diff --git a/subprojects/gst-plugins-base/docs/plugins/gst_plugins_cache.json b/subprojects/gst-plugins-base/docs/plugins/gst_plugins_cache.json index 300aa08915..ca9963f1b7 100644 --- a/subprojects/gst-plugins-base/docs/plugins/gst_plugins_cache.json +++ b/subprojects/gst-plugins-base/docs/plugins/gst_plugins_cache.json @@ -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, diff --git a/subprojects/gst-plugins-base/gst/audioconvert/gstaudioconvert.c b/subprojects/gst-plugins-base/gst/audioconvert/gstaudioconvert.c index 474dcaea8f..10027bda8b 100644 --- a/subprojects/gst-plugins-base/gst/audioconvert/gstaudioconvert.c +++ b/subprojects/gst-plugins-base/gst/audioconvert/gstaudioconvert.c @@ -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; diff --git a/subprojects/gst-plugins-base/tests/check/elements/audioconvert.c b/subprojects/gst-plugins-base/tests/check/elements/audioconvert.c index 7186baf35a..c628caf4d4 100644 --- a/subprojects/gst-plugins-base/tests/check/elements/audioconvert.c +++ b/subprojects/gst-plugins-base/tests/check/elements/audioconvert.c @@ -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,