diff mbox series

[FFmpeg-devel,4/4] avformat: add Argonaut Games CVG muxer

Message ID 20210503113157.2215-4-zane@zanevaniperen.com
State Accepted
Headers show
Series [FFmpeg-devel,1/4] avcodec/adpcm_psx: always fetch next byte irregardless of flag | expand

Checks

Context Check Description
andriy/x86_make success Make finished
andriy/x86_make_fate success Make fate finished
andriy/PPC64_make success Make finished
andriy/PPC64_make_fate success Make fate finished

Commit Message

Zane van Iperen May 3, 2021, 11:31 a.m. UTC
Signed-off-by: Zane van Iperen <zane@zanevaniperen.com>
---
 Changelog                |   1 +
 libavformat/Makefile     |   1 +
 libavformat/allformats.c |   1 +
 libavformat/argo_cvg.c   | 149 ++++++++++++++++++++++++++++++++++++++-
 libavformat/version.h    |   2 +-
 5 files changed, 152 insertions(+), 2 deletions(-)

Comments

Michael Niedermayer Feb. 14, 2022, 7:32 p.m. UTC | #1
On Mon, May 03, 2021 at 09:31:57PM +1000, Zane van Iperen wrote:
> Signed-off-by: Zane van Iperen <zane@zanevaniperen.com>
> ---
>  Changelog                |   1 +
>  libavformat/Makefile     |   1 +
>  libavformat/allformats.c |   1 +
>  libavformat/argo_cvg.c   | 149 ++++++++++++++++++++++++++++++++++++++-
>  libavformat/version.h    |   2 +-
>  5 files changed, 152 insertions(+), 2 deletions(-)
> 
> diff --git a/Changelog b/Changelog
> index 9567009d63..41b1690dfd 100644
> --- a/Changelog
> +++ b/Changelog
> @@ -5,6 +5,7 @@ version <next>:
>  - ADPCM IMA Westwood encoder
>  - Westwood AUD muxer
>  - Argonaut Games CVG demuxer
> +- Argonaut Games CVG muxer
>  
>  
>  version 4.4:
> diff --git a/libavformat/Makefile b/libavformat/Makefile
> index 0dca1ffd77..c9ef564523 100644
> --- a/libavformat/Makefile
> +++ b/libavformat/Makefile
> @@ -108,6 +108,7 @@ OBJS-$(CONFIG_ARGO_ASF_DEMUXER)          += argo_asf.o
>  OBJS-$(CONFIG_ARGO_ASF_MUXER)            += argo_asf.o
>  OBJS-$(CONFIG_ARGO_BRP_DEMUXER)          += argo_brp.o argo_asf.o
>  OBJS-$(CONFIG_ARGO_CVG_DEMUXER)          += argo_cvg.o
> +OBJS-$(CONFIG_ARGO_CVG_MUXER)            += argo_cvg.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 923af3f649..111ca3cbf0 100644
> --- a/libavformat/allformats.c
> +++ b/libavformat/allformats.c
> @@ -67,6 +67,7 @@ extern const AVInputFormat  ff_argo_asf_demuxer;
>  extern const AVOutputFormat ff_argo_asf_muxer;
>  extern const AVInputFormat  ff_argo_brp_demuxer;
>  extern const AVInputFormat  ff_argo_cvg_demuxer;
> +extern const AVOutputFormat ff_argo_cvg_muxer;
>  extern const AVInputFormat  ff_asf_demuxer;
>  extern const AVOutputFormat ff_asf_muxer;
>  extern const AVInputFormat  ff_asf_o_demuxer;
> diff --git a/libavformat/argo_cvg.c b/libavformat/argo_cvg.c
> index 2851c1649f..f0d9f3e076 100644
> --- a/libavformat/argo_cvg.c
> +++ b/libavformat/argo_cvg.c
> @@ -1,5 +1,5 @@
>  /*
> - * Argonaut Games CVG demuxer
> + * Argonaut Games CVG (de)muxer
>   *
>   * Copyright (C) 2021 Zane van Iperen (zane@zanevaniperen.com)
>   *
> @@ -21,6 +21,7 @@
>   */
>  #include "avformat.h"
>  #include "internal.h"
> +#include "libavutil/opt.h"
>  #include "libavutil/intreadwrite.h"
>  
>  /*
> @@ -53,6 +54,14 @@ typedef struct ArgoCVGDemuxContext {
>      uint32_t      blocks_read;
>  } ArgoCVGDemuxContext;
>  
> +typedef struct ArgoCVGMuxContext {
> +    const AVClass *class;
> +    int           skip_rate_check;
> +    uint32_t      checksum;
> +    size_t        size;
> +} ArgoCVGMuxContext;
> +
> +#if CONFIG_ARGO_CVG_DEMUXER
>  /* "Special" files that are played at a different rate. */
>  static ArgoCVGOverride overrides[] = {
>      { "CRYS.CVG",     { 23592, 0, 1 }, 2495499, 88200 }, /* Beta */
> @@ -243,3 +252,141 @@ const AVInputFormat ff_argo_cvg_demuxer = {
>      .read_packet    = argo_cvg_read_packet,
>      .read_seek      = argo_cvg_seek,
>  };
> +#endif
> +
> +#if CONFIG_ARGO_CVG_MUXER
> +static int argo_cvg_write_init(AVFormatContext *s)
> +{
> +    ArgoCVGMuxContext *ctx = s->priv_data;
> +    const AVCodecParameters *par;
> +
> +    if (s->nb_streams != 1) {
> +        av_log(s, AV_LOG_ERROR, "CVG files have exactly one stream\n");
> +        return AVERROR(EINVAL);
> +    }
> +
> +    par = s->streams[0]->codecpar;
> +
> +    if (par->codec_id != AV_CODEC_ID_ADPCM_PSX) {
> +        av_log(s, AV_LOG_ERROR, "%s codec not supported\n",
> +               avcodec_get_name(par->codec_id));
> +        return AVERROR(EINVAL);
> +    }
> +
> +    if (par->channels != 1) {
> +        av_log(s, AV_LOG_ERROR, "CVG files only support 1 channel\n");
> +        return AVERROR(EINVAL);
> +    }
> +
> +    if (par->block_align != ARGO_CVG_BLOCK_ALIGN)
> +        return AVERROR(EINVAL);
> +
> +    if (!ctx->skip_rate_check && par->sample_rate != 22050) {
> +        av_log(s, AV_LOG_ERROR, "Sample rate must be 22050\n");
> +        return AVERROR(EINVAL);
> +    }
> +
> +    if (!(s->pb->seekable & AVIO_SEEKABLE_NORMAL)) {
> +        av_log(s, AV_LOG_ERROR, "Stream not seekable, unable to write output file\n");
> +        return AVERROR(EINVAL);
> +    }
> +
> +    return 0;
> +}
> +
> +static int argo_cvg_write_header(AVFormatContext *s)
> +{
> +    ArgoCVGMuxContext *ctx = s->priv_data;
> +
> +    avio_wl32(s->pb, 0); /* Size, fixed later. */
> +    avio_wl32(s->pb, 0);
> +    avio_wl32(s->pb, 1);
> +
> +    ctx->checksum = 1;
> +    ctx->size     = 8;
> +    return 0;
> +}
> +
> +static int argo_cvg_write_packet(AVFormatContext *s, AVPacket *pkt)
> +{
> +    ArgoCVGMuxContext *ctx = s->priv_data;
> +    AVCodecParameters *par = s->streams[0]->codecpar;
> +
> +    if (pkt->size % par->block_align != 0)
> +        return AVERROR_INVALIDDATA;
> +
> +    avio_write(s->pb, pkt->data, pkt->size);
> +
> +    ctx->size += pkt->size;
> +
> +    if (ctx->size > UINT32_MAX)
> +        return AVERROR_INVALIDDATA;
> +
> +    for (int i = 0; i < pkt->size; i++)
> +        ctx->checksum += pkt->data[i];
> +
> +    return 0;
> +}
> +
> +static int argo_cvg_write_trailer(AVFormatContext *s)
> +{
> +    ArgoCVGMuxContext *ctx = s->priv_data;
> +    int64_t ret;
> +
> +    av_log(s, AV_LOG_TRACE, "size     = %zu\n", ctx->size);
> +    av_log(s, AV_LOG_TRACE, "checksum = %u\n",  ctx->checksum);
> +
> +    /*
> +     * NB: This is wrong. We're always slightly under the original.
> +     *     Verified by remuxing. For reference (orig - remuxed):

> +     *     - TCLD.CVG:     4706074 - 4705696 = 378
> +     *     - DANLOOP1.CVG: 5684641 - 5684212 = 429
> +     *     - CRYS.CVG:     2495499 - 2495367 = 132
> +     *     - PICKUP88.CVG: 1348091 - 1347937 = 154
> +     *     - SELECT1.CVG:   549987 - 549752  = 235

Are these files available somewhere ?

thx

[...]
Zane van Iperen Feb. 15, 2022, 12:09 a.m. UTC | #2
On 15/2/22 05:32, Michael Niedermayer wrote:

>> +static int argo_cvg_write_trailer(AVFormatContext *s)
>> +{
>> +    ArgoCVGMuxContext *ctx = s->priv_data;
>> +    int64_t ret;
>> +
>> +    av_log(s, AV_LOG_TRACE, "size     = %zu\n", ctx->size);
>> +    av_log(s, AV_LOG_TRACE, "checksum = %u\n",  ctx->checksum);
>> +
>> +    /*
>> +     * NB: This is wrong. We're always slightly under the original.
>> +     *     Verified by remuxing. For reference (orig - remuxed):
> 
>> +     *     - TCLD.CVG:     4706074 - 4705696 = 378
>> +     *     - DANLOOP1.CVG: 5684641 - 5684212 = 429
>> +     *     - CRYS.CVG:     2495499 - 2495367 = 132
>> +     *     - PICKUP88.CVG: 1348091 - 1347937 = 154
>> +     *     - SELECT1.CVG:   549987 - 549752  = 235
> 
> Are these files available somewhere ?
> 

Yup, here you go: https://0x0.st/o8NK.xz

That's a .tar.xz, the extension was stripped.

Zane
Michael Niedermayer Feb. 15, 2022, 1:06 p.m. UTC | #3
On Tue, Feb 15, 2022 at 10:09:50AM +1000, Zane van Iperen wrote:
> 
> 
> On 15/2/22 05:32, Michael Niedermayer wrote:
> 
> > > +static int argo_cvg_write_trailer(AVFormatContext *s)
> > > +{
> > > +    ArgoCVGMuxContext *ctx = s->priv_data;
> > > +    int64_t ret;
> > > +
> > > +    av_log(s, AV_LOG_TRACE, "size     = %zu\n", ctx->size);
> > > +    av_log(s, AV_LOG_TRACE, "checksum = %u\n",  ctx->checksum);
> > > +
> > > +    /*
> > > +     * NB: This is wrong. We're always slightly under the original.
> > > +     *     Verified by remuxing. For reference (orig - remuxed):
> > 
> > > +     *     - TCLD.CVG:     4706074 - 4705696 = 378
> > > +     *     - DANLOOP1.CVG: 5684641 - 5684212 = 429
> > > +     *     - CRYS.CVG:     2495499 - 2495367 = 132
> > > +     *     - PICKUP88.CVG: 1348091 - 1347937 = 154
> > > +     *     - SELECT1.CVG:   549987 - 549752  = 235
> > 
> > Are these files available somewhere ?
> > 
> 
> Yup, here you go: https://0x0.st/o8NK.xz
> 
> That's a .tar.xz, the extension was stripped.

thanks, just posted a patch which fixes the checksum

[...]
diff mbox series

Patch

diff --git a/Changelog b/Changelog
index 9567009d63..41b1690dfd 100644
--- a/Changelog
+++ b/Changelog
@@ -5,6 +5,7 @@  version <next>:
 - ADPCM IMA Westwood encoder
 - Westwood AUD muxer
 - Argonaut Games CVG demuxer
+- Argonaut Games CVG muxer
 
 
 version 4.4:
diff --git a/libavformat/Makefile b/libavformat/Makefile
index 0dca1ffd77..c9ef564523 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -108,6 +108,7 @@  OBJS-$(CONFIG_ARGO_ASF_DEMUXER)          += argo_asf.o
 OBJS-$(CONFIG_ARGO_ASF_MUXER)            += argo_asf.o
 OBJS-$(CONFIG_ARGO_BRP_DEMUXER)          += argo_brp.o argo_asf.o
 OBJS-$(CONFIG_ARGO_CVG_DEMUXER)          += argo_cvg.o
+OBJS-$(CONFIG_ARGO_CVG_MUXER)            += argo_cvg.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 923af3f649..111ca3cbf0 100644
--- a/libavformat/allformats.c
+++ b/libavformat/allformats.c
@@ -67,6 +67,7 @@  extern const AVInputFormat  ff_argo_asf_demuxer;
 extern const AVOutputFormat ff_argo_asf_muxer;
 extern const AVInputFormat  ff_argo_brp_demuxer;
 extern const AVInputFormat  ff_argo_cvg_demuxer;
+extern const AVOutputFormat ff_argo_cvg_muxer;
 extern const AVInputFormat  ff_asf_demuxer;
 extern const AVOutputFormat ff_asf_muxer;
 extern const AVInputFormat  ff_asf_o_demuxer;
diff --git a/libavformat/argo_cvg.c b/libavformat/argo_cvg.c
index 2851c1649f..f0d9f3e076 100644
--- a/libavformat/argo_cvg.c
+++ b/libavformat/argo_cvg.c
@@ -1,5 +1,5 @@ 
 /*
- * Argonaut Games CVG demuxer
+ * Argonaut Games CVG (de)muxer
  *
  * Copyright (C) 2021 Zane van Iperen (zane@zanevaniperen.com)
  *
@@ -21,6 +21,7 @@ 
  */
 #include "avformat.h"
 #include "internal.h"
+#include "libavutil/opt.h"
 #include "libavutil/intreadwrite.h"
 
 /*
@@ -53,6 +54,14 @@  typedef struct ArgoCVGDemuxContext {
     uint32_t      blocks_read;
 } ArgoCVGDemuxContext;
 
+typedef struct ArgoCVGMuxContext {
+    const AVClass *class;
+    int           skip_rate_check;
+    uint32_t      checksum;
+    size_t        size;
+} ArgoCVGMuxContext;
+
+#if CONFIG_ARGO_CVG_DEMUXER
 /* "Special" files that are played at a different rate. */
 static ArgoCVGOverride overrides[] = {
     { "CRYS.CVG",     { 23592, 0, 1 }, 2495499, 88200 }, /* Beta */
@@ -243,3 +252,141 @@  const AVInputFormat ff_argo_cvg_demuxer = {
     .read_packet    = argo_cvg_read_packet,
     .read_seek      = argo_cvg_seek,
 };
+#endif
+
+#if CONFIG_ARGO_CVG_MUXER
+static int argo_cvg_write_init(AVFormatContext *s)
+{
+    ArgoCVGMuxContext *ctx = s->priv_data;
+    const AVCodecParameters *par;
+
+    if (s->nb_streams != 1) {
+        av_log(s, AV_LOG_ERROR, "CVG files have exactly one stream\n");
+        return AVERROR(EINVAL);
+    }
+
+    par = s->streams[0]->codecpar;
+
+    if (par->codec_id != AV_CODEC_ID_ADPCM_PSX) {
+        av_log(s, AV_LOG_ERROR, "%s codec not supported\n",
+               avcodec_get_name(par->codec_id));
+        return AVERROR(EINVAL);
+    }
+
+    if (par->channels != 1) {
+        av_log(s, AV_LOG_ERROR, "CVG files only support 1 channel\n");
+        return AVERROR(EINVAL);
+    }
+
+    if (par->block_align != ARGO_CVG_BLOCK_ALIGN)
+        return AVERROR(EINVAL);
+
+    if (!ctx->skip_rate_check && par->sample_rate != 22050) {
+        av_log(s, AV_LOG_ERROR, "Sample rate must be 22050\n");
+        return AVERROR(EINVAL);
+    }
+
+    if (!(s->pb->seekable & AVIO_SEEKABLE_NORMAL)) {
+        av_log(s, AV_LOG_ERROR, "Stream not seekable, unable to write output file\n");
+        return AVERROR(EINVAL);
+    }
+
+    return 0;
+}
+
+static int argo_cvg_write_header(AVFormatContext *s)
+{
+    ArgoCVGMuxContext *ctx = s->priv_data;
+
+    avio_wl32(s->pb, 0); /* Size, fixed later. */
+    avio_wl32(s->pb, 0);
+    avio_wl32(s->pb, 1);
+
+    ctx->checksum = 1;
+    ctx->size     = 8;
+    return 0;
+}
+
+static int argo_cvg_write_packet(AVFormatContext *s, AVPacket *pkt)
+{
+    ArgoCVGMuxContext *ctx = s->priv_data;
+    AVCodecParameters *par = s->streams[0]->codecpar;
+
+    if (pkt->size % par->block_align != 0)
+        return AVERROR_INVALIDDATA;
+
+    avio_write(s->pb, pkt->data, pkt->size);
+
+    ctx->size += pkt->size;
+
+    if (ctx->size > UINT32_MAX)
+        return AVERROR_INVALIDDATA;
+
+    for (int i = 0; i < pkt->size; i++)
+        ctx->checksum += pkt->data[i];
+
+    return 0;
+}
+
+static int argo_cvg_write_trailer(AVFormatContext *s)
+{
+    ArgoCVGMuxContext *ctx = s->priv_data;
+    int64_t ret;
+
+    av_log(s, AV_LOG_TRACE, "size     = %zu\n", ctx->size);
+    av_log(s, AV_LOG_TRACE, "checksum = %u\n",  ctx->checksum);
+
+    /*
+     * NB: This is wrong. We're always slightly under the original.
+     *     Verified by remuxing. For reference (orig - remuxed):
+     *     - TCLD.CVG:     4706074 - 4705696 = 378
+     *     - DANLOOP1.CVG: 5684641 - 5684212 = 429
+     *     - CRYS.CVG:     2495499 - 2495367 = 132
+     *     - PICKUP88.CVG: 1348091 - 1347937 = 154
+     *     - SELECT1.CVG:   549987 - 549752  = 235
+     *     Also NB: it doesn't matter, the game doesn't check them.
+     */
+    avio_wl32(s->pb, ctx->checksum);
+
+    if ((ret = avio_seek(s->pb, 0, SEEK_SET) < 0))
+        return ret;
+
+    avio_wl32(s->pb, (uint32_t)ctx->size);
+    return 0;
+}
+
+static const AVOption argo_cvg_options[] = {
+    {
+        .name        = "skip_rate_check",
+        .help        = "skip sample rate check",
+        .offset      = offsetof(ArgoCVGMuxContext, skip_rate_check),
+        .type        = AV_OPT_TYPE_BOOL,
+        .default_val = {.i64 = 0},
+        .min         = 0,
+        .max         = 1,
+        .flags       = AV_OPT_FLAG_ENCODING_PARAM
+    },
+    { NULL }
+};
+
+static const AVClass argo_cvg_muxer_class = {
+    .class_name = "argo_cvg_muxer",
+    .item_name  = av_default_item_name,
+    .option     = argo_cvg_options,
+    .version    = LIBAVUTIL_VERSION_INT
+};
+
+const AVOutputFormat ff_argo_cvg_muxer = {
+    .name           = "argo_cvg",
+    .long_name      = NULL_IF_CONFIG_SMALL("Argonaut Games CVG"),
+    .extensions     = "cvg",
+    .audio_codec    = AV_CODEC_ID_ADPCM_PSX,
+    .video_codec    = AV_CODEC_ID_NONE,
+    .init           = argo_cvg_write_init,
+    .write_header   = argo_cvg_write_header,
+    .write_packet   = argo_cvg_write_packet,
+    .write_trailer  = argo_cvg_write_trailer,
+    .priv_class     = &argo_cvg_muxer_class,
+    .priv_data_size = sizeof(ArgoCVGMuxContext)
+};
+#endif
diff --git a/libavformat/version.h b/libavformat/version.h
index 53273334e2..cb9f5d33dc 100644
--- a/libavformat/version.h
+++ b/libavformat/version.h
@@ -32,7 +32,7 @@ 
 // 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  59
-#define LIBAVFORMAT_VERSION_MINOR   1
+#define LIBAVFORMAT_VERSION_MINOR   2
 #define LIBAVFORMAT_VERSION_MICRO 100
 
 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \