PR #24843 opened by James Almer (jamrial) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24843 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24843.patch
Alternative to !23999, without moving the LCEVC decoding implementation to a handful of decoders. >From ca1e4e39e23488a7d0272cd48f6da42b73406c5f Mon Sep 17 00:00:00 2001 From: James Almer <[email protected]> Date: Thu, 1 Oct 2026 17:03:53 -0300 Subject: [PATCH 1/2] avcodec: allow adding AVOption enabled butt non-public fields to AVCodecContext Making AVCodecInternal fields AVOption enabled requires extra considerations in av_codec_context_class, and they will only be settable with an open context or by passing the options directly as part of avcodec_open2(). They also would require to allow searching children classes, which in the case of a fake object results in looking at every single underlying decoder/encoder option. Instead of that, extend AVCodecContext internally the same way we did for other contexts like AVFormatContext, and add fields outside the public part. Signed-off-by: James Almer <[email protected]> --- libavcodec/avcodec_internal.h | 13 ++++++++++++- libavcodec/options.c | 8 ++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/libavcodec/avcodec_internal.h b/libavcodec/avcodec_internal.h index 06645e91a0..ebd9458120 100644 --- a/libavcodec/avcodec_internal.h +++ b/libavcodec/avcodec_internal.h @@ -27,9 +27,20 @@ #include "libavutil/frame.h" +#include "avcodec.h" #include "packet.h" -struct AVCodecContext; +typedef struct FFCodecContext { + /** + * The public context. + */ + AVCodecContext pub; +} FFCodecContext; + +static av_always_inline FFCodecContext *ffcodeccontext(AVCodecContext *avctx) +{ + return (FFCodecContext*)avctx; +} typedef struct SideDataMap { enum AVPacketSideDataType packet; diff --git a/libavcodec/options.c b/libavcodec/options.c index 834beb5757..d85137a99e 100644 --- a/libavcodec/options.c +++ b/libavcodec/options.c @@ -148,11 +148,15 @@ static int init_context_defaults(AVCodecContext *s, const AVCodec *codec) AVCodecContext *avcodec_alloc_context3(const AVCodec *codec) { - AVCodecContext *avctx= av_malloc(sizeof(AVCodecContext)); + FFCodecContext *ffavc; + AVCodecContext *avctx; - if (!avctx) + ffavc = av_mallocz(sizeof(*ffavc)); + if (!ffavc) return NULL; + avctx = &ffavc->pub; + if (init_context_defaults(avctx, codec) < 0) { av_free(avctx); return NULL; -- 2.52.0 >From 422ae94e6c740bcacf82064dd06ae85af55ac8a2 Mon Sep 17 00:00:00 2001 From: James Almer <[email protected]> Date: Thu, 1 Oct 2026 17:35:23 -0300 Subject: [PATCH 2/2] avcodec/lcevcdec: add support for passing options Signed-off-by: James Almer <[email protected]> --- libavcodec/avcodec_internal.h | 2 + libavcodec/decode.c | 4 +- libavcodec/lcevcdec.c | 81 ++++++++++++++++++++++++++++++++--- libavcodec/lcevcdec.h | 9 +++- libavcodec/options_table.h | 6 +++ 5 files changed, 94 insertions(+), 8 deletions(-) diff --git a/libavcodec/avcodec_internal.h b/libavcodec/avcodec_internal.h index ebd9458120..d009138665 100644 --- a/libavcodec/avcodec_internal.h +++ b/libavcodec/avcodec_internal.h @@ -35,6 +35,8 @@ typedef struct FFCodecContext { * The public context. */ AVCodecContext pub; + + struct AVDictionary *lcevc_params; } FFCodecContext; static av_always_inline FFCodecContext *ffcodeccontext(AVCodecContext *avctx) diff --git a/libavcodec/decode.c b/libavcodec/decode.c index 94dfc06a84..627ad9420a 100644 --- a/libavcodec/decode.c +++ b/libavcodec/decode.c @@ -2152,7 +2152,9 @@ 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, av_log_get_level() + avctx->log_level_offset); + FFCodecContext *ffavc = ffcodeccontext(avctx); + ret = ff_lcevc_alloc(&dc->lcevc.ctx, &ffavc->lcevc_params, + av_log_get_level() + avctx->log_level_offset); if (ret < 0 && (avctx->err_recognition & AV_EF_EXPLODE)) return ret; #endif diff --git a/libavcodec/lcevcdec.c b/libavcodec/lcevcdec.c index d62adfba13..c7872b5016 100644 --- a/libavcodec/lcevcdec.c +++ b/libavcodec/lcevcdec.c @@ -21,6 +21,7 @@ #include "libavutil/imgutils.h" #include "libavutil/log.h" #include "libavutil/mem.h" +#include "libavutil/opt.h" #include "libavutil/refstruct.h" #include "cbs.h" @@ -380,6 +381,7 @@ static int lcevc_init(FFLCEVCContext *lcevc) { LCEVC_AccelContextHandle dummy = { 0 }; const int32_t event = LCEVC_Log; + LCEVC_ReturnCode res; int level; if (LCEVC_CreateDecoder(&lcevc->decoder, dummy) != LCEVC_Success) { @@ -387,9 +389,42 @@ static int lcevc_init(FFLCEVCContext *lcevc) return AVERROR_EXTERNAL; } + if (lcevc->pipeline) { + res = LCEVC_ConfigureDecoderString(lcevc->decoder, "pipeline", lcevc->pipeline); + if (res != LCEVC_Success) { + av_log(lcevc, AV_LOG_ERROR, "Failed to set pipeline to \"%s\"\n", lcevc->pipeline); + LCEVC_DestroyDecoder(lcevc->decoder); + return AVERROR_EXTERNAL; + } + } + level = get_log_level(lcevc->loglevel); LCEVC_ConfigureDecoderInt(lcevc->decoder, "log_level", level); + res = LCEVC_ConfigureDecoderInt(lcevc->decoder, "threads", lcevc->threads); + if (res != LCEVC_Success) { + av_log(lcevc, AV_LOG_ERROR, "Failed to set threads to %d\n", lcevc->threads); + LCEVC_DestroyDecoder(lcevc->decoder); + return AVERROR_EXTERNAL; + } + res = LCEVC_ConfigureDecoderInt(lcevc->decoder, "passthrough_mode", lcevc->passthrough_mode); + if (res != LCEVC_Success) { + av_log(lcevc, AV_LOG_ERROR, "Failed to set passthrough_mode to %d\n", lcevc->passthrough_mode); + LCEVC_DestroyDecoder(lcevc->decoder); + return AVERROR_EXTERNAL; + } + res = LCEVC_ConfigureDecoderBool(lcevc->decoder, "allow_dithering", lcevc->allow_dithering); + if (res != LCEVC_Success) { + av_log(lcevc, AV_LOG_ERROR, "Failed to set allow_dithering to %d\n", lcevc->allow_dithering); + LCEVC_DestroyDecoder(lcevc->decoder); + return AVERROR_EXTERNAL; + } + res = LCEVC_ConfigureDecoderBool(lcevc->decoder, "highlight_residuals", lcevc->highlight_residuals); + if (res != LCEVC_Success) { + av_log(lcevc, AV_LOG_ERROR, "Failed to set highlight_residuals to %d\n", lcevc->highlight_residuals); + LCEVC_DestroyDecoder(lcevc->decoder); + return AVERROR_EXTERNAL; + } LCEVC_ConfigureDecoderIntArray(lcevc->decoder, "events", 1, &event); LCEVC_SetDecoderEventCallback(lcevc->decoder, event_callback, lcevc); @@ -477,22 +512,61 @@ static const CodedBitstreamUnitType decompose_unit_types[] = { LCEVC_NON_IDR_NUT, }; +#define OFFSET(x) offsetof(FFLCEVCContext, x) +#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_DECODING_PARAM) +static const AVOption lcevc_opts[] = { + { "passthrough_mode", "Set passthrough mode", OFFSET(passthrough_mode), AV_OPT_TYPE_INT, + { .i64 = 0 }, -1, 2, FLAGS, .unit = "passthrough_mode" }, + { "disable", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = -1 }, .unit = "passthrough_mode" }, + { "allow", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, .unit = "passthrough_mode" }, + { "force", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, .unit = "passthrough_mode" }, + { "scale", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 2 }, .unit = "passthrough_mode" }, + { "allow_dithering", "Allow dithering", OFFSET(allow_dithering), AV_OPT_TYPE_BOOL, + { .i64 = 1 }, 0, 1, FLAGS }, + { "highlight_residuals", "Highlight residuals", OFFSET(highlight_residuals), AV_OPT_TYPE_BOOL, + { .i64 = 0 }, 0, 1, FLAGS }, + { "threads", "Thread count", OFFSET(threads), AV_OPT_TYPE_INT, + { .i64 = 1 }, 0, INT_MAX, FLAGS }, + { "pipeline", "Decoding pipeline", OFFSET(pipeline), AV_OPT_TYPE_STRING, + { .str = NULL }, 0, 0, FLAGS }, + { NULL }, +}; +#undef OFFSET +#undef FLAGS static const AVClass lcevcdec_context_class = { .class_name = "liblcevc_dec", .item_name = av_default_item_name, + .option = lcevc_opts, .version = LIBAVUTIL_VERSION_INT, .category = AV_CLASS_CATEGORY_DECODER, }; -int ff_lcevc_alloc(FFLCEVCContext **plcevc, int loglevel) +int ff_lcevc_alloc(FFLCEVCContext **plcevc, AVDictionary **options, int loglevel) { FFLCEVCContext *lcevc = NULL; + const AVDictionaryEntry *t; int ret; lcevc = av_refstruct_alloc_ext(sizeof(*lcevc), 0, NULL, lcevc_free); - if (!lcevc) + if (!lcevc) { + av_dict_free(options); return AVERROR(ENOMEM); + } + + lcevc->class = &lcevcdec_context_class; + av_opt_set_defaults(lcevc); + + lcevc->loglevel = loglevel; + + ret = av_opt_set_dict2(lcevc, options, 0); + if (ret < 0) + return ret; + + if (t = av_dict_iterate(*options, NULL)) { + av_log(NULL, AV_LOG_FATAL, "liblcevc-dec option %s not found.\n", t->key); + return AVERROR_OPTION_NOT_FOUND; + } lcevc->frag = av_mallocz(sizeof(*lcevc->frag)); if (!lcevc->frag) { @@ -516,9 +590,6 @@ int ff_lcevc_alloc(FFLCEVCContext **plcevc, int loglevel) goto fail; } - lcevc->class = &lcevcdec_context_class; - lcevc->loglevel = loglevel; - *plcevc = lcevc; return 0; fail: diff --git a/libavcodec/lcevcdec.h b/libavcodec/lcevcdec.h index b53547f112..b2132123ea 100644 --- a/libavcodec/lcevcdec.h +++ b/libavcodec/lcevcdec.h @@ -22,7 +22,7 @@ #include "config.h" #include "libavutil/log.h" -#include "libavutil/pixfmt.h" +#include "libavutil/dict.h" #include <stdint.h> #if CONFIG_LIBLCEVC_DEC @@ -42,6 +42,11 @@ typedef struct FFLCEVCContext { struct AVRefStructPool *frame_pool; ///< pool of FFLCEVCFrame int64_t last_pts; int loglevel; + int threads; + int passthrough_mode; + int allow_dithering; + int highlight_residuals; + uint8_t *pipeline; int initialized; } FFLCEVCContext; @@ -52,7 +57,7 @@ typedef struct FFLCEVCFrame { struct AVFrame *frame; } FFLCEVCFrame; -int ff_lcevc_alloc(FFLCEVCContext **plcevc, int loglevel); +int ff_lcevc_alloc(FFLCEVCContext **plcevc, AVDictionary **options, int loglevel); int ff_lcevc_process(void *logctx, struct AVFrame *frame); int ff_lcevc_parse_frame(FFLCEVCContext *lcevc, const struct AVFrame *frame, enum AVPixelFormat *format, int *width, int *height); diff --git a/libavcodec/options_table.h b/libavcodec/options_table.h index 100095b24c..bdda81aae5 100644 --- a/libavcodec/options_table.h +++ b/libavcodec/options_table.h @@ -30,8 +30,10 @@ #include "libavutil/opt.h" #include "avcodec.h" +#include "avcodec_internal.h" #include "version_major.h" +#define FFOFFSET(x) offsetof(FFCodecContext,x) #define OFFSET(x) offsetof(AVCodecContext,x) #define DEFAULT 0 //should be NAN but it does not work as it is not a constant in glibc as required by ANSI/ISO C //these names are too long to be readable @@ -420,6 +422,9 @@ static const AVOption avcodec_options[] = { {"content_light_level", .default_val.i64 = AV_PKT_DATA_CONTENT_LIGHT_LEVEL, .type = AV_OPT_TYPE_CONST, .flags = A|D, .unit = "side_data_pkt" }, {"icc_profile", .default_val.i64 = AV_PKT_DATA_ICC_PROFILE, .type = AV_OPT_TYPE_CONST, .flags = A|D, .unit = "side_data_pkt" }, {"exif", .default_val.i64 = AV_PKT_DATA_EXIF, .type = AV_OPT_TYPE_CONST, .flags = A|D, .unit = "side_data_pkt" }, + +// FFCodecContext options +{ "lcevc-dec-params", "Configure liblcevc-dec using a :-separated list of key=value parameters", FFOFFSET(lcevc_params), AV_OPT_TYPE_DICT, { 0 }, 0, 0, V|D }, {NULL}, }; @@ -431,5 +436,6 @@ static const AVOption avcodec_options[] = { #undef CC #undef DEFAULT #undef OFFSET +#undef FFOFFSET #endif /* AVCODEC_OPTIONS_TABLE_H */ -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
