gstpad.c 134 KB
Newer Older
1
2
3
4
/* GStreamer
 * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
 *                    2000 Wim Taymans <wtay@chello.be>
 *
5
 * gstpad.c: Pads for linking elements together
Erik Walthinsen's avatar
Erik Walthinsen committed
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 *
 * 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.
 */
22
23
/**
 * SECTION:gstpad
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
24
25
 * @short_description: Object contained by elements that allows links to
 *                     other elements
26
27
 * @see_also: #GstPadTemplate, #GstElement, #GstEvent
 *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
28
29
30
 * A #GstElement is linked to other elements via "pads", which are extremely
 * light-weight generic link points.
 * After two pads are retrieved from an element with gst_element_get_pad(),
31
32
33
34
 * the pads can be link with gst_pad_link(). (For quick links,
 * you can also use gst_element_link(), which will make the obvious
 * link for you if it's straightforward.)
 *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
35
 * Pads are typically created from a #GstPadTemplate with
36
37
 * gst_pad_new_from_template().
 *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
38
 * Pads have #GstCaps attached to it to describe the media type they are
39
 * capable of dealing with.  gst_pad_query_caps() and gst_pad_set_caps() are
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
40
 * used to manipulate the caps of the pads.
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
41
 * Pads created from a pad template cannot set capabilities that are
42
43
44
45
46
47
48
49
 * incompatible with the pad template capabilities.
 *
 * Pads without pad templates can be created with gst_pad_new(),
 * which takes a direction and a name as an argument.  If the name is NULL,
 * then a guaranteed unique name will be assigned to it.
 *
 * gst_pad_get_parent() will retrieve the #GstElement that owns the pad.
 *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
50
51
 * A #GstElement creating a pad will typically use the various
 * gst_pad_set_*_function() calls to register callbacks for various events
52
53
 * on the pads.
 *
Wim Taymans's avatar
Wim Taymans committed
54
 * GstElements will use gst_pad_push() and gst_pad_pull_range() to push out
55
56
 * or pull in a buffer.
 *
57
58
 * To send a #GstEvent on a pad, use gst_pad_send_event() and
 * gst_pad_push_event().
Wim Taymans's avatar
Wim Taymans committed
59
 *
60
 * Last reviewed on 2006-07-06 (0.10.9)
61
 */
Erik Walthinsen's avatar
Erik Walthinsen committed
62

63
64
#include "gst_private.h"

65
#include "gstpad.h"
66
#include "gstpadtemplate.h"
67
#include "gstenumtypes.h"
68
#include "gstutils.h"
69
#include "gstinfo.h"
70
#include "gsterror.h"
David Schleef's avatar
David Schleef committed
71
#include "gstvalue.h"
72
#include "glib-compat-private.h"
David Schleef's avatar
David Schleef committed
73

74
GST_DEBUG_CATEGORY_STATIC (debug_dataflow);
75
76
#define GST_CAT_DEFAULT GST_CAT_PADS

77
78
79
80
81
82
/* Pad signals and args */
enum
{
  PAD_LINKED,
  PAD_UNLINKED,
  /* FILL ME */
83
  LAST_SIGNAL
84
85
86
87
88
89
90
91
92
93
94
};

enum
{
  PAD_PROP_0,
  PAD_PROP_CAPS,
  PAD_PROP_DIRECTION,
  PAD_PROP_TEMPLATE,
  /* FILL ME */
};

Wim Taymans's avatar
Wim Taymans committed
95
96
97
#define GST_PAD_GET_PRIVATE(obj)  \
   (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_PAD, GstPadPrivate))

98
99
100
/* we have a pending and an active event on the pad. On source pads only the
 * active event is used. On sinkpads, events are copied to the pending entry and
 * moved to the active event when the eventfunc returned TRUE. */
101
102
typedef struct
{
Wim Taymans's avatar
Wim Taymans committed
103
  gboolean received;
104
105
106
  GstEvent *event;
} PadEvent;

Wim Taymans's avatar
Wim Taymans committed
107
108
struct _GstPadPrivate
{
Wim Taymans's avatar
Wim Taymans committed
109
110
  guint events_cookie;
  GArray *events;
111
112

  gint using;
Wim Taymans's avatar
Wim Taymans committed
113
114
  guint probe_list_cookie;
  guint probe_cookie;
Wim Taymans's avatar
Wim Taymans committed
115
116
};

117
118
119
120
121
122
123
124
125
126
127
typedef struct
{
  GHook hook;
  guint cookie;
} GstProbe;

#define PROBE_COOKIE(h) (((GstProbe *)(h))->cookie)

typedef struct
{
  GstPad *pad;
Wim Taymans's avatar
Wim Taymans committed
128
  GstPadProbeInfo *info;
Wim Taymans's avatar
Wim Taymans committed
129
  gboolean dropped;
130
  gboolean pass;
Wim Taymans's avatar
Wim Taymans committed
131
  gboolean marshalled;
132
133
134
  guint cookie;
} ProbeMarshall;

135
static void gst_pad_dispose (GObject * object);
136
137
138
139
140
static void gst_pad_finalize (GObject * object);
static void gst_pad_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec);
static void gst_pad_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec);
141

142
static void gst_pad_set_pad_template (GstPad * pad, GstPadTemplate * templ);
143
static gboolean gst_pad_activate_default (GstPad * pad, GstObject * parent);
144
static GstFlowReturn gst_pad_chain_list_default (GstPad * pad,
145
    GstObject * parent, GstBufferList * list);
146

Wim Taymans's avatar
Wim Taymans committed
147
148
149
static GstFlowReturn gst_pad_send_event_unchecked (GstPad * pad,
    GstEvent * event, GstPadProbeType type);
static GstFlowReturn gst_pad_push_event_unchecked (GstPad * pad,
150
    GstEvent * event, GstPadProbeType type);
Wim Taymans's avatar
Wim Taymans committed
151

152
static guint gst_pad_signals[LAST_SIGNAL] = { 0 };
Erik Walthinsen's avatar
Erik Walthinsen committed
153

154
155
static GParamSpec *pspec_caps = NULL;

156
/* quarks for probe signals */
157
static GQuark buffer_quark;
158
static GQuark buffer_list_quark;
159
160
static GQuark event_quark;

161
162
typedef struct
{
163
164
  const gint ret;
  const gchar *name;
165
166
167
168
  GQuark quark;
} GstFlowQuarks;

static GstFlowQuarks flow_quarks[] = {
169
  {GST_FLOW_CUSTOM_SUCCESS, "custom-success", 0},
170
171
  {GST_FLOW_OK, "ok", 0},
  {GST_FLOW_NOT_LINKED, "not-linked", 0},
172
  {GST_FLOW_FLUSHING, "flushing", 0},
173
  {GST_FLOW_EOS, "eos", 0},
174
175
176
  {GST_FLOW_NOT_NEGOTIATED, "not-negotiated", 0},
  {GST_FLOW_ERROR, "error", 0},
  {GST_FLOW_NOT_SUPPORTED, "not-supported", 0},
177
  {GST_FLOW_CUSTOM_ERROR, "custom-error", 0}
178
179
180
181
182
183
184
185
};

/**
 * gst_flow_get_name:
 * @ret: a #GstFlowReturn to get the name of.
 *
 * Gets a string representing the given flow return.
 *
186
 * Returns: a static string with the name of the flow return.
187
 */
188
const gchar *
189
190
191
192
gst_flow_get_name (GstFlowReturn ret)
{
  gint i;

193
194
  ret = CLAMP (ret, GST_FLOW_CUSTOM_ERROR, GST_FLOW_CUSTOM_SUCCESS);

195
  for (i = 0; i < G_N_ELEMENTS (flow_quarks); i++) {
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
    if (ret == flow_quarks[i].ret)
      return flow_quarks[i].name;
  }
  return "unknown";
}

/**
 * gst_flow_to_quark:
 * @ret: a #GstFlowReturn to get the quark of.
 *
 * Get the unique quark for the given GstFlowReturn.
 *
 * Returns: the quark associated with the flow return or 0 if an
 * invalid return was specified.
 */
GQuark
gst_flow_to_quark (GstFlowReturn ret)
{
  gint i;

216
217
  ret = CLAMP (ret, GST_FLOW_CUSTOM_ERROR, GST_FLOW_CUSTOM_SUCCESS);

218
  for (i = 0; i < G_N_ELEMENTS (flow_quarks); i++) {
219
220
221
222
223
224
    if (ret == flow_quarks[i].ret)
      return flow_quarks[i].quark;
  }
  return 0;
}

225
226
227
228
229
#define _do_init \
{ \
  gint i; \
  \
  buffer_quark = g_quark_from_static_string ("buffer"); \
230
  buffer_list_quark = g_quark_from_static_string ("bufferlist"); \
231
232
  event_quark = g_quark_from_static_string ("event"); \
  \
233
  for (i = 0; i < G_N_ELEMENTS (flow_quarks); i++) {			\
234
235
236
237
238
    flow_quarks[i].quark = g_quark_from_static_string (flow_quarks[i].name); \
  } \
  \
  GST_DEBUG_CATEGORY_INIT (debug_dataflow, "GST_DATAFLOW", \
      GST_DEBUG_BOLD | GST_DEBUG_FG_GREEN, "dataflow inside pads"); \
Erik Walthinsen's avatar
Erik Walthinsen committed
239
240
}

241
#define gst_pad_parent_class parent_class
242
243
G_DEFINE_TYPE_WITH_CODE (GstPad, gst_pad, GST_TYPE_OBJECT, _do_init);

Erik Walthinsen's avatar
Erik Walthinsen committed
244
static void
245
gst_pad_class_init (GstPadClass * klass)
246
{
247
  GObjectClass *gobject_class;
248
  GstObjectClass *gstobject_class;
Erik Walthinsen's avatar
Erik Walthinsen committed
249

250
251
  gobject_class = G_OBJECT_CLASS (klass);
  gstobject_class = GST_OBJECT_CLASS (klass);
Erik Walthinsen's avatar
Erik Walthinsen committed
252

Wim Taymans's avatar
Wim Taymans committed
253
254
  g_type_class_add_private (klass, sizeof (GstPadPrivate));

255
256
257
258
  gobject_class->dispose = gst_pad_dispose;
  gobject_class->finalize = gst_pad_finalize;
  gobject_class->set_property = gst_pad_set_property;
  gobject_class->get_property = gst_pad_get_property;
Erik Walthinsen's avatar
Erik Walthinsen committed
259

260
261
262
263
264
265
266
  /**
   * GstPad::linked:
   * @pad: the pad that emitted the signal
   * @peer: the peer pad that has been connected
   *
   * Signals that a pad has been linked to the peer pad.
   */
267
  gst_pad_signals[PAD_LINKED] =
268
      g_signal_new ("linked", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
269
      G_STRUCT_OFFSET (GstPadClass, linked), NULL, NULL,
270
      g_cclosure_marshal_generic, G_TYPE_NONE, 1, GST_TYPE_PAD);
271
272
273
274
275
276
277
  /**
   * GstPad::unlinked:
   * @pad: the pad that emitted the signal
   * @peer: the peer pad that has been disconnected
   *
   * Signals that a pad has been unlinked from the peer pad.
   */
278
  gst_pad_signals[PAD_UNLINKED] =
279
      g_signal_new ("unlinked", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
280
      G_STRUCT_OFFSET (GstPadClass, unlinked), NULL, NULL,
281
      g_cclosure_marshal_generic, G_TYPE_NONE, 1, GST_TYPE_PAD);
282

283
284
285
286
287
  pspec_caps = g_param_spec_boxed ("caps", "Caps",
      "The capabilities of the pad", GST_TYPE_CAPS,
      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
  g_object_class_install_property (gobject_class, PAD_PROP_CAPS, pspec_caps);

288
  g_object_class_install_property (gobject_class, PAD_PROP_DIRECTION,
289
290
      g_param_spec_enum ("direction", "Direction", "The direction of the pad",
          GST_TYPE_PAD_DIRECTION, GST_PAD_UNKNOWN,
291
          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
292

293
  /* FIXME, Make G_PARAM_CONSTRUCT_ONLY when we fix ghostpads. */
294
  g_object_class_install_property (gobject_class, PAD_PROP_TEMPLATE,
295
296
      g_param_spec_object ("template", "Template",
          "The GstPadTemplate of this pad", GST_TYPE_PAD_TEMPLATE,
297
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
298
299

  gstobject_class->path_string_separator = ".";
300

301
302
303
304
305
  /* Register common function pointer descriptions */
  GST_DEBUG_REGISTER_FUNCPTR (gst_pad_activate_default);
  GST_DEBUG_REGISTER_FUNCPTR (gst_pad_event_default);
  GST_DEBUG_REGISTER_FUNCPTR (gst_pad_query_default);
  GST_DEBUG_REGISTER_FUNCPTR (gst_pad_iterate_internal_links_default);
306
  GST_DEBUG_REGISTER_FUNCPTR (gst_pad_chain_list_default);
Erik Walthinsen's avatar
Erik Walthinsen committed
307
308
}

Wim Taymans's avatar
Wim Taymans committed
309
static void
310
gst_pad_init (GstPad * pad)
311
{
Wim Taymans's avatar
Wim Taymans committed
312
  pad->priv = GST_PAD_GET_PRIVATE (pad);
Wim Taymans's avatar
Wim Taymans committed
313

314
  GST_PAD_DIRECTION (pad) = GST_PAD_UNKNOWN;
315

316
317
318
319
  GST_PAD_ACTIVATEFUNC (pad) = gst_pad_activate_default;
  GST_PAD_EVENTFUNC (pad) = gst_pad_event_default;
  GST_PAD_QUERYFUNC (pad) = gst_pad_query_default;
  GST_PAD_ITERINTLINKFUNC (pad) = gst_pad_iterate_internal_links_default;
320
  GST_PAD_CHAINLISTFUNC (pad) = gst_pad_chain_list_default;
321

322
  GST_PAD_SET_FLUSHING (pad);
Wim Taymans's avatar
Wim Taymans committed
323

Wim Taymans's avatar
Wim Taymans committed
324
  g_rec_mutex_init (&pad->stream_rec_lock);
Wim Taymans's avatar
Wim Taymans committed
325

Wim Taymans's avatar
Wim Taymans committed
326
  g_cond_init (&pad->block_cond);
327
328

  g_hook_list_init (&pad->probes, sizeof (GstProbe));
329

330
  pad->priv->events = g_array_sized_new (FALSE, TRUE, sizeof (PadEvent), 16);
Wim Taymans's avatar
Wim Taymans committed
331
332
}

333
334
/* called when setting the pad inactive. It removes all sticky events from
 * the pad */
335
static void
Wim Taymans's avatar
Wim Taymans committed
336
remove_events (GstPad * pad)
337
{
Wim Taymans's avatar
Wim Taymans committed
338
339
  guint i, len;
  GArray *events;
340

Wim Taymans's avatar
Wim Taymans committed
341
  events = pad->priv->events;
Wim Taymans's avatar
Wim Taymans committed
342

Wim Taymans's avatar
Wim Taymans committed
343
344
345
346
347
  len = events->len;
  for (i = 0; i < len; i++) {
    PadEvent *ev = &g_array_index (events, PadEvent, i);
    gst_event_unref (ev->event);
  }
Wim Taymans's avatar
Wim Taymans committed
348
  GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_PENDING_EVENTS);
Wim Taymans's avatar
Wim Taymans committed
349
350
  g_array_set_size (events, 0);
  pad->priv->events_cookie++;
Wim Taymans's avatar
Wim Taymans committed
351
352
}

Wim Taymans's avatar
Wim Taymans committed
353
354
static PadEvent *
find_event_by_type (GstPad * pad, GstEventType type, guint idx)
355
{
Wim Taymans's avatar
Wim Taymans committed
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
  guint i, len;
  GArray *events;
  PadEvent *ev;

  events = pad->priv->events;
  len = events->len;

  for (i = 0; i < len; i++) {
    ev = &g_array_index (events, PadEvent, i);
    if (ev->event == NULL)
      continue;

    if (GST_EVENT_TYPE (ev->event) == type) {
      if (idx == 0)
        goto found;
      idx--;
    }
373
  }
Wim Taymans's avatar
Wim Taymans committed
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
  ev = NULL;
found:
  return ev;
}

static PadEvent *
find_event (GstPad * pad, GstEvent * event)
{
  guint i, len;
  GArray *events;
  PadEvent *ev;

  events = pad->priv->events;
  len = events->len;

  for (i = 0; i < len; i++) {
    ev = &g_array_index (events, PadEvent, i);
    if (event == ev->event)
      goto found;
  }
  ev = NULL;
found:
  return ev;
397
398
}

Wim Taymans's avatar
Wim Taymans committed
399
static void
Wim Taymans's avatar
Wim Taymans committed
400
remove_event_by_type (GstPad * pad, GstEventType type)
Wim Taymans's avatar
Wim Taymans committed
401
{
Wim Taymans's avatar
Wim Taymans committed
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
  guint i, len;
  GArray *events;
  PadEvent *ev;

  events = pad->priv->events;
  len = events->len;

  i = 0;
  while (i < len) {
    ev = &g_array_index (events, PadEvent, i);
    if (ev->event == NULL)
      goto next;

    if (GST_EVENT_TYPE (ev->event) != type)
      goto next;

    gst_event_unref (ev->event);
    g_array_remove_index (events, i);
    len--;
    pad->priv->events_cookie++;
    continue;

  next:
    i++;
  }
}

Wim Taymans's avatar
Wim Taymans committed
429
430
431
/* check all events on srcpad against those on sinkpad. All events that are not
 * on sinkpad are marked as received=FALSE and the PENDING_EVENTS is set on the
 * srcpad so that the events will be sent next time */
Wim Taymans's avatar
Wim Taymans committed
432
433
434
435
436
437
static void
schedule_events (GstPad * srcpad, GstPad * sinkpad)
{
  gint i, len;
  GArray *events;
  PadEvent *ev;
Wim Taymans's avatar
Wim Taymans committed
438
  gboolean pending = FALSE;
Wim Taymans's avatar
Wim Taymans committed
439

Wim Taymans's avatar
Wim Taymans committed
440
441
442
443
444
445
446
  events = srcpad->priv->events;
  len = events->len;

  for (i = 0; i < len; i++) {
    ev = &g_array_index (events, PadEvent, i);
    if (ev->event == NULL)
      continue;
Wim Taymans's avatar
Wim Taymans committed
447

Wim Taymans's avatar
Wim Taymans committed
448
449
450
451
452
    if (sinkpad == NULL || !find_event (sinkpad, ev->event)) {
      ev->received = FALSE;
      pending = TRUE;
    }
  }
Wim Taymans's avatar
Wim Taymans committed
453
  if (pending)
Wim Taymans's avatar
Wim Taymans committed
454
    GST_OBJECT_FLAG_SET (srcpad, GST_PAD_FLAG_PENDING_EVENTS);
Wim Taymans's avatar
Wim Taymans committed
455
456
}

Wim Taymans's avatar
Wim Taymans committed
457
458
459
typedef gboolean (*PadEventFunction) (GstPad * pad, PadEvent * ev,
    gpointer user_data);

Wim Taymans's avatar
Wim Taymans committed
460
/* should be called with pad LOCK */
Wim Taymans's avatar
Wim Taymans committed
461
462
463
464
465
466
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
495
static void
events_foreach (GstPad * pad, PadEventFunction func, gpointer user_data)
{
  guint i, len;
  GArray *events;
  gboolean ret;
  guint cookie;

  events = pad->priv->events;

restart:
  cookie = pad->priv->events_cookie;
  i = 0;
  len = events->len;
  while (i < len) {
    PadEvent *ev, ev_ret;

    ev = &g_array_index (events, PadEvent, i);
    if (G_UNLIKELY (ev->event == NULL))
      goto next;

    /* take aditional ref, func might release the lock */
    ev_ret.event = gst_event_ref (ev->event);
    ev_ret.received = ev->received;

    ret = func (pad, &ev_ret, user_data);

    /* recheck the cookie, lock might have been released and the list could have
     * changed */
    if (G_UNLIKELY (cookie != pad->priv->events_cookie)) {
      if (G_LIKELY (ev_ret.event))
        gst_event_unref (ev_ret.event);
      goto restart;
    }

496
497
498
    /* store the received state */
    ev->received = ev_ret.received;

Wim Taymans's avatar
Wim Taymans committed
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
    /* if the event changed, we need to do something */
    if (G_UNLIKELY (ev->event != ev_ret.event)) {
      if (G_UNLIKELY (ev_ret.event == NULL)) {
        /* function unreffed and set the event to NULL, remove it */
        g_array_remove_index (events, i);
        len--;
        cookie = ++pad->priv->events_cookie;
        continue;
      } else {
        /* function gave a new event for us */
        gst_event_take (&ev->event, ev_ret.event);
      }
    } else {
      /* just unref, nothing changed */
      gst_event_unref (ev_ret.event);
    }
    if (!ret)
      break;
  next:
    i++;
  }
}
Wim Taymans's avatar
Wim Taymans committed
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538

/* should be called with LOCK */
static GstEvent *
apply_pad_offset (GstPad * pad, GstEvent * event)
{
  /* check if we need to adjust the segment */
  if (pad->offset != 0) {
    GstSegment segment;

    /* copy segment values */
    gst_event_copy_segment (event, &segment);
    gst_event_unref (event);

    /* adjust and make a new event with the offset applied */
    segment.base += pad->offset;
    event = gst_event_new_segment (&segment);
  }
  return event;
Wim Taymans's avatar
Wim Taymans committed
539
540
}

Wim Taymans's avatar
Wim Taymans committed
541
/* should be called with the OBJECT_LOCK */
542
543
544
545
static GstCaps *
get_pad_caps (GstPad * pad)
{
  GstCaps *caps = NULL;
Wim Taymans's avatar
Wim Taymans committed
546
  PadEvent *ev;
547

Wim Taymans's avatar
Wim Taymans committed
548
549
550
  ev = find_event_by_type (pad, GST_EVENT_CAPS, 0);
  if (ev && ev->event)
    gst_event_parse_caps (ev->event, &caps);
551
552
553

  return caps;
}
554

555
static void
556
557
gst_pad_dispose (GObject * object)
{
Wim Taymans's avatar
Wim Taymans committed
558
  GstPad *pad = GST_PAD_CAST (object);
559
  GstPad *peer;
560

561
  GST_CAT_DEBUG_OBJECT (GST_CAT_REFCOUNTING, pad, "dispose");
562

563
564
565
566
567
568
569
570
571
572
573
574
  /* unlink the peer pad */
  if ((peer = gst_pad_get_peer (pad))) {
    /* window for MT unsafeness, someone else could unlink here
     * and then we call unlink with wrong pads. The unlink
     * function would catch this and safely return failed. */
    if (GST_PAD_IS_SRC (pad))
      gst_pad_unlink (pad, peer);
    else
      gst_pad_unlink (peer, pad);

    gst_object_unref (peer);
  }
575

576
  gst_pad_set_pad_template (pad, NULL);
577

Wim Taymans's avatar
Wim Taymans committed
578
  remove_events (pad);
579

580
581
  g_hook_list_clear (&pad->probes);

582
  G_OBJECT_CLASS (parent_class)->dispose (object);
583
584
585
586
587
}

static void
gst_pad_finalize (GObject * object)
{
Wim Taymans's avatar
Wim Taymans committed
588
  GstPad *pad = GST_PAD_CAST (object);
589
590
591
592
593
594
595
596
  GstTask *task;

  /* in case the task is still around, clean it up */
  if ((task = GST_PAD_TASK (pad))) {
    gst_task_join (task);
    GST_PAD_TASK (pad) = NULL;
    gst_object_unref (task);
  }
597

598
  if (pad->activatenotify)
599
    pad->activatenotify (pad->activatedata);
600
  if (pad->activatemodenotify)
601
    pad->activatemodenotify (pad->activatemodedata);
602
  if (pad->linknotify)
603
    pad->linknotify (pad->linkdata);
604
  if (pad->unlinknotify)
605
    pad->unlinknotify (pad->unlinkdata);
606
  if (pad->chainnotify)
607
    pad->chainnotify (pad->chaindata);
608
  if (pad->chainlistnotify)
609
    pad->chainlistnotify (pad->chainlistdata);
610
  if (pad->getrangenotify)
611
    pad->getrangenotify (pad->getrangedata);
612
  if (pad->eventnotify)
613
    pad->eventnotify (pad->eventdata);
614
  if (pad->querynotify)
615
    pad->querynotify (pad->querydata);
616
  if (pad->iterintlinknotify)
617
    pad->iterintlinknotify (pad->iterintlinkdata);
618

Wim Taymans's avatar
Wim Taymans committed
619
620
  g_rec_mutex_clear (&pad->stream_rec_lock);
  g_cond_clear (&pad->block_cond);
Wim Taymans's avatar
Wim Taymans committed
621
  g_array_free (pad->priv->events, TRUE);
622

623
  G_OBJECT_CLASS (parent_class)->finalize (object);
624
625
626
627
}

static void
gst_pad_set_property (GObject * object, guint prop_id,
628
    const GValue * value, GParamSpec * pspec)
629
{
630
  g_return_if_fail (GST_IS_PAD (object));
631

632
  switch (prop_id) {
633
    case PAD_PROP_DIRECTION:
634
      GST_PAD_DIRECTION (object) = (GstPadDirection) g_value_get_enum (value);
635
636
637
      break;
    case PAD_PROP_TEMPLATE:
      gst_pad_set_pad_template (GST_PAD_CAST (object),
638
          (GstPadTemplate *) g_value_get_object (value));
639
      break;
640
    default:
641
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
642
643
644
645
646
      break;
  }
}

static void
647
gst_pad_get_property (GObject * object, guint prop_id,
648
    GValue * value, GParamSpec * pspec)
649
650
651
{
  g_return_if_fail (GST_IS_PAD (object));

652
  switch (prop_id) {
653
    case PAD_PROP_CAPS:
654
      GST_OBJECT_LOCK (object);
655
      g_value_set_boxed (value, get_pad_caps (GST_PAD_CAST (object)));
656
      GST_OBJECT_UNLOCK (object);
657
658
659
660
661
      break;
    case PAD_PROP_DIRECTION:
      g_value_set_enum (value, GST_PAD_DIRECTION (object));
      break;
    case PAD_PROP_TEMPLATE:
662
      g_value_set_object (value, GST_PAD_PAD_TEMPLATE (object));
Wim Taymans's avatar
Wim Taymans committed
663
      break;
664
    default:
665
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
666
667
668
669
      break;
  }
}

670
/**
671
 * gst_pad_new:
672
673
 * @name: the name of the new pad.
 * @direction: the #GstPadDirection of the pad.
674
 *
675
 * Creates a new pad with the given name in the given direction.
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
676
 * If name is NULL, a guaranteed unique name (across all pads)
677
 * will be assigned.
678
 * This function makes a copy of the name so you can safely free the name.
679
 *
680
 * Returns: (transfer floating): a new #GstPad, or NULL in case of an error.
681
682
 *
 * MT safe.
683
 */
684
685
GstPad *
gst_pad_new (const gchar * name, GstPadDirection direction)
686
{
687
688
  return g_object_new (GST_TYPE_PAD,
      "name", name, "direction", direction, NULL);
689
}
690

691
/**
692
693
694
 * gst_pad_new_from_template:
 * @templ: the pad template to use
 * @name: the name of the element
695
 *
696
 * Creates a new pad with the given name from the given template.
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
697
 * If name is NULL, a guaranteed unique name (across all pads)
698
 * will be assigned.
699
 * This function makes a copy of the name so you can safely free the name.
700
 *
701
 * Returns: (transfer full): a new #GstPad, or NULL in case of an error.
702
 */
703
GstPad *
704
gst_pad_new_from_template (GstPadTemplate * templ, const gchar * name)
705
{
706
  g_return_val_if_fail (GST_IS_PAD_TEMPLATE (templ), NULL);
707

708
709
  return g_object_new (GST_TYPE_PAD,
      "name", name, "direction", templ->direction, "template", templ, NULL);
Erik Walthinsen's avatar
Erik Walthinsen committed
710
711
}

712
713
714
715
716
717
718
719
720
721
/**
 * gst_pad_new_from_static_template:
 * @templ: the #GstStaticPadTemplate to use
 * @name: the name of the element
 *
 * Creates a new pad with the given name from the given static template.
 * If name is NULL, a guaranteed unique name (across all pads)
 * will be assigned.
 * This function makes a copy of the name so you can safely free the name.
 *
722
 * Returns: (transfer full): a new #GstPad, or NULL in case of an error.
723
724
725
726
727
728
729
730
731
732
733
734
735
736
 */
GstPad *
gst_pad_new_from_static_template (GstStaticPadTemplate * templ,
    const gchar * name)
{
  GstPad *pad;
  GstPadTemplate *template;

  template = gst_static_pad_template_get (templ);
  pad = gst_pad_new_from_template (template, name);
  gst_object_unref (template);
  return pad;
}

737
738
739
740
741
742
743
744
745
746
747
748
749
750
#define ACQUIRE_PARENT(pad, parent, label)                      \
  G_STMT_START {                                                \
    if (G_LIKELY ((parent = GST_OBJECT_PARENT (pad))))          \
      gst_object_ref (parent);                                  \
    else if (G_LIKELY (GST_PAD_NEEDS_PARENT (pad)))             \
      goto label;                                               \
  } G_STMT_END

#define RELEASE_PARENT(parent)                                  \
  G_STMT_START {                                                \
    if (G_LIKELY (parent))                                      \
      gst_object_unref (parent);                                \
  } G_STMT_END

751
752
/**
 * gst_pad_get_direction:
753
 * @pad: a #GstPad to get the direction of.
754
 *
755
 * Gets the direction of the pad. The direction of the pad is
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
756
 * decided at construction time so this function does not take
757
 * the LOCK.
758
 *
759
 * Returns: the #GstPadDirection of the pad.
760
761
 *
 * MT safe.
762
 */
Wim Taymans's avatar
Wim Taymans committed
763
GstPadDirection
764
gst_pad_get_direction (GstPad * pad)
765
{
766
767
  GstPadDirection result;

768
  /* PAD_UNKNOWN is a little silly but we need some sort of
769
   * error return value */
770
  g_return_val_if_fail (GST_IS_PAD (pad), GST_PAD_UNKNOWN);
Erik Walthinsen's avatar
Erik Walthinsen committed
771

772
  result = GST_PAD_DIRECTION (pad);
773
774

  return result;
Erik Walthinsen's avatar
Erik Walthinsen committed
775
776
}

777
static gboolean
778
gst_pad_activate_default (GstPad * pad, GstObject * parent)
779
{
780
  return gst_pad_activate_mode (pad, GST_PAD_MODE_PUSH, TRUE);
781
782
783
}

static void
Wim Taymans's avatar
Wim Taymans committed
784
pre_activate (GstPad * pad, GstPadMode new_mode)
785
{
786
  switch (new_mode) {
Wim Taymans's avatar
Wim Taymans committed
787
    case GST_PAD_MODE_NONE:
788
      GST_OBJECT_LOCK (pad);
789
      GST_DEBUG_OBJECT (pad, "setting PAD_MODE NONE, set flushing");
790
      GST_PAD_SET_FLUSHING (pad);
Wim Taymans's avatar
Wim Taymans committed
791
      GST_PAD_MODE (pad) = new_mode;
792
      /* unlock blocked pads so element can resume and stop */
793
      GST_PAD_BLOCK_BROADCAST (pad);
794
      GST_OBJECT_UNLOCK (pad);
795
      break;
796
797
798
799
800
801
    case GST_PAD_MODE_PUSH:
    case GST_PAD_MODE_PULL:
      GST_OBJECT_LOCK (pad);
      GST_DEBUG_OBJECT (pad, "setting PAD_MODE %d, unset flushing", new_mode);
      GST_PAD_UNSET_FLUSHING (pad);
      GST_PAD_MODE (pad) = new_mode;
Wim Taymans's avatar
Wim Taymans committed
802
803
804
805
806
807
808
809
810
811
812
      if (GST_PAD_IS_SINK (pad)) {
        GstPad *peer;
        /* make sure the peer src pad sends us all events */
        if ((peer = GST_PAD_PEER (pad))) {
          gst_object_ref (peer);
          GST_OBJECT_UNLOCK (pad);

          GST_DEBUG_OBJECT (pad, "reschedule events on peer %s:%s",
              GST_DEBUG_PAD_NAME (peer));

          GST_OBJECT_LOCK (peer);
Wim Taymans's avatar
Wim Taymans committed
813
          schedule_events (peer, NULL);
Wim Taymans's avatar
Wim Taymans committed
814
815
816
817
818
819
820
821
822
          GST_OBJECT_UNLOCK (peer);

          gst_object_unref (peer);
        } else {
          GST_OBJECT_UNLOCK (pad);
        }
      } else {
        GST_OBJECT_UNLOCK (pad);
      }
823
      break;
824
825
826
827
  }
}

static void
Wim Taymans's avatar
Wim Taymans committed
828
post_activate (GstPad * pad, GstPadMode new_mode)
829
{
830
  switch (new_mode) {
Wim Taymans's avatar
Wim Taymans committed
831
    case GST_PAD_MODE_NONE:
832
      /* ensures that streaming stops */
833
      GST_PAD_STREAM_LOCK (pad);
834
      GST_DEBUG_OBJECT (pad, "stopped streaming");
Wim Taymans's avatar
Wim Taymans committed
835
      GST_OBJECT_LOCK (pad);
Wim Taymans's avatar
Wim Taymans committed
836
      remove_events (pad);
Wim Taymans's avatar
Wim Taymans committed
837
      GST_OBJECT_UNLOCK (pad);
838
      GST_PAD_STREAM_UNLOCK (pad);
839
      break;
840
841
842
843
    case GST_PAD_MODE_PUSH:
    case GST_PAD_MODE_PULL:
      /* NOP */
      break;
844
845
846
  }
}

847
848
/**
 * gst_pad_set_active:
849
 * @pad: the #GstPad to activate or deactivate.
850
 * @active: whether or not the pad should be active.
851
 *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
852
 * Activates or deactivates the given pad.
853
 * Normally called from within core state change functions.
Wim Taymans's avatar
Wim Taymans committed
854
 *
855
856
857
 * If @active, makes sure the pad is active. If it is already active, either in
 * push or pull mode, just return. Otherwise dispatches to the pad's activate
 * function to perform the actual activation.
Wim Taymans's avatar
Wim Taymans committed
858
 *
859
860
861
 * If not @active, checks the pad's current mode and calls
 * gst_pad_activate_push() or gst_pad_activate_pull(), as appropriate, with a
 * FALSE argument.
862
 *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
863
 * Returns: #TRUE if the operation was successful.
Wim Taymans's avatar
Wim Taymans committed
864
 *
865
 * MT safe.
Wim Taymans's avatar
Wim Taymans committed
866
867
 */
gboolean
868
gst_pad_set_active (GstPad * pad, gboolean active)
Wim Taymans's avatar
Wim Taymans committed
869
{
870
  GstObject *parent;
Wim Taymans's avatar
Wim Taymans committed
871
  GstPadMode old;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
872
  gboolean ret = FALSE;
Wim Taymans's avatar
Wim Taymans committed
873
874
875

  g_return_val_if_fail (GST_IS_PAD (pad), FALSE);

876
  GST_OBJECT_LOCK (pad);
Wim Taymans's avatar
Wim Taymans committed
877
  old = GST_PAD_MODE (pad);
878
  ACQUIRE_PARENT (pad, parent, no_parent);
879
  GST_OBJECT_UNLOCK (pad);
Wim Taymans's avatar
Wim Taymans committed
880

881
  if (active) {
882
883
884
885
886
887
    if (old == GST_PAD_MODE_NONE) {
      GST_DEBUG_OBJECT (pad, "activating pad from none");
      ret = (GST_PAD_ACTIVATEFUNC (pad)) (pad, parent);
    } else {
      GST_DEBUG_OBJECT (pad, "pad was active in mode %d", old);
      ret = TRUE;
888
889
    }
  } else {
890
891
892
893
894
895
    if (old == GST_PAD_MODE_NONE) {
      GST_DEBUG_OBJECT (pad, "pad was inactive");
      ret = TRUE;
    } else {
      GST_DEBUG_OBJECT (pad, "deactivating pad from mode %d", old);
      ret = gst_pad_activate_mode (pad, old, FALSE);
896
897
898
    }
  }

899
900
  RELEASE_PARENT (parent);

901
  if (G_UNLIKELY (!ret))
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
    goto failed;

  if (!active) {
    GST_OBJECT_LOCK (pad);
    GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_NEED_RECONFIGURE);
    GST_OBJECT_UNLOCK (pad);
  }
  return ret;

  /* ERRORS */
no_parent:
  {
    GST_DEBUG_OBJECT (pad, "no parent");
    GST_OBJECT_UNLOCK (pad);
    return FALSE;
  }
failed:
  {
920
    GST_OBJECT_LOCK (pad);
921
922
923
924
    if (!active) {
      g_critical ("Failed to deactivate pad %s:%s, very bad",
          GST_DEBUG_PAD_NAME (pad));
    } else {
925
      GST_WARNING_OBJECT (pad, "Failed to activate pad");
926
    }
927
    GST_OBJECT_UNLOCK (pad);
928
    return FALSE;
929
  }
930
}
931

932
/**
933
 * gst_pad_activate_mode:
934
 * @pad: the #GstPad to activate or deactivate.
935
 * @mode: the requested activation mode
936
937
 * @active: whether or not the pad should be active.
 *
938
939
 * Activates or deactivates the given pad in @mode via dispatching to the
 * pad's activatemodefunc. For use from within pad activation functions only.
940
941
942
 *
 * If you don't know what this is, you probably don't want to call it.
 *
943
 * Returns: TRUE if the operation was successful.
944
945
946
947
 *
 * MT safe.
 */
gboolean
948
gst_pad_activate_mode (GstPad * pad, GstPadMode mode, gboolean active)
949
{
950
951
  gboolean res = FALSE;
  GstObject *parent;
Wim Taymans's avatar
Wim Taymans committed
952
  GstPadMode old, new;
953
  GstPadDirection dir;
954
  GstPad *peer;
955
956
957

  g_return_val_if_fail (GST_IS_PAD (pad), FALSE);

958
  GST_OBJECT_LOCK (pad);
Wim Taymans's avatar
Wim Taymans committed
959
  old = GST_PAD_MODE (pad);
960
  dir = GST_PAD_DIRECTION (pad);
961
  ACQUIRE_PARENT (pad, parent, no_parent);
962
  GST_OBJECT_UNLOCK (pad);
963

964
965
966
967
968
969
970
971
972
973
974
  new = active ? mode : GST_PAD_MODE_NONE;

  if (old == new)
    goto was_ok;

  if (active && old != mode) {
    /* pad was activate in the wrong direction, deactivate it
     * and reactivate it in the requested mode */
    GST_DEBUG_OBJECT (pad, "deactivating pad from mode %d", old);
    if (G_UNLIKELY (!gst_pad_activate_mode (pad, old, FALSE)))
      goto deactivate_failed;
Wim Taymans's avatar
Wim Taymans committed
975
976
  }

977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
  switch (mode) {
    case GST_PAD_MODE_PULL:
    {
      if (dir == GST_PAD_SINK) {
        if ((peer = gst_pad_get_peer (pad))) {
          GST_DEBUG_OBJECT (pad, "calling peer");
          if (G_UNLIKELY (!gst_pad_activate_mode (peer, mode, active)))
            goto peer_failed;
          gst_object_unref (peer);
        } else {
          /* there is no peer, this is only fatal when we activate. When we
           * deactivate, we must assume the application has unlinked the peer and
           * will deactivate it eventually. */
          if (active)
            goto not_linked;
          else
            GST_DEBUG_OBJECT (pad, "deactivating unlinked pad");
        }
      } else {
        if (G_UNLIKELY (GST_PAD_GETRANGEFUNC (pad) == NULL))
          goto failure;         /* Can't activate pull on a src without a
998
                                   getrange function */
999
1000
1001
1002
1003
      }
      break;
    }
    default:
      break;
Wim Taymans's avatar
Wim Taymans committed
1004
1005
  }

1006
  pre_activate (pad, new);
Wim Taymans's avatar
Wim Taymans committed
1007

1008
1009
1010
  if (GST_PAD_ACTIVATEMODEFUNC (pad)) {
    if (G_UNLIKELY (!GST_PAD_ACTIVATEMODEFUNC (pad) (pad, parent, mode,
                active)))
1011
      goto failure;
1012
  } else {
1013
    /* can happen for sinks of passthrough elements */
1014
  }
Wim Taymans's avatar
Wim Taymans committed
1015

Wim Taymans's avatar