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:
parent
57d9670a70
commit
c0b192bedc
|
@ -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 {
|
||||||
|
|
125
src/switch_rtp.c
125
src/switch_rtp.c
|
@ -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,69 +1298,15 @@ 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:
|
||||||
|
|
||||||
if (rtp_session->ms_per_packet) {
|
if (rtp_session->ms_per_packet) {
|
||||||
switch_yield((rtp_session->ms_per_packet / 1000) * 750);
|
switch_yield((rtp_session->ms_per_packet / 1000) * 750);
|
||||||
} else {
|
} else {
|
||||||
switch_yield(1000);
|
switch_yield(1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*payload_type = (switch_payload_t) rtp_session->recv_msg.header.pt;
|
*payload_type = (switch_payload_t) rtp_session->recv_msg.header.pt;
|
||||||
|
@ -1437,7 +1442,7 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_zerocopy_read_frame(switch_rtp_t *rtp
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes = rtp_common_read(rtp_session, &frame->payload, &frame->flags);
|
bytes = rtp_common_read(rtp_session, &frame->payload, &frame->flags);
|
||||||
|
|
||||||
frame->data = rtp_session->recv_msg.body;
|
frame->data = rtp_session->recv_msg.body;
|
||||||
frame->packet = &rtp_session->recv_msg;
|
frame->packet = &rtp_session->recv_msg;
|
||||||
frame->packetlen = bytes;
|
frame->packetlen = bytes;
|
||||||
|
|
Loading…
Reference in New Issue