gstadder.c 49.7 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
 *
 * 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
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
20 21
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
Andy Wingo's avatar
Andy Wingo committed
22
 */
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
 *
 * The adder currently mixes all data received on the sinkpads as soon as
 * possible without trying to synchronize the streams.
 *
32 33 34 35
 * Check out the audiomixer element in gst-plugins-bad for a better-behaving
 * audio mixing element: It will sync input streams correctly and also handle
 * live inputs properly.
 *
36
 * <refsect2>
37
 * <title>Example launch line</title>
38
 * |[
39
 * gst-launch-1.0 audiotestsrc freq=100 ! adder name=mix ! audioconvert ! autoaudiosink audiotestsrc freq=500 ! mix.
40
 * ]| This pipeline produces two sine waves mixed together.
41 42
 * </refsect2>
 */
43
/* Element-Checklist-Version: 5 */
Andy Wingo's avatar
Andy Wingo committed
44

45 46 47
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
48

Andy Wingo's avatar
Andy Wingo committed
49
#include "gstadder.h"
50
#include <gst/audio/audio.h>
51
#include <string.h>             /* strcmp */
52
#include "gstadderorc.h"
Andy Wingo's avatar
Andy Wingo committed
53

54 55 56
#define GST_CAT_DEFAULT gst_adder_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);

57 58 59
#define DEFAULT_PAD_VOLUME (1.0)
#define DEFAULT_PAD_MUTE (FALSE)

60 61 62 63 64 65 66 67 68 69 70 71 72
/* some defines for audio processing */
/* the volume factor is a range from 0.0 to (arbitrary) VOLUME_MAX_DOUBLE = 10.0
 * we map 1.0 to VOLUME_UNITY_INT*
 */
#define VOLUME_UNITY_INT8            8  /* internal int for unity 2^(8-5) */
#define VOLUME_UNITY_INT8_BIT_SHIFT  3  /* number of bits to shift for unity */
#define VOLUME_UNITY_INT16           2048       /* internal int for unity 2^(16-5) */
#define VOLUME_UNITY_INT16_BIT_SHIFT 11 /* number of bits to shift for unity */
#define VOLUME_UNITY_INT24           524288     /* internal int for unity 2^(24-5) */
#define VOLUME_UNITY_INT24_BIT_SHIFT 19 /* number of bits to shift for unity */
#define VOLUME_UNITY_INT32           134217728  /* internal int for unity 2^(32-5) */
#define VOLUME_UNITY_INT32_BIT_SHIFT 27

73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
enum
{
  PROP_PAD_0,
  PROP_PAD_VOLUME,
  PROP_PAD_MUTE
};

G_DEFINE_TYPE (GstAdderPad, gst_adder_pad, GST_TYPE_PAD);

static void
gst_adder_pad_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec)
{
  GstAdderPad *pad = GST_ADDER_PAD (object);

  switch (prop_id) {
    case PROP_PAD_VOLUME:
      g_value_set_double (value, pad->volume);
      break;
    case PROP_PAD_MUTE:
      g_value_set_boolean (value, pad->mute);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
gst_adder_pad_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec)
{
  GstAdderPad *pad = GST_ADDER_PAD (object);

  switch (prop_id) {
    case PROP_PAD_VOLUME:
      GST_OBJECT_LOCK (pad);
      pad->volume = g_value_get_double (value);
111 112 113
      pad->volume_i8 = pad->volume * VOLUME_UNITY_INT8;
      pad->volume_i16 = pad->volume * VOLUME_UNITY_INT16;
      pad->volume_i32 = pad->volume * VOLUME_UNITY_INT32;
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
      GST_OBJECT_UNLOCK (pad);
      break;
    case PROP_PAD_MUTE:
      GST_OBJECT_LOCK (pad);
      pad->mute = g_value_get_boolean (value);
      GST_OBJECT_UNLOCK (pad);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
gst_adder_pad_class_init (GstAdderPadClass * klass)
{
  GObjectClass *gobject_class = (GObjectClass *) klass;

  gobject_class->set_property = gst_adder_pad_set_property;
  gobject_class->get_property = gst_adder_pad_get_property;

  g_object_class_install_property (gobject_class, PROP_PAD_VOLUME,
      g_param_spec_double ("volume", "Volume", "Volume of this pad",
          0.0, 10.0, DEFAULT_PAD_VOLUME,
          G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (gobject_class, PROP_PAD_MUTE,
      g_param_spec_boolean ("mute", "Mute", "Mute this pad",
          DEFAULT_PAD_MUTE,
          G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
}

static void
gst_adder_pad_init (GstAdderPad * pad)
{
  pad->volume = DEFAULT_PAD_VOLUME;
  pad->mute = DEFAULT_PAD_MUTE;
}

152 153 154 155 156 157
enum
{
  PROP_0,
  PROP_FILTER_CAPS
};

158
/* elementfactory information */
159

160
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
161
#define CAPS \
162 163
  GST_AUDIO_CAPS_MAKE ("{ S32LE, U32LE, S16LE, U16LE, S8, U8, F32LE, F64LE }") \
  ", layout = (string) { interleaved, non-interleaved }"
164 165
#else
#define CAPS \
166 167
  GST_AUDIO_CAPS_MAKE ("{ S32BE, U32BE, S16BE, U16BE, S8, U8, F32BE, F64BE }") \
  ", layout = (string) { interleaved, non-interleaved }"
168
#endif
Andy Wingo's avatar
Andy Wingo committed
169

David Schleef's avatar
David Schleef committed
170
static GstStaticPadTemplate gst_adder_src_template =
171
GST_STATIC_PAD_TEMPLATE ("src",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
172 173
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
174
    GST_STATIC_CAPS (CAPS)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
175
    );
Andy Wingo's avatar
Andy Wingo committed
176

David Schleef's avatar
David Schleef committed
177
static GstStaticPadTemplate gst_adder_sink_template =
178
GST_STATIC_PAD_TEMPLATE ("sink_%u",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
179 180
    GST_PAD_SINK,
    GST_PAD_REQUEST,
181
    GST_STATIC_CAPS (CAPS)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
182 183
    );

184 185
static void gst_adder_child_proxy_init (gpointer g_iface, gpointer iface_data);

186
#define gst_adder_parent_class parent_class
187 188
G_DEFINE_TYPE_WITH_CODE (GstAdder, gst_adder, GST_TYPE_ELEMENT,
    G_IMPLEMENT_INTERFACE (GST_TYPE_CHILD_PROXY, gst_adder_child_proxy_init));
Stefan Kost's avatar
Stefan Kost committed
189

190 191 192 193 194
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
195

196 197
static gboolean gst_adder_setcaps (GstAdder * adder, GstPad * pad,
    GstCaps * caps);
Wim Taymans's avatar
Wim Taymans committed
198 199
static gboolean gst_adder_src_query (GstPad * pad, GstObject * parent,
    GstQuery * query);
200 201
static gboolean gst_adder_sink_query (GstCollectPads * pads,
    GstCollectData * pad, GstQuery * query, gpointer user_data);
Wim Taymans's avatar
Wim Taymans committed
202 203
static gboolean gst_adder_src_event (GstPad * pad, GstObject * parent,
    GstEvent * event);
204 205
static gboolean gst_adder_sink_event (GstCollectPads * pads,
    GstCollectData * pad, GstEvent * event, gpointer user_data);
206

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
207
static GstPad *gst_adder_request_new_pad (GstElement * element,
208
    GstPadTemplate * temp, const gchar * unused, const GstCaps * caps);
209 210
static void gst_adder_release_pad (GstElement * element, GstPad * pad);

211 212
static GstStateChangeReturn gst_adder_change_state (GstElement * element,
    GstStateChange transition);
Andy Wingo's avatar
Andy Wingo committed
213

214 215
static GstFlowReturn gst_adder_do_clip (GstCollectPads * pads,
    GstCollectData * data, GstBuffer * buffer, GstBuffer ** out,
216
    gpointer user_data);
217
static GstFlowReturn gst_adder_collected (GstCollectPads * pads,
218
    gpointer user_data);
Andy Wingo's avatar
Andy Wingo committed
219

220 221 222
/* we can only accept caps that we and downstream can handle.
 * if we have filtercaps set, use those to constrain the target caps.
 */
223
static GstCaps *
224
gst_adder_sink_getcaps (GstPad * pad, GstCaps * filter)
225 226
{
  GstAdder *adder;
227
  GstCaps *result, *peercaps, *current_caps, *filter_caps;
228 229
  GstStructure *s;
  gint i, n;
230 231 232 233

  adder = GST_ADDER (GST_PAD_PARENT (pad));

  GST_OBJECT_LOCK (adder);
234
  /* take filter */
235 236 237 238 239 240 241 242
  if ((filter_caps = adder->filter_caps)) {
    if (filter)
      filter_caps =
          gst_caps_intersect_full (filter, filter_caps,
          GST_CAPS_INTERSECT_FIRST);
    else
      gst_caps_ref (filter_caps);
  } else {
Stefan Sauer's avatar
Stefan Sauer committed
243
    filter_caps = filter ? gst_caps_ref (filter) : NULL;
244
  }
245 246
  GST_OBJECT_UNLOCK (adder);

247 248 249 250 251
  if (filter_caps && gst_caps_is_empty (filter_caps)) {
    GST_WARNING_OBJECT (pad, "Empty filter caps");
    return filter_caps;
  }

252
  /* get the downstream possible caps */
253
  peercaps = gst_pad_peer_query_caps (adder->srcpad, filter_caps);
254

Wim Taymans's avatar
Wim Taymans committed
255
  /* get the allowed caps on this sinkpad */
256 257 258 259 260 261 262
  GST_OBJECT_LOCK (adder);
  current_caps =
      adder->current_caps ? gst_caps_ref (adder->current_caps) : NULL;
  if (current_caps == NULL) {
    current_caps = gst_pad_get_pad_template_caps (pad);
    if (!current_caps)
      current_caps = gst_caps_new_any ();
Wim Taymans's avatar
Wim Taymans committed
263
  }
264
  GST_OBJECT_UNLOCK (adder);
Wim Taymans's avatar
Wim Taymans committed
265

266 267
  if (peercaps) {
    /* if the peer has caps, intersect */
268
    GST_DEBUG_OBJECT (adder, "intersecting peer and our caps");
269
    result =
270 271
        gst_caps_intersect_full (peercaps, current_caps,
        GST_CAPS_INTERSECT_FIRST);
272
    gst_caps_unref (peercaps);
273
    gst_caps_unref (current_caps);
274 275 276
  } else {
    /* the peer has no caps (or there is no peer), just use the allowed caps
     * of this sinkpad. */
277
    /* restrict with filter-caps if any */
278
    if (filter_caps) {
279
      GST_DEBUG_OBJECT (adder, "no peer caps, using filtered caps");
280
      result =
281
          gst_caps_intersect_full (filter_caps, current_caps,
282
          GST_CAPS_INTERSECT_FIRST);
283
      gst_caps_unref (current_caps);
284
    } else {
285 286
      GST_DEBUG_OBJECT (adder, "no peer caps, using our caps");
      result = current_caps;
287
    }
288
  }
289

290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
  result = gst_caps_make_writable (result);

  n = gst_caps_get_size (result);
  for (i = 0; i < n; i++) {
    GstStructure *sref;

    s = gst_caps_get_structure (result, i);
    sref = gst_structure_copy (s);
    gst_structure_set (sref, "channels", GST_TYPE_INT_RANGE, 0, 2, NULL);
    if (gst_structure_is_subset (s, sref)) {
      /* This field is irrelevant when in mono or stereo */
      gst_structure_remove_field (s, "channel-mask");
    }
    gst_structure_free (sref);
  }

306 307
  if (filter_caps)
    gst_caps_unref (filter_caps);
308

309 310 311
  GST_LOG_OBJECT (adder, "getting caps on pad %p,%s to %" GST_PTR_FORMAT, pad,
      GST_PAD_NAME (pad), result);

312 313 314
  return result;
}

Wim Taymans's avatar
Wim Taymans committed
315
static gboolean
316 317
gst_adder_sink_query (GstCollectPads * pads, GstCollectData * pad,
    GstQuery * query, gpointer user_data)
Wim Taymans's avatar
Wim Taymans committed
318 319 320 321 322 323 324 325 326
{
  gboolean res = FALSE;

  switch (GST_QUERY_TYPE (query)) {
    case GST_QUERY_CAPS:
    {
      GstCaps *filter, *caps;

      gst_query_parse_caps (query, &filter);
327
      caps = gst_adder_sink_getcaps (pad->pad, filter);
Wim Taymans's avatar
Wim Taymans committed
328 329 330 331 332 333
      gst_query_set_caps_result (query, caps);
      gst_caps_unref (caps);
      res = TRUE;
      break;
    }
    default:
334
      res = gst_collect_pads_query_default (pads, pad, query, FALSE);
Wim Taymans's avatar
Wim Taymans committed
335 336
      break;
  }
337

Wim Taymans's avatar
Wim Taymans committed
338 339 340
  return res;
}

341 342
/* 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.
343
 */
344
static gboolean
345
gst_adder_setcaps (GstAdder * adder, GstPad * pad, GstCaps * orig_caps)
346
{
347
  GstCaps *caps;
348
  GstAudioInfo info;
349 350 351 352 353 354 355 356 357
  GstStructure *s;
  gint channels;

  caps = gst_caps_copy (orig_caps);

  s = gst_caps_get_structure (caps, 0);
  if (gst_structure_get_int (s, "channels", &channels))
    if (channels <= 2)
      gst_structure_remove_field (s, "channel-mask");
358 359 360 361

  if (!gst_audio_info_from_caps (&info, caps))
    goto invalid_format;

362
  GST_OBJECT_LOCK (adder);
363 364 365 366 367
  /* don't allow reconfiguration for now; there's still a race between the
   * different upstream threads doing query_caps + accept_caps + sending
   * (possibly different) CAPS events, but there's not much we can do about
   * that, upstream needs to deal with it. */
  if (adder->current_caps != NULL) {
368
    if (gst_audio_info_is_equal (&info, &adder->info)) {
369
      GST_OBJECT_UNLOCK (adder);
370
      gst_caps_unref (caps);
371 372 373 374
      return TRUE;
    } else {
      GST_DEBUG_OBJECT (pad, "got input caps %" GST_PTR_FORMAT ", but "
          "current caps are %" GST_PTR_FORMAT, caps, adder->current_caps);
375
      GST_OBJECT_UNLOCK (adder);
376
      gst_pad_push_event (pad, gst_event_new_reconfigure ());
377
      gst_caps_unref (caps);
378 379 380 381 382 383
      return FALSE;
    }
  }

  GST_INFO_OBJECT (pad, "setting caps to %" GST_PTR_FORMAT, caps);
  adder->current_caps = gst_caps_ref (caps);
384 385

  memcpy (&adder->info, &info, sizeof (info));
386
  GST_OBJECT_UNLOCK (adder);
387
  /* send caps event later, after stream-start event */
388

389
  GST_INFO_OBJECT (pad, "handle caps change to %" GST_PTR_FORMAT, caps);
390

391 392
  gst_caps_unref (caps);

393 394
  return TRUE;

395
  /* ERRORS */
Wim Taymans's avatar
Wim Taymans committed
396
invalid_format:
397
  {
398
    gst_caps_unref (caps);
399
    GST_WARNING_OBJECT (adder, "invalid format set as caps");
400 401
    return FALSE;
  }
402 403
}

404
/* FIXME, the duration query should reflect how long you will produce
405
 * data, that is the amount of stream time until you will emit EOS.
406 407 408 409
 *
 * For synchronized mixing this is always the max of all the durations
 * of upstream since we emit EOS when all of them finished.
 *
410 411 412
 * 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.
413
 *
414 415
 * 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
416
 * message to notify this fact to the parent.
417
 * For now we take the max of all the upstream elements so the simple
418
 * cases work at least somewhat.
419
 */
420
static gboolean
421
gst_adder_query_duration (GstAdder * adder, GstQuery * query)
422 423 424 425
{
  gint64 max;
  gboolean res;
  GstFormat format;
426 427
  GstIterator *it;
  gboolean done;
428
  GValue item = { 0, };
429 430

  /* parse format */
431
  gst_query_parse_duration (query, &format, NULL);
432 433 434

  max = -1;
  res = TRUE;
435
  done = FALSE;
436

437 438 439
  it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (adder));
  while (!done) {
    GstIteratorResult ires;
440

441 442 443 444 445 446 447
    ires = gst_iterator_next (it, &item);
    switch (ires) {
      case GST_ITERATOR_DONE:
        done = TRUE;
        break;
      case GST_ITERATOR_OK:
      {
448
        GstPad *pad = g_value_get_object (&item);
449 450 451
        gint64 duration;

        /* ask sink peer for duration */
452
        res &= gst_pad_peer_query_duration (pad, format, &duration);
453 454 455 456 457 458 459 460 461 462 463
        /* 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;
        }
464
        g_value_reset (&item);
465 466
        break;
      }
467 468 469
      case GST_ITERATOR_RESYNC:
        max = -1;
        res = TRUE;
470
        gst_iterator_resync (it);
471 472 473 474 475
        break;
      default:
        res = FALSE;
        done = TRUE;
        break;
476 477
    }
  }
478
  g_value_unset (&item);
479
  gst_iterator_free (it);
480

481 482
  if (res) {
    /* and store the max */
483 484
    GST_DEBUG_OBJECT (adder, "Total duration in format %s: %"
        GST_TIME_FORMAT, gst_format_get_name (format), GST_TIME_ARGS (max));
485
    gst_query_set_duration (query, format, max);
486
  }
487 488 489 490

  return res;
}

491
static gboolean
Wim Taymans's avatar
Wim Taymans committed
492
gst_adder_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
493
{
Wim Taymans's avatar
Wim Taymans committed
494
  GstAdder *adder = GST_ADDER (parent);
495 496
  gboolean res = FALSE;

497
  switch (GST_QUERY_TYPE (query)) {
498 499 500 501
    case GST_QUERY_POSITION:
    {
      GstFormat format;

502
      gst_query_parse_position (query, &format, NULL);
Wim Taymans's avatar
Wim Taymans committed
503 504 505

      switch (format) {
        case GST_FORMAT_TIME:
506
          /* FIXME, bring to stream time, might be tricky */
507
          gst_query_set_position (query, format, adder->segment.position);
Wim Taymans's avatar
Wim Taymans committed
508 509 510
          res = TRUE;
          break;
        case GST_FORMAT_DEFAULT:
511
          gst_query_set_position (query, format, adder->offset);
Wim Taymans's avatar
Wim Taymans committed
512 513 514 515
          res = TRUE;
          break;
        default:
          break;
516 517 518
      }
      break;
    }
Wim Taymans's avatar
Wim Taymans committed
519
    case GST_QUERY_DURATION:
520
      res = gst_adder_query_duration (adder, query);
Wim Taymans's avatar
Wim Taymans committed
521
      break;
522
    default:
523 524
      /* FIXME, needs a custom query handler because we have multiple
       * sinkpads */
Wim Taymans's avatar
Wim Taymans committed
525
      res = gst_pad_query_default (pad, parent, query);
526 527 528 529 530 531
      break;
  }

  return res;
}

Stefan Sauer's avatar
Stefan Sauer committed
532 533
/* event handling */

534 535 536 537 538 539
typedef struct
{
  GstEvent *event;
  gboolean flush;
} EventData;

540
static gboolean
541
forward_event_func (const GValue * val, GValue * ret, EventData * data)
542
{
543
  GstPad *pad = g_value_get_object (val);
544
  GstEvent *event = data->event;
545
  GstPad *peer;
546

547
  gst_event_ref (event);
548
  GST_LOG_OBJECT (pad, "About to send event %s", GST_EVENT_TYPE_NAME (event));
549 550 551 552 553 554
  peer = gst_pad_get_peer (pad);
  /* collect pad might have been set flushing,
   * so bypass core checking that and send directly to peer */
  if (!peer || !gst_pad_send_event (peer, event)) {
    if (!peer)
      gst_event_unref (event);
555 556
    GST_WARNING_OBJECT (pad, "Sending event  %p (%s) failed.",
        event, GST_EVENT_TYPE_NAME (event));
557 558 559
    /* quick hack to unflush the pads, ideally we need a way to just unflush
     * this single collect pad */
    if (data->flush)
560
      gst_pad_send_event (pad, gst_event_new_flush_stop (TRUE));
561
  } else {
562
    g_value_set_boolean (ret, TRUE);
563 564 565
    GST_LOG_OBJECT (pad, "Sent event  %p (%s).",
        event, GST_EVENT_TYPE_NAME (event));
  }
566 567
  if (peer)
    gst_object_unref (peer);
568

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

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

588
  GST_LOG_OBJECT (adder, "Forwarding event %p (%s)", event,
589 590
      GST_EVENT_TYPE_NAME (event));

591 592
  data.event = event;
  data.flush = flush;
593

594
  g_value_init (&vret, G_TYPE_BOOLEAN);
595
  g_value_set_boolean (&vret, FALSE);
596
  it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (adder));
597
  while (TRUE) {
598 599
    ires =
        gst_iterator_fold (it, (GstIteratorFoldFunction) forward_event_func,
600
        &vret, &data);
601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616
    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:
617
  gst_iterator_free (it);
618 619
  GST_LOG_OBJECT (adder, "Forwarded event %p (%s), ret=%d", event,
      GST_EVENT_TYPE_NAME (event), ret);
620 621 622 623 624 625
  gst_event_unref (event);

  return ret;
}

static gboolean
Wim Taymans's avatar
Wim Taymans committed
626
gst_adder_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
627 628 629 630
{
  GstAdder *adder;
  gboolean result;

Wim Taymans's avatar
Wim Taymans committed
631
  adder = GST_ADDER (parent);
632

633 634 635
  GST_DEBUG_OBJECT (pad, "Got %s event on src pad",
      GST_EVENT_TYPE_NAME (event));

636 637
  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
638 639
    {
      GstSeekFlags flags;
Wim Taymans's avatar
Wim Taymans committed
640
      gdouble rate;
Stefan Sauer's avatar
Stefan Sauer committed
641 642 643
      GstSeekType start_type, stop_type;
      gint64 start, stop;
      GstFormat seek_format, dest_format;
Wim Taymans's avatar
Wim Taymans committed
644
      gboolean flush;
645

646
      /* parse the seek parameters */
Stefan Sauer's avatar
Stefan Sauer committed
647 648
      gst_event_parse_seek (event, &rate, &seek_format, &flags, &start_type,
          &start, &stop_type, &stop);
649

Stefan Sauer's avatar
Stefan Sauer committed
650 651
      if ((start_type != GST_SEEK_TYPE_NONE)
          && (start_type != GST_SEEK_TYPE_SET)) {
652 653
        result = FALSE;
        GST_DEBUG_OBJECT (adder,
Stefan Sauer's avatar
Stefan Sauer committed
654
            "seeking failed, unhandled seek type for start: %d", start_type);
655 656
        goto done;
      }
Stefan Sauer's avatar
Stefan Sauer committed
657
      if ((stop_type != GST_SEEK_TYPE_NONE) && (stop_type != GST_SEEK_TYPE_SET)) {
658 659
        result = FALSE;
        GST_DEBUG_OBJECT (adder,
Stefan Sauer's avatar
Stefan Sauer committed
660 661 662 663 664 665 666 667 668
            "seeking failed, unhandled seek type for end: %d", stop_type);
        goto done;
      }

      dest_format = adder->segment.format;
      if (seek_format != dest_format) {
        result = FALSE;
        GST_DEBUG_OBJECT (adder,
            "seeking failed, unhandled seek format: %d", seek_format);
669 670 671
        goto done;
      }

Wim Taymans's avatar
Wim Taymans committed
672 673
      flush = (flags & GST_SEEK_FLAG_FLUSH) == GST_SEEK_FLAG_FLUSH;

674
      /* check if we are flushing */
Wim Taymans's avatar
Wim Taymans committed
675
      if (flush) {
676
        /* flushing seek, start flush downstream, the flush will be done
677 678 679 680 681
         * when all pads received a FLUSH_STOP.
         * Make sure we accept nothing anymore and return WRONG_STATE.
         * We send a flush-start before, to ensure no streaming is done
         * as we need to take the stream lock.
         */
682
        gst_pad_push_event (adder->srcpad, gst_event_new_flush_start ());
683
        gst_collect_pads_set_flushing (adder->collect, TRUE);
684 685 686 687 688 689 690

        /* 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.
         */
691 692 693
        GST_COLLECT_PADS_STREAM_LOCK (adder->collect);
        adder->flush_stop_pending = TRUE;
        GST_COLLECT_PADS_STREAM_UNLOCK (adder->collect);
694
        GST_DEBUG_OBJECT (adder, "mark pending flush stop event");
695
      }
696
      GST_DEBUG_OBJECT (adder, "handling seek event: %" GST_PTR_FORMAT, event);
Wim Taymans's avatar
Wim Taymans committed
697

698
      /* now wait for the collected to be finished and mark a new
Wim Taymans's avatar
Wim Taymans committed
699 700
       * 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. */
701
      GST_COLLECT_PADS_STREAM_LOCK (adder->collect);
Stefan Sauer's avatar
Stefan Sauer committed
702 703 704 705 706 707 708
      /* clip position and update our segment */
      if (adder->segment.stop != -1) {
        adder->segment.position = adder->segment.stop;
      }
      gst_segment_do_seek (&adder->segment, rate, seek_format, flags,
          start_type, start, stop_type, stop, NULL);

709 710 711
      if (flush) {
        /* Yes, we need to call _set_flushing again *WHEN* the streaming threads
         * have stopped so that the cookie gets properly updated. */
712
        gst_collect_pads_set_flushing (adder->collect, TRUE);
713
      }
714
      GST_COLLECT_PADS_STREAM_UNLOCK (adder->collect);
715 716
      GST_DEBUG_OBJECT (adder, "forwarding seek event: %" GST_PTR_FORMAT,
          event);
Stefan Sauer's avatar
Stefan Sauer committed
717 718
      GST_DEBUG_OBJECT (adder, "updated segment: %" GST_SEGMENT_FORMAT,
          &adder->segment);
719

720 721
      /* we're forwarding seek to all upstream peers and wait for one to reply
       * with a newsegment-event before we send a newsegment-event downstream */
722
      g_atomic_int_set (&adder->new_segment_pending, TRUE);
723
      result = forward_event (adder, event, flush);
724
      if (!result) {
Wim Taymans's avatar
Wim Taymans committed
725 726
        /* seek failed. maybe source is a live source. */
        GST_DEBUG_OBJECT (adder, "seeking failed");
727
      }
728 729 730
      if (g_atomic_int_compare_and_exchange (&adder->flush_stop_pending,
              TRUE, FALSE)) {
        GST_DEBUG_OBJECT (adder, "pending flush stop");
731 732 733 734
        if (!gst_pad_push_event (adder->srcpad,
                gst_event_new_flush_stop (TRUE))) {
          GST_WARNING_OBJECT (adder, "Sending flush stop event failed");
        }
735
      }
736
      break;
737
    }
738 739 740
    case GST_EVENT_QOS:
      /* QoS might be tricky */
      result = FALSE;
741
      gst_event_unref (event);
742
      break;
743 744 745
    case GST_EVENT_NAVIGATION:
      /* navigation is rather pointless. */
      result = FALSE;
746
      gst_event_unref (event);
747 748 749
      break;
    default:
      /* just forward the rest for now */
750 751
      GST_DEBUG_OBJECT (adder, "forward unhandled event: %s",
          GST_EVENT_TYPE_NAME (event));
752
      result = forward_event (adder, event, FALSE);
753 754
      break;
  }
755 756

done:
757

758 759 760
  return result;
}

761
static gboolean
762
gst_adder_sink_event (GstCollectPads * pads, GstCollectData * pad,
Stefan Sauer's avatar
Stefan Sauer committed
763
    GstEvent * event, gpointer user_data)
764
{
Stefan Sauer's avatar
Stefan Sauer committed
765
  GstAdder *adder = GST_ADDER (user_data);
766
  gboolean res = TRUE, discard = FALSE;
767

768
  GST_DEBUG_OBJECT (pad->pad, "Got %s event on sink pad",
769
      GST_EVENT_TYPE_NAME (event));
770 771

  switch (GST_EVENT_TYPE (event)) {
772 773 774 775 776
    case GST_EVENT_CAPS:
    {
      GstCaps *caps;

      gst_event_parse_caps (event, &caps);
777
      res = gst_adder_setcaps (adder, pad->pad, caps);
778
      gst_event_unref (event);
779
      event = NULL;
Stefan Sauer's avatar
Stefan Sauer committed
780
      break;
781
    }
782
    case GST_EVENT_FLUSH_START:
783
      /* ensure that we will send a flush stop */
784 785
      res = gst_collect_pads_event_default (pads, pad, event, discard);
      event = NULL;
786 787
      GST_COLLECT_PADS_STREAM_LOCK (adder->collect);
      adder->flush_stop_pending = TRUE;
788
      GST_COLLECT_PADS_STREAM_UNLOCK (adder->collect);
789
      break;
790
    case GST_EVENT_FLUSH_STOP:
Stefan Sauer's avatar
Stefan Sauer committed
791 792
      /* we received a flush-stop. We will only forward it when
       * flush_stop_pending is set, and we will unset it then.
793
       */
794
      g_atomic_int_set (&adder->new_segment_pending, TRUE);
795 796
      GST_COLLECT_PADS_STREAM_LOCK (adder->collect);
      if (adder->flush_stop_pending) {
Stefan Sauer's avatar
Stefan Sauer committed
797
        GST_DEBUG_OBJECT (pad->pad, "forwarding flush stop");
798 799 800
        res = gst_collect_pads_event_default (pads, pad, event, discard);
        adder->flush_stop_pending = FALSE;
        event = NULL;
Stefan Sauer's avatar
Stefan Sauer committed
801
      } else {
802
        discard = TRUE;
Stefan Sauer's avatar
Stefan Sauer committed
803 804
        GST_DEBUG_OBJECT (pad->pad, "eating flush stop");
      }
805
      GST_COLLECT_PADS_STREAM_UNLOCK (adder->collect);
806 807 808 809 810 811
      /* 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;
      }
812
      break;
813
    case GST_EVENT_TAG:
Wim Taymans's avatar
Wim Taymans committed
814
      /* collect tags here so we can push them out when we collect data */
815
      adder->pending_events = g_list_append (adder->pending_events, event);
816
      event = NULL;
Stefan Sauer's avatar
Stefan Sauer committed
817
      break;
818 819 820 821 822 823 824 825 826 827 828
    case GST_EVENT_SEGMENT:{
      const GstSegment *segment;
      gst_event_parse_segment (event, &segment);
      if (segment->rate != adder->segment.rate) {
        GST_ERROR_OBJECT (pad->pad,
            "Got segment event with wrong rate %lf, expected %lf",
            segment->rate, adder->segment.rate);
        res = FALSE;
        gst_event_unref (event);
        event = NULL;
      }
829
      discard = TRUE;
830
      break;
831
    }
832 833 834 835
    default:
      break;
  }

836
  if (G_LIKELY (event))
837
    return gst_collect_pads_event_default (pads, pad, event, discard);
838 839
  else
    return res;
840 841
}

Stefan Kost's avatar
Stefan Kost committed
842 843 844 845 846
static void
gst_adder_class_init (GstAdderClass * klass)
{
  GObjectClass *gobject_class = (GObjectClass *) klass;
  GstElementClass *gstelement_class = (GstElementClass *) klass;
847

Stefan Kost's avatar
Stefan Kost committed
848 849 850
  gobject_class->set_property = gst_adder_set_property;
  gobject_class->get_property = gst_adder_get_property;
  gobject_class->dispose = gst_adder_dispose;
851

852
  g_object_class_install_property (gobject_class, PROP_FILTER_CAPS,
Stefan Kost's avatar
Stefan Kost committed
853 854
      g_param_spec_boxed ("caps", "Target caps",
          "Set target format for mixing (NULL means ANY). "
855 856 857 858
          "Setting this property takes a reference to the supplied GstCaps "
          "object.", GST_TYPE_CAPS,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

859 860 861 862
  gst_element_class_add_static_pad_template (gstelement_class,
      &gst_adder_src_template);
  gst_element_class_add_static_pad_template (gstelement_class,
      &gst_adder_sink_template);
863
  gst_element_class_set_static_metadata (gstelement_class, "Adder",
864
      "Generic/Audio", "Add N audio channels together",
865 866
      "Thomas Vander Stichele <thomas at apestaart dot org>");

867 868 869 870
  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's avatar
Andy Wingo committed
871 872
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
873
static void
874
gst_adder_init (GstAdder * adder)
Andy Wingo's avatar
Andy Wingo committed
875
{
Christophe Fergeau's avatar
Christophe Fergeau committed
876 877 878 879 880
  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);
881

882
  gst_pad_set_query_function (adder->srcpad,
Wim Taymans's avatar
Wim Taymans committed
883
      GST_DEBUG_FUNCPTR (gst_adder_src_query));
884 885
  gst_pad_set_event_function (adder->srcpad,
      GST_DEBUG_FUNCPTR (gst_adder_src_event));
Wim Taymans's avatar
Wim Taymans committed
886
  GST_PAD_SET_PROXY_CAPS (adder->srcpad);
887
  gst_element_add_pad (GST_ELEMENT (adder), adder->srcpad);
Andy Wingo's avatar
Andy Wingo committed
888

889
  adder->current_caps = NULL;
Wim Taymans's avatar
Wim Taymans committed
890
  gst_audio_info_init (&adder->info);
891
  adder->padcount = 0;
892

893
  adder->filter_caps = NULL;
894

Andy Wingo's avatar
Andy Wingo committed
895
  /* keep track of the sinkpads requested */
896 897
  adder->collect = gst_collect_pads_new ();
  gst_collect_pads_set_function (adder->collect,
898
      GST_DEBUG_FUNCPTR (gst_adder_collected), adder);
899
  gst_collect_pads_set_clip_function (adder->collect,
900
      GST_DEBUG_FUNCPTR (gst_adder_do_clip), adder);
901
  gst_collect_pads_set_event_function (adder->collect,
Stefan Sauer's avatar
Stefan Sauer committed
902
      GST_DEBUG_FUNCPTR (gst_adder_sink_event), adder);
903 904
  gst_collect_pads_set_query_function (adder->collect,
      GST_DEBUG_FUNCPTR (gst_adder_sink_query), adder);
Andy Wingo's avatar
Andy Wingo committed
905 906
}

907
static void
908
gst_adder_dispose (GObject * object)
909 910 911
{
  GstAdder *adder = GST_ADDER (object);

912 913 914 915 916
  if (adder->collect) {
    gst_object_unref (adder->collect);
    adder->collect = NULL;
  }
  gst_caps_replace (&adder->filter_caps, NULL);
917 918
  gst_caps_replace (&adder->current_caps, NULL);

919 920 921 922 923
  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;
  }
924

925
  G_OBJECT_CLASS (parent_class)->dispose (object);
926 927
}

928 929 930 931 932 933 934 935
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:{
936
      GstCaps *new_caps = NULL;
937 938 939
      GstCaps *old_caps;
      const GstCaps *new_caps_val = gst_value_get_caps (value);

940
      if (new_caps_val != NULL) {
941 942 943 944 945 946 947 948 949
        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);

950 951
      if (old_caps)
        gst_caps_unref (old_caps);
952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980

      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
981 982
static GstPad *
gst_adder_request_new_pad (GstElement * element, GstPadTemplate * templ,
983
    const gchar * unused, const GstCaps * caps)
Andy Wingo's avatar
Andy Wingo committed
984
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
985 986
  gchar *name;
  GstAdder *adder;
987
  GstPad *newpad;
988
  gint padcount;
Andy Wingo's avatar
Andy Wingo committed
989

990 991
  if (templ->direction != GST_PAD_SINK)
    goto not_sink;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
992

993 994
  adder = GST_ADDER (element);

995
  /* increment pad counter */
996
  padcount = g_atomic_int_add (&adder->padcount, 1);
997

998
  name = g_strdup_printf ("sink_%u", padcount);
999 1000
  newpad = g_object_new (GST_TYPE_ADDER_PAD, "name", name, "direction",
      templ->direction, "template", templ, NULL);
1001
  GST_DEBUG_OBJECT (adder, "request new pad %s", name);
1002
  g_free (name);
Andy Wingo's avatar
Andy Wingo committed
1003

1004 1005
  gst_collect_pads_add_pad (adder->collect, newpad, sizeof (GstCollectData),
      NULL, TRUE);
1006

1007
  /* takes ownership of the pad */
1008 1009
  if (!gst_element_add_pad (GST_ELEMENT (adder), newpad))
    goto could_not_add;
1010

1011 1012 1013
  gst_child_proxy_child_added (GST_CHILD_PROXY (adder), G_OBJECT (newpad),
      GST_OBJECT_NAME (newpad));

1014
  return newpad;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1015