PR #22691 opened by sohamukute
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22691
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22691.patch

Add a deterministic API FATE test for public class and option entry points 
around AVFormatContext, AVStream, AVStreamGroup, IAMF helpers and protocol 
classes.

This improves branch coverage for:
  - libavformat/options.c: 55.63% -> 78.17% branches taken
  - libavutil/iamf.c: 48.84% -> 69.77% branches taken
  - libavformat/protocols.c: 30.56% -> 69.44% branches taken

The test stays within public API boundaries and exercises class getters, option 
lookup, child class iteration, stream and stream group creation, selected valid 
failure paths, IAMF allocation helpers, protocol enumeration, and stable option 
setting paths.

Signed-off-by: Soham Kute <[email protected]>


>From 2441012a15736520442b06e40b52371985034a1e Mon Sep 17 00:00:00 2001
From: Soham Kute <[email protected]>
Date: Thu, 2 Apr 2026 23:01:01 +0530
Subject: [PATCH] tests/api: cover format, stream, stream group, IAMF and
 protocol class APIs

Add a deterministic API FATE test that exercises public AVClass and option 
paths in libavformat/options.c, libavutil/iamf.c and libavformat/protocols.c.

The test covers class getters, option lookup, child class iteration, protocol 
enumeration, stream and stream-group creation, selected valid failure paths, 
and IAMF allocation and option-setting paths, without changing runtime code or 
relying on samples.

Signed-off-by: Soham Kute <[email protected]>
---
 tests/api/Makefile                |   1 +
 tests/api/api-format-class-test.c | 493 ++++++++++++++++++++++++++++++
 tests/fate/api.mak                |   4 +
 tests/ref/fate/api-format-class   |   5 +
 4 files changed, 503 insertions(+)
 create mode 100644 tests/api/api-format-class-test.c
 create mode 100644 tests/ref/fate/api-format-class

diff --git a/tests/api/Makefile b/tests/api/Makefile
index 899aeb1f54..c1890a5ba6 100644
--- a/tests/api/Makefile
+++ b/tests/api/Makefile
@@ -2,6 +2,7 @@ APITESTPROGS-$(call ENCDEC, FLAC, FLAC) += api-flac
 APITESTPROGS-$(call DEMDEC, H264, H264) += api-h264
 APITESTPROGS-$(call DEMDEC, H264, H264) += api-h264-slice
 APITESTPROGS-yes += api-seek api-dump-stream-meta
+APITESTPROGS-$(call ALLYES, AVFORMAT IAMF FILE_PROTOCOL WAV_MUXER WAV_DEMUXER) 
+= api-format-class
 APITESTPROGS-$(call DEMDEC, H263, H263) += api-band
 APITESTPROGS-$(HAVE_THREADS) += api-threadmessage
 APITESTPROGS += $(APITESTPROGS-yes)
diff --git a/tests/api/api-format-class-test.c 
b/tests/api/api-format-class-test.c
new file mode 100644
index 0000000000..cd17bda3f6
--- /dev/null
+++ b/tests/api/api-format-class-test.c
@@ -0,0 +1,493 @@
+/*
+ * Copyright (c) 2026 Soham Kute
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to 
deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "libavformat/avformat.h"
+#include "libavformat/avio.h"
+#include "libavutil/iamf.h"
+#include "libavutil/log.h"
+#include "libavutil/mem.h"
+#include "libavutil/opt.h"
+
+#define CHECK(cond, fmt, ...)                                                  
 \
+    do {                                                                       
 \
+        if (!(cond)) {                                                         
 \
+            fprintf(stderr, fmt "\n", ##__VA_ARGS__);                          
 \
+            return 1;                                                          
 \
+        }                                                                      
 \
+    } while (0)
+
+static int fake_opt_exists(const AVClass *cls, const char *name, int 
search_flags)
+{
+    return !!av_opt_find(&cls, name, NULL, 0, search_flags | 
AV_OPT_SEARCH_FAKE_OBJ);
+}
+
+static int test_protocol_api(void)
+{
+    const AVClass *file_cls;
+    const AVOption *opt;
+    const char *name;
+    void *opaque = NULL;
+    int saw_file_input = 0;
+    int saw_file_output = 0;
+
+    file_cls = avio_protocol_get_class("file");
+    CHECK(file_cls, "avio_protocol_get_class(file) returned NULL");
+    CHECK(!strcmp(file_cls->class_name, "file"),
+          "unexpected file protocol class: %s", file_cls->class_name);
+
+    opt = av_opt_find(&file_cls, "truncate", NULL, 0, AV_OPT_SEARCH_FAKE_OBJ);
+    CHECK(opt, "file protocol option truncate not found");
+
+    CHECK(!avio_protocol_get_class("no_such_protocol"),
+          "unexpected class for missing protocol");
+
+    while ((name = avio_enum_protocols(&opaque, 0)))
+        saw_file_input |= !strcmp(name, "file");
+    opaque = NULL;
+    while ((name = avio_enum_protocols(&opaque, 1)))
+        saw_file_output |= !strcmp(name, "file");
+
+    CHECK(saw_file_input, "file protocol missing from input protocol 
enumeration");
+    CHECK(saw_file_output, "file protocol missing from output protocol 
enumeration");
+
+    printf("protocols file=%d missing=%d enum=%d\n",
+           1, 1, saw_file_input && saw_file_output);
+    return 0;
+}
+
+static int test_format_api(void)
+{
+    AVFormatContext *ctx = NULL;
+    AVFormatContext *out = NULL;
+    const AVClass *format_cls = avformat_get_class();
+    const AVClass *child_cls;
+    const AVOption *opt;
+    void *iter = NULL;
+    void *child;
+    int saw_avio = 0;
+    int saw_wav_muxer = 0;
+    int saw_wav_demuxer = 0;
+    int ret;
+
+    CHECK(fake_opt_exists(format_cls, "duration_probesize", 0),
+          "duration_probesize not found on AVFormatContext class");
+
+    ctx = avformat_alloc_context();
+    CHECK(ctx, "avformat_alloc_context failed");
+    CHECK(!strcmp(ctx->av_class->item_name(ctx), "AVFormatContext"),
+          "unexpected bare format item_name: %s", 
ctx->av_class->item_name(ctx));
+
+    ctx->name = av_strdup("named-format");
+    CHECK(ctx->name, "av_strdup failed");
+    CHECK(!strcmp(ctx->av_class->item_name(ctx), "named-format"),
+          "unexpected named format item_name: %s", 
ctx->av_class->item_name(ctx));
+    av_freep(&ctx->name);
+
+    ctx->iformat = av_find_input_format("wav");
+    CHECK(ctx->iformat, "av_find_input_format(wav) failed");
+    CHECK(!strcmp(ctx->av_class->item_name(ctx), "wav"),
+          "unexpected input format item_name: %s", 
ctx->av_class->item_name(ctx));
+    CHECK(ctx->av_class->get_category(ctx) == AV_CLASS_CATEGORY_DEMUXER,
+          "unexpected input format category");
+    avformat_free_context(ctx);
+    ctx = NULL;
+
+    ret = avformat_alloc_output_context2(&out, NULL, "wav", NULL);
+    CHECK(ret >= 0 && out, "avformat_alloc_output_context2(wav) failed: %d", 
ret);
+    CHECK(!strcmp(out->av_class->item_name(out), "wav"),
+          "unexpected output format item_name: %s", 
out->av_class->item_name(out));
+    CHECK(out->av_class->get_category(out) == AV_CLASS_CATEGORY_MUXER,
+          "unexpected output format category");
+
+    child = av_opt_child_next(out, NULL);
+    CHECK(child == out->priv_data, "first AVFormatContext child is not 
priv_data");
+    CHECK(!av_opt_child_next(out, child), "unexpected second AVFormatContext 
child");
+
+    while ((child_cls = av_opt_child_class_iterate(format_cls, &iter))) {
+        saw_avio      |= !strcmp(child_cls->class_name, "AVIOContext");
+        saw_wav_muxer |= !strcmp(child_cls->class_name, "WAV muxer");
+        saw_wav_demuxer |= !strcmp(child_cls->class_name, "WAV demuxer");
+    }
+
+    CHECK(saw_avio, "AVIOContext child class not found");
+    CHECK(saw_wav_muxer, "WAV muxer child class not found");
+    CHECK(saw_wav_demuxer, "WAV demuxer child class not found");
+
+    opt = av_opt_find2(out, "write_bext", NULL, 0, AV_OPT_SEARCH_CHILDREN, 
&child);
+    CHECK(opt && child == out->priv_data,
+          "write_bext option was not resolved to muxer private data");
+    avformat_free_context(out);
+
+    printf("format names=%d children=%d classes=%d\n", 1, 1, 1);
+    return 0;
+}
+
+static int test_stream_api(void)
+{
+    AVFormatContext *ctx = NULL;
+    AVFormatContext *limit_ctx = NULL;
+    AVStream *limited_st;
+    AVStream *st;
+    const AVClass *stream_cls = av_stream_get_class();
+    int log_level;
+
+    CHECK(fake_opt_exists(stream_cls, "discard", 0),
+          "discard not found on AVStream class");
+    CHECK(fake_opt_exists(stream_cls, "disposition", 0),
+          "disposition not found on AVStream class");
+
+    limit_ctx = avformat_alloc_context();
+    CHECK(limit_ctx, "avformat_alloc_context failed");
+    limit_ctx->max_streams = 0;
+    log_level = av_log_get_level();
+    av_log_set_level(AV_LOG_QUIET);
+    limited_st = avformat_new_stream(limit_ctx, NULL);
+    av_log_set_level(log_level);
+    CHECK(!limited_st,
+          "avformat_new_stream unexpectedly ignored max_streams");
+    avformat_free_context(limit_ctx);
+
+    ctx = avformat_alloc_context();
+    CHECK(ctx, "avformat_alloc_context failed");
+    ctx->iformat = av_find_input_format("wav");
+    CHECK(ctx->iformat, "av_find_input_format(wav) failed");
+
+    st = avformat_new_stream(ctx, NULL);
+    CHECK(st, "avformat_new_stream failed");
+
+    CHECK(av_opt_set(st, "discard", "all", 0) >= 0, "setting discard failed");
+    CHECK(st->discard == AVDISCARD_ALL, "discard value was not applied");
+
+    CHECK(av_opt_set(st, "disposition", "forced", 0) >= 0,
+          "setting disposition failed");
+    CHECK(st->disposition == AV_DISPOSITION_FORCED,
+          "disposition value was not applied");
+
+    CHECK(av_disposition_from_string("forced") == AV_DISPOSITION_FORCED,
+          "av_disposition_from_string(forced) failed");
+    CHECK(av_disposition_from_string("not-a-disposition") == AVERROR(EINVAL),
+          "unexpected result for invalid disposition");
+    CHECK(!strcmp(av_disposition_to_string(AV_DISPOSITION_FORCED), "forced"),
+          "av_disposition_to_string(forced) failed");
+    CHECK(!av_disposition_to_string(0),
+          "av_disposition_to_string(0) returned a string");
+
+    avformat_free_context(ctx);
+
+    printf("stream discard=%d disposition=%d strings=%d\n", 1, 1, 1);
+    return 0;
+}
+
+static int test_stream_group_api(void)
+{
+    AVFormatContext *ctx = NULL;
+    AVFormatContext *ctx2 = NULL;
+    AVStreamGroup *invalid;
+    AVStreamGroup *audio = NULL, *mix = NULL, *tile = NULL, *lcevc = NULL;
+    AVStream *st = NULL, *foreign = NULL;
+    const AVClass *stream_group_cls = av_stream_group_get_class();
+    const AVClass *child_cls;
+    AVDictionary *invalid_opts = NULL;
+    AVDictionary *opts = NULL;
+    void *iter = NULL;
+    void *target = NULL;
+    int ret;
+    int log_level;
+    int saw_audio = 0;
+    int saw_mix = 0;
+    int saw_tile = 0;
+    int saw_lcevc = 0;
+
+    CHECK(fake_opt_exists(stream_group_cls, "id", 0),
+          "id not found on AVStreamGroup class");
+    CHECK(fake_opt_exists(stream_group_cls, "default_w", 
AV_OPT_SEARCH_CHILDREN),
+          "default_w child option not found on AVStreamGroup class");
+    CHECK(fake_opt_exists(stream_group_cls, "grid_size", 
AV_OPT_SEARCH_CHILDREN),
+          "grid_size child option not found on AVStreamGroup class");
+    CHECK(fake_opt_exists(stream_group_cls, "video_size", 
AV_OPT_SEARCH_CHILDREN),
+          "video_size child option not found on AVStreamGroup class");
+
+    while ((child_cls = av_opt_child_class_iterate(stream_group_cls, &iter))) {
+        saw_audio |= !strcmp(child_cls->class_name, "AVIAMFAudioElement");
+        saw_mix   |= !strcmp(child_cls->class_name, "AVIAMFMixPresentation");
+        saw_tile  |= !strcmp(child_cls->class_name, "AVStreamGroupTileGrid");
+        saw_lcevc |= !strcmp(child_cls->class_name, "AVStreamGroupLCEVC");
+    }
+    CHECK(saw_audio && saw_mix && saw_tile && saw_lcevc,
+          "AVStreamGroup child classes were incomplete");
+
+    ctx = avformat_alloc_context();
+    ctx2 = avformat_alloc_context();
+    CHECK(ctx && ctx2, "avformat_alloc_context failed");
+
+    st = avformat_new_stream(ctx, NULL);
+    foreign = avformat_new_stream(ctx2, NULL);
+    CHECK(st && foreign, "avformat_new_stream failed");
+
+    av_dict_set(&opts, "id", "11", 0);
+    av_dict_set(&opts, "grid_size", "64x64", 0);
+    tile = avformat_stream_group_create(ctx, AV_STREAM_GROUP_PARAMS_TILE_GRID, 
&opts);
+    CHECK(tile, "creating tile stream group failed");
+    CHECK(!opts, "tile stream group options were not fully consumed");
+    CHECK(tile->id == 11, "tile stream group id was not applied");
+    CHECK(tile->params.tile_grid->coded_width == 64 &&
+          tile->params.tile_grid->coded_height == 64,
+          "tile grid_size was not applied");
+
+    audio = avformat_stream_group_create(ctx, 
AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT, NULL);
+    mix   = avformat_stream_group_create(ctx, 
AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION, NULL);
+    lcevc = avformat_stream_group_create(ctx, AV_STREAM_GROUP_PARAMS_LCEVC, 
NULL);
+    CHECK(audio && mix && lcevc, "creating stream groups failed");
+    CHECK(!avformat_stream_group_create(ctx, AV_STREAM_GROUP_PARAMS_NONE, 
NULL),
+          "invalid stream group type unexpectedly succeeded");
+    av_dict_set(&invalid_opts, "grid_size", "bad", 0);
+    log_level = av_log_get_level();
+    av_log_set_level(AV_LOG_QUIET);
+    invalid = avformat_stream_group_create(ctx, 
AV_STREAM_GROUP_PARAMS_TILE_GRID, &invalid_opts);
+    av_log_set_level(log_level);
+    CHECK(!invalid,
+          "invalid stream group options unexpectedly succeeded");
+    av_dict_free(&invalid_opts);
+
+    CHECK(av_opt_child_next(audio, NULL) == audio->params.iamf_audio_element,
+          "audio stream group child_next failed");
+    CHECK(!av_opt_child_next(audio, audio->params.iamf_audio_element),
+          "audio stream group has unexpected extra child");
+
+    CHECK(av_opt_child_next(mix, NULL) == mix->params.iamf_mix_presentation,
+          "mix stream group child_next failed");
+    CHECK(!av_opt_child_next(mix, mix->params.iamf_mix_presentation),
+          "mix stream group has unexpected extra child");
+
+    CHECK(av_opt_child_next(tile, NULL) == tile->params.tile_grid,
+          "tile stream group child_next failed");
+    CHECK(!av_opt_child_next(tile, tile->params.tile_grid),
+          "tile stream group has unexpected extra child");
+
+    CHECK(av_opt_child_next(lcevc, NULL) == lcevc->params.lcevc,
+          "lcevc stream group child_next failed");
+    CHECK(!av_opt_child_next(lcevc, lcevc->params.lcevc),
+          "lcevc stream group has unexpected extra child");
+
+    CHECK(av_opt_set_int(audio, "default_w", 5, AV_OPT_SEARCH_CHILDREN) >= 0,
+          "setting default_w via stream group failed");
+    CHECK(audio->params.iamf_audio_element->default_w == 5,
+          "default_w was not applied");
+
+    CHECK(av_opt_set_image_size(lcevc, "video_size", 16, 16, 
AV_OPT_SEARCH_CHILDREN) >= 0,
+          "setting video_size via stream group failed");
+    CHECK(lcevc->params.lcevc->width == 16 && lcevc->params.lcevc->height == 
16,
+          "video_size was not applied");
+
+    CHECK(av_opt_find2(tile, "grid_size", NULL, 0, AV_OPT_SEARCH_CHILDREN, 
&target) &&
+          target == tile->params.tile_grid,
+          "grid_size did not resolve to the tile grid child");
+    CHECK(av_opt_find2(audio, "default_w", NULL, 0, AV_OPT_SEARCH_CHILDREN, 
&target) &&
+          target == audio->params.iamf_audio_element,
+          "default_w did not resolve to the IAMF audio child");
+    CHECK(av_opt_find2(lcevc, "video_size", NULL, 0, AV_OPT_SEARCH_CHILDREN, 
&target) &&
+          target == lcevc->params.lcevc,
+          "video_size did not resolve to the LCEVC child");
+
+    CHECK(avformat_stream_group_add_stream(tile, st) == 0,
+          "avformat_stream_group_add_stream failed");
+    ret = avformat_stream_group_add_stream(tile, st);
+    CHECK(ret == AVERROR(EEXIST),
+          "duplicate avformat_stream_group_add_stream returned %d", ret);
+    ret = avformat_stream_group_add_stream(tile, foreign);
+    CHECK(ret == AVERROR(EINVAL),
+          "foreign avformat_stream_group_add_stream returned %d", ret);
+
+    avformat_free_context(ctx2);
+    avformat_free_context(ctx);
+
+    printf("stream_group classes=%d children=%d add=%d set=%d\n", 1, 1, 1, 1);
+    return 0;
+}
+
+static int test_iamf_api(void)
+{
+    const AVClass *param_cls = av_iamf_param_definition_get_class();
+    const AVClass *audio_cls = av_iamf_audio_element_get_class();
+    const AVClass *mix_cls   = av_iamf_mix_presentation_get_class();
+    const AVClass *child_cls;
+    AVChannelLayout stereo = AV_CHANNEL_LAYOUT_STEREO;
+    AVChannelLayout binaural = AV_CHANNEL_LAYOUT_BINAURAL;
+    AVIAMFParamDefinition *par_mix = NULL, *par_demix = NULL, *par_recon = 
NULL;
+    AVIAMFAudioElement *audio = NULL;
+    AVIAMFLayer *layer;
+    AVIAMFMixPresentation *mix = NULL;
+    AVIAMFSubmix *submix;
+    AVIAMFSubmixElement *element;
+    AVIAMFSubmixLayout *layout;
+    void *iter = NULL;
+    size_t size = 0;
+    int saw_mix = 0;
+    int saw_demix = 0;
+    int saw_recon = 0;
+
+    CHECK(param_cls && audio_cls && mix_cls, "IAMF class getter returned 
NULL");
+    CHECK(fake_opt_exists(param_cls, "parameter_id", 0),
+          "parameter_id not found on param definition class");
+    CHECK(fake_opt_exists(param_cls, "subblock_duration", 
AV_OPT_SEARCH_CHILDREN),
+          "subblock_duration child option not found on param definition 
class");
+    CHECK(fake_opt_exists(audio_cls, "default_w", 0),
+          "default_w not found on audio element class");
+    CHECK(fake_opt_exists(mix_cls, "annotations", 0),
+          "annotations not found on mix presentation class");
+
+    while ((child_cls = av_opt_child_class_iterate(param_cls, &iter))) {
+        saw_mix   |= !strcmp(child_cls->class_name, "AVIAMFMixGain");
+        saw_demix |= !strcmp(child_cls->class_name, "AVIAMFDemixingInfo");
+        saw_recon |= !strcmp(child_cls->class_name, "AVIAMFReconGain");
+    }
+    CHECK(saw_mix && saw_demix && saw_recon,
+          "IAMF param definition child classes were incomplete");
+
+    CHECK(!av_iamf_param_definition_alloc((enum AVIAMFParamDefinitionType)-1, 
1, NULL),
+          "invalid IAMF param definition type unexpectedly succeeded");
+
+    par_mix = 
av_iamf_param_definition_alloc(AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN, 2, &size);
+    par_demix = 
av_iamf_param_definition_alloc(AV_IAMF_PARAMETER_DEFINITION_DEMIXING, 1, NULL);
+    par_recon = 
av_iamf_param_definition_alloc(AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN, 1, 
NULL);
+    CHECK(par_mix && par_demix && par_recon, "IAMF param definition allocation 
failed");
+    CHECK(size > sizeof(*par_mix), "unexpected IAMF param definition size");
+    CHECK(av_opt_set_int(par_mix, "parameter_id", 7, 0) >= 0,
+          "setting parameter_id failed");
+    CHECK(par_mix->parameter_id == 7, "parameter_id was not applied");
+
+    audio = av_iamf_audio_element_alloc();
+    CHECK(audio, "av_iamf_audio_element_alloc failed");
+    CHECK(av_opt_set(audio, "audio_element_type", "channel", 0) >= 0,
+          "setting audio_element_type failed");
+    CHECK(av_opt_set_int(audio, "default_w", 3, 0) >= 0,
+          "setting default_w failed");
+    CHECK(audio->audio_element_type == AV_IAMF_AUDIO_ELEMENT_TYPE_CHANNEL &&
+          audio->default_w == 3,
+          "audio element options were not applied");
+    audio->demixing_info = par_demix;
+    audio->recon_gain_info = par_recon;
+    par_demix = NULL;
+    par_recon = NULL;
+
+    layer = av_iamf_audio_element_add_layer(audio);
+    CHECK(layer, "av_iamf_audio_element_add_layer failed");
+    CHECK(av_opt_set_chlayout(layer, "ch_layout", &stereo, 0) >= 0,
+          "setting ch_layout failed");
+    CHECK(av_opt_set(layer, "output_gain_flags", "FL", 0) >= 0,
+          "setting output_gain_flags failed");
+    CHECK(av_opt_set(layer, "flags", "recon_gain", 0) >= 0,
+          "setting layer flags failed");
+    CHECK(av_opt_set_q(layer, "output_gain", (AVRational){ 5, 4 }, 0) >= 0,
+          "setting layer output_gain failed");
+
+    mix = av_iamf_mix_presentation_alloc();
+    CHECK(mix, "av_iamf_mix_presentation_alloc failed");
+    submix = av_iamf_mix_presentation_add_submix(mix);
+    CHECK(submix, "av_iamf_mix_presentation_add_submix failed");
+    element = av_iamf_submix_add_element(submix);
+    CHECK(element, "av_iamf_submix_add_element failed");
+    layout = av_iamf_submix_add_layout(submix);
+    CHECK(layout, "av_iamf_submix_add_layout failed");
+
+    element->element_mix_config =
+        av_iamf_param_definition_alloc(AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN, 
1, NULL);
+    submix->output_mix_config =
+        av_iamf_param_definition_alloc(AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN, 
1, NULL);
+    CHECK(element->element_mix_config && submix->output_mix_config,
+          "IAMF mix config allocation failed");
+
+    iter = NULL;
+    child_cls = av_opt_child_class_iterate(submix->av_class, &iter);
+    CHECK(child_cls && !strcmp(child_cls->class_name, "AVIAMFSubmixElement"),
+          "unexpected IAMF submix child class");
+    child_cls = av_opt_child_class_iterate(submix->av_class, &iter);
+    CHECK(child_cls && !strcmp(child_cls->class_name, "AVIAMFSubmixLayout"),
+          "unexpected IAMF submix layout class");
+    child_cls = av_opt_child_class_iterate(submix->av_class, &iter);
+    CHECK(child_cls && !strcmp(child_cls->class_name, "AVIAMFParamDefinition"),
+          "unexpected IAMF submix parameter class");
+    CHECK(!av_opt_child_class_iterate(submix->av_class, &iter),
+          "IAMF submix child class iterate returned extra child");
+
+    CHECK(av_opt_child_next(element, NULL) == element->element_mix_config,
+          "submix element child_next failed");
+    CHECK(!av_opt_child_next(element, element->element_mix_config),
+          "submix element has unexpected extra child");
+    CHECK(av_opt_child_next(submix, NULL) == submix->output_mix_config,
+          "submix child_next failed");
+    CHECK(!av_opt_child_next(submix, submix->output_mix_config),
+          "submix has unexpected extra child");
+
+    CHECK(av_opt_set_q(element, "default_mix_gain", (AVRational){ 1, 2 }, 0) 
>= 0,
+          "setting element default_mix_gain failed");
+    CHECK(av_opt_set(element, "headphones_rendering_mode", "binaural", 0) >= 0,
+          "setting headphones_rendering_mode failed");
+    CHECK(av_opt_set_q(submix, "default_mix_gain", (AVRational){ 3, 4 }, 0) >= 
0,
+          "setting submix default_mix_gain failed");
+    CHECK(av_opt_set_chlayout(layout, "sound_system", &binaural, 0) >= 0,
+          "setting sound_system failed");
+    CHECK(av_opt_set(layout, "layout_type", "binaural", 0) >= 0,
+          "setting layout_type failed");
+    CHECK(element->default_mix_gain.num == 1 && element->default_mix_gain.den 
== 2,
+          "element default_mix_gain was not applied");
+    CHECK(element->headphones_rendering_mode == 
AV_IAMF_HEADPHONES_MODE_BINAURAL,
+          "headphones_rendering_mode was not applied");
+    CHECK(submix->default_mix_gain.num == 3 && submix->default_mix_gain.den == 
4,
+          "submix default_mix_gain was not applied");
+    CHECK(layout->layout_type == AV_IAMF_SUBMIX_LAYOUT_TYPE_BINAURAL,
+          "layout_type was not applied");
+    CHECK(av_channel_layout_compare(&layout->sound_system, &binaural) == 0,
+          "sound_system was not applied");
+    CHECK(layer->flags == AV_IAMF_LAYER_FLAG_RECON_GAIN,
+          "layer flags were not applied");
+    CHECK(layer->output_gain_flags == 1 << 5,
+          "layer output_gain_flags were not applied");
+    CHECK(layer->output_gain.num == 5 && layer->output_gain.den == 4,
+          "layer output_gain was not applied");
+
+    av_iamf_mix_presentation_free(&mix);
+    av_iamf_audio_element_free(&audio);
+    av_free(par_mix);
+    av_free(par_demix);
+    av_free(par_recon);
+
+    printf("iamf param=%d alloc=%d mix=%d children=%d\n", 1, 1, 1, 1);
+    return 0;
+}
+
+int main(void)
+{
+    int ret = 0;
+
+    ret |= test_protocol_api();
+    ret |= test_format_api();
+    ret |= test_stream_api();
+    ret |= test_stream_group_api();
+    ret |= test_iamf_api();
+
+    return ret;
+}
diff --git a/tests/fate/api.mak b/tests/fate/api.mak
index b760de5eb6..e49f920f90 100644
--- a/tests/fate/api.mak
+++ b/tests/fate/api.mak
@@ -28,6 +28,10 @@ fate-api-threadmessage: 
$(APITESTSDIR)/api-threadmessage-test$(EXESUF)
 fate-api-threadmessage: CMD = run 
$(APITESTSDIR)/api-threadmessage-test$(EXESUF) 3 10 30 50 2 20 40
 fate-api-threadmessage: CMP = null
 
+FATE_API_LIBAVFORMAT-$(call ALLYES, AVFORMAT IAMF FILE_PROTOCOL WAV_MUXER 
WAV_DEMUXER) += fate-api-format-class
+fate-api-format-class: $(APITESTSDIR)/api-format-class-test$(EXESUF)
+fate-api-format-class: CMD = run $(APITESTSDIR)/api-format-class-test$(EXESUF)
+
 FATE_API_SAMPLES-$(CONFIG_AVFORMAT) += $(FATE_API_SAMPLES_LIBAVFORMAT-yes)
 
 ifdef SAMPLES
diff --git a/tests/ref/fate/api-format-class b/tests/ref/fate/api-format-class
new file mode 100644
index 0000000000..b91bcd7cf1
--- /dev/null
+++ b/tests/ref/fate/api-format-class
@@ -0,0 +1,5 @@
+protocols file=1 missing=1 enum=1
+format names=1 children=1 classes=1
+stream discard=1 disposition=1 strings=1
+stream_group classes=1 children=1 add=1 set=1
+iamf param=1 alloc=1 mix=1 children=1
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to