gstadder.c 39.1 KB
Newer Older
Andy Wingo Wingo's avatar
Andy Wingo 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 Wingo's avatar
Andy Wingo 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
/**
 * SECTION:element-adder
 *
26
 * The adder allows to mix several streams into one by adding the data.
27
 * Mixed data is clamped to the min/max values of the data format.
28
29
30
31
32
 *
 * The adder currently mixes all data received on the sinkpads as soon as
 * possible without trying to synchronize the streams.
 *
 * <refsect2>
33
 * <title>Example launch line</title>
34
 * |[
35
 * gst-launch audiotestsrc freq=100 ! adder name=mix ! audioconvert ! alsasink audiotestsrc freq=500 ! mix.
36
 * ]| This pipeline produces two sine waves mixed together.
37
 * </refsect2>
38
39
 *
 * Last reviewed on 2006-05-09 (0.10.7)
40
 */
41
/* Element-Checklist-Version: 5 */
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
42

43
44
45
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
46
#include "gstadder.h"
47
#include <gst/audio/audio.h>
48
#include <string.h>             /* strcmp */
49
#include "gstadderorc.h"
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
50

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
51
/* highest positive/lowest negative x-bit value we can use for clamping */
52
53
54
55
56
57
58
59
60
61
62
63
64
#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 Wingo's avatar
Andy Wingo Wingo committed
65

66
67
68
69
70
71
enum
{
  PROP_0,
  PROP_FILTER_CAPS
};

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

75
/* elementfactory information */
76
77
78
79
80
81

#define CAPS \
  "audio/x-raw-int, " \
  "rate = (int) [ 1, MAX ], " \
  "channels = (int) [ 1, MAX ], " \
  "endianness = (int) BYTE_ORDER, " \
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
  "width = (int) 32, " \
  "depth = (int) 32, " \
  "signed = (boolean) { true, false } ;" \
  "audio/x-raw-int, " \
  "rate = (int) [ 1, MAX ], " \
  "channels = (int) [ 1, MAX ], " \
  "endianness = (int) BYTE_ORDER, " \
  "width = (int) 16, " \
  "depth = (int) 16, " \
  "signed = (boolean) { true, false } ;" \
  "audio/x-raw-int, " \
  "rate = (int) [ 1, MAX ], " \
  "channels = (int) [ 1, MAX ], " \
  "endianness = (int) BYTE_ORDER, " \
  "width = (int) 8, " \
  "depth = (int) 8, " \
98
99
100
101
102
103
  "signed = (boolean) { true, false } ;" \
  "audio/x-raw-float, " \
  "rate = (int) [ 1, MAX ], " \
  "channels = (int) [ 1, MAX ], " \
  "endianness = (int) BYTE_ORDER, " \
  "width = (int) { 32, 64 }"
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
104

David Schleef's avatar
David Schleef committed
105
static GstStaticPadTemplate gst_adder_src_template =
106
GST_STATIC_PAD_TEMPLATE ("src",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
107
108
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
109
    GST_STATIC_CAPS (CAPS)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
110
    );
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
111

David Schleef's avatar
David Schleef committed
112
static GstStaticPadTemplate gst_adder_sink_template =
113
GST_STATIC_PAD_TEMPLATE ("sink%d",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
114
115
    GST_PAD_SINK,
    GST_PAD_REQUEST,
116
    GST_STATIC_CAPS (CAPS)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
117
118
    );

Stefan Kost's avatar
Stefan Kost committed
119
120
GST_BOILERPLATE (GstAdder, gst_adder, GstElement, GST_TYPE_ELEMENT);

121
122
123
124
125
static void gst_adder_dispose (GObject * object);
static void gst_adder_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec);
static void gst_adder_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
126

127
128
129
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);
130
static gboolean gst_adder_sink_event (GstPad * pad, GstEvent * event);
131

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
132
133
static GstPad *gst_adder_request_new_pad (GstElement * element,
    GstPadTemplate * temp, const gchar * unused);
134
135
static void gst_adder_release_pad (GstElement * element, GstPad * pad);

136
137
static GstStateChangeReturn gst_adder_change_state (GstElement * element,
    GstStateChange transition);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
138

139
140
static GstBuffer *gst_adder_do_clip (GstCollectPads * pads,
    GstCollectData * data, GstBuffer * buffer, gpointer user_data);
141
142
static GstFlowReturn gst_adder_collected (GstCollectPads * pads,
    gpointer user_data);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
143

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

152
/* *INDENT-OFF* */
153
MAKE_FUNC_NC (add_float64, gdouble)
154
155
/* *INDENT-ON* */

156
157
158
/* we can only accept caps that we and downstream can handle.
 * if we have filtercaps set, use those to constrain the target caps.
 */
159
160
161
162
static GstCaps *
gst_adder_sink_getcaps (GstPad * pad)
{
  GstAdder *adder;
163
  GstCaps *result, *peercaps, *sinkcaps, *filter_caps;
164
165
166
167

  adder = GST_ADDER (GST_PAD_PARENT (pad));

  GST_OBJECT_LOCK (adder);
168
169
170
171
172
  /* take filter */
  if ((filter_caps = adder->filter_caps))
    gst_caps_ref (filter_caps);
  GST_OBJECT_UNLOCK (adder);

173
174
  /* get the downstream possible caps */
  peercaps = gst_pad_peer_get_caps (adder->srcpad);
175

176
177
178
179
  /* 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) {
180
    /* restrict with filter-caps if any */
181
    if (filter_caps) {
182
      GST_DEBUG_OBJECT (adder, "filtering peer caps");
183
      result = gst_caps_intersect (peercaps, filter_caps);
184
185
186
      gst_caps_unref (peercaps);
      peercaps = result;
    }
187
188
189
190
191
192
193
194
    /* 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. */
195
    /* restrict with filter-caps if any */
196
    if (filter_caps) {
197
      GST_DEBUG_OBJECT (adder, "no peer caps, using filtered sinkcaps");
198
      result = gst_caps_intersect (sinkcaps, filter_caps);
199
200
201
202
203
      gst_caps_unref (sinkcaps);
    } else {
      GST_DEBUG_OBJECT (adder, "no peer caps, using sinkcaps");
      result = sinkcaps;
    }
204
  }
205
206
207

  if (filter_caps)
    gst_caps_unref (filter_caps);
208

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

212
213
214
215
216
  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.
217
 */
218
219
static gboolean
gst_adder_setcaps (GstPad * pad, GstCaps * caps)
220
221
{
  GstAdder *adder;
222
  GList *pads;
223
  GstStructure *structure;
224
  const char *media_type;
225

226
  adder = GST_ADDER (GST_PAD_PARENT (pad));
227

228
229
230
  GST_LOG_OBJECT (adder, "setting caps on pad %p,%s to %" GST_PTR_FORMAT, pad,
      GST_PAD_NAME (pad), caps);

231
232
  /* FIXME, see if the other pads can accept the format. Also lock the
   * format on the other pads to this new format. */
233
  GST_OBJECT_LOCK (adder);
234
  pads = GST_ELEMENT (adder)->pads;
235
236
237
238
  while (pads) {
    GstPad *otherpad = GST_PAD (pads->data);

    if (otherpad != pad) {
239
      gst_caps_replace (&GST_PAD_CAPS (otherpad), caps);
240
    }
241
242
    pads = g_list_next (pads);
  }
243
  GST_OBJECT_UNLOCK (adder);
244

245
  /* parse caps now */
246
247
248
  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
249
250
251
252
253
    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);
254

255
    GST_INFO_OBJECT (pad, "parse_caps sets adder to format int, %d bit",
256
257
        adder->width);

258
259
260
261
262
263
264
    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);
265
        adder->sample_size = 1;
266
267
268
269
        break;
      case 16:
        adder->func = (adder->is_signed ?
            (GstAdderFunction) add_int16 : (GstAdderFunction) add_uint16);
270
        adder->sample_size = 2;
271
272
273
274
        break;
      case 32:
        adder->func = (adder->is_signed ?
            (GstAdderFunction) add_int32 : (GstAdderFunction) add_uint32);
275
        adder->sample_size = 4;
276
277
278
279
        break;
      default:
        goto not_supported;
    }
280
  } else if (strcmp (media_type, "audio/x-raw-float") == 0) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
281
282
    adder->format = GST_ADDER_FORMAT_FLOAT;
    gst_structure_get_int (structure, "width", &adder->width);
283
284
    gst_structure_get_int (structure, "endianness", &adder->endianness);

285
    GST_INFO_OBJECT (pad, "parse_caps sets adder to format float, %d bit",
286
287
        adder->width);

288
289
    if (adder->endianness != G_BYTE_ORDER)
      goto not_supported;
290
291
292
293

    switch (adder->width) {
      case 32:
        adder->func = (GstAdderFunction) add_float32;
294
        adder->sample_size = 4;
295
296
297
        break;
      case 64:
        adder->func = (GstAdderFunction) add_float64;
298
        adder->sample_size = 8;
299
300
301
302
303
304
        break;
      default:
        goto not_supported;
    }
  } else {
    goto not_supported;
305
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
306

307
308
  gst_structure_get_int (structure, "channels", &adder->channels);
  gst_structure_get_int (structure, "rate", &adder->rate);
309
310
  /* precalc bps */
  adder->bps = (adder->width / 8) * adder->channels;
311
312
313

  return TRUE;

314
  /* ERRORS */
315
316
not_supported:
  {
317
    GST_DEBUG_OBJECT (adder, "unsupported format set as caps");
318
319
    return FALSE;
  }
320
321
}

322
/* FIXME, the duration query should reflect how long you will produce
323
 * data, that is the amount of stream time until you will emit EOS.
324
325
326
327
 *
 * For synchronized mixing this is always the max of all the durations
 * of upstream since we emit EOS when all of them finished.
 *
328
329
330
 * 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.
331
 *
332
333
 * 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
334
 * message to notify this fact to the parent.
335
 * For now we take the max of all the upstream elements so the simple
336
 * cases work at least somewhat.
337
 */
338
339
340
341
342
343
static gboolean
gst_adder_query_duration (GstAdder * adder, GstQuery * query)
{
  gint64 max;
  gboolean res;
  GstFormat format;
344
345
346
347
348
  GstIterator *it;
  gboolean done;

  /* parse format */
  gst_query_parse_duration (query, &format, NULL);
349
350
351

  max = -1;
  res = TRUE;
352
  done = FALSE;
353

354
355
356
  it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (adder));
  while (!done) {
    GstIteratorResult ires;
357

358
    gpointer item;
359

360
361
362
363
364
365
366
367
    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);
368

369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
        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;
        }
384
        gst_object_unref (pad);
385
386
        break;
      }
387
388
389
      case GST_ITERATOR_RESYNC:
        max = -1;
        res = TRUE;
390
        gst_iterator_resync (it);
391
392
393
394
395
        break;
      default:
        res = FALSE;
        done = TRUE;
        break;
396
397
    }
  }
398
  gst_iterator_free (it);
399

400
401
  if (res) {
    /* and store the max */
402
403
    GST_DEBUG_OBJECT (adder, "Total duration in format %s: %"
        GST_TIME_FORMAT, gst_format_get_name (format), GST_TIME_ARGS (max));
404
405
    gst_query_set_duration (query, format, max);
  }
406
407
408
409

  return res;
}

410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
static gboolean
gst_adder_query_latency (GstAdder * adder, GstQuery * query)
{
  GstClockTime min, max;
  gboolean live;
  gboolean res;
  GstIterator *it;
  gboolean done;

  res = TRUE;
  done = FALSE;

  live = FALSE;
  min = 0;
  max = GST_CLOCK_TIME_NONE;

  /* Take maximum of all latency values */
  it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (adder));
  while (!done) {
    GstIteratorResult ires;
430

431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
    gpointer item;

    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);
        GstQuery *peerquery;
        GstClockTime min_cur, max_cur;
        gboolean live_cur;

        peerquery = gst_query_new_latency ();

        /* Ask peer for latency */
        res &= gst_pad_peer_query (pad, peerquery);

        /* take max from all valid return values */
        if (res) {
          gst_query_parse_latency (peerquery, &live_cur, &min_cur, &max_cur);

          if (min_cur > min)
            min = min_cur;

          if (max_cur != GST_CLOCK_TIME_NONE &&
              ((max != GST_CLOCK_TIME_NONE && max_cur > max) ||
                  (max == GST_CLOCK_TIME_NONE)))
            max = max_cur;

          live = live || live_cur;
        }

        gst_query_unref (peerquery);
466
        gst_object_unref (pad);
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
        break;
      }
      case GST_ITERATOR_RESYNC:
        live = FALSE;
        min = 0;
        max = GST_CLOCK_TIME_NONE;
        res = TRUE;
        gst_iterator_resync (it);
        break;
      default:
        res = FALSE;
        done = TRUE;
        break;
    }
  }
  gst_iterator_free (it);

  if (res) {
    /* store the results */
    GST_DEBUG_OBJECT (adder, "Calculated total latency: live %s, min %"
        GST_TIME_FORMAT ", max %" GST_TIME_FORMAT,
        (live ? "yes" : "no"), GST_TIME_ARGS (min), GST_TIME_ARGS (max));
    gst_query_set_latency (query, live, min, max);
  }

  return res;
}

495
496
497
498
499
500
501
502
503
504
505
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
506
507
508
509
      gst_query_parse_position (query, &format, NULL);

      switch (format) {
        case GST_FORMAT_TIME:
510
511
          /* FIXME, bring to stream time, might be tricky */
          gst_query_set_position (query, format, adder->timestamp);
Wim Taymans's avatar
Wim Taymans committed
512
513
514
          res = TRUE;
          break;
        case GST_FORMAT_DEFAULT:
515
          gst_query_set_position (query, format, adder->offset);
Wim Taymans's avatar
Wim Taymans committed
516
517
518
519
          res = TRUE;
          break;
        default:
          break;
520
521
522
      }
      break;
    }
Wim Taymans's avatar
Wim Taymans committed
523
    case GST_QUERY_DURATION:
524
      res = gst_adder_query_duration (adder, query);
Wim Taymans's avatar
Wim Taymans committed
525
      break;
526
527
528
    case GST_QUERY_LATENCY:
      res = gst_adder_query_latency (adder, query);
      break;
529
    default:
530
531
      /* FIXME, needs a custom query handler because we have multiple
       * sinkpads */
532
      res = gst_pad_query_default (pad, query);
533
534
535
536
537
538
539
      break;
  }

  gst_object_unref (adder);
  return res;
}

540
541
542
543
544
545
typedef struct
{
  GstEvent *event;
  gboolean flush;
} EventData;

546
static gboolean
547
forward_event_func (GstPad * pad, GValue * ret, EventData * data)
548
{
549
550
  GstEvent *event = data->event;

551
  gst_event_ref (event);
552
  GST_LOG_OBJECT (pad, "About to send event %s", GST_EVENT_TYPE_NAME (event));
553
554
555
  if (!gst_pad_push_event (pad, event)) {
    GST_WARNING_OBJECT (pad, "Sending event  %p (%s) failed.",
        event, GST_EVENT_TYPE_NAME (event));
556
557
558
559
    /* quick hack to unflush the pads, ideally we need a way to just unflush
     * this single collect pad */
    if (data->flush)
      gst_pad_send_event (pad, gst_event_new_flush_stop ());
560
  } else {
561
    g_value_set_boolean (ret, TRUE);
562
563
564
565
    GST_LOG_OBJECT (pad, "Sent event  %p (%s).",
        event, GST_EVENT_TYPE_NAME (event));
  }
  gst_object_unref (pad);
566

567
  /* continue on other pads, even if one failed */
568
569
570
  return TRUE;
}

571
/* forwards the event to all sinkpads, takes ownership of the
572
573
574
575
576
 * event
 *
 * Returns: TRUE if the event could be forwarded on all
 * sinkpads.
 */
577
static gboolean
578
forward_event (GstAdder * adder, GstEvent * event, gboolean flush)
579
{
580
  gboolean ret;
581
  GstIterator *it;
582
  GstIteratorResult ires;
583
  GValue vret = { 0 };
584
  EventData data;
585

586
  GST_LOG_OBJECT (adder, "Forwarding event %p (%s)", event,
587
588
      GST_EVENT_TYPE_NAME (event));

589
590
  data.event = event;
  data.flush = flush;
591

592
  g_value_init (&vret, G_TYPE_BOOLEAN);
593
  g_value_set_boolean (&vret, FALSE);
594
  it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (adder));
595
596
  while (TRUE) {
    ires = gst_iterator_fold (it, (GstIteratorFoldFunction) forward_event_func,
597
        &vret, &data);
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
    switch (ires) {
      case GST_ITERATOR_RESYNC:
        GST_WARNING ("resync");
        gst_iterator_resync (it);
        g_value_set_boolean (&vret, TRUE);
        break;
      case GST_ITERATOR_OK:
      case GST_ITERATOR_DONE:
        ret = g_value_get_boolean (&vret);
        goto done;
      default:
        ret = FALSE;
        goto done;
    }
  }
done:
614
  gst_iterator_free (it);
615
616
  GST_LOG_OBJECT (adder, "Forwarded event %p (%s), ret=%d", event,
      GST_EVENT_TYPE_NAME (event), ret);
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
  gst_event_unref (event);

  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_SEEK:
632
633
    {
      GstSeekFlags flags;
Stefan Kost's avatar
Stefan Kost committed
634
635
      GstSeekType curtype, endtype;
      gint64 cur, end;
Wim Taymans's avatar
Wim Taymans committed
636
      gboolean flush;
637

638
639
      /* parse the seek parameters */
      gst_event_parse_seek (event, &adder->segment_rate, NULL, &flags, &curtype,
Stefan Kost's avatar
Stefan Kost committed
640
          &cur, &endtype, &end);
641

642
643
644
645
646
647
648
649
650
651
652
653
654
      if ((curtype != GST_SEEK_TYPE_NONE) && (curtype != GST_SEEK_TYPE_SET)) {
        result = FALSE;
        GST_DEBUG_OBJECT (adder,
            "seeking failed, unhandled seek type for start: %d", curtype);
        goto done;
      }
      if ((endtype != GST_SEEK_TYPE_NONE) && (endtype != GST_SEEK_TYPE_SET)) {
        result = FALSE;
        GST_DEBUG_OBJECT (adder,
            "seeking failed, unhandled seek type for end: %d", endtype);
        goto done;
      }

Wim Taymans's avatar
Wim Taymans committed
655
656
      flush = (flags & GST_SEEK_FLAG_FLUSH) == GST_SEEK_FLAG_FLUSH;

657
      /* check if we are flushing */
Wim Taymans's avatar
Wim Taymans committed
658
      if (flush) {
659
660
        /* make sure we accept nothing anymore and return WRONG_STATE */
        gst_collect_pads_set_flushing (adder->collect, TRUE);
661

662
663
664
        /* 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 ());
665
666
667
668
669
670
671
672

        /* We can't send FLUSH_STOP here since upstream could start pushing data
         * after we unlock adder->collect.
         * We set flush_stop_pending to TRUE instead and send FLUSH_STOP after
         * forwarding the seek upstream or from gst_adder_collected,
         * whichever happens first.
         */
        adder->flush_stop_pending = TRUE;
673
      }
674
      GST_DEBUG_OBJECT (adder, "handling seek event: %" GST_PTR_FORMAT, event);
Wim Taymans's avatar
Wim Taymans committed
675

676
      /* now wait for the collected to be finished and mark a new
Wim Taymans's avatar
Wim Taymans committed
677
678
       * segment. After we have the lock, no collect function is running and no
       * new collect function will be called for as long as we're flushing. */
679
      GST_OBJECT_LOCK (adder->collect);
680
      if (curtype == GST_SEEK_TYPE_SET)
Stefan Kost's avatar
Stefan Kost committed
681
        adder->segment_start = cur;
682
      else
Stefan Kost's avatar
Stefan Kost committed
683
684
685
686
687
        adder->segment_start = 0;
      if (endtype == GST_SEEK_TYPE_SET)
        adder->segment_end = end;
      else
        adder->segment_end = GST_CLOCK_TIME_NONE;
688
689
690
      /* make sure we push a new segment, to inform about new basetime
       * see FIXME in gst_adder_collected() */
      adder->segment_pending = TRUE;
691
692
693
694
695
      if (flush) {
        /* Yes, we need to call _set_flushing again *WHEN* the streaming threads
         * have stopped so that the cookie gets properly updated. */
        gst_collect_pads_set_flushing (adder->collect, TRUE);
      }
696
      GST_OBJECT_UNLOCK (adder->collect);
697
698
      GST_DEBUG_OBJECT (adder, "forwarding seek event: %" GST_PTR_FORMAT,
          event);
699

700
      result = forward_event (adder, event, flush);
701
      if (!result) {
Wim Taymans's avatar
Wim Taymans committed
702
703
        /* seek failed. maybe source is a live source. */
        GST_DEBUG_OBJECT (adder, "seeking failed");
704
      }
705
706
707
708
709
      if (g_atomic_int_compare_and_exchange (&adder->flush_stop_pending,
              TRUE, FALSE)) {
        GST_DEBUG_OBJECT (adder, "pending flush stop");
        gst_pad_push_event (adder->srcpad, gst_event_new_flush_stop ());
      }
710
      break;
711
    }
712
713
714
715
    case GST_EVENT_QOS:
      /* QoS might be tricky */
      result = FALSE;
      break;
716
717
718
719
720
721
    case GST_EVENT_NAVIGATION:
      /* navigation is rather pointless. */
      result = FALSE;
      break;
    default:
      /* just forward the rest for now */
722
723
      GST_DEBUG_OBJECT (adder, "forward unhandled event: %s",
          GST_EVENT_TYPE_NAME (event));
724
      result = forward_event (adder, event, FALSE);
725
726
      break;
  }
727
728

done:
729
  gst_object_unref (adder);
730

731
732
733
  return result;
}

734
735
736
737
static gboolean
gst_adder_sink_event (GstPad * pad, GstEvent * event)
{
  GstAdder *adder;
738
  gboolean ret = TRUE;
739
740
741
742
743
744
745
746

  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:
Wim Taymans's avatar
Wim Taymans committed
747
748
      /* we received a flush-stop. The collect_event function will push the
       * event past our element. We simply forward all flush-stop events, even
749
       * when no flush-stop was pending, this is required because collectpads
Wim Taymans's avatar
Wim Taymans committed
750
751
752
       * does not provide an API to handle-but-not-forward the flush-stop.
       * We unset the pending flush-stop flag so that we don't send anymore
       * flush-stop from the collect function later.
753
       */
Wim Taymans's avatar
Wim Taymans committed
754
      GST_OBJECT_LOCK (adder->collect);
755
      adder->segment_pending = TRUE;
756
      adder->flush_stop_pending = FALSE;
757
758
759
760
761
762
      /* Clear pending tags */
      if (adder->pending_events) {
        g_list_foreach (adder->pending_events, (GFunc) gst_event_unref, NULL);
        g_list_free (adder->pending_events);
        adder->pending_events = NULL;
      }
Wim Taymans's avatar
Wim Taymans committed
763
      GST_OBJECT_UNLOCK (adder->collect);
764
      break;
765
766
    case GST_EVENT_TAG:
      GST_OBJECT_LOCK (adder->collect);
Wim Taymans's avatar
Wim Taymans committed
767
      /* collect tags here so we can push them out when we collect data */
768
769
770
      adder->pending_events = g_list_append (adder->pending_events, event);
      GST_OBJECT_UNLOCK (adder->collect);
      goto beach;
771
772
773
774
775
776
777
    default:
      break;
  }

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

778
beach:
779
780
781
782
  gst_object_unref (adder);
  return ret;
}

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
783
static void
Stefan Kost's avatar
Stefan Kost committed
784
gst_adder_base_init (gpointer g_class)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
785
{
Stefan Kost's avatar
Stefan Kost committed
786
  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
787

788
  gst_element_class_add_pad_template (gstelement_class,
David Schleef's avatar
David Schleef committed
789
      gst_static_pad_template_get (&gst_adder_src_template));
790
  gst_element_class_add_pad_template (gstelement_class,
David Schleef's avatar
David Schleef committed
791
      gst_static_pad_template_get (&gst_adder_sink_template));
792
793
  gst_element_class_set_details_simple (gstelement_class, "Adder",
      "Generic/Audio",
794
795
      "Add N audio channels together",
      "Thomas Vander Stichele <thomas at apestaart dot org>");
Stefan Kost's avatar
Stefan Kost committed
796
797
798
799
800
801
802
}

static void
gst_adder_class_init (GstAdderClass * klass)
{
  GObjectClass *gobject_class = (GObjectClass *) klass;
  GstElementClass *gstelement_class = (GstElementClass *) klass;
803

Stefan Kost's avatar
Stefan Kost committed
804
805
806
  gobject_class->set_property = gst_adder_set_property;
  gobject_class->get_property = gst_adder_get_property;
  gobject_class->dispose = gst_adder_dispose;
807

Stefan Kost's avatar
Stefan Kost committed
808
809
810
811
812
  /**
   * GstAdder:caps:
   *
   * Since: 0.10.24
   */
813
  g_object_class_install_property (gobject_class, PROP_FILTER_CAPS,
Stefan Kost's avatar
Stefan Kost committed
814
815
      g_param_spec_boxed ("caps", "Target caps",
          "Set target format for mixing (NULL means ANY). "
816
817
818
819
          "Setting this property takes a reference to the supplied GstCaps "
          "object.", GST_TYPE_CAPS,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

820
821
822
823
  gstelement_class->request_new_pad =
      GST_DEBUG_FUNCPTR (gst_adder_request_new_pad);
  gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_adder_release_pad);
  gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_adder_change_state);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
824
825
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
826
static void
Stefan Kost's avatar
Stefan Kost committed
827
gst_adder_init (GstAdder * adder, GstAdderClass * klass)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
828
{
Christophe Fergeau's avatar
Christophe Fergeau committed
829
830
831
832
833
  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);
834

835
836
837
838
839
840
  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));
841
842
  gst_pad_set_event_function (adder->srcpad,
      GST_DEBUG_FUNCPTR (gst_adder_src_event));
843
  gst_element_add_pad (GST_ELEMENT (adder), adder->srcpad);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
844

845
  adder->format = GST_ADDER_FORMAT_UNSET;
846
  adder->padcount = 0;
847
  adder->func = NULL;
848

849
  adder->filter_caps = NULL;
850

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
851
  /* keep track of the sinkpads requested */
852
  adder->collect = gst_collect_pads_new ();
853
854
  gst_collect_pads_set_function (adder->collect,
      GST_DEBUG_FUNCPTR (gst_adder_collected), adder);
855
856
  gst_collect_pads_set_clip_function (adder->collect,
      GST_DEBUG_FUNCPTR (gst_adder_do_clip), adder);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
857
858
}

859
static void
860
gst_adder_dispose (GObject * object)
861
862
863
{
  GstAdder *adder = GST_ADDER (object);

864
865
866
867
868
  if (adder->collect) {
    gst_object_unref (adder->collect);
    adder->collect = NULL;
  }
  gst_caps_replace (&adder->filter_caps, NULL);
869
870
871
872
873
  if (adder->pending_events) {
    g_list_foreach (adder->pending_events, (GFunc) gst_event_unref, NULL);
    g_list_free (adder->pending_events);
    adder->pending_events = NULL;
  }
874

875
  G_OBJECT_CLASS (parent_class)->dispose (object);
876
877
}

878
879
880
881
882
883
884
885
static void
gst_adder_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec)
{
  GstAdder *adder = GST_ADDER (object);

  switch (prop_id) {
    case PROP_FILTER_CAPS:{
886
      GstCaps *new_caps = NULL;
887
888
889
      GstCaps *old_caps;
      const GstCaps *new_caps_val = gst_value_get_caps (value);

890
      if (new_caps_val != NULL) {
891
892
893
894
895
896
897
898
899
        new_caps = (GstCaps *) new_caps_val;
        gst_caps_ref (new_caps);
      }

      GST_OBJECT_LOCK (adder);
      old_caps = adder->filter_caps;
      adder->filter_caps = new_caps;
      GST_OBJECT_UNLOCK (adder);

900
901
      if (old_caps)
        gst_caps_unref (old_caps);
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930

      GST_DEBUG_OBJECT (adder, "set new caps %" GST_PTR_FORMAT, new_caps);
      break;
    }
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
gst_adder_get_property (GObject * object, guint prop_id, GValue * value,
    GParamSpec * pspec)
{
  GstAdder *adder = GST_ADDER (object);

  switch (prop_id) {
    case PROP_FILTER_CAPS:
      GST_OBJECT_LOCK (adder);
      gst_value_set_caps (value, adder->filter_caps);
      GST_OBJECT_UNLOCK (adder);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}


Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
931
932
933
static GstPad *
gst_adder_request_new_pad (GstElement * element, GstPadTemplate * templ,
    const gchar * unused)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
934
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
935
936
  gchar *name;
  GstAdder *adder;
937
  GstPad *newpad;
938
  gint padcount;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
939

940
941
  if (templ->direction != GST_PAD_SINK)
    goto not_sink;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
942

943
944
  adder = GST_ADDER (element);

945
946
947
948
  /* increment pad counter */
  padcount = g_atomic_int_exchange_and_add (&adder->padcount, 1);

  name = g_strdup_printf ("sink%d", padcount);
949
  newpad = gst_pad_new_from_template (templ, name);
950
  GST_DEBUG_OBJECT (adder, "request new pad %s", name);
951
  g_free (name);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
952

953
  gst_pad_set_getcaps_function (newpad,
954
      GST_DEBUG_FUNCPTR (gst_adder_sink_getcaps));
955
  gst_pad_set_setcaps_function (newpad, GST_DEBUG_FUNCPTR (gst_adder_setcaps));
956
  gst_collect_pads_add_pad (adder->collect, newpad, sizeof (GstCollectData));
957

958
959
960
961
962
963
  /* 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));

964
  /* takes ownership of the pad */
965
966
  if (!gst_element_add_pad (GST_ELEMENT (adder), newpad))
    goto could_not_add;
967

968
  return newpad;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
969

970
971
972
973
974
975
976
977
  /* errors */
not_sink:
  {
    g_warning ("gstadder: request new pad that is not a SINK pad\n");
    return NULL;
  }
could_not_add:
  {
978
    GST_DEBUG_OBJECT (adder, "could not add pad");
979
    gst_collect_pads_remove_pad (adder->collect, newpad);
980
    gst_object_unref (newpad);
981
    return NULL;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
982
983
984
  }
}

985
986
987
988
989
990
991
992
993
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));

994
  gst_collect_pads_remove_pad (adder->collect, pad);
995
996
997
  gst_element_remove_pad (element, pad);
}

998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
static GstBuffer *
gst_adder_do_clip (GstCollectPads * pads, GstCollectData * data,
    GstBuffer * buffer, gpointer user_data)
{
  GstAdder *adder = GST_ADDER (user_data);

  buffer = gst_audio_buffer_clip (buffer, &data->segment, adder->rate,
      adder->bps);

  return buffer;
}

1010
1011
static GstFlowReturn
gst_adder_collected (GstCollectPads * pads, gpointer user_data)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
1012
1013
{
  /*
1014
   * combine streams by adding data values
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
1015
   * basic algorithm :
1016
1017
1018
1019
   * - 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
1020
   *   - if there's an EOS event, remove the input channel
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
1021
   * - push out the output buffer
1022
1023
1024
1025
1026
1027
   *
   * todo:
   * - would be nice to have a mixing mode, where instead of adding we mix
   *   - for float we could downscale after collect loop
   *   - for int we need to downscale each input to avoid clipping or
   *     mix into a temp (float) buffer and scale afterwards as well
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
1028
   */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1029
  GstAdder *adder;
1030
  GSList *collected, *next = NULL;
1031
  GstFlowReturn ret;
1032
  GstBuffer *outbuf = NULL, *gapbuf = NULL;
1033
1034
  gpointer outdata = NULL;
  guint outsize;
Stefan Kost's avatar
Stefan Kost committed
1035
1036
  gint64 next_offset;
  gint64 next_timestamp;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
1037

1038
  adder = GST_ADDER (user_data);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
1039

1040
1041
1042
  /* this is fatal */
  if (G_UNLIKELY (adder->func == NULL))
    goto not_negotiated;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
1043

1044
1045
1046
  if (g_atomic_int_compare_and_exchange (&adder->flush_stop_pending,
          TRUE, FALSE)) {
    GST_DEBUG_OBJECT (adder, "pending flush stop");
Wim Taymans's avatar
Wim Taymans committed
1047
1048
1049
    gst_pad_push_event (adder->srcpad, gst_event_new_flush_stop ());
  }

1050
1051
1052
  /* 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. */
  outsize = gst_collect_pads_available (pads);
1053
1054
1055
  /* can only happen when no pads to collect or all EOS */
  if (outsize == 0)
    goto eos;
Wim Taymans's avatar
Wim Taymans committed
1056

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1057
  GST_LOG_OBJECT (adder,
1058
1059
      "starting to cycle through channels, %d bytes available (bps = %d)",
      outsize, adder->bps);
1060

1061
  for (collected = pads->data; collected; collected = next) {
1062
    GstCollectData *collect_data;
1063
    GstBuffer *inbuf;
1064
1065
1066
1067
    gboolean is_gap;

    /* take next to see if this is the last collectdata */
    next = g_slist_next (collected);
Wim Taymans's avatar
Wim Taymans committed
1068

1069
    collect_data = (GstCollectData *) collected->data;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
1070

1071
1072
    /* get a buffer of size bytes, if we get a buffer, it is at least outsize
     * bytes big. */
1073
    inbuf = gst_collect_pads_take_buffer (pads, collect_data, outsize);
1074
    /* NULL means EOS or an empty buffer so we still need to flush in
1075
     * case of an empty buffer. */
1076
    if (inbuf == NULL) {