diff --git a/conf/autoload_configs/portaudio.conf.xml b/conf/autoload_configs/portaudio.conf.xml index 1f758de896..02c21ef449 100644 --- a/conf/autoload_configs/portaudio.conf.xml +++ b/conf/autoload_configs/portaudio.conf.xml @@ -32,4 +32,187 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/conf/lang/en/dir/sounds.xml b/conf/lang/en/dir/sounds.xml index 15ecec304a..85dd01c599 100644 --- a/conf/lang/en/dir/sounds.xml +++ b/conf/lang/en/dir/sounds.xml @@ -94,16 +94,16 @@ - + - + - + - + diff --git a/conf/lang/en/en.xml b/conf/lang/en/en.xml index 1e23dc5d7b..2ccd513823 100644 --- a/conf/lang/en/en.xml +++ b/conf/lang/en/en.xml @@ -6,6 +6,7 @@ + diff --git a/debian/changelog b/debian/changelog index 35a8787d38..abfe17e081 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +freeswitch (1.0.head-git.master.20110530.1-1) unstable; urgency=low + + * added mod_cdr_sqlite + + -- Michal Bielicki Mon, 30 May 2011 16:02:02 +0200 + freeswitch (1.0.head-git.master.20110402.1-1) unstable; urgency=low * Added Hebrew lang package diff --git a/debian/freeswitch.install b/debian/freeswitch.install index 72ded2c48c..a72200294f 100644 --- a/debian/freeswitch.install +++ b/debian/freeswitch.install @@ -155,6 +155,7 @@ opt/freeswitch/mod/mod_skinny.so* opt/freeswitch/mod/mod_skypopen.so* opt/freeswitch/mod/mod_sndfile.so* opt/freeswitch/mod/mod_snom.so* +opt/freeswitch/mod/mod_cdr_sqlite.so* opt/freeswitch/mod/mod_sofia.so* opt/freeswitch/mod/mod_spandsp.so* opt/freeswitch/mod/mod_speex.so* diff --git a/debian/rules b/debian/rules index d6b1915c88..2227a76552 100755 --- a/debian/rules +++ b/debian/rules @@ -23,7 +23,7 @@ export CODECS_MODULES= codecs/mod_bv codecs/mod_h26x codecs/mod_speex codecs/mod export DIALPLANS_MODULES= dialplans/mod_dialplan_asterisk dialplans/mod_dialplan_directory dialplans/mod_dialplan_xml export ENDPOINTS_MODULES= endpoints/mod_dingaling endpoints/mod_portaudio endpoints/mod_sofia endpoints/mod_loopback \ ../../libs/freetdm/mod_freetdm endpoints/mod_skypopen endpoints/mod_skinny -export EVENT_HANDLERS_MODULES=event_handlers/mod_event_multicast event_handlers/mod_event_socket event_handlers/mod_cdr_csv +export EVENT_HANDLERS_MODULES=event_handlers/mod_event_multicast event_handlers/mod_event_socket event_handlers/mod_cdr_csv event_handlers/mod_cdr_sqlite export FORMATS_MODULES= formats/mod_local_stream formats/mod_native_file formats/mod_portaudio_stream \ formats/mod_shout formats/mod_sndfile formats/mod_tone_stream export LANGUAGES_MODULES=languages/mod_spidermonkey languages/mod_perl languages/mod_lua languages/mod_python diff --git a/docs/phrase/phrase_en.xml b/docs/phrase/phrase_en.xml index 6871ecafe4..88d0950d29 100644 --- a/docs/phrase/phrase_en.xml +++ b/docs/phrase/phrase_en.xml @@ -492,6 +492,8 @@ + + diff --git a/freeswitch.spec b/freeswitch.spec index d13ae2686d..102879dd28 100644 --- a/freeswitch.spec +++ b/freeswitch.spec @@ -5,7 +5,7 @@ # # includes module(s): freeswitch-devel freeswitch-codec-passthru-amr freeswitch-codec-passthru-amrwb freeswitch-codec-passthru-g729 # freeswitch-codec-passthru-g7231 freeswitch-lua freeswitch-perl freeswitch-python freeswitch-spidermonkey -# freeswitch-lan-de freeswitch-lang-en freeswitch-lang-fr freeswitch-lang-ru freeswitch-freetdm +# freeswitch-lan-de freeswitch-lang-en freeswitch-lang-fr freeswitch-lang-hu freeswitch-lang-ru freeswitch-freetdm # # Initial Version Copyright (C) 2007 Peter Nixon and Michal Bielicki, All Rights Reserved. # diff --git a/libs/.gitignore b/libs/.gitignore index b9aa87286c..2a213f9ee0 100644 --- a/libs/.gitignore +++ b/libs/.gitignore @@ -276,6 +276,17 @@ /ldns/packaging/ldns-config /ldns/packaging/libldns.pc /ldns-1.6.9/ +/libcodec2/src/c2dec +/libcodec2/src/c2enc +/libcodec2/src/c2sim +/libcodec2/unittest/extract +/libcodec2/unittest/genlsp +/libcodec2/unittest/genres +/libcodec2/unittest/tcodec2 +/libcodec2/unittest/tinterp +/libcodec2/unittest/tnlp +/libcodec2/unittest/tquant +/libcodec2/unittest/vqtrain /libdingaling/Makefile /libdingaling/Makefile.in /libdingaling/aclocal.m4 diff --git a/libs/esl/src/esl_event.c b/libs/esl/src/esl_event.c index 8280f3e892..7aa12a8cd6 100644 --- a/libs/esl/src/esl_event.c +++ b/libs/esl/src/esl_event.c @@ -365,7 +365,6 @@ ESL_DECLARE(int) esl_event_add_array(esl_event_t *event, const char *var, const { char *data; char **array; - int idx; int max = 0; int len; const char *p; @@ -395,7 +394,7 @@ ESL_DECLARE(int) esl_event_add_array(esl_event_t *event, const char *var, const esl_assert(array); memset(array, 0, len); - idx = esl_separate_string_string(data, "|:", array, max); + esl_separate_string_string(data, "|:", array, max); for(i = 0; i < max; i++) { esl_event_add_header_string(event, ESL_STACK_PUSH, var, array[i]); @@ -479,6 +478,13 @@ static esl_status_t esl_event_base_add_header(esl_event_t *event, esl_stack_t st if (!header) { + + if (esl_strlen_zero(data)) { + esl_event_del_header(event, header_name); + FREE(data); + goto end; + } + if (esl_test_flag(event, ESL_EF_UNIQ_HEADERS)) { esl_event_del_header(event, header_name); } diff --git a/libs/freetdm/src/ftmod/ftmod_zt/ftmod_zt.c b/libs/freetdm/src/ftmod/ftmod_zt/ftmod_zt.c index fa9f76cd3f..b66eb0bb6f 100644 --- a/libs/freetdm/src/ftmod/ftmod_zt/ftmod_zt.c +++ b/libs/freetdm/src/ftmod/ftmod_zt/ftmod_zt.c @@ -1072,16 +1072,20 @@ static __inline__ ftdm_status_t zt_channel_process_event(ftdm_channel_t *fchan, break; case ZT_EVENT_BADFCS: { - ftdm_log_chan_msg(fchan, FTDM_LOG_ERROR, "Bad frame checksum (ZT_EVENT_BADFCS)!\n"); - /* What else could we do? */ - *event_id = FTDM_OOB_NOOP; + ftdm_log_chan_msg(fchan, FTDM_LOG_ERROR, "Bad frame checksum (ZT_EVENT_BADFCS)\n"); + *event_id = FTDM_OOB_NOOP; /* What else could we do? */ } break; case ZT_EVENT_OVERRUN: { - ftdm_log_chan_msg(fchan, FTDM_LOG_ERROR, "Driver overrun! (ZT_EVENT_OVERRUN)\n"); - /* What else could we do? */ - *event_id = FTDM_OOB_NOOP; + ftdm_log_chan_msg(fchan, FTDM_LOG_ERROR, "HDLC frame overrun (ZT_EVENT_OVERRUN)\n"); + *event_id = FTDM_OOB_NOOP; /* What else could we do? */ + } + break; + case ZT_EVENT_ABORT: + { + ftdm_log_chan_msg(fchan, FTDM_LOG_ERROR, "HDLC abort frame received (ZT_EVENT_ABORT)\n"); + *event_id = FTDM_OOB_NOOP; /* What else could we do? */ } break; case ZT_EVENT_NONE: diff --git a/libs/stfu/stfu.c b/libs/stfu/stfu.c index a4c3bb78d3..75a659d894 100644 --- a/libs/stfu/stfu.c +++ b/libs/stfu/stfu.c @@ -231,7 +231,7 @@ void stfu_n_debug(stfu_instance_t *i, const char *name) void stfu_n_report(stfu_instance_t *i, stfu_report_t *r) { - assert(i); + stfu_assert(i); r->qlen = i->qlen; r->packet_in_count = i->period_packet_in_count; r->clean_count = i->period_clean_count; @@ -580,7 +580,7 @@ static int stfu_n_find_any_frame(stfu_instance_t *in, stfu_queue_t *queue, stfu_ uint32_t i = 0; stfu_frame_t *frame = NULL; - assert(r_frame); + stfu_assert(r_frame); *r_frame = NULL; diff --git a/libs/stfu/stfu.h b/libs/stfu/stfu.h index b802bbaef6..3e989b0a97 100644 --- a/libs/stfu/stfu.h +++ b/libs/stfu/stfu.h @@ -40,6 +40,13 @@ extern "C" { #include #include +#if (_MSC_VER >= 1400) // VC8+ +#define stfu_assert(expr) assert(expr);__analysis_assume( expr ) +#endif + +#ifndef stfu_assert +#define stfu_assert(_x) assert(_x) +#endif #ifdef _MSC_VER #ifndef uint32_t diff --git a/src/include/switch_core.h b/src/include/switch_core.h index ee890a80c9..681fbf4dc6 100644 --- a/src/include/switch_core.h +++ b/src/include/switch_core.h @@ -248,6 +248,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_bug_close(_Inout_ switch_media SWITCH_DECLARE(switch_status_t) switch_core_media_bug_remove_all(_In_ switch_core_session_t *session); SWITCH_DECLARE(switch_status_t) switch_core_media_bug_enumerate(switch_core_session_t *session, switch_stream_handle_t *stream); +SWITCH_DECLARE(switch_status_t) switch_core_media_bug_transfer_recordings(switch_core_session_t *orig_session, switch_core_session_t *new_session); /*! \brief Read a frame from the bug diff --git a/src/include/switch_ivr.h b/src/include/switch_ivr.h index 36eaaec9ca..288751d850 100644 --- a/src/include/switch_ivr.h +++ b/src/include/switch_ivr.h @@ -62,6 +62,12 @@ SWITCH_BEGIN_EXTERN_C struct switch_unicast_conninfo { }; typedef struct switch_unicast_conninfo switch_unicast_conninfo_t; +#define SWITCH_IVR_VERIFY_SILENCE_DIVISOR(divisor) \ + { \ + if ((divisor) <= 0 && (divisor) != -1) { \ + divisor = 400; \ + } \ + } /** * @defgroup switch_ivr IVR Library diff --git a/src/include/switch_platform.h b/src/include/switch_platform.h index 7603b58544..609dde8b7a 100644 --- a/src/include/switch_platform.h +++ b/src/include/switch_platform.h @@ -137,17 +137,14 @@ typedef int gid_t; #ifdef WIN32 #if defined(SWITCH_CORE_DECLARE_STATIC) #define SWITCH_DECLARE(type) type __stdcall -#define SWITCH_DECLARE_TYPEDEF(type, name) type (__stdcall name) #define SWITCH_DECLARE_NONSTD(type) type __cdecl #define SWITCH_DECLARE_DATA #elif defined(FREESWITCHCORE_EXPORTS) #define SWITCH_DECLARE(type) __declspec(dllexport) type __stdcall -#define SWITCH_DECLARE_TYPEDEF(type, name) __declspec(dllexport) type (__stdcall name) #define SWITCH_DECLARE_NONSTD(type) __declspec(dllexport) type __cdecl #define SWITCH_DECLARE_DATA __declspec(dllexport) #else #define SWITCH_DECLARE(type) __declspec(dllimport) type __stdcall -#define SWITCH_DECLARE_TYPEDEF(type, name) __declspec(dllimport) type (__stdcall name) #define SWITCH_DECLARE_NONSTD(type) __declspec(dllimport) type __cdecl #define SWITCH_DECLARE_DATA __declspec(dllimport) #endif @@ -177,7 +174,6 @@ typedef int gid_t; #define O_BINARY 0 #if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(SWITCH_API_VISIBILITY) #define SWITCH_DECLARE(type) __attribute__((visibility("default"))) type -#define SWITCH_DECLARE_TYPEDEF(type, name) type (name) #define SWITCH_DECLARE_NONSTD(type) __attribute__((visibility("default"))) type #define SWITCH_DECLARE_DATA __attribute__((visibility("default"))) #define SWITCH_MOD_DECLARE(type) __attribute__((visibility("default"))) type @@ -186,7 +182,6 @@ typedef int gid_t; #define SWITCH_DECLARE_CLASS __attribute__((visibility("default"))) #else #define SWITCH_DECLARE(type) type -#define SWITCH_DECLARE_TYPEDEF(type, name) type (name) #define SWITCH_DECLARE_NONSTD(type) type #define SWITCH_DECLARE_DATA #define SWITCH_MOD_DECLARE(type) type diff --git a/src/include/switch_regex.h b/src/include/switch_regex.h index 15b54aa09e..27f4852966 100644 --- a/src/include/switch_regex.h +++ b/src/include/switch_regex.h @@ -73,7 +73,7 @@ SWITCH_DECLARE(switch_status_t) switch_regex_match_partial(const char *target, c SWITCH_DECLARE(void) switch_capture_regex(switch_regex_t *re, int match_count, const char *field_data, int *ovector, const char *var, switch_cap_callback_t callback, void *user_data); -SWITCH_DECLARE(void) switch_regex_set_var_callback(const char *var, const char *val, void *user_data); +SWITCH_DECLARE_NONSTD(void) switch_regex_set_var_callback(const char *var, const char *val, void *user_data); #define switch_regex_safe_free(re) if (re) {\ switch_regex_free(re);\ diff --git a/src/include/switch_types.h b/src/include/switch_types.h index a94bfbd29b..2b2b3de45d 100644 --- a/src/include/switch_types.h +++ b/src/include/switch_types.h @@ -1690,7 +1690,7 @@ struct switch_console_callback_match { }; typedef struct switch_console_callback_match switch_console_callback_match_t; -typedef SWITCH_DECLARE_TYPEDEF(void, *switch_cap_callback_t) (const char *var, const char *val, void *user_data); +typedef void (*switch_cap_callback_t) (const char *var, const char *val, void *user_data); typedef switch_status_t (*switch_console_complete_callback_t) (const char *, const char *, switch_console_callback_match_t **matches); typedef switch_bool_t (*switch_media_bug_callback_t) (switch_media_bug_t *, void *, switch_abc_type_t); typedef switch_bool_t (*switch_tone_detect_callback_t) (switch_core_session_t *, const char *, const char *); diff --git a/src/include/switch_utils.h b/src/include/switch_utils.h index 88c13379b3..f798e92386 100644 --- a/src/include/switch_utils.h +++ b/src/include/switch_utils.h @@ -793,9 +793,9 @@ SWITCH_DECLARE(int) switch_split_user_domain(char *in, char **user, char **domai /* malloc or DIE macros */ #ifdef NDEBUG -#define switch_malloc(ptr, len) (void)( (!!(ptr = malloc(len))) || (fprintf(stderr,"ABORT! Malloc failure at: %s:%s", __FILE__, __LINE__),abort(), 0), ptr ) -#define switch_zmalloc(ptr, len) (void)( (!!(ptr = calloc(1, (len)))) || (fprintf(stderr,"ABORT! Malloc failure at: %s:%s", __FILE__, __LINE__),abort(), 0), ptr) -#define switch_strdup(ptr, s) (void)( (!!(ptr = strdup(s))) || (fprintf(stderr,"ABORT! Malloc failure at: %s:%s", __FILE__, __LINE__),abort(), 0), ptr) +#define switch_malloc(ptr, len) (void)( (!!(ptr = malloc(len))) || (fprintf(stderr,"ABORT! Malloc failure at: %s:%d", __FILE__, __LINE__),abort(), 0), ptr ) +#define switch_zmalloc(ptr, len) (void)( (!!(ptr = calloc(1, (len)))) || (fprintf(stderr,"ABORT! Malloc failure at: %s:%d", __FILE__, __LINE__),abort(), 0), ptr) +#define switch_strdup(ptr, s) (void)( (!!(ptr = strdup(s))) || (fprintf(stderr,"ABORT! Malloc failure at: %s:%d", __FILE__, __LINE__),abort(), 0), ptr) #else #if (_MSC_VER >= 1500) // VC9+ #define switch_malloc(ptr, len) (void)(assert(((ptr) = malloc((len)))),ptr);__analysis_assume( ptr ) diff --git a/src/mod/applications/mod_commands/mod_commands.c b/src/mod/applications/mod_commands/mod_commands.c index 86d1e46d65..40fe306d68 100644 --- a/src/mod/applications/mod_commands/mod_commands.c +++ b/src/mod/applications/mod_commands/mod_commands.c @@ -2123,6 +2123,34 @@ SWITCH_STANDARD_API(kill_function) return SWITCH_STATUS_SUCCESS; } +#define OUTGOING_ANSWER_SYNTAX "" +SWITCH_STANDARD_API(outgoing_answer_function) +{ + switch_core_session_t *outgoing_session = NULL; + char *mycmd = NULL; + + if (zstr(cmd) || !(mycmd = strdup(cmd))) { + stream->write_function(stream, "-USAGE: %s\n", OUTGOING_ANSWER_SYNTAX); + return SWITCH_STATUS_SUCCESS; + } + + if (zstr(mycmd) || !(outgoing_session = switch_core_session_locate(mycmd))) { + stream->write_function(stream, "-ERR No Such Channel!\n"); + } else { + switch_channel_t *channel = switch_core_session_get_channel(outgoing_session); + if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND) { + switch_channel_mark_answered(channel); + stream->write_function(stream, "+OK\n"); + } else { + stream->write_function(stream, "-ERR Not an outbound channel!\n"); + } + switch_core_session_rwunlock(outgoing_session); + } + + switch_safe_free(mycmd); + return SWITCH_STATUS_SUCCESS; +} + #define PREPROCESS_SYNTAX "<>" SWITCH_STANDARD_API(preprocess_function) { @@ -5238,6 +5266,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load) SWITCH_ADD_API(commands_api_interface, "uuid_getvar", "uuid_getvar", uuid_getvar_function, GETVAR_SYNTAX); SWITCH_ADD_API(commands_api_interface, "uuid_hold", "hold", uuid_hold_function, HOLD_SYNTAX); SWITCH_ADD_API(commands_api_interface, "uuid_kill", "Kill Channel", kill_function, KILL_SYNTAX); + SWITCH_ADD_API(commands_api_interface, "uuid_outgoing_answer", "Answer Outgoing Channel", outgoing_answer_function, OUTGOING_ANSWER_SYNTAX); SWITCH_ADD_API(commands_api_interface, "uuid_limit", "Increase limit resource", uuid_limit_function, LIMIT_SYNTAX); SWITCH_ADD_API(commands_api_interface, "uuid_limit_release", "Release limit resource", uuid_limit_release_function, LIMIT_RELEASE_SYNTAX); SWITCH_ADD_API(commands_api_interface, "uuid_loglevel", "set loglevel on session", uuid_loglevel, UUID_LOGLEVEL_SYNTAX); @@ -5360,6 +5389,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load) switch_console_set_complete("add uuid_hold ::console::list_uuid"); switch_console_set_complete("add uuid_jitterbuffer ::console::list_uuid"); switch_console_set_complete("add uuid_kill ::console::list_uuid"); + switch_console_set_complete("add uuid_outgoing_answer ::console::list_uuid"); switch_console_set_complete("add uuid_limit ::console::list_uuid"); switch_console_set_complete("add uuid_limit_release ::console::list_uuid"); switch_console_set_complete("add uuid_loglevel ::console::list_uuid console"); diff --git a/src/mod/applications/mod_dptools/mod_dptools.c b/src/mod/applications/mod_dptools/mod_dptools.c index e0b8e0aa93..c98e632f94 100755 --- a/src/mod/applications/mod_dptools/mod_dptools.c +++ b/src/mod/applications/mod_dptools/mod_dptools.c @@ -2529,8 +2529,7 @@ SWITCH_STANDARD_APP(audio_bridge_function) char *tof_data = NULL; char *tof_array[4] = { 0 }; //int tof_arrayc = 0; - const char *continue_on_fail = NULL, *failure_causes = NULL, - *v_campon = NULL, *v_campon_retries, *v_campon_sleep, *v_campon_timeout, *v_campon_fallback_exten = NULL; + const char *v_campon = NULL, *v_campon_retries, *v_campon_sleep, *v_campon_timeout, *v_campon_fallback_exten = NULL; switch_call_cause_t cause = SWITCH_CAUSE_NORMAL_CLEARING; int campon_retries = 100, campon_timeout = 10, campon_sleep = 10, tmp, camping = 0, fail = 0, thread_started = 0; struct camping_stake stake = { 0 }; @@ -2544,15 +2543,11 @@ SWITCH_STANDARD_APP(audio_bridge_function) return; } - continue_on_fail = switch_channel_get_variable(caller_channel, "continue_on_fail"); - transfer_on_fail = switch_channel_get_variable(caller_channel, "transfer_on_fail"); tof_data = switch_core_session_strdup(session, transfer_on_fail); switch_split(tof_data, ' ', tof_array); transfer_on_fail = tof_array[0]; - failure_causes = switch_channel_get_variable(caller_channel, "failure_causes"); - if ((v_campon = switch_channel_get_variable(caller_channel, "campon")) && switch_true(v_campon)) { const char *cid_name = NULL; const char *cid_number = NULL; @@ -2693,6 +2688,11 @@ SWITCH_STANDARD_APP(audio_bridge_function) EXCEPTION... ATTENDED_TRANSFER never is a reason to continue....... */ if (cause != SWITCH_CAUSE_ATTENDED_TRANSFER) { + const char *continue_on_fail = NULL, *failure_causes = NULL; + + continue_on_fail = switch_channel_get_variable(caller_channel, "continue_on_fail"); + failure_causes = switch_channel_get_variable(caller_channel, "failure_causes"); + if (continue_on_fail || failure_causes) { const char *cause_str; char cause_num[35] = ""; diff --git a/src/mod/applications/mod_fsv/mod_fsv.c b/src/mod/applications/mod_fsv/mod_fsv.c index 7b59796d99..a4c8969621 100644 --- a/src/mod/applications/mod_fsv/mod_fsv.c +++ b/src/mod/applications/mod_fsv/mod_fsv.c @@ -51,6 +51,7 @@ struct record_helper { switch_mutex_t *mutex; int fd; int up; + switch_size_t shared_ts; }; static void *SWITCH_THREAD_FUNC record_video_thread(switch_thread_t *thread, void *obj) @@ -63,7 +64,7 @@ static void *SWITCH_THREAD_FUNC record_video_thread(switch_thread_t *thread, voi int bytes; eh->up = 1; - while (switch_channel_ready(channel)) { + while (switch_channel_ready(channel) && eh->up) { status = switch_core_session_read_video_frame(session, &read_frame, SWITCH_IO_FLAG_NONE, 0); if (!SWITCH_READ_ACCEPTABLE(status)) { @@ -108,11 +109,13 @@ SWITCH_STANDARD_APP(record_fsv_function) switch_mutex_t *mutex = NULL; switch_codec_t codec, *vid_codec; switch_codec_implementation_t read_impl = { 0 }; + switch_dtmf_t dtmf = { 0 }; int count = 0, sanity = 30; switch_core_session_get_read_impl(session, &read_impl); switch_channel_answer(channel); + switch_channel_set_variable(channel, SWITCH_PLAYBACK_TERMINATOR_USED, ""); while (switch_channel_up(channel) && !switch_channel_test_flag(channel, CF_VIDEO)) { switch_yield(10000); @@ -125,6 +128,7 @@ SWITCH_STANDARD_APP(record_fsv_function) if (!--sanity) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "%s timeout waiting for video.\n", switch_channel_get_name(channel)); + switch_channel_set_variable(channel, SWITCH_CURRENT_APPLICATION_RESPONSE_VARIABLE, "Got timeout while waiting for video"); return; } } @@ -132,11 +136,13 @@ SWITCH_STANDARD_APP(record_fsv_function) if (!switch_channel_ready(channel)) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "%s not ready.\n", switch_channel_get_name(channel)); + switch_channel_set_variable(channel, SWITCH_CURRENT_APPLICATION_RESPONSE_VARIABLE, "Channel not ready"); return; } if ((fd = open((char *) data, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR)) < 0) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Error opening file %s\n", (char *) data); + switch_channel_set_variable(channel, SWITCH_CURRENT_APPLICATION_RESPONSE_VARIABLE, "Got error while opening file"); return; } @@ -150,6 +156,7 @@ SWITCH_STANDARD_APP(record_fsv_function) switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Audio Codec Activation Success\n"); } else { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Audio Codec Activation Fail\n"); + switch_channel_set_variable(channel, SWITCH_CURRENT_APPLICATION_RESPONSE_VARIABLE, "Audio codec activation failed"); goto end; } @@ -170,6 +177,7 @@ SWITCH_STANDARD_APP(record_fsv_function) h.audio_ptime = read_impl.microseconds_per_packet / 1000; if (write(fd, &h, sizeof(h)) != sizeof(h)) { + switch_channel_set_variable(channel, SWITCH_CURRENT_APPLICATION_RESPONSE_VARIABLE, "File write failed"); goto end; } @@ -186,9 +194,37 @@ SWITCH_STANDARD_APP(record_fsv_function) while (switch_channel_ready(channel)) { - status = switch_core_session_read_frame(session, &read_frame, SWITCH_IO_FLAG_NONE, 0); + status = switch_core_session_read_frame(session, &read_frame, SWITCH_IO_FLAG_SINGLE_READ, 0); + + if (switch_channel_test_flag(channel, CF_BREAK)) { + switch_channel_clear_flag(channel, CF_BREAK); + eh.up = 0; + break; + } + + switch_ivr_parse_all_events(session); + + //check for dtmf interrupts + if (switch_channel_has_dtmf(channel)) { + const char * terminators = switch_channel_get_variable(channel, SWITCH_PLAYBACK_TERMINATORS_VARIABLE); + switch_channel_dequeue_dtmf(channel, &dtmf); + + if (terminators && !strcasecmp(terminators, "none")) + { + terminators = NULL; + } + + if (terminators && strchr(terminators, dtmf.digit)) { + + char sbuf[2] = {dtmf.digit, '\0'}; + switch_channel_set_variable(channel, SWITCH_PLAYBACK_TERMINATOR_USED, sbuf); + eh.up = 0; + break; + } + } if (!SWITCH_READ_ACCEPTABLE(status)) { + eh.up = 0; break; } @@ -213,12 +249,15 @@ SWITCH_STANDARD_APP(record_fsv_function) } break; } - + if (mutex) { switch_mutex_unlock(mutex); } + + switch_core_session_write_frame(session, read_frame, SWITCH_IO_FLAG_NONE, 0); } + switch_channel_set_variable(channel, SWITCH_CURRENT_APPLICATION_RESPONSE_VARIABLE, "OK"); end: @@ -239,7 +278,7 @@ SWITCH_STANDARD_APP(play_fsv_function) switch_frame_t write_frame = { 0 }, vid_frame = { 0}; int fd = -1; - int bytes; + int bytes; switch_codec_t codec = { 0 }, vid_codec = { 0}, *read_vid_codec; unsigned char *aud_buffer; @@ -248,24 +287,32 @@ SWITCH_STANDARD_APP(play_fsv_function) uint32_t ts = 0, last = 0; switch_timer_t timer = { 0 }; switch_payload_t pt = 0; + switch_dtmf_t dtmf = { 0 }; + switch_frame_t *read_frame; switch_codec_implementation_t read_impl = { 0 }; + switch_core_session_get_read_impl(session, &read_impl); aud_buffer = switch_core_session_alloc(session, SWITCH_RECOMMENDED_BUFFER_SIZE); vid_buffer = switch_core_session_alloc(session, SWITCH_RECOMMENDED_BUFFER_SIZE); + switch_channel_set_variable(channel, SWITCH_PLAYBACK_TERMINATOR_USED, ""); + if ((fd = open((char *) data, O_RDONLY | O_BINARY)) < 0) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Error opening file %s\n", (char *) data); + switch_channel_set_variable(channel, SWITCH_CURRENT_APPLICATION_RESPONSE_VARIABLE, "Got error while opening file"); return; } if (read(fd, &h, sizeof(h)) != sizeof(h)) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Error reading file header\n"); + switch_channel_set_variable(channel, SWITCH_CURRENT_APPLICATION_RESPONSE_VARIABLE, "Got error reading file header"); goto end; } if (h.version != VERSION) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "File version does not match!\n"); + switch_channel_set_variable(channel, SWITCH_CURRENT_APPLICATION_RESPONSE_VARIABLE, "File version does not match!"); goto end; } @@ -290,6 +337,7 @@ SWITCH_STANDARD_APP(play_fsv_function) if (switch_core_timer_init(&timer, "soft", read_impl.microseconds_per_packet / 1000, read_impl.samples_per_packet, switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Timer Activation Fail\n"); + switch_channel_set_variable(channel, SWITCH_CURRENT_APPLICATION_RESPONSE_VARIABLE, "Timer activation failed!"); goto end; } @@ -303,6 +351,7 @@ SWITCH_STANDARD_APP(play_fsv_function) switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Audio Codec Activation Success\n"); } else { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Audio Codec Activation Fail\n"); + switch_channel_set_variable(channel, SWITCH_CURRENT_APPLICATION_RESPONSE_VARIABLE, "Audio codec activation failed"); goto end; } @@ -316,6 +365,7 @@ SWITCH_STANDARD_APP(play_fsv_function) switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Video Codec Activation Success\n"); } else { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Video Codec Activation Fail\n"); + switch_channel_set_variable(channel, SWITCH_CURRENT_APPLICATION_RESPONSE_VARIABLE, "Video codec activation failed"); goto end; } switch_core_session_set_read_codec(session, &codec); @@ -353,15 +403,45 @@ SWITCH_STANDARD_APP(play_fsv_function) if (bytes > (int) write_frame.buflen) { bytes = write_frame.buflen; } + if ((write_frame.datalen = read(fd, write_frame.data, bytes)) <= 0) { break; } switch_core_session_write_frame(session, &write_frame, SWITCH_IO_FLAG_NONE, 0); switch_core_timer_next(&timer); - } + switch_core_session_read_frame(session, &read_frame, SWITCH_IO_FLAG_NONE, 0); + + if (switch_channel_test_flag(channel, CF_BREAK)) { + switch_channel_clear_flag(channel, CF_BREAK); + break; + } + + switch_ivr_parse_all_events(session); + + //check for dtmf interrupts + if (switch_channel_has_dtmf(channel)) { + const char * terminators = switch_channel_get_variable(channel, SWITCH_PLAYBACK_TERMINATORS_VARIABLE); + switch_channel_dequeue_dtmf(channel, &dtmf); + + if (terminators && !strcasecmp(terminators, "none")) + { + terminators = NULL; + } + + if (terminators && strchr(terminators, dtmf.digit)) { + + char sbuf[2] = {dtmf.digit, '\0'}; + switch_channel_set_variable(channel, SWITCH_PLAYBACK_TERMINATOR_USED, sbuf); + break; + } + } + } + } + switch_channel_set_variable(channel, SWITCH_CURRENT_APPLICATION_RESPONSE_VARIABLE, "OK"); + end: if (timer.interval) { diff --git a/src/mod/asr_tts/mod_unimrcp/mod_unimrcp.c b/src/mod/asr_tts/mod_unimrcp/mod_unimrcp.c index 53281bc389..c869d9e8da 100644 --- a/src/mod/asr_tts/mod_unimrcp/mod_unimrcp.c +++ b/src/mod/asr_tts/mod_unimrcp/mod_unimrcp.c @@ -3958,8 +3958,13 @@ static mrcp_client_t *mod_unimrcp_client_create(switch_memory_pool_t *mod_pool) client = NULL; goto done; } + if (zstr(param_value)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing SPEAK param value\n"); + client = NULL; + goto done; + } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Loading SPEAK Param %s:%s\n", param_name, param_value); - switch_core_hash_insert(mod_profile->default_synth_params, param_name, param_value); + switch_core_hash_insert(mod_profile->default_synth_params, switch_core_strdup(pool, param_name), switch_core_strdup(pool, param_value)); } } @@ -3976,8 +3981,13 @@ static mrcp_client_t *mod_unimrcp_client_create(switch_memory_pool_t *mod_pool) client = NULL; goto done; } + if (zstr(param_value)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing RECOGNIZE param value\n"); + client = NULL; + goto done; + } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Loading RECOGNIZE Param %s:%s\n", param_name, param_value); - switch_core_hash_insert(mod_profile->default_recog_params, param_name, param_value); + switch_core_hash_insert(mod_profile->default_recog_params, switch_core_strdup(pool, param_name), switch_core_strdup(pool, param_value)); } } diff --git a/src/mod/codecs/mod_sangoma_codec/mod_sangoma_codec.c b/src/mod/codecs/mod_sangoma_codec/mod_sangoma_codec.c index 52782ad983..6da0ca0b17 100644 --- a/src/mod/codecs/mod_sangoma_codec/mod_sangoma_codec.c +++ b/src/mod/codecs/mod_sangoma_codec/mod_sangoma_codec.c @@ -224,7 +224,7 @@ static int sangoma_create_rtp_port(void *usr_priv, uint32_t host_ip, uint32_t *p switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "New allocated port %d for IP %s/%d.%d.%d.%d\n", rtp_port, local_ip, SNGTC_NIPV4(host_ip)); *p_rtp_port = rtp_port; - *rtp_fd = (void *)(long)rtp_port; + *rtp_fd = NULL; return 0; } @@ -259,6 +259,9 @@ static int sangoma_create_rtp(void *usr_priv, sngtc_codec_request_leg_t *codec_r switch_port_t rtp_port; struct sangoma_transcoding_session *sess = usr_priv; + rtp_port = codec_req_leg->host_udp_port; + *rtp_fd = NULL; + /* * We *MUST* use a new pool * Do not use the session pool since the session may go away while the RTP socket should linger around @@ -271,11 +274,6 @@ static int sangoma_create_rtp(void *usr_priv, sngtc_codec_request_leg_t *codec_r return -1; } - rtp_port = (switch_port_t)(long)*rtp_fd; - *rtp_fd = NULL; - - codec_req_leg->host_udp_port = rtp_port; - local_ip_addr.s_addr = htonl(codec_req_leg->host_ip); switch_inet_ntop(AF_INET, &local_ip_addr, local_ip, sizeof(local_ip)); sngtc_codec_ipv4_hex_to_str(codec_reply_leg->codec_ip, codec_ip); @@ -1271,10 +1269,10 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_sangoma_codec_load) * At this point there is an empty shell codec interface registered, but not yet implementations */ SWITCH_ADD_CODEC(codec_interface, g_codec_map[c].fs_name); - /* Now add as many codec implementations as needed, just up to 40ms for now */ + /* Now add as many codec implementations as needed, just up to 200ms for now */ if (g_codec_map[c].autoinit) { int ms = 0; - for (i = 1; i <= 4; i++) { + for (i = 1; i <= 20; i++) { ms = i * 10; if (g_codec_map[c].maxms < ms) { break; diff --git a/src/mod/endpoints/mod_loopback/mod_loopback.c b/src/mod/endpoints/mod_loopback/mod_loopback.c index 8fb8384e6b..97684b6f10 100644 --- a/src/mod/endpoints/mod_loopback/mod_loopback.c +++ b/src/mod/endpoints/mod_loopback/mod_loopback.c @@ -54,7 +54,8 @@ typedef enum { TFLAG_BLEG = (1 << 6), TFLAG_APP = (1 << 7), TFLAG_RUNNING_APP = (1 << 8), - TFLAG_BOWOUT_USED = (1 << 9) + TFLAG_BOWOUT_USED = (1 << 9), + TFLAG_CLEAR = (1 << 10) } TFLAGS; struct private_object { @@ -105,6 +106,17 @@ static switch_status_t channel_read_frame(switch_core_session_t *session, switch static switch_status_t channel_write_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags, int stream_id); static switch_status_t channel_kill_channel(switch_core_session_t *session, int sig); + +static void clear_queue(private_t *tech_pvt) +{ + void *pop; + + while (switch_queue_trypop(tech_pvt->frame_queue, &pop) == SWITCH_STATUS_SUCCESS && pop) { + switch_frame_t *frame = (switch_frame_t *) pop; + switch_frame_free(&frame); + } +} + static switch_status_t tech_init(private_t *tech_pvt, switch_core_session_t *session, switch_codec_t *codec) { const char *iananame = "L16"; @@ -566,6 +578,12 @@ static switch_status_t channel_read_frame(switch_core_session_t *session, switch mutex = tech_pvt->mutex; switch_mutex_lock(mutex); + + if (switch_test_flag(tech_pvt, TFLAG_CLEAR)) { + clear_queue(tech_pvt); + switch_clear_flag(tech_pvt, TFLAG_CLEAR); + } + if (switch_queue_trypop(tech_pvt->frame_queue, &pop) == SWITCH_STATUS_SUCCESS && pop) { if (tech_pvt->write_frame) { switch_frame_free(&tech_pvt->write_frame); @@ -599,17 +617,6 @@ static switch_status_t channel_read_frame(switch_core_session_t *session, switch return status; } -static void clear_queue(private_t *tech_pvt) -{ - void *pop; - - while (switch_queue_trypop(tech_pvt->frame_queue, &pop) == SWITCH_STATUS_SUCCESS && pop) { - switch_frame_t *frame = (switch_frame_t *) pop; - switch_frame_free(&frame); - } -} - - static switch_status_t channel_write_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags, int stream_id) { switch_channel_t *channel = NULL; @@ -750,9 +757,9 @@ static switch_status_t channel_receive_message(switch_core_session_t *session, s { done = 1; + switch_set_flag(tech_pvt, TFLAG_CLEAR); + switch_set_flag(tech_pvt->other_tech_pvt, TFLAG_CLEAR); - clear_queue(tech_pvt); - clear_queue(tech_pvt->other_tech_pvt); switch_core_timer_sync(&tech_pvt->timer); switch_core_timer_sync(&tech_pvt->other_tech_pvt->timer); } diff --git a/src/mod/endpoints/mod_portaudio/mod_portaudio.c b/src/mod/endpoints/mod_portaudio/mod_portaudio.c index 8564a771e5..f84fdeee96 100644 --- a/src/mod/endpoints/mod_portaudio/mod_portaudio.c +++ b/src/mod/endpoints/mod_portaudio/mod_portaudio.c @@ -24,6 +24,7 @@ * Contributor(s): * * Anthony Minessale II + * Moises Silva (Multiple endpoints work sponsored by Comrex Corporation) * * * mod_portaudio.c -- PortAudio Endpoint Module @@ -45,6 +46,9 @@ #define MY_EVENT_ERROR_AUDIO_DEV "portaudio::audio_dev_error" #define SWITCH_PA_CALL_ID_VARIABLE "pa_call_id" +#define MIN_STREAM_SAMPLE_RATE 8000 +#define STREAM_SAMPLES_PER_PACKET(stream) ((stream->codec_ms * stream->sample_rate) / 1000) + SWITCH_MODULE_LOAD_FUNCTION(mod_portaudio_load); SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_portaudio_shutdown); //SWITCH_MODULE_RUNTIME_FUNCTION(mod_portaudio_runtime); @@ -79,6 +83,76 @@ typedef enum { TFLAG_AUTO_ANSWER = (1 << 10) } TFLAGS; +struct audio_stream { + int indev; + int outdev; + PABLIO_Stream *stream; + switch_timer_t write_timer; + struct audio_stream *next; +}; +typedef struct audio_stream audio_stream_t; + +/* Audio stream that can be shared across endpoints */ +typedef struct _shared_audio_stream_t { + /*! Friendly name for this stream */ + char name[255]; + /*! Sampling rate */ + int sample_rate; + /*! Buffer packetization (and therefore timing) */ + int codec_ms; + /*! The PA input device */ + int indev; + /*! Input channels being used */ + uint8_t inchan_used[MAX_IO_CHANNELS]; + /*! The PA output device */ + int outdev; + /*! Output channels being used */ + uint8_t outchan_used[MAX_IO_CHANNELS]; + /*! How many channels to create (for both indev and outdev) */ + int channels; + /*! The io stream helper to buffer audio */ + PABLIO_Stream *stream; + /* It can be shared after all :-) */ + switch_mutex_t *mutex; +} shared_audio_stream_t; + +typedef struct private_object private_t; +/* Endpoint that can be called via portaudio/endpoint/ */ +typedef struct _audio_endpoint { + /*! Friendly name for this endpoint */ + char name[255]; + + /*! Input stream for this endpoint */ + shared_audio_stream_t *in_stream; + + /*! Output stream for this endpoint */ + shared_audio_stream_t *out_stream; + + /*! Channel index within the input stream where we get the audio for this endpoint */ + int inchan; + + /*! Channel index within the output stream where we get the audio for this endpoint */ + int outchan; + + /*! Associated private information if involved in a call */ + private_t *master; + + /*! For timed read and writes */ + switch_timer_t read_timer; + switch_timer_t write_timer; + + /* We need our own read frame */ + switch_frame_t read_frame; + unsigned char read_buf[SWITCH_RECOMMENDED_BUFFER_SIZE]; + + /* Needed codecs for the core to read/write in the proper format */ + switch_codec_t read_codec; + switch_codec_t write_codec; + + /*! Let's be safe */ + switch_mutex_t *mutex; +} audio_endpoint_t; + struct private_object { unsigned int flags; switch_core_session_t *session; @@ -92,19 +166,10 @@ struct private_object { switch_file_handle_t *hfh; switch_frame_t hold_frame; unsigned char holdbuf[SWITCH_RECOMMENDED_BUFFER_SIZE]; + audio_endpoint_t *audio_endpoint; struct private_object *next; }; -typedef struct private_object private_t; - -struct audio_stream { - int indev; - int outdev; - PABLIO_Stream *stream; - switch_timer_t write_timer; - struct audio_stream *next; -}; -typedef struct audio_stream audio_stream_t; static struct { int debug; @@ -138,6 +203,10 @@ static struct { unsigned char cngbuf[SWITCH_RECOMMENDED_BUFFER_SIZE]; private_t *call_list; audio_stream_t *stream_list; + /*! Streams that can be used by multiple endpoints at the same time */ + switch_hash_t *sh_streams; + /*! Endpoints configured */ + switch_hash_t *endpoints; int ring_interval; GFLAGS flags; switch_timer_t read_timer; @@ -243,13 +312,16 @@ static switch_status_t channel_on_routing(switch_core_session_t *session) if (hold_file) { tech_pvt->hold_file = switch_core_session_strdup(session, hold_file); } - if (switch_test_flag(tech_pvt, TFLAG_OUTBOUND)) { - if (validate_main_audio_stream() != SWITCH_STATUS_SUCCESS) { + if (switch_test_flag(tech_pvt, TFLAG_OUTBOUND)) { + if (!tech_pvt->audio_endpoint && validate_main_audio_stream() != SWITCH_STATUS_SUCCESS) { switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); return SWITCH_STATUS_FALSE; } - if (switch_test_flag(tech_pvt, TFLAG_OUTBOUND) && !switch_test_flag(tech_pvt, TFLAG_AUTO_ANSWER)) { + if (!tech_pvt->audio_endpoint && + switch_test_flag(tech_pvt, TFLAG_OUTBOUND) && + !switch_test_flag(tech_pvt, TFLAG_AUTO_ANSWER)) { + add_pvt(tech_pvt, PA_SLAVE); ring_file = globals.ring_file; @@ -281,18 +353,22 @@ static switch_status_t channel_on_routing(switch_core_session_t *session) } } - if (switch_test_flag(tech_pvt, TFLAG_AUTO_ANSWER)) { + if (tech_pvt->audio_endpoint || switch_test_flag(tech_pvt, TFLAG_AUTO_ANSWER)) { switch_mutex_lock(globals.pvt_lock); add_pvt(tech_pvt, PA_MASTER); - switch_channel_mark_answered(channel); - switch_set_flag(tech_pvt, TFLAG_ANSWER); + if (switch_test_flag(tech_pvt, TFLAG_AUTO_ANSWER)) { + switch_channel_mark_answered(channel); + switch_set_flag(tech_pvt, TFLAG_ANSWER); + } switch_mutex_unlock(globals.pvt_lock); switch_yield(1000000); } else { switch_channel_mark_ring_ready(channel); } - while (switch_channel_get_state(channel) == CS_ROUTING && !switch_test_flag(tech_pvt, TFLAG_ANSWER)) { + while (switch_channel_get_state(channel) == CS_ROUTING && + !switch_channel_test_flag(channel, CF_ANSWERED) && + !switch_test_flag(tech_pvt, TFLAG_ANSWER)) { switch_size_t olen = globals.readfile_timer.samples; if (switch_micro_time_now() - last >= waitsec) { @@ -325,7 +401,7 @@ static switch_status_t channel_on_routing(switch_core_session_t *session) if (globals.ring_stream && (! switch_test_flag(globals.call_list, TFLAG_MASTER) || ( !globals.no_ring_during_call && globals.main_stream != globals.ring_stream)) ) { //if there is a ring stream and not an active call or if there is an active call and we are allowed to ring during it AND the ring stream is not the main stream - WriteAudioStream(globals.ring_stream->stream, abuf, (long) olen, &globals.ring_stream->write_timer); + WriteAudioStream(globals.ring_stream->stream, abuf, (long) olen, 0, &globals.ring_stream->write_timer); } } else { switch_yield(10000); @@ -339,10 +415,12 @@ static switch_status_t channel_on_routing(switch_core_session_t *session) } if (switch_test_flag(tech_pvt, TFLAG_OUTBOUND)) { - if (!switch_test_flag(tech_pvt, TFLAG_ANSWER)) { + if (!switch_test_flag(tech_pvt, TFLAG_ANSWER) && + !switch_channel_test_flag(channel, CF_ANSWERED)) { switch_channel_hangup(channel, SWITCH_CAUSE_NO_ANSWER); return SWITCH_STATUS_SUCCESS; } + switch_set_flag(tech_pvt, TFLAG_ANSWER); } switch_set_flag_locked(tech_pvt, TFLAG_IO); @@ -389,6 +467,7 @@ static audio_stream_t* find_audio_stream(int indev, int outdev, int already_lock } return NULL; } + static void destroy_audio_streams() { int close_wait = 4; @@ -402,6 +481,7 @@ static void destroy_audio_streams() } globals.destroying_streams = 0; } + static switch_status_t validate_main_audio_stream() { if (globals.read_timer.timer_interface) { @@ -578,9 +658,12 @@ static void add_pvt(private_t *tech_pvt, int master) switch_snprintf(tech_pvt->call_id, sizeof(tech_pvt->call_id), "%d", ++globals.call_id); switch_channel_set_variable(switch_core_session_get_channel(tech_pvt->session), SWITCH_PA_CALL_ID_VARIABLE, tech_pvt->call_id); switch_core_hash_insert(globals.call_hash, tech_pvt->call_id, tech_pvt); - switch_core_session_set_read_codec(tech_pvt->session, &globals.read_codec); - switch_core_session_set_write_codec(tech_pvt->session, &globals.write_codec); + if (!tech_pvt->audio_endpoint) { + switch_core_session_set_read_codec(tech_pvt->session, &globals.read_codec); + switch_core_session_set_write_codec(tech_pvt->session, &globals.write_codec); + } switch_mutex_unlock(globals.pa_mutex); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "Added call %s\n", tech_pvt->call_id); } for (tp = globals.call_list; tp; tp = tp->next) { @@ -589,7 +672,7 @@ static void add_pvt(private_t *tech_pvt, int master) } if (master && switch_test_flag(tp, TFLAG_MASTER) ) { switch_clear_flag_locked(tp, TFLAG_MASTER); - create_hold_event(tp,0); + create_hold_event(tp, 0); } } @@ -663,11 +746,30 @@ static switch_status_t channel_on_destroy(switch_core_session_t *session) return SWITCH_STATUS_SUCCESS; } +static int release_stream_channel(shared_audio_stream_t *stream, int index, int input); static switch_status_t channel_on_hangup(switch_core_session_t *session) { private_t *tech_pvt = switch_core_session_get_private(session); switch_assert(tech_pvt != NULL); + if (tech_pvt->audio_endpoint) { + audio_endpoint_t *endpoint = tech_pvt->audio_endpoint; + + tech_pvt->audio_endpoint = NULL; + + switch_mutex_lock(endpoint->mutex); + + release_stream_channel(endpoint->in_stream, endpoint->inchan, 1); + release_stream_channel(endpoint->out_stream, endpoint->outchan, 0); + switch_core_timer_destroy(&endpoint->read_timer); + switch_core_timer_destroy(&endpoint->write_timer); + switch_core_codec_destroy(&endpoint->read_codec); + switch_core_codec_destroy(&endpoint->write_codec); + endpoint->master = NULL; + + switch_mutex_unlock(endpoint->mutex); + } + switch_mutex_lock(globals.pa_mutex); switch_core_hash_delete(globals.call_hash, tech_pvt->call_id); switch_mutex_unlock(globals.pa_mutex); @@ -727,12 +829,46 @@ static switch_status_t channel_send_dtmf(switch_core_session_t *session, const s return SWITCH_STATUS_SUCCESS; } +static switch_status_t channel_endpoint_read(audio_endpoint_t *endpoint, switch_frame_t **frame) +{ + int samples = 0; + + if (!endpoint->in_stream) { + switch_core_timer_next(&endpoint->read_timer); + *frame = &globals.cng_frame; + return SWITCH_STATUS_SUCCESS; + } + + endpoint->read_frame.data = endpoint->read_buf; + endpoint->read_frame.buflen = sizeof(endpoint->read_buf); + endpoint->read_frame.source = __FILE__; + samples = ReadAudioStream(endpoint->in_stream->stream, + endpoint->read_frame.data, STREAM_SAMPLES_PER_PACKET(endpoint->in_stream), + endpoint->inchan, &endpoint->read_timer); + + if (!samples) { + switch_core_timer_next(&endpoint->read_timer); + *frame = &globals.cng_frame; + return SWITCH_STATUS_SUCCESS; + } + + endpoint->read_frame.datalen = (samples * sizeof(int16_t)); + endpoint->read_frame.samples = samples; + endpoint->read_frame.codec = &endpoint->read_codec; + *frame = &endpoint->read_frame; + return SWITCH_STATUS_SUCCESS; +} + static switch_status_t channel_read_frame(switch_core_session_t *session, switch_frame_t **frame, switch_io_flag_t flags, int stream_id) { private_t *tech_pvt = switch_core_session_get_private(session); int samples = 0; switch_status_t status = SWITCH_STATUS_FALSE; switch_assert(tech_pvt != NULL); + + if (tech_pvt->audio_endpoint) { + return channel_endpoint_read(tech_pvt->audio_endpoint, frame); + } if (!globals.main_stream) { goto normal_return; @@ -796,7 +932,7 @@ static switch_status_t channel_read_frame(switch_core_session_t *session, switch } switch_mutex_lock(globals.device_lock); - samples = ReadAudioStream(globals.main_stream->stream, globals.read_frame.data, globals.read_codec.implementation->samples_per_packet, &globals.read_timer); + samples = ReadAudioStream(globals.main_stream->stream, globals.read_frame.data, globals.read_codec.implementation->samples_per_packet, 0, &globals.read_timer); switch_mutex_unlock(globals.device_lock); if (samples) { @@ -827,12 +963,37 @@ normal_return: } +static switch_status_t channel_endpoint_write(audio_endpoint_t *endpoint, switch_frame_t *frame) +{ + if (!endpoint->out_stream) { + switch_core_timer_next(&endpoint->write_timer); + return SWITCH_STATUS_SUCCESS; + } + if (!endpoint->master) { + return SWITCH_STATUS_SUCCESS; + } + if (switch_test_flag(endpoint->master, TFLAG_HUP)) { + return SWITCH_STATUS_FALSE; + } + if (!switch_test_flag(endpoint->master, TFLAG_IO)) { + return SWITCH_STATUS_SUCCESS; + } + WriteAudioStream(endpoint->out_stream->stream, (short *)frame->data, + (int)(frame->datalen / sizeof(SAMPLE)), + endpoint->outchan, &(endpoint->write_timer)); + return SWITCH_STATUS_SUCCESS; +} + static switch_status_t channel_write_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags, int stream_id) { switch_status_t status = SWITCH_STATUS_FALSE; private_t *tech_pvt = switch_core_session_get_private(session); switch_assert(tech_pvt != NULL); + if (tech_pvt->audio_endpoint) { + return channel_endpoint_write(tech_pvt->audio_endpoint, frame); + } + if (!globals.main_stream) { return SWITCH_STATUS_FALSE; } @@ -847,7 +1008,7 @@ static switch_status_t channel_write_frame(switch_core_session_t *session, switc if (globals.main_stream) { if (switch_test_flag((&globals), GFLAG_EAR)) { - WriteAudioStream(globals.main_stream->stream, (short *) frame->data, (int) (frame->datalen / sizeof(SAMPLE)), &(globals.main_stream->write_timer)); + WriteAudioStream(globals.main_stream->stream, (short *) frame->data, (int) (frame->datalen / sizeof(SAMPLE)), 0, &(globals.main_stream->write_timer)); } status = SWITCH_STATUS_SUCCESS; } @@ -904,6 +1065,72 @@ switch_io_routines_t portaudio_io_routines = { /*.receive_message */ channel_receive_message }; +static int create_shared_audio_stream(shared_audio_stream_t *stream); +static int destroy_shared_audio_stream(shared_audio_stream_t *stream); +static int take_stream_channel(shared_audio_stream_t *stream, int index, int input) +{ + int rc = 0; + if (!stream) { + return rc; + } + + switch_mutex_lock(stream->mutex); + + if (!stream->stream && create_shared_audio_stream(stream)) { + rc = -1; + goto done; + } + + if (input) { + if (stream->inchan_used[index]) { + rc = -1; + goto done; + } + stream->inchan_used[index] = 1; + } else { + if (!input && stream->outchan_used[index]) { + rc = -1; + goto done; + } + stream->outchan_used[index] = 1; + } + +done: + switch_mutex_unlock(stream->mutex); + return rc; +} + +static int release_stream_channel(shared_audio_stream_t *stream, int index, int input) +{ + int i = 0; + int destroy_stream = 1; + int rc = 0; + + if (!stream) { + return rc; + } + + switch_mutex_lock(stream->mutex); + + if (input) { + stream->inchan_used[index] = 0; + } else { + stream->outchan_used[index] = 0; + } + + for (i = 0; i < stream->channels; i++) { + if (stream->inchan_used[i] || stream->outchan_used[i]) { + destroy_stream = 0; + } + } + if (destroy_stream) { + destroy_shared_audio_stream(stream); + } + + switch_mutex_unlock(stream->mutex); + return rc; +} + /* 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! */ @@ -912,51 +1139,161 @@ static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *sessi switch_core_session_t **new_session, switch_memory_pool_t **pool, switch_originate_flag_t flags, switch_call_cause_t *cancel_cause) { + char name[128]; + const char *id = NULL; + private_t *tech_pvt = NULL; + switch_channel_t *channel = NULL; + switch_caller_profile_t *caller_profile = NULL; + switch_call_cause_t retcause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + int codec_ms = -1; + int samples_per_packet = -1; + int sample_rate = 0; + audio_endpoint_t *endpoint = NULL; + char *endpoint_name = NULL; + const char *endpoint_answer = NULL; - if ((*new_session = switch_core_session_request(portaudio_endpoint_interface, SWITCH_CALL_DIRECTION_OUTBOUND, flags, pool)) != 0) { - private_t *tech_pvt; - switch_channel_t *channel; - switch_caller_profile_t *caller_profile; - - switch_core_session_add_stream(*new_session, NULL); - if ((tech_pvt = (private_t *) switch_core_session_alloc(*new_session, sizeof(private_t))) != 0) { - memset(tech_pvt, 0, sizeof(*tech_pvt)); - switch_mutex_init(&tech_pvt->flag_mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(*new_session)); - channel = switch_core_session_get_channel(*new_session); - switch_core_session_set_private(*new_session, tech_pvt); - tech_pvt->session = *new_session; - } else { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(*new_session), SWITCH_LOG_CRIT, "Hey where is my memory pool?\n"); - switch_core_session_destroy(new_session); - return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; - } - - if (outbound_profile) { - char name[128]; - const char *id = !zstr(outbound_profile->caller_id_number) ? outbound_profile->caller_id_number : "na"; - switch_snprintf(name, sizeof(name), "portaudio/%s", id); - - switch_channel_set_name(channel, name); - - caller_profile = switch_caller_profile_clone(*new_session, outbound_profile); - switch_channel_set_caller_profile(channel, caller_profile); - tech_pvt->caller_profile = caller_profile; - if (outbound_profile->destination_number && !strcasecmp(outbound_profile->destination_number, "auto_answer")) { - switch_set_flag(tech_pvt, TFLAG_AUTO_ANSWER); - } - - } else { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(*new_session), SWITCH_LOG_ERROR, "Doh! no caller profile\n"); - switch_core_session_destroy(new_session); - return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; - } - - switch_set_flag_locked(tech_pvt, TFLAG_OUTBOUND); - switch_channel_set_state(channel, CS_INIT); - return SWITCH_CAUSE_SUCCESS; + if (!outbound_profile) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing caller profile\n"); + return retcause; } - return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + if (!(*new_session = switch_core_session_request(portaudio_endpoint_interface, SWITCH_CALL_DIRECTION_OUTBOUND, flags, pool))) { + return retcause; + } + + switch_core_session_add_stream(*new_session, NULL); + if ((tech_pvt = (private_t *) switch_core_session_alloc(*new_session, sizeof(private_t))) != 0) { + memset(tech_pvt, 0, sizeof(*tech_pvt)); + switch_mutex_init(&tech_pvt->flag_mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(*new_session)); + channel = switch_core_session_get_channel(*new_session); + switch_core_session_set_private(*new_session, tech_pvt); + tech_pvt->session = *new_session; + } else { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(*new_session), SWITCH_LOG_CRIT, "Hey where is my memory pool?\n"); + switch_core_session_destroy(new_session); + return retcause; + } + + if (outbound_profile->destination_number && !strncasecmp(outbound_profile->destination_number, "endpoint", sizeof("endpoint")-1)) { + codec_ms = -1; + samples_per_packet = -1; + endpoint = NULL; + endpoint_name = switch_core_strdup(outbound_profile->pool, outbound_profile->destination_number); + endpoint_name = strchr(endpoint_name, '/'); + if (!endpoint_name) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(*new_session), SWITCH_LOG_CRIT, "No portaudio endpoint specified\n"); + goto error; + } + endpoint_name++; + endpoint = switch_core_hash_find(globals.endpoints, endpoint_name); + if (!endpoint) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(*new_session), SWITCH_LOG_CRIT, "Invalid portaudio endpoint %s\n", endpoint_name); + goto error; + } + + switch_mutex_lock(endpoint->mutex); + + if (endpoint->master) { + /* someone already has this endpoint */ + retcause = SWITCH_CAUSE_USER_BUSY; + goto error; + } + + codec_ms = endpoint->in_stream ? endpoint->in_stream->codec_ms : endpoint->out_stream->codec_ms; + samples_per_packet = endpoint->in_stream ? + STREAM_SAMPLES_PER_PACKET(endpoint->in_stream) : STREAM_SAMPLES_PER_PACKET(endpoint->out_stream); + sample_rate = endpoint->in_stream ? endpoint->in_stream->sample_rate : endpoint->out_stream->sample_rate; + + if (switch_core_timer_init(&endpoint->read_timer, + globals.timer_name, codec_ms, + samples_per_packet, module_pool) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "failed to setup read timer for endpoint '%s'!\n", endpoint->name); + goto error; + } + + /* The write timer must be setup regardless */ + if (switch_core_timer_init(&endpoint->write_timer, + globals.timer_name, codec_ms, + samples_per_packet, module_pool) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "failed to setup read timer for endpoint '%s'!\n", endpoint->name); + goto error; + } + + if (switch_core_codec_init(&endpoint->read_codec, + "L16", NULL, sample_rate, codec_ms, 1, + SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, NULL) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't load codec?\n"); + goto error; + } + + if (switch_core_codec_init(&endpoint->write_codec, + "L16", NULL, sample_rate, codec_ms, 1, + SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, NULL) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't load codec?\n"); + goto error; + } + switch_core_session_set_read_codec(tech_pvt->session, &endpoint->read_codec); + switch_core_session_set_write_codec(tech_pvt->session, &endpoint->write_codec); + + /* try to acquire the stream */ + if (take_stream_channel(endpoint->in_stream, endpoint->inchan, 1)) { + retcause = SWITCH_CAUSE_USER_BUSY; + goto error; + } + if (take_stream_channel(endpoint->out_stream, endpoint->outchan, 0)) { + release_stream_channel(endpoint->in_stream, endpoint->inchan, 1); + retcause = SWITCH_CAUSE_USER_BUSY; + goto error; + } + switch_snprintf(name, sizeof(name), "portaudio/endpoint-%s", endpoint_name); + if (var_event && (endpoint_answer = (switch_event_get_header(var_event, "endpoint_answer")))) { + if (switch_true(endpoint_answer)) { + switch_set_flag(tech_pvt, TFLAG_AUTO_ANSWER); + } + } else { + switch_set_flag(tech_pvt, TFLAG_AUTO_ANSWER); + } + endpoint->master = tech_pvt; + tech_pvt->audio_endpoint = endpoint; + switch_mutex_unlock(endpoint->mutex); + } else { + id = !zstr(outbound_profile->caller_id_number) ? outbound_profile->caller_id_number : "na"; + switch_snprintf(name, sizeof(name), "portaudio/%s", id); + if (outbound_profile->destination_number && !strcasecmp(outbound_profile->destination_number, "auto_answer")) { + switch_set_flag(tech_pvt, TFLAG_AUTO_ANSWER); + } + } + switch_channel_set_name(channel, name); + caller_profile = switch_caller_profile_clone(*new_session, outbound_profile); + switch_channel_set_caller_profile(channel, caller_profile); + tech_pvt->caller_profile = caller_profile; + + switch_set_flag_locked(tech_pvt, TFLAG_OUTBOUND); + switch_channel_set_state(channel, CS_INIT); + return SWITCH_CAUSE_SUCCESS; + +error: + if (endpoint) { + if (!endpoint->master) { + if (endpoint->read_timer.interval) { + switch_core_timer_destroy(&endpoint->read_timer); + } + if (endpoint->write_timer.interval) { + switch_core_timer_destroy(&endpoint->write_timer); + } + if (endpoint->read_codec.codec_interface) { + switch_core_codec_destroy(&endpoint->read_codec); + } + if (endpoint->write_codec.codec_interface) { + switch_core_codec_destroy(&endpoint->write_codec); + } + } + switch_mutex_unlock(endpoint->mutex); + } + if (new_session && *new_session) { + switch_core_session_destroy(new_session); + } + return retcause; } @@ -971,12 +1308,14 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_portaudio_load) memset(&globals, 0, sizeof(globals)); switch_core_hash_init(&globals.call_hash, module_pool); + switch_core_hash_init(&globals.sh_streams, module_pool); + switch_core_hash_init(&globals.endpoints, module_pool); switch_mutex_init(&globals.device_lock, SWITCH_MUTEX_NESTED, module_pool); switch_mutex_init(&globals.pvt_lock, SWITCH_MUTEX_NESTED, module_pool); switch_mutex_init(&globals.streams_lock, SWITCH_MUTEX_NESTED, module_pool); switch_mutex_init(&globals.flag_mutex, SWITCH_MUTEX_NESTED, module_pool); switch_mutex_init(&globals.pa_mutex, SWITCH_MUTEX_NESTED, module_pool); - globals.codecs_inited=0; + globals.codecs_inited = 0; globals.read_frame.data = globals.databuf; globals.read_frame.buflen = sizeof(globals.databuf); globals.cng_frame.data = globals.cngbuf; @@ -1055,16 +1394,248 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_portaudio_load) switch_console_set_complete("add pa play"); switch_console_set_complete("add pa playdev"); switch_console_set_complete("add pa looptest"); + switch_console_set_complete("add pa shstreams"); + switch_console_set_complete("add pa endpoints"); /* indicate that the module should continue to be loaded */ return SWITCH_STATUS_SUCCESS; } +static int check_device(char *devstr, int check_input) +{ + int devval; + if (devstr[0] == '#') { + devval = get_dev_by_number(devstr + 1, check_input); + } else { + devval = get_dev_by_name(devstr, check_input); + } + return devval; +} + +static switch_status_t load_streams(switch_xml_t streams) +{ + switch_status_t status = SWITCH_STATUS_SUCCESS; + switch_xml_t param, mystream; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Loading streams ...\n"); + for (mystream = switch_xml_child(streams, "stream"); mystream; mystream = mystream->next) { + shared_audio_stream_t *stream = NULL; + int devval = -1; + char *stream_name = (char *) switch_xml_attr_soft(mystream, "name"); + + if (!stream_name) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing stream name attribute, skipping ...\n"); + continue; + } + + /* check if that stream name is not already used */ + stream = switch_core_hash_find(globals.sh_streams, stream_name); + if (stream) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "A stream with name '%s' already exists\n", stream_name); + continue; + } + + stream = switch_core_alloc(module_pool, sizeof(*stream)); + if (!stream) { + continue; + } + switch_mutex_init(&stream->mutex, SWITCH_MUTEX_NESTED, module_pool); + stream->indev = -1; + stream->outdev = -1; + stream->sample_rate = globals.sample_rate; + stream->codec_ms = globals.codec_ms; + stream->channels = 1; + switch_snprintf(stream->name, sizeof(stream->name), "%s", stream_name); + for (param = switch_xml_child(mystream, "param"); param; param = param->next) { + char *var = (char *) switch_xml_attr_soft(param, "name"); + char *val = (char *) switch_xml_attr_soft(param, "value"); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Parsing stream '%s' parameter %s = %s\n", stream_name, var, val); + if (!strcmp(var, "indev")) { + devval = check_device(val, 1); + if (devval < 0) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, + "Invalid indev specified for stream '%s'\n", stream_name); + stream->indev = -1; + continue; + } + stream->indev = devval; + } else if (!strcmp(var, "outdev")) { + devval = check_device(val, 0); + if (devval < 0) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, + "Invalid outdev specified for stream '%s'\n", stream_name); + stream->outdev = -1; + continue; + } + stream->outdev = devval; + } else if (!strcmp(var, "sample-rate")) { + stream->sample_rate = atoi(val); + if (stream->sample_rate < MIN_STREAM_SAMPLE_RATE) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, + "Invalid sample rate specified for stream '%s', forcing to 8000\n", stream_name); + stream->sample_rate = MIN_STREAM_SAMPLE_RATE; + } + } else if (!strcmp(var, "codec-ms")) { + int tmp = atoi(val); + if (switch_check_interval(stream->sample_rate, tmp)) { + stream->codec_ms = tmp; + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, + "codec-ms must be multiple of 10 and less than %d, Using default of 20\n", SWITCH_MAX_INTERVAL); + } + } else if (!strcmp(var, "channels")) { + stream->channels = atoi(val); + if (stream->channels < 1 || stream->channels > MAX_IO_CHANNELS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, + "Invalid number of channels specified for stream '%s', forcing to 1\n", stream_name); + stream->channels = 1; + } + } + } + if (stream->indev < 0 && stream->outdev < 0) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, + "You need at least one device for stream '%s'\n", stream_name); + continue; + } + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, + "Created stream '%s', sample-rate = %d, codec-ms = %d\n", stream->name, stream->sample_rate, stream->codec_ms); + switch_core_hash_insert(globals.sh_streams, stream->name, stream); + } + return status; +} + +static int check_stream_compat(shared_audio_stream_t *in_stream, shared_audio_stream_t *out_stream) +{ + if (!in_stream || !out_stream) { + /* nothing to be compatible with */ + return 0; + } + if (in_stream->sample_rate != out_stream->sample_rate) { + return -1; + } + if (in_stream->codec_ms != out_stream->codec_ms) { + return -1; + } + return 0; +} + +static shared_audio_stream_t *check_stream(char *streamstr, int check_input, int *chanindex) +{ + shared_audio_stream_t *stream = NULL; + int cnum = 0; + char stream_name[255]; + char *chan = NULL; + + *chanindex = -1; + + switch_snprintf(stream_name, sizeof(stream_name), "%s", streamstr); + + chan = strchr(stream_name, ':'); + if (!chan) { + return NULL; + } + *chan = 0; + chan++; + cnum = atoi(chan); + + stream = switch_core_hash_find(globals.sh_streams, stream_name); + if (!stream) { + return NULL; + } + + if (cnum < 0 || cnum > stream->channels) { + return NULL; + } + + if (check_input && stream->indev < 0) { + return NULL; + } + + if (!check_input && stream->outdev < 0) { + return NULL; + } + + *chanindex = cnum; + + return stream; +} + +static switch_status_t load_endpoints(switch_xml_t endpoints) +{ + switch_status_t status = SWITCH_STATUS_SUCCESS; + switch_xml_t param, myendpoint; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Loading endpoints ...\n"); + for (myendpoint = switch_xml_child(endpoints, "endpoint"); myendpoint; myendpoint = myendpoint->next) { + audio_endpoint_t *endpoint = NULL; + shared_audio_stream_t *stream = NULL; + char *endpoint_name = (char *) switch_xml_attr_soft(myendpoint, "name"); + + if (!endpoint_name) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing endpoint name attribute, skipping ...\n"); + continue; + } + + /* check if that endpoint name is not already used */ + endpoint = switch_core_hash_find(globals.endpoints, endpoint_name); + if (endpoint) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "An endpoint with name '%s' already exists\n", endpoint_name); + continue; + } + + endpoint = switch_core_alloc(module_pool, sizeof(*endpoint)); + if (!endpoint) { + continue; + } + switch_mutex_init(&endpoint->mutex, SWITCH_MUTEX_NESTED, module_pool); + endpoint->inchan = -1; + endpoint->outchan = -1; + switch_snprintf(endpoint->name, sizeof(endpoint->name), "%s", endpoint_name); + for (param = switch_xml_child(myendpoint, "param"); param; param = param->next) { + char *var = (char *) switch_xml_attr_soft(param, "name"); + char *val = (char *) switch_xml_attr_soft(param, "value"); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Parsing endpoint '%s' parameter %s = %s\n", endpoint_name, var, val); + if (!strcmp(var, "instream")) { + stream = check_stream(val, 1, &endpoint->inchan) ; + if (!stream) { + endpoint->in_stream = NULL; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, + "Invalid instream specified for endpoint '%s'\n", endpoint_name); + continue; + } + endpoint->in_stream = stream; + } else if (!strcmp(var, "outstream")) { + stream = check_stream(val, 0, &endpoint->outchan); + if (!stream) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, + "Invalid outstream specified for endpoint '%s'\n", endpoint_name); + endpoint->out_stream = NULL; + continue; + } + endpoint->out_stream = stream; + } + } + if (!endpoint->in_stream && !endpoint->out_stream) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, + "You need at least one stream for endpoint '%s'\n", endpoint_name); + continue; + } + if (check_stream_compat(endpoint->in_stream, endpoint->out_stream)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, + "Incomatible input and output streams for endpoint '%s'\n", endpoint_name); + continue; + } + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, + "Created endpoint '%s', instream = %s, outstream = %s\n", endpoint->name, + endpoint->in_stream ? endpoint->in_stream->name : "(none)", + endpoint->out_stream ? endpoint->out_stream->name : "(none)"); + switch_core_hash_insert(globals.endpoints, endpoint->name, endpoint); + } + return status; +} static switch_status_t load_config(void) { char *cf = "portaudio.conf"; - switch_xml_t cfg, xml, settings, param; + switch_xml_t cfg, xml, settings, streams, endpoints, param; switch_status_t status = SWITCH_STATUS_SUCCESS; if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) { @@ -1205,11 +1776,21 @@ static switch_status_t load_config(void) if (globals.ringdev < 0) { if (globals.outdev > -1) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid or no ring device configured, using output device as ring device\n"); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Invalid or no ring device configured, using output device as ring device\n"); globals.ringdev = globals.outdev; } } + /* streams and endpoints must be last, some initialization depend on globals defaults */ + if ((streams = switch_xml_child(cfg, "streams"))) { + load_streams(streams); + } + + if ((endpoints = switch_xml_child(cfg, "endpoints"))) { + load_endpoints(endpoints); + } + + switch_xml_free(xml); return status; @@ -1223,6 +1804,8 @@ SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_portaudio_shutdown) Pa_Terminate(); switch_core_hash_destroy(&globals.call_hash); + switch_core_hash_destroy(&globals.sh_streams); + switch_core_hash_destroy(&globals.endpoints); switch_event_free_subclass(MY_EVENT_RINGING); switch_event_free_subclass(MY_EVENT_MAKE_CALL); @@ -1399,7 +1982,7 @@ static switch_status_t play_dev(switch_stream_handle_t *stream, int outdev, char break; } - WriteAudioStream(audio_stream->stream, abuf, (long) olen, &(audio_stream->write_timer)); + WriteAudioStream(audio_stream->stream, abuf, (long) olen, 0, &(audio_stream->write_timer)); wrote += (int) olen; if (samples) { samples -= (int) olen; @@ -1706,6 +2289,7 @@ static switch_status_t switch_audio_stream() return SWITCH_STATUS_SUCCESS; } + PaError open_audio_stream(PABLIO_Stream **stream, const PaStreamParameters * inputParameters, const PaStreamParameters * outputParameters) { if (inputParameters->device != -1) { @@ -1714,6 +2298,75 @@ PaError open_audio_stream(PABLIO_Stream **stream, const PaStreamParameters * inp return OpenAudioStream(stream, NULL, outputParameters, globals.sample_rate, paClipOff, globals.read_codec.implementation->samples_per_packet, 0); } +PaError open_shared_audio_stream(shared_audio_stream_t *shstream, const PaStreamParameters * inputParameters, const PaStreamParameters * outputParameters) +{ + PaError err; + if (inputParameters->device != -1) { + err = OpenAudioStream(&shstream->stream, inputParameters->device != -1 ? inputParameters : NULL, + outputParameters->device != -1 ? outputParameters : NULL, shstream->sample_rate, + paClipOff, STREAM_SAMPLES_PER_PACKET(shstream), globals.dual_streams); + } else { + err = OpenAudioStream(&shstream->stream, NULL, outputParameters, shstream->sample_rate, + paClipOff, STREAM_SAMPLES_PER_PACKET(shstream), 0); + } + if (err != paNoError) { + shstream->stream = NULL; + } + return err; +} + +static int create_shared_audio_stream(shared_audio_stream_t *shstream) +{ + PaStreamParameters inputParameters, outputParameters; + PaError err; + switch_event_t *event; + + inputParameters.device = shstream->indev; + if (shstream->indev != -1) { + inputParameters.channelCount = shstream->channels; + inputParameters.sampleFormat = SAMPLE_TYPE; + inputParameters.suggestedLatency = Pa_GetDeviceInfo(inputParameters.device)->defaultLowInputLatency; + inputParameters.hostApiSpecificStreamInfo = NULL; + } + outputParameters.device = shstream->outdev; + if (shstream->outdev != -1) { + outputParameters.channelCount = shstream->channels; + outputParameters.sampleFormat = SAMPLE_TYPE; + outputParameters.suggestedLatency = Pa_GetDeviceInfo(outputParameters.device)->defaultLowOutputLatency; + outputParameters.hostApiSpecificStreamInfo = NULL; + } + + err = open_shared_audio_stream(shstream, &inputParameters, &outputParameters); + if (err != paNoError) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, + "Error opening audio device retrying (indev = %d, outdev = %d, error = %s)\n", + inputParameters.device, outputParameters.device, Pa_GetErrorText(err)); + switch_yield(1000000); + err = open_shared_audio_stream(shstream, &inputParameters, &outputParameters); + } + + if (err != paNoError) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't open audio device (indev = %d, outdev = %d, error = %s)\n", + inputParameters.device, outputParameters.device, Pa_GetErrorText(err)); + if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, MY_EVENT_ERROR_AUDIO_DEV) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Reason", Pa_GetErrorText(err)); + switch_event_fire(&event); + } + return -1; + } + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Created shared audio stream %s: %d channels %d\n", + shstream->name, shstream->sample_rate, shstream->channels); + return 0; +} + +static int destroy_shared_audio_stream(shared_audio_stream_t *shstream) +{ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Destroying shared audio stream %s\n", shstream->name); + CloseAudioStream(shstream->stream); + shstream->stream = NULL; + return 0; +} + static audio_stream_t *create_audio_stream(int indev, int outdev) { PaStreamParameters inputParameters, outputParameters; @@ -1721,12 +2374,12 @@ static audio_stream_t *create_audio_stream(int indev, int outdev) switch_event_t *event; audio_stream_t *stream; - stream = malloc(sizeof(audio_stream_t)); + stream = malloc(sizeof(*stream)); if (stream == NULL) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Unable to alloc memory\n"); return NULL; } - memset(stream, 0, sizeof(audio_stream_t)); + memset(stream, 0, sizeof(*stream)); stream->next = NULL; stream->stream = NULL; stream->indev = indev; @@ -1817,6 +2470,43 @@ static switch_status_t dtmf_call(char **argv, int argc, switch_stream_handle_t * return SWITCH_STATUS_SUCCESS; } +static switch_status_t list_shared_streams(char **argv, int argc, switch_stream_handle_t *stream) +{ + switch_hash_index_t *hi; + int cnt = 0; + for (hi = switch_hash_first(NULL, globals.sh_streams); hi; hi = switch_hash_next(hi)) { + const void *var; + void *val; + shared_audio_stream_t *s = NULL; + switch_hash_this(hi, &var, NULL, &val); + s = val; + stream->write_function(stream, "%s> indev: %d, outdev: %d, sample-rate: %d, codec-ms: %d, channels: %d\n", + s->name, s->indev, s->outdev, s->sample_rate, s->codec_ms, s->channels); + cnt++; + } + stream->write_function(stream, "Total streams: %d\n", cnt); + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t list_endpoints(char **argv, int argc, switch_stream_handle_t *stream) +{ + switch_hash_index_t *hi; + int cnt = 0; + for (hi = switch_hash_first(NULL, globals.endpoints); hi; hi = switch_hash_next(hi)) { + const void *var; + void *val; + audio_endpoint_t *e = NULL; + switch_hash_this(hi, &var, NULL, &val); + e = val; + stream->write_function(stream, "%s> instream: %s, outstream: %s\n", + e->name, e->in_stream ? e->in_stream->name : "(none)", + e->out_stream ? e->out_stream->name : "(none)"); + cnt++; + } + stream->write_function(stream, "Total endpoints: %d\n", cnt); + return SWITCH_STATUS_SUCCESS; +} + static switch_status_t close_streams(char **argv, int argc, switch_stream_handle_t *stream) { if (globals.call_list) { @@ -1978,8 +2668,8 @@ static switch_status_t looptest(char **argv, int argc, switch_stream_handle_t *s if (globals.destroying_streams || ! globals.main_stream->stream) { break; } - if ((samples = ReadAudioStream(globals.main_stream->stream, globals.read_frame.data, globals.read_codec.implementation->samples_per_packet, &globals.read_timer))) { - WriteAudioStream(globals.main_stream->stream, globals.read_frame.data, (long) samples, &(globals.main_stream->write_timer)); + if ((samples = ReadAudioStream(globals.main_stream->stream, globals.read_frame.data, globals.read_codec.implementation->samples_per_packet, 0, &globals.read_timer))) { + WriteAudioStream(globals.main_stream->stream, globals.read_frame.data, (long) samples, 0, &(globals.main_stream->write_timer)); success = 1; } switch_yield(10000); @@ -2386,6 +3076,8 @@ SWITCH_STANDARD_API(pa_cmd) "pa playdev # [ringtest|] [seconds] [no_close]\n" "pa ringfile [filename]\n" "pa looptest\n" + "pa shstreams\n" + "pa endpoints\n" "--------------------------------------------------------------------------------\n"; @@ -2510,6 +3202,10 @@ SWITCH_STANDARD_API(pa_cmd) func = looptest; } else if (!strcasecmp(argv[0], "ringfile")) { func = set_ringfile; + } else if (!strcasecmp(argv[0], "shstreams")) { + func = list_shared_streams; + } else if (!strcasecmp(argv[0], "endpoints")) { + func = list_endpoints; } else { stream->write_function(stream, "Unknown Command or not enough args [%s]\n", argv[0]); } diff --git a/src/mod/endpoints/mod_portaudio/pablio.c b/src/mod/endpoints/mod_portaudio/pablio.c index 05fdf0f280..6423dc762d 100644 --- a/src/mod/endpoints/mod_portaudio/pablio.c +++ b/src/mod/endpoints/mod_portaudio/pablio.c @@ -78,14 +78,23 @@ static PaError PABLIO_TermFIFO(PaUtilRingBuffer * rbuf); static int iblockingIOCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo * timeInfo, PaStreamCallbackFlags statusFlags, void *userData) { + int c = 0, i = 0, j = 0; PABLIO_Stream *data = (PABLIO_Stream *) userData; long numBytes = data->bytesPerFrame * framesPerBuffer; + const int16_t *inputSamples = inputBuffer; + int16_t *chanSamples = (int16_t*)data->iobuff; /* This may get called with NULL inputBuffer during initial setup. */ if (inputBuffer != NULL) { - if (PaUtil_WriteRingBuffer(&data->inFIFO, inputBuffer, numBytes) != numBytes) { - PaUtil_FlushRingBuffer(&data->inFIFO); - PaUtil_WriteRingBuffer(&data->inFIFO, inputBuffer, numBytes); + /* retrieve the data for each channel and put it in the ring buffer */ + for (c = 0; c < data->channelCount; c++) { + for (i = 0, j = c; i < framesPerBuffer; j += data->channelCount, i++) { + chanSamples[i] = inputSamples[j]; + } + if (PaUtil_WriteRingBuffer(&data->inFIFOs[c], chanSamples, numBytes) != numBytes) { + PaUtil_FlushRingBuffer(&data->inFIFOs[c]); + PaUtil_WriteRingBuffer(&data->inFIFOs[c], inputBuffer, numBytes); + } } } @@ -97,13 +106,21 @@ static int oblockingIOCallback(const void *inputBuffer, void *outputBuffer, { PABLIO_Stream *data = (PABLIO_Stream *) userData; long numBytes = data->bytesPerFrame * framesPerBuffer; + int16_t *outputSamples = outputBuffer; + int16_t *chanSamples = (short *)data->iobuff; + int c = 0, i = 0, j = 0; if (outputBuffer != NULL) { - int i; - int numRead = PaUtil_ReadRingBuffer(&data->outFIFO, outputBuffer, numBytes); - /* Zero out remainder of buffer if we run out of data. */ - for (i = numRead; i < numBytes; i++) { - ((char *) outputBuffer)[i] = 0; + for (c = 0; c < data->channelCount; c++) { + int numRead = PaUtil_ReadRingBuffer(&data->outFIFOs[c], chanSamples, numBytes); + numRead = numRead / sizeof(int16_t); + for (i = 0, j = c; i < framesPerBuffer; j += data->channelCount, i++) { + if (i < numRead) { + outputSamples[j] = chanSamples[i]; + } else { + outputSamples[j] = 0; + } + } } } @@ -143,7 +160,7 @@ static PaError PABLIO_TermFIFO(PaUtilRingBuffer * rbuf) * Write data to ring buffer. * Will not return until all the data has been written. */ -long WriteAudioStream(PABLIO_Stream * aStream, void *data, long numFrames, switch_timer_t *timer) +long WriteAudioStream(PABLIO_Stream * aStream, void *data, long numFrames, int chan, switch_timer_t *timer) { long bytesWritten; char *p = (char *) data; @@ -151,12 +168,12 @@ long WriteAudioStream(PABLIO_Stream * aStream, void *data, long numFrames, switc switch_core_timer_next(timer); - bytesWritten = PaUtil_WriteRingBuffer(&aStream->outFIFO, p, numBytes); + bytesWritten = PaUtil_WriteRingBuffer(&aStream->outFIFOs[chan], p, numBytes); numBytes -= bytesWritten; p += bytesWritten; if (numBytes > 0) { - PaUtil_FlushRingBuffer(&aStream->outFIFO); + PaUtil_FlushRingBuffer(&aStream->outFIFOs[chan]); return 0; } return numFrames; @@ -166,7 +183,7 @@ long WriteAudioStream(PABLIO_Stream * aStream, void *data, long numFrames, switc * Read data from ring buffer. * Will not return until all the data has been read. */ -long ReadAudioStream(PABLIO_Stream * aStream, void *data, long numFrames, switch_timer_t *timer) +long ReadAudioStream(PABLIO_Stream * aStream, void *data, long numFrames, int chan, switch_timer_t *timer) { long bytesRead = 0; char *p = (char *) data; @@ -177,17 +194,17 @@ long ReadAudioStream(PABLIO_Stream * aStream, void *data, long numFrames, switch while (totalBytes < neededBytes && --max > 0) { - avail = PaUtil_GetRingBufferReadAvailable(&aStream->inFIFO); + avail = PaUtil_GetRingBufferReadAvailable(&aStream->inFIFOs[chan]); //printf("AVAILABLE BYTES %ld pass %d\n", avail, 5000 - max); if (avail >= neededBytes * 6) { - PaUtil_FlushRingBuffer(&aStream->inFIFO); + PaUtil_FlushRingBuffer(&aStream->inFIFOs[chan]); avail = 0; } else { bytesRead = 0; if (totalBytes < neededBytes && avail >= neededBytes) { - bytesRead = PaUtil_ReadRingBuffer(&aStream->inFIFO, p, neededBytes); + bytesRead = PaUtil_ReadRingBuffer(&aStream->inFIFOs[chan], p, neededBytes); totalBytes += bytesRead; } @@ -206,9 +223,9 @@ long ReadAudioStream(PABLIO_Stream * aStream, void *data, long numFrames, switch * Return the number of frames that could be written to the stream without * having to wait. */ -long GetAudioStreamWriteable(PABLIO_Stream * aStream) +long GetAudioStreamWriteable(PABLIO_Stream * aStream, int chan) { - int bytesEmpty = PaUtil_GetRingBufferWriteAvailable(&aStream->outFIFO); + int bytesEmpty = PaUtil_GetRingBufferWriteAvailable(&aStream->outFIFOs[chan]); return bytesEmpty / aStream->bytesPerFrame; } @@ -216,9 +233,9 @@ 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) +long GetAudioStreamReadable(PABLIO_Stream * aStream, int chan) { - int bytesFull = PaUtil_GetRingBufferReadAvailable(&aStream->inFIFO); + int bytesFull = PaUtil_GetRingBufferReadAvailable(&aStream->inFIFOs[chan]); return bytesFull / aStream->bytesPerFrame; } @@ -251,7 +268,8 @@ PaError OpenAudioStream(PABLIO_Stream ** rwblPtr, PABLIO_Stream *aStream; long numFrames; //long numBytes; - //int channels = 1; + int c = 0; + int channels = 1; if (!(inputParameters || outputParameters)) { return -1; @@ -262,31 +280,34 @@ PaError OpenAudioStream(PABLIO_Stream ** rwblPtr, switch_assert(aStream); memset(aStream, 0, sizeof(PABLIO_Stream)); - /* if (inputParameters) { channels = inputParameters->channelCount; } else if (outputParameters) { channels = outputParameters->channelCount; } - */ numFrames = RoundUpToNextPowerOf2(samples_per_packet * 5); aStream->bytesPerFrame = bytesPerSample; + aStream->channelCount = channels; /* Initialize Ring Buffers */ if (inputParameters) { - err = PABLIO_InitFIFO(&aStream->inFIFO, numFrames, aStream->bytesPerFrame); - if (err != paNoError) { - goto error; + for (c = 0; c < channels; c++) { + err = PABLIO_InitFIFO(&aStream->inFIFOs[c], numFrames, aStream->bytesPerFrame); + if (err != paNoError) { + goto error; + } } aStream->has_in = 1; } if (outputParameters) { - err = PABLIO_InitFIFO(&aStream->outFIFO, numFrames, aStream->bytesPerFrame); - if (err != paNoError) { - goto error; + for (c = 0; c < channels; c++) { + err = PABLIO_InitFIFO(&aStream->outFIFOs[c], numFrames, aStream->bytesPerFrame); + if (err != paNoError) { + goto error; + } } aStream->has_out = 1; } @@ -355,17 +376,21 @@ PaError CloseAudioStream(PABLIO_Stream * aStream) { int bytesEmpty; int byteSize; + int c = 0; - byteSize = aStream->outFIFO.bufferSize; - if (aStream->has_out) { - /* If we are writing data, make sure we play everything written. */ - if (byteSize > 0) { - bytesEmpty = PaUtil_GetRingBufferWriteAvailable(&aStream->outFIFO); - while (bytesEmpty < byteSize) { - Pa_Sleep(10); - bytesEmpty = PaUtil_GetRingBufferWriteAvailable(&aStream->outFIFO); + + for (c = 0; c < aStream->channelCount; c++) { + byteSize = aStream->outFIFOs[c].bufferSize; + + /* If we are writing data, make sure we play everything written. */ + if (byteSize > 0) { + bytesEmpty = PaUtil_GetRingBufferWriteAvailable(&aStream->outFIFOs[c]); + while (bytesEmpty < byteSize) { + Pa_Sleep(10); + bytesEmpty = PaUtil_GetRingBufferWriteAvailable(&aStream->outFIFOs[c]); + } } } } @@ -401,11 +426,15 @@ PaError CloseAudioStream(PABLIO_Stream * aStream) } if (aStream->has_in) { - PABLIO_TermFIFO(&aStream->inFIFO); + for (c = 0; c < aStream->channelCount; c++) { + PABLIO_TermFIFO(&aStream->inFIFOs[c]); + } } if (aStream->has_out) { - PABLIO_TermFIFO(&aStream->outFIFO); + for (c = 0; c < aStream->channelCount; c++) { + PABLIO_TermFIFO(&aStream->outFIFOs[c]); + } } free(aStream); @@ -413,3 +442,4 @@ PaError CloseAudioStream(PABLIO_Stream * aStream) return paNoError; } + diff --git a/src/mod/endpoints/mod_portaudio/pablio.h b/src/mod/endpoints/mod_portaudio/pablio.h index 0bd3c41e46..435e4b0ca7 100644 --- a/src/mod/endpoints/mod_portaudio/pablio.h +++ b/src/mod/endpoints/mod_portaudio/pablio.h @@ -56,17 +56,30 @@ extern "C" { #include - typedef struct { - PaUtilRingBuffer inFIFO; - PaUtilRingBuffer outFIFO; - PaStream *istream; - PaStream *ostream; - PaStream *iostream; - int bytesPerFrame; - int do_dual; - int has_in; - int has_out; - } PABLIO_Stream; +/*! Maximum number of channels per stream */ +#define MAX_IO_CHANNELS 2 + +/*! Maximum numer of milliseconds per packet */ +#define MAX_IO_MS 100 + +/*! Maximum sampling rate (48Khz) */ +#define MAX_SAMPLING_RATE 48000 + +/* Maximum size of a read */ +#define MAX_IO_BUFFER (((MAX_IO_MS * MAX_SAMPLING_RATE)/1000)*sizeof(int16_t)) +typedef struct { + PaStream *istream; + PaStream *ostream; + PaStream *iostream; + int bytesPerFrame; + int do_dual; + int has_in; + int has_out; + PaUtilRingBuffer inFIFOs[MAX_IO_CHANNELS]; + PaUtilRingBuffer outFIFOs[MAX_IO_CHANNELS]; + int channelCount; + char iobuff[MAX_IO_BUFFER]; +} PABLIO_Stream; /* Values for flags for OpenAudioStream(). */ #define PABLIO_READ (1<<0) @@ -79,25 +92,25 @@ extern "C" { * Write data to ring buffer. * Will not return until all the data has been written. */ - long WriteAudioStream(PABLIO_Stream * aStream, void *data, long numFrames, switch_timer_t *timer); +long WriteAudioStream(PABLIO_Stream * aStream, void *data, long numFrames, int chan, switch_timer_t *timer); /************************************************************ * Read data from ring buffer. * Will not return until all the data has been read. */ - long ReadAudioStream(PABLIO_Stream * aStream, void *data, long numFrames, switch_timer_t *timer); +long ReadAudioStream(PABLIO_Stream * aStream, void *data, long numFrames, int chan, switch_timer_t *timer); /************************************************************ * Return the number of frames that could be written to the stream without * having to wait. */ - long GetAudioStreamWriteable(PABLIO_Stream * aStream); +long GetAudioStreamWriteable(PABLIO_Stream * aStream, int chan); /************************************************************ * Return the number of frames that are available to be read from the * stream without having to wait. */ - long GetAudioStreamReadable(PABLIO_Stream * aStream); +long GetAudioStreamReadable(PABLIO_Stream * aStream, int chan); /************************************************************ * Opens a PortAudio stream with default characteristics. @@ -107,12 +120,12 @@ extern "C" { * PABLIO_READ, PABLIO_WRITE, or PABLIO_READ_WRITE, * and either PABLIO_MONO or PABLIO_STEREO */ - PaError OpenAudioStream(PABLIO_Stream ** rwblPtr, - const PaStreamParameters * inputParameters, - const PaStreamParameters * outputParameters, - double sampleRate, PaStreamCallbackFlags statusFlags, long samples_per_packet, int do_dual); +PaError OpenAudioStream(PABLIO_Stream ** rwblPtr, + const PaStreamParameters * inputParameters, + const PaStreamParameters * outputParameters, + double sampleRate, PaStreamCallbackFlags statusFlags, long samples_per_packet, int do_dual); - PaError CloseAudioStream(PABLIO_Stream * aStream); +PaError CloseAudioStream(PABLIO_Stream * aStream); #ifdef __cplusplus } diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.c b/src/mod/endpoints/mod_sofia/mod_sofia.c index 99c00424dc..b672cccb80 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.c +++ b/src/mod/endpoints/mod_sofia/mod_sofia.c @@ -569,9 +569,31 @@ static switch_status_t sofia_answer_channel(switch_core_session_t *session) const char *val; const char *b_sdp = NULL; int is_proxy = 0; + int is_3pcc = 0; char *sticky = NULL; const char *call_info = switch_channel_get_variable(channel, "presence_call_info_full"); + + if(sofia_test_flag(tech_pvt, TFLAG_3PCC_INVITE)) { + // SNARK: complete hack to get final ack sent when a 3pcc invite has been passed from the other leg in bypass_media mode. + // This code handles the pass_indication sent after the 3pcc ack is received by the other leg in the is_3pcc && is_proxy case below. + // Is there a better place to hang this...? + b_sdp = switch_channel_get_variable(channel, SWITCH_B_SDP_VARIABLE); + sofia_glue_tech_set_local_sdp(tech_pvt, b_sdp, SWITCH_TRUE); + + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "3PCC-PROXY nomedia - sending ack\n"); + nua_ack(tech_pvt->nh, + TAG_IF(!zstr(tech_pvt->user_via), SIPTAG_VIA_STR(tech_pvt->user_via)), + SIPTAG_CONTACT_STR(tech_pvt->reply_contact), + SOATAG_USER_SDP_STR(tech_pvt->local_sdp_str), + SOATAG_REUSE_REJECTED(1), + SOATAG_RTP_SELECT(1), SOATAG_ORDERED_USER(1), SOATAG_AUDIO_AUX("cn telephone-event"), + TAG_IF(sofia_test_pflag(tech_pvt->profile, PFLAG_DISABLE_100REL), NUTAG_INCLUDE_EXTRA_SDP(1)), + TAG_END()); + sofia_clear_flag(tech_pvt, TFLAG_3PCC_INVITE); // all done + return SWITCH_STATUS_SUCCESS; + } + if (sofia_test_flag(tech_pvt, TFLAG_ANS) || switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND) { return SWITCH_STATUS_SUCCESS; } @@ -579,8 +601,9 @@ static switch_status_t sofia_answer_channel(switch_core_session_t *session) b_sdp = switch_channel_get_variable(channel, SWITCH_B_SDP_VARIABLE); is_proxy = (switch_channel_test_flag(channel, CF_PROXY_MODE) || switch_channel_test_flag(channel, CF_PROXY_MEDIA)); + is_3pcc = (sofia_test_pflag(tech_pvt->profile, PFLAG_3PCC_PROXY) && sofia_test_flag(tech_pvt, TFLAG_3PCC)); - if (b_sdp && is_proxy) { + if (b_sdp && is_proxy && !is_3pcc) { sofia_glue_tech_set_local_sdp(tech_pvt, b_sdp, SWITCH_TRUE); if (switch_channel_test_flag(channel, CF_PROXY_MEDIA)) { @@ -591,23 +614,35 @@ static switch_status_t sofia_answer_channel(switch_core_session_t *session) } } else { /* This if statement check and handles the 3pcc proxy mode */ - if (sofia_test_pflag(tech_pvt->profile, PFLAG_3PCC_PROXY) && sofia_test_flag(tech_pvt, TFLAG_3PCC)) { + if (is_3pcc) { + if(!is_proxy) { tech_pvt->num_codecs = 0; sofia_glue_tech_prepare_codecs(tech_pvt); tech_pvt->local_sdp_str = NULL; sofia_glue_tech_choose_port(tech_pvt, 0); sofia_glue_set_local_sdp(tech_pvt, NULL, 0, NULL, 0); + } else { + sofia_glue_tech_set_local_sdp(tech_pvt, b_sdp, SWITCH_TRUE); + + if (switch_channel_test_flag(channel, CF_PROXY_MEDIA)) { + sofia_glue_tech_patch_sdp(tech_pvt); + if (sofia_glue_activate_rtp(tech_pvt, 0) != SWITCH_STATUS_SUCCESS) { + return SWITCH_STATUS_FALSE; + } + } + } /* Send the 200 OK */ if (!sofia_test_flag(tech_pvt, TFLAG_BYE)) { char *extra_headers = sofia_glue_get_extra_headers(channel, SOFIA_SIP_RESPONSE_HEADER_PREFIX); if (sofia_use_soa(tech_pvt)) { nua_respond(tech_pvt->nh, SIP_200_OK, + TAG_IF(is_proxy, NUTAG_AUTOANSWER(0)), SIPTAG_CONTACT_STR(tech_pvt->profile->url), SOATAG_USER_SDP_STR(tech_pvt->local_sdp_str), TAG_IF(call_info, SIPTAG_CALL_INFO_STR(call_info)), - SOATAG_REUSE_REJECTED(1), + SOATAG_REUSE_REJECTED(1), TAG_IF(is_proxy, SOATAG_RTP_SELECT(1)), SOATAG_ORDERED_USER(1), SOATAG_AUDIO_AUX("cn telephone-event"), NUTAG_INCLUDE_EXTRA_SDP(1), TAG_IF(!zstr(extra_headers), SIPTAG_HEADER_STR(extra_headers)), TAG_IF(switch_stristr("update_display", tech_pvt->x_freeswitch_support_remote), @@ -639,6 +674,14 @@ static switch_status_t sofia_answer_channel(switch_core_session_t *session) /* Regain lock on sofia */ switch_mutex_lock(tech_pvt->sofia_mutex); + if(is_proxy) { + sofia_clear_flag(tech_pvt, TFLAG_3PCC_HAS_ACK); + sofia_clear_flag(tech_pvt, TFLAG_3PCC); + // This sends the message to the other leg that causes it to call the TFLAG_3PCC_INVITE code at the start of this function. + // Is there another message it would be better to hang this on though? + switch_core_session_pass_indication(session, SWITCH_MESSAGE_INDICATE_ANSWER); + } + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "3PCC-PROXY, Done waiting for ACK\n"); return SWITCH_STATUS_SUCCESS; } @@ -1345,7 +1388,7 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi case SWITCH_MESSAGE_INDICATE_JITTER_BUFFER: { if (switch_rtp_ready(tech_pvt->rtp_session)) { - int len, maxlen = 0, qlen = 0, maxqlen = 50, max_drift = 0; + int len = 0, maxlen = 0, qlen = 0, maxqlen = 50, max_drift = 0; if (msg->string_arg) { char *p, *q; @@ -1626,7 +1669,7 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi ip = switch_channel_get_variable(channel, SWITCH_REMOTE_MEDIA_IP_VARIABLE); port = switch_channel_get_variable(channel, SWITCH_REMOTE_MEDIA_PORT_VARIABLE); if (ip && port) { - sofia_glue_set_local_sdp(tech_pvt, ip, atoi(port), msg->string_arg, 1); + sofia_glue_set_local_sdp(tech_pvt, ip, (switch_port_t)atoi(port), msg->string_arg, 1); } if (!sofia_test_flag(tech_pvt, TFLAG_BYE)) { @@ -1666,7 +1709,7 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi port = switch_channel_get_variable(other_channel, SWITCH_REMOTE_MEDIA_PORT_VARIABLE); switch_core_session_rwunlock(other_session); if (ip && port) { - sofia_glue_set_local_sdp(tech_pvt, ip, atoi(port), NULL, 1); + sofia_glue_set_local_sdp(tech_pvt, ip, (switch_port_t)atoi(port), NULL, 1); } } @@ -1685,6 +1728,10 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi switch_channel_get_name(channel), msg->string_arg); sofia_glue_tech_set_local_sdp(tech_pvt, msg->string_arg, SWITCH_TRUE); + if(zstr(tech_pvt->local_sdp_str)) { + sofia_set_flag(tech_pvt, TFLAG_3PCC_INVITE); + } + sofia_set_flag_locked(tech_pvt, TFLAG_SENT_UPDATE); switch_channel_set_flag(channel, CF_REQ_MEDIA); sofia_glue_do_invite(session); @@ -2183,6 +2230,23 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi SIPTAG_PAYLOAD_STR(tech_pvt->local_sdp_str), TAG_IF(!zstr(extra_headers), SIPTAG_HEADER_STR(extra_headers)), TAG_END()); } + if (sofia_test_pflag(tech_pvt->profile, PFLAG_3PCC_PROXY) && sofia_test_flag(tech_pvt, TFLAG_3PCC)) { + /* Unlock the session signal to allow the ack to make it in */ + // Maybe we should timeout? + switch_mutex_unlock(tech_pvt->sofia_mutex); + + while (switch_channel_ready(channel) && !sofia_test_flag(tech_pvt, TFLAG_3PCC_HAS_ACK)) { + switch_cond_next(); + } + + /* Regain lock on sofia */ + switch_mutex_lock(tech_pvt->sofia_mutex); + + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "3PCC-PROXY, Done waiting for ACK\n"); + sofia_clear_flag(tech_pvt, TFLAG_3PCC); + sofia_clear_flag(tech_pvt, TFLAG_3PCC_HAS_ACK); + switch_core_session_pass_indication(session, SWITCH_MESSAGE_INDICATE_ANSWER); + } } else { nua_respond(tech_pvt->nh, code, su_strdup(nua_handle_home(tech_pvt->nh), reason), SIPTAG_CONTACT_STR(tech_pvt->reply_contact), TAG_IF(!zstr(extra_headers), SIPTAG_HEADER_STR(extra_headers)), TAG_END()); @@ -4379,6 +4443,14 @@ static switch_call_cause_t sofia_outgoing_channel(switch_core_session_t *session } else { sofia_clear_flag(ctech_pvt, TFLAG_ENABLE_SOA); } + + /* SNARK: lets copy this across so we can see if we're the other leg of 3PCC + bypass_media... */ + if (sofia_test_flag(ctech_pvt, TFLAG_3PCC) && (switch_channel_test_flag(o_channel, CF_PROXY_MODE) || switch_channel_test_flag(o_channel, CF_PROXY_MEDIA))) { + sofia_set_flag(tech_pvt, TFLAG_3PCC_INVITE); + sofia_set_flag(tech_pvt, TFLAG_LATE_NEGOTIATION); + } else { + sofia_clear_flag(tech_pvt, TFLAG_3PCC_INVITE); + } } if (switch_channel_test_flag(o_channel, CF_PROXY_MEDIA)) { @@ -4634,7 +4706,7 @@ static void general_event_handler(switch_event_t *event) id = switch_mprintf("sip:%s@%s", user, host); switch_assert(id); - + for (m = list->head; m; m = m->next) { contact = sofia_glue_get_url_from_contact(m->val, 0); @@ -4647,10 +4719,9 @@ static void general_event_handler(switch_event_t *event) nua_message(nh, NUTAG_NEWSUB(1), SIPTAG_CONTENT_TYPE_STR(ct), TAG_IF(!zstr(body), SIPTAG_PAYLOAD_STR(body)), TAG_IF(!zstr(subject), SIPTAG_SUBJECT_STR(subject)), TAG_END()); - - - free(id); } + + free(id); switch_console_free_matches(&list); sofia_glue_release_profile(profile); diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.h b/src/mod/endpoints/mod_sofia/mod_sofia.h index 65404050e4..4b41189a02 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.h +++ b/src/mod/endpoints/mod_sofia/mod_sofia.h @@ -294,6 +294,7 @@ typedef enum { TFLAG_AUTOFLUSH_DURING_BRIDGE, TFLAG_NOTIMER_DURING_BRIDGE, TFLAG_JB_PAUSED, + TFLAG_3PCC_INVITE, /* No new flags below this line */ TFLAG_MAX } TFLAGS; @@ -795,7 +796,7 @@ switch_status_t sofia_glue_activate_rtp(private_object_t *tech_pvt, switch_rtp_f void sofia_glue_deactivate_rtp(private_object_t *tech_pvt); -void sofia_glue_set_local_sdp(private_object_t *tech_pvt, const char *ip, uint32_t port, const char *sr, int force); +void sofia_glue_set_local_sdp(private_object_t *tech_pvt, const char *ip, switch_port_t port, const char *sr, int force); void sofia_glue_tech_prepare_codecs(private_object_t *tech_pvt); @@ -1065,6 +1066,6 @@ void sofia_glue_proxy_codec(switch_core_session_t *session, const char *r_sdp); switch_status_t sofia_glue_sdp_map(const char *r_sdp, switch_event_t **fmtp, switch_event_t **pt); void sofia_glue_build_vid_refresh_message(switch_core_session_t *session, const char *pl); void sofia_glue_check_dtmf_type(private_object_t *tech_pvt); -void sofia_glue_parse_rtp_bugs(uint32_t *flag_pole, const char *str); +void sofia_glue_parse_rtp_bugs(switch_rtp_bug_flag_t *flag_pole, const char *str); char *sofia_glue_gen_contact_str(sofia_profile_t *profile, sip_t const *sip, sofia_nat_parse_t *np); void sofia_glue_pause_jitterbuffer(switch_core_session_t *session, switch_bool_t on); diff --git a/src/mod/endpoints/mod_sofia/sofia.c b/src/mod/endpoints/mod_sofia/sofia.c index be24a00d98..12b0280974 100644 --- a/src/mod/endpoints/mod_sofia/sofia.c +++ b/src/mod/endpoints/mod_sofia/sofia.c @@ -1326,6 +1326,10 @@ void *SWITCH_THREAD_FUNC sofia_profile_worker_thread_run(switch_thread_t *thread (statements == 0 || (statements <= 1024 && (switch_micro_time_now() - last_commit)/1000 < profile->trans_timeout)))) { switch_interval_time_t sleepy_time = !statements ? 1000000 : switch_micro_time_now() - last_commit - profile->trans_timeout*1000; + + if (sleepy_time < 1000 || sleepy_time > 1000000) { + sleepy_time = 1000; + } if (sql || (switch_queue_pop_timeout(profile->sql_queue, &pop, sleepy_time) == SWITCH_STATUS_SUCCESS && pop)) { switch_size_t newlen; @@ -4373,6 +4377,11 @@ static void sofia_handle_sip_r_invite(switch_core_session_t *session, int status if (sofia_test_pflag(profile, PFLAG_MANUAL_REDIRECT)) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Redirect: Transfering to %s %s %s\n", p_contact->m_url->url_user, sip_redirect_dialplan, sip_redirect_context); + + if (switch_true(switch_channel_get_variable(channel, "recording_follow_transfer"))) { + switch_core_media_bug_transfer_recordings(session, a_session); + } + switch_ivr_session_transfer(a_session, p_contact->m_url->url_user, sip_redirect_dialplan, sip_redirect_context); switch_channel_hangup(channel, SWITCH_CAUSE_REDIRECTION_TO_NEW_DESTINATION); } else if ((!strcmp(profile->sipip, p_contact->m_url->url_host)) @@ -4380,6 +4389,11 @@ static void sofia_handle_sip_r_invite(switch_core_session_t *session, int status || (switch_xml_locate_domain(p_contact->m_url->url_host, NULL, &root, &domain) == SWITCH_STATUS_SUCCESS)) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Redirect: Transfering to %s\n", p_contact->m_url->url_user); + + if (switch_true(switch_channel_get_variable(channel, "recording_follow_transfer"))) { + switch_core_media_bug_transfer_recordings(session, a_session); + } + switch_ivr_session_transfer(a_session, p_contact->m_url->url_user, NULL, NULL); switch_channel_hangup(channel, SWITCH_CAUSE_REDIRECTION_TO_NEW_DESTINATION); switch_xml_free(root); @@ -4520,7 +4534,9 @@ static void sofia_handle_sip_r_invite(switch_core_session_t *session, int status } } - switch_core_session_queue_message(other_session, msg); + /* SNARK: message to respond to reinvite wasn't being delivered in 3pcc+bypass media case. */ + //switch_core_session_queue_message(other_session, msg); + switch_core_session_receive_message(other_session, msg); switch_core_session_rwunlock(other_session); } @@ -4694,6 +4710,7 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, const char *uuid; switch_core_session_t *other_session = NULL; switch_channel_t *other_channel = NULL; + private_object_t *other_tech_pvt = NULL; char st[80] = ""; int is_dup_sdp = 0; switch_event_t *s_event = NULL; @@ -4894,6 +4911,24 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, break; case nua_callstate_completing: { + if (r_sdp && sofia_test_flag(tech_pvt, TFLAG_3PCC_INVITE) && !sofia_test_flag(tech_pvt, TFLAG_SDP)) { + sofia_set_flag(tech_pvt, TFLAG_SDP); + + if ((uuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE)) + && (other_session = switch_core_session_locate(uuid))) { + other_channel = switch_core_session_get_channel(other_session); + other_tech_pvt = switch_core_session_get_private(other_session); + + if (!switch_channel_get_variable(other_channel, SWITCH_B_SDP_VARIABLE)) { + switch_channel_set_variable(other_channel, SWITCH_B_SDP_VARIABLE, r_sdp); + } + switch_core_session_queue_indication(other_session, SWITCH_MESSAGE_INDICATE_ANSWER); + switch_core_session_rwunlock(other_session); + } + goto done; + + } + if (sofia_test_pflag(profile, PFLAG_TRACK_CALLS)) { const char *invite_full_via = switch_channel_get_variable(tech_pvt->channel, "sip_invite_full_via"); const char *invite_route_uri = switch_channel_get_variable(tech_pvt->channel, "sip_invite_route_uri"); @@ -4971,6 +5006,14 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, char *br_a = b_private->uuid; if (br_b) { + switch_core_session_t *tmp; + + if (switch_true(switch_channel_get_variable(channel, "recording_follow_transfer")) && + (tmp = switch_core_session_locate(br_a))) { + switch_core_media_bug_transfer_recordings(session, tmp); + switch_core_session_rwunlock(tmp); + } + switch_ivr_uuid_bridge(br_a, br_b); switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "ATTENDED_TRANSFER"); sofia_clear_flag_locked(tech_pvt, TFLAG_SIP_HOLD); @@ -4998,10 +5041,12 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, switch_channel_hangup(channel, SWITCH_CAUSE_INCOMPATIBLE_DESTINATION); } } else { + if (sofia_test_pflag(profile, PFLAG_3PCC)) { if (switch_channel_test_flag(channel, CF_PROXY_MODE) || switch_channel_test_flag(channel, CF_PROXY_MEDIA)) { - goto done; + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "No SDP in INVITE and 3pcc=yes cannot work with bypass or proxy media, hanging up.\n"); + switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "3PCC DISABLED"); + switch_channel_hangup(channel, SWITCH_CAUSE_MANDATORY_IE_MISSING); } else { - if (sofia_test_pflag(profile, PFLAG_3PCC)) { switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "RECEIVED_NOSDP"); sofia_glue_tech_choose_port(tech_pvt, 0); sofia_glue_set_local_sdp(tech_pvt, NULL, 0, NULL, 0); @@ -5020,8 +5065,10 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, SIPTAG_CONTACT_STR(tech_pvt->profile->url), SIPTAG_CONTENT_TYPE_STR("application/sdp"), SIPTAG_PAYLOAD_STR(tech_pvt->local_sdp_str), TAG_END()); } + } } else if (sofia_test_pflag(profile, PFLAG_3PCC_PROXY)) { //3PCC proxy mode delays the 200 OK until the call is answered + // so can be made to work with bypass media as we have time to find out what the other end thinks codec offer should be... switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "RECEIVED_NOSDP"); sofia_set_flag_locked(tech_pvt, TFLAG_3PCC); //sofia_glue_tech_choose_port(tech_pvt, 0); @@ -5036,11 +5083,29 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, } goto done; } - } } else if (tech_pvt && sofia_test_flag(tech_pvt, TFLAG_SDP) && !r_sdp) { - nua_respond(tech_pvt->nh, SIP_200_OK, TAG_END()); sofia_set_flag_locked(tech_pvt, TFLAG_NOSDP_REINVITE); + if (switch_channel_test_flag(channel, CF_PROXY_MODE) || switch_channel_test_flag(channel, CF_PROXY_MEDIA)) { + sofia_set_flag_locked(tech_pvt, TFLAG_3PCC); + if ((uuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE)) + && (other_session = switch_core_session_locate(uuid))) { + switch_core_session_message_t *msg; + msg = switch_core_session_alloc(other_session, sizeof(*msg)); + msg->message_id = SWITCH_MESSAGE_INDICATE_MEDIA_REDIRECT; + msg->from = __FILE__; + msg->string_arg = NULL; + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Passing NOSDP to other leg.\n"); + switch_core_session_queue_message(other_session, msg); + switch_core_session_rwunlock(other_session); + } else { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, + "NOSDP Re-INVITE to a proxy mode channel that is not in a bridge.\n"); + switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + } + goto done; + } + nua_respond(tech_pvt->nh, SIP_200_OK, TAG_END()); goto done; } else { ss_state = nua_callstate_completed; @@ -5052,12 +5117,10 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, break; case nua_callstate_completed: if (r_sdp) { + const char *var; uint8_t match = 0, is_ok = 1, is_t38 = 0; tech_pvt->hold_laps = 0; - if (r_sdp) { - const char *var; - if ((var = switch_channel_get_variable(channel, "sip_ignore_reinvites")) && switch_true(var)) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Ignoring Re-invite\n"); nua_respond(tech_pvt->nh, SIP_200_OK, TAG_END()); @@ -5218,7 +5281,6 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, nua_respond(tech_pvt->nh, SIP_488_NOT_ACCEPTABLE, TAG_END()); } } - } break; case nua_callstate_ready: if (r_sdp && !is_dup_sdp && switch_rtp_ready(tech_pvt->rtp_session)) { @@ -5251,10 +5313,34 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, } if (r_sdp && sofia_test_flag(tech_pvt, TFLAG_NOSDP_REINVITE)) { + sofia_clear_flag_locked(tech_pvt, TFLAG_NOSDP_REINVITE); + if (switch_channel_test_flag(channel, CF_PROXY_MODE) || switch_channel_test_flag(channel, CF_PROXY_MEDIA)) { + if (switch_channel_test_flag(channel, CF_PROXY_MEDIA)) { + if (sofia_glue_activate_rtp(tech_pvt, 0) != SWITCH_STATUS_SUCCESS) { + goto done; + } + } + + if ((uuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE)) + && (other_session = switch_core_session_locate(uuid))) { + other_channel = switch_core_session_get_channel(other_session); + if (!switch_channel_get_variable(other_channel, SWITCH_B_SDP_VARIABLE)) { + switch_channel_set_variable(other_channel, SWITCH_B_SDP_VARIABLE, r_sdp); + } + + if (sofia_test_flag(tech_pvt, TFLAG_3PCC) && sofia_test_pflag(profile, PFLAG_3PCC_PROXY)) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "3PCC-PROXY, Got my ACK\n"); + sofia_set_flag(tech_pvt, TFLAG_3PCC_HAS_ACK); + } else { + switch_core_session_queue_indication(other_session, SWITCH_MESSAGE_INDICATE_ANSWER); + } + + switch_core_session_rwunlock(other_session); + } + } else { uint8_t match = 0; int is_ok = 1; - sofia_clear_flag_locked(tech_pvt, TFLAG_NOSDP_REINVITE); if (tech_pvt->num_codecs) { match = sofia_glue_negotiate_sdp(session, r_sdp); @@ -5277,7 +5363,7 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, nua_respond(nh, SIP_488_NOT_ACCEPTABLE, TAG_END()); switch_channel_hangup(tech_pvt->channel, SWITCH_CAUSE_INCOMPATIBLE_DESTINATION); } - + } goto done; } @@ -5344,6 +5430,12 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, switch_core_session_rwunlock(other_session); } + + if (sofia_test_flag(tech_pvt, TFLAG_3PCC) && sofia_test_pflag(profile, PFLAG_3PCC_PROXY)) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "3PCC-PROXY, Got my ACK\n"); + sofia_set_flag(tech_pvt, TFLAG_3PCC_HAS_ACK); + } + goto done; } else { uint8_t match = 0; @@ -5474,6 +5566,11 @@ void *SWITCH_THREAD_FUNC nightmare_xfer_thread_run(switch_thread_t *thread, void if ((status = switch_ivr_originate(NULL, &tsession, &cause, nhelper->exten_with_params, timeout, NULL, NULL, NULL, switch_channel_get_caller_profile(channel_a), nhelper->vars, SOF_NONE, NULL)) == SWITCH_STATUS_SUCCESS) { if (switch_channel_up(channel_a)) { + + if (switch_true(switch_channel_get_variable(channel_a, "recording_follow_transfer"))) { + switch_core_media_bug_transfer_recordings(session, a_session); + } + tuuid_str = switch_core_session_get_uuid(tsession); switch_ivr_uuid_bridge(nhelper->bridge_to_uuid, tuuid_str); switch_channel_set_variable(channel_a, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "ATTENDED_TRANSFER"); @@ -5668,6 +5765,8 @@ void sofia_handle_sip_i_refer(nua_t *nua, sofia_profile_t *profile, nua_handle_t switch_channel_set_variable(channel_a, SOFIA_REPLACES_HEADER, rep); if ((b_private = nua_handle_magic(bnh))) { + int deny_refer_requests = 0; + if (!(b_session = switch_core_session_locate(b_private->uuid))) { goto done; } @@ -5685,7 +5784,46 @@ void sofia_handle_sip_i_refer(nua_t *nua, sofia_profile_t *profile, nua_handle_t br_b = NULL; } - if (switch_channel_test_flag(channel_b, CF_ORIGINATOR)) { + if (channel_a && switch_true(switch_channel_get_variable(channel_a, "deny_refer_requests"))) { + deny_refer_requests = 1; + } + + if (!deny_refer_requests && channel_b && switch_true(switch_channel_get_variable(channel_b, "deny_refer_requests"))) { + deny_refer_requests = 1; + } + + if (!deny_refer_requests && br_a) { + switch_core_session_t *a_session; + if ((a_session = switch_core_session_locate(br_a))) { + switch_channel_t *a_channel = switch_core_session_get_channel(a_session); + + if (a_channel && switch_true(switch_channel_get_variable(a_channel, "deny_refer_requests"))) { + deny_refer_requests = 1; + } + switch_core_session_rwunlock(a_session); + } + } + + if (!deny_refer_requests && br_b) { + switch_core_session_t *b_session; + if ((b_session = switch_core_session_locate(br_b))) { + switch_channel_t *b_channel = switch_core_session_get_channel(b_session); + + if (b_channel && switch_true(switch_channel_get_variable(b_channel, "deny_refer_requests"))) { + deny_refer_requests = 1; + } + switch_core_session_rwunlock(b_session); + } + } + + if (deny_refer_requests) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_NOTICE, "Denying Attended Transfer, variable [deny_refer_requests] was set to true\n"); + + nua_notify(tech_pvt->nh, NUTAG_NEWSUB(1), SIPTAG_CONTENT_TYPE_STR("message/sipfrag;version=2.0"), + NUTAG_SUBSTATE(nua_substate_terminated), + SIPTAG_PAYLOAD_STR("SIP/2.0 403 Forbidden\r\n"), SIPTAG_EVENT_STR(etmp), TAG_END()); + + } else if (switch_channel_test_flag(channel_b, CF_ORIGINATOR)) { switch_core_session_t *a_session; switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_NOTICE, @@ -5783,6 +5921,21 @@ void sofia_handle_sip_i_refer(nua_t *nua, sofia_profile_t *profile, nua_handle_t switch_core_session_rwunlock(tmp); } + + if (switch_true(switch_channel_get_variable(channel_a, "recording_follow_transfer")) && + (tmp = switch_core_session_locate(br_a))) { + switch_core_media_bug_transfer_recordings(session, tmp); + switch_core_session_rwunlock(tmp); + } + + + if (switch_true(switch_channel_get_variable(channel_b, "recording_follow_transfer")) && + (tmp = switch_core_session_locate(br_b))) { + switch_core_media_bug_transfer_recordings(b_session, tmp); + switch_core_session_rwunlock(tmp); + } + + switch_ivr_uuid_bridge(br_b, br_a); switch_channel_set_variable(channel_b, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "ATTENDED_TRANSFER"); nua_notify(tech_pvt->nh, NUTAG_NEWSUB(1), SIPTAG_CONTENT_TYPE_STR("message/sipfrag;version=2.0"), @@ -5803,17 +5956,19 @@ void sofia_handle_sip_i_refer(nua_t *nua, sofia_profile_t *profile, nua_handle_t NUTAG_SUBSTATE(nua_substate_terminated), SIPTAG_PAYLOAD_STR("SIP/2.0 403 Forbidden\r\n"), SIPTAG_EVENT_STR(etmp), TAG_END()); } else { - switch_core_session_t *t_session; + switch_core_session_t *t_session, *hup_session; switch_channel_t *hup_channel; const char *ext; if (br_a && !br_b) { t_session = switch_core_session_locate(br_a); hup_channel = channel_b; + hup_session = b_session; } else { private_object_t *h_tech_pvt = (private_object_t *) switch_core_session_get_private(b_session); t_session = switch_core_session_locate(br_b); hup_channel = channel_a; + hup_session = session; sofia_clear_flag_locked(tech_pvt, TFLAG_SIP_HOLD); switch_channel_clear_flag(tech_pvt->channel, CF_LEG_HOLDING); sofia_clear_flag_locked(h_tech_pvt, TFLAG_SIP_HOLD); @@ -5834,6 +5989,11 @@ void sofia_handle_sip_i_refer(nua_t *nua, sofia_profile_t *profile, nua_handle_t switch_channel_set_variable(t_channel, SOFIA_REFER_TO_VARIABLE, full_ref_to); } + + if (switch_true(switch_channel_get_variable(hup_channel, "recording_follow_transfer"))) { + switch_core_media_bug_transfer_recordings(hup_session, t_session); + } + if (idest) { switch_ivr_session_transfer(t_session, idest, "inline", NULL); } else { @@ -5996,6 +6156,10 @@ void sofia_handle_sip_i_refer(nua_t *nua, sofia_profile_t *profile, nua_handle_t switch_channel_set_variable(b_channel, SOFIA_REFER_TO_VARIABLE, full_ref_to); } + if (switch_true(switch_channel_get_variable(channel, "recording_follow_transfer"))) { + switch_core_media_bug_transfer_recordings(session, b_session); + } + switch_ivr_session_transfer(b_session, exten, NULL, NULL); switch_core_session_rwunlock(b_session); } @@ -6096,6 +6260,11 @@ void sofia_handle_sip_i_info(nua_t *nua, sofia_profile_t *profile, nua_handle_t const char *clientcode_header; switch_dtmf_t dtmf = { 0, switch_core_default_dtmf_duration(0) }; switch_event_t *event; + private_object_t *tech_pvt = NULL; + + if (session) { + tech_pvt = (private_object_t *) switch_core_session_get_private(session); + } if (sofia_test_pflag(profile, PFLAG_EXTENDED_INFO_PARSING)) { if (sip && sip->sip_content_type && sip->sip_content_type->c_type && sip->sip_content_type->c_subtype && @@ -6228,7 +6397,7 @@ void sofia_handle_sip_i_info(nua_t *nua, sofia_profile_t *profile, nua_handle_t goto end; } - if (dtmf.digit) { + if (dtmf.digit && tech_pvt->dtmf_type == DTMF_INFO) { /* queue it up */ switch_channel_queue_dtmf(channel, &dtmf); @@ -6253,6 +6422,9 @@ void sofia_handle_sip_i_info(nua_t *nua, sofia_profile_t *profile, nua_handle_t /* Send 200 OK response */ nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS(nua), TAG_END()); + } else { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, + "IGNORE INFO DTMF(%c) (This channel was not configured to use INFO DTMF!)\n", dtmf.digit); } goto end; } @@ -6806,7 +6978,7 @@ void sofia_handle_sip_i_invite(nua_t *nua, sofia_profile_t *profile, nua_handle_ if (sip->sip_to && sip->sip_to->a_url) { const char *host, *user; - int port; + int port, check_nat = 0; url_t *transport_url; if (sip->sip_record_route && sip->sip_record_route->r_url) { @@ -6846,10 +7018,22 @@ void sofia_handle_sip_i_invite(nua_t *nua, sofia_profile_t *profile, nua_handle_ if (sofia_glue_check_nat(profile, tech_pvt->remote_ip)) { url = (sofia_glue_transport_has_tls(transport)) ? profile->tls_public_url : profile->public_url; + check_nat = 1; } else { url = (sofia_glue_transport_has_tls(transport)) ? profile->tls_url : profile->url; } + if (!url) { + if (check_nat) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Nat detected but no external address configured.\n"); + } + url = profile->url; + } + + if (!url) { + switch_channel_hangup(tech_pvt->channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + } + tmp = sofia_overcome_sip_uri_weakness(session, url, transport, SWITCH_TRUE, NULL); if ((at = strchr(tmp, '@'))) { diff --git a/src/mod/endpoints/mod_sofia/sofia_glue.c b/src/mod/endpoints/mod_sofia/sofia_glue.c index 8af77a17d7..b308b6038e 100644 --- a/src/mod/endpoints/mod_sofia/sofia_glue.c +++ b/src/mod/endpoints/mod_sofia/sofia_glue.c @@ -358,7 +358,7 @@ void sofia_glue_check_dtmf_type(private_object_t *tech_pvt) } -void sofia_glue_set_local_sdp(private_object_t *tech_pvt, const char *ip, uint32_t port, const char *sr, int force) +void sofia_glue_set_local_sdp(private_object_t *tech_pvt, const char *ip, switch_port_t port, const char *sr, int force) { char buf[2048]; int ptime = 0; @@ -2396,6 +2396,7 @@ switch_status_t sofia_glue_do_invite(switch_core_session_t *session) if (sofia_use_soa(tech_pvt)) { nua_invite(tech_pvt->nh, NUTAG_AUTOANSWER(0), + NUTAG_AUTOACK(0), NUTAG_SESSION_TIMER(session_timeout), NUTAG_SESSION_REFRESHER(session_timeout ? nua_local_refresher : nua_no_refresher), TAG_IF(sofia_test_flag(tech_pvt, TFLAG_RECOVERED), NUTAG_INVITE_TIMER(UINT_MAX)), @@ -2417,15 +2418,19 @@ switch_status_t sofia_glue_do_invite(switch_core_session_t *session) TAG_IF(!zstr(route), SIPTAG_ROUTE_STR(route)), TAG_IF(tech_pvt->profile->minimum_session_expires, NUTAG_MIN_SE(tech_pvt->profile->minimum_session_expires)), TAG_IF(cseq, SIPTAG_CSEQ(cseq)), - SOATAG_ADDRESS(tech_pvt->adv_sdp_audio_ip), - SOATAG_USER_SDP_STR(tech_pvt->local_sdp_str), - SOATAG_REUSE_REJECTED(1), - SOATAG_ORDERED_USER(1), - SOATAG_RTP_SORT(SOA_RTP_SORT_REMOTE), - SOATAG_RTP_SELECT(SOA_RTP_SELECT_ALL), TAG_IF(rep, SIPTAG_REPLACES_STR(rep)), SOATAG_HOLD(holdstr), TAG_END()); + TAG_IF(zstr(tech_pvt->local_sdp_str), SIPTAG_PAYLOAD_STR("")), + TAG_IF(!zstr(tech_pvt->local_sdp_str), SOATAG_ADDRESS(tech_pvt->adv_sdp_audio_ip)), + TAG_IF(!zstr(tech_pvt->local_sdp_str), SOATAG_USER_SDP_STR(tech_pvt->local_sdp_str)), + TAG_IF(!zstr(tech_pvt->local_sdp_str), SOATAG_REUSE_REJECTED(1)), + TAG_IF(!zstr(tech_pvt->local_sdp_str), SOATAG_ORDERED_USER(1)), + TAG_IF(!zstr(tech_pvt->local_sdp_str), SOATAG_RTP_SORT(SOA_RTP_SORT_REMOTE)), + TAG_IF(!zstr(tech_pvt->local_sdp_str), SOATAG_RTP_SELECT(SOA_RTP_SELECT_ALL)), + TAG_IF(rep, SIPTAG_REPLACES_STR(rep)), + TAG_IF(!zstr(tech_pvt->local_sdp_str), SOATAG_HOLD(holdstr)), TAG_END()); } else { nua_invite(tech_pvt->nh, NUTAG_AUTOANSWER(0), + NUTAG_AUTOACK(0), NUTAG_SESSION_TIMER(session_timeout), TAG_IF(session_timeout, NUTAG_SESSION_REFRESHER(nua_remote_refresher)), TAG_IF(sofia_test_flag(tech_pvt, TFLAG_RECOVERED), NUTAG_INVITE_TIMER(UINT_MAX)), @@ -3579,7 +3584,7 @@ static void add_audio_codec(sdp_rtpmap_t *map, int ptime, char *buf, switch_size codec_ms = switch_default_ptime(map->rm_encoding, map->rm_pt); } - map_bit_rate = switch_known_bitrate(map->rm_pt); + map_bit_rate = switch_known_bitrate((switch_payload_t)map->rm_pt); if (!ptime && !strcasecmp(map->rm_encoding, "g723")) { ptime = codec_ms = 30; @@ -4649,7 +4654,7 @@ uint8_t sofia_glue_negotiate_sdp(switch_core_session_t *session, const char *r_s codec_ms = switch_default_ptime(rm_encoding, map->rm_pt); } - map_bit_rate = switch_known_bitrate(map->rm_pt); + map_bit_rate = switch_known_bitrate((switch_payload_t)map->rm_pt); if (!ptime && !strcasecmp(map->rm_encoding, "g723")) { ptime = codec_ms = 30; @@ -4765,7 +4770,7 @@ uint8_t sofia_glue_negotiate_sdp(switch_core_session_t *session, const char *r_s switch_snprintf(tmp, sizeof(tmp), "%d", tech_pvt->remote_sdp_audio_port); switch_channel_set_variable(tech_pvt->channel, SWITCH_REMOTE_MEDIA_IP_VARIABLE, tech_pvt->remote_sdp_audio_ip); switch_channel_set_variable(tech_pvt->channel, SWITCH_REMOTE_MEDIA_PORT_VARIABLE, tmp); - tech_pvt->audio_recv_pt = map->rm_pt; + tech_pvt->audio_recv_pt = (switch_payload_t)map->rm_pt; if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND && !sofia_test_flag(tech_pvt, TFLAG_REINVITE)) { sofia_glue_get_offered_pt(tech_pvt, mimp, &tech_pvt->audio_recv_pt); @@ -4889,7 +4894,7 @@ uint8_t sofia_glue_negotiate_sdp(switch_core_session_t *session, const char *r_s switch_channel_set_variable(tech_pvt->channel, "sip_video_pt", tmp); sofia_glue_check_video_codecs(tech_pvt); - tech_pvt->video_recv_pt = map->rm_pt; + tech_pvt->video_recv_pt = (switch_payload_t)map->rm_pt; if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND) { sofia_glue_get_offered_pt(tech_pvt, mimp, &tech_pvt->video_recv_pt); @@ -5458,13 +5463,13 @@ static int recover_callback(void *pArg, int argc, char **argv, char **columnName if (switch_rtp_ready(tech_pvt->rtp_session)) { if ((tmp = switch_channel_get_variable(channel, "sip_audio_recv_pt"))) { - switch_rtp_set_recv_pt(tech_pvt->rtp_session, atoi(tmp)); + switch_rtp_set_recv_pt(tech_pvt->rtp_session, (switch_payload_t)atoi(tmp)); } } if (switch_rtp_ready(tech_pvt->video_rtp_session)) { if ((tmp = switch_channel_get_variable(channel, "sip_video_recv_pt"))) { - switch_rtp_set_recv_pt(tech_pvt->rtp_session, atoi(tmp)); + switch_rtp_set_recv_pt(tech_pvt->rtp_session, (switch_payload_t)atoi(tmp)); } } @@ -6415,7 +6420,7 @@ void sofia_glue_build_vid_refresh_message(switch_core_session_t *session, const } -void sofia_glue_parse_rtp_bugs(uint32_t *flag_pole, const char *str) +void sofia_glue_parse_rtp_bugs(switch_rtp_bug_flag_t *flag_pole, const char *str) { if (switch_stristr("clear", str)) { diff --git a/src/mod/endpoints/mod_sofia/sofia_reg.c b/src/mod/endpoints/mod_sofia/sofia_reg.c index c757a9e43e..fa837eb830 100644 --- a/src/mod/endpoints/mod_sofia/sofia_reg.c +++ b/src/mod/endpoints/mod_sofia/sofia_reg.c @@ -1050,7 +1050,7 @@ uint8_t sofia_reg_handle_register(nua_t *nua, sofia_profile_t *profile, nua_hand } if (authorization) { - char *v_contact_str; + char *v_contact_str = NULL; const char *username = "unknown"; const char *realm = reg_host; if ((auth_res = sofia_reg_parse_auth(profile, authorization, sip, sip->sip_request->rq_method_name, diff --git a/src/mod/formats/mod_portaudio_stream/mod_portaudio_stream.c b/src/mod/formats/mod_portaudio_stream/mod_portaudio_stream.c index 1ad1645554..b7e49e9e24 100644 --- a/src/mod/formats/mod_portaudio_stream/mod_portaudio_stream.c +++ b/src/mod/formats/mod_portaudio_stream/mod_portaudio_stream.c @@ -285,7 +285,7 @@ static void *SWITCH_THREAD_FUNC read_stream_thread(switch_thread_t *thread, void samples = 0; switch_mutex_lock(source->device_lock); samples = ReadAudioStream(source->audio_stream, source->databuf, - source->read_codec.implementation->samples_per_packet, &source->timer); + source->read_codec.implementation->samples_per_packet, 0, &source->timer); switch_mutex_unlock(source->device_lock); diff --git a/src/mod/formats/mod_shout/mod_shout.c b/src/mod/formats/mod_shout/mod_shout.c index a4134db943..4ac6790933 100644 --- a/src/mod/formats/mod_shout/mod_shout.c +++ b/src/mod/formats/mod_shout/mod_shout.c @@ -655,9 +655,6 @@ static switch_status_t shout_file_open(switch_file_handle_t *handle, const char } } else if (switch_test_flag(handle, SWITCH_FILE_FLAG_WRITE)) { - if (switch_test_flag(handle, SWITCH_FILE_WRITE_APPEND)) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Appending to MP3 not supported.\n"); - } if (!(context->gfp = lame_init())) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not allocate lame\n"); goto error; @@ -781,8 +778,13 @@ static switch_status_t shout_file_open(switch_file_handle_t *handle, const char } } else { + const char *mask = "wb+"; + + if (switch_test_flag(handle, SWITCH_FILE_WRITE_APPEND)) { + mask = "ab+"; + } /* lame being lame and all has FILE * coded into it's API for some functions so we gotta use it */ - if (!(context->fp = fopen(path, "wb+"))) { + if (!(context->fp = fopen(path, mask))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error opening %s\n", path); goto error; } diff --git a/src/mod/languages/mod_managed/freeswitch_wrap.2010.cxx b/src/mod/languages/mod_managed/freeswitch_wrap.2010.cxx index d5141e7c04..24a6591078 100644 --- a/src/mod/languages/mod_managed/freeswitch_wrap.2010.cxx +++ b/src/mod/languages/mod_managed/freeswitch_wrap.2010.cxx @@ -6449,6 +6449,20 @@ SWIGEXPORT int SWIGSTDCALL CSharp_switch_core_media_bug_enumerate(void * jarg1, } +SWIGEXPORT int SWIGSTDCALL CSharp_switch_core_media_bug_transfer_recordings(void * jarg1, void * jarg2) { + int jresult ; + switch_core_session_t *arg1 = (switch_core_session_t *) 0 ; + switch_core_session_t *arg2 = (switch_core_session_t *) 0 ; + switch_status_t result; + + arg1 = (switch_core_session_t *)jarg1; + arg2 = (switch_core_session_t *)jarg2; + result = (switch_status_t)switch_core_media_bug_transfer_recordings(arg1,arg2); + jresult = result; + return jresult; +} + + SWIGEXPORT int SWIGSTDCALL CSharp_switch_core_media_bug_read(void * jarg1, void * jarg2, int jarg3) { int jresult ; switch_media_bug_t *arg1 = (switch_media_bug_t *) 0 ; @@ -25393,6 +25407,22 @@ SWIGEXPORT int SWIGSTDCALL CSharp_switch_event_del_header_val(void * jarg1, char } +SWIGEXPORT int SWIGSTDCALL CSharp_switch_event_add_array(void * jarg1, char * jarg2, char * jarg3) { + int jresult ; + switch_event_t *arg1 = (switch_event_t *) 0 ; + char *arg2 = (char *) 0 ; + char *arg3 = (char *) 0 ; + int result; + + arg1 = (switch_event_t *)jarg1; + arg2 = (char *)jarg2; + arg3 = (char *)jarg3; + result = (int)switch_event_add_array(arg1,(char const *)arg2,(char const *)arg3); + jresult = result; + return jresult; +} + + SWIGEXPORT void SWIGSTDCALL CSharp_switch_event_destroy(void * jarg1) { switch_event_t **arg1 = (switch_event_t **) 0 ; diff --git a/src/mod/languages/mod_managed/managed/swig.2010.cs b/src/mod/languages/mod_managed/managed/swig.2010.cs index 09b9570021..4779f13044 100644 --- a/src/mod/languages/mod_managed/managed/swig.2010.cs +++ b/src/mod/languages/mod_managed/managed/swig.2010.cs @@ -1073,6 +1073,11 @@ public class freeswitch { return ret; } + public static switch_status_t switch_core_media_bug_transfer_recordings(SWIGTYPE_p_switch_core_session orig_session, SWIGTYPE_p_switch_core_session new_session) { + switch_status_t ret = (switch_status_t)freeswitchPINVOKE.switch_core_media_bug_transfer_recordings(SWIGTYPE_p_switch_core_session.getCPtr(orig_session), SWIGTYPE_p_switch_core_session.getCPtr(new_session)); + return ret; + } + public static switch_status_t switch_core_media_bug_read(SWIGTYPE_p_switch_media_bug bug, switch_frame frame, switch_bool_t fill) { switch_status_t ret = (switch_status_t)freeswitchPINVOKE.switch_core_media_bug_read(SWIGTYPE_p_switch_media_bug.getCPtr(bug), switch_frame.getCPtr(frame), (int)fill); return ret; @@ -3956,6 +3961,11 @@ public class freeswitch { return ret; } + public static int switch_event_add_array(switch_event arg0, string var, string val) { + int ret = freeswitchPINVOKE.switch_event_add_array(switch_event.getCPtr(arg0), var, val); + return ret; + } + public static void switch_event_destroy(SWIGTYPE_p_p_switch_event arg0) { freeswitchPINVOKE.switch_event_destroy(SWIGTYPE_p_p_switch_event.getCPtr(arg0)); } @@ -7566,6 +7576,9 @@ class freeswitchPINVOKE { [DllImport("mod_managed", EntryPoint="CSharp_switch_core_media_bug_enumerate")] public static extern int switch_core_media_bug_enumerate(HandleRef jarg1, HandleRef jarg2); + [DllImport("mod_managed", EntryPoint="CSharp_switch_core_media_bug_transfer_recordings")] + public static extern int switch_core_media_bug_transfer_recordings(HandleRef jarg1, HandleRef jarg2); + [DllImport("mod_managed", EntryPoint="CSharp_switch_core_media_bug_read")] public static extern int switch_core_media_bug_read(HandleRef jarg1, HandleRef jarg2, int jarg3); @@ -12138,6 +12151,9 @@ class freeswitchPINVOKE { [DllImport("mod_managed", EntryPoint="CSharp_switch_event_del_header_val")] public static extern int switch_event_del_header_val(HandleRef jarg1, string jarg2, string jarg3); + [DllImport("mod_managed", EntryPoint="CSharp_switch_event_add_array")] + public static extern int switch_event_add_array(HandleRef jarg1, string jarg2, string jarg3); + [DllImport("mod_managed", EntryPoint="CSharp_switch_event_destroy")] public static extern void switch_event_destroy(HandleRef jarg1); @@ -25168,8 +25184,7 @@ public class switch_event : IDisposable { namespace FreeSWITCH.Native { public enum switch_event_flag_t { - EF_UNIQ_HEADERS = (1 << 0), - EF_CONTAINS_ARRAYS = (1 << 1) + EF_UNIQ_HEADERS = (1 << 0) } } diff --git a/src/switch_channel.c b/src/switch_channel.c index d31b75e478..01c2937470 100644 --- a/src/switch_channel.c +++ b/src/switch_channel.c @@ -635,7 +635,7 @@ SWITCH_DECLARE(void) switch_channel_mark_hold(switch_channel_t *channel, switch_ switch_event_t *event; if (!!on == !!switch_channel_test_flag(channel, CF_LEG_HOLDING)) { - return; + goto end; } if (on) { @@ -649,6 +649,18 @@ SWITCH_DECLARE(void) switch_channel_mark_hold(switch_channel_t *channel, switch_ switch_event_fire(&event); } + end: + + if (on) { + if (switch_true(switch_channel_get_variable(channel, "flip_record_on_hold"))) { + switch_core_session_t *other_session; + if (switch_core_session_get_partner(channel->session, &other_session) == SWITCH_STATUS_SUCCESS) { + switch_core_media_bug_transfer_recordings(channel->session, other_session); + switch_core_session_rwunlock(other_session); + } + } + } + } SWITCH_DECLARE(const char *) switch_channel_get_hold_music(switch_channel_t *channel) @@ -1032,7 +1044,9 @@ SWITCH_DECLARE(switch_status_t) switch_channel_set_variable_var_check(switch_cha switch_mutex_lock(channel->profile_mutex); if (channel->variables && !zstr(varname)) { - if (!zstr(value)) { + if (zstr(value)) { + switch_event_del_header(channel->variables, varname); + } else { int ok = 1; if (var_check) { @@ -1061,7 +1075,9 @@ SWITCH_DECLARE(switch_status_t) switch_channel_add_variable_var_check(switch_cha switch_mutex_lock(channel->profile_mutex); if (channel->variables && !zstr(varname)) { - if (!zstr(value)) { + if (zstr(value)) { + switch_event_del_header(channel->variables, varname); + } else { int ok = 1; if (var_check) { @@ -3627,7 +3643,7 @@ SWITCH_DECLARE(switch_status_t) switch_channel_set_timestamps(switch_channel_t * switch_snprintf(tmp, sizeof(tmp), "%d", billsec); switch_channel_set_variable(channel, "billsec", tmp); - switch_snprintf(tmp, sizeof(tmp), "%d", progresssec); + switch_snprintf(tmp, sizeof(tmp), "%"SWITCH_TIME_T_FMT, progresssec); switch_channel_set_variable(channel, "progresssec", tmp); switch_snprintf(tmp, sizeof(tmp), "%d", answersec); @@ -3636,7 +3652,7 @@ SWITCH_DECLARE(switch_status_t) switch_channel_set_timestamps(switch_channel_t * switch_snprintf(tmp, sizeof(tmp), "%d", waitsec); switch_channel_set_variable(channel, "waitsec", tmp); - switch_snprintf(tmp, sizeof(tmp), "%d", progress_mediasec); + switch_snprintf(tmp, sizeof(tmp), "%"SWITCH_TIME_T_FMT, progress_mediasec); switch_channel_set_variable(channel, "progress_mediasec", tmp); switch_snprintf(tmp, sizeof(tmp), "%d", legbillsec); diff --git a/src/switch_console.c b/src/switch_console.c index d902af69e7..c54ba643d2 100644 --- a/src/switch_console.c +++ b/src/switch_console.c @@ -105,7 +105,7 @@ static switch_status_t console_xml_config(void) for (param = switch_xml_child(settings, "key"); param; param = param->next) { char *var = (char *) switch_xml_attr_soft(param, "name"); char *val = (char *) switch_xml_attr_soft(param, "value"); - int i = atoi(var); + i = atoi(var); if ((i < 1) || (i > 12)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Keybind %s is invalid, range is from 1 to 12\n", var); } else { @@ -913,7 +913,7 @@ static unsigned char console_fnkey_pressed(int i) { char *c, *cmd; - assert((i > 0) && (i <= 12)); + switch_assert((i > 0) && (i <= 12)); c = console_fnkeys[i - 1]; @@ -1675,6 +1675,7 @@ SWITCH_DECLARE(void) switch_console_sort_matches(switch_console_callback_match_t sort[3] = sort[2] ? sort[2]->next : NULL; for (j = 1; j <= (matches->count - i); j++) { + switch_assert(sort[1] && sort[2]); if (strcmp(sort[1]->val, sort[2]->val) > 0) { sort[1]->next = sort[3]; sort[2]->next = sort[1]; diff --git a/src/switch_core_media_bug.c b/src/switch_core_media_bug.c index 5cc28a3c88..2c33ca2707 100644 --- a/src/switch_core_media_bug.c +++ b/src/switch_core_media_bug.c @@ -392,6 +392,47 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_bug_flush_all(switch_core_sess return SWITCH_STATUS_FALSE; } +SWITCH_DECLARE(switch_status_t) switch_core_media_bug_transfer_recordings(switch_core_session_t *orig_session, switch_core_session_t *new_session) +{ + switch_media_bug_t *bp; + char *list[100] = { 0 }; + int stop_times[100] = { 0 }; + int i = 0, x = 0; + + if (orig_session->bugs) { + switch_channel_t *new_channel = switch_core_session_get_channel(new_session); + const char *save = switch_channel_get_variable(new_channel, "record_append"); + + switch_thread_rwlock_wrlock(orig_session->bug_rwlock); + + switch_channel_set_variable(new_channel, "record_append", "true"); + + for (bp = orig_session->bugs; bp; bp = bp->next) { + if (!strcmp(bp->function, "session_record")) { + list[x] = switch_core_session_strdup(new_session, bp->target); + if (bp->stop_time > 0) { + stop_times[x] = (int)(bp->stop_time - switch_epoch_time_now(NULL)); + } + x++; + } + } + + switch_thread_rwlock_unlock(orig_session->bug_rwlock); + + for(i = 0; i < x; i++) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(orig_session), SWITCH_LOG_CRIT, "Transfering %s from %s to %s\n", list[i], + switch_core_session_get_name(orig_session), switch_core_session_get_name(new_session)); + switch_ivr_stop_record_session(orig_session, list[i]); + switch_ivr_record_session(new_session, list[i], stop_times[i], NULL); + } + + switch_channel_set_variable(new_channel, "record_append", save); + + } + + return x ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE; +} + SWITCH_DECLARE(switch_status_t) switch_core_media_bug_enumerate(switch_core_session_t *session, switch_stream_handle_t *stream) { switch_media_bug_t *bp; diff --git a/src/switch_core_session.c b/src/switch_core_session.c index ade22d2f32..ea07a7805e 100644 --- a/src/switch_core_session.c +++ b/src/switch_core_session.c @@ -616,6 +616,10 @@ static const char *message_names[] = { "APPLICATION_EXEC_COMPLETE", "PHONE_EVENT", "T38_DESCRIPTION" + "UDPTL_MODE", + "CLEAR_PROGRESS", + "JITTER_BUFFER", + "RECOVERY_REFRESH", "INVALID" }; @@ -644,8 +648,8 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_perform_receive_message(swit message->_line = line; } - if (message->message_id > SWITCH_MESSAGE_INVALID) { - message->message_id = SWITCH_MESSAGE_INVALID; + if (message->message_id > SWITCH_MESSAGE_INVALID-1) { + message->message_id = SWITCH_MESSAGE_INVALID-1; } switch_log_printf(SWITCH_CHANNEL_ID_LOG, message->_file, message->_func, message->_line, diff --git a/src/switch_event.c b/src/switch_event.c index 79881e69ae..0929be27d9 100644 --- a/src/switch_event.c +++ b/src/switch_event.c @@ -856,7 +856,6 @@ SWITCH_DECLARE(int) switch_event_add_array(switch_event_t *event, const char *va { char *data; char **array; - int idx; int max = 0; int len; const char *p; @@ -887,7 +886,7 @@ SWITCH_DECLARE(int) switch_event_add_array(switch_event_t *event, const char *va array = malloc(len); memset(array, 0, len); - idx = switch_separate_string_string(data, "|:", array, max); + switch_separate_string_string(data, "|:", array, max); for(i = 0; i < max; i++) { switch_event_add_header_string(event, SWITCH_STACK_PUSH, var, array[i]); @@ -917,7 +916,7 @@ static switch_status_t switch_event_base_add_header(switch_event_t *event, switc } header_name = real_header_name; } - + if (index_ptr || (stack & SWITCH_STACK_PUSH) || (stack & SWITCH_STACK_UNSHIFT)) { if (!(header = switch_event_get_header_ptr(event, header_name)) && index_ptr) { @@ -971,6 +970,13 @@ static switch_status_t switch_event_base_add_header(switch_event_t *event, switc if (!header) { + + if (zstr(data)) { + switch_event_del_header(event, header_name); + FREE(data); + goto end; + } + if (switch_test_flag(event, EF_UNIQ_HEADERS)) { switch_event_del_header(event, header_name); } @@ -1296,11 +1302,10 @@ SWITCH_DECLARE(switch_status_t) switch_event_serialize(switch_event_t *event, ch llen = strlen(hp->name) + strlen(encode_buf) + 8; if ((len + llen) > dlen) { - char *m; + char *m = buf; dlen += (blocksize + (len + llen)); - if ((m = realloc(buf, dlen))) { + if (!(buf = realloc(buf, dlen))) { buf = m; - } else { abort(); } } @@ -1323,11 +1328,10 @@ SWITCH_DECLARE(switch_status_t) switch_event_serialize(switch_event_t *event, ch } if ((len + llen) > dlen) { - char *m; + char *m = buf; dlen += (blocksize + (len + llen)); - if ((m = realloc(buf, dlen))) { + if (!(buf = realloc(buf, dlen))) { buf = m; - } else { abort(); } } diff --git a/src/switch_ivr.c b/src/switch_ivr.c index 5ab9f440c7..92eb97ef12 100644 --- a/src/switch_ivr.c +++ b/src/switch_ivr.c @@ -155,7 +155,13 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_sleep(switch_core_session_t *session, return SWITCH_STATUS_SUCCESS; } - if (ms > 100 && (var = switch_channel_get_variable(channel, SWITCH_SEND_SILENCE_WHEN_IDLE_VARIABLE)) && (sval = atoi(var))) { + var = switch_channel_get_variable(channel, SWITCH_SEND_SILENCE_WHEN_IDLE_VARIABLE); + if (var) { + sval = atoi(var); + SWITCH_IVR_VERIFY_SILENCE_DIVISOR(sval); + } + + if (ms > 100 && sval) { switch_core_session_get_read_impl(session, &imp); if (switch_core_codec_init(&codec, @@ -744,34 +750,6 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_park(switch_core_session_t *session, unsigned char *abuf = NULL; switch_codec_implementation_t imp = { 0 }; - if ((var = switch_channel_get_variable(channel, SWITCH_SEND_SILENCE_WHEN_IDLE_VARIABLE)) && (sval = atoi(var))) { - switch_core_session_get_read_impl(session, &imp); - - if (switch_core_codec_init(&codec, - "L16", - NULL, - imp.samples_per_second, - imp.microseconds_per_packet / 1000, - imp.number_of_channels, - SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, - switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Codec Error L16@%uhz %u channels %dms\n", - imp.samples_per_second, imp.number_of_channels, imp.microseconds_per_packet / 1000); - return SWITCH_STATUS_FALSE; - } - - - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Codec Activated L16@%uhz %u channels %dms\n", - imp.samples_per_second, imp.number_of_channels, imp.microseconds_per_packet / 1000); - - write_frame.codec = &codec; - switch_zmalloc(abuf, SWITCH_RECOMMENDED_BUFFER_SIZE); - write_frame.data = abuf; - write_frame.buflen = SWITCH_RECOMMENDED_BUFFER_SIZE; - write_frame.datalen = imp.decoded_bytes_per_packet; - write_frame.samples = write_frame.datalen / sizeof(int16_t); - } - if (switch_channel_test_flag(channel, CF_RECOVERED) && switch_channel_test_flag(channel, CF_CONTROLLED)) { switch_channel_clear_flag(channel, CF_CONTROLLED); } @@ -815,6 +793,34 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_park(switch_core_session_t *session, switch_core_session_get_read_impl(session, &read_impl); rate = read_impl.actual_samples_per_second; bpf = read_impl.decoded_bytes_per_packet; + + if ((var = switch_channel_get_variable(channel, SWITCH_SEND_SILENCE_WHEN_IDLE_VARIABLE)) && (sval = atoi(var))) { + switch_core_session_get_read_impl(session, &imp); + + if (switch_core_codec_init(&codec, + "L16", + NULL, + imp.samples_per_second, + imp.microseconds_per_packet / 1000, + imp.number_of_channels, + SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, + switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Codec Error L16@%uhz %u channels %dms\n", + imp.samples_per_second, imp.number_of_channels, imp.microseconds_per_packet / 1000); + return SWITCH_STATUS_FALSE; + } + + + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Codec Activated L16@%uhz %u channels %dms\n", + imp.samples_per_second, imp.number_of_channels, imp.microseconds_per_packet / 1000); + + write_frame.codec = &codec; + switch_zmalloc(abuf, SWITCH_RECOMMENDED_BUFFER_SIZE); + write_frame.data = abuf; + write_frame.buflen = SWITCH_RECOMMENDED_BUFFER_SIZE; + write_frame.datalen = imp.decoded_bytes_per_packet; + write_frame.samples = write_frame.datalen / sizeof(int16_t); + } } if (rate) { @@ -839,7 +845,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_park(switch_core_session_t *session, break; } - if (write_frame.data) { + if (rate && write_frame.data && sval) { switch_generate_sln_silence((int16_t *) write_frame.data, write_frame.samples, sval); switch_core_session_write_frame(session, &write_frame, SWITCH_IO_FLAG_NONE, 0); } diff --git a/src/switch_ivr_async.c b/src/switch_ivr_async.c index ed69799d26..f9cf24ef83 100644 --- a/src/switch_ivr_async.c +++ b/src/switch_ivr_async.c @@ -936,12 +936,6 @@ static switch_bool_t record_callback(switch_media_bug_t *bug, void *user_data, s switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Stop recording file %s\n", rh->file); switch_channel_set_private(channel, rh->file, NULL); - if (switch_event_create(&event, SWITCH_EVENT_RECORD_STOP) == SWITCH_STATUS_SUCCESS) { - switch_channel_event_set_data(channel, event); - switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Record-File-Path", rh->file); - switch_event_fire(&event); - } - if (rh->fh) { switch_size_t len; uint8_t data[SWITCH_RECOMMENDED_BUFFER_SIZE]; @@ -970,6 +964,12 @@ static switch_bool_t record_callback(switch_media_bug_t *bug, void *user_data, s } } + if (switch_event_create(&event, SWITCH_EVENT_RECORD_STOP) == SWITCH_STATUS_SUCCESS) { + switch_channel_event_set_data(channel, event); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Record-File-Path", rh->file); + switch_event_fire(&event); + } + if ((var = switch_channel_get_variable(channel, "record_post_process_exec_app"))) { char *app = switch_core_session_strdup(session, var); char *data; @@ -1444,7 +1444,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_record_session(switch_core_session_t int file_flags = SWITCH_FILE_FLAG_WRITE | SWITCH_FILE_DATA_SHORT; switch_bool_t hangup_on_error = SWITCH_FALSE; char *file_path = NULL; - + if ((p = switch_channel_get_variable(channel, "RECORD_HANGUP_ON_ERROR"))) { hangup_on_error = switch_true(p); } @@ -1463,7 +1463,36 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_record_session(switch_core_session_t channels = read_impl.number_of_channels; if ((bug = switch_channel_get_private(channel, file))) { - return switch_ivr_stop_record_session(session, file); + if (switch_true(switch_channel_get_variable(channel, "RECORD_TOGGLE_ON_REPEAT"))) { + return switch_ivr_stop_record_session(session, file); + } + + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Already recording [%s]\n", file); + return SWITCH_STATUS_SUCCESS; + } + + + if ((p = switch_channel_get_variable(channel, "RECORD_CHECK_BRIDGE")) && switch_true(p)) { + switch_core_session_t *other_session; + int exist = 0; + switch_status_t rstatus = SWITCH_STATUS_SUCCESS; + + if (switch_core_session_get_partner(session, &other_session) == SWITCH_STATUS_SUCCESS) { + switch_channel_t *other_channel = switch_core_session_get_channel(other_session); + if ((bug = switch_channel_get_private(other_channel, file))) { + if (switch_true(switch_channel_get_variable(other_channel, "RECORD_TOGGLE_ON_REPEAT"))) { + rstatus = switch_ivr_stop_record_session(other_session, file); + } else { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(other_session), SWITCH_LOG_WARNING, "Already recording [%s]\n", file); + } + exist = 1; + } + switch_core_session_rwunlock(other_session); + } + + if (exist) { + return rstatus; + } } if (!fh) { @@ -1615,7 +1644,9 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_record_session(switch_core_session_t rh->file = switch_core_session_strdup(session, file); rh->packet_len = read_impl.decoded_bytes_per_packet; - rh->min_sec = 3; + if (file_flags & SWITCH_FILE_WRITE_APPEND) { + rh->min_sec = 3; + } if ((p = switch_channel_get_variable(channel, "RECORD_MIN_SEC"))) { int tmp = atoi(p); @@ -1638,6 +1669,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_record_session(switch_core_session_t return SWITCH_STATUS_SUCCESS; } + typedef struct { SpeexPreprocessState *read_st; SpeexPreprocessState *write_st; diff --git a/src/switch_ivr_originate.c b/src/switch_ivr_originate.c index ff8aa88d66..840705aad5 100644 --- a/src/switch_ivr_originate.c +++ b/src/switch_ivr_originate.c @@ -910,9 +910,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_wait_for_answer(switch_core_session_t ringback.silence = atoi(p); } } - if (ringback.silence <= 0) { - ringback.silence = 400; - } + SWITCH_IVR_VERIFY_SILENCE_DIVISOR(ringback.silence); } else { switch_buffer_create_dynamic(&ringback.audio_buffer, 512, 1024, 0); switch_buffer_set_loops(ringback.audio_buffer, -1); @@ -1203,9 +1201,7 @@ static switch_status_t setup_ringback(originate_global_t *oglobals, originate_st ringback->silence = atoi(c); } } - if (ringback->silence <= 0) { - ringback->silence = 400; - } + SWITCH_IVR_VERIFY_SILENCE_DIVISOR(ringback->silence); } else { switch_buffer_create_dynamic(&ringback->audio_buffer, 512, 1024, 0); switch_buffer_set_loops(ringback->audio_buffer, -1); @@ -2938,7 +2934,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess oglobals.idx = IDX_CANCEL; } - if (oglobals.session && (ringback_data || !(switch_channel_test_flag(caller_channel, CF_PROXY_MODE) && + if (oglobals.session && (ringback_data || !(switch_channel_test_flag(caller_channel, CF_PROXY_MODE) || switch_channel_test_flag(caller_channel, CF_PROXY_MEDIA)))) { switch_core_session_reset(oglobals.session, SWITCH_FALSE, SWITCH_TRUE); } @@ -3127,10 +3123,22 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess switch_core_session_t *holding_session; if ((holding_session = switch_core_session_locate(holding))) { - switch_channel_set_variable(switch_core_session_get_channel(holding_session), SWITCH_HANGUP_AFTER_BRIDGE_VARIABLE, "true"); + switch_channel_t *holding_channel = switch_core_session_get_channel(holding_session); + + switch_channel_set_variable(holding_channel, SWITCH_HANGUP_AFTER_BRIDGE_VARIABLE, "true"); + + if (caller_channel && switch_true(switch_channel_get_variable(caller_channel, "recording_follow_transfer"))) { + switch_core_media_bug_transfer_recordings(session, originate_status[i].peer_session); + } + + if (switch_true(switch_channel_get_variable(holding_channel, "recording_follow_transfer"))) { + switch_core_media_bug_transfer_recordings(holding_session, originate_status[i].peer_session); + } + switch_core_session_rwunlock(holding_session); } switch_channel_set_flag(originate_status[i].peer_channel, CF_LAZY_ATTENDED_TRANSFER); + switch_ivr_uuid_bridge(holding, switch_core_session_get_uuid(originate_status[i].peer_session)); holding = NULL; } else { diff --git a/src/switch_regex.c b/src/switch_regex.c index 3cfa01e116..e6aa375077 100644 --- a/src/switch_regex.c +++ b/src/switch_regex.c @@ -249,7 +249,7 @@ SWITCH_DECLARE(switch_status_t) switch_regex_match(const char *target, const cha return switch_regex_match_partial(target, expression, &partial); } -SWITCH_DECLARE(void) switch_regex_set_var_callback(const char *var, const char *val, void *user_data) +SWITCH_DECLARE_NONSTD(void) switch_regex_set_var_callback(const char *var, const char *val, void *user_data) { switch_core_session_t *session = (switch_core_session_t *) user_data; switch_channel_t *channel = switch_core_session_get_channel(session); diff --git a/src/switch_resample.c b/src/switch_resample.c index 9c7f9b95d2..7355384915 100644 --- a/src/switch_resample.c +++ b/src/switch_resample.c @@ -190,6 +190,11 @@ SWITCH_DECLARE(void) switch_generate_sln_silence(int16_t *data, uint32_t samples assert(divisor); + if (divisor == (uint32_t)-1) { + memset(data, 0, sizeof(*data)); + return; + } + for (i = 0; i < samples; i++, sum_rnd = 0) { for (x = 0; x < 6; x++) { rnd2 = rnd2 * 31821U + 13849U; diff --git a/src/switch_utils.c b/src/switch_utils.c index 99d1d98275..906a02eba6 100644 --- a/src/switch_utils.c +++ b/src/switch_utils.c @@ -2669,7 +2669,7 @@ SWITCH_DECLARE(switch_bool_t) switch_dow_cmp(const char *exp, int val) /* Save the previous token and move to the next one */ range_start = prev; } else if (cur == DOW_ERR) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Parse error for [%s] at position %td (%.6s)\n", exp, p - exp, p); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Parse error for [%s] at position %ld (%.6s)\n", exp, (long) (p - exp), p); break; } else { /* Valid day found */ diff --git a/src/switch_xml.c b/src/switch_xml.c index 4664c07c71..fa53406822 100644 --- a/src/switch_xml.c +++ b/src/switch_xml.c @@ -1125,12 +1125,12 @@ SWITCH_DECLARE(switch_xml_t) switch_xml_parse_fp(FILE * fp) do { len += (l = fread((s + len), 1, SWITCH_XML_BUFSIZE, fp)); if (l == SWITCH_XML_BUFSIZE) { - char *tmp = (char *) realloc(s, len + SWITCH_XML_BUFSIZE); - if (!tmp) { - free(s); + char *tmp = s; + s = (char *) realloc(s, len + SWITCH_XML_BUFSIZE); + if (!s) { + free(tmp); return NULL; } - s = tmp; } } while (s && l == SWITCH_XML_BUFSIZE); @@ -2279,10 +2279,10 @@ static char *switch_xml_toxml_r(switch_xml_t xml, char **s, switch_size_t *len, *s = switch_xml_ampencode(txt + start, xml->off - start, s, len, max, 0); while (*len + strlen(xml->name) + 5 + (strlen(XML_INDENT) * (*count)) + 1 > *max) { /* reallocate s */ - char *tmp = (char *) realloc(*s, *max += SWITCH_XML_BUFSIZE); - if (!tmp) - return *s; - *s = tmp; + char *tmp = *s; + *s = (char *) realloc(*s, *max += SWITCH_XML_BUFSIZE); + if (!*s) + return tmp; } if (*len && *(*s + (*len) - 1) == '>') { @@ -2335,10 +2335,10 @@ static char *switch_xml_toxml_r(switch_xml_t xml, char **s, switch_size_t *len, } while (*len + strlen(xml->name) + 5 + (strlen(XML_INDENT) * (*count)) > *max) { /* reallocate s */ - char *tmp = (char *) realloc(*s, *max += SWITCH_XML_BUFSIZE); - if (!tmp) - return *s; - *s = tmp; + char *tmp = *s; + *s = (char *) realloc(*s, *max += SWITCH_XML_BUFSIZE); + if (!*s) + return tmp; } if (xml->child || xml->txt) {