diff options
author | Anssi Hannula <anssi.hannula@iki.fi> | 2010-11-22 22:09:26 +0000 |
---|---|---|
committer | Carl Eugen Hoyos <cehoyos@rainbow.studorg.tuwien.ac.at> | 2010-11-22 22:09:26 +0000 |
commit | 3b39e2739fd2c3fbb52f0824178973fdc1f41382 (patch) | |
tree | 8ff2b302a91b7f23e50875901640617b151d7610 | |
parent | b7be04f3c04fa69b09660dda4e91f569d054bab6 (diff) | |
download | ffmpeg-3b39e2739fd2c3fbb52f0824178973fdc1f41382.tar.gz |
S/PDIF demuxer
Patch by Anssi Hannula, anssi d hannula a iki d fi
Originally committed as revision 25804 to svn://svn.ffmpeg.org/ffmpeg/trunk
-rw-r--r-- | Changelog | 1 | ||||
-rw-r--r-- | doc/general.texi | 2 | ||||
-rw-r--r-- | libavformat/Makefile | 1 | ||||
-rw-r--r-- | libavformat/allformats.c | 2 | ||||
-rw-r--r-- | libavformat/avformat.h | 2 | ||||
-rw-r--r-- | libavformat/spdifdec.c | 236 |
6 files changed, 241 insertions, 3 deletions
@@ -58,6 +58,7 @@ version <next>: - IEC 61937 encapsulation for E-AC3 (for HDMI passthrough) - overlay filter added - rename aspect filter to setdar, and pixelaspect to setsar +- IEC 61937 demuxer version 0.6: diff --git a/doc/general.texi b/doc/general.texi index f59258c8e1..e839c6c00c 100644 --- a/doc/general.texi +++ b/doc/general.texi @@ -103,7 +103,7 @@ library: @item id Quake II CIN video @tab @tab X @item id RoQ @tab X @tab X @tab Used in Quake III, Jedi Knight 2, other computer games. -@item IEC61937 encapsulation @tab X @tab +@item IEC61937 encapsulation @tab X @tab X @item IFF @tab @tab X @tab Interchange File Format @item Interplay MVE @tab @tab X diff --git a/libavformat/Makefile b/libavformat/Makefile index a412abad7f..1f63feee94 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -256,6 +256,7 @@ OBJS-$(CONFIG_SMACKER_DEMUXER) += smacker.o OBJS-$(CONFIG_SOL_DEMUXER) += sol.o pcm.o OBJS-$(CONFIG_SOX_DEMUXER) += soxdec.o pcm.o OBJS-$(CONFIG_SOX_MUXER) += soxenc.o +OBJS-$(CONFIG_SPDIF_DEMUXER) += spdif.o spdifdec.o OBJS-$(CONFIG_SPDIF_MUXER) += spdif.o spdifenc.o OBJS-$(CONFIG_SRT_DEMUXER) += srtdec.o OBJS-$(CONFIG_SRT_MUXER) += rawenc.o diff --git a/libavformat/allformats.c b/libavformat/allformats.c index e35f4f8b0a..df8963502e 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -193,7 +193,7 @@ void av_register_all(void) REGISTER_DEMUXER (SMACKER, smacker); REGISTER_DEMUXER (SOL, sol); REGISTER_MUXDEMUX (SOX, sox); - REGISTER_MUXER (SPDIF, spdif); + REGISTER_MUXDEMUX (SPDIF, spdif); REGISTER_MUXDEMUX (SRT, srt); REGISTER_DEMUXER (STR, str); REGISTER_MUXDEMUX (SWF, swf); diff --git a/libavformat/avformat.h b/libavformat/avformat.h index 1d9b666d60..d34d0d20bb 100644 --- a/libavformat/avformat.h +++ b/libavformat/avformat.h @@ -22,7 +22,7 @@ #define AVFORMAT_AVFORMAT_H #define LIBAVFORMAT_VERSION_MAJOR 52 -#define LIBAVFORMAT_VERSION_MINOR 85 +#define LIBAVFORMAT_VERSION_MINOR 86 #define LIBAVFORMAT_VERSION_MICRO 0 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ diff --git a/libavformat/spdifdec.c b/libavformat/spdifdec.c new file mode 100644 index 0000000000..81e94e7ef1 --- /dev/null +++ b/libavformat/spdifdec.c @@ -0,0 +1,236 @@ +/* + * IEC 61937 demuxer + * Copyright (c) 2010 Anssi Hannula <anssi.hannula at iki.fi> + * + * 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 + * IEC 61937 demuxer, used for compressed data in S/PDIF + * @author Anssi Hannula + */ + +#include "avformat.h" +#include "spdif.h" +#include "libavcodec/ac3.h" +#include "libavcodec/aacadtsdec.h" + +static int spdif_get_offset_and_codec(AVFormatContext *s, + enum IEC958DataType data_type, + const char *buf, int *offset, + enum CodecID *codec) +{ + AACADTSHeaderInfo aac_hdr; + GetBitContext gbc; + + switch (data_type & 0xff) { + case IEC958_AC3: + *offset = AC3_FRAME_SIZE << 2; + *codec = CODEC_ID_AC3; + break; + case IEC958_MPEG1_LAYER1: + *offset = spdif_mpeg_pkt_offset[1][0]; + *codec = CODEC_ID_MP1; + break; + case IEC958_MPEG1_LAYER23: + *offset = spdif_mpeg_pkt_offset[1][0]; + *codec = CODEC_ID_MP3; + break; + case IEC958_MPEG2_EXT: + *offset = 4608; + *codec = CODEC_ID_MP3; + break; + case IEC958_MPEG2_AAC: + init_get_bits(&gbc, buf, AAC_ADTS_HEADER_SIZE * 8); + if (ff_aac_parse_header(&gbc, &aac_hdr)) { + if (s) /* be silent during a probe */ + av_log(s, AV_LOG_ERROR, "Invalid AAC packet in IEC 61937\n"); + return AVERROR_INVALIDDATA; + } + *offset = aac_hdr.samples << 2; + *codec = CODEC_ID_AAC; + break; + case IEC958_MPEG2_LAYER1_LSF: + *offset = spdif_mpeg_pkt_offset[0][0]; + *codec = CODEC_ID_MP1; + break; + case IEC958_MPEG2_LAYER2_LSF: + *offset = spdif_mpeg_pkt_offset[0][1]; + *codec = CODEC_ID_MP2; + break; + case IEC958_MPEG2_LAYER3_LSF: + *offset = spdif_mpeg_pkt_offset[0][2]; + *codec = CODEC_ID_MP3; + break; + case IEC958_DTS1: + *offset = 2048; + *codec = CODEC_ID_DTS; + break; + case IEC958_DTS2: + *offset = 4096; + *codec = CODEC_ID_DTS; + break; + case IEC958_DTS3: + *offset = 8192; + *codec = CODEC_ID_DTS; + break; + default: + if (s) { /* be silent during a probe */ + av_log(s, AV_LOG_WARNING, "Data type 0x%04x", data_type); + av_log_missing_feature(s, " in IEC 61937 is", 1); + } + return AVERROR_PATCHWELCOME; + } + return 0; +} + +/* Largest offset between bursts we currently handle, i.e. AAC with + aac_hdr.samples = 4096 */ +#define SPDIF_MAX_OFFSET 16384 + +static int spdif_probe(AVProbeData *p) +{ + const uint8_t *buf = p->buf; + const uint8_t *probe_end = p->buf + FFMIN(2 * SPDIF_MAX_OFFSET, p->buf_size - 1); + const uint8_t *expected_code = buf + 7; + uint32_t state = 0; + int sync_codes = 0; + int consecutive_codes = 0; + int offset; + enum CodecID codec; + + for (; buf < probe_end; buf++) { + state = (state << 8) | *buf; + + if (state == (AV_BSWAP16C(SYNCWORD1) << 16 | AV_BSWAP16C(SYNCWORD2)) + && buf[1] < 0x37) { + sync_codes++; + + if (buf == expected_code) { + if (++consecutive_codes >= 2) + return AVPROBE_SCORE_MAX; + } else + consecutive_codes = 0; + + if (buf + 4 + AAC_ADTS_HEADER_SIZE > p->buf + p->buf_size) + break; + + /* continue probing to find more sync codes */ + probe_end = FFMIN(buf + SPDIF_MAX_OFFSET, p->buf + p->buf_size - 1); + + /* skip directly to the next sync code */ + if (!spdif_get_offset_and_codec(NULL, (buf[2] << 8) | buf[1], + &buf[5], &offset, &codec)) { + if (buf + offset >= p->buf + p->buf_size) + break; + expected_code = buf + offset; + buf = expected_code - 7; + } + } + } + + if (!sync_codes) + return 0; + + if (sync_codes >= 6) + /* good amount of sync codes but with unexpected offsets */ + return AVPROBE_SCORE_MAX / 2; + + /* some sync codes were found */ + return AVPROBE_SCORE_MAX / 8; +} + +static int spdif_read_header(AVFormatContext *s, AVFormatParameters *ap) +{ + s->ctx_flags |= AVFMTCTX_NOHEADER; + return 0; +} + +static int spdif_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + ByteIOContext *pb = s->pb; + enum IEC958DataType data_type; + enum CodecID codec_id; + uint32_t state = 0; + int pkt_size_bits, offset, ret; + + while (state != (AV_BSWAP16C(SYNCWORD1) << 16 | AV_BSWAP16C(SYNCWORD2))) { + state = (state << 8) | get_byte(pb); + if (url_feof(pb)) + return AVERROR_EOF; + } + + data_type = get_le16(pb); + pkt_size_bits = get_le16(pb); + + if (pkt_size_bits % 16) + av_log_ask_for_sample(s, "Packet does not end to a 16-bit boundary."); + + ret = av_new_packet(pkt, FFALIGN(pkt_size_bits, 16) >> 3); + if (ret) + return ret; + + pkt->pos = url_ftell(pb) - BURST_HEADER_SIZE; + + if (get_buffer(pb, pkt->data, pkt->size) < pkt->size) { + av_free_packet(pkt); + return AVERROR_EOF; + } + ff_spdif_bswap_buf16((uint16_t *)pkt->data, (uint16_t *)pkt->data, pkt->size >> 1); + + ret = spdif_get_offset_and_codec(s, data_type, pkt->data, + &offset, &codec_id); + if (ret) { + av_free_packet(pkt); + return ret; + } + + /* skip over the padding to the beginning of the next frame */ + url_fskip(pb, offset - pkt->size - BURST_HEADER_SIZE); + + if (!s->nb_streams) { + /* first packet, create a stream */ + AVStream *st = av_new_stream(s, 0); + if (!st) { + av_free_packet(pkt); + return AVERROR(ENOMEM); + } + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = codec_id; + } else if (codec_id != s->streams[0]->codec->codec_id) { + av_log_missing_feature(s, "codec change in IEC 61937", 0); + return AVERROR_PATCHWELCOME; + } + + if (!s->bit_rate && s->streams[0]->codec->sample_rate) + /* stream bitrate matches 16-bit stereo PCM bitrate for currently + supported codecs */ + s->bit_rate = 2 * 16 * s->streams[0]->codec->sample_rate; + + return 0; +} + +AVInputFormat spdif_demuxer = { + "spdif", + NULL_IF_CONFIG_SMALL("IEC 61937 (compressed data in S/PDIF)"), + 0, + spdif_probe, + spdif_read_header, + spdif_read_packet, + .flags = AVFMT_GENERIC_INDEX, +}; |