From 974e908c61b508d96b62d5c142b1605e693db17c Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Thu, 22 Jun 2023 22:23:48 -0400 Subject: [PATCH] ges: project: Start making MT safe where needed Formatters might call "loaded" from the `gessrc` streaming thread meaning that the `->formatters` field need to be protected. Several other APIs are called from gesbasedemux, in some radom thread, so we should ensure that this is all MT. safe, and the API makes it simple. Co-authored-by: Philippe Normand Part-of: --- girs/GES-1.0.gir | 85 +++++--- .../gst-editing-services/ges/ges-internal.h | 3 + .../gst-editing-services/ges/ges-project.c | 199 ++++++++++++++---- .../gst-editing-services/ges/ges-timeline.c | 6 + 4 files changed, 224 insertions(+), 69 deletions(-) diff --git a/girs/GES-1.0.gir b/girs/GES-1.0.gir index 8e403664f7..9e4a497255 100644 --- a/girs/GES-1.0.gir +++ b/girs/GES-1.0.gir @@ -7138,18 +7138,18 @@ is really not in good shape and is deprecated. #GESAsset with `GES_TYPE_TIMELINE` as @extractable_type itself. That means that you can extract #GESTimeline from a project as followed: -|[ - GESProject *project; - GESTimeline *timeline; +```c +GESProject *project; +GESTimeline *timeline; - project = ges_project_new ("file:///path/to/a/valid/project/uri"); +project = ges_project_new ("file:///path/to/a/valid/project/uri"); - // Here you can connect to the various signal to get more infos about - // what is happening and recover from errors if possible - ... +// Here you can connect to the various signal to get more infos about +// what is happening and recover from errors if possible +... - timeline = ges_asset_extract (GES_ASSET (project)); -]| +timeline = ges_asset_extract (GES_ASSET (project)); +``` The #GESProject class offers a higher level API to handle #GESAsset-s. It lets you request new asset, and it informs you about new assets through @@ -7185,7 +7185,9 @@ other locations, it will never be updated again and the first valid URI is the URI it will keep refering to. - A newly created #GESProject + A newly created #GESProject + +MT safe. @@ -7310,7 +7312,9 @@ the URI it will keep refering to. %TRUE if the asset could be added %FALSE it was already -in the project +in the project. + +MT safe. @@ -7326,11 +7330,13 @@ in the project Adds @profile to the project. It lets you save in what format -the project has been renders and keep a reference to those formats. -Also, those formats will be saves to the project file when possible. +the project will be rendered and keep a reference to those formats. +Also, those formats will be saved to the project file when possible. - %TRUE if @profile could be added, %FALSE otherwize + %TRUE if @profile could be added, %FALSE otherwise + +MT safe. @@ -7340,13 +7346,16 @@ Also, those formats will be saves to the project file when possible. A #GstEncodingProfile to add to the project. If a profile with -the same name already exists, it will be replaced +the same name already exists, it will be replaced. - Adds a formatter as used to load @project + Adds a formatter to be used to load @project + + +MT safe. @@ -7368,8 +7377,10 @@ the same name already exists, it will be replaced @project - %TRUE if the asset started to be added %FALSE it was already -in the project + %TRUE if the asset was added and started loading, %FALSE it was +already in the project. + +MT safe. @@ -7393,7 +7404,9 @@ in the project @project - The newly created #GESAsset or %NULL. + The newly created #GESAsset or %NULL. + +MT safe. @@ -7415,7 +7428,9 @@ in the project The #GESAsset with -@id or %NULL if no asset with @id as an ID +@id or %NULL if no asset with @id as an ID + +MT safe. @@ -7440,7 +7455,9 @@ to retrieve from @object A set of loading asset that will be added to @project. Note that those Asset are *not* loaded yet, -and thus can not be used +and thus can not be used. + +MT safe. @@ -7456,7 +7473,9 @@ and thus can not be used Retrieve the uri that is currently set on @project - a newly allocated string representing uri. + a newly allocated string representing uri. + +MT safe. @@ -7473,7 +7492,9 @@ in time. The list of -#GESAsset the object contains +#GESAsset the object contains + +MT safe. @@ -7512,7 +7533,9 @@ list of #GstEncodingProfile used in @project Loads @project into @timeline - %TRUE if the project could be loaded %FALSE otherwize. + %TRUE if the project could be loaded %FALSE otherwise. + +MT safe. @@ -7527,10 +7550,12 @@ list of #GstEncodingProfile used in @project - remove a @asset to from @project. + Remove @asset from @project. - %TRUE if the asset could be removed %FALSE otherwise + %TRUE if the asset could be removed %FALSE otherwise + +MT safe. @@ -7550,7 +7575,9 @@ is one of the timelines that have been extracted from @project (using ges_asset_extract (@project);) - %TRUE if the project could be save, %FALSE otherwize + %TRUE if the project could be save, %FALSE otherwise + +MT safe. @@ -7687,7 +7714,7 @@ failed loading - |[ + ```c static gchar source_moved_cb (GESProject *project, GError *error, GESAsset *asset_with_error) { @@ -7703,7 +7730,7 @@ main (int argc, gchar ** argv) g_signal_connect (project, "missing-uri", source_moved_cb, NULL); timeline = ges_asset_extract (GES_ASSET (project)); } -]| +``` The new URI of @wrong_asset diff --git a/subprojects/gst-editing-services/ges/ges-internal.h b/subprojects/gst-editing-services/ges/ges-internal.h index cdb1495faf..fe3759da6e 100644 --- a/subprojects/gst-editing-services/ges/ges-internal.h +++ b/subprojects/gst-editing-services/ges/ges-internal.h @@ -191,6 +191,9 @@ ges_timeline_get_smart_rendering (GESTimeline *timeline); G_GNUC_INTERNAL GstStreamCollection* ges_timeline_get_stream_collection (GESTimeline *timeline); +G_GNUC_INTERNAL gboolean +ges_timeline_in_current_thread (GESTimeline *timeline); + G_GNUC_INTERNAL void ges_auto_transition_set_source (GESAutoTransition * self, GESTrackElement * source, GESEdge edge); diff --git a/subprojects/gst-editing-services/ges/ges-project.c b/subprojects/gst-editing-services/ges/ges-project.c index 48e7d0ceb7..477fb9de74 100644 --- a/subprojects/gst-editing-services/ges/ges-project.c +++ b/subprojects/gst-editing-services/ges/ges-project.c @@ -26,18 +26,18 @@ * #GESAsset with `GES_TYPE_TIMELINE` as @extractable_type itself. That * means that you can extract #GESTimeline from a project as followed: * - * |[ - * GESProject *project; - * GESTimeline *timeline; + * ```c + * GESProject *project; + * GESTimeline *timeline; * - * project = ges_project_new ("file:///path/to/a/valid/project/uri"); + * project = ges_project_new ("file:///path/to/a/valid/project/uri"); * - * // Here you can connect to the various signal to get more infos about - * // what is happening and recover from errors if possible - * ... + * // Here you can connect to the various signal to get more infos about + * // what is happening and recover from errors if possible + * ... * - * timeline = ges_asset_extract (GES_ASSET (project)); - * ]| + * timeline = ges_asset_extract (GES_ASSET (project)); + * ``` * * The #GESProject class offers a higher level API to handle #GESAsset-s. * It lets you request new asset, and it informs you about new assets through @@ -72,6 +72,10 @@ static GPtrArray *new_paths = NULL; static GHashTable *tried_uris = NULL; +#define GES_PROJECT_LOCK(project) (g_mutex_lock (&project->priv->lock)) +#define GES_PROJECT_UNLOCK(project) (g_mutex_unlock (&project->priv->lock)) + +/* Fields are protected by GES_PROJECT_LOCK */ struct _GESProjectPrivate { GHashTable *assets; @@ -85,6 +89,8 @@ struct _GESProjectPrivate gchar *uri; GList *encoding_profiles; + + GMutex lock; }; typedef struct EmitLoadedInIdle @@ -150,9 +156,11 @@ _emit_loaded_in_idle (EmitLoadedInIdle * data) * @project: The project to add a formatter to * @formatter: A formatter used by @project * - * Adds a formatter as used to load @project + * Adds a formatter to be used to load @project * * Since: 1.18 + * + * MT safe. */ void ges_project_add_formatter (GESProject * project, GESFormatter * formatter) @@ -160,27 +168,35 @@ ges_project_add_formatter (GESProject * project, GESFormatter * formatter) GESProjectPrivate *priv = GES_PROJECT (project)->priv; ges_formatter_set_project (formatter, project); + GES_PROJECT_LOCK (project); priv->formatters = g_list_append (priv->formatters, formatter); + GES_PROJECT_UNLOCK (project); gst_object_ref_sink (formatter); } +/* Internally takes project mutex */ static void ges_project_remove_formatter (GESProject * project, GESFormatter * formatter) { GList *tmp; GESProjectPrivate *priv = GES_PROJECT (project)->priv; + GES_PROJECT_LOCK (project); for (tmp = priv->formatters; tmp; tmp = tmp->next) { if (tmp->data == formatter) { gst_object_unref (formatter); priv->formatters = g_list_delete_link (priv->formatters, tmp); - return; + goto done; } } + +done: + GES_PROJECT_UNLOCK (project); } +/* Internally takes project mutex */ static void ges_project_set_uri (GESProject * project, const gchar * uri) { @@ -188,17 +204,19 @@ ges_project_set_uri (GESProject * project, const gchar * uri) g_return_if_fail (GES_IS_PROJECT (project)); + GES_PROJECT_LOCK (project); + priv = project->priv; if (priv->uri) { if (g_strcmp0 (priv->uri, uri)) GST_WARNING_OBJECT (project, "Trying to reset URI, this is prohibited"); - return; + goto done; } if (uri == NULL) { GST_LOG_OBJECT (project, "Uri should not be NULL"); - return; + goto done; } priv->uri = g_strdup (uri); @@ -206,20 +224,29 @@ ges_project_set_uri (GESProject * project, const gchar * uri) /* We use that URI as ID */ ges_asset_set_id (GES_ASSET (project), uri); - return; +done: + GES_PROJECT_UNLOCK (project); } +/* Internally takes project mutex */ static gboolean _load_project (GESProject * project, GESTimeline * timeline, GError ** error) { GError *lerr = NULL; GESProjectPrivate *priv; GESFormatter *formatter; + gboolean has_uri = FALSE; + gchar *uri = NULL; priv = GES_PROJECT (project)->priv; g_signal_emit (project, _signals[LOADING_SIGNAL], 0, timeline); - if (priv->uri == NULL) { + + GES_PROJECT_LOCK (project); + has_uri = priv->uri != NULL; + GES_PROJECT_UNLOCK (project); + + if (!has_uri) { const gchar *id = ges_asset_get_id (GES_ASSET (project)); if (id && gst_uri_is_valid (id)) { @@ -241,6 +268,8 @@ _load_project (GESProject * project, GESTimeline * timeline, GError ** error) } } + GES_PROJECT_LOCK (project); + if (priv->formatter_asset == NULL) priv->formatter_asset = _find_formatter_asset_for_id (priv->uri); @@ -257,17 +286,29 @@ _load_project (GESProject * project, GESTimeline * timeline, GError ** error) goto failed; } + uri = g_strdup (priv->uri); + + GES_PROJECT_UNLOCK (project); ges_project_add_formatter (GES_PROJECT (project), formatter); - ges_formatter_load_from_uri (formatter, timeline, priv->uri, &lerr); + /* ges_formatter_load_from_uri() might indirectly lead to a + ges_project_add_asset() call, so do the loading unlocked. */ + ges_formatter_load_from_uri (formatter, timeline, uri, &lerr); + GES_PROJECT_LOCK (project); + + g_free (uri); + if (lerr) { GST_WARNING_OBJECT (project, "Could not load the timeline," " returning: %s", lerr->message); goto failed; } + GES_PROJECT_UNLOCK (project); return TRUE; failed: + GES_PROJECT_UNLOCK (project); + if (lerr) g_propagate_error (error, lerr); return FALSE; @@ -454,7 +495,9 @@ _get_property (GESProject * project, guint property_id, switch (property_id) { case PROP_URI: + GES_PROJECT_LOCK (project); g_value_set_string (value, priv->uri); + GES_PROJECT_UNLOCK (project); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (project, property_id, pspec); @@ -467,7 +510,9 @@ _set_property (GESProject * project, guint property_id, { switch (property_id) { case PROP_URI: + GES_PROJECT_LOCK (project); project->priv->uri = g_value_dup_string (value); + GES_PROJECT_UNLOCK (project); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (project, property_id, pspec); @@ -557,7 +602,7 @@ ges_project_class_init (GESProjectClass * klass) * @wrong_asset: The asset with the wrong ID, you should us it and its content * only to find out what the new location is. * - * |[ + * ```c * static gchar * source_moved_cb (GESProject *project, GError *error, GESAsset *asset_with_error) * { @@ -573,7 +618,7 @@ ges_project_class_init (GESProjectClass * klass) * g_signal_connect (project, "missing-uri", source_moved_cb, NULL); * timeline = ges_asset_extract (GES_ASSET (project)); * } - * ]| + * ``` * * Returns: (transfer full) (nullable): The new URI of @wrong_asset */ @@ -654,6 +699,7 @@ ges_project_internal_asset_id (GESAsset * asset) (asset), ges_asset_get_id (asset)); } +/* Internally takes project mutex */ static void _send_error_loading_asset (GESProject * project, GESAsset * asset, GError * error) @@ -662,12 +708,15 @@ _send_error_loading_asset (GESProject * project, GESAsset * asset, const gchar *id = ges_asset_get_id (asset); GST_DEBUG_OBJECT (project, "Sending error loading asset for %s", id); + GES_PROJECT_LOCK (project); g_hash_table_remove (project->priv->loading_assets, internal_id); g_hash_table_add (project->priv->loaded_with_error, internal_id); + GES_PROJECT_UNLOCK (project); g_signal_emit (project, _signals[ERROR_LOADING_ASSET], 0, error, id, ges_asset_get_extractable_type (asset)); } +/* Internally takes project mutex */ gchar * ges_project_try_updating_id (GESProject * project, GESAsset * asset, GError * error) @@ -707,7 +756,9 @@ ges_project_try_updating_id (GESProject * project, GESAsset * asset, } internal_id = ges_project_internal_asset_id (asset); + GES_PROJECT_LOCK (project); g_hash_table_remove (project->priv->loading_assets, internal_id); + GES_PROJECT_UNLOCK (project); g_free (internal_id); if (new_id == NULL) @@ -752,7 +803,7 @@ new_asset_cb (GESAsset * source, GAsyncResult * res, GESProject * project) * Emits the "loaded" signal. This method should be called by sublasses when * the project is fully loaded. * - * Returns: %TRUE if the signale could be emitted %FALSE otherwize + * Returns: %TRUE if the signale could be emitted %FALSE otherwise */ gboolean ges_project_set_loaded (GESProject * project, GESFormatter * formatter, @@ -764,13 +815,16 @@ ges_project_set_loaded (GESProject * project, GESFormatter * formatter, error); } - GST_INFO_OBJECT (project, "Emit project loaded"); - if (GST_STATE (formatter->timeline) < GST_STATE_PAUSED) { + if (!ges_timeline_in_current_thread (formatter->timeline)) { + GST_INFO_OBJECT (project, "Loaded in a different thread, " + "not committing timeline"); + } else if (GST_STATE (formatter->timeline) < GST_STATE_PAUSED) { timeline_fill_gaps (formatter->timeline); } else { ges_timeline_commit (formatter->timeline); } + GST_INFO_OBJECT (project, "Emit project loaded"); g_signal_emit (project, _signals[LOADED_SIGNAL], 0, formatter->timeline); /* We are now done with that formatter */ @@ -778,6 +832,7 @@ ges_project_set_loaded (GESProject * project, GESFormatter * formatter, return TRUE; } +/* Internally takes project mutex */ void ges_project_add_loading_asset (GESProject * project, GType extractable_type, const gchar * id) @@ -785,9 +840,14 @@ ges_project_add_loading_asset (GESProject * project, GType extractable_type, GESAsset *asset; if ((asset = ges_asset_cache_lookup (extractable_type, id))) { + GES_PROJECT_LOCK (project); if (g_hash_table_insert (project->priv->loading_assets, - ges_project_internal_asset_id (asset), gst_object_ref (asset))) + ges_project_internal_asset_id (asset), gst_object_ref (asset))) { + GES_PROJECT_UNLOCK (project); g_signal_emit (project, _signals[ASSET_LOADING_SIGNAL], 0, asset); + } else { + GES_PROJECT_UNLOCK (project); + } } } @@ -807,8 +867,10 @@ ges_project_add_loading_asset (GESProject * project, GType extractable_type, * "asset-added" signal to get the asset when it finally gets added to * @project * - * Returns: %TRUE if the asset started to be added %FALSE it was already - * in the project + * Returns: %TRUE if the asset was added and started loading, %FALSE it was + * already in the project. + * + * MT safe. */ gboolean ges_project_create_asset (GESProject * project, const gchar * id, @@ -823,13 +885,16 @@ ges_project_create_asset (GESProject * project, const gchar * id, id = g_type_name (extractable_type); internal_id = ges_project_internal_extractable_type_id (extractable_type, id); + GES_PROJECT_LOCK (project); if (g_hash_table_lookup (project->priv->assets, internal_id) || g_hash_table_lookup (project->priv->loading_assets, internal_id) || g_hash_table_lookup (project->priv->loaded_with_error, internal_id)) { + GES_PROJECT_UNLOCK (project); g_free (internal_id); return FALSE; } + GES_PROJECT_UNLOCK (project); g_free (internal_id); /* TODO Add a GCancellable somewhere in our API */ @@ -852,6 +917,8 @@ ges_project_create_asset (GESProject * project, const gchar * id, * @project * * Returns: (transfer full) (nullable): The newly created #GESAsset or %NULL. + * + * MT safe. */ GESAsset * ges_project_create_asset_sync (GESProject * project, const gchar * id, @@ -869,16 +936,20 @@ ges_project_create_asset_sync (GESProject * project, const gchar * id, id = g_type_name (extractable_type); internal_id = ges_project_internal_extractable_type_id (extractable_type, id); + GES_PROJECT_LOCK (project); if ((asset = g_hash_table_lookup (project->priv->assets, internal_id))) { + GES_PROJECT_UNLOCK (project); g_free (internal_id); return gst_object_ref (asset); } else if (g_hash_table_lookup (project->priv->loading_assets, internal_id) || g_hash_table_lookup (project->priv->loaded_with_error, internal_id)) { + GES_PROJECT_UNLOCK (project); g_free (internal_id); return NULL; } + GES_PROJECT_UNLOCK (project); g_free (internal_id); /* TODO Add a GCancellable somewhere in our API */ @@ -894,8 +965,14 @@ ges_project_create_asset_sync (GESProject * project, const gchar * id, retry = FALSE; internal_id = ges_project_internal_extractable_type_id (extractable_type, id); - if ((!g_hash_table_lookup (project->priv->assets, internal_id))) + GES_PROJECT_LOCK (project); + if ((!g_hash_table_lookup (project->priv->assets, internal_id))) { + GES_PROJECT_UNLOCK (project); g_signal_emit (project, _signals[ASSET_LOADING_SIGNAL], 0, asset); + } else { + GES_PROJECT_UNLOCK (project); + } + g_free (internal_id); if (possible_id) { @@ -941,7 +1018,9 @@ ges_project_create_asset_sync (GESProject * project, const gchar * id, * @asset. * * Returns: %TRUE if the asset could be added %FALSE it was already - * in the project + * in the project. + * + * MT safe. */ gboolean ges_project_add_asset (GESProject * project, GESAsset * asset) @@ -949,15 +1028,18 @@ ges_project_add_asset (GESProject * project, GESAsset * asset) gchar *internal_id; g_return_val_if_fail (GES_IS_PROJECT (project), FALSE); + GES_PROJECT_LOCK (project); internal_id = ges_project_internal_asset_id (asset); if (g_hash_table_lookup (project->priv->assets, internal_id)) { g_free (internal_id); + GES_PROJECT_UNLOCK (project); return TRUE; } g_hash_table_insert (project->priv->assets, internal_id, gst_object_ref (asset)); g_hash_table_remove (project->priv->loading_assets, internal_id); + GES_PROJECT_UNLOCK (project); GST_DEBUG_OBJECT (project, "Asset added: %s", ges_asset_get_id (asset)); g_signal_emit (project, _signals[ASSET_ADDED_SIGNAL], 0, asset); @@ -969,9 +1051,11 @@ ges_project_add_asset (GESProject * project, GESAsset * asset) * @project: A #GESProject * @asset: (transfer none): A #GESAsset to remove from @project * - * remove a @asset to from @project. + * Remove @asset from @project. * * Returns: %TRUE if the asset could be removed %FALSE otherwise + * + * MT safe. */ gboolean ges_project_remove_asset (GESProject * project, GESAsset * asset) @@ -982,7 +1066,9 @@ ges_project_remove_asset (GESProject * project, GESAsset * asset) g_return_val_if_fail (GES_IS_PROJECT (project), FALSE); internal_id = ges_project_internal_asset_id (asset); + GES_PROJECT_LOCK (project); ret = g_hash_table_remove (project->priv->assets, internal_id); + GES_PROJECT_UNLOCK (project); g_free (internal_id); g_signal_emit (project, _signals[ASSET_REMOVED_SIGNAL], 0, asset); @@ -998,6 +1084,8 @@ ges_project_remove_asset (GESProject * project, GESAsset * asset) * * Returns: (transfer full) (nullable): The #GESAsset with * @id or %NULL if no asset with @id as an ID + * + * MT safe. */ GESAsset * ges_project_get_asset (GESProject * project, const gchar * id, @@ -1011,7 +1099,9 @@ ges_project_get_asset (GESProject * project, const gchar * id, NULL); internal_id = ges_project_internal_extractable_type_id (extractable_type, id); + GES_PROJECT_LOCK (project); asset = g_hash_table_lookup (project->priv->assets, internal_id); + GES_PROJECT_UNLOCK (project); g_free (internal_id); if (asset) @@ -1032,6 +1122,8 @@ ges_project_get_asset (GESProject * project, const gchar * id, * * Returns: (transfer full) (element-type GESAsset): The list of * #GESAsset the object contains + * + * MT safe. */ GList * ges_project_list_assets (GESProject * project, GType filter) @@ -1044,12 +1136,14 @@ ges_project_list_assets (GESProject * project, GType filter) g_return_val_if_fail (filter == G_TYPE_NONE || g_type_is_a (filter, GES_TYPE_EXTRACTABLE), NULL); + GES_PROJECT_LOCK (project); g_hash_table_iter_init (&iter, project->priv->assets); while (g_hash_table_iter_next (&iter, &key, &value)) { if (g_type_is_a (ges_asset_get_extractable_type (GES_ASSET (value)), filter)) ret = g_list_append (ret, gst_object_ref (value)); } + GES_PROJECT_UNLOCK (project); return ret; } @@ -1070,7 +1164,9 @@ ges_project_list_assets (GESProject * project, GType filter) * is one of the timelines that have been extracted from @project * (using ges_asset_extract (@project);) * - * Returns: %TRUE if the project could be save, %FALSE otherwize + * Returns: %TRUE if the project could be save, %FALSE otherwise + * + * MT safe. */ gboolean ges_project_save (GESProject * project, GESTimeline * timeline, @@ -1087,6 +1183,8 @@ ges_project_save (GESProject * project, GESTimeline * timeline, GES_TYPE_FORMATTER), FALSE); g_return_val_if_fail ((error == NULL || *error == NULL), FALSE); + GES_PROJECT_LOCK (project); + tl_asset = ges_extractable_get_asset (GES_EXTRACTABLE (timeline)); if (tl_asset == NULL && project->priv->uri == NULL) { GESAsset *asset = ges_asset_cache_lookup (GES_TYPE_PROJECT, uri); @@ -1123,12 +1221,17 @@ ges_project_save (GESProject * project, GESTimeline * timeline, goto out; } + GES_PROJECT_UNLOCK (project); ges_project_add_formatter (project, formatter); ret = ges_formatter_save_to_uri (formatter, timeline, uri, overwrite, error); if (ret && project->priv->uri == NULL) ges_project_set_uri (project, uri); + GES_PROJECT_LOCK (project); + out: + GES_PROJECT_UNLOCK (project); + if (formatter_asset) gst_object_unref (formatter_asset); ges_project_remove_formatter (project, formatter); @@ -1147,6 +1250,8 @@ out: * the URI it will keep refering to. * * Returns: A newly created #GESProject + * + * MT safe. */ GESProject * ges_project_new (const gchar * uri) @@ -1176,7 +1281,9 @@ ges_project_new (const gchar * uri) * * Loads @project into @timeline * - * Returns: %TRUE if the project could be loaded %FALSE otherwize. + * Returns: %TRUE if the project could be loaded %FALSE otherwise. + * + * MT safe. */ gboolean ges_project_load (GESProject * project, GESTimeline * timeline, GError ** error) @@ -1201,31 +1308,37 @@ ges_project_load (GESProject * project, GESTimeline * timeline, GError ** error) * Retrieve the uri that is currently set on @project * * Returns: (transfer full) (nullable): a newly allocated string representing uri. + * + * MT safe. */ gchar * ges_project_get_uri (GESProject * project) { - GESProjectPrivate *priv; + gchar *uri = NULL; g_return_val_if_fail (GES_IS_PROJECT (project), FALSE); - priv = project->priv; - if (priv->uri) - return g_strdup (priv->uri); - return NULL; + GES_PROJECT_LOCK (project); + if (project->priv->uri) + uri = g_strdup (project->priv->uri); + GES_PROJECT_UNLOCK (project); + + return uri; } /** * ges_project_add_encoding_profile: * @project: A #GESProject * @profile: A #GstEncodingProfile to add to the project. If a profile with - * the same name already exists, it will be replaced + * the same name already exists, it will be replaced. * * Adds @profile to the project. It lets you save in what format - * the project has been renders and keep a reference to those formats. - * Also, those formats will be saves to the project file when possible. + * the project will be rendered and keep a reference to those formats. + * Also, those formats will be saved to the project file when possible. * - * Returns: %TRUE if @profile could be added, %FALSE otherwize + * Returns: %TRUE if @profile could be added, %FALSE otherwise + * + * MT safe. */ gboolean ges_project_add_encoding_profile (GESProject * project, @@ -1237,6 +1350,7 @@ ges_project_add_encoding_profile (GESProject * project, g_return_val_if_fail (GES_IS_PROJECT (project), FALSE); g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), FALSE); + GES_PROJECT_LOCK (project); priv = project->priv; for (tmp = priv->encoding_profiles; tmp; tmp = tmp->next) { GstEncodingProfile *tmpprofile = GST_ENCODING_PROFILE (tmp->data); @@ -1248,13 +1362,14 @@ ges_project_add_encoding_profile (GESProject * project, gst_object_unref (tmp->data); tmp->data = gst_object_ref (profile); + GES_PROJECT_UNLOCK (project); return TRUE; } } priv->encoding_profiles = g_list_prepend (priv->encoding_profiles, gst_object_ref (profile)); - + GES_PROJECT_UNLOCK (project); return TRUE; } @@ -1284,7 +1399,9 @@ ges_project_list_encoding_profiles (GESProject * project) * * Returns: (transfer full) (element-type GES.Asset): A set of loading asset * that will be added to @project. Note that those Asset are *not* loaded yet, - * and thus can not be used + * and thus can not be used. + * + * MT safe. */ GList * ges_project_get_loading_assets (GESProject * project) @@ -1296,9 +1413,11 @@ ges_project_get_loading_assets (GESProject * project) g_return_val_if_fail (GES_IS_PROJECT (project), NULL); + GES_PROJECT_LOCK (project); g_hash_table_iter_init (&iter, project->priv->loading_assets); while (g_hash_table_iter_next (&iter, &key, &value)) ret = g_list_prepend (ret, gst_object_ref (value)); + GES_PROJECT_UNLOCK (project); return ret; } diff --git a/subprojects/gst-editing-services/ges/ges-timeline.c b/subprojects/gst-editing-services/ges/ges-timeline.c index 971e4d7ac3..5d033d6e32 100644 --- a/subprojects/gst-editing-services/ges/ges-timeline.c +++ b/subprojects/gst-editing-services/ges/ges-timeline.c @@ -2165,6 +2165,12 @@ ges_timeline_get_stream_collection (GESTimeline * timeline) return gst_object_ref (timeline->priv->stream_collection); } +gboolean +ges_timeline_in_current_thread (GESTimeline * timeline) +{ + return timeline->priv->valid_thread == g_thread_self (); +} + /**** API *****/ /** * ges_timeline_new: