diff mbox series

[FFmpeg-devel,2/2] aeaenc: add an aea muxer

Message ID MURnoA5--3-2@lynne.ee
State New
Headers show
Series [FFmpeg-devel,1/2] aea: make demuxer probing actually work and set title info
Related show

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

Lynne Feb. 26, 2021, 5:28 a.m. UTC
This allows to (re)mux Sony ATRAC1 files.

Patch attached
Subject: [PATCH 2/2] aeaenc: add an aea muxer

This allows to (re)mux Sony ATRAC1 files.
---
 libavformat/Makefile     |   1 +
 libavformat/aeaenc.c     | 107 +++++++++++++++++++++++++++++++++++++++
 libavformat/allformats.c |   1 +
 3 files changed, 109 insertions(+)
 create mode 100644 libavformat/aeaenc.c

Comments

Andreas Rheinhardt Feb. 26, 2021, 5:48 a.m. UTC | #1
Lynne:
> This allows to (re)mux Sony ATRAC1 files.
> 
> Patch attached
> 
> +#include "libavutil/intreadwrite.h"
> +#include "avformat.h"
> +#include "rawenc.h"
> +
> +#define AT1_SU_SIZE 212
> +
> +typedef struct AEAEncContext {
> +    uint32_t frame_cnt;
> +} AEAEncContext;
> +
> +static int aea_write_header(AVFormatContext *s)
> +{
> +    AVDictionaryEntry *dtmp;
> +    AVCodecParameters *par = s->streams[0]->codecpar;
> +    AVIOContext *pb = s->pb;
> +
> +    if (par->channels != 1 && par->channels != 2) {
> +        av_log(s, AV_LOG_ERROR, "Unsupported number of channels %i!\n", par->channels);
> +        return AVERROR(EINVAL);
> +    }
> +
> +    if (par->sample_rate != 44100) {
> +        av_log(s, AV_LOG_ERROR, "Unsupported samplerate %i!\n", par->sample_rate);
> +        return AVERROR(EINVAL);
> +    }
> +
> +    if (par->block_align != (AT1_SU_SIZE * par->channels)) {
> +        av_log(s, AV_LOG_ERROR, "Unsupported block align %i!\n", par->block_align);
> +        return AVERROR(EINVAL);
> +    }
> +
> +    avio_wl32(pb, 0x800);       // magic
> +
> +    dtmp = av_dict_get(s->metadata, "title", NULL, 0);
> +    if (dtmp) {
> +        int len = FFMIN(strlen(dtmp->value), 15);
> +        avio_write(pb, dtmp->value, len);
> +        for (int i = 0; i < (16 - len); i++)
> +            avio_w8(pb, 0);
> +    } else {
> +        avio_wl64(pb, 0);
> +        avio_wl64(pb, 0);
> +    }

How about
int len = 0;
...
if (dtmp) {
    len = FFMIN
    avio_write(pb, drmp->value, len);
}
ffio_fill(pb, 0, 16 - len);

> +
> +    avio_skip(pb, 240);           // not needed
> +

skip? For a muxer? There is a danger of producing nondeterministic
output here. Better add these 240 bytes to the ffio_fill I just suggested.

> +    avio_wl32(pb,   0);           // #frames field overwritten when closing

This could also be done by ffio_fill.

> +    avio_w8  (pb, par->channels); // Number of channels
> +
> +    /* Skip to 2048 */
> +    avio_skip(pb, 2048 - avio_tell(pb));

Same problem as above. Best to just use ffio_fill(pb, 0, 2048 - 265).

> +
> +    return 0;
> +}
> +
> +static int aea_write_packet(AVFormatContext *s, AVPacket *pkt)
> +{
> +    AEAEncContext *ctx = s->priv_data;
> +    ctx->frame_cnt++;

Unnecessary: The muxing code generically sets AVStream.nb_frames. You
can just use ff_raw_write_packet as your write_packet function and can
remove the whole context.

> +    return ff_raw_write_packet(s, pkt);
> +}
> +
> +static int aea_write_trailer(AVFormatContext *s)
> +{
> +    AVIOContext *pb = s->pb;
> +    AEAEncContext *ctx = s->priv_data;
> +
> +    if ((pb->seekable & AVIO_SEEKABLE_NORMAL) && ctx->frame_cnt > 1) {
> +        int64_t end = avio_tell(pb);
> +
> +        avio_seek(pb, 260, SEEK_SET);
> +        avio_wl32(pb, ctx->frame_cnt);
> +        avio_seek(pb, end, SEEK_SET);
> +    }
> +
> +    return 0;
> +}
> +
> +AVOutputFormat ff_aea_muxer = {
> +    .name              = "aea",
> +    .long_name         = NULL_IF_CONFIG_SMALL("MD STUDIO audio"),
> +    .priv_data_size    = sizeof(AEAEncContext),
> +    .extensions        = "aea",
> +    .audio_codec       = AV_CODEC_ID_ATRAC1,
> +    .write_header      = aea_write_header,
> +    .write_packet      = aea_write_packet,
> +    .write_trailer     = aea_write_trailer,
> +    .flags             = AVFMT_NOTIMESTAMPS,
> +};
diff mbox series

Patch

diff --git a/libavformat/Makefile b/libavformat/Makefile
index 10fee749c8..aced6edba5 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -80,6 +80,7 @@  OBJS-$(CONFIG_ADTS_MUXER)                += adtsenc.o apetag.o img2.o \
 OBJS-$(CONFIG_ADX_DEMUXER)               += adxdec.o
 OBJS-$(CONFIG_ADX_MUXER)                 += rawenc.o
 OBJS-$(CONFIG_AEA_DEMUXER)               += aea.o pcm.o
+OBJS-$(CONFIG_AEA_MUXER)                 += aeaenc.o rawenc.o
 OBJS-$(CONFIG_AFC_DEMUXER)               += afc.o
 OBJS-$(CONFIG_AIFF_DEMUXER)              += aiffdec.o pcm.o isom.o \
                                             mov_chan.o replaygain.o
diff --git a/libavformat/aeaenc.c b/libavformat/aeaenc.c
new file mode 100644
index 0000000000..c25adc15d4
--- /dev/null
+++ b/libavformat/aeaenc.c
@@ -0,0 +1,107 @@ 
+/*
+ * 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 "libavutil/intreadwrite.h"
+#include "avformat.h"
+#include "rawenc.h"
+
+#define AT1_SU_SIZE 212
+
+typedef struct AEAEncContext {
+    uint32_t frame_cnt;
+} AEAEncContext;
+
+static int aea_write_header(AVFormatContext *s)
+{
+    AVDictionaryEntry *dtmp;
+    AVCodecParameters *par = s->streams[0]->codecpar;
+    AVIOContext *pb = s->pb;
+
+    if (par->channels != 1 && par->channels != 2) {
+        av_log(s, AV_LOG_ERROR, "Unsupported number of channels %i!\n", par->channels);
+        return AVERROR(EINVAL);
+    }
+
+    if (par->sample_rate != 44100) {
+        av_log(s, AV_LOG_ERROR, "Unsupported samplerate %i!\n", par->sample_rate);
+        return AVERROR(EINVAL);
+    }
+
+    if (par->block_align != (AT1_SU_SIZE * par->channels)) {
+        av_log(s, AV_LOG_ERROR, "Unsupported block align %i!\n", par->block_align);
+        return AVERROR(EINVAL);
+    }
+
+    avio_wl32(pb, 0x800);       // magic
+
+    dtmp = av_dict_get(s->metadata, "title", NULL, 0);
+    if (dtmp) {
+        int len = FFMIN(strlen(dtmp->value), 15);
+        avio_write(pb, dtmp->value, len);
+        for (int i = 0; i < (16 - len); i++)
+            avio_w8(pb, 0);
+    } else {
+        avio_wl64(pb, 0);
+        avio_wl64(pb, 0);
+    }
+
+    avio_skip(pb, 240);           // not needed
+
+    avio_wl32(pb,   0);           // #frames field overwritten when closing
+    avio_w8  (pb, par->channels); // Number of channels
+
+    /* Skip to 2048 */
+    avio_skip(pb, 2048 - avio_tell(pb));
+
+    return 0;
+}
+
+static int aea_write_packet(AVFormatContext *s, AVPacket *pkt)
+{
+    AEAEncContext *ctx = s->priv_data;
+    ctx->frame_cnt++;
+    return ff_raw_write_packet(s, pkt);
+}
+
+static int aea_write_trailer(AVFormatContext *s)
+{
+    AVIOContext *pb = s->pb;
+    AEAEncContext *ctx = s->priv_data;
+
+    if ((pb->seekable & AVIO_SEEKABLE_NORMAL) && ctx->frame_cnt > 1) {
+        int64_t end = avio_tell(pb);
+
+        avio_seek(pb, 260, SEEK_SET);
+        avio_wl32(pb, ctx->frame_cnt);
+        avio_seek(pb, end, SEEK_SET);
+    }
+
+    return 0;
+}
+
+AVOutputFormat ff_aea_muxer = {
+    .name              = "aea",
+    .long_name         = NULL_IF_CONFIG_SMALL("MD STUDIO audio"),
+    .priv_data_size    = sizeof(AEAEncContext),
+    .extensions        = "aea",
+    .audio_codec       = AV_CODEC_ID_ATRAC1,
+    .write_header      = aea_write_header,
+    .write_packet      = aea_write_packet,
+    .write_trailer     = aea_write_trailer,
+    .flags             = AVFMT_NOTIMESTAMPS,
+};
diff --git a/libavformat/allformats.c b/libavformat/allformats.c
index f837ddabc8..6501a5d2f5 100644
--- a/libavformat/allformats.c
+++ b/libavformat/allformats.c
@@ -44,6 +44,7 @@  extern AVOutputFormat ff_adts_muxer;
 extern AVInputFormat  ff_adx_demuxer;
 extern AVOutputFormat ff_adx_muxer;
 extern AVInputFormat  ff_aea_demuxer;
+extern AVOutputFormat ff_aea_muxer;
 extern AVInputFormat  ff_afc_demuxer;
 extern AVInputFormat  ff_aiff_demuxer;
 extern AVOutputFormat ff_aiff_muxer;