From fa47edf84698ffd8a62e5c72ff2ef918f3fe30f0 Mon Sep 17 00:00:00 2001 From: Vivia Nikolaidou Date: Mon, 16 Jan 2017 19:36:07 +0200 Subject: [PATCH] audiomixmatrix: New element that mixes audio channels This element transforms a given number of input channels into a given number of output channels according to a given transformation matrix. The matrix coefficients must be between -1 and 1. In the auto mode, input/output channels are automatically negotiated and the transformation matrix is a truncated or zero-padded identity matrix. https://bugzilla.gnome.org/show_bug.cgi?id=777376 --- configure.ac | 3 + docs/plugins/gst-plugins-bad-plugins-sections.txt | 16 + gst/audiomixmatrix/Makefile.am | 10 + gst/audiomixmatrix/gstaudiomixmatrix.c | 813 +++++++++++++++++++++ gst/audiomixmatrix/gstaudiomixmatrix.h | 78 ++ tests/examples/Makefile.am | 6 +- tests/examples/audiomixmatrix/Makefile.am | 16 + .../examples/audiomixmatrix/test-audiomixmatrix.c | 244 +++++++ 8 files changed, 1184 insertions(+), 2 deletions(-) create mode 100644 gst/audiomixmatrix/Makefile.am create mode 100644 gst/audiomixmatrix/gstaudiomixmatrix.c create mode 100644 gst/audiomixmatrix/gstaudiomixmatrix.h create mode 100644 tests/examples/audiomixmatrix/Makefile.am create mode 100644 tests/examples/audiomixmatrix/test-audiomixmatrix.c diff --git a/configure.ac b/configure.ac index 10b850d..648ea87 100644 --- a/configure.ac +++ b/configure.ac @@ -470,6 +470,7 @@ AG_GST_CHECK_PLUGIN(asfmux) AG_GST_CHECK_PLUGIN(audiobuffersplit) AG_GST_CHECK_PLUGIN(audiofxbad) AG_GST_CHECK_PLUGIN(audiomixer) +AG_GST_CHECK_PLUGIN(audiomixmatrix) AG_GST_CHECK_PLUGIN(compositor) AG_GST_CHECK_PLUGIN(audiovisualizers) AG_GST_CHECK_PLUGIN(autoconvert) @@ -3455,6 +3456,7 @@ gst/asfmux/Makefile gst/audiobuffersplit/Makefile gst/audiofxbad/Makefile gst/audiomixer/Makefile +gst/audiomixmatrix/Makefile gst/audiovisualizers/Makefile gst/autoconvert/Makefile gst/bayer/Makefile @@ -3565,6 +3567,7 @@ tests/examples/avsamplesink/Makefile tests/examples/camerabin2/Makefile tests/examples/codecparsers/Makefile tests/examples/directfb/Makefile +tests/examples/audiomixmatrix/Makefile tests/examples/gl/Makefile tests/examples/gl/cocoa/Makefile tests/examples/gl/clutter/Makefile diff --git a/docs/plugins/gst-plugins-bad-plugins-sections.txt b/docs/plugins/gst-plugins-bad-plugins-sections.txt index 8d420a3..4912657 100644 --- a/docs/plugins/gst-plugins-bad-plugins-sections.txt +++ b/docs/plugins/gst-plugins-bad-plugins-sections.txt @@ -223,6 +223,22 @@ gst_audio_mixer_get_type
+element-audiomixmatrix +audiomixmatrix +GstAudioMixMatrix + +GstAudioMixMatrixClass +GST_AUDIO_MIX_MATRIX +GST_AUDIO_MIX_MATRIX_CAST +GST_IS_AUDIO_MIX_MATRIX +GST_AUDIO_MIX_MATRIX_CLASS +GST_IS_AUDIO_MIX_MATRIX_CLASS +GST_TYPE_AUDIO_MIX_MATRIX + +gst_audio_mix_matrix_get_type +
+ +
element-audioparse audioparse GstAudioParse diff --git a/gst/audiomixmatrix/Makefile.am b/gst/audiomixmatrix/Makefile.am new file mode 100644 index 0000000..e54eb9d --- /dev/null +++ b/gst/audiomixmatrix/Makefile.am @@ -0,0 +1,10 @@ +plugin_LTLIBRARIES = libgstaudiomixmatrix.la + +libgstaudiomixmatrix_la_SOURCES = gstaudiomixmatrix.c + +libgstaudiomixmatrix_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) +libgstaudiomixmatrix_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgstaudio-$(GST_API_VERSION) $(GST_BASE_LIBS) $(GST_LIBS) $(LIBM) +libgstaudiomixmatrix_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstaudiomixmatrix_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) + +noinst_HEADERS = gstaudiomixmatrix.h diff --git a/gst/audiomixmatrix/gstaudiomixmatrix.c b/gst/audiomixmatrix/gstaudiomixmatrix.c new file mode 100644 index 0000000..5604d06 --- /dev/null +++ b/gst/audiomixmatrix/gstaudiomixmatrix.c @@ -0,0 +1,813 @@ +/* + * GStreamer + * Copyright (C) 2017 Vivia Nikolaidou + * + * gstaudiomixmatrix.c + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-audiomixmatrix + * @short_description: Transform input/output channels according to a matrix + * + * This element transforms a given number of input channels into a given + * number of output channels according to a given transformation matrix. The + * matrix coefficients must be between -1 and 1: the number of rows is equal + * to the number of output channels and the number of columns is equal to the + * number of input channels. In the first-channels mode, input/output channels + * are automatically negotiated and the transformation matrix is a truncated + * identity matrix. + * + * + * Example matrix generation code + * To generate the matrix using code: + * + * |[ + * GValue v = G_VALUE_INIT; + * GValue v2 = G_VALUE_INIT; + * GValue v3 = G_VALUE_INIT; + * + * g_value_init (&v2, GST_TYPE_ARRAY); + * g_value_init (&v3, G_TYPE_DOUBLE); + * g_value_set_double (&v3, 1); + * gst_value_array_append_value (&v2, &v3); + * g_value_unset (&v3); + * [ Repeat for as many double as your input channels - unset and reinit v3 ] + * g_value_init (&v, GST_TYPE_ARRAY); + * gst_value_array_append_value (&v, &v2); + * g_value_unset (&v2); + * [ Repeat for as many v2's as your output channels - unset and reinit v2] + * g_object_set_property (G_OBJECT (audiomixmatrix), "matrix", &v); + * g_value_unset (&v); + * ]| + * + * + * + * Example launch line + * |[ + * gst-launch-1.0 audiotestsrc ! audio/x-raw,channels=4 ! audiomixmatrix in-channels=4 out-channels=2 channel-mask=-1 matrix="<<(double)1, (double)0, (double)0, (double)0>, <0.0, 1.0, 0.0, 0.0>>" ! audio/x-raw,channels=2 ! autoaudiosink + * ]| + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* FIXME 2.0: suppress warnings for deprecated API such as GValueArray + * with newer GLib versions (>= 2.31.0) */ +#define GLIB_DISABLE_DEPRECATION_WARNINGS + +#include "gstaudiomixmatrix.h" + +#include +#include +#include +#include + +GST_DEBUG_CATEGORY_STATIC (audiomixmatrix_debug); +#define GST_CAT_DEFAULT audiomixmatrix_debug + +/* GstAudioMixMatrix properties */ +enum +{ + PROP_0, + PROP_IN_CHANNELS, + PROP_OUT_CHANNELS, + PROP_MATRIX, + PROP_MATRIX_VALUE_ARRAY, + PROP_CHANNEL_MASK, + PROP_MODE +}; + +GType +gst_audio_mix_matrix_mode_get_type (void) +{ + static GType gst_audio_mix_matrix_mode_type = 0; + static const GEnumValue gst_audio_mix_matrix_mode[] = { + {GST_AUDIO_MIX_MATRIX_MODE_MANUAL, + "Manual mode: please specify input/output channels and transformation matrix", + "manual"}, + {GST_AUDIO_MIX_MATRIX_MODE_FIRST_CHANNELS, + "First channels mode: input/output channels are auto-negotiated, transformation matrix is a truncated identity matrix", + "first-channels"}, + {0, NULL, NULL} + + }; + + if (!gst_audio_mix_matrix_mode_type) { + gst_audio_mix_matrix_mode_type = + g_enum_register_static ("GstAudioMixMatrixModeType", + gst_audio_mix_matrix_mode); + } + return gst_audio_mix_matrix_mode_type; +} + +static GstStaticPadTemplate gst_audio_mix_matrix_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS + ("audio/x-raw, channels = [1, max], layout = (string) interleaved, format = (string) {" + GST_AUDIO_NE (F32) "," GST_AUDIO_NE (F64) "," GST_AUDIO_NE (S16) "," + GST_AUDIO_NE (S32) "}") + ); + +static GstStaticPadTemplate gst_audio_mix_matrix_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS + ("audio/x-raw, channels = [1, max], layout = (string) interleaved, format = (string) {" + GST_AUDIO_NE (F32) "," GST_AUDIO_NE (F64) "," GST_AUDIO_NE (S16) "," + GST_AUDIO_NE (S32) "}") + ); + +static void gst_audio_mix_matrix_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_audio_mix_matrix_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static void gst_audio_mix_matrix_dispose (GObject * object); +static gboolean gst_audio_mix_matrix_get_unit_size (GstBaseTransform * trans, + GstCaps * caps, gsize * size); +static gboolean gst_audio_mix_matrix_set_caps (GstBaseTransform * trans, + GstCaps * incaps, GstCaps * outcaps); +static GstFlowReturn gst_audio_mix_matrix_transform (GstBaseTransform * trans, + GstBuffer * inbuf, GstBuffer * outbuf); +static GstCaps *gst_audio_mix_matrix_transform_caps (GstBaseTransform * trans, + GstPadDirection direction, GstCaps * caps, GstCaps * filter); +static GstCaps *gst_audio_mix_matrix_fixate_caps (GstBaseTransform * trans, + GstPadDirection direction, GstCaps * caps, GstCaps * othercaps); +static GstStateChangeReturn gst_audio_mix_matrix_change_state (GstElement * + element, GstStateChange transition); + +G_DEFINE_TYPE (GstAudioMixMatrix, gst_audio_mix_matrix, + GST_TYPE_BASE_TRANSFORM); + +static void +gst_audio_mix_matrix_class_init (GstAudioMixMatrixClass * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + GstElementClass *element_class = (GstElementClass *) klass; + GstBaseTransformClass *trans_class = (GstBaseTransformClass *) klass; + + GST_DEBUG_CATEGORY_INIT (audiomixmatrix_debug, "audiomixmatrix", 0, + "audiomixmatrix"); + gst_element_class_set_static_metadata (element_class, "Matrix audio mix", + "Filter/Audio", + "Mixes a number of input channels into a number of output channels according to a transformation matrix", + "Vivia Nikolaidou "); + + gobject_class->set_property = gst_audio_mix_matrix_set_property; + gobject_class->get_property = gst_audio_mix_matrix_get_property; + gobject_class->dispose = gst_audio_mix_matrix_dispose; + + g_object_class_install_property (gobject_class, PROP_IN_CHANNELS, + g_param_spec_uint ("in-channels", "Input audio channels", + "How many audio channels we have on the input side", + 0, 64, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_OUT_CHANNELS, + g_param_spec_uint ("out-channels", "Output audio channels", + "How many audio channels we have on the output side", + 0, 64, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_MATRIX, + gst_param_spec_array ("matrix", + "Input/output channel matrix", + "Transformation matrix for input/output channels", + gst_param_spec_array ("matrix-in1", "rows", "rows", + g_param_spec_double ("matrix-in2", "cols", "cols", + -1, 1, 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS), + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS), + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_MATRIX_VALUE_ARRAY, + g_param_spec_value_array ("matrix-value-array", + "Input/output channel matrix (GValueArray)", + "Transformation matrix (GValueArray) for input/output channels", + g_param_spec_value_array ("matrix-va-in1", "rows", "rows", + g_param_spec_double ("matrix-va-in2", "cols", "cols", + -1, 1, 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS), + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS), + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_CHANNEL_MASK, + g_param_spec_uint64 ("channel-mask", + "Output channel mask", + "Output channel mask (-1 means \"default for these channels\")", + 0, G_MAXUINT64, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_MODE, + g_param_spec_enum ("mode", + "Channel/matrix mode", + "Whether to auto-negotiate input/output channels and matrix", + GST_TYPE_AUDIO_MIX_MATRIX_MODE, + GST_AUDIO_MIX_MATRIX_MODE_MANUAL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_audio_mix_matrix_sink_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_audio_mix_matrix_src_template)); + + trans_class->get_unit_size = + GST_DEBUG_FUNCPTR (gst_audio_mix_matrix_get_unit_size); + trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_audio_mix_matrix_set_caps); + trans_class->transform = GST_DEBUG_FUNCPTR (gst_audio_mix_matrix_transform); + trans_class->transform_caps = + GST_DEBUG_FUNCPTR (gst_audio_mix_matrix_transform_caps); + trans_class->fixate_caps = + GST_DEBUG_FUNCPTR (gst_audio_mix_matrix_fixate_caps); + + element_class->change_state = + GST_DEBUG_FUNCPTR (gst_audio_mix_matrix_change_state); +} + +static void +gst_audio_mix_matrix_init (GstAudioMixMatrix * self) +{ + self->in_channels = 0; + self->out_channels = 0; + self->matrix = NULL; + self->channel_mask = 0; + self->s16_conv_matrix = NULL; + self->s32_conv_matrix = NULL; + self->mode = GST_AUDIO_MIX_MATRIX_MODE_MANUAL; +} + +static void +gst_audio_mix_matrix_dispose (GObject * object) +{ + GstAudioMixMatrix *self = GST_AUDIO_MIX_MATRIX (object); + + if (self->matrix) { + g_free (self->matrix); + self->matrix = NULL; + } + + G_OBJECT_CLASS (gst_audio_mix_matrix_parent_class)->dispose (object); +} + +static void +gst_audio_mix_matrix_convert_s16_matrix (GstAudioMixMatrix * self) +{ + gint i; + + /* converted bits - input bits - sign - bits needed for channel */ + self->shift_bytes = 32 - 16 - 1 - ceil (log (self->in_channels) / log (2)); + + if (self->s16_conv_matrix) + g_free (self->s16_conv_matrix); + self->s16_conv_matrix = + g_new (gint32, self->in_channels * self->out_channels); + for (i = 0; i < self->in_channels * self->out_channels; i++) { + self->s16_conv_matrix[i] = + (gint32) ((self->matrix[i]) * (1 << self->shift_bytes)); + } +} + +static void +gst_audio_mix_matrix_convert_s32_matrix (GstAudioMixMatrix * self) +{ + gint i; + + /* converted bits - input bits - sign - bits needed for channel */ + self->shift_bytes = 64 - 32 - 1 - (gint) (log (self->in_channels) / log (2)); + + if (self->s32_conv_matrix) + g_free (self->s32_conv_matrix); + self->s32_conv_matrix = + g_new (gint64, self->in_channels * self->out_channels); + for (i = 0; i < self->in_channels * self->out_channels; i++) { + self->s32_conv_matrix[i] = + (gint64) ((self->matrix[i]) * (1 << self->shift_bytes)); + } +} + + +static void +gst_audio_mix_matrix_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstAudioMixMatrix *self = GST_AUDIO_MIX_MATRIX (object); + + switch (prop_id) { + case PROP_IN_CHANNELS: + self->in_channels = g_value_get_uint (value); + if (self->matrix) { + gst_audio_mix_matrix_convert_s16_matrix (self); + gst_audio_mix_matrix_convert_s32_matrix (self); + } + break; + case PROP_OUT_CHANNELS: + self->out_channels = g_value_get_uint (value); + if (self->matrix) { + gst_audio_mix_matrix_convert_s16_matrix (self); + gst_audio_mix_matrix_convert_s32_matrix (self); + } + break; + case PROP_MATRIX:{ + gint in, out; + + if (self->matrix) + g_free (self->matrix); + self->matrix = g_new (gdouble, self->in_channels * self->out_channels); + + g_return_if_fail (gst_value_array_get_size (value) == self->out_channels); + for (out = 0; out < self->out_channels; out++) { + const GValue *row = gst_value_array_get_value (value, out); + g_return_if_fail (gst_value_array_get_size (row) == self->in_channels); + for (in = 0; in < self->in_channels; in++) { + const GValue *itm; + gdouble coefficient; + + itm = gst_value_array_get_value (row, in); + g_return_if_fail (G_VALUE_HOLDS_DOUBLE (itm)); + coefficient = g_value_get_double (itm); + self->matrix[out * self->in_channels + in] = coefficient; + } + } + gst_audio_mix_matrix_convert_s16_matrix (self); + gst_audio_mix_matrix_convert_s32_matrix (self); + break; + } + case PROP_MATRIX_VALUE_ARRAY:{ + gint in, out; + GValueArray *a; + + if (self->matrix) + g_free (self->matrix); + self->matrix = g_new (gdouble, self->in_channels * self->out_channels); + + a = g_value_get_boxed (value); + g_return_if_fail (a->n_values == self->out_channels); + for (out = 0; out < self->out_channels; out++) { + const GValue *row = g_value_array_get_nth (a, out); + GValueArray *ra = g_value_get_boxed (row); + + g_return_if_fail (ra->n_values == self->in_channels); + for (in = 0; in < self->in_channels; in++) { + const GValue *itm; + gdouble coefficient; + + itm = g_value_array_get_nth (ra, in); + g_return_if_fail (G_VALUE_HOLDS_DOUBLE (itm)); + coefficient = g_value_get_double (itm); + self->matrix[out * self->in_channels + in] = coefficient; + } + } + gst_audio_mix_matrix_convert_s16_matrix (self); + gst_audio_mix_matrix_convert_s32_matrix (self); + break; + } + case PROP_CHANNEL_MASK: + self->channel_mask = g_value_get_uint64 (value); + break; + case PROP_MODE: + self->mode = g_value_get_enum (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_audio_mix_matrix_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstAudioMixMatrix *self = GST_AUDIO_MIX_MATRIX (object); + + switch (prop_id) { + case PROP_IN_CHANNELS: + g_value_set_uint (value, self->in_channels); + break; + case PROP_OUT_CHANNELS: + g_value_set_uint (value, self->out_channels); + break; + case PROP_MATRIX:{ + gint in, out; + + for (out = 0; out < self->out_channels; out++) { + GValue row = G_VALUE_INIT; + g_value_init (&row, GST_TYPE_ARRAY); + for (in = 0; in < self->in_channels; in++) { + GValue itm = G_VALUE_INIT; + g_value_init (&itm, G_TYPE_DOUBLE); + g_value_set_double (&itm, self->matrix[out * self->in_channels + in]); + gst_value_array_append_value (&row, &itm); + g_value_unset (&itm); + } + gst_value_array_append_value (value, &row); + g_value_unset (&row); + } + break; + } + case PROP_MATRIX_VALUE_ARRAY:{ + gint in, out; + GValueArray *a = g_value_array_new (self->out_channels); + + for (out = 0; out < self->out_channels; out++) { + GValue row = G_VALUE_INIT; + GValueArray *ra = g_value_array_new (self->in_channels); + + g_value_init (&row, G_TYPE_VALUE_ARRAY); + for (in = 0; in < self->in_channels; in++) { + GValue itm = G_VALUE_INIT; + g_value_init (&itm, G_TYPE_DOUBLE); + g_value_set_double (&itm, self->matrix[out * self->in_channels + in]); + g_value_array_append (ra, &itm); + g_value_unset (&itm); + } + g_value_take_boxed (&row, ra); + g_value_array_append (a, &row); + g_value_unset (&row); + } + g_value_take_boxed (value, a); + break; + } + case PROP_CHANNEL_MASK: + g_value_set_uint64 (value, self->channel_mask); + break; + case PROP_MODE: + g_value_set_enum (value, self->mode); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GstStateChangeReturn +gst_audio_mix_matrix_change_state (GstElement * element, + GstStateChange transition) +{ + GstAudioMixMatrix *self = GST_AUDIO_MIX_MATRIX (element); + GstStateChangeReturn s; + + s = GST_ELEMENT_CLASS (gst_audio_mix_matrix_parent_class)->change_state + (element, transition); + + if (transition == GST_STATE_CHANGE_PAUSED_TO_READY) { + if (self->s16_conv_matrix) { + g_free (self->s16_conv_matrix); + self->s16_conv_matrix = NULL; + } + + if (self->s32_conv_matrix) { + g_free (self->s32_conv_matrix); + self->s32_conv_matrix = NULL; + } + } + + return s; +} + + +static GstFlowReturn +gst_audio_mix_matrix_transform (GstBaseTransform * vfilter, + GstBuffer * inbuf, GstBuffer * outbuf) +{ + GstMapInfo inmap, outmap; + GstAudioMixMatrix *self = GST_AUDIO_MIX_MATRIX (vfilter); + gint in, out, sample; + guint inchannels = self->in_channels; + guint outchannels = self->out_channels; + gdouble *matrix = self->matrix; + + if (!gst_buffer_map (inbuf, &inmap, GST_MAP_READ)) { + return GST_FLOW_ERROR; + } + if (!gst_buffer_map (outbuf, &outmap, GST_MAP_WRITE)) { + gst_buffer_unmap (inbuf, &inmap); + return GST_FLOW_ERROR; + } + + switch (self->format) { + case GST_AUDIO_FORMAT_F32LE: + case GST_AUDIO_FORMAT_F32BE:{ + const gfloat *inarray; + gfloat *outarray; + guint n_samples = outmap.size / (sizeof (gfloat) * outchannels); + + inarray = (gfloat *) inmap.data; + outarray = (gfloat *) outmap.data; + + for (sample = 0; sample < n_samples; sample++) { + for (out = 0; out < outchannels; out++) { + gfloat outval = 0; + for (in = 0; in < inchannels; in++) { + outval += + inarray[sample * inchannels + + in] * matrix[out * inchannels + in]; + } + outarray[sample * outchannels + out] = outval; + } + } + break; + } + case GST_AUDIO_FORMAT_F64LE: + case GST_AUDIO_FORMAT_F64BE:{ + const gdouble *inarray; + gdouble *outarray; + guint n_samples = outmap.size / (sizeof (gdouble) * outchannels); + + inarray = (gdouble *) inmap.data; + outarray = (gdouble *) outmap.data; + + for (sample = 0; sample < n_samples; sample++) { + for (out = 0; out < outchannels; out++) { + gdouble outval = 0; + for (in = 0; in < inchannels; in++) { + outval += + inarray[sample * inchannels + + in] * matrix[out * inchannels + in]; + } + outarray[sample * outchannels + out] = outval; + } + } + break; + } + case GST_AUDIO_FORMAT_S16LE: + case GST_AUDIO_FORMAT_S16BE:{ + const gint16 *inarray; + gint16 *outarray; + guint n_samples = outmap.size / (sizeof (gint16) * outchannels); + guint n = self->shift_bytes; + gint32 *conv_matrix = self->s16_conv_matrix; + + inarray = (gint16 *) inmap.data; + outarray = (gint16 *) outmap.data; + + for (sample = 0; sample < n_samples; sample++) { + for (out = 0; out < outchannels; out++) { + gint32 outval = 0; + for (in = 0; in < inchannels; in++) { + outval += (gint32) (inarray[sample * inchannels + in] * + conv_matrix[out * inchannels + in]); + } + outarray[sample * outchannels + out] = (gint16) (outval >> n); + } + } + break; + } + case GST_AUDIO_FORMAT_S32LE: + case GST_AUDIO_FORMAT_S32BE:{ + const gint32 *inarray; + gint32 *outarray; + guint n_samples = outmap.size / (sizeof (gint32) * outchannels); + guint n = self->shift_bytes; + gint64 *conv_matrix = self->s32_conv_matrix; + + inarray = (gint32 *) inmap.data; + outarray = (gint32 *) outmap.data; + + for (sample = 0; sample < n_samples; sample++) { + for (out = 0; out < outchannels; out++) { + gint64 outval = 0; + for (in = 0; in < inchannels; in++) { + outval += (gint64) (inarray[sample * inchannels + in] * + conv_matrix[out * inchannels + in]); + } + outarray[sample * outchannels + out] = (gint32) (outval >> n); + } + } + break; + } + default: + gst_buffer_unmap (inbuf, &inmap); + gst_buffer_unmap (outbuf, &outmap); + return GST_FLOW_NOT_SUPPORTED; + + } + + gst_buffer_unmap (inbuf, &inmap); + gst_buffer_unmap (outbuf, &outmap); + return GST_FLOW_OK; +} + +static gboolean +gst_audio_mix_matrix_get_unit_size (GstBaseTransform * trans, + GstCaps * caps, gsize * size) +{ + GstAudioInfo info; + + if (!gst_audio_info_from_caps (&info, caps)) + return FALSE; + + *size = GST_AUDIO_INFO_BPF (&info); + + return TRUE; +} + +static gboolean +gst_audio_mix_matrix_set_caps (GstBaseTransform * trans, GstCaps * incaps, + GstCaps * outcaps) +{ + GstAudioMixMatrix *self = GST_AUDIO_MIX_MATRIX (trans); + GstAudioInfo info, out_info; + + if (!gst_audio_info_from_caps (&info, incaps)) + return FALSE; + + if (!gst_audio_info_from_caps (&out_info, outcaps)) + return FALSE; + + self->format = info.finfo->format; + + if (self->mode == GST_AUDIO_MIX_MATRIX_MODE_FIRST_CHANNELS) { + gint in, out; + + self->in_channels = info.channels; + self->out_channels = out_info.channels; + + self->matrix = g_new (gdouble, self->in_channels * self->out_channels); + + for (out = 0; out < self->out_channels; out++) { + for (in = 0; in < self->in_channels; in++) { + self->matrix[out * self->in_channels + in] = (out == in); + } + } + } else if (!self->matrix || info.channels != self->in_channels || + out_info.channels != self->out_channels) { + GST_ELEMENT_ERROR (self, LIBRARY, SETTINGS, + ("Erroneous matrix detected"), + ("Please enter a matrix with the correct input and output channels")); + return FALSE; + } + + switch (self->format) { + case GST_AUDIO_FORMAT_S16LE: + case GST_AUDIO_FORMAT_S16BE:{ + gst_audio_mix_matrix_convert_s16_matrix (self); + break; + } + case GST_AUDIO_FORMAT_S32LE: + case GST_AUDIO_FORMAT_S32BE:{ + gst_audio_mix_matrix_convert_s32_matrix (self); + break; + } + default: + break; + } + return TRUE; +} + +static GstCaps * +gst_audio_mix_matrix_fixate_caps (GstBaseTransform * trans, + GstPadDirection direction, GstCaps * caps, GstCaps * othercaps) +{ + GstAudioMixMatrix *self = GST_AUDIO_MIX_MATRIX (trans); + GstStructure *s, *s2; + guint capssize = gst_caps_get_size (othercaps); + gint i; + gint channels; + + if (self->mode == GST_AUDIO_MIX_MATRIX_MODE_FIRST_CHANNELS) { + s2 = gst_caps_get_structure (caps, 0); + + /* Try to keep channel configuration as much as possible */ + if (gst_structure_get_int (s2, "channels", &channels)) { + gint mindiff = channels; + othercaps = gst_caps_make_writable (othercaps); + for (i = 0; i < capssize; i++) { + s = gst_caps_get_structure (othercaps, i); + if (!gst_structure_has_field (s, "channels")) { + mindiff = 0; + gst_structure_set (s, "channels", G_TYPE_INT, channels, NULL); + } else { + gint outchannels; + gint diff; + + gst_structure_fixate_field_nearest_int (s, "channels", channels); + if (gst_structure_get_int (s, "channels", &outchannels)) { + diff = ABS (channels - outchannels); + if (diff < mindiff) + mindiff = diff; + } + } + } + + for (i = 0; i < capssize; i++) { + gint outchannels, diff; + s = gst_caps_get_structure (othercaps, i); + if (gst_structure_get_int (s, "channels", &outchannels)) { + diff = ABS (channels - outchannels); + if (diff > mindiff) { + gst_caps_remove_structure (othercaps, i--); + capssize--; + } + } + } + } + } + + othercaps = + GST_BASE_TRANSFORM_CLASS (gst_audio_mix_matrix_parent_class)->fixate_caps + (trans, direction, caps, othercaps); + + s = gst_caps_get_structure (othercaps, 0); + + if (!gst_structure_has_field (s, "channel-mask")) { + if (self->mode == GST_AUDIO_MIX_MATRIX_MODE_FIRST_CHANNELS || + self->channel_mask == -1) { + gint channels; + + g_assert (gst_structure_get_int (s, "channels", &channels)); + gst_structure_set (s, "channel-mask", GST_TYPE_BITMASK, + gst_audio_channel_get_fallback_mask (channels), NULL); + } else { + gst_structure_set (s, "channel-mask", GST_TYPE_BITMASK, + self->channel_mask, NULL); + } + } + + return othercaps; + +} + +static GstCaps * +gst_audio_mix_matrix_transform_caps (GstBaseTransform * trans, + GstPadDirection direction, GstCaps * caps, GstCaps * filter) +{ + GstAudioMixMatrix *self = GST_AUDIO_MIX_MATRIX (trans); + GstCaps *outcaps = gst_caps_copy (caps); + GstCaps *ret; + GstStructure *s; + gint i; + guint capssize = gst_caps_get_size (outcaps); + + if (self->mode == GST_AUDIO_MIX_MATRIX_MODE_FIRST_CHANNELS) { + for (i = 0; i < capssize; i++) { + s = gst_caps_get_structure (outcaps, i); + if (gst_structure_has_field (s, "channels")) { + gst_structure_remove_field (s, "channels"); + } + if (gst_structure_has_field (s, "channel-mask")) { + gst_structure_remove_field (s, "channel-mask"); + } + } + goto beach; + } + + if (self->in_channels == 0 || self->out_channels == 0 || self->matrix == NULL) { + /* Not dispatching element error because we return empty caps anyway and + * we should let it fail to link. Additionally, the element error would be + * printed as WARN, so a possible gst-launch pipeline would appear to + * hang. */ + GST_ERROR_OBJECT (self, "Invalid settings detected in manual mode. " + "Please specify in-channels, out-channels and matrix."); + return gst_caps_new_empty (); + } + + if (self->in_channels == self->out_channels) { + goto beach; + } + + for (i = 0; i < capssize; i++) { + s = gst_caps_get_structure (outcaps, i); + if (direction == GST_PAD_SRC) { + gst_structure_set (s, "channels", G_TYPE_INT, self->in_channels, NULL); + gst_structure_remove_field (s, "channel-mask"); + } else if (direction == GST_PAD_SINK) { + gst_structure_set (s, "channels", G_TYPE_INT, self->out_channels, + "channel-mask", GST_TYPE_BITMASK, self->channel_mask, NULL); + } else { + g_assert_not_reached (); + } + } + +beach: + if (filter) { + ret = gst_caps_intersect_full (filter, outcaps, GST_CAPS_INTERSECT_FIRST); + gst_caps_unref (outcaps); + } else { + ret = outcaps; + } + return ret; +} + +static gboolean +plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "audiomixmatrix", GST_RANK_NONE, + GST_TYPE_AUDIO_MIX_MATRIX); +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + audiomixmatrix, + "Audio matrix mix", + plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN); diff --git a/gst/audiomixmatrix/gstaudiomixmatrix.h b/gst/audiomixmatrix/gstaudiomixmatrix.h new file mode 100644 index 0000000..5321954 --- /dev/null +++ b/gst/audiomixmatrix/gstaudiomixmatrix.h @@ -0,0 +1,78 @@ +/* + * GStreamer + * Copyright (C) 2017 Vivia Nikolaidou + * + * gstaudiomixmatrix.h + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_AUDIO_MIX_MATRIX_H__ +#define __GST_AUDIO_MIX_MATRIX_H__ + +#include +#include + +#define GST_TYPE_AUDIO_MIX_MATRIX (gst_audio_mix_matrix_get_type()) +#define GST_AUDIO_MIX_MATRIX(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AUDIO_MIX_MATRIX,GstAudioMixMatrix)) +#define GST_AUDIO_MIX_MATRIX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_AUDIO_MIX_MATRIX,GstAudioMixMatrixClass)) +#define GST_AUDIO_MIX_MATRIX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GST_TYPE_AUDIO_MIX_MATRIX,GstAudioMixMatrixClass)) +#define GST_IS_AUDIO_MIX_MATRIX(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AUDIO_MIX_MATRIX)) +#define GST_IS_AUDIO_MIX_MATRIX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_AUDIO_MIX_MATRIX)) +#define GST_TYPE_AUDIO_MIX_MATRIX_MODE (gst_audio_mix_matrix_mode_get_type()) + +typedef struct _GstAudioMixMatrix GstAudioMixMatrix; +typedef struct _GstAudioMixMatrixClass GstAudioMixMatrixClass; + +typedef enum _GstAudioMixMatrixMode +{ + GST_AUDIO_MIX_MATRIX_MODE_MANUAL = 0, + GST_AUDIO_MIX_MATRIX_MODE_FIRST_CHANNELS = 1 +} GstAudioMixMatrixMode; + +/** + * GstAudioMixMatrix: + * + * Opaque data structure. + */ +struct _GstAudioMixMatrix +{ + GstBaseTransform audiofilter; + + /* < private > */ + guint in_channels; + guint out_channels; + gdouble *matrix; + guint64 channel_mask; + GstAudioMixMatrixMode mode; + gint32 *s16_conv_matrix; + gint64 *s32_conv_matrix; + gint shift_bytes; + + GstAudioFormat format; +}; + +struct _GstAudioMixMatrixClass +{ + GstBaseTransformClass parent_class; +}; + +GType gst_audio_mix_matrix_get_type (void); + +GType gst_audio_mix_matrix_mode_get_type (void); + +G_END_DECLS +#endif /* __GST_AUDIO_MIX_MATRIX_H__ */ diff --git a/tests/examples/Makefile.am b/tests/examples/Makefile.am index 57ed6dd..c9b5e0b 100644 --- a/tests/examples/Makefile.am +++ b/tests/examples/Makefile.am @@ -18,6 +18,8 @@ endif OPENCV_EXAMPLES=opencv +MATRIXMIX_DIR=audiomixmatrix + if USE_OPENGL GL_DIR=gl else @@ -57,8 +59,8 @@ playout_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) playout_LDADD = $(GST_PLUGINS_BASE_LIBS) -lgstvideo-$(GST_API_VERSION) $(GST_LIBS) SUBDIRS= codecparsers mpegts $(DIRECTFB_DIR) $(GTK_EXAMPLES) $(OPENCV_EXAMPLES) \ - $(GL_DIR) $(GTK3_DIR) $(AVSAMPLE_DIR) $(WAYLAND_DIR) + $(GL_DIR) $(GTK3_DIR) $(AVSAMPLE_DIR) $(WAYLAND_DIR) $(MATRIXMIX_DIR) DIST_SUBDIRS= codecparsers mpegts camerabin2 directfb mxf opencv uvch264 gl gtk \ - avsamplesink waylandsink + avsamplesink waylandsink audiomixmatrix include $(top_srcdir)/common/parallel-subdirs.mak diff --git a/tests/examples/audiomixmatrix/Makefile.am b/tests/examples/audiomixmatrix/Makefile.am new file mode 100644 index 0000000..e79c355 --- /dev/null +++ b/tests/examples/audiomixmatrix/Makefile.am @@ -0,0 +1,16 @@ +TEST_AUDIOMIXMATRIX_EXAMPLES = test-audiomixmatrix + +test_audiomixmatrix_SOURCES = test-audiomixmatrix.c +test_audiomixmatrix_CFLAGS = \ + $(GST_PLUGINS_BAD_CFLAGS) \ + $(GST_PLUGINS_BASE_CFLAGS) \ + $(GST_CFLAGS) \ + $(GMODULE_EXPORT_CFLAGS) \ + -DGST_USE_UNSTABLE_API +test_audiomixmatrix_LDADD = \ + $(GST_PLUGINS_BASE_LIBS) \ + $(GST_LIBS) \ + $(GMODULE_EXPORT_LIBS) + +noinst_PROGRAMS = $(TEST_AUDIOMIXMATRIX_EXAMPLES) + diff --git a/tests/examples/audiomixmatrix/test-audiomixmatrix.c b/tests/examples/audiomixmatrix/test-audiomixmatrix.c new file mode 100644 index 0000000..3cddc10 --- /dev/null +++ b/tests/examples/audiomixmatrix/test-audiomixmatrix.c @@ -0,0 +1,244 @@ +/* + * GStreamer + * Copyright (C) 2017 Vivia Nikolaidou + * + * test-audiomixmatrix.c + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#define USE_GST_VALUE_ARRAY + +#ifndef USE_GST_VALUE_ARRAY +/* FIXME 2.0: suppress warnings for deprecated API such as GValueArray + * with newer GLib versions (>= 2.31.0) */ +#define GLIB_DISABLE_DEPRECATION_WARNINGS +#endif + +#include +#include + +static gboolean +message_cb (GstBus * bus, GstMessage * message, gpointer user_data) +{ + switch (GST_MESSAGE_TYPE (message)) { + case GST_MESSAGE_ERROR:{ + GError *err = NULL; + gchar *debug = NULL; + + gst_message_parse_error (message, &err, &debug); + gst_printerrln ("Error message received: %s", err->message); + gst_printerrln ("Debug info: %s", debug); + g_error_free (err); + g_free (debug); + } + case GST_MESSAGE_EOS: + g_main_loop_quit (user_data); + break; + default: + break; + } + return TRUE; +} + +static GstPadProbeReturn +_event_received (GstPad * pad, GstPadProbeInfo * info, gpointer user_data) +{ + GstCaps *caps; + gchar *caps_str; + GstEvent *event = GST_EVENT (info->data); + + if (GST_EVENT_TYPE (event) != GST_EVENT_CAPS) + return GST_PAD_PROBE_OK; + + gst_event_parse_caps (event, &caps); + if (!gst_caps_is_fixed (caps)) + return GST_PAD_PROBE_OK; + + caps_str = gst_caps_to_string (caps); + + g_print ("Caps received on %s: %s\n", + GST_PAD_IS_SRC (pad) ? "source" : "sink", caps_str); + + g_free (caps_str); + + return GST_PAD_PROBE_OK; +} + +int +main (int argc, char **argv) +{ + GstElement *audiotestsrc, *capsfilter, *audiomixmatrix, *audioconvert, *sink; + GstPad *srcpad, *sinkpad; + GstBus *bus; + GMainLoop *loop; + GstCaps *caps; + GValue v2 = G_VALUE_INIT; + GValue v3 = G_VALUE_INIT; + GstElement *pipeline; +#ifdef USE_GST_VALUE_ARRAY + GValue v = G_VALUE_INIT; + gchar *serialized_matrix; +#else + GValueArray *a, *ra; +#endif + + gst_init (&argc, &argv); + + audiotestsrc = gst_element_factory_make ("audiotestsrc", "audiotestsrc"); + capsfilter = gst_element_factory_make ("capsfilter", "capsfilter"); + caps = + gst_caps_from_string + ("audio/x-raw,channels=4,channel-mask=(bitmask)0,format=S32LE"); + g_object_set (capsfilter, "caps", caps, NULL); + gst_caps_unref (caps); + audiomixmatrix = + gst_element_factory_make ("audiomixmatrix", "audiomixmatrix"); + g_object_set (audiomixmatrix, "in-channels", 4, NULL); + g_object_set (audiomixmatrix, "out-channels", 2, NULL); + g_object_set (audiomixmatrix, "channel-mask", 3, NULL); + /* So the serialized matrix will be: < < 1, 0, 0, 0 >, < 0, 1, 0, 0 > > */ +#ifdef USE_GST_VALUE_ARRAY + g_value_init (&v, GST_TYPE_ARRAY); + g_value_init (&v2, GST_TYPE_ARRAY); + g_value_init (&v3, G_TYPE_DOUBLE); + g_value_set_double (&v3, 1); + gst_value_array_append_value (&v2, &v3); + g_value_unset (&v3); + g_value_init (&v3, G_TYPE_DOUBLE); + g_value_set_double (&v3, 0); + gst_value_array_append_value (&v2, &v3); + g_value_unset (&v3); + g_value_init (&v3, G_TYPE_DOUBLE); + g_value_set_double (&v3, 0); + gst_value_array_append_value (&v2, &v3); + g_value_unset (&v3); + g_value_init (&v3, G_TYPE_DOUBLE); + g_value_set_double (&v3, 0); + gst_value_array_append_value (&v2, &v3); + g_value_unset (&v3); + gst_value_array_append_value (&v, &v2); + g_value_unset (&v2); + g_value_init (&v2, GST_TYPE_ARRAY); + g_value_init (&v3, G_TYPE_DOUBLE); + g_value_set_double (&v3, 0); + gst_value_array_append_value (&v2, &v3); + g_value_unset (&v3); + g_value_init (&v3, G_TYPE_DOUBLE); + g_value_set_double (&v3, 1); + gst_value_array_append_value (&v2, &v3); + g_value_unset (&v3); + g_value_init (&v3, G_TYPE_DOUBLE); + g_value_set_double (&v3, 0); + gst_value_array_append_value (&v2, &v3); + g_value_unset (&v3); + g_value_init (&v3, G_TYPE_DOUBLE); + g_value_set_double (&v3, 0); + gst_value_array_append_value (&v2, &v3); + g_value_unset (&v3); + gst_value_array_append_value (&v, &v2); + g_value_unset (&v2); + g_object_set_property (G_OBJECT (audiomixmatrix), "matrix", &v); + /* Alternatively: gst_util_set_object_arg (audiomixmatrix, "matrix", "< < 1, 0> ..."); */ + serialized_matrix = gst_value_serialize (&v); + gst_printerrln ("Serialized matrix: %s", serialized_matrix); + g_free (serialized_matrix); + g_value_unset (&v); +#else + a = g_value_array_new (4); + ra = g_value_array_new (2); + g_value_init (&v2, G_TYPE_VALUE_ARRAY); + g_value_init (&v3, G_TYPE_DOUBLE); + g_value_set_double (&v3, 1); + g_value_array_append (ra, &v3); + g_value_unset (&v3); + g_value_init (&v3, G_TYPE_DOUBLE); + g_value_set_double (&v3, 0); + g_value_array_append (ra, &v3); + g_value_unset (&v3); + g_value_init (&v3, G_TYPE_DOUBLE); + g_value_set_double (&v3, 0); + g_value_array_append (ra, &v3); + g_value_unset (&v3); + g_value_init (&v3, G_TYPE_DOUBLE); + g_value_set_double (&v3, 0); + g_value_array_append (ra, &v3); + g_value_unset (&v3); + g_value_take_boxed (&v2, ra); + g_value_array_append (a, &v2); + g_value_unset (&v2); + ra = g_value_array_new (2); + g_value_init (&v2, G_TYPE_VALUE_ARRAY); + g_value_init (&v3, G_TYPE_DOUBLE); + g_value_set_double (&v3, 0); + g_value_array_append (ra, &v3); + g_value_unset (&v3); + g_value_init (&v3, G_TYPE_DOUBLE); + g_value_set_double (&v3, 1); + g_value_array_append (ra, &v3); + g_value_unset (&v3); + g_value_init (&v3, G_TYPE_DOUBLE); + g_value_set_double (&v3, 0); + g_value_array_append (ra, &v3); + g_value_unset (&v3); + g_value_init (&v3, G_TYPE_DOUBLE); + g_value_set_double (&v3, 0); + g_value_array_append (ra, &v3); + g_value_unset (&v3); + g_value_take_boxed (&v2, ra); + g_value_array_append (a, &v2); + g_value_unset (&v2); + g_object_set (G_OBJECT (audiomixmatrix), "matrix-value-array", a, NULL); + g_value_array_free (a); +#endif + audioconvert = gst_element_factory_make ("audioconvert", "audioconvert"); + sink = gst_element_factory_make ("autoaudiosink", "sink"); + pipeline = gst_pipeline_new ("pipe"); + gst_bin_add_many (GST_BIN (pipeline), audiotestsrc, capsfilter, + audiomixmatrix, audioconvert, sink, NULL); + gst_element_link_many (audiotestsrc, capsfilter, + audiomixmatrix, audioconvert, sink, NULL); + + srcpad = gst_element_get_static_pad (audiomixmatrix, "src"); + gst_pad_add_probe (srcpad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, + _event_received, NULL, NULL); + gst_object_unref (srcpad); + sinkpad = gst_element_get_static_pad (audiomixmatrix, "sink"); + gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, + _event_received, NULL, NULL); + gst_object_unref (sinkpad); + + if (gst_element_set_state (pipeline, GST_STATE_PLAYING) == + GST_STATE_CHANGE_FAILURE) { + g_printerr ("ERROR: Could not change state in pipeline!\n"); + gst_object_unref (pipeline); + return 1; + } + + bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); + loop = g_main_loop_new (NULL, FALSE); + gst_bus_add_signal_watch (bus); + g_signal_connect (G_OBJECT (bus), "message", G_CALLBACK (message_cb), loop); + g_main_loop_run (loop); + + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (bus); + gst_object_unref (pipeline); + + + return 0; + +} -- 2.7.4