yading@11: /* yading@11: * Xiph RTP Protocols yading@11: * Copyright (c) 2009 Colin McQuillian yading@11: * Copyright (c) 2010 Josh Allmann yading@11: * yading@11: * This file is part of FFmpeg. yading@11: * yading@11: * FFmpeg is free software; you can redistribute it and/or yading@11: * modify it under the terms of the GNU Lesser General Public yading@11: * License as published by the Free Software Foundation; either yading@11: * version 2.1 of the License, or (at your option) any later version. yading@11: * yading@11: * FFmpeg is distributed in the hope that it will be useful, yading@11: * but WITHOUT ANY WARRANTY; without even the implied warranty of yading@11: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU yading@11: * Lesser General Public License for more details. yading@11: * yading@11: * You should have received a copy of the GNU Lesser General Public yading@11: * License along with FFmpeg; if not, write to the Free Software yading@11: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA yading@11: */ yading@11: yading@11: /** yading@11: * @file yading@11: * @brief Xiph / RTP Code yading@11: * @author Colin McQuillan yading@11: * @author Josh Allmann yading@11: */ yading@11: yading@11: #include "libavutil/avassert.h" yading@11: #include "libavutil/avstring.h" yading@11: #include "libavutil/base64.h" yading@11: #include "libavcodec/bytestream.h" yading@11: yading@11: #include "rtpdec.h" yading@11: #include "rtpdec_formats.h" yading@11: yading@11: /** yading@11: * RTP/Xiph specific private data. yading@11: */ yading@11: struct PayloadContext { yading@11: unsigned ident; ///< 24-bit stream configuration identifier yading@11: uint32_t timestamp; yading@11: AVIOContext* fragment; ///< buffer for split payloads yading@11: uint8_t *split_buf; yading@11: int split_pos, split_buf_len, split_buf_size; yading@11: int split_pkts; yading@11: }; yading@11: yading@11: static PayloadContext *xiph_new_context(void) yading@11: { yading@11: return av_mallocz(sizeof(PayloadContext)); yading@11: } yading@11: yading@11: static inline void free_fragment_if_needed(PayloadContext * data) yading@11: { yading@11: if (data->fragment) { yading@11: uint8_t* p; yading@11: avio_close_dyn_buf(data->fragment, &p); yading@11: av_free(p); yading@11: data->fragment = NULL; yading@11: } yading@11: } yading@11: yading@11: static void xiph_free_context(PayloadContext * data) yading@11: { yading@11: free_fragment_if_needed(data); yading@11: av_free(data->split_buf); yading@11: av_free(data); yading@11: } yading@11: yading@11: static int xiph_vorbis_init(AVFormatContext *ctx, int st_index, yading@11: PayloadContext *data) yading@11: { yading@11: if (st_index < 0) yading@11: return 0; yading@11: ctx->streams[st_index]->need_parsing = AVSTREAM_PARSE_HEADERS; yading@11: return 0; yading@11: } yading@11: yading@11: yading@11: static int xiph_handle_packet(AVFormatContext *ctx, PayloadContext *data, yading@11: AVStream *st, AVPacket *pkt, uint32_t *timestamp, yading@11: const uint8_t *buf, int len, uint16_t seq, yading@11: int flags) yading@11: { yading@11: yading@11: int ident, fragmented, tdt, num_pkts, pkt_len; yading@11: yading@11: if (!buf) { yading@11: if (!data->split_buf || data->split_pos + 2 > data->split_buf_len || yading@11: data->split_pkts <= 0) { yading@11: av_log(ctx, AV_LOG_ERROR, "No more data to return\n"); yading@11: return AVERROR_INVALIDDATA; yading@11: } yading@11: pkt_len = AV_RB16(data->split_buf + data->split_pos); yading@11: data->split_pos += 2; yading@11: if (data->split_pos + pkt_len > data->split_buf_len) { yading@11: av_log(ctx, AV_LOG_ERROR, "Not enough data to return\n"); yading@11: return AVERROR_INVALIDDATA; yading@11: } yading@11: if (av_new_packet(pkt, pkt_len)) { yading@11: av_log(ctx, AV_LOG_ERROR, "Out of memory.\n"); yading@11: return AVERROR(ENOMEM); yading@11: } yading@11: pkt->stream_index = st->index; yading@11: memcpy(pkt->data, data->split_buf + data->split_pos, pkt_len); yading@11: data->split_pos += pkt_len; yading@11: data->split_pkts--; yading@11: return data->split_pkts > 0; yading@11: } yading@11: yading@11: if (len < 6) { yading@11: av_log(ctx, AV_LOG_ERROR, "Invalid %d byte packet\n", len); yading@11: return AVERROR_INVALIDDATA; yading@11: } yading@11: yading@11: // read xiph rtp headers yading@11: ident = AV_RB24(buf); yading@11: fragmented = buf[3] >> 6; yading@11: tdt = (buf[3] >> 4) & 3; yading@11: num_pkts = buf[3] & 0xf; yading@11: pkt_len = AV_RB16(buf + 4); yading@11: yading@11: if (pkt_len > len - 6) { yading@11: av_log(ctx, AV_LOG_ERROR, yading@11: "Invalid packet length %d in %d byte packet\n", pkt_len, yading@11: len); yading@11: return AVERROR_INVALIDDATA; yading@11: } yading@11: yading@11: if (ident != data->ident) { yading@11: av_log(ctx, AV_LOG_ERROR, yading@11: "Unimplemented Xiph SDP configuration change detected\n"); yading@11: return AVERROR_PATCHWELCOME; yading@11: } yading@11: yading@11: if (tdt) { yading@11: av_log(ctx, AV_LOG_ERROR, yading@11: "Unimplemented RTP Xiph packet settings (%d,%d,%d)\n", yading@11: fragmented, tdt, num_pkts); yading@11: return AVERROR_PATCHWELCOME; yading@11: } yading@11: yading@11: buf += 6; // move past header bits yading@11: len -= 6; yading@11: yading@11: if (fragmented == 0) { yading@11: if (av_new_packet(pkt, pkt_len)) { yading@11: av_log(ctx, AV_LOG_ERROR, "Out of memory.\n"); yading@11: return AVERROR(ENOMEM); yading@11: } yading@11: pkt->stream_index = st->index; yading@11: memcpy(pkt->data, buf, pkt_len); yading@11: buf += pkt_len; yading@11: len -= pkt_len; yading@11: num_pkts--; yading@11: yading@11: if (num_pkts > 0) { yading@11: if (len > data->split_buf_size || !data->split_buf) { yading@11: av_freep(&data->split_buf); yading@11: data->split_buf_size = 2 * len; yading@11: data->split_buf = av_malloc(data->split_buf_size); yading@11: if (!data->split_buf) { yading@11: av_log(ctx, AV_LOG_ERROR, "Out of memory.\n"); yading@11: av_free_packet(pkt); yading@11: return AVERROR(ENOMEM); yading@11: } yading@11: } yading@11: memcpy(data->split_buf, buf, len); yading@11: data->split_buf_len = len; yading@11: data->split_pos = 0; yading@11: data->split_pkts = num_pkts; yading@11: return 1; yading@11: } yading@11: yading@11: return 0; yading@11: yading@11: } else if (fragmented == 1) { yading@11: // start of xiph data fragment yading@11: int res; yading@11: yading@11: // end packet has been lost somewhere, so drop buffered data yading@11: free_fragment_if_needed(data); yading@11: yading@11: if((res = avio_open_dyn_buf(&data->fragment)) < 0) yading@11: return res; yading@11: yading@11: avio_write(data->fragment, buf, pkt_len); yading@11: data->timestamp = *timestamp; yading@11: yading@11: } else { yading@11: av_assert1(fragmented < 4); yading@11: if (data->timestamp != *timestamp) { yading@11: // skip if fragmented timestamp is incorrect; yading@11: // a start packet has been lost somewhere yading@11: free_fragment_if_needed(data); yading@11: av_log(ctx, AV_LOG_ERROR, "RTP timestamps don't match!\n"); yading@11: return AVERROR_INVALIDDATA; yading@11: } yading@11: if (!data->fragment) { yading@11: av_log(ctx, AV_LOG_WARNING, yading@11: "Received packet without a start fragment; dropping.\n"); yading@11: return AVERROR(EAGAIN); yading@11: } yading@11: yading@11: // copy data to fragment buffer yading@11: avio_write(data->fragment, buf, pkt_len); yading@11: yading@11: if (fragmented == 3) { yading@11: // end of xiph data packet yading@11: int ret = ff_rtp_finalize_packet(pkt, &data->fragment, st->index); yading@11: if (ret < 0) { yading@11: av_log(ctx, AV_LOG_ERROR, yading@11: "Error occurred when getting fragment buffer."); yading@11: return ret; yading@11: } yading@11: yading@11: return 0; yading@11: } yading@11: } yading@11: yading@11: return AVERROR(EAGAIN); yading@11: } yading@11: yading@11: /** yading@11: * Length encoding described in RFC5215 section 3.1.1. yading@11: */ yading@11: static int get_base128(const uint8_t ** buf, const uint8_t * buf_end) yading@11: { yading@11: int n = 0; yading@11: for (; *buf < buf_end; ++*buf) { yading@11: n <<= 7; yading@11: n += **buf & 0x7f; yading@11: if (!(**buf & 0x80)) { yading@11: ++*buf; yading@11: return n; yading@11: } yading@11: } yading@11: return 0; yading@11: } yading@11: yading@11: /** yading@11: * Based off parse_packed_headers in Vorbis RTP yading@11: */ yading@11: static int yading@11: parse_packed_headers(const uint8_t * packed_headers, yading@11: const uint8_t * packed_headers_end, yading@11: AVCodecContext * codec, PayloadContext * xiph_data) yading@11: { yading@11: yading@11: unsigned num_packed, num_headers, length, length1, length2, extradata_alloc; yading@11: uint8_t *ptr; yading@11: yading@11: if (packed_headers_end - packed_headers < 9) { yading@11: av_log(codec, AV_LOG_ERROR, yading@11: "Invalid %td byte packed header.", yading@11: packed_headers_end - packed_headers); yading@11: return AVERROR_INVALIDDATA; yading@11: } yading@11: yading@11: num_packed = bytestream_get_be32(&packed_headers); yading@11: xiph_data->ident = bytestream_get_be24(&packed_headers); yading@11: length = bytestream_get_be16(&packed_headers); yading@11: num_headers = get_base128(&packed_headers, packed_headers_end); yading@11: length1 = get_base128(&packed_headers, packed_headers_end); yading@11: length2 = get_base128(&packed_headers, packed_headers_end); yading@11: yading@11: if (num_packed != 1 || num_headers > 3) { yading@11: av_log(codec, AV_LOG_ERROR, yading@11: "Unimplemented number of headers: %d packed headers, %d headers\n", yading@11: num_packed, num_headers); yading@11: return AVERROR_PATCHWELCOME; yading@11: } yading@11: yading@11: if (packed_headers_end - packed_headers != length || yading@11: length1 > length || length2 > length - length1) { yading@11: av_log(codec, AV_LOG_ERROR, yading@11: "Bad packed header lengths (%d,%d,%td,%d)\n", length1, yading@11: length2, packed_headers_end - packed_headers, length); yading@11: return AVERROR_INVALIDDATA; yading@11: } yading@11: yading@11: /* allocate extra space: yading@11: * -- length/255 +2 for xiphlacing yading@11: * -- one for the '2' marker yading@11: * -- FF_INPUT_BUFFER_PADDING_SIZE required */ yading@11: extradata_alloc = length + length/255 + 3 + FF_INPUT_BUFFER_PADDING_SIZE; yading@11: yading@11: ptr = codec->extradata = av_malloc(extradata_alloc); yading@11: if (!ptr) { yading@11: av_log(codec, AV_LOG_ERROR, "Out of memory\n"); yading@11: return AVERROR(ENOMEM); yading@11: } yading@11: *ptr++ = 2; yading@11: ptr += av_xiphlacing(ptr, length1); yading@11: ptr += av_xiphlacing(ptr, length2); yading@11: memcpy(ptr, packed_headers, length); yading@11: ptr += length; yading@11: codec->extradata_size = ptr - codec->extradata; yading@11: // clear out remaining parts of the buffer yading@11: memset(ptr, 0, extradata_alloc - codec->extradata_size); yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: static int xiph_parse_fmtp_pair(AVStream* stream, yading@11: PayloadContext *xiph_data, yading@11: char *attr, char *value) yading@11: { yading@11: AVCodecContext *codec = stream->codec; yading@11: int result = 0; yading@11: yading@11: if (!strcmp(attr, "sampling")) { yading@11: if (!strcmp(value, "YCbCr-4:2:0")) { yading@11: codec->pix_fmt = AV_PIX_FMT_YUV420P; yading@11: } else if (!strcmp(value, "YCbCr-4:4:2")) { yading@11: codec->pix_fmt = AV_PIX_FMT_YUV422P; yading@11: } else if (!strcmp(value, "YCbCr-4:4:4")) { yading@11: codec->pix_fmt = AV_PIX_FMT_YUV444P; yading@11: } else { yading@11: av_log(codec, AV_LOG_ERROR, yading@11: "Unsupported pixel format %s\n", attr); yading@11: return AVERROR_INVALIDDATA; yading@11: } yading@11: } else if (!strcmp(attr, "width")) { yading@11: /* This is an integer between 1 and 1048561 yading@11: * and MUST be in multiples of 16. */ yading@11: codec->width = atoi(value); yading@11: return 0; yading@11: } else if (!strcmp(attr, "height")) { yading@11: /* This is an integer between 1 and 1048561 yading@11: * and MUST be in multiples of 16. */ yading@11: codec->height = atoi(value); yading@11: return 0; yading@11: } else if (!strcmp(attr, "delivery-method")) { yading@11: /* Possible values are: inline, in_band, out_band/specific_name. */ yading@11: return AVERROR_PATCHWELCOME; yading@11: } else if (!strcmp(attr, "configuration-uri")) { yading@11: /* NOTE: configuration-uri is supported only under 2 conditions: yading@11: *--after the delivery-method tag yading@11: * --with a delivery-method value of out_band */ yading@11: return AVERROR_PATCHWELCOME; yading@11: } else if (!strcmp(attr, "configuration")) { yading@11: /* NOTE: configuration is supported only AFTER the delivery-method tag yading@11: * The configuration value is a base64 encoded packed header */ yading@11: uint8_t *decoded_packet = NULL; yading@11: int packet_size; yading@11: size_t decoded_alloc = strlen(value) / 4 * 3 + 4; yading@11: yading@11: if (decoded_alloc <= INT_MAX) { yading@11: decoded_packet = av_malloc(decoded_alloc); yading@11: if (decoded_packet) { yading@11: packet_size = yading@11: av_base64_decode(decoded_packet, value, decoded_alloc); yading@11: yading@11: result = parse_packed_headers yading@11: (decoded_packet, decoded_packet + packet_size, codec, yading@11: xiph_data); yading@11: } else { yading@11: av_log(codec, AV_LOG_ERROR, yading@11: "Out of memory while decoding SDP configuration.\n"); yading@11: result = AVERROR(ENOMEM); yading@11: } yading@11: } else { yading@11: av_log(codec, AV_LOG_ERROR, "Packet too large\n"); yading@11: result = AVERROR_INVALIDDATA; yading@11: } yading@11: av_free(decoded_packet); yading@11: } yading@11: return result; yading@11: } yading@11: yading@11: static int xiph_parse_sdp_line(AVFormatContext *s, int st_index, yading@11: PayloadContext *data, const char *line) yading@11: { yading@11: const char *p; yading@11: yading@11: if (st_index < 0) yading@11: return 0; yading@11: yading@11: if (av_strstart(line, "fmtp:", &p)) { yading@11: return ff_parse_fmtp(s->streams[st_index], data, p, yading@11: xiph_parse_fmtp_pair); yading@11: } yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: RTPDynamicProtocolHandler ff_theora_dynamic_handler = { yading@11: .enc_name = "theora", yading@11: .codec_type = AVMEDIA_TYPE_VIDEO, yading@11: .codec_id = AV_CODEC_ID_THEORA, yading@11: .parse_sdp_a_line = xiph_parse_sdp_line, yading@11: .alloc = xiph_new_context, yading@11: .free = xiph_free_context, yading@11: .parse_packet = xiph_handle_packet yading@11: }; yading@11: yading@11: RTPDynamicProtocolHandler ff_vorbis_dynamic_handler = { yading@11: .enc_name = "vorbis", yading@11: .codec_type = AVMEDIA_TYPE_AUDIO, yading@11: .codec_id = AV_CODEC_ID_VORBIS, yading@11: .init = xiph_vorbis_init, yading@11: .parse_sdp_a_line = xiph_parse_sdp_line, yading@11: .alloc = xiph_new_context, yading@11: .free = xiph_free_context, yading@11: .parse_packet = xiph_handle_packet yading@11: };