gst/audioconvert/: Add support for more than 8 channels and NONE channel layouts...
authorTim-Philipp Müller <tim.muller@collabora.co.uk>
Tue, 6 May 2008 12:12:16 +0000 (12:12 +0000)
committerSebastian Dröge <slomo@circular-chaos.org>
Tue, 6 May 2008 12:12:16 +0000 (12:12 +0000)
Original commit message from CVS:
Based on a patch by: Tim-Philipp Müller  <tim.muller at collabora co uk>
* gst/audioconvert/audioconvert.c: (audio_convert_prepare_context):
* gst/audioconvert/audioconvert.h:
* gst/audioconvert/gstaudioconvert.c:
(gst_audio_convert_parse_caps),
(structure_has_fixed_channel_positions),
(gst_audio_convert_transform_caps):
* gst/audioconvert/gstchannelmix.c: (gst_channel_mix_fill_matrix):
Add support for more than 8 channels and NONE channel layouts. For
more than 8 channels no channel conversion is supported yet, only
format conversions are supported. Fixes bug #398033.
* tests/check/elements/audioconvert.c: (verify_convert),
(GST_START_TEST), (audioconvert_suite):
Add some unit tests by Tim for checking the NONE channel layouts
and more than 8 channels and add some more unit tests for channel
conversions.

ChangeLog
gst/audioconvert/audioconvert.c
gst/audioconvert/audioconvert.h
gst/audioconvert/gstaudioconvert.c
gst/audioconvert/gstchannelmix.c
tests/check/elements/audioconvert.c

index 841c95a..657c41b 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,24 @@
+2008-05-06  Sebastian Dröge  <slomo@circular-chaos.org>
+
+       Based on a patch by: Tim-Philipp Müller  <tim.muller at collabora co uk>
+
+       * gst/audioconvert/audioconvert.c: (audio_convert_prepare_context):
+       * gst/audioconvert/audioconvert.h:
+       * gst/audioconvert/gstaudioconvert.c:
+       (gst_audio_convert_parse_caps),
+       (structure_has_fixed_channel_positions),
+       (gst_audio_convert_transform_caps):
+       * gst/audioconvert/gstchannelmix.c: (gst_channel_mix_fill_matrix):
+       Add support for more than 8 channels and NONE channel layouts. For
+       more than 8 channels no channel conversion is supported yet, only
+       format conversions are supported. Fixes bug #398033.
+
+       * tests/check/elements/audioconvert.c: (verify_convert),
+       (GST_START_TEST), (audioconvert_suite):
+       Add some unit tests by Tim for checking the NONE channel layouts
+       and more than 8 channels and add some more unit tests for channel
+       conversions.
+
 2008-05-06  Wim Taymans  <wim.taymans@collabora.co.uk>
 
        * gst/playback/gstdecodebin2.c: (connect_pad):
index f3948bb..b4f8c74 100644 (file)
@@ -420,6 +420,9 @@ audio_convert_prepare_context (AudioConvertCtx * ctx, AudioConvertFmt * in,
   /* first clean the existing context */
   audio_convert_clean_context (ctx);
 
+  g_return_val_if_fail (in->unpositioned_layout == out->unpositioned_layout,
+      FALSE);
+
   ctx->in = *in;
   ctx->out = *out;
 
index 7e79f5a..9c61cc6 100644 (file)
@@ -54,6 +54,7 @@ struct _AudioConvertFmt
   gint rate;
   gint channels;
   GstAudioChannelPosition *pos;
+  gboolean unpositioned_layout;
 
   /* int audio caps */
   gboolean sign;
index 3c437d0..8a66376 100644 (file)
@@ -107,7 +107,8 @@ static void gst_audio_convert_set_property (GObject * object, guint prop_id,
     const GValue * value, GParamSpec * pspec);
 static void gst_audio_convert_get_property (GObject * object, guint prop_id,
     GValue * value, GParamSpec * pspec);
-
+static gboolean structure_has_fixed_channel_positions (GstStructure * s,
+    gboolean * unpositioned_layout);
 
 /* AudioConvert signals and args */
 enum
@@ -135,37 +136,37 @@ GST_BOILERPLATE_FULL (GstAudioConvert, gst_audio_convert, GstBaseTransform,
 GST_STATIC_CAPS ( \
   "audio/x-raw-float, " \
     "rate = (int) [ 1, MAX ], " \
-    "channels = (int) [ 1, 8 ], " \
+    "channels = (int) [ 1, MAX ], " \
     "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \
     "width = (int) 64;" \
   "audio/x-raw-float, " \
     "rate = (int) [ 1, MAX ], " \
-    "channels = (int) [ 1, 8 ], " \
+    "channels = (int) [ 1, MAX ], " \
     "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \
     "width = (int) 32;" \
   "audio/x-raw-int, " \
     "rate = (int) [ 1, MAX ], " \
-    "channels = (int) [ 1, 8 ], " \
+    "channels = (int) [ 1, MAX ], " \
     "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \
     "width = (int) 32, " \
     "depth = (int) [ 1, 32 ], " \
     "signed = (boolean) { true, false }; " \
   "audio/x-raw-int, "   \
     "rate = (int) [ 1, MAX ], " \
-    "channels = (int) [ 1, 8 ], "       \
+    "channels = (int) [ 1, MAX ], "       \
     "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, "        \
     "width = (int) 24, "        \
     "depth = (int) [ 1, 24 ], " "signed = (boolean) { true, false }; "  \
   "audio/x-raw-int, " \
     "rate = (int) [ 1, MAX ], " \
-    "channels = (int) [ 1, 8 ], " \
+    "channels = (int) [ 1, MAX ], " \
     "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \
     "width = (int) 16, " \
     "depth = (int) [ 1, 16 ], " \
     "signed = (boolean) { true, false }; " \
   "audio/x-raw-int, " \
     "rate = (int) [ 1, MAX ], " \
-    "channels = (int) [ 1, 8 ], " \
+    "channels = (int) [ 1, MAX ], " \
     "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \
     "width = (int) 8, " \
     "depth = (int) [ 1, 8 ], " \
@@ -333,6 +334,10 @@ gst_audio_convert_parse_caps (const GstCaps * caps, AudioConvertFmt * fmt)
     goto no_values;
   if (!(fmt->pos = gst_audio_get_channel_positions (structure)))
     goto no_values;
+
+  fmt->unpositioned_layout = FALSE;
+  structure_has_fixed_channel_positions (structure, &fmt->unpositioned_layout);
+
   if (!gst_structure_get_int (structure, "width", &fmt->width))
     goto no_values;
   if (!gst_structure_get_int (structure, "rate", &fmt->rate))
@@ -532,6 +537,42 @@ append_with_other_format (GstCaps * caps, GstStructure * s, gboolean isfloat)
   }
 }
 
+static gboolean
+structure_has_fixed_channel_positions (GstStructure * s,
+    gboolean * unpositioned_layout)
+{
+  GstAudioChannelPosition *pos;
+  const GValue *val;
+  gint channels = 0;
+
+  if (!gst_structure_get_int (s, "channels", &channels))
+    return FALSE;               /* probably a range */
+
+  if (channels > 8) {
+    GST_LOG ("%d channels, undefined channel positions are implicit", channels);
+    *unpositioned_layout = TRUE;
+    return TRUE;
+  }
+
+  val = gst_structure_get_value (s, "channel-positions");
+  if (val == NULL || !gst_value_is_fixed (val)) {
+    GST_LOG ("no or unfixed channel-positions in %" GST_PTR_FORMAT, s);
+    return FALSE;
+  }
+
+  pos = gst_audio_get_channel_positions (s);
+  if ((pos && pos[0] == GST_AUDIO_CHANNEL_POSITION_NONE) || channels > 8) {
+    GST_LOG ("fixed undefined channel-positions in %" GST_PTR_FORMAT, s);
+    *unpositioned_layout = TRUE;
+  } else {
+    GST_LOG ("fixed defined channel-positions in %" GST_PTR_FORMAT, s);
+    *unpositioned_layout = FALSE;
+  }
+  g_free (pos);
+
+  return TRUE;
+}
+
 /* Audioconvert can perform all conversions on audio except for resampling. 
  * However, there are some conversions we _prefer_ not to do. For example, it's
  * better to convert format (float<->int, endianness, etc) than the number of
@@ -549,8 +590,8 @@ gst_audio_convert_transform_caps (GstBaseTransform * base,
 {
   GstCaps *ret;
   GstStructure *s, *structure;
-  gboolean isfloat;
-  gint width, depth, channels;
+  gboolean isfloat, allow_mixing;
+  gint width, depth, channels = 0;
   const gchar *fields_used[] = {
     "width", "depth", "rate", "channels", "endianness", "signed"
   };
@@ -606,11 +647,26 @@ gst_audio_convert_transform_caps (GstBaseTransform * base,
     }
   }
 
+  allow_mixing = TRUE;
   if (gst_structure_get_int (structure, "channels", &channels)) {
-    if (channels == 8)
-      gst_structure_set (s, "channels", G_TYPE_INT, 8, NULL);
+    gboolean unpositioned;
+
+    /* we don't support mixing for channels without channel positions */
+    if (structure_has_fixed_channel_positions (structure, &unpositioned))
+      allow_mixing = (unpositioned == FALSE);
+  }
+
+  if (!allow_mixing || channels == 8) {
+    gst_structure_set (s, "channels", G_TYPE_INT, channels, NULL);
+    if (gst_structure_has_field (structure, "channel-positions"))
+      gst_structure_set_value (s, "channel-positions",
+          gst_structure_get_value (structure, "channel-positions"));
+  } else {
+    if (channels == 0)
+      gst_structure_set (s, "channels", GST_TYPE_INT_RANGE, 1, 8, NULL);
     else
       gst_structure_set (s, "channels", GST_TYPE_INT_RANGE, channels, 8, NULL);
+    gst_structure_remove_field (s, "channel-positions");
   }
   gst_caps_append_structure (ret, s);
 
@@ -639,7 +695,16 @@ gst_audio_convert_transform_caps (GstBaseTransform * base,
    * it's very bad to drop channels entirely.
    */
   s = gst_structure_copy (s);
-  gst_structure_set (s, "channels", GST_TYPE_INT_RANGE, 1, 8, NULL);
+  if (allow_mixing) {
+    gst_structure_set (s, "channels", GST_TYPE_INT_RANGE, 1, 8, NULL);
+    gst_structure_remove_field (s, "channel-positions");
+  } else {
+    /* allow_mixing can only be FALSE if we got a fixed number of channels */
+    gst_structure_set (s, "channels", G_TYPE_INT, channels, NULL);
+    if (gst_structure_has_field (structure, "channel-positions"))
+      gst_structure_set_value (s, "channel-positions",
+          gst_structure_get_value (structure, "channel-positions"));
+  }
   gst_caps_append_structure (ret, s);
 
   /* Same, plus a float<->int conversion */
index 94023d9..98648db 100644 (file)
@@ -464,9 +464,12 @@ static void
 gst_channel_mix_fill_matrix (AudioConvertCtx * this)
 {
   gst_channel_mix_fill_identical (this);
-  gst_channel_mix_fill_compatible (this);
-  gst_channel_mix_fill_others (this);
-  gst_channel_mix_fill_normalize (this);
+
+  if (!this->in.unpositioned_layout) {
+    gst_channel_mix_fill_compatible (this);
+    gst_channel_mix_fill_others (this);
+    gst_channel_mix_fill_normalize (this);
+  }
 }
 
 /* only call after this->out and this->in are filled in */
index c61d7e9..92e62db 100644 (file)
@@ -3,6 +3,7 @@
  * unit test for audioconvert
  *
  * Copyright (C) <2005> Thomas Vander Stichele <thomas at apestaart dot org>
+ * Copyright (C) <2007> Tim-Philipp Müller <tim centricular net>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -34,33 +35,33 @@ static GstPad *mysrcpad, *mysinkpad;
 #define CONVERT_CAPS_TEMPLATE_STRING    \
   "audio/x-raw-float, " \
     "rate = (int) [ 1, MAX ], " \
-    "channels = (int) [ 1, 8 ], " \
+    "channels = (int) [ 1, MAX ], " \
     "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \
     "width = (int) { 32, 64 };" \
   "audio/x-raw-int, " \
     "rate = (int) [ 1, MAX ], " \
-    "channels = (int) [ 1, 8 ], " \
+    "channels = (int) [ 1, MAX ], " \
     "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \
     "width = (int) 32, " \
     "depth = (int) [ 1, 32 ], " \
     "signed = (boolean) { true, false }; " \
   "audio/x-raw-int, " \
     "rate = (int) [ 1, MAX ], " \
-    "channels = (int) [ 1, 8 ], " \
+    "channels = (int) [ 1, MAX ], " \
     "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \
     "width = (int) 24, " \
     "depth = (int) [ 1, 24 ], " \
     "signed = (boolean) { true, false }; " \
   "audio/x-raw-int, " \
     "rate = (int) [ 1, MAX ], " \
-    "channels = (int) [ 1, 8 ], " \
+    "channels = (int) [ 1, MAX ], " \
     "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \
     "width = (int) 16, " \
     "depth = (int) [ 1, 16 ], " \
     "signed = (boolean) { true, false }; " \
   "audio/x-raw-int, " \
     "rate = (int) [ 1, MAX ], " \
-    "channels = (int) [ 1, 8 ], " \
+    "channels = (int) [ 1, MAX ], " \
     "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \
     "width = (int) 8, " \
     "depth = (int) [ 1, 8 ], " \
@@ -229,6 +230,65 @@ static GstAudioChannelPosition mixed_up_positions[][6] = {
       }
 };
 
+/* we get this when recording from a soundcard with lots of input channels */
+static GstAudioChannelPosition undefined_positions[][9] = {
+  {
+      GST_AUDIO_CHANNEL_POSITION_NONE},
+  {
+        GST_AUDIO_CHANNEL_POSITION_NONE,
+      GST_AUDIO_CHANNEL_POSITION_NONE},
+  {
+        GST_AUDIO_CHANNEL_POSITION_NONE,
+        GST_AUDIO_CHANNEL_POSITION_NONE,
+      GST_AUDIO_CHANNEL_POSITION_NONE},
+  {
+        GST_AUDIO_CHANNEL_POSITION_NONE,
+        GST_AUDIO_CHANNEL_POSITION_NONE,
+        GST_AUDIO_CHANNEL_POSITION_NONE,
+      GST_AUDIO_CHANNEL_POSITION_NONE},
+  {
+        GST_AUDIO_CHANNEL_POSITION_NONE,
+        GST_AUDIO_CHANNEL_POSITION_NONE,
+        GST_AUDIO_CHANNEL_POSITION_NONE,
+        GST_AUDIO_CHANNEL_POSITION_NONE,
+      GST_AUDIO_CHANNEL_POSITION_NONE},
+  {
+        GST_AUDIO_CHANNEL_POSITION_NONE,
+        GST_AUDIO_CHANNEL_POSITION_NONE,
+        GST_AUDIO_CHANNEL_POSITION_NONE,
+        GST_AUDIO_CHANNEL_POSITION_NONE,
+        GST_AUDIO_CHANNEL_POSITION_NONE,
+      GST_AUDIO_CHANNEL_POSITION_NONE},
+  {
+        GST_AUDIO_CHANNEL_POSITION_NONE,
+        GST_AUDIO_CHANNEL_POSITION_NONE,
+        GST_AUDIO_CHANNEL_POSITION_NONE,
+        GST_AUDIO_CHANNEL_POSITION_NONE,
+        GST_AUDIO_CHANNEL_POSITION_NONE,
+        GST_AUDIO_CHANNEL_POSITION_NONE,
+      GST_AUDIO_CHANNEL_POSITION_NONE},
+  {
+        GST_AUDIO_CHANNEL_POSITION_NONE,
+        GST_AUDIO_CHANNEL_POSITION_NONE,
+        GST_AUDIO_CHANNEL_POSITION_NONE,
+        GST_AUDIO_CHANNEL_POSITION_NONE,
+        GST_AUDIO_CHANNEL_POSITION_NONE,
+        GST_AUDIO_CHANNEL_POSITION_NONE,
+        GST_AUDIO_CHANNEL_POSITION_NONE,
+      GST_AUDIO_CHANNEL_POSITION_NONE},
+  {
+        GST_AUDIO_CHANNEL_POSITION_NONE,
+        GST_AUDIO_CHANNEL_POSITION_NONE,
+        GST_AUDIO_CHANNEL_POSITION_NONE,
+        GST_AUDIO_CHANNEL_POSITION_NONE,
+        GST_AUDIO_CHANNEL_POSITION_NONE,
+        GST_AUDIO_CHANNEL_POSITION_NONE,
+        GST_AUDIO_CHANNEL_POSITION_NONE,
+        GST_AUDIO_CHANNEL_POSITION_NONE,
+      GST_AUDIO_CHANNEL_POSITION_NONE}
+
+};
+
 static void
 set_channel_positions (GstCaps * caps, int channels,
     GstAudioChannelPosition * channelpositions)
@@ -289,7 +349,8 @@ get_int_mc_caps (guint channels, gchar * endianness, guint width,
 /* eats the refs to the caps */
 static void
 verify_convert (const gchar * which, void *in, int inlength,
-    GstCaps * incaps, void *out, int outlength, GstCaps * outcaps)
+    GstCaps * incaps, void *out, int outlength, GstCaps * outcaps,
+    GstFlowReturn expected_flow)
 {
   GstBuffer *inbuffer, *outbuffer;
   GstElement *audioconvert;
@@ -315,8 +376,12 @@ verify_convert (const gchar * which, void *in, int inlength,
 
   /* pushing gives away my reference ... */
   GST_DEBUG ("push it");
-  fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+  fail_unless_equals_int (gst_pad_push (mysrcpad, inbuffer), expected_flow);
   GST_DEBUG ("pushed it");
+
+  if (expected_flow != GST_FLOW_OK)
+    goto done;
+
   /* ... and puts a new buffer on the global list */
   fail_unless (g_list_length (buffers) == 1);
   fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
@@ -355,6 +420,8 @@ verify_convert (const gchar * which, void *in, int inlength,
 
   buffers = g_list_remove (buffers, outbuffer);
   gst_buffer_unref (outbuffer);
+
+done:
   fail_unless (gst_element_set_state (audioconvert,
           GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS, "could not set to null");
   /* cleanup */
@@ -368,7 +435,11 @@ verify_convert (const gchar * which, void *in, int inlength,
 
 #define RUN_CONVERSION(which, inarray, in_get_caps, outarray, out_get_caps)    \
   verify_convert (which, inarray, sizeof (inarray),                            \
-        in_get_caps, outarray, sizeof (outarray), out_get_caps)
+        in_get_caps, outarray, sizeof (outarray), out_get_caps, GST_FLOW_OK)
+
+#define RUN_CONVERSION_TO_FAIL(which, inarray, in_caps, outarray, out_caps)    \
+  verify_convert (which, inarray, sizeof (inarray),                            \
+        in_caps, outarray, sizeof (outarray), out_caps, GST_FLOW_NOT_NEGOTIATED)
 
 
 GST_START_TEST (test_int16)
@@ -733,23 +804,54 @@ GST_END_TEST;
 GST_START_TEST (test_multichannel_conversion)
 {
   {
-    /* Ensure that audioconvert prefers to convert to integer, rather than mix
-     * to mono
-     */
     gfloat in[] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
     gfloat out[] = { 0.0, 0.0 };
 
-    /* only one direction conversion, the other direction does
-     * not produce exactly the same as the input due to floating
-     * point rounding errors etc. */
     RUN_CONVERSION ("3 channels to 1", in, get_float_mc_caps (3,
             "BYTE_ORDER", 32, FALSE), out, get_float_caps (1, "BYTE_ORDER",
             32));
+    RUN_CONVERSION ("1 channels to 3", out, get_float_caps (1,
+            "BYTE_ORDER", 32), in, get_float_mc_caps (3, "BYTE_ORDER",
+            32, TRUE));
+  }
+
+  {
+    gint16 in[] = { 0, 0, 0, 0, 0, 0 };
+    gint16 out[] = { 0, 0 };
+
+    RUN_CONVERSION ("3 channels to 1", in, get_int_mc_caps (3,
+            "BYTE_ORDER", 16, 16, TRUE, FALSE), out, get_int_caps (1,
+            "BYTE_ORDER", 16, 16, TRUE));
+    RUN_CONVERSION ("1 channels to 3", out, get_int_caps (1, "BYTE_ORDER", 16,
+            16, TRUE), in, get_int_mc_caps (3, "BYTE_ORDER", 16, 16, TRUE,
+            TRUE));
   }
+
 }
 
 GST_END_TEST;
 
+/* for testing channel remapping with 8 channels */
+static GstAudioChannelPosition n8chan_pos_remap_in[8] = {
+  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_SIDE_LEFT,
+  GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT
+};
+static GstAudioChannelPosition n8chan_pos_remap_out[8] = {
+  GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
+  GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
+  GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
+  GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT,
+  GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT,
+  GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
+  GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
+  GST_AUDIO_CHANNEL_POSITION_LFE
+};
 
 GST_START_TEST (test_channel_remapping)
 {
@@ -775,8 +877,52 @@ GST_START_TEST (test_channel_remapping)
         out, out_caps);
   }
 
-  /* TODO: float => int conversion with remapping and vice versa,
-   *       int   => int conversion with remapping */
+  /* int with 8 channels (= largest number allowed with channel positions) */
+  {
+    guint16 in[] = { 0, 1, 2, 3, 4, 5, 6, 7 };
+    guint16 out[] = { 4, 0, 1, 6, 7, 2, 3, 5 };
+    GstCaps *in_caps = get_int_mc_caps (8, "BYTE_ORDER", 16, 16, FALSE, FALSE);
+    GstCaps *out_caps = get_int_mc_caps (8, "BYTE_ORDER", 16, 16, FALSE, TRUE);
+
+    set_channel_positions (in_caps, 8, n8chan_pos_remap_in);
+    set_channel_positions (out_caps, 8, n8chan_pos_remap_out);
+
+    RUN_CONVERSION ("8 channels layout remapping int", in, in_caps,
+        out, out_caps);
+  }
+
+  /* int16 to int32 with 8 channels (= largest number allowed with channel positions) */
+  {
+    guint16 in[] = { 0, 1, 2, 3, 4, 5, 6, 7 };
+    guint32 out[] =
+        { 4 << 16, 0, 1 << 16, 6 << 16, 7 << 16, 2 << 16, 3 << 16, 5 << 16 };
+    GstCaps *in_caps = get_int_mc_caps (8, "BYTE_ORDER", 16, 16, FALSE, FALSE);
+    GstCaps *out_caps = get_int_mc_caps (8, "BYTE_ORDER", 32, 32, FALSE, TRUE);
+
+    set_channel_positions (in_caps, 8, n8chan_pos_remap_in);
+    set_channel_positions (out_caps, 8, n8chan_pos_remap_out);
+
+    RUN_CONVERSION ("8 channels layout remapping int16 --> int32", in, in_caps,
+        out, out_caps);
+
+    in_caps = get_int_mc_caps (8, "BYTE_ORDER", 16, 16, FALSE, FALSE);
+    out_caps = get_int_mc_caps (8, "BYTE_ORDER", 32, 32, FALSE, TRUE);
+    set_channel_positions (in_caps, 8, n8chan_pos_remap_in);
+    set_channel_positions (out_caps, 8, n8chan_pos_remap_out);
+    RUN_CONVERSION ("8 channels layout remapping int16 <-- int32", out,
+        out_caps, in, in_caps);
+  }
+
+  /* float to gint16 with 3 channels */
+  {
+    gfloat in[] = { 100.0 / G_MAXINT16, 0.0, -100.0 / G_MAXINT16 };
+    gint16 out[] = { -100, 0, 100 };
+    GstCaps *in_caps = get_float_mc_caps (3, "BYTE_ORDER", 32, TRUE);
+    GstCaps *out_caps = get_int_mc_caps (3, "BYTE_ORDER", 16, 16, TRUE, FALSE);
+
+    RUN_CONVERSION ("3 channels layout remapping float32 --> int16", in,
+        in_caps, out, out_caps);
+  }
 }
 
 GST_END_TEST;
@@ -847,6 +993,265 @@ GST_START_TEST (test_caps_negotiation)
 
 GST_END_TEST;
 
+GST_START_TEST (test_convert_undefined_multichannel)
+{
+  /* (A) CONVERSION FROM 'WORSE' TO 'BETTER' FORMAT */
+
+  /* 1 channel, NONE positions, int8 => int16 */
+  {
+    guint16 out[] = { 0x2000 };
+    guint8 in[] = { 0x20 };
+    GstCaps *out_caps = get_int_mc_caps (1, "BYTE_ORDER", 16, 16, FALSE, FALSE);
+    GstCaps *in_caps = get_int_mc_caps (1, "BYTE_ORDER", 8, 8, FALSE, FALSE);
+
+    set_channel_positions (out_caps, 1, undefined_positions[1 - 1]);
+    set_channel_positions (in_caps, 1, undefined_positions[1 - 1]);
+
+    RUN_CONVERSION ("1 channel, undefined layout, identity conversion, "
+        "int8 => int16", in, in_caps, out, out_caps);
+  }
+
+  /* 2 channels, NONE positions, int8 => int16 */
+  {
+    guint16 out[] = { 0x8000, 0x2000 };
+    guint8 in[] = { 0x80, 0x20 };
+    GstCaps *out_caps = get_int_mc_caps (2, "BYTE_ORDER", 16, 16, FALSE, FALSE);
+    GstCaps *in_caps = get_int_mc_caps (2, "BYTE_ORDER", 8, 8, FALSE, FALSE);
+
+    set_channel_positions (out_caps, 2, undefined_positions[2 - 1]);
+    set_channel_positions (in_caps, 2, undefined_positions[2 - 1]);
+
+    RUN_CONVERSION ("2 channels, undefined layout, identity conversion, "
+        "int8 => int16", in, in_caps, out, out_caps);
+  }
+
+  /* 6 channels, NONE positions, int8 => int16 */
+  {
+    guint16 out[] = { 0x0000, 0x2000, 0x8000, 0x2000, 0x0000, 0xff00 };
+    guint8 in[] = { 0x00, 0x20, 0x80, 0x20, 0x00, 0xff };
+    GstCaps *out_caps = get_int_mc_caps (6, "BYTE_ORDER", 16, 16, FALSE, FALSE);
+    GstCaps *in_caps = get_int_mc_caps (6, "BYTE_ORDER", 8, 8, FALSE, FALSE);
+
+    set_channel_positions (out_caps, 6, undefined_positions[6 - 1]);
+    set_channel_positions (in_caps, 6, undefined_positions[6 - 1]);
+
+    RUN_CONVERSION ("6 channels, undefined layout, identity conversion, "
+        "int8 => int16", in, in_caps, out, out_caps);
+  }
+
+  /* 9 channels, NONE positions, int8 => int16 */
+  {
+    guint16 out[] = { 0x0000, 0xff00, 0x0000, 0x2000, 0x8000, 0x2000,
+      0x0000, 0xff00, 0x0000
+    };
+    guint8 in[] = { 0x00, 0xff, 0x00, 0x20, 0x80, 0x20, 0x00, 0xff, 0x00 };
+    GstCaps *out_caps = get_int_mc_caps (9, "BYTE_ORDER", 16, 16, FALSE, FALSE);
+    GstCaps *in_caps = get_int_mc_caps (9, "BYTE_ORDER", 8, 8, FALSE, FALSE);
+
+    set_channel_positions (out_caps, 9, undefined_positions[9 - 1]);
+    set_channel_positions (in_caps, 9, undefined_positions[9 - 1]);
+
+    RUN_CONVERSION ("9 channels, undefined layout, identity conversion, "
+        "int8 => int16", in, in_caps, out, out_caps);
+  }
+
+  /* (B) CONVERSION FROM 'BETTER' TO 'WORSE' FORMAT */
+
+  /* 1 channel, NONE positions, int16 => int8 */
+  {
+    guint16 in[] = { 0x2000 };
+    guint8 out[] = { 0x20 };
+    GstCaps *in_caps = get_int_mc_caps (1, "BYTE_ORDER", 16, 16, FALSE, FALSE);
+    GstCaps *out_caps = get_int_mc_caps (1, "BYTE_ORDER", 8, 8, FALSE, FALSE);
+
+    set_channel_positions (out_caps, 1, undefined_positions[1 - 1]);
+    set_channel_positions (in_caps, 1, undefined_positions[1 - 1]);
+
+    RUN_CONVERSION ("1 channel, undefined layout, identity conversion, "
+        "int16 => int8", in, in_caps, out, out_caps);
+  }
+
+  /* 2 channels, NONE positions, int16 => int8 */
+  {
+    guint16 in[] = { 0x8000, 0x2000 };
+    guint8 out[] = { 0x80, 0x20 };
+    GstCaps *in_caps = get_int_mc_caps (2, "BYTE_ORDER", 16, 16, FALSE, FALSE);
+    GstCaps *out_caps = get_int_mc_caps (2, "BYTE_ORDER", 8, 8, FALSE, FALSE);
+
+    set_channel_positions (out_caps, 2, undefined_positions[2 - 1]);
+    set_channel_positions (in_caps, 2, undefined_positions[2 - 1]);
+
+    RUN_CONVERSION ("2 channels, undefined layout, identity conversion, "
+        "int16 => int8", in, in_caps, out, out_caps);
+  }
+
+  /* 6 channels, NONE positions, int16 => int8 */
+  {
+    guint16 in[] = { 0x0000, 0x2000, 0x8000, 0x2000, 0x0000, 0xff00 };
+    guint8 out[] = { 0x00, 0x20, 0x80, 0x20, 0x00, 0xff };
+    GstCaps *in_caps = get_int_mc_caps (6, "BYTE_ORDER", 16, 16, FALSE, FALSE);
+    GstCaps *out_caps = get_int_mc_caps (6, "BYTE_ORDER", 8, 8, FALSE, FALSE);
+
+    set_channel_positions (out_caps, 6, undefined_positions[6 - 1]);
+    set_channel_positions (in_caps, 6, undefined_positions[6 - 1]);
+
+    RUN_CONVERSION ("6 channels, undefined layout, identity conversion, "
+        "int16 => int8", in, in_caps, out, out_caps);
+  }
+
+  /* 9 channels, NONE positions, int16 => int8 */
+  {
+    guint16 in[] = { 0x0000, 0xff00, 0x0000, 0x2000, 0x8000, 0x2000,
+      0x0000, 0xff00, 0x0000
+    };
+    guint8 out[] = { 0x00, 0xff, 0x00, 0x20, 0x80, 0x20, 0x00, 0xff, 0x00 };
+    GstCaps *in_caps = get_int_mc_caps (9, "BYTE_ORDER", 16, 16, FALSE, FALSE);
+    GstCaps *out_caps = get_int_mc_caps (9, "BYTE_ORDER", 8, 8, FALSE, FALSE);
+
+    set_channel_positions (out_caps, 9, undefined_positions[9 - 1]);
+    set_channel_positions (in_caps, 9, undefined_positions[9 - 1]);
+
+    RUN_CONVERSION ("9 channels, undefined layout, identity conversion, "
+        "int16 => int8", in, in_caps, out, out_caps);
+  }
+
+  /* (C) NO CONVERSION, SAME FORMAT */
+
+  /* 1 channel, NONE positions, int16 => int16 */
+  {
+    guint16 in[] = { 0x2000 };
+    guint16 out[] = { 0x2000 };
+    GstCaps *in_caps = get_int_mc_caps (1, "BYTE_ORDER", 16, 16, FALSE, FALSE);
+    GstCaps *out_caps = get_int_mc_caps (1, "BYTE_ORDER", 16, 16, FALSE, FALSE);
+
+    set_channel_positions (out_caps, 1, undefined_positions[1 - 1]);
+    set_channel_positions (in_caps, 1, undefined_positions[1 - 1]);
+
+    RUN_CONVERSION ("1 channel, undefined layout, identity conversion, "
+        "int16 => int16", in, in_caps, out, out_caps);
+  }
+
+  /* 2 channels, NONE positions, int16 => int16 */
+  {
+    guint16 in[] = { 0x8000, 0x2000 };
+    guint16 out[] = { 0x8000, 0x2000 };
+    GstCaps *in_caps = get_int_mc_caps (2, "BYTE_ORDER", 16, 16, FALSE, FALSE);
+    GstCaps *out_caps = get_int_mc_caps (2, "BYTE_ORDER", 16, 16, FALSE, FALSE);
+
+    set_channel_positions (out_caps, 2, undefined_positions[2 - 1]);
+    set_channel_positions (in_caps, 2, undefined_positions[2 - 1]);
+
+    RUN_CONVERSION ("2 channels, undefined layout, identity conversion, "
+        "int16 => int16", in, in_caps, out, out_caps);
+  }
+
+  /* 6 channels, NONE positions, int16 => int16 */
+  {
+    guint16 in[] = { 0x0000, 0x2000, 0x8000, 0x2000, 0x0000, 0xff00 };
+    guint16 out[] = { 0x0000, 0x2000, 0x8000, 0x2000, 0x0000, 0xff00 };
+    GstCaps *in_caps = get_int_mc_caps (6, "BYTE_ORDER", 16, 16, FALSE, FALSE);
+    GstCaps *out_caps = get_int_mc_caps (6, "BYTE_ORDER", 16, 16, FALSE, FALSE);
+
+    set_channel_positions (out_caps, 6, undefined_positions[6 - 1]);
+    set_channel_positions (in_caps, 6, undefined_positions[6 - 1]);
+
+    RUN_CONVERSION ("6 channels, undefined layout, identity conversion, "
+        "int16 => int16", in, in_caps, out, out_caps);
+  }
+
+  /* 9 channels, NONE positions, int16 => int16 */
+  {
+    guint16 in[] = { 0x0000, 0xff00, 0x0000, 0x2000, 0x8000, 0x2000,
+      0x0000, 0xff00, 0x0000
+    };
+    guint16 out[] = { 0x0000, 0xff00, 0x0000, 0x2000, 0x8000, 0x2000,
+      0x0000, 0xff00, 0x0000
+    };
+    GstCaps *in_caps = get_int_mc_caps (9, "BYTE_ORDER", 16, 16, FALSE, FALSE);
+    GstCaps *out_caps = get_int_mc_caps (9, "BYTE_ORDER", 16, 16, FALSE, FALSE);
+
+    set_channel_positions (out_caps, 9, undefined_positions[9 - 1]);
+    set_channel_positions (in_caps, 9, undefined_positions[9 - 1]);
+
+    RUN_CONVERSION ("9 channels, undefined layout, identity conversion, "
+        "int16 => int16", in, in_caps, out, out_caps);
+  }
+
+  /* (C) int16 => float */
+
+  /* 9 channels, NONE positions, int16 => float */
+  {
+    guint16 in[] = { 0x0000, 0x8000, 0x0000, 0x8000, 0x8000, 0x8000,
+      0x0000, 0x8000, 0x0000
+    };
+    gfloat out[] = { -1.0, 0.0, -1.0, 0.0, 0.0, 0.0, -1.0, 0.0, -1.0 };
+    GstCaps *in_caps = get_int_mc_caps (9, "BYTE_ORDER", 16, 16, FALSE, FALSE);
+    GstCaps *out_caps = get_float_mc_caps (9, "BYTE_ORDER", 32, FALSE);
+
+    set_channel_positions (out_caps, 9, undefined_positions[9 - 1]);
+    set_channel_positions (in_caps, 9, undefined_positions[9 - 1]);
+
+    RUN_CONVERSION ("9 channels, undefined layout, identity conversion, "
+        "int16 => float", in, in_caps, out, out_caps);
+  }
+
+  /* 9 channels, NONE positions, int16 => float (same as above, but no
+   * position on output caps to see if audioconvert transforms correctly) */
+  {
+    guint16 in[] = { 0x0000, 0x8000, 0x0000, 0x8000, 0x8000, 0x8000,
+      0x0000, 0x8000, 0x0000
+    };
+    gfloat out[] = { -1.0, 0.0, -1.0, 0.0, 0.0, 0.0, -1.0, 0.0, -1.0 };
+    GstCaps *in_caps = get_int_mc_caps (9, "BYTE_ORDER", 16, 16, FALSE, FALSE);
+    GstCaps *out_caps = get_float_mc_caps (9, "BYTE_ORDER", 32, FALSE);
+
+    //set_channel_positions (out_caps, 9, undefined_positions[9 - 1]);
+    set_channel_positions (in_caps, 9, undefined_positions[9 - 1]);
+
+    RUN_CONVERSION ("9 channels, undefined layout, identity conversion, "
+        "int16 => float", in, in_caps, out, out_caps);
+  }
+
+  /* 8 channels, NONE positions => 2 channels: should fail, no mixing allowed */
+  {
+    guint16 in[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+    gfloat out[] = { -1.0, -1.0 };
+    GstCaps *in_caps = get_int_mc_caps (8, "BYTE_ORDER", 16, 16, FALSE, FALSE);
+    GstCaps *out_caps = get_float_mc_caps (2, "BYTE_ORDER", 32, FALSE);
+
+    set_channel_positions (in_caps, 8, undefined_positions[8 - 1]);
+
+    RUN_CONVERSION_TO_FAIL ("8 channels with layout => 2 channels",
+        in, in_caps, out, out_caps);
+  }
+
+  /* 8 channels, with positions => 2 channels (makes sure channel-position
+   * fields are removed properly in some cases in ::transform_caps, so we
+   * don't up with caps with 2 channels and 8 channel positions) */
+  {
+    GstAudioChannelPosition layout8ch[] = {
+      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_SIDE_LEFT,
+      GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT
+    };
+    gint16 in[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+    gint16 out[] = { 0, 0 };
+    GstCaps *in_caps = get_int_mc_caps (8, "BYTE_ORDER", 16, 16, TRUE, FALSE);
+    GstCaps *out_caps = get_int_mc_caps (2, "BYTE_ORDER", 16, 16, TRUE, FALSE);
+
+    set_channel_positions (in_caps, 8, layout8ch);
+
+    RUN_CONVERSION ("8 channels with layout => 2 channels",
+        in, in_caps, out, out_caps);
+  }
+}
+
+GST_END_TEST;
 
 static Suite *
 audioconvert_suite (void)
@@ -862,23 +1267,9 @@ audioconvert_suite (void)
   tcase_add_test (tc_chain, test_multichannel_conversion);
   tcase_add_test (tc_chain, test_channel_remapping);
   tcase_add_test (tc_chain, test_caps_negotiation);
+  tcase_add_test (tc_chain, test_convert_undefined_multichannel);
 
   return s;
 }
 
-int
-main (int argc, char **argv)
-{
-  int nf;
-
-  Suite *s = audioconvert_suite ();
-  SRunner *sr = srunner_create (s);
-
-  gst_check_init (&argc, &argv);
-
-  srunner_run_all (sr, CK_NORMAL);
-  nf = srunner_ntests_failed (sr);
-  srunner_free (sr);
-
-  return nf;
-}
+GST_CHECK_MAIN (audioconvert);