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.
34 #include "ges-internal.h"
35 #include "ges-layer.h"
37 #include "ges-source-clip.h"
39 static void ges_meta_container_interface_init
40 (GESMetaContainerInterface * iface);
42 G_DEFINE_TYPE_WITH_CODE (GESLayer, ges_layer,
43 G_TYPE_INITIALLY_UNOWNED, G_IMPLEMENT_INTERFACE (GES_TYPE_EXTRACTABLE, NULL)
44 G_IMPLEMENT_INTERFACE (GES_TYPE_META_CONTAINER,
45 ges_meta_container_interface_init));
47 struct _GESLayerPrivate
50 GList *clips_start; /* The Clips sorted by start and
53 guint32 priority; /* The priority of the layer within the
54 * containing timeline */
55 gboolean auto_transition;
79 static guint ges_layer_signals[LAST_SIGNAL] = { 0 };
81 /* GObject standard vmethods */
83 ges_layer_get_property (GObject * object, guint property_id,
84 GValue * value, GParamSpec * pspec)
86 GESLayer *layer = GES_LAYER (object);
88 switch (property_id) {
90 g_value_set_uint (value, layer->priv->priority);
92 case PROP_AUTO_TRANSITION:
93 g_value_set_boolean (value, layer->priv->auto_transition);
96 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
101 ges_layer_set_property (GObject * object, guint property_id,
102 const GValue * value, GParamSpec * pspec)
104 GESLayer *layer = GES_LAYER (object);
106 switch (property_id) {
108 ges_layer_set_priority (layer, g_value_get_uint (value));
110 case PROP_AUTO_TRANSITION:
111 ges_layer_set_auto_transition (layer, g_value_get_boolean (value));
114 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
119 ges_layer_dispose (GObject * object)
121 GESLayer *layer = GES_LAYER (object);
122 GESLayerPrivate *priv = layer->priv;
124 GST_DEBUG ("Disposing layer");
126 while (priv->clips_start)
127 ges_layer_remove_clip (layer, (GESClip *) priv->clips_start->data);
129 G_OBJECT_CLASS (ges_layer_parent_class)->dispose (object);
133 _register_metas (GESLayer * layer)
135 ges_meta_container_register_meta_float (GES_META_CONTAINER (layer),
136 GES_META_READ_WRITE, GES_META_VOLUME, 1.0);
142 ges_meta_container_interface_init (GESMetaContainerInterface * iface)
148 ges_layer_class_init (GESLayerClass * klass)
150 GObjectClass *object_class = G_OBJECT_CLASS (klass);
152 g_type_class_add_private (klass, sizeof (GESLayerPrivate));
154 object_class->get_property = ges_layer_get_property;
155 object_class->set_property = ges_layer_set_property;
156 object_class->dispose = ges_layer_dispose;
161 * The priority of the layer in the #GESTimeline. 0 is the highest
162 * priority. Conceptually, a #GESTimeline is a stack of GESLayers,
163 * and the priority of the layer represents its position in the stack. Two
164 * layers should not have the same priority within a given GESTimeline.
166 * Note that the timeline needs to be commited (with #ges_timeline_commit)
167 * for the change to be taken into account.
169 g_object_class_install_property (object_class, PROP_PRIORITY,
170 g_param_spec_uint ("priority", "Priority",
171 "The priority of the layer", 0, G_MAXUINT, 0, G_PARAM_READWRITE));
174 * GESLayer:auto-transition:
176 * Sets whether transitions are added automagically when clips overlap.
178 g_object_class_install_property (object_class, PROP_AUTO_TRANSITION,
179 g_param_spec_boolean ("auto-transition", "Auto-Transition",
180 "whether the transitions are added", FALSE, G_PARAM_READWRITE));
183 * GESLayer::clip-added:
184 * @layer: the #GESLayer
185 * @clip: the #GESClip that was added.
187 * Will be emitted after the clip was added to the layer.
189 ges_layer_signals[OBJECT_ADDED] =
190 g_signal_new ("clip-added", G_TYPE_FROM_CLASS (klass),
191 G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESLayerClass, object_added),
192 NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, GES_TYPE_CLIP);
195 * GESLayer::clip-removed:
196 * @layer: the #GESLayer
197 * @clip: the #GESClip that was removed
199 * Will be emitted after the clip was removed from the layer.
201 ges_layer_signals[OBJECT_REMOVED] =
202 g_signal_new ("clip-removed", G_TYPE_FROM_CLASS (klass),
203 G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESLayerClass,
204 object_removed), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE,
209 ges_layer_init (GESLayer * self)
211 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
212 GES_TYPE_LAYER, GESLayerPrivate);
214 self->priv->priority = 0;
215 self->priv->auto_transition = FALSE;
216 self->min_nle_priority = MIN_NLE_PRIO;
217 self->max_nle_priority = LAYER_HEIGHT + MIN_NLE_PRIO;
219 _register_metas (self);
223 ges_layer_resync_priorities_by_type (GESLayer * layer,
224 gint starting_priority, GType type)
226 GstClockTime next_reset = 0;
227 gint priority = starting_priority, max_priority = priority;
229 GESTimelineElement *element;
231 for (tmp = layer->priv->clips_start; tmp; tmp = tmp->next) {
233 element = GES_TIMELINE_ELEMENT (tmp->data);
235 if (GES_IS_TRANSITION_CLIP (element)) {
236 /* Blindly set transitions priorities to 0 */
237 _set_priority0 (element, 0);
239 } else if (!g_type_is_a (G_OBJECT_TYPE (element), type))
242 if (element->start > next_reset) {
243 priority = starting_priority;
247 if (element->start + element->duration > next_reset)
248 next_reset = element->start + element->duration;
250 _set_priority0 (element, priority);
251 priority = priority + GES_CONTAINER_HEIGHT (element);
253 if (priority > max_priority)
254 max_priority = priority;
261 * ges_layer_resync_priorities:
262 * @layer: a #GESLayer
264 * Resyncs the priorities of the clips controlled by @layer.
267 ges_layer_resync_priorities (GESLayer * layer)
269 gint min_source_prios;
271 GST_INFO_OBJECT (layer, "Resync priorities (prio: %d)",
272 layer->priv->priority);
274 min_source_prios = ges_layer_resync_priorities_by_type (layer, 1,
275 GES_TYPE_OPERATION_CLIP);
277 ges_layer_resync_priorities_by_type (layer, min_source_prios,
278 GES_TYPE_SOURCE_CLIP);
284 new_asset_cb (GESAsset * source, GAsyncResult * res, NewAssetUData * udata)
286 GError *error = NULL;
288 GESAsset *asset = ges_asset_request_finish (res, &error);
290 GST_DEBUG_OBJECT (udata->layer, "%" GST_PTR_FORMAT " Asset loaded, "
291 "setting its asset", udata->clip);
294 GESProject *project = udata->layer->timeline ?
295 GES_PROJECT (ges_extractable_get_asset (GES_EXTRACTABLE
296 (udata->layer->timeline))) : NULL;
300 possible_id = ges_project_try_updating_id (project, source, error);
302 ges_asset_request_async (ges_asset_get_extractable_type (source),
303 possible_id, NULL, (GAsyncReadyCallback) new_asset_cb, udata);
304 g_free (possible_id);
309 GST_ERROR ("Asset could not be created for uri %s, error: %s",
310 ges_asset_get_id (asset), error->message);
313 GESProject *project = udata->layer->timeline ?
314 GES_PROJECT (ges_extractable_get_asset (GES_EXTRACTABLE
315 (udata->layer->timeline))) : NULL;
316 ges_extractable_set_asset (GES_EXTRACTABLE (udata->clip), asset);
318 ges_project_add_asset (project, asset);
319 ges_layer_add_clip (udata->layer, udata->clip);
322 gst_object_unref (asset);
323 g_slice_free (NewAssetUData, udata);
327 * ges_layer_get_duration:
328 * @layer: The #GESLayer to get the duration from
330 * Lets you retrieve the duration of the layer, which means
331 * the end time of the last clip inside it
333 * Returns: The duration of a layer
336 ges_layer_get_duration (GESLayer * layer)
339 GstClockTime duration = 0;
341 for (tmp = layer->priv->clips_start; tmp; tmp = tmp->next) {
342 duration = MAX (duration, _END (tmp->data));
350 * ges_layer_remove_clip:
351 * @layer: a #GESLayer
352 * @clip: the #GESClip to remove
354 * Removes the given @clip from the @layer and unparents it.
355 * Unparenting it means the reference owned by @layer on the @clip will be
356 * removed. If you wish to use the @clip after this function, make sure you
357 * call gst_object_ref() before removing it from the @layer.
359 * Returns: TRUE if the clip could be removed, FALSE if the layer does
360 * not want to remove the clip.
363 ges_layer_remove_clip (GESLayer * layer, GESClip * clip)
365 GESLayer *current_layer;
367 g_return_val_if_fail (GES_IS_LAYER (layer), FALSE);
368 g_return_val_if_fail (GES_IS_CLIP (clip), FALSE);
370 GST_DEBUG ("layer:%p, clip:%p", layer, clip);
372 current_layer = ges_clip_get_layer (clip);
373 if (G_UNLIKELY (current_layer != layer)) {
374 GST_WARNING ("Clip doesn't belong to this layer");
376 if (current_layer != NULL)
377 gst_object_unref (current_layer);
381 gst_object_unref (current_layer);
383 /* Remove it from our list of controlled objects */
384 layer->priv->clips_start = g_list_remove (layer->priv->clips_start, clip);
386 /* emit 'clip-removed' */
387 g_signal_emit (layer, ges_layer_signals[OBJECT_REMOVED], 0, clip);
389 /* inform the clip it's no longer in a layer */
390 ges_clip_set_layer (clip, NULL);
391 /* so neither in a timeline */
393 ges_timeline_element_set_timeline (GES_TIMELINE_ELEMENT (clip), NULL);
395 /* Remove our reference to the clip */
396 gst_object_unref (clip);
402 * ges_layer_set_priority:
403 * @layer: a #GESLayer
404 * @priority: the priority to set
406 * Sets the layer to the given @priority. See the documentation of the
407 * priority property for more information.
410 ges_layer_set_priority (GESLayer * layer, guint priority)
412 g_return_if_fail (GES_IS_LAYER (layer));
414 GST_DEBUG ("layer:%p, priority:%d", layer, priority);
416 if (priority != layer->priv->priority) {
417 layer->priv->priority = priority;
418 layer->min_nle_priority = (priority * LAYER_HEIGHT) + MIN_NLE_PRIO;
419 layer->max_nle_priority = ((priority + 1) * LAYER_HEIGHT) + MIN_NLE_PRIO;
421 ges_layer_resync_priorities (layer);
424 g_object_notify (G_OBJECT (layer), "priority");
428 * ges_layer_get_auto_transition:
429 * @layer: a #GESLayer
431 * Gets whether transitions are automatically added when objects
434 * Returns: %TRUE if transitions are automatically added, else %FALSE.
437 ges_layer_get_auto_transition (GESLayer * layer)
439 g_return_val_if_fail (GES_IS_LAYER (layer), 0);
441 return layer->priv->auto_transition;
445 * ges_layer_set_auto_transition:
446 * @layer: a #GESLayer
447 * @auto_transition: whether the auto_transition is active
449 * Sets the layer to the given @auto_transition. See the documentation of the
450 * property auto_transition for more information.
453 ges_layer_set_auto_transition (GESLayer * layer, gboolean auto_transition)
456 g_return_if_fail (GES_IS_LAYER (layer));
458 layer->priv->auto_transition = auto_transition;
459 g_object_notify (G_OBJECT (layer), "auto-transition");
463 * ges_layer_get_priority:
464 * @layer: a #GESLayer
466 * Get the priority of @layer within the timeline.
468 * Returns: The priority of the @layer within the timeline.
471 ges_layer_get_priority (GESLayer * layer)
473 g_return_val_if_fail (GES_IS_LAYER (layer), 0);
475 return layer->priv->priority;
479 * ges_layer_get_clips:
480 * @layer: a #GESLayer
482 * Get the clips this layer contains.
484 * Returns: (transfer full) (element-type GESClip): a #GList of
485 * clips. The user is responsible for
486 * unreffing the contained objects and freeing the list.
490 ges_layer_get_clips (GESLayer * layer)
492 GESLayerClass *klass;
494 g_return_val_if_fail (GES_IS_LAYER (layer), NULL);
496 klass = GES_LAYER_GET_CLASS (layer);
498 if (klass->get_objects) {
499 return klass->get_objects (layer);
502 return g_list_sort (g_list_copy_deep (layer->priv->clips_start,
503 (GCopyFunc) gst_object_ref, NULL),
504 (GCompareFunc) element_start_compare);
508 * ges_layer_is_empty:
509 * @layer: The #GESLayer to check
511 * Convenience method to check if @layer is empty (doesn't contain any clip),
514 * Returns: %TRUE if @layer is empty, %FALSE if it already contains at least
518 ges_layer_is_empty (GESLayer * layer)
520 g_return_val_if_fail (GES_IS_LAYER (layer), FALSE);
522 return (layer->priv->clips_start == NULL);
526 * ges_layer_add_clip:
527 * @layer: a #GESLayer
528 * @clip: (transfer full): the #GESClip to add.
530 * Adds the given clip to the layer. Sets the clip's parent, and thus
531 * takes ownership of the clip.
533 * An clip can only be added to one layer.
535 * Calling this method will construct and properly set all the media related
536 * elements on @clip. If you need to know when those objects (actually #GESTrackElement)
537 * are constructed, you should connect to the container::child-added signal which
538 * is emited right after those elements are ready to be used.
540 * Returns: %TRUE if the clip was properly added to the layer, or %FALSE
541 * if the @layer refuses to add the clip.
544 ges_layer_add_clip (GESLayer * layer, GESClip * clip)
547 GESLayerPrivate *priv;
548 GESLayer *current_layer;
550 g_return_val_if_fail (GES_IS_LAYER (layer), FALSE);
551 g_return_val_if_fail (GES_IS_CLIP (clip), FALSE);
553 GST_DEBUG_OBJECT (layer, "adding clip:%p", clip);
556 current_layer = ges_clip_get_layer (clip);
557 if (G_UNLIKELY (current_layer)) {
558 GST_WARNING ("Clip %p already belongs to another layer", clip);
559 gst_object_unref (current_layer);
564 asset = ges_extractable_get_asset (GES_EXTRACTABLE (clip));
567 NewAssetUData *mudata = g_slice_new (NewAssetUData);
570 mudata->layer = layer;
572 GST_DEBUG_OBJECT (layer, "%" GST_PTR_FORMAT " as no reference to any "
573 "assets creating a asset... trying sync", clip);
575 id = ges_extractable_get_id (GES_EXTRACTABLE (clip));
576 asset = ges_asset_request (G_OBJECT_TYPE (clip), id, NULL);
578 GESProject *project = layer->timeline ?
579 GES_PROJECT (ges_extractable_get_asset (GES_EXTRACTABLE
580 (layer->timeline))) : NULL;
582 ges_asset_request_async (G_OBJECT_TYPE (clip),
583 id, NULL, (GAsyncReadyCallback) new_asset_cb, mudata);
586 ges_project_add_loading_asset (project, G_OBJECT_TYPE (clip), id);
589 GST_LOG_OBJECT (layer, "Object added async");
594 ges_extractable_set_asset (GES_EXTRACTABLE (clip), asset);
596 g_slice_free (NewAssetUData, mudata);
600 gst_object_ref_sink (clip);
602 /* Take a reference to the clip and store it stored by start/priority */
603 priv->clips_start = g_list_insert_sorted (priv->clips_start, clip,
604 (GCompareFunc) element_start_compare);
606 /* Inform the clip it's now in this layer */
607 ges_clip_set_layer (clip, layer);
609 GST_DEBUG ("current clip priority : %d, Height: %d", _PRIORITY (clip),
612 /* Set the priority. */
613 if (_PRIORITY (clip) > LAYER_HEIGHT) {
614 GST_WARNING_OBJECT (layer,
615 "%p is out of the layer space, setting its priority to "
616 "%d, setting it to the maximum priority of the layer: %d", clip,
617 _PRIORITY (clip), LAYER_HEIGHT - 1);
618 _set_priority0 (GES_TIMELINE_ELEMENT (clip), LAYER_HEIGHT - 1);
621 ges_layer_resync_priorities (layer);
623 ges_timeline_element_set_timeline (GES_TIMELINE_ELEMENT (clip),
626 /* emit 'clip-added' */
627 g_signal_emit (layer, ges_layer_signals[OBJECT_ADDED], 0, clip);
633 * ges_layer_add_asset:
634 * @layer: a #GESLayer
635 * @asset: The asset to add to
636 * @start: The start value to set on the new #GESClip,
637 * if @start == GST_CLOCK_TIME_NONE, it will be set to
638 * the current duration of @layer
639 * @inpoint: The inpoint value to set on the new #GESClip
640 * @duration: The duration value to set on the new #GESClip
641 * @track_types: The #GESTrackType to set on the the new #GESClip
643 * Creates Clip from asset, adds it to layer and
644 * returns a reference to it.
646 * Returns: (transfer none): Created #GESClip
649 ges_layer_add_asset (GESLayer * layer,
650 GESAsset * asset, GstClockTime start, GstClockTime inpoint,
651 GstClockTime duration, GESTrackType track_types)
655 g_return_val_if_fail (GES_IS_LAYER (layer), NULL);
656 g_return_val_if_fail (GES_IS_ASSET (asset), NULL);
657 g_return_val_if_fail (g_type_is_a (ges_asset_get_extractable_type
658 (asset), GES_TYPE_CLIP), NULL);
660 GST_DEBUG_OBJECT (layer, "Adding asset %s with: start: %" GST_TIME_FORMAT
661 " inpoint: %" GST_TIME_FORMAT " duration: %" GST_TIME_FORMAT
662 " track types: %d (%s)", ges_asset_get_id (asset), GST_TIME_ARGS (start),
663 GST_TIME_ARGS (inpoint), GST_TIME_ARGS (duration), track_types,
664 ges_track_type_name (track_types));
666 clip = GES_CLIP (ges_asset_extract (asset, NULL));
668 if (!GST_CLOCK_TIME_IS_VALID (start)) {
669 start = ges_layer_get_duration (layer);
671 GST_DEBUG_OBJECT (layer,
672 "No start specified, setting it to %" GST_TIME_FORMAT,
673 GST_TIME_ARGS (start));
676 _set_start0 (GES_TIMELINE_ELEMENT (clip), start);
677 _set_inpoint0 (GES_TIMELINE_ELEMENT (clip), inpoint);
678 if (track_types != GES_TRACK_TYPE_UNKNOWN)
679 ges_clip_set_supported_formats (clip, track_types);
681 if (GST_CLOCK_TIME_IS_VALID (duration)) {
682 _set_duration0 (GES_TIMELINE_ELEMENT (clip), duration);
685 if (!ges_layer_add_clip (layer, clip)) {
686 gst_object_unref (clip);
697 * Creates a new #GESLayer.
699 * Returns: (transfer floating): A new #GESLayer
704 return g_object_new (GES_TYPE_LAYER, NULL);
708 * ges_layer_get_timeline:
709 * @layer: The #GESLayer to get the parent #GESTimeline from
711 * Get the #GESTimeline in which #GESLayer currently is.
713 * Returns: (transfer none) (nullable): the #GESTimeline in which #GESLayer
714 * currently is or %NULL if not in any timeline yet.
717 ges_layer_get_timeline (GESLayer * layer)
719 g_return_val_if_fail (GES_IS_LAYER (layer), NULL);
721 return layer->timeline;
725 ges_layer_set_timeline (GESLayer * layer, GESTimeline * timeline)
729 GST_DEBUG ("layer:%p, timeline:%p", layer, timeline);
731 for (tmp = layer->priv->clips_start; tmp; tmp = tmp->next) {
732 ges_timeline_element_set_timeline (tmp->data, timeline);
735 layer->timeline = timeline;
739 * ges_layer_get_clips_in_interval:
740 * @layer: a #GESLayer
741 * @start: start of the interval
742 * @end: end of the interval
744 * Gets the clips which appear between @start and @end on @layer.
746 * Returns: (transfer full) (element-type GESClip): a #GList of clips intersecting [@start, @end) interval on @layer.
749 ges_layer_get_clips_in_interval (GESLayer * layer, GstClockTime start,
753 GList *intersecting_clips = NULL;
754 GstClockTime clip_start, clip_end;
755 gboolean clip_intersects;
756 for (tmp = layer->priv->clips_start; tmp; tmp = tmp->next) {
757 clip_intersects = FALSE;
758 clip_start = ges_timeline_element_get_start (tmp->data);
759 clip_end = clip_start + ges_timeline_element_get_duration (tmp->data);
760 if (start <= clip_start && clip_start < end)
761 clip_intersects = TRUE;
762 else if (start < clip_end && clip_end <= end)
763 clip_intersects = TRUE;
764 else if (clip_start < start && clip_end > end)
765 clip_intersects = TRUE;
769 g_list_insert_sorted (intersecting_clips, tmp->data,
770 (GCompareFunc) element_start_compare);
772 return intersecting_clips;