1 /* GStreamer Editing Services
2 * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
3 * 2009 Nokia Corporation
4 * 2011 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
5 * 2013 Thibault Saunier <thibault.saunier@collabora.com>
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
26 * @short_description: Non-overlapping sequence of GESClip
28 * Responsible for the ordering of the various contained Clip(s). A
29 * timeline layer has a "priority" property, which is used to manage the
30 * priorities of individual Clips. Two layers should not have the
31 * same priority within a given timeline.
37 #include "ges-internal.h"
38 #include "ges-layer.h"
40 #include "ges-source-clip.h"
42 static void ges_meta_container_interface_init
43 (GESMetaContainerInterface * iface);
45 struct _GESLayerPrivate
48 GList *clips_start; /* The Clips sorted by start and
51 guint32 priority; /* The priority of the layer within the
52 * containing timeline */
53 gboolean auto_transition;
77 static guint ges_layer_signals[LAST_SIGNAL] = { 0 };
79 G_DEFINE_TYPE_WITH_CODE (GESLayer, ges_layer,
80 G_TYPE_INITIALLY_UNOWNED, G_IMPLEMENT_INTERFACE (GES_TYPE_EXTRACTABLE, NULL)
81 G_ADD_PRIVATE (GESLayer)
82 G_IMPLEMENT_INTERFACE (GES_TYPE_META_CONTAINER,
83 ges_meta_container_interface_init));
85 /* GObject standard vmethods */
87 ges_layer_get_property (GObject * object, guint property_id,
88 GValue * value, GParamSpec * pspec)
90 GESLayer *layer = GES_LAYER (object);
92 switch (property_id) {
94 g_value_set_uint (value, layer->priv->priority);
96 case PROP_AUTO_TRANSITION:
97 g_value_set_boolean (value, layer->priv->auto_transition);
100 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
105 ges_layer_set_property (GObject * object, guint property_id,
106 const GValue * value, GParamSpec * pspec)
108 GESLayer *layer = GES_LAYER (object);
110 switch (property_id) {
112 GST_FIXME ("Deprecated, use ges_timeline_move_layer instead");
113 layer_set_priority (layer, g_value_get_uint (value), FALSE);
115 case PROP_AUTO_TRANSITION:
116 ges_layer_set_auto_transition (layer, g_value_get_boolean (value));
119 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
124 ges_layer_dispose (GObject * object)
126 GESLayer *layer = GES_LAYER (object);
127 GESLayerPrivate *priv = layer->priv;
129 GST_DEBUG ("Disposing layer");
131 while (priv->clips_start)
132 ges_layer_remove_clip (layer, (GESClip *) priv->clips_start->data);
134 G_OBJECT_CLASS (ges_layer_parent_class)->dispose (object);
138 _register_metas (GESLayer * layer)
140 ges_meta_container_register_meta_float (GES_META_CONTAINER (layer),
141 GES_META_READ_WRITE, GES_META_VOLUME, 1.0);
147 ges_meta_container_interface_init (GESMetaContainerInterface * iface)
153 ges_layer_class_init (GESLayerClass * klass)
155 GObjectClass *object_class = G_OBJECT_CLASS (klass);
157 object_class->get_property = ges_layer_get_property;
158 object_class->set_property = ges_layer_set_property;
159 object_class->dispose = ges_layer_dispose;
164 * The priority of the layer in the #GESTimeline. 0 is the highest
165 * priority. Conceptually, a #GESTimeline is a stack of GESLayers,
166 * and the priority of the layer represents its position in the stack. Two
167 * layers should not have the same priority within a given GESTimeline.
169 * Note that the timeline needs to be commited (with #ges_timeline_commit)
170 * for the change to be taken into account.
172 * Deprecated:1.16.0: use #ges_timeline_move_layer instead. This deprecation means
173 * that you will not need to handle layer priorities at all yourself, GES
174 * will make sure there is never 'gaps' between layer priorities.
176 g_object_class_install_property (object_class, PROP_PRIORITY,
177 g_param_spec_uint ("priority", "Priority",
178 "The priority of the layer", 0, G_MAXUINT, 0, G_PARAM_READWRITE));
181 * GESLayer:auto-transition:
183 * Sets whether transitions are added automagically when clips overlap.
185 g_object_class_install_property (object_class, PROP_AUTO_TRANSITION,
186 g_param_spec_boolean ("auto-transition", "Auto-Transition",
187 "whether the transitions are added", FALSE, G_PARAM_READWRITE));
190 * GESLayer::clip-added:
191 * @layer: the #GESLayer
192 * @clip: the #GESClip that was added.
194 * Will be emitted after the clip was added to the layer.
196 ges_layer_signals[OBJECT_ADDED] =
197 g_signal_new ("clip-added", G_TYPE_FROM_CLASS (klass),
198 G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESLayerClass, object_added),
199 NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, GES_TYPE_CLIP);
202 * GESLayer::clip-removed:
203 * @layer: the #GESLayer
204 * @clip: the #GESClip that was removed
206 * Will be emitted after the clip was removed from the layer.
208 ges_layer_signals[OBJECT_REMOVED] =
209 g_signal_new ("clip-removed", G_TYPE_FROM_CLASS (klass),
210 G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESLayerClass,
211 object_removed), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE,
216 ges_layer_init (GESLayer * self)
218 self->priv = ges_layer_get_instance_private (self);
220 self->priv->priority = 0;
221 self->priv->auto_transition = FALSE;
222 self->min_nle_priority = MIN_NLE_PRIO;
223 self->max_nle_priority = LAYER_HEIGHT + MIN_NLE_PRIO;
225 _register_metas (self);
229 ges_layer_resync_priorities_by_type (GESLayer * layer,
230 gint starting_priority, GType type)
232 GstClockTime next_reset = 0;
233 gint priority = starting_priority, max_priority = priority;
235 GESTimelineElement *element;
237 layer->priv->clips_start =
238 g_list_sort (layer->priv->clips_start,
239 (GCompareFunc) element_start_compare);
240 for (tmp = layer->priv->clips_start; tmp; tmp = tmp->next) {
242 element = GES_TIMELINE_ELEMENT (tmp->data);
244 if (GES_IS_TRANSITION_CLIP (element)) {
245 /* Blindly set transitions priorities to 0 */
246 _set_priority0 (element, 0);
248 } else if (!g_type_is_a (G_OBJECT_TYPE (element), type))
251 if (element->start > next_reset) {
252 priority = starting_priority;
256 if (element->start + element->duration > next_reset)
257 next_reset = element->start + element->duration;
259 _set_priority0 (element, priority);
260 priority = priority + GES_CONTAINER_HEIGHT (element);
262 if (priority > max_priority)
263 max_priority = priority;
270 * ges_layer_resync_priorities:
271 * @layer: a #GESLayer
273 * Resyncs the priorities of the clips controlled by @layer.
276 ges_layer_resync_priorities (GESLayer * layer)
278 gint min_source_prios;
280 g_return_val_if_fail (GES_IS_LAYER (layer), FALSE);
282 GST_INFO_OBJECT (layer, "Resync priorities (prio: %d)",
283 layer->priv->priority);
285 min_source_prios = ges_layer_resync_priorities_by_type (layer, 1,
286 GES_TYPE_OPERATION_CLIP);
288 ges_layer_resync_priorities_by_type (layer, min_source_prios,
289 GES_TYPE_SOURCE_CLIP);
295 layer_set_priority (GESLayer * layer, guint priority, gboolean emit)
297 GST_DEBUG ("layer:%p, priority:%d", layer, priority);
299 if (priority != layer->priv->priority) {
300 layer->priv->priority = priority;
301 layer->min_nle_priority = (priority * LAYER_HEIGHT) + MIN_NLE_PRIO;
302 layer->max_nle_priority = ((priority + 1) * LAYER_HEIGHT) + MIN_NLE_PRIO;
304 ges_layer_resync_priorities (layer);
308 g_object_notify (G_OBJECT (layer), "priority");
312 new_asset_cb (GESAsset * source, GAsyncResult * res, NewAssetUData * udata)
314 GError *error = NULL;
316 GESAsset *asset = ges_asset_request_finish (res, &error);
318 GST_DEBUG_OBJECT (udata->layer, "%" GST_PTR_FORMAT " Asset loaded, "
319 "setting its asset", udata->clip);
322 GESProject *project = udata->layer->timeline ?
323 GES_PROJECT (ges_extractable_get_asset (GES_EXTRACTABLE
324 (udata->layer->timeline))) : NULL;
328 possible_id = ges_project_try_updating_id (project, source, error);
330 ges_asset_request_async (ges_asset_get_extractable_type (source),
331 possible_id, NULL, (GAsyncReadyCallback) new_asset_cb, udata);
332 g_free (possible_id);
337 GST_ERROR ("Asset could not be created for uri %s, error: %s",
338 ges_asset_get_id (asset), error->message);
340 GESProject *project = udata->layer->timeline ?
341 GES_PROJECT (ges_extractable_get_asset (GES_EXTRACTABLE
342 (udata->layer->timeline))) : NULL;
343 ges_extractable_set_asset (GES_EXTRACTABLE (udata->clip), asset);
345 ges_project_add_asset (project, asset);
347 /* clip was already ref-sinked when creating udata,
348 * gst_layer_add_clip() creates a new ref as such and
349 * below we unref the ref from udata */
350 ges_layer_add_clip (udata->layer, udata->clip);
353 gst_object_unref (asset);
354 gst_object_unref (udata->clip);
355 g_slice_free (NewAssetUData, udata);
359 * ges_layer_get_duration:
360 * @layer: The #GESLayer to get the duration from
362 * Lets you retrieve the duration of the layer, which means
363 * the end time of the last clip inside it
365 * Returns: The duration of a layer
368 ges_layer_get_duration (GESLayer * layer)
371 GstClockTime duration = 0;
373 g_return_val_if_fail (GES_IS_LAYER (layer), 0);
375 for (tmp = layer->priv->clips_start; tmp; tmp = tmp->next) {
376 duration = MAX (duration, _END (tmp->data));
384 * ges_layer_remove_clip:
385 * @layer: a #GESLayer
386 * @clip: the #GESClip to remove
388 * Removes the given @clip from the @layer and unparents it.
389 * Unparenting it means the reference owned by @layer on the @clip will be
390 * removed. If you wish to use the @clip after this function, make sure you
391 * call gst_object_ref() before removing it from the @layer.
393 * Returns: %TRUE if the clip could be removed, %FALSE if the layer does
394 * not want to remove the clip.
397 ges_layer_remove_clip (GESLayer * layer, GESClip * clip)
399 GESLayer *current_layer;
401 g_return_val_if_fail (GES_IS_LAYER (layer), FALSE);
402 g_return_val_if_fail (GES_IS_CLIP (clip), FALSE);
404 GST_DEBUG ("layer:%p, clip:%p", layer, clip);
406 current_layer = ges_clip_get_layer (clip);
407 if (G_UNLIKELY (current_layer != layer)) {
408 GST_WARNING ("Clip doesn't belong to this layer");
410 if (current_layer != NULL)
411 gst_object_unref (current_layer);
415 gst_object_unref (current_layer);
417 /* Remove it from our list of controlled objects */
418 layer->priv->clips_start = g_list_remove (layer->priv->clips_start, clip);
420 /* emit 'clip-removed' */
421 g_signal_emit (layer, ges_layer_signals[OBJECT_REMOVED], 0, clip);
423 /* inform the clip it's no longer in a layer */
424 ges_clip_set_layer (clip, NULL);
425 /* so neither in a timeline */
427 ges_timeline_element_set_timeline (GES_TIMELINE_ELEMENT (clip), NULL);
429 /* Remove our reference to the clip */
430 gst_object_unref (clip);
436 * ges_layer_set_priority:
437 * @layer: a #GESLayer
438 * @priority: the priority to set
440 * Sets the layer to the given @priority. See the documentation of the
441 * priority property for more information.
443 * Deprecated:1.16.0: use #ges_timeline_move_layer instead. This deprecation means
444 * that you will not need to handle layer priorities at all yourself, GES
445 * will make sure there is never 'gaps' between layer priorities.
448 ges_layer_set_priority (GESLayer * layer, guint priority)
450 g_return_if_fail (GES_IS_LAYER (layer));
452 GST_FIXME ("Deprecated, use ges_timeline_move_layer instead");
454 layer_set_priority (layer, priority, TRUE);
458 * ges_layer_get_auto_transition:
459 * @layer: a #GESLayer
461 * Gets whether transitions are automatically added when objects
464 * Returns: %TRUE if transitions are automatically added, else %FALSE.
467 ges_layer_get_auto_transition (GESLayer * layer)
469 g_return_val_if_fail (GES_IS_LAYER (layer), 0);
471 return layer->priv->auto_transition;
475 * ges_layer_set_auto_transition:
476 * @layer: a #GESLayer
477 * @auto_transition: whether the auto_transition is active
479 * Sets the layer to the given @auto_transition. See the documentation of the
480 * property auto_transition for more information.
483 ges_layer_set_auto_transition (GESLayer * layer, gboolean auto_transition)
486 g_return_if_fail (GES_IS_LAYER (layer));
488 layer->priv->auto_transition = auto_transition;
489 g_object_notify (G_OBJECT (layer), "auto-transition");
493 * ges_layer_get_priority:
494 * @layer: a #GESLayer
496 * Get the priority of @layer within the timeline.
498 * Returns: The priority of the @layer within the timeline.
501 ges_layer_get_priority (GESLayer * layer)
503 g_return_val_if_fail (GES_IS_LAYER (layer), 0);
505 return layer->priv->priority;
509 * ges_layer_get_clips:
510 * @layer: a #GESLayer
512 * Get the clips this layer contains.
514 * Returns: (transfer full) (element-type GESClip): a #GList of
515 * clips. The user is responsible for
516 * unreffing the contained objects and freeing the list.
520 ges_layer_get_clips (GESLayer * layer)
522 GESLayerClass *klass;
524 g_return_val_if_fail (GES_IS_LAYER (layer), NULL);
526 klass = GES_LAYER_GET_CLASS (layer);
528 if (klass->get_objects) {
529 return klass->get_objects (layer);
532 return g_list_sort (g_list_copy_deep (layer->priv->clips_start,
533 (GCopyFunc) gst_object_ref, NULL),
534 (GCompareFunc) element_start_compare);
538 * ges_layer_is_empty:
539 * @layer: The #GESLayer to check
541 * Convenience method to check if @layer is empty (doesn't contain any clip),
544 * Returns: %TRUE if @layer is empty, %FALSE if it already contains at least
548 ges_layer_is_empty (GESLayer * layer)
550 g_return_val_if_fail (GES_IS_LAYER (layer), FALSE);
552 return (layer->priv->clips_start == NULL);
556 * ges_layer_add_clip:
557 * @layer: a #GESLayer
558 * @clip: (transfer floating): the #GESClip to add.
560 * Adds the given clip to the layer. Sets the clip's parent, and thus
561 * takes ownership of the clip.
563 * An clip can only be added to one layer.
565 * Calling this method will construct and properly set all the media related
566 * elements on @clip. If you need to know when those objects (actually #GESTrackElement)
567 * are constructed, you should connect to the container::child-added signal which
568 * is emited right after those elements are ready to be used.
570 * Returns: %TRUE if the clip was properly added to the layer, or %FALSE
571 * if the @layer refuses to add the clip.
574 ges_layer_add_clip (GESLayer * layer, GESClip * clip)
577 GESLayerPrivate *priv;
578 GESLayer *current_layer;
580 g_return_val_if_fail (GES_IS_LAYER (layer), FALSE);
581 g_return_val_if_fail (GES_IS_CLIP (clip), FALSE);
583 GST_DEBUG_OBJECT (layer, "adding clip:%p", clip);
586 current_layer = ges_clip_get_layer (clip);
587 if (G_UNLIKELY (current_layer)) {
588 GST_WARNING ("Clip %p already belongs to another layer", clip);
589 gst_object_ref_sink (clip);
590 gst_object_unref (current_layer);
595 asset = ges_extractable_get_asset (GES_EXTRACTABLE (clip));
598 NewAssetUData *mudata = g_slice_new (NewAssetUData);
600 mudata->clip = gst_object_ref_sink (clip);
601 mudata->layer = layer;
603 GST_DEBUG_OBJECT (layer, "%" GST_PTR_FORMAT " as no reference to any "
604 "assets creating a asset... trying sync", clip);
606 id = ges_extractable_get_id (GES_EXTRACTABLE (clip));
607 asset = ges_asset_request (G_OBJECT_TYPE (clip), id, NULL);
609 GESProject *project = layer->timeline ?
610 GES_PROJECT (ges_extractable_get_asset (GES_EXTRACTABLE
611 (layer->timeline))) : NULL;
613 ges_asset_request_async (G_OBJECT_TYPE (clip),
614 id, NULL, (GAsyncReadyCallback) new_asset_cb, mudata);
617 ges_project_add_loading_asset (project, G_OBJECT_TYPE (clip), id);
620 GST_LOG_OBJECT (layer, "Object added async");
625 ges_extractable_set_asset (GES_EXTRACTABLE (clip), asset);
627 g_slice_free (NewAssetUData, mudata);
629 gst_object_ref_sink (clip);
632 /* Take a reference to the clip and store it stored by start/priority */
633 priv->clips_start = g_list_insert_sorted (priv->clips_start, clip,
634 (GCompareFunc) element_start_compare);
636 /* Inform the clip it's now in this layer */
637 ges_clip_set_layer (clip, layer);
639 GST_DEBUG ("current clip priority : %d, Height: %d", _PRIORITY (clip),
642 /* Set the priority. */
643 if (_PRIORITY (clip) > LAYER_HEIGHT) {
644 GST_WARNING_OBJECT (layer,
645 "%p is out of the layer space, setting its priority to "
646 "%d, setting it to the maximum priority of the layer: %d", clip,
647 _PRIORITY (clip), LAYER_HEIGHT - 1);
648 _set_priority0 (GES_TIMELINE_ELEMENT (clip), LAYER_HEIGHT - 1);
651 ges_layer_resync_priorities (layer);
653 ges_timeline_element_set_timeline (GES_TIMELINE_ELEMENT (clip),
656 /* emit 'clip-added' */
657 g_signal_emit (layer, ges_layer_signals[OBJECT_ADDED], 0, clip);
663 * ges_layer_add_asset:
664 * @layer: a #GESLayer
665 * @asset: The asset to add to
666 * @start: The start value to set on the new #GESClip,
667 * if @start == GST_CLOCK_TIME_NONE, it will be set to
668 * the current duration of @layer
669 * @inpoint: The inpoint value to set on the new #GESClip
670 * @duration: The duration value to set on the new #GESClip
671 * @track_types: The #GESTrackType to set on the the new #GESClip
673 * Creates Clip from asset, adds it to layer and
674 * returns a reference to it.
676 * Returns: (transfer none): Created #GESClip
679 ges_layer_add_asset (GESLayer * layer,
680 GESAsset * asset, GstClockTime start, GstClockTime inpoint,
681 GstClockTime duration, GESTrackType track_types)
685 g_return_val_if_fail (GES_IS_LAYER (layer), NULL);
686 g_return_val_if_fail (GES_IS_ASSET (asset), NULL);
687 g_return_val_if_fail (g_type_is_a (ges_asset_get_extractable_type
688 (asset), GES_TYPE_CLIP), NULL);
690 GST_DEBUG_OBJECT (layer, "Adding asset %s with: start: %" GST_TIME_FORMAT
691 " inpoint: %" GST_TIME_FORMAT " duration: %" GST_TIME_FORMAT
692 " track types: %d (%s)", ges_asset_get_id (asset), GST_TIME_ARGS (start),
693 GST_TIME_ARGS (inpoint), GST_TIME_ARGS (duration), track_types,
694 ges_track_type_name (track_types));
696 clip = GES_CLIP (ges_asset_extract (asset, NULL));
698 if (!GST_CLOCK_TIME_IS_VALID (start)) {
699 start = ges_layer_get_duration (layer);
701 GST_DEBUG_OBJECT (layer,
702 "No start specified, setting it to %" GST_TIME_FORMAT,
703 GST_TIME_ARGS (start));
706 _set_start0 (GES_TIMELINE_ELEMENT (clip), start);
707 _set_inpoint0 (GES_TIMELINE_ELEMENT (clip), inpoint);
708 if (track_types != GES_TRACK_TYPE_UNKNOWN)
709 ges_clip_set_supported_formats (clip, track_types);
711 if (GST_CLOCK_TIME_IS_VALID (duration)) {
712 _set_duration0 (GES_TIMELINE_ELEMENT (clip), duration);
715 if (!ges_layer_add_clip (layer, clip)) {
725 * Creates a new #GESLayer.
727 * Returns: (transfer floating): A new #GESLayer
732 return g_object_new (GES_TYPE_LAYER, NULL);
736 * ges_layer_get_timeline:
737 * @layer: The #GESLayer to get the parent #GESTimeline from
739 * Get the #GESTimeline in which #GESLayer currently is.
741 * Returns: (transfer none) (nullable): the #GESTimeline in which #GESLayer
742 * currently is or %NULL if not in any timeline yet.
745 ges_layer_get_timeline (GESLayer * layer)
747 g_return_val_if_fail (GES_IS_LAYER (layer), NULL);
749 return layer->timeline;
753 ges_layer_set_timeline (GESLayer * layer, GESTimeline * timeline)
757 g_return_if_fail (GES_IS_LAYER (layer));
759 GST_DEBUG ("layer:%p, timeline:%p", layer, timeline);
761 for (tmp = layer->priv->clips_start; tmp; tmp = tmp->next) {
762 ges_timeline_element_set_timeline (tmp->data, timeline);
765 layer->timeline = timeline;
769 * ges_layer_get_clips_in_interval:
770 * @layer: a #GESLayer
771 * @start: start of the interval
772 * @end: end of the interval
774 * Gets the clips which appear between @start and @end on @layer.
776 * Returns: (transfer full) (element-type GESClip): a #GList of clips intersecting [@start, @end) interval on @layer.
779 ges_layer_get_clips_in_interval (GESLayer * layer, GstClockTime start,
783 GList *intersecting_clips = NULL;
784 GstClockTime clip_start, clip_end;
785 gboolean clip_intersects;
787 g_return_val_if_fail (GES_IS_LAYER (layer), NULL);
789 layer->priv->clips_start =
790 g_list_sort (layer->priv->clips_start,
791 (GCompareFunc) element_start_compare);
792 for (tmp = layer->priv->clips_start; tmp; tmp = tmp->next) {
793 clip_intersects = FALSE;
794 clip_start = ges_timeline_element_get_start (tmp->data);
795 clip_end = clip_start + ges_timeline_element_get_duration (tmp->data);
796 if (start <= clip_start && clip_start < end)
797 clip_intersects = TRUE;
798 else if (start < clip_end && clip_end <= end)
799 clip_intersects = TRUE;
800 else if (clip_start < start && clip_end > end)
801 clip_intersects = TRUE;
805 g_list_insert_sorted (intersecting_clips,
806 gst_object_ref (tmp->data), (GCompareFunc) element_start_compare);
808 return intersecting_clips;