Skip to content

Commit

Permalink
Issue 912: cmx_3600 adapter incorrectly processing three-clip transit…
Browse files Browse the repository at this point in the history
…ions (#919)

* Issue #912 - Test for dissolve issue with cmx_3600 adapter
* Issue #912 - Change cmx_3600 adapter for A->B->C Transitions"
* Update src/py-opentimelineio/opentimelineio/adapters/cmx_3600.py

Co-authored-by: jburnell <[email protected]>
Co-authored-by: Stephan Steinbach <[email protected]>
  • Loading branch information
3 people authored Mar 25, 2021
1 parent d25cd13 commit 5aeaedd
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 8 deletions.
20 changes: 12 additions & 8 deletions src/py-opentimelineio/opentimelineio/adapters/cmx_3600.py
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,16 @@ def parse(self, comment):
self.unhandled.append(stripped)


def _get_next_clip(start_index, track):
"""Get the next clip with a non-zero duration"""
# Iterate over the following clips and return the first "real" one
for clip in track[start_index + 1:]:
if clip.duration().value > 0:
return clip

return None


def _expand_transitions(timeline):
"""Convert clips with metadata/transition == 'D' into OTIO transitions."""

Expand All @@ -635,13 +645,10 @@ def _expand_transitions(timeline):
replace_or_insert_list = []
append_list = []
for track in tracks:
track_iter = iter(track)
# avid inserts an extra clip for the source
prev_prev = None
prev = None
clip = next(track_iter, None)
next_clip = next(track_iter, None)
while clip is not None:
for index, clip in enumerate(track):
transition_type = clip.metadata.get('cmx_3600', {}).get(
'transition',
'C'
Expand All @@ -651,8 +658,6 @@ def _expand_transitions(timeline):
# nothing to do, continue to the next iteration of the loop
prev_prev = prev
prev = clip
clip = next_clip
next_clip = next(track_iter, None)
continue

wipe_match = re.match(r'W(\d{3})', transition_type)
Expand Down Expand Up @@ -730,6 +735,7 @@ def _expand_transitions(timeline):

# expand the next_clip or contract this clip
keep_transition_clip = False
next_clip = _get_next_clip(index, track)
if next_clip:
if _transition_clips_continuous(clip, next_clip):
sr = next_clip.source_range
Expand Down Expand Up @@ -771,8 +777,6 @@ def _expand_transitions(timeline):
del(new_trx.metadata['previous_metadata'])

prev = clip
clip = next_clip
next_clip = next(track_iter, None)

for (insert, track, from_clip, to_transition) in replace_or_insert_list:
clip_index = track.index(from_clip)
Expand Down
12 changes: 12 additions & 0 deletions tests/sample_data/dissolve_test_4.edl
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
TITLE: TRANSITION_TEST_2
FCM: NON-DROP FRAME
001 ABC0000. V C 01:00:06:18 01:00:08:00 01:04:11:17 01:04:12:23
002 ABC0010. V C 01:00:06:15 01:00:08:18 01:04:12:23 01:04:15:02
003 ABC0010. V C 01:00:08:18 01:00:08:18 01:04:15:02 01:04:15:02
003 ABC0020. V D 035 01:00:06:22 01:00:10:07 01:04:15:02 01:04:18:11
* BLEND, DISSOLVE
004 ABC0020. V C 01:00:10:07 01:00:10:07 01:04:18:11 01:04:18:11
004 ABC0030. V D 064 01:00:06:10 01:00:09:22 01:04:18:11 01:04:21:23
* BLEND, DISSOLVE
005 ABC0040. V C 01:00:08:14 01:00:12:14 01:04:21:23 01:04:25:23
060 ABC0050. V C 01:00:12:06 01:00:17:21 01:04:25:23 01:04:31:14
21 changes: 21 additions & 0 deletions tests/test_cmx_3600_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
DISSOLVE_TEST = os.path.join(SAMPLE_DATA_DIR, "dissolve_test.edl")
DISSOLVE_TEST_2 = os.path.join(SAMPLE_DATA_DIR, "dissolve_test_2.edl")
DISSOLVE_TEST_3 = os.path.join(SAMPLE_DATA_DIR, "dissolve_test_3.edl")
DISSOLVE_TEST_4 = os.path.join(SAMPLE_DATA_DIR, "dissolve_test_4.edl")
GAP_TEST = os.path.join(SAMPLE_DATA_DIR, "gap_test.edl")
WIPE_TEST = os.path.join(SAMPLE_DATA_DIR, "wipe_test.edl")
TIMECODE_MISMATCH_TEST = os.path.join(SAMPLE_DATA_DIR, "timecode_mismatch.edl")
Expand Down Expand Up @@ -1047,6 +1048,26 @@ def test_transition_duration(self):

self.assertEqual(tl.tracks[0][2].duration().value, 26.0)

def test_three_part_transition(self):
"""
Test A->B->C Transition
"""
tl = otio.adapters.read_from_file(DISSOLVE_TEST_4)
self.assertEqual(len(tl.tracks[0]), 8)

self.assertIsInstance(tl.tracks[0][2], otio.schema.Transition)
self.assertIsInstance(tl.tracks[0][4], otio.schema.Transition)

self.assertEqual(tl.tracks[0][2].duration().value, 35.0)
self.assertEqual(tl.tracks[0][4].duration().value, 64.0)

self.assertEqual(tl.tracks[0][0].duration().value, 30.0)
self.assertEqual(tl.tracks[0][1].duration().value, 68.0)
self.assertEqual(tl.tracks[0][3].duration().value, 96.0)
self.assertEqual(tl.tracks[0][5].duration().value, 52.0)
self.assertEqual(tl.tracks[0][6].duration().value, 96.0)
self.assertEqual(tl.tracks[0][7].duration().value, 135.0)


if __name__ == "__main__":
unittest.main()

0 comments on commit 5aeaedd

Please sign in to comment.