Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 help / color / mirror / Atom feed
* [FFmpeg-devel] [PR] [release/8.0] backport LCEVC fixes (PR #21433)
@ 2026-01-11 23:42 James Almer via ffmpeg-devel
  0 siblings, 0 replies; only message in thread
From: James Almer via ffmpeg-devel @ 2026-01-11 23:42 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: James Almer

PR #21433 opened by James Almer (jamrial)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21433
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21433.patch


>From 02e30de0432aa3f988bfd2e66d12424fc9d7926f Mon Sep 17 00:00:00 2001
From: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
Date: Sun, 2 Nov 2025 15:29:59 +0100
Subject: [PATCH 1/8] avcodec/decode: Don't allocate LCEVC context for
 non-video

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
(cherry picked from commit 182b9c7a4a7117371d51caa917f26162db53cc56)
---
 libavcodec/decode.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/libavcodec/decode.c b/libavcodec/decode.c
index 730d4f90fe..2178931758 100644
--- a/libavcodec/decode.c
+++ b/libavcodec/decode.c
@@ -1995,9 +1995,11 @@ int ff_decode_preinit(AVCodecContext *avctx)
         return ret;
 
     if (!(avctx->export_side_data & AV_CODEC_EXPORT_DATA_ENHANCEMENTS)) {
-        ret = ff_lcevc_alloc(&dc->lcevc);
-        if (ret < 0 && (avctx->err_recognition & AV_EF_EXPLODE))
-            return ret;
+        if (avctx->codec_type == AVMEDIA_TYPE_VIDEO) {
+            ret = ff_lcevc_alloc(&dc->lcevc);
+            if (ret < 0 && (avctx->err_recognition & AV_EF_EXPLODE))
+                return ret;
+        }
     }
 
     return 0;
-- 
2.49.1


>From a635df770f0c0bd6e0ad3d06cac8c5d7d6982525 Mon Sep 17 00:00:00 2001
From: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
Date: Sun, 2 Nov 2025 16:00:06 +0100
Subject: [PATCH 2/8] avcodec/decode: Put lcevc fields into structure of their
 own

Makes it easier to see that width and height in DecodeContext is
actually a lcevc field.

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
(cherry picked from commit 2786e5a9ad32920fccee9352161e81c8e733563b)
---
 libavcodec/decode.c | 34 ++++++++++++++++++----------------
 1 file changed, 18 insertions(+), 16 deletions(-)

diff --git a/libavcodec/decode.c b/libavcodec/decode.c
index 2178931758..d88d245f3d 100644
--- a/libavcodec/decode.c
+++ b/libavcodec/decode.c
@@ -93,10 +93,12 @@ typedef struct DecodeContext {
      */
     uint64_t side_data_pref_mask;
 
-    FFLCEVCContext *lcevc;
-    int lcevc_frame;
-    int width;
-    int height;
+    struct {
+        FFLCEVCContext *ctx;
+        int frame;
+        int width;
+        int height;
+    } lcevc;
 } DecodeContext;
 
 static DecodeContext *decode_ctx(AVCodecInternal *avci)
@@ -1576,12 +1578,12 @@ static void update_frame_props(AVCodecContext *avctx, AVFrame *frame)
     AVCodecInternal    *avci = avctx->internal;
     DecodeContext        *dc = decode_ctx(avci);
 
-    dc->lcevc_frame = dc->lcevc && avctx->codec_type == AVMEDIA_TYPE_VIDEO &&
+    dc->lcevc.frame = dc->lcevc.ctx && avctx->codec_type == AVMEDIA_TYPE_VIDEO &&
                       av_frame_get_side_data(frame, AV_FRAME_DATA_LCEVC);
 
-    if (dc->lcevc_frame) {
-        dc->width     = frame->width;
-        dc->height    = frame->height;
+    if (dc->lcevc.frame) {
+        dc->lcevc.width  = frame->width;
+        dc->lcevc.height = frame->height;
         frame->width  = frame->width  * 2 / FFMAX(frame->sample_aspect_ratio.den, 1);
         frame->height = frame->height * 2 / FFMAX(frame->sample_aspect_ratio.num, 1);
     }
@@ -1592,7 +1594,7 @@ static int attach_post_process_data(AVCodecContext *avctx, AVFrame *frame)
     AVCodecInternal    *avci = avctx->internal;
     DecodeContext        *dc = decode_ctx(avci);
 
-    if (dc->lcevc_frame) {
+    if (dc->lcevc.frame) {
         FrameDecodeData *fdd = frame->private_ref;
         FFLCEVCFrame *frame_ctx;
         int ret;
@@ -1607,13 +1609,13 @@ static int attach_post_process_data(AVCodecContext *avctx, AVFrame *frame)
             return AVERROR(ENOMEM);
         }
 
-        frame_ctx->lcevc = av_refstruct_ref(dc->lcevc);
+        frame_ctx->lcevc = av_refstruct_ref(dc->lcevc.ctx);
         frame_ctx->frame->width  = frame->width;
         frame_ctx->frame->height = frame->height;
         frame_ctx->frame->format = frame->format;
 
-        frame->width  = dc->width;
-        frame->height = dc->height;
+        frame->width  = dc->lcevc.width;
+        frame->height = dc->lcevc.height;
 
         ret = avctx->get_buffer2(avctx, frame_ctx->frame, 0);
         if (ret < 0) {
@@ -1627,7 +1629,7 @@ static int attach_post_process_data(AVCodecContext *avctx, AVFrame *frame)
         fdd->post_process_opaque_free = ff_lcevc_unref;
         fdd->post_process = ff_lcevc_process;
     }
-    dc->lcevc_frame = 0;
+    dc->lcevc.frame = 0;
 
     return 0;
 }
@@ -1996,7 +1998,7 @@ int ff_decode_preinit(AVCodecContext *avctx)
 
     if (!(avctx->export_side_data & AV_CODEC_EXPORT_DATA_ENHANCEMENTS)) {
         if (avctx->codec_type == AVMEDIA_TYPE_VIDEO) {
-            ret = ff_lcevc_alloc(&dc->lcevc);
+            ret = ff_lcevc_alloc(&dc->lcevc.ctx);
             if (ret < 0 && (avctx->err_recognition & AV_EF_EXPLODE))
                 return ret;
         }
@@ -2239,7 +2241,7 @@ void ff_decode_internal_sync(AVCodecContext *dst, const AVCodecContext *src)
 
     dst_dc->initial_pict_type = src_dc->initial_pict_type;
     dst_dc->intra_only_flag   = src_dc->intra_only_flag;
-    av_refstruct_replace(&dst_dc->lcevc, src_dc->lcevc);
+    av_refstruct_replace(&dst_dc->lcevc.ctx, src_dc->lcevc.ctx);
 }
 
 void ff_decode_internal_uninit(AVCodecContext *avctx)
@@ -2247,5 +2249,5 @@ void ff_decode_internal_uninit(AVCodecContext *avctx)
     AVCodecInternal *avci = avctx->internal;
     DecodeContext *dc = decode_ctx(avci);
 
-    av_refstruct_unref(&dc->lcevc);
+    av_refstruct_unref(&dc->lcevc.ctx);
 }
-- 
2.49.1


>From 050da957c38851f6ee7e7fe4f6ccf478defaf459 Mon Sep 17 00:00:00 2001
From: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
Date: Sun, 2 Nov 2025 16:50:36 +0100
Subject: [PATCH 3/8] avcodec/decode: Optimize lcevc away if disabled

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
(cherry picked from commit 8e90f150ebccf3f30fe139245b7d22fd6f1ee4a9)
---
 libavcodec/Makefile   |  2 +-
 libavcodec/decode.c   | 12 ++++++++++++
 libavcodec/lcevcdec.c | 10 ----------
 3 files changed, 13 insertions(+), 11 deletions(-)

diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index fb22541f8d..5a4d4d341c 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -45,7 +45,6 @@ OBJS = ac3_parser.o                                                     \
        get_buffer.o                                                     \
        imgconvert.o                                                     \
        jni.o                                                            \
-       lcevcdec.o                                                       \
        mathtables.o                                                     \
        mediacodec.o                                                     \
        mpeg12framerate.o                                                \
@@ -128,6 +127,7 @@ OBJS-$(CONFIG_IVIDSP)                  += ivi_dsp.o
 OBJS-$(CONFIG_JNI)                     += ffjni.o jni.o
 OBJS-$(CONFIG_JPEGTABLES)              += jpegtables.o
 OBJS-$(CONFIG_LCMS2)                   += fflcms2.o
+OBJS-$(CONFIG_LIBLCEVC_DEC)            += lcevcdec.o
 OBJS-$(CONFIG_LLAUDDSP)                += lossless_audiodsp.o
 OBJS-$(CONFIG_LLVIDDSP)                += lossless_videodsp.o
 OBJS-$(CONFIG_LLVIDENCDSP)             += lossless_videoencdsp.o
diff --git a/libavcodec/decode.c b/libavcodec/decode.c
index d88d245f3d..d94232081c 100644
--- a/libavcodec/decode.c
+++ b/libavcodec/decode.c
@@ -93,12 +93,14 @@ typedef struct DecodeContext {
      */
     uint64_t side_data_pref_mask;
 
+#if CONFIG_LIBLCEVC_DEC
     struct {
         FFLCEVCContext *ctx;
         int frame;
         int width;
         int height;
     } lcevc;
+#endif
 } DecodeContext;
 
 static DecodeContext *decode_ctx(AVCodecInternal *avci)
@@ -1575,6 +1577,7 @@ int ff_attach_decode_data(AVFrame *frame)
 
 static void update_frame_props(AVCodecContext *avctx, AVFrame *frame)
 {
+#if CONFIG_LIBLCEVC_DEC
     AVCodecInternal    *avci = avctx->internal;
     DecodeContext        *dc = decode_ctx(avci);
 
@@ -1587,10 +1590,12 @@ static void update_frame_props(AVCodecContext *avctx, AVFrame *frame)
         frame->width  = frame->width  * 2 / FFMAX(frame->sample_aspect_ratio.den, 1);
         frame->height = frame->height * 2 / FFMAX(frame->sample_aspect_ratio.num, 1);
     }
+#endif
 }
 
 static int attach_post_process_data(AVCodecContext *avctx, AVFrame *frame)
 {
+#if CONFIG_LIBLCEVC_DEC
     AVCodecInternal    *avci = avctx->internal;
     DecodeContext        *dc = decode_ctx(avci);
 
@@ -1630,6 +1635,7 @@ static int attach_post_process_data(AVCodecContext *avctx, AVFrame *frame)
         fdd->post_process = ff_lcevc_process;
     }
     dc->lcevc.frame = 0;
+#endif
 
     return 0;
 }
@@ -1998,9 +2004,11 @@ int ff_decode_preinit(AVCodecContext *avctx)
 
     if (!(avctx->export_side_data & AV_CODEC_EXPORT_DATA_ENHANCEMENTS)) {
         if (avctx->codec_type == AVMEDIA_TYPE_VIDEO) {
+#if CONFIG_LIBLCEVC_DEC
             ret = ff_lcevc_alloc(&dc->lcevc.ctx);
             if (ret < 0 && (avctx->err_recognition & AV_EF_EXPLODE))
                 return ret;
+#endif
         }
     }
 
@@ -2241,13 +2249,17 @@ void ff_decode_internal_sync(AVCodecContext *dst, const AVCodecContext *src)
 
     dst_dc->initial_pict_type = src_dc->initial_pict_type;
     dst_dc->intra_only_flag   = src_dc->intra_only_flag;
+#if CONFIG_LIBLCEVC_DEC
     av_refstruct_replace(&dst_dc->lcevc.ctx, src_dc->lcevc.ctx);
+#endif
 }
 
 void ff_decode_internal_uninit(AVCodecContext *avctx)
 {
+#if CONFIG_LIBLCEVC_DEC
     AVCodecInternal *avci = avctx->internal;
     DecodeContext *dc = decode_ctx(avci);
 
     av_refstruct_unref(&dc->lcevc.ctx);
+#endif
 }
diff --git a/libavcodec/lcevcdec.c b/libavcodec/lcevcdec.c
index 158957abc5..45c4ad391c 100644
--- a/libavcodec/lcevcdec.c
+++ b/libavcodec/lcevcdec.c
@@ -16,8 +16,6 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#include "config_components.h"
-
 #include "libavutil/avassert.h"
 #include "libavutil/frame.h"
 #include "libavutil/imgutils.h"
@@ -28,7 +26,6 @@
 #include "decode.h"
 #include "lcevcdec.h"
 
-#if CONFIG_LIBLCEVC_DEC
 static LCEVC_ColorFormat map_format(int format)
 {
     switch (format) {
@@ -257,11 +254,9 @@ static void lcevc_free(AVRefStructOpaque unused, void *obj)
         LCEVC_DestroyDecoder(lcevc->decoder);
     memset(lcevc, 0, sizeof(*lcevc));
 }
-#endif
 
 static int lcevc_init(FFLCEVCContext *lcevc, void *logctx)
 {
-#if CONFIG_LIBLCEVC_DEC
     LCEVC_AccelContextHandle dummy = { 0 };
     const int32_t event = LCEVC_Log;
 
@@ -280,7 +275,6 @@ static int lcevc_init(FFLCEVCContext *lcevc, void *logctx)
         return AVERROR_EXTERNAL;
     }
 
-#endif
     lcevc->initialized = 1;
 
     return 0;
@@ -299,7 +293,6 @@ int ff_lcevc_process(void *logctx, AVFrame *frame)
             return ret;
     }
 
-#if CONFIG_LIBLCEVC_DEC
     av_assert0(frame_ctx->frame);
 
 
@@ -312,7 +305,6 @@ int ff_lcevc_process(void *logctx, AVFrame *frame)
         return ret;
 
     av_frame_remove_side_data(frame, AV_FRAME_DATA_LCEVC);
-#endif
 
     return 0;
 }
@@ -320,11 +312,9 @@ int ff_lcevc_process(void *logctx, AVFrame *frame)
 int ff_lcevc_alloc(FFLCEVCContext **plcevc)
 {
     FFLCEVCContext *lcevc = NULL;
-#if CONFIG_LIBLCEVC_DEC
     lcevc = av_refstruct_alloc_ext(sizeof(*lcevc), 0, NULL, lcevc_free);
     if (!lcevc)
         return AVERROR(ENOMEM);
-#endif
     *plcevc = lcevc;
     return 0;
 }
-- 
2.49.1


>From c92c2bc86370f9f9fde549f827feaddd62b26a58 Mon Sep 17 00:00:00 2001
From: James Almer <jamrial@gmail.com>
Date: Mon, 22 Dec 2025 21:53:47 -0300
Subject: [PATCH 4/8] avcodec/lcevcdec: avoid copying the input frame

Based on the lcevc filter implementation.

Signed-off-by: James Almer <jamrial@gmail.com>
(cherry picked from commit b392d75cf72cca90b5476e0c1b2e0876f3aff4ab)
---
 libavcodec/lcevcdec.c | 37 +++++++------------------------------
 1 file changed, 7 insertions(+), 30 deletions(-)

diff --git a/libavcodec/lcevcdec.c b/libavcodec/lcevcdec.c
index 45c4ad391c..d5cc2b73f3 100644
--- a/libavcodec/lcevcdec.c
+++ b/libavcodec/lcevcdec.c
@@ -49,10 +49,7 @@ static int alloc_base_frame(void *logctx, FFLCEVCContext *lcevc,
 {
     LCEVC_PictureDesc desc;
     LCEVC_ColorFormat fmt = map_format(frame->format);
-    LCEVC_PictureLockHandle lock;
-    uint8_t *data[4] = { NULL };
-    int linesizes[4] = { 0 };
-    uint32_t planes;
+    LCEVC_PicturePlaneDesc planes[AV_VIDEO_MAX_PLANES] = { 0 };
     LCEVC_ReturnCode res;
 
     res = LCEVC_DefaultPictureDesc(&desc, fmt, frame->width, frame->height);
@@ -66,36 +63,16 @@ static int alloc_base_frame(void *logctx, FFLCEVCContext *lcevc,
     desc.sampleAspectRatioNum  = frame->sample_aspect_ratio.num;
     desc.sampleAspectRatioDen  = frame->sample_aspect_ratio.den;
 
+    for (int i = 0; i < AV_VIDEO_MAX_PLANES; i++) {
+        planes[i].firstSample = frame->data[i];
+        planes[i].rowByteStride = frame->linesize[i];
+    }
+
     /* Allocate LCEVC Picture */
-    res = LCEVC_AllocPicture(lcevc->decoder, &desc, picture);
+    res = LCEVC_AllocPictureExternal(lcevc->decoder, &desc, NULL, planes, picture);
     if (res != LCEVC_Success) {
         return AVERROR_EXTERNAL;
     }
-    res = LCEVC_LockPicture(lcevc->decoder, *picture, LCEVC_Access_Write, &lock);
-    if (res != LCEVC_Success)
-        return AVERROR_EXTERNAL;
-
-    res = LCEVC_GetPicturePlaneCount(lcevc->decoder, *picture, &planes);
-    if (res != LCEVC_Success)
-        return AVERROR_EXTERNAL;
-
-    for (unsigned i = 0; i < planes; i++) {
-        LCEVC_PicturePlaneDesc plane;
-
-        res = LCEVC_GetPictureLockPlaneDesc(lcevc->decoder, lock, i, &plane);
-        if (res != LCEVC_Success)
-            return AVERROR_EXTERNAL;
-
-        data[i] = plane.firstSample;
-        linesizes[i] = plane.rowByteStride;
-    }
-
-    av_image_copy2(data, linesizes, frame->data, frame->linesize,
-                   frame->format, frame->width, frame->height);
-
-    res = LCEVC_UnlockPicture(lcevc->decoder, lock);
-    if (res != LCEVC_Success)
-        return AVERROR_EXTERNAL;
 
     return 0;
 }
-- 
2.49.1


>From 3db8e93a209904786c679261a38d9e1acb43d4d7 Mon Sep 17 00:00:00 2001
From: James Almer <jamrial@gmail.com>
Date: Mon, 22 Dec 2025 21:54:43 -0300
Subject: [PATCH 5/8] avcodec/lcevcdec: fix input dimensions for the base
 picture

Fixes crashes with some samples.

Signed-off-by: James Almer <jamrial@gmail.com>
(cherry picked from commit 4f86ebfe940cd11de8f5ccf39e2b45da8354269d)
---
 libavcodec/lcevcdec.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/libavcodec/lcevcdec.c b/libavcodec/lcevcdec.c
index d5cc2b73f3..a88a6167dd 100644
--- a/libavcodec/lcevcdec.c
+++ b/libavcodec/lcevcdec.c
@@ -50,9 +50,11 @@ static int alloc_base_frame(void *logctx, FFLCEVCContext *lcevc,
     LCEVC_PictureDesc desc;
     LCEVC_ColorFormat fmt = map_format(frame->format);
     LCEVC_PicturePlaneDesc planes[AV_VIDEO_MAX_PLANES] = { 0 };
+    int width = frame->width - frame->crop_left - frame->crop_right;
+    int height = frame->height - frame->crop_top - frame->crop_bottom;
     LCEVC_ReturnCode res;
 
-    res = LCEVC_DefaultPictureDesc(&desc, fmt, frame->width, frame->height);
+    res = LCEVC_DefaultPictureDesc(&desc, fmt, width, height);
     if (res != LCEVC_Success)
         return AVERROR_EXTERNAL;
 
-- 
2.49.1


>From c9f97fcabfbc6a7f7e9d311df6d6171c8559587c Mon Sep 17 00:00:00 2001
From: James Almer <jamrial@gmail.com>
Date: Mon, 22 Dec 2025 21:56:09 -0300
Subject: [PATCH 6/8] avcodec/lcevcdec: free pictures on error

Signed-off-by: James Almer <jamrial@gmail.com>
(cherry picked from commit fe1dae1e1857ebda9624f4be3f93f447097ae942)
---
 libavcodec/lcevcdec.c | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/libavcodec/lcevcdec.c b/libavcodec/lcevcdec.c
index a88a6167dd..97c1dfc5b7 100644
--- a/libavcodec/lcevcdec.c
+++ b/libavcodec/lcevcdec.c
@@ -134,8 +134,10 @@ static int lcevc_send_frame(void *logctx, FFLCEVCFrame *frame_ctx, const AVFrame
 #else
     res = LCEVC_SendDecoderBase(lcevc->decoder, in->pts, 0, picture, -1, NULL);
 #endif
-    if (res != LCEVC_Success)
+    if (res != LCEVC_Success) {
+        LCEVC_FreePicture(lcevc->decoder, picture);
         return AVERROR_EXTERNAL;
+    }
 
     memset(&picture, 0, sizeof(picture));
     ret = alloc_enhanced_frame(logctx, frame_ctx, &picture);
@@ -143,8 +145,10 @@ static int lcevc_send_frame(void *logctx, FFLCEVCFrame *frame_ctx, const AVFrame
         return ret;
 
     res = LCEVC_SendDecoderPicture(lcevc->decoder, picture);
-    if (res != LCEVC_Success)
+    if (res != LCEVC_Success) {
+        LCEVC_FreePicture(lcevc->decoder, picture);
         return AVERROR_EXTERNAL;
+    }
 
     return 0;
 }
@@ -162,8 +166,10 @@ static int generate_output(void *logctx, FFLCEVCFrame *frame_ctx, AVFrame *out)
         return AVERROR_EXTERNAL;
 
     res = LCEVC_GetPictureDesc(lcevc->decoder, picture, &desc);
-    if (res != LCEVC_Success)
+    if (res != LCEVC_Success) {
+        LCEVC_FreePicture(lcevc->decoder, picture);
         return AVERROR_EXTERNAL;
+    }
 
     out->crop_top = desc.cropTop;
     out->crop_bottom = desc.cropBottom;
-- 
2.49.1


>From 8a825fe4155fde5348a5b74ad4559c7160bf165a Mon Sep 17 00:00:00 2001
From: James Almer <jamrial@gmail.com>
Date: Wed, 7 Jan 2026 12:16:27 -0300
Subject: [PATCH 7/8] avcodec/lcevc: attach a reference to the source frame to
 each passed in base picture

This way we can ensure a frame reference will always exists for as long as the
external library needs the base picture.

Signed-off-by: James Almer <jamrial@gmail.com>
(cherry picked from commit 188521c7ad9e2a82d913770c3102593ebcd3454d)
---
 libavcodec/lcevcdec.c | 59 +++++++++++++++++++++++++++++++------------
 1 file changed, 43 insertions(+), 16 deletions(-)

diff --git a/libavcodec/lcevcdec.c b/libavcodec/lcevcdec.c
index 97c1dfc5b7..010a7bc791 100644
--- a/libavcodec/lcevcdec.c
+++ b/libavcodec/lcevcdec.c
@@ -110,6 +110,7 @@ static int lcevc_send_frame(void *logctx, FFLCEVCFrame *frame_ctx, const AVFrame
 {
     FFLCEVCContext *lcevc = frame_ctx->lcevc;
     const AVFrameSideData *sd = av_frame_get_side_data(in, AV_FRAME_DATA_LCEVC);
+    AVFrame *opaque;
     LCEVC_PictureHandle picture;
     LCEVC_ReturnCode res;
     int ret = 0;
@@ -129,13 +130,27 @@ static int lcevc_send_frame(void *logctx, FFLCEVCFrame *frame_ctx, const AVFrame
     if (ret < 0)
         return ret;
 
+    opaque = av_frame_clone(in);
+    if (!opaque) {
+        LCEVC_FreePicture(lcevc->decoder, picture);
+        return AVERROR(ENOMEM);
+    }
+
+    res = LCEVC_SetPictureUserData(lcevc->decoder, picture, opaque);
+    if (res != LCEVC_Success) {
+        LCEVC_FreePicture(lcevc->decoder, picture);
+        av_frame_free(&opaque);
+        return AVERROR_EXTERNAL;
+    }
+
 #ifdef LCEVC_DEC_VERSION_MAJOR
-    res = LCEVC_SendDecoderBase(lcevc->decoder, in->pts, picture, -1, NULL);
+    res = LCEVC_SendDecoderBase(lcevc->decoder, in->pts, picture, -1, opaque);
 #else
-    res = LCEVC_SendDecoderBase(lcevc->decoder, in->pts, 0, picture, -1, NULL);
+    res = LCEVC_SendDecoderBase(lcevc->decoder, in->pts, 0, picture, -1, opaque);
 #endif
     if (res != LCEVC_Success) {
         LCEVC_FreePicture(lcevc->decoder, picture);
+        av_frame_free(&opaque);
         return AVERROR_EXTERNAL;
     }
 
@@ -171,17 +186,16 @@ static int generate_output(void *logctx, FFLCEVCFrame *frame_ctx, AVFrame *out)
         return AVERROR_EXTERNAL;
     }
 
+    av_frame_unref(out);
+    av_frame_copy_props(frame_ctx->frame, (AVFrame *)info.baseUserData);
+    av_frame_move_ref(out, frame_ctx->frame);
+
     out->crop_top = desc.cropTop;
     out->crop_bottom = desc.cropBottom;
     out->crop_left = desc.cropLeft;
     out->crop_right = desc.cropRight;
     out->sample_aspect_ratio.num = desc.sampleAspectRatioNum;
     out->sample_aspect_ratio.den = desc.sampleAspectRatioDen;
-
-    av_frame_copy_props(frame_ctx->frame, out);
-    av_frame_unref(out);
-    av_frame_move_ref(out, frame_ctx->frame);
-
     out->width = desc.width + out->crop_left + out->crop_right;
     out->height = desc.height + out->crop_top + out->crop_bottom;
 
@@ -192,18 +206,13 @@ static int generate_output(void *logctx, FFLCEVCFrame *frame_ctx, AVFrame *out)
     return 0;
 }
 
-static int lcevc_receive_frame(void *logctx, FFLCEVCFrame *frame_ctx, AVFrame *out)
+static int lcevc_flush_pictures(FFLCEVCContext *lcevc)
 {
-    FFLCEVCContext *lcevc = frame_ctx->lcevc;
     LCEVC_PictureHandle picture;
     LCEVC_ReturnCode res;
-    int ret;
-
-    ret = generate_output(logctx, frame_ctx, out);
-    if (ret < 0)
-        return ret;
 
     while (1) {
+        AVFrame *base = NULL;
         res = LCEVC_ReceiveDecoderBase (lcevc->decoder, &picture);
         if (res != LCEVC_Success && res != LCEVC_Again)
             return AVERROR_EXTERNAL;
@@ -211,6 +220,9 @@ static int lcevc_receive_frame(void *logctx, FFLCEVCFrame *frame_ctx, AVFrame *o
         if (res == LCEVC_Again)
             break;
 
+        LCEVC_GetPictureUserData(lcevc->decoder, picture, (void **)&base);
+        av_frame_free(&base);
+
         res = LCEVC_FreePicture(lcevc->decoder, picture);
         if (res != LCEVC_Success)
             return AVERROR_EXTERNAL;
@@ -219,6 +231,18 @@ static int lcevc_receive_frame(void *logctx, FFLCEVCFrame *frame_ctx, AVFrame *o
     return 0;
 }
 
+static int lcevc_receive_frame(void *logctx, FFLCEVCFrame *frame_ctx, AVFrame *out)
+{
+    FFLCEVCContext *lcevc = frame_ctx->lcevc;
+    int ret;
+
+    ret = generate_output(logctx, frame_ctx, out);
+    if (ret < 0)
+        return ret;
+
+    return lcevc_flush_pictures(lcevc);
+}
+
 static void event_callback(LCEVC_DecoderHandle dec, LCEVC_Event event,
     LCEVC_PictureHandle pic, const LCEVC_DecodeInformation *info,
     const uint8_t *data, uint32_t size, void *logctx)
@@ -235,8 +259,11 @@ static void event_callback(LCEVC_DecoderHandle dec, LCEVC_Event event,
 static void lcevc_free(AVRefStructOpaque unused, void *obj)
 {
     FFLCEVCContext *lcevc = obj;
-    if (lcevc->initialized)
+    if (lcevc->initialized) {
+        LCEVC_FlushDecoder(lcevc->decoder);
+        lcevc_flush_pictures(lcevc);
         LCEVC_DestroyDecoder(lcevc->decoder);
+    }
     memset(lcevc, 0, sizeof(*lcevc));
 }
 
@@ -285,7 +312,7 @@ int ff_lcevc_process(void *logctx, AVFrame *frame)
     if (ret)
         return ret < 0 ? ret : 0;
 
-    lcevc_receive_frame(logctx, frame_ctx, frame);
+    ret = lcevc_receive_frame(logctx, frame_ctx, frame);
     if (ret < 0)
         return ret;
 
-- 
2.49.1


>From 0592be14ff099604ec2235e05f4b6b0c3b92c7cf Mon Sep 17 00:00:00 2001
From: James Almer <jamrial@gmail.com>
Date: Sat, 10 Jan 2026 15:48:46 -0300
Subject: [PATCH 8/8] avfilter/vf_lcevc: attach a reference to the source frame
 to each passed in base picture

And free them once they are guaranteed to be no longer needed, instead of freeing them
when returned with an enhanced output.

Signed-off-by: James Almer <jamrial@gmail.com>
(cherry picked from commit af136db1c3c5d72c4a71cedaf7b06d2f8ae3819e)
---
 libavfilter/vf_lcevc.c | 17 ++++++++++++++---
 1 file changed, 14 insertions(+), 3 deletions(-)

diff --git a/libavfilter/vf_lcevc.c b/libavfilter/vf_lcevc.c
index 74875e1b84..203b7e2ab9 100644
--- a/libavfilter/vf_lcevc.c
+++ b/libavfilter/vf_lcevc.c
@@ -152,6 +152,13 @@ static int send_frame(AVFilterLink *inlink, AVFrame *in)
         }
     }
 
+    res = LCEVC_SetPictureUserData(lcevc->decoder, picture, in);
+    if (res != LCEVC_Success) {
+        av_log(ctx, AV_LOG_ERROR, "LCEVC_SetPictureUserData failed\n");
+        LCEVC_FreePicture(lcevc->decoder, picture);
+        return AVERROR_EXTERNAL;
+    }
+
 #ifdef LCEVC_DEC_VERSION_MAJOR
     res = LCEVC_SendDecoderBase(lcevc->decoder, in->pts, picture, -1, in);
 #else
@@ -223,8 +230,6 @@ static int generate_output(AVFilterLink *inlink, AVFrame *out)
     av_frame_copy_props(out, (AVFrame *)info.baseUserData);
     av_frame_remove_side_data(out, AV_FRAME_DATA_LCEVC);
 
-    av_frame_free((AVFrame **)&info.baseUserData);
-
     res = LCEVC_GetPictureDesc(lcevc->decoder, picture, &desc);
     LCEVC_FreePicture(lcevc->decoder, picture);
 
@@ -292,8 +297,12 @@ static void flush_bases(AVFilterContext *ctx)
     LCEVCContext *lcevc = ctx->priv;
     LCEVC_PictureHandle picture;
 
-    while (LCEVC_ReceiveDecoderBase(lcevc->decoder, &picture) == LCEVC_Success)
+    while (LCEVC_ReceiveDecoderBase(lcevc->decoder, &picture) == LCEVC_Success) {
+        AVFrame *base = NULL;
+        LCEVC_GetPictureUserData(lcevc->decoder, picture, (void **)&base);
         LCEVC_FreePicture(lcevc->decoder, picture);
+        av_frame_free(&base);
+    }
 }
 
 static int activate(AVFilterContext *ctx)
@@ -407,6 +416,8 @@ static av_cold void uninit(AVFilterContext *ctx)
 {
     LCEVCContext *lcevc = ctx->priv;
 
+    LCEVC_FlushDecoder(lcevc->decoder);
+    flush_bases(ctx);
     LCEVC_DestroyDecoder(lcevc->decoder);
 }
 
-- 
2.49.1

_______________________________________________
ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org
To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2026-01-11 23:42 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-01-11 23:42 [FFmpeg-devel] [PR] [release/8.0] backport LCEVC fixes (PR #21433) James Almer via ffmpeg-devel

Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

This inbox may be cloned and mirrored by anyone:

	git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
		ffmpegdev@gitmailbox.com
	public-inbox-index ffmpegdev

Example config snippet for mirrors.


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git