PR #22930 opened by Macdu URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22930 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22930.patch
**Quick explanation of the atrac9 specificities**: Frames in the atrac9 format have a fixed sample size, however they have a variable byte length. Frames are grouped into what is called a superframe, and a superframe contains either 1 or 4 frames. Like frames, a superframe has a fixed sample size but its byte length is also fixed. The number of samples in a frame, number of frames in a superframe and byte size of a superframe are all specified in the extradata and set during the decoder initialization. **Content of the PR** Up to now, the atrac9 decoder in ffmpeg was trying to decode a whole superframe at a time. This PR modifies its behavior so that it only decodes a single frame. I also included another commit to fix the output of `get_audio_frame_duration` for the atrac9 format. Right now it returns the number of samples in a superframe and also assumes that a superframe always has exactly 1024 samples (which is wrong). With this commit, it should fall back to returning `frame_size` which is the correct value. **Small motivation for this PR** The Atrac9 format is still widely used for console games. In particular all "recent" Sony consoles (PS Vita, PS4, PS5) have hardware accelerated decoding support for this format (here are a few examples emulator-wise): [https://github.com/Vita3K/Vita3K/blob/master/vita3k/codec/src/atrac9.cpp](https://github.com/Vita3K/Vita3K/blob/master/vita3k/codec/src/atrac9.cpp) [https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/ajm/ajm_at9.cpp](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/ajm/ajm_at9.cpp) [https://github.com/RPCSX/rpcsx/blob/master/rpcsx/iodev/ajm.cpp](https://github.com/RPCSX/rpcsx/blob/master/rpcsx/iodev/ajm.cpp) One thing to note from the examples above is that they all use [LibAtrac9](https://github.com/Thealexbarney/LibAtrac9) (the only other open-source atrac9 decoder) to decode atrac9 audio (while they all already have ffmpeg as a dependency). One of the reasons for this is that LibAtrac9 can decode a single frame of data while up to now, ffmpeg could not. This PR tries to bring ffmpeg on par with LibAtrac9 in this regard. >From 0ef3882ddda35bc2bcb4a6e3bfb28658f9ef4c79 Mon Sep 17 00:00:00 2001 From: Macdu <[email protected]> Date: Sun, 26 Apr 2026 22:21:17 +0200 Subject: [PATCH 1/2] avcodec/atrac9dec: decode a single frame at a time --- libavcodec/atrac9dec.c | 57 ++++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/libavcodec/atrac9dec.c b/libavcodec/atrac9dec.c index 3c18ae8b42..976f746d14 100644 --- a/libavcodec/atrac9dec.c +++ b/libavcodec/atrac9dec.c @@ -92,9 +92,12 @@ typedef struct ATRAC9Context { ATRAC9BlockData block[5]; AVLFG lfg; + int frame_index_in_superframe; + int superframe_bytes_left; + /* Set on init */ int frame_log2; - int avg_frame_size; + int superframe_bytes; int frame_count; int samplerate_idx; const ATRAC9BlockConfig *block_config; @@ -646,7 +649,7 @@ static inline void apply_band_extension(ATRAC9Context *s, ATRAC9BlockData *b, static int atrac9_decode_block(ATRAC9Context *s, GetBitContext *gb, ATRAC9BlockData *b, AVFrame *frame, - int frame_idx, int block_idx) + int block_idx) { const int first_in_pkt = !get_bits1(gb); const int reuse_params = get_bits1(gb); @@ -777,8 +780,7 @@ imdct: ATRAC9ChannelData *c = &b->channel[i]; const int dst_idx = s->block_config->plane_map[block_idx][i]; const int wsize = 1 << s->frame_log2; - const ptrdiff_t offset = wsize*frame_idx*sizeof(float); - float *dst = (float *)(frame->extended_data[dst_idx] + offset); + float *dst = (float *)frame->extended_data[dst_idx]; s->tx_fn(s->tx, s->temp, c->coeffs, sizeof(float)); s->fdsp->vector_fmul_window(dst, c->prev_win, s->temp, @@ -795,9 +797,10 @@ static int atrac9_decode_frame(AVCodecContext *avctx, AVFrame *frame, int ret; GetBitContext gb; ATRAC9Context *s = avctx->priv_data; - const int frames = FFMIN(avpkt->size / s->avg_frame_size, s->frame_count); + const int buf_size = avpkt->size; + int buf_consumed; - frame->nb_samples = (1 << s->frame_log2) * frames; + frame->nb_samples = 1 << s->frame_log2; ret = ff_get_buffer(avctx, frame, 0); if (ret < 0) return ret; @@ -806,24 +809,36 @@ static int atrac9_decode_frame(AVCodecContext *avctx, AVFrame *frame, if (ret < 0) return ret; - for (int i = 0; i < frames; i++) { - for (int j = 0; j < s->block_config->count; j++) { - ret = atrac9_decode_block(s, &gb, &s->block[j], frame, i, j); - if (ret) - return ret; - align_get_bits(&gb); - } + for (int i = 0; i < s->block_config->count; i++) { + ret = atrac9_decode_block(s, &gb, &s->block[i], frame, i); + if (ret) + return ret; + align_get_bits(&gb); } *got_frame_ptr = 1; - return avctx->block_align; + buf_consumed = get_bits_count(&gb) >> 3; + s->superframe_bytes_left -= buf_consumed; + s->frame_index_in_superframe++; + + if (s->frame_index_in_superframe == s->frame_count) { + buf_consumed += s->superframe_bytes_left; + + s->frame_index_in_superframe = 0; + s->superframe_bytes_left = s->superframe_bytes; + } + + return FFMIN(buf_consumed, buf_size); } static av_cold void atrac9_decode_flush(AVCodecContext *avctx) { ATRAC9Context *s = avctx->priv_data; + s->frame_index_in_superframe = 0; + s->superframe_bytes_left = s->superframe_bytes; + for (int j = 0; j < s->block_config->count; j++) { ATRAC9BlockData *b = &s->block[j]; const int stereo = s->block_config->type[j] == ATRAC9_BLOCK_TYPE_CPE; @@ -902,17 +917,12 @@ static av_cold int atrac9_decode_init(AVCodecContext *avctx) static AVOnce static_table_init = AV_ONCE_INIT; GetBitContext gb; ATRAC9Context *s = avctx->priv_data; - int err, version, block_config_idx, superframe_idx, alloc_c_len; + int err, version, block_config_idx, superframe_idx, alloc_c_len, avg_frame_size; s->avctx = avctx; av_lfg_init(&s->lfg, 0xFBADF00D); - if (avctx->block_align <= 0) { - av_log(avctx, AV_LOG_ERROR, "Invalid block align\n"); - return AVERROR_INVALIDDATA; - } - if (avctx->extradata_size != 12) { av_log(avctx, AV_LOG_ERROR, "Invalid extradata length!\n"); return AVERROR_INVALIDDATA; @@ -953,7 +963,7 @@ static av_cold int atrac9_decode_init(AVCodecContext *avctx) } /* Average frame size in bytes */ - s->avg_frame_size = get_bits(&gb, 11) + 1; + avg_frame_size = get_bits(&gb, 11) + 1; superframe_idx = get_bits(&gb, 2); if (superframe_idx & 1) { @@ -963,6 +973,11 @@ static av_cold int atrac9_decode_init(AVCodecContext *avctx) s->frame_count = 1 << superframe_idx; s->frame_log2 = at9_tab_sri_frame_log2[s->samplerate_idx]; + avctx->frame_size = 1 << s->frame_log2; + s->superframe_bytes = avg_frame_size << superframe_idx; + + s->frame_index_in_superframe = 0; + s->superframe_bytes_left = s->superframe_bytes; scale = 1.0f / 32768.0; err = av_tx_init(&s->tx, &s->tx_fn, AV_TX_FLOAT_MDCT, 1, -- 2.52.0 >From bbb735b16dfa19fa2a868a39055107e20c2ea302 Mon Sep 17 00:00:00 2001 From: Macdu <[email protected]> Date: Sun, 26 Apr 2026 22:24:30 +0200 Subject: [PATCH 2/2] avcodec/utils: correct atrac9 frame size computation --- libavcodec/utils.c | 1 - 1 file changed, 1 deletion(-) diff --git a/libavcodec/utils.c b/libavcodec/utils.c index 615d60cd58..d9ffd27213 100644 --- a/libavcodec/utils.c +++ b/libavcodec/utils.c @@ -594,7 +594,6 @@ static int get_audio_frame_duration(enum AVCodecID id, int sr, int ch, int ba, case AV_CODEC_ID_GSM_MS: return 320; case AV_CODEC_ID_MP1: return 384; case AV_CODEC_ID_ATRAC1: return 512; - case AV_CODEC_ID_ATRAC9: case AV_CODEC_ID_ATRAC3: if (framecount > INT_MAX/1024) return 0; -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
