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