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;
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;
/* 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;
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;
}
for (i = 0; i < 64; i++) {
if (x & y)
c++;
y <<= 1;
}
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
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 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");
/* 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;
}
}
if (in_chans == out_chans && in_layout != NULL) {
GValue res = { 0, };
/* get the channel layout of the input if any */
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 */
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 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 (GST_VALUE_HOLDS_ARRAY (out_layout) &&
gst_value_array_get_size (out_layout) == out_chans) {
if (n_bits_set (out_mask) == out_chans)
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);
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;
/* 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 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");
}
} 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 (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);
/* 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);
}
}

View 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

View file

@ -25,16 +25,11 @@
#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;