-/* GStreamer
+/* GStreamer input selector
* Copyright (C) 2003 Julien Moutte <julien@moutte.net>
* Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
* Copyright (C) 2005 Jan Schmidt <thaytan@mad.scientist.com>
* Copyright (C) 2007 Wim Taymans <wim.taymans@gmail.com>
* Copyright (C) 2007 Andy Wingo <wingo@pobox.com>
* Copyright (C) 2008 Nokia Corporation. (contact <stefan.kost@nokia.com>)
+ * Copyright (C) 2011 Sebastian Dröge <sebastian.droege@collabora.co.uk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* @see_also: #GstOutputSelector
*
* Direct one out of N input streams to the output pad.
+ *
+ * The input pads are from a GstPad subclass and have additional
+ * properties, which users may find useful, namely:
+ *
+ * <itemizedlist>
+ * <listitem>
+ * "running-time": Running time of stream on pad (#gint64)
+ * </listitem>
+ * <listitem>
+ * "tags": The currently active tags on the pad (#GstTagList, boxed type)
+ * </listitem>
+ * <listitem>
+ * "active": If the pad is currently active (#gboolean)
+ * </listitem>
+ * <listitem>
+ * "always-ok" : Make an inactive pads return #GST_FLOW_OK instead of
+ * #GST_FLOW_NOT_LINKED
+ * </listitem>
+ * </itemizedlist>
+ *
+ * Since: 0.10.32
*/
#ifdef HAVE_CONFIG_H
GST_DEBUG_CATEGORY_STATIC (input_selector_debug);
#define GST_CAT_DEFAULT input_selector_debug
+#if GLIB_CHECK_VERSION(2, 26, 0)
+#define NOTIFY_MUTEX_LOCK()
+#define NOTIFY_MUTEX_UNLOCK()
+#else
+static GStaticRecMutex notify_mutex = G_STATIC_REC_MUTEX_INIT;
+#define NOTIFY_MUTEX_LOCK() g_static_rec_mutex_lock (¬ify_mutex)
+#define NOTIFY_MUTEX_UNLOCK() g_static_rec_mutex_unlock (¬ify_mutex)
+#endif
+
+#define GST_INPUT_SELECTOR_GET_LOCK(sel) (((GstInputSelector*)(sel))->lock)
+#define GST_INPUT_SELECTOR_GET_COND(sel) (((GstInputSelector*)(sel))->cond)
+#define GST_INPUT_SELECTOR_LOCK(sel) (g_mutex_lock (GST_INPUT_SELECTOR_GET_LOCK(sel)))
+#define GST_INPUT_SELECTOR_UNLOCK(sel) (g_mutex_unlock (GST_INPUT_SELECTOR_GET_LOCK(sel)))
+#define GST_INPUT_SELECTOR_WAIT(sel) (g_cond_wait (GST_INPUT_SELECTOR_GET_COND(sel), \
+ GST_INPUT_SELECTOR_GET_LOCK(sel)))
+#define GST_INPUT_SELECTOR_BROADCAST(sel) (g_cond_broadcast (GST_INPUT_SELECTOR_GET_COND(sel)))
+
static GstStaticPadTemplate gst_input_selector_sink_factory =
GST_STATIC_PAD_TEMPLATE ("sink%d",
GST_PAD_SINK,
{
PROP_0,
PROP_N_PADS,
- PROP_ACTIVE_PAD,
- PROP_SELECT_ALL,
- PROP_LAST
+ PROP_ACTIVE_PAD
};
-#define DEFAULT_PAD_ALWAYS_OK TRUE
+#define DEFAULT_PAD_ALWAYS_OK TRUE
enum
{
PROP_PAD_RUNNING_TIME,
PROP_PAD_TAGS,
PROP_PAD_ACTIVE,
- PROP_PAD_ALWAYS_OK,
- PROP_PAD_LAST
+ PROP_PAD_ALWAYS_OK
};
enum
GstPad * pad);
static GstPad *gst_input_selector_get_linked_pad (GstPad * pad,
gboolean strict);
-static gboolean gst_input_selector_check_eos (GstElement * selector);
#define GST_TYPE_SELECTOR_PAD \
(gst_selector_pad_get_type())
GstPad parent;
gboolean active; /* when buffer have passed the pad */
+ gboolean pushed; /* when buffer was pushed downstream since activation */
gboolean eos; /* when EOS has been received */
+ gboolean eos_sent; /* when EOS was sent downstream */
gboolean discont; /* after switching we create a discont */
+ gboolean flushing; /* set after flush-start and before flush-stop */
gboolean always_ok;
GstSegment segment; /* the current segment on the pad */
GstTagList *tags; /* last tags received on the pad */
static GType
gst_selector_pad_get_type (void)
{
- static GType selector_pad_type = 0;
-
- if (!selector_pad_type) {
- static const GTypeInfo selector_pad_info = {
- sizeof (GstSelectorPadClass),
- NULL,
- NULL,
- (GClassInitFunc) gst_selector_pad_class_init,
- NULL,
- NULL,
- sizeof (GstSelectorPad),
- 0,
- (GInstanceInitFunc) gst_selector_pad_init,
- };
-
- selector_pad_type =
- g_type_register_static (GST_TYPE_PAD, "GstSelectorPad",
+ static volatile gsize selector_pad_type = 0;
+ static const GTypeInfo selector_pad_info = {
+ sizeof (GstSelectorPadClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) gst_selector_pad_class_init,
+ NULL,
+ NULL,
+ sizeof (GstSelectorPad),
+ 0,
+ (GInstanceInitFunc) gst_selector_pad_init,
+ };
+
+ if (g_once_init_enter (&selector_pad_type)) {
+ GType tmp = g_type_register_static (GST_TYPE_PAD, "GstSelectorPad",
&selector_pad_info, 0);
+ g_once_init_leave (&selector_pad_type, tmp);
}
- return selector_pad_type;
+
+ return (GType) selector_pad_type;
}
static void
g_param_spec_boolean ("active", "Active",
"If the pad is currently active", FALSE,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+ /* FIXME: better property name? */
g_object_class_install_property (gobject_class, PROP_PAD_ALWAYS_OK,
g_param_spec_boolean ("always-ok", "Always OK",
"Make an inactive pad return OK instead of NOT_LINKED",
{
GST_OBJECT_LOCK (pad);
pad->active = FALSE;
+ pad->pushed = FALSE;
pad->eos = FALSE;
+ pad->eos_sent = FALSE;
pad->segment_pending = FALSE;
pad->discont = FALSE;
+ pad->flushing = FALSE;
gst_segment_init (&pad->segment, GST_FORMAT_UNDEFINED);
GST_OBJECT_UNLOCK (pad);
}
gst_selector_pad_event (GstPad * pad, GstEvent * event)
{
gboolean res = TRUE;
- gboolean forward = TRUE;
+ gboolean forward;
GstInputSelector *sel;
GstSelectorPad *selpad;
GstPad *prev_active_sinkpad;
prev_active_sinkpad = sel->active_sinkpad;
active_sinkpad = gst_input_selector_activate_sinkpad (sel, pad);
- /* only forward if we are dealing with the active sinkpad or if select_all
- * is enabled */
- if (pad != active_sinkpad && !sel->select_all)
- forward = FALSE;
+ /* only forward if we are dealing with the active sinkpad */
+ forward = (pad == active_sinkpad);
GST_INPUT_SELECTOR_UNLOCK (sel);
- if (prev_active_sinkpad != active_sinkpad && pad == active_sinkpad)
+ if (prev_active_sinkpad != active_sinkpad && pad == active_sinkpad) {
+ NOTIFY_MUTEX_LOCK ();
g_object_notify (G_OBJECT (sel), "active-pad");
+ NOTIFY_MUTEX_UNLOCK ();
+ }
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_FLUSH_START:
- /* FIXME, flush out the waiter */
+ /* Unblock the pad if it's waiting */
+ GST_INPUT_SELECTOR_LOCK (sel);
+ selpad->flushing = TRUE;
+ GST_INPUT_SELECTOR_BROADCAST (sel);
+ GST_INPUT_SELECTOR_UNLOCK (sel);
break;
case GST_EVENT_FLUSH_STOP:
GST_INPUT_SELECTOR_LOCK (sel);
rate, arate, format, start, stop, time);
GST_OBJECT_UNLOCK (selpad);
- /* If we aren't forwarding the event (because the pad is not the
- * active_sinkpad, and select_all is not set, then set the flag on the
+ /* If we aren't forwarding the event because the pad is not the
+ * active_sinkpad, then set the flag on the pad
* that says a segment needs sending if/when that pad is activated.
* For all other cases, we send the event immediately, which makes
* sparse streams and other segment updates work correctly downstream.
}
case GST_EVENT_EOS:
selpad->eos = TRUE;
- GST_DEBUG_OBJECT (pad, "received EOS");
- /* don't forward eos in select_all mode until all sink pads have eos */
- if (sel->select_all && !gst_input_selector_check_eos (GST_ELEMENT (sel))) {
- forward = FALSE;
+
+ if (forward) {
+ selpad->eos_sent = TRUE;
+ } else {
+ GstSelectorPad *tmp;
+
+ /* If the active sinkpad is in EOS state but EOS
+ * was not sent downstream this means that the pad
+ * got EOS before it was set as active pad and that
+ * the previously active pad got EOS after it was
+ * active
+ */
+ GST_INPUT_SELECTOR_LOCK (sel);
+ active_sinkpad = gst_input_selector_activate_sinkpad (sel, pad);
+ tmp = GST_SELECTOR_PAD (active_sinkpad);
+ forward = (tmp->eos && !tmp->eos_sent);
+ tmp->eos_sent = TRUE;
+ GST_INPUT_SELECTOR_UNLOCK (sel);
}
+ GST_DEBUG_OBJECT (pad, "received EOS");
break;
default:
break;
GST_INPUT_SELECTOR_UNLOCK (sel);
- if (prev_active_sinkpad != active_sinkpad && pad == active_sinkpad)
+ if (prev_active_sinkpad != active_sinkpad && pad == active_sinkpad) {
+ NOTIFY_MUTEX_LOCK ();
g_object_notify (G_OBJECT (sel), "active-pad");
+ NOTIFY_MUTEX_UNLOCK ();
+ }
result = gst_pad_alloc_buffer (sel->srcpad, offset, size, caps, buf);
/* ERRORS */
not_active:
{
+ gboolean active_pad_pushed = GST_SELECTOR_PAD_CAST (active_sinkpad)->pushed;
+
GST_INPUT_SELECTOR_UNLOCK (sel);
/* unselected pad, perform fallback alloc or return unlinked when
* asked */
GST_OBJECT_LOCK (selpad);
- if (selpad->always_ok) {
+ if (selpad->always_ok || !active_pad_pushed) {
GST_DEBUG_OBJECT (pad, "Not selected, performing fallback allocation");
*buf = NULL;
result = GST_FLOW_OK;
/* must be called with the SELECTOR_LOCK, will block while the pad is blocked
* or return TRUE when flushing */
static gboolean
-gst_input_selector_wait (GstInputSelector * self, GstPad * pad)
+gst_input_selector_wait (GstInputSelector * self, GstSelectorPad * pad)
{
- while (self->blocked && !self->flushing) {
+ while (self->blocked && !self->flushing && !pad->flushing) {
/* we can be unlocked here when we are shutting down (flushing) or when we
* get unblocked */
GST_INPUT_SELECTOR_WAIT (self);
GST_INPUT_SELECTOR_LOCK (sel);
/* wait or check for flushing */
- if (gst_input_selector_wait (sel, pad))
+ if (gst_input_selector_wait (sel, selpad))
goto flushing;
GST_LOG_OBJECT (pad, "getting active pad");
}
GST_INPUT_SELECTOR_UNLOCK (sel);
- if (prev_active_sinkpad != active_sinkpad && pad == active_sinkpad)
+ if (prev_active_sinkpad != active_sinkpad && pad == active_sinkpad) {
+ NOTIFY_MUTEX_LOCK ();
g_object_notify (G_OBJECT (sel), "active-pad");
+ NOTIFY_MUTEX_UNLOCK ();
+ }
if (close_event)
gst_pad_push_event (sel->srcpad, close_event);
gst_pad_push_event (sel->srcpad, start_event);
if (selpad->discont) {
- buf = gst_buffer_make_metadata_writable (buf);
+ buf = gst_buffer_make_writable (buf);
GST_DEBUG_OBJECT (pad, "Marking discont buffer %p", buf);
GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
}
res = gst_pad_push (sel->srcpad, buf);
+ selpad->pushed = TRUE;
done:
gst_object_unref (sel);
/* dropped buffers */
ignore:
{
+ gboolean active_pad_pushed = GST_SELECTOR_PAD_CAST (active_sinkpad)->pushed;
+
GST_DEBUG_OBJECT (pad, "Pad not active, discard buffer %p", buf);
/* when we drop a buffer, we're creating a discont on this pad */
selpad->discont = TRUE;
/* figure out what to return upstream */
GST_OBJECT_LOCK (selpad);
- if (selpad->always_ok)
+ if (selpad->always_ok || !active_pad_pushed)
res = GST_FLOW_OK;
else
res = GST_FLOW_NOT_LINKED;
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
gst_element_class_set_details_simple (element_class, "Input selector",
- "Generic", "N-to-1 input stream selectoring",
+ "Generic", "N-to-1 input stream selector",
"Julien Moutte <julien@moutte.net>, "
"Jan Schmidt <thaytan@mad.scientist.com>, "
"Wim Taymans <wim.taymans@gmail.com>");
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
- parent_class = g_type_class_peek_parent (klass);
-
gobject_class->dispose = gst_input_selector_dispose;
gobject_class->set_property = gst_input_selector_set_property;
"The currently active sink pad", GST_TYPE_PAD,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
- g_object_class_install_property (gobject_class, PROP_SELECT_ALL,
- g_param_spec_boolean ("select-all", "Select all mode",
- "Forwards data from all input pads", FALSE,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
/**
* GstInputSelector::block:
* @inputselector: the #GstInputSelector
sel->lock = g_mutex_new ();
sel->cond = g_cond_new ();
sel->blocked = FALSE;
-
- sel->select_all = FALSE;
}
static void
gst_segment_set_stop (&self->segment, stop_time);
self->pending_close = TRUE;
}
+ if (old)
+ old->pushed = FALSE;
if (new && new->active && start_time >= 0) {
GST_DEBUG_OBJECT (self, "setting start_time to %" GST_TIME_FORMAT,
gst_segment_set_start (&new->segment, start_time);
new->segment_pending = TRUE;
}
+ if (new)
+ new->pushed = FALSE;
active_pad_p = &self->active_sinkpad;
gst_object_replace ((GstObject **) active_pad_p, GST_OBJECT_CAST (pad));
GST_INPUT_SELECTOR_UNLOCK (sel);
break;
}
- case PROP_SELECT_ALL:
- GST_INPUT_SELECTOR_LOCK (object);
- sel->select_all = g_value_get_boolean (value);
- GST_INPUT_SELECTOR_UNLOCK (object);
- break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
g_value_set_object (value, sel->active_sinkpad);
GST_INPUT_SELECTOR_UNLOCK (object);
break;
- case PROP_SELECT_ALL:
- GST_INPUT_SELECTOR_LOCK (object);
- g_value_set_boolean (value, sel->select_all);
- GST_INPUT_SELECTOR_UNLOCK (object);
- break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
otherpad = gst_input_selector_get_linked_pad (pad, FALSE);
if (!otherpad) {
- if (GST_INPUT_SELECTOR (parent)->select_all) {
- GST_DEBUG_OBJECT (parent,
- "Pad %s:%s not linked, returning merge of caps",
- GST_DEBUG_PAD_NAME (pad));
- caps = gst_pad_proxy_getcaps (pad);
- } else {
- GST_DEBUG_OBJECT (parent,
- "Pad %s:%s not linked, returning ANY", GST_DEBUG_PAD_NAME (pad));
- caps = gst_caps_new_any ();
- }
+ GST_DEBUG_OBJECT (pad, "Pad not linked, returning ANY");
+ caps = gst_caps_new_any ();
} else {
- GST_DEBUG_OBJECT (parent,
- "Pad %s:%s is linked (to %s:%s), returning peer caps",
- GST_DEBUG_PAD_NAME (pad), GST_DEBUG_PAD_NAME (otherpad));
+ GST_DEBUG_OBJECT (pad, "Pad is linked (to %s:%s), returning peer caps",
+ GST_DEBUG_PAD_NAME (otherpad));
/* if the peer has caps, use those. If the pad is not linked, this function
* returns NULL and we return ANY */
if (!(caps = gst_pad_peer_get_caps_reffed (otherpad)))
selpad->active = TRUE;
active_sinkpad = sel->active_sinkpad;
- if (active_sinkpad == NULL || sel->select_all) {
- /* first pad we get activity on becomes the activated pad by default, if we
- * select all, we also remember the last used pad. */
+ if (active_sinkpad == NULL) {
+ /* first pad we get activity on becomes the activated pad by default */
if (sel->active_sinkpad)
gst_object_unref (sel->active_sinkpad);
active_sinkpad = sel->active_sinkpad = gst_object_ref (pad);
GST_INPUT_SELECTOR_BROADCAST (self);
GST_INPUT_SELECTOR_UNLOCK (self);
- if (changed)
+ if (changed) {
+ NOTIFY_MUTEX_LOCK ();
g_object_notify (G_OBJECT (self), "active-pad");
-}
-
-static gboolean
-gst_input_selector_check_eos (GstElement * selector)
-{
- GstIterator *it = gst_element_iterate_sink_pads (selector);
- GstIteratorResult ires;
- gpointer item;
- gboolean done = FALSE, is_eos = FALSE;
- GstSelectorPad *pad;
-
- while (!done) {
- ires = gst_iterator_next (it, &item);
- switch (ires) {
- case GST_ITERATOR_DONE:
- GST_INFO_OBJECT (selector, "all sink pads have eos");
- done = TRUE;
- is_eos = TRUE;
- break;
- case GST_ITERATOR_OK:
- pad = GST_SELECTOR_PAD_CAST (item);
- if (!pad->eos) {
- done = TRUE;
- }
- gst_object_unref (pad);
- break;
- case GST_ITERATOR_RESYNC:
- gst_iterator_resync (it);
- break;
- default:
- done = TRUE;
- break;
- }
+ NOTIFY_MUTEX_UNLOCK ();
}
- gst_iterator_free (it);
-
- return is_eos;
}