PR #24446 opened by James Almer (jamrial)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24446
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24446.patch

Too many merge conflicts to attempt individual cherry-picks.


>From 5df6c1e37b39842a2eaed168938fd7933b2202d1 Mon Sep 17 00:00:00 2001
From: James Almer <[email protected]>
Date: Thu, 10 Sep 2026 16:40:16 -0300
Subject: [PATCH] avformat/iamf: backport assorted fixes

Too many merge conflics to attempt individual cherry-picks.

Signed-off-by: James Almer <[email protected]>
---
 libavformat/iamf_parse.c  | 84 +++++++++++++++++++++++++++++++++++----
 libavformat/iamf_reader.c | 32 +++++++++++++--
 libavformat/iamf_writer.c | 27 ++++++++++---
 3 files changed, 126 insertions(+), 17 deletions(-)

diff --git a/libavformat/iamf_parse.c b/libavformat/iamf_parse.c
index 29b8acd351..87f13069e6 100644
--- a/libavformat/iamf_parse.c
+++ b/libavformat/iamf_parse.c
@@ -406,6 +406,7 @@ static int scalable_channel_layout_config(void *s, 
AVIOContext *pb,
         int substream_count, coupled_substream_count;
         int expanded_loudspeaker_layout = -1;
         int ret, byte = avio_r8(pb);
+        int channels;
 
         layer = av_iamf_audio_element_add_layer(audio_element->element);
         if (!layer)
@@ -418,7 +419,8 @@ static int scalable_channel_layout_config(void *s, 
AVIOContext *pb,
         substream_count = avio_r8(pb);
         coupled_substream_count = avio_r8(pb);
 
-        if (substream_count + k > audio_element->nb_substreams)
+        if (!substream_count || coupled_substream_count > substream_count ||
+            substream_count + k > audio_element->nb_substreams)
             return AVERROR_INVALIDDATA;
 
         audio_element->layers[i].substream_count         = substream_count;
@@ -428,8 +430,14 @@ static int scalable_channel_layout_config(void *s, 
AVIOContext *pb,
             layer->output_gain = av_make_q(sign_extend(avio_rb16(pb), 16), 1 
<< 8);
         }
 
-        if (!i && loudspeaker_layout == 15)
+        if (loudspeaker_layout == 15) {
+            if (i) {
+                av_log(s, AV_LOG_ERROR, "expanded_loudspeaker_layout set with 
more than one layer in Audio Element #%d\n",
+                       audio_element->audio_element_id);
+                return AVERROR_INVALIDDATA;
+            }
             expanded_loudspeaker_layout = avio_r8(pb);
+        }
         if (expanded_loudspeaker_layout >= 0 && expanded_loudspeaker_layout < 
13) {
             av_channel_layout_copy(&ch_layout, 
&ff_iamf_expanded_scalable_ch_layouts[expanded_loudspeaker_layout]);
         } else if (loudspeaker_layout < 10) {
@@ -451,7 +459,13 @@ static int scalable_channel_layout_config(void *s, 
AVIOContext *pb,
             return AVERROR_PATCHWELCOME;
         }
 
-        if (i && ch_layout.nb_channels <= 
audio_element->element->layers[i-1]->ch_layout.nb_channels)
+        channels = ch_layout.nb_channels;
+        if (i) {
+            if (ch_layout.nb_channels <= 
audio_element->element->layers[i-1]->ch_layout.nb_channels)
+                return AVERROR_INVALIDDATA;
+            channels -= 
audio_element->element->layers[i-1]->ch_layout.nb_channels;
+        }
+        if (channels != substream_count + coupled_substream_count)
             return AVERROR_INVALIDDATA;
 
         for (int j = 0; j < substream_count; j++) {
@@ -489,6 +503,9 @@ static int scalable_channel_layout_config(void *s, 
AVIOContext *pb,
                 }
             }
 
+            if (n != ch_layout.nb_channels)
+                return AVERROR_INVALIDDATA;
+
             ret = av_channel_layout_retype(&layer->ch_layout, 
AV_CHANNEL_ORDER_NATIVE, 0);
             if (ret < 0 && ret != AVERROR(ENOSYS))
                 return ret;
@@ -590,6 +607,7 @@ static int ambisonics_config(void *s, AVIOContext *pb,
 static int param_parse(void *s, IAMFContext *c, AVIOContext *pb,
                        unsigned int type,
                        const IAMFAudioElement *audio_element,
+                       const IAMFCodecConfig *codec_config,
                        AVIAMFParamDefinition **out_param_definition)
 {
     IAMFParamDefinition *param_definition = NULL;
@@ -612,17 +630,29 @@ static int param_parse(void *s, IAMFContext *c, 
AVIOContext *pb,
 
     if (mode == 0) {
         duration = ffio_read_leb(pb);
-        if (!duration)
+        if (!duration || duration > av_rescale(codec_config->nb_samples,
+                                               codec_config->sample_rate, 
parameter_rate)) {
+            av_log(s, AV_LOG_ERROR, "Invalid block duration in parameter_id 
%u\n", parameter_id);
             return AVERROR_INVALIDDATA;
+        }
         constant_subblock_duration = ffio_read_leb(pb);
         if (constant_subblock_duration == 0)
             nb_subblocks = ffio_read_leb(pb);
         else {
+            if (constant_subblock_duration > duration) {
+                av_log(s, AV_LOG_ERROR, "Invalid block duration in 
parameter_id %u\n", parameter_id);
+                return AVERROR_INVALIDDATA;
+            }
             nb_subblocks = duration / constant_subblock_duration;
             total_duration = duration;
         }
     }
 
+    if (nb_subblocks > duration) {
+        av_log(s, AV_LOG_ERROR, "Invalid duration or subblock count in 
parameter_id %u\n", parameter_id);
+        return AVERROR_INVALIDDATA;
+    }
+
     param = av_iamf_param_definition_alloc(type, nb_subblocks, &param_size);
     if (!param)
         return AVERROR(ENOMEM);
@@ -633,6 +663,11 @@ static int param_parse(void *s, IAMFContext *c, 
AVIOContext *pb,
 
         if (constant_subblock_duration == 0) {
             subblock_duration = ffio_read_leb(pb);
+            if (subblock_duration > duration - total_duration) {
+                av_log(s, AV_LOG_ERROR, "Invalid subblock durations in 
parameter_id %u\n", parameter_id);
+                av_free(param);
+                return AVERROR_INVALIDDATA;
+            }
             total_duration += subblock_duration;
         } else if (i == nb_subblocks - 1)
             subblock_duration = duration - i * constant_subblock_duration;
@@ -859,13 +894,17 @@ static int audio_element_obu(void *s, IAMFContext *c, 
AVIOContext *pb, int len)
                 ret = AVERROR_INVALIDDATA;
                 goto fail;
             }
-            ret = param_parse(s, c, pbc, type, audio_element, 
&element->demixing_info);
+            ret = param_parse(s, c, pbc, type,
+                              audio_element, codec_config,
+                              &element->demixing_info);
         } else if (type == AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN) {
             if (element->recon_gain_info) {
                 ret = AVERROR_INVALIDDATA;
                 goto fail;
             }
-            ret = param_parse(s, c, pbc, type, audio_element, 
&element->recon_gain_info);
+            ret = param_parse(s, c, pbc, type,
+                              audio_element, codec_config,
+                              &element->recon_gain_info);
         } else {
             unsigned param_definition_size = ffio_read_leb(pbc);
             avio_skip(pbc, param_definition_size);
@@ -920,6 +959,7 @@ static int label_string(AVIOContext *pb, char **label)
 static int mix_presentation_obu(void *s, IAMFContext *c, AVIOContext *pb, int 
len)
 {
     AVIAMFMixPresentation *mix;
+    const IAMFCodecConfig *codec_config = NULL;
     IAMFMixPresentation **tmp, *mix_presentation = NULL;
     FFIOContext b;
     AVIOContext *pbc;
@@ -969,6 +1009,11 @@ static int mix_presentation_obu(void *s, IAMFContext *c, 
AVIOContext *pb, int le
     mix_presentation->cmix = mix;
 
     mix_presentation->count_label = ffio_read_leb(pbc);
+    if (mix_presentation->count_label > len - avio_tell(pbc)) {
+        mix_presentation->count_label = 0;
+        ret = AVERROR_INVALIDDATA;
+        goto fail;
+    }
     mix_presentation->language_label = av_calloc(mix_presentation->count_label,
                                                  
sizeof(*mix_presentation->language_label));
     if (!mix_presentation->language_label) {
@@ -995,6 +1040,12 @@ static int mix_presentation_obu(void *s, IAMFContext *c, 
AVIOContext *pb, int le
     }
 
     nb_submixes = ffio_read_leb(pbc);
+    if (!nb_submixes) {
+        av_log(s, AV_LOG_ERROR, "Mix presentation %u has no submixes\n", 
mix_presentation_id);
+        ret = AVERROR_INVALIDDATA;
+        goto fail;
+    }
+
     for (int i = 0; i < nb_submixes; i++) {
         AVIAMFSubmix *sub_mix;
         unsigned nb_elements, nb_layouts;
@@ -1006,9 +1057,17 @@ static int mix_presentation_obu(void *s, IAMFContext *c, 
AVIOContext *pb, int le
         }
 
         nb_elements = ffio_read_leb(pbc);
+        if (!nb_elements) {
+            av_log(s, AV_LOG_ERROR, "Submix %d from Mix presentation %u has no 
audio elements\n",
+                   i, mix_presentation_id);
+            ret = AVERROR_INVALIDDATA;
+            goto fail;
+        }
+
         for (int j = 0; j < nb_elements; j++) {
             AVIAMFSubmixElement *submix_element;
             IAMFAudioElement *audio_element = NULL;
+            const IAMFCodecConfig *config = NULL;
             unsigned int rendering_config_extension_size;
 
             submix_element = av_iamf_submix_add_element(sub_mix);
@@ -1031,6 +1090,7 @@ static int mix_presentation_obu(void *s, IAMFContext *c, 
AVIOContext *pb, int le
                 ret = AVERROR_INVALIDDATA;
                 goto fail;
             }
+            config = ff_iamf_get_codec_config(c, 
audio_element->codec_config_id);
 
             for (int k = 0; k < mix_presentation->count_label; k++) {
                 char *annotation = NULL;
@@ -1049,14 +1109,22 @@ static int mix_presentation_obu(void *s, IAMFContext 
*c, AVIOContext *pb, int le
             avio_skip(pbc, rendering_config_extension_size);
 
             ret = param_parse(s, c, pbc, AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN,
-                              NULL,
+                              audio_element, config,
                               &submix_element->element_mix_config);
             if (ret < 0)
                 goto fail;
             submix_element->default_mix_gain = 
av_make_q(sign_extend(avio_rb16(pbc), 16), 1 << 8);
+
+            if (!codec_config || (config->nb_samples >
+                                  av_rescale(codec_config->nb_samples,
+                                             codec_config->sample_rate,
+                                             config->sample_rate)))
+                codec_config = config;
         }
 
-        ret = param_parse(s, c, pbc, AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN, 
NULL, &sub_mix->output_mix_config);
+        ret = param_parse(s, c, pbc, AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN,
+                          NULL, codec_config,
+                          &sub_mix->output_mix_config);
         if (ret < 0)
             goto fail;
         sub_mix->default_mix_gain = av_make_q(sign_extend(avio_rb16(pbc), 16), 
1 << 8);
diff --git a/libavformat/iamf_reader.c b/libavformat/iamf_reader.c
index 2098f264aa..eab49a8fc0 100644
--- a/libavformat/iamf_reader.c
+++ b/libavformat/iamf_reader.c
@@ -105,6 +105,7 @@ static int audio_frame_obu(AVFormatContext *s, const 
IAMFDemuxContext *c,
 static int parameter_block_obu(AVFormatContext *s, IAMFDemuxContext *c,
                                AVIOContext *pbc, int len)
 {
+    const IAMFAudioElement *audio_element;
     const IAMFParamDefinition *param_definition;
     const AVIAMFParamDefinition *param;
     AVIAMFParamDefinition *out_param = NULL;
@@ -141,6 +142,7 @@ static int parameter_block_obu(AVFormatContext *s, 
IAMFDemuxContext *c,
         goto fail;
     }
 
+    audio_element = param_definition->audio_element;
     param = param_definition->param;
     if (!param_definition->mode) {
         duration = ffio_read_leb(pb);
@@ -148,10 +150,23 @@ static int parameter_block_obu(AVFormatContext *s, 
IAMFDemuxContext *c,
             ret = AVERROR_INVALIDDATA;
             goto fail;
         }
+        if (audio_element) {
+            const IAMFCodecConfig *codec_config = 
ff_iamf_get_codec_config(&c->iamf, audio_element->codec_config_id);
+            if (duration > av_rescale(codec_config->nb_samples, 
codec_config->sample_rate, param->parameter_rate)) {
+                av_log(s, AV_LOG_ERROR, "Invalid block duration in 
parameter_id %u\n", parameter_id);
+                ret = AVERROR_INVALIDDATA;
+                goto fail;
+            }
+        }
         constant_subblock_duration = ffio_read_leb(pb);
         if (constant_subblock_duration == 0)
             nb_subblocks = ffio_read_leb(pb);
         else {
+            if (constant_subblock_duration > duration) {
+                av_log(s, AV_LOG_ERROR, "Invalid block duration in 
parameter_id %u\n", parameter_id);
+                ret = AVERROR_INVALIDDATA;
+                goto fail;
+            }
             nb_subblocks = duration / constant_subblock_duration;
             total_duration = duration;
         }
@@ -161,6 +176,12 @@ static int parameter_block_obu(AVFormatContext *s, 
IAMFDemuxContext *c,
         nb_subblocks = param->nb_subblocks;
     }
 
+    if (nb_subblocks > duration) {
+        av_log(s, AV_LOG_ERROR, "Invalid duration or subblock count in 
parameter_id %u\n", parameter_id);
+        ret = AVERROR_INVALIDDATA;
+        goto fail;
+    }
+
     out_param = av_iamf_param_definition_alloc(param->type, nb_subblocks, 
&out_param_size);
     if (!out_param) {
         ret = AVERROR(ENOMEM);
@@ -180,6 +201,11 @@ static int parameter_block_obu(AVFormatContext *s, 
IAMFDemuxContext *c,
 
         if (!param_definition->mode && !constant_subblock_duration) {
             subblock_duration = ffio_read_leb(pb);
+            if (duration - total_duration > subblock_duration) {
+                av_log(s, AV_LOG_ERROR, "Invalid subblock durations in 
parameter_id %u\n", parameter_id);
+                ret = AVERROR_INVALIDDATA;
+                goto fail;
+            }
             total_duration += subblock_duration;
         } else if (i == nb_subblocks - 1)
             subblock_duration = duration - i * constant_subblock_duration;
@@ -214,10 +240,10 @@ static int parameter_block_obu(AVFormatContext *s, 
IAMFDemuxContext *c,
         }
         case AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN: {
             AVIAMFReconGain *recon = subblock;
-            const IAMFAudioElement *audio_element = 
param_definition->audio_element;
-            const AVIAMFAudioElement *element = audio_element->celement;
+            const AVIAMFAudioElement *element;
 
-            av_assert0(audio_element && element);
+            av_assert0(audio_element && audio_element->celement);
+            element = audio_element->celement;
             for (int i = 0; i < element->nb_layers; i++) {
                 const AVIAMFLayer *layer = element->layers[i];
                 if (layer->flags & AV_IAMF_LAYER_FLAG_RECON_GAIN) {
diff --git a/libavformat/iamf_writer.c b/libavformat/iamf_writer.c
index 4b2bd87004..dcd8dcfa19 100644
--- a/libavformat/iamf_writer.c
+++ b/libavformat/iamf_writer.c
@@ -100,12 +100,18 @@ static int populate_audio_roll_distance(IAMFCodecConfig 
*codec_config)
 }
 
 static int fill_codec_config(IAMFContext *iamf, const AVStreamGroup *stg,
-                             IAMFCodecConfig *codec_config)
+                             IAMFCodecConfig *codec_config, void *log_ctx)
 {
     const AVStream *st = stg->streams[0];
     IAMFCodecConfig **tmp;
     int j, ret = 0;
 
+    if (!st->codecpar->frame_size) {
+        av_log(log_ctx, AV_LOG_ERROR, "frame_size is unset for stream id %d\n",
+               st->id);
+        return AVERROR(EINVAL);
+    }
+
     codec_config->codec_id = st->codecpar->codec_id;
     codec_config->codec_tag = st->codecpar->codec_tag;
     switch (codec_config->codec_id) {
@@ -308,7 +314,7 @@ int ff_iamf_add_audio_element(IAMFContext *iamf, const 
AVStreamGroup *stg, void
     if (!codec_config)
         return AVERROR(ENOMEM);
 
-    ret = fill_codec_config(iamf, stg, codec_config);
+    ret = fill_codec_config(iamf, stg, codec_config, log_ctx);
     if (ret < 0) {
         av_free(codec_config);
         return ret;
@@ -733,8 +739,16 @@ static int iamf_write_audio_element(const IAMFContext 
*iamf,
         int layout = 0, expanded_layout = 0;
         get_loudspeaker_layout(element->layers[0], &layout, &expanded_layout);
         /* When the loudspeaker_layout = 15, the type 
PARAMETER_DEFINITION_DEMIXING SHALL NOT be present. */
-        if (layout == 15)
+        if (layout == 15) {
             param_definition_types &= ~AV_IAMF_PARAMETER_DEFINITION_DEMIXING;
+            /* expanded_loudspeaker_layout SHALL only be present when 
num_layers = 1 and loudspeaker_layout is set to 15 */
+            if (element->nb_layers > 1) {
+                av_log(log_ctx, AV_LOG_ERROR, "expanded_loudspeaker_layout 
present when using more than one layer in "
+                                              "Stream Group #%u\n",
+                       audio_element->audio_element_id);
+                return AVERROR(EINVAL);
+            }
+        }
         /* When the loudspeaker_layout of the (non-)scalable channel audio 
(i.e., num_layers = 1) is less than or equal to 3.1.2ch,
          * (i.e., Mono, Stereo, or 3.1.2ch), the type 
PARAMETER_DEFINITION_DEMIXING SHALL NOT be present. */
         else if (element->nb_layers == 1 && (layout == 0 || layout == 1 || 
layout == 8))
@@ -1222,9 +1236,10 @@ int ff_iamf_write_audio_frame(const IAMFContext *iamf, 
AVIOContext *pb,
 
     if (codec_config->codec_id == AV_CODEC_ID_OPUS) {
         // IAMF's num_samples_to_trim_at_start is the same as Opus's pre-skip.
-        skip_samples = pkt->dts < 0
-            ? av_rescale(-pkt->dts, 48000, pkt->time_base.den)
-            : 0;
+        if (!skip_samples)
+            skip_samples = pkt->dts < 0
+                ? av_rescale(-pkt->dts, 48000, pkt->time_base.den)
+                : 0;
         discard_padding = av_rescale(discard_padding, 48000, 
pkt->time_base.den);
     }
 
-- 
2.52.0

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

Reply via email to