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

LLM-GPT-6-ASTRA investigation: a 997 Hz tone placed only in BL or BR of 7.1, or 
BC of 6.1, measures −19.520 LUFS instead of the mono reference's −21.010 LUFS 
because the filter unconditionally gives these channels weight 1.41.

Implement the layout distinction described by jlivingston in #24284: when both 
side channels are present, assign the rear pair weight 1.00; otherwise preserve 
the 1.41 surround weighting used by 5.x(back). Give BC weight 1.00 
unconditionally. This follows Tables 4 and 5 of Recommendation ITU-R BS.1770-5. 
Use `av_channel_layout_subset()` to inspect channel presence independently of 
channel order.

Add seven synthetic FATE tests covering BL/BR in 7.1, BC in 6.1, SL/SR in 7.1, 
and BL/BR in 5.1. Expected output is stored in `tests/ref/fate`; no new 
external samples are required.

Validation:

- On parent 404c2e47cf9a, the three affected-channel tests fail and the four 
controls pass.
- All seven new tests and both existing ebur128 metadata tests pass with the 
fix on aarch64 and x86-64.
- A 121-case single-channel sweep across 13 layouts confirms the intended 
rear/back-center changes and unchanged controls, including height and LFE 
channels.

Fixes #24284.


From 294ddbc4757605aa84f78c46c381070b1fb302e0 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Mon, 5 Oct 2026 23:27:33 +0000
Subject: [PATCH] avfilter/ebur128: correct back-channel loudness weights

ITU-R BS.1770-5 Tables 4 and 5 assign weight 1.0 to the M+/-135
rear pair and M180 back center. The fixed channel-name mask gave
all back channels weight 1.41.

Use the presence of both side channels to distinguish the rear pair
from the +/-110-degree surround pair used by 5.x(back). Keep the
latter at 1.41, and remove back center from the boosted channels.
Use av_channel_layout_subset() so the lookup also handles custom
channel orders.

Implement the layout distinction described by jlivingston in #24284.
Add seven synthetic FATE tests covering both rear channels, back
center, both side channels, and both 5.1 surround channels.

The three affected-channel tests fail before the change. All seven
new tests and the two existing ebur128 metadata tests pass on aarch64
and x86-64 after it. No external samples are needed for the new tests.

Fixes #24284.

Assisted-by: Fairy
---
 libavfilter/f_ebur128.c                       | 15 ++++++-----
 tests/fate/filter-video.mak                   | 25 +++++++++++++++++--
 .../fate/filter-metadata-ebur128-back-center  |  4 +++
 .../fate/filter-metadata-ebur128-rear-left    |  4 +++
 .../fate/filter-metadata-ebur128-rear-right   |  4 +++
 .../fate/filter-metadata-ebur128-side-left    |  4 +++
 .../fate/filter-metadata-ebur128-side-right   |  4 +++
 .../filter-metadata-ebur128-surround-left     |  4 +++
 .../filter-metadata-ebur128-surround-right    |  4 +++
 9 files changed, 60 insertions(+), 8 deletions(-)
 create mode 100644 tests/ref/fate/filter-metadata-ebur128-back-center
 create mode 100644 tests/ref/fate/filter-metadata-ebur128-rear-left
 create mode 100644 tests/ref/fate/filter-metadata-ebur128-rear-right
 create mode 100644 tests/ref/fate/filter-metadata-ebur128-side-left
 create mode 100644 tests/ref/fate/filter-metadata-ebur128-side-right
 create mode 100644 tests/ref/fate/filter-metadata-ebur128-surround-left
 create mode 100644 tests/ref/fate/filter-metadata-ebur128-surround-right

diff --git a/libavfilter/f_ebur128.c b/libavfilter/f_ebur128.c
index f2744fb8e6..574e865c76 100644
--- a/libavfilter/f_ebur128.c
+++ b/libavfilter/f_ebur128.c
@@ -432,12 +432,15 @@ static int config_audio_output(AVFilterLink *outlink)
     AVFilterContext *ctx = outlink->src;
     EBUR128Context *ebur128 = ctx->priv;
     const int nb_channels = outlink->ch_layout.nb_channels;
+    const uint64_t side_mask = AV_CH_SIDE_LEFT | AV_CH_SIDE_RIGHT;
+    uint64_t weight_1_41_mask = side_mask | AV_CH_SURROUND_DIRECT_LEFT |
+                                         AV_CH_SURROUND_DIRECT_RIGHT;
 
-    /* Height channels always use a weight of 1.0 in ITU-R BS.1770. */
-#define WEIGHT_1_41_MASK (AV_CH_BACK_LEFT             |AV_CH_BACK_CENTER       
   | \
-                          AV_CH_BACK_RIGHT            |AV_CH_SIDE_LEFT         
   | \
-                          AV_CH_SIDE_RIGHT            
|AV_CH_SURROUND_DIRECT_LEFT | \
-                          AV_CH_SURROUND_DIRECT_RIGHT)
+    /* With a side pair, the back pair is at +/-135 degrees (weight 1.0).
+     * Otherwise retain the +/-110-degree surround weighting of 5.x(back).
+     * Back center and height channels use 1.0 in ITU-R BS.1770. */
+    if (av_channel_layout_subset(&outlink->ch_layout, side_mask) != side_mask)
+        weight_1_41_mask |= AV_CH_BACK_LEFT | AV_CH_BACK_RIGHT;
 
     ebur128->nb_channels  = nb_channels;
     ebur128->dsp.y        = av_calloc(nb_channels, 3 * 
sizeof(*ebur128->dsp.y));
@@ -471,7 +474,7 @@ static int config_audio_output(AVFilterLink *outlink)
         const enum AVChannel chl = 
av_channel_layout_channel_from_index(&outlink->ch_layout, i);
         if (chl == AV_CHAN_LOW_FREQUENCY || chl == AV_CHAN_LOW_FREQUENCY_2) {
             ebur128->ch_weighting[i] = 0;
-        } else if (chl < 64 && (1ULL << chl) & WEIGHT_1_41_MASK) {
+        } else if (chl < 64 && (1ULL << chl) & weight_1_41_mask) {
             ebur128->ch_weighting[i] = 1.41;
         } else {
             ebur128->ch_weighting[i] = 1.0;
diff --git a/tests/fate/filter-video.mak b/tests/fate/filter-video.mak
index ebbc0a7d1c..961a11875c 100644
--- a/tests/fate/filter-video.mak
+++ b/tests/fate/filter-video.mak
@@ -923,10 +923,31 @@ FATE_METADATA_FILTER-$(call ALLYES, 
$(EBUR128_METADATA_DEPS)) += fate-filter-met
 fate-filter-metadata-ebur128: SRC = 
$(TARGET_SAMPLES)/filter/seq-3341-7_seq-3342-5-24bit.flac
 fate-filter-metadata-ebur128: CMD = run $(FILTER_METADATA_COMMAND) 
"amovie='$(SRC)',ebur128=peak=sample:metadata=1"
 
-EBUR128_HEIGHT_DEPS = FFPROBE LAVFI_INDEV AEVALSRC_FILTER ARESAMPLE_FILTER 
EBUR128_FILTER
-FATE_FILTER_FFPROBE-$(call ALLYES, $(EBUR128_HEIGHT_DEPS)) += 
fate-filter-metadata-ebur128-height
+EBUR128_CHANNEL_DEPS = FFPROBE LAVFI_INDEV AEVALSRC_FILTER ARESAMPLE_FILTER 
EBUR128_FILTER
+FATE_FILTER_FFPROBE-$(call ALLYES, $(EBUR128_CHANNEL_DEPS)) += 
fate-filter-metadata-ebur128-height
 fate-filter-metadata-ebur128-height: CMD = run $(FILTER_METADATA_COMMAND) 
"aevalsrc=0.12589*sin(2*PI*997*t):channel_layout=TBL:sample_rate=48000:duration=0.4,ebur128=metadata=1"
 
+FATE_FILTER_FFPROBE-$(call ALLYES, $(EBUR128_CHANNEL_DEPS)) += 
fate-filter-metadata-ebur128-rear-left
+fate-filter-metadata-ebur128-rear-left: CMD = run $(FILTER_METADATA_COMMAND) 
"aevalsrc=0|0|0|0|0.12589*sin(2*PI*997*t)|0|0|0:channel_layout=7.1:sample_rate=48000:duration=0.4,ebur128=metadata=1"
+
+FATE_FILTER_FFPROBE-$(call ALLYES, $(EBUR128_CHANNEL_DEPS)) += 
fate-filter-metadata-ebur128-rear-right
+fate-filter-metadata-ebur128-rear-right: CMD = run $(FILTER_METADATA_COMMAND) 
"aevalsrc=0|0|0|0|0|0.12589*sin(2*PI*997*t)|0|0:channel_layout=7.1:sample_rate=48000:duration=0.4,ebur128=metadata=1"
+
+FATE_FILTER_FFPROBE-$(call ALLYES, $(EBUR128_CHANNEL_DEPS)) += 
fate-filter-metadata-ebur128-back-center
+fate-filter-metadata-ebur128-back-center: CMD = run $(FILTER_METADATA_COMMAND) 
"aevalsrc=0|0|0|0|0.12589*sin(2*PI*997*t)|0|0:channel_layout=6.1:sample_rate=48000:duration=0.4,ebur128=metadata=1"
+
+FATE_FILTER_FFPROBE-$(call ALLYES, $(EBUR128_CHANNEL_DEPS)) += 
fate-filter-metadata-ebur128-side-left
+fate-filter-metadata-ebur128-side-left: CMD = run $(FILTER_METADATA_COMMAND) 
"aevalsrc=0|0|0|0|0|0|0.12589*sin(2*PI*997*t)|0:channel_layout=7.1:sample_rate=48000:duration=0.4,ebur128=metadata=1"
+
+FATE_FILTER_FFPROBE-$(call ALLYES, $(EBUR128_CHANNEL_DEPS)) += 
fate-filter-metadata-ebur128-side-right
+fate-filter-metadata-ebur128-side-right: CMD = run $(FILTER_METADATA_COMMAND) 
"aevalsrc=0|0|0|0|0|0|0|0.12589*sin(2*PI*997*t):channel_layout=7.1:sample_rate=48000:duration=0.4,ebur128=metadata=1"
+
+FATE_FILTER_FFPROBE-$(call ALLYES, $(EBUR128_CHANNEL_DEPS)) += 
fate-filter-metadata-ebur128-surround-left
+fate-filter-metadata-ebur128-surround-left: CMD = run 
$(FILTER_METADATA_COMMAND) 
"aevalsrc=0|0|0|0|0.12589*sin(2*PI*997*t)|0:channel_layout=5.1:sample_rate=48000:duration=0.4,ebur128=metadata=1"
+
+FATE_FILTER_FFPROBE-$(call ALLYES, $(EBUR128_CHANNEL_DEPS)) += 
fate-filter-metadata-ebur128-surround-right
+fate-filter-metadata-ebur128-surround-right: CMD = run 
$(FILTER_METADATA_COMMAND) 
"aevalsrc=0|0|0|0|0|0.12589*sin(2*PI*997*t):channel_layout=5.1:sample_rate=48000:duration=0.4,ebur128=metadata=1"
+
 READVITC_METADATA_DEPS = LAVFI_INDEV MOVIE_FILTER \
                          AVI_DEMUXER FFVHUFF_DECODER READVITC_FILTER
 FATE_METADATA_FILTER-$(call ALLYES, $(READVITC_METADATA_DEPS)) += 
fate-filter-metadata-readvitc-def
diff --git a/tests/ref/fate/filter-metadata-ebur128-back-center 
b/tests/ref/fate/filter-metadata-ebur128-back-center
new file mode 100644
index 0000000000..f182793368
--- /dev/null
+++ b/tests/ref/fate/filter-metadata-ebur128-back-center
@@ -0,0 +1,4 @@
+pts=0|tag:lavfi.r128.M=-120.691|tag:lavfi.r128.S=-120.691|tag:lavfi.r128.I=-70.000|tag:lavfi.r128.LRA=0.000|tag:lavfi.r128.LRA.low=0.000|tag:lavfi.r128.LRA.high=0.000
+pts=4800|tag:lavfi.r128.M=-120.691|tag:lavfi.r128.S=-120.691|tag:lavfi.r128.I=-70.000|tag:lavfi.r128.LRA=0.000|tag:lavfi.r128.LRA.low=0.000|tag:lavfi.r128.LRA.high=0.000
+pts=9600|tag:lavfi.r128.M=-120.691|tag:lavfi.r128.S=-120.691|tag:lavfi.r128.I=-70.000|tag:lavfi.r128.LRA=0.000|tag:lavfi.r128.LRA.low=0.000|tag:lavfi.r128.LRA.high=0.000
+pts=14400|tag:lavfi.r128.M=-21.010|tag:lavfi.r128.S=-120.691|tag:lavfi.r128.I=-21.010|tag:lavfi.r128.LRA=0.000|tag:lavfi.r128.LRA.low=0.000|tag:lavfi.r128.LRA.high=0.000
diff --git a/tests/ref/fate/filter-metadata-ebur128-rear-left 
b/tests/ref/fate/filter-metadata-ebur128-rear-left
new file mode 100644
index 0000000000..f182793368
--- /dev/null
+++ b/tests/ref/fate/filter-metadata-ebur128-rear-left
@@ -0,0 +1,4 @@
+pts=0|tag:lavfi.r128.M=-120.691|tag:lavfi.r128.S=-120.691|tag:lavfi.r128.I=-70.000|tag:lavfi.r128.LRA=0.000|tag:lavfi.r128.LRA.low=0.000|tag:lavfi.r128.LRA.high=0.000
+pts=4800|tag:lavfi.r128.M=-120.691|tag:lavfi.r128.S=-120.691|tag:lavfi.r128.I=-70.000|tag:lavfi.r128.LRA=0.000|tag:lavfi.r128.LRA.low=0.000|tag:lavfi.r128.LRA.high=0.000
+pts=9600|tag:lavfi.r128.M=-120.691|tag:lavfi.r128.S=-120.691|tag:lavfi.r128.I=-70.000|tag:lavfi.r128.LRA=0.000|tag:lavfi.r128.LRA.low=0.000|tag:lavfi.r128.LRA.high=0.000
+pts=14400|tag:lavfi.r128.M=-21.010|tag:lavfi.r128.S=-120.691|tag:lavfi.r128.I=-21.010|tag:lavfi.r128.LRA=0.000|tag:lavfi.r128.LRA.low=0.000|tag:lavfi.r128.LRA.high=0.000
diff --git a/tests/ref/fate/filter-metadata-ebur128-rear-right 
b/tests/ref/fate/filter-metadata-ebur128-rear-right
new file mode 100644
index 0000000000..f182793368
--- /dev/null
+++ b/tests/ref/fate/filter-metadata-ebur128-rear-right
@@ -0,0 +1,4 @@
+pts=0|tag:lavfi.r128.M=-120.691|tag:lavfi.r128.S=-120.691|tag:lavfi.r128.I=-70.000|tag:lavfi.r128.LRA=0.000|tag:lavfi.r128.LRA.low=0.000|tag:lavfi.r128.LRA.high=0.000
+pts=4800|tag:lavfi.r128.M=-120.691|tag:lavfi.r128.S=-120.691|tag:lavfi.r128.I=-70.000|tag:lavfi.r128.LRA=0.000|tag:lavfi.r128.LRA.low=0.000|tag:lavfi.r128.LRA.high=0.000
+pts=9600|tag:lavfi.r128.M=-120.691|tag:lavfi.r128.S=-120.691|tag:lavfi.r128.I=-70.000|tag:lavfi.r128.LRA=0.000|tag:lavfi.r128.LRA.low=0.000|tag:lavfi.r128.LRA.high=0.000
+pts=14400|tag:lavfi.r128.M=-21.010|tag:lavfi.r128.S=-120.691|tag:lavfi.r128.I=-21.010|tag:lavfi.r128.LRA=0.000|tag:lavfi.r128.LRA.low=0.000|tag:lavfi.r128.LRA.high=0.000
diff --git a/tests/ref/fate/filter-metadata-ebur128-side-left 
b/tests/ref/fate/filter-metadata-ebur128-side-left
new file mode 100644
index 0000000000..35914bc544
--- /dev/null
+++ b/tests/ref/fate/filter-metadata-ebur128-side-left
@@ -0,0 +1,4 @@
+pts=0|tag:lavfi.r128.M=-120.691|tag:lavfi.r128.S=-120.691|tag:lavfi.r128.I=-70.000|tag:lavfi.r128.LRA=0.000|tag:lavfi.r128.LRA.low=0.000|tag:lavfi.r128.LRA.high=0.000
+pts=4800|tag:lavfi.r128.M=-120.691|tag:lavfi.r128.S=-120.691|tag:lavfi.r128.I=-70.000|tag:lavfi.r128.LRA=0.000|tag:lavfi.r128.LRA.low=0.000|tag:lavfi.r128.LRA.high=0.000
+pts=9600|tag:lavfi.r128.M=-120.691|tag:lavfi.r128.S=-120.691|tag:lavfi.r128.I=-70.000|tag:lavfi.r128.LRA=0.000|tag:lavfi.r128.LRA.low=0.000|tag:lavfi.r128.LRA.high=0.000
+pts=14400|tag:lavfi.r128.M=-19.517|tag:lavfi.r128.S=-120.691|tag:lavfi.r128.I=-19.520|tag:lavfi.r128.LRA=0.000|tag:lavfi.r128.LRA.low=0.000|tag:lavfi.r128.LRA.high=0.000
diff --git a/tests/ref/fate/filter-metadata-ebur128-side-right 
b/tests/ref/fate/filter-metadata-ebur128-side-right
new file mode 100644
index 0000000000..35914bc544
--- /dev/null
+++ b/tests/ref/fate/filter-metadata-ebur128-side-right
@@ -0,0 +1,4 @@
+pts=0|tag:lavfi.r128.M=-120.691|tag:lavfi.r128.S=-120.691|tag:lavfi.r128.I=-70.000|tag:lavfi.r128.LRA=0.000|tag:lavfi.r128.LRA.low=0.000|tag:lavfi.r128.LRA.high=0.000
+pts=4800|tag:lavfi.r128.M=-120.691|tag:lavfi.r128.S=-120.691|tag:lavfi.r128.I=-70.000|tag:lavfi.r128.LRA=0.000|tag:lavfi.r128.LRA.low=0.000|tag:lavfi.r128.LRA.high=0.000
+pts=9600|tag:lavfi.r128.M=-120.691|tag:lavfi.r128.S=-120.691|tag:lavfi.r128.I=-70.000|tag:lavfi.r128.LRA=0.000|tag:lavfi.r128.LRA.low=0.000|tag:lavfi.r128.LRA.high=0.000
+pts=14400|tag:lavfi.r128.M=-19.517|tag:lavfi.r128.S=-120.691|tag:lavfi.r128.I=-19.520|tag:lavfi.r128.LRA=0.000|tag:lavfi.r128.LRA.low=0.000|tag:lavfi.r128.LRA.high=0.000
diff --git a/tests/ref/fate/filter-metadata-ebur128-surround-left 
b/tests/ref/fate/filter-metadata-ebur128-surround-left
new file mode 100644
index 0000000000..35914bc544
--- /dev/null
+++ b/tests/ref/fate/filter-metadata-ebur128-surround-left
@@ -0,0 +1,4 @@
+pts=0|tag:lavfi.r128.M=-120.691|tag:lavfi.r128.S=-120.691|tag:lavfi.r128.I=-70.000|tag:lavfi.r128.LRA=0.000|tag:lavfi.r128.LRA.low=0.000|tag:lavfi.r128.LRA.high=0.000
+pts=4800|tag:lavfi.r128.M=-120.691|tag:lavfi.r128.S=-120.691|tag:lavfi.r128.I=-70.000|tag:lavfi.r128.LRA=0.000|tag:lavfi.r128.LRA.low=0.000|tag:lavfi.r128.LRA.high=0.000
+pts=9600|tag:lavfi.r128.M=-120.691|tag:lavfi.r128.S=-120.691|tag:lavfi.r128.I=-70.000|tag:lavfi.r128.LRA=0.000|tag:lavfi.r128.LRA.low=0.000|tag:lavfi.r128.LRA.high=0.000
+pts=14400|tag:lavfi.r128.M=-19.517|tag:lavfi.r128.S=-120.691|tag:lavfi.r128.I=-19.520|tag:lavfi.r128.LRA=0.000|tag:lavfi.r128.LRA.low=0.000|tag:lavfi.r128.LRA.high=0.000
diff --git a/tests/ref/fate/filter-metadata-ebur128-surround-right 
b/tests/ref/fate/filter-metadata-ebur128-surround-right
new file mode 100644
index 0000000000..35914bc544
--- /dev/null
+++ b/tests/ref/fate/filter-metadata-ebur128-surround-right
@@ -0,0 +1,4 @@
+pts=0|tag:lavfi.r128.M=-120.691|tag:lavfi.r128.S=-120.691|tag:lavfi.r128.I=-70.000|tag:lavfi.r128.LRA=0.000|tag:lavfi.r128.LRA.low=0.000|tag:lavfi.r128.LRA.high=0.000
+pts=4800|tag:lavfi.r128.M=-120.691|tag:lavfi.r128.S=-120.691|tag:lavfi.r128.I=-70.000|tag:lavfi.r128.LRA=0.000|tag:lavfi.r128.LRA.low=0.000|tag:lavfi.r128.LRA.high=0.000
+pts=9600|tag:lavfi.r128.M=-120.691|tag:lavfi.r128.S=-120.691|tag:lavfi.r128.I=-70.000|tag:lavfi.r128.LRA=0.000|tag:lavfi.r128.LRA.low=0.000|tag:lavfi.r128.LRA.high=0.000
+pts=14400|tag:lavfi.r128.M=-19.517|tag:lavfi.r128.S=-120.691|tag:lavfi.r128.I=-19.520|tag:lavfi.r128.LRA=0.000|tag:lavfi.r128.LRA.low=0.000|tag:lavfi.r128.LRA.high=0.000
-- 
2.52.0

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

Reply via email to