mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-26 19:51:11 +00:00
Reimplement the auto-transition feature
+ Actually implement unit tests
This commit is contained in:
parent
5f69200a3a
commit
02652902f5
6 changed files with 1646 additions and 502 deletions
|
@ -55,6 +55,7 @@ libges_@GST_API_VERSION@_la_SOURCES = \
|
|||
ges-project.c \
|
||||
ges-base-xml-formatter.c \
|
||||
ges-xml-formatter.c \
|
||||
ges-auto-transition.c \
|
||||
ges-utils.c
|
||||
|
||||
libges_@GST_API_VERSION@includedir = $(includedir)/gstreamer-@GST_API_VERSION@/ges/
|
||||
|
@ -111,6 +112,7 @@ libges_@GST_API_VERSION@include_HEADERS = \
|
|||
|
||||
noinst_HEADERS = \
|
||||
ges-internal.h \
|
||||
ges-auto-transition.h \
|
||||
ges-internal-enums.h
|
||||
|
||||
libges_@GST_API_VERSION@_la_CFLAGS = -I$(top_srcdir) $(GST_PBUTILS_CFLAGS) \
|
||||
|
|
182
ges/ges-auto-transition.c
Normal file
182
ges/ges-auto-transition.c
Normal file
|
@ -0,0 +1,182 @@
|
|||
/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 2 -*- */
|
||||
/*
|
||||
* gst-editing-services
|
||||
* Copyright (C) 2013 Thibault Saunier <thibault.saunier@collabora.com>
|
||||
*
|
||||
* gst-editing-services is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* gst-editing-services is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.";
|
||||
*/
|
||||
|
||||
/* This class warps a GESTimelineTransition, letting any implementation
|
||||
* of a GESTimelineTransition to be used.
|
||||
*
|
||||
* NOTE: This is for internal use exclusively
|
||||
*/
|
||||
|
||||
#include "ges-auto-transition.h"
|
||||
#include "ges-internal.h"
|
||||
enum
|
||||
{
|
||||
DESTROY_ME,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static guint auto_transition_signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
G_DEFINE_TYPE (GESAutoTransition, ges_auto_transition, G_TYPE_OBJECT);
|
||||
|
||||
static void
|
||||
neighbour_changed_cb (GESTimelineObject * obj, GParamSpec * arg G_GNUC_UNUSED,
|
||||
GESAutoTransition * self)
|
||||
{
|
||||
gint64 new_duration;
|
||||
|
||||
if (self->next_source->priority / LAYER_HEIGHT !=
|
||||
self->previous_source->priority / LAYER_HEIGHT) {
|
||||
GST_DEBUG_OBJECT (self, "Destroy changed layer");
|
||||
g_signal_emit (self, auto_transition_signals[DESTROY_ME], 0);
|
||||
return;
|
||||
}
|
||||
|
||||
new_duration =
|
||||
(self->previous_source->start + self->previous_source->duration) -
|
||||
self->next_source->start;
|
||||
|
||||
if (new_duration <= 0 || new_duration >= self->previous_source->duration ||
|
||||
new_duration >= self->next_source->duration) {
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Destroy %" G_GINT64_FORMAT " not a valid duration",
|
||||
new_duration);
|
||||
g_signal_emit (self, auto_transition_signals[DESTROY_ME], 0);
|
||||
return;
|
||||
}
|
||||
|
||||
ges_timeline_object_set_start (self->timeline_transition,
|
||||
self->next_source->start);
|
||||
ges_timeline_object_set_duration (self->timeline_transition, new_duration);
|
||||
}
|
||||
|
||||
static void
|
||||
_height_changed_cb (GESTimelineObject * obj, GParamSpec * arg G_GNUC_UNUSED,
|
||||
GESAutoTransition * self)
|
||||
{
|
||||
/* FIXME This is really not smart and we should properly implement timelineobject
|
||||
* priority management at the TimelineLayer level */
|
||||
ges_timeline_object_set_priority (self->next_timeline_object,
|
||||
self->previous_timeline_object->priority +
|
||||
self->previous_timeline_object->height);
|
||||
}
|
||||
|
||||
static void
|
||||
_track_changed_cb (GESTrackObject * obj, GParamSpec * arg G_GNUC_UNUSED,
|
||||
GESAutoTransition * self)
|
||||
{
|
||||
if (ges_track_object_get_track (obj) == NULL) {
|
||||
GST_DEBUG_OBJECT (self, "Neighboor %" GST_PTR_FORMAT
|
||||
" removed from track ... auto destructing", obj);
|
||||
|
||||
g_signal_emit (self, auto_transition_signals[DESTROY_ME], 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
ges_auto_transition_init (GESAutoTransition * ges_auto_transition)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
ges_auto_transition_finalize (GObject * object)
|
||||
{
|
||||
GESAutoTransition *self = GES_AUTO_TRANSITION (object);
|
||||
|
||||
g_signal_handlers_disconnect_by_func (self->previous_source,
|
||||
neighbour_changed_cb, self);
|
||||
g_signal_handlers_disconnect_by_func (self->next_source, neighbour_changed_cb,
|
||||
self);
|
||||
g_signal_handlers_disconnect_by_func (self->previous_timeline_object,
|
||||
_height_changed_cb, self);
|
||||
g_signal_handlers_disconnect_by_func (self->next_source, _track_changed_cb,
|
||||
self);
|
||||
g_signal_handlers_disconnect_by_func (self->previous_source,
|
||||
_track_changed_cb, self);
|
||||
|
||||
g_free (self->key);
|
||||
|
||||
G_OBJECT_CLASS (ges_auto_transition_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
ges_auto_transition_class_init (GESAutoTransitionClass * klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
auto_transition_signals[DESTROY_ME] =
|
||||
g_signal_new ("destroy-me", G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE, 0, NULL, NULL, NULL,
|
||||
G_TYPE_NONE, 0);
|
||||
object_class->finalize = ges_auto_transition_finalize;
|
||||
}
|
||||
|
||||
|
||||
GESAutoTransition *
|
||||
ges_auto_transition_new (GESTrackObject * transition,
|
||||
GESTrackObject * previous_source, GESTrackObject * next_source)
|
||||
{
|
||||
GESAutoTransition *self = g_object_new (GES_TYPE_AUTO_TRANSITION, NULL);
|
||||
|
||||
self->previous_source = previous_source;
|
||||
self->next_source = next_source;
|
||||
self->transition = transition;
|
||||
|
||||
self->previous_timeline_object =
|
||||
ges_track_object_get_timeline_object (previous_source);
|
||||
self->next_timeline_object =
|
||||
ges_track_object_get_timeline_object (next_source);
|
||||
self->timeline_transition = ges_track_object_get_timeline_object (transition);
|
||||
|
||||
g_signal_connect (previous_source, "notify::start",
|
||||
G_CALLBACK (neighbour_changed_cb), self);
|
||||
g_signal_connect (previous_source, "notify::priority",
|
||||
G_CALLBACK (neighbour_changed_cb), self);
|
||||
g_signal_connect (next_source, "notify::start",
|
||||
G_CALLBACK (neighbour_changed_cb), self);
|
||||
g_signal_connect (next_source, "notify::priority",
|
||||
G_CALLBACK (neighbour_changed_cb), self);
|
||||
g_signal_connect (previous_source, "notify::duration",
|
||||
G_CALLBACK (neighbour_changed_cb), self);
|
||||
g_signal_connect (next_source, "notify::duration",
|
||||
G_CALLBACK (neighbour_changed_cb), self);
|
||||
g_signal_connect (self->previous_timeline_object, "notify::height",
|
||||
G_CALLBACK (_height_changed_cb), self);
|
||||
|
||||
g_signal_connect (next_source, "notify::track",
|
||||
G_CALLBACK (_track_changed_cb), self);
|
||||
g_signal_connect (previous_source, "notify::track",
|
||||
G_CALLBACK (_track_changed_cb), self);
|
||||
|
||||
_height_changed_cb (self->previous_timeline_object, NULL, self);
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Created transition %" GST_PTR_FORMAT
|
||||
" between %" GST_PTR_FORMAT " and: %" GST_PTR_FORMAT
|
||||
" in layer nb %i, start: %" GST_TIME_FORMAT " duration: %"
|
||||
GST_TIME_FORMAT, transition, next_source, previous_source,
|
||||
ges_timeline_layer_get_priority (ges_timeline_object_get_layer
|
||||
(self->previous_timeline_object)), GST_TIME_ARGS (transition->start),
|
||||
GST_TIME_ARGS (transition->duration));
|
||||
|
||||
self->key = g_strdup_printf ("%p%p", self->previous_source,
|
||||
self->next_source);
|
||||
|
||||
return self;
|
||||
}
|
78
ges/ges-auto-transition.h
Normal file
78
ges/ges-auto-transition.h
Normal file
|
@ -0,0 +1,78 @@
|
|||
/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 2 -*- */
|
||||
/*
|
||||
* gst-editing-services
|
||||
* Copyright (C) 2013 Thibault Saunier <thibault.saunier@collabora.com>
|
||||
*
|
||||
* gst-editing-services is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* gst-editing-services is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.";
|
||||
*/
|
||||
|
||||
#ifndef _GES_AUTO_TRANSITION_H_
|
||||
#define _GES_AUTO_TRANSITION_H_
|
||||
|
||||
#include <glib-object.h>
|
||||
#include "ges-track-object.h"
|
||||
#include "ges-timeline-object.h"
|
||||
#include "ges-timeline-layer.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GES_TYPE_AUTO_TRANSITION (ges_auto_transition_get_type ())
|
||||
#define GES_AUTO_TRANSITION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_AUTO_TRANSITION, GESAutoTransition))
|
||||
#define GES_AUTO_TRANSITION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_AUTO_TRANSITION, GESAutoTransitionClass))
|
||||
#define GES_IS_AUTO_TRANSITION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_AUTO_TRANSITION))
|
||||
#define GES_IS_AUTO_TRANSITION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_AUTO_TRANSITION))
|
||||
#define GES_AUTO_TRANSITION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_AUTO_TRANSITION, GESAutoTransitionClass))
|
||||
|
||||
typedef struct _GESAutoTransitionClass GESAutoTransitionClass;
|
||||
typedef struct _GESAutoTransition GESAutoTransition;
|
||||
|
||||
|
||||
|
||||
struct _GESAutoTransitionClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
|
||||
/* Padding for API extension */
|
||||
gpointer _ges_reserved[GES_PADDING];
|
||||
};
|
||||
|
||||
struct _GESAutoTransition
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
/* <read only and construct only> */
|
||||
GESTrackObject *previous_source;
|
||||
GESTrackObject *next_source;
|
||||
GESTrackObject *transition;
|
||||
|
||||
GESTimelineLayer *layer;
|
||||
|
||||
GESTimelineObject *previous_timeline_object;
|
||||
GESTimelineObject *next_timeline_object;
|
||||
GESTimelineObject *timeline_transition;
|
||||
|
||||
gchar *key;
|
||||
|
||||
/* Padding for API extension */
|
||||
gpointer _ges_reserved[GES_PADDING];
|
||||
};
|
||||
|
||||
GType ges_auto_transition_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GESAutoTransition * ges_auto_transition_new (GESTrackObject * transition,
|
||||
GESTrackObject * previous_source,
|
||||
GESTrackObject * next_source);
|
||||
|
||||
G_END_DECLS
|
||||
#endif /* _GES_AUTO_TRANSITION_H_ */
|
|
@ -35,10 +35,6 @@
|
|||
#include "ges.h"
|
||||
#include "ges-timeline-source.h"
|
||||
|
||||
static void
|
||||
timeline_object_height_changed_cb (GESTimelineObject * obj,
|
||||
GESTrackEffect * tr_eff, GESTimelineObject * second_obj);
|
||||
|
||||
static void ges_meta_container_interface_init
|
||||
(GESMetaContainerInterface * iface);
|
||||
|
||||
|
@ -211,316 +207,6 @@ ges_timeline_layer_init (GESTimelineLayer * self)
|
|||
self->max_gnl_priority = LAYER_HEIGHT;
|
||||
}
|
||||
|
||||
static GList *
|
||||
track_get_by_layer (GESTimelineLayer * layer, GESTrack * track)
|
||||
{
|
||||
GESTrackObject *tckobj;
|
||||
guint32 layer_prio = layer->priv->priority;
|
||||
|
||||
GList *tck_objects_list = NULL, *tmp = NULL, *return_list = NULL;
|
||||
|
||||
tck_objects_list = ges_track_get_objects (track);
|
||||
for (tmp = tck_objects_list; tmp; tmp = tmp->next) {
|
||||
tckobj = GES_TRACK_OBJECT (tmp->data);
|
||||
|
||||
if (tckobj->priority / LAYER_HEIGHT == layer_prio) {
|
||||
/* We steal the reference from tck_objects_list */
|
||||
return_list = g_list_append (return_list, tmp->data);
|
||||
} else
|
||||
g_object_unref (tmp->data);
|
||||
}
|
||||
|
||||
return return_list;
|
||||
}
|
||||
|
||||
/* Compare:
|
||||
* @compared: The #GList of #GESTrackObjects that we compare with @track_object
|
||||
* @track_object: The #GESTrackObject that serves as a reference
|
||||
* @ahead: %TRUE if we are comparing frontward %FALSE if we are comparing
|
||||
* backward*/
|
||||
static void
|
||||
compare (GList * compared, GESTrackObject * track_object, gboolean ahead)
|
||||
{
|
||||
GList *tmp;
|
||||
gint64 start, duration, compared_start, compared_duration, end, compared_end,
|
||||
tr_start, tr_duration;
|
||||
GESTimelineStandardTransition *trans = NULL;
|
||||
GESTrack *track;
|
||||
GESTimelineLayer *layer;
|
||||
GESTimelineObject *object, *compared_object, *first_object, *second_object;
|
||||
gint priority;
|
||||
|
||||
g_return_if_fail (compared);
|
||||
|
||||
GST_DEBUG ("Recalculating transitions");
|
||||
|
||||
object = ges_track_object_get_timeline_object (track_object);
|
||||
|
||||
if (!object) {
|
||||
GST_WARNING ("Trackobject not in a timeline object: "
|
||||
"Can not calulate transitions");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
compared_object = ges_track_object_get_timeline_object (compared->data);
|
||||
layer = ges_timeline_object_get_layer (object);
|
||||
|
||||
start = ges_track_object_get_start (track_object);
|
||||
duration = ges_track_object_get_duration (track_object);
|
||||
compared_start = ges_track_object_get_start (compared->data);
|
||||
compared_duration = ges_track_object_get_duration (compared->data);
|
||||
end = start + duration;
|
||||
compared_end = compared_start + compared_duration;
|
||||
|
||||
if (ahead) {
|
||||
/* Make sure we remove the last transition we created it is not needed
|
||||
* FIXME make it a smarter way */
|
||||
if (compared->prev && GES_IS_TRACK_TRANSITION (compared->prev->data)) {
|
||||
trans = GES_TIMELINE_STANDARD_TRANSITION
|
||||
(ges_track_object_get_timeline_object (compared->prev->data));
|
||||
g_object_get (compared->prev->data, "start", &tr_start, "duration",
|
||||
&tr_duration, NULL);
|
||||
if (tr_start >= compared_start && tr_start + tr_duration <= compared_end)
|
||||
ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (trans));
|
||||
trans = NULL;
|
||||
}
|
||||
|
||||
for (tmp = compared->next; tmp; tmp = tmp->next) {
|
||||
/* If we have a transitionmnmnm we recaluculuculate its values */
|
||||
if (GES_IS_TRACK_TRANSITION (tmp->data)) {
|
||||
g_object_get (tmp->data, "start", &tr_start, "duration", &tr_duration,
|
||||
NULL);
|
||||
|
||||
if (tr_start + tr_duration == compared_start + compared_duration) {
|
||||
GESTimelineObject *tlobj;
|
||||
tlobj = ges_track_object_get_timeline_object (tmp->data);
|
||||
|
||||
trans = GES_TIMELINE_STANDARD_TRANSITION (tlobj);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (compared_end <= start) {
|
||||
if (trans) {
|
||||
ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (trans));
|
||||
g_object_get (compared_object, "priority", &priority, NULL);
|
||||
g_object_set (object, "priority", priority, NULL);
|
||||
}
|
||||
|
||||
goto clean;
|
||||
} else if (start > compared_start && end < compared_end) {
|
||||
if (trans) {
|
||||
/* Transition not needed anymore */
|
||||
ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (trans));
|
||||
}
|
||||
goto clean;
|
||||
} else if (start <= compared_start) {
|
||||
if (trans) {
|
||||
ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (trans));
|
||||
}
|
||||
|
||||
goto clean;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (compared->next && GES_IS_TRACK_TRANSITION (compared->next->data)) {
|
||||
trans = GES_TIMELINE_STANDARD_TRANSITION
|
||||
(ges_track_object_get_timeline_object (compared->next->data));
|
||||
g_object_get (compared->prev->data, "start", &tr_start, "duration",
|
||||
&tr_duration, NULL);
|
||||
if (tr_start >= compared_start && tr_start + tr_duration <= compared_end)
|
||||
ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (trans));
|
||||
trans = NULL;
|
||||
}
|
||||
for (tmp = compared->prev; tmp; tmp = tmp->prev) {
|
||||
if GES_IS_TRACK_TRANSITION
|
||||
(tmp->data) {
|
||||
g_object_get (tmp->data, "start", &tr_start, "duration", &tr_duration,
|
||||
NULL);
|
||||
if (tr_start == compared_start) {
|
||||
trans = GES_TIMELINE_STANDARD_TRANSITION
|
||||
(ges_track_object_get_timeline_object (tmp->data));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (start + duration <= compared_start) {
|
||||
if (trans) {
|
||||
ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (trans));
|
||||
g_object_get (object, "priority", &priority, NULL);
|
||||
g_object_set (compared_object, "priority", priority, NULL);
|
||||
}
|
||||
goto clean;
|
||||
|
||||
} else if (start > compared_start) {
|
||||
if (trans)
|
||||
ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (trans));
|
||||
|
||||
goto clean;
|
||||
} else if (start < compared_start && end > compared_end) {
|
||||
if (trans) {
|
||||
ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (trans));
|
||||
}
|
||||
|
||||
goto clean;
|
||||
}
|
||||
}
|
||||
|
||||
if (!trans) {
|
||||
gint height;
|
||||
|
||||
trans =
|
||||
ges_timeline_standard_transition_new_for_nick ((gchar *) "crossfade");
|
||||
track = ges_track_object_get_track (track_object);
|
||||
|
||||
ges_timeline_object_set_supported_formats (GES_TIMELINE_OBJECT (trans),
|
||||
track->type);
|
||||
|
||||
ges_timeline_layer_add_object (layer, GES_TIMELINE_OBJECT (trans));
|
||||
|
||||
if (ahead) {
|
||||
first_object = ges_track_object_get_timeline_object (compared->data);
|
||||
second_object = object;
|
||||
} else {
|
||||
second_object = ges_track_object_get_timeline_object (compared->data);
|
||||
first_object = object;
|
||||
}
|
||||
|
||||
g_object_get (first_object, "priority", &priority, "height", &height, NULL);
|
||||
g_object_set (second_object, "priority", priority + height, NULL);
|
||||
g_signal_connect (first_object, "notify::height",
|
||||
(GCallback) timeline_object_height_changed_cb, second_object);
|
||||
}
|
||||
|
||||
if (ahead) {
|
||||
g_object_set (trans, "start", start, "duration",
|
||||
compared_duration + compared_start - start, NULL);
|
||||
} else {
|
||||
g_object_set (trans, "start", compared_start, "duration",
|
||||
start + duration - compared_start, NULL);
|
||||
}
|
||||
|
||||
clean:
|
||||
g_object_unref (layer);
|
||||
}
|
||||
|
||||
static void
|
||||
calculate_next_transition_with_list (GESTrackObject * track_object,
|
||||
GList * tckobjs_in_layer, GESTimelineLayer * layer)
|
||||
{
|
||||
GList *compared;
|
||||
|
||||
if (!(compared = g_list_find (tckobjs_in_layer, track_object)))
|
||||
return;
|
||||
|
||||
if (compared == NULL)
|
||||
/* This is the last TrackObject of the Track */
|
||||
return;
|
||||
|
||||
do {
|
||||
compared = compared->next;
|
||||
if (compared == NULL)
|
||||
return;
|
||||
} while (!GES_IS_TRACK_SOURCE (compared->data));
|
||||
|
||||
compare (compared, track_object, FALSE);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
calculate_next_transition (GESTrackObject * track_object,
|
||||
GESTimelineLayer * layer)
|
||||
{
|
||||
GESTrack *track;
|
||||
GList *tckobjs_in_layer;
|
||||
|
||||
if ((track = ges_track_object_get_track (track_object))) {
|
||||
tckobjs_in_layer = track_get_by_layer (layer, track);
|
||||
calculate_next_transition_with_list (track_object, tckobjs_in_layer, layer);
|
||||
|
||||
g_list_foreach (tckobjs_in_layer, (GFunc) g_object_unref, NULL);
|
||||
g_list_free (tckobjs_in_layer);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
calculate_transitions (GESTrackObject * track_object)
|
||||
{
|
||||
GList *tckobjs_in_layer, *compared;
|
||||
GESTimelineLayer *layer;
|
||||
GESTimelineObject *tlobj;
|
||||
|
||||
GESTrack *track = ges_track_object_get_track (track_object);
|
||||
|
||||
if (track == NULL)
|
||||
return;
|
||||
|
||||
tlobj = ges_track_object_get_timeline_object (track_object);
|
||||
layer = ges_timeline_object_get_layer (tlobj);
|
||||
tckobjs_in_layer = track_get_by_layer (layer, track);
|
||||
if (!(compared = g_list_find (tckobjs_in_layer, track_object)))
|
||||
return;
|
||||
do {
|
||||
compared = compared->prev;
|
||||
|
||||
if (compared == NULL) {
|
||||
/* Nothing before, let's check after */
|
||||
calculate_next_transition_with_list (track_object, tckobjs_in_layer,
|
||||
layer);
|
||||
goto done;
|
||||
|
||||
}
|
||||
} while (!GES_IS_TRACK_SOURCE (compared->data));
|
||||
|
||||
compare (compared, track_object, TRUE);
|
||||
|
||||
calculate_next_transition_with_list (track_object, tckobjs_in_layer, layer);
|
||||
|
||||
done:
|
||||
g_list_foreach (tckobjs_in_layer, (GFunc) g_object_unref, NULL);
|
||||
g_list_free (tckobjs_in_layer);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
look_for_transition (GESTrackObject * track_object, GESTimelineLayer * layer)
|
||||
{
|
||||
GESTrack *track;
|
||||
GList *track_objects, *tmp, *cur;
|
||||
|
||||
track = ges_track_object_get_track (track_object);
|
||||
track_objects = ges_track_get_objects (track);
|
||||
|
||||
cur = g_list_find (track_objects, track_object);
|
||||
|
||||
for (tmp = cur->next; tmp; tmp = tmp->next) {
|
||||
if (GES_IS_TRACK_SOURCE (tmp->data)) {
|
||||
break;
|
||||
}
|
||||
if (GES_IS_TRACK_AUDIO_TRANSITION (tmp->data)
|
||||
|| GES_IS_TRACK_VIDEO_TRANSITION (tmp->data)) {
|
||||
ges_timeline_layer_remove_object (layer,
|
||||
ges_track_object_get_timeline_object (tmp->data));
|
||||
}
|
||||
}
|
||||
|
||||
for (tmp = cur->prev; tmp; tmp = tmp->prev) {
|
||||
if (GES_IS_TRACK_SOURCE (tmp->data)) {
|
||||
break;
|
||||
}
|
||||
if (GES_IS_TRACK_AUDIO_TRANSITION (tmp->data)
|
||||
|| GES_IS_TRACK_VIDEO_TRANSITION (tmp->data)) {
|
||||
ges_timeline_layer_remove_object (layer,
|
||||
ges_track_object_get_timeline_object (tmp->data));
|
||||
}
|
||||
}
|
||||
g_list_foreach (track_objects, (GFunc) g_object_unref, NULL);
|
||||
g_list_free (track_objects);
|
||||
}
|
||||
|
||||
/**
|
||||
* ges_timeline_layer_resync_priorities:
|
||||
* @layer: a #GESTimelineLayer
|
||||
|
@ -548,140 +234,6 @@ ges_timeline_layer_resync_priorities (GESTimelineLayer * layer)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
/* Callbacks */
|
||||
|
||||
static void
|
||||
track_object_duration_cb (GESTrackObject * track_object,
|
||||
GParamSpec * arg G_GNUC_UNUSED)
|
||||
{
|
||||
GESTimelineLayer *layer;
|
||||
GESTimelineObject *tlobj;
|
||||
|
||||
tlobj = ges_track_object_get_timeline_object (track_object);
|
||||
layer = ges_timeline_object_get_layer (tlobj);
|
||||
if (G_LIKELY (GES_IS_TRACK_SOURCE (track_object)))
|
||||
calculate_next_transition (track_object, layer);
|
||||
}
|
||||
|
||||
static void
|
||||
track_object_removed_cb (GESTrack * track, GESTrackObject * track_object)
|
||||
{
|
||||
GList *track_objects, *tmp, *cur;
|
||||
GESTimelineLayer *layer;
|
||||
|
||||
track_objects = ges_track_get_objects (track);
|
||||
cur = g_list_find (track_objects, track_object);
|
||||
for (tmp = cur->next; tmp; tmp = tmp->next) {
|
||||
if (GES_IS_TRACK_SOURCE (tmp->data)) {
|
||||
break;
|
||||
}
|
||||
if (GES_IS_TRACK_AUDIO_TRANSITION (tmp->data)
|
||||
|| GES_IS_TRACK_VIDEO_TRANSITION (tmp->data)) {
|
||||
layer =
|
||||
ges_timeline_object_get_layer (ges_track_object_get_timeline_object
|
||||
(tmp->data));
|
||||
if (ges_timeline_layer_get_auto_transition (layer)) {
|
||||
ges_track_enable_update (track, FALSE);
|
||||
ges_timeline_layer_remove_object (layer,
|
||||
ges_track_object_get_timeline_object (tmp->data));
|
||||
ges_track_enable_update (track, TRUE);
|
||||
}
|
||||
g_object_unref (layer);
|
||||
}
|
||||
}
|
||||
|
||||
for (tmp = cur->prev; tmp; tmp = tmp->prev) {
|
||||
if (GES_IS_TRACK_SOURCE (tmp->data)) {
|
||||
break;
|
||||
}
|
||||
if (GES_IS_TRACK_AUDIO_TRANSITION (tmp->data)
|
||||
|| GES_IS_TRACK_VIDEO_TRANSITION (tmp->data)) {
|
||||
layer =
|
||||
ges_timeline_object_get_layer (ges_track_object_get_timeline_object
|
||||
(tmp->data));
|
||||
if (ges_timeline_layer_get_auto_transition (layer)) {
|
||||
ges_track_enable_update (track, FALSE);
|
||||
ges_timeline_layer_remove_object (layer,
|
||||
ges_track_object_get_timeline_object (tmp->data));
|
||||
ges_track_enable_update (track, TRUE);
|
||||
}
|
||||
g_object_unref (layer);
|
||||
}
|
||||
}
|
||||
g_object_unref (track_object);
|
||||
}
|
||||
|
||||
static void
|
||||
track_object_changed_cb (GESTrackObject * track_object,
|
||||
GParamSpec * arg G_GNUC_UNUSED)
|
||||
{
|
||||
if (G_LIKELY (GES_IS_TRACK_SOURCE (track_object)))
|
||||
calculate_transitions (track_object);
|
||||
}
|
||||
|
||||
static void
|
||||
track_object_added_cb (GESTrack * track, GESTrackObject * track_object,
|
||||
GESTimelineLayer * layer)
|
||||
{
|
||||
if (GES_IS_TRACK_SOURCE (track_object)) {
|
||||
g_signal_connect (G_OBJECT (track_object), "notify::start",
|
||||
G_CALLBACK (track_object_changed_cb), NULL);
|
||||
g_signal_connect (G_OBJECT (track_object), "notify::duration",
|
||||
G_CALLBACK (track_object_duration_cb), NULL);
|
||||
calculate_transitions (track_object);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
track_removed_cb (GESTrack * track, GESTrackObject * track_object,
|
||||
GESTimelineLayer * layer)
|
||||
{
|
||||
g_signal_handlers_disconnect_by_func (track, track_object_added_cb, layer);
|
||||
g_signal_handlers_disconnect_by_func (track, track_object_removed_cb, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
track_added_cb (GESTimeline * timeline, GESTrack * track,
|
||||
GESTimelineLayer * layer)
|
||||
{
|
||||
g_signal_connect (track, "track-object-removed",
|
||||
(GCallback) track_object_removed_cb, NULL);
|
||||
g_signal_connect (track, "track-object-added",
|
||||
(GCallback) track_object_added_cb, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
timeline_object_height_changed_cb (GESTimelineObject * obj,
|
||||
GESTrackEffect * tr_eff, GESTimelineObject * second_obj)
|
||||
{
|
||||
gint priority, height;
|
||||
g_object_get (obj, "height", &height, "priority", &priority, NULL);
|
||||
g_object_set (second_obj, "priority", priority + height, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
start_calculating_transitions (GESTimelineLayer * layer)
|
||||
{
|
||||
GList *tmp, *tracks = ges_timeline_get_tracks (layer->timeline);
|
||||
|
||||
g_signal_connect (layer->timeline, "track-added", G_CALLBACK (track_added_cb),
|
||||
layer);
|
||||
g_signal_connect (layer->timeline, "track-removed",
|
||||
G_CALLBACK (track_removed_cb), layer);
|
||||
|
||||
for (tmp = tracks; tmp; tmp = tmp->next) {
|
||||
g_signal_connect (G_OBJECT (tmp->data), "track-object-added",
|
||||
G_CALLBACK (track_object_added_cb), layer);
|
||||
g_signal_connect (G_OBJECT (tmp->data), "track-object-removed",
|
||||
G_CALLBACK (track_object_removed_cb), NULL);
|
||||
}
|
||||
|
||||
g_list_free_full (tracks, g_object_unref);
|
||||
|
||||
/* FIXME calculate all the transitions at that time */
|
||||
}
|
||||
|
||||
static void
|
||||
new_asset_cb (GESAsset * source, GAsyncResult * res, NewAssetUData * udata)
|
||||
{
|
||||
|
@ -741,7 +293,6 @@ ges_timeline_layer_remove_object (GESTimelineLayer * layer,
|
|||
GESTimelineObject * object)
|
||||
{
|
||||
GESTimelineLayer *current_layer;
|
||||
GList *trackobjects, *tmp;
|
||||
|
||||
g_return_val_if_fail (GES_IS_TIMELINE_LAYER (layer), FALSE);
|
||||
g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
|
||||
|
@ -759,17 +310,6 @@ ges_timeline_layer_remove_object (GESTimelineLayer * layer,
|
|||
}
|
||||
g_object_unref (current_layer);
|
||||
|
||||
if (layer->priv->auto_transition && GES_IS_TIMELINE_SOURCE (object)) {
|
||||
trackobjects = ges_timeline_object_get_track_objects (object);
|
||||
|
||||
for (tmp = trackobjects; tmp; tmp = tmp->next) {
|
||||
look_for_transition (tmp->data, layer);
|
||||
}
|
||||
|
||||
g_list_foreach (trackobjects, (GFunc) g_object_unref, NULL);
|
||||
g_list_free (trackobjects);
|
||||
}
|
||||
|
||||
/* emit 'object-removed' */
|
||||
g_signal_emit (layer, ges_timeline_layer_signals[OBJECT_REMOVED], 0, object);
|
||||
|
||||
|
@ -842,9 +382,6 @@ ges_timeline_layer_set_auto_transition (GESTimelineLayer * layer,
|
|||
|
||||
g_return_if_fail (GES_IS_TIMELINE_LAYER (layer));
|
||||
|
||||
if (auto_transition && layer->timeline)
|
||||
start_calculating_transitions (layer);
|
||||
|
||||
layer->priv->auto_transition = auto_transition;
|
||||
g_object_notify (G_OBJECT (layer), "auto-transition");
|
||||
}
|
||||
|
@ -946,7 +483,6 @@ ges_timeline_layer_add_object (GESTimelineLayer * layer,
|
|||
|
||||
priv = layer->priv;
|
||||
current_layer = ges_timeline_object_get_layer (object);
|
||||
|
||||
if (G_UNLIKELY (current_layer)) {
|
||||
GST_WARNING ("TimelineObject %p already belongs to another layer", object);
|
||||
g_object_unref (current_layer);
|
||||
|
@ -1107,18 +643,5 @@ ges_timeline_layer_set_timeline (GESTimelineLayer * layer,
|
|||
{
|
||||
GST_DEBUG ("layer:%p, timeline:%p", layer, timeline);
|
||||
|
||||
if (layer->priv->auto_transition == TRUE) {
|
||||
if (layer->timeline != NULL) {
|
||||
g_signal_handlers_disconnect_by_func (layer->timeline, track_added_cb,
|
||||
layer);
|
||||
g_signal_handlers_disconnect_by_func (layer->timeline, track_removed_cb,
|
||||
layer);
|
||||
}
|
||||
|
||||
layer->timeline = timeline;
|
||||
if (timeline != NULL)
|
||||
start_calculating_transitions (layer);
|
||||
|
||||
} else
|
||||
layer->timeline = timeline;
|
||||
layer->timeline = timeline;
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include "ges-timeline.h"
|
||||
#include "ges-track.h"
|
||||
#include "ges-timeline-layer.h"
|
||||
#include "ges-auto-transition.h"
|
||||
#include "ges.h"
|
||||
|
||||
typedef struct _MoveContext MoveContext;
|
||||
|
@ -143,8 +144,17 @@ struct _GESTimelinePrivate
|
|||
* probably through a ges_timeline_layer_get_track_objects () method */
|
||||
GHashTable *by_layer; /* {layer: GSequence of TrackObject by start/priorities} */
|
||||
|
||||
/* The set of auto_transitions we control, currently the key is
|
||||
* pointerToPreviousiTrackObjAdresspointerToNextTrackObjAdress as a string,
|
||||
* ... not really optimal but it works */
|
||||
GHashTable *auto_transitions;
|
||||
|
||||
MoveContext movecontext;
|
||||
|
||||
/* This variable is set to %TRUE when it makes sense to update the transitions,
|
||||
* and %FALSE otherwize */
|
||||
gboolean needs_transitions_update;
|
||||
|
||||
gboolean updates_enabled;
|
||||
};
|
||||
|
||||
|
@ -292,12 +302,15 @@ ges_timeline_dispose (GObject * object)
|
|||
g_hash_table_unref (priv->by_start);
|
||||
g_hash_table_unref (priv->by_end);
|
||||
g_hash_table_unref (priv->by_object);
|
||||
g_hash_table_unref (priv->by_layer);
|
||||
g_hash_table_unref (priv->obj_iters);
|
||||
g_sequence_free (priv->starts_ends);
|
||||
g_sequence_free (priv->tracksources);
|
||||
g_list_free (priv->movecontext.moving_tckobjs);
|
||||
g_hash_table_unref (priv->movecontext.moving_tlobjs);
|
||||
|
||||
g_hash_table_unref (priv->auto_transitions);
|
||||
|
||||
G_OBJECT_CLASS (ges_timeline_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
|
@ -513,6 +526,12 @@ ges_timeline_init (GESTimeline * self)
|
|||
priv->starts_ends = g_sequence_new (g_free);
|
||||
priv->tracksources = g_sequence_new (g_object_unref);
|
||||
|
||||
priv->auto_transitions =
|
||||
g_hash_table_new_full (g_str_hash, g_str_equal, NULL, gst_object_unref);
|
||||
priv->needs_transitions_update = TRUE;
|
||||
|
||||
priv->updates_enabled = TRUE;
|
||||
|
||||
g_signal_connect_after (self, "select-tracks-for-object",
|
||||
G_CALLBACK (select_tracks_for_object_default), NULL);
|
||||
}
|
||||
|
@ -629,6 +648,227 @@ sort_starts_ends_start (GESTimeline * timeline, TrackObjIters * iters)
|
|||
timeline_update_duration (timeline);
|
||||
}
|
||||
|
||||
static void
|
||||
_destroy_auto_transition_cb (GESAutoTransition * auto_transition,
|
||||
GESTimeline * timeline)
|
||||
{
|
||||
GESTimelinePrivate *priv = timeline->priv;
|
||||
GESTimelineObject *transition = auto_transition->timeline_transition;
|
||||
GESTimelineLayer *layer = ges_timeline_object_get_layer (transition);
|
||||
|
||||
ges_timeline_layer_remove_object (layer, transition);
|
||||
g_signal_handlers_disconnect_by_func (auto_transition,
|
||||
_destroy_auto_transition_cb, timeline);
|
||||
|
||||
if (!g_hash_table_remove (priv->auto_transitions, auto_transition->key))
|
||||
GST_WARNING_OBJECT (timeline, "Could not remove auto_transition %"
|
||||
GST_PTR_FORMAT, auto_transition->key);
|
||||
}
|
||||
|
||||
static GESAutoTransition *
|
||||
create_transition (GESTimeline * timeline, GESTrackObject * previous,
|
||||
GESTrackObject * next, GESTimelineObject * transition,
|
||||
GESTimelineLayer * layer, guint64 start, guint64 duration)
|
||||
{
|
||||
GList *tckobjs;
|
||||
GESAsset *asset;
|
||||
GESAutoTransition *auto_transition;
|
||||
|
||||
if (transition == NULL) {
|
||||
/* TODO make it possible to specify a Transition asset in the API */
|
||||
asset = ges_asset_request (GES_TYPE_TIMELINE_STANDARD_TRANSITION,
|
||||
"crossfade", NULL);
|
||||
transition =
|
||||
ges_timeline_layer_add_asset (layer, asset, start, 0, duration, 1,
|
||||
ges_track_object_get_track_type (next));
|
||||
} else {
|
||||
GST_DEBUG_OBJECT (timeline,
|
||||
"Reusing already existing transition: %" GST_PTR_FORMAT, transition);
|
||||
}
|
||||
|
||||
/* We know there is only 1 TrackObject */
|
||||
tckobjs = ges_timeline_object_get_track_objects (transition);
|
||||
auto_transition = ges_auto_transition_new (tckobjs->data, previous, next);
|
||||
g_list_free_full (tckobjs, gst_object_unref);
|
||||
|
||||
g_signal_connect (auto_transition, "destroy-me",
|
||||
G_CALLBACK (_destroy_auto_transition_cb), timeline);
|
||||
|
||||
g_hash_table_insert (timeline->priv->auto_transitions,
|
||||
auto_transition->key, auto_transition);
|
||||
|
||||
return auto_transition;
|
||||
}
|
||||
|
||||
typedef GESAutoTransition *(*GetAutoTransitionFunc) (GESTimeline * timeline,
|
||||
GESTimelineLayer * layer, GESTrack * track, GESTrackObject * previous,
|
||||
GESTrackObject * next, GstClockTime transition_duration);
|
||||
|
||||
static GESAutoTransition *
|
||||
_find_transition_from_auto_transitions (GESTimeline * timeline,
|
||||
GESTimelineLayer * layer, GESTrack * track, GESTrackObject * prev,
|
||||
GESTrackObject * next, GstClockTime transition_duration)
|
||||
{
|
||||
GESAutoTransition *auto_transition;
|
||||
|
||||
gchar *key = g_strdup_printf ("%p%p", prev, next);
|
||||
|
||||
auto_transition = g_hash_table_lookup (timeline->priv->auto_transitions, key);
|
||||
g_free (key);
|
||||
|
||||
return auto_transition;
|
||||
}
|
||||
|
||||
static GESAutoTransition *
|
||||
_create_auto_transition_from_transitions (GESTimeline * timeline,
|
||||
GESTimelineLayer * layer, GESTrack * track, GESTrackObject * prev,
|
||||
GESTrackObject * next, GstClockTime transition_duration)
|
||||
{
|
||||
GSequenceIter *tmp_iter;
|
||||
GSequence *by_layer_sequence;
|
||||
|
||||
GESTimelinePrivate *priv = timeline->priv;
|
||||
GESAutoTransition *auto_transition =
|
||||
_find_transition_from_auto_transitions (timeline, layer, track, prev,
|
||||
next, transition_duration);
|
||||
|
||||
if (auto_transition)
|
||||
return auto_transition;
|
||||
|
||||
|
||||
/* Try to find a transition that perfectly fits with the one that
|
||||
* should be added at that place
|
||||
* optimize: Use g_sequence_search instead of going over all the
|
||||
* sequence */
|
||||
by_layer_sequence = g_hash_table_lookup (priv->by_layer, layer);
|
||||
for (tmp_iter = g_sequence_get_begin_iter (by_layer_sequence);
|
||||
tmp_iter && !g_sequence_iter_is_end (tmp_iter);
|
||||
tmp_iter = g_sequence_iter_next (tmp_iter)) {
|
||||
GESTrackObject *maybe_transition = g_sequence_get (tmp_iter);
|
||||
|
||||
if (ges_track_object_get_track (maybe_transition) != track)
|
||||
continue;
|
||||
|
||||
if (maybe_transition->start > next->start)
|
||||
break;
|
||||
else if (maybe_transition->start != next->start ||
|
||||
maybe_transition->duration != transition_duration)
|
||||
continue;
|
||||
else if (GES_IS_TRACK_TRANSITION (maybe_transition))
|
||||
/* Use that transition */
|
||||
/* TODO We should make sure that the transition contains only
|
||||
* TrackObject-s in @track and if it is not the case properly unlink the
|
||||
* object to use it */
|
||||
return create_transition (timeline, prev, next,
|
||||
ges_track_object_get_timeline_object (maybe_transition), layer,
|
||||
next->start, transition_duration);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Create all transition that do not exist on @layer.
|
||||
* @get_auto_transition is called to check if a particular transition exists
|
||||
* if @ track is specified, we will create the transitions only for that particular
|
||||
* track */
|
||||
static void
|
||||
_create_transitions_on_layer (GESTimeline * timeline, GESTimelineLayer * layer,
|
||||
GESTrack * track, GESTrackObject * initiating_obj,
|
||||
GetAutoTransitionFunc get_auto_transition)
|
||||
{
|
||||
guint32 layer_prio;
|
||||
GSequenceIter *iter;
|
||||
GESAutoTransition *transition;
|
||||
|
||||
GESTrack *ctrack = track;
|
||||
GList *entered = NULL; /* List of TrackObject for wich we walk through the
|
||||
* "start" but not the "end" in the starts_ends list */
|
||||
GESTimelinePrivate *priv = timeline->priv;
|
||||
|
||||
if (!layer || !ges_timeline_layer_get_auto_transition (layer))
|
||||
return;
|
||||
|
||||
layer_prio = ges_timeline_layer_get_priority (layer);
|
||||
for (iter = g_sequence_get_begin_iter (priv->starts_ends);
|
||||
iter && !g_sequence_iter_is_end (iter);
|
||||
iter = g_sequence_iter_next (iter)) {
|
||||
GList *tmp;
|
||||
guint *start_or_end = g_sequence_get (iter);
|
||||
GESTrackObject *next = g_hash_table_lookup (timeline->priv->by_object,
|
||||
start_or_end);
|
||||
|
||||
/* Only object that are in that layer and track */
|
||||
if ((next->priority / LAYER_HEIGHT) != layer_prio ||
|
||||
(track && track != ges_track_object_get_track (next)))
|
||||
continue;
|
||||
|
||||
if (track == NULL)
|
||||
ctrack = ges_track_object_get_track (next);
|
||||
|
||||
if (start_or_end == g_hash_table_lookup (priv->by_end, next)) {
|
||||
if (initiating_obj == next) {
|
||||
/* We passed the objects that initiated the research
|
||||
* we are now done */
|
||||
g_list_free (entered);
|
||||
return;
|
||||
}
|
||||
entered = g_list_remove (entered, next);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
for (tmp = entered; tmp; tmp = tmp->next) {
|
||||
gint64 transition_duration;
|
||||
|
||||
GESTrackObject *prev = tmp->data;
|
||||
|
||||
if (ctrack != ges_track_object_get_track (prev))
|
||||
continue;
|
||||
|
||||
transition_duration = (prev->start + prev->duration) - next->start;
|
||||
if (transition_duration > 0 && transition_duration < prev->duration &&
|
||||
transition_duration < next->duration) {
|
||||
transition =
|
||||
get_auto_transition (timeline, layer, ctrack, prev, next,
|
||||
transition_duration);
|
||||
if (!transition)
|
||||
transition = create_transition (timeline, prev, next, NULL, layer,
|
||||
next->start, transition_duration);
|
||||
}
|
||||
}
|
||||
|
||||
/* And add that object to the entered list so that it we can possibly set
|
||||
* a transition on its end edge */
|
||||
entered = g_list_append (entered, next);
|
||||
}
|
||||
}
|
||||
|
||||
/* @tck_obj must be a GESTrackSource */
|
||||
static void
|
||||
create_transitions (GESTimeline * timeline, GESTrackObject * tck_obj)
|
||||
{
|
||||
GESTrack *track;
|
||||
GList *layer_node;
|
||||
|
||||
GESTimelinePrivate *priv = timeline->priv;
|
||||
|
||||
if (!priv->needs_transitions_update || !priv->updates_enabled)
|
||||
return;
|
||||
|
||||
GST_DEBUG_OBJECT (timeline, "Creating transitions around %p", tck_obj);
|
||||
|
||||
track = ges_track_object_get_track (tck_obj);
|
||||
layer_node = g_list_find_custom (timeline->layers,
|
||||
GINT_TO_POINTER (tck_obj->priority / LAYER_HEIGHT),
|
||||
(GCompareFunc) find_layer_by_prio);
|
||||
|
||||
_create_transitions_on_layer (timeline,
|
||||
layer_node ? layer_node->data : NULL, track, tck_obj,
|
||||
_find_transition_from_auto_transitions);
|
||||
|
||||
GST_DEBUG_OBJECT (timeline, "Done updating transitions");
|
||||
}
|
||||
|
||||
/* Timeline edition functions */
|
||||
static inline void
|
||||
init_movecontext (MoveContext * mv_ctx, gboolean first_init)
|
||||
|
@ -744,6 +984,7 @@ start_tracking_track_object (GESTimeline * timeline, GESTrackObject * tckobj)
|
|||
timeline->priv->movecontext.needs_move_ctx = TRUE;
|
||||
|
||||
timeline_update_duration (timeline);
|
||||
create_transitions (timeline, tckobj);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1084,9 +1325,12 @@ ges_timeline_trim_object_simple (GESTimeline * timeline, GESTrackObject * obj,
|
|||
duration = MAX (0, real_dur);
|
||||
duration = MIN (duration, max_duration - obj->inpoint);
|
||||
|
||||
timeline->priv->needs_transitions_update = FALSE;
|
||||
ges_track_object_set_start (obj, nstart);
|
||||
ges_track_object_set_duration (obj, duration);
|
||||
ges_track_object_set_inpoint (obj, inpoint);
|
||||
timeline->priv->needs_transitions_update = TRUE;
|
||||
|
||||
ges_track_object_set_duration (obj, duration);
|
||||
break;
|
||||
case GES_EDGE_END:
|
||||
{
|
||||
|
@ -1133,6 +1377,7 @@ timeline_ripple_object (GESTimeline * timeline, GESTrackObject * obj,
|
|||
case GES_EDGE_NONE:
|
||||
GST_DEBUG ("Simply rippling");
|
||||
|
||||
/* We should be smart here to avoid recalculate transitions when possible */
|
||||
cur = g_hash_table_lookup (timeline->priv->by_end, obj);
|
||||
snapped = ges_timeline_snap_position (timeline, obj, cur, position, TRUE);
|
||||
if (snapped)
|
||||
|
@ -1163,6 +1408,7 @@ timeline_ripple_object (GESTimeline * timeline, GESTrackObject * obj,
|
|||
|
||||
break;
|
||||
case GES_EDGE_END:
|
||||
timeline->priv->needs_transitions_update = FALSE;
|
||||
GST_DEBUG ("Rippling end");
|
||||
|
||||
cur = g_hash_table_lookup (timeline->priv->by_end, obj);
|
||||
|
@ -1195,6 +1441,7 @@ timeline_ripple_object (GESTimeline * timeline, GESTrackObject * obj,
|
|||
}
|
||||
|
||||
g_list_free (moved_tlobjs);
|
||||
timeline->priv->needs_transitions_update = TRUE;
|
||||
GST_DEBUG ("Done Rippling end");
|
||||
break;
|
||||
case GES_EDGE_START:
|
||||
|
@ -1273,6 +1520,7 @@ timeline_roll_object (GESTimeline * timeline, GESTrackObject * obj,
|
|||
duration = ges_track_object_get_duration (obj);
|
||||
end = start + duration;
|
||||
|
||||
timeline->priv->needs_transitions_update = FALSE;
|
||||
switch (edge) {
|
||||
case GES_EDGE_START:
|
||||
|
||||
|
@ -1587,6 +1835,15 @@ add_object_to_tracks (GESTimeline * timeline, GESTimelineObject * object,
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
layer_auto_transition_changed_cb (GESTimelineLayer * layer,
|
||||
GParamSpec * arg G_GNUC_UNUSED, GESTimeline * timeline)
|
||||
{
|
||||
_create_transitions_on_layer (timeline, layer, NULL, NULL,
|
||||
_create_auto_transition_from_transitions);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
layer_object_added_cb (GESTimelineLayer * layer, GESTimelineObject * object,
|
||||
GESTimeline * timeline)
|
||||
|
@ -1595,6 +1852,10 @@ layer_object_added_cb (GESTimelineLayer * layer, GESTimelineObject * object,
|
|||
GST_DEBUG ("TimelineObject %p is moving from a layer to another, not doing"
|
||||
" anything on it", object);
|
||||
timeline->priv->movecontext.needs_move_ctx = TRUE;
|
||||
|
||||
_create_transitions_on_layer (timeline, layer, NULL, NULL,
|
||||
_find_transition_from_auto_transitions);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1637,8 +1898,8 @@ layer_object_removed_cb (GESTimelineLayer * layer, GESTimelineObject * object,
|
|||
ges_track_object_get_track (trobj),
|
||||
(GCompareFunc) custom_find_track))) {
|
||||
GST_DEBUG ("Belongs to one of the tracks we control");
|
||||
ges_track_remove_object (ges_track_object_get_track (trobj), trobj);
|
||||
|
||||
ges_track_remove_object (ges_track_object_get_track (trobj), trobj);
|
||||
ges_timeline_object_release_track_object (object, trobj);
|
||||
}
|
||||
/* removing the reference added by _get_track_objects() */
|
||||
|
@ -1674,6 +1935,7 @@ trackobj_start_changed_cb (GESTrackObject * child,
|
|||
timeline->priv->snapping_distance == 0)
|
||||
timeline->priv->movecontext.needs_move_ctx = TRUE;
|
||||
|
||||
create_transitions (timeline, child);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1736,6 +1998,8 @@ trackobj_duration_changed_cb (GESTrackObject * child,
|
|||
timeline->priv->snapping_distance == 0) {
|
||||
timeline->priv->movecontext.needs_move_ctx = TRUE;
|
||||
}
|
||||
|
||||
create_transitions (timeline, child);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1760,16 +2024,18 @@ track_object_removed_cb (GESTrack * track, GESTrackObject * object,
|
|||
{
|
||||
|
||||
if (GES_IS_TRACK_SOURCE (object)) {
|
||||
|
||||
/* Make sure to reinitialise the moving context next time */
|
||||
timeline->priv->movecontext.needs_move_ctx = TRUE;
|
||||
}
|
||||
|
||||
/* Disconnect all signal handlers */
|
||||
g_signal_handlers_disconnect_by_func (object, trackobj_start_changed_cb,
|
||||
NULL);
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -2014,6 +2280,8 @@ ges_timeline_add_layer (GESTimeline * timeline, GESTimelineLayer * layer)
|
|||
G_CALLBACK (layer_object_removed_cb), timeline);
|
||||
g_signal_connect (layer, "notify::priority",
|
||||
G_CALLBACK (layer_priority_changed_cb), timeline);
|
||||
g_signal_connect (layer, "notify::auto-transition",
|
||||
G_CALLBACK (layer_auto_transition_changed_cb), timeline);
|
||||
|
||||
GST_DEBUG ("Done adding layer, emitting 'layer-added' signal");
|
||||
g_signal_emit (timeline, ges_timeline_signals[LAYER_ADDED], 0, layer);
|
||||
|
@ -2069,10 +2337,13 @@ ges_timeline_remove_layer (GESTimeline * timeline, GESTimelineLayer * layer)
|
|||
g_signal_handlers_disconnect_by_func (layer, layer_object_added_cb, timeline);
|
||||
g_signal_handlers_disconnect_by_func (layer, layer_object_removed_cb,
|
||||
timeline);
|
||||
g_signal_handlers_disconnect_by_func (layer, layer_priority_changed_cb,
|
||||
timeline);
|
||||
g_signal_handlers_disconnect_by_func (layer,
|
||||
layer_auto_transition_changed_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);
|
||||
|
||||
g_signal_emit (timeline, ges_timeline_signals[LAYER_REMOVED], 0, layer);
|
||||
|
@ -2358,6 +2629,12 @@ ges_timeline_enable_update (GESTimeline * timeline, gboolean enabled)
|
|||
/* Make sure we reset the context */
|
||||
timeline->priv->movecontext.needs_move_ctx = TRUE;
|
||||
timeline->priv->updates_enabled = enabled;
|
||||
|
||||
for (tmp = timeline->layers; tmp; tmp = tmp->next) {
|
||||
_create_transitions_on_layer (timeline, GES_TIMELINE_LAYER (tmp->data),
|
||||
NULL, NULL, _find_transition_from_auto_transitions);
|
||||
}
|
||||
|
||||
if (res)
|
||||
g_object_notify_by_pspec (G_OBJECT (timeline), properties[PROP_UPDATE]);
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue