gstpad.c 139 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
Wim Taymans's avatar
Wim Taymans committed
26
 * @see_also: #GstPadTemplate, #GstElement, #GstEvent, #GstQuery, #GstBuffer
27
 *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
28 29
 * A #GstElement is linked to other elements via "pads", which are extremely
 * light-weight generic link points.
30
 *
Wim Taymans's avatar
Wim Taymans committed
31 32
 * Pads have a #GstPadDirection, source pads produce data, sink pads consume
 * data.
33
 *
Wim Taymans's avatar
Wim Taymans committed
34 35 36 37 38
 * Pads are typically created from a #GstPadTemplate with
 * gst_pad_new_from_template() and are then added to a #GstElement. This usually
 * happens when the element is created but it can also happen dynamically based
 * on the data that the element is processing or based on the pads that the
 * application requests.
39 40 41 42 43
 *
 * 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.
 *
Wim Taymans's avatar
Wim Taymans committed
44 45 46 47
 * A #GstElement creating a pad will typically use the various
 * gst_pad_set_*_function() calls to register callbacks for events, queries or
 * dataflow on the pads.
 *
48 49
 * gst_pad_get_parent() will retrieve the #GstElement that owns the pad.
 *
Wim Taymans's avatar
Wim Taymans committed
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
 * After two pads are retrieved from an element with gst_element_get_pad(),
 * the pads can be linked 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.). Pads can be unlinked again with
 * gst_pad_unlink(). gst_pad_get_peer() can be used to check what the pad is
 * linked to.
 *
 * Before dataflow is possible on the pads, they need to be activated with
 * gst_pad_set_active().
 *
 * gst_pad_query() and gst_pad_peer_query() can be used to query various
 * properties of the pad and the stream.
 *
 * To send a #GstEvent on a pad, use gst_pad_send_event() and
 * gst_pad_push_event(). Some events will be sticky on the pad, meaning that
 * after they pass on the pad they can be queried later with
 * gst_pad_get_sticky_event() and gst_pad_sticky_events_foreach().
 * gst_pad_get_current_caps() and gst_pad_has_current_caps() are convenience
 * functions to query the current sticky CAPS event on a pad.
69
 *
Wim Taymans's avatar
Wim Taymans committed
70
 * GstElements will use gst_pad_push() and gst_pad_pull_range() to push out
71 72
 * or pull in a buffer.
 *
Wim Taymans's avatar
Wim Taymans committed
73 74 75 76 77 78 79 80 81 82 83 84 85 86
 * The dataflow, events and queries that happen on a pad can be monitored with
 * probes that can be installed with gst_pad_add_probe(). gst_pad_is_blocked()
 * can be used to check if a block probe is installed on the pad.
 * gst_pad_is_blocking() checks if the blocking probe is currently blocking the
 * pad. gst_pad_remove_probe() is used to remove a previously installed probe
 * and unblock blocking probes if any.
 *
 * Pad have an offset that can be retrieved with gst_pad_get_offset(). This
 * offset will be applied to the running_time of all data passing over the pad.
 * gst_pad_set_offset() can be used to change the offset.
 *
 * Convenience functions exist to start, pause and stop the task on a pad with
 * gst_pad_start_task(), gst_pad_pause_task() and gst_pad_stop_task()
 * respectively.
Wim Taymans's avatar
Wim Taymans committed
87
 *
Wim Taymans's avatar
Wim Taymans committed
88
 * Last reviewed on 2012-03-29 (0.11.3)
89
 */
Erik Walthinsen's avatar
Erik Walthinsen committed
90

91 92
#include "gst_private.h"

93
#include "gstpad.h"
94
#include "gstpadtemplate.h"
95
#include "gstenumtypes.h"
96
#include "gstutils.h"
97
#include "gstinfo.h"
98
#include "gsterror.h"
David Schleef's avatar
David Schleef committed
99
#include "gstvalue.h"
100
#include "glib-compat-private.h"
David Schleef's avatar
David Schleef committed
101

102
GST_DEBUG_CATEGORY_STATIC (debug_dataflow);
103 104
#define GST_CAT_DEFAULT GST_CAT_PADS

105 106 107 108 109 110
/* Pad signals and args */
enum
{
  PAD_LINKED,
  PAD_UNLINKED,
  /* FILL ME */
111
  LAST_SIGNAL
112 113 114 115 116 117 118 119 120 121 122
};

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

Wim Taymans's avatar
Wim Taymans committed
123 124 125
#define GST_PAD_GET_PRIVATE(obj)  \
   (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_PAD, GstPadPrivate))

126 127 128
/* 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. */
129 130
typedef struct
{
Wim Taymans's avatar
Wim Taymans committed
131
  gboolean received;
132 133 134
  GstEvent *event;
} PadEvent;

Wim Taymans's avatar
Wim Taymans committed
135 136
struct _GstPadPrivate
{
Wim Taymans's avatar
Wim Taymans committed
137 138
  guint events_cookie;
  GArray *events;
139 140

  gint using;
Wim Taymans's avatar
Wim Taymans committed
141 142
  guint probe_list_cookie;
  guint probe_cookie;
Wim Taymans's avatar
Wim Taymans committed
143 144
};

145 146 147 148 149 150 151 152 153 154 155
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
156
  GstPadProbeInfo *info;
Wim Taymans's avatar
Wim Taymans committed
157
  gboolean dropped;
158
  gboolean pass;
Wim Taymans's avatar
Wim Taymans committed
159
  gboolean marshalled;
160 161 162
  guint cookie;
} ProbeMarshall;

163
static void gst_pad_dispose (GObject * object);
164 165 166 167 168
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);
169

170
static void gst_pad_set_pad_template (GstPad * pad, GstPadTemplate * templ);
171
static gboolean gst_pad_activate_default (GstPad * pad, GstObject * parent);
172
static GstFlowReturn gst_pad_chain_list_default (GstPad * pad,
173
    GstObject * parent, GstBufferList * list);
174

Wim Taymans's avatar
Wim Taymans committed
175 176 177
static GstFlowReturn gst_pad_send_event_unchecked (GstPad * pad,
    GstEvent * event, GstPadProbeType type);
static GstFlowReturn gst_pad_push_event_unchecked (GstPad * pad,
178
    GstEvent * event, GstPadProbeType type);
Wim Taymans's avatar
Wim Taymans committed
179

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

182 183
static GParamSpec *pspec_caps = NULL;

184
/* quarks for probe signals */
185
static GQuark buffer_quark;
186
static GQuark buffer_list_quark;
187 188
static GQuark event_quark;

189 190
typedef struct
{
191 192
  const gint ret;
  const gchar *name;
193 194 195 196
  GQuark quark;
} GstFlowQuarks;

static GstFlowQuarks flow_quarks[] = {
197
  {GST_FLOW_CUSTOM_SUCCESS, "custom-success", 0},
198 199
  {GST_FLOW_OK, "ok", 0},
  {GST_FLOW_NOT_LINKED, "not-linked", 0},
200
  {GST_FLOW_FLUSHING, "flushing", 0},
201
  {GST_FLOW_EOS, "eos", 0},
202 203 204
  {GST_FLOW_NOT_NEGOTIATED, "not-negotiated", 0},
  {GST_FLOW_ERROR, "error", 0},
  {GST_FLOW_NOT_SUPPORTED, "not-supported", 0},
205
  {GST_FLOW_CUSTOM_ERROR, "custom-error", 0}
206 207 208 209 210 211 212 213
};

/**
 * gst_flow_get_name:
 * @ret: a #GstFlowReturn to get the name of.
 *
 * Gets a string representing the given flow return.
 *
214
 * Returns: a static string with the name of the flow return.
215
 */
216
const gchar *
217 218 219 220
gst_flow_get_name (GstFlowReturn ret)
{
  gint i;

221 222
  ret = CLAMP (ret, GST_FLOW_CUSTOM_ERROR, GST_FLOW_CUSTOM_SUCCESS);

223
  for (i = 0; i < G_N_ELEMENTS (flow_quarks); i++) {
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243
    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;

244 245
  ret = CLAMP (ret, GST_FLOW_CUSTOM_ERROR, GST_FLOW_CUSTOM_SUCCESS);

246
  for (i = 0; i < G_N_ELEMENTS (flow_quarks); i++) {
247 248 249 250 251 252
    if (ret == flow_quarks[i].ret)
      return flow_quarks[i].quark;
  }
  return 0;
}

253 254 255 256 257
#define _do_init \
{ \
  gint i; \
  \
  buffer_quark = g_quark_from_static_string ("buffer"); \
258
  buffer_list_quark = g_quark_from_static_string ("bufferlist"); \
259 260
  event_quark = g_quark_from_static_string ("event"); \
  \
261
  for (i = 0; i < G_N_ELEMENTS (flow_quarks); i++) {			\
262 263 264 265 266
    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
267 268
}

269
#define gst_pad_parent_class parent_class
270 271
G_DEFINE_TYPE_WITH_CODE (GstPad, gst_pad, GST_TYPE_OBJECT, _do_init);

Erik Walthinsen's avatar
Erik Walthinsen committed
272
static void
273
gst_pad_class_init (GstPadClass * klass)
274
{
275
  GObjectClass *gobject_class;
276
  GstObjectClass *gstobject_class;
Erik Walthinsen's avatar
Erik Walthinsen committed
277

278 279
  gobject_class = G_OBJECT_CLASS (klass);
  gstobject_class = GST_OBJECT_CLASS (klass);
Erik Walthinsen's avatar
Erik Walthinsen committed
280

Wim Taymans's avatar
Wim Taymans committed
281 282
  g_type_class_add_private (klass, sizeof (GstPadPrivate));

283 284 285 286
  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
287

288 289 290 291 292 293 294
  /**
   * 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.
   */
295
  gst_pad_signals[PAD_LINKED] =
296
      g_signal_new ("linked", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
297
      G_STRUCT_OFFSET (GstPadClass, linked), NULL, NULL,
298
      g_cclosure_marshal_generic, G_TYPE_NONE, 1, GST_TYPE_PAD);
299 300 301 302 303 304 305
  /**
   * 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.
   */
306
  gst_pad_signals[PAD_UNLINKED] =
307
      g_signal_new ("unlinked", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
308
      G_STRUCT_OFFSET (GstPadClass, unlinked), NULL, NULL,
309
      g_cclosure_marshal_generic, G_TYPE_NONE, 1, GST_TYPE_PAD);
310

311 312 313 314 315
  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);

316
  g_object_class_install_property (gobject_class, PAD_PROP_DIRECTION,
317 318
      g_param_spec_enum ("direction", "Direction", "The direction of the pad",
          GST_TYPE_PAD_DIRECTION, GST_PAD_UNKNOWN,
319
          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
320

321
  /* FIXME, Make G_PARAM_CONSTRUCT_ONLY when we fix ghostpads. */
322
  g_object_class_install_property (gobject_class, PAD_PROP_TEMPLATE,
323 324
      g_param_spec_object ("template", "Template",
          "The GstPadTemplate of this pad", GST_TYPE_PAD_TEMPLATE,
325
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
326 327

  gstobject_class->path_string_separator = ".";
328

329 330 331 332 333
  /* 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);
334
  GST_DEBUG_REGISTER_FUNCPTR (gst_pad_chain_list_default);
Erik Walthinsen's avatar
Erik Walthinsen committed
335 336
}

Wim Taymans's avatar
Wim Taymans committed
337
static void
338
gst_pad_init (GstPad * pad)
339
{
Wim Taymans's avatar
Wim Taymans committed
340
  pad->priv = GST_PAD_GET_PRIVATE (pad);
Wim Taymans's avatar
Wim Taymans committed
341

342
  GST_PAD_DIRECTION (pad) = GST_PAD_UNKNOWN;
343

344 345 346 347
  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;
348
  GST_PAD_CHAINLISTFUNC (pad) = gst_pad_chain_list_default;
349

350
  GST_PAD_SET_FLUSHING (pad);
Wim Taymans's avatar
Wim Taymans committed
351

Wim Taymans's avatar
Wim Taymans committed
352
  g_rec_mutex_init (&pad->stream_rec_lock);
Wim Taymans's avatar
Wim Taymans committed
353

Wim Taymans's avatar
Wim Taymans committed
354
  g_cond_init (&pad->block_cond);
355 356

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

358
  pad->priv->events = g_array_sized_new (FALSE, TRUE, sizeof (PadEvent), 16);
Wim Taymans's avatar
Wim Taymans committed
359 360
}

361 362
/* called when setting the pad inactive. It removes all sticky events from
 * the pad */
363
static void
Wim Taymans's avatar
Wim Taymans committed
364
remove_events (GstPad * pad)
365
{
Wim Taymans's avatar
Wim Taymans committed
366 367
  guint i, len;
  GArray *events;
368

Wim Taymans's avatar
Wim Taymans committed
369
  events = pad->priv->events;
Wim Taymans's avatar
Wim Taymans committed
370

Wim Taymans's avatar
Wim Taymans committed
371 372 373 374 375
  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
376
  GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_PENDING_EVENTS);
Wim Taymans's avatar
Wim Taymans committed
377 378
  g_array_set_size (events, 0);
  pad->priv->events_cookie++;
Wim Taymans's avatar
Wim Taymans committed
379 380
}

Wim Taymans's avatar
Wim Taymans committed
381 382
static PadEvent *
find_event_by_type (GstPad * pad, GstEventType type, guint idx)
383
{
Wim Taymans's avatar
Wim Taymans committed
384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400
  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--;
    }
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
  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;
425 426
}

Wim Taymans's avatar
Wim Taymans committed
427
static void
Wim Taymans's avatar
Wim Taymans committed
428
remove_event_by_type (GstPad * pad, GstEventType type)
Wim Taymans's avatar
Wim Taymans committed
429
{
Wim Taymans's avatar
Wim Taymans committed
430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456
  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
457 458 459
/* 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
460 461 462 463 464 465
static void
schedule_events (GstPad * srcpad, GstPad * sinkpad)
{
  gint i, len;
  GArray *events;
  PadEvent *ev;
Wim Taymans's avatar
Wim Taymans committed
466
  gboolean pending = FALSE;
Wim Taymans's avatar
Wim Taymans committed
467

Wim Taymans's avatar
Wim Taymans committed
468 469 470 471 472 473 474
  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
475

Wim Taymans's avatar
Wim Taymans committed
476 477 478 479 480
    if (sinkpad == NULL || !find_event (sinkpad, ev->event)) {
      ev->received = FALSE;
      pending = TRUE;
    }
  }
Wim Taymans's avatar
Wim Taymans committed
481
  if (pending)
Wim Taymans's avatar
Wim Taymans committed
482
    GST_OBJECT_FLAG_SET (srcpad, GST_PAD_FLAG_PENDING_EVENTS);
Wim Taymans's avatar
Wim Taymans committed
483 484
}

Wim Taymans's avatar
Wim Taymans committed
485 486 487
typedef gboolean (*PadEventFunction) (GstPad * pad, PadEvent * ev,
    gpointer user_data);

Wim Taymans's avatar
Wim Taymans committed
488
/* should be called with pad LOCK */
Wim Taymans's avatar
Wim Taymans committed
489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523
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;
    }

524 525 526
    /* store the received state */
    ev->received = ev_ret.received;

Wim Taymans's avatar
Wim Taymans committed
527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
    /* 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
549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566

/* 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
567 568
}

Wim Taymans's avatar
Wim Taymans committed
569
/* should be called with the OBJECT_LOCK */
570 571 572 573
static GstCaps *
get_pad_caps (GstPad * pad)
{
  GstCaps *caps = NULL;
Wim Taymans's avatar
Wim Taymans committed
574
  PadEvent *ev;
575

Wim Taymans's avatar
Wim Taymans committed
576 577 578
  ev = find_event_by_type (pad, GST_EVENT_CAPS, 0);
  if (ev && ev->event)
    gst_event_parse_caps (ev->event, &caps);
579 580 581

  return caps;
}
582

583
static void
584 585
gst_pad_dispose (GObject * object)
{
Wim Taymans's avatar
Wim Taymans committed
586
  GstPad *pad = GST_PAD_CAST (object);
587
  GstPad *peer;
588

589
  GST_CAT_DEBUG_OBJECT (GST_CAT_REFCOUNTING, pad, "dispose");
590

591 592 593 594 595 596 597 598 599 600 601 602
  /* 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);
  }
603

604
  gst_pad_set_pad_template (pad, NULL);
605

Wim Taymans's avatar
Wim Taymans committed
606
  remove_events (pad);
607

608 609
  g_hook_list_clear (&pad->probes);

610
  G_OBJECT_CLASS (parent_class)->dispose (object);
611 612 613 614 615
}

static void
gst_pad_finalize (GObject * object)
{
Wim Taymans's avatar
Wim Taymans committed
616
  GstPad *pad = GST_PAD_CAST (object);
617 618 619 620 621 622 623 624
  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);
  }
625

626
  if (pad->activatenotify)
627
    pad->activatenotify (pad->activatedata);
628
  if (pad->activatemodenotify)
629
    pad->activatemodenotify (pad->activatemodedata);
630
  if (pad->linknotify)
631
    pad->linknotify (pad->linkdata);
632
  if (pad->unlinknotify)
633
    pad->unlinknotify (pad->unlinkdata);
634
  if (pad->chainnotify)
635
    pad->chainnotify (pad->chaindata);
636
  if (pad->chainlistnotify)
637
    pad->chainlistnotify (pad->chainlistdata);
638
  if (pad->getrangenotify)
639
    pad->getrangenotify (pad->getrangedata);
640
  if (pad->eventnotify)
641
    pad->eventnotify (pad->eventdata);
642
  if (pad->querynotify)
643
    pad->querynotify (pad->querydata);
644
  if (pad->iterintlinknotify)
645
    pad->iterintlinknotify (pad->iterintlinkdata);
646

Wim Taymans's avatar
Wim Taymans committed
647 648
  g_rec_mutex_clear (&pad->stream_rec_lock);
  g_cond_clear (&pad->block_cond);
Wim Taymans's avatar
Wim Taymans committed
649
  g_array_free (pad->priv->events, TRUE);
650

651
  G_OBJECT_CLASS (parent_class)->finalize (object);
652 653 654 655
}

static void
gst_pad_set_property (GObject * object, guint prop_id,
656
    const GValue * value, GParamSpec * pspec)
657
{
658
  g_return_if_fail (GST_IS_PAD (object));
659

660
  switch (prop_id) {
661
    case PAD_PROP_DIRECTION:
662
      GST_PAD_DIRECTION (object) = (GstPadDirection) g_value_get_enum (value);
663 664 665
      break;
    case PAD_PROP_TEMPLATE:
      gst_pad_set_pad_template (GST_PAD_CAST (object),
666
          (GstPadTemplate *) g_value_get_object (value));
667
      break;
668
    default:
669
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
670 671 672 673 674
      break;
  }
}

static void
675
gst_pad_get_property (GObject * object, guint prop_id,
676
    GValue * value, GParamSpec * pspec)
677 678 679
{
  g_return_if_fail (GST_IS_PAD (object));

680
  switch (prop_id) {
681
    case PAD_PROP_CAPS:
682
      GST_OBJECT_LOCK (object);
683
      g_value_set_boxed (value, get_pad_caps (GST_PAD_CAST (object)));
684
      GST_OBJECT_UNLOCK (object);
685 686 687 688 689
      break;
    case PAD_PROP_DIRECTION:
      g_value_set_enum (value, GST_PAD_DIRECTION (object));
      break;
    case PAD_PROP_TEMPLATE:
690
      g_value_set_object (value, GST_PAD_PAD_TEMPLATE (object));
Wim Taymans's avatar
Wim Taymans committed
691
      break;
692
    default:
693
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
694 695 696 697
      break;
  }
}

698
/**
699
 * gst_pad_new:
700 701
 * @name: the name of the new pad.
 * @direction: the #GstPadDirection of the pad.
702
 *
703
 * Creates a new pad with the given name in the given direction.
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
704
 * If name is NULL, a guaranteed unique name (across all pads)
705
 * will be assigned.
706
 * This function makes a copy of the name so you can safely free the name.
707
 *
708
 * Returns: (transfer floating): a new #GstPad, or NULL in case of an error.
709 710
 *
 * MT safe.
711
 */
712 713
GstPad *
gst_pad_new (const gchar * name, GstPadDirection direction)
714
{
715 716
  return g_object_new (GST_TYPE_PAD,
      "name", name, "direction", direction, NULL);
717
}
718

719
/**
720 721 722
 * gst_pad_new_from_template:
 * @templ: the pad template to use
 * @name: the name of the element
723
 *
724
 * Creates a new pad with the given name from the given template.
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
725
 * If name is NULL, a guaranteed unique name (across all pads)
726
 * will be assigned.
727
 * This function makes a copy of the name so you can safely free the name.
728
 *
729
 * Returns: (transfer full): a new #GstPad, or NULL in case of an error.
730
 */
731
GstPad *
732
gst_pad_new_from_template (GstPadTemplate * templ, const gchar * name)
733
{
734
  g_return_val_if_fail (GST_IS_PAD_TEMPLATE (templ), NULL);
735

736 737
  return g_object_new (GST_TYPE_PAD,
      "name", name, "direction", templ->direction, "template", templ, NULL);
Erik Walthinsen's avatar
Erik Walthinsen committed
738 739
}

740 741 742 743 744 745 746 747 748 749
/**
 * 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.
 *
750
 * Returns: (transfer full): a new #GstPad, or NULL in case of an error.
751 752 753 754 755 756 757 758 759 760 761 762 763 764
 */
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;
}

765 766 767 768 769 770 771 772 773 774 775 776 777 778
#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

779 780
/**
 * gst_pad_get_direction:
781
 * @pad: a #GstPad to get the direction of.
782
 *
783
 * Gets the direction of the pad. The direction of the pad is
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
784
 * decided at construction time so this function does not take
785
 * the LOCK.
786
 *
787
 * Returns: the #GstPadDirection of the pad.
788 789
 *
 * MT safe.
790
 */
Wim Taymans's avatar
Wim Taymans committed
791
GstPadDirection
792
gst_pad_get_direction (GstPad * pad)
793
{
794 795
  GstPadDirection result;

796
  /* PAD_UNKNOWN is a little silly but we need some sort of
797
   * error return value */
798
  g_return_val_if_fail (GST_IS_PAD (pad), GST_PAD_UNKNOWN);
Erik Walthinsen's avatar
Erik Walthinsen committed
799

800
  result = GST_PAD_DIRECTION (pad);
801 802

  return result;
Erik Walthinsen's avatar
Erik Walthinsen committed
803 804
}

805
static gboolean
806
gst_pad_activate_default (GstPad * pad, GstObject * parent)
807
{
808
  return gst_pad_activate_mode (pad, GST_PAD_MODE_PUSH, TRUE);
809 810 811
}

static void
Wim Taymans's avatar
Wim Taymans committed
812
pre_activate (GstPad * pad, GstPadMode new_mode)
813
{
814
  switch (new_mode) {
Wim Taymans's avatar
Wim Taymans committed
815
    case GST_PAD_MODE_NONE:
816
      GST_OBJECT_LOCK (pad);
817
      GST_DEBUG_OBJECT (pad, "setting PAD_MODE NONE, set flushing");
818
      GST_PAD_SET_FLUSHING (pad);
Wim Taymans's avatar
Wim Taymans committed
819
      GST_PAD_MODE (pad) = new_mode;
820
      /* unlock blocked pads so element can resume and stop */
821
      GST_PAD_BLOCK_BROADCAST (pad);
822
      GST_OBJECT_UNLOCK (pad);
823
      break;
824 825 826 827 828 829
    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
830 831 832 833 834 835 836 837 838 839 840
      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
841
          schedule_events (peer, NULL);
Wim Taymans's avatar
Wim Taymans committed
842 843 844 845 846 847 848 849 850
          GST_OBJECT_UNLOCK (peer);

          gst_object_unref (peer);
        } else {
          GST_OBJECT_UNLOCK (pad);
        }
      } else {
        GST_OBJECT_UNLOCK (pad);
      }
851
      break;
852 853 854 855
  }
}

static void
Wim Taymans's avatar
Wim Taymans committed
856
post_activate (GstPad * pad, GstPadMode new_mode)
857
{
858
  switch (new_mode) {
Wim Taymans's avatar
Wim Taymans committed
859
    case GST_PAD_MODE_NONE:
860
      /* ensures that streaming stops */
861
      GST_PAD_STREAM_LOCK (pad);
862
      GST_DEBUG_OBJECT (pad, "stopped streaming");
Wim Taymans's avatar
Wim Taymans committed
863
      GST_OBJECT_LOCK (pad);
Wim Taymans's avatar
Wim Taymans committed
864
      remove_events (pad);
Wim Taymans's avatar
Wim Taymans committed
865
      GST_OBJECT_UNLOCK (pad);
866
      GST_PAD_STREAM_UNLOCK (pad);
867
      break;
868 869 870 871
    case GST_PAD_MODE_PUSH:
    case GST_PAD_MODE_PULL:
      /* NOP */
      break;
872 873 874
  }
}

875 876
/**
 * gst_pad_set_active:
877
 * @pad: the #GstPad to activate or deactivate.
878
 * @active: whether or not the pad should be active.
879
 *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
880
 * Activates or deactivates the given pad.
881
 * Normally called from within core state change functions.
Wim Taymans's avatar
Wim Taymans committed
882
 *
883 884 885
 * 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
886
 *
887 888 889
 * 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.
890
 *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
891
 * Returns: #TRUE if the operation was successful.
Wim Taymans's avatar
Wim Taymans committed
892
 *
893
 * MT safe.
Wim Taymans's avatar
Wim Taymans committed
894 895
 */
gboolean
896
gst_pad_set_active (GstPad * pad, gboolean active)
Wim Taymans's avatar
Wim Taymans committed
897
{
898
  GstObject *parent;
Wim Taymans's avatar
Wim Taymans committed
899
  GstPadMode old;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
900
  gboolean ret = FALSE;
Wim Taymans's avatar
Wim Taymans committed
901 902 903

  g_return_val_if_fail (GST_IS_PAD (pad), FALSE);

904
  GST_OBJECT_LOCK (pad);
Wim Taymans's avatar
Wim Taymans committed
905
  old = GST_PAD_MODE (pad);
906
  ACQUIRE_PARENT (pad, parent, no_parent);
907
  GST_OBJECT_UNLOCK (pad);
Wim Taymans's avatar
Wim Taymans committed
908

909
  if (active) {
910 911 912 913 914 915
    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;
916 917
    }
  } else {
918 919 920 921 922 923
    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);
924 925 926
    }
  }

927 928
  RELEASE_PARENT (parent);

929
  if (G_UNLIKELY (!ret))
930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947
    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:
  {
948
    GST_OBJECT_LOCK (pad);
949 950 951 952
    if (!active) {
      g_critical ("Failed to deactivate pad %s:%s, very bad",
          GST_DEBUG_PAD_NAME (pad));
    } else {
953
      GST_WARNING_OBJECT (pad, "Failed to activate pad");
954
    }
955
    GST_OBJECT_UNLOCK (pad);
956
    return FALSE;
957
  }
958
}
959

960
/**
961
 * gst_pad_activate_mode:
962
 * @pad: the #GstPad to activate or deactivate.
963
 * @mode: the requested activation mode
964 965
 * @active: whether or not the pad should be active.
 *
966 967
 * Activates or deactivates the given pad in @mode via dispatching to the
 * pad's activatemodefunc. For use from within pad activation functions only.
968 969 970
 *
 * If you don't know what this is, you probably don't want to call it.
 *
971
 * Returns: TRUE if the operation was successful.
972 973 974 975
 *
 * MT safe.
 */
gboolean
976
gst_pad_activate_mode (GstPad * pad, GstPadMode mode, gboolean active)
977
{
978 979
  gboolean res = FALSE;
  GstObject *parent;