Merged revisions 51311 via svnmerge from

https://origsvn.digium.com/svn/asterisk/branches/1.4

........
r51311 | russell | 2007-01-19 11:49:38 -0600 (Fri, 19 Jan 2007) | 23 lines

Merge the changes from the /team/group/vldtmf_fixup branch.

The main bug being addressed here is a problem introduced when two SIP
channels using SIP INFO dtmf have their media directly bridged.  So, when a
DTMF END frame comes into Asterisk from an incoming INFO message, Asterisk
would try to emulate a digit of some length by first sending a DTMF BEGIN
frame and sending a DTMF END later timed off of incoming audio.  However,
since there was no audio coming in, the DTMF_END was never generated.  This
caused DTMF based features to no longer work.

To fix this, the core now knows when a channel doesn't care about DTMF BEGIN
frames (such as a SIP channel sending INFO dtmf).  If this is the case, then
Asterisk will not emulate a digit of some length, and will instead just pass
through the single DTMF END event.

Channel drivers also now get passed the length of the digit to their digit_end
callback.  This improves SIP INFO support even further by enabling us to put
the real digit duration in the INFO message instead of a hard coded 250ms.
Also, for an incoming INFO message, the duration is read from the frame and
passed into the core instead of just getting ignored.

(issue #8597, maybe others...)

........


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@51314 65c4cc65-6c06-0410-ace0-fbb531ad65f3
1.6.0
Russell Bryant 19 years ago
parent 9d509eaf76
commit dcca8f345f

@ -230,7 +230,7 @@ static struct ast_channel *agent_request(const char *type, int format, void *dat
static int agent_devicestate(void *data); static int agent_devicestate(void *data);
static void agent_logoff_maintenance(struct agent_pvt *p, char *loginchan, long logintime, const char *uniqueid, char *logcommand); static void agent_logoff_maintenance(struct agent_pvt *p, char *loginchan, long logintime, const char *uniqueid, char *logcommand);
static int agent_digit_begin(struct ast_channel *ast, char digit); static int agent_digit_begin(struct ast_channel *ast, char digit);
static int agent_digit_end(struct ast_channel *ast, char digit); static int agent_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
static int agent_call(struct ast_channel *ast, char *dest, int timeout); static int agent_call(struct ast_channel *ast, char *dest, int timeout);
static int agent_hangup(struct ast_channel *ast); static int agent_hangup(struct ast_channel *ast);
static int agent_answer(struct ast_channel *ast); static int agent_answer(struct ast_channel *ast);
@ -610,12 +610,12 @@ static int agent_digit_begin(struct ast_channel *ast, char digit)
return res; return res;
} }
static int agent_digit_end(struct ast_channel *ast, char digit) static int agent_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
{ {
struct agent_pvt *p = ast->tech_pvt; struct agent_pvt *p = ast->tech_pvt;
int res = -1; int res = -1;
ast_mutex_lock(&p->lock); ast_mutex_lock(&p->lock);
ast_senddigit_end(p->chan, digit); ast_senddigit_end(p->chan, digit, duration);
ast_mutex_unlock(&p->lock); ast_mutex_unlock(&p->lock);
return res; return res;
} }

@ -185,7 +185,7 @@ static int nosound = 0;
/* ZZ */ /* ZZ */
static struct ast_channel *alsa_request(const char *type, int format, void *data, int *cause); static struct ast_channel *alsa_request(const char *type, int format, void *data, int *cause);
static int alsa_digit(struct ast_channel *c, char digit); static int alsa_digit(struct ast_channel *c, char digit, unsigned int duration);
static int alsa_text(struct ast_channel *c, const char *text); static int alsa_text(struct ast_channel *c, const char *text);
static int alsa_hangup(struct ast_channel *c); static int alsa_hangup(struct ast_channel *c);
static int alsa_answer(struct ast_channel *c); static int alsa_answer(struct ast_channel *c);
@ -494,10 +494,11 @@ static int soundcard_init(void)
return readdev; return readdev;
} }
static int alsa_digit(struct ast_channel *c, char digit) static int alsa_digit(struct ast_channel *c, char digit, unsigned int duration)
{ {
ast_mutex_lock(&alsalock); ast_mutex_lock(&alsalock);
ast_verbose(" << Console Received digit %c >> \n", digit); ast_verbose(" << Console Received digit %c of duration %u ms >> \n",
digit, duration);
ast_mutex_unlock(&alsalock); ast_mutex_unlock(&alsalock);
return 0; return 0;
} }

@ -93,7 +93,7 @@ static AST_LIST_HEAD_STATIC(features, feature_pvt);
static struct ast_channel *features_request(const char *type, int format, void *data, int *cause); static struct ast_channel *features_request(const char *type, int format, void *data, int *cause);
static int features_digit_begin(struct ast_channel *ast, char digit); static int features_digit_begin(struct ast_channel *ast, char digit);
static int features_digit_end(struct ast_channel *ast, char digit); static int features_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
static int features_call(struct ast_channel *ast, char *dest, int timeout); static int features_call(struct ast_channel *ast, char *dest, int timeout);
static int features_hangup(struct ast_channel *ast); static int features_hangup(struct ast_channel *ast);
static int features_answer(struct ast_channel *ast); static int features_answer(struct ast_channel *ast);
@ -316,7 +316,7 @@ static int features_digit_begin(struct ast_channel *ast, char digit)
return res; return res;
} }
static int features_digit_end(struct ast_channel *ast, char digit) static int features_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
{ {
struct feature_pvt *p = ast->tech_pvt; struct feature_pvt *p = ast->tech_pvt;
int res = -1; int res = -1;
@ -326,7 +326,7 @@ static int features_digit_end(struct ast_channel *ast, char digit)
ast_mutex_lock(&p->lock); ast_mutex_lock(&p->lock);
x = indexof(p, ast, 0); x = indexof(p, ast, 0);
if (!x && p->subchan) if (!x && p->subchan)
res = ast_senddigit_end(p->subchan, digit); res = ast_senddigit_end(p->subchan, digit, duration);
ast_mutex_unlock(&p->lock); ast_mutex_unlock(&p->lock);
return res; return res;
} }

@ -170,7 +170,9 @@ AST_MUTEX_DEFINE_STATIC(gtalklock); /*!< Protect the interface list (of gtalk_pv
/* Forward declarations */ /* Forward declarations */
static struct ast_channel *gtalk_request(const char *type, int format, void *data, int *cause); static struct ast_channel *gtalk_request(const char *type, int format, void *data, int *cause);
static int gtalk_digit(struct ast_channel *ast, char digit); static int gtalk_digit(struct ast_channel *ast, char digit, unsigned int duration);
static int gtalk_digit_begin(struct ast_channel *ast, char digit);
static int gtalk_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
static int gtalk_call(struct ast_channel *ast, char *dest, int timeout); static int gtalk_call(struct ast_channel *ast, char *dest, int timeout);
static int gtalk_hangup(struct ast_channel *ast); static int gtalk_hangup(struct ast_channel *ast);
static int gtalk_answer(struct ast_channel *ast); static int gtalk_answer(struct ast_channel *ast);
@ -195,8 +197,8 @@ static const struct ast_channel_tech gtalk_tech = {
.description = "Gtalk Channel Driver", .description = "Gtalk Channel Driver",
.capabilities = ((AST_FORMAT_MAX_AUDIO << 1) - 1), .capabilities = ((AST_FORMAT_MAX_AUDIO << 1) - 1),
.requester = gtalk_request, .requester = gtalk_request,
.send_digit_begin = gtalk_digit, .send_digit_begin = gtalk_digit_begin,
.send_digit_end = gtalk_digit, .send_digit_end = gtalk_digit_end,
.bridge = ast_rtp_bridge, .bridge = ast_rtp_bridge,
.call = gtalk_call, .call = gtalk_call,
.hangup = gtalk_hangup, .hangup = gtalk_hangup,
@ -1338,7 +1340,17 @@ static int gtalk_indicate(struct ast_channel *ast, int condition, const void *da
return res; return res;
} }
static int gtalk_digit(struct ast_channel *ast, char digit) static int gtalk_digit_begin(struct ast_channel *chan, char digit)
{
return gtalk_digit(chan, digit, 0);
}
static int gtalk_digit_end(struct ast_channel *chan, char digit, unsigned int duration)
{
return gtalk_digit(chan, digit, duration);
}
static int gtalk_digit(struct ast_channel *ast, char digit, unsigned int duration)
{ {
struct gtalk_pvt *p = ast->tech_pvt; struct gtalk_pvt *p = ast->tech_pvt;
struct gtalk *client = p->parent; struct gtalk *client = p->parent;
@ -1374,7 +1386,7 @@ static int gtalk_digit(struct ast_channel *ast, char digit)
ast_mutex_lock(&p->lock); ast_mutex_lock(&p->lock);
if (ast->dtmff.frametype == AST_FRAME_DTMF) { if (ast->dtmff.frametype == AST_FRAME_DTMF) {
ast_verbose("Sending 250ms dtmf!\n"); ast_log(LOG_DEBUG, "Sending 250ms dtmf!\n");
} else if (ast->dtmff.frametype == AST_FRAME_DTMF_BEGIN) { } else if (ast->dtmff.frametype == AST_FRAME_DTMF_BEGIN) {
iks_insert_attrib(dtmf, "action", "button-down"); iks_insert_attrib(dtmf, "action", "button-down");
} else if (ast->dtmff.frametype == AST_FRAME_DTMF_END) { } else if (ast->dtmff.frametype == AST_FRAME_DTMF_END) {

@ -235,7 +235,7 @@ static int h323_do_reload(void);
static struct ast_channel *oh323_request(const char *type, int format, void *data, int *cause); static struct ast_channel *oh323_request(const char *type, int format, void *data, int *cause);
static int oh323_digit_begin(struct ast_channel *c, char digit); static int oh323_digit_begin(struct ast_channel *c, char digit);
static int oh323_digit_end(struct ast_channel *c, char digit); static int oh323_digit_end(struct ast_channel *c, char digit, unsigned int duration);
static int oh323_call(struct ast_channel *c, char *dest, int timeout); static int oh323_call(struct ast_channel *c, char *dest, int timeout);
static int oh323_hangup(struct ast_channel *c); static int oh323_hangup(struct ast_channel *c);
static int oh323_answer(struct ast_channel *c); static int oh323_answer(struct ast_channel *c);
@ -548,7 +548,7 @@ static int oh323_digit_begin(struct ast_channel *c, char digit)
* Send (play) the specified digit to the channel. * Send (play) the specified digit to the channel.
* *
*/ */
static int oh323_digit_end(struct ast_channel *c, char digit) static int oh323_digit_end(struct ast_channel *c, char digit, unsigned int duration)
{ {
struct oh323_pvt *pvt = (struct oh323_pvt *) c->tech_pvt; struct oh323_pvt *pvt = (struct oh323_pvt *) c->tech_pvt;
char *token; char *token;

@ -817,7 +817,7 @@ static int iax2_answer(struct ast_channel *c);
static int iax2_call(struct ast_channel *c, char *dest, int timeout); static int iax2_call(struct ast_channel *c, char *dest, int timeout);
static int iax2_devicestate(void *data); static int iax2_devicestate(void *data);
static int iax2_digit_begin(struct ast_channel *c, char digit); static int iax2_digit_begin(struct ast_channel *c, char digit);
static int iax2_digit_end(struct ast_channel *c, char digit); static int iax2_digit_end(struct ast_channel *c, char digit, unsigned int duration);
static int iax2_do_register(struct iax2_registry *reg); static int iax2_do_register(struct iax2_registry *reg);
static int iax2_fixup(struct ast_channel *oldchannel, struct ast_channel *newchan); static int iax2_fixup(struct ast_channel *oldchannel, struct ast_channel *newchan);
static int iax2_hangup(struct ast_channel *c); static int iax2_hangup(struct ast_channel *c);
@ -2476,7 +2476,7 @@ static int iax2_digit_begin(struct ast_channel *c, char digit)
return send_command_locked(PTR_TO_CALLNO(c->tech_pvt), AST_FRAME_DTMF_BEGIN, digit, 0, NULL, 0, -1); return send_command_locked(PTR_TO_CALLNO(c->tech_pvt), AST_FRAME_DTMF_BEGIN, digit, 0, NULL, 0, -1);
} }
static int iax2_digit_end(struct ast_channel *c, char digit) static int iax2_digit_end(struct ast_channel *c, char digit, unsigned int duration)
{ {
return send_command_locked(PTR_TO_CALLNO(c->tech_pvt), AST_FRAME_DTMF_END, digit, 0, NULL, 0, -1); return send_command_locked(PTR_TO_CALLNO(c->tech_pvt), AST_FRAME_DTMF_END, digit, 0, NULL, 0, -1);
} }
@ -6353,7 +6353,7 @@ static int socket_process_meta(int packet_len, struct ast_iax2_meta_hdr *meta, s
if (packet_len < (sizeof(*meta) + sizeof(*mth))) { if (packet_len < (sizeof(*meta) + sizeof(*mth))) {
ast_log(LOG_WARNING, "midget meta trunk packet received (%d of %d min)\n", packet_len, ast_log(LOG_WARNING, "midget meta trunk packet received (%d of %d min)\n", packet_len,
sizeof(*meta) + sizeof(*mth)); (int) (sizeof(*meta) + sizeof(*mth)));
return 1; return 1;
} }
mth = (struct ast_iax2_meta_trunk_hdr *)(meta->data); mth = (struct ast_iax2_meta_trunk_hdr *)(meta->data);
@ -6523,7 +6523,7 @@ static int socket_process(struct iax2_thread *thread)
memcpy(&sin, &thread->iosin, sizeof(sin)); memcpy(&sin, &thread->iosin, sizeof(sin));
if (res < sizeof(*mh)) { if (res < sizeof(*mh)) {
ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, sizeof(*mh)); ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int) sizeof(*mh));
return 1; return 1;
} }
if ((vh->zeros == 0) && (ntohs(vh->callno) & 0x8000)) { if ((vh->zeros == 0) && (ntohs(vh->callno) & 0x8000)) {
@ -6684,7 +6684,7 @@ static int socket_process(struct iax2_thread *thread)
} }
/* A full frame */ /* A full frame */
if (res < sizeof(*fh)) { if (res < sizeof(*fh)) {
ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, sizeof(*fh)); ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int) sizeof(*fh));
ast_mutex_unlock(&iaxsl[fr->callno]); ast_mutex_unlock(&iaxsl[fr->callno]);
return 1; return 1;
} }

@ -169,7 +169,7 @@ AST_MUTEX_DEFINE_STATIC(jinglelock); /*!< Protect the interface list (of jingle_
/* Forward declarations */ /* Forward declarations */
static struct ast_channel *jingle_request(const char *type, int format, void *data, int *cause); static struct ast_channel *jingle_request(const char *type, int format, void *data, int *cause);
static int jingle_digit_begin(struct ast_channel *ast, char digit); static int jingle_digit_begin(struct ast_channel *ast, char digit);
static int jingle_digit_end(struct ast_channel *ast, char digit); static int jingle_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
static int jingle_call(struct ast_channel *ast, char *dest, int timeout); static int jingle_call(struct ast_channel *ast, char *dest, int timeout);
static int jingle_hangup(struct ast_channel *ast); static int jingle_hangup(struct ast_channel *ast);
static int jingle_answer(struct ast_channel *ast); static int jingle_answer(struct ast_channel *ast);
@ -1182,13 +1182,7 @@ static int jingle_indicate(struct ast_channel *ast, int condition, const void *d
return res; return res;
} }
static int jingle_digit_begin(struct ast_channel *chan, char digit) static int jingle_digit(struct ast_channel *ast, char digit, unsigned int duration)
{
/* XXX Does jingle have a concept of the length of a dtmf digit ? */
return 0;
}
static int jingle_digit_end(struct ast_channel *ast, char digit)
{ {
struct jingle_pvt *p = ast->tech_pvt; struct jingle_pvt *p = ast->tech_pvt;
struct jingle *client = p->parent; struct jingle *client = p->parent;
@ -1223,9 +1217,7 @@ static int jingle_digit_end(struct ast_channel *ast, char digit)
iks_insert_node(jingle, dtmf); iks_insert_node(jingle, dtmf);
ast_mutex_lock(&p->lock); ast_mutex_lock(&p->lock);
if(ast->dtmff.frametype == AST_FRAME_DTMF) { if (ast->dtmff.frametype == AST_FRAME_DTMF_BEGIN) {
ast_verbose("Sending 250ms dtmf!\n");
} else if (ast->dtmff.frametype == AST_FRAME_DTMF_BEGIN) {
iks_insert_attrib(dtmf, "action", "button-down"); iks_insert_attrib(dtmf, "action", "button-down");
} else if (ast->dtmff.frametype == AST_FRAME_DTMF_END) { } else if (ast->dtmff.frametype == AST_FRAME_DTMF_END) {
iks_insert_attrib(dtmf, "action", "button-up"); iks_insert_attrib(dtmf, "action", "button-up");
@ -1238,6 +1230,16 @@ static int jingle_digit_end(struct ast_channel *ast, char digit)
return 0; return 0;
} }
static int jingle_digit_begin(struct ast_channel *chan, char digit)
{
return jingle_digit(chan, digit, 0);
}
static int jingle_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
{
return jingle_digit(ast, digit, duration);
}
static int jingle_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen) static int jingle_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen)
{ {
ast_log(LOG_NOTICE, "XXX Implement jingle sendhtml XXX\n"); ast_log(LOG_NOTICE, "XXX Implement jingle sendhtml XXX\n");

@ -68,7 +68,7 @@ static const char tdesc[] = "Local Proxy Channel Driver";
static struct ast_channel *local_request(const char *type, int format, void *data, int *cause); static struct ast_channel *local_request(const char *type, int format, void *data, int *cause);
static int local_digit_begin(struct ast_channel *ast, char digit); static int local_digit_begin(struct ast_channel *ast, char digit);
static int local_digit_end(struct ast_channel *ast, char digit); static int local_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
static int local_call(struct ast_channel *ast, char *dest, int timeout); static int local_call(struct ast_channel *ast, char *dest, int timeout);
static int local_hangup(struct ast_channel *ast); static int local_hangup(struct ast_channel *ast);
static int local_answer(struct ast_channel *ast); static int local_answer(struct ast_channel *ast);
@ -368,7 +368,7 @@ static int local_digit_begin(struct ast_channel *ast, char digit)
return res; return res;
} }
static int local_digit_end(struct ast_channel *ast, char digit) static int local_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
{ {
struct local_pvt *p = ast->tech_pvt; struct local_pvt *p = ast->tech_pvt;
int res = -1; int res = -1;
@ -381,6 +381,7 @@ static int local_digit_end(struct ast_channel *ast, char digit)
ast_mutex_lock(&p->lock); ast_mutex_lock(&p->lock);
isoutbound = IS_OUTBOUND(ast, p); isoutbound = IS_OUTBOUND(ast, p);
f.subclass = digit; f.subclass = digit;
f.len = duration;
res = local_queue_frame(p, isoutbound, &f, ast); res = local_queue_frame(p, isoutbound, &f, ast);
ast_mutex_unlock(&p->lock); ast_mutex_unlock(&p->lock);

@ -428,7 +428,7 @@ static int mgcp_write(struct ast_channel *ast, struct ast_frame *frame);
static int mgcp_indicate(struct ast_channel *ast, int ind, const void *data, size_t datalen); static int mgcp_indicate(struct ast_channel *ast, int ind, const void *data, size_t datalen);
static int mgcp_fixup(struct ast_channel *oldchan, struct ast_channel *newchan); static int mgcp_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
static int mgcp_senddigit_begin(struct ast_channel *ast, char digit); static int mgcp_senddigit_begin(struct ast_channel *ast, char digit);
static int mgcp_senddigit_end(struct ast_channel *ast, char digit); static int mgcp_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration);
static int mgcp_devicestate(void *data); static int mgcp_devicestate(void *data);
static const struct ast_channel_tech mgcp_tech = { static const struct ast_channel_tech mgcp_tech = {
@ -1282,7 +1282,7 @@ static int mgcp_senddigit_begin(struct ast_channel *ast, char digit)
return -1; return -1;
} }
static int mgcp_senddigit_end(struct ast_channel *ast, char digit) static int mgcp_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration)
{ {
struct mgcp_subchannel *sub = ast->tech_pvt; struct mgcp_subchannel *sub = ast->tech_pvt;
char tmp[4]; char tmp[4];

@ -2067,7 +2067,7 @@ static int misdn_digit_begin(struct ast_channel *chan, char digit)
return 0; return 0;
} }
static int misdn_digit_end(struct ast_channel *ast, char digit ) static int misdn_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
{ {
struct chan_list *p; struct chan_list *p;

@ -406,7 +406,7 @@ static int setformat(struct chan_oss_pvt *o, int mode);
static struct ast_channel *oss_request(const char *type, int format, void *data static struct ast_channel *oss_request(const char *type, int format, void *data
, int *cause); , int *cause);
static int oss_digit_begin(struct ast_channel *c, char digit); static int oss_digit_begin(struct ast_channel *c, char digit);
static int oss_digit_end(struct ast_channel *c, char digit); static int oss_digit_end(struct ast_channel *c, char digit, unsigned int duration);
static int oss_text(struct ast_channel *c, const char *text); static int oss_text(struct ast_channel *c, const char *text);
static int oss_hangup(struct ast_channel *c); static int oss_hangup(struct ast_channel *c);
static int oss_answer(struct ast_channel *c); static int oss_answer(struct ast_channel *c);
@ -776,10 +776,11 @@ static int oss_digit_begin(struct ast_channel *c, char digit)
return 0; return 0;
} }
static int oss_digit_end(struct ast_channel *c, char digit) static int oss_digit_end(struct ast_channel *c, char digit, unsigned int duration)
{ {
/* no better use for received digits than print them */ /* no better use for received digits than print them */
ast_verbose(" << Console Received digit %c >> \n", digit); ast_verbose(" << Console Received digit %c of duration %u ms >> \n",
digit, duration);
return 0; return 0;
} }

@ -156,7 +156,7 @@ static char cid_name[AST_MAX_EXTENSION];
static struct ast_channel *phone_request(const char *type, int format, void *data, int *cause); static struct ast_channel *phone_request(const char *type, int format, void *data, int *cause);
static int phone_digit_begin(struct ast_channel *ast, char digit); static int phone_digit_begin(struct ast_channel *ast, char digit);
static int phone_digit_end(struct ast_channel *ast, char digit); static int phone_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
static int phone_call(struct ast_channel *ast, char *dest, int timeout); static int phone_call(struct ast_channel *ast, char *dest, int timeout);
static int phone_hangup(struct ast_channel *ast); static int phone_hangup(struct ast_channel *ast);
static int phone_answer(struct ast_channel *ast); static int phone_answer(struct ast_channel *ast);
@ -244,12 +244,12 @@ static int phone_digit_begin(struct ast_channel *chan, char digit)
return 0; return 0;
} }
static int phone_digit_end(struct ast_channel *ast, char digit) static int phone_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
{ {
struct phone_pvt *p; struct phone_pvt *p;
int outdigit; int outdigit;
p = ast->tech_pvt; p = ast->tech_pvt;
ast_log(LOG_NOTICE, "Dialed %c\n", digit); ast_log(LOG_DEBUG, "Dialed %c\n", digit);
switch(digit) { switch(digit) {
case '0': case '0':
case '1': case '1':
@ -280,7 +280,7 @@ static int phone_digit_end(struct ast_channel *ast, char digit)
ast_log(LOG_WARNING, "Unknown digit '%c'\n", digit); ast_log(LOG_WARNING, "Unknown digit '%c'\n", digit);
return -1; return -1;
} }
ast_log(LOG_NOTICE, "Dialed %d\n", outdigit); ast_log(LOG_DEBUG, "Dialed %d\n", outdigit);
ioctl(p->fd, PHONE_PLAY_TONE, outdigit); ioctl(p->fd, PHONE_PLAY_TONE, outdigit);
p->lastformat = -1; p->lastformat = -1;
return 0; return 0;
@ -333,7 +333,7 @@ static int phone_call(struct ast_channel *ast, char *dest, int timeout)
{ {
digit++; digit++;
while (*digit) while (*digit)
phone_digit_end(ast, *digit++); phone_digit_end(ast, *digit++, 0);
} }
} }

@ -1228,7 +1228,7 @@ static int sip_indicate(struct ast_channel *ast, int condition, const void *data
static int sip_transfer(struct ast_channel *ast, const char *dest); static int sip_transfer(struct ast_channel *ast, const char *dest);
static int sip_fixup(struct ast_channel *oldchan, struct ast_channel *newchan); static int sip_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
static int sip_senddigit_begin(struct ast_channel *ast, char digit); static int sip_senddigit_begin(struct ast_channel *ast, char digit);
static int sip_senddigit_end(struct ast_channel *ast, char digit); static int sip_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration);
/*--- Transmitting responses and requests */ /*--- Transmitting responses and requests */
static int sipsock_read(int *id, int fd, short events, void *ignore); static int sipsock_read(int *id, int fd, short events, void *ignore);
@ -1250,7 +1250,7 @@ static int transmit_request(struct sip_pvt *p, int sipmethod, int inc, enum xmit
static int transmit_request_with_auth(struct sip_pvt *p, int sipmethod, int seqno, enum xmittype reliable, int newbranch); static int transmit_request_with_auth(struct sip_pvt *p, int sipmethod, int seqno, enum xmittype reliable, int newbranch);
static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init); static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init);
static int transmit_reinvite_with_sdp(struct sip_pvt *p, int t38version); static int transmit_reinvite_with_sdp(struct sip_pvt *p, int t38version);
static int transmit_info_with_digit(struct sip_pvt *p, const char digit); static int transmit_info_with_digit(struct sip_pvt *p, const char digit, unsigned int duration);
static int transmit_info_with_vidupdate(struct sip_pvt *p); static int transmit_info_with_vidupdate(struct sip_pvt *p);
static int transmit_message_with_text(struct sip_pvt *p, const char *text); static int transmit_message_with_text(struct sip_pvt *p, const char *text);
static int transmit_refer(struct sip_pvt *p, const char *dest); static int transmit_refer(struct sip_pvt *p, const char *dest);
@ -1495,7 +1495,7 @@ static int add_header(struct sip_request *req, const char *var, const char *valu
static int add_header_contentLength(struct sip_request *req, int len); static int add_header_contentLength(struct sip_request *req, int len);
static int add_line(struct sip_request *req, const char *line); static int add_line(struct sip_request *req, const char *line);
static int add_text(struct sip_request *req, const char *text); static int add_text(struct sip_request *req, const char *text);
static int add_digit(struct sip_request *req, char digit); static int add_digit(struct sip_request *req, char digit, unsigned int duration);
static int add_vidupdate(struct sip_request *req); static int add_vidupdate(struct sip_request *req);
static void add_route(struct sip_request *req, struct sip_route *route); static void add_route(struct sip_request *req, struct sip_route *route);
static int copy_header(struct sip_request *req, const struct sip_request *orig, const char *field); static int copy_header(struct sip_request *req, const struct sip_request *orig, const char *field);
@ -1564,6 +1564,30 @@ static const struct ast_channel_tech sip_tech = {
.send_text = sip_sendtext, .send_text = sip_sendtext,
}; };
/*! \brief This version of the sip channel tech has no send_digit_begin
* callback. This is for use with channels using SIP INFO DTMF so that
* the core knows that the channel doesn't want DTMF BEGIN frames. */
static const struct ast_channel_tech sip_tech_info = {
.type = "SIP",
.description = "Session Initiation Protocol (SIP)",
.capabilities = ((AST_FORMAT_MAX_AUDIO << 1) - 1),
.properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER,
.requester = sip_request_call,
.devicestate = sip_devicestate,
.call = sip_call,
.hangup = sip_hangup,
.answer = sip_answer,
.read = sip_read,
.write = sip_write,
.write_video = sip_write,
.indicate = sip_indicate,
.transfer = sip_transfer,
.fixup = sip_fixup,
.send_digit_end = sip_senddigit_end,
.bridge = ast_rtp_bridge,
.send_text = sip_sendtext,
};
/**--- some list management macros. **/ /**--- some list management macros. **/
#define UNLINK(element, head, prev) do { \ #define UNLINK(element, head, prev) do { \
@ -3688,7 +3712,7 @@ static int sip_senddigit_begin(struct ast_channel *ast, char digit)
/*! \brief Send DTMF character on SIP channel /*! \brief Send DTMF character on SIP channel
within one call, we're able to transmit in many methods simultaneously */ within one call, we're able to transmit in many methods simultaneously */
static int sip_senddigit_end(struct ast_channel *ast, char digit) static int sip_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration)
{ {
struct sip_pvt *p = ast->tech_pvt; struct sip_pvt *p = ast->tech_pvt;
int res = 0; int res = 0;
@ -3696,7 +3720,7 @@ static int sip_senddigit_end(struct ast_channel *ast, char digit)
sip_pvt_lock(p); sip_pvt_lock(p);
switch (ast_test_flag(&p->flags[0], SIP_DTMF)) { switch (ast_test_flag(&p->flags[0], SIP_DTMF)) {
case SIP_DTMF_INFO: case SIP_DTMF_INFO:
transmit_info_with_digit(p, digit); transmit_info_with_digit(p, digit, duration);
break; break;
case SIP_DTMF_RFC2833: case SIP_DTMF_RFC2833:
if (p->rtp) if (p->rtp)
@ -3857,6 +3881,10 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state, const char *tit
return NULL; return NULL;
} }
sip_pvt_lock(i); sip_pvt_lock(i);
if (ast_test_flag(&i->flags[0], SIP_DTMF) == SIP_DTMF_INFO)
tmp->tech = &sip_tech_info;
else
tmp->tech = &sip_tech; tmp->tech = &sip_tech;
/* Select our native format based on codec preference until we receive /* Select our native format based on codec preference until we receive
@ -5927,11 +5955,11 @@ static int add_text(struct sip_request *req, const char *text)
/*! \brief Add DTMF INFO tone to sip message */ /*! \brief Add DTMF INFO tone to sip message */
/* Always adds default duration 250 ms, regardless of what came in over the line */ /* Always adds default duration 250 ms, regardless of what came in over the line */
static int add_digit(struct sip_request *req, char digit) static int add_digit(struct sip_request *req, char digit, unsigned int duration)
{ {
char tmp[256]; char tmp[256];
snprintf(tmp, sizeof(tmp), "Signal=%c\r\nDuration=250\r\n", digit); snprintf(tmp, sizeof(tmp), "Signal=%c\r\nDuration=%u\r\n", digit, duration);
add_header(req, "Content-Type", "application/dtmf-relay"); add_header(req, "Content-Type", "application/dtmf-relay");
add_header_contentLength(req, strlen(tmp)); add_header_contentLength(req, strlen(tmp));
add_line(req, tmp); add_line(req, tmp);
@ -7491,12 +7519,12 @@ static int transmit_refer(struct sip_pvt *p, const char *dest)
/*! \brief Send SIP INFO dtmf message, see Cisco documentation on cisco.com */ /*! \brief Send SIP INFO dtmf message, see Cisco documentation on cisco.com */
static int transmit_info_with_digit(struct sip_pvt *p, const char digit) static int transmit_info_with_digit(struct sip_pvt *p, const char digit, unsigned int duration)
{ {
struct sip_request req; struct sip_request req;
reqprep(&req, p, SIP_INFO, 0, 1); reqprep(&req, p, SIP_INFO, 0, 1);
add_digit(&req, digit); add_digit(&req, digit, duration);
return send_request(p, &req, XMIT_RELIABLE, p->ocseq); return send_request(p, &req, XMIT_RELIABLE, p->ocseq);
} }
@ -10881,6 +10909,7 @@ static void handle_request_info(struct sip_pvt *p, struct sip_request *req)
/* Need to check the media/type */ /* Need to check the media/type */
if (!strcasecmp(c, "application/dtmf-relay") || if (!strcasecmp(c, "application/dtmf-relay") ||
!strcasecmp(c, "application/vnd.nortelnetworks.digits")) { !strcasecmp(c, "application/vnd.nortelnetworks.digits")) {
unsigned int duration = 0;
/* Try getting the "signal=" part */ /* Try getting the "signal=" part */
if (ast_strlen_zero(c = get_body(req, "Signal")) && ast_strlen_zero(c = get_body(req, "d"))) { if (ast_strlen_zero(c = get_body(req, "Signal")) && ast_strlen_zero(c = get_body(req, "d"))) {
@ -10891,6 +10920,11 @@ static void handle_request_info(struct sip_pvt *p, struct sip_request *req)
ast_copy_string(buf, c, sizeof(buf)); ast_copy_string(buf, c, sizeof(buf));
} }
if (!ast_strlen_zero((c = get_body(req, "Duration"))))
duration = atoi(c);
if (!duration)
duration = 100; /* 100 ms */
if (!p->owner) { /* not a PBX call */ if (!p->owner) { /* not a PBX call */
transmit_response(p, "481 Call leg/transaction does not exist", req); transmit_response(p, "481 Call leg/transaction does not exist", req);
sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
@ -10928,6 +10962,7 @@ static void handle_request_info(struct sip_pvt *p, struct sip_request *req)
} else if (event < 16) { } else if (event < 16) {
f.subclass = 'A' + (event - 12); f.subclass = 'A' + (event - 12);
} }
f.len = duration;
ast_queue_frame(p->owner, &f); ast_queue_frame(p->owner, &f);
if (sipdebug) if (sipdebug)
ast_verbose("* DTMF-relay event received: %c\n", f.subclass); ast_verbose("* DTMF-relay event received: %c\n", f.subclass);
@ -11428,7 +11463,7 @@ static int func_header_read(struct ast_channel *chan, const char *function, char
} }
ast_channel_lock(chan); ast_channel_lock(chan);
if (chan->tech != &sip_tech) { if (chan->tech != &sip_tech && chan->tech != &sip_tech_info) {
ast_log(LOG_WARNING, "This function can only be used on SIP channels.\n"); ast_log(LOG_WARNING, "This function can only be used on SIP channels.\n");
ast_channel_unlock(chan); ast_channel_unlock(chan);
return -1; return -1;
@ -11603,7 +11638,7 @@ static int function_sipchaninfo_read(struct ast_channel *chan, const char *cmd,
} }
ast_channel_lock(chan); ast_channel_lock(chan);
if (chan->tech != &sip_tech) { if (chan->tech != &sip_tech && chan->tech != &sip_tech_info) {
ast_log(LOG_WARNING, "This function can only be used on SIP channels.\n"); ast_log(LOG_WARNING, "This function can only be used on SIP channels.\n");
ast_channel_unlock(chan); ast_channel_unlock(chan);
return -1; return -1;
@ -11851,7 +11886,7 @@ static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, stru
ast_log(LOG_WARNING, "Ooooh.. no tech! That's REALLY bad\n"); ast_log(LOG_WARNING, "Ooooh.. no tech! That's REALLY bad\n");
break; break;
} }
if (bridgepeer->tech == &sip_tech) { if (bridgepeer->tech == &sip_tech || bridgepeer->tech == &sip_tech_info) {
bridgepvt = (struct sip_pvt*)(bridgepeer->tech_pvt); bridgepvt = (struct sip_pvt*)(bridgepeer->tech_pvt);
if (bridgepvt->udptl) { if (bridgepvt->udptl) {
if (p->t38.state == T38_PEER_REINVITE) { if (p->t38.state == T38_PEER_REINVITE) {
@ -13654,7 +13689,7 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int
if ((bridgepeer = ast_bridged_channel(p->owner))) { if ((bridgepeer = ast_bridged_channel(p->owner))) {
/* We have a bridge, and this is re-invite to switchover to T38 so we send re-invite with T38 SDP, to other side of bridge*/ /* We have a bridge, and this is re-invite to switchover to T38 so we send re-invite with T38 SDP, to other side of bridge*/
/*! XXX: we should also check here does the other side supports t38 at all !!! XXX */ /*! XXX: we should also check here does the other side supports t38 at all !!! XXX */
if (bridgepeer->tech == &sip_tech) { if (bridgepeer->tech == &sip_tech || bridgepeer->tech == &sip_tech_info) {
bridgepvt = (struct sip_pvt*)bridgepeer->tech_pvt; bridgepvt = (struct sip_pvt*)bridgepeer->tech_pvt;
if (bridgepvt->t38.state == T38_DISABLED) { if (bridgepvt->t38.state == T38_DISABLED) {
if (bridgepvt->udptl) { /* If everything is OK with other side's udptl struct */ if (bridgepvt->udptl) { /* If everything is OK with other side's udptl struct */
@ -13708,7 +13743,7 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int
struct ast_channel *bridgepeer = NULL; struct ast_channel *bridgepeer = NULL;
struct sip_pvt *bridgepvt = NULL; struct sip_pvt *bridgepvt = NULL;
if ((bridgepeer = ast_bridged_channel(p->owner))) { if ((bridgepeer = ast_bridged_channel(p->owner))) {
if (bridgepeer->tech == &sip_tech) { if (bridgepeer->tech == &sip_tech || bridgepeer->tech == &sip_tech_info) {
bridgepvt = (struct sip_pvt*)bridgepeer->tech_pvt; bridgepvt = (struct sip_pvt*)bridgepeer->tech_pvt;
/* Does the bridged peer have T38 ? */ /* Does the bridged peer have T38 ? */
if (bridgepvt->t38.state == T38_ENABLED) { if (bridgepvt->t38.state == T38_ENABLED) {
@ -17015,7 +17050,7 @@ static int sip_dtmfmode(struct ast_channel *chan, void *data)
return 0; return 0;
} }
ast_channel_lock(chan); ast_channel_lock(chan);
if (chan->tech != &sip_tech) { if (chan->tech != &sip_tech && chan->tech != &sip_tech_info) {
ast_log(LOG_WARNING, "Call this application only on SIP incoming calls\n"); ast_log(LOG_WARNING, "Call this application only on SIP incoming calls\n");
ast_channel_unlock(chan); ast_channel_unlock(chan);
return 0; return 0;

@ -1028,7 +1028,7 @@ static int skinny_write(struct ast_channel *ast, struct ast_frame *frame);
static int skinny_indicate(struct ast_channel *ast, int ind, const void *data, size_t datalen); static int skinny_indicate(struct ast_channel *ast, int ind, const void *data, size_t datalen);
static int skinny_fixup(struct ast_channel *oldchan, struct ast_channel *newchan); static int skinny_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
static int skinny_senddigit_begin(struct ast_channel *ast, char digit); static int skinny_senddigit_begin(struct ast_channel *ast, char digit);
static int skinny_senddigit_end(struct ast_channel *ast, char digit); static int skinny_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration);
static const struct ast_channel_tech skinny_tech = { static const struct ast_channel_tech skinny_tech = {
.type = "Skinny", .type = "Skinny",
@ -2565,7 +2565,7 @@ static int skinny_senddigit_begin(struct ast_channel *ast, char digit)
return -1; /* Start inband indications */ return -1; /* Start inband indications */
} }
static int skinny_senddigit_end(struct ast_channel *ast, char digit) static int skinny_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration)
{ {
#if 0 #if 0
struct skinny_subchannel *sub = ast->tech_pvt; struct skinny_subchannel *sub = ast->tech_pvt;

@ -724,7 +724,7 @@ static struct zt_chan_conf zt_chan_conf_default(void) {
static struct ast_channel *zt_request(const char *type, int format, void *data, int *cause); static struct ast_channel *zt_request(const char *type, int format, void *data, int *cause);
static int zt_digit_begin(struct ast_channel *ast, char digit); static int zt_digit_begin(struct ast_channel *ast, char digit);
static int zt_digit_end(struct ast_channel *ast, char digit); static int zt_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
static int zt_sendtext(struct ast_channel *c, const char *text); static int zt_sendtext(struct ast_channel *c, const char *text);
static int zt_call(struct ast_channel *ast, char *rdest, int timeout); static int zt_call(struct ast_channel *ast, char *rdest, int timeout);
static int zt_hangup(struct ast_channel *ast); static int zt_hangup(struct ast_channel *ast);
@ -1170,7 +1170,7 @@ out:
return 0; return 0;
} }
static int zt_digit_end(struct ast_channel *chan, char digit) static int zt_digit_end(struct ast_channel *chan, char digit, unsigned int duration)
{ {
struct zt_pvt *pvt; struct zt_pvt *pvt;
int res = 0; int res = 0;

@ -242,7 +242,7 @@ struct ast_channel_tech {
int (* const send_digit_begin)(struct ast_channel *chan, char digit); int (* const send_digit_begin)(struct ast_channel *chan, char digit);
/*! \brief Stop sending a literal DTMF digit */ /*! \brief Stop sending a literal DTMF digit */
int (* const send_digit_end)(struct ast_channel *chan, char digit); int (* const send_digit_end)(struct ast_channel *chan, char digit, unsigned int duration);
/*! \brief Call a given phone number (address, etc), but don't /*! \brief Call a given phone number (address, etc), but don't
take longer than timeout seconds to do so. */ take longer than timeout seconds to do so. */
@ -479,7 +479,8 @@ struct ast_channel {
struct ast_jb jb; /*!< The jitterbuffer state */ struct ast_jb jb; /*!< The jitterbuffer state */
char emulate_dtmf_digit; /*!< Digit being emulated */ char emulate_dtmf_digit; /*!< Digit being emulated */
unsigned int emulate_dtmf_samples; /*!< Number of samples left to emulate DTMF for */ unsigned int emulate_dtmf_duration; /*!< Number of ms left to emulate DTMF for */
struct timeval dtmf_begin_tv; /*!< The time that an in process digit began */
/*! \brief Data stores on the channel */ /*! \brief Data stores on the channel */
AST_LIST_HEAD_NOLOCK(datastores, ast_datastore) datastores; AST_LIST_HEAD_NOLOCK(datastores, ast_datastore) datastores;
@ -526,6 +527,9 @@ enum {
/*! A DTMF_END was received when not IN_DTMF, so the length of the digit is /*! A DTMF_END was received when not IN_DTMF, so the length of the digit is
* currently being emulated */ * currently being emulated */
AST_FLAG_EMULATE_DTMF = (1 << 13), AST_FLAG_EMULATE_DTMF = (1 << 13),
/*! This is set to tell the channel not to generate DTMF begin frames, and
* to instead only generate END frames. */
AST_FLAG_END_DTMF_ONLY = (1 << 14),
}; };
/*! \brief ast_bridge_config flags */ /*! \brief ast_bridge_config flags */
@ -934,14 +938,16 @@ int ast_senddigit(struct ast_channel *chan, char digit);
* \return Returns 0 on success, -1 on failure * \return Returns 0 on success, -1 on failure
*/ */
int ast_senddigit_begin(struct ast_channel *chan, char digit); int ast_senddigit_begin(struct ast_channel *chan, char digit);
/*! \brief Send a DTMF digit to a channel /*! \brief Send a DTMF digit to a channel
* Send a DTMF digit to a channel. * Send a DTMF digit to a channel.
* \param chan channel to act upon * \param chan channel to act upon
* \param digit the DTMF digit to send, encoded in ASCII * \param digit the DTMF digit to send, encoded in ASCII
* \param duration the duration of the digit ending in ms
* \return Returns 0 on success, -1 on failure * \return Returns 0 on success, -1 on failure
*/ */
int ast_senddigit_end(struct ast_channel *chan, char digit); int ast_senddigit_end(struct ast_channel *chan, char digit, unsigned int duration);
/*! \brief Receives a text string from a channel /*! \brief Receives a text string from a channel
* Read a string of text from a channel * Read a string of text from a channel

@ -101,8 +101,8 @@ unsigned long global_fin, global_fout;
AST_THREADSTORAGE(state2str_threadbuf); AST_THREADSTORAGE(state2str_threadbuf);
#define STATE2STR_BUFSIZE 32 #define STATE2STR_BUFSIZE 32
/* XXX 100ms ... this won't work with wideband support */ /*! 100ms */
#define AST_DEFAULT_EMULATE_DTMF_SAMPLES 800 #define AST_DEFAULT_EMULATE_DTMF_DURATION 100
struct chanlist { struct chanlist {
const struct ast_channel_tech *tech; const struct ast_channel_tech *tech;
@ -2003,14 +2003,19 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
if (!ast_test_flag(chan, AST_FLAG_DEFER_DTMF | AST_FLAG_EMULATE_DTMF | AST_FLAG_IN_DTMF) && if (!ast_test_flag(chan, AST_FLAG_DEFER_DTMF | AST_FLAG_EMULATE_DTMF | AST_FLAG_IN_DTMF) &&
!ast_strlen_zero(chan->dtmfq)) { !ast_strlen_zero(chan->dtmfq)) {
/* We have DTMF that has been deferred. Return it now */ /* We have DTMF that has been deferred. Return it now */
chan->dtmff.frametype = AST_FRAME_DTMF_BEGIN;
chan->dtmff.subclass = chan->dtmfq[0]; chan->dtmff.subclass = chan->dtmfq[0];
/* Drop first digit from the buffer */ /* Drop first digit from the buffer */
memmove(chan->dtmfq, chan->dtmfq + 1, sizeof(chan->dtmfq) - 1); memmove(chan->dtmfq, chan->dtmfq + 1, sizeof(chan->dtmfq) - 1);
f = &chan->dtmff; f = &chan->dtmff;
if (ast_test_flag(chan, AST_FLAG_END_DTMF_ONLY))
chan->dtmff.frametype = AST_FRAME_DTMF_END;
else {
chan->dtmff.frametype = AST_FRAME_DTMF_BEGIN;
ast_set_flag(chan, AST_FLAG_EMULATE_DTMF); ast_set_flag(chan, AST_FLAG_EMULATE_DTMF);
chan->emulate_dtmf_digit = f->subclass; chan->emulate_dtmf_digit = f->subclass;
chan->emulate_dtmf_samples = AST_DEFAULT_EMULATE_DTMF_SAMPLES; chan->emulate_dtmf_duration = AST_DEFAULT_EMULATE_DTMF_DURATION;
chan->dtmf_begin_tv = ast_tvnow();
}
goto done; goto done;
} }
@ -2128,38 +2133,47 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
break; break;
case AST_FRAME_DTMF_END: case AST_FRAME_DTMF_END:
ast_log(LOG_DTMF, "DTMF end '%c' received on %s\n", f->subclass, chan->name); ast_log(LOG_DTMF, "DTMF end '%c' received on %s\n", f->subclass, chan->name);
if (ast_test_flag(chan, AST_FLAG_DEFER_DTMF | AST_FLAG_EMULATE_DTMF)) { /* Queue it up if DTMF is deffered, or if DTMF emulation is forced.
* However, only let emulation be forced if the other end cares about BEGIN frames */
if ( ast_test_flag(chan, AST_FLAG_DEFER_DTMF) ||
(ast_test_flag(chan, AST_FLAG_EMULATE_DTMF) && !ast_test_flag(chan, AST_FLAG_END_DTMF_ONLY)) ) {
if (strlen(chan->dtmfq) < sizeof(chan->dtmfq) - 2) if (strlen(chan->dtmfq) < sizeof(chan->dtmfq) - 2)
chan->dtmfq[strlen(chan->dtmfq)] = f->subclass; chan->dtmfq[strlen(chan->dtmfq)] = f->subclass;
else else
ast_log(LOG_WARNING, "Dropping deferred DTMF digits on %s\n", chan->name); ast_log(LOG_WARNING, "Dropping deferred DTMF digits on %s\n", chan->name);
ast_frfree(f); ast_frfree(f);
f = &ast_null_frame; f = &ast_null_frame;
} else if (!ast_test_flag(chan, AST_FLAG_IN_DTMF)) { } else if (!ast_test_flag(chan, AST_FLAG_IN_DTMF | AST_FLAG_END_DTMF_ONLY)) {
f->frametype = AST_FRAME_DTMF_BEGIN; f->frametype = AST_FRAME_DTMF_BEGIN;
ast_set_flag(chan, AST_FLAG_EMULATE_DTMF); ast_set_flag(chan, AST_FLAG_EMULATE_DTMF);
chan->emulate_dtmf_digit = f->subclass; chan->emulate_dtmf_digit = f->subclass;
if (f->samples) chan->dtmf_begin_tv = ast_tvnow();
chan->emulate_dtmf_samples = f->samples; if (f->len)
chan->emulate_dtmf_duration = f->len;
else else
chan->emulate_dtmf_samples = AST_DEFAULT_EMULATE_DTMF_SAMPLES; chan->emulate_dtmf_duration = AST_DEFAULT_EMULATE_DTMF_DURATION;
} else } else {
ast_clear_flag(chan, AST_FLAG_IN_DTMF); ast_clear_flag(chan, AST_FLAG_IN_DTMF);
if (!f->len)
f->len = ast_tvdiff_ms(chan->dtmf_begin_tv, ast_tvnow());
}
break; break;
case AST_FRAME_DTMF_BEGIN: case AST_FRAME_DTMF_BEGIN:
ast_log(LOG_DTMF, "DTMF begin '%c' received on %s\n", f->subclass, chan->name); ast_log(LOG_DTMF, "DTMF begin '%c' received on %s\n", f->subclass, chan->name);
if (ast_test_flag(chan, AST_FLAG_DEFER_DTMF)) { if (ast_test_flag(chan, AST_FLAG_DEFER_DTMF | AST_FLAG_END_DTMF_ONLY)) {
ast_frfree(f); ast_frfree(f);
f = &ast_null_frame; f = &ast_null_frame;
} else } else {
ast_set_flag(chan, AST_FLAG_IN_DTMF); ast_set_flag(chan, AST_FLAG_IN_DTMF);
chan->dtmf_begin_tv = ast_tvnow();
}
break; break;
case AST_FRAME_VOICE: case AST_FRAME_VOICE:
/* The EMULATE_DTMF flag must be cleared here as opposed to when the samples /* The EMULATE_DTMF flag must be cleared here as opposed to when the samples
* first get to zero, because we want to make sure we pass at least one * first get to zero, because we want to make sure we pass at least one
* voice frame through before starting the next digit, to ensure a gap * voice frame through before starting the next digit, to ensure a gap
* between DTMF digits. */ * between DTMF digits. */
if (ast_test_flag(chan, AST_FLAG_EMULATE_DTMF) && !chan->emulate_dtmf_samples) { if (ast_test_flag(chan, AST_FLAG_EMULATE_DTMF) && !chan->emulate_dtmf_duration) {
ast_clear_flag(chan, AST_FLAG_EMULATE_DTMF); ast_clear_flag(chan, AST_FLAG_EMULATE_DTMF);
chan->emulate_dtmf_digit = 0; chan->emulate_dtmf_digit = 0;
} }
@ -2168,12 +2182,12 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
ast_frfree(f); ast_frfree(f);
f = &ast_null_frame; f = &ast_null_frame;
} else if (ast_test_flag(chan, AST_FLAG_EMULATE_DTMF)) { } else if (ast_test_flag(chan, AST_FLAG_EMULATE_DTMF)) {
if (f->samples >= chan->emulate_dtmf_samples) { if ((f->samples / 8) >= chan->emulate_dtmf_duration) { /* XXX 8kHz */
chan->emulate_dtmf_samples = 0; chan->emulate_dtmf_duration = 0;
f->frametype = AST_FRAME_DTMF_END; f->frametype = AST_FRAME_DTMF_END;
f->subclass = chan->emulate_dtmf_digit; f->subclass = chan->emulate_dtmf_digit;
} else { } else {
chan->emulate_dtmf_samples -= f->samples; chan->emulate_dtmf_duration -= f->samples / 8; /* XXX 8kHz */
ast_frfree(f); ast_frfree(f);
f = &ast_null_frame; f = &ast_null_frame;
} }
@ -2448,12 +2462,12 @@ int ast_senddigit_begin(struct ast_channel *chan, char digit)
return 0; return 0;
} }
int ast_senddigit_end(struct ast_channel *chan, char digit) int ast_senddigit_end(struct ast_channel *chan, char digit, unsigned int duration)
{ {
int res = -1; int res = -1;
if (chan->tech->send_digit_end) if (chan->tech->send_digit_end)
res = chan->tech->send_digit_end(chan, digit); res = chan->tech->send_digit_end(chan, digit, duration);
if (res && chan->generator) if (res && chan->generator)
ast_playtones_stop(chan); ast_playtones_stop(chan);
@ -2463,11 +2477,12 @@ int ast_senddigit_end(struct ast_channel *chan, char digit)
int ast_senddigit(struct ast_channel *chan, char digit) int ast_senddigit(struct ast_channel *chan, char digit)
{ {
if (!ast_test_flag(chan, AST_FLAG_END_DTMF_ONLY)) {
ast_senddigit_begin(chan, digit); ast_senddigit_begin(chan, digit);
ast_safe_sleep(chan, 100); /* XXX 100ms ... probably should be configurable */ ast_safe_sleep(chan, 100); /* XXX 100ms ... probably should be configurable */
}
return ast_senddigit_end(chan, digit); return ast_senddigit_end(chan, digit, 100);
} }
int ast_prod(struct ast_channel *chan) int ast_prod(struct ast_channel *chan)
@ -2545,7 +2560,7 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
case AST_FRAME_DTMF_END: case AST_FRAME_DTMF_END:
ast_clear_flag(chan, AST_FLAG_BLOCKING); ast_clear_flag(chan, AST_FLAG_BLOCKING);
ast_channel_unlock(chan); ast_channel_unlock(chan);
res = ast_senddigit_end(chan, fr->subclass); res = ast_senddigit_end(chan, fr->subclass, fr->len);
ast_channel_lock(chan); ast_channel_lock(chan);
CHECK_BLOCKING(chan); CHECK_BLOCKING(chan);
break; break;
@ -3841,6 +3856,11 @@ enum ast_bridge_result ast_channel_bridge(struct ast_channel *c0, struct ast_cha
nexteventts = ast_tvsub(nexteventts, ast_samp2tv(config->play_warning, 1000)); nexteventts = ast_tvsub(nexteventts, ast_samp2tv(config->play_warning, 1000));
} }
if (!c0->tech->send_digit_begin)
ast_set_flag(c1, AST_FLAG_END_DTMF_ONLY);
if (!c1->tech->send_digit_begin)
ast_set_flag(c0, AST_FLAG_END_DTMF_ONLY);
for (/* ever */;;) { for (/* ever */;;) {
struct timeval now = { 0, }; struct timeval now = { 0, };
int to; int to;
@ -3994,6 +4014,9 @@ enum ast_bridge_result ast_channel_bridge(struct ast_channel *c0, struct ast_cha
break; break;
} }
ast_clear_flag(c0, AST_FLAG_END_DTMF_ONLY);
ast_clear_flag(c1, AST_FLAG_END_DTMF_ONLY);
c0->_bridge = NULL; c0->_bridge = NULL;
c1->_bridge = NULL; c1->_bridge = NULL;

@ -501,11 +501,9 @@ struct ast_frame *ast_frdup(const struct ast_frame *f)
strcpy((char *)out->src, f->src); strcpy((char *)out->src, f->src);
} }
out->has_timing_info = f->has_timing_info; out->has_timing_info = f->has_timing_info;
if (f->has_timing_info) {
out->ts = f->ts; out->ts = f->ts;
out->len = f->len; out->len = f->len;
out->seqno = f->seqno; out->seqno = f->seqno;
}
return out; return out;
} }

@ -140,7 +140,7 @@ struct ast_rtp {
char resp; char resp;
unsigned int lasteventendseqn; unsigned int lasteventendseqn;
int dtmfcount; int dtmfcount;
unsigned int dtmfduration; unsigned int dtmfsamples;
/* DTMF Transmission Variables */ /* DTMF Transmission Variables */
unsigned int lastdigitts; unsigned int lastdigitts;
char sending_digit; /* boolean - are we sending digits */ char sending_digit; /* boolean - are we sending digits */
@ -431,7 +431,7 @@ static int stun_handle_packet(int s, struct sockaddr_in *src, unsigned char *dat
if (len < sizeof(struct stun_header)) { if (len < sizeof(struct stun_header)) {
if (option_debug) if (option_debug)
ast_log(LOG_DEBUG, "Runt STUN packet (only %d, wanting at least %d)\n", (int) len, sizeof(struct stun_header)); ast_log(LOG_DEBUG, "Runt STUN packet (only %d, wanting at least %d)\n", (int) len, (int) sizeof(struct stun_header));
return -1; return -1;
} }
if (stundebug) if (stundebug)
@ -446,7 +446,7 @@ static int stun_handle_packet(int s, struct sockaddr_in *src, unsigned char *dat
while(len) { while(len) {
if (len < sizeof(struct stun_attr)) { if (len < sizeof(struct stun_attr)) {
if (option_debug) if (option_debug)
ast_log(LOG_DEBUG, "Runt Attribute (got %d, expecting %d)\n", (int)len, sizeof(struct stun_attr)); ast_log(LOG_DEBUG, "Runt Attribute (got %d, expecting %d)\n", (int)len, (int) sizeof(struct stun_attr));
break; break;
} }
attr = (struct stun_attr *)data; attr = (struct stun_attr *)data;
@ -619,7 +619,7 @@ static struct ast_frame *send_dtmf(struct ast_rtp *rtp, enum ast_frame_type type
if (option_debug) if (option_debug)
ast_log(LOG_DEBUG, "Ignore potential DTMF echo from '%s'\n", ast_inet_ntoa(rtp->them.sin_addr)); ast_log(LOG_DEBUG, "Ignore potential DTMF echo from '%s'\n", ast_inet_ntoa(rtp->them.sin_addr));
rtp->resp = 0; rtp->resp = 0;
rtp->dtmfduration = 0; rtp->dtmfsamples = 0;
return &ast_null_frame; return &ast_null_frame;
} }
if (option_debug) if (option_debug)
@ -732,14 +732,14 @@ static struct ast_frame *process_cisco_dtmf(struct ast_rtp *rtp, unsigned char *
/* Why we should care on DTMF compensation at reception? */ /* Why we should care on DTMF compensation at reception? */
if (!ast_test_flag(rtp, FLAG_DTMF_COMPENSATE)) { if (!ast_test_flag(rtp, FLAG_DTMF_COMPENSATE)) {
f = send_dtmf(rtp, AST_FRAME_DTMF_BEGIN); f = send_dtmf(rtp, AST_FRAME_DTMF_BEGIN);
rtp->dtmfduration = 0; rtp->dtmfsamples = 0;
} }
} else if ((rtp->resp == resp) && !power) { } else if ((rtp->resp == resp) && !power) {
f = send_dtmf(rtp, AST_FRAME_DTMF_END); f = send_dtmf(rtp, AST_FRAME_DTMF_END);
f->samples = rtp->dtmfduration * 8; f->samples = rtp->dtmfsamples * 8;
rtp->resp = 0; rtp->resp = 0;
} else if (rtp->resp == resp) } else if (rtp->resp == resp)
rtp->dtmfduration += 20 * 8; rtp->dtmfsamples += 20 * 8;
rtp->dtmfcount = dtmftimeout; rtp->dtmfcount = dtmftimeout;
return f; return f;
} }
@ -759,18 +759,18 @@ static struct ast_frame *process_rfc2833(struct ast_rtp *rtp, unsigned char *dat
{ {
unsigned int event; unsigned int event;
unsigned int event_end; unsigned int event_end;
unsigned int duration; unsigned int samples;
char resp = 0; char resp = 0;
struct ast_frame *f = NULL; struct ast_frame *f = NULL;
/* Figure out event, event end, and duration */ /* Figure out event, event end, and samples */
event = ntohl(*((unsigned int *)(data))); event = ntohl(*((unsigned int *)(data)));
event >>= 24; event >>= 24;
event_end = ntohl(*((unsigned int *)(data))); event_end = ntohl(*((unsigned int *)(data)));
event_end <<= 8; event_end <<= 8;
event_end >>= 24; event_end >>= 24;
duration = ntohl(*((unsigned int *)(data))); samples = ntohl(*((unsigned int *)(data)));
duration &= 0xFFFF; samples &= 0xFFFF;
/* Print out debug if turned on */ /* Print out debug if turned on */
if (rtpdebug || option_debug > 2) if (rtpdebug || option_debug > 2)
@ -795,19 +795,19 @@ static struct ast_frame *process_rfc2833(struct ast_rtp *rtp, unsigned char *dat
f = send_dtmf(rtp, AST_FRAME_DTMF_BEGIN); f = send_dtmf(rtp, AST_FRAME_DTMF_BEGIN);
} else if (event_end & 0x80 && rtp->lasteventendseqn != seqno && rtp->resp) { } else if (event_end & 0x80 && rtp->lasteventendseqn != seqno && rtp->resp) {
f = send_dtmf(rtp, AST_FRAME_DTMF_END); f = send_dtmf(rtp, AST_FRAME_DTMF_END);
f->samples = duration; f->len = ast_tvdiff_ms(ast_samp2tv(samples, 8000), ast_tv(0, 0)); /* XXX hard coded 8kHz */
rtp->resp = 0; rtp->resp = 0;
rtp->lasteventendseqn = seqno; rtp->lasteventendseqn = seqno;
} else if (ast_test_flag(rtp, FLAG_DTMF_COMPENSATE) && event_end & 0x80 && rtp->lasteventendseqn != seqno) { } else if (ast_test_flag(rtp, FLAG_DTMF_COMPENSATE) && event_end & 0x80 && rtp->lasteventendseqn != seqno) {
rtp->resp = resp; rtp->resp = resp;
f = send_dtmf(rtp, AST_FRAME_DTMF_END); f = send_dtmf(rtp, AST_FRAME_DTMF_END);
f->samples = duration; f->len = ast_tvdiff_ms(ast_samp2tv(samples, 8000), ast_tv(0, 0)); /* XXX hard coded 8kHz */
rtp->resp = 0; rtp->resp = 0;
rtp->lasteventendseqn = seqno; rtp->lasteventendseqn = seqno;
} }
rtp->dtmfcount = dtmftimeout; rtp->dtmfcount = dtmftimeout;
rtp->dtmfduration = duration; rtp->dtmfsamples = samples;
return f; return f;
} }
@ -2065,7 +2065,7 @@ void ast_rtp_reset(struct ast_rtp *rtp)
rtp->lasttxformat = 0; rtp->lasttxformat = 0;
rtp->lastrxformat = 0; rtp->lastrxformat = 0;
rtp->dtmfcount = 0; rtp->dtmfcount = 0;
rtp->dtmfduration = 0; rtp->dtmfsamples = 0;
rtp->seqno = 0; rtp->seqno = 0;
rtp->rxseqno = 0; rtp->rxseqno = 0;
} }
@ -3281,6 +3281,22 @@ enum ast_bridge_result ast_rtp_bridge(struct ast_channel *c0, struct ast_channel
audio_p1_res = AST_RTP_TRY_PARTIAL; audio_p1_res = AST_RTP_TRY_PARTIAL;
} }
/* If both sides are not using the same method of DTMF transmission
* (ie: one is RFC2833, other is INFO... then we can not do direct media.
* --------------------------------------------------
* | DTMF Mode | HAS_DTMF | Accepts Begin Frames |
* |-----------|------------|-----------------------|
* | Inband | False | True |
* | RFC2833 | True | True |
* | SIP Info | False | False |
* --------------------------------------------------
*/
if ( (ast_test_flag(p0, FLAG_HAS_DTMF) != ast_test_flag(p1, FLAG_HAS_DTMF)) ||
(!c0->tech->send_digit_begin != !c1->tech->send_digit_begin)) {
audio_p0_res = AST_RTP_TRY_PARTIAL;
audio_p1_res = AST_RTP_TRY_PARTIAL;
}
/* Get codecs from both sides */ /* Get codecs from both sides */
codec0 = pr0->get_codec ? pr0->get_codec(c0) : 0; codec0 = pr0->get_codec ? pr0->get_codec(c0) : 0;
codec1 = pr1->get_codec ? pr1->get_codec(c1) : 0; codec1 = pr1->get_codec ? pr1->get_codec(c1) : 0;

Loading…
Cancel
Save