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]
