mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-24 01:00:37 +00:00
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:
parent
225238a913
commit
e0f9b4fffc
3 changed files with 155 additions and 95 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue