diff mbox series

[FFmpeg-devel,4/5] lavu/log, opt: detect setting non-runtime options post-init

Message ID 20240928142830.961-4-anton@khirnov.net
State New
Headers show
Series [FFmpeg-devel,1/5] lavu/class: improve AVClass doxy | expand

Checks

Context Check Description
andriy/make_x86 success Make finished
andriy/make_fate_x86 success Make fate finished

Commit Message

Anton Khirnov Sept. 28, 2024, 2:28 p.m. UTC
Add a mechanism to AVClass to allow objects to signal their state to
generic code. When an object flags itself with the 'initialized' state,
print an error (and fail, after the next major bump) if the caller
attempts to set non-runtime options.
---
 doc/APIchanges      |  3 +++
 libavutil/log.h     | 18 ++++++++++++++++++
 libavutil/opt.c     | 33 +++++++++++++++++++++++++++++++++
 libavutil/version.h |  2 +-
 4 files changed, 55 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/doc/APIchanges b/doc/APIchanges
index ddd4e6c9e0..9974a5ede2 100644
--- a/doc/APIchanges
+++ b/doc/APIchanges
@@ -2,6 +2,9 @@  The last version increases of all libraries were on 2024-03-07
 
 API changes, most recent first:
 
+2024-10-xx - xxxxxxxxxx - lavu 59.41.100 - log.h
+  Add AVClass.state_flags_offset and AV_CLASS_STATE_INITIALIZED.
+
 -------- 8< --------- FFmpeg 7.1 was cut here -------- 8< ---------
 
 2024-09-23 - 6940a6de2f0 - lavu 59.38.100 - frame.h
diff --git a/libavutil/log.h b/libavutil/log.h
index c6b47cc054..63b3283223 100644
--- a/libavutil/log.h
+++ b/libavutil/log.h
@@ -46,6 +46,15 @@  typedef enum {
     AV_CLASS_CATEGORY_NB  ///< not part of ABI/API
 }AVClassCategory;
 
+enum AVClassStateFlags {
+    /**
+     * Object initialization has finished and it is now in the 'runtime' stage.
+     * This affects e.g. what options can be set on the object (only
+     * AV_OPT_FLAG_RUNTIME_PARAM options can be set on initialized objects).
+     */
+    AV_CLASS_STATE_INITIALIZED         = (1 << 0),
+};
+
 #define AV_IS_INPUT_DEVICE(category) \
     (((category) == AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT) || \
      ((category) == AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT) || \
@@ -155,6 +164,15 @@  typedef struct AVClass {
      *       instance of this class.
      */
     const struct AVClass* (*child_class_iterate)(void **iter);
+
+    /**
+     * When non-zero, offset in the object to an unsigned int holding object
+     * state flags, a combination of AVClassStateFlags values. The flags are
+     * updated by the object to signal its state to the generic code.
+     *
+     * Added in version 59.41.100.
+     */
+    int state_flags_offset;
 } AVClass;
 
 /**
diff --git a/libavutil/opt.c b/libavutil/opt.c
index 81cb4b10f4..b85af68eff 100644
--- a/libavutil/opt.c
+++ b/libavutil/opt.c
@@ -185,6 +185,39 @@  static int opt_set_init(void *obj, const char *name, int search_flags,
         return AVERROR(EINVAL);
     }
 
+    if (!(o->flags & AV_OPT_FLAG_RUNTIME_PARAM)) {
+        unsigned *state_flags = NULL;
+        const AVClass *class;
+
+        // try state flags first from the target (child), then from its parent
+        class = *(const AVClass**)tgt;
+        if (
+#if LIBAVUTIL_VERSION_MAJOR < 60
+            class->version >= AV_VERSION_INT(59, 41, 100) &&
+#endif
+            class->state_flags_offset)
+            state_flags = (unsigned*)((uint8_t*)tgt + class->state_flags_offset);
+
+        if (!state_flags && obj != tgt) {
+            class = *(const AVClass**)obj;
+            if (
+#if LIBAVUTIL_VERSION_MAJOR < 60
+                class->version >= AV_VERSION_INT(59, 41, 100) &&
+#endif
+                class->state_flags_offset)
+                state_flags = (unsigned*)((uint8_t*)obj + class->state_flags_offset);
+        }
+
+        if (state_flags && (*state_flags & AV_CLASS_STATE_INITIALIZED)) {
+            av_log(obj, AV_LOG_ERROR, "Option '%s' is not a runtime option and "
+                   "so cannot be set after the object has been initialized\n",
+                   o->name);
+#if LIBAVUTIL_VERSION_MAJOR >= 60
+            return AVERROR(EINVAL);
+#endif
+        }
+    }
+
     if (o->flags & AV_OPT_FLAG_DEPRECATED)
         av_log(obj, AV_LOG_WARNING, "The \"%s\" option is deprecated: %s\n", name, o->help);
 
diff --git a/libavutil/version.h b/libavutil/version.h
index 1ae3f4ef87..f08a42f3d1 100644
--- a/libavutil/version.h
+++ b/libavutil/version.h
@@ -79,7 +79,7 @@ 
  */
 
 #define LIBAVUTIL_VERSION_MAJOR  59
-#define LIBAVUTIL_VERSION_MINOR  40
+#define LIBAVUTIL_VERSION_MINOR  41
 #define LIBAVUTIL_VERSION_MICRO 100
 
 #define LIBAVUTIL_VERSION_INT   AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \