/**
* SECTION:element-input-selector
+ * @title: input-selector
* @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
+ * * "running-time": Running time of stream on pad (#gint64)
+ * * "tags": The currently active tags on the pad (#GstTagList, boxed type)
+ * * "active": If the pad is currently active (#gboolean)
+ * * "always-ok" : Make an inactive pads return #GST_FLOW_OK instead of
* #GST_FLOW_NOT_LINKED
- * </listitem>
- * </itemizedlist>
+ *
*/
#ifdef HAVE_CONFIG_H
GstPad parent;
gboolean pushed; /* when buffer was pushed downstream since activation */
+ guint group_id; /* Group ID from the last stream-start */
+ gboolean group_done; /* when Stream Group Done has been
+ received */
gboolean eos; /* when EOS has been received */
gboolean eos_sent; /* when EOS was sent downstream */
gboolean discont; /* after switching we create a discont */
{
GST_OBJECT_LOCK (pad);
pad->pushed = FALSE;
+ pad->group_done = FALSE;
pad->eos = FALSE;
pad->eos_sent = FALSE;
pad->events_pending = FALSE;
if (selpad->segment.format != GST_FORMAT_TIME) {
GST_DEBUG_OBJECT (selpad, "Buffer %p with segment not in time format, "
"not caching", buffer);
+ gst_buffer_unref (buffer);
return;
}
gst_pad_push_event (self->srcpad, gst_event_ref (eos_event));
GST_INPUT_SELECTOR_LOCK (self);
+ /* Wake up other pads so they can continue when syncing to
+ * running time, as this pad just switched to EOS and
+ * may enable others to progress */
+ GST_INPUT_SELECTOR_BROADCAST (self);
pad->eos_sent = TRUE;
} else {
/* we can be unlocked here when we are shutting down (flushing) or when we
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_STREAM_START:{
- guint group_id;
-
- if (!gst_event_parse_group_id (event, &group_id))
+ if (!gst_event_parse_group_id (event, &selpad->group_id)) {
sel->have_group_id = FALSE;
+ selpad->group_id = 0;
+ }
break;
}
case GST_EVENT_FLUSH_START:
/* Unblock the pad if it's waiting */
selpad->flushing = TRUE;
sel->eos = FALSE;
+ selpad->group_done = FALSE;
GST_INPUT_SELECTOR_BROADCAST (sel);
break;
case GST_EVENT_FLUSH_STOP:
}
break;
+ case GST_EVENT_STREAM_GROUP_DONE:{
+ GST_DEBUG_OBJECT (sel, "Stream group-done in inputselector pad %s",
+ GST_OBJECT_NAME (selpad));
+ gst_event_parse_stream_group_done (event, &selpad->group_id);
+ selpad->group_done = TRUE;
+ if (sel->sync_streams && active_sinkpad == pad)
+ GST_INPUT_SELECTOR_BROADCAST (sel);
+ break;
+ }
default:
break;
}
GST_INPUT_SELECTOR_UNLOCK (sel);
if (new_tags)
g_object_notify (G_OBJECT (selpad), "tags");
- if (event) {
- if (forward) {
- GST_DEBUG_OBJECT (pad, "forwarding event");
- res = gst_pad_push_event (sel->srcpad, event);
- } else {
- /* 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.
- */
- if (GST_EVENT_IS_STICKY (event))
- selpad->events_pending = TRUE;
- gst_event_unref (event);
- }
+ if (forward) {
+ GST_DEBUG_OBJECT (pad, "forwarding event");
+ res = gst_pad_push_event (sel->srcpad, event);
+ } else {
+ /* 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.
+ */
+ if (GST_EVENT_IS_STICKY (event))
+ selpad->events_pending = TRUE;
+ gst_event_unref (event);
}
return res;
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_CAPS:
- /* always proxy caps query, regardless of active pad or not */
+ case GST_QUERY_POSITION:
+ case GST_QUERY_DURATION:
+ case GST_QUERY_CONTEXT:
+ /* always proxy caps/position/duration/context queries, regardless of active pad or not
+ * See https://bugzilla.gnome.org/show_bug.cgi?id=775445 */
res = gst_pad_peer_query (self->srcpad, query);
break;
case GST_QUERY_ALLOCATION:{
GST_FORMAT_TIME, active_seg->position);
}
+ /* Don't wait if the group is finished on the active pad,
+ * as the running time won't progress now */
+ if (selpad != active_selpad && active_selpad->group_done &&
+ selpad->group_id == active_selpad->group_id) {
+ GST_DEBUG_OBJECT (selpad, "Active pad received group-done. Unblocking");
+ GST_INPUT_SELECTOR_UNLOCK (sel);
+ break;
+ }
+
if (selpad != active_selpad && !sel->eos && !sel->flushing
&& !selpad->flushing && (cur_running_time == GST_CLOCK_TIME_NONE
|| running_time >= cur_running_time)) {
static gboolean gst_input_selector_event (GstPad * pad, GstObject * parent,
GstEvent * event);
+static gboolean gst_input_selector_query (GstPad * pad, GstObject * parent,
+ GstQuery * query);
#define _do_init \
GST_DEBUG_CATEGORY_INIT (input_selector_debug, \
*
* The active pad may push more buffers than what is currently displayed/consumed
* and when changing pads those buffers will be discarded and the only way to
- * reactivate that pad without loosing the already consumed buffers is to enable cache.
+ * reactivate that pad without losing the already consumed buffers is to enable cache.
*/
g_object_class_install_property (gobject_class, PROP_CACHE_BUFFERS,
g_param_spec_boolean ("cache-buffers", "Cache Buffers",
"Julien Moutte <julien@moutte.net>, "
"Jan Schmidt <thaytan@mad.scientist.com>, "
"Wim Taymans <wim.taymans@gmail.com>");
- gst_element_class_add_pad_template (gstelement_class,
- gst_static_pad_template_get (&gst_input_selector_sink_factory));
- gst_element_class_add_pad_template (gstelement_class,
- gst_static_pad_template_get (&gst_input_selector_src_factory));
+ gst_element_class_add_static_pad_template_with_gtype (gstelement_class,
+ &gst_input_selector_sink_factory, GST_TYPE_SELECTOR_PAD);
+ gst_element_class_add_static_pad_template (gstelement_class,
+ &gst_input_selector_src_factory);
gstelement_class->request_new_pad = gst_input_selector_request_new_pad;
gstelement_class->release_pad = gst_input_selector_release_pad;
GST_DEBUG_FUNCPTR (gst_selector_pad_iterate_linked_pads));
gst_pad_set_event_function (sel->srcpad,
GST_DEBUG_FUNCPTR (gst_input_selector_event));
+ gst_pad_set_query_function (sel->srcpad,
+ GST_DEBUG_FUNCPTR (gst_input_selector_query));
GST_OBJECT_FLAG_SET (sel->srcpad, GST_PAD_FLAG_PROXY_CAPS);
gst_element_add_pad (GST_ELEMENT (sel), sel->srcpad);
/* sinkpad management */
if (pad == self->active_sinkpad)
return FALSE;
+ /* guard against users setting a src pad or foreign pad as active pad */
+ if (pad != NULL) {
+ g_return_val_if_fail (GST_PAD_IS_SINK (pad), FALSE);
+ g_return_val_if_fail (GST_IS_SELECTOR_PAD (pad), FALSE);
+ g_return_val_if_fail (GST_PAD_PARENT (pad) == GST_ELEMENT_CAST (self),
+ FALSE);
+ }
+
old = GST_SELECTOR_PAD_CAST (self->active_sinkpad);
new = GST_SELECTOR_PAD_CAST (pad);
return result;
}
+typedef struct
+{
+ gboolean live;
+ GstClockTime min, max;
+} LatencyFoldData;
+
+static gboolean
+query_latency_default_fold (const GValue * item, GValue * ret,
+ gpointer user_data)
+{
+ GstPad *pad = g_value_get_object (item), *peer;
+ LatencyFoldData *fold_data = user_data;
+ GstQuery *query;
+ gboolean res = FALSE;
+
+ query = gst_query_new_latency ();
+
+ peer = gst_pad_get_peer (pad);
+ if (peer) {
+ res = gst_pad_peer_query (pad, query);
+ } else {
+ GST_LOG_OBJECT (pad, "No peer pad found, ignoring this pad");
+ }
+
+ if (res) {
+ gboolean live;
+ GstClockTime min, max;
+
+ gst_query_parse_latency (query, &live, &min, &max);
+
+ GST_LOG_OBJECT (pad, "got latency live:%s min:%" G_GINT64_FORMAT
+ " max:%" G_GINT64_FORMAT, live ? "true" : "false", min, max);
+
+ if (live) {
+ if (min > fold_data->min)
+ fold_data->min = min;
+
+ if (fold_data->max == GST_CLOCK_TIME_NONE)
+ fold_data->max = max;
+ else if (max < fold_data->max)
+ fold_data->max = max;
+ fold_data->live = live;
+ }
+ } else if (peer) {
+ GST_DEBUG_OBJECT (pad, "latency query failed");
+ g_value_set_boolean (ret, FALSE);
+ }
+
+ gst_query_unref (query);
+ if (peer)
+ gst_object_unref (peer);
+
+ return TRUE;
+}
+
+static gboolean
+gst_input_selector_query_latency (GstInputSelector * sel, GstPad * pad,
+ GstQuery * query)
+{
+ GstIterator *it;
+ GstIteratorResult res;
+ GValue ret = G_VALUE_INIT;
+ gboolean query_ret;
+ LatencyFoldData fold_data;
+
+ /* This is basically gst_pad_query_latency_default() but with a different
+ * iterator. We query all sinkpads! */
+ it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (sel));
+ if (!it) {
+ GST_DEBUG_OBJECT (pad, "Can't iterate internal links");
+ return FALSE;
+ }
+
+ g_value_init (&ret, G_TYPE_BOOLEAN);
+
+retry:
+ fold_data.live = FALSE;
+ fold_data.min = 0;
+ fold_data.max = GST_CLOCK_TIME_NONE;
+
+ g_value_set_boolean (&ret, TRUE);
+ res = gst_iterator_fold (it, query_latency_default_fold, &ret, &fold_data);
+ switch (res) {
+ case GST_ITERATOR_OK:
+ g_assert_not_reached ();
+ break;
+ case GST_ITERATOR_DONE:
+ break;
+ case GST_ITERATOR_ERROR:
+ g_value_set_boolean (&ret, FALSE);
+ break;
+ case GST_ITERATOR_RESYNC:
+ gst_iterator_resync (it);
+ goto retry;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+ gst_iterator_free (it);
+
+ query_ret = g_value_get_boolean (&ret);
+ if (query_ret) {
+ GST_LOG_OBJECT (pad, "got latency live:%s min:%" G_GINT64_FORMAT
+ " max:%" G_GINT64_FORMAT, fold_data.live ? "true" : "false",
+ fold_data.min, fold_data.max);
+
+ if (fold_data.min > fold_data.max) {
+ GST_ERROR_OBJECT (pad, "minimum latency bigger than maximum latency");
+ }
+
+ gst_query_set_latency (query, fold_data.live, fold_data.min, fold_data.max);
+ } else {
+ GST_LOG_OBJECT (pad, "latency query failed");
+ }
+
+ return query_ret;
+}
+
+static gboolean
+gst_input_selector_query (GstPad * pad, GstObject * parent, GstQuery * query)
+{
+ GstInputSelector *sel = GST_INPUT_SELECTOR (parent);
+
+ switch (GST_QUERY_TYPE (query)) {
+ case GST_QUERY_LATENCY:
+ /* Query all sink pads for the latency, not just the active one */
+ return gst_input_selector_query_latency (sel, pad, query);
+ default:
+ return gst_pad_query_default (pad, parent, query);
+ }
+}
+
/* check if the pad is the active sinkpad */
static inline gboolean
gst_input_selector_is_active_sinkpad (GstInputSelector * sel, GstPad * pad)