[FFmpeg-devel] [PATCH 4/6] avformat: add argo_asf muxer

Andreas Rheinhardt andreas.rheinhardt at gmail.com
Sun Aug 2 14:28:10 EEST 2020


Zane van Iperen:
> Signed-off-by: Zane van Iperen <zane at zanevaniperen.com>
> ---
>  Changelog                |   1 +
>  libavformat/Makefile     |   1 +
>  libavformat/allformats.c |   1 +
>  libavformat/argo_asf.c   | 123 ++++++++++++++++++++++++++++++++++++++-
>  libavformat/version.h    |   2 +-
>  5 files changed, 126 insertions(+), 2 deletions(-)
> 
> diff --git a/Changelog b/Changelog
> index 0f0fbf2abb..0108f8f1a8 100644
> --- a/Changelog
> +++ b/Changelog
> @@ -12,6 +12,7 @@ version <next>:
>  - AV1 encoding support SVT-AV1
>  - Cineform HD encoder
>  - ADPCM Argonaut Games encoder
> +- Argonaut Games ASF muxer
>  
>  
>  version 4.3:
> diff --git a/libavformat/Makefile b/libavformat/Makefile
> index 62d8cbb54e..cbeb99a836 100644
> --- a/libavformat/Makefile
> +++ b/libavformat/Makefile
> @@ -103,6 +103,7 @@ 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_ARGO_ASF_MUXER)            += argo_asf.o rawenc.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 fd9e46e233..b7e59ae170 100644
> --- a/libavformat/allformats.c
> +++ b/libavformat/allformats.c
> @@ -64,6 +64,7 @@ extern AVInputFormat  ff_aptx_hd_demuxer;
>  extern AVOutputFormat ff_aptx_hd_muxer;
>  extern AVInputFormat  ff_aqtitle_demuxer;
>  extern AVInputFormat  ff_argo_asf_demuxer;
> +extern AVOutputFormat ff_argo_asf_muxer;
>  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
> index 3339425244..1198176bd1 100644
> --- a/libavformat/argo_asf.c
> +++ b/libavformat/argo_asf.c
> @@ -1,5 +1,5 @@
>  /*
> - * Argonaut Games ASF demuxer
> + * Argonaut Games ASF (de)muxer
>   *
>   * Copyright (C) 2020 Zane van Iperen (zane at zanevaniperen.com)
>   *
> @@ -21,6 +21,7 @@
>   */
>  #include "avformat.h"
>  #include "internal.h"
> +#include "rawenc.h"
>  #include "libavutil/intreadwrite.h"
>  #include "libavutil/avassert.h"
>  
> @@ -62,6 +63,7 @@ typedef struct ArgoASFDemuxContext {
>      uint32_t            blocks_read;
>  } ArgoASFDemuxContext;
>  
> +#if CONFIG_ARGO_ASF_DEMUXER
>  static void argo_asf_parse_file_header(ArgoASFFileHeader *hdr, const uint8_t *buf)
>  {
>      hdr->magic          = AV_RL32(buf + 0);
> @@ -247,3 +249,122 @@ AVInputFormat ff_argo_asf_demuxer = {
>      .read_header    = argo_asf_read_header,
>      .read_packet    = argo_asf_read_packet
>  };
> +#endif
> +
> +#if CONFIG_ARGO_ASF_MUXER
> +static int argo_asf_write_init(AVFormatContext *s)
> +{
> +    AVCodecParameters *par;
> +
> +    if (s->nb_streams != 1) {
> +        av_log(s, AV_LOG_ERROR, "ASF files have exactly one stream\n");
> +        return AVERROR(EINVAL);
> +    }
> +
> +    par = s->streams[0]->codecpar;
> +
> +    if (par->codec_id != AV_CODEC_ID_ADPCM_ARGO) {
> +        av_log(s, AV_LOG_ERROR, "%s codec not supported\n",
> +               avcodec_get_name(par->codec_id));
> +        return AVERROR(EINVAL);
> +    }
> +
> +    if (par->channels > 2) {
> +        av_log(s, AV_LOG_ERROR, "ASF files only support up to 2 channels\n");
> +        return AVERROR(EINVAL);
> +    }
> +
> +    if (par->sample_rate > UINT16_MAX) {
> +        av_log(s, AV_LOG_ERROR, "Sample rate too large\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 void argo_asf_write_file_header(const ArgoASFFileHeader *fhdr, AVIOContext *pb)
> +{
> +    avio_wl32(pb, fhdr->magic);
> +    avio_wl16(pb, fhdr->version_major);
> +    avio_wl16(pb, fhdr->version_minor);
> +    avio_wl32(pb, fhdr->num_chunks);
> +    avio_wl32(pb, fhdr->chunk_offset);
> +    for (int i = 0; i < FF_ARRAY_ELEMS(fhdr->name); i++)
> +        avio_w8(pb, fhdr->name[i]);

Are you aware of avio_write()?

> +}
> +
> +static void argo_asf_write_chunk_header(const ArgoASFChunkHeader *ckhdr, AVIOContext *pb)
> +{
> +    avio_wl32(pb, ckhdr->num_blocks);
> +    avio_wl32(pb, ckhdr->num_samples);
> +    avio_wl32(pb, ckhdr->unk1);
> +    avio_wl16(pb, ckhdr->sample_rate);
> +    avio_wl16(pb, ckhdr->unk2);
> +    avio_wl32(pb, ckhdr->flags);
> +}
> +
> +static int argo_asf_write_header(AVFormatContext *s)
> +{
> +    AVCodecParameters  *par = s->streams[0]->codecpar;
> +    ArgoASFFileHeader  fhdr = { 0 };
> +    ArgoASFChunkHeader chdr = { 0 };

The initializations are completely unnecessary (and actually the
structures are, too).

> +
> +    fhdr.magic         = ASF_TAG;
> +    fhdr.version_major = 2;
> +    fhdr.version_minor = 1;
> +    fhdr.num_chunks    = 1;
> +    fhdr.chunk_offset  = ASF_FILE_HEADER_SIZE;
> +    strncpy(fhdr.name, av_basename(s->url), FF_ARRAY_ELEMS(fhdr.name));
> +
> +    chdr.num_blocks    = 0;
> +    chdr.num_samples   = 32;
> +    chdr.unk1          = 0;
> +    chdr.sample_rate   = par->sample_rate;
> +    chdr.unk2          = ~0;
> +    chdr.flags         = ASF_CF_BITS_PER_SAMPLE | ASF_CF_ALWAYS1;
> +
> +    if (par->channels == 2)
> +        chdr.flags |= ASF_CF_STEREO;
> +
> +    argo_asf_write_file_header(&fhdr, s->pb);
> +    argo_asf_write_chunk_header(&chdr, s->pb);
> +    return 0;
> +}
> +
> +static int argo_asf_write_trailer(AVFormatContext *s)
> +{
> +    int64_t data_size, num_blocks;
> +
> +    data_size = avio_tell(s->pb) - (ASF_FILE_HEADER_SIZE + ASF_CHUNK_HEADER_SIZE);
> +    num_blocks = data_size / (17 * s->streams[0]->codecpar->channels);
> +
> +    av_assert0(data_size % (17 * s->streams[0]->codecpar->channels) == 0);

You must not assert this as you have not have any check in place that
guarantees this.

The comment in the demuxer and the value you assign to num_samples when
writing the header indicates to me that num_samples is supposed to be
always 32; yet there is no check for that in the demuxer and if it is a
value different from 32 and 33, then simply remuxing such a file can
trigger this assert.

> +
> +    if (num_blocks > UINT32_MAX) {
> +        av_log(s, AV_LOG_ERROR,
> +               "Block count %"PRId64" invalid for ASF, output file will be broken\n",
> +               num_blocks);

If the output will be broken, why don't you error out?

> +    }
> +
> +    avio_seek(s->pb, ASF_FILE_HEADER_SIZE, SEEK_SET);
> +    avio_wl32(s->pb, (uint32_t)num_blocks);
> +    return 0;
> +}
> +
> +AVOutputFormat ff_argo_asf_muxer = {
> +    .name           = "argo_asf",
> +    .long_name      = NULL_IF_CONFIG_SMALL("Argonaut Games ASF"),
> +    .extensions     = "asf",
> +    .audio_codec    = AV_CODEC_ID_ADPCM_ARGO,
> +    .video_codec    = AV_CODEC_ID_NONE,
> +    .init           = argo_asf_write_init,
> +    .write_header   = argo_asf_write_header,
> +    .write_packet   = ff_raw_write_packet,
> +    .write_trailer  = argo_asf_write_trailer
> +};
> +#endif
> \ No newline at end of file
> diff --git a/libavformat/version.h b/libavformat/version.h
> index 33cebed85e..4d31e1ec3e 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  58
> -#define LIBAVFORMAT_VERSION_MINOR  49
> +#define LIBAVFORMAT_VERSION_MINOR  50
>  #define LIBAVFORMAT_VERSION_MICRO 100
>  
>  #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
> 



More information about the ffmpeg-devel mailing list