diff mbox series

[FFmpeg-devel] avcodec/libopusenc: add support for mapping family 2

Message ID 20241017024408.27575-1-jamrial@gmail.com
State New
Headers show
Series [FFmpeg-devel] avcodec/libopusenc: add support for mapping family 2 | expand

Commit Message

James Almer Oct. 17, 2024, 2:44 a.m. UTC
Signed-off-by: James Almer <jamrial@gmail.com>
---
 libavcodec/libopusenc.c | 55 +++++++++++++++++++++++++++++++----------
 1 file changed, 42 insertions(+), 13 deletions(-)
diff mbox series

Patch

diff --git a/libavcodec/libopusenc.c b/libavcodec/libopusenc.c
index 6b8b2cda0e..fcd0254be3 100644
--- a/libavcodec/libopusenc.c
+++ b/libavcodec/libopusenc.c
@@ -191,21 +191,25 @@  static int libopus_check_max_channels(AVCodecContext *avctx,
 }
 
 static int libopus_check_vorbis_layout(AVCodecContext *avctx, int mapping_family) {
-    av_assert2(avctx->ch_layout.nb_channels < FF_ARRAY_ELEMS(ff_vorbis_ch_layouts));
-
     if (avctx->ch_layout.order == AV_CHANNEL_ORDER_UNSPEC) {
         av_log(avctx, AV_LOG_WARNING,
                "No channel layout specified. Opus encoder will use Vorbis "
                "channel layout for %d channels.\n", avctx->ch_layout.nb_channels);
-    } else if (av_channel_layout_compare(&avctx->ch_layout, &ff_vorbis_ch_layouts[avctx->ch_layout.nb_channels - 1])) {
-        char name[32];
-
-        av_channel_layout_describe(&avctx->ch_layout, name, sizeof(name));
-        av_log(avctx, AV_LOG_ERROR,
-               "Invalid channel layout %s for specified mapping family %d.\n",
-               name, mapping_family);
-
-        return AVERROR(EINVAL);
+    } else if (avctx->ch_layout.order == AV_CHANNEL_ORDER_NATIVE) {
+        av_assert2(avctx->ch_layout.nb_channels < FF_ARRAY_ELEMS(ff_vorbis_ch_layouts));
+        if (av_channel_layout_compare(&avctx->ch_layout, &ff_vorbis_ch_layouts[avctx->ch_layout.nb_channels - 1])) {
+            char name[32];
+            av_channel_layout_describe(&avctx->ch_layout, name, sizeof(name));
+            av_log(avctx, AV_LOG_ERROR,
+                   "Invalid channel layout %s for specified mapping family %d.\n",
+                   name, mapping_family);
+
+            return AVERROR(EINVAL);
+        }
+    } else if (avctx->ch_layout.order != AV_CHANNEL_ORDER_AMBISONIC) {
+        av_log(avctx, AV_LOG_WARNING,
+               "Unsupported channel layout specified. Opus encoder will use Vorbis "
+               "channel layout for %d channels.\n", avctx->ch_layout.nb_channels);
     }
 
     return 0;
@@ -221,7 +225,8 @@  static int libopus_validate_layout_and_get_channel_map(
 
     switch (mapping_family) {
     case -1:
-        ret = libopus_check_max_channels(avctx, 8);
+        ret = libopus_check_max_channels(avctx, avctx->ch_layout.order == AV_CHANNEL_ORDER_AMBISONIC
+                                                ? 227 : 8);
         if (ret == 0) {
             ret = libopus_check_vorbis_layout(avctx, mapping_family);
             /* Channels do not need to be reordered. */
@@ -242,6 +247,9 @@  static int libopus_validate_layout_and_get_channel_map(
             channel_map = ff_vorbis_channel_layout_offsets[avctx->ch_layout.nb_channels - 1];
         }
         break;
+    case 2:
+        ret = libopus_check_max_channels(avctx, 227);
+        break;
     case 255:
         ret = libopus_check_max_channels(avctx, 254);
         break;
@@ -345,7 +353,7 @@  static av_cold int libopus_encode_init(AVCodecContext *avctx)
         return av_ret;
     }
 
-    if (opus->opts.mapping_family == -1) {
+    if (opus->opts.mapping_family == -1 && avctx->ch_layout.order != AV_CHANNEL_ORDER_AMBISONIC) {
         /* By default, use mapping family 1 for the header but use the older
          * libopus multistream API to avoid surround masking. */
 
@@ -367,6 +375,27 @@  static av_cold int libopus_encode_init(AVCodecContext *avctx)
          * mapping and coupled stream counts to its internal defaults and will
          * use surround masking analysis to save bits. */
         mapping_family = opus->opts.mapping_family;
+        if (mapping_family == -1) {
+            int ambisonic_order, non_diegetic_channels;
+
+            mapping_family = 2;
+            av_assert2(avctx->ch_layout.order == AV_CHANNEL_ORDER_AMBISONIC);
+            ambisonic_order = av_channel_layout_ambisonic_order(&avctx->ch_layout);
+            if (ambisonic_order < 0) {
+                av_log(avctx, AV_LOG_ERROR, "Invalid ambisonic channel layout for mapping channel 2\n");
+                ret = AVERROR(EINVAL);
+                goto fail;
+            }
+
+            non_diegetic_channels = channels - (ambisonic_order + 1) * (ambisonic_order + 1);
+            if (non_diegetic_channels &&
+                (non_diegetic_channels != 2 ||
+                 av_channel_layout_subset(&avctx->ch_layout, AV_CH_LAYOUT_STEREO) != AV_CH_LAYOUT_STEREO)) {
+                av_log(avctx, AV_LOG_ERROR, "Invalid amount of non-diegetic channels for mapping channel 2\n");
+                ret = AVERROR(EINVAL);
+                goto fail;
+            }
+        }
         enc = opus_multistream_surround_encoder_create(
             avctx->sample_rate, channels, mapping_family,
             &opus->stream_count, &coupled_stream_count, libopus_channel_mapping,