audioconvert: Fix fallback to identity mixing matrix when setting an empty mix-matrix

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5530>
This commit is contained in:
Lieven Paulissen 2023-10-20 15:21:03 +02:00 committed by GStreamer Marge Bot
parent d20d94cc48
commit 9cc7e8c035
3 changed files with 84 additions and 16 deletions

View file

@ -866,7 +866,7 @@
"writable": true
},
"mix-matrix": {
"blurb": "Transformation matrix for input/output channels",
"blurb": "Transformation matrix for input/output channels.",
"conditionally-available": false,
"construct": false,
"construct-only": false,

View file

@ -46,6 +46,9 @@
*
* A mix matrix can be passed to audioconvert, that will govern the
* remapping of input to output channels.
* This is required if the input channels are unpositioned and no standard layout can be determined.
* If an empty mix matrix is specified, a (potentially truncated) identity matrix will be generated.
*
* ## Example matrix generation code
* To generate the matrix using code:
*
@ -73,8 +76,6 @@
* gst-launch-1.0 audiotestsrc ! audio/x-raw, channels=4 ! audioconvert mix-matrix="<<(float)1.0, (float)0.0, (float)0.0, (float)0.0>, <(float)0.0, (float)1.0, (float)0.0, (float)0.0>>" ! audio/x-raw,channels=2 ! autoaudiosink
* ]|
*
* > If an empty mix matrix is specified, a (potentially truncated)
* > identity matrix will be generated.
*
* ## Example empty matrix generation code
* |[
@ -214,10 +215,18 @@ gst_audio_convert_class_init (GstAudioConvertClass * klass)
GST_TYPE_AUDIO_NOISE_SHAPING_METHOD, GST_AUDIO_NOISE_SHAPING_NONE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstAudioConvert:mix-matrix:
*
* Transformation matrix for input/output channels.
* Required if the input channels are unpositioned and no standard layout can be determined.
* Setting an empty matrix like \"< >\" will generate an identity matrix."
*
*/
g_object_class_install_property (gobject_class, PROP_MIX_MATRIX,
gst_param_spec_array ("mix-matrix",
"Input/output channel matrix",
"Transformation matrix for input/output channels",
"Transformation matrix for input/output channels.",
gst_param_spec_array ("matrix-rows", "rows", "rows",
g_param_spec_float ("matrix-cols", "cols", "cols",
-1, 1, 0,
@ -345,8 +354,9 @@ remove_channels_from_structure (GstCapsFeatures * features, GstStructure * s,
gint channels;
GstAudioConvert *this = GST_AUDIO_CONVERT (user_data);
/* Only remove the channels and channel-mask for non-NONE layouts,
* or if a mix matrix was manually specified */
/* Only remove the channels and channel-mask if a (empty) mix matrix was manually specified,
* if no channel-mask is specified, for non-NONE channel layouts or for a single channel layout
*/
if (this->mix_matrix_is_set ||
!gst_structure_get (s, "channel-mask", GST_TYPE_BITMASK, &mask, NULL) ||
(mask != 0 || (gst_structure_get_int (s, "channels", &channels)
@ -1006,7 +1016,7 @@ gst_audio_convert_set_property (GObject * object, guint prop_id,
break;
case PROP_MIX_MATRIX:
if (!gst_value_array_get_size (value)) {
this->mix_matrix_is_set = FALSE;
this->mix_matrix_is_set = TRUE;
} else {
const GValue *first_row = gst_value_array_get_value (value, 0);
@ -1017,7 +1027,8 @@ gst_audio_convert_set_property (GObject * object, guint prop_id,
/* issue a reconfigure upstream */
gst_base_transform_reconfigure_sink (GST_BASE_TRANSFORM (this));
} else {
g_warning ("Empty mix matrix's first row");
g_warning ("Empty mix matrix's first row.");
this->mix_matrix_is_set = FALSE;
}
}
break;

View file

@ -46,7 +46,8 @@ static GstPad *mysrcpad, *mysinkpad;
/* takes over reference for outcaps */
static GstElement *
setup_audioconvert (GstCaps * outcaps)
setup_audioconvert (GstCaps * outcaps, gboolean use_mix_matrix,
GValue * mix_matrix)
{
GstPadTemplate *sinktemplate;
static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
@ -63,6 +64,9 @@ setup_audioconvert (GstCaps * outcaps)
audioconvert = gst_check_setup_element ("audioconvert");
g_object_set (G_OBJECT (audioconvert), "dithering", 0, NULL);
g_object_set (G_OBJECT (audioconvert), "noise-shaping", 0, NULL);
if (use_mix_matrix) {
g_object_set_property (G_OBJECT (audioconvert), "mix-matrix", mix_matrix);
}
mysrcpad = gst_check_setup_src_pad (audioconvert, &srctemplate);
mysinkpad =
gst_check_setup_sink_pad_from_template (audioconvert, sinktemplate);
@ -408,7 +412,8 @@ get_int_mc_caps (guint channels, gint endianness, guint width,
static void
verify_convert (const gchar * which, void *in, int inlength,
GstCaps * incaps, void *out, int outlength, GstCaps * outcaps,
GstFlowReturn expected_flow, gboolean in_place_allowed)
GstFlowReturn expected_flow, gboolean in_place_allowed,
gboolean use_mix_matrix, GValue * mix_matrix)
{
GstBuffer *inbuffer, *outbuffer;
GstElement *audioconvert;
@ -419,7 +424,7 @@ verify_convert (const gchar * which, void *in, int inlength,
GST_DEBUG ("outcaps: %" GST_PTR_FORMAT, outcaps);
ASSERT_CAPS_REFCOUNT (incaps, "incaps", 1);
ASSERT_CAPS_REFCOUNT (outcaps, "outcaps", 1);
audioconvert = setup_audioconvert (outcaps);
audioconvert = setup_audioconvert (outcaps, use_mix_matrix, mix_matrix);
ASSERT_CAPS_REFCOUNT (outcaps, "outcaps", 2);
fail_unless (gst_element_set_state (audioconvert,
@ -505,17 +510,22 @@ done:
#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, GST_FLOW_OK, \
TRUE)
TRUE, FALSE, &(GValue) G_VALUE_INIT);
#define RUN_CONVERSION_WITH_MATRIX(which, inarray, in_get_caps, outarray, out_get_caps, mix_matrix) \
verify_convert (which, inarray, sizeof (inarray), \
in_get_caps, outarray, sizeof (outarray), out_get_caps, GST_FLOW_OK, \
TRUE, TRUE, mix_matrix);
#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, TRUE)
GST_FLOW_NOT_NEGOTIATED, TRUE, FALSE, &(GValue) G_VALUE_INIT);
#define RUN_CONVERSION_NOT_INPLACE(which, inarray, in_get_caps, outarray, out_get_caps) \
verify_convert (which, inarray, sizeof (inarray), \
in_get_caps, outarray, sizeof (outarray), out_get_caps, GST_FLOW_OK, \
FALSE)
FALSE, FALSE, &(GValue) G_VALUE_INIT);
#define INTERLEAVED GST_AUDIO_LAYOUT_INTERLEAVED
#define PLANAR GST_AUDIO_LAYOUT_NON_INTERLEAVED
@ -1559,6 +1569,53 @@ GST_START_TEST (test_convert_undefined_multichannel)
RUN_CONVERSION ("8 channels with layout => 2 channels",
in, in_caps, out, out_caps);
}
/* 9 channels, NONE positions => 2 channels, with empty mix-matrix */
{
guint16 in[] =
{ 0, 0, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000 };
gfloat out[] = { -1.0, -1.0 };
GstCaps *in_caps = get_int_mc_caps (9, G_BYTE_ORDER, 16, 16, FALSE,
INTERLEAVED, undefined_positions[9 - 1]);
GstCaps *out_caps = get_float_mc_caps (2, G_BYTE_ORDER, 32, INTERLEAVED,
NULL);
GValue empty_mix_matrix = G_VALUE_INIT;
g_value_init (&empty_mix_matrix, GST_TYPE_ARRAY);
RUN_CONVERSION_WITH_MATRIX ("9 channels, undefined layout => 2 channels",
in, in_caps, out, out_caps, &empty_mix_matrix);
g_value_unset (&empty_mix_matrix);
}
/* 9 channels, NONE positions => 2 channels, with specified mix-matrix */
{
guint16 in[] =
{ 0, 0, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000 };
gfloat out[] = { -1.0, -1.0 };
GstCaps *in_caps = get_int_mc_caps (9, G_BYTE_ORDER, 16, 16, FALSE,
INTERLEAVED, undefined_positions[9 - 1]);
GstCaps *out_caps = get_float_mc_caps (2, G_BYTE_ORDER, 32, INTERLEAVED,
NULL);
GValue mix_matrix = G_VALUE_INIT;
GValue row = G_VALUE_INIT;
GValue value = G_VALUE_INIT;
g_value_init (&mix_matrix, GST_TYPE_ARRAY);
for (int j = 0; j < 2; j++) {
g_value_init (&row, GST_TYPE_ARRAY);
for (int i = 0; i < 9; i++) {
g_value_init (&value, G_TYPE_FLOAT);
g_value_set_float (&value, i == j && i < 2 ? 1 : 0);
gst_value_array_append_value (&row, &value);
g_value_unset (&value);
}
gst_value_array_append_value (&mix_matrix, &row);
g_value_unset (&row);
}
RUN_CONVERSION_WITH_MATRIX ("9 channels, undefined layout => 2 channels",
in, in_caps, out, out_caps, &mix_matrix);
g_value_unset (&mix_matrix);
}
}
GST_END_TEST;
@ -1637,7 +1694,7 @@ GST_START_TEST (test_gap_buffers)
gsize data_len = sizeof (data);
gint i;
audioconvert = setup_audioconvert (caps);
audioconvert = setup_audioconvert (caps, FALSE, &(GValue) G_VALUE_INIT);
fail_unless (gst_element_set_state (audioconvert,
GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
@ -1797,7 +1854,7 @@ GST_START_TEST (test_layout_conv_fixate_caps)
"layout = (string) non-interleaved, "
"rate = (int) [ 1, MAX ], " "channels = (int) [1, 8]");
audioconvert = setup_audioconvert (outcaps);
audioconvert = setup_audioconvert (outcaps, FALSE, &(GValue) G_VALUE_INIT);
fail_unless (gst_element_set_state (audioconvert,
GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,