nle: Handle nested timelines update when file changes
authorThibault Saunier <tsaunier@igalia.com>
Wed, 10 Jul 2019 15:02:07 +0000 (11:02 -0400)
committerThibault Saunier <tsaunier@igalia.com>
Fri, 26 Jul 2019 17:48:51 +0000 (13:48 -0400)
When we have nested timelines, we need to make sure the underlying
formatted file is reloaded when commiting the main composition to
take into account the new timeline.

In other to make the implementation as simple as possible we make
sure that whenever the toplevel composition is commited, the decodebin
holding the gesdemux is torn down so that a new demuxer is created
with the new content of the timeline.

To do that a we do a NleCompositionQueryNeedsTearDown query to which
gesdemux answers leading to a full nlecomposition stack
deactivation/activation cycle.

ges/ges-structured-interface.c
plugins/ges/gesdemux.c
plugins/nle/nlecomposition.c

index 45502c6..04ad5be 100644 (file)
@@ -150,7 +150,6 @@ _check_fields (GstStructure * structure, FieldsError fields_error,
 
     if (error)
       *error = g_error_new_literal (GES_ERROR, 0, msg->str);
-    GST_ERROR ("%s", msg->str);
 
     g_string_free (msg, TRUE);
 
index 6017978..1fa2128 100644 (file)
@@ -62,6 +62,9 @@ struct _GESDemux
   GstPad *sinkpad;
 
   GstAdapter *input_adapter;
+
+  gchar *upstream_uri;
+  GStatBuf stats;
 };
 
 G_DEFINE_TYPE (GESDemux, ges_demux, ges_base_bin_get_type ());
@@ -166,8 +169,54 @@ error_loading_asset_cb (GESProject * project, GError * error, gchar * id,
 static gboolean
 ges_demux_src_probe (GstPad * pad, GstPadProbeInfo * info, GstElement * parent)
 {
-  GstEvent *event = info->data;
+  GESDemux *self = GES_DEMUX (parent);
+  GstEvent *event;
+
+  if (info->type & (GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM)) {
+    GstQuery *query = info->data;
+
+    if (GST_QUERY_TYPE (query) == GST_QUERY_CUSTOM) {
+      GstStructure *structure =
+          (GstStructure *) gst_query_get_structure (query);
+
+      if (gst_structure_has_name (structure,
+              "NleCompositionQueryNeedsTearDown")) {
+        GstQuery *uri_query = gst_query_new_uri ();
+
+        if (gst_pad_peer_query (self->sinkpad, uri_query)) {
+          gchar *upstream_uri = NULL;
+          GStatBuf stats;
+          gst_query_parse_uri (uri_query, &upstream_uri);
+
+          if (gst_uri_has_protocol (upstream_uri, "file")) {
+            gchar *location = gst_uri_get_location (upstream_uri);
+
+            g_stat (location, &stats);
+            g_free (location);
+            GST_OBJECT_LOCK (self);
+            if (g_strcmp0 (upstream_uri, self->upstream_uri)
+                || stats.st_mtime != self->stats.st_mtime
+                || stats.st_size != self->stats.st_size) {
+              GST_INFO_OBJECT (self,
+                  "Underlying file changed, asking for an update");
+              gst_structure_set (structure, "result", G_TYPE_BOOLEAN, TRUE,
+                  NULL);
+              g_free (self->upstream_uri);
+              self->upstream_uri = upstream_uri;
+              self->stats = stats;
+            } else {
+              g_free (upstream_uri);
+            }
+            GST_OBJECT_UNLOCK (self);
+          }
+        }
+        gst_query_unref (uri_query);
+      }
+    }
 
+    return GST_PAD_PROBE_OK;
+  }
+  event = info->data;
   switch (GST_EVENT_TYPE (event)) {
     case GST_EVENT_STREAM_START:
     {
@@ -198,7 +247,8 @@ static gboolean
 ges_demux_set_srcpad_probe (GstElement * element, GstPad * pad,
     gpointer user_data)
 {
-  gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
+  gst_pad_add_probe (pad,
+      GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM | GST_PAD_PROBE_TYPE_QUERY_UPSTREAM,
       (GstPadProbeCallback) ges_demux_src_probe, element, NULL);
   return TRUE;
 }
@@ -234,21 +284,29 @@ ges_demux_create_timeline (GESDemux * self, gchar * uri, GError ** error)
 
   query = gst_query_new_uri ();
   if (gst_pad_peer_query (self->sinkpad, query)) {
-    gchar *upstream_uri = NULL;
     GList *assets, *tmp;
-    gst_query_parse_uri (query, &upstream_uri);
+
+    GST_OBJECT_LOCK (self);
+    g_free (self->upstream_uri);
+    gst_query_parse_uri (query, &self->upstream_uri);
+    if (gst_uri_has_protocol (self->upstream_uri, "file")) {
+      gchar *location = gst_uri_get_location (self->upstream_uri);
+
+      g_stat (location, &self->stats);
+      g_free (location);
+    }
 
     assets = ges_project_list_assets (project, GES_TYPE_URI_CLIP);
     for (tmp = assets; tmp; tmp = tmp->next) {
       const gchar *id = ges_asset_get_id (tmp->data);
 
-      if (!g_strcmp0 (id, upstream_uri)) {
+      if (!g_strcmp0 (id, self->upstream_uri)) {
         g_set_error (error, GST_STREAM_ERROR, GST_STREAM_ERROR_DEMUX,
-            "Recursively loading uri: %s", upstream_uri);
+            "Recursively loading uri: %s", self->upstream_uri);
         break;
       }
     }
-
+    GST_OBJECT_UNLOCK (self);
     g_list_free_full (assets, g_object_unref);
   }
 
index c0ad82b..828a36a 100644 (file)
@@ -3108,6 +3108,23 @@ _dump_stack (NleComposition * comp, GNode * stack)
 #endif
 }
 
+static gboolean
+nle_composition_query_needs_teardown (NleComposition * comp,
+    NleUpdateStackReason reason)
+{
+  gboolean res = FALSE;
+  GstStructure *structure =
+      gst_structure_new ("NleCompositionQueryNeedsTearDown", "reason",
+      G_TYPE_STRING, UPDATE_PIPELINE_REASONS[reason], NULL);
+  GstQuery *query = gst_query_new_custom (GST_QUERY_CUSTOM, structure);
+
+  gst_pad_query (NLE_OBJECT_SRC (comp), query);
+  gst_structure_get_boolean (structure, "result", &res);
+
+  gst_query_unref (query);
+  return res;
+}
+
 /*
  * update_pipeline:
  * @comp: The #NleComposition
@@ -3129,7 +3146,7 @@ update_pipeline (NleComposition * comp, GstClockTime currenttime, gint32 seqnum,
   GstEvent *toplevel_seek;
 
   GNode *stack = NULL;
-  gboolean samestack = FALSE;
+  gboolean tear_down = FALSE;
   gboolean updatestoponly = FALSE;
   GstState state = GST_STATE (comp);
   NleCompositionPrivate *priv = comp->priv;
@@ -3167,7 +3184,8 @@ update_pipeline (NleComposition * comp, GstClockTime currenttime, gint32 seqnum,
 
   /* Get new stack and compare it to current one */
   stack = get_clean_toplevel_stack (comp, &currenttime, &new_start, &new_stop);
-  samestack = are_same_stacks (priv->current, stack);
+  tear_down = !are_same_stacks (priv->current, stack)
+      || nle_composition_query_needs_teardown (comp, update_reason);
 
   /* set new current_stack_start/stop (the current zone over which the new stack
    * is valid) */
@@ -3194,7 +3212,7 @@ update_pipeline (NleComposition * comp, GstClockTime currenttime, gint32 seqnum,
     stopchanged = priv->current_stack_stop != currenttime;
   }
 
-  if (samestack) {
+  if (!tear_down) {
     if (startchanged || stopchanged) {
       /* Update seek events need to be flushing if not in PLAYING,
        * else we will encounter deadlocks. */
@@ -3210,7 +3228,7 @@ update_pipeline (NleComposition * comp, GstClockTime currenttime, gint32 seqnum,
   _remove_update_actions (comp);
 
   /* If stacks are different, unlink/relink objects */
-  if (!samestack) {
+  if (tear_down) {
     _dump_stack (comp, stack);
     _deactivate_stack (comp, update_reason);
     _relink_new_stack (comp, stack, toplevel_seek);
@@ -3248,7 +3266,7 @@ update_pipeline (NleComposition * comp, GstClockTime currenttime, gint32 seqnum,
   }
 
   /* Activate stack */
-  if (!samestack)
+  if (tear_down)
     return _activate_new_stack (comp);
   else
     return _seek_current_stack (comp, toplevel_seek,