Commit 6287ad1f9b for ffmpeg

commit 6287ad1f9bd168e2a417b91df0a705c8d62925f3
Author: Lynne <dev@lynne.ee>
Date:   Sun Jul 26 13:55:17 2026 +0800

    jpeg2000dec: convert the decoder to the receive_frame API

    Convert the decoder to the receive_frame API (one codestream per
    packet; skipped frames pull the next packet instead of returning
    empty-handed), finish threading setup before the tile decode, and do
    not leave a partially initialized frame on the output on error.

    Move the tag-tree size, exp2fi and band stepsize helpers into the
    shared header as ff_-prefixed inlines so code outside jpeg2000.c can
    derive the same geometry and quantization parameters.

diff --git a/libavcodec/jpeg2000.c b/libavcodec/jpeg2000.c
index 6433e13892..a21f9c2a82 100644
--- a/libavcodec/jpeg2000.c
+++ b/libavcodec/jpeg2000.c
@@ -39,18 +39,6 @@

 /* tag tree routines */

-static int32_t tag_tree_size(int w, int h)
-{
-    int64_t res = 0;
-    while (w > 1 || h > 1) {
-        res += w * (int64_t)h;
-        av_assert0(res + 1 < INT32_MAX);
-        w = (w + 1) >> 1;
-        h = (h + 1) >> 1;
-    }
-    return (int32_t)(res + 1);
-}
-
 /* allocate the memory for tag tree */
 static Jpeg2000TgtNode *ff_jpeg2000_tag_tree_init(int w, int h)
 {
@@ -58,7 +46,7 @@ static Jpeg2000TgtNode *ff_jpeg2000_tag_tree_init(int w, int h)
     Jpeg2000TgtNode *res, *t, *t2;
     int32_t tt_size;

-    tt_size = tag_tree_size(w, h);
+    tt_size = ff_jpeg2000_tag_tree_size(w, h);

     t = res = av_calloc(tt_size, sizeof(*t));
     if (!res)
@@ -85,7 +73,7 @@ static Jpeg2000TgtNode *ff_jpeg2000_tag_tree_init(int w, int h)

 void ff_tag_tree_zero(Jpeg2000TgtNode *t, int w, int h, int val)
 {
-    int i, siz = tag_tree_size(w, h);
+    int i, siz = ff_jpeg2000_tag_tree_size(w, h);

     for (i = 0; i < siz; i++) {
         t[i].val = val;
@@ -199,33 +187,22 @@ void ff_jpeg2000_set_significance(Jpeg2000T1Context *t1, int x, int y,
     t1->flags[(y - 1) * t1->stride + x - 1] |= JPEG2000_T1_SIG_SE;
 }

+/* TODO: Implementation of quantization step not finished, see
+ * ISO/IEC 15444-1:2002 E.1 and A.6.4. */
 // static const uint8_t lut_gain[2][4] = { { 0, 0, 0, 0 }, { 0, 1, 1, 2 } }; (unused)
-
-/**
- * 2^(x) for integer x in the range -126..128.
- * @return correctly rounded float
- */
-static av_always_inline float exp2fi(int x)
+float ff_jpeg2000_band_stepsize(AVCodecContext *avctx,
+                                              Jpeg2000CodingStyle *codsty,
+                                              Jpeg2000QuantStyle *qntsty,
+                                              int bandno, int gbandno,
+                                              int reslevelno, int cbps)
 {
-    av_assert2(-126 <= x && x <= 128);
-    /* Normal range */
-    return av_int2float((x+127) << 23);
-}
+    float fss;

-static void init_band_stepsize(AVCodecContext *avctx,
-                               Jpeg2000Band *band,
-                               Jpeg2000CodingStyle *codsty,
-                               Jpeg2000QuantStyle *qntsty,
-                               int bandno, int gbandno, int reslevelno,
-                               int cbps)
-{
-    /* TODO: Implementation of quantization step not finished,
-     * see ISO/IEC 15444-1:2002 E.1 and A.6.4. */
     switch (qntsty->quantsty) {
         uint8_t gain;
     case JPEG2000_QSTY_NONE:
         /* TODO: to verify. No quantization in this case */
-        band->f_stepsize = 1;
+        fss = 1;
         break;
     case JPEG2000_QSTY_SI:
         /*TODO: Compute formula to implement. */
@@ -240,35 +217,47 @@ static void init_band_stepsize(AVCodecContext *avctx,
          * delta_b = 2 ^ (R_b - expn_b) * (1 + (mant_b / 2 ^ 11))
          * R_b = R_I + log2 (gain_b )
          * see ISO/IEC 15444-1:2002 E.1.1 eqn. E-3 and E-4 */
-        gain            = cbps;
-        band->f_stepsize  = exp2fi(gain - qntsty->expn[gbandno]);
-        band->f_stepsize *= qntsty->mant[gbandno] / 2048.0 + 1.0;
+        gain = cbps;
+        fss  = ff_jpeg2000_exp2fi(gain - qntsty->expn[gbandno]);
+        fss *= qntsty->mant[gbandno] / 2048.0 + 1.0;
         break;
     default:
-        band->f_stepsize = 0;
         av_log(avctx, AV_LOG_ERROR, "Unknown quantization format\n");
-        break;
+        return 0;
     }
     if (codsty->transform != FF_DWT53) {
         int lband = 0;
         switch (bandno + (reslevelno > 0)) {
-            case 1:
-            case 2:
-                band->f_stepsize *= F_LFTG_X * 2;
-                lband = 1;
-                break;
-            case 3:
-                band->f_stepsize *= F_LFTG_X * F_LFTG_X * 4;
-                break;
+        case 1:
+        case 2:
+            fss *= F_LFTG_X * 2;
+            lband = 1;
+            break;
+        case 3:
+            fss *= F_LFTG_X * F_LFTG_X * 4;
+            break;
         }
-        band->f_stepsize *= pow(F_LFTG_K, 2*(codsty->nreslevels2decode - reslevelno) + lband - 2);
+        fss *= pow(F_LFTG_K, 2 * (codsty->nreslevels2decode - reslevelno) +
+                             lband - 2);
     }

-    if (band->f_stepsize > (INT_MAX >> 15)) {
-        band->f_stepsize = 0;
+    if (fss > (INT_MAX >> 15)) {
         av_log(avctx, AV_LOG_ERROR, "stepsize out of range\n");
+        return 0;
     }
+    return fss;
+}

+static void init_band_stepsize(AVCodecContext *avctx,
+                               Jpeg2000Band *band,
+                               Jpeg2000CodingStyle *codsty,
+                               Jpeg2000QuantStyle *qntsty,
+                               int bandno, int gbandno, int reslevelno,
+                               int cbps)
+{
+    band->f_stepsize = ff_jpeg2000_band_stepsize(avctx, codsty, qntsty,
+                                                 bandno, gbandno, reslevelno,
+                                                 cbps);
     band->i_stepsize = (int)floorf(band->f_stepsize * (1 << 15));
 }

diff --git a/libavcodec/jpeg2000.h b/libavcodec/jpeg2000.h
index 93221d90ca..52c00f4268 100644
--- a/libavcodec/jpeg2000.h
+++ b/libavcodec/jpeg2000.h
@@ -31,6 +31,9 @@

 #include <stdint.h>

+#include "libavutil/avassert.h"
+#include "libavutil/intfloat.h"
+
 #include "avcodec.h"
 #include "mqc.h"
 #include "jpeg2000dwt.h"
@@ -289,6 +292,39 @@ static inline int ff_jpeg2000_getsgnctxno(int flag, int *xorbit)
     return ff_jpeg2000_sgnctxno_lut[flag & 15][(flag >> 8) & 15];
 }

+/* Node count of the tag-tree pyramid over a (w, h) leaf grid. */
+static inline int32_t ff_jpeg2000_tag_tree_size(int w, int h)
+{
+    int64_t res = 0;
+    while (w > 1 || h > 1) {
+        res += w * (int64_t)h;
+        av_assert0(res + 1 < INT32_MAX);
+        w = (w + 1) >> 1;
+        h = (h + 1) >> 1;
+    }
+    return (int32_t)(res + 1);
+}
+
+/**
+ * 2^(x) for integer x in the range -126..128.
+ * @return correctly rounded float
+ */
+static av_always_inline float ff_jpeg2000_exp2fi(int x)
+{
+    av_assert2(-126 <= x && x <= 128);
+    /* Normal range */
+    return av_int2float((x + 127) << 23);
+}
+
+/* Band dequantization step size (ISO/IEC 15444-1:2002 E.1), shared between
+ * the software decoder's band setup and the Vulkan hwaccel's inline
+ * geometry. */
+float ff_jpeg2000_band_stepsize(AVCodecContext *avctx,
+                                Jpeg2000CodingStyle *codsty,
+                                Jpeg2000QuantStyle *qntsty,
+                                int bandno, int gbandno,
+                                int reslevelno, int cbps);
+
 int ff_jpeg2000_init_component(Jpeg2000Component *comp,
                                Jpeg2000CodingStyle *codsty,
                                Jpeg2000QuantStyle *qntsty,
diff --git a/libavcodec/jpeg2000dec.c b/libavcodec/jpeg2000dec.c
index 369377e4fc..870678f0b0 100644
--- a/libavcodec/jpeg2000dec.c
+++ b/libavcodec/jpeg2000dec.c
@@ -39,6 +39,7 @@
 #include "bytestream.h"
 #include "codec_internal.h"
 #include "decode.h"
+#include "internal.h"
 #include "thread.h"
 #include "jpeg2000.h"
 #include "jpeg2000dsp.h"
@@ -2855,7 +2856,7 @@ static av_cold int jpeg2000_decode_init(AVCodecContext *avctx)
 }

 static int jpeg2000_decode_frame(AVCodecContext *avctx, AVFrame *picture,
-                                 int *got_frame, AVPacket *avpkt)
+                                 const AVPacket *avpkt)
 {
     Jpeg2000DecoderContext *s = avctx->priv_data;
     int ret;
@@ -2902,7 +2903,7 @@ static int jpeg2000_decode_frame(AVCodecContext *avctx, AVFrame *picture,

     if (avctx->skip_frame >= AVDISCARD_ALL) {
         jpeg2000_dec_cleanup(s);
-        return avpkt->size;
+        return 0;
     }

     /* get picture buffer */
@@ -2916,22 +2917,47 @@ static int jpeg2000_decode_frame(AVCodecContext *avctx, AVFrame *picture,
         if (++x == s->ncomponents)
             picture->flags |= AV_FRAME_FLAG_LOSSLESS;

-    avctx->execute2(avctx, jpeg2000_decode_tile, picture, NULL, s->numXtiles * s->numYtiles);
+    ff_thread_finish_setup(avctx);

-    jpeg2000_dec_cleanup(s);
+    avctx->execute2(avctx, jpeg2000_decode_tile, picture, NULL,
+                    s->numXtiles * s->numYtiles);

-    *got_frame = 1;
+    jpeg2000_dec_cleanup(s);

     if (s->avctx->pix_fmt == AV_PIX_FMT_PAL8)
         memcpy(picture->data[1], s->palette, 256 * sizeof(uint32_t));

-    return bytestream2_tell(&s->g);
+    return 0;

 end:
+    /* Do not leave a partially initialized frame on the output. */
+    av_frame_unref(picture);
     jpeg2000_dec_cleanup(s);
     return ret;
 }

+static int jpeg2000_receive_frame(AVCodecContext *avctx, AVFrame *frame)
+{
+    AVPacket *const avpkt = avctx->internal->in_pkt;
+    int ret;
+
+    /* One codestream per packet. Loop so that skipped frames (produce no
+     * output) transparently pull the next packet instead of returning to the
+     * caller empty-handed. */
+    do {
+        ret = ff_decode_get_packet(avctx, avpkt);
+        if (ret < 0)
+            return ret;
+
+        ret = jpeg2000_decode_frame(avctx, frame, avpkt);
+        av_packet_unref(avpkt);
+        if (ret < 0)
+            return ret;
+    } while (!frame->buf[0]);
+
+    return 0;
+}
+
 #define OFFSET(x) offsetof(Jpeg2000DecoderContext, x)
 #define VD AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM

@@ -2956,7 +2982,7 @@ const FFCodec ff_jpeg2000_decoder = {
     .p.capabilities   = AV_CODEC_CAP_SLICE_THREADS | AV_CODEC_CAP_FRAME_THREADS | AV_CODEC_CAP_DR1,
     .priv_data_size   = sizeof(Jpeg2000DecoderContext),
     .init             = jpeg2000_decode_init,
-    FF_CODEC_DECODE_CB(jpeg2000_decode_frame),
+    FF_CODEC_RECEIVE_FRAME_CB(jpeg2000_receive_frame),
     .p.priv_class     = &jpeg2000_class,
     .p.max_lowres     = 5,
     .p.profiles       = NULL_IF_CONFIG_SMALL(ff_jpeg2000_profiles),