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, ¶m_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]
