[FFmpeg-devel,RFC] channel_layout: add support for ambisonics

Submitted by Rostislav Pehlivanov on Nov. 29, 2018, 2:02 a.m.

Details

Message ID 20181129020243.20905-1-atomnuker@gmail.com
State New
Headers show

Commit Message

Rostislav Pehlivanov Nov. 29, 2018, 2:02 a.m.
This is an RFC to add support for tagging channel layouts as ambisonics
in a backward-compatible way.
For now ambisonics up to third order are supported.
The functions have been updated to support and propagate the
AV_CH_LAYOUT_AMBISONICS flag.
This is messy but does not require a new API for layouts. Perhaps the
new proposed API might be a better solution, comments are welcome.
---
 doc/APIchanges             |  4 ++
 libavutil/channel_layout.c | 85 +++++++++++++++++++++-----------------
 libavutil/channel_layout.h | 19 ++++++++-
 libavutil/version.h        |  4 +-
 4 files changed, 72 insertions(+), 40 deletions(-)

Comments

Marton Balint Nov. 29, 2018, 9:55 p.m.
On Thu, 29 Nov 2018, Rostislav Pehlivanov wrote:

> This is an RFC to add support for tagging channel layouts as ambisonics
> in a backward-compatible way.
> For now ambisonics up to third order are supported.
> The functions have been updated to support and propagate the
> AV_CH_LAYOUT_AMBISONICS flag.
> This is messy but does not require a new API for layouts. Perhaps the
> new proposed API might be a better solution, comments are welcome.
> ---
> doc/APIchanges             |  4 ++
> libavutil/channel_layout.c | 85 +++++++++++++++++++++-----------------
> libavutil/channel_layout.h | 19 ++++++++-
> libavutil/version.h        |  4 +-
> 4 files changed, 72 insertions(+), 40 deletions(-)

Using separate channel_layout bits for each (up to 16) ambisonic channel 
seems a lot cleaner.

Regards,
Marton
Michael Niedermayer Nov. 30, 2018, 12:57 a.m.
On Thu, Nov 29, 2018 at 02:02:43AM +0000, Rostislav Pehlivanov wrote:
> This is an RFC to add support for tagging channel layouts as ambisonics
> in a backward-compatible way.
> For now ambisonics up to third order are supported.
> The functions have been updated to support and propagate the
> AV_CH_LAYOUT_AMBISONICS flag.
> This is messy but does not require a new API for layouts. Perhaps the
> new proposed API might be a better solution, comments are welcome.
> ---
>  doc/APIchanges             |  4 ++
>  libavutil/channel_layout.c | 85 +++++++++++++++++++++-----------------
>  libavutil/channel_layout.h | 19 ++++++++-
>  libavutil/version.h        |  4 +-
>  4 files changed, 72 insertions(+), 40 deletions(-)

breaks make fate
make: *** [fate-filter-join] Error 1
make: *** [fate-filter-channelsplit] Error 1


[...]

Patch hide | download patch | download mbox

diff --git a/doc/APIchanges b/doc/APIchanges
index db1879e6e2..88e2d0764b 100644
--- a/doc/APIchanges
+++ b/doc/APIchanges
@@ -15,6 +15,10 @@  libavutil:     2017-10-21
 
 API changes, most recent first:
 
+2018-xx-xx - xxxxxxxxxx - lavu 56.25.100 - channel_layout.h
+  Add AV_CH_LAYOUT_AMBISONICS, AV_CH_LAYOUT_AMBISONICS_1ST,
+  AV_CH_LAYOUT_AMBISONICS_2ND and AV_CH_LAYOUT_AMBISONICS_3RD.
+
 -------- 8< --------- FFmpeg 4.1 was cut here -------- 8< ---------
 
 2018-10-27 - 718044dc19 - lavu 56.21.100 - pixdesc.h
diff --git a/libavutil/channel_layout.c b/libavutil/channel_layout.c
index 3bd5ee29b7..30297138fe 100644
--- a/libavutil/channel_layout.c
+++ b/libavutil/channel_layout.c
@@ -33,42 +33,45 @@ 
 
 struct channel_name {
     const char *name;
+    const char *ambisonics_name;
     const char *description;
+    const char *ambisonics_description;
 };
 
 static const struct channel_name channel_names[] = {
-     [0] = { "FL",        "front left"            },
-     [1] = { "FR",        "front right"           },
-     [2] = { "FC",        "front center"          },
-     [3] = { "LFE",       "low frequency"         },
-     [4] = { "BL",        "back left"             },
-     [5] = { "BR",        "back right"            },
-     [6] = { "FLC",       "front left-of-center"  },
-     [7] = { "FRC",       "front right-of-center" },
-     [8] = { "BC",        "back center"           },
-     [9] = { "SL",        "side left"             },
-    [10] = { "SR",        "side right"            },
-    [11] = { "TC",        "top center"            },
-    [12] = { "TFL",       "top front left"        },
-    [13] = { "TFC",       "top front center"      },
-    [14] = { "TFR",       "top front right"       },
-    [15] = { "TBL",       "top back left"         },
-    [16] = { "TBC",       "top back center"       },
-    [17] = { "TBR",       "top back right"        },
-    [29] = { "DL",        "downmix left"          },
-    [30] = { "DR",        "downmix right"         },
-    [31] = { "WL",        "wide left"             },
-    [32] = { "WR",        "wide right"            },
-    [33] = { "SDL",       "surround direct left"  },
-    [34] = { "SDR",       "surround direct right" },
-    [35] = { "LFE2",      "low frequency 2"       },
+     [0] = { "FL",   "1",  "front left",            "ambisonics component 1"  },
+     [1] = { "FR",   "2",  "front right",           "ambisonics component 2"  },
+     [2] = { "FC",   "0",  "front center",          "ambisonics component 0"  },
+     [3] = { "LFE",  "3",  "low frequency",         "ambisonics component 3"  },
+     [4] = { "BL",   "4",  "back left",             "ambisonics component 4"  },
+     [5] = { "BR",   "5",  "back right",            "ambisonics component 5"  },
+     [6] = { "FLC",  "6",  "front left-of-center",  "ambisonics component 6"  },
+     [7] = { "FRC",  "7",  "front right-of-center", "ambisonics component 7"  },
+     [8] = { "BC",   "8",  "back center",           "ambisonics component 8"  },
+     [9] = { "SL",   "9",  "side left",             "ambisonics component 9"  },
+    [10] = { "SR",   "10", "side right",            "ambisonics component 10" },
+    [11] = { "TC",   "11", "top center",            "ambisonics component 11" },
+    [12] = { "TFL",  "12", "top front left",        "ambisonics component 12" },
+    [13] = { "TFC",  "13", "top front center",      "ambisonics component 13" },
+    [14] = { "TFR",  "14", "top front right",       "ambisonics component 14" },
+    [15] = { "TBL",  "15", "top back left",         "ambisonics component 15" },
+    [16] = { "TBC",  NULL, "top back center",       NULL                      },
+    [17] = { "TBR",  NULL, "top back right",        NULL                      },
+    [29] = { "DL",   NULL, "downmix left",          NULL                      },
+    [30] = { "DR",   NULL, "downmix right",         NULL                      },
+    [31] = { "WL",   NULL, "wide left",             NULL                      },
+    [32] = { "WR",   NULL, "wide right",            NULL                      },
+    [33] = { "SDL",  NULL, "surround direct left",  NULL                      },
+    [34] = { "SDR",  NULL, "surround direct right", NULL                      },
+    [35] = { "LFE2", NULL, "low frequency 2",       NULL                      },
 };
 
-static const char *get_channel_name(int channel_id)
+static const char *get_channel_name(int channel_id, int ambisonics)
 {
     if (channel_id < 0 || channel_id >= FF_ARRAY_ELEMS(channel_names))
         return NULL;
-    return channel_names[channel_id].name;
+    return ambisonics ? channel_names[channel_id].ambisonics_name :
+                        channel_names[channel_id].name;
 }
 
 static const struct {
@@ -104,6 +107,10 @@  static const struct {
     { "octagonal",   8,  AV_CH_LAYOUT_OCTAGONAL },
     { "hexadecagonal", 16, AV_CH_LAYOUT_HEXADECAGONAL },
     { "downmix",     2,  AV_CH_LAYOUT_STEREO_DOWNMIX, },
+    { "ambisonics_0th", 1, AV_CH_LAYOUT_AMBISONICS | AV_CH_LAYOUT_MONO },
+    { "ambisonics_1st", 4, AV_CH_LAYOUT_AMBISONICS | (1 << 4) - 1 },
+    { "ambisonics_2nd", 9, AV_CH_LAYOUT_AMBISONICS | (1 << 9) - 1 },
+    { "ambisonics_3rd", 16, AV_CH_LAYOUT_AMBISONICS | (1 << 16) - 1 },
 };
 
 static uint64_t get_channel_layout_single(const char *name, int name_len)
@@ -194,8 +201,8 @@  void av_bprint_channel_layout(struct AVBPrint *bp,
         int i, ch;
         av_bprintf(bp, " (");
         for (i = 0, ch = 0; i < 64; i++) {
-            if ((channel_layout & (UINT64_C(1) << i))) {
-                const char *name = get_channel_name(i);
+            if (((channel_layout & ~AV_CH_LAYOUT_AMBISONICS) & (UINT64_C(1) << i))) {
+                const char *name = get_channel_name(i, channel_layout & AV_CH_LAYOUT_AMBISONICS);
                 if (name) {
                     if (ch > 0)
                         av_bprintf(bp, "+");
@@ -219,7 +226,7 @@  void av_get_channel_layout_string(char *buf, int buf_size,
 
 int av_get_channel_layout_nb_channels(uint64_t channel_layout)
 {
-    return av_popcount64(channel_layout);
+    return av_popcount64(channel_layout & ~AV_CH_LAYOUT_AMBISONICS);
 }
 
 int64_t av_get_default_channel_layout(int nb_channels) {
@@ -233,7 +240,7 @@  int64_t av_get_default_channel_layout(int nb_channels) {
 int av_get_channel_layout_channel_index(uint64_t channel_layout,
                                         uint64_t channel)
 {
-    if (!(channel_layout & channel) ||
+    if (!((channel_layout & ~AV_CH_LAYOUT_AMBISONICS) & channel) ||
         av_get_channel_layout_nb_channels(channel) != 1)
         return AVERROR(EINVAL);
     channel_layout &= channel - 1;
@@ -246,8 +253,8 @@  const char *av_get_channel_name(uint64_t channel)
     if (av_get_channel_layout_nb_channels(channel) != 1)
         return NULL;
     for (i = 0; i < 64; i++)
-        if ((1ULL<<i) & channel)
-            return get_channel_name(i);
+        if ((1ULL<<i) & (channel & ~AV_CH_LAYOUT_AMBISONICS))
+            return get_channel_name(i, channel & AV_CH_LAYOUT_AMBISONICS);
     return NULL;
 }
 
@@ -257,8 +264,12 @@  const char *av_get_channel_description(uint64_t channel)
     if (av_get_channel_layout_nb_channels(channel) != 1)
         return NULL;
     for (i = 0; i < FF_ARRAY_ELEMS(channel_names); i++)
-        if ((1ULL<<i) & channel)
-            return channel_names[i].description;
+        if ((1ULL<<i) & (channel & ~AV_CH_LAYOUT_AMBISONICS)) {
+            if (channel & AV_CH_LAYOUT_AMBISONICS)
+                return channel_names[i].ambisonics_description;
+            else
+                return channel_names[i].description;
+        }
     return NULL;
 }
 
@@ -270,8 +281,8 @@  uint64_t av_channel_layout_extract_channel(uint64_t channel_layout, int index)
         return 0;
 
     for (i = 0; i < 64; i++) {
-        if ((1ULL << i) & channel_layout && !index--)
-            return 1ULL << i;
+        if ((1ULL << i) & (channel_layout & ~AV_CH_LAYOUT_AMBISONICS) && !index--)
+            return (1ULL << i) & (channel_layout & AV_CH_LAYOUT_AMBISONICS);
     }
     return 0;
 }
diff --git a/libavutil/channel_layout.h b/libavutil/channel_layout.h
index 50bb8f03c5..8fd006647d 100644
--- a/libavutil/channel_layout.h
+++ b/libavutil/channel_layout.h
@@ -72,6 +72,17 @@ 
 #define AV_CH_SURROUND_DIRECT_RIGHT  0x0000000400000000ULL
 #define AV_CH_LOW_FREQUENCY_2        0x0000000800000000ULL
 
+/** Indicates channel layout is ambisonics.
+    If this is flagged, the number of channels must be either 1, 4, 9 or 16
+    All functions will work as stated, with the flag being preserved where
+    possible.
+    The layout, channel descriptions and names follow the "Ambisonics Channel
+    Number (ACN)" specifications, published as
+    [Michael Chapman et al.,
+     A standard for interchange of Ambisonic signal sets,
+     Ambisonics Symposium, Graz 2009] */
+#define AV_CH_LAYOUT_AMBISONICS      0x4000000000000000ULL
+
 /** Channel mask value used for AVCodecContext.request_channel_layout
     to indicate that the user requests the channel order of the decoder output
     to be the native codec channel order. */
@@ -111,6 +122,11 @@ 
 #define AV_CH_LAYOUT_HEXADECAGONAL     (AV_CH_LAYOUT_OCTAGONAL|AV_CH_WIDE_LEFT|AV_CH_WIDE_RIGHT|AV_CH_TOP_BACK_LEFT|AV_CH_TOP_BACK_RIGHT|AV_CH_TOP_BACK_CENTER|AV_CH_TOP_FRONT_CENTER|AV_CH_TOP_FRONT_LEFT|AV_CH_TOP_FRONT_RIGHT)
 #define AV_CH_LAYOUT_STEREO_DOWNMIX    (AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT)
 
+#define AV_CH_LAYOUT_AMBISONICS_0TH    (AV_CH_LAYOUT_MONO | AV_CH_LAYOUT_AMBISONICS)
+#define AV_CH_LAYOUT_AMBISONICS_1ST    (((1 << 4) - 1) | AV_CH_LAYOUT_AMBISONICS)
+#define AV_CH_LAYOUT_AMBISONICS_2ND    (((1 << 9) - 1) | AV_CH_LAYOUT_AMBISONICS)
+#define AV_CH_LAYOUT_AMBISONICS_3ST    (((1 << 16) - 1) | AV_CH_LAYOUT_AMBISONICS)
+
 enum AVMatrixEncoding {
     AV_MATRIX_ENCODING_NONE,
     AV_MATRIX_ENCODING_DOLBY,
@@ -200,6 +216,7 @@  uint64_t av_channel_layout_extract_channel(uint64_t channel_layout, int index);
 /**
  * Get the name of a given channel.
  *
+ * @param channel  a channel layout with a single channel and a flag
  * @return channel name on success, NULL on error.
  */
 const char *av_get_channel_name(uint64_t channel);
@@ -207,7 +224,7 @@  const char *av_get_channel_name(uint64_t channel);
 /**
  * Get the description of a given channel.
  *
- * @param channel  a channel layout with a single channel
+ * @param channel  a channel layout with a single channel and a flag
  * @return  channel description on success, NULL on error
  */
 const char *av_get_channel_description(uint64_t channel);
diff --git a/libavutil/version.h b/libavutil/version.h
index 62112a6049..f9976151a7 100644
--- a/libavutil/version.h
+++ b/libavutil/version.h
@@ -79,8 +79,8 @@ 
  */
 
 #define LIBAVUTIL_VERSION_MAJOR  56
-#define LIBAVUTIL_VERSION_MINOR  24
-#define LIBAVUTIL_VERSION_MICRO 101
+#define LIBAVUTIL_VERSION_MINOR  25
+#define LIBAVUTIL_VERSION_MICRO 100
 
 #define LIBAVUTIL_VERSION_INT   AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \
                                                LIBAVUTIL_VERSION_MINOR, \