mirror of
https://github.com/signalwire/freeswitch.git
synced 2026-07-24 13:12:03 +00:00
update
git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@263 d0543943-73ff-0310-b7d9-9358b9ac24b2
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
#CFLAGS += -I/usr/src/common/src
|
||||
LDFLAGS += -leXosip2 -ljrtp4c
|
||||
|
||||
ifeq ($(OSARCH),Darwin)
|
||||
LINKER=g++
|
||||
else
|
||||
LINKER=$(CC)
|
||||
endif
|
||||
|
||||
all: depends $(MOD).so
|
||||
|
||||
depends:
|
||||
$(BASE)/buildlib.sh $(BASE) install jthread-1.1.2.tar.gz --prefix=$(PREFIX)
|
||||
$(BASE)/buildlib.sh $(BASE) install jrtplib-3.3.0.tar.gz --prefix=$(PREFIX)
|
||||
$(BASE)/buildlib.sh $(BASE) install jrtp4c --prefix=$(PREFIX)
|
||||
$(BASE)/buildlib.sh $(BASE) install libosip2-2.2.2.tar.gz --prefix=$(PREFIX)
|
||||
$(BASE)/buildlib.sh $(BASE) install libeXosip2-2.2.2.tar.gz --disable-josua --prefix=$(PREFIX)
|
||||
|
||||
|
||||
$(MOD).so: $(MOD).c
|
||||
$(CC) $(CFLAGS) -fPIC -c $(MOD).c -o $(MOD).o
|
||||
$(LINKER) $(SOLINK) -o $(MOD).so $(MOD).o $(LDFLAGS)
|
||||
|
||||
clean:
|
||||
rm -fr *.so *.o *~
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,229 @@
|
||||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="8.00"
|
||||
Name="mod_exosip"
|
||||
ProjectGUID="{45DF84ED-D24A-4FF6-B5B0-0A9A5FDB9552}"
|
||||
RootNamespace="mod_exosip"
|
||||
Keyword="Win32Proj"
|
||||
>
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"
|
||||
/>
|
||||
</Platforms>
|
||||
<ToolFiles>
|
||||
</ToolFiles>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
OutputDirectory="Debug"
|
||||
IntermediateDirectory="Debug"
|
||||
ConfigurationType="2"
|
||||
InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
|
||||
CharacterSet="2"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
CommandLine="cscript /nologo $(InputDir)..\..\..\w32\vsnet\getlibs.vbs Mod_Exosip Debug"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
AdditionalOptions="/D _CRT_SECURE_NO_DEPRECATE"
|
||||
Optimization="0"
|
||||
AdditionalIncludeDirectories="..\..\include;..\..\..\libs\include;..\..\..\libs\libeXosip2\include;..\..\..\libs\osip\include;..\..\..\libs\jrtp4c\src;..\..\..\libs\osip\src\osipparser2;..\..\..\libs\jrtplib\src;"..\..\..\libs\jthread-1.1.2\src""
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;MOD_EXPORTS"
|
||||
GeneratePreprocessedFile="0"
|
||||
MinimalRebuild="true"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="1"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="true"
|
||||
DebugInformationFormat="4"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalOptions="/NODEFAULTLIB:LIBCMT"
|
||||
AdditionalDependencies="Ws2_32.lib Iphlpapi.lib libcpmt.lib eXosip.lib jrtplib.lib jthread.lib osipparser2.lib osip2.lib"
|
||||
ShowProgress="0"
|
||||
OutputFile="..\..\..\w32\vsnet\$(OutDir)/mod/mod_exosip.dll"
|
||||
LinkIncremental="2"
|
||||
AdditionalLibraryDirectories=""$(InputDir)..\..\..\libs\apr\Debug";"$(InputDir)..\..\..\libs\jrtp4c\w32\Debug";"$(InputDir)..\..\..\libs\jrtplib\Debug";"$(InputDir)..\..\..\libs\jthread-1.1.2\Debug";"$(InputDir)..\..\..\libs\libeXosip2\platform\vsnet\Debug";"$(InputDir)..\..\..\libs\osip\platform\vsnet\Debug""
|
||||
IgnoreAllDefaultLibraries="false"
|
||||
IgnoreDefaultLibraryNames=""
|
||||
GenerateDebugInformation="true"
|
||||
ProgramDatabaseFile="$(OutDir)/mod_exosip.pdb"
|
||||
SubSystem="2"
|
||||
ImportLibrary="$(OutDir)/mod_exosip.lib"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
OutputDirectory="Release"
|
||||
IntermediateDirectory="Release"
|
||||
ConfigurationType="2"
|
||||
InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
|
||||
CharacterSet="2"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
CommandLine="cscript /nologo $(InputDir)..\..\..\w32\vsnet\getlibs.vbs Mod_Exosip Release"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
AdditionalIncludeDirectories="..\..\include;..\..\..\libs\include;..\..\..\libs\libeXosip2\include;..\..\..\libs\osip\include;..\..\..\libs\jrtp4c\src;..\..\..\libs\osip\src\osipparser2;..\..\..\libs\jrtplib\src;"..\..\..\libs\jthread-1.1.2\src""
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;MOD_EXPORTS"
|
||||
RuntimeLibrary="0"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="true"
|
||||
DebugInformationFormat="3"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="Ws2_32.lib Iphlpapi.lib libcpmt.lib eXosip.lib jrtplib.lib jthread.lib osipparser2.lib osip2.lib"
|
||||
OutputFile="..\..\..\w32\vsnet\$(OutDir)/mod/mod_exosip.dll"
|
||||
LinkIncremental="1"
|
||||
AdditionalLibraryDirectories=""$(InputDir)..\..\..\libs\apr\Release";"$(InputDir)..\..\..\libs\jrtp4c\w32\Release";"$(InputDir)..\..\..\libs\jrtplib\Release";"$(InputDir)..\..\..\libs\jthread-1.1.2\Release";"$(InputDir)..\..\..\libs\libeXosip2\platform\vsnet\Release";"$(InputDir)..\..\..\libs\osip\platform\vsnet\Release""
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="2"
|
||||
OptimizeReferences="2"
|
||||
EnableCOMDATFolding="2"
|
||||
ImportLibrary="$(OutDir)/mod_exosip.lib"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<Filter
|
||||
Name="Source Files"
|
||||
Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
|
||||
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
|
||||
>
|
||||
<File
|
||||
RelativePath="..\..\..\libs\jrtp4c\src\jrtp4c.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\mod_exosip.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\libs\osip\src\osipparser2\osip_rfc3264.c"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Header Files"
|
||||
Filter="h;hpp;hxx;hm;inl;inc;xsd"
|
||||
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
|
||||
>
|
||||
<File
|
||||
RelativePath="..\..\..\libs\osip\src\osipparser2\osip_rfc3264i.h"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Resource Files"
|
||||
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
|
||||
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
|
||||
>
|
||||
</Filter>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,15 @@
|
||||
LDFLAGS += -liax -L/usr/local/lib
|
||||
|
||||
all: depends $(MOD).so
|
||||
|
||||
depends:
|
||||
$(BASE)/buildlib.sh $(BASE) install iax --enable-newjb --prefix=$(PREFIX)
|
||||
|
||||
$(MOD).so: $(MOD).c
|
||||
$(CC) $(CFLAGS) -fPIC -c $(MOD).c -o $(MOD).o
|
||||
$(CC) $(SOLINK) -o $(MOD).so $(MOD).o $(LDFLAGS)
|
||||
|
||||
|
||||
clean:
|
||||
rm -fr *.so *.o *~
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="8.00"
|
||||
Name="mod_IaxChan"
|
||||
ProjectGUID="{3A5B9131-F20C-4A85-9447-6C1610941CEE}"
|
||||
RootNamespace="mod_IaxChan"
|
||||
Keyword="Win32Proj"
|
||||
>
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"
|
||||
/>
|
||||
</Platforms>
|
||||
<ToolFiles>
|
||||
</ToolFiles>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
OutputDirectory="Debug"
|
||||
IntermediateDirectory="Debug"
|
||||
ConfigurationType="2"
|
||||
InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
|
||||
CharacterSet="2"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
CommandLine="cscript /nologo $(InputDir)..\..\..\w32\vsnet\getlibs.vbs Mod_IaxChan Debug"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
AdditionalIncludeDirectories=""$(InputDir)..\..\include";"$(InputDir)include";"$(InputDir)..\..\..\libs\include";"$(InputDir)..\..\..\libs\iax\src""
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;MOD_EXPORTS"
|
||||
MinimalRebuild="true"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="3"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="true"
|
||||
DebugInformationFormat="4"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="Ws2_32.lib Iphlpapi.lib libiax2.lib Winmm.lib"
|
||||
OutputFile="..\..\..\w32\vsnet\$(OutDir)/mod/mod_IaxChan.dll"
|
||||
LinkIncremental="2"
|
||||
AdditionalLibraryDirectories="$(InputDir)..\..\..\libs\iax\Debug"
|
||||
GenerateDebugInformation="true"
|
||||
ProgramDatabaseFile="$(OutDir)/mod_IaxChan.pdb"
|
||||
SubSystem="2"
|
||||
ImportLibrary="$(OutDir)/mod_IaxChan.lib"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
OutputDirectory="Release"
|
||||
IntermediateDirectory="Release"
|
||||
ConfigurationType="2"
|
||||
InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
|
||||
CharacterSet="2"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
CommandLine="cscript /nologo $(InputDir)..\..\..\w32\vsnet\getlibs.vbs Mod_IaxChan Release"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
AdditionalIncludeDirectories=""$(InputDir)..\..\include";"$(InputDir)include";"$(InputDir)..\..\..\libs\include";"$(InputDir)..\..\..\libs\iax\src""
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;MOD_EXPORTS"
|
||||
RuntimeLibrary="2"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="true"
|
||||
DebugInformationFormat="3"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="Ws2_32.lib Iphlpapi.lib libiax2.lib Winmm.lib"
|
||||
OutputFile="..\..\..\w32\vsnet\$(OutDir)/mod/mod_IaxChan.dll"
|
||||
LinkIncremental="1"
|
||||
AdditionalLibraryDirectories=""$(InputDir)..\..\..\libs\iax\Release""
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="2"
|
||||
OptimizeReferences="2"
|
||||
EnableCOMDATFolding="2"
|
||||
LinkTimeCodeGeneration="1"
|
||||
ImportLibrary="$(OutDir)/mod_IaxChan.lib"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<Filter
|
||||
Name="Source Files"
|
||||
Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
|
||||
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\mod_iaxchan.c"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Header Files"
|
||||
Filter="h;hpp;hxx;hm;inl;inc;xsd"
|
||||
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
|
||||
>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Resource Files"
|
||||
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
|
||||
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
|
||||
>
|
||||
</Filter>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
||||
@@ -0,0 +1,986 @@
|
||||
/*
|
||||
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
* Copyright (C) 2005/2006, Anthony Minessale II <anthmct@yahoo.com>
|
||||
*
|
||||
* Version: MPL 1.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Anthony Minessale II <anthmct@yahoo.com>
|
||||
* Portions created by the Initial Developer are Copyright (C)
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Anthony Minessale II <anthmct@yahoo.com>
|
||||
*
|
||||
*
|
||||
* mod_iaxchan.c -- IAX2 Endpoint Module
|
||||
*
|
||||
*/
|
||||
#include <switch.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <iax2.h>
|
||||
#include <iax-client.h>
|
||||
#include <iax2-parser.h>
|
||||
#include <sys/timeb.h>
|
||||
#else
|
||||
#include <iax/iax2.h>
|
||||
#include <iax/iax-client.h>
|
||||
#include <iax/iax2-parser.h>
|
||||
#endif
|
||||
|
||||
static const char modname[] = "mod_iaxchan";
|
||||
|
||||
static switch_memory_pool *module_pool;
|
||||
static int running = 1;
|
||||
|
||||
|
||||
typedef enum {
|
||||
TFLAG_IO = (1 << 0),
|
||||
TFLAG_INBOUND = (1 << 1),
|
||||
TFLAG_OUTBOUND = (1 << 2),
|
||||
TFLAG_DTMF = (1 << 3),
|
||||
TFLAG_VOICE = (1 << 4),
|
||||
TFLAG_HANGUP = (1 << 5),
|
||||
TFLAG_LINEAR = (1 << 6)
|
||||
} TFLAGS;
|
||||
|
||||
typedef enum {
|
||||
GFLAG_MY_CODEC_PREFS = (1 << 0)
|
||||
} GFLAGS;
|
||||
|
||||
static struct {
|
||||
int debug;
|
||||
int port;
|
||||
char *dialplan;
|
||||
char *codec_string;
|
||||
char *codec_order[SWITCH_MAX_CODECS];
|
||||
int codec_order_last;
|
||||
unsigned int flags;
|
||||
} globals;
|
||||
|
||||
struct private_object {
|
||||
unsigned int flags;
|
||||
switch_codec read_codec;
|
||||
switch_codec write_codec;
|
||||
struct switch_frame read_frame;
|
||||
unsigned char databuf[1024];
|
||||
switch_core_session *session;
|
||||
struct iax_session *iax_session;
|
||||
switch_caller_profile *caller_profile;
|
||||
unsigned int codec;
|
||||
unsigned int codecs;
|
||||
switch_mutex_t *mutex;
|
||||
switch_thread_cond_t *cond;
|
||||
};
|
||||
|
||||
SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_dialplan, globals.dialplan)
|
||||
SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_codec_string, globals.codec_string)
|
||||
|
||||
|
||||
static char *IAXNAMES[] = {"IAX_EVENT_CONNECT","IAX_EVENT_ACCEPT","IAX_EVENT_HANGUP","IAX_EVENT_REJECT","IAX_EVENT_VOICE",
|
||||
"IAX_EVENT_DTMF","IAX_EVENT_TIMEOUT","IAX_EVENT_LAGRQ","IAX_EVENT_LAGRP","IAX_EVENT_RINGA",
|
||||
"IAX_EVENT_PING","IAX_EVENT_PONG","IAX_EVENT_BUSY","IAX_EVENT_ANSWER","IAX_EVENT_IMAGE",
|
||||
"IAX_EVENT_AUTHRQ","IAX_EVENT_AUTHRP","IAX_EVENT_REGREQ","IAX_EVENT_REGACK",
|
||||
"IAX_EVENT_URL","IAX_EVENT_LDCOMPLETE","IAX_EVENT_TRANSFER","IAX_EVENT_DPREQ",
|
||||
"IAX_EVENT_DPREP","IAX_EVENT_DIAL","IAX_EVENT_QUELCH","IAX_EVENT_UNQUELCH",
|
||||
"IAX_EVENT_UNLINK","IAX_EVENT_LINKREJECT","IAX_EVENT_TEXT","IAX_EVENT_REGREJ",
|
||||
"IAX_EVENT_LINKURL","IAX_EVENT_CNG","IAX_EVENT_REREQUEST","IAX_EVENT_TXREPLY",
|
||||
"IAX_EVENT_TXREJECT","IAX_EVENT_TXACCEPT","IAX_EVENT_TXREADY"};
|
||||
|
||||
|
||||
struct ast_iana {
|
||||
const unsigned int ast;
|
||||
const int iana;
|
||||
char *name;
|
||||
};
|
||||
|
||||
//999 means it's wrong nad i dont know the real one
|
||||
static struct ast_iana AST_IANA[] =
|
||||
{{AST_FORMAT_G723_1, 4, "g723.1"},
|
||||
{AST_FORMAT_GSM, 3, "gsm"},
|
||||
{AST_FORMAT_ULAW, 0, "ulaw"},
|
||||
{AST_FORMAT_ALAW, 8, "alaw"},
|
||||
{AST_FORMAT_G726, 999, "g726"},
|
||||
{AST_FORMAT_ADPCM, 999, "adpcm"},
|
||||
{AST_FORMAT_SLINEAR, 10, "slinear"},
|
||||
{AST_FORMAT_LPC10, 7, "lpc10"},
|
||||
{AST_FORMAT_G729A, 18, "g729"},
|
||||
{AST_FORMAT_SPEEX, 98, "speex"},
|
||||
{AST_FORMAT_ILBC, 999, "ilbc"},
|
||||
{AST_FORMAT_MAX_AUDIO, 999, ""},
|
||||
{AST_FORMAT_JPEG, 999, ""},
|
||||
{AST_FORMAT_PNG, 999, ""},
|
||||
{AST_FORMAT_H261, 999, ""},
|
||||
{AST_FORMAT_H263, 999, ""},
|
||||
{AST_FORMAT_MAX_VIDEO, 999, ""},
|
||||
{0,0}
|
||||
};
|
||||
|
||||
static char *ast2str(unsigned int ast)
|
||||
{
|
||||
int x;
|
||||
for(x = 0; x < 32; x++) {
|
||||
if ((1 << x) == ast) {
|
||||
return AST_IANA[x].name;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
static unsigned int iana2ast(int iana)
|
||||
{
|
||||
int x = 0;
|
||||
unsigned int ast = 0;
|
||||
|
||||
for(x = 0; AST_IANA[x].ast; x++) {
|
||||
if (AST_IANA[x].iana == iana) {
|
||||
ast = AST_IANA[x].ast;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ast;
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
IAX_SET = 1,
|
||||
IAX_QUERY = 2
|
||||
} iax_io_t;
|
||||
|
||||
static switch_status iax_set_codec(struct private_object *tech_pvt, struct iax_session *iax_session, unsigned int *format, unsigned int *cababilities, iax_io_t io)
|
||||
{
|
||||
char *dname = NULL;
|
||||
//int rate = 8000;
|
||||
//int codec_ms = 20;
|
||||
switch_channel *channel;
|
||||
switch_codec_interface *codecs[SWITCH_MAX_CODECS];
|
||||
int num_codecs = 0;
|
||||
unsigned int local_cap = 0, mixed_cap = 0, chosen = 0, leading = 0;
|
||||
int x;
|
||||
|
||||
if (globals.codec_string) {
|
||||
if (!(num_codecs = loadable_module_get_codecs_sorted(switch_core_session_get_pool(tech_pvt->session),
|
||||
codecs,
|
||||
SWITCH_MAX_CODECS,
|
||||
globals.codec_order,
|
||||
globals.codec_order_last)) > 0) {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "NO codecs?\n");
|
||||
return SWITCH_STATUS_GENERR;
|
||||
}
|
||||
} else if (!(num_codecs = loadable_module_get_codecs(switch_core_session_get_pool(tech_pvt->session), codecs, SWITCH_MAX_CODECS)) > 0) {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "NO codecs?\n");
|
||||
return SWITCH_STATUS_GENERR;
|
||||
}
|
||||
|
||||
for (x = 0; x < num_codecs; x++) {
|
||||
unsigned int codec = iana2ast(codecs[x]->ianacode);
|
||||
if (io == IAX_QUERY) {
|
||||
iax_pref_codec_add(iax_session, codec);
|
||||
}
|
||||
local_cap |= codec;
|
||||
}
|
||||
|
||||
if (io == IAX_SET) {
|
||||
mixed_cap = (local_cap & *cababilities);
|
||||
} else {
|
||||
mixed_cap = local_cap;
|
||||
}
|
||||
|
||||
leading = iana2ast(codecs[0]->ianacode);
|
||||
if (io == IAX_QUERY) {
|
||||
chosen = leading;
|
||||
*format = chosen;
|
||||
*cababilities = local_cap;
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
} else if (switch_test_flag(&globals, GFLAG_MY_CODEC_PREFS) && (leading & mixed_cap)) {
|
||||
chosen = leading;
|
||||
dname = codecs[0]->iananame;
|
||||
} else {
|
||||
unsigned int prefs[32];
|
||||
int len = 0;
|
||||
|
||||
if (!switch_test_flag(&globals, GFLAG_MY_CODEC_PREFS)) {
|
||||
len = iax_pref_codec_get(iax_session, prefs, sizeof(prefs));
|
||||
}
|
||||
|
||||
if (len) { /*they sent us a pref and we don't want to be codec master*/
|
||||
char pref_str[256] = "(";
|
||||
|
||||
for (x = 0; x < len; x++) {
|
||||
strncat(pref_str, ast2str(prefs[x]), sizeof(pref_str));
|
||||
strncat(pref_str, x == len - 1 ? ")" : ",", sizeof(pref_str));
|
||||
}
|
||||
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Codec Prefs Detected: %s\n", pref_str);
|
||||
|
||||
for (x = 0; x < len; x++) {
|
||||
if ((prefs[x] & mixed_cap)) {
|
||||
int z;
|
||||
chosen = prefs[x];
|
||||
for (z = 0; z < num_codecs; z++) {
|
||||
if (prefs[x] == iana2ast(codecs[z]->ianacode)) {
|
||||
dname = codecs[z]->iananame;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (*format & mixed_cap) { /* is the one we asked for here? */
|
||||
chosen = *format;
|
||||
for (x = 0; x < num_codecs; x++) {
|
||||
unsigned int cap = iana2ast(codecs[x]->ianacode);
|
||||
if (cap == chosen) {
|
||||
dname = codecs[x]->iananame;
|
||||
}
|
||||
}
|
||||
} else { /* c'mon there has to be SOMETHING...*/
|
||||
for (x = 0; x < num_codecs; x++) {
|
||||
unsigned int cap = iana2ast(codecs[x]->ianacode);
|
||||
if (cap & mixed_cap) {
|
||||
chosen = cap;
|
||||
dname = codecs[x]->iananame;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!dname && !chosen) {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "NO codecs?\n");
|
||||
return SWITCH_STATUS_GENERR;
|
||||
}
|
||||
|
||||
channel = switch_core_session_get_channel(tech_pvt->session);
|
||||
assert(channel != NULL);
|
||||
|
||||
if (!strcasecmp(dname, "l16")) {
|
||||
switch_set_flag(tech_pvt, TFLAG_LINEAR);
|
||||
}
|
||||
if (switch_core_codec_init(&tech_pvt->read_codec,
|
||||
dname,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE,
|
||||
NULL,
|
||||
switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Can't load codec?\n");
|
||||
return SWITCH_STATUS_GENERR;
|
||||
} else {
|
||||
if (switch_core_codec_init(&tech_pvt->write_codec,
|
||||
dname,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
SWITCH_CODEC_FLAG_ENCODE |SWITCH_CODEC_FLAG_DECODE,
|
||||
NULL,
|
||||
switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Can't load codec?\n");
|
||||
switch_core_codec_destroy(&tech_pvt->read_codec);
|
||||
return SWITCH_STATUS_GENERR;
|
||||
} else {
|
||||
int ms;
|
||||
int rate;
|
||||
ms = tech_pvt->write_codec.implementation->microseconds_per_frame / 1000;
|
||||
rate = tech_pvt->write_codec.implementation->samples_per_second;
|
||||
tech_pvt->read_frame.rate = rate;
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Activate Codec %s/%d %d ms\n", dname, rate, ms);
|
||||
tech_pvt->read_frame.codec = &tech_pvt->read_codec;
|
||||
switch_core_session_set_read_codec(tech_pvt->session, &tech_pvt->read_codec);
|
||||
switch_core_session_set_write_codec(tech_pvt->session, &tech_pvt->write_codec);
|
||||
}
|
||||
tech_pvt->codec = chosen;
|
||||
tech_pvt->codecs = local_cap;
|
||||
}
|
||||
|
||||
if (io == IAX_QUERY) {
|
||||
*format = tech_pvt->codec;
|
||||
*cababilities = local_cap;
|
||||
}
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static const switch_endpoint_interface channel_endpoint_interface;
|
||||
|
||||
static switch_status channel_on_init(switch_core_session *session);
|
||||
static switch_status channel_on_hangup(switch_core_session *session);
|
||||
static switch_status channel_on_ring(switch_core_session *session);
|
||||
static switch_status channel_on_loopback(switch_core_session *session);
|
||||
static switch_status channel_on_transmit(switch_core_session *session);
|
||||
static switch_status channel_outgoing_channel(switch_core_session *session, switch_caller_profile *outbound_profile, switch_core_session **new_session);
|
||||
static switch_status channel_read_frame(switch_core_session *session, switch_frame **frame, int timeout, switch_io_flag flags);
|
||||
static switch_status channel_write_frame(switch_core_session *session, switch_frame *frame, int timeout, switch_io_flag flags);
|
||||
static switch_status channel_kill_channel(switch_core_session *session, int sig);
|
||||
|
||||
|
||||
static void iax_err_cb(const char *s)
|
||||
{
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE_CLEAN, "IAX ERR: %s", s);
|
||||
}
|
||||
|
||||
static void iax_out_cb(const char *s)
|
||||
{
|
||||
if (globals.debug) {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE_CLEAN, "IAX INFO: %s", s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
State methods they get called when the state changes to the specific state
|
||||
returning SWITCH_STATUS_SUCCESS tells the core to execute the standard state method next
|
||||
so if you fully implement the state you can return SWITCH_STATUS_FALSE to skip it.
|
||||
*/
|
||||
static switch_status channel_on_init(switch_core_session *session)
|
||||
{
|
||||
switch_channel *channel;
|
||||
struct private_object *tech_pvt = NULL;
|
||||
|
||||
tech_pvt = switch_core_session_get_private(session);
|
||||
assert(tech_pvt != NULL);
|
||||
|
||||
channel = switch_core_session_get_channel(session);
|
||||
assert(channel != NULL);
|
||||
|
||||
tech_pvt->read_frame.data = tech_pvt->databuf;
|
||||
tech_pvt->read_frame.buflen = sizeof(tech_pvt->databuf);
|
||||
iax_set_private(tech_pvt->iax_session, tech_pvt);
|
||||
|
||||
switch_set_flag(tech_pvt, TFLAG_IO);
|
||||
|
||||
switch_mutex_init(&tech_pvt->mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session));
|
||||
switch_thread_cond_create(&tech_pvt->cond, switch_core_session_get_pool(session));
|
||||
switch_mutex_lock(tech_pvt->mutex);
|
||||
|
||||
/* Move Channel's State Machine to RING */
|
||||
switch_channel_set_state(channel, CS_RING);
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static switch_status channel_on_ring(switch_core_session *session)
|
||||
{
|
||||
switch_channel *channel = NULL;
|
||||
struct private_object *tech_pvt = NULL;
|
||||
|
||||
channel = switch_core_session_get_channel(session);
|
||||
assert(channel != NULL);
|
||||
|
||||
tech_pvt = switch_core_session_get_private(session);
|
||||
assert(tech_pvt != NULL);
|
||||
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "%s CHANNEL RING\n", switch_channel_get_name(channel));
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static switch_status channel_on_execute(switch_core_session *session)
|
||||
{
|
||||
|
||||
switch_channel *channel = NULL;
|
||||
struct private_object *tech_pvt = NULL;
|
||||
|
||||
channel = switch_core_session_get_channel(session);
|
||||
assert(channel != NULL);
|
||||
|
||||
tech_pvt = switch_core_session_get_private(session);
|
||||
assert(tech_pvt != NULL);
|
||||
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "%s CHANNEL EXECUTE\n", switch_channel_get_name(channel));
|
||||
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static switch_status channel_on_hangup(switch_core_session *session)
|
||||
{
|
||||
switch_channel *channel = NULL;
|
||||
struct private_object *tech_pvt = NULL;
|
||||
|
||||
channel = switch_core_session_get_channel(session);
|
||||
assert(channel != NULL);
|
||||
|
||||
tech_pvt = switch_core_session_get_private(session);
|
||||
assert(tech_pvt != NULL);
|
||||
|
||||
switch_clear_flag(tech_pvt, TFLAG_IO);
|
||||
switch_thread_cond_signal(tech_pvt->cond);
|
||||
|
||||
switch_core_codec_destroy(&tech_pvt->read_codec);
|
||||
switch_core_codec_destroy(&tech_pvt->write_codec);
|
||||
|
||||
if (tech_pvt->iax_session) {
|
||||
if (!switch_test_flag(tech_pvt, TFLAG_HANGUP)) {
|
||||
iax_hangup(tech_pvt->iax_session, "Hangup");
|
||||
switch_set_flag(tech_pvt, TFLAG_HANGUP);
|
||||
}
|
||||
iax_session_destroy(&tech_pvt->iax_session);
|
||||
}
|
||||
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "%s CHANNEL HANGUP\n", switch_channel_get_name(channel));
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static switch_status channel_kill_channel(switch_core_session *session, int sig)
|
||||
{
|
||||
switch_channel *channel = NULL;
|
||||
struct private_object *tech_pvt = NULL;
|
||||
|
||||
channel = switch_core_session_get_channel(session);
|
||||
assert(channel != NULL);
|
||||
|
||||
tech_pvt = switch_core_session_get_private(session);
|
||||
assert(tech_pvt != NULL);
|
||||
|
||||
switch_clear_flag(tech_pvt, TFLAG_IO);
|
||||
switch_clear_flag(tech_pvt, TFLAG_VOICE);
|
||||
switch_channel_hangup(channel);
|
||||
switch_thread_cond_signal(tech_pvt->cond);
|
||||
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "%s CHANNEL KILL\n", switch_channel_get_name(channel));
|
||||
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static switch_status channel_on_loopback(switch_core_session *session)
|
||||
{
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "CHANNEL LOOPBACK\n");
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static switch_status channel_on_transmit(switch_core_session *session)
|
||||
{
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "CHANNEL TRANSMIT\n");
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/* Make sure when you have 2 sessions in the same scope that you pass the appropriate one to the routines
|
||||
that allocate memory or you will have 1 channel with memory allocated from another channel's pool!
|
||||
*/
|
||||
static switch_status channel_outgoing_channel(switch_core_session *session, switch_caller_profile *outbound_profile, switch_core_session **new_session)
|
||||
{
|
||||
if ((*new_session = switch_core_session_request(&channel_endpoint_interface, NULL))) {
|
||||
struct private_object *tech_pvt;
|
||||
switch_channel *channel;
|
||||
switch_caller_profile *caller_profile;
|
||||
unsigned int req = 0, cap = 0;
|
||||
|
||||
if ((tech_pvt = (struct private_object *) switch_core_session_alloc(*new_session, sizeof(struct private_object)))) {
|
||||
memset(tech_pvt, 0, sizeof(*tech_pvt));
|
||||
channel = switch_core_session_get_channel(*new_session);
|
||||
switch_core_session_set_private(*new_session, tech_pvt);
|
||||
tech_pvt->session = *new_session;
|
||||
} else {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Hey where is my memory pool?\n");
|
||||
switch_core_session_destroy(new_session);
|
||||
return SWITCH_STATUS_GENERR;
|
||||
}
|
||||
|
||||
if (outbound_profile) {
|
||||
char name[128];
|
||||
caller_profile = switch_caller_profile_clone(*new_session, outbound_profile);
|
||||
switch_channel_set_caller_profile(channel, caller_profile);
|
||||
tech_pvt->caller_profile = caller_profile;
|
||||
snprintf(name, sizeof(name), "IAX/%s-%04x", caller_profile->destination_number, rand() & 0xffff);
|
||||
switch_channel_set_name(channel, name);
|
||||
} else {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Doh! no caller profile\n");
|
||||
switch_core_session_destroy(new_session);
|
||||
return SWITCH_STATUS_GENERR;
|
||||
}
|
||||
|
||||
if (!(tech_pvt->iax_session = iax_session_new())) {
|
||||
switch_core_session_destroy(new_session);
|
||||
return SWITCH_STATUS_GENERR;
|
||||
}
|
||||
|
||||
|
||||
if (iax_set_codec(tech_pvt, tech_pvt->iax_session, &req, &cap, IAX_QUERY) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_core_session_destroy(new_session);
|
||||
return SWITCH_STATUS_GENERR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
iax_call(tech_pvt->iax_session,
|
||||
caller_profile->caller_id_number,
|
||||
caller_profile->caller_id_name,
|
||||
caller_profile->destination_number,
|
||||
NULL, 0, req, cap);
|
||||
|
||||
switch_channel_set_flag(channel, CF_OUTBOUND);
|
||||
switch_set_flag(tech_pvt, TFLAG_OUTBOUND);
|
||||
switch_channel_set_state(channel, CS_INIT);
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
return SWITCH_STATUS_GENERR;
|
||||
|
||||
}
|
||||
|
||||
static switch_status channel_waitfor_read(switch_core_session *session, int ms)
|
||||
{
|
||||
struct private_object *tech_pvt = NULL;
|
||||
|
||||
tech_pvt = switch_core_session_get_private(session);
|
||||
assert(tech_pvt != NULL);
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static switch_status channel_waitfor_write(switch_core_session *session, int ms)
|
||||
{
|
||||
struct private_object *tech_pvt = NULL;
|
||||
|
||||
tech_pvt = switch_core_session_get_private(session);
|
||||
assert(tech_pvt != NULL);
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
|
||||
}
|
||||
|
||||
static switch_status channel_send_dtmf(switch_core_session *session, char *dtmf)
|
||||
{
|
||||
struct private_object *tech_pvt = NULL;
|
||||
char *digit;
|
||||
|
||||
tech_pvt = switch_core_session_get_private(session);
|
||||
assert(tech_pvt != NULL);
|
||||
if (tech_pvt->iax_session) {
|
||||
for(digit = dtmf; *digit; digit++) {
|
||||
iax_send_dtmf(tech_pvt->iax_session, *digit);
|
||||
}
|
||||
}
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static switch_status channel_read_frame(switch_core_session *session, switch_frame **frame, int timeout, switch_io_flag flags)
|
||||
{
|
||||
switch_channel *channel = NULL;
|
||||
struct private_object *tech_pvt = NULL;
|
||||
|
||||
channel = switch_core_session_get_channel(session);
|
||||
assert(channel != NULL);
|
||||
|
||||
tech_pvt = switch_core_session_get_private(session);
|
||||
assert(tech_pvt != NULL);
|
||||
|
||||
for(;;) {
|
||||
if (switch_test_flag(tech_pvt, TFLAG_IO)) {
|
||||
switch_thread_cond_wait(tech_pvt->cond, tech_pvt->mutex);
|
||||
if (!switch_test_flag(tech_pvt, TFLAG_IO)) {
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
|
||||
if (switch_test_flag(tech_pvt, TFLAG_IO)) {
|
||||
switch_clear_flag(tech_pvt, TFLAG_VOICE);
|
||||
if(!tech_pvt->read_frame.datalen) {
|
||||
continue;
|
||||
}
|
||||
|
||||
*frame = &tech_pvt->read_frame;
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
|
||||
static switch_status channel_write_frame(switch_core_session *session, switch_frame *frame, int timeout, switch_io_flag flags)
|
||||
{
|
||||
switch_channel *channel = NULL;
|
||||
struct private_object *tech_pvt = NULL;
|
||||
//switch_frame *pframe;
|
||||
|
||||
channel = switch_core_session_get_channel(session);
|
||||
assert(channel != NULL);
|
||||
|
||||
tech_pvt = switch_core_session_get_private(session);
|
||||
assert(tech_pvt != NULL);
|
||||
|
||||
if(!switch_test_flag(tech_pvt, TFLAG_IO)) {
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
|
||||
#ifndef BIGENDIAN
|
||||
if (switch_test_flag(tech_pvt, TFLAG_LINEAR)) {
|
||||
switch_swap_linear(frame->data, (int)frame->datalen / 2);
|
||||
}
|
||||
#endif
|
||||
iax_send_voice(tech_pvt->iax_session, tech_pvt->codec, frame->data, (int)frame->datalen, tech_pvt->write_codec.implementation->samples_per_frame);
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
|
||||
}
|
||||
|
||||
static switch_status channel_answer_channel(switch_core_session *session)
|
||||
{
|
||||
struct private_object *tech_pvt;
|
||||
switch_channel *channel = NULL;
|
||||
|
||||
channel = switch_core_session_get_channel(session);
|
||||
assert(channel != NULL);
|
||||
|
||||
tech_pvt = switch_core_session_get_private(session);
|
||||
assert(tech_pvt != NULL);
|
||||
|
||||
iax_answer(tech_pvt->iax_session);
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static const switch_event_handler_table channel_event_handlers = {
|
||||
/*.on_init*/ channel_on_init,
|
||||
/*.on_ring*/ channel_on_ring,
|
||||
/*.on_execute*/ channel_on_execute,
|
||||
/*.on_hangup*/ channel_on_hangup,
|
||||
/*.on_loopback*/ channel_on_loopback,
|
||||
/*.on_transmit*/ channel_on_transmit
|
||||
};
|
||||
|
||||
static const switch_io_routines channel_io_routines = {
|
||||
/*.outgoing_channel*/ channel_outgoing_channel,
|
||||
/*.answer_channel*/ channel_answer_channel,
|
||||
/*.read_frame*/ channel_read_frame,
|
||||
/*.write_frame*/ channel_write_frame,
|
||||
/*.kill_channel*/ channel_kill_channel,
|
||||
/*.waitfor_read*/ channel_waitfor_read,
|
||||
/*.waitfor_write*/ channel_waitfor_write,
|
||||
/*.send_dtmf*/ channel_send_dtmf
|
||||
};
|
||||
|
||||
static const switch_endpoint_interface channel_endpoint_interface = {
|
||||
/*.interface_name*/ "iax",
|
||||
/*.io_routines*/ &channel_io_routines,
|
||||
/*.event_handlers*/ &channel_event_handlers,
|
||||
/*.private*/ NULL,
|
||||
/*.next*/ NULL
|
||||
};
|
||||
|
||||
static const switch_loadable_module_interface channel_module_interface = {
|
||||
/*.module_name*/ modname,
|
||||
/*.endpoint_interface*/ &channel_endpoint_interface,
|
||||
/*.timer_interface*/ NULL,
|
||||
/*.dialplan_interface*/ NULL,
|
||||
/*.codec_interface*/ NULL,
|
||||
/*.application_interface*/ NULL
|
||||
};
|
||||
|
||||
|
||||
SWITCH_MOD_DECLARE(switch_status) switch_module_load(const switch_loadable_module_interface **interface, char *filename) {
|
||||
|
||||
if (switch_core_new_memory_pool(&module_pool) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "OH OH no pool\n");
|
||||
return SWITCH_STATUS_TERM;
|
||||
}
|
||||
|
||||
/* connect my internal structure to the blank pointer passed to me */
|
||||
*interface = &channel_module_interface;
|
||||
|
||||
/* indicate that the module should continue to be loaded */
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
#define PHONE_FREED 0
|
||||
#define PHONE_CALLACCEPTED 1
|
||||
#define PHONE_RINGING 2
|
||||
#define PHONE_ANSWERED 3
|
||||
#define PHONE_CONNECTED 4
|
||||
|
||||
#define UNREGISTERED 0
|
||||
#define REGISTERED 1
|
||||
|
||||
|
||||
static switch_status load_config(void)
|
||||
{
|
||||
switch_config cfg;
|
||||
char *var, *val;
|
||||
char *cf = "iax.conf";
|
||||
|
||||
memset(&globals, 0, sizeof(globals));
|
||||
|
||||
if (!switch_config_open_file(&cfg, cf)) {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "open of %s failed\n", cf);
|
||||
return SWITCH_STATUS_TERM;
|
||||
}
|
||||
|
||||
while (switch_config_next_pair(&cfg, &var, &val)) {
|
||||
if (!strcasecmp(cfg.category, "settings")) {
|
||||
if (!strcmp(var, "debug")) {
|
||||
globals.debug = atoi(val);
|
||||
} else if (!strcmp(var, "port")) {
|
||||
globals.port = atoi(val);
|
||||
} else if (!strcmp(var, "codec_master")) {
|
||||
if (!strcasecmp(val, "us")) {
|
||||
switch_set_flag(&globals, GFLAG_MY_CODEC_PREFS);
|
||||
}
|
||||
} else if (!strcmp(var, "dialplan")) {
|
||||
set_global_dialplan(val);
|
||||
} else if (!strcmp(var, "codec_prefs")) {
|
||||
set_global_codec_string(val);
|
||||
globals.codec_order_last = switch_separate_string(globals.codec_string, ',', globals.codec_order, SWITCH_MAX_CODECS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!globals.dialplan) {
|
||||
set_global_dialplan("default");
|
||||
}
|
||||
if (!globals.port) {
|
||||
globals.port = 4569;
|
||||
}
|
||||
|
||||
switch_config_close_file(&cfg);
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
SWITCH_MOD_DECLARE(switch_status) switch_module_runtime(void)
|
||||
{
|
||||
int res;
|
||||
int netfd;
|
||||
int refresh;
|
||||
struct iax_event *iaxevent = NULL;
|
||||
|
||||
load_config();
|
||||
|
||||
if (globals.debug) {
|
||||
iax_enable_debug();
|
||||
}
|
||||
if ((res = iax_init(globals.port) < 0)) {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Error Binding Port!\n");
|
||||
return SWITCH_STATUS_TERM;
|
||||
}
|
||||
|
||||
iax_set_error(iax_err_cb);
|
||||
iax_set_output(iax_out_cb);
|
||||
netfd = iax_get_fd();
|
||||
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "IAX Ready Port %d\n", globals.port);
|
||||
|
||||
for(;;) {
|
||||
|
||||
if (running == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Wait for an event.*/
|
||||
if ((iaxevent = iax_get_event(0)) == NULL) {
|
||||
switch_yield(1000);
|
||||
continue;
|
||||
} else if (iaxevent) {
|
||||
struct private_object *tech_pvt = iax_get_private(iaxevent->session);
|
||||
|
||||
if (globals.debug && iaxevent->etype != IAX_EVENT_VOICE) {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Event %d [%s]!\n",
|
||||
iaxevent->etype, IAXNAMES[iaxevent->etype]);
|
||||
}
|
||||
switch (iaxevent->etype) {
|
||||
case IAX_EVENT_REGACK:
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Registration completed successfully.\n");
|
||||
if (iaxevent->ies.refresh) refresh = iaxevent->ies.refresh;
|
||||
break;
|
||||
case IAX_EVENT_REGREJ:
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Registration failed.\n");
|
||||
break;
|
||||
case IAX_EVENT_TIMEOUT:
|
||||
break;
|
||||
case IAX_EVENT_ACCEPT:
|
||||
if (tech_pvt) {
|
||||
unsigned int cap = iax_session_get_capability(iaxevent->session);
|
||||
unsigned int format = iaxevent->ies.format;
|
||||
|
||||
if (iax_set_codec(tech_pvt, iaxevent->session, &format, &cap, IAX_SET) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "WTF? %d %d\n",iaxevent->ies.format, iaxevent->ies.capability);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Call accepted.\n");
|
||||
break;
|
||||
case IAX_EVENT_RINGA:
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Ringing heard.\n");
|
||||
break;
|
||||
case IAX_EVENT_PONG:
|
||||
// informative only
|
||||
break;
|
||||
case IAX_EVENT_ANSWER:
|
||||
// the other side answered our call
|
||||
if (tech_pvt) {
|
||||
switch_channel *channel;
|
||||
if ((channel = switch_core_session_get_channel(tech_pvt->session))) {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Answer %s\n", switch_channel_get_name(channel));
|
||||
switch_channel_answer(channel);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case IAX_EVENT_CONNECT:
|
||||
// incoming call detected
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE,
|
||||
"Incoming call connected %s, %s, %s %d/%d\n",
|
||||
iaxevent->ies.called_number,
|
||||
iaxevent->ies.calling_number,
|
||||
iaxevent->ies.calling_name,
|
||||
iaxevent->ies.format,
|
||||
iaxevent->ies.capability);
|
||||
|
||||
if (iaxevent) {
|
||||
switch_core_session *session;
|
||||
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "New Inbound Channel %s!\n", iaxevent->ies.calling_name);
|
||||
if ((session = switch_core_session_request(&channel_endpoint_interface, NULL))) {
|
||||
struct private_object *tech_pvt;
|
||||
switch_channel *channel;
|
||||
|
||||
if ((tech_pvt = (struct private_object *) switch_core_session_alloc(session, sizeof(struct private_object)))) {
|
||||
memset(tech_pvt, 0, sizeof(*tech_pvt));
|
||||
channel = switch_core_session_get_channel(session);
|
||||
switch_core_session_set_private(session, tech_pvt);
|
||||
tech_pvt->session = session;
|
||||
} else {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Hey where is my memory pool?\n");
|
||||
switch_core_session_destroy(&session);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if ((tech_pvt->caller_profile = switch_caller_profile_new(session,
|
||||
globals.dialplan,
|
||||
iaxevent->ies.calling_name,
|
||||
iaxevent->ies.calling_number,
|
||||
iax_get_peer_ip(iaxevent->session),
|
||||
iaxevent->ies.calling_ani,
|
||||
NULL,
|
||||
iaxevent->ies.called_number))) {
|
||||
char name[128];
|
||||
switch_channel_set_caller_profile(channel, tech_pvt->caller_profile);
|
||||
snprintf(name, sizeof(name), "IAX/%s-%04x", tech_pvt->caller_profile->destination_number, rand() & 0xffff);
|
||||
switch_channel_set_name(channel, name);
|
||||
}
|
||||
|
||||
if (iax_set_codec(tech_pvt, iaxevent->session,
|
||||
&iaxevent->ies.format,
|
||||
&iaxevent->ies.capability,
|
||||
IAX_SET) != SWITCH_STATUS_SUCCESS) {
|
||||
iax_reject(iaxevent->session, "Codec Error!");
|
||||
switch_core_session_destroy(&session);
|
||||
} else {
|
||||
tech_pvt->iax_session = iaxevent->session;
|
||||
tech_pvt->session = session;
|
||||
iax_accept(tech_pvt->iax_session, tech_pvt->codec);
|
||||
iax_ring_announce(tech_pvt->iax_session);
|
||||
switch_channel_set_state(channel, CS_INIT);
|
||||
switch_core_session_thread_launch(session);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case IAX_EVENT_REJECT:
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Rejected call.\n");
|
||||
case IAX_EVENT_BUSY:
|
||||
case IAX_EVENT_HANGUP:
|
||||
if (tech_pvt) {
|
||||
switch_channel *channel;
|
||||
|
||||
switch_clear_flag(tech_pvt, TFLAG_IO);
|
||||
switch_clear_flag(tech_pvt, TFLAG_VOICE);
|
||||
if ((channel = switch_core_session_get_channel(tech_pvt->session))) {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Hangup %s\n", switch_channel_get_name(channel));
|
||||
switch_set_flag(tech_pvt, TFLAG_HANGUP);
|
||||
switch_channel_hangup(channel);
|
||||
switch_thread_cond_signal(tech_pvt->cond);
|
||||
iaxevent->session = NULL;
|
||||
} else {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "No Session? %s\n", switch_test_flag(tech_pvt, TFLAG_VOICE) ? "yes" : "no");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case IAX_EVENT_CNG:
|
||||
// pseudo-silence
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "sending silence\n");
|
||||
break;
|
||||
case IAX_EVENT_VOICE:
|
||||
if (tech_pvt && (tech_pvt->read_frame.datalen = iaxevent->datalen)) {
|
||||
int bytes = tech_pvt->read_codec.implementation->encoded_bytes_per_frame;
|
||||
int frames = (int)(tech_pvt->read_frame.datalen / bytes);
|
||||
tech_pvt->read_frame.samples = frames * tech_pvt->read_codec.implementation->samples_per_frame;
|
||||
memcpy(tech_pvt->read_frame.data, iaxevent->data, iaxevent->datalen);
|
||||
/* wake up the i/o thread*/
|
||||
switch_set_flag(tech_pvt, TFLAG_VOICE);
|
||||
switch_thread_cond_signal(tech_pvt->cond);
|
||||
}
|
||||
break;
|
||||
case IAX_EVENT_TRANSFER:
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Call transfer occurred.\n");
|
||||
//session[0] = iaxevent->session;
|
||||
break;
|
||||
case IAX_EVENT_DTMF:
|
||||
if (tech_pvt) {
|
||||
switch_channel *channel;
|
||||
if ((channel = switch_core_session_get_channel(tech_pvt->session))) {
|
||||
char str[2] = {iaxevent->subclass};
|
||||
if (globals.debug) {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "%s DTMF %s\n", str, switch_channel_get_name(channel));
|
||||
}
|
||||
switch_channel_queue_dtmf(channel, str);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Don't know what to do with IAX event %d.\n", iaxevent->etype);
|
||||
break;
|
||||
}
|
||||
|
||||
iax_event_free(iaxevent);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
running = 0;
|
||||
|
||||
return SWITCH_STATUS_TERM;
|
||||
}
|
||||
|
||||
|
||||
SWITCH_MOD_DECLARE(switch_status) switch_module_shutdown(void)
|
||||
{
|
||||
int x = 0;
|
||||
|
||||
running = -1;
|
||||
|
||||
iax_shutdown();
|
||||
|
||||
while (running) {
|
||||
if (x++ > 100) {
|
||||
break;
|
||||
}
|
||||
switch_yield(20000);
|
||||
}
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
#CFLAGS += -I/usr/src/common/src
|
||||
LDFLAGS += -liax
|
||||
|
||||
all: $(MOD).so
|
||||
|
||||
$(MOD).so: $(MOD).c
|
||||
$(CC) $(CFLAGS) -fPIC -c $(MOD).c -o $(MOD).o
|
||||
$(CC) $(SOLINK) -o $(MOD).so $(MOD).o $(LDFLAGS)
|
||||
|
||||
clean:
|
||||
rm -fr *.so *.o *~
|
||||
|
||||
@@ -0,0 +1,385 @@
|
||||
/*
|
||||
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
* Copyright (C) 2005/2006, Anthony Minessale II <anthmct@yahoo.com>
|
||||
*
|
||||
* Version: MPL 1.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Anthony Minessale II <anthmct@yahoo.com>
|
||||
* Portions created by the Initial Developer are Copyright (C)
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Anthony Minessale II <anthmct@yahoo.com>
|
||||
*
|
||||
*
|
||||
* mod_opalchan.c -- OPAL2 Endpoint Module
|
||||
*
|
||||
*/
|
||||
#include <switch.h>
|
||||
|
||||
|
||||
static const char modname[] = "mod_woomera";
|
||||
|
||||
static switch_memory_pool *module_pool;
|
||||
|
||||
|
||||
typedef enum {
|
||||
TFLAG_IO = (1 << 0),
|
||||
TFLAG_INBOUND = (1 << 1),
|
||||
TFLAG_OUTBOUND = (1 << 2),
|
||||
TFLAG_DTMF = (1 << 3),
|
||||
} TFLAGS;
|
||||
|
||||
static struct {
|
||||
int debug;
|
||||
int port;
|
||||
} globals;
|
||||
|
||||
struct private_object {
|
||||
unsigned int flags;
|
||||
struct switch_frame frame;
|
||||
unsigned char databuf[1024];
|
||||
switch_core_session *session;
|
||||
switch_caller_profile *caller_profile;
|
||||
};
|
||||
|
||||
|
||||
static const switch_endpoint_interface channel_endpoint_interface;
|
||||
|
||||
static switch_status channel_on_init(switch_core_session *session);
|
||||
static switch_status channel_on_hangup(switch_core_session *session);
|
||||
static switch_status channel_on_ring(switch_core_session *session);
|
||||
static switch_status channel_on_loopback(switch_core_session *session);
|
||||
static switch_status channel_on_transmit(switch_core_session *session);
|
||||
static switch_status channel_outgoing_channel(switch_core_session *session, switch_caller_profile *outbound_profile, switch_core_session **new_session);
|
||||
static switch_status channel_read_frame(switch_core_session *session, switch_frame **frame, int timeout, switch_io_flag flags);
|
||||
static switch_status channel_write_frame(switch_core_session *session, switch_frame *frame, int timeout, switch_io_flag flags);
|
||||
static switch_status channel_kill_channel(switch_core_session *session, int sig);
|
||||
|
||||
|
||||
/*
|
||||
State methods they get called when the state changes to the specific state
|
||||
returning SWITCH_STATUS_SUCCESS tells the core to execute the standard state method next
|
||||
so if you fully implement the state you can return SWITCH_STATUS_FALSE to skip it.
|
||||
*/
|
||||
static switch_status channel_on_init(switch_core_session *session)
|
||||
{
|
||||
switch_channel *channel;
|
||||
struct private_object *tech_pvt = NULL;
|
||||
|
||||
tech_pvt = switch_core_session_get_private(session);
|
||||
assert(tech_pvt != NULL);
|
||||
|
||||
channel = switch_core_session_get_channel(session);
|
||||
assert(channel != NULL);
|
||||
|
||||
tech_pvt->frame.data = tech_pvt->databuf;
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static switch_status channel_on_ring(switch_core_session *session)
|
||||
{
|
||||
switch_channel *channel = NULL;
|
||||
struct private_object *tech_pvt = NULL;
|
||||
|
||||
channel = switch_core_session_get_channel(session);
|
||||
assert(channel != NULL);
|
||||
|
||||
tech_pvt = switch_core_session_get_private(session);
|
||||
assert(tech_pvt != NULL);
|
||||
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "%s CHANNEL RING\n", switch_channel_get_name(channel));
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static switch_status channel_on_execute(switch_core_session *session)
|
||||
{
|
||||
|
||||
switch_channel *channel = NULL;
|
||||
struct private_object *tech_pvt = NULL;
|
||||
|
||||
channel = switch_core_session_get_channel(session);
|
||||
assert(channel != NULL);
|
||||
|
||||
tech_pvt = switch_core_session_get_private(session);
|
||||
assert(tech_pvt != NULL);
|
||||
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "%s CHANNEL EXECUTE\n", switch_channel_get_name(channel));
|
||||
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static switch_status channel_on_hangup(switch_core_session *session)
|
||||
{
|
||||
switch_channel *channel = NULL;
|
||||
struct private_object *tech_pvt = NULL;
|
||||
|
||||
channel = switch_core_session_get_channel(session);
|
||||
assert(channel != NULL);
|
||||
|
||||
tech_pvt = switch_core_session_get_private(session);
|
||||
assert(tech_pvt != NULL);
|
||||
|
||||
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "%s CHANNEL HANGUP\n", switch_channel_get_name(channel));
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static switch_status channel_kill_channel(switch_core_session *session, int sig)
|
||||
{
|
||||
switch_channel *channel = NULL;
|
||||
struct private_object *tech_pvt = NULL;
|
||||
|
||||
channel = switch_core_session_get_channel(session);
|
||||
assert(channel != NULL);
|
||||
|
||||
tech_pvt = switch_core_session_get_private(session);
|
||||
assert(tech_pvt != NULL);
|
||||
|
||||
switch_channel_hangup(channel);
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "%s CHANNEL KILL\n", switch_channel_get_name(channel));
|
||||
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static switch_status channel_on_loopback(switch_core_session *session)
|
||||
{
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "CHANNEL LOOPBACK\n");
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static switch_status channel_on_transmit(switch_core_session *session)
|
||||
{
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "CHANNEL TRANSMIT\n");
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/* Make sure when you have 2 sessions in the same scope that you pass the appropriate one to the routines
|
||||
that allocate memory or you will have 1 channel with memory allocated from another channel's pool!
|
||||
*/
|
||||
static switch_status channel_outgoing_channel(switch_core_session *session, switch_caller_profile *outbound_profile, switch_core_session **new_session)
|
||||
{
|
||||
if ((*new_session = switch_core_session_request(&channel_endpoint_interface, NULL))) {
|
||||
struct private_object *tech_pvt;
|
||||
switch_channel *channel, *orig_channel;
|
||||
switch_caller_profile *caller_profile, *originator_caller_profile = NULL;
|
||||
|
||||
if ((tech_pvt = (struct private_object *) switch_core_session_alloc(*new_session, sizeof(struct private_object)))) {
|
||||
memset(tech_pvt, 0, sizeof(*tech_pvt));
|
||||
channel = switch_core_session_get_channel(*new_session);
|
||||
switch_core_session_set_private(*new_session, tech_pvt);
|
||||
tech_pvt->session = *new_session;
|
||||
} else {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Hey where is my memory pool?\n");
|
||||
switch_core_session_destroy(new_session);
|
||||
return SWITCH_STATUS_GENERR;
|
||||
}
|
||||
|
||||
if (outbound_profile) {
|
||||
char name[128];
|
||||
caller_profile = switch_caller_profile_clone(*new_session, outbound_profile);
|
||||
switch_channel_set_caller_profile(channel, caller_profile);
|
||||
tech_pvt->caller_profile = caller_profile;
|
||||
snprintf(name, sizeof(name), "Opal/%s-%04x", caller_profile->destination_number, rand() & 0xffff);
|
||||
switch_channel_set_name(channel, name);
|
||||
} else {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Doh! no caller profile\n");
|
||||
switch_core_session_destroy(new_session);
|
||||
return SWITCH_STATUS_GENERR;
|
||||
}
|
||||
|
||||
/* (session == NULL) means it was originated from the core not from another channel */
|
||||
if (session && (orig_channel = switch_core_session_get_channel(session))) {
|
||||
switch_caller_profile *cloned_profile;
|
||||
|
||||
if ((originator_caller_profile = switch_channel_get_caller_profile(orig_channel))) {
|
||||
cloned_profile = switch_caller_profile_clone(*new_session, originator_caller_profile);
|
||||
switch_channel_set_originator_caller_profile(channel, cloned_profile);
|
||||
}
|
||||
}
|
||||
|
||||
switch_channel_set_flag(channel, CF_OUTBOUND);
|
||||
switch_set_flag(tech_pvt, TFLAG_OUTBOUND);
|
||||
switch_channel_set_state(channel, CS_INIT);
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
return SWITCH_STATUS_GENERR;
|
||||
|
||||
}
|
||||
|
||||
static switch_status channel_waitfor_read(switch_core_session *session, int ms)
|
||||
{
|
||||
struct private_object *tech_pvt = NULL;
|
||||
|
||||
tech_pvt = switch_core_session_get_private(session);
|
||||
assert(tech_pvt != NULL);
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static switch_status channel_waitfor_write(switch_core_session *session, int ms)
|
||||
{
|
||||
struct private_object *tech_pvt = NULL;
|
||||
|
||||
tech_pvt = switch_core_session_get_private(session);
|
||||
assert(tech_pvt != NULL);
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
|
||||
}
|
||||
|
||||
static switch_status channel_read_frame(switch_core_session *session, switch_frame **frame, int timeout, switch_io_flag flags)
|
||||
{
|
||||
switch_channel *channel = NULL;
|
||||
struct private_object *tech_pvt = NULL;
|
||||
//switch_frame *pframe;
|
||||
//switch_status status;
|
||||
|
||||
channel = switch_core_session_get_channel(session);
|
||||
assert(channel != NULL);
|
||||
|
||||
tech_pvt = switch_core_session_get_private(session);
|
||||
assert(tech_pvt != NULL);
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static switch_status channel_write_frame(switch_core_session *session, switch_frame *frame, int timeout, switch_io_flag flags)
|
||||
{
|
||||
switch_channel *channel = NULL;
|
||||
struct private_object *tech_pvt = NULL;
|
||||
//switch_frame *pframe;
|
||||
|
||||
channel = switch_core_session_get_channel(session);
|
||||
assert(channel != NULL);
|
||||
|
||||
tech_pvt = switch_core_session_get_private(session);
|
||||
assert(tech_pvt != NULL);
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
|
||||
}
|
||||
|
||||
static const switch_event_handler_table channel_event_handlers = {
|
||||
/*.on_init*/ channel_on_init,
|
||||
/*.on_ring*/ channel_on_ring,
|
||||
/*.on_execute*/ channel_on_execute,
|
||||
/*.on_hangup*/ channel_on_hangup,
|
||||
/*.on_loopback*/ channel_on_loopback,
|
||||
/*.on_transmit*/ channel_on_transmit
|
||||
};
|
||||
|
||||
static const switch_io_routines channel_io_routines = {
|
||||
/*.outgoing_channel*/ channel_outgoing_channel,
|
||||
/*.answer_channel*/ NULL,
|
||||
/*.read_frame*/ channel_read_frame,
|
||||
/*.write_frame*/ channel_write_frame,
|
||||
/*.kill_channel*/ channel_kill_channel,
|
||||
/*.waitfor_read*/ channel_waitfor_read,
|
||||
/*.waitfor_write*/ channel_waitfor_write
|
||||
};
|
||||
|
||||
static const switch_endpoint_interface channel_endpoint_interface = {
|
||||
/*.interface_name*/ "opal",
|
||||
/*.io_routines*/ &channel_io_routines,
|
||||
/*.event_handlers*/ &channel_event_handlers,
|
||||
/*.private*/ NULL,
|
||||
/*.next*/ NULL
|
||||
};
|
||||
|
||||
static const switch_loadable_module_interface channel_module_interface = {
|
||||
/*.module_name*/ modname,
|
||||
/*.endpoint_interface*/ &channel_endpoint_interface,
|
||||
/*.timer_interface*/ NULL,
|
||||
/*.dialplan_interface*/ NULL,
|
||||
/*.codec_interface*/ NULL,
|
||||
/*.application_interface*/ NULL
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
|
||||
|
||||
|
||||
SWITCH_MOD_DECLARE(switch_status) switch_module_shutdown(void)
|
||||
{
|
||||
int x = 0;
|
||||
opal_profile_thread_running(&default_profile, 1, 0);
|
||||
while (!opal_profile_thread_running(&default_profile, 0, 0)) {
|
||||
opal_socket_close(&default_profile.opal_socket);
|
||||
if (x++ > 10) {
|
||||
break;
|
||||
}
|
||||
switch_yield(1);
|
||||
}
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
*/
|
||||
|
||||
SWITCH_MOD_DECLARE(switch_status) switch_module_load(const switch_loadable_module_interface **interface, char *filename) {
|
||||
|
||||
switch_config cfg;
|
||||
char *var, *val;
|
||||
char *cf = "opal.conf";
|
||||
|
||||
memset(&globals, 0, sizeof(globals));
|
||||
|
||||
if (!switch_config_open_file(&cfg, cf)) {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "open of %s failed\n", cf);
|
||||
return SWITCH_STATUS_TERM;
|
||||
}
|
||||
|
||||
|
||||
while (switch_config_next_pair(&cfg, &var, &val)) {
|
||||
if (!strcasecmp(cfg.category, "settings")) {
|
||||
if (!strcmp(var, "debug")) {
|
||||
globals.debug = atoi(val);
|
||||
} else if (!strcmp(var, "port")) {
|
||||
globals.port = atoi(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch_config_close_file(&cfg);
|
||||
|
||||
|
||||
if (switch_core_new_memory_pool(&module_pool) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "OH OH no pool\n");
|
||||
return SWITCH_STATUS_TERM;
|
||||
}
|
||||
|
||||
/* connect my internal structure to the blank pointer passed to me */
|
||||
*interface = &channel_module_interface;
|
||||
|
||||
/* indicate that the module should continue to be loaded */
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
SWITCH_MOD_DECLARE(switch_status) switch_module_runtime(void)
|
||||
{
|
||||
|
||||
return SWITCH_STATUS_TERM;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
LDFLAGS += -lportaudio -L/usr/local/lib
|
||||
MYOBJS = pablio.o ringbuffer.o
|
||||
|
||||
all: depends $(MOD).so
|
||||
|
||||
depends:
|
||||
$(BASE)/buildlib.sh $(BASE) install portaudio.tar.gz --prefix=$(PREFIX)
|
||||
%.o: %.c
|
||||
$(CC) -fPIC $(CFLAGS) -c -o $@ $<
|
||||
|
||||
$(MOD).so: $(MOD).c $(MYOBJS)
|
||||
$(CC) $(CFLAGS) -fPIC -c $(MOD).c -o $(MOD).o
|
||||
$(CC) $(SOLINK) -o $(MOD).so $(MOD).o $(MYOBJS) $(LDFLAGS)
|
||||
|
||||
|
||||
|
||||
clean:
|
||||
rm -fr *.so *.o *~
|
||||
|
||||
@@ -0,0 +1,228 @@
|
||||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="8.00"
|
||||
Name="mod_PortAudio"
|
||||
ProjectGUID="{5FD31A25-5D83-4794-8BEE-904DAD84CE71}"
|
||||
RootNamespace="mod_PortAudio"
|
||||
Keyword="Win32Proj"
|
||||
>
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"
|
||||
/>
|
||||
</Platforms>
|
||||
<ToolFiles>
|
||||
</ToolFiles>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
OutputDirectory="Debug"
|
||||
IntermediateDirectory="Debug"
|
||||
ConfigurationType="2"
|
||||
InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
|
||||
CharacterSet="2"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
CommandLine="cscript /nologo $(InputDir)..\..\..\w32\vsnet\getlibs.vbs Mod_PortAudio Debug"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
AdditionalIncludeDirectories=""$(InputDir)..\..\include";"$(InputDir)";"$(InputDir)..\..\..\libs\include";"$(InputDir)..\..\..\libs\portaudio\pa_common";"$(InputDir)..\..\..\libs\portaudio\pa_win_wmme""
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;MOD_EXPORTS"
|
||||
MinimalRebuild="true"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="1"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="true"
|
||||
DebugInformationFormat="4"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalOptions="/NODEFAULTLIB:LIMBCTD"
|
||||
AdditionalDependencies="Ws2_32.lib Iphlpapi.lib PAStaticWMMED.lib Winmm.lib"
|
||||
OutputFile="..\..\..\w32\vsnet\$(OutDir)/mod/mod_PortAudio.dll"
|
||||
LinkIncremental="2"
|
||||
AdditionalLibraryDirectories=""$(InputDir)..\..\..\libs\portaudio\winvc\Lib""
|
||||
GenerateDebugInformation="true"
|
||||
ProgramDatabaseFile="$(OutDir)/mod_PortAudio.pdb"
|
||||
SubSystem="2"
|
||||
ImportLibrary="$(OutDir)/mod_PortAudio.lib"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
OutputDirectory="Release"
|
||||
IntermediateDirectory="Release"
|
||||
ConfigurationType="2"
|
||||
InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
|
||||
CharacterSet="2"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
CommandLine="cscript /nologo $(InputDir)..\..\..\w32\vsnet\getlibs.vbs Mod_PortAudio Release"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
AdditionalIncludeDirectories=""$(InputDir)..\..\include";"$(InputDir)";"$(InputDir)..\..\..\libs\include";"$(InputDir)..\..\..\libs\portaudio\pa_common";"$(InputDir)..\..\..\libs\portaudio\pa_win_wmme""
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;MOD_EXPORTS"
|
||||
RuntimeLibrary="0"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="true"
|
||||
DebugInformationFormat="3"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="Ws2_32.lib Iphlpapi.lib PAStaticWMME.lib Winmm.lib"
|
||||
OutputFile="..\..\..\w32\vsnet\$(OutDir)/mod/mod_PortAudio.dll"
|
||||
LinkIncremental="1"
|
||||
AdditionalLibraryDirectories=""$(InputDir)..\..\..\libs\portaudio\winvc\Lib""
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="2"
|
||||
OptimizeReferences="2"
|
||||
EnableCOMDATFolding="2"
|
||||
ImportLibrary="$(OutDir)/mod_PortAudio.lib"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<Filter
|
||||
Name="Source Files"
|
||||
Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
|
||||
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\mod_PortAudio.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\pablio.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\ringbuffer.c"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Header Files"
|
||||
Filter="h;hpp;hxx;hm;inl;inc;xsd"
|
||||
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\pablio.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\ringbuffer.h"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Resource Files"
|
||||
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
|
||||
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
|
||||
>
|
||||
</Filter>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
||||
@@ -0,0 +1,934 @@
|
||||
/*
|
||||
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
* Copyright (C) 2005/2006, Anthony Minessale II <anthmct@yahoo.com>
|
||||
*
|
||||
* Version: MPL 1.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Anthony Minessale II <anthmct@yahoo.com>
|
||||
* Portions created by the Initial Developer are Copyright (C)
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Anthony Minessale II <anthmct@yahoo.com>
|
||||
*
|
||||
*
|
||||
* mod_portaudio.c -- PortAudio Endpoint Module
|
||||
*
|
||||
*/
|
||||
#include <switch.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "pablio.h"
|
||||
#include <string.h>
|
||||
|
||||
#define MY_EVENT_RINGING "portaudio::ringing"
|
||||
|
||||
static const char modname[] = "mod_portaudio";
|
||||
|
||||
static switch_memory_pool *module_pool;
|
||||
//static int running = 1;
|
||||
|
||||
#define SAMPLE_TYPE paInt16
|
||||
//#define SAMPLE_TYPE paFloat32
|
||||
typedef short SAMPLE;
|
||||
|
||||
typedef enum {
|
||||
TFLAG_IO = (1 << 0),
|
||||
TFLAG_INBOUND = (1 << 1),
|
||||
TFLAG_OUTBOUND = (1 << 2),
|
||||
TFLAG_DTMF = (1 << 3),
|
||||
TFLAG_VOICE = (1 << 4),
|
||||
TFLAG_HANGUP = (1 << 5),
|
||||
TFLAG_LINEAR = (1 << 6),
|
||||
TFLAG_ANSWER = (1 << 7)
|
||||
} TFLAGS;
|
||||
|
||||
typedef enum {
|
||||
GFLAG_MY_CODEC_PREFS = (1 << 0)
|
||||
} GFLAGS;
|
||||
|
||||
static struct {
|
||||
int debug;
|
||||
int port;
|
||||
char *cid_name;
|
||||
char *cid_num;
|
||||
char *dialplan;
|
||||
unsigned int flags;
|
||||
int indev;
|
||||
int outdev;
|
||||
int call_id;
|
||||
switch_hash *call_hash;
|
||||
switch_mutex_t *device_lock;
|
||||
int sample_rate;
|
||||
} globals;
|
||||
|
||||
struct private_object {
|
||||
unsigned int flags;
|
||||
switch_codec read_codec;
|
||||
switch_codec write_codec;
|
||||
struct switch_frame read_frame;
|
||||
unsigned char databuf[1024];
|
||||
switch_core_session *session;
|
||||
switch_caller_profile *caller_profile;
|
||||
char call_id[50];
|
||||
PaError err;
|
||||
PABLIO_Stream *audio_in;
|
||||
PABLIO_Stream *audio_out;
|
||||
int indev;
|
||||
int outdev;
|
||||
};
|
||||
|
||||
SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_dialplan, globals.dialplan)
|
||||
SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_cid_name, globals.cid_name)
|
||||
SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_cid_num, globals.cid_num)
|
||||
|
||||
|
||||
static const switch_endpoint_interface channel_endpoint_interface;
|
||||
|
||||
static switch_status channel_on_init(switch_core_session *session);
|
||||
static switch_status channel_on_hangup(switch_core_session *session);
|
||||
static switch_status channel_on_ring(switch_core_session *session);
|
||||
static switch_status channel_on_loopback(switch_core_session *session);
|
||||
static switch_status channel_on_transmit(switch_core_session *session);
|
||||
static switch_status channel_outgoing_channel(switch_core_session *session, switch_caller_profile *outbound_profile, switch_core_session **new_session);
|
||||
static switch_status channel_read_frame(switch_core_session *session, switch_frame **frame, int timeout, switch_io_flag flags);
|
||||
static switch_status channel_write_frame(switch_core_session *session, switch_frame *frame, int timeout, switch_io_flag flags);
|
||||
static switch_status channel_kill_channel(switch_core_session *session, int sig);
|
||||
static switch_status engage_device(struct private_object *tech_pvt);
|
||||
static int dump_info(void);
|
||||
static switch_status load_config(void);
|
||||
static int get_dev_by_name(char *name, int in);
|
||||
static switch_status place_call(char *dest, char *out, size_t outlen);
|
||||
static switch_status hup_call(char *callid, char *out, size_t outlen);
|
||||
static switch_status call_info(char *callid, char *out, size_t outlen);
|
||||
static switch_status send_dtmf(char *callid, char *out, size_t outlen);
|
||||
static switch_status answer_call(char *callid, char *out, size_t outlen);
|
||||
|
||||
/*
|
||||
State methods they get called when the state changes to the specific state
|
||||
returning SWITCH_STATUS_SUCCESS tells the core to execute the standard state method next
|
||||
so if you fully implement the state you can return SWITCH_STATUS_FALSE to skip it.
|
||||
*/
|
||||
static switch_status channel_on_init(switch_core_session *session)
|
||||
{
|
||||
switch_channel *channel;
|
||||
struct private_object *tech_pvt = NULL;
|
||||
|
||||
tech_pvt = switch_core_session_get_private(session);
|
||||
assert(tech_pvt != NULL);
|
||||
|
||||
channel = switch_core_session_get_channel(session);
|
||||
assert(channel != NULL);
|
||||
|
||||
tech_pvt->read_frame.data = tech_pvt->databuf;
|
||||
tech_pvt->read_frame.buflen = sizeof(tech_pvt->databuf);
|
||||
|
||||
switch_set_flag(tech_pvt, TFLAG_IO);
|
||||
|
||||
/* Move Channel's State Machine to RING */
|
||||
switch_channel_set_state(channel, CS_RING);
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static switch_status channel_on_ring(switch_core_session *session)
|
||||
{
|
||||
switch_channel *channel = NULL;
|
||||
struct private_object *tech_pvt = NULL;
|
||||
|
||||
channel = switch_core_session_get_channel(session);
|
||||
assert(channel != NULL);
|
||||
|
||||
tech_pvt = switch_core_session_get_private(session);
|
||||
assert(tech_pvt != NULL);
|
||||
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "%s CHANNEL RING\n", switch_channel_get_name(channel));
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static switch_status channel_on_execute(switch_core_session *session)
|
||||
{
|
||||
|
||||
switch_channel *channel = NULL;
|
||||
struct private_object *tech_pvt = NULL;
|
||||
|
||||
channel = switch_core_session_get_channel(session);
|
||||
assert(channel != NULL);
|
||||
|
||||
tech_pvt = switch_core_session_get_private(session);
|
||||
assert(tech_pvt != NULL);
|
||||
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "%s CHANNEL EXECUTE\n", switch_channel_get_name(channel));
|
||||
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static void deactivate_audio_device(struct private_object *tech_pvt)
|
||||
{
|
||||
switch_mutex_lock(globals.device_lock);
|
||||
if (tech_pvt->audio_in) {
|
||||
CloseAudioStream(tech_pvt->audio_in);
|
||||
tech_pvt->audio_in = NULL;
|
||||
}
|
||||
if (tech_pvt->audio_out) {
|
||||
CloseAudioStream(tech_pvt->audio_out);
|
||||
tech_pvt->audio_out = NULL;
|
||||
}
|
||||
switch_mutex_unlock(globals.device_lock);
|
||||
}
|
||||
|
||||
static switch_status channel_on_hangup(switch_core_session *session)
|
||||
{
|
||||
switch_channel *channel = NULL;
|
||||
struct private_object *tech_pvt = NULL;
|
||||
|
||||
channel = switch_core_session_get_channel(session);
|
||||
assert(channel != NULL);
|
||||
|
||||
tech_pvt = switch_core_session_get_private(session);
|
||||
assert(tech_pvt != NULL);
|
||||
|
||||
switch_clear_flag(tech_pvt, TFLAG_IO);
|
||||
switch_core_hash_delete(globals.call_hash, tech_pvt->call_id);
|
||||
|
||||
switch_core_codec_destroy(&tech_pvt->read_codec);
|
||||
switch_core_codec_destroy(&tech_pvt->write_codec);
|
||||
|
||||
deactivate_audio_device(tech_pvt);
|
||||
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "%s CHANNEL HANGUP\n", switch_channel_get_name(channel));
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static switch_status channel_kill_channel(switch_core_session *session, int sig)
|
||||
{
|
||||
switch_channel *channel = NULL;
|
||||
struct private_object *tech_pvt = NULL;
|
||||
|
||||
channel = switch_core_session_get_channel(session);
|
||||
assert(channel != NULL);
|
||||
|
||||
tech_pvt = switch_core_session_get_private(session);
|
||||
assert(tech_pvt != NULL);
|
||||
|
||||
switch_clear_flag(tech_pvt, TFLAG_IO);
|
||||
deactivate_audio_device(tech_pvt);
|
||||
switch_channel_hangup(channel);
|
||||
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "%s CHANNEL KILL\n", switch_channel_get_name(channel));
|
||||
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static switch_status channel_on_loopback(switch_core_session *session)
|
||||
{
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "CHANNEL LOOPBACK\n");
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static switch_status channel_on_transmit(switch_core_session *session)
|
||||
{
|
||||
switch_channel *channel = NULL;
|
||||
struct private_object *tech_pvt = NULL;
|
||||
switch_time_t last;
|
||||
int waitsec = 5 * 1000000;
|
||||
|
||||
channel = switch_core_session_get_channel(session);
|
||||
assert(channel != NULL);
|
||||
|
||||
tech_pvt = switch_core_session_get_private(session);
|
||||
assert(tech_pvt != NULL);
|
||||
|
||||
last = switch_time_now() - waitsec;
|
||||
|
||||
/* Turn on the device */
|
||||
engage_device(tech_pvt);
|
||||
|
||||
while(switch_channel_get_state(channel) == CS_TRANSMIT && !switch_test_flag(tech_pvt, TFLAG_ANSWER)) {
|
||||
if (switch_time_now() - last >= waitsec) {
|
||||
char buf[512];
|
||||
switch_event *event;
|
||||
|
||||
snprintf(buf, sizeof(buf), "BRRRRING! BRRRRING! call %s\n", tech_pvt->call_id);
|
||||
|
||||
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, MY_EVENT_RINGING) == SWITCH_STATUS_SUCCESS) {
|
||||
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_info", buf);
|
||||
switch_event_fire(&event);
|
||||
}
|
||||
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "%s\n", buf);
|
||||
last = switch_time_now();
|
||||
}
|
||||
switch_yield(50000);
|
||||
}
|
||||
|
||||
|
||||
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "CHANNEL TRANSMIT\n");
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/* Make sure when you have 2 sessions in the same scope that you pass the appropriate one to the routines
|
||||
that allocate memory or you will have 1 channel with memory allocated from another channel's pool!
|
||||
*/
|
||||
static switch_status channel_outgoing_channel(switch_core_session *session, switch_caller_profile *outbound_profile, switch_core_session **new_session)
|
||||
{
|
||||
if ((*new_session = switch_core_session_request(&channel_endpoint_interface, NULL))) {
|
||||
struct private_object *tech_pvt;
|
||||
switch_channel *channel;
|
||||
switch_caller_profile *caller_profile;
|
||||
|
||||
if ((tech_pvt = (struct private_object *) switch_core_session_alloc(*new_session, sizeof(struct private_object)))) {
|
||||
memset(tech_pvt, 0, sizeof(*tech_pvt));
|
||||
channel = switch_core_session_get_channel(*new_session);
|
||||
switch_core_session_set_private(*new_session, tech_pvt);
|
||||
tech_pvt->session = *new_session;
|
||||
} else {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Hey where is my memory pool?\n");
|
||||
switch_core_session_destroy(new_session);
|
||||
return SWITCH_STATUS_GENERR;
|
||||
}
|
||||
|
||||
if (outbound_profile) {
|
||||
char name[128];
|
||||
caller_profile = switch_caller_profile_clone(*new_session, outbound_profile);
|
||||
switch_channel_set_caller_profile(channel, caller_profile);
|
||||
tech_pvt->caller_profile = caller_profile;
|
||||
snprintf(name, sizeof(name), "PortAudio/%s-%04x", caller_profile->destination_number ? caller_profile->destination_number : modname, rand() & 0xffff);
|
||||
switch_channel_set_name(channel, name);
|
||||
} else {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Doh! no caller profile\n");
|
||||
switch_core_session_destroy(new_session);
|
||||
return SWITCH_STATUS_GENERR;
|
||||
}
|
||||
|
||||
switch_channel_set_flag(channel, CF_OUTBOUND);
|
||||
switch_set_flag(tech_pvt, TFLAG_OUTBOUND);
|
||||
switch_channel_set_state(channel, CS_INIT);
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
return SWITCH_STATUS_GENERR;
|
||||
|
||||
}
|
||||
|
||||
static switch_status channel_waitfor_read(switch_core_session *session, int ms)
|
||||
{
|
||||
struct private_object *tech_pvt = NULL;
|
||||
|
||||
tech_pvt = switch_core_session_get_private(session);
|
||||
assert(tech_pvt != NULL);
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static switch_status channel_waitfor_write(switch_core_session *session, int ms)
|
||||
{
|
||||
struct private_object *tech_pvt = NULL;
|
||||
|
||||
tech_pvt = switch_core_session_get_private(session);
|
||||
assert(tech_pvt != NULL);
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
|
||||
}
|
||||
|
||||
static switch_status channel_send_dtmf(switch_core_session *session, char *dtmf)
|
||||
{
|
||||
struct private_object *tech_pvt = NULL;
|
||||
|
||||
tech_pvt = switch_core_session_get_private(session);
|
||||
assert(tech_pvt != NULL);
|
||||
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "DTMF ON CALL %s [%s]\n", tech_pvt->call_id, dtmf);
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static switch_status channel_read_frame(switch_core_session *session, switch_frame **frame, int timeout, switch_io_flag flags)
|
||||
{
|
||||
switch_channel *channel = NULL;
|
||||
struct private_object *tech_pvt = NULL;
|
||||
int samples;
|
||||
switch_status status = SWITCH_STATUS_FALSE;
|
||||
|
||||
channel = switch_core_session_get_channel(session);
|
||||
assert(channel != NULL);
|
||||
|
||||
tech_pvt = switch_core_session_get_private(session);
|
||||
assert(tech_pvt != NULL);
|
||||
|
||||
if (!switch_test_flag(tech_pvt, TFLAG_IO)) {
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
|
||||
|
||||
switch_mutex_lock(globals.device_lock);
|
||||
if (tech_pvt->audio_in &&
|
||||
(samples = ReadAudioStream(tech_pvt->audio_in, tech_pvt->read_frame.data, tech_pvt->read_codec.implementation->samples_per_frame))) {
|
||||
tech_pvt->read_frame.datalen = samples * 2;
|
||||
tech_pvt->read_frame.samples = samples;
|
||||
*frame = &tech_pvt->read_frame;
|
||||
status = SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
switch_mutex_unlock(globals.device_lock);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static switch_status channel_write_frame(switch_core_session *session, switch_frame *frame, int timeout, switch_io_flag flags)
|
||||
{
|
||||
switch_channel *channel = NULL;
|
||||
struct private_object *tech_pvt = NULL;
|
||||
switch_status status = SWITCH_STATUS_FALSE;
|
||||
channel = switch_core_session_get_channel(session);
|
||||
assert(channel != NULL);
|
||||
|
||||
tech_pvt = switch_core_session_get_private(session);
|
||||
assert(tech_pvt != NULL);
|
||||
|
||||
if(!switch_test_flag(tech_pvt, TFLAG_IO)) {
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
|
||||
//switch_mutex_lock(globals.device_lock);
|
||||
if (tech_pvt->audio_out) {
|
||||
WriteAudioStream(tech_pvt->audio_out, (short *)frame->data, (int)(frame->datalen / sizeof(SAMPLE)));
|
||||
status = SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
//switch_mutex_unlock(globals.device_lock);
|
||||
|
||||
return status;
|
||||
|
||||
}
|
||||
|
||||
static switch_status channel_answer_channel(switch_core_session *session)
|
||||
{
|
||||
struct private_object *tech_pvt;
|
||||
switch_channel *channel = NULL;
|
||||
|
||||
channel = switch_core_session_get_channel(session);
|
||||
assert(channel != NULL);
|
||||
|
||||
tech_pvt = switch_core_session_get_private(session);
|
||||
assert(tech_pvt != NULL);
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static struct switch_api_interface send_dtmf_interface = {
|
||||
/*.interface_name*/ "padtmf",
|
||||
/*.desc*/ "PortAudio Dial DTMF",
|
||||
/*.function*/ send_dtmf,
|
||||
/*.next*/ NULL
|
||||
};
|
||||
|
||||
static struct switch_api_interface answer_call_interface = {
|
||||
/*.interface_name*/ "paoffhook",
|
||||
/*.desc*/ "PortAudio Answer Call",
|
||||
/*.function*/ answer_call,
|
||||
/*.next*/ &send_dtmf_interface
|
||||
};
|
||||
|
||||
static struct switch_api_interface channel_info_interface = {
|
||||
/*.interface_name*/ "painfo",
|
||||
/*.desc*/ "PortAudio Call Info",
|
||||
/*.function*/ call_info,
|
||||
/*.next*/ &answer_call_interface
|
||||
};
|
||||
|
||||
static struct switch_api_interface channel_hup_interface = {
|
||||
/*.interface_name*/ "pahup",
|
||||
/*.desc*/ "PortAudio Hangup Call",
|
||||
/*.function*/ hup_call,
|
||||
/*.next*/ &channel_info_interface
|
||||
};
|
||||
|
||||
static struct switch_api_interface channel_api_interface = {
|
||||
/*.interface_name*/ "pacall",
|
||||
/*.desc*/ "PortAudio Call",
|
||||
/*.function*/ place_call,
|
||||
/*.next*/ &channel_hup_interface
|
||||
};
|
||||
|
||||
static const switch_event_handler_table channel_event_handlers = {
|
||||
/*.on_init*/ channel_on_init,
|
||||
/*.on_ring*/ channel_on_ring,
|
||||
/*.on_execute*/ channel_on_execute,
|
||||
/*.on_hangup*/ channel_on_hangup,
|
||||
/*.on_loopback*/ channel_on_loopback,
|
||||
/*.on_transmit*/ channel_on_transmit
|
||||
};
|
||||
|
||||
static const switch_io_routines channel_io_routines = {
|
||||
/*.outgoing_channel*/ channel_outgoing_channel,
|
||||
/*.answer_channel*/ channel_answer_channel,
|
||||
/*.read_frame*/ channel_read_frame,
|
||||
/*.write_frame*/ channel_write_frame,
|
||||
/*.kill_channel*/ channel_kill_channel,
|
||||
/*.waitfor_read*/ channel_waitfor_read,
|
||||
/*.waitfor_write*/ channel_waitfor_write,
|
||||
/*.send_dtmf*/ channel_send_dtmf
|
||||
};
|
||||
|
||||
static const switch_endpoint_interface channel_endpoint_interface = {
|
||||
/*.interface_name*/ "portaudio",
|
||||
/*.io_routines*/ &channel_io_routines,
|
||||
/*.event_handlers*/ &channel_event_handlers,
|
||||
/*.private*/ NULL,
|
||||
/*.next*/ NULL
|
||||
};
|
||||
|
||||
static const switch_loadable_module_interface channel_module_interface = {
|
||||
/*.module_name*/ modname,
|
||||
/*.endpoint_interface*/ &channel_endpoint_interface,
|
||||
/*.timer_interface*/ NULL,
|
||||
/*.dialplan_interface*/ NULL,
|
||||
/*.codec_interface*/ NULL,
|
||||
/*.application_interface*/ NULL,
|
||||
/*.api_interface*/ &channel_api_interface
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
SWITCH_MOD_DECLARE(switch_status) switch_module_load(const switch_loadable_module_interface **interface, char *filename) {
|
||||
|
||||
if (switch_core_new_memory_pool(&module_pool) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "OH OH no pool\n");
|
||||
return SWITCH_STATUS_TERM;
|
||||
}
|
||||
|
||||
|
||||
Pa_Initialize();
|
||||
load_config();
|
||||
switch_core_hash_init(&globals.call_hash, module_pool);
|
||||
switch_mutex_init(&globals.device_lock, SWITCH_MUTEX_NESTED, module_pool);
|
||||
|
||||
dump_info();
|
||||
|
||||
if (switch_event_reserve_subclass(MY_EVENT_RINGING) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Couldn't register subclass!");
|
||||
return SWITCH_STATUS_GENERR;
|
||||
}
|
||||
|
||||
/* connect my internal structure to the blank pointer passed to me */
|
||||
*interface = &channel_module_interface;
|
||||
|
||||
/* indicate that the module should continue to be loaded */
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static switch_status load_config(void)
|
||||
{
|
||||
switch_config cfg;
|
||||
char *var, *val;
|
||||
char *cf = "portaudio.conf";
|
||||
|
||||
memset(&globals, 0, sizeof(globals));
|
||||
|
||||
if (!switch_config_open_file(&cfg, cf)) {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "open of %s failed\n", cf);
|
||||
return SWITCH_STATUS_TERM;
|
||||
}
|
||||
|
||||
while (switch_config_next_pair(&cfg, &var, &val)) {
|
||||
if (!strcasecmp(cfg.category, "settings")) {
|
||||
if (!strcmp(var, "debug")) {
|
||||
globals.debug = atoi(val);
|
||||
} else if (!strcmp(var, "sample_rate")) {
|
||||
globals.sample_rate = atoi(val);
|
||||
} else if (!strcmp(var, "dialplan")) {
|
||||
set_global_dialplan(val);
|
||||
} else if (!strcmp(var, "cid_name")) {
|
||||
set_global_cid_name(val);
|
||||
} else if (!strcmp(var, "cid_num")) {
|
||||
set_global_cid_num(val);
|
||||
} else if (!strcmp(var, "indev")) {
|
||||
if (*val == '#') {
|
||||
globals.indev = atoi(val+1);
|
||||
} else {
|
||||
globals.indev = get_dev_by_name(val, 1);
|
||||
}
|
||||
} else if (!strcmp(var, "outdev")) {
|
||||
if (*val == '#') {
|
||||
globals.outdev = atoi(val+1);
|
||||
} else {
|
||||
globals.outdev = get_dev_by_name(val, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!globals.dialplan) {
|
||||
set_global_dialplan("default");
|
||||
}
|
||||
|
||||
if (!globals.sample_rate) {
|
||||
globals.sample_rate = 8000;
|
||||
}
|
||||
|
||||
switch_config_close_file(&cfg);
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
SWITCH_MOD_DECLARE(switch_status) switch_module_runtime(void)
|
||||
{
|
||||
|
||||
switch_yield(50000);
|
||||
make_call("8888");
|
||||
return SWITCH_STATUS_TERM;
|
||||
}
|
||||
*/
|
||||
|
||||
SWITCH_MOD_DECLARE(switch_status) switch_module_shutdown(void)
|
||||
{
|
||||
Pa_Terminate();
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static int get_dev_by_name(char *name, int in)
|
||||
{
|
||||
int i;
|
||||
int numDevices;
|
||||
const PaDeviceInfo *pdi;
|
||||
numDevices = Pa_CountDevices();
|
||||
|
||||
if( numDevices < 0 ) {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE_CLEAN, "ERROR: Pa_CountDevices returned 0x%x\n", numDevices );
|
||||
return -2;
|
||||
}
|
||||
|
||||
for( i=0; i<numDevices; i++ ) {
|
||||
pdi = Pa_GetDeviceInfo( i );
|
||||
if(strstr(pdi->name, name)) {
|
||||
if(in && pdi->maxInputChannels) {
|
||||
return i;
|
||||
} else if (!in && pdi->maxOutputChannels) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static int dump_info(void)
|
||||
{
|
||||
int i,j;
|
||||
int numDevices;
|
||||
const PaDeviceInfo *pdi;
|
||||
PaError err;
|
||||
numDevices = Pa_CountDevices();
|
||||
if( numDevices < 0 )
|
||||
{
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE_CLEAN, "ERROR: Pa_CountDevices returned 0x%x\n", numDevices );
|
||||
err = numDevices;
|
||||
goto error;
|
||||
}
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE_CLEAN, "Number of devices = %d\n", numDevices );
|
||||
for( i=0; i<numDevices; i++ )
|
||||
{
|
||||
pdi = Pa_GetDeviceInfo( i );
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE_CLEAN, "---------------------------------------------- #%d", i );
|
||||
if( i == Pa_GetDefaultInputDeviceID() ) switch_console_printf(SWITCH_CHANNEL_CONSOLE_CLEAN, " DefaultInput");
|
||||
if( i == Pa_GetDefaultOutputDeviceID() ) switch_console_printf(SWITCH_CHANNEL_CONSOLE_CLEAN, " DefaultOutput");
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE_CLEAN, "\nName = %s\n", pdi->name );
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE_CLEAN, "Max Inputs = %d", pdi->maxInputChannels );
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE_CLEAN, ", Max Outputs = %d\n", pdi->maxOutputChannels );
|
||||
if( pdi->numSampleRates == -1 )
|
||||
{
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE_CLEAN, "Sample Rate Range = %f to %f\n", pdi->sampleRates[0], pdi->sampleRates[1] );
|
||||
}
|
||||
else
|
||||
{
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE_CLEAN, "Sample Rates =");
|
||||
for( j=0; j<pdi->numSampleRates; j++ )
|
||||
{
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE_CLEAN, " %8.2f,", pdi->sampleRates[j] );
|
||||
}
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE_CLEAN, "\n");
|
||||
}
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE_CLEAN, "Native Sample Formats = ");
|
||||
if( pdi->nativeSampleFormats & paInt8 ) switch_console_printf(SWITCH_CHANNEL_CONSOLE_CLEAN, "paInt8, ");
|
||||
if( pdi->nativeSampleFormats & paUInt8 ) switch_console_printf(SWITCH_CHANNEL_CONSOLE_CLEAN, "paUInt8, ");
|
||||
if( pdi->nativeSampleFormats & paInt16 ) switch_console_printf(SWITCH_CHANNEL_CONSOLE_CLEAN, "paInt16, ");
|
||||
if( pdi->nativeSampleFormats & paInt32 ) switch_console_printf(SWITCH_CHANNEL_CONSOLE_CLEAN, "paInt32, ");
|
||||
if( pdi->nativeSampleFormats & paFloat32 ) switch_console_printf(SWITCH_CHANNEL_CONSOLE_CLEAN, "paFloat32, ");
|
||||
if( pdi->nativeSampleFormats & paInt24 ) switch_console_printf(SWITCH_CHANNEL_CONSOLE_CLEAN, "paInt24, ");
|
||||
if( pdi->nativeSampleFormats & paPackedInt24 ) switch_console_printf(SWITCH_CHANNEL_CONSOLE_CLEAN, "paPackedInt24, ");
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE_CLEAN, "\n");
|
||||
}
|
||||
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE_CLEAN, "----------------------------------------------\n");
|
||||
return 0;
|
||||
error:
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE_CLEAN, "An error occured while using the portaudio stream\n" );
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE_CLEAN, "Error number: %d\n", err );
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE_CLEAN, "Error message: %s\n", Pa_GetErrorText( err ) );
|
||||
return err;
|
||||
}
|
||||
|
||||
static switch_status engage_device(struct private_object *tech_pvt)
|
||||
{
|
||||
int sample_rate = globals.sample_rate;
|
||||
int codec_ms = 20;
|
||||
|
||||
switch_channel *channel;
|
||||
channel = switch_core_session_get_channel(tech_pvt->session);
|
||||
assert(channel != NULL);
|
||||
|
||||
if (switch_core_codec_init(&tech_pvt->read_codec,
|
||||
"L16",
|
||||
sample_rate,
|
||||
codec_ms,
|
||||
1,
|
||||
SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE,
|
||||
NULL,
|
||||
switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Can't load codec?\n");
|
||||
return SWITCH_STATUS_FALSE;
|
||||
} else {
|
||||
if (switch_core_codec_init(&tech_pvt->write_codec,
|
||||
"L16",
|
||||
sample_rate,
|
||||
codec_ms,
|
||||
1,
|
||||
SWITCH_CODEC_FLAG_ENCODE |SWITCH_CODEC_FLAG_DECODE,
|
||||
NULL,
|
||||
switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Can't load codec?\n");
|
||||
switch_core_codec_destroy(&tech_pvt->read_codec);
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
}
|
||||
tech_pvt->read_frame.rate = sample_rate;
|
||||
tech_pvt->read_frame.codec = &tech_pvt->read_codec;
|
||||
switch_core_session_set_read_codec(tech_pvt->session, &tech_pvt->read_codec);
|
||||
switch_core_session_set_write_codec(tech_pvt->session, &tech_pvt->write_codec);
|
||||
|
||||
tech_pvt->indev = globals.indev;
|
||||
tech_pvt->outdev = globals.outdev;
|
||||
|
||||
switch_mutex_lock(globals.device_lock);
|
||||
if ((tech_pvt->err = OpenAudioStream( &tech_pvt->audio_in, sample_rate, SAMPLE_TYPE, PABLIO_READ | PABLIO_MONO, tech_pvt->indev, -1)) == paNoError) {
|
||||
if ((tech_pvt->err = OpenAudioStream(&tech_pvt->audio_out, sample_rate, SAMPLE_TYPE, PABLIO_WRITE | PABLIO_MONO, -1, tech_pvt->outdev)) != paNoError) {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Can't open audio out [%d]!\n", tech_pvt->outdev);
|
||||
CloseAudioStream(tech_pvt->audio_in);
|
||||
tech_pvt->audio_in = NULL;
|
||||
}
|
||||
} else {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Can't open audio in [%d]!\n", tech_pvt->indev);
|
||||
}
|
||||
switch_mutex_unlock(globals.device_lock);
|
||||
|
||||
if (tech_pvt->err == paNoError) {
|
||||
snprintf(tech_pvt->call_id, sizeof(tech_pvt->call_id), "%d", globals.call_id++);
|
||||
switch_core_hash_insert(globals.call_hash, tech_pvt->call_id, tech_pvt);
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
} else {
|
||||
switch_core_codec_destroy(&tech_pvt->read_codec);
|
||||
switch_core_codec_destroy(&tech_pvt->write_codec);
|
||||
switch_core_session_destroy(&tech_pvt->session);
|
||||
}
|
||||
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
|
||||
static switch_status place_call(char *dest, char *out, size_t outlen)
|
||||
{
|
||||
switch_core_session *session;
|
||||
switch_status status = SWITCH_STATUS_FALSE;
|
||||
|
||||
if (!dest) {
|
||||
strncpy(out, "Usage: pacall <exten>", outlen - 1);
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
|
||||
strncpy(out, "FAIL", outlen - 1);
|
||||
|
||||
if ((session = switch_core_session_request(&channel_endpoint_interface, NULL))) {
|
||||
struct private_object *tech_pvt;
|
||||
switch_channel *channel;
|
||||
if ((tech_pvt = (struct private_object *) switch_core_session_alloc(session, sizeof(struct private_object)))) {
|
||||
memset(tech_pvt, 0, sizeof(*tech_pvt));
|
||||
channel = switch_core_session_get_channel(session);
|
||||
switch_core_session_set_private(session, tech_pvt);
|
||||
tech_pvt->session = session;
|
||||
} else {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Hey where is my memory pool?\n");
|
||||
switch_core_session_destroy(&session);
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
|
||||
if ((tech_pvt->caller_profile = switch_caller_profile_new(session,
|
||||
globals.dialplan,
|
||||
globals.cid_name,
|
||||
globals.cid_num,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
dest))) {
|
||||
char name[128];
|
||||
switch_channel_set_caller_profile(channel, tech_pvt->caller_profile);
|
||||
snprintf(name, sizeof(name), "PortAudio/%s-%04x", tech_pvt->caller_profile->destination_number ? tech_pvt->caller_profile->destination_number : modname, rand() & 0xffff);
|
||||
switch_channel_set_name(channel, name);
|
||||
}
|
||||
tech_pvt->session = session;
|
||||
if ((status = engage_device(tech_pvt)) == SWITCH_STATUS_SUCCESS) {
|
||||
switch_channel_set_state(channel, CS_INIT);
|
||||
switch_core_session_thread_launch(tech_pvt->session);
|
||||
snprintf(out, outlen, "SUCCESS: %s", tech_pvt->call_id);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static switch_status hup_call(char *callid, char *out, size_t outlen)
|
||||
{
|
||||
struct private_object *tech_pvt;
|
||||
switch_channel *channel = NULL;
|
||||
char tmp[50];
|
||||
|
||||
if (callid && !strcasecmp(callid, "last")) {
|
||||
snprintf(tmp, sizeof(tmp), "%d", globals.call_id - 1);
|
||||
callid = tmp;
|
||||
}
|
||||
if (!callid || !strcasecmp(callid, "all")) {
|
||||
switch_hash_index_t* hi;
|
||||
void *val;
|
||||
int i = 0;
|
||||
|
||||
for (hi = apr_hash_first(module_pool, globals.call_hash); hi; hi = switch_hash_next(hi)) {
|
||||
switch_hash_this(hi, NULL, NULL, &val);
|
||||
tech_pvt = val;
|
||||
channel = switch_core_session_get_channel(tech_pvt->session);
|
||||
assert(channel != NULL);
|
||||
switch_channel_hangup(channel);
|
||||
i++;
|
||||
}
|
||||
|
||||
snprintf(out, outlen, "HUNGUP: %d", i);
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if ((tech_pvt = switch_core_hash_find(globals.call_hash, callid))) {
|
||||
|
||||
channel = switch_core_session_get_channel(tech_pvt->session);
|
||||
assert(channel != NULL);
|
||||
|
||||
switch_channel_hangup(channel);
|
||||
strncpy(out, "OK", outlen - 1);
|
||||
} else {
|
||||
strncpy(out, "NO SUCH CALL", outlen - 1);
|
||||
}
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static switch_status send_dtmf(char *callid, char *out, size_t outlen)
|
||||
{
|
||||
struct private_object *tech_pvt = NULL;
|
||||
switch_channel *channel = NULL;
|
||||
char *dtmf;
|
||||
|
||||
if ((dtmf = strchr(callid, ' '))) {
|
||||
*dtmf++ = '\0';
|
||||
} else {
|
||||
dtmf = "";
|
||||
}
|
||||
|
||||
if ((tech_pvt = switch_core_hash_find(globals.call_hash, callid))) {
|
||||
channel = switch_core_session_get_channel(tech_pvt->session);
|
||||
assert(channel != NULL);
|
||||
switch_channel_queue_dtmf(channel, dtmf);
|
||||
strncpy(out, "OK", outlen - 1);
|
||||
} else {
|
||||
strncpy(out, "NO SUCH CALL", outlen - 1);
|
||||
}
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static switch_status answer_call(char *callid, char *out, size_t outlen)
|
||||
{
|
||||
struct private_object *tech_pvt = NULL;
|
||||
switch_channel *channel = NULL;
|
||||
|
||||
if ((tech_pvt = switch_core_hash_find(globals.call_hash, callid))) {
|
||||
channel = switch_core_session_get_channel(tech_pvt->session);
|
||||
assert(channel != NULL);
|
||||
switch_set_flag(tech_pvt, TFLAG_ANSWER);
|
||||
switch_channel_answer(channel);
|
||||
} else {
|
||||
strncpy(out, "NO SUCH CALL", outlen - 1);
|
||||
}
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static void print_info(struct private_object *tech_pvt, char *out, size_t outlen)
|
||||
{
|
||||
switch_channel *channel = NULL;
|
||||
channel = switch_core_session_get_channel(tech_pvt->session);
|
||||
assert(channel != NULL);
|
||||
|
||||
snprintf(out, outlen, "CALL %s\t%s\t%s\t%s\t%s\n",
|
||||
tech_pvt->call_id,
|
||||
tech_pvt->caller_profile->caller_id_name ? tech_pvt->caller_profile->caller_id_name : "n/a",
|
||||
tech_pvt->caller_profile->caller_id_number ? tech_pvt->caller_profile->caller_id_number : "n/a",
|
||||
tech_pvt->caller_profile->destination_number ? tech_pvt->caller_profile->destination_number : "n/a",
|
||||
switch_channel_get_name(channel));
|
||||
|
||||
}
|
||||
|
||||
static switch_status call_info(char *callid, char *out, size_t outlen)
|
||||
{
|
||||
struct private_object *tech_pvt;
|
||||
switch_hash_index_t* hi;
|
||||
void *val;
|
||||
if (!callid || !strcasecmp(callid, "all")) {
|
||||
for (hi = apr_hash_first(module_pool, globals.call_hash); hi; hi = switch_hash_next(hi)) {
|
||||
switch_hash_this(hi, NULL, NULL, &val);
|
||||
tech_pvt = val;
|
||||
print_info(tech_pvt, out + strlen(out), outlen - strlen(out));
|
||||
}
|
||||
} else if ((tech_pvt = switch_core_hash_find(globals.call_hash, callid))) {
|
||||
print_info(tech_pvt, out, outlen);
|
||||
} else {
|
||||
strncpy(out, "NO SUCH CALL", outlen - 1);
|
||||
}
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,327 @@
|
||||
/*
|
||||
* $Id: pablio.c,v 1.1.1.1.4.4 2003/03/13 17:28:33 philburk Exp $
|
||||
* pablio.c
|
||||
* Portable Audio Blocking Input/Output utility.
|
||||
*
|
||||
* Author: Phil Burk, http://www.softsynth.com
|
||||
*
|
||||
* This program uses the PortAudio Portable Audio Library.
|
||||
* For more information see: http://www.audiomulch.com/portaudio/
|
||||
* Copyright (c) 1999-2000 Ross Bencina and Phil Burk
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* Any person wishing to distribute modifications to the Software is
|
||||
* requested to send the modifications to the original developer so that
|
||||
* they can be incorporated into the canonical version.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
/* History:
|
||||
* PLB021214 - check for valid stream in CloseAudioStream() to prevent hang.
|
||||
* add timeOutMSec to CloseAudioStream() to prevent hang.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "portaudio.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "pablio.h"
|
||||
#include <string.h>
|
||||
|
||||
/************************************************************************/
|
||||
/******** Constants *****************************************************/
|
||||
/************************************************************************/
|
||||
|
||||
#define FRAMES_PER_BUFFER (256)
|
||||
|
||||
/************************************************************************/
|
||||
/******** Prototypes ****************************************************/
|
||||
/************************************************************************/
|
||||
|
||||
static int blockingIOCallback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData );
|
||||
static PaError PABLIO_InitFIFO( RingBuffer *rbuf, long numFrames, long bytesPerFrame );
|
||||
static PaError PABLIO_TermFIFO( RingBuffer *rbuf );
|
||||
|
||||
/************************************************************************/
|
||||
/******** Functions *****************************************************/
|
||||
/************************************************************************/
|
||||
|
||||
/* Called from PortAudio.
|
||||
* Read and write data only if there is room in FIFOs.
|
||||
*/
|
||||
static int blockingIOCallback( void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer,
|
||||
PaTimestamp outTime, void *userData )
|
||||
{
|
||||
PABLIO_Stream *data = (PABLIO_Stream*)userData;
|
||||
long numBytes = data->bytesPerFrame * framesPerBuffer;
|
||||
(void) outTime;
|
||||
|
||||
/* This may get called with NULL inputBuffer during initial setup. */
|
||||
if( inputBuffer != NULL )
|
||||
{
|
||||
RingBuffer_Write( &data->inFIFO, inputBuffer, numBytes );
|
||||
}
|
||||
if( outputBuffer != NULL )
|
||||
{
|
||||
int i;
|
||||
int numRead = RingBuffer_Read( &data->outFIFO, outputBuffer, numBytes );
|
||||
/* Zero out remainder of buffer if we run out of data. */
|
||||
for( i=numRead; i<numBytes; i++ )
|
||||
{
|
||||
((char *)outputBuffer)[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Allocate buffer. */
|
||||
static PaError PABLIO_InitFIFO( RingBuffer *rbuf, long numFrames, long bytesPerFrame )
|
||||
{
|
||||
long numBytes = numFrames * bytesPerFrame;
|
||||
char *buffer = (char *) malloc( numBytes );
|
||||
if( buffer == NULL ) return paInsufficientMemory;
|
||||
memset( buffer, 0, numBytes );
|
||||
return (PaError) RingBuffer_Init( rbuf, numBytes, buffer );
|
||||
}
|
||||
|
||||
/* Free buffer. */
|
||||
static PaError PABLIO_TermFIFO( RingBuffer *rbuf )
|
||||
{
|
||||
if( rbuf->buffer ) free( rbuf->buffer );
|
||||
rbuf->buffer = NULL;
|
||||
return paNoError;
|
||||
}
|
||||
|
||||
/************************************************************
|
||||
* Write data to ring buffer.
|
||||
* Will not return until all the data has been written.
|
||||
*/
|
||||
long WriteAudioStream( PABLIO_Stream *aStream, void *data, long numFrames )
|
||||
{
|
||||
long bytesWritten;
|
||||
char *p = (char *) data;
|
||||
long numBytes = aStream->bytesPerFrame * numFrames;
|
||||
while( numBytes > 0)
|
||||
{
|
||||
bytesWritten = RingBuffer_Write( &aStream->outFIFO, p, numBytes );
|
||||
numBytes -= bytesWritten;
|
||||
p += bytesWritten;
|
||||
if( numBytes > 0) Pa_Sleep(10);
|
||||
}
|
||||
return numFrames;
|
||||
}
|
||||
|
||||
/************************************************************
|
||||
* Read data from ring buffer.
|
||||
* Will not return until all the data has been read.
|
||||
*/
|
||||
long ReadAudioStream( PABLIO_Stream *aStream, void *data, long numFrames )
|
||||
{
|
||||
long bytesRead;
|
||||
char *p = (char *) data;
|
||||
long numBytes = aStream->bytesPerFrame * numFrames;
|
||||
while( numBytes > 0)
|
||||
{
|
||||
bytesRead = RingBuffer_Read( &aStream->inFIFO, p, numBytes );
|
||||
numBytes -= bytesRead;
|
||||
p += bytesRead;
|
||||
if( numBytes > 0) Pa_Sleep(10);
|
||||
}
|
||||
return numFrames;
|
||||
}
|
||||
|
||||
/************************************************************
|
||||
* Return the number of frames that could be written to the stream without
|
||||
* having to wait.
|
||||
*/
|
||||
long GetAudioStreamWriteable( PABLIO_Stream *aStream )
|
||||
{
|
||||
int bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO );
|
||||
return bytesEmpty / aStream->bytesPerFrame;
|
||||
}
|
||||
|
||||
/************************************************************
|
||||
* Return the number of frames that are available to be read from the
|
||||
* stream without having to wait.
|
||||
*/
|
||||
long GetAudioStreamReadable( PABLIO_Stream *aStream )
|
||||
{
|
||||
int bytesFull = RingBuffer_GetReadAvailable( &aStream->inFIFO );
|
||||
return bytesFull / aStream->bytesPerFrame;
|
||||
}
|
||||
|
||||
/************************************************************/
|
||||
static unsigned long RoundUpToNextPowerOf2( unsigned long n )
|
||||
{
|
||||
long numBits = 0;
|
||||
if( ((n-1) & n) == 0) return n; /* Already Power of two. */
|
||||
while( n > 0 )
|
||||
{
|
||||
n= n>>1;
|
||||
numBits++;
|
||||
}
|
||||
return (1<<numBits);
|
||||
}
|
||||
|
||||
/************************************************************
|
||||
* Opens a PortAudio stream with default characteristics.
|
||||
* Allocates PABLIO_Stream structure.
|
||||
*
|
||||
* flags parameter can be an ORed combination of:
|
||||
* PABLIO_READ, PABLIO_WRITE, or PABLIO_READ_WRITE,
|
||||
* and either PABLIO_MONO or PABLIO_STEREO
|
||||
*/
|
||||
PaError OpenAudioStream( PABLIO_Stream **rwblPtr, double sampleRate,
|
||||
PaSampleFormat format, long flags, int indev, int outdev )
|
||||
{
|
||||
long bytesPerSample;
|
||||
long doRead = 0;
|
||||
long doWrite = 0;
|
||||
PaError err;
|
||||
PABLIO_Stream *aStream;
|
||||
long minNumBuffers;
|
||||
long numFrames;
|
||||
|
||||
/* Allocate PABLIO_Stream structure for caller. */
|
||||
aStream = (PABLIO_Stream *) malloc( sizeof(PABLIO_Stream) );
|
||||
if( aStream == NULL ) return paInsufficientMemory;
|
||||
memset( aStream, 0, sizeof(PABLIO_Stream) );
|
||||
|
||||
/* Determine size of a sample. */
|
||||
bytesPerSample = Pa_GetSampleSize( format );
|
||||
if( bytesPerSample < 0 )
|
||||
{
|
||||
err = (PaError) bytesPerSample;
|
||||
goto error;
|
||||
}
|
||||
aStream->samplesPerFrame = ((flags&PABLIO_MONO) != 0) ? 1 : 2;
|
||||
aStream->bytesPerFrame = bytesPerSample * aStream->samplesPerFrame;
|
||||
|
||||
/* Initialize PortAudio */
|
||||
err = Pa_Initialize();
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
/* Warning: numFrames must be larger than amount of data processed per interrupt
|
||||
* inside PA to prevent glitches. Just to be safe, adjust size upwards.
|
||||
*/
|
||||
minNumBuffers = 2 * Pa_GetMinNumBuffers( FRAMES_PER_BUFFER, sampleRate );
|
||||
numFrames = minNumBuffers * FRAMES_PER_BUFFER;
|
||||
/* The PortAudio callback runs in a high priority thread. But PABLIO
|
||||
* runs in a normal foreground thread. So we may have much worse
|
||||
* latency in PABLIO. So adjust latency to a safe level.
|
||||
*/
|
||||
{
|
||||
const int safeLatencyMSec = 200;
|
||||
int minLatencyMSec = (int) ((1000 * numFrames) / sampleRate);
|
||||
if( minLatencyMSec < safeLatencyMSec )
|
||||
{
|
||||
numFrames = (int) ((safeLatencyMSec * sampleRate) / 1000);
|
||||
}
|
||||
}
|
||||
numFrames = RoundUpToNextPowerOf2( numFrames );
|
||||
|
||||
/* Initialize Ring Buffers */
|
||||
doRead = ((flags & PABLIO_READ) != 0);
|
||||
doWrite = ((flags & PABLIO_WRITE) != 0);
|
||||
if(doRead)
|
||||
{
|
||||
err = PABLIO_InitFIFO( &aStream->inFIFO, numFrames, aStream->bytesPerFrame );
|
||||
if( err != paNoError ) goto error;
|
||||
}
|
||||
if(doWrite)
|
||||
{
|
||||
long numBytes;
|
||||
err = PABLIO_InitFIFO( &aStream->outFIFO, numFrames, aStream->bytesPerFrame );
|
||||
if( err != paNoError ) goto error;
|
||||
/* Make Write FIFO appear full initially. */
|
||||
numBytes = RingBuffer_GetWriteAvailable( &aStream->outFIFO );
|
||||
RingBuffer_AdvanceWriteIndex( &aStream->outFIFO, numBytes );
|
||||
}
|
||||
|
||||
/* Open a PortAudio stream that we will use to communicate with the underlying
|
||||
* audio drivers. */
|
||||
err = Pa_OpenStream(
|
||||
&aStream->stream,
|
||||
(doRead ? (indev > -1) ? indev : Pa_GetDefaultInputDeviceID() : paNoDevice),
|
||||
(doRead ? aStream->samplesPerFrame : 0 ),
|
||||
format,
|
||||
NULL,
|
||||
(doWrite ? (outdev > -1) ? outdev : Pa_GetDefaultOutputDeviceID() : paNoDevice),
|
||||
(doWrite ? aStream->samplesPerFrame : 0 ),
|
||||
format,
|
||||
NULL,
|
||||
sampleRate,
|
||||
FRAMES_PER_BUFFER,
|
||||
minNumBuffers,
|
||||
paClipOff, /* we won't output out of range samples so don't bother clipping them */
|
||||
blockingIOCallback,
|
||||
aStream );
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
err = Pa_StartStream( aStream->stream );
|
||||
if( err != paNoError ) goto error;
|
||||
|
||||
*rwblPtr = aStream;
|
||||
return paNoError;
|
||||
|
||||
error:
|
||||
CloseAudioStream( aStream );
|
||||
*rwblPtr = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
/************************************************************/
|
||||
PaError CloseAudioStream( PABLIO_Stream *aStream )
|
||||
{
|
||||
PaError err = paNoError;
|
||||
int bytesEmpty;
|
||||
int byteSize = aStream->outFIFO.bufferSize;
|
||||
|
||||
if( aStream->stream != NULL ) /* Make sure stream was opened. PLB021214 */
|
||||
{
|
||||
/* If we are writing data, make sure we play everything written. */
|
||||
if( byteSize > 0 )
|
||||
{
|
||||
int timeOutMSec = 2000;
|
||||
bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO );
|
||||
while( (bytesEmpty < byteSize) && (timeOutMSec > 0) )
|
||||
{
|
||||
Pa_Sleep( 20 );
|
||||
timeOutMSec -= 20;
|
||||
bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO );
|
||||
}
|
||||
}
|
||||
err = Pa_StopStream( aStream->stream );
|
||||
if( err != paNoError ) goto error;
|
||||
err = Pa_CloseStream( aStream->stream );
|
||||
}
|
||||
|
||||
error:
|
||||
Pa_Terminate();
|
||||
PABLIO_TermFIFO( &aStream->inFIFO );
|
||||
PABLIO_TermFIFO( &aStream->outFIFO );
|
||||
free( aStream );
|
||||
return err;
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
#ifndef _PABLIO_H
|
||||
#define _PABLIO_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/*
|
||||
* $Id: pablio.h,v 1.1.1.1 2002/01/22 00:52:53 phil Exp $
|
||||
* PABLIO.h
|
||||
* Portable Audio Blocking read/write utility.
|
||||
*
|
||||
* Author: Phil Burk, http://www.softsynth.com/portaudio/
|
||||
*
|
||||
* Include file for PABLIO, the Portable Audio Blocking I/O Library.
|
||||
* PABLIO is built on top of PortAudio, the Portable Audio Library.
|
||||
* For more information see: http://www.audiomulch.com/portaudio/
|
||||
* Copyright (c) 1999-2000 Ross Bencina and Phil Burk
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* Any person wishing to distribute modifications to the Software is
|
||||
* requested to send the modifications to the original developer so that
|
||||
* they can be incorporated into the canonical version.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "portaudio.h"
|
||||
#include "ringbuffer.h"
|
||||
#include <string.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
RingBuffer inFIFO;
|
||||
RingBuffer outFIFO;
|
||||
PortAudioStream *stream;
|
||||
int bytesPerFrame;
|
||||
int samplesPerFrame;
|
||||
}
|
||||
PABLIO_Stream;
|
||||
|
||||
/* Values for flags for OpenAudioStream(). */
|
||||
#define PABLIO_READ (1<<0)
|
||||
#define PABLIO_WRITE (1<<1)
|
||||
#define PABLIO_READ_WRITE (PABLIO_READ|PABLIO_WRITE)
|
||||
#define PABLIO_MONO (1<<2)
|
||||
#define PABLIO_STEREO (1<<3)
|
||||
|
||||
/************************************************************
|
||||
* Write data to ring buffer.
|
||||
* Will not return until all the data has been written.
|
||||
*/
|
||||
long WriteAudioStream( PABLIO_Stream *aStream, void *data, long numFrames );
|
||||
|
||||
/************************************************************
|
||||
* Read data from ring buffer.
|
||||
* Will not return until all the data has been read.
|
||||
*/
|
||||
long ReadAudioStream( PABLIO_Stream *aStream, void *data, long numFrames );
|
||||
|
||||
/************************************************************
|
||||
* Return the number of frames that could be written to the stream without
|
||||
* having to wait.
|
||||
*/
|
||||
long GetAudioStreamWriteable( PABLIO_Stream *aStream );
|
||||
|
||||
/************************************************************
|
||||
* Return the number of frames that are available to be read from the
|
||||
* stream without having to wait.
|
||||
*/
|
||||
long GetAudioStreamReadable( PABLIO_Stream *aStream );
|
||||
|
||||
/************************************************************
|
||||
* Opens a PortAudio stream with default characteristics.
|
||||
* Allocates PABLIO_Stream structure.
|
||||
*
|
||||
* flags parameter can be an ORed combination of:
|
||||
* PABLIO_READ, PABLIO_WRITE, or PABLIO_READ_WRITE,
|
||||
* and either PABLIO_MONO or PABLIO_STEREO
|
||||
*/
|
||||
PaError OpenAudioStream( PABLIO_Stream **aStreamPtr, double sampleRate,
|
||||
PaSampleFormat format, long flags, int indev, int outdev );
|
||||
|
||||
PaError CloseAudioStream( PABLIO_Stream *aStream );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* _PABLIO_H */
|
||||
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* $Id: ringbuffer.c,v 1.1.1.1 2002/01/22 00:52:53 phil Exp $
|
||||
* ringbuffer.c
|
||||
* Ring Buffer utility..
|
||||
*
|
||||
* Author: Phil Burk, http://www.softsynth.com
|
||||
*
|
||||
* This program uses the PortAudio Portable Audio Library.
|
||||
* For more information see: http://www.audiomulch.com/portaudio/
|
||||
* Copyright (c) 1999-2000 Ross Bencina and Phil Burk
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* Any person wishing to distribute modifications to the Software is
|
||||
* requested to send the modifications to the original developer so that
|
||||
* they can be incorporated into the canonical version.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "ringbuffer.h"
|
||||
#include <string.h>
|
||||
|
||||
/***************************************************************************
|
||||
* Initialize FIFO.
|
||||
* numBytes must be power of 2, returns -1 if not.
|
||||
*/
|
||||
long RingBuffer_Init( RingBuffer *rbuf, long numBytes, void *dataPtr )
|
||||
{
|
||||
if( ((numBytes-1) & numBytes) != 0) return -1; /* Not Power of two. */
|
||||
rbuf->bufferSize = numBytes;
|
||||
rbuf->buffer = (char *)dataPtr;
|
||||
RingBuffer_Flush( rbuf );
|
||||
rbuf->bigMask = (numBytes*2)-1;
|
||||
rbuf->smallMask = (numBytes)-1;
|
||||
return 0;
|
||||
}
|
||||
/***************************************************************************
|
||||
** Return number of bytes available for reading. */
|
||||
long RingBuffer_GetReadAvailable( RingBuffer *rbuf )
|
||||
{
|
||||
return ( (rbuf->writeIndex - rbuf->readIndex) & rbuf->bigMask );
|
||||
}
|
||||
/***************************************************************************
|
||||
** Return number of bytes available for writing. */
|
||||
long RingBuffer_GetWriteAvailable( RingBuffer *rbuf )
|
||||
{
|
||||
return ( rbuf->bufferSize - RingBuffer_GetReadAvailable(rbuf));
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
** Clear buffer. Should only be called when buffer is NOT being read. */
|
||||
void RingBuffer_Flush( RingBuffer *rbuf )
|
||||
{
|
||||
rbuf->writeIndex = rbuf->readIndex = 0;
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
** Get address of region(s) to which we can write data.
|
||||
** If the region is contiguous, size2 will be zero.
|
||||
** If non-contiguous, size2 will be the size of second region.
|
||||
** Returns room available to be written or numBytes, whichever is smaller.
|
||||
*/
|
||||
long RingBuffer_GetWriteRegions( RingBuffer *rbuf, long numBytes,
|
||||
void **dataPtr1, long *sizePtr1,
|
||||
void **dataPtr2, long *sizePtr2 )
|
||||
{
|
||||
long index;
|
||||
long available = RingBuffer_GetWriteAvailable( rbuf );
|
||||
if( numBytes > available ) numBytes = available;
|
||||
/* Check to see if write is not contiguous. */
|
||||
index = rbuf->writeIndex & rbuf->smallMask;
|
||||
if( (index + numBytes) > rbuf->bufferSize )
|
||||
{
|
||||
/* Write data in two blocks that wrap the buffer. */
|
||||
long firstHalf = rbuf->bufferSize - index;
|
||||
*dataPtr1 = &rbuf->buffer[index];
|
||||
*sizePtr1 = firstHalf;
|
||||
*dataPtr2 = &rbuf->buffer[0];
|
||||
*sizePtr2 = numBytes - firstHalf;
|
||||
}
|
||||
else
|
||||
{
|
||||
*dataPtr1 = &rbuf->buffer[index];
|
||||
*sizePtr1 = numBytes;
|
||||
*dataPtr2 = NULL;
|
||||
*sizePtr2 = 0;
|
||||
}
|
||||
return numBytes;
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
*/
|
||||
long RingBuffer_AdvanceWriteIndex( RingBuffer *rbuf, long numBytes )
|
||||
{
|
||||
return rbuf->writeIndex = (rbuf->writeIndex + numBytes) & rbuf->bigMask;
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
** Get address of region(s) from which we can read data.
|
||||
** If the region is contiguous, size2 will be zero.
|
||||
** If non-contiguous, size2 will be the size of second region.
|
||||
** Returns room available to be written or numBytes, whichever is smaller.
|
||||
*/
|
||||
long RingBuffer_GetReadRegions( RingBuffer *rbuf, long numBytes,
|
||||
void **dataPtr1, long *sizePtr1,
|
||||
void **dataPtr2, long *sizePtr2 )
|
||||
{
|
||||
long index;
|
||||
long available = RingBuffer_GetReadAvailable( rbuf );
|
||||
if( numBytes > available ) numBytes = available;
|
||||
/* Check to see if read is not contiguous. */
|
||||
index = rbuf->readIndex & rbuf->smallMask;
|
||||
if( (index + numBytes) > rbuf->bufferSize )
|
||||
{
|
||||
/* Write data in two blocks that wrap the buffer. */
|
||||
long firstHalf = rbuf->bufferSize - index;
|
||||
*dataPtr1 = &rbuf->buffer[index];
|
||||
*sizePtr1 = firstHalf;
|
||||
*dataPtr2 = &rbuf->buffer[0];
|
||||
*sizePtr2 = numBytes - firstHalf;
|
||||
}
|
||||
else
|
||||
{
|
||||
*dataPtr1 = &rbuf->buffer[index];
|
||||
*sizePtr1 = numBytes;
|
||||
*dataPtr2 = NULL;
|
||||
*sizePtr2 = 0;
|
||||
}
|
||||
return numBytes;
|
||||
}
|
||||
/***************************************************************************
|
||||
*/
|
||||
long RingBuffer_AdvanceReadIndex( RingBuffer *rbuf, long numBytes )
|
||||
{
|
||||
return rbuf->readIndex = (rbuf->readIndex + numBytes) & rbuf->bigMask;
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
** Return bytes written. */
|
||||
long RingBuffer_Write( RingBuffer *rbuf, void *data, long numBytes )
|
||||
{
|
||||
long size1, size2, numWritten;
|
||||
void *data1, *data2;
|
||||
numWritten = RingBuffer_GetWriteRegions( rbuf, numBytes, &data1, &size1, &data2, &size2 );
|
||||
if( size2 > 0 )
|
||||
{
|
||||
|
||||
memcpy( data1, data, size1 );
|
||||
data = ((char *)data) + size1;
|
||||
memcpy( data2, data, size2 );
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy( data1, data, size1 );
|
||||
}
|
||||
RingBuffer_AdvanceWriteIndex( rbuf, numWritten );
|
||||
return numWritten;
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
** Return bytes read. */
|
||||
long RingBuffer_Read( RingBuffer *rbuf, void *data, long numBytes )
|
||||
{
|
||||
long size1, size2, numRead;
|
||||
void *data1, *data2;
|
||||
numRead = RingBuffer_GetReadRegions( rbuf, numBytes, &data1, &size1, &data2, &size2 );
|
||||
if( size2 > 0 )
|
||||
{
|
||||
memcpy( data, data1, size1 );
|
||||
data = ((char *)data) + size1;
|
||||
memcpy( data, data2, size2 );
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy( data, data1, size1 );
|
||||
}
|
||||
RingBuffer_AdvanceReadIndex( rbuf, numRead );
|
||||
return numRead;
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
#ifndef _RINGBUFFER_H
|
||||
#define _RINGBUFFER_H
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/*
|
||||
* $Id: ringbuffer.h,v 1.1.1.1.4.2 2003/04/28 17:45:34 philburk Exp $
|
||||
* ringbuffer.h
|
||||
* Ring Buffer utility..
|
||||
*
|
||||
* Author: Phil Burk, http://www.softsynth.com
|
||||
*
|
||||
* This program is distributed with the PortAudio Portable Audio Library.
|
||||
* For more information see: http://www.audiomulch.com/portaudio/
|
||||
* Copyright (c) 1999-2000 Ross Bencina and Phil Burk
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* Any person wishing to distribute modifications to the Software is
|
||||
* requested to send the modifications to the original developer so that
|
||||
* they can be incorporated into the canonical version.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#define PortAudioStream PaStream
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "ringbuffer.h"
|
||||
#include <string.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
long bufferSize; /* Number of bytes in FIFO. Power of 2. Set by RingBuffer_Init. */
|
||||
/* These are declared volatile because they are written by a different thread than the reader. */
|
||||
volatile long writeIndex; /* Index of next writable byte. Set by RingBuffer_AdvanceWriteIndex. */
|
||||
volatile long readIndex; /* Index of next readable byte. Set by RingBuffer_AdvanceReadIndex. */
|
||||
long bigMask; /* Used for wrapping indices with extra bit to distinguish full/empty. */
|
||||
long smallMask; /* Used for fitting indices to buffer. */
|
||||
char *buffer;
|
||||
}
|
||||
RingBuffer;
|
||||
/*
|
||||
* Initialize Ring Buffer.
|
||||
* numBytes must be power of 2, returns -1 if not.
|
||||
*/
|
||||
long RingBuffer_Init( RingBuffer *rbuf, long numBytes, void *dataPtr );
|
||||
|
||||
/* Clear buffer. Should only be called when buffer is NOT being read. */
|
||||
void RingBuffer_Flush( RingBuffer *rbuf );
|
||||
|
||||
/* Return number of bytes available for writing. */
|
||||
long RingBuffer_GetWriteAvailable( RingBuffer *rbuf );
|
||||
/* Return number of bytes available for read. */
|
||||
long RingBuffer_GetReadAvailable( RingBuffer *rbuf );
|
||||
/* Return bytes written. */
|
||||
long RingBuffer_Write( RingBuffer *rbuf, void *data, long numBytes );
|
||||
/* Return bytes read. */
|
||||
long RingBuffer_Read( RingBuffer *rbuf, void *data, long numBytes );
|
||||
|
||||
/* Get address of region(s) to which we can write data.
|
||||
** If the region is contiguous, size2 will be zero.
|
||||
** If non-contiguous, size2 will be the size of second region.
|
||||
** Returns room available to be written or numBytes, whichever is smaller.
|
||||
*/
|
||||
long RingBuffer_GetWriteRegions( RingBuffer *rbuf, long numBytes,
|
||||
void **dataPtr1, long *sizePtr1,
|
||||
void **dataPtr2, long *sizePtr2 );
|
||||
long RingBuffer_AdvanceWriteIndex( RingBuffer *rbuf, long numBytes );
|
||||
|
||||
/* Get address of region(s) from which we can read data.
|
||||
** If the region is contiguous, size2 will be zero.
|
||||
** If non-contiguous, size2 will be the size of second region.
|
||||
** Returns room available to be read or numBytes, whichever is smaller.
|
||||
*/
|
||||
long RingBuffer_GetReadRegions( RingBuffer *rbuf, long numBytes,
|
||||
void **dataPtr1, long *sizePtr1,
|
||||
void **dataPtr2, long *sizePtr2 );
|
||||
|
||||
long RingBuffer_AdvanceReadIndex( RingBuffer *rbuf, long numBytes );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* _RINGBUFFER_H */
|
||||
@@ -0,0 +1,634 @@
|
||||
/*
|
||||
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
* Copyright (C) 2005/2006, Anthony Minessale II <anthmct@yahoo.com>
|
||||
*
|
||||
* Version: MPL 1.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Anthony Minessale II <anthmct@yahoo.com>
|
||||
* Portions created by the Initial Developer are Copyright (C)
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Anthony Minessale II <anthmct@yahoo.com>
|
||||
*
|
||||
*
|
||||
* mod_wanchan.c -- WANPIPE PRI Channel Module
|
||||
*
|
||||
*/
|
||||
#include <switch.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/queue.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <libsangoma.h>
|
||||
#include <sangoma_pri.h>
|
||||
|
||||
static const char modname[] = "mod_wanchan";
|
||||
#define STRLEN 15
|
||||
|
||||
static switch_memory_pool *module_pool;
|
||||
|
||||
typedef enum {
|
||||
PFLAG_ANSWER = (1 << 0),
|
||||
PFLAG_HANGUP = (1 << 1),
|
||||
} PFLAGS;
|
||||
|
||||
|
||||
typedef enum {
|
||||
PPFLAG_RING = (1 << 0),
|
||||
} PPFLAGS;
|
||||
|
||||
typedef enum {
|
||||
TFLAG_MEDIA = (1 << 0),
|
||||
TFLAG_INBOUND = (1 << 1),
|
||||
TFLAG_OUTBOUND = (1 << 2),
|
||||
TFLAG_INCOMING = (1 << 3),
|
||||
TFLAG_PARSE_INCOMING = (1 << 4),
|
||||
TFLAG_ACTIVATE = (1 << 5),
|
||||
TFLAG_DTMF = (1 << 6),
|
||||
TFLAG_DESTROY = (1 << 7),
|
||||
TFLAG_ABORT = (1 << 8),
|
||||
TFLAG_SWITCH = (1 << 9),
|
||||
} TFLAGS;
|
||||
|
||||
#define PACKET_LEN 160
|
||||
#define DEFAULT_BYTES_PER_FRAME 160
|
||||
|
||||
static struct {
|
||||
int debug;
|
||||
int panic;
|
||||
int span;
|
||||
int dchan;
|
||||
int node;
|
||||
int pswitch;
|
||||
int bytes_per_frame;
|
||||
char *dialplan;
|
||||
} globals;
|
||||
|
||||
|
||||
|
||||
struct private_object {
|
||||
unsigned int flags; /* FLAGS */
|
||||
struct switch_frame frame; /* Frame for Writing */
|
||||
unsigned char databuf[1024];
|
||||
struct sangoma_pri *spri;
|
||||
pri_event ring_event;
|
||||
pri_event hangup_event;
|
||||
sangoma_api_hdr_t hdrframe;
|
||||
switch_caller_profile *caller_profile;
|
||||
int socket;
|
||||
int callno;
|
||||
int cause;
|
||||
};
|
||||
|
||||
struct channel_map {
|
||||
switch_core_session *map[36];
|
||||
};
|
||||
|
||||
|
||||
static void set_global_dialplan(char *dialplan)
|
||||
{
|
||||
if (globals.dialplan) {
|
||||
free(globals.dialplan);
|
||||
globals.dialplan = NULL;
|
||||
}
|
||||
|
||||
globals.dialplan = strdup(dialplan);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int str2node(char *node)
|
||||
{
|
||||
if (!strcasecmp(node, "cpe"))
|
||||
return PRI_CPE;
|
||||
if (!strcasecmp(node, "network"))
|
||||
return PRI_NETWORK;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int str2switch(char *swtype)
|
||||
{
|
||||
if (!strcasecmp(swtype, "ni2"))
|
||||
return PRI_SWITCH_NI2;
|
||||
if (!strcasecmp(swtype, "dms100"))
|
||||
return PRI_SWITCH_DMS100;
|
||||
if (!strcasecmp(swtype, "lucent5e"))
|
||||
return PRI_SWITCH_LUCENT5E;
|
||||
if (!strcasecmp(swtype, "att4ess"))
|
||||
return PRI_SWITCH_ATT4ESS;
|
||||
if (!strcasecmp(swtype, "euroisdn"))
|
||||
return PRI_SWITCH_EUROISDN_E1;
|
||||
if (!strcasecmp(swtype, "gr303eoc"))
|
||||
return PRI_SWITCH_GR303_EOC;
|
||||
if (!strcasecmp(swtype, "gr303tmc"))
|
||||
return PRI_SWITCH_GR303_TMC;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void set_global_dialplan(char *dialplan);
|
||||
static int str2node(char *node);
|
||||
static int str2switch(char *swtype);
|
||||
static switch_status wanchan_on_init(switch_core_session *session);
|
||||
static switch_status wanchan_on_hangup(switch_core_session *session);
|
||||
static switch_status wanchan_on_loopback(switch_core_session *session);
|
||||
static switch_status wanchan_on_transmit(switch_core_session *session);
|
||||
static switch_status wanchan_outgoing_channel(switch_core_session *session, switch_caller_profile *outbound_profile, switch_core_session **new_session);
|
||||
static switch_status wanchan_read_frame(switch_core_session *session, switch_frame **frame, int timeout, switch_io_flag flags);
|
||||
static switch_status wanchan_write_frame(switch_core_session *session, switch_frame *frame, int timeout, switch_io_flag flags);
|
||||
static int on_info(struct sangoma_pri *spri, sangoma_pri_event_t event_type, pri_event *event);
|
||||
static int on_hangup(struct sangoma_pri *spri, sangoma_pri_event_t event_type, pri_event *event);
|
||||
static int on_ring(struct sangoma_pri *spri, sangoma_pri_event_t event_type, pri_event *event);
|
||||
static int check_flags(struct sangoma_pri *spri);
|
||||
static int on_restart(struct sangoma_pri *spri, sangoma_pri_event_t event_type, pri_event *event);
|
||||
static int on_anything(struct sangoma_pri *spri, sangoma_pri_event_t event_type, pri_event *event);
|
||||
static void *pri_thread_run(switch_thread *thread, void *obj);
|
||||
static int config_wanpipe(int reload);
|
||||
|
||||
|
||||
|
||||
/*
|
||||
State methods they get called when the state changes to the specific state
|
||||
returning SWITCH_STATUS_SUCCESS tells the core to execute the standard state method next
|
||||
so if you fully implement the state you can return SWITCH_STATUS_FALSE to skip it.
|
||||
*/
|
||||
static switch_status wanchan_on_init(switch_core_session *session)
|
||||
{
|
||||
struct private_object *tech_pvt;
|
||||
switch_channel *channel = NULL;
|
||||
|
||||
channel = switch_core_session_get_channel(session);
|
||||
assert(channel != NULL);
|
||||
|
||||
tech_pvt = switch_core_session_get_private(session);
|
||||
assert(tech_pvt != NULL);
|
||||
|
||||
tech_pvt->frame.data = tech_pvt->databuf;
|
||||
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "WANCHAN INIT\n");
|
||||
|
||||
|
||||
/* Move Channel's State Machine to RING */
|
||||
switch_channel_set_state(channel, CS_RING);
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static switch_status wanchan_on_ring(switch_core_session *session)
|
||||
{
|
||||
switch_channel *channel = NULL;
|
||||
struct private_object *tech_pvt = NULL;
|
||||
|
||||
channel = switch_core_session_get_channel(session);
|
||||
assert(channel != NULL);
|
||||
|
||||
tech_pvt = switch_core_session_get_private(session);
|
||||
assert(tech_pvt != NULL);
|
||||
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "WANCHAN RING\n");
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static switch_status wanchan_on_hangup(switch_core_session *session)
|
||||
{
|
||||
struct private_object *tech_pvt;
|
||||
switch_channel *channel = NULL;
|
||||
|
||||
channel = switch_core_session_get_channel(session);
|
||||
assert(channel != NULL);
|
||||
|
||||
tech_pvt = switch_core_session_get_private(session);
|
||||
assert(tech_pvt != NULL);
|
||||
|
||||
switch_socket_close(&tech_pvt->socket);
|
||||
|
||||
pri_hangup(tech_pvt->spri->pri, tech_pvt->hangup_event.hangup.call ? tech_pvt->hangup_event.hangup.call : tech_pvt->ring_event.ring.call, tech_pvt->cause);
|
||||
pri_destroycall(tech_pvt->spri->pri, tech_pvt->hangup_event.hangup.call ? tech_pvt->hangup_event.hangup.call : tech_pvt->ring_event.ring.call);
|
||||
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "WANCHAN HANGUP\n");
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static switch_status wanchan_on_loopback(switch_core_session *session)
|
||||
{
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "WANCHAN LOOPBACK\n");
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static switch_status wanchan_on_transmit(switch_core_session *session)
|
||||
{
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "WANCHAN TRANSMIT\n");
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static switch_status wanchan_outgoing_channel(switch_core_session *session, switch_caller_profile *outbound_profile, switch_core_session **new_session)
|
||||
{
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "NOT IMPLEMENTED\n");
|
||||
|
||||
return SWITCH_STATUS_GENERR;
|
||||
}
|
||||
|
||||
static switch_status wanchan_answer_channel(switch_core_session *session)
|
||||
{
|
||||
struct private_object *tech_pvt;
|
||||
switch_channel *channel = NULL;
|
||||
|
||||
channel = switch_core_session_get_channel(session);
|
||||
assert(channel != NULL);
|
||||
|
||||
tech_pvt = switch_core_session_get_private(session);
|
||||
assert(tech_pvt != NULL);
|
||||
|
||||
pri_answer(tech_pvt->spri->pri, tech_pvt->ring_event.ring.call, 0, 1);
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static switch_status wanchan_read_frame(switch_core_session *session, switch_frame **frame, int timeout, switch_io_flag flags)
|
||||
{
|
||||
struct private_object *tech_pvt;
|
||||
switch_channel *channel = NULL;
|
||||
void *bp;
|
||||
int bytes = 0, res = 0;
|
||||
|
||||
channel = switch_core_session_get_channel(session);
|
||||
assert(channel != NULL);
|
||||
|
||||
tech_pvt = switch_core_session_get_private(session);
|
||||
assert(tech_pvt != NULL);
|
||||
|
||||
bp = tech_pvt->databuf;
|
||||
|
||||
*frame = NULL;
|
||||
memset(tech_pvt->databuf, 0, sizeof(tech_pvt->databuf));
|
||||
while (bytes < globals.bytes_per_frame) {
|
||||
if ((res = switch_socket_waitfor(tech_pvt->socket, timeout, POLLIN|POLLERR)) < 0) {
|
||||
return SWITCH_STATUS_GENERR;
|
||||
} else if (res == 0) {
|
||||
tech_pvt->frame.datalen = 0;
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if ((res = sangoma_readmsg_socket(
|
||||
tech_pvt->socket,
|
||||
&tech_pvt->hdrframe,
|
||||
sizeof(tech_pvt->hdrframe),
|
||||
bp,
|
||||
sizeof(tech_pvt->databuf) - bytes, 0)) < 0) {
|
||||
if (errno == EBUSY) {
|
||||
continue;
|
||||
} else {
|
||||
return SWITCH_STATUS_GENERR;
|
||||
}
|
||||
}
|
||||
bytes += res;
|
||||
bp += bytes;
|
||||
}
|
||||
tech_pvt->frame.datalen = bytes;
|
||||
|
||||
*frame = &tech_pvt->frame;
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static switch_status wanchan_write_frame(switch_core_session *session, switch_frame *frame, int timeout, switch_io_flag flags)
|
||||
{
|
||||
struct private_object *tech_pvt;
|
||||
switch_channel *channel = NULL;
|
||||
int res = 0;
|
||||
int bytes = frame->datalen;
|
||||
void *bp = frame->data;
|
||||
switch_status status = SWITCH_STATUS_SUCCESS;
|
||||
|
||||
channel = switch_core_session_get_channel(session);
|
||||
assert(channel != NULL);
|
||||
|
||||
tech_pvt = switch_core_session_get_private(session);
|
||||
assert(tech_pvt != NULL);
|
||||
|
||||
while (bytes > 0) {
|
||||
switch_socket_waitfor(tech_pvt->socket, -1, POLLOUT | POLLERR | POLLHUP);
|
||||
res = sangoma_sendmsg_socket(
|
||||
tech_pvt->socket,
|
||||
&tech_pvt->hdrframe,
|
||||
sizeof(tech_pvt->hdrframe),
|
||||
bp,
|
||||
PACKET_LEN, 0);
|
||||
if (res < 0) {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Bad Write frame len %d write %d bytes returned %d errno %d!\n", frame->datalen, PACKET_LEN, res, errno);
|
||||
if (errno == EBUSY) {
|
||||
continue;
|
||||
}
|
||||
status = SWITCH_STATUS_GENERR;
|
||||
break;
|
||||
} else {
|
||||
bytes -= res;
|
||||
bp += res;
|
||||
res = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static const switch_io_routines wanchan_io_routines*/ {
|
||||
/*.outgoing_channel*/ wanchan_outgoing_channel,
|
||||
/*.answer_channel*/ wanchan_answer_channel,
|
||||
/*.read_frame*/ wanchan_read_frame,
|
||||
/*.write_frame*/ wanchan_write_frame
|
||||
};
|
||||
|
||||
static const switch_event_handler_table wanchan_event_handlers = {
|
||||
/*.on_init*/ wanchan_on_init,
|
||||
/*.on_ring*/ wanchan_on_ring,
|
||||
/*.on_execute*/ NULL,
|
||||
/*.on_hangup*/ wanchan_on_hangup,
|
||||
/*.on_loopback*/ wanchan_on_loopback,
|
||||
/*.on_transmit*/ wanchan_on_transmit
|
||||
};
|
||||
|
||||
static const switch_endpoint_interface wanchan_endpoint_interface = {
|
||||
/*.interface_name*/ "wanchan",
|
||||
/*.io_routines*/ &wanchan_io_routines,
|
||||
/*.event_handlers*/ &wanchan_event_handlers,
|
||||
/*.private*/ NULL,
|
||||
/*.next*/ NULL
|
||||
};
|
||||
|
||||
static const switch_loadable_module_interface wanchan_module_interface = {
|
||||
/*.module_name*/ modname,
|
||||
/*.endpoint_interface*/ &wanchan_endpoint_interface,
|
||||
/*.timer_interface*/ NULL,
|
||||
/*.dialplan_interface*/ NULL,
|
||||
/*.codec_interface*/ NULL,
|
||||
/*.application_interface*/ NULL
|
||||
};
|
||||
|
||||
Public switch_status switch_module_load(const switch_loadable_module_interface **interface, chanr *filename) {
|
||||
|
||||
|
||||
if (switch_core_new_memory_pool(&module_pool) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "OH OH no pool\n");
|
||||
return SWITCH_STATUS_TERM;
|
||||
}
|
||||
|
||||
/* connect my internal structure to the blank pointer passed to me */
|
||||
*interface = &wanchan_module_interface;
|
||||
|
||||
/* indicate that the module should continue to be loaded */
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* Event Handlers */
|
||||
|
||||
static int on_info(struct sangoma_pri *spri, sangoma_pri_event_t event_type, pri_event *event)
|
||||
{
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "number is: %s\n", event->ring.callednum);
|
||||
if (strlen(event->ring.callednum) > 3) {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "final number is: %s\n", event->ring.callednum);
|
||||
pri_answer(spri->pri, event->ring.call, 0, 1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_hangup(struct sangoma_pri *spri, sangoma_pri_event_t event_type, pri_event *event)
|
||||
{
|
||||
struct channel_map *chanmap;
|
||||
switch_core_session *session;
|
||||
struct private_object *tech_pvt;
|
||||
|
||||
chanmap = spri->private;
|
||||
if ((session = chanmap->map[event->hangup.channel])) {
|
||||
switch_channel *channel = NULL;
|
||||
|
||||
channel = switch_core_session_get_channel(session);
|
||||
assert(channel != NULL);
|
||||
|
||||
tech_pvt = switch_core_session_get_private(session);
|
||||
assert(tech_pvt != NULL);
|
||||
|
||||
tech_pvt->cause = event->hangup.cause;
|
||||
memcpy(&tech_pvt->hangup_event, event, sizeof(*event));
|
||||
|
||||
switch_channel_set_state(channel, CS_HANGUP);
|
||||
}
|
||||
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE,"-- Hanging up channel %d\n", event->hangup.channel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_ring(struct sangoma_pri *spri, sangoma_pri_event_t event_type, pri_event *event)
|
||||
{
|
||||
char name[128];
|
||||
switch_core_session *session;
|
||||
switch_channel *channel;
|
||||
struct channel_map *chanmap;
|
||||
|
||||
|
||||
|
||||
chanmap = spri->private;
|
||||
if (chanmap->map[event->ring.channel]) {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE,"--Duplicate Ring on channel %d (ignored)\n", event->ring.channel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE,"-- Ring on channel %d (from %s to %s)\n", event->ring.channel, event->ring.callingnum, event->ring.callednum);
|
||||
|
||||
sprintf(name, "w%dg%d", globals.span, event->ring.channel);
|
||||
if ((session = switch_core_session_request(&wanchan_endpoint_interface, NULL))) {
|
||||
struct private_object *tech_pvt;
|
||||
int fd;
|
||||
char ani2str[4] = "";
|
||||
//wanpipe_tdm_api_t tdm_api;
|
||||
|
||||
if ((tech_pvt = (struct private_object *) switch_core_session_alloc(session, sizeof(struct private_object)))) {
|
||||
memset(tech_pvt, 0, sizeof(*tech_pvt));
|
||||
channel = switch_core_session_get_channel(session);
|
||||
switch_core_session_set_private(session, tech_pvt);
|
||||
} else {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Hey where is my memory pool?\n");
|
||||
switch_core_session_destroy(&session);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (event->ring.ani2 >= 0) {
|
||||
snprintf(ani2str, 5, "%.2d", event->ring.ani2);
|
||||
}
|
||||
|
||||
if ((tech_pvt->caller_profile = switch_caller_profile_new(session,
|
||||
globals.dialplan,
|
||||
"wanchan fixme",
|
||||
event->ring.callingnum,
|
||||
event->ring.callingani,
|
||||
switch_strlen_zero(ani2str) ? NULL : ani2str,
|
||||
event->ring.callednum))) {
|
||||
switch_channel_set_caller_profile(channel, tech_pvt->caller_profile);
|
||||
}
|
||||
|
||||
switch_set_flag(tech_pvt, TFLAG_INBOUND);
|
||||
tech_pvt->spri = spri;
|
||||
tech_pvt->cause = -1;
|
||||
|
||||
memcpy(&tech_pvt->ring_event, event, sizeof(*event));
|
||||
|
||||
tech_pvt->callno = event->ring.channel;
|
||||
|
||||
if ((fd = sangoma_create_socket_intr(spri->span, event->ring.channel)) < 0) {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Can't open fd!\n");
|
||||
}
|
||||
|
||||
//sangoma_tdm_set_hw_period(fd, &tdm_api, 480);
|
||||
|
||||
tech_pvt->socket = fd;
|
||||
chanmap->map[event->ring.channel] = session;
|
||||
|
||||
switch_channel_set_state(channel, CS_INIT);
|
||||
switch_core_session_thread_launch(session);
|
||||
} else {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Cannot Create new Inbound Channel!\n");
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int check_flags(struct sangoma_pri *spri)
|
||||
{
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_restart(struct sangoma_pri *spri, sangoma_pri_event_t event_type, pri_event *event)
|
||||
{
|
||||
int fd;
|
||||
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE,"-- Restarting channel %d\n", event->restart.channel);
|
||||
|
||||
if ((fd = sangoma_create_socket_intr(spri->span, event->restart.channel)) < 0) {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE,"Can't open fd!\n");
|
||||
} else {
|
||||
close(fd);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_anything(struct sangoma_pri *spri, sangoma_pri_event_t event_type, pri_event *event)
|
||||
{
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE,"Caught Event %d (%s)\n", event_type, sangoma_pri_event_str(event_type));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void *pri_thread_run(switch_thread *thread, void *obj)
|
||||
{
|
||||
struct sangoma_pri *spri = obj;
|
||||
struct channel_map chanmap;
|
||||
|
||||
SANGOMA_MAP_PRI_EVENT((*spri), SANGOMA_PRI_EVENT_ANY, on_anything);
|
||||
SANGOMA_MAP_PRI_EVENT((*spri), SANGOMA_PRI_EVENT_RING, on_ring);
|
||||
SANGOMA_MAP_PRI_EVENT((*spri), SANGOMA_PRI_EVENT_HANGUP_REQ, on_hangup);
|
||||
SANGOMA_MAP_PRI_EVENT((*spri), SANGOMA_PRI_EVENT_INFO_RECEIVED, on_info);
|
||||
SANGOMA_MAP_PRI_EVENT((*spri), SANGOMA_PRI_EVENT_RESTART, on_restart);
|
||||
|
||||
spri->on_loop = check_flags;
|
||||
spri->private = &chanmap;
|
||||
sangoma_run_pri(spri);
|
||||
|
||||
free(spri);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static int config_wanpipe(int reload)
|
||||
{
|
||||
switch_config cfg;
|
||||
char *var, *val;
|
||||
int count = 0;
|
||||
struct sangoma_pri *spri;
|
||||
char *cf = "wanchan.conf";
|
||||
|
||||
globals.bytes_per_frame = DEFAULT_BYTES_PER_FRAME;
|
||||
|
||||
if (!switch_config_open_file(&cfg, cf)) {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "open of %s failed\n", cf);
|
||||
return SWITCH_STATUS_TERM;
|
||||
}
|
||||
|
||||
while (switch_config_next_pair(&cfg, &var, &val)) {
|
||||
if (!strcasecmp(cfg.category, "settings")) {
|
||||
if (!strcmp(var, "debug")) {
|
||||
globals.debug = atoi(val);
|
||||
} else if (!strcmp(var, "span")) {
|
||||
globals.span = atoi(val);
|
||||
} else if (!strcmp(var, "dchan")) {
|
||||
globals.dchan = atoi(val);
|
||||
} else if (!strcmp(var, "node")) {
|
||||
globals.node = str2node(val);
|
||||
} else if (!strcmp(var, "switch")) {
|
||||
globals.pswitch = str2switch(val);
|
||||
} else if (!strcmp(var, "bpf")) {
|
||||
globals.bytes_per_frame = atoi(val);
|
||||
} else if (!strcmp(var, "dialplan")) {
|
||||
set_global_dialplan(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch_config_close_file(&cfg);
|
||||
|
||||
if (!globals.dialplan) {
|
||||
set_global_dialplan("default");
|
||||
}
|
||||
|
||||
if ((spri=switch_core_alloc(module_pool, sizeof(*spri)))) {
|
||||
memset(spri, 0, sizeof(*spri));
|
||||
sangoma_init_pri(spri,
|
||||
globals.span,
|
||||
globals.dchan,
|
||||
23,
|
||||
globals.pswitch,
|
||||
globals.node,
|
||||
globals.debug);
|
||||
|
||||
pri_thread_run(NULL, spri);
|
||||
|
||||
} else {
|
||||
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "error!\n");
|
||||
}
|
||||
|
||||
return count;
|
||||
|
||||
}
|
||||
|
||||
|
||||
Public switch_status switch_module_runtime(void)
|
||||
{
|
||||
config_wanpipe(0);
|
||||
return SWITCH_STATUS_TERM;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,207 @@
|
||||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="8.00"
|
||||
Name="mod_woomerachan"
|
||||
ProjectGUID="{FE3540C5-3303-46E0-A69E-D92F775687F1}"
|
||||
RootNamespace="mod_woomerachan"
|
||||
Keyword="Win32Proj"
|
||||
>
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"
|
||||
/>
|
||||
</Platforms>
|
||||
<ToolFiles>
|
||||
</ToolFiles>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
OutputDirectory="Debug"
|
||||
IntermediateDirectory="Debug"
|
||||
ConfigurationType="2"
|
||||
InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
|
||||
CharacterSet="2"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
AdditionalIncludeDirectories=""$(InputDir)..\..\include";"$(InputDir)include";"$(InputDir)..\..\..\libs\include""
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;MOD_EXPORTS"
|
||||
MinimalRebuild="true"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="1"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="true"
|
||||
DebugInformationFormat="4"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
OutputFile="..\..\..\w32\vsnet\$(OutDir)/mod/mod_woomerachan.dll"
|
||||
LinkIncremental="2"
|
||||
AdditionalLibraryDirectories="$(InputDir)..\..\libs\apr\Debug"
|
||||
GenerateDebugInformation="true"
|
||||
ProgramDatabaseFile="$(OutDir)/mod_woomerachan.pdb"
|
||||
SubSystem="2"
|
||||
ImportLibrary="$(OutDir)/mod_woomerachan.lib"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
OutputDirectory="Release"
|
||||
IntermediateDirectory="Release"
|
||||
ConfigurationType="2"
|
||||
InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
|
||||
CharacterSet="2"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
AdditionalIncludeDirectories=""$(InputDir)..\..\include";"$(InputDir)include";"$(InputDir)..\..\..\libs\include""
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;MOD_EXPORTS"
|
||||
RuntimeLibrary="0"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="true"
|
||||
DebugInformationFormat="3"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
OutputFile="..\..\..\w32\vsnet\$(OutDir)/mod/mod_woomerachan.dll"
|
||||
LinkIncremental="1"
|
||||
AdditionalLibraryDirectories=""$(InputDir)..\..\libs\apr\Release""
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="2"
|
||||
OptimizeReferences="2"
|
||||
EnableCOMDATFolding="2"
|
||||
ImportLibrary="$(OutDir)/mod_woomerachan.lib"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<Filter
|
||||
Name="Source Files"
|
||||
Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
|
||||
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\mod_woomerachan.c"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Header Files"
|
||||
Filter="h;hpp;hxx;hm;inl;inc;xsd"
|
||||
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
|
||||
>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Resource Files"
|
||||
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
|
||||
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
|
||||
>
|
||||
</Filter>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
||||
Reference in New Issue
Block a user