From 9124d807dc0d6eabed297c922cc2a4d18e08ee0a Mon Sep 17 00:00:00 2001 From: asivery Date: Fri, 8 Mar 2024 14:45:02 +0100 Subject: [PATCH] avformat/aea: add aea muxer Signed-off-by: asivery --- Changelog | 1 + doc/muxers.texi | 10 +++ libavformat/Makefile | 3 +- libavformat/{aea.c => aeadec.c} | 0 libavformat/aeaenc.c | 115 ++++++++++++++++++++++++++++++++ libavformat/allformats.c | 1 + 6 files changed, 129 insertions(+), 1 deletion(-) rename libavformat/{aea.c => aeadec.c} (100%) create mode 100644 libavformat/aeaenc.c diff --git a/Changelog b/Changelog index 069b827448..641c6b7cc5 100644 --- a/Changelog +++ b/Changelog @@ -32,6 +32,7 @@ version : - DVD-Video demuxer, powered by libdvdnav and libdvdread - ffprobe -show_stream_groups option - ffprobe (with -export_side_data film_grain) now prints film grain metadata +- AEA muxer version 6.1: diff --git a/doc/muxers.texi b/doc/muxers.texi index 7f05dfcb69..a697e4b153 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -684,6 +684,16 @@ Enable to set MPEG version bit in the ADTS frame header to 1 which indicates MPEG-2. Default is 0, which indicates MPEG-4. @end table +@anchor{aea} +@section aea +MD STUDIO audio muxer. + +This muxer accepts a single ATRAC1 audio stream with either one or two channels +and a sample rate of 44100Hz. + +As AEA supports storing the track title, this muxer will also write +the title from stream's metadata to the container. + @anchor{aiff} @section aiff Audio Interchange File Format muxer. diff --git a/libavformat/Makefile b/libavformat/Makefile index a3bfc209c3..785349c036 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -91,7 +91,8 @@ OBJS-$(CONFIG_ADTS_MUXER) += adtsenc.o apetag.o img2.o \ id3v2enc.o OBJS-$(CONFIG_ADX_DEMUXER) += adxdec.o OBJS-$(CONFIG_ADX_MUXER) += rawenc.o -OBJS-$(CONFIG_AEA_DEMUXER) += aea.o pcm.o +OBJS-$(CONFIG_AEA_DEMUXER) += aeadec.o pcm.o +OBJS-$(CONFIG_AEA_MUXER) += aeaenc.o rawenc.o OBJS-$(CONFIG_AFC_DEMUXER) += afc.o OBJS-$(CONFIG_AIFF_DEMUXER) += aiffdec.o aiff.o pcm.o \ mov_chan.o replaygain.o diff --git a/libavformat/aea.c b/libavformat/aeadec.c similarity index 100% rename from libavformat/aea.c rename to libavformat/aeadec.c diff --git a/libavformat/aeaenc.c b/libavformat/aeaenc.c new file mode 100644 index 0000000000..65d018c844 --- /dev/null +++ b/libavformat/aeaenc.c @@ -0,0 +1,115 @@ +/* + * MD STUDIO audio muxer + * + * Copyright (c) 2024 asivery + * + * 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 "avformat.h" +#include "avio_internal.h" +#include "rawenc.h" +#include "mux.h" + +static int aea_write_header(AVFormatContext *s) +{ + const AVDictionaryEntry *title_entry; + size_t title_length = 0; + AVStream *st; + + if (s->nb_streams > 1) { + av_log(s, AV_LOG_ERROR, "Got more than one stream to encode. This is not supported.\n"); + return AVERROR(EINVAL); + } + + st = s->streams[0]; + if (st->codecpar->ch_layout.nb_channels != 1 && st->codecpar->ch_layout.nb_channels != 2) { + av_log(s, AV_LOG_ERROR, "Only maximum 2 channels are supported in the audio" + " stream, %d channels were found.\n", st->codecpar->ch_layout.nb_channels); + return AVERROR(EINVAL); + } + + if (st->codecpar->codec_id != AV_CODEC_ID_ATRAC1) { + av_log(s, AV_LOG_ERROR, "AEA can only store ATRAC1 streams, %s was found.\n", avcodec_get_name(st->codecpar->codec_id)); + return AVERROR(EINVAL); + } + + if (st->codecpar->sample_rate != 44100) { + av_log(s, AV_LOG_ERROR, "Invalid sample rate (%d) AEA only supports 44.1kHz.\n", st->codecpar->sample_rate); + return AVERROR(EINVAL); + } + + /* Write magic */ + avio_wl32(s->pb, 2048); + + /* Write AEA title */ + title_entry = av_dict_get(st->metadata, "title", NULL, 0); + if (title_entry) { + const char *title_contents = title_entry->value; + title_length = strlen(title_contents); + if (title_length > 256) { + av_log(s, AV_LOG_WARNING, "Title too long, truncated to 256 bytes.\n"); + title_length = 256; + } + avio_write(s->pb, title_contents, title_length); + } + + ffio_fill(s->pb, 0, 256 - title_length); + + /* Write number of frames (zero at header-writing time, will seek later), number of channels */ + avio_wl32(s->pb, 0); + avio_w8(s->pb, st->codecpar->ch_layout.nb_channels); + avio_w8(s->pb, 0); + + /* Pad the header to 2048 bytes */ + ffio_fill(s->pb, 0, 1782); + + return 0; +} + +static int aea_write_trailer(struct AVFormatContext *s) +{ + int64_t total_blocks; + AVIOContext *pb = s->pb; + AVStream *st = s->streams[0]; + if (pb->seekable & AVIO_SEEKABLE_NORMAL) { + /* Seek to rewrite the block count. */ + avio_seek(pb, 260, SEEK_SET); + total_blocks = st->nb_frames * st->codecpar->ch_layout.nb_channels; + if (total_blocks > UINT32_MAX) { + av_log(s, AV_LOG_WARNING, "Too many frames in the file to properly encode the header (%ld)." + " Block count in the header will be truncated.\n", total_blocks); + total_blocks = UINT32_MAX; + } + avio_wl32(pb, total_blocks); + } else { + av_log(s, AV_LOG_WARNING, "Unable to rewrite AEA header.\n"); + } + + return 0; +} + +const FFOutputFormat ff_aea_muxer = { + .p.name = "aea", + .p.long_name = NULL_IF_CONFIG_SMALL("MD STUDIO audio"), + .p.extensions = "aea", + .p.audio_codec = AV_CODEC_ID_ATRAC1, + + .write_header = aea_write_header, + .write_packet = ff_raw_write_packet, + .write_trailer = aea_write_trailer, +}; diff --git a/libavformat/allformats.c b/libavformat/allformats.c index 0a0e76138f..5639715104 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -47,6 +47,7 @@ extern const FFOutputFormat ff_adts_muxer; extern const FFInputFormat ff_adx_demuxer; extern const FFOutputFormat ff_adx_muxer; extern const FFInputFormat ff_aea_demuxer; +extern const FFOutputFormat ff_aea_muxer; extern const FFInputFormat ff_afc_demuxer; extern const FFInputFormat ff_aiff_demuxer; extern const FFOutputFormat ff_aiff_muxer;