mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-08 16:35:40 +00:00
timeline: re-handle clip children track selection
The way a clip's track elements are added to tracks was re-handled. This doesn't affect the normal usage of a simple audio-video timeline, where the tracks are added before any clips, but usage for multi-track timelines has improved. The main changes are: + We can now handle a track being selected for more than one track, including a full copy of their children properties and bindings. (Previously broken.) + When a clip is split, we copy the new elements directly into the same track, avoiding select-tracks-for-object. + When a clip is grouped or ungrouped, we avoid moving the elements to or from tracks. + Added API to allow users to copy the core elements of a clip directly into a track, complementing select-tracks-for-object. + Enforced the rule that a clip can only contain one core child in a track, and all the non-core children must be added to tracks that already contains a core child. This extends the previous condition that two sources from the same clip should not be added to the same track. + Made ges_track_add_element check that the newly added track element does not break the configuration rules of the timeline. + When adding a track to a timeline, we only use select-tracks-for-object to check whether track elements should be added to the new track, not existing ones. + When removing a track from a timeline, we empty it of all the track elements that are controlled by a clip. Thus, we ensure that a clip only contains elements that are in the tracks of the same timeline, or no track. Similarly, when removing a clip from a timeline. + We can now avoid unsupported timeline configurations when a layer is added to a timeline, and already contains clips. + We can now avoid unsupported timeline configurations when a track is added to a timeline, and the timeline already contains clips. Fixes https://gitlab.freedesktop.org/gstreamer/gst-editing-services/-/issues/84
This commit is contained in:
parent
f7a1bdb289
commit
269c2d1dc0
11 changed files with 2230 additions and 838 deletions
615
ges/ges-clip.c
615
ges/ges-clip.c
|
@ -113,6 +113,8 @@ struct _GESClipPrivate
|
|||
gboolean prevent_max_duration_update;
|
||||
gboolean setting_inpoint;
|
||||
|
||||
gboolean allow_any_track;
|
||||
|
||||
/* The formats supported by this Clip */
|
||||
GESTrackType supportedformats;
|
||||
};
|
||||
|
@ -148,8 +150,10 @@ G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GESClip, ges_clip,
|
|||
* Listen to our children *
|
||||
****************************************************/
|
||||
|
||||
#define _IS_CORE_CHILD(child) \
|
||||
(ges_track_element_get_creators (GES_TRACK_ELEMENT (child)) != NULL)
|
||||
#define _IS_CORE_CHILD(child) GES_TRACK_ELEMENT_IS_CORE(child)
|
||||
|
||||
#define _IS_TOP_EFFECT(child) \
|
||||
(!_IS_CORE_CHILD (child) && GES_IS_BASE_EFFECT (child))
|
||||
|
||||
#define _IS_CORE_INTERNAL_SOURCE_CHILD(child) \
|
||||
(_IS_CORE_CHILD (child) \
|
||||
|
@ -275,6 +279,85 @@ _child_has_internal_source_changed_cb (GESTimelineElement * child,
|
|||
_set_inpoint0 (child, _INPOINT (container));
|
||||
}
|
||||
|
||||
/****************************************************
|
||||
* Restrict our children *
|
||||
****************************************************/
|
||||
|
||||
static gboolean
|
||||
_track_contains_core (GESClip * clip, GESTrack * track, gboolean core)
|
||||
{
|
||||
GList *tmp;
|
||||
for (tmp = GES_CONTAINER_CHILDREN (clip); tmp; tmp = tmp->next) {
|
||||
GESTrackElement *child = tmp->data;
|
||||
if (_IS_CORE_CHILD (child) == core
|
||||
&& ges_track_element_get_track (child) == track)
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
ges_clip_can_set_track_of_child (GESClip * clip, GESTrackElement * child,
|
||||
GESTrack * track)
|
||||
{
|
||||
GESTrack *current_track = ges_track_element_get_track (child);
|
||||
|
||||
if (clip->priv->allow_any_track)
|
||||
return TRUE;
|
||||
|
||||
if (current_track == track)
|
||||
return TRUE;
|
||||
|
||||
if (current_track) {
|
||||
/* can not remove a core element from a track if a non-core one sits
|
||||
* above it */
|
||||
if (_IS_CORE_CHILD (child)
|
||||
&& _track_contains_core (clip, current_track, FALSE)) {
|
||||
GST_INFO_OBJECT (clip, "Cannot move the core child %" GES_FORMAT
|
||||
" to the track %" GST_PTR_FORMAT " because it has non-core "
|
||||
"siblings above it in its current track %" GST_PTR_FORMAT,
|
||||
GES_ARGS (child), track, current_track);
|
||||
return FALSE;
|
||||
}
|
||||
/* otherwise can remove */
|
||||
}
|
||||
if (track) {
|
||||
GESTimeline *clip_timeline = GES_TIMELINE_ELEMENT_TIMELINE (clip);
|
||||
const GESTimeline *track_timeline = ges_track_get_timeline (track);
|
||||
if (track_timeline == NULL) {
|
||||
GST_INFO_OBJECT (clip, "Cannot move the child %" GES_FORMAT
|
||||
" to the track %" GST_PTR_FORMAT " because it is not part "
|
||||
"of a timeline", GES_ARGS (child), track);
|
||||
return FALSE;
|
||||
}
|
||||
if (track_timeline != clip_timeline) {
|
||||
GST_INFO_OBJECT (clip, "Cannot move the child %" GES_FORMAT
|
||||
" to the track %" GST_PTR_FORMAT " because its timeline %"
|
||||
GST_PTR_FORMAT " does not match the clip's timeline %"
|
||||
GST_PTR_FORMAT, GES_ARGS (child), track, track_timeline,
|
||||
clip_timeline);
|
||||
return FALSE;
|
||||
}
|
||||
/* one core child per track, and other children (effects) can only be
|
||||
* placed in a track that already has a core child */
|
||||
if (_IS_CORE_CHILD (child)) {
|
||||
if (_track_contains_core (clip, track, TRUE)) {
|
||||
GST_INFO_OBJECT (clip, "Cannot move the core child %" GES_FORMAT
|
||||
" to the track %" GST_PTR_FORMAT " because it contains a "
|
||||
"core sibling", GES_ARGS (child), track);
|
||||
return FALSE;
|
||||
}
|
||||
} else {
|
||||
if (!_track_contains_core (clip, track, TRUE)) {
|
||||
GST_INFO_OBJECT (clip, "Cannot move the non-core child %"
|
||||
GES_FORMAT " to the track %" GST_PTR_FORMAT " because it "
|
||||
" does not contain a core sibling", GES_ARGS (child), track);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*****************************************************
|
||||
* *
|
||||
|
@ -574,17 +657,18 @@ _add_child (GESContainer * container, GESTimelineElement * element)
|
|||
{
|
||||
GESClipClass *klass = GES_CLIP_GET_CLASS (GES_CLIP (container));
|
||||
guint max_prio, min_prio;
|
||||
GESTrack *track;
|
||||
GList *creators;
|
||||
GESTimeline *timeline = GES_TIMELINE_ELEMENT_TIMELINE (container);
|
||||
GESClipPrivate *priv = GES_CLIP (container)->priv;
|
||||
|
||||
g_return_val_if_fail (GES_IS_TRACK_ELEMENT (element), FALSE);
|
||||
|
||||
if (element->timeline
|
||||
&& element->timeline != GES_TIMELINE_ELEMENT_TIMELINE (container)) {
|
||||
if (element->timeline && element->timeline != timeline) {
|
||||
GST_WARNING_OBJECT (container, "Cannot add %" GES_FORMAT " as a child "
|
||||
"because its timeline is %" GST_PTR_FORMAT " rather than the "
|
||||
"clip's timeline %" GST_PTR_FORMAT, GES_ARGS (element),
|
||||
element->timeline, GES_TIMELINE_ELEMENT_TIMELINE (container));
|
||||
element->timeline, timeline);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
@ -597,12 +681,34 @@ _add_child (GESContainer * container, GESTimelineElement * element)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
track = ges_track_element_get_track (GES_TRACK_ELEMENT (element));
|
||||
|
||||
if (track && ges_track_get_timeline (track) != timeline) {
|
||||
/* really, an element in a track should have the same timeline as
|
||||
* the track, so we would have checked this with the
|
||||
* element->timeline check. But technically a user could get around
|
||||
* this, so we double check here. */
|
||||
GST_WARNING_OBJECT (container, "Cannot add %" GES_FORMAT " as a child "
|
||||
"because its track %" GST_PTR_FORMAT " is part of the timeline %"
|
||||
GST_PTR_FORMAT " rather than the clip's timeline %" GST_PTR_FORMAT,
|
||||
GES_ARGS (element), track, ges_track_get_timeline (track), timeline);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* NOTE: notifies are currently frozen by ges_container_add */
|
||||
_get_priority_range (container, &min_prio, &max_prio);
|
||||
if (creators) {
|
||||
/* NOTE: Core track elements that are base effects are added like any
|
||||
* other core clip. In particular, they are *not* added to the list of
|
||||
* added effects, so we don not increase nb_effects. */
|
||||
* other core elements. In particular, they are *not* added to the
|
||||
* list of added effects, so we do not increase nb_effects. */
|
||||
|
||||
if (track && !priv->allow_any_track
|
||||
&& _track_contains_core (GES_CLIP (container), track, TRUE)) {
|
||||
GST_WARNING_OBJECT (container, "Cannot add the core child %" GES_FORMAT
|
||||
" because it is in the same track %" GST_PTR_FORMAT " as an "
|
||||
"existing core child", GES_ARGS (element), track);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Set the core element to have the same in-point, which we don't
|
||||
* apply to effects */
|
||||
|
@ -620,12 +726,20 @@ _add_child (GESContainer * container, GESTimelineElement * element)
|
|||
|
||||
/* Always add at the same priority, on top of existing effects */
|
||||
_set_priority0 (element, min_prio + priv->nb_effects);
|
||||
} else if (GES_CLIP_CLASS_CAN_ADD_EFFECTS (klass) &&
|
||||
GES_IS_BASE_EFFECT (element)) {
|
||||
} else if (GES_CLIP_CLASS_CAN_ADD_EFFECTS (klass) && _IS_TOP_EFFECT (element)) {
|
||||
GList *tmp;
|
||||
/* Add the effect at the lowest priority among effects (just after
|
||||
* the core elements). Need to shift the core elements up by 1
|
||||
* to make room. */
|
||||
|
||||
if (track && !priv->allow_any_track
|
||||
&& !_track_contains_core (GES_CLIP (container), track, TRUE)) {
|
||||
GST_WARNING_OBJECT (container, "Cannot add the effect %" GES_FORMAT
|
||||
" because its track %" GST_PTR_FORMAT " does not contain one "
|
||||
"of the clip's core children", GES_ARGS (element), track);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (container, "Adding %ith effect: %" GES_FORMAT
|
||||
" Priority %i", priv->nb_effects + 1, GES_ARGS (element),
|
||||
min_prio + priv->nb_effects);
|
||||
|
@ -646,7 +760,7 @@ _add_child (GESContainer * container, GESTimelineElement * element)
|
|||
/* The height has already changed (increased by 1) */
|
||||
_compute_height (container);
|
||||
} else {
|
||||
if (GES_IS_BASE_EFFECT (element))
|
||||
if (_IS_TOP_EFFECT (element))
|
||||
GST_WARNING_OBJECT (container, "Cannot add the effect %" GES_FORMAT
|
||||
" because it is not a core element created by the clip itself "
|
||||
"and the %s class does not allow for adding extra effects",
|
||||
|
@ -674,7 +788,7 @@ _remove_child (GESContainer * container, GESTimelineElement * element)
|
|||
GESClipPrivate *priv = GES_CLIP (container)->priv;
|
||||
|
||||
/* NOTE: notifies are currently frozen by ges_container_add */
|
||||
if (!_IS_CORE_CHILD (element) && GES_IS_BASE_EFFECT (element)) {
|
||||
if (_IS_TOP_EFFECT (element)) {
|
||||
GList *tmp;
|
||||
GST_DEBUG_OBJECT (container, "Resyncing effects priority.");
|
||||
|
||||
|
@ -701,13 +815,13 @@ _remove_child (GESContainer * container, GESTimelineElement * element)
|
|||
static void
|
||||
_child_added (GESContainer * container, GESTimelineElement * element)
|
||||
{
|
||||
g_signal_connect (G_OBJECT (element), "notify::priority",
|
||||
g_signal_connect (element, "notify::priority",
|
||||
G_CALLBACK (_child_priority_changed_cb), container);
|
||||
g_signal_connect (G_OBJECT (element), "notify::in-point",
|
||||
g_signal_connect (element, "notify::in-point",
|
||||
G_CALLBACK (_child_inpoint_changed_cb), container);
|
||||
g_signal_connect (G_OBJECT (element), "notify::max-duration",
|
||||
g_signal_connect (element, "notify::max-duration",
|
||||
G_CALLBACK (_child_max_duration_changed_cb), container);
|
||||
g_signal_connect (G_OBJECT (element), "notify::has-internal-source",
|
||||
g_signal_connect (element, "notify::has-internal-source",
|
||||
G_CALLBACK (_child_has_internal_source_changed_cb), container);
|
||||
|
||||
_child_priority_changed_cb (element, NULL, container);
|
||||
|
@ -738,12 +852,24 @@ add_clip_to_list (gpointer key, gpointer clip, GList ** list)
|
|||
*list = g_list_prepend (*list, gst_object_ref (clip));
|
||||
}
|
||||
|
||||
/* NOTE: Since this does not change the track of @child, this should
|
||||
* only be called if it is guaranteed that neither @from_clip nor @to_clip
|
||||
* will not break the track rules:
|
||||
* + no more than one core child per track
|
||||
* + every non-core child must be in the same track as a core child
|
||||
*/
|
||||
static void
|
||||
_transfer_child (GESClip * from_clip, GESClip * to_clip,
|
||||
GESTrackElement * child)
|
||||
{
|
||||
GESTimeline *timeline = GES_TIMELINE_ELEMENT_TIMELINE (to_clip);
|
||||
|
||||
/* We need to bump the refcount to avoid the object to be destroyed */
|
||||
gst_object_ref (child);
|
||||
|
||||
/* don't want to change tracks */
|
||||
ges_timeline_set_moving_track_elements (timeline, TRUE);
|
||||
|
||||
ges_container_remove (GES_CONTAINER (from_clip),
|
||||
GES_TIMELINE_ELEMENT (child));
|
||||
|
||||
|
@ -752,7 +878,11 @@ _transfer_child (GESClip * from_clip, GESClip * to_clip,
|
|||
ges_track_element_add_creator (child, to_clip);
|
||||
}
|
||||
|
||||
to_clip->priv->allow_any_track = TRUE;
|
||||
ges_container_add (GES_CONTAINER (to_clip), GES_TIMELINE_ELEMENT (child));
|
||||
to_clip->priv->allow_any_track = FALSE;
|
||||
ges_timeline_set_moving_track_elements (timeline, FALSE);
|
||||
|
||||
gst_object_unref (child);
|
||||
}
|
||||
|
||||
|
@ -982,10 +1112,87 @@ _group (GList * containers)
|
|||
done:
|
||||
if (tracks)
|
||||
g_free (tracks);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
ges_clip_empty_from_track (GESClip * clip, GESTrack * track)
|
||||
{
|
||||
GList *tmp;
|
||||
if (track == NULL)
|
||||
return;
|
||||
/* allow us to remove in any order */
|
||||
clip->priv->allow_any_track = TRUE;
|
||||
|
||||
for (tmp = GES_CONTAINER_CHILDREN (clip); tmp; tmp = tmp->next) {
|
||||
GESTrackElement *child = tmp->data;
|
||||
if (ges_track_element_get_track (child) == track) {
|
||||
if (!ges_track_remove_element (track, child))
|
||||
GST_ERROR_OBJECT (clip, "Failed to remove child %" GES_FORMAT
|
||||
" from the track %" GST_PTR_FORMAT, GES_ARGS (child), track);
|
||||
}
|
||||
}
|
||||
clip->priv->allow_any_track = FALSE;
|
||||
}
|
||||
|
||||
static GESTrackElement *
|
||||
_copy_track_element_to (GESTrackElement * orig, GESClip * to_clip,
|
||||
GstClockTime position)
|
||||
{
|
||||
GESTrackElement *copy;
|
||||
GESTimelineElement *el_copy, *el_orig;
|
||||
|
||||
/* NOTE: we do not deep copy the track element, we instead call
|
||||
* ges_track_element_copy_properties explicitly, which is the
|
||||
* deep_copy for the GESTrackElementClass. */
|
||||
el_orig = GES_TIMELINE_ELEMENT (orig);
|
||||
el_copy = ges_timeline_element_copy (el_orig, FALSE);
|
||||
|
||||
if (el_copy == NULL)
|
||||
return NULL;
|
||||
|
||||
copy = GES_TRACK_ELEMENT (el_copy);
|
||||
ges_track_element_copy_properties (el_orig, el_copy);
|
||||
/* NOTE: control bindings that are not registered in GES are not
|
||||
* handled */
|
||||
ges_track_element_copy_bindings (orig, copy, position);
|
||||
|
||||
if (_IS_CORE_CHILD (orig))
|
||||
ges_track_element_add_creator (copy, to_clip);
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
static GESTrackElement *
|
||||
ges_clip_copy_track_element_into (GESClip * clip, GESTrackElement * orig,
|
||||
GstClockTime position)
|
||||
{
|
||||
GESTimeline *timeline = GES_TIMELINE_ELEMENT_TIMELINE (clip);
|
||||
GESTrackElement *copy;
|
||||
|
||||
copy = _copy_track_element_to (orig, clip, position);
|
||||
if (copy == NULL) {
|
||||
GST_ERROR_OBJECT (clip, "Failed to create a copy of the "
|
||||
"element %" GES_FORMAT " for the clip", GES_ARGS (orig));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gst_object_ref (copy);
|
||||
ges_timeline_set_moving_track_elements (timeline, TRUE);
|
||||
if (!ges_container_add (GES_CONTAINER (clip), GES_TIMELINE_ELEMENT (copy))) {
|
||||
GST_ERROR_OBJECT (clip, "Failed to add the copied child track "
|
||||
"element %" GES_FORMAT " to the clip", GES_ARGS (copy));
|
||||
ges_timeline_set_moving_track_elements (timeline, FALSE);
|
||||
gst_object_unref (copy);
|
||||
return NULL;
|
||||
}
|
||||
ges_timeline_set_moving_track_elements (timeline, FALSE);
|
||||
/* now owned by the clip */
|
||||
gst_object_unref (copy);
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
static void
|
||||
_deep_copy (GESTimelineElement * element, GESTimelineElement * copy)
|
||||
{
|
||||
|
@ -993,20 +1200,26 @@ _deep_copy (GESTimelineElement * element, GESTimelineElement * copy)
|
|||
GESClip *self = GES_CLIP (element), *ccopy = GES_CLIP (copy);
|
||||
GESTrackElement *el, *el_copy;
|
||||
|
||||
if (!self->priv->layer)
|
||||
return;
|
||||
|
||||
/* NOTE: this should only be called on a newly created @copy, so
|
||||
* its copied_track_elements, and copied_layer, should be free to set
|
||||
* without disposing of the previous values */
|
||||
for (tmp = GES_CONTAINER_CHILDREN (element); tmp; tmp = tmp->next) {
|
||||
el = GES_TRACK_ELEMENT (tmp->data);
|
||||
/* copies the children properties */
|
||||
el_copy = GES_TRACK_ELEMENT (ges_timeline_element_copy (tmp->data, TRUE));
|
||||
|
||||
/* any element created by self, will have its copy considered created
|
||||
* by self's copy */
|
||||
if (_IS_CORE_CHILD (el))
|
||||
ges_track_element_add_creator (el_copy, ccopy);
|
||||
el_copy = _copy_track_element_to (el, ccopy, GST_CLOCK_TIME_NONE);
|
||||
if (!el_copy) {
|
||||
GST_ERROR_OBJECT (element, "Failed to copy the track element %"
|
||||
GES_FORMAT " for pasting", GES_ARGS (el));
|
||||
continue;
|
||||
}
|
||||
/* owned by copied_track_elements */
|
||||
gst_object_ref_sink (el_copy);
|
||||
|
||||
ges_track_element_copy_bindings (el, el_copy, GST_CLOCK_TIME_NONE);
|
||||
/* _add_child will add core elements at the lowest priority and new
|
||||
* non-core effects at the lowest effect priority, so we need to add
|
||||
* the highest priority children first to preserve the effect order.
|
||||
* The clip's children are already ordered by highest priority first.
|
||||
* So we order copied_track_elements in the same way */
|
||||
ccopy->priv->copied_track_elements =
|
||||
g_list_append (ccopy->priv->copied_track_elements, el_copy);
|
||||
}
|
||||
|
@ -1022,48 +1235,12 @@ _paste (GESTimelineElement * element, GESTimelineElement * ref,
|
|||
GESClip *self = GES_CLIP (element);
|
||||
GESClip *nclip = GES_CLIP (ges_timeline_element_copy (element, FALSE));
|
||||
|
||||
if (self->priv->copied_layer)
|
||||
nclip->priv->copied_layer = g_object_ref (self->priv->copied_layer);
|
||||
ges_timeline_element_set_start (GES_TIMELINE_ELEMENT (nclip), paste_position);
|
||||
|
||||
for (tmp = self->priv->copied_track_elements; tmp; tmp = tmp->next) {
|
||||
GESTrackElement *new_trackelement, *trackelement =
|
||||
GES_TRACK_ELEMENT (tmp->data);
|
||||
/* paste in order of priority (highest first) */
|
||||
for (tmp = self->priv->copied_track_elements; tmp; tmp = tmp->next)
|
||||
ges_clip_copy_track_element_into (nclip, tmp->data, GST_CLOCK_TIME_NONE);
|
||||
|
||||
/* NOTE: we do not deep copy the track element, we instead call
|
||||
* ges_track_element_copy_properties explicitly, which is the
|
||||
* deep_copy for the GESTrackElementClass. */
|
||||
new_trackelement =
|
||||
GES_TRACK_ELEMENT (ges_timeline_element_copy (GES_TIMELINE_ELEMENT
|
||||
(trackelement), FALSE));
|
||||
if (new_trackelement == NULL) {
|
||||
GST_WARNING_OBJECT (trackelement, "Could not create a copy");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_IS_CORE_CHILD (trackelement))
|
||||
ges_track_element_add_creator (new_trackelement, nclip);
|
||||
|
||||
gst_object_ref_sink (new_trackelement);
|
||||
if (!ges_container_add (GES_CONTAINER (nclip),
|
||||
GES_TIMELINE_ELEMENT (new_trackelement))) {
|
||||
GST_ERROR_OBJECT (self, "Failed add the copied child track element %"
|
||||
GES_FORMAT " to the copy %" GES_FORMAT,
|
||||
GES_ARGS (new_trackelement), GES_ARGS (nclip));
|
||||
gst_object_unref (new_trackelement);
|
||||
continue;
|
||||
}
|
||||
|
||||
ges_track_element_copy_properties (GES_TIMELINE_ELEMENT (trackelement),
|
||||
GES_TIMELINE_ELEMENT (new_trackelement));
|
||||
|
||||
ges_track_element_copy_bindings (trackelement, new_trackelement,
|
||||
GST_CLOCK_TIME_NONE);
|
||||
gst_object_unref (new_trackelement);
|
||||
}
|
||||
|
||||
/* FIXME: should we bypass the select-tracks-for-object signal when
|
||||
* copying and pasting? */
|
||||
if (self->priv->copied_layer) {
|
||||
if (!ges_layer_add_clip (self->priv->copied_layer, nclip)) {
|
||||
GST_INFO ("%" GES_FORMAT " could not be pasted to %" GST_TIME_FORMAT,
|
||||
|
@ -1071,9 +1248,12 @@ _paste (GESTimelineElement * element, GESTimelineElement * ref,
|
|||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* NOTE: self should not be used and be freed after this call, so we can
|
||||
* leave the freeing of copied_layer and copied_track_elements to the
|
||||
* dispose method */
|
||||
|
||||
return GES_TIMELINE_ELEMENT (nclip);
|
||||
}
|
||||
|
||||
|
@ -1138,7 +1318,7 @@ ges_clip_dispose (GObject * object)
|
|||
{
|
||||
GESClip *self = GES_CLIP (object);
|
||||
|
||||
g_list_free_full (self->priv->copied_track_elements, g_object_unref);
|
||||
g_list_free_full (self->priv->copied_track_elements, gst_object_unref);
|
||||
self->priv->copied_track_elements = NULL;
|
||||
g_clear_object (&self->priv->copied_layer);
|
||||
|
||||
|
@ -1218,15 +1398,7 @@ ges_clip_class_init (GESClipClass * klass)
|
|||
static void
|
||||
ges_clip_init (GESClip * self)
|
||||
{
|
||||
GESClipPrivate *priv;
|
||||
priv = self->priv = ges_clip_get_instance_private (self);
|
||||
priv->layer = NULL;
|
||||
priv->nb_effects = 0;
|
||||
priv->prevent_priority_offset_update = FALSE;
|
||||
priv->prevent_resort = FALSE;
|
||||
priv->updating_max_duration = FALSE;
|
||||
priv->prevent_max_duration_update = FALSE;
|
||||
priv->setting_inpoint = FALSE;
|
||||
self->priv = ges_clip_get_instance_private (self);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1236,9 +1408,6 @@ ges_clip_init (GESClip * self)
|
|||
*
|
||||
* Creates the core #GESTrackElement of the clip, of the given track type.
|
||||
*
|
||||
* Note, unlike ges_clip_create_track_elements(), this does not add the
|
||||
* created track element to the clip or set their timings.
|
||||
*
|
||||
* Returns: (transfer floating) (nullable): The element created
|
||||
* by @clip, or %NULL if @clip can not provide a track element for the
|
||||
* given @type or an error occurred.
|
||||
|
@ -1277,7 +1446,7 @@ ges_clip_create_track_element (GESClip * clip, GESTrackType type)
|
|||
* @type: The track-type to create elements for
|
||||
*
|
||||
* Creates the core #GESTrackElement-s of the clip, of the given track
|
||||
* type, and adds them to the clip.
|
||||
* type.
|
||||
*
|
||||
* Returns: (transfer container) (element-type GESTrackElement): A list of
|
||||
* the #GESTrackElement-s created by @clip for the given @type, or %NULL
|
||||
|
@ -1287,14 +1456,15 @@ ges_clip_create_track_element (GESClip * clip, GESTrackType type)
|
|||
GList *
|
||||
ges_clip_create_track_elements (GESClip * clip, GESTrackType type)
|
||||
{
|
||||
/* add_list holds a ref to its elements to keep them alive
|
||||
* result does not */
|
||||
GList *result = NULL, *add_list = NULL, *tmp, *children;
|
||||
GList *tmp, *ret;
|
||||
GESClipClass *klass;
|
||||
gboolean readding_effects_only = TRUE;
|
||||
gboolean already_created = FALSE;
|
||||
|
||||
g_return_val_if_fail (GES_IS_CLIP (clip), NULL);
|
||||
|
||||
if ((clip->priv->supportedformats & type) == 0)
|
||||
return NULL;
|
||||
|
||||
klass = GES_CLIP_GET_CLASS (clip);
|
||||
|
||||
if (!(klass->create_track_elements)) {
|
||||
|
@ -1304,71 +1474,24 @@ ges_clip_create_track_elements (GESClip * clip, GESTrackType type)
|
|||
|
||||
GST_DEBUG_OBJECT (clip, "Creating TrackElements for type: %s",
|
||||
ges_track_type_name (type));
|
||||
children = ges_container_get_children (GES_CONTAINER (clip), TRUE);
|
||||
for (tmp = children; tmp; tmp = tmp->next) {
|
||||
for (tmp = GES_CONTAINER_CHILDREN (clip); tmp; tmp = tmp->next) {
|
||||
GESTrackElement *child = GES_TRACK_ELEMENT (tmp->data);
|
||||
|
||||
if (!ges_track_element_get_track (child)
|
||||
if (_IS_CORE_CHILD (child)
|
||||
&& ges_track_element_get_track_type (child) & type) {
|
||||
GST_DEBUG_OBJECT (clip, "Removing for reusage: %" GST_PTR_FORMAT, child);
|
||||
add_list = g_list_append (add_list, gst_object_ref (child));
|
||||
ges_container_remove (GES_CONTAINER (clip), tmp->data);
|
||||
if (_IS_CORE_CHILD (child))
|
||||
readding_effects_only = FALSE;
|
||||
/* assume the core track elements have all been created if we find
|
||||
* at least one core child with the same type */
|
||||
already_created = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
g_list_free_full (children, gst_object_unref);
|
||||
if (already_created)
|
||||
return NULL;
|
||||
|
||||
/* FIXME: we need something smarter to determine whether we should
|
||||
* create the track elements.
|
||||
* Currently, if the clip contains at least one element with a matching
|
||||
* track-type, not in a track and not a GESBaseEffect, we will not
|
||||
* recreate the track elements! But this is not a reliable indicator.
|
||||
*
|
||||
* For example, consider a uri clip that creates two audio track
|
||||
* elements: El_A and El_B. First, we add the clip to a timeline that
|
||||
* only has a single track: Track_A, and we connect to the timeline's
|
||||
* ::select-tracks-for-object signal to only allow El_A to end up in
|
||||
* Track_A. As such, whilst both El_A and El_B are initially created,
|
||||
* El_B will eventually be removed from the clip since it has no track
|
||||
* (see clip_track_element_added_cb in ges-timeline.c). As such, we now
|
||||
* have a clip that only contains El_A.
|
||||
* Next, we remove Track_A from the timeline. El_A will remain a child
|
||||
* of the clip, but now has its track unset.
|
||||
* Next, we add Track_B to the timeline, and we connect to the
|
||||
* timeline's ::select-tracks-for-object signal to only allow El_B to
|
||||
* end up in Track_B.
|
||||
*
|
||||
* However, since the clip contains an audio track element, that is not
|
||||
* an effect and has no track set: El_A. Therefore, the
|
||||
* create_track_elements method below will not be called, so we will not
|
||||
* have an El_B created for Track_B!
|
||||
*
|
||||
* Moreover, even if we did recreate the track elements, we would be
|
||||
* creating El_A again! We could destroy and recreate El_A instead, or
|
||||
* we would need a way to determine exactly which elements need to be
|
||||
* recreated.
|
||||
*/
|
||||
if (readding_effects_only) {
|
||||
GList *track_elements = klass->create_track_elements (clip, type);
|
||||
for (tmp = track_elements; tmp; tmp = tmp->next) {
|
||||
gst_object_ref_sink (tmp->data);
|
||||
ges_track_element_add_creator (tmp->data, clip);
|
||||
}
|
||||
add_list = g_list_concat (track_elements, add_list);
|
||||
}
|
||||
|
||||
for (tmp = add_list; tmp; tmp = tmp->next) {
|
||||
GESTimelineElement *el = GES_TIMELINE_ELEMENT (tmp->data);
|
||||
if (ges_container_add (GES_CONTAINER (clip), el))
|
||||
result = g_list_append (result, el);
|
||||
else
|
||||
GST_ERROR_OBJECT (clip, "Failed add the track element %"
|
||||
GES_FORMAT " to the clip", GES_ARGS (el));
|
||||
}
|
||||
g_list_free_full (add_list, gst_object_unref);
|
||||
|
||||
return result;
|
||||
ret = klass->create_track_elements (clip, type);
|
||||
for (tmp = ret; tmp; tmp = tmp->next)
|
||||
ges_track_element_add_creator (tmp->data, clip);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1605,7 +1728,7 @@ ges_clip_get_top_effects (GESClip * clip)
|
|||
|
||||
for (tmp = GES_CONTAINER_CHILDREN (clip); tmp; tmp = tmp->next) {
|
||||
child = tmp->data;
|
||||
if (GES_IS_BASE_EFFECT (child) && !_IS_CORE_CHILD (child))
|
||||
if (_IS_TOP_EFFECT (child))
|
||||
ret = g_list_append (ret, gst_object_ref (child));
|
||||
}
|
||||
|
||||
|
@ -1801,14 +1924,20 @@ ges_clip_split (GESClip * clip, guint64 position)
|
|||
GESClip *new_object;
|
||||
GstClockTime start, inpoint, duration, old_duration, new_duration;
|
||||
gdouble media_duration_factor;
|
||||
GESTimelineElement *element;
|
||||
GESTimeline *timeline;
|
||||
GHashTable *track_for_copy;
|
||||
|
||||
g_return_val_if_fail (GES_IS_CLIP (clip), NULL);
|
||||
g_return_val_if_fail (clip->priv->layer, NULL);
|
||||
g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (position), NULL);
|
||||
|
||||
duration = _DURATION (clip);
|
||||
start = _START (clip);
|
||||
inpoint = _INPOINT (clip);
|
||||
element = GES_TIMELINE_ELEMENT (clip);
|
||||
timeline = element->timeline;
|
||||
|
||||
duration = element->duration;
|
||||
start = element->start;
|
||||
inpoint = element->inpoint;
|
||||
|
||||
if (position >= start + duration || position <= start) {
|
||||
GST_WARNING_OBJECT (clip, "Can not split %" GST_TIME_FORMAT
|
||||
|
@ -1817,9 +1946,9 @@ ges_clip_split (GESClip * clip, guint64 position)
|
|||
}
|
||||
|
||||
old_duration = position - start;
|
||||
if (!timeline_tree_can_move_element (timeline_get_tree
|
||||
(GES_TIMELINE_ELEMENT_TIMELINE (clip)), GES_TIMELINE_ELEMENT (clip),
|
||||
GES_TIMELINE_ELEMENT_LAYER_PRIORITY (clip),
|
||||
if (timeline && !timeline_tree_can_move_element (timeline_get_tree
|
||||
(timeline), element,
|
||||
ges_timeline_element_get_layer_priority (element),
|
||||
start, old_duration, NULL)) {
|
||||
GST_WARNING_OBJECT (clip,
|
||||
"Can not split %" GES_FORMAT " at %" GST_TIME_FORMAT
|
||||
|
@ -1829,10 +1958,10 @@ ges_clip_split (GESClip * clip, guint64 position)
|
|||
}
|
||||
|
||||
new_duration = duration + start - position;
|
||||
if (!timeline_tree_can_move_element (timeline_get_tree
|
||||
(GES_TIMELINE_ELEMENT_TIMELINE (clip)), GES_TIMELINE_ELEMENT (clip),
|
||||
GES_TIMELINE_ELEMENT_LAYER_PRIORITY (clip), position, new_duration,
|
||||
NULL)) {
|
||||
if (timeline && !timeline_tree_can_move_element (timeline_get_tree
|
||||
(timeline), element,
|
||||
ges_timeline_element_get_layer_priority (element),
|
||||
position, new_duration, NULL)) {
|
||||
GST_WARNING_OBJECT (clip,
|
||||
"Can not split %" GES_FORMAT " at %" GST_TIME_FORMAT
|
||||
" as timeline would end up in an illegal" " state.", GES_ARGS (clip),
|
||||
|
@ -1844,64 +1973,60 @@ ges_clip_split (GESClip * clip, guint64 position)
|
|||
GST_TIME_ARGS (position));
|
||||
|
||||
/* Create the new Clip */
|
||||
new_object = GES_CLIP (ges_timeline_element_copy (GES_TIMELINE_ELEMENT (clip),
|
||||
FALSE));
|
||||
new_object = GES_CLIP (ges_timeline_element_copy (element, FALSE));
|
||||
|
||||
GST_DEBUG_OBJECT (new_object, "New 'splitted' clip");
|
||||
/* Set new timing properties on the Clip */
|
||||
media_duration_factor =
|
||||
ges_timeline_element_get_media_duration_factor (GES_TIMELINE_ELEMENT
|
||||
(clip));
|
||||
ges_timeline_element_get_media_duration_factor (element);
|
||||
_set_start0 (GES_TIMELINE_ELEMENT (new_object), position);
|
||||
_set_inpoint0 (GES_TIMELINE_ELEMENT (new_object),
|
||||
inpoint + old_duration * media_duration_factor);
|
||||
_set_duration0 (GES_TIMELINE_ELEMENT (new_object), new_duration);
|
||||
|
||||
_DURATION (clip) = old_duration;
|
||||
g_object_notify (G_OBJECT (clip), "duration");
|
||||
|
||||
/* We do not want the timeline to create again TrackElement-s */
|
||||
ges_clip_set_moving_from_layer (new_object, TRUE);
|
||||
/* adding to the same layer should not fail when moving */
|
||||
ges_layer_add_clip (clip->priv->layer, new_object);
|
||||
ges_clip_set_moving_from_layer (new_object, FALSE);
|
||||
|
||||
/* split binding before duration changes */
|
||||
track_for_copy = g_hash_table_new_full (NULL, NULL,
|
||||
gst_object_unref, gst_object_unref);
|
||||
/* _add_child will add core elements at the lowest priority and new
|
||||
* non-core effects at the lowest effect priority, so we need to add the
|
||||
* highest priority children first to preserve the effect order. The
|
||||
* clip's children are already ordered by highest priority first. */
|
||||
for (tmp = GES_CONTAINER_CHILDREN (clip); tmp; tmp = tmp->next) {
|
||||
GESTrackElement *new_trackelement, *trackelement =
|
||||
GES_TRACK_ELEMENT (tmp->data);
|
||||
|
||||
new_trackelement =
|
||||
GES_TRACK_ELEMENT (ges_timeline_element_copy (GES_TIMELINE_ELEMENT
|
||||
(trackelement), FALSE));
|
||||
if (new_trackelement == NULL) {
|
||||
GST_WARNING_OBJECT (trackelement, "Could not create a copy");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_IS_CORE_CHILD (trackelement))
|
||||
ges_track_element_add_creator (new_trackelement, new_object);
|
||||
|
||||
/* FIXME: in-point for non-core track elements should be shifted by
|
||||
* the split (adding them to the new clip will not set their in-point)
|
||||
* Handle this once generic time effects are supported in splitting */
|
||||
ges_container_add (GES_CONTAINER (new_object),
|
||||
GES_TIMELINE_ELEMENT (new_trackelement));
|
||||
ges_track_element_copy_properties (GES_TIMELINE_ELEMENT (trackelement),
|
||||
GES_TIMELINE_ELEMENT (new_trackelement));
|
||||
|
||||
GESTrackElement *copy, *orig = tmp->data;
|
||||
GESTrack *track = ges_track_element_get_track (orig);
|
||||
/* FIXME: is position - start + inpoint always the correct splitting
|
||||
* point for control bindings? What coordinate system are control
|
||||
* bindings given in? */
|
||||
/* NOTE: control bindings that are not registered in GES are not
|
||||
* handled */
|
||||
ges_track_element_copy_bindings (trackelement, new_trackelement,
|
||||
copy = ges_clip_copy_track_element_into (new_object, orig,
|
||||
position - start + inpoint);
|
||||
if (copy && track)
|
||||
g_hash_table_insert (track_for_copy, gst_object_ref (copy),
|
||||
gst_object_ref (track));
|
||||
}
|
||||
|
||||
/* FIXME: The below leads to a *second* notify signal for duration */
|
||||
ELEMENT_SET_FLAG (clip, GES_TIMELINE_ELEMENT_SET_SIMPLE);
|
||||
_DURATION (clip) = duration;
|
||||
_set_duration0 (GES_TIMELINE_ELEMENT (clip), old_duration);
|
||||
ELEMENT_UNSET_FLAG (clip, GES_TIMELINE_ELEMENT_SET_SIMPLE);
|
||||
g_object_notify (G_OBJECT (clip), "duration");
|
||||
|
||||
/* add to the track after the duration change so we don't overlap! */
|
||||
for (tmp = GES_CONTAINER_CHILDREN (new_object); tmp; tmp = tmp->next) {
|
||||
GESTrackElement *copy = tmp->data;
|
||||
GESTrack *track = g_hash_table_lookup (track_for_copy, copy);
|
||||
if (track) {
|
||||
new_object->priv->allow_any_track = TRUE;
|
||||
ges_track_add_element (track, copy);
|
||||
new_object->priv->allow_any_track = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
g_hash_table_unref (track_for_copy);
|
||||
|
||||
return new_object;
|
||||
}
|
||||
|
@ -2133,3 +2258,117 @@ ges_clip_get_timeline_time_from_source_frame (GESClip * clip,
|
|||
|
||||
return GST_CLOCK_DIFF (inpoint_diff, _START (clip));
|
||||
}
|
||||
|
||||
/**
|
||||
* ges_clip_add_child_to_track:
|
||||
* @clip: A #GESClip
|
||||
* @child: A child of @clip
|
||||
* @track: The track to add @child to
|
||||
* @err: Return location for an error
|
||||
*
|
||||
* Adds the track element child of the clip to a specific track.
|
||||
*
|
||||
* If the given child is already in another track, this will create a copy
|
||||
* of the child, add it to the clip, and add this copy to the track.
|
||||
*
|
||||
* You should only call this whilst a clip is part of a #GESTimeline, and
|
||||
* for tracks that are in the same timeline.
|
||||
*
|
||||
* This method is an alternative to using the
|
||||
* #GESTimeline::select-tracks-for-object signal, but can be used to
|
||||
* complement it when, say, you wish to copy a clip's children from one
|
||||
* track into a new one.
|
||||
*
|
||||
* When the child is a core child, it must be added to a track that does
|
||||
* not already contain another core child of the same clip. If it is not a
|
||||
* core child (an additional effect), then it must be added to a track
|
||||
* that already contains one of the core children of the same clip.
|
||||
*
|
||||
* This method can also fail if the adding the track element to the track
|
||||
* would break a configuration rule of the corresponding #GESTimeline,
|
||||
* such as causing three sources to overlap at a single time, or causing
|
||||
* a source to completely overlap another in the same track.
|
||||
*
|
||||
* Note that @err will not always be set upon failure.
|
||||
*
|
||||
* Returns: (transfer none): The element that was added to @track, either
|
||||
* @child or a copy of child, or %NULL if the element could not be added.
|
||||
*/
|
||||
GESTrackElement *
|
||||
ges_clip_add_child_to_track (GESClip * clip, GESTrackElement * child,
|
||||
GESTrack * track, GError ** err)
|
||||
{
|
||||
GESTimeline *timeline;
|
||||
GESTrackElement *el;
|
||||
GESTrack *current_track;
|
||||
|
||||
g_return_val_if_fail (GES_IS_CLIP (clip), NULL);
|
||||
g_return_val_if_fail (GES_IS_TRACK_ELEMENT (child), NULL);
|
||||
g_return_val_if_fail (GES_IS_TRACK (track), NULL);
|
||||
g_return_val_if_fail (!err || !*err, NULL);
|
||||
|
||||
timeline = GES_TIMELINE_ELEMENT_TIMELINE (clip);
|
||||
|
||||
if (!g_list_find (GES_CONTAINER_CHILDREN (clip), child)) {
|
||||
GST_WARNING_OBJECT (clip, "The track element %" GES_FORMAT " is not "
|
||||
"a child of the clip", GES_ARGS (child));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!timeline) {
|
||||
GST_WARNING_OBJECT (clip, "Cannot add children to tracks unless "
|
||||
"the clip is part of a timeline");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (timeline != ges_track_get_timeline (track)) {
|
||||
GST_WARNING_OBJECT (clip, "Cannot add the children to the track %"
|
||||
GST_PTR_FORMAT " because its timeline is %" GST_PTR_FORMAT
|
||||
" rather than that of the clip %" GST_PTR_FORMAT,
|
||||
track, ges_track_get_timeline (track), timeline);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
current_track = ges_track_element_get_track (child);
|
||||
|
||||
if (current_track == track) {
|
||||
GST_WARNING_OBJECT (clip, "Child %s" GES_FORMAT " is already in the "
|
||||
"track %" GST_PTR_FORMAT, GES_ARGS (child), track);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* copy if the element is already in a track */
|
||||
if (current_track) {
|
||||
el = ges_clip_copy_track_element_into (clip, child, GST_CLOCK_TIME_NONE);
|
||||
if (!el) {
|
||||
GST_ERROR_OBJECT (clip, "Could not add a copy of the track element %"
|
||||
GES_FORMAT " to the clip so cannot add it to the track %"
|
||||
GST_PTR_FORMAT, GES_ARGS (child), track);
|
||||
return NULL;
|
||||
}
|
||||
if (_IS_TOP_EFFECT (child)) {
|
||||
/* add at next lowest priority */
|
||||
ges_clip_set_top_effect_index (clip, GES_BASE_EFFECT (el),
|
||||
ges_clip_get_top_effect_index (clip, GES_BASE_EFFECT (child)) + 1);
|
||||
}
|
||||
} else {
|
||||
el = child;
|
||||
}
|
||||
|
||||
/* FIXME: set error if can not be added to track:
|
||||
* Either breaks the track rules for the clip, or the timeline
|
||||
* configuration rules */
|
||||
if (!ges_track_add_element (track, el)) {
|
||||
GST_WARNING_OBJECT (clip, "Could not add the track element %"
|
||||
GES_FORMAT " to the track %" GST_PTR_FORMAT, GES_ARGS (el), track);
|
||||
if (el != child)
|
||||
ges_container_remove (GES_CONTAINER (clip), GES_TIMELINE_ELEMENT (el));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (GES_IS_SOURCE (el))
|
||||
timeline_tree_create_transitions (timeline_get_tree (timeline),
|
||||
ges_timeline_find_auto_transition);
|
||||
|
||||
return el;
|
||||
}
|
||||
|
|
|
@ -155,6 +155,9 @@ GES_API
|
|||
GList * ges_clip_find_track_elements (GESClip * clip, GESTrack * track,
|
||||
GESTrackType track_type, GType type);
|
||||
|
||||
GES_API
|
||||
GESTrackElement * ges_clip_add_child_to_track (GESClip * clip, GESTrackElement * child, GESTrack * track, GError **err);
|
||||
|
||||
/****************************************************
|
||||
* Layer *
|
||||
****************************************************/
|
||||
|
|
|
@ -83,6 +83,9 @@ GstDebugCategory * _ges_debug (void);
|
|||
#define GES_FORMAT GES_TIMELINE_ELEMENT_FORMAT
|
||||
#define GES_ARGS GES_TIMELINE_ELEMENT_ARGS
|
||||
|
||||
#define GES_TRACK_ELEMENT_IS_CORE(child) \
|
||||
(ges_track_element_get_creators (GES_TRACK_ELEMENT (child)) != NULL)
|
||||
|
||||
#define SUPRESS_UNUSED_WARNING(a) (void)a
|
||||
|
||||
G_GNUC_INTERNAL gboolean
|
||||
|
@ -156,6 +159,14 @@ timeline_create_transitions (GESTimeline * timeline, GESTrackElement * track_ele
|
|||
|
||||
G_GNUC_INTERNAL void timeline_get_framerate(GESTimeline *self, gint *fps_n,
|
||||
gint *fps_d);
|
||||
G_GNUC_INTERNAL void
|
||||
ges_timeline_set_moving_track_elements (GESTimeline * timeline, gboolean moving);
|
||||
|
||||
G_GNUC_INTERNAL gboolean
|
||||
ges_timeline_add_clip (GESTimeline * timeline, GESClip * clip);
|
||||
|
||||
G_GNUC_INTERNAL void
|
||||
ges_timeline_remove_clip (GESTimeline * timeline, GESClip * clip);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
void
|
||||
|
@ -406,6 +417,8 @@ G_GNUC_INTERNAL void ges_clip_set_moving_from_layer (GESClip *clip
|
|||
G_GNUC_INTERNAL GESTrackElement* ges_clip_create_track_element (GESClip *clip, GESTrackType type);
|
||||
G_GNUC_INTERNAL GList* ges_clip_create_track_elements (GESClip *clip, GESTrackType type);
|
||||
G_GNUC_INTERNAL gboolean ges_clip_can_set_inpoint_of_child (GESClip * clip, GESTimelineElement * child, GstClockTime inpoint);
|
||||
G_GNUC_INTERNAL gboolean ges_clip_can_set_track_of_child (GESClip * clip, GESTrackElement * child, GESTrack * tack);
|
||||
G_GNUC_INTERNAL void ges_clip_empty_from_track (GESClip * clip, GESTrack * track);
|
||||
|
||||
/****************************************************
|
||||
* GESLayer *
|
||||
|
|
|
@ -482,6 +482,7 @@ ges_layer_remove_clip_internal (GESLayer * layer, GESClip * clip,
|
|||
{
|
||||
GESLayer *current_layer;
|
||||
GList *tmp;
|
||||
GESTimeline *timeline = layer->timeline;
|
||||
|
||||
GST_DEBUG ("layer:%p, clip:%p", layer, clip);
|
||||
|
||||
|
@ -507,8 +508,10 @@ ges_layer_remove_clip_internal (GESLayer * layer, GESClip * clip,
|
|||
/* inform the clip it's no longer in a layer */
|
||||
ges_clip_set_layer (clip, NULL);
|
||||
/* so neither in a timeline */
|
||||
if (layer->timeline)
|
||||
if (timeline) {
|
||||
ges_timeline_remove_clip (timeline, clip);
|
||||
ges_timeline_element_set_timeline (GES_TIMELINE_ELEMENT (clip), NULL);
|
||||
}
|
||||
|
||||
for (tmp = GES_CONTAINER_CHILDREN (clip); tmp; tmp = tmp->next)
|
||||
ges_track_element_set_layer_active (tmp->data, TRUE);
|
||||
|
@ -766,25 +769,17 @@ ges_layer_add_clip (GESLayer * layer, GESClip * clip)
|
|||
ges_timeline_element_set_timeline (GES_TIMELINE_ELEMENT (clip),
|
||||
layer->timeline);
|
||||
|
||||
/* emit 'clip-added' */
|
||||
/* FIXME: we are emitting the 'clip-added' signal even though we still
|
||||
* might fail. This is because the timeline uses this signal to create
|
||||
* the auto-transitions etc needed for timeline_tree_can_move_element
|
||||
* below, which checks whether the added clip is in a legal position.
|
||||
* However, we should have a way to check that adding a clip will be
|
||||
* legal **before** we actually add it!
|
||||
* A user connecting to 'clip-added' in such a case would receive a
|
||||
* signal saying that the clip was added, but a return value that says
|
||||
* something else! */
|
||||
/* FIXME: ideally we would only emit if we are going to return TRUE.
|
||||
* However, for backward-compatibility, we ensure the "clip-added"
|
||||
* signal is released before the clip's "child-added" signal, which is
|
||||
* invoked by ges_timeline_add_clip */
|
||||
g_signal_emit (layer, ges_layer_signals[OBJECT_ADDED], 0, clip);
|
||||
|
||||
if (!ELEMENT_FLAG_IS_SET (clip, GES_CLIP_IS_MOVING) && layer->timeline
|
||||
&& !timeline_tree_can_move_element (timeline_get_tree (layer->timeline),
|
||||
GES_TIMELINE_ELEMENT (clip),
|
||||
GES_TIMELINE_ELEMENT_LAYER_PRIORITY (clip),
|
||||
GES_TIMELINE_ELEMENT_START (clip),
|
||||
GES_TIMELINE_ELEMENT_DURATION (clip), NULL)) {
|
||||
GST_INFO_OBJECT (layer, "Clip %" GES_FORMAT, GES_ARGS (clip));
|
||||
if (layer->timeline && !ges_timeline_add_clip (layer->timeline, clip)) {
|
||||
GST_WARNING_OBJECT (layer, "Could not add the clip %" GES_FORMAT
|
||||
" to the timeline %" GST_PTR_FORMAT, GES_ARGS (clip), layer->timeline);
|
||||
/* FIXME: change emit signal to FALSE once we are able to delay the
|
||||
* "clip-added" signal until after ges_timeline_add_clip */
|
||||
ges_layer_remove_clip_internal (layer, clip, TRUE);
|
||||
return FALSE;
|
||||
}
|
||||
|
|
|
@ -409,7 +409,8 @@ ges_timeline_element_class_init (GESTimelineElementClass * klass)
|
|||
*/
|
||||
properties[PROP_TIMELINE] =
|
||||
g_param_spec_object ("timeline", "Timeline",
|
||||
"The timeline the object is in", GES_TYPE_TIMELINE, G_PARAM_READWRITE);
|
||||
"The timeline the object is in", GES_TYPE_TIMELINE,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GESTimelineElement:start:
|
||||
|
@ -980,6 +981,9 @@ ges_timeline_element_set_timeline (GESTimelineElement * self,
|
|||
|
||||
GST_DEBUG_OBJECT (self, "set timeline to %" GST_PTR_FORMAT, timeline);
|
||||
|
||||
if (self->timeline == timeline)
|
||||
return TRUE;
|
||||
|
||||
if (timeline != NULL && G_UNLIKELY (self->timeline != NULL))
|
||||
goto had_timeline;
|
||||
|
||||
|
|
|
@ -152,7 +152,8 @@ struct _GESTimelinePrivate
|
|||
|
||||
/* While we are creating and adding the TrackElements for a clip, we need to
|
||||
* ignore the child-added signal */
|
||||
GESClip *ignore_track_element_added;
|
||||
gboolean track_elements_moving;
|
||||
gboolean track_selection_error;
|
||||
GList *groups;
|
||||
|
||||
guint stream_start_group_id;
|
||||
|
@ -702,8 +703,12 @@ ges_timeline_class_init (GESTimelineClass * klass)
|
|||
* @clip: The clip that @track_element is being added to
|
||||
* @track_element: The element being added
|
||||
*
|
||||
* This will be emitted whenever a new track element is added to a
|
||||
* clip within the timeline.
|
||||
* This will be emitted whenever the timeline needs to determine which
|
||||
* tracks a clip's children should be added to. The track element will
|
||||
* be added to each of the tracks given in the return. If a track
|
||||
* element is selected to go into multiple tracks, it will be copied
|
||||
* into the additional tracks, under the same clip. Note that the copy
|
||||
* will *not* keep its properties or state in sync with the original.
|
||||
*
|
||||
* Connect to this signal once if you wish to control which element
|
||||
* should be added to which track. Doing so will overwrite the default
|
||||
|
@ -711,16 +716,48 @@ ges_timeline_class_init (GESTimelineClass * klass)
|
|||
* #GESTrack:track-type includes the @track_element's
|
||||
* #GESTrackElement:track-type.
|
||||
*
|
||||
* If you wish to use this, you should add all necessary tracks to the
|
||||
* timeline before adding any clips. In particular, this signal is
|
||||
* **not** re-emitted for the existing clips when a new track is added
|
||||
* to the timeline.
|
||||
* Note that under the default track selection, if a clip would produce
|
||||
* multiple core children of the same #GESTrackType, it will choose
|
||||
* one of the core children arbitrarily to place in the corresponding
|
||||
* tracks, with a warning for the other core children that are not
|
||||
* placed in the track. For example, this would happen for a #GESUriClip
|
||||
* that points to a file that contains multiple audio streams. If you
|
||||
* wish to choose the stream, you could connect to this signal, and use,
|
||||
* say, ges_uri_source_asset_get_stream_info() to choose which core
|
||||
* source to add.
|
||||
*
|
||||
* When a clip is first added to a timeline, its core elements will
|
||||
* be created for the current tracks in the timeline if they have not
|
||||
* already been created. Then this will be emitted for each of these
|
||||
* core children to select which tracks, if any, they should be added
|
||||
* to. It will then be called for any non-core children in the clip.
|
||||
*
|
||||
* In addition, if a new track element is ever added to a clip in a
|
||||
* timeline (and it is not already part of a track) this will be emitted
|
||||
* to select which tracks the element should be added to.
|
||||
*
|
||||
* Finally, as a special case, if a track is added to the timeline
|
||||
* *after* it already contains clips, then it will request the creation
|
||||
* of the clips' core elements of the corresponding type, if they have
|
||||
* not already been created, and this signal will be emitted for each of
|
||||
* these newly created elements. In addition, this will also be released
|
||||
* for all other track elements in the timeline's clips that have not
|
||||
* yet been assigned a track. However, in this final case, the timeline
|
||||
* will only check whether the newly added track appears in the track
|
||||
* list. If it does appear, the track element will be added to the newly
|
||||
* added track. All other tracks in the returned track list are ignored.
|
||||
*
|
||||
* In this latter case, track elements that are already part of a track
|
||||
* will not be asked if they want to be copied into the new track. If
|
||||
* you wish to do this, you can use ges_clip_add_child_to_track().
|
||||
*
|
||||
* Note that the returned #GPtrArray should own a new reference to each
|
||||
* of its contained #GESTrack. The timeline will set the #GDestroyNotify
|
||||
* free function on the #GPtrArray to dereference the elements.
|
||||
*
|
||||
* Returns: (transfer full) (element-type GESTrack): An array of
|
||||
* #GESTrack-s that @track_element should be added to. If this contains
|
||||
* more than one track, a copy of @track_element will be added to the
|
||||
* other tracks. If this is empty, @track_element will also be removed
|
||||
* from @clip.
|
||||
* #GESTrack-s that @track_element should be added to, or %NULL to
|
||||
* not add the element to any track.
|
||||
*/
|
||||
ges_timeline_signals[SELECT_TRACKS_FOR_OBJECT] =
|
||||
g_signal_new ("select-tracks-for-object", G_TYPE_FROM_CLASS (klass),
|
||||
|
@ -1297,12 +1334,26 @@ timeline_remove_group (GESTimeline * timeline, GESGroup * group)
|
|||
gst_object_unref (group);
|
||||
}
|
||||
|
||||
static GESTrackElement *
|
||||
_core_in_track (GESTrack * track, GESClip * clip)
|
||||
{
|
||||
GList *tmp;
|
||||
for (tmp = GES_CONTAINER_CHILDREN (clip); tmp; tmp = tmp->next) {
|
||||
if (GES_TRACK_ELEMENT_IS_CORE (tmp->data)
|
||||
&& ges_track_element_get_track (tmp->data) == track) {
|
||||
return tmp->data;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static GPtrArray *
|
||||
select_tracks_for_object_default (GESTimeline * timeline,
|
||||
GESClip * clip, GESTrackElement * tr_object, gpointer user_data)
|
||||
{
|
||||
GPtrArray *result;
|
||||
GList *tmp;
|
||||
GESTrackElement *core;
|
||||
|
||||
result = g_ptr_array_new ();
|
||||
|
||||
|
@ -1311,6 +1362,24 @@ select_tracks_for_object_default (GESTimeline * timeline,
|
|||
GESTrack *track = GES_TRACK (tmp->data);
|
||||
|
||||
if ((track->type & ges_track_element_get_track_type (tr_object))) {
|
||||
if (GES_TRACK_ELEMENT_IS_CORE (tr_object)) {
|
||||
core = _core_in_track (track, clip);
|
||||
if (core) {
|
||||
GST_WARNING_OBJECT (timeline, "The clip '%s' contains multiple "
|
||||
"core elements of the same %s track type. The core child "
|
||||
"'%s' has already been chosen arbitrarily for the track %"
|
||||
GST_PTR_FORMAT ", which means that the other core child "
|
||||
"'%s' of the same type can not be added to the track. "
|
||||
"Consider connecting to "
|
||||
"GESTimeline::select-tracks-for-objects to be able to "
|
||||
"specify which core element should land in the track",
|
||||
GES_TIMELINE_ELEMENT_NAME (clip),
|
||||
ges_track_type_name (track->type),
|
||||
GES_TIMELINE_ELEMENT_NAME (core), track,
|
||||
GES_TIMELINE_ELEMENT_NAME (tr_object));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
gst_object_ref (track);
|
||||
g_ptr_array_add (result, track);
|
||||
}
|
||||
|
@ -1320,34 +1389,273 @@ select_tracks_for_object_default (GESTimeline * timeline,
|
|||
return result;
|
||||
}
|
||||
|
||||
static GPtrArray *
|
||||
_get_selected_tracks (GESTimeline * timeline, GESClip * clip,
|
||||
GESTrackElement * track_element)
|
||||
{
|
||||
guint i, j;
|
||||
GPtrArray *tracks = NULL;
|
||||
|
||||
g_signal_emit (G_OBJECT (timeline),
|
||||
ges_timeline_signals[SELECT_TRACKS_FOR_OBJECT], 0, clip, track_element,
|
||||
&tracks);
|
||||
|
||||
if (tracks == NULL)
|
||||
tracks = g_ptr_array_new ();
|
||||
|
||||
g_ptr_array_set_free_func (tracks, gst_object_unref);
|
||||
|
||||
/* make sure unique */
|
||||
for (i = 0; i < tracks->len;) {
|
||||
GESTrack *track = GES_TRACK (g_ptr_array_index (tracks, i));
|
||||
|
||||
for (j = i + 1; j < tracks->len;) {
|
||||
if (track == g_ptr_array_index (tracks, j)) {
|
||||
GST_WARNING_OBJECT (timeline, "Found the track %" GST_PTR_FORMAT
|
||||
" more than once in the return for select-tracks-for-object "
|
||||
"signal for track element %" GES_FORMAT " in clip %"
|
||||
GES_FORMAT ". Ignoring the extra track", track,
|
||||
GES_ARGS (track_element), GES_ARGS (clip));
|
||||
g_ptr_array_remove_index (tracks, j);
|
||||
/* don't increase index since the next track is in its place */
|
||||
continue;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
|
||||
if (ges_track_get_timeline (track) != timeline) {
|
||||
GST_WARNING_OBJECT (timeline, "The track %" GST_PTR_FORMAT
|
||||
" found in the return for select-tracks-for-object belongs "
|
||||
"to a different timeline %" GST_PTR_FORMAT ". Ignoring this "
|
||||
"track", track, ges_track_get_timeline (track));
|
||||
g_ptr_array_remove_index (tracks, i);
|
||||
/* don't increase index since the next track is in its place */
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return tracks;
|
||||
}
|
||||
|
||||
/* returns TRUE if track element was successfully added to all the
|
||||
* selected tracks */
|
||||
static gboolean
|
||||
_add_track_element_to_tracks (GESTimeline * timeline, GESClip * clip,
|
||||
GESTrackElement * track_element)
|
||||
{
|
||||
guint i;
|
||||
gboolean ret = TRUE;
|
||||
GPtrArray *tracks = _get_selected_tracks (timeline, clip, track_element);
|
||||
|
||||
for (i = 0; i < tracks->len; i++) {
|
||||
GESTrack *track = GES_TRACK (g_ptr_array_index (tracks, i));
|
||||
if (!ges_clip_add_child_to_track (clip, track_element, track, NULL))
|
||||
ret = FALSE;
|
||||
}
|
||||
|
||||
g_ptr_array_unref (tracks);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_try_add_track_element_to_track (GESTimeline * timeline, GESClip * clip,
|
||||
GESTrackElement * track_element, GESTrack * track)
|
||||
{
|
||||
gboolean no_error = TRUE;
|
||||
GPtrArray *tracks = _get_selected_tracks (timeline, clip, track_element);
|
||||
|
||||
/* if we are trying to add the element to a newly added track, then
|
||||
* we only check whether the track list contains the newly added track,
|
||||
* if it does we add the track element to the track, or add a copy if
|
||||
* the track element is already in a track */
|
||||
if (g_ptr_array_find (tracks, track, NULL)) {
|
||||
if (!ges_clip_add_child_to_track (clip, track_element, track, NULL))
|
||||
no_error = FALSE;
|
||||
}
|
||||
|
||||
g_ptr_array_unref (tracks);
|
||||
return no_error;
|
||||
}
|
||||
|
||||
/* accepts NULL */
|
||||
void
|
||||
ges_timeline_set_moving_track_elements (GESTimeline * timeline, gboolean moving)
|
||||
{
|
||||
if (timeline) {
|
||||
LOCK_DYN (timeline);
|
||||
timeline->priv->track_elements_moving = moving;
|
||||
UNLOCK_DYN (timeline);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_set_track_selection_error (GESTimeline * timeline, gboolean error)
|
||||
{
|
||||
LOCK_DYN (timeline);
|
||||
timeline->priv->track_selection_error = error;
|
||||
UNLOCK_DYN (timeline);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_get_track_selection_error (GESTimeline * timeline)
|
||||
{
|
||||
gboolean ret;
|
||||
|
||||
LOCK_DYN (timeline);
|
||||
ret = timeline->priv->track_selection_error;
|
||||
timeline->priv->track_selection_error = FALSE;
|
||||
UNLOCK_DYN (timeline);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
clip_track_element_added_cb (GESClip * clip,
|
||||
GESTrackElement * track_element, GESTimeline * timeline)
|
||||
{
|
||||
gboolean error = FALSE;
|
||||
|
||||
if (timeline->priv->track_elements_moving) {
|
||||
GST_DEBUG_OBJECT (timeline, "Ignoring element added: %" GES_FORMAT
|
||||
" in %" GES_FORMAT, GES_ARGS (track_element), GES_ARGS (clip));
|
||||
return;
|
||||
}
|
||||
|
||||
if (ges_track_element_get_track (track_element) != NULL) {
|
||||
GST_DEBUG_OBJECT (timeline, "Not selecting tracks for %" GES_FORMAT
|
||||
" in %" GES_FORMAT " because it already part of the track %"
|
||||
GST_PTR_FORMAT, GES_ARGS (track_element), GES_ARGS (clip),
|
||||
ges_track_element_get_track (track_element));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_add_track_element_to_tracks (timeline, clip, track_element))
|
||||
error = TRUE;
|
||||
|
||||
if (error)
|
||||
_set_track_selection_error (timeline, TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
clip_track_element_removed_cb (GESClip * clip,
|
||||
GESTrackElement * track_element, GESTimeline * timeline)
|
||||
{
|
||||
GESTrack *track = ges_track_element_get_track (track_element);
|
||||
|
||||
if (timeline->priv->track_elements_moving) {
|
||||
GST_DEBUG_OBJECT (timeline, "Ignoring element removed (%" GST_PTR_FORMAT
|
||||
" in %" GST_PTR_FORMAT, track_element, clip);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (track) {
|
||||
/* if we have non-core elements in the same track, they should be
|
||||
* removed from them to preserve the rule that a non-core can only be
|
||||
* in the same track as a core element from the same clip */
|
||||
if (GES_TRACK_ELEMENT_IS_CORE (track_element))
|
||||
ges_clip_empty_from_track (clip, track);
|
||||
ges_track_remove_element (track, track_element);
|
||||
}
|
||||
}
|
||||
|
||||
/* returns TRUE if no errors in adding to tracks */
|
||||
static gboolean
|
||||
_add_clip_children_to_tracks (GESTimeline * timeline, GESClip * clip,
|
||||
gboolean add_core, GESTrack * new_track, GList * blacklist)
|
||||
{
|
||||
GList *tmp, *children;
|
||||
gboolean no_errors = TRUE;
|
||||
|
||||
/* list of children may change if some are copied into tracks */
|
||||
children = ges_container_get_children (GES_CONTAINER (clip), FALSE);
|
||||
for (tmp = children; tmp; tmp = tmp->next) {
|
||||
GESTrackElement *el = tmp->data;
|
||||
if (GES_TRACK_ELEMENT_IS_CORE (el) != add_core)
|
||||
continue;
|
||||
if (g_list_find (blacklist, el))
|
||||
continue;
|
||||
if (ges_track_element_get_track (el) == NULL) {
|
||||
gboolean res;
|
||||
if (new_track)
|
||||
res = _try_add_track_element_to_track (timeline, clip, el, new_track);
|
||||
else
|
||||
res = _add_track_element_to_tracks (timeline, clip, el);
|
||||
if (!res)
|
||||
no_errors = FALSE;
|
||||
}
|
||||
}
|
||||
g_list_free_full (children, gst_object_unref);
|
||||
|
||||
return no_errors;
|
||||
}
|
||||
|
||||
/* returns TRUE if no errors in adding to tracks */
|
||||
static gboolean
|
||||
add_object_to_tracks (GESTimeline * timeline, GESClip * clip, GESTrack * track)
|
||||
{
|
||||
gint i;
|
||||
GList *tmp, *list;
|
||||
GESTrackType types, visited_type = GES_TRACK_TYPE_UNKNOWN;
|
||||
GList *tracks, *tmp, *list, *created, *just_added = NULL;
|
||||
gboolean no_errors = TRUE;
|
||||
/* TODO: extend with GError ** argument, which is accepted by
|
||||
* ges_clip_add_child_to_track */
|
||||
|
||||
GST_DEBUG_OBJECT (timeline, "Creating %" GST_PTR_FORMAT
|
||||
" trackelements and adding them to our tracks", clip);
|
||||
|
||||
types = ges_clip_get_supported_formats (clip);
|
||||
if (track) {
|
||||
if ((types & track->type) == 0)
|
||||
return;
|
||||
types = track->type;
|
||||
}
|
||||
|
||||
LOCK_DYN (timeline);
|
||||
for (i = 0, tmp = timeline->tracks; tmp; tmp = tmp->next, i++) {
|
||||
GESTrack *track = GES_TRACK (tmp->data);
|
||||
/* FIXME: visited_type is essentially unused */
|
||||
if (((track->type & types) == 0 || (track->type & visited_type)))
|
||||
continue;
|
||||
|
||||
list = ges_clip_create_track_elements (clip, track->type);
|
||||
g_list_free (list);
|
||||
}
|
||||
tracks =
|
||||
g_list_copy_deep (timeline->tracks, (GCopyFunc) gst_object_ref, NULL);
|
||||
UNLOCK_DYN (timeline);
|
||||
/* create core elements */
|
||||
for (tmp = tracks; tmp; tmp = tmp->next) {
|
||||
GESTrack *track = GES_TRACK (tmp->data);
|
||||
list = ges_clip_create_track_elements (clip, track->type);
|
||||
for (created = list; created; created = created->next) {
|
||||
GESTimelineElement *el = created->data;
|
||||
|
||||
gst_object_ref (el);
|
||||
|
||||
/* make track selection be handled by clip_track_element_added_cb
|
||||
* This is needed for backward-compatibility: when adding a clip to
|
||||
* a layer, the track is set for the core elements of the clip
|
||||
* during the child-added signal emission, just before the user's
|
||||
* own connection.
|
||||
* NOTE: for the children that have not just been created, they
|
||||
* are already part of the clip and so child-added will not be
|
||||
* released. And when a child is selected for multiple tracks, their
|
||||
* copy will be added to the clip before the track is selected, so
|
||||
* the track will not be set in the child-added signal */
|
||||
_set_track_selection_error (timeline, FALSE);
|
||||
if (!ges_container_add (GES_CONTAINER (clip), el))
|
||||
GST_ERROR_OBJECT (clip, "Could not add the core element %s "
|
||||
"to the clip", el->name);
|
||||
if (_get_track_selection_error (timeline))
|
||||
no_errors = FALSE;
|
||||
|
||||
gst_object_unref (el);
|
||||
}
|
||||
/* just_added only used for pointer comparison, so safe to include
|
||||
* elements that are now destroyed because they failed to be added to
|
||||
* the clip */
|
||||
just_added = g_list_concat (just_added, list);
|
||||
}
|
||||
g_list_free_full (tracks, gst_object_unref);
|
||||
|
||||
/* set the tracks for the other children, with core elements first to
|
||||
* make sure the non-core can be placed above them in the track (a
|
||||
* non-core can not be in a track by itself) */
|
||||
/* include just_added as a blacklist to ensure we do not try the track
|
||||
* selection a second time when track selection returns no tracks */
|
||||
if (!_add_clip_children_to_tracks (timeline, clip, TRUE, track, just_added))
|
||||
no_errors = FALSE;
|
||||
if (!_add_clip_children_to_tracks (timeline, clip, FALSE, track, just_added))
|
||||
no_errors = FALSE;
|
||||
|
||||
g_list_free (just_added);
|
||||
|
||||
return no_errors;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -1392,160 +1700,14 @@ layer_auto_transition_changed_cb (GESLayer * layer,
|
|||
g_list_free_full (clips, gst_object_unref);
|
||||
}
|
||||
|
||||
static void
|
||||
clip_track_element_added_cb (GESClip * clip,
|
||||
GESTrackElement * track_element, GESTimeline * timeline)
|
||||
{
|
||||
guint i;
|
||||
GESTrack *track;
|
||||
gboolean is_source;
|
||||
GPtrArray *tracks = NULL;
|
||||
GESTrackElement *existing_src = NULL;
|
||||
|
||||
if (timeline->priv->ignore_track_element_added == clip) {
|
||||
GST_DEBUG_OBJECT (timeline, "Ignoring element added (%" GST_PTR_FORMAT
|
||||
" in %" GST_PTR_FORMAT, track_element, clip);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (ges_track_element_get_track (track_element)) {
|
||||
GST_WARNING_OBJECT (track_element, "Already in a track");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
g_signal_emit (G_OBJECT (timeline),
|
||||
ges_timeline_signals[SELECT_TRACKS_FOR_OBJECT], 0, clip, track_element,
|
||||
&tracks);
|
||||
/* FIXME: make sure each track in the list is unique */
|
||||
/* FIXME: make sure each track is part of the same timeline as the clip,
|
||||
* and warn and ignore the track if it isn't */
|
||||
|
||||
if (!tracks || tracks->len == 0) {
|
||||
GST_WARNING_OBJECT (timeline, "Got no Track to add %p (type %s), removing"
|
||||
" from clip (stopping 'child-added' signal emission).",
|
||||
track_element, ges_track_type_name (ges_track_element_get_track_type
|
||||
(track_element)));
|
||||
|
||||
if (tracks)
|
||||
g_ptr_array_unref (tracks);
|
||||
|
||||
g_signal_stop_emission_by_name (clip, "child-added");
|
||||
ges_container_remove (GES_CONTAINER (clip),
|
||||
GES_TIMELINE_ELEMENT (track_element));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* We add the current element to the first track */
|
||||
track = g_ptr_array_index (tracks, 0);
|
||||
|
||||
is_source = g_type_is_a (G_OBJECT_TYPE (track_element), GES_TYPE_SOURCE);
|
||||
if (is_source)
|
||||
existing_src = ges_clip_find_track_element (clip, track, GES_TYPE_SOURCE);
|
||||
|
||||
if (existing_src == NULL) {
|
||||
if (!ges_track_add_element (track, track_element)) {
|
||||
GST_WARNING_OBJECT (clip, "Failed to add track element to track");
|
||||
ges_container_remove (GES_CONTAINER (clip),
|
||||
GES_TIMELINE_ELEMENT (track_element));
|
||||
/* FIXME: unref all the individual track in tracks */
|
||||
g_ptr_array_unref (tracks);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
GST_INFO_OBJECT (clip, "Already had a Source Element in %" GST_PTR_FORMAT
|
||||
" of type %s, removing new one. (stopping 'child-added' emission)",
|
||||
track, G_OBJECT_TYPE_NAME (track_element));
|
||||
g_signal_stop_emission_by_name (clip, "child-added");
|
||||
ges_container_remove (GES_CONTAINER (clip),
|
||||
GES_TIMELINE_ELEMENT (track_element));
|
||||
}
|
||||
gst_object_unref (track);
|
||||
g_clear_object (&existing_src);
|
||||
|
||||
/* And create copies to add to other tracks */
|
||||
timeline->priv->ignore_track_element_added = clip;
|
||||
for (i = 1; i < tracks->len; i++) {
|
||||
GESTrack *track;
|
||||
GESTrackElement *track_element_copy;
|
||||
|
||||
track = g_ptr_array_index (tracks, i);
|
||||
if (is_source)
|
||||
existing_src = ges_clip_find_track_element (clip, track, GES_TYPE_SOURCE);
|
||||
if (existing_src == NULL) {
|
||||
ges_container_remove (GES_CONTAINER (clip),
|
||||
GES_TIMELINE_ELEMENT (track_element));
|
||||
gst_object_unref (track);
|
||||
/* FIXME: tracks is needed for the next loop after continue */
|
||||
g_ptr_array_unref (tracks);
|
||||
continue;
|
||||
} else {
|
||||
GST_INFO_OBJECT (clip, "Already had a Source Element in %" GST_PTR_FORMAT
|
||||
" of type %s, removing new one. (stopping 'child-added' emission)",
|
||||
track, G_OBJECT_TYPE_NAME (track_element));
|
||||
g_signal_stop_emission_by_name (clip, "child-added");
|
||||
ges_container_remove (GES_CONTAINER (clip),
|
||||
GES_TIMELINE_ELEMENT (track_element));
|
||||
}
|
||||
/* FIXME: in both cases track_element is removed from the clip! */
|
||||
g_clear_object (&existing_src);
|
||||
|
||||
track_element_copy =
|
||||
GES_TRACK_ELEMENT (ges_timeline_element_copy (GES_TIMELINE_ELEMENT
|
||||
(track_element), TRUE));
|
||||
/* if a core child, mark the copy as core so it can be added */
|
||||
if (ges_track_element_get_creators (track_element))
|
||||
ges_track_element_add_creator (track_element_copy, clip);
|
||||
|
||||
GST_LOG_OBJECT (timeline, "Trying to add %p to track %p",
|
||||
track_element_copy, track);
|
||||
|
||||
if (!ges_container_add (GES_CONTAINER (clip),
|
||||
GES_TIMELINE_ELEMENT (track_element_copy))) {
|
||||
GST_WARNING_OBJECT (clip, "Failed to add track element to clip");
|
||||
gst_object_unref (track_element_copy);
|
||||
/* FIXME: unref **all** the individual track in tracks */
|
||||
g_ptr_array_unref (tracks);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ges_track_add_element (track, track_element_copy)) {
|
||||
GST_WARNING_OBJECT (clip, "Failed to add track element to track");
|
||||
ges_container_remove (GES_CONTAINER (clip),
|
||||
GES_TIMELINE_ELEMENT (track_element_copy));
|
||||
/* FIXME: should we also stop the child-added and child-removed
|
||||
* emissions? */
|
||||
gst_object_unref (track_element_copy);
|
||||
/* FIXME: unref **all** the individual track in tracks */
|
||||
g_ptr_array_unref (tracks);
|
||||
return;
|
||||
}
|
||||
|
||||
gst_object_unref (track);
|
||||
}
|
||||
timeline->priv->ignore_track_element_added = NULL;
|
||||
g_ptr_array_unref (tracks);
|
||||
if (GES_IS_SOURCE (track_element))
|
||||
timeline_tree_create_transitions (timeline->priv->tree,
|
||||
ges_timeline_find_auto_transition);
|
||||
}
|
||||
|
||||
static void
|
||||
clip_track_element_removed_cb (GESClip * clip,
|
||||
GESTrackElement * track_element, GESTimeline * timeline)
|
||||
{
|
||||
GESTrack *track = ges_track_element_get_track (track_element);
|
||||
|
||||
if (track)
|
||||
ges_track_remove_element (track, track_element);
|
||||
}
|
||||
|
||||
static void
|
||||
layer_object_added_cb (GESLayer * layer, GESClip * clip, GESTimeline * timeline)
|
||||
/* returns TRUE if selecting of tracks did not error */
|
||||
gboolean
|
||||
ges_timeline_add_clip (GESTimeline * timeline, GESClip * clip)
|
||||
{
|
||||
GESProject *project;
|
||||
gboolean ret;
|
||||
/* TODO: extend with GError ** argument, which is accepted by
|
||||
* ges_clip_add_child_to_track */
|
||||
|
||||
/* We make sure not to be connected twice */
|
||||
g_signal_handlers_disconnect_by_func (clip, clip_track_element_added_cb,
|
||||
|
@ -1564,11 +1726,11 @@ layer_object_added_cb (GESLayer * layer, GESClip * clip, GESTimeline * timeline)
|
|||
"TrackElement", clip);
|
||||
timeline_tree_create_transitions (timeline->priv->tree,
|
||||
ges_timeline_find_auto_transition);
|
||||
return;
|
||||
ret = TRUE;
|
||||
} else {
|
||||
ret = add_object_to_tracks (timeline, clip, NULL);
|
||||
}
|
||||
|
||||
add_object_to_tracks (timeline, clip, NULL);
|
||||
|
||||
GST_DEBUG ("Making sure that the asset is in our project");
|
||||
project =
|
||||
GES_PROJECT (ges_extractable_get_asset (GES_EXTRACTABLE (timeline)));
|
||||
|
@ -1576,6 +1738,8 @@ layer_object_added_cb (GESLayer * layer, GESClip * clip, GESTimeline * timeline)
|
|||
ges_extractable_get_asset (GES_EXTRACTABLE (clip)));
|
||||
|
||||
GST_DEBUG ("Done");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -1589,11 +1753,10 @@ layer_priority_changed_cb (GESLayer * layer,
|
|||
sort_layers);
|
||||
}
|
||||
|
||||
static void
|
||||
layer_object_removed_cb (GESLayer * layer, GESClip * clip,
|
||||
GESTimeline * timeline)
|
||||
void
|
||||
ges_timeline_remove_clip (GESTimeline * timeline, GESClip * clip)
|
||||
{
|
||||
GList *trackelements, *tmp;
|
||||
GList *tmp;
|
||||
|
||||
if (ges_clip_is_moving_from_layer (clip)) {
|
||||
GST_DEBUG ("Clip %p is moving from a layer to another, not doing"
|
||||
|
@ -1601,41 +1764,19 @@ layer_object_removed_cb (GESLayer * layer, GESClip * clip,
|
|||
return;
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (timeline, "Clip %" GES_FORMAT " removed from layer %p",
|
||||
GES_ARGS (clip), layer);
|
||||
GST_DEBUG_OBJECT (timeline, "Clip %" GES_FORMAT " removed from layer",
|
||||
GES_ARGS (clip));
|
||||
|
||||
/* Go over the clip's track element and figure out which one belongs to
|
||||
* the list of tracks we control */
|
||||
LOCK_DYN (timeline);
|
||||
for (tmp = timeline->tracks; tmp; tmp = tmp->next)
|
||||
ges_clip_empty_from_track (clip, tmp->data);
|
||||
UNLOCK_DYN (timeline);
|
||||
|
||||
trackelements = ges_container_get_children (GES_CONTAINER (clip), FALSE);
|
||||
for (tmp = trackelements; tmp; tmp = tmp->next) {
|
||||
GESTrackElement *track_element = (GESTrackElement *) tmp->data;
|
||||
GESTrack *track = ges_track_element_get_track (track_element);
|
||||
|
||||
if (!track)
|
||||
continue;
|
||||
|
||||
GST_DEBUG_OBJECT (timeline, "Trying to remove TrackElement %p",
|
||||
track_element);
|
||||
|
||||
/* FIXME Check if we should actually check that we control the
|
||||
* track in the new management of TrackElement context */
|
||||
LOCK_DYN (timeline);
|
||||
if (G_LIKELY (g_list_find_custom (timeline->priv->priv_tracks, track,
|
||||
(GCompareFunc) custom_find_track) || track == NULL)) {
|
||||
GST_DEBUG ("Belongs to one of the tracks we control");
|
||||
|
||||
ges_track_remove_element (track, track_element);
|
||||
}
|
||||
UNLOCK_DYN (timeline);
|
||||
}
|
||||
g_signal_handlers_disconnect_by_func (clip, clip_track_element_added_cb,
|
||||
timeline);
|
||||
g_signal_handlers_disconnect_by_func (clip, clip_track_element_removed_cb,
|
||||
timeline);
|
||||
|
||||
g_list_free_full (trackelements, gst_object_unref);
|
||||
|
||||
GST_DEBUG ("Done");
|
||||
}
|
||||
|
||||
|
@ -1971,6 +2112,17 @@ ges_timeline_append_layer (GESTimeline * timeline)
|
|||
*
|
||||
* Add a layer to the timeline.
|
||||
*
|
||||
* If the layer contains #GESClip-s, then this may trigger the creation of
|
||||
* their core track element children for the timeline's tracks, and the
|
||||
* placement of the clip's children in the tracks of the timeline using
|
||||
* #GESTimeline::select-tracks-for-object. Some errors may occur if this
|
||||
* would break one of the configuration rules of the timeline in one of
|
||||
* its tracks. In such cases, some track elements would fail to be added
|
||||
* to their tracks, but this method would still return %TRUE. As such, it
|
||||
* is advised that you only add clips to layers that already part of a
|
||||
* timeline. In such situations, ges_layer_add_clip() is able to fail if
|
||||
* adding the clip would cause such an error.
|
||||
*
|
||||
* Deprecated: 1.18: This method requires you to ensure the layer's
|
||||
* #GESLayer:priority will be unique to the timeline. Use
|
||||
* ges_timeline_append_layer() and ges_timeline_move_layer() instead.
|
||||
|
@ -2025,10 +2177,6 @@ ges_timeline_add_layer (GESTimeline * timeline, GESLayer * layer)
|
|||
ges_layer_set_timeline (layer, timeline);
|
||||
|
||||
/* Connect to 'clip-added'/'clip-removed' signal from the new layer */
|
||||
g_signal_connect_after (layer, "clip-added",
|
||||
G_CALLBACK (layer_object_added_cb), timeline);
|
||||
g_signal_connect_after (layer, "clip-removed",
|
||||
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",
|
||||
|
@ -2041,12 +2189,9 @@ ges_timeline_add_layer (GESTimeline * timeline, GESLayer * layer)
|
|||
|
||||
/* add any existing clips to the timeline */
|
||||
objects = ges_layer_get_clips (layer);
|
||||
for (tmp = objects; tmp; tmp = tmp->next) {
|
||||
layer_object_added_cb (layer, tmp->data, timeline);
|
||||
gst_object_unref (tmp->data);
|
||||
tmp->data = NULL;
|
||||
}
|
||||
g_list_free (objects);
|
||||
for (tmp = objects; tmp; tmp = tmp->next)
|
||||
ges_timeline_add_clip (timeline, tmp->data);
|
||||
g_list_free_full (objects, gst_object_unref);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
@ -2080,18 +2225,12 @@ ges_timeline_remove_layer (GESTimeline * timeline, GESLayer * layer)
|
|||
/* remove objects from any private data structures */
|
||||
|
||||
layer_objects = ges_layer_get_clips (layer);
|
||||
for (tmp = layer_objects; tmp; tmp = tmp->next) {
|
||||
layer_object_removed_cb (layer, GES_CLIP (tmp->data), timeline);
|
||||
gst_object_unref (G_OBJECT (tmp->data));
|
||||
tmp->data = NULL;
|
||||
}
|
||||
g_list_free (layer_objects);
|
||||
for (tmp = layer_objects; tmp; tmp = tmp->next)
|
||||
ges_timeline_remove_clip (timeline, tmp->data);
|
||||
g_list_free_full (layer_objects, gst_object_unref);
|
||||
|
||||
/* Disconnect signals */
|
||||
GST_DEBUG ("Disconnecting signal callbacks");
|
||||
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,
|
||||
|
@ -2115,8 +2254,16 @@ ges_timeline_remove_layer (GESTimeline * timeline, GESLayer * layer)
|
|||
* @timeline: The #GESTimeline
|
||||
* @track: (transfer full): The track to add
|
||||
*
|
||||
* Add a track to the timeline. Existing #GESClip-s in the timeline will,
|
||||
* where appropriate, add their controlled elements to the new track.
|
||||
* Add a track to the timeline.
|
||||
*
|
||||
* If the timeline already contains clips, then this may trigger the
|
||||
* creation of their core track element children for the track, and the
|
||||
* placement of the clip's children in the track of the timeline using
|
||||
* #GESTimeline::select-tracks-for-object. Some errors may occur if this
|
||||
* would break one of the configuration rules for the timeline in the
|
||||
* track. In such cases, some track elements would fail to be added to the
|
||||
* track, but this method would still return %TRUE. As such, it is advised
|
||||
* that you avoid adding tracks to timelines that already contain clips.
|
||||
*
|
||||
* Returns: %TRUE if @track was properly added.
|
||||
*/
|
||||
|
@ -2184,13 +2331,10 @@ ges_timeline_add_track (GESTimeline * timeline, GESTrack * track)
|
|||
GList *objects, *obj;
|
||||
objects = ges_layer_get_clips (tmp->data);
|
||||
|
||||
for (obj = objects; obj; obj = obj->next) {
|
||||
GESClip *clip = obj->data;
|
||||
for (obj = objects; obj; obj = obj->next)
|
||||
add_object_to_tracks (timeline, obj->data, track);
|
||||
|
||||
add_object_to_tracks (timeline, clip, track);
|
||||
gst_object_unref (clip);
|
||||
}
|
||||
g_list_free (objects);
|
||||
g_list_free_full (objects, gst_object_unref);
|
||||
}
|
||||
|
||||
/* FIXME Check if we should rollback if we can't sync state */
|
||||
|
@ -2240,8 +2384,22 @@ ges_timeline_remove_track (GESTimeline * timeline, GESTrack * track)
|
|||
gst_object_unref (tr_priv->pad);
|
||||
priv->priv_tracks = g_list_remove (priv->priv_tracks, tr_priv);
|
||||
UNLOCK_DYN (timeline);
|
||||
timeline->tracks = g_list_remove (timeline->tracks, track);
|
||||
|
||||
/* empty track of all elements that belong to the timeline's clips */
|
||||
/* elements with no parent can stay in the track, but their timeline
|
||||
* will be set to NULL when the track's timeline is set to NULL */
|
||||
|
||||
for (tmp = timeline->layers; tmp; tmp = tmp->next) {
|
||||
GList *clips, *clip;
|
||||
clips = ges_layer_get_clips (tmp->data);
|
||||
|
||||
for (clip = clips; clip; clip = clip->next)
|
||||
ges_clip_empty_from_track (clip->data, track);
|
||||
|
||||
g_list_free_full (clips, gst_object_unref);
|
||||
}
|
||||
|
||||
timeline->tracks = g_list_remove (timeline->tracks, track);
|
||||
ges_track_set_timeline (track, NULL);
|
||||
|
||||
/* Remove ghost pad */
|
||||
|
|
|
@ -1049,12 +1049,20 @@ ges_track_element_add_children_props (GESTrackElement * self,
|
|||
gboolean
|
||||
ges_track_element_set_track (GESTrackElement * object, GESTrack * track)
|
||||
{
|
||||
gboolean ret = TRUE;
|
||||
GESTimelineElement *parent = GES_TIMELINE_ELEMENT_PARENT (object);
|
||||
|
||||
g_return_val_if_fail (object->priv->nleobject, FALSE);
|
||||
|
||||
GST_DEBUG_OBJECT (object, "new track: %" GST_PTR_FORMAT, track);
|
||||
|
||||
if (GES_IS_CLIP (parent)
|
||||
&& !ges_clip_can_set_track_of_child (GES_CLIP (parent), object, track)) {
|
||||
GST_WARNING_OBJECT (object, "The parent clip %" GES_FORMAT " would "
|
||||
"not allow the track to be set to %" GST_PTR_FORMAT,
|
||||
GES_ARGS (parent), track);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
object->priv->track = track;
|
||||
|
||||
if (object->priv->track) {
|
||||
|
@ -1065,7 +1073,7 @@ ges_track_element_set_track (GESTrackElement * object, GESTrack * track)
|
|||
}
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_TRACK]);
|
||||
return ret;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
114
ges/ges-track.c
114
ges/ges-track.c
|
@ -382,7 +382,8 @@ ges_track_get_composition (GESTrack * track)
|
|||
* accessing it
|
||||
*/
|
||||
static gboolean
|
||||
remove_object_internal (GESTrack * track, GESTrackElement * object)
|
||||
remove_object_internal (GESTrack * track, GESTrackElement * object,
|
||||
gboolean emit)
|
||||
{
|
||||
GESTrackPrivate *priv;
|
||||
GstElement *nleobject;
|
||||
|
@ -392,25 +393,30 @@ remove_object_internal (GESTrack * track, GESTrackElement * object)
|
|||
priv = track->priv;
|
||||
|
||||
if (G_UNLIKELY (ges_track_element_get_track (object) != track)) {
|
||||
GST_WARNING ("Object belongs to another track");
|
||||
GST_WARNING_OBJECT (track, "Object belongs to another track");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!ges_track_element_set_track (object, NULL)) {
|
||||
GST_WARNING_OBJECT (track, "Failed to unset the track for %" GES_FORMAT,
|
||||
GES_ARGS (object));
|
||||
return FALSE;
|
||||
}
|
||||
ges_timeline_element_set_timeline (GES_TIMELINE_ELEMENT (object), NULL);
|
||||
|
||||
if ((nleobject = ges_track_element_get_nleobject (object))) {
|
||||
GST_DEBUG ("Removing NleObject '%s' from composition '%s'",
|
||||
GST_ELEMENT_NAME (nleobject), GST_ELEMENT_NAME (priv->composition));
|
||||
|
||||
if (!ges_nle_composition_remove_object (priv->composition, nleobject)) {
|
||||
GST_WARNING ("Failed to remove nleobject from composition");
|
||||
GST_WARNING_OBJECT (track, "Failed to remove nleobject from composition");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
ges_track_element_set_track (object, NULL);
|
||||
ges_timeline_element_set_timeline (GES_TIMELINE_ELEMENT (object), NULL);
|
||||
|
||||
g_signal_emit (track, ges_track_signals[TRACK_ELEMENT_REMOVED], 0,
|
||||
GES_TRACK_ELEMENT (object));
|
||||
if (emit)
|
||||
g_signal_emit (track, ges_track_signals[TRACK_ELEMENT_REMOVED], 0,
|
||||
GES_TRACK_ELEMENT (object));
|
||||
|
||||
gst_object_unref (object);
|
||||
|
||||
|
@ -420,7 +426,7 @@ remove_object_internal (GESTrack * track, GESTrackElement * object)
|
|||
static void
|
||||
dispose_trackelements_foreach (GESTrackElement * trackelement, GESTrack * track)
|
||||
{
|
||||
remove_object_internal (track, trackelement);
|
||||
remove_object_internal (track, trackelement, TRUE);
|
||||
}
|
||||
|
||||
/* GstElement virtual methods */
|
||||
|
@ -918,9 +924,19 @@ ges_track_new (GESTrackType type, GstCaps * caps)
|
|||
void
|
||||
ges_track_set_timeline (GESTrack * track, GESTimeline * timeline)
|
||||
{
|
||||
GSequenceIter *it;
|
||||
g_return_if_fail (GES_IS_TRACK (track));
|
||||
g_return_if_fail (timeline == NULL || GES_IS_TIMELINE (timeline));
|
||||
GST_DEBUG ("track:%p, timeline:%p", track, timeline);
|
||||
|
||||
track->priv->timeline = timeline;
|
||||
|
||||
for (it = g_sequence_get_begin_iter (track->priv->trackelements_by_start);
|
||||
g_sequence_iter_is_end (it) == FALSE; it = g_sequence_iter_next (it)) {
|
||||
GESTimelineElement *trackelement =
|
||||
GES_TIMELINE_ELEMENT (g_sequence_get (it));
|
||||
ges_timeline_element_set_timeline (trackelement, timeline);
|
||||
}
|
||||
track_resort_and_fill_gaps (track);
|
||||
}
|
||||
|
||||
|
@ -1089,6 +1105,31 @@ notify:
|
|||
GST_DEBUG_OBJECT (track, "The track has been set to mixing = %d", mixing);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
remove_element_internal (GESTrack * track, GESTrackElement * object,
|
||||
gboolean emit)
|
||||
{
|
||||
GSequenceIter *it;
|
||||
GESTrackPrivate *priv = track->priv;
|
||||
|
||||
GST_DEBUG_OBJECT (track, "Removing %" GST_PTR_FORMAT, object);
|
||||
|
||||
it = g_hash_table_lookup (priv->trackelements_iter, object);
|
||||
g_sequence_remove (it);
|
||||
|
||||
if (remove_object_internal (track, object, emit) == TRUE) {
|
||||
ges_timeline_element_set_timeline (GES_TIMELINE_ELEMENT (object), NULL);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
g_hash_table_insert (track->priv->trackelements_iter, object,
|
||||
g_sequence_insert_sorted (track->priv->trackelements_by_start, object,
|
||||
(GCompareDataFunc) element_start_compare, NULL));
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ges_track_add_element:
|
||||
* @track: A #GESTrack
|
||||
|
@ -1097,6 +1138,9 @@ notify:
|
|||
* Adds the given track element to the track, which takes ownership of the
|
||||
* element.
|
||||
*
|
||||
* Note that this can fail if it would break a configuration rule of the
|
||||
* track's #GESTimeline.
|
||||
*
|
||||
* Note that a #GESTrackElement can only be added to one track.
|
||||
*
|
||||
* Returns: %TRUE if @object was successfully added to @track.
|
||||
|
@ -1104,8 +1148,14 @@ notify:
|
|||
gboolean
|
||||
ges_track_add_element (GESTrack * track, GESTrackElement * object)
|
||||
{
|
||||
GESTimeline *timeline;
|
||||
GESTimelineElement *el;
|
||||
|
||||
g_return_val_if_fail (GES_IS_TRACK (track), FALSE);
|
||||
g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), FALSE);
|
||||
|
||||
el = GES_TIMELINE_ELEMENT (object);
|
||||
|
||||
CHECK_THREAD (track);
|
||||
|
||||
GST_DEBUG ("track:%p, object:%p", track, object);
|
||||
|
@ -1117,12 +1167,14 @@ ges_track_add_element (GESTrack * track, GESTrackElement * object)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
if (G_UNLIKELY (!ges_track_element_set_track (object, track))) {
|
||||
GST_ERROR ("Couldn't properly add the object to the Track");
|
||||
if (!ges_track_element_set_track (object, track)) {
|
||||
GST_WARNING_OBJECT (track, "Failed to set the track for %" GES_FORMAT,
|
||||
GES_ARGS (object));
|
||||
gst_object_ref_sink (object);
|
||||
gst_object_unref (object);
|
||||
return FALSE;
|
||||
}
|
||||
ges_timeline_element_set_timeline (el, NULL);
|
||||
|
||||
GST_DEBUG ("Adding object %s to ourself %s",
|
||||
GST_OBJECT_NAME (ges_track_element_get_nleobject (object)),
|
||||
|
@ -1131,6 +1183,7 @@ ges_track_add_element (GESTrack * track, GESTrackElement * object)
|
|||
if (G_UNLIKELY (!ges_nle_composition_add_object (track->priv->composition,
|
||||
ges_track_element_get_nleobject (object)))) {
|
||||
GST_WARNING ("Couldn't add object to the NleComposition");
|
||||
ges_track_element_set_track (object, NULL);
|
||||
gst_object_ref_sink (object);
|
||||
gst_object_unref (object);
|
||||
return FALSE;
|
||||
|
@ -1141,8 +1194,20 @@ ges_track_add_element (GESTrack * track, GESTrackElement * object)
|
|||
g_sequence_insert_sorted (track->priv->trackelements_by_start, object,
|
||||
(GCompareDataFunc) element_start_compare, NULL));
|
||||
|
||||
ges_timeline_element_set_timeline (GES_TIMELINE_ELEMENT (object),
|
||||
track->priv->timeline);
|
||||
timeline = track->priv->timeline;
|
||||
ges_timeline_element_set_timeline (el, timeline);
|
||||
if (timeline
|
||||
&& !timeline_tree_can_move_element (timeline_get_tree (timeline), el,
|
||||
GES_TIMELINE_ELEMENT_LAYER_PRIORITY (el), el->start, el->duration,
|
||||
NULL)) {
|
||||
GST_WARNING_OBJECT (track,
|
||||
"Could not add the track element %" GES_FORMAT
|
||||
" to the track because it breaks the timeline " "configuration rules",
|
||||
GES_ARGS (el));
|
||||
remove_element_internal (track, object, FALSE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_signal_emit (track, ges_track_signals[TRACK_ELEMENT_ADDED], 0,
|
||||
GES_TRACK_ELEMENT (object));
|
||||
|
||||
|
@ -1188,31 +1253,12 @@ ges_track_get_elements (GESTrack * track)
|
|||
gboolean
|
||||
ges_track_remove_element (GESTrack * track, GESTrackElement * object)
|
||||
{
|
||||
GSequenceIter *it;
|
||||
GESTrackPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (GES_IS_TRACK (track), FALSE);
|
||||
g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), FALSE);
|
||||
|
||||
CHECK_THREAD (track);
|
||||
|
||||
priv = track->priv;
|
||||
|
||||
GST_DEBUG_OBJECT (track, "Removing %" GST_PTR_FORMAT, object);
|
||||
|
||||
it = g_hash_table_lookup (priv->trackelements_iter, object);
|
||||
g_sequence_remove (it);
|
||||
|
||||
if (remove_object_internal (track, object) == TRUE) {
|
||||
ges_timeline_element_set_timeline (GES_TIMELINE_ELEMENT (object), NULL);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
g_hash_table_insert (track->priv->trackelements_iter, object,
|
||||
g_sequence_insert_sorted (track->priv->trackelements_by_start, object,
|
||||
(GCompareDataFunc) element_start_compare, NULL));
|
||||
|
||||
return FALSE;
|
||||
return remove_element_internal (track, object, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -54,8 +54,7 @@ GST_START_TEST (test_ges_scenario)
|
|||
|
||||
layers = ges_timeline_get_layers (timeline);
|
||||
fail_unless (g_list_find (layers, layer) != NULL);
|
||||
g_list_foreach (layers, (GFunc) gst_object_unref, NULL);
|
||||
g_list_free (layers);
|
||||
g_list_free_full (layers, gst_object_unref);
|
||||
|
||||
/* Give the Timeline a Track */
|
||||
GST_DEBUG ("Create a Track");
|
||||
|
@ -101,6 +100,7 @@ GST_START_TEST (test_ges_scenario)
|
|||
* 3 by the timeline
|
||||
* 1 by the track */
|
||||
ASSERT_OBJECT_REFCOUNT (trackelement, "trackelement", 3);
|
||||
fail_unless (ges_track_element_get_track (trackelement) == track);
|
||||
|
||||
GST_DEBUG ("Remove the Clip from the layer");
|
||||
|
||||
|
@ -108,6 +108,10 @@ GST_START_TEST (test_ges_scenario)
|
|||
gst_object_ref (source);
|
||||
ASSERT_OBJECT_REFCOUNT (layer, "layer", 1);
|
||||
fail_unless (ges_layer_remove_clip (layer, GES_CLIP (source)));
|
||||
/* track elements emptied from the track, but stay in clip */
|
||||
fail_unless (GES_TIMELINE_ELEMENT_PARENT (trackelement) ==
|
||||
GES_TIMELINE_ELEMENT (source));
|
||||
fail_unless (ges_track_element_get_track (trackelement) == NULL);
|
||||
ASSERT_OBJECT_REFCOUNT (source, "source", 1);
|
||||
ASSERT_OBJECT_REFCOUNT (layer, "layer", 1);
|
||||
tmp_layer = ges_clip_get_layer (GES_CLIP (source));
|
||||
|
@ -118,7 +122,7 @@ GST_START_TEST (test_ges_scenario)
|
|||
/* Remove the track from the timeline */
|
||||
gst_object_ref (track);
|
||||
fail_unless (ges_timeline_remove_track (timeline, track));
|
||||
fail_unless (ges_track_get_timeline (track) == NULL);
|
||||
assert_num_in_track (track, 0);
|
||||
|
||||
tracks = ges_timeline_get_tracks (timeline);
|
||||
fail_unless (tracks == NULL);
|
||||
|
@ -149,12 +153,23 @@ GST_END_TEST;
|
|||
* and then add it to the timeline.
|
||||
*/
|
||||
|
||||
#define _CREATE_SOURCE(layer, clip, start, duration) \
|
||||
{ \
|
||||
GESAsset *asset = ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL); \
|
||||
GST_DEBUG ("Creating a source"); \
|
||||
fail_unless (clip = ges_layer_add_asset (layer, asset, start, 0, \
|
||||
duration, GES_TRACK_TYPE_UNKNOWN)); \
|
||||
assert_layer(clip, layer); \
|
||||
ASSERT_OBJECT_REFCOUNT (layer, "layer", 1); \
|
||||
gst_object_unref (asset); \
|
||||
}
|
||||
|
||||
GST_START_TEST (test_ges_timeline_add_layer)
|
||||
{
|
||||
GESTimeline *timeline;
|
||||
GESLayer *layer, *tmp_layer;
|
||||
GESLayer *layer;
|
||||
GESTrack *track;
|
||||
GESTestClip *s1, *s2, *s3;
|
||||
GESClip *s1, *s2, *s3;
|
||||
GList *trackelements, *layers;
|
||||
GESTrackElement *trackelement;
|
||||
|
||||
|
@ -180,34 +195,6 @@ GST_START_TEST (test_ges_timeline_add_layer)
|
|||
fail_unless (ges_track_get_timeline (track) == timeline);
|
||||
fail_unless ((gpointer) GST_ELEMENT_PARENT (track) == (gpointer) timeline);
|
||||
|
||||
/* Create a source and add it to the Layer */
|
||||
GST_DEBUG ("Creating a source");
|
||||
s1 = ges_test_clip_new ();
|
||||
fail_unless (s1 != NULL);
|
||||
fail_unless (ges_layer_add_clip (layer, GES_CLIP (s1)));
|
||||
tmp_layer = ges_clip_get_layer (GES_CLIP (s1));
|
||||
fail_unless (tmp_layer == layer);
|
||||
ASSERT_OBJECT_REFCOUNT (layer, "layer", 2);
|
||||
gst_object_unref (tmp_layer);
|
||||
|
||||
GST_DEBUG ("Creating a source");
|
||||
s2 = ges_test_clip_new ();
|
||||
fail_unless (s2 != NULL);
|
||||
fail_unless (ges_layer_add_clip (layer, GES_CLIP (s2)));
|
||||
tmp_layer = ges_clip_get_layer (GES_CLIP (s2));
|
||||
fail_unless (tmp_layer == layer);
|
||||
ASSERT_OBJECT_REFCOUNT (layer, "layer", 2);
|
||||
gst_object_unref (tmp_layer);
|
||||
|
||||
GST_DEBUG ("Creating a source");
|
||||
s3 = ges_test_clip_new ();
|
||||
fail_unless (s3 != NULL);
|
||||
fail_unless (ges_layer_add_clip (layer, GES_CLIP (s3)));
|
||||
tmp_layer = ges_clip_get_layer (GES_CLIP (s3));
|
||||
fail_unless (tmp_layer == layer);
|
||||
ASSERT_OBJECT_REFCOUNT (layer, "layer", 2);
|
||||
gst_object_unref (tmp_layer);
|
||||
|
||||
GST_DEBUG ("Add the layer to the timeline");
|
||||
fail_unless (ges_timeline_add_layer (timeline, layer));
|
||||
/* The timeline steals our reference to the layer */
|
||||
|
@ -215,8 +202,14 @@ GST_START_TEST (test_ges_timeline_add_layer)
|
|||
fail_unless (layer->timeline == timeline);
|
||||
layers = ges_timeline_get_layers (timeline);
|
||||
fail_unless (g_list_find (layers, layer) != NULL);
|
||||
g_list_foreach (layers, (GFunc) gst_object_unref, NULL);
|
||||
g_list_free (layers);
|
||||
g_list_free_full (layers, gst_object_unref);
|
||||
|
||||
_CREATE_SOURCE (layer, s1, 0, 10);
|
||||
ASSERT_OBJECT_REFCOUNT (layer, "1 for the timeline", 1);
|
||||
_CREATE_SOURCE (layer, s2, 20, 10);
|
||||
ASSERT_OBJECT_REFCOUNT (layer, "1 for the timeline", 1);
|
||||
_CREATE_SOURCE (layer, s3, 40, 10);
|
||||
ASSERT_OBJECT_REFCOUNT (layer, "1 for the timeline", 1);
|
||||
|
||||
/* Make sure the associated TrackElements are in the Track */
|
||||
trackelements = GES_CONTAINER_CHILDREN (s1);
|
||||
|
@ -266,9 +259,9 @@ GST_END_TEST;
|
|||
GST_START_TEST (test_ges_timeline_add_layer_first)
|
||||
{
|
||||
GESTimeline *timeline;
|
||||
GESLayer *layer, *tmp_layer;
|
||||
GESLayer *layer;
|
||||
GESTrack *track;
|
||||
GESTestClip *s1, *s2, *s3;
|
||||
GESClip *s1, *s2, *s3;
|
||||
GList *trackelements, *tmp, *layers;
|
||||
|
||||
ges_init ();
|
||||
|
@ -286,30 +279,9 @@ GST_START_TEST (test_ges_timeline_add_layer_first)
|
|||
track = GES_TRACK (ges_video_track_new ());
|
||||
fail_unless (track != NULL);
|
||||
|
||||
/* Create a source and add it to the Layer */
|
||||
GST_DEBUG ("Creating a source");
|
||||
s1 = ges_test_clip_new ();
|
||||
fail_unless (s1 != NULL);
|
||||
fail_unless (ges_layer_add_clip (layer, GES_CLIP (s1)));
|
||||
tmp_layer = ges_clip_get_layer (GES_CLIP (s1));
|
||||
fail_unless (tmp_layer == layer);
|
||||
gst_object_unref (tmp_layer);
|
||||
|
||||
GST_DEBUG ("Creating a source");
|
||||
s2 = ges_test_clip_new ();
|
||||
fail_unless (s2 != NULL);
|
||||
fail_unless (ges_layer_add_clip (layer, GES_CLIP (s2)));
|
||||
tmp_layer = ges_clip_get_layer (GES_CLIP (s2));
|
||||
fail_unless (tmp_layer == layer);
|
||||
gst_object_unref (tmp_layer);
|
||||
|
||||
GST_DEBUG ("Creating a source");
|
||||
s3 = ges_test_clip_new ();
|
||||
fail_unless (s3 != NULL);
|
||||
fail_unless (ges_layer_add_clip (layer, GES_CLIP (s3)));
|
||||
tmp_layer = ges_clip_get_layer (GES_CLIP (s3));
|
||||
fail_unless (tmp_layer == layer);
|
||||
gst_object_unref (tmp_layer);
|
||||
_CREATE_SOURCE (layer, s1, 0, 10);
|
||||
_CREATE_SOURCE (layer, s2, 20, 10);
|
||||
_CREATE_SOURCE (layer, s3, 40, 10);
|
||||
|
||||
GST_DEBUG ("Add the layer to the timeline");
|
||||
fail_unless (ges_timeline_add_layer (timeline, layer));
|
||||
|
@ -318,8 +290,7 @@ GST_START_TEST (test_ges_timeline_add_layer_first)
|
|||
fail_unless (layer->timeline == timeline);
|
||||
layers = ges_timeline_get_layers (timeline);
|
||||
fail_unless (g_list_find (layers, layer) != NULL);
|
||||
g_list_foreach (layers, (GFunc) gst_object_unref, NULL);
|
||||
g_list_free (layers);
|
||||
g_list_free_full (layers, gst_object_unref);
|
||||
|
||||
GST_DEBUG ("Add the track to the timeline");
|
||||
fail_unless (ges_timeline_add_track (timeline, track));
|
||||
|
@ -369,9 +340,9 @@ GST_END_TEST;
|
|||
GST_START_TEST (test_ges_timeline_remove_track)
|
||||
{
|
||||
GESTimeline *timeline;
|
||||
GESLayer *layer, *tmp_layer;
|
||||
GESLayer *layer;
|
||||
GESTrack *track;
|
||||
GESTestClip *s1, *s2, *s3;
|
||||
GESClip *s1, *s2, *s3;
|
||||
GESTrackElement *t1, *t2, *t3;
|
||||
GList *trackelements, *tmp, *layers;
|
||||
|
||||
|
@ -390,32 +361,11 @@ GST_START_TEST (test_ges_timeline_remove_track)
|
|||
track = GES_TRACK (ges_video_track_new ());
|
||||
fail_unless (track != NULL);
|
||||
|
||||
/* Create a source and add it to the Layer */
|
||||
GST_DEBUG ("Creating a source");
|
||||
s1 = ges_test_clip_new ();
|
||||
fail_unless (s1 != NULL);
|
||||
fail_unless (ges_layer_add_clip (layer, GES_CLIP (s1)));
|
||||
tmp_layer = ges_clip_get_layer (GES_CLIP (s1));
|
||||
fail_unless (tmp_layer == layer);
|
||||
gst_object_unref (tmp_layer);
|
||||
_CREATE_SOURCE (layer, s1, 0, 10);
|
||||
ASSERT_OBJECT_REFCOUNT (layer, "1 for the timeline", 1);
|
||||
|
||||
GST_DEBUG ("Creating a source");
|
||||
s2 = ges_test_clip_new ();
|
||||
fail_unless (s2 != NULL);
|
||||
fail_unless (ges_layer_add_clip (layer, GES_CLIP (s2)));
|
||||
tmp_layer = ges_clip_get_layer (GES_CLIP (s2));
|
||||
fail_unless (tmp_layer == layer);
|
||||
gst_object_unref (tmp_layer);
|
||||
_CREATE_SOURCE (layer, s2, 20, 10);
|
||||
ASSERT_OBJECT_REFCOUNT (layer, "1 for the timeline", 1);
|
||||
|
||||
GST_DEBUG ("Creating a source");
|
||||
s3 = ges_test_clip_new ();
|
||||
fail_unless (s3 != NULL);
|
||||
fail_unless (ges_layer_add_clip (layer, GES_CLIP (s3)));
|
||||
tmp_layer = ges_clip_get_layer (GES_CLIP (s3));
|
||||
fail_unless (tmp_layer == layer);
|
||||
gst_object_unref (tmp_layer);
|
||||
_CREATE_SOURCE (layer, s3, 40, 10);
|
||||
ASSERT_OBJECT_REFCOUNT (layer, "1 for the timeline", 1);
|
||||
|
||||
GST_DEBUG ("Add the layer to the timeline");
|
||||
|
@ -426,8 +376,7 @@ GST_START_TEST (test_ges_timeline_remove_track)
|
|||
|
||||
layers = ges_timeline_get_layers (timeline);
|
||||
fail_unless (g_list_find (layers, layer) != NULL);
|
||||
g_list_foreach (layers, (GFunc) gst_object_unref, NULL);
|
||||
g_list_free (layers);
|
||||
g_list_free_full (layers, gst_object_unref);
|
||||
ASSERT_OBJECT_REFCOUNT (layer, "1 for the timeline", 1);
|
||||
|
||||
GST_DEBUG ("Add the track to the timeline");
|
||||
|
@ -485,8 +434,18 @@ GST_START_TEST (test_ges_timeline_remove_track)
|
|||
* 1 by the timeline */
|
||||
ASSERT_OBJECT_REFCOUNT (t3, "t3", 3);
|
||||
|
||||
fail_unless (ges_track_element_get_track (t1) == track);
|
||||
fail_unless (ges_track_element_get_track (t2) == track);
|
||||
fail_unless (ges_track_element_get_track (t3) == track);
|
||||
|
||||
/* remove the track and check that the track elements have been released */
|
||||
gst_object_ref (track);
|
||||
fail_unless (ges_timeline_remove_track (timeline, track));
|
||||
assert_num_in_track (track, 0);
|
||||
gst_object_unref (track);
|
||||
fail_unless (ges_track_element_get_track (t1) == NULL);
|
||||
fail_unless (ges_track_element_get_track (t2) == NULL);
|
||||
fail_unless (ges_track_element_get_track (t3) == NULL);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (t1, "trackelement", 1);
|
||||
ASSERT_OBJECT_REFCOUNT (t2, "trackelement", 1);
|
||||
|
@ -507,22 +466,67 @@ GST_END_TEST;
|
|||
|
||||
typedef struct
|
||||
{
|
||||
GESTestClip **o1, **o2, **o3;
|
||||
GESTrack **tr1, **tr2;
|
||||
GESClip *clips[4];
|
||||
guint num_calls[4];
|
||||
GESTrackElement *effects[3];
|
||||
GESTrack *tr1, *tr2;
|
||||
guint num_unrecognised;
|
||||
} SelectTracksData;
|
||||
|
||||
static GPtrArray *
|
||||
select_tracks_cb (GESTimeline * timeline, GESClip * clip,
|
||||
GESTrackElement * track_element, SelectTracksData * st_data)
|
||||
GESTrackElement * track_element, SelectTracksData * data)
|
||||
{
|
||||
GESTrack *track;
|
||||
|
||||
GPtrArray *ret = g_ptr_array_new ();
|
||||
track = (clip == (GESClip *) * st_data->o2) ? *st_data->tr2 : *st_data->tr1;
|
||||
gboolean track1 = FALSE;
|
||||
gboolean track2 = FALSE;
|
||||
guint i;
|
||||
gboolean recognise_clip = FALSE;
|
||||
|
||||
gst_object_ref (track);
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (clip == data->clips[i]) {
|
||||
data->num_calls[i]++;
|
||||
recognise_clip = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
g_ptr_array_add (ret, track);
|
||||
if (!recognise_clip) {
|
||||
GST_DEBUG_OBJECT (timeline, "unrecognised clip %" GES_FORMAT " for "
|
||||
"track element %" GES_FORMAT, GES_ARGS (clip),
|
||||
GES_ARGS (track_element));
|
||||
data->num_unrecognised++;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (GES_IS_BASE_EFFECT (track_element)) {
|
||||
if (track_element == data->effects[0]) {
|
||||
track1 = TRUE;
|
||||
} else if (track_element == data->effects[1]) {
|
||||
track1 = TRUE;
|
||||
track2 = TRUE;
|
||||
} else if (track_element == data->effects[2]) {
|
||||
track2 = TRUE;
|
||||
} else {
|
||||
GST_DEBUG_OBJECT (timeline, "unrecognised effect %" GES_FORMAT,
|
||||
GES_ARGS (track_element));
|
||||
data->num_unrecognised++;
|
||||
}
|
||||
} else if (GES_IS_SOURCE (track_element)) {
|
||||
if (clip == data->clips[0] || clip == data->clips[1])
|
||||
track1 = TRUE;
|
||||
if (clip == data->clips[1] || clip == data->clips[2])
|
||||
track2 = TRUE;
|
||||
/* clips[3] has no tracks selected */
|
||||
} else {
|
||||
GST_DEBUG_OBJECT (timeline, "unrecognised track element %" GES_FORMAT,
|
||||
GES_ARGS (track_element));
|
||||
data->num_unrecognised++;
|
||||
}
|
||||
|
||||
if (track1)
|
||||
g_ptr_array_add (ret, gst_object_ref (data->tr1));
|
||||
if (track2)
|
||||
g_ptr_array_add (ret, gst_object_ref (data->tr2));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -530,12 +534,14 @@ select_tracks_cb (GESTimeline * timeline, GESClip * clip,
|
|||
GST_START_TEST (test_ges_timeline_multiple_tracks)
|
||||
{
|
||||
GESTimeline *timeline;
|
||||
GESLayer *layer, *tmp_layer;
|
||||
GESLayer *layer;
|
||||
GESTrack *track1, *track2;
|
||||
GESTestClip *s1, *s2, *s3;
|
||||
GESTrackElement *t1, *t2, *t3;
|
||||
GESClip *s1, *s2, *s3, *s4;
|
||||
GESTrackElement *e1, *e2, *e3, *el, *el2, *e_copy;
|
||||
gboolean found_e1 = FALSE, found_e2 = FALSE, found_e3 = FALSE;
|
||||
GList *trackelements, *tmp, *layers;
|
||||
SelectTracksData st_data = { &s1, &s2, &s3, &track1, &track2 };
|
||||
GstControlSource *ctrl_source;
|
||||
SelectTracksData st_data;
|
||||
|
||||
ges_init ();
|
||||
|
||||
|
@ -544,9 +550,6 @@ GST_START_TEST (test_ges_timeline_multiple_tracks)
|
|||
timeline = ges_timeline_new ();
|
||||
fail_unless (timeline != NULL);
|
||||
|
||||
g_signal_connect (timeline, "select-tracks-for-object",
|
||||
G_CALLBACK (select_tracks_cb), &st_data);
|
||||
|
||||
GST_DEBUG ("Create a layer");
|
||||
layer = ges_layer_new ();
|
||||
fail_unless (layer != NULL);
|
||||
|
@ -570,31 +573,71 @@ GST_START_TEST (test_ges_timeline_multiple_tracks)
|
|||
fail_unless (ges_track_get_timeline (track2) == timeline);
|
||||
fail_unless ((gpointer) GST_ELEMENT_PARENT (track2) == (gpointer) timeline);
|
||||
|
||||
/* Create a source and add it to the Layer */
|
||||
GST_DEBUG ("Creating a source");
|
||||
s1 = ges_test_clip_new ();
|
||||
fail_unless (s1 != NULL);
|
||||
fail_unless (ges_layer_add_clip (layer, GES_CLIP (s1)));
|
||||
tmp_layer = ges_clip_get_layer (GES_CLIP (s1));
|
||||
fail_unless (tmp_layer == layer);
|
||||
gst_object_unref (tmp_layer);
|
||||
/* adding to the layer before it is part of the timeline does not
|
||||
* trigger track selection */
|
||||
/* s1 and s3 can overlap since they are destined for different tracks */
|
||||
/* s2 will overlap both */
|
||||
/* s4 destined for no track */
|
||||
_CREATE_SOURCE (layer, s1, 0, 10);
|
||||
_CREATE_SOURCE (layer, s2, 5, 10);
|
||||
_CREATE_SOURCE (layer, s3, 0, 10);
|
||||
_CREATE_SOURCE (layer, s4, 0, 20);
|
||||
|
||||
GST_DEBUG ("Creating a source");
|
||||
s2 = ges_test_clip_new ();
|
||||
fail_unless (s2 != NULL);
|
||||
fail_unless (ges_layer_add_clip (layer, GES_CLIP (s2)));
|
||||
tmp_layer = ges_clip_get_layer (GES_CLIP (s2));
|
||||
fail_unless (tmp_layer == layer);
|
||||
gst_object_unref (tmp_layer);
|
||||
e1 = GES_TRACK_ELEMENT (ges_effect_new ("videobalance"));
|
||||
fail_unless (ges_container_add (GES_CONTAINER (s2),
|
||||
GES_TIMELINE_ELEMENT (e1)));
|
||||
e2 = GES_TRACK_ELEMENT (ges_effect_new ("agingtv ! vertigotv"));
|
||||
fail_unless (ges_container_add (GES_CONTAINER (s2),
|
||||
GES_TIMELINE_ELEMENT (e2)));
|
||||
e3 = GES_TRACK_ELEMENT (ges_effect_new ("alpha"));
|
||||
fail_unless (ges_container_add (GES_CONTAINER (s2),
|
||||
GES_TIMELINE_ELEMENT (e3)));
|
||||
assert_equals_int (0,
|
||||
ges_clip_get_top_effect_index (s2, GES_BASE_EFFECT (e1)));
|
||||
assert_equals_int (1,
|
||||
ges_clip_get_top_effect_index (s2, GES_BASE_EFFECT (e2)));
|
||||
assert_equals_int (2,
|
||||
ges_clip_get_top_effect_index (s2, GES_BASE_EFFECT (e3)));
|
||||
|
||||
GST_DEBUG ("Creating a source");
|
||||
s3 = ges_test_clip_new ();
|
||||
fail_unless (s3 != NULL);
|
||||
fail_unless (ges_layer_add_clip (layer, GES_CLIP (s3)));
|
||||
tmp_layer = ges_clip_get_layer (GES_CLIP (s3));
|
||||
fail_unless (tmp_layer == layer);
|
||||
gst_object_unref (tmp_layer);
|
||||
assert_num_children (s1, 0);
|
||||
assert_num_children (s2, 3);
|
||||
assert_num_children (s3, 0);
|
||||
|
||||
ges_timeline_element_set_child_properties (GES_TIMELINE_ELEMENT (s2),
|
||||
"scratch-lines", 2, "speed", 50.0, NULL);
|
||||
|
||||
ctrl_source = GST_CONTROL_SOURCE (gst_interpolation_control_source_new ());
|
||||
g_object_set (G_OBJECT (ctrl_source), "mode",
|
||||
GST_INTERPOLATION_MODE_NONE, NULL);
|
||||
fail_unless (gst_timed_value_control_source_set
|
||||
(GST_TIMED_VALUE_CONTROL_SOURCE (ctrl_source), 0, 1.0));
|
||||
fail_unless (gst_timed_value_control_source_set
|
||||
(GST_TIMED_VALUE_CONTROL_SOURCE (ctrl_source), 4, 7.0));
|
||||
fail_unless (gst_timed_value_control_source_set
|
||||
(GST_TIMED_VALUE_CONTROL_SOURCE (ctrl_source), 8, 3.0));
|
||||
fail_unless (ges_track_element_set_control_source (e2, ctrl_source,
|
||||
"scratch-lines", "direct-absolute"));
|
||||
gst_object_unref (ctrl_source);
|
||||
|
||||
st_data.tr1 = track1;
|
||||
st_data.tr2 = track2;
|
||||
st_data.clips[0] = s1;
|
||||
st_data.clips[1] = s2;
|
||||
st_data.clips[2] = s3;
|
||||
st_data.clips[3] = s4;
|
||||
st_data.num_calls[0] = 0;
|
||||
st_data.num_calls[1] = 0;
|
||||
st_data.num_calls[2] = 0;
|
||||
st_data.num_calls[3] = 0;
|
||||
st_data.effects[0] = e1;
|
||||
st_data.effects[1] = e2;
|
||||
st_data.effects[2] = e3;
|
||||
st_data.num_unrecognised = 0;
|
||||
|
||||
g_signal_connect (timeline, "select-tracks-for-object",
|
||||
G_CALLBACK (select_tracks_cb), &st_data);
|
||||
|
||||
/* adding layer to the timeline will trigger track selection, this */
|
||||
GST_DEBUG ("Add the layer to the timeline");
|
||||
fail_unless (ges_timeline_add_layer (timeline, layer));
|
||||
/* The timeline steals our reference to the layer */
|
||||
|
@ -603,70 +646,121 @@ GST_START_TEST (test_ges_timeline_multiple_tracks)
|
|||
|
||||
layers = ges_timeline_get_layers (timeline);
|
||||
fail_unless (g_list_find (layers, layer) != NULL);
|
||||
g_list_foreach (layers, (GFunc) gst_object_unref, NULL);
|
||||
g_list_free (layers);
|
||||
g_list_free_full (layers, gst_object_unref);
|
||||
|
||||
assert_equals_int (st_data.num_unrecognised, 0);
|
||||
|
||||
/* Make sure the associated TrackElements are in the Track */
|
||||
trackelements = GES_CONTAINER_CHILDREN (s1);
|
||||
fail_unless (trackelements != NULL);
|
||||
t1 = GES_TRACK_ELEMENT ((trackelements)->data);
|
||||
for (tmp = trackelements; tmp; tmp = tmp->next) {
|
||||
/* There are 3 references held:
|
||||
* 1 by the clip
|
||||
* 1 by the track
|
||||
* 1 by the timeline */
|
||||
ASSERT_OBJECT_REFCOUNT (GES_TRACK_ELEMENT (tmp->data), "trackelement", 3);
|
||||
fail_unless (ges_track_element_get_track (tmp->data) == track1);
|
||||
}
|
||||
gst_object_ref (t1);
|
||||
/* There are 3 references held:
|
||||
* 1 by the container
|
||||
* 1 by the track
|
||||
* 1 by the timeline
|
||||
* 1 added by ourselves above (gst_object_ref (t1)) */
|
||||
ASSERT_OBJECT_REFCOUNT (t1, "trackelement", 4);
|
||||
assert_num_children (s1, 1);
|
||||
el = GES_CONTAINER_CHILDREN (s1)->data;
|
||||
fail_unless (GES_IS_SOURCE (el));
|
||||
fail_unless (ges_track_element_get_track (el) == track1);
|
||||
ASSERT_OBJECT_REFCOUNT (el, "1 timeline + 1 track + 1 clip", 3);
|
||||
/* called once for source */
|
||||
assert_equals_int (st_data.num_calls[0], 1);
|
||||
|
||||
/* 2 sources + 4 effects */
|
||||
assert_num_children (s2, 6);
|
||||
trackelements = GES_CONTAINER_CHILDREN (s2);
|
||||
fail_unless (trackelements != NULL);
|
||||
t2 = GES_TRACK_ELEMENT (trackelements->data);
|
||||
for (tmp = trackelements; tmp; tmp = tmp->next) {
|
||||
/* There are 3 references held:
|
||||
* 1 by the clip
|
||||
* 1 by the track
|
||||
* 1 by the timeline */
|
||||
ASSERT_OBJECT_REFCOUNT (GES_TRACK_ELEMENT (tmp->data), "trackelement", 3);
|
||||
fail_unless (ges_track_element_get_track (tmp->data) == track2);
|
||||
}
|
||||
gst_object_ref (t2);
|
||||
/* There are 3 references held:
|
||||
* 1 by the container
|
||||
* 1 by the track
|
||||
* 1 by the timeline
|
||||
* 1 added by ourselves above (gst_object_ref (t2)) */
|
||||
ASSERT_OBJECT_REFCOUNT (t2, "t2", 4);
|
||||
/* sources at the end */
|
||||
el = g_list_nth_data (trackelements, 5);
|
||||
fail_unless (GES_IS_SOURCE (el));
|
||||
el2 = g_list_nth_data (trackelements, 4);
|
||||
fail_unless (GES_IS_SOURCE (el2));
|
||||
|
||||
/* font-desc is originally "", but on setting switches to Normal, so we
|
||||
* set it explicitly */
|
||||
ges_timeline_element_set_child_properties (GES_TIMELINE_ELEMENT (el),
|
||||
"font-desc", "Normal", NULL);
|
||||
assert_equal_children_properties (el, el2);
|
||||
assert_equal_bindings (el, el2);
|
||||
|
||||
assert_equals_int (GES_TIMELINE_ELEMENT_PRIORITY (el),
|
||||
GES_TIMELINE_ELEMENT_PRIORITY (el2));
|
||||
|
||||
/* check one in each track */
|
||||
fail_unless (ges_track_element_get_track (el)
|
||||
!= ges_track_element_get_track (el2));
|
||||
fail_unless (ges_track_element_get_track (el) == track1
|
||||
|| ges_track_element_get_track (el2) == track1);
|
||||
fail_unless (ges_track_element_get_track (el) == track2
|
||||
|| ges_track_element_get_track (el2) == track2);
|
||||
|
||||
/* effects */
|
||||
e_copy = NULL;
|
||||
for (tmp = trackelements; tmp; tmp = tmp->next) {
|
||||
el = tmp->data;
|
||||
ASSERT_OBJECT_REFCOUNT (el, "1 timeline + 1 track + 1 clip", 3);
|
||||
if (GES_IS_BASE_EFFECT (el)) {
|
||||
if (el == e1) {
|
||||
fail_if (found_e1);
|
||||
found_e1 = TRUE;
|
||||
} else if (el == e2) {
|
||||
fail_if (found_e2);
|
||||
found_e2 = TRUE;
|
||||
} else if (el == e3) {
|
||||
fail_if (found_e3);
|
||||
found_e3 = TRUE;
|
||||
} else {
|
||||
fail_if (e_copy);
|
||||
e_copy = el;
|
||||
}
|
||||
}
|
||||
}
|
||||
fail_unless (found_e1);
|
||||
fail_unless (found_e2);
|
||||
fail_unless (found_e3);
|
||||
fail_unless (e_copy);
|
||||
|
||||
fail_unless (ges_track_element_get_track (e1) == track1);
|
||||
fail_unless (ges_track_element_get_track (e3) == track2);
|
||||
|
||||
assert_equal_children_properties (e2, e_copy);
|
||||
assert_equal_bindings (e2, e_copy);
|
||||
|
||||
/* check one in each track */
|
||||
fail_unless (ges_track_element_get_track (e2)
|
||||
!= ges_track_element_get_track (e_copy));
|
||||
fail_unless (ges_track_element_get_track (e2) == track1
|
||||
|| ges_track_element_get_track (e_copy) == track1);
|
||||
fail_unless (ges_track_element_get_track (e2) == track2
|
||||
|| ges_track_element_get_track (e_copy) == track2);
|
||||
|
||||
/* e2 copy placed next to e2 in top effect list */
|
||||
assert_equals_int (0,
|
||||
ges_clip_get_top_effect_index (s2, GES_BASE_EFFECT (e1)));
|
||||
assert_equals_int (1,
|
||||
ges_clip_get_top_effect_index (s2, GES_BASE_EFFECT (e2)));
|
||||
assert_equals_int (2,
|
||||
ges_clip_get_top_effect_index (s2, GES_BASE_EFFECT (e_copy)));
|
||||
assert_equals_int (3,
|
||||
ges_clip_get_top_effect_index (s2, GES_BASE_EFFECT (e3)));
|
||||
|
||||
/* called 4 times: 1 for source, and 1 for each effect (3) */
|
||||
assert_equals_int (st_data.num_calls[1], 4);
|
||||
|
||||
assert_num_children (s3, 1);
|
||||
el = GES_CONTAINER_CHILDREN (s3)->data;
|
||||
fail_unless (GES_IS_SOURCE (el));
|
||||
fail_unless (ges_track_element_get_track (el) == track2);
|
||||
ASSERT_OBJECT_REFCOUNT (el, "1 timeline + 1 track + 1 clip", 3);
|
||||
/* called once for source */
|
||||
assert_equals_int (st_data.num_calls[2], 1);
|
||||
|
||||
/* one child but no track */
|
||||
assert_num_children (s4, 1);
|
||||
el = GES_CONTAINER_CHILDREN (s4)->data;
|
||||
fail_unless (GES_IS_SOURCE (el));
|
||||
fail_unless (ges_track_element_get_track (el) == NULL);
|
||||
ASSERT_OBJECT_REFCOUNT (el, "1 clip", 1);
|
||||
/* called once for source (where no track was selected) */
|
||||
assert_equals_int (st_data.num_calls[0], 1);
|
||||
|
||||
/* 2 sources + 2 effects */
|
||||
assert_num_in_track (track1, 4);
|
||||
assert_num_in_track (track2, 4);
|
||||
|
||||
trackelements = GES_CONTAINER_CHILDREN (s3);
|
||||
fail_unless (trackelements != NULL);
|
||||
t3 = GES_TRACK_ELEMENT (trackelements->data);
|
||||
for (tmp = trackelements; tmp; tmp = tmp->next) {
|
||||
/* There are 3 references held:
|
||||
* 1 by the clip
|
||||
* 1 by the track
|
||||
* 1 by the timeline */
|
||||
ASSERT_OBJECT_REFCOUNT (GES_TRACK_ELEMENT (tmp->data), "trackelement", 3);
|
||||
fail_unless (ges_track_element_get_track (tmp->data) == track1);
|
||||
}
|
||||
gst_object_ref (t3);
|
||||
/* There are 3 references held:
|
||||
* 1 by the container
|
||||
* 1 by the track
|
||||
* 1 by the timeline
|
||||
* 1 added by ourselves above (gst_object_ref (t3)) */
|
||||
ASSERT_OBJECT_REFCOUNT (t3, "t3", 4);
|
||||
|
||||
gst_object_unref (t1);
|
||||
gst_object_unref (t2);
|
||||
gst_object_unref (t3);
|
||||
|
||||
gst_object_unref (timeline);
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -106,6 +106,23 @@ G_STMT_START { \
|
|||
GST_TIME_ARGS (_MAX_DURATION(obj)), GST_TIME_ARGS (max_duration)); \
|
||||
}
|
||||
|
||||
#define assert_num_in_track(track, val) \
|
||||
{ \
|
||||
GList *tmp = ges_track_get_elements (track); \
|
||||
guint length = g_list_length (tmp); \
|
||||
fail_unless (length == val, "Track %" GST_PTR_FORMAT \
|
||||
" contains %u track elements, rather than %u", track, length, val); \
|
||||
g_list_free_full (tmp, gst_object_unref); \
|
||||
}
|
||||
|
||||
#define assert_num_children(clip, cmp) \
|
||||
{ \
|
||||
guint num_children = g_list_length (GES_CONTAINER_CHILDREN (clip)); \
|
||||
fail_unless (cmp == num_children, \
|
||||
"clip %s contains %u children rather than %u", \
|
||||
GES_TIMELINE_ELEMENT_NAME (clip), num_children, cmp); \
|
||||
}
|
||||
|
||||
/* assert that the time property (start, duration or in-point) is the
|
||||
* same as @cmp for the clip and all its children */
|
||||
#define assert_clip_children_time_val(clip, property, cmp) \
|
||||
|
@ -142,6 +159,27 @@ G_STMT_START { \
|
|||
GES_TIMELINE_ELEMENT_LAYER_PRIORITY (clip), layer_prio); \
|
||||
}
|
||||
|
||||
#define assert_layer(clip, layer) \
|
||||
{ \
|
||||
GESLayer *tmp_layer = ges_clip_get_layer (GES_CLIP (clip)); \
|
||||
fail_unless (tmp_layer == GES_LAYER (layer), "clip %s belongs to " \
|
||||
"layer %u (timeline %" GST_PTR_FORMAT ") rather than layer %u " \
|
||||
"(timeline %" GST_PTR_FORMAT ")", \
|
||||
tmp_layer ? ges_layer_get_priority (tmp_layer) : 0, \
|
||||
tmp_layer ? tmp_layer->timeline : NULL, \
|
||||
layer ? ges_layer_get_priority (GES_LAYER (layer)) : 0, \
|
||||
layer ? GES_LAYER (layer)->timeline : NULL); \
|
||||
if (tmp_layer) \
|
||||
gst_object_unref (tmp_layer); \
|
||||
if (layer) { \
|
||||
GList *layer_clips = ges_layer_get_clips (GES_LAYER (layer)); \
|
||||
fail_unless (g_list_find (layer_clips, clip), "clip %s not found " \
|
||||
"in layer %u (timeline %" GST_PTR_FORMAT ")", \
|
||||
ges_layer_get_priority (GES_LAYER (layer)), layer->timeline); \
|
||||
g_list_free_full (layer_clips, gst_object_unref); \
|
||||
} \
|
||||
}
|
||||
|
||||
/* test that the two property lists contain the same properties the same
|
||||
* number of times */
|
||||
#define assert_property_list_match(list1, len1, list2, len2) \
|
||||
|
@ -188,5 +226,144 @@ G_STMT_START { \
|
|||
g_free (count_list1); \
|
||||
}
|
||||
|
||||
#define assert_equal_children_properties(el1, el2) \
|
||||
{ \
|
||||
guint i, num1, num2; \
|
||||
const gchar *name1 = GES_TIMELINE_ELEMENT_NAME (el1); \
|
||||
const gchar *name2 = GES_TIMELINE_ELEMENT_NAME (el2); \
|
||||
GParamSpec **el_props1 = ges_timeline_element_list_children_properties ( \
|
||||
GES_TIMELINE_ELEMENT (el1), &num1); \
|
||||
GParamSpec **el_props2 = ges_timeline_element_list_children_properties ( \
|
||||
GES_TIMELINE_ELEMENT (el2), &num2); \
|
||||
assert_property_list_match (el_props1, num1, el_props2, num2); \
|
||||
\
|
||||
for (i = 0; i < num1; i++) { \
|
||||
gchar *ser1, *ser2; \
|
||||
GParamSpec *prop = el_props1[i]; \
|
||||
GValue val1 = G_VALUE_INIT, val2 = G_VALUE_INIT; \
|
||||
/* name property can be different */ \
|
||||
if (g_strcmp0 (prop->name, "name") == 0) \
|
||||
continue; \
|
||||
if (g_strcmp0 (prop->name, "parent") == 0) \
|
||||
continue; \
|
||||
g_value_init (&val1, prop->value_type); \
|
||||
g_value_init (&val2, prop->value_type); \
|
||||
ges_timeline_element_get_child_property_by_pspec ( \
|
||||
GES_TIMELINE_ELEMENT (el1), prop, &val1); \
|
||||
ges_timeline_element_get_child_property_by_pspec ( \
|
||||
GES_TIMELINE_ELEMENT (el2), prop, &val2); \
|
||||
ser1 = gst_value_serialize (&val1); \
|
||||
ser2 = gst_value_serialize (&val2); \
|
||||
fail_unless (gst_value_compare (&val1, &val2) == GST_VALUE_EQUAL, \
|
||||
"Child property '%s' for %s does not match that for %s (%s vs %s)", \
|
||||
prop->name, name1, name2, ser1, ser2); \
|
||||
g_free (ser1); \
|
||||
g_free (ser2); \
|
||||
g_value_unset (&val1); \
|
||||
g_value_unset (&val2); \
|
||||
} \
|
||||
free_children_properties (el_props1, num1); \
|
||||
free_children_properties (el_props2, num2); \
|
||||
}
|
||||
|
||||
#define assert_equal_bindings(el1, el2) \
|
||||
{ \
|
||||
guint i, num1, num2; \
|
||||
const gchar *name1 = GES_TIMELINE_ELEMENT_NAME (el1); \
|
||||
const gchar *name2 = GES_TIMELINE_ELEMENT_NAME (el2); \
|
||||
GParamSpec **props1 = ges_timeline_element_list_children_properties ( \
|
||||
GES_TIMELINE_ELEMENT (el1), &num1); \
|
||||
GParamSpec **props2 = ges_timeline_element_list_children_properties ( \
|
||||
GES_TIMELINE_ELEMENT (el2), &num2); \
|
||||
assert_property_list_match (props1, num1, props2, num2); \
|
||||
\
|
||||
for (i = 0; i < num1; i++) { \
|
||||
const gchar *prop = props1[i]->name; \
|
||||
GList *tmp1, *tmp2; \
|
||||
GList *timed_vals1, *timed_vals2; \
|
||||
GObject *object1, *object2; \
|
||||
gboolean abs1, abs2; \
|
||||
GstControlSource *source1, *source2; \
|
||||
GstInterpolationMode mode1, mode2; \
|
||||
GstControlBinding *binding1, *binding2; \
|
||||
guint j; \
|
||||
\
|
||||
binding1 = ges_track_element_get_control_binding ( \
|
||||
GES_TRACK_ELEMENT (el1), prop); \
|
||||
binding2 = ges_track_element_get_control_binding ( \
|
||||
GES_TRACK_ELEMENT (el2), prop); \
|
||||
if (binding1 == NULL) { \
|
||||
fail_unless (binding2 == NULL, "%s has a binding for property " \
|
||||
" '%s', whilst %s does not", name2, prop, name1); \
|
||||
continue; \
|
||||
} \
|
||||
if (binding2 == NULL) { \
|
||||
fail_unless (binding1 == NULL, "%s has a binding for property " \
|
||||
"'%s', whilst %s does not", name1, prop, name2); \
|
||||
continue; \
|
||||
} \
|
||||
\
|
||||
fail_unless (G_OBJECT_TYPE (binding1) == GST_TYPE_DIRECT_CONTROL_BINDING, \
|
||||
"%s binding for property '%s' is not a direct control binding, " \
|
||||
"so cannot be handled", prop, name1); \
|
||||
fail_unless (G_OBJECT_TYPE (binding2) == GST_TYPE_DIRECT_CONTROL_BINDING, \
|
||||
"%s binding for property '%s' is not a direct control binding, " \
|
||||
"so cannot be handled", prop, name2); \
|
||||
\
|
||||
g_object_get (G_OBJECT (binding1), "control-source", &source1, \
|
||||
"absolute", &abs1, "object", &object1, NULL); \
|
||||
g_object_get (G_OBJECT (binding2), "control-source", &source2, \
|
||||
"absolute", &abs2, "object", &object2, NULL); \
|
||||
\
|
||||
fail_unless (G_OBJECT_TYPE (object1) == G_OBJECT_TYPE (object2), \
|
||||
"The child object for property '%s' for %s and %s correspond " \
|
||||
"to different object types (%s vs %s)", prop, name1, name2, \
|
||||
G_OBJECT_TYPE_NAME (object1), G_OBJECT_TYPE_NAME (object2)); \
|
||||
gst_object_unref (object1); \
|
||||
gst_object_unref (object2); \
|
||||
\
|
||||
fail_unless (abs1 == abs2, "control biding for property '%s' " \
|
||||
" is %s absolute for %s, but %s absolute for %s", prop, \
|
||||
abs1 ? "" : "not", name1, abs2 ? "" : "not", name2); \
|
||||
\
|
||||
fail_unless (GST_IS_INTERPOLATION_CONTROL_SOURCE (source1), \
|
||||
"%s does not have an interpolation control source for " \
|
||||
"property '%s', so cannot be handled", name1, prop); \
|
||||
fail_unless (GST_IS_INTERPOLATION_CONTROL_SOURCE (source2), \
|
||||
"%s does not have an interpolation control source for " \
|
||||
"property '%s', so cannot be handled", name2, prop); \
|
||||
g_object_get (G_OBJECT (source1), "mode", &mode1, NULL); \
|
||||
g_object_get (G_OBJECT (source2), "mode", &mode2, NULL); \
|
||||
fail_unless (mode1 == mode2, "control source for property '%s' " \
|
||||
"has different modes for %s and %s (%i vs %i)", prop, \
|
||||
name1, name2, mode1, mode2); \
|
||||
\
|
||||
timed_vals1 = gst_timed_value_control_source_get_all ( \
|
||||
GST_TIMED_VALUE_CONTROL_SOURCE (source1)); \
|
||||
timed_vals2 = gst_timed_value_control_source_get_all ( \
|
||||
GST_TIMED_VALUE_CONTROL_SOURCE (source2)); \
|
||||
\
|
||||
for (j = 0, tmp1 = timed_vals1, tmp2 = timed_vals2; tmp1 && tmp2; \
|
||||
j++, tmp1 = tmp1->next, tmp2 = tmp2->next) { \
|
||||
GstTimedValue *val1 = tmp1->data, *val2 = tmp2->data; \
|
||||
fail_unless (val1->timestamp == val2->timestamp && \
|
||||
val1->value == val2->value, "The %uth timed value for property " \
|
||||
"'%s' is different for %s and %s: (%" G_GUINT64_FORMAT ": %g) vs " \
|
||||
"(%" G_GUINT64_FORMAT ": %g)", j, prop, name1, name2, \
|
||||
val1->timestamp, val1->value, val2->timestamp, val2->value); \
|
||||
} \
|
||||
fail_unless (tmp1 == NULL, "Found too many timed values for " \
|
||||
"property '%s' for %s", prop, name1); \
|
||||
fail_unless (tmp2 == NULL, "Found too many timed values for " \
|
||||
"property '%s' for %s", prop, name2); \
|
||||
\
|
||||
g_list_free (timed_vals1); \
|
||||
g_list_free (timed_vals2); \
|
||||
gst_object_unref (source1); \
|
||||
gst_object_unref (source2); \
|
||||
} \
|
||||
free_children_properties (props1, num1); \
|
||||
free_children_properties (props2, num2); \
|
||||
}
|
||||
|
||||
void print_timeline(GESTimeline *timeline);
|
||||
|
|
Loading…
Reference in a new issue