GstStructure *st;
gint i, n;
GstCaps *res;
+ guint64 channel_mask;
res = gst_caps_new_empty ();
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);
}
static const GstAudioChannelPosition default_positions[8][8] = {
/* 1 channel */
{
- GST_AUDIO_CHANNEL_POSITION_FRONT_MONO,
+ GST_AUDIO_CHANNEL_POSITION_MONO,
},
/* 2 channels */
{
{
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,
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? */
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;
}
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;
}
* 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);
}
}
#include <math.h>
#include <string.h>
-#include <gst/audio/multichannel.h>
#include "gstchannelmix.h"
{ {
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,
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;
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;
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;
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;
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);
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