--- /dev/null
+/*
+ * 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_bridgecall.c -- Channel Bridge Application Module
+ *
+ */
+#include <switch.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+
+static const char modname[] = "mod_bridgecall";
+
+struct audio_bridge_data {
+ switch_core_session *session_a;
+ switch_core_session *session_b;
+ int running;
+};
+
+static void *audio_bridge_thread(switch_thread *thread, void *obj)
+{
+ struct switch_core_thread_session *data = obj;
+
+ switch_channel *chan_a, *chan_b;
+ switch_frame *read_frame;
+ switch_core_session *session_a, *session_b;
+
+ session_a = data->objs[0];
+ session_b = data->objs[1];
+
+ chan_a = switch_core_session_get_channel(session_a);
+ chan_b = switch_core_session_get_channel(session_b);
+
+ while(data->running > 0) {
+ switch_channel_state b_state = switch_channel_get_state(chan_b);
+
+ switch (b_state) {
+case CS_HANGUP:
+ data->running = -1;
+ continue;
+ break;
+default:
+ break;
+ }
+
+ if (switch_channel_has_dtmf(chan_a)) {
+ char dtmf[128];
+ switch_channel_dequeue_dtmf(chan_a, dtmf, sizeof(dtmf));
+ switch_core_session_send_dtmf(session_b, dtmf);
+ }
+ if (switch_core_session_read_frame(session_a, &read_frame, -1) == SWITCH_STATUS_SUCCESS && read_frame->datalen) {
+ if (switch_core_session_write_frame(session_b, read_frame, -1) != SWITCH_STATUS_SUCCESS) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "write: Bad Frame.... %d Bubye!\n", read_frame->datalen);
+ data->running = -1;
+ }
+ } else {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "read: Bad Frame.... %d Bubye!\n", read_frame->datalen);
+ data->running = -1;
+ }
+ }
+
+ switch_channel_hangup(chan_b);
+ data->running = 0;
+
+ return NULL;
+}
+
+
+static switch_status audio_bridge_on_hangup(switch_core_session *session)
+{
+ switch_core_session *other_session;
+ switch_channel *channel = NULL, *other_channel = NULL;
+
+ channel = switch_core_session_get_channel(session);
+ assert(channel != NULL);
+
+ other_session = switch_channel_get_private(channel);
+ assert(other_session != NULL);
+
+ other_channel = switch_core_session_get_channel(other_session);
+ assert(other_channel != NULL);
+
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "CUSTOM HANGUP %s kill %s\n", switch_channel_get_name(channel), switch_channel_get_name(other_channel));
+
+ switch_core_session_kill_channel(other_session, SWITCH_SIG_KILL);
+ switch_core_session_kill_channel(session, SWITCH_SIG_KILL);
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status audio_bridge_on_ring(switch_core_session *session)
+{
+ switch_channel *channel = NULL;
+
+ channel = switch_core_session_get_channel(session);
+ assert(channel != NULL);
+
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "CUSTOM RING\n");
+
+ /* put the channel in a passive state so we can loop audio to it */
+ if (switch_channel_test_flag(channel, CF_OUTBOUND)) {
+ switch_channel_set_state(channel, CS_TRANSMIT);
+ return SWITCH_STATUS_FALSE;
+ }
+
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static const switch_event_handler_table audio_bridge_peer_event_handlers = {
+ /*.on_init*/ NULL,
+ /*.on_ring*/ audio_bridge_on_ring,
+ /*.on_execute*/ NULL,
+ /*.on_hangup*/ audio_bridge_on_hangup,
+ /*.on_loopback*/ NULL,
+ /*.on_transmit*/ NULL
+};
+
+static const switch_event_handler_table audio_bridge_caller_event_handlers = {
+ /*.on_init*/ NULL,
+ /*.on_ring*/ NULL,
+ /*.on_execute*/ NULL,
+ /*.on_hangup*/ audio_bridge_on_hangup,
+ /*.on_loopback*/ NULL,
+ /*.on_transmit*/ NULL
+};
+
+static void audio_bridge_function(switch_core_session *session, char *data)
+{
+ switch_channel *caller_channel, *peer_channel;
+ switch_core_session *peer_session;
+ switch_caller_profile *caller_profile, *caller_caller_profile;
+ char chan_type[128]= {'\0'}, *chan_data;
+ int timelimit = 60; /* probably a useful option to pass in when there's time */
+ caller_channel = switch_core_session_get_channel(session);
+ assert(caller_channel != NULL);
+
+
+ strncpy(chan_type, data, sizeof(chan_type));
+
+ if ((chan_data = strchr(chan_type, '/'))) {
+ *chan_data = '\0';
+ chan_data++;
+ }
+
+ caller_caller_profile = switch_channel_get_caller_profile(caller_channel);
+ caller_profile = switch_caller_profile_new(session,
+ caller_caller_profile->dialplan,
+ caller_caller_profile->caller_id_name,
+ caller_caller_profile->caller_id_number,
+ caller_caller_profile->network_addr,
+ NULL,
+ NULL,
+ chan_data);
+
+
+
+ if (switch_core_session_outgoing_channel(session, chan_type, caller_profile, &peer_session) != SWITCH_STATUS_SUCCESS) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "DOH!\n");
+ switch_channel_hangup(caller_channel);
+ return;
+ } else {
+ struct switch_core_thread_session this_audio_thread, other_audio_thread;
+ time_t start;
+
+ peer_channel = switch_core_session_get_channel(peer_session);
+ memset(&other_audio_thread, 0, sizeof(other_audio_thread));
+ memset(&this_audio_thread, 0, sizeof(this_audio_thread));
+ other_audio_thread.objs[0] = session;
+ other_audio_thread.objs[1] = peer_session;
+ other_audio_thread.running = 5;
+
+ this_audio_thread.objs[0] = peer_session;
+ this_audio_thread.objs[1] = session;
+ this_audio_thread.running = 2;
+
+
+ switch_channel_set_private(caller_channel, peer_session);
+ switch_channel_set_private(peer_channel, session);
+ switch_channel_set_event_handlers(caller_channel, &audio_bridge_caller_event_handlers);
+ switch_channel_set_event_handlers(peer_channel, &audio_bridge_peer_event_handlers);
+ switch_core_session_thread_launch(peer_session);
+
+ for(;;) {
+ int state = switch_channel_get_state(peer_channel);
+ if (state > CS_RING) {
+ break;
+ }
+ switch_yield(1000);
+ }
+
+ time(&start);
+ while(switch_channel_get_state(caller_channel) == CS_EXECUTE &&
+ switch_channel_get_state(peer_channel) == CS_TRANSMIT &&
+ !switch_channel_test_flag(peer_channel, CF_ANSWERED) &&
+ ((time(NULL) - start) < timelimit)) {
+ switch_yield(20000);
+ }
+
+ if (switch_channel_test_flag(peer_channel, CF_ANSWERED)) {
+ switch_channel_answer(caller_channel);
+
+ switch_core_session_launch_thread(session, audio_bridge_thread, (void *) &other_audio_thread);
+ audio_bridge_thread(NULL, (void *) &this_audio_thread);
+ switch_channel_hangup(peer_channel);
+ if (other_audio_thread.running > 0) {
+ other_audio_thread.running = -1;
+ /* wait for the other audio thread */
+ while (other_audio_thread.running) {
+ switch_yield(1000);
+ }
+ }
+
+
+ }
+ }
+
+ switch_channel_hangup(caller_channel);
+ switch_channel_hangup(peer_channel);
+}
+
+
+static const switch_application_interface bridge_application_interface = {
+ /*.interface_name*/ "bridge",
+ /*.application_function*/ audio_bridge_function
+};
+
+
+static const switch_loadable_module_interface mod_bridgecall_module_interface = {
+ /*.module_name = */ modname,
+ /*.endpoint_interface = */ NULL,
+ /*.timer_interface = */ NULL,
+ /*.dialplan_interface = */ NULL,
+ /*.codec_interface = */ NULL,
+ /*.application_interface*/ &bridge_application_interface
+};
+
+SWITCH_MOD_DECLARE(switch_status) switch_module_load(const switch_loadable_module_interface **interface, char *filename) {
+
+ /* connect my internal structure to the blank pointer passed to me */
+ *interface = &mod_bridgecall_module_interface;
+
+ /* indicate that the module should continue to be loaded */
+ return SWITCH_STATUS_SUCCESS;
+}
--- /dev/null
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+ ProjectType="Visual C++"\r
+ Version="8.00"\r
+ Name="mod_bridgecall"\r
+ ProjectGUID="{E1794405-29D4-466D-9BE3-DD2344C2A663}"\r
+ RootNamespace="mod_bridgecall"\r
+ Keyword="Win32Proj"\r
+ >\r
+ <Platforms>\r
+ <Platform\r
+ Name="Win32"\r
+ />\r
+ </Platforms>\r
+ <ToolFiles>\r
+ </ToolFiles>\r
+ <Configurations>\r
+ <Configuration\r
+ Name="Debug|Win32"\r
+ OutputDirectory="Debug"\r
+ IntermediateDirectory="Debug"\r
+ ConfigurationType="2"\r
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"\r
+ CharacterSet="2"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""$(InputDir)..\..\include";"$(InputDir)include";"$(InputDir)..\..\..\libs\include""\r
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;MOD_EXPORTS"\r
+ MinimalRebuild="true"\r
+ BasicRuntimeChecks="3"\r
+ RuntimeLibrary="1"\r
+ UsePrecompiledHeader="0"\r
+ WarningLevel="3"\r
+ Detect64BitPortabilityProblems="true"\r
+ DebugInformationFormat="4"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ OutputFile="..\..\..\w32\vsnet\$(OutDir)/mod/mod_bridgecall.dll"\r
+ LinkIncremental="2"\r
+ AdditionalLibraryDirectories="$(InputDir)..\..\libs\apr\Debug"\r
+ GenerateDebugInformation="true"\r
+ ProgramDatabaseFile="$(OutDir)/mod_bridgecall.pdb"\r
+ SubSystem="2"\r
+ ImportLibrary="$(OutDir)/mod_bridgecall.lib"\r
+ TargetMachine="1"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebDeploymentTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ <Configuration\r
+ Name="Release|Win32"\r
+ OutputDirectory="Release"\r
+ IntermediateDirectory="Release"\r
+ ConfigurationType="2"\r
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"\r
+ CharacterSet="2"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""$(InputDir)..\..\include";"$(InputDir)include";"$(InputDir)..\..\..\libs\include""\r
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;MOD_EXPORTS"\r
+ RuntimeLibrary="0"\r
+ UsePrecompiledHeader="0"\r
+ WarningLevel="3"\r
+ Detect64BitPortabilityProblems="true"\r
+ DebugInformationFormat="3"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ OutputFile="..\..\..\w32\vsnet\$(OutDir)/mod/mod_bridgecall.dll"\r
+ LinkIncremental="1"\r
+ AdditionalLibraryDirectories=""$(InputDir)..\..\libs\apr\Release""\r
+ GenerateDebugInformation="true"\r
+ SubSystem="2"\r
+ OptimizeReferences="2"\r
+ EnableCOMDATFolding="2"\r
+ ImportLibrary="$(OutDir)/mod_bridgecall.lib"\r
+ TargetMachine="1"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebDeploymentTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ </Configurations>\r
+ <References>\r
+ </References>\r
+ <Files>\r
+ <Filter\r
+ Name="Source Files"\r
+ Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"\r
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"\r
+ >\r
+ <File\r
+ RelativePath=".\mod_bridgecall.c"\r
+ >\r
+ </File>\r
+ </Filter>\r
+ <Filter\r
+ Name="Header Files"\r
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"\r
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"\r
+ >\r
+ </Filter>\r
+ <Filter\r
+ Name="Resource Files"\r
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"\r
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"\r
+ >\r
+ </Filter>\r
+ </Files>\r
+ <Globals>\r
+ </Globals>\r
+</VisualStudioProject>\r
--- /dev/null
+/*
+ * 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_playback.c -- Raw Audio File Streaming Application Module
+ *
+ */
+#include <switch.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+
+static const char modname[] = "mod_playback";
+
+
+void playback_function(switch_core_session *session, char *data)
+{
+ switch_channel *channel;
+ short buf[960];
+ char dtmf[128];
+ int interval = 0, samples = 0;
+ size_t len = 0, ilen = 0;
+ switch_frame write_frame;
+ switch_timer timer;
+ switch_core_thread_session thread_session;
+ switch_codec codec;
+ switch_memory_pool *pool = switch_core_session_get_pool(session);
+ switch_file_handle fh;
+ char *codec_name;
+ int x;
+
+ channel = switch_core_session_get_channel(session);
+ assert(channel != NULL);
+
+ if (switch_core_file_open(&fh,
+ data,
+ SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT,
+ switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) {
+ switch_channel_hangup(channel);
+ return;
+ }
+
+ switch_channel_answer(channel);
+
+ write_frame.data = buf;
+ write_frame.buflen = sizeof(buf);
+
+
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "OPEN FILE %s %dhz %d channels\n", data, fh.samplerate, fh.channels);
+
+ interval = 20;
+ samples = (fh.samplerate / 50) * fh.channels;
+ len = samples * 2;
+
+ codec_name = "L16";
+
+ if (switch_core_codec_init(&codec,
+ codec_name,
+ fh.samplerate,
+ interval,
+ fh.channels,
+ SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE,
+ NULL,
+ pool) == SWITCH_STATUS_SUCCESS) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Raw Codec Activated\n");
+ write_frame.codec = &codec;
+ } else {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Raw Codec Activation Failed %s@%dhz %d channels %dms\n", codec_name, fh.samplerate, fh.channels, interval);
+ switch_core_file_close(&fh);
+ switch_channel_hangup(channel);
+ return;
+ }
+
+ if (switch_core_timer_init(&timer, "soft", interval, samples, pool) != SWITCH_STATUS_SUCCESS) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "setup timer failed!\n");
+ switch_core_codec_destroy(&codec);
+ switch_core_file_close(&fh);
+ switch_channel_hangup(channel);
+ return;
+ }
+ write_frame.rate = fh.samplerate;
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "setup timer success %d bytes per %d ms!\n", len, interval);
+
+ /* start a thread to absorb incoming audio */
+ switch_core_service_session(session, &thread_session);
+ ilen = samples;
+ while(switch_channel_get_state(channel) == CS_EXECUTE) {
+ int done = 0;
+
+ if (switch_channel_has_dtmf(channel)) {
+ switch_channel_dequeue_dtmf(channel, dtmf, sizeof(dtmf));
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "DTMF [%s]\n", dtmf);
+
+ switch (*dtmf) {
+ case '*':
+ done = 1;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (done) {
+ break;
+ }
+
+ switch_core_file_read(&fh, buf, &ilen);
+
+ if (ilen <= 0) {
+ break;
+ }
+
+ write_frame.datalen = ilen * 2;
+ write_frame.samples = (int)ilen;
+#ifdef SWAP_LINEAR
+ switch_swap_linear(write_frame.data, (int)write_frame.datalen / 2);
+#endif
+ if (switch_core_session_write_frame(session, &write_frame, -1) != SWITCH_STATUS_SUCCESS) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Bad Write\n");
+ break;
+ }
+
+ if ((x = switch_core_timer_next(&timer)) < 0) {
+ break;
+ }
+ }
+
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "done playing file\n");
+ switch_core_file_close(&fh);
+
+ /* End the audio absorbing thread */
+ switch_core_thread_session_end(&thread_session);
+
+ switch_core_timer_destroy(&timer);
+
+ switch_core_codec_destroy(&codec);
+
+ switch_channel_hangup(channel);
+}
+
+static const switch_application_interface playback_application_interface = {
+ /*.interface_name*/ "playback",
+ /*.application_function*/ playback_function
+};
+
+static const switch_loadable_module_interface mod_playback_module_interface = {
+ /*.module_name = */ modname,
+ /*.endpoint_interface = */ NULL,
+ /*.timer_interface = */ NULL,
+ /*.dialplan_interface = */ NULL,
+ /*.codec_interface = */ NULL,
+ /*.application_interface*/ &playback_application_interface
+};
+
+SWITCH_MOD_DECLARE(switch_status) switch_module_load(const switch_loadable_module_interface **interface, char *filename) {
+
+ /* connect my internal structure to the blank pointer passed to me */
+ *interface = &mod_playback_module_interface;
+
+
+ /* indicate that the module should continue to be loaded */
+ return SWITCH_STATUS_SUCCESS;
+}
+
+/* 'switch_module_runtime' will start up in a thread by itself just by having it exist
+if it returns anything but SWITCH_STATUS_TERM it will be called again automaticly
+*/
+
+
+//switch_status switch_module_runtime(void)
+
+
+
+
--- /dev/null
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+ ProjectType="Visual C++"\r
+ Version="8.00"\r
+ Name="mod_playback"\r
+ ProjectGUID="{78100236-7CEA-4948-96CC-E8ED3160329C}"\r
+ RootNamespace="mod_playback"\r
+ Keyword="Win32Proj"\r
+ >\r
+ <Platforms>\r
+ <Platform\r
+ Name="Win32"\r
+ />\r
+ </Platforms>\r
+ <ToolFiles>\r
+ </ToolFiles>\r
+ <Configurations>\r
+ <Configuration\r
+ Name="Debug|Win32"\r
+ OutputDirectory="Debug"\r
+ IntermediateDirectory="Debug"\r
+ ConfigurationType="2"\r
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"\r
+ CharacterSet="2"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""$(InputDir)..\..\include";"$(InputDir)include";"$(InputDir)..\..\..\libs\include""\r
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;MOD_EXPORTS"\r
+ MinimalRebuild="true"\r
+ BasicRuntimeChecks="3"\r
+ RuntimeLibrary="1"\r
+ UsePrecompiledHeader="0"\r
+ WarningLevel="3"\r
+ Detect64BitPortabilityProblems="true"\r
+ DebugInformationFormat="4"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ OutputFile="..\..\..\w32\vsnet\$(OutDir)/mod/mod_playback.dll"\r
+ LinkIncremental="2"\r
+ AdditionalLibraryDirectories="$(InputDir)..\..\libs\apr\Debug"\r
+ GenerateDebugInformation="true"\r
+ ProgramDatabaseFile="$(OutDir)/mod_playback.pdb"\r
+ SubSystem="2"\r
+ ImportLibrary="$(OutDir)/mod_playback.lib"\r
+ TargetMachine="1"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebDeploymentTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ <Configuration\r
+ Name="Release|Win32"\r
+ OutputDirectory="Release"\r
+ IntermediateDirectory="Release"\r
+ ConfigurationType="2"\r
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"\r
+ CharacterSet="2"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""$(InputDir)..\..\include";"$(InputDir)include";"$(InputDir)..\..\..\libs\include""\r
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;MOD_EXPORTS"\r
+ RuntimeLibrary="0"\r
+ UsePrecompiledHeader="0"\r
+ WarningLevel="3"\r
+ Detect64BitPortabilityProblems="true"\r
+ DebugInformationFormat="3"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ OutputFile="..\..\..\w32\vsnet\$(OutDir)/mod/mod_playback.dll"\r
+ LinkIncremental="1"\r
+ AdditionalLibraryDirectories=""$(InputDir)..\..\libs\apr\Release""\r
+ GenerateDebugInformation="true"\r
+ SubSystem="2"\r
+ OptimizeReferences="2"\r
+ EnableCOMDATFolding="2"\r
+ ImportLibrary="$(OutDir)/mod_playback.lib"\r
+ TargetMachine="1"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebDeploymentTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ </Configurations>\r
+ <References>\r
+ </References>\r
+ <Files>\r
+ <Filter\r
+ Name="Source Files"\r
+ Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"\r
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"\r
+ >\r
+ <File\r
+ RelativePath=".\mod_playback.c"\r
+ >\r
+ </File>\r
+ </Filter>\r
+ <Filter\r
+ Name="Header Files"\r
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"\r
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"\r
+ >\r
+ </Filter>\r
+ <Filter\r
+ Name="Resource Files"\r
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"\r
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"\r
+ >\r
+ </Filter>\r
+ </Files>\r
+ <Globals>\r
+ </Globals>\r
+</VisualStudioProject>\r
--- /dev/null
+/*
+ * 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_skel.c -- Framework Demo Module
+ *
+ */
+#include <switch.h>
+
+static const char modname[] = "mod_skel";
+
+static switch_loadable_module_interface skel_module_interface = {
+ /*.module_name*/ modname,
+ /*.endpoint_interface*/ NULL,
+ /*.timer_interface*/ NULL,
+ /*.dialplan_interface*/ NULL,
+ /*.codec_interface*/ NULL,
+ /*.application_interface*/ NULL
+};
+
+switch_status switch_module_load(switch_loadable_module_interface **interface, char *filename) {
+ /* connect my internal structure to the blank pointer passed to me */
+ *interface = &skel_module_interface;
+
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Hello World!\n");
+
+ /* indicate that the module should continue to be loaded */
+ return SWITCH_STATUS_SUCCESS;
+}
+
--- /dev/null
+CFLAGS += -I/usr/local/include/libg729
+LDFLAGS +=-lg729
+
+all: $(MOD).so
+
+$(MOD).so: $(MOD).c
+ $(CC) $(CFLAGS) -fPIC -c $(MOD).c -o $(MOD).o
+ $(CC) $(SOLINK) $(MOD).o -o $(MOD).so $(LDFLAGS) -lspeex
+
+clean:
+ rm -fr *.so *.o *~
+
--- /dev/null
+/* \r
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application\r
+ * Copyright (C) 2005/2006, Anthony Minessale II <anthmct@yahoo.com>\r
+ *\r
+ * Version: MPL 1.1\r
+ *\r
+ * The contents of this file are subject to the Mozilla Public License Version\r
+ * 1.1 (the "License"); you may not use this file except in compliance with\r
+ * the License. You may obtain a copy of the License at\r
+ * http://www.mozilla.org/MPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS IS" basis,\r
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\r
+ * for the specific language governing rights and limitations under the\r
+ * License.\r
+ *\r
+ * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application\r
+ *\r
+ * The Initial Developer of the Original Code is\r
+ * Anthony Minessale II <anthmct@yahoo.com>\r
+ * Portions created by the Initial Developer are Copyright (C)\r
+ * the Initial Developer. All Rights Reserved.\r
+ *\r
+ * Contributor(s):\r
+ * \r
+ * Anthony Minessale II <anthmct@yahoo.com>\r
+ * Michael Jerris <mike@jerris.com>\r
+ *\r
+ * mod_codec_g729.c -- G729 Codec Module\r
+ *\r
+ */\r
+#include "switch.h"\r
+#include "g729.h"\r
+\r
+static const char modname[] = "mod_codec_g729";\r
+\r
+struct g729_context {\r
+ struct dec_state decoder_object;\r
+ struct cod_state encoder_object;\r
+};\r
+\r
+static switch_status switch_g729_init(switch_codec *codec, switch_codec_flag flags, const struct switch_codec_settings *codec_settings)\r
+{\r
+ struct g729_context *context = NULL;\r
+ int encoding, decoding;\r
+\r
+ encoding = (flags & SWITCH_CODEC_FLAG_ENCODE);\r
+ decoding = (flags & SWITCH_CODEC_FLAG_DECODE);\r
+\r
+ if (!(encoding || decoding) || (!(context = switch_core_alloc(codec->memory_pool, sizeof(struct g729_context))))) {\r
+ return SWITCH_STATUS_FALSE;\r
+ } else {\r
+ if (encoding) {\r
+ g729_init_coder(&context->encoder_object, 0);\r
+ }\r
+ if (decoding) {\r
+ g729_init_decoder(&context->decoder_object);\r
+ }\r
+\r
+ codec->private = context;\r
+\r
+ return SWITCH_STATUS_SUCCESS;\r
+ }\r
+}\r
+\r
+static switch_status switch_g729_destroy(switch_codec *codec)\r
+{\r
+ codec->private = NULL;\r
+ return SWITCH_STATUS_SUCCESS;\r
+}\r
+\r
+\r
+static switch_status switch_g729_encode(switch_codec *codec,\r
+ switch_codec *other_codec,\r
+ void *decoded_data,\r
+ size_t decoded_data_len,\r
+ int decoded_rate,\r
+ void *encoded_data,\r
+ size_t *encoded_data_len,\r
+ int *encoded_rate,\r
+ unsigned int *flag)\r
+{\r
+ struct g729_context *context = codec->private;\r
+ int cbret = 0;\r
+\r
+ if (!context) {\r
+ return SWITCH_STATUS_FALSE;\r
+ }\r
+ if (decoded_data_len % 160 == 0) {\r
+ unsigned int new_len = 0;\r
+ INT16 *ddp = decoded_data;\r
+ char *edp = encoded_data;\r
+ int x;\r
+ int loops = (int) decoded_data_len / 160;\r
+\r
+ for(x = 0; x < loops && new_len < *encoded_data_len; x++) {\r
+ g729_coder(&context->encoder_object, ddp, edp, &cbret);\r
+ edp += 10;\r
+ ddp += 80;\r
+ new_len += 10;\r
+ }\r
+ if( new_len <= *encoded_data_len ) {\r
+ *encoded_data_len = new_len;\r
+ } else {\r
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "buffer overflow!!! %u >= %u\n", new_len, *encoded_data_len);\r
+ return SWITCH_STATUS_FALSE; \r
+ }\r
+ }\r
+ return SWITCH_STATUS_SUCCESS;\r
+}\r
+\r
+\r
+static switch_status switch_g729_decode(switch_codec *codec,\r
+ switch_codec *other_codec,\r
+ void *encoded_data,\r
+ size_t encoded_data_len,\r
+ int encoded_rate,\r
+ void *decoded_data,\r
+ size_t *decoded_data_len,\r
+ int *decoded_rate,\r
+ unsigned int *flag) \r
+{\r
+ struct g729_context *context = codec->private;\r
+\r
+ if (!context) {\r
+ return SWITCH_STATUS_FALSE;\r
+ }\r
+\r
+\r
+ if (encoded_data_len % 10 == 0) {\r
+ int loops = (int) encoded_data_len / 10;\r
+ char *edp = encoded_data;\r
+ short *ddp = decoded_data;\r
+ int x;\r
+ unsigned int new_len = 0;\r
+ for(x = 0; x < loops && new_len < *decoded_data_len; x++) {\r
+ g729_decoder(&context->decoder_object, ddp, edp, 10);\r
+ ddp += 80;\r
+ edp += 10;\r
+ new_len += 160;\r
+ }\r
+ if (new_len <= *decoded_data_len) {\r
+ *decoded_data_len = new_len;\r
+ } else {\r
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "buffer overflow!!!\n");\r
+ return SWITCH_STATUS_FALSE;\r
+ }\r
+ } else {\r
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "yo this frame is an odd size [%d]\n", encoded_data_len);\r
+ }\r
+\r
+\r
+ return SWITCH_STATUS_SUCCESS;\r
+}\r
+\r
+/* Registration */\r
+\r
+static const switch_codec_implementation g729_8k_implementation = {\r
+ /*.samples_per_second*/ 8000,\r
+ /*.bits_per_second*/ 64000,\r
+ /*.microseconds_per_frame*/ 20000,\r
+ /*.samples_per_frame*/ 160,\r
+ /*.bytes_per_frame*/ 320,\r
+ /*.encoded_bytes_per_frame*/ 20,\r
+ /*.number_of_channels*/ 1,\r
+ /*.pref_frames_per_packet*/ 1,\r
+ /*.max_frames_per_packet*/ 24,\r
+ /*.init*/ switch_g729_init,\r
+ /*.encode*/ switch_g729_encode,\r
+ /*.decode*/ switch_g729_decode,\r
+ /*.destroy*/ switch_g729_destroy,\r
+};\r
+\r
+static const switch_codec_interface g729_codec_interface = {\r
+ /*.interface_name*/ "g729",\r
+ /*.codec_type*/ SWITCH_CODEC_TYPE_AUDIO,\r
+ /*.ianacode*/ 18,\r
+ /*.iananame*/ "G729",\r
+ /*.implementations*/ &g729_8k_implementation,\r
+};\r
+\r
+static switch_loadable_module_interface g729_module_interface = {\r
+ /*.module_name*/ modname,\r
+ /*.endpoint_interface*/ NULL,\r
+ /*.timer_interface*/ NULL,\r
+ /*.dialplan_interface*/ NULL,\r
+ /*.codec_interface*/ &g729_codec_interface,\r
+ /*.application_interface*/ NULL\r
+};\r
+\r
+\r
+SWITCH_MOD_DECLARE(switch_status) switch_module_load(const switch_loadable_module_interface **interface, char *filename) {\r
+ /* connect my internal structure to the blank pointer passed to me */\r
+ *interface = &g729_module_interface;\r
+\r
+ /* indicate that the module should continue to be loaded */\r
+ return SWITCH_STATUS_SUCCESS;\r
+}\r
+\r
+\r
+\r
+\r
+\r
--- /dev/null
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+ ProjectType="Visual C++"\r
+ Version="8.00"\r
+ Name="mod_codec_g729"\r
+ ProjectGUID="{1D95CD95-0DE2-48C3-AC23-D5C7D1C9C0F0}"\r
+ RootNamespace="mod_codec_g729"\r
+ Keyword="Win32Proj"\r
+ >\r
+ <Platforms>\r
+ <Platform\r
+ Name="Win32"\r
+ />\r
+ </Platforms>\r
+ <ToolFiles>\r
+ </ToolFiles>\r
+ <Configurations>\r
+ <Configuration\r
+ Name="Debug|Win32"\r
+ OutputDirectory="Debug"\r
+ IntermediateDirectory="Debug"\r
+ ConfigurationType="2"\r
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"\r
+ CharacterSet="2"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ CommandLine="cscript /nologo $(InputDir)..\..\..\w32\vsnet\getlibs.vbs Mod_CodecG729 Debug"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""$(InputDir)..\..\include";"$(InputDir)..\..\..\libs\include";"$(InputDir)..\..\..\libs\codec\libg729""\r
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;MOD_EXPORTS"\r
+ MinimalRebuild="true"\r
+ BasicRuntimeChecks="3"\r
+ RuntimeLibrary="1"\r
+ UsePrecompiledHeader="0"\r
+ WarningLevel="3"\r
+ Detect64BitPortabilityProblems="true"\r
+ DebugInformationFormat="4"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ AdditionalDependencies="libg729.lib FreeSwitchCore.lib"\r
+ OutputFile="..\..\..\w32\vsnet\$(OutDir)/mod/mod_codec_g729.dll"\r
+ LinkIncremental="2"\r
+ AdditionalLibraryDirectories=""$(InputDir)..\..\..\libs\apr\Debug";"$(InputDir)..\..\..\libs\codec\libg729\Debug";..\..\..\w32\vsnet\Debug"\r
+ GenerateDebugInformation="true"\r
+ ProgramDatabaseFile="$(OutDir)/mod_codec_g729.pdb"\r
+ SubSystem="2"\r
+ ImportLibrary="$(OutDir)/mod_codec_g729.lib"\r
+ TargetMachine="1"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebDeploymentTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ <Configuration\r
+ Name="Release|Win32"\r
+ OutputDirectory="Release"\r
+ IntermediateDirectory="Release"\r
+ ConfigurationType="2"\r
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"\r
+ CharacterSet="2"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ CommandLine="cscript /nologo $(InputDir)..\..\..\w32\vsnet\getlibs.vbs Mod_CodecG729 Release"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""$(InputDir)..\..\include";"$(InputDir)..\..\..\libs\include";"$(InputDir)..\..\..\libs\codec\libg729""\r
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;MOD_EXPORTS"\r
+ RuntimeLibrary="0"\r
+ UsePrecompiledHeader="0"\r
+ WarningLevel="3"\r
+ Detect64BitPortabilityProblems="true"\r
+ DebugInformationFormat="3"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ AdditionalDependencies="libg729.lib FreeSwitchCore.lib"\r
+ OutputFile="..\..\..\w32\vsnet\$(OutDir)/mod/mod_codec_g729.dll"\r
+ LinkIncremental="1"\r
+ AdditionalLibraryDirectories=""$(InputDir)..\..\..\libs\apr\Release";"$(InputDir)..\..\..\libs\codec\libg729\Release";..\..\..\w32\vsnet\Release"\r
+ GenerateDebugInformation="true"\r
+ SubSystem="2"\r
+ OptimizeReferences="2"\r
+ EnableCOMDATFolding="2"\r
+ ImportLibrary="$(OutDir)/mod_codec_g729.lib"\r
+ TargetMachine="1"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebDeploymentTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ </Configurations>\r
+ <References>\r
+ </References>\r
+ <Files>\r
+ <Filter\r
+ Name="Source Files"\r
+ Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"\r
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"\r
+ >\r
+ <File\r
+ RelativePath="mod_codec_g729.c"\r
+ >\r
+ </File>\r
+ </Filter>\r
+ <Filter\r
+ Name="Header Files"\r
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"\r
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"\r
+ >\r
+ </Filter>\r
+ <Filter\r
+ Name="Resource Files"\r
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"\r
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"\r
+ >\r
+ </Filter>\r
+ </Files>\r
+ <Globals>\r
+ </Globals>\r
+</VisualStudioProject>\r
--- /dev/null
+
+all: $(MOD).so
+
+g711.o: g711.c g711.h
+ $(CC) -c -O2 -pthread -DLINUX=2 -D_REENTRANT -D_GNU_SOURCE -D_LARGEFILE64_SOURCE g711.c -o g711.o
+
+$(MOD).so: $(MOD).c g711.o
+ $(CC) $(CFLAGS) -fPIC -c $(MOD).c -o $(MOD).o
+ $(CC) $(SOLINK) g711.o $(MOD).o -o $(MOD).so $(LDFLAGS)
+
+
+
+clean:
+ rm -fr *.so *.o *~
+
--- /dev/null
+/*
+ * This source code is a product of Sun Microsystems, Inc. and is provided
+ * for unrestricted use. Users may copy or modify this source code without
+ * charge.
+ *
+ * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
+ * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun source code is provided with no support and without any obligation on
+ * the part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+/*
+ * December 30, 1994:
+ * Functions linear2alaw, linear2ulaw have been updated to correctly
+ * convert unquantized 16 bit values.
+ * Tables for direct u- to A-law and A- to u-law conversions have been
+ * corrected.
+ * Borge Lindberg, Center for PersonKommunikation, Aalborg University.
+ * bli@cpk.auc.dk
+ *
+ */
+/*
+ * Downloaded from comp.speech site in Cambridge.
+ *
+ */
+
+#include "g711.h"
+
+/*
+ * g711.c
+ *
+ * u-law, A-law and linear PCM conversions.
+ */
+#define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */
+#define QUANT_MASK (0xf) /* Quantization field mask. */
+#define NSEGS (8) /* Number of A-law segments. */
+#define SEG_SHIFT (4) /* Left shift for segment number. */
+#define SEG_MASK (0x70) /* Segment field mask. */
+
+static short seg_aend[8] = {0x1F, 0x3F, 0x7F, 0xFF,
+ 0x1FF, 0x3FF, 0x7FF, 0xFFF};
+static short seg_uend[8] = {0x3F, 0x7F, 0xFF, 0x1FF,
+ 0x3FF, 0x7FF, 0xFFF, 0x1FFF};
+
+/* copy from CCITT G.711 specifications */
+unsigned char _u2a[128] = { /* u- to A-law conversions */
+ 1, 1, 2, 2, 3, 3, 4, 4,
+ 5, 5, 6, 6, 7, 7, 8, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 27, 29, 31, 33, 34, 35, 36,
+ 37, 38, 39, 40, 41, 42, 43, 44,
+ 46, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, 62,
+ 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 77, 78, 79,
+/* corrected:
+ 81, 82, 83, 84, 85, 86, 87, 88,
+ should be: */
+ 80, 82, 83, 84, 85, 86, 87, 88,
+ 89, 90, 91, 92, 93, 94, 95, 96,
+ 97, 98, 99, 100, 101, 102, 103, 104,
+ 105, 106, 107, 108, 109, 110, 111, 112,
+ 113, 114, 115, 116, 117, 118, 119, 120,
+ 121, 122, 123, 124, 125, 126, 127, 128};
+
+unsigned char _a2u[128] = { /* A- to u-law conversions */
+ 1, 3, 5, 7, 9, 11, 13, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 32, 33, 33, 34, 34, 35, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, 46, 47, 48, 48, 49, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 62, 63, 64, 64,
+ 65, 66, 67, 68, 69, 70, 71, 72,
+/* corrected:
+ 73, 74, 75, 76, 77, 78, 79, 79,
+ should be: */
+ 73, 74, 75, 76, 77, 78, 79, 80,
+ 80, 81, 82, 83, 84, 85, 86, 87,
+ 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127};
+
+static short search(
+ short val,
+ short *table,
+ short size)
+{
+ short i;
+
+ for (i = 0; i < size; i++) {
+ if (val <= *table++)
+ return (i);
+ }
+ return (size);
+}
+
+/*
+ * linear2alaw() - Convert a 16-bit linear PCM value to 8-bit A-law
+ *
+ * linear2alaw() accepts an 16-bit integer and encodes it as A-law data.
+ *
+ * Linear Input Code Compressed Code
+ * ------------------------ ---------------
+ * 0000000wxyza 000wxyz
+ * 0000001wxyza 001wxyz
+ * 000001wxyzab 010wxyz
+ * 00001wxyzabc 011wxyz
+ * 0001wxyzabcd 100wxyz
+ * 001wxyzabcde 101wxyz
+ * 01wxyzabcdef 110wxyz
+ * 1wxyzabcdefg 111wxyz
+ *
+ * For further information see John C. Bellamy's Digital Telephony, 1982,
+ * John Wiley & Sons, pps 98-111 and 472-476.
+ */
+unsigned char
+linear2alaw(short pcm_val) /* 2's complement (16-bit range) */
+{
+ short mask;
+ short seg;
+ unsigned char aval;
+
+ pcm_val = pcm_val >> 3;
+
+ if (pcm_val >= 0) {
+ mask = 0xD5; /* sign (7th) bit = 1 */
+ } else {
+ mask = 0x55; /* sign bit = 0 */
+ pcm_val = -pcm_val - 1;
+ }
+
+ /* Convert the scaled magnitude to segment number. */
+ seg = search(pcm_val, seg_aend, 8);
+
+ /* Combine the sign, segment, and quantization bits. */
+
+ if (seg >= 8) /* out of range, return maximum value. */
+ return (unsigned char) (0x7F ^ mask);
+ else {
+ aval = (unsigned char) seg << SEG_SHIFT;
+ if (seg < 2)
+ aval |= (pcm_val >> 1) & QUANT_MASK;
+ else
+ aval |= (pcm_val >> seg) & QUANT_MASK;
+ return (aval ^ mask);
+ }
+}
+
+/*
+ * alaw2linear() - Convert an A-law value to 16-bit linear PCM
+ *
+ */
+short
+alaw2linear(
+ unsigned char a_val)
+{
+ short t;
+ short seg;
+
+ a_val ^= 0x55;
+
+ t = (a_val & QUANT_MASK) << 4;
+ seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT;
+ switch (seg) {
+ case 0:
+ t += 8;
+ break;
+ case 1:
+ t += 0x108;
+ break;
+ default:
+ t += 0x108;
+ t <<= seg - 1;
+ }
+ return ((a_val & SIGN_BIT) ? t : -t);
+}
+
+#define BIAS (0x84) /* Bias for linear code. */
+#define CLIP 8159
+
+/*
+* linear2ulaw() - Convert a linear PCM value to u-law
+*
+* In order to simplify the encoding process, the original linear magnitude
+* is biased by adding 33 which shifts the encoding range from (0 - 8158) to
+* (33 - 8191). The result can be seen in the following encoding table:
+*
+* Biased Linear Input Code Compressed Code
+* ------------------------ ---------------
+* 00000001wxyza 000wxyz
+* 0000001wxyzab 001wxyz
+* 000001wxyzabc 010wxyz
+* 00001wxyzabcd 011wxyz
+* 0001wxyzabcde 100wxyz
+* 001wxyzabcdef 101wxyz
+* 01wxyzabcdefg 110wxyz
+* 1wxyzabcdefgh 111wxyz
+*
+* Each biased linear code has a leading 1 which identifies the segment
+* number. The value of the segment number is equal to 7 minus the number
+* of leading 0's. The quantization interval is directly available as the
+* four bits wxyz. * The trailing bits (a - h) are ignored.
+*
+* Ordinarily the complement of the resulting code word is used for
+* transmission, and so the code word is complemented before it is returned.
+*
+* For further information see John C. Bellamy's Digital Telephony, 1982,
+* John Wiley & Sons, pps 98-111 and 472-476.
+*/
+unsigned char
+linear2ulaw(
+ short pcm_val) /* 2's complement (16-bit range) */
+{
+ short mask;
+ short seg;
+ unsigned char uval;
+
+ /* Get the sign and the magnitude of the value. */
+ pcm_val = pcm_val >> 2;
+ if (pcm_val < 0) {
+ pcm_val = -pcm_val;
+ mask = 0x7F;
+ } else {
+ mask = 0xFF;
+ }
+ if ( pcm_val > CLIP ) pcm_val = CLIP; /* clip the magnitude */
+ pcm_val += (BIAS >> 2);
+
+ /* Convert the scaled magnitude to segment number. */
+ seg = search(pcm_val, seg_uend, 8);
+
+ /*
+ * Combine the sign, segment, quantization bits;
+ * and complement the code word.
+ */
+ if (seg >= 8) /* out of range, return maximum value. */
+ return (unsigned char) (0x7F ^ mask);
+ else {
+ uval = (unsigned char) (seg << 4) | ((pcm_val >> (seg + 1)) & 0xF);
+ return (uval ^ mask);
+ }
+
+}
+
+/*
+ * ulaw2linear() - Convert a u-law value to 16-bit linear PCM
+ *
+ * First, a biased linear code is derived from the code word. An unbiased
+ * output can then be obtained by subtracting 33 from the biased code.
+ *
+ * Note that this function expects to be passed the complement of the
+ * original code word. This is in keeping with ISDN conventions.
+ */
+short
+ulaw2linear(
+ unsigned char u_val)
+{
+ short t;
+
+ /* Complement to obtain normal u-law value. */
+ u_val = ~u_val;
+
+ /*
+ * Extract and bias the quantization bits. Then
+ * shift up by the segment number and subtract out the bias.
+ */
+ t = ((u_val & QUANT_MASK) << 3) + BIAS;
+ t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT;
+
+ return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
+}
+
+/* A-law to u-law conversion */
+unsigned char
+alaw2ulaw(
+ unsigned char aval)
+{
+ aval &= 0xff;
+ return (unsigned char) ((aval & 0x80) ? (0xFF ^ _a2u[aval ^ 0xD5]) :
+ (0x7F ^ _a2u[aval ^ 0x55]));
+}
+
+/* u-law to A-law conversion */
+unsigned char
+ulaw2alaw(
+ unsigned char uval)
+{
+ uval &= 0xff;
+ return (unsigned char) ((uval & 0x80) ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) :
+ (0x55 ^ (_u2a[0x7F ^ uval] - 1)));
+}
+
+/* ---------- end of g711.c ----------------------------------------------------- */
--- /dev/null
+/*
+ * This source code is a product of Sun Microsystems, Inc. and is provided
+ * for unrestricted use. Users may copy or modify this source code without
+ * charge.
+ *
+ * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
+ * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun source code is provided with no support and without any obligation on
+ * the part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+/*
+ * December 30, 1994:
+ * Functions linear2alaw, linear2ulaw have been updated to correctly
+ * convert unquantized 16 bit values.
+ * Tables for direct u- to A-law and A- to u-law conversions have been
+ * corrected.
+ * Borge Lindberg, Center for PersonKommunikation, Aalborg University.
+ * bli@cpk.auc.dk
+ *
+ */
+/*
+ * Downloaded from comp.speech site in Cambridge.
+ *
+ */
+
+#ifndef _G711_H_
+#define _G711_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+unsigned char linear2alaw(short pcm_val);
+short alaw2linear(unsigned char a_val);
+unsigned char linear2ulaw(short pcm_val);
+short ulaw2linear(unsigned char u_val);
+unsigned char alaw2ulaw(unsigned char aval);
+unsigned char ulaw2alaw(unsigned char uval);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _G711_H_ */
--- /dev/null
+/*
+ * 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_g711codec.c -- G711 Ulaw/Alaw Codec Module
+ *
+ */
+#include <switch.h>
+#include "g711.h"
+
+
+static const char modname[] = "mod_g711codec";
+
+
+static switch_status switch_g711u_init(switch_codec *codec, switch_codec_flag flags, const struct switch_codec_settings *codec_settings)
+{
+ int encoding, decoding;
+
+ encoding = (flags & SWITCH_CODEC_FLAG_ENCODE);
+ decoding = (flags & SWITCH_CODEC_FLAG_DECODE);
+
+ if (!(encoding || decoding)) {
+ return SWITCH_STATUS_FALSE;
+ } else {
+ return SWITCH_STATUS_SUCCESS;
+ }
+}
+
+
+static switch_status switch_g711u_encode(switch_codec *codec,
+ switch_codec *other_codec,
+ void *decoded_data,
+ size_t decoded_data_len,
+ int decoded_rate,
+ void *encoded_data,
+ size_t *encoded_data_len,
+ int *encoded_rate,
+ unsigned int *flag)
+{
+ short *dbuf;
+ unsigned char *ebuf;
+ size_t i;
+
+ dbuf = decoded_data;
+ ebuf = encoded_data;
+
+ for (i = 0; i < decoded_data_len / sizeof(short); i++) {
+ ebuf[i] = linear2ulaw(dbuf[i]);
+ }
+
+ *encoded_data_len = i;
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status switch_g711u_decode(switch_codec *codec,
+ switch_codec *other_codec,
+ void *encoded_data,
+ size_t encoded_data_len,
+ int encoded_rate,
+ void *decoded_data,
+ size_t *decoded_data_len,
+ int *decoded_rate,
+ unsigned int *flag)
+{
+ short *dbuf;
+ unsigned char *ebuf;
+ size_t i;
+
+ dbuf = decoded_data;
+ ebuf = encoded_data;
+
+ if (*flag & SWITCH_CODEC_FLAG_SILENCE) {
+ memset(dbuf, 0, codec->implementation->bytes_per_frame);
+ *decoded_data_len = codec->implementation->bytes_per_frame;
+ } else {
+ for (i = 0; i < encoded_data_len; i++) {
+ dbuf[i] = ulaw2linear(ebuf[i]);
+ }
+
+ *decoded_data_len = i * 2;
+ }
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status switch_g711u_destroy(switch_codec *codec)
+{
+ return SWITCH_STATUS_SUCCESS;
+}
+
+
+static switch_status switch_g711a_init(switch_codec *codec, switch_codec_flag flags, const struct switch_codec_settings *codec_settings)
+{
+ int encoding, decoding;
+
+ encoding = (flags & SWITCH_CODEC_FLAG_ENCODE);
+ decoding = (flags & SWITCH_CODEC_FLAG_DECODE);
+
+ if (!(encoding || decoding)) {
+ return SWITCH_STATUS_FALSE;
+ } else {
+ return SWITCH_STATUS_SUCCESS;
+ }
+}
+
+
+static switch_status switch_g711a_encode(switch_codec *codec,
+ switch_codec *other_codec,
+ void *decoded_data,
+ size_t decoded_data_len,
+ int decoded_rate,
+ void *encoded_data,
+ size_t *encoded_data_len,
+ int *encoded_rate,
+ unsigned int *flag)
+{
+ short *dbuf;
+ unsigned char *ebuf;
+ size_t i;
+
+ dbuf = decoded_data;
+ ebuf = encoded_data;
+
+ for (i = 0; i < decoded_data_len / sizeof(short); i++) {
+ ebuf[i] = linear2alaw(dbuf[i]);
+ }
+
+ *encoded_data_len = i;
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status switch_g711a_decode(switch_codec *codec,
+ switch_codec *other_codec,
+ void *encoded_data,
+ size_t encoded_data_len,
+ int encoded_rate,
+ void *decoded_data,
+ size_t *decoded_data_len,
+ int *decoded_rate,
+ unsigned int *flag)
+{
+ short *dbuf;
+ unsigned char *ebuf;
+ size_t i;
+
+ dbuf = decoded_data;
+ ebuf = encoded_data;
+
+ if (*flag & SWITCH_CODEC_FLAG_SILENCE) {
+ memset(dbuf, 0, codec->implementation->bytes_per_frame);
+ *decoded_data_len = codec->implementation->bytes_per_frame;
+ } else {
+ for (i = 0; i < encoded_data_len; i++) {
+ dbuf[i] = alaw2linear(ebuf[i]);
+ }
+
+ *decoded_data_len = i * 2;
+ }
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status switch_g711a_destroy(switch_codec *codec)
+{
+ return SWITCH_STATUS_SUCCESS;
+}
+
+/* Registration */
+
+
+static const switch_codec_implementation g711u_8k_60ms_implementation = {
+ /*.samples_per_second*/ 8000,
+ /*.bits_per_second*/ 19200,
+ /*.microseconds_per_frame*/ 60000,
+ /*.samples_per_frame*/ 480,
+ /*.bytes_per_frame*/ 960,
+ /*.encoded_bytes_per_frame*/ 480,
+ /*.number_of_channels*/ 1,
+ /*.pref_frames_per_packet*/ 1,
+ /*.max_frames_per_packet*/ 1,
+ /*.init*/ switch_g711u_init,
+ /*.encode*/ switch_g711u_encode,
+ /*.decode*/ switch_g711u_decode,
+ /*.destroy*/ switch_g711u_destroy
+};
+
+static const switch_codec_implementation g711u_8k_30ms_implementation = {
+ /*.samples_per_second*/ 8000,
+ /*.bits_per_second*/ 96000,
+ /*.microseconds_per_frame*/ 30000,
+ /*.samples_per_frame*/ 240,
+ /*.bytes_per_frame*/ 480,
+ /*.encoded_bytes_per_frame*/ 240,
+ /*.number_of_channels*/ 1,
+ /*.pref_frames_per_packet*/ 1,
+ /*.max_frames_per_packet*/ 1,
+ /*.init*/ switch_g711u_init,
+ /*.encode*/ switch_g711u_encode,
+ /*.decode*/ switch_g711u_decode,
+ /*.destroy*/ switch_g711u_destroy,
+ /*.next*/ &g711u_8k_60ms_implementation
+};
+
+static const switch_codec_implementation g711u_8k_implementation = {
+ /*.samples_per_second*/ 8000,
+ /*.bits_per_second*/ 64000,
+ /*.microseconds_per_frame*/ 20000,
+ /*.samples_per_frame*/ 160,
+ /*.bytes_per_frame*/ 320,
+ /*.encoded_bytes_per_frame*/ 160,
+ /*.number_of_channels*/ 1,
+ /*.pref_frames_per_packet*/ 1,
+ /*.max_frames_per_packet*/ 1,
+ /*.init*/ switch_g711u_init,
+ /*.encode*/ switch_g711u_encode,
+ /*.decode*/ switch_g711u_decode,
+ /*.destroy*/ switch_g711u_destroy,
+ ///*.next*/ &g711u_8k_30ms_implementation
+};
+
+
+static const switch_codec_implementation g711a_8k_implementation = {
+ /*.samples_per_second*/ 8000,
+ /*.bits_per_second*/ 64000,
+ /*.microseconds_per_frame*/ 20000,
+ /*.samples_per_frame*/ 160,
+ /*.bytes_per_frame*/ 320,
+ /*.encoded_bytes_per_frame*/ 160,
+ /*.number_of_channels*/ 1,
+ /*.pref_frames_per_packet*/ 1,
+ /*.max_frames_per_packet*/ 1,
+ /*.init*/ switch_g711a_init,
+ /*.encode*/ switch_g711a_encode,
+ /*.decode*/ switch_g711a_decode,
+ /*.destroy*/ switch_g711a_destroy
+};
+
+
+static const switch_codec_interface g711a_codec_interface = {
+ /*.interface_name*/ "g711 alaw",
+ /*.codec_type*/ SWITCH_CODEC_TYPE_AUDIO,
+ /*.ianacode*/ 8,
+ /*.iananame*/ "PCMA",
+ /*.implementations*/ &g711a_8k_implementation
+};
+
+static const switch_codec_interface g711u_codec_interface = {
+ /*.interface_name*/ "g711 ulaw",
+ /*.codec_type*/ SWITCH_CODEC_TYPE_AUDIO,
+ /*.ianacode*/ 0,
+ /*.iananame*/ "PCMU",
+ /*.implementations*/ &g711u_8k_implementation,
+ /*.next*/ &g711a_codec_interface
+};
+
+static switch_loadable_module_interface g711_module_interface = {
+ /*.module_name*/ modname,
+ /*.endpoint_interface*/ NULL,
+ /*.timer_interface*/ NULL,
+ /*.dialplan_interface*/ NULL,
+ /*.codec_interface*/ &g711u_codec_interface,
+ /*.application_interface*/ NULL
+};
+
+
+SWITCH_MOD_DECLARE(switch_status) switch_module_load(const switch_loadable_module_interface **interface, char *filename) {
+ /* connect my internal structure to the blank pointer passed to me */
+ *interface = &g711_module_interface;
+
+ /* indicate that the module should continue to be loaded */
+ return SWITCH_STATUS_SUCCESS;
+}
+
+
+
+
+
--- /dev/null
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+ ProjectType="Visual C++"\r
+ Version="8.00"\r
+ Name="mod_g711codec"\r
+ ProjectGUID="{B1FE4613-3F4B-4DAF-9714-2472BF8F56AE}"\r
+ RootNamespace="mod_g711codec"\r
+ Keyword="Win32Proj"\r
+ >\r
+ <Platforms>\r
+ <Platform\r
+ Name="Win32"\r
+ />\r
+ </Platforms>\r
+ <ToolFiles>\r
+ </ToolFiles>\r
+ <Configurations>\r
+ <Configuration\r
+ Name="Debug|Win32"\r
+ OutputDirectory="Debug"\r
+ IntermediateDirectory="Debug"\r
+ ConfigurationType="2"\r
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"\r
+ CharacterSet="2"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""$(InputDir)..\..\include";"$(InputDir)include";"$(InputDir)..\..\..\libs\include""\r
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;MOD_EXPORTS"\r
+ MinimalRebuild="true"\r
+ BasicRuntimeChecks="3"\r
+ RuntimeLibrary="1"\r
+ UsePrecompiledHeader="0"\r
+ WarningLevel="3"\r
+ Detect64BitPortabilityProblems="true"\r
+ DebugInformationFormat="4"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ OutputFile="..\..\..\w32\vsnet\$(OutDir)/mod/mod_g711codec.dll"\r
+ LinkIncremental="2"\r
+ AdditionalLibraryDirectories="$(InputDir)..\..\libs\apr\Debug"\r
+ GenerateDebugInformation="true"\r
+ ProgramDatabaseFile="$(OutDir)/mod_g711codec.pdb"\r
+ SubSystem="2"\r
+ ImportLibrary="$(OutDir)/mod_g711codec.lib"\r
+ TargetMachine="1"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebDeploymentTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ <Configuration\r
+ Name="Release|Win32"\r
+ OutputDirectory="Release"\r
+ IntermediateDirectory="Release"\r
+ ConfigurationType="2"\r
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"\r
+ CharacterSet="2"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""$(InputDir)..\..\include";"$(InputDir)include";"$(InputDir)..\..\..\libs\include""\r
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;MOD_EXPORTS"\r
+ RuntimeLibrary="0"\r
+ UsePrecompiledHeader="0"\r
+ WarningLevel="3"\r
+ Detect64BitPortabilityProblems="true"\r
+ DebugInformationFormat="3"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ OutputFile="..\..\..\w32\vsnet\$(OutDir)/mod/mod_g711codec.dll"\r
+ LinkIncremental="1"\r
+ AdditionalLibraryDirectories=""$(InputDir)..\..\libs\apr\Release""\r
+ GenerateDebugInformation="true"\r
+ SubSystem="2"\r
+ OptimizeReferences="2"\r
+ EnableCOMDATFolding="2"\r
+ ImportLibrary="$(OutDir)/mod_g711codec.lib"\r
+ TargetMachine="1"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebDeploymentTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ </Configurations>\r
+ <References>\r
+ </References>\r
+ <Files>\r
+ <Filter\r
+ Name="Source Files"\r
+ Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"\r
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"\r
+ >\r
+ <File\r
+ RelativePath=".\g711.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath=".\mod_g711codec.c"\r
+ >\r
+ </File>\r
+ </Filter>\r
+ <Filter\r
+ Name="Header Files"\r
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"\r
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"\r
+ >\r
+ <File\r
+ RelativePath=".\g711.h"\r
+ >\r
+ </File>\r
+ </Filter>\r
+ <Filter\r
+ Name="Resource Files"\r
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"\r
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"\r
+ >\r
+ </Filter>\r
+ </Files>\r
+ <Globals>\r
+ </Globals>\r
+</VisualStudioProject>\r
--- /dev/null
+/*
+ * 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_rawaudio.c -- Raw Signed Linear Codec
+ *
+ */
+#include <switch.h>
+#include <libresample.h>
+
+static const char modname[] = "mod_rawaudio";
+
+
+static switch_status switch_raw_init(switch_codec *codec, switch_codec_flag flags, const struct switch_codec_settings *codec_settings)
+{
+ int encoding, decoding;
+ struct raw_context *context = NULL;
+
+ encoding = (flags & SWITCH_CODEC_FLAG_ENCODE);
+ decoding = (flags & SWITCH_CODEC_FLAG_DECODE);
+
+ if (!(encoding || decoding)) {
+ return SWITCH_STATUS_FALSE;
+ } else {
+ return SWITCH_STATUS_SUCCESS;
+ }
+}
+
+static switch_status switch_raw_encode(switch_codec *codec,
+ switch_codec *other_codec,
+ void *decoded_data,
+ size_t decoded_data_len,
+ int decoded_rate,
+ void *encoded_data,
+ size_t *encoded_data_len,
+ int *encoded_rate,
+ unsigned int *flag)
+{
+
+ /* NOOP indicates that the audio in is already the same as the audio out, so no conversion was necessary.*/
+ if (decoded_rate != codec->implementation->samples_per_second) {
+ memcpy(encoded_data, decoded_data, decoded_data_len);
+ *encoded_data_len = decoded_data_len;
+ return SWITCH_STATUS_RESAMPLE;
+ }
+ return SWITCH_STATUS_NOOP;
+}
+
+static switch_status switch_raw_decode(switch_codec *codec,
+ switch_codec *other_codec,
+ void *encoded_data,
+ size_t encoded_data_len,
+ int encoded_rate,
+ void *decoded_data,
+ size_t *decoded_data_len,
+ int *decoded_rate,
+ unsigned int *flag)
+{
+ if (encoded_rate != other_codec->implementation->samples_per_second) {
+ memcpy(decoded_data, encoded_data, encoded_data_len);
+ *decoded_data_len = encoded_data_len;
+ return SWITCH_STATUS_RESAMPLE;
+ }
+ return SWITCH_STATUS_NOOP;
+}
+
+
+static switch_status switch_raw_destroy(switch_codec *codec)
+{
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static const switch_codec_implementation raw_32k_implementation = {
+ /*.samples_per_second = */ 32000,
+ /*.bits_per_second = */ 512000,
+ /*.microseconds_per_frame = */ 20000,
+ /*.samples_per_frame = */ 640,
+ /*.bytes_per_frame = */ 1280,
+ /*.encoded_bytes_per_frame = */ 1280,
+ /*.number_of_channels = */ 1,
+ /*.pref_frames_per_packet = */ 1,
+ /*.max_frames_per_packet = */ 1,
+ /*.init = */ switch_raw_init,
+ /*.encode = */ switch_raw_encode,
+ /*.decode = */ switch_raw_decode,
+ /*.destroy = */ switch_raw_destroy
+};
+
+static const switch_codec_implementation raw_22k_implementation = {
+ /*.samples_per_second = */ 22050,
+ /*.bits_per_second = */ 352800,
+ /*.microseconds_per_frame = */ 20000,
+ /*.samples_per_frame = */ 441,
+ /*.bytes_per_frame = */ 882,
+ /*.encoded_bytes_per_frame = */ 882,
+ /*.number_of_channels = */ 1,
+ /*.pref_frames_per_packet = */ 1,
+ /*.max_frames_per_packet = */ 1,
+ /*.init = */ switch_raw_init,
+ /*.encode = */ switch_raw_encode,
+ /*.decode = */ switch_raw_decode,
+ /*.destroy = */ switch_raw_destroy,
+ /*.next = */ &raw_32k_implementation
+};
+
+static const switch_codec_implementation raw_16k_implementation = {
+ /*.samples_per_second = */ 16000,
+ /*.bits_per_second = */ 256000,
+ /*.microseconds_per_frame = */ 20000,
+ /*.samples_per_frame = */ 320,
+ /*.bytes_per_frame = */ 640,
+ /*.encoded_bytes_per_frame = */ 640,
+ /*.number_of_channels = */ 1,
+ /*.pref_frames_per_packet = */ 1,
+ /*.max_frames_per_packet = */ 1,
+ /*.init = */ switch_raw_init,
+ /*.encode = */ switch_raw_encode,
+ /*.decode = */ switch_raw_decode,
+ /*.destroy = */ switch_raw_destroy,
+ /*.next = */ &raw_22k_implementation
+};
+
+static const switch_codec_implementation raw_8k_implementation = {
+ /*.samples_per_second = */ 8000,
+ /*.bits_per_second = */ 128000,
+ /*.microseconds_per_frame = */ 20000,
+ /*.samples_per_frame = */ 160,
+ /*.bytes_per_frame = */ 320,
+ /*.encoded_bytes_per_frame = */ 320,
+ /*.number_of_channels = */ 1,
+ /*.pref_frames_per_packet = */ 1,
+ /*.max_frames_per_packet = */ 1,
+ /*.init = */ switch_raw_init,
+ /*.encode = */ switch_raw_encode,
+ /*.decode = */ switch_raw_decode,
+ /*.destroy = */ switch_raw_destroy,
+ /*.next = */ &raw_16k_implementation
+};
+
+
+static const switch_codec_implementation raw_8k_30ms_implementation = {
+ /*.samples_per_second*/ 8000,
+ /*.bits_per_second*/ 128000,
+ /*.microseconds_per_frame*/ 30000,
+ /*.samples_per_frame*/ 240,
+ /*.bytes_per_frame*/ 480,
+ /*.encoded_bytes_per_frame*/ 480,
+ /*.number_of_channels*/ 1,
+ /*.pref_frames_per_packet*/ 1,
+ /*.max_frames_per_packet*/ 1,
+ /*.init*/ switch_raw_init,
+ /*.encode*/ switch_raw_encode,
+ /*.decode*/ switch_raw_decode,
+ /*.destroy*/ switch_raw_destroy,
+ /*.next*/ &raw_8k_implementation
+};
+
+
+static const switch_codec_interface raw_codec_interface = {
+ /*.interface_name*/ "raw signed linear (16 bit)",
+ /*.codec_type*/ SWITCH_CODEC_TYPE_AUDIO,
+ /*.ianacode*/ 10,
+ /*.iananame*/ "L16",
+ /*.implementations*/ &raw_8k_30ms_implementation
+};
+
+static switch_loadable_module_interface raw_module_interface = {
+ /*.module_name*/ modname,
+ /*.endpoint_interface*/ NULL,
+ /*.timer_interface*/ NULL,
+ /*.dialplan_interface*/ NULL,
+ /*.codec_interface*/ &raw_codec_interface,
+ /*.application_interface*/ NULL,
+ /*.api_interface*/ NULL,
+ ///*.file_interface*/ &raw_file_interface
+};
+
+
+SWITCH_MOD_DECLARE(switch_status) switch_module_load(const switch_loadable_module_interface **interface, char *filename) {
+ /* connect my internal structure to the blank pointer passed to me */
+ *interface = &raw_module_interface;
+
+ /* indicate that the module should continue to be loaded */
+ return SWITCH_STATUS_SUCCESS;
+}
+
+
+
+
+
--- /dev/null
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+ ProjectType="Visual C++"\r
+ Version="8.00"\r
+ Name="mod_rawaudio"\r
+ ProjectGUID="{5844AFE1-AA3E-4BDB-A9EF-119AEF19DF88}"\r
+ RootNamespace="mod_rawaudio"\r
+ Keyword="Win32Proj"\r
+ >\r
+ <Platforms>\r
+ <Platform\r
+ Name="Win32"\r
+ />\r
+ </Platforms>\r
+ <ToolFiles>\r
+ </ToolFiles>\r
+ <Configurations>\r
+ <Configuration\r
+ Name="Debug|Win32"\r
+ OutputDirectory="Debug"\r
+ IntermediateDirectory="Debug"\r
+ ConfigurationType="2"\r
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"\r
+ CharacterSet="2"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ CommandLine="cscript /nologo $(InputDir)..\..\..\w32\vsnet\getlibs.vbs Mod_rawaudio Debug"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""$(InputDir)..\..\include";"$(InputDir)include";"$(InputDir)..\..\..\libs\include";"$(InputDir)..\..\..\libs\libresample\include""\r
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;MOD_EXPORTS"\r
+ MinimalRebuild="true"\r
+ BasicRuntimeChecks="3"\r
+ RuntimeLibrary="1"\r
+ UsePrecompiledHeader="0"\r
+ WarningLevel="3"\r
+ Detect64BitPortabilityProblems="true"\r
+ DebugInformationFormat="4"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ AdditionalDependencies="libresampled.lib"\r
+ OutputFile="..\..\..\w32\vsnet\$(OutDir)/mod/mod_rawaudio.dll"\r
+ LinkIncremental="2"\r
+ AdditionalLibraryDirectories=""$(InputDir)..\..\..\libs\libresample\win""\r
+ GenerateDebugInformation="true"\r
+ ProgramDatabaseFile="$(OutDir)/mod_rawaudio.pdb"\r
+ SubSystem="2"\r
+ ImportLibrary="$(OutDir)/mod_rawaudio.lib"\r
+ TargetMachine="1"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebDeploymentTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ <Configuration\r
+ Name="Release|Win32"\r
+ OutputDirectory="Release"\r
+ IntermediateDirectory="Release"\r
+ ConfigurationType="2"\r
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"\r
+ CharacterSet="2"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ CommandLine="cscript /nologo $(InputDir)..\..\..\w32\vsnet\getlibs.vbs Mod_rawaudio Release"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""$(InputDir)..\..\include";"$(InputDir)include";"$(InputDir)..\..\..\libs\include";"$(InputDir)..\..\..\libs\libresample\include""\r
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;MOD_EXPORTS"\r
+ RuntimeLibrary="0"\r
+ UsePrecompiledHeader="0"\r
+ WarningLevel="3"\r
+ Detect64BitPortabilityProblems="true"\r
+ DebugInformationFormat="3"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ AdditionalDependencies="libresample.lib"\r
+ OutputFile="..\..\..\w32\vsnet\$(OutDir)/mod/mod_rawaudio.dll"\r
+ LinkIncremental="1"\r
+ AdditionalLibraryDirectories=""$(InputDir)..\..\..\libs\libresample\win""\r
+ GenerateDebugInformation="true"\r
+ SubSystem="2"\r
+ OptimizeReferences="2"\r
+ EnableCOMDATFolding="2"\r
+ ImportLibrary="$(OutDir)/mod_rawaudio.lib"\r
+ TargetMachine="1"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebDeploymentTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ </Configurations>\r
+ <References>\r
+ </References>\r
+ <Files>\r
+ <Filter\r
+ Name="Source Files"\r
+ Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"\r
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"\r
+ >\r
+ <File\r
+ RelativePath=".\mod_rawaudio.c"\r
+ >\r
+ </File>\r
+ </Filter>\r
+ <Filter\r
+ Name="Header Files"\r
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"\r
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"\r
+ >\r
+ </Filter>\r
+ <Filter\r
+ Name="Resource Files"\r
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"\r
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"\r
+ >\r
+ </Filter>\r
+ </Files>\r
+ <Globals>\r
+ </Globals>\r
+</VisualStudioProject>\r
--- /dev/null
+all: depends $(MOD).so
+
+depends:
+ $(BASE)/buildlib.sh $(BASE) install speex-1.1.11.1.tar.gz --prefix=$(PREFIX)
+
+$(MOD).so: $(MOD).c
+ $(CC) $(CFLAGS) -fPIC -c $(MOD).c -o $(MOD).o
+ $(CC) $(SOLINK) $(MOD).o -o $(MOD).so $(LDFLAGS) -lspeex
+
+clean:
+ rm -fr *.so *.o *~
+
--- /dev/null
+/*
+ * 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_speexcodec.c -- Speex Codec Module
+ *
+ */
+#include <switch.h>
+#include <speex/speex.h>
+#include <speex/speex_preprocess.h>
+
+static const char modname[] = "mod_speexcodec";
+
+const struct switch_codec_settings default_codec_settings = {
+ /*.quality*/ 5,
+ /*.complexity*/ 5,
+ /*.enhancement*/ 1,
+ /*.vad*/ 0,
+ /*.vbr*/ 0,
+ /*.vbr_quality*/ 4,
+ /*.abr*/ 0,
+ /*.dtx*/ 0,
+ /*.preproc*/ 0,
+ /*.pp_vad*/ 0,
+ /*.pp_agc*/ 0,
+ /*.pp_agc_level*/ 8000,
+ /*.pp_denoise*/ 0,
+ /*.pp_dereverb*/ 0,
+ /*.pp_dereverb_decay*/ 0.4f,
+ /*.pp_dereverb_level*/ 0.3f,
+};
+
+struct speex_context {
+ switch_codec *codec;
+ unsigned int flags;
+
+ /* Encoder */
+ void *encoder_state;
+ struct SpeexBits encoder_bits;
+ unsigned int encoder_frame_size;
+ int encoder_mode;
+ SpeexPreprocessState *pp;
+
+ /* Decoder */
+ void *decoder_state;
+ struct SpeexBits decoder_bits;
+ unsigned int decoder_frame_size;
+ int decoder_mode;
+};
+
+static switch_status switch_speex_init(switch_codec *codec, switch_codec_flag flags, const struct switch_codec_settings *codec_settings)
+{
+ struct speex_context *context = NULL;
+ int encoding, decoding;
+
+ encoding = (flags & SWITCH_CODEC_FLAG_ENCODE);
+ decoding = (flags & SWITCH_CODEC_FLAG_DECODE);
+
+ if (!codec_settings) {
+ codec_settings = &default_codec_settings;
+ }
+
+ memcpy(&codec->codec_settings, codec_settings, sizeof(codec->codec_settings));
+
+ if (!(encoding || decoding) || (!(context = switch_core_alloc(codec->memory_pool, sizeof(*context))))) {
+ return SWITCH_STATUS_FALSE;
+ } else {
+ const SpeexMode *mode = NULL;
+
+ context->codec = codec;
+ if (codec->implementation->samples_per_second == 8000) {
+ mode = &speex_nb_mode;
+ } else if (codec->implementation->samples_per_second == 16000) {
+ mode = &speex_wb_mode;
+ } else if (codec->implementation->samples_per_second == 32000) {
+ mode = &speex_uwb_mode;
+ }
+
+ if (!mode) {
+ return SWITCH_STATUS_FALSE;
+ }
+
+ if (encoding) {
+ speex_bits_init(&context->encoder_bits);
+ context->encoder_state = speex_encoder_init(mode);
+ speex_encoder_ctl(context->encoder_state, SPEEX_GET_FRAME_SIZE, &context->encoder_frame_size);
+ speex_encoder_ctl(context->encoder_state, SPEEX_SET_COMPLEXITY, &codec->codec_settings.complexity);
+ if (codec->codec_settings.preproc) {
+ context->pp = speex_preprocess_state_init(context->encoder_frame_size, codec->implementation->samples_per_second);
+ speex_preprocess_ctl(context->pp, SPEEX_PREPROCESS_SET_VAD, &codec->codec_settings.pp_vad);
+ speex_preprocess_ctl(context->pp, SPEEX_PREPROCESS_SET_AGC, &codec->codec_settings.pp_agc);
+ speex_preprocess_ctl(context->pp, SPEEX_PREPROCESS_SET_AGC_LEVEL, &codec->codec_settings.pp_agc_level);
+ speex_preprocess_ctl(context->pp, SPEEX_PREPROCESS_SET_DENOISE, &codec->codec_settings.pp_denoise);
+ speex_preprocess_ctl(context->pp, SPEEX_PREPROCESS_SET_DEREVERB, &codec->codec_settings.pp_dereverb);
+ speex_preprocess_ctl(context->pp, SPEEX_PREPROCESS_SET_DEREVERB_DECAY, &codec->codec_settings.pp_dereverb_decay);
+ speex_preprocess_ctl(context->pp, SPEEX_PREPROCESS_SET_DEREVERB_LEVEL, &codec->codec_settings.pp_dereverb_level);
+ }
+
+ if (!codec->codec_settings.abr && !codec->codec_settings.vbr) {
+ speex_encoder_ctl(context->encoder_state, SPEEX_SET_QUALITY, &codec->codec_settings.quality);
+ if (codec->codec_settings.vad) {
+ speex_encoder_ctl(context->encoder_state, SPEEX_SET_VAD, &codec->codec_settings.vad);
+ }
+ }
+ if (codec->codec_settings.vbr) {
+ speex_encoder_ctl(context->encoder_state, SPEEX_SET_VBR, &codec->codec_settings.vbr);
+ speex_encoder_ctl(context->encoder_state, SPEEX_SET_VBR_QUALITY, &codec->codec_settings.vbr_quality);
+ }
+ if (codec->codec_settings.abr) {
+ speex_encoder_ctl(context->encoder_state, SPEEX_SET_ABR, &codec->codec_settings.abr);
+ }
+ if (codec->codec_settings.dtx) {
+ speex_encoder_ctl(context->encoder_state, SPEEX_SET_DTX, &codec->codec_settings.dtx);
+ }
+ }
+
+ if (decoding) {
+ speex_bits_init(&context->decoder_bits);
+ context->decoder_state = speex_decoder_init(mode);
+ if (codec->codec_settings.enhancement) {
+ speex_decoder_ctl(context->decoder_state, SPEEX_SET_ENH, &codec->codec_settings.enhancement);
+ }
+ }
+
+
+
+ codec->private = context;
+ return SWITCH_STATUS_SUCCESS;
+ }
+}
+
+static switch_status switch_speex_encode(switch_codec *codec,
+ switch_codec *other_codec,
+ void *decoded_data,
+ size_t decoded_data_len,
+ int decoded_rate,
+ void *encoded_data,
+ size_t *encoded_data_len,
+ int *encoded_rate,
+ unsigned int *flag)
+{
+ struct speex_context *context = codec->private;
+ short *buf;
+ int is_speech = 1;
+
+ if (!context) {
+ return SWITCH_STATUS_FALSE;
+ }
+
+ buf = decoded_data;
+
+ if (context->pp) {
+ is_speech = speex_preprocess(context->pp, buf, NULL);
+ }
+
+ if (is_speech) {
+ is_speech = speex_encode_int(context->encoder_state, buf, &context->encoder_bits) || ! context->codec->codec_settings.dtx;
+ } else {
+ speex_bits_pack(&context->encoder_bits, 0, 5);
+ }
+
+
+ if (is_speech) {
+ switch_clear_flag(context, SWITCH_CODEC_FLAG_SILENCE);
+ *flag |= SWITCH_CODEC_FLAG_SILENCE_STOP;
+ } else {
+ if (switch_test_flag(context, SWITCH_CODEC_FLAG_SILENCE)) {
+ *encoded_data_len = 0;
+ *flag |= SWITCH_CODEC_FLAG_SILENCE;
+ return SWITCH_STATUS_SUCCESS;
+ }
+
+ switch_set_flag(context, SWITCH_CODEC_FLAG_SILENCE);
+ *flag |= SWITCH_CODEC_FLAG_SILENCE_START;
+ }
+
+
+
+
+ speex_bits_pack(&context->encoder_bits, 15, 5);
+ *encoded_data_len = speex_bits_write(&context->encoder_bits, (char *)encoded_data, context->encoder_frame_size);
+ speex_bits_reset(&context->encoder_bits);
+
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status switch_speex_decode(switch_codec *codec,
+ switch_codec *other_codec,
+ void *encoded_data,
+ size_t encoded_data_len,
+ int encoded_rate,
+ void *decoded_data,
+ size_t *decoded_data_len,
+ int *decoded_rate,
+ unsigned int *flag)
+{
+ struct speex_context *context = codec->private;
+ short *buf;
+
+ if (!context) {
+ return SWITCH_STATUS_FALSE;
+ }
+
+ buf = decoded_data;
+ if (*flag & SWITCH_CODEC_FLAG_SILENCE) {
+ speex_decode_int(context->decoder_state, NULL, buf);
+ } else {
+ speex_bits_read_from(&context->decoder_bits, (char *)encoded_data, (int)*decoded_data_len);
+ speex_decode_int(context->decoder_state, &context->decoder_bits, buf);
+ }
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+
+static switch_status switch_speex_destroy(switch_codec *codec)
+{
+ int encoding, decoding;
+ struct speex_context *context = codec->private;
+
+ if (!context) {
+ return SWITCH_STATUS_FALSE;
+ }
+
+ encoding = (codec->flags & SWITCH_CODEC_FLAG_ENCODE);
+ decoding = (codec->flags & SWITCH_CODEC_FLAG_DECODE);
+
+ if (encoding) {
+ speex_bits_destroy(&context->encoder_bits);
+ speex_encoder_destroy(context->encoder_state);
+ }
+
+ if (decoding) {
+ speex_bits_destroy(&context->decoder_bits);
+ speex_decoder_destroy(context->decoder_state);
+ }
+
+ codec->private = NULL;
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+/* Registration */
+static const switch_codec_implementation speex_32k_implementation = {
+ /*.samples_per_second*/ 32000,
+ /*.bits_per_second*/ 512000,
+ /*.nanoseconds_per_frame*/ 20000,
+ /*.samples_per_frame*/ 640,
+ /*.bytes_per_frame*/ 1280,
+ /*.encoded_bytes_per_frame*/ 1280,
+ /*.number_of_channels*/ 1,
+ /*.pref_frames_per_packet*/ 1,
+ /*.max_frames_per_packet*/ 1,
+ /*.init*/ switch_speex_init,
+ /*.encode*/ switch_speex_encode,
+ /*.decode*/ switch_speex_decode,
+ /*.destroy*/ switch_speex_destroy
+};
+
+static const switch_codec_implementation speex_16k_implementation = {
+ /*.samples_per_second*/ 16000,
+ /*.bits_per_second*/ 256000,
+ /*.nanoseconds_per_frame*/ 20000,
+ /*.samples_per_frame*/ 320,
+ /*.bytes_per_frame*/ 640,
+ /*.encoded_bytes_per_frame*/ 640,
+ /*.number_of_channels*/ 1,
+ /*.pref_frames_per_packet*/ 1,
+ /*.max_frames_per_packet*/ 1,
+ /*.init*/ switch_speex_init,
+ /*.encode*/ switch_speex_encode,
+ /*.decode*/ switch_speex_decode,
+ /*.destroy*/ switch_speex_destroy,
+ /*.next*/ &speex_32k_implementation
+};
+
+static const switch_codec_implementation speex_8k_implementation = {
+ /*.samples_per_second*/ 8000,
+ /*.bits_per_second*/ 128000,
+ /*.nanoseconds_per_frame*/ 20000,
+ /*.samples_per_frame*/ 160,
+ /*.bytes_per_frame*/ 320,
+ /*.encoded_bytes_per_frame*/ 320,
+ /*.number_of_channels*/ 1,
+ /*.pref_frames_per_packet*/ 1,
+ /*.max_frames_per_packet*/ 1,
+ /*.init*/ switch_speex_init,
+ /*.encode*/ switch_speex_encode,
+ /*.decode*/ switch_speex_decode,
+ /*.destroy*/ switch_speex_destroy,
+ /*.next*/ &speex_16k_implementation
+};
+
+static const switch_codec_interface speex_codec_interface = {
+ /*.interface_name*/ "speex",
+ /*.codec_type*/ SWITCH_CODEC_TYPE_AUDIO,
+ /*.ianacode*/ 98,
+ /*.iananame*/ "speex",
+ /*.implementations*/ &speex_8k_implementation
+};
+
+static switch_loadable_module_interface speex_module_interface = {
+ /*.module_name*/ modname,
+ /*.endpoint_interface*/ NULL,
+ /*.timer_interface*/ NULL,
+ /*.dialplan_interface*/ NULL,
+ /*.codec_interface*/ &speex_codec_interface,
+ /*.application_interface*/ NULL
+};
+
+SWITCH_MOD_DECLARE(switch_status) switch_module_load(switch_loadable_module_interface **interface, char *filename) {
+ /* connect my internal structure to the blank pointer passed to me */
+ *interface = &speex_module_interface;
+
+ /* indicate that the module should continue to be loaded */
+ return SWITCH_STATUS_SUCCESS;
+}
--- /dev/null
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+ ProjectType="Visual C++"\r
+ Version="8.00"\r
+ Name="mod_speexcodec"\r
+ ProjectGUID="{5580D60E-0F77-4716-9CD4-B8E5986FA375}"\r
+ RootNamespace="mod_speexcodec"\r
+ Keyword="Win32Proj"\r
+ >\r
+ <Platforms>\r
+ <Platform\r
+ Name="Win32"\r
+ />\r
+ </Platforms>\r
+ <ToolFiles>\r
+ </ToolFiles>\r
+ <Configurations>\r
+ <Configuration\r
+ Name="Debug|Win32"\r
+ OutputDirectory="Debug"\r
+ IntermediateDirectory="Debug"\r
+ ConfigurationType="2"\r
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"\r
+ CharacterSet="2"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ CommandLine="cscript /nologo $(InputDir)..\..\..\w32\vsnet\getlibs.vbs Mod_SpeexCodec Debug"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""$(InputDir)..\..\include";"$(InputDir)include";"$(InputDir)..\..\..\libs\include";"$(InputDir)..\..\..\libs\speex\include";"$(InputDir)..\..\..\libs\speex\include\speex""\r
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;MOD_EXPORTS"\r
+ MinimalRebuild="true"\r
+ BasicRuntimeChecks="3"\r
+ RuntimeLibrary="1"\r
+ UsePrecompiledHeader="0"\r
+ WarningLevel="3"\r
+ Detect64BitPortabilityProblems="true"\r
+ DebugInformationFormat="4"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ AdditionalDependencies="libspeex.lib"\r
+ OutputFile="..\..\..\w32\vsnet\$(OutDir)/mod/mod_speexcodec.dll"\r
+ LinkIncremental="2"\r
+ AdditionalLibraryDirectories=""$(InputDir)..\..\..\libs\speex\win32\libspeex\Debug""\r
+ GenerateDebugInformation="true"\r
+ ProgramDatabaseFile="$(OutDir)/mod_speexcodec.pdb"\r
+ SubSystem="2"\r
+ ImportLibrary="$(OutDir)/mod_speexcodec.lib"\r
+ TargetMachine="1"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebDeploymentTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ <Configuration\r
+ Name="Release|Win32"\r
+ OutputDirectory="Release"\r
+ IntermediateDirectory="Release"\r
+ ConfigurationType="2"\r
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"\r
+ CharacterSet="2"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ CommandLine="cscript /nologo $(InputDir)..\..\..\w32\vsnet\getlibs.vbs Mod_SpeexCodec Release"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""$(InputDir)..\..\include";"$(InputDir)include";"$(InputDir)..\..\..\libs\include";"$(InputDir)..\..\..\libs\speex\include";"$(InputDir)..\..\..\libs\speex\include\speex""\r
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;MOD_EXPORTS"\r
+ RuntimeLibrary="0"\r
+ UsePrecompiledHeader="0"\r
+ WarningLevel="3"\r
+ Detect64BitPortabilityProblems="true"\r
+ DebugInformationFormat="3"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ AdditionalDependencies="libspeex.lib"\r
+ OutputFile="..\..\..\w32\vsnet\$(OutDir)/mod/mod_speexcodec.dll"\r
+ LinkIncremental="1"\r
+ AdditionalLibraryDirectories=""$(InputDir)..\..\..\libs\speex\win32\libspeex\Release""\r
+ GenerateDebugInformation="true"\r
+ SubSystem="2"\r
+ OptimizeReferences="2"\r
+ EnableCOMDATFolding="2"\r
+ ImportLibrary="$(OutDir)/mod_speexcodec.lib"\r
+ TargetMachine="1"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebDeploymentTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ </Configurations>\r
+ <References>\r
+ </References>\r
+ <Files>\r
+ <Filter\r
+ Name="Source Files"\r
+ Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"\r
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"\r
+ >\r
+ <File\r
+ RelativePath=".\mod_speexcodec.c"\r
+ >\r
+ </File>\r
+ </Filter>\r
+ <Filter\r
+ Name="Header Files"\r
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"\r
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"\r
+ >\r
+ </Filter>\r
+ <Filter\r
+ Name="Resource Files"\r
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"\r
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"\r
+ >\r
+ </Filter>\r
+ </Files>\r
+ <Globals>\r
+ </Globals>\r
+</VisualStudioProject>\r
--- /dev/null
+/*
+ * 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_dialplan_demo.c -- Example Dialplan Module
+ *
+ */
+#include <switch.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+
+static const char modname[] = "mod_dialplan_demo";
+
+
+switch_caller_extension *demo_dialplan_hunt(switch_core_session *session)
+{
+ switch_caller_profile *caller_profile;
+ switch_caller_extension *extension = NULL;
+ switch_channel *channel;
+ char *cf = "extensions.conf";
+ switch_config cfg;
+ char *var, *val;
+ char app[1024];
+
+ channel = switch_core_session_get_channel(session);
+ caller_profile = switch_channel_get_caller_profile(channel);
+ //switch_channel_set_variable(channel, "pleasework", "yay");
+
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Hello %s You Dialed %s!\n", caller_profile->caller_id_name, caller_profile->destination_number);
+
+ if (!switch_config_open_file(&cfg, cf)) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "open of %s failed\n", cf);
+ switch_channel_hangup(channel);
+ return NULL;
+ }
+
+ while (switch_config_next_pair(&cfg, &var, &val)) {
+ if (!strcasecmp(cfg.category, "extensions")) {
+ if (!strcmp(var, caller_profile->destination_number) && val) {
+ char *data;
+
+ memset(app, 0, sizeof(app));
+ strncpy(app, val, sizeof(app));
+
+ if ((data = strchr(app, ' '))) {
+ *data = '\0';
+ data++;
+ } else {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "invalid extension on line %d\n", cfg.lineno);
+ continue;
+ }
+ if (!extension) {
+ if (!(extension = switch_caller_extension_new(session, caller_profile->destination_number, caller_profile->destination_number))) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "memory error!\n");
+ break;
+ }
+ }
+
+ switch_caller_extension_add_application(session, extension, app, data);
+ }
+ }
+ }
+
+ switch_config_close_file(&cfg);
+
+ if (extension) {
+ switch_channel_set_state(channel, CS_EXECUTE);
+ } else {
+ switch_channel_hangup(channel);
+ }
+
+ return extension;
+}
+
+
+static const switch_dialplan_interface demo_dialplan_interface = {
+ /*.interface_name =*/ "demo",
+ /*.hunt_function = */ demo_dialplan_hunt
+ /*.next = NULL */
+};
+
+static const switch_loadable_module_interface demo_dialplan_module_interface = {
+ /*.module_name = */ modname,
+ /*.endpoint_interface = */ NULL,
+ /*.timer_interface = */ NULL,
+ /*.dialplan_interface = */ &demo_dialplan_interface,
+ /*.codec_interface = */ NULL,
+ /*.application_interface =*/ NULL
+};
+
+SWITCH_MOD_DECLARE(switch_status) switch_module_load(const switch_loadable_module_interface **interface, char *filename) {
+
+ /* connect my internal structure to the blank pointer passed to me */
+ *interface = &demo_dialplan_module_interface;
+
+ /* indicate that the module should continue to be loaded */
+ return SWITCH_STATUS_SUCCESS;
+}
--- /dev/null
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+ ProjectType="Visual C++"\r
+ Version="8.00"\r
+ Name="mod_dialplan_demo"\r
+ ProjectGUID="{2988EB83-785F-45D4-8731-8E1E4345177E}"\r
+ RootNamespace="mod_dialplan_demo"\r
+ Keyword="Win32Proj"\r
+ >\r
+ <Platforms>\r
+ <Platform\r
+ Name="Win32"\r
+ />\r
+ </Platforms>\r
+ <ToolFiles>\r
+ </ToolFiles>\r
+ <Configurations>\r
+ <Configuration\r
+ Name="Debug|Win32"\r
+ OutputDirectory="Debug"\r
+ IntermediateDirectory="Debug"\r
+ ConfigurationType="2"\r
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"\r
+ CharacterSet="2"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""$(InputDir)..\..\include";"$(InputDir)include";"$(InputDir)..\..\..\libs\include""\r
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;MOD_EXPORTS"\r
+ MinimalRebuild="true"\r
+ BasicRuntimeChecks="3"\r
+ RuntimeLibrary="1"\r
+ UsePrecompiledHeader="0"\r
+ WarningLevel="3"\r
+ Detect64BitPortabilityProblems="true"\r
+ DebugInformationFormat="4"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ OutputFile="..\..\..\w32\vsnet\$(OutDir)/mod/mod_dialplan_demo.dll"\r
+ LinkIncremental="2"\r
+ AdditionalLibraryDirectories="$(InputDir)..\..\libs\apr\Debug"\r
+ GenerateDebugInformation="true"\r
+ ProgramDatabaseFile="$(OutDir)/mod_dialplan_demo.pdb"\r
+ SubSystem="2"\r
+ ImportLibrary="$(OutDir)/mod_dialplan_demo.lib"\r
+ TargetMachine="1"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebDeploymentTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ <Configuration\r
+ Name="Release|Win32"\r
+ OutputDirectory="Release"\r
+ IntermediateDirectory="Release"\r
+ ConfigurationType="2"\r
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"\r
+ CharacterSet="2"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""$(InputDir)..\..\include";"$(InputDir)include";"$(InputDir)..\..\..\libs\include""\r
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;MOD_EXPORTS"\r
+ RuntimeLibrary="0"\r
+ UsePrecompiledHeader="0"\r
+ WarningLevel="3"\r
+ Detect64BitPortabilityProblems="true"\r
+ DebugInformationFormat="3"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ OutputFile="..\..\..\w32\vsnet\$(OutDir)/mod/mod_dialplan_demo.dll"\r
+ LinkIncremental="1"\r
+ AdditionalLibraryDirectories=""$(InputDir)..\..\libs\apr\Release""\r
+ GenerateDebugInformation="true"\r
+ SubSystem="2"\r
+ OptimizeReferences="2"\r
+ EnableCOMDATFolding="2"\r
+ ImportLibrary="$(OutDir)/mod_dialplan_demo.lib"\r
+ TargetMachine="1"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebDeploymentTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ </Configurations>\r
+ <References>\r
+ </References>\r
+ <Files>\r
+ <Filter\r
+ Name="Source Files"\r
+ Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"\r
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"\r
+ >\r
+ <File\r
+ RelativePath=".\mod_dialplan_demo.c"\r
+ >\r
+ </File>\r
+ </Filter>\r
+ <Filter\r
+ Name="Header Files"\r
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"\r
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"\r
+ >\r
+ </Filter>\r
+ <Filter\r
+ Name="Resource Files"\r
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"\r
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"\r
+ >\r
+ </Filter>\r
+ </Files>\r
+ <Globals>\r
+ </Globals>\r
+</VisualStudioProject>\r
--- /dev/null
+#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 *~
+
--- /dev/null
+/*
+ * 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_exosip.c -- eXoSIP SIP Endpoint
+ *
+ */
+
+#define HAVE_APR
+#include <switch.h>
+#include <jrtp4c.h>
+#include <eXosip2/eXosip.h>
+#include <osip2/osip_mt.h>
+#include <osipparser2/osip_rfc3264.h>
+#include <osipparser2/osip_port.h>
+
+
+static const char modname[] = "mod_exosip";
+#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_IO = (1 << 0),
+ TFLAG_INBOUND = (1 << 1),
+ TFLAG_OUTBOUND = (1 << 2),
+ TFLAG_DTMF = (1 << 3),
+ TFLAG_READING = (1 << 4),
+ TFLAG_WRITING = (1 << 5),
+ TFLAG_USING_CODEC = (1 << 6),
+ TFLAG_RTP = (1 << 7),
+ TFLAG_BYE = (1 << 8)
+} TFLAGS;
+
+
+#define PACKET_LEN 160
+#define DEFAULT_BYTES_PER_FRAME 160
+
+
+static const switch_endpoint_interface exosip_endpoint_interface;
+
+static struct {
+ int debug;
+ int bytes_per_frame;
+ char *dialplan;
+ int port;
+ int rtp_start;
+ int rtp_end;
+ switch_hash *call_hash;
+ switch_mutex_t *port_lock;
+ int running;
+ int codec_ms;
+} globals;
+
+struct private_object {
+ unsigned int flags;
+ switch_core_session *session;
+ switch_frame read_frame;
+ switch_codec read_codec;
+ switch_codec write_codec;
+ unsigned char read_buf[1024];
+ switch_caller_profile *caller_profile;
+ int cid;
+ int did;
+ int tid;
+ int32_t timestamp_send;
+ int32_t timestamp_recv;
+ int payload_num;
+ struct jrtp4c *rtp_session;
+ struct osip_rfc3264 *sdp_config;
+ sdp_message_t *remote_sdp;
+ sdp_message_t *local_sdp;
+ char remote_sdp_audio_ip[50];
+ int remote_sdp_audio_port;
+ char local_sdp_audio_ip[50];
+ int local_sdp_audio_port;
+ char call_id[50];
+ int ssrc;
+ switch_mutex_t *rtp_lock;
+};
+
+
+static int next_rtp_port(void)
+{
+ int port;
+
+ switch_mutex_lock(globals.port_lock);
+ port = globals.rtp_start;
+ globals.rtp_start += 2;
+ if (port >= globals.rtp_end) {
+ port = globals.rtp_start;
+ }
+ switch_mutex_unlock(globals.port_lock);
+ return port;
+}
+
+
+static void set_global_dialplan(char *dialplan)
+{
+ if (globals.dialplan) {
+ free(globals.dialplan);
+ globals.dialplan = NULL;
+ }
+
+ globals.dialplan = strdup(dialplan);
+}
+
+static void set_global_dialplan(char *dialplan);
+static switch_status exosip_on_init(switch_core_session *session);
+static switch_status exosip_on_hangup(switch_core_session *session);
+static switch_status exosip_on_loopback(switch_core_session *session);
+static switch_status exosip_on_transmit(switch_core_session *session);
+static switch_status exosip_outgoing_channel(switch_core_session *session, switch_caller_profile *outbound_profile, switch_core_session **new_session);
+static switch_status exosip_read_frame(switch_core_session *session, switch_frame **frame, int timeout, switch_io_flag flags);
+static switch_status exosip_write_frame(switch_core_session *session, switch_frame *frame, int timeout, switch_io_flag flags);
+static int config_exosip(int reload);
+static switch_status parse_sdp_media(sdp_media_t *media, char **dname, char **drate, char **dpayload);
+static switch_status exosip_kill_channel(switch_core_session *session, int sig);
+static void activate_rtp(struct private_object *tech_pvt);
+static void deactivate_rtp(struct private_object *tech_pvt);
+
+static struct private_object *get_pvt_by_call_id(int id)
+{
+ char name[50];
+ snprintf(name, sizeof(name), "%d", id);
+ return (struct private_object *) switch_core_hash_find(globals.call_hash, name);
+}
+
+static switch_status exosip_on_execute(switch_core_session *session)
+{
+ return SWITCH_STATUS_SUCCESS;
+}
+
+
+static int sdp_add_codec(struct osip_rfc3264 *cnf, int codec_type, int payload, char *attribute, int rate, int index)
+{
+ char tmp[4] = "", string[32] = "";
+ sdp_media_t *med = NULL;
+ sdp_attribute_t *attr = NULL;
+
+ sdp_media_init(&med);
+ if (med == NULL)
+ return -1;
+
+ if (!index) {
+ snprintf(tmp, sizeof(tmp), "%i", payload);
+ med->m_proto = osip_strdup("RTP/AVP");
+ osip_list_add(med->m_payloads, osip_strdup(tmp), -1);
+ }
+ if (attribute) {
+ sdp_attribute_init(&attr);
+ attr->a_att_field = osip_strdup("rtpmap");
+ snprintf(string, sizeof(string), "%i %s/%i", payload, attribute, rate);
+ attr->a_att_value = osip_strdup(string);
+ osip_list_add(med->a_attributes, attr, -1);
+ }
+
+ switch (codec_type) {
+case SWITCH_CODEC_TYPE_AUDIO:
+ med->m_media = osip_strdup("audio");
+ osip_rfc3264_add_audio_media(cnf, med, -1);
+ break;
+case SWITCH_CODEC_TYPE_VIDEO:
+ med->m_media = osip_strdup("video");
+ osip_rfc3264_add_video_media(cnf, med, -1);
+ break;
+default:
+ break;
+ }
+ return 0;
+}
+
+
+/*
+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 exosip_on_init(switch_core_session *session)
+{
+ struct private_object *tech_pvt;
+ switch_channel *channel = NULL;
+ char from_uri[512] = "", localip[128] = "", port[7] = "", *buf = NULL, tmp[512] = "";
+ osip_message_t *invite = 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->read_frame.data = tech_pvt->read_buf;
+ tech_pvt->read_frame.buflen = sizeof(tech_pvt->read_buf);
+
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "EXOSIP INIT\n");
+
+ if (switch_test_flag(tech_pvt, TFLAG_OUTBOUND)) {
+ char *dest_uri;
+ switch_codec_interface *codecs[SWITCH_MAX_CODECS];
+ int num_codecs = 0;
+ /* do SIP Goodies...*/
+
+ /* Generate callerid URI */
+ eXosip_guess_localip(AF_INET, localip, 128);
+ snprintf(from_uri, sizeof(from_uri), "<sip:%s@%s>", tech_pvt->caller_profile->caller_id_number, localip);
+ /* Setup codec negotiation stuffs */
+ osip_rfc3264_init(&tech_pvt->sdp_config);
+ /* Decide on local IP and rtp port */
+ strncpy(tech_pvt->local_sdp_audio_ip, localip, sizeof(tech_pvt->local_sdp_audio_ip));
+ tech_pvt->local_sdp_audio_port = next_rtp_port();
+ /* Initialize SDP */
+ sdp_message_init(&tech_pvt->local_sdp);
+ sdp_message_v_version_set(tech_pvt->local_sdp, "0");
+ sdp_message_o_origin_set(tech_pvt->local_sdp, "OpenSWITCH2", "0", "0", "IN", "IP4", tech_pvt->local_sdp_audio_ip);
+ sdp_message_s_name_set(tech_pvt->local_sdp, "SIP Call");
+ sdp_message_c_connection_add(tech_pvt->local_sdp, -1, "IN", "IP4", tech_pvt->local_sdp_audio_ip, NULL, NULL);
+ sdp_message_t_time_descr_add(tech_pvt->local_sdp, "0", "0");
+ snprintf(port, sizeof(port), "%i", tech_pvt->local_sdp_audio_port);
+ sdp_message_m_media_add(tech_pvt->local_sdp, "audio", port, NULL, "RTP/AVP");
+ /* Add in every codec we support on this outbound call */
+ if ((num_codecs = loadable_module_get_codecs(switch_core_session_get_pool(session), codecs, sizeof(codecs)/sizeof(codecs[0]))) > 0) {
+ int i;
+ static const switch_codec_implementation *imp;
+ for (i = 0; i < num_codecs; i++) {
+ int x = 0;
+
+ snprintf(tmp, sizeof(tmp), "%i", codecs[i]->ianacode);
+ sdp_message_m_payload_add(tech_pvt->local_sdp, 0, osip_strdup(tmp));
+ for (imp = codecs[i]->implementations ; imp ; imp = imp->next) {
+ /* Add to SDP config */
+ sdp_add_codec(tech_pvt->sdp_config, codecs[i]->codec_type, codecs[i]->ianacode, codecs[i]->iananame, imp->samples_per_second, x++);
+ /* Add to SDP message */
+
+ snprintf(tmp, sizeof(tmp), "%i %s/%i", codecs[i]->ianacode, codecs[i]->iananame, imp->samples_per_second);
+ sdp_message_a_attribute_add(tech_pvt->local_sdp, 0, "rtpmap", osip_strdup(tmp));
+ memset(tmp, 0, sizeof(tmp));
+ }
+ }
+ }
+ /* Setup our INVITE */
+ eXosip_lock();
+ if (!(dest_uri = (char *) switch_core_session_alloc(session, strlen(tech_pvt->caller_profile->destination_number) + 10))) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "AIEEEE!\n");
+ assert(dest_uri != NULL);
+ }
+ sprintf(dest_uri, "sip:%s", tech_pvt->caller_profile->destination_number);
+ eXosip_call_build_initial_invite(&invite, dest_uri, from_uri, NULL, NULL);
+ osip_message_set_supported(invite, "100rel, replaces");
+ /* Add SDP to the INVITE */
+ sdp_message_to_str(tech_pvt->local_sdp, &buf);
+ osip_message_set_body(invite, buf, strlen(buf));
+ osip_message_set_content_type(invite, "application/sdp");
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "OUTBOUND SDP:\n%s\n", buf);
+ free(buf);
+ /* Send the INVITE */
+ tech_pvt->cid = eXosip_call_send_initial_invite(invite);
+ snprintf(tech_pvt->call_id, sizeof(tech_pvt->call_id), "%d", tech_pvt->cid);
+ switch_core_hash_insert(globals.call_hash, tech_pvt->call_id, tech_pvt);
+ tech_pvt->did = -1;
+ eXosip_unlock();
+ }
+
+ /* Let Media Work */
+ 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 exosip_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, "EXOSIP RING\n");
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status exosip_on_hangup(switch_core_session *session)
+{
+ struct private_object *tech_pvt;
+ switch_channel *channel = NULL;
+ int i;
+
+ channel = switch_core_session_get_channel(session);
+ assert(channel != NULL);
+
+ tech_pvt = switch_core_session_get_private(session);
+ assert(tech_pvt != NULL);
+
+
+ switch_core_hash_delete(globals.call_hash, tech_pvt->call_id);
+
+
+ switch_set_flag(tech_pvt, TFLAG_BYE);
+ switch_clear_flag(tech_pvt, TFLAG_IO);
+
+ deactivate_rtp(tech_pvt);
+
+ i = eXosip_call_terminate(tech_pvt->cid, tech_pvt->did);
+
+ if (switch_test_flag(tech_pvt, TFLAG_USING_CODEC)) {
+ switch_core_codec_destroy(&tech_pvt->read_codec);
+ switch_core_codec_destroy(&tech_pvt->write_codec);
+ }
+
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "EXOSIP HANGUP %s %d/%d=%d\n", switch_channel_get_name(channel), tech_pvt->cid, tech_pvt->did, i);
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status exosip_on_loopback(switch_core_session *session)
+{
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "EXOSIP LOOPBACK\n");
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status exosip_on_transmit(switch_core_session *session)
+{
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "EXOSIP TRANSMIT\n");
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status exosip_outgoing_channel(switch_core_session *session, switch_caller_profile *outbound_profile, switch_core_session **new_session)
+{
+ if ((*new_session = switch_core_session_request(&exosip_endpoint_interface, NULL))) {
+ struct private_object *tech_pvt;
+ switch_channel *channel;
+
+ 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);
+ switch_mutex_init(&tech_pvt->rtp_lock, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(*new_session));
+ 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];
+ switch_caller_profile *caller_profile;
+
+ 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), "Exosip/%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;
+ }
+
+ 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 void deactivate_rtp(struct private_object *tech_pvt)
+{
+ int loops = 0;
+ if (tech_pvt->rtp_session) {
+ switch_mutex_lock(tech_pvt->rtp_lock);
+
+ while(loops < 10 && (switch_test_flag(tech_pvt, TFLAG_READING) || switch_test_flag(tech_pvt, TFLAG_WRITING))) {
+ switch_yield(10000);
+ loops++;
+ }
+
+ jrtp4c_destroy(&tech_pvt->rtp_session);
+ tech_pvt->rtp_session = NULL;
+ switch_mutex_unlock(tech_pvt->rtp_lock);
+ }
+}
+
+static void activate_rtp(struct private_object *tech_pvt)
+{
+ int bw, ms;
+ switch_channel *channel;
+ const char *err;
+
+ assert(tech_pvt != NULL);
+
+ channel = switch_core_session_get_channel(tech_pvt->session);
+ assert(channel != NULL);
+
+ if (tech_pvt->rtp_session) {
+ return;
+ }
+
+ switch_mutex_lock(tech_pvt->rtp_lock);
+
+ if (tech_pvt->rtp_session) {
+ switch_mutex_unlock(tech_pvt->rtp_lock);
+ return;
+ }
+
+ if (switch_test_flag(tech_pvt, TFLAG_USING_CODEC)) {
+ bw = tech_pvt->read_codec.implementation->bits_per_second;
+ ms = tech_pvt->read_codec.implementation->microseconds_per_frame;
+ } else {
+ switch_channel_get_raw_mode(channel, NULL, NULL, NULL, &ms, &bw);
+ bw *= 8;
+ }
+
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Activating RTP %s:%d->%s:%d codec: %d ms: %d\n",
+ tech_pvt->local_sdp_audio_ip,
+ tech_pvt->local_sdp_audio_port,
+ tech_pvt->remote_sdp_audio_ip,
+ tech_pvt->remote_sdp_audio_port,
+ tech_pvt->read_codec.codec_interface->ianacode,
+ ms
+ );
+
+
+
+
+ tech_pvt->rtp_session = jrtp4c_new(
+ tech_pvt->local_sdp_audio_ip,
+ tech_pvt->local_sdp_audio_port,
+ tech_pvt->remote_sdp_audio_ip,
+ tech_pvt->remote_sdp_audio_port,
+ tech_pvt->read_codec.codec_interface->ianacode,
+ tech_pvt->read_codec.implementation->samples_per_second,
+ &err);
+
+ if (tech_pvt->rtp_session) {
+ tech_pvt->ssrc = jrtp4c_get_ssrc(tech_pvt->rtp_session);
+ jrtp4c_start(tech_pvt->rtp_session);
+ switch_set_flag(tech_pvt, TFLAG_RTP);
+ } else {
+ switch_channel *channel = switch_core_session_get_channel(tech_pvt->session);
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Oh oh? [%s]\n", err);
+ switch_channel_hangup(channel);
+ switch_set_flag(tech_pvt, TFLAG_BYE);
+ switch_clear_flag(tech_pvt, TFLAG_IO);
+ }
+
+ switch_mutex_unlock(tech_pvt->rtp_lock);
+}
+
+static switch_status exosip_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);
+
+ if (!switch_channel_test_flag(channel, CF_OUTBOUND)) {
+ char *buf = NULL;
+ osip_message_t *answer = NULL;
+
+
+ /* Transmit 200 OK with SDP */
+ eXosip_lock();
+ eXosip_call_build_answer(tech_pvt->tid, 200, &answer);
+ sdp_message_to_str(tech_pvt->local_sdp, &buf);
+ osip_message_set_body(answer, buf, strlen(buf));
+ osip_message_set_content_type(answer, "application/sdp");
+ free(buf);
+ eXosip_call_send_answer(tech_pvt->tid, 200, answer);
+ eXosip_unlock();
+ }
+
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+
+static switch_status exosip_read_frame(switch_core_session *session, switch_frame **frame, int timeout, switch_io_flag flags)
+{
+ struct private_object *tech_pvt = NULL;
+ size_t bytes = 0, samples = 0, frames=0, ms=0;
+ 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->read_frame.datalen = 0;
+ switch_set_flag(tech_pvt, TFLAG_READING);
+
+ if (switch_test_flag(tech_pvt, TFLAG_USING_CODEC)) {
+ bytes = tech_pvt->read_codec.implementation->encoded_bytes_per_frame;
+ samples = tech_pvt->read_codec.implementation->samples_per_frame;
+ ms = tech_pvt->read_codec.implementation->microseconds_per_frame;
+ } else {
+ assert(0);
+ }
+
+ if (switch_test_flag(tech_pvt, TFLAG_IO)) {
+ if (!switch_test_flag(tech_pvt, TFLAG_RTP)) {
+ return SWITCH_STATUS_GENERR;
+ }
+
+
+ assert(tech_pvt->rtp_session != NULL);
+ tech_pvt->read_frame.datalen = 0;
+
+ while(!switch_test_flag(tech_pvt, TFLAG_BYE) && switch_test_flag(tech_pvt, TFLAG_IO) && tech_pvt->read_frame.datalen == 0) {
+ tech_pvt->read_frame.datalen = jrtp4c_read(tech_pvt->rtp_session,
+ tech_pvt->read_frame.data,
+ sizeof(tech_pvt->read_buf));
+
+ if (tech_pvt->read_frame.datalen > 0) {
+ bytes = tech_pvt->read_codec.implementation->encoded_bytes_per_frame;
+ frames = (tech_pvt->read_frame.datalen / bytes);
+ samples = frames * tech_pvt->read_codec.implementation->samples_per_frame;
+ ms = frames * tech_pvt->read_codec.implementation->microseconds_per_frame;
+ tech_pvt->timestamp_recv += (int32_t)samples;
+ tech_pvt->read_frame.samples = (int)samples;
+ break;
+ }
+
+ switch_yield(100);
+ }
+
+ //tech_pvt->timestamp_recv += samples;
+
+
+ //printf("%s %s->%s recv %d bytes %d samples in %d frames taking up %d ms ts=%d\n", switch_channel_get_name(channel), tech_pvt->local_sdp_audio_ip, tech_pvt->local_sdp_audio_ip, tech_pvt->read_frame.datalen, samples, frames, ms, tech_pvt->timestamp_recv);
+
+
+ //switch_mutex_unlock(tech_pvt->rtp_lock);
+
+ } else {
+ memset(tech_pvt->read_buf, 0, 160);
+ tech_pvt->read_frame.datalen = 160;
+ }
+
+ switch_clear_flag(tech_pvt, TFLAG_READING);
+
+ if (switch_test_flag(tech_pvt, TFLAG_BYE)) {
+ switch_channel_hangup(channel);
+ return SWITCH_STATUS_FALSE;
+ }
+
+ *frame = &tech_pvt->read_frame;
+
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+
+static switch_status exosip_write_frame(switch_core_session *session, switch_frame *frame, int timeout, switch_io_flag flags)
+{
+ struct private_object *tech_pvt;
+ switch_channel *channel = NULL;
+ switch_status status = SWITCH_STATUS_SUCCESS;
+ int bytes=0, samples=0, ms=0, frames=0;
+
+ 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_RTP)) {
+ return SWITCH_STATUS_GENERR;
+ }
+
+ if (!switch_test_flag(tech_pvt, TFLAG_IO)) {
+ return SWITCH_STATUS_SUCCESS;
+ }
+
+ if (switch_test_flag(tech_pvt, TFLAG_BYE)) {
+ switch_channel_hangup(channel);
+ return SWITCH_STATUS_FALSE;
+ }
+
+ switch_set_flag(tech_pvt, TFLAG_WRITING);
+ //switch_mutex_lock(tech_pvt->rtp_lock);
+
+ if (switch_test_flag(tech_pvt, TFLAG_USING_CODEC)) {
+ bytes = tech_pvt->read_codec.implementation->encoded_bytes_per_frame;
+ frames = ((int)frame->datalen / bytes);
+ samples = frames * tech_pvt->read_codec.implementation->samples_per_frame;
+ ms = frames * tech_pvt->read_codec.implementation->microseconds_per_frame / 1000;
+ } else {
+ assert(0);
+ }
+
+
+ //printf("%s %s->%s send %d bytes %d samples in %d frames taking up %d ms ts=%d\n", switch_channel_get_name(channel), tech_pvt->local_sdp_audio_ip, tech_pvt->remote_sdp_audio_ip, frame->datalen, samples, frames, ms, tech_pvt->timestamp_send);
+
+
+ jrtp4c_write(tech_pvt->rtp_session, frame->data, (int)frame->datalen, samples);
+ tech_pvt->timestamp_send += (int)samples;
+
+ switch_clear_flag(tech_pvt, TFLAG_WRITING);
+ //switch_mutex_unlock(tech_pvt->rtp_lock);
+ return status;
+}
+
+
+
+static switch_status exosip_kill_channel(switch_core_session *session, int sig)
+{
+ 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_clear_flag(tech_pvt, TFLAG_IO);
+ switch_set_flag(tech_pvt, TFLAG_BYE);
+
+ return SWITCH_STATUS_SUCCESS;
+
+}
+
+static switch_status exosip_waitfor_read(switch_core_session *session, int ms)
+{
+ 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 switch_status exosip_waitfor_write(switch_core_session *session, int ms)
+{
+ 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 const switch_io_routines exosip_io_routines = {
+ /*.outgoing_channel*/ exosip_outgoing_channel,
+ /*.answer_channel*/ exosip_answer_channel,
+ /*.read_frame*/ exosip_read_frame,
+ /*.write_frame*/ exosip_write_frame,
+ /*.kill_channel*/ exosip_kill_channel,
+ /*.waitfor_read*/ exosip_waitfor_read,
+ /*.waitfor_read*/ exosip_waitfor_write
+};
+
+static const switch_event_handler_table exosip_event_handlers = {
+ /*.on_init*/ exosip_on_init,
+ /*.on_ring*/ exosip_on_ring,
+ /*.on_execute*/ exosip_on_execute,
+ /*.on_hangup*/ exosip_on_hangup,
+ /*.on_loopback*/ exosip_on_loopback,
+ /*.on_transmit*/ exosip_on_transmit
+};
+
+static const switch_endpoint_interface exosip_endpoint_interface = {
+ /*.interface_name*/ "exosip",
+ /*.io_routines*/ &exosip_io_routines,
+ /*.event_handlers*/ &exosip_event_handlers,
+ /*.private*/ NULL,
+ /*.next*/ NULL
+};
+
+static const switch_loadable_module_interface exosip_module_interface = {
+ /*.module_name*/ modname,
+ /*.endpoint_interface*/ &exosip_endpoint_interface,
+ /*.timer_interface*/ NULL,
+ /*.dialplan_interface*/ NULL,
+ /*.codec_interface*/ NULL,
+ /*.application_interface*/ NULL
+};
+
+
+SWITCH_MOD_DECLARE(switch_status) switch_module_shutdown(void)
+{
+ if (globals.running) {
+ globals.running = -1;
+ while(globals.running) {
+ switch_yield(1000);
+ }
+ }
+ return SWITCH_STATUS_SUCCESS;
+}
+
+SWITCH_MOD_DECLARE(switch_status) switch_module_load(const switch_loadable_module_interface **interface, char *filename) {
+ /* NOTE: **interface is **_interface because the common lib redefines interface to struct in some situations */
+
+ 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 = &exosip_module_interface;
+
+ /* indicate that the module should continue to be loaded */
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status exosip_create_call(eXosip_event_t *event)
+{
+ switch_core_session *session;
+ sdp_message_t *remote_sdp = NULL;
+ sdp_connection_t *conn = NULL;
+ sdp_media_t *remote_med = NULL, *audio_tab[10], *video_tab[10], *t38_tab[10], *app_tab[10];
+ char local_sdp_str[8192] = "", port[8] = "";
+ int mline = 0, pos = 0;
+ switch_channel *channel = NULL;
+ char name[128];
+ char *dpayload, *dname, *drate;
+ char *remote_sdp_str = NULL;
+
+ if ((session = switch_core_session_request(&exosip_endpoint_interface, NULL))) {
+ struct private_object *tech_pvt;
+ switch_codec_interface *codecs[SWITCH_MAX_CODECS];
+ int num_codecs = 0;
+
+
+ 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;
+ switch_mutex_init(&tech_pvt->rtp_lock, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session));
+ } else {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Hey where is my memory pool?\n");
+ switch_core_session_destroy(&session);
+ return SWITCH_STATUS_MEMERR;
+ }
+
+ if ((tech_pvt->caller_profile = switch_caller_profile_new(session,
+ globals.dialplan,
+ event->request->from->displayname,
+ event->request->from->url->username,
+ event->request->from->url->host,
+ NULL,
+ NULL,
+ event->request->req_uri->username))) {
+ switch_channel_set_caller_profile(channel, tech_pvt->caller_profile);
+ }
+
+ switch_set_flag(tech_pvt, TFLAG_INBOUND);
+ tech_pvt->did = event->did;
+ tech_pvt->cid = event->cid;
+ tech_pvt->tid = event->tid;
+
+ snprintf(name, sizeof(name), "Exosip/%s-%04x", tech_pvt->caller_profile->destination_number, rand() & 0xffff);
+ switch_channel_set_name(channel, name);
+
+ if (!(remote_sdp = eXosip_get_sdp_info(event->request))) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Cannot Find Remote SDP!\n");
+ exosip_on_hangup(session);
+ switch_core_session_destroy(&session);
+ return SWITCH_STATUS_GENERR;
+ }
+
+ eXosip_guess_localip(AF_INET, tech_pvt->local_sdp_audio_ip, 50);
+ tech_pvt->local_sdp_audio_port = next_rtp_port();
+ osip_rfc3264_init(&tech_pvt->sdp_config);
+ /* Add in what codecs we support locally */
+
+ if ((num_codecs = loadable_module_get_codecs(switch_core_session_get_pool(session), codecs, sizeof(codecs)/sizeof(codecs[0]))) > 0) {
+ int i;
+ static const switch_codec_implementation *imp;
+
+ for (i = 0; i < num_codecs; i++) {
+ int x = 0;
+ for (imp = codecs[i]->implementations ; imp ; imp = imp->next) {
+ sdp_add_codec(tech_pvt->sdp_config, codecs[i]->codec_type, codecs[i]->ianacode, codecs[i]->iananame, imp->samples_per_second, x++);
+ }
+ }
+ }
+ osip_rfc3264_prepare_answer(tech_pvt->sdp_config, remote_sdp, local_sdp_str, 8192);
+ sdp_message_init(&tech_pvt->local_sdp);
+ sdp_message_parse(tech_pvt->local_sdp, local_sdp_str);
+
+ sdp_message_to_str(remote_sdp, &remote_sdp_str);
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "LOCAL SDP:\n%s\nREMOTE SDP:\n%s", local_sdp_str,remote_sdp_str);
+
+ mline = 0;
+ while (0==osip_rfc3264_match(tech_pvt->sdp_config, remote_sdp, audio_tab, video_tab, t38_tab, app_tab, mline)) {
+ if (audio_tab[0] == NULL && video_tab[0] == NULL && t38_tab[0] == NULL && app_tab[0] == NULL) {
+
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Got no compatible codecs!\n");
+ break;
+ }
+ for (pos=0; audio_tab[pos]!=NULL; pos++) {
+ osip_rfc3264_complete_answer(tech_pvt->sdp_config, remote_sdp, tech_pvt->local_sdp, audio_tab[pos], mline);
+ if (parse_sdp_media(audio_tab[pos], &dname, &drate, &dpayload) == SWITCH_STATUS_SUCCESS) {
+ tech_pvt->payload_num = atoi(dpayload);
+ break;
+ }
+ }
+ mline++;
+ }
+ free(remote_sdp_str);
+ sdp_message_o_origin_set(tech_pvt->local_sdp, "OpenSWITCH2", "0", "0", "IN", "IP4", tech_pvt->local_sdp_audio_ip);
+ sdp_message_s_name_set(tech_pvt->local_sdp, "SIP Call");
+ sdp_message_c_connection_add(tech_pvt->local_sdp, -1, "IN", "IP4", tech_pvt->local_sdp_audio_ip, NULL, NULL);
+ snprintf(port, sizeof(port), "%i", tech_pvt->local_sdp_audio_port);
+ sdp_message_m_port_set(tech_pvt->local_sdp, 0, osip_strdup(port));
+
+ conn = eXosip_get_audio_connection(remote_sdp);
+ remote_med = eXosip_get_audio_media(remote_sdp);
+ snprintf(tech_pvt->remote_sdp_audio_ip, 50, conn->c_addr);
+
+ tech_pvt->remote_sdp_audio_port = atoi(remote_med->m_port);
+
+ snprintf(tech_pvt->call_id, sizeof(tech_pvt->call_id), "%d", event->cid);
+ switch_core_hash_insert(globals.call_hash, tech_pvt->call_id, tech_pvt);
+
+ if (!dname) {
+ exosip_on_hangup(session);
+ switch_core_session_destroy(&session);
+ return SWITCH_STATUS_GENERR;
+ }
+
+ switch_channel_set_state(channel, CS_INIT);
+
+
+ if (1) {
+ int rate = atoi(drate);
+
+ if (switch_core_codec_init(&tech_pvt->read_codec,
+ dname,
+ rate,
+ globals.codec_ms,
+ 1,
+ SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE,
+ NULL,
+ switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Can't load codec?\n");
+ switch_channel_hangup(channel);
+ return SWITCH_STATUS_FALSE;
+ } else {
+ if (switch_core_codec_init(&tech_pvt->write_codec,
+ dname,
+ rate,
+ globals.codec_ms,
+ 1,
+ SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE,
+ NULL,
+ switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Can't load codec?\n");
+ switch_channel_hangup(channel);
+ return SWITCH_STATUS_FALSE;
+ } else {
+ int ms;
+ tech_pvt->read_frame.rate = rate;
+ switch_set_flag(tech_pvt, TFLAG_USING_CODEC);
+ ms = tech_pvt->write_codec.implementation->microseconds_per_frame / 1000;
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Activate Inbound Codec %s/%d %d ms\n", dname, rate, ms);
+ tech_pvt->read_frame.codec = &tech_pvt->read_codec;
+ switch_core_session_set_read_codec(session, &tech_pvt->read_codec);
+ switch_core_session_set_write_codec(session, &tech_pvt->write_codec);
+ }
+ }
+ }
+
+ activate_rtp(tech_pvt);
+
+ if (switch_test_flag(tech_pvt, TFLAG_RTP)) {
+ switch_core_session_thread_launch(session);
+ } else {
+ switch_core_session_destroy(&session);
+ return SWITCH_STATUS_FALSE;
+ }
+ } else {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Cannot Create new Inbound Channel!\n");
+ }
+
+
+ return 0;
+
+}
+
+static void destroy_call_by_event(eXosip_event_t *event)
+{
+ struct private_object *tech_pvt;
+ switch_channel *channel = NULL;
+
+ if (!(tech_pvt = get_pvt_by_call_id(event->cid))) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Um in case you are interested, Can't find the pvt [%d]!\n", event->cid);
+ return;
+ }
+
+ channel = switch_core_session_get_channel(tech_pvt->session);
+ assert(channel != NULL);
+
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "destroy %s\n", switch_channel_get_name(channel));
+ exosip_kill_channel(tech_pvt->session, SWITCH_SIG_KILL);
+ switch_channel_hangup(channel);
+
+}
+
+static switch_status parse_sdp_media(sdp_media_t *media, char **dname, char **drate, char **dpayload)
+{
+ int pos = 0;
+ sdp_attribute_t *attr = NULL;
+ char *name, *rate, *payload;
+ switch_status status = SWITCH_STATUS_GENERR;
+
+ while (osip_list_eol(media->a_attributes, pos) == 0) {
+ attr = (sdp_attribute_t *)osip_list_get(media->a_attributes, pos);
+ if (attr != NULL && strcasecmp(attr->a_att_field, "rtpmap") == 0) {
+ payload = attr->a_att_value;
+ if ((name = strchr(payload, ' '))) {
+ *(name++) = '\0';
+ /* Name and payload are required */
+ *dpayload = strdup(payload);
+ status = SWITCH_STATUS_SUCCESS;
+ if ((rate = strchr(name, '/'))) {
+ *(rate++) = '\0';
+ *drate = strdup(rate);
+ *dname = strdup(name);
+ } else {
+ *dname = strdup(name);
+ *drate = strdup("8000");
+ }
+ } else {
+ *dpayload = strdup("10");
+ *dname = strdup("L16");
+ *drate = strdup("8000");
+ }
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Found negotiated codec Payload: %s Name: %s Rate: %s\n", *dpayload, *dname, *drate);
+ break;
+ }
+ attr = NULL;
+ pos++;
+ }
+
+ return status;
+}
+
+static void handle_answer(eXosip_event_t *event)
+{
+ osip_message_t *ack = NULL;
+ sdp_message_t *remote_sdp = NULL;
+ sdp_connection_t *conn = NULL;
+ sdp_media_t *remote_med = NULL;
+ struct private_object *tech_pvt;
+ char *dpayload = NULL, *dname = NULL, *drate = NULL;
+ switch_channel *channel;
+
+
+ if (!(tech_pvt = get_pvt_by_call_id(event->cid))) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Um in case you are interested, Can't find the pvt!\n");
+ return;
+ }
+
+ channel = switch_core_session_get_channel(tech_pvt->session);
+ assert(channel != NULL);
+
+ if (!event->response) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Someone answered... with no SDP information - WTF?!?\n");
+ switch_channel_hangup(channel);
+ return;
+ }
+
+ /* Get all of the remote SDP elements... stuff */
+ if (!(remote_sdp = eXosip_get_sdp_info(event->response))) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Cant Find SDP?\n");
+ switch_channel_hangup(channel);
+ return;
+ }
+
+
+ conn = eXosip_get_audio_connection(remote_sdp);
+ remote_med = eXosip_get_audio_media(remote_sdp);
+
+ /* Grab IP/port */
+ tech_pvt->remote_sdp_audio_port = atoi(remote_med->m_port);
+ snprintf(tech_pvt->remote_sdp_audio_ip, 50, conn->c_addr);
+
+ /* Grab codec elements */
+ if (parse_sdp_media(remote_med, &dname, &drate, &dpayload) == SWITCH_STATUS_SUCCESS) {
+ tech_pvt->payload_num = atoi(dpayload);
+ }
+
+ /* Assign them thar IDs */
+ tech_pvt->did = event->did;
+ tech_pvt->tid = event->tid;
+
+
+ if (1) {
+ int rate = atoi(drate);
+
+
+ if (switch_core_codec_init(&tech_pvt->read_codec,
+ dname,
+ rate,
+ globals.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_channel_hangup(channel);
+ return;
+ } else {
+ if (switch_core_codec_init(&tech_pvt->write_codec,
+ dname,
+ rate,
+ globals.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_channel_hangup(channel);
+ return;
+ } else {
+ int ms;
+ tech_pvt->read_frame.rate = rate;
+ switch_set_flag(tech_pvt, TFLAG_USING_CODEC);
+ ms = tech_pvt->write_codec.implementation->microseconds_per_frame / 1000;
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Activate Outbound 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);
+ }
+ }
+ }
+
+
+ eXosip_lock();
+ eXosip_call_build_ack(event->did, &ack);
+ eXosip_call_send_ack(event->did, ack);
+ eXosip_unlock();
+
+ free(dname);
+ free(drate);
+ free(dpayload);
+
+
+ activate_rtp(tech_pvt);
+
+ if (switch_test_flag(tech_pvt, TFLAG_RTP)) {
+ channel = switch_core_session_get_channel(tech_pvt->session);
+ assert(channel != NULL);
+ switch_channel_answer(channel);
+ }
+}
+
+static void log_event(eXosip_event_t *je)
+{
+ char buf[100];
+
+ buf[0] = '\0';
+ if (je->type == EXOSIP_CALL_NOANSWER) {
+ snprintf (buf, 99, "<- (%i %i) No answer", je->cid, je->did);
+ } else if (je->type == EXOSIP_CALL_CLOSED) {
+ snprintf (buf, 99, "<- (%i %i) Call Closed", je->cid, je->did);
+ } else if (je->type == EXOSIP_CALL_RELEASED) {
+ snprintf (buf, 99, "<- (%i %i) Call released", je->cid, je->did);
+ } else if (je->type == EXOSIP_MESSAGE_NEW
+ && je->request!=NULL && MSG_IS_MESSAGE(je->request)) {
+ char *tmp = NULL;
+
+ if (je->request != NULL) {
+ osip_body_t *body;
+ osip_from_to_str (je->request->from, &tmp);
+
+ osip_message_get_body (je->request, 0, &body);
+ if (body != NULL && body->body != NULL) {
+ snprintf (buf, 99, "<- (%i) from: %s TEXT: %s",
+ je->tid, tmp, body->body);
+ }
+ osip_free (tmp);
+ } else {
+ snprintf (buf, 99, "<- (%i) New event for unknown request?", je->tid);
+ }
+ } else if (je->type == EXOSIP_MESSAGE_NEW) {
+ char *tmp = NULL;
+
+ osip_from_to_str (je->request->from, &tmp);
+ snprintf (buf, 99, "<- (%i) %s from: %s",
+ je->tid, je->request->sip_method, tmp);
+ osip_free (tmp);
+ } else if (je->type == EXOSIP_MESSAGE_PROCEEDING
+ || je->type == EXOSIP_MESSAGE_ANSWERED
+ || je->type == EXOSIP_MESSAGE_REDIRECTED
+ || je->type == EXOSIP_MESSAGE_REQUESTFAILURE
+ || je->type == EXOSIP_MESSAGE_SERVERFAILURE
+ || je->type == EXOSIP_MESSAGE_GLOBALFAILURE) {
+ if (je->response != NULL && je->request != NULL) {
+ char *tmp = NULL;
+
+ osip_to_to_str (je->request->to, &tmp);
+ snprintf (buf, 99, "<- (%i) [%i %s for %s] to: %s",
+ je->tid, je->response->status_code,
+ je->response->reason_phrase, je->request->sip_method, tmp);
+ osip_free (tmp);
+ } else if (je->request != NULL) {
+ snprintf (buf, 99, "<- (%i) Error for %s request",
+ je->tid, je->request->sip_method);
+ } else {
+ snprintf (buf, 99, "<- (%i) Error for unknown request", je->tid);
+ }
+ } else if (je->response == NULL && je->request != NULL && je->cid > 0) {
+ char *tmp = NULL;
+
+ osip_from_to_str (je->request->from, &tmp);
+ snprintf (buf, 99, "<- (%i %i) %s from: %s",
+ je->cid, je->did, je->request->cseq->method, tmp);
+ osip_free (tmp);
+ } else if (je->response != NULL && je->cid > 0) {
+ char *tmp = NULL;
+
+ osip_to_to_str (je->request->to, &tmp);
+ snprintf (buf, 99, "<- (%i %i) [%i %s] for %s to: %s",
+ je->cid, je->did, je->response->status_code,
+ je->response->reason_phrase, je->request->sip_method, tmp);
+ osip_free (tmp);
+ } else if (je->response == NULL && je->request != NULL && je->rid > 0) {
+ char *tmp = NULL;
+
+ osip_from_to_str (je->request->from, &tmp);
+ snprintf (buf, 99, "<- (%i) %s from: %s",
+ je->rid, je->request->cseq->method, tmp);
+ osip_free (tmp);
+ } else if (je->response != NULL && je->rid > 0) {
+ char *tmp = NULL;
+
+ osip_from_to_str (je->request->from, &tmp);
+ snprintf (buf, 99, "<- (%i) [%i %s] from: %s",
+ je->rid, je->response->status_code,
+ je->response->reason_phrase, tmp);
+ osip_free (tmp);
+ } else if (je->response == NULL && je->request != NULL && je->sid > 0) {
+ char *tmp = NULL;
+ char *stat = NULL;
+ osip_header_t *sub_state;
+
+ osip_message_header_get_byname (je->request, "subscription-state",
+ 0, &sub_state);
+ if (sub_state != NULL && sub_state->hvalue != NULL)
+ stat = sub_state->hvalue;
+
+ osip_uri_to_str (je->request->from->url, &tmp);
+ snprintf (buf, 99, "<- (%i) [%s] %s from: %s",
+ je->sid, stat, je->request->cseq->method, tmp);
+ osip_free (tmp);
+ } else if (je->response != NULL && je->sid > 0) {
+ char *tmp = NULL;
+
+ osip_uri_to_str (je->request->to->url, &tmp);
+ snprintf (buf, 99, "<- (%i) [%i %s] from: %s",
+ je->sid, je->response->status_code,
+ je->response->reason_phrase, tmp);
+ osip_free (tmp);
+ } else if (je->response == NULL && je->request != NULL) {
+ char *tmp = NULL;
+
+ osip_from_to_str (je->request->from, &tmp);
+ snprintf (buf, 99, "<- (c=%i|d=%i|s=%i|n=%i) %s from: %s",
+ je->cid, je->did, je->sid, je->nid,
+ je->request->sip_method, tmp);
+ osip_free (tmp);
+ } else if (je->response != NULL) {
+ char *tmp = NULL;
+
+ osip_from_to_str (je->request->from, &tmp);
+ snprintf (buf, 99, "<- (c=%i|d=%i|s=%i|n=%i) [%i %s] for %s from: %s",
+ je->cid, je->did, je->sid, je->nid,
+ je->response->status_code, je->response->reason_phrase,
+ je->request->sip_method, tmp);
+ osip_free (tmp);
+ } else {
+ snprintf (buf, 99, "<- (c=%i|d=%i|s=%i|n=%i|t=%i) %s",
+ je->cid, je->did, je->sid, je->nid, je->tid, je->textinfo);
+ }
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "\n%s\n", buf);
+ /* Print it out */
+}
+
+
+
+static void *monitor_thread_run(void)
+{
+ eXosip_event_t *event = NULL;
+
+ globals.running = 1;
+ while (globals.running > 0) {
+ if (!(event = eXosip_event_wait(0,100))) {
+ switch_yield(100);
+ continue;
+ }
+
+ eXosip_lock();
+ eXosip_automatic_action ();
+ eXosip_unlock();
+
+ log_event(event);
+
+ switch(event->type) {
+ case EXOSIP_CALL_INVITE:
+ exosip_create_call(event);
+ break;
+ case EXOSIP_CALL_REINVITE:
+ /* See what the reinvite is about - on hold or whatever */
+ //handle_reinvite(event);
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Got a reinvite.\n");
+ break;
+ case EXOSIP_CALL_MESSAGE_NEW:
+ if (event->request != NULL && MSG_IS_REFER(event->request)) {
+ //handle_call_transfer(event);
+ }
+ break;
+ case EXOSIP_CALL_ACK:
+ /* If audio is not flowing and this has SDP - fire it up! */
+ break;
+ case EXOSIP_CALL_ANSWERED:
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "The call was answered.\n");
+ handle_answer(event);
+ break;
+ case EXOSIP_CALL_PROCEEDING:
+ /* This is like a 100 Trying... yeah */
+ break;
+ case EXOSIP_CALL_RINGING:
+ //handle_ringing(event);
+ break;
+ case EXOSIP_CALL_REDIRECTED:
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Call was redirect\n");
+ break;
+ case EXOSIP_CALL_CLOSED:
+ destroy_call_by_event(event);
+ break;
+ case EXOSIP_CALL_RELEASED:
+ destroy_call_by_event(event);
+ break;
+ case EXOSIP_CALL_NOANSWER:
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "The call was not answered.\n");
+ destroy_call_by_event(event);
+ break;
+ case EXOSIP_CALL_REQUESTFAILURE:
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Request failure\n");
+ destroy_call_by_event(event);
+ break;
+ case EXOSIP_CALL_SERVERFAILURE:
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Server failure\n");
+ destroy_call_by_event(event);
+ break;
+ case EXOSIP_CALL_GLOBALFAILURE:
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Global failure\n");
+ destroy_call_by_event(event);
+ break;
+ /* Registration related stuff */
+ case EXOSIP_REGISTRATION_NEW:
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Received registration attempt\n");
+ break;
+ default:
+ /* Unknown event... casually absorb it for now */
+ break;
+ }
+
+ //switch_console_printf(SWITCH_CHANNEL_CONSOLE, "There was an event (%d) [%s]\n", event->type, event->textinfo);
+ /* Free the event */
+ eXosip_event_free(event);
+ }
+
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Monitor Thread Exiting\n");
+ globals.running = 0;
+ return NULL;
+}
+
+
+static int config_exosip(int reload)
+{
+ switch_config cfg;
+ char *var, *val;
+ char *cf = "exosip.conf";
+
+ globals.bytes_per_frame = DEFAULT_BYTES_PER_FRAME;
+ switch_core_hash_init(&globals.call_hash, module_pool);
+
+ if (!switch_config_open_file(&cfg, cf)) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "open of %s failed\n", cf);
+ return SWITCH_STATUS_TERM;
+ }
+
+ globals.rtp_start = 16384;
+ globals.rtp_end = 32768;
+
+ 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, "dialplan")) {
+ set_global_dialplan(val);
+ } else if (!strcmp(var, "rtp_min_port")) {
+ globals.rtp_start = atoi(val);
+ } else if (!strcmp(var, "rtp_max_port")) {
+ globals.rtp_end = atoi(val);
+ } else if (!strcmp(var, "codec_ms")) {
+ globals.codec_ms = atoi(val);
+ }
+ }
+ }
+
+ if (!globals.codec_ms) {
+ globals.codec_ms = 20;
+ }
+
+ if (!globals.port) {
+ globals.port = 5060;
+ }
+
+ switch_config_close_file(&cfg);
+
+ if (!globals.dialplan) {
+ set_global_dialplan("default");
+ }
+
+ if (eXosip_init ()) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "eXosip_init initialization failed!\n");
+ return SWITCH_STATUS_GENERR;
+ }
+ if (eXosip_listen_addr (IPPROTO_UDP, NULL, globals.port, AF_INET, 0)) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "eXosip_listen_addr failed!\n");
+ return SWITCH_STATUS_GENERR;
+ }
+
+ switch_mutex_init(&globals.port_lock, SWITCH_MUTEX_NESTED, module_pool);
+
+ /* Setup the user agent */
+ eXosip_set_user_agent("FreeSWITCH");
+
+ monitor_thread_run();
+
+ eXosip_quit();
+
+ return 0;
+
+}
+
+
+SWITCH_MOD_DECLARE(switch_status) switch_module_runtime(void)
+{
+ config_exosip(0);
+ return SWITCH_STATUS_TERM;
+}
+
--- /dev/null
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+ ProjectType="Visual C++"\r
+ Version="8.00"\r
+ Name="mod_exosip"\r
+ ProjectGUID="{45DF84ED-D24A-4FF6-B5B0-0A9A5FDB9552}"\r
+ RootNamespace="mod_exosip"\r
+ Keyword="Win32Proj"\r
+ >\r
+ <Platforms>\r
+ <Platform\r
+ Name="Win32"\r
+ />\r
+ </Platforms>\r
+ <ToolFiles>\r
+ </ToolFiles>\r
+ <Configurations>\r
+ <Configuration\r
+ Name="Debug|Win32"\r
+ OutputDirectory="Debug"\r
+ IntermediateDirectory="Debug"\r
+ ConfigurationType="2"\r
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"\r
+ CharacterSet="2"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ CommandLine="cscript /nologo $(InputDir)..\..\..\w32\vsnet\getlibs.vbs Mod_Exosip Debug"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalOptions="/D _CRT_SECURE_NO_DEPRECATE"\r
+ Optimization="0"\r
+ 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""\r
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;MOD_EXPORTS"\r
+ GeneratePreprocessedFile="0"\r
+ MinimalRebuild="true"\r
+ BasicRuntimeChecks="3"\r
+ RuntimeLibrary="1"\r
+ UsePrecompiledHeader="0"\r
+ WarningLevel="3"\r
+ Detect64BitPortabilityProblems="true"\r
+ DebugInformationFormat="4"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ AdditionalOptions="/NODEFAULTLIB:LIBCMT"\r
+ AdditionalDependencies="Ws2_32.lib Iphlpapi.lib libcpmt.lib eXosip.lib jrtplib.lib jthread.lib osipparser2.lib osip2.lib"\r
+ ShowProgress="0"\r
+ OutputFile="..\..\..\w32\vsnet\$(OutDir)/mod/mod_exosip.dll"\r
+ LinkIncremental="2"\r
+ 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""\r
+ IgnoreAllDefaultLibraries="false"\r
+ IgnoreDefaultLibraryNames=""\r
+ GenerateDebugInformation="true"\r
+ ProgramDatabaseFile="$(OutDir)/mod_exosip.pdb"\r
+ SubSystem="2"\r
+ ImportLibrary="$(OutDir)/mod_exosip.lib"\r
+ TargetMachine="1"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebDeploymentTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ <Configuration\r
+ Name="Release|Win32"\r
+ OutputDirectory="Release"\r
+ IntermediateDirectory="Release"\r
+ ConfigurationType="2"\r
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"\r
+ CharacterSet="2"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ CommandLine="cscript /nologo $(InputDir)..\..\..\w32\vsnet\getlibs.vbs Mod_Exosip Release"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ 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""\r
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;MOD_EXPORTS"\r
+ RuntimeLibrary="0"\r
+ UsePrecompiledHeader="0"\r
+ WarningLevel="3"\r
+ Detect64BitPortabilityProblems="true"\r
+ DebugInformationFormat="3"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ AdditionalDependencies="Ws2_32.lib Iphlpapi.lib libcpmt.lib eXosip.lib jrtplib.lib jthread.lib osipparser2.lib osip2.lib"\r
+ OutputFile="..\..\..\w32\vsnet\$(OutDir)/mod/mod_exosip.dll"\r
+ LinkIncremental="1"\r
+ 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""\r
+ GenerateDebugInformation="true"\r
+ SubSystem="2"\r
+ OptimizeReferences="2"\r
+ EnableCOMDATFolding="2"\r
+ ImportLibrary="$(OutDir)/mod_exosip.lib"\r
+ TargetMachine="1"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebDeploymentTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ </Configurations>\r
+ <References>\r
+ </References>\r
+ <Files>\r
+ <Filter\r
+ Name="Source Files"\r
+ Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"\r
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"\r
+ >\r
+ <File\r
+ RelativePath="..\..\..\libs\jrtp4c\src\jrtp4c.cpp"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath=".\mod_exosip.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath="..\..\..\libs\osip\src\osipparser2\osip_rfc3264.c"\r
+ >\r
+ </File>\r
+ </Filter>\r
+ <Filter\r
+ Name="Header Files"\r
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"\r
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"\r
+ >\r
+ <File\r
+ RelativePath="..\..\..\libs\osip\src\osipparser2\osip_rfc3264i.h"\r
+ >\r
+ </File>\r
+ </Filter>\r
+ <Filter\r
+ Name="Resource Files"\r
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"\r
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"\r
+ >\r
+ </Filter>\r
+ </Files>\r
+ <Globals>\r
+ </Globals>\r
+</VisualStudioProject>\r
--- /dev/null
+/*
+ * 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_exosip.c -- eXoSIP SIP Endpoint
+ *
+ */
+
+
+#define HAVE_APR
+#include <switch.h>
+#include <ccrtp4c.h>
+#include <eXosip2/eXosip.h>
+#include <osip2/osip_mt.h>
+#include <osipparser2/osip_rfc3264.h>
+#include <osipparser2/osip_port.h>
+
+
+static const char modname[] = "mod_exosip";
+#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_IO = (1 << 0),
+ TFLAG_INBOUND = (1 << 1),
+ TFLAG_OUTBOUND = (1 << 2),
+ TFLAG_DTMF = (1 << 3),
+ TFLAG_READING = (1 << 4),
+ TFLAG_WRITING = (1 << 5),
+ TFLAG_USING_CODEC = (1 << 6),
+ TFLAG_RTP = (1 << 7),
+ TFLAG_BYE = (1 << 8)
+} TFLAGS;
+
+
+#define PACKET_LEN 160
+#define DEFAULT_BYTES_PER_FRAME 160
+
+
+static const switch_endpoint_interface exosip_endpoint_interface;
+
+static struct {
+ int debug;
+ int bytes_per_frame;
+ char *dialplan;
+ int port;
+ int rtp_start;
+ int rtp_end;
+ switch_hash *call_hash;
+ switch_mutex_t *port_lock;
+ int running;
+ int codec_ms;
+} globals;
+
+struct private_object {
+ unsigned int flags;
+ switch_core_session *session;
+ switch_frame read_frame;
+ switch_codec read_codec;
+ switch_codec write_codec;
+ unsigned char read_buf[1024];
+ switch_caller_profile *caller_profile;
+ int cid;
+ int did;
+ int tid;
+ int32_t timestamp_send;
+ int32_t timestamp_recv;
+ int payload_num;
+ struct ccrtp4c *rtp_session;
+ struct osip_rfc3264 *sdp_config;
+ sdp_message_t *remote_sdp;
+ sdp_message_t *local_sdp;
+ char remote_sdp_audio_ip[50];
+ int remote_sdp_audio_port;
+ char local_sdp_audio_ip[50];
+ int local_sdp_audio_port;
+ char call_id[50];
+ int ssrc;
+ //switch_mutex_t *rtp_lock;
+};
+
+
+static int next_rtp_port(void)
+{
+ int port;
+
+ switch_mutex_lock(globals.port_lock);
+ port = globals.rtp_start;
+ globals.rtp_start += 2;
+ if (port >= globals.rtp_end) {
+ port = globals.rtp_start;
+ }
+ switch_mutex_unlock(globals.port_lock);
+ return port;
+}
+
+
+static void set_global_dialplan(char *dialplan)
+{
+ if (globals.dialplan) {
+ free(globals.dialplan);
+ globals.dialplan = NULL;
+ }
+
+ globals.dialplan = strdup(dialplan);
+}
+
+static void set_global_dialplan(char *dialplan);
+static switch_status exosip_on_init(switch_core_session *session);
+static switch_status exosip_on_hangup(switch_core_session *session);
+static switch_status exosip_on_loopback(switch_core_session *session);
+static switch_status exosip_on_transmit(switch_core_session *session);
+static switch_status exosip_outgoing_channel(switch_core_session *session, switch_caller_profile *outbound_profile, switch_core_session **new_session);
+static switch_status exosip_read_frame(switch_core_session *session, switch_frame **frame, int timeout, switch_io_flag flags);
+static switch_status exosip_write_frame(switch_core_session *session, switch_frame *frame, int timeout, switch_io_flag flags);
+static int config_exosip(int reload);
+static switch_status parse_sdp_media(sdp_media_t *media, char **dname, char **drate, char **dpayload);
+static switch_status exosip_kill_channel(switch_core_session *session, int sig);
+static void activate_rtp(struct private_object *tech_pvt);
+static void deactivate_rtp(struct private_object *tech_pvt);
+
+static struct private_object *get_pvt_by_call_id(int id)
+{
+ char name[50];
+ snprintf(name, sizeof(name), "%d", id);
+ return (struct private_object *) switch_core_hash_find(globals.call_hash, name);
+}
+
+static switch_status exosip_on_execute(switch_core_session *session)
+{
+ return SWITCH_STATUS_SUCCESS;
+}
+
+
+static int sdp_add_codec(struct osip_rfc3264 *cnf, int codec_type, int payload, char *attribute, int rate, int index)
+{
+ char tmp[4] = "", string[32] = "";
+ sdp_media_t *med = NULL;
+ sdp_attribute_t *attr = NULL;
+
+ sdp_media_init(&med);
+ if (med == NULL)
+ return -1;
+
+ if (!index) {
+ snprintf(tmp, sizeof(tmp), "%i", payload);
+ med->m_proto = osip_strdup("RTP/AVP");
+ osip_list_add(med->m_payloads, osip_strdup(tmp), -1);
+ }
+ if (attribute) {
+ sdp_attribute_init(&attr);
+ attr->a_att_field = osip_strdup("rtpmap");
+ snprintf(string, sizeof(string), "%i %s/%i", payload, attribute, rate);
+ attr->a_att_value = osip_strdup(string);
+ osip_list_add(med->a_attributes, attr, -1);
+ }
+
+ switch (codec_type) {
+ case SWITCH_CODEC_TYPE_AUDIO:
+ med->m_media = osip_strdup("audio");
+ osip_rfc3264_add_audio_media(cnf, med, -1);
+ break;
+ case SWITCH_CODEC_TYPE_VIDEO:
+ med->m_media = osip_strdup("video");
+ osip_rfc3264_add_video_media(cnf, med, -1);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+
+/*
+ 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 exosip_on_init(switch_core_session *session)
+{
+ struct private_object *tech_pvt;
+ switch_channel *channel = NULL;
+ char from_uri[512] = "", localip[128] = "", port[7] = "", *buf = NULL, tmp[512] = "";
+ osip_message_t *invite = 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->read_frame.data = tech_pvt->read_buf;
+
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "EXOSIP INIT\n");
+
+ if (switch_test_flag(tech_pvt, TFLAG_OUTBOUND)) {
+ char *dest_uri;
+ switch_codec_interface *codecs[512];
+ int num_codecs = 0;
+ /* do SIP Goodies...*/
+
+ /* Generate callerid URI */
+ eXosip_guess_localip(AF_INET, localip, 128);
+ snprintf(from_uri, sizeof(from_uri), "<sip:%s@%s>", tech_pvt->caller_profile->caller_id_number, localip);
+ /* Setup codec negotiation stuffs */
+ osip_rfc3264_init(&tech_pvt->sdp_config);
+ /* Decide on local IP and rtp port */
+ strncpy(tech_pvt->local_sdp_audio_ip, localip, sizeof(tech_pvt->local_sdp_audio_ip));
+ tech_pvt->local_sdp_audio_port = next_rtp_port();
+ /* Initialize SDP */
+ sdp_message_init(&tech_pvt->local_sdp);
+ sdp_message_v_version_set(tech_pvt->local_sdp, "0");
+ sdp_message_o_origin_set(tech_pvt->local_sdp, "OpenSWITCH2", "0", "0", "IN", "IP4", tech_pvt->local_sdp_audio_ip);
+ sdp_message_s_name_set(tech_pvt->local_sdp, "SIP Call");
+ sdp_message_c_connection_add(tech_pvt->local_sdp, -1, "IN", "IP4", tech_pvt->local_sdp_audio_ip, NULL, NULL);
+ sdp_message_t_time_descr_add(tech_pvt->local_sdp, "0", "0");
+ snprintf(port, sizeof(port), "%i", tech_pvt->local_sdp_audio_port);
+ sdp_message_m_media_add(tech_pvt->local_sdp, "audio", port, NULL, "RTP/AVP");
+ /* Add in every codec we support on this outbound call */
+ if ((num_codecs = loadable_module_get_codecs(switch_core_session_get_pool(session), codecs, sizeof(codecs)/sizeof(codecs[0]))) > 0) {
+ int i;
+ static const switch_codec_implementation *imp;
+ for (i = 0; i < num_codecs; i++) {
+ int x = 0;
+
+ snprintf(tmp, sizeof(tmp), "%i", codecs[i]->ianacode);
+ sdp_message_m_payload_add(tech_pvt->local_sdp, 0, osip_strdup(tmp));
+ for (imp = codecs[i]->implementations ; imp ; imp = imp->next) {
+ /* Add to SDP config */
+ sdp_add_codec(tech_pvt->sdp_config, codecs[i]->codec_type, codecs[i]->ianacode, codecs[i]->iananame, imp->samples_per_second, x++);
+ /* Add to SDP message */
+
+ snprintf(tmp, sizeof(tmp), "%i %s/%i", codecs[i]->ianacode, codecs[i]->iananame, imp->samples_per_second);
+ sdp_message_a_attribute_add(tech_pvt->local_sdp, 0, "rtpmap", osip_strdup(tmp));
+ memset(tmp, 0, sizeof(tmp));
+ }
+ }
+ }
+ /* Setup our INVITE */
+ eXosip_lock();
+ if (!(dest_uri = (char *) switch_core_session_alloc(session, strlen(tech_pvt->caller_profile->destination_number) + 10))) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "AIEEEE!\n");
+ assert(dest_uri != NULL);
+ }
+ sprintf(dest_uri, "sip:%s", tech_pvt->caller_profile->destination_number);
+ eXosip_call_build_initial_invite(&invite, dest_uri, from_uri, NULL, NULL);
+ osip_message_set_supported(invite, "100rel, replaces");
+ /* Add SDP to the INVITE */
+ sdp_message_to_str(tech_pvt->local_sdp, &buf);
+ osip_message_set_body(invite, buf, strlen(buf));
+ osip_message_set_content_type(invite, "application/sdp");
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "OUTBOUND SDP:\n%s\n", buf);
+ free(buf);
+ /* Send the INVITE */
+ tech_pvt->cid = eXosip_call_send_initial_invite(invite);
+ snprintf(tech_pvt->call_id, sizeof(tech_pvt->call_id), "%d", tech_pvt->cid);
+ switch_core_hash_insert(globals.call_hash, tech_pvt->call_id, tech_pvt);
+ tech_pvt->did = -1;
+ eXosip_unlock();
+ }
+
+ /* Let Media Work */
+ 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 exosip_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, "EXOSIP RING\n");
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status exosip_on_hangup(switch_core_session *session)
+{
+ struct private_object *tech_pvt;
+ switch_channel *channel = NULL;
+ int i;
+
+ channel = switch_core_session_get_channel(session);
+ assert(channel != NULL);
+
+ tech_pvt = switch_core_session_get_private(session);
+ assert(tech_pvt != NULL);
+
+
+ switch_core_hash_delete(globals.call_hash, tech_pvt->call_id);
+
+
+ switch_set_flag(tech_pvt, TFLAG_BYE);
+ switch_clear_flag(tech_pvt, TFLAG_IO);
+
+ deactivate_rtp(tech_pvt);
+
+ i = eXosip_call_terminate(tech_pvt->cid, tech_pvt->did);
+
+ if (switch_test_flag(tech_pvt, TFLAG_USING_CODEC)) {
+ switch_core_codec_destroy(&tech_pvt->read_codec);
+ switch_core_codec_destroy(&tech_pvt->write_codec);
+ }
+
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "EXOSIP HANGUP %s %d/%d=%d\n", switch_channel_get_name(channel), tech_pvt->cid, tech_pvt->did, i);
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status exosip_on_loopback(switch_core_session *session)
+{
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "EXOSIP LOOPBACK\n");
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status exosip_on_transmit(switch_core_session *session)
+{
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "EXOSIP TRANSMIT\n");
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status exosip_outgoing_channel(switch_core_session *session, switch_caller_profile *outbound_profile, switch_core_session **new_session)
+{
+ if ((*new_session = switch_core_session_request(&exosip_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);
+ //switch_mutex_init(&tech_pvt->rtp_lock, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(*new_session));
+ 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), "Exosip/%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 void deactivate_rtp(struct private_object *tech_pvt)
+{
+ int loops = 0;
+ if (tech_pvt->rtp_session) {
+ //switch_mutex_lock(tech_pvt->rtp_lock);
+
+ while(loops < 10 && (switch_test_flag(tech_pvt, TFLAG_READING) || switch_test_flag(tech_pvt, TFLAG_WRITING))) {
+ switch_yield(10000);
+ loops++;
+ }
+
+ ccrtp4c_destroy(&tech_pvt->rtp_session);
+ tech_pvt->rtp_session = NULL;
+ //switch_mutex_unlock(tech_pvt->rtp_lock);
+ }
+}
+
+static void activate_rtp(struct private_object *tech_pvt)
+{
+ int bw, ms;
+ switch_channel *channel;
+
+ assert(tech_pvt != NULL);
+
+ channel = switch_core_session_get_channel(tech_pvt->session);
+ assert(channel != NULL);
+
+
+
+ //switch_mutex_lock(tech_pvt->rtp_lock);
+ if (switch_test_flag(tech_pvt, TFLAG_USING_CODEC)) {
+ bw = tech_pvt->read_codec.implementation->bits_per_second;
+ ms = tech_pvt->read_codec.implementation->nanoseconds_per_frame;
+ } else {
+ switch_channel_get_raw_mode(channel, NULL, NULL, NULL, &ms, &bw);
+ bw *= 8;
+ }
+
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Activating RTP %s:%d->%s:%d codec: %d ms: %d\n",
+ tech_pvt->local_sdp_audio_ip,
+ tech_pvt->local_sdp_audio_port,
+ tech_pvt->remote_sdp_audio_ip,
+ tech_pvt->remote_sdp_audio_port,
+ tech_pvt->read_codec.codec_interface->ianacode,
+ ms
+ );
+
+
+
+
+ tech_pvt->rtp_session = ccrtp4c_new(
+ tech_pvt->local_sdp_audio_ip,
+ tech_pvt->local_sdp_audio_port,
+ tech_pvt->remote_sdp_audio_ip,
+ tech_pvt->remote_sdp_audio_port,
+ tech_pvt->read_codec.codec_interface->ianacode,
+ ms,
+ ms * 15);
+
+ if (tech_pvt->rtp_session) {
+ tech_pvt->ssrc = ccrtp4c_get_ssrc(tech_pvt->rtp_session);
+ ccrtp4c_start(tech_pvt->rtp_session);
+ switch_set_flag(tech_pvt, TFLAG_RTP);
+ } else {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Oh oh?\n");
+ }
+
+ //switch_mutex_unlock(tech_pvt->rtp_lock);
+}
+
+static switch_status exosip_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);
+
+ if (!switch_channel_test_flag(channel, CF_OUTBOUND)) {
+ char *buf = NULL;
+ osip_message_t *answer = NULL;
+
+
+ /* Transmit 200 OK with SDP */
+ eXosip_lock();
+ eXosip_call_build_answer(tech_pvt->tid, 200, &answer);
+ sdp_message_to_str(tech_pvt->local_sdp, &buf);
+ osip_message_set_body(answer, buf, strlen(buf));
+ osip_message_set_content_type(answer, "application/sdp");
+ free(buf);
+ eXosip_call_send_answer(tech_pvt->tid, 200, answer);
+ eXosip_unlock();
+ }
+
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+
+static switch_status exosip_read_frame(switch_core_session *session, switch_frame **frame, int timeout, switch_io_flag flags)
+{
+ struct private_object *tech_pvt = NULL;
+ size_t bytes = 0, samples = 0, frames=0, ms=0;
+ 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->read_frame.datalen = 0;
+ switch_set_flag(tech_pvt, TFLAG_READING);
+
+ if (switch_test_flag(tech_pvt, TFLAG_USING_CODEC)) {
+ bytes = tech_pvt->read_codec.implementation->encoded_bytes_per_frame;
+ samples = tech_pvt->read_codec.implementation->samples_per_frame;
+ } else {
+ assert(0);
+ }
+
+ if (switch_test_flag(tech_pvt, TFLAG_IO)) {
+ if (!switch_test_flag(tech_pvt, TFLAG_RTP)) {
+ activate_rtp(tech_pvt);
+ }
+
+ assert(tech_pvt->rtp_session != NULL);
+ tech_pvt->read_frame.datalen = 0;
+
+ while(!switch_test_flag(tech_pvt, TFLAG_BYE) && switch_test_flag(tech_pvt, TFLAG_IO) && tech_pvt->read_frame.datalen == 0) {
+ if ((tech_pvt->read_frame.datalen =
+ ccrtp4c_read(tech_pvt->rtp_session,
+ tech_pvt->read_frame.data,
+ sizeof(tech_pvt->read_buf),
+ &tech_pvt->timestamp_recv))) {
+ bytes = tech_pvt->read_codec.implementation->encoded_bytes_per_frame;
+ frames = (tech_pvt->read_frame.datalen / bytes);
+ samples = frames * tech_pvt->read_codec.implementation->samples_per_frame;
+ ms = frames * tech_pvt->read_codec.implementation->nanoseconds_per_frame / 1000;
+ tech_pvt->timestamp_recv += samples;
+ break;
+ }
+ switch_yield(100);
+ }
+
+ //printf("%s %s->%s recv %d bytes %d samples in %d frames taking up %d ms ts=%d\n", switch_channel_get_name(channel), tech_pvt->local_sdp_audio_ip, tech_pvt->local_sdp_audio_ip, tech_pvt->read_frame.datalen, samples, frames, ms, tech_pvt->timestamp_recv);
+
+
+ //switch_mutex_unlock(tech_pvt->rtp_lock);
+
+ } else {
+ memset(tech_pvt->read_buf, 0, 160);
+ tech_pvt->read_frame.datalen = 160;
+ }
+
+ switch_clear_flag(tech_pvt, TFLAG_READING);
+
+ if (switch_test_flag(tech_pvt, TFLAG_BYE)) {
+ switch_channel_hangup(channel);
+ return SWITCH_STATUS_FALSE;
+ }
+
+ *frame = &tech_pvt->read_frame;
+
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+
+static switch_status exosip_write_frame(switch_core_session *session, switch_frame *frame, int timeout, switch_io_flag flags)
+{
+ struct private_object *tech_pvt;
+ switch_channel *channel = NULL;
+ switch_status status = SWITCH_STATUS_SUCCESS;
+ int bytes=0, samples=0, ms=0, frames=0;
+
+ 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_RTP)) {
+ activate_rtp(tech_pvt);
+ }
+
+ if (!switch_test_flag(tech_pvt, TFLAG_IO)) {
+ return SWITCH_STATUS_SUCCESS;
+ }
+
+ if (switch_test_flag(tech_pvt, TFLAG_BYE)) {
+ switch_channel_hangup(channel);
+ return SWITCH_STATUS_FALSE;
+ }
+
+ switch_set_flag(tech_pvt, TFLAG_WRITING);
+ //switch_mutex_lock(tech_pvt->rtp_lock);
+
+ if (switch_test_flag(tech_pvt, TFLAG_USING_CODEC)) {
+ bytes = tech_pvt->read_codec.implementation->encoded_bytes_per_frame;
+ frames = (frame->datalen / bytes);
+ samples = frames * tech_pvt->read_codec.implementation->samples_per_frame;
+ ms = frames * tech_pvt->read_codec.implementation->nanoseconds_per_frame / 1000;
+ } else {
+ assert(0);
+ }
+
+
+ //printf("%s %s->%s send %d bytes %d samples in %d frames taking up %d ms ts=%d\n", switch_channel_get_name(channel), tech_pvt->local_sdp_audio_ip, tech_pvt->remote_sdp_audio_ip, frame->datalen, samples, frames, ms, tech_pvt->timestamp_send);
+
+ tech_pvt->timestamp_send += (int)samples;
+ ccrtp4c_write(tech_pvt->rtp_session, frame->data, frame->datalen, &tech_pvt->timestamp_send);
+
+
+
+
+ switch_clear_flag(tech_pvt, TFLAG_WRITING);
+ //switch_mutex_unlock(tech_pvt->rtp_lock);
+ return status;
+}
+
+
+
+static switch_status exosip_kill_channel(switch_core_session *session, int sig)
+{
+ 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_clear_flag(tech_pvt, TFLAG_IO);
+ switch_set_flag(tech_pvt, TFLAG_BYE);
+
+ return SWITCH_STATUS_SUCCESS;
+
+}
+
+static switch_status exosip_waitfor_read(switch_core_session *session, int ms)
+{
+ 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 switch_status exosip_waitfor_write(switch_core_session *session, int ms)
+{
+ 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 const switch_io_routines exosip_io_routines = {
+ /*.outgoing_channel*/ exosip_outgoing_channel,
+ /*.answer_channel*/ exosip_answer_channel,
+ /*.read_frame*/ exosip_read_frame,
+ /*.write_frame*/ exosip_write_frame,
+ /*.kill_channel*/ exosip_kill_channel,
+ /*.waitfor_read*/ exosip_waitfor_read,
+ /*.waitfor_read*/ exosip_waitfor_write
+};
+
+static const switch_event_handler_table exosip_event_handlers = {
+ /*.on_init*/ exosip_on_init,
+ /*.on_ring*/ exosip_on_ring,
+ /*.on_execute*/ exosip_on_execute,
+ /*.on_hangup*/ exosip_on_hangup,
+ /*.on_loopback*/ exosip_on_loopback,
+ /*.on_transmit*/ exosip_on_transmit
+};
+
+static const switch_endpoint_interface exosip_endpoint_interface = {
+ /*.interface_name*/ "exosip",
+ /*.io_routines*/ &exosip_io_routines,
+ /*.event_handlers*/ &exosip_event_handlers,
+ /*.private*/ NULL,
+ /*.next*/ NULL
+};
+
+static const switch_loadable_module_interface exosip_module_interface = {
+ /*.module_name*/ modname,
+ /*.endpoint_interface*/ &exosip_endpoint_interface,
+ /*.timer_interface*/ NULL,
+ /*.dialplan_interface*/ NULL,
+ /*.codec_interface*/ NULL,
+ /*.application_interface*/ NULL
+};
+
+
+SWITCH_MOD_DECLARE(switch_status) switch_module_shutdown(void)
+{
+ if (globals.running) {
+ globals.running = -1;
+ while(globals.running) {
+ switch_yield(1000);
+ }
+ }
+ eXosip_quit();
+ return SWITCH_STATUS_SUCCESS;
+}
+
+SWITCH_MOD_DECLARE(switch_status) switch_module_load(const switch_loadable_module_interface **interface, char *filename) {
+ /* NOTE: **interface is **_interface because the common lib redefines interface to struct in some situations */
+
+ 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 = &exosip_module_interface;
+
+ /* indicate that the module should continue to be loaded */
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status exosip_create_call(eXosip_event_t *event)
+{
+ switch_core_session *session;
+ sdp_message_t *remote_sdp = NULL;
+ sdp_connection_t *conn = NULL;
+ sdp_media_t *remote_med = NULL, *audio_tab[10], *video_tab[10], *t38_tab[10], *app_tab[10];
+ char local_sdp_str[8192] = "", port[8] = "";
+ int mline = 0, pos = 0;
+ switch_channel *channel = NULL;
+ char name[128];
+ char *dpayload, *dname, *drate;
+ char *remote_sdp_str = NULL;
+
+ if ((session = switch_core_session_request(&exosip_endpoint_interface, NULL))) {
+ struct private_object *tech_pvt;
+ switch_codec_interface *codecs[512];
+ int num_codecs = 0;
+
+
+ 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;
+ //switch_mutex_init(&tech_pvt->rtp_lock, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session));
+ } else {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Hey where is my memory pool?\n");
+ switch_core_session_destroy(&session);
+ return SWITCH_STATUS_MEMERR;
+ }
+
+ if ((tech_pvt->caller_profile = switch_caller_profile_new(session,
+ globals.dialplan,
+ event->request->from->displayname,
+ event->request->from->url->username,
+ event->request->from->url->username,
+ NULL,
+ event->request->req_uri->username))) {
+ switch_channel_set_caller_profile(channel, tech_pvt->caller_profile);
+ }
+
+ switch_set_flag(tech_pvt, TFLAG_INBOUND);
+ tech_pvt->did = event->did;
+ tech_pvt->cid = event->cid;
+ tech_pvt->tid = event->tid;
+
+ snprintf(name, sizeof(name), "Exosip/%s-%04x", tech_pvt->caller_profile->destination_number, rand() & 0xffff);
+ switch_channel_set_name(channel, name);
+
+ if (!(remote_sdp = eXosip_get_sdp_info(event->request))) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Cannot Find Remote SDP!\n");
+ exosip_on_hangup(session);
+ switch_core_session_destroy(&session);
+ return SWITCH_STATUS_GENERR;
+ }
+
+ eXosip_guess_localip(AF_INET, tech_pvt->local_sdp_audio_ip, 50);
+ tech_pvt->local_sdp_audio_port = next_rtp_port();
+ osip_rfc3264_init(&tech_pvt->sdp_config);
+ /* Add in what codecs we support locally */
+
+ if ((num_codecs = loadable_module_get_codecs(switch_core_session_get_pool(session), codecs, sizeof(codecs)/sizeof(codecs[0]))) > 0) {
+ int i;
+ static const switch_codec_implementation *imp;
+
+ for (i = 0; i < num_codecs; i++) {
+ int x = 0;
+ for (imp = codecs[i]->implementations ; imp ; imp = imp->next) {
+ sdp_add_codec(tech_pvt->sdp_config, codecs[i]->codec_type, codecs[i]->ianacode, codecs[i]->iananame, imp->samples_per_second, x++);
+ }
+ }
+ }
+ osip_rfc3264_prepare_answer(tech_pvt->sdp_config, remote_sdp, local_sdp_str, 8192);
+ sdp_message_init(&tech_pvt->local_sdp);
+ sdp_message_parse(tech_pvt->local_sdp, local_sdp_str);
+
+ sdp_message_to_str(remote_sdp, &remote_sdp_str);
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "LOCAL SDP:\n%s\nREMOTE SDP:\n%s", local_sdp_str,remote_sdp_str);
+
+ mline = 0;
+ while (0==osip_rfc3264_match(tech_pvt->sdp_config, remote_sdp, audio_tab, video_tab, t38_tab, app_tab, mline)) {
+ if (audio_tab[0] == NULL && video_tab[0] == NULL && t38_tab[0] == NULL && app_tab[0] == NULL) {
+
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Got no compatible codecs!\n");
+ break;
+ }
+ for (pos=0; audio_tab[pos]!=NULL; pos++) {
+ osip_rfc3264_complete_answer(tech_pvt->sdp_config, remote_sdp, tech_pvt->local_sdp, audio_tab[pos], mline);
+ if (parse_sdp_media(audio_tab[pos], &dname, &drate, &dpayload) == SWITCH_STATUS_SUCCESS) {
+ tech_pvt->payload_num = atoi(dpayload);
+ break;
+ }
+ }
+ mline++;
+ }
+ free(remote_sdp_str);
+ sdp_message_o_origin_set(tech_pvt->local_sdp, "OpenSWITCH2", "0", "0", "IN", "IP4", tech_pvt->local_sdp_audio_ip);
+ sdp_message_s_name_set(tech_pvt->local_sdp, "SIP Call");
+ sdp_message_c_connection_add(tech_pvt->local_sdp, -1, "IN", "IP4", tech_pvt->local_sdp_audio_ip, NULL, NULL);
+ snprintf(port, sizeof(port), "%i", tech_pvt->local_sdp_audio_port);
+ sdp_message_m_port_set(tech_pvt->local_sdp, 0, osip_strdup(port));
+
+ conn = eXosip_get_audio_connection(remote_sdp);
+ remote_med = eXosip_get_audio_media(remote_sdp);
+ snprintf(tech_pvt->remote_sdp_audio_ip, 50, conn->c_addr);
+
+ tech_pvt->remote_sdp_audio_port = atoi(remote_med->m_port);
+
+ snprintf(tech_pvt->call_id, sizeof(tech_pvt->call_id), "%d", event->cid);
+ switch_core_hash_insert(globals.call_hash, tech_pvt->call_id, tech_pvt);
+
+ if (!dname) {
+ exosip_on_hangup(session);
+ switch_core_session_destroy(&session);
+ return SWITCH_STATUS_GENERR;
+ }
+
+ switch_channel_set_state(channel, CS_INIT);
+
+
+ if (1) {
+ int rate = atoi(drate);
+
+ if (switch_core_codec_init(&tech_pvt->read_codec,
+ dname,
+ rate,
+ globals.codec_ms,
+ SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE,
+ NULL) != SWITCH_STATUS_SUCCESS) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Can't load codec?\n");
+ switch_channel_hangup(channel);
+ return SWITCH_STATUS_FALSE;
+ } else {
+ if (switch_core_codec_init(&tech_pvt->write_codec,
+ dname,
+ rate,
+ globals.codec_ms,
+ SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE,
+ NULL) != SWITCH_STATUS_SUCCESS) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Can't load codec?\n");
+ switch_channel_hangup(channel);
+ return SWITCH_STATUS_FALSE;
+ } else {
+ int ms;
+ switch_set_flag(tech_pvt, TFLAG_USING_CODEC);
+ ms = tech_pvt->write_codec.implementation->nanoseconds_per_frame / 1000;
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Activate Inbound Codec %s/%d %d ms\n", dname, rate, ms);
+ tech_pvt->read_frame.codec = &tech_pvt->read_codec;
+ switch_core_session_set_read_codec(session, &tech_pvt->read_codec);
+ switch_core_session_set_write_codec(session, &tech_pvt->write_codec);
+ }
+ }
+ }
+
+ switch_core_session_thread_launch(session);
+ } else {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Cannot Create new Inbound Channel!\n");
+ }
+
+
+ return 0;
+
+
+
+}
+
+static void destroy_call_by_event(eXosip_event_t *event)
+{
+ struct private_object *tech_pvt;
+ switch_channel *channel = NULL;
+
+ if (!(tech_pvt = get_pvt_by_call_id(event->cid))) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Um in case you are interested, Can't find the pvt [%d]!\n", event->cid);
+ return;
+ }
+
+ channel = switch_core_session_get_channel(tech_pvt->session);
+ assert(channel != NULL);
+
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "destroy %s\n", switch_channel_get_name(channel));
+ exosip_kill_channel(tech_pvt->session, SWITCH_SIG_KILL);
+ switch_channel_hangup(channel);
+
+}
+
+static switch_status parse_sdp_media(sdp_media_t *media, char **dname, char **drate, char **dpayload)
+{
+ int pos = 0;
+ sdp_attribute_t *attr = NULL;
+ char *name, *rate, *payload;
+ switch_status status = SWITCH_STATUS_GENERR;
+
+ while (osip_list_eol(media->a_attributes, pos) == 0) {
+ attr = (sdp_attribute_t *)osip_list_get(media->a_attributes, pos);
+ if (attr != NULL && strcasecmp(attr->a_att_field, "rtpmap") == 0) {
+ payload = attr->a_att_value;
+ if ((name = strchr(payload, ' '))) {
+ *(name++) = '\0';
+ /* Name and payload are required */
+ *dpayload = strdup(payload);
+ status = SWITCH_STATUS_SUCCESS;
+ if ((rate = strchr(name, '/'))) {
+ *(rate++) = '\0';
+ *drate = strdup(rate);
+ *dname = strdup(name);
+ } else {
+ *dname = strdup(name);
+ *drate = strdup("8000");
+ }
+ } else {
+ *dpayload = strdup("10");
+ *dname = strdup("L16");
+ *drate = strdup("8000");
+ }
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Found negotiated codec Payload: %s Name: %s Rate: %s\n", *dpayload, *dname, *drate);
+ break;
+ }
+ attr = NULL;
+ pos++;
+ }
+
+ return status;
+}
+
+static void handle_answer(eXosip_event_t *event)
+{
+ osip_message_t *ack = NULL;
+ sdp_message_t *remote_sdp = NULL;
+ sdp_connection_t *conn = NULL;
+ sdp_media_t *remote_med = NULL;
+ struct private_object *tech_pvt;
+ char *dpayload = NULL, *dname = NULL, *drate = NULL;
+ switch_channel *channel;
+
+
+ if (!(tech_pvt = get_pvt_by_call_id(event->cid))) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Um in case you are interested, Can't find the pvt!\n");
+ return;
+ }
+
+ channel = switch_core_session_get_channel(tech_pvt->session);
+ assert(channel != NULL);
+
+ if (!event->response) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Someone answered... with no SDP information - WTF?!?\n");
+ switch_channel_hangup(channel);
+ return;
+ }
+
+ /* Get all of the remote SDP elements... stuff */
+ if (!(remote_sdp = eXosip_get_sdp_info(event->response))) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Cant Find SDP?\n");
+ switch_channel_hangup(channel);
+ return;
+ }
+
+
+ conn = eXosip_get_audio_connection(remote_sdp);
+ remote_med = eXosip_get_audio_media(remote_sdp);
+
+ /* Grab IP/port */
+ tech_pvt->remote_sdp_audio_port = atoi(remote_med->m_port);
+ snprintf(tech_pvt->remote_sdp_audio_ip, 50, conn->c_addr);
+
+ /* Grab codec elements */
+ if (parse_sdp_media(remote_med, &dname, &drate, &dpayload) == SWITCH_STATUS_SUCCESS) {
+ tech_pvt->payload_num = atoi(dpayload);
+ }
+
+ /* Assign them thar IDs */
+ tech_pvt->did = event->did;
+ tech_pvt->tid = event->tid;
+
+
+ if (1) {
+ int rate = atoi(drate);
+
+
+ if (switch_core_codec_init(&tech_pvt->read_codec, dname, rate, globals.codec_ms, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL) != SWITCH_STATUS_SUCCESS) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Can't load codec?\n");
+ switch_channel_hangup(channel);
+ return;
+ } else {
+ if (switch_core_codec_init(&tech_pvt->write_codec, dname, rate, globals.codec_ms, SWITCH_CODEC_FLAG_ENCODE |SWITCH_CODEC_FLAG_DECODE, NULL) != SWITCH_STATUS_SUCCESS) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Can't load codec?\n");
+ switch_channel_hangup(channel);
+ return;
+ } else {
+ int ms;
+ switch_set_flag(tech_pvt, TFLAG_USING_CODEC);
+ ms = tech_pvt->write_codec.implementation->nanoseconds_per_frame / 1000;
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Activate Outbound 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);
+ }
+ }
+ }
+
+
+ eXosip_lock();
+ eXosip_call_build_ack(event->did, &ack);
+ eXosip_call_send_ack(event->did, ack);
+ eXosip_unlock();
+
+ free(dname);
+ free(drate);
+ free(dpayload);
+
+
+
+ channel = switch_core_session_get_channel(tech_pvt->session);
+ assert(channel != NULL);
+
+ switch_channel_answer(channel);
+
+}
+
+static void log_event(eXosip_event_t *je)
+{
+ char buf[100];
+
+ buf[0] = '\0';
+ if (je->type == EXOSIP_CALL_NOANSWER) {
+ snprintf (buf, 99, "<- (%i %i) No answer", je->cid, je->did);
+ } else if (je->type == EXOSIP_CALL_CLOSED) {
+ snprintf (buf, 99, "<- (%i %i) Call Closed", je->cid, je->did);
+ } else if (je->type == EXOSIP_CALL_RELEASED) {
+ snprintf (buf, 99, "<- (%i %i) Call released", je->cid, je->did);
+ } else if (je->type == EXOSIP_MESSAGE_NEW
+ && je->request!=NULL && MSG_IS_MESSAGE(je->request)) {
+ char *tmp = NULL;
+
+ if (je->request != NULL) {
+ osip_body_t *body;
+ osip_from_to_str (je->request->from, &tmp);
+
+ osip_message_get_body (je->request, 0, &body);
+ if (body != NULL && body->body != NULL) {
+ snprintf (buf, 99, "<- (%i) from: %s TEXT: %s",
+ je->tid, tmp, body->body);
+ }
+ osip_free (tmp);
+ } else {
+ snprintf (buf, 99, "<- (%i) New event for unknown request?", je->tid);
+ }
+ } else if (je->type == EXOSIP_MESSAGE_NEW) {
+ char *tmp = NULL;
+
+ osip_from_to_str (je->request->from, &tmp);
+ snprintf (buf, 99, "<- (%i) %s from: %s",
+ je->tid, je->request->sip_method, tmp);
+ osip_free (tmp);
+ } else if (je->type == EXOSIP_MESSAGE_PROCEEDING
+ || je->type == EXOSIP_MESSAGE_ANSWERED
+ || je->type == EXOSIP_MESSAGE_REDIRECTED
+ || je->type == EXOSIP_MESSAGE_REQUESTFAILURE
+ || je->type == EXOSIP_MESSAGE_SERVERFAILURE
+ || je->type == EXOSIP_MESSAGE_GLOBALFAILURE) {
+ if (je->response != NULL && je->request != NULL) {
+ char *tmp = NULL;
+
+ osip_to_to_str (je->request->to, &tmp);
+ snprintf (buf, 99, "<- (%i) [%i %s for %s] to: %s",
+ je->tid, je->response->status_code,
+ je->response->reason_phrase, je->request->sip_method, tmp);
+ osip_free (tmp);
+ } else if (je->request != NULL) {
+ snprintf (buf, 99, "<- (%i) Error for %s request",
+ je->tid, je->request->sip_method);
+ } else {
+ snprintf (buf, 99, "<- (%i) Error for unknown request", je->tid);
+ }
+ } else if (je->response == NULL && je->request != NULL && je->cid > 0) {
+ char *tmp = NULL;
+
+ osip_from_to_str (je->request->from, &tmp);
+ snprintf (buf, 99, "<- (%i %i) %s from: %s",
+ je->cid, je->did, je->request->cseq->method, tmp);
+ osip_free (tmp);
+ } else if (je->response != NULL && je->cid > 0) {
+ char *tmp = NULL;
+
+ osip_to_to_str (je->request->to, &tmp);
+ snprintf (buf, 99, "<- (%i %i) [%i %s] for %s to: %s",
+ je->cid, je->did, je->response->status_code,
+ je->response->reason_phrase, je->request->sip_method, tmp);
+ osip_free (tmp);
+ } else if (je->response == NULL && je->request != NULL && je->rid > 0) {
+ char *tmp = NULL;
+
+ osip_from_to_str (je->request->from, &tmp);
+ snprintf (buf, 99, "<- (%i) %s from: %s",
+ je->rid, je->request->cseq->method, tmp);
+ osip_free (tmp);
+ } else if (je->response != NULL && je->rid > 0) {
+ char *tmp = NULL;
+
+ osip_from_to_str (je->request->from, &tmp);
+ snprintf (buf, 99, "<- (%i) [%i %s] from: %s",
+ je->rid, je->response->status_code,
+ je->response->reason_phrase, tmp);
+ osip_free (tmp);
+ } else if (je->response == NULL && je->request != NULL && je->sid > 0) {
+ char *tmp = NULL;
+ char *stat = NULL;
+ osip_header_t *sub_state;
+
+ osip_message_header_get_byname (je->request, "subscription-state",
+ 0, &sub_state);
+ if (sub_state != NULL && sub_state->hvalue != NULL)
+ stat = sub_state->hvalue;
+
+ osip_uri_to_str (je->request->from->url, &tmp);
+ snprintf (buf, 99, "<- (%i) [%s] %s from: %s",
+ je->sid, stat, je->request->cseq->method, tmp);
+ osip_free (tmp);
+ } else if (je->response != NULL && je->sid > 0) {
+ char *tmp = NULL;
+
+ osip_uri_to_str (je->request->to->url, &tmp);
+ snprintf (buf, 99, "<- (%i) [%i %s] from: %s",
+ je->sid, je->response->status_code,
+ je->response->reason_phrase, tmp);
+ osip_free (tmp);
+ } else if (je->response == NULL && je->request != NULL) {
+ char *tmp = NULL;
+
+ osip_from_to_str (je->request->from, &tmp);
+ snprintf (buf, 99, "<- (c=%i|d=%i|s=%i|n=%i) %s from: %s",
+ je->cid, je->did, je->sid, je->nid,
+ je->request->sip_method, tmp);
+ osip_free (tmp);
+ } else if (je->response != NULL) {
+ char *tmp = NULL;
+
+ osip_from_to_str (je->request->from, &tmp);
+ snprintf (buf, 99, "<- (c=%i|d=%i|s=%i|n=%i) [%i %s] for %s from: %s",
+ je->cid, je->did, je->sid, je->nid,
+ je->response->status_code, je->response->reason_phrase,
+ je->request->sip_method, tmp);
+ osip_free (tmp);
+ } else {
+ snprintf (buf, 99, "<- (c=%i|d=%i|s=%i|n=%i|t=%i) %s",
+ je->cid, je->did, je->sid, je->nid, je->tid, je->textinfo);
+ }
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "\n%s\n", buf);
+ /* Print it out */
+}
+
+
+
+static void *monitor_thread_run(void)
+{
+ eXosip_event_t *event = NULL;
+
+ globals.running = 1;
+ while (globals.running > 0) {
+ if (!(event = eXosip_event_wait(0,100))) {
+ switch_yield(100);
+ continue;
+ }
+
+ eXosip_lock();
+ eXosip_automatic_action ();
+ eXosip_unlock();
+
+ log_event(event);
+
+ switch(event->type) {
+ case EXOSIP_CALL_INVITE:
+ exosip_create_call(event);
+ break;
+ case EXOSIP_CALL_REINVITE:
+ /* See what the reinvite is about - on hold or whatever */
+ //handle_reinvite(event);
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Got a reinvite.\n");
+ break;
+ case EXOSIP_CALL_MESSAGE_NEW:
+ if (event->request != NULL && MSG_IS_REFER(event->request)) {
+ //handle_call_transfer(event);
+ }
+ break;
+ case EXOSIP_CALL_ACK:
+ /* If audio is not flowing and this has SDP - fire it up! */
+ break;
+ case EXOSIP_CALL_ANSWERED:
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "The call was answered.\n");
+ handle_answer(event);
+ break;
+ case EXOSIP_CALL_PROCEEDING:
+ /* This is like a 100 Trying... yeah */
+ break;
+ case EXOSIP_CALL_RINGING:
+ //handle_ringing(event);
+ break;
+ case EXOSIP_CALL_REDIRECTED:
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Call was redirect\n");
+ break;
+ case EXOSIP_CALL_CLOSED:
+ destroy_call_by_event(event);
+ break;
+ case EXOSIP_CALL_RELEASED:
+ destroy_call_by_event(event);
+ break;
+ case EXOSIP_CALL_NOANSWER:
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "The call was not answered.\n");
+ destroy_call_by_event(event);
+ break;
+ case EXOSIP_CALL_REQUESTFAILURE:
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Request failure\n");
+ destroy_call_by_event(event);
+ break;
+ case EXOSIP_CALL_SERVERFAILURE:
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Server failure\n");
+ destroy_call_by_event(event);
+ break;
+ case EXOSIP_CALL_GLOBALFAILURE:
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Global failure\n");
+ destroy_call_by_event(event);
+ break;
+ /* Registration related stuff */
+ case EXOSIP_REGISTRATION_NEW:
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Received registration attempt\n");
+ break;
+ default:
+ /* Unknown event... casually absorb it for now */
+ break;
+ }
+
+ //switch_console_printf(SWITCH_CHANNEL_CONSOLE, "There was an event (%d) [%s]\n", event->type, event->textinfo);
+ /* Free the event */
+ eXosip_event_free(event);
+ }
+
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Monitor Thread Exiting\n");
+ globals.running = 0;
+ return NULL;
+}
+
+
+static int config_exosip(int reload)
+{
+ switch_config cfg;
+ char *var, *val;
+ char *cf = "exosip.conf";
+
+ globals.bytes_per_frame = DEFAULT_BYTES_PER_FRAME;
+ switch_core_hash_init(&globals.call_hash, module_pool);
+
+ if (!switch_config_open_file(&cfg, cf)) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "open of %s failed\n", cf);
+ return SWITCH_STATUS_TERM;
+ }
+
+ globals.rtp_start = 16384;
+ globals.rtp_end = 32768;
+
+ 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, "dialplan")) {
+ set_global_dialplan(val);
+ } else if (!strcmp(var, "rtp_min_port")) {
+ globals.rtp_start = atoi(val);
+ } else if (!strcmp(var, "rtp_max_port")) {
+ globals.rtp_end = atoi(val);
+ } else if (!strcmp(var, "codec_ms")) {
+ globals.codec_ms = atoi(val);
+ }
+ }
+ }
+
+ if (!globals.codec_ms) {
+ globals.codec_ms = 20;
+ }
+
+ if (!globals.port) {
+ globals.port = 5060;
+ }
+
+ switch_config_close_file(&cfg);
+
+ if (!globals.dialplan) {
+ set_global_dialplan("default");
+ }
+
+ if (eXosip_init ()) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "eXosip_init initialization failed!\n");
+ return SWITCH_STATUS_GENERR;
+ }
+ if (eXosip_listen_addr (IPPROTO_UDP, NULL, globals.port, AF_INET, 0)) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "eXosip_listen_addr failed!\n");
+ return SWITCH_STATUS_GENERR;
+ }
+
+ switch_mutex_init(&globals.port_lock, SWITCH_MUTEX_NESTED, module_pool);
+
+
+ /* Setup the user agent */
+ eXosip_set_user_agent("OPENSWITCH 2.0");
+
+
+ monitor_thread_run();
+ return 0;
+
+}
+
+
+SWITCH_MOD_DECLARE(switch_status) switch_module_runtime(void)
+{
+ config_exosip(0);
+ return SWITCH_STATUS_TERM;
+}
+
--- /dev/null
+/*
+ * 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_exosip.c -- eXoSIP SIP Endpoint
+ *
+ */
+
+
+#define HAVE_APR
+
+#include <config_unix.h>
+#include <config_win32.h>
+
+#include <switch.h>
+
+#include <debug.h>
+#include <memory.h>
+#include <rtp.h>
+
+#include <eXosip2/eXosip.h>
+#include <osip2/osip_mt.h>
+#include <osipparser2/osip_rfc3264.h>
+#include <osipparser2/osip_port.h>
+
+
+
+//#include <sys/queue.h>
+//#include <ctype.h>
+//#include <string.h>
+//#include <byteswap.h>
+//#include <uclconf.h>
+
+static const char modname[] = "mod_exosip";
+#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_IO = (1 << 0),
+ TFLAG_INBOUND = (1 << 1),
+ TFLAG_OUTBOUND = (1 << 2),
+ TFLAG_DTMF = (1 << 3),
+ TFLAG_READING = (1 << 4),
+ TFLAG_WRITING = (1 << 5),
+ TFLAG_USING_CODEC = (1 << 6),
+ TFLAG_RTP = (1 << 7),
+ TFLAG_BYE = (1 << 8)
+} TFLAGS;
+
+
+#define PACKET_LEN 160
+#define DEFAULT_BYTES_PER_FRAME 160
+
+
+static const switch_endpoint_interface exosip_endpoint_interface;
+
+static struct {
+ int debug;
+ int bytes_per_frame;
+ char *dialplan;
+ int port;
+ int rtp_start;
+ int rtp_end;
+ switch_hash *call_hash;
+ switch_mutex_t *port_lock;
+ int running;
+ int codec_ms;
+} globals;
+
+struct private_object {
+ unsigned int flags;
+ switch_core_session *session;
+ switch_frame read_frame;
+ switch_codec read_codec;
+ switch_codec write_codec;
+ unsigned char read_buf[RTP_MAX_PACKET_LEN];
+ switch_caller_profile *caller_profile;
+ int cid;
+ int did;
+ int tid;
+ int32_t timestamp_send;
+ int32_t timestamp_recv;
+ int payload_num;
+ struct rtp *rtp_session;
+ struct osip_rfc3264 *sdp_config;
+ sdp_message_t *remote_sdp;
+ sdp_message_t *local_sdp;
+ char remote_sdp_audio_ip[50];
+ int remote_sdp_audio_port;
+ char local_sdp_audio_ip[50];
+ int local_sdp_audio_port;
+ char call_id[50];
+ int ssrc;
+ //switch_mutex_t *rtp_lock;
+};
+
+
+static int next_rtp_port(void)
+{
+ int port;
+
+ switch_mutex_lock(globals.port_lock);
+ port = globals.rtp_start;
+ globals.rtp_start += 2;
+ if (port >= globals.rtp_end) {
+ port = globals.rtp_start;
+ }
+ switch_mutex_unlock(globals.port_lock);
+ return port;
+}
+
+
+static void set_global_dialplan(char *dialplan)
+{
+ if (globals.dialplan) {
+ free(globals.dialplan);
+ globals.dialplan = NULL;
+ }
+
+ globals.dialplan = strdup(dialplan);
+}
+
+static void set_global_dialplan(char *dialplan);
+static switch_status exosip_on_init(switch_core_session *session);
+static switch_status exosip_on_hangup(switch_core_session *session);
+static switch_status exosip_on_loopback(switch_core_session *session);
+static switch_status exosip_on_transmit(switch_core_session *session);
+static switch_status exosip_outgoing_channel(switch_core_session *session, switch_caller_profile *outbound_profile, switch_core_session **new_session);
+static switch_status exosip_read_frame(switch_core_session *session, switch_frame **frame, int timeout, switch_io_flag flags);
+static switch_status exosip_write_frame(switch_core_session *session, switch_frame *frame, int timeout, switch_io_flag flags);
+static int config_exosip(int reload);
+static switch_status parse_sdp_media(sdp_media_t *media, char **dname, char **drate, char **dpayload);
+static switch_status exosip_kill_channel(switch_core_session *session, int sig);
+static void activate_rtp(struct private_object *tech_pvt);
+static void deactivate_rtp(struct private_object *tech_pvt);
+
+static struct private_object *get_pvt_by_call_id(int id)
+{
+ char name[50];
+ snprintf(name, sizeof(name), "%d", id);
+ return (struct private_object *) switch_core_hash_find(globals.call_hash, name);
+}
+
+static switch_status exosip_on_execute(switch_core_session *session)
+{
+ return SWITCH_STATUS_SUCCESS;
+}
+
+
+static int sdp_add_codec(struct osip_rfc3264 *cnf, int codec_type, int payload, char *attribute, int rate, int index)
+{
+ char tmp[4] = "", string[32] = "";
+ sdp_media_t *med = NULL;
+ sdp_attribute_t *attr = NULL;
+
+ sdp_media_init(&med);
+ if (med == NULL)
+ return -1;
+
+ if (!index) {
+ snprintf(tmp, sizeof(tmp), "%i", payload);
+ med->m_proto = osip_strdup("RTP/AVP");
+ osip_list_add(med->m_payloads, osip_strdup(tmp), -1);
+ }
+ if (attribute) {
+ sdp_attribute_init(&attr);
+ attr->a_att_field = osip_strdup("rtpmap");
+ snprintf(string, sizeof(string), "%i %s/%i", payload, attribute, rate);
+ attr->a_att_value = osip_strdup(string);
+ osip_list_add(med->a_attributes, attr, -1);
+ }
+
+ switch (codec_type) {
+ case SWITCH_CODEC_TYPE_AUDIO:
+ med->m_media = osip_strdup("audio");
+ osip_rfc3264_add_audio_media(cnf, med, -1);
+ break;
+ case SWITCH_CODEC_TYPE_VIDEO:
+ med->m_media = osip_strdup("video");
+ osip_rfc3264_add_video_media(cnf, med, -1);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+
+/*
+ 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 exosip_on_init(switch_core_session *session)
+{
+ struct private_object *tech_pvt;
+ switch_channel *channel = NULL;
+ char from_uri[512] = "", localip[128] = "", port[7] = "", *buf = NULL, tmp[512] = "";
+ osip_message_t *invite = 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->read_frame.data = tech_pvt->read_buf;
+
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "EXOSIP INIT\n");
+
+ if (switch_test_flag(tech_pvt, TFLAG_OUTBOUND)) {
+ char *dest_uri;
+ switch_codec_interface *codecs[512];
+ int num_codecs = 0;
+ /* do SIP Goodies...*/
+
+ /* Generate callerid URI */
+ eXosip_guess_localip(AF_INET, localip, 128);
+ snprintf(from_uri, sizeof(from_uri), "<sip:%s@%s>", tech_pvt->caller_profile->caller_id_number, localip);
+ /* Setup codec negotiation stuffs */
+ osip_rfc3264_init(&tech_pvt->sdp_config);
+ /* Decide on local IP and rtp port */
+ strncpy(tech_pvt->local_sdp_audio_ip, localip, sizeof(tech_pvt->local_sdp_audio_ip));
+ tech_pvt->local_sdp_audio_port = next_rtp_port();
+ /* Initialize SDP */
+ sdp_message_init(&tech_pvt->local_sdp);
+ sdp_message_v_version_set(tech_pvt->local_sdp, "0");
+ sdp_message_o_origin_set(tech_pvt->local_sdp, "OpenSWITCH2", "0", "0", "IN", "IP4", tech_pvt->local_sdp_audio_ip);
+ sdp_message_s_name_set(tech_pvt->local_sdp, "SIP Call");
+ sdp_message_c_connection_add(tech_pvt->local_sdp, -1, "IN", "IP4", tech_pvt->local_sdp_audio_ip, NULL, NULL);
+ sdp_message_t_time_descr_add(tech_pvt->local_sdp, "0", "0");
+ snprintf(port, sizeof(port), "%i", tech_pvt->local_sdp_audio_port);
+ sdp_message_m_media_add(tech_pvt->local_sdp, "audio", port, NULL, "RTP/AVP");
+ /* Add in every codec we support on this outbound call */
+ if ((num_codecs = loadable_module_get_codecs(switch_core_session_get_pool(session), codecs, sizeof(codecs)/sizeof(codecs[0]))) > 0) {
+ int i;
+ static const switch_codec_implementation *imp;
+ for (i = 0; i < num_codecs; i++) {
+ int x = 0;
+
+ snprintf(tmp, sizeof(tmp), "%i", codecs[i]->ianacode);
+ sdp_message_m_payload_add(tech_pvt->local_sdp, 0, osip_strdup(tmp));
+ for (imp = codecs[i]->implementations ; imp ; imp = imp->next) {
+ /* Add to SDP config */
+ sdp_add_codec(tech_pvt->sdp_config, codecs[i]->codec_type, codecs[i]->ianacode, codecs[i]->iananame, imp->samples_per_second, x++);
+ /* Add to SDP message */
+
+ snprintf(tmp, sizeof(tmp), "%i %s/%i", codecs[i]->ianacode, codecs[i]->iananame, imp->samples_per_second);
+ sdp_message_a_attribute_add(tech_pvt->local_sdp, 0, "rtpmap", osip_strdup(tmp));
+ memset(tmp, 0, sizeof(tmp));
+ }
+ }
+ }
+ /* Setup our INVITE */
+ eXosip_lock();
+ if (!(dest_uri = (char *) switch_core_session_alloc(session, strlen(tech_pvt->caller_profile->destination_number) + 10))) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "AIEEEE!\n");
+ assert(dest_uri != NULL);
+ }
+ sprintf(dest_uri, "sip:%s", tech_pvt->caller_profile->destination_number);
+ eXosip_call_build_initial_invite(&invite, dest_uri, from_uri, NULL, NULL);
+ osip_message_set_supported(invite, "100rel, replaces");
+ /* Add SDP to the INVITE */
+ sdp_message_to_str(tech_pvt->local_sdp, &buf);
+ osip_message_set_body(invite, buf, strlen(buf));
+ osip_message_set_content_type(invite, "application/sdp");
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "OUTBOUND SDP:\n%s\n", buf);
+ free(buf);
+ /* Send the INVITE */
+ tech_pvt->cid = eXosip_call_send_initial_invite(invite);
+ snprintf(tech_pvt->call_id, sizeof(tech_pvt->call_id), "%d", tech_pvt->cid);
+ switch_core_hash_insert(globals.call_hash, tech_pvt->call_id, tech_pvt);
+ tech_pvt->did = -1;
+ eXosip_unlock();
+ }
+
+
+ /* Let Media Work */
+ 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 exosip_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, "EXOSIP RING\n");
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status exosip_on_hangup(switch_core_session *session)
+{
+ struct private_object *tech_pvt;
+ switch_channel *channel = NULL;
+ int i;
+
+ channel = switch_core_session_get_channel(session);
+ assert(channel != NULL);
+
+ tech_pvt = switch_core_session_get_private(session);
+ assert(tech_pvt != NULL);
+
+
+ switch_core_hash_delete(globals.call_hash, tech_pvt->call_id);
+
+
+ switch_set_flag(tech_pvt, TFLAG_BYE);
+ switch_clear_flag(tech_pvt, TFLAG_IO);
+
+ deactivate_rtp(tech_pvt);
+
+ i = eXosip_call_terminate(tech_pvt->cid, tech_pvt->did);
+
+ if (switch_test_flag(tech_pvt, TFLAG_USING_CODEC)) {
+ switch_core_codec_destroy(&tech_pvt->read_codec);
+ switch_core_codec_destroy(&tech_pvt->write_codec);
+ }
+
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "EXOSIP HANGUP %s %d/%d=%d\n", switch_channel_get_name(channel), tech_pvt->cid, tech_pvt->did, i);
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status exosip_on_loopback(switch_core_session *session)
+{
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "EXOSIP LOOPBACK\n");
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status exosip_on_transmit(switch_core_session *session)
+{
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "EXOSIP TRANSMIT\n");
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status exosip_outgoing_channel(switch_core_session *session, switch_caller_profile *outbound_profile, switch_core_session **new_session)
+{
+ if ((*new_session = switch_core_session_request(&exosip_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);
+ //switch_mutex_init(&tech_pvt->rtp_lock, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(*new_session));
+ 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), "Exosip/%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;
+}
+
+#if 0
+static const char *event_names[] = {
+ "RX_RTP",
+ "RX_SR",
+ "RX_RR",
+ "RX_SDES",
+ "RX_BYE",
+ "SOURCE_CREATED",
+ "SOURCE_DELETED",
+ "RX_RR_EMPTY",
+ "RX_RTCP_START",
+ "RX_RTCP_FINISH",
+ "RR_TIMEOUT",
+ "RX_APP"
+};
+#endif
+
+static void rtp_event_handler(struct rtp *session, rtp_event *event)
+{
+ rtp_packet *packet;
+ struct private_object *tech_pvt = (struct private_object *) rtp_get_userdata(session);
+
+ if (!session) {
+ return;
+ }
+
+
+ //switch_console_printf(SWITCH_CHANNEL_CONSOLE, "RTP EVENT (%s)\n", event_names[event->type]);
+
+ switch(event->type) {
+ case RX_RTP:
+ if((packet = (rtp_packet *) event->data)) {
+ if (packet->data_len < 10000) {
+ memcpy(tech_pvt->read_buf, packet->data, packet->data_len);
+ tech_pvt->read_frame.datalen = packet->data_len;
+ } else {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "WTF PACKET LEN %d\n", packet->data_len);
+ }
+ free(packet); /* xfree() is mandatory to release RTP packet data */
+ }
+ break;
+ case RR_TIMEOUT:
+ case RX_BYE:
+ switch_set_flag(tech_pvt, TFLAG_BYE);
+ default:
+ break;
+ }
+}
+
+static void deactivate_rtp(struct private_object *tech_pvt)
+{
+ int loops = 0;
+ if (tech_pvt->rtp_session) {
+ //switch_mutex_lock(tech_pvt->rtp_lock);
+
+ while(loops < 10 && (switch_test_flag(tech_pvt, TFLAG_READING) || switch_test_flag(tech_pvt, TFLAG_WRITING))) {
+ switch_yield(10000);
+ loops++;
+ }
+ rtp_send_bye(tech_pvt->rtp_session);
+ rtp_done(tech_pvt->rtp_session);
+ tech_pvt->rtp_session = NULL;
+ //switch_mutex_unlock(tech_pvt->rtp_lock);
+ }
+}
+
+static void activate_rtp(struct private_object *tech_pvt)
+{
+ int bw, ms;
+ switch_channel *channel;
+
+ assert(tech_pvt != NULL);
+
+ channel = switch_core_session_get_channel(tech_pvt->session);
+ assert(channel != NULL);
+
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Activating RTP %d->%s:%d\n",
+ tech_pvt->local_sdp_audio_port,
+ tech_pvt->remote_sdp_audio_ip,
+ tech_pvt->remote_sdp_audio_port);
+
+
+ //switch_mutex_lock(tech_pvt->rtp_lock);
+ if (switch_test_flag(tech_pvt, TFLAG_USING_CODEC)) {
+ bw = tech_pvt->read_codec.implementation->bits_per_second;
+ ms = tech_pvt->read_codec.implementation->nanoseconds_per_frame / 1000;
+ } else {
+ switch_channel_get_raw_mode(channel, NULL, NULL, NULL, &ms, &bw);
+ bw *= 8;
+ }
+
+ tech_pvt->rtp_session = rtp_init(tech_pvt->remote_sdp_audio_ip, /* Host/Group IP address */
+ tech_pvt->local_sdp_audio_port, /* receive port */
+ tech_pvt->remote_sdp_audio_port, /* transmit port */
+ ms * 2, /* time-to-live */
+ bw, /* B/W estimate */
+ rtp_event_handler, /* RTP event callback */
+ (void *) tech_pvt); /* App. specific data */
+
+
+ if (tech_pvt->rtp_session) {
+ tech_pvt->ssrc = rtp_my_ssrc(tech_pvt->rtp_session);
+ rtp_set_option(tech_pvt->rtp_session, RTP_OPT_PROMISC, 1);
+ /* Set local participant info */
+ rtp_set_sdes(tech_pvt->rtp_session, tech_pvt->ssrc, RTCP_SDES_NAME, "test", 4);
+ rtp_set_sdes(tech_pvt->rtp_session, tech_pvt->ssrc, RTCP_SDES_PHONE, "test", 4);
+ rtp_set_sdes(tech_pvt->rtp_session, tech_pvt->ssrc, RTCP_SDES_TOOL, "test", 4);
+ switch_set_flag(tech_pvt, TFLAG_RTP);
+ } else {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Oh oh is your port even?\n");
+ }
+ //switch_mutex_unlock(tech_pvt->rtp_lock);
+}
+
+static switch_status exosip_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);
+
+ if (!switch_channel_test_flag(channel, CF_OUTBOUND)) {
+ char *buf = NULL;
+ osip_message_t *answer = NULL;
+
+
+ /* Transmit 200 OK with SDP */
+ eXosip_lock();
+ eXosip_call_build_answer(tech_pvt->tid, 200, &answer);
+ sdp_message_to_str(tech_pvt->local_sdp, &buf);
+ osip_message_set_body(answer, buf, strlen(buf));
+ osip_message_set_content_type(answer, "application/sdp");
+ free(buf);
+ eXosip_call_send_answer(tech_pvt->tid, 200, answer);
+ eXosip_unlock();
+ }
+
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+
+static switch_status exosip_read_frame(switch_core_session *session, switch_frame **frame, int timeout, switch_io_flag flags)
+{
+ struct private_object *tech_pvt = NULL;
+ size_t bytes = 0, samples = 0, frames, ms, x;
+ switch_channel *channel = NULL;
+ int ns=0;
+ struct timeval tv;
+
+ channel = switch_core_session_get_channel(session);
+ assert(channel != NULL);
+
+ tech_pvt = switch_core_session_get_private(session);
+ assert(tech_pvt != NULL);
+
+ tech_pvt->read_frame.datalen = 0;
+ switch_set_flag(tech_pvt, TFLAG_READING);
+
+ if (switch_test_flag(tech_pvt, TFLAG_USING_CODEC)) {
+ bytes = tech_pvt->read_codec.implementation->encoded_bytes_per_frame;
+ samples = tech_pvt->read_codec.implementation->samples_per_frame;
+ } else {
+ assert(0);
+ }
+
+ if (timeout > 0) {
+ ns = timeout * 10000;
+ } else {
+ ns = tech_pvt->read_codec.implementation->nanoseconds_per_frame;
+ }
+
+ if (switch_test_flag(tech_pvt, TFLAG_IO)) {
+ if (!switch_test_flag(tech_pvt, TFLAG_RTP)) {
+ activate_rtp(tech_pvt);
+ }
+
+ assert(tech_pvt->rtp_session != NULL);
+ tech_pvt->read_frame.datalen = 0;
+ //switch_mutex_lock(tech_pvt->rtp_lock);
+ tv.tv_sec = 0;
+ tv.tv_usec = ns;
+ while(switch_test_flag(tech_pvt, TFLAG_IO) && !switch_test_flag(tech_pvt, TFLAG_BYE)) {
+ x = rtp_recv(tech_pvt->rtp_session, &tv, tech_pvt->timestamp_recv);
+ if(x < 0) {
+ break;
+ }
+ if (tech_pvt->read_frame.datalen) {
+ bytes = tech_pvt->read_codec.implementation->encoded_bytes_per_frame;
+ frames = (tech_pvt->read_frame.datalen / bytes);
+ samples = frames * tech_pvt->read_codec.implementation->samples_per_frame;
+ ms = frames * tech_pvt->read_codec.implementation->nanoseconds_per_frame / 1000;
+ break;
+ }
+ }
+ tech_pvt->timestamp_recv += (int)samples;
+ //printf("%s\trecv %d bytes %d samples in %d frames taking up %d ms ts=%d\n", switch_channel_get_name(channel), tech_pvt->read_frame.datalen, samples, frames, ms, tech_pvt->timestamp_recv);
+ //switch_mutex_unlock(tech_pvt->rtp_lock);
+
+ } else {
+ memset(tech_pvt->read_buf, 0, 160);
+ tech_pvt->read_frame.datalen = 160;
+ }
+
+ switch_clear_flag(tech_pvt, TFLAG_READING);
+
+ if (switch_test_flag(tech_pvt, TFLAG_BYE)) {
+ switch_channel_hangup(channel);
+ return SWITCH_STATUS_FALSE;
+ }
+
+ *frame = &tech_pvt->read_frame;
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+
+static switch_status exosip_write_frame(switch_core_session *session, switch_frame *frame, int timeout, switch_io_flag flags)
+{
+ struct private_object *tech_pvt;
+ switch_channel *channel = NULL;
+ switch_status status = SWITCH_STATUS_SUCCESS;
+ size_t x, bytes, samples, ms, frames;
+
+ 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_RTP)) {
+ activate_rtp(tech_pvt);
+ }
+
+ if (!switch_test_flag(tech_pvt, TFLAG_IO)) {
+ return SWITCH_STATUS_SUCCESS;
+ }
+
+ if (switch_test_flag(tech_pvt, TFLAG_BYE)) {
+ switch_channel_hangup(channel);
+ return SWITCH_STATUS_FALSE;
+ }
+
+ switch_set_flag(tech_pvt, TFLAG_WRITING);
+ //switch_mutex_lock(tech_pvt->rtp_lock);
+
+ if (switch_test_flag(tech_pvt, TFLAG_USING_CODEC)) {
+ bytes = tech_pvt->read_codec.implementation->encoded_bytes_per_frame;
+ frames = (frame->datalen / bytes);
+ samples = frames * tech_pvt->read_codec.implementation->samples_per_frame;
+ ms = frames * tech_pvt->read_codec.implementation->nanoseconds_per_frame / 1000;
+ } else {
+ assert(0);
+ }
+ tech_pvt->timestamp_send += (int)samples;
+
+
+ //printf("%s %s->%s send %d bytes %d samples in %d frames taking up %d ms ts=%d\n", switch_channel_get_name(channel), tech_pvt->local_sdp_audio_ip, tech_pvt->remote_sdp_audio_ip, frame->datalen, samples, frames, ms, tech_pvt->timestamp_recv);
+
+ rtp_send_ctrl(tech_pvt->rtp_session, tech_pvt->timestamp_send, NULL);
+ x = rtp_send_data(tech_pvt->rtp_session, tech_pvt->timestamp_send, tech_pvt->payload_num,
+ 0, 0, 0,
+ (char *)frame->data, (int)frame->datalen,
+ 0, 0, 0);
+
+
+ switch_clear_flag(tech_pvt, TFLAG_WRITING);
+ //switch_mutex_unlock(tech_pvt->rtp_lock);
+ return status;
+}
+
+
+
+static switch_status exosip_kill_channel(switch_core_session *session, int sig)
+{
+ 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_clear_flag(tech_pvt, TFLAG_IO);
+ switch_set_flag(tech_pvt, TFLAG_BYE);
+ //rtp_kill(tech_pvt->rtp_session);
+
+ return SWITCH_STATUS_SUCCESS;
+
+}
+
+static switch_status exosip_waitfor_read(switch_core_session *session, int ms)
+{
+ 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 switch_status exosip_waitfor_write(switch_core_session *session, int ms)
+{
+ 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 const switch_io_routines exosip_io_routines = {
+ /*.outgoing_channel*/ exosip_outgoing_channel,
+ /*.answer_channel*/ exosip_answer_channel,
+ /*.read_frame*/ exosip_read_frame,
+ /*.write_frame*/ exosip_write_frame,
+ /*.kill_channel*/ exosip_kill_channel,
+ /*.waitfor_read*/ exosip_waitfor_read,
+ /*.waitfor_read*/ exosip_waitfor_write
+};
+
+static const switch_event_handler_table exosip_event_handlers = {
+ /*.on_init*/ exosip_on_init,
+ /*.on_ring*/ exosip_on_ring,
+ /*.on_execute*/ exosip_on_execute,
+ /*.on_hangup*/ exosip_on_hangup,
+ /*.on_loopback*/ exosip_on_loopback,
+ /*.on_transmit*/ exosip_on_transmit
+};
+
+static const switch_endpoint_interface exosip_endpoint_interface = {
+ /*.interface_name*/ "exosip",
+ /*.io_routines*/ &exosip_io_routines,
+ /*.event_handlers*/ &exosip_event_handlers,
+ /*.private*/ NULL,
+ /*.next*/ NULL
+};
+
+static const switch_loadable_module_interface exosip_module_interface = {
+ /*.module_name*/ modname,
+ /*.endpoint_interface*/ &exosip_endpoint_interface,
+ /*.timer_interface*/ NULL,
+ /*.dialplan_interface*/ NULL,
+ /*.codec_interface*/ NULL,
+ /*.application_interface*/ NULL
+};
+
+
+SWITCH_MOD_DECLARE(switch_status) switch_module_shutdown(void)
+{
+ if (globals.running) {
+ globals.running = -1;
+ while(globals.running) {
+ switch_yield(1000);
+ }
+ }
+ eXosip_quit();
+ return SWITCH_STATUS_SUCCESS;
+}
+
+SWITCH_MOD_DECLARE(switch_status) switch_module_load(const switch_loadable_module_interface **_interface, char *filename) {
+ /* NOTE: **interface is **_interface because the common lib redefines interface to struct in some situations */
+
+ 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 = &exosip_module_interface;
+
+ /* indicate that the module should continue to be loaded */
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status exosip_create_call(eXosip_event_t *event)
+{
+ switch_core_session *session;
+ sdp_message_t *remote_sdp = NULL;
+ sdp_connection_t *conn = NULL;
+ sdp_media_t *remote_med = NULL, *audio_tab[10], *video_tab[10], *t38_tab[10], *app_tab[10];
+ char local_sdp_str[8192] = "", port[8] = "";
+ int mline = 0, pos = 0;
+ switch_channel *channel = NULL;
+ char name[128];
+ char *dpayload, *dname, *drate;
+ char *remote_sdp_str = NULL;
+
+ if ((session = switch_core_session_request(&exosip_endpoint_interface, NULL))) {
+ struct private_object *tech_pvt;
+ switch_codec_interface *codecs[512];
+ int num_codecs = 0;
+
+
+ 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;
+ //switch_mutex_init(&tech_pvt->rtp_lock, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session));
+ } else {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Hey where is my memory pool?\n");
+ switch_core_session_destroy(&session);
+ return SWITCH_STATUS_MEMERR;
+ }
+
+ if ((tech_pvt->caller_profile = switch_caller_profile_new(session,
+ globals.dialplan,
+ event->request->from->displayname,
+ event->request->from->url->username,
+ event->request->from->url->username,
+ NULL,
+ event->request->req_uri->username))) {
+ switch_channel_set_caller_profile(channel, tech_pvt->caller_profile);
+ }
+
+ switch_set_flag(tech_pvt, TFLAG_INBOUND);
+ tech_pvt->did = event->did;
+ tech_pvt->cid = event->cid;
+ tech_pvt->tid = event->tid;
+
+ snprintf(name, sizeof(name), "Exosip/%s-%04x", tech_pvt->caller_profile->destination_number, rand() & 0xffff);
+ switch_channel_set_name(channel, name);
+
+ if (!(remote_sdp = eXosip_get_sdp_info(event->request))) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Cannot Find Remote SDP!\n");
+ exosip_on_hangup(session);
+ switch_core_session_destroy(&session);
+ return SWITCH_STATUS_GENERR;
+ }
+
+ eXosip_guess_localip(AF_INET, tech_pvt->local_sdp_audio_ip, 50);
+ tech_pvt->local_sdp_audio_port = next_rtp_port();
+ osip_rfc3264_init(&tech_pvt->sdp_config);
+ /* Add in what codecs we support locally */
+
+ if ((num_codecs = loadable_module_get_codecs(switch_core_session_get_pool(session), codecs, sizeof(codecs)/sizeof(codecs[0]))) > 0) {
+ int i;
+ static const switch_codec_implementation *imp;
+
+ for (i = 0; i < num_codecs; i++) {
+ int x = 0;
+ for (imp = codecs[i]->implementations ; imp ; imp = imp->next) {
+ sdp_add_codec(tech_pvt->sdp_config, codecs[i]->codec_type, codecs[i]->ianacode, codecs[i]->iananame, imp->samples_per_second, x++);
+ }
+ }
+ }
+ osip_rfc3264_prepare_answer(tech_pvt->sdp_config, remote_sdp, local_sdp_str, 8192);
+ sdp_message_init(&tech_pvt->local_sdp);
+ sdp_message_parse(tech_pvt->local_sdp, local_sdp_str);
+
+ sdp_message_to_str(remote_sdp, &remote_sdp_str);
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "LOCAL SDP:\n%s\nREMOTE SDP:\n%s", local_sdp_str,remote_sdp_str);
+
+ mline = 0;
+ while (0==osip_rfc3264_match(tech_pvt->sdp_config, remote_sdp, audio_tab, video_tab, t38_tab, app_tab, mline)) {
+ if (audio_tab[0] == NULL && video_tab[0] == NULL && t38_tab[0] == NULL && app_tab[0] == NULL) {
+
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Got no compatible codecs!\n");
+ break;
+ }
+ for (pos=0; audio_tab[pos]!=NULL; pos++) {
+ osip_rfc3264_complete_answer(tech_pvt->sdp_config, remote_sdp, tech_pvt->local_sdp, audio_tab[pos], mline);
+ if (parse_sdp_media(audio_tab[pos], &dname, &drate, &dpayload) == SWITCH_STATUS_SUCCESS) {
+ tech_pvt->payload_num = atoi(dpayload);
+ break;
+ }
+ }
+ mline++;
+ }
+ free(remote_sdp_str);
+ sdp_message_o_origin_set(tech_pvt->local_sdp, "OpenSWITCH2", "0", "0", "IN", "IP4", tech_pvt->local_sdp_audio_ip);
+ sdp_message_s_name_set(tech_pvt->local_sdp, "SIP Call");
+ sdp_message_c_connection_add(tech_pvt->local_sdp, -1, "IN", "IP4", tech_pvt->local_sdp_audio_ip, NULL, NULL);
+ snprintf(port, sizeof(port), "%i", tech_pvt->local_sdp_audio_port);
+ sdp_message_m_port_set(tech_pvt->local_sdp, 0, osip_strdup(port));
+
+ conn = eXosip_get_audio_connection(remote_sdp);
+ remote_med = eXosip_get_audio_media(remote_sdp);
+ snprintf(tech_pvt->remote_sdp_audio_ip, 50, conn->c_addr);
+
+ tech_pvt->remote_sdp_audio_port = atoi(remote_med->m_port);
+
+ snprintf(tech_pvt->call_id, sizeof(tech_pvt->call_id), "%d", event->cid);
+ switch_core_hash_insert(globals.call_hash, tech_pvt->call_id, tech_pvt);
+
+ if (!dname) {
+ exosip_on_hangup(session);
+ switch_core_session_destroy(&session);
+ return SWITCH_STATUS_GENERR;
+ }
+
+ switch_channel_set_state(channel, CS_INIT);
+
+
+ if (1) {
+ int rate = atoi(drate);
+
+ if (switch_core_codec_init(&tech_pvt->read_codec,
+ dname,
+ rate,
+ globals.codec_ms,
+ SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE,
+ NULL) != SWITCH_STATUS_SUCCESS) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Can't load codec?\n");
+ switch_channel_hangup(channel);
+ return SWITCH_STATUS_FALSE;
+ } else {
+ if (switch_core_codec_init(&tech_pvt->write_codec,
+ dname,
+ rate,
+ globals.codec_ms,
+ SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE,
+ NULL) != SWITCH_STATUS_SUCCESS) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Can't load codec?\n");
+ switch_channel_hangup(channel);
+ return SWITCH_STATUS_FALSE;
+ } else {
+ int ms;
+ switch_set_flag(tech_pvt, TFLAG_USING_CODEC);
+ ms = tech_pvt->write_codec.implementation->nanoseconds_per_frame / 1000;
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Activate Inbound Codec %s/%d %d ms\n", dname, rate, ms);
+ tech_pvt->read_frame.codec = &tech_pvt->read_codec;
+ switch_core_session_set_read_codec(session, &tech_pvt->read_codec);
+ switch_core_session_set_write_codec(session, &tech_pvt->write_codec);
+ }
+ }
+ }
+
+ switch_core_session_thread_launch(session);
+ } else {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Cannot Create new Inbound Channel!\n");
+ }
+
+
+ return 0;
+
+
+
+}
+
+static void destroy_call_by_event(eXosip_event_t *event)
+{
+ struct private_object *tech_pvt;
+ switch_channel *channel = NULL;
+
+ if (!(tech_pvt = get_pvt_by_call_id(event->cid))) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Um in case you are interested, Can't find the pvt [%d]!\n", event->cid);
+ return;
+ }
+
+ channel = switch_core_session_get_channel(tech_pvt->session);
+ assert(channel != NULL);
+
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "destroy %s\n", switch_channel_get_name(channel));
+ exosip_kill_channel(tech_pvt->session, SWITCH_SIG_KILL);
+ switch_channel_hangup(channel);
+
+}
+
+static switch_status parse_sdp_media(sdp_media_t *media, char **dname, char **drate, char **dpayload)
+{
+ int pos = 0;
+ sdp_attribute_t *attr = NULL;
+ char *name, *rate, *payload;
+ switch_status status = SWITCH_STATUS_GENERR;
+
+ while (osip_list_eol(media->a_attributes, pos) == 0) {
+ attr = (sdp_attribute_t *)osip_list_get(media->a_attributes, pos);
+ if (attr != NULL && strcasecmp(attr->a_att_field, "rtpmap") == 0) {
+ payload = attr->a_att_value;
+ if ((name = strchr(payload, ' '))) {
+ *(name++) = '\0';
+ /* Name and payload are required */
+ *dpayload = strdup(payload);
+ status = SWITCH_STATUS_SUCCESS;
+ if ((rate = strchr(name, '/'))) {
+ *(rate++) = '\0';
+ *drate = strdup(rate);
+ *dname = strdup(name);
+ } else {
+ *dname = strdup(name);
+ *drate = strdup("8000");
+ }
+ } else {
+ *dpayload = strdup("10");
+ *dname = strdup("L16");
+ *drate = strdup("8000");
+ }
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Found negotiated codec Payload: %s Name: %s Rate: %s\n", *dpayload, *dname, *drate);
+ break;
+ }
+ attr = NULL;
+ pos++;
+ }
+
+ return status;
+}
+
+static void handle_answer(eXosip_event_t *event)
+{
+ osip_message_t *ack = NULL;
+ sdp_message_t *remote_sdp = NULL;
+ sdp_connection_t *conn = NULL;
+ sdp_media_t *remote_med = NULL;
+ struct private_object *tech_pvt;
+ char *dpayload = NULL, *dname = NULL, *drate = NULL;
+ switch_channel *channel;
+
+
+ if (!(tech_pvt = get_pvt_by_call_id(event->cid))) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Um in case you are interested, Can't find the pvt!\n");
+ return;
+ }
+
+ channel = switch_core_session_get_channel(tech_pvt->session);
+ assert(channel != NULL);
+
+ if (!event->response) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Someone answered... with no SDP information - WTF?!?\n");
+ switch_channel_hangup(channel);
+ return;
+ }
+
+ /* Get all of the remote SDP elements... stuff */
+ if (!(remote_sdp = eXosip_get_sdp_info(event->response))) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Cant Find SDP?\n");
+ switch_channel_hangup(channel);
+ return;
+ }
+
+
+ conn = eXosip_get_audio_connection(remote_sdp);
+ remote_med = eXosip_get_audio_media(remote_sdp);
+
+ /* Grab IP/port */
+ tech_pvt->remote_sdp_audio_port = atoi(remote_med->m_port);
+ snprintf(tech_pvt->remote_sdp_audio_ip, 50, conn->c_addr);
+
+ /* Grab codec elements */
+ if (parse_sdp_media(remote_med, &dname, &drate, &dpayload) == SWITCH_STATUS_SUCCESS) {
+ tech_pvt->payload_num = atoi(dpayload);
+ }
+
+ /* Assign them thar IDs */
+ tech_pvt->did = event->did;
+ tech_pvt->tid = event->tid;
+
+
+ if (1) {
+ int rate = atoi(drate);
+
+
+ if (switch_core_codec_init(&tech_pvt->read_codec, dname, rate, globals.codec_ms, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL) != SWITCH_STATUS_SUCCESS) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Can't load codec?\n");
+ switch_channel_hangup(channel);
+ return;
+ } else {
+ if (switch_core_codec_init(&tech_pvt->write_codec, dname, rate, globals.codec_ms, SWITCH_CODEC_FLAG_ENCODE |SWITCH_CODEC_FLAG_DECODE, NULL) != SWITCH_STATUS_SUCCESS) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Can't load codec?\n");
+ switch_channel_hangup(channel);
+ return;
+ } else {
+ int ms;
+ switch_set_flag(tech_pvt, TFLAG_USING_CODEC);
+ ms = tech_pvt->write_codec.implementation->nanoseconds_per_frame / 1000;
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Activate Outbound 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);
+ }
+ }
+ }
+
+
+ eXosip_lock();
+ eXosip_call_build_ack(event->did, &ack);
+ eXosip_call_send_ack(event->did, ack);
+ eXosip_unlock();
+
+ free(dname);
+ free(drate);
+ free(dpayload);
+
+ channel = switch_core_session_get_channel(tech_pvt->session);
+ assert(channel != NULL);
+
+ switch_channel_answer(channel);
+
+}
+
+static void log_event(eXosip_event_t *je)
+{
+ char buf[100];
+
+ buf[0] = '\0';
+ if (je->type == EXOSIP_CALL_NOANSWER) {
+ snprintf (buf, 99, "<- (%i %i) No answer", je->cid, je->did);
+ } else if (je->type == EXOSIP_CALL_CLOSED) {
+ snprintf (buf, 99, "<- (%i %i) Call Closed", je->cid, je->did);
+ } else if (je->type == EXOSIP_CALL_RELEASED) {
+ snprintf (buf, 99, "<- (%i %i) Call released", je->cid, je->did);
+ } else if (je->type == EXOSIP_MESSAGE_NEW
+ && je->request!=NULL && MSG_IS_MESSAGE(je->request)) {
+ char *tmp = NULL;
+
+ if (je->request != NULL) {
+ osip_body_t *body;
+ osip_from_to_str (je->request->from, &tmp);
+
+ osip_message_get_body (je->request, 0, &body);
+ if (body != NULL && body->body != NULL) {
+ snprintf (buf, 99, "<- (%i) from: %s TEXT: %s",
+ je->tid, tmp, body->body);
+ }
+ osip_free (tmp);
+ } else {
+ snprintf (buf, 99, "<- (%i) New event for unknown request?", je->tid);
+ }
+ } else if (je->type == EXOSIP_MESSAGE_NEW) {
+ char *tmp = NULL;
+
+ osip_from_to_str (je->request->from, &tmp);
+ snprintf (buf, 99, "<- (%i) %s from: %s",
+ je->tid, je->request->sip_method, tmp);
+ osip_free (tmp);
+ } else if (je->type == EXOSIP_MESSAGE_PROCEEDING
+ || je->type == EXOSIP_MESSAGE_ANSWERED
+ || je->type == EXOSIP_MESSAGE_REDIRECTED
+ || je->type == EXOSIP_MESSAGE_REQUESTFAILURE
+ || je->type == EXOSIP_MESSAGE_SERVERFAILURE
+ || je->type == EXOSIP_MESSAGE_GLOBALFAILURE) {
+ if (je->response != NULL && je->request != NULL) {
+ char *tmp = NULL;
+
+ osip_to_to_str (je->request->to, &tmp);
+ snprintf (buf, 99, "<- (%i) [%i %s for %s] to: %s",
+ je->tid, je->response->status_code,
+ je->response->reason_phrase, je->request->sip_method, tmp);
+ osip_free (tmp);
+ } else if (je->request != NULL) {
+ snprintf (buf, 99, "<- (%i) Error for %s request",
+ je->tid, je->request->sip_method);
+ } else {
+ snprintf (buf, 99, "<- (%i) Error for unknown request", je->tid);
+ }
+ } else if (je->response == NULL && je->request != NULL && je->cid > 0) {
+ char *tmp = NULL;
+
+ osip_from_to_str (je->request->from, &tmp);
+ snprintf (buf, 99, "<- (%i %i) %s from: %s",
+ je->cid, je->did, je->request->cseq->method, tmp);
+ osip_free (tmp);
+ } else if (je->response != NULL && je->cid > 0) {
+ char *tmp = NULL;
+
+ osip_to_to_str (je->request->to, &tmp);
+ snprintf (buf, 99, "<- (%i %i) [%i %s] for %s to: %s",
+ je->cid, je->did, je->response->status_code,
+ je->response->reason_phrase, je->request->sip_method, tmp);
+ osip_free (tmp);
+ } else if (je->response == NULL && je->request != NULL && je->rid > 0) {
+ char *tmp = NULL;
+
+ osip_from_to_str (je->request->from, &tmp);
+ snprintf (buf, 99, "<- (%i) %s from: %s",
+ je->rid, je->request->cseq->method, tmp);
+ osip_free (tmp);
+ } else if (je->response != NULL && je->rid > 0) {
+ char *tmp = NULL;
+
+ osip_from_to_str (je->request->from, &tmp);
+ snprintf (buf, 99, "<- (%i) [%i %s] from: %s",
+ je->rid, je->response->status_code,
+ je->response->reason_phrase, tmp);
+ osip_free (tmp);
+ } else if (je->response == NULL && je->request != NULL && je->sid > 0) {
+ char *tmp = NULL;
+ char *stat = NULL;
+ osip_header_t *sub_state;
+
+ osip_message_header_get_byname (je->request, "subscription-state",
+ 0, &sub_state);
+ if (sub_state != NULL && sub_state->hvalue != NULL)
+ stat = sub_state->hvalue;
+
+ osip_uri_to_str (je->request->from->url, &tmp);
+ snprintf (buf, 99, "<- (%i) [%s] %s from: %s",
+ je->sid, stat, je->request->cseq->method, tmp);
+ osip_free (tmp);
+ } else if (je->response != NULL && je->sid > 0) {
+ char *tmp = NULL;
+
+ osip_uri_to_str (je->request->to->url, &tmp);
+ snprintf (buf, 99, "<- (%i) [%i %s] from: %s",
+ je->sid, je->response->status_code,
+ je->response->reason_phrase, tmp);
+ osip_free (tmp);
+ } else if (je->response == NULL && je->request != NULL) {
+ char *tmp = NULL;
+
+ osip_from_to_str (je->request->from, &tmp);
+ snprintf (buf, 99, "<- (c=%i|d=%i|s=%i|n=%i) %s from: %s",
+ je->cid, je->did, je->sid, je->nid,
+ je->request->sip_method, tmp);
+ osip_free (tmp);
+ } else if (je->response != NULL) {
+ char *tmp = NULL;
+
+ osip_from_to_str (je->request->from, &tmp);
+ snprintf (buf, 99, "<- (c=%i|d=%i|s=%i|n=%i) [%i %s] for %s from: %s",
+ je->cid, je->did, je->sid, je->nid,
+ je->response->status_code, je->response->reason_phrase,
+ je->request->sip_method, tmp);
+ osip_free (tmp);
+ } else {
+ snprintf (buf, 99, "<- (c=%i|d=%i|s=%i|n=%i|t=%i) %s",
+ je->cid, je->did, je->sid, je->nid, je->tid, je->textinfo);
+ }
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "\n%s\n", buf);
+ /* Print it out */
+}
+
+
+
+static void *monitor_thread_run(void)
+{
+ eXosip_event_t *event = NULL;
+
+ globals.running = 1;
+ while (globals.running > 0) {
+ if (!(event = eXosip_event_wait(0,100))) {
+ switch_yield(100);
+ continue;
+ }
+
+ eXosip_lock();
+ eXosip_automatic_action ();
+ eXosip_unlock();
+
+ log_event(event);
+
+ switch(event->type) {
+ case EXOSIP_CALL_INVITE:
+ exosip_create_call(event);
+ break;
+ case EXOSIP_CALL_REINVITE:
+ /* See what the reinvite is about - on hold or whatever */
+ //handle_reinvite(event);
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Got a reinvite.\n");
+ break;
+ case EXOSIP_CALL_MESSAGE_NEW:
+ if (event->request != NULL && MSG_IS_REFER(event->request)) {
+ //handle_call_transfer(event);
+ }
+ break;
+ case EXOSIP_CALL_ACK:
+ /* If audio is not flowing and this has SDP - fire it up! */
+ break;
+ case EXOSIP_CALL_ANSWERED:
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "The call was answered.\n");
+ handle_answer(event);
+ break;
+ case EXOSIP_CALL_PROCEEDING:
+ /* This is like a 100 Trying... yeah */
+ break;
+ case EXOSIP_CALL_RINGING:
+ //handle_ringing(event);
+ break;
+ case EXOSIP_CALL_REDIRECTED:
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Call was redirect\n");
+ break;
+ case EXOSIP_CALL_CLOSED:
+ destroy_call_by_event(event);
+ break;
+ case EXOSIP_CALL_RELEASED:
+ destroy_call_by_event(event);
+ break;
+ case EXOSIP_CALL_NOANSWER:
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "The call was not answered.\n");
+ destroy_call_by_event(event);
+ break;
+ case EXOSIP_CALL_REQUESTFAILURE:
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Request failure\n");
+ destroy_call_by_event(event);
+ break;
+ case EXOSIP_CALL_SERVERFAILURE:
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Server failure\n");
+ destroy_call_by_event(event);
+ break;
+ case EXOSIP_CALL_GLOBALFAILURE:
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Global failure\n");
+ destroy_call_by_event(event);
+ break;
+ /* Registration related stuff */
+ case EXOSIP_REGISTRATION_NEW:
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Received registration attempt\n");
+ break;
+ default:
+ /* Unknown event... casually absorb it for now */
+ break;
+ }
+
+ //switch_console_printf(SWITCH_CHANNEL_CONSOLE, "There was an event (%d) [%s]\n", event->type, event->textinfo);
+ /* Free the event */
+ eXosip_event_free(event);
+ }
+
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Monitor Thread Exiting\n");
+ globals.running = 0;
+ return NULL;
+}
+
+
+static int config_exosip(int reload)
+{
+ switch_config cfg;
+ char *var, *val;
+ char *cf = "exosip.conf";
+
+ globals.bytes_per_frame = DEFAULT_BYTES_PER_FRAME;
+ switch_core_hash_init(&globals.call_hash, module_pool);
+
+ if (!switch_config_open_file(&cfg, cf)) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "open of %s failed\n", cf);
+ return SWITCH_STATUS_TERM;
+ }
+
+ globals.rtp_start = 16384;
+ globals.rtp_end = 32768;
+
+ 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, "dialplan")) {
+ set_global_dialplan(val);
+ } else if (!strcmp(var, "rtp_min_port")) {
+ globals.rtp_start = atoi(val);
+ } else if (!strcmp(var, "rtp_max_port")) {
+ globals.rtp_end = atoi(val);
+ } else if (!strcmp(var, "codec_ms")) {
+ globals.codec_ms = atoi(val);
+ }
+ }
+ }
+
+ if (!globals.codec_ms) {
+ globals.codec_ms = 20;
+ }
+
+ if (!globals.port) {
+ globals.port = 5060;
+ }
+
+ switch_config_close_file(&cfg);
+
+ if (!globals.dialplan) {
+ set_global_dialplan("default");
+ }
+
+ if (eXosip_init ()) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "eXosip_init initialization failed!\n");
+ return SWITCH_STATUS_GENERR;
+ }
+ if (eXosip_listen_addr (IPPROTO_UDP, NULL, globals.port, AF_INET, 0)) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "eXosip_listen_addr failed!\n");
+ return SWITCH_STATUS_GENERR;
+ }
+
+ switch_mutex_init(&globals.port_lock, SWITCH_MUTEX_NESTED, module_pool);
+
+
+ /* Setup the user agent */
+ eXosip_set_user_agent("OPENSWITCH 2.0");
+
+
+ monitor_thread_run();
+ return 0;
+
+}
+
+
+SWITCH_MOD_DECLARE(switch_status) switch_module_runtime(void)
+{
+ config_exosip(0);
+ return SWITCH_STATUS_TERM;
+}
+
--- /dev/null
+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 *~
+
--- /dev/null
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+ ProjectType="Visual C++"\r
+ Version="8.00"\r
+ Name="mod_IaxChan"\r
+ ProjectGUID="{3A5B9131-F20C-4A85-9447-6C1610941CEE}"\r
+ RootNamespace="mod_IaxChan"\r
+ Keyword="Win32Proj"\r
+ >\r
+ <Platforms>\r
+ <Platform\r
+ Name="Win32"\r
+ />\r
+ </Platforms>\r
+ <ToolFiles>\r
+ </ToolFiles>\r
+ <Configurations>\r
+ <Configuration\r
+ Name="Debug|Win32"\r
+ OutputDirectory="Debug"\r
+ IntermediateDirectory="Debug"\r
+ ConfigurationType="2"\r
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"\r
+ CharacterSet="2"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ CommandLine="cscript /nologo $(InputDir)..\..\..\w32\vsnet\getlibs.vbs Mod_IaxChan Debug"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""$(InputDir)..\..\include";"$(InputDir)include";"$(InputDir)..\..\..\libs\include";"$(InputDir)..\..\..\libs\iax\src""\r
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;MOD_EXPORTS"\r
+ MinimalRebuild="true"\r
+ BasicRuntimeChecks="3"\r
+ RuntimeLibrary="3"\r
+ UsePrecompiledHeader="0"\r
+ WarningLevel="3"\r
+ Detect64BitPortabilityProblems="true"\r
+ DebugInformationFormat="4"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ AdditionalDependencies="Ws2_32.lib Iphlpapi.lib libiax2.lib Winmm.lib"\r
+ OutputFile="..\..\..\w32\vsnet\$(OutDir)/mod/mod_IaxChan.dll"\r
+ LinkIncremental="2"\r
+ AdditionalLibraryDirectories="$(InputDir)..\..\..\libs\iax\Debug"\r
+ GenerateDebugInformation="true"\r
+ ProgramDatabaseFile="$(OutDir)/mod_IaxChan.pdb"\r
+ SubSystem="2"\r
+ ImportLibrary="$(OutDir)/mod_IaxChan.lib"\r
+ TargetMachine="1"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebDeploymentTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ <Configuration\r
+ Name="Release|Win32"\r
+ OutputDirectory="Release"\r
+ IntermediateDirectory="Release"\r
+ ConfigurationType="2"\r
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"\r
+ CharacterSet="2"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ CommandLine="cscript /nologo $(InputDir)..\..\..\w32\vsnet\getlibs.vbs Mod_IaxChan Release"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""$(InputDir)..\..\include";"$(InputDir)include";"$(InputDir)..\..\..\libs\include";"$(InputDir)..\..\..\libs\iax\src""\r
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;MOD_EXPORTS"\r
+ RuntimeLibrary="2"\r
+ UsePrecompiledHeader="0"\r
+ WarningLevel="3"\r
+ Detect64BitPortabilityProblems="true"\r
+ DebugInformationFormat="3"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ AdditionalDependencies="Ws2_32.lib Iphlpapi.lib libiax2.lib Winmm.lib"\r
+ OutputFile="..\..\..\w32\vsnet\$(OutDir)/mod/mod_IaxChan.dll"\r
+ LinkIncremental="1"\r
+ AdditionalLibraryDirectories=""$(InputDir)..\..\..\libs\iax\Release""\r
+ GenerateDebugInformation="true"\r
+ SubSystem="2"\r
+ OptimizeReferences="2"\r
+ EnableCOMDATFolding="2"\r
+ LinkTimeCodeGeneration="1"\r
+ ImportLibrary="$(OutDir)/mod_IaxChan.lib"\r
+ TargetMachine="1"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebDeploymentTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ </Configurations>\r
+ <References>\r
+ </References>\r
+ <Files>\r
+ <Filter\r
+ Name="Source Files"\r
+ Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"\r
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"\r
+ >\r
+ <File\r
+ RelativePath=".\mod_iaxchan.c"\r
+ >\r
+ </File>\r
+ </Filter>\r
+ <Filter\r
+ Name="Header Files"\r
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"\r
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"\r
+ >\r
+ </Filter>\r
+ <Filter\r
+ Name="Resource Files"\r
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"\r
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"\r
+ >\r
+ </Filter>\r
+ </Files>\r
+ <Globals>\r
+ </Globals>\r
+</VisualStudioProject>\r
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+#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 *~
+
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+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 *~
+
--- /dev/null
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+ ProjectType="Visual C++"\r
+ Version="8.00"\r
+ Name="mod_PortAudio"\r
+ ProjectGUID="{5FD31A25-5D83-4794-8BEE-904DAD84CE71}"\r
+ RootNamespace="mod_PortAudio"\r
+ Keyword="Win32Proj"\r
+ >\r
+ <Platforms>\r
+ <Platform\r
+ Name="Win32"\r
+ />\r
+ </Platforms>\r
+ <ToolFiles>\r
+ </ToolFiles>\r
+ <Configurations>\r
+ <Configuration\r
+ Name="Debug|Win32"\r
+ OutputDirectory="Debug"\r
+ IntermediateDirectory="Debug"\r
+ ConfigurationType="2"\r
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"\r
+ CharacterSet="2"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ CommandLine="cscript /nologo $(InputDir)..\..\..\w32\vsnet\getlibs.vbs Mod_PortAudio Debug"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""$(InputDir)..\..\include";"$(InputDir)";"$(InputDir)..\..\..\libs\include";"$(InputDir)..\..\..\libs\portaudio\pa_common";"$(InputDir)..\..\..\libs\portaudio\pa_win_wmme""\r
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;MOD_EXPORTS"\r
+ MinimalRebuild="true"\r
+ BasicRuntimeChecks="3"\r
+ RuntimeLibrary="1"\r
+ UsePrecompiledHeader="0"\r
+ WarningLevel="3"\r
+ Detect64BitPortabilityProblems="true"\r
+ DebugInformationFormat="4"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ AdditionalOptions="/NODEFAULTLIB:LIMBCTD"\r
+ AdditionalDependencies="Ws2_32.lib Iphlpapi.lib PAStaticWMMED.lib Winmm.lib"\r
+ OutputFile="..\..\..\w32\vsnet\$(OutDir)/mod/mod_PortAudio.dll"\r
+ LinkIncremental="2"\r
+ AdditionalLibraryDirectories=""$(InputDir)..\..\..\libs\portaudio\winvc\Lib""\r
+ GenerateDebugInformation="true"\r
+ ProgramDatabaseFile="$(OutDir)/mod_PortAudio.pdb"\r
+ SubSystem="2"\r
+ ImportLibrary="$(OutDir)/mod_PortAudio.lib"\r
+ TargetMachine="1"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebDeploymentTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ <Configuration\r
+ Name="Release|Win32"\r
+ OutputDirectory="Release"\r
+ IntermediateDirectory="Release"\r
+ ConfigurationType="2"\r
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"\r
+ CharacterSet="2"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ CommandLine="cscript /nologo $(InputDir)..\..\..\w32\vsnet\getlibs.vbs Mod_PortAudio Release"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""$(InputDir)..\..\include";"$(InputDir)";"$(InputDir)..\..\..\libs\include";"$(InputDir)..\..\..\libs\portaudio\pa_common";"$(InputDir)..\..\..\libs\portaudio\pa_win_wmme""\r
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;MOD_EXPORTS"\r
+ RuntimeLibrary="0"\r
+ UsePrecompiledHeader="0"\r
+ WarningLevel="3"\r
+ Detect64BitPortabilityProblems="true"\r
+ DebugInformationFormat="3"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ AdditionalDependencies="Ws2_32.lib Iphlpapi.lib PAStaticWMME.lib Winmm.lib"\r
+ OutputFile="..\..\..\w32\vsnet\$(OutDir)/mod/mod_PortAudio.dll"\r
+ LinkIncremental="1"\r
+ AdditionalLibraryDirectories=""$(InputDir)..\..\..\libs\portaudio\winvc\Lib""\r
+ GenerateDebugInformation="true"\r
+ SubSystem="2"\r
+ OptimizeReferences="2"\r
+ EnableCOMDATFolding="2"\r
+ ImportLibrary="$(OutDir)/mod_PortAudio.lib"\r
+ TargetMachine="1"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebDeploymentTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ </Configurations>\r
+ <References>\r
+ </References>\r
+ <Files>\r
+ <Filter\r
+ Name="Source Files"\r
+ Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"\r
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"\r
+ >\r
+ <File\r
+ RelativePath=".\mod_PortAudio.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath=".\pablio.c"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath=".\ringbuffer.c"\r
+ >\r
+ </File>\r
+ </Filter>\r
+ <Filter\r
+ Name="Header Files"\r
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"\r
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"\r
+ >\r
+ <File\r
+ RelativePath=".\pablio.h"\r
+ >\r
+ </File>\r
+ <File\r
+ RelativePath=".\ringbuffer.h"\r
+ >\r
+ </File>\r
+ </Filter>\r
+ <Filter\r
+ Name="Resource Files"\r
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"\r
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"\r
+ >\r
+ </Filter>\r
+ </Files>\r
+ <Globals>\r
+ </Globals>\r
+</VisualStudioProject>\r
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*\r
+ * $Id: pablio.c,v 1.1.1.1.4.4 2003/03/13 17:28:33 philburk Exp $\r
+ * pablio.c\r
+ * Portable Audio Blocking Input/Output utility.\r
+ *\r
+ * Author: Phil Burk, http://www.softsynth.com\r
+ *\r
+ * This program uses the PortAudio Portable Audio Library.\r
+ * For more information see: http://www.audiomulch.com/portaudio/\r
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining\r
+ * a copy of this software and associated documentation files\r
+ * (the "Software"), to deal in the Software without restriction,\r
+ * including without limitation the rights to use, copy, modify, merge,\r
+ * publish, distribute, sublicense, and/or sell copies of the Software,\r
+ * and to permit persons to whom the Software is furnished to do so,\r
+ * subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be\r
+ * included in all copies or substantial portions of the Software.\r
+ *\r
+ * Any person wishing to distribute modifications to the Software is\r
+ * requested to send the modifications to the original developer so that\r
+ * they can be incorporated into the canonical version.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\r
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR\r
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\r
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+ *\r
+ */\r
+\r
+/* History:\r
+ * PLB021214 - check for valid stream in CloseAudioStream() to prevent hang.\r
+ * add timeOutMSec to CloseAudioStream() to prevent hang.\r
+ */\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <math.h>\r
+#include "portaudio.h"\r
+#include "ringbuffer.h"\r
+#include "pablio.h"\r
+#include <string.h>\r
+\r
+/************************************************************************/\r
+/******** Constants *****************************************************/\r
+/************************************************************************/\r
+\r
+#define FRAMES_PER_BUFFER (256)\r
+\r
+/************************************************************************/\r
+/******** Prototypes ****************************************************/\r
+/************************************************************************/\r
+\r
+static int blockingIOCallback( void *inputBuffer, void *outputBuffer,\r
+ unsigned long framesPerBuffer,\r
+ PaTimestamp outTime, void *userData );\r
+static PaError PABLIO_InitFIFO( RingBuffer *rbuf, long numFrames, long bytesPerFrame );\r
+static PaError PABLIO_TermFIFO( RingBuffer *rbuf );\r
+\r
+/************************************************************************/\r
+/******** Functions *****************************************************/\r
+/************************************************************************/\r
+\r
+/* Called from PortAudio.\r
+ * Read and write data only if there is room in FIFOs.\r
+ */\r
+static int blockingIOCallback( void *inputBuffer, void *outputBuffer,\r
+ unsigned long framesPerBuffer,\r
+ PaTimestamp outTime, void *userData )\r
+{\r
+ PABLIO_Stream *data = (PABLIO_Stream*)userData;\r
+ long numBytes = data->bytesPerFrame * framesPerBuffer;\r
+ (void) outTime;\r
+\r
+ /* This may get called with NULL inputBuffer during initial setup. */\r
+ if( inputBuffer != NULL )\r
+ {\r
+ RingBuffer_Write( &data->inFIFO, inputBuffer, numBytes );\r
+ }\r
+ if( outputBuffer != NULL )\r
+ {\r
+ int i;\r
+ int numRead = RingBuffer_Read( &data->outFIFO, outputBuffer, numBytes );\r
+ /* Zero out remainder of buffer if we run out of data. */\r
+ for( i=numRead; i<numBytes; i++ )\r
+ {\r
+ ((char *)outputBuffer)[i] = 0;\r
+ }\r
+ }\r
+\r
+ return 0;\r
+}\r
+\r
+/* Allocate buffer. */\r
+static PaError PABLIO_InitFIFO( RingBuffer *rbuf, long numFrames, long bytesPerFrame )\r
+{\r
+ long numBytes = numFrames * bytesPerFrame;\r
+ char *buffer = (char *) malloc( numBytes );\r
+ if( buffer == NULL ) return paInsufficientMemory;\r
+ memset( buffer, 0, numBytes );\r
+ return (PaError) RingBuffer_Init( rbuf, numBytes, buffer );\r
+}\r
+\r
+/* Free buffer. */\r
+static PaError PABLIO_TermFIFO( RingBuffer *rbuf )\r
+{\r
+ if( rbuf->buffer ) free( rbuf->buffer );\r
+ rbuf->buffer = NULL;\r
+ return paNoError;\r
+}\r
+\r
+/************************************************************\r
+ * Write data to ring buffer.\r
+ * Will not return until all the data has been written.\r
+ */\r
+long WriteAudioStream( PABLIO_Stream *aStream, void *data, long numFrames )\r
+{\r
+ long bytesWritten;\r
+ char *p = (char *) data;\r
+ long numBytes = aStream->bytesPerFrame * numFrames;\r
+ while( numBytes > 0)\r
+ {\r
+ bytesWritten = RingBuffer_Write( &aStream->outFIFO, p, numBytes );\r
+ numBytes -= bytesWritten;\r
+ p += bytesWritten;\r
+ if( numBytes > 0) Pa_Sleep(10);\r
+ }\r
+ return numFrames;\r
+}\r
+\r
+/************************************************************\r
+ * Read data from ring buffer.\r
+ * Will not return until all the data has been read.\r
+ */\r
+long ReadAudioStream( PABLIO_Stream *aStream, void *data, long numFrames )\r
+{\r
+ long bytesRead;\r
+ char *p = (char *) data;\r
+ long numBytes = aStream->bytesPerFrame * numFrames;\r
+ while( numBytes > 0)\r
+ {\r
+ bytesRead = RingBuffer_Read( &aStream->inFIFO, p, numBytes );\r
+ numBytes -= bytesRead;\r
+ p += bytesRead;\r
+ if( numBytes > 0) Pa_Sleep(10);\r
+ }\r
+ return numFrames;\r
+}\r
+\r
+/************************************************************\r
+ * Return the number of frames that could be written to the stream without\r
+ * having to wait.\r
+ */\r
+long GetAudioStreamWriteable( PABLIO_Stream *aStream )\r
+{\r
+ int bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO );\r
+ return bytesEmpty / aStream->bytesPerFrame;\r
+}\r
+\r
+/************************************************************\r
+ * Return the number of frames that are available to be read from the\r
+ * stream without having to wait.\r
+ */\r
+long GetAudioStreamReadable( PABLIO_Stream *aStream )\r
+{\r
+ int bytesFull = RingBuffer_GetReadAvailable( &aStream->inFIFO );\r
+ return bytesFull / aStream->bytesPerFrame;\r
+}\r
+\r
+/************************************************************/\r
+static unsigned long RoundUpToNextPowerOf2( unsigned long n )\r
+{\r
+ long numBits = 0;\r
+ if( ((n-1) & n) == 0) return n; /* Already Power of two. */\r
+ while( n > 0 )\r
+ {\r
+ n= n>>1;\r
+ numBits++;\r
+ }\r
+ return (1<<numBits);\r
+}\r
+\r
+/************************************************************\r
+ * Opens a PortAudio stream with default characteristics.\r
+ * Allocates PABLIO_Stream structure.\r
+ *\r
+ * flags parameter can be an ORed combination of:\r
+ * PABLIO_READ, PABLIO_WRITE, or PABLIO_READ_WRITE,\r
+ * and either PABLIO_MONO or PABLIO_STEREO\r
+ */\r
+PaError OpenAudioStream( PABLIO_Stream **rwblPtr, double sampleRate,\r
+ PaSampleFormat format, long flags, int indev, int outdev )\r
+{\r
+ long bytesPerSample;\r
+ long doRead = 0;\r
+ long doWrite = 0;\r
+ PaError err;\r
+ PABLIO_Stream *aStream;\r
+ long minNumBuffers;\r
+ long numFrames;\r
+\r
+ /* Allocate PABLIO_Stream structure for caller. */\r
+ aStream = (PABLIO_Stream *) malloc( sizeof(PABLIO_Stream) );\r
+ if( aStream == NULL ) return paInsufficientMemory;\r
+ memset( aStream, 0, sizeof(PABLIO_Stream) );\r
+\r
+ /* Determine size of a sample. */\r
+ bytesPerSample = Pa_GetSampleSize( format );\r
+ if( bytesPerSample < 0 )\r
+ {\r
+ err = (PaError) bytesPerSample;\r
+ goto error;\r
+ }\r
+ aStream->samplesPerFrame = ((flags&PABLIO_MONO) != 0) ? 1 : 2;\r
+ aStream->bytesPerFrame = bytesPerSample * aStream->samplesPerFrame;\r
+\r
+ /* Initialize PortAudio */\r
+ err = Pa_Initialize();\r
+ if( err != paNoError ) goto error;\r
+\r
+ /* Warning: numFrames must be larger than amount of data processed per interrupt\r
+ * inside PA to prevent glitches. Just to be safe, adjust size upwards.\r
+ */\r
+ minNumBuffers = 2 * Pa_GetMinNumBuffers( FRAMES_PER_BUFFER, sampleRate );\r
+ numFrames = minNumBuffers * FRAMES_PER_BUFFER;\r
+ /* The PortAudio callback runs in a high priority thread. But PABLIO\r
+ * runs in a normal foreground thread. So we may have much worse\r
+ * latency in PABLIO. So adjust latency to a safe level.\r
+ */\r
+ {\r
+ const int safeLatencyMSec = 200;\r
+ int minLatencyMSec = (int) ((1000 * numFrames) / sampleRate);\r
+ if( minLatencyMSec < safeLatencyMSec )\r
+ {\r
+ numFrames = (int) ((safeLatencyMSec * sampleRate) / 1000);\r
+ }\r
+ }\r
+ numFrames = RoundUpToNextPowerOf2( numFrames );\r
+\r
+ /* Initialize Ring Buffers */\r
+ doRead = ((flags & PABLIO_READ) != 0);\r
+ doWrite = ((flags & PABLIO_WRITE) != 0);\r
+ if(doRead)\r
+ {\r
+ err = PABLIO_InitFIFO( &aStream->inFIFO, numFrames, aStream->bytesPerFrame );\r
+ if( err != paNoError ) goto error;\r
+ }\r
+ if(doWrite)\r
+ {\r
+ long numBytes;\r
+ err = PABLIO_InitFIFO( &aStream->outFIFO, numFrames, aStream->bytesPerFrame );\r
+ if( err != paNoError ) goto error;\r
+ /* Make Write FIFO appear full initially. */\r
+ numBytes = RingBuffer_GetWriteAvailable( &aStream->outFIFO );\r
+ RingBuffer_AdvanceWriteIndex( &aStream->outFIFO, numBytes );\r
+ }\r
+\r
+ /* Open a PortAudio stream that we will use to communicate with the underlying\r
+ * audio drivers. */\r
+ err = Pa_OpenStream(\r
+ &aStream->stream,\r
+ (doRead ? (indev > -1) ? indev : Pa_GetDefaultInputDeviceID() : paNoDevice),\r
+ (doRead ? aStream->samplesPerFrame : 0 ),\r
+ format,\r
+ NULL,\r
+ (doWrite ? (outdev > -1) ? outdev : Pa_GetDefaultOutputDeviceID() : paNoDevice),\r
+ (doWrite ? aStream->samplesPerFrame : 0 ),\r
+ format,\r
+ NULL,\r
+ sampleRate,\r
+ FRAMES_PER_BUFFER,\r
+ minNumBuffers,\r
+ paClipOff, /* we won't output out of range samples so don't bother clipping them */\r
+ blockingIOCallback,\r
+ aStream );\r
+ if( err != paNoError ) goto error;\r
+\r
+ err = Pa_StartStream( aStream->stream );\r
+ if( err != paNoError ) goto error;\r
+\r
+ *rwblPtr = aStream;\r
+ return paNoError;\r
+\r
+error:\r
+ CloseAudioStream( aStream );\r
+ *rwblPtr = NULL;\r
+ return err;\r
+}\r
+\r
+/************************************************************/\r
+PaError CloseAudioStream( PABLIO_Stream *aStream )\r
+{\r
+ PaError err = paNoError;\r
+ int bytesEmpty;\r
+ int byteSize = aStream->outFIFO.bufferSize;\r
+\r
+ if( aStream->stream != NULL ) /* Make sure stream was opened. PLB021214 */\r
+ {\r
+ /* If we are writing data, make sure we play everything written. */\r
+ if( byteSize > 0 )\r
+ {\r
+ int timeOutMSec = 2000;\r
+ bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO );\r
+ while( (bytesEmpty < byteSize) && (timeOutMSec > 0) )\r
+ {\r
+ Pa_Sleep( 20 );\r
+ timeOutMSec -= 20;\r
+ bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO );\r
+ }\r
+ }\r
+ err = Pa_StopStream( aStream->stream );\r
+ if( err != paNoError ) goto error;\r
+ err = Pa_CloseStream( aStream->stream );\r
+ }\r
+\r
+error:\r
+ Pa_Terminate();\r
+ PABLIO_TermFIFO( &aStream->inFIFO );\r
+ PABLIO_TermFIFO( &aStream->outFIFO );\r
+ free( aStream );\r
+ return err;\r
+}\r
--- /dev/null
+#ifndef _PABLIO_H\r
+#define _PABLIO_H\r
+\r
+#ifdef __cplusplus\r
+extern "C"\r
+{\r
+#endif /* __cplusplus */\r
+\r
+/*\r
+ * $Id: pablio.h,v 1.1.1.1 2002/01/22 00:52:53 phil Exp $\r
+ * PABLIO.h\r
+ * Portable Audio Blocking read/write utility.\r
+ *\r
+ * Author: Phil Burk, http://www.softsynth.com/portaudio/\r
+ *\r
+ * Include file for PABLIO, the Portable Audio Blocking I/O Library.\r
+ * PABLIO is built on top of PortAudio, the Portable Audio Library.\r
+ * For more information see: http://www.audiomulch.com/portaudio/\r
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining\r
+ * a copy of this software and associated documentation files\r
+ * (the "Software"), to deal in the Software without restriction,\r
+ * including without limitation the rights to use, copy, modify, merge,\r
+ * publish, distribute, sublicense, and/or sell copies of the Software,\r
+ * and to permit persons to whom the Software is furnished to do so,\r
+ * subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be\r
+ * included in all copies or substantial portions of the Software.\r
+ *\r
+ * Any person wishing to distribute modifications to the Software is\r
+ * requested to send the modifications to the original developer so that\r
+ * they can be incorporated into the canonical version.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\r
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR\r
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\r
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+ *\r
+ */\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <math.h>\r
+#include "portaudio.h"\r
+#include "ringbuffer.h"\r
+#include <string.h>\r
+\r
+typedef struct\r
+{\r
+ RingBuffer inFIFO;\r
+ RingBuffer outFIFO;\r
+ PortAudioStream *stream;\r
+ int bytesPerFrame;\r
+ int samplesPerFrame;\r
+}\r
+PABLIO_Stream;\r
+\r
+/* Values for flags for OpenAudioStream(). */\r
+#define PABLIO_READ (1<<0)\r
+#define PABLIO_WRITE (1<<1)\r
+#define PABLIO_READ_WRITE (PABLIO_READ|PABLIO_WRITE)\r
+#define PABLIO_MONO (1<<2)\r
+#define PABLIO_STEREO (1<<3)\r
+\r
+/************************************************************\r
+ * Write data to ring buffer.\r
+ * Will not return until all the data has been written.\r
+ */\r
+long WriteAudioStream( PABLIO_Stream *aStream, void *data, long numFrames );\r
+\r
+/************************************************************\r
+ * Read data from ring buffer.\r
+ * Will not return until all the data has been read.\r
+ */\r
+long ReadAudioStream( PABLIO_Stream *aStream, void *data, long numFrames );\r
+\r
+/************************************************************\r
+ * Return the number of frames that could be written to the stream without\r
+ * having to wait.\r
+ */\r
+long GetAudioStreamWriteable( PABLIO_Stream *aStream );\r
+\r
+/************************************************************\r
+ * Return the number of frames that are available to be read from the\r
+ * stream without having to wait.\r
+ */\r
+long GetAudioStreamReadable( PABLIO_Stream *aStream );\r
+\r
+/************************************************************\r
+ * Opens a PortAudio stream with default characteristics.\r
+ * Allocates PABLIO_Stream structure.\r
+ *\r
+ * flags parameter can be an ORed combination of:\r
+ * PABLIO_READ, PABLIO_WRITE, or PABLIO_READ_WRITE,\r
+ * and either PABLIO_MONO or PABLIO_STEREO\r
+ */\r
+PaError OpenAudioStream( PABLIO_Stream **aStreamPtr, double sampleRate,\r
+ PaSampleFormat format, long flags, int indev, int outdev );\r
+\r
+PaError CloseAudioStream( PABLIO_Stream *aStream );\r
+\r
+#ifdef __cplusplus\r
+}\r
+#endif /* __cplusplus */\r
+#endif /* _PABLIO_H */\r
--- /dev/null
+/*\r
+ * $Id: ringbuffer.c,v 1.1.1.1 2002/01/22 00:52:53 phil Exp $\r
+ * ringbuffer.c\r
+ * Ring Buffer utility..\r
+ *\r
+ * Author: Phil Burk, http://www.softsynth.com\r
+ *\r
+ * This program uses the PortAudio Portable Audio Library.\r
+ * For more information see: http://www.audiomulch.com/portaudio/\r
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining\r
+ * a copy of this software and associated documentation files\r
+ * (the "Software"), to deal in the Software without restriction,\r
+ * including without limitation the rights to use, copy, modify, merge,\r
+ * publish, distribute, sublicense, and/or sell copies of the Software,\r
+ * and to permit persons to whom the Software is furnished to do so,\r
+ * subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be\r
+ * included in all copies or substantial portions of the Software.\r
+ *\r
+ * Any person wishing to distribute modifications to the Software is\r
+ * requested to send the modifications to the original developer so that\r
+ * they can be incorporated into the canonical version.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\r
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR\r
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\r
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+ *\r
+ */\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <math.h>\r
+#include "ringbuffer.h"\r
+#include <string.h>\r
+\r
+/***************************************************************************\r
+ * Initialize FIFO.\r
+ * numBytes must be power of 2, returns -1 if not.\r
+ */\r
+long RingBuffer_Init( RingBuffer *rbuf, long numBytes, void *dataPtr )\r
+{\r
+ if( ((numBytes-1) & numBytes) != 0) return -1; /* Not Power of two. */\r
+ rbuf->bufferSize = numBytes;\r
+ rbuf->buffer = (char *)dataPtr;\r
+ RingBuffer_Flush( rbuf );\r
+ rbuf->bigMask = (numBytes*2)-1;\r
+ rbuf->smallMask = (numBytes)-1;\r
+ return 0;\r
+}\r
+/***************************************************************************\r
+** Return number of bytes available for reading. */\r
+long RingBuffer_GetReadAvailable( RingBuffer *rbuf )\r
+{\r
+ return ( (rbuf->writeIndex - rbuf->readIndex) & rbuf->bigMask );\r
+}\r
+/***************************************************************************\r
+** Return number of bytes available for writing. */\r
+long RingBuffer_GetWriteAvailable( RingBuffer *rbuf )\r
+{\r
+ return ( rbuf->bufferSize - RingBuffer_GetReadAvailable(rbuf));\r
+}\r
+\r
+/***************************************************************************\r
+** Clear buffer. Should only be called when buffer is NOT being read. */\r
+void RingBuffer_Flush( RingBuffer *rbuf )\r
+{\r
+ rbuf->writeIndex = rbuf->readIndex = 0;\r
+}\r
+\r
+/***************************************************************************\r
+** Get address of region(s) to which we can write data.\r
+** If the region is contiguous, size2 will be zero.\r
+** If non-contiguous, size2 will be the size of second region.\r
+** Returns room available to be written or numBytes, whichever is smaller.\r
+*/\r
+long RingBuffer_GetWriteRegions( RingBuffer *rbuf, long numBytes,\r
+ void **dataPtr1, long *sizePtr1,\r
+ void **dataPtr2, long *sizePtr2 )\r
+{\r
+ long index;\r
+ long available = RingBuffer_GetWriteAvailable( rbuf );\r
+ if( numBytes > available ) numBytes = available;\r
+ /* Check to see if write is not contiguous. */\r
+ index = rbuf->writeIndex & rbuf->smallMask;\r
+ if( (index + numBytes) > rbuf->bufferSize )\r
+ {\r
+ /* Write data in two blocks that wrap the buffer. */\r
+ long firstHalf = rbuf->bufferSize - index;\r
+ *dataPtr1 = &rbuf->buffer[index];\r
+ *sizePtr1 = firstHalf;\r
+ *dataPtr2 = &rbuf->buffer[0];\r
+ *sizePtr2 = numBytes - firstHalf;\r
+ }\r
+ else\r
+ {\r
+ *dataPtr1 = &rbuf->buffer[index];\r
+ *sizePtr1 = numBytes;\r
+ *dataPtr2 = NULL;\r
+ *sizePtr2 = 0;\r
+ }\r
+ return numBytes;\r
+}\r
+\r
+\r
+/***************************************************************************\r
+*/\r
+long RingBuffer_AdvanceWriteIndex( RingBuffer *rbuf, long numBytes )\r
+{\r
+ return rbuf->writeIndex = (rbuf->writeIndex + numBytes) & rbuf->bigMask;\r
+}\r
+\r
+/***************************************************************************\r
+** Get address of region(s) from which we can read data.\r
+** If the region is contiguous, size2 will be zero.\r
+** If non-contiguous, size2 will be the size of second region.\r
+** Returns room available to be written or numBytes, whichever is smaller.\r
+*/\r
+long RingBuffer_GetReadRegions( RingBuffer *rbuf, long numBytes,\r
+ void **dataPtr1, long *sizePtr1,\r
+ void **dataPtr2, long *sizePtr2 )\r
+{\r
+ long index;\r
+ long available = RingBuffer_GetReadAvailable( rbuf );\r
+ if( numBytes > available ) numBytes = available;\r
+ /* Check to see if read is not contiguous. */\r
+ index = rbuf->readIndex & rbuf->smallMask;\r
+ if( (index + numBytes) > rbuf->bufferSize )\r
+ {\r
+ /* Write data in two blocks that wrap the buffer. */\r
+ long firstHalf = rbuf->bufferSize - index;\r
+ *dataPtr1 = &rbuf->buffer[index];\r
+ *sizePtr1 = firstHalf;\r
+ *dataPtr2 = &rbuf->buffer[0];\r
+ *sizePtr2 = numBytes - firstHalf;\r
+ }\r
+ else\r
+ {\r
+ *dataPtr1 = &rbuf->buffer[index];\r
+ *sizePtr1 = numBytes;\r
+ *dataPtr2 = NULL;\r
+ *sizePtr2 = 0;\r
+ }\r
+ return numBytes;\r
+}\r
+/***************************************************************************\r
+*/\r
+long RingBuffer_AdvanceReadIndex( RingBuffer *rbuf, long numBytes )\r
+{\r
+ return rbuf->readIndex = (rbuf->readIndex + numBytes) & rbuf->bigMask;\r
+}\r
+\r
+/***************************************************************************\r
+** Return bytes written. */\r
+long RingBuffer_Write( RingBuffer *rbuf, void *data, long numBytes )\r
+{\r
+ long size1, size2, numWritten;\r
+ void *data1, *data2;\r
+ numWritten = RingBuffer_GetWriteRegions( rbuf, numBytes, &data1, &size1, &data2, &size2 );\r
+ if( size2 > 0 )\r
+ {\r
+\r
+ memcpy( data1, data, size1 );\r
+ data = ((char *)data) + size1;\r
+ memcpy( data2, data, size2 );\r
+ }\r
+ else\r
+ {\r
+ memcpy( data1, data, size1 );\r
+ }\r
+ RingBuffer_AdvanceWriteIndex( rbuf, numWritten );\r
+ return numWritten;\r
+}\r
+\r
+/***************************************************************************\r
+** Return bytes read. */\r
+long RingBuffer_Read( RingBuffer *rbuf, void *data, long numBytes )\r
+{\r
+ long size1, size2, numRead;\r
+ void *data1, *data2;\r
+ numRead = RingBuffer_GetReadRegions( rbuf, numBytes, &data1, &size1, &data2, &size2 );\r
+ if( size2 > 0 )\r
+ {\r
+ memcpy( data, data1, size1 );\r
+ data = ((char *)data) + size1;\r
+ memcpy( data, data2, size2 );\r
+ }\r
+ else\r
+ {\r
+ memcpy( data, data1, size1 );\r
+ }\r
+ RingBuffer_AdvanceReadIndex( rbuf, numRead );\r
+ return numRead;\r
+}\r
--- /dev/null
+#ifndef _RINGBUFFER_H\r
+#define _RINGBUFFER_H\r
+#ifdef __cplusplus\r
+extern "C"\r
+{\r
+#endif /* __cplusplus */\r
+\r
+/*\r
+ * $Id: ringbuffer.h,v 1.1.1.1.4.2 2003/04/28 17:45:34 philburk Exp $\r
+ * ringbuffer.h\r
+ * Ring Buffer utility..\r
+ *\r
+ * Author: Phil Burk, http://www.softsynth.com\r
+ *\r
+ * This program is distributed with the PortAudio Portable Audio Library.\r
+ * For more information see: http://www.audiomulch.com/portaudio/\r
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining\r
+ * a copy of this software and associated documentation files\r
+ * (the "Software"), to deal in the Software without restriction,\r
+ * including without limitation the rights to use, copy, modify, merge,\r
+ * publish, distribute, sublicense, and/or sell copies of the Software,\r
+ * and to permit persons to whom the Software is furnished to do so,\r
+ * subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be\r
+ * included in all copies or substantial portions of the Software.\r
+ *\r
+ * Any person wishing to distribute modifications to the Software is\r
+ * requested to send the modifications to the original developer so that\r
+ * they can be incorporated into the canonical version.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\r
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR\r
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\r
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+ *\r
+ */\r
+#define PortAudioStream PaStream\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <math.h>\r
+#include "ringbuffer.h"\r
+#include <string.h>\r
+\r
+typedef struct\r
+{\r
+ long bufferSize; /* Number of bytes in FIFO. Power of 2. Set by RingBuffer_Init. */\r
+/* These are declared volatile because they are written by a different thread than the reader. */\r
+ volatile long writeIndex; /* Index of next writable byte. Set by RingBuffer_AdvanceWriteIndex. */\r
+ volatile long readIndex; /* Index of next readable byte. Set by RingBuffer_AdvanceReadIndex. */\r
+ long bigMask; /* Used for wrapping indices with extra bit to distinguish full/empty. */\r
+ long smallMask; /* Used for fitting indices to buffer. */\r
+ char *buffer;\r
+}\r
+RingBuffer;\r
+/*\r
+ * Initialize Ring Buffer.\r
+ * numBytes must be power of 2, returns -1 if not.\r
+ */\r
+long RingBuffer_Init( RingBuffer *rbuf, long numBytes, void *dataPtr );\r
+\r
+/* Clear buffer. Should only be called when buffer is NOT being read. */\r
+void RingBuffer_Flush( RingBuffer *rbuf );\r
+\r
+/* Return number of bytes available for writing. */\r
+long RingBuffer_GetWriteAvailable( RingBuffer *rbuf );\r
+/* Return number of bytes available for read. */\r
+long RingBuffer_GetReadAvailable( RingBuffer *rbuf );\r
+/* Return bytes written. */\r
+long RingBuffer_Write( RingBuffer *rbuf, void *data, long numBytes );\r
+/* Return bytes read. */\r
+long RingBuffer_Read( RingBuffer *rbuf, void *data, long numBytes );\r
+\r
+/* Get address of region(s) to which we can write data.\r
+** If the region is contiguous, size2 will be zero.\r
+** If non-contiguous, size2 will be the size of second region.\r
+** Returns room available to be written or numBytes, whichever is smaller.\r
+*/\r
+long RingBuffer_GetWriteRegions( RingBuffer *rbuf, long numBytes,\r
+ void **dataPtr1, long *sizePtr1,\r
+ void **dataPtr2, long *sizePtr2 );\r
+long RingBuffer_AdvanceWriteIndex( RingBuffer *rbuf, long numBytes );\r
+\r
+/* Get address of region(s) from which we can read data.\r
+** If the region is contiguous, size2 will be zero.\r
+** If non-contiguous, size2 will be the size of second region.\r
+** Returns room available to be read or numBytes, whichever is smaller.\r
+*/\r
+long RingBuffer_GetReadRegions( RingBuffer *rbuf, long numBytes,\r
+ void **dataPtr1, long *sizePtr1,\r
+ void **dataPtr2, long *sizePtr2 );\r
+\r
+long RingBuffer_AdvanceReadIndex( RingBuffer *rbuf, long numBytes );\r
+\r
+#ifdef __cplusplus\r
+}\r
+#endif /* __cplusplus */\r
+#endif /* _RINGBUFFER_H */\r
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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_woomerachan.c -- Woomera Endpoint Module
+ *
+ */
+#include <switch.h>
+
+#define WOOMERA_STRLEN 256
+#define WOOMERA_ARRAY_LEN 50
+#define WOOMERA_MIN_PORT 9900
+#define WOOMERA_MAX_PORT 9999
+#define WOOMERA_BODYLEN 2048
+#define WOOMERA_LINE_SEPERATOR "\r\n"
+#define WOOMERA_RECORD_SEPERATOR "\r\n\r\n"
+#define WOOMERA_DEBUG_PREFIX "**[DEBUG]** "
+#define WOOMERA_DEBUG_LINE "--------------------------------------------------------------------------------"
+#define WOOMERA_HARD_TIMEOUT -10000
+#define WOOMERA_QLEN 10
+#define WOOMERA_RECONNECT_TIME 5000000
+#define MEDIA_ANSWER "ANSWER"
+// THE ONE ABOVE OR THE 2 BELOW BUT NOT BOTH
+//#define MEDIA_ANSWER "ANSWER"
+//#define USE_ANSWER 1
+
+static const char modname[] = "mod_woomera";
+
+static switch_memory_pool *module_pool;
+
+#define STRLEN 15
+#define FRAME_LEN 480
+//static int WFORMAT = AST_FORMAT_SLINEAR;
+
+typedef enum {
+ WFLAG_EXISTS = (1 << 0),
+ WFLAG_EVENT = (1 << 1),
+ WFLAG_CONTENT = (1 << 2),
+} WFLAGS;
+
+
+typedef enum {
+ WCFLAG_NOWAIT = (1 << 0)
+} WCFLAGS;
+
+
+typedef enum {
+ PFLAG_INBOUND = (1 << 0),
+ PFLAG_OUTBOUND = (1 << 1),
+ PFLAG_DYNAMIC = (1 << 2),
+ PFLAG_DISABLED = (1 << 3)
+} PFLAGS;
+
+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),
+ TFLAG_ANSWER = (1 << 10),
+} TFLAGS;
+
+struct woomera_message {
+ char callid[WOOMERA_STRLEN];
+ int mval;
+ char command[WOOMERA_STRLEN];
+ char command_args[WOOMERA_STRLEN];
+ char names[WOOMERA_STRLEN][WOOMERA_ARRAY_LEN];
+ char values[WOOMERA_STRLEN][WOOMERA_ARRAY_LEN];
+ char body[WOOMERA_BODYLEN];
+ unsigned int flags;
+ int last;
+ struct woomera_message *next;
+};
+
+
+static struct {
+ int next_woomera_port;
+ int debug;
+ int panic;
+ int rtpmode;
+} globals;
+
+struct woomera_event_queue {
+ struct woomera_message *head;
+};
+
+struct woomera_profile {
+ char *name;
+ switch_socket_t *woomera_socket;
+ apr_thread_mutex_t *iolock;
+ char woomera_host[WOOMERA_STRLEN];
+ int woomera_port;
+ char audio_ip[WOOMERA_STRLEN];
+ char dialplan[WOOMERA_STRLEN];
+// pthread_t thread;
+ unsigned int flags;
+ int thread_running;
+ struct woomera_event_queue event_queue;
+};
+
+
+struct private_object {
+ char *name;
+ switch_frame frame;
+ switch_codec read_codec;
+ switch_codec write_codec;
+ switch_core_session *session;
+ switch_pollfd_t read_poll;
+ switch_pollfd_t write_poll;
+ switch_pollfd_t command_poll;
+ unsigned char databuf[2048];
+ switch_mutex_t *iolock;
+ switch_sockaddr_t *udpread;
+ switch_sockaddr_t *udpwrite;
+ switch_socket_t *command_channel;
+ switch_socket_t *udp_socket;
+ unsigned int flags;
+ short fdata[FRAME_LEN];
+ struct woomera_message call_info;
+ struct woomera_profile *profile;
+ char dest[WOOMERA_STRLEN];
+ int port;
+ switch_time_t started;
+ int timeout;
+ char dtmfbuf[WOOMERA_STRLEN];
+ switch_caller_profile *caller_profile;
+ struct woomera_event_queue event_queue;
+};
+
+typedef struct private_object private_object;
+typedef struct woomera_message woomera_message;
+typedef struct woomera_profile woomera_profile;
+typedef struct woomera_event_queue woomera_event_queue;
+
+static woomera_profile default_profile;
+
+static const switch_endpoint_interface woomerachan_endpoint_interface;
+
+static switch_status woomerachan_on_init(switch_core_session *session);
+static switch_status woomerachan_on_hangup(switch_core_session *session);
+static switch_status woomerachan_on_ring(switch_core_session *session);
+static switch_status woomerachan_on_loopback(switch_core_session *session);
+static switch_status woomerachan_on_transmit(switch_core_session *session);
+static switch_status woomerachan_outgoing_channel(switch_core_session *session, switch_caller_profile *outbound_profile, switch_core_session **new_session);
+static switch_status woomerachan_read_frame(switch_core_session *session, switch_frame **frame, int timeout, switch_io_flag flags);
+static switch_status woomerachan_write_frame(switch_core_session *session, switch_frame *frame, int timeout, switch_io_flag flags);
+static switch_status woomerachan_kill_channel(switch_core_session *session, int sig);
+static void tech_destroy(private_object *tech_pvt);
+static void woomera_printf(woomera_profile *profile, switch_socket_t *socket, char *fmt, ...);
+static char *woomera_message_header(woomera_message *wmsg, char *key);
+static int woomera_enqueue_event(woomera_event_queue *event_queue, woomera_message *wmsg);
+static int woomera_dequeue_event(woomera_event_queue *event_queue, woomera_message *wmsg);
+static int woomera_message_parse(switch_socket_t *fd, woomera_message *wmsg, int timeout, woomera_profile *profile, woomera_event_queue *event_queue);
+static int connect_woomera(switch_socket_t **new_sock, woomera_profile *profile, int flags);
+static int woomera_profile_thread_running(woomera_profile *profile, int set, int new);
+static int woomera_locate_socket(woomera_profile *profile, switch_socket_t **woomera_socket);
+static int tech_create_read_socket(private_object *tech_pvt);
+static void *woomera_channel_thread_run(switch_thread *thread, void *obj);
+static void *woomera_thread_run(void *obj);
+static int tech_activate(private_object *tech_pvt);
+
+/*
+ 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 woomerachan_on_init(switch_core_session *session)
+{
+ switch_channel *channel;
+ struct private_object *tech_pvt = NULL;
+ int rate = 8000;
+
+ 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;
+
+ if (switch_core_codec_init(&tech_pvt->read_codec, "L16", rate, 30, 1, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "%s Cannot set read codec\n", switch_channel_get_name(channel));
+ switch_channel_hangup(channel);
+ return SWITCH_STATUS_FALSE;
+ }
+
+ if (switch_core_codec_init(&tech_pvt->write_codec, "L16", rate, 30, 1, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "%s Cannot set read codec\n", switch_channel_get_name(channel));
+ switch_channel_hangup(channel);
+ return SWITCH_STATUS_FALSE;
+ }
+ tech_pvt->frame.rate = rate;
+ tech_pvt->frame.codec = &tech_pvt->read_codec;
+ switch_core_session_set_read_codec(session, &tech_pvt->read_codec);
+ switch_core_session_set_write_codec(session, &tech_pvt->write_codec);
+
+
+ switch_set_flag(tech_pvt, TFLAG_ACTIVATE);
+
+ switch_core_session_launch_thread(session, woomera_channel_thread_run, session);
+
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "%s WOOMERACHAN INIT\n", switch_channel_get_name(channel));
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status woomerachan_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 WOOMERACHAN RING\n", switch_channel_get_name(channel));
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status woomerachan_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 WOOMERACHAN EXECUTE\n", switch_channel_get_name(channel));
+
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status woomerachan_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 WOOMERACHAN HANGUP\n", switch_channel_get_name(channel));
+ tech_destroy(tech_pvt);
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static void woomera_socket_close(switch_socket_t **socket)
+{
+ if (*socket) {
+ switch_socket_close(*socket);
+ *socket = NULL;
+ }
+}
+
+
+static void udp_socket_close(struct private_object *tech_pvt)
+{
+ if (tech_pvt->udp_socket) {
+ apr_socket_shutdown(tech_pvt->udp_socket, APR_SHUTDOWN_READWRITE);
+ woomera_socket_close(&tech_pvt->udp_socket);
+ }
+}
+
+
+static switch_status woomerachan_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);
+
+ if (!tech_pvt->udp_socket) {
+ return SWITCH_STATUS_FALSE;
+ }
+
+ udp_socket_close(tech_pvt);
+
+ switch_channel_hangup(channel);
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "%s WOOMERACHAN KILL %d\n", switch_channel_get_name(channel), tech_pvt->udp_socket);
+
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status woomerachan_on_loopback(switch_core_session *session)
+{
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "WOOMERACHAN LOOPBACK\n");
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status woomerachan_on_transmit(switch_core_session *session)
+{
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "WOOMERACHAN 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 woomerachan_outgoing_channel(switch_core_session *session, switch_caller_profile *outbound_profile, switch_core_session **new_session)
+{
+ if ((*new_session = switch_core_session_request(&woomerachan_endpoint_interface, NULL))) {
+ struct private_object *tech_pvt;
+ switch_channel *channel;
+
+
+ if ((tech_pvt = (struct private_object *) switch_core_session_alloc(*new_session, sizeof(struct private_object)))) {
+ memset(tech_pvt, 0, sizeof(*tech_pvt));
+ tech_pvt->profile = &default_profile;
+ 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];
+ switch_caller_profile *caller_profile;
+
+ 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), "Woomera/%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;
+ }
+
+ 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 woomerachan_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_socket_waitfor(&tech_pvt->read_poll, ms);
+}
+
+static switch_status woomerachan_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;
+ return switch_socket_waitfor(&tech_pvt->write_poll, ms);
+}
+
+static switch_status woomerachan_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);
+
+ if (!tech_pvt->udp_socket) {
+ return SWITCH_STATUS_GENERR;
+ }
+ /*
+ if ((status = woomerachan_waitfor_read(session, -1)) != SWITCH_STATUS_SUCCESS) {
+ return status;
+ }1<
+ */
+ pframe = &tech_pvt->frame;
+ *frame = pframe;
+
+ pframe->datalen = sizeof(tech_pvt->databuf);
+ if ((status = switch_socket_recvfrom (tech_pvt->udpread, tech_pvt->udp_socket, 0, tech_pvt->databuf, &pframe->datalen)) == SWITCH_STATUS_SUCCESS) {
+ pframe->samples = (int)pframe->datalen / 2;
+ }
+ return status;
+}
+
+static switch_status woomerachan_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 (!tech_pvt->udp_socket) {
+ return SWITCH_STATUS_GENERR;
+ }
+
+ pframe = &tech_pvt->frame;
+ return switch_socket_sendto(tech_pvt->udp_socket, tech_pvt->udpwrite, 0, frame->data, &frame->datalen);
+
+}
+
+static const switch_event_handler_table woomerachan_event_handlers = {
+ /*.on_init*/ woomerachan_on_init,
+ /*.on_ring*/ woomerachan_on_ring,
+ /*.on_execute*/ woomerachan_on_execute,
+ /*.on_hangup*/ woomerachan_on_hangup,
+ /*.on_loopback*/ woomerachan_on_loopback,
+ /*.on_transmit*/ woomerachan_on_transmit
+};
+
+static const switch_io_routines woomerachan_io_routines = {
+ /*.outgoing_channel*/ woomerachan_outgoing_channel,
+ /*.answer_channel*/ NULL,
+ /*.read_frame*/ woomerachan_read_frame,
+ /*.write_frame*/ woomerachan_write_frame,
+ /*.kill_channel*/ woomerachan_kill_channel,
+ /*.waitfor_read*/ woomerachan_waitfor_read,
+ /*.waitfor_write*/ woomerachan_waitfor_write
+};
+
+static const switch_endpoint_interface woomerachan_endpoint_interface = {
+ /*.interface_name*/ "woomera",
+ /*.io_routines*/ &woomerachan_io_routines,
+ /*.event_handlers*/ &woomerachan_event_handlers,
+ /*.private*/ NULL,
+ /*.next*/ NULL
+};
+
+static const switch_loadable_module_interface woomerachan_module_interface = {
+ /*.module_name*/ modname,
+ /*.endpoint_interface*/ &woomerachan_endpoint_interface,
+ /*.timer_interface*/ NULL,
+ /*.dialplan_interface*/ NULL,
+ /*.codec_interface*/ NULL,
+ /*.application_interface*/ NULL
+};
+
+
+static void tech_destroy(private_object *tech_pvt)
+{
+ woomera_message wmsg;
+
+ if (globals.debug > 1) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, WOOMERA_DEBUG_PREFIX "+++DESTROY\n");
+ }
+
+
+ woomera_printf(tech_pvt->profile, tech_pvt->command_channel, "hangup %s%s", tech_pvt->call_info.callid, WOOMERA_RECORD_SEPERATOR);
+ if(woomera_message_parse(tech_pvt->command_channel,
+ &wmsg,
+ WOOMERA_HARD_TIMEOUT,
+ tech_pvt->profile,
+ &tech_pvt->event_queue
+ ) < 0) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "{%s} Already Disconnected\n", tech_pvt->profile->name);
+ }
+
+ woomera_printf(tech_pvt->profile, tech_pvt->command_channel, "bye%s", WOOMERA_RECORD_SEPERATOR);
+ woomera_socket_close(&tech_pvt->command_channel);
+ udp_socket_close(tech_pvt);
+}
+
+
+static void woomera_printf(woomera_profile *profile, switch_socket_t *socket, char *fmt, ...)
+{
+ char *stuff;
+ size_t res = 0, len = 0;
+
+ va_list ap;
+ va_start(ap, fmt);
+#ifndef vasprintf
+ stuff = (char *)malloc(10240);
+ vsnprintf(stuff, 10240, fmt, ap);
+#else
+ res = vasprintf(&stuff, fmt, ap);
+#endif
+ va_end(ap);
+ if (res == -1) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Out of memory\n");
+ } else {
+ if (profile && globals.debug) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Send Message: {%s} [%s/%d]\n%s\n%s", profile->name, profile->woomera_host, profile->woomera_port, WOOMERA_DEBUG_LINE, stuff);
+ }
+ len = strlen(stuff);
+ switch_socket_send(socket, stuff, &len);
+
+ free(stuff);
+ }
+
+}
+
+static char *woomera_message_header(woomera_message *wmsg, char *key)
+{
+ int x = 0;
+ char *value = NULL;
+
+ for (x = 0 ; x < wmsg->last ; x++) {
+ if (!strcasecmp(wmsg->names[x], key)) {
+ value = wmsg->values[x];
+ break;
+ }
+ }
+
+ return value;
+}
+
+static int woomera_enqueue_event(woomera_event_queue *event_queue, woomera_message *wmsg)
+{
+ woomera_message *new, *mptr;
+
+ if ((new = malloc(sizeof(woomera_message)))) {
+ memcpy(new, wmsg, sizeof(woomera_message));
+ new->next = NULL;
+
+ if (!event_queue->head) {
+ event_queue->head = new;
+ } else {
+ for (mptr = event_queue->head; mptr && mptr->next ; mptr = mptr->next);
+ mptr->next = new;
+ }
+ return 1;
+ } else {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Memory Allocation Error!\n");
+ }
+
+ return 0;
+}
+
+static int woomera_dequeue_event(woomera_event_queue *event_queue, woomera_message *wmsg)
+{
+ woomera_message *mptr = NULL;
+
+ if (event_queue->head) {
+ mptr = event_queue->head;
+ event_queue->head = mptr->next;
+ }
+
+ if (mptr) {
+ memcpy(wmsg, mptr, sizeof(woomera_message));
+ free(mptr);
+ return 1;
+ } else {
+ memset(wmsg, 0, sizeof(woomera_message));
+ }
+
+ return 0;
+}
+
+static int woomera_message_parse(switch_socket_t *fd, woomera_message *wmsg, int timeout, woomera_profile *profile, woomera_event_queue *event_queue)
+{
+ char *cur, *cr, *next = NULL, *eor = NULL;
+ char buf[2048] = "", *ptr;
+ int bytes = 0;
+ int failto = 0;
+
+ memset(wmsg, 0, sizeof(woomera_message));
+
+ if (fd < 0 ) {
+ return -1;
+ }
+
+ if (timeout < 0) {
+ timeout = abs(timeout);
+ failto = 1;
+ } else if (timeout == 0) {
+ timeout = -1;
+ }
+
+ ptr = buf;
+ bytes = 0;
+ while (!(eor = strstr(buf, WOOMERA_RECORD_SEPERATOR))) {
+ size_t len = 1;
+
+ if (!profile->thread_running) {
+ return -1;
+ }
+
+ if (switch_socket_recv(fd, ptr, &len) != SWITCH_STATUS_SUCCESS) {
+ return -1;
+ }
+ ptr++;
+ bytes++;
+ }
+ //*eor = '\0';
+ next = buf;
+
+ if (globals.debug) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Receive Message: {%s} [%s/%d]\n%s\n%s", profile->name, profile->woomera_host, profile->woomera_port, WOOMERA_DEBUG_LINE, buf);
+ }
+
+ while((cur = next)) {
+ if ((cr = strstr(cur, WOOMERA_LINE_SEPERATOR))) {
+ *cr = '\0';
+ next = cr + (sizeof(WOOMERA_LINE_SEPERATOR) - 1);
+ if (!strcmp(next, WOOMERA_RECORD_SEPERATOR)) {
+ break;
+ }
+ }
+
+ if (!cur || !cur[0]) {
+ break;
+ }
+
+ if (!wmsg->last) {
+ switch_set_flag(wmsg, WFLAG_EXISTS);
+ if (!strncasecmp(cur, "EVENT", 5)) {
+ cur += 6;
+ switch_set_flag(wmsg, WFLAG_EVENT);
+
+ if (cur && (cr = strchr(cur, ' '))) {
+ char *id;
+
+ *cr = '\0';
+ cr++;
+ id = cr;
+ if (cr && (cr = strchr(cr, ' '))) {
+ *cr = '\0';
+ cr++;
+ strncpy(wmsg->command_args, cr, WOOMERA_STRLEN);
+ }
+ if (id) {
+ strncpy(wmsg->callid, id, sizeof(wmsg->callid) - 1);
+ }
+ }
+ } else {
+ if (cur && (cur = strchr(cur, ' '))) {
+ *cur = '\0';
+ cur++;
+ wmsg->mval = atoi(buf);
+ } else {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Malformed Message!\n");
+ break;
+ }
+ }
+ if (cur) {
+ strncpy(wmsg->command, cur, WOOMERA_STRLEN);
+ } else {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Malformed Message!\n");
+ break;
+ }
+ } else {
+ char *name, *val;
+ name = cur;
+ if ((val = strchr(name, ':'))) {
+ *val = '\0';
+ val++;
+ while (*val == ' ') {
+ *val = '\0';
+ val++;
+ }
+ strncpy(wmsg->values[wmsg->last-1], val, WOOMERA_STRLEN);
+ }
+ strncpy(wmsg->names[wmsg->last-1], name, WOOMERA_STRLEN);
+ if (name && val && !strcasecmp(name, "content-type")) {
+ switch_set_flag(wmsg, WFLAG_CONTENT);
+ bytes = atoi(val);
+ }
+
+ }
+ wmsg->last++;
+ }
+
+ wmsg->last--;
+
+ if (bytes && switch_test_flag(wmsg, WFLAG_CONTENT)) {
+ size_t len = (bytes > sizeof(wmsg->body)) ? sizeof(wmsg->body) : bytes;
+ switch_socket_recv(fd, wmsg->body, &len);
+
+ if (globals.debug) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "%s\n", wmsg->body);
+ }
+ }
+
+ if (event_queue && switch_test_flag(wmsg, WFLAG_EVENT)) {
+ if (globals.debug) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Queue Event: {%s} [%s]\n", profile->name, wmsg->command);
+ }
+ /* we don't want events we want a reply so we will stash them for later */
+ woomera_enqueue_event(event_queue, wmsg);
+
+ /* call ourself recursively to find the reply. we'll keep doing this as long we get events.
+ * wmsg will be overwritten but it's ok we just queued it.
+ */
+ return woomera_message_parse(fd, wmsg, timeout, profile, event_queue);
+
+ } else if (wmsg->mval > 99 && wmsg->mval < 200) {
+ /* reply in the 100's are nice but we need to wait for another reply
+ call ourself recursively to find the reply > 199 and forget this reply.
+ */
+ return woomera_message_parse(fd, wmsg, timeout, profile, event_queue);
+ } else {
+ return switch_test_flag(wmsg, WFLAG_EXISTS);
+ }
+}
+
+
+static int connect_woomera(switch_socket_t **new_sock, woomera_profile *profile, int flags)
+{
+
+ switch_sockaddr_t *sa;
+ switch_status status;
+
+ status = switch_sockaddr_info_get(&sa, profile->woomera_host, AF_INET, profile->woomera_port, 0, module_pool);
+ if (status != SWITCH_STATUS_SUCCESS) {
+ return -1;
+ }
+ status = switch_socket_create(new_sock, AF_INET, SOCK_STREAM, 0, module_pool);
+ if (status != SWITCH_STATUS_SUCCESS) {
+ return -1;
+ }
+ /*
+ status = switch_socket_bind((*new_sock), sa);
+ if (0 && status != SWITCH_STATUS_SUCCESS) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Can't Bind to %s:%d!\n", profile->woomera_host, profile->woomera_port);
+ return -1;
+ }
+ */
+ status = switch_socket_connect((*new_sock), sa);
+ if (status != SWITCH_STATUS_SUCCESS) {
+ return -1;
+ }
+
+ return 1;
+}
+
+static int woomera_profile_thread_running(woomera_profile *profile, int set, int new)
+{
+ int running = 0;
+
+ switch_mutex_lock(profile->iolock);
+ if (set) {
+ profile->thread_running = new;
+ }
+ running = profile->thread_running;
+ switch_mutex_unlock(profile->iolock);
+ return running;
+
+}
+
+static int woomera_locate_socket(woomera_profile *profile, switch_socket_t **woomera_socket)
+{
+ woomera_message wmsg;
+
+ for (;;) {
+
+ while (connect_woomera(woomera_socket, profile, 0) < 0) {
+ if (!woomera_profile_thread_running(profile, 0, 0)) {
+ break;
+ }
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "{%s} Cannot Reconnect to Woomera! retry in 5 seconds\n", profile->name);
+ switch_sleep(WOOMERA_RECONNECT_TIME);
+ }
+
+ if (*woomera_socket) {
+ if (switch_test_flag(profile, PFLAG_INBOUND)) {
+ woomera_printf(profile, *woomera_socket, "LISTEN%s", WOOMERA_RECORD_SEPERATOR);
+ if (woomera_message_parse(*woomera_socket,
+ &wmsg,
+ WOOMERA_HARD_TIMEOUT,
+ profile,
+ &profile->event_queue
+ ) < 0) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "{%s} HELP! Woomera is broken!\n", profile->name);
+ globals.panic = 1;
+ woomera_profile_thread_running(&default_profile, 1, 0);
+ switch_sleep(WOOMERA_RECONNECT_TIME);
+ if (*woomera_socket) {
+ woomera_socket_close(woomera_socket);
+ }
+
+ continue;
+ }
+ }
+
+ }
+ switch_sleep(100);
+ break;
+ }
+ return *woomera_socket ? 1 : 0;
+}
+
+
+
+static int tech_create_read_socket(private_object *tech_pvt)
+{
+ switch_memory_pool *pool = switch_core_session_get_pool(tech_pvt->session);
+
+ if ((tech_pvt->port = globals.next_woomera_port++) >= WOOMERA_MAX_PORT) {
+ tech_pvt->port = globals.next_woomera_port = WOOMERA_MIN_PORT;
+ }
+
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "connect %s:%d\n", tech_pvt->profile->audio_ip, tech_pvt->port);
+ //tech_pvt->udp_socket = create_udp_socket(tech_pvt->profile->audio_ip, tech_pvt->port, &tech_pvt->udpread, 0);
+
+ switch_sockaddr_info_get(&tech_pvt->udpread, tech_pvt->profile->audio_ip, SWITCH_UNSPEC, tech_pvt->port, 0, pool);
+ if (switch_socket_create(&tech_pvt->udp_socket, AF_INET, SOCK_DGRAM, 0, pool) == SWITCH_STATUS_SUCCESS) {
+ switch_socket_bind(tech_pvt->udp_socket, tech_pvt->udpread);
+ switch_socket_create_pollfd(&tech_pvt->read_poll, tech_pvt->udp_socket, SWITCH_POLLIN|SWITCH_POLLERR, pool);
+ switch_socket_create_pollfd(&tech_pvt->write_poll, tech_pvt->udp_socket, SWITCH_POLLOUT|SWITCH_POLLERR, pool);
+ }
+
+ return 0;
+}
+
+
+static int tech_activate(private_object *tech_pvt)
+{
+ woomera_message wmsg;
+
+ if (tech_pvt) {
+ if((connect_woomera(&tech_pvt->command_channel, tech_pvt->profile, 0))) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "connected to woomera!\n");
+ } else {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Can't connect to woomera!\n");
+ switch_sleep(WOOMERA_RECONNECT_TIME);
+ return -1;
+ }
+
+ if (switch_test_flag(tech_pvt, TFLAG_OUTBOUND)) {
+
+ woomera_printf(tech_pvt->profile,
+ tech_pvt->command_channel,
+ "CALL %s%sRaw-Audio: %s/%d%sLocal-Name: %s!%s%s",
+ tech_pvt->caller_profile->destination_number,
+ WOOMERA_LINE_SEPERATOR,
+ tech_pvt->profile->audio_ip,
+ tech_pvt->port,
+ WOOMERA_LINE_SEPERATOR,
+ tech_pvt->caller_profile->caller_id_name,
+ tech_pvt->caller_profile->caller_id_number,
+ WOOMERA_RECORD_SEPERATOR
+ );
+
+ woomera_message_parse(tech_pvt->command_channel,
+ &wmsg,
+ WOOMERA_HARD_TIMEOUT,
+ tech_pvt->profile,
+ &tech_pvt->event_queue
+ );
+ } else {
+ switch_set_flag(tech_pvt, TFLAG_PARSE_INCOMING);
+ woomera_printf(tech_pvt->profile, tech_pvt->command_channel, "LISTEN%s", WOOMERA_RECORD_SEPERATOR);
+ if (woomera_message_parse(tech_pvt->command_channel,
+ &wmsg,
+ WOOMERA_HARD_TIMEOUT,
+ tech_pvt->profile,
+ &tech_pvt->event_queue
+ ) < 0) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "{%s} HELP! Woomera is broken!\n", tech_pvt->profile->name);
+ switch_set_flag(tech_pvt, TFLAG_ABORT);
+ globals.panic = 1;
+ }
+ }
+ } else {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Where's my tech_pvt?\n");
+ }
+
+ return 0;
+}
+
+
+
+static void *woomera_channel_thread_run(switch_thread *thread, void *obj)
+{
+ switch_core_session *session = obj;
+ switch_channel *channel = NULL;
+ struct private_object *tech_pvt = NULL;
+ woomera_message wmsg;
+ int res = 0;
+
+ assert(session != NULL);
+
+ channel = switch_core_session_get_channel(session);
+ assert(channel != NULL);
+
+ tech_pvt = switch_core_session_get_private(session);
+ assert(tech_pvt != NULL);
+
+ if (!tech_pvt->udp_socket) {
+ tech_create_read_socket(tech_pvt);
+ }
+
+
+ for(;;) {
+ if (globals.panic) {
+ switch_set_flag(tech_pvt, TFLAG_ABORT);
+ }
+
+ if (switch_test_flag(tech_pvt, TFLAG_ABORT)) {
+ if (switch_channel_get_state(channel) < CS_HANGUP) {
+ switch_channel_set_state(channel, CS_HANGUP);
+ }
+ udp_socket_close(tech_pvt);
+ break;
+ }
+
+ if (switch_test_flag(tech_pvt, TFLAG_ACTIVATE)) {
+ switch_clear_flag(tech_pvt, TFLAG_ACTIVATE);
+ tech_activate(tech_pvt);
+ }
+
+ if (switch_test_flag(tech_pvt, TFLAG_ANSWER)) {
+ switch_clear_flag(tech_pvt, TFLAG_ANSWER);
+#ifdef USE_ANSWER
+ woomera_printf(tech_pvt->profile, tech_pvt->command_channel, "ANSWER %s%s",tech_pvt->call_info.callid, WOOMERA_RECORD_SEPERATOR);
+ if(woomera_message_parse(tech_pvt->command_channel,
+ &wmsg,
+ WOOMERA_HARD_TIMEOUT,
+ tech_pvt->profile,
+ &tech_pvt->event_queue
+ ) < 0) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "{%s} HELP! Woomera is broken!\n", tech_pvt->profile->name);
+ switch_set_flag(tech_pvt, TFLAG_ABORT);
+ globals.panic = 1;
+ continue;
+ }
+#endif
+ }
+
+ if (switch_test_flag(tech_pvt, TFLAG_DTMF)) {
+ switch_mutex_lock(tech_pvt->iolock);
+ woomera_printf(tech_pvt->profile, tech_pvt->command_channel, "DTMF %s %s%s",tech_pvt->call_info.callid, tech_pvt->dtmfbuf, WOOMERA_RECORD_SEPERATOR);
+ if(woomera_message_parse(tech_pvt->command_channel,
+ &wmsg,
+ WOOMERA_HARD_TIMEOUT,
+ tech_pvt->profile,
+ &tech_pvt->event_queue
+ ) < 0) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "{%s} HELP! Woomera is broken!\n", tech_pvt->profile->name);
+ switch_set_flag(tech_pvt, TFLAG_ABORT);
+ globals.panic = 1;
+ continue;
+ }
+ switch_clear_flag(tech_pvt, TFLAG_DTMF);
+ memset(tech_pvt->dtmfbuf, 0, sizeof(tech_pvt->dtmfbuf));
+ switch_mutex_unlock(tech_pvt->iolock);
+ }
+
+#if 1==0 /*convert to use switch_time_now */
+ if(tech_pvt->timeout) {
+ struct timeval now;
+ int elapsed;
+ gettimeofday(&now, NULL);
+ elapsed = (((now.tv_sec * 1000) + now.tv_usec / 1000) - ((tech_pvt->started.tv_sec * 1000) + tech_pvt->started.tv_usec / 1000));
+ if (elapsed > tech_pvt->timeout) {
+ /* call timed out! */
+ switch_set_flag(tech_pvt, TFLAG_ABORT);
+ }
+ }
+#endif
+
+ if (!tech_pvt->command_channel) {
+ break;
+ }
+ /* Check for events */
+ if((res = woomera_dequeue_event(&tech_pvt->event_queue, &wmsg)) ||
+ (res = woomera_message_parse(tech_pvt->command_channel,
+ &wmsg,
+ 100,
+ tech_pvt->profile,
+ NULL
+ ))) {
+
+ if (res < 0 || !strcasecmp(wmsg.command, "HANGUP")) {
+ switch_set_flag(tech_pvt, TFLAG_ABORT);
+ continue;
+ } else if (!strcasecmp(wmsg.command, "DTMF")) {
+ /*
+ struct ast_frame dtmf_frame = {AST_FRAME_DTMF};
+ int x = 0;
+ for (x = 0; x < strlen(wmsg.command_args); x++) {
+ dtmf_frame.subclass = wmsg.command_args[x];
+ ast_queue_frame(tech_pvt->owner, ast_frdup(&dtmf_frame));
+ if (globals.debug > 1) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, WOOMERA_DEBUG_PREFIX "SEND DTMF [%c] to %s\n", dtmf_frame.subclass, tech_pvt->owner->name);
+ }
+ }
+ */
+ } else if (!strcasecmp(wmsg.command, "PROCEED")) {
+ /* This packet has lots of info so well keep it */
+ tech_pvt->call_info = wmsg;
+ } else if (switch_test_flag(tech_pvt, TFLAG_PARSE_INCOMING) && !strcasecmp(wmsg.command, "INCOMING")) {
+ char *exten;
+ char cid_name[512];
+ char *cid_num;
+ char *ip;
+ char *p;
+ switch_clear_flag(tech_pvt, TFLAG_PARSE_INCOMING);
+ switch_set_flag(tech_pvt, TFLAG_INCOMING);
+ tech_pvt->call_info = wmsg;
+
+ exten = woomera_message_header(&wmsg, "Local-Number");
+ if (switch_strlen_zero(exten)) {
+ exten = "s";
+ }
+
+ if ((p = woomera_message_header(&wmsg, "Remote-Name"))) {
+ strncpy(cid_name, p, sizeof(cid_name));
+ }
+
+ if ((cid_num = strchr(cid_name, '!'))) {
+ *cid_num = '\0';
+ cid_num++;
+ } else {
+ cid_num = woomera_message_header(&wmsg, "Remote-Number");
+ }
+ ip = woomera_message_header(&wmsg, "Remote-Address");
+
+ if ((tech_pvt->caller_profile = switch_caller_profile_new(session,
+ tech_pvt->profile->dialplan,
+ cid_name,
+ cid_num,
+ ip,
+ NULL,
+ NULL,
+ exten))) {
+ char name[128];
+ switch_channel_set_caller_profile(channel, tech_pvt->caller_profile);
+ snprintf(name, sizeof(name), "Woomera/%s-%04x", tech_pvt->caller_profile->destination_number, rand() & 0xffff);
+ switch_channel_set_name(channel, name);
+ }
+
+ woomera_printf(tech_pvt->profile, tech_pvt->command_channel,
+ "%s %s%s"
+ "Raw-Audio: %s/%d%s",
+ MEDIA_ANSWER,
+ wmsg.callid,
+ WOOMERA_LINE_SEPERATOR,
+ tech_pvt->profile->audio_ip,
+ tech_pvt->port,
+ WOOMERA_RECORD_SEPERATOR);
+
+ if(woomera_message_parse(tech_pvt->command_channel,
+ &wmsg,
+ WOOMERA_HARD_TIMEOUT,
+ tech_pvt->profile,
+ &tech_pvt->event_queue
+ ) < 0) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "{%s} HELP! Woomera is broken!\n", tech_pvt->profile->name);
+ switch_set_flag(tech_pvt, TFLAG_ABORT);
+ globals.panic = 1;
+ continue;
+ }
+
+ } else if (!strcasecmp(wmsg.command, "CONNECT")) {
+
+ } else if (!strcasecmp(wmsg.command, "MEDIA")) {
+ char *raw_audio_header;
+
+ if ((raw_audio_header = woomera_message_header(&wmsg, "Raw-Audio"))) {
+ char ip[25];
+ char *ptr;
+ int port = 0;
+
+ strncpy(ip, raw_audio_header, sizeof(ip) - 1);
+ if ((ptr=strchr(ip, '/'))) {
+ *ptr = '\0';
+ ptr++;
+ port = atoi(ptr);
+ }
+ /* Move Channel's State Machine to RING */
+ switch_channel_answer(channel);
+ switch_channel_set_state(channel, CS_RING);
+
+ if (switch_sockaddr_info_get(&tech_pvt->udpwrite,
+ ip,
+ SWITCH_UNSPEC,
+ port,
+ 0,
+ switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) {
+ if (globals.debug) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, WOOMERA_DEBUG_PREFIX "{%s} Cannot resolve %s\n", tech_pvt->profile->name, ip);
+ }
+ switch_channel_hangup(channel);
+ }
+ }
+ }
+ }
+ if (globals.debug > 2) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, WOOMERA_DEBUG_PREFIX "CHECK {%s}(%d)\n", tech_pvt->profile->name, res);
+ }
+ }
+ if (globals.debug > 1) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, WOOMERA_DEBUG_PREFIX "Monitor thread for %s done.\n", tech_pvt->profile->name);
+ }
+
+ return NULL;
+}
+
+
+
+
+static void *woomera_thread_run(void *obj)
+{
+
+ int res = 0;
+ woomera_message wmsg;
+ woomera_profile *profile;
+
+ profile = obj;
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Started Woomera Thread {%s}.\n", profile->name);
+
+ profile->thread_running = 1;
+ profile->woomera_socket = NULL;
+
+
+ while(woomera_profile_thread_running(profile, 0, 0)) {
+ /* listen on socket and handle events */
+ if (globals.panic == 2) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Woomera is disabled!\n");
+ switch_sleep(WOOMERA_RECONNECT_TIME);
+ continue;
+ }
+
+ if (! profile->woomera_socket) {
+ if (woomera_locate_socket(profile, &profile->woomera_socket)) {
+ globals.panic = 0;
+ }
+ if (!woomera_profile_thread_running(profile, 0, 0)) {
+ break;
+ }
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Woomera Thread Up {%s} %s/%d\n", profile->name, profile->woomera_host, profile->woomera_port);
+
+ }
+
+ if (globals.panic) {
+ if (globals.panic != 2) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Help I'm in a state of panic!\n");
+ globals.panic = 0;//fix
+ }
+ woomera_socket_close(&profile->woomera_socket);
+
+
+ continue;
+ }
+
+ if ((res = woomera_dequeue_event(&profile->event_queue, &wmsg) ||
+ (res = woomera_message_parse(profile->woomera_socket,
+ &wmsg,
+ /* if we are not stingy with threads we can block forever */
+ 0,
+ profile,
+ NULL
+ )))) {
+ if (res < 0) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "{%s} HELP! I lost my connection to woomera!\n", profile->name);
+ woomera_socket_close(&profile->woomera_socket);
+
+ //global_set_flag(TFLAG_ABORT);
+ globals.panic = 1;
+ continue;
+
+ if (profile->woomera_socket) {
+ if (switch_test_flag(profile, PFLAG_INBOUND)) {
+ woomera_printf(profile, profile->woomera_socket, "LISTEN%s", WOOMERA_RECORD_SEPERATOR);
+ if (woomera_message_parse(profile->woomera_socket,
+ &wmsg,
+ WOOMERA_HARD_TIMEOUT,
+ profile,
+ &profile->event_queue
+ ) < 0) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "{%s} HELP! Woomera is broken!\n", profile->name);
+ globals.panic = 1;
+ woomera_socket_close(&profile->woomera_socket);
+ }
+ }
+ if (profile->woomera_socket) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Woomera Thread Up {%s} %s/%d\n", profile->name, profile->woomera_host, profile->woomera_port);
+ }
+ }
+ continue;
+ }
+
+ if (!strcasecmp(wmsg.command, "INCOMING")) {
+ char *name;
+ switch_core_session *session;
+
+ if (!(name = woomera_message_header(&wmsg, "Remote-Address"))) {
+ name = woomera_message_header(&wmsg, "Channel-Name");
+ }
+
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "New Inbound Channel %s!\n", name);
+ if ((session = switch_core_session_request(&woomerachan_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));
+ tech_pvt->profile = &default_profile;
+ 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;
+ }
+ switch_channel_set_state(channel, CS_INIT);
+ switch_core_session_thread_launch(session);
+ }
+ }
+ }
+
+ if (globals.debug > 2) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Main Thread {%s} Select Return %d\n", profile->name, res);
+ }
+
+ switch_yield(100);
+ }
+
+
+ if (profile->woomera_socket) {
+ woomera_printf(profile, profile->woomera_socket, "BYE%s", WOOMERA_RECORD_SEPERATOR);
+ woomera_socket_close(&profile->woomera_socket);
+ }
+
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Ended Woomera Thread {%s}.\n", profile->name);
+ woomera_profile_thread_running(profile, 1, -1);
+ return NULL;
+}
+
+SWITCH_MOD_DECLARE(switch_status) switch_module_runtime(void)
+{
+
+ woomera_thread_run(&default_profile);
+
+ return SWITCH_STATUS_TERM;
+}
+
+SWITCH_MOD_DECLARE(switch_status) switch_module_shutdown(void)
+{
+ int x = 0;
+ woomera_profile_thread_running(&default_profile, 1, 0);
+ while (!woomera_profile_thread_running(&default_profile, 0, 0)) {
+ woomera_socket_close(&default_profile.woomera_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;
+ struct woomera_profile *profile = &default_profile;
+ char *cf = "woomera.conf";
+
+ memset(&globals, 0, sizeof(globals));
+ globals.next_woomera_port = WOOMERA_MIN_PORT;
+
+ if (!switch_config_open_file(&cfg, cf)) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "open of %s failed\n", cf);
+ return SWITCH_STATUS_TERM;
+ }
+
+ switch_set_flag(profile, PFLAG_INBOUND | PFLAG_OUTBOUND);
+ profile->name = "main";
+ strncpy(profile->dialplan, "default", sizeof(profile->dialplan) - 1);
+
+ while (switch_config_next_pair(&cfg, &var, &val)) {
+ if (!strcasecmp(cfg.category, "settings")) {
+ if (!strcmp(var, "noload") && atoi(val)) {
+ return SWITCH_STATUS_TERM;
+ }
+ if (!strcmp(var, "debug")) {
+ globals.debug = atoi(val);
+ }
+ } else if (!strcasecmp(cfg.category, "profile")) {
+ if (!strcmp(var, "audio_ip")) {
+ strncpy(profile->audio_ip, val, sizeof(profile->audio_ip) - 1);
+ } else if (!strcmp(var, "host")) {
+ strncpy(profile->woomera_host, val, sizeof(profile->woomera_host) - 1);
+ } else if (!strcmp(var, "port")) {
+ profile->woomera_port = atoi(val);
+ } else if (!strcmp(var, "disabled")) {
+ if (atoi(val) > 0) {
+ switch_set_flag(profile, PFLAG_DISABLED);
+ }
+ } else if (!strcmp(var, "inbound")) {
+ if (atoi(val) < 1) {
+ switch_clear_flag(profile, PFLAG_INBOUND);
+ }
+ } else if (!strcmp(var, "outbound")) {
+ if (atoi(val) < 1) {
+ switch_clear_flag(profile, PFLAG_OUTBOUND);
+ }
+ } else if (!strcmp(var, "dialplan")) {
+ strncpy(profile->dialplan, val, sizeof(profile->dialplan) - 1);
+ }
+ }
+ }
+
+ 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");
+
+ 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_MEMERR;
+ }
+ return SWITCH_STATUS_MEMERR;
+ }
+
+
+
+ if (switch_mutex_init(&default_profile.iolock, SWITCH_MUTEX_NESTED, module_pool) != SWITCH_STATUS_SUCCESS) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "OH OH no lock\n");
+ return SWITCH_STATUS_TERM;
+ }
+
+
+ /* connect my internal structure to the blank pointer passed to me */
+ *interface = &woomerachan_module_interface;
+
+ /* indicate that the module should continue to be loaded */
+ return SWITCH_STATUS_SUCCESS;
+}
+
+
+
--- /dev/null
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+ ProjectType="Visual C++"\r
+ Version="8.00"\r
+ Name="mod_woomerachan"\r
+ ProjectGUID="{FE3540C5-3303-46E0-A69E-D92F775687F1}"\r
+ RootNamespace="mod_woomerachan"\r
+ Keyword="Win32Proj"\r
+ >\r
+ <Platforms>\r
+ <Platform\r
+ Name="Win32"\r
+ />\r
+ </Platforms>\r
+ <ToolFiles>\r
+ </ToolFiles>\r
+ <Configurations>\r
+ <Configuration\r
+ Name="Debug|Win32"\r
+ OutputDirectory="Debug"\r
+ IntermediateDirectory="Debug"\r
+ ConfigurationType="2"\r
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"\r
+ CharacterSet="2"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""$(InputDir)..\..\include";"$(InputDir)include";"$(InputDir)..\..\..\libs\include""\r
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;MOD_EXPORTS"\r
+ MinimalRebuild="true"\r
+ BasicRuntimeChecks="3"\r
+ RuntimeLibrary="1"\r
+ UsePrecompiledHeader="0"\r
+ WarningLevel="3"\r
+ Detect64BitPortabilityProblems="true"\r
+ DebugInformationFormat="4"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ OutputFile="..\..\..\w32\vsnet\$(OutDir)/mod/mod_woomerachan.dll"\r
+ LinkIncremental="2"\r
+ AdditionalLibraryDirectories="$(InputDir)..\..\libs\apr\Debug"\r
+ GenerateDebugInformation="true"\r
+ ProgramDatabaseFile="$(OutDir)/mod_woomerachan.pdb"\r
+ SubSystem="2"\r
+ ImportLibrary="$(OutDir)/mod_woomerachan.lib"\r
+ TargetMachine="1"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebDeploymentTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ <Configuration\r
+ Name="Release|Win32"\r
+ OutputDirectory="Release"\r
+ IntermediateDirectory="Release"\r
+ ConfigurationType="2"\r
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"\r
+ CharacterSet="2"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""$(InputDir)..\..\include";"$(InputDir)include";"$(InputDir)..\..\..\libs\include""\r
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;MOD_EXPORTS"\r
+ RuntimeLibrary="0"\r
+ UsePrecompiledHeader="0"\r
+ WarningLevel="3"\r
+ Detect64BitPortabilityProblems="true"\r
+ DebugInformationFormat="3"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ OutputFile="..\..\..\w32\vsnet\$(OutDir)/mod/mod_woomerachan.dll"\r
+ LinkIncremental="1"\r
+ AdditionalLibraryDirectories=""$(InputDir)..\..\libs\apr\Release""\r
+ GenerateDebugInformation="true"\r
+ SubSystem="2"\r
+ OptimizeReferences="2"\r
+ EnableCOMDATFolding="2"\r
+ ImportLibrary="$(OutDir)/mod_woomerachan.lib"\r
+ TargetMachine="1"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebDeploymentTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ </Configurations>\r
+ <References>\r
+ </References>\r
+ <Files>\r
+ <Filter\r
+ Name="Source Files"\r
+ Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"\r
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"\r
+ >\r
+ <File\r
+ RelativePath=".\mod_woomerachan.c"\r
+ >\r
+ </File>\r
+ </Filter>\r
+ <Filter\r
+ Name="Header Files"\r
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"\r
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"\r
+ >\r
+ </Filter>\r
+ <Filter\r
+ Name="Resource Files"\r
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"\r
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"\r
+ >\r
+ </Filter>\r
+ </Files>\r
+ <Globals>\r
+ </Globals>\r
+</VisualStudioProject>\r
--- /dev/null
+/*
+ * 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_event_test.c -- Framework Demo Module
+ *
+ */
+#include <switch.h>
+
+static const char modname[] = "mod_event_test";
+
+static void event_handler (switch_event *event)
+{
+ char buf[1024];
+
+ switch(event->event_id) {
+ case SWITCH_EVENT_LOG:
+ return;
+ break;
+ default:
+ switch_event_serialize(event, buf, sizeof(buf), NULL);
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "\nEVENT\n--------------------------------\n%s\n", buf);
+ break;
+ }
+}
+
+
+static switch_loadable_module_interface event_test_module_interface = {
+ /*.module_name*/ modname,
+ /*.endpoint_interface*/ NULL,
+ /*.timer_interface*/ NULL,
+ /*.dialplan_interface*/ NULL,
+ /*.codec_interface*/ NULL,
+ /*.application_interface*/ NULL
+};
+
+#define MY_EVENT_COOL "test::cool"
+
+
+//#define TORTURE_ME
+
+#ifdef TORTURE_ME
+#define TTHREADS 500
+static int THREADS = 0;
+
+static void *torture_thread(switch_thread *thread, void *obj)
+{
+ int y = 0;
+ int z = 0;
+ switch_core_thread_session *ts = obj;
+ switch_event *event;
+
+ z = THREADS++;
+
+ while(THREADS > 0) {
+ int x;
+ for(x = 0; x < 1; x++) {
+ if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, MY_EVENT_COOL) == SWITCH_STATUS_SUCCESS) {
+ switch_event_add_header(event, "event_info", "hello world %d %d", z, y++);
+ switch_event_fire(&event);
+ }
+ }
+ switch_yield(100000);
+ }
+
+ if (ts->pool) {
+ switch_memory_pool *pool = ts->pool;
+ switch_core_destroy_memory_pool(&pool);
+ }
+
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Thread Ended\n");
+}
+
+SWITCH_MOD_DECLARE(switch_status) switch_module_shutdown(void)
+{
+ THREADS = -1;
+ switch_yield(100000);
+ return SWITCH_STATUS_SUCCESS;
+}
+#endif
+
+
+SWITCH_MOD_DECLARE(switch_status) switch_module_load(switch_loadable_module_interface **interface, char *filename) {
+ /* connect my internal structure to the blank pointer passed to me */
+ *interface = &event_test_module_interface;
+
+ if (switch_event_reserve_subclass(MY_EVENT_COOL) != SWITCH_STATUS_SUCCESS) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Couldn't register subclass!");
+ return SWITCH_STATUS_GENERR;
+ }
+
+#ifdef TORTURE_ME
+ if (switch_event_bind((char *)modname, SWITCH_EVENT_ALL, SWITCH_EVENT_SUBCLASS_ANY, event_handler, NULL) != SWITCH_STATUS_SUCCESS) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Couldn't bind!\n");
+ return SWITCH_STATUS_GENERR;
+ }
+
+ if (1) {
+ int x = 0;
+ for(x = 0 ; x < TTHREADS ; x++) {
+ switch_core_launch_thread(torture_thread, NULL);
+ }
+ }
+#endif
+
+ /* indicate that the module should continue to be loaded */
+ return SWITCH_STATUS_SUCCESS;
+}
+
--- /dev/null
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+ ProjectType="Visual C++"\r
+ Version="8.00"\r
+ Name="mod_event_test"\r
+ ProjectGUID="{3A2A7795-C216-4FFF-B8EF-4D17A84BACCC}"\r
+ RootNamespace="mod_event_test"\r
+ Keyword="Win32Proj"\r
+ >\r
+ <Platforms>\r
+ <Platform\r
+ Name="Win32"\r
+ />\r
+ </Platforms>\r
+ <ToolFiles>\r
+ </ToolFiles>\r
+ <Configurations>\r
+ <Configuration\r
+ Name="Debug|Win32"\r
+ OutputDirectory="Debug"\r
+ IntermediateDirectory="Debug"\r
+ ConfigurationType="2"\r
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"\r
+ CharacterSet="2"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""$(InputDir)..\..\include";"$(InputDir)include";"$(InputDir)..\..\..\libs\include""\r
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;MOD_EXPORTS"\r
+ MinimalRebuild="true"\r
+ BasicRuntimeChecks="3"\r
+ RuntimeLibrary="1"\r
+ UsePrecompiledHeader="0"\r
+ WarningLevel="3"\r
+ Detect64BitPortabilityProblems="true"\r
+ DebugInformationFormat="4"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ OutputFile="..\..\..\w32\vsnet\$(OutDir)/mod/mod_event_test.dll"\r
+ LinkIncremental="2"\r
+ AdditionalLibraryDirectories="$(InputDir)..\..\libs\apr\Debug"\r
+ GenerateDebugInformation="true"\r
+ ProgramDatabaseFile="$(OutDir)/mod_event_test.pdb"\r
+ SubSystem="2"\r
+ ImportLibrary="$(OutDir)/mod_event_test.lib"\r
+ TargetMachine="1"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebDeploymentTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ <Configuration\r
+ Name="Release|Win32"\r
+ OutputDirectory="Release"\r
+ IntermediateDirectory="Release"\r
+ ConfigurationType="2"\r
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"\r
+ CharacterSet="2"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""$(InputDir)..\..\include";"$(InputDir)include";"$(InputDir)..\..\..\libs\include""\r
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;MOD_EXPORTS"\r
+ RuntimeLibrary="0"\r
+ UsePrecompiledHeader="0"\r
+ WarningLevel="3"\r
+ Detect64BitPortabilityProblems="true"\r
+ DebugInformationFormat="3"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ OutputFile="..\..\..\w32\vsnet\$(OutDir)/mod/mod_event_test.dll"\r
+ LinkIncremental="1"\r
+ AdditionalLibraryDirectories=""$(InputDir)..\..\libs\apr\Release""\r
+ GenerateDebugInformation="true"\r
+ SubSystem="2"\r
+ OptimizeReferences="2"\r
+ EnableCOMDATFolding="2"\r
+ ImportLibrary="$(OutDir)/mod_event_test.lib"\r
+ TargetMachine="1"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebDeploymentTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ </Configurations>\r
+ <References>\r
+ </References>\r
+ <Files>\r
+ <Filter\r
+ Name="Source Files"\r
+ Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"\r
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"\r
+ >\r
+ <File\r
+ RelativePath=".\mod_event_test.c"\r
+ >\r
+ </File>\r
+ </Filter>\r
+ <Filter\r
+ Name="Header Files"\r
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"\r
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"\r
+ >\r
+ </Filter>\r
+ <Filter\r
+ Name="Resource Files"\r
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"\r
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"\r
+ >\r
+ </Filter>\r
+ </Files>\r
+ <Globals>\r
+ </Globals>\r
+</VisualStudioProject>\r
--- /dev/null
+LDFLAGS += -liksemel -L/usr/local/lib
+
+all: depends $(MOD).so
+
+depends:
+ $(BASE)/buildlib.sh $(BASE) install iksemel-1.2.tar.gz --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 *~
+
--- /dev/null
+/*
+ * 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_xmpp_event.c -- XMPP Event Logger
+ *
+ */
+#include <switch.h>
+#include <iksemel.h>
+
+static const char modname[] = "mod_xmpp_event";
+
+static int RUNNING = 0;
+static iksfilter *my_filter;
+static int opt_timeout = 30;
+static int opt_use_tls = 0;
+
+/* stuff we keep per session */
+struct session {
+ iksparser *parser;
+ iksid *acc;
+ char *pass;
+ int features;
+ int authorized;
+ int counter;
+ int job_done;
+};
+
+static struct {
+ char *jid;
+ char *passwd;
+ char *target_jid;
+ int debug;
+ struct session session;
+} globals;
+
+static void event_handler (switch_event *event)
+{
+ char buf[1024];
+ iks *msg;
+ int loops = 0;
+
+ if (!RUNNING) {
+ return;
+ }
+
+ while (!globals.session.authorized) {
+ switch_yield(100000);
+ if (loops++ > 5) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Nothing to do with this Event!\n");
+ return;
+ }
+ }
+
+ switch(event->event_id) {
+ default:
+ switch_event_serialize(event, buf, sizeof(buf), NULL);
+ //switch_console_printf(SWITCH_CHANNEL_CONSOLE, "\nEVENT\n--------------------------------\n%s\n", buf);
+ msg = iks_make_msg(IKS_TYPE_NONE, globals.target_jid, buf);
+ iks_insert_attrib(msg, "subject", "Event");
+ iks_send(globals.session.parser, msg);
+
+ break;
+ }
+}
+
+
+
+SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_jid, globals.jid)
+ SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_target_jid, globals.target_jid)
+ SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_passwd, globals.passwd)
+
+
+ static switch_status load_config(void)
+{
+ switch_config cfg;
+ switch_status status = SWITCH_STATUS_FALSE;
+ char *var, *val;
+ char *cf = "xmpp_event.conf";
+ int count = 0;
+
+ 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, "jid")) {
+ set_global_jid(val);
+ count++;
+ } else if (!strcmp(var, "target_jid")) {
+ set_global_target_jid(val);
+ count++;
+ } else if (!strcmp(var, "passwd")) {
+ set_global_passwd(val);
+ count++;
+ } else if (!strcmp(var, "debug")) {
+ globals.debug = atoi(val);
+ }
+ }
+ }
+
+ switch_config_close_file(&cfg);
+
+ if (count == 3) {
+ /* TBD use config to pick what events to bind to */
+ if (switch_event_bind((char *)modname, SWITCH_EVENT_ALL, SWITCH_EVENT_SUBCLASS_ANY, event_handler, NULL) != SWITCH_STATUS_SUCCESS) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Couldn't bind!\n");
+ return SWITCH_STATUS_GENERR;
+ }
+
+ status = SWITCH_STATUS_SUCCESS;
+ }
+
+ return status;
+
+}
+
+int on_result (struct session *sess, ikspak *pak)
+{
+
+ return IKS_FILTER_EAT;
+}
+
+int on_stream (struct session *sess, int type, iks *node)
+{
+ sess->counter = opt_timeout;
+
+ switch (type) {
+ case IKS_NODE_START:
+ if (opt_use_tls && !iks_is_secure (sess->parser)) {
+ iks_start_tls (sess->parser);
+ }
+ break;
+ case IKS_NODE_NORMAL:
+ if (strcmp ("stream:features", iks_name (node)) == 0) {
+ sess->features = iks_stream_features (node);
+ if (opt_use_tls && !iks_is_secure (sess->parser)) break;
+ if (sess->authorized) {
+ iks *t;
+ if (sess->features & IKS_STREAM_BIND) {
+ t = iks_make_resource_bind (sess->acc);
+ iks_send (sess->parser, t);
+ iks_delete (t);
+ }
+ if (sess->features & IKS_STREAM_SESSION) {
+ t = iks_make_session ();
+ iks_insert_attrib (t, "id", "auth");
+ iks_send (sess->parser, t);
+ iks_delete (t);
+ }
+ } else {
+ if (sess->features & IKS_STREAM_SASL_MD5)
+ iks_start_sasl (sess->parser, IKS_SASL_DIGEST_MD5, sess->acc->user, sess->pass);
+ else if (sess->features & IKS_STREAM_SASL_PLAIN)
+ iks_start_sasl (sess->parser, IKS_SASL_PLAIN, sess->acc->user, sess->pass);
+ }
+ } else if (strcmp ("failure", iks_name (node)) == 0) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "sasl authentication failed\n");
+ } else if (strcmp ("success", iks_name (node)) == 0) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "server connected\n");
+ sess->authorized = 1;
+ iks_send_header (sess->parser, sess->acc->server);
+ } else {
+ ikspak *pak;
+
+ pak = iks_packet (node);
+ iks_filter_packet (my_filter, pak);
+ if (sess->job_done == 1) return IKS_HOOK;
+ }
+ break;
+#if 0
+ case IKS_NODE_STOP:
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "server disconnected\n");
+ break;
+
+ case IKS_NODE_ERROR:
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "stream error\n");
+ break;
+#endif
+
+ }
+
+ if (node) iks_delete (node);
+ return IKS_OK;
+}
+
+int on_msg (void *user_data, ikspak *pak)
+{
+ char *cmd = iks_find_cdata (pak->x, "body");
+ char *arg = NULL;
+ char retbuf[1024] = "";
+ char *p;
+
+ if ((p = strchr(cmd, '\r'))) {
+ *p++ = '\0';
+ } else if ((p = strchr(cmd, '\n'))) {
+ *p++ = '\0';
+ }
+
+ if ((arg = strchr(cmd, ' '))) {
+ *arg++ = '\0';
+ }
+
+ switch_api_execute(cmd, arg, retbuf, sizeof(retbuf));
+
+ return 0;
+}
+
+int on_error (void *user_data, ikspak *pak)
+{
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "authorization failed\n");
+ return IKS_FILTER_EAT;
+}
+
+void on_log (struct session *sess, const char *data, size_t size, int is_incoming)
+{
+ if (iks_is_secure (sess->parser)) fprintf (stderr, "Sec");
+ if (is_incoming) fprintf (stderr, "RECV"); else fprintf (stderr, "SEND");
+ fprintf (stderr, "[%s]\n", data);
+}
+
+void j_setup_filter (struct session *sess)
+{
+ if (my_filter) iks_filter_delete (my_filter);
+ my_filter = iks_filter_new ();
+ iks_filter_add_rule (my_filter, on_msg, 0,
+ IKS_RULE_TYPE, IKS_PAK_MESSAGE,
+ IKS_RULE_SUBTYPE, IKS_TYPE_CHAT,
+ IKS_RULE_FROM, globals.target_jid,
+ IKS_RULE_DONE);
+ iks_filter_add_rule (my_filter, (iksFilterHook *) on_result, sess,
+ IKS_RULE_TYPE, IKS_PAK_IQ,
+ IKS_RULE_SUBTYPE, IKS_TYPE_RESULT,
+ IKS_RULE_ID, "auth",
+ IKS_RULE_DONE);
+ iks_filter_add_rule (my_filter, on_error, sess,
+ IKS_RULE_TYPE, IKS_PAK_IQ,
+ IKS_RULE_SUBTYPE, IKS_TYPE_ERROR,
+ IKS_RULE_ID, "auth",
+ IKS_RULE_DONE);
+}
+
+static void xmpp_connect (char *jabber_id, char *pass)
+{
+ while (RUNNING == 1) {
+ int e;
+
+ memset (&globals.session, 0, sizeof (globals.session));
+ globals.session.parser = iks_stream_new (IKS_NS_CLIENT, &globals.session, (iksStreamHook *) on_stream);
+ if (globals.debug) iks_set_log_hook (globals.session.parser, (iksLogHook *) on_log);
+ globals.session.acc = iks_id_new (iks_parser_stack (globals.session.parser), jabber_id);
+ if (NULL == globals.session.acc->resource) {
+ /* user gave no resource name, use the default */
+ char tmp[512];
+ sprintf (tmp, "%s@%s/%s", globals.session.acc->user, globals.session.acc->server, modname);
+ globals.session.acc = iks_id_new (iks_parser_stack (globals.session.parser), tmp);
+ }
+ globals.session.pass = pass;
+
+ j_setup_filter (&globals.session);
+
+ e = iks_connect_tcp (globals.session.parser, globals.session.acc->server, IKS_JABBER_PORT);
+ switch (e) {
+ case IKS_OK:
+ break;
+ case IKS_NET_NODNS:
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "hostname lookup failed\n");
+ case IKS_NET_NOCONN:
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "connection failed\n");
+ default:
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "io error\n");
+ switch_sleep(5000000);
+ continue;
+ }
+
+ globals.session.counter = opt_timeout;
+ while (RUNNING == 1) {
+ e = iks_recv (globals.session.parser, 1);
+
+ if(globals.session.job_done) {
+ break;
+ }
+
+ if (IKS_HOOK == e) {
+ break;
+ }
+
+ if (IKS_OK != e) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "io error %d\n", e);
+ switch_sleep(5000000);
+ break;
+ }
+
+ if (!globals.session.authorized) {
+ if (IKS_NET_TLSFAIL == e) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "tls handshake failed\n");
+ switch_sleep(5000000);
+ break;
+ }
+
+ if (globals.session.counter == 0) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "network timeout\n");
+ switch_sleep(5000000);
+ break;
+ }
+ }
+ }
+
+ iks_disconnect(globals.session.parser);
+ iks_parser_delete (globals.session.parser);
+ globals.session.authorized = 0;
+ }
+ RUNNING = 0;
+
+}
+
+static switch_loadable_module_interface xmpp_event_module_interface = {
+ /*.module_name*/ modname,
+ /*.endpoint_interface*/ NULL,
+ /*.timer_interface*/ NULL,
+ /*.dialplan_interface*/ NULL,
+ /*.codec_interface*/ NULL,
+ /*.application_interface*/ NULL
+};
+
+SWITCH_MOD_DECLARE(switch_status) switch_module_load(switch_loadable_module_interface **interface, char *filename) {
+ /* connect my internal structure to the blank pointer passed to me */
+ *interface = &xmpp_event_module_interface;
+
+ if (load_config() != SWITCH_STATUS_SUCCESS) {
+ return SWITCH_STATUS_FALSE;
+ }
+
+ /* indicate that the module should continue to be loaded */
+ return SWITCH_STATUS_SUCCESS;
+}
+
+
+SWITCH_MOD_DECLARE(switch_status) switch_module_shutdown(void)
+{
+
+ if (RUNNING) {
+ RUNNING = -1;
+ while (RUNNING) {
+ switch_yield(1000);
+ }
+ }
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+SWITCH_MOD_DECLARE(switch_status) switch_module_runtime(void)
+{
+ RUNNING = 1;
+ xmpp_connect(globals.jid, globals.passwd);
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "disconnecting client %d\n", RUNNING);
+ return RUNNING ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_TERM;
+}
--- /dev/null
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+ ProjectType="Visual C++"\r
+ Version="8.00"\r
+ Name="mod_xmpp_event"\r
+ ProjectGUID="{F10BE67C-A8FF-4CB2-AF29-D46D2590DC59}"\r
+ RootNamespace="mod_xmpp_event"\r
+ Keyword="Win32Proj"\r
+ >\r
+ <Platforms>\r
+ <Platform\r
+ Name="Win32"\r
+ />\r
+ </Platforms>\r
+ <ToolFiles>\r
+ </ToolFiles>\r
+ <Configurations>\r
+ <Configuration\r
+ Name="Debug|Win32"\r
+ OutputDirectory="Debug"\r
+ IntermediateDirectory="Debug"\r
+ ConfigurationType="2"\r
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"\r
+ CharacterSet="2"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ CommandLine="cscript /nologo $(InputDir)..\..\..\w32\vsnet\getlibs.vbs Mod_XMPPEvent Debug"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""$(InputDir)..\..\include";"$(InputDir)..\..\..\libs\include";"$(InputDir)..\..\..\libs\iksemel\include""\r
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;MOD_EXPORTS"\r
+ MinimalRebuild="true"\r
+ BasicRuntimeChecks="3"\r
+ RuntimeLibrary="3"\r
+ UsePrecompiledHeader="0"\r
+ WarningLevel="3"\r
+ Detect64BitPortabilityProblems="true"\r
+ DebugInformationFormat="4"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ AdditionalDependencies="FreeSwitchCore.lib iksemel.lib Ws2_32.lib"\r
+ OutputFile="..\..\..\w32\vsnet\$(OutDir)/mod/mod_xmpp_event.dll"\r
+ LinkIncremental="2"\r
+ AdditionalLibraryDirectories="..\..\..\w32\vsnet\Debug;..\..\..\libs\iksemel\Debug"\r
+ GenerateDebugInformation="true"\r
+ ProgramDatabaseFile="$(OutDir)/mod_xmpp_event.pdb"\r
+ SubSystem="2"\r
+ ImportLibrary="$(OutDir)/mod_xmpp_event.lib"\r
+ TargetMachine="1"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebDeploymentTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ <Configuration\r
+ Name="Release|Win32"\r
+ OutputDirectory="Release"\r
+ IntermediateDirectory="Release"\r
+ ConfigurationType="2"\r
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"\r
+ CharacterSet="2"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ CommandLine="cscript /nologo $(InputDir)..\..\..\w32\vsnet\getlibs.vbs Mod_XMPPEvent Release"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""$(InputDir)..\..\include";"$(InputDir)..\..\..\libs\include";"$(InputDir)..\..\..\libs\iksemel\include""\r
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;MOD_EXPORTS"\r
+ RuntimeLibrary="2"\r
+ UsePrecompiledHeader="0"\r
+ WarningLevel="3"\r
+ Detect64BitPortabilityProblems="true"\r
+ DebugInformationFormat="3"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ AdditionalDependencies="FreeSwitchCore.lib iksemel.lib Ws2_32.lib"\r
+ OutputFile="..\..\..\w32\vsnet\$(OutDir)/mod/mod_xmpp_event.dll"\r
+ LinkIncremental="1"\r
+ AdditionalLibraryDirectories="..\..\..\w32\vsnet\Release;..\..\..\libs\iksemel\Release"\r
+ GenerateDebugInformation="true"\r
+ SubSystem="2"\r
+ OptimizeReferences="2"\r
+ EnableCOMDATFolding="2"\r
+ LinkTimeCodeGeneration="1"\r
+ ImportLibrary="$(OutDir)/mod_xmpp_event.lib"\r
+ TargetMachine="1"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebDeploymentTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ </Configurations>\r
+ <References>\r
+ </References>\r
+ <Files>\r
+ <Filter\r
+ Name="Source Files"\r
+ Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"\r
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"\r
+ >\r
+ <File\r
+ RelativePath="mod_xmpp_event.c"\r
+ >\r
+ </File>\r
+ </Filter>\r
+ <Filter\r
+ Name="Header Files"\r
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"\r
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"\r
+ >\r
+ </Filter>\r
+ <Filter\r
+ Name="Resource Files"\r
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"\r
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"\r
+ >\r
+ </Filter>\r
+ </Files>\r
+ <Globals>\r
+ </Globals>\r
+</VisualStudioProject>\r
--- /dev/null
+LDFLAGS += -lsndfile -L/usr/local/lib
+
+all: depends $(MOD).so
+
+depends:
+ $(BASE)/buildlib.sh $(BASE) install libsndfile-1.0.12.tar.gz --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 *~
+
--- /dev/null
+/*
+ * 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_sndfile.c -- Framework Demo Module
+ *
+ */
+#include <switch.h>
+#include <sndfile.h>
+
+static const char modname[] = "mod_sndfile";
+
+struct sndfile_context {
+ SF_INFO sfinfo;
+ SNDFILE* handle;
+};
+
+typedef struct sndfile_context sndfile_context;
+
+switch_status sndfile_file_open(switch_file_handle *handle, char *path)
+{
+ sndfile_context *context;
+ int mode = 0;
+ char *ext;
+
+ if (!(ext = strrchr(path, '.'))) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Invalid Format\n");
+ return SWITCH_STATUS_GENERR;
+ }
+ ext++;
+
+
+ if (switch_test_flag(handle, SWITCH_FILE_FLAG_READ)) {
+ mode += SFM_READ;
+ }
+
+ if (switch_test_flag(handle, SWITCH_FILE_FLAG_WRITE)) {
+ mode += SFM_WRITE;
+ }
+
+ if (!mode) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Invalid Mode!\n");
+ return SWITCH_STATUS_GENERR;
+ }
+
+
+ if (!(context = switch_core_alloc(handle->memory_pool, sizeof(*context)))) {
+ return SWITCH_STATUS_MEMERR;
+ }
+
+ if (!strcmp(ext, "r8") || !strcmp(ext, "raw")) {
+ context->sfinfo.format = SF_FORMAT_RAW | SF_FORMAT_PCM_16;
+ context->sfinfo.channels = 1;
+ context->sfinfo.samplerate = 8000;
+ }
+
+ if (!strcmp(ext, "r16")) {
+ context->sfinfo.format = SF_FORMAT_RAW | SF_FORMAT_PCM_16;
+ context->sfinfo.channels = 1;
+ context->sfinfo.samplerate = 16000;
+ }
+
+ if (!strcmp(ext, "r24")) {
+ context->sfinfo.format = SF_FORMAT_RAW | SF_FORMAT_PCM_24;
+ context->sfinfo.channels = 1;
+ context->sfinfo.samplerate = 24000;
+ }
+
+ if (!strcmp(ext, "r32")) {
+ context->sfinfo.format = SF_FORMAT_RAW | SF_FORMAT_PCM_32;
+ context->sfinfo.channels = 1;
+ context->sfinfo.samplerate = 32000;
+ }
+
+ if (!strcmp(ext, "gsm")) {
+ context->sfinfo.format = SF_FORMAT_RAW |SF_FORMAT_GSM610;
+ context->sfinfo.channels = 1;
+ context->sfinfo.samplerate = 8000;
+ }
+
+ if (!(context->handle = sf_open(path, mode, &context->sfinfo))) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Error Opening File [%s] [%s]\n", path, sf_strerror(context->handle));
+ return SWITCH_STATUS_GENERR;
+ }
+
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Opening File [%s] %dhz\n", path, context->sfinfo.samplerate);
+ handle->samples = (unsigned int)context->sfinfo.frames;
+ handle->samplerate = context->sfinfo.samplerate;
+ handle->channels = context->sfinfo.channels;
+ handle->format = context->sfinfo.format;
+ handle->sections = context->sfinfo.sections;
+ handle->seekable = context->sfinfo.seekable;
+
+ handle->private = context;
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+switch_status sndfile_file_close(switch_file_handle *handle)
+{
+ sndfile_context *context = handle->private;
+
+ sf_close(context->handle);
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+switch_status sndfile_file_seek(switch_file_handle *handle, unsigned int *cur_sample, unsigned int samples, int whence)
+{
+ sndfile_context *context = handle->private;
+
+ if (!handle->seekable) {
+ return SWITCH_STATUS_NOTIMPL;
+ }
+
+ *cur_sample = (unsigned int)sf_seek(context->handle, samples, whence);
+
+ return SWITCH_STATUS_SUCCESS;
+
+}
+
+switch_status sndfile_file_read (switch_file_handle *handle, void *data, size_t *len)
+{
+ size_t inlen = *len;
+ sndfile_context *context = handle->private;
+
+ if (switch_test_flag(handle, SWITCH_FILE_DATA_RAW)) {
+ *len = (size_t)sf_read_raw (context->handle, data, inlen);
+ } else if (switch_test_flag(handle, SWITCH_FILE_DATA_INT)) {
+ *len = (size_t)sf_readf_int(context->handle, (int *) data, inlen);
+ } else if (switch_test_flag(handle, SWITCH_FILE_DATA_SHORT)) {
+ *len = (size_t)sf_readf_short(context->handle, (short *) data, inlen);
+ } else if (switch_test_flag(handle, SWITCH_FILE_DATA_FLOAT)) {
+ *len = (size_t)sf_readf_float(context->handle, (float *) data, inlen);
+ } else if (switch_test_flag(handle, SWITCH_FILE_DATA_DOUBLE)) {
+ *len = (size_t)sf_readf_double(context->handle, (double *) data, inlen);
+ } else {
+ *len = (size_t)sf_readf_int(context->handle, (int *) data, inlen);
+ }
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+switch_status sndfile_file_write (switch_file_handle *handle, void *data, size_t *len)
+{
+ size_t inlen = *len;
+ sndfile_context *context = handle->private;
+
+ if (switch_test_flag(handle, SWITCH_FILE_DATA_RAW)) {
+ *len = (size_t)sf_write_raw (context->handle, data, inlen);
+ } else if (switch_test_flag(handle, SWITCH_FILE_DATA_INT)) {
+ *len = (size_t)sf_writef_int(context->handle, (int *) data, inlen);
+ } else if (switch_test_flag(handle, SWITCH_FILE_DATA_SHORT)) {
+ *len = (size_t)sf_writef_short(context->handle, (short *) data, inlen);
+ } else if (switch_test_flag(handle, SWITCH_FILE_DATA_FLOAT)) {
+ *len = (size_t)sf_writef_float(context->handle, (float *) data, inlen);
+ } else if (switch_test_flag(handle, SWITCH_FILE_DATA_DOUBLE)) {
+ *len = (size_t)sf_writef_double(context->handle, (double *) data, inlen);
+ } else {
+ *len = (size_t)sf_writef_int(context->handle, (int *) data, inlen);
+ }
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+/* Registration */
+
+static char **supported_formats;
+
+static switch_file_interface sndfile_file_interface = {
+ /*.interface_name*/ modname,
+ /*.file_open*/ sndfile_file_open,
+ /*.file_close*/ sndfile_file_close,
+ /*.file_read*/ sndfile_file_read,
+ /*.file_write*/ sndfile_file_write,
+ /*.file_seek*/ sndfile_file_seek,
+ /*.extens*/ NULL,
+ /*.next*/ NULL,
+};
+
+static switch_loadable_module_interface sndfile_module_interface = {
+ /*.module_name*/ modname,
+ /*.endpoint_interface*/ NULL,
+ /*.timer_interface*/ NULL,
+ /*.dialplan_interface*/ NULL,
+ /*.codec_interface*/ NULL,
+ /*.application_interface*/ NULL,
+ /*.api_interface*/ NULL,
+ /*.file_interface*/ &sndfile_file_interface
+};
+
+static switch_status setup_formats(void)
+{
+ SF_FORMAT_INFO info ;
+ SF_INFO sfinfo ;
+ char buffer [128] ;
+ int format, major_count, subtype_count, m, s ;
+ int len,x,skip;
+ char *extras[] = {"r8", "r16", "r24", "r32", "gsm", NULL};
+ int exlen = (sizeof(extras) / sizeof(extras[0]));
+ buffer [0] = 0 ;
+ sf_command (NULL, SFC_GET_LIB_VERSION, buffer, sizeof (buffer)) ;
+ if (strlen (buffer) < 1) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE_CLEAN, "Line %d: could not retrieve lib version.\n", __LINE__) ;
+ return SWITCH_STATUS_FALSE;
+ }
+
+
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE, "\nLibSndFile Version : %s Supported Formats\n", buffer) ;
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE_CLEAN, "================================================================================\n");
+ sf_command (NULL, SFC_GET_FORMAT_MAJOR_COUNT, &major_count, sizeof (int)) ;
+ sf_command (NULL, SFC_GET_FORMAT_SUBTYPE_COUNT, &subtype_count, sizeof (int)) ;
+
+ sfinfo.channels = 1 ;
+ len = ((major_count + (exlen + 2)) * sizeof(char *));
+ supported_formats = switch_core_permenant_alloc(len);
+
+ len = 0;
+ for (m = 0 ; m < major_count ; m++) {
+ skip = 0;
+ info.format = m ;
+ sf_command (NULL, SFC_GET_FORMAT_MAJOR, &info, sizeof (info)) ;
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE_CLEAN, "%s (extension \"%s\")\n", info.name, info.extension) ;
+ for (x = 0 ; x < len ; x++) {
+ if (supported_formats[x] == info.extension) {
+ skip++;
+ break;
+ }
+ }
+ if (!skip) {
+ supported_formats[len++] = (char *) info.extension;
+ }
+ format = info.format;
+
+ for (s = 0 ; s < subtype_count ; s++) {
+ info.format = s ;
+ sf_command (NULL, SFC_GET_FORMAT_SUBTYPE, &info, sizeof (info)) ;
+ format = (format & SF_FORMAT_TYPEMASK) | info.format ;
+ sfinfo.format = format ;
+ if (sf_format_check (&sfinfo)) {
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE_CLEAN, " %s\n", info.name) ;
+ }
+ }
+
+ }
+ for(m=0; m< exlen; m++) {
+ supported_formats[len++] = extras[m];
+ }
+
+
+
+ switch_console_printf(SWITCH_CHANNEL_CONSOLE_CLEAN, "================================================================================\n");
+ return SWITCH_STATUS_SUCCESS;
+}
+
+SWITCH_MOD_DECLARE(switch_status) switch_module_load(switch_loadable_module_interface **interface, char *filename) {
+
+
+ if (setup_formats() != SWITCH_STATUS_SUCCESS) {
+ return SWITCH_STATUS_FALSE;
+ }
+
+ /* connect my internal structure to the blank pointer passed to me */
+ sndfile_file_interface.extens = supported_formats;
+ *interface = &sndfile_module_interface;
+
+ /* indicate that the module should continue to be loaded */
+ return SWITCH_STATUS_SUCCESS;
+}
+
--- /dev/null
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+ ProjectType="Visual C++"\r
+ Version="8.00"\r
+ Name="mod_sndfile"\r
+ ProjectGUID="{AFAC0568-7548-42D5-9F6A-8D3400A1E4F6}"\r
+ RootNamespace="mod_sndfile"\r
+ Keyword="Win32Proj"\r
+ >\r
+ <Platforms>\r
+ <Platform\r
+ Name="Win32"\r
+ />\r
+ </Platforms>\r
+ <ToolFiles>\r
+ </ToolFiles>\r
+ <Configurations>\r
+ <Configuration\r
+ Name="Debug|Win32"\r
+ OutputDirectory="Debug"\r
+ IntermediateDirectory="Debug"\r
+ ConfigurationType="2"\r
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"\r
+ CharacterSet="2"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ CommandLine="cscript /nologo $(InputDir)..\..\..\w32\vsnet\getlibs.vbs Mod_sndfile Debug"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""$(InputDir)..\..\include";"$(InputDir)..\..\..\libs\include";"$(InputDir)..\..\..\libs\libsndfile\Win32""\r
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;MOD_EXPORTS"\r
+ MinimalRebuild="true"\r
+ BasicRuntimeChecks="3"\r
+ RuntimeLibrary="3"\r
+ UsePrecompiledHeader="0"\r
+ WarningLevel="3"\r
+ Detect64BitPortabilityProblems="true"\r
+ DebugInformationFormat="4"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ AdditionalDependencies="libsndfile.lib"\r
+ OutputFile="..\..\..\w32\vsnet\$(OutDir)/mod/mod_sndfile.dll"\r
+ LinkIncremental="2"\r
+ AdditionalLibraryDirectories="$(InputDir)..\..\..\libs\libsndfile\Win32\Debug"\r
+ GenerateDebugInformation="true"\r
+ ProgramDatabaseFile="$(OutDir)/mod_sndfile.pdb"\r
+ SubSystem="2"\r
+ ImportLibrary="$(OutDir)/mod_sndfile.lib"\r
+ TargetMachine="1"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebDeploymentTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ <Configuration\r
+ Name="Release|Win32"\r
+ OutputDirectory="Release"\r
+ IntermediateDirectory="Release"\r
+ ConfigurationType="2"\r
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"\r
+ CharacterSet="2"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ CommandLine="cscript /nologo $(InputDir)..\..\..\w32\vsnet\getlibs.vbs Mod_sndfile Release"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""$(InputDir)..\..\include";"$(InputDir)..\..\..\libs\include";"$(InputDir)..\..\..\libs\libsndfile\Win32""\r
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;MOD_EXPORTS"\r
+ RuntimeLibrary="2"\r
+ UsePrecompiledHeader="0"\r
+ WarningLevel="3"\r
+ Detect64BitPortabilityProblems="true"\r
+ DebugInformationFormat="3"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ AdditionalDependencies="libsndfile.lib"\r
+ OutputFile="..\..\..\w32\vsnet\$(OutDir)/mod/mod_sndfile.dll"\r
+ LinkIncremental="1"\r
+ AdditionalLibraryDirectories="$(InputDir)..\..\..\libs\libsndfile\Win32\Release"\r
+ GenerateDebugInformation="true"\r
+ SubSystem="2"\r
+ OptimizeReferences="2"\r
+ EnableCOMDATFolding="2"\r
+ LinkTimeCodeGeneration="1"\r
+ ImportLibrary="$(OutDir)/mod_sndfile.lib"\r
+ TargetMachine="1"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebDeploymentTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ </Configurations>\r
+ <References>\r
+ </References>\r
+ <Files>\r
+ <Filter\r
+ Name="Source Files"\r
+ Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"\r
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"\r
+ >\r
+ <File\r
+ RelativePath=".\mod_sndfile.c"\r
+ >\r
+ </File>\r
+ </Filter>\r
+ <Filter\r
+ Name="Header Files"\r
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"\r
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"\r
+ >\r
+ </Filter>\r
+ <Filter\r
+ Name="Resource Files"\r
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"\r
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"\r
+ >\r
+ </Filter>\r
+ </Files>\r
+ <Globals>\r
+ </Globals>\r
+</VisualStudioProject>\r
--- /dev/null
+/*
+ * 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_softtimer.c -- Software Timer Module
+ *
+ */
+#include <switch.h>
+#include <stdio.h>
+
+static const char modname[] = "mod_softtimer";
+
+#ifdef WIN32
+//#define WINTIMER
+#endif
+
+struct timer_private {
+#ifdef WINTIMER
+ LARGE_INTEGER freq;
+ LARGE_INTEGER base;
+ LARGE_INTEGER now;
+#else
+ switch_time_t reference;
+#endif
+};
+
+static switch_status soft_timer_init(switch_timer *timer)
+{
+ struct timer_private *private;
+
+ private = switch_core_alloc(timer->memory_pool, sizeof(*private));
+ timer->private = private;
+
+#ifdef WINTIMER
+ QueryPerformanceFrequency(&private->freq);
+ QueryPerformanceCounter(&private->base);
+#else
+ private->reference = switch_time_now();
+#endif
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status soft_timer_next(switch_timer *timer)
+{
+ struct timer_private *private = timer->private;
+
+#ifdef WINTIMER
+ private->base.QuadPart += timer->interval * (private->freq.QuadPart / 1000);
+ for(;;) {
+ QueryPerformanceCounter(&private->now);
+ if(private->now.QuadPart >= private->base.QuadPart) {
+ break;
+ }
+ switch_yield(100);
+ }
+#else
+ private->reference += timer->interval * 1000;
+
+ while (switch_time_now() < private->reference) {
+ switch_yield(1000);
+ }
+#endif
+
+ timer->samplecount += timer->samples;
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status soft_timer_destroy(switch_timer *timer)
+{
+ timer->private = NULL;
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static const switch_timer_interface soft_timer_interface = {
+ /*.interface_name*/ "soft",
+ /*.timer_init*/ soft_timer_init,
+ /*.timer_next*/ soft_timer_next,
+ /*.timer_destroy*/ soft_timer_destroy
+};
+
+static const switch_loadable_module_interface mod_timers_module_interface = {
+ /*.module_name*/ modname,
+ /*.endpoint_interface*/ NULL,
+ /*.timer_interface*/ &soft_timer_interface,
+ /*.switch_dialplan_interface*/ NULL,
+ /*.switch_codec_interface*/ NULL,
+ /*.switch_application_interface*/ NULL
+};
+
+SWITCH_MOD_DECLARE(switch_status) switch_module_load(const switch_loadable_module_interface **interface, char *filename) {
+
+ /* connect my internal structure to the blank pointer passed to me */
+ *interface = &mod_timers_module_interface;
+
+ /* indicate that the module should continue to be loaded */
+ return SWITCH_STATUS_SUCCESS;
+}
+
+
+
+
+
--- /dev/null
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+ ProjectType="Visual C++"\r
+ Version="8.00"\r
+ Name="mod_softtimer"\r
+ ProjectGUID="{DCC13474-28DF-47CA-A8EB-72F8CE9A78C5}"\r
+ RootNamespace="mod_softtimer"\r
+ Keyword="Win32Proj"\r
+ >\r
+ <Platforms>\r
+ <Platform\r
+ Name="Win32"\r
+ />\r
+ </Platforms>\r
+ <ToolFiles>\r
+ </ToolFiles>\r
+ <Configurations>\r
+ <Configuration\r
+ Name="Debug|Win32"\r
+ OutputDirectory="Debug"\r
+ IntermediateDirectory="Debug"\r
+ ConfigurationType="2"\r
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"\r
+ CharacterSet="2"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""$(InputDir)..\..\include";"$(InputDir)include";"$(InputDir)..\..\..\libs\include""\r
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;MOD_EXPORTS"\r
+ MinimalRebuild="true"\r
+ BasicRuntimeChecks="3"\r
+ RuntimeLibrary="1"\r
+ UsePrecompiledHeader="0"\r
+ WarningLevel="3"\r
+ Detect64BitPortabilityProblems="true"\r
+ DebugInformationFormat="4"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ OutputFile="..\..\..\w32\vsnet\$(OutDir)/mod/mod_softtimer.dll"\r
+ LinkIncremental="2"\r
+ AdditionalLibraryDirectories="$(InputDir)..\..\libs\apr\Debug"\r
+ GenerateDebugInformation="true"\r
+ ProgramDatabaseFile="$(OutDir)/mod_softtimer.pdb"\r
+ SubSystem="2"\r
+ ImportLibrary="$(OutDir)/mod_softtimer.lib"\r
+ TargetMachine="1"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebDeploymentTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ <Configuration\r
+ Name="Release|Win32"\r
+ OutputDirectory="Release"\r
+ IntermediateDirectory="Release"\r
+ ConfigurationType="2"\r
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"\r
+ CharacterSet="2"\r
+ >\r
+ <Tool\r
+ Name="VCPreBuildEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ />\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"\r
+ />\r
+ <Tool\r
+ Name="VCMIDLTool"\r
+ />\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""$(InputDir)..\..\include";"$(InputDir)include";"$(InputDir)..\..\..\libs\include""\r
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;MOD_EXPORTS"\r
+ RuntimeLibrary="0"\r
+ UsePrecompiledHeader="0"\r
+ WarningLevel="3"\r
+ Detect64BitPortabilityProblems="true"\r
+ DebugInformationFormat="3"\r
+ />\r
+ <Tool\r
+ Name="VCManagedResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ />\r
+ <Tool\r
+ Name="VCPreLinkEventTool"\r
+ />\r
+ <Tool\r
+ Name="VCLinkerTool"\r
+ OutputFile="..\..\..\w32\vsnet\$(OutDir)/mod/mod_softtimer.dll"\r
+ LinkIncremental="1"\r
+ AdditionalLibraryDirectories=""$(InputDir)..\..\libs\apr\Release""\r
+ GenerateDebugInformation="true"\r
+ SubSystem="2"\r
+ OptimizeReferences="2"\r
+ EnableCOMDATFolding="2"\r
+ ImportLibrary="$(OutDir)/mod_softtimer.lib"\r
+ TargetMachine="1"\r
+ />\r
+ <Tool\r
+ Name="VCALinkTool"\r
+ />\r
+ <Tool\r
+ Name="VCManifestTool"\r
+ />\r
+ <Tool\r
+ Name="VCXDCMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCBscMakeTool"\r
+ />\r
+ <Tool\r
+ Name="VCFxCopTool"\r
+ />\r
+ <Tool\r
+ Name="VCAppVerifierTool"\r
+ />\r
+ <Tool\r
+ Name="VCWebDeploymentTool"\r
+ />\r
+ <Tool\r
+ Name="VCPostBuildEventTool"\r
+ />\r
+ </Configuration>\r
+ </Configurations>\r
+ <References>\r
+ </References>\r
+ <Files>\r
+ <Filter\r
+ Name="Source Files"\r
+ Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"\r
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"\r
+ >\r
+ <File\r
+ RelativePath=".\mod_softtimer.c"\r
+ >\r
+ </File>\r
+ </Filter>\r
+ <Filter\r
+ Name="Header Files"\r
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"\r
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"\r
+ >\r
+ </Filter>\r
+ <Filter\r
+ Name="Resource Files"\r
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"\r
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"\r
+ >\r
+ </Filter>\r
+ </Files>\r
+ <Globals>\r
+ </Globals>\r
+</VisualStudioProject>\r