From c6977b9983db4f58446bfbc65a5b028cda8244ee Mon Sep 17 00:00:00 2001 From: Joshua Colp <jcolp@digium.com> Date: Thu, 31 Aug 2006 01:59:02 +0000 Subject: [PATCH] Merge in VLDTMF support with Zaptel/Core done by the ever great Darumkilla Russell Bryant and the RTP portion done by myself, Muffinlicious Joshua Colp. This has gone through so many discussions/revisions it's not funny but we finally have it! git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@41507 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- UPGRADE.txt | 6 + apps/app_echo.c | 8 +- channels/chan_agent.c | 30 +++-- channels/chan_alsa.c | 2 +- channels/chan_features.c | 26 +++- channels/chan_h323.c | 14 +- channels/chan_iax2.c | 15 ++- channels/chan_jingle.c | 14 +- channels/chan_local.c | 27 +++- channels/chan_mgcp.c | 16 ++- channels/chan_misdn.c | 14 +- channels/chan_oss.c | 13 +- channels/chan_phone.c | 19 ++- channels/chan_sip.c | 46 ++++++- channels/chan_skinny.c | 15 ++- channels/chan_vpb.cc | 55 ++++---- channels/chan_zap.c | 155 ++++++++++++++------- channels/iax2-parser.c | 9 +- configs/sip.conf.sample | 9 +- include/asterisk/channel.h | 40 +++--- include/asterisk/frame.h | 66 ++++----- include/asterisk/rtp.h | 7 +- main/app.c | 11 +- main/channel.c | 133 +++++++++++++----- main/dsp.c | 63 ++++----- main/file.c | 7 +- main/rtp.c | 269 +++++++++++++++++++++++-------------- res/res_agi.c | 2 + res/res_features.c | 6 +- 29 files changed, 738 insertions(+), 359 deletions(-) diff --git a/UPGRADE.txt b/UPGRADE.txt index 9a75748a87..f0e7246b70 100644 --- a/UPGRADE.txt +++ b/UPGRADE.txt @@ -303,6 +303,12 @@ The SIP channel: option in sip.conf is removed to osp.conf as authpolicy. allowguest option in sip.conf cannot be set as osp anymore. +* The Asterisk RTP stack has been changed in regards to RFC2833 reception + and transmission. Packets will now be sent with proper duration instead of all + at once. If you are receiving calls from a pre-1.4 Asterisk installation you + will want to turn on the rfc2833compensate option. Without this option your + DTMF reception may act poorly. + * The $SIPUSERAGENT dialplan variable is deprecated and will be removed in coming versions of Asterisk. Please use the dialplan function SIPCHANINFO(useragent) instead. diff --git a/apps/app_echo.c b/apps/app_echo.c index 19a86382ec..4b2c940562 100644 --- a/apps/app_echo.c +++ b/apps/app_echo.c @@ -71,19 +71,13 @@ static int echo_exec(struct ast_channel *chan, void *data) f->delivery.tv_usec = 0; switch (f->frametype) { case AST_FRAME_DTMF: - case AST_FRAME_DTMF_END: if (f->subclass == '#') { res = 0; ast_frfree(f); goto end; } /* fall through */ - case AST_FRAME_DTMF_BEGIN: - case AST_FRAME_VOICE: - case AST_FRAME_VIDEO: - case AST_FRAME_TEXT: - case AST_FRAME_HTML: - case AST_FRAME_IMAGE: + default: if (ast_write(chan, f)) { ast_frfree(f); goto end; diff --git a/channels/chan_agent.c b/channels/chan_agent.c index 0a9b8906ac..4eeffe99c8 100644 --- a/channels/chan_agent.c +++ b/channels/chan_agent.c @@ -238,7 +238,8 @@ static AST_LIST_HEAD_STATIC(agents, agent_pvt); /*!< Holds the list of agents (l static struct ast_channel *agent_request(const char *type, int format, void *data, int *cause); 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 int agent_digit(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_call(struct ast_channel *ast, char *dest, int timeout); static int agent_hangup(struct ast_channel *ast); static int agent_answer(struct ast_channel *ast); @@ -258,7 +259,8 @@ static const struct ast_channel_tech agent_tech = { .capabilities = -1, .requester = agent_request, .devicestate = agent_devicestate, - .send_digit = agent_digit, + .send_digit_begin = agent_digit_begin, + .send_digit_end = agent_digit_end, .call = agent_call, .hangup = agent_hangup, .answer = agent_answer, @@ -491,7 +493,8 @@ static struct ast_frame *agent_read(struct ast_channel *ast) } } break; - case AST_FRAME_DTMF: + case AST_FRAME_DTMF_BEGIN: + case AST_FRAME_DTMF_END: if (!p->acknowledged && (f->subclass == '#')) { if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "%s acknowledged\n", p->chan->name); @@ -511,7 +514,9 @@ static struct ast_frame *agent_read(struct ast_channel *ast) ast_frfree(f); f = &ast_null_frame; } - break; + default: + /* pass everything else on through */ + break; } } @@ -603,15 +608,22 @@ static int agent_indicate(struct ast_channel *ast, int condition, const void *da return res; } -static int agent_digit(struct ast_channel *ast, char digit) +static int agent_digit_begin(struct ast_channel *ast, char digit) { struct agent_pvt *p = ast->tech_pvt; int res = -1; ast_mutex_lock(&p->lock); - if (p->chan) - res = p->chan->tech->send_digit(p->chan, digit); - else - res = 0; + ast_senddigit_begin(p->chan, digit); + ast_mutex_unlock(&p->lock); + return res; +} + +static int agent_digit_end(struct ast_channel *ast, char digit) +{ + struct agent_pvt *p = ast->tech_pvt; + int res = -1; + ast_mutex_lock(&p->lock); + ast_senddigit_end(p->chan, digit); ast_mutex_unlock(&p->lock); return res; } diff --git a/channels/chan_alsa.c b/channels/chan_alsa.c index 5a4d6276f6..4237ba800e 100644 --- a/channels/chan_alsa.c +++ b/channels/chan_alsa.c @@ -203,7 +203,7 @@ static const struct ast_channel_tech alsa_tech = { .description = tdesc, .capabilities = AST_FORMAT_SLINEAR, .requester = alsa_request, - .send_digit = alsa_digit, + .send_digit_end = alsa_digit, .send_text = alsa_text, .hangup = alsa_hangup, .answer = alsa_answer, diff --git a/channels/chan_features.c b/channels/chan_features.c index d1feadbea9..66b6da3e37 100644 --- a/channels/chan_features.c +++ b/channels/chan_features.c @@ -95,7 +95,8 @@ static AST_LIST_HEAD_STATIC(features, feature_pvt); #define SUB_THREEWAY 2 /* Three-way call */ static struct ast_channel *features_request(const char *type, int format, void *data, int *cause); -static int features_digit(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_call(struct ast_channel *ast, char *dest, int timeout); static int features_hangup(struct ast_channel *ast); static int features_answer(struct ast_channel *ast); @@ -109,7 +110,8 @@ static const struct ast_channel_tech features_tech = { .description = tdesc, .capabilities = -1, .requester = features_request, - .send_digit = features_digit, + .send_digit_begin = features_digit_begin, + .send_digit_end = features_digit_end, .call = features_call, .hangup = features_hangup, .answer = features_answer, @@ -300,7 +302,7 @@ static int features_indicate(struct ast_channel *ast, int condition, const void return res; } -static int features_digit(struct ast_channel *ast, char digit) +static int features_digit_begin(struct ast_channel *ast, char digit) { struct feature_pvt *p = ast->tech_pvt; int res = -1; @@ -310,7 +312,23 @@ static int features_digit(struct ast_channel *ast, char digit) ast_mutex_lock(&p->lock); x = indexof(p, ast, 0); if (!x && p->subchan) - res = ast_senddigit(p->subchan, digit); + res = ast_senddigit_begin(p->subchan, digit); + ast_mutex_unlock(&p->lock); + + return res; +} + +static int features_digit_end(struct ast_channel *ast, char digit) +{ + struct feature_pvt *p = ast->tech_pvt; + int res = -1; + int x; + + /* Queue up a frame representing the indication as a control frame */ + ast_mutex_lock(&p->lock); + x = indexof(p, ast, 0); + if (!x && p->subchan) + res = ast_senddigit_end(p->subchan, digit); ast_mutex_unlock(&p->lock); return res; } diff --git a/channels/chan_h323.c b/channels/chan_h323.c index 6b956e517a..dc498a2d9d 100644 --- a/channels/chan_h323.c +++ b/channels/chan_h323.c @@ -220,7 +220,8 @@ static int restart_monitor(void); static int h323_do_reload(void); static struct ast_channel *oh323_request(const char *type, int format, void *data, int *cause); -static int oh323_digit(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_call(struct ast_channel *c, char *dest, int timeout); static int oh323_hangup(struct ast_channel *c); static int oh323_answer(struct ast_channel *c); @@ -235,7 +236,8 @@ static const struct ast_channel_tech oh323_tech = { .capabilities = ((AST_FORMAT_MAX_AUDIO << 1) - 1), .properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER, .requester = oh323_request, - .send_digit = oh323_digit, + .send_digit_begin = oh323_digit_begin, + .send_digit_end = oh323_digit_end, .call = oh323_call, .hangup = oh323_hangup, .answer = oh323_answer, @@ -381,11 +383,17 @@ static void oh323_destroy(struct oh323_pvt *pvt) ast_mutex_unlock(&iflock); } +static int oh323_digit_begin(struct ast_channel *chan, char digit) +{ + /* XXX Implement me, plz, kthx */ + return 0; +} + /** * Send (play) the specified digit to the channel. * */ -static int oh323_digit(struct ast_channel *c, char digit) +static int oh323_digit_end(struct ast_channel *c, char digit) { struct oh323_pvt *pvt = (struct oh323_pvt *) c->tech_pvt; char *token; diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c index 1d404c3fbf..60841e0ae4 100644 --- a/channels/chan_iax2.c +++ b/channels/chan_iax2.c @@ -776,7 +776,8 @@ static int expire_registry(void *data); static int iax2_answer(struct ast_channel *c); static int iax2_call(struct ast_channel *c, char *dest, int timeout); static int iax2_devicestate(void *data); -static int iax2_digit(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_do_register(struct iax2_registry *reg); static int iax2_fixup(struct ast_channel *oldchannel, struct ast_channel *newchan); static int iax2_hangup(struct ast_channel *c); @@ -809,7 +810,8 @@ static const struct ast_channel_tech iax2_tech = { .properties = AST_CHAN_TP_WANTSJITTER, .requester = iax2_request, .devicestate = iax2_devicestate, - .send_digit = iax2_digit, + .send_digit_begin = iax2_digit_begin, + .send_digit_end = iax2_digit_end, .send_text = iax2_sendtext, .send_image = iax2_sendimage, .send_html = iax2_sendhtml, @@ -2379,9 +2381,14 @@ static int iax2_transmit(struct iax_frame *fr) -static int iax2_digit(struct ast_channel *c, char digit) +static int iax2_digit_begin(struct ast_channel *c, char digit) { - return send_command_locked(PTR_TO_CALLNO(c->tech_pvt), AST_FRAME_DTMF, 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) +{ + return send_command_locked(PTR_TO_CALLNO(c->tech_pvt), AST_FRAME_DTMF_END, digit, 0, NULL, 0, -1); } static int iax2_sendtext(struct ast_channel *c, const char *text) diff --git a/channels/chan_jingle.c b/channels/chan_jingle.c index b3d7ea68a1..a44b42085b 100644 --- a/channels/chan_jingle.c +++ b/channels/chan_jingle.c @@ -171,7 +171,8 @@ AST_MUTEX_DEFINE_STATIC(jinglelock); /*!< Protect the interface list (of jingle_ /* Forward declarations */ static struct ast_channel *jingle_request(const char *type, int format, void *data, int *cause); -static int jingle_digit(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_call(struct ast_channel *ast, char *dest, int timeout); static int jingle_hangup(struct ast_channel *ast); static int jingle_answer(struct ast_channel *ast); @@ -194,7 +195,8 @@ static const struct ast_channel_tech jingle_tech = { .description = "Jingle Channel Driver", .capabilities = ((AST_FORMAT_MAX_AUDIO << 1) - 1), .requester = jingle_request, - .send_digit = jingle_digit, + .send_digit_begin = jingle_digit_begin, + .send_digit_end = jingle_digit_end, .bridge = ast_rtp_bridge, .call = jingle_call, .hangup = jingle_hangup, @@ -1181,7 +1183,13 @@ static int jingle_indicate(struct ast_channel *ast, int condition, const void *d return res; } -static int jingle_digit(struct ast_channel *ast, char digit) +static int jingle_digit_begin(struct ast_channel *chan, char digit) +{ + /* 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 *client = p->parent; diff --git a/channels/chan_local.c b/channels/chan_local.c index a9b0582f9b..fec9c6273f 100644 --- a/channels/chan_local.c +++ b/channels/chan_local.c @@ -67,7 +67,8 @@ static const char tdesc[] = "Local Proxy Channel Driver"; #define IS_OUTBOUND(a,b) (a == b->chan ? 1 : 0) static struct ast_channel *local_request(const char *type, int format, void *data, int *cause); -static int local_digit(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_call(struct ast_channel *ast, char *dest, int timeout); static int local_hangup(struct ast_channel *ast); static int local_answer(struct ast_channel *ast); @@ -85,7 +86,8 @@ static const struct ast_channel_tech local_tech = { .description = tdesc, .capabilities = -1, .requester = local_request, - .send_digit = local_digit, + .send_digit_begin = local_digit_begin, + .send_digit_end = local_digit_end, .call = local_call, .hangup = local_hangup, .answer = local_answer, @@ -327,11 +329,11 @@ static int local_indicate(struct ast_channel *ast, int condition, const void *da return res; } -static int local_digit(struct ast_channel *ast, char digit) +static int local_digit_begin(struct ast_channel *ast, char digit) { struct local_pvt *p = ast->tech_pvt; int res = -1; - struct ast_frame f = { AST_FRAME_DTMF, }; + struct ast_frame f = { AST_FRAME_DTMF_BEGIN, }; int isoutbound; ast_mutex_lock(&p->lock); @@ -339,6 +341,23 @@ static int local_digit(struct ast_channel *ast, char digit) f.subclass = digit; res = local_queue_frame(p, isoutbound, &f, ast); ast_mutex_unlock(&p->lock); + + return res; +} + +static int local_digit_end(struct ast_channel *ast, char digit) +{ + struct local_pvt *p = ast->tech_pvt; + int res = -1; + struct ast_frame f = { AST_FRAME_DTMF_END, }; + int isoutbound; + + ast_mutex_lock(&p->lock); + isoutbound = IS_OUTBOUND(ast, p); + f.subclass = digit; + res = local_queue_frame(p, isoutbound, &f, ast); + ast_mutex_unlock(&p->lock); + return res; } diff --git a/channels/chan_mgcp.c b/channels/chan_mgcp.c index c77b8a61e5..05ab81edfd 100644 --- a/channels/chan_mgcp.c +++ b/channels/chan_mgcp.c @@ -427,7 +427,8 @@ static struct ast_frame *mgcp_read(struct ast_channel *ast); 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_fixup(struct ast_channel *oldchan, struct ast_channel *newchan); -static int mgcp_senddigit(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_devicestate(void *data); static const struct ast_channel_tech mgcp_tech = { @@ -444,7 +445,8 @@ static const struct ast_channel_tech mgcp_tech = { .write = mgcp_write, .indicate = mgcp_indicate, .fixup = mgcp_fixup, - .send_digit = mgcp_senddigit, + .send_digit_begin = mgcp_senddigit_begin, + .send_digit_end = mgcp_senddigit_end, .bridge = ast_rtp_bridge, }; @@ -1221,7 +1223,13 @@ static int mgcp_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) return 0; } -static int mgcp_senddigit(struct ast_channel *ast, char digit) +static int mgcp_senddigit_begin(struct ast_channel *ast, char digit) +{ + /* Let asterisk play inband indications */ + return -1; +} + +static int mgcp_senddigit_end(struct ast_channel *ast, char digit) { struct mgcp_subchannel *sub = ast->tech_pvt; char tmp[4]; @@ -1233,7 +1241,7 @@ static int mgcp_senddigit(struct ast_channel *ast, char digit) ast_mutex_lock(&sub->lock); transmit_notify_request(sub, tmp); ast_mutex_unlock(&sub->lock); - return -1; + return -1; /* Return non-zero so that Asterisk will stop the inband indications */ } /*! diff --git a/channels/chan_misdn.c b/channels/chan_misdn.c index 9631a8cb53..201d38c39c 100644 --- a/channels/chan_misdn.c +++ b/channels/chan_misdn.c @@ -2006,7 +2006,13 @@ static int misdn_answer(struct ast_channel *ast) return 0; } -static int misdn_digit(struct ast_channel *ast, char digit ) +static int misdn_digit_begin(struct ast_channel *chan, char digit) +{ + /* XXX Modify this callback to support Asterisk controlling the length of DTMF */ + return 0; +} + +static int misdn_digit_end(struct ast_channel *ast, char digit ) { struct chan_list *p; @@ -2983,7 +2989,8 @@ static struct ast_channel_tech misdn_tech = { .description="Channel driver for mISDN Support (Bri/Pri)", .capabilities= AST_FORMAT_ALAW , .requester=misdn_request, - .send_digit=misdn_digit, + .send_digit_begin=misdn_digit_begin, + .send_digit_end=misdn_digit_end, .call=misdn_call, .bridge=misdn_bridge, .hangup=misdn_hangup, @@ -3001,7 +3008,8 @@ static struct ast_channel_tech misdn_tech_wo_bridge = { .description="Channel driver for mISDN Support (Bri/Pri)", .capabilities=AST_FORMAT_ALAW , .requester=misdn_request, - .send_digit=misdn_digit, + .send_digit_begin=misdn_digit_begin, + .send_digit_end=misdn_digit_end, .call=misdn_call, .hangup=misdn_hangup, .answer=misdn_answer, diff --git a/channels/chan_oss.c b/channels/chan_oss.c index b2981463c6..e40aacb0f2 100644 --- a/channels/chan_oss.c +++ b/channels/chan_oss.c @@ -407,7 +407,8 @@ static int setformat(struct chan_oss_pvt *o, int mode); static struct ast_channel *oss_request(const char *type, int format, void *data , int *cause); -static int oss_digit(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_text(struct ast_channel *c, const char *text); static int oss_hangup(struct ast_channel *c); static int oss_answer(struct ast_channel *c); @@ -423,7 +424,8 @@ static const struct ast_channel_tech oss_tech = { .description = tdesc, .capabilities = AST_FORMAT_SLINEAR, .requester = oss_request, - .send_digit = oss_digit, + .send_digit_begin = oss_digit_begin, + .send_digit_end = oss_digit_end, .send_text = oss_text, .hangup = oss_hangup, .answer = oss_answer, @@ -757,7 +759,12 @@ static int setformat(struct chan_oss_pvt *o, int mode) /* * some of the standard methods supported by channels. */ -static int oss_digit(struct ast_channel *c, char digit) +static int oss_digit_begin(struct ast_channel *c, char digit) +{ + return 0; +} + +static int oss_digit_end(struct ast_channel *c, char digit) { /* no better use for received digits than print them */ ast_verbose( " << Console Received digit %c >> \n", digit); diff --git a/channels/chan_phone.c b/channels/chan_phone.c index 87e971abbf..bb31ebae6a 100644 --- a/channels/chan_phone.c +++ b/channels/chan_phone.c @@ -161,7 +161,8 @@ static char cid_num[AST_MAX_EXTENSION]; static char cid_name[AST_MAX_EXTENSION]; static struct ast_channel *phone_request(const char *type, int format, void *data, int *cause); -static int phone_digit(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_call(struct ast_channel *ast, char *dest, int timeout); static int phone_hangup(struct ast_channel *ast); static int phone_answer(struct ast_channel *ast); @@ -177,7 +178,8 @@ static const struct ast_channel_tech phone_tech = { .description = tdesc, .capabilities = AST_FORMAT_G723_1 | AST_FORMAT_SLINEAR | AST_FORMAT_ULAW, .requester = phone_request, - .send_digit = phone_digit, + .send_digit_begin = phone_digit_begin, + .send_digit_end = phone_digit_end, .call = phone_call, .hangup = phone_hangup, .answer = phone_answer, @@ -192,7 +194,8 @@ static struct ast_channel_tech phone_tech_fxs = { .type = "Phone", .description = tdesc, .requester = phone_request, - .send_digit = phone_digit, + .send_digit_begin = phone_digit_begin, + .send_digit_end = phone_digit_end, .call = phone_call, .hangup = phone_hangup, .answer = phone_answer, @@ -240,7 +243,13 @@ static int phone_fixup(struct ast_channel *old, struct ast_channel *new) return 0; } -static int phone_digit(struct ast_channel *ast, char digit) +static int phone_digit_begin(struct ast_channel *chan, char digit) +{ + /* XXX Modify this callback to let Asterisk support controlling the length of DTMF */ + return 0; +} + +static int phone_digit_end(struct ast_channel *ast, char digit) { struct phone_pvt *p; int outdigit; @@ -329,7 +338,7 @@ static int phone_call(struct ast_channel *ast, char *dest, int timeout) { digit++; while (*digit) - phone_digit(ast, *digit++); + phone_digit_end(ast, *digit++); } } diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 948dc3bc63..aed8b5828f 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -743,9 +743,10 @@ struct sip_auth { #define SIP_PAGE2_CALL_ONHOLD (3 << 23) /*!< Call states */ #define SIP_PAGE2_CALL_ONHOLD_ONEDIR (1 << 23) /*!< 23: One directional hold */ #define SIP_PAGE2_CALL_ONHOLD_INACTIVE (2 << 24) /*!< 24: Inactive */ +#define SIP_PAGE2_RFC2833_COMPENSATE (1 << 26) #define SIP_PAGE2_FLAGS_TO_COPY \ - (SIP_PAGE2_ALLOWSUBSCRIBE | SIP_PAGE2_ALLOWOVERLAP | SIP_PAGE2_VIDEOSUPPORT | SIP_PAGE2_T38SUPPORT) + (SIP_PAGE2_ALLOWSUBSCRIBE | SIP_PAGE2_ALLOWOVERLAP | SIP_PAGE2_VIDEOSUPPORT | SIP_PAGE2_T38SUPPORT | SIP_PAGE2_RFC2833_COMPENSATE) /* SIP packet flags */ #define SIP_PKT_DEBUG (1 << 0) /*!< Debug this packet */ @@ -1161,7 +1162,8 @@ static int sip_write(struct ast_channel *ast, struct ast_frame *frame); static int sip_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen); 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_senddigit(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); /*--- Transmitting responses and requests */ static int sipsock_read(int *id, int fd, short events, void *ignore); @@ -1512,7 +1514,8 @@ static const struct ast_channel_tech sip_tech = { .indicate = sip_indicate, .transfer = sip_transfer, .fixup = sip_fixup, - .send_digit = sip_senddigit, + .send_digit_begin = sip_senddigit_begin, + .send_digit_end = sip_senddigit_end, .bridge = ast_rtp_bridge, .send_text = sip_sendtext, }; @@ -2515,12 +2518,14 @@ static int create_addr_from_peer(struct sip_pvt *r, struct sip_peer *peer) ast_log(LOG_DEBUG, "Setting NAT on RTP to %s\n", natflags ? "On" : "Off"); ast_rtp_setnat(r->rtp, natflags); ast_rtp_setdtmf(r->rtp, ast_test_flag(&r->flags[0], SIP_DTMF) != SIP_DTMF_INFO); + ast_rtp_setdtmfcompensate(r->rtp, ast_test_flag(&r->flags[1], SIP_PAGE2_RFC2833_COMPENSATE)); } if (r->vrtp) { if (option_debug) ast_log(LOG_DEBUG, "Setting NAT on VRTP to %s\n", natflags ? "On" : "Off"); ast_rtp_setnat(r->vrtp, natflags); ast_rtp_setdtmf(r->vrtp, 0); + ast_rtp_setdtmfcompensate(r->vrtp, 0); } if (r->udptl) { if (option_debug) @@ -3441,9 +3446,31 @@ static int sip_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) return ret; } +static int sip_senddigit_begin(struct ast_channel *ast, char digit) +{ + struct sip_pvt *p = ast->tech_pvt; + int res = 0; + + ast_mutex_lock(&p->lock); + switch (ast_test_flag(&p->flags[0], SIP_DTMF)) { + case SIP_DTMF_INBAND: + res = -1; /* Tell Asterisk to generate inband indications */ + break; + case SIP_DTMF_RFC2833: + if (p->rtp) + ast_rtp_senddigit_begin(p->rtp, digit); + break; + default: + break; + } + ast_mutex_unlock(&p->lock); + + return res; +} + /*! \brief Send DTMF character on SIP channel within one call, we're able to transmit in many methods simultaneously */ -static int sip_senddigit(struct ast_channel *ast, char digit) +static int sip_senddigit_end(struct ast_channel *ast, char digit) { struct sip_pvt *p = ast->tech_pvt; int res = 0; @@ -3455,13 +3482,14 @@ static int sip_senddigit(struct ast_channel *ast, char digit) break; case SIP_DTMF_RFC2833: if (p->rtp) - ast_rtp_senddigit(p->rtp, digit); + ast_rtp_senddigit_end(p->rtp, digit); break; case SIP_DTMF_INBAND: - res = -1; + res = -1; /* Tell Asterisk to stop inband indications */ break; } ast_mutex_unlock(&p->lock); + return res; } @@ -4049,10 +4077,12 @@ static struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *si return NULL; } ast_rtp_setdtmf(p->rtp, ast_test_flag(&p->flags[0], SIP_DTMF) != SIP_DTMF_INFO); + ast_rtp_setdtmfcompensate(p->rtp, ast_test_flag(&p->flags[1], SIP_PAGE2_RFC2833_COMPENSATE)); ast_rtp_settos(p->rtp, global_tos_audio); if (p->vrtp) { ast_rtp_settos(p->vrtp, global_tos_video); ast_rtp_setdtmf(p->vrtp, 0); + ast_rtp_setdtmfcompensate(p->vrtp, 0); } if (p->udptl) ast_udptl_settos(p->udptl, global_tos_audio); @@ -12835,6 +12865,7 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int extract_uri(p, req); /* Get the Contact URI */ build_contact(p); /* Build our contact header */ ast_rtp_setdtmf(p->rtp, ast_test_flag(&p->flags[0], SIP_DTMF) != SIP_DTMF_INFO); + ast_rtp_setdtmfcompensate(p->rtp, ast_test_flag(&p->flags[1], SIP_PAGE2_RFC2833_COMPENSATE)); if (!replace_id && gotdest) { /* No matching extension found */ if (gotdest == 1 && ast_test_flag(&p->flags[1], SIP_PAGE2_ALLOWOVERLAP)) { @@ -14856,6 +14887,9 @@ static int handle_common_options(struct ast_flags *flags, struct ast_flags *mask } else if (!strcasecmp(v->name, "t38pt_tcp")) { ast_set_flag(&mask[1], SIP_PAGE2_T38SUPPORT_TCP); ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_T38SUPPORT_TCP); + } else if (!strcasecmp(v->name, "rfc2833compensate")) { + ast_set_flag(&mask[1], SIP_PAGE2_RFC2833_COMPENSATE); + ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_RFC2833_COMPENSATE); } return res; diff --git a/channels/chan_skinny.c b/channels/chan_skinny.c index b05fabb9b1..97c12455e8 100644 --- a/channels/chan_skinny.c +++ b/channels/chan_skinny.c @@ -1013,7 +1013,8 @@ static struct ast_frame *skinny_read(struct ast_channel *ast); 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_fixup(struct ast_channel *oldchan, struct ast_channel *newchan); -static int skinny_senddigit(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 const struct ast_channel_tech skinny_tech = { .type = "Skinny", @@ -1028,7 +1029,8 @@ static const struct ast_channel_tech skinny_tech = { .write = skinny_write, .indicate = skinny_indicate, .fixup = skinny_fixup, - .send_digit = skinny_senddigit, + .send_digit_begin = skinny_senddigit_begin, + .send_digit_end = skinny_senddigit_end, /* .bridge = ast_rtp_bridge, */ }; @@ -2467,7 +2469,12 @@ static int skinny_fixup(struct ast_channel *oldchan, struct ast_channel *newchan return 0; } -static int skinny_senddigit(struct ast_channel *ast, char digit) +static int skinny_senddigit_begin(struct ast_channel *ast, char digit) +{ + return -1; /* Start inband indications */ +} + +static int skinny_senddigit_end(struct ast_channel *ast, char digit) { #if 0 struct skinny_subchannel *sub = ast->tech_pvt; @@ -2478,7 +2485,7 @@ static int skinny_senddigit(struct ast_channel *ast, char digit) sprintf(tmp, "%d", digit); transmit_tone(d->session, digit); #endif - return -1; + return -1; /* Stop inband indications */ } static char *control2str(int ind) { diff --git a/channels/chan_vpb.cc b/channels/chan_vpb.cc index 0a98590d95..26e1beea0d 100644 --- a/channels/chan_vpb.cc +++ b/channels/chan_vpb.cc @@ -341,9 +341,9 @@ static struct vpb_pvt { static struct ast_channel *vpb_new(struct vpb_pvt *i, enum ast_channel_state state, char *context); static void *do_chanreads(void *pvt); - static struct ast_channel *vpb_request(const char *type, int format, void *data, int *cause); -static int vpb_digit(struct ast_channel *ast, char digit); +static int vpb_digit_begin(struct ast_channel *ast, char digit); +static int vpb_digit_end(struct ast_channel *ast, char digit); static int vpb_call(struct ast_channel *ast, char *dest, int timeout); static int vpb_hangup(struct ast_channel *ast); static int vpb_answer(struct ast_channel *ast); @@ -360,9 +360,8 @@ static struct ast_channel_tech vpb_tech = { properties: 0, requester: vpb_request, devicestate: NULL, - send_digit: vpb_digit, - send_digit_begin: NULL, - send_digit_end: NULL, + send_digit_begin: vpb_digit_begin, + send_digit_end: vpb_digit_end, call: vpb_call, hangup: vpb_hangup, answer: vpb_answer, @@ -389,9 +388,8 @@ static struct ast_channel_tech vpb_tech_indicate = { properties: 0, requester: vpb_request, devicestate: NULL, - send_digit: vpb_digit, - send_digit_begin: NULL, - send_digit_end: NULL, + send_digit_begin: vpb_digit_begin, + send_digit_end: vpb_digit_end, call: vpb_call, hangup: vpb_hangup, answer: vpb_answer, @@ -863,11 +861,11 @@ static inline int monitor_handle_owned(struct vpb_pvt *p, VPB_EVENT *e) vpb_timer_stop(p->ring_timer); vpb_timer_start(p->ring_timer); } else - f.frametype = -1; /* ignore ring on station port. */ + f.frametype = AST_FRAME_NULL; /* ignore ring on station port. */ break; case VPB_RING_OFF: - f.frametype = -1; + f.frametype = AST_FRAME_NULL; break; case VPB_TIMEREXP: @@ -876,12 +874,12 @@ static inline int monitor_handle_owned(struct vpb_pvt *p, VPB_EVENT *e) p->state = VPB_STATE_PLAYBUSY; vpb_timer_stop(p->busy_timer); vpb_timer_start(p->busy_timer); - f.frametype = -1; + f.frametype = AST_FRAME_NULL; } else if (e->data == p->ringback_timer_id) { playtone(p->handle, &Ringbacktone); vpb_timer_stop(p->ringback_timer); vpb_timer_start(p->ringback_timer); - f.frametype = -1; + f.frametype = AST_FRAME_NULL; } else if (e->data == p->ring_timer_id) { /* We didnt get another ring in time! */ if (p->owner->_state != AST_STATE_UP) { @@ -890,23 +888,23 @@ static inline int monitor_handle_owned(struct vpb_pvt *p, VPB_EVENT *e) f.subclass = AST_CONTROL_HANGUP; } else { vpb_timer_stop(p->ring_timer); - f.frametype = -1; + f.frametype = AST_FRAME_NULL; } } else { - f.frametype = -1; /* Ignore. */ + f.frametype = AST_FRAME_NULL; /* Ignore. */ } break; case VPB_DTMF_DOWN: case VPB_DTMF: if (use_ast_dtmfdet){ - f.frametype = -1; + f.frametype = AST_FRAME_NULL; } else if (p->owner->_state == AST_STATE_UP) { f.frametype = AST_FRAME_DTMF; f.subclass = e->data; } else - f.frametype = -1; + f.frametype = AST_FRAME_NULL; break; case VPB_TONEDETECT: @@ -950,11 +948,11 @@ static inline int monitor_handle_owned(struct vpb_pvt *p, VPB_EVENT *e) f.subclass = AST_CONTROL_HANGUP; } else { p->lastgrunt = ast_tvnow(); - f.frametype = -1; + f.frametype = AST_FRAME_NULL; } } else { - f.frametype = -1; + f.frametype = AST_FRAME_NULL; } break; @@ -970,7 +968,7 @@ static inline int monitor_handle_owned(struct vpb_pvt *p, VPB_EVENT *e) f.subclass = AST_CONTROL_HANGUP; #else ast_log(LOG_NOTICE,"%s: Got call progress callback but blind dialing \n", p->dev); - f.frametype = -1; + f.frametype = AST_FRAME_NULL; #endif break; @@ -983,14 +981,14 @@ static inline int monitor_handle_owned(struct vpb_pvt *p, VPB_EVENT *e) if (p->owner->_state == AST_STATE_UP) f.subclass = AST_CONTROL_HANGUP; else - f.frametype = -1; + f.frametype = AST_FRAME_NULL; } break; case VPB_LOOP_ONHOOK: if (p->owner->_state == AST_STATE_UP) f.subclass = AST_CONTROL_HANGUP; else - f.frametype = -1; + f.frametype = AST_FRAME_NULL; break; case VPB_STATION_ONHOOK: f.subclass = AST_CONTROL_HANGUP; @@ -1009,22 +1007,22 @@ static inline int monitor_handle_owned(struct vpb_pvt *p, VPB_EVENT *e) if (option_verbose > 1) ast_verbose(VERBOSE_PREFIX_2 "%s: Dialend\n", p->dev); } else { - f.frametype = -1; + f.frametype = AST_FRAME_NULL; } break; case VPB_PLAY_UNDERFLOW: - f.frametype = -1; + f.frametype = AST_FRAME_NULL; vpb_reset_play_fifo_alarm(p->handle); break; case VPB_RECORD_OVERFLOW: - f.frametype = -1; + f.frametype = AST_FRAME_NULL; vpb_reset_record_fifo_alarm(p->handle); break; default: - f.frametype = -1; + f.frametype = AST_FRAME_NULL; break; } @@ -1831,7 +1829,12 @@ static int vpb_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) return 0; } -static int vpb_digit(struct ast_channel *ast, char digit) +static int vpb_digit_begin(struct ast_channel *ast, char digit) +{ + /* XXX Modify this callback to let Asterisk control the length of DTMF */ + return 0; +} +static int vpb_digit_end(struct ast_channel *ast, char digit) { struct vpb_pvt *p = (struct vpb_pvt *)ast->tech_pvt; char s[2]; diff --git a/channels/chan_zap.c b/channels/chan_zap.c index ae3e94277a..7c860ff502 100644 --- a/channels/chan_zap.c +++ b/channels/chan_zap.c @@ -690,10 +690,12 @@ static struct zt_pvt { #endif int polarity; int dsp_features; + char begindigit; } *iflist = NULL, *ifend = NULL; static struct ast_channel *zt_request(const char *type, int format, void *data, int *cause); -static int zt_digit(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_sendtext(struct ast_channel *c, const char *text); static int zt_call(struct ast_channel *ast, char *rdest, int timeout); static int zt_hangup(struct ast_channel *ast); @@ -711,7 +713,8 @@ static const struct ast_channel_tech zap_tech = { .description = tdesc, .capabilities = AST_FORMAT_SLINEAR | AST_FORMAT_ULAW | AST_FORMAT_ALAW, .requester = zt_request, - .send_digit = zt_digit, + .send_digit_begin = zt_digit_begin, + .send_digit_end = zt_digit_end, .send_text = zt_sendtext, .call = zt_call, .hangup = zt_hangup, @@ -1002,45 +1005,113 @@ static int unalloc_sub(struct zt_pvt *p, int x) return 0; } -static int zt_digit(struct ast_channel *ast, char digit) +static int digit_to_dtmfindex(char digit) { - ZT_DIAL_OPERATION zo; - struct zt_pvt *p; + if (isdigit(digit)) + return ZT_TONE_DTMF_BASE + (digit - '0'); + else if (digit >= 'A' && digit <= 'D') + return ZT_TONE_DTMF_A + (digit - 'A'); + else if (digit >= 'a' && digit <= 'd') + return ZT_TONE_DTMF_A + (digit - 'a'); + else if (digit == '*') + return ZT_TONE_DTMF_s; + else if (digit == '#') + return ZT_TONE_DTMF_p; + else + return -1; +} + +static int zt_digit_begin(struct ast_channel *chan, char digit) +{ + struct zt_pvt *pvt; + int index; + int dtmf = -1; + + pvt = chan->tech_pvt; + + ast_mutex_lock(&pvt->lock); + + index = zt_get_index(chan, pvt, 0); + + if ((index != SUB_REAL) || !pvt->owner) + goto out; + +#ifdef HAVE_PRI + if ((pvt->sig == SIG_PRI) && (chan->_state == AST_STATE_DIALING) && !pvt->proceeding) { + if (pvt->setup_ack) { + if (!pri_grab(pvt, pvt->pri)) { + pri_information(pvt->pri->pri, pvt->call, digit); + pri_rel(pvt->pri); + } else + ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", pvt->span); + } else if (strlen(pvt->dialdest) < sizeof(pvt->dialdest) - 1) { + int res; + ast_log(LOG_DEBUG, "Queueing digit '%c' since setup_ack not yet received\n", digit); + res = strlen(pvt->dialdest); + pvt->dialdest[res++] = digit; + pvt->dialdest[res] = '\0'; + } + goto out; + } +#endif + if ((dtmf = digit_to_dtmfindex(digit)) == -1) + goto out; + + if (pvt->pulse || ioctl(pvt->subs[SUB_REAL].zfd, ZT_SENDTONE, &dtmf)) { + int res; + ZT_DIAL_OPERATION zo = { + .op = ZT_DIAL_OP_APPEND, + .dialstr[0] = 'T', + .dialstr[1] = digit, + .dialstr[2] = 0, + }; + if ((res = ioctl(pvt->subs[SUB_REAL].zfd, ZT_DIAL, &zo))) + ast_log(LOG_WARNING, "Couldn't dial digit %c\n", digit); + else + pvt->dialing = 1; + } else { + ast_log(LOG_DEBUG, "Started VLDTMF digit '%c'\n", digit); + pvt->dialing = 1; + pvt->begindigit = digit; + } + +out: + ast_mutex_unlock(&pvt->lock); + + return 0; /* Tell Asterisk not to generate inband indications */ +} + +static int zt_digit_end(struct ast_channel *chan, char digit) +{ + struct zt_pvt *pvt; int res = 0; int index; - p = ast->tech_pvt; - ast_mutex_lock(&p->lock); - index = zt_get_index(ast, p, 0); - if ((index == SUB_REAL) && p->owner) { + + pvt = chan->tech_pvt; + + ast_mutex_lock(&pvt->lock); + + index = zt_get_index(chan, pvt, 0); + + if ((index != SUB_REAL) || !pvt->owner || pvt->pulse) + goto out; + #ifdef HAVE_PRI - if ((p->sig == SIG_PRI) && (ast->_state == AST_STATE_DIALING) && !p->proceeding) { - if (p->setup_ack) { - if (!pri_grab(p, p->pri)) { - pri_information(p->pri->pri,p->call,digit); - pri_rel(p->pri); - } else - ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->span); - } else if (strlen(p->dialdest) < sizeof(p->dialdest) - 1) { - ast_log(LOG_DEBUG, "Queueing digit '%c' since setup_ack not yet received\n", digit); - res = strlen(p->dialdest); - p->dialdest[res++] = digit; - p->dialdest[res] = '\0'; - } - } else { -#else - { + /* This means that the digit was already sent via PRI signalling */ + if (pvt->sig == SIG_PRI && !pvt->begindigit) + goto out; #endif - zo.op = ZT_DIAL_OP_APPEND; - zo.dialstr[0] = 'T'; - zo.dialstr[1] = digit; - zo.dialstr[2] = 0; - if ((res = ioctl(p->subs[SUB_REAL].zfd, ZT_DIAL, &zo))) - ast_log(LOG_WARNING, "Couldn't dial digit %c\n", digit); - else - p->dialing = 1; - } + + if (pvt->begindigit) { + ast_log(LOG_DEBUG, "Ending VLDTMF digit '%c'\n", digit); + res = ioctl(pvt->subs[SUB_REAL].zfd, ZT_SENDTONE, -1); + pvt->dialing = 0; + pvt->begindigit = 0; } - ast_mutex_unlock(&p->lock); + +out: + ast_mutex_unlock(&pvt->lock); + return res; } @@ -3543,21 +3614,14 @@ static struct ast_frame *zt_handle_event(struct ast_channel *ast) ast_log(LOG_DEBUG, "Got event %s(%d) on channel %d (index %d)\n", event2str(res), res, p->channel, index); if (res & (ZT_EVENT_PULSEDIGIT | ZT_EVENT_DTMFUP)) { - if (res & ZT_EVENT_PULSEDIGIT) - p->pulsedial = 1; - else - p->pulsedial = 0; + p->pulsedial = (res & ZT_EVENT_PULSEDIGIT) ? 1 : 0; ast_log(LOG_DEBUG, "Detected %sdigit '%c'\n", p->pulsedial ? "pulse ": "", res & 0xff); #ifdef HAVE_PRI if (!p->proceeding && p->sig == SIG_PRI && p->pri && p->pri->overlapdial) { /* absorb event */ } else { #endif - /* Send a DTMF event for 'legacy' channels and all applications, - and a DTMF_BEGIN event for channels that handle variable duration - DTMF events - */ - p->subs[index].f.frametype = AST_FRAME_DTMF_BEGIN; + p->subs[index].f.frametype = AST_FRAME_DTMF_END; p->subs[index].f.subclass = res & 0xff; #ifdef HAVE_PRI } @@ -3571,9 +3635,6 @@ static struct ast_frame *zt_handle_event(struct ast_channel *ast) ast_log(LOG_DEBUG, "DTMF Down '%c'\n", res & 0xff); /* Mute conference */ zt_confmute(p, 1); - /* Send a DTMF_BEGIN event for devices that want variable - duration DTMF events - */ p->subs[index].f.frametype = AST_FRAME_DTMF_BEGIN; p->subs[index].f.subclass = res & 0xff; return &p->subs[index].f; diff --git a/channels/iax2-parser.c b/channels/iax2-parser.c index 94aaf4c706..56c45e2422 100644 --- a/channels/iax2-parser.c +++ b/channels/iax2-parser.c @@ -395,7 +395,7 @@ void iax_showframe(struct iax_frame *f, struct ast_iax2_full_hdr *fhi, int rx, s { const char *frames[] = { "(0?)", - "DTMF ", + "DTMF_E ", "VOICE ", "VIDEO ", "CONTROL", @@ -404,7 +404,10 @@ void iax_showframe(struct iax_frame *f, struct ast_iax2_full_hdr *fhi, int rx, s "TEXT ", "IMAGE ", "HTML ", - "CNG " }; + "CNG ", + "MODEM ", + "DTMF_B ", + }; const char *iaxs[] = { "(0?)", "NEW ", @@ -508,7 +511,7 @@ void iax_showframe(struct iax_frame *f, struct ast_iax2_full_hdr *fhi, int rx, s } else { class = frames[(int)fh->type]; } - if (fh->type == AST_FRAME_DTMF) { + if (fh->type == AST_FRAME_DTMF_BEGIN || fh->type == AST_FRAME_DTMF_END) { sprintf(subclass2, "%c", fh->csub); subclass = subclass2; } else if (fh->type == AST_FRAME_IAX) { diff --git a/configs/sip.conf.sample b/configs/sip.conf.sample index 3f24034cc1..b04c60e77e 100644 --- a/configs/sip.conf.sample +++ b/configs/sip.conf.sample @@ -417,7 +417,7 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls ; subscribecontext subscribecontext ; videosupport videosupport ; maxcallbitrate maxcallbitrate -; mailbox +; rfc2833compensate mailbox ; username ; template ; fromdomain @@ -431,6 +431,7 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls ; rtpholdtimeout ; sendrpid ; outboundproxy +; rfc2833compensate ;[sip_proxy] ; For incoming calls only. Example: FWD (Free World Dialup) @@ -587,3 +588,9 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls ; Normally you do NOT need to set this parameter ;setvar=CUSTID=5678 ; Channel variable to be set for all calls from this device +;[pre14-asterisk] +;type=friend +;secret=digium +;host=dynamic +;rfc2833compensate=yes ; Compensate for pre-1.4 DTMF transmission from another Asterisk machine. + ; You must have this turned on or DTMF reception will work improperly. diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index 95cb3196eb..d54a1bdd79 100644 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -200,13 +200,11 @@ struct ast_channel_tech { int (* const devicestate)(void *data); /*!< Devicestate call back */ - int (* const send_digit)(struct ast_channel *chan, char digit); /*!< Send a literal DTMF digit */ - /*! \brief Start sending a literal DTMF digit */ int (* const send_digit_begin)(struct ast_channel *chan, char digit); - /*! \brief Stop sending the last literal DTMF digit */ - int (* const send_digit_end)(struct ast_channel *chan); + /*! \brief Stop sending a literal DTMF digit */ + int (* const send_digit_end)(struct ast_channel *chan, char digit); /*! \brief Call a given phone number (address, etc), but don't take longer than timeout seconds to do so. */ @@ -424,8 +422,12 @@ struct ast_channel { struct ast_channel_spy_list *spies; /*!< Chan Spy stuff */ struct ast_channel_whisper_buffer *whisper; /*!< Whisper Paging buffer */ AST_LIST_ENTRY(ast_channel) chan_list; /*!< For easy linking */ + struct ast_jb jb; /*!< The jitterbuffer state */ + char emulate_dtmf_digit; /*!< Digit being emulated */ + unsigned int emulate_dtmf_samples; /*!< Number of samples left to emulate DTMF for */ + /*! \brief Data stores on the channel */ AST_LIST_HEAD_NOLOCK(datastores, ast_datastore) datastores; }; @@ -443,29 +445,34 @@ enum { /*! \brief ast_channel flags */ enum { /*! Queue incoming dtmf, to be released when this flag is turned off */ - AST_FLAG_DEFER_DTMF = (1 << 1), + AST_FLAG_DEFER_DTMF = (1 << 1), /*! write should be interrupt generator */ - AST_FLAG_WRITE_INT = (1 << 2), + AST_FLAG_WRITE_INT = (1 << 2), /*! a thread is blocking on this channel */ - AST_FLAG_BLOCKING = (1 << 3), + AST_FLAG_BLOCKING = (1 << 3), /*! This is a zombie channel */ - AST_FLAG_ZOMBIE = (1 << 4), + AST_FLAG_ZOMBIE = (1 << 4), /*! There is an exception pending */ - AST_FLAG_EXCEPTION = (1 << 5), + AST_FLAG_EXCEPTION = (1 << 5), /*! Listening to moh XXX anthm promises me this will disappear XXX */ - AST_FLAG_MOH = (1 << 6), + AST_FLAG_MOH = (1 << 6), /*! This channel is spying on another channel */ - AST_FLAG_SPYING = (1 << 7), + AST_FLAG_SPYING = (1 << 7), /*! This channel is in a native bridge */ - AST_FLAG_NBRIDGE = (1 << 8), + AST_FLAG_NBRIDGE = (1 << 8), /*! the channel is in an auto-incrementing dialplan processor, * so when ->priority is set, it will get incremented before * finding the next priority to run */ - AST_FLAG_IN_AUTOLOOP = (1 << 9), + AST_FLAG_IN_AUTOLOOP = (1 << 9), /*! This is an outgoing call */ - AST_FLAG_OUTGOING = (1 << 10), + AST_FLAG_OUTGOING = (1 << 10), /*! This channel is being whispered on */ - AST_FLAG_WHISPER = (1 << 11), + AST_FLAG_WHISPER = (1 << 11), + /*! A DTMF_BEGIN frame has been read from this channel, but not yet an END */ + AST_FLAG_IN_DTMF = (1 << 12), + /*! A DTMF_END was received when not IN_DTMF, so the length of the digit is + * currently being emulated */ + AST_FLAG_EMULATE_DTMF = (1 << 13), }; /*! \brief ast_bridge_config flags */ @@ -876,6 +883,9 @@ int ast_recvchar(struct ast_channel *chan, int timeout); */ int ast_senddigit(struct ast_channel *chan, char digit); +int ast_senddigit_begin(struct ast_channel *chan, char digit); +int ast_senddigit_end(struct ast_channel *chan, char digit); + /*! \brief Receives a text string from a channel * Read a string of text from a channel * \param chan channel to act upon diff --git a/include/asterisk/frame.h b/include/asterisk/frame.h index 0c9c651d27..bc7273a092 100644 --- a/include/asterisk/frame.h +++ b/include/asterisk/frame.h @@ -85,11 +85,46 @@ struct ast_codec_pref { */ +/*! + * \brief Frame types + * + * \note It is important that the values of each frame type are never changed, + * because it will break backwards compatability with older versions. + */ +enum ast_frame_type { + /*! DTMF end event, subclass is the digit */ + AST_FRAME_DTMF_END = 1, + /*! Voice data, subclass is AST_FORMAT_* */ + AST_FRAME_VOICE, + /*! Video frame, maybe?? :) */ + AST_FRAME_VIDEO, + /*! A control frame, subclass is AST_CONTROL_* */ + AST_FRAME_CONTROL, + /*! An empty, useless frame */ + AST_FRAME_NULL, + /*! Inter Asterisk Exchange private frame type */ + AST_FRAME_IAX, + /*! Text messages */ + AST_FRAME_TEXT, + /*! Image Frames */ + AST_FRAME_IMAGE, + /*! HTML Frame */ + AST_FRAME_HTML, + /*! Comfort Noise frame (subclass is level of CNG in -dBov), + body may include zero or more 8-bit quantization coefficients */ + AST_FRAME_CNG, + /*! Modem-over-IP data streams */ + AST_FRAME_MODEM, + /*! DTMF begin event, subclass is the digit */ + AST_FRAME_DTMF_BEGIN, +}; +#define AST_FRAME_DTMF AST_FRAME_DTMF_END + /*! \brief Data structure associated with a single frame of data */ struct ast_frame { /*! Kind of frame */ - int frametype; + enum ast_frame_type frametype; /*! Subclass, frame dependent */ int subclass; /*! Length of data */ @@ -151,35 +186,6 @@ extern struct ast_frame ast_null_frame; /*! Need the source be free'd? (haha!) */ #define AST_MALLOCD_SRC (1 << 2) -/* Frame types */ -/*! A DTMF digit, subclass is the digit */ -#define AST_FRAME_DTMF 1 -/*! Voice data, subclass is AST_FORMAT_* */ -#define AST_FRAME_VOICE 2 -/*! Video frame, maybe?? :) */ -#define AST_FRAME_VIDEO 3 -/*! A control frame, subclass is AST_CONTROL_* */ -#define AST_FRAME_CONTROL 4 -/*! An empty, useless frame */ -#define AST_FRAME_NULL 5 -/*! Inter Asterisk Exchange private frame type */ -#define AST_FRAME_IAX 6 -/*! Text messages */ -#define AST_FRAME_TEXT 7 -/*! Image Frames */ -#define AST_FRAME_IMAGE 8 -/*! HTML Frame */ -#define AST_FRAME_HTML 9 -/*! Comfort Noise frame (subclass is level of CNG in -dBov), - body may include zero or more 8-bit quantization coefficients */ -#define AST_FRAME_CNG 10 -/*! Modem-over-IP data streams */ -#define AST_FRAME_MODEM 11 -/*! DTMF begin event, subclass is the digit */ -#define AST_FRAME_DTMF_BEGIN 12 -/*! DTMF end event, subclass is the digit */ -#define AST_FRAME_DTMF_END 13 - /* MODEM subclasses */ /*! T.38 Fax-over-IP */ #define AST_MODEM_T38 1 diff --git a/include/asterisk/rtp.h b/include/asterisk/rtp.h index f99d4dec63..6780c2adc9 100644 --- a/include/asterisk/rtp.h +++ b/include/asterisk/rtp.h @@ -142,7 +142,9 @@ int ast_rtp_fd(struct ast_rtp *rtp); int ast_rtcp_fd(struct ast_rtp *rtp); -int ast_rtp_senddigit(struct ast_rtp *rtp, char digit); +int ast_rtp_senddigit_begin(struct ast_rtp *rtp, char digit); + +int ast_rtp_senddigit_end(struct ast_rtp *rtp, char digit); int ast_rtp_sendcng(struct ast_rtp *rtp, int level); @@ -181,6 +183,9 @@ void ast_rtp_setnat(struct ast_rtp *rtp, int nat); /*! \brief Indicate whether this RTP session is carrying DTMF or not */ void ast_rtp_setdtmf(struct ast_rtp *rtp, int dtmf); +/*! \brief Compensate for devices that send RFC2833 packets all at once */ +void ast_rtp_setdtmfcompensate(struct ast_rtp *rtp, int compensate); + int ast_rtp_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms); int ast_rtp_proto_register(struct ast_rtp_protocol *proto); diff --git a/main/app.c b/main/app.c index 1099feaea3..b6a7f73a9a 100644 --- a/main/app.c +++ b/main/app.c @@ -212,10 +212,6 @@ int ast_dtmf_stream(struct ast_channel *chan, struct ast_channel *peer, const ch { const char *ptr; int res = 0; - struct ast_frame f = { - .frametype = AST_FRAME_DTMF, - .src = "ast_dtmf_stream" - }; if (!between) between = 100; @@ -240,11 +236,8 @@ int ast_dtmf_stream(struct ast_channel *chan, struct ast_channel *peer, const ch if (*ptr == 'f' || *ptr == 'F') { /* ignore return values if not supported by channel */ ast_indicate(chan, AST_CONTROL_FLASH); - } else { - f.subclass = *ptr; - if ((res = ast_write(chan, &f))) - break; - } + } else + ast_senddigit(chan, *ptr); /* pause between digits */ if ((res = ast_safe_sleep(chan, between))) break; diff --git a/main/channel.c b/main/channel.c index 61df3d2439..6ba79eea6e 100644 --- a/main/channel.c +++ b/main/channel.c @@ -102,6 +102,9 @@ unsigned long global_fin = 0, global_fout = 0; AST_THREADSTORAGE(state2str_threadbuf, state2str_threadbuf_init); #define STATE2STR_BUFSIZE 32 +/* XXX 100ms ... this won't work with wideband support */ +#define AST_DEFAULT_EMULATE_DTMF_SAMPLES 800 + struct chanlist { const struct ast_channel_tech *tech; AST_LIST_ENTRY(chanlist) list; @@ -240,7 +243,8 @@ static int show_channeltype(int fd, int argc, char *argv[]) " Indication: %s\n" " Transfer : %s\n" " Capabilities: %d\n" - " Send Digit: %s\n" + " Digit Begin: %s\n" + " Digit End: %s\n" " Send HTML : %s\n" " Image Support: %s\n" " Text Support: %s\n", @@ -249,7 +253,8 @@ static int show_channeltype(int fd, int argc, char *argv[]) (cl->tech->indicate) ? "yes" : "no", (cl->tech->transfer) ? "yes" : "no", (cl->tech->capabilities) ? cl->tech->capabilities : -1, - (cl->tech->send_digit) ? "yes" : "no", + (cl->tech->send_digit_begin) ? "yes" : "no", + (cl->tech->send_digit_end) ? "yes" : "no", (cl->tech->send_html) ? "yes" : "no", (cl->tech->send_image) ? "yes" : "no", (cl->tech->send_text) ? "yes" : "no" @@ -1862,8 +1867,10 @@ int ast_waitfordigit_full(struct ast_channel *c, int ms, int audiofd, int cmdfd) /* Write audio if appropriate */ if (audiofd > -1) write(audiofd, f->data, f->datalen); + default: + /* Ignore */ + break; } - /* Ignore */ ast_frfree(f); } } @@ -1881,11 +1888,10 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio) */ ast_channel_lock(chan); if (chan->masq) { - if (ast_do_masquerade(chan)) { + if (ast_do_masquerade(chan)) ast_log(LOG_WARNING, "Failed to perform masquerade\n"); - } else { + else f = &ast_null_frame; - } goto done; } @@ -1897,13 +1903,17 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio) } prestate = chan->_state; - if (!ast_test_flag(chan, AST_FLAG_DEFER_DTMF) && !ast_strlen_zero(chan->dtmfq)) { + if (!ast_test_flag(chan, AST_FLAG_DEFER_DTMF | AST_FLAG_EMULATE_DTMF | AST_FLAG_IN_DTMF) && + !ast_strlen_zero(chan->dtmfq)) { /* We have DTMF that has been deferred. Return it now */ - chan->dtmff.frametype = AST_FRAME_DTMF; + chan->dtmff.frametype = AST_FRAME_DTMF_BEGIN; chan->dtmff.subclass = chan->dtmfq[0]; /* Drop first digit from the buffer */ memmove(chan->dtmfq, chan->dtmfq + 1, sizeof(chan->dtmfq) - 1); f = &chan->dtmff; + ast_set_flag(chan, AST_FLAG_EMULATE_DTMF); + chan->emulate_dtmf_digit = f->subclass; + chan->emulate_dtmf_samples = AST_DEFAULT_EMULATE_DTMF_SAMPLES; goto done; } @@ -2017,27 +2027,57 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio) } } break; - case AST_FRAME_DTMF: - ast_log(LOG_DTMF, "DTMF '%c' received on %s\n", f->subclass, chan->name); - if (ast_test_flag(chan, AST_FLAG_DEFER_DTMF)) { + case AST_FRAME_DTMF_END: + 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)) { if (strlen(chan->dtmfq) < sizeof(chan->dtmfq) - 2) chan->dtmfq[strlen(chan->dtmfq)] = f->subclass; else ast_log(LOG_WARNING, "Dropping deferred DTMF digits on %s\n", chan->name); ast_frfree(f); f = &ast_null_frame; - } + } else if (!ast_test_flag(chan, AST_FLAG_IN_DTMF)) { + f->frametype = AST_FRAME_DTMF_BEGIN; + ast_set_flag(chan, AST_FLAG_EMULATE_DTMF); + chan->emulate_dtmf_digit = f->subclass; + if (f->samples) + chan->emulate_dtmf_samples = f->samples; + else + chan->emulate_dtmf_samples = AST_DEFAULT_EMULATE_DTMF_SAMPLES; + } else + ast_clear_flag(chan, AST_FLAG_IN_DTMF); break; case AST_FRAME_DTMF_BEGIN: ast_log(LOG_DTMF, "DTMF begin '%c' received on %s\n", f->subclass, chan->name); - break; - case AST_FRAME_DTMF_END: - 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_frfree(f); + f = &ast_null_frame; + } else + ast_set_flag(chan, AST_FLAG_IN_DTMF); break; case AST_FRAME_VOICE: - if (dropaudio) { + /* 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 + * voice frame through before starting the next digit, to ensure a gap + * between DTMF digits. */ + if (ast_test_flag(chan, AST_FLAG_EMULATE_DTMF) && !chan->emulate_dtmf_samples) { + ast_clear_flag(chan, AST_FLAG_EMULATE_DTMF); + chan->emulate_dtmf_digit = 0; + } + + if (dropaudio || ast_test_flag(chan, AST_FLAG_IN_DTMF)) { ast_frfree(f); f = &ast_null_frame; + } else if (ast_test_flag(chan, AST_FLAG_EMULATE_DTMF)) { + if (f->samples >= chan->emulate_dtmf_samples) { + chan->emulate_dtmf_samples = 0; + f->frametype = AST_FRAME_DTMF_END; + f->subclass = chan->emulate_dtmf_digit; + } else { + chan->emulate_dtmf_samples -= f->samples; + ast_frfree(f); + f = &ast_null_frame; + } } else if (!(f->subclass & chan->nativeformats)) { /* This frame can't be from the current native formats -- drop it on the floor */ @@ -2106,6 +2146,9 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio) } } } + default: + /* Just pass it on! */ + break; } } else { /* Make sure we always return NULL in the future */ @@ -2258,12 +2301,13 @@ int ast_sendtext(struct ast_channel *chan, const char *text) return res; } -static int do_senddigit(struct ast_channel *chan, char digit) +int ast_senddigit_begin(struct ast_channel *chan, char digit) { int res = -1; - if (chan->tech->send_digit) - res = chan->tech->send_digit(chan, digit); + if (chan->tech->send_digit_begin) + res = chan->tech->send_digit_begin(chan, digit); + if (res) { /* * Device does not support DTMF tones, lets fake @@ -2299,12 +2343,30 @@ static int do_senddigit(struct ast_channel *chan, char digit) ast_log(LOG_DEBUG, "Unable to generate DTMF tone '%c' for '%s'\n", digit, chan->name); } } + + return 0; +} + +int ast_senddigit_end(struct ast_channel *chan, char digit) +{ + int res = -1; + + if (chan->tech->send_digit_end) + res = chan->tech->send_digit_end(chan, digit); + + if (res && chan->generator) + ast_playtones_stop(chan); + return 0; } int ast_senddigit(struct ast_channel *chan, char digit) { - return do_senddigit(chan, digit); + ast_senddigit_begin(chan, digit); + + ast_safe_sleep(chan, 100); /* XXX 100ms ... probably should be configurable */ + + return ast_senddigit_end(chan, digit); } int ast_prod(struct ast_channel *chan) @@ -2372,17 +2434,16 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr) chan->tech->indicate(chan, fr->subclass, fr->data, fr->datalen); break; case AST_FRAME_DTMF_BEGIN: - res = (chan->tech->send_digit_begin == NULL) ? 0 : - chan->tech->send_digit_begin(chan, fr->subclass); + ast_clear_flag(chan, AST_FLAG_BLOCKING); + ast_channel_unlock(chan); + res = ast_senddigit_begin(chan, fr->subclass); + ast_channel_lock(chan); + CHECK_BLOCKING(chan); break; case AST_FRAME_DTMF_END: - res = (chan->tech->send_digit_end == NULL) ? 0 : - chan->tech->send_digit_end(chan); - break; - case AST_FRAME_DTMF: ast_clear_flag(chan, AST_FLAG_BLOCKING); ast_channel_unlock(chan); - res = do_senddigit(chan,fr->subclass); + res = ast_senddigit_end(chan, fr->subclass); ast_channel_lock(chan); CHECK_BLOCKING(chan); break; @@ -2467,7 +2528,14 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr) res = chan->tech->write(chan, f); } - break; + break; + case AST_FRAME_NULL: + case AST_FRAME_IAX: + /* Ignore these */ + break; + default: + res = chan->tech->write(chan, f); + break; } if (f && f != fr) @@ -3496,6 +3564,7 @@ static enum ast_bridge_result ast_generic_bridge(struct ast_channel *c0, struct break; } if ((f->frametype == AST_FRAME_VOICE) || + (f->frametype == AST_FRAME_DTMF_BEGIN) || (f->frametype == AST_FRAME_DTMF) || (f->frametype == AST_FRAME_VIDEO) || (f->frametype == AST_FRAME_IMAGE) || @@ -3505,10 +3574,14 @@ static enum ast_bridge_result ast_generic_bridge(struct ast_channel *c0, struct /* monitored dtmf causes exit from bridge */ int monitored_source = (who == c0) ? watch_c0_dtmf : watch_c1_dtmf; - if (f->frametype == AST_FRAME_DTMF && monitored_source) { + if (monitored_source && + (f->frametype == AST_FRAME_DTMF_END || + f->frametype == AST_FRAME_DTMF_BEGIN)) { *fo = f; *rc = who; - ast_log(LOG_DEBUG, "Got DTMF on channel (%s)\n", who->name); + ast_log(LOG_DEBUG, "Got DTMF %s on channel (%s)\n", + f->frametype == AST_FRAME_DTMF_END ? "end" : "begin", + who->name); break; } /* Write immediately frames, not passed through jb */ diff --git a/main/dsp.c b/main/dsp.c index 7e62c8bf72..2992031b4e 100644 --- a/main/dsp.c +++ b/main/dsp.c @@ -1497,42 +1497,45 @@ struct ast_frame *ast_dsp_process(struct ast_channel *chan, struct ast_dsp *dsp, } else { if (digit) { /* Thought we saw one last time. Pretty sure we really have now */ - if (dsp->thinkdigit) { - if ((dsp->thinkdigit != 'x') && (dsp->thinkdigit != digit)) { - /* If we found a digit, and we're changing digits, go - ahead and send this one, but DON'T stop confmute because - we're detecting something else, too... */ - memset(&dsp->f, 0, sizeof(dsp->f)); - dsp->f.frametype = AST_FRAME_DTMF; - dsp->f.subclass = dsp->thinkdigit; - FIX_INF(af); - if (chan) - ast_queue_frame(chan, af); - ast_frfree(af); - } + if ((dsp->thinkdigit != 'x') && (dsp->thinkdigit != digit)) { + /* If we found a digit, and we're changing digits, go + ahead and send this one, but DON'T stop confmute because + we're detecting something else, too... */ + memset(&dsp->f, 0, sizeof(dsp->f)); + dsp->f.frametype = AST_FRAME_DTMF_END; + dsp->f.subclass = dsp->thinkdigit; + FIX_INF(af); + if (chan) + ast_queue_frame(chan, af); + ast_frfree(af); + } else { dsp->thinkdigit = digit; - return &dsp->f; - } - dsp->thinkdigit = digit; - } else { - if (dsp->thinkdigit) { memset(&dsp->f, 0, sizeof(dsp->f)); - if (dsp->thinkdigit != 'x') { - /* If we found a digit, send it now */ - dsp->f.frametype = AST_FRAME_DTMF; - dsp->f.subclass = dsp->thinkdigit; - dsp->thinkdigit = 0; - } else { - dsp->f.frametype = AST_FRAME_DTMF; - dsp->f.subclass = 'u'; - dsp->thinkdigit = 0; - } + dsp->f.frametype = AST_FRAME_DTMF_BEGIN; + dsp->f.subclass = dsp->thinkdigit; FIX_INF(af); if (chan) ast_queue_frame(chan, af); ast_frfree(af); - return &dsp->f; } + return &dsp->f; + } else { + memset(&dsp->f, 0, sizeof(dsp->f)); + if (dsp->thinkdigit != 'x') { + /* If we found a digit, send it now */ + dsp->f.frametype = AST_FRAME_DTMF_END; + dsp->f.subclass = dsp->thinkdigit; + dsp->thinkdigit = 0; + } else { + dsp->f.frametype = AST_FRAME_DTMF; + dsp->f.subclass = 'u'; + dsp->thinkdigit = 0; + } + FIX_INF(af); + if (chan) + ast_queue_frame(chan, af); + ast_frfree(af); + return &dsp->f; } } } else if (!digit) { @@ -1553,7 +1556,7 @@ struct ast_frame *ast_dsp_process(struct ast_channel *chan, struct ast_dsp *dsp, } else { if (dsp->td.dtmf.current_digits) { memset(&dsp->f, 0, sizeof(dsp->f)); - dsp->f.frametype = AST_FRAME_DTMF; + dsp->f.frametype = AST_FRAME_DTMF_END; dsp->f.subclass = dsp->td.dtmf.digits[0]; memmove(dsp->td.dtmf.digits, dsp->td.dtmf.digits + 1, dsp->td.dtmf.current_digits); dsp->td.dtmf.current_digits--; diff --git a/main/file.c b/main/file.c index 365d233027..48c1660694 100644 --- a/main/file.c +++ b/main/file.c @@ -1029,7 +1029,8 @@ static int waitstream_core(struct ast_channel *c, const char *breakon, if (!fr) return -1; switch(fr->frametype) { - case AST_FRAME_DTMF: + case AST_FRAME_DTMF_BEGIN: + case AST_FRAME_DTMF_END: if (context) { const char exten[2] = { fr->subclass, '\0' }; if (ast_exists_extension(c, context, exten, 1, c->cid.cid_num)) { @@ -1065,8 +1066,10 @@ static int waitstream_core(struct ast_channel *c, const char *breakon, /* Write audio if appropriate */ if (audiofd > -1) write(audiofd, fr->data, fr->datalen); + default: + /* Ignore all others */ + break; } - /* Ignore all others */ ast_frfree(fr); } ast_sched_runq(c->sched); diff --git a/main/rtp.c b/main/rtp.c index 5703d71403..b53ad405bb 100644 --- a/main/rtp.c +++ b/main/rtp.c @@ -106,14 +106,12 @@ struct rtpPayloadType { /*! \brief RTP session description */ struct ast_rtp { int s; - char resp; struct ast_frame f; unsigned char rawdata[8192 + AST_FRIENDLY_OFFSET]; unsigned int ssrc; /*!< Synchronization source, RFC 3550, page 10. */ unsigned int themssrc; /*!< Their SSRC */ unsigned int rxssrc; unsigned int lastts; - unsigned int lastdigitts; unsigned int lastrxts; unsigned int lastividtimestamp; unsigned int lastovidtimestamp; @@ -128,11 +126,17 @@ struct ast_rtp { unsigned int cycles; /*!< Shifted count of sequence number cycles */ double rxjitter; /*!< Interarrival jitter at the moment */ double rxtransit; /*!< Relative transit time for previous packet */ - unsigned int lasteventendseqn; int lasttxformat; int lastrxformat; + /* DTMF Reception Variables */ + char resp; + unsigned int lasteventendseqn; int dtmfcount; unsigned int dtmfduration; + /* DTMF Transmission Variables */ + unsigned int lastdigitts; + char send_digit; + int send_payload; int nat; unsigned int flags; struct sockaddr_in us; /*!< Socket representation of the local endpoint. */ @@ -164,6 +168,8 @@ static void timeval2ntp(struct timeval tv, unsigned int *msw, unsigned int *lsw) static int ast_rtcp_write_sr(void *data); static int ast_rtcp_write_rr(void *data); static unsigned int ast_rtcp_calc_interval(struct ast_rtp *rtp); +static int ast_rtp_senddigit_continuation(struct ast_rtp *rtp); +int ast_rtp_senddigit_end(struct ast_rtp *rtp, char digit); static int bridge_p2p_rtcp_write(struct ast_rtp *rtp, unsigned int *rtcpheader, int len); #define FLAG_3389_WARNING (1 << 0) @@ -174,6 +180,7 @@ static int bridge_p2p_rtcp_write(struct ast_rtp *rtp, unsigned int *rtcpheader, #define FLAG_P2P_SENT_MARK (1 << 4) #define FLAG_P2P_NEED_DTMF (1 << 5) #define FLAG_CALLBACK_MODE (1 << 6) +#define FLAG_DTMF_COMPENSATE (1 << 7) /*! * \brief Structure defining an RTCP session. @@ -531,7 +538,12 @@ void ast_rtp_setdtmf(struct ast_rtp *rtp, int dtmf) ast_set2_flag(rtp, dtmf ? 1 : 0, FLAG_HAS_DTMF); } -static struct ast_frame *send_dtmf(struct ast_rtp *rtp) +void ast_rtp_setdtmfcompensate(struct ast_rtp *rtp, int compensate) +{ + ast_set2_flag(rtp, compensate ? 1 : 0, FLAG_DTMF_COMPENSATE); +} + +static struct ast_frame *send_dtmf(struct ast_rtp *rtp, enum ast_frame_type type) { if (ast_tvcmp(ast_tvnow(), rtp->dtmfmute) < 0) { if (option_debug) @@ -546,15 +558,13 @@ static struct ast_frame *send_dtmf(struct ast_rtp *rtp) rtp->f.frametype = AST_FRAME_CONTROL; rtp->f.subclass = AST_CONTROL_FLASH; } else { - rtp->f.frametype = AST_FRAME_DTMF; + rtp->f.frametype = type; rtp->f.subclass = rtp->resp; } rtp->f.datalen = 0; rtp->f.samples = 0; rtp->f.mallocd = 0; rtp->f.src = "RTP"; - rtp->resp = 0; - rtp->dtmfduration = 0; return &rtp->f; } @@ -607,7 +617,7 @@ static struct ast_frame *process_cisco_dtmf(struct ast_rtp *rtp, unsigned char * resp = 'X'; } if (rtp->resp && (rtp->resp != resp)) { - f = send_dtmf(rtp); + f = send_dtmf(rtp, AST_FRAME_DTMF_END); } rtp->resp = resp; rtp->dtmfcount = dtmftimeout; @@ -633,6 +643,7 @@ static struct ast_frame *process_rfc2833(struct ast_rtp *rtp, unsigned char *dat char resp = 0; struct ast_frame *f = NULL; + /* Figure out event, event end, and duration */ event = ntohl(*((unsigned int *)(data))); event >>= 24; event_end = ntohl(*((unsigned int *)(data))); @@ -640,8 +651,12 @@ static struct ast_frame *process_rfc2833(struct ast_rtp *rtp, unsigned char *dat event_end >>= 24; duration = ntohl(*((unsigned int *)(data))); duration &= 0xFFFF; + + /* Print out debug if turned on */ if (rtpdebug || option_debug > 2) ast_log(LOG_DEBUG, "- RTP 2833 Event: %08x (len = %d)\n", event, len); + + /* Figure out what digit was pressed */ if (event < 10) { resp = '0' + event; } else if (event < 11) { @@ -653,25 +668,21 @@ static struct ast_frame *process_rfc2833(struct ast_rtp *rtp, unsigned char *dat } else if (event < 17) { /* Event 16: Hook flash */ resp = 'X'; } - if (rtp->resp && (rtp->resp != resp)) { - f = send_dtmf(rtp); - } else if (event_end & 0x80) { - if (rtp->resp) { - if (rtp->lasteventendseqn != seqno) { - f = send_dtmf(rtp); - rtp->lasteventendseqn = seqno; - } - rtp->resp = 0; - } - resp = 0; - duration = 0; - } else if (rtp->resp && rtp->dtmfduration && (duration < rtp->dtmfduration)) { - f = send_dtmf(rtp); - } - if (!(event_end & 0x80)) + + if ((!(rtp->resp) && (!(event_end & 0x80))) || (rtp->resp && rtp->resp != resp)) { rtp->resp = resp; + if (!ast_test_flag(rtp, FLAG_DTMF_COMPENSATE)) + f = send_dtmf(rtp, AST_FRAME_DTMF_BEGIN); + } else if (event_end & 0x80 && rtp->lasteventendseqn != seqno && rtp->resp) { + f = send_dtmf(rtp, AST_FRAME_DTMF_END); + f->samples = duration; + rtp->resp = 0; + rtp->lasteventendseqn = seqno; + } + rtp->dtmfcount = dtmftimeout; rtp->dtmfduration = duration; + return f; } @@ -1030,6 +1041,10 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp) unsigned int *rtpheader; struct rtpPayloadType rtpPT; + /* If time is up, kill it */ + if (rtp->send_digit) + ast_rtp_senddigit_continuation(rtp); + len = sizeof(sin); /* Cache where the header will go */ @@ -1172,13 +1187,13 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp) duration &= 0xFFFF; ast_verbose("Got RTP RFC2833 from %s:%d (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u, mark %d, event %08x, end %d, duration %-5.5d) \n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), payloadtype, seqno, timestamp, res - hdrlen, (mark?1:0), event, ((event_end & 0x80)?1:0), duration); } - if (rtp->lasteventseqn <= seqno || rtp->resp == 0 || (rtp->lasteventseqn >= 65530 && seqno <= 6)) { + if (rtp->lasteventseqn <= seqno || (rtp->lasteventseqn >= 65530 && seqno <= 6)) { f = process_rfc2833(rtp, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen, seqno); rtp->lasteventseqn = seqno; } } else if (rtpPT.code == AST_RTP_CISCO_DTMF) { /* It's really special -- process it the Cisco way */ - if (rtp->lasteventseqn <= seqno || rtp->resp == 0 || (rtp->lasteventseqn >= 65530 && seqno <= 6)) { + if (rtp->lasteventseqn <= seqno || (rtp->lasteventseqn >= 65530 && seqno <= 6)) { f = process_cisco_dtmf(rtp, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen); rtp->lasteventseqn = seqno; } @@ -1198,26 +1213,9 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp) rtp->rxseqno = seqno; - if (rtp->dtmfcount) { -#if 0 - printf("dtmfcount was %d\n", rtp->dtmfcount); -#endif - rtp->dtmfcount -= (timestamp - rtp->lastrxts); - if (rtp->dtmfcount < 0) - rtp->dtmfcount = 0; -#if 0 - if (dtmftimeout != rtp->dtmfcount) - printf("dtmfcount is %d\n", rtp->dtmfcount); -#endif - } + /* Record received timestamp as last received now */ rtp->lastrxts = timestamp; - /* Send any pending DTMF */ - if (rtp->resp && !rtp->dtmfcount) { - if (option_debug) - ast_log(LOG_DEBUG, "Sending pending DTMF\n"); - return send_dtmf(rtp); - } rtp->f.mallocd = 0; rtp->f.datalen = res - hdrlen; rtp->f.data = rtp->rawdata + hdrlen + AST_FRIENDLY_OFFSET; @@ -1962,35 +1960,42 @@ static unsigned int calc_txstamp(struct ast_rtp *rtp, struct timeval *delivery) return (unsigned int) ms; } -int ast_rtp_senddigit(struct ast_rtp *rtp, char digit) +/* Convert DTMF digit into something usable */ +static int digit_convert(char digit) +{ + if ((digit <= '9') && (digit >= '0')) + digit -= '0'; + else if (digit == '*') + digit = 10; + else if (digit == '#') + digit = 11; + else if ((digit >= 'A') && (digit <= 'D')) + digit = digit - 'A' + 12; + else if ((digit >= 'a') && (digit <= 'd')) + digit = digit - 'a' + 12; + else { + ast_log(LOG_WARNING, "Don't know how to represent '%c'\n", digit); + return -1; + } + return 0; +} + +/*! \brief Send begin frames for DTMF */ +int ast_rtp_senddigit_begin(struct ast_rtp *rtp, char digit) { unsigned int *rtpheader; - int hdrlen = 12; - int res; - int x; - int payload; + int hdrlen = 12, res = 0, i = 0, payload = 0; char data[256]; - if ((digit <= '9') && (digit >= '0')) - digit -= '0'; - else if (digit == '*') - digit = 10; - else if (digit == '#') - digit = 11; - else if ((digit >= 'A') && (digit <= 'D')) - digit = digit - 'A' + 12; - else if ((digit >= 'a') && (digit <= 'd')) - digit = digit - 'a' + 12; - else { - ast_log(LOG_WARNING, "Don't know how to represent '%c'\n", digit); + if (digit_convert(digit)) return -1; - } - payload = ast_rtp_lookup_code(rtp, 0, AST_RTP_DTMF); /* If we have no peer, return immediately */ - if (!rtp->them.sin_addr.s_addr) + if (!rtp->them.sin_addr.s_addr || !rtp->them.sin_port) return 0; + payload = ast_rtp_lookup_code(rtp, 0, AST_RTP_DTMF); + rtp->dtmfmute = ast_tvadd(ast_tvnow(), ast_tv(0, 500000)); /* Get a pointer to the header */ @@ -1999,51 +2004,111 @@ int ast_rtp_senddigit(struct ast_rtp *rtp, char digit) rtpheader[1] = htonl(rtp->lastdigitts); rtpheader[2] = htonl(rtp->ssrc); rtpheader[3] = htonl((digit << 24) | (0xa << 16) | (0)); - for (x = 0; x < 6; x++) { - if (rtp->them.sin_port && rtp->them.sin_addr.s_addr) { - res = sendto(rtp->s, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &rtp->them, sizeof(rtp->them)); - if (res < 0) - ast_log(LOG_ERROR, "RTP Transmission error to %s:%d: %s\n", - ast_inet_ntoa(rtp->them.sin_addr), - ntohs(rtp->them.sin_port), strerror(errno)); - if (rtp_debug_test_addr(&rtp->them)) - ast_verbose("Sent RTP DTMF packet to %s:%d (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n", - ast_inet_ntoa(rtp->them.sin_addr), - ntohs(rtp->them.sin_port), payload, rtp->seqno, rtp->lastdigitts, res - hdrlen); - } - /* Sequence number of last two end packets does not get incremented */ - if (x < 3) - rtp->seqno++; + + for (i = 0; i < 2; i++) { + res = sendto(rtp->s, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &rtp->them, sizeof(rtp->them)); + if (res < 0) + ast_log(LOG_ERROR, "RTP Transmission error to %s:%d: %s\n", + ast_inet_ntoa(rtp->them.sin_addr), + ntohs(rtp->them.sin_port), strerror(errno)); + if (rtp_debug_test_addr(&rtp->them)) + ast_verbose("Sent RTP DTMF packet to %s:%d (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n", + ast_inet_ntoa(rtp->them.sin_addr), + ntohs(rtp->them.sin_port), payload, rtp->seqno, rtp->lastdigitts, res - hdrlen); + /* Increment sequence number */ + rtp->seqno++; /* Clear marker bit and set seqno */ rtpheader[0] = htonl((2 << 30) | (payload << 16) | (rtp->seqno)); - /* For the last three packets, set the duration and the end bit */ - if (x == 2) { -#if 0 - /* No, this is wrong... Do not increment lastdigitts, that's not according - to the RFC, as best we can determine */ - rtp->lastdigitts++; /* or else the SPA3000 will click instead of beeping... */ - rtpheader[1] = htonl(rtp->lastdigitts); -#endif - /* Make duration 800 (100ms) */ - rtpheader[3] |= htonl((800)); - /* Set the End bit */ - rtpheader[3] |= htonl((1 << 23)); - } } - /*! \note Increment the digit timestamp by 120ms, to ensure that digits - sent sequentially with no intervening non-digit packets do not - get sent with the same timestamp, and that sequential digits - have some 'dead air' in between them - */ - rtp->lastdigitts += 960; - /* Increment the sequence number to reflect the last packet - that was sent - */ + + /* Since we received a begin, we can safely store the digit and disable any compensation */ + rtp->send_digit = digit; + rtp->send_payload = payload; + + return 0; +} + +/*! \brief Send continuation frame for DTMF */ +static int ast_rtp_senddigit_continuation(struct ast_rtp *rtp) +{ + unsigned int *rtpheader; + int hdrlen = 12, res = 0; + char data[256]; + + if (!rtp->them.sin_addr.s_addr || !rtp->them.sin_port) + return 0; + + /* Setup packet to send */ + rtpheader = (unsigned int *)data; + rtpheader[0] = htonl((2 << 30) | (1 << 23) | (rtp->send_payload << 16) | (rtp->seqno)); + rtpheader[1] = htonl(rtp->lastdigitts); + rtpheader[2] = htonl(rtp->ssrc); + rtpheader[3] = htonl((rtp->send_digit << 24) | (0xa << 16) | (0)); + rtpheader[0] = htonl((2 << 30) | (rtp->send_payload << 16) | (rtp->seqno)); + + /* Transmit */ + res = sendto(rtp->s, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &rtp->them, sizeof(rtp->them)); + if (res < 0) + ast_log(LOG_ERROR, "RTP Transmission error to %s:%d: %s\n", + ast_inet_ntoa(rtp->them.sin_addr), + ntohs(rtp->them.sin_port), strerror(errno)); + if (rtp_debug_test_addr(&rtp->them)) + ast_verbose("Sent RTP DTMF packet to %s:%d (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n", + ast_inet_ntoa(rtp->them.sin_addr), + ntohs(rtp->them.sin_port), rtp->send_payload, rtp->seqno, rtp->lastdigitts, res - hdrlen); + + /* Increment sequence number */ rtp->seqno++; + return 0; } -/* \brief Public function: Send an H.261 fast update request, some devices need this rather than SIP XML */ +/*! \brief Send end packets for DTMF */ +int ast_rtp_senddigit_end(struct ast_rtp *rtp, char digit) +{ + unsigned int *rtpheader; + int hdrlen = 12, res = 0, i = 0; + char data[256]; + + /* If no address, then bail out */ + if (!rtp->them.sin_addr.s_addr || !rtp->them.sin_port) + return 0; + + /* Convert our digit to the crazy RTP way */ + if (digit_convert(digit)) + return -1; + + rtpheader = (unsigned int *)data; + rtpheader[0] = htonl((2 << 30) | (1 << 23) | (rtp->send_payload << 16) | (rtp->seqno)); + rtpheader[1] = htonl(rtp->lastdigitts); + rtpheader[2] = htonl(rtp->ssrc); + rtpheader[3] = htonl((digit << 24) | (0xa << 16) | (0)); + /* Send duration to 100ms */ + rtpheader[3] |= htonl((800)); + /* Set end bit */ + rtpheader[3] |= htonl((1 << 23)); + rtpheader[0] = htonl((2 << 30) | (rtp->send_payload << 16) | (rtp->seqno)); + /* Send 3 termination packets */ + for (i = 0; i < 3; i++) { + res = sendto(rtp->s, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &rtp->them, sizeof(rtp->them)); + if (res < 0) + ast_log(LOG_ERROR, "RTP Transmission error to %s:%d: %s\n", + ast_inet_ntoa(rtp->them.sin_addr), + ntohs(rtp->them.sin_port), strerror(errno)); + if (rtp_debug_test_addr(&rtp->them)) + ast_verbose("Sent RTP DTMF packet to %s:%d (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n", + ast_inet_ntoa(rtp->them.sin_addr), + ntohs(rtp->them.sin_port), rtp->send_payload, rtp->seqno, rtp->lastdigitts, res - hdrlen); + } + rtp->send_digit = 0; + /* Increment lastdigitts */ + rtp->lastdigitts += 960; + rtp->seqno++; + + return res; +} + +/*! \brief Public function: Send an H.261 fast update request, some devices need this rather than SIP XML */ int ast_rtcp_send_h261fur(void *data) { struct ast_rtp *rtp = data; diff --git a/res/res_agi.c b/res/res_agi.c index a050d1c8da..bf51946f9f 100644 --- a/res/res_agi.c +++ b/res/res_agi.c @@ -1016,6 +1016,8 @@ static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, char break; case AST_FRAME_VIDEO: ast_writestream(fs, f); + default: + /* Ignore all other frames */ break; } ast_frfree(f); diff --git a/res/res_features.c b/res/res_features.c index 10faf00270..aba923fb8b 100644 --- a/res/res_features.c +++ b/res/res_features.c @@ -1439,9 +1439,9 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast if (aoh && aoh->flag == AST_OPTION_FLAG_REQUEST) ast_channel_setoption(other, ntohs(aoh->option), aoh->data, f->datalen - sizeof(struct ast_option_header), 0); } - } - /* check for '*', if we find it it's time to disconnect */ - if (f->frametype == AST_FRAME_DTMF) { + } else if (f->frametype == AST_FRAME_DTMF_BEGIN) { + /* eat it */ + } else if (f->frametype == AST_FRAME_DTMF) { char *featurecode; int sense; -- GitLab