/**
* 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;
}
}
static gboolean
+forward_sticky_events (GstPad * sinkpad, GstEvent ** event, gpointer user_data)
+{
+ GstInputSelector *sel = GST_INPUT_SELECTOR (user_data);
+
+ GST_DEBUG_OBJECT (sinkpad, "forward sticky event %" GST_PTR_FORMAT, *event);
+
+ if (GST_EVENT_TYPE (*event) == GST_EVENT_SEGMENT) {
+ GstSegment *seg = &GST_SELECTOR_PAD (sinkpad)->segment;
+ GstEvent *e;
+
+ e = gst_event_new_segment (seg);
+ gst_event_set_seqnum (e, GST_SELECTOR_PAD_CAST (sinkpad)->segment_seqnum);
+
+ gst_pad_push_event (sel->srcpad, e);
+ } else if (GST_EVENT_TYPE (*event) == GST_EVENT_STREAM_START
+ && !sel->have_group_id) {
+ GstEvent *tmp =
+ gst_pad_get_sticky_event (sel->srcpad, GST_EVENT_STREAM_START, 0);
+
+ /* Only push stream-start once if not all our streams have a stream-id */
+ if (!tmp) {
+ gst_pad_push_event (sel->srcpad, gst_event_ref (*event));
+ } else {
+ gst_event_unref (tmp);
+ }
+ } else {
+ gst_pad_push_event (sel->srcpad, gst_event_ref (*event));
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gst_input_selector_eos_wait (GstInputSelector * self, GstSelectorPad * pad,
+ GstEvent * eos_event)
+{
+ while (!self->eos && !self->flushing && !pad->flushing) {
+ GstPad *active_sinkpad;
+ active_sinkpad = gst_input_selector_get_active_sinkpad (self);
+ if (pad == GST_SELECTOR_PAD_CAST (active_sinkpad) && pad->eos
+ && !pad->eos_sent) {
+ GST_DEBUG_OBJECT (pad, "send EOS event");
+ GST_INPUT_SELECTOR_UNLOCK (self);
+ /* if we have a pending events, push them now */
+ if (pad->events_pending) {
+ gst_pad_sticky_events_foreach (GST_PAD_CAST (pad),
+ forward_sticky_events, self);
+ pad->events_pending = FALSE;
+ }
+
+ 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
+ * get unblocked */
+ GST_INPUT_SELECTOR_WAIT (self);
+ }
+ }
+
+ return self->flushing;
+}
+
+static gboolean
+gst_input_selector_all_eos (GstInputSelector * sel)
+{
+ GList *walk;
+
+ for (walk = GST_ELEMENT_CAST (sel)->sinkpads; walk; walk = walk->next) {
+ GstSelectorPad *selpad;
+
+ selpad = GST_SELECTOR_PAD_CAST (walk->data);
+ if (!selpad->eos) {
+ return FALSE;
+ }
+
+ }
+
+ return TRUE;
+}
+
+static gboolean
gst_selector_pad_event (GstPad * pad, GstObject * parent, GstEvent * event)
{
gboolean res = TRUE;
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:
}
case GST_EVENT_EOS:
selpad->eos = TRUE;
-
- if (!forward) {
- forward = TRUE;
- /* Wait until we're the active sink pad or we're flushing */
- while (!sel->eos && !sel->flushing && !selpad->flushing)
- GST_INPUT_SELECTOR_WAIT (sel);
- } else {
- /* Notify all waiting pads about going EOS now */
+ GST_DEBUG_OBJECT (pad, "received EOS");
+ if (gst_input_selector_all_eos (sel)) {
+ GST_DEBUG_OBJECT (pad, "All sink pad received EOS");
sel->eos = TRUE;
GST_INPUT_SELECTOR_BROADCAST (sel);
- }
-
- if (sel->eos_sent) {
- gst_event_unref (event);
- event = NULL;
} else {
- /* prevent any further EOS event being pushed */
- sel->eos_sent = TRUE;
+ gst_input_selector_eos_wait (sel, selpad, event);
+ forward = FALSE;
}
-
- selpad->eos_sent = TRUE;
-
- GST_DEBUG_OBJECT (pad, "received EOS");
break;
case GST_EVENT_GAP:{
GstClockTime ts, dur;
}
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:
+ /* always proxy caps/position/duration query, 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:{
/* If the active segment is configured but not to time format
* we can't do any syncing at all */
- if (active_seg->format != GST_FORMAT_TIME
- && active_seg->format != GST_FORMAT_UNDEFINED) {
+ if ((active_seg->format != GST_FORMAT_TIME
+ && active_seg->format != GST_FORMAT_UNDEFINED)) {
GST_DEBUG_OBJECT (selpad,
"Not waiting because active segment isn't in TIME format");
GST_INPUT_SELECTOR_UNLOCK (sel);
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)) {
return (sel->flushing || selpad->flushing);
}
-static gboolean
-forward_sticky_events (GstPad * sinkpad, GstEvent ** event, gpointer user_data)
-{
- GstInputSelector *sel = GST_INPUT_SELECTOR (user_data);
-
- if (GST_EVENT_TYPE (*event) == GST_EVENT_SEGMENT) {
- GstSegment *seg = &GST_SELECTOR_PAD (sinkpad)->segment;
- GstEvent *e;
-
- e = gst_event_new_segment (seg);
- gst_event_set_seqnum (e, GST_SELECTOR_PAD_CAST (sinkpad)->segment_seqnum);
-
- gst_pad_push_event (sel->srcpad, e);
- } else if (GST_EVENT_TYPE (*event) == GST_EVENT_STREAM_START
- && !sel->have_group_id) {
- GstEvent *tmp =
- gst_pad_get_sticky_event (sel->srcpad, GST_EVENT_STREAM_START, 0);
-
- /* Only push stream-start once if not all our streams have a stream-id */
- if (!tmp) {
- gst_pad_push_event (sel->srcpad, gst_event_ref (*event));
- } else {
- gst_event_unref (tmp);
- }
- } else {
- gst_pad_push_event (sel->srcpad, gst_event_ref (*event));
- }
-
- return TRUE;
-}
-
#if DEBUG_CACHED_BUFFERS
static void
gst_input_selector_debug_cached_buffers (GstInputSelector * sel)
GST_INPUT_SELECTOR_LOCK (sel);
- if (sel->eos) {
- GST_INPUT_SELECTOR_UNLOCK (sel);
- goto eos;
- }
-
if (sel->flushing) {
GST_INPUT_SELECTOR_UNLOCK (sel);
goto flushing;
active_sinkpad = gst_input_selector_get_active_sinkpad (sel);
}
- if (sel->eos) {
- GST_INPUT_SELECTOR_UNLOCK (sel);
- goto eos;
- }
-
/* update the segment on the srcpad */
if (GST_BUFFER_PTS_IS_VALID (buf)) {
GstClockTime start_time = GST_BUFFER_PTS (buf);
res = GST_FLOW_FLUSHING;
goto done;
}
-eos:
- {
- GST_DEBUG_OBJECT (pad, "We are eos, discard buffer %p", buf);
- gst_buffer_unref (buf);
- res = GST_FLOW_EOS;
- goto done;
- }
}
static void gst_input_selector_dispose (GObject * object);
"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 (gstelement_class,
+ &gst_input_selector_sink_factory);
+ 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;
sel->active_sinkpad = NULL;
sel->padcount = 0;
sel->sync_streams = DEFAULT_SYNC_STREAMS;
+ sel->sync_mode = DEFAULT_SYNC_MODE;
sel->have_group_id = TRUE;
g_mutex_init (&sel->lock);
GST_DEBUG_OBJECT (self, "New active pad is %" GST_PTR_FORMAT,
self->active_sinkpad);
- if (old != new && new && new->eos && !new->eos_sent) {
- self->eos = TRUE;
+ if (old != new && new && new->eos) {
+ new->eos_sent = FALSE;
GST_INPUT_SELECTOR_BROADCAST (self);
}