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]

Reply via email to