GSequenceIter *iter_start;
GSequenceIter *iter_end;
GSequenceIter *iter_obj;
+ GSequenceIter *iter_by_layer;
+
+ GESTimelineLayer *layer;
+ GESTrackObject *tckobj;
} TrackObjIters;
static void
GSequence *tracksources; /* TrackSource-s sorted by start/priorities */
GList *priv_tracks;
+ /* FIXME: We should definitly offer an API over this,
+ * probably through a ges_timeline_layer_get_track_objects () method */
+ GHashTable *by_layer; /* {layer: GSequence of TrackObject by start/priorities} */
MoveContext movecontext;
};
priv->by_start = g_hash_table_new (g_direct_hash, g_direct_equal);
priv->by_end = g_hash_table_new (g_direct_hash, g_direct_equal);
priv->by_object = g_hash_table_new (g_direct_hash, g_direct_equal);
+ priv->by_layer = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
+ (GDestroyNotify) g_sequence_free);
priv->obj_iters = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
(GDestroyNotify) _destroy_obj_iters);
priv->starts_ends = g_sequence_new (g_free);
}
}
-static inline void
-sort_track_objects (GESTimeline * timeline, GESTrackObject * obj)
+static gint
+find_layer_by_prio (GESTimelineLayer * a, gpointer pprio)
{
- TrackObjIters *iters = g_hash_table_lookup (timeline->priv->obj_iters, obj);
+ gint prio = GPOINTER_TO_INT (pprio), lprio =
+ ges_timeline_layer_get_priority (a);
+
+ if (lprio < prio)
+ return -1;
+ if (lprio > prio)
+ return 1;
+ return 0;
+}
+static void
+sort_track_objects (GESTimeline * timeline, TrackObjIters * iters)
+{
g_sequence_sort_changed (iters->iter_obj,
(GCompareDataFunc) track_object_start_compare, NULL);
}
}
static inline void
-sort_starts_ends_end (GESTimeline * timeline, GESTrackObject * obj)
+sort_starts_ends_end (GESTimeline * timeline, TrackObjIters * iters)
{
+ GESTrackObject *obj = iters->tckobj;
GESTimelinePrivate *priv = timeline->priv;
guint64 *end = g_hash_table_lookup (priv->by_end, obj);
- TrackObjIters *iters = g_hash_table_lookup (priv->obj_iters, obj);
*end = obj->start + obj->duration;
}
static inline void
-sort_starts_ends_start (GESTimeline * timeline, GESTrackObject * obj)
+sort_starts_ends_start (GESTimeline * timeline, TrackObjIters * iters)
{
+ GESTrackObject *obj = iters->tckobj;
GESTimelinePrivate *priv = timeline->priv;
guint64 *start = g_hash_table_lookup (priv->by_start, obj);
- TrackObjIters *iters = g_hash_table_lookup (priv->obj_iters, obj);
*start = obj->start;
}
static void
-stop_tracking_for_snapping (GESTimeline * timeline, GESTrackObject * tckobj)
+stop_tracking_track_object (GESTimeline * timeline, GESTrackObject * tckobj)
{
guint64 *start, *end;
- GESTimelinePrivate *priv = timeline->priv;
TrackObjIters *iters;
+ GESTimelinePrivate *priv = timeline->priv;
-
- start = g_hash_table_lookup (priv->by_start, tckobj);
- end = g_hash_table_lookup (priv->by_end, tckobj);
iters = g_hash_table_lookup (priv->obj_iters, tckobj);
+ if (G_LIKELY (iters->iter_by_layer)) {
+ g_sequence_remove (iters->iter_by_layer);
+ } else {
+ GST_WARNING_OBJECT (timeline, "TrackObject %p was in no layer", tckobj);
+ }
- g_hash_table_remove (priv->by_start, tckobj);
- g_hash_table_remove (priv->by_end, tckobj);
- g_hash_table_remove (priv->by_object, end);
- g_hash_table_remove (priv->by_object, start);
- g_sequence_remove (iters->iter_start);
- g_sequence_remove (iters->iter_end);
- g_sequence_remove (iters->iter_obj);
+ if (GES_IS_TRACK_SOURCE (tckobj)) {
+ start = g_hash_table_lookup (priv->by_start, tckobj);
+ end = g_hash_table_lookup (priv->by_end, tckobj);
+
+ g_hash_table_remove (priv->by_start, tckobj);
+ g_hash_table_remove (priv->by_end, tckobj);
+ g_hash_table_remove (priv->by_object, end);
+ g_hash_table_remove (priv->by_object, start);
+ g_sequence_remove (iters->iter_start);
+ g_sequence_remove (iters->iter_end);
+ g_sequence_remove (iters->iter_obj);
+ timeline_update_duration (timeline);
+ }
g_hash_table_remove (priv->obj_iters, tckobj);
- timeline_update_duration (timeline);
}
static void
-start_tracking_track_obj (GESTimeline * timeline, GESTrackObject * tckobj)
+start_tracking_track_object (GESTimeline * timeline, GESTrackObject * tckobj)
{
- TrackObjIters *iters;
guint64 *pstart, *pend;
+ GSequence *by_layer_sequence;
+ TrackObjIters *iters;
GESTimelinePrivate *priv = timeline->priv;
- pstart = g_malloc (sizeof (guint64));
- pend = g_malloc (sizeof (guint64));
- *pstart = tckobj->start;
- *pend = *pstart + tckobj->duration;
+ guint layer_prio = tckobj->priority / LAYER_HEIGHT;
+ GList *layer_node = g_list_find_custom (timeline->layers,
+ GINT_TO_POINTER (layer_prio), (GCompareFunc) find_layer_by_prio);
+ GESTimelineLayer *layer = layer_node ? layer_node->data : NULL;
- iters = g_slice_new (TrackObjIters);
- iters->iter_start = g_sequence_insert_sorted (priv->starts_ends, pstart,
- (GCompareDataFunc) compare_uint64, NULL);
- iters->iter_end = g_sequence_insert_sorted (priv->starts_ends, pend,
- (GCompareDataFunc) compare_uint64, NULL);
- iters->iter_obj =
- g_sequence_insert_sorted (priv->tracksources, g_object_ref (tckobj),
- (GCompareDataFunc) track_object_start_compare, NULL);
+ iters = g_slice_new0 (TrackObjIters);
- g_hash_table_insert (priv->by_start, tckobj, pstart);
- g_hash_table_insert (priv->by_object, pstart, tckobj);
- g_hash_table_insert (priv->by_end, tckobj, pend);
- g_hash_table_insert (priv->by_object, pend, tckobj);
+ /* We add all TrackObject to obj_iters as we always follow them
+ * in the by_layer Sequences */
g_hash_table_insert (priv->obj_iters, tckobj, iters);
- timeline->priv->movecontext.needs_move_ctx = TRUE;
+ /* Track all objects by layer */
+ if (G_UNLIKELY (layer == NULL)) {
+ /* We handle the case where we have TrackObject that are in no layer by not
+ * tracking them
+ *
+ * FIXME: Check if we should rather try to track them in some sort of
+ * dummy layer, or even refuse TrackObjects to be added in Tracks if
+ * they land in no layer the timeline controls.
+ */
+ GST_ERROR_OBJECT (timeline, "Adding a TrackObject that lands in no layer "
+ "we are controlling");
+ } else {
+ by_layer_sequence = g_hash_table_lookup (priv->by_layer, layer);
+ iters->iter_by_layer = g_sequence_insert_sorted (by_layer_sequence, tckobj,
+ (GCompareDataFunc) track_object_start_compare, NULL);
+ iters->layer = layer;
+ }
- timeline_update_duration (timeline);
+ if (GES_IS_TRACK_SOURCE (tckobj)) {
+ /* Track only sources for timeline edition and snapping */
+ pstart = g_malloc (sizeof (guint64));
+ pend = g_malloc (sizeof (guint64));
+ *pstart = tckobj->start;
+ *pend = *pstart + tckobj->duration;
+
+ iters->iter_start = g_sequence_insert_sorted (priv->starts_ends, pstart,
+ (GCompareDataFunc) compare_uint64, NULL);
+ iters->iter_end = g_sequence_insert_sorted (priv->starts_ends, pend,
+ (GCompareDataFunc) compare_uint64, NULL);
+ iters->iter_obj =
+ g_sequence_insert_sorted (priv->tracksources, g_object_ref (tckobj),
+ (GCompareDataFunc) track_object_start_compare, NULL);
+ iters->tckobj = tckobj;
+
+ g_hash_table_insert (priv->by_start, tckobj, pstart);
+ g_hash_table_insert (priv->by_object, pstart, tckobj);
+ g_hash_table_insert (priv->by_end, tckobj, pend);
+ g_hash_table_insert (priv->by_object, pend, tckobj);
+
+ timeline->priv->movecontext.needs_move_ctx = TRUE;
+
+ timeline_update_duration (timeline);
+ }
}
static inline void
tckobj_iter = iters->iter_obj;
switch (edge) {
case GES_EDGE_START:
- /* set it properly int the context of "trimming" */
+ /* set it properly in the context of "trimming" */
mv_ctx->max_trim_pos = 0;
start = obj->start;
trackobj_start_changed_cb (GESTrackObject * child,
GParamSpec * arg G_GNUC_UNUSED, GESTimeline * timeline)
{
- sort_track_objects (timeline, child);
- sort_starts_ends_start (timeline, child);
- sort_starts_ends_end (timeline, child);
+ GESTimelinePrivate *priv = timeline->priv;
+ TrackObjIters *iters = g_hash_table_lookup (priv->obj_iters, child);
+
+ if (G_LIKELY (iters->iter_by_layer))
+ g_sequence_sort_changed (iters->iter_by_layer,
+ (GCompareDataFunc) track_object_start_compare, NULL);
+
+ if (GES_IS_TRACK_SOURCE (child)) {
+ sort_track_objects (timeline, iters);
+ sort_starts_ends_start (timeline, iters);
+ sort_starts_ends_end (timeline, iters);
+
+ /* If the timeline is set to snap objects together, we
+ * are sure that all movement of TrackObject-s are done within
+ * the moving context, so we do not need to recalculate the
+ * move context as often */
+ if (timeline->priv->movecontext.ignore_needs_ctx &&
+ timeline->priv->snapping_distance == 0)
+ timeline->priv->movecontext.needs_move_ctx = TRUE;
- /* If the timeline is set to snap objects together, we
- * are sure that all movement of TrackObject-s are done within
- * the moving context, so we do not need to recalculate the
- * move context as often */
- if (timeline->priv->movecontext.ignore_needs_ctx &&
- timeline->priv->snapping_distance == 0)
- timeline->priv->movecontext.needs_move_ctx = TRUE;
+ }
+}
+
+static void
+trackobj_priority_changed_cb (GESTrackObject * child,
+ GParamSpec * arg G_GNUC_UNUSED, GESTimeline * timeline)
+{
+ GESTimelinePrivate *priv = timeline->priv;
+
+ GList *layer_node = g_list_find_custom (timeline->layers,
+ GINT_TO_POINTER (child->priority / LAYER_HEIGHT),
+ (GCompareFunc) find_layer_by_prio);
+ GESTimelineLayer *layer = layer_node ? layer_node->data : NULL;
+ TrackObjIters *iters = g_hash_table_lookup (priv->obj_iters,
+ child);
+
+ if (G_UNLIKELY (layer == NULL)) {
+ GST_ERROR_OBJECT (timeline,
+ "Changing a TrackObject prio, which would not "
+ "land in no layer we are controlling");
+ g_sequence_remove (iters->iter_by_layer);
+ iters->iter_by_layer = NULL;
+ iters->layer = NULL;
+ } else {
+ /* If it moves from layer, properly change it */
+ if (layer != iters->layer) {
+ GSequence *by_layer_sequence =
+ g_hash_table_lookup (priv->by_layer, layer);
+
+ g_sequence_remove (iters->iter_by_layer);
+ iters->iter_by_layer =
+ g_sequence_insert_sorted (by_layer_sequence, child,
+ (GCompareDataFunc) track_object_start_compare, NULL);
+ iters->layer = layer;
+ } else {
+ g_sequence_sort_changed (iters->iter_by_layer,
+ (GCompareDataFunc) track_object_start_compare, NULL);
+ }
+ }
+
+ if (GES_IS_TRACK_SOURCE (child))
+ sort_track_objects (timeline, iters);
}
static void
trackobj_duration_changed_cb (GESTrackObject * child,
GParamSpec * arg G_GNUC_UNUSED, GESTimeline * timeline)
{
- sort_starts_ends_end (timeline, child);
+ GESTimelinePrivate *priv = timeline->priv;
+ TrackObjIters *iters = g_hash_table_lookup (priv->obj_iters, child);
- /* If the timeline is set to snap objects together, we
- * are sure that all movement of TrackObject-s are done within
- * the moving context, so we do not need to recalculate the
- * move context as often */
- if (timeline->priv->movecontext.ignore_needs_ctx &&
- timeline->priv->snapping_distance == 0)
- timeline->priv->movecontext.needs_move_ctx = TRUE;
+ if (GES_IS_TRACK_SOURCE (child)) {
+ sort_starts_ends_end (timeline, iters);
+
+ /* If the timeline is set to snap objects together, we
+ * are sure that all movement of TrackObject-s are done within
+ * the moving context, so we do not need to recalculate the
+ * move context as often */
+ if (timeline->priv->movecontext.ignore_needs_ctx &&
+ timeline->priv->snapping_distance == 0) {
+ timeline->priv->movecontext.needs_move_ctx = TRUE;
+ }
+ }
}
static void
track_object_added_cb (GESTrack * track, GESTrackObject * object,
GESTimeline * timeline)
{
- /* We only work with sources */
- if (GES_IS_TRACK_SOURCE (object)) {
- start_tracking_track_obj (timeline, object);
+ /* Auto transition should be updated before we receive the signal */
+ g_signal_connect_after (GES_TRACK_OBJECT (object), "notify::start",
+ G_CALLBACK (trackobj_start_changed_cb), timeline);
+ g_signal_connect_after (GES_TRACK_OBJECT (object), "notify::duration",
+ G_CALLBACK (trackobj_duration_changed_cb), timeline);
+ g_signal_connect_after (GES_TRACK_OBJECT (object), "notify::priority",
+ G_CALLBACK (trackobj_priority_changed_cb), timeline);
- g_signal_connect (GES_TRACK_OBJECT (object), "notify::start",
- G_CALLBACK (trackobj_start_changed_cb), timeline);
- g_signal_connect (GES_TRACK_OBJECT (object), "notify::duration",
- G_CALLBACK (trackobj_duration_changed_cb), timeline);
- }
+ start_tracking_track_object (timeline, object);
}
static void
track_object_removed_cb (GESTrack * track, GESTrackObject * object,
GESTimeline * timeline)
{
- /* We only work with sources */
+
if (GES_IS_TRACK_SOURCE (object)) {
- g_signal_handlers_disconnect_by_func (object, trackobj_start_changed_cb,
- NULL);
- g_signal_handlers_disconnect_by_func (object, trackobj_duration_changed_cb,
- NULL);
/* Make sure to reinitialise the moving context next time */
timeline->priv->movecontext.needs_move_ctx = TRUE;
- stop_tracking_for_snapping (timeline, object);
}
+
+ /* Disconnect all signal handlers */
+ g_signal_handlers_disconnect_by_func (object, trackobj_duration_changed_cb,
+ NULL);
+ g_signal_handlers_disconnect_by_func (object, trackobj_priority_changed_cb,
+ NULL);
+ stop_tracking_track_object (timeline, object);
}
static void
/* Inform the layer that it belongs to a new timeline */
ges_timeline_layer_set_timeline (layer, timeline);
+ g_hash_table_insert (timeline->priv->by_layer, layer, g_sequence_new (NULL));
+
/* Connect to 'object-added'/'object-removed' signal from the new layer */
g_signal_connect (layer, "object-added", G_CALLBACK (layer_object_added_cb),
timeline);
g_signal_handlers_disconnect_by_func (layer, layer_object_removed_cb,
timeline);
+ g_hash_table_remove (timeline->priv->by_layer, layer);
timeline->layers = g_list_remove (timeline->layers, layer);
ges_timeline_layer_set_timeline (layer, NULL);