mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-04 07:26:33 +00:00
6b7c658b6a
We were implementing the logic for moving/trimming elements specific to SourceClip but this was not correct ass the new timeline tree allows us to handle that for all element types in a generic and nice way. This make us need to have groups trimming properly implemented in the timeline tree, leading to some fixes in the group tests. This adds tests for the various cases known to not be handled properly by the previous code. Fixes https://gitlab.freedesktop.org/gstreamer/gst-editing-services/issues/92
1262 lines
45 KiB
Python
1262 lines
45 KiB
Python
# -*- coding: utf-8 -*-
|
|
#
|
|
# Copyright (c) 2016 Alexandru Băluț <alexandru.balut@gmail.com>
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU Lesser General Public
|
|
# License as published by the Free Software Foundation; either
|
|
# version 2.1 of the License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
# Lesser General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Lesser General Public
|
|
# License along with this program; if not, write to the
|
|
# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
# Boston, MA 02110-1301, USA.
|
|
|
|
from . import overrides_hack
|
|
|
|
import tempfile # noqa
|
|
import gi
|
|
|
|
gi.require_version("Gst", "1.0")
|
|
gi.require_version("GES", "1.0")
|
|
|
|
from gi.repository import Gst # noqa
|
|
from gi.repository import GES # noqa
|
|
import unittest # noqa
|
|
from unittest import mock
|
|
|
|
from . import common # noqa
|
|
|
|
Gst.init(None)
|
|
GES.init()
|
|
|
|
|
|
class TestTimeline(common.GESSimpleTimelineTest):
|
|
|
|
def test_signals_not_emitted_when_loading(self):
|
|
mainloop = common.create_main_loop()
|
|
timeline = common.create_project(with_group=True, saved=True)
|
|
|
|
# Reload the project, check the group.
|
|
project = GES.Project.new(uri=timeline.get_asset().props.uri)
|
|
|
|
loaded_called = False
|
|
def loaded(unused_project, unused_timeline):
|
|
nonlocal loaded_called
|
|
loaded_called = True
|
|
mainloop.quit()
|
|
project.connect("loaded", loaded)
|
|
|
|
timeline = project.extract()
|
|
|
|
signals = ["layer-added", "group-added", "track-added"]
|
|
handle = mock.Mock()
|
|
for signal in signals:
|
|
timeline.connect(signal, handle)
|
|
|
|
mainloop.run()
|
|
self.assertTrue(loaded_called)
|
|
handle.assert_not_called()
|
|
|
|
def test_deeply_nested_serialization(self):
|
|
deep_timeline = common.create_project(with_group=True, saved="deep")
|
|
deep_project = deep_timeline.get_asset()
|
|
|
|
deep_asset = GES.UriClipAsset.request_sync(deep_project.props.id)
|
|
|
|
nested_timeline = common.create_project(with_group=False, saved=False)
|
|
nested_project = nested_timeline.get_asset()
|
|
nested_project.add_asset(deep_project)
|
|
nested_timeline.append_layer().add_asset(deep_asset, 0, 0, 5 * Gst.SECOND, GES.TrackType.UNKNOWN)
|
|
|
|
uri = "file://%s" % tempfile.NamedTemporaryFile(suffix="-nested.xges").name
|
|
nested_timeline.get_asset().save(nested_timeline, uri, None, overwrite=True)
|
|
|
|
asset = GES.UriClipAsset.request_sync(nested_project.props.id)
|
|
project = self.timeline.get_asset()
|
|
project.add_asset(nested_project)
|
|
refclip = self.layer.add_asset(asset, 0, 0, 5 * Gst.SECOND, GES.TrackType.VIDEO)
|
|
|
|
uri = "file://%s" % tempfile.NamedTemporaryFile(suffix=".xges").name
|
|
project.save(self.timeline, uri, None, overwrite=True)
|
|
self.assertEqual(len(project.list_assets(GES.Extractable)), 2)
|
|
|
|
mainloop = common.create_main_loop()
|
|
def loaded_cb(unused_project, unused_timeline):
|
|
mainloop.quit()
|
|
project.connect("loaded", loaded_cb)
|
|
|
|
# Extract again the timeline and compare with previous one.
|
|
timeline = project.extract()
|
|
mainloop.run()
|
|
layer, = timeline.get_layers()
|
|
clip, = layer.get_clips()
|
|
self.assertEqual(clip.props.uri, refclip.props.uri)
|
|
self.assertEqual(timeline.props.duration, self.timeline.props.duration)
|
|
|
|
self.assertEqual(timeline.get_asset(), project)
|
|
self.assertEqual(len(project.list_assets(GES.Extractable)), 2)
|
|
|
|
def test_iter_timeline(self):
|
|
all_clips = set()
|
|
for l in range(5):
|
|
self.timeline.append_layer()
|
|
for _ in range(5):
|
|
all_clips.add(self.append_clip(l))
|
|
self.assertEqual(set(self.timeline.iter_clips()), all_clips)
|
|
|
|
|
|
def test_nested_serialization(self):
|
|
nested_timeline = common.create_project(with_group=True, saved=True)
|
|
nested_project = nested_timeline.get_asset()
|
|
layer = nested_timeline.append_layer()
|
|
|
|
asset = GES.UriClipAsset.request_sync(nested_project.props.id)
|
|
refclip = self.layer.add_asset(asset, 0, 0, 110 * Gst.SECOND, GES.TrackType.UNKNOWN)
|
|
nested_project.save(nested_timeline, nested_project.props.id, None, True)
|
|
|
|
project = self.timeline.get_asset()
|
|
project.add_asset(nested_project)
|
|
uri = "file://%s" % tempfile.NamedTemporaryFile(suffix=".xges").name
|
|
self.assertEqual(len(project.list_assets(GES.Extractable)), 2)
|
|
project.save(self.timeline, uri, None, overwrite=True)
|
|
self.assertEqual(len(project.list_assets(GES.Extractable)), 2)
|
|
|
|
mainloop = common.create_main_loop()
|
|
def loaded(unused_project, unused_timeline):
|
|
mainloop.quit()
|
|
project.connect("loaded", loaded)
|
|
|
|
# Extract again the timeline and compare with previous one.
|
|
timeline = project.extract()
|
|
mainloop.run()
|
|
layer, = timeline.get_layers()
|
|
clip, = layer.get_clips()
|
|
self.assertEqual(clip.props.uri, refclip.props.uri)
|
|
self.assertEqual(timeline.props.duration, self.timeline.props.duration)
|
|
|
|
self.assertEqual(timeline.get_asset(), project)
|
|
self.assertEqual(len(project.list_assets(GES.Extractable)), 2)
|
|
|
|
def test_timeline_duration(self):
|
|
self.append_clip()
|
|
self.append_clip()
|
|
clips = self.layer.get_clips()
|
|
|
|
self.assertEqual(self.timeline.props.duration, 20)
|
|
self.layer.remove_clip(clips[1])
|
|
self.assertEqual(self.timeline.props.duration, 10)
|
|
|
|
self.append_clip()
|
|
self.append_clip()
|
|
clips = self.layer.get_clips()
|
|
self.assertEqual(self.timeline.props.duration, 30)
|
|
|
|
group = GES.Container.group(clips[1:])
|
|
self.assertEqual(self.timeline.props.duration, 30)
|
|
|
|
group1 = GES.Container.group([])
|
|
group1.add(group)
|
|
self.assertEqual(self.timeline.props.duration, 30)
|
|
|
|
def test_spliting_with_auto_transition_on_the_left(self):
|
|
self.track_types = [GES.TrackType.AUDIO]
|
|
super().setUp()
|
|
|
|
self.timeline.props.auto_transition = True
|
|
clip1 = self.add_clip(0, 0, 100)
|
|
clip2 = self.add_clip(50, 0, 100)
|
|
self.assertTimelineTopology([
|
|
[ # Unique layer
|
|
(GES.TestClip, 0, 100),
|
|
(GES.TransitionClip, 50, 50),
|
|
(GES.TestClip, 50, 100)
|
|
]
|
|
])
|
|
|
|
clip1.split(25)
|
|
self.assertTimelineTopology([
|
|
[ # Unique layer
|
|
(GES.TestClip, 0, 25),
|
|
(GES.TestClip, 25, 75),
|
|
(GES.TransitionClip, 50, 50),
|
|
(GES.TestClip, 50, 100),
|
|
]
|
|
])
|
|
|
|
clip2.split(125)
|
|
self.assertTimelineTopology([
|
|
[ # Unique layer
|
|
(GES.TestClip, 0, 25),
|
|
(GES.TestClip, 25, 75),
|
|
(GES.TransitionClip, 50, 50),
|
|
(GES.TestClip, 50, 75),
|
|
(GES.TestClip, 125, 25),
|
|
]
|
|
])
|
|
|
|
|
|
class TestEditing(common.GESSimpleTimelineTest):
|
|
|
|
def test_transition_disappears_when_moving_to_another_layer(self):
|
|
self.timeline.props.auto_transition = True
|
|
unused_clip1 = self.add_clip(0, 0, 100)
|
|
clip2 = self.add_clip(50, 0, 100)
|
|
self.assertEqual(len(self.layer.get_clips()), 4)
|
|
|
|
layer2 = self.timeline.append_layer()
|
|
clip2.edit([], layer2.get_priority(), GES.EditMode.EDIT_NORMAL,
|
|
GES.Edge.EDGE_NONE, clip2.props.start)
|
|
self.assertEqual(len(self.layer.get_clips()), 1)
|
|
self.assertEqual(len(layer2.get_clips()), 1)
|
|
|
|
def activate_snapping(self):
|
|
self.timeline.set_snapping_distance(5)
|
|
self.snapped_at = []
|
|
|
|
def _snapped_cb(timeline, elem1, elem2, position):
|
|
self.snapped_at.append(position)
|
|
Gst.error('%s' % position)
|
|
|
|
def _snapped_end_cb(timeline, elem1, elem2, position):
|
|
if self.snapped_at: # Ignoring first snap end.
|
|
self.snapped_at.append(Gst.CLOCK_TIME_NONE)
|
|
Gst.error('%s' % position)
|
|
|
|
self.timeline.connect("snapping-started", _snapped_cb)
|
|
self.timeline.connect("snapping-ended", _snapped_end_cb)
|
|
|
|
def test_snap_start_snap_end(self):
|
|
clip = self.append_clip()
|
|
self.append_clip()
|
|
|
|
self.activate_snapping()
|
|
self.assertTimelineTopology([
|
|
[ # Unique layer
|
|
(GES.TestClip, 0, 10),
|
|
(GES.TestClip, 10, 10),
|
|
]
|
|
])
|
|
|
|
clip.props.start = 18
|
|
self.assertTimelineTopology([
|
|
[ # Unique layer
|
|
(GES.TestClip, 10, 10),
|
|
(GES.TestClip, 20, 10),
|
|
]
|
|
])
|
|
self.assertEqual(self.snapped_at, [20])
|
|
|
|
clip.props.start = 30
|
|
self.assertTimelineTopology([
|
|
[ # Unique layer
|
|
(GES.TestClip, 10, 10),
|
|
(GES.TestClip, 30, 10),
|
|
]
|
|
])
|
|
self.assertEqual(self.snapped_at, [20, Gst.CLOCK_TIME_NONE])
|
|
|
|
clip.props.start = 18
|
|
self.assertTimelineTopology([
|
|
[ # Unique layer
|
|
(GES.TestClip, 10, 10),
|
|
(GES.TestClip, 20, 10),
|
|
]
|
|
])
|
|
self.assertEqual(self.snapped_at, [20, Gst.CLOCK_TIME_NONE,
|
|
Gst.CLOCK_TIME_NONE, 20])
|
|
clip.props.start = 19
|
|
self.assertTimelineTopology([
|
|
[ # Unique layer
|
|
(GES.TestClip, 10, 10),
|
|
(GES.TestClip, 20, 10),
|
|
]
|
|
])
|
|
self.assertEqual(self.snapped_at, [20, Gst.CLOCK_TIME_NONE,
|
|
Gst.CLOCK_TIME_NONE, 20])
|
|
|
|
def test_rippling_snaps(self):
|
|
self.timeline.props.auto_transition = True
|
|
self.append_clip()
|
|
clip = self.append_clip()
|
|
|
|
self.activate_snapping()
|
|
self.assertTimelineTopology([
|
|
[ # Unique layer
|
|
(GES.TestClip, 0, 10),
|
|
(GES.TestClip, 10, 10),
|
|
]
|
|
])
|
|
|
|
clip.edit([], 0, GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_NONE, 15)
|
|
self.assertEqual(self.snapped_at, [10])
|
|
self.assertTimelineTopology([
|
|
[ # Unique layer
|
|
(GES.TestClip, 0, 10),
|
|
(GES.TestClip, 10, 10),
|
|
]
|
|
])
|
|
|
|
clip.edit([], 0, GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_NONE, 20)
|
|
self.assertEqual(self.snapped_at, [10, Gst.CLOCK_TIME_NONE])
|
|
self.assertTimelineTopology([
|
|
[ # Unique layer
|
|
(GES.TestClip, 0, 10),
|
|
(GES.TestClip, 20, 10),
|
|
]
|
|
])
|
|
|
|
def test_transition_moves_when_rippling_to_another_layer(self):
|
|
self.timeline.props.auto_transition = True
|
|
clip1 = self.add_clip(0, 0, 100)
|
|
clip2 = self.add_clip(50, 0, 100)
|
|
all_clips = self.layer.get_clips()
|
|
self.assertEqual(len(all_clips), 4)
|
|
|
|
layer2 = self.timeline.append_layer()
|
|
clip1.edit([], layer2.get_priority(), GES.EditMode.EDIT_RIPPLE,
|
|
GES.Edge.EDGE_NONE, clip1.props.start)
|
|
self.assertEqual(self.layer.get_clips(), [])
|
|
self.assertEqual(set(layer2.get_clips()), set(all_clips))
|
|
|
|
def test_transition_rippling_after_next_clip_stays(self):
|
|
self.timeline.props.auto_transition = True
|
|
clip1 = self.add_clip(0, 0, 100)
|
|
clip2 = self.add_clip(50, 0, 100)
|
|
all_clips = self.layer.get_clips()
|
|
self.assertEqual(len(all_clips), 4)
|
|
|
|
clip1.edit([], self.layer.get_priority(), GES.EditMode.EDIT_RIPPLE,
|
|
GES.Edge.EDGE_NONE, clip2.props.start + 1)
|
|
self.assertEqual(set(self.layer.get_clips()), set(all_clips))
|
|
|
|
def test_transition_rippling_over_does_not_create_another_transition(self):
|
|
self.timeline.props.auto_transition = True
|
|
|
|
clip1 = self.add_clip(0, 0, 17 * Gst.SECOND)
|
|
clip2 = clip1.split(7.0 * Gst.SECOND)
|
|
# Make a transition between the two clips
|
|
clip1.edit([], self.layer.get_priority(),
|
|
GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, 4.5 * Gst.SECOND)
|
|
|
|
# Rippl clip1 and check that transitions ar always the sames
|
|
all_clips = self.layer.get_clips()
|
|
self.assertEqual(len(all_clips), 4)
|
|
clip1.edit([], self.layer.get_priority(), GES.EditMode.EDIT_RIPPLE,
|
|
GES.Edge.EDGE_NONE, 41.5 * Gst.SECOND)
|
|
self.assertEqual(len(self.layer.get_clips()), 4)
|
|
clip1.edit([], self.layer.get_priority(),
|
|
GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_NONE, 35 * Gst.SECOND)
|
|
self.assertEqual(len(self.layer.get_clips()), 4)
|
|
|
|
def test_trim_transition(self):
|
|
self.track_types = [GES.TrackType.AUDIO]
|
|
super().setUp()
|
|
|
|
self.timeline.props.auto_transition = True
|
|
self.add_clip(0, 0, 10)
|
|
self.add_clip(5, 0, 10)
|
|
self.assertTimelineTopology([
|
|
[ # Unique layer
|
|
(GES.TestClip, 0, 10),
|
|
(GES.TransitionClip, 5, 5),
|
|
(GES.TestClip, 5, 10),
|
|
]
|
|
])
|
|
transition = self.layer.get_clips()[1]
|
|
self.assertTrue(transition.edit([], -1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, 7))
|
|
|
|
self.assertTimelineTopology([
|
|
[ # Unique layer
|
|
(GES.TestClip, 0, 10),
|
|
(GES.TransitionClip, 7, 3),
|
|
(GES.TestClip, 7, 8),
|
|
]
|
|
])
|
|
|
|
def test_trim_start(self):
|
|
clip = self.append_clip()
|
|
self.assertTrue(clip.edit([], -1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, 10))
|
|
self.assertTimelineTopology([
|
|
[ # Unique layer
|
|
(GES.TestClip, 10, 10),
|
|
]
|
|
])
|
|
|
|
self.assertFalse(clip.edit([], -1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_NONE, 0))
|
|
self.assertTimelineTopology([
|
|
[ # Unique layer
|
|
(GES.TestClip, 10, 10),
|
|
]
|
|
])
|
|
|
|
def test_ripple_end(self):
|
|
clip = self.append_clip()
|
|
clip.set_max_duration(20)
|
|
self.append_clip().set_max_duration(10)
|
|
self.append_clip().set_max_duration(10)
|
|
self.print_timeline()
|
|
self.assertTrue(clip.edit([], -1, GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_END, 20))
|
|
self.assertTimelineTopology([
|
|
[ # Unique layer
|
|
(GES.TestClip, 0, 20),
|
|
(GES.TestClip, 20, 10),
|
|
(GES.TestClip, 30, 10),
|
|
]
|
|
])
|
|
|
|
self.assertTrue(clip.edit([], -1, GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_END, 15))
|
|
self.assertTimelineTopology([
|
|
[ # Unique layer
|
|
(GES.TestClip, 0, 15),
|
|
(GES.TestClip, 15, 10),
|
|
(GES.TestClip, 25, 10),
|
|
]
|
|
])
|
|
|
|
def test_move_group_full_overlap(self):
|
|
self.track_types = [GES.TrackType.AUDIO]
|
|
super().setUp()
|
|
|
|
for _ in range(4):
|
|
self.append_clip()
|
|
clips = self.layer.get_clips()
|
|
|
|
self.assertTrue(clips[0].ripple(20))
|
|
self.assertTimelineTopology([
|
|
[
|
|
(GES.TestClip, 20, 10),
|
|
(GES.TestClip, 30, 10),
|
|
(GES.TestClip, 40, 10),
|
|
(GES.TestClip, 50, 10),
|
|
]
|
|
])
|
|
group = GES.Container.group(clips[1:])
|
|
self.print_timeline()
|
|
self.assertFalse(group.edit([], -1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, 0))
|
|
self.print_timeline()
|
|
self.assertTimelineTopology([
|
|
[
|
|
(GES.TestClip, 20, 10),
|
|
(GES.TestClip, 30, 10),
|
|
(GES.TestClip, 40, 10),
|
|
(GES.TestClip, 50, 10),
|
|
]
|
|
])
|
|
|
|
self.assertFalse(clips[1].edit([], -1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, 0))
|
|
self.print_timeline()
|
|
self.assertTimelineTopology([
|
|
[
|
|
(GES.TestClip, 20, 10),
|
|
(GES.TestClip, 30, 10),
|
|
(GES.TestClip, 40, 10),
|
|
(GES.TestClip, 50, 10),
|
|
]
|
|
])
|
|
|
|
def test_trim_inside_group(self):
|
|
self.track_types = [GES.TrackType.AUDIO]
|
|
super().setUp()
|
|
|
|
for _ in range(2):
|
|
self.append_clip()
|
|
clips = self.layer.get_clips()
|
|
group = GES.Container.group(clips)
|
|
self.assertTimelineTopology([
|
|
[ # Unique layer
|
|
(GES.TestClip, 0, 10),
|
|
(GES.TestClip, 10, 10),
|
|
]
|
|
])
|
|
self.assertEqual(group.props.start, 0)
|
|
self.assertEqual(group.props.duration, 20)
|
|
|
|
clips[0].trim(5)
|
|
self.assertTimelineTopology([
|
|
[ # Unique layer
|
|
(GES.TestClip, 5, 5),
|
|
(GES.TestClip, 10, 10),
|
|
]
|
|
])
|
|
self.assertEqual(group.props.start, 5)
|
|
self.assertEqual(group.props.duration, 15)
|
|
|
|
def test_trim_end_past_max_duration(self):
|
|
clip = self.append_clip()
|
|
max_duration = clip.props.duration
|
|
clip.set_max_duration(max_duration)
|
|
self.assertTrue(clip.edit([], -1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, 5))
|
|
self.assertTimelineTopology([
|
|
[ # Unique layer
|
|
(GES.TestClip, 5, 5),
|
|
]
|
|
])
|
|
|
|
self.assertFalse(clip.edit([], -1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_END, 15))
|
|
self.assertTimelineTopology([
|
|
[ # Unique layer
|
|
(GES.TestClip, 5, 5),
|
|
]
|
|
])
|
|
|
|
def test_illegal_effect_move(self):
|
|
c0 = self.append_clip()
|
|
self.append_clip()
|
|
self.assertTimelineTopology([
|
|
[
|
|
(GES.TestClip, 0, 10),
|
|
(GES.TestClip, 10, 10),
|
|
]
|
|
])
|
|
|
|
effect = GES.Effect.new("agingtv")
|
|
c0.add(effect)
|
|
self.assertTimelineTopology([
|
|
[
|
|
(GES.TestClip, 0, 10),
|
|
(GES.TestClip, 10, 10),
|
|
]
|
|
])
|
|
|
|
self.assertFalse(effect.set_start(10))
|
|
self.assertEqual(effect.props.start, 0)
|
|
self.assertTimelineTopology([
|
|
[
|
|
(GES.TestClip, 0, 10),
|
|
(GES.TestClip, 10, 10),
|
|
]
|
|
])
|
|
|
|
|
|
self.assertFalse(effect.set_duration(20))
|
|
self.assertEqual(effect.props.duration, 10)
|
|
self.assertTimelineTopology([
|
|
[
|
|
(GES.TestClip, 0, 10),
|
|
(GES.TestClip, 10, 10),
|
|
]
|
|
])
|
|
|
|
def test_moving_overlay_clip_in_group(self):
|
|
c0 = self.append_clip()
|
|
overlay = self.append_clip(asset_type=GES.TextOverlayClip)
|
|
group = GES.Group.new()
|
|
group.add(c0)
|
|
group.add(overlay)
|
|
|
|
self.assertTimelineTopology([
|
|
[
|
|
(GES.TestClip, 0, 10),
|
|
(GES.TextOverlayClip, 10, 10),
|
|
]
|
|
], groups=[(c0, overlay)])
|
|
|
|
self.assertTrue(overlay.set_start(20))
|
|
self.assertTimelineTopology([
|
|
[
|
|
(GES.TestClip, 10, 10),
|
|
(GES.TextOverlayClip, 20, 10),
|
|
]
|
|
], groups=[(c0, overlay)])
|
|
|
|
def test_moving_group_in_group(self):
|
|
c0 = self.append_clip()
|
|
overlay = self.append_clip(asset_type=GES.TextOverlayClip)
|
|
group0 = GES.Group.new()
|
|
group0.add(c0)
|
|
group0.add(overlay)
|
|
|
|
c1 = self.append_clip()
|
|
group1 = GES.Group.new()
|
|
group1.add(group0)
|
|
group1.add(c1)
|
|
|
|
self.assertTimelineTopology([
|
|
[
|
|
(GES.TestClip, 0, 10),
|
|
(GES.TextOverlayClip, 10, 10),
|
|
(GES.TestClip, 20, 10),
|
|
]
|
|
], groups=[(c1, group0), (c0, overlay)])
|
|
|
|
self.assertTrue(group0.set_start(10))
|
|
self.assertTimelineTopology([
|
|
[
|
|
(GES.TestClip, 10, 10),
|
|
(GES.TextOverlayClip, 20, 10),
|
|
(GES.TestClip, 30, 10),
|
|
]
|
|
], groups=[(c1, group0), (c0, overlay)])
|
|
self.check_element_values(group0, 10, 0, 20)
|
|
self.check_element_values(group1, 10, 0, 30)
|
|
|
|
def test_illegal_group_child_move(self):
|
|
clip0 = self.append_clip()
|
|
_clip1 = self.add_clip(20, 0, 10)
|
|
overlay = self.add_clip(20, 0, 10, asset_type=GES.TextOverlayClip)
|
|
|
|
group = GES.Group.new()
|
|
group.add(clip0)
|
|
group.add(overlay)
|
|
|
|
self.assertTimelineTopology([
|
|
[
|
|
(GES.TestClip, 0, 10),
|
|
(GES.TextOverlayClip, 20, 10),
|
|
(GES.TestClip, 20, 10),
|
|
]
|
|
], groups=[(clip0, overlay),])
|
|
|
|
# Can't move as clip0 and clip1 would fully overlap
|
|
self.assertFalse(overlay.set_start(40))
|
|
self.assertTimelineTopology([
|
|
[
|
|
(GES.TestClip, 0, 10),
|
|
(GES.TextOverlayClip, 20, 10),
|
|
(GES.TestClip, 20, 10),
|
|
]
|
|
], groups=[(clip0, overlay)])
|
|
|
|
def test_child_duration_change(self):
|
|
c0 = self.append_clip()
|
|
|
|
self.assertTimelineTopology([
|
|
[
|
|
(GES.TestClip, 0, 10),
|
|
]
|
|
])
|
|
self.assertTrue(c0.set_duration(40))
|
|
self.assertTimelineTopology([
|
|
[
|
|
(GES.TestClip, 0, 40),
|
|
]
|
|
])
|
|
|
|
c0.children[0].set_duration(10)
|
|
self.assertTimelineTopology([
|
|
[
|
|
(GES.TestClip, 0, 10),
|
|
]
|
|
])
|
|
|
|
self.assertTrue(c0.set_start(40))
|
|
self.assertTimelineTopology([
|
|
[
|
|
(GES.TestClip, 40, 10),
|
|
]
|
|
])
|
|
|
|
c0.children[0].set_start(10)
|
|
self.assertTimelineTopology([
|
|
[
|
|
(GES.TestClip, 10, 10),
|
|
]
|
|
])
|
|
|
|
|
|
class TestInvalidOverlaps(common.GESSimpleTimelineTest):
|
|
|
|
def test_adding_or_moving(self):
|
|
clip1 = self.add_clip(start=10, in_point=0, duration=3)
|
|
self.assertIsNotNone(clip1)
|
|
|
|
def check_add_move_clip(start, duration):
|
|
self.timeline.props.auto_transition = True
|
|
self.layer.props.auto_transition = True
|
|
clip2 = GES.TestClip()
|
|
clip2.props.start = start
|
|
clip2.props.duration = duration
|
|
self.assertFalse(self.layer.add_clip(clip2))
|
|
self.assertEqual(len(self.layer.get_clips()), 1)
|
|
|
|
# Add the clip at a different position.
|
|
clip2.props.start = 25
|
|
self.assertTrue(self.layer.add_clip(clip2))
|
|
self.assertEqual(clip2.props.start, 25)
|
|
|
|
# Try to move the second clip by editing it.
|
|
self.assertFalse(clip2.edit([], -1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, start))
|
|
self.assertEqual(clip2.props.start, 25)
|
|
|
|
# Try to put it in a group and move the group.
|
|
clip3 = GES.TestClip()
|
|
clip3.props.start = 20
|
|
clip3.props.duration = 1
|
|
self.assertTrue(self.layer.add_clip(clip3))
|
|
group = GES.Container.group([clip3, clip2])
|
|
self.assertTrue(group.props.start, 20)
|
|
self.assertFalse(group.edit([], -1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, start - 5))
|
|
self.assertEqual(group.props.start, 20)
|
|
self.assertEqual(clip3.props.start, 20)
|
|
self.assertEqual(clip2.props.start, 25)
|
|
|
|
for clip in group.ungroup(False):
|
|
self.assertTrue(self.layer.remove_clip(clip))
|
|
|
|
# clip1 contains...
|
|
check_add_move_clip(start=10, duration=1)
|
|
check_add_move_clip(start=11, duration=1)
|
|
check_add_move_clip(start=12, duration=1)
|
|
|
|
def test_splitting(self):
|
|
clip1 = self.add_clip(start=9, in_point=0, duration=3)
|
|
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))
|
|
|
|
def test_split_with_transition(self):
|
|
self.track_types = [GES.TrackType.AUDIO]
|
|
super().setUp()
|
|
self.timeline.set_auto_transition(True)
|
|
|
|
clip0 = self.add_clip(start=0, in_point=0, duration=50)
|
|
clip1 = self.add_clip(start=20, in_point=0, duration=50)
|
|
self.assertTimelineTopology([
|
|
[
|
|
(GES.TestClip, 0, 50),
|
|
(GES.TransitionClip, 20, 30),
|
|
(GES.TestClip, 20, 50)
|
|
]
|
|
])
|
|
|
|
# Split should file as the first part of the split
|
|
# would be fully overlapping clip0
|
|
self.assertIsNone(clip1.split(40))
|
|
self.assertTimelineTopology([
|
|
[
|
|
(GES.TestClip, 0, 50),
|
|
(GES.TransitionClip, 20, 30),
|
|
(GES.TestClip, 20, 50)
|
|
]
|
|
])
|
|
|
|
def test_changing_duration(self):
|
|
clip1 = self.add_clip(start=9, in_point=0, duration=2)
|
|
clip2 = self.add_clip(start=10, in_point=0, duration=2)
|
|
|
|
self.assertFalse(clip1.set_start(10))
|
|
self.assertFalse(clip1.edit([], -1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_END, clip2.props.start + clip2.props.duration))
|
|
self.assertFalse(clip1.ripple_end(clip2.props.start + clip2.props.duration))
|
|
self.assertFalse(clip1.roll_end(clip2.props.start + clip2.props.duration))
|
|
|
|
# clip2's end edge to the left, to decrease its duration.
|
|
self.assertFalse(clip2.edit([], -1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_END, clip1.props.start + clip1.props.duration))
|
|
self.assertFalse(clip2.ripple_end(clip1.props.start + clip1.props.duration))
|
|
self.assertFalse(clip2.roll_end(clip1.props.start + clip1.props.duration))
|
|
|
|
# clip2's start edge to the left, to increase its duration.
|
|
self.assertFalse(clip2.edit([], -1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, clip1.props.start))
|
|
self.assertFalse(clip2.trim(clip1.props.start))
|
|
|
|
# clip1's start edge to the right, to decrease its duration.
|
|
self.assertFalse(clip1.edit([], -1, GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, clip2.props.start))
|
|
self.assertFalse(clip1.trim(clip2.props.start))
|
|
|
|
def test_rippling_backward(self):
|
|
self.track_types = [GES.TrackType.AUDIO]
|
|
super().setUp()
|
|
self.maxDiff = None
|
|
for i in range(4):
|
|
self.append_clip()
|
|
self.assertTimelineTopology([
|
|
[ # Unique layer
|
|
(GES.TestClip, 0, 10),
|
|
(GES.TestClip, 10, 10),
|
|
(GES.TestClip, 20, 10),
|
|
(GES.TestClip, 30, 10),
|
|
]
|
|
])
|
|
|
|
clip = self.layer.get_clips()[2]
|
|
self.assertFalse(clip.edit([], -1, GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_NONE, clip.props.start - 20))
|
|
self.assertTimelineTopology([
|
|
[ # Unique layer
|
|
(GES.TestClip, 0, 10),
|
|
(GES.TestClip, 10, 10),
|
|
(GES.TestClip, 20, 10),
|
|
(GES.TestClip, 30, 10),
|
|
]
|
|
])
|
|
self.assertTrue(clip.edit([], -1, GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_NONE, clip.props.start + 10))
|
|
|
|
self.assertTimelineTopology([
|
|
[ # Unique layer
|
|
(GES.TestClip, 0, 10),
|
|
(GES.TestClip, 10, 10),
|
|
(GES.TestClip, 30, 10),
|
|
(GES.TestClip, 40, 10),
|
|
]
|
|
])
|
|
|
|
self.assertFalse(clip.edit([], -1, GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_NONE, clip.props.start -20))
|
|
self.assertTimelineTopology([
|
|
[ # Unique layer
|
|
(GES.TestClip, 0, 10),
|
|
(GES.TestClip, 10, 10),
|
|
(GES.TestClip, 30, 10),
|
|
(GES.TestClip, 40, 10),
|
|
]
|
|
])
|
|
|
|
def test_rolling(self):
|
|
clip1 = self.add_clip(start=9, in_point=0, duration=2)
|
|
clip2 = self.add_clip(start=10, in_point=0, duration=2)
|
|
clip3 = self.add_clip(start=11, in_point=0, duration=2)
|
|
|
|
# Rolling clip1's end -1 would lead to clip3 to overlap 100% with clip2.
|
|
self.assertTimelineTopology([
|
|
[ # Unique layer
|
|
(GES.TestClip, 9, 2),
|
|
(GES.TestClip, 10, 2),
|
|
(GES.TestClip, 11, 2)
|
|
]
|
|
])
|
|
self.assertFalse(clip1.edit([], -1, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_END, clip1.props.start + clip1.props.duration - 1))
|
|
self.assertFalse(clip1.roll_end(13))
|
|
self.assertTimelineTopology([
|
|
[ # Unique layer
|
|
(GES.TestClip, 9, 2),
|
|
(GES.TestClip, 10, 2),
|
|
(GES.TestClip, 11, 2)
|
|
]
|
|
])
|
|
|
|
# Rolling clip3's start +1 would lead to clip1 to overlap 100% with clip2.
|
|
self.assertFalse(clip3.edit([], -1, GES.EditMode.EDIT_ROLL, GES.Edge.EDGE_START, 12))
|
|
self.assertTimelineTopology([
|
|
[ # Unique layer
|
|
(GES.TestClip, 9, 2),
|
|
(GES.TestClip, 10, 2),
|
|
(GES.TestClip, 11, 2)
|
|
]
|
|
])
|
|
|
|
def test_layers(self):
|
|
self.track_types = [GES.TrackType.AUDIO]
|
|
super().setUp()
|
|
self.maxDiff = None
|
|
self.timeline.append_layer()
|
|
|
|
for i in range(2):
|
|
self.append_clip()
|
|
self.append_clip(1)
|
|
|
|
self.assertTimelineTopology([
|
|
[
|
|
(GES.TestClip, 0, 10),
|
|
(GES.TestClip, 10, 10),
|
|
],
|
|
[
|
|
(GES.TestClip, 0, 10),
|
|
(GES.TestClip, 10, 10),
|
|
]
|
|
])
|
|
|
|
clip = self.layer.get_clips()[0]
|
|
self.assertFalse(clip.edit([], 1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, 0))
|
|
self.assertTimelineTopology([
|
|
[
|
|
(GES.TestClip, 0, 10),
|
|
(GES.TestClip, 10, 10),
|
|
],
|
|
[
|
|
(GES.TestClip, 0, 10),
|
|
(GES.TestClip, 10, 10),
|
|
]
|
|
])
|
|
|
|
def test_rippling(self):
|
|
self.timeline.remove_track(self.timeline.get_tracks()[0])
|
|
clip1 = self.add_clip(start=9, in_point=0, duration=2)
|
|
clip2 = self.add_clip(start=10, in_point=0, duration=2)
|
|
clip3 = self.add_clip(start=11, in_point=0, duration=2)
|
|
|
|
# Rippling clip2's start -2 would bring clip3 exactly on top of clip1.
|
|
self.assertFalse(clip2.edit([], -1, GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_NONE, 8))
|
|
self.assertFalse(clip2.ripple(8))
|
|
|
|
# Rippling clip1's end -1 would bring clip3 exactly on top of clip2.
|
|
self.assertFalse(clip1.edit([], -1, GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_END, 8))
|
|
self.assertFalse(clip1.ripple_end(8))
|
|
|
|
def test_move_group_to_layer(self):
|
|
self.track_types = [GES.TrackType.AUDIO]
|
|
super().setUp()
|
|
self.append_clip()
|
|
self.append_clip()
|
|
self.append_clip()
|
|
|
|
clips = self.layer.get_clips()
|
|
|
|
clips[1].props.start += 2
|
|
group = GES.Container.group(clips[1:])
|
|
self.assertTrue(clips[1].edit([], 1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE,
|
|
group.props.start))
|
|
|
|
self.assertTimelineTopology([
|
|
[
|
|
(GES.TestClip, 0, 10),
|
|
],
|
|
[
|
|
(GES.TestClip, 12, 10),
|
|
(GES.TestClip, 20, 10),
|
|
]
|
|
])
|
|
|
|
clips[0].props.start = 15
|
|
self.assertTimelineTopology([
|
|
[
|
|
(GES.TestClip, 15, 10),
|
|
],
|
|
[
|
|
(GES.TestClip, 12, 10),
|
|
(GES.TestClip, 20, 10),
|
|
]
|
|
])
|
|
|
|
self.assertFalse(clips[1].edit([], 0, GES.EditMode.EDIT_NORMAL,
|
|
GES.Edge.EDGE_NONE, group.props.start))
|
|
self.assertTimelineTopology([
|
|
[
|
|
(GES.TestClip, 15, 10),
|
|
],
|
|
[
|
|
(GES.TestClip, 12, 10),
|
|
(GES.TestClip, 20, 10),
|
|
]
|
|
])
|
|
|
|
def test_copy_paste_overlapping(self):
|
|
self.track_types = [GES.TrackType.AUDIO]
|
|
super().setUp()
|
|
clip = self.append_clip()
|
|
|
|
copy = clip.copy(True)
|
|
self.assertIsNone(copy.paste(copy.props.start))
|
|
self.assertTimelineTopology([
|
|
[
|
|
(GES.TestClip, 0, 10),
|
|
|
|
]
|
|
])
|
|
copy = clip.copy(True)
|
|
pasted = copy.paste(copy.props.start + 1)
|
|
self.assertTimelineTopology([
|
|
[
|
|
(GES.TestClip, 0, 10),
|
|
(GES.TestClip, 1, 10),
|
|
]
|
|
])
|
|
|
|
pasted.move_to_layer(self.timeline.append_layer())
|
|
self.assertTimelineTopology([
|
|
[
|
|
(GES.TestClip, 0, 10),
|
|
],
|
|
[
|
|
(GES.TestClip, 1, 10),
|
|
]
|
|
])
|
|
|
|
copy = pasted.copy(True)
|
|
self.assertIsNotNone(copy.paste(pasted.props.start - 1))
|
|
self.assertTimelineTopology([
|
|
[
|
|
(GES.TestClip, 0, 10),
|
|
],
|
|
[
|
|
(GES.TestClip, 0, 10),
|
|
(GES.TestClip, 1, 10),
|
|
],
|
|
])
|
|
|
|
group = GES.Group.new()
|
|
group.add(clip)
|
|
|
|
copied_group = group.copy(True)
|
|
self.assertFalse(copied_group.paste(group.props.start))
|
|
self.assertTimelineTopology([
|
|
[
|
|
(GES.TestClip, 0, 10),
|
|
],
|
|
[
|
|
(GES.TestClip, 0, 10),
|
|
(GES.TestClip, 1, 10),
|
|
],
|
|
])
|
|
|
|
def test_move_group_with_overlaping_clips(self):
|
|
self.track_types = [GES.TrackType.AUDIO]
|
|
super().setUp()
|
|
self.append_clip()
|
|
self.append_clip()
|
|
self.append_clip()
|
|
|
|
self.timeline.props.auto_transition = True
|
|
clips = self.layer.get_clips()
|
|
|
|
clips[1].props.start += 5
|
|
group = GES.Container.group(clips[1:])
|
|
self.assertTimelineTopology([
|
|
[
|
|
(GES.TestClip, 0, 10),
|
|
(GES.TestClip, 15, 10),
|
|
(GES.TransitionClip, 20, 5),
|
|
(GES.TestClip, 20, 10),
|
|
]
|
|
])
|
|
|
|
clips[0].props.start = 30
|
|
self.assertTimelineTopology([
|
|
[
|
|
(GES.TestClip, 15, 10),
|
|
(GES.TransitionClip, 20, 5),
|
|
(GES.TestClip, 20, 10),
|
|
(GES.TestClip, 30, 10),
|
|
]
|
|
])
|
|
|
|
# the 3 clips would overlap
|
|
self.assertFalse(clips[1].edit([], 0, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, 25))
|
|
self.assertTimelineTopology([
|
|
[
|
|
(GES.TestClip, 15, 10),
|
|
(GES.TransitionClip, 20, 5),
|
|
(GES.TestClip, 20, 10),
|
|
(GES.TestClip, 30, 10),
|
|
]
|
|
])
|
|
|
|
|
|
class TestSnapping(common.GESSimpleTimelineTest):
|
|
|
|
def test_snapping(self):
|
|
self.timeline.props.auto_transition = True
|
|
self.timeline.set_snapping_distance(1)
|
|
clip1 = self.add_clip(0, 0, 100)
|
|
|
|
# Split clip1.
|
|
split_position = 50
|
|
clip2 = clip1.split(split_position)
|
|
self.assertEqual(len(self.layer.get_clips()), 2)
|
|
self.assertEqual(clip1.props.duration, split_position)
|
|
self.assertEqual(clip2.props.start, split_position)
|
|
|
|
# Make sure snapping prevents clip2 to be moved to the left.
|
|
clip2.edit([], self.layer.get_priority(), GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE,
|
|
clip2.props.start - 1)
|
|
self.assertEqual(clip2.props.start, split_position)
|
|
|
|
def test_trim_snapps_inside_group(self):
|
|
self.track_types = [GES.TrackType.AUDIO]
|
|
super().setUp()
|
|
|
|
self.timeline.props.auto_transition = True
|
|
self.timeline.set_snapping_distance(5)
|
|
|
|
snaps = []
|
|
def snapping_started_cb(timeline, element1, element2, dist, self):
|
|
snaps.append(set([element1, element2]))
|
|
|
|
self.timeline.connect('snapping-started', snapping_started_cb, self)
|
|
clip = self.append_clip()
|
|
clip1 = self.append_clip()
|
|
|
|
self.assertTimelineTopology([
|
|
[ # Unique layer
|
|
(GES.TestClip, 0, 10),
|
|
(GES.TestClip, 10, 10),
|
|
]
|
|
])
|
|
|
|
clip1.edit([], self.layer.get_priority(), GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, 15)
|
|
self.assertTimelineTopology([
|
|
[ # Unique layer
|
|
(GES.TestClip, 0, 10),
|
|
(GES.TestClip, 10, 10),
|
|
]
|
|
])
|
|
self.assertEqual(snaps[0], set([clip.get_children(False)[0], clip1.get_children(False)[0]]))
|
|
|
|
clip1.edit([], self.layer.get_priority(), GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, 16)
|
|
self.assertTimelineTopology([
|
|
[ # Unique layer
|
|
(GES.TestClip, 0, 10),
|
|
(GES.TestClip, 16, 4),
|
|
]
|
|
])
|
|
|
|
def test_trim_no_snapping_on_same_clip(self):
|
|
self.timeline.props.auto_transition = True
|
|
self.timeline.set_snapping_distance(1)
|
|
|
|
not_called = []
|
|
def snapping_started_cb(timeline, element1, element2, dist, self):
|
|
not_called.append("No snapping should happen")
|
|
|
|
self.timeline.connect('snapping-started', snapping_started_cb, self)
|
|
clip = self.append_clip()
|
|
clip.edit([], self.layer.get_priority(), GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, 5)
|
|
self.assertEqual(not_called, [])
|
|
self.assertTimelineTopology([
|
|
[ # Unique layer
|
|
(GES.TestClip, 5, 5),
|
|
]
|
|
])
|
|
|
|
clip.edit([], self.layer.get_priority(), GES.EditMode.EDIT_TRIM, GES.Edge.EDGE_START, 4)
|
|
self.assertEqual(not_called, [])
|
|
self.assertTimelineTopology([
|
|
[ # Unique layer
|
|
(GES.TestClip, 4, 6),
|
|
]
|
|
])
|
|
|
|
def test_no_snapping_on_split(self):
|
|
self.timeline.props.auto_transition = True
|
|
self.timeline.set_snapping_distance(1)
|
|
|
|
not_called = []
|
|
def snapping_started_cb(timeline, element1, element2, dist, self):
|
|
not_called.append("No snapping should happen")
|
|
|
|
self.timeline.connect('snapping-started', snapping_started_cb, self)
|
|
clip1 = self.add_clip(0, 0, 100)
|
|
|
|
# Split clip1.
|
|
split_position = 50
|
|
clip2 = clip1.split(split_position)
|
|
self.assertEqual(not_called, [])
|
|
self.assertEqual(len(self.layer.get_clips()), 2)
|
|
self.assertEqual(clip1.props.duration, split_position)
|
|
self.assertEqual(clip2.props.start, split_position)
|
|
|
|
|
|
class TestTransitions(common.GESSimpleTimelineTest):
|
|
|
|
def test_emission_order_for_transition_clip_added_signal(self):
|
|
self.timeline.props.auto_transition = True
|
|
unused_clip1 = self.add_clip(0, 0, 100)
|
|
clip2 = self.add_clip(100, 0, 100)
|
|
|
|
# Connect to signals to track in which order they are emitted.
|
|
signals = []
|
|
|
|
def clip_added_cb(layer, clip):
|
|
self.assertIsInstance(clip, GES.TransitionClip)
|
|
signals.append("clip-added")
|
|
self.layer.connect("clip-added", clip_added_cb)
|
|
|
|
def property_changed_cb(clip, pspec):
|
|
self.assertEqual(clip, clip2)
|
|
self.assertEqual(pspec.name, "start")
|
|
signals.append("notify::start")
|
|
clip2.connect("notify::start", property_changed_cb)
|
|
|
|
# Move clip2 to create a transition with clip1.
|
|
clip2.edit([], self.layer.get_priority(),
|
|
GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, 50)
|
|
# The clip-added signal is emitted twice, once for the video
|
|
# transition and once for the audio transition.
|
|
self.assertEqual(
|
|
signals, ["notify::start", "clip-added", "clip-added"])
|
|
|
|
def create_xges(self):
|
|
uri = common.get_asset_uri("png.png")
|
|
return """<ges version='0.4'>
|
|
<project properties='properties;' metadatas='metadatas, author=(string)"", render-scale=(double)100, format-version=(string)0.4;'>
|
|
<ressources>
|
|
<asset id='GESTitleClip' extractable-type-name='GESTitleClip' properties='properties;' metadatas='metadatas;' />
|
|
<asset id='bar-wipe-lr' extractable-type-name='GESTransitionClip' properties='properties;' metadatas='metadatas, description=(string)GES_VIDEO_STANDARD_TRANSITION_TYPE_BAR_WIPE_LR;' />
|
|
<asset id='%(uri)s' extractable-type-name='GESUriClip' properties='properties, supported-formats=(int)4, duration=(guint64)18446744073709551615;' metadatas='metadatas, video-codec=(string)PNG, file-size=(guint64)73294;' />
|
|
<asset id='crossfade' extractable-type-name='GESTransitionClip' properties='properties;' metadatas='metadatas, description=(string)GES_VIDEO_STANDARD_TRANSITION_TYPE_CROSSFADE;' />
|
|
</ressources>
|
|
<timeline properties='properties, auto-transition=(boolean)true, snapping-distance=(guint64)31710871;' metadatas='metadatas, duration=(guint64)13929667032;'>
|
|
<track caps='video/x-raw(ANY)' track-type='4' track-id='0' properties='properties, async-handling=(boolean)false, message-forward=(boolean)true, caps=(string)"video/x-raw\(ANY\)", restriction-caps=(string)"video/x-raw\,\ width\=\(int\)1920\,\ height\=\(int\)1080\,\ framerate\=\(fraction\)30/1", mixing=(boolean)true;' metadatas='metadatas;'/>
|
|
<track caps='audio/x-raw(ANY)' track-type='2' track-id='1' properties='properties, async-handling=(boolean)false, message-forward=(boolean)true, caps=(string)"audio/x-raw\(ANY\)", restriction-caps=(string)"audio/x-raw\,\ format\=\(string\)S32LE\,\ channels\=\(int\)2\,\ rate\=\(int\)44100\,\ layout\=\(string\)interleaved", mixing=(boolean)true;' metadatas='metadatas;'/>
|
|
<layer priority='0' properties='properties, auto-transition=(boolean)true;' metadatas='metadatas, volume=(float)1;'>
|
|
<clip id='0' asset-id='%(uri)s' type-name='GESUriClip' layer-priority='0' track-types='6' start='0' duration='4558919302' inpoint='0' rate='0' properties='properties, name=(string)uriclip25263, mute=(boolean)false, is-image=(boolean)false;' />
|
|
<clip id='1' asset-id='bar-wipe-lr' type-name='GESTransitionClip' layer-priority='0' track-types='4' start='3225722559' duration='1333196743' inpoint='0' rate='0' properties='properties, name=(string)transitionclip84;' children-properties='properties, GESVideoTransition::border=(uint)0, GESVideoTransition::invert=(boolean)false;'/>
|
|
<clip id='2' asset-id='%(uri)s' type-name='GESUriClip' layer-priority='0' track-types='6' start='3225722559' duration='3479110239' inpoint='4558919302' rate='0' properties='properties, name=(string)uriclip25265, mute=(boolean)false, is-image=(boolean)false;' />
|
|
</layer>
|
|
<groups>
|
|
</groups>
|
|
</timeline>
|
|
</project>
|
|
</ges>""" % {"uri": uri}
|
|
|
|
def test_auto_transition(self):
|
|
xges = self.create_xges()
|
|
with common.created_project_file(xges) as proj_uri:
|
|
project = GES.Project.new(proj_uri)
|
|
timeline = project.extract()
|
|
|
|
mainloop = common.create_main_loop()
|
|
def loaded_cb(unused_project, unused_timeline):
|
|
mainloop.quit()
|
|
project.connect("loaded", loaded_cb)
|
|
|
|
mainloop.run()
|
|
|
|
layers = timeline.get_layers()
|
|
self.assertEqual(len(layers), 1)
|
|
|
|
self.assertTrue(layers[0].props.auto_transition)
|
|
|
|
def test_transition_type(self):
|
|
xges = self.create_xges()
|
|
with common.created_project_file(xges) as proj_uri:
|
|
project = GES.Project.new(proj_uri)
|
|
timeline = project.extract()
|
|
|
|
mainloop = common.create_main_loop()
|
|
def loaded_cb(unused_project, unused_timeline):
|
|
mainloop.quit()
|
|
project.connect("loaded", loaded_cb)
|
|
mainloop.run()
|
|
|
|
layers = timeline.get_layers()
|
|
self.assertEqual(len(layers), 1)
|
|
|
|
clips = layers[0].get_clips()
|
|
clip1 = clips[0]
|
|
clip2 = clips[-1]
|
|
# There should be a transition because clip1 intersects clip2
|
|
self.assertLess(clip1.props.start, clip2.props.start)
|
|
self.assertLess(clip2.props.start, clip1.props.start + clip1.props.duration)
|
|
self.assertLess(clip1.props.start + clip1.props.duration, clip2.props.start + clip2.props.duration)
|
|
self.assertEqual(len(clips), 3)
|
|
|
|
|
|
class TestPriorities(common.GESSimpleTimelineTest):
|
|
|
|
def test_clips_priorities(self):
|
|
clip = self.add_clip(0, 0, 100)
|
|
clip1 = self.add_clip(100, 0, 100)
|
|
self.timeline.commit()
|
|
|
|
self.assertLess(clip.props.priority, clip1.props.priority)
|
|
|
|
clip.props.start = 101
|
|
self.timeline.commit()
|
|
self.assertGreater(clip.props.priority, clip1.props.priority)
|
|
|
|
|
|
class TestTimelineElement(common.GESSimpleTimelineTest):
|
|
|
|
def test_set_child_property(self):
|
|
clip = self.add_clip(0, 0, 100)
|
|
source = clip.find_track_element(None, GES.VideoSource)
|
|
self.assertTrue(source.set_child_property("height", 5))
|
|
self.assertEqual(clip.get_child_property("height"), (True, 5))
|