def setUp(self): TestCase.setUp(self) self.factory = StubFactory() self.stream = VideoStream(gst.Caps('video/x-raw-rgb')) self.factory.addOutputStream(self.stream) self.track1 = Track(self.stream) self.track2 = Track(self.stream)
def setUp(self): TestCase.setUp(self) stream = AudioStream(gst.Caps("audio/x-raw-int")) self.factory = StubFactory() gst.debug("%r" % self.factory.duration) self.factory.addOutputStream(stream) self.track_object = SourceTrackObject(self.factory, stream) self.monitor = TrackSignalMonitor(self.track_object)
class TestTrack(TestCase): def setUp(self): TestCase.setUp(self) self.factory = StubFactory() self.stream = VideoStream(gst.Caps('video/x-raw-rgb')) self.factory.addOutputStream(self.stream) self.track1 = Track(self.stream) self.track2 = Track(self.stream) def tearDown(self): self.factory = None self.stream = None self.track1 = None self.track2 = None TestCase.tearDown(self) def testAddRemoveObjects(self): factory = self.factory stream = self.stream track1 = self.track1 track2 = self.track2 # add an object obj1 = SourceTrackObject(factory, stream) self.failUnlessEqual(obj1.track, None) track1.addTrackObject(obj1) self.failIfEqual(obj1.track, None) # can't add twice self.failUnlessRaises(TrackError, track1.addTrackObject, obj1) # can't add to two different tracks self.failUnlessRaises(TrackError, track2.addTrackObject, obj1) # add a second object obj2 = SourceTrackObject(factory, stream) self.failUnlessEqual(obj2.track, None) track1.addTrackObject(obj2) self.failIfEqual(obj2.track, None) # remove track1.removeTrackObject(obj1) self.failUnlessEqual(obj1.track, None) # can't remove twice self.failUnlessRaises(TrackError, track1.removeTrackObject, obj1) track1.removeTrackObject(obj2) self.failUnlessEqual(obj2.track, None) def testRemoveAllTrackObjects(self): track = self.track1 factory = self.factory # check that can be called on an empty track track.removeAllTrackObjects() objs = [] for i in xrange(10): obj = SourceTrackObject(factory, self.stream) objs.append(obj) track.addTrackObject(obj) for obj in objs: self.failIfEqual(obj.track, None) track.removeAllTrackObjects() for obj in objs: self.failUnlessEqual(obj.track, None) def testMaxPriority(self): track = self.track1 factory = self.factory obj1 = SourceTrackObject(factory, self.stream) obj1.priority = 10 self.failUnlessEqual(track.max_priority, 0) track.addTrackObject(obj1) self.failUnlessEqual(track.max_priority, 10) obj2 = SourceTrackObject(factory, self.stream) obj2.priority = 5 track.addTrackObject(obj2) self.failUnlessEqual(track.max_priority, 10) obj3 = SourceTrackObject(factory, self.stream) obj3.priority = 14 track.addTrackObject(obj3) self.failUnlessEqual(track.max_priority, 14) obj3.priority = 9 self.failUnlessEqual(track.max_priority, 10) obj2.priority = 11 self.failUnlessEqual(track.max_priority, 11) track.removeTrackObject(obj1) self.failUnlessEqual(track.max_priority, 11) track.removeTrackObject(obj2) self.failUnlessEqual(track.max_priority, 9) track.removeTrackObject(obj3) self.failUnlessEqual(track.max_priority, 0) def testGetPreviousTrackObject(self): factory = self.factory stream = self.stream track1 = self.track1 obj1 = SourceTrackObject(factory, stream) track1.addTrackObject(obj1) obj2 = SourceTrackObject(factory, stream) track1.addTrackObject(obj2) obj3 = SourceTrackObject(factory, stream) track1.addTrackObject(obj3) obj4 = SourceTrackObject(factory, stream) track1.addTrackObject(obj4) obj1.start = 1 * gst.SECOND obj1.duration = 5 * gst.SECOND obj1.priority = 1 obj2.start = 8 * gst.SECOND obj2.duration = 5 * gst.SECOND obj2.priority = 1 obj3.start = 6 * gst.SECOND obj3.duration = 5 * gst.SECOND obj3.priority = 2 obj4.start = 7 * gst.SECOND obj4.duration = 5 * gst.SECOND obj4.priority = 3 # no previous object self.failUnlessRaises(TrackError, track1.getPreviousTrackObject, obj4) # same priority prev = track1.getPreviousTrackObject(obj2) self.failUnlessEqual(prev, obj1) # given priority prev = track1.getPreviousTrackObject(obj2, priority=2) self.failUnlessEqual(prev, obj3) # any priority prev = track1.getPreviousTrackObject(obj2, priority=None) self.failUnlessEqual(prev, obj4) obj3.start = 8 * gst.SECOND # same start prev = track1.getPreviousTrackObject(obj2, priority=None) self.failUnlessEqual(prev, obj3) def testGetNextTrackObject(self): factory = self.factory stream = self.stream track1 = self.track1 obj1 = SourceTrackObject(factory, stream) track1.addTrackObject(obj1) obj2 = SourceTrackObject(factory, stream) track1.addTrackObject(obj2) obj3 = SourceTrackObject(factory, stream) track1.addTrackObject(obj3) obj4 = SourceTrackObject(factory, stream) track1.addTrackObject(obj4) obj1.start = 1 * gst.SECOND obj1.duration = 5 * gst.SECOND obj1.priority = 1 obj2.start = 8 * gst.SECOND obj2.duration = 5 * gst.SECOND obj2.priority = 1 obj3.start = 6 * gst.SECOND obj3.duration = 5 * gst.SECOND obj3.priority = 2 obj4.start = 7 * gst.SECOND obj4.duration = 5 * gst.SECOND obj4.priority = 3 # no next object self.failUnlessRaises(TrackError, track1.getNextTrackObject, obj2) # same priority prev = track1.getNextTrackObject(obj1) self.failUnlessEqual(prev, obj2) # given priority prev = track1.getNextTrackObject(obj1, priority=2) self.failUnlessEqual(prev, obj3) # any priority prev = track1.getNextTrackObject(obj3, priority=None) self.failUnlessEqual(prev, obj4) def testCopyMakeBinNotCalled(self): factory = self.factory stream = self.stream obj1 = SourceTrackObject(factory, stream) # this used to raise an exception obj2 = obj1.copy() self.failUnlessEqual(obj1.start, obj2.start)
def setUp(self): self.factory = StubFactory() self.stream = AudioStream(gst.Caps('audio/x-raw-int')) self.factory.addOutputStream(self.stream) self.track1 = Track(self.stream) self.timeline = Timeline()
class TestTrackObject(TestCase): def setUp(self): TestCase.setUp(self) stream = AudioStream(gst.Caps("audio/x-raw-int")) self.factory = StubFactory() gst.debug("%r" % self.factory.duration) self.factory.addOutputStream(stream) self.track_object = SourceTrackObject(self.factory, stream) self.monitor = TrackSignalMonitor(self.track_object) def tearDown(self): self.monitor = None self.track_object.release() self.track_oject = None self.factory = None TestCase.tearDown(self) def testDefaultProperties(self): obj = self.track_object self.failUnlessEqual(obj.start, 0) self.failUnlessEqual(obj.duration, self.factory.duration) self.failUnlessEqual(obj.in_point, 0) self.failUnlessEqual(obj.out_point, self.factory.duration) self.failUnlessEqual(obj.media_duration, self.factory.duration) self.failUnlessEqual(obj.rate, 1) self.failUnlessEqual(obj.priority, 0) gnl_object = obj.gnl_object self.failUnlessEqual(gnl_object.props.start, 0) self.failUnlessEqual(gnl_object.props.duration, self.factory.duration) self.failUnlessEqual(gnl_object.props.media_start, 0) self.failUnlessEqual(gnl_object.props.media_stop, self.factory.duration) self.failUnlessEqual(gnl_object.props.media_duration, self.factory.duration) self.failUnlessEqual(gnl_object.props.rate, 1) self.failUnlessEqual(obj.priority, 0) def testChangePropertiesFromTrackObject(self): obj = self.track_object gnl_object = obj.gnl_object start = 1 * gst.SECOND obj.start = start self.failUnlessEqual(obj.start, start) self.failUnlessEqual(gnl_object.props.start, start) self.failUnlessEqual(self.monitor.start_changed_count, 1) duration = 10 * gst.SECOND obj.duration = duration self.failUnlessEqual(obj.duration, duration) self.failUnlessEqual(gnl_object.props.duration, duration) self.failUnlessEqual(self.monitor.duration_changed_count, 1) in_point = 5 * gst.SECOND obj.in_point = in_point self.failUnlessEqual(obj.in_point, in_point) self.failUnlessEqual(gnl_object.props.media_start, in_point) self.failUnlessEqual(self.monitor.in_point_changed_count, 1) media_duration = 5 * gst.SECOND obj.media_duration = media_duration self.failUnlessEqual(obj.media_duration, media_duration) self.failUnlessEqual(gnl_object.props.media_duration, media_duration) self.failUnlessEqual(obj.out_point, in_point + media_duration) self.failUnlessEqual(gnl_object.props.media_stop, in_point + media_duration) self.failUnlessEqual(self.monitor.media_duration_changed_count, 1) self.failUnlessEqual(self.monitor.out_point_changed_count, 1) # test video stream$ obj.stream_type = VideoStream priority = 100 gnl_priority = 3 * 100 + 3 + obj._stagger obj.priority = priority self.failUnlessEqual(obj.priority, priority) self.failUnlessEqual(gnl_object.props.priority, gnl_priority) self.failUnlessEqual(self.monitor.priority_changed_count, 1) # test audio stream obj.stream_type = AudioStream priority = 55 gnl_priority = 4 * 55 + 3 + 2 * obj._stagger obj.priority = priority self.failUnlessEqual(obj.priority, priority) self.failUnlessEqual(gnl_object.props.priority, gnl_priority) self.failUnlessEqual(self.monitor.priority_changed_count, 2) def testChangePropertiesFromGnlObject(self): obj = self.track_object gnl_object = obj.gnl_object start = 1 * gst.SECOND gnl_object.props.start = start self.failUnlessEqual(obj.start, start) self.failUnlessEqual(self.monitor.start_changed_count, 1) duration = 10 * gst.SECOND gnl_object.props.duration = duration self.failUnlessEqual(obj.duration, duration) self.failUnlessEqual(self.monitor.duration_changed_count, 1) in_point = 5 * gst.SECOND gnl_object.props.media_start = in_point self.failUnlessEqual(obj.in_point, in_point) self.failUnlessEqual(self.monitor.in_point_changed_count, 1) media_duration = 5 * gst.SECOND gnl_object.props.media_duration = media_duration self.failUnlessEqual(obj.media_duration, media_duration) self.failUnlessEqual(self.monitor.media_duration_changed_count, 1) self.failUnlessEqual(obj.out_point, in_point + media_duration) self.failUnlessEqual(self.monitor.media_duration_changed_count, 1) self.failUnlessEqual(self.monitor.out_point_changed_count, 1) # video stream obj.stream_type = VideoStream gnl_priority = 100 priority = (100 - 2 - obj._stagger) // 3 gnl_object.props.priority = gnl_priority self.failUnlessEqual(obj.priority, priority) self.failUnlessEqual(gnl_object.props.priority, gnl_priority) self.failUnlessEqual(self.monitor.priority_changed_count, 1) # video stream obj.stream_type = AudioStream gnl_priority = 55 priority = (55 - 2 - obj._stagger) // 4 gnl_object.props.priority = gnl_priority self.failUnlessEqual(obj.priority, priority) self.failUnlessEqual(gnl_object.props.priority, gnl_priority) self.failUnlessEqual(self.monitor.priority_changed_count, 2) def testTrimStart(self): obj = self.track_object # start at 2 seconds with length 10 seconds obj.start = 2 * gst.SECOND obj.in_point = 1 * gst.SECOND obj.duration = 10 * gst.SECOND self.failUnlessEqual(self.monitor.duration_changed_count, 1) # trim at lower edge monitor = TrackSignalMonitor(obj) time = 2 * gst.SECOND obj.trimStart(time) self.failUnlessEqual(obj.start, time) self.failUnlessEqual(obj.in_point, 1 * gst.SECOND) self.failUnlessEqual(obj.duration, 10 * gst.SECOND) self.failUnlessEqual(obj.rate, 1) # we didn't change the start/in-point/duration (it was the same as before) self.failUnlessEqual(monitor.start_changed_count, 0) self.failUnlessEqual(monitor.in_point_changed_count, 0) self.failUnlessEqual(monitor.duration_changed_count, 0) # trim at upper edge monitor = TrackSignalMonitor(obj) time = 12 * gst.SECOND obj.trimStart(time) self.failUnlessEqual(obj.start, time) self.failUnlessEqual(obj.in_point, 11 * gst.SECOND) self.failUnlessEqual(obj.duration, 0) self.failUnlessEqual(obj.rate, 1) self.failUnlessEqual(monitor.start_changed_count, 1) self.failUnlessEqual(monitor.in_point_changed_count, 1) self.failUnlessEqual(monitor.duration_changed_count, 1) # trim before lower edge, should clamp monitor = TrackSignalMonitor(obj) time = 0 * gst.SECOND obj.trimStart(time) self.failUnlessEqual(obj.start, 1 * gst.SECOND) self.failUnlessEqual(obj.in_point, 0) self.failUnlessEqual(obj.duration, 11 * gst.SECOND) self.failUnlessEqual(obj.rate, 1) self.failUnlessEqual(monitor.start_changed_count, 1) self.failUnlessEqual(monitor.in_point_changed_count, 1) self.failUnlessEqual(monitor.duration_changed_count, 1) # trimp past upper edge, should clamp monitor = TrackSignalMonitor(obj) time = 13 * gst.SECOND obj.trimStart(time) self.failUnlessEqual(obj.start, 12 * gst.SECOND) self.failUnlessEqual(obj.in_point, 11 * gst.SECOND) self.failUnlessEqual(obj.duration, 0) self.failUnlessEqual(obj.rate, 1) self.failUnlessEqual(monitor.start_changed_count, 1) self.failUnlessEqual(monitor.in_point_changed_count, 1) self.failUnlessEqual(monitor.duration_changed_count, 1) # trim somewhere in the middle monitor = TrackSignalMonitor(obj) time = 7 * gst.SECOND obj.trimStart(time) self.failUnlessEqual(obj.start, time) self.failUnlessEqual(obj.in_point, 6 * gst.SECOND) self.failUnlessEqual(obj.duration, 5 * gst.SECOND) self.failUnlessEqual(obj.rate, 1) self.failUnlessEqual(monitor.start_changed_count, 1) self.failUnlessEqual(monitor.in_point_changed_count, 1) self.failUnlessEqual(monitor.duration_changed_count, 1) obj.start = 10 * gst.SECOND obj.in_point = 11 * gst.SECOND obj.duration = 15 * gst.SECOND # this should be possible monitor = TrackSignalMonitor(obj) time = 0 * gst.SECOND obj.trimStart(time) self.failUnlessEqual(obj.start, 0 * gst.SECOND) self.failUnlessEqual(obj.in_point, 1 * gst.SECOND) self.failUnlessEqual(obj.duration, 25 * gst.SECOND) self.failUnlessEqual(obj.rate, 1) self.failUnlessEqual(monitor.start_changed_count, 1) self.failUnlessEqual(monitor.in_point_changed_count, 1) self.failUnlessEqual(monitor.duration_changed_count, 1) def testSplitObject(self): DURATION = 10 * gst.SECOND factory = AudioTestSourceFactory() factory.duration = DURATION stream_ = AudioStream(gst.Caps("audio/x-raw-int")) obj = SourceTrackObject(factory, stream_) track = Track(stream_) track.addTrackObject(obj) obj.start = 3 * gst.SECOND obj.duration = DURATION # create a zig-zag volume curve interpolator = obj.getInterpolator("volume") expected = dict(((t * gst.SECOND, (t % 2, gst.INTERPOLATE_LINEAR)) for t in xrange(3, 10, 3))) for time, (value, mode) in expected.iteritems(): interpolator.newKeyframe(time, value, mode) def getKeyframes(obj): keyframes = obj.getInterpolator("volume").getInteriorKeyframes() return dict(((kf.time, (kf.value, kf.mode)) for kf in keyframes)) monitor = TrackSignalMonitor(obj) self.failUnlessRaises(TrackError, obj.splitObject, 2 * gst.SECOND) self.failUnlessRaises(TrackError, obj.splitObject, 14 * gst.SECOND) # should these be possible (ie create empty objects) ? self.failUnlessRaises(TrackError, obj.splitObject, 3 * gst.SECOND) self.failUnlessRaises(TrackError, obj.splitObject, 13 * gst.SECOND) # splitObject at 4s should result in: # obj (start 3, end 4) other1 (start 4, end 13) other1 = obj.splitObject(4 * gst.SECOND) self.failUnlessEqual(expected, getKeyframes(other1)) self.failUnlessEqual(obj.start, 3 * gst.SECOND) self.failUnlessEqual(obj.in_point, 0 * gst.SECOND) self.failUnlessEqual(obj.duration, 1 * gst.SECOND) self.failUnlessEqual(obj.rate, 1) self.failUnlessEqual(other1.start, 4 * gst.SECOND) self.failUnlessEqual(other1.in_point, 1 * gst.SECOND) self.failUnlessEqual(other1.duration, 9 * gst.SECOND) self.failUnlessEqual(other1.rate, 1) self.failUnlessEqual(monitor.start_changed_count, 0) self.failUnlessEqual(monitor.duration_changed_count, 1) # move other1 back to start = 1 other1.start = 1 * gst.SECOND # splitObject again other1 monitor = TrackSignalMonitor(other1) other2 = other1.splitObject(6 * gst.SECOND) self.failUnlessEqual(other1.start, 1 * gst.SECOND) self.failUnlessEqual(other1.in_point, 1 * gst.SECOND) self.failUnlessEqual(other1.duration, 5 * gst.SECOND) self.failUnlessEqual(other1.rate, 1) self.failUnlessEqual(other2.start, 6 * gst.SECOND) self.failUnlessEqual(other2.in_point, 6 * gst.SECOND) self.failUnlessEqual(other2.duration, 4 * gst.SECOND) self.failUnlessEqual(other2.rate, 1) self.failUnlessEqual(monitor.start_changed_count, 0) self.failUnlessEqual(monitor.duration_changed_count, 1) def testSplitObjectKeyframes(self): DURATION = 10 * gst.SECOND factory = AudioTestSourceFactory() factory.duration = DURATION stream_ = AudioStream(gst.Caps("audio/x-raw-int")) obj = SourceTrackObject(factory, stream_) track = Track(stream_) track.addTrackObject(obj) obj.start = 3 * gst.SECOND obj.duration = DURATION # create a three keyframes at: 3, 6 and 9 seconds interpolator = obj.getInterpolator("volume") keyframes = dict(((t * gst.SECOND, (t % 2, gst.INTERPOLATE_LINEAR)) for t in xrange(3, 10, 3))) expected = [] expected2 = [] for time, (value, mode) in keyframes.iteritems(): kf = interpolator.newKeyframe(time, value, mode) if time < (5 * gst.SECOND): expected.append(kf) else: expected2.append(kf) def getKeyframes(obj): keyframes = obj.getInterpolator("volume").getInteriorKeyframes() return list(keyframes) obj2 = obj.splitObject(8 * gst.SECOND) keyframes = getKeyframes(obj) keyframes2 = getKeyframes(obj2) self.failUnlessEqual(keyframes, expected) self.failUnlessEqual(keyframes2, expected2)
class TestGap(TestCase): def setUp(self): self.factory = StubFactory() self.stream = AudioStream(gst.Caps('audio/x-raw-int')) self.factory.addOutputStream(self.stream) self.track1 = Track(self.stream) self.timeline = Timeline() def makeTimelineObject(self): track_object = SourceTrackObject(self.factory, self.stream) self.track1.addTrackObject(track_object) timeline_object = TimelineObject(self.factory) timeline_object.addTrackObject(track_object) self.timeline.addTimelineObject(timeline_object) return timeline_object def testGapCmp(self): gap1 = Gap(None, None, start=10, duration=5) gap2 = Gap(None, None, start=10, duration=5) self.failUnlessEqual(gap1, gap2) gap2 = Gap(None, None, start=15, duration=4) self.failUnless(gap1 > gap2) self.failUnless(gap2 < gap1) def testFindAroundObject(self): timeline_object1 = self.makeTimelineObject() timeline_object2 = self.makeTimelineObject() timeline_object1.start = 5 * gst.SECOND timeline_object1.duration = 10 * gst.SECOND timeline_object2.start = 20 * gst.SECOND timeline_object2.duration = 10 * gst.SECOND left_gap, right_gap = Gap.findAroundObject(timeline_object1) self.failUnlessEqual(left_gap.left_object, None) self.failUnlessEqual(left_gap.right_object, timeline_object1) self.failUnlessEqual(left_gap.start, 0 * gst.SECOND) self.failUnlessEqual(left_gap.duration, 5 * gst.SECOND) self.failUnlessEqual(right_gap.left_object, timeline_object1) self.failUnlessEqual(right_gap.right_object, timeline_object2) self.failUnlessEqual(right_gap.start, 15 * gst.SECOND) self.failUnlessEqual(right_gap.duration, 5 * gst.SECOND) left_gap, right_gap = Gap.findAroundObject(timeline_object2) self.failUnlessEqual(left_gap.left_object, timeline_object1) self.failUnlessEqual(left_gap.right_object, timeline_object2) self.failUnlessEqual(left_gap.start, 15 * gst.SECOND) self.failUnlessEqual(left_gap.duration, 5 * gst.SECOND) self.failUnlessEqual(right_gap.left_object, timeline_object2) self.failUnlessEqual(right_gap.right_object, None) self.failUnlessEqual(right_gap.start, 30 * gst.SECOND) self.failUnlessEqual(right_gap.duration, infinity) # make the objects overlap timeline_object2.start = 10 * gst.SECOND left_gap, right_gap = Gap.findAroundObject(timeline_object1) self.failUnlessEqual(right_gap.left_object, timeline_object1) self.failUnlessEqual(right_gap.right_object, timeline_object2) self.failUnlessEqual(right_gap.start, 15 * gst.SECOND) self.failUnlessEqual(right_gap.duration, -5 * gst.SECOND) def testGapFinder(self): timeline_object1 = self.makeTimelineObject() timeline_object2 = self.makeTimelineObject() timeline_object3 = self.makeTimelineObject() timeline_object4 = self.makeTimelineObject() timeline_object1.start = 5 * gst.SECOND timeline_object1.duration = 10 * gst.SECOND timeline_object1.priority = 1 timeline_object2.start = 20 * gst.SECOND timeline_object2.duration = 10 * gst.SECOND timeline_object2.priority = 1 timeline_object3.start = 31 * gst.SECOND timeline_object3.duration = 10 * gst.SECOND timeline_object3.priority = 2 timeline_object4.start = 50 * gst.SECOND timeline_object4.duration = 10 * gst.SECOND timeline_object4.priority = 2 gap_finder = SmallestGapsFinder(set([timeline_object2, timeline_object3])) gap_finder.update(*Gap.findAroundObject(timeline_object2)) gap_finder.update(*Gap.findAroundObject(timeline_object3)) left_gap = gap_finder.left_gap right_gap = gap_finder.right_gap self.failUnlessEqual(left_gap.left_object, timeline_object1) self.failUnlessEqual(left_gap.right_object, timeline_object2) self.failUnlessEqual(left_gap.start, 15 * gst.SECOND) self.failUnlessEqual(left_gap.duration, 5 * gst.SECOND) self.failUnlessEqual(right_gap.left_object, timeline_object3) self.failUnlessEqual(right_gap.right_object, timeline_object4) self.failUnlessEqual(right_gap.start, 41 * gst.SECOND) self.failUnlessEqual(right_gap.duration, 9 * gst.SECOND) # make timeline_object3 and timeline_object4 overlap timeline_object3.duration = 20 * gst.SECOND gap_finder = SmallestGapsFinder(set([timeline_object4])) gap_finder.update(*Gap.findAroundObject(timeline_object4)) left_gap = gap_finder.left_gap right_gap = gap_finder.right_gap self.failUnlessEqual(left_gap, invalid_gap) self.failUnlessEqual(right_gap.left_object, timeline_object4) self.failUnlessEqual(right_gap.right_object, None) self.failUnlessEqual(right_gap.start, 60 * gst.SECOND) self.failUnlessEqual(right_gap.duration, infinity) gap_finder = SmallestGapsFinder(set([timeline_object3])) gap_finder.update(*Gap.findAroundObject(timeline_object3)) left_gap = gap_finder.left_gap right_gap = gap_finder.right_gap self.failUnlessEqual(left_gap.left_object, None) self.failUnlessEqual(left_gap.right_object, timeline_object3) self.failUnlessEqual(left_gap.start, 0 * gst.SECOND) self.failUnlessEqual(left_gap.duration, 31 * gst.SECOND) self.failUnlessEqual(right_gap, invalid_gap) def testFindAllGaps(self): simple = ( (3 * gst.SECOND, 1 * gst.SECOND), (1 * gst.SECOND, 1 * gst.SECOND) ) objs = [] for start, duration in simple: obj = self.makeTimelineObject() obj.start = start obj.duration = duration objs.append(obj) result = [(g.start, g.initial_duration) for g in Gap.findAllGaps(objs)] self.assertEquals(result, [ (0 * gst.SECOND, 1 * gst.SECOND), (2 * gst.SECOND, 1 * gst.SECOND), ]) complex = [ (1 * gst.SECOND, 2 * gst.SECOND), (6 * gst.SECOND, 2 * gst.SECOND), (10 * gst.SECOND, 2 * gst.SECOND), (8 * gst.SECOND, 2 * gst.SECOND), (14 * gst.SECOND, 1 * gst.SECOND), (4 * gst.SECOND, 1 * gst.SECOND), ] objs = [] for start, duration in complex: obj = self.makeTimelineObject() obj.start = start obj.duration = duration objs.append(obj) result = [(g.start, g.initial_duration) for g in Gap.findAllGaps(objs)] self.assertEquals(result, [ (0 * gst.SECOND, 1 * gst.SECOND), (3 * gst.SECOND, 1 * gst.SECOND), (5 * gst.SECOND, 1 * gst.SECOND), (12 * gst.SECOND, 2 * gst.SECOND), ]) complex.append((2 * gst.SECOND, 5 * gst.SECOND)) objs = [] for start, duration in complex: obj = self.makeTimelineObject() obj.start = start obj.duration = duration objs.append(obj) result = [(g.start, g.initial_duration) for g in Gap.findAllGaps(objs)] self.assertEquals(result, [ (0 * gst.SECOND, 1 * gst.SECOND), (12 * gst.SECOND, 2 * gst.SECOND), ])
class TestTransitions(TestCase): def setUp(self): TestCase.setUp(self) self.factory = StubFactory() self.stream = VideoStream(gst.Caps('video/x-raw-rgb')) self.factory.addOutputStream(self.stream) self.track1 = Track(self.stream) def tearDown(self): self.factory = None self.stream = None self.track1 = None TestCase.tearDown(self) def testAddRemoveTransitions(self): factory = self.factory track1 = self.track1 track1._update_transitions = False stream = self.stream test_data = [ ("a", 0, 10), ("b", 5, 15), ("c", 15, 20), ("d", 30, 35), ("e", 30, 35), ] transitions = [ ("a", "b"), ("d", "e"), ] objs = {} names = {} for name, start, end in test_data: obj = SourceTrackObject(factory, stream) obj.start = start * gst.SECOND obj.in_point = 0 obj.duration = end * gst.SECOND - obj.start obj.media_duration = obj.duration track1.addTrackObject(obj) names[obj] = name objs[name] = obj result = [] transition_objects = {} def addTransition(b, c): tr = Transition(objs[b], objs[c]) track1.addTransition(tr) def transitionAddedCb(track, transition): values = (names[transition.a], names[transition.b]) result.append(values) transition_objects[values] = transition def transitionRemovedCb(track, transition): values = (names[transition.a], names[transition.b]) result.remove(values) track1.connect("transition-added", transitionAddedCb) track1.connect("transition-removed", transitionRemovedCb) # add transitions and check that initial properties are properly # evaluated for a, b in transitions: addTransition(a, b) self.failUnlessEqual(result, transitions) # check that adding a transition with a bogus track object raises an # error track1.removeTrackObject(objs["c"]) self.failUnlessRaises(TrackError, addTransition, "b", "c") # check that adding a transition that already exists raises an error self.failUnlessRaises(TrackError, addTransition, "d", "e") # check that removing a transition directly works track1.removeTransition(transition_objects["d", "e"]) self.failUnlessEqual(result, [("a", "b")]) # check tht we can restore a transition after deleting it addTransition("d", "e") self.failUnlessEqual(result, [("a", "b"), ("d", "e")]) def testTransitionProperties(self): factory = self.factory track1 = self.track1 track1._update_transitions = False stream = self.stream test_data = [ ("a", 0, 10), ("b", 5, 15), ] objs = {} names = {} for name, start, end in test_data: obj = SourceTrackObject(factory, stream) obj.start = start * gst.SECOND obj.in_point = 0 obj.duration = end * gst.SECOND - obj.start obj.media_duration = obj.duration track1.addTrackObject(obj) names[obj] = name objs[name] = obj # add transitions and check that initial properties are properly # evaluated at = AudioTransition(objs["a"], objs["b"]) vt = VideoTransition(objs["a"], objs["b"]) # move a and b together, # check that transition start, duration are updated objs["a"].start = 5 * gst.SECOND objs["b"].start = 10 * gst.SECOND self.failUnlessEqual(vt.start, 10 * gst.SECOND) self.failUnlessEqual(vt.duration, 5 * gst.SECOND) self.failUnlessEqual(vt.operation.props.start, 10 * gst.SECOND) self.failUnlessEqual(vt.operation.props.duration, 5 * gst.SECOND) self.failUnlessEqual(at.start, 10 * gst.SECOND) self.failUnlessEqual(at.duration, 5 * gst.SECOND) self.failUnlessEqual(at.a_operation.props.start, 10 * gst.SECOND) self.failUnlessEqual(at.a_operation.props.duration, 5 * gst.SECOND) self.failUnlessEqual(at.b_operation.props.start, 10 * gst.SECOND) self.failUnlessEqual(at.b_operation.props.duration, 5 * gst.SECOND) # make A longer objs["a"].duration = 11 * gst.SECOND self.failUnlessEqual(vt.start, 10 * gst.SECOND) self.failUnlessEqual(vt.duration, 6 * gst.SECOND) self.failUnlessEqual(vt.operation.props.start, 10 * gst.SECOND) self.failUnlessEqual(vt.operation.props.duration, 6 * gst.SECOND) self.failUnlessEqual(at.start, 10 * gst.SECOND) self.failUnlessEqual(at.duration, 6 * gst.SECOND) self.failUnlessEqual(at.a_operation.props.start, 10 * gst.SECOND) self.failUnlessEqual(at.a_operation.props.duration, 6 * gst.SECOND) self.failUnlessEqual(at.b_operation.props.start, 10 * gst.SECOND) self.failUnlessEqual(at.b_operation.props.duration, 6 * gst.SECOND) # move B earlier objs["b"].start = 9 * gst.SECOND self.failUnlessEqual(vt.start, 9 * gst.SECOND) self.failUnlessEqual(vt.duration, 7 * gst.SECOND) self.failUnlessEqual(vt.operation.props.start, 9 * gst.SECOND) self.failUnlessEqual(vt.operation.props.duration, 7 * gst.SECOND) self.failUnlessEqual(vt.operation.props.media_duration, 7 * gst.SECOND) self.failUnlessEqual(at.start, 9 * gst.SECOND) self.failUnlessEqual(at.duration, 7 * gst.SECOND) self.failUnlessEqual(at.a_operation.props.start, 9 * gst.SECOND) self.failUnlessEqual(at.a_operation.props.duration, 7 * gst.SECOND) self.failUnlessEqual(at.a_operation.props.media_duration, 7 * gst.SECOND) self.failUnlessEqual(at.b_operation.props.start, 9 * gst.SECOND) self.failUnlessEqual(at.b_operation.props.duration, 7 * gst.SECOND) self.failUnlessEqual(at.b_operation.props.media_duration, 7 * gst.SECOND) # check priority is currently zero self.failUnlessEqual(vt.priority, 0) # check video transition priority basic properties self.failUnlessEqual(vt.operation.props.priority, 1) vt.a.priority = 2 vt.b.priority = 2 self.failUnlessEqual(vt.priority, 2) self.failUnlessEqual(vt.operation.props.priority, 7) self.failUnlessEqual(at.priority, 2) # check controller for even - odd stagger vt.a.updatePosition(0) vt.b.updatePosition(1) self.failUnlessEqual(vt.a.gnl_object.props.priority, 9) self.failUnlessEqual(at.a._stagger, 0) self.failUnlessEqual(at.b._stagger, 1) self.failUnlessEqual(vt.b.gnl_object.props.priority, 10) self.failUnlessEqual(vt.controller.get("alpha", 0), 1.0) self.failUnlessEqual(vt.controller.get("alpha", vt.duration), 0.0) self.failUnlessEqual(at.a_controller.get("volume", 0), 1.0) self.failUnlessEqual(at.a_controller.get("volume", vt.duration), 0.0) self.failUnlessEqual(at.b_controller.get("volume", 0), 0.0) self.failUnlessEqual(at.b_controller.get("volume", vt.duration), 1.0) self.failUnlessEqual(at.a_operation.props.priority, 9) self.failUnlessEqual(at.b_operation.props.priority, 11) # check controller for odd - even stagger vt.a.updatePosition(1) vt.b.updatePosition(2) self.failUnlessEqual(vt.controller.get("alpha", 0), 0.0) self.failUnlessEqual(vt.controller.get("alpha", vt.duration), 1.0) self.failUnlessEqual(at.a_controller.get("volume", 0), 1.0) self.failUnlessEqual(at.a_controller.get("volume", vt.duration), 0.0) self.failUnlessEqual(at.b_controller.get("volume", 0), 0.0) self.failUnlessEqual(at.b_controller.get("volume", vt.duration), 1.0) self.failUnlessEqual(at.a_operation.props.priority, 11) self.failUnlessEqual(at.b_operation.props.priority, 9) def testGetTrackObjectsGroupedByLayer(self): factory = self.factory stream = self.stream track1 = self.track1 test_data = [ ("a", 0, 10, 0), ("b", 5, 15, 0), ("c", 20, 25, 0), ("d", 30, 35, 0), ("e", 30, 35, 2), ("f", 35, 45, 0), ("g", 40, 50, 0), ("h", 50, 60, 0), ("i", 55, 65, 1), ("j", 57, 60, 2), ("k", 62, 70, 3), ("l", 63, 67, 0), ] expected = [["a", "b", "c", "d", "f", "g", "h", "l"], ["i"], ["e", "j"], ["k"]] objs = {} for name, start, end, priority in test_data: obj = SourceTrackObject(factory, stream) obj.start = start * gst.SECOND obj.duration = end * gst.SECOND - obj.start obj.priority = priority track1.addTrackObject(obj) objs[obj] = name result = [[objs[obj] for obj in layer] for layer in track1.getTrackObjectsGroupedByLayer()] self.failUnlessEqual(result, expected) def testGetValidTransitionSlots(self): factory = self.factory stream = self.stream track1 = self.track1 test_data = [ ("a", 0, 10), ("b", 5, 15), ("c", 20, 25), ("d", 30, 35), ("e", 30, 35), ("f", 35, 45), ("g", 40, 50), ("h", 50, 60), ("i", 55, 65), ("j", 57, 60), ("k", 62, 70), ("l", 63, 67), ] expected = [["a", "b"], ["d", "e"], ["f", "g"]] objs = {} names = {} ordered = [] for name, start, end in test_data: obj = SourceTrackObject(factory, stream) obj.start = start * gst.SECOND obj.duration = end * gst.SECOND - obj.start track1.addTrackObject(obj) objs[name] = obj names[obj] = name ordered.append(obj) slots, valid = track1.getValidTransitionSlots(ordered) result = [[names[obj] for obj in layer] for layer in slots] self.failUnlessEqual(result, expected) self.failUnlessEqual(track1.valid_arrangement, False) test_data = [("a", 0, 5), ("b", 9, 12), ("c", 8, 13)] ordered = [] for name, start, end in test_data: ordered.append(objs[name]) objs[name].start = gst.SECOND * start objs[name].duration = gst.SECOND * (end - start) slots, valid = track1.getValidTransitionSlots(ordered) self.failUnlessEqual(valid, False) test_data = [ ("a", 0, 5), ("b", 1, 4), ("c", 8, 13), ] ordered = [] for name, start, end in test_data: ordered.append(objs[name]) objs[name].start = gst.SECOND * start objs[name].duration = gst.SECOND * (end - start) slots, valid = track1.getValidTransitionSlots(ordered) self.failUnlessEqual(valid, False) def testUpdateTransitions(self): factory = self.factory stream = self.stream track1 = self.track1 track1._update_transitions = False test_data = [ ("a", 0, 10), ("b", 5, 15), ("c", 20, 25), ("d", 30, 35), ("e", 30, 35), ("f", 35, 45), ("g", 40, 50), ("h", 50, 60), ("i", 55, 65), ("j", 57, 60), ("k", 62, 70), ("l", 63, 67), ] expected = [("a", "b"), ("d", "e"), ("f", "g")] objs = {} names = {} for name, start, end in test_data: obj = SourceTrackObject(factory, stream) obj.start = start * gst.SECOND obj.in_point = 0 obj.duration = end * gst.SECOND - obj.start obj.media_duration = obj.duration track1.addTrackObject(obj) names[obj] = name objs[name] = obj result = [] added = set() removed = set() def transitionAddedCb(track, transition): pair = (names[transition.a], names[transition.b]) result.append(pair) added.add(pair) def transitionRemovedCb(track, transition): pair = (names[transition.a], names[transition.b]) result.remove(pair) removed.add(pair) track1.connect("transition-added", transitionAddedCb) track1.connect("transition-removed", transitionRemovedCb) track1.updateTransitions() self.failUnlessEqual(result, expected) # move c so that it overlaps with b # move g so that it overlaps d, e, f # update the transitions, check that we have the expected # configuration test_data = [ ("c", 12, 20), ("g", 30, 46), ] expected = [("a", "b"), ("b", "c")] added = set() removed = set() for name, start, end in test_data: objs[name].start = start * gst.SECOND objs[name].duration = (end - start) * gst.SECOND track1.updateTransitions() self.failUnlessEqual(result, expected) # check that *only* (b, c) was added in the update self.failUnlessEqual(added, set([("b", "c")])) # check that *only* (d, e) was removed in the update self.failUnlessEqual(removed, set([("d", "e"), ("f", "g")])) # move c to a different layer. check that (b, c) transition is removed added = set() removed = set() objs["c"].priority = 1 expected = [("a", "b")] track1.updateTransitions() self.failUnlessEqual(result, expected) self.failUnlessEqual(added, set()) self.failUnlessEqual(removed, set([("b", "c")])) def testUpdateAfterAddingAndRemovingTrackObjects(self): factory = self.factory stream = self.stream track1 = self.track1 test_data = [ ("a", 0, 10), ("b", 5, 15), ("c", 20, 25), ("d", 30, 35), ("f", 35, 45), ("g", 40, 50), ("e", 30, 35), ("h", 50, 60), ("i", 55, 65), ("j", 57, 60), ("k", 62, 70), ("l", 63, 67), ] # object j makes track arrangment invalid valid_in_order = [True for x in "abcdefghi"] valid_in_order.extend([False, False, False]) added_in_order = [("a", "b"), ("f", "g"), ("d", "e"), ("h", "i"), ("i", "k"), ("h", "i")] removed_in_order = [("h", "i"), ("i", "k")] objs = {} names = {} added = [] removed = [] def transitionAddedCb(track, transition): added.append((names[transition.a], names[transition.b])) def transitionRemovedCb(track, transition): removed.append((names[transition.a], names[transition.b])) track1.connect("transition-added", transitionAddedCb) track1.connect("transition-removed", transitionRemovedCb) valid = [] for name, start, end in test_data: obj = SourceTrackObject(factory, stream) obj.start = start * gst.SECOND obj.in_point = 0 obj.duration = end * gst.SECOND - obj.start obj.media_duration = obj.duration names[obj] = name objs[name] = obj track1.addTrackObject(obj) valid.append(track1.valid_arrangement) # removing this object brings (h, i) back track1.removeTrackObject(objs["j"]) self.failUnlessEqual(added, added_in_order) self.failUnlessEqual(removed, removed_in_order) self.failUnlessEqual(valid, valid_in_order) # removing this should make the track valid again track1.removeTrackObject(objs["l"]) self.failUnlessEqual(track1.valid_arrangement, True) def testUpdatesAfterEnablingUpdates(self): factory = self.factory stream = self.stream track1 = self.track1 test_data = [ ("a", 0, 10), ("b", 5, 15), ("c", 20, 25), ("d", 30, 35), ("e", 30, 35), ("f", 35, 45), ("g", 40, 50), ("h", 50, 60), ("i", 55, 65), ("j", 57, 60), ("k", 62, 70), ("l", 63, 67), ] expected = [("a", "b"), ("d", "e"), ("f", "g")] result = [] added = set() removed = set() def transitionAddedCb(track, transition): pair = (names[transition.a], names[transition.b]) result.append(pair) added.add(pair) def transitionRemovedCb(track, transition): pair = (names[transition.a], names[transition.b]) result.remove(pair) removed.add(pair) track1.connect("transition-added", transitionAddedCb) track1.connect("transition-removed", transitionRemovedCb) objs = {} names = {} for name, start, end in test_data: obj = SourceTrackObject(factory, stream) obj.start = start * gst.SECOND obj.in_point = 0 obj.duration = end * gst.SECOND - obj.start obj.media_duration = obj.duration names[obj] = name objs[name] = obj track1.addTrackObject(obj) self.failUnlessEqual(result, expected) track1.disableUpdates() # move c so that it overlaps with b # move g so that it overlaps d, e, f # update the transitions, check that we have the expected # configuration test_data = [ ("c", 12, 20), ("g", 30, 46), ] expected = [("a", "b"), ("b", "c")] added = set() removed = set() for name, start, end in test_data: objs[name].start = start * gst.SECOND objs[name].duration = (end - start) * gst.SECOND track1.enableUpdates() self.failUnlessEqual(result, expected) # check that *only* (b, c) was added in the update self.failUnlessEqual(added, set([("b", "c")])) # check that *only* (d, e) was removed in the update self.failUnlessEqual(removed, set([("d", "e"), ("f", "g")])) # move c to a different layer. check that (b, c) transition is removed track1.disableUpdates() added = set() removed = set() objs["c"].priority = 1 expected = [("a", "b")] track1.enableUpdates() self.failUnlessEqual(result, expected) self.failUnlessEqual(added, set()) self.failUnlessEqual(removed, set([("b", "c")])) def testTrackObjectStagger(self): factory = self.factory stream = self.stream track1 = self.track1 test_data = [ ("a", 0, 10), ("b", 8, 18), ("c", 16, 26), ("d", 24, 32), ("e", 30, 40), ("f", 38, 48), # --- ("g", 50, 60), ("h", 58, 68), # --- ("i", 70, 80), # --- ("j", 80, 90), ("k", 88, 98), # --- ("l", 100, 110), ] transitions = ["ab", "cd", "ef", "gh", "ik"] objs = {} names = {} def addClip(name, start, end): obj = SourceTrackObject(factory, stream) obj.start = start * gst.SECOND obj.in_point = 0 obj.duration = end * gst.SECOND - obj.start obj.media_duration = obj.duration obj.name = name names[obj] = name objs[name] = obj track1.addTrackObject(obj) for name, start, end in test_data: addClip(name, start, end) def verify_result(expected): expected_stagger = [(name, pos % 2) for name, pos in zip(*expected)] result = ["", []] for obj in track1.track_objects: result[0] += names[obj] result[1].append(obj._position) resulting_stagger = [(names[obj], obj._stagger) for obj in track1.track_objects] self.failUnlessEqual(result, expected) self.failUnlessEqual(resulting_stagger, expected_stagger) # check that intiial configuration matches expected = ["abcdefghijkl", [0, 1, 2, 3, 4, 5, 6, 7, 0, 8, 9, 0]] verify_result(expected) # remove a clip, which removes its associated transition track1.removeTrackObject(objs["e"]) expected = ["abcdfghijkl", [0, 1, 2, 3, 5, 4, 5, 0, 6, 7, 0]] verify_result(expected) # add a clip addClip('m', 16, 17) expected = ["abcmdfghijkl", [0, 1, 2, 0, 3, 5, 4, 5, 0, 6, 7, 0]] verify_result(expected) # re-order a few clips track1.disableUpdates() objs["d"].start = 18 * gst.SECOND objs["i"].start = 25 * gst.SECOND track1.enableUpdates() expected = ["abcmdifghjkl", [0, 1, 2, 0, 3, 0, 5, 2, 3, 4, 5, 0]] verify_result(expected)
class TestTransitions(TestCase): def setUp(self): TestCase.setUp(self) self.factory = StubFactory() self.stream = VideoStream(gst.Caps('video/x-raw-rgb')) self.factory.addOutputStream(self.stream) self.track1 = Track(self.stream) def tearDown(self): self.factory = None self.stream = None self.track1 = None TestCase.tearDown(self) def testAddRemoveTransitions(self): factory = self.factory track1 = self.track1 track1._update_transitions = False stream = self.stream test_data = [ ("a", 0, 10), ("b", 5, 15), ("c", 15, 20), ("d", 30, 35), ("e", 30, 35), ] transitions = [ ("a", "b"), ("d", "e"), ] objs = {} names = {} for name, start, end in test_data: obj = SourceTrackObject(factory, stream) obj.start = start * gst.SECOND obj.in_point = 0 obj.duration = end * gst.SECOND - obj.start obj.media_duration = obj.duration track1.addTrackObject(obj) names[obj] = name objs[name] = obj result = [] transition_objects = {} def addTransition(b, c): tr = Transition(objs[b], objs[c]) track1.addTransition(tr) def transitionAddedCb(track, transition): values =(names[transition.a], names[transition.b]) result.append(values) transition_objects[values] = transition def transitionRemovedCb(track, transition): values =(names[transition.a], names[transition.b]) result.remove(values) track1.connect("transition-added", transitionAddedCb) track1.connect("transition-removed", transitionRemovedCb) # add transitions and check that initial properties are properly # evaluated for a, b in transitions: addTransition(a, b) self.failUnlessEqual(result, transitions) # check that adding a transition with a bogus track object raises an # error track1.removeTrackObject(objs["c"]) self.failUnlessRaises(TrackError, addTransition, "b", "c") # check that adding a transition that already exists raises an error self.failUnlessRaises(TrackError, addTransition, "d", "e") # check that removing a transition directly works track1.removeTransition(transition_objects["d", "e"]) self.failUnlessEqual(result, [("a", "b")]) # check tht we can restore a transition after deleting it addTransition("d", "e") self.failUnlessEqual(result, [("a", "b"), ("d", "e")]) def testTransitionProperties(self): factory = self.factory track1 = self.track1 track1._update_transitions = False stream = self.stream test_data = [ ("a", 0, 10), ("b", 5, 15), ] objs = {} names = {} for name, start, end in test_data: obj = SourceTrackObject(factory, stream) obj.start = start * gst.SECOND obj.in_point = 0 obj.duration = end * gst.SECOND - obj.start obj.media_duration = obj.duration track1.addTrackObject(obj) names[obj] = name objs[name] = obj # add transitions and check that initial properties are properly # evaluated at = AudioTransition(objs["a"], objs["b"]) vt = VideoTransition(objs["a"], objs["b"]) # move a and b together, # check that transition start, duration are updated objs["a"].start = 5 * gst.SECOND objs["b"].start = 10 * gst.SECOND self.failUnlessEqual(vt.start, 10 * gst.SECOND) self.failUnlessEqual(vt.duration, 5 * gst.SECOND) self.failUnlessEqual(vt.operation.props.start, 10 * gst.SECOND) self.failUnlessEqual(vt.operation.props.duration, 5 * gst.SECOND) self.failUnlessEqual(at.start, 10 * gst.SECOND) self.failUnlessEqual(at.duration, 5 * gst.SECOND) self.failUnlessEqual(at.a_operation.props.start, 10 * gst.SECOND) self.failUnlessEqual(at.a_operation.props.duration, 5 * gst.SECOND) self.failUnlessEqual(at.b_operation.props.start, 10 * gst.SECOND) self.failUnlessEqual(at.b_operation.props.duration, 5 * gst.SECOND) # make A longer objs["a"].duration = 11 * gst.SECOND self.failUnlessEqual(vt.start, 10 * gst.SECOND) self.failUnlessEqual(vt.duration, 6 * gst.SECOND) self.failUnlessEqual(vt.operation.props.start, 10 * gst.SECOND) self.failUnlessEqual(vt.operation.props.duration, 6 * gst.SECOND) self.failUnlessEqual(at.start, 10 * gst.SECOND) self.failUnlessEqual(at.duration, 6 * gst.SECOND) self.failUnlessEqual(at.a_operation.props.start, 10 * gst.SECOND) self.failUnlessEqual(at.a_operation.props.duration, 6 * gst.SECOND) self.failUnlessEqual(at.b_operation.props.start, 10 * gst.SECOND) self.failUnlessEqual(at.b_operation.props.duration, 6 * gst.SECOND) # move B earlier objs["b"].start = 9 * gst.SECOND self.failUnlessEqual(vt.start, 9 * gst.SECOND) self.failUnlessEqual(vt.duration, 7 * gst.SECOND) self.failUnlessEqual(vt.operation.props.start, 9 * gst.SECOND) self.failUnlessEqual(vt.operation.props.duration, 7 * gst.SECOND) self.failUnlessEqual(vt.operation.props.media_duration, 7 * gst.SECOND) self.failUnlessEqual(at.start, 9 * gst.SECOND) self.failUnlessEqual(at.duration, 7 * gst.SECOND) self.failUnlessEqual(at.a_operation.props.start, 9 * gst.SECOND) self.failUnlessEqual(at.a_operation.props.duration, 7 * gst.SECOND) self.failUnlessEqual(at.a_operation.props.media_duration, 7 * gst.SECOND) self.failUnlessEqual(at.b_operation.props.start, 9 * gst.SECOND) self.failUnlessEqual(at.b_operation.props.duration, 7 * gst.SECOND) self.failUnlessEqual(at.b_operation.props.media_duration, 7 * gst.SECOND) # check priority is currently zero self.failUnlessEqual(vt.priority, 0) # check video transition priority basic properties self.failUnlessEqual(vt.operation.props.priority, 1) vt.a.priority = 2 vt.b.priority = 2 self.failUnlessEqual(vt.priority, 2) self.failUnlessEqual(vt.operation.props.priority, 7) self.failUnlessEqual(at.priority, 2) # check controller for even - odd stagger vt.a.updatePosition(0) vt.b.updatePosition(1) self.failUnlessEqual(vt.a.gnl_object.props.priority, 9) self.failUnlessEqual(at.a._stagger, 0) self.failUnlessEqual(at.b._stagger, 1) self.failUnlessEqual(vt.b.gnl_object.props.priority, 10) self.failUnlessEqual(vt.controller.get("alpha", 0), 1.0) self.failUnlessEqual(vt.controller.get("alpha", vt.duration), 0.0) self.failUnlessEqual(at.a_controller.get("volume", 0), 1.0) self.failUnlessEqual(at.a_controller.get("volume", vt.duration), 0.0) self.failUnlessEqual(at.b_controller.get("volume", 0), 0.0) self.failUnlessEqual(at.b_controller.get("volume", vt.duration), 1.0) self.failUnlessEqual(at.a_operation.props.priority, 9) self.failUnlessEqual(at.b_operation.props.priority, 11) # check controller for odd - even stagger vt.a.updatePosition(1) vt.b.updatePosition(2) self.failUnlessEqual(vt.controller.get("alpha", 0), 0.0) self.failUnlessEqual(vt.controller.get("alpha", vt.duration), 1.0) self.failUnlessEqual(at.a_controller.get("volume", 0), 1.0) self.failUnlessEqual(at.a_controller.get("volume", vt.duration), 0.0) self.failUnlessEqual(at.b_controller.get("volume", 0), 0.0) self.failUnlessEqual(at.b_controller.get("volume", vt.duration), 1.0) self.failUnlessEqual(at.a_operation.props.priority, 11) self.failUnlessEqual(at.b_operation.props.priority, 9) def testGetTrackObjectsGroupedByLayer(self): factory = self.factory stream = self.stream track1 = self.track1 test_data = [ ("a", 0, 10, 0), ("b", 5, 15, 0), ("c", 20, 25, 0), ("d", 30, 35, 0), ("e", 30, 35, 2), ("f", 35, 45, 0), ("g", 40, 50, 0), ("h", 50, 60, 0), ("i", 55, 65, 1), ("j", 57, 60, 2), ("k", 62, 70, 3), ("l", 63, 67, 0), ] expected = [ ["a", "b", "c", "d", "f", "g", "h", "l"], ["i"], ["e", "j"], ["k"] ] objs = {} for name, start, end, priority in test_data: obj = SourceTrackObject(factory, stream) obj.start = start * gst.SECOND obj.duration = end * gst.SECOND - obj.start obj.priority = priority track1.addTrackObject(obj) objs[obj] = name result = [[objs[obj] for obj in layer] for layer in track1.getTrackObjectsGroupedByLayer()] self.failUnlessEqual(result, expected) def testGetValidTransitionSlots(self): factory = self.factory stream = self.stream track1 = self.track1 test_data = [ ("a", 0, 10), ("b", 5, 15), ("c", 20, 25), ("d", 30, 35), ("e", 30, 35), ("f", 35, 45), ("g", 40, 50), ("h", 50, 60), ("i", 55, 65), ("j", 57, 60), ("k", 62, 70), ("l", 63, 67), ] expected = [["a", "b"], ["d", "e"], ["f", "g"]] objs = {} names = {} ordered = [] for name, start, end in test_data: obj = SourceTrackObject(factory, stream) obj.start = start * gst.SECOND obj.duration = end * gst.SECOND - obj.start track1.addTrackObject(obj) objs[name] = obj names[obj] = name ordered.append(obj) slots, valid = track1.getValidTransitionSlots(ordered) result = [[names[obj] for obj in layer] for layer in slots] self.failUnlessEqual(result, expected) self.failUnlessEqual(track1.valid_arrangement, False) test_data = [ ("a", 0, 5), ("b", 9, 12), ("c", 8, 13) ] ordered = [] for name, start, end in test_data: ordered.append(objs[name]) objs[name].start = gst.SECOND * start objs[name].duration = gst.SECOND * (end - start) slots, valid = track1.getValidTransitionSlots(ordered) self.failUnlessEqual(valid, False) test_data = [ ("a", 0, 5), ("b", 1, 4), ("c", 8, 13), ] ordered = [] for name, start, end in test_data: ordered.append(objs[name]) objs[name].start = gst.SECOND * start objs[name].duration = gst.SECOND * (end - start) slots, valid = track1.getValidTransitionSlots(ordered) self.failUnlessEqual(valid, False) def testUpdateTransitions(self): factory = self.factory stream = self.stream track1 = self.track1 track1._update_transitions = False test_data = [ ("a", 0, 10), ("b", 5, 15), ("c", 20, 25), ("d", 30, 35), ("e", 30, 35), ("f", 35, 45), ("g", 40, 50), ("h", 50, 60), ("i", 55, 65), ("j", 57, 60), ("k", 62, 70), ("l", 63, 67), ] expected = [("a", "b"), ("d", "e"), ("f", "g")] objs = {} names = {} for name, start, end in test_data: obj = SourceTrackObject(factory, stream) obj.start = start * gst.SECOND obj.in_point = 0 obj.duration = end * gst.SECOND - obj.start obj.media_duration = obj.duration track1.addTrackObject(obj) names[obj] = name objs[name] = obj result = [] added = set() removed = set() def transitionAddedCb(track, transition): pair =(names[transition.a], names[transition.b]) result.append(pair) added.add(pair) def transitionRemovedCb(track, transition): pair = (names[transition.a], names[transition.b]) result.remove(pair) removed.add(pair) track1.connect("transition-added", transitionAddedCb) track1.connect("transition-removed", transitionRemovedCb) track1.updateTransitions() self.failUnlessEqual(result, expected) # move c so that it overlaps with b # move g so that it overlaps d, e, f # update the transitions, check that we have the expected # configuration test_data = [ ("c", 12, 20), ("g", 30, 46), ] expected = [("a", "b"), ("b", "c")] added = set() removed = set() for name, start, end in test_data: objs[name].start = start * gst.SECOND objs[name].duration = (end - start) * gst.SECOND track1.updateTransitions() self.failUnlessEqual(result, expected) # check that *only* (b, c) was added in the update self.failUnlessEqual(added, set([("b", "c")])) # check that *only* (d, e) was removed in the update self.failUnlessEqual(removed, set([("d", "e"), ("f", "g")])) # move c to a different layer. check that (b, c) transition is removed added = set() removed = set() objs["c"].priority = 1 expected = [("a", "b")] track1.updateTransitions() self.failUnlessEqual(result, expected) self.failUnlessEqual(added, set()) self.failUnlessEqual(removed, set([("b", "c")])) def testUpdateAfterAddingAndRemovingTrackObjects(self): factory = self.factory stream = self.stream track1 = self.track1 test_data = [ ("a", 0, 10), ("b", 5, 15), ("c", 20, 25), ("d", 30, 35), ("f", 35, 45), ("g", 40, 50), ("e", 30, 35), ("h", 50, 60), ("i", 55, 65), ("j", 57, 60), ("k", 62, 70), ("l", 63, 67), ] # object j makes track arrangment invalid valid_in_order = [True for x in "abcdefghi"] valid_in_order.extend([False, False, False]) added_in_order = [("a", "b"), ("f", "g"), ("d", "e"), ("h", "i"), ("i", "k"), ("h", "i")] removed_in_order = [("h", "i"), ("i", "k")] objs = {} names = {} added = [] removed = [] def transitionAddedCb(track, transition): added.append((names[transition.a], names[transition.b])) def transitionRemovedCb(track, transition): removed.append((names[transition.a], names[transition.b])) track1.connect("transition-added", transitionAddedCb) track1.connect("transition-removed", transitionRemovedCb) valid = [] for name, start, end in test_data: obj = SourceTrackObject(factory, stream) obj.start = start * gst.SECOND obj.in_point = 0 obj.duration = end * gst.SECOND - obj.start obj.media_duration = obj.duration names[obj] = name objs[name] = obj track1.addTrackObject(obj) valid.append(track1.valid_arrangement) # removing this object brings (h, i) back track1.removeTrackObject(objs["j"]) self.failUnlessEqual(added, added_in_order) self.failUnlessEqual(removed, removed_in_order) self.failUnlessEqual(valid, valid_in_order) # removing this should make the track valid again track1.removeTrackObject(objs["l"]) self.failUnlessEqual(track1.valid_arrangement, True) def testUpdatesAfterEnablingUpdates(self): factory = self.factory stream = self.stream track1 = self.track1 test_data = [ ("a", 0, 10), ("b", 5, 15), ("c", 20, 25), ("d", 30, 35), ("e", 30, 35), ("f", 35, 45), ("g", 40, 50), ("h", 50, 60), ("i", 55, 65), ("j", 57, 60), ("k", 62, 70), ("l", 63, 67), ] expected = [("a", "b"), ("d", "e"), ("f", "g")] result = [] added = set() removed = set() def transitionAddedCb(track, transition): pair =(names[transition.a], names[transition.b]) result.append(pair) added.add(pair) def transitionRemovedCb(track, transition): pair = (names[transition.a], names[transition.b]) result.remove(pair) removed.add(pair) track1.connect("transition-added", transitionAddedCb) track1.connect("transition-removed", transitionRemovedCb) objs = {} names = {} for name, start, end in test_data: obj = SourceTrackObject(factory, stream) obj.start = start * gst.SECOND obj.in_point = 0 obj.duration = end * gst.SECOND - obj.start obj.media_duration = obj.duration names[obj] = name objs[name] = obj track1.addTrackObject(obj) self.failUnlessEqual(result, expected) track1.disableUpdates() # move c so that it overlaps with b # move g so that it overlaps d, e, f # update the transitions, check that we have the expected # configuration test_data = [ ("c", 12, 20), ("g", 30, 46), ] expected = [("a", "b"), ("b", "c")] added = set() removed = set() for name, start, end in test_data: objs[name].start = start * gst.SECOND objs[name].duration = (end - start) * gst.SECOND track1.enableUpdates() self.failUnlessEqual(result, expected) # check that *only* (b, c) was added in the update self.failUnlessEqual(added, set([("b", "c")])) # check that *only* (d, e) was removed in the update self.failUnlessEqual(removed, set([("d", "e"), ("f", "g")])) # move c to a different layer. check that (b, c) transition is removed track1.disableUpdates() added = set() removed = set() objs["c"].priority = 1 expected = [("a", "b")] track1.enableUpdates() self.failUnlessEqual(result, expected) self.failUnlessEqual(added, set()) self.failUnlessEqual(removed, set([("b", "c")])) def testTrackObjectStagger(self): factory = self.factory stream = self.stream track1 = self.track1 test_data = [ ("a", 0, 10), ("b", 8, 18), ("c", 16, 26), ("d", 24, 32), ("e", 30, 40), ("f", 38, 48), # --- ("g", 50, 60), ("h", 58, 68), # --- ("i", 70, 80), # --- ("j", 80, 90), ("k", 88, 98), # --- ("l", 100, 110), ] transitions = ["ab", "cd", "ef", "gh", "ik"] objs = {} names = {} def addClip(name, start, end): obj = SourceTrackObject(factory, stream) obj.start = start * gst.SECOND obj.in_point = 0 obj.duration = end * gst.SECOND - obj.start obj.media_duration = obj.duration obj.name = name names[obj] = name objs[name] = obj track1.addTrackObject(obj) for name, start, end in test_data: addClip(name, start, end) def verify_result(expected): expected_stagger = [(name, pos % 2) for name, pos in zip(*expected)] result = ["", []] for obj in track1.track_objects: result[0] += names[obj] result[1].append(obj._position) resulting_stagger = [(names[obj], obj._stagger) for obj in track1.track_objects] self.failUnlessEqual(result, expected) self.failUnlessEqual(resulting_stagger, expected_stagger) # check that intiial configuration matches expected = ["abcdefghijkl", [0, 1, 2, 3, 4, 5, 6, 7, 0, 8, 9,0]] verify_result(expected) # remove a clip, which removes its associated transition track1.removeTrackObject(objs["e"]) expected = ["abcdfghijkl", [0, 1, 2, 3, 5, 4, 5, 0, 6, 7, 0]] verify_result(expected) # add a clip addClip('m', 16, 17) expected = ["abcmdfghijkl", [0, 1, 2, 0, 3, 5, 4, 5, 0, 6, 7, 0]] verify_result(expected) # re-order a few clips track1.disableUpdates() objs["d"].start = 18 * gst.SECOND objs["i"].start = 25 * gst.SECOND track1.enableUpdates() expected = ["abcmdifghjkl", [0, 1, 2, 0, 3, 0, 5, 2, 3, 4, 5, 0]] verify_result(expected)
class TestGap(TestCase): def setUp(self): self.factory = StubFactory() self.stream = AudioStream(gst.Caps('audio/x-raw-int')) self.factory.addOutputStream(self.stream) self.track1 = Track(self.stream) self.timeline = Timeline() def makeTimelineObject(self): track_object = SourceTrackObject(self.factory, self.stream) self.track1.addTrackObject(track_object) timeline_object = TimelineObject(self.factory) timeline_object.addTrackObject(track_object) self.timeline.addTimelineObject(timeline_object) return timeline_object def testGapCmp(self): gap1 = Gap(None, None, start=10, duration=5) gap2 = Gap(None, None, start=10, duration=5) self.failUnlessEqual(gap1, gap2) gap2 = Gap(None, None, start=15, duration=4) self.failUnless(gap1 > gap2) self.failUnless(gap2 < gap1) def testFindAroundObject(self): timeline_object1 = self.makeTimelineObject() timeline_object2 = self.makeTimelineObject() timeline_object1.start = 5 * gst.SECOND timeline_object1.duration = 10 * gst.SECOND timeline_object2.start = 20 * gst.SECOND timeline_object2.duration = 10 * gst.SECOND left_gap, right_gap = Gap.findAroundObject(timeline_object1) self.failUnlessEqual(left_gap.left_object, None) self.failUnlessEqual(left_gap.right_object, timeline_object1) self.failUnlessEqual(left_gap.start, 0 * gst.SECOND) self.failUnlessEqual(left_gap.duration, 5 * gst.SECOND) self.failUnlessEqual(right_gap.left_object, timeline_object1) self.failUnlessEqual(right_gap.right_object, timeline_object2) self.failUnlessEqual(right_gap.start, 15 * gst.SECOND) self.failUnlessEqual(right_gap.duration, 5 * gst.SECOND) left_gap, right_gap = Gap.findAroundObject(timeline_object2) self.failUnlessEqual(left_gap.left_object, timeline_object1) self.failUnlessEqual(left_gap.right_object, timeline_object2) self.failUnlessEqual(left_gap.start, 15 * gst.SECOND) self.failUnlessEqual(left_gap.duration, 5 * gst.SECOND) self.failUnlessEqual(right_gap.left_object, timeline_object2) self.failUnlessEqual(right_gap.right_object, None) self.failUnlessEqual(right_gap.start, 30 * gst.SECOND) self.failUnlessEqual(right_gap.duration, infinity) # make the objects overlap timeline_object2.start = 10 * gst.SECOND left_gap, right_gap = Gap.findAroundObject(timeline_object1) self.failUnlessEqual(right_gap.left_object, timeline_object1) self.failUnlessEqual(right_gap.right_object, timeline_object2) self.failUnlessEqual(right_gap.start, 15 * gst.SECOND) self.failUnlessEqual(right_gap.duration, -5 * gst.SECOND) def testGapFinder(self): timeline_object1 = self.makeTimelineObject() timeline_object2 = self.makeTimelineObject() timeline_object3 = self.makeTimelineObject() timeline_object4 = self.makeTimelineObject() timeline_object1.start = 5 * gst.SECOND timeline_object1.duration = 10 * gst.SECOND timeline_object1.priority = 1 timeline_object2.start = 20 * gst.SECOND timeline_object2.duration = 10 * gst.SECOND timeline_object2.priority = 1 timeline_object3.start = 31 * gst.SECOND timeline_object3.duration = 10 * gst.SECOND timeline_object3.priority = 2 timeline_object4.start = 50 * gst.SECOND timeline_object4.duration = 10 * gst.SECOND timeline_object4.priority = 2 gap_finder = SmallestGapsFinder( set([timeline_object2, timeline_object3])) gap_finder.update(*Gap.findAroundObject(timeline_object2)) gap_finder.update(*Gap.findAroundObject(timeline_object3)) left_gap = gap_finder.left_gap right_gap = gap_finder.right_gap self.failUnlessEqual(left_gap.left_object, timeline_object1) self.failUnlessEqual(left_gap.right_object, timeline_object2) self.failUnlessEqual(left_gap.start, 15 * gst.SECOND) self.failUnlessEqual(left_gap.duration, 5 * gst.SECOND) self.failUnlessEqual(right_gap.left_object, timeline_object3) self.failUnlessEqual(right_gap.right_object, timeline_object4) self.failUnlessEqual(right_gap.start, 41 * gst.SECOND) self.failUnlessEqual(right_gap.duration, 9 * gst.SECOND) # make timeline_object3 and timeline_object4 overlap timeline_object3.duration = 20 * gst.SECOND gap_finder = SmallestGapsFinder(set([timeline_object4])) gap_finder.update(*Gap.findAroundObject(timeline_object4)) left_gap = gap_finder.left_gap right_gap = gap_finder.right_gap self.failUnlessEqual(left_gap, invalid_gap) self.failUnlessEqual(right_gap.left_object, timeline_object4) self.failUnlessEqual(right_gap.right_object, None) self.failUnlessEqual(right_gap.start, 60 * gst.SECOND) self.failUnlessEqual(right_gap.duration, infinity) gap_finder = SmallestGapsFinder(set([timeline_object3])) gap_finder.update(*Gap.findAroundObject(timeline_object3)) left_gap = gap_finder.left_gap right_gap = gap_finder.right_gap self.failUnlessEqual(left_gap.left_object, None) self.failUnlessEqual(left_gap.right_object, timeline_object3) self.failUnlessEqual(left_gap.start, 0 * gst.SECOND) self.failUnlessEqual(left_gap.duration, 31 * gst.SECOND) self.failUnlessEqual(right_gap, invalid_gap) def testFindAllGaps(self): simple = ((3 * gst.SECOND, 1 * gst.SECOND), (1 * gst.SECOND, 1 * gst.SECOND)) objs = [] for start, duration in simple: obj = self.makeTimelineObject() obj.start = start obj.duration = duration objs.append(obj) result = [(g.start, g.initial_duration) for g in Gap.findAllGaps(objs)] self.assertEquals(result, [ (0 * gst.SECOND, 1 * gst.SECOND), (2 * gst.SECOND, 1 * gst.SECOND), ]) complex = [ (1 * gst.SECOND, 2 * gst.SECOND), (6 * gst.SECOND, 2 * gst.SECOND), (10 * gst.SECOND, 2 * gst.SECOND), (8 * gst.SECOND, 2 * gst.SECOND), (14 * gst.SECOND, 1 * gst.SECOND), (4 * gst.SECOND, 1 * gst.SECOND), ] objs = [] for start, duration in complex: obj = self.makeTimelineObject() obj.start = start obj.duration = duration objs.append(obj) result = [(g.start, g.initial_duration) for g in Gap.findAllGaps(objs)] self.assertEquals(result, [ (0 * gst.SECOND, 1 * gst.SECOND), (3 * gst.SECOND, 1 * gst.SECOND), (5 * gst.SECOND, 1 * gst.SECOND), (12 * gst.SECOND, 2 * gst.SECOND), ]) complex.append((2 * gst.SECOND, 5 * gst.SECOND)) objs = [] for start, duration in complex: obj = self.makeTimelineObject() obj.start = start obj.duration = duration objs.append(obj) result = [(g.start, g.initial_duration) for g in Gap.findAllGaps(objs)] self.assertEquals(result, [ (0 * gst.SECOND, 1 * gst.SECOND), (12 * gst.SECOND, 2 * gst.SECOND), ])