Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Difference between group clips and sequences - both are composition mobs #118

Open
davidnorden opened this issue Apr 5, 2023 · 4 comments

Comments

@davidnorden
Copy link

I am trying to export group clips from an avb-file, and assume it should be possible since there is already a script that exports a sequence as an aaf from a avb-file (using pyavb). Both are composition mobs, and exporting a group clip from Media Composer and importing into Premiere Pro will show that Premiere Pro interprets the aaf for a group clip as a sequence. So what is the unique identifier in the aaf-file that lets Media Composer know that the group clip is NOT a sequence (since both are composition mobs...) Does anyone know?

@TrevorAyl
Copy link

I think group clips are actually 'selector's - the active clip being selected and the others 'alternatives'.

Although chatgpt mentions 'essence groups' too

Still trying to get my head around it...

@markreidvfx
Copy link
Owner

Yes GroupClips are called selectors.
EssenceGroups are different, they are use for different quality levels of the same media.
Essence is what AAF calls media.

I wouldn't trust any code chatgpt gives you for using this library. There aren't enough examples in the wild for it to steal code from.

@TrevorAyl
Copy link

TrevorAyl commented Mar 1, 2024

This is as far as I can get by myself!

Something wrong in how I append the selector (e.g. do I need the selector_sequence?)

Hopefully someone (@markreidvfx) can point out the errors of my ways...


import sys
import aaf2
import json
import os
from datetime import datetime

file_name = "./testSelector.aaf"
edit_rate = 50
sequence_start = 1*60*60*edit_rate
sequence_length = 20*edit_rate
dictMobID = {}

with aaf2.open(file_name, "w")  as f:
    # Composition Mob created first
    comp_mob = f.create.CompositionMob()
    comp_mob.usage = "Usage_TopLevel"
    comp_mob.name = "test"

    # Create a TimelineMobSlot with a Timecode Segment for the start timecode
    tc_segment = f.create.Timecode(edit_rate)
    tc_segment.start = sequence_start
    tc_slot = comp_mob.create_timeline_slot(edit_rate, slot_id=1)
    tc_slot.segment = tc_segment

    # Create Selector Composition Mob outside the Master MOBs loop
    selector_comp_mob = f.create.CompositionMob()
    selector_comp_mob.usage = "Usage_TopLevel"
    selector_comp_mob.name = "GRP - sequence test"

    selector_sequence = f.create.Sequence(media_kind="picture")
    selector_timeline_slot = selector_comp_mob.create_timeline_slot(edit_rate)
    selector_timeline_slot.segment = selector_sequence
    selector_timeline_slot.name = "GRP-TrackC"

    # Make the Master MOBs
    for i in range(1,5):
        tape_name= f"tapeName_{i}"

        # Make the Tape MOB
        tape_mob = f.create.SourceMob()
        
        tape_slot, tape_timecode_slot = tape_mob.create_tape_slots(tape_name, edit_rate, edit_rate)        
        
        # Set start time for clip
        tape_timecode_slot.segment.start = sequence_start
        # Reduce from default 12 hour length
        tape_slot.segment.length = sequence_length

        f.content.mobs.append(tape_mob)
        # Make a FileMob
        file_mob = f.create.SourceMob()

        file_description = f.create.CDCIDescriptor()
        file_description['ComponentWidth'].value = 8
        file_description['HorizontalSubsampling'].value = 4
        file_description['ImageAspectRatio'].value = '16/9'
        file_description['StoredWidth'].value = 1920
        file_description['StoredHeight'].value = 1080
        file_description['FrameLayout'].value = 'FullFrame'
        file_description['VideoLineMap'].value = [42, 0]
        file_description['SampleRate'].value = edit_rate
        file_description['Length'].value = sequence_length

        file_mob.descriptor = file_description
        # This length affects length of master mob and in timeline
        tape_clip = tape_mob.create_source_clip(slot_id=1, length=sequence_length)
        slot = file_mob.create_picture_slot(edit_rate)
        slot.segment.components.append(tape_clip)

        f.content.mobs.append(file_mob)

        master_mob = f.create.MasterMob()
        master_mob.name = f"MM_name_{i}"
        clip = file_mob.create_source_clip(slot_id=1)
        slot = master_mob.create_picture_slot(edit_rate)
        slot.segment.components.append(clip)

        dictMobID[i] = master_mob

        f.content.mobs.append(master_mob)

    for i in range(1,2):
        sequence = f.create.Sequence(media_kind="picture")
        timeline_slot = comp_mob.create_timeline_slot(edit_rate)
        timeline_slot.segment= sequence
        timeline_slot.name = "SRC"

        dictClipID = {}
        clip_position = 200
        for key in dictMobID:

            mm = dictMobID[key]
            # print(mm.mob_id)
            clip_start = 0
            clip_length = 2*edit_rate
            # Create a SourceClip
            clip = mm.create_source_clip(slot_id=1)
            # print(str((clip.mob_id)))
            # This is the start point of the master mob in the source clip?
            clip.start = clip_position
            # This is the length of the source clip - filled with the master mob
            clip.length = clip_length
            dictClipID[key] = clip

            sequence.components.append(clip)

    # Attempt at creating selector
    selector = f.create.Selector()
    selector.media_kind = "picture"

    # Set the first clip as selected and rest as alternates
    first_clip = True
    for clip in dictClipID.values():
        if first_clip:
            selector['Selected'].value = clip
            first_clip = False
        # else:
            selector['Alternates'].append(clip)

    selector_sequence.components.append(selector)
    sequence.components.append(selector_sequence)


    # Append your main composition mob at the end
    f.content.mobs.append(comp_mob)

errror message:

    raise AAFAttachError("cannot attached obj to %s already attached to %s" % (dir_entry.path(), self.dir.path()))
aaf2.exceptions.AAFAttachError: cannot attached obj to /Header-2/Content-3b03/Mobs-1901{c}/Slots-4403{1}/Segment-4803/Components-1001{4}/Components-1001{0}/Selected-f01 already attached to /Header-2/Content-3b03/Mobs-1901{c}/Slots-4403{1}/Segment-4803/Components-1001{0}

@XDeschuyteneer
Copy link

XDeschuyteneer commented Mar 1, 2024 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants