pipeline: fix base_time selection when flush seeking live
authorMathieu Duponchelle <mathieu@centricular.com>
Wed, 8 Apr 2020 20:22:48 +0000 (22:22 +0200)
committerGStreamer Merge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Thu, 9 Apr 2020 15:41:36 +0000 (15:41 +0000)
When a live pipeline goes to PLAYING, its change_state method
is called twice for PAUSED_TO_PLAYING: the first time is
from GstElement, when NO_PREROLL is returned, the second
is from GstBin, after all async_done messages have been
collected.

base_time selection is done only the first time, through
comparisons with start_time.

On the other hand, when this live pipeline gets flush seeked,
even though start_time is reset by the sink upon reception
of flush_stop(reset_time=TRUE), PAUSED_TO_PLAYING only occurs
once, from GstBin, after all async_done messages have been
collected. This causes the base_time to be off by <latency>.

This commit addresses this by mimicing the behaviour of
GstElement on NO_PREROLL, and calling the change_state
method manually when the following conditions are met:

* The pipeline is live

* The target state is PLAYING

gst/gstpipeline.c

index f967d8e..032fe69 100644 (file)
@@ -109,6 +109,7 @@ struct _GstPipelinePrivate
 {
   /* with LOCK */
   gboolean auto_flush_bus;
+  gboolean is_live;
 
   /* when we need to update stream_time or clock when going back to
    * PLAYING*/
@@ -228,6 +229,8 @@ gst_pipeline_init (GstPipeline * pipeline)
   pipeline->delay = DEFAULT_DELAY;
   pipeline->priv->latency = DEFAULT_LATENCY;
 
+  pipeline->priv->is_live = FALSE;
+
   /* create and set a default bus */
   bus = gst_bus_new ();
 #if 0
@@ -512,6 +515,7 @@ gst_pipeline_change_state (GstElement * element, GstStateChange transition)
       break;
     }
     case GST_STATE_CHANGE_PAUSED_TO_READY:
+      pipeline->priv->is_live = FALSE;
       reset_start_time (pipeline, 0);
       break;
     case GST_STATE_CHANGE_READY_TO_NULL:
@@ -520,6 +524,12 @@ gst_pipeline_change_state (GstElement * element, GstStateChange transition)
 
   result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
 
+  if (GST_STATE_TRANSITION_NEXT (transition) == GST_STATE_PAUSED) {
+    pipeline->priv->is_live = result == GST_STATE_CHANGE_NO_PREROLL;
+    GST_INFO_OBJECT (pipeline, "pipeline is%slive",
+        pipeline->priv->is_live ? " " : " not ");
+  }
+
   switch (transition) {
     case GST_STATE_CHANGE_NULL_TO_NULL:
       break;
@@ -613,6 +623,14 @@ gst_pipeline_handle_message (GstBin * bin, GstMessage * message)
       /* reset our running time if we need to distribute a new base_time to the
        * children. */
       reset_start_time (pipeline, running_time);
+
+      /* If we are live, sample a new base_time immediately */
+      if (pipeline->priv->is_live
+          && GST_STATE_TARGET (pipeline) == GST_STATE_PLAYING) {
+        gst_pipeline_change_state (GST_ELEMENT (pipeline),
+            GST_STATE_CHANGE_PAUSED_TO_PLAYING);
+      }
+
       break;
     }
     case GST_MESSAGE_CLOCK_LOST: