gstadder.c 26.3 KB
Newer Older
Andy Wingo's avatar
Andy Wingo committed
1
2
3
/* GStreamer
 * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
 *                    2001 Thomas <thomas@apestaart.org>
4
 *               2005,2006 Wim Taymans <wim@fluendo.com>
Andy Wingo's avatar
Andy Wingo committed
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 *
 * adder.c: Adder element, N in, one out, samples are added
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
23
24
25
26
/**
 * SECTION:element-adder
 *
 * <refsect2>
27
 * <para>
28
29
 * The Adder allows to mix several streams into one by adding the data.
 * Mixed data is clamped to the min/max values of the data format.
30
 * </para>
31
32
33
34
35
36
37
 * <title>Example launch line</title>
 * <para>
 * <programlisting>
 * gst-launch audiotestsrc freq=100 ! adder name=mix ! audioconvert ! alsasink audiotestsrc freq=500 ! mix.
 * </programlisting>
 * This pipeline produces two sine waves mixed together.
 * </para>
38
39
40
41
 * <para>
 * The Adder currently mixes all data received on the sinkpads as soon as possible
 * without trying to synchronize the streams.
 * </para>
42
 * </refsect2>
43
44
 *
 * Last reviewed on 2006-05-09 (0.10.7)
45
 */
46
/* Element-Checklist-Version: 5 */
Andy Wingo's avatar
Andy Wingo committed
47

48
49
50
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
Andy Wingo's avatar
Andy Wingo committed
51
#include "gstadder.h"
52
#include <gst/audio/audio.h>
53
#include <string.h>             /* strcmp */
Andy Wingo's avatar
Andy Wingo committed
54

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
55
/* highest positive/lowest negative x-bit value we can use for clamping */
56
57
58
59
60
61
62
63
64
65
66
67
68
#define MAX_INT_32  ((gint32) (0x7fffffff))
#define MAX_INT_16  ((gint16) (0x7fff))
#define MAX_INT_8   ((gint8)  (0x7f))
#define MAX_UINT_32 ((guint32)(0xffffffff))
#define MAX_UINT_16 ((guint16)(0xffff))
#define MAX_UINT_8  ((guint8) (0xff))

#define MIN_INT_32  ((gint32) (0x80000000))
#define MIN_INT_16  ((gint16) (0x8000))
#define MIN_INT_8   ((gint8)  (0x80))
#define MIN_UINT_32 ((guint32)(0x00000000))
#define MIN_UINT_16 ((guint16)(0x0000))
#define MIN_UINT_8  ((guint8) (0x00))
Andy Wingo's avatar
Andy Wingo committed
69

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
70
#define GST_CAT_DEFAULT gst_adder_debug
71
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
72

73
/* elementfactory information */
74
static const GstElementDetails adder_details = GST_ELEMENT_DETAILS ("Adder",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
75
76
77
    "Generic/Audio",
    "Add N audio channels together",
    "Thomas <thomas@apestaart.org>");
Andy Wingo's avatar
Andy Wingo committed
78

David Schleef's avatar
David Schleef committed
79
static GstStaticPadTemplate gst_adder_src_template =
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
80
81
82
83
    GST_STATIC_PAD_TEMPLATE ("src",
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS (GST_AUDIO_INT_PAD_TEMPLATE_CAPS "; "
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
84
        GST_AUDIO_FLOAT_PAD_TEMPLATE_CAPS)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
85
    );
Andy Wingo's avatar
Andy Wingo committed
86

David Schleef's avatar
David Schleef committed
87
static GstStaticPadTemplate gst_adder_sink_template =
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
88
89
90
91
    GST_STATIC_PAD_TEMPLATE ("sink%d",
    GST_PAD_SINK,
    GST_PAD_REQUEST,
    GST_STATIC_CAPS (GST_AUDIO_INT_PAD_TEMPLATE_CAPS "; "
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
92
        GST_AUDIO_FLOAT_PAD_TEMPLATE_CAPS)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
93
94
95
96
    );

static void gst_adder_class_init (GstAdderClass * klass);
static void gst_adder_init (GstAdder * adder);
97
static void gst_adder_finalize (GObject * object);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
98

99
100
101
static gboolean gst_adder_setcaps (GstPad * pad, GstCaps * caps);
static gboolean gst_adder_query (GstPad * pad, GstQuery * query);
static gboolean gst_adder_src_event (GstPad * pad, GstEvent * event);
102
static gboolean gst_adder_sink_event (GstPad * pad, GstEvent * event);
103

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
104
105
static GstPad *gst_adder_request_new_pad (GstElement * element,
    GstPadTemplate * temp, const gchar * unused);
106
107
static void gst_adder_release_pad (GstElement * element, GstPad * pad);

108
109
static GstStateChangeReturn gst_adder_change_state (GstElement * element,
    GstStateChange transition);
Andy Wingo's avatar
Andy Wingo committed
110

111
112
static GstFlowReturn gst_adder_collected (GstCollectPads * pads,
    gpointer user_data);
Andy Wingo's avatar
Andy Wingo committed
113
114

static GstElementClass *parent_class = NULL;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
115

Andy Wingo's avatar
Andy Wingo committed
116
GType
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
117
118
gst_adder_get_type (void)
{
Andy Wingo's avatar
Andy Wingo committed
119
120
  static GType adder_type = 0;

121
  if (G_UNLIKELY (adder_type == 0)) {
Andy Wingo's avatar
Andy Wingo committed
122
    static const GTypeInfo adder_info = {
123
124
      sizeof (GstAdderClass), NULL, NULL,
      (GClassInitFunc) gst_adder_class_init, NULL, NULL,
125
      sizeof (GstAdder), 0,
126
      (GInstanceInitFunc) gst_adder_init,
Andy Wingo's avatar
Andy Wingo committed
127
    };
128

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
129
    adder_type = g_type_register_static (GST_TYPE_ELEMENT, "GstAdder",
130
        &adder_info, 0);
131
    GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "adder", 0,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
132
        "audio channel mixing element");
Andy Wingo's avatar
Andy Wingo committed
133
134
135
136
  }
  return adder_type;
}

137
/* clipping versions */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
138
139
140
141
142
#define MAKE_FUNC(name,type,ttype,min,max)                      \
static void name (type *out, type *in, gint bytes) {            \
  gint i;                                                       \
  for (i = 0; i < bytes / sizeof (type); i++)                   \
    out[i] = CLAMP ((ttype)out[i] + (ttype)in[i], min, max);    \
143
144
}

145
146
147
148
149
150
151
152
/* non-clipping versions (for float) */
#define MAKE_FUNC_NC(name,type,ttype)                           \
static void name (type *out, type *in, gint bytes) {            \
  gint i;                                                       \
  for (i = 0; i < bytes / sizeof (type); i++)                   \
    out[i] = (ttype)out[i] + (ttype)in[i];                      \
}

153
/* *INDENT-OFF* */
154
MAKE_FUNC (add_int32, gint32, gint64, MIN_INT_32, MAX_INT_32)
155
156
157
158
159
MAKE_FUNC (add_int16, gint16, gint32, MIN_INT_16, MAX_INT_16)
MAKE_FUNC (add_int8, gint8, gint16, MIN_INT_8, MAX_INT_8)
MAKE_FUNC (add_uint32, guint32, guint64, MIN_UINT_32, MAX_UINT_32)
MAKE_FUNC (add_uint16, guint16, guint32, MIN_UINT_16, MAX_UINT_16)
MAKE_FUNC (add_uint8, guint8, guint16, MIN_UINT_8, MAX_UINT_8)
160
161
MAKE_FUNC_NC (add_float64, gdouble, gdouble)
MAKE_FUNC_NC (add_float32, gfloat, gfloat)
162
163
/* *INDENT-ON* */

164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
/* we can only accept caps that we and downstream can handle. */
static GstCaps *
gst_adder_sink_getcaps (GstPad * pad)
{
  GstAdder *adder;
  GstCaps *result, *peercaps, *sinkcaps;

  adder = GST_ADDER (GST_PAD_PARENT (pad));

  GST_OBJECT_LOCK (adder);
  /* get the downstream possible caps */
  peercaps = gst_pad_peer_get_caps (adder->srcpad);
  /* get the allowed caps on this sinkpad, we use the fixed caps function so
   * that it does not call recursively in this function. */
  sinkcaps = gst_pad_get_fixed_caps_func (pad);
  if (peercaps) {
    /* if the peer has caps, intersect */
    GST_DEBUG_OBJECT (adder, "intersecting peer and template caps");
    result = gst_caps_intersect (peercaps, sinkcaps);
    gst_caps_unref (peercaps);
    gst_caps_unref (sinkcaps);
  } else {
    /* the peer has no caps (or there is no peer), just use the allowed caps
     * of this sinkpad. */
    GST_DEBUG_OBJECT (adder, "no peer caps, using sinkcaps");
    result = sinkcaps;
  }
  GST_OBJECT_UNLOCK (adder);

  return result;
}

/* the first caps we receive on any of the sinkpads will define the caps for all
 * the other sinkpads because we can only mix streams with the same caps.
 * */
199
200
static gboolean
gst_adder_setcaps (GstPad * pad, GstCaps * caps)
201
202
{
  GstAdder *adder;
203
  GList *pads;
204
  GstStructure *structure;
205
  const char *media_type;
206

207
  adder = GST_ADDER (GST_PAD_PARENT (pad));
208

209
210
211
  GST_LOG_OBJECT (adder, "setting caps on pad %p,%s to %" GST_PTR_FORMAT, pad,
      GST_PAD_NAME (pad), caps);

212
213
  /* FIXME, see if the other pads can accept the format. Also lock the
   * format on the other pads to this new format. */
214
  GST_OBJECT_LOCK (adder);
215
  pads = GST_ELEMENT (adder)->pads;
216
217
218
219
  while (pads) {
    GstPad *otherpad = GST_PAD (pads->data);

    if (otherpad != pad) {
220
      gst_caps_replace (&GST_PAD_CAPS (otherpad), caps);
221
    }
222
223
    pads = g_list_next (pads);
  }
224
  GST_OBJECT_UNLOCK (adder);
225

226
  /* parse caps now */
227
228
229
  structure = gst_caps_get_structure (caps, 0);
  media_type = gst_structure_get_name (structure);
  if (strcmp (media_type, "audio/x-raw-int") == 0) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
230
    GST_DEBUG_OBJECT (adder, "parse_caps sets adder to format int");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
231
232
233
234
235
    adder->format = GST_ADDER_FORMAT_INT;
    gst_structure_get_int (structure, "width", &adder->width);
    gst_structure_get_int (structure, "depth", &adder->depth);
    gst_structure_get_int (structure, "endianness", &adder->endianness);
    gst_structure_get_boolean (structure, "signed", &adder->is_signed);
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255

    if (adder->endianness != G_BYTE_ORDER)
      goto not_supported;

    switch (adder->width) {
      case 8:
        adder->func = (adder->is_signed ?
            (GstAdderFunction) add_int8 : (GstAdderFunction) add_uint8);
        break;
      case 16:
        adder->func = (adder->is_signed ?
            (GstAdderFunction) add_int16 : (GstAdderFunction) add_uint16);
        break;
      case 32:
        adder->func = (adder->is_signed ?
            (GstAdderFunction) add_int32 : (GstAdderFunction) add_uint32);
        break;
      default:
        goto not_supported;
    }
256
  } else if (strcmp (media_type, "audio/x-raw-float") == 0) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
257
    GST_DEBUG_OBJECT (adder, "parse_caps sets adder to format float");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
258
259
    adder->format = GST_ADDER_FORMAT_FLOAT;
    gst_structure_get_int (structure, "width", &adder->width);
260
261
262
263
264
265
266
267
268
269
270
271
272

    switch (adder->width) {
      case 32:
        adder->func = (GstAdderFunction) add_float32;
        break;
      case 64:
        adder->func = (GstAdderFunction) add_float64;
        break;
      default:
        goto not_supported;
    }
  } else {
    goto not_supported;
273
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
274

275
276
  gst_structure_get_int (structure, "channels", &adder->channels);
  gst_structure_get_int (structure, "rate", &adder->rate);
277
278
  /* precalc bps */
  adder->bps = (adder->width / 8) * adder->channels;
279
280
281

  return TRUE;

282
  /* ERRORS */
283
284
not_supported:
  {
285
    GST_DEBUG_OBJECT (adder, "unsupported format set as caps");
286
287
    return FALSE;
  }
288
289
}

290
/* FIXME, the duration query should reflect how long you will produce
291
 * data, that is the amount of stream time until you will emit EOS.
292
293
294
295
 *
 * For synchronized mixing this is always the max of all the durations
 * of upstream since we emit EOS when all of them finished.
 *
296
297
298
 * We don't do synchronized mixing so this really depends on where the
 * streams where punched in and what their relative offsets are against
 * eachother which we can get from the first timestamps we see.
299
 *
300
301
 * When we add a new stream (or remove a stream) the duration might
 * also become invalid again and we need to post a new DURATION
302
 * message to notify this fact to the parent.
303
 * For now we take the max of all the upstream elements so the simple
304
 * cases work at least somewhat.
305
 */
306
307
308
309
310
311
static gboolean
gst_adder_query_duration (GstAdder * adder, GstQuery * query)
{
  gint64 max;
  gboolean res;
  GstFormat format;
312
313
314
315
316
  GstIterator *it;
  gboolean done;

  /* parse format */
  gst_query_parse_duration (query, &format, NULL);
317
318
319

  max = -1;
  res = TRUE;
320
  done = FALSE;
321

322
323
324
325
  it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (adder));
  while (!done) {
    GstIteratorResult ires;
    gpointer item;
326

327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
    ires = gst_iterator_next (it, &item);
    switch (ires) {
      case GST_ITERATOR_DONE:
        done = TRUE;
        break;
      case GST_ITERATOR_OK:
      {
        GstPad *pad = GST_PAD_CAST (item);
        gint64 duration;

        /* ask sink peer for duration */
        res &= gst_pad_query_peer_duration (pad, &format, &duration);
        /* take max from all valid return values */
        if (res) {
          /* valid unknown length, stop searching */
          if (duration == -1) {
            max = duration;
            done = TRUE;
          }
          /* else see if bigger than current max */
          else if (duration > max)
            max = duration;
        }
350
351
        break;
      }
352
353
354
355
356
357
358
359
      case GST_ITERATOR_RESYNC:
        max = -1;
        res = TRUE;
        break;
      default:
        res = FALSE;
        done = TRUE;
        break;
360
361
    }
  }
362
  gst_iterator_free (it);
363

364
365
366
367
  if (res) {
    /* and store the max */
    gst_query_set_duration (query, format, max);
  }
368
369
370
371

  return res;
}

372
373
374
375
376
377
378
379
380
381
382
static gboolean
gst_adder_query (GstPad * pad, GstQuery * query)
{
  GstAdder *adder = GST_ADDER (gst_pad_get_parent (pad));
  gboolean res = FALSE;

  switch (GST_QUERY_TYPE (query)) {
    case GST_QUERY_POSITION:
    {
      GstFormat format;

Wim Taymans's avatar
Wim Taymans committed
383
384
385
386
      gst_query_parse_position (query, &format, NULL);

      switch (format) {
        case GST_FORMAT_TIME:
387
388
          /* FIXME, bring to stream time, might be tricky */
          gst_query_set_position (query, format, adder->timestamp);
Wim Taymans's avatar
Wim Taymans committed
389
390
391
          res = TRUE;
          break;
        case GST_FORMAT_DEFAULT:
392
          gst_query_set_position (query, format, adder->offset);
Wim Taymans's avatar
Wim Taymans committed
393
394
395
396
          res = TRUE;
          break;
        default:
          break;
397
398
399
      }
      break;
    }
Wim Taymans's avatar
Wim Taymans committed
400
    case GST_QUERY_DURATION:
401
      res = gst_adder_query_duration (adder, query);
Wim Taymans's avatar
Wim Taymans committed
402
      break;
403
    default:
404
405
      /* FIXME, needs a custom query handler because we have multiple
       * sinkpads */
406
      res = gst_pad_query_default (pad, query);
407
408
409
410
411
412
413
      break;
  }

  gst_object_unref (adder);
  return res;
}

414
415
416
417
static gboolean
forward_event_func (GstPad * pad, GValue * ret, GstEvent * event)
{
  gst_event_ref (event);
418
  GST_LOG_OBJECT (pad, "About to send event %s", GST_EVENT_TYPE_NAME (event));
419
420
421
422
423
424
425
426
427
428
429
430
  if (!gst_pad_push_event (pad, event)) {
    g_value_set_boolean (ret, FALSE);
    GST_WARNING_OBJECT (pad, "Sending event  %p (%s) failed.",
        event, GST_EVENT_TYPE_NAME (event));
  } else {
    GST_LOG_OBJECT (pad, "Sent event  %p (%s).",
        event, GST_EVENT_TYPE_NAME (event));
  }
  gst_object_unref (pad);
  return TRUE;
}

431
/* forwards the event to all sinkpads, takes ownership of the
432
433
434
435
436
 * event
 *
 * Returns: TRUE if the event could be forwarded on all
 * sinkpads.
 */
437
static gboolean
438
forward_event (GstAdder * adder, GstEvent * event)
439
{
440
  gboolean ret;
441
442
  GstIterator *it;
  GValue vret = { 0 };
443

444
  GST_LOG_OBJECT (adder, "Forwarding event %p (%s)", event,
445
446
      GST_EVENT_TYPE_NAME (event));

447
448
  ret = TRUE;

449
450
451
452
453
454
  g_value_init (&vret, G_TYPE_BOOLEAN);
  g_value_set_boolean (&vret, TRUE);
  it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (adder));
  gst_iterator_fold (it, (GstIteratorFoldFunction) forward_event_func, &vret,
      event);
  gst_iterator_free (it);
455
456
  gst_event_unref (event);

457
458
  ret = g_value_get_boolean (&vret);

459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
  return ret;
}

static gboolean
gst_adder_src_event (GstPad * pad, GstEvent * event)
{
  GstAdder *adder;
  gboolean result;

  adder = GST_ADDER (gst_pad_get_parent (pad));

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_QOS:
      /* QoS might be tricky */
      result = FALSE;
      break;
    case GST_EVENT_SEEK:
476
477
    {
      GstSeekFlags flags;
478
      GstSeekType curtype;
479
      gint64 cur;
480

481
482
483
      /* parse the seek parameters */
      gst_event_parse_seek (event, &adder->segment_rate, NULL, &flags, &curtype,
          &cur, NULL, NULL);
484

485
486
487
488
      /* check if we are flushing */
      if (flags & GST_SEEK_FLAG_FLUSH) {
        /* make sure we accept nothing anymore and return WRONG_STATE */
        gst_collect_pads_set_flushing (adder->collect, TRUE);
489

490
491
492
493
        /* flushing seek, start flush downstream, the flush will be done
         * when all pads received a FLUSH_STOP. */
        gst_pad_push_event (adder->srcpad, gst_event_new_flush_start ());
      }
494

495
      /* now wait for the collected to be finished and mark a new
496
497
       * segment */
      GST_OBJECT_LOCK (adder->collect);
498
499
500
501
      if (curtype == GST_SEEK_TYPE_SET)
        adder->segment_position = cur;
      else
        adder->segment_position = 0;
502
503
504
      adder->segment_pending = TRUE;
      GST_OBJECT_UNLOCK (adder->collect);

505
506
      result = forward_event (adder, event);
      break;
507
    }
508
509
510
511
512
513
514
515
516
    case GST_EVENT_NAVIGATION:
      /* navigation is rather pointless. */
      result = FALSE;
      break;
    default:
      /* just forward the rest for now */
      result = forward_event (adder, event);
      break;
  }
517
  gst_object_unref (adder);
518

519
520
521
  return result;
}

522
523
524
525
526
527
528
529
530
531
532
533
534
535
static gboolean
gst_adder_sink_event (GstPad * pad, GstEvent * event)
{
  GstAdder *adder;
  gboolean ret;

  adder = GST_ADDER (gst_pad_get_parent (pad));

  GST_DEBUG ("Got %s event on pad %s:%s", GST_EVENT_TYPE_NAME (event),
      GST_DEBUG_PAD_NAME (pad));

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_FLUSH_STOP:
      /* mark a pending new segment. This event is synchronized
536
       * with the streaming thread so we can safely update the
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
       * variable without races. It's somewhat weird because we
       * assume the collectpads forwarded the FLUSH_STOP past us
       * and downstream (using our source pad, the bastard!).
       */
      adder->segment_pending = TRUE;
      break;
    default:
      break;
  }

  /* now GstCollectPads can take care of the rest, e.g. EOS */
  ret = adder->collect_event (pad, event);

  gst_object_unref (adder);
  return ret;
}

Andy Wingo's avatar
Andy Wingo committed
554
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
555
gst_adder_class_init (GstAdderClass * klass)
Andy Wingo's avatar
Andy Wingo committed
556
557
558
559
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

560
  gobject_class = (GObjectClass *) klass;
561

562
  gobject_class->finalize = gst_adder_finalize;
563

564
  gstelement_class = (GstElementClass *) klass;
565

566
  gst_element_class_add_pad_template (gstelement_class,
David Schleef's avatar
David Schleef committed
567
      gst_static_pad_template_get (&gst_adder_src_template));
568
  gst_element_class_add_pad_template (gstelement_class,
David Schleef's avatar
David Schleef committed
569
      gst_static_pad_template_get (&gst_adder_sink_template));
570
571
  gst_element_class_set_details (gstelement_class, &adder_details);

572
  parent_class = g_type_class_peek_parent (klass);
573

Andy Wingo's avatar
Andy Wingo committed
574
  gstelement_class->request_new_pad = gst_adder_request_new_pad;
575
  gstelement_class->release_pad = gst_adder_release_pad;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
576
  gstelement_class->change_state = gst_adder_change_state;
Andy Wingo's avatar
Andy Wingo committed
577
578
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
579
580
static void
gst_adder_init (GstAdder * adder)
Andy Wingo's avatar
Andy Wingo committed
581
{
Christophe Fergeau's avatar
Christophe Fergeau committed
582
583
584
585
586
  GstPadTemplate *template;

  template = gst_static_pad_template_get (&gst_adder_src_template);
  adder->srcpad = gst_pad_new_from_template (template, "src");
  gst_object_unref (template);
587
588
589
590
591
592
  gst_pad_set_getcaps_function (adder->srcpad,
      GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps));
  gst_pad_set_setcaps_function (adder->srcpad,
      GST_DEBUG_FUNCPTR (gst_adder_setcaps));
  gst_pad_set_query_function (adder->srcpad,
      GST_DEBUG_FUNCPTR (gst_adder_query));
593
594
  gst_pad_set_event_function (adder->srcpad,
      GST_DEBUG_FUNCPTR (gst_adder_src_event));
595
  gst_element_add_pad (GST_ELEMENT (adder), adder->srcpad);
Andy Wingo's avatar
Andy Wingo committed
596

597
  adder->format = GST_ADDER_FORMAT_UNSET;
598
  adder->padcount = 0;
599
  adder->func = NULL;
600

Andy Wingo's avatar
Andy Wingo committed
601
  /* keep track of the sinkpads requested */
602
  adder->collect = gst_collect_pads_new ();
603
604
  gst_collect_pads_set_function (adder->collect,
      GST_DEBUG_FUNCPTR (gst_adder_collected), adder);
Andy Wingo's avatar
Andy Wingo committed
605
606
}

607
static void
608
gst_adder_finalize (GObject * object)
609
610
611
612
613
{
  GstAdder *adder = GST_ADDER (object);

  gst_object_unref (adder->collect);
  adder->collect = NULL;
614
615

  G_OBJECT_CLASS (parent_class)->finalize (object);
616
617
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
618
619
620
static GstPad *
gst_adder_request_new_pad (GstElement * element, GstPadTemplate * templ,
    const gchar * unused)
Andy Wingo's avatar
Andy Wingo committed
621
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
622
623
  gchar *name;
  GstAdder *adder;
624
  GstPad *newpad;
625
  gint padcount;
Andy Wingo's avatar
Andy Wingo committed
626

627
628
  if (templ->direction != GST_PAD_SINK)
    goto not_sink;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
629

630
631
  adder = GST_ADDER (element);

632
633
634
635
  /* increment pad counter */
  padcount = g_atomic_int_exchange_and_add (&adder->padcount, 1);

  name = g_strdup_printf ("sink%d", padcount);
636
  newpad = gst_pad_new_from_template (templ, name);
637
  GST_DEBUG_OBJECT (adder, "request new pad %s", name);
638
  g_free (name);
Andy Wingo's avatar
Andy Wingo committed
639

640
  gst_pad_set_getcaps_function (newpad,
641
      GST_DEBUG_FUNCPTR (gst_adder_sink_getcaps));
642
  gst_pad_set_setcaps_function (newpad, GST_DEBUG_FUNCPTR (gst_adder_setcaps));
643
  gst_collect_pads_add_pad (adder->collect, newpad, sizeof (GstCollectData));
644

645
646
647
648
649
650
  /* FIXME: hacked way to override/extend the event function of
   * GstCollectPads; because it sets its own event function giving the
   * element no access to events */
  adder->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
  gst_pad_set_event_function (newpad, GST_DEBUG_FUNCPTR (gst_adder_sink_event));

651
  /* takes ownership of the pad */
652
653
  if (!gst_element_add_pad (GST_ELEMENT (adder), newpad))
    goto could_not_add;
654

655
  return newpad;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
656

657
658
659
660
661
662
663
664
  /* errors */
not_sink:
  {
    g_warning ("gstadder: request new pad that is not a SINK pad\n");
    return NULL;
  }
could_not_add:
  {
665
    GST_DEBUG_OBJECT (adder, "could not add pad");
666
    gst_collect_pads_remove_pad (adder->collect, newpad);
667
    gst_object_unref (newpad);
668
    return NULL;
Andy Wingo's avatar
Andy Wingo committed
669
670
671
  }
}

672
673
674
675
676
677
678
679
680
681
682
683
684
static void
gst_adder_release_pad (GstElement * element, GstPad * pad)
{
  GstAdder *adder;

  adder = GST_ADDER (element);

  GST_DEBUG_OBJECT (adder, "release pad %s:%s", GST_DEBUG_PAD_NAME (pad));

  gst_collect_pads_remove_pad (adder->collect, pad);
  gst_element_remove_pad (element, pad);
}

685
686
static GstFlowReturn
gst_adder_collected (GstCollectPads * pads, gpointer user_data)
Andy Wingo's avatar
Andy Wingo committed
687
688
689
690
{
  /*
   * combine channels by adding sample values
   * basic algorithm :
691
692
693
694
   * - this function is called when all pads have a buffer
   * - get available bytes on all pads.
   * - repeat for each input pad :
   *   - read available bytes, copy or add to target buffer
695
   *   - if there's an EOS event, remove the input channel
Andy Wingo's avatar
Andy Wingo committed
696
697
   * - push out the output buffer
   */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
698
  GstAdder *adder;
699
700
701
702
703
  guint size;
  GSList *collected;
  GstBuffer *outbuf;
  GstFlowReturn ret;
  gpointer outbytes;
Andy Wingo's avatar
Andy Wingo committed
704

705
  adder = GST_ADDER (user_data);
Andy Wingo's avatar
Andy Wingo committed
706

707
708
709
  /* this is fatal */
  if (G_UNLIKELY (adder->func == NULL))
    goto not_negotiated;
Andy Wingo's avatar
Andy Wingo committed
710

711
712
713
714
  /* get available bytes for reading, this can be 0 which could mean
   * empty buffers or EOS, which we will catch when we loop over the
   * pads. */
  size = gst_collect_pads_available (pads);
Wim Taymans's avatar
Wim Taymans committed
715

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
716
  GST_LOG_OBJECT (adder,
717
718
719
720
721
      "starting to cycle through channels, %d bytes available (bps = %d)", size,
      adder->bps);

  outbuf = NULL;
  outbytes = NULL;
722

723
724
725
726
  for (collected = pads->data; collected; collected = g_slist_next (collected)) {
    GstCollectData *data;
    guint8 *bytes;
    guint len;
Wim Taymans's avatar
Wim Taymans committed
727

728
    data = (GstCollectData *) collected->data;
Andy Wingo's avatar
Andy Wingo committed
729

730
    /* get pointer to copy size bytes */
731
    len = gst_collect_pads_read (pads, data, &bytes, size);
732
733
734
735
736
737
    /* length 0 means EOS or an empty buffer so we still need to flush in
     * case of an empty buffer. */
    if (len == 0) {
      GST_LOG_OBJECT (adder, "channel %p: no bytes available", data);
      goto next;
    }
Wim Taymans's avatar
Wim Taymans committed
738

739
    if (outbuf == NULL) {
740
741
742
743
744
      GST_LOG_OBJECT (adder, "channel %p: making output buffer of %d bytes",
          data, size);

      /* first buffer, alloc size bytes. FIXME, we can easily subbuffer
       * and _make_writable. */
745
746
      outbuf = gst_buffer_new_and_alloc (size);
      outbytes = GST_BUFFER_DATA (outbuf);
747
748
749
750
751
      gst_buffer_set_caps (outbuf, GST_PAD_CAPS (adder->srcpad));

      /* clear if we are only going to fill a partial buffer */
      if (G_UNLIKELY (size > len))
        memset (outbytes, 0, size);
752

753
754
      GST_LOG_OBJECT (adder, "channel %p: copying %d bytes from data %p",
          data, len, bytes);
755

756
757
      /* and copy the data into it */
      memcpy (outbytes, bytes, len);
Wim Taymans's avatar
Wim Taymans committed
758
    } else {
759
760
      GST_LOG_OBJECT (adder, "channel %p: mixing %d bytes from data %p",
          data, len, bytes);
761
762
      /* other buffers, need to add them */
      adder->func ((gpointer) outbytes, (gpointer) bytes, len);
Wim Taymans's avatar
Wim Taymans committed
763
    }
764
  next:
765
    gst_collect_pads_flush (pads, data, len);
766
  }
Andy Wingo's avatar
Andy Wingo committed
767

768
769
770
771
  /* can only happen when no pads to collect or all EOS */
  if (outbuf == NULL)
    goto eos;

772
  /* our timestamping is very simple, just an ever incrementing
773
774
   * counter, the new segment time will take care of their respective
   * stream time. */
775
776
777
  if (adder->segment_pending) {
    GstEvent *event;

778
    /* FIXME, use rate/applied_rate as set on all sinkpads.
779
     * - currently we just set rate as received from last seek-event
780
781
782
783
784
785
786
787
788
789
790
     * We could potentially figure out the duration as well using
     * the current segment positions and the stated stop positions.
     * Also we just start from stream time 0 which is rather
     * weird. For non-synchronized mixing, the time should be
     * the min of the stream times of all received segments,
     * rationale being that the duration is at least going to
     * be as long as the earliest stream we start mixing. This
     * would also be correct for synchronized mixing but then
     * the later streams would be delayed until the stream times
     * match.
     */
791
    event = gst_event_new_new_segment_full (FALSE, adder->segment_rate,
792
        1.0, GST_FORMAT_TIME, adder->timestamp, -1, adder->segment_position);
793
794
795

    gst_pad_push_event (adder->srcpad, event);
    adder->segment_pending = FALSE;
796
    adder->segment_position = 0;
797
798
  }

799
  /* set timestamps on the output buffer */
800
801
  GST_BUFFER_TIMESTAMP (outbuf) = adder->timestamp;
  GST_BUFFER_OFFSET (outbuf) = adder->offset;
802

803
804
805
806
807
  /* for the next timestamp, use the sample counter, which will
   * never accumulate rounding errors */
  adder->offset += size / adder->bps;
  adder->timestamp = gst_util_uint64_scale_int (adder->offset,
      GST_SECOND, adder->rate);
808

809
810
811
  /* now we can set the duration of the buffer */
  GST_BUFFER_DURATION (outbuf) = adder->timestamp -
      GST_BUFFER_TIMESTAMP (outbuf);
Wim Taymans's avatar
Wim Taymans committed
812
813

  /* send it out */
814
815
  GST_LOG_OBJECT (adder, "pushing outbuf, timestamp %" GST_TIME_FORMAT,
      GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)));
816
  ret = gst_pad_push (adder->srcpad, outbuf);
Wim Taymans's avatar
Wim Taymans committed
817

818
  return ret;
Wim Taymans's avatar
Wim Taymans committed
819

820
821
822
  /* ERRORS */
not_negotiated:
  {
823
824
    GST_ELEMENT_ERROR (adder, STREAM, FORMAT, (NULL),
        ("Unknown data received, not negotiated"));
825
826
    return GST_FLOW_NOT_NEGOTIATED;
  }
827
828
829
830
831
832
eos:
  {
    GST_DEBUG_OBJECT (adder, "no data available, must be EOS");
    gst_pad_push_event (adder->srcpad, gst_event_new_eos ());
    return GST_FLOW_UNEXPECTED;
  }
833
}
834

835
836
static GstStateChangeReturn
gst_adder_change_state (GstElement * element, GstStateChange transition)
Wim Taymans's avatar
Wim Taymans committed
837
838
{
  GstAdder *adder;
839
  GstStateChangeReturn ret;
840

Wim Taymans's avatar
Wim Taymans committed
841
  adder = GST_ADDER (element);
842

843
  switch (transition) {
844
    case GST_STATE_CHANGE_NULL_TO_READY:
Wim Taymans's avatar
Wim Taymans committed
845
      break;
846
    case GST_STATE_CHANGE_READY_TO_PAUSED:
Wim Taymans's avatar
Wim Taymans committed
847
848
      adder->timestamp = 0;
      adder->offset = 0;
849
      adder->segment_pending = TRUE;
850
      adder->segment_position = 0;
851
      adder->segment_rate = 1.0;
852
      gst_segment_init (&adder->segment, GST_FORMAT_UNDEFINED);
853
      gst_collect_pads_start (adder->collect);
Wim Taymans's avatar
Wim Taymans committed
854
      break;
855
    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
856
      break;
857
858
859
    case GST_STATE_CHANGE_PAUSED_TO_READY:
      /* need to unblock the collectpads before calling the
       * parent change_state so that streaming can finish */
860
      gst_collect_pads_stop (adder->collect);
861
      break;
862
863
864
865
866
867
868
    default:
      break;
  }

  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);

  switch (transition) {
Wim Taymans's avatar
Wim Taymans committed
869
870
871
    default:
      break;
  }
872

873
  return ret;
Andy Wingo's avatar
Andy Wingo committed
874
875
}

876

Andy Wingo's avatar
Andy Wingo committed
877
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
878
plugin_init (GstPlugin * plugin)
Andy Wingo's avatar
Andy Wingo committed
879
{
880
881
882
  if (!gst_element_register (plugin, "adder", GST_RANK_NONE, GST_TYPE_ADDER)) {
    return FALSE;
  }
883

Andy Wingo's avatar
Andy Wingo committed
884
885
886
  return TRUE;
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
887
888
889
890
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
    GST_VERSION_MINOR,
    "adder",
    "Adds multiple streams",
891
    plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)