gstpad.c 135 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
Wim Taymans's avatar
Wim Taymans committed
39
 * capable of dealing with.  gst_pad_get_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 "gstmarshal.h"
69
#include "gstutils.h"
70
#include "gstinfo.h"
71
#include "gsterror.h"
David Schleef's avatar
David Schleef committed
72
#include "gstvalue.h"
73
#include "glib-compat-private.h"
David Schleef's avatar
David Schleef committed
74

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

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

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

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

99 100 101
/* 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. */
102 103
typedef struct
{
104
  GstEvent *pending;
105 106 107
  GstEvent *event;
} PadEvent;

Wim Taymans's avatar
Wim Taymans committed
108 109
struct _GstPadPrivate
{
110
  PadEvent events[GST_EVENT_MAX_STICKY];
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 GstCaps *gst_pad_get_caps_unlocked (GstPad * pad, GstCaps * filter);
143
static void gst_pad_set_pad_template (GstPad * pad, GstPadTemplate * templ);
144
static gboolean gst_pad_activate_default (GstPad * pad);
145 146
static GstFlowReturn gst_pad_chain_list_default (GstPad * pad,
    GstBufferList * list);
147

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

150 151
static GParamSpec *pspec_caps = NULL;

152
/* quarks for probe signals */
153
static GQuark buffer_quark;
154
static GQuark buffer_list_quark;
155 156
static GQuark event_quark;

157 158
typedef struct
{
159 160
  const gint ret;
  const gchar *name;
161 162 163 164
  GQuark quark;
} GstFlowQuarks;

static GstFlowQuarks flow_quarks[] = {
165
  {GST_FLOW_CUSTOM_SUCCESS, "custom-success", 0},
166 167 168 169
  {GST_FLOW_RESEND, "resend", 0},
  {GST_FLOW_OK, "ok", 0},
  {GST_FLOW_NOT_LINKED, "not-linked", 0},
  {GST_FLOW_WRONG_STATE, "wrong-state", 0},
170
  {GST_FLOW_EOS, "eos", 0},
171 172 173
  {GST_FLOW_NOT_NEGOTIATED, "not-negotiated", 0},
  {GST_FLOW_ERROR, "error", 0},
  {GST_FLOW_NOT_SUPPORTED, "not-supported", 0},
174
  {GST_FLOW_CUSTOM_ERROR, "custom-error", 0}
175 176 177 178 179 180 181 182
};

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

190 191
  ret = CLAMP (ret, GST_FLOW_CUSTOM_ERROR, GST_FLOW_CUSTOM_SUCCESS);

192
  for (i = 0; i < G_N_ELEMENTS (flow_quarks); i++) {
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
    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;

213 214
  ret = CLAMP (ret, GST_FLOW_CUSTOM_ERROR, GST_FLOW_CUSTOM_SUCCESS);

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

222 223 224 225 226
#define _do_init \
{ \
  gint i; \
  \
  buffer_quark = g_quark_from_static_string ("buffer"); \
227
  buffer_list_quark = g_quark_from_static_string ("bufferlist"); \
228 229
  event_quark = g_quark_from_static_string ("event"); \
  \
230
  for (i = 0; i < G_N_ELEMENTS (flow_quarks); i++) {			\
231 232 233 234 235
    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
236 237
}

238
#define gst_pad_parent_class parent_class
239 240
G_DEFINE_TYPE_WITH_CODE (GstPad, gst_pad, GST_TYPE_OBJECT, _do_init);

Erik Walthinsen's avatar
Erik Walthinsen committed
241
static void
242
gst_pad_class_init (GstPadClass * klass)
243
{
244
  GObjectClass *gobject_class;
245
  GstObjectClass *gstobject_class;
Erik Walthinsen's avatar
Erik Walthinsen committed
246

247 248
  gobject_class = G_OBJECT_CLASS (klass);
  gstobject_class = GST_OBJECT_CLASS (klass);
Erik Walthinsen's avatar
Erik Walthinsen committed
249

Wim Taymans's avatar
Wim Taymans committed
250 251
  g_type_class_add_private (klass, sizeof (GstPadPrivate));

252 253 254 255
  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
256

257 258 259 260 261 262 263
  /**
   * 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.
   */
264
  gst_pad_signals[PAD_LINKED] =
265
      g_signal_new ("linked", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
266
      G_STRUCT_OFFSET (GstPadClass, linked), NULL, NULL,
267
      gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_PAD);
268 269 270 271 272 273 274
  /**
   * 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.
   */
275
  gst_pad_signals[PAD_UNLINKED] =
276
      g_signal_new ("unlinked", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
277
      G_STRUCT_OFFSET (GstPadClass, unlinked), NULL, NULL,
278
      gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_PAD);
279

280 281 282 283 284
  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);

285
  g_object_class_install_property (gobject_class, PAD_PROP_DIRECTION,
286 287
      g_param_spec_enum ("direction", "Direction", "The direction of the pad",
          GST_TYPE_PAD_DIRECTION, GST_PAD_UNKNOWN,
288
          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
289

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

  gstobject_class->path_string_separator = ".";
297

298 299 300 301 302
  /* 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);
303
  GST_DEBUG_REGISTER_FUNCPTR (gst_pad_chain_list_default);
Erik Walthinsen's avatar
Erik Walthinsen committed
304 305
}

Wim Taymans's avatar
Wim Taymans committed
306
static void
307
gst_pad_init (GstPad * pad)
308
{
Wim Taymans's avatar
Wim Taymans committed
309
  pad->priv = GST_PAD_GET_PRIVATE (pad);
Wim Taymans's avatar
Wim Taymans committed
310

311
  GST_PAD_DIRECTION (pad) = GST_PAD_UNKNOWN;
312

313 314 315 316
  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;
317
  GST_PAD_CHAINLISTFUNC (pad) = gst_pad_chain_list_default;
318

319
  GST_PAD_SET_FLUSHING (pad);
Wim Taymans's avatar
Wim Taymans committed
320

321
  g_static_rec_mutex_init (&pad->stream_rec_lock);
Wim Taymans's avatar
Wim Taymans committed
322 323

  pad->block_cond = g_cond_new ();
324 325

  g_hook_list_init (&pad->probes, sizeof (GstProbe));
326 327
}

Wim Taymans's avatar
Wim Taymans committed
328 329 330 331 332 333 334
static void
clear_event (PadEvent events[], guint idx)
{
  gst_event_replace (&events[idx].event, NULL);
  gst_event_replace (&events[idx].pending, NULL);
}

335 336
/* called when setting the pad inactive. It removes all sticky events from
 * the pad */
337
static void
338
clear_events (PadEvent events[])
339 340 341
{
  guint i;

Wim Taymans's avatar
Wim Taymans committed
342 343
  for (i = 0; i < GST_EVENT_MAX_STICKY; i++)
    clear_event (events, i);
Wim Taymans's avatar
Wim Taymans committed
344 345
}

Wim Taymans's avatar
Wim Taymans committed
346
/* The sticky event with @idx from the srcpad is copied to the
347
 * pending event on the sinkpad (when different).
Wim Taymans's avatar
Wim Taymans committed
348 349 350
 * This function applies the pad offsets in case of segment events.
 * This will make sure that we send the event to the sinkpad event
 * function when the next buffer of event arrives.
351 352 353
 * Should be called with the OBJECT lock of both pads.
 * This function returns TRUE when there is a pending event on the
 * sinkpad */
354
static gboolean
Wim Taymans's avatar
Wim Taymans committed
355
replace_event (GstPad * srcpad, GstPad * sinkpad, guint idx)
356
{
Wim Taymans's avatar
Wim Taymans committed
357 358
  PadEvent *srcev, *sinkev;
  GstEvent *event;
359
  gboolean pending = FALSE;
360

Wim Taymans's avatar
Wim Taymans committed
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386
  srcev = &srcpad->priv->events[idx];

  if ((event = srcev->event)) {
    sinkev = &sinkpad->priv->events[idx];

    switch (GST_EVENT_TYPE (event)) {
      case GST_EVENT_SEGMENT:
      {
        GstSegment segment;
        gint64 offset;

        offset = srcpad->offset + sinkpad->offset;
        if (offset != 0) {
          gst_event_copy_segment (event, &segment);
          /* adjust the base time. FIXME, check negative times, try to tweak the
           * start to do clipping on negative times */
          segment.base += offset;
          /* make a new event from the updated segment */
          event = gst_event_new_segment (&segment);
        }
        break;
      }
      default:
        break;
    }
    if (sinkev->event != event) {
387
      /* put in the pending entry when different */
388 389 390
      GST_CAT_DEBUG_OBJECT (GST_CAT_SCHEDULING, srcpad,
          "Putting event %p (%s) on pad %s:%s", event,
          GST_EVENT_TYPE_NAME (event), GST_DEBUG_PAD_NAME (sinkpad));
391 392
      gst_event_replace (&sinkev->pending, event);
      pending = TRUE;
393
    }
394
  }
395
  return pending;
396 397
}

Wim Taymans's avatar
Wim Taymans committed
398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415

static void
prepare_event_update (GstPad * srcpad, GstPad * sinkpad)
{
  gboolean pending;
  gint i;

  /* make sure we push the events from the source to this new peer, for this we
   * copy the events on the sinkpad and mark EVENTS_PENDING */
  pending = FALSE;
  for (i = 0; i < GST_EVENT_MAX_STICKY; i++)
    pending |= replace_event (srcpad, sinkpad, i);

  /* we had some new pending events, set our flag */
  if (pending)
    GST_OBJECT_FLAG_SET (sinkpad, GST_PAD_NEED_EVENTS);
}

Wim Taymans's avatar
Wim Taymans committed
416
/* should be called with the OBJECT_LOCK */
417 418 419 420 421 422 423 424
static GstCaps *
get_pad_caps (GstPad * pad)
{
  GstCaps *caps = NULL;
  GstEvent *event;
  guint idx;

  idx = GST_EVENT_STICKY_IDX_TYPE (GST_EVENT_CAPS);
425
  /* we can only use the caps when we have successfully send the caps
426 427 428
   * event to the event function and is thus in the active entry */
  if ((event = pad->priv->events[idx].event))
    gst_event_parse_caps (event, &caps);
429 430 431

  return caps;
}
432

433
static void
434 435
gst_pad_dispose (GObject * object)
{
Wim Taymans's avatar
Wim Taymans committed
436
  GstPad *pad = GST_PAD_CAST (object);
437
  GstPad *peer;
438

439
  GST_CAT_DEBUG_OBJECT (GST_CAT_REFCOUNTING, pad, "dispose");
440

441 442 443 444 445 446 447 448 449 450 451 452
  /* 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);
  }
453

454
  gst_pad_set_pad_template (pad, NULL);
455

456
  clear_events (pad->priv->events);
457

458 459
  g_hook_list_clear (&pad->probes);

460
  G_OBJECT_CLASS (parent_class)->dispose (object);
461 462 463 464 465
}

static void
gst_pad_finalize (GObject * object)
{
Wim Taymans's avatar
Wim Taymans committed
466
  GstPad *pad = GST_PAD_CAST (object);
467 468 469 470 471 472 473 474
  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);
  }
475

476 477
  g_static_rec_mutex_free (&pad->stream_rec_lock);
  g_cond_free (pad->block_cond);
478

479
  G_OBJECT_CLASS (parent_class)->finalize (object);
480 481 482 483
}

static void
gst_pad_set_property (GObject * object, guint prop_id,
484
    const GValue * value, GParamSpec * pspec)
485
{
486
  g_return_if_fail (GST_IS_PAD (object));
487

488
  switch (prop_id) {
489
    case PAD_PROP_DIRECTION:
490
      GST_PAD_DIRECTION (object) = (GstPadDirection) g_value_get_enum (value);
491 492 493
      break;
    case PAD_PROP_TEMPLATE:
      gst_pad_set_pad_template (GST_PAD_CAST (object),
494
          (GstPadTemplate *) g_value_get_object (value));
495
      break;
496
    default:
497
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
498 499 500 501 502
      break;
  }
}

static void
503
gst_pad_get_property (GObject * object, guint prop_id,
504
    GValue * value, GParamSpec * pspec)
505 506 507
{
  g_return_if_fail (GST_IS_PAD (object));

508
  switch (prop_id) {
509
    case PAD_PROP_CAPS:
510
      GST_OBJECT_LOCK (object);
511
      g_value_set_boxed (value, get_pad_caps (GST_PAD_CAST (object)));
512
      GST_OBJECT_UNLOCK (object);
513 514 515 516 517
      break;
    case PAD_PROP_DIRECTION:
      g_value_set_enum (value, GST_PAD_DIRECTION (object));
      break;
    case PAD_PROP_TEMPLATE:
518
      g_value_set_object (value, GST_PAD_PAD_TEMPLATE (object));
Wim Taymans's avatar
Wim Taymans committed
519
      break;
520
    default:
521
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
522 523 524 525
      break;
  }
}

526
/**
527
 * gst_pad_new:
528 529
 * @name: the name of the new pad.
 * @direction: the #GstPadDirection of the pad.
530
 *
531
 * Creates a new pad with the given name in the given direction.
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
532
 * If name is NULL, a guaranteed unique name (across all pads)
533
 * will be assigned.
534
 * This function makes a copy of the name so you can safely free the name.
535
 *
536
 * Returns: (transfer full): a new #GstPad, or NULL in case of an error.
537 538
 *
 * MT safe.
539
 */
540 541
GstPad *
gst_pad_new (const gchar * name, GstPadDirection direction)
542
{
543 544
  return g_object_new (GST_TYPE_PAD,
      "name", name, "direction", direction, NULL);
545
}
546

547
/**
548 549 550
 * gst_pad_new_from_template:
 * @templ: the pad template to use
 * @name: the name of the element
551
 *
552
 * Creates a new pad with the given name from the given template.
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
553
 * If name is NULL, a guaranteed unique name (across all pads)
554
 * will be assigned.
555
 * This function makes a copy of the name so you can safely free the name.
556
 *
557
 * Returns: (transfer full): a new #GstPad, or NULL in case of an error.
558
 */
559
GstPad *
560
gst_pad_new_from_template (GstPadTemplate * templ, const gchar * name)
561
{
562
  g_return_val_if_fail (GST_IS_PAD_TEMPLATE (templ), NULL);
563

564 565
  return g_object_new (GST_TYPE_PAD,
      "name", name, "direction", templ->direction, "template", templ, NULL);
Erik Walthinsen's avatar
Erik Walthinsen committed
566 567
}

568 569 570 571 572 573 574 575 576 577
/**
 * 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.
 *
578
 * Returns: (transfer full): a new #GstPad, or NULL in case of an error.
579 580 581 582 583 584 585 586 587 588 589 590 591 592
 */
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;
}

593 594
/**
 * gst_pad_get_direction:
595
 * @pad: a #GstPad to get the direction of.
596
 *
597
 * Gets the direction of the pad. The direction of the pad is
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
598
 * decided at construction time so this function does not take
599
 * the LOCK.
600
 *
601
 * Returns: the #GstPadDirection of the pad.
602 603
 *
 * MT safe.
604
 */
Wim Taymans's avatar
Wim Taymans committed
605
GstPadDirection
606
gst_pad_get_direction (GstPad * pad)
607
{
608 609
  GstPadDirection result;

610
  /* PAD_UNKNOWN is a little silly but we need some sort of
611
   * error return value */
612
  g_return_val_if_fail (GST_IS_PAD (pad), GST_PAD_UNKNOWN);
Erik Walthinsen's avatar
Erik Walthinsen committed
613

614
  result = GST_PAD_DIRECTION (pad);
615 616

  return result;
Erik Walthinsen's avatar
Erik Walthinsen committed
617 618
}

619 620 621 622 623 624 625
static gboolean
gst_pad_activate_default (GstPad * pad)
{
  return gst_pad_activate_push (pad, TRUE);
}

static void
626
pre_activate (GstPad * pad, GstPadActivateMode new_mode)
627
{
628
  switch (new_mode) {
629 630
    case GST_PAD_ACTIVATE_PUSH:
    case GST_PAD_ACTIVATE_PULL:
631
      GST_OBJECT_LOCK (pad);
632 633
      GST_DEBUG_OBJECT (pad, "setting ACTIVATE_MODE %d, unset flushing",
          new_mode);
634 635
      GST_PAD_UNSET_FLUSHING (pad);
      GST_PAD_ACTIVATE_MODE (pad) = new_mode;
636
      GST_OBJECT_UNLOCK (pad);
637
      break;
638
    case GST_PAD_ACTIVATE_NONE:
639
      GST_OBJECT_LOCK (pad);
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
640
      GST_DEBUG_OBJECT (pad, "setting ACTIVATE_MODE NONE, set flushing");
641
      GST_PAD_SET_FLUSHING (pad);
642
      GST_PAD_ACTIVATE_MODE (pad) = new_mode;
643
      /* unlock blocked pads so element can resume and stop */
644
      GST_PAD_BLOCK_BROADCAST (pad);
645
      GST_OBJECT_UNLOCK (pad);
646
      break;
647 648 649 650
  }
}

static void
651
post_activate (GstPad * pad, GstPadActivateMode new_mode)
652
{
653
  switch (new_mode) {
654 655
    case GST_PAD_ACTIVATE_PUSH:
    case GST_PAD_ACTIVATE_PULL:
656 657
      /* nop */
      break;
658
    case GST_PAD_ACTIVATE_NONE:
659
      /* ensures that streaming stops */
660
      GST_PAD_STREAM_LOCK (pad);
661
      GST_DEBUG_OBJECT (pad, "stopped streaming");
Wim Taymans's avatar
Wim Taymans committed
662
      GST_OBJECT_LOCK (pad);
663
      clear_events (pad->priv->events);
Wim Taymans's avatar
Wim Taymans committed
664
      GST_OBJECT_UNLOCK (pad);
665
      GST_PAD_STREAM_UNLOCK (pad);
666
      break;
667 668 669
  }
}

670 671
/**
 * gst_pad_set_active:
672
 * @pad: the #GstPad to activate or deactivate.
673
 * @active: whether or not the pad should be active.
674
 *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
675
 * Activates or deactivates the given pad.
676
 * Normally called from within core state change functions.
Wim Taymans's avatar
Wim Taymans committed
677
 *
678 679 680
 * 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
681
 *
682 683 684
 * 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.
685
 *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
686
 * Returns: #TRUE if the operation was successful.
Wim Taymans's avatar
Wim Taymans committed
687
 *
688
 * MT safe.
Wim Taymans's avatar
Wim Taymans committed
689 690
 */
gboolean
691
gst_pad_set_active (GstPad * pad, gboolean active)
Wim Taymans's avatar
Wim Taymans committed
692
{
693
  GstPadActivateMode old;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
694
  gboolean ret = FALSE;
Wim Taymans's avatar
Wim Taymans committed
695 696 697

  g_return_val_if_fail (GST_IS_PAD (pad), FALSE);

698
  GST_OBJECT_LOCK (pad);
699
  old = GST_PAD_ACTIVATE_MODE (pad);
700
  GST_OBJECT_UNLOCK (pad);
Wim Taymans's avatar
Wim Taymans committed
701

702 703
  if (active) {
    switch (old) {
704
      case GST_PAD_ACTIVATE_PUSH:
705 706 707
        GST_DEBUG_OBJECT (pad, "activating pad from push");
        ret = TRUE;
        break;
708
      case GST_PAD_ACTIVATE_PULL:
709
        GST_DEBUG_OBJECT (pad, "activating pad from pull");
710 711
        ret = TRUE;
        break;
712
      case GST_PAD_ACTIVATE_NONE:
713
        GST_DEBUG_OBJECT (pad, "activating pad from none");
714 715
        ret = (GST_PAD_ACTIVATEFUNC (pad)) (pad);
        break;
Wim Taymans's avatar
Wim Taymans committed
716 717 718
      default:
        GST_DEBUG_OBJECT (pad, "unknown activation mode!");
        break;
719 720 721
    }
  } else {
    switch (old) {
722
      case GST_PAD_ACTIVATE_PUSH:
723
        GST_DEBUG_OBJECT (pad, "deactivating pad from push");
724 725
        ret = gst_pad_activate_push (pad, FALSE);
        break;
726
      case GST_PAD_ACTIVATE_PULL:
727
        GST_DEBUG_OBJECT (pad, "deactivating pad from pull");
728 729
        ret = gst_pad_activate_pull (pad, FALSE);
        break;
730
      case GST_PAD_ACTIVATE_NONE:
731
        GST_DEBUG_OBJECT (pad, "deactivating pad from none");
732 733
        ret = TRUE;
        break;
Wim Taymans's avatar
Wim Taymans committed
734 735 736
      default:
        GST_DEBUG_OBJECT (pad, "unknown activation mode!");
        break;
737 738 739
    }
  }

740
  if (!ret) {
741
    GST_OBJECT_LOCK (pad);
742 743 744 745
    if (!active) {
      g_critical ("Failed to deactivate pad %s:%s, very bad",
          GST_DEBUG_PAD_NAME (pad));
    } else {
746
      GST_WARNING_OBJECT (pad, "Failed to activate pad");
747
    }
748
    GST_OBJECT_UNLOCK (pad);
749 750 751 752 753 754
  } else {
    if (!active) {
      GST_OBJECT_LOCK (pad);
      GST_OBJECT_FLAG_UNSET (pad, GST_PAD_NEED_RECONFIGURE);
      GST_OBJECT_UNLOCK (pad);
    }
755 756
  }

757 758
  return ret;
}
759

760 761 762 763 764 765 766
/**
 * gst_pad_activate_pull:
 * @pad: the #GstPad to activate or deactivate.
 * @active: whether or not the pad should be active.
 *
 * Activates or deactivates the given pad in pull mode via dispatching to the
 * pad's activatepullfunc. For use from within pad activation functions only.
767 768 769
 * When called on sink pads, will first proxy the call to the peer pad, which
 * is expected to activate its internally linked pads from within its
 * activate_pull function.
770 771 772
 *
 * If you don't know what this is, you probably don't want to call it.
 *
773
 * Returns: TRUE if the operation was successful.
774 775 776 777 778 779
 *
 * MT safe.
 */
gboolean
gst_pad_activate_pull (GstPad * pad, gboolean active)
{
780
  GstPadActivateMode old, new;
781
  GstPad *peer;
782 783 784

  g_return_val_if_fail (GST_IS_PAD (pad), FALSE);

785
  GST_OBJECT_LOCK (pad);
786
  old = GST_PAD_ACTIVATE_MODE (pad);
787
  GST_OBJECT_UNLOCK (pad);
788 789

  if (active) {
790
    switch (old) {
791
      case GST_PAD_ACTIVATE_PULL:
792
        GST_DEBUG_OBJECT (pad, "activating pad from pull, was ok");
793
        goto was_ok;
794
      case GST_PAD_ACTIVATE_PUSH:
795 796
        GST_DEBUG_OBJECT (pad,
            "activating pad from push, deactivate push first");
797 798 799 800 801
        /* pad was activate in the wrong direction, deactivate it
         * and reactivate it in pull mode */
        if (G_UNLIKELY (!gst_pad_activate_push (pad, FALSE)))
          goto deactivate_failed;
        /* fallthrough, pad is deactivated now. */
802
      case GST_PAD_ACTIVATE_NONE:
803
        GST_DEBUG_OBJECT (pad, "activating pad from none");
804 805
        break;
    }
806
  } else {
807
    switch (old) {
808
      case GST_PAD_ACTIVATE_NONE:
809
        GST_DEBUG_OBJECT (pad, "deactivating pad from none, was ok");
810
        goto was_ok;
811
      case GST_PAD_ACTIVATE_PUSH:
812
        GST_DEBUG_OBJECT (pad, "deactivating pad from push, weird");
813 814 815 816 817 818
        /* pad was activated in the other direction, deactivate it
         * in push mode, this should not happen... */
        if (G_UNLIKELY (!gst_pad_activate_push (pad, FALSE)))
          goto deactivate_failed;
        /* everything is fine now */
        goto was_ok;
819
      case GST_PAD_ACTIVATE_PULL:
820
        GST_DEBUG_OBJECT (pad, "deactivating pad from pull");
821 822
        break;
    }
Wim Taymans's avatar
Wim Taymans committed
823 824
  }

825
  if (gst_pad_get_direction (pad) == GST_PAD_SINK) {
826
    if ((peer = gst_pad_get_peer (pad))) {
827
      GST_DEBUG_OBJECT (pad, "calling peer");
828
      if (G_UNLIKELY (!gst_pad_activate_pull (peer, active)))
Wim Taymans's avatar