From patchwork Sun Jan 19 03:11:34 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Zane van Iperen X-Patchwork-Id: 17414 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 2A7F244B565 for ; Sun, 19 Jan 2020 05:11:46 +0200 (EET) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 15C8E68AF44; Sun, 19 Jan 2020 05:11:46 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-40131.protonmail.ch (mail-40131.protonmail.ch [185.70.40.131]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 6B90B68AF90 for ; Sun, 19 Jan 2020 05:11:40 +0200 (EET) Date: Sun, 19 Jan 2020 03:11:34 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=zanevaniperen.com; s=protonmail; t=1579403496; bh=1quN1Iq61oSbA1ld8HMtaOK3XRdgB9CDwmAIffNxe+Q=; h=Date:To:From:Cc:Reply-To:Subject:In-Reply-To:References: Feedback-ID:From; b=wFU5sYwqoGTi/g05ZED9ovGXOrVncqmU6WDMeKgVcQO+/JcPdBQnh8hFMnR0hFFJi /l4b3ibfNGh4EOJcr/aqbKwH2OTS0lWofWIG9LNyzLM/nJ1TPs6LZmpaFTRMOBuxrd 8ercmcS/2A20MiKXGQS/aN/M0CwwX/SNBEHg0VKQ= To: ffmpeg-devel@ffmpeg.org From: Zane van Iperen Message-ID: <20200119031111.26647-3-zane@zanevaniperen.com> In-Reply-To: <20200119031111.26647-1-zane@zanevaniperen.com> References: <20200119031111.26647-1-zane@zanevaniperen.com> Feedback-ID: xylLYHwBzJW7F28tHN-oL9EBm6h5yqtCqG6YwPpJ2oMwBYJT6-HxTnFTF6p18axLiSywX61iUfj4CElBGg8-GA==:Ext:ProtonMail MIME-Version: 1.0 X-Spam-Status: No, score=-1.2 required=7.0 tests=ALL_TRUSTED,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF shortcircuit=no autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on mail.protonmail.ch Subject: [FFmpeg-devel] [PATCH 2/2] avformat: add demuxer for argonaut games' ASF format X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Zane van Iperen Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Adds support for the custom ASF container used by some Argonaut Games' games, such as 'Croc! Legend of the Gobbos', and 'Croc 2'. Can also handle the sample files in: https://samples.ffmpeg.org/game-formats/brender/part2.zip Signed-off-by: Zane van Iperen --- Changelog | 1 + libavformat/Makefile | 1 + libavformat/allformats.c | 1 + libavformat/argo_asf.c | 254 +++++++++++++++++++++++++++++++++++++++ libavformat/version.h | 4 +- 5 files changed, 259 insertions(+), 2 deletions(-) create mode 100644 libavformat/argo_asf.c diff --git a/Changelog b/Changelog index e26320c0ce..bc1593bfd1 100644 --- a/Changelog +++ b/Changelog @@ -31,6 +31,7 @@ version : - thistogram filter - freezeframes filter - Argonaut Games ADPCM decoder +- Argonaut Games ASF demuxer version 4.2: - tpad filter diff --git a/libavformat/Makefile b/libavformat/Makefile index 52f29b1a6d..ba6ea8c4a6 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -99,6 +99,7 @@ OBJS-$(CONFIG_APTX_MUXER) += rawenc.o OBJS-$(CONFIG_APTX_HD_DEMUXER) += aptxdec.o rawdec.o OBJS-$(CONFIG_APTX_HD_MUXER) += rawenc.o OBJS-$(CONFIG_AQTITLE_DEMUXER) += aqtitledec.o subtitles.o +OBJS-$(CONFIG_ARGO_ASF_DEMUXER) += argo_asf.o OBJS-$(CONFIG_ASF_DEMUXER) += asfdec_f.o asf.o asfcrypt.o \ avlanguage.o OBJS-$(CONFIG_ASF_O_DEMUXER) += asfdec_o.o asf.o asfcrypt.o \ diff --git a/libavformat/allformats.c b/libavformat/allformats.c index ff9bdb906f..fe74a32e47 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -60,6 +60,7 @@ extern AVOutputFormat ff_aptx_muxer; extern AVInputFormat ff_aptx_hd_demuxer; extern AVOutputFormat ff_aptx_hd_muxer; extern AVInputFormat ff_aqtitle_demuxer; +extern AVInputFormat ff_argo_asf_demuxer; extern AVInputFormat ff_asf_demuxer; extern AVOutputFormat ff_asf_muxer; extern AVInputFormat ff_asf_o_demuxer; diff --git a/libavformat/argo_asf.c b/libavformat/argo_asf.c new file mode 100644 index 0000000000..ab8a0955ab --- /dev/null +++ b/libavformat/argo_asf.c @@ -0,0 +1,254 @@ +/* + * Argonaut Games ASF demuxer + * + * Copyright (C) 2020 Zane van Iperen (zane@zanevaniperen.com) + * + * 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 "avformat.h" +#include "internal.h" +#include "libavutil/internal.h" +#include "libavutil/bswap.h" +#include "libavutil/avassert.h" + +#pragma pack(push, 1) +typedef struct ArgoASFFileHeader { + uint8_t magic[4]; /*< Magic Number, {'A', 'S', 'F', '\0'} */ + uint16_t version_major; /*< File Major Version. */ + uint16_t version_minor; /*< File Minor Version. */ + uint32_t num_chunks; /*< No. chunks in the file. */ + uint32_t chunk_offset; /*< Offset to the first chunk from the start of the file. */ + int8_t name[8]; /*< Name. */ +} ArgoASFFileHeader; + +typedef struct ArgoASFChunkHeader { + uint32_t num_blocks; /*< No. blocks in the chunk. */ + uint32_t num_samples; /*< No. samples per channel in a block. */ + uint32_t unk1; /*< Unknown */ + uint16_t sample_rate; /*< Sample rate. */ + uint16_t unk2; /*< Unknown. */ + uint32_t flags; /*< Stream flags. */ +} ArgoASFChunkHeader; +#pragma pack(pop) + +enum { + ASF_CF_BITS_PER_SAMPLE = (1 << 0), /*< 16-bit if set, 8 otherwise. */ + ASF_CF_STEREO = (1 << 1), /*< Stereo if set, mono otherwise. */ + ASF_CF_ALWAYS1_1 = (1 << 2), /*< Unknown, always seems to be set. */ + ASF_CF_ALWAYS1_2 = (1 << 3), /*< Unknown, always seems to be set. */ + + ASF_CF_ALWAYS1 = ASF_CF_ALWAYS1_1 | ASF_CF_ALWAYS1_2, + ASF_CF_ALWAYS0 = ~(ASF_CF_BITS_PER_SAMPLE | ASF_CF_STEREO | ASF_CF_ALWAYS1) +}; + +typedef struct ArgoASFDemuxContext +{ + ArgoASFFileHeader fhdr; + ArgoASFChunkHeader ckhdr; + uint32_t blocks_read; +} ArgoASFDemuxContext; + +static void argo_asf_fix_header_endianness(ArgoASFFileHeader *hdr) +{ + hdr->version_major = av_le2ne16(hdr->version_major); + hdr->version_minor = av_le2ne16(hdr->version_minor); + hdr->num_chunks = av_le2ne32(hdr->num_chunks); + hdr->chunk_offset = av_le2ne32(hdr->chunk_offset); +} + +static void argo_asf_fix_chunk_endianness(ArgoASFChunkHeader *ck) +{ + ck->num_blocks = av_le2ne32(ck->num_blocks); + ck->num_samples = av_le2ne32(ck->num_samples); + ck->unk1 = av_le2ne32(ck->unk1); + ck->sample_rate = av_le2ne16(ck->sample_rate); + ck->unk2 = av_le2ne32(ck->unk2); + ck->flags = av_le2ne32(ck->flags); +} + +/* + * Known versions: + * 1.1: The sample files in /game-formats/brender/part2.zip + * 1.2: Croc! Legend of the Gobbos + * 2.1: Croc 2 + */ +static int argo_asf_is_known_version(const ArgoASFFileHeader *hdr) +{ + return (hdr->version_major == 1 && hdr->version_minor == 1) || + (hdr->version_major == 1 && hdr->version_minor == 2) || + (hdr->version_major == 2 && hdr->version_minor == 1); +} + +static int argo_asf_probe(const AVProbeData *p) +{ + int score; + ArgoASFFileHeader hdr; + + av_assert0(sizeof(ArgoASFFileHeader) == 24); + av_assert0(sizeof(ArgoASFChunkHeader) == 20); + + if (p->buf_size < sizeof(ArgoASFFileHeader)) + return AVPROBE_SCORE_RETRY; + + if (p->buf[0] != 'A' || p->buf[1] != 'S' || + p->buf[2] != 'F' || p->buf[3] != '\0') + return 0; + + score = 0; + if (av_match_ext(p->filename, "asf")) + score += AVPROBE_SCORE_EXTENSION; + + hdr = *(ArgoASFFileHeader*)p->buf; + argo_asf_fix_header_endianness(&hdr); + + if (argo_asf_is_known_version(&hdr)) + score += 25; + + /* Have only ever seen these with 1 chunk. */ + if (hdr.num_chunks == 1) + score += 25; + + if (score > AVPROBE_SCORE_MAX) + score = AVPROBE_SCORE_MAX; + + return score; +} + +static int argo_asf_read_header(AVFormatContext *s) +{ + int ret; + AVIOContext *pb = s->pb; + AVStream *st; + ArgoASFDemuxContext *asf = s->priv_data; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + if (avio_read(pb, (unsigned char*)&asf->fhdr, sizeof(asf->fhdr)) != sizeof(asf->fhdr)) + return AVERROR(EIO); + + argo_asf_fix_header_endianness(&asf->fhdr); + + if (!argo_asf_is_known_version(&asf->fhdr)) { + avpriv_request_sample(s, "Version %hu.%hu", + asf->fhdr.version_major, asf->fhdr.version_minor + ); + return AVERROR_PATCHWELCOME; + } + + if ((ret = avio_seek(pb, asf->fhdr.chunk_offset, SEEK_SET)) < 0) + return ret; + + argo_asf_fix_chunk_endianness(&asf->ckhdr); + + if (avio_read(pb, (unsigned char*)&asf->ckhdr, sizeof(asf->ckhdr)) != sizeof(asf->ckhdr)) + return AVERROR(EIO); + + if ((asf->ckhdr.flags & ASF_CF_ALWAYS1) != ASF_CF_ALWAYS1 || (asf->ckhdr.flags & ASF_CF_ALWAYS0) != 0) { + avpriv_request_sample(s, "Nonstandard flags (0x%08X)", asf->ckhdr.flags); + return AVERROR_PATCHWELCOME; + } + + st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; + st->codecpar->codec_id = AV_CODEC_ID_ADPCM_ARGO; + st->codecpar->format = AV_SAMPLE_FMT_S16; + + if (asf->ckhdr.flags & ASF_CF_STEREO) { + st->codecpar->channel_layout = AV_CH_LAYOUT_STEREO; + st->codecpar->channels = 2; + } else { + st->codecpar->channel_layout = AV_CH_LAYOUT_MONO; + st->codecpar->channels = 1; + } + + st->codecpar->sample_rate = asf->ckhdr.sample_rate; + + st->codecpar->bits_per_coded_sample = 4; + + if (asf->ckhdr.flags & ASF_CF_BITS_PER_SAMPLE) + st->codecpar->bits_per_raw_sample = 16; + else + st->codecpar->bits_per_raw_sample = 8; + + if (st->codecpar->bits_per_raw_sample != 16) { + /* The header allows for these, but I've never seen any files with them. */ + avpriv_request_sample(s, "Non 16-bit samples"); + return AVERROR_PATCHWELCOME; + } + + /* + * (nchannel control bytes) + ((bytes_per_channel) * nchannel) + * For mono, this is 17. For stereo, this is 34. + */ + st->codecpar->frame_size = st->codecpar->channels + + (asf->ckhdr.num_samples / 2) * + st->codecpar->channels; + + st->codecpar->block_align = st->codecpar->frame_size; + + st->codecpar->bit_rate = st->codecpar->channels * + st->codecpar->sample_rate * + st->codecpar->bits_per_coded_sample; + + avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate); + st->start_time = 0; + st->duration = asf->ckhdr.num_blocks * asf->ckhdr.num_samples; + st->nb_frames = asf->ckhdr.num_blocks; + + asf->blocks_read = 0; + return 0; +} + +static int argo_asf_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + ArgoASFDemuxContext *asf = s->priv_data; + + AVStream *st = s->streams[0]; + AVIOContext *pb = s->pb; + int ret; + + if (asf->blocks_read >= asf->ckhdr.num_blocks) + return AVERROR_EOF; + + ret = av_get_packet(pb, pkt, st->codecpar->frame_size); + if (ret < 0) + return ret; + + if (ret != st->codecpar->frame_size) + return AVERROR_INVALIDDATA; + + pkt->stream_index = st->index; + pkt->duration = asf->ckhdr.num_samples; + + ++asf->blocks_read; + return ret; +} + +/* + * Not actually sure what ASF stands for. + * - Argonaut Sound File? + * - Audio Stream File? + */ +AVInputFormat ff_argo_asf_demuxer = { + .name = "argo_asf", + .long_name = NULL_IF_CONFIG_SMALL("Argonaut Games ASF"), + .priv_data_size = sizeof(ArgoASFDemuxContext), + .read_probe = argo_asf_probe, + .read_header = argo_asf_read_header, + .read_packet = argo_asf_read_packet +}; \ No newline at end of file diff --git a/libavformat/version.h b/libavformat/version.h index 0a79868663..f72fb9478a 100644 --- a/libavformat/version.h +++ b/libavformat/version.h @@ -32,8 +32,8 @@ // Major bumping may affect Ticket5467, 5421, 5451(compatibility with Chromium) // Also please add any ticket numbers that you believe might be affected here #define LIBAVFORMAT_VERSION_MAJOR 58 -#define LIBAVFORMAT_VERSION_MINOR 35 -#define LIBAVFORMAT_VERSION_MICRO 103 +#define LIBAVFORMAT_VERSION_MINOR 36 +#define LIBAVFORMAT_VERSION_MICRO 100 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ LIBAVFORMAT_VERSION_MINOR, \