(GST_CLOCK_TIME_IS_VALID (a) ? \
(GST_CLOCK_TIME_IS_VALID (b) ? MIN (a, b) : a) : b) \
-#define _CLOCK_TIME_IS_LESS(first, second) \
- (GST_CLOCK_TIME_IS_VALID (first) && (!GST_CLOCK_TIME_IS_VALID (second) \
- || first < second))
-
/****************************************************
* duration-limit *
****************************************************/
GST_INFO_OBJECT (self, "duration-limit for the clip is %"
GST_TIME_FORMAT, GST_TIME_ARGS (duration_limit));
- if (_CLOCK_TIME_IS_LESS (duration_limit, element->duration)
+ if (GES_CLOCK_TIME_IS_LESS (duration_limit, element->duration)
&& !GES_TIMELINE_ELEMENT_BEING_EDITED (self)) {
gboolean res;
if (element->timeline)
res = timeline_tree_trim (timeline_get_tree (element->timeline),
element, 0, GST_CLOCK_DIFF (duration_limit, element->duration),
- GES_EDGE_END, 0);
+ GES_EDGE_END, 0, NULL);
else
res = ges_timeline_element_set_duration (element, duration_limit);
/* transfer full of child_data */
static gboolean
-_can_update_duration_limit (GESClip * self, GList * child_data)
+_can_update_duration_limit (GESClip * self, GList * child_data, GError ** error)
{
GESTimeline *timeline = GES_TIMELINE_ELEMENT_TIMELINE (self);
GstClockTime duration = _calculate_duration_limit (self, child_data);
GESTimelineElement *element = GES_TIMELINE_ELEMENT (self);
- if (_CLOCK_TIME_IS_LESS (duration, element->duration)) {
+ if (GES_CLOCK_TIME_IS_LESS (duration, element->duration)) {
/* NOTE: timeline would normally not be NULL at this point */
if (timeline
&& !timeline_tree_can_move_element (timeline_get_tree (timeline),
element, ges_timeline_element_get_layer_priority (element),
- element->start, duration)) {
+ element->start, duration, error)) {
return FALSE;
}
}
gboolean
ges_clip_can_set_priority_of_child (GESClip * clip, GESTrackElement * child,
- guint32 priority)
+ guint32 priority, GError ** error)
{
GList *child_data;
DurationLimitData *data;
child_data = _duration_limit_data_list_with_data (clip, data);
- if (!_can_update_duration_limit (clip, child_data)) {
+ if (!_can_update_duration_limit (clip, child_data, error)) {
GST_INFO_OBJECT (clip, "Cannot move the child %" GES_FORMAT " from "
"priority %" G_GUINT32_FORMAT " to %" G_GUINT32_FORMAT " because "
"the duration-limit cannot be adjusted", GES_ARGS (child),
****************************************************/
static gboolean
-_can_set_inpoint_of_core_children (GESClip * clip, GstClockTime inpoint)
+_can_set_inpoint_of_core_children (GESClip * clip, GstClockTime inpoint,
+ GError ** error)
{
GList *tmp;
GList *child_data = NULL;
_duration_limit_data_new (GES_TRACK_ELEMENT (child));
if (_IS_CORE_INTERNAL_SOURCE_CHILD (child)) {
- if (GST_CLOCK_TIME_IS_VALID (child->maxduration)
- && child->maxduration < inpoint) {
+ if (GES_CLOCK_TIME_IS_LESS (child->maxduration, inpoint)) {
GST_INFO_OBJECT (clip, "Cannot set the in-point from %"
- G_GUINT64_FORMAT " to %" G_GUINT64_FORMAT " because it would "
+ GST_TIME_FORMAT " to %" GST_TIME_FORMAT " because it would "
"cause the in-point of its core child %" GES_FORMAT
- " to exceed its max-duration", _INPOINT (clip), inpoint,
- GES_ARGS (child));
+ " to exceed its max-duration", GST_TIME_ARGS (_INPOINT (clip)),
+ GST_TIME_ARGS (inpoint), GES_ARGS (child));
+ g_set_error (error, GES_ERROR, GES_ERROR_NOT_ENOUGH_INTERNAL_CONTENT,
+ "Cannot set the in-point of \"%s\" to %" GST_TIME_FORMAT
+ " because it would exceed the max-duration of %" GST_TIME_FORMAT
+ " for the child \"%s\"", GES_TIMELINE_ELEMENT_NAME (clip),
+ GST_TIME_ARGS (inpoint), GST_TIME_ARGS (child->maxduration),
+ child->name);
_duration_limit_data_free (data);
g_list_free_full (child_data, _duration_limit_data_free);
child_data = g_list_prepend (child_data, data);
}
- if (!_can_update_duration_limit (clip, child_data)) {
- GST_INFO_OBJECT (clip, "Cannot set the in-point from %" G_GUINT64_FORMAT
- " to %" G_GUINT64_FORMAT " because the duration-limit cannot be "
- "adjusted", _INPOINT (clip), inpoint);
+ if (!_can_update_duration_limit (clip, child_data, error)) {
+ GST_INFO_OBJECT (clip, "Cannot set the in-point from %" GST_TIME_FORMAT
+ " to %" GST_TIME_FORMAT " because the duration-limit cannot be "
+ "adjusted", GST_TIME_ARGS (_INPOINT (clip)), GST_TIME_ARGS (inpoint));
return FALSE;
}
* its children have a max-duration below it */
gboolean
ges_clip_can_set_inpoint_of_child (GESClip * clip, GESTrackElement * child,
- GstClockTime inpoint)
+ GstClockTime inpoint, GError ** error)
{
/* don't bother checking if we are setting the value */
if (clip->priv->setting_inpoint)
child_data = _duration_limit_data_list_with_data (clip, data);
- if (!_can_update_duration_limit (clip, child_data)) {
+ if (!_can_update_duration_limit (clip, child_data, error)) {
GST_INFO_OBJECT (clip, "Cannot set the in-point of non-core child %"
- GES_FORMAT " from %" G_GUINT64_FORMAT " to %" G_GUINT64_FORMAT
+ GES_FORMAT " from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT
" because the duration-limit cannot be adjusted", GES_ARGS (child),
- _INPOINT (child), inpoint);
+ GST_TIME_ARGS (_INPOINT (child)), GST_TIME_ARGS (inpoint));
return FALSE;
}
/* setting the in-point of a core child will shift the in-point of all
* core children with an internal source */
- return _can_set_inpoint_of_core_children (clip, inpoint);
+ return _can_set_inpoint_of_core_children (clip, inpoint, error);
}
/* returns TRUE if duration-limit needs to be updated */
gboolean
ges_clip_can_set_max_duration_of_child (GESClip * clip, GESTrackElement * child,
- GstClockTime max_duration)
+ GstClockTime max_duration, GError ** error)
{
GList *child_data;
DurationLimitData *data;
child_data = _duration_limit_data_list_with_data (clip, data);
- if (!_can_update_duration_limit (clip, child_data)) {
+ if (!_can_update_duration_limit (clip, child_data, error)) {
GST_INFO_OBJECT (clip, "Cannot set the max-duration of child %"
- GES_FORMAT " from %" G_GUINT64_FORMAT " to %" G_GUINT64_FORMAT
+ GES_FORMAT " from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT
" because the duration-limit cannot be adjusted", GES_ARGS (child),
- _MAXDURATION (child), max_duration);
+ GST_TIME_ARGS (_MAXDURATION (child)), GST_TIME_ARGS (max_duration));
return FALSE;
}
gboolean
ges_clip_can_set_active_of_child (GESClip * clip, GESTrackElement * child,
- gboolean active)
+ gboolean active, GError ** error)
{
GESTrack *track = ges_track_element_get_track (child);
gboolean is_core = _IS_CORE_CHILD (child);
}
}
- if (!_can_update_duration_limit (clip, child_data)) {
+ if (!_can_update_duration_limit (clip, child_data, error)) {
GST_INFO_OBJECT (clip, "Cannot set the active of child %" GES_FORMAT
" from %i to %i because the duration-limit cannot be adjusted",
GES_ARGS (child), ges_track_element_is_active (child), active);
gboolean
ges_clip_can_set_track_of_child (GESClip * clip, GESTrackElement * child,
- GESTrack * track)
+ GESTrack * track, GError ** error)
{
GList *child_data;
DurationLimitData *data;
if (current_track == track)
return TRUE;
+ /* NOTE: we consider the following error cases programming errors by
+ * the user */
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_non_core (clip, current_track)) {
- GST_INFO_OBJECT (clip, "Cannot move the core child %" GES_FORMAT
+ GST_WARNING_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);
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
+ GST_WARNING_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
+ GST_WARNING_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,
* placed in a track that already has a core child */
if (_IS_CORE_CHILD (child)) {
if (core) {
- GST_INFO_OBJECT (clip, "Cannot move the core child %" GES_FORMAT
+ GST_WARNING_OBJECT (clip, "Cannot move the core child %" GES_FORMAT
" to the track %" GST_PTR_FORMAT " because it contains a "
"core sibling %" GES_FORMAT, GES_ARGS (child), track,
GES_ARGS (core));
}
} else {
if (!core) {
- GST_INFO_OBJECT (clip, "Cannot move the non-core child %"
+ GST_WARNING_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;
child_data = _duration_limit_data_list_with_data (clip, data);
- if (!_can_update_duration_limit (clip, child_data)) {
+ if (!_can_update_duration_limit (clip, child_data, error)) {
GST_INFO_OBJECT (clip, "Cannot move the child %" GES_FORMAT " from "
"track %" GST_PTR_FORMAT " to track %" GST_PTR_FORMAT " because "
"the duration-limit cannot be adjusted", GES_ARGS (child),
static gboolean
_set_inpoint (GESTimelineElement * element, GstClockTime inpoint)
{
- if (!_can_set_inpoint_of_core_children (GES_CLIP (element), inpoint)) {
+ if (!_can_set_inpoint_of_core_children (GES_CLIP (element), inpoint, NULL)) {
GST_WARNING_OBJECT (element, "Cannot set the in-point to %"
GST_TIME_FORMAT, GST_TIME_ARGS (inpoint));
return FALSE;
child_data = g_list_prepend (child_data, data);
}
- if (!_can_update_duration_limit (self, child_data)) {
+ if (!_can_update_duration_limit (self, child_data, NULL)) {
GST_WARNING_OBJECT (self, "Cannot set the max-duration from %"
- G_GUINT64_FORMAT " to %" G_GUINT64_FORMAT " because the "
- "duration-limit cannot be adjusted", element->maxduration, maxduration);
+ GST_TIME_FORMAT " to %" GST_TIME_FORMAT " because the "
+ "duration-limit cannot be adjusted",
+ GST_TIME_ARGS (element->maxduration), GST_TIME_ARGS (maxduration));
return FALSE;
}
child_data = _duration_limit_data_list_with_data (self, data);
- if (!_can_update_duration_limit (self, child_data)) {
+ if (!_can_update_duration_limit (self, child_data, NULL)) {
GST_WARNING_OBJECT (self, "Cannot add core %" GES_FORMAT " as a "
"child because the duration-limit cannot be adjusted",
GES_ARGS (element));
data->priority++;
}
- if (!_can_update_duration_limit (self, child_data)) {
+ if (!_can_update_duration_limit (self, child_data, NULL)) {
GST_WARNING_OBJECT (self, "Cannot add effect %" GES_FORMAT " as "
"a child because the duration-limit cannot be adjusted",
GES_ARGS (element));
g_list_prepend (child_data, _duration_limit_data_new (child));
}
- if (!_can_update_duration_limit (self, child_data)) {
+ if (!_can_update_duration_limit (self, child_data, NULL)) {
GST_WARNING_OBJECT (self, "Cannot remove the child %" GES_FORMAT
" because the duration-limit cannot be adjusted", GES_ARGS (el));
return FALSE;
}
/**
- * ges_clip_move_to_layer:
+ * ges_clip_move_to_layer_full:
* @clip: A #GESClip
* @layer: The new layer
+ * @error: (nullable): Return location for an error
*
* Moves a clip to a new layer. If the clip already exists in a layer, it
* is first removed from its current layer before being added to the new
* Returns: %TRUE if @clip was successfully moved to @layer.
*/
gboolean
-ges_clip_move_to_layer (GESClip * clip, GESLayer * layer)
+ges_clip_move_to_layer_full (GESClip * clip, GESLayer * layer, GError ** error)
{
gboolean ret = FALSE;
GESLayer *current_layer;
g_return_val_if_fail (GES_IS_CLIP (clip), FALSE);
g_return_val_if_fail (GES_IS_LAYER (layer), FALSE);
+ g_return_val_if_fail (!error || !*error, FALSE);
element = GES_TIMELINE_ELEMENT (clip);
current_layer = clip->priv->layer;
/* move to new layer, also checks moving of toplevel */
return timeline_tree_move (timeline_get_tree (layer->timeline),
element, (gint64) ges_layer_get_priority (current_layer) -
- (gint64) ges_layer_get_priority (layer), 0, GES_EDGE_NONE, 0);
+ (gint64) ges_layer_get_priority (layer), 0, GES_EDGE_NONE, 0, error);
}
gst_object_ref (clip);
return ret && (clip->priv->layer == layer);
}
+/**
+ * ges_clip_move_to_layer:
+ * @clip: A #GESClip
+ * @layer: The new layer
+ *
+ * See ges_clip_move_to_layer_full(), which also gives an error.
+ *
+ * Returns: %TRUE if @clip was successfully moved to @layer.
+ */
+gboolean
+ges_clip_move_to_layer (GESClip * clip, GESLayer * layer)
+{
+ return ges_clip_move_to_layer_full (clip, layer, NULL);
+}
+
/**
* ges_clip_find_track_element:
* @clip: A #GESClip
}
/**
- * ges_clip_set_top_effect_index:
+ * ges_clip_set_top_effect_index_full:
* @clip: A #GESClip
* @effect: An effect within @clip to move
* @newindex: The index for @effect in @clip
+ * @error: (nullable): Return location for an error
*
* Set the index of an effect within the clip. See
* ges_clip_get_top_effect_index(). The new index must be an existing
* Returns: %TRUE if @effect was successfully moved to @newindex.
*/
gboolean
-ges_clip_set_top_effect_index (GESClip * clip, GESBaseEffect * effect,
- guint newindex)
+ges_clip_set_top_effect_index_full (GESClip * clip, GESBaseEffect * effect,
+ guint newindex, GError ** error)
{
gint inc;
GList *top_effects, *tmp;
g_return_val_if_fail (GES_IS_CLIP (clip), FALSE);
g_return_val_if_fail (GES_IS_BASE_EFFECT (effect), FALSE);
+ g_return_val_if_fail (!error || !*error, FALSE);
if (!_is_added_effect (clip, effect))
return FALSE;
child_data = g_list_prepend (child_data, data);
}
- if (!_can_update_duration_limit (clip, child_data)) {
- GST_WARNING_OBJECT (clip, "Cannot move top effect %" GES_FORMAT
+ if (!_can_update_duration_limit (clip, child_data, error)) {
+ GST_INFO_OBJECT (clip, "Cannot move top effect %" GES_FORMAT
" to index %i because the duration-limit cannot adjust",
GES_ARGS (effect), newindex);
return FALSE;
}
/**
- * ges_clip_split:
+ * ges_clip_set_top_effect_index:
+ * @clip: A #GESClip
+ * @effect: An effect within @clip to move
+ * @newindex: The index for @effect in @clip
+ *
+ * See ges_clip_set_top_effect_index_full(), which also gives an error.
+ *
+ * Returns: %TRUE if @effect was successfully moved to @newindex.
+ */
+gboolean
+ges_clip_set_top_effect_index (GESClip * clip, GESBaseEffect * effect,
+ guint newindex)
+{
+ return ges_clip_set_top_effect_index_full (clip, effect, newindex, NULL);
+}
+
+/**
+ * ges_clip_split_full:
* @clip: The #GESClip to split
- * @position: The timeline position at which to perform the split
+ * @position: The timeline position at which to perform the split, between
+ * the start and end of the clip
+ * @error: (nullable): Return location for an error
*
* Splits a clip at the given timeline position into two clips. The clip
* must already have a #GESClip:layer.
* from the splitting @clip, or %NULL if @clip can't be split.
*/
GESClip *
-ges_clip_split (GESClip * clip, guint64 position)
+ges_clip_split_full (GESClip * clip, guint64 position, GError ** error)
{
GList *tmp, *transitions = NULL;
GESClip *new_object;
GESTimelineElement *element;
GESTimeline *timeline;
GHashTable *track_for_copy;
+ guint32 layer_prio;
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);
+ g_return_val_if_fail (!error || !*error, NULL);
element = GES_TIMELINE_ELEMENT (clip);
timeline = element->timeline;
return NULL;
}
+ layer_prio = ges_timeline_element_get_layer_priority (element);
+
old_duration = position - start;
- if (timeline && !timeline_tree_can_move_element (timeline_get_tree
- (timeline), element,
- ges_timeline_element_get_layer_priority (element),
- start, old_duration)) {
- GST_WARNING_OBJECT (clip,
+ if (timeline
+ && !timeline_tree_can_move_element (timeline_get_tree (timeline), element,
+ layer_prio, start, old_duration, error)) {
+ GST_INFO_OBJECT (clip,
"Can not split %" GES_FORMAT " at %" GST_TIME_FORMAT
- " as timeline would be in an illegal" " state.", GES_ARGS (clip),
+ " as timeline would be in an illegal state.", GES_ARGS (clip),
GST_TIME_ARGS (position));
return NULL;
}
new_duration = duration + start - position;
- if (timeline && !timeline_tree_can_move_element (timeline_get_tree
- (timeline), element,
- ges_timeline_element_get_layer_priority (element),
- position, new_duration)) {
- GST_WARNING_OBJECT (clip,
+ if (timeline
+ && !timeline_tree_can_move_element (timeline_get_tree (timeline), element,
+ layer_prio, position, new_duration, error)) {
+ GST_INFO_OBJECT (clip,
"Can not split %" GES_FORMAT " at %" GST_TIME_FORMAT
- " as timeline would end up in an illegal" " state.", GES_ARGS (clip),
+ " as timeline would be in an illegal state.", GES_ARGS (clip),
GST_TIME_ARGS (position));
return NULL;
}
return new_object;
}
+/**
+ * ges_clip_split:
+ * @clip: The #GESClip to split
+ * @position: The timeline position at which to perform the split
+ *
+ * See ges_clip_split_full(), which also gives an error.
+ *
+ * Returns: (transfer none) (nullable): The newly created clip resulting
+ * from the splitting @clip, or %NULL if @clip can't be split.
+ */
+GESClip *
+ges_clip_split (GESClip * clip, guint64 position)
+{
+ return ges_clip_split_full (clip, position, NULL);
+}
+
/**
* ges_clip_set_supported_formats:
* @clip: A #GESClip
* @clip: A #GESClip
* @child: A child of @clip
* @track: The track to add @child to
- * @err: Return location for an error
+ * @error: (nullable): Return location for an error
*
* Adds the track element child of the clip to a specific track.
*
* 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)
+ GESTrack * track, GError ** error)
{
GESTimeline *timeline;
GESTrackElement *el;
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);
+ g_return_val_if_fail (!error || !*error, NULL);
timeline = GES_TIMELINE_ELEMENT_TIMELINE (clip);
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 %"
+ if (!ges_track_add_element_full (track, el, error)) {
+ GST_INFO_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));
GES_API
GESTrackType ges_clip_get_supported_formats (GESClip *clip);
GES_API
-void ges_clip_set_supported_formats (GESClip *clip, GESTrackType supportedformats);
+void ges_clip_set_supported_formats (GESClip *clip,
+ GESTrackType supportedformats);
GES_API
-GESTrackElement* ges_clip_add_asset (GESClip *clip, GESAsset *asset);
+GESTrackElement* ges_clip_add_asset (GESClip *clip,
+ GESAsset *asset);
GES_API
-GESTrackElement* ges_clip_find_track_element (GESClip *clip, GESTrack *track,
+GESTrackElement* ges_clip_find_track_element (GESClip *clip,
+ GESTrack *track,
GType type);
GES_API
-GList * ges_clip_find_track_elements (GESClip * clip, GESTrack * track,
- GESTrackType track_type, GType type);
+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);
+GESTrackElement * ges_clip_add_child_to_track (GESClip * clip,
+ GESTrackElement * child,
+ GESTrack * track,
+ GError ** error);
/****************************************************
* Layer *
****************************************************/
GES_API
-GESLayer* ges_clip_get_layer (GESClip *clip);
+GESLayer* ges_clip_get_layer (GESClip * clip);
+GES_API
+gboolean ges_clip_move_to_layer (GESClip * clip,
+ GESLayer * layer);
GES_API
-gboolean ges_clip_move_to_layer (GESClip *clip, GESLayer *layer);
+gboolean ges_clip_move_to_layer_full (GESClip * clip,
+ GESLayer * layer,
+ GError ** error);
/****************************************************
* Effects *
****************************************************/
GES_API
-GList* ges_clip_get_top_effects (GESClip *clip);
+GList* ges_clip_get_top_effects (GESClip * clip);
GES_API
-gint ges_clip_get_top_effect_position (GESClip *clip, GESBaseEffect *effect);
+gint ges_clip_get_top_effect_position (GESClip * clip,
+ GESBaseEffect * effect);
GES_API
-gint ges_clip_get_top_effect_index (GESClip *clip, GESBaseEffect *effect);
+gint ges_clip_get_top_effect_index (GESClip * clip,
+ GESBaseEffect * effect);
GES_API
-gboolean ges_clip_set_top_effect_priority (GESClip *clip, GESBaseEffect *effect,
+gboolean ges_clip_set_top_effect_priority (GESClip * clip,
+ GESBaseEffect * effect,
guint newpriority);
GES_API
-gboolean ges_clip_set_top_effect_index (GESClip *clip, GESBaseEffect *effect,
+gboolean ges_clip_set_top_effect_index (GESClip * clip,
+ GESBaseEffect * effect,
guint newindex);
+GES_API
+gboolean ges_clip_set_top_effect_index_full (GESClip * clip,
+ GESBaseEffect * effect,
+ guint newindex,
+ GError ** error);
/****************************************************
* Editing *
****************************************************/
GES_API
-GESClip* ges_clip_split (GESClip *clip, guint64 position);
+GESClip* ges_clip_split (GESClip *clip,
+ guint64 position);
+GES_API
+GESClip* ges_clip_split_full (GESClip *clip,
+ guint64 position,
+ GError ** error);
GES_API
GstClockTime ges_clip_get_timeline_time_from_source_frame (GESClip * clip,
* @GES_ERROR_ASSET_WRONG_ID: The ID passed is malformed
* @GES_ERROR_ASSET_LOADING: An error happened while loading the asset
* @GES_ERROR_FORMATTER_MALFORMED_INPUT_FILE: The formatted files was malformed
+ * @GES_ERROR_INVALID_FRAME_NUMBER: The frame number is invalid
+ * @GES_ERROR_NEGATIVE_LAYER: The operation would lead to a negative
+ * #GES_TIMELINE_ELEMENT_LAYER_PRIORITY
+ * @GES_ERROR_NEGATIVE_TIME: The operation would lead to a negative time.
+ * E.g. for the #GESTimelineElement:start #GESTimelineElement:duration or
+ * #GESTimelineElement:in-point.
+ * @GES_ERROR_NOT_ENOUGH_INTERNAL_CONTENT: Some #GESTimelineElement does
+ * not have a large enough #GESTimelineElement:max-duration to cover the
+ * desired operation
+ * @GES_ERROR_INVALID_OVERLAP_IN_TRACK: The operation would break one of
+ * the overlap conditions for the #GESTimeline
*/
typedef enum
{
GES_ERROR_ASSET_LOADING,
GES_ERROR_FORMATTER_MALFORMED_INPUT_FILE,
GES_ERROR_INVALID_FRAME_NUMBER,
+ GES_ERROR_NEGATIVE_LAYER,
+ GES_ERROR_NEGATIVE_TIME,
+ GES_ERROR_NOT_ENOUGH_INTERNAL_CONTENT,
+ GES_ERROR_INVALID_OVERLAP_IN_TRACK,
} GESError;
-G_END_DECLS
\ No newline at end of file
+G_END_DECLS
return timeline_tree_move (timeline_get_tree (timeline),
element, (gint64) (element->priority) - (gint64) priority, 0,
- GES_EDGE_NONE, 0);
+ GES_EDGE_NONE, 0, NULL);
}
static gboolean
#define _set_duration0 ges_timeline_element_set_duration
#define _set_priority0 ges_timeline_element_set_priority
+#define GES_CLOCK_TIME_IS_LESS(first, second) \
+ (GST_CLOCK_TIME_IS_VALID (first) && (!GST_CLOCK_TIME_IS_VALID (second) \
+ || (first) < (second)))
+
#define DEFAULT_FRAMERATE_N 30
#define DEFAULT_FRAMERATE_D 1
#define DEFAULT_WIDTH 1280
G_GNUC_INTERNAL gboolean
ges_timeline_edit (GESTimeline * timeline, GESTimelineElement * element,
- GList * layers, gint64 new_layer_priority, GESEditMode mode, GESEdge edge,
- guint64 position);
+ gint64 new_layer_priority, GESEditMode mode, GESEdge edge,
+ guint64 position, GError ** error);
G_GNUC_INTERNAL void
timeline_add_group (GESTimeline *timeline,
ges_timeline_set_moving_track_elements (GESTimeline * timeline, gboolean moving);
G_GNUC_INTERNAL gboolean
-ges_timeline_add_clip (GESTimeline * timeline, GESClip * clip);
+ges_timeline_add_clip (GESTimeline * timeline, GESClip * clip, GError ** error);
G_GNUC_INTERNAL void
ges_timeline_remove_clip (GESTimeline * timeline, GESClip * clip);
G_GNUC_INTERNAL void ges_clip_set_moving_from_layer (GESClip *clip, gboolean is_moving);
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, GESTrackElement * child, GstClockTime inpoint);
-G_GNUC_INTERNAL gboolean ges_clip_can_set_max_duration_of_child (GESClip * clip, GESTrackElement * child, GstClockTime max_duration);
-G_GNUC_INTERNAL gboolean ges_clip_can_set_active_of_child (GESClip * clip, GESTrackElement * child, gboolean active);
-G_GNUC_INTERNAL gboolean ges_clip_can_set_priority_of_child (GESClip * clip, GESTrackElement * child, guint32 priority);
-G_GNUC_INTERNAL gboolean ges_clip_can_set_track_of_child (GESClip * clip, GESTrackElement * child, GESTrack * tack);
+G_GNUC_INTERNAL gboolean ges_clip_can_set_inpoint_of_child (GESClip * clip, GESTrackElement * child, GstClockTime inpoint, GError ** error);
+G_GNUC_INTERNAL gboolean ges_clip_can_set_max_duration_of_child (GESClip * clip, GESTrackElement * child, GstClockTime max_duration, GError ** error);
+G_GNUC_INTERNAL gboolean ges_clip_can_set_active_of_child (GESClip * clip, GESTrackElement * child, gboolean active, GError ** error);
+G_GNUC_INTERNAL gboolean ges_clip_can_set_priority_of_child (GESClip * clip, GESTrackElement * child, guint32 priority, GError ** error);
+G_GNUC_INTERNAL gboolean ges_clip_can_set_track_of_child (GESClip * clip, GESTrackElement * child, GESTrack * tack, GError ** error);
G_GNUC_INTERNAL void ges_clip_empty_from_track (GESClip * clip, GESTrack * track);
/****************************************************
* GESTrackElement *
****************************************************/
#define NLE_OBJECT_TRACK_ELEMENT_QUARK (g_quark_from_string ("nle_object_track_element_quark"))
-G_GNUC_INTERNAL gboolean ges_track_element_set_track (GESTrackElement * object, GESTrack * track);
+G_GNUC_INTERNAL gboolean ges_track_element_set_track (GESTrackElement * object, GESTrack * track, GError ** error);
G_GNUC_INTERNAL void ges_track_element_copy_properties (GESTimelineElement * element,
GESTimelineElement * elementcopy);
G_GNUC_INTERNAL void ges_track_element_set_layer_active (GESTrackElement *element, gboolean active);
* GESLayer:auto-transition:
*
* Whether to automatically create a #GESTransitionClip whenever two
- * #GESClip-s overlap in the layer. Specifically, if this is set to
- * %TRUE, then wherever two #GESSource-s (that belong to some clip in
- * the layer) share the same #GESTrackElement:track and the end time of
- * one source exceeds the #GESTimelineElement:start time of the other,
- * there will exist a corresponding #GESTransitionClip in the same
- * layer. These automatic transitions will be created and removed
- * accordingly. Their temporal extent will cover the overlap of the
- * two sources (their #GESTimelineElement:start will be set to the
- * #GESTimelineElement:start of the later source, and their
- * #GESTimelineElement:duration will be the difference between the
- * #GESTimelineElement:start of the later source, and the end time of
- * the earlier source).
- *
- * It may be difficult to use this feature if your timeline has multiple
- * tracks of the same #GESTrack:track-type and you use the
- * #GESTimeline::select-tracks-for-object signal. You will have to
- * ensure that any #GESTransition that belongs to a newly created
- * transition clip is able to arrive in the correct track.
+ * #GESSource-s that both belong to a #GESClip in the layer overlap.
+ * See #GESTimeline for what counts as an overlap.
*
* When a layer is added to a #GESTimeline, if this property is left as
* %FALSE, but the timeline's #GESTimeline:auto-transition is %TRUE, it
}
/**
- * ges_layer_add_clip:
+ * ges_layer_add_clip_full:
* @layer: The #GESLayer
* @clip: (transfer floating): The clip to add
+ * @error: (nullable): Return location for an error
*
* Adds the given clip to the layer. If the method succeeds, the layer
* will take ownership of the clip.
* if @layer refused to add @clip.
*/
gboolean
-ges_layer_add_clip (GESLayer * layer, GESClip * clip)
+ges_layer_add_clip_full (GESLayer * layer, GESClip * clip, GError ** error)
{
- GList *tmp, *prev_children;
+ GList *tmp, *prev_children, *new_children;
GESAsset *asset;
GESLayerPrivate *priv;
GESLayer *current_layer;
GESTimeline *timeline;
GESContainer *container;
+ GError *timeline_error = NULL;
g_return_val_if_fail (GES_IS_LAYER (layer), FALSE);
g_return_val_if_fail (GES_IS_CLIP (clip), FALSE);
+ g_return_val_if_fail (!error || !*error, FALSE);
timeline = GES_TIMELINE_ELEMENT_TIMELINE (clip);
container = GES_CONTAINER (clip);
prev_children = ges_container_get_children (container, FALSE);
- if (timeline && !ges_timeline_add_clip (timeline, clip)) {
+ if (timeline && !ges_timeline_add_clip (timeline, clip, &timeline_error)) {
+ GST_INFO_OBJECT (layer, "Could not add the clip %" GES_FORMAT
+ " to the timeline %" GST_PTR_FORMAT, GES_ARGS (clip), timeline);
+
+ if (timeline_error) {
+ if (error) {
+ *error = timeline_error;
+ } else {
+ GST_WARNING_OBJECT (timeline, "Adding the clip %" GES_FORMAT
+ " to the timeline failed: %s", GES_ARGS (clip),
+ timeline_error->message);
+ g_error_free (timeline_error);
+ }
+ }
+
/* remove any track elements that were newly created */
- GList *new_children = ges_container_get_children (container, FALSE);
+ new_children = ges_container_get_children (container, FALSE);
for (tmp = new_children; tmp; tmp = tmp->next) {
if (!g_list_find (prev_children, tmp->data))
ges_container_remove (container, tmp->data);
g_list_free_full (prev_children, gst_object_unref);
g_list_free_full (new_children, gst_object_unref);
- GST_WARNING_OBJECT (layer, "Could not add the clip %" GES_FORMAT
- " to the timeline %" GST_PTR_FORMAT, GES_ARGS (clip), 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);
}
/**
- * ges_layer_add_asset:
+ * ges_layer_add_clip:
+ * @layer: The #GESLayer
+ * @clip: (transfer floating): The clip to add
+ *
+ * See ges_layer_add_clip_full(), which also gives an error.
+ *
+ * Returns: %TRUE if @clip was properly added to @layer, or %FALSE
+ * if @layer refused to add @clip.
+ */
+gboolean
+ges_layer_add_clip (GESLayer * layer, GESClip * clip)
+{
+ return ges_layer_add_clip_full (layer, clip, NULL);
+}
+
+/**
+ * ges_layer_add_asset_full:
* @layer: The #GESLayer
* @asset: The asset to extract the new clip from
* @start: The #GESTimelineElement:start value to set on the new clip
* clip
* @track_types: The #GESClip:supported-formats to set on the the new
* clip, or #GES_TRACK_TYPE_UNKNOWN to use the default
+ * @error: (nullable): Return location for an error
*
* Extracts a new clip from an asset and adds it to the layer with
* the given properties.
* Returns: (transfer none): The newly created clip.
*/
GESClip *
-ges_layer_add_asset (GESLayer * layer,
+ges_layer_add_asset_full (GESLayer * layer,
GESAsset * asset, GstClockTime start, GstClockTime inpoint,
- GstClockTime duration, GESTrackType track_types)
+ GstClockTime duration, GESTrackType track_types, GError ** error)
{
GESClip *clip;
g_return_val_if_fail (GES_IS_LAYER (layer), NULL);
g_return_val_if_fail (GES_IS_ASSET (asset), NULL);
+ g_return_val_if_fail (!error || !*error, NULL);
g_return_val_if_fail (g_type_is_a (ges_asset_get_extractable_type
(asset), GES_TYPE_CLIP), NULL);
_set_duration0 (GES_TIMELINE_ELEMENT (clip), duration);
}
- if (!ges_layer_add_clip (layer, clip)) {
+ if (!ges_layer_add_clip_full (layer, clip, error)) {
return NULL;
}
return clip;
}
+/**
+ * ges_layer_add_asset:
+ * @layer: The #GESLayer
+ * @asset: The asset to extract the new clip from
+ * @start: The #GESTimelineElement:start value to set on the new clip
+ * If `start == #GST_CLOCK_TIME_NONE`, it will be added to the end
+ * of @layer, i.e. it will be set to @layer's duration
+ * @inpoint: The #GESTimelineElement:in-point value to set on the new
+ * clip
+ * @duration: The #GESTimelineElement:duration value to set on the new
+ * clip
+ * @track_types: The #GESClip:supported-formats to set on the the new
+ * clip, or #GES_TRACK_TYPE_UNKNOWN to use the default
+ *
+ * See ges_layer_add_asset_full(), which also gives an error.
+ *
+ * Returns: (transfer none): The newly created clip.
+ */
+GESClip *
+ges_layer_add_asset (GESLayer * layer,
+ GESAsset * asset, GstClockTime start, GstClockTime inpoint,
+ GstClockTime duration, GESTrackType track_types)
+{
+ return ges_layer_add_asset_full (layer, asset, start, inpoint, duration,
+ track_types, NULL);
+}
+
/**
* ges_layer_new:
*
/*< private >*/
/* Signals */
- void (*object_added) (GESLayer * layer, GESClip * object);
- void (*object_removed) (GESLayer * layer, GESClip * object);
+ void (*object_added) (GESLayer * layer, GESClip * object);
+ void (*object_removed) (GESLayer * layer, GESClip * object);
/* Padding for API extension */
gpointer _ges_reserved[GES_PADDING];
};
GES_API
-GESLayer* ges_layer_new (void);
+GESLayer* ges_layer_new (void);
GES_API
-void ges_layer_set_timeline (GESLayer * layer,
- GESTimeline * timeline);
-
-GES_API GESTimeline *
-ges_layer_get_timeline (GESLayer * layer);
+void ges_layer_set_timeline (GESLayer * layer,
+ GESTimeline * timeline);
+GES_API
+GESTimeline * ges_layer_get_timeline (GESLayer * layer);
GES_API
-gboolean ges_layer_add_clip (GESLayer * layer,
- GESClip * clip);
+gboolean ges_layer_add_clip (GESLayer * layer,
+ GESClip * clip);
+GES_API
+gboolean ges_layer_add_clip_full (GESLayer * layer,
+ GESClip * clip,
+ GError ** error);
GES_API
-GESClip * ges_layer_add_asset (GESLayer *layer,
- GESAsset *asset,
- GstClockTime start,
- GstClockTime inpoint,
- GstClockTime duration,
- GESTrackType track_types);
+GESClip * ges_layer_add_asset (GESLayer *layer,
+ GESAsset *asset,
+ GstClockTime start,
+ GstClockTime inpoint,
+ GstClockTime duration,
+ GESTrackType track_types);
+GES_API
+GESClip * ges_layer_add_asset_full (GESLayer *layer,
+ GESAsset *asset,
+ GstClockTime start,
+ GstClockTime inpoint,
+ GstClockTime duration,
+ GESTrackType track_types,
+ GError ** error);
GES_API
-gboolean ges_layer_remove_clip (GESLayer * layer,
- GESClip * clip);
+gboolean ges_layer_remove_clip (GESLayer * layer,
+ GESClip * clip);
GES_DEPRECATED_FOR(ges_timeline_move_layer)
-void ges_layer_set_priority (GESLayer * layer,
- guint priority);
+void ges_layer_set_priority (GESLayer * layer,
+ guint priority);
GES_API
-gboolean ges_layer_is_empty (GESLayer * layer);
+gboolean ges_layer_is_empty (GESLayer * layer);
GES_API
-GList* ges_layer_get_clips_in_interval (GESLayer * layer, GstClockTime start, GstClockTime end);
+GList* ges_layer_get_clips_in_interval (GESLayer * layer,
+ GstClockTime start,
+ GstClockTime end);
GES_API
-guint ges_layer_get_priority (GESLayer * layer);
+guint ges_layer_get_priority (GESLayer * layer);
GES_API
-gboolean ges_layer_get_auto_transition (GESLayer * layer);
+gboolean ges_layer_get_auto_transition (GESLayer * layer);
GES_API
-void ges_layer_set_auto_transition (GESLayer * layer,
- gboolean auto_transition);
+void ges_layer_set_auto_transition (GESLayer * layer,
+ gboolean auto_transition);
GES_API
-GList* ges_layer_get_clips (GESLayer * layer);
+GList* ges_layer_get_clips (GESLayer * layer);
GES_API
-GstClockTime ges_layer_get_duration (GESLayer *layer);
+GstClockTime ges_layer_get_duration (GESLayer *layer);
GES_API
-gboolean ges_layer_set_active_for_tracks (GESLayer *layer, gboolean active,
- GList *tracks);
+gboolean ges_layer_set_active_for_tracks (GESLayer *layer,
+ gboolean active,
+ GList *tracks);
-GES_API gboolean ges_layer_get_active_for_track (GESLayer *layer,
- GESTrack *track);
+GES_API
+gboolean ges_layer_get_active_for_track (GESLayer *layer,
+ GESTrack *track);
G_END_DECLS
* similar changes in neighbouring or later elements in the timeline.
*
* However, a timeline may refuse a change in these properties if they
- * would place the timeline in an unsupported configuration. For example,
- * it is not possible for three #GESSourceClip-s in the same layer and
- * with the same track types to overlap at any given position in the
- * timeline (only two may overlap, which corresponds to a single
- * #GESTransition). Similarly, a #GESSourceClip may not entirely cover
- * another #GESSourceClip in the same layer and with the same track types.
+ * would place the timeline in an unsupported configuration. See
+ * #GESTimeline for its overlap rules.
+ *
* Additionally, an edit may be refused if it would place one of the
* timing properties out of bounds (such as a negative time value for
* #GESTimelineElement:start, or having insufficient internal
if (G_UNLIKELY (inpoint == self->inpoint))
return TRUE;
- if (GST_CLOCK_TIME_IS_VALID (self->maxduration)
- && inpoint > self->maxduration) {
+ if (GES_CLOCK_TIME_IS_LESS (self->maxduration, inpoint)) {
GST_WARNING_OBJECT (self, "Can not set an in-point of %" GST_TIME_FORMAT
" because it exceeds the element's max-duration: %" GST_TIME_FORMAT,
GST_TIME_ARGS (inpoint), GST_TIME_ARGS (self->maxduration));
if (G_UNLIKELY (maxduration == self->maxduration))
return TRUE;
- if (GST_CLOCK_TIME_IS_VALID (maxduration) && self->inpoint > maxduration) {
+ if (GES_CLOCK_TIME_IS_LESS (maxduration, self->inpoint)) {
GST_WARNING_OBJECT (self, "Can not set a max-duration of %"
GST_TIME_FORMAT " because it lies below the element's in-point: %"
GST_TIME_FORMAT, GST_TIME_ARGS (maxduration),
}
/**
- * ges_timeline_element_edit:
+ * ges_timeline_element_edit_full:
* @self: The #GESTimelineElement to edit
- * @layers: (element-type GESLayer) (nullable): A whitelist of layers
- * where the edit can be performed, %NULL allows all layers in the
- * timeline
* @new_layer_priority: The priority/index of the layer @self should be
* moved to. -1 means no move
* @mode: The edit mode
* @edge: The edge of @self where the edit should occur
* @position: The edit position: a new location for the edge of @self
* (in nanoseconds)
+ * @error: (nullable): Return location for an error
*
* Edits the element within its timeline by adjusting its
* #GESTimelineElement:start, #GESTimelineElement:duration or
* the corresponding layer priority/index does not yet exist for the
* timeline.
*
- * @layers can be used as a whitelist to limit changes to elements that
- * exist in the corresponding layers. If you intend to also switch
- * elements between layers, then you must ensure that all the involved
- * layers are included for the switch to succeed (with the exception of
- * layers may be newly created).
- *
* Returns: %TRUE if the edit of @self completed, %FALSE on failure.
- *
- * Since: 1.18
*/
-/* FIXME: handle the layers argument. Currently we always treat it as if
- * it is NULL in the ges-timeline code */
gboolean
-ges_timeline_element_edit (GESTimelineElement * self, GList * layers,
- gint64 new_layer_priority, GESEditMode mode, GESEdge edge, guint64 position)
+ges_timeline_element_edit_full (GESTimelineElement * self,
+ gint64 new_layer_priority, GESEditMode mode, GESEdge edge, guint64 position,
+ GError ** error)
{
GESTimeline *timeline;
guint32 layer_prio;
g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (position), FALSE);
+ g_return_val_if_fail (!error || !*error, FALSE);
timeline = GES_TIMELINE_ELEMENT_TIMELINE (self);
g_return_val_if_fail (timeline, FALSE);
self->name, ges_edge_name (edge), GST_TIME_ARGS (position),
ges_edit_mode_name (mode), new_layer_priority);
- return ges_timeline_edit (timeline, self, layers, new_layer_priority, mode,
- edge, position);
+ return ges_timeline_edit (timeline, self, new_layer_priority, mode,
+ edge, position, error);
+}
+
+/**
+ * ges_timeline_element_edit:
+ * @self: The #GESTimelineElement to edit
+ * @layers: (element-type GESLayer) (nullable): A whitelist of layers
+ * where the edit can be performed, %NULL allows all layers in the
+ * timeline.
+ * @new_layer_priority: The priority/index of the layer @self should be
+ * moved to. -1 means no move
+ * @mode: The edit mode
+ * @edge: The edge of @self where the edit should occur
+ * @position: The edit position: a new location for the edge of @self
+ * (in nanoseconds) in the timeline coordinates
+ *
+ * See ges_timeline_element_edit_full(), which also gives an error.
+ *
+ * Note that the @layers argument is currently ignored, so you should
+ * just pass %NULL.
+ *
+ * Returns: %TRUE if the edit of @self completed, %FALSE on failure.
+ *
+ * Since: 1.18
+ */
+
+/* FIXME: handle the layers argument. Currently we always treat it as if
+ * it is NULL in the ges-timeline code */
+gboolean
+ges_timeline_element_edit (GESTimelineElement * self, GList * layers,
+ gint64 new_layer_priority, GESEditMode mode, GESEdge edge, guint64 position)
+{
+ return ges_timeline_element_edit_full (self, new_layer_priority, mode, edge,
+ position, NULL);
}
/**
GESEditMode mode,
GESEdge edge,
guint64 position);
+
+GES_API
+gboolean ges_timeline_element_edit_full (GESTimelineElement * self,
+ gint64 new_layer_priority,
+ GESEditMode mode,
+ GESEdge edge,
+ guint64 position,
+ GError ** error);
G_END_DECLS
{
GNode *root;
gboolean res;
+ /* an error to set */
+ GError **error;
/* The element we are visiting */
GESTimelineElement *element;
return GST_CLOCK_TIME_NONE;
if (time >= (G_MAXUINT64 - add)) {
- GST_INFO ("The time %" G_GUINT64_FORMAT " would overflow when "
+ GST_ERROR ("The time %" G_GUINT64_FORMAT " would overflow when "
"adding %" G_GUINT64_FORMAT, time, add);
return GST_CLOCK_TIME_NONE;
}
return time2 - time1;
}
-static gboolean
+static void
get_start_end_from_offset (GESTimelineElement * element, ElementEditMode mode,
GstClockTimeDiff offset, GstClockTime * start, gboolean * negative_start,
GstClockTime * end, gboolean * negative_end)
*start = new_start;
if (end)
*end = new_end;
-
- if (start && !GST_CLOCK_TIME_IS_VALID (new_start)) {
- GST_INFO_OBJECT (element, "Cannot edit element %" GES_FORMAT " with "
- "offset %" G_GINT64_FORMAT " because it would result in an invalid "
- "start", GES_ARGS (element), offset);
- return FALSE;
- }
-
- if (end && !GST_CLOCK_TIME_IS_VALID (new_end)) {
- GST_INFO_OBJECT (element, "Cannot edit element %" GES_FORMAT " with "
- "offset %" G_GINT64_FORMAT " because it would result in an invalid "
- "end", GES_ARGS (element), offset);
- return FALSE;
- }
-
- return TRUE;
}
/****************************************************
/* Allow negative start/end positions in case a snap makes them valid!
* But we can still only snap to an existing edge in the timeline,
* which should be a valid time */
- if (!get_start_end_from_offset (GES_TIMELINE_ELEMENT (source), mode,
- *offset, &start, &negative_start, &end, &negative_end))
+ get_start_end_from_offset (GES_TIMELINE_ELEMENT (source), mode, *offset,
+ &start, &negative_start, &end, &negative_end);
+
+ if (!GST_CLOCK_TIME_IS_VALID (start)) {
+ GST_INFO_OBJECT (element, "Cannot edit element %" GES_FORMAT
+ " with offset %" G_GINT64_FORMAT " because it would result in "
+ "an invalid start", GES_ARGS (element), *offset);
goto done;
+ }
+
+ if (!GST_CLOCK_TIME_IS_VALID (end)) {
+ GST_INFO_OBJECT (element, "Cannot edit element %" GES_FORMAT
+ " with offset %" G_GINT64_FORMAT " because it would result in "
+ "an invalid end", GES_ARGS (element), *offset);
+ goto done;
+ }
switch (mode) {
case EDIT_MOVE:
* Check Overlaps *
****************************************************/
+#define _SOURCE_FORMAT "\"%s\"%s%s%s"
+#define _SOURCE_ARGS(element) \
+ element->name, element->parent ? " (parent: \"" : "", \
+ element->parent ? element->parent->name : "", \
+ element->parent ? "\")" : ""
+
+static void
+set_full_overlap_error (GError ** error, GESTimelineElement * super,
+ GESTimelineElement * sub, GESTrack * track)
+{
+ if (error) {
+ gchar *track_name = gst_object_get_name (GST_OBJECT (track));
+ g_set_error (error, GES_ERROR, GES_ERROR_INVALID_OVERLAP_IN_TRACK,
+ "The source " _SOURCE_FORMAT " would totally overlap the "
+ "source " _SOURCE_FORMAT " in the track \"%s\"", _SOURCE_ARGS (super),
+ _SOURCE_ARGS (sub), track_name);
+ g_free (track_name);
+ }
+}
+
+static void
+set_triple_overlap_error (GError ** error, GESTimelineElement * first,
+ GESTimelineElement * second, GESTimelineElement * third, GESTrack * track)
+{
+ if (error) {
+ gchar *track_name = gst_object_get_name (GST_OBJECT (track));
+ g_set_error (error, GES_ERROR, GES_ERROR_INVALID_OVERLAP_IN_TRACK,
+ "The sources " _SOURCE_FORMAT ", " _SOURCE_FORMAT " and "
+ _SOURCE_FORMAT " would all overlap at the same point in the "
+ "track \"%s\"", _SOURCE_ARGS (first), _SOURCE_ARGS (second),
+ _SOURCE_ARGS (third), track_name);
+ g_free (track_name);
+ }
+}
+
#define _ELEMENT_FORMAT \
"%s (under %s) [%" GST_TIME_FORMAT " - %" GST_TIME_FORMAT "] " \
"(layer: %" G_GUINT32_FORMAT ") (track :%" GST_PTR_FORMAT ")"
return FALSE;
}
- if ((cmp_start <= start && cmp_end >= end) ||
- (cmp_start >= start && cmp_end <= end)) {
+ if (cmp_start <= start && cmp_end >= end) {
+ /* cmp fully overlaps e */
GST_INFO (_ELEMENT_FORMAT " and " _ELEMENT_FORMAT " fully overlap",
_CMP_ARGS, _E_ARGS);
+ set_full_overlap_error (data->error, cmp, e, track);
+ goto error;
+ }
+
+ if (cmp_start >= start && cmp_end <= end) {
+ /* e fully overlaps cmp */
+ GST_INFO (_ELEMENT_FORMAT " and " _ELEMENT_FORMAT " fully overlap",
+ _CMP_ARGS, _E_ARGS);
+ set_full_overlap_error (data->error, e, cmp, track);
goto error;
}
if (data->overlaping_on_start) {
GST_INFO (_ELEMENT_FORMAT " is overlapped by %s and %s on its start",
_CMP_ARGS, data->overlaping_on_start->name, e->name);
+ set_triple_overlap_error (data->error, cmp, e, data->overlaping_on_start,
+ track);
goto error;
}
if (GST_CLOCK_TIME_IS_VALID (data->overlap_end_first_time) &&
GST_INFO (_ELEMENT_FORMAT " overlaps %s on its start and %s on its "
"end, but they already overlap each other", _CMP_ARGS, e->name,
data->overlaping_on_end->name);
+ set_triple_overlap_error (data->error, cmp, e, data->overlaping_on_end,
+ track);
goto error;
}
/* record the time at which the overlapped ends */
if (data->overlaping_on_end) {
GST_INFO (_ELEMENT_FORMAT " is overlapped by %s and %s on its end",
_CMP_ARGS, data->overlaping_on_end->name, e->name);
+ set_triple_overlap_error (data->error, cmp, e, data->overlaping_on_end,
+ track);
goto error;
}
if (GST_CLOCK_TIME_IS_VALID (data->overlap_start_final_time) &&
GST_INFO (_ELEMENT_FORMAT " overlaps %s on its end and %s on its "
"start, but they already overlap each other", _CMP_ARGS, e->name,
data->overlaping_on_start->name);
+ set_triple_overlap_error (data->error, cmp, e, data->overlaping_on_start,
+ track);
goto error;
}
/* record the time at which the overlapped starts */
/* whether the elements in moving can be moved to their corresponding
* PositionData */
static gboolean
-timeline_tree_can_move_elements (GNode * root, GHashTable * moving)
+timeline_tree_can_move_elements (GNode * root, GHashTable * moving,
+ GError ** error)
{
TreeIterationData data = tree_iteration_data_init;
data.moving = moving;
data.root = root;
data.res = TRUE;
+ data.error = error;
/* sufficient to check the leaves, which is all the track elements or
* empty clips
* should also be sufficient to only check the moving elements */
* Setting Edit Data *
****************************************************/
+static void
+set_negative_start_error (GError ** error, GESTimelineElement * element,
+ GstClockTime neg_start)
+{
+ g_set_error (error, GES_ERROR, GES_ERROR_NEGATIVE_TIME,
+ "The element \"%s\" would have a negative start of -%"
+ GST_TIME_FORMAT, element->name, GST_TIME_ARGS (neg_start));
+}
+
+static void
+set_negative_duration_error (GError ** error, GESTimelineElement * element,
+ GstClockTime neg_duration)
+{
+ g_set_error (error, GES_ERROR, GES_ERROR_NEGATIVE_TIME,
+ "The element \"%s\" would have a negative duration of -%"
+ GST_TIME_FORMAT, element->name, GST_TIME_ARGS (neg_duration));
+}
+
+static void
+set_negative_inpoint_error (GError ** error, GESTimelineElement * element,
+ GstClockTime neg_inpoint)
+{
+ g_set_error (error, GES_ERROR, GES_ERROR_NEGATIVE_TIME,
+ "The element \"%s\" would have a negative in-point of -%"
+ GST_TIME_FORMAT, element->name, GST_TIME_ARGS (neg_inpoint));
+}
+
+static void
+set_negative_layer_error (GError ** error, GESTimelineElement * element,
+ gint64 neg_layer)
+{
+ g_set_error (error, GES_ERROR, GES_ERROR_NEGATIVE_LAYER,
+ "The element \"%s\" would have a negative layer priority of -%"
+ G_GINT64_FORMAT, element->name, neg_layer);
+}
+
+static void
+set_breaks_duration_limit_error (GError ** error, GESClip * clip,
+ GstClockTime duration, GstClockTime duration_limit)
+{
+ g_set_error (error, GES_ERROR, GES_ERROR_NOT_ENOUGH_INTERNAL_CONTENT,
+ "The clip \"%s\" would have a duration of %" GST_TIME_FORMAT
+ " that would break its duration-limit of %" GST_TIME_FORMAT,
+ GES_TIMELINE_ELEMENT_NAME (clip), GST_TIME_ARGS (duration),
+ GST_TIME_ARGS (duration_limit));
+}
+
static gboolean
-set_layer_priority (GESTimelineElement * element, EditData * data)
+set_layer_priority (GESTimelineElement * element, EditData * data,
+ GError ** error)
{
gint64 layer_offset = data->layer_offset;
guint32 layer_prio = ges_timeline_element_get_layer_priority (element);
GST_INFO_OBJECT (element, "%s would have a negative layer priority (%"
G_GUINT32_FORMAT " - %" G_GINT64_FORMAT ")", element->name,
layer_prio, layer_offset);
+ set_negative_layer_error (error, element,
+ layer_offset - (gint64) layer_prio);
return FALSE;
}
if ((layer_prio - (gint64) layer_offset) >= G_MAXUINT32) {
- GST_INFO_OBJECT (element, "%s would have an overflowing layer priority",
+ GST_ERROR_OBJECT (element, "%s would have an overflowing layer priority",
element->name);
return FALSE;
}
}
static gboolean
-set_edit_move_values (GESTimelineElement * element, EditData * data)
+set_edit_move_values (GESTimelineElement * element, EditData * data,
+ GError ** error)
{
+ gboolean negative = FALSE;
GstClockTime new_start =
- _clock_time_minus_diff (element->start, data->offset, NULL);
- if (!GST_CLOCK_TIME_IS_VALID (new_start)) {
+ _clock_time_minus_diff (element->start, data->offset, &negative);
+ if (negative || !GST_CLOCK_TIME_IS_VALID (new_start)) {
GST_INFO_OBJECT (element, "Cannot move %" GES_FORMAT " with offset %"
G_GINT64_FORMAT " because it would result in an invalid start",
GES_ARGS (element), data->offset);
+ if (negative)
+ set_negative_start_error (error, element, new_start);
return FALSE;
}
_CHECK_END (element, new_start, element->duration);
data->start = new_start;
+
+ if (GES_IS_GROUP (element))
+ return TRUE;
+
GST_INFO_OBJECT (element, "%s will move by setting start to %"
GST_TIME_FORMAT, element->name, GST_TIME_ARGS (data->start));
- return set_layer_priority (element, data);
+ return set_layer_priority (element, data, error);
}
static gboolean
set_edit_trim_start_inpoint_value (GESTimelineElement * element,
- EditData * data)
+ EditData * data, GError ** error)
{
+ gboolean negative = FALSE;
GstClockTime new_inpoint = _clock_time_minus_diff (element->inpoint,
- data->offset, NULL);
- if (!GST_CLOCK_TIME_IS_VALID (new_inpoint)) {
+ data->offset, &negative);
+ if (negative || !GST_CLOCK_TIME_IS_VALID (new_inpoint)) {
GST_INFO_OBJECT (element, "Cannot trim start of %" GES_FORMAT
" with offset %" G_GINT64_FORMAT " because it would result in an "
"invalid in-point", GES_ARGS (element), data->offset);
+ if (negative)
+ set_negative_inpoint_error (error, element, new_inpoint);
return FALSE;
}
data->inpoint = new_inpoint;
static gboolean
set_edit_trim_start_non_core_children (GESTimelineElement * clip,
- GstClockTimeDiff offset, GHashTable * edit_table)
+ GstClockTimeDiff offset, GHashTable * edit_table, GError ** error)
{
GList *tmp;
GESTimelineElement *child;
data = new_edit_data (EDIT_TRIM_START, offset, 0);
g_hash_table_insert (edit_table, child, data);
- if (!set_edit_trim_start_inpoint_value (child, data))
+ if (!set_edit_trim_start_inpoint_value (child, data, error))
return FALSE;
}
}
/* trim the start of a clip or a track element */
static gboolean
set_edit_trim_start_values (GESTimelineElement * element, EditData * data,
- GHashTable * edit_table)
+ GHashTable * edit_table, GError ** error)
{
+ gboolean negative = FALSE;
+ GstClockTime new_duration;
GstClockTime new_start =
- _clock_time_minus_diff (element->start, data->offset, NULL);
- GstClockTime new_duration =
- _clock_time_minus_diff (element->duration, -data->offset, NULL);
+ _clock_time_minus_diff (element->start, data->offset, &negative);
- if (!GST_CLOCK_TIME_IS_VALID (new_start)) {
+ if (negative || !GST_CLOCK_TIME_IS_VALID (new_start)) {
GST_INFO_OBJECT (element, "Cannot trim start of %" GES_FORMAT
" with offset %" G_GINT64_FORMAT " because it would result in an "
"invalid start", GES_ARGS (element), data->offset);
+ if (negative)
+ set_negative_start_error (error, element, new_start);
return FALSE;
}
- if (!GST_CLOCK_TIME_IS_VALID (new_duration)) {
+
+ new_duration =
+ _clock_time_minus_diff (element->duration, -data->offset, &negative);
+
+ if (negative || !GST_CLOCK_TIME_IS_VALID (new_duration)) {
GST_INFO_OBJECT (element, "Cannot trim start of %" GES_FORMAT
" with offset %" G_GINT64_FORMAT " because it would result in an "
"invalid duration", GES_ARGS (element), data->offset);
+ if (negative)
+ set_negative_duration_error (error, element, new_duration);
return FALSE;
}
_CHECK_END (element, new_start, new_duration);
+ data->start = new_start;
+ data->duration = new_duration;
+
+ if (GES_IS_GROUP (element))
+ return TRUE;
+
if (GES_IS_CLIP (element)) {
- if (!set_edit_trim_start_inpoint_value (element, data))
+ if (!set_edit_trim_start_inpoint_value (element, data, error))
return FALSE;
if (!set_edit_trim_start_non_core_children (element, data->offset,
- edit_table))
+ edit_table, error))
return FALSE;
} else if (GES_IS_TRACK_ELEMENT (element)
&& ges_track_element_has_internal_source (GES_TRACK_ELEMENT (element))) {
- if (!set_edit_trim_start_inpoint_value (element, data))
+ if (!set_edit_trim_start_inpoint_value (element, data, error))
return FALSE;
}
- data->start = new_start;
- data->duration = new_duration;
/* NOTE: without time effects, the duration-limit will increase with
* a decrease in in-point by the same amount that duration increases,
"to %" GST_TIME_FORMAT, element->name, GST_TIME_ARGS (data->start),
GST_TIME_ARGS (data->inpoint), GST_TIME_ARGS (data->duration));
- return set_layer_priority (element, data);
+ return set_layer_priority (element, data, error);
}
/* trim the end of a clip or a track element */
static gboolean
-set_edit_trim_end_values (GESTimelineElement * element, EditData * data)
+set_edit_trim_end_values (GESTimelineElement * element, EditData * data,
+ GError ** error)
{
+ gboolean negative = FALSE;
GstClockTime new_duration =
- _clock_time_minus_diff (element->duration, data->offset, NULL);
- if (!GST_CLOCK_TIME_IS_VALID (new_duration)) {
+ _clock_time_minus_diff (element->duration, data->offset, &negative);
+ if (negative || !GST_CLOCK_TIME_IS_VALID (new_duration)) {
GST_INFO_OBJECT (element, "Cannot trim end of %" GES_FORMAT
" with offset %" G_GINT64_FORMAT " because it would result in an "
"invalid duration", GES_ARGS (element), data->offset);
+ if (negative)
+ set_negative_duration_error (error, element, new_duration);
return FALSE;
}
_CHECK_END (element, element->start, new_duration);
if (GES_IS_CLIP (element)) {
- GstClockTime limit = ges_clip_get_duration_limit (GES_CLIP (element));
- if (GST_CLOCK_TIME_IS_VALID (limit) && new_duration > limit) {
+ GESClip *clip = GES_CLIP (element);
+ GstClockTime limit = ges_clip_get_duration_limit (clip);
+
+ if (GES_CLOCK_TIME_IS_LESS (limit, new_duration)) {
GST_INFO_OBJECT (element, "Cannot trim end of %" GES_FORMAT
" with offset %" G_GINT64_FORMAT " because the duration would "
"exceed the clip's duration-limit %" G_GINT64_FORMAT,
GES_ARGS (element), data->offset, limit);
+
+ set_breaks_duration_limit_error (error, clip, new_duration, limit);
return FALSE;
}
}
data->duration = new_duration;
+
+ if (GES_IS_GROUP (element))
+ return TRUE;
+
GST_INFO_OBJECT (element, "%s will trim end by setting duration to %"
GST_TIME_FORMAT, element->name, GST_TIME_ARGS (data->duration));
- return set_layer_priority (element, data);
+ return set_layer_priority (element, data, error);
}
-/* handles clips and track elements with no parents */
static gboolean
-set_clip_edit_values (GESTimelineElement * element, EditData * data,
- GHashTable * edit_table)
+set_edit_values (GESTimelineElement * element, EditData * data,
+ GHashTable * edit_table, GError ** error)
{
switch (data->mode) {
case EDIT_MOVE:
- return set_edit_move_values (element, data);
+ return set_edit_move_values (element, data, error);
case EDIT_TRIM_START:
- return set_edit_trim_start_values (element, data, edit_table);
+ return set_edit_trim_start_values (element, data, edit_table, error);
case EDIT_TRIM_END:
- return set_edit_trim_end_values (element, data);
+ return set_edit_trim_end_values (element, data, error);
}
return FALSE;
}
static gboolean
replace_group_with_clip_edits (GNode * root, GESTimelineElement * group,
- GHashTable * edit_table)
+ GHashTable * edit_table, GError ** err)
{
gboolean ret = TRUE;
GList *tmp, *clips = NULL;
goto error;
}
- layer_offset = group_edit->layer_offset;
- mode = group_edit->mode;
+ group_edit->start = group->start;
+ group_edit->duration = group->duration;
+
+ /* should only set the start and duration fields, table should not be
+ * needed, so we pass NULL */
+ if (!set_edit_values (group, group_edit, NULL, err))
+ goto error;
+
+ new_start = group_edit->start;
+ new_end = _clock_time_plus (group_edit->start, group_edit->duration);
- if (!get_start_end_from_offset (group, mode, group_edit->offset,
- &new_start, NULL, &new_end, NULL))
+ if (!GST_CLOCK_TIME_IS_VALID (new_start)
+ || !GST_CLOCK_TIME_IS_VALID (new_end)) {
+ GST_ERROR_OBJECT (group, "Edit data gave an invalid start or end");
goto error;
+ }
+
+ layer_offset = group_edit->layer_offset;
+ mode = group_edit->mode;
/* can traverse leaves to find all the clips since they are at _most_
* one step above the track elements */
}
clip_data = new_edit_data (clip_mode, offset, layer_offset);
g_hash_table_insert (edit_table, clip, clip_data);
- if (!set_clip_edit_values (clip, clip_data, edit_table))
+ if (!set_edit_values (clip, clip_data, edit_table, err))
goto error;
}
}
/* set the edit values for the entries in @edits
* any groups in @edits will be replaced by their clip children */
static gboolean
-timeline_tree_set_element_edit_values (GNode * root, GHashTable * edits)
+timeline_tree_set_element_edit_values (GNode * root, GHashTable * edits,
+ GError ** err)
{
gboolean ret = TRUE;
GESTimelineElement *element;
goto error;
}
if (GES_IS_GROUP (element))
- res = replace_group_with_clip_edits (root, element, edits);
+ res = replace_group_with_clip_edits (root, element, edits, err);
else
- res = set_clip_edit_values (element, edit_data, edits);
+ res = set_edit_values (element, edit_data, edits, err);
if (!res)
goto error;
}
gboolean
timeline_tree_can_move_element (GNode * root,
GESTimelineElement * element, guint32 priority, GstClockTime start,
- GstClockTime duration)
+ GstClockTime duration, GError ** error)
{
gboolean ret = FALSE;
guint32 layer_prio = ges_timeline_element_get_layer_priority (element);
/* assume both edits can be performed if each could occur individually */
/* should not effect duration or in-point */
- if (!timeline_tree_set_element_edit_values (root, move_edits))
+ if (!timeline_tree_set_element_edit_values (root, move_edits, error))
goto done;
/* should not effect start or in-point or layer */
- if (!timeline_tree_set_element_edit_values (root, trim_edits))
+ if (!timeline_tree_set_element_edit_values (root, trim_edits, error))
goto done;
/* merge the two edits into moving positions */
}
/* check overlaps */
- if (!timeline_tree_can_move_elements (root, moving))
+ if (!timeline_tree_can_move_elements (root, moving, error))
goto done;
ret = TRUE;
gboolean
timeline_tree_ripple (GNode * root, GESTimelineElement * element,
gint64 layer_priority_offset, GstClockTimeDiff offset, GESEdge edge,
- GstClockTime snapping_distance)
+ GstClockTime snapping_distance, GError ** error)
{
gboolean res = TRUE;
GNode *node;
/* check and set edits using snapped values */
give_edits_same_offset (edits, offset, layer_priority_offset);
- if (!timeline_tree_set_element_edit_values (root, edits))
+ if (!timeline_tree_set_element_edit_values (root, edits, error))
goto error;
/* check overlaps */
set_moving_positions_from_edits (moving, edits);
- if (!timeline_tree_can_move_elements (root, moving))
+ if (!timeline_tree_can_move_elements (root, moving, error))
goto error;
/* emit snapping now. Edits should only fail if a programming error
gboolean
timeline_tree_trim (GNode * root, GESTimelineElement * element,
gint64 layer_priority_offset, GstClockTimeDiff offset, GESEdge edge,
- GstClockTime snapping_distance)
+ GstClockTime snapping_distance, GError ** error)
{
gboolean res = TRUE;
GHashTable *edits = new_edit_table ();
/* check and set edits using snapped values */
give_edits_same_offset (edits, offset, layer_priority_offset);
- if (!timeline_tree_set_element_edit_values (root, edits))
+ if (!timeline_tree_set_element_edit_values (root, edits, error))
goto error;
/* check overlaps */
set_moving_positions_from_edits (moving, edits);
- if (!timeline_tree_can_move_elements (root, moving)) {
+ if (!timeline_tree_can_move_elements (root, moving, error)) {
goto error;
}
gboolean
timeline_tree_move (GNode * root, GESTimelineElement * element,
gint64 layer_priority_offset, GstClockTimeDiff offset, GESEdge edge,
- GstClockTime snapping_distance)
+ GstClockTime snapping_distance, GError ** error)
{
gboolean res = TRUE;
GHashTable *edits = new_edit_table ();
/* check and set edits using snapped values */
give_edits_same_offset (edits, offset, layer_priority_offset);
- if (!timeline_tree_set_element_edit_values (root, edits))
+ if (!timeline_tree_set_element_edit_values (root, edits, error))
goto error;
/* check overlaps */
set_moving_positions_from_edits (moving, edits);
- if (!timeline_tree_can_move_elements (root, moving)) {
+ if (!timeline_tree_can_move_elements (root, moving, error)) {
goto error;
}
gboolean
timeline_tree_roll (GNode * root, GESTimelineElement * element,
- GstClockTimeDiff offset, GESEdge edge, GstClockTime snapping_distance)
+ GstClockTimeDiff offset, GESEdge edge, GstClockTime snapping_distance,
+ GError ** error)
{
gboolean res = TRUE;
GList *tmp;
/* check and set edits using snapped values */
give_edits_same_offset (edits, offset, 0);
- if (!timeline_tree_set_element_edit_values (root, edits))
+ if (!timeline_tree_set_element_edit_values (root, edits, error))
goto error;
/* check overlaps */
set_moving_positions_from_edits (moving, edits);
- if (!timeline_tree_can_move_elements (root, moving)) {
+ if (!timeline_tree_can_move_elements (root, moving, error)) {
goto error;
}
GESTimelineElement *element,
guint32 priority,
GstClockTime start,
- GstClockTime duration);
+ GstClockTime duration,
+ GError ** error);
gboolean timeline_tree_ripple (GNode *root,
GESTimelineElement *element,
gint64 layer_priority_offset,
GstClockTimeDiff offset,
GESEdge edge,
- GstClockTime snapping_distance);
+ GstClockTime snapping_distance,
+ GError ** error);
void ges_timeline_emit_snapping (GESTimeline * timeline,
GESTrackElement * elem1,
gint64 layer_priority_offset,
GstClockTimeDiff offset,
GESEdge edge,
- GstClockTime snapping_distance);
+ GstClockTime snapping_distance,
+ GError ** error);
gboolean timeline_tree_move (GNode *root,
gint64 layer_priority_offset,
GstClockTimeDiff offset,
GESEdge edge,
- GstClockTime snapping_distance);
+ GstClockTime snapping_distance,
+ GError ** error);
gboolean timeline_tree_roll (GNode * root,
GESTimelineElement * element,
GstClockTimeDiff offset,
GESEdge edge,
- GstClockTime snapping_distance);
+ GstClockTime snapping_distance,
+ GError ** error);
typedef GESAutoTransition *
(*GESTreeGetAutoTransitionFunc) (GESTimeline * timeline,
* content prioritised in the tracks. This ordering can be changed using
* ges_timeline_move_layer().
*
- * ## Saving
- *
- * To save/load a timeline, you can use the ges_timeline_load_from_uri()
- * and ges_timeline_save_to_uri() methods that use the default format.
- *
* ## Editing
*
+ * See #GESTimelineElement for the various ways the elements of a timeline
+ * can be edited.
+ *
* If you change the timing or ordering of a timeline's
* #GESTimelineElement-s, then these changes will not actually be taken
- * into account in the timeline until the ges_timeline_commit() method is
- * called. This allows you to move its elements around, say, in
- * response to an end user's mouse dragging, with little expense before
- * finalising their effect.
+ * into account in the output of the timeline's tracks until the
+ * ges_timeline_commit() method is called. This allows you to move its
+ * elements around, say, in response to an end user's mouse dragging, with
+ * little expense before finalising their effect on the produced data.
+ *
+ * ## Overlaps and Auto-Transitions
+ *
+ * There are certain restrictions placed on how #GESSource-s may overlap
+ * in a #GESTrack that belongs to a timeline. These will be enforced by
+ * GES, so the user will not need to keep track of them, but they should
+ * be aware that certain edits will be refused as a result if the overlap
+ * rules would be broken.
+ *
+ * Consider two #GESSource-s, `A` and `B`, with start times `startA` and
+ * `startB`, and end times `endA` and `endB`, respectively. The start
+ * time refers to their #GESTimelineElement:start, and the end time is
+ * their #GESTimelineElement:start + #GESTimelineElement:duration. These
+ * two sources *overlap* if:
+ *
+ * + they share the same #GESTrackElement:track (non %NULL), which belongs
+ * to the timeline;
+ * + they share the same #GES_TIMELINE_ELEMENT_LAYER_PRIORITY; and
+ * + `startA < endB` and `startB < endA `.
+ *
+ * Note that when `startA = endB` or `startB = endA` then the two sources
+ * will *touch* at their edges, but are not considered overlapping.
+ *
+ * If, in addition, `startA < startB < endA`, then we can say that the
+ * end of `A` overlaps the start of `B`.
+ *
+ * If, instead, `startA <= startB` and `endA >= endB`, then we can say
+ * that `A` fully overlaps `B`.
+ *
+ * The overlap rules for a timeline are that:
+ *
+ * 1. One source cannot fully overlap another source.
+ * 2. A source can only overlap the end of up to one other source at its
+ * start.
+ * 3. A source can only overlap the start of up to one other source at its
+ * end.
+ *
+ * The last two rules combined essentially mean that at any given timeline
+ * position, only up to two #GESSource-s may overlap at that position. So
+ * triple or more overlaps are not allowed.
+ *
+ * If you switch on #GESTimeline:auto-transition, then at any moment when
+ * the end of one source (the first source) overlaps the start of another
+ * (the second source), a #GESTransitionClip will be automatically created
+ * for the pair in the same layer and it will cover their overlap. If the
+ * two elements are edited in a way such that the end of the first source
+ * no longer overlaps the start of the second, the transition will be
+ * automatically removed from the timeline. However, if the two sources
+ * still overlap at the same edges after the edit, then the same
+ * transition object will be kept, but with its timing and layer adjusted
+ * accordingly.
+ *
+ * ## Saving
+ *
+ * To save/load a timeline, you can use the ges_timeline_load_from_uri()
+ * and ges_timeline_save_to_uri() methods that use the default format.
*
* ## Playing
*
/* While we are creating and adding the TrackElements for a clip, we need to
* ignore the child-added signal */
gboolean track_elements_moving;
- gboolean track_selection_error;
+ /* whether any error occurred during track selection, including
+ * programming or usage errors */
+ gboolean has_any_track_selection_error;
+ /* error set for non-programming/usage errors */
+ GError *track_selection_error;
GList *groups;
guint stream_start_group_id;
gst_clear_object (&priv->auto_transition_track);
gst_clear_object (&priv->new_track);
+ g_clear_error (&priv->track_selection_error);
+ priv->track_selection_error = NULL;
G_OBJECT_CLASS (ges_timeline_parent_class)->dispose (object);
}
/**
* GESTimeline:auto-transition:
*
- * Whether to automatically create a transition whenever two clips
- * overlap in the timeline. See #GESLayer:auto-transition.
+ * Whether to automatically create a transition whenever two
+ * #GESSource-s overlap in a track of the timeline. See
+ * #GESLayer:auto-transition if you want this to only happen in some
+ * layers.
*/
g_object_class_install_property (object_class, PROP_AUTO_TRANSITION,
g_param_spec_boolean ("auto-transition", "Auto-Transition",
static gint
_edit_auto_transition (GESTimeline * timeline, GESTimelineElement * element,
- GList * layers, gint64 new_layer_priority, GESEditMode mode, GESEdge edge,
- GstClockTime position)
+ gint64 new_layer_priority, GESEditMode mode, GESEdge edge,
+ GstClockTime position, GError ** error)
{
GList *tmp;
guint32 layer_prio = ges_timeline_element_get_layer_priority (element);
GST_INFO_OBJECT (element, "Trimming %" GES_FORMAT " in place of "
"trimming the corresponding auto-transition", GES_ARGS (replace));
- return ges_timeline_element_edit (replace, layers, -1, mode, edge,
- position);
+ return ges_timeline_element_edit_full (replace, -1, mode, edge,
+ position, error);
}
}
gboolean
ges_timeline_edit (GESTimeline * timeline, GESTimelineElement * element,
- GList * layers, gint64 new_layer_priority, GESEditMode mode, GESEdge edge,
- guint64 position)
+ gint64 new_layer_priority, GESEditMode mode, GESEdge edge,
+ guint64 position, GError ** error)
{
GstClockTimeDiff edge_diff = (edge == GES_EDGE_END ?
GST_CLOCK_DIFF (position, element->start + element->duration) :
gint res = -1;
if ((GES_IS_TRANSITION (element) || GES_IS_TRANSITION_CLIP (element)))
- res = _edit_auto_transition (timeline, element, layers, new_layer_priority,
- mode, edge, position);
+ res = _edit_auto_transition (timeline, element, new_layer_priority, mode,
+ edge, position, error);
if (res != -1)
return res;
switch (mode) {
case GES_EDIT_MODE_RIPPLE:
return timeline_tree_ripple (timeline->priv->tree, element, prio_diff,
- edge_diff, edge, timeline->priv->snapping_distance);
+ edge_diff, edge, timeline->priv->snapping_distance, error);
case GES_EDIT_MODE_TRIM:
return timeline_tree_trim (timeline->priv->tree, element, prio_diff,
- edge_diff, edge, timeline->priv->snapping_distance);
+ edge_diff, edge, timeline->priv->snapping_distance, error);
case GES_EDIT_MODE_NORMAL:
return timeline_tree_move (timeline->priv->tree, element, prio_diff,
- edge_diff, edge, timeline->priv->snapping_distance);
+ edge_diff, edge, timeline->priv->snapping_distance, error);
case GES_EDIT_MODE_ROLL:
if (prio_diff != 0) {
GST_WARNING_OBJECT (element, "Cannot roll an element to a new layer");
return FALSE;
}
return timeline_tree_roll (timeline->priv->tree, element,
- edge_diff, edge, timeline->priv->snapping_distance);
+ edge_diff, edge, timeline->priv->snapping_distance, error);
case GES_EDIT_MODE_SLIDE:
GST_ERROR_OBJECT (element, "Sliding not implemented.");
return FALSE;
* selected tracks */
static gboolean
_add_track_element_to_tracks (GESTimeline * timeline, GESClip * clip,
- GESTrackElement * track_element)
+ GESTrackElement * track_element, GError ** error)
{
guint i;
gboolean ret = TRUE;
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))
+ if (!ges_clip_add_child_to_track (clip, track_element, track, error)) {
ret = FALSE;
+ if (error)
+ break;
+ }
}
g_ptr_array_unref (tracks);
static gboolean
_try_add_track_element_to_track (GESTimeline * timeline, GESClip * clip,
- GESTrackElement * track_element, GESTrack * track)
+ GESTrackElement * track_element, GESTrack * track, GError ** error)
{
gboolean no_error = TRUE;
GPtrArray *tracks = _get_selected_tracks (timeline, clip, track_element);
* 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))
+ if (!ges_clip_add_child_to_track (clip, track_element, track, error))
no_error = FALSE;
}
}
static void
-_set_track_selection_error (GESTimeline * timeline, gboolean error)
+ges_timeline_set_track_selection_error (GESTimeline * timeline,
+ gboolean was_error, GError * error)
{
+ GESTimelinePrivate *priv;
+
LOCK_DYN (timeline);
- timeline->priv->track_selection_error = error;
+
+ priv = timeline->priv;
+ g_clear_error (&priv->track_selection_error);
+ priv->track_selection_error = error;
+ priv->has_any_track_selection_error = was_error;
+
UNLOCK_DYN (timeline);
}
static gboolean
-_get_track_selection_error (GESTimeline * timeline)
+ges_timeline_take_track_selection_error (GESTimeline * timeline,
+ GError ** error)
{
gboolean ret;
+ GESTimelinePrivate *priv;
LOCK_DYN (timeline);
- ret = timeline->priv->track_selection_error;
- timeline->priv->track_selection_error = FALSE;
+
+ priv = timeline->priv;
+ if (error) {
+ if (*error) {
+ GST_ERROR_OBJECT (timeline, "Error not handled %s", (*error)->message);
+ g_error_free (*error);
+ }
+ *error = priv->track_selection_error;
+ } else if (priv->track_selection_error) {
+ GST_WARNING_OBJECT (timeline, "Got track selection error: %s",
+ priv->track_selection_error->message);
+ g_error_free (priv->track_selection_error);
+ }
+ priv->track_selection_error = NULL;
+ ret = priv->has_any_track_selection_error;
+ priv->has_any_track_selection_error = FALSE;
+
UNLOCK_DYN (timeline);
return ret;
GESTrackElement * track_element, GESTimeline * timeline)
{
GESTrack *auto_trans_track, *new_track;
- gboolean error = FALSE;
+ GError *error = NULL;
+ gboolean success = FALSE;
if (timeline->priv->track_elements_moving) {
GST_DEBUG_OBJECT (timeline, "Ignoring element added: %" GES_FORMAT
if (auto_trans_track) {
/* don't use track-selection */
- if (!ges_clip_add_child_to_track (clip, track_element, auto_trans_track,
- NULL))
- error = TRUE;
+ success = ! !ges_clip_add_child_to_track (clip, track_element,
+ auto_trans_track, &error);
gst_object_unref (auto_trans_track);
} else {
if (new_track)
- error =
- !_try_add_track_element_to_track (timeline, clip, track_element,
- new_track);
+ success = _try_add_track_element_to_track (timeline, clip, track_element,
+ new_track, &error);
else
- error = !_add_track_element_to_tracks (timeline, clip, track_element);
+ success = _add_track_element_to_tracks (timeline, clip, track_element,
+ &error);
}
- if (error)
- _set_track_selection_error (timeline, TRUE);
+ if (error || !success) {
+ if (!error)
+ GST_WARNING_OBJECT (timeline, "Track selection failed for %" GES_FORMAT,
+ GES_ARGS (track_element));
+ ges_timeline_set_track_selection_error (timeline, TRUE, error);
+ }
}
static void
/* 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)
+ gboolean add_core, GESTrack * new_track, GList * blacklist, GError ** error)
{
GList *tmp, *children;
gboolean no_errors = TRUE;
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);
+ res = _try_add_track_element_to_track (timeline, clip, el, new_track,
+ error);
else
- res = _add_track_element_to_tracks (timeline, clip, el);
- if (!res)
+ res = _add_track_element_to_tracks (timeline, clip, el, error);
+ if (!res) {
no_errors = FALSE;
+ if (error)
+ goto done;
+ }
}
}
+
+done:
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 * new_track)
+ GESTrack * new_track, GError ** error)
{
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);
g_list_copy_deep (timeline->tracks, (GCopyFunc) gst_object_ref, NULL);
timeline->priv->new_track = new_track ? gst_object_ref (new_track) : NULL;
UNLOCK_DYN (timeline);
+
/* create core elements */
for (tmp = tracks; tmp; tmp = tmp->next) {
GESTrack *track = GES_TRACK (tmp->data);
if (new_track && track != new_track)
continue;
+
list = ges_clip_create_track_elements (clip, track->type);
+ /* just_added only used for pointer comparison, so safe to include
+ * elements that may be destroyed because they fail to be added to
+ * the clip */
+ just_added = g_list_concat (just_added, list);
+
for (created = list; created; created = created->next) {
GESTimelineElement *el = created->data;
* 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))
+ ges_timeline_set_track_selection_error (timeline, FALSE, NULL);
+ if (!ges_container_add (GES_CONTAINER (clip), el)) {
+ no_errors = FALSE;
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);
+
+ if (error && !no_errors)
+ goto done;
+
+ if (ges_timeline_take_track_selection_error (timeline, error)) {
+ no_errors = FALSE;
+ if (error)
+ goto done;
+ /* else, carry on as much as we can */
+ }
}
- /* 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
/* 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, new_track,
- just_added))
+ just_added, error)) {
no_errors = FALSE;
+ if (error)
+ goto done;
+ }
+
if (!_add_clip_children_to_tracks (timeline, clip, FALSE, new_track,
- just_added))
+ just_added, error)) {
no_errors = FALSE;
+ if (error)
+ goto done;
+ }
+
+done:
+ g_list_free_full (tracks, gst_object_unref);
LOCK_DYN (timeline);
gst_clear_object (&timeline->priv->new_track);
/* returns TRUE if selecting of tracks did not error */
gboolean
-ges_timeline_add_clip (GESTimeline * timeline, GESClip * clip)
+ges_timeline_add_clip (GESTimeline * timeline, GESClip * clip, GError ** error)
{
GESProject *project;
gboolean ret;
- /* TODO: extend with GError ** argument, which is accepted by
- * ges_clip_add_child_to_track */
ges_timeline_element_set_timeline (GES_TIMELINE_ELEMENT (clip), timeline);
/* timeline-tree handles creation of auto-transitions */
ret = TRUE;
} else {
- ret = add_object_to_tracks (timeline, clip, NULL);
+ ret = add_object_to_tracks (timeline, clip, NULL, error);
}
GST_DEBUG ("Done");
/* add any existing clips to the timeline */
objects = ges_layer_get_clips (layer);
for (tmp = objects; tmp; tmp = tmp->next)
- ges_timeline_add_clip (timeline, tmp->data);
+ ges_timeline_add_clip (timeline, tmp->data, NULL);
g_list_free_full (objects, gst_object_unref);
return TRUE;
objects = ges_layer_get_clips (tmp->data);
for (obj = objects; obj; obj = obj->next)
- add_object_to_tracks (timeline, obj->data, track);
+ add_object_to_tracks (timeline, obj->data, track, NULL);
g_list_free_full (objects, gst_object_unref);
}
{
GESTrackElement *object = GES_TRACK_ELEMENT (element);
GESTimelineElement *parent = element->parent;
+ GError *error = NULL;
g_return_val_if_fail (object->priv->nleobject, FALSE);
if (inpoint && !object->priv->has_internal_source) {
if (GES_IS_CLIP (parent)
&& !ges_clip_can_set_inpoint_of_child (GES_CLIP (parent), object,
- inpoint)) {
+ inpoint, &error)) {
GST_WARNING_OBJECT (element, "Cannot set an in-point of %"
GST_TIME_FORMAT " because the parent clip %" GES_FORMAT
- " would not allow it", GST_TIME_ARGS (inpoint), GES_ARGS (parent));
+ " would not allow it%s%s", GST_TIME_ARGS (inpoint),
+ GES_ARGS (parent), error ? ": " : "", error ? error->message : "");
+ g_clear_error (&error);
return FALSE;
}
{
GESTrackElement *object = GES_TRACK_ELEMENT (element);
GESTimelineElement *parent = element->parent;
+ GError *error = NULL;
if (GST_CLOCK_TIME_IS_VALID (max_duration)
&& !object->priv->has_internal_source) {
if (GES_IS_CLIP (parent)
&& !ges_clip_can_set_max_duration_of_child (GES_CLIP (parent), object,
- max_duration)) {
+ max_duration, &error)) {
GST_WARNING_OBJECT (element, "Cannot set a max-duration of %"
GST_TIME_FORMAT " because the parent clip %" GES_FORMAT
- " would not allow it", GST_TIME_ARGS (max_duration), GES_ARGS (parent));
+ " would not allow it%s%s", GST_TIME_ARGS (max_duration),
+ GES_ARGS (parent), error ? ": " : "", error ? error->message : "");
+ g_clear_error (&error);
return FALSE;
}
{
GESTrackElement *object = GES_TRACK_ELEMENT (element);
GESTimelineElement *parent = element->parent;
+ GError *error = NULL;
g_return_val_if_fail (object->priv->nleobject, FALSE);
if (GES_IS_CLIP (parent)
&& !ges_clip_can_set_priority_of_child (GES_CLIP (parent), object,
- priority)) {
+ priority, &error)) {
GST_WARNING_OBJECT (element, "Cannot set a priority of %"
G_GUINT32_FORMAT " because the parent clip %" GES_FORMAT
- " would not allow it", priority, GES_ARGS (parent));
+ " would not allow it%s%s", priority, GES_ARGS (parent),
+ error ? ": " : "", error ? error->message : "");
+ g_clear_error (&error);
return FALSE;
}
ges_track_element_set_active (GESTrackElement * object, gboolean active)
{
GESTimelineElement *parent;
+ GError *error = NULL;
g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), FALSE);
g_return_val_if_fail (object->priv->nleobject, FALSE);
parent = GES_TIMELINE_ELEMENT_PARENT (object);
if (GES_IS_CLIP (parent)
- && !ges_clip_can_set_active_of_child (GES_CLIP (parent), object, active)) {
- GST_WARNING_OBJECT (object, "Cannot set active to %i because the parent "
- "clip %" GES_FORMAT " would not allow it", active, GES_ARGS (parent));
+ && !ges_clip_can_set_active_of_child (GES_CLIP (parent), object, active,
+ &error)) {
+ GST_WARNING_OBJECT (object,
+ "Cannot set active to %i because the parent clip %" GES_FORMAT
+ " would not allow it%s%s", active, GES_ARGS (parent), error ? ": " : "",
+ error ? error->message : "");
+ g_clear_error (&error);
return FALSE;
}
/* INTERNAL USAGE */
gboolean
-ges_track_element_set_track (GESTrackElement * object, GESTrack * track)
+ges_track_element_set_track (GESTrackElement * object, GESTrack * track,
+ GError ** error)
{
GESTimelineElement *parent = GES_TIMELINE_ELEMENT_PARENT (object);
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);
+ && !ges_clip_can_set_track_of_child (GES_CLIP (parent), object, track,
+ error)) {
+ GST_INFO_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;
}
*/
static gboolean
remove_object_internal (GESTrack * track, GESTrackElement * object,
- gboolean emit)
+ gboolean emit, GError ** error)
{
GESTrackPrivate *priv;
GstElement *nleobject;
return FALSE;
}
- if (!ges_track_element_set_track (object, NULL)) {
- GST_WARNING_OBJECT (track, "Failed to unset the track for %" GES_FORMAT,
+ if (!ges_track_element_set_track (object, NULL, error)) {
+ GST_INFO_OBJECT (track, "Failed to unset the track for %" GES_FORMAT,
GES_ARGS (object));
return FALSE;
}
static void
dispose_trackelements_foreach (GESTrackElement * trackelement, GESTrack * track)
{
- remove_object_internal (track, trackelement, TRUE);
+ remove_object_internal (track, trackelement, TRUE, NULL);
}
/* GstElement virtual methods */
static gboolean
remove_element_internal (GESTrack * track, GESTrackElement * object,
- gboolean emit)
+ gboolean emit, GError ** error)
{
GSequenceIter *it;
GESTrackPrivate *priv = track->priv;
it = g_hash_table_lookup (priv->trackelements_iter, object);
g_sequence_remove (it);
- if (remove_object_internal (track, object, emit) == TRUE) {
+ if (remove_object_internal (track, object, emit, error) == TRUE) {
ges_timeline_element_set_timeline (GES_TIMELINE_ELEMENT (object), NULL);
return TRUE;
}
/**
- * ges_track_add_element:
+ * ges_track_add_element_full:
* @track: A #GESTrack
* @object: (transfer floating): The element to add
+ * @error: (nullable): Return location for an error
*
* Adds the given track element to the track, which takes ownership of the
* element.
* Returns: %TRUE if @object was successfully added to @track.
*/
gboolean
-ges_track_add_element (GESTrack * track, GESTrackElement * object)
+ges_track_add_element_full (GESTrack * track, GESTrackElement * object,
+ GError ** error)
{
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);
+ g_return_val_if_fail (!error || !*error, FALSE);
el = GES_TIMELINE_ELEMENT (object);
return FALSE;
}
- if (!ges_track_element_set_track (object, track)) {
- GST_WARNING_OBJECT (track, "Failed to set the track for %" GES_FORMAT,
+ if (!ges_track_element_set_track (object, track, error)) {
+ GST_INFO_OBJECT (track, "Failed to set the track for %" GES_FORMAT,
GES_ARGS (object));
gst_object_ref_sink (object);
gst_object_unref (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);
+ if (!ges_track_element_set_track (object, NULL, NULL))
+ GST_ERROR_OBJECT (track, "Failed to unset track of element %"
+ GES_FORMAT, GES_ARGS (object));
gst_object_ref_sink (object);
gst_object_unref (object);
return FALSE;
* element to the track */
if (timeline
&& !timeline_tree_can_move_element (timeline_get_tree (timeline), el,
- GES_TIMELINE_ELEMENT_LAYER_PRIORITY (el), el->start, el->duration)) {
- GST_WARNING_OBJECT (track,
+ GES_TIMELINE_ELEMENT_LAYER_PRIORITY (el), el->start, el->duration,
+ error)) {
+ GST_INFO_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);
+ remove_element_internal (track, object, FALSE, NULL);
return FALSE;
}
return TRUE;
}
+/**
+ * ges_track_add_element:
+ * @track: A #GESTrack
+ * @object: (transfer floating): The element to add
+ *
+ * See ges_track_add_element(), which also gives an error.
+ *
+ * Returns: %TRUE if @object was successfully added to @track.
+ */
+gboolean
+ges_track_add_element (GESTrack * track, GESTrackElement * object)
+{
+ return ges_track_add_element_full (track, object, NULL);
+}
+
/**
* ges_track_get_elements:
* @track: A #GESTrack
}
/**
- * ges_track_remove_element:
+ * ges_track_remove_element_full:
* @track: A #GESTrack
* @object: The element to remove
+ * @error: (nullable): Return location for an error
*
* Removes the given track element from the track, which revokes
* ownership of the element.
* Returns: %TRUE if @object was successfully removed from @track.
*/
gboolean
-ges_track_remove_element (GESTrack * track, GESTrackElement * object)
+ges_track_remove_element_full (GESTrack * track, GESTrackElement * object,
+ GError ** error)
{
g_return_val_if_fail (GES_IS_TRACK (track), FALSE);
g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), FALSE);
+ g_return_val_if_fail (!error || !*error, FALSE);
if (!track->priv->timeline
|| !ges_timeline_is_disposed (track->priv->timeline))
CHECK_THREAD (track);
- return remove_element_internal (track, object, TRUE);
+ return remove_element_internal (track, object, TRUE, error);
+}
+
+/**
+ * ges_track_remove_element:
+ * @track: A #GESTrack
+ * @object: The element to remove
+ *
+ * See ges_track_remove_element_full(), which also returns an error.
+ *
+ * Returns: %TRUE if @object was successfully removed from @track.
+ */
+gboolean
+ges_track_remove_element (GESTrack * track, GESTrackElement * object)
+{
+ return ges_track_remove_element_full (track, object, NULL);
}
/**
GES_API
gboolean ges_track_commit (GESTrack *track);
GES_API
-void ges_track_set_timeline (GESTrack *track, GESTimeline *timeline);
+void ges_track_set_timeline (GESTrack *track,
+ GESTimeline *timeline);
GES_API
-gboolean ges_track_add_element (GESTrack *track, GESTrackElement *object);
+gboolean ges_track_add_element (GESTrack *track,
+ GESTrackElement *object);
GES_API
-gboolean ges_track_remove_element (GESTrack *track, GESTrackElement *object);
+gboolean ges_track_add_element_full (GESTrack *track,
+ GESTrackElement *object,
+ GError ** error);
GES_API
-void ges_track_set_create_element_for_gap_func (GESTrack *track, GESCreateElementForGapFunc func);
+gboolean ges_track_remove_element (GESTrack *track,
+ GESTrackElement *object);
GES_API
-void ges_track_set_mixing (GESTrack *track, gboolean mixing);
+gboolean ges_track_remove_element_full (GESTrack *track,
+ GESTrackElement *object,
+ GError ** error);
+GES_API
+void ges_track_set_create_element_for_gap_func (GESTrack *track,
+ GESCreateElementForGapFunc func);
+GES_API
+void ges_track_set_mixing (GESTrack *track,
+ gboolean mixing);
GES_API
gboolean ges_track_get_mixing (GESTrack *track);
GES_API
-void ges_track_set_restriction_caps (GESTrack *track, const GstCaps *caps);
+void ges_track_set_restriction_caps (GESTrack *track,
+ const GstCaps *caps);
GES_API
-void ges_track_update_restriction_caps (GESTrack *track, const GstCaps *caps);
+void ges_track_update_restriction_caps (GESTrack *track,
+ const GstCaps *caps);
GES_API
GstCaps * ges_track_get_restriction_caps (GESTrack * track);
GES_API
-GESTrack* ges_track_new (GESTrackType type, GstCaps * caps);
+GESTrack* ges_track_new (GESTrackType type,
+ GstCaps * caps);
G_END_DECLS
GESTrackElement *source, *effect, *effect2, *added, *added2, *added3;
GstControlSource *ctrl_source;
guint selection_called = 0;
+ GError *error = NULL;
ges_init ();
track1 = GES_TRACK (ges_video_track_new ());
track2 = GES_TRACK (ges_video_track_new ());
-
/* only add two for now */
fail_unless (ges_timeline_add_track (timeline, track1));
gst_object_unref (ctrl_source);
/* can't add to a track that does not belong to the timeline */
- fail_if (ges_clip_add_child_to_track (clip, source, track2, NULL));
+ fail_if (ges_clip_add_child_to_track (clip, source, track2, &error));
assert_num_children (clip, 3);
fail_unless (ges_track_element_get_track (source) == track1);
assert_num_in_track (track1, 3);
assert_num_in_track (track2, 0);
+ /* programming/usage error gives no error code/message */
+ fail_if (error);
/* can't add the clip to a track that already contains our source */
- fail_if (ges_clip_add_child_to_track (clip, source, track1, NULL));
+ fail_if (ges_clip_add_child_to_track (clip, source, track1, &error));
assert_num_children (clip, 3);
fail_unless (ges_track_element_get_track (source) == track1);
assert_num_in_track (track1, 3);
assert_num_in_track (track2, 0);
+ fail_if (error);
/* can't remove a core element from its track whilst a non-core sits
* above it */
assert_num_in_track (track2, 0);
/* can not add to the same track as it is currently in */
- fail_if (ges_clip_add_child_to_track (clip, effect, track1, NULL));
+ fail_if (ges_clip_add_child_to_track (clip, effect, track1, &error));
fail_unless (ges_track_element_get_track (effect) == track1);
assert_num_in_track (track1, 3);
assert_num_in_track (track2, 0);
+ fail_if (error);
/* adding another video track, select-tracks-for-object will do nothing
* since no each track element is already part of a track */
assert_num_in_track (track2, 0);
/* can not add effect to a track that does not contain a core child */
- fail_if (ges_clip_add_child_to_track (clip, effect, track2, NULL));
+ fail_if (ges_clip_add_child_to_track (clip, effect, track2, &error));
assert_num_children (clip, 3);
assert_num_in_track (track1, 3);
assert_num_in_track (track2, 0);
+ fail_if (error);
/* can add core */
- added = ges_clip_add_child_to_track (clip, source, track2, NULL);
+ added = ges_clip_add_child_to_track (clip, source, track2, &error);
fail_unless (added);
assert_num_children (clip, 4);
fail_unless (added != source);
fail_unless (ges_track_element_get_track (added) == track2);
assert_num_in_track (track1, 3);
assert_num_in_track (track2, 1);
+ fail_if (error);
assert_equal_children_properties (added, source);
assert_equal_bindings (added, source);
assert_equals_int (1,
ges_clip_get_top_effect_index (clip, GES_BASE_EFFECT (effect2)));
- added2 = ges_clip_add_child_to_track (clip, effect, track2, NULL);
+ added2 = ges_clip_add_child_to_track (clip, effect, track2, &error);
fail_unless (added2);
+ fail_if (error);
assert_num_children (clip, 5);
fail_unless (added2 != effect);
fail_unless (ges_track_element_get_track (effect) == track1);
assert_equals_int (2,
ges_clip_get_top_effect_index (clip, GES_BASE_EFFECT (effect2)));
- added3 = ges_clip_add_child_to_track (clip, effect2, track2, NULL);
+ added3 = ges_clip_add_child_to_track (clip, effect2, track2, &error);
fail_unless (added3);
+ fail_if (error);
assert_num_children (clip, 6);
fail_unless (added3 != effect2);
fail_unless (ges_track_element_get_track (effect2) == track1);
/* can not add the source to the track because it would overlap another
* source */
- fail_if (ges_clip_add_child_to_track (clip, source, track1, NULL));
+ fail_if (ges_clip_add_child_to_track (clip, source, track1, &error));
assert_num_children (clip, 3);
assert_num_in_track (track1, 4);
+ assert_GESError (error, GES_ERROR_INVALID_OVERLAP_IN_TRACK);
/* can not add source at time 23 because it would result in three
* overlapping sources in the track */
assert_set_start (clip, 23);
- fail_if (ges_clip_add_child_to_track (clip, source, track1, NULL));
+ fail_if (ges_clip_add_child_to_track (clip, source, track1, &error));
assert_num_children (clip, 3);
assert_num_in_track (track1, 4);
+ assert_GESError (error, GES_ERROR_INVALID_OVERLAP_IN_TRACK);
/* can add at 5, with overlap */
assert_set_start (clip, 5);
- added = ges_clip_add_child_to_track (clip, source, track1, NULL);
+ added = ges_clip_add_child_to_track (clip, source, track1, &error);
/* added is the source since it was not already in a track */
fail_unless (added == source);
+ fail_if (error);
assert_num_children (clip, 3);
/* 4 sources + 2 transitions */
assert_num_in_track (track1, 6);
/* also add effect */
- added = ges_clip_add_child_to_track (clip, effect, track1, NULL);
+ added = ges_clip_add_child_to_track (clip, effect, track1, &error);
/* added is the source since it was not already in a track */
fail_unless (added == effect);
+ fail_if (error);
assert_num_children (clip, 3);
assert_num_in_track (track1, 7);
- added = ges_clip_add_child_to_track (clip, effect2, track1, NULL);
+ added = ges_clip_add_child_to_track (clip, effect2, track1, &error);
/* added is the source since it was not already in a track */
fail_unless (added == effect2);
+ fail_if (error);
assert_num_children (clip, 3);
assert_num_in_track (track1, 8);
GESTrackElement *effect0, *effect1, *effect2;
GESTrack *track0, *track1;
gint limit_notify_count = 0;
+ GError *error = NULL;
ges_init ();
CHECK_OBJECT_PROPS_MAX (source1, 10, 16, 20, 36);
CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 10);
- fail_if (ges_clip_add_child_to_track (clip, effect0, track0, NULL));
+ fail_if (ges_clip_add_child_to_track (clip, effect0, track0, &error));
+ assert_GESError (error, GES_ERROR_INVALID_OVERLAP_IN_TRACK);
/* set max-duration to 11 and we are fine to select a track */
assert_set_max_duration (effect0, 11);
_assert_duration_limit (clip, 20);
fail_unless (ges_clip_add_child_to_track (clip, effect0, track0,
- NULL) == effect0);
+ &error) == effect0);
+ fail_if (error);
assert_equals_int (limit_notify_count, 2);
_assert_duration_limit (clip, 11);
free_children_properties (props2, num2); \
}
+#define assert_GESError(error, error_code) \
+{ \
+ fail_unless (error); \
+ fail_unless (error->domain == GES_ERROR); \
+ assert_equals_int (error->code, error_code); \
+ g_error_free (error); \
+ error = NULL; \
+}
+
void print_timeline(GESTimeline *timeline);
for effect in effects]
self.assertEqual(indexes, list(range(len(effects))))
+ def assertGESError(self, error, code, message=""):
+ if error is None:
+ raise AssertionError(
+ "{}{}Received no error".format(message, message and ": "))
+ if error.domain != "GES_ERROR":
+ raise AssertionError(
+ "{}{}Received error ({}) in domain {} rather than "
+ "GES_ERROR".format(
+ message, message and ": ", error.message, error.domain))
+ err_code = GES.Error(error.code)
+ if err_code != code:
+ raise AssertionError(
+ "{}{}Received {} error ({}) rather than {}".format(
+ message, message and ": ", err_code.value_name,
+ error.message, code.value_name))
class GESSimpleTimelineTest(GESTest):
def assertEdit(self, element, layer, mode, edge, position, snap,
snap_froms, snap_tos, new_props, new_transitions,
lost_transitions):
- if not element.edit([], layer, mode, edge, position):
+ if not element.edit_full(layer, mode, edge, position):
raise AssertionError(
"Edit of {} to layer {}, mode {}, edge {}, at position {} "
"failed when a success was expected".format(
snap_tos=snap_tos, new_transitions=new_transitions,
lost_transitions=lost_transitions)
- def assertFailEdit(self, element, layer, mode, edge, position):
- if element.edit([], layer, mode, edge, position):
- raise AssertionError(
- "Edit of {} to layer {}, mode {}, edge {}, at position {} "
- "succeeded when a failure was expected".format(
- element, layer, mode, edge, position))
+ def assertFailEdit(self, element, layer, mode, edge, position, err_code):
+ res = None
+ error = None
+ try:
+ res = element.edit_full(layer, mode, edge, position)
+ except GLib.Error as exception:
+ error = exception
+
+ if err_code is None:
+ if res is not False:
+ raise AssertionError(
+ "Edit of {} to layer {}, mode {}, edge {}, at "
+ "position {} succeeded when a failure was expected"
+ "".format(
+ element, layer, mode, edge, position))
+ if error is not None:
+ raise AssertionError(
+ "Edit of {} to layer {}, mode {}, edge {}, at "
+ "position {} did produced an error when none was "
+ "expected".format(
+ element, layer, mode, edge, position))
+ else:
+ self.assertGESError(
+ error, err_code,
+ "Edit of {} to layer {}, mode {}, edge {}, at "
+ "position {}".format(element, layer, mode, edge, position))
# should be no change or snapping if edit fails
self.assertTimelineConfig()
from gi.repository import Gst # noqa
from gi.repository import GES # noqa
+from gi.repository import GLib # noqa
import unittest # noqa
from unittest import mock
self.assertTrue(clip.set_duration(10))
# cannot trim to a 0 because effect0 would have a negative in-point
- self.assertFalse(
- clip.edit([], -1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, 0))
+ error = None
+ try:
+ clip.edit_full(-1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, 0)
+ except GLib.Error as err:
+ error = err
+ self.assertGESError(error, GES.Error.NEGATIVE_TIME)
self.assertEqual(clip.start, 10)
self.assertEqual(clip.inpoint, 12)
clip2 = self.add_clip(start=10, in_point=0, duration=4)
clip3 = self.add_clip(start=12, in_point=0, duration=3)
- self.assertIsNone(clip1.split(13))
- self.assertIsNone(clip1.split(8))
-
- self.assertIsNone(clip3.split(12))
- self.assertIsNone(clip3.split(15))
+ self.assertIsNone(clip1.split_full(13))
+ self.assertIsNone(clip1.split_full(8))
+ self.assertIsNone(clip3.split_full(12))
+ self.assertIsNone(clip3.split_full(15))
+
+ def _fail_split(self, clip, position):
+ split = None
+ error = None
+ try:
+ split = clip.split_full(position)
+ except GLib.Error as err:
+ error = err
+ self.assertGESError(error, GES.Error.INVALID_OVERLAP_IN_TRACK)
+ self.assertIsNone(split)
def test_split_with_transition(self):
self.track_types = [GES.TrackType.AUDIO]
clip0 = self.add_clip(start=0, in_point=0, duration=50)
clip1 = self.add_clip(start=20, in_point=0, duration=50)
+ clip2 = self.add_clip(start=60, in_point=0, duration=20)
self.assertTimelineTopology([
[
(GES.TestClip, 0, 50),
(GES.TransitionClip, 20, 30),
- (GES.TestClip, 20, 50)
+ (GES.TestClip, 20, 50),
+ (GES.TransitionClip, 60, 10),
+ (GES.TestClip, 60, 20),
]
])
- # Split should file as the first part of the split
+ # Split should fail as the first part of the split
# would be fully overlapping clip0
- self.assertIsNone(clip1.split(40))
+ self._fail_split(clip1, 40)
+
+ self.assertTimelineTopology([
+ [
+ (GES.TestClip, 0, 50),
+ (GES.TransitionClip, 20, 30),
+ (GES.TestClip, 20, 50),
+ (GES.TransitionClip, 60, 10),
+ (GES.TestClip, 60, 20),
+ ]
+ ])
+
+ # same with end of the clip
+ self._fail_split(clip1, 65)
+
self.assertTimelineTopology([
[
(GES.TestClip, 0, 50),
(GES.TransitionClip, 20, 30),
- (GES.TestClip, 20, 50)
+ (GES.TestClip, 20, 50),
+ (GES.TransitionClip, 60, 10),
+ (GES.TestClip, 60, 20),
]
])
class TestConfigurationRules(common.GESSimpleTimelineTest):
- def _try_add_clip(self, start, duration, layer=None):
+ def _try_add_clip(self, start, duration, layer=None, error=None):
if layer is None:
layer = self.layer
asset = GES.Asset.request(GES.TestClip, None)
+ found_err = None
+ clip = None
# large inpoint to allow trims
- return layer.add_asset (asset, start, 1000, duration,
- GES.TrackType.UNKNOWN)
+ try:
+ clip = layer.add_asset_full(
+ asset, start, 1000, duration, GES.TrackType.UNKNOWN)
+ except GLib.Error as err:
+ found_err = err
+ if error is None:
+ self.assertIsNotNone(clip)
+ else:
+ self.assertIsNone(clip)
+ self.assertGESError(found_err, error)
+ return clip
def test_full_overlap_add(self):
clip1 = self._try_add_clip(50, 50)
- self.assertIsNotNone(clip1)
- self.assertIsNone(self._try_add_clip(50, 50))
- self.assertIsNone(self._try_add_clip(49, 51))
- self.assertIsNone(self._try_add_clip(51, 49))
+ self._try_add_clip(50, 50, error=GES.Error.INVALID_OVERLAP_IN_TRACK)
+ self._try_add_clip(49, 51, error=GES.Error.INVALID_OVERLAP_IN_TRACK)
+ self._try_add_clip(51, 49, error=GES.Error.INVALID_OVERLAP_IN_TRACK)
def test_triple_overlap_add(self):
clip1 = self._try_add_clip(0, 50)
- self.assertIsNotNone(clip1)
clip2 = self._try_add_clip(40, 50)
- self.assertIsNotNone(clip2)
- self.assertIsNone(self._try_add_clip(40, 10))
- self.assertIsNone(self._try_add_clip(30, 30))
- self.assertIsNone(self._try_add_clip(1, 88))
+ self._try_add_clip(39, 12, error=GES.Error.INVALID_OVERLAP_IN_TRACK)
+ self._try_add_clip(30, 30, error=GES.Error.INVALID_OVERLAP_IN_TRACK)
+ self._try_add_clip(1, 88, error=GES.Error.INVALID_OVERLAP_IN_TRACK)
def test_full_overlap_move(self):
clip1 = self._try_add_clip(0, 50)
- self.assertIsNotNone(clip1)
clip2 = self._try_add_clip(50, 50)
- self.assertIsNotNone(clip2)
self.assertFalse(clip2.set_start(0))
def test_triple_overlap_move(self):
clip1 = self._try_add_clip(0, 50)
- self.assertIsNotNone(clip1)
clip2 = self._try_add_clip(40, 50)
- self.assertIsNotNone(clip2)
clip3 = self._try_add_clip(100, 60)
- self.assertIsNotNone(clip3)
self.assertFalse(clip3.set_start(30))
def test_full_overlap_move_into_layer(self):
clip1 = self._try_add_clip(0, 50)
- self.assertIsNotNone(clip1)
layer2 = self.timeline.append_layer()
clip2 = self._try_add_clip(0, 50, layer2)
- self.assertIsNotNone(clip2)
- self.assertFalse(clip2.move_to_layer(self.layer))
+ res = None
+ try:
+ res = clip2.move_to_layer_full(self.layer)
+ except GLib.Error as error:
+ self.assertGESError(error, GES.Error.INVALID_OVERLAP_IN_TRACK)
+ self.assertIsNone(res)
def test_triple_overlap_move_into_layer(self):
clip1 = self._try_add_clip(0, 50)
- self.assertIsNotNone(clip1)
clip2 = self._try_add_clip(40, 50)
- self.assertIsNotNone(clip2)
layer2 = self.timeline.append_layer()
clip3 = self._try_add_clip(30, 30, layer2)
- self.assertIsNotNone(clip3)
- self.assertFalse(clip3.move_to_layer(self.layer))
+ res = None
+ try:
+ res = clip3.move_to_layer_full(self.layer)
+ except GLib.Error as error:
+ self.assertGESError(error, GES.Error.INVALID_OVERLAP_IN_TRACK)
+ self.assertIsNone(res)
def test_full_overlap_trim(self):
clip1 = self._try_add_clip(0, 50)
- self.assertIsNotNone(clip1)
clip2 = self._try_add_clip(50, 50)
- self.assertIsNotNone(clip2)
self.assertFalse(clip2.trim(0))
self.assertFalse(clip1.set_duration(100))
def test_triple_overlap_trim(self):
clip1 = self._try_add_clip(0, 20)
- self.assertIsNotNone(clip1)
clip2 = self._try_add_clip(10, 30)
- self.assertIsNotNone(clip2)
clip3 = self._try_add_clip(30, 20)
- self.assertIsNotNone(clip3)
self.assertFalse(clip3.trim(19))
self.assertFalse(clip1.set_duration(31))
# cannot move c0 up one layer because it would cause a triple
# overlap between c1, c2 and c3 when g0 moves
self.assertFailEdit(
- c0, 1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, 23)
+ c0, 1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, 23,
+ GES.Error.INVALID_OVERLAP_IN_TRACK)
# cannot move c0, without moving g1, to 21 layer 1 because it
# would be completely overlapped by c2
self.assertFailEdit(
- c0, 1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_START, 20)
+ c0, 1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_START, 20,
+ GES.Error.INVALID_OVERLAP_IN_TRACK)
# cannot move c1, without moving g1, with end 25 because it
# would be completely overlapped by c2
self.assertFailEdit(
- c0, 1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_END, 25)
+ c0, 1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_END, 25,
+ GES.Error.INVALID_OVERLAP_IN_TRACK)
# cannot move g0 to layer 0 because it would make c0 go to a
# negative layer
self.assertFailEdit(
- g0, 0, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, 10)
+ g0, 0, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, 10,
+ GES.Error.NEGATIVE_LAYER)
# cannot move c1 for same reason
- self.assertFalse(
- c1.move_to_layer(self.timeline.get_layer(0)))
+ error = None
+ try:
+ c1.move_to_layer_full(self.timeline.get_layer(0))
+ except GLib.Error as err:
+ error = err
+ self.assertGESError(error, GES.Error.NEGATIVE_LAYER)
self.assertTimelineConfig({}, [])
# failure with snapping
# cannot move to 0 because end edge of c0 would snap with end of
# c3, making the new start become negative
self.assertFailEdit(
- g0, 1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, 0)
+ g0, 1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, 0,
+ GES.Error.NEGATIVE_TIME)
# cannot move start of c1 to 14 because snapping causes a full
# overlap with c0
self.assertFailEdit(
- c1, 1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_START, 14)
+ c1, 1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_START, 14,
+ GES.Error.INVALID_OVERLAP_IN_TRACK)
# cannot move end of c2 to 21 because snapping causes a full
# overlap with c0
self.assertFailEdit(
- c2, 1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_END, 21)
+ c2, 1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_END, 21,
+ GES.Error.INVALID_OVERLAP_IN_TRACK)
# successes
self.timeline.set_snapping_distance(3)
# would cause negative layer priority for c0
self.assertFailEdit(
- c1, 0, GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_NONE, 5)
+ c1, 0, GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_NONE, 5,
+ GES.Error.NEGATIVE_LAYER)
# would lead to c2 fully overlapping c3 since c2 does ripple
# but c3 does not(c3 shares a toplevel with c0, and
# GES_EDGE_START, same as NORMAL mode, does not move the
# toplevel
self.assertFailEdit(
- c2, 1, GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_END, 25)
+ c2, 1, GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_END, 25,
+ GES.Error.INVALID_OVERLAP_IN_TRACK)
# would lead to c2 fully overlapping c3 since c2 does not
# ripple but c3 does
self.assertFailEdit(
- c0, 0, GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_START, 13)
+ c0, 0, GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_START, 13,
+ GES.Error.INVALID_OVERLAP_IN_TRACK)
# add two more clips
# cannot trim end of g0 to 16 because a0 and a1 would fully
# overlap
self.assertFailEdit(
- g0, 1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_END, 15)
+ g0, 1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_END, 15,
+ GES.Error.INVALID_OVERLAP_IN_TRACK)
# cannot edit to new layer because there would be triple overlaps
# between v2, v3, v4 and v5
self.assertFailEdit(
- g2, 1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_END, 20)
+ g2, 1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_END, 20,
+ GES.Error.INVALID_OVERLAP_IN_TRACK)
# cannot trim g1 end to 14 because it would result in a negative
# duration for a2 and a4
self.assertFailEdit(
- g1, 1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_END, 14)
+ g1, 1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_END, 14,
+ GES.Error.NEGATIVE_TIME)
# cannot trim end of v2 below its start
self.assertFailEdit(
- v2, 2, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_END, 2)
+ v2, 2, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_END, 2,
+ GES.Error.NEGATIVE_TIME)
# cannot trim end of g0 because a0's duration-limit would be
# exceeded
self.assertFailEdit(
- g0, 0, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_END, 23)
+ g0, 0, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_END, 23,
+ GES.Error.NOT_ENOUGH_INTERNAL_CONTENT)
# cannot trim g0 to 12 because a0 and a1 would fully overlap
self.assertFailEdit(
- g0, 0, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, 12)
+ g0, 0, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, 12,
+ GES.Error.INVALID_OVERLAP_IN_TRACK)
# cannot trim start of v2 beyond its end point
self.assertFailEdit(
- v2, 2, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, 20)
+ v2, 2, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, 20,
+ GES.Error.NEGATIVE_TIME)
# with snapping
self.timeline.set_snapping_distance(4)
# cannot trim end of g2 to 19 because v1 and v2 would fully
# overlap after snapping to v5 start edge(18)
self.assertFailEdit(
- g2, 0, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_END, 19)
+ g2, 0, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_END, 19,
+ GES.Error.INVALID_OVERLAP_IN_TRACK)
# cannot trim g2 to 3 because it would snap to start edge of
# v4(0), causing v2's in-point to be negative
self.assertFailEdit(
- g2, 0, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, 3)
+ g2, 0, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, 3,
+ GES.Error.NEGATIVE_TIME)
# success
# cannot roll c10 to 22, which snaps to 23, because it will
# extend c5 beyond its duration limit of 8
self.assertFailEdit(
- c10, -1, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_START, 22)
+ c10, -1, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_START, 22,
+ GES.Error.NOT_ENOUGH_INTERNAL_CONTENT)
# same with g2
self.assertFailEdit(
- g2, -1, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_START, 22)
+ g2, -1, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_START, 22,
+ GES.Error.NOT_ENOUGH_INTERNAL_CONTENT)
# cannot roll end c9 to 8, which snaps to 7, because it would
# cause c3's in-point to become negative
self.assertFailEdit(
- c9, -1, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_END, 8)
+ c9, -1, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_END, 8,
+ GES.Error.NEGATIVE_TIME)
# same with g1
self.assertFailEdit(
- g1, -1, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_END, 8)
+ g1, -1, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_END, 8,
+ GES.Error.NEGATIVE_TIME)
# cannot roll c13 to 19, snap to 20, because it would cause
# c4 to fully overlap c5
self.assertFailEdit(
- c13, -1, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_START, 19)
+ c13, -1, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_START, 19,
+ GES.Error.INVALID_OVERLAP_IN_TRACK)
# cannot roll c12 to 11, snap to 10, because it would cause
# c3 to fully overlap c4
self.assertFailEdit(
- c12, -1, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_END, 11)
+ c12, -1, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_END, 11,
+ GES.Error.INVALID_OVERLAP_IN_TRACK)
+ # give c6 a bit more allowed duration so we can focus on c9
+ self.assertTrue(c6.set_inpoint(10))
+ self.assertTimelineConfig({ c6 : {"in-point": 10}})
# cannot roll c6 to 0 because it would cause c9 to be trimmed
# below its start
self.assertFailEdit(
- c6, -1, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_START, 0)
-
+ c6, -1, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_START, 0,
+ GES.Error.NEGATIVE_TIME)
+ # set back
+ self.assertTrue(c6.set_inpoint(7))
+ self.assertTimelineConfig({ c6 : {"in-point": 7}})
+
+ # give c7 a bit more allowed duration so we can focus on c10
+ self.assertTrue(c7.set_inpoint(0))
+ self.assertTimelineConfig({ c7 : {"in-point": 0}})
# cannot roll end c7 to 30 because it would cause c10 to be
# trimmed beyond its end
self.assertFailEdit(
- c7, -1, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_END, 30)
+ c7, -1, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_END, 30,
+ GES.Error.NEGATIVE_TIME)
+ # set back
+ self.assertTrue(c7.set_inpoint(1))
+ self.assertTimelineConfig({ c7 : {"in-point": 1}})
# moving layer is not supported
self.assertFailEdit(
- c0, 2, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_START, 7)
+ c0, 2, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_START, 7, None)
self.assertFailEdit(
- c0, 2, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_END, 23)
+ c0, 2, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_END, 23, None)
# successes
self.timeline.set_snapping_distance(0)