diff --git a/ges/ges-clip.c b/ges/ges-clip.c index 713719664a..a15cc137e5 100644 --- a/ges/ges-clip.c +++ b/ges/ges-clip.c @@ -863,6 +863,55 @@ ges_clip_can_set_max_duration_of_child (GESClip * clip, GESTrackElement * child, return TRUE; } +gboolean +ges_clip_can_set_max_duration_of_all_core (GESClip * clip, + GstClockTime max_duration, GError ** error) +{ + GList *tmp; + GList *child_data = NULL; + + for (tmp = GES_CONTAINER_CHILDREN (clip); tmp; tmp = tmp->next) { + GESTimelineElement *child = tmp->data; + DurationLimitData *data = + _duration_limit_data_new (GES_TRACK_ELEMENT (child)); + + if (_IS_CORE_CHILD (child)) { + /* don't check that it has an internal-source, since we are assuming + * we will have one if the max-duration is valid */ + if (GES_CLOCK_TIME_IS_LESS (max_duration, child->inpoint)) { + GST_INFO_OBJECT (clip, "Cannot set the max-duration from %" + GST_TIME_FORMAT " to %" GST_TIME_FORMAT " because it would " + "cause the in-point of its core child %" GES_FORMAT + " to exceed its max-duration", + GST_TIME_ARGS (child->maxduration), + GST_TIME_ARGS (max_duration), GES_ARGS (child)); + g_set_error (error, GES_ERROR, GES_ERROR_NOT_ENOUGH_INTERNAL_CONTENT, + "Cannot set the max-duration of the child \"%s\" under the " + "clip \"%s\" to %" GST_TIME_FORMAT " because it would be " + "below the in-point of %" GST_TIME_FORMAT " of the child", + child->name, GES_TIMELINE_ELEMENT_NAME (clip), + GST_TIME_ARGS (max_duration), GST_TIME_ARGS (child->inpoint)); + + _duration_limit_data_free (data); + g_list_free_full (child_data, _duration_limit_data_free); + return FALSE; + } + data->max_duration = max_duration; + } + + child_data = g_list_prepend (child_data, data); + } + + if (!_can_update_duration_limit (clip, child_data, error)) { + GST_INFO_OBJECT (clip, "Cannot set the max-duration of the core " + "children to %" GST_TIME_FORMAT " because the duration-limit " + "cannot be adjusted", GST_TIME_ARGS (max_duration)); + return FALSE; + } + + return TRUE; +} + static void _child_max_duration_changed (GESContainer * container, GESTimelineElement * child) diff --git a/ges/ges-internal.h b/ges/ges-internal.h index 0241877685..b7fca65b26 100644 --- a/ges/ges-internal.h +++ b/ges/ges-internal.h @@ -423,6 +423,7 @@ G_GNUC_INTERNAL void ges_clip_set_moving_from_layer (GESClip *clip G_GNUC_INTERNAL GESTrackElement* ges_clip_create_track_element (GESClip *clip, GESTrackType type); G_GNUC_INTERNAL GList* ges_clip_create_track_elements (GESClip *clip, GESTrackType type); G_GNUC_INTERNAL gboolean ges_clip_can_set_inpoint_of_child (GESClip * clip, GESTrackElement * child, GstClockTime inpoint, GError ** error); +G_GNUC_INTERNAL gboolean ges_clip_can_set_max_duration_of_all_core (GESClip * clip, GstClockTime max_duration, GError ** error); G_GNUC_INTERNAL gboolean ges_clip_can_set_max_duration_of_child (GESClip * clip, GESTrackElement * child, GstClockTime max_duration, GError ** error); G_GNUC_INTERNAL gboolean ges_clip_can_set_active_of_child (GESClip * clip, GESTrackElement * child, gboolean active, GError ** error); G_GNUC_INTERNAL gboolean ges_clip_can_set_priority_of_child (GESClip * clip, GESTrackElement * child, guint32 priority, GError ** error); diff --git a/ges/ges-uri-clip.c b/ges/ges-uri-clip.c index 512991d636..c0cab061a3 100644 --- a/ges/ges-uri-clip.c +++ b/ges/ges-uri-clip.c @@ -231,34 +231,38 @@ extractable_set_asset (GESExtractable * self, GESAsset * asset) GESUriClip *uriclip = GES_URI_CLIP (self); GESUriClipAsset *uri_clip_asset; GESClip *clip = GES_CLIP (self); + GESContainer *container = GES_CONTAINER (clip); + GESTimelineElement *element = GES_TIMELINE_ELEMENT (self); GESLayer *layer = ges_clip_get_layer (clip); GList *tmp, *children; GHashTable *source_by_track; + GstClockTime max_duration; + GESAsset *prev_asset; g_return_val_if_fail (GES_IS_URI_CLIP_ASSET (asset), FALSE); uri_clip_asset = GES_URI_CLIP_ASSET (asset); - if (GST_CLOCK_TIME_IS_VALID (GES_TIMELINE_ELEMENT_DURATION (self)) && - ges_uri_clip_asset_get_duration (uri_clip_asset) < - GES_TIMELINE_ELEMENT_INPOINT (self) + - GES_TIMELINE_ELEMENT_DURATION (self)) { - GST_INFO_OBJECT (self, - "Can not set asset to %p as its duration is %" GST_TIME_FORMAT - " < to inpoint %" GST_TIME_FORMAT " + %" GST_TIME_FORMAT " = %" - GST_TIME_FORMAT, asset, - GST_TIME_ARGS (ges_uri_clip_asset_get_duration (uri_clip_asset)), - GST_TIME_ARGS (GES_TIMELINE_ELEMENT_INPOINT (self)), - GST_TIME_ARGS (GES_TIMELINE_ELEMENT_DURATION (self)), - GST_TIME_ARGS (GES_TIMELINE_ELEMENT_INPOINT (self) + - GES_TIMELINE_ELEMENT_DURATION (self))); + /* new sources elements will have their max-duration set to + * max_duration. Check that this is possible with the new uri + * NOTE: we are assuming that all the new core children will end up + * in the same tracks as the previous core children */ + max_duration = ges_uri_clip_asset_get_max_duration (uri_clip_asset); + if (!ges_clip_can_set_max_duration_of_all_core (clip, max_duration, NULL)) { + GST_INFO_OBJECT (self, "Can not set asset to %p as its max-duration %" + GST_TIME_FORMAT " is too low", asset, GST_TIME_ARGS (max_duration)); return FALSE; } - if (GST_CLOCK_TIME_IS_VALID (GES_TIMELINE_ELEMENT_DURATION (clip)) == FALSE) - _set_duration0 (GES_TIMELINE_ELEMENT (uriclip), - ges_uri_clip_asset_get_duration (uri_clip_asset)); + if (!container->children && !GST_CLOCK_TIME_IS_VALID (element->duration)) { + if (!ges_timeline_element_set_duration (element, + ges_uri_clip_asset_get_duration (uri_clip_asset))) { + GST_ERROR_OBJECT (self, "Failed to set the duration using a new " + "uri asset when we have no children. This should not happen"); + return FALSE; + } + } ges_uri_clip_set_is_image (uriclip, ges_uri_clip_asset_is_image (uri_clip_asset)); @@ -268,11 +272,35 @@ extractable_set_asset (GESExtractable * self, GESAsset * asset) ges_clip_asset_get_supported_formats (GES_CLIP_ASSET (uri_clip_asset))); } - GES_TIMELINE_ELEMENT (uriclip)->asset = asset; + prev_asset = element->asset; + element->asset = asset; + + /* FIXME: it would be much better if we could have a way to replace + * each source one-to-one with a new source in the same track, e.g. + * a user supplied + * GESSource * ges_uri_clip_swap_source ( + * GESClip * clip, GESSource * replace, GList * new_sources, + * gpointer user_data) + * + * and they select a new source from new_sources to replace @replace, or + * %NULL to remove it without a replacement. The default would swap + * one video for another video, etc. + * + * Then we could use this information with + * ges_clip_can_update_duration_limit, using the new max-duration and + * replacing each source in the same track, to test that the operation + * can succeed (basically extending + * ges_clip_can_set_max_duration_of_all_core, but with the added + * information that sources without a replacement will not contribute + * to the duration-limit, and all of the siblings in the same track will + * also be removed from the track). + * + * Then we can perform the replacement, whilst avoiding track-selection + * (similar to GESClip's _transfer_child). */ source_by_track = g_hash_table_new_full (NULL, NULL, gst_object_unref, gst_object_unref); - children = ges_container_get_children (GES_CONTAINER (self), TRUE); + children = ges_container_get_children (container, FALSE); for (tmp = children; tmp; tmp = tmp->next) { GESTrackElement *child = tmp->data; @@ -287,39 +315,51 @@ extractable_set_asset (GESExtractable * self, GESAsset * asset) * timeline will remove it from its track */ /* removing the core element will also empty its non-core siblings * from the same track */ - ges_container_remove (GES_CONTAINER (self), GES_TIMELINE_ELEMENT (child)); + ges_container_remove (container, GES_TIMELINE_ELEMENT (child)); } } g_list_free_full (children, g_object_unref); contains_core = FALSE; + /* keep alive */ + gst_object_ref (self); if (layer) { - gst_object_ref (clip); - ges_layer_remove_clip (layer, clip); - /* adding back to the layer will trigger the re-creation of the core - * children */ - res = ges_layer_add_clip (layer, clip); + res = ges_layer_remove_clip (layer, clip); - /* NOTE: assume that core children in the same tracks correspond to - * the same source! */ - for (tmp = GES_CONTAINER_CHILDREN (clip); tmp; tmp = tmp->next) { - GESTrackElement *child = tmp->data; - if (ges_track_element_is_core (child)) { - GESTrackElement *orig_source = g_hash_table_lookup (source_by_track, - ges_track_element_get_track (child)); - contains_core = TRUE; + if (res) { + /* adding back to the layer will trigger the re-creation of the core + * children */ + res = ges_layer_add_clip (layer, clip); - if (orig_source) { - ges_track_element_copy_properties (GES_TIMELINE_ELEMENT (orig_source), - GES_TIMELINE_ELEMENT (child)); - ges_track_element_copy_bindings (orig_source, child, - GST_CLOCK_TIME_NONE); + if (!res) + GST_ERROR_OBJECT (self, "Failed to add the uri clip %s back into " + "its layer. This is likely caused by track-selection for the " + "core sources and effects failing because the core sources " + "were not replaced in the same tracks", element->name); + + /* NOTE: assume that core children in the same tracks correspond to + * the same source! */ + for (tmp = container->children; tmp; tmp = tmp->next) { + GESTrackElement *child = tmp->data; + if (ges_track_element_is_core (child)) { + GESTrackElement *orig_source = g_hash_table_lookup (source_by_track, + ges_track_element_get_track (child)); + contains_core = TRUE; + + if (orig_source) { + ges_track_element_copy_properties (GES_TIMELINE_ELEMENT + (orig_source), GES_TIMELINE_ELEMENT (child)); + ges_track_element_copy_bindings (orig_source, child, + GST_CLOCK_TIME_NONE); + } } } + } else { + GST_ERROR_OBJECT (self, "Failed to remove from the layer. This " + "should not happen"); } - gst_object_unref (clip); gst_object_unref (layer); } g_hash_table_unref (source_by_track); @@ -327,11 +367,18 @@ extractable_set_asset (GESExtractable * self, GESAsset * asset) if (res) { g_free (uriclip->priv->uri); uriclip->priv->uri = g_strdup (ges_asset_get_id (asset)); + + if (!contains_core) { + if (!ges_timeline_element_set_max_duration (element, max_duration)) + GST_ERROR_OBJECT (self, "Failed to set the max-duration on the uri " + "clip when it has no children. This should not happen"); + } + } else { + element->asset = prev_asset; } - if (!contains_core) - ges_timeline_element_set_max_duration (GES_TIMELINE_ELEMENT (uriclip), - ges_uri_clip_asset_get_max_duration (uri_clip_asset)); + /* if re-adding failed, clip may be destroyed */ + gst_object_unref (self); return res; } diff --git a/tests/check/ges/asset.c b/tests/check/ges/asset.c index d8cb49b7bf..1e1188ad9f 100644 --- a/tests/check/ges/asset.c +++ b/tests/check/ges/asset.c @@ -228,9 +228,6 @@ GST_START_TEST (test_uri_clip_change_asset) asset1 = GES_ASSET (ges_uri_clip_asset_request_sync (uri1, NULL)); fail_unless_equals_int (g_list_length (GES_CONTAINER_CHILDREN (extractable)), 2); - fail_if (ges_extractable_set_asset (extractable, asset1)); - ges_timeline_element_set_duration (GES_TIMELINE_ELEMENT (extractable), - ges_uri_clip_asset_get_duration (GES_URI_CLIP_ASSET (asset1))); fail_unless (ges_extractable_set_asset (extractable, asset1)); fail_unless_equals_int (g_list_length (GES_CONTAINER_CHILDREN (extractable)), 1);