Libav
|
00001 /* 00002 * RTP AMR Depacketizer, RFC 3267 00003 * Copyright (c) 2010 Martin Storsjo 00004 * 00005 * This file is part of FFmpeg. 00006 * 00007 * FFmpeg is free software; you can redistribute it and/or 00008 * modify it under the terms of the GNU Lesser General Public 00009 * License as published by the Free Software Foundation; either 00010 * version 2.1 of the License, or (at your option) any later version. 00011 * 00012 * FFmpeg is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 * Lesser General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU Lesser General Public 00018 * License along with FFmpeg; if not, write to the Free Software 00019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00020 */ 00021 00022 #include "avformat.h" 00023 #include "rtpdec_amr.h" 00024 #include "libavutil/avstring.h" 00025 00026 static const uint8_t frame_sizes_nb[16] = { 00027 12, 13, 15, 17, 19, 20, 26, 31, 5, 0, 0, 0, 0, 0, 0, 0 00028 }; 00029 static const uint8_t frame_sizes_wb[16] = { 00030 17, 23, 32, 36, 40, 46, 50, 58, 60, 5, 5, 0, 0, 0, 0, 0 00031 }; 00032 00033 static int amr_handle_packet(AVFormatContext *ctx, 00034 PayloadContext *data, 00035 AVStream *st, 00036 AVPacket * pkt, 00037 uint32_t * timestamp, 00038 const uint8_t * buf, 00039 int len, int flags) 00040 { 00041 const uint8_t *frame_sizes = NULL; 00042 int frames; 00043 int i; 00044 const uint8_t *speech_data; 00045 uint8_t *ptr; 00046 00047 if (st->codec->codec_id == CODEC_ID_AMR_NB) { 00048 frame_sizes = frame_sizes_nb; 00049 } else if (st->codec->codec_id == CODEC_ID_AMR_WB) { 00050 frame_sizes = frame_sizes_wb; 00051 } else { 00052 av_log(ctx, AV_LOG_ERROR, "Bad codec ID\n"); 00053 return AVERROR_INVALIDDATA; 00054 } 00055 00056 if (st->codec->channels != 1) { 00057 av_log(ctx, AV_LOG_ERROR, "Only mono AMR is supported\n"); 00058 return AVERROR_INVALIDDATA; 00059 } 00060 00061 /* The AMR RTP packet consists of one header byte, followed 00062 * by one TOC byte for each AMR frame in the packet, followed 00063 * by the speech data for all the AMR frames. 00064 * 00065 * The header byte contains only a codec mode request, for 00066 * requesting what kind of AMR data the sender wants to 00067 * receive. Not used at the moment. 00068 */ 00069 00070 /* Count the number of frames in the packet. The highest bit 00071 * is set in a TOC byte if there are more frames following. 00072 */ 00073 for (frames = 1; frames < len && (buf[frames] & 0x80); frames++) ; 00074 00075 if (1 + frames >= len) { 00076 /* We hit the end of the packet while counting frames. */ 00077 av_log(ctx, AV_LOG_ERROR, "No speech data found\n"); 00078 return AVERROR_INVALIDDATA; 00079 } 00080 00081 speech_data = buf + 1 + frames; 00082 00083 /* Everything except the codec mode request byte should be output. */ 00084 if (av_new_packet(pkt, len - 1)) { 00085 av_log(ctx, AV_LOG_ERROR, "Out of memory\n"); 00086 return AVERROR(ENOMEM); 00087 } 00088 pkt->stream_index = st->index; 00089 ptr = pkt->data; 00090 00091 for (i = 0; i < frames; i++) { 00092 uint8_t toc = buf[1 + i]; 00093 int frame_size = frame_sizes[(toc >> 3) & 0x0f]; 00094 00095 if (speech_data + frame_size > buf + len) { 00096 /* Too little speech data */ 00097 av_log(ctx, AV_LOG_WARNING, "Too little speech data in the RTP packet\n"); 00098 /* Set the unwritten part of the packet to zero. */ 00099 memset(ptr, 0, pkt->data + pkt->size - ptr); 00100 pkt->size = ptr - pkt->data; 00101 return 0; 00102 } 00103 00104 /* Extract the AMR frame mode from the TOC byte */ 00105 *ptr++ = toc & 0x7C; 00106 00107 /* Copy the speech data */ 00108 memcpy(ptr, speech_data, frame_size); 00109 speech_data += frame_size; 00110 ptr += frame_size; 00111 } 00112 00113 if (speech_data < buf + len) { 00114 av_log(ctx, AV_LOG_WARNING, "Too much speech data in the RTP packet?\n"); 00115 /* Set the unwritten part of the packet to zero. */ 00116 memset(ptr, 0, pkt->data + pkt->size - ptr); 00117 pkt->size = ptr - pkt->data; 00118 } 00119 00120 return 0; 00121 } 00122 00123 static int amr_parse_sdp_line(AVFormatContext *s, int st_index, 00124 PayloadContext *data, const char *line) 00125 { 00126 const char *p; 00127 char attr[25], value[25]; 00128 00129 /* Parse an fmtp line this one: 00130 * a=fmtp:97 octet-align=1; interleaving=0 00131 * That is, a normal fmtp: line followed by semicolon & space 00132 * separated key/value pairs. 00133 */ 00134 if (av_strstart(line, "fmtp:", &p)) { 00135 int octet_align = 0; 00136 int crc = 0; 00137 int interleaving = 0; 00138 int channels = 1; 00139 00140 while (*p && *p == ' ') p++; /* strip spaces */ 00141 while (*p && *p != ' ') p++; /* eat protocol identifier */ 00142 while (*p && *p == ' ') p++; /* strip trailing spaces */ 00143 00144 while (ff_rtsp_next_attr_and_value(&p, attr, sizeof(attr), value, sizeof(value))) { 00145 /* Some AMR SDP configurations contain "octet-align", without 00146 * the trailing =1. Therefore, if the value is empty, 00147 * interpret it as "1". 00148 */ 00149 if (!strcmp(value, "")) { 00150 av_log(s, AV_LOG_WARNING, "AMR fmtp attribute %s had " 00151 "nonstandard empty value\n", attr); 00152 strcpy(value, "1"); 00153 } 00154 if (!strcmp(attr, "octet-align")) 00155 octet_align = atoi(value); 00156 else if (!strcmp(attr, "crc")) 00157 crc = atoi(value); 00158 else if (!strcmp(attr, "interleaving")) 00159 interleaving = atoi(value); 00160 else if (!strcmp(attr, "channels")) 00161 channels = atoi(value); 00162 } 00163 if (!octet_align || crc || interleaving || channels != 1) { 00164 av_log(s, AV_LOG_ERROR, "Unsupported RTP/AMR configuration!\n"); 00165 return -1; 00166 } 00167 } 00168 return 0; 00169 } 00170 00171 RTPDynamicProtocolHandler ff_amr_nb_dynamic_handler = { 00172 .enc_name = "AMR", 00173 .codec_type = AVMEDIA_TYPE_AUDIO, 00174 .codec_id = CODEC_ID_AMR_NB, 00175 .parse_sdp_a_line = amr_parse_sdp_line, 00176 .parse_packet = amr_handle_packet, 00177 }; 00178 00179 RTPDynamicProtocolHandler ff_amr_wb_dynamic_handler = { 00180 .enc_name = "AMR-WB", 00181 .codec_type = AVMEDIA_TYPE_AUDIO, 00182 .codec_id = CODEC_ID_AMR_WB, 00183 .parse_sdp_a_line = amr_parse_sdp_line, 00184 .parse_packet = amr_handle_packet, 00185 }; 00186