pyaaf2

repository·main·Indexed 41 days ago

https://github.com/markreidvfx/pyaaf2

A pure Python implementation for reading, writing, and modifying Advanced Authoring Format (AAF) files. It provides low-level access to AAF structures and supports embedding media such as DNxHD, DNxHR, and WAV without requiring the official AAF SDK.

Tokens
9.3K
Snippets
29
Records
48
Agent score
52%

What's inside pyaaf2

  1. Overview of pyaaf2 features

    main

    pyaaf2 is a pure Python module designed for reading and writing Advanced Authoring Format (AAF) files. It is a rewrite of pyaaf1 and is designed to be lightweight with zero dependencies, meaning it does not require the AAF SDK.

    Key capabilities include:

    • Reading and writing AAF files.
    • Modifying existing AAF files in-place.
    • Embedding media such as DNxHD, DNxHR, and WAV.
    • Copying objects between different AAF files.
    • Low-level access to Compound File Binary.
    • Lazy file reading for efficiency.
  2. Embed footage in an AAF file

    main

    To create an AAF file with embedded media, follow these steps:

    1. Open a file in write mode ('w').
    2. Use the factory on the AAFFile object (f.create) to instantiate objects like MasterMob or SourceMob.
    3. Append created objects to f.content.mobs.
    4. For timecoded media, create a SourceMob and use create_tape_slots to define timecode parameters.
    5. Use create_source_clip to reference specific timecode locations.
    6. Use import_dnxhd_essence or import_audio_essence to embed the media files into the MOB.
    import aaf2
    
    with aaf2.open("example2.aaf", 'w') as f:
        # objects are create with a factory on the AAFFile Object
        mob = f.create.MasterMob("Demo2")
    
        # add the mob to the file
        f.content.mobs.append(mob)
    
        edit_rate = 25
    
        # lets also create a tape so we can add timecode (optional)
        tape_mob = f.create.SourceMob()
        f.content.mobs.append(tape_mob)
    
        timecode_rate = 25
        start_time = timecode_rate * 60 * 60 # 1 hour
        tape_name = "Demo Tape"
    
        # add tape slots to tape mob
        tape_mob.create_tape_slots(tape_name, edit_rate, 
                                   timecode_rate, media_kind='picture')
    
        # create sourceclip that references timecode
        tape_clip = tape_mob.create_source_clip(1, start_time)
    
        # now finally import the generated media
        mob.import_dnxhd_essence("sample.dnxhd", edit_rate, tape_clip)
        mob.import_audio_essence("sample.wav", edit_rate)
  3. Read an AAF file

    main

    To read an AAF file, use aaf2.open() in read mode ('r'). You can access the file's content, retrieve the top-level composition, and interact with AAFObjects using dictionary-like syntax or slot access for media tracks.

    import aaf2
    
    with aaf2.open("path/to/file.aaf", "r") as f:
        # get the main composition
        main_compostion = next(f.content.toplevel())
    
        # print the name of the composition
        print(main_compostion.name)
    
        # AAFObjects have properties that can be accessed just like a dictionary
        print(main_compostion['CreationTime'].value)
    
        # video, audio and other track types are stored in slots on a mob object.
        for slot in main_compostion.slots:
            segment = slot.segment
            print(segment)
  4. Install pyaaf2 from source (development version)

    main

    If you need the latest development version from the git master branch, clone the repository and run the setup script manually.

    git clone https://github.com/markreidvfx/pyaaf2
    cd pyaaf2
    python setup.py install
  5. Understand the Mob hierarchy in pyaaf2

    main

    In pyaaf2, a Mob (Metadata Object) is the fundamental building block of an AAF file. All major structural elements are subclasses of Mob.

    Key subclasses include:

    • Mob: The base class for all metadata objects.
    • CompositionMob: Used for representing compositions/timelines.
    • MasterMob: Used for master-level metadata.
    • SourceMob: Represents source media (e.g., raw video or audio files) and holds essence data.

    Every Mob has a unique mob_id (of type MobID) and a name.

  6. Understand MXF object types and AUIDs

    main

    The library uses AUID (Application Unique Identifier) to identify and register MXF classes. Many core MXF components are implemented as subclasses of MXFObject or MXFComponent and are registered via the @register_mxf_class decorator.

    Key component types include:

    • Packages: MXFMaterialPackage, MXFSourcePackage (subclass of MXFPackage)
    • Tracks: MXFStaticTrack, MXFEventTrack (subclass of MXFTrack)
    • Components: MXFSequence, MXFSourceClip, MXFTimecode, MXFMPEG2VideoDescriptor (subclass of MXFComponent)
    • Descriptors: MXFMultipleDescriptor, MXFCDCIDescriptor, MXFRGBADescriptor, MXFPCMDescriptor (subclass of MXFDescriptor)
  7. Open AAF files using AAFFile

    main

    The AAFFile class is the primary entry point for the PyAAF API. It is designed to behave similarly to Python's native open() function. It is highly recommended to use the with statement to ensure the file is properly closed and saved.

    Warning: If an exception occurs inside a with block while the file is open in a writable mode (wb+ or rb+), the resulting AAF file may be corrupted and should not be trusted.

    # Opening an existing AAF file for reading
    with aaf.open('/path/to/aaf_file.aaf', 'r') as f:
        # perform read operations
    
    # Opening an existing AAF file for reading and writing
    with aaf.open('/path/to/aaf_file.aaf', 'rw') as f:
        # perform read and write operations
    
    # Creating a new AAF file (overwriting existing)
    with aaf.open('/path/to/aaf_file.aaf', 'w') as f:
        # perform write operations
    
    # Opening an AAF file in memory using BytesIO
    with aaf.open() as f:
        # perform operations on in-memory file
  8. Compare AUID instances

    main

    You can compare AUID instances using standard Python comparison operators:

    • ==: Checks equality against another AUID or a uuid.UUID object.
    • <: Compares two AUID instances based on their integer value.
    • hash(): AUID objects are hashable and can be used as keys in dictionaries.
    from aaf2.auid import AUID
    import uuid
    
    a = AUID(hex='550e8400e29b41d4a716446655440000')
    b = AUID(hex='550e8400e29b41d4a716446655440000')
    c = uuid.UUID('550e8400-e29b-41d4-a716-446655440000')
    
    print(a == b) # True
    print(a == c) # True
    print(hash(a)) # integer hash