aboutsummaryrefslogtreecommitdiffstats
path: root/libavformat/argo_asf.c
diff options
context:
space:
mode:
authorZane van Iperen <zane@zanevaniperen.com>2020-07-27 22:53:24 +1000
committerZane van Iperen <zane@zanevaniperen.com>2020-08-07 23:14:28 +1000
commit0549daa71bfab36c06be44273759c2593b35ee8f (patch)
treeeae3abae49ed20ef7a1ab6f8898ebddfe40ea218 /libavformat/argo_asf.c
parent62da99e1d0985dcb19fc2150bee35dcb4b98267d (diff)
downloadffmpeg-0549daa71bfab36c06be44273759c2593b35ee8f.tar.gz
avformat: add argo_asf muxer
Signed-off-by: Zane van Iperen <zane@zanevaniperen.com> Reviewed-by: Andreas Rheinhardt <andreas.rheinhardt@gmail.com>
Diffstat (limited to 'libavformat/argo_asf.c')
-rw-r--r--libavformat/argo_asf.c127
1 files changed, 126 insertions, 1 deletions
diff --git a/libavformat/argo_asf.c b/libavformat/argo_asf.c
index 9de64dfab4..671b7482f9 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)
*
@@ -63,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);
@@ -254,3 +255,127 @@ 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)
+{
+ const 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);
+ avio_write(pb, fhdr->name, sizeof(fhdr->name));
+}
+
+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)
+{
+ const AVCodecParameters *par = s->streams[0]->codecpar;
+ ArgoASFFileHeader fhdr;
+ ArgoASFChunkHeader chdr;
+
+ 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 = ASF_SAMPLE_COUNT;
+ 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_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ if (pkt->size != 17 * s->streams[0]->codecpar->channels)
+ return AVERROR_INVALIDDATA;
+
+ if (s->streams[0]->nb_frames >= UINT32_MAX)
+ return AVERROR_INVALIDDATA;
+
+ avio_write(s->pb, pkt->data, pkt->size);
+ return 0;
+}
+
+static int argo_asf_write_trailer(AVFormatContext *s)
+{
+ int64_t ret;
+
+ if ((ret = avio_seek(s->pb, ASF_FILE_HEADER_SIZE, SEEK_SET) < 0))
+ return ret;
+
+ avio_wl32(s->pb, (uint32_t)s->streams[0]->nb_frames);
+ return 0;
+}
+
+AVOutputFormat ff_argo_asf_muxer = {
+ .name = "argo_asf",
+ .long_name = NULL_IF_CONFIG_SMALL("Argonaut Games ASF"),
+ /*
+ * NB: Can't do this as it conflicts with the actual ASF format.
+ * .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 = argo_asf_write_packet,
+ .write_trailer = argo_asf_write_trailer
+};
+#endif