Commit e0e6ca1e29 for ffmpeg

commit e0e6ca1e29ef3f6b1e9b7d8656fe341eee8cf647
Author: James Almer <jamrial@gmail.com>
Date:   Thu Oct 1 17:35:23 2026 -0300

    avcodec/lcevcdec: add support for passing options

    Signed-off-by: James Almer <jamrial@gmail.com>

diff --git a/doc/codecs.texi b/doc/codecs.texi
index 8f746dbc24..b85f405cf6 100644
--- a/doc/codecs.texi
+++ b/doc/codecs.texi
@@ -1041,6 +1041,8 @@ Note: The required alignment depends on if @code{AV_CODEC_FLAG_UNALIGNED} is set
 CPU. @code{AV_CODEC_FLAG_UNALIGNED} cannot be changed from the command line. Also hardware
 decoders will not apply left/top Cropping.

+@item lcevc-dec-params @var{string} (@emph{decoding,video})
+A :-separated list of key=value parameters passed to liblcevc-dec to initialize it.

 @end table

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..a2bda77b4a 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"
@@ -320,7 +321,7 @@ 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 == FF_LCEVCDEC_INIT) {
         LCEVC_FlushDecoder(lcevc->decoder);
         lcevc_flush_pictures(lcevc);
         LCEVC_DestroyDecoder(lcevc->decoder);
@@ -380,16 +381,52 @@ static int lcevc_init(FFLCEVCContext *lcevc)
 {
     LCEVC_AccelContextHandle dummy = { 0 };
     const int32_t event = LCEVC_Log;
+    LCEVC_ReturnCode res;
     int level;

+    lcevc->initialized = FF_LCEVCDEC_FAIL;
+
     if (LCEVC_CreateDecoder(&lcevc->decoder, dummy) != LCEVC_Success) {
         av_log(lcevc, AV_LOG_ERROR, "Failed to create LCEVC decoder\n");
         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);

@@ -399,7 +436,7 @@ static int lcevc_init(FFLCEVCContext *lcevc)
         return AVERROR_EXTERNAL;
     }

-    lcevc->initialized = 1;
+    lcevc->initialized = FF_LCEVCDEC_INIT;

     return 0;
 }
@@ -411,11 +448,12 @@ int ff_lcevc_process(void *logctx, AVFrame *frame)
     FFLCEVCContext *lcevc = frame_ctx->lcevc;
     int ret;

-    if (!lcevc->initialized) {
+    if (lcevc->initialized == FF_LCEVCDEC_UNINIT) {
         ret = lcevc_init(lcevc);
         if (ret < 0)
             return ret;
-    }
+    } else if (lcevc->initialized == FF_LCEVCDEC_FAIL)
+        return 0;

     av_assert0(frame_ctx->frame);

@@ -477,23 +515,60 @@ 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)
         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) {
         ret = AVERROR(ENOMEM);
@@ -516,9 +591,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..b757acc3f9 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
@@ -34,6 +34,12 @@ typedef uintptr_t LCEVC_DecoderHandle;
 struct CodedBitstreamContext;
 struct CodedBitstreamFragment;

+enum {
+    FF_LCEVCDEC_UNINIT = 0,
+    FF_LCEVCDEC_INIT,
+    FF_LCEVCDEC_FAIL,
+};
+
 typedef struct FFLCEVCContext {
     const AVClass *class;
     LCEVC_DecoderHandle decoder;
@@ -42,6 +48,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 +63,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 a329a84db1..83f362a556 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 */
diff --git a/libavcodec/version.h b/libavcodec/version.h
index 65bc52fb24..d8604fd00c 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -30,7 +30,7 @@
 #include "version_major.h"

 #define LIBAVCODEC_VERSION_MINOR  18
-#define LIBAVCODEC_VERSION_MICRO 100
+#define LIBAVCODEC_VERSION_MICRO 101

 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
                                                LIBAVCODEC_VERSION_MINOR, \