audioconvert: Port to the new multichannel caps
authorSebastian Dröge <sebastian.droege@collabora.co.uk>
Mon, 19 Dec 2011 11:41:24 +0000 (12:41 +0100)
committerSebastian Dröge <sebastian.droege@collabora.co.uk>
Thu, 5 Jan 2012 09:34:19 +0000 (10:34 +0100)
audioconvert still needs support for mixing all the new
channel positions, see:
https://bugzilla.gnome.org/show_bug.cgi?id=666506

gst/audioconvert/gstaudioconvert.c
gst/audioconvert/gstchannelmix.c
gst/audioconvert/plugin.c

index dea382e..dcb139d 100644 (file)
@@ -275,6 +275,7 @@ gst_audio_convert_caps_remove_format_info (GstCaps * caps)
   GstStructure *st;
   gint i, n;
   GstCaps *res;
+  guint64 channel_mask;
 
   res = gst_caps_new_empty ();
 
@@ -288,8 +289,16 @@ gst_audio_convert_caps_remove_format_info (GstCaps * caps)
       continue;
 
     st = gst_structure_copy (st);
-    gst_structure_remove_fields (st, "format", "channel-positions", "channels",
-        NULL);
+    gst_structure_remove_field (st, "format");
+
+    /* Only remove the channels and channel-mask for non-NONE layouts */
+    if (gst_structure_get (st, "channel-mask", GST_TYPE_BITMASK, &channel_mask,
+            NULL)) {
+      if (channel_mask != 0)
+        gst_structure_remove_fields (st, "channel-mask", "channels", NULL);
+    } else {
+      gst_structure_remove_fields (st, "channel-mask", "channels", NULL);
+    }
 
     gst_caps_append_structure (res, st);
   }
@@ -327,7 +336,7 @@ gst_audio_convert_transform_caps (GstBaseTransform * btrans,
 static const GstAudioChannelPosition default_positions[8][8] = {
   /* 1 channel */
   {
-        GST_AUDIO_CHANNEL_POSITION_FRONT_MONO,
+        GST_AUDIO_CHANNEL_POSITION_MONO,
       },
   /* 2 channels */
   {
@@ -338,9 +347,9 @@ static const GstAudioChannelPosition default_positions[8][8] = {
   {
         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
-        GST_AUDIO_CHANNEL_POSITION_LFE, /* or FRONT_CENTER for 3.0? */
+        GST_AUDIO_CHANNEL_POSITION_LFE1,
       },
-  /* 4 channels (4.0 or 3.1?) */
+  /* 4 channels (4.0) */
   {
         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
@@ -355,68 +364,82 @@ static const GstAudioChannelPosition default_positions[8][8] = {
         GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
         GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
       },
-  /* 6 channels */
+  /* 6 channels (5.1) */
   {
         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
         GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
         GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
         GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
-        GST_AUDIO_CHANNEL_POSITION_LFE,
+        GST_AUDIO_CHANNEL_POSITION_LFE1,
       },
-  /* 7 channels */
+  /* 7 channels (6.1) */
   {
         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
         GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
         GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
         GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
-        GST_AUDIO_CHANNEL_POSITION_LFE,
+        GST_AUDIO_CHANNEL_POSITION_LFE1,
         GST_AUDIO_CHANNEL_POSITION_REAR_CENTER,
       },
-  /* 8 channels */
+  /* 8 channels (7.1) */
   {
         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
         GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
         GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
         GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
-        GST_AUDIO_CHANNEL_POSITION_LFE,
+        GST_AUDIO_CHANNEL_POSITION_LFE1,
         GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT,
         GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT,
       }
 };
 
-static const GValue *
-find_suitable_channel_layout (const GValue * val, guint chans)
+static gint
+n_bits_set (guint64 x)
 {
-  /* if output layout is fixed already and looks sane, we're done */
-  if (GST_VALUE_HOLDS_ARRAY (val) && gst_value_array_get_size (val) == chans)
-    return val;
+  gint i;
+  gint c = 0;
+  guint64 y = 1;
+
+  for (i = 0; i < 64; i++) {
+    if (x & y)
+      c++;
+    y <<= 1;
+  }
 
-  /* if it's a list, go through it recursively and return the first
-   * sane-enough looking value we find */
-  if (GST_VALUE_HOLDS_LIST (val)) {
-    gint i;
+  return c;
+}
 
-    for (i = 0; i < gst_value_list_get_size (val); ++i) {
-      const GValue *v, *ret;
+static guint64
+find_suitable_mask (guint64 mask, gint n_chans)
+{
+  guint64 intersection;
+  gint i;
 
-      v = gst_value_list_get_value (val, i);
-      if ((ret = find_suitable_channel_layout (v, chans)))
-        return ret;
-    }
-  }
+  i = 0;
+
+  g_assert (n_bits_set (mask) >= n_chans);
 
-  return NULL;
+  intersection = mask;
+  do {
+    intersection = intersection & ((~G_GUINT64_CONSTANT (0)) >> i);
+    i++;
+  } while (n_bits_set (intersection) > n_chans && i < 64);
+
+  if (i < 64)
+    return intersection;
+  return 0;
 }
 
 static void
 gst_audio_convert_fixate_channels (GstBaseTransform * base, GstStructure * ins,
     GstStructure * outs)
 {
-  const GValue *in_layout, *out_layout;
   gint in_chans, out_chans;
+  guint64 in_mask = 0, out_mask = 0;
+  gboolean has_in_mask = FALSE, has_out_mask = FALSE;
 
   if (!gst_structure_get_int (ins, "channels", &in_chans))
     return;                     /* this shouldn't really happen, should it? */
@@ -424,7 +447,7 @@ gst_audio_convert_fixate_channels (GstBaseTransform * base, GstStructure * ins,
   if (!gst_structure_has_field (outs, "channels")) {
     /* we could try to get the implied number of channels from the layout,
      * but that seems overdoing it for a somewhat exotic corner case */
-    gst_structure_remove_field (outs, "channel-positions");
+    gst_structure_remove_field (outs, "channel-mask");
     return;
   }
 
@@ -433,70 +456,101 @@ gst_audio_convert_fixate_channels (GstBaseTransform * base, GstStructure * ins,
 
   if (!gst_structure_get_int (outs, "channels", &out_chans)) {
     /* shouldn't really happen ... */
-    gst_structure_remove_field (outs, "channel-positions");
+    gst_structure_remove_field (outs, "channel-mask");
     return;
   }
 
-  /* check if the output has a channel layout (or a list of layouts) */
-  out_layout = gst_structure_get_value (outs, "channel-positions");
+  /* get the channel layout of the output if any */
+  has_out_mask = gst_structure_has_field (outs, "channel-mask");
+  if (has_out_mask) {
+    gst_structure_get (outs, "channel-mask", GST_TYPE_BITMASK, &out_mask, NULL);
+  } else {
+    /* channels == 1 => MONO */
+    if (out_chans == 2) {
+      out_mask =
+          GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT |
+          GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
+      has_out_mask = TRUE;
+    }
+  }
 
   /* get the channel layout of the input if any */
-  in_layout = gst_structure_get_value (ins, "channel-positions");
-
-  if (out_layout == NULL) {
-    if (out_chans <= 2 && (in_chans != out_chans || in_layout == NULL))
-      return;                   /* nothing to do, default layout will be assumed */
-    GST_WARNING_OBJECT (base, "downstream caps contain no channel layout");
+  has_in_mask = gst_structure_has_field (ins, "channel-mask");
+  if (has_in_mask) {
+    gst_structure_get (ins, "channel-mask", GST_TYPE_BITMASK, &in_mask, NULL);
+  } else {
+    /* channels == 1 => MONO */
+    if (in_chans == 2) {
+      in_mask =
+          GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT |
+          GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
+      has_in_mask = TRUE;
+    } else if (in_chans > 2)
+      g_warning ("%s: Upstream caps contain no channel mask",
+          GST_ELEMENT_NAME (base));
   }
 
-  if (in_chans == out_chans && in_layout != NULL) {
-    GValue res = { 0, };
+  if (!has_out_mask && out_chans == 1 && (in_chans != out_chans
+          || !has_in_mask))
+    return;                     /* nothing to do, default layout will be assumed */
 
+  if (in_chans == out_chans && (has_in_mask || in_chans == 1)) {
     /* same number of channels and no output layout: just use input layout */
-    if (out_layout == NULL) {
-      gst_structure_set_value (outs, "channel-positions", in_layout);
+    if (!has_out_mask) {
+      /* in_chans == 1 handled above already */
+      gst_structure_set (outs, "channel-mask", GST_TYPE_BITMASK, in_mask, NULL);
       return;
     }
 
-    /* if output layout is fixed already and looks sane, we're done */
-    if (GST_VALUE_HOLDS_ARRAY (out_layout) &&
-        gst_value_array_get_size (out_layout) == out_chans) {
+    /* If both masks are the same we're done, this includes the NONE layout case */
+    if (in_mask == out_mask)
       return;
-    }
 
-    /* if the output layout is not fixed, check if the output layout contains
-     * the input layout */
-    if (gst_value_intersect (&res, in_layout, out_layout)) {
-      gst_structure_set_value (outs, "channel-positions", in_layout);
-      g_value_unset (&res);
+    /* if output layout is fixed already and looks sane, we're done */
+    if (n_bits_set (out_mask) == out_chans)
       return;
-    }
 
-    /* output layout is not fixed and does not contain the input layout, so
-     * just pick the first layout in the list (it should be a list ...) */
-    if ((out_layout = find_suitable_channel_layout (out_layout, out_chans))) {
-      gst_structure_set_value (outs, "channel-positions", out_layout);
-      return;
+    if (n_bits_set (out_mask) < in_chans) {
+      /* Not much we can do here, this shouldn't just happen */
+      g_warning ("%s: Invalid downstream channel-mask with too few bits set",
+          GST_ELEMENT_NAME (base));
+    } else {
+      guint64 intersection;
+
+      /* if the output layout is not fixed, check if the output layout contains
+       * the input layout */
+      intersection = in_mask & out_mask;
+      if (n_bits_set (intersection) >= in_chans) {
+        gst_structure_set (outs, "channel-mask", GST_TYPE_BITMASK, in_mask,
+            NULL);
+        return;
+      }
+
+      /* output layout is not fixed and does not contain the input layout, so
+       * just pick the first possibility */
+      intersection = find_suitable_mask (out_mask, out_chans);
+      if (intersection) {
+        gst_structure_set (outs, "channel-mask", GST_TYPE_BITMASK, intersection,
+            NULL);
+        return;
+      }
     }
 
     /* ... else fall back to default layout (NB: out_layout is NULL here) */
     GST_WARNING_OBJECT (base, "unexpected output channel layout");
-  }
-
-  /* number of input channels != number of output channels:
-   * if this value contains a list of channel layouts (or even worse: a list
-   * with another list), just pick the first value and repeat until we find a
-   * channel position array or something else that's not a list; we assume
-   * the input if half-way sane and don't try to fall back on other list items
-   * if the first one is something unexpected or non-channel-pos-array-y */
-  if (out_layout != NULL && GST_VALUE_HOLDS_LIST (out_layout))
-    out_layout = find_suitable_channel_layout (out_layout, out_chans);
-
-  if (out_layout != NULL) {
-    if (GST_VALUE_HOLDS_ARRAY (out_layout) &&
-        gst_value_array_get_size (out_layout) == out_chans) {
-      /* looks sane enough, let's use it */
-      gst_structure_set_value (outs, "channel-positions", out_layout);
+  } else {
+    guint64 intersection;
+
+    /* number of input channels != number of output channels:
+     * if this value contains a list of channel layouts (or even worse: a list
+     * with another list), just pick the first value and repeat until we find a
+     * channel position array or something else that's not a list; we assume
+     * the input if half-way sane and don't try to fall back on other list items
+     * if the first one is something unexpected or non-channel-pos-array-y */
+    if (n_bits_set (out_mask) >= out_chans) {
+      intersection = find_suitable_mask (out_mask, out_chans);
+      gst_structure_set (outs, "channel-mask", GST_TYPE_BITMASK, intersection,
+          NULL);
       return;
     }
 
@@ -510,8 +564,18 @@ gst_audio_convert_fixate_channels (GstBaseTransform * base, GstStructure * ins,
    * layout based on LFE-presence in input layout, but let's save that for
    * another day) */
   if (out_chans > 0 && out_chans <= G_N_ELEMENTS (default_positions[0])) {
+    gint i;
+
     GST_DEBUG_OBJECT (base, "using default channel layout as fallback");
-    gst_audio_set_channel_positions (outs, default_positions[out_chans - 1]);
+
+    out_mask = 0;
+    for (i = 0; i < out_chans; i++)
+      out_mask |= default_positions[out_chans - 1][i];
+
+    gst_structure_set (outs, "channel-mask", GST_TYPE_BITMASK, out_mask, NULL);
+  } else {
+    GST_ERROR_OBJECT (base, "Have no default layout for %d channels",
+        out_chans);
   }
 }
 
index 2694780..21a70ca 100644 (file)
@@ -26,7 +26,6 @@
 
 #include <math.h>
 #include <string.h>
-#include <gst/audio/multichannel.h>
 
 #include "gstchannelmix.h"
 
@@ -96,7 +95,7 @@ gst_channel_mix_fill_compatible (AudioConvertCtx * this)
     { {
     GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
             GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, {
-    GST_AUDIO_CHANNEL_POSITION_FRONT_MONO}},
+    GST_AUDIO_CHANNEL_POSITION_MONO}},
         /* front center: 2 <-> 1 */
     { {
     GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
@@ -191,7 +190,7 @@ gst_channel_mix_detect_pos (GstAudioInfo * info,
 
   for (n = 0; n < info->channels; n++) {
     switch (info->position[n]) {
-      case GST_AUDIO_CHANNEL_POSITION_FRONT_MONO:
+      case GST_AUDIO_CHANNEL_POSITION_MONO:
         f[1] = n;
         *has_f = TRUE;
         break;
@@ -235,7 +234,7 @@ gst_channel_mix_detect_pos (GstAudioInfo * info,
         s[2] = n;
         *has_s = TRUE;
         break;
-      case GST_AUDIO_CHANNEL_POSITION_LFE:
+      case GST_AUDIO_CHANNEL_POSITION_LFE1:
         *has_b = TRUE;
         b[1] = n;
         break;
@@ -552,7 +551,7 @@ gst_channel_mix_fill_special (AudioConvertCtx * this)
               in->position[1] == GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT) ||
           (in->position[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT &&
               in->position[1] == GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT)) &&
-      out->position[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_MONO) {
+      out->position[0] == GST_AUDIO_CHANNEL_POSITION_MONO) {
     this->matrix[0][0] = 0.5;
     this->matrix[1][0] = 0.5;
     return TRUE;
@@ -561,7 +560,7 @@ gst_channel_mix_fill_special (AudioConvertCtx * this)
               out->position[1] == GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT) ||
           (out->position[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT &&
               out->position[1] == GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT)) &&
-      in->position[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_MONO) {
+      in->position[0] == GST_AUDIO_CHANNEL_POSITION_MONO) {
     this->matrix[0][0] = 1.0;
     this->matrix[0][1] = 1.0;
     return TRUE;
@@ -584,8 +583,7 @@ gst_channel_mix_fill_matrix (AudioConvertCtx * this)
 
   gst_channel_mix_fill_identical (this);
 
-  if (!GST_AUDIO_INFO_HAS_DEFAULT_POSITIONS (&this->in) &&
-      !GST_AUDIO_INFO_IS_UNPOSITIONED (&this->in)) {
+  if (!GST_AUDIO_INFO_IS_UNPOSITIONED (&this->in)) {
     gst_channel_mix_fill_compatible (this);
     gst_channel_mix_fill_others (this);
     gst_channel_mix_fill_normalize (this);
@@ -650,17 +648,20 @@ gboolean
 gst_channel_mix_passthrough (AudioConvertCtx * this)
 {
   gint i;
+  guint64 in_mask, out_mask;
 
   /* only NxN matrices can be identities */
   if (this->in.channels != this->out.channels)
     return FALSE;
 
-  /* this assumes a normalized matrix */
-  for (i = 0; i < this->in.channels; i++)
-    if (this->matrix[i][i] != 1.)
-      return FALSE;
+  /* passthrough if both channel masks are the same */
+  in_mask = out_mask = 0;
+  for (i = 0; i < this->in.channels; i++) {
+    in_mask |= this->in.position[i];
+    out_mask |= this->out.position[i];
+  }
 
-  return TRUE;
+  return in_mask == out_mask;
 }
 
 /* IMPORTANT: out_data == in_data is possible, make sure to not overwrite data
index 105f0e9..5218b7c 100644 (file)
 
 #include "plugin.h"
 
-#include <gst/audio/multichannel.h>
 #include "gstaudioconvertorc.h"
 
 static gboolean
 plugin_init (GstPlugin * plugin)
 {
-  /* ensure GstAudioChannelPosition type is registered */
-  if (!gst_audio_channel_position_get_type ())
-    return FALSE;
-
   if (!gst_element_register (plugin, "audioconvert",
           GST_RANK_PRIMARY, gst_audio_convert_get_type ()))
     return FALSE;