* 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
*
* 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
*/
#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_ACTIVE_PAD
};
#define DEFAULT_PAD_ALWAYS_OK TRUE
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 */
{
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) {
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;
/* 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_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;
"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);
NOTIFY_MUTEX_UNLOCK ();
}
}
-
-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;
- }
- }
- gst_iterator_free (it);
-
- return is_eos;
-}