Skip to content
Permalink
Browse files

AAF reader: global_start_timecode fixes (#558)

* AAF global_start_timecode fixes including:
1. Added code to look for LegacyTimecode (Premiere AAFs) in addition to Timecode
2. Updated to support fps fractions
3. Timecode candidate selected for global_start_timecode is now selected using PhysicalTrackNumber
* Fixed AAF test for python 2.7 integer floor division
* Adjusted from x import y imports so that they retain module namespacing
  • Loading branch information...
reinecke authored and ssteinbach committed Aug 15, 2019
1 parent faa5827 commit 36f85f78ca14b2d60b598164e9e21e50f609a1d0
@@ -32,7 +32,8 @@
import sys
import numbers
import copy
from collections import Iterable
import collections
import fractions
import opentimelineio as otio

lib_path = os.environ.get("OTIO_AAF_PYTHON_LIB")
@@ -51,6 +52,10 @@
__names = set()


class AAFAdapterError(otio.exceptions.OTIOError):
""" Raised for AAF adatper-specific errors. """


def _get_parameter(item, parameter_name):
values = dict((value.name, value) for value in item.parameters.value)
return values.get(parameter_name)
@@ -465,7 +470,7 @@ def _transcribe(item, parents, editRate, masterMobs):
# elif isinstance(item, pyaaf.AxProperty):
# self.properties['Value'] = str(item.GetValue())

elif isinstance(item, Iterable):
elif isinstance(item, collections.Iterable):
result = otio.schema.SerializableCollection()
for child in item:
result.append(
@@ -506,7 +511,7 @@ def _transcribe(item, parents, editRate, masterMobs):
and result.source_range is not None
and result.source_range.duration.value != length
):
raise otio.exceptions.OTIOError(
raise AAFAdapterError(
"Wrong duration? {} should be {} in {}".format(
result.source_range.duration.value,
length,
@@ -537,22 +542,36 @@ def _find_timecode_track_start(track):
aaf_metadata = track.metadata.get("AAF", {})

# Is this a Timecode track?
if aaf_metadata.get("MediaKind") == "Timecode":
edit_rate = aaf_metadata.get("EditRate", "0")
fps = aaf_metadata.get("Segment", {}).get("FPS", 0)
start = aaf_metadata.get("Segment", {}).get("Start", "0")

# Often times there are several timecode tracks, so
# we use a heuristic to only pay attention to Timecode
# tracks with a FPS that matches the edit rate.
if edit_rate == str(fps):
return otio.opentime.RationalTime(
value=int(start),
rate=float(edit_rate)
)
if aaf_metadata.get("MediaKind") not in {"Timecode", "LegacyTimecode"}:
return

# Edit Protocol section 3.6 specifies PhysicalTrackNumber of 1 as the
# Primary timecode
try:
physical_track_number = aaf_metadata["PhysicalTrackNumber"]
except KeyError:
raise AAFAdapterError("Timecode missing 'PhysicalTrackNumber'")

if physical_track_number != 1:
return

try:
edit_rate = fractions.Fraction(aaf_metadata["EditRate"])
start = aaf_metadata["Segment"]["Start"]
except KeyError as e:
raise AAFAdapterError(
"Timecode missing '{}'".format(e)
)

# We didn't find anything useful
return None
if edit_rate.denominator == 1:
rate = edit_rate.numerator
else:
rate = float(edit_rate)

return otio.opentime.RationalTime(
value=int(start),
rate=rate,
)


def _transcribe_linear_timewarp(item, parameters):
Binary file not shown.
@@ -106,6 +106,10 @@
SAMPLE_DATA_DIR,
"2997fps.aaf"
)
FPS2997_DFTC_PATH = os.path.join(
SAMPLE_DATA_DIR,
"2997fps-DFTC.aaf"
)
DUPLICATES_PATH = os.path.join(
SAMPLE_DATA_DIR,
"duplicates.aaf"
@@ -231,6 +235,13 @@ def test_aaf_global_start_time(self):
timeline.global_start_time
)

def test_aaf_global_start_time_NTSC_DFTC(self):
timeline = otio.adapters.read_from_file(FPS2997_DFTC_PATH)
self.assertEqual(
otio.opentime.from_timecode("05:00:00;00", rate=(30000.0 / 1001)),
timeline.global_start_time
)

def test_aaf_read_trims(self):
aaf_path = TRIMS_EXAMPLE_PATH
timeline = otio.adapters.read_from_file(aaf_path)

0 comments on commit 36f85f7

Please sign in to comment.
You can’t perform that action at this time.