Ross Finlayson
2003-Feb-26 05:45 UTC
[Asterisk-Users] [PATCH] To fix dynamic RTP payload type handling (for SIP)
Index: rtp.c ==================================================================RCS file: /usr/cvsroot/asterisk/rtp.c,v retrieving revision 1.26 diff -u -r1.26 rtp.c --- rtp.c 24 Feb 2003 14:39:44 -0000 1.26 +++ rtp.c 26 Feb 2003 11:25:08 -0000 @@ -283,28 +283,24 @@ #endif rtp->f.frametype = AST_FRAME_VOICE; rtp->f.subclass = rtp2ast(payloadtype); - if (rtp->f.subclass < 0) { - f = NULL; - if (payloadtype == 101) { - /* It's special -- rfc2833 process it */ - f = process_rfc2833(rtp, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen); - } else if (payloadtype == 121) { - /* CISCO proprietary DTMF bridge */ - f = process_type121(rtp, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen); - } else if (payloadtype == 100) { - /* CISCO's notso proprietary DTMF bridge */ - f = process_rfc2833(rtp, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen); - } else if (payloadtype == 13) { - f = process_rfc3389(rtp, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen); - } else { - ast_log(LOG_NOTICE, "Unknown RTP codec %d received\n", payloadtype); - } - if (f) - return f; - else - return &null_frame; - } else - rtp->lastrxformat = rtp->f.subclass; + // Check for special in-band data (DTMF or Comfort Noise): + if (rtp->f.subclass == AST_FORMAT_DTMF) { + /* It's special -- rfc2833 process it */ + f = process_rfc2833(rtp, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen); + if (f) return f; else return &null_frame; + } else if (rtp->f.subclass == AST_FORMAT_DTMF_CISCO) { + /* CISCO proprietary DTMF bridge */ + f = process_type121(rtp, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen); + if (f) return f; else return &null_frame; + } else if (rtp->f.subclass == AST_FORMAT_CN) { + /* Comfort Noise */ + f = process_rfc3389(rtp, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen); + if (f) return f; else return &null_frame; + } else if (rtp->f.subclass < 0) { + ast_log(LOG_NOTICE, "Unknown RTP codec %d received\n", payloadtype); + return &null_frame; + } + rtp->lastrxformat = rtp->f.subclass; if (!rtp->lastrxts) rtp->lastrxts = timestamp; @@ -360,48 +356,122 @@ return &rtp->f; } +// The following array defines the MIME type (and subtype) for each +// defined "AST_FORMAT_..." value. static struct { - int rtp; - int ast; - char *label; -} cmap[] = { - { 0, AST_FORMAT_ULAW, "PCMU" }, - { 3, AST_FORMAT_GSM, "GSM" }, - { 4, AST_FORMAT_G723_1, "G723" }, - { 5, AST_FORMAT_ADPCM, "ADPCM" }, - { 8, AST_FORMAT_ALAW, "PCMA" }, - { 18, AST_FORMAT_G729A, "G729" }, + int ast; + char* type; + char* subtype; +} mimeTypes[] = { + {AST_FORMAT_G723_1, "audio", "G723"}, + {AST_FORMAT_GSM, "audio", "GSM"}, + {AST_FORMAT_ULAW, "audio", "PCMU"}, + {AST_FORMAT_ALAW, "audio", "PCMA"}, + {AST_FORMAT_MP3, "audio", "MPA"}, + {AST_FORMAT_ADPCM, "audio", "DVI4"}, + {AST_FORMAT_SLINEAR, "audio", "L16"}, + {AST_FORMAT_LPC10, "audio", "LPC"}, + {AST_FORMAT_G729A, "audio", "G729"}, + {AST_FORMAT_SPEEX, "audio", "SPEEX"}, + {AST_FORMAT_DTMF, "audio", "TELEPHONE-EVENT"}, + {AST_FORMAT_DTMF_CISCO, "audio", "TELEPHONE-EVENT-CISCO"/*Fix this!*/}, + {AST_FORMAT_CN, "audio", "CN"}, + {AST_FORMAT_JPEG, "video", "JPEG"}, + {AST_FORMAT_PNG, "video", "PNG"}, + {AST_FORMAT_H261, "video", "H261"}, + {AST_FORMAT_H263, "video", "H263"}, }; -int rtp2ast(int id) -{ - int x; - for (x=0;x<sizeof(cmap) / sizeof(cmap[0]); x++) { - if (cmap[x].rtp == id) - return cmap[x].ast; - } - return -1; +#define MAX_RTP_PT 256 + +// Static (i.e., well-known) RTP payload types for our "AST_FORMAT..."s: +static int static_RTP_PT[MAX_RTP_PT] = { + [0] = AST_FORMAT_ULAW, + [3] = AST_FORMAT_GSM, + [4] = AST_FORMAT_G723_1, + [5] = AST_FORMAT_ADPCM, // 8 kHz + [6] = AST_FORMAT_ADPCM, // 16 kHz + [7] = AST_FORMAT_LPC10, + [8] = AST_FORMAT_ALAW, + [10] = AST_FORMAT_SLINEAR, // 2 channels + [11] = AST_FORMAT_SLINEAR, // 1 channel + [13] = AST_FORMAT_CN, + [14] = AST_FORMAT_MP3, + [16] = AST_FORMAT_ADPCM, // 11.025 kHz + [17] = AST_FORMAT_ADPCM, // 22.050 kHz + [18] = AST_FORMAT_G729A, + [26] = AST_FORMAT_JPEG, + [31] = AST_FORMAT_H261, + [34] = AST_FORMAT_H263, +}; + +// The set of RTP payload types that the current caller has requested: +static int current_RTP_PT[MAX_RTP_PT]; + +void rtp_pt_init() { + int i; + for (i = 0; i < MAX_RTP_PT; ++i) { + current_RTP_PT[i] = 0; + } } -int ast2rtp(int id) -{ - int x; - for (x=0;x<sizeof(cmap) / sizeof(cmap[0]); x++) { - if (cmap[x].ast == id) - return cmap[x].rtp; - } - return -1; +// Make a note of a RTP payload type that was seen in a SDP "m=" line. +// By default, use the well-known value for this type (although it may +// still be set to a different value by a subsequent "a=rtpmap:" line): +void rtp_set_m_type(int pt) { + if (pt < 0 || pt > MAX_RTP_PT) return; // bogus payload type + + if (static_RTP_PT[pt] != 0) { + current_RTP_PT[pt] = static_RTP_PT[pt]; + } +} + +// Make a note of a RTP payload type (with MIME type) that was seen in +// a SDP "a=rtpmap:" line. +void rtp_set_rtpmap_type(int pt, char* mimeType, char* mimeSubtype) { + int i; + + if (pt < 0 || pt > MAX_RTP_PT) return; // bogus payload type + + for (i = 0; i < sizeof mimeTypes/sizeof mimeTypes[0]; ++i) { + if (strcmp(mimeSubtype, mimeTypes[i].subtype) == 0 && + strcmp(mimeType, mimeTypes[i].type) == 0) { + current_RTP_PT[pt] = mimeTypes[i].ast; + return; + } + } +} + +// Return the union of all of the codecs that were set by rtp_set...() calls +int rtp_get_current_codecs() { + int result = 0, pt; + for (pt = 0; pt < MAX_RTP_PT; ++pt) result |= current_RTP_PT[pt]; + + return result; } -char *ast2rtpn(int id) -{ - int x; - for (x=0;x<sizeof(cmap) / sizeof(cmap[0]); x++) { - if (cmap[x].ast == id) - return cmap[x].label; - } - return ""; +int rtp2ast(int pt) { + if (pt < 0 || pt > MAX_RTP_PT) return -1; // bogus payload type + return current_RTP_PT[pt]; +} + +int ast2rtp(int id) { + int pt; + for (pt = 0; pt < MAX_RTP_PT; ++pt) { + if (current_RTP_PT[pt] == id) return pt; + } + return -1; } + +char* ast2rtpn(int id) { + int i; + + for (i = 0; i < sizeof mimeTypes/sizeof mimeTypes[0]; ++i) { + if (mimeTypes[i].ast == id) return mimeTypes[i].subtype; + } + return ""; +} + struct ast_rtp *ast_rtp_new(struct sched_context *sched, struct io_context *io) { struct ast_rtp *rtp; Index: channels/chan_sip.c ==================================================================RCS file: /usr/cvsroot/asterisk/channels/chan_sip.c,v retrieving revision 1.129 diff -u -r1.129 chan_sip.c --- channels/chan_sip.c 24 Feb 2003 15:20:42 -0000 1.129 +++ channels/chan_sip.c 26 Feb 2003 11:25:10 -0000 @@ -974,21 +974,41 @@ { "Via", "v" }, }; -static char *get_sdp(struct sip_request *req, char *name) -{ - int x; - int len = strlen(name); - char *r; - for (x=0;x<req->lines;x++) { - if (!strncasecmp(req->line[x], name, len) && - (req->line[x][len] == '=')) { - r = req->line[x] + len + 1; - while(*r && (*r < 33)) - r++; - return r; - } - } - return ""; +static char* get_sdp_by_line(char* line, char *name, int nameLen) { + if (strncasecmp(line, name, nameLen) == 0 && line[nameLen] == '=') { + char* r = line + nameLen + 1; + while (*r && (*r < 33)) ++r; + return r; + } + + return ""; +} + +static char *get_sdp(struct sip_request *req, char *name) { + int x; + int len = strlen(name); + char *r; + + for (x=0; x<req->lines; x++) { + r = get_sdp_by_line(req->line[x], name, len); + if (r[0] != '\0') return r; + } + return ""; +} + +static int sdpLineNum_iterator; +void sdpLineNum_iterator_init() { + sdpLineNum_iterator = 0; +} + +static char* get_sdp_iterate(struct sip_request *req, char *name) { + int len = strlen(name); + char *r; + while (sdpLineNum_iterator < req->lines) { + r = get_sdp_by_line(req->line[sdpLineNum_iterator++], name, len); + if (r[0] != '\0') return r; + } + return ""; } static char *__get_header(struct sip_request *req, char *name, int *start) @@ -1288,6 +1308,7 @@ { char *m; char *c; + char *a; char host[258]; int len = -1; int portno; @@ -1329,21 +1350,38 @@ #if 0 printf("Peer RTP is at port %s:%d\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); #endif - peercapability = 0; + // Scan through the RTP payload types specified in a "m=" line: + rtp_pt_init(); codecs = m + len; while(strlen(codecs)) { if (sscanf(codecs, "%d %n", &codec, &len) != 1) { ast_log(LOG_WARNING, "Error in codec string '%s'\n", codecs); return -1; } -#if 0 - printf("Codec: %d\n", codec); -#endif - codec = rtp2ast(codec); - if (codec > -1) - peercapability |= codec; + rtp_set_m_type(codec); codecs += len; } + + // Next, scan through each "a=rtpmap:" line, noting each + // specified RTP payload type (with corresponding MIME subtype): + sdpLineNum_iterator_init(); + while ((a = get_sdp_iterate(req, "a"))[0] != '\0') { + char mimeSubtype[100]; + int subtypeLen, i; + if (sscanf(a, "rtpmap: %u %[^/]/", &codec, mimeSubtype) != 2) continue; + // Note: should really look at the 'freq' and '#chans' params too + mimeSubtype[sizeof mimeSubtype - 1] = '\0'; // in case... + subtypeLen = strlen(mimeSubtype); + // Convert the MIME subtype to upper case, for ease of searching: + for (i = 0; i < subtypeLen; ++i) { + mimeSubtype[i] = toupper(mimeSubtype[i]); + } + rtp_set_rtpmap_type(codec, "audio", mimeSubtype); + } + + // Now gather all of the codecs that were asked for: + peercapability = rtp_get_current_codecs(); + capability |= AST_FORMAT_DTMF|AST_FORMAT_DTMF_CISCO; // include DTMF p->capability = capability & peercapability; if (sipdebug) ast_verbose("Capabilities: us - %d, them - %d, combined - %d\n", @@ -1722,13 +1760,17 @@ strcat(m, costr); snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/8000\r\n", codec, ast2rtpn(x)); strcat(a, costr); + if (x == AST_FORMAT_DTMF || + x == AST_FORMAT_DTMF_CISCO) { + /* Indicate we support DTMF only... Not sure about 16, but MSN supports it so dang it, we will too... */ + snprintf(costr, sizeof costr, "a=fmtp:%d 0-16\r\n", + codec); + strcat(a, costr); + } } } } - strcat(m, " 101\r\n"); - strcat(a, "a=rtpmap:101 telephone-event/8000\r\n"); - /* Indicate we support DTMF only... Not sure about 16, but MSN supports it so dang it, we will too... */ - strcat(a, "a=fmtp:101 0-16\r\n"); + strcat(m, "\r\n"); len = strlen(v) + strlen(s) + strlen(o) + strlen(c) + strlen(t) + strlen(m) + strlen(a); snprintf(costr, sizeof(costr), "%d", len); add_header(resp, "Content-Type", "application/sdp"); Index: include/asterisk/frame.h ==================================================================RCS file: /usr/cvsroot/asterisk/include/asterisk/frame.h,v retrieving revision 1.41 diff -u -r1.41 frame.h --- include/asterisk/frame.h 5 Feb 2003 19:26:49 -0000 1.41 +++ include/asterisk/frame.h 26 Feb 2003 11:25:11 -0000 @@ -131,6 +131,12 @@ #define AST_FORMAT_G729A (1 << 8) /*! SpeeX Free Compression */ #define AST_FORMAT_SPEEX (1 << 9) +/*! DTMF (in RTP) */ +#define AST_FORMAT_DTMF (1 << 10) +/*! Cisco's proprietary DTMF variant (in RTP) */ +#define AST_FORMAT_DTMF_CISCO (1 << 11) +/*! Comfort Noise (RFC 3389) (in RTP) */ +#define AST_FORMAT_CN (1 << 12) /*! Maximum audio format */ #define AST_FORMAT_MAX_AUDIO (1 << 15) /*! JPEG Images */ Index: include/asterisk/rtp.h ==================================================================RCS file: /usr/cvsroot/asterisk/include/asterisk/rtp.h,v retrieving revision 1.7 diff -u -r1.7 rtp.h --- include/asterisk/rtp.h 15 Feb 2003 23:41:23 -0000 1.7 +++ include/asterisk/rtp.h 26 Feb 2003 11:25:11 -0000 @@ -61,6 +61,14 @@ int ast_rtp_settos(struct ast_rtp *rtp, int tos); +void rtp_pt_init(void); + +void rtp_set_m_type(int pt); + +void rtp_set_rtpmap_type(int pt, char* mimeType, char* mimeSubtype); + +int rtp_get_current_codecs(void); + int ast2rtp(int id); int rtp2ast(int id);