From a55296314c6bd19683cdaf77146deedab247e9a6 Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Wed, 10 Jul 2019 11:02:07 -0400 Subject: [PATCH] nle: Handle nested timelines update when file changes 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 | 1 - plugins/ges/gesdemux.c | 72 ++++++++++++++++++++++++++++++++++++++---- plugins/nle/nlecomposition.c | 28 +++++++++++++--- 3 files changed, 88 insertions(+), 13 deletions(-) diff --git a/ges/ges-structured-interface.c b/ges/ges-structured-interface.c index 45502c6..04ad5be 100644 --- a/ges/ges-structured-interface.c +++ b/ges/ges-structured-interface.c @@ -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); diff --git a/plugins/ges/gesdemux.c b/plugins/ges/gesdemux.c index 6017978..1fa2128 100644 --- a/plugins/ges/gesdemux.c +++ b/plugins/ges/gesdemux.c @@ -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); } diff --git a/plugins/nle/nlecomposition.c b/plugins/nle/nlecomposition.c index c0ad82b..828a36a 100644 --- a/plugins/nle/nlecomposition.c +++ b/plugins/nle/nlecomposition.c @@ -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, ¤ttime, &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, -- 2.7.4