| /* |
| * GStreamer |
| * Copyright (C) 2017 Vivia Nikolaidou <vivia@toolsonair.com> |
| * |
| * 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 |
| * @title: 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 |
| |
| #include "gstaudiomixmatrix.h" |
| |
| #include <gst/gst.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <math.h> |
| |
| 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_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 <vivia@toolsonair.com>"); |
| |
| 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_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_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; |
| |
| if (self->matrix == NULL) |
| break; |
| |
| 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_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 = -1; |
| 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 (mindiff < 0 || diff < mindiff) |
| mindiff = diff; |
| } |
| } |
| } |
| |
| if (mindiff >= 0) { |
| 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--; |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| if (gst_caps_is_empty (othercaps)) |
| return othercaps; |
| |
| 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); |