Skip to content

AAF roundtrip breaks multi track video #69

Description

@zephilooo

Problem

Hi,

I was trying to roundtrip a multi-video-track AAF file through otioconvert and I noticed the output file doesnt behave the same as the input when I tried to open it in DaVinci Resolve. Resolve interprets each video track as a separate independent timeline instead of a single multi-track composition.
Im not sure whether Im not using the adapter correctly or if this is a genuine bug, so I wanted to share what I observed.

What I observed in the reference file

Inspecting sample multitrack.aaf with:

import aaf2
with aaf2.open('multitrack.aaf') as f:
    for mob in f.content.compositionmobs():
        for slot in mob.slots:
            print('slot_id:', slot.slot_id,
                  'physical:', slot['PhysicalTrackNumber'].value,
                  'type:', type(slot.segment).__name__)

gives

slot_id: 1  physical: 1  type: Timecode
slot_id: 2  physical: 1  type: NestedScope
slot_id: 3  physical: 1  type: Sequence (Audio 1)
slot_id: 4  physical: 2  type: Sequence  (Audio 2)

All three video tracks live inside a single NestedScope in slot_id: 2. So when I open this file in Resolve, all 3 video tracks are reconstructed correctly.

Roundtrip with simplify=True (default)

Then I roundtrip the file with

otioconvert -i multitrack.aaf -o multitrack.otio
otioconvert -i multitrack.otio -o multitrack2.aaf

Then the NestedScope gets flattened into three independent top level tracks in the OTIO, as the simplify=True is applied. So then when written back to AAF:

slot_id: 2  type: Sequence   :  was V1, now independent slot
slot_id: 3  type: Sequence   : was V2, now independent slot
slot_id: 4  type: Sequence   : was V3, now independent slot
slot_id: 5  type: Sequence   : Audio 1
slot_id: 6  type: Sequence   : Audio 2
slot_id: 7  type: Timecode

Everything is normal here

Roundtrip with simplify=False

But when I try

otioconvert -i multitrack.aaf -o multitrack.otio -a simplify=False
otioconvert -i multitrack.otio -o multitrack2.aaf

With simplify=False the NestedScope is correctly preserved in the OTIO as a nested Stack. However the write step fails with two different errors depending on how it's invoked:

Error 1, when passing the file directly, the read produces a SerializableCollection and the writer crashes:

AttributeError: 'SerializableCollection' object has no attribute 'tracks'
  File "aaf_writer.py", line 374, in _stackify_nested_groups

error 2, when extracting the Timeline manually from the collection and passing it directly to write_to_file, the writer crashes in _stackify_nested_groups:

ValueError
  File "aaf_writer.py", line 380, in _stackify_nested_groups
    track.remove(child)

Question

Then am I using the adapter incorrectly, or is the multi-video-track roundtrip currently not supported despite being listed in the README feature matrix? I couldnt find any example or documentation showing how to correctly roundtrip a multi-video-track AAF.

Also, the documentation for read_from_file states it returns otio.schema.Timeline, but with simplify=False on a multi-mob AAF file it returns a SerializableCollection.

Full bash code :

python3 -c "
import aaf2
with aaf2.open('/path/to/samples/multitrack.aaf') as f:
    for mob in f.content.compositionmobs():
        for slot in mob.slots:
            print('slot_id:', slot.slot_id, 'physical:', slot['PhysicalTrackNumber'].value, 'type:', type(slot.segment).__name__)
"
slot_id: 10 physical: 1 type: Timecode
slot_id: 11 physical: 3 type: Pulldown
slot_id: 12 physical: 2 type: Timecode
slot_id: 13 physical: 4 type: Timecode
slot_id: 14 physical: 5 type: Pulldown
slot_id: 15 physical: 6 type: Pulldown
slot_id: 16 physical: 7 type: Pulldown
slot_id: 17 physical: 1 type: EdgeCode
slot_id: 9 physical: 1 type: NestedScope
slot_id: 1008 physical: 1 type: Sequence
slot_id: 1009 physical: 2 type: Sequence
slot_id: 1010 physical: 3 type: Sequence
slot_id: 20 physical: 1 type: Sequence
slot_id: 21 physical: 1 type: OperationGroup
slot_id: 22 physical: 2 type: OperationGroup
otioconvert -i /path/to/samples/multitrack.aaf -o /tmp/multitrack.otio
otioconvert -i /tmp/multitrack.otio -o /tmp/multitrack2.aaf

python3 -c "
import aaf2
with aaf2.open('/tmp/multitrack2.aaf') as f:
    for mob in f.content.compositionmobs():
        for slot in mob.slots:
            print('slot_id:', slot.slot_id, 'physical:', slot['PhysicalTrackNumber'].value, 'type:', type(slot.segment).__name__)
"
slot_id: 1 physical: None type: Sequence
slot_id: 2 physical: None type: Sequence
slot_id: 3 physical: None type: Sequence
slot_id: 4 physical: None type: OperationGroup
slot_id: 5 physical: None type: OperationGroup
slot_id: 6 physical: 1 type: Timecode

python3 -c "
import opentimelineio as otio
result = otio.adapters.read_from_file('/tmp/multitrack_nosimplify.otio')
print(type(result).__name__)
"
SerializableCollection
otioconvert -i /tmp/multitrack_nosimplify.otio -o /tmp/multitrack2.aaf
Traceback (most recent call last):
  File ".../bin/otioconvert", line 8, in <module>
    sys.exit(main())
  File ".../site-packages/opentimelineio/console/otioconvert.py", line 273, in main
    otio.adapters.write_to_file(
  ...
  File ".../opentimelineio/adapters/__init__.py", line 203, in write_to_file
    return adapter.write_to_file(...)
  File ".../otio_aaf_adapter/adapters/advanced_authoring_format.py", line 1941, in write_to_file
    timeline = aaf_writer._stackify_nested_groups(hook_tl)
  File ".../otio_aaf_adapter/adapters/aaf_adapter/aaf_writer.py", line 374, in _stackify_nested_groups
    for track in copied.tracks:
AttributeError: 'SerializableCollection' object has no attribute 'tracks'
python3 -c "
import opentimelineio as otio
col = otio.adapters.read_from_file('/tmp/multitrack_nosimplify.otio')
for item in col:
    if isinstance(item, otio.schema.Timeline):
        otio.adapters.write_to_file(item, '/tmp/multitrack2.aaf', use_empty_mob_ids=True)
        break
"
Traceback (most recent call last):
  File "<string>", line 6, in <module>
    otio.adapters.write_to_file(...)
  File ".../opentimelineio/adapters/__init__.py", line 203, in write_to_file
    return adapter.write_to_file(...)
  File ".../otio_aaf_adapter/adapters/advanced_authoring_format.py", line 1941, in write_to_file
    timeline = aaf_writer._stackify_nested_groups(hook_tl)
  File ".../otio_aaf_adapter/adapters/aaf_adapter/aaf_writer.py", line 380, in _stackify_nested_groups
    track.remove(child)
ValueError

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions