Skip to content

Commit 50f8258

Browse files
timlehrjminor
authored andcommitted
AAF adapter: added support for keyframed properties (#1151)
Keyframed properties are now included in AAF metadata and translated as a dictionary containing control points, interpolation type and optionally baked values.
1 parent 3966b31 commit 50f8258

File tree

4 files changed

+412
-11
lines changed

4 files changed

+412
-11
lines changed

contrib/opentimelineio_contrib/adapters/advanced_authoring_format.py

Lines changed: 76 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
import aaf2.mobs # noqa: E402
5858
import aaf2.components # noqa: E402
5959
import aaf2.core # noqa: E402
60+
import aaf2.misc # noqa: E402
6061
from opentimelineio_contrib.adapters.aaf_adapter import aaf_writer # noqa: E402
6162

6263

@@ -65,6 +66,16 @@
6566
# If enabled, output recursive traversal info of _transcribe() method.
6667
_TRANSCRIBE_DEBUG = False
6768

69+
# bake keyframed parameter
70+
_BAKE_KEYFRAMED_PROPERTIES_VALUES = False
71+
72+
_PROPERTY_INTERPOLATION_MAP = {
73+
aaf2.misc.ConstantInterp: "Constant",
74+
aaf2.misc.LinearInterp: "Linear",
75+
aaf2.misc.BezierInterpolator: "Bezier",
76+
aaf2.misc.CubicInterpolator: "Cubic",
77+
}
78+
6879

6980
def _transcribe_log(s, indent=0, always_print=False):
7081
if always_print or _TRANSCRIBE_DEBUG:
@@ -108,7 +119,7 @@ def _get_class_name(item):
108119
return item.__class__.__name__
109120

110121

111-
def _transcribe_property(prop):
122+
def _transcribe_property(prop, owner=None):
112123
# XXX: The unicode type doesn't exist in Python 3 (all strings are unicode)
113124
# so we have to use type(u"") which works in both Python 2 and 3.
114125
if isinstance(prop, (str, type(u""), numbers.Integral, float, dict)):
@@ -118,19 +129,66 @@ def _transcribe_property(prop):
118129
elif isinstance(prop, list):
119130
result = {}
120131
for child in prop:
121-
if hasattr(child, "name") and hasattr(child, "value"):
122-
result[child.name] = _transcribe_property(child.value)
132+
if hasattr(child, "name"):
133+
if isinstance(child, aaf2.misc.VaryingValue):
134+
# keyframed values
135+
control_points = []
136+
for control_point in child["PointList"]:
137+
try:
138+
# Some values cannot be transcribed yet
139+
control_points.append(
140+
[
141+
control_point.time,
142+
_transcribe_property(control_point.value),
143+
]
144+
)
145+
except TypeError:
146+
_transcribe_log(
147+
"Unable to transcribe value for property: "
148+
"'{}' (Type: '{}', Parent: '{}')".format(
149+
child.name, type(child), prop
150+
)
151+
)
152+
153+
# bake keyframe values for owner time range
154+
baked_values = None
155+
if _BAKE_KEYFRAMED_PROPERTIES_VALUES:
156+
if isinstance(owner, aaf2.components.Component):
157+
baked_values = []
158+
for t in range(0, owner.length):
159+
baked_values.append([t, child.value_at(t)])
160+
else:
161+
_transcribe_log(
162+
"Unable to bake values for property: "
163+
"'{}'. Owner: {}, Control Points: {}".format(
164+
child.name, owner, control_points
165+
)
166+
)
167+
168+
value_dict = {
169+
"_aaf_keyframed_property": True,
170+
"keyframe_values": control_points,
171+
"keyframe_interpolation": _PROPERTY_INTERPOLATION_MAP.get(
172+
child.interpolationdef.auid, "Linear"
173+
),
174+
"keyframe_baked_values": baked_values
175+
}
176+
result[child.name] = value_dict
177+
178+
elif hasattr(child, "value"):
179+
# static value
180+
result[child.name] = _transcribe_property(child.value, owner=owner)
123181
else:
124182
# @TODO: There may be more properties that we might want also.
125183
# If you want to see what is being skipped, turn on debug.
126184
if debug:
127-
debug_message = "Skipping unrecognized property: {} of parent {}"
128-
print(debug_message.format(child, prop))
185+
debug_message = "Skipping unrecognized property: '{}', parent '{}'"
186+
_transcribe_log(debug_message.format(child, prop))
129187
return result
130188
elif hasattr(prop, "properties"):
131189
result = {}
132190
for child in prop.properties():
133-
result[child.name] = _transcribe_property(child.value)
191+
result[child.name] = _transcribe_property(child.value, owner=owner)
134192
return result
135193
else:
136194
return str(prop)
@@ -364,7 +422,7 @@ def _transcribe(item, parents, edit_rate, indent=0):
364422
if hasattr(prop, 'name') and hasattr(prop, 'value'):
365423
key = str(prop.name)
366424
value = prop.value
367-
metadata[key] = _transcribe_property(value)
425+
metadata[key] = _transcribe_property(value, owner=item)
368426

369427
# Now we will use the item's class to determine which OTIO type
370428
# to transcribe into. Note that the order of this if/elif/... chain
@@ -1396,7 +1454,13 @@ def _contains_something_valuable(thing):
13961454
return True
13971455

13981456

1399-
def read_from_file(filepath, simplify=True, transcribe_log=False, attach_markers=True):
1457+
def read_from_file(
1458+
filepath,
1459+
simplify=True,
1460+
transcribe_log=False,
1461+
attach_markers=True,
1462+
bake_keyframed_properties=False
1463+
):
14001464
"""Reads AAF content from `filepath` and outputs an OTIO timeline object.
14011465
14021466
Args:
@@ -1405,7 +1469,8 @@ def read_from_file(filepath, simplify=True, transcribe_log=False, attach_markers
14051469
transcribe_log (bool, optional): log activity as items are getting transcribed
14061470
attach_markers (bool, optional): attaches markers to their appropriate items
14071471
like clip, gap. etc on the track
1408-
1472+
bake_keyframed_properties (bool, optional): bakes animated property values
1473+
for each frame in a source clip
14091474
Returns:
14101475
otio.schema.Timeline
14111476
@@ -1414,8 +1479,9 @@ def read_from_file(filepath, simplify=True, transcribe_log=False, attach_markers
14141479
# Note that a global 'switch' is used in order to avoid
14151480
# passing another argument around in the _transcribe() method.
14161481
#
1417-
global _TRANSCRIBE_DEBUG
1482+
global _TRANSCRIBE_DEBUG, _BAKE_KEYFRAMED_PROPERTIES_VALUES
14181483
_TRANSCRIBE_DEBUG = transcribe_log
1484+
_BAKE_KEYFRAMED_PROPERTIES_VALUES = bake_keyframed_properties
14191485

14201486
with aaf2.open(filepath) as aaf_file:
14211487

1.24 MB
Binary file not shown.

0 commit comments

Comments
 (0)