Permalink
Browse files

Support custom fps in cmx3600 adapter (#187)

* Added a rate parameter to the cmx3600 EDL adapter's read and write functions.
  • Loading branch information...
tuan-huy-truong authored and jminor committed Nov 20, 2017
1 parent d0ee896 commit 774324aa822685b067adc7076eba74a39208995c
Showing with 39 additions and 12 deletions.
  1. +12 −12 opentimelineio/adapters/cmx_3600.py
  2. +15 −0 tests/sample_data/25fps.edl
  3. +12 −0 tests/test_cmx_3600_adapter.py
@@ -72,20 +72,20 @@ class EDLParseError(otio.exceptions.OTIOError):
class EDLParser(object):
def __init__(self, edl_string):
def __init__(self, edl_string, rate=24):
self.timeline = otio.schema.Timeline()
# Start with no tracks. They will be added as we encounter them.
# This dict maps a track name (e.g "A2" or "V") to an OTIO Track.
self.tracks_by_name = {}
self.parse_edl(edl_string)
self.parse_edl(edl_string, rate=rate)
# TODO: Sort the tracks V, then A1,A2,etc.
def add_clip(self, line, comments):
def add_clip(self, line, comments, rate=24):
comment_handler = CommentHandler(comments)
clip_handler = ClipHandler(line, comment_handler.handled)
clip_handler = ClipHandler(line, comment_handler.handled, rate=rate)
if comment_handler.unhandled:
clip_handler.clip.metadata.setdefault("cmx_3600", {})
clip_handler.clip.metadata['cmx_3600'].setdefault("comments", [])
@@ -138,7 +138,7 @@ def add_transition(self, clip_handler, transition, data):
md = clip_handler.clip.metadata.setdefault("cmx_3600", {})
md["transition"] = transition
def parse_edl(self, edl_string):
def parse_edl(self, edl_string, rate=24):
# edl 'events' can be comprised of an indeterminate amount of lines
# we are to translating 'events' to a single clip and transition
# then we add the transition and the clip to all channels the 'event'
@@ -214,19 +214,19 @@ def parse_edl(self, edl_string):
else:
break
self.add_clip(line, comments)
self.add_clip(line, comments, rate=rate)
else:
raise EDLParseError('Unknown event type')
class ClipHandler(object):
def __init__(self, line, comment_data):
def __init__(self, line, comment_data, rate=24):
self.clip_num = None
self.reel = None
self.channel_code = None
self.edl_rate = 24
self.edl_rate = rate
self.transition_id = None
self.transition_data = None
self.source_tc_in = None
@@ -544,14 +544,14 @@ def expand_transitions(timeline):
return timeline
def read_from_string(input_str):
parser = EDLParser(input_str)
def read_from_string(input_str, rate=24):
parser = EDLParser(input_str, rate=rate)
result = parser.timeline
result = expand_transitions(result)
return result
def write_to_string(input_otio):
def write_to_string(input_otio, rate=None):
# TODO: We should have convenience functions in Timeline for this?
# also only works for a single video track at the moment
video_tracks = [t for t in input_otio.tracks
@@ -580,12 +580,12 @@ def write_to_string(input_otio):
lines.append("TITLE: {}".format(input_otio.name))
# TODO: We should try to detect the frame rate and output an
# appropriate "FCM: NON-DROP FRAME" etc here.
edl_rate = 24
lines.append("")
edit_number = 1
track = input_otio.tracks[0]
edl_rate = rate or track.duration().rate
for i, clip in enumerate(track):
source_tc_in = otio.opentime.to_timecode(
clip.source_range.start_time,
@@ -0,0 +1,15 @@
TITLE: SQ0002_A2
FCM: NON-DROP FRAME
001 SC00.JPG V C 01:00:00:00 01:00:06:11 00:00:00:00 00:00:06:11
* FROM CLIP NAME: sq001_s001
* sq001_s001
002 SC00.JPG V C 01:00:00:00 01:00:08:00 00:00:06:11 00:00:14:11
* FROM CLIP NAME: sq001_s002
* sq001_s002
003 SC00.JPG V C 01:00:00:00 01:00:03:11 00:00:14:11 00:00:17:22
* FROM CLIP NAME: sq001_s003
* sq001_s003
004 SC00.JPG V C 01:00:00:00 01:00:01:24 00:00:17:22 00:00:19:21
* FROM CLIP NAME: sq001_s004
* sq001_s004
@@ -34,6 +34,7 @@
SAMPLE_DATA_DIR = os.path.join(os.path.dirname(__file__), "sample_data")
SCREENING_EXAMPLE_PATH = os.path.join(SAMPLE_DATA_DIR, "screening_example.edl")
EXEMPLE_25_FPS_PATH = os.path.join(SAMPLE_DATA_DIR, "25fps.edl")
NO_SPACES_PATH = os.path.join(SAMPLE_DATA_DIR, "no_spaces_test.edl")
DISSOLVE_TEST = os.path.join(SAMPLE_DATA_DIR, "dissolve_test.edl")
DISSOLVE_TEST_2 = os.path.join(SAMPLE_DATA_DIR, "dissolve_test_2.edl")
@@ -293,6 +294,17 @@ def test_fade_to_black_ends_with_gap(self):
self.assertEqual(tl.tracks[0][2].duration().value, 12)
self.assertEqual(tl.tracks[0][2].source_range.start_time.value, 0)
def test_edl_25fps(self):
# EXERCISE
edl_path = EXEMPLE_25_FPS_PATH
fps = 25
timeline = otio.adapters.read_from_file(edl_path, rate=fps)
track = timeline.tracks[0]
self.assertEqual(track[0].source_range.duration.value, 161)
self.assertEqual(track[1].source_range.duration.value, 200)
self.assertEqual(track[2].source_range.duration.value, 86)
self.assertEqual(track[3].source_range.duration.value, 49)
if __name__ == '__main__':
unittest.main()

0 comments on commit 774324a

Please sign in to comment.