From f6e9bc335b6c947b3c86042d5731d0d8ba7e9a75 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Wed, 31 Oct 2007 16:44:02 +0000 Subject: [PATCH] add inband dtmf generator git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@6097 d0543943-73ff-0310-b7d9-9358b9ac24b2 --- src/include/switch_ivr.h | 15 ++ .../applications/mod_dptools/mod_dptools.c | 22 +++ src/switch_ivr_async.c | 159 +++++++++++++++++- 3 files changed, 192 insertions(+), 4 deletions(-) diff --git a/src/include/switch_ivr.h b/src/include/switch_ivr.h index 50add0cb73..22613ba13e 100644 --- a/src/include/switch_ivr.h +++ b/src/include/switch_ivr.h @@ -227,6 +227,21 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_inband_dtmf_session(switch_core_sessi */ SWITCH_DECLARE(switch_status_t) switch_ivr_stop_inband_dtmf_session(switch_core_session_t *session); + +/*! + \brief Start generating DTMF inband + \param session the session to generate on + \return SWITCH_STATUS_SUCCESS if all is well +*/ +SWITCH_DECLARE(switch_status_t) switch_ivr_inband_dtmf_generate_session(switch_core_session_t *session, switch_bool_t read_stream); + +/*! + \brief Stop generating DTMF inband + \param session the session to stop generating + \return SWITCH_STATUS_SUCCESS if all is well +*/ +SWITCH_DECLARE(switch_status_t) switch_ivr_stop_inband_dtmf_generate_session(switch_core_session_t *session); + /*! \brief XXX DESCRIBE ME \param session the session to act on diff --git a/src/mod/applications/mod_dptools/mod_dptools.c b/src/mod/applications/mod_dptools/mod_dptools.c index efea476fcb..1a73d697b7 100644 --- a/src/mod/applications/mod_dptools/mod_dptools.c +++ b/src/mod/applications/mod_dptools/mod_dptools.c @@ -819,6 +819,25 @@ SWITCH_STANDARD_APP(stop_dtmf_session_function) switch_ivr_stop_inband_dtmf_session(session); } +SWITCH_STANDARD_APP(dtmf_session_generate_function) +{ + switch_bool_t do_read = SWITCH_TRUE; + + if (!switch_strlen_zero(data)) { + if (!strcasecmp(data, "write")) { + do_read = SWITCH_FALSE; + } + } + + switch_ivr_inband_dtmf_generate_session(session, do_read); +} + + +SWITCH_STANDARD_APP(stop_dtmf_session_generate_function) +{ + switch_ivr_stop_inband_dtmf_generate_session(session); +} + SWITCH_STANDARD_APP(fax_detect_session_function) { switch_ivr_tone_detect_session(session, "fax", "1100.0", "r", 0, NULL, NULL); @@ -1346,6 +1365,9 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_dptools_load) SWITCH_ADD_APP(app_interface, "execute_extension", "Execute an extension", "Execute an extension", exe_function, EXE_SYNTAX, SAF_SUPPORT_NOMEDIA); SWITCH_ADD_APP(app_interface, "stop_dtmf", "stop inband dtmf", "Stop detecting inband dtmf.", stop_dtmf_session_function, "", SAF_NONE); SWITCH_ADD_APP(app_interface, "start_dtmf", "Detect dtmf", "Detect inband dtmf on the session", dtmf_session_function, "", SAF_NONE); + SWITCH_ADD_APP(app_interface, "stop_dtmf_generate", "stop inband dtmf generation", "Stop generating inband dtmf.", + stop_dtmf_session_generate_function, "[write]", SAF_NONE); + SWITCH_ADD_APP(app_interface, "start_dtmf_generate", "Generate dtmf", "Generate inband dtmf on the session", dtmf_session_generate_function, "", SAF_NONE); SWITCH_ADD_APP(app_interface, "stop_tone_detect", "stop detecting tones", "Stop detecting tones", stop_fax_detect_session_function, "", SAF_NONE); SWITCH_ADD_APP(app_interface, "fax_detect", "Detect faxes", "Detect fax send tone", fax_detect_session_function, "", SAF_NONE); SWITCH_ADD_APP(app_interface, "tone_detect", "Detect tones", "Detect tones", tone_detect_session_function, "", SAF_NONE); diff --git a/src/switch_ivr_async.c b/src/switch_ivr_async.c index 468a752379..32dddf4998 100644 --- a/src/switch_ivr_async.c +++ b/src/switch_ivr_async.c @@ -70,7 +70,7 @@ SWITCH_DECLARE(void) switch_ivr_session_echo(switch_core_session_t *session) switch_threadattr_t *thd_attr = NULL; - switch_channel_answer(channel); + switch_channel_pre_answer(channel); if (switch_channel_test_flag(channel, CF_VIDEO)) { eh.session = session; @@ -221,7 +221,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_displace_session(switch_core_session_ return SWITCH_STATUS_GENERR; } - switch_channel_answer(channel); + switch_channel_pre_answer(channel); if (limit) { to = time(NULL) + limit; @@ -343,7 +343,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_record_session(switch_core_session_t return SWITCH_STATUS_GENERR; } - switch_channel_answer(channel); + switch_channel_pre_answer(channel); if ((p = switch_channel_get_variable(channel, "RECORD_TITLE"))) { vval = (const char *) switch_core_session_strdup(session, p); @@ -474,7 +474,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_inband_dtmf_session(switch_core_sessi pvt->session = session; - switch_channel_answer(channel); + switch_channel_pre_answer(channel); if ((status = switch_core_media_bug_add(session, inband_dtmf_callback, pvt, 0, SMBF_READ_STREAM, &bug)) != SWITCH_STATUS_SUCCESS) { return status; @@ -486,6 +486,157 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_inband_dtmf_session(switch_core_sessi } +typedef struct { + switch_core_session_t *session; + teletone_generation_session_t ts; + switch_buffer_t *audio_buffer; + switch_mutex_t *mutex; + int read; +} switch_inband_dtmf_generate_t; + +static int teletone_dtmf_generate_handler(teletone_generation_session_t * ts, teletone_tone_map_t * map) +{ + switch_buffer_t *audio_buffer = ts->user_data; + int wrote; + + if (!audio_buffer) { + return -1; + } + + wrote = teletone_mux_tones(ts, map); + switch_buffer_write(audio_buffer, ts->buffer, wrote * 2); + + return 0; +} + + +static switch_status_t generate_on_dtmf(switch_core_session_t *session, const char *dtmf) +{ + + switch_media_bug_t *bug; + switch_channel_t *channel = switch_core_session_get_channel(session); + + if ((bug = (switch_media_bug_t *) switch_channel_get_private(channel, "dtmf_generate"))) { + switch_inband_dtmf_generate_t *pvt = (switch_inband_dtmf_generate_t *) switch_core_media_bug_get_user_data(bug); + + if (pvt) { + switch_mutex_lock(pvt->mutex); + teletone_run(&pvt->ts, (char *)dtmf); + switch_mutex_unlock(pvt->mutex); + return SWITCH_STATUS_FALSE; + } + } + return SWITCH_STATUS_SUCCESS; +} + +static switch_bool_t inband_dtmf_generate_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type) +{ + switch_inband_dtmf_generate_t *pvt = (switch_inband_dtmf_generate_t *) user_data; + switch_frame_t *frame; + switch_channel_t *channel = switch_core_session_get_channel(pvt->session); + switch_codec_t *read_codec; + + assert(channel != NULL); + read_codec = switch_core_session_get_read_codec(pvt->session); + + switch (type) { + case SWITCH_ABC_TYPE_INIT: + { + switch_buffer_create_dynamic(&pvt->audio_buffer, 512, 1024, 0); + teletone_init_session(&pvt->ts, 0, teletone_dtmf_generate_handler, pvt->audio_buffer); + pvt->ts.rate = read_codec->implementation->actual_samples_per_second; + pvt->ts.channels = 1; + switch_mutex_init(&pvt->mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(pvt->session)); + switch_core_event_hook_add_recv_dtmf(pvt->session, generate_on_dtmf); + } + break; + case SWITCH_ABC_TYPE_CLOSE: + { + switch_buffer_destroy(&pvt->audio_buffer); + teletone_destroy_session(&pvt->ts); + switch_core_event_hook_remove_recv_dtmf(pvt->session, generate_on_dtmf); + } + break; + case SWITCH_ABC_TYPE_READ_REPLACE: + case SWITCH_ABC_TYPE_WRITE_REPLACE: + { + switch_size_t bytes; + switch_mutex_lock(pvt->mutex); + if (pvt->read) { + frame = switch_core_media_bug_get_read_replace_frame(bug); + } else { + frame = switch_core_media_bug_get_write_replace_frame(bug); + } + if (switch_buffer_inuse(pvt->audio_buffer) && (bytes = switch_buffer_read(pvt->audio_buffer, frame->data, frame->datalen))) { + if (bytes < frame->datalen) { + switch_byte_t *dp = frame->data; + memset(dp + bytes, 0, frame->datalen - bytes); + } + } + if (pvt->read) { + switch_core_media_bug_set_read_replace_frame(bug, frame); + } else { + switch_core_media_bug_set_write_replace_frame(bug, frame); + } + switch_mutex_unlock(pvt->mutex); + } + break; + default: + break; + } + + return SWITCH_TRUE; +} + +SWITCH_DECLARE(switch_status_t) switch_ivr_stop_inband_dtmf_generate_session(switch_core_session_t *session) +{ + switch_media_bug_t *bug; + switch_channel_t *channel = switch_core_session_get_channel(session); + + assert(channel != NULL); + if ((bug = switch_channel_get_private(channel, "dtmf_generate"))) { + switch_channel_set_private(channel, "dtmf_generate", NULL); + switch_core_media_bug_remove(session, &bug); + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_FALSE; + +} + +SWITCH_DECLARE(switch_status_t) switch_ivr_inband_dtmf_generate_session(switch_core_session_t *session, switch_bool_t read_stream) +{ + switch_channel_t *channel; + switch_codec_t *read_codec; + switch_media_bug_t *bug; + switch_status_t status; + switch_inband_dtmf_generate_t *pvt; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + read_codec = switch_core_session_get_read_codec(session); + assert(read_codec != NULL); + + if (!(pvt = switch_core_session_alloc(session, sizeof(*pvt)))) { + return SWITCH_STATUS_MEMERR; + } + + pvt->session = session; + pvt->read = !!read_stream; + switch_channel_pre_answer(channel); + + if ((status = switch_core_media_bug_add(session, inband_dtmf_generate_callback, pvt, 0, + pvt->read ? SMBF_READ_REPLACE : SMBF_WRITE_REPLACE, &bug)) != SWITCH_STATUS_SUCCESS) { + return status; + } + + switch_channel_set_private(channel, "dtmf_generate", bug); + + return SWITCH_STATUS_SUCCESS; +} + + #define MAX_TONES 16 typedef struct { teletone_multi_tone_t mt;