fix inband DTMF generation issue

git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@7599 d0543943-73ff-0310-b7d9-9358b9ac24b2
This commit is contained in:
Anthony Minessale 2008-02-13 00:32:09 +00:00
parent 57d9670a70
commit c0b192bedc
2 changed files with 114 additions and 67 deletions

View File

@ -878,9 +878,11 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_inband_dtmf_session(switch_core_sessi
typedef struct { typedef struct {
switch_core_session_t *session; switch_core_session_t *session;
teletone_generation_session_t ts; teletone_generation_session_t ts;
switch_queue_t *digit_queue;
switch_buffer_t *audio_buffer; switch_buffer_t *audio_buffer;
switch_mutex_t *mutex; switch_mutex_t *mutex;
int read; int read;
int ready;
} switch_inband_dtmf_generate_t; } switch_inband_dtmf_generate_t;
static int teletone_dtmf_generate_handler(teletone_generation_session_t * ts, teletone_tone_map_t * map) static int teletone_dtmf_generate_handler(teletone_generation_session_t * ts, teletone_tone_map_t * map)
@ -902,22 +904,36 @@ static switch_status_t generate_on_dtmf(switch_core_session_t *session, const sw
{ {
switch_channel_t *channel = switch_core_session_get_channel(session); switch_channel_t *channel = switch_core_session_get_channel(session);
switch_media_bug_t *bug = switch_channel_get_private(channel, "dtmf_generate"); switch_media_bug_t *bug = switch_channel_get_private(channel, "dtmf_generate");
switch_status_t status = SWITCH_STATUS_SUCCESS;
if (bug) { if (bug) {
switch_inband_dtmf_generate_t *pvt = (switch_inband_dtmf_generate_t *) switch_core_media_bug_get_user_data(bug); switch_inband_dtmf_generate_t *pvt = (switch_inband_dtmf_generate_t *) switch_core_media_bug_get_user_data(bug);
if (pvt) { if (pvt) {
char buf[2] = "";
switch_mutex_lock(pvt->mutex); switch_mutex_lock(pvt->mutex);
buf[0] = dtmf->digit; if (pvt->ready) {
teletone_run(&pvt->ts, buf); switch_dtmf_t *dt = NULL;
switch_zmalloc(dt, sizeof(*dt));
*dt = *dtmf;
if (switch_queue_trypush(pvt->digit_queue, dt) == SWITCH_STATUS_SUCCESS) {
dt = NULL;
/*
SWITCH_STATUS_FALSE indicates pretend there never was a DTMF
since we will be generating it inband now.
*/
status = SWITCH_STATUS_FALSE;
} else {
free(dt);
}
}
switch_mutex_unlock(pvt->mutex); switch_mutex_unlock(pvt->mutex);
return SWITCH_STATUS_FALSE;
} }
} }
return SWITCH_STATUS_SUCCESS;
return status;
} }
static switch_bool_t inband_dtmf_generate_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type) 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_inband_dtmf_generate_t *pvt = (switch_inband_dtmf_generate_t *) user_data;
@ -929,37 +945,63 @@ static switch_bool_t inband_dtmf_generate_callback(switch_media_bug_t *bug, void
switch (type) { switch (type) {
case SWITCH_ABC_TYPE_INIT: case SWITCH_ABC_TYPE_INIT:
{ {
switch_queue_create(&pvt->digit_queue, 100, switch_core_session_get_pool(pvt->session));
switch_buffer_create_dynamic(&pvt->audio_buffer, 512, 1024, 0); switch_buffer_create_dynamic(&pvt->audio_buffer, 512, 1024, 0);
teletone_init_session(&pvt->ts, 0, teletone_dtmf_generate_handler, pvt->audio_buffer); 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.rate = read_codec->implementation->actual_samples_per_second;
pvt->ts.channels = 1; pvt->ts.channels = 1;
switch_mutex_init(&pvt->mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(pvt->session)); 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); switch_core_event_hook_add_recv_dtmf(pvt->session, generate_on_dtmf);
switch_mutex_lock(pvt->mutex);
pvt->ready = 1;
switch_mutex_unlock(pvt->mutex);
} }
break; break;
case SWITCH_ABC_TYPE_CLOSE: case SWITCH_ABC_TYPE_CLOSE:
{ {
switch_mutex_lock(pvt->mutex);
pvt->ready = 0;
switch_core_event_hook_remove_recv_dtmf(pvt->session, generate_on_dtmf);
switch_buffer_destroy(&pvt->audio_buffer); switch_buffer_destroy(&pvt->audio_buffer);
teletone_destroy_session(&pvt->ts); teletone_destroy_session(&pvt->ts);
switch_core_event_hook_remove_recv_dtmf(pvt->session, generate_on_dtmf); switch_mutex_unlock(pvt->mutex);
} }
break; break;
case SWITCH_ABC_TYPE_READ_REPLACE: case SWITCH_ABC_TYPE_READ_REPLACE:
case SWITCH_ABC_TYPE_WRITE_REPLACE: case SWITCH_ABC_TYPE_WRITE_REPLACE:
{ {
switch_size_t bytes; switch_size_t bytes;
void *pop;
switch_mutex_lock(pvt->mutex); switch_mutex_lock(pvt->mutex);
if (!pvt->ready) {
switch_mutex_unlock(pvt->mutex);
return SWITCH_FALSE;
}
if (pvt->read) { if (pvt->read) {
frame = switch_core_media_bug_get_read_replace_frame(bug); frame = switch_core_media_bug_get_read_replace_frame(bug);
} else { } else {
frame = switch_core_media_bug_get_write_replace_frame(bug); frame = switch_core_media_bug_get_write_replace_frame(bug);
} }
while (switch_queue_trypop(pvt->digit_queue, &pop) == SWITCH_STATUS_SUCCESS) {
switch_dtmf_t *dtmf = (switch_dtmf_t *) pop;
char buf[2] = "";
buf[0] = dtmf->digit;
pvt->ts.duration = dtmf->duration;
teletone_run(&pvt->ts, buf);
}
if (switch_buffer_inuse(pvt->audio_buffer) && (bytes = switch_buffer_read(pvt->audio_buffer, frame->data, frame->datalen))) { if (switch_buffer_inuse(pvt->audio_buffer) && (bytes = switch_buffer_read(pvt->audio_buffer, frame->data, frame->datalen))) {
if (bytes < frame->datalen) { if (bytes < frame->datalen) {
switch_byte_t *dp = frame->data; switch_byte_t *dp = frame->data;
memset(dp + bytes, 0, frame->datalen - bytes); memset(dp + bytes, 0, frame->datalen - bytes);
} }
} }
if (pvt->read) { if (pvt->read) {
switch_core_media_bug_set_read_replace_frame(bug, frame); switch_core_media_bug_set_read_replace_frame(bug, frame);
} else { } else {

View File

@ -1038,6 +1038,8 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_
READ_INC(rtp_session); READ_INC(rtp_session);
while (switch_rtp_ready(rtp_session)) { while (switch_rtp_ready(rtp_session)) {
int do_cng = 0;
bytes = sizeof(rtp_msg_t); bytes = sizeof(rtp_msg_t);
status = switch_socket_recvfrom(rtp_session->from_addr, rtp_session->sock, 0, (void *) &rtp_session->recv_msg, &bytes); status = switch_socket_recvfrom(rtp_session->from_addr, rtp_session->sock, 0, (void *) &rtp_session->recv_msg, &bytes);
@ -1102,7 +1104,64 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_
} }
} }
if (!bytes && switch_test_flag(rtp_session, SWITCH_RTP_FLAG_BREAK)) {
/* RFC2833 ... like all RFC RE: VoIP, guarenteed to drive you to insanity!
We know the real rules here, but if we enforce them, it's an interop nightmare so,
we put up with as much as we can so we don't have to deal with being punished for
doing it right. Nice guys finish last!
*/
if (bytes && !switch_test_flag(rtp_session, SWITCH_RTP_FLAG_PASS_RFC2833) && rtp_session->recv_msg.header.pt == rtp_session->te) {
unsigned char *packet = (unsigned char *) rtp_session->recv_msg.body;
int end = packet[1] & 0x80 ? 1 : 0;
uint16_t duration = (packet[2] << 8) + packet[3];
char key = switch_rfc2833_to_char(packet[0]);
uint16_t in_digit_seq = ntohs((uint16_t) rtp_session->recv_msg.header.seq);
if (in_digit_seq > rtp_session->dtmf_data.in_digit_seq) {
uint32_t ts = htonl(rtp_session->recv_msg.header.ts);
//int m = rtp_session->recv_msg.header.m;
rtp_session->dtmf_data.in_digit_seq = in_digit_seq;
//printf("%c %u %u %u\n", key, in_digit_seq, ts, duration);
if (rtp_session->dtmf_data.last_duration > duration && ts == rtp_session->dtmf_data.in_digit_ts) {
rtp_session->dtmf_data.flip++;
}
if (end) {
if (rtp_session->dtmf_data.in_digit_ts) {
switch_dtmf_t dtmf = { key, duration };
if (ts > rtp_session->dtmf_data.in_digit_ts) {
dtmf.duration += (ts - rtp_session->dtmf_data.in_digit_ts);
}
if (rtp_session->dtmf_data.flip) {
dtmf.duration += rtp_session->dtmf_data.flip * 0xFFFF;
rtp_session->dtmf_data.flip = 0;
//printf("you're welcome!\n");
}
//printf("done digit=%c ts=%u start_ts=%u dur=%u ddur=%u\n",
//dtmf.digit, ts, rtp_session->dtmf_data.in_digit_ts, duration, dtmf.duration);
switch_rtp_queue_rfc2833_in(rtp_session, &dtmf);
switch_set_flag_locked(rtp_session, SWITCH_RTP_FLAG_BREAK);
rtp_session->dtmf_data.last_digit = rtp_session->dtmf_data.first_digit;
}
rtp_session->dtmf_data.in_digit_ts = 0;
} else if (!rtp_session->dtmf_data.in_digit_ts) {
rtp_session->dtmf_data.in_digit_ts = ts;
rtp_session->dtmf_data.first_digit = key;
}
rtp_session->dtmf_data.last_duration = duration;
}
do_cng = 1;
}
if (do_cng || (!bytes && switch_test_flag(rtp_session, SWITCH_RTP_FLAG_BREAK))) {
switch_clear_flag_locked(rtp_session, SWITCH_RTP_FLAG_BREAK); switch_clear_flag_locked(rtp_session, SWITCH_RTP_FLAG_BREAK);
memset(&rtp_session->recv_msg.body, 0, 2); memset(&rtp_session->recv_msg.body, 0, 2);
@ -1239,60 +1298,6 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_
rtp_session->rpayload = (switch_payload_t) rtp_session->recv_msg.header.pt; rtp_session->rpayload = (switch_payload_t) rtp_session->recv_msg.header.pt;
/* RFC2833 ... like all RFC RE: VoIP, guarenteed to drive you to insanity!
We know the real rules here, but if we enforce them, it's an interop nightmare so,
we put up with as much as we can so we don't have to deal with being punished for
doing it right. Nice guys finish last!
*/
if (!switch_test_flag(rtp_session, SWITCH_RTP_FLAG_PASS_RFC2833) && rtp_session->recv_msg.header.pt == rtp_session->te) {
unsigned char *packet = (unsigned char *) rtp_session->recv_msg.body;
int end = packet[1] & 0x80 ? 1 : 0;
uint16_t duration = (packet[2] << 8) + packet[3];
char key = switch_rfc2833_to_char(packet[0]);
uint16_t in_digit_seq = ntohs((uint16_t) rtp_session->recv_msg.header.seq);
if (in_digit_seq > rtp_session->dtmf_data.in_digit_seq) {
uint32_t ts = htonl(rtp_session->recv_msg.header.ts);
//int m = rtp_session->recv_msg.header.m;
rtp_session->dtmf_data.in_digit_seq = in_digit_seq;
//printf("%c %u %u %u\n", key, in_digit_seq, ts, duration);
if (rtp_session->dtmf_data.last_duration > duration && ts == rtp_session->dtmf_data.in_digit_ts) {
rtp_session->dtmf_data.flip++;
}
if (end) {
if (rtp_session->dtmf_data.in_digit_ts) {
switch_dtmf_t dtmf = { key, duration };
if (ts > rtp_session->dtmf_data.in_digit_ts) {
dtmf.duration += (ts - rtp_session->dtmf_data.in_digit_ts);
}
if (rtp_session->dtmf_data.flip) {
dtmf.duration += rtp_session->dtmf_data.flip * 0xFFFF;
rtp_session->dtmf_data.flip = 0;
//printf("you're welcome!\n");
}
//printf("done digit=%c ts=%u start_ts=%u dur=%u ddur=%u\n",
//dtmf.digit, ts, rtp_session->dtmf_data.in_digit_ts, duration, dtmf.duration);
switch_rtp_queue_rfc2833_in(rtp_session, &dtmf);
switch_set_flag_locked(rtp_session, SWITCH_RTP_FLAG_BREAK);
rtp_session->dtmf_data.last_digit = rtp_session->dtmf_data.first_digit;
}
rtp_session->dtmf_data.in_digit_ts = 0;
} else if (!rtp_session->dtmf_data.in_digit_ts) {
rtp_session->dtmf_data.in_digit_ts = ts;
rtp_session->dtmf_data.first_digit = key;
}
rtp_session->dtmf_data.last_duration = duration;
}
goto do_continue;
}
break; break;
do_continue: do_continue: