summaryrefslogtreecommitdiff
path: root/main
diff options
context:
space:
mode:
authorJoshua Colp <jcolp@digium.com>2006-08-31 01:59:02 +0000
committerJoshua Colp <jcolp@digium.com>2006-08-31 01:59:02 +0000
commitc6977b9983db4f58446bfbc65a5b028cda8244ee (patch)
tree95a924d57b906428b26d398758facf98b92bdcb2 /main
parent5418a7a1a8326544f2275819c93649c97261a5f3 (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: https://origsvn.digium.com/svn/asterisk/trunk@41507 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'main')
-rw-r--r--main/app.c11
-rw-r--r--main/channel.c133
-rw-r--r--main/dsp.c63
-rw-r--r--main/file.c7
-rw-r--r--main/rtp.c269
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;