PR #25414 opened by Simon Capriotti (capriots) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25414 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25414.patch
Adds a decoder and parser for ATRAC3+ streams that include an "ATS" header (Sony calls it that in official documentation, but I couldn't find any reference to what it actually means). The ATS header is at the start of each regular ATRAC3+ frame. It includes a sync word, then sample rate, channel config, and block_align parameters then downmix levels. Downmixing isn't implemented in this PR. ATRAC3+ ATS streams are used in PSMF movies included in PSP games and UMD-Videos, and PAMF movies included in PS3 games. Additionally, the official encoder tool outputs Wave files with the extension .atx and GUID 0d5c1350-1ba2-ec4b-91ed-6eddb83c8c66. The attached samples are the FATE samples for regular ATRAC3+ modified to include the ATS header. Please let me know if you want other specific samples/tests. <!-- If this PR requires new FATE test samples, attach them to the PR and list their target paths below (relative to the fate-suite root). Attached filenames must match the sample's filename: ```fate-samples # e.g. vorbis/new-sample.ogg ``` --> ```fate-samples atrac3p-ats/at3p_sample1.atx atrac3p-ats/at3p_sample1.pcm atrac3p-ats/sonateno14op27-2-cut.atx atrac3p-ats/sonateno14op27-2-cut.pcm ``` >From f832cb7d5cb1200b32fc06dd5ed1a09135e1aac1 Mon Sep 17 00:00:00 2001 From: Simon Capriotti <[email protected]> Date: Sat, 17 Oct 2026 14:29:38 +0200 Subject: [PATCH 1/7] avcodec: add ATRAC3+ ATS codec ID and descriptor Signed-off-by: Simon Capriotti <[email protected]> --- libavcodec/codec_desc.c | 7 +++++++ libavcodec/codec_id.h | 1 + 2 files changed, 8 insertions(+) diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c index 70d8df71f0b..80d55efbfbb 100644 --- a/libavcodec/codec_desc.c +++ b/libavcodec/codec_desc.c @@ -3603,6 +3603,13 @@ static const AVCodecDescriptor codec_descriptors[] = { .long_name = NULL_IF_CONFIG_SMALL("Apple Positional Audio Codec"), .props = AV_CODEC_PROP_LOSSY, }, + { + .id = AV_CODEC_ID_ATRAC3P_ATS, + .type = AVMEDIA_TYPE_AUDIO, + .name = "atrac3plus_ats", + .long_name = NULL_IF_CONFIG_SMALL("ATRAC3+ ATS (Adaptive TRansform Acoustic Coding 3+ ATS Syntax)"), + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, + }, /* subtitle codecs */ { diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h index a4e8e30dfd9..79b203337fd 100644 --- a/libavcodec/codec_id.h +++ b/libavcodec/codec_id.h @@ -565,6 +565,7 @@ enum AVCodecID { AV_CODEC_ID_G728, AV_CODEC_ID_AHX, AV_CODEC_ID_APPLE_APAC, + AV_CODEC_ID_ATRAC3P_ATS, /* subtitle codecs */ AV_CODEC_ID_FIRST_SUBTITLE = 0x17000, ///< A dummy ID pointing at the start of subtitle codecs. -- 2.54.0 >From 6a402e33579c8c9f8bf1488e4b068c92cb67c011 Mon Sep 17 00:00:00 2001 From: Simon Capriotti <[email protected]> Date: Sat, 17 Oct 2026 14:30:19 +0200 Subject: [PATCH 2/7] avcodec: add an ATRAC3+ ATS parser Signed-off-by: Simon Capriotti <[email protected]> --- libavcodec/Makefile | 1 + libavcodec/atrac3plus.h | 4 + libavcodec/atrac3plus_ats_parser.c | 157 +++++++++++++++++++++++++++++ libavcodec/atrac3plus_ats_parser.h | 54 ++++++++++ libavcodec/parsers.c | 1 + 5 files changed, 217 insertions(+) create mode 100644 libavcodec/atrac3plus_ats_parser.c create mode 100644 libavcodec/atrac3plus_ats_parser.h diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 2ea4f9417e6..8925c3f061c 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -1261,6 +1261,7 @@ OBJS-$(CONFIG_ADX_PARSER) += adx_parser.o OBJS-$(CONFIG_AHX_PARSER) += ahx_parser.o OBJS-$(CONFIG_AMR_PARSER) += amr_parser.o OBJS-$(CONFIG_APV_PARSER) += apv_parser.o +OBJS-$(CONFIG_ATRAC3P_ATS_PARSER) += atrac3plus_ats_parser.o OBJS-$(CONFIG_AV1_PARSER) += av1_parser.o av1_parse.o OBJS-$(CONFIG_AVS2_PARSER) += avs2.o avs2_parser.o OBJS-$(CONFIG_AVS3_PARSER) += avs3_parser.o diff --git a/libavcodec/atrac3plus.h b/libavcodec/atrac3plus.h index aa949a7e4ca..adbbce3bd87 100644 --- a/libavcodec/atrac3plus.h +++ b/libavcodec/atrac3plus.h @@ -48,6 +48,10 @@ /** Global constants */ #define ATRAC3P_POWER_COMP_OFF 15 ///< disable power compensation +/** ATS header constants */ +#define ATRAC3P_ATS_SYNC_WORD 0x0fd0 +#define ATRAC3P_ATS_HEADER_SIZE 8 + /** ATRAC3+ channel unit types */ enum Atrac3pChannelUnitTypes { CH_UNIT_MONO = 0, ///< unit containing one coded channel diff --git a/libavcodec/atrac3plus_ats_parser.c b/libavcodec/atrac3plus_ats_parser.c new file mode 100644 index 00000000000..25f8c35f1e7 --- /dev/null +++ b/libavcodec/atrac3plus_ats_parser.c @@ -0,0 +1,157 @@ +/* + * ATRAC3+ ATS parser + * Copyright (c) 2026 Simon Capriotti + * + * 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 "atrac3plus.h" +#include "atrac3plus_ats_parser.h" +#include "codec_id.h" +#include "get_bits.h" +#include "parser.h" +#include "parser_internal.h" + +int ff_atrac3p_ats_parse_header(const uint8_t *buf, int buf_size, AVCodecContext *avctx, + Atrac3pAtsDownmixLevels *downmix_levels) +{ + static const uint16_t ch_layouts_tab[7] = { + AV_CH_LAYOUT_MONO, + AV_CH_LAYOUT_STEREO, + AV_CH_LAYOUT_SURROUND, + AV_CH_LAYOUT_4POINT0, + AV_CH_LAYOUT_5POINT1_BACK, + AV_CH_LAYOUT_6POINT1_BACK, + AV_CH_LAYOUT_7POINT1 + }; + static const uint16_t sample_rate_tab[3] = { 32000, 44100, 48000 }; + + GetBitContext gb; + + if (buf_size < ATRAC3P_ATS_HEADER_SIZE) { + av_log(avctx, AV_LOG_ERROR, "buf_size is too small\n"); + return AVERROR_INVALIDDATA; + } + + const int ret = init_get_bits8(&gb, buf, buf_size); + av_assert0(ret == 0); + + const uint16_t sync_word = get_bits(&gb, 16); + if (sync_word != ATRAC3P_ATS_SYNC_WORD) { + av_log(avctx, AV_LOG_ERROR, "Invalid sync word in the ATS header (%04X)\n", + sync_word); + return AVERROR_INVALIDDATA; + } + + const uint8_t sample_rate_idc = get_bits(&gb, 3); + if (sample_rate_idc >= FF_ARRAY_ELEMS(sample_rate_tab)) { + av_log(avctx, AV_LOG_ERROR, "Invalid sample rate indicator in the ATS header (%d)\n", + sample_rate_idc); + return AVERROR_INVALIDDATA; + } + + const uint16_t sample_rate = sample_rate_tab[sample_rate_idc]; + + const uint8_t channel_config_idc = get_bits(&gb, 3); + if (channel_config_idc - 1 >= FF_ARRAY_ELEMS(ch_layouts_tab)) { + av_log(avctx, AV_LOG_ERROR, "Invalid channel configuration indicator in the ATS header (%d)\n", + channel_config_idc); + return AVERROR_INVALIDDATA; + } + + const uint16_t ch_layout_mask = ch_layouts_tab[channel_config_idc - 1]; + + const uint16_t block_align = (get_bits(&gb, 10) + 1) * 8 + ATRAC3P_ATS_HEADER_SIZE; + + if (downmix_levels) { + downmix_levels->front_downmix_level = get_bits(&gb, 3); + downmix_levels->center_downmix_level = get_bits(&gb, 3); + downmix_levels->back_downmix_level = get_bits(&gb, 3); + downmix_levels->lfe_downmix_level = get_bits(&gb, 3); + downmix_levels->side_downmix_level = get_bits(&gb, 3); + } + + avctx->sample_rate = sample_rate; + av_channel_layout_uninit(&avctx->ch_layout); + av_channel_layout_from_mask(&avctx->ch_layout, ch_layout_mask); + avctx->block_align = block_align; + avctx->frame_size = ATRAC3P_FRAME_SAMPLES; + avctx->bit_rate = block_align * CHAR_BIT * sample_rate / ATRAC3P_FRAME_SAMPLES; + return 0; +} + +typedef struct Atrac3pAtsParseContext { + ParseContext pc; + int remaining_size; +} Atrac3pAtsParseContext; + +static int parse(AVCodecParserContext *s1, AVCodecContext *avctx, + const uint8_t **poutbuf, int *poutbuf_size, + const uint8_t *buf, int buf_size) +{ + Atrac3pAtsParseContext *const s = s1->priv_data; + int next = END_NOT_FOUND; + + s1->duration = ATRAC3P_FRAME_SAMPLES; + + if (s1->flags & PARSER_FLAG_COMPLETE_FRAMES) { + (void)ff_atrac3p_ats_parse_header(buf, buf_size, avctx, NULL); + next = buf_size; + } else { + if (!s->pc.frame_start_found) + for (int i = 0; i < buf_size; i++) { + s->pc.state64 = (s->pc.state64 << 8) | buf[i]; + if (s->pc.state64 >> 48 == ATRAC3P_ATS_SYNC_WORD) { + uint8_t tmp_buf[ATRAC3P_ATS_HEADER_SIZE + AV_INPUT_BUFFER_PADDING_SIZE]; + AV_WB64(tmp_buf, s->pc.state64); + if (ff_atrac3p_ats_parse_header(tmp_buf, ATRAC3P_ATS_HEADER_SIZE, avctx, NULL) < 0) + continue; + + s->pc.frame_start_found = 1; + s->remaining_size = avctx->block_align + i - 7; + break; + } + } + + if (s->pc.frame_start_found) + if (s->remaining_size <= buf_size) { + next = s->remaining_size; + s->remaining_size = 0; + s->pc.frame_start_found = 0; + s->pc.state64 = 0; + } else { + s->remaining_size -= buf_size; + } + + if (ff_combine_frame(&s->pc, next, &buf, &buf_size) < 0) { + *poutbuf = NULL; + *poutbuf_size = 0; + return buf_size; + } + } + + *poutbuf = buf; + *poutbuf_size = buf_size; + return next; +} + +const FFCodecParser ff_atrac3p_ats_parser = { + PARSER_CODEC_LIST(AV_CODEC_ID_ATRAC3P_ATS), + .priv_data_size = sizeof(Atrac3pAtsParseContext), + .parse = parse, + .close = ff_parse_close, +}; diff --git a/libavcodec/atrac3plus_ats_parser.h b/libavcodec/atrac3plus_ats_parser.h new file mode 100644 index 00000000000..70b51a2214a --- /dev/null +++ b/libavcodec/atrac3plus_ats_parser.h @@ -0,0 +1,54 @@ +/* + * ATRAC3plus ATS parser + * Copyright (c) 2026 Simon Capriotti + * + * 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 + */ + +#ifndef AVCODEC_ATRAC3PLUS_ATS_PARSER_H +#define AVCODEC_ATRAC3PLUS_ATS_PARSER_H + +#include <stdint.h> + +/* TODO downmix levels from 0 to 7 are the following: + -0dB, -1.5dB, -3dB, -4.5dB, -6dB, -7.5dB, -9dB, -infinity dB */ +typedef struct Atrac3pAtsDownmixLevels { + uint8_t front_downmix_level : 3; + uint8_t center_downmix_level : 3; + uint8_t back_downmix_level : 3; + uint8_t lfe_downmix_level : 3; + uint8_t side_downmix_level : 3; +} Atrac3pAtsDownmixLevels; + +/** + * Parses the ATS header and updates the codec context with the values from the + * header. + * + * Optionally retrieves downmix levels from the header. + * + * @param buf Input buffer containing the header + * @param buf_size Input buffer size + * @param avctx Codec context to update + * @param downmix_levels Will contain the downmix levels after parsing. + * Can be NULL + * @retval 0 Success + * @retval AVERROR_INVALIDDATA The header contained an invalid value + */ +int ff_atrac3p_ats_parse_header(const uint8_t *buf, int buf_size, AVCodecContext *avctx, + Atrac3pAtsDownmixLevels *downmix_levels); + +#endif /* AVCODEC_ATRAC3PLUS_ATS_PARSER_H */ diff --git a/libavcodec/parsers.c b/libavcodec/parsers.c index 263b606407e..8b107773fce 100644 --- a/libavcodec/parsers.c +++ b/libavcodec/parsers.c @@ -28,6 +28,7 @@ extern const FFCodecParser ff_adx_parser; extern const FFCodecParser ff_ahx_parser; extern const FFCodecParser ff_amr_parser; extern const FFCodecParser ff_apv_parser; +extern const FFCodecParser ff_atrac3p_ats_parser; extern const FFCodecParser ff_av1_parser; extern const FFCodecParser ff_avs2_parser; extern const FFCodecParser ff_avs3_parser; -- 2.54.0 >From a1a89dbab130dde7b17dcaa773da58b842203b02 Mon Sep 17 00:00:00 2001 From: Simon Capriotti <[email protected]> Date: Sat, 17 Oct 2026 14:30:27 +0200 Subject: [PATCH 3/7] avcodec/atrac3plusdec: split initialization of channel units into separate function For ATRAC3+ streams with an ATS header, the channel units need to be initialized on decoding the first frame, since the number of channels isn't known until then. Signed-off-by: Simon Capriotti <[email protected]> --- libavcodec/atrac3plusdec.c | 67 +++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 26 deletions(-) diff --git a/libavcodec/atrac3plusdec.c b/libavcodec/atrac3plusdec.c index 9696a523be6..f7748e7d3cb 100644 --- a/libavcodec/atrac3plusdec.c +++ b/libavcodec/atrac3plusdec.c @@ -169,40 +169,16 @@ static av_cold void atrac3p_init_static(void) ff_atrac3p_init_dsp_static(); } -static av_cold int atrac3p_decode_init(AVCodecContext *avctx) +static av_cold int init_ch_units(ATRAC3PContext *ctx, AVCodecContext *avctx) { - static AVOnce init_static_once = AV_ONCE_INIT; - ATRAC3PContext *ctx = avctx->priv_data; - float scale; int i, ch, ret; - if (!avctx->block_align) { - av_log(avctx, AV_LOG_ERROR, "block_align is not set\n"); - return AVERROR(EINVAL); - } - - /* initialize IPQF */ - scale = 32.0 / 32768.0; - ret = av_tx_init(&ctx->ipqf_dct_ctx, &ctx->ipqf_dct_fn, AV_TX_FLOAT_MDCT, - 1, 16, &scale, 0); - if (ret < 0) - return ret; - - scale = -1.0f; - ret = av_tx_init(&ctx->mdct_ctx, &ctx->mdct_fn, AV_TX_FLOAT_MDCT, - 1, 128, &scale, AV_TX_FULL_IMDCT); - if (ret < 0) - return ret; - - ff_atrac_init_gain_compensation(&ctx->gainc_ctx, 6, 2); - if ((ret = set_channel_params(ctx, avctx)) < 0) return ret; ctx->ch_units = av_calloc(ctx->num_channel_blocks, sizeof(*ctx->ch_units)); - ctx->fdsp = avpriv_float_dsp_alloc(avctx->flags & AV_CODEC_FLAG_BITEXACT); - if (!ctx->ch_units || !ctx->fdsp) { + if (!ctx->ch_units) { return AVERROR(ENOMEM); } @@ -221,6 +197,45 @@ static av_cold int atrac3p_decode_init(AVCodecContext *avctx) ctx->ch_units[i].waves_info_prev = &ctx->ch_units[i].wave_synth_hist[1]; } + return 0; +} + +static av_cold int atrac3p_decode_init(AVCodecContext *avctx) +{ + static AVOnce init_static_once = AV_ONCE_INIT; + ATRAC3PContext *ctx = avctx->priv_data; + float scale; + int ret; + + if (!avctx->block_align) { + av_log(avctx, AV_LOG_ERROR, "block_align is not set\n"); + return AVERROR(EINVAL); + } + + ret = init_ch_units(ctx, avctx); + if (ret < 0) + return ret; + + /* initialize IPQF */ + scale = 32.0 / 32768.0; + ret = av_tx_init(&ctx->ipqf_dct_ctx, &ctx->ipqf_dct_fn, AV_TX_FLOAT_MDCT, + 1, 16, &scale, 0); + if (ret < 0) + return ret; + + scale = -1.0f; + ret = av_tx_init(&ctx->mdct_ctx, &ctx->mdct_fn, AV_TX_FLOAT_MDCT, + 1, 128, &scale, AV_TX_FULL_IMDCT); + if (ret < 0) + return ret; + + ff_atrac_init_gain_compensation(&ctx->gainc_ctx, 6, 2); + + ctx->fdsp = avpriv_float_dsp_alloc(avctx->flags & AV_CODEC_FLAG_BITEXACT); + + if (!ctx->fdsp) + return AVERROR(ENOMEM); + avctx->sample_fmt = AV_SAMPLE_FMT_FLTP; ff_thread_once(&init_static_once, atrac3p_init_static); -- 2.54.0 >From d7e53ed046d0cadcf4152989bf9e466d5d69d137 Mon Sep 17 00:00:00 2001 From: Simon Capriotti <[email protected]> Date: Sat, 17 Oct 2026 14:30:32 +0200 Subject: [PATCH 4/7] avcodec: add an ATRAC3+ ATS decoder Signed-off-by: Simon Capriotti <[email protected]> --- configure | 1 + libavcodec/allcodecs.c | 1 + libavcodec/atrac3plusdec.c | 88 ++++++++++++++++++++++++++++++++++---- libavcodec/utils.c | 1 + 4 files changed, 82 insertions(+), 9 deletions(-) diff --git a/configure b/configure index 083bd652c49..76d537126e9 100755 --- a/configure +++ b/configure @@ -3106,6 +3106,7 @@ asv2_encoder_select="aandcttables bswapdsp fdctdsp pixblockdsp" atrac1_decoder_select="sinewin" atrac3p_decoder_select="sinewin" atrac3pal_decoder_select="sinewin" +atrac3p_ats_decoder_select="atrac3p_ats_parser atrac3p_decoder" av1_decoder_select="cbs_av1 itut_t35" bink_decoder_select="blockdsp hpeldsp" binkaudio_dct_decoder_select="wma_freqs" diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index b12dcc405aa..dea3a985560 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -449,6 +449,7 @@ extern const FFCodec ff_atrac3_decoder; extern const FFCodec ff_atrac3al_decoder; extern const FFCodec ff_atrac3p_decoder; extern const FFCodec ff_atrac3pal_decoder; +extern const FFCodec ff_atrac3p_ats_decoder; extern const FFCodec ff_atrac9_decoder; extern const FFCodec ff_binkaudio_dct_decoder; extern const FFCodec ff_binkaudio_rdft_decoder; diff --git a/libavcodec/atrac3plusdec.c b/libavcodec/atrac3plusdec.c index f7748e7d3cb..aa0bc78bc16 100644 --- a/libavcodec/atrac3plusdec.c +++ b/libavcodec/atrac3plusdec.c @@ -2,6 +2,7 @@ * ATRAC3+ compatible decoder * * Copyright (c) 2010-2013 Maxim Poliakovski + * Copyright (c) 2026 Simon Capriotti * * This file is part of FFmpeg. * @@ -25,7 +26,9 @@ * Sony ATRAC3+ compatible decoder. * * Container formats used to store its data: - * RIFF WAV (.at3) and Sony OpenMG (.oma, .aa3). + * RIFF WAV (.at3, .atx), Sony OpenMG (.oma, .aa3), + * and MPEG-PS (PSP Movie Format (for UMD Video): .mps, + * PSP Movie Format (for games): .pmf, PlayStation Advanced Movie Format: .pam) * * Technical description of this codec can be found here: * http://wiki.multimedia.cx/index.php?title=ATRAC3plus @@ -34,6 +37,8 @@ * for their precious technical help! */ +#include "config_components.h" + #include <stdint.h> #include <string.h> @@ -207,14 +212,16 @@ static av_cold int atrac3p_decode_init(AVCodecContext *avctx) float scale; int ret; - if (!avctx->block_align) { - av_log(avctx, AV_LOG_ERROR, "block_align is not set\n"); - return AVERROR(EINVAL); - } + if (avctx->codec_id != AV_CODEC_ID_ATRAC3P_ATS) { + if (!avctx->block_align) { + av_log(avctx, AV_LOG_ERROR, "block_align is not set\n"); + return AVERROR(EINVAL); + } - ret = init_ch_units(ctx, avctx); - if (ret < 0) - return ret; + ret = init_ch_units(ctx, avctx); + if (ret < 0) + return ret; + } /* initialize IPQF */ scale = 32.0 / 32768.0; @@ -429,7 +436,9 @@ static int atrac3p_decode_frame(AVCodecContext *avctx, AVFrame *frame, *got_frame_ptr = 1; - return avctx->codec_id == AV_CODEC_ID_ATRAC3P ? FFMIN(avctx->block_align, avpkt->size) : avpkt->size; + return avctx->codec_id == AV_CODEC_ID_ATRAC3P ? FFMIN(avctx->block_align, avpkt->size) : + avctx->codec_id == AV_CODEC_ID_ATRAC3P_ATS ? FFMIN(avctx->block_align - ATRAC3P_ATS_HEADER_SIZE, avpkt->size) : + avpkt->size; } const FFCodec ff_atrac3p_decoder = { @@ -457,3 +466,64 @@ const FFCodec ff_atrac3pal_decoder = { .close = atrac3p_decode_close, FF_CODEC_DECODE_CB(atrac3p_decode_frame), }; + +#if CONFIG_ATRAC3P_ATS_DECODER + +#include "atrac3plus_ats_parser.h" + +typedef struct ATRAC3PATSContext { + ATRAC3PContext atrac3p_ctx; + uint64_t prev_header; +} ATRAC3PATSContext; + +static int atrac3p_ats_decode_frame(AVCodecContext *avctx, AVFrame *frame, + int *got_frame_ptr, AVPacket *avpkt) +{ + ATRAC3PATSContext *const ctx = avctx->priv_data; + + if (avpkt->size < ATRAC3P_ATS_HEADER_SIZE) { + av_log(avctx, AV_LOG_ERROR, "Packet too small!\n"); + return AVERROR_INVALIDDATA; + } + + const uint64_t ats_header = AV_RB64(avpkt->data); + if (ats_header != ctx->prev_header) { + Atrac3pAtsDownmixLevels downmix_levels; + + int ret = ff_atrac3p_ats_parse_header(avpkt->data, avpkt->size, avctx, &downmix_levels); + if (ret < 0) + return ret; + + av_freep(&ctx->atrac3p_ctx.ch_units); + ret = init_ch_units(&ctx->atrac3p_ctx, avctx); + if (ret < 0) + return ret; + + if (downmix_levels.front_downmix_level || downmix_levels.center_downmix_level || + downmix_levels.back_downmix_level || downmix_levels.lfe_downmix_level || + downmix_levels.side_downmix_level) + avpriv_report_missing_feature(avctx, "Downmixing"); + + ctx->prev_header = ats_header; + } + + avpkt->data += ATRAC3P_ATS_HEADER_SIZE; + avpkt->size -= ATRAC3P_ATS_HEADER_SIZE; + + return atrac3p_decode_frame(avctx, frame, got_frame_ptr, avpkt); +} + +const FFCodec ff_atrac3p_ats_decoder = { + .p.name = "atrac3plus_ats", + CODEC_LONG_NAME("ATRAC3+ ATS (Adaptive TRansform Acoustic Coding 3+ ATS syntax)"), + .p.type = AVMEDIA_TYPE_AUDIO, + .p.id = AV_CODEC_ID_ATRAC3P_ATS, + .p.capabilities = AV_CODEC_CAP_CHANNEL_CONF | AV_CODEC_CAP_DR1, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, + .priv_data_size = sizeof(ATRAC3PATSContext), + .init = atrac3p_decode_init, + .close = atrac3p_decode_close, + FF_CODEC_DECODE_CB(atrac3p_ats_decode_frame), +}; + +#endif diff --git a/libavcodec/utils.c b/libavcodec/utils.c index 306696ecc5c..2d0d1b3dcd7 100644 --- a/libavcodec/utils.c +++ b/libavcodec/utils.c @@ -608,6 +608,7 @@ static int get_audio_frame_duration(enum AVCodecID id, int sr, int ch, int ba, if (framecount > INT_MAX/1024) return 0; return 1024 * framecount; + case AV_CODEC_ID_ATRAC3P_ATS: case AV_CODEC_ID_ATRAC3P: return 2048; case AV_CODEC_ID_MP2: case AV_CODEC_ID_MUSEPACK7: return 1152; -- 2.54.0 >From 3867736e4b4acef71f870e852b67ef570df14d2f Mon Sep 17 00:00:00 2001 From: Simon Capriotti <[email protected]> Date: Sat, 17 Oct 2026 14:30:50 +0200 Subject: [PATCH 5/7] avformat/riffdec: add support for ATRAC3+ ATS Signed-off-by: Simon Capriotti <[email protected]> --- libavformat/riff.c | 1 + libavformat/riffdec.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/libavformat/riff.c b/libavformat/riff.c index 50c14ecb661..4a4c5564deb 100644 --- a/libavformat/riff.c +++ b/libavformat/riff.c @@ -662,6 +662,7 @@ const struct AVCodecTag *avformat_get_riff_audio_tags(void) const AVCodecGuid ff_codec_wav_guids[] = { { AV_CODEC_ID_AC3, { 0x2C, 0x80, 0x6D, 0xE0, 0x46, 0xDB, 0xCF, 0x11, 0xB4, 0xD1, 0x00, 0x80, 0x5F, 0x6C, 0xBB, 0xEA } }, { AV_CODEC_ID_ATRAC3P, { 0xBF, 0xAA, 0x23, 0xE9, 0x58, 0xCB, 0x71, 0x44, 0xA1, 0x19, 0xFF, 0xFA, 0x01, 0xE4, 0xCE, 0x62 } }, + { AV_CODEC_ID_ATRAC3P_ATS, { 0x0D, 0x5C, 0x13, 0x50, 0x1B, 0xA2, 0xEC, 0x4B, 0x91, 0xED, 0x6E, 0xDD, 0xB8, 0x3C, 0x8C, 0x66 } }, { AV_CODEC_ID_ATRAC9, { 0xD2, 0x42, 0xE1, 0x47, 0xBA, 0x36, 0x8D, 0x4D, 0x88, 0xFC, 0x61, 0x65, 0x4F, 0x8C, 0x83, 0x6C } }, { AV_CODEC_ID_EAC3, { 0xAF, 0x87, 0xFB, 0xA7, 0x02, 0x2D, 0xFB, 0x42, 0xA4, 0xD4, 0x05, 0xCD, 0x93, 0x84, 0x3B, 0xDD } }, { AV_CODEC_ID_MP2, { 0x2B, 0x80, 0x6D, 0xE0, 0x46, 0xDB, 0xCF, 0x11, 0xB4, 0xD1, 0x00, 0x80, 0x5F, 0x6C, 0xBB, 0xEA } }, diff --git a/libavformat/riffdec.c b/libavformat/riffdec.c index cb258897982..9b63d358f53 100644 --- a/libavformat/riffdec.c +++ b/libavformat/riffdec.c @@ -254,6 +254,9 @@ int ff_get_wav_header(AVFormatContext *s, AVIOContext *pb, /* override bits_per_coded_sample for G.726 */ if (par->codec_id == AV_CODEC_ID_ADPCM_G726 && par->sample_rate) par->bits_per_coded_sample = par->bit_rate / par->sample_rate; + /* add the size of the ATS header for ATRAC3+ ATS */ + if (par->codec_id == AV_CODEC_ID_ATRAC3P_ATS) + par->block_align += 8; if (par->codec_id == AV_CODEC_ID_ADPCM_RHETOREX && par->bits_per_coded_sample == 3) par->sample_rate = par->sample_rate * 3LL / 4; -- 2.54.0 >From fdc244a6cf7e9e2292072dda9a908a2f4dfbbc8c Mon Sep 17 00:00:00 2001 From: Simon Capriotti <[email protected]> Date: Sat, 17 Oct 2026 14:30:56 +0200 Subject: [PATCH 6/7] Changelog: add entry for ATRAC3+ ATS Signed-off-by: Simon Capriotti <[email protected]> --- Changelog | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog b/Changelog index 055b0cfa797..5d4c49746b1 100644 --- a/Changelog +++ b/Changelog @@ -3,6 +3,7 @@ releases are sorted from youngest to oldest. version <next>: - JPEG Multi-Picture Format demuxer +- ATRAC3+ ATS decoder and parser version 9.1: - ASTC image encoding and decoding via libastcenc, with .astc and KTX 1.0 muxers and demuxers -- 2.54.0 >From 14d8cfa9fdac20a4a7a78343b0e4cbf8ef5c16ec Mon Sep 17 00:00:00 2001 From: Simon Capriotti <[email protected]> Date: Sat, 17 Oct 2026 14:31:01 +0200 Subject: [PATCH 7/7] tests/fate/atrac: add tests for ATRAC3+ ATS Signed-off-by: Simon Capriotti <[email protected]> --- tests/fate/atrac.mak | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tests/fate/atrac.mak b/tests/fate/atrac.mak index 847eed5b619..1e18bc9b239 100644 --- a/tests/fate/atrac.mak +++ b/tests/fate/atrac.mak @@ -33,7 +33,17 @@ fate-atrac3p-2: REF = $(SAMPLES)/atrac3p/sonateno14op27-2-cut.pcm FATE_ATRAC3P-$(call PCM, OMA, ATRAC3P, ARESAMPLE_FILTER) += $(FATE_ATRAC3P) -FATE_ATRAC_ALL = $(FATE_ATRAC1-yes) $(FATE_ATRAC3-yes) $(FATE_ATRAC3P-yes) +FATE_ATRAC3P_ATS += fate-atrac3p-ats-1 +fate-atrac3p-ats-1: CMD = pcm -i $(TARGET_SAMPLES)/atrac3p-ats/at3p_sample1.atx +fate-atrac3p-ats-1: REF = $(SAMPLES)/atrac3p-ats/at3p_sample1.pcm + +FATE_ATRAC3P_ATS += fate-atrac3p-ats-2 +fate-atrac3p-ats-2: CMD = pcm -i $(TARGET_SAMPLES)/atrac3p-ats/sonateno14op27-2-cut.atx +fate-atrac3p-ats-2: REF = $(SAMPLES)/atrac3p-ats/sonateno14op27-2-cut.pcm + +FATE_ATRAC3P_ATS-$(call PCM, WAV, ATRAC3P_ATS, ARESAMPLE_FILTER) += $(FATE_ATRAC3P_ATS) + +FATE_ATRAC_ALL = $(FATE_ATRAC1-yes) $(FATE_ATRAC3-yes) $(FATE_ATRAC3P-yes) $(FATE_ATRAC3P_ATS-yes) $(FATE_ATRAC_ALL): CMP = oneoff @@ -50,3 +60,4 @@ FATE_SAMPLES_FFMPEG += $(FATE_ATRAC_ALL) $(FATE_ATRAC_REMUX-yes) fate-atrac: $(FATE_ATRAC_ALL) $(FATE_ATRAC_REMUX-yes) fate-atrac3: $(FATE_ATRAC3-yes) fate-atrac3p: $(FATE_ATRAC3P-yes) +fate-atrac3p-ats: $(FATE_ATRAC3P_ATS-yes) -- 2.54.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
