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.
This commit is contained in:
Thibault Saunier 2019-07-10 11:02:07 -04:00
parent 70d423575d
commit a55296314c
3 changed files with 88 additions and 13 deletions

View 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);

View 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);
}

View 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,