Signed-off-by: Vittorio Giovara <vittorio.giov...@gmail.com>
---
 libavutil/channel_layout.c | 86 ++++++++++++++++++++++++++++++++++++++++++++--
 libavutil/channel_layout.h | 33 ++++++++++++++++++
 2 files changed, 116 insertions(+), 3 deletions(-)

diff --git a/libavutil/channel_layout.c b/libavutil/channel_layout.c
index 285997446d..d4791a9b61 100644
--- a/libavutil/channel_layout.c
+++ b/libavutil/channel_layout.c
@@ -260,7 +260,7 @@ void av_channel_layout_from_mask(AVChannelLayout 
*channel_layout,
 int av_channel_layout_from_string(AVChannelLayout *channel_layout,
                                   const char *str)
 {
-    int i, channels;
+    int i, channels, order;
     const char *dup = str;
     uint64_t mask = 0;
 
@@ -309,6 +309,44 @@ int av_channel_layout_from_string(AVChannelLayout 
*channel_layout,
         return 0;
     }
 
+    /* ambisonic */
+    if (sscanf(str, "ambisonic channels %d order %d", &channels, &order) == 2) 
{
+        AVChannelLayout extra = {0};
+        int harmonics = 0;
+
+        // handle nondiegetic channels or half-sphere harmonics
+        dup = str;
+        while (*dup) {
+            char *chname = av_get_token(&dup, "|");
+            if (!chname)
+                return AVERROR(ENOMEM);
+            if (*dup)
+                dup++; // skip separator
+
+            // no extra channel found
+            if (!strcmp(chname, str))
+                break;
+
+            if (av_channel_from_string(chname) == AV_CHAN_AMBISONIC)
+                harmonics++;
+            else {
+                char *nondiegetic = strstr(str, chname);
+                int ret = av_channel_layout_from_string(&extra, nondiegetic);
+                // no other channels allowed after nondiegetic
+                av_free(chname);
+                if (ret < 0)
+                    return ret;
+                break;
+            }
+            av_free(chname);
+        }
+
+        channel_layout->nb_channels = channels + harmonics + extra.nb_channels;
+        channel_layout->u.mask = extra.u.mask;
+
+        return 0;
+    }
+
     return AVERROR_INVALIDDATA;
 }
 
@@ -361,6 +399,35 @@ char *av_channel_layout_describe(const AVChannelLayout 
*channel_layout)
         }
         return ret;
         }
+    case AV_CHANNEL_ORDER_AMBISONIC: {
+        char buf[64];
+        int order = floor(sqrt(channel_layout->nb_channels)) - 1;
+        int channels = (order + 1) * (order + 1);
+
+        snprintf(buf, sizeof(buf), "ambisonic channels %d order %d",
+                 channels, order);
+
+        // handle nondiegetic channels or half-sphere harmonics
+        for (i = channels; i < channel_layout->nb_channels; i++) {
+            enum AVChannel chan = 
av_channel_layout_get_channel(channel_layout, i);
+            if (chan == AV_CHAN_AMBISONIC) {
+                av_strlcat(buf, "|", sizeof(buf));
+                av_strlcat(buf, av_channel_name(chan), sizeof(buf));
+            }
+        }
+        if (channel_layout->u.mask) {
+            AVChannelLayout extra = {0};
+            char *chlstr;
+
+            av_channel_layout_from_mask(&extra, channel_layout->u.mask);
+            chlstr = av_channel_layout_describe(&extra);
+            av_strlcat(buf, "|", sizeof(buf));
+            av_strlcat(buf, chlstr, sizeof(buf));
+            av_free(chlstr);
+        }
+
+        return av_strdup(buf);
+        }
     case AV_CHANNEL_ORDER_UNSPEC: {
         char buf[64];
         snprintf(buf, sizeof(buf), "%d channels", channel_layout->nb_channels);
@@ -381,6 +448,11 @@ int av_channel_layout_get_channel(const AVChannelLayout 
*channel_layout, int idx
     switch (channel_layout->order) {
     case AV_CHANNEL_ORDER_CUSTOM:
         return channel_layout->u.map[idx];
+    case AV_CHANNEL_ORDER_AMBISONIC:
+        idx -= channel_layout->nb_channels - 
av_popcount64(channel_layout->u.mask);
+        if (idx < 0)
+            return AV_CHAN_AMBISONIC;
+        // fall-through
     case AV_CHANNEL_ORDER_NATIVE:
         for (i = 0; i < 64; i++) {
             if ((1ULL << i) & channel_layout->u.mask && !idx--)
@@ -394,7 +466,7 @@ int av_channel_layout_get_channel(const AVChannelLayout 
*channel_layout, int idx
 int av_channel_layout_channel_index(const AVChannelLayout *channel_layout,
                                     enum AVChannel channel)
 {
-    int i;
+    int i, off = 0;
 
     switch (channel_layout->order) {
     case AV_CHANNEL_ORDER_CUSTOM:
@@ -402,12 +474,17 @@ int av_channel_layout_channel_index(const AVChannelLayout 
*channel_layout,
             if (channel_layout->u.map[i] == channel)
                 return i;
         return AVERROR(EINVAL);
+    case AV_CHANNEL_ORDER_AMBISONIC:
+        if (channel == AV_CHAN_AMBISONIC)
+            return 0;
+        off = channel_layout->nb_channels - 
av_popcount64(channel_layout->u.mask);
+        // fall-through
     case AV_CHANNEL_ORDER_NATIVE: {
         uint64_t mask = channel_layout->u.mask;
         if (!(mask & (1ULL << channel)))
             return AVERROR(EINVAL);
         mask &= (1ULL << channel) - 1;
-        return av_popcount64(mask);
+        return av_popcount64(mask) + off;
         }
     default:
         return AVERROR(EINVAL);
@@ -422,6 +499,9 @@ int av_channel_layout_check(const AVChannelLayout 
*channel_layout)
     switch (channel_layout->order) {
     case AV_CHANNEL_ORDER_NATIVE:
         return av_popcount64(channel_layout->u.mask) == 
channel_layout->nb_channels;
+    case AV_CHANNEL_ORDER_AMBISONIC:
+        return channel_layout->nb_channels != 2 &&
+               channel_layout->nb_channels >= 
av_popcount64(channel_layout->u.mask);
     case AV_CHANNEL_ORDER_CUSTOM:
         return !!channel_layout->u.map;
     case AV_CHANNEL_ORDER_UNSPEC:
diff --git a/libavutil/channel_layout.h b/libavutil/channel_layout.h
index 2604ad1537..bd78cc9ff9 100644
--- a/libavutil/channel_layout.h
+++ b/libavutil/channel_layout.h
@@ -68,6 +68,8 @@ enum AVChannel {
 
     /** Channel is empty can be safely skipped. */
     AV_CHAN_SILENCE = 64,
+    /** Channel represents an ambisonic component. */
+    AV_CHAN_AMBISONIC,
 };
 
 enum AVChannelOrder {
@@ -88,6 +90,26 @@ enum AVChannelOrder {
      * about the channel order.
      */
     AV_CHANNEL_ORDER_UNSPEC,
+    /**
+     * Each channel represents a different speaker position, also known as
+     * ambisonic components. Channels are ordered according to ACN (Ambisonic
+     * Channel Number), and they follow these mathematical properties:
+     *
+     * @code{.unparsed}
+     *   ACN = n * (n + 1) + m
+     *   n   = floor(sqrt(k)) - 1,
+     *   m   = k - n * (n + 1) - 1.
+     * @endcode
+     *
+     * for order n and degree m; the ACN component corresponds to channel
+     * index as k = ACN + 1. In case non-diegetic channels are present,
+     * they are always the last ones, and mask is initialized with a correct
+     * layout.
+     *
+     * Normalization is assumed to be SN3D (Schmidt Semi-Normalization)
+     * as defined in AmbiX format ยง 2.1.
+     */
+    AV_CHANNEL_ORDER_AMBISONIC,
 };
 
 
@@ -224,6 +246,10 @@ typedef struct AVChannelLayout {
          * modified manually (i.e.  not using any of the av_channel_layout_*
          * functions), the code doing it must ensure that the number of set 
bits
          * is equal to nb_channels.
+         *
+         * This member maybe be optionially used for 
AV_CHANNEL_ORDER_AMBISONIC.
+         * It is a bitmask that indicates the channel layout of the last
+         * non-diegetic channels present in the stream.
          */
         uint64_t mask;
         /**
@@ -295,6 +321,8 @@ typedef struct AVChannelLayout {
     { .order = AV_CHANNEL_ORDER_NATIVE, .nb_channels = 16, .u = { .mask = 
AV_CH_LAYOUT_HEXAGONAL }}
 #define AV_CHANNEL_LAYOUT_STEREO_DOWNMIX \
     { .order = AV_CHANNEL_ORDER_NATIVE, .nb_channels = 2,  .u = { .mask = 
AV_CH_LAYOUT_STEREO_DOWNMIX }}
+#define AV_CHANNEL_LAYOUT_AMBISONIC_FIRST_ORDER \
+    { .order = AV_CHANNEL_ORDER_AMBISONIC, .nb_channels = 4, .u = { .mask = 0 
}}
 
 #if FF_API_OLD_CHANNEL_LAYOUT
 /**
@@ -403,6 +431,8 @@ void av_channel_layout_from_mask(AVChannelLayout 
*channel_layout, uint64_t mask)
  *  - a hexadecimal value of a channel layout (eg. "0x4")
  *  - the number of channels with default layout (eg. "5")
  *  - the number of unordered channels (eg. "4 channels")
+ *  - the ambisonic channel count and order followed by optional non-diegetic
+ *    channels (eg. "ambisonic channels 9 order 2|stereo")
  *
  * @param channel_layout input channel layout
  * @param str string describing the channel layout
@@ -452,6 +482,9 @@ int av_channel_layout_get_channel(const AVChannelLayout 
*channel_layout, int idx
 /**
  * Get the index of a given channel in a channel layout.
  *
+ * @note AV_CHAN_AMBISONIC will always be at index 0: callers need to use the
+ * ACN mathematical properties to determine the order.
+ *
  * @return index of channel in channel_layout on success or a negative number 
if
  *         channel is not present in channel_layout.
  */
-- 
2.13.1

_______________________________________________
libav-devel mailing list
libav-devel@libav.org
https://lists.libav.org/mailman/listinfo/libav-devel

Reply via email to