From 6668d0282f5c0df9bcaa3fbca6f02fc65e586487 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Tue, 3 Jan 2006 22:13:59 +0000 Subject: [PATCH] update git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@263 d0543943-73ff-0310-b7d9-9358b9ac24b2 --- src/mod/aplications/mod_bridgecall.c | 271 ++++ src/mod/aplications/mod_bridgecall.vcproj | 207 +++ .../aplications/mod_playback/mod_playback.c | 199 +++ .../mod_playback/mod_playback.vcproj | 207 +++ src/mod/aplications/mod_skel/mod_skel.c | 54 + src/mod/codec/mod_codec_g729/Makefile | 12 + src/mod/codec/mod_codec_g729/mod_codec_g729.c | 203 +++ .../mod_codec_g729/mod_codec_g729.vcproj | 211 +++ src/mod/codec/mod_g711codec/Makefile | 15 + src/mod/codec/mod_g711codec/g711.c | 312 ++++ src/mod/codec/mod_g711codec/g711.h | 60 + src/mod/codec/mod_g711codec/mod_g711codec.c | 303 ++++ .../codec/mod_g711codec/mod_g711codec.vcproj | 215 +++ src/mod/codec/mod_rawaudio/mod_rawaudio.c | 215 +++ .../codec/mod_rawaudio/mod_rawaudio.vcproj | 211 +++ src/mod/codec/mod_speexcodec/Makefile | 12 + src/mod/codec/mod_speexcodec/mod_speexcodec.c | 342 ++++ .../mod_speexcodec/mod_speexcodec.vcproj | 211 +++ .../mod_dialplan_demo/mod_dialplan_demo.c | 124 ++ .../mod_dialplan_demo.vcproj | 207 +++ src/mod/endpoints/mod_exosip/Makefile | 26 + src/mod/endpoints/mod_exosip/mod_exosip.c | 1409 ++++++++++++++++ .../endpoints/mod_exosip/mod_exosip.vcproj | 229 +++ .../endpoints/mod_exosip/mod_exosip_ccrtp.c | 1373 ++++++++++++++++ src/mod/endpoints/mod_exosip/mod_exosip_ucl.c | 1438 +++++++++++++++++ src/mod/endpoints/mod_iaxchan/Makefile | 15 + .../endpoints/mod_iaxchan/mod_IaxChan.vcproj | 212 +++ src/mod/endpoints/mod_iaxchan/mod_iaxchan.c | 986 +++++++++++ src/mod/endpoints/mod_opalchan/Makefile | 12 + src/mod/endpoints/mod_opalchan/mod_opalchan.c | 385 +++++ src/mod/endpoints/mod_portaudio/Makefile | 19 + .../mod_portaudio/mod_PortAudio.vcproj | 228 +++ .../endpoints/mod_portaudio/mod_portaudio.c | 934 +++++++++++ src/mod/endpoints/mod_portaudio/pablio.c | 327 ++++ src/mod/endpoints/mod_portaudio/pablio.h | 109 ++ src/mod/endpoints/mod_portaudio/ringbuffer.c | 199 +++ src/mod/endpoints/mod_portaudio/ringbuffer.h | 103 ++ src/mod/endpoints/mod_wanchan/mod_wanchan.c | 634 ++++++++ .../mod_woomerachan/mod_woomerachan.c | 1387 ++++++++++++++++ .../mod_woomerachan/mod_woomerachan.vcproj | 207 +++ .../mod_event_test/mod_event_test.c | 133 ++ .../mod_event_test/mod_event_test.vcproj | 207 +++ .../event_handlers/mod_xmpp_event/Makefile | 14 + .../mod_xmpp_event/mod_xmpp_event.c | 386 +++++ .../mod_xmpp_event/mod_xmpp_event.vcproj | 212 +++ src/mod/formats/mod_sndfile/Makefile | 15 + src/mod/formats/mod_sndfile/mod_sndfile.c | 294 ++++ .../formats/mod_sndfile/mod_sndfilel.vcproj | 212 +++ src/mod/timers/mod_softtimer/mod_softtimer.c | 128 ++ .../timers/mod_softtimer/mod_softtimer.vcproj | 207 +++ 50 files changed, 15631 insertions(+) create mode 100644 src/mod/aplications/mod_bridgecall.c create mode 100644 src/mod/aplications/mod_bridgecall.vcproj create mode 100644 src/mod/aplications/mod_playback/mod_playback.c create mode 100644 src/mod/aplications/mod_playback/mod_playback.vcproj create mode 100644 src/mod/aplications/mod_skel/mod_skel.c create mode 100644 src/mod/codec/mod_codec_g729/Makefile create mode 100644 src/mod/codec/mod_codec_g729/mod_codec_g729.c create mode 100644 src/mod/codec/mod_codec_g729/mod_codec_g729.vcproj create mode 100644 src/mod/codec/mod_g711codec/Makefile create mode 100644 src/mod/codec/mod_g711codec/g711.c create mode 100644 src/mod/codec/mod_g711codec/g711.h create mode 100644 src/mod/codec/mod_g711codec/mod_g711codec.c create mode 100644 src/mod/codec/mod_g711codec/mod_g711codec.vcproj create mode 100644 src/mod/codec/mod_rawaudio/mod_rawaudio.c create mode 100644 src/mod/codec/mod_rawaudio/mod_rawaudio.vcproj create mode 100644 src/mod/codec/mod_speexcodec/Makefile create mode 100644 src/mod/codec/mod_speexcodec/mod_speexcodec.c create mode 100644 src/mod/codec/mod_speexcodec/mod_speexcodec.vcproj create mode 100644 src/mod/dialplan/mod_dialplan_demo/mod_dialplan_demo.c create mode 100644 src/mod/dialplan/mod_dialplan_demo/mod_dialplan_demo.vcproj create mode 100644 src/mod/endpoints/mod_exosip/Makefile create mode 100644 src/mod/endpoints/mod_exosip/mod_exosip.c create mode 100644 src/mod/endpoints/mod_exosip/mod_exosip.vcproj create mode 100644 src/mod/endpoints/mod_exosip/mod_exosip_ccrtp.c create mode 100644 src/mod/endpoints/mod_exosip/mod_exosip_ucl.c create mode 100644 src/mod/endpoints/mod_iaxchan/Makefile create mode 100644 src/mod/endpoints/mod_iaxchan/mod_IaxChan.vcproj create mode 100644 src/mod/endpoints/mod_iaxchan/mod_iaxchan.c create mode 100644 src/mod/endpoints/mod_opalchan/Makefile create mode 100644 src/mod/endpoints/mod_opalchan/mod_opalchan.c create mode 100644 src/mod/endpoints/mod_portaudio/Makefile create mode 100644 src/mod/endpoints/mod_portaudio/mod_PortAudio.vcproj create mode 100644 src/mod/endpoints/mod_portaudio/mod_portaudio.c create mode 100644 src/mod/endpoints/mod_portaudio/pablio.c create mode 100644 src/mod/endpoints/mod_portaudio/pablio.h create mode 100644 src/mod/endpoints/mod_portaudio/ringbuffer.c create mode 100644 src/mod/endpoints/mod_portaudio/ringbuffer.h create mode 100644 src/mod/endpoints/mod_wanchan/mod_wanchan.c create mode 100644 src/mod/endpoints/mod_woomerachan/mod_woomerachan.c create mode 100644 src/mod/endpoints/mod_woomerachan/mod_woomerachan.vcproj create mode 100644 src/mod/event_handlers/mod_event_test/mod_event_test.c create mode 100644 src/mod/event_handlers/mod_event_test/mod_event_test.vcproj create mode 100644 src/mod/event_handlers/mod_xmpp_event/Makefile create mode 100644 src/mod/event_handlers/mod_xmpp_event/mod_xmpp_event.c create mode 100644 src/mod/event_handlers/mod_xmpp_event/mod_xmpp_event.vcproj create mode 100644 src/mod/formats/mod_sndfile/Makefile create mode 100644 src/mod/formats/mod_sndfile/mod_sndfile.c create mode 100644 src/mod/formats/mod_sndfile/mod_sndfilel.vcproj create mode 100644 src/mod/timers/mod_softtimer/mod_softtimer.c create mode 100644 src/mod/timers/mod_softtimer/mod_softtimer.vcproj diff --git a/src/mod/aplications/mod_bridgecall.c b/src/mod/aplications/mod_bridgecall.c new file mode 100644 index 0000000000..facdca0734 --- /dev/null +++ b/src/mod/aplications/mod_bridgecall.c @@ -0,0 +1,271 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * + * + * mod_bridgecall.c -- Channel Bridge Application Module + * + */ +#include +#include +#include +#include + + +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; +} diff --git a/src/mod/aplications/mod_bridgecall.vcproj b/src/mod/aplications/mod_bridgecall.vcproj new file mode 100644 index 0000000000..86366d09e7 --- /dev/null +++ b/src/mod/aplications/mod_bridgecall.vcproj @@ -0,0 +1,207 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mod/aplications/mod_playback/mod_playback.c b/src/mod/aplications/mod_playback/mod_playback.c new file mode 100644 index 0000000000..757f7e7906 --- /dev/null +++ b/src/mod/aplications/mod_playback/mod_playback.c @@ -0,0 +1,199 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * + * + * mod_playback.c -- Raw Audio File Streaming Application Module + * + */ +#include +#include +#include +#include + + +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) + + + + diff --git a/src/mod/aplications/mod_playback/mod_playback.vcproj b/src/mod/aplications/mod_playback/mod_playback.vcproj new file mode 100644 index 0000000000..a63395a01c --- /dev/null +++ b/src/mod/aplications/mod_playback/mod_playback.vcproj @@ -0,0 +1,207 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mod/aplications/mod_skel/mod_skel.c b/src/mod/aplications/mod_skel/mod_skel.c new file mode 100644 index 0000000000..21126d1f3e --- /dev/null +++ b/src/mod/aplications/mod_skel/mod_skel.c @@ -0,0 +1,54 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * + * + * mod_skel.c -- Framework Demo Module + * + */ +#include + +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; +} + diff --git a/src/mod/codec/mod_codec_g729/Makefile b/src/mod/codec/mod_codec_g729/Makefile new file mode 100644 index 0000000000..6657b4c1fa --- /dev/null +++ b/src/mod/codec/mod_codec_g729/Makefile @@ -0,0 +1,12 @@ +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 *~ + diff --git a/src/mod/codec/mod_codec_g729/mod_codec_g729.c b/src/mod/codec/mod_codec_g729/mod_codec_g729.c new file mode 100644 index 0000000000..e85d461ea1 --- /dev/null +++ b/src/mod/codec/mod_codec_g729/mod_codec_g729.c @@ -0,0 +1,203 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * Michael Jerris + * + * mod_codec_g729.c -- G729 Codec Module + * + */ +#include "switch.h" +#include "g729.h" + +static const char modname[] = "mod_codec_g729"; + +struct g729_context { + struct dec_state decoder_object; + struct cod_state encoder_object; +}; + +static switch_status switch_g729_init(switch_codec *codec, switch_codec_flag flags, const struct switch_codec_settings *codec_settings) +{ + struct g729_context *context = NULL; + int encoding, decoding; + + encoding = (flags & SWITCH_CODEC_FLAG_ENCODE); + decoding = (flags & SWITCH_CODEC_FLAG_DECODE); + + if (!(encoding || decoding) || (!(context = switch_core_alloc(codec->memory_pool, sizeof(struct g729_context))))) { + return SWITCH_STATUS_FALSE; + } else { + if (encoding) { + g729_init_coder(&context->encoder_object, 0); + } + if (decoding) { + g729_init_decoder(&context->decoder_object); + } + + codec->private = context; + + return SWITCH_STATUS_SUCCESS; + } +} + +static switch_status switch_g729_destroy(switch_codec *codec) +{ + codec->private = NULL; + return SWITCH_STATUS_SUCCESS; +} + + +static switch_status switch_g729_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 g729_context *context = codec->private; + int cbret = 0; + + if (!context) { + return SWITCH_STATUS_FALSE; + } + if (decoded_data_len % 160 == 0) { + unsigned int new_len = 0; + INT16 *ddp = decoded_data; + char *edp = encoded_data; + int x; + int loops = (int) decoded_data_len / 160; + + for(x = 0; x < loops && new_len < *encoded_data_len; x++) { + g729_coder(&context->encoder_object, ddp, edp, &cbret); + edp += 10; + ddp += 80; + new_len += 10; + } + if( new_len <= *encoded_data_len ) { + *encoded_data_len = new_len; + } else { + switch_console_printf(SWITCH_CHANNEL_CONSOLE, "buffer overflow!!! %u >= %u\n", new_len, *encoded_data_len); + return SWITCH_STATUS_FALSE; + } + } + return SWITCH_STATUS_SUCCESS; +} + + +static switch_status switch_g729_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 g729_context *context = codec->private; + + if (!context) { + return SWITCH_STATUS_FALSE; + } + + + if (encoded_data_len % 10 == 0) { + int loops = (int) encoded_data_len / 10; + char *edp = encoded_data; + short *ddp = decoded_data; + int x; + unsigned int new_len = 0; + for(x = 0; x < loops && new_len < *decoded_data_len; x++) { + g729_decoder(&context->decoder_object, ddp, edp, 10); + ddp += 80; + edp += 10; + new_len += 160; + } + if (new_len <= *decoded_data_len) { + *decoded_data_len = new_len; + } else { + switch_console_printf(SWITCH_CHANNEL_CONSOLE, "buffer overflow!!!\n"); + return SWITCH_STATUS_FALSE; + } + } else { + switch_console_printf(SWITCH_CHANNEL_CONSOLE, "yo this frame is an odd size [%d]\n", encoded_data_len); + } + + + return SWITCH_STATUS_SUCCESS; +} + +/* Registration */ + +static const switch_codec_implementation g729_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*/ 20, + /*.number_of_channels*/ 1, + /*.pref_frames_per_packet*/ 1, + /*.max_frames_per_packet*/ 24, + /*.init*/ switch_g729_init, + /*.encode*/ switch_g729_encode, + /*.decode*/ switch_g729_decode, + /*.destroy*/ switch_g729_destroy, +}; + +static const switch_codec_interface g729_codec_interface = { + /*.interface_name*/ "g729", + /*.codec_type*/ SWITCH_CODEC_TYPE_AUDIO, + /*.ianacode*/ 18, + /*.iananame*/ "G729", + /*.implementations*/ &g729_8k_implementation, +}; + +static switch_loadable_module_interface g729_module_interface = { + /*.module_name*/ modname, + /*.endpoint_interface*/ NULL, + /*.timer_interface*/ NULL, + /*.dialplan_interface*/ NULL, + /*.codec_interface*/ &g729_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 = &g729_module_interface; + + /* indicate that the module should continue to be loaded */ + return SWITCH_STATUS_SUCCESS; +} + + + + + diff --git a/src/mod/codec/mod_codec_g729/mod_codec_g729.vcproj b/src/mod/codec/mod_codec_g729/mod_codec_g729.vcproj new file mode 100644 index 0000000000..9b7ecdaa62 --- /dev/null +++ b/src/mod/codec/mod_codec_g729/mod_codec_g729.vcproj @@ -0,0 +1,211 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mod/codec/mod_g711codec/Makefile b/src/mod/codec/mod_g711codec/Makefile new file mode 100644 index 0000000000..60ab1c465c --- /dev/null +++ b/src/mod/codec/mod_g711codec/Makefile @@ -0,0 +1,15 @@ + +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 *~ + diff --git a/src/mod/codec/mod_g711codec/g711.c b/src/mod/codec/mod_g711codec/g711.c new file mode 100644 index 0000000000..b69eb4f912 --- /dev/null +++ b/src/mod/codec/mod_g711codec/g711.c @@ -0,0 +1,312 @@ +/* + * 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 ----------------------------------------------------- */ diff --git a/src/mod/codec/mod_g711codec/g711.h b/src/mod/codec/mod_g711codec/g711.h new file mode 100644 index 0000000000..015c23267c --- /dev/null +++ b/src/mod/codec/mod_g711codec/g711.h @@ -0,0 +1,60 @@ +/* + * 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_ */ diff --git a/src/mod/codec/mod_g711codec/mod_g711codec.c b/src/mod/codec/mod_g711codec/mod_g711codec.c new file mode 100644 index 0000000000..e1cd6fce69 --- /dev/null +++ b/src/mod/codec/mod_g711codec/mod_g711codec.c @@ -0,0 +1,303 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * + * + * mod_g711codec.c -- G711 Ulaw/Alaw Codec Module + * + */ +#include +#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; +} + + + + + diff --git a/src/mod/codec/mod_g711codec/mod_g711codec.vcproj b/src/mod/codec/mod_g711codec/mod_g711codec.vcproj new file mode 100644 index 0000000000..2d79448078 --- /dev/null +++ b/src/mod/codec/mod_g711codec/mod_g711codec.vcproj @@ -0,0 +1,215 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mod/codec/mod_rawaudio/mod_rawaudio.c b/src/mod/codec/mod_rawaudio/mod_rawaudio.c new file mode 100644 index 0000000000..7dca8096a4 --- /dev/null +++ b/src/mod/codec/mod_rawaudio/mod_rawaudio.c @@ -0,0 +1,215 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * + * + * mod_rawaudio.c -- Raw Signed Linear Codec + * + */ +#include +#include + +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; +} + + + + + diff --git a/src/mod/codec/mod_rawaudio/mod_rawaudio.vcproj b/src/mod/codec/mod_rawaudio/mod_rawaudio.vcproj new file mode 100644 index 0000000000..2fb53179c8 --- /dev/null +++ b/src/mod/codec/mod_rawaudio/mod_rawaudio.vcproj @@ -0,0 +1,211 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mod/codec/mod_speexcodec/Makefile b/src/mod/codec/mod_speexcodec/Makefile new file mode 100644 index 0000000000..396db30128 --- /dev/null +++ b/src/mod/codec/mod_speexcodec/Makefile @@ -0,0 +1,12 @@ +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 *~ + diff --git a/src/mod/codec/mod_speexcodec/mod_speexcodec.c b/src/mod/codec/mod_speexcodec/mod_speexcodec.c new file mode 100644 index 0000000000..d20d646cab --- /dev/null +++ b/src/mod/codec/mod_speexcodec/mod_speexcodec.c @@ -0,0 +1,342 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * + * + * mod_speexcodec.c -- Speex Codec Module + * + */ +#include +#include +#include + +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; +} diff --git a/src/mod/codec/mod_speexcodec/mod_speexcodec.vcproj b/src/mod/codec/mod_speexcodec/mod_speexcodec.vcproj new file mode 100644 index 0000000000..0abb26ac7c --- /dev/null +++ b/src/mod/codec/mod_speexcodec/mod_speexcodec.vcproj @@ -0,0 +1,211 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mod/dialplan/mod_dialplan_demo/mod_dialplan_demo.c b/src/mod/dialplan/mod_dialplan_demo/mod_dialplan_demo.c new file mode 100644 index 0000000000..c071f613bb --- /dev/null +++ b/src/mod/dialplan/mod_dialplan_demo/mod_dialplan_demo.c @@ -0,0 +1,124 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * + * + * mod_dialplan_demo.c -- Example Dialplan Module + * + */ +#include +#include +#include +#include + + +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; +} diff --git a/src/mod/dialplan/mod_dialplan_demo/mod_dialplan_demo.vcproj b/src/mod/dialplan/mod_dialplan_demo/mod_dialplan_demo.vcproj new file mode 100644 index 0000000000..ecf5b98985 --- /dev/null +++ b/src/mod/dialplan/mod_dialplan_demo/mod_dialplan_demo.vcproj @@ -0,0 +1,207 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mod/endpoints/mod_exosip/Makefile b/src/mod/endpoints/mod_exosip/Makefile new file mode 100644 index 0000000000..5e8906382f --- /dev/null +++ b/src/mod/endpoints/mod_exosip/Makefile @@ -0,0 +1,26 @@ +#CFLAGS += -I/usr/src/common/src +LDFLAGS += -leXosip2 -ljrtp4c + +ifeq ($(OSARCH),Darwin) + LINKER=g++ +else + LINKER=$(CC) +endif + +all: depends $(MOD).so + +depends: + $(BASE)/buildlib.sh $(BASE) install jthread-1.1.2.tar.gz --prefix=$(PREFIX) + $(BASE)/buildlib.sh $(BASE) install jrtplib-3.3.0.tar.gz --prefix=$(PREFIX) + $(BASE)/buildlib.sh $(BASE) install jrtp4c --prefix=$(PREFIX) + $(BASE)/buildlib.sh $(BASE) install libosip2-2.2.2.tar.gz --prefix=$(PREFIX) + $(BASE)/buildlib.sh $(BASE) install libeXosip2-2.2.2.tar.gz --disable-josua --prefix=$(PREFIX) + + +$(MOD).so: $(MOD).c + $(CC) $(CFLAGS) -fPIC -c $(MOD).c -o $(MOD).o + $(LINKER) $(SOLINK) -o $(MOD).so $(MOD).o $(LDFLAGS) + +clean: + rm -fr *.so *.o *~ + diff --git a/src/mod/endpoints/mod_exosip/mod_exosip.c b/src/mod/endpoints/mod_exosip/mod_exosip.c new file mode 100644 index 0000000000..972797c379 --- /dev/null +++ b/src/mod/endpoints/mod_exosip/mod_exosip.c @@ -0,0 +1,1409 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * + * + * mod_exosip.c -- eXoSIP SIP Endpoint + * + */ + +#define HAVE_APR +#include +#include +#include +#include +#include +#include + + +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), "", 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; +} + diff --git a/src/mod/endpoints/mod_exosip/mod_exosip.vcproj b/src/mod/endpoints/mod_exosip/mod_exosip.vcproj new file mode 100644 index 0000000000..014a0e56fb --- /dev/null +++ b/src/mod/endpoints/mod_exosip/mod_exosip.vcproj @@ -0,0 +1,229 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mod/endpoints/mod_exosip/mod_exosip_ccrtp.c b/src/mod/endpoints/mod_exosip/mod_exosip_ccrtp.c new file mode 100644 index 0000000000..4d895ca0fc --- /dev/null +++ b/src/mod/endpoints/mod_exosip/mod_exosip_ccrtp.c @@ -0,0 +1,1373 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * + * + * mod_exosip.c -- eXoSIP SIP Endpoint + * + */ + + +#define HAVE_APR +#include +#include +#include +#include +#include +#include + + +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), "", 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; +} + diff --git a/src/mod/endpoints/mod_exosip/mod_exosip_ucl.c b/src/mod/endpoints/mod_exosip/mod_exosip_ucl.c new file mode 100644 index 0000000000..54c3b0364c --- /dev/null +++ b/src/mod/endpoints/mod_exosip/mod_exosip_ucl.c @@ -0,0 +1,1438 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * + * + * mod_exosip.c -- eXoSIP SIP Endpoint + * + */ + + +#define HAVE_APR + +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include + + + +//#include +//#include +//#include +//#include +//#include + +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), "", 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; +} + diff --git a/src/mod/endpoints/mod_iaxchan/Makefile b/src/mod/endpoints/mod_iaxchan/Makefile new file mode 100644 index 0000000000..6e4f38205a --- /dev/null +++ b/src/mod/endpoints/mod_iaxchan/Makefile @@ -0,0 +1,15 @@ +LDFLAGS += -liax -L/usr/local/lib + +all: depends $(MOD).so + +depends: + $(BASE)/buildlib.sh $(BASE) install iax --enable-newjb --prefix=$(PREFIX) + +$(MOD).so: $(MOD).c + $(CC) $(CFLAGS) -fPIC -c $(MOD).c -o $(MOD).o + $(CC) $(SOLINK) -o $(MOD).so $(MOD).o $(LDFLAGS) + + +clean: + rm -fr *.so *.o *~ + diff --git a/src/mod/endpoints/mod_iaxchan/mod_IaxChan.vcproj b/src/mod/endpoints/mod_iaxchan/mod_IaxChan.vcproj new file mode 100644 index 0000000000..3b0a8f4d62 --- /dev/null +++ b/src/mod/endpoints/mod_iaxchan/mod_IaxChan.vcproj @@ -0,0 +1,212 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mod/endpoints/mod_iaxchan/mod_iaxchan.c b/src/mod/endpoints/mod_iaxchan/mod_iaxchan.c new file mode 100644 index 0000000000..b3faf65c5e --- /dev/null +++ b/src/mod/endpoints/mod_iaxchan/mod_iaxchan.c @@ -0,0 +1,986 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * + * + * mod_iaxchan.c -- IAX2 Endpoint Module + * + */ +#include + +#ifdef WIN32 +#include +#include +#include +#include +#else +#include +#include +#include +#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; +} + diff --git a/src/mod/endpoints/mod_opalchan/Makefile b/src/mod/endpoints/mod_opalchan/Makefile new file mode 100644 index 0000000000..709a5b7bc5 --- /dev/null +++ b/src/mod/endpoints/mod_opalchan/Makefile @@ -0,0 +1,12 @@ +#CFLAGS += -I/usr/src/common/src +LDFLAGS += -liax + +all: $(MOD).so + +$(MOD).so: $(MOD).c + $(CC) $(CFLAGS) -fPIC -c $(MOD).c -o $(MOD).o + $(CC) $(SOLINK) -o $(MOD).so $(MOD).o $(LDFLAGS) + +clean: + rm -fr *.so *.o *~ + diff --git a/src/mod/endpoints/mod_opalchan/mod_opalchan.c b/src/mod/endpoints/mod_opalchan/mod_opalchan.c new file mode 100644 index 0000000000..8aae7ecb7d --- /dev/null +++ b/src/mod/endpoints/mod_opalchan/mod_opalchan.c @@ -0,0 +1,385 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * + * + * mod_opalchan.c -- OPAL2 Endpoint Module + * + */ +#include + + +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; +} diff --git a/src/mod/endpoints/mod_portaudio/Makefile b/src/mod/endpoints/mod_portaudio/Makefile new file mode 100644 index 0000000000..0d78e0b06a --- /dev/null +++ b/src/mod/endpoints/mod_portaudio/Makefile @@ -0,0 +1,19 @@ +LDFLAGS += -lportaudio -L/usr/local/lib +MYOBJS = pablio.o ringbuffer.o + +all: depends $(MOD).so + +depends: + $(BASE)/buildlib.sh $(BASE) install portaudio.tar.gz --prefix=$(PREFIX) +%.o: %.c + $(CC) -fPIC $(CFLAGS) -c -o $@ $< + +$(MOD).so: $(MOD).c $(MYOBJS) + $(CC) $(CFLAGS) -fPIC -c $(MOD).c -o $(MOD).o + $(CC) $(SOLINK) -o $(MOD).so $(MOD).o $(MYOBJS) $(LDFLAGS) + + + +clean: + rm -fr *.so *.o *~ + diff --git a/src/mod/endpoints/mod_portaudio/mod_PortAudio.vcproj b/src/mod/endpoints/mod_portaudio/mod_PortAudio.vcproj new file mode 100644 index 0000000000..5afdc338d3 --- /dev/null +++ b/src/mod/endpoints/mod_portaudio/mod_PortAudio.vcproj @@ -0,0 +1,228 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mod/endpoints/mod_portaudio/mod_portaudio.c b/src/mod/endpoints/mod_portaudio/mod_portaudio.c new file mode 100644 index 0000000000..51e7ec29bd --- /dev/null +++ b/src/mod/endpoints/mod_portaudio/mod_portaudio.c @@ -0,0 +1,934 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * + * + * mod_portaudio.c -- PortAudio Endpoint Module + * + */ +#include + +#include +#include +#include +#include "pablio.h" +#include + +#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; iname, 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; iname ); + 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; jnumSampleRates; 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 ", 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; +} diff --git a/src/mod/endpoints/mod_portaudio/pablio.c b/src/mod/endpoints/mod_portaudio/pablio.c new file mode 100644 index 0000000000..b5735daea0 --- /dev/null +++ b/src/mod/endpoints/mod_portaudio/pablio.c @@ -0,0 +1,327 @@ +/* + * $Id: pablio.c,v 1.1.1.1.4.4 2003/03/13 17:28:33 philburk Exp $ + * pablio.c + * Portable Audio Blocking Input/Output utility. + * + * Author: Phil Burk, http://www.softsynth.com + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.audiomulch.com/portaudio/ + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +/* History: + * PLB021214 - check for valid stream in CloseAudioStream() to prevent hang. + * add timeOutMSec to CloseAudioStream() to prevent hang. + */ +#include +#include +#include +#include "portaudio.h" +#include "ringbuffer.h" +#include "pablio.h" +#include + +/************************************************************************/ +/******** Constants *****************************************************/ +/************************************************************************/ + +#define FRAMES_PER_BUFFER (256) + +/************************************************************************/ +/******** Prototypes ****************************************************/ +/************************************************************************/ + +static int blockingIOCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ); +static PaError PABLIO_InitFIFO( RingBuffer *rbuf, long numFrames, long bytesPerFrame ); +static PaError PABLIO_TermFIFO( RingBuffer *rbuf ); + +/************************************************************************/ +/******** Functions *****************************************************/ +/************************************************************************/ + +/* Called from PortAudio. + * Read and write data only if there is room in FIFOs. + */ +static int blockingIOCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + PABLIO_Stream *data = (PABLIO_Stream*)userData; + long numBytes = data->bytesPerFrame * framesPerBuffer; + (void) outTime; + + /* This may get called with NULL inputBuffer during initial setup. */ + if( inputBuffer != NULL ) + { + RingBuffer_Write( &data->inFIFO, inputBuffer, numBytes ); + } + if( outputBuffer != NULL ) + { + int i; + int numRead = RingBuffer_Read( &data->outFIFO, outputBuffer, numBytes ); + /* Zero out remainder of buffer if we run out of data. */ + for( i=numRead; ibuffer ) free( rbuf->buffer ); + rbuf->buffer = NULL; + return paNoError; +} + +/************************************************************ + * Write data to ring buffer. + * Will not return until all the data has been written. + */ +long WriteAudioStream( PABLIO_Stream *aStream, void *data, long numFrames ) +{ + long bytesWritten; + char *p = (char *) data; + long numBytes = aStream->bytesPerFrame * numFrames; + while( numBytes > 0) + { + bytesWritten = RingBuffer_Write( &aStream->outFIFO, p, numBytes ); + numBytes -= bytesWritten; + p += bytesWritten; + if( numBytes > 0) Pa_Sleep(10); + } + return numFrames; +} + +/************************************************************ + * Read data from ring buffer. + * Will not return until all the data has been read. + */ +long ReadAudioStream( PABLIO_Stream *aStream, void *data, long numFrames ) +{ + long bytesRead; + char *p = (char *) data; + long numBytes = aStream->bytesPerFrame * numFrames; + while( numBytes > 0) + { + bytesRead = RingBuffer_Read( &aStream->inFIFO, p, numBytes ); + numBytes -= bytesRead; + p += bytesRead; + if( numBytes > 0) Pa_Sleep(10); + } + return numFrames; +} + +/************************************************************ + * Return the number of frames that could be written to the stream without + * having to wait. + */ +long GetAudioStreamWriteable( PABLIO_Stream *aStream ) +{ + int bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO ); + return bytesEmpty / aStream->bytesPerFrame; +} + +/************************************************************ + * Return the number of frames that are available to be read from the + * stream without having to wait. + */ +long GetAudioStreamReadable( PABLIO_Stream *aStream ) +{ + int bytesFull = RingBuffer_GetReadAvailable( &aStream->inFIFO ); + return bytesFull / aStream->bytesPerFrame; +} + +/************************************************************/ +static unsigned long RoundUpToNextPowerOf2( unsigned long n ) +{ + long numBits = 0; + if( ((n-1) & n) == 0) return n; /* Already Power of two. */ + while( n > 0 ) + { + n= n>>1; + numBits++; + } + return (1<samplesPerFrame = ((flags&PABLIO_MONO) != 0) ? 1 : 2; + aStream->bytesPerFrame = bytesPerSample * aStream->samplesPerFrame; + + /* Initialize PortAudio */ + err = Pa_Initialize(); + if( err != paNoError ) goto error; + + /* Warning: numFrames must be larger than amount of data processed per interrupt + * inside PA to prevent glitches. Just to be safe, adjust size upwards. + */ + minNumBuffers = 2 * Pa_GetMinNumBuffers( FRAMES_PER_BUFFER, sampleRate ); + numFrames = minNumBuffers * FRAMES_PER_BUFFER; + /* The PortAudio callback runs in a high priority thread. But PABLIO + * runs in a normal foreground thread. So we may have much worse + * latency in PABLIO. So adjust latency to a safe level. + */ + { + const int safeLatencyMSec = 200; + int minLatencyMSec = (int) ((1000 * numFrames) / sampleRate); + if( minLatencyMSec < safeLatencyMSec ) + { + numFrames = (int) ((safeLatencyMSec * sampleRate) / 1000); + } + } + numFrames = RoundUpToNextPowerOf2( numFrames ); + + /* Initialize Ring Buffers */ + doRead = ((flags & PABLIO_READ) != 0); + doWrite = ((flags & PABLIO_WRITE) != 0); + if(doRead) + { + err = PABLIO_InitFIFO( &aStream->inFIFO, numFrames, aStream->bytesPerFrame ); + if( err != paNoError ) goto error; + } + if(doWrite) + { + long numBytes; + err = PABLIO_InitFIFO( &aStream->outFIFO, numFrames, aStream->bytesPerFrame ); + if( err != paNoError ) goto error; + /* Make Write FIFO appear full initially. */ + numBytes = RingBuffer_GetWriteAvailable( &aStream->outFIFO ); + RingBuffer_AdvanceWriteIndex( &aStream->outFIFO, numBytes ); + } + + /* Open a PortAudio stream that we will use to communicate with the underlying + * audio drivers. */ + err = Pa_OpenStream( + &aStream->stream, + (doRead ? (indev > -1) ? indev : Pa_GetDefaultInputDeviceID() : paNoDevice), + (doRead ? aStream->samplesPerFrame : 0 ), + format, + NULL, + (doWrite ? (outdev > -1) ? outdev : Pa_GetDefaultOutputDeviceID() : paNoDevice), + (doWrite ? aStream->samplesPerFrame : 0 ), + format, + NULL, + sampleRate, + FRAMES_PER_BUFFER, + minNumBuffers, + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + blockingIOCallback, + aStream ); + if( err != paNoError ) goto error; + + err = Pa_StartStream( aStream->stream ); + if( err != paNoError ) goto error; + + *rwblPtr = aStream; + return paNoError; + +error: + CloseAudioStream( aStream ); + *rwblPtr = NULL; + return err; +} + +/************************************************************/ +PaError CloseAudioStream( PABLIO_Stream *aStream ) +{ + PaError err = paNoError; + int bytesEmpty; + int byteSize = aStream->outFIFO.bufferSize; + + if( aStream->stream != NULL ) /* Make sure stream was opened. PLB021214 */ + { + /* If we are writing data, make sure we play everything written. */ + if( byteSize > 0 ) + { + int timeOutMSec = 2000; + bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO ); + while( (bytesEmpty < byteSize) && (timeOutMSec > 0) ) + { + Pa_Sleep( 20 ); + timeOutMSec -= 20; + bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO ); + } + } + err = Pa_StopStream( aStream->stream ); + if( err != paNoError ) goto error; + err = Pa_CloseStream( aStream->stream ); + } + +error: + Pa_Terminate(); + PABLIO_TermFIFO( &aStream->inFIFO ); + PABLIO_TermFIFO( &aStream->outFIFO ); + free( aStream ); + return err; +} diff --git a/src/mod/endpoints/mod_portaudio/pablio.h b/src/mod/endpoints/mod_portaudio/pablio.h new file mode 100644 index 0000000000..25bccf8998 --- /dev/null +++ b/src/mod/endpoints/mod_portaudio/pablio.h @@ -0,0 +1,109 @@ +#ifndef _PABLIO_H +#define _PABLIO_H + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/* + * $Id: pablio.h,v 1.1.1.1 2002/01/22 00:52:53 phil Exp $ + * PABLIO.h + * Portable Audio Blocking read/write utility. + * + * Author: Phil Burk, http://www.softsynth.com/portaudio/ + * + * Include file for PABLIO, the Portable Audio Blocking I/O Library. + * PABLIO is built on top of PortAudio, the Portable Audio Library. + * For more information see: http://www.audiomulch.com/portaudio/ + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include +#include "portaudio.h" +#include "ringbuffer.h" +#include + +typedef struct +{ + RingBuffer inFIFO; + RingBuffer outFIFO; + PortAudioStream *stream; + int bytesPerFrame; + int samplesPerFrame; +} +PABLIO_Stream; + +/* Values for flags for OpenAudioStream(). */ +#define PABLIO_READ (1<<0) +#define PABLIO_WRITE (1<<1) +#define PABLIO_READ_WRITE (PABLIO_READ|PABLIO_WRITE) +#define PABLIO_MONO (1<<2) +#define PABLIO_STEREO (1<<3) + +/************************************************************ + * Write data to ring buffer. + * Will not return until all the data has been written. + */ +long WriteAudioStream( PABLIO_Stream *aStream, void *data, long numFrames ); + +/************************************************************ + * Read data from ring buffer. + * Will not return until all the data has been read. + */ +long ReadAudioStream( PABLIO_Stream *aStream, void *data, long numFrames ); + +/************************************************************ + * Return the number of frames that could be written to the stream without + * having to wait. + */ +long GetAudioStreamWriteable( PABLIO_Stream *aStream ); + +/************************************************************ + * Return the number of frames that are available to be read from the + * stream without having to wait. + */ +long GetAudioStreamReadable( PABLIO_Stream *aStream ); + +/************************************************************ + * Opens a PortAudio stream with default characteristics. + * Allocates PABLIO_Stream structure. + * + * flags parameter can be an ORed combination of: + * PABLIO_READ, PABLIO_WRITE, or PABLIO_READ_WRITE, + * and either PABLIO_MONO or PABLIO_STEREO + */ +PaError OpenAudioStream( PABLIO_Stream **aStreamPtr, double sampleRate, + PaSampleFormat format, long flags, int indev, int outdev ); + +PaError CloseAudioStream( PABLIO_Stream *aStream ); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* _PABLIO_H */ diff --git a/src/mod/endpoints/mod_portaudio/ringbuffer.c b/src/mod/endpoints/mod_portaudio/ringbuffer.c new file mode 100644 index 0000000000..867b501965 --- /dev/null +++ b/src/mod/endpoints/mod_portaudio/ringbuffer.c @@ -0,0 +1,199 @@ +/* + * $Id: ringbuffer.c,v 1.1.1.1 2002/01/22 00:52:53 phil Exp $ + * ringbuffer.c + * Ring Buffer utility.. + * + * Author: Phil Burk, http://www.softsynth.com + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.audiomulch.com/portaudio/ + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include +#include "ringbuffer.h" +#include + +/*************************************************************************** + * Initialize FIFO. + * numBytes must be power of 2, returns -1 if not. + */ +long RingBuffer_Init( RingBuffer *rbuf, long numBytes, void *dataPtr ) +{ + if( ((numBytes-1) & numBytes) != 0) return -1; /* Not Power of two. */ + rbuf->bufferSize = numBytes; + rbuf->buffer = (char *)dataPtr; + RingBuffer_Flush( rbuf ); + rbuf->bigMask = (numBytes*2)-1; + rbuf->smallMask = (numBytes)-1; + return 0; +} +/*************************************************************************** +** Return number of bytes available for reading. */ +long RingBuffer_GetReadAvailable( RingBuffer *rbuf ) +{ + return ( (rbuf->writeIndex - rbuf->readIndex) & rbuf->bigMask ); +} +/*************************************************************************** +** Return number of bytes available for writing. */ +long RingBuffer_GetWriteAvailable( RingBuffer *rbuf ) +{ + return ( rbuf->bufferSize - RingBuffer_GetReadAvailable(rbuf)); +} + +/*************************************************************************** +** Clear buffer. Should only be called when buffer is NOT being read. */ +void RingBuffer_Flush( RingBuffer *rbuf ) +{ + rbuf->writeIndex = rbuf->readIndex = 0; +} + +/*************************************************************************** +** Get address of region(s) to which we can write data. +** If the region is contiguous, size2 will be zero. +** If non-contiguous, size2 will be the size of second region. +** Returns room available to be written or numBytes, whichever is smaller. +*/ +long RingBuffer_GetWriteRegions( RingBuffer *rbuf, long numBytes, + void **dataPtr1, long *sizePtr1, + void **dataPtr2, long *sizePtr2 ) +{ + long index; + long available = RingBuffer_GetWriteAvailable( rbuf ); + if( numBytes > available ) numBytes = available; + /* Check to see if write is not contiguous. */ + index = rbuf->writeIndex & rbuf->smallMask; + if( (index + numBytes) > rbuf->bufferSize ) + { + /* Write data in two blocks that wrap the buffer. */ + long firstHalf = rbuf->bufferSize - index; + *dataPtr1 = &rbuf->buffer[index]; + *sizePtr1 = firstHalf; + *dataPtr2 = &rbuf->buffer[0]; + *sizePtr2 = numBytes - firstHalf; + } + else + { + *dataPtr1 = &rbuf->buffer[index]; + *sizePtr1 = numBytes; + *dataPtr2 = NULL; + *sizePtr2 = 0; + } + return numBytes; +} + + +/*************************************************************************** +*/ +long RingBuffer_AdvanceWriteIndex( RingBuffer *rbuf, long numBytes ) +{ + return rbuf->writeIndex = (rbuf->writeIndex + numBytes) & rbuf->bigMask; +} + +/*************************************************************************** +** Get address of region(s) from which we can read data. +** If the region is contiguous, size2 will be zero. +** If non-contiguous, size2 will be the size of second region. +** Returns room available to be written or numBytes, whichever is smaller. +*/ +long RingBuffer_GetReadRegions( RingBuffer *rbuf, long numBytes, + void **dataPtr1, long *sizePtr1, + void **dataPtr2, long *sizePtr2 ) +{ + long index; + long available = RingBuffer_GetReadAvailable( rbuf ); + if( numBytes > available ) numBytes = available; + /* Check to see if read is not contiguous. */ + index = rbuf->readIndex & rbuf->smallMask; + if( (index + numBytes) > rbuf->bufferSize ) + { + /* Write data in two blocks that wrap the buffer. */ + long firstHalf = rbuf->bufferSize - index; + *dataPtr1 = &rbuf->buffer[index]; + *sizePtr1 = firstHalf; + *dataPtr2 = &rbuf->buffer[0]; + *sizePtr2 = numBytes - firstHalf; + } + else + { + *dataPtr1 = &rbuf->buffer[index]; + *sizePtr1 = numBytes; + *dataPtr2 = NULL; + *sizePtr2 = 0; + } + return numBytes; +} +/*************************************************************************** +*/ +long RingBuffer_AdvanceReadIndex( RingBuffer *rbuf, long numBytes ) +{ + return rbuf->readIndex = (rbuf->readIndex + numBytes) & rbuf->bigMask; +} + +/*************************************************************************** +** Return bytes written. */ +long RingBuffer_Write( RingBuffer *rbuf, void *data, long numBytes ) +{ + long size1, size2, numWritten; + void *data1, *data2; + numWritten = RingBuffer_GetWriteRegions( rbuf, numBytes, &data1, &size1, &data2, &size2 ); + if( size2 > 0 ) + { + + memcpy( data1, data, size1 ); + data = ((char *)data) + size1; + memcpy( data2, data, size2 ); + } + else + { + memcpy( data1, data, size1 ); + } + RingBuffer_AdvanceWriteIndex( rbuf, numWritten ); + return numWritten; +} + +/*************************************************************************** +** Return bytes read. */ +long RingBuffer_Read( RingBuffer *rbuf, void *data, long numBytes ) +{ + long size1, size2, numRead; + void *data1, *data2; + numRead = RingBuffer_GetReadRegions( rbuf, numBytes, &data1, &size1, &data2, &size2 ); + if( size2 > 0 ) + { + memcpy( data, data1, size1 ); + data = ((char *)data) + size1; + memcpy( data, data2, size2 ); + } + else + { + memcpy( data, data1, size1 ); + } + RingBuffer_AdvanceReadIndex( rbuf, numRead ); + return numRead; +} diff --git a/src/mod/endpoints/mod_portaudio/ringbuffer.h b/src/mod/endpoints/mod_portaudio/ringbuffer.h new file mode 100644 index 0000000000..3cd559488b --- /dev/null +++ b/src/mod/endpoints/mod_portaudio/ringbuffer.h @@ -0,0 +1,103 @@ +#ifndef _RINGBUFFER_H +#define _RINGBUFFER_H +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/* + * $Id: ringbuffer.h,v 1.1.1.1.4.2 2003/04/28 17:45:34 philburk Exp $ + * ringbuffer.h + * Ring Buffer utility.. + * + * Author: Phil Burk, http://www.softsynth.com + * + * This program is distributed with the PortAudio Portable Audio Library. + * For more information see: http://www.audiomulch.com/portaudio/ + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#define PortAudioStream PaStream +#include +#include +#include +#include "ringbuffer.h" +#include + +typedef struct +{ + long bufferSize; /* Number of bytes in FIFO. Power of 2. Set by RingBuffer_Init. */ +/* These are declared volatile because they are written by a different thread than the reader. */ + volatile long writeIndex; /* Index of next writable byte. Set by RingBuffer_AdvanceWriteIndex. */ + volatile long readIndex; /* Index of next readable byte. Set by RingBuffer_AdvanceReadIndex. */ + long bigMask; /* Used for wrapping indices with extra bit to distinguish full/empty. */ + long smallMask; /* Used for fitting indices to buffer. */ + char *buffer; +} +RingBuffer; +/* + * Initialize Ring Buffer. + * numBytes must be power of 2, returns -1 if not. + */ +long RingBuffer_Init( RingBuffer *rbuf, long numBytes, void *dataPtr ); + +/* Clear buffer. Should only be called when buffer is NOT being read. */ +void RingBuffer_Flush( RingBuffer *rbuf ); + +/* Return number of bytes available for writing. */ +long RingBuffer_GetWriteAvailable( RingBuffer *rbuf ); +/* Return number of bytes available for read. */ +long RingBuffer_GetReadAvailable( RingBuffer *rbuf ); +/* Return bytes written. */ +long RingBuffer_Write( RingBuffer *rbuf, void *data, long numBytes ); +/* Return bytes read. */ +long RingBuffer_Read( RingBuffer *rbuf, void *data, long numBytes ); + +/* Get address of region(s) to which we can write data. +** If the region is contiguous, size2 will be zero. +** If non-contiguous, size2 will be the size of second region. +** Returns room available to be written or numBytes, whichever is smaller. +*/ +long RingBuffer_GetWriteRegions( RingBuffer *rbuf, long numBytes, + void **dataPtr1, long *sizePtr1, + void **dataPtr2, long *sizePtr2 ); +long RingBuffer_AdvanceWriteIndex( RingBuffer *rbuf, long numBytes ); + +/* Get address of region(s) from which we can read data. +** If the region is contiguous, size2 will be zero. +** If non-contiguous, size2 will be the size of second region. +** Returns room available to be read or numBytes, whichever is smaller. +*/ +long RingBuffer_GetReadRegions( RingBuffer *rbuf, long numBytes, + void **dataPtr1, long *sizePtr1, + void **dataPtr2, long *sizePtr2 ); + +long RingBuffer_AdvanceReadIndex( RingBuffer *rbuf, long numBytes ); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* _RINGBUFFER_H */ diff --git a/src/mod/endpoints/mod_wanchan/mod_wanchan.c b/src/mod/endpoints/mod_wanchan/mod_wanchan.c new file mode 100644 index 0000000000..3fd8080084 --- /dev/null +++ b/src/mod/endpoints/mod_wanchan/mod_wanchan.c @@ -0,0 +1,634 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * + * + * mod_wanchan.c -- WANPIPE PRI Channel Module + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; +} + diff --git a/src/mod/endpoints/mod_woomerachan/mod_woomerachan.c b/src/mod/endpoints/mod_woomerachan/mod_woomerachan.c new file mode 100644 index 0000000000..9ecbf59f11 --- /dev/null +++ b/src/mod/endpoints/mod_woomerachan/mod_woomerachan.c @@ -0,0 +1,1387 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * + * + * mod_woomerachan.c -- Woomera Endpoint Module + * + */ +#include + +#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; +} + + + diff --git a/src/mod/endpoints/mod_woomerachan/mod_woomerachan.vcproj b/src/mod/endpoints/mod_woomerachan/mod_woomerachan.vcproj new file mode 100644 index 0000000000..2d89ed1f2f --- /dev/null +++ b/src/mod/endpoints/mod_woomerachan/mod_woomerachan.vcproj @@ -0,0 +1,207 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mod/event_handlers/mod_event_test/mod_event_test.c b/src/mod/event_handlers/mod_event_test/mod_event_test.c new file mode 100644 index 0000000000..58c406c719 --- /dev/null +++ b/src/mod/event_handlers/mod_event_test/mod_event_test.c @@ -0,0 +1,133 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * + * + * mod_event_test.c -- Framework Demo Module + * + */ +#include + +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; +} + diff --git a/src/mod/event_handlers/mod_event_test/mod_event_test.vcproj b/src/mod/event_handlers/mod_event_test/mod_event_test.vcproj new file mode 100644 index 0000000000..6952239ce6 --- /dev/null +++ b/src/mod/event_handlers/mod_event_test/mod_event_test.vcproj @@ -0,0 +1,207 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mod/event_handlers/mod_xmpp_event/Makefile b/src/mod/event_handlers/mod_xmpp_event/Makefile new file mode 100644 index 0000000000..e99f9650eb --- /dev/null +++ b/src/mod/event_handlers/mod_xmpp_event/Makefile @@ -0,0 +1,14 @@ +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 *~ + diff --git a/src/mod/event_handlers/mod_xmpp_event/mod_xmpp_event.c b/src/mod/event_handlers/mod_xmpp_event/mod_xmpp_event.c new file mode 100644 index 0000000000..6f983723d6 --- /dev/null +++ b/src/mod/event_handlers/mod_xmpp_event/mod_xmpp_event.c @@ -0,0 +1,386 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * + * + * mod_xmpp_event.c -- XMPP Event Logger + * + */ +#include +#include + +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; +} diff --git a/src/mod/event_handlers/mod_xmpp_event/mod_xmpp_event.vcproj b/src/mod/event_handlers/mod_xmpp_event/mod_xmpp_event.vcproj new file mode 100644 index 0000000000..fa9167be6a --- /dev/null +++ b/src/mod/event_handlers/mod_xmpp_event/mod_xmpp_event.vcproj @@ -0,0 +1,212 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mod/formats/mod_sndfile/Makefile b/src/mod/formats/mod_sndfile/Makefile new file mode 100644 index 0000000000..1056e8fdd8 --- /dev/null +++ b/src/mod/formats/mod_sndfile/Makefile @@ -0,0 +1,15 @@ +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 *~ + diff --git a/src/mod/formats/mod_sndfile/mod_sndfile.c b/src/mod/formats/mod_sndfile/mod_sndfile.c new file mode 100644 index 0000000000..98a4f7dc25 --- /dev/null +++ b/src/mod/formats/mod_sndfile/mod_sndfile.c @@ -0,0 +1,294 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * + * + * mod_sndfile.c -- Framework Demo Module + * + */ +#include +#include + +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; +} + diff --git a/src/mod/formats/mod_sndfile/mod_sndfilel.vcproj b/src/mod/formats/mod_sndfile/mod_sndfilel.vcproj new file mode 100644 index 0000000000..79db6aea16 --- /dev/null +++ b/src/mod/formats/mod_sndfile/mod_sndfilel.vcproj @@ -0,0 +1,212 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mod/timers/mod_softtimer/mod_softtimer.c b/src/mod/timers/mod_softtimer/mod_softtimer.c new file mode 100644 index 0000000000..c379436ab3 --- /dev/null +++ b/src/mod/timers/mod_softtimer/mod_softtimer.c @@ -0,0 +1,128 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * + * + * mod_softtimer.c -- Software Timer Module + * + */ +#include +#include + +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; +} + + + + + diff --git a/src/mod/timers/mod_softtimer/mod_softtimer.vcproj b/src/mod/timers/mod_softtimer/mod_softtimer.vcproj new file mode 100644 index 0000000000..b268d2fe5d --- /dev/null +++ b/src/mod/timers/mod_softtimer/mod_softtimer.vcproj @@ -0,0 +1,207 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +