Skip to content

Commit 0303169

Browse files
authored
Fixed issue where CMX3600 adapter would try to add the same clip to multiple tracks. Also moved some code out of a loop it didn't need to be in. (#644)
1 parent 1deda32 commit 0303169

File tree

3 files changed

+85
-54
lines changed

3 files changed

+85
-54
lines changed

src/py-opentimelineio/opentimelineio/adapters/cmx_3600.py

Lines changed: 60 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
# TODO: currently tracks with linked audio/video will lose their linkage when
3434
# read into OTIO.
3535

36+
import copy
3637
import os
3738
import re
3839
import math
@@ -137,62 +138,67 @@ def add_clip(self, line, comments, rate=24):
137138
clip_handler.transition_data
138139
)
139140

140-
tracks = self.tracks_for_channel(clip_handler.channel_code)
141-
for track in tracks:
141+
edl_rate = clip_handler.edl_rate
142+
record_in = opentime.from_timecode(
143+
clip_handler.record_tc_in,
144+
edl_rate
145+
)
146+
record_out = opentime.from_timecode(
147+
clip_handler.record_tc_out,
148+
edl_rate
149+
)
142150

143-
edl_rate = clip_handler.edl_rate
144-
record_in = opentime.from_timecode(
145-
clip_handler.record_tc_in,
146-
edl_rate
147-
)
148-
record_out = opentime.from_timecode(
149-
clip_handler.record_tc_out,
150-
edl_rate
151-
)
151+
src_duration = clip.duration()
152+
rec_duration = record_out - record_in
153+
if rec_duration != src_duration:
154+
motion = comment_handler.handled.get('motion_effect')
155+
freeze = comment_handler.handled.get('freeze_frame')
156+
if motion is not None or freeze is not None:
157+
# Adjust the clip to match the record duration
158+
clip.source_range = opentime.TimeRange(
159+
start_time=clip.source_range.start_time,
160+
duration=rec_duration
161+
)
152162

153-
src_duration = clip.duration()
154-
rec_duration = record_out - record_in
155-
if rec_duration != src_duration:
156-
motion = comment_handler.handled.get('motion_effect')
157-
freeze = comment_handler.handled.get('freeze_frame')
158-
if motion is not None or freeze is not None:
159-
# Adjust the clip to match the record duration
160-
clip.source_range = opentime.TimeRange(
161-
start_time=clip.source_range.start_time,
162-
duration=rec_duration
163+
if freeze is not None:
164+
clip.effects.append(schema.FreezeFrame())
165+
# XXX remove 'FF' suffix (writing edl will add it back)
166+
if clip.name.endswith(' FF'):
167+
clip.name = clip.name[:-3]
168+
elif motion is not None:
169+
fps = float(
170+
SPEED_EFFECT_RE.match(motion).group("speed")
171+
)
172+
time_scalar = fps / rate
173+
clip.effects.append(
174+
schema.LinearTimeWarp(time_scalar=time_scalar)
163175
)
164176

165-
if freeze is not None:
166-
clip.effects.append(schema.FreezeFrame())
167-
# XXX remove 'FF' suffix (writing edl will add it back)
168-
if clip.name.endswith(' FF'):
169-
clip.name = clip.name[:-3]
170-
elif motion is not None:
171-
fps = float(
172-
SPEED_EFFECT_RE.match(motion).group("speed")
173-
)
174-
time_scalar = fps / rate
175-
clip.effects.append(
176-
schema.LinearTimeWarp(time_scalar=time_scalar)
177-
)
178-
179-
elif self.ignore_timecode_mismatch:
180-
# Pretend there was no problem by adjusting the record_out.
181-
# Note that we don't actually use record_out after this
182-
# point in the code, since all of the subsequent math uses
183-
# the clip's source_range. Adjusting the record_out is
184-
# just to document what the implications of ignoring the
185-
# mismatch here entails.
186-
record_out = record_in + src_duration
177+
elif self.ignore_timecode_mismatch:
178+
# Pretend there was no problem by adjusting the record_out.
179+
# Note that we don't actually use record_out after this
180+
# point in the code, since all of the subsequent math uses
181+
# the clip's source_range. Adjusting the record_out is
182+
# just to document what the implications of ignoring the
183+
# mismatch here entails.
184+
record_out = record_in + src_duration
187185

188-
else:
189-
raise EDLParseError(
190-
"Source and record duration don't match: {} != {}"
191-
" for clip {}".format(
192-
src_duration,
193-
rec_duration,
194-
clip.name
195-
))
186+
else:
187+
raise EDLParseError(
188+
"Source and record duration don't match: {} != {}"
189+
" for clip {}".format(
190+
src_duration,
191+
rec_duration,
192+
clip.name
193+
))
194+
195+
# Add clip instances to the tracks
196+
tracks = self.tracks_for_channel(clip_handler.channel_code)
197+
for track in tracks:
198+
if len(tracks) > 1:
199+
track_clip = copy.deepcopy(clip)
200+
else:
201+
track_clip = clip
196202

197203
if track.source_range is None:
198204
zero = opentime.RationalTime(0, edl_rate)
@@ -211,7 +217,7 @@ def add_clip(self, line, comments, rate=24):
211217
raise EDLParseError(
212218
"Overlapping record in value: {} for clip {}".format(
213219
clip_handler.record_tc_in,
214-
clip.name
220+
track_clip.name
215221
))
216222

217223
# If the next clip is supposed to start beyond the end of the
@@ -228,8 +234,8 @@ def add_clip(self, line, comments, rate=24):
228234
track.append(gap)
229235
_extend_source_range_duration(track, gap.duration())
230236

231-
track.append(clip)
232-
_extend_source_range_duration(track, clip.duration())
237+
track.append(track_clip)
238+
_extend_source_range_duration(track, track_clip.duration())
233239

234240
def guess_kind_for_track_name(self, name):
235241
if name.startswith("V"):

tests/sample_data/multi_audio.edl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
TITLE: MultiAudio
2+
FCM: NON-DROP FRAME
3+
4+
001 AX AA C 00:00:00:00 00:56:55:22 00:00:00:00 00:56:55:22
5+
* FROM CLIP NAME: AX
6+
AUD 3

tests/test_cmx_3600_adapter.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
SAMPLE_DATA_DIR,
4949
"speed_effects_small.edl"
5050
)
51+
MULTIPLE_TARGET_AUDIO_PATH = os.path.join(SAMPLE_DATA_DIR, "multi_audio.edl")
5152

5253

5354
class EDLAdapterTest(unittest.TestCase, otio_test_utils.OTIOAssertions):
@@ -812,6 +813,24 @@ def test_nucoda_edl_write_with_double_transition(self):
812813

813814
self.assertMultiLineEqual(result, expected)
814815

816+
def test_read_edl_with_multiple_target_audio_tracks(self):
817+
tl = otio.adapters.read_from_file(MULTIPLE_TARGET_AUDIO_PATH)
818+
819+
self.assertEqual(len(tl.audio_tracks()), 2)
820+
821+
first_track, second_track = tl.audio_tracks()
822+
self.assertEqual(first_track.name, "A1")
823+
self.assertEqual(second_track.name, "A2")
824+
825+
self.assertEqual(first_track[0].name, "AX")
826+
self.assertEqual(second_track[0].name, "AX")
827+
828+
expected_range = otio.opentime.TimeRange(
829+
duration=otio.opentime.from_timecode("00:56:55:22", rate=24)
830+
)
831+
self.assertEqual(first_track[0].source_range, expected_range)
832+
self.assertEqual(second_track[0].source_range, expected_range)
833+
815834
def test_custom_reel_names(self):
816835
track = otio.schema.Track()
817836
tl = otio.schema.Timeline(tracks=[track])

0 commit comments

Comments
 (0)