| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * Xiph RTP Protocols | ||
| 3 | * Copyright (c) 2009 Colin McQuillian | ||
| 4 | * Copyright (c) 2010 Josh Allmann | ||
| 5 | * | ||
| 6 | * This file is part of FFmpeg. | ||
| 7 | * | ||
| 8 | * FFmpeg is free software; you can redistribute it and/or | ||
| 9 | * modify it under the terms of the GNU Lesser General Public | ||
| 10 | * License as published by the Free Software Foundation; either | ||
| 11 | * version 2.1 of the License, or (at your option) any later version. | ||
| 12 | * | ||
| 13 | * FFmpeg is distributed in the hope that it will be useful, | ||
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 16 | * Lesser General Public License for more details. | ||
| 17 | * | ||
| 18 | * You should have received a copy of the GNU Lesser General Public | ||
| 19 | * License along with FFmpeg; if not, write to the Free Software | ||
| 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 21 | */ | ||
| 22 | |||
| 23 | /** | ||
| 24 | * @file | ||
| 25 | * @brief Xiph / RTP Code | ||
| 26 | * @author Colin McQuillan <m.niloc@gmail.com> | ||
| 27 | * @author Josh Allmann <joshua.allmann@gmail.com> | ||
| 28 | */ | ||
| 29 | |||
| 30 | #include "libavutil/attributes.h" | ||
| 31 | #include "libavutil/avassert.h" | ||
| 32 | #include "libavutil/avstring.h" | ||
| 33 | #include "libavutil/base64.h" | ||
| 34 | #include "libavutil/mem.h" | ||
| 35 | #include "libavcodec/bytestream.h" | ||
| 36 | |||
| 37 | #include "avio_internal.h" | ||
| 38 | #include "internal.h" | ||
| 39 | #include "rtpdec.h" | ||
| 40 | #include "rtpdec_formats.h" | ||
| 41 | |||
| 42 | /** | ||
| 43 | * RTP/Xiph specific private data. | ||
| 44 | */ | ||
| 45 | struct PayloadContext { | ||
| 46 | unsigned ident; ///< 24-bit stream configuration identifier | ||
| 47 | uint32_t timestamp; | ||
| 48 | AVIOContext* fragment; ///< buffer for split payloads | ||
| 49 | uint8_t *split_buf; | ||
| 50 | int split_pos, split_buf_len, split_buf_size; | ||
| 51 | int split_pkts; | ||
| 52 | }; | ||
| 53 | |||
| 54 | ✗ | static void xiph_close_context(PayloadContext * data) | |
| 55 | { | ||
| 56 | ✗ | ffio_free_dyn_buf(&data->fragment); | |
| 57 | ✗ | av_freep(&data->split_buf); | |
| 58 | ✗ | } | |
| 59 | |||
| 60 | |||
| 61 | ✗ | static int xiph_handle_packet(AVFormatContext *ctx, PayloadContext *data, | |
| 62 | AVStream *st, AVPacket *pkt, uint32_t *timestamp, | ||
| 63 | const uint8_t *buf, int len, uint16_t seq, | ||
| 64 | int flags) | ||
| 65 | { | ||
| 66 | |||
| 67 | int ident, fragmented, tdt, num_pkts, pkt_len, ret; | ||
| 68 | |||
| 69 | ✗ | if (!buf) { | |
| 70 | ✗ | if (!data->split_buf || data->split_pos + 2 > data->split_buf_len || | |
| 71 | ✗ | data->split_pkts <= 0) { | |
| 72 | ✗ | av_log(ctx, AV_LOG_ERROR, "No more data to return\n"); | |
| 73 | ✗ | return AVERROR_INVALIDDATA; | |
| 74 | } | ||
| 75 | ✗ | pkt_len = AV_RB16(data->split_buf + data->split_pos); | |
| 76 | ✗ | data->split_pos += 2; | |
| 77 | ✗ | if (pkt_len > data->split_buf_len - data->split_pos) { | |
| 78 | ✗ | av_log(ctx, AV_LOG_ERROR, "Not enough data to return\n"); | |
| 79 | ✗ | return AVERROR_INVALIDDATA; | |
| 80 | } | ||
| 81 | ✗ | if ((ret = av_new_packet(pkt, pkt_len)) < 0) { | |
| 82 | ✗ | av_log(ctx, AV_LOG_ERROR, "Out of memory.\n"); | |
| 83 | ✗ | return ret; | |
| 84 | } | ||
| 85 | ✗ | pkt->stream_index = st->index; | |
| 86 | ✗ | memcpy(pkt->data, data->split_buf + data->split_pos, pkt_len); | |
| 87 | ✗ | data->split_pos += pkt_len; | |
| 88 | ✗ | data->split_pkts--; | |
| 89 | ✗ | return data->split_pkts > 0; | |
| 90 | } | ||
| 91 | |||
| 92 | ✗ | if (len < 6 || len > INT_MAX/2) { | |
| 93 | ✗ | av_log(ctx, AV_LOG_ERROR, "Invalid %d byte packet\n", len); | |
| 94 | ✗ | return AVERROR_INVALIDDATA; | |
| 95 | } | ||
| 96 | |||
| 97 | // read xiph rtp headers | ||
| 98 | ✗ | ident = AV_RB24(buf); | |
| 99 | ✗ | fragmented = buf[3] >> 6; | |
| 100 | ✗ | tdt = (buf[3] >> 4) & 3; | |
| 101 | ✗ | num_pkts = buf[3] & 0xf; | |
| 102 | ✗ | pkt_len = AV_RB16(buf + 4); | |
| 103 | |||
| 104 | ✗ | if (pkt_len > len - 6) { | |
| 105 | ✗ | av_log(ctx, AV_LOG_ERROR, | |
| 106 | "Invalid packet length %d in %d byte packet\n", pkt_len, | ||
| 107 | len); | ||
| 108 | ✗ | return AVERROR_INVALIDDATA; | |
| 109 | } | ||
| 110 | |||
| 111 | ✗ | if (ident != data->ident) { | |
| 112 | ✗ | avpriv_report_missing_feature(ctx, "Xiph SDP configuration change"); | |
| 113 | ✗ | return AVERROR_PATCHWELCOME; | |
| 114 | } | ||
| 115 | |||
| 116 | ✗ | if (tdt) { | |
| 117 | ✗ | avpriv_report_missing_feature(ctx, | |
| 118 | "RTP Xiph packet settings (%d,%d,%d)", | ||
| 119 | fragmented, tdt, num_pkts); | ||
| 120 | ✗ | return AVERROR_PATCHWELCOME; | |
| 121 | } | ||
| 122 | |||
| 123 | ✗ | buf += 6; // move past header bits | |
| 124 | ✗ | len -= 6; | |
| 125 | |||
| 126 | ✗ | if (fragmented == 0) { | |
| 127 | ✗ | if ((ret = av_new_packet(pkt, pkt_len)) < 0) { | |
| 128 | ✗ | av_log(ctx, AV_LOG_ERROR, "Out of memory.\n"); | |
| 129 | ✗ | return ret; | |
| 130 | } | ||
| 131 | ✗ | pkt->stream_index = st->index; | |
| 132 | ✗ | memcpy(pkt->data, buf, pkt_len); | |
| 133 | ✗ | buf += pkt_len; | |
| 134 | ✗ | len -= pkt_len; | |
| 135 | ✗ | num_pkts--; | |
| 136 | |||
| 137 | ✗ | if (num_pkts > 0) { | |
| 138 | ✗ | if (len > data->split_buf_size || !data->split_buf) { | |
| 139 | ✗ | av_freep(&data->split_buf); | |
| 140 | ✗ | data->split_buf_size = 2 * len; | |
| 141 | ✗ | data->split_buf = av_malloc(data->split_buf_size); | |
| 142 | ✗ | if (!data->split_buf) { | |
| 143 | ✗ | av_log(ctx, AV_LOG_ERROR, "Out of memory.\n"); | |
| 144 | ✗ | av_packet_unref(pkt); | |
| 145 | ✗ | return AVERROR(ENOMEM); | |
| 146 | } | ||
| 147 | } | ||
| 148 | ✗ | memcpy(data->split_buf, buf, len); | |
| 149 | ✗ | data->split_buf_len = len; | |
| 150 | ✗ | data->split_pos = 0; | |
| 151 | ✗ | data->split_pkts = num_pkts; | |
| 152 | ✗ | return 1; | |
| 153 | } | ||
| 154 | |||
| 155 | ✗ | return 0; | |
| 156 | |||
| 157 | ✗ | } else if (fragmented == 1) { | |
| 158 | // start of xiph data fragment | ||
| 159 | int res; | ||
| 160 | |||
| 161 | // end packet has been lost somewhere, so drop buffered data | ||
| 162 | ✗ | ffio_free_dyn_buf(&data->fragment); | |
| 163 | |||
| 164 | ✗ | if((res = avio_open_dyn_buf(&data->fragment)) < 0) | |
| 165 | ✗ | return res; | |
| 166 | |||
| 167 | ✗ | avio_write(data->fragment, buf, pkt_len); | |
| 168 | ✗ | data->timestamp = *timestamp; | |
| 169 | |||
| 170 | } else { | ||
| 171 | av_assert1(fragmented < 4); | ||
| 172 | ✗ | if (data->timestamp != *timestamp) { | |
| 173 | // skip if fragmented timestamp is incorrect; | ||
| 174 | // a start packet has been lost somewhere | ||
| 175 | ✗ | ffio_free_dyn_buf(&data->fragment); | |
| 176 | ✗ | av_log(ctx, AV_LOG_ERROR, "RTP timestamps don't match!\n"); | |
| 177 | ✗ | return AVERROR_INVALIDDATA; | |
| 178 | } | ||
| 179 | ✗ | if (!data->fragment) { | |
| 180 | ✗ | av_log(ctx, AV_LOG_WARNING, | |
| 181 | "Received packet without a start fragment; dropping.\n"); | ||
| 182 | ✗ | return AVERROR(EAGAIN); | |
| 183 | } | ||
| 184 | |||
| 185 | // copy data to fragment buffer | ||
| 186 | ✗ | avio_write(data->fragment, buf, pkt_len); | |
| 187 | |||
| 188 | ✗ | if (fragmented == 3) { | |
| 189 | // end of xiph data packet | ||
| 190 | ✗ | int ret = ff_rtp_finalize_packet(pkt, &data->fragment, st->index); | |
| 191 | ✗ | if (ret < 0) { | |
| 192 | ✗ | av_log(ctx, AV_LOG_ERROR, | |
| 193 | "Error occurred when getting fragment buffer."); | ||
| 194 | ✗ | return ret; | |
| 195 | } | ||
| 196 | |||
| 197 | ✗ | return 0; | |
| 198 | } | ||
| 199 | } | ||
| 200 | |||
| 201 | ✗ | return AVERROR(EAGAIN); | |
| 202 | } | ||
| 203 | |||
| 204 | /** | ||
| 205 | * Length encoding described in RFC5215 section 3.1.1. | ||
| 206 | */ | ||
| 207 | ✗ | static int get_base128(const uint8_t ** buf, const uint8_t * buf_end) | |
| 208 | { | ||
| 209 | ✗ | int n = 0; | |
| 210 | ✗ | for (; *buf < buf_end; ++*buf) { | |
| 211 | ✗ | n <<= 7; | |
| 212 | ✗ | n += **buf & 0x7f; | |
| 213 | ✗ | if (!(**buf & 0x80)) { | |
| 214 | ✗ | ++*buf; | |
| 215 | ✗ | return n; | |
| 216 | } | ||
| 217 | } | ||
| 218 | ✗ | return 0; | |
| 219 | } | ||
| 220 | |||
| 221 | /** | ||
| 222 | * Based off parse_packed_headers in Vorbis RTP | ||
| 223 | */ | ||
| 224 | static int | ||
| 225 | ✗ | parse_packed_headers(AVFormatContext *s, | |
| 226 | const uint8_t * packed_headers, | ||
| 227 | const uint8_t * packed_headers_end, | ||
| 228 | AVCodecParameters *par, PayloadContext * xiph_data) | ||
| 229 | { | ||
| 230 | |||
| 231 | unsigned num_packed, num_headers, length, length1, length2, extradata_alloc; | ||
| 232 | int ret; | ||
| 233 | uint8_t *ptr; | ||
| 234 | |||
| 235 | ✗ | if (packed_headers_end - packed_headers < 9) { | |
| 236 | ✗ | av_log(s, AV_LOG_ERROR, | |
| 237 | "Invalid %"PTRDIFF_SPECIFIER" byte packed header.", | ||
| 238 | packed_headers_end - packed_headers); | ||
| 239 | ✗ | return AVERROR_INVALIDDATA; | |
| 240 | } | ||
| 241 | |||
| 242 | ✗ | num_packed = bytestream_get_be32(&packed_headers); | |
| 243 | ✗ | xiph_data->ident = bytestream_get_be24(&packed_headers); | |
| 244 | ✗ | length = bytestream_get_be16(&packed_headers); | |
| 245 | ✗ | num_headers = get_base128(&packed_headers, packed_headers_end); | |
| 246 | ✗ | length1 = get_base128(&packed_headers, packed_headers_end); | |
| 247 | ✗ | length2 = get_base128(&packed_headers, packed_headers_end); | |
| 248 | |||
| 249 | ✗ | if (num_packed != 1 || num_headers > 3) { | |
| 250 | ✗ | avpriv_report_missing_feature(s, "%u packed headers, %u headers", | |
| 251 | num_packed, num_headers); | ||
| 252 | ✗ | return AVERROR_PATCHWELCOME; | |
| 253 | } | ||
| 254 | |||
| 255 | ✗ | if (packed_headers_end - packed_headers != length || | |
| 256 | ✗ | length1 > length || length2 > length - length1) { | |
| 257 | ✗ | av_log(s, AV_LOG_ERROR, | |
| 258 | "Bad packed header lengths (%d,%d,%"PTRDIFF_SPECIFIER",%u)\n", length1, | ||
| 259 | length2, packed_headers_end - packed_headers, length); | ||
| 260 | ✗ | return AVERROR_INVALIDDATA; | |
| 261 | } | ||
| 262 | |||
| 263 | /* allocate extra space: | ||
| 264 | * -- length/255 +2 for xiphlacing | ||
| 265 | * -- one for the '2' marker | ||
| 266 | * -- AV_INPUT_BUFFER_PADDING_SIZE required */ | ||
| 267 | ✗ | extradata_alloc = length + length/255 + 3 + AV_INPUT_BUFFER_PADDING_SIZE; | |
| 268 | |||
| 269 | ✗ | if ((ret = ff_alloc_extradata(par, extradata_alloc)) < 0) { | |
| 270 | ✗ | av_log(s, AV_LOG_ERROR, "Out of memory\n"); | |
| 271 | ✗ | return ret; | |
| 272 | } | ||
| 273 | ✗ | ptr = par->extradata; | |
| 274 | ✗ | *ptr++ = 2; | |
| 275 | ✗ | ptr += av_xiphlacing(ptr, length1); | |
| 276 | ✗ | ptr += av_xiphlacing(ptr, length2); | |
| 277 | ✗ | memcpy(ptr, packed_headers, length); | |
| 278 | ✗ | ptr += length; | |
| 279 | ✗ | par->extradata_size = ptr - par->extradata; | |
| 280 | // clear out remaining parts of the buffer | ||
| 281 | ✗ | memset(ptr, 0, extradata_alloc - par->extradata_size); | |
| 282 | |||
| 283 | ✗ | return 0; | |
| 284 | } | ||
| 285 | |||
| 286 | ✗ | static int xiph_parse_fmtp_pair(AVFormatContext *s, | |
| 287 | AVStream* stream, | ||
| 288 | PayloadContext *xiph_data, | ||
| 289 | const char *attr, const char *value) | ||
| 290 | { | ||
| 291 | ✗ | AVCodecParameters *par = stream->codecpar; | |
| 292 | ✗ | int result = 0; | |
| 293 | |||
| 294 | ✗ | if (!strcmp(attr, "sampling")) { | |
| 295 | ✗ | if (!strcmp(value, "YCbCr-4:2:0")) { | |
| 296 | ✗ | par->format = AV_PIX_FMT_YUV420P; | |
| 297 | ✗ | } else if (!strcmp(value, "YCbCr-4:4:2")) { | |
| 298 | ✗ | par->format = AV_PIX_FMT_YUV422P; | |
| 299 | ✗ | } else if (!strcmp(value, "YCbCr-4:4:4")) { | |
| 300 | ✗ | par->format = AV_PIX_FMT_YUV444P; | |
| 301 | } else { | ||
| 302 | ✗ | av_log(s, AV_LOG_ERROR, | |
| 303 | "Unsupported pixel format %s\n", attr); | ||
| 304 | ✗ | return AVERROR_INVALIDDATA; | |
| 305 | } | ||
| 306 | ✗ | } else if (!strcmp(attr, "width")) { | |
| 307 | /* This is an integer between 1 and 1048561 | ||
| 308 | * and MUST be in multiples of 16. */ | ||
| 309 | ✗ | par->width = atoi(value); | |
| 310 | ✗ | return 0; | |
| 311 | ✗ | } else if (!strcmp(attr, "height")) { | |
| 312 | /* This is an integer between 1 and 1048561 | ||
| 313 | * and MUST be in multiples of 16. */ | ||
| 314 | ✗ | par->height = atoi(value); | |
| 315 | ✗ | return 0; | |
| 316 | ✗ | } else if (!strcmp(attr, "delivery-method")) { | |
| 317 | /* Possible values are: inline, in_band, out_band/specific_name. */ | ||
| 318 | ✗ | return AVERROR_PATCHWELCOME; | |
| 319 | ✗ | } else if (!strcmp(attr, "configuration-uri")) { | |
| 320 | /* NOTE: configuration-uri is supported only under 2 conditions: | ||
| 321 | *--after the delivery-method tag | ||
| 322 | * --with a delivery-method value of out_band */ | ||
| 323 | ✗ | return AVERROR_PATCHWELCOME; | |
| 324 | ✗ | } else if (!strcmp(attr, "configuration")) { | |
| 325 | /* NOTE: configuration is supported only AFTER the delivery-method tag | ||
| 326 | * The configuration value is a base64 encoded packed header */ | ||
| 327 | ✗ | uint8_t *decoded_packet = NULL; | |
| 328 | int packet_size; | ||
| 329 | ✗ | size_t decoded_alloc = strlen(value) / 4 * 3 + 4; | |
| 330 | |||
| 331 | ✗ | if (decoded_alloc <= INT_MAX) { | |
| 332 | ✗ | decoded_packet = av_malloc(decoded_alloc); | |
| 333 | ✗ | if (decoded_packet) { | |
| 334 | packet_size = | ||
| 335 | ✗ | av_base64_decode(decoded_packet, value, decoded_alloc); | |
| 336 | |||
| 337 | ✗ | result = parse_packed_headers | |
| 338 | ✗ | (s, decoded_packet, decoded_packet + packet_size, par, | |
| 339 | xiph_data); | ||
| 340 | } else { | ||
| 341 | ✗ | av_log(s, AV_LOG_ERROR, | |
| 342 | "Out of memory while decoding SDP configuration.\n"); | ||
| 343 | ✗ | result = AVERROR(ENOMEM); | |
| 344 | } | ||
| 345 | } else { | ||
| 346 | ✗ | av_log(s, AV_LOG_ERROR, "Packet too large\n"); | |
| 347 | ✗ | result = AVERROR_INVALIDDATA; | |
| 348 | } | ||
| 349 | ✗ | av_free(decoded_packet); | |
| 350 | } | ||
| 351 | ✗ | return result; | |
| 352 | } | ||
| 353 | |||
| 354 | ✗ | static int xiph_parse_sdp_line(AVFormatContext *s, int st_index, | |
| 355 | PayloadContext *data, const char *line) | ||
| 356 | { | ||
| 357 | const char *p; | ||
| 358 | |||
| 359 | ✗ | if (st_index < 0) | |
| 360 | ✗ | return 0; | |
| 361 | |||
| 362 | ✗ | if (av_strstart(line, "fmtp:", &p)) { | |
| 363 | ✗ | return ff_parse_fmtp(s, s->streams[st_index], data, p, | |
| 364 | xiph_parse_fmtp_pair); | ||
| 365 | } | ||
| 366 | |||
| 367 | ✗ | return 0; | |
| 368 | } | ||
| 369 | |||
| 370 | const RTPDynamicProtocolHandler ff_theora_dynamic_handler = { | ||
| 371 | .enc_name = "theora", | ||
| 372 | .codec_type = AVMEDIA_TYPE_VIDEO, | ||
| 373 | .codec_id = AV_CODEC_ID_THEORA, | ||
| 374 | .priv_data_size = sizeof(PayloadContext), | ||
| 375 | .parse_sdp_a_line = xiph_parse_sdp_line, | ||
| 376 | .close = xiph_close_context, | ||
| 377 | .parse_packet = xiph_handle_packet, | ||
| 378 | }; | ||
| 379 | |||
| 380 | const RTPDynamicProtocolHandler ff_vorbis_dynamic_handler = { | ||
| 381 | .enc_name = "vorbis", | ||
| 382 | .codec_type = AVMEDIA_TYPE_AUDIO, | ||
| 383 | .codec_id = AV_CODEC_ID_VORBIS, | ||
| 384 | .need_parsing = AVSTREAM_PARSE_HEADERS, | ||
| 385 | .priv_data_size = sizeof(PayloadContext), | ||
| 386 | .parse_sdp_a_line = xiph_parse_sdp_line, | ||
| 387 | .close = xiph_close_context, | ||
| 388 | .parse_packet = xiph_handle_packet, | ||
| 389 | }; | ||
| 390 |