diff --git a/frame.c b/frame.c index d5c60bfc33..e37e816b3b 100755 --- a/frame.c +++ b/frame.c @@ -11,6 +11,7 @@ * the GNU General Public License */ +#include #include #include #include @@ -34,23 +35,27 @@ struct ast_smoother { int size; int format; int readdata; - float timeperbyte; + float samplesperbyte; struct ast_frame f; char data[SMOOTHER_SIZE]; char framedata[SMOOTHER_SIZE + AST_FRIENDLY_OFFSET]; int len; }; +void ast_smoother_reset(struct ast_smoother *s, int size) +{ + memset(s, 0, sizeof(struct ast_smoother)); + s->size = size; +} + struct ast_smoother *ast_smoother_new(int size) { struct ast_smoother *s; if (size < 1) return NULL; s = malloc(sizeof(struct ast_smoother)); - if (s) { - memset(s, 0, sizeof(struct ast_smoother)); - s->size = size; - } + if (s) + ast_smoother_reset(s, size); return s; } @@ -62,7 +67,7 @@ int ast_smoother_feed(struct ast_smoother *s, struct ast_frame *f) } if (!s->format) { s->format = f->subclass; - s->timeperbyte = (float)f->timelen / (float)f->datalen; + s->samplesperbyte = (float)f->samples / (float)f->datalen; } else if (s->format != f->subclass) { ast_log(LOG_WARNING, "Smoother was working on %d format frames, now trying to feed %d?\n", s->format, f->subclass); return -1; @@ -88,7 +93,7 @@ struct ast_frame *ast_smoother_read(struct ast_smoother *s) s->f.data = s->framedata + AST_FRIENDLY_OFFSET; s->f.offset = AST_FRIENDLY_OFFSET; s->f.datalen = s->size; - s->f.timelen = s->size * s->timeperbyte; + s->f.samples = s->size * s->samplesperbyte; /* Fill Data */ memcpy(s->f.data, s->data, s->size); s->len -= s->size; @@ -169,7 +174,7 @@ struct ast_frame *ast_frisolate(struct ast_frame *fr) out->frametype = fr->frametype; out->subclass = fr->subclass; out->datalen = 0; - out->timelen = fr->timelen; + out->samples = fr->samples; out->offset = 0; out->src = NULL; out->data = NULL; @@ -299,6 +304,8 @@ int ast_getformatbyname(char *name) return AST_FORMAT_LPC10; else if (!strcasecmp(name, "adpcm")) return AST_FORMAT_ADPCM; + else if (!strcasecmp(name, "g729")) + return AST_FORMAT_G729A; else if (!strcasecmp(name, "speex")) return AST_FORMAT_SPEEX; else if (!strcasecmp(name, "all")) diff --git a/include/asterisk/frame.h b/include/asterisk/frame.h index f5e1479b65..1916a714b5 100755 --- a/include/asterisk/frame.h +++ b/include/asterisk/frame.h @@ -33,8 +33,8 @@ struct ast_frame { int subclass; /*! Length of data */ int datalen; - /*! Amount of time associated with this frame */ - int timelen; + /*! Number of 8khz samples in this frame */ + int samples; /*! Was the data malloc'd? i.e. should we free it when we discard the frame? */ int mallocd; /*! How far into "data" the data really starts */ @@ -165,6 +165,10 @@ struct ast_frame_chain { #define AST_CONTROL_WINK 10 /*! Set a low-level option */ #define AST_CONTROL_OPTION 11 +/*! Key Radio */ +#define AST_CONTROL_RADIO_KEY 12 +/*! Un-Key Radio */ +#define AST_CONTROL_RADIO_UNKEY 13 /* Option identifiers and flags */ #define AST_OPTION_FLAG_REQUEST 0 @@ -181,6 +185,9 @@ struct ast_frame_chain { /* Put a compatible channel into TDD (TTY for the hearing-impared) mode */ #define AST_OPTION_TDD 2 +/* Relax the parameters for DTMF reception (mainly for radio use) */ +#define AST_OPTION_RELAXDTMF 3 + struct ast_option_header { /* Always keep in network byte order */ #if __BYTE_ORDER == __BIG_ENDIAN @@ -280,6 +287,7 @@ struct ast_smoother; extern struct ast_smoother *ast_smoother_new(int bytes); extern void ast_smoother_free(struct ast_smoother *s); +extern void ast_smoother_reset(struct ast_smoother *s, int bytes); extern int ast_smoother_feed(struct ast_smoother *s, struct ast_frame *f); extern struct ast_frame *ast_smoother_read(struct ast_smoother *s); diff --git a/rtp.c b/rtp.c index 5162193f14..2a015b6e4a 100755 --- a/rtp.c +++ b/rtp.c @@ -31,6 +31,11 @@ #include #include +#define TYPE_SILENCE 0x2 +#define TYPE_HIGH 0x0 +#define TYPE_LOW 0x1 +#define TYPE_MASK 0x3 + static int dtmftimeout = 300; /* 300 samples */ struct ast_rtp { @@ -41,6 +46,7 @@ struct ast_rtp { unsigned int ssrc; unsigned int lastts; unsigned int lastrxts; + int lasttxformat; int dtmfcount; struct sockaddr_in us; struct sockaddr_in them; @@ -56,6 +62,40 @@ struct ast_rtp { }; +static int g723_len(unsigned char buf) +{ + switch(buf & TYPE_MASK) { + case TYPE_MASK: + case TYPE_SILENCE: + return 4; + break; + case TYPE_HIGH: + return 24; + break; + case TYPE_LOW: + return 20; + break; + default: + ast_log(LOG_WARNING, "Badly encoded frame (%d)\n", buf & TYPE_MASK); + } + return -1; +} + +static int g723_samples(unsigned char *buf, int maxlen) +{ + int pos = 0; + int samples = 0; + int res; + while(pos < maxlen) { + res = g723_len(buf[pos]); + if (res < 0) + break; + samples += 240; + pos += res; + } + return samples; +} + void ast_rtp_set_data(struct ast_rtp *rtp, void *data) { rtp->data = data; @@ -72,7 +112,7 @@ static void send_dtmf(struct ast_rtp *rtp) rtp->f.frametype = AST_FRAME_DTMF; rtp->f.subclass = rtp->resp; rtp->f.datalen = 0; - rtp->f.timelen = 0; + rtp->f.samples = 0; rtp->f.mallocd = 0; rtp->f.src = "RTP"; rtp->resp = 0; @@ -185,6 +225,9 @@ static int rtpread(int *id, int fd, short events, void *cbdata) } else if (payloadtype == 121) { /* CISCO proprietary DTMF bridge */ process_type121(rtp, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen); + } else if (payloadtype == 100) { + /* CISCO's notso proprietary DTMF bridge */ + process_rfc2833(rtp, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen); } else { ast_log(LOG_NOTICE, "Unknown RTP codec %d received\n", payloadtype); } @@ -222,22 +265,25 @@ static int rtpread(int *id, int fd, short events, void *cbdata) switch(rtp->f.subclass) { case AST_FORMAT_ULAW: case AST_FORMAT_ALAW: - rtp->f.timelen = rtp->f.datalen / 8; + rtp->f.samples = rtp->f.datalen; break; case AST_FORMAT_SLINEAR: - rtp->f.timelen = rtp->f.datalen / 16; + rtp->f.samples = rtp->f.datalen / 2; break; case AST_FORMAT_GSM: - rtp->f.timelen = 20 * (rtp->f.datalen / 33); + rtp->f.samples = 160 * (rtp->f.datalen / 33); break; case AST_FORMAT_ADPCM: - rtp->f.timelen = rtp->f.datalen / 4; + rtp->f.samples = rtp->f.datalen * 2; break; case AST_FORMAT_G729A: - rtp->f.timelen = rtp->f.datalen; + rtp->f.samples = rtp->f.datalen * 8; + break; + case AST_FORMAT_G723_1: + rtp->f.samples = g723_samples(rtp->f.data, rtp->f.datalen); break; default: - ast_log(LOG_NOTICE, "Unable to calculate timelen for format %d\n", rtp->f.subclass); + ast_log(LOG_NOTICE, "Unable to calculate samples for format %d\n", rtp->f.subclass); break; } rtp->f.src = "RTP"; @@ -330,12 +376,27 @@ struct ast_rtp *ast_rtp_new(struct sched_context *sched, struct io_context *io) return rtp; } +int ast_rtp_settos(struct ast_rtp *rtp, int tos) +{ + int res; + if ((res = setsockopt(rtp->s, SOL_IP, IP_TOS, &tos, sizeof(tos)))) + ast_log(LOG_WARNING, "Unable to set TOS to %d\n", tos); + return res; +} + void ast_rtp_set_peer(struct ast_rtp *rtp, struct sockaddr_in *them) { rtp->them.sin_port = them->sin_port; rtp->them.sin_addr = them->sin_addr; } +void ast_rtp_get_peer(struct ast_rtp *rtp, struct sockaddr_in *them) +{ + them->sin_family = AF_INET; + them->sin_port = rtp->them.sin_port; + them->sin_addr = rtp->them.sin_addr; +} + void ast_rtp_get_us(struct ast_rtp *rtp, struct sockaddr_in *us) { memcpy(us, &rtp->us, sizeof(rtp->us)); @@ -365,6 +426,67 @@ static unsigned int calc_txstamp(struct ast_rtp *rtp) return ms; } +int ast_rtp_senddigit(struct ast_rtp *rtp, char digit) +{ + unsigned int *rtpheader; + int hdrlen = 12; + int res; + int ms; + int pred; + int x; + 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); + return -1; + } + + + /* If we have no peer, return immediately */ + if (!rtp->them.sin_addr.s_addr) + return 0; + + ms = calc_txstamp(rtp); + /* Default prediction */ + pred = ms * 8; + + /* Get a pointer to the header */ + rtpheader = (unsigned int *)data; + rtpheader[0] = htonl((2 << 30) | (1 << 23) | (101 << 16) | (rtp->seqno++)); + rtpheader[1] = htonl(rtp->lastts); + rtpheader[2] = htonl(rtp->ssrc); + rtpheader[3] = htonl((digit << 24) | (0xa << 16) | (0)); + for (x=0;x<4;x++) { + if (rtp->them.sin_port && rtp->them.sin_addr.s_addr) { + res = sendto(rtp->s, (void *)rtpheader, hdrlen + 4, 0, &rtp->them, sizeof(rtp->them)); + if (res <0) + ast_log(LOG_NOTICE, "RTP Transmission error to %s:%d: %s\n", inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port), strerror(errno)); + #if 0 + printf("Sent %d bytes of RTP data to %s:%d\n", res, inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port)); + #endif + } + if (x ==0) { + /* Clear marker bit and increment seqno */ + rtpheader[0] = htonl((2 << 30) | (101 << 16) | (rtp->seqno++)); + /* Make duration 240 */ + rtpheader[3] |= htonl((240)); + /* Set the End bit for the last 3 */ + rtpheader[3] |= htonl((1 << 23)); + } + } + return 0; +} + static int ast_rtp_raw_write(struct ast_rtp *rtp, struct ast_frame *f, int codec) { unsigned int *rtpheader; @@ -387,6 +509,12 @@ static int ast_rtp_raw_write(struct ast_rtp *rtp, struct ast_frame *f, int codec case AST_FORMAT_G729A: pred = rtp->lastts + f->datalen * 8; break; + case AST_FORMAT_GSM: + pred = rtp->lastts + f->datalen * 20 / 33; + break; + case AST_FORMAT_G723_1: + pred = rtp->lastts + g723_samples(f->data, f->datalen); + break; default: ast_log(LOG_WARNING, "Not sure about timestamp format for codec format %d\n", f->subclass); } @@ -423,8 +551,12 @@ int ast_rtp_write(struct ast_rtp *rtp, struct ast_frame *_f) int codec; int hdrlen = 12; - /* Make sure we have enough space for RTP header */ + + /* If we have no peer, return immediately */ + if (!rtp->them.sin_addr.s_addr) + return 0; + /* Make sure we have enough space for RTP header */ if (_f->frametype != AST_FRAME_VOICE) { ast_log(LOG_WARNING, "RTP can only send voice\n"); return -1; @@ -436,6 +568,15 @@ int ast_rtp_write(struct ast_rtp *rtp, struct ast_frame *_f) return -1; } + if (rtp->lasttxformat != _f->subclass) { + /* New format, reset the smoother */ + ast_log(LOG_DEBUG, "Ooh, format changed from %d to %d\n", rtp->lasttxformat, _f->subclass); + rtp->lasttxformat = _f->subclass; + if (rtp->smoother) + ast_smoother_free(rtp->smoother); + rtp->smoother = NULL; + } + switch(_f->subclass) { case AST_FORMAT_ULAW: @@ -465,7 +606,18 @@ int ast_rtp_write(struct ast_rtp *rtp, struct ast_frame *_f) while((f = ast_smoother_read(rtp->smoother))) ast_rtp_raw_write(rtp, f, codec); break; - + case AST_FORMAT_GSM: + if (!rtp->smoother) { + rtp->smoother = ast_smoother_new(33); + } + if (!rtp->smoother) { + ast_log(LOG_WARNING, "Unable to create GSM smoother :(\n"); + return -1; + } + ast_smoother_feed(rtp->smoother, _f); + while((f = ast_smoother_read(rtp->smoother))) + ast_rtp_raw_write(rtp, f, codec); + break; default: ast_log(LOG_WARNING, "Not sure about sending format %d packets\n", _f->subclass); if (_f->offset < hdrlen) {