• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2000 Wim Taymans <wtay@chello.be>
4  *                    2005 Wim Taymans <wim@fluendo.com>
5  *                    2007 Andy Wingo <wingo at pobox.com>
6  *                    2008 Sebastian Dröge <slomo@circular-chaos.org>
7  *                    2014 Collabora
8  *                        Olivier Crete <olivier.crete@collabora.com>
9  *
10  * gstaudiointerleave.c: audiointerleave element, N in, one out,
11  * samples are added
12  *
13  * This library is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU Library General Public
15  * License as published by the Free Software Foundation; either
16  * version 2 of the License, or (at your option) any later version.
17  *
18  * This library is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21  * Library General Public License for more details.
22  *
23  * You should have received a copy of the GNU Library General Public
24  * License along with this library; if not, write to the
25  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
26  * Boston, MA 02110-1301, USA.
27  */
28 /**
29  * SECTION:element-audiointerleave
30  * @title: audiointerleave
31  *
32  */
33 
34 /* FIXME 0.11: suppress warnings for deprecated API such as GValueArray
35  * with newer GLib versions (>= 2.31.0) */
36 #define GLIB_DISABLE_DEPRECATION_WARNINGS
37 
38 #ifdef HAVE_CONFIG_H
39 #include "config.h"
40 #endif
41 
42 #include "gstaudiomixerelements.h"
43 #include "gstaudiointerleave.h"
44 
45 #define GST_CAT_DEFAULT gst_audio_interleave_debug
46 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
47 
48 enum
49 {
50   PROP_PAD_0,
51   PROP_PAD_CHANNEL
52 };
53 
54 G_DEFINE_TYPE (GstAudioInterleavePad, gst_audio_interleave_pad,
55     GST_TYPE_AUDIO_AGGREGATOR_PAD);
56 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (audiointerleave, "audiointerleave",
57     GST_RANK_NONE, GST_TYPE_AUDIO_INTERLEAVE, audiomixer_element_init (plugin));
58 
59 static void
gst_audio_interleave_pad_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)60 gst_audio_interleave_pad_get_property (GObject * object, guint prop_id,
61     GValue * value, GParamSpec * pspec)
62 {
63   GstAudioInterleavePad *pad = GST_AUDIO_INTERLEAVE_PAD (object);
64 
65   switch (prop_id) {
66     case PROP_PAD_CHANNEL:
67       g_value_set_uint (value, pad->channel);
68       break;
69     default:
70       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
71       break;
72   }
73 }
74 
75 
76 static void
gst_audio_interleave_pad_class_init(GstAudioInterleavePadClass * klass)77 gst_audio_interleave_pad_class_init (GstAudioInterleavePadClass * klass)
78 {
79   GObjectClass *gobject_class = (GObjectClass *) klass;
80 
81   gobject_class->get_property = gst_audio_interleave_pad_get_property;
82 
83   g_object_class_install_property (gobject_class,
84       PROP_PAD_CHANNEL,
85       g_param_spec_uint ("channel",
86           "Channel number",
87           "Number of the channel of this pad in the output", 0, G_MAXUINT, 0,
88           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
89 }
90 
91 static void
gst_audio_interleave_pad_init(GstAudioInterleavePad * pad)92 gst_audio_interleave_pad_init (GstAudioInterleavePad * pad)
93 {
94 }
95 
96 enum
97 {
98   PROP_0,
99   PROP_CHANNEL_POSITIONS,
100   PROP_CHANNEL_POSITIONS_FROM_INPUT
101 };
102 
103 /* elementfactory information */
104 
105 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
106 #define CAPS \
107   GST_AUDIO_CAPS_MAKE ("{ S32LE, U32LE, S16LE, U16LE, S8, U8, F32LE, F64LE }") \
108   ", layout = (string) { interleaved, non-interleaved }"
109 #else
110 #define CAPS \
111   GST_AUDIO_CAPS_MAKE ("{ S32BE, U32BE, S16BE, U16BE, S8, U8, F32BE, F64BE }") \
112   ", layout = (string) { interleaved, non-interleaved }"
113 #endif
114 
115 static GstStaticPadTemplate gst_audio_interleave_sink_template =
116 GST_STATIC_PAD_TEMPLATE ("sink_%u",
117     GST_PAD_SINK,
118     GST_PAD_REQUEST,
119     GST_STATIC_CAPS ("audio/x-raw, "
120         "rate = (int) [ 1, MAX ], "
121         "channels = (int) 1, "
122         "format = (string) " GST_AUDIO_FORMATS_ALL ", "
123         "layout = (string) {non-interleaved, interleaved}")
124     );
125 
126 static GstStaticPadTemplate gst_audio_interleave_src_template =
127 GST_STATIC_PAD_TEMPLATE ("src",
128     GST_PAD_SRC,
129     GST_PAD_ALWAYS,
130     GST_STATIC_CAPS ("audio/x-raw, "
131         "rate = (int) [ 1, MAX ], "
132         "channels = (int) [ 1, MAX ], "
133         "format = (string) " GST_AUDIO_FORMATS_ALL ", "
134         "layout = (string) interleaved")
135     );
136 
137 static void gst_audio_interleave_child_proxy_init (gpointer g_iface,
138     gpointer iface_data);
139 
140 #define gst_audio_interleave_parent_class parent_class
141 G_DEFINE_TYPE_WITH_CODE (GstAudioInterleave, gst_audio_interleave,
142     GST_TYPE_AUDIO_AGGREGATOR, G_IMPLEMENT_INTERFACE (GST_TYPE_CHILD_PROXY,
143         gst_audio_interleave_child_proxy_init));
144 
145 static void gst_audio_interleave_finalize (GObject * object);
146 static void gst_audio_interleave_set_property (GObject * object, guint prop_id,
147     const GValue * value, GParamSpec * pspec);
148 static void gst_audio_interleave_get_property (GObject * object, guint prop_id,
149     GValue * value, GParamSpec * pspec);
150 
151 static gboolean gst_audio_interleave_setcaps (GstAudioInterleave * self,
152     GstPad * pad, GstCaps * caps);
153 static GstPad *gst_audio_interleave_request_new_pad (GstElement * element,
154     GstPadTemplate * temp, const gchar * req_name, const GstCaps * caps);
155 static void gst_audio_interleave_release_pad (GstElement * element,
156     GstPad * pad);
157 
158 static gboolean gst_audio_interleave_stop (GstAggregator * agg);
159 
160 static gboolean
161 gst_audio_interleave_aggregate_one_buffer (GstAudioAggregator * aagg,
162     GstAudioAggregatorPad * aaggpad, GstBuffer * inbuf, guint in_offset,
163     GstBuffer * outbuf, guint out_offset, guint num_samples);
164 
165 
166 static void
__remove_channels(GstCaps * caps)167 __remove_channels (GstCaps * caps)
168 {
169   GstStructure *s;
170   gint i, size;
171 
172   size = gst_caps_get_size (caps);
173   for (i = 0; i < size; i++) {
174     s = gst_caps_get_structure (caps, i);
175     gst_structure_remove_field (s, "channel-mask");
176     gst_structure_remove_field (s, "channels");
177   }
178 }
179 
180 static void
__set_channels(GstCaps * caps,gint channels)181 __set_channels (GstCaps * caps, gint channels)
182 {
183   GstStructure *s;
184   gint i, size;
185 
186   size = gst_caps_get_size (caps);
187   for (i = 0; i < size; i++) {
188     s = gst_caps_get_structure (caps, i);
189     if (channels > 0)
190       gst_structure_set (s, "channels", G_TYPE_INT, channels, NULL);
191     else
192       gst_structure_set (s, "channels", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL);
193   }
194 }
195 
196 /* we can only accept caps that we and downstream can handle.
197  * if we have filtercaps set, use those to constrain the target caps.
198  */
199 static GstCaps *
gst_audio_interleave_sink_getcaps(GstAggregator * agg,GstPad * pad,GstCaps * filter)200 gst_audio_interleave_sink_getcaps (GstAggregator * agg, GstPad * pad,
201     GstCaps * filter)
202 {
203   GstAudioInterleave *self = GST_AUDIO_INTERLEAVE (agg);
204   GstCaps *result = NULL, *peercaps, *sinkcaps;
205 
206   GST_OBJECT_LOCK (self);
207   /* If we already have caps on one of the sink pads return them */
208   if (self->sinkcaps)
209     result = gst_caps_copy (self->sinkcaps);
210   GST_OBJECT_UNLOCK (self);
211 
212   if (result == NULL) {
213     /* get the downstream possible caps */
214     peercaps = gst_pad_peer_query_caps (agg->srcpad, NULL);
215 
216     /* get the allowed caps on this sinkpad */
217     sinkcaps = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
218     __remove_channels (sinkcaps);
219     if (peercaps) {
220       peercaps = gst_caps_make_writable (peercaps);
221       __remove_channels (peercaps);
222       /* if the peer has caps, intersect */
223       GST_DEBUG_OBJECT (pad, "intersecting peer and template caps");
224       result = gst_caps_intersect (peercaps, sinkcaps);
225       gst_caps_unref (peercaps);
226       gst_caps_unref (sinkcaps);
227     } else {
228       /* the peer has no caps (or there is no peer), just use the allowed caps
229        * of this sinkpad. */
230       GST_DEBUG_OBJECT (pad, "no peer caps, using sinkcaps");
231       result = sinkcaps;
232     }
233     __set_channels (result, 1);
234   }
235 
236   if (filter != NULL) {
237     GstCaps *caps = result;
238 
239     GST_LOG_OBJECT (pad, "intersecting filter caps %" GST_PTR_FORMAT " with "
240         "preliminary result %" GST_PTR_FORMAT, filter, caps);
241 
242     result = gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
243     gst_caps_unref (caps);
244   }
245 
246   GST_DEBUG_OBJECT (pad, "Returning caps %" GST_PTR_FORMAT, result);
247 
248   return result;
249 }
250 
251 static gboolean
gst_audio_interleave_sink_query(GstAggregator * agg,GstAggregatorPad * aggpad,GstQuery * query)252 gst_audio_interleave_sink_query (GstAggregator * agg, GstAggregatorPad * aggpad,
253     GstQuery * query)
254 {
255   gboolean res = FALSE;
256 
257   switch (GST_QUERY_TYPE (query)) {
258     case GST_QUERY_CAPS:
259     {
260       GstCaps *filter, *caps;
261 
262       gst_query_parse_caps (query, &filter);
263       caps = gst_audio_interleave_sink_getcaps (agg, GST_PAD (aggpad), filter);
264       gst_query_set_caps_result (query, caps);
265       gst_caps_unref (caps);
266       res = TRUE;
267       break;
268     }
269     default:
270       res =
271           GST_AGGREGATOR_CLASS (parent_class)->sink_query (agg, aggpad, query);
272       break;
273   }
274 
275   return res;
276 }
277 
278 static gint
compare_positions(gconstpointer a,gconstpointer b,gpointer user_data)279 compare_positions (gconstpointer a, gconstpointer b, gpointer user_data)
280 {
281   const gint i = *(const gint *) a;
282   const gint j = *(const gint *) b;
283   const gint *pos = (const gint *) user_data;
284 
285   if (pos[i] < pos[j])
286     return -1;
287   else if (pos[i] > pos[j])
288     return 1;
289   else
290     return 0;
291 }
292 
293 static gboolean
gst_audio_interleave_channel_positions_to_mask(GValueArray * positions,gint default_ordering_map[64],guint64 * mask)294 gst_audio_interleave_channel_positions_to_mask (GValueArray * positions,
295     gint default_ordering_map[64], guint64 * mask)
296 {
297   gint i;
298   guint channels;
299   GstAudioChannelPosition *pos;
300   gboolean ret;
301 
302   channels = positions->n_values;
303   pos = g_new (GstAudioChannelPosition, channels);
304 
305   for (i = 0; i < channels; i++) {
306     GValue *val;
307 
308     val = g_value_array_get_nth (positions, i);
309     pos[i] = g_value_get_enum (val);
310   }
311 
312   /* sort the default ordering map according to the position order */
313   for (i = 0; i < channels; i++) {
314     default_ordering_map[i] = i;
315   }
316   g_qsort_with_data (default_ordering_map, channels,
317       sizeof (*default_ordering_map), compare_positions, pos);
318 
319   ret = gst_audio_channel_positions_to_mask (pos, channels, FALSE, mask);
320   g_free (pos);
321 
322   return ret;
323 }
324 
325 
326 /* Must be called with the object lock held */
327 
328 static guint64
gst_audio_interleave_get_channel_mask(GstAudioInterleave * self)329 gst_audio_interleave_get_channel_mask (GstAudioInterleave * self)
330 {
331   guint64 channel_mask = 0;
332 
333   if (self->channels <= 64 &&
334       self->channel_positions != NULL &&
335       self->channels == self->channel_positions->n_values) {
336     if (!gst_audio_interleave_channel_positions_to_mask
337         (self->channel_positions, self->default_channels_ordering_map,
338             &channel_mask)) {
339       GST_WARNING_OBJECT (self, "Invalid channel positions, using NONE");
340       channel_mask = 0;
341     }
342   } else if (self->channels <= 64) {
343     GST_WARNING_OBJECT (self, "Using NONE channel positions");
344   }
345 
346   return channel_mask;
347 }
348 
349 
350 #define MAKE_FUNC(type) \
351 static void interleave_##type (guint##type *out, guint##type *in, \
352     guint stride, guint nframes) \
353 { \
354   gint i; \
355   \
356   for (i = 0; i < nframes; i++) { \
357     *out = in[i]; \
358     out += stride; \
359   } \
360 }
361 
362 MAKE_FUNC (8);
363 MAKE_FUNC (16);
364 MAKE_FUNC (32);
365 MAKE_FUNC (64);
366 
367 static void
interleave_24(guint8 * out,guint8 * in,guint stride,guint nframes)368 interleave_24 (guint8 * out, guint8 * in, guint stride, guint nframes)
369 {
370   gint i;
371 
372   for (i = 0; i < nframes; i++) {
373     memcpy (out, in, 3);
374     out += stride * 3;
375     in += 3;
376   }
377 }
378 
379 static void
gst_audio_interleave_set_process_function(GstAudioInterleave * self,GstAudioInfo * info)380 gst_audio_interleave_set_process_function (GstAudioInterleave * self,
381     GstAudioInfo * info)
382 {
383   switch (GST_AUDIO_INFO_WIDTH (info)) {
384     case 8:
385       self->func = (GstInterleaveFunc) interleave_8;
386       break;
387     case 16:
388       self->func = (GstInterleaveFunc) interleave_16;
389       break;
390     case 24:
391       self->func = (GstInterleaveFunc) interleave_24;
392       break;
393     case 32:
394       self->func = (GstInterleaveFunc) interleave_32;
395       break;
396     case 64:
397       self->func = (GstInterleaveFunc) interleave_64;
398       break;
399     default:
400       g_assert_not_reached ();
401       break;
402   }
403 }
404 
405 
406 /* the first caps we receive on any of the sinkpads will define the caps for all
407  * the other sinkpads because we can only mix streams with the same caps.
408  */
409 static gboolean
gst_audio_interleave_setcaps(GstAudioInterleave * self,GstPad * pad,GstCaps * caps)410 gst_audio_interleave_setcaps (GstAudioInterleave * self, GstPad * pad,
411     GstCaps * caps)
412 {
413   GstAudioAggregator *aagg = GST_AUDIO_AGGREGATOR (self);
414   GstAudioInfo info;
415   GValue *val;
416   guint channel;
417   gboolean new = FALSE;
418 
419   if (!gst_audio_info_from_caps (&info, caps))
420     goto invalid_format;
421 
422   GST_OBJECT_LOCK (self);
423   if (self->sinkcaps && !gst_caps_is_subset (caps, self->sinkcaps))
424     goto cannot_change_caps;
425 
426   if (!self->sinkcaps) {
427     GstCaps *sinkcaps = gst_caps_copy (caps);
428     GstStructure *s = gst_caps_get_structure (sinkcaps, 0);
429 
430     gst_structure_remove_field (s, "channel-mask");
431 
432     GST_DEBUG_OBJECT (self, "setting sinkcaps %" GST_PTR_FORMAT, sinkcaps);
433 
434     gst_caps_replace (&self->sinkcaps, sinkcaps);
435     gst_pad_mark_reconfigure (GST_AGGREGATOR_SRC_PAD (aagg));
436 
437     gst_caps_unref (sinkcaps);
438     new = TRUE;
439   }
440 
441   if (self->channel_positions_from_input
442       && GST_AUDIO_INFO_CHANNELS (&info) == 1) {
443     channel = GST_AUDIO_INTERLEAVE_PAD (pad)->channel;
444     val = g_value_array_get_nth (self->input_channel_positions, channel);
445     g_value_set_enum (val, GST_AUDIO_INFO_POSITION (&info, 0));
446   }
447   GST_OBJECT_UNLOCK (self);
448 
449   gst_audio_aggregator_set_sink_caps (aagg, GST_AUDIO_AGGREGATOR_PAD (pad),
450       caps);
451 
452   if (!new)
453     return TRUE;
454 
455   GST_INFO_OBJECT (pad, "handle caps change to %" GST_PTR_FORMAT, caps);
456 
457   return TRUE;
458 
459   /* ERRORS */
460 invalid_format:
461   {
462     GST_WARNING_OBJECT (self, "invalid format set as caps: %" GST_PTR_FORMAT,
463         caps);
464     return FALSE;
465   }
466 cannot_change_caps:
467   {
468     GST_OBJECT_UNLOCK (self);
469     GST_WARNING_OBJECT (self, "caps of %" GST_PTR_FORMAT " already set, can't "
470         "change", self->sinkcaps);
471     return FALSE;
472   }
473 }
474 
475 static gboolean
gst_audio_interleave_sink_event(GstAggregator * agg,GstAggregatorPad * aggpad,GstEvent * event)476 gst_audio_interleave_sink_event (GstAggregator * agg, GstAggregatorPad * aggpad,
477     GstEvent * event)
478 {
479   GstAudioInterleave *self = GST_AUDIO_INTERLEAVE (agg);
480   gboolean res = TRUE;
481 
482   GST_DEBUG_OBJECT (aggpad, "Got %s event on sink pad",
483       GST_EVENT_TYPE_NAME (event));
484 
485   switch (GST_EVENT_TYPE (event)) {
486     case GST_EVENT_CAPS:
487     {
488       GstCaps *caps;
489 
490       gst_event_parse_caps (event, &caps);
491       res = gst_audio_interleave_setcaps (self, GST_PAD_CAST (aggpad), caps);
492       gst_event_unref (event);
493       event = NULL;
494       break;
495     }
496     default:
497       break;
498   }
499 
500   if (event != NULL)
501     return GST_AGGREGATOR_CLASS (parent_class)->sink_event (agg, aggpad, event);
502 
503   return res;
504 }
505 
506 static GstFlowReturn
gst_audio_interleave_update_src_caps(GstAggregator * agg,GstCaps * caps,GstCaps ** ret)507 gst_audio_interleave_update_src_caps (GstAggregator * agg, GstCaps * caps,
508     GstCaps ** ret)
509 {
510   GstAudioInterleave *self = GST_AUDIO_INTERLEAVE (agg);
511   GstStructure *s;
512 
513   /* This means that either no caps have been set on the sink pad (if
514    * sinkcaps is NULL) or that there is no sink pad (if channels == 0).
515    */
516   GST_OBJECT_LOCK (self);
517   if (self->sinkcaps == NULL || self->channels == 0) {
518     GST_OBJECT_UNLOCK (self);
519     return GST_FLOW_NOT_NEGOTIATED;
520   }
521 
522   *ret = gst_caps_copy (self->sinkcaps);
523   s = gst_caps_get_structure (*ret, 0);
524 
525   gst_structure_set (s, "channels", G_TYPE_INT, self->channels, "layout",
526       G_TYPE_STRING, "interleaved", "channel-mask", GST_TYPE_BITMASK,
527       gst_audio_interleave_get_channel_mask (self), NULL);
528 
529   GST_OBJECT_UNLOCK (self);
530 
531   return GST_FLOW_OK;
532 }
533 
534 static gboolean
gst_audio_interleave_negotiated_src_caps(GstAggregator * agg,GstCaps * caps)535 gst_audio_interleave_negotiated_src_caps (GstAggregator * agg, GstCaps * caps)
536 {
537   GstAudioInterleave *self = GST_AUDIO_INTERLEAVE (agg);
538   GstAudioAggregatorPad *srcpad = GST_AUDIO_AGGREGATOR_PAD (agg->srcpad);
539 
540   if (!GST_AGGREGATOR_CLASS (parent_class)->negotiated_src_caps (agg, caps))
541     return FALSE;
542 
543   gst_audio_interleave_set_process_function (self, &srcpad->info);
544 
545   return TRUE;
546 }
547 
548 static void
gst_audio_interleave_class_init(GstAudioInterleaveClass * klass)549 gst_audio_interleave_class_init (GstAudioInterleaveClass * klass)
550 {
551   GObjectClass *gobject_class = (GObjectClass *) klass;
552   GstElementClass *gstelement_class = (GstElementClass *) klass;
553   GstAggregatorClass *agg_class = (GstAggregatorClass *) klass;
554   GstAudioAggregatorClass *aagg_class = (GstAudioAggregatorClass *) klass;
555 
556   GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "audiointerleave", 0,
557       "audio interleaving element");
558 
559   gobject_class->set_property = gst_audio_interleave_set_property;
560   gobject_class->get_property = gst_audio_interleave_get_property;
561   gobject_class->finalize = gst_audio_interleave_finalize;
562 
563   gst_element_class_add_static_pad_template_with_gtype (gstelement_class,
564       &gst_audio_interleave_src_template, GST_TYPE_AUDIO_AGGREGATOR_PAD);
565   gst_element_class_add_static_pad_template_with_gtype (gstelement_class,
566       &gst_audio_interleave_sink_template, GST_TYPE_AUDIO_INTERLEAVE_PAD);
567   gst_element_class_set_static_metadata (gstelement_class, "AudioInterleave",
568       "Generic/Audio", "Mixes multiple audio streams",
569       "Olivier Crete <olivier.crete@collabora.com>");
570 
571   gstelement_class->request_new_pad =
572       GST_DEBUG_FUNCPTR (gst_audio_interleave_request_new_pad);
573   gstelement_class->release_pad =
574       GST_DEBUG_FUNCPTR (gst_audio_interleave_release_pad);
575 
576   agg_class->sink_query = GST_DEBUG_FUNCPTR (gst_audio_interleave_sink_query);
577   agg_class->sink_event = GST_DEBUG_FUNCPTR (gst_audio_interleave_sink_event);
578   agg_class->stop = gst_audio_interleave_stop;
579   agg_class->update_src_caps = gst_audio_interleave_update_src_caps;
580   agg_class->negotiated_src_caps = gst_audio_interleave_negotiated_src_caps;
581 
582   aagg_class->aggregate_one_buffer = gst_audio_interleave_aggregate_one_buffer;
583 
584   /**
585    * GstInterleave:channel-positions
586    *
587    * Channel positions: This property controls the channel positions
588    * that are used on the src caps. The number of elements should be
589    * the same as the number of sink pads and the array should contain
590    * a valid list of channel positions. The n-th element of the array
591    * is the position of the n-th sink pad.
592    *
593    * These channel positions will only be used if they're valid and the
594    * number of elements is the same as the number of channels. If this
595    * is not given a NONE layout will be used.
596    *
597    */
598   g_object_class_install_property (gobject_class, PROP_CHANNEL_POSITIONS,
599       g_param_spec_value_array ("channel-positions", "Channel positions",
600           "Channel positions used on the output",
601           g_param_spec_enum ("channel-position", "Channel position",
602               "Channel position of the n-th input",
603               GST_TYPE_AUDIO_CHANNEL_POSITION,
604               GST_AUDIO_CHANNEL_POSITION_NONE,
605               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS),
606           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
607 
608   /**
609    * GstInterleave:channel-positions-from-input
610    *
611    * Channel positions from input: If this property is set to %TRUE the channel
612    * positions will be taken from the input caps if valid channel positions for
613    * the output can be constructed from them. If this is set to %TRUE setting the
614    * channel-positions property overwrites this property again.
615    *
616    */
617   g_object_class_install_property (gobject_class,
618       PROP_CHANNEL_POSITIONS_FROM_INPUT,
619       g_param_spec_boolean ("channel-positions-from-input",
620           "Channel positions from input",
621           "Take channel positions from the input", TRUE,
622           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
623 
624   gst_type_mark_as_plugin_api (GST_TYPE_AUDIO_INTERLEAVE_PAD, 0);
625 }
626 
627 static void
gst_audio_interleave_init(GstAudioInterleave * self)628 gst_audio_interleave_init (GstAudioInterleave * self)
629 {
630   self->input_channel_positions = g_value_array_new (0);
631   self->channel_positions_from_input = TRUE;
632   self->channel_positions = self->input_channel_positions;
633 }
634 
635 static void
gst_audio_interleave_finalize(GObject * object)636 gst_audio_interleave_finalize (GObject * object)
637 {
638   GstAudioInterleave *self = GST_AUDIO_INTERLEAVE (object);
639 
640   if (self->channel_positions
641       && self->channel_positions != self->input_channel_positions) {
642     g_value_array_free (self->channel_positions);
643     self->channel_positions = NULL;
644   }
645 
646   if (self->input_channel_positions) {
647     g_value_array_free (self->input_channel_positions);
648     self->input_channel_positions = NULL;
649   }
650 
651   G_OBJECT_CLASS (parent_class)->finalize (object);
652 }
653 
654 static void
gst_audio_interleave_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)655 gst_audio_interleave_set_property (GObject * object, guint prop_id,
656     const GValue * value, GParamSpec * pspec)
657 {
658   GstAudioInterleave *self = GST_AUDIO_INTERLEAVE (object);
659 
660   switch (prop_id) {
661     case PROP_CHANNEL_POSITIONS:
662       g_return_if_fail (
663           ((GValueArray *) g_value_get_boxed (value))->n_values > 0);
664 
665       if (self->channel_positions &&
666           self->channel_positions != self->input_channel_positions)
667         g_value_array_free (self->channel_positions);
668 
669       self->channel_positions = g_value_dup_boxed (value);
670       self->channel_positions_from_input = FALSE;
671       break;
672     case PROP_CHANNEL_POSITIONS_FROM_INPUT:
673       self->channel_positions_from_input = g_value_get_boolean (value);
674 
675       if (self->channel_positions_from_input) {
676         if (self->channel_positions &&
677             self->channel_positions != self->input_channel_positions)
678           g_value_array_free (self->channel_positions);
679         self->channel_positions = self->input_channel_positions;
680       }
681       break;
682     default:
683       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
684       break;
685   }
686 }
687 
688 static void
gst_audio_interleave_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)689 gst_audio_interleave_get_property (GObject * object, guint prop_id,
690     GValue * value, GParamSpec * pspec)
691 {
692   GstAudioInterleave *self = GST_AUDIO_INTERLEAVE (object);
693 
694   switch (prop_id) {
695     case PROP_CHANNEL_POSITIONS:
696       g_value_set_boxed (value, self->channel_positions);
697       break;
698     case PROP_CHANNEL_POSITIONS_FROM_INPUT:
699       g_value_set_boolean (value, self->channel_positions_from_input);
700       break;
701     default:
702       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
703       break;
704   }
705 }
706 
707 static gboolean
gst_audio_interleave_stop(GstAggregator * agg)708 gst_audio_interleave_stop (GstAggregator * agg)
709 {
710   GstAudioInterleave *self = GST_AUDIO_INTERLEAVE (agg);
711 
712   if (!GST_AGGREGATOR_CLASS (parent_class)->stop (agg))
713     return FALSE;
714 
715   gst_caps_replace (&self->sinkcaps, NULL);
716 
717   return TRUE;
718 }
719 
720 static GstPad *
gst_audio_interleave_request_new_pad(GstElement * element,GstPadTemplate * templ,const gchar * req_name,const GstCaps * caps)721 gst_audio_interleave_request_new_pad (GstElement * element,
722     GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps)
723 {
724   GstAudioInterleave *self = GST_AUDIO_INTERLEAVE (element);
725   GstAudioInterleavePad *newpad;
726   gchar *pad_name;
727   gint channel, padnumber;
728   GValue val = { 0, };
729 
730   /* FIXME: We ignore req_name, this is evil! */
731 
732   GST_OBJECT_LOCK (self);
733   padnumber = g_atomic_int_add (&self->padcounter, 1);
734   channel = self->channels++;
735   if (!self->channel_positions_from_input)
736     channel = padnumber;
737   GST_OBJECT_UNLOCK (self);
738 
739   pad_name = g_strdup_printf ("sink_%u", padnumber);
740   newpad = (GstAudioInterleavePad *)
741       GST_ELEMENT_CLASS (parent_class)->request_new_pad (element,
742       templ, pad_name, caps);
743   g_free (pad_name);
744   if (newpad == NULL)
745     goto could_not_create;
746 
747   newpad->channel = channel;
748   gst_pad_use_fixed_caps (GST_PAD (newpad));
749 
750   gst_child_proxy_child_added (GST_CHILD_PROXY (element), G_OBJECT (newpad),
751       GST_OBJECT_NAME (newpad));
752 
753 
754   g_value_init (&val, GST_TYPE_AUDIO_CHANNEL_POSITION);
755   g_value_set_enum (&val, GST_AUDIO_CHANNEL_POSITION_NONE);
756   self->input_channel_positions =
757       g_value_array_append (self->input_channel_positions, &val);
758   g_value_unset (&val);
759 
760   /* Update the src caps if we already have them */
761   gst_pad_mark_reconfigure (GST_AGGREGATOR_SRC_PAD (self));
762 
763   return GST_PAD_CAST (newpad);
764 
765 could_not_create:
766   {
767     GST_DEBUG_OBJECT (element, "could not create/add  pad");
768     return NULL;
769   }
770 }
771 
772 static void
gst_audio_interleave_release_pad(GstElement * element,GstPad * pad)773 gst_audio_interleave_release_pad (GstElement * element, GstPad * pad)
774 {
775   GstAudioInterleave *self;
776   gint position;
777   GList *l;
778 
779   self = GST_AUDIO_INTERLEAVE (element);
780 
781   /* Take lock to make sure we're not changing this when processing buffers */
782   GST_OBJECT_LOCK (self);
783 
784   self->channels--;
785 
786   position = GST_AUDIO_INTERLEAVE_PAD (pad)->channel;
787   g_value_array_remove (self->input_channel_positions, position);
788 
789   /* Update channel numbers */
790   /* Taken above, GST_OBJECT_LOCK (self); */
791   for (l = GST_ELEMENT_CAST (self)->sinkpads; l != NULL; l = l->next) {
792     GstAudioInterleavePad *ipad = GST_AUDIO_INTERLEAVE_PAD (l->data);
793 
794     if (GST_AUDIO_INTERLEAVE_PAD (pad)->channel < ipad->channel)
795       ipad->channel--;
796   }
797 
798   gst_pad_mark_reconfigure (GST_AGGREGATOR_SRC_PAD (self));
799   GST_OBJECT_UNLOCK (self);
800 
801 
802   GST_DEBUG_OBJECT (self, "release pad %s:%s", GST_DEBUG_PAD_NAME (pad));
803 
804   gst_child_proxy_child_removed (GST_CHILD_PROXY (self), G_OBJECT (pad),
805       GST_OBJECT_NAME (pad));
806 
807   GST_ELEMENT_CLASS (parent_class)->release_pad (element, pad);
808 }
809 
810 
811 /* Called with object lock and pad object lock held */
812 static gboolean
gst_audio_interleave_aggregate_one_buffer(GstAudioAggregator * aagg,GstAudioAggregatorPad * aaggpad,GstBuffer * inbuf,guint in_offset,GstBuffer * outbuf,guint out_offset,guint num_frames)813 gst_audio_interleave_aggregate_one_buffer (GstAudioAggregator * aagg,
814     GstAudioAggregatorPad * aaggpad, GstBuffer * inbuf, guint in_offset,
815     GstBuffer * outbuf, guint out_offset, guint num_frames)
816 {
817   GstAudioInterleave *self = GST_AUDIO_INTERLEAVE (aagg);
818   GstAudioInterleavePad *pad = GST_AUDIO_INTERLEAVE_PAD (aaggpad);
819   GstMapInfo inmap;
820   GstMapInfo outmap;
821   gint out_width, in_bpf, out_bpf, out_channels, channel;
822   guint8 *outdata;
823   GstAggregator *agg = GST_AGGREGATOR (aagg);
824   GstAudioAggregatorPad *srcpad = GST_AUDIO_AGGREGATOR_PAD (agg->srcpad);
825 
826   GST_OBJECT_LOCK (aagg);
827   GST_OBJECT_LOCK (aaggpad);
828 
829   out_width = GST_AUDIO_INFO_WIDTH (&srcpad->info) / 8;
830   in_bpf = GST_AUDIO_INFO_BPF (&aaggpad->info);
831   out_bpf = GST_AUDIO_INFO_BPF (&srcpad->info);
832   out_channels = GST_AUDIO_INFO_CHANNELS (&srcpad->info);
833 
834   gst_buffer_map (outbuf, &outmap, GST_MAP_READWRITE);
835   gst_buffer_map (inbuf, &inmap, GST_MAP_READ);
836   GST_LOG_OBJECT (pad, "interleaves %u frames on channel %d/%d at offset %u"
837       " from offset %u", num_frames, pad->channel, out_channels,
838       out_offset * out_bpf, in_offset * in_bpf);
839 
840   if (self->channels > 64) {
841     channel = pad->channel;
842   } else {
843     channel = self->default_channels_ordering_map[pad->channel];
844   }
845 
846   outdata = outmap.data + (out_offset * out_bpf) + (out_width * channel);
847 
848 
849   self->func (outdata, inmap.data + (in_offset * in_bpf), out_channels,
850       num_frames);
851 
852 
853   gst_buffer_unmap (inbuf, &inmap);
854   gst_buffer_unmap (outbuf, &outmap);
855 
856   GST_OBJECT_UNLOCK (aaggpad);
857   GST_OBJECT_UNLOCK (aagg);
858 
859   return TRUE;
860 }
861 
862 
863 /* GstChildProxy implementation */
864 static GObject *
gst_audio_interleave_child_proxy_get_child_by_index(GstChildProxy * child_proxy,guint index)865 gst_audio_interleave_child_proxy_get_child_by_index (GstChildProxy *
866     child_proxy, guint index)
867 {
868   GstAudioInterleave *self = GST_AUDIO_INTERLEAVE (child_proxy);
869   GObject *obj = NULL;
870 
871   GST_OBJECT_LOCK (self);
872   obj = g_list_nth_data (GST_ELEMENT_CAST (self)->sinkpads, index);
873   if (obj)
874     gst_object_ref (obj);
875   GST_OBJECT_UNLOCK (self);
876 
877   return obj;
878 }
879 
880 static guint
gst_audio_interleave_child_proxy_get_children_count(GstChildProxy * child_proxy)881 gst_audio_interleave_child_proxy_get_children_count (GstChildProxy *
882     child_proxy)
883 {
884   guint count = 0;
885   GstAudioInterleave *self = GST_AUDIO_INTERLEAVE (child_proxy);
886 
887   GST_OBJECT_LOCK (self);
888   count = GST_ELEMENT_CAST (self)->numsinkpads;
889   GST_OBJECT_UNLOCK (self);
890   GST_INFO_OBJECT (self, "Children Count: %d", count);
891 
892   return count;
893 }
894 
895 static void
gst_audio_interleave_child_proxy_init(gpointer g_iface,gpointer iface_data)896 gst_audio_interleave_child_proxy_init (gpointer g_iface, gpointer iface_data)
897 {
898   GstChildProxyInterface *iface = g_iface;
899 
900   GST_INFO ("initializing child proxy interface");
901   iface->get_child_by_index =
902       gst_audio_interleave_child_proxy_get_child_by_index;
903   iface->get_children_count =
904       gst_audio_interleave_child_proxy_get_children_count;
905 }
906