PR #22545 opened by James Almer (jamrial) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22545 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22545.patch
>From a2e15f15b0ca877692e398b1452612fa02507c3f 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]> (cherry picked from commit ce7375fc1772671f04b34790e859306400da8690) --- 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 395dc6fa2737c2d8bc248e0dd1d6aec86ac8e51e 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]> (cherry picked from commit c5aa31d2521bb2f3808eb2c2afe018e12359e45c) --- libavcodec/Makefile | 2 +- libavcodec/lcevc_parser.c | 24 +++-------------------- libavcodec/lcevctab.c | 19 ++++++++++++++++++ libavcodec/lcevctab.h | 41 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 64 insertions(+), 22 deletions(-) create mode 100644 libavcodec/lcevctab.c create mode 100644 libavcodec/lcevctab.h diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 12a8265025..1410bd8142 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -1277,7 +1277,7 @@ OBJS-$(CONFIG_IPU_PARSER) += ipu_parser.o OBJS-$(CONFIG_JPEG2000_PARSER) += jpeg2000_parser.o OBJS-$(CONFIG_JPEGXL_PARSER) += jpegxl_parser.o jpegxl_parse.o OBJS-$(CONFIG_JPEGXS_PARSER) += jpegxs_parser.o -OBJS-$(CONFIG_LCEVC_PARSER) += lcevc_parser.o +OBJS-$(CONFIG_LCEVC_PARSER) += lcevc_parser.o lcevctab.o OBJS-$(CONFIG_MISC4_PARSER) += misc4_parser.o OBJS-$(CONFIG_MJPEG_PARSER) += mjpeg_parser.o OBJS-$(CONFIG_MLP_PARSER) += mlp_parse.o mlp_parser.o mlp.o diff --git a/libavcodec/lcevc_parser.c b/libavcodec/lcevc_parser.c index 1de44a8bda..8564fbb528 100644 --- a/libavcodec/lcevc_parser.c +++ b/libavcodec/lcevc_parser.c @@ -26,6 +26,7 @@ #include "h2645_parse.h" #include "lcevc.h" #include "lcevc_parse.h" +#include "lcevctab.h" #include "parser.h" #include "parser_internal.h" @@ -82,25 +83,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) { @@ -152,8 +134,8 @@ static int parse_nal_unit(AVCodecParserContext *s, AVCodecContext *avctx, s->format = pix_fmts[bit_depth][chroma_format_idc]; if (resolution_type < 63) { - s->width = resolution_type_lut[resolution_type].width; - s->height = resolution_type_lut[resolution_type].height; + s->width = ff_lcevc_resolution_type[resolution_type].width; + s->height = ff_lcevc_resolution_type[resolution_type].height; } else { int upsample_type, tile_dimensions_type; int temporal_step_width_modifier_signalled_flag, level1_filtering_signalled_flag; diff --git a/libavcodec/lcevctab.c b/libavcodec/lcevctab.c new file mode 100644 index 0000000000..76853b80e5 --- /dev/null +++ b/libavcodec/lcevctab.c @@ -0,0 +1,19 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "lcevctab.h" diff --git a/libavcodec/lcevctab.h b/libavcodec/lcevctab.h new file mode 100644 index 0000000000..6803589531 --- /dev/null +++ b/libavcodec/lcevctab.h @@ -0,0 +1,41 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_LCEVCTAB_H +#define AVCODEC_LCEVCTAB_H + +static const struct { + int width; + int height; +} ff_lcevc_resolution_type[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 }, +}; + +#endif /* AVCODEC_LCEVCTAB_H */ -- 2.52.0 >From 9abdfda6f2cda9b397078ad4ce72741caf425ad1 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] avcodec/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]> (cherry picked from commit 49d75d81f6753623a3d1f5e8c5c6aa8bf669f744) --- libavcodec/decode.c | 44 +++++++++++++++------ libavcodec/lcevcdec.c | 79 +++++++++++++++++++++++++++++++++++++- libavcodec/lcevcdec.h | 9 ++++- libavcodec/pthread_frame.c | 1 + 4 files changed, 120 insertions(+), 13 deletions(-) diff --git a/libavcodec/decode.c b/libavcodec/decode.c index ca1adfa386..5de5a84038 100644 --- a/libavcodec/decode.c +++ b/libavcodec/decode.c @@ -98,6 +98,8 @@ typedef struct DecodeContext { struct { FFLCEVCContext *ctx; int frame; + int base_width; + int base_height; int width; int height; } lcevc; @@ -1661,7 +1663,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 +1673,23 @@ 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; + + // force get_buffer2() to allocate the base frame using the same dimensions + // as the final enhanced frame, in order to prevent reinitializing the buffer + // pools unnecessarely + if (dc->lcevc.width && dc->lcevc.height) { + dc->lcevc.base_width = frame->width; + dc->lcevc.base_height = frame->height; + frame->width = dc->lcevc.width; + frame->height = dc->lcevc.height; + } } #endif + return 0; } static int attach_post_process_data(AVCodecContext *avctx, AVFrame *frame) @@ -1690,6 +1703,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,12 +1719,12 @@ 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; + frame->width = dc->lcevc.base_width; + frame->height = dc->lcevc.base_height; ret = avctx->get_buffer2(avctx, frame_ctx->frame, 0); if (ret < 0) { @@ -1771,7 +1789,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 +2111,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 +2358,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..69a73866fe 100644 --- a/libavcodec/lcevcdec.c +++ b/libavcodec/lcevcdec.c @@ -23,8 +23,12 @@ #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" +#include "lcevctab.h" static LCEVC_ColorFormat map_format(int format) { @@ -191,6 +195,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 +269,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 +330,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 = ff_lcevc_resolution_type[gc->resolution_type].width; + *height = ff_lcevc_resolution_type[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]
