Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
303 changes: 224 additions & 79 deletions contrib/opentimelineio_contrib/adapters/advanced_authoring_format.py

Large diffs are not rendered by default.

Binary file not shown.
Binary file not shown.
Binary file not shown.
105 changes: 105 additions & 0 deletions contrib/opentimelineio_contrib/adapters/tests/test_aaf_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,23 @@
"composite.aaf"
)

SUBCLIP_PATH = os.path.join(
SAMPLE_DATA_DIR,
"subclip_sourceclip_references_compositionmob_with_mastermob.aaf"
)

COMPOSITION_METADATA_MASTERMOB_METADATA_PATH = os.path.join(
SAMPLE_DATA_DIR,
"normalclip_sourceclip_references_compositionmob_"
"has_also_mastermob_usercomments.aaf"
)

COMPOSITION_METADATA_PATH = os.path.join(
SAMPLE_DATA_DIR,
"normalclip_sourceclip_references_compositionmob_"
"with_usercomments_no_mastermob_usercomments.aaf"
)


def safe_str(maybe_str):
"""To help with testing between python 2 and 3, this function attempts to
Expand Down Expand Up @@ -884,6 +901,94 @@ def test_external_reference_paths(self):
self.assertEqual(clip.media_reference.target_url,
audio_target_urls[track_index][clip_index])

def test_aaf_subclip_metadata(self):
"""
For subclips, the AAF SourceClip can actually reference a CompositionMob
(instead of a MasterMob)
In which case we need to drill down into the CompositionMob
to find the MasterMob with the UserComments.
"""

timeline = otio.adapters.read_from_file(SUBCLIP_PATH)
audio_track = timeline.audio_tracks()[0]
first_clip = audio_track[0]

aaf_metadata = first_clip.media_reference.metadata.get("AAF")

expected_md = {"Director": "director_name",
"Line": "script_line",
"Talent": "Speaker",
"Logger": "logger",
"Character": "character_name"}

self._verify_user_comments(aaf_metadata, expected_md)

def test_aaf_composition_metadata(self):
"""
For standard clips the AAF SourceClip can actually reference a
CompositionMob (instead of a MasterMob) and the composition mob is holding the
UserComments instead of the MasterMob.
My guess is that the CompositionMob is used to share the same metadata
between different SourceClips
"""

timeline = otio.adapters.read_from_file(COMPOSITION_METADATA_PATH)

audio_track = timeline.audio_tracks()[0]
first_clip = audio_track[0]

aaf_metadata = first_clip.media_reference.metadata.get("AAF")

expected_md = {"Director": "director",
"Line": "scriptline",
"Talent": "talent",
"Logger": "",
"Character": "character"}

self._verify_user_comments(aaf_metadata, expected_md)

def test_aaf_composition_metadata_mastermob(self):
"""
For standard clips the AAF SourceClip can actually reference a
CompositionMob (instead of a masterMob), the CompositionMob is holding
UserComments AND the MasterMob is holding UserComments.
In this case the masterMob has the valid UserComments (empirically determined)
"""

timeline = otio.adapters.read_from_file(
COMPOSITION_METADATA_MASTERMOB_METADATA_PATH)

audio_track = timeline.audio_tracks()[0]
first_clip = audio_track[0]

aaf_metadata = first_clip.metadata.get("AAF")

expected_md = {"Director": "director",
"Line": "scriptline",
"Talent": "talent",
"Logger": "logger",
"Character": "character"}

self._verify_user_comments(aaf_metadata, expected_md)

def test_aaf_transcribe_log(self):

# Excercise an aaf-adapter read with transcribe_logging enabled,
# for coverage purposes, result is ignored.
otio.adapters.read_from_file(SUBCLIP_PATH, transcribe_log=True)

def _verify_user_comments(self, aaf_metadata, expected_md):

self.assertTrue(aaf_metadata is not None)
self.assertTrue("UserComments" in aaf_metadata.keys())

user_comments = aaf_metadata['UserComments']

user_comment_keys = user_comments.keys()
for k, v in expected_md.items():
self.assertTrue(k in user_comment_keys)
self.assertEqual(user_comments[k], v)


class AAFWriterTests(unittest.TestCase):
def test_aaf_writer_gaps(self):
Expand Down
1 change: 1 addition & 0 deletions docs/tutorials/otio-plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ Depending on if/where PyAAF is installed, you may need to set this env var:
- read_from_file:
- filepath
- simplify
- transcribe_log
- write_to_file:
- input_otio
- filepath
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@ def test_otio():

install_requires=(
[
'pyaaf2==1.2.0',
'pyaaf2==1.4.0',
]
),
entry_points={
Expand Down