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

Not only do some sources not provide an aspect ratio, as is the case of 
MPEG-TS, but also some enhanced streams have no change in dimensions, and this 
heuristic would generate bogus values.
Instead, we need to parse the LCEVC bitstream for a Global Config process block 
in order to get the actual dimensions.

This does add a little bit of overhead, but it can'be avoided.


>From 1e12a8465a8ae3c8fec4e2c4b3d049f160527455 Mon Sep 17 00:00:00 2001
From: James Almer <[email protected]>
Date: Mon, 9 Mar 2026 20:19:33 -0300
Subject: [PATCH 1/3] avcodec/cbs_lcevc: don't look for process blocks if the
 unit was not decomposed

Signed-off-by: James Almer <[email protected]>
---
 libavcodec/cbs_lcevc.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libavcodec/cbs_lcevc.c b/libavcodec/cbs_lcevc.c
index 11b47199b6..62fbacab1c 100644
--- a/libavcodec/cbs_lcevc.c
+++ b/libavcodec/cbs_lcevc.c
@@ -674,6 +674,9 @@ int ff_cbs_lcevc_find_process_block(CodedBitstreamContext 
*ctx,
         CodedBitstreamUnit *unit = &au->units[i];
         LCEVCRawProcessBlockList *list;
 
+        if (!unit->content)
+            continue;
+
         err = cbs_lcevc_get_process_block_list(ctx, unit, &list);
         if (err < 0)
             continue;
-- 
2.52.0


>From 565de9bd3fee8878e09c31e42659fb70c48f02a9 Mon Sep 17 00:00:00 2001
From: James Almer <[email protected]>
Date: Mon, 9 Mar 2026 20:20:53 -0300
Subject: [PATCH 2/3] avcodec/lcevc_parser: move the resolution type table to a
 header

Will be useful in the following commit.

Signed-off-by: James Almer <[email protected]>
---
 libavcodec/lcevc_parse.h  | 19 +++++++++++++++++++
 libavcodec/lcevc_parser.c | 19 -------------------
 2 files changed, 19 insertions(+), 19 deletions(-)

diff --git a/libavcodec/lcevc_parse.h b/libavcodec/lcevc_parse.h
index f56758a1a5..cd9cd212b2 100644
--- a/libavcodec/lcevc_parse.h
+++ b/libavcodec/lcevc_parse.h
@@ -23,6 +23,25 @@
 
 #include "get_bits.h"
 
+static const struct {
+    int width;
+    int height;
+} resolution_type_lut[63] = {
+    { 0, 0},
+    { 360,  200 },  { 400,  240 },  { 480,  320 },  { 640,  360 },
+    { 640,  480 },  { 768,  480 },  { 800,  600 },  { 852,  480 },
+    { 854,  480 },  { 856,  480 },  { 960,  540 },  { 960,  640 },
+    { 1024, 576 },  { 1024, 600 },  { 1024, 768 },  { 1152, 864 },
+    { 1280, 720 },  { 1280, 800 },  { 1280, 1024 }, { 1360, 768 },
+    { 1366, 768 },  { 1920, 1200 }, { 2048, 1080 }, { 2048, 1152 },
+    { 2048, 1536 }, { 2160, 1440 }, { 2560, 1440 }, { 2560, 1600 },
+    { 2560, 2048 }, { 3200, 1800 }, { 3200, 2048 }, { 3200, 2400 },
+    { 3440, 1440 }, { 3840, 1600 }, { 3840, 2160 }, { 3840, 2400 },
+    { 4096, 2160 }, { 4096, 3072 }, { 5120, 2880 }, { 5120, 3200 },
+    { 5120, 4096 }, { 6400, 4096 }, { 6400, 4800 }, { 7680, 4320 },
+    { 7680, 4800 },
+};
+
 static inline uint64_t get_mb(GetBitContext *s) {
     int more, i = 0;
     uint64_t mb = 0;
diff --git a/libavcodec/lcevc_parser.c b/libavcodec/lcevc_parser.c
index f3338c4659..0d7023cdf1 100644
--- a/libavcodec/lcevc_parser.c
+++ b/libavcodec/lcevc_parser.c
@@ -82,25 +82,6 @@ static const enum AVPixelFormat pix_fmts[4][4] = {
       AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV444P14, },
 };
 
-static const struct {
-    int width;
-    int height;
-} resolution_type_lut[63] = {
-    { 0, 0},
-    { 360,  200 },  { 400,  240 },  { 480,  320 },  { 640,  360 },
-    { 640,  480 },  { 768,  480 },  { 800,  600 },  { 852,  480 },
-    { 854,  480 },  { 856,  480 },  { 960,  540 },  { 960,  640 },
-    { 1024, 576 },  { 1024, 600 },  { 1024, 768 },  { 1152, 864 },
-    { 1280, 720 },  { 1280, 800 },  { 1280, 1024 }, { 1360, 768 },
-    { 1366, 768 },  { 1920, 1200 }, { 2048, 1080 }, { 2048, 1152 },
-    { 2048, 1536 }, { 2160, 1440 }, { 2560, 1440 }, { 2560, 1600 },
-    { 2560, 2048 }, { 3200, 1800 }, { 3200, 2048 }, { 3200, 2400 },
-    { 3440, 1440 }, { 3840, 1600 }, { 3840, 2160 }, { 3840, 2400 },
-    { 4096, 2160 }, { 4096, 3072 }, { 5120, 2880 }, { 5120, 3200 },
-    { 5120, 4096 }, { 6400, 4096 }, { 6400, 4800 }, { 7680, 4320 },
-    { 7680, 4800 },
-};
-
 static int parse_nal_unit(AVCodecParserContext *s, AVCodecContext *avctx,
                           const H2645NAL *nal)
 {
-- 
2.52.0


>From af4d2973ae5775d4f566a92b42a10a026a640ff3 Mon Sep 17 00:00:00 2001
From: James Almer <[email protected]>
Date: Mon, 9 Mar 2026 20:22:27 -0300
Subject: [PATCH 3/3] avcoec/lcevcdec: don't try to derive final dimensions
 from SAR

Not only do some sources not provide an aspect ratio, as is the case of
MPEG-TS, but also some enhanced streams have no change in dimensions, and this
heuristic would generate bugus values.
Instead, we need to parse the LCEVC bitstream for a Global Config process block
in order to get the actual dimensions. This add a little overhead, but it can't
be avoided.

Signed-off-by: James Almer <[email protected]>
---
 libavcodec/decode.c        | 31 +++++++++------
 libavcodec/lcevcdec.c      | 78 +++++++++++++++++++++++++++++++++++++-
 libavcodec/lcevcdec.h      |  9 ++++-
 libavcodec/pthread_frame.c |  1 +
 4 files changed, 105 insertions(+), 14 deletions(-)

diff --git a/libavcodec/decode.c b/libavcodec/decode.c
index ca1adfa386..8403b8fc9e 100644
--- a/libavcodec/decode.c
+++ b/libavcodec/decode.c
@@ -1661,7 +1661,7 @@ int ff_attach_decode_data(AVFrame *frame)
     return 0;
 }
 
-static void update_frame_props(AVCodecContext *avctx, AVFrame *frame)
+static int update_frame_props(AVCodecContext *avctx, AVFrame *frame)
 {
 #if CONFIG_LIBLCEVC_DEC
     AVCodecInternal    *avci = avctx->internal;
@@ -1671,12 +1671,13 @@ static void update_frame_props(AVCodecContext *avctx, 
AVFrame *frame)
                       av_frame_get_side_data(frame, AV_FRAME_DATA_LCEVC);
 
     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);
+        int ret = ff_lcevc_parse_frame(dc->lcevc.ctx, frame,
+                                       &dc->lcevc.width, &dc->lcevc.height, 
avctx);
+        if (ret < 0)
+            return ret;
     }
 #endif
+    return 0;
 }
 
 static int attach_post_process_data(AVCodecContext *avctx, AVFrame *frame)
@@ -1690,6 +1691,11 @@ static int attach_post_process_data(AVCodecContext 
*avctx, AVFrame *frame)
         FFLCEVCFrame *frame_ctx;
         int ret;
 
+        if (!dc->lcevc.width || !dc->lcevc.height) {
+            dc->lcevc.frame = 0;
+            return 0;
+        }
+
         frame_ctx = av_mallocz(sizeof(*frame_ctx));
         if (!frame_ctx)
             return AVERROR(ENOMEM);
@@ -1701,13 +1707,10 @@ static int attach_post_process_data(AVCodecContext 
*avctx, AVFrame *frame)
         }
 
         frame_ctx->lcevc = av_refstruct_ref(dc->lcevc.ctx);
-        frame_ctx->frame->width  = frame->width;
-        frame_ctx->frame->height = frame->height;
+        frame_ctx->frame->width  = dc->lcevc.width;
+        frame_ctx->frame->height = dc->lcevc.height;
         frame_ctx->frame->format = frame->format;
 
-        frame->width  = dc->lcevc.width;
-        frame->height = dc->lcevc.height;
-
         ret = avctx->get_buffer2(avctx, frame_ctx->frame, 0);
         if (ret < 0) {
             ff_lcevc_unref(frame_ctx);
@@ -1771,7 +1774,9 @@ int ff_get_buffer(AVCodecContext *avctx, AVFrame *frame, 
int flags)
         }
     } else {
         avctx->sw_pix_fmt = avctx->pix_fmt;
-        update_frame_props(avctx, frame);
+        ret = update_frame_props(avctx, frame);
+        if (ret < 0)
+            goto fail;
     }
 
     ret = avctx->get_buffer2(avctx, frame, flags);
@@ -2091,7 +2096,7 @@ av_cold 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);
+            ret = ff_lcevc_alloc(&dc->lcevc.ctx, avctx);
             if (ret < 0 && (avctx->err_recognition & AV_EF_EXPLODE))
                 return ret;
 #endif
@@ -2338,6 +2343,8 @@ av_cold void ff_decode_internal_sync(AVCodecContext *dst, 
const AVCodecContext *
     dst_dc->side_data_pref_mask = src_dc->side_data_pref_mask;
 #if CONFIG_LIBLCEVC_DEC
     av_refstruct_replace(&dst_dc->lcevc.ctx, src_dc->lcevc.ctx);
+    dst_dc->lcevc.width = src_dc->lcevc.width;
+    dst_dc->lcevc.height = src_dc->lcevc.height;
 #endif
 }
 
diff --git a/libavcodec/lcevcdec.c b/libavcodec/lcevcdec.c
index 036b700775..d8de2e47ec 100644
--- a/libavcodec/lcevcdec.c
+++ b/libavcodec/lcevcdec.c
@@ -23,7 +23,10 @@
 #include "libavutil/mem.h"
 #include "libavutil/refstruct.h"
 
+#include "cbs.h"
+#include "cbs_lcevc.h"
 #include "decode.h"
+#include "lcevc_parse.h"
 #include "lcevcdec.h"
 
 static LCEVC_ColorFormat map_format(int format)
@@ -191,6 +194,15 @@ static int generate_output(void *logctx, FFLCEVCFrame 
*frame_ctx, AVFrame *out)
     out->width = desc.width + out->crop_left + out->crop_right;
     out->height = desc.height + out->crop_top + out->crop_bottom;
 
+    av_log(logctx, AV_LOG_DEBUG, "out PTS %"PRId64", %dx%d, "
+                                 "%zu/%zu/%zu/%zu, "
+                                 "SAR %d:%d, "
+                                 "hasEnhancement %d, enhanced %d\n",
+           out->pts, out->width, out->height,
+           out->crop_top, out->crop_bottom, out->crop_left, out->crop_right,
+           out->sample_aspect_ratio.num, out->sample_aspect_ratio.den,
+           info.hasEnhancement, info.enhanced);
+
     res = LCEVC_FreePicture(lcevc->decoder, picture);
     if (res != LCEVC_Success)
         return AVERROR_EXTERNAL;
@@ -256,6 +268,10 @@ static void lcevc_free(AVRefStructOpaque unused, void *obj)
         lcevc_flush_pictures(lcevc);
         LCEVC_DestroyDecoder(lcevc->decoder);
     }
+    if (lcevc->frag)
+        ff_cbs_fragment_free(lcevc->frag);
+    av_freep(&lcevc->frag);
+    ff_cbs_close(&lcevc->cbc);
     memset(lcevc, 0, sizeof(*lcevc));
 }
 
@@ -313,14 +329,74 @@ int ff_lcevc_process(void *logctx, AVFrame *frame)
     return 0;
 }
 
-int ff_lcevc_alloc(FFLCEVCContext **plcevc)
+int ff_lcevc_parse_frame(FFLCEVCContext *lcevc, const AVFrame *frame,
+                         int *width, int *height, void *logctx)
+{
+    LCEVCRawProcessBlock *block = NULL;
+    LCEVCRawGlobalConfig *gc = NULL;
+    AVFrameSideData *sd = av_frame_get_side_data(frame, AV_FRAME_DATA_LCEVC);
+    int ret;
+
+    ret = ff_cbs_read(lcevc->cbc, lcevc->frag, sd->buf, sd->data, sd->size);
+    if (ret < 0) {
+        av_log(logctx, AV_LOG_ERROR, "Failed to parse Access Unit.\n");
+        goto end;
+    }
+
+    ret = ff_cbs_lcevc_find_process_block(lcevc->cbc, lcevc->frag,
+                                          LCEVC_PAYLOAD_TYPE_GLOBAL_CONFIG, 
&block);
+    if (ret < 0) {
+        ret = 0;
+        goto end;
+    }
+
+    gc = block->payload;
+    if (gc->resolution_type < 63) {
+        *width  = resolution_type_lut[gc->resolution_type].width;
+        *height = resolution_type_lut[gc->resolution_type].height;
+    } else {
+        *width  = gc->custom_resolution_width;
+        *height = gc->custom_resolution_height;
+    }
+
+    ret = 0;
+end:
+    ff_cbs_fragment_reset(lcevc->frag);
+
+    return ret;
+}
+
+static const CodedBitstreamUnitType decompose_unit_types[] = {
+    LCEVC_IDR_NUT,
+};
+
+int ff_lcevc_alloc(FFLCEVCContext **plcevc, void *logctx)
 {
     FFLCEVCContext *lcevc = NULL;
+    int ret;
+
     lcevc = av_refstruct_alloc_ext(sizeof(*lcevc), 0, NULL, lcevc_free);
     if (!lcevc)
         return AVERROR(ENOMEM);
+
+    lcevc->frag = av_mallocz(sizeof(*lcevc->frag));
+    if (!lcevc->frag) {
+        ret = AVERROR(ENOMEM);
+        goto fail;
+    }
+
+    ret = ff_cbs_init(&lcevc->cbc, AV_CODEC_ID_LCEVC, logctx);
+    if (ret < 0)
+        goto fail;
+
+    lcevc->cbc->decompose_unit_types    = decompose_unit_types;
+    lcevc->cbc->nb_decompose_unit_types = FF_ARRAY_ELEMS(decompose_unit_types);
+
     *plcevc = lcevc;
     return 0;
+fail:
+    av_refstruct_unref(&lcevc);
+    return ret;
 }
 
 void ff_lcevc_unref(void *opaque)
diff --git a/libavcodec/lcevcdec.h b/libavcodec/lcevcdec.h
index 62014132d9..a65214344d 100644
--- a/libavcodec/lcevcdec.h
+++ b/libavcodec/lcevcdec.h
@@ -28,8 +28,13 @@
 typedef uintptr_t LCEVC_DecoderHandle;
 #endif
 
+struct CodedBitstreamContext;
+struct CodedBitstreamFragment;
+
 typedef struct FFLCEVCContext {
     LCEVC_DecoderHandle decoder;
+    struct CodedBitstreamContext *cbc;
+    struct CodedBitstreamFragment *frag;
     int initialized;
 } FFLCEVCContext;
 
@@ -40,7 +45,9 @@ typedef struct FFLCEVCFrame {
     struct AVFrame *frame;
 } FFLCEVCFrame;
 
-int ff_lcevc_alloc(FFLCEVCContext **plcevc);
+int ff_lcevc_alloc(FFLCEVCContext **plcevc, void *logctx);
 int ff_lcevc_process(void *logctx, struct AVFrame *frame);
+int ff_lcevc_parse_frame(FFLCEVCContext *lcevc, const struct AVFrame *frame,
+                         int *width, int *height, void *logctx);
 void ff_lcevc_unref(void *opaque);
 #endif /* AVCODEC_LCEVCDEC_H */
diff --git a/libavcodec/pthread_frame.c b/libavcodec/pthread_frame.c
index 2115d4204d..c2853086ce 100644
--- a/libavcodec/pthread_frame.c
+++ b/libavcodec/pthread_frame.c
@@ -403,6 +403,7 @@ FF_ENABLE_DEPRECATION_WARNINGS
         dst->hwaccel_flags = src->hwaccel_flags;
 
         av_refstruct_replace(&dst->internal->pool, src->internal->pool);
+        ff_decode_internal_sync(dst, src);
     }
 
     if (for_user) {
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to