EDIT_MOVE,
EDIT_TRIM_START,
EDIT_TRIM_END,
+ EDIT_TRIM_INPOINT_ONLY,
} ElementEditMode;
typedef struct _EditData
*negative_start = FALSE;
new_end = _clock_time_minus_diff (current_end, offset, negative_end);
break;
+ case EDIT_TRIM_INPOINT_ONLY:
+ GST_ERROR_OBJECT (element, "Trim in-point only not handled");
+ break;
}
if (start)
*start = new_start;
g_node_traverse (node, G_IN_ORDER, G_TRAVERSE_LEAVES, -1,
(GNodeTraverseFunc) find_source_at_edge, &data);
break;
+ case EDIT_TRIM_INPOINT_ONLY:
+ GST_ERROR_OBJECT (element, "Trim in-point only not handled");
+ goto done;
}
for (tmp = data.sources; tmp; tmp = tmp->next) {
/* only snap the start of the source */
find_snap_for_element (source, end, negative_end, &data);
break;
+ case EDIT_TRIM_INPOINT_ONLY:
+ GST_ERROR_OBJECT (element, "Trim in-point only not handled");
+ goto done;
}
}
GST_TIME_ARGS (duration_limit));
}
+static void
+set_inpoint_breaks_max_duration_error (GError ** error,
+ GESTimelineElement * element, GstClockTime inpoint,
+ GstClockTime max_duration)
+{
+ g_set_error (error, GES_ERROR, GES_ERROR_NOT_ENOUGH_INTERNAL_CONTENT,
+ "The element \"%s\" would have an in-point of %" GST_TIME_FORMAT
+ " that would break its max-duration of %" GST_TIME_FORMAT,
+ GES_TIMELINE_ELEMENT_NAME (element), GST_TIME_ARGS (inpoint),
+ GST_TIME_ARGS (max_duration));
+}
+
static gboolean
set_layer_priority (GESTimelineElement * element, EditData * data,
GError ** error)
}
static gboolean
-set_edit_trim_start_inpoint_value (GESTimelineElement * element,
- EditData * data, GError ** error)
+set_edit_trim_start_clip_inpoints (GESClip * clip, EditData * clip_data,
+ GHashTable * edit_table, GError ** error)
{
- gboolean negative = FALSE;
- GstClockTime new_inpoint = _clock_time_minus_diff (element->inpoint,
- data->offset, &negative);
- if (negative || !GST_CLOCK_TIME_IS_VALID (new_inpoint)) {
- GST_INFO_OBJECT (element, "Cannot trim start of %" GES_FORMAT
+ gboolean ret = FALSE;
+ GList *tmp;
+ GstClockTime duration_limit;
+ GstClockTime clip_inpoint;
+ GstClockTime new_start = clip_data->start;
+ gboolean no_core = FALSE;
+ GHashTable *child_inpoints;
+
+ child_inpoints = g_hash_table_new_full (NULL, NULL, gst_object_unref, g_free);
+
+ clip_inpoint = ges_clip_get_core_internal_time_from_timeline_time (clip,
+ new_start, &no_core, error);
+
+ if (no_core) {
+ GST_INFO_OBJECT (clip, "Clip %" GES_FORMAT " has no active core "
+ "children with an internal source. Not setting in-point during "
+ "trim to start", GES_ARGS (clip));
+ clip_inpoint = GES_TIMELINE_ELEMENT_INPOINT (clip);
+ } else if (!GST_CLOCK_TIME_IS_VALID (clip_inpoint)) {
+ GST_INFO_OBJECT (clip, "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;
+ "invalid in-point for its core children", GES_ARGS (clip),
+ clip_data->offset);
+ goto done;
+ } else {
+ GST_INFO_OBJECT (clip, "Clip %" GES_FORMAT " will have its in-point "
+ " set to %" GST_TIME_FORMAT " because its start is being trimmed "
+ "to %" GST_TIME_FORMAT, GES_ARGS (clip),
+ GST_TIME_ARGS (clip_inpoint), GST_TIME_ARGS (new_start));
+ clip_data->inpoint = clip_inpoint;
}
- data->inpoint = new_inpoint;
- return TRUE;
-}
-
-static gboolean
-set_edit_trim_start_non_core_children (GESTimelineElement * clip,
- GstClockTimeDiff offset, GHashTable * edit_table, GError ** error)
-{
- GList *tmp;
- GESTimelineElement *child;
- GESTrackElement *el;
- EditData *data;
/* need to set in-point of active non-core children to keep their
- * internal content at the same timeline position. This also ensures
- * the duration-limit will not be broken */
+ * internal content at the same timeline position */
for (tmp = GES_CONTAINER_CHILDREN (clip); tmp; tmp = tmp->next) {
- child = tmp->data;
- el = tmp->data;
- if (ges_track_element_has_internal_source (el)
- && ges_track_element_is_active (el)
- && !ges_track_element_is_core (el)) {
-
- GST_INFO_OBJECT (child, "Setting track element %s to trim in-point "
- "with offset %" G_GINT64_FORMAT " since the parent clip %"
- GES_FORMAT " is being trimmed at its start", child->name, offset,
- GES_ARGS (clip));
-
- if (g_hash_table_contains (edit_table, child)) {
- GST_ERROR_OBJECT (child, "Already set to be edited");
- return FALSE;
+ GESTimelineElement *child = tmp->data;
+ GESTrackElement *el = tmp->data;
+ GstClockTime new_inpoint = child->inpoint;
+ GstClockTime *inpoint_p;
+
+ if (ges_track_element_has_internal_source (el)) {
+ if (ges_track_element_is_core (el)) {
+ new_inpoint = clip_inpoint;
+ } else if (ges_track_element_is_active (el)) {
+ EditData *data;
+
+ if (g_hash_table_contains (edit_table, child)) {
+ GST_ERROR_OBJECT (child, "Already set to be edited");
+ goto done;
+ }
+
+ new_inpoint = ges_clip_get_internal_time_from_timeline_time (clip, el,
+ new_start, error);
+
+ if (!GST_CLOCK_TIME_IS_VALID (new_inpoint)) {
+ GST_INFO_OBJECT (clip, "Cannot trim start of %" GES_FORMAT
+ " to %" GST_TIME_FORMAT " because it would result in an "
+ "invalid in-point for the non-core child %" GES_FORMAT,
+ GES_ARGS (clip), GST_TIME_ARGS (new_start), GES_ARGS (child));
+ goto done;
+ }
+
+ GST_INFO_OBJECT (child, "Setting track element %s to trim "
+ "in-point to %" GST_TIME_FORMAT " since the parent clip %"
+ GES_FORMAT " is being trimmed to start %" GST_TIME_FORMAT,
+ child->name, GST_TIME_ARGS (new_inpoint), GES_ARGS (clip),
+ GST_TIME_ARGS (new_start));
+
+ data = new_edit_data (EDIT_TRIM_INPOINT_ONLY, 0, 0);
+ data->inpoint = new_inpoint;
+ g_hash_table_insert (edit_table, child, data);
}
+ }
- 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, error))
- return FALSE;
+ if (GES_CLOCK_TIME_IS_LESS (child->maxduration, new_inpoint)) {
+ GST_INFO_OBJECT (clip, "Cannot trim start of %" GES_FORMAT
+ " to %" GST_TIME_FORMAT " because it would result in an "
+ "in-point of %" GST_TIME_FORMAT " for the child %" GES_FORMAT
+ ", which breaks its max-duration", GES_ARGS (clip),
+ GST_TIME_ARGS (new_start), GST_TIME_ARGS (new_inpoint),
+ GES_ARGS (child));
+
+ set_inpoint_breaks_max_duration_error (error, child, new_inpoint,
+ child->maxduration);
+ goto done;
}
+
+ inpoint_p = g_new (GstClockTime, 1);
+ *inpoint_p = new_inpoint;
+ g_hash_table_insert (child_inpoints, gst_object_ref (child), inpoint_p);
}
- return TRUE;
+
+ duration_limit =
+ ges_clip_duration_limit_with_new_children_inpoints (clip, child_inpoints);
+
+ if (GES_CLOCK_TIME_IS_LESS (duration_limit, clip_data->duration)) {
+ GST_INFO_OBJECT (clip, "Cannot trim start of %" GES_FORMAT
+ " to %" GST_TIME_FORMAT " because it would result in a "
+ "duration of %" GST_TIME_FORMAT " that breaks its new "
+ "duration-limit of %" GST_TIME_FORMAT, GES_ARGS (clip),
+ GST_TIME_ARGS (new_start), GST_TIME_ARGS (clip_data->duration),
+ GST_TIME_ARGS (duration_limit));
+
+ set_breaks_duration_limit_error (error, clip, clip_data->duration,
+ duration_limit);
+ goto done;
+ }
+
+ ret = TRUE;
+
+done:
+ g_hash_table_unref (child_inpoints);
+
+ return ret;
}
/* trim the start of a clip or a track element */
return TRUE;
if (GES_IS_CLIP (element)) {
- if (!set_edit_trim_start_inpoint_value (element, data, error))
- return FALSE;
- if (!set_edit_trim_start_non_core_children (element, data->offset,
+ if (!set_edit_trim_start_clip_inpoints (GES_CLIP (element), data,
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, error))
+ GstClockTime new_inpoint =
+ _clock_time_minus_diff (element->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;
+ }
}
- /* NOTE: without time effects, the duration-limit will increase with
- * a decrease in in-point by the same amount that duration increases,
- * and vis-versa. So the new duration-limit should remain above the
- * new duration */
-
GST_INFO_OBJECT (element, "%s will trim start by setting start to %"
GST_TIME_FORMAT ", in-point to %" GST_TIME_FORMAT " and duration "
"to %" GST_TIME_FORMAT, element->name, GST_TIME_ARGS (data->start),
return set_edit_trim_start_values (element, data, edit_table, error);
case EDIT_TRIM_END:
return set_edit_trim_end_values (element, data, error);
+ case EDIT_TRIM_INPOINT_ONLY:
+ GST_ERROR_OBJECT (element, "Trim in-point only not handled");
+ return FALSE;
}
return FALSE;
}
case EDIT_TRIM_END:
GST_LOG_OBJECT (element, "%s set to trim end", element->name);
break;
+ case EDIT_TRIM_INPOINT_ONLY:
+ GST_ERROR_OBJECT (element, "%s set to trim in-point only", element->name);
+ return FALSE;
}
g_hash_table_insert (edits, element, new_edit_data (mode, 0, 0));
GST_TIME_ARGS (_END (element)),
GST_TIME_ARGS (element->start + edit->duration));
break;
+ case EDIT_TRIM_INPOINT_ONLY:
+ GST_INFO_OBJECT (element, "Trimming %s in-point from %"
+ GST_TIME_FORMAT " to %" GST_TIME_FORMAT, element->name,
+ GST_TIME_ARGS (element->inpoint), GST_TIME_ARGS (edit->inpoint));
+ break;
}
if (!GES_IS_CLIP (element) && !GES_IS_TRACK_ELEMENT (element)) {
self.assertEqual(effect3.inpoint, 20)
self.assertTrue(
- clip.edit([], -1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, 5))
+ clip.edit_full(-1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, 5))
self.assertEqual(clip.start, 5)
self.assertEqual(clip.duration, 15)
self.assertEqual(effect3.inpoint, 20)
self.assertTrue(
- clip.edit([], -1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, 15))
+ clip.edit_full(-1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, 15))
self.assertEqual(clip.start, 15)
self.assertEqual(clip.duration, 5)
self.assertEqual(effect2.inpoint, 13)
self.assertEqual(effect3.inpoint, 20)
+ def test_trim_time_effects(self):
+ self.track_types = [GES.TrackType.VIDEO]
+ super().setUp()
+ clip = self.append_clip()
+ self.assertTrue(clip.set_inpoint(12))
+ self.assertTrue(clip.set_max_duration(30))
+ self.assertEqual(clip.get_duration_limit(), 18)
+
+ children = clip.get_children(False)
+ self.assertTrue(children)
+ self.assertEqual(len(children), 1)
+
+ source = children[0]
+ self.assertEqual(source.get_inpoint(), 12)
+ self.assertEqual(source.get_max_duration(), 30)
+
+ rate0 = GES.Effect.new("videorate rate=0.25")
+
+ overlay = GES.Effect.new("textoverlay")
+ overlay.set_has_internal_source(True)
+ self.assertTrue(overlay.set_inpoint(5))
+ self.assertTrue(overlay.set_max_duration(16))
+
+ rate1 = GES.Effect.new("videorate rate=2.0")
+
+ self.assertTrue(clip.add(rate0))
+ self.assertTrue(clip.add(overlay))
+ self.assertTrue(clip.add(rate1))
+
+ # source -> rate1 -> overlay -> rate0
+ # in-point/max-dur 12-30 5-16
+ # internal
+ # start/end 12-30 0-9 5-14 0-36
+ self.assertEqual(clip.get_duration_limit(), 36)
+ self.assertTrue(clip.set_start(40))
+ self.assertTrue(clip.set_duration(10))
+
+ # cannot trim to a 16 because overlay would have a negative in-point
+ error = None
+ try:
+ clip.edit_full(-1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, 16)
+ except GLib.Error as err:
+ error = err
+ self.assertGESError(error, GES.Error.NEGATIVE_TIME)
+
+ self.assertEqual(clip.get_start(), 40)
+ self.assertEqual(clip.get_duration(), 10)
+ self.assertEqual(source.get_inpoint(), 12)
+ self.assertEqual(source.get_max_duration(), 30)
+ self.assertEqual(overlay.get_inpoint(), 5)
+ self.assertEqual(overlay.get_max_duration(), 16)
+
+ # trim backwards to 20
+ self.assertTrue(
+ clip.edit_full(-1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, 20))
+
+ self.assertEqual(clip.get_start(), 20)
+ self.assertEqual(clip.get_duration(), 30)
+ # reduced by 10
+ self.assertEqual(source.get_inpoint(), 2)
+ self.assertEqual(source.get_max_duration(), 30)
+ # reduced by 5
+ self.assertEqual(overlay.get_inpoint(), 0)
+ self.assertEqual(overlay.get_max_duration(), 16)
+
+ # trim forwards to 28
+ self.assertTrue(
+ clip.edit_full(-1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, 28))
+ self.assertEqual(clip.get_start(), 28)
+ self.assertEqual(clip.get_duration(), 22)
+ # increased by 4
+ self.assertEqual(source.get_inpoint(), 6)
+ self.assertEqual(source.get_max_duration(), 30)
+ # increased by 2
+ self.assertEqual(overlay.get_inpoint(), 2)
+ self.assertEqual(overlay.get_max_duration(), 16)
+
def test_ripple_end(self):
clip = self.append_clip()
clip.set_max_duration(20)