Skip to content

Commit

Permalink
Merge pull request #51 from timlehr/mobAttributeList_tlehr
Browse files Browse the repository at this point in the history
Writer: added support for mob attribute list
  • Loading branch information
meshula authored Oct 15, 2024
2 parents d179bd6 + 319bd04 commit 61a77c5
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 3 deletions.
25 changes: 22 additions & 3 deletions src/otio_aaf_adapter/adapters/aaf_adapter/aaf_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ def __init__(self, input_otio, aaf_file, **kwargs):

# transcribe timeline comments onto composition mob
self._transcribe_user_comments(input_otio, self.compositionmob)
self._transcribe_mob_attributes(input_otio, self.compositionmob)

def _unique_mastermob(self, otio_clip):
"""Get a unique mastermob, identified by clip metadata mob id."""
Expand All @@ -126,12 +127,14 @@ def _unique_mastermob(self, otio_clip):
self.aaf_file.content.mobs.append(mastermob)
self._unique_mastermobs[mob_id] = mastermob

# transcribe clip comments onto master mob
# transcribe clip comments / mob attributes onto master mob
self._transcribe_user_comments(otio_clip, mastermob)
self._transcribe_mob_attributes(otio_clip, mastermob)

# transcribe media reference comments onto master mob.
# this might overwrite clip comments.
# transcribe media reference comments / mob attributes onto master mob.
# this might overwrite clip comments / attributes.
self._transcribe_user_comments(otio_clip.media_reference, mastermob)
self._transcribe_mob_attributes(otio_clip.media_reference, mastermob)

return mastermob

Expand Down Expand Up @@ -226,6 +229,22 @@ def _transcribe_user_comments(self, otio_item, target_mob):
f"'{type(val)}' for key '{key}'."
)

def _transcribe_mob_attributes(self, otio_item, target_mob):
"""Transcribes mob attribute list onto the `target_mob`.
This can be used to roundtrip specific mob config values, like audio channel
settings.
"""
mob_attr_map = otio_item.metadata.get("AAF", {}).get("MobAttributeList", {})
mob_attr_list = aaf2.misc.TaggedValueHelper(target_mob['MobAttributeList'])
for key, val in mob_attr_map.items():
if isinstance(val, (int, str)):
mob_attr_list[key] = val
elif isinstance(val, (float, Rational)):
mob_attr_list[key] = aaf2.rational.AAFRational(val)
else:
raise ValueError(f"Unsupported mob attribute type '{type(val)}' for "
f"key '{key}'.")


def validate_metadata(timeline):
"""Print a check of necessary metadata requirements for an otio timeline."""
Expand Down
24 changes: 24 additions & 0 deletions tests/test_aaf_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -1906,6 +1906,30 @@ def test_aaf_writer_user_comments(self):
self.assertEqual(dict(master_mob.comments.items()), expected_comments)
self.assertEqual(dict(comp_mob.comments.items()), expected_comments)

def test_aaf_writer_metadata_roundtrip(self):
"""Tries to roundtrip metadata through AAF and `MobAttributeList`."""
og_aaf_tl = otio.adapters.read_from_file(ONE_AUDIO_CLIP_PATH)
clip = og_aaf_tl.find_clips()[0]

# change a value to test roundtrip
clip.media_reference.metadata["AAF"]["MobAttributeList"]["_USER_POS"] = 2
_, tmp_aaf_path = tempfile.mkstemp(suffix='.aaf')
otio.adapters.write_to_file(og_aaf_tl, tmp_aaf_path)

roundtripped_tl = otio.adapters.read_from_file(tmp_aaf_path)

clip = roundtripped_tl.find_clips()[0]
expected = {
"_IMPORTSETTING": "__AttributeList",
"_SAVED_AAF_AUDIO_LENGTH": 0,
"_SAVED_AAF_AUDIO_RATE_DEN": 1,
"_SAVED_AAF_AUDIO_RATE_NUM": 24,
"_USER_POS": 2,
"_VERSION": 2
}
self.assertEqual(clip.media_reference.metadata["AAF"]["MobAttributeList"],
expected)

def test_aaf_writer_global_start_time(self):
for tc, rate in [("01:00:00:00", 23.97),
("01:00:00:00", 24),
Expand Down

0 comments on commit 61a77c5

Please sign in to comment.