PR #25305 opened by Marti2203
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25305
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25305.patch
The `child_class_iterate` callbacks of `AVIAMFAudioElement`,
`AVIAMFSubmixElement` and `AVIAMFMixPresentation` use `if (i)` where `if (!i)`
is meant. The iteration state starts at 0, so they never return their child
class. As a result `av_opt_child_class_iterate()` reports no children for these
classes, and `av_opt_find2()` with `AV_OPT_SEARCH_CHILDREN |
AV_OPT_SEARCH_FAKE_OBJ` cannot find the `AVIAMFLayer`, `AVIAMFSubmix`,
`AVIAMFSubmixElement` or `AVIAMFParamDefinition` options from them.
`submix_presentation_child_iterate()` and `param_definition_child_iterate()` in
the same file use `case 0:` and work.
This is only reachable through the AVOption API, so the reproducer is a small
program against libavutil that prints the child class tree and does a few
lookups:
```
master:
AVIAMFAudioElement
AVIAMFMixPresentation
av_opt_find2(AVIAMFAudioElement, "ch_layout", SEARCH_CHILDREN|FAKE_OBJ): NOT
FOUND
av_opt_find2(AVIAMFMixPresentation, "default_mix_gain",
SEARCH_CHILDREN|FAKE_OBJ): NOT FOUND
av_opt_find2(AVIAMFMixPresentation, "headphones_rendering_mode",
SEARCH_CHILDREN|FAKE_OBJ): NOT FOUND
with the patch:
AVIAMFAudioElement
AVIAMFLayer
AVIAMFMixPresentation
AVIAMFSubmix
AVIAMFSubmixElement
AVIAMFParamDefinition
...
AVIAMFSubmixLayout
AVIAMFParamDefinition
...
(all three lookups: found)
```
The second commit adds `libavutil/tests/iamf.c` and `fate-iamf-class`, which
prints the same tree and lookups. (The FATE name is `iamf-class` because
`fate-iamf` is already the target for the IAMF muxing tests.) It fails on
master and passes with the fix. `fate-libavutil` and `fate-iamf` pass.
The ffmpeg CLI does not search these classes with `AV_OPT_SEARCH_FAKE_OBJ`, so
the change does not affect muxing IAMF with `-stream_group`. The IAMF FATE
tests give the same output with and without the patch.
This issue was found by an LLM-Enhanced Static Analysis Engine and verified
manually.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
From 355d9265aece328f6d36f124f16a7cc8e9cbf462 Mon Sep 17 00:00:00 2001
From: Martin Mirchev <[email protected]>
Date: Wed, 21 Oct 2026 19:03:15 +0800
Subject: [PATCH 1/2] avutil/iamf: fix child_class_iterate callbacks
The child_class_iterate callbacks of AVIAMFAudioElement,
AVIAMFSubmixElement and AVIAMFMixPresentation returned their single
child class only for a non-zero iteration state. The state starts at
zero, so they never returned anything, and av_opt_child_class_iterate()
and av_opt_find2() with AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ
could not reach the AVIAMFLayer, AVIAMFParamDefinition and AVIAMFSubmix
options from these classes.
Return the child class on the first call instead.
Signed-off-by: Martin Mirchev <[email protected]>
---
libavutil/iamf.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/libavutil/iamf.c b/libavutil/iamf.c
index 76707563cbb..66c6f350f79 100644
--- a/libavutil/iamf.c
+++ b/libavutil/iamf.c
@@ -300,7 +300,7 @@ static const AVClass *audio_element_child_iterate(void
**opaque)
uintptr_t i = (uintptr_t)*opaque;
const AVClass *ret = NULL;
- if (i)
+ if (!i)
ret = &layer_class;
if (ret)
@@ -387,7 +387,7 @@ static const AVClass *submix_element_child_iterate(void
**opaque)
uintptr_t i = (uintptr_t)*opaque;
const AVClass *ret = NULL;
- if (i)
+ if (!i)
ret = ¶m_definition_class;
if (ret)
@@ -498,7 +498,7 @@ static const AVClass *mix_presentation_child_iterate(void
**opaque)
uintptr_t i = (uintptr_t)*opaque;
const AVClass *ret = NULL;
- if (i)
+ if (!i)
ret = &submix_class;
if (ret)
--
2.54.0
From 29c46cf9b011b1fb49aed82348432d5c48508951 Mon Sep 17 00:00:00 2001
From: Martin Mirchev <[email protected]>
Date: Wed, 21 Oct 2026 19:04:05 +0800
Subject: [PATCH 2/2] tests/fate/libavutil: add a test for the IAMF AVClass
hierarchy
Print the child class tree of the IAMF AVClasses as returned by
av_opt_child_class_iterate() and look up options of the child classes
with av_opt_find2() and AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ.
Signed-off-by: Martin Mirchev <[email protected]>
---
libavutil/Makefile | 1 +
libavutil/tests/iamf.c | 68 +++++++++++++++++++++++++++++++++++++++
tests/fate/libavutil.mak | 4 +++
tests/ref/fate/iamf-class | 30 +++++++++++++++++
4 files changed, 103 insertions(+)
create mode 100644 libavutil/tests/iamf.c
create mode 100644 tests/ref/fate/iamf-class
diff --git a/libavutil/Makefile b/libavutil/Makefile
index 6e6246cb970..737d98e56fe 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -292,6 +292,7 @@ TESTPROGS = adler32
\
hdr_dynamic_vivid_metadata \
hmac \
hwdevice \
+ iamf \
integer \
imgutils \
lfg \
diff --git a/libavutil/tests/iamf.c b/libavutil/tests/iamf.c
new file mode 100644
index 00000000000..97959486212
--- /dev/null
+++ b/libavutil/tests/iamf.c
@@ -0,0 +1,68 @@
+/*
+ * 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 <stdio.h>
+
+#include "libavutil/iamf.h"
+#include "libavutil/log.h"
+#include "libavutil/opt.h"
+
+static void print_class_tree(const AVClass *class, int depth)
+{
+ const AVClass *child;
+ void *iter = NULL;
+
+ printf("%*s%s\n", depth * 2, "", class->class_name);
+ if (depth >= 8)
+ return;
+ while ((child = av_opt_child_class_iterate(class, &iter)))
+ print_class_tree(child, depth + 1);
+}
+
+static void find_option(const AVClass *class, const char *name)
+{
+ const AVOption *o = av_opt_find2(&class, name, NULL, 0,
+ AV_OPT_SEARCH_CHILDREN |
AV_OPT_SEARCH_FAKE_OBJ,
+ NULL);
+ printf("%s: %s %s\n", class->class_name, name, o ? "found" : "not found");
+}
+
+int main(void)
+{
+ const AVClass *audio_element = av_iamf_audio_element_get_class();
+ const AVClass *mix_presentation = av_iamf_mix_presentation_get_class();
+ const AVClass *param_definition = av_iamf_param_definition_get_class();
+
+ printf("Testing av_opt_child_class_iterate()\n");
+ print_class_tree(audio_element, 0);
+ print_class_tree(mix_presentation, 0);
+ print_class_tree(param_definition, 0);
+
+ printf("\nTesting av_opt_find2() with AV_OPT_SEARCH_CHILDREN |
AV_OPT_SEARCH_FAKE_OBJ\n");
+ find_option(audio_element, "default_w");
+ find_option(audio_element, "ch_layout");
+ find_option(mix_presentation, "annotations");
+ find_option(mix_presentation, "default_mix_gain");
+ find_option(mix_presentation, "headphones_rendering_mode");
+ find_option(mix_presentation, "sound_system");
+ find_option(mix_presentation, "parameter_rate");
+ find_option(param_definition, "parameter_rate");
+ find_option(param_definition, "subblock_duration");
+
+ return 0;
+}
diff --git a/tests/fate/libavutil.mak b/tests/fate/libavutil.mak
index d45c7f5d894..fa55ebdbe36 100644
--- a/tests/fate/libavutil.mak
+++ b/tests/fate/libavutil.mak
@@ -139,6 +139,10 @@ FATE_LIBAVUTIL += fate-hmac
fate-hmac: libavutil/tests/hmac$(EXESUF)
fate-hmac: CMD = run libavutil/tests/hmac$(EXESUF)
+FATE_LIBAVUTIL += fate-iamf-class
+fate-iamf-class: libavutil/tests/iamf$(EXESUF)
+fate-iamf-class: CMD = run libavutil/tests/iamf$(EXESUF)
+
FATE_LIBAVUTIL += fate-imgutils
fate-imgutils: libavutil/tests/imgutils$(EXESUF)
fate-imgutils: CMD = run libavutil/tests/imgutils$(EXESUF)
diff --git a/tests/ref/fate/iamf-class b/tests/ref/fate/iamf-class
new file mode 100644
index 00000000000..518835a202d
--- /dev/null
+++ b/tests/ref/fate/iamf-class
@@ -0,0 +1,30 @@
+Testing av_opt_child_class_iterate()
+AVIAMFAudioElement
+ AVIAMFLayer
+AVIAMFMixPresentation
+ AVIAMFSubmix
+ AVIAMFSubmixElement
+ AVIAMFParamDefinition
+ AVIAMFMixGain
+ AVIAMFDemixingInfo
+ AVIAMFReconGain
+ AVIAMFSubmixLayout
+ AVIAMFParamDefinition
+ AVIAMFMixGain
+ AVIAMFDemixingInfo
+ AVIAMFReconGain
+AVIAMFParamDefinition
+ AVIAMFMixGain
+ AVIAMFDemixingInfo
+ AVIAMFReconGain
+
+Testing av_opt_find2() with AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ
+AVIAMFAudioElement: default_w found
+AVIAMFAudioElement: ch_layout found
+AVIAMFMixPresentation: annotations found
+AVIAMFMixPresentation: default_mix_gain found
+AVIAMFMixPresentation: headphones_rendering_mode found
+AVIAMFMixPresentation: sound_system found
+AVIAMFMixPresentation: parameter_rate found
+AVIAMFParamDefinition: parameter_rate found
+AVIAMFParamDefinition: subblock_duration found
--
2.54.0
_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]