audioconvert: Port to the new multichannel caps

audioconvert still needs support for mixing all the new
channel positions, see:
https://bugzilla.gnome.org/show_bug.cgi?id=666506
This commit is contained in:
Sebastian Dröge 2011-12-19 12:41:24 +01:00
parent 225238a913
commit e0f9b4fffc
3 changed files with 155 additions and 95 deletions

View file

@ -275,6 +275,7 @@ gst_audio_convert_caps_remove_format_info (GstCaps * caps)
GstStructure *st; GstStructure *st;
gint i, n; gint i, n;
GstCaps *res; GstCaps *res;
guint64 channel_mask;
res = gst_caps_new_empty (); res = gst_caps_new_empty ();
@ -288,8 +289,16 @@ gst_audio_convert_caps_remove_format_info (GstCaps * caps)
continue; continue;
st = gst_structure_copy (st); st = gst_structure_copy (st);
gst_structure_remove_fields (st, "format", "channel-positions", "channels", gst_structure_remove_field (st, "format");
NULL);
/* 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); gst_caps_append_structure (res, st);
} }
@ -327,7 +336,7 @@ gst_audio_convert_transform_caps (GstBaseTransform * btrans,
static const GstAudioChannelPosition default_positions[8][8] = { static const GstAudioChannelPosition default_positions[8][8] = {
/* 1 channel */ /* 1 channel */
{ {
GST_AUDIO_CHANNEL_POSITION_FRONT_MONO, GST_AUDIO_CHANNEL_POSITION_MONO,
}, },
/* 2 channels */ /* 2 channels */
{ {
@ -338,9 +347,9 @@ static const GstAudioChannelPosition default_positions[8][8] = {
{ {
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, 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_LEFT,
GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, 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_REAR_RIGHT,
GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
}, },
/* 6 channels */ /* 6 channels (5.1) */
{ {
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, 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_LEFT,
GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
GST_AUDIO_CHANNEL_POSITION_LFE, GST_AUDIO_CHANNEL_POSITION_LFE1,
GST_AUDIO_CHANNEL_POSITION_REAR_CENTER, GST_AUDIO_CHANNEL_POSITION_REAR_CENTER,
}, },
/* 8 channels */ /* 8 channels (7.1) */
{ {
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, 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_LEFT,
GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT, GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT,
} }
}; };
static const GValue * static gint
find_suitable_channel_layout (const GValue * val, guint chans) n_bits_set (guint64 x)
{ {
/* if output layout is fixed already and looks sane, we're done */ gint i;
if (GST_VALUE_HOLDS_ARRAY (val) && gst_value_array_get_size (val) == chans) gint c = 0;
return val; guint64 y = 1;
/* if it's a list, go through it recursively and return the first for (i = 0; i < 64; i++) {
* sane-enough looking value we find */ if (x & y)
if (GST_VALUE_HOLDS_LIST (val)) { c++;
gint i; y <<= 1;
for (i = 0; i < gst_value_list_get_size (val); ++i) {
const GValue *v, *ret;
v = gst_value_list_get_value (val, i);
if ((ret = find_suitable_channel_layout (v, chans)))
return ret;
}
} }
return NULL; return c;
}
static guint64
find_suitable_mask (guint64 mask, gint n_chans)
{
guint64 intersection;
gint i;
i = 0;
g_assert (n_bits_set (mask) >= n_chans);
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 static void
gst_audio_convert_fixate_channels (GstBaseTransform * base, GstStructure * ins, gst_audio_convert_fixate_channels (GstBaseTransform * base, GstStructure * ins,
GstStructure * outs) GstStructure * outs)
{ {
const GValue *in_layout, *out_layout;
gint in_chans, out_chans; 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)) if (!gst_structure_get_int (ins, "channels", &in_chans))
return; /* this shouldn't really happen, should it? */ 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")) { if (!gst_structure_has_field (outs, "channels")) {
/* we could try to get the implied number of channels from the layout, /* we could try to get the implied number of channels from the layout,
* but that seems overdoing it for a somewhat exotic corner case */ * 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; return;
} }
@ -433,70 +456,101 @@ gst_audio_convert_fixate_channels (GstBaseTransform * base, GstStructure * ins,
if (!gst_structure_get_int (outs, "channels", &out_chans)) { if (!gst_structure_get_int (outs, "channels", &out_chans)) {
/* shouldn't really happen ... */ /* shouldn't really happen ... */
gst_structure_remove_field (outs, "channel-positions"); gst_structure_remove_field (outs, "channel-mask");
return; return;
} }
/* check if the output has a channel layout (or a list of layouts) */ /* get the channel layout of the output if any */
out_layout = gst_structure_get_value (outs, "channel-positions"); has_out_mask = gst_structure_has_field (outs, "channel-mask");
if (has_out_mask) {
/* get the channel layout of the input if any */ gst_structure_get (outs, "channel-mask", GST_TYPE_BITMASK, &out_mask, NULL);
in_layout = gst_structure_get_value (ins, "channel-positions"); } else {
/* channels == 1 => MONO */
if (out_layout == NULL) { if (out_chans == 2) {
if (out_chans <= 2 && (in_chans != out_chans || in_layout == NULL)) out_mask =
return; /* nothing to do, default layout will be assumed */ GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT |
GST_WARNING_OBJECT (base, "downstream caps contain no channel layout"); GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
has_out_mask = TRUE;
}
} }
if (in_chans == out_chans && in_layout != NULL) { /* get the channel layout of the input if any */
GValue res = { 0, }; 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 (!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 */ /* same number of channels and no output layout: just use input layout */
if (out_layout == NULL) { if (!has_out_mask) {
gst_structure_set_value (outs, "channel-positions", in_layout); /* in_chans == 1 handled above already */
gst_structure_set (outs, "channel-mask", GST_TYPE_BITMASK, in_mask, NULL);
return; return;
} }
/* If both masks are the same we're done, this includes the NONE layout case */
if (in_mask == out_mask)
return;
/* if output layout is fixed already and looks sane, we're done */ /* if output layout is fixed already and looks sane, we're done */
if (GST_VALUE_HOLDS_ARRAY (out_layout) && if (n_bits_set (out_mask) == out_chans)
gst_value_array_get_size (out_layout) == out_chans) {
return; return;
}
/* if the output layout is not fixed, check if the output layout contains if (n_bits_set (out_mask) < in_chans) {
* the input layout */ /* Not much we can do here, this shouldn't just happen */
if (gst_value_intersect (&res, in_layout, out_layout)) { g_warning ("%s: Invalid downstream channel-mask with too few bits set",
gst_structure_set_value (outs, "channel-positions", in_layout); GST_ELEMENT_NAME (base));
g_value_unset (&res); } else {
return; guint64 intersection;
}
/* output layout is not fixed and does not contain the input layout, so /* if the output layout is not fixed, check if the output layout contains
* just pick the first layout in the list (it should be a list ...) */ * the input layout */
if ((out_layout = find_suitable_channel_layout (out_layout, out_chans))) { intersection = in_mask & out_mask;
gst_structure_set_value (outs, "channel-positions", out_layout); if (n_bits_set (intersection) >= in_chans) {
return; 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) */ /* ... else fall back to default layout (NB: out_layout is NULL here) */
GST_WARNING_OBJECT (base, "unexpected output channel layout"); GST_WARNING_OBJECT (base, "unexpected output channel layout");
} } else {
guint64 intersection;
/* number of input channels != number of output channels: /* number of input channels != number of output channels:
* if this value contains a list of channel layouts (or even worse: a list * 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 * 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 * 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 * 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 the first one is something unexpected or non-channel-pos-array-y */
if (out_layout != NULL && GST_VALUE_HOLDS_LIST (out_layout)) if (n_bits_set (out_mask) >= out_chans) {
out_layout = find_suitable_channel_layout (out_layout, out_chans); intersection = find_suitable_mask (out_mask, out_chans);
gst_structure_set (outs, "channel-mask", GST_TYPE_BITMASK, intersection,
if (out_layout != NULL) { 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);
return; 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 * layout based on LFE-presence in input layout, but let's save that for
* another day) */ * another day) */
if (out_chans > 0 && out_chans <= G_N_ELEMENTS (default_positions[0])) { 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_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);
} }
} }

View file

@ -26,7 +26,6 @@
#include <math.h> #include <math.h>
#include <string.h> #include <string.h>
#include <gst/audio/multichannel.h>
#include "gstchannelmix.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_LEFT,
GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, { GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, {
GST_AUDIO_CHANNEL_POSITION_FRONT_MONO}}, GST_AUDIO_CHANNEL_POSITION_MONO}},
/* front center: 2 <-> 1 */ /* front center: 2 <-> 1 */
{ { { {
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, 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++) { for (n = 0; n < info->channels; n++) {
switch (info->position[n]) { switch (info->position[n]) {
case GST_AUDIO_CHANNEL_POSITION_FRONT_MONO: case GST_AUDIO_CHANNEL_POSITION_MONO:
f[1] = n; f[1] = n;
*has_f = TRUE; *has_f = TRUE;
break; break;
@ -235,7 +234,7 @@ gst_channel_mix_detect_pos (GstAudioInfo * info,
s[2] = n; s[2] = n;
*has_s = TRUE; *has_s = TRUE;
break; break;
case GST_AUDIO_CHANNEL_POSITION_LFE: case GST_AUDIO_CHANNEL_POSITION_LFE1:
*has_b = TRUE; *has_b = TRUE;
b[1] = n; b[1] = n;
break; break;
@ -552,7 +551,7 @@ gst_channel_mix_fill_special (AudioConvertCtx * this)
in->position[1] == GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT) || in->position[1] == GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT) ||
(in->position[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT && (in->position[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT &&
in->position[1] == GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT)) && 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[0][0] = 0.5;
this->matrix[1][0] = 0.5; this->matrix[1][0] = 0.5;
return TRUE; return TRUE;
@ -561,7 +560,7 @@ gst_channel_mix_fill_special (AudioConvertCtx * this)
out->position[1] == GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT) || out->position[1] == GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT) ||
(out->position[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT && (out->position[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT &&
out->position[1] == GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT)) && 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][0] = 1.0;
this->matrix[0][1] = 1.0; this->matrix[0][1] = 1.0;
return TRUE; return TRUE;
@ -584,8 +583,7 @@ gst_channel_mix_fill_matrix (AudioConvertCtx * this)
gst_channel_mix_fill_identical (this); gst_channel_mix_fill_identical (this);
if (!GST_AUDIO_INFO_HAS_DEFAULT_POSITIONS (&this->in) && if (!GST_AUDIO_INFO_IS_UNPOSITIONED (&this->in)) {
!GST_AUDIO_INFO_IS_UNPOSITIONED (&this->in)) {
gst_channel_mix_fill_compatible (this); gst_channel_mix_fill_compatible (this);
gst_channel_mix_fill_others (this); gst_channel_mix_fill_others (this);
gst_channel_mix_fill_normalize (this); gst_channel_mix_fill_normalize (this);
@ -650,17 +648,20 @@ gboolean
gst_channel_mix_passthrough (AudioConvertCtx * this) gst_channel_mix_passthrough (AudioConvertCtx * this)
{ {
gint i; gint i;
guint64 in_mask, out_mask;
/* only NxN matrices can be identities */ /* only NxN matrices can be identities */
if (this->in.channels != this->out.channels) if (this->in.channels != this->out.channels)
return FALSE; return FALSE;
/* this assumes a normalized matrix */ /* passthrough if both channel masks are the same */
for (i = 0; i < this->in.channels; i++) in_mask = out_mask = 0;
if (this->matrix[i][i] != 1.) for (i = 0; i < this->in.channels; i++) {
return FALSE; 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 /* IMPORTANT: out_data == in_data is possible, make sure to not overwrite data

View file

@ -25,16 +25,11 @@
#include "plugin.h" #include "plugin.h"
#include <gst/audio/multichannel.h>
#include "gstaudioconvertorc.h" #include "gstaudioconvertorc.h"
static gboolean static gboolean
plugin_init (GstPlugin * plugin) plugin_init (GstPlugin * plugin)
{ {
/* ensure GstAudioChannelPosition type is registered */
if (!gst_audio_channel_position_get_type ())
return FALSE;
if (!gst_element_register (plugin, "audioconvert", if (!gst_element_register (plugin, "audioconvert",
GST_RANK_PRIMARY, gst_audio_convert_get_type ())) GST_RANK_PRIMARY, gst_audio_convert_get_type ()))
return FALSE; return FALSE;