PR #23160 opened by chris-hld
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23160
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23160.patch


>From 7d29d2777c92c2c6f55c7af234a90754d8b3a50d Mon Sep 17 00:00:00 2001
From: chrishold <[email protected]>
Date: Tue, 19 May 2026 19:07:58 +0100
Subject: [PATCH] Handle CMF2, CMF3, and CMF255 with libopus decoder

---
 libavcodec/libopusdec.c | 37 ++++++++++++++++++++++++++++++-------
 1 file changed, 30 insertions(+), 7 deletions(-)

diff --git a/libavcodec/libopusdec.c b/libavcodec/libopusdec.c
index fc2119baff..6a992c2d70 100644
--- a/libavcodec/libopusdec.c
+++ b/libavcodec/libopusdec.c
@@ -65,19 +65,39 @@ static av_cold int libopus_decode_init(AVCodecContext *avc)
     avc->sample_rate    = 48000;
     avc->sample_fmt     = avc->request_sample_fmt == AV_SAMPLE_FMT_FLT ?
                           AV_SAMPLE_FMT_FLT : AV_SAMPLE_FMT_S16;
+    if (avc->extradata_size >= OPUS_HEAD_SIZE) {
+        opus->pre_skip = AV_RL16(avc->extradata + 10);
+        gain_db     = sign_extend(AV_RL16(avc->extradata + 16), 16);
+        channel_map = AV_RL8 (avc->extradata + 18);
+    }
+
     av_channel_layout_uninit(&avc->ch_layout);
-    if (channels > 8) {
+    if (channel_map == 2 || channel_map == 3) {
+        /* RFC 7845: channel mapping families 2 and 3 are SN3D Ambisonics in
+         * ACN order. Family 3 appends a stereo non-diegetic pair (FL+FR)
+         * after the (order+1)^2 ambisonic channels. */
+        int nondiegetic_channels = (channel_map == 3) ? 2 : 0;
+        int ambi_channels = channels - nondiegetic_channels;
+        int order = 0;
+        while ((order + 1) * (order + 1) < ambi_channels)
+            order++;
+        if (ambi_channels < 1 || (order + 1) * (order + 1) != ambi_channels) {
+            avc->ch_layout.order       = AV_CHANNEL_ORDER_UNSPEC;
+            avc->ch_layout.nb_channels = channels;
+        } else {
+            avc->ch_layout.order       = AV_CHANNEL_ORDER_AMBISONIC;
+            avc->ch_layout.nb_channels = channels;
+            avc->ch_layout.u.mask      = nondiegetic_channels ? 
AV_CH_LAYOUT_STEREO : 0;
+        }
+    } else if (channels > 8 || channel_map > 1) {
+        /* Channel mapping family != 1 (e.g. family 255) has discrete or
+         * undefined channel order. Do not impose a native (Vorbis) layout. */
         avc->ch_layout.order       = AV_CHANNEL_ORDER_UNSPEC;
         avc->ch_layout.nb_channels = channels;
     } else {
         av_channel_layout_copy(&avc->ch_layout, &ff_vorbis_ch_layouts[channels 
- 1]);
     }
 
-    if (avc->extradata_size >= OPUS_HEAD_SIZE) {
-        opus->pre_skip = AV_RL16(avc->extradata + 10);
-        gain_db     = sign_extend(AV_RL16(avc->extradata + 16), 16);
-        channel_map = AV_RL8 (avc->extradata + 18);
-    }
     if (avc->extradata_size >= OPUS_HEAD_SIZE + 2 + channels) {
         nb_streams = avc->extradata[OPUS_HEAD_SIZE + 0];
         nb_coupled = avc->extradata[OPUS_HEAD_SIZE + 1];
@@ -95,7 +115,10 @@ static av_cold int libopus_decode_init(AVCodecContext *avc)
         mapping    = mapping_arr;
     }
 
-    if (channels > 2 && channels <= 8) {
+    /* Only mapping family 1 uses Vorbis channel ordering. For families
+     * 0/2/3/255 the mapping array is in identity/discrete order and must
+     * be passed through unchanged. */
+    if (channel_map == 1 && channels > 2 && channels <= 8) {
         const uint8_t *vorbis_offset = 
ff_vorbis_channel_layout_offsets[channels - 1];
         int ch;
 
-- 
2.52.0

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

Reply via email to