From 3b39e2739fd2c3fbb52f0824178973fdc1f41382 Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Mon, 22 Nov 2010 22:09:26 +0000 Subject: 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 --- libavformat/spdifdec.c | 236 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 236 insertions(+) create mode 100644 libavformat/spdifdec.c (limited to 'libavformat/spdifdec.c') 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 + * + * 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, +}; -- cgit v1.2.3