Message ID | 20200802102031.1149937-4-zane@zanevaniperen.com |
---|---|
State | Superseded |
Headers | show |
Series | [FFmpeg-devel,1/6] avcodec/adpcm_argo: fix incorrect documentation | expand |
Context | Check | Description |
---|---|---|
andriy/default | pending | |
andriy/make | success | Make finished |
andriy/make_fate | fail | Make fate failed |
Zane van Iperen: > Signed-off-by: Zane van Iperen <zane@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@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, \ >
On Sun, 2 Aug 2020 13:28:10 +0200 > > + for (int i = 0; i < FF_ARRAY_ELEMS(fhdr->name); i++) > > + avio_w8(pb, fhdr->name[i]); > > Are you aware of avio_write()? ...yes. I basically copied the reading code, which used AV_RL8. I've changed it. > > +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). I mean, technically yes, but it seems neater to fill out the structures and then write them. I'd prefer to keep them personally. If efficiency is paramount, I could AV_WLXX() to a buffer and avio_write() that instead? I can remove the initialisations though, that's no problem. > > +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. > You're right, num_samples should always be 32. I will add a check in the demuxer. The 17 comes from "control byte + (32 / 2)" (the frame size for a single channel), so adding that check should guarantee the assertion. > > + > > + 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? > Because... I forgot to. Is fixed.
Zane van Iperen: >>> +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. >> > > You're right, num_samples should always be 32. I will add a check in > the demuxer. > > The 17 comes from "control byte + (32 / 2)" (the frame size for a > single channel), so adding that check should guarantee the assertion. > Why? The packets sent to this muxer need not originate from the demuxer via remuxing or from the encoder that you are about to add. - Andreas
On Sun, 2 Aug 2020 14:14:45 +0200 "Andreas Rheinhardt" <andreas.rheinhardt@gmail.com> wrote: > > Zane van Iperen: > >>> +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. > >> > > > > You're right, num_samples should always be 32. I will add a check in > > the demuxer. > > > > The 17 comes from "control byte + (32 / 2)" (the frame size for a > > single channel), so adding that check should guarantee the assertion. > > > > Why? The packets sent to this muxer need not originate from the demuxer > via remuxing or from the encoder that you are about to add. So, something like this basically: static int argo_asf_write_packet(AVFormatContext *s, AVPacket *pkt) { if (pkt->size != 17 && pkt->size != 34) return AVERROR(EINVAL); avio_write(s->pb, pkt->data, pkt->size); return 0; }
Zane van Iperen: > On Sun, 2 Aug 2020 14:14:45 +0200 > "Andreas Rheinhardt" <andreas.rheinhardt@gmail.com> wrote: > >> >> Zane van Iperen: >>>>> +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. >>>> >>> >>> You're right, num_samples should always be 32. I will add a check in >>> the demuxer. >>> >>> The 17 comes from "control byte + (32 / 2)" (the frame size for a >>> single channel), so adding that check should guarantee the assertion. >>> >> >> Why? The packets sent to this muxer need not originate from the demuxer >> via remuxing or from the encoder that you are about to add. > > So, something like this basically: > > static int argo_asf_write_packet(AVFormatContext *s, AVPacket *pkt) > { > if (pkt->size != 17 && pkt->size != 34) > return AVERROR(EINVAL); > > avio_write(s->pb, pkt->data, pkt->size); > return 0; > } > I think it is rather AVERROR_INVALIDDATA. But even then there are still two problems: You allow 17 byte packets in case of stereo, yet in this case you still assert that the data size is divisible by 34. And more serious: Currently, the AVIOContext's position is incremented even if a write error happened or if writing is not even attempted because of an earlier write error, yet there is no guarantee that it always stays that way. For the record, AVStream.nb_frames contains the number of frames successfully written. You could simply use that and ignore any divisibility. Or you could check the sizes in a custom write_packet function and still just use AVStream.nb_frames when writing the trailer. - Andreas
On Sun, 2 Aug 2020 14:43:20 +0200 "Andreas Rheinhardt" <andreas.rheinhardt@gmail.com> wrote: > > So, something like this basically: > > > > static int argo_asf_write_packet(AVFormatContext *s, AVPacket *pkt) > > { > > if (pkt->size != 17 && pkt->size != 34) > > return AVERROR(EINVAL); > > > > avio_write(s->pb, pkt->data, pkt->size); > > return 0; > > } > > > I think it is rather AVERROR_INVALIDDATA. But even then there are still > two problems: You allow 17 byte packets in case of stereo, yet in this > case you still assert that the data size is divisible by 34. And more > serious: Currently, the AVIOContext's position is incremented even if a > write error happened or if writing is not even attempted because of an > earlier write error, yet there is no guarantee that it always stays that > way. Yeah, it's late here, I shouldn't be doing this when tired. That check should have been: if (pkt->size != 17 * s->streams[0]->codecpar->channels) return AVERROR_INVALIDDATA; > > For the record, AVStream.nb_frames contains the number of frames > successfully written. You could simply use that and ignore any > divisibility. Or you could check the sizes in a custom write_packet > function and still just use AVStream.nb_frames when writing the trailer. > I didn't know about AVStream.nb_frames, that makes things much nicer. I've removed the assert and changed it to do what you suggested. Zane
On Sun, Aug 02, 2020 at 10:21:12AM +0000, Zane van Iperen wrote: > Signed-off-by: Zane van Iperen <zane@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(-) > I see that argo decoder does not use block_align at all. That is very harmful and have big decoding CPU overhead. There is no point to decode from very small packets... Instead make use of block_align and use bigger packets.
On Fri, 11 Sep 2020 13:45:58 +0200 "Paul B Mahol" <onemda@gmail.com> wrote: > > On Sun, Aug 02, 2020 at 10:21:12AM +0000, Zane van Iperen wrote: > > Signed-off-by: Zane van Iperen <zane@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(-) > > > > I see that argo decoder does not use block_align at all. > > That is very harmful and have big decoding CPU overhead. > > There is no point to decode from very small packets... > > Instead make use of block_align and use bigger packets. The patch you're replying to was from July, is this in regards to my ping on the BRP demuxer patch? I agree it could be changed to use bigger packets, but one thing at a time. This'll be a task once the BRP demuxer is merged.
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@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]); +} + +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 }; + + 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); + + 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); + } + + 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, \
Signed-off-by: Zane van Iperen <zane@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(-)