demux: Create timeline from the streaming thread

First marshilling it to the main thread is dangerous as it is a blocking
operation and it should never happen there.

The asset cache is MT safe now so it is possible to load the timeline
from that thread directly
This commit is contained in:
Thibault Saunier 2019-06-14 23:48:20 -04:00
parent 61c952c714
commit 6d895ea111

View file

@ -135,25 +135,19 @@ ges_demux_class_init (GESDemuxClass * self_class)
typedef struct typedef struct
{ {
GESTimeline *timeline; GESTimeline *timeline;
gchar *uri;
GMainLoop *ml; GMainLoop *ml;
GError *error; GError *error;
GMutex lock;
GCond cond;
gulong loaded_sigid; gulong loaded_sigid;
gulong error_sigid; gulong error_sigid;
GESDemux *self;
} TimelineConstructionData; } TimelineConstructionData;
static void static void
project_loaded_cb (GESProject * project, GESTimeline * timeline, project_loaded_cb (GESProject * project, GESTimeline * timeline,
TimelineConstructionData * data) TimelineConstructionData * data)
{ {
g_mutex_lock (&data->lock);
data->timeline = timeline; data->timeline = timeline;
g_signal_handler_disconnect (project, data->loaded_sigid); g_signal_handler_disconnect (project, data->loaded_sigid);
data->loaded_sigid = 0; data->loaded_sigid = 0;
g_mutex_unlock (&data->lock);
g_main_loop_quit (data->ml); g_main_loop_quit (data->ml);
} }
@ -162,69 +156,55 @@ static void
error_loading_asset_cb (GESProject * project, GError * error, gchar * id, error_loading_asset_cb (GESProject * project, GError * error, gchar * id,
GType extractable_type, TimelineConstructionData * data) GType extractable_type, TimelineConstructionData * data)
{ {
g_mutex_lock (&data->lock);
data->error = g_error_copy (error); data->error = g_error_copy (error);
g_signal_handler_disconnect (project, data->error_sigid); g_signal_handler_disconnect (project, data->error_sigid);
data->error_sigid = 0; data->error_sigid = 0;
g_mutex_unlock (&data->lock);
g_main_loop_quit (data->ml); g_main_loop_quit (data->ml);
} }
static gboolean static gboolean
ges_timeline_new_from_uri_from_main_thread (TimelineConstructionData * data) ges_demux_create_timeline (GESDemux * self, gchar * uri, GError ** error)
{ {
GESProject *project = ges_project_new (data->uri); GESProject *project = ges_project_new (uri);
G_GNUC_UNUSED void *unused; G_GNUC_UNUSED void *unused;
TimelineConstructionData data = { 0, };
GMainContext *ctx = g_main_context_new ();
g_mutex_lock (&data->lock); g_main_context_push_thread_default (ctx);
klass->discoverer = gst_discoverer_new (timeout, &data->error); data.ml = g_main_loop_new (ctx, TRUE);
g_object_set (klass->discoverer, "use-cache", TRUE, NULL);
if (data->error) {
g_mutex_unlock (&data->lock);
goto done; data.loaded_sigid =
}
g_signal_connect (klass->discoverer, "discovered",
G_CALLBACK (klass->discovered), NULL);
gst_discoverer_start (klass->discoverer);
data->ml = g_main_loop_new (NULL, TRUE);
data->loaded_sigid =
g_signal_connect (project, "loaded", G_CALLBACK (project_loaded_cb), g_signal_connect (project, "loaded", G_CALLBACK (project_loaded_cb),
data); &data);
data->error_sigid = data.error_sigid =
g_signal_connect (project, "error-loading-asset", g_signal_connect_after (project, "error-loading-asset",
G_CALLBACK (error_loading_asset_cb), data); G_CALLBACK (error_loading_asset_cb), &data);
unused = GES_TIMELINE (ges_asset_extract (GES_ASSET (project), &data->error)); unused = GES_TIMELINE (ges_asset_extract (GES_ASSET (project), &data.error));
if (data->error) { if (data.error) {
g_mutex_unlock (&data->lock); *error = data.error;
goto done; goto done;
} }
g_mutex_unlock (&data->lock);
g_main_loop_run (data->ml); g_main_loop_run (data.ml);
g_main_loop_unref (data->ml); g_main_loop_unref (data.ml);
done: done:
if (data.loaded_sigid)
g_signal_handler_disconnect (project, data.loaded_sigid);
g_mutex_lock (&data->lock); if (data.error_sigid)
g_signal_handler_disconnect (project, data.error_sigid);
if (data->loaded_sigid) g_clear_object (&project);
g_signal_handler_disconnect (project, data->loaded_sigid);
if (data->error_sigid) GST_INFO_OBJECT (self, "Timeline properly loaded: %" GST_PTR_FORMAT,
g_signal_handler_disconnect (project, data->error_sigid); data.timeline);
ges_base_bin_set_timeline (GES_BASE_BIN (self), data.timeline);
gst_clear_object (&project); g_main_context_pop_thread_default (ctx);
GST_INFO_OBJECT (data->self, "Timeline properly loaded: %" GST_PTR_FORMAT,
data->timeline);
ges_base_bin_set_timeline (GES_BASE_BIN (data->self), data->timeline);
g_cond_broadcast (&data->cond);
g_mutex_unlock (&data->lock);
return G_SOURCE_REMOVE; return G_SOURCE_REMOVE;
} }
@ -253,9 +233,8 @@ ges_demux_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
if (gst_buffer_map (xges_buffer, &map, GST_MAP_READ)) { if (gst_buffer_map (xges_buffer, &map, GST_MAP_READ)) {
GError *err = NULL; GError *err = NULL;
gchar *filename = NULL, *uri = NULL; gchar *filename = NULL, *uri = NULL;
TimelineConstructionData data = { 0, }; GError *error = NULL;
gint f = g_file_open_tmp (NULL, &filename, &err); gint f = g_file_open_tmp (NULL, &filename, &err);
GMainContext *main_context = g_main_context_default ();
if (err) { if (err) {
GST_ELEMENT_ERROR (self, RESOURCE, OPEN_WRITE, GST_ELEMENT_ERROR (self, RESOURCE, OPEN_WRITE,
@ -275,34 +254,25 @@ ges_demux_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
} }
uri = gst_filename_to_uri (filename, NULL); uri = gst_filename_to_uri (filename, NULL);
data.uri = uri; GST_INFO_OBJECT (self, "Pre loading the timeline.");
data.self = self;
g_main_context_invoke (main_context,
(GSourceFunc) ges_timeline_new_from_uri_from_main_thread, &data);
g_mutex_lock (&data.lock);
while (!data.error && !data.timeline)
g_cond_wait (&data.cond, &data.lock);
data.loaded_sigid = 0;
data.error_sigid = 0;
g_mutex_unlock (&data.lock);
if (data.error) {
GST_ELEMENT_ERROR (self, STREAM, DEMUX,
("Could not create timeline from description"),
("%s", data.error->message));
g_clear_error (&data.error);
ges_demux_create_timeline (self, uri, &error);
if (error)
goto error; goto error;
}
done: done:
g_free (filename); g_free (filename);
g_free (uri); g_free (uri);
g_close (f, NULL); g_close (f, NULL);
return ret; return ret;
error: error:
ret = FALSE; ret = FALSE;
gst_element_post_message (GST_ELEMENT (self),
gst_message_new_error (parent, error,
"Could not create timeline from description"));
g_clear_error (&error);
goto done; goto done;
} else { } else {
GST_ELEMENT_ERROR (self, RESOURCE, READ, GST_ELEMENT_ERROR (self, RESOURCE, READ,