From 27998451d86d2da50a20317d0442fa0d6e77100b Mon Sep 17 00:00:00 2001 From: Peter Ross Date: Thu, 17 Apr 2014 21:01:08 +1000 Subject: [PATCH] DSD Stream File (DSF) demuxer Signed-off-by: Peter Ross Signed-off-by: Michael Niedermayer --- doc/general.texi | 1 + libavformat/Makefile | 1 + libavformat/allformats.c | 1 + libavformat/dsfdec.c | 158 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 161 insertions(+) create mode 100644 libavformat/dsfdec.c diff --git a/doc/general.texi b/doc/general.texi index b1118b93d1..8bf0379e00 100644 --- a/doc/general.texi +++ b/doc/general.texi @@ -259,6 +259,7 @@ library: @item Deluxe Paint Animation @tab @tab X @item DFA @tab @tab X @tab This format is used in Chronomaster game +@item DSD Stream File (DSF) @tab @tab X @item DV video @tab X @tab X @item DXA @tab @tab X @tab This format is used in the non-Windows version of the Feeble Files diff --git a/libavformat/Makefile b/libavformat/Makefile index 3df5c88605..38b7f9ae36 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -118,6 +118,7 @@ OBJS-$(CONFIG_DIRAC_DEMUXER) += diracdec.o rawdec.o OBJS-$(CONFIG_DIRAC_MUXER) += rawenc.o OBJS-$(CONFIG_DNXHD_DEMUXER) += dnxhddec.o rawdec.o OBJS-$(CONFIG_DNXHD_MUXER) += rawenc.o +OBJS-$(CONFIG_DSF_DEMUXER) += dsfdec.o OBJS-$(CONFIG_DSICIN_DEMUXER) += dsicin.o OBJS-$(CONFIG_DTSHD_DEMUXER) += dtshddec.o OBJS-$(CONFIG_DTS_DEMUXER) += dtsdec.o rawdec.o diff --git a/libavformat/allformats.c b/libavformat/allformats.c index b5cbbef9d2..f6d78aed67 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -106,6 +106,7 @@ void av_register_all(void) REGISTER_DEMUXER (DFA, dfa); REGISTER_MUXDEMUX(DIRAC, dirac); REGISTER_MUXDEMUX(DNXHD, dnxhd); + REGISTER_DEMUXER (DSF, dsf); REGISTER_DEMUXER (DSICIN, dsicin); REGISTER_MUXDEMUX(DTS, dts); REGISTER_DEMUXER (DTSHD, dtshd); diff --git a/libavformat/dsfdec.c b/libavformat/dsfdec.c new file mode 100644 index 0000000000..74fd7aa7d7 --- /dev/null +++ b/libavformat/dsfdec.c @@ -0,0 +1,158 @@ +/* + * DSD Stream File (DSF) demuxer + * Copyright (c) 2014 Peter Ross + * + * 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 + */ + +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "internal.h" +#include "id3v2.h" + +typedef struct { + uint64_t data_end; +} DSFContext; + +static int dsf_probe(AVProbeData *p) +{ + if (p->buf_size < 12 || memcmp(p->buf, "DSD ", 4) || AV_RL64(p->buf + 4) != 28) + return 0; + return AVPROBE_SCORE_MAX; +} + +static const uint64_t dsf_channel_layout[] = { + 0, + AV_CH_LAYOUT_MONO, + AV_CH_LAYOUT_STEREO, + AV_CH_LAYOUT_SURROUND, + AV_CH_LAYOUT_QUAD, + AV_CH_LAYOUT_4POINT0, + AV_CH_LAYOUT_5POINT0_BACK, + AV_CH_LAYOUT_5POINT1_BACK, +}; + +static void read_id3(AVFormatContext *s, uint64_t id3pos) +{ + ID3v2ExtraMeta *id3v2_extra_meta = NULL; + if (avio_seek(s->pb, id3pos, SEEK_SET) < 0) + return; + + ff_id3v2_read(s, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta); + if (id3v2_extra_meta) + ff_id3v2_parse_apic(s, &id3v2_extra_meta); + ff_id3v2_free_extra_meta(&id3v2_extra_meta); +} + +static int dsf_read_header(AVFormatContext *s) +{ + DSFContext *dsf = s->priv_data; + AVIOContext *pb = s->pb; + AVStream *st; + uint64_t id3pos; + unsigned int channel_type; + + avio_skip(pb, 4); + if (avio_rl64(pb) != 28) + return AVERROR_INVALIDDATA; + + avio_skip(pb, 8); + id3pos = avio_rl64(pb); + if (pb->seekable) { + read_id3(s, id3pos); + avio_seek(pb, 28, SEEK_SET); + } + + /* fmt chunk */ + + if (avio_rl32(pb) != MKTAG('f', 'm', 't', ' ') || avio_rl64(pb) != 52) + return AVERROR_INVALIDDATA; + + if (avio_rl32(pb) != 1) { + avpriv_request_sample(s, "unknown format version"); + return AVERROR_INVALIDDATA; + } + + if (avio_rl32(pb)) { + avpriv_request_sample(s, "unknown format id"); + return AVERROR_INVALIDDATA; + } + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + channel_type = avio_rl32(pb); + if (channel_type < FF_ARRAY_ELEMS(dsf_channel_layout)) + st->codec->channel_layout = dsf_channel_layout[channel_type]; + if (!st->codec->channel_layout) + avpriv_request_sample(s, "channel type %i", channel_type); + + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->channels = avio_rl32(pb); + st->codec->sample_rate = avio_rl32(pb) / 8; + + switch(avio_rl32(pb)) { + case 1: st->codec->codec_id = AV_CODEC_ID_DSD_LSBF_PLANAR; break; + case 8: st->codec->codec_id = AV_CODEC_ID_DSD_MSBF_PLANAR; break; + default: + avpriv_request_sample(s, "unknown most significant bit"); + return AVERROR_INVALIDDATA; + } + + avio_skip(pb, 8); + st->codec->block_align = avio_rl32(pb); + if (st->codec->block_align > INT_MAX / st->codec->channels) { + avpriv_request_sample(s, "block_align overflow"); + return AVERROR_INVALIDDATA; + } + st->codec->block_align *= st->codec->channels; + avio_skip(pb, 4); + + /* data chunk */ + + dsf->data_end = avio_tell(pb); + if (avio_rl32(pb) != MKTAG('d', 'a', 't', 'a')) + return AVERROR_INVALIDDATA; + dsf->data_end += avio_rl64(pb); + + return 0; +} + +static int dsf_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + DSFContext *dsf = s->priv_data; + AVIOContext *pb = s->pb; + AVStream *st = s->streams[0]; + int64_t pos = avio_tell(pb); + + if (pos >= dsf->data_end) + return AVERROR_EOF; + + pkt->stream_index = 0; + return av_get_packet(pb, pkt, FFMIN(dsf->data_end - pos, st->codec->block_align)); +} + +AVInputFormat ff_dsf_demuxer = { + .name = "dsf", + .long_name = NULL_IF_CONFIG_SMALL("DSD Stream File (DSF)"), + .priv_data_size = sizeof(DSFContext), + .read_probe = dsf_probe, + .read_header = dsf_read_header, + .read_packet = dsf_read_packet, + .flags = AVFMT_GENERIC_INDEX | AVFMT_NO_BYTE_SEEK, +};