diff options
Diffstat (limited to 'ffmpeg1/libavformat/spdifdec.c')
| -rw-r--r-- | ffmpeg1/libavformat/spdifdec.c | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/ffmpeg1/libavformat/spdifdec.c b/ffmpeg1/libavformat/spdifdec.c new file mode 100644 index 0000000..726a85c --- /dev/null +++ b/ffmpeg1/libavformat/spdifdec.c @@ -0,0 +1,240 @@ +/* + * 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 IEC61937DataType data_type, + const char *buf, int *offset, + enum AVCodecID *codec) +{ + AACADTSHeaderInfo aac_hdr; + GetBitContext gbc; + + switch (data_type & 0xff) { + case IEC61937_AC3: + *offset = AC3_FRAME_SIZE << 2; + *codec = AV_CODEC_ID_AC3; + break; + case IEC61937_MPEG1_LAYER1: + *offset = spdif_mpeg_pkt_offset[1][0]; + *codec = AV_CODEC_ID_MP1; + break; + case IEC61937_MPEG1_LAYER23: + *offset = spdif_mpeg_pkt_offset[1][0]; + *codec = AV_CODEC_ID_MP3; + break; + case IEC61937_MPEG2_EXT: + *offset = 4608; + *codec = AV_CODEC_ID_MP3; + break; + case IEC61937_MPEG2_AAC: + init_get_bits(&gbc, buf, AAC_ADTS_HEADER_SIZE * 8); + if (avpriv_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 = AV_CODEC_ID_AAC; + break; + case IEC61937_MPEG2_LAYER1_LSF: + *offset = spdif_mpeg_pkt_offset[0][0]; + *codec = AV_CODEC_ID_MP1; + break; + case IEC61937_MPEG2_LAYER2_LSF: + *offset = spdif_mpeg_pkt_offset[0][1]; + *codec = AV_CODEC_ID_MP2; + break; + case IEC61937_MPEG2_LAYER3_LSF: + *offset = spdif_mpeg_pkt_offset[0][2]; + *codec = AV_CODEC_ID_MP3; + break; + case IEC61937_DTS1: + *offset = 2048; + *codec = AV_CODEC_ID_DTS; + break; + case IEC61937_DTS2: + *offset = 4096; + *codec = AV_CODEC_ID_DTS; + break; + case IEC61937_DTS3: + *offset = 8192; + *codec = AV_CODEC_ID_DTS; + break; + default: + if (s) { /* be silent during a probe */ + avpriv_request_sample(s, "Data type 0x%04x in IEC 61937", + data_type); + } + 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) +{ + enum AVCodecID codec; + return ff_spdif_probe (p->buf, p->buf_size, &codec); +} + +int ff_spdif_probe(const uint8_t *p_buf, int buf_size, enum AVCodecID *codec) +{ + const uint8_t *buf = p_buf; + const uint8_t *probe_end = p_buf + FFMIN(2 * SPDIF_MAX_OFFSET, buf_size - 1); + const uint8_t *expected_code = buf + 7; + uint32_t state = 0; + int sync_codes = 0; + int consecutive_codes = 0; + int offset; + + 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 + buf_size) + break; + + /* continue probing to find more sync codes */ + probe_end = FFMIN(buf + SPDIF_MAX_OFFSET, p_buf + 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 + 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) +{ + s->ctx_flags |= AVFMTCTX_NOHEADER; + return 0; +} + +int ff_spdif_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + AVIOContext *pb = s->pb; + enum IEC61937DataType data_type; + enum AVCodecID codec_id; + uint32_t state = 0; + int pkt_size_bits, offset, ret; + + while (state != (AV_BSWAP16C(SYNCWORD1) << 16 | AV_BSWAP16C(SYNCWORD2))) { + state = (state << 8) | avio_r8(pb); + if (url_feof(pb)) + return AVERROR_EOF; + } + + data_type = avio_rl16(pb); + pkt_size_bits = avio_rl16(pb); + + if (pkt_size_bits % 16) + avpriv_request_sample(s, "Packet not ending at a 16-bit boundary"); + + ret = av_new_packet(pkt, FFALIGN(pkt_size_bits, 16) >> 3); + if (ret) + return ret; + + pkt->pos = avio_tell(pb) - BURST_HEADER_SIZE; + + if (avio_read(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 */ + avio_skip(pb, offset - pkt->size - BURST_HEADER_SIZE); + + if (!s->nb_streams) { + /* first packet, create a stream */ + AVStream *st = avformat_new_stream(s, NULL); + 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) { + avpriv_report_missing_feature(s, "Codec change in IEC 61937"); + 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 ff_spdif_demuxer = { + .name = "spdif", + .long_name = NULL_IF_CONFIG_SMALL("IEC 61937 (compressed data in S/PDIF)"), + .read_probe = spdif_probe, + .read_header = spdif_read_header, + .read_packet = ff_spdif_read_packet, + .flags = AVFMT_GENERIC_INDEX, +}; |
