57
57
import aaf2 .mobs # noqa: E402
58
58
import aaf2 .components # noqa: E402
59
59
import aaf2 .core # noqa: E402
60
+ import aaf2 .misc # noqa: E402
60
61
from opentimelineio_contrib .adapters .aaf_adapter import aaf_writer # noqa: E402
61
62
62
63
65
66
# If enabled, output recursive traversal info of _transcribe() method.
66
67
_TRANSCRIBE_DEBUG = False
67
68
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
+
68
79
69
80
def _transcribe_log (s , indent = 0 , always_print = False ):
70
81
if always_print or _TRANSCRIBE_DEBUG :
@@ -108,7 +119,7 @@ def _get_class_name(item):
108
119
return item .__class__ .__name__
109
120
110
121
111
- def _transcribe_property (prop ):
122
+ def _transcribe_property (prop , owner = None ):
112
123
# XXX: The unicode type doesn't exist in Python 3 (all strings are unicode)
113
124
# so we have to use type(u"") which works in both Python 2 and 3.
114
125
if isinstance (prop , (str , type (u"" ), numbers .Integral , float , dict )):
@@ -118,19 +129,66 @@ def _transcribe_property(prop):
118
129
elif isinstance (prop , list ):
119
130
result = {}
120
131
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 )
123
181
else :
124
182
# @TODO: There may be more properties that we might want also.
125
183
# If you want to see what is being skipped, turn on debug.
126
184
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 ))
129
187
return result
130
188
elif hasattr (prop , "properties" ):
131
189
result = {}
132
190
for child in prop .properties ():
133
- result [child .name ] = _transcribe_property (child .value )
191
+ result [child .name ] = _transcribe_property (child .value , owner = owner )
134
192
return result
135
193
else :
136
194
return str (prop )
@@ -364,7 +422,7 @@ def _transcribe(item, parents, edit_rate, indent=0):
364
422
if hasattr (prop , 'name' ) and hasattr (prop , 'value' ):
365
423
key = str (prop .name )
366
424
value = prop .value
367
- metadata [key ] = _transcribe_property (value )
425
+ metadata [key ] = _transcribe_property (value , owner = item )
368
426
369
427
# Now we will use the item's class to determine which OTIO type
370
428
# to transcribe into. Note that the order of this if/elif/... chain
@@ -1396,7 +1454,13 @@ def _contains_something_valuable(thing):
1396
1454
return True
1397
1455
1398
1456
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
+ ):
1400
1464
"""Reads AAF content from `filepath` and outputs an OTIO timeline object.
1401
1465
1402
1466
Args:
@@ -1405,7 +1469,8 @@ def read_from_file(filepath, simplify=True, transcribe_log=False, attach_markers
1405
1469
transcribe_log (bool, optional): log activity as items are getting transcribed
1406
1470
attach_markers (bool, optional): attaches markers to their appropriate items
1407
1471
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
1409
1474
Returns:
1410
1475
otio.schema.Timeline
1411
1476
@@ -1414,8 +1479,9 @@ def read_from_file(filepath, simplify=True, transcribe_log=False, attach_markers
1414
1479
# Note that a global 'switch' is used in order to avoid
1415
1480
# passing another argument around in the _transcribe() method.
1416
1481
#
1417
- global _TRANSCRIBE_DEBUG
1482
+ global _TRANSCRIBE_DEBUG , _BAKE_KEYFRAMED_PROPERTIES_VALUES
1418
1483
_TRANSCRIBE_DEBUG = transcribe_log
1484
+ _BAKE_KEYFRAMED_PROPERTIES_VALUES = bake_keyframed_properties
1419
1485
1420
1486
with aaf2 .open (filepath ) as aaf_file :
1421
1487
0 commit comments