diff options
author | Ronald S. Bultje <rsbultje@gmail.com> | 2009-04-14 15:01:46 +0000 |
---|---|---|
committer | Ronald S. Bultje <rsbultje@gmail.com> | 2009-04-14 15:01:46 +0000 |
commit | e6327fba9859eac1e87d02d91a6834090c16eb3b (patch) | |
tree | 0cba35cf567a40457457d57bc52883c74c704c24 | |
parent | d648c720af2f011b3d5884bffbefed069cd815d2 (diff) | |
download | ffmpeg-e6327fba9859eac1e87d02d91a6834090c16eb3b.tar.gz |
Add a Vorbis payload parser. Implemented by Colin McQuillan as a GSoC
qualification task, see "RTP/Vorbis payload implementation (GSoC qual
task)" thread on mailinglist.
Originally committed as revision 18509 to svn://svn.ffmpeg.org/ffmpeg/trunk
-rw-r--r-- | Changelog | 1 | ||||
-rw-r--r-- | libavformat/Makefile | 2 | ||||
-rw-r--r-- | libavformat/rtp_vorbis.c | 218 | ||||
-rw-r--r-- | libavformat/rtp_vorbis.h | 45 | ||||
-rw-r--r-- | libavformat/rtpdec.c | 2 | ||||
-rw-r--r-- | libavformat/rtsp.c | 10 |
6 files changed, 275 insertions, 3 deletions
@@ -8,6 +8,7 @@ version <next>: - PCX encoder - RTP packetization of H.263 - RTP packetization of AMR +- RTP depacketization of Vorbis diff --git a/libavformat/Makefile b/libavformat/Makefile index e04b86f42e..4335332bd0 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -196,7 +196,7 @@ OBJS-$(CONFIG_RTP_MUXER) += rtp.o \ rtpenc_h264.o \ avc.o OBJS-$(CONFIG_RTSP_DEMUXER) += rdt.o rtsp.o -OBJS-$(CONFIG_SDP_DEMUXER) += rtsp.o rtp.o rtpdec.o rtp_h264.o +OBJS-$(CONFIG_SDP_DEMUXER) += rtsp.o rtp.o rtpdec.o rtp_h264.o rtp_vorbis.o OBJS-$(CONFIG_SEGAFILM_DEMUXER) += segafilm.o OBJS-$(CONFIG_SHORTEN_DEMUXER) += raw.o id3v2.o OBJS-$(CONFIG_SIFF_DEMUXER) += siff.o diff --git a/libavformat/rtp_vorbis.c b/libavformat/rtp_vorbis.c new file mode 100644 index 0000000000..d9bdf4909d --- /dev/null +++ b/libavformat/rtp_vorbis.c @@ -0,0 +1,218 @@ +/* + * RTP Vorbis Protocol (RFC5215) + * Copyright (c) 2009 Colin McQuillan + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file libavformat/rtp_vorbis.c + * @brief Vorbis / RTP Code (RFC 5215) + * @author Colin McQuillan <m.niloc@gmail.com> + */ + +#include "libavutil/base64.h" +#include "libavutil/avstring.h" +#include "libavcodec/bytestream.h" + +#include <assert.h> + +#include "rtpdec.h" +#include "rtp_vorbis.h" + +/** + * RTP/Vorbis specific private data. + */ +struct PayloadContext { + unsigned ident; ///< 24-bit stream configuration identifier +}; + +/** + * Length encoding described in RFC5215 section 3.1.1. + */ +static int get_base128(const uint8_t ** buf, const uint8_t * buf_end) +{ + int n = 0; + for (; *buf < buf_end; ++*buf) { + n <<= 7; + n += **buf & 0x7f; + if (!(**buf & 0x80)) { + ++*buf; + return n; + } + } + return 0; +} + +/** + * Out-of-band headers, described in RFC 5251 section 3.2.1 + */ +static unsigned int +parse_packed_headers(const uint8_t * packed_headers, + const uint8_t * packed_headers_end, + AVCodecContext * codec, PayloadContext * vorbis_data) +{ + unsigned num_packed, num_headers, length, length1, length2; + uint8_t *ptr; + + num_packed = bytestream_get_be32(&packed_headers); + vorbis_data->ident = bytestream_get_be24(&packed_headers); + length = bytestream_get_be16(&packed_headers); + num_headers = get_base128(&packed_headers, packed_headers_end); + length1 = get_base128(&packed_headers, packed_headers_end); + length2 = get_base128(&packed_headers, packed_headers_end); + + if (num_packed != 1 || num_headers > 3) { + av_log(codec, AV_LOG_ERROR, + "Unimplemented number of headers: %d packed headers, %d headers\n", + num_packed, num_headers); + return AVERROR_PATCHWELCOME; + } + + if (packed_headers_end - packed_headers != length || + length1 > length || length2 > length - length1) { + av_log(codec, AV_LOG_ERROR, + "Bad packed header lengths (%d,%d,%d,%d)\n", length1, + length2, packed_headers_end - packed_headers, length); + return AVERROR_INVALIDDATA; + } + + ptr = codec->extradata = av_mallocz(length + length / 255 + 64); + if (!ptr) { + av_log(codec, AV_LOG_ERROR, "Out of memory"); + return AVERROR_NOMEM; + } + *ptr++ = 2; + ptr += av_xiphlacing(ptr, length1); + ptr += av_xiphlacing(ptr, length2); + memcpy(ptr, packed_headers, length); + ptr += length; + codec->extradata_size = ptr - codec->extradata; + + return 0; +} + +int +ff_vorbis_parse_fmtp_config(AVCodecContext * codec, + void *vorbis_data, char *attr, char *value) +{ + int result = 0; + assert(codec->codec_id == CODEC_ID_VORBIS); + assert(vorbis_data); + + // The configuration value is a base64 encoded packed header + if (!strcmp(attr, "configuration")) { + uint8_t *decoded_packet = NULL; + int packet_size; + size_t decoded_alloc = strlen(value) / 4 * 3 + 4; + + if (decoded_alloc <= INT_MAX) { + decoded_packet = av_malloc(decoded_alloc); + if (decoded_packet) { + packet_size = + av_base64_decode(decoded_packet, value, decoded_alloc); + + result = parse_packed_headers + (decoded_packet, decoded_packet + packet_size, codec, + vorbis_data); + } else { + av_log(codec, AV_LOG_ERROR, + "Out of memory while decoding SDP configuration.\n"); + result = AVERROR_NOMEM; + } + } else { + av_log(codec, AV_LOG_ERROR, "Packet too large\n"); + result = AVERROR_INVALIDDATA; + } + av_free(decoded_packet); + } + return result; +} + +static PayloadContext *vorbis_new_extradata(void) +{ + return av_mallocz(sizeof(PayloadContext)); +} + +static void vorbis_free_extradata(PayloadContext * data) +{ + av_free(data); +} + +/** + * Handle payload as described in RFC 5215 section 2.2 + */ +static int +vorbis_handle_packet(AVFormatContext * ctx, + PayloadContext * data, + AVStream * st, + AVPacket * pkt, + uint32_t * timestamp, + const uint8_t * buf, int len, int flags) +{ + int ident, fragmented, vdt, num_pkts, pkt_len; + + if (len < 6) { + av_log(ctx, AV_LOG_ERROR, "Invalid %d byte packet\n", len); + return AVERROR_INVALIDDATA; + } + + ident = AV_RB24(buf); + fragmented = buf[3] >> 6; + vdt = (buf[3] >> 4) & 3; + num_pkts = buf[3] & 7; + pkt_len = AV_RB16(buf + 4); + + if (pkt_len > len - 6) { + av_log(ctx, AV_LOG_ERROR, + "Invalid packet length %d in %d byte packet\n", pkt_len, + len); + return AVERROR_INVALIDDATA; + } + + if (ident != data->ident) { + av_log(ctx, AV_LOG_ERROR, + "Unimplemented Vorbis SDP configuration change detected\n"); + return AVERROR_PATCHWELCOME; + } + + if (fragmented != 0 || vdt != 0 || num_pkts != 1) { + av_log(ctx, AV_LOG_ERROR, + "Unimplemented RTP Vorbis packet settings (%d,%d,%d)\n", + fragmented, vdt, num_pkts); + return AVERROR_PATCHWELCOME; + } + + if (av_new_packet(pkt, pkt_len)) { + av_log(ctx, AV_LOG_ERROR, "Out of memory.\n"); + return AVERROR_NOMEM; + } + + memcpy(pkt->data, buf + 6, pkt_len); + pkt->stream_index = st->index; + return 0; +} + +RTPDynamicProtocolHandler ff_vorbis_dynamic_handler = { + "vorbis", + CODEC_TYPE_AUDIO, + CODEC_ID_VORBIS, + NULL, + vorbis_new_extradata, + vorbis_free_extradata, + vorbis_handle_packet +}; diff --git a/libavformat/rtp_vorbis.h b/libavformat/rtp_vorbis.h new file mode 100644 index 0000000000..bd93ef74d7 --- /dev/null +++ b/libavformat/rtp_vorbis.h @@ -0,0 +1,45 @@ +/* + * RTP Vorbis Protocol (RFC 5215) + * Copyright (c) 2009 Colin McQuillan + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_RTP_VORBIS_H +#define AVFORMAT_RTP_VORBIS_H + +#include "libavcodec/avcodec.h" +#include "rtpdec.h" + +/** + * Handle a Vorbis-specific FMTP parameter + * + * @param codec The context of the codec + * @param ctx Private Vorbis RTP context + * @param attr Format-specific parameter name + * @param value Format-specific paremeter value + */ +int +ff_vorbis_parse_fmtp_config(AVCodecContext * codec, + void *ctx, char *attr, char *value); + +/** + * Vorbis RTP callbacks. + */ +extern RTPDynamicProtocolHandler ff_vorbis_dynamic_handler; + +#endif /* AVFORMAT_RTP_VORBIS_H */ diff --git a/libavformat/rtpdec.c b/libavformat/rtpdec.c index f6793167c8..48995e7de4 100644 --- a/libavformat/rtpdec.c +++ b/libavformat/rtpdec.c @@ -32,6 +32,7 @@ #include "rtpdec.h" #include "rtp_asf.h" #include "rtp_h264.h" +#include "rtp_vorbis.h" //#define DEBUG @@ -61,6 +62,7 @@ void av_register_rtp_dynamic_payload_handlers(void) ff_register_dynamic_payload_handler(&mp4v_es_handler); ff_register_dynamic_payload_handler(&mpeg4_generic_handler); ff_register_dynamic_payload_handler(&ff_h264_dynamic_handler); + ff_register_dynamic_payload_handler(&ff_vorbis_dynamic_handler); ff_register_dynamic_payload_handler(&ff_ms_rtp_asf_pfv_handler); ff_register_dynamic_payload_handler(&ff_ms_rtp_asf_pfa_handler); diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c index bb78def919..362714b4a4 100644 --- a/libavformat/rtsp.c +++ b/libavformat/rtsp.c @@ -37,6 +37,7 @@ #include "rtpdec.h" #include "rdt.h" #include "rtp_asf.h" +#include "rtp_vorbis.h" //#define DEBUG //#define DEBUG_RTP_TCP @@ -196,7 +197,8 @@ static int hex_to_data(uint8_t *data, const char *p) return len; } -static void sdp_parse_fmtp_config(AVCodecContext *codec, char *attr, char *value) +static void sdp_parse_fmtp_config(AVCodecContext * codec, void *ctx, + char *attr, char *value) { switch (codec->codec_id) { case CODEC_ID_MPEG4: @@ -213,6 +215,9 @@ static void sdp_parse_fmtp_config(AVCodecContext *codec, char *attr, char *value hex_to_data(codec->extradata, value); } break; + case CODEC_ID_VORBIS: + ff_vorbis_parse_fmtp_config(codec, ctx, attr, value); + break; default: break; } @@ -274,7 +279,8 @@ static void sdp_parse_fmtp(AVStream *st, const char *p) while(rtsp_next_attr_and_value(&p, attr, sizeof(attr), value, sizeof(value))) { /* grab the codec extra_data from the config parameter of the fmtp line */ - sdp_parse_fmtp_config(codec, attr, value); + sdp_parse_fmtp_config(codec, rtsp_st->dynamic_protocol_context, + attr, value); /* Looking for a known attribute */ for (i = 0; attr_names[i].str; ++i) { if (!strcasecmp(attr, attr_names[i].str)) { |