1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2001 Thomas <thomas@apestaart.org>
4  *               2005,2006 Wim Taymans <wim@fluendo.com>
5  *                    2013 Sebastian Dröge <sebastian@centricular.com>
6  *
7  * audiomixer.c: AudioMixer element, N in, one out, samples are added
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
22  * Boston, MA 02110-1301, USA.
23  */
24 /**
25  * SECTION:element-audiomixer
26  * @title: audiomixer
27  *
28  * The audiomixer allows to mix several streams into one by adding the data.
29  * Mixed data is clamped to the min/max values of the data format.
30  *
31  * Unlike the adder element audiomixer properly synchronises all input streams
32  * and also handles live inputs such as capture sources or RTP properly.
33  *
34  * The audiomixer element can accept any sort of raw audio data, it will
35  * be converted to the target format if necessary, with the exception
36  * of the sample rate, which has to be identical to either what downstream
37  * expects, or the sample rate of the first configured pad. Use a capsfilter
38  * after the audiomixer element if you want to precisely control the format
39  * that comes out of the audiomixer, which supports changing the format of
40  * its output while playing.
41  *
42  * If you want to control the manner in which incoming data gets converted,
43  * see the #GstAudioAggregatorPad:converter-config property, which will let
44  * you for example change the way in which channels may get remapped.
45  *
46  * The input pads are from a GstPad subclass and have additional
47  * properties to mute each pad individually and set the volume:
48  *
49  * * "mute": Whether to mute the pad or not (#gboolean)
50  * * "volume": The volume of the pad, between 0.0 and 10.0 (#gdouble)
51  *
52  * ## Example launch line
53  * |[
54  * gst-launch-1.0 audiotestsrc freq=100 ! audiomixer name=mix ! audioconvert ! alsasink audiotestsrc freq=500 ! mix.
55  * ]| This pipeline produces two sine waves mixed together.
56  *
57  */
58 
59 #ifdef HAVE_CONFIG_H
60 #include "config.h"
61 #endif
62 
63 #include "gstaudiomixer.h"
64 #include <gst/audio/audio.h>
65 #include <string.h>             /* strcmp */
66 #include "gstaudiomixerorc.h"
67 
68 #include "gstaudiointerleave.h"
69 
70 #define GST_CAT_DEFAULT gst_audiomixer_debug
71 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
72 
73 #define DEFAULT_PAD_VOLUME (1.0)
74 #define DEFAULT_PAD_MUTE (FALSE)
75 
76 /* some defines for audio processing */
77 /* the volume factor is a range from 0.0 to (arbitrary) VOLUME_MAX_DOUBLE = 10.0
78  * we map 1.0 to VOLUME_UNITY_INT*
79  */
80 #define VOLUME_UNITY_INT8            8  /* internal int for unity 2^(8-5) */
81 #define VOLUME_UNITY_INT8_BIT_SHIFT  3  /* number of bits to shift for unity */
82 #define VOLUME_UNITY_INT16           2048       /* internal int for unity 2^(16-5) */
83 #define VOLUME_UNITY_INT16_BIT_SHIFT 11 /* number of bits to shift for unity */
84 #define VOLUME_UNITY_INT24           524288     /* internal int for unity 2^(24-5) */
85 #define VOLUME_UNITY_INT24_BIT_SHIFT 19 /* number of bits to shift for unity */
86 #define VOLUME_UNITY_INT32           134217728  /* internal int for unity 2^(32-5) */
87 #define VOLUME_UNITY_INT32_BIT_SHIFT 27
88 
89 enum
90 {
91   PROP_PAD_0,
92   PROP_PAD_VOLUME,
93   PROP_PAD_MUTE
94 };
95 
96 G_DEFINE_TYPE (GstAudioMixerPad, gst_audiomixer_pad,
97     GST_TYPE_AUDIO_AGGREGATOR_CONVERT_PAD);
98 
99 static void
gst_audiomixer_pad_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)100 gst_audiomixer_pad_get_property (GObject * object, guint prop_id,
101     GValue * value, GParamSpec * pspec)
102 {
103   GstAudioMixerPad *pad = GST_AUDIO_MIXER_PAD (object);
104 
105   switch (prop_id) {
106     case PROP_PAD_VOLUME:
107       g_value_set_double (value, pad->volume);
108       break;
109     case PROP_PAD_MUTE:
110       g_value_set_boolean (value, pad->mute);
111       break;
112     default:
113       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
114       break;
115   }
116 }
117 
118 static void
gst_audiomixer_pad_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)119 gst_audiomixer_pad_set_property (GObject * object, guint prop_id,
120     const GValue * value, GParamSpec * pspec)
121 {
122   GstAudioMixerPad *pad = GST_AUDIO_MIXER_PAD (object);
123 
124   switch (prop_id) {
125     case PROP_PAD_VOLUME:
126       GST_OBJECT_LOCK (pad);
127       pad->volume = g_value_get_double (value);
128       pad->volume_i8 = pad->volume * VOLUME_UNITY_INT8;
129       pad->volume_i16 = pad->volume * VOLUME_UNITY_INT16;
130       pad->volume_i32 = pad->volume * VOLUME_UNITY_INT32;
131       GST_OBJECT_UNLOCK (pad);
132       break;
133     case PROP_PAD_MUTE:
134       GST_OBJECT_LOCK (pad);
135       pad->mute = g_value_get_boolean (value);
136       GST_OBJECT_UNLOCK (pad);
137       break;
138     default:
139       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
140       break;
141   }
142 }
143 
144 static void
gst_audiomixer_pad_class_init(GstAudioMixerPadClass * klass)145 gst_audiomixer_pad_class_init (GstAudioMixerPadClass * klass)
146 {
147   GObjectClass *gobject_class = (GObjectClass *) klass;
148 
149   gobject_class->set_property = gst_audiomixer_pad_set_property;
150   gobject_class->get_property = gst_audiomixer_pad_get_property;
151 
152   g_object_class_install_property (gobject_class, PROP_PAD_VOLUME,
153       g_param_spec_double ("volume", "Volume", "Volume of this pad",
154           0.0, 10.0, DEFAULT_PAD_VOLUME,
155           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
156   g_object_class_install_property (gobject_class, PROP_PAD_MUTE,
157       g_param_spec_boolean ("mute", "Mute", "Mute this pad",
158           DEFAULT_PAD_MUTE,
159           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
160 }
161 
162 static void
gst_audiomixer_pad_init(GstAudioMixerPad * pad)163 gst_audiomixer_pad_init (GstAudioMixerPad * pad)
164 {
165   pad->volume = DEFAULT_PAD_VOLUME;
166   pad->mute = DEFAULT_PAD_MUTE;
167 }
168 
169 enum
170 {
171   PROP_0
172 };
173 
174 /* These are the formats we can mix natively */
175 
176 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
177 #define CAPS \
178   GST_AUDIO_CAPS_MAKE ("{ S32LE, U32LE, S16LE, U16LE, S8, U8, F32LE, F64LE }") \
179   ", layout = interleaved"
180 #else
181 #define CAPS \
182   GST_AUDIO_CAPS_MAKE ("{ S32BE, U32BE, S16BE, U16BE, S8, U8, F32BE, F64BE }") \
183   ", layout = interleaved"
184 #endif
185 
186 static GstStaticPadTemplate gst_audiomixer_src_template =
187 GST_STATIC_PAD_TEMPLATE ("src",
188     GST_PAD_SRC,
189     GST_PAD_ALWAYS,
190     GST_STATIC_CAPS (CAPS)
191     );
192 
193 #define SINK_CAPS \
194   GST_STATIC_CAPS (GST_AUDIO_CAPS_MAKE (GST_AUDIO_FORMATS_ALL) \
195       ", layout=interleaved")
196 
197 static GstStaticPadTemplate gst_audiomixer_sink_template =
198 GST_STATIC_PAD_TEMPLATE ("sink_%u",
199     GST_PAD_SINK,
200     GST_PAD_REQUEST,
201     SINK_CAPS);
202 
203 static void gst_audiomixer_child_proxy_init (gpointer g_iface,
204     gpointer iface_data);
205 
206 #define gst_audiomixer_parent_class parent_class
207 G_DEFINE_TYPE_WITH_CODE (GstAudioMixer, gst_audiomixer,
208     GST_TYPE_AUDIO_AGGREGATOR, G_IMPLEMENT_INTERFACE (GST_TYPE_CHILD_PROXY,
209         gst_audiomixer_child_proxy_init));
210 
211 static GstPad *gst_audiomixer_request_new_pad (GstElement * element,
212     GstPadTemplate * temp, const gchar * req_name, const GstCaps * caps);
213 static void gst_audiomixer_release_pad (GstElement * element, GstPad * pad);
214 
215 static gboolean
216 gst_audiomixer_aggregate_one_buffer (GstAudioAggregator * aagg,
217     GstAudioAggregatorPad * aaggpad, GstBuffer * inbuf, guint in_offset,
218     GstBuffer * outbuf, guint out_offset, guint num_samples);
219 
220 
221 static void
gst_audiomixer_class_init(GstAudioMixerClass * klass)222 gst_audiomixer_class_init (GstAudioMixerClass * klass)
223 {
224   GstElementClass *gstelement_class = (GstElementClass *) klass;
225   GstAudioAggregatorClass *aagg_class = (GstAudioAggregatorClass *) klass;
226 
227   gst_element_class_add_static_pad_template_with_gtype (gstelement_class,
228       &gst_audiomixer_src_template, GST_TYPE_AUDIO_AGGREGATOR_CONVERT_PAD);
229   gst_element_class_add_static_pad_template_with_gtype (gstelement_class,
230       &gst_audiomixer_sink_template, GST_TYPE_AUDIO_MIXER_PAD);
231   gst_element_class_set_static_metadata (gstelement_class, "AudioMixer",
232       "Generic/Audio", "Mixes multiple audio streams",
233       "Sebastian Dröge <sebastian@centricular.com>");
234 
235   gstelement_class->request_new_pad =
236       GST_DEBUG_FUNCPTR (gst_audiomixer_request_new_pad);
237   gstelement_class->release_pad =
238       GST_DEBUG_FUNCPTR (gst_audiomixer_release_pad);
239 
240   aagg_class->aggregate_one_buffer = gst_audiomixer_aggregate_one_buffer;
241 }
242 
243 static void
gst_audiomixer_init(GstAudioMixer * audiomixer)244 gst_audiomixer_init (GstAudioMixer * audiomixer)
245 {
246 }
247 
248 static GstPad *
gst_audiomixer_request_new_pad(GstElement * element,GstPadTemplate * templ,const gchar * req_name,const GstCaps * caps)249 gst_audiomixer_request_new_pad (GstElement * element, GstPadTemplate * templ,
250     const gchar * req_name, const GstCaps * caps)
251 {
252   GstAudioMixerPad *newpad;
253 
254   newpad = (GstAudioMixerPad *)
255       GST_ELEMENT_CLASS (parent_class)->request_new_pad (element,
256       templ, req_name, caps);
257 
258   if (newpad == NULL)
259     goto could_not_create;
260 
261   gst_child_proxy_child_added (GST_CHILD_PROXY (element), G_OBJECT (newpad),
262       GST_OBJECT_NAME (newpad));
263 
264   return GST_PAD_CAST (newpad);
265 
266 could_not_create:
267   {
268     GST_DEBUG_OBJECT (element, "could not create/add  pad");
269     return NULL;
270   }
271 }
272 
273 static void
gst_audiomixer_release_pad(GstElement * element,GstPad * pad)274 gst_audiomixer_release_pad (GstElement * element, GstPad * pad)
275 {
276   GstAudioMixer *audiomixer;
277 
278   audiomixer = GST_AUDIO_MIXER (element);
279 
280   GST_DEBUG_OBJECT (audiomixer, "release pad %s:%s", GST_DEBUG_PAD_NAME (pad));
281 
282   gst_child_proxy_child_removed (GST_CHILD_PROXY (audiomixer), G_OBJECT (pad),
283       GST_OBJECT_NAME (pad));
284 
285   GST_ELEMENT_CLASS (parent_class)->release_pad (element, pad);
286 }
287 
288 
289 static gboolean
gst_audiomixer_aggregate_one_buffer(GstAudioAggregator * aagg,GstAudioAggregatorPad * aaggpad,GstBuffer * inbuf,guint in_offset,GstBuffer * outbuf,guint out_offset,guint num_frames)290 gst_audiomixer_aggregate_one_buffer (GstAudioAggregator * aagg,
291     GstAudioAggregatorPad * aaggpad, GstBuffer * inbuf, guint in_offset,
292     GstBuffer * outbuf, guint out_offset, guint num_frames)
293 {
294   GstAudioMixerPad *pad = GST_AUDIO_MIXER_PAD (aaggpad);
295   GstMapInfo inmap;
296   GstMapInfo outmap;
297   gint bpf;
298   GstAggregator *agg = GST_AGGREGATOR (aagg);
299   GstAudioAggregatorPad *srcpad = GST_AUDIO_AGGREGATOR_PAD (agg->srcpad);
300 
301   GST_OBJECT_LOCK (aagg);
302   GST_OBJECT_LOCK (aaggpad);
303 
304   if (pad->mute || pad->volume < G_MINDOUBLE) {
305     GST_DEBUG_OBJECT (pad, "Skipping muted pad");
306     GST_OBJECT_UNLOCK (aaggpad);
307     GST_OBJECT_UNLOCK (aagg);
308     return FALSE;
309   }
310 
311   bpf = GST_AUDIO_INFO_BPF (&srcpad->info);
312 
313   gst_buffer_map (outbuf, &outmap, GST_MAP_READWRITE);
314   gst_buffer_map (inbuf, &inmap, GST_MAP_READ);
315   GST_LOG_OBJECT (pad, "mixing %u bytes at offset %u from offset %u",
316       num_frames * bpf, out_offset * bpf, in_offset * bpf);
317 
318   /* further buffers, need to add them */
319   if (pad->volume == 1.0) {
320     switch (srcpad->info.finfo->format) {
321       case GST_AUDIO_FORMAT_U8:
322         audiomixer_orc_add_u8 ((gpointer) (outmap.data + out_offset * bpf),
323             (gpointer) (inmap.data + in_offset * bpf),
324             num_frames * srcpad->info.channels);
325         break;
326       case GST_AUDIO_FORMAT_S8:
327         audiomixer_orc_add_s8 ((gpointer) (outmap.data + out_offset * bpf),
328             (gpointer) (inmap.data + in_offset * bpf),
329             num_frames * srcpad->info.channels);
330         break;
331       case GST_AUDIO_FORMAT_U16:
332         audiomixer_orc_add_u16 ((gpointer) (outmap.data + out_offset * bpf),
333             (gpointer) (inmap.data + in_offset * bpf),
334             num_frames * srcpad->info.channels);
335         break;
336       case GST_AUDIO_FORMAT_S16:
337         audiomixer_orc_add_s16 ((gpointer) (outmap.data + out_offset * bpf),
338             (gpointer) (inmap.data + in_offset * bpf),
339             num_frames * srcpad->info.channels);
340         break;
341       case GST_AUDIO_FORMAT_U32:
342         audiomixer_orc_add_u32 ((gpointer) (outmap.data + out_offset * bpf),
343             (gpointer) (inmap.data + in_offset * bpf),
344             num_frames * srcpad->info.channels);
345         break;
346       case GST_AUDIO_FORMAT_S32:
347         audiomixer_orc_add_s32 ((gpointer) (outmap.data + out_offset * bpf),
348             (gpointer) (inmap.data + in_offset * bpf),
349             num_frames * srcpad->info.channels);
350         break;
351       case GST_AUDIO_FORMAT_F32:
352         audiomixer_orc_add_f32 ((gpointer) (outmap.data + out_offset * bpf),
353             (gpointer) (inmap.data + in_offset * bpf),
354             num_frames * srcpad->info.channels);
355         break;
356       case GST_AUDIO_FORMAT_F64:
357         audiomixer_orc_add_f64 ((gpointer) (outmap.data + out_offset * bpf),
358             (gpointer) (inmap.data + in_offset * bpf),
359             num_frames * srcpad->info.channels);
360         break;
361       default:
362         g_assert_not_reached ();
363         break;
364     }
365   } else {
366     switch (srcpad->info.finfo->format) {
367       case GST_AUDIO_FORMAT_U8:
368         audiomixer_orc_add_volume_u8 ((gpointer) (outmap.data +
369                 out_offset * bpf), (gpointer) (inmap.data + in_offset * bpf),
370             pad->volume_i8, num_frames * srcpad->info.channels);
371         break;
372       case GST_AUDIO_FORMAT_S8:
373         audiomixer_orc_add_volume_s8 ((gpointer) (outmap.data +
374                 out_offset * bpf), (gpointer) (inmap.data + in_offset * bpf),
375             pad->volume_i8, num_frames * srcpad->info.channels);
376         break;
377       case GST_AUDIO_FORMAT_U16:
378         audiomixer_orc_add_volume_u16 ((gpointer) (outmap.data +
379                 out_offset * bpf), (gpointer) (inmap.data + in_offset * bpf),
380             pad->volume_i16, num_frames * srcpad->info.channels);
381         break;
382       case GST_AUDIO_FORMAT_S16:
383         audiomixer_orc_add_volume_s16 ((gpointer) (outmap.data +
384                 out_offset * bpf), (gpointer) (inmap.data + in_offset * bpf),
385             pad->volume_i16, num_frames * srcpad->info.channels);
386         break;
387       case GST_AUDIO_FORMAT_U32:
388         audiomixer_orc_add_volume_u32 ((gpointer) (outmap.data +
389                 out_offset * bpf), (gpointer) (inmap.data + in_offset * bpf),
390             pad->volume_i32, num_frames * srcpad->info.channels);
391         break;
392       case GST_AUDIO_FORMAT_S32:
393         audiomixer_orc_add_volume_s32 ((gpointer) (outmap.data +
394                 out_offset * bpf), (gpointer) (inmap.data + in_offset * bpf),
395             pad->volume_i32, num_frames * srcpad->info.channels);
396         break;
397       case GST_AUDIO_FORMAT_F32:
398         audiomixer_orc_add_volume_f32 ((gpointer) (outmap.data +
399                 out_offset * bpf), (gpointer) (inmap.data + in_offset * bpf),
400             pad->volume, num_frames * srcpad->info.channels);
401         break;
402       case GST_AUDIO_FORMAT_F64:
403         audiomixer_orc_add_volume_f64 ((gpointer) (outmap.data +
404                 out_offset * bpf), (gpointer) (inmap.data + in_offset * bpf),
405             pad->volume, num_frames * srcpad->info.channels);
406         break;
407       default:
408         g_assert_not_reached ();
409         break;
410     }
411   }
412   gst_buffer_unmap (inbuf, &inmap);
413   gst_buffer_unmap (outbuf, &outmap);
414 
415   GST_OBJECT_UNLOCK (aaggpad);
416   GST_OBJECT_UNLOCK (aagg);
417 
418   return TRUE;
419 }
420 
421 
422 /* GstChildProxy implementation */
423 static GObject *
gst_audiomixer_child_proxy_get_child_by_index(GstChildProxy * child_proxy,guint index)424 gst_audiomixer_child_proxy_get_child_by_index (GstChildProxy * child_proxy,
425     guint index)
426 {
427   GstAudioMixer *audiomixer = GST_AUDIO_MIXER (child_proxy);
428   GObject *obj = NULL;
429 
430   GST_OBJECT_LOCK (audiomixer);
431   obj = g_list_nth_data (GST_ELEMENT_CAST (audiomixer)->sinkpads, index);
432   if (obj)
433     gst_object_ref (obj);
434   GST_OBJECT_UNLOCK (audiomixer);
435 
436   return obj;
437 }
438 
439 static guint
gst_audiomixer_child_proxy_get_children_count(GstChildProxy * child_proxy)440 gst_audiomixer_child_proxy_get_children_count (GstChildProxy * child_proxy)
441 {
442   guint count = 0;
443   GstAudioMixer *audiomixer = GST_AUDIO_MIXER (child_proxy);
444 
445   GST_OBJECT_LOCK (audiomixer);
446   count = GST_ELEMENT_CAST (audiomixer)->numsinkpads;
447   GST_OBJECT_UNLOCK (audiomixer);
448   GST_INFO_OBJECT (audiomixer, "Children Count: %d", count);
449 
450   return count;
451 }
452 
453 static void
gst_audiomixer_child_proxy_init(gpointer g_iface,gpointer iface_data)454 gst_audiomixer_child_proxy_init (gpointer g_iface, gpointer iface_data)
455 {
456   GstChildProxyInterface *iface = g_iface;
457 
458   GST_INFO ("intializing child proxy interface");
459   iface->get_child_by_index = gst_audiomixer_child_proxy_get_child_by_index;
460   iface->get_children_count = gst_audiomixer_child_proxy_get_children_count;
461 }
462 
463 /* Empty liveadder alias with non-zero latency */
464 
465 typedef GstAudioMixer GstLiveAdder;
466 typedef GstAudioMixerClass GstLiveAdderClass;
467 
468 static GType gst_live_adder_get_type (void);
469 #define GST_TYPE_LIVE_ADDER gst_live_adder_get_type ()
470 
471 G_DEFINE_TYPE (GstLiveAdder, gst_live_adder, GST_TYPE_AUDIO_MIXER);
472 
473 enum
474 {
475   LIVEADDER_PROP_LATENCY = 1
476 };
477 
478 static void
gst_live_adder_init(GstLiveAdder * self)479 gst_live_adder_init (GstLiveAdder * self)
480 {
481 }
482 
483 static void
gst_live_adder_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)484 gst_live_adder_set_property (GObject * object, guint prop_id,
485     const GValue * value, GParamSpec * pspec)
486 {
487   switch (prop_id) {
488     case LIVEADDER_PROP_LATENCY:
489     {
490       GParamSpec *parent_spec =
491           g_object_class_find_property (G_OBJECT_CLASS
492           (gst_live_adder_parent_class), "latency");
493       GObjectClass *pspec_class = g_type_class_peek (parent_spec->owner_type);
494       GValue v = { 0 };
495 
496       g_value_init (&v, G_TYPE_UINT64);
497 
498       g_value_set_uint64 (&v, g_value_get_uint (value) * GST_MSECOND);
499 
500       G_OBJECT_CLASS (pspec_class)->set_property (object,
501           parent_spec->param_id, &v, parent_spec);
502       break;
503     }
504     default:
505       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
506       break;
507   }
508 }
509 
510 static void
gst_live_adder_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)511 gst_live_adder_get_property (GObject * object, guint prop_id, GValue * value,
512     GParamSpec * pspec)
513 {
514   switch (prop_id) {
515     case LIVEADDER_PROP_LATENCY:
516     {
517       GParamSpec *parent_spec =
518           g_object_class_find_property (G_OBJECT_CLASS
519           (gst_live_adder_parent_class), "latency");
520       GObjectClass *pspec_class = g_type_class_peek (parent_spec->owner_type);
521       GValue v = { 0 };
522 
523       g_value_init (&v, G_TYPE_UINT64);
524 
525       G_OBJECT_CLASS (pspec_class)->get_property (object,
526           parent_spec->param_id, &v, parent_spec);
527 
528       g_value_set_uint (value, g_value_get_uint64 (&v) / GST_MSECOND);
529       break;
530     }
531     default:
532       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
533       break;
534   }
535 }
536 
537 
538 static void
gst_live_adder_class_init(GstLiveAdderClass * klass)539 gst_live_adder_class_init (GstLiveAdderClass * klass)
540 {
541   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
542 
543   gobject_class->set_property = gst_live_adder_set_property;
544   gobject_class->get_property = gst_live_adder_get_property;
545 
546   g_object_class_install_property (gobject_class, LIVEADDER_PROP_LATENCY,
547       g_param_spec_uint ("latency", "Buffer latency",
548           "Additional latency in live mode to allow upstream "
549           "to take longer to produce buffers for the current "
550           "position (in milliseconds)", 0, G_MAXUINT,
551           30, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
552 }
553 
554 static gboolean
plugin_init(GstPlugin * plugin)555 plugin_init (GstPlugin * plugin)
556 {
557   GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "audiomixer", 0,
558       "audio mixing element");
559 
560   if (!gst_element_register (plugin, "audiomixer", GST_RANK_NONE,
561           GST_TYPE_AUDIO_MIXER))
562     return FALSE;
563 
564   if (!gst_element_register (plugin, "liveadder", GST_RANK_NONE,
565           GST_TYPE_LIVE_ADDER))
566     return FALSE;
567 
568   if (!gst_element_register (plugin, "audiointerleave", GST_RANK_NONE,
569           GST_TYPE_AUDIO_INTERLEAVE))
570     return FALSE;
571 
572   return TRUE;
573 }
574 
575 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
576     GST_VERSION_MINOR,
577     audiomixer,
578     "Mixes multiple audio streams",
579     plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
580