PR #24963 opened by superuser404 URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24963 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24963.patch
`complexity_index_type_a` is read from the independent substream header only, and re-read on every packet. When the ETSI TS 103 420 object metadata is carried by a **dependent** substream, as with an AC-3 core followed by an E-AC-3 dependent frame (the usual Blu-ray layout for DD+ with Atmos), the independent header has no extension, so `mov_write_eac3_tag()` writes the short `dec3` form and `flag_ec3_extension_type_a` is lost. Players that select object audio from that flag then render the channel bed only. The AC-3 parser already reads `addbsi` for dependent frames; `handle_eac3()` just never takes the value from them. This takes it from every substream of the access unit and keeps the maximum, as is already done for `data_rate`, so a later packet can no longer reset it to the independent header's 0. ### Reproduction (existing FATE sample) `fate-suite/eac3/the_great_wall_7.1.eac3` is such a stream: an AC-3 core (`bsid 6`) plus an E-AC-3 dependent substream, which `ffprobe` reports as `Dolby Digital Plus + Dolby Atmos`, 7.1. ``` ffmpeg -i the_great_wall_7.1.eac3 -c copy -f mp4 out.mp4 ``` `dec3` box in `out.mp4`: - master (`f10fdd6310` included, so `chan_loc` is already right): `00 00 00 0e 64 65 63 33 12 00 0c 0f 02 02` - with this patch: `00 00 00 10 64 65 63 33 12 00 0c 0f 02 02 01 0c` (extension present, `complexity_index_type_a` 12) Since the independent substream produced no extension before, the value comes from the dependent one. This is the only change in the `lavf-fate-eac3.mp4` reference: the file grows by these two bytes, and the packet CRC is unchanged. A second stream with the objects in the dependent substream (5.1 AC-3 core, dependent `chanmap` 0xA010, `complexity_index_type_a` 16), muxed through the mp4 muxer with the codec parameters set by hand: `14 00 0c 0f 02 40` before, `14 00 0c 0f 02 40 01 10` after, with a trailer-written moov and with `-movflags frag_keyframe` alike. An AC-3 core without a dependent substream keeps its short box (`14 00 0c 0f 00`). ### Tests `fate-ac3`, `fate-eac3`, `fate-lavf-fate-eac3.mp4` (reference updated), `fate-hls-fmp4_ac3`, `fate-lavf-mov`, `fate-lavf-mp4`, `fate-lavf-ismv`, `fate-lavf-mov_rtphint`, `fate-mov-mp4-frag-flush`, `fate-api-movenc`, all passing on aarch64 macOS. >From 369be41ea59993a5c621a1152e48f4c8c8d0f941 Mon Sep 17 00:00:00 2001 From: Vincent Herbst <[email protected]> Date: Fri, 9 Oct 2026 06:05:57 +0200 Subject: [PATCH] avformat/movenc: keep the JOC extension of a dependent substream in dec3 complexity_index_type_a was read from the independent substream header only, and re-read on every packet. When the object metadata (ETSI TS 103 420) is carried by a dependent substream, as with an AC-3 core followed by an E-AC-3 dependent frame, the independent header has no extension, so the dec3 box was written in its short form and lost flag_ec3_extension_type_a. Players that select object audio from that flag then render the channel bed only. Take the value from every substream of the access unit and keep its maximum, as is already done for data_rate. fate-suite eac3/the_great_wall_7.1.eac3 is such a stream (ffprobe reports "Dolby Digital Plus + Dolby Atmos"); its dec3 now carries the extension with complexity_index_type_a 12, which is the two-byte change in the lavf-fate-eac3.mp4 reference. The packet CRC is unchanged. Signed-off-by: Vincent Herbst <[email protected]> --- libavformat/movenc.c | 8 +++++++- tests/ref/lavf-fate/eac3.mp4 | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/libavformat/movenc.c b/libavformat/movenc.c index 6492f4ffb3..dd4b8c2372 100644 --- a/libavformat/movenc.c +++ b/libavformat/movenc.c @@ -502,7 +502,8 @@ static int handle_eac3(MOVMuxContext *mov, AVPacket *pkt, MOVTrack *track) info->data_rate = FFMAX(info->data_rate, hdr->bit_rate / 1000); info->ac3_bit_rate_code = FFMAX(info->ac3_bit_rate_code, hdr->ac3_bit_rate_code); - info->complexity_index_type_a = hdr->complexity_index_type_a; + info->complexity_index_type_a = FFMAX(info->complexity_index_type_a, + hdr->complexity_index_type_a); num_blocks = hdr->num_blocks; @@ -591,6 +592,11 @@ static int handle_eac3(MOVMuxContext *mov, AVPacket *pkt, MOVTrack *track) } else { info->substream[parent].chan_loc |= hdr->channel_mode; } + /* the JOC extension may be carried by a dependent substream + * only, e.g. an AC-3 core with the objects in an E-AC-3 + * dependent frame (ETSI TS 103 420) */ + info->complexity_index_type_a = FFMAX(info->complexity_index_type_a, + hdr->complexity_index_type_a); cumul_size += hdr->frame_size; } } diff --git a/tests/ref/lavf-fate/eac3.mp4 b/tests/ref/lavf-fate/eac3.mp4 index 5eb0945586..e65688c972 100644 --- a/tests/ref/lavf-fate/eac3.mp4 +++ b/tests/ref/lavf-fate/eac3.mp4 @@ -1,3 +1,3 @@ -a6381a110c8ae457934e1ba7e9f157cb *tests/data/lavf-fate/lavf.eac3.mp4 -724180 tests/data/lavf-fate/lavf.eac3.mp4 +45262fcf8767de6ee502b754c5de87ce *tests/data/lavf-fate/lavf.eac3.mp4 +724182 tests/data/lavf-fate/lavf.eac3.mp4 tests/data/lavf-fate/lavf.eac3.mp4 CRC=0x7a2b48f0 -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
