diff options
-rw-r--r-- | channels/chan_h323.c | 9 | ||||
-rw-r--r-- | channels/chan_sip.c | 166 | ||||
-rw-r--r-- | formats/format_siren14.c | 139 | ||||
-rw-r--r-- | formats/format_siren7.c | 138 | ||||
-rw-r--r-- | include/asterisk/frame.h | 20 | ||||
-rw-r--r-- | include/asterisk/rtp.h | 58 | ||||
-rw-r--r-- | main/frame.c | 33 | ||||
-rw-r--r-- | main/rtp.c | 156 |
8 files changed, 579 insertions, 140 deletions
diff --git a/channels/chan_h323.c b/channels/chan_h323.c index f327b9b20..051166d4b 100644 --- a/channels/chan_h323.c +++ b/channels/chan_h323.c @@ -1922,15 +1922,6 @@ static struct rtp_info *external_rtp_create(unsigned call_reference, const char return info; } -/* - * Definition taken from rtp.c for rtpPayloadType because we need it here. - */ - -struct rtpPayloadType { - int isAstFormat; /* whether the following code is an AST_FORMAT */ - int code; -}; - /*! \brief * Call-back function passing remote ip/port information from H.323 to asterisk * diff --git a/channels/chan_sip.c b/channels/chan_sip.c index f0a7fb9e3..a81b56289 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -2286,10 +2286,10 @@ static const char* get_sdp_iterate(int* start, struct sip_request *req, const ch static const char *get_sdp(struct sip_request *req, const char *name); static int find_sdp(struct sip_request *req); static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action); -static void add_codec_to_sdp(const struct sip_pvt *p, int codec, int sample_rate, +static void add_codec_to_sdp(const struct sip_pvt *p, int codec, struct ast_str **m_buf, struct ast_str **a_buf, int debug, int *min_packet_size); -static void add_noncodec_to_sdp(const struct sip_pvt *p, int format, int sample_rate, +static void add_noncodec_to_sdp(const struct sip_pvt *p, int format, struct ast_str **m_buf, struct ast_str **a_buf, int debug); static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int oldsdp); @@ -7671,22 +7671,17 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action iterator = req->sdp_start; while ((a = get_sdp_iterate(&iterator, req, "a"))[0] != '\0') { char mimeSubtype[128]; + char fmtp_string[64]; + unsigned int sample_rate; + if (option_debug > 1) { int breakout = FALSE; - + /* If we're debugging, check for unsupported sdp options */ if (!strncasecmp(a, "rtcp:", (size_t) 5)) { if (debug) ast_verbose("Got unsupported a:rtcp in SDP offer \n"); breakout = TRUE; - } else if (!strncasecmp(a, "fmtp:", (size_t) 5)) { - /* Format parameters: Not supported */ - /* Note: This is used for codec parameters, like bitrate for - G722 and video formats for H263 and H264 - See RFC2327 for an example */ - if (debug) - ast_verbose("Got unsupported a:fmtp in SDP offer \n"); - breakout = TRUE; } else if (!strncasecmp(a, "framerate:", (size_t) 10)) { /* Video stuff: Not supported */ if (debug) @@ -7706,21 +7701,29 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action if (breakout) /* We have a match, skip to next header */ continue; } + if (!strcasecmp(a, "sendonly")) { if (sendonly == -1) sendonly = 1; continue; - } else if (!strcasecmp(a, "inactive")) { + } + + if (!strcasecmp(a, "inactive")) { if (sendonly == -1) sendonly = 2; continue; - } else if (!strcasecmp(a, "sendrecv")) { + } + + if (!strcasecmp(a, "sendrecv")) { if (sendonly == -1) sendonly = 0; continue; - } else if (strlen(a) > 5 && !strncasecmp(a, "ptime", 5)) { + } + + if (!strncasecmp(a, "ptime", 5)) { char *tmp = strrchr(a, ':'); long int framing = 0; + if (tmp) { tmp++; framing = strtol(tmp, NULL, 10); @@ -7744,8 +7747,9 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action ast_rtp_codec_setpref(p->rtp, pref); } continue; - - } else if (!strncmp(a, red_fmtp, strlen(red_fmtp))) { + } + + if (!strncmp(a, red_fmtp, strlen(red_fmtp))) { /* count numbers of generations in fmtp */ red_cp = &red_fmtp[strlen(red_fmtp)]; strncpy(red_fmtp, a, 100); @@ -7757,15 +7761,59 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action red_cp = strtok(NULL, "/"); } red_cp = red_fmtp; + continue; + } + + if (sscanf(a, "fmtp: %u %63s", &codec, fmtp_string) == 2) { + struct rtpPayloadType payload; + unsigned int handled = 0; + + payload = ast_rtp_lookup_pt(newaudiortp, codec); + if (!payload.code) { + /* it wasn't found, try the video rtp */ + payload = ast_rtp_lookup_pt(newvideortp, codec); + } + if (payload.code && payload.isAstFormat) { + unsigned int bit_rate; + + switch (payload.code) { + case AST_FORMAT_SIREN7: + if (sscanf(fmtp_string, "bitrate=%u", &bit_rate) == 1) { + if (bit_rate != 32000) { + ast_log(LOG_WARNING, "Got Siren7 offer at %d bps, but only 32000 bps supported; ignoring.\n", bit_rate); + ast_rtp_unset_m_type(newaudiortp, codec); + } else { + handled = 1; + } + } + break; + case AST_FORMAT_SIREN14: + if (sscanf(fmtp_string, "bitrate=%u", &bit_rate) == 1) { + if (bit_rate != 48000) { + ast_log(LOG_WARNING, "Got Siren14 offer at %d bps, but only 48000 bps supported; ignoring.\n", bit_rate); + ast_rtp_unset_m_type(newaudiortp, codec); + } else { + handled = 1; + } + } + break; + } + } + + if (!handled) { + ast_debug(1, "Got unsupported a:%s in SDP offer\n", a); + } + continue; + } - } else if (sscanf(a, "rtpmap: %u %127[^/]/", &codec, mimeSubtype) == 2) { + if (sscanf(a, "rtpmap: %u %127[^/]/%u", &codec, mimeSubtype, &sample_rate) == 3) { /* We have a rtpmap to handle */ if (last_rtpmap_codec < SDP_MAX_RTPMAP_CODECS) { - /* Note: should really look at the 'freq' and '#chans' params too */ + /* Note: should really look at the '#chans' params too */ /* Note: This should all be done in the context of the m= above */ if (!strncasecmp(mimeSubtype, "H26", 3) || !strncasecmp(mimeSubtype, "MP4", 3)) { /* Video */ - if(ast_rtp_set_rtpmap_type(newvideortp, codec, "video", mimeSubtype, 0) != -1) { + if (ast_rtp_set_rtpmap_type_rate(newvideortp, codec, "video", mimeSubtype, 0, sample_rate) != -1) { if (debug) ast_verbose("Found video description format %s for ID %d\n", mimeSubtype, codec); found_rtpmap_codecs[last_rtpmap_codec] = codec; @@ -7787,11 +7835,12 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action sprintf(red_fmtp, "fmtp:%d ", red_pt); if (debug) - ast_verbose("Red submimetype has payload type: %d\n", red_pt); + ast_verbose("RED submimetype has payload type: %d\n", red_pt); } } else { /* Must be audio?? */ - if(ast_rtp_set_rtpmap_type(newaudiortp, codec, "audio", mimeSubtype, - ast_test_flag(&p->flags[0], SIP_G726_NONSTANDARD) ? AST_RTP_OPT_G726_NONSTANDARD : 0) != -1) { + if (ast_rtp_set_rtpmap_type_rate(newaudiortp, codec, "audio", mimeSubtype, + ast_test_flag(&p->flags[0], SIP_G726_NONSTANDARD) ? AST_RTP_OPT_G726_NONSTANDARD : 0, + sample_rate) != -1) { if (debug) ast_verbose("Found audio description format %s for ID %d\n", mimeSubtype, codec); found_rtpmap_codecs[last_rtpmap_codec] = codec; @@ -8895,7 +8944,7 @@ static int add_vidupdate(struct sip_request *req) } /*! \brief Add codec offer to SDP offer/answer body in INVITE or 200 OK */ -static void add_codec_to_sdp(const struct sip_pvt *p, int codec, int sample_rate, +static void add_codec_to_sdp(const struct sip_pvt *p, int codec, struct ast_str **m_buf, struct ast_str **a_buf, int debug, int *min_packet_size) { @@ -8915,18 +8964,31 @@ static void add_codec_to_sdp(const struct sip_pvt *p, int codec, int sample_rate return; ast_str_append(m_buf, 0, " %d", rtp_code); ast_str_append(a_buf, 0, "a=rtpmap:%d %s/%d\r\n", rtp_code, - ast_rtp_lookup_mime_subtype(1, codec, - ast_test_flag(&p->flags[0], SIP_G726_NONSTANDARD) ? AST_RTP_OPT_G726_NONSTANDARD : 0), - sample_rate); - if (codec == AST_FORMAT_G729A) { + ast_rtp_lookup_mime_subtype(1, codec, + ast_test_flag(&p->flags[0], SIP_G726_NONSTANDARD) ? AST_RTP_OPT_G726_NONSTANDARD : 0), + ast_rtp_lookup_sample_rate(1, codec)); + + switch (codec) { + case AST_FORMAT_G729A: /* Indicate that we don't support VAD (G.729 annex B) */ ast_str_append(a_buf, 0, "a=fmtp:%d annexb=no\r\n", rtp_code); - } else if (codec == AST_FORMAT_G723_1) { + break; + case AST_FORMAT_G723_1: /* Indicate that we don't support VAD (G.723.1 annex A) */ ast_str_append(a_buf, 0, "a=fmtp:%d annexa=no\r\n", rtp_code); - } else if (codec == AST_FORMAT_ILBC) { + break; + case AST_FORMAT_ILBC: /* Add information about us using only 20/30 ms packetization */ ast_str_append(a_buf, 0, "a=fmtp:%d mode=%d\r\n", rtp_code, fmt.cur_ms); + break; + case AST_FORMAT_SIREN7: + /* Indicate that we only expect 32Kbps */ + ast_str_append(a_buf, 0, "a=fmtp:%d bitrate=32000\r\n", rtp_code); + break; + case AST_FORMAT_SIREN14: + /* Indicate that we only expect 48Kbps */ + ast_str_append(a_buf, 0, "a=fmtp:%d bitrate=48000\r\n", rtp_code); + break; } if (fmt.cur_ms && (fmt.cur_ms < *min_packet_size)) @@ -8939,7 +9001,7 @@ static void add_codec_to_sdp(const struct sip_pvt *p, int codec, int sample_rate /*! \brief Add video codec offer to SDP offer/answer body in INVITE or 200 OK */ /* This is different to the audio one now so we can add more caps later */ -static void add_vcodec_to_sdp(const struct sip_pvt *p, int codec, int sample_rate, +static void add_vcodec_to_sdp(const struct sip_pvt *p, int codec, struct ast_str **m_buf, struct ast_str **a_buf, int debug, int *min_packet_size) { @@ -8956,12 +9018,13 @@ static void add_vcodec_to_sdp(const struct sip_pvt *p, int codec, int sample_rat ast_str_append(m_buf, 0, " %d", rtp_code); ast_str_append(a_buf, 0, "a=rtpmap:%d %s/%d\r\n", rtp_code, - ast_rtp_lookup_mime_subtype(1, codec, 0), sample_rate); + ast_rtp_lookup_mime_subtype(1, codec, 0), + ast_rtp_lookup_sample_rate(1, codec)); /* Add fmtp code here */ } /*! \brief Add text codec offer to SDP offer/answer body in INVITE or 200 OK */ -static void add_tcodec_to_sdp(const struct sip_pvt *p, int codec, int sample_rate, +static void add_tcodec_to_sdp(const struct sip_pvt *p, int codec, struct ast_str **m_buf, struct ast_str **a_buf, int debug, int *min_packet_size) { @@ -8978,11 +9041,12 @@ static void add_tcodec_to_sdp(const struct sip_pvt *p, int codec, int sample_rat ast_str_append(m_buf, 0, " %d", rtp_code); ast_str_append(a_buf, 0, "a=rtpmap:%d %s/%d\r\n", rtp_code, - ast_rtp_lookup_mime_subtype(1, codec, 0), sample_rate); + ast_rtp_lookup_mime_subtype(1, codec, 0), + ast_rtp_lookup_sample_rate(1, codec)); /* Add fmtp code here */ if (codec == AST_FORMAT_T140RED) { - ast_str_append(a_buf, 0, "a=fmtp:%d %d/%d/%d\r\n", rtp_code, + ast_str_append(a_buf, 0, "a=fmtp:%d %d/%d/%d\r\n", rtp_code, ast_rtp_lookup_code(p->trtp, 1, AST_FORMAT_T140), ast_rtp_lookup_code(p->trtp, 1, AST_FORMAT_T140), ast_rtp_lookup_code(p->trtp, 1, AST_FORMAT_T140)); @@ -9107,7 +9171,7 @@ static int add_t38_sdp(struct sip_request *resp, struct sip_pvt *p) /*! \brief Add RFC 2833 DTMF offer to SDP */ -static void add_noncodec_to_sdp(const struct sip_pvt *p, int format, int sample_rate, +static void add_noncodec_to_sdp(const struct sip_pvt *p, int format, struct ast_str **m_buf, struct ast_str **a_buf, int debug) { @@ -9120,8 +9184,8 @@ static void add_noncodec_to_sdp(const struct sip_pvt *p, int format, int sample_ ast_str_append(m_buf, 0, " %d", rtp_code); ast_str_append(a_buf, 0, "a=rtpmap:%d %s/%d\r\n", rtp_code, - ast_rtp_lookup_mime_subtype(0, format, 0), - sample_rate); + ast_rtp_lookup_mime_subtype(0, format, 0), + ast_rtp_lookup_sample_rate(0, format)); if (format == AST_RTP_DTMF) /* Indicate we support DTMF and FLASH... */ ast_str_append(a_buf, 0, "a=fmtp:%d 0-16\r\n", rtp_code); } @@ -9162,13 +9226,6 @@ static void get_our_media_address(struct sip_pvt *p, int needvideo, } -/*! - * \note G.722 actually is supposed to specified as 8 kHz, even though it is - * really 16 kHz. Update this macro for other formats as they are added in - * the future. - */ -#define SDP_SAMPLE_RATE(x) 8000 - /*! \brief Add Session Description Protocol message If oldsdp is TRUE, then the SDP version number is not incremented. This mechanism @@ -9340,9 +9397,7 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int if (capability & p->prefcodec) { int codec = p->prefcodec & AST_FORMAT_AUDIO_MASK; - add_codec_to_sdp(p, codec, SDP_SAMPLE_RATE(codec), - &m_audio, &a_audio, - debug, &min_audio_packet_size); + add_codec_to_sdp(p, codec, &m_audio, &a_audio, debug, &min_audio_packet_size); alreadysent |= codec; } @@ -9359,9 +9414,7 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int if (alreadysent & codec) continue; - add_codec_to_sdp(p, codec, SDP_SAMPLE_RATE(codec), - &m_audio, &a_audio, - debug, &min_audio_packet_size); + add_codec_to_sdp(p, codec, &m_audio, &a_audio, debug, &min_audio_packet_size); alreadysent |= codec; } @@ -9374,14 +9427,11 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int continue; if (x & AST_FORMAT_AUDIO_MASK) - add_codec_to_sdp(p, x, SDP_SAMPLE_RATE(x), - &m_audio, &a_audio, debug, &min_audio_packet_size); - else if (x & AST_FORMAT_VIDEO_MASK) - add_vcodec_to_sdp(p, x, 90000, - &m_video, &a_video, debug, &min_video_packet_size); + add_codec_to_sdp(p, x, &m_audio, &a_audio, debug, &min_audio_packet_size); + else if (x & AST_FORMAT_VIDEO_MASK) + add_vcodec_to_sdp(p, x, &m_video, &a_video, debug, &min_video_packet_size); else if (x & AST_FORMAT_TEXT_MASK) - add_tcodec_to_sdp(p, x, 1000, - &m_text, &a_text, debug, &min_text_packet_size); + add_tcodec_to_sdp(p, x, &m_text, &a_text, debug, &min_text_packet_size); } /* Now add DTMF RFC2833 telephony-event as a codec */ @@ -9389,7 +9439,7 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int if (!(p->jointnoncodeccapability & x)) continue; - add_noncodec_to_sdp(p, x, 8000, &m_audio, &a_audio, debug); + add_noncodec_to_sdp(p, x, &m_audio, &a_audio, debug); } ast_debug(3, "-- Done with adding codecs to SDP\n"); diff --git a/formats/format_siren14.c b/formats/format_siren14.c new file mode 100644 index 000000000..109c586fb --- /dev/null +++ b/formats/format_siren14.c @@ -0,0 +1,139 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2008, Anthony Minessale and Digium, Inc. + * Anthony Minessale (anthmct@yahoo.com) + * Kevin P. Fleming <kpfleming@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief ITU G.722.1 Annex C (Siren14, licensed from Polycom) format, 48kbps bitrate only + * \arg File name extensions: siren14 + * \ingroup formats + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/mod_format.h" +#include "asterisk/module.h" +#include "asterisk/endian.h" + +#define BUF_SIZE 120 /* 20 milliseconds == 120 bytes, 640 samples */ +#define SAMPLES_TO_BYTES(x) ((typeof(x)) x / ((float) 640 / 120)) +#define BYTES_TO_SAMPLES(x) ((typeof(x)) x * ((float) 640 / 120)) + +static struct ast_frame *siren14read(struct ast_filestream *s, int *whennext) +{ + int res; + /* Send a frame from the file to the appropriate channel */ + + s->fr.frametype = AST_FRAME_VOICE; + s->fr.subclass = AST_FORMAT_SIREN14; + s->fr.mallocd = 0; + AST_FRAME_SET_BUFFER(&s->fr, s->buf, AST_FRIENDLY_OFFSET, BUF_SIZE); + if ((res = fread(s->fr.data.ptr, 1, s->fr.datalen, s->f)) != s->fr.datalen) { + if (res) + ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno)); + return NULL; + } + *whennext = s->fr.samples = BYTES_TO_SAMPLES(res); + ast_log(LOG_DEBUG, "Read frame of %d bytes and %d samples\n", res, s->fr.samples); + return &s->fr; +} + +static int siren14write(struct ast_filestream *fs, struct ast_frame *f) +{ + int res; + + if (f->frametype != AST_FRAME_VOICE) { + ast_log(LOG_WARNING, "Asked to write non-voice frame!\n"); + return -1; + } + if (f->subclass != AST_FORMAT_SIREN14) { + ast_log(LOG_WARNING, "Asked to write non-Siren14 frame (%d)!\n", f->subclass); + return -1; + } + if ((res = fwrite(f->data.ptr, 1, f->datalen, fs->f)) != f->datalen) { + ast_log(LOG_WARNING, "Bad write (%d/%d): %s\n", res, f->datalen, strerror(errno)); + return -1; + } + return 0; +} + +static int siren14seek(struct ast_filestream *fs, off_t sample_offset, int whence) +{ + off_t offset = 0, min = 0, cur, max; + + sample_offset = SAMPLES_TO_BYTES(sample_offset); + + cur = ftello(fs->f); + + fseeko(fs->f, 0, SEEK_END); + + max = ftello(fs->f); + + if (whence == SEEK_SET) + offset = sample_offset; + else if (whence == SEEK_CUR || whence == SEEK_FORCECUR) + offset = sample_offset + cur; + else if (whence == SEEK_END) + offset = max - sample_offset; + + if (whence != SEEK_FORCECUR) + offset = (offset > max) ? max : offset; + + /* always protect against seeking past begining. */ + offset = (offset < min) ? min : offset; + + return fseeko(fs->f, offset, SEEK_SET); +} + +static int siren14trunc(struct ast_filestream *fs) +{ + return ftruncate(fileno(fs->f), ftello(fs->f)); +} + +static off_t siren14tell(struct ast_filestream *fs) +{ + return BYTES_TO_SAMPLES(ftello(fs->f)); +} + +static const struct ast_format siren14_f = { + .name = "siren14", + .exts = "siren14", + .format = AST_FORMAT_SIREN14, + .write = siren14write, + .seek = siren14seek, + .trunc = siren14trunc, + .tell = siren14tell, + .read = siren14read, + .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET, +}; + +static int load_module(void) +{ + if (ast_format_register(&siren14_f)) + return AST_MODULE_LOAD_DECLINE; + + return AST_MODULE_LOAD_SUCCESS; +} + +static int unload_module(void) +{ + return ast_format_unregister(siren14_f.name); +} + +AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "ITU G.722.1 Annex C (Siren14, licensed from Polycom)"); diff --git a/formats/format_siren7.c b/formats/format_siren7.c new file mode 100644 index 000000000..0b61a72b2 --- /dev/null +++ b/formats/format_siren7.c @@ -0,0 +1,138 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2008, Anthony Minessale and Digium, Inc. + * Anthony Minessale (anthmct@yahoo.com) + * Kevin P. Fleming <kpfleming@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief ITU G.722.1 (Siren7, licensed from Polycom) format, 32kbps bitrate only + * \arg File name extensions: siren7 + * \ingroup formats + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/mod_format.h" +#include "asterisk/module.h" +#include "asterisk/endian.h" + +#define BUF_SIZE 80 /* 20 milliseconds == 80 bytes, 320 samples */ +#define SAMPLES_TO_BYTES(x) x / (320 / 80) +#define BYTES_TO_SAMPLES(x) x * (320 / 80) + +static struct ast_frame *siren7read(struct ast_filestream *s, int *whennext) +{ + int res; + /* Send a frame from the file to the appropriate channel */ + + s->fr.frametype = AST_FRAME_VOICE; + s->fr.subclass = AST_FORMAT_SIREN7; + s->fr.mallocd = 0; + AST_FRAME_SET_BUFFER(&s->fr, s->buf, AST_FRIENDLY_OFFSET, BUF_SIZE); + if ((res = fread(s->fr.data.ptr, 1, s->fr.datalen, s->f)) != s->fr.datalen) { + if (res) + ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno)); + return NULL; + } + *whennext = s->fr.samples = BYTES_TO_SAMPLES(res); + return &s->fr; +} + +static int siren7write(struct ast_filestream *fs, struct ast_frame *f) +{ + int res; + + if (f->frametype != AST_FRAME_VOICE) { + ast_log(LOG_WARNING, "Asked to write non-voice frame!\n"); + return -1; + } + if (f->subclass != AST_FORMAT_SIREN7) { + ast_log(LOG_WARNING, "Asked to write non-Siren7 frame (%d)!\n", f->subclass); + return -1; + } + if ((res = fwrite(f->data.ptr, 1, f->datalen, fs->f)) != f->datalen) { + ast_log(LOG_WARNING, "Bad write (%d/%d): %s\n", res, f->datalen, strerror(errno)); + return -1; + } + return 0; +} + +static int siren7seek(struct ast_filestream *fs, off_t sample_offset, int whence) +{ + off_t offset = 0, min = 0, cur, max; + + sample_offset = SAMPLES_TO_BYTES(sample_offset); + + cur = ftello(fs->f); + + fseeko(fs->f, 0, SEEK_END); + + max = ftello(fs->f); + + if (whence == SEEK_SET) + offset = sample_offset; + else if (whence == SEEK_CUR || whence == SEEK_FORCECUR) + offset = sample_offset + cur; + else if (whence == SEEK_END) + offset = max - sample_offset; + + if (whence != SEEK_FORCECUR) + offset = (offset > max) ? max : offset; + + /* always protect against seeking past begining. */ + offset = (offset < min) ? min : offset; + + return fseeko(fs->f, offset, SEEK_SET); +} + +static int siren7trunc(struct ast_filestream *fs) +{ + return ftruncate(fileno(fs->f), ftello(fs->f)); +} + +static off_t siren7tell(struct ast_filestream *fs) +{ + return BYTES_TO_SAMPLES(ftello(fs->f)); +} + +static const struct ast_format siren7_f = { + .name = "siren7", + .exts = "siren7", + .format = AST_FORMAT_SIREN7, + .write = siren7write, + .seek = siren7seek, + .trunc = siren7trunc, + .tell = siren7tell, + .read = siren7read, + .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET, +}; + +static int load_module(void) +{ + if (ast_format_register(&siren7_f)) + return AST_MODULE_LOAD_DECLINE; + + return AST_MODULE_LOAD_SUCCESS; +} + +static int unload_module(void) +{ + return ast_format_unregister(siren7_f.name); +} + +AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "ITU G.722.1 (Siren7, licensed from Polycom)"); diff --git a/include/asterisk/frame.h b/include/asterisk/frame.h index 267a820e8..4a18bdbac 100644 --- a/include/asterisk/frame.h +++ b/include/asterisk/frame.h @@ -259,6 +259,10 @@ extern struct ast_frame ast_null_frame; #define AST_FORMAT_G726 (1 << 11) /*! G.722 */ #define AST_FORMAT_G722 (1 << 12) +/*! G.722.1 (also known as Siren7, 32kbps assumed) */ +#define AST_FORMAT_SIREN7 (1 << 13) +/*! G.722.1 Annex C (also known as Siren14, 48kbps assumed) */ +#define AST_FORMAT_SIREN14 (1 << 14) /*! Raw 16-bit Signed Linear (16000 Hz) PCM */ #define AST_FORMAT_SLINEAR16 (1 << 15) /*! Maximum audio mask */ @@ -523,8 +527,8 @@ struct ast_frame *ast_smoother_read(struct ast_smoother *s); #endif /*@} Doxygen marker */ -struct ast_format_list *ast_get_format_list_index(int index); -struct ast_format_list *ast_get_format_list(size_t *size); +const struct ast_format_list *ast_get_format_list_index(int index); +const struct ast_format_list *ast_get_format_list(size_t *size); void ast_frame_dump(const char *name, struct ast_frame *f, char *prefix); /*! \page AudioCodecPref Audio Codec Preferences @@ -630,10 +634,16 @@ int ast_frame_slinear_sum(struct ast_frame *f1, struct ast_frame *f2); */ static force_inline int ast_format_rate(int format) { - if (format == AST_FORMAT_G722 || format == AST_FORMAT_SLINEAR16) + switch (format) { + case AST_FORMAT_G722: + case AST_FORMAT_SLINEAR16: + case AST_FORMAT_SIREN7: return 16000; - - return 8000; + case AST_FORMAT_SIREN14: + return 32000; + default: + return 8000; + } } #if defined(__cplusplus) || defined(c_plusplus) diff --git a/include/asterisk/rtp.h b/include/asterisk/rtp.h index 800519572..4648a2a34 100644 --- a/include/asterisk/rtp.h +++ b/include/asterisk/rtp.h @@ -84,6 +84,12 @@ struct ast_rtp; /*! T.140 Redundancy structure*/ struct rtp_red; +/*! \brief The value of each payload format mapping: */ +struct rtpPayloadType { + int isAstFormat; /*!< whether the following code is an AST_FORMAT */ + int code; +}; + /*! \brief This is the structure that binds a channel (SIP/Jingle/H.323) to the RTP subsystem */ struct ast_rtp_protocol { @@ -136,7 +142,7 @@ size_t ast_rtp_alloc_size(void); * \param io * \param rtcpenable * \param callbackmode - * \returns A representation (structure) of an RTP session. + * \return A representation (structure) of an RTP session. */ struct ast_rtp *ast_rtp_new(struct sched_context *sched, struct io_context *io, int rtcpenable, int callbackmode); @@ -150,7 +156,7 @@ struct ast_rtp *ast_rtp_new(struct sched_context *sched, struct io_context *io, * \param rtcpenable * \param callbackmode * \param in - * \returns A representation (structure) of an RTP session. + * \return A representation (structure) of an RTP session. */ struct ast_rtp *ast_rtp_new_with_bindaddr(struct sched_context *sched, struct io_context *io, int rtcpenable, int callbackmode, struct in_addr in); @@ -209,22 +215,66 @@ void ast_rtp_set_m_type(struct ast_rtp* rtp, int pt); /*! \brief clear payload type */ void ast_rtp_unset_m_type(struct ast_rtp* rtp, int pt); -/*! \brief Initiate payload type to a known MIME media type for a codec */ +/*! \brief Set payload type to a known MIME media type for a codec + * + * \param rtp RTP structure to modify + * \param pt Payload type entry to modify + * \param mimeType top-level MIME type of media stream (typically "audio", "video", "text", etc.) + * \param mimeSubtype MIME subtype of media stream (typically a codec name) + * \param options Zero or more flags from the ast_rtp_options enum + * + * This function 'fills in' an entry in the list of possible formats for + * a media stream associated with an RTP structure. + * + * \retval 0 on success + * \retval -1 if the payload type is out of range + * \retval -2 if the mimeType/mimeSubtype combination was not found + */ int ast_rtp_set_rtpmap_type(struct ast_rtp* rtp, int pt, char *mimeType, char *mimeSubtype, enum ast_rtp_options options); +/*! \brief Set payload type to a known MIME media type for a codec with a specific sample rate + * + * \param rtp RTP structure to modify + * \param pt Payload type entry to modify + * \param mimeType top-level MIME type of media stream (typically "audio", "video", "text", etc.) + * \param mimeSubtype MIME subtype of media stream (typically a codec name) + * \param options Zero or more flags from the ast_rtp_options enum + * \param sample_rate The sample rate of the media stream + * + * This function 'fills in' an entry in the list of possible formats for + * a media stream associated with an RTP structure. + * + * \retval 0 on success + * \retval -1 if the payload type is out of range + * \retval -2 if the mimeType/mimeSubtype combination was not found + */ +int ast_rtp_set_rtpmap_type_rate(struct ast_rtp* rtp, int pt, + char *mimeType, char *mimeSubtype, + enum ast_rtp_options options, + unsigned int sample_rate); + /*! \brief Mapping between RTP payload format codes and Asterisk codes: */ struct rtpPayloadType ast_rtp_lookup_pt(struct ast_rtp* rtp, int pt); int ast_rtp_lookup_code(struct ast_rtp* rtp, int isAstFormat, int code); void ast_rtp_get_current_formats(struct ast_rtp* rtp, - int* astFormats, int* nonAstFormats); + int* astFormats, int* nonAstFormats); /*! \brief Mapping an Asterisk code into a MIME subtype (string): */ const char *ast_rtp_lookup_mime_subtype(int isAstFormat, int code, enum ast_rtp_options options); +/*! \brief Get the sample rate associated with known RTP payload types + * + * \param isAstFormat True if the value in the 'code' parameter is an AST_FORMAT value + * \param code Format code, either from AST_FORMAT list or from AST_RTP list + * + * \return the sample rate if the format was found, zero if it was not found + */ +unsigned int ast_rtp_lookup_sample_rate(int isAstFormat, int code); + /*! \brief Build a string of MIME subtype names from a capability list */ char *ast_rtp_lookup_mime_multiple(char *buf, size_t size, const int capability, const int isAstFormat, enum ast_rtp_options options); diff --git a/main/frame.c b/main/frame.c index 84a2b0a5a..7d950d740 100644 --- a/main/frame.c +++ b/main/frame.c @@ -98,7 +98,7 @@ struct ast_smoother { }; /*! \brief Definition of supported media formats (codecs) */ -static struct ast_format_list AST_FORMAT_LIST[] = { +static const struct ast_format_list AST_FORMAT_LIST[] = { { AST_FORMAT_G723_1 , "g723", 8000, "G.723.1", 20, 30, 300, 30, 30 }, /*!< G723.1 */ { AST_FORMAT_GSM, "gsm", 8000, "GSM", 33, 20, 300, 20, 20 }, /*!< codec_gsm.c */ { AST_FORMAT_ULAW, "ulaw", 8000, "G.711 u-law", 80, 10, 150, 10, 20 }, /*!< codec_ulaw.c */ @@ -120,8 +120,10 @@ static struct ast_format_list AST_FORMAT_LIST[] = { { AST_FORMAT_H263_PLUS, "h263p", 0, "H.263+ Video" }, /*!< H.263plus passthrough support See format_h263.c */ { AST_FORMAT_H264, "h264", 0, "H.264 Video" }, /*!< Passthrough support, see format_h263.c */ { AST_FORMAT_MP4_VIDEO, "mpeg4", 0, "MPEG4 Video" }, /*!< Passthrough support for MPEG4 */ - { AST_FORMAT_T140RED, "red", 1, "T.140 Realtime Text with redundancy"}, /*!< Redundant T.140 Realtime Text */ + { AST_FORMAT_T140RED, "red", 1, "T.140 Realtime Text with redundancy"}, /*!< Redundant T.140 Realtime Text */ { AST_FORMAT_T140, "t140", 0, "Passthrough T.140 Realtime Text" }, /*!< Passthrough support for T.140 Realtime Text */ + { AST_FORMAT_SIREN7, "siren7", 16000, "ITU G.722.1 (Siren7, licensed from Polycom)", 80, 20, 80, 20, 20 }, /*!< Binary commercial distribution */ + { AST_FORMAT_SIREN14, "siren14", 32000, "ITU G.722.1 Annex C, (Siren14, licensed from Polycom)", 120, 20, 80, 20, 20 }, /*!< Binary commercial distribution */ }; struct ast_frame ast_null_frame = { AST_FRAME_NULL, }; @@ -505,12 +507,12 @@ void ast_swapcopy_samples(void *dst, const void *src, int samples) } -struct ast_format_list *ast_get_format_list_index(int idx) +const struct ast_format_list *ast_get_format_list_index(int idx) { return &AST_FORMAT_LIST[idx]; } -struct ast_format_list *ast_get_format_list(size_t *size) +const struct ast_format_list *ast_get_format_list(size_t *size) { *size = ARRAY_LEN(AST_FORMAT_LIST); return AST_FORMAT_LIST; @@ -564,6 +566,8 @@ static struct ast_codec_alias_table { { "slinear", "slin"}, { "slinear16", "slin16"}, { "g723.1", "g723"}, + { "g722.1", "siren7"}, + { "g722.1c", "siren14"}, }; static const char *ast_expand_codec_alias(const char *in) @@ -1407,7 +1411,8 @@ static int speex_samples(unsigned char *data, int len) int ast_codec_get_samples(struct ast_frame *f) { - int samples=0; + int samples = 0; + switch(f->subclass) { case AST_FORMAT_SPEEX: samples = speex_samples(f->data.ptr, f->datalen); @@ -1443,6 +1448,14 @@ int ast_codec_get_samples(struct ast_frame *f) case AST_FORMAT_G726_AAL2: samples = f->datalen * 2; break; + case AST_FORMAT_SIREN7: + /* 16,000 samples per second at 32kbps is 4,000 bytes per second */ + samples = f->datalen * (16000 / 4000); + break; + case AST_FORMAT_SIREN14: + /* 32,000 samples per second at 48kbps is 6,000 bytes per second */ + samples = (int) f->datalen * ((float) 32000 / 6000); + break; default: ast_log(LOG_WARNING, "Unable to calculate samples for format %s\n", ast_getformatname(f->subclass)); } @@ -1453,7 +1466,7 @@ int ast_codec_get_len(int format, int samples) { int len = 0; - /* XXX Still need speex, g723, and lpc10 XXX */ + /* XXX Still need speex, and lpc10 XXX */ switch(format) { case AST_FORMAT_G723_1: len = (samples / 240) * 20; @@ -1481,6 +1494,14 @@ int ast_codec_get_len(int format, int samples) case AST_FORMAT_G726_AAL2: len = samples / 2; break; + case AST_FORMAT_SIREN7: + /* 16,000 samples per second at 32kbps is 4,000 bytes per second */ + len = samples / (16000 / 4000); + break; + case AST_FORMAT_SIREN14: + /* 32,000 samples per second at 48kbps is 6,000 bytes per second */ + len = (int) samples / ((float) 32000 / 6000); + break; default: ast_log(LOG_WARNING, "Unable to calculate sample length for format %s\n", ast_getformatname(format)); } diff --git a/main/rtp.c b/main/rtp.c index a39fc10f2..8240a9fe3 100644 --- a/main/rtp.c +++ b/main/rtp.c @@ -97,12 +97,6 @@ enum strict_rtp_state { * RTP session is defined on page 9 of RFC 3550: "An association among a set of participants communicating with RTP. A participant may be involved in multiple RTP sessions at the same time [...]" * */ -/*! \brief The value of each payload format mapping: */ -struct rtpPayloadType { - int isAstFormat; /*!< whether the following code is an AST_FORMAT */ - int code; -}; - /*! \brief RTP session description */ struct ast_rtp { @@ -1832,7 +1826,7 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp) /* Add timing data to let ast_generic_bridge() put the frame into a jitterbuf */ ast_set_flag(&rtp->f, AST_FRFLAG_HAS_TIMING_INFO); rtp->f.ts = timestamp / 8; - rtp->f.len = rtp->f.samples / ( (ast_format_rate(rtp->f.subclass) == 16000) ? 16 : 8 ); + rtp->f.len = rtp->f.samples / ((ast_format_rate(rtp->f.subclass) / 1000)); } else if (rtp->f.subclass & AST_FORMAT_VIDEO_MASK) { /* Video -- samples is # of samples vs. 90000 */ if (!rtp->lastividtimestamp) @@ -1863,40 +1857,46 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp) /* The following array defines the MIME Media type (and subtype) for each of our codecs, or RTP-specific data type. */ -static struct { +static const struct mimeType { struct rtpPayloadType payloadType; - char* type; - char* subtype; + char *type; + char *subtype; + unsigned int sample_rate; } mimeTypes[] = { - {{1, AST_FORMAT_G723_1}, "audio", "G723"}, - {{1, AST_FORMAT_GSM}, "audio", "GSM"}, - {{1, AST_FORMAT_ULAW}, "audio", "PCMU"}, - {{1, AST_FORMAT_ULAW}, "audio", "G711U"}, - {{1, AST_FORMAT_ALAW}, "audio", "PCMA"}, - {{1, AST_FORMAT_ALAW}, "audio", "G711A"}, - {{1, AST_FORMAT_G726}, "audio", "G726-32"}, - {{1, AST_FORMAT_ADPCM}, "audio", "DVI4"}, - {{1, AST_FORMAT_SLINEAR}, "audio", "L16"}, - {{1, AST_FORMAT_LPC10}, "audio", "LPC"}, - {{1, AST_FORMAT_G729A}, "audio", "G729"}, - {{1, AST_FORMAT_G729A}, "audio", "G729A"}, - {{1, AST_FORMAT_G729A}, "audio", "G.729"}, - {{1, AST_FORMAT_SPEEX}, "audio", "speex"}, - {{1, AST_FORMAT_ILBC}, "audio", "iLBC"}, - {{1, AST_FORMAT_G722}, "audio", "G722"}, - {{1, AST_FORMAT_G726_AAL2}, "audio", "AAL2-G726-32"}, - {{0, AST_RTP_DTMF}, "audio", "telephone-event"}, - {{0, AST_RTP_CISCO_DTMF}, "audio", "cisco-telephone-event"}, - {{0, AST_RTP_CN}, "audio", "CN"}, - {{1, AST_FORMAT_JPEG}, "video", "JPEG"}, - {{1, AST_FORMAT_PNG}, "video", "PNG"}, - {{1, AST_FORMAT_H261}, "video", "H261"}, - {{1, AST_FORMAT_H263}, "video", "H263"}, - {{1, AST_FORMAT_H263_PLUS}, "video", "h263-1998"}, - {{1, AST_FORMAT_H264}, "video", "H264"}, - {{1, AST_FORMAT_MP4_VIDEO}, "video", "MP4V-ES"}, - {{1, AST_FORMAT_T140RED}, "text", "RED"}, - {{1, AST_FORMAT_T140}, "text", "T140"}, + {{1, AST_FORMAT_G723_1}, "audio", "G723", 8000}, + {{1, AST_FORMAT_GSM}, "audio", "GSM", 8000}, + {{1, AST_FORMAT_ULAW}, "audio", "PCMU", 8000}, + {{1, AST_FORMAT_ULAW}, "audio", "G711U", 8000}, + {{1, AST_FORMAT_ALAW}, "audio", "PCMA", 8000}, + {{1, AST_FORMAT_ALAW}, "audio", "G711A", 8000}, + {{1, AST_FORMAT_G726}, "audio", "G726-32", 8000}, + {{1, AST_FORMAT_ADPCM}, "audio", "DVI4", 8000}, + {{1, AST_FORMAT_SLINEAR}, "audio", "L16", 8000}, + {{1, AST_FORMAT_LPC10}, "audio", "LPC", 8000}, + {{1, AST_FORMAT_G729A}, "audio", "G729", 8000}, + {{1, AST_FORMAT_G729A}, "audio", "G729A", 8000}, + {{1, AST_FORMAT_G729A}, "audio", "G.729", 8000}, + {{1, AST_FORMAT_SPEEX}, "audio", "speex", 8000}, + {{1, AST_FORMAT_ILBC}, "audio", "iLBC", 8000}, + /* this is the sample rate listed in the RTP profile for the G.722 + codec, *NOT* the actual sample rate of the media stream + */ + {{1, AST_FORMAT_G722}, "audio", "G722", 8000}, + {{1, AST_FORMAT_G726_AAL2}, "audio", "AAL2-G726-32", 8000}, + {{0, AST_RTP_DTMF}, "audio", "telephone-event", 8000}, + {{0, AST_RTP_CISCO_DTMF}, "audio", "cisco-telephone-event", 8000}, + {{0, AST_RTP_CN}, "audio", "CN", 8000}, + {{1, AST_FORMAT_JPEG}, "video", "JPEG", 90000}, + {{1, AST_FORMAT_PNG}, "video", "PNG", 90000}, + {{1, AST_FORMAT_H261}, "video", "H261", 90000}, + {{1, AST_FORMAT_H263}, "video", "H263", 90000}, + {{1, AST_FORMAT_H263_PLUS}, "video", "h263-1998", 90000}, + {{1, AST_FORMAT_H264}, "video", "H264", 90000}, + {{1, AST_FORMAT_MP4_VIDEO}, "video", "MP4V-ES", 90000}, + {{1, AST_FORMAT_T140RED}, "text", "RED", 1000}, + {{1, AST_FORMAT_T140}, "text", "T140", 1000}, + {{1, AST_FORMAT_SIREN7}, "audio", "G7221", 16000}, + {{1, AST_FORMAT_SIREN14}, "audio", "G7221", 32000}, }; /*! @@ -1909,7 +1909,7 @@ static struct { * See http://www.iana.org/assignments/rtp-parameters for a list of * assigned values */ -static struct rtpPayloadType static_RTP_PT[MAX_RTP_PT] = { +static const struct rtpPayloadType static_RTP_PT[MAX_RTP_PT] = { [0] = {1, AST_FORMAT_ULAW}, #ifdef USE_DEPRECATED_G726 [2] = {1, AST_FORMAT_G726}, /* Technically this is G.721, but if Cisco can do it, so can we... */ @@ -1935,6 +1935,7 @@ static struct rtpPayloadType static_RTP_PT[MAX_RTP_PT] = { [98] = {1, AST_FORMAT_H263_PLUS}, [99] = {1, AST_FORMAT_H264}, [101] = {0, AST_RTP_DTMF}, + [102] = {1, AST_FORMAT_SIREN7}, [103] = {1, AST_FORMAT_H263_PLUS}, [104] = {1, AST_FORMAT_MP4_VIDEO}, [105] = {1, AST_FORMAT_T140RED}, /* Real time text chat (with redundancy encoding) */ @@ -1942,6 +1943,7 @@ static struct rtpPayloadType static_RTP_PT[MAX_RTP_PT] = { [110] = {1, AST_FORMAT_SPEEX}, [111] = {1, AST_FORMAT_G726}, [112] = {1, AST_FORMAT_G726_AAL2}, + [115] = {1, AST_FORMAT_SIREN14}, [121] = {0, AST_RTP_CISCO_DTMF}, /* Must be type 121 */ }; @@ -2211,35 +2213,61 @@ void ast_rtp_unset_m_type(struct ast_rtp* rtp, int pt) * an SDP "a=rtpmap:" line. * \return 0 if the MIME type was found and set, -1 if it wasn't found */ -int ast_rtp_set_rtpmap_type(struct ast_rtp *rtp, int pt, - char *mimeType, char *mimeSubtype, - enum ast_rtp_options options) +int ast_rtp_set_rtpmap_type_rate(struct ast_rtp *rtp, int pt, + char *mimeType, char *mimeSubtype, + enum ast_rtp_options options, + unsigned int sample_rate) { unsigned int i; int found = 0; - if (pt < 0 || pt > MAX_RTP_PT) + if (pt < 0 || pt > MAX_RTP_PT) return -1; /* bogus payload type */ - + rtp_bridge_lock(rtp); for (i = 0; i < ARRAY_LEN(mimeTypes); ++i) { - if (strcasecmp(mimeSubtype, mimeTypes[i].subtype) == 0 && - strcasecmp(mimeType, mimeTypes[i].type) == 0) { - found = 1; - rtp->current_RTP_PT[pt] = mimeTypes[i].payloadType; - if ((mimeTypes[i].payloadType.code == AST_FORMAT_G726) && - mimeTypes[i].payloadType.isAstFormat && - (options & AST_RTP_OPT_G726_NONSTANDARD)) - rtp->current_RTP_PT[pt].code = AST_FORMAT_G726_AAL2; - break; + const struct mimeType *t = &mimeTypes[i]; + + if (strcasecmp(mimeSubtype, t->subtype)) { + continue; + } + + if (strcasecmp(mimeType, t->type)) { + continue; + } + + /* if both sample rates have been supplied, and they don't match, + then this not a match; if one has not been supplied, then the + rates are not compared */ + if (sample_rate && t->sample_rate && + (sample_rate != t->sample_rate)) { + continue; + } + + found = 1; + rtp->current_RTP_PT[pt] = t->payloadType; + + if ((t->payloadType.code == AST_FORMAT_G726) && + t->payloadType.isAstFormat && + (options & AST_RTP_OPT_G726_NONSTANDARD)) { + rtp->current_RTP_PT[pt].code = AST_FORMAT_G726_AAL2; } + + break; } rtp_bridge_unlock(rtp); - return (found ? 0 : -1); -} + return (found ? 0 : -2); +} + +int ast_rtp_set_rtpmap_type(struct ast_rtp *rtp, int pt, + char *mimeType, char *mimeSubtype, + enum ast_rtp_options options) +{ + return ast_rtp_set_rtpmap_type_rate(rtp, pt, mimeType, mimeSubtype, options, 0); +} /*! \brief Return the union of all of the codecs that were set by rtp_set...() calls * They're returned as two distinct sets: AST_FORMATs, and AST_RTPs */ @@ -2344,6 +2372,19 @@ const char *ast_rtp_lookup_mime_subtype(const int isAstFormat, const int code, return ""; } +unsigned int ast_rtp_lookup_sample_rate(int isAstFormat, int code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_LEN(mimeTypes); ++i) { + if ((mimeTypes[i].payloadType.code == code) && (mimeTypes[i].payloadType.isAstFormat == isAstFormat)) { + return mimeTypes[i].sample_rate; + } + } + + return 0; +} + char *ast_rtp_lookup_mime_multiple(char *buf, size_t size, const int capability, const int isAstFormat, enum ast_rtp_options options) { @@ -4773,9 +4814,8 @@ void red_buffer_t140(struct ast_rtp *rtp, struct ast_frame *f) { if (f->datalen > -1) { struct rtp_red *red = rtp->red; - memcpy(&red->buf_data[red->t140.datalen], f->data.ptr, f->datalen); + memcpy(&red->buf_data[red->t140.datalen], f->data.ptr, f->datalen); red->t140.datalen += f->datalen; red->t140.ts = f->ts; } } - |