diff options
author | file <file@f38db490-d61c-443f-a65b-d21fe96a405b> | 2006-08-31 01:59:02 +0000 |
---|---|---|
committer | file <file@f38db490-d61c-443f-a65b-d21fe96a405b> | 2006-08-31 01:59:02 +0000 |
commit | 3f22aa53af0a7bb3efb280a1adda11f00a7f68fc (patch) | |
tree | 95a924d57b906428b26d398758facf98b92bdcb2 /main | |
parent | 0fcb352fba60344329a582f891ddd4433f48c6fa (diff) |
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: http://svn.digium.com/svn/asterisk/trunk@41507 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'main')
-rw-r--r-- | main/app.c | 11 | ||||
-rw-r--r-- | main/channel.c | 133 | ||||
-rw-r--r-- | main/dsp.c | 63 | ||||
-rw-r--r-- | main/file.c | 7 | ||||
-rw-r--r-- | main/rtp.c | 269 |
5 files changed, 310 insertions, 173 deletions
diff --git a/main/app.c b/main/app.c index 1099feaea..b6a7f73a9 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 61df3d243..6ba79eea6 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 7e62c8bf7..2992031b4 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 365d23302..48c166069 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 5703d7140..b53ad405b 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; |