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]

Reply via email to