Libav 0.7.1
|
00001 /* 00002 * Xiph RTP Protocols 00003 * Copyright (c) 2009 Colin McQuillian 00004 * Copyright (c) 2010 Josh Allmann 00005 * 00006 * This file is part of Libav. 00007 * 00008 * Libav is free software; you can redistribute it and/or 00009 * modify it under the terms of the GNU Lesser General Public 00010 * License as published by the Free Software Foundation; either 00011 * version 2.1 of the License, or (at your option) any later version. 00012 * 00013 * Libav is distributed in the hope that it will be useful, 00014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00016 * Lesser General Public License for more details. 00017 * 00018 * You should have received a copy of the GNU Lesser General Public 00019 * License along with Libav; if not, write to the Free Software 00020 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00021 */ 00022 00030 #include "libavutil/avstring.h" 00031 #include "libavutil/base64.h" 00032 #include "libavcodec/bytestream.h" 00033 00034 #include <assert.h> 00035 00036 #include "rtpdec.h" 00037 #include "rtpdec_formats.h" 00038 00042 struct PayloadContext { 00043 unsigned ident; 00044 uint32_t timestamp; 00045 AVIOContext* fragment; 00046 uint8_t *split_buf; 00047 int split_pos, split_buf_len, split_buf_size; 00048 int split_pkts; 00049 }; 00050 00051 static PayloadContext *xiph_new_context(void) 00052 { 00053 return av_mallocz(sizeof(PayloadContext)); 00054 } 00055 00056 static inline void free_fragment_if_needed(PayloadContext * data) 00057 { 00058 if (data->fragment) { 00059 uint8_t* p; 00060 avio_close_dyn_buf(data->fragment, &p); 00061 av_free(p); 00062 data->fragment = NULL; 00063 } 00064 } 00065 00066 static void xiph_free_context(PayloadContext * data) 00067 { 00068 free_fragment_if_needed(data); 00069 av_free(data->split_buf); 00070 av_free(data); 00071 } 00072 00073 static int xiph_handle_packet(AVFormatContext * ctx, 00074 PayloadContext * data, 00075 AVStream * st, 00076 AVPacket * pkt, 00077 uint32_t * timestamp, 00078 const uint8_t * buf, int len, int flags) 00079 { 00080 00081 int ident, fragmented, tdt, num_pkts, pkt_len; 00082 00083 if (!buf) { 00084 if (!data->split_buf || data->split_pos + 2 > data->split_buf_len || 00085 data->split_pkts <= 0) { 00086 av_log(ctx, AV_LOG_ERROR, "No more data to return\n"); 00087 return AVERROR_INVALIDDATA; 00088 } 00089 pkt_len = AV_RB16(data->split_buf + data->split_pos); 00090 data->split_pos += 2; 00091 if (data->split_pos + pkt_len > data->split_buf_len) { 00092 av_log(ctx, AV_LOG_ERROR, "Not enough data to return\n"); 00093 return AVERROR_INVALIDDATA; 00094 } 00095 if (av_new_packet(pkt, pkt_len)) { 00096 av_log(ctx, AV_LOG_ERROR, "Out of memory.\n"); 00097 return AVERROR(ENOMEM); 00098 } 00099 pkt->stream_index = st->index; 00100 memcpy(pkt->data, data->split_buf + data->split_pos, pkt_len); 00101 data->split_pos += pkt_len; 00102 data->split_pkts--; 00103 return data->split_pkts > 0; 00104 } 00105 00106 if (len < 6) { 00107 av_log(ctx, AV_LOG_ERROR, "Invalid %d byte packet\n", len); 00108 return AVERROR_INVALIDDATA; 00109 } 00110 00111 // read xiph rtp headers 00112 ident = AV_RB24(buf); 00113 fragmented = buf[3] >> 6; 00114 tdt = (buf[3] >> 4) & 3; 00115 num_pkts = buf[3] & 0xf; 00116 pkt_len = AV_RB16(buf + 4); 00117 00118 if (pkt_len > len - 6) { 00119 av_log(ctx, AV_LOG_ERROR, 00120 "Invalid packet length %d in %d byte packet\n", pkt_len, 00121 len); 00122 return AVERROR_INVALIDDATA; 00123 } 00124 00125 if (ident != data->ident) { 00126 av_log(ctx, AV_LOG_ERROR, 00127 "Unimplemented Xiph SDP configuration change detected\n"); 00128 return AVERROR_PATCHWELCOME; 00129 } 00130 00131 if (tdt) { 00132 av_log(ctx, AV_LOG_ERROR, 00133 "Unimplemented RTP Xiph packet settings (%d,%d,%d)\n", 00134 fragmented, tdt, num_pkts); 00135 return AVERROR_PATCHWELCOME; 00136 } 00137 00138 buf += 6; // move past header bits 00139 len -= 6; 00140 00141 if (fragmented == 0) { 00142 if (av_new_packet(pkt, pkt_len)) { 00143 av_log(ctx, AV_LOG_ERROR, "Out of memory.\n"); 00144 return AVERROR(ENOMEM); 00145 } 00146 pkt->stream_index = st->index; 00147 memcpy(pkt->data, buf, pkt_len); 00148 buf += pkt_len; 00149 len -= pkt_len; 00150 num_pkts--; 00151 00152 if (num_pkts > 0) { 00153 if (len > data->split_buf_size || !data->split_buf) { 00154 av_freep(&data->split_buf); 00155 data->split_buf_size = 2 * len; 00156 data->split_buf = av_malloc(data->split_buf_size); 00157 if (!data->split_buf) { 00158 av_log(ctx, AV_LOG_ERROR, "Out of memory.\n"); 00159 av_free_packet(pkt); 00160 return AVERROR(ENOMEM); 00161 } 00162 } 00163 memcpy(data->split_buf, buf, len); 00164 data->split_buf_len = len; 00165 data->split_pos = 0; 00166 data->split_pkts = num_pkts; 00167 return 1; 00168 } 00169 00170 return 0; 00171 00172 } else if (fragmented == 1) { 00173 // start of xiph data fragment 00174 int res; 00175 00176 // end packet has been lost somewhere, so drop buffered data 00177 free_fragment_if_needed(data); 00178 00179 if((res = avio_open_dyn_buf(&data->fragment)) < 0) 00180 return res; 00181 00182 avio_write(data->fragment, buf, pkt_len); 00183 data->timestamp = *timestamp; 00184 00185 } else { 00186 assert(fragmented < 4); 00187 if (data->timestamp != *timestamp) { 00188 // skip if fragmented timestamp is incorrect; 00189 // a start packet has been lost somewhere 00190 free_fragment_if_needed(data); 00191 av_log(ctx, AV_LOG_ERROR, "RTP timestamps don't match!\n"); 00192 return AVERROR_INVALIDDATA; 00193 } 00194 if (!data->fragment) { 00195 av_log(ctx, AV_LOG_WARNING, 00196 "Received packet without a start fragment; dropping.\n"); 00197 return AVERROR(EAGAIN); 00198 } 00199 00200 // copy data to fragment buffer 00201 avio_write(data->fragment, buf, pkt_len); 00202 00203 if (fragmented == 3) { 00204 // end of xiph data packet 00205 av_init_packet(pkt); 00206 pkt->size = avio_close_dyn_buf(data->fragment, &pkt->data); 00207 00208 if (pkt->size < 0) { 00209 av_log(ctx, AV_LOG_ERROR, 00210 "Error occurred when getting fragment buffer."); 00211 return pkt->size; 00212 } 00213 00214 pkt->stream_index = st->index; 00215 pkt->destruct = av_destruct_packet; 00216 00217 data->fragment = NULL; 00218 00219 return 0; 00220 } 00221 } 00222 00223 return AVERROR(EAGAIN); 00224 } 00225 00229 static int get_base128(const uint8_t ** buf, const uint8_t * buf_end) 00230 { 00231 int n = 0; 00232 for (; *buf < buf_end; ++*buf) { 00233 n <<= 7; 00234 n += **buf & 0x7f; 00235 if (!(**buf & 0x80)) { 00236 ++*buf; 00237 return n; 00238 } 00239 } 00240 return 0; 00241 } 00242 00246 static unsigned int 00247 parse_packed_headers(const uint8_t * packed_headers, 00248 const uint8_t * packed_headers_end, 00249 AVCodecContext * codec, PayloadContext * xiph_data) 00250 { 00251 00252 unsigned num_packed, num_headers, length, length1, length2, extradata_alloc; 00253 uint8_t *ptr; 00254 00255 if (packed_headers_end - packed_headers < 9) { 00256 av_log(codec, AV_LOG_ERROR, 00257 "Invalid %td byte packed header.", 00258 packed_headers_end - packed_headers); 00259 return AVERROR_INVALIDDATA; 00260 } 00261 00262 num_packed = bytestream_get_be32(&packed_headers); 00263 xiph_data->ident = bytestream_get_be24(&packed_headers); 00264 length = bytestream_get_be16(&packed_headers); 00265 num_headers = get_base128(&packed_headers, packed_headers_end); 00266 length1 = get_base128(&packed_headers, packed_headers_end); 00267 length2 = get_base128(&packed_headers, packed_headers_end); 00268 00269 if (num_packed != 1 || num_headers > 3) { 00270 av_log(codec, AV_LOG_ERROR, 00271 "Unimplemented number of headers: %d packed headers, %d headers\n", 00272 num_packed, num_headers); 00273 return AVERROR_PATCHWELCOME; 00274 } 00275 00276 if (packed_headers_end - packed_headers != length || 00277 length1 > length || length2 > length - length1) { 00278 av_log(codec, AV_LOG_ERROR, 00279 "Bad packed header lengths (%d,%d,%td,%d)\n", length1, 00280 length2, packed_headers_end - packed_headers, length); 00281 return AVERROR_INVALIDDATA; 00282 } 00283 00284 /* allocate extra space: 00285 * -- length/255 +2 for xiphlacing 00286 * -- one for the '2' marker 00287 * -- FF_INPUT_BUFFER_PADDING_SIZE required */ 00288 extradata_alloc = length + length/255 + 3 + FF_INPUT_BUFFER_PADDING_SIZE; 00289 00290 ptr = codec->extradata = av_malloc(extradata_alloc); 00291 if (!ptr) { 00292 av_log(codec, AV_LOG_ERROR, "Out of memory\n"); 00293 return AVERROR(ENOMEM); 00294 } 00295 *ptr++ = 2; 00296 ptr += av_xiphlacing(ptr, length1); 00297 ptr += av_xiphlacing(ptr, length2); 00298 memcpy(ptr, packed_headers, length); 00299 ptr += length; 00300 codec->extradata_size = ptr - codec->extradata; 00301 // clear out remaining parts of the buffer 00302 memset(ptr, 0, extradata_alloc - codec->extradata_size); 00303 00304 return 0; 00305 } 00306 00307 static int xiph_parse_fmtp_pair(AVStream* stream, 00308 PayloadContext *xiph_data, 00309 char *attr, char *value) 00310 { 00311 AVCodecContext *codec = stream->codec; 00312 int result = 0; 00313 00314 if (!strcmp(attr, "sampling")) { 00315 if (!strcmp(value, "YCbCr-4:2:0")) { 00316 codec->pix_fmt = PIX_FMT_YUV420P; 00317 } else if (!strcmp(value, "YCbCr-4:4:2")) { 00318 codec->pix_fmt = PIX_FMT_YUV422P; 00319 } else if (!strcmp(value, "YCbCr-4:4:4")) { 00320 codec->pix_fmt = PIX_FMT_YUV444P; 00321 } else { 00322 av_log(codec, AV_LOG_ERROR, 00323 "Unsupported pixel format %s\n", attr); 00324 return AVERROR_INVALIDDATA; 00325 } 00326 } else if (!strcmp(attr, "width")) { 00327 /* This is an integer between 1 and 1048561 00328 * and MUST be in multiples of 16. */ 00329 codec->width = atoi(value); 00330 return 0; 00331 } else if (!strcmp(attr, "height")) { 00332 /* This is an integer between 1 and 1048561 00333 * and MUST be in multiples of 16. */ 00334 codec->height = atoi(value); 00335 return 0; 00336 } else if (!strcmp(attr, "delivery-method")) { 00337 /* Possible values are: inline, in_band, out_band/specific_name. */ 00338 return AVERROR_PATCHWELCOME; 00339 } else if (!strcmp(attr, "configuration-uri")) { 00340 /* NOTE: configuration-uri is supported only under 2 conditions: 00341 *--after the delivery-method tag 00342 * --with a delivery-method value of out_band */ 00343 return AVERROR_PATCHWELCOME; 00344 } else if (!strcmp(attr, "configuration")) { 00345 /* NOTE: configuration is supported only AFTER the delivery-method tag 00346 * The configuration value is a base64 encoded packed header */ 00347 uint8_t *decoded_packet = NULL; 00348 int packet_size; 00349 size_t decoded_alloc = strlen(value) / 4 * 3 + 4; 00350 00351 if (decoded_alloc <= INT_MAX) { 00352 decoded_packet = av_malloc(decoded_alloc); 00353 if (decoded_packet) { 00354 packet_size = 00355 av_base64_decode(decoded_packet, value, decoded_alloc); 00356 00357 result = parse_packed_headers 00358 (decoded_packet, decoded_packet + packet_size, codec, 00359 xiph_data); 00360 } else { 00361 av_log(codec, AV_LOG_ERROR, 00362 "Out of memory while decoding SDP configuration.\n"); 00363 result = AVERROR(ENOMEM); 00364 } 00365 } else { 00366 av_log(codec, AV_LOG_ERROR, "Packet too large\n"); 00367 result = AVERROR_INVALIDDATA; 00368 } 00369 av_free(decoded_packet); 00370 } 00371 return result; 00372 } 00373 00374 static int xiph_parse_sdp_line(AVFormatContext *s, int st_index, 00375 PayloadContext *data, const char *line) 00376 { 00377 const char *p; 00378 00379 if (av_strstart(line, "fmtp:", &p)) { 00380 return ff_parse_fmtp(s->streams[st_index], data, p, 00381 xiph_parse_fmtp_pair); 00382 } 00383 00384 return 0; 00385 } 00386 00387 RTPDynamicProtocolHandler ff_theora_dynamic_handler = { 00388 .enc_name = "theora", 00389 .codec_type = AVMEDIA_TYPE_VIDEO, 00390 .codec_id = CODEC_ID_THEORA, 00391 .parse_sdp_a_line = xiph_parse_sdp_line, 00392 .alloc = xiph_new_context, 00393 .free = xiph_free_context, 00394 .parse_packet = xiph_handle_packet 00395 }; 00396 00397 RTPDynamicProtocolHandler ff_vorbis_dynamic_handler = { 00398 .enc_name = "vorbis", 00399 .codec_type = AVMEDIA_TYPE_AUDIO, 00400 .codec_id = CODEC_ID_VORBIS, 00401 .parse_sdp_a_line = xiph_parse_sdp_line, 00402 .alloc = xiph_new_context, 00403 .free = xiph_free_context, 00404 .parse_packet = xiph_handle_packet 00405 };