• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * GStreamer
3  * Copyright (C) 2017 Vivia Nikolaidou <vivia@toolsonair.com>
4  *
5  * gstaudiomixmatrix.c
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22 
23 /**
24  * SECTION:element-audiomixmatrix
25  * @title: audiomixmatrix
26  * @short_description: Transform input/output channels according to a matrix
27  *
28  * This element transforms a given number of input channels into a given
29  * number of output channels according to a given transformation matrix. The
30  * matrix coefficients must be between -1 and 1: the number of rows is equal
31  * to the number of output channels and the number of columns is equal to the
32  * number of input channels. In the first-channels mode, input/output channels
33  * are automatically negotiated and the transformation matrix is a truncated
34  * identity matrix.
35  *
36  * ## Example matrix generation code
37  * To generate the matrix using code:
38  *
39  * |[
40  * GValue v = G_VALUE_INIT;
41  * GValue v2 = G_VALUE_INIT;
42  * GValue v3 = G_VALUE_INIT;
43  *
44  * g_value_init (&v2, GST_TYPE_ARRAY);
45  * g_value_init (&v3, G_TYPE_DOUBLE);
46  * g_value_set_double (&v3, 1);
47  * gst_value_array_append_value (&v2, &v3);
48  * g_value_unset (&v3);
49  * [ Repeat for as many double as your input channels - unset and reinit v3 ]
50  * g_value_init (&v, GST_TYPE_ARRAY);
51  * gst_value_array_append_value (&v, &v2);
52  * g_value_unset (&v2);
53  * [ Repeat for as many v2's as your output channels - unset and reinit v2]
54  * g_object_set_property (G_OBJECT (audiomixmatrix), "matrix", &v);
55  * g_value_unset (&v);
56  * ]|
57  *
58  * ## Example launch line
59  * |[
60  * 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
61  * ]|
62  *
63  */
64 
65 #ifdef HAVE_CONFIG_H
66 #include "config.h"
67 #endif
68 
69 #include "gstaudiomixmatrix.h"
70 
71 #include <gst/gst.h>
72 #include <stdlib.h>
73 #include <string.h>
74 #include <math.h>
75 
76 GST_DEBUG_CATEGORY_STATIC (audiomixmatrix_debug);
77 #define GST_CAT_DEFAULT audiomixmatrix_debug
78 
79 /* GstAudioMixMatrix properties */
80 enum
81 {
82   PROP_0,
83   PROP_IN_CHANNELS,
84   PROP_OUT_CHANNELS,
85   PROP_MATRIX,
86   PROP_CHANNEL_MASK,
87   PROP_MODE
88 };
89 
90 GType
gst_audio_mix_matrix_mode_get_type(void)91 gst_audio_mix_matrix_mode_get_type (void)
92 {
93   static GType gst_audio_mix_matrix_mode_type = 0;
94   static const GEnumValue gst_audio_mix_matrix_mode[] = {
95     {GST_AUDIO_MIX_MATRIX_MODE_MANUAL,
96           "Manual mode: please specify input/output channels and transformation matrix",
97         "manual"},
98     {GST_AUDIO_MIX_MATRIX_MODE_FIRST_CHANNELS,
99           "First channels mode: input/output channels are auto-negotiated, transformation matrix is a truncated identity matrix",
100         "first-channels"},
101     {0, NULL, NULL}
102 
103   };
104 
105   if (!gst_audio_mix_matrix_mode_type) {
106     gst_audio_mix_matrix_mode_type =
107         g_enum_register_static ("GstAudioMixMatrixModeType",
108         gst_audio_mix_matrix_mode);
109   }
110   return gst_audio_mix_matrix_mode_type;
111 }
112 
113 static GstStaticPadTemplate gst_audio_mix_matrix_src_template =
114 GST_STATIC_PAD_TEMPLATE ("src",
115     GST_PAD_SRC,
116     GST_PAD_ALWAYS,
117     GST_STATIC_CAPS
118     ("audio/x-raw, channels = [1, max], layout = (string) interleaved, format = (string) {"
119         GST_AUDIO_NE (F32) "," GST_AUDIO_NE (F64) "," GST_AUDIO_NE (S16) ","
120         GST_AUDIO_NE (S32) "}")
121     );
122 
123 static GstStaticPadTemplate gst_audio_mix_matrix_sink_template =
124 GST_STATIC_PAD_TEMPLATE ("sink",
125     GST_PAD_SINK,
126     GST_PAD_ALWAYS,
127     GST_STATIC_CAPS
128     ("audio/x-raw, channels = [1, max], layout = (string) interleaved, format = (string) {"
129         GST_AUDIO_NE (F32) "," GST_AUDIO_NE (F64) "," GST_AUDIO_NE (S16) ","
130         GST_AUDIO_NE (S32) "}")
131     );
132 
133 static void gst_audio_mix_matrix_set_property (GObject * object, guint prop_id,
134     const GValue * value, GParamSpec * pspec);
135 static void gst_audio_mix_matrix_get_property (GObject * object, guint prop_id,
136     GValue * value, GParamSpec * pspec);
137 static void gst_audio_mix_matrix_dispose (GObject * object);
138 static gboolean gst_audio_mix_matrix_get_unit_size (GstBaseTransform * trans,
139     GstCaps * caps, gsize * size);
140 static gboolean gst_audio_mix_matrix_set_caps (GstBaseTransform * trans,
141     GstCaps * incaps, GstCaps * outcaps);
142 static GstFlowReturn gst_audio_mix_matrix_transform (GstBaseTransform * trans,
143     GstBuffer * inbuf, GstBuffer * outbuf);
144 static GstCaps *gst_audio_mix_matrix_transform_caps (GstBaseTransform * trans,
145     GstPadDirection direction, GstCaps * caps, GstCaps * filter);
146 static GstCaps *gst_audio_mix_matrix_fixate_caps (GstBaseTransform * trans,
147     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps);
148 static GstStateChangeReturn gst_audio_mix_matrix_change_state (GstElement *
149     element, GstStateChange transition);
150 
151 G_DEFINE_TYPE (GstAudioMixMatrix, gst_audio_mix_matrix,
152     GST_TYPE_BASE_TRANSFORM);
153 GST_ELEMENT_REGISTER_DEFINE (audiomixmatrix, "audiomixmatrix", GST_RANK_NONE,
154     GST_TYPE_AUDIO_MIX_MATRIX);
155 
156 static void
gst_audio_mix_matrix_class_init(GstAudioMixMatrixClass * klass)157 gst_audio_mix_matrix_class_init (GstAudioMixMatrixClass * klass)
158 {
159   GObjectClass *gobject_class = (GObjectClass *) klass;
160   GstElementClass *element_class = (GstElementClass *) klass;
161   GstBaseTransformClass *trans_class = (GstBaseTransformClass *) klass;
162 
163   GST_DEBUG_CATEGORY_INIT (audiomixmatrix_debug, "audiomixmatrix", 0,
164       "audiomixmatrix");
165   gst_element_class_set_static_metadata (element_class, "Matrix audio mix",
166       "Filter/Audio",
167       "Mixes a number of input channels into a number of output channels according to a transformation matrix",
168       "Vivia Nikolaidou <vivia@toolsonair.com>");
169 
170   gobject_class->set_property = gst_audio_mix_matrix_set_property;
171   gobject_class->get_property = gst_audio_mix_matrix_get_property;
172   gobject_class->dispose = gst_audio_mix_matrix_dispose;
173 
174   g_object_class_install_property (gobject_class, PROP_IN_CHANNELS,
175       g_param_spec_uint ("in-channels", "Input audio channels",
176           "How many audio channels we have on the input side",
177           0, 64, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
178   g_object_class_install_property (gobject_class, PROP_OUT_CHANNELS,
179       g_param_spec_uint ("out-channels", "Output audio channels",
180           "How many audio channels we have on the output side",
181           0, 64, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
182   g_object_class_install_property (gobject_class, PROP_MATRIX,
183       gst_param_spec_array ("matrix",
184           "Input/output channel matrix",
185           "Transformation matrix for input/output channels",
186           gst_param_spec_array ("matrix-in1", "rows", "rows",
187               g_param_spec_double ("matrix-in2", "cols", "cols",
188                   -1, 1, 0,
189                   G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS),
190               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS),
191           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
192   g_object_class_install_property (gobject_class, PROP_CHANNEL_MASK,
193       g_param_spec_uint64 ("channel-mask",
194           "Output channel mask",
195           "Output channel mask (-1 means \"default for these channels\")",
196           0, G_MAXUINT64, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
197   g_object_class_install_property (gobject_class, PROP_MODE,
198       g_param_spec_enum ("mode",
199           "Channel/matrix mode",
200           "Whether to auto-negotiate input/output channels and matrix",
201           GST_TYPE_AUDIO_MIX_MATRIX_MODE,
202           GST_AUDIO_MIX_MATRIX_MODE_MANUAL,
203           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
204 
205   gst_element_class_add_pad_template (element_class,
206       gst_static_pad_template_get (&gst_audio_mix_matrix_sink_template));
207   gst_element_class_add_pad_template (element_class,
208       gst_static_pad_template_get (&gst_audio_mix_matrix_src_template));
209 
210   trans_class->get_unit_size =
211       GST_DEBUG_FUNCPTR (gst_audio_mix_matrix_get_unit_size);
212   trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_audio_mix_matrix_set_caps);
213   trans_class->transform = GST_DEBUG_FUNCPTR (gst_audio_mix_matrix_transform);
214   trans_class->transform_caps =
215       GST_DEBUG_FUNCPTR (gst_audio_mix_matrix_transform_caps);
216   trans_class->fixate_caps =
217       GST_DEBUG_FUNCPTR (gst_audio_mix_matrix_fixate_caps);
218 
219   element_class->change_state =
220       GST_DEBUG_FUNCPTR (gst_audio_mix_matrix_change_state);
221 
222   gst_type_mark_as_plugin_api (GST_TYPE_AUDIO_MIX_MATRIX_MODE, 0);
223 }
224 
225 static void
gst_audio_mix_matrix_init(GstAudioMixMatrix * self)226 gst_audio_mix_matrix_init (GstAudioMixMatrix * self)
227 {
228   self->in_channels = 0;
229   self->out_channels = 0;
230   self->matrix = NULL;
231   self->channel_mask = 0;
232   self->s16_conv_matrix = NULL;
233   self->s32_conv_matrix = NULL;
234   self->mode = GST_AUDIO_MIX_MATRIX_MODE_MANUAL;
235 }
236 
237 static void
gst_audio_mix_matrix_dispose(GObject * object)238 gst_audio_mix_matrix_dispose (GObject * object)
239 {
240   GstAudioMixMatrix *self = GST_AUDIO_MIX_MATRIX (object);
241 
242   if (self->matrix) {
243     g_free (self->matrix);
244     self->matrix = NULL;
245   }
246 
247   G_OBJECT_CLASS (gst_audio_mix_matrix_parent_class)->dispose (object);
248 }
249 
250 static void
gst_audio_mix_matrix_convert_s16_matrix(GstAudioMixMatrix * self)251 gst_audio_mix_matrix_convert_s16_matrix (GstAudioMixMatrix * self)
252 {
253   gint i;
254 
255   /* converted bits - input bits - sign - bits needed for channel */
256   self->shift_bytes = 32 - 16 - 1 - ceil (log (self->in_channels) / log (2));
257 
258   if (self->s16_conv_matrix)
259     g_free (self->s16_conv_matrix);
260   self->s16_conv_matrix =
261       g_new (gint32, self->in_channels * self->out_channels);
262   for (i = 0; i < self->in_channels * self->out_channels; i++) {
263     self->s16_conv_matrix[i] =
264         (gint32) ((self->matrix[i]) * (1 << self->shift_bytes));
265   }
266 }
267 
268 static void
gst_audio_mix_matrix_convert_s32_matrix(GstAudioMixMatrix * self)269 gst_audio_mix_matrix_convert_s32_matrix (GstAudioMixMatrix * self)
270 {
271   gint i;
272 
273   /* converted bits - input bits - sign - bits needed for channel */
274   self->shift_bytes = 64 - 32 - 1 - (gint) (log (self->in_channels) / log (2));
275 
276   if (self->s32_conv_matrix)
277     g_free (self->s32_conv_matrix);
278   self->s32_conv_matrix =
279       g_new (gint64, self->in_channels * self->out_channels);
280   for (i = 0; i < self->in_channels * self->out_channels; i++) {
281     self->s32_conv_matrix[i] =
282         (gint64) ((self->matrix[i]) * (1 << self->shift_bytes));
283   }
284 }
285 
286 
287 static void
gst_audio_mix_matrix_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)288 gst_audio_mix_matrix_set_property (GObject * object, guint prop_id,
289     const GValue * value, GParamSpec * pspec)
290 {
291   GstAudioMixMatrix *self = GST_AUDIO_MIX_MATRIX (object);
292 
293   switch (prop_id) {
294     case PROP_IN_CHANNELS:
295       self->in_channels = g_value_get_uint (value);
296       if (self->matrix) {
297         gst_audio_mix_matrix_convert_s16_matrix (self);
298         gst_audio_mix_matrix_convert_s32_matrix (self);
299       }
300       break;
301     case PROP_OUT_CHANNELS:
302       self->out_channels = g_value_get_uint (value);
303       if (self->matrix) {
304         gst_audio_mix_matrix_convert_s16_matrix (self);
305         gst_audio_mix_matrix_convert_s32_matrix (self);
306       }
307       break;
308     case PROP_MATRIX:{
309       gint in, out;
310 
311       if (self->matrix)
312         g_free (self->matrix);
313       self->matrix = g_new (gdouble, self->in_channels * self->out_channels);
314 
315       g_return_if_fail (gst_value_array_get_size (value) == self->out_channels);
316       for (out = 0; out < self->out_channels; out++) {
317         const GValue *row = gst_value_array_get_value (value, out);
318         g_return_if_fail (gst_value_array_get_size (row) == self->in_channels);
319         for (in = 0; in < self->in_channels; in++) {
320           const GValue *itm;
321           gdouble coefficient;
322 
323           itm = gst_value_array_get_value (row, in);
324           g_return_if_fail (G_VALUE_HOLDS_DOUBLE (itm));
325           coefficient = g_value_get_double (itm);
326           self->matrix[out * self->in_channels + in] = coefficient;
327         }
328       }
329       gst_audio_mix_matrix_convert_s16_matrix (self);
330       gst_audio_mix_matrix_convert_s32_matrix (self);
331       break;
332     }
333     case PROP_CHANNEL_MASK:
334       self->channel_mask = g_value_get_uint64 (value);
335       break;
336     case PROP_MODE:
337       self->mode = g_value_get_enum (value);
338       break;
339     default:
340       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
341       break;
342   }
343 }
344 
345 static void
gst_audio_mix_matrix_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)346 gst_audio_mix_matrix_get_property (GObject * object, guint prop_id,
347     GValue * value, GParamSpec * pspec)
348 {
349   GstAudioMixMatrix *self = GST_AUDIO_MIX_MATRIX (object);
350 
351   switch (prop_id) {
352     case PROP_IN_CHANNELS:
353       g_value_set_uint (value, self->in_channels);
354       break;
355     case PROP_OUT_CHANNELS:
356       g_value_set_uint (value, self->out_channels);
357       break;
358     case PROP_MATRIX:{
359       gint in, out;
360 
361       if (self->matrix == NULL)
362         break;
363 
364       for (out = 0; out < self->out_channels; out++) {
365         GValue row = G_VALUE_INIT;
366         g_value_init (&row, GST_TYPE_ARRAY);
367         for (in = 0; in < self->in_channels; in++) {
368           GValue itm = G_VALUE_INIT;
369           g_value_init (&itm, G_TYPE_DOUBLE);
370           g_value_set_double (&itm, self->matrix[out * self->in_channels + in]);
371           gst_value_array_append_value (&row, &itm);
372           g_value_unset (&itm);
373         }
374         gst_value_array_append_value (value, &row);
375         g_value_unset (&row);
376       }
377       break;
378     }
379     case PROP_CHANNEL_MASK:
380       g_value_set_uint64 (value, self->channel_mask);
381       break;
382     case PROP_MODE:
383       g_value_set_enum (value, self->mode);
384       break;
385     default:
386       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
387       break;
388   }
389 }
390 
391 static GstStateChangeReturn
gst_audio_mix_matrix_change_state(GstElement * element,GstStateChange transition)392 gst_audio_mix_matrix_change_state (GstElement * element,
393     GstStateChange transition)
394 {
395   GstAudioMixMatrix *self = GST_AUDIO_MIX_MATRIX (element);
396   GstStateChangeReturn s;
397 
398   s = GST_ELEMENT_CLASS (gst_audio_mix_matrix_parent_class)->change_state
399       (element, transition);
400 
401   if (transition == GST_STATE_CHANGE_PAUSED_TO_READY) {
402     if (self->s16_conv_matrix) {
403       g_free (self->s16_conv_matrix);
404       self->s16_conv_matrix = NULL;
405     }
406 
407     if (self->s32_conv_matrix) {
408       g_free (self->s32_conv_matrix);
409       self->s32_conv_matrix = NULL;
410     }
411   }
412 
413   return s;
414 }
415 
416 
417 static GstFlowReturn
gst_audio_mix_matrix_transform(GstBaseTransform * vfilter,GstBuffer * inbuf,GstBuffer * outbuf)418 gst_audio_mix_matrix_transform (GstBaseTransform * vfilter,
419     GstBuffer * inbuf, GstBuffer * outbuf)
420 {
421   GstMapInfo inmap, outmap;
422   GstAudioMixMatrix *self = GST_AUDIO_MIX_MATRIX (vfilter);
423   gint in, out, sample;
424   guint inchannels = self->in_channels;
425   guint outchannels = self->out_channels;
426   gdouble *matrix = self->matrix;
427 
428   if (!gst_buffer_map (inbuf, &inmap, GST_MAP_READ)) {
429     return GST_FLOW_ERROR;
430   }
431   if (!gst_buffer_map (outbuf, &outmap, GST_MAP_WRITE)) {
432     gst_buffer_unmap (inbuf, &inmap);
433     return GST_FLOW_ERROR;
434   }
435 
436   switch (self->format) {
437     case GST_AUDIO_FORMAT_F32LE:
438     case GST_AUDIO_FORMAT_F32BE:{
439       const gfloat *inarray;
440       gfloat *outarray;
441       guint n_samples = outmap.size / (sizeof (gfloat) * outchannels);
442 
443       inarray = (gfloat *) inmap.data;
444       outarray = (gfloat *) outmap.data;
445 
446       for (sample = 0; sample < n_samples; sample++) {
447         for (out = 0; out < outchannels; out++) {
448           gfloat outval = 0;
449           for (in = 0; in < inchannels; in++) {
450             outval +=
451                 inarray[sample * inchannels +
452                 in] * matrix[out * inchannels + in];
453           }
454           outarray[sample * outchannels + out] = outval;
455         }
456       }
457       break;
458     }
459     case GST_AUDIO_FORMAT_F64LE:
460     case GST_AUDIO_FORMAT_F64BE:{
461       const gdouble *inarray;
462       gdouble *outarray;
463       guint n_samples = outmap.size / (sizeof (gdouble) * outchannels);
464 
465       inarray = (gdouble *) inmap.data;
466       outarray = (gdouble *) outmap.data;
467 
468       for (sample = 0; sample < n_samples; sample++) {
469         for (out = 0; out < outchannels; out++) {
470           gdouble outval = 0;
471           for (in = 0; in < inchannels; in++) {
472             outval +=
473                 inarray[sample * inchannels +
474                 in] * matrix[out * inchannels + in];
475           }
476           outarray[sample * outchannels + out] = outval;
477         }
478       }
479       break;
480     }
481     case GST_AUDIO_FORMAT_S16LE:
482     case GST_AUDIO_FORMAT_S16BE:{
483       const gint16 *inarray;
484       gint16 *outarray;
485       guint n_samples = outmap.size / (sizeof (gint16) * outchannels);
486       guint n = self->shift_bytes;
487       gint32 *conv_matrix = self->s16_conv_matrix;
488 
489       inarray = (gint16 *) inmap.data;
490       outarray = (gint16 *) outmap.data;
491 
492       for (sample = 0; sample < n_samples; sample++) {
493         for (out = 0; out < outchannels; out++) {
494           gint32 outval = 0;
495           for (in = 0; in < inchannels; in++) {
496             outval += (gint32) (inarray[sample * inchannels + in] *
497                 conv_matrix[out * inchannels + in]);
498           }
499           outarray[sample * outchannels + out] = (gint16) (outval >> n);
500         }
501       }
502       break;
503     }
504     case GST_AUDIO_FORMAT_S32LE:
505     case GST_AUDIO_FORMAT_S32BE:{
506       const gint32 *inarray;
507       gint32 *outarray;
508       guint n_samples = outmap.size / (sizeof (gint32) * outchannels);
509       guint n = self->shift_bytes;
510       gint64 *conv_matrix = self->s32_conv_matrix;
511 
512       inarray = (gint32 *) inmap.data;
513       outarray = (gint32 *) outmap.data;
514 
515       for (sample = 0; sample < n_samples; sample++) {
516         for (out = 0; out < outchannels; out++) {
517           gint64 outval = 0;
518           for (in = 0; in < inchannels; in++) {
519             outval += (gint64) (inarray[sample * inchannels + in] *
520                 conv_matrix[out * inchannels + in]);
521           }
522           outarray[sample * outchannels + out] = (gint32) (outval >> n);
523         }
524       }
525       break;
526     }
527     default:
528       gst_buffer_unmap (inbuf, &inmap);
529       gst_buffer_unmap (outbuf, &outmap);
530       return GST_FLOW_NOT_SUPPORTED;
531 
532   }
533 
534   gst_buffer_unmap (inbuf, &inmap);
535   gst_buffer_unmap (outbuf, &outmap);
536   return GST_FLOW_OK;
537 }
538 
539 static gboolean
gst_audio_mix_matrix_get_unit_size(GstBaseTransform * trans,GstCaps * caps,gsize * size)540 gst_audio_mix_matrix_get_unit_size (GstBaseTransform * trans,
541     GstCaps * caps, gsize * size)
542 {
543   GstAudioInfo info;
544 
545   if (!gst_audio_info_from_caps (&info, caps))
546     return FALSE;
547 
548   *size = GST_AUDIO_INFO_BPF (&info);
549 
550   return TRUE;
551 }
552 
553 static gboolean
gst_audio_mix_matrix_set_caps(GstBaseTransform * trans,GstCaps * incaps,GstCaps * outcaps)554 gst_audio_mix_matrix_set_caps (GstBaseTransform * trans, GstCaps * incaps,
555     GstCaps * outcaps)
556 {
557   GstAudioMixMatrix *self = GST_AUDIO_MIX_MATRIX (trans);
558   GstAudioInfo info, out_info;
559 
560   if (!gst_audio_info_from_caps (&info, incaps))
561     return FALSE;
562 
563   if (!gst_audio_info_from_caps (&out_info, outcaps))
564     return FALSE;
565 
566   self->format = info.finfo->format;
567 
568   if (self->mode == GST_AUDIO_MIX_MATRIX_MODE_FIRST_CHANNELS) {
569     gint in, out;
570 
571     self->in_channels = info.channels;
572     self->out_channels = out_info.channels;
573 
574     self->matrix = g_new (gdouble, self->in_channels * self->out_channels);
575 
576     for (out = 0; out < self->out_channels; out++) {
577       for (in = 0; in < self->in_channels; in++) {
578         self->matrix[out * self->in_channels + in] = (out == in);
579       }
580     }
581   } else if (!self->matrix || info.channels != self->in_channels ||
582       out_info.channels != self->out_channels) {
583     GST_ELEMENT_ERROR (self, LIBRARY, SETTINGS,
584         ("Erroneous matrix detected"),
585         ("Please enter a matrix with the correct input and output channels"));
586     return FALSE;
587   }
588 
589   switch (self->format) {
590     case GST_AUDIO_FORMAT_S16LE:
591     case GST_AUDIO_FORMAT_S16BE:{
592       gst_audio_mix_matrix_convert_s16_matrix (self);
593       break;
594     }
595     case GST_AUDIO_FORMAT_S32LE:
596     case GST_AUDIO_FORMAT_S32BE:{
597       gst_audio_mix_matrix_convert_s32_matrix (self);
598       break;
599     }
600     default:
601       break;
602   }
603   return TRUE;
604 }
605 
606 static GstCaps *
gst_audio_mix_matrix_fixate_caps(GstBaseTransform * trans,GstPadDirection direction,GstCaps * caps,GstCaps * othercaps)607 gst_audio_mix_matrix_fixate_caps (GstBaseTransform * trans,
608     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
609 {
610   GstAudioMixMatrix *self = GST_AUDIO_MIX_MATRIX (trans);
611   GstStructure *s, *s2;
612   guint capssize = gst_caps_get_size (othercaps);
613   gint i;
614   gint channels;
615 
616   if (self->mode == GST_AUDIO_MIX_MATRIX_MODE_FIRST_CHANNELS) {
617     s2 = gst_caps_get_structure (caps, 0);
618 
619     /* Try to keep channel configuration as much as possible */
620     if (gst_structure_get_int (s2, "channels", &channels)) {
621       gint mindiff = -1;
622       othercaps = gst_caps_make_writable (othercaps);
623       for (i = 0; i < capssize; i++) {
624         s = gst_caps_get_structure (othercaps, i);
625         if (!gst_structure_has_field (s, "channels")) {
626           mindiff = 0;
627           gst_structure_set (s, "channels", G_TYPE_INT, channels, NULL);
628         } else {
629           gint outchannels;
630           gint diff;
631 
632           gst_structure_fixate_field_nearest_int (s, "channels", channels);
633           if (gst_structure_get_int (s, "channels", &outchannels)) {
634             diff = ABS (channels - outchannels);
635             if (mindiff < 0 || diff < mindiff)
636               mindiff = diff;
637           }
638         }
639       }
640 
641       if (mindiff >= 0) {
642         for (i = 0; i < capssize; i++) {
643           gint outchannels, diff;
644           s = gst_caps_get_structure (othercaps, i);
645           if (gst_structure_get_int (s, "channels", &outchannels)) {
646             diff = ABS (channels - outchannels);
647             if (diff > mindiff) {
648               gst_caps_remove_structure (othercaps, i--);
649               capssize--;
650             }
651           }
652         }
653       }
654     }
655   }
656 
657   if (gst_caps_is_empty (othercaps))
658     return othercaps;
659 
660   othercaps =
661       GST_BASE_TRANSFORM_CLASS (gst_audio_mix_matrix_parent_class)->fixate_caps
662       (trans, direction, caps, othercaps);
663 
664   s = gst_caps_get_structure (othercaps, 0);
665 
666   if (!gst_structure_has_field (s, "channel-mask")) {
667     if (self->mode == GST_AUDIO_MIX_MATRIX_MODE_FIRST_CHANNELS ||
668         self->channel_mask == -1) {
669       gint channels;
670 
671       g_assert (gst_structure_get_int (s, "channels", &channels));
672       gst_structure_set (s, "channel-mask", GST_TYPE_BITMASK,
673           gst_audio_channel_get_fallback_mask (channels), NULL);
674     } else {
675       gst_structure_set (s, "channel-mask", GST_TYPE_BITMASK,
676           self->channel_mask, NULL);
677     }
678   }
679 
680   return othercaps;
681 
682 }
683 
684 static GstCaps *
gst_audio_mix_matrix_transform_caps(GstBaseTransform * trans,GstPadDirection direction,GstCaps * caps,GstCaps * filter)685 gst_audio_mix_matrix_transform_caps (GstBaseTransform * trans,
686     GstPadDirection direction, GstCaps * caps, GstCaps * filter)
687 {
688   GstAudioMixMatrix *self = GST_AUDIO_MIX_MATRIX (trans);
689   GstCaps *outcaps = gst_caps_copy (caps);
690   GstCaps *ret;
691   GstStructure *s;
692   gint i;
693   guint capssize = gst_caps_get_size (outcaps);
694 
695   if (self->mode == GST_AUDIO_MIX_MATRIX_MODE_FIRST_CHANNELS) {
696     for (i = 0; i < capssize; i++) {
697       s = gst_caps_get_structure (outcaps, i);
698       if (gst_structure_has_field (s, "channels")) {
699         gst_structure_remove_field (s, "channels");
700       }
701       if (gst_structure_has_field (s, "channel-mask")) {
702         gst_structure_remove_field (s, "channel-mask");
703       }
704     }
705     goto beach;
706   }
707 
708   if (self->in_channels == 0 || self->out_channels == 0 || self->matrix == NULL) {
709     /* Not dispatching element error because we return empty caps anyway and
710      * we should let it fail to link. Additionally, the element error would be
711      * printed as WARN, so a possible gst-launch pipeline would appear to
712      * hang. */
713     GST_ERROR_OBJECT (self, "Invalid settings detected in manual mode. "
714         "Please specify in-channels, out-channels and matrix.");
715     return gst_caps_new_empty ();
716   }
717 
718   if (self->in_channels == self->out_channels) {
719     goto beach;
720   }
721 
722   for (i = 0; i < capssize; i++) {
723     s = gst_caps_get_structure (outcaps, i);
724     if (direction == GST_PAD_SRC) {
725       gst_structure_set (s, "channels", G_TYPE_INT, self->in_channels, NULL);
726       gst_structure_remove_field (s, "channel-mask");
727     } else if (direction == GST_PAD_SINK) {
728       gst_structure_set (s, "channels", G_TYPE_INT, self->out_channels,
729           "channel-mask", GST_TYPE_BITMASK, self->channel_mask, NULL);
730     } else {
731       g_assert_not_reached ();
732     }
733   }
734 
735 beach:
736   if (filter) {
737     ret = gst_caps_intersect_full (filter, outcaps, GST_CAPS_INTERSECT_FIRST);
738     gst_caps_unref (outcaps);
739   } else {
740     ret = outcaps;
741   }
742   return ret;
743 }
744 
745 static gboolean
plugin_init(GstPlugin * plugin)746 plugin_init (GstPlugin * plugin)
747 {
748   return GST_ELEMENT_REGISTER (audiomixmatrix, plugin);
749 }
750 
751 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
752     GST_VERSION_MINOR,
753     audiomixmatrix,
754     "Audio matrix mix",
755     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
756