예제 #1
0
파일: switch_cpp.cpp 프로젝트: gujun/sscore
SWITCH_DECLARE(void) bridge(CoreSession &session_a, CoreSession &session_b)
{
	switch_input_callback_function_t dtmf_func = NULL;
	switch_input_args_t args;
	switch_channel_t *channel_a = NULL, *channel_b = NULL;
	const char *err = "Channels not ready\n";
	
	if (session_a.allocated && session_a.session && session_b.allocated && session_b.session) {
		channel_a = switch_core_session_get_channel(session_a.session);
		channel_b = switch_core_session_get_channel(session_b.session);

		if (switch_channel_ready(channel_a) && switch_channel_ready(channel_b)) {
			session_a.begin_allow_threads();
			if (!switch_channel_test_flag(channel_a, CF_OUTBOUND) && !switch_channel_media_ready(channel_a)) {
				switch_channel_pre_answer(channel_a);
			}

			if (switch_channel_ready(channel_a) && switch_channel_ready(channel_b)) {
				args = session_a.get_cb_args();  // get the cb_args data structure for session a
				dtmf_func = args.input_callback;   // get the call back function
				err = NULL;
				switch_ivr_multi_threaded_bridge(session_a.session, session_b.session, dtmf_func, args.buf, args.buf);
			}
			session_a.end_allow_threads();
		}
	}

	if (err) {
		switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session_a.session), SWITCH_LOG_ERROR, "%s", err);
	}


}
예제 #2
0
switch_status_t fsk_detect_session(switch_core_session_t *session, const char *flags)
{
	switch_channel_t *channel = switch_core_session_get_channel(session);
	switch_media_bug_t *bug;
	switch_status_t status;
	switch_fsk_detect_t *pvt = { 0 };
	switch_codec_implementation_t read_impl = { 0 };
	int bflags = SMBF_READ_REPLACE;

	if (strchr(flags, 'w')) {
		bflags = SMBF_WRITE_REPLACE;
	}

	switch_core_session_get_read_impl(session, &read_impl);

	if (!(pvt = switch_core_session_alloc(session, sizeof(*pvt)))) {
		return SWITCH_STATUS_MEMERR;
	}

   	pvt->session = session;


	if (switch_channel_pre_answer(channel) != SWITCH_STATUS_SUCCESS) {
		return SWITCH_STATUS_FALSE;
	}

	if ((status = switch_core_media_bug_add(session, "fsk_detect", NULL,
                                            fsk_detect_callback, pvt, 0, bflags | SMBF_NO_PAUSE, &bug)) != SWITCH_STATUS_SUCCESS) {
		return status;
	}

	switch_channel_set_private(channel, "fsk", bug);

	return SWITCH_STATUS_SUCCESS;
}
예제 #3
0
파일: switch_cpp.cpp 프로젝트: gujun/sscore
SWITCH_DECLARE(int) CoreSession::preAnswer()
{
    switch_status_t status;
	this_check(-1);
	sanity_check(-1);
    status = switch_channel_pre_answer(channel);
    return status == SWITCH_STATUS_SUCCESS ? 1 : 0;
}
예제 #4
0
static switch_status_t start_capture(switch_core_session_t *session, unsigned int seconds, switch_media_bug_flag_t flags, const char *base)
{
	switch_channel_t *channel = switch_core_session_get_channel(session);
	switch_media_bug_t *bug;
	switch_status_t status;
	switch_codec_implementation_t read_impl = { 0 };
	struct cap_cb *cb;
	switch_size_t bytes;
	switch_bind_flag_t bind_flags = 0;

	if (switch_channel_get_private(channel, "snapshot")) {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Already Running.\n");
		return SWITCH_STATUS_FALSE;
	}

	if (seconds < 5) {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Must be at least 5 seconds!\n");
		return SWITCH_STATUS_FALSE;
	}

	switch_core_session_get_read_impl(session, &read_impl);

	if (switch_channel_pre_answer(channel) != SWITCH_STATUS_SUCCESS) {
		return SWITCH_STATUS_FALSE;
	}

	cb = switch_core_session_alloc(session, sizeof(*cb));
	cb->base = switch_core_session_strdup(session, base);

	bytes = read_impl.samples_per_second * seconds * 2;

	switch_buffer_create_dynamic(&cb->buffer, bytes, bytes, bytes);
	switch_mutex_init(&cb->mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session));

	if ((status = switch_core_media_bug_add(session, "snapshot", NULL, capture_callback, cb, 0, flags, &bug)) != SWITCH_STATUS_SUCCESS) {
		return status;
	}

	bind_flags = SBF_DIAL_ALEG | SBF_EXEC_ALEG | SBF_EXEC_SAME;
	switch_ivr_bind_dtmf_meta_session(session, 7, bind_flags, "snapshot::snap");

	switch_channel_set_private(channel, "snapshot", bug);

	return SWITCH_STATUS_SUCCESS;
}
SWITCH_DECLARE(switch_status_t) switch_core_media_bug_add(switch_core_session_t *session,
														  const char *function,
														  const char *target,
														  switch_media_bug_callback_t callback,
														  void *user_data, time_t stop_time, 
														  switch_media_bug_flag_t flags, 
														  switch_media_bug_t **new_bug)
{
	switch_media_bug_t *bug, *bp;
	switch_size_t bytes;
	switch_event_t *event;
	int tap_only = 1, punt = 0;

	const char *p;

	if (!zstr(function)) {
		if ((flags & SMBF_ONE_ONLY)) {
			switch_thread_rwlock_wrlock(session->bug_rwlock);
			for (bp = session->bugs; bp; bp = bp->next) {
				if (!zstr(bp->function) && !strcasecmp(function, bp->function)) {
					punt = 1;
					break;
				} 
			}
			switch_thread_rwlock_unlock(session->bug_rwlock);
		}
	}
	
	if (punt) {
		switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Only one bug of this type allowed!\n");
	}


	if (!switch_channel_media_ready(session->channel)) {
		if (switch_channel_pre_answer(session->channel) != SWITCH_STATUS_SUCCESS) {
			return SWITCH_STATUS_FALSE;
		}
	}



	*new_bug = NULL;


	if ((p = switch_channel_get_variable(session->channel, "media_bug_answer_req")) && switch_true(p)) {
		flags |= SMBF_ANSWER_REQ;
	}
#if 0
	if (flags & SMBF_WRITE_REPLACE) {
		switch_thread_rwlock_wrlock(session->bug_rwlock);
		for (bp = session->bugs; bp; bp = bp->next) {
			if (switch_test_flag(bp, SMBF_WRITE_REPLACE)) {
				switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Only one bug of this type allowed!\n");
				switch_thread_rwlock_unlock(session->bug_rwlock);
				return SWITCH_STATUS_GENERR;
			}
		}
		switch_thread_rwlock_unlock(session->bug_rwlock);
	}

	if (flags & SMBF_READ_REPLACE) {
		switch_thread_rwlock_wrlock(session->bug_rwlock);
		for (bp = session->bugs; bp; bp = bp->next) {
			if (switch_test_flag(bp, SMBF_READ_REPLACE)) {
				switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Only one bug of this type allowed!\n");
				switch_thread_rwlock_unlock(session->bug_rwlock);
				return SWITCH_STATUS_GENERR;
			}
		}
		switch_thread_rwlock_unlock(session->bug_rwlock);
	}
#endif

	if (!(bug = switch_core_session_alloc(session, sizeof(*bug)))) {
		return SWITCH_STATUS_MEMERR;
	}

	bug->callback = callback;
	bug->user_data = user_data;
	bug->session = session;
	bug->flags = flags;
	bug->function = "N/A";
	bug->target = "N/A";

	switch_core_session_get_read_impl(session, &bug->read_impl);
	switch_core_session_get_write_impl(session, &bug->write_impl);

	if (function) {
		bug->function = switch_core_session_strdup(session, function);
	}

	if (target) {
		bug->target = switch_core_session_strdup(session, target);
	}
	
	bug->stop_time = stop_time;
	bytes = bug->read_impl.decoded_bytes_per_packet;

	if (!bug->flags) {
		bug->flags = (SMBF_READ_STREAM | SMBF_WRITE_STREAM);
	}

	if (switch_test_flag(bug, SMBF_READ_STREAM) || switch_test_flag(bug, SMBF_READ_PING)) {
		switch_buffer_create_dynamic(&bug->raw_read_buffer, bytes * SWITCH_BUFFER_BLOCK_FRAMES, bytes * SWITCH_BUFFER_START_FRAMES, MAX_BUG_BUFFER);
		switch_mutex_init(&bug->read_mutex, SWITCH_MUTEX_NESTED, session->pool);
	}

	bytes = bug->write_impl.decoded_bytes_per_packet;

	if (switch_test_flag(bug, SMBF_WRITE_STREAM)) {
		switch_buffer_create_dynamic(&bug->raw_write_buffer, bytes * SWITCH_BUFFER_BLOCK_FRAMES, bytes * SWITCH_BUFFER_START_FRAMES, MAX_BUG_BUFFER);
		switch_mutex_init(&bug->write_mutex, SWITCH_MUTEX_NESTED, session->pool);
	}

	if ((bug->flags & SMBF_THREAD_LOCK)) {
		bug->thread_id = switch_thread_self();
	}

	if (bug->callback) {
		switch_bool_t result = bug->callback(bug, bug->user_data, SWITCH_ABC_TYPE_INIT);
		if (result == SWITCH_FALSE) {
			switch_core_media_bug_destroy(bug);
			switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Error attaching BUG to %s\n",
							  switch_channel_get_name(session->channel));
			return SWITCH_STATUS_GENERR;
		}
	}

	switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Attaching BUG to %s\n", switch_channel_get_name(session->channel));
	bug->ready = 1;
	switch_thread_rwlock_wrlock(session->bug_rwlock);
	bug->next = session->bugs;
	session->bugs = bug;

	for(bp = session->bugs; bp; bp = bp->next) {
		if (bp->ready && !switch_test_flag(bp, SMBF_TAP_NATIVE_READ) && !switch_test_flag(bp, SMBF_TAP_NATIVE_WRITE)) {
			tap_only = 0;
		}	
	}

	switch_thread_rwlock_unlock(session->bug_rwlock);
	*new_bug = bug;


	if (tap_only) {
		switch_set_flag(session, SSF_MEDIA_BUG_TAP_ONLY);
	} else {
		switch_clear_flag(session, SSF_MEDIA_BUG_TAP_ONLY);
	}

	if (switch_event_create(&event, SWITCH_EVENT_MEDIA_BUG_START) == SWITCH_STATUS_SUCCESS) {
		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Media-Bug-Function", "%s", bug->function);
		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Media-Bug-Target", "%s", bug->target);
		switch_channel_event_set_data(session->channel, event);
		switch_event_fire(&event);
	}

	return SWITCH_STATUS_SUCCESS;
}
예제 #6
0
SWITCH_DECLARE(switch_status_t) switch_core_session_send_dtmf_string(switch_core_session_t *session, const char *dtmf_string)
{
	char *p;
	switch_dtmf_t dtmf = { 0, switch_core_default_dtmf_duration(0) };
	int sent = 0, dur;
	char *string;
	int i, argc;
	char *argv[256];
	int dur_total = 0;

	switch_assert(session != NULL);


	if (switch_channel_down(session->channel)) {
		return SWITCH_STATUS_FALSE;
	}

	if (zstr(dtmf_string)) {
		return SWITCH_STATUS_FALSE;
	}

	if (strlen(dtmf_string) > 99) {
		switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Attempt to send very large dtmf string ignored!\n");
		return SWITCH_STATUS_FALSE;
	}

	string = switch_core_session_strdup(session, dtmf_string);
	argc = switch_separate_string(string, '+', argv, (sizeof(argv) / sizeof(argv[0])));

	if (argc) {
		switch_channel_pre_answer(session->channel);
	}

	for (i = 0; i < argc; i++) {
		dtmf.duration = switch_core_default_dtmf_duration(0);
		dur = switch_core_default_dtmf_duration(0) / 8;
		if ((p = strchr(argv[i], '@'))) {
			*p++ = '\0';
			if ((dur = atoi(p)) > 50) {
				dtmf.duration = dur * 8;
			}
		}


		if (dtmf.duration > switch_core_max_dtmf_duration(0)) {
			switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "%s EXCESSIVE DTMF DIGIT [%c] LEN [%d]\n",
							  switch_channel_get_name(session->channel), dtmf.digit, dtmf.duration);
			dtmf.duration = switch_core_max_dtmf_duration(0);
		} else if (dtmf.duration < switch_core_min_dtmf_duration(0)) {
			switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "%s SHORT DTMF DIGIT [%c] LEN [%d]\n",
							  switch_channel_get_name(session->channel), dtmf.digit, dtmf.duration);
			dtmf.duration = switch_core_min_dtmf_duration(0);
		} else if (!dtmf.duration) {
			dtmf.duration = switch_core_default_dtmf_duration(0);
		}

		for (p = argv[i]; p && *p; p++) {
			if (is_dtmf(*p)) {
				dtmf.digit = *p;
				if (switch_core_session_send_dtmf(session, &dtmf) == SWITCH_STATUS_SUCCESS) {
					switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s send dtmf\ndigit=%c ms=%u samples=%u\n",
									  switch_channel_get_name(session->channel), dtmf.digit, dur, dtmf.duration);
					sent++;
					dur_total += dtmf.duration + 2000;	/* account for 250ms pause */
				}
			}
		}

		if (dur_total) {
			char tmp[32] = "";
			switch_snprintf(tmp, sizeof(tmp), "%d", dur_total / 8);
			switch_channel_set_variable(session->channel, "last_dtmf_duration", tmp);
		}

	}
	return sent ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
}
예제 #7
0
파일: mod_loopback.c 프로젝트: gujun/sscore
static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *session, switch_event_t *var_event,
													switch_caller_profile_t *outbound_profile,
													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];

	if (session) {
		switch_channel_t *channel = switch_core_session_get_channel(session);
		switch_channel_clear_flag(channel, CF_PROXY_MEDIA);
		switch_channel_clear_flag(channel, CF_PROXY_MODE);
		switch_channel_pre_answer(channel);
	}

	if ((*new_session = switch_core_session_request(loopback_endpoint_interface, SWITCH_CALL_DIRECTION_OUTBOUND, 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) {
			channel = switch_core_session_get_channel(*new_session);
			switch_snprintf(name, sizeof(name), "loopback/%s-a", outbound_profile->destination_number);
			switch_channel_set_name(channel, name);
			if (tech_init(tech_pvt, *new_session, session ? switch_core_session_get_read_codec(session) : NULL) != SWITCH_STATUS_SUCCESS) {
				switch_core_session_destroy(new_session);
				return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER;
			}
		} 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 *dialplan = NULL, *context = NULL;

			caller_profile = switch_caller_profile_clone(*new_session, outbound_profile);
			caller_profile->source = switch_core_strdup(caller_profile->pool, modname);
			if (!strncasecmp(caller_profile->destination_number, "app=", 4)) {
				char *dest = switch_core_session_strdup(*new_session, caller_profile->destination_number);
				char *app = dest + 4;
				char *arg = NULL;

				if ((arg = strchr(app, ':'))) {
					*arg++ = '\0';
				}

				switch_channel_set_variable(channel, "loopback_app", app);
				if (arg) {
					switch_channel_set_variable(channel, "loopback_app_arg", arg);
				}

				caller_profile->destination_number = switch_core_strdup(caller_profile->pool, app);
			}

			if ((context = strchr(caller_profile->destination_number, '/'))) {
				*context++ = '\0';

				if ((dialplan = strchr(context, '/'))) {
					*dialplan++ = '\0';
				}

				if (!zstr(context)) {
					caller_profile->context = switch_core_strdup(caller_profile->pool, context);
				}

				if (!zstr(dialplan)) {
					caller_profile->dialplan = switch_core_strdup(caller_profile->pool, dialplan);
				}
			}

			if (zstr(caller_profile->context)) {
				caller_profile->context = switch_core_strdup(caller_profile->pool, "default");
			}

			if (zstr(caller_profile->dialplan)) {
				caller_profile->dialplan = switch_core_strdup(caller_profile->pool, "xml");
			}

			switch_snprintf(name, sizeof(name), "loopback/%s-a", caller_profile->destination_number);
			switch_channel_set_name(channel, name);
			switch_set_flag_locked(tech_pvt, TFLAG_OUTBOUND);
			switch_channel_set_caller_profile(channel, caller_profile);
			tech_pvt->caller_profile = caller_profile;
		} 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_channel_set_state(channel, CS_INIT);

		return SWITCH_CAUSE_SUCCESS;
	}

	return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER;
}
예제 #8
0
void fs_channel_pre_answer(switch_core_session_t *session)
{
	switch_channel_t *channel = switch_core_session_get_channel(session);
	switch_channel_pre_answer(channel);
}
예제 #9
0
switch_status_t ladspa_session(switch_core_session_t *session, const char *flags, const char *plugin_name, const char *label, const char *params)
{
    switch_channel_t *channel = switch_core_session_get_channel(session);
    switch_media_bug_t *bug;
    switch_status_t status;
    switch_ladspa_t *pvt = { 0 };
    switch_codec_implementation_t read_impl = { 0 };
    int i, bflags = SMBF_READ_REPLACE | SMBF_ANSWER_REQ;
    char *pstr;
    int argc;
    char *argv[50];
    char *dparams = NULL;

    if (zstr(plugin_name)) {
        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "%s INVALID PLUGIN\n", switch_channel_get_name(channel));
        return SWITCH_STATUS_FALSE;
    }

    if (zstr(flags)) {
        flags = "r";
    }

    if (strchr(flags, 'w')) {
        bflags = SMBF_WRITE_REPLACE;
    }

    switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "FLAGS: %s PLUGIN: %s LABEL: %s PARAMS: %s\n",
                      flags, plugin_name, label, params);

    switch_core_session_get_read_impl(session, &read_impl);

    pvt = switch_core_session_alloc(session, sizeof(*pvt));

    pvt->session = session;
    if (!zstr(label)) {
        pvt->label_name = switch_core_session_strdup(session, label);
    } else {
        char *p;
        pvt->label_name = switch_core_session_strdup(session, plugin_name);
        if ((p = strrchr(pvt->label_name, '.'))) {
            *p = '\0';
        }
    }

    if (strstr(plugin_name, ".so")) {
        pvt->plugin_name = switch_core_session_strdup(session, plugin_name);
    } else {
        pvt->plugin_name = switch_core_session_sprintf(session, "%s.so", plugin_name);
    }

    dparams = switch_core_session_strdup(session, params);

    argc = switch_split(dparams, ' ', argv);

    for (i = 0; i < argc; i++) {
        if (switch_is_number(argv[i])) {
            if (pvt->num_idx < MAX_INDEX) {
                pvt->config[pvt->num_idx] = atof(argv[i]);
                pvt->has_config[pvt->num_idx] = 1;
                pvt->num_idx++;
            }
        } else {
            if (pvt->str_idx < MAX_INDEX) {
                pvt->str_config[pvt->str_idx++] = switch_core_session_strdup(session, argv[i]);
            }
        }
    }

    if (switch_channel_pre_answer(channel) != SWITCH_STATUS_SUCCESS) {
        return SWITCH_STATUS_FALSE;
    }

    pstr = switch_core_session_sprintf(session, "%s|%s|%s|%s", flags, plugin_name, label, params);

    if ((status = switch_core_media_bug_add(session, "ladspa", pstr,
                                            ladspa_callback, pvt, 0, bflags | SMBF_NO_PAUSE, &bug)) != SWITCH_STATUS_SUCCESS) {
        return status;
    }

    switch_channel_set_private(channel, "ladspa", bug);

    return SWITCH_STATUS_SUCCESS;
}
예제 #10
0
switch_status_t spandsp_inband_dtmf_session(switch_core_session_t *session)
{
	switch_channel_t *channel = switch_core_session_get_channel(session);
	switch_media_bug_t *bug;
	switch_status_t status;
	switch_inband_dtmf_t *pvt;
	switch_codec_implementation_t read_impl = { 0 };
	const char *value;

	switch_core_session_get_read_impl(session, &read_impl);

	if (!(pvt = switch_core_session_alloc(session, sizeof(*pvt)))) {
		return SWITCH_STATUS_MEMERR;
	}

	pvt->session = session;

	/* get detector params */
	pvt->min_dup_digit_spacing = 0;
	value = switch_channel_get_variable(channel, "min_dup_digit_spacing_ms");
	if (!zstr(value) && switch_is_number(value)) {
		int val = atoi(value) * 8; /* convert from ms to samples */
		if (val < 0) {
			switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "min_dup_digit_spacing_ms must be >= 0\n");
		} else {
			pvt->min_dup_digit_spacing = val;
		}
	}

	pvt->threshold = -100;
	value = switch_channel_get_variable(channel, "spandsp_dtmf_rx_threshold");
	if (!zstr(value) && switch_is_number(value)) {
		int val = atoi(value);
		if (val < -99) {
			switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "spandsp_dtmf_rx_threshold must be >= -99 dBm0\n");
		} else {
			pvt->threshold = val;
		}
	}

	pvt->twist = -1;
	value = switch_channel_get_variable(channel, "spandsp_dtmf_rx_twist");
	if (!zstr(value) && switch_is_number(value)) {
		int val = atoi(value);
		if (val < 0) {
			switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "spandsp_dtmf_rx_twist must be >= 0 dB\n");
		} else {
			pvt->twist = val;
		}
	}

	pvt->reverse_twist = -1;
	value = switch_channel_get_variable(channel, "spandsp_dtmf_rx_reverse_twist");
	if (!zstr(value) && switch_is_number(value)) {
		int val = atoi(value);
		if (val < 0) {
			switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "spandsp_dtmf_rx_reverse_twist must be >= 0 dB\n");
		} else {
			pvt->reverse_twist = val;
		}
	}

	pvt->filter_dialtone = -1;
	value = switch_channel_get_variable(channel, "spandsp_dtmf_rx_filter_dialtone");
	if (switch_true(value)) {
		pvt->filter_dialtone = 1;
	} else if (switch_false(value)) {
		pvt->filter_dialtone = 0;
	}

	if (switch_channel_pre_answer(channel) != SWITCH_STATUS_SUCCESS) {
		return SWITCH_STATUS_FALSE;
	}

	if ((status = switch_core_media_bug_add(session, "spandsp_dtmf_detect", NULL,
						inband_dtmf_callback, pvt, 0, SMBF_READ_REPLACE | SMBF_NO_PAUSE, &bug)) != SWITCH_STATUS_SUCCESS) {
		return status;
	}

	switch_channel_set_private(channel, "dtmf", bug);

	return SWITCH_STATUS_SUCCESS;
}