markreidvfx / pyaaf

Python Bindings for the Advanced Authoring Format (AAF)
http://markreidvfx.github.io/pyaaf
MIT License
49 stars 9 forks source link

Master Mob importing as clip / RGBADescriptor extended defs #41

Closed ding-hub closed 6 years ago

ding-hub commented 7 years ago

Im trying to create an AAF with an OperationGroup so that I can bring in one composite clip with an effect already applied to it but when I bring in my AAF into Avid I get the grouped clip along with the master mob clip but I just want the grouped clip only. Is this the expected behavior or am I doing something wrong. Here is what I'm doing. Im bringing in two clips in the same master mob using the function import_video_essence. Then I'm passing it to this function.

def add_clip(f, slot, tape_name, frame_rate, length, timecode_fps=29.97):
    clip_type="Source"
    source_clip = slot.segment
    source_mob = source_clip.resolve_ref()
    source_slot_id = source_clip.slot_id

    # create tape mob
    tape_mob = f.create.SourceMob(tape_name)

    if '_alpha' in tape_name:
        rgba_description = f.create.RGBADescriptor()
        rgba_description.set_pixelLayout(1, 8, "Alpha")
        rgba_description.set_max_ref(235)
        rgba_description.set_min_ref(16)
        tape_mob.essence_descriptor = rgba_description
        clip_type="Alpha"
    else:
        tape_mob.essence_descriptor = f.create.TapeDescriptor()
        # add timecode to tape mob
        tc = aaf.util.Timecode(fps=timecode_fps)
        tc.drop = "drop"
        tape_mob.append_timecode_slot(frame_rate, 2, tc, length)

    tape_mob.add_nil_ref(1, length, "DataDef_Picture", frame_rate)

    # overwrite source mob source clip with tape mob
    tapeclip = tape_mob.create_clip(1, source_clip.length, 0)
    source_mob.slot_at(source_slot_id).segment = tapeclip
    f.storage.add_mob(tape_mob)

    essence_access = mastermob.open_essence(slot.slot_id)
    essence_access.codec_flavour = "Flavour_VC3_1242"

    clip = mastermob.create_clip(slot.slot_id)
    composite[clip_type] = clip

    return clip

Then Im putting each clip into an OperationGroup and adding that to a TimelineMobSlot. When I bring in the AAF I get the group with the effect but also the master mob clip. Is there a way I can avoid bringing in the extra clip?

BTW I extended RGBADescriptor. I don't know how to use github but I can post what I did if you are interested. I tried to use the set max/min reference to set the max/min luma reference of the matte clip but it is not working as I expected and my matte is a bit transparent. I guess a topic for another day.

ding-hub commented 7 years ago

Well just in case...

AAF.pxd

    cdef aafUID_t AUID_AAFRGBADescriptor
    cdef GUID IID_IAAFRGBADescriptor2
    cdef cppclass IAAFRGBADescriptor2(IUnknown):
        HRESULT Initialize()
        HRESULT SetPixelLayout(aafUInt32 numberElements, aafRGBAComponent_t *PixelLayoutArray) 
        HRESULT GetPixelLayout(aafUInt32 numberElements, aafRGBAComponent_t *PixelLayoutArray) 
        HRESULT SetComponentMaxRef(aafUInt32 componentMaxRef)   
        HRESULT GetComponentMaxRef(aafUInt32 * componentMaxRef)
        HRESULT SetComponentMinRef(aafUInt32 componentMinRef)
        HRESULT GetComponentMinRef(aafUInt32 * componentMinRef)

RGBADescriptor.pyx

cdef class RGBADescriptor(DigitalImageDescriptor):
    def __cinit__(self):
        self.iid = lib.IID_IAAFRGBADescriptor2
        self.auid = lib.AUID_AAFRGBADescriptor
        self.ptr = NULL

    cdef lib.IUnknown **get_ptr(self):
        return <lib.IUnknown **> &self.ptr

    cdef query_interface(self, AAFBase obj = None):
        if obj is None:
            obj = self
        else:
            query_interface(obj.get_ptr(), <lib.IUnknown **> &self.ptr, lib.IID_IAAFRGBADescriptor2)

        DigitalImageDescriptor.query_interface(self, obj)

    def __dealloc__(self):
        if self.ptr:
            self.ptr.Release()

    def __init__(self, root):
        cdef Dictionary dictionary = root.dictionary
        dictionary.create_instance(self)

    def get_pixelLayout(self, lib.aafUInt32 element):
        cdef lib.aafRGBAComponent_t PixelLayoutArray 
        error_check(self.ptr.GetPixelLayout(element, &PixelLayoutArray))
        return PixelLayoutArray

    def set_pixelLayout(self, lib.aafUInt32 element, size, type = "None"):
        if type == "None":
            type = lib.kAAFCompNone
        elif type == "Alpha":
            type = lib.kAAFCompAlpha
        elif type == "Blue":
            type = lib.kAAFCompBlue
        elif type == "Fill":
            type = lib.kAAFCompFill
        elif type == "Green":
            type = lib.kAAFCompGreen
        elif type == "Palette":
            type = lib.kAAFCompPalette
        elif type == "Red":
            type = lib.kAAFCompRed
        elif type == "Null":
            type = lib.kAAFCompNull

        cdef lib.aafUInt32 numberElements = element
        cdef lib.aafRGBAComponent_t PixelLayoutArray
        PixelLayoutArray.Code = type
        PixelLayoutArray.Size = size

        error_check(self.ptr.SetPixelLayout(element, &PixelLayoutArray))

    def get_max_ref(self):
        cdef lib.aafUInt32 ref
        error_check(self.ptr.GetComponentMaxRef(&ref))
        return ref

    def set_max_ref(self, lib.aafUInt32 ref):
        error_check(self.ptr.SetComponentMaxRef(ref))

    def get_min_ref(self):
        cdef lib.aafUInt32 ref
        error_check(self.ptr.GetComponentMinRef(&ref))
        return ref

    def set_min_ref(self, lib.aafUInt32 ref):
        error_check(self.ptr.SetComponentMinRef(ref))

essence.pxd

cdef class RGBADescriptor(DigitalImageDescriptor):
    cdef lib.IAAFRGBADescriptor2 *ptr

AAFTypes.pxd

    ctypedef aafInt32 aafRGBAComponentKind_t

    ctypedef enum aafRGBAComponentKind_e "_aafRGBAComponentKind_e":
        kAAFCompNone = 0x30
        kAAFCompAlpha = 0x41
        kAAFCompBlue = 0x42
        kAAFCompFill = 0x46
        kAAFCompGreen = 0x47
        kAAFCompPalette = 0x50
        kAAFCompRed = 0x52
        kAAFCompNull = 0x0

    ctypedef struct aafRGBAComponent_t:
        aafRGBAComponentKind_t Code
        aafUInt8 Size

I'm just learning python/cython so I hope it will help.

markreidvfx commented 7 years ago

hi @ding-hub, sorry, I'm not entirely sure I understand what your trying to do. Are you trying a clip with a effect on it? Could you perhaps provide a sample aaf (or aaf2xml.py dump of one) from Media Composer that your trying to match?

Thanks for sharing your RGBADescriptor changes, I'll try incorporate some them when I get a chance.

btw some those properties can be set via the properties interface already

f = aaf.open()
d = f.create.RGBADescriptor()
# print all the property names
print d.all_keys()
# set min/max ref
d['ComponentMinRef'].value = 16
d['ComponentMaxRef'].value = 235
ding-hub commented 7 years ago

Yeah, I'm trying to create a clip with alpha channel. Here is the xml to the aaf I'm trying to copy. aaf.xml I'm doing the following.

# CREATE OPERATION GROUP
opgroup = aaf.component.OperationGroup(f, "picture", 30, op_def)

def import_dnxhd_video_essence(f, mastermob, path, length, framerate):
    slot = mastermob.import_video_essence(path, framerate)
    add_clip(f, slot, ntpath.basename(path), 30000/1001, length, framerate)

# ADD NESTED SCOPE TO OP GROUP
scopesequence = f.create.Sequence("picture")
opgroup.insert(0, scopesequence)
scope = aaf.component.NestedScope(f, "picture", 30)
scopesequence.append(scope)

# ADD ESSENCE FILES
for path in filelist:
    if '.dnxhd' in path:
        path=os.path.join(folderpath,path)
        import_dnxhd_video_essence(f, mastermob, path, 30, 29.97)

opgroup.insert(1, composite["Source"])
opgroup.insert(2, composite["Alpha"])

# CREATE TIMELINE SLOT
timeline_slot = f.create.TimelineMobSlot()
timeline_slot.editrate = 29.97
timeline_slot.segment = opgroup
comp_mob.append_slot(timeline_slot)
markreidvfx commented 7 years ago

Okay I think I understand now, your getting this: screen shot 2017-11-05 at 3 17 41 pm

And you want this: screen shot 2017-11-05 at 3 47 31 pm

Through some testing its seems to be the custom AppCode property that hides the mastermob.

f = aaf.open()

# add AppCode property to Mob classdef
mob_classdef = f.dictionary.lookup_classdef("Mob")
int32_typedef = f.dictionary.lookup_typedef("Int32")
prop_id = AUID("urn:uuid:96c46992-4f62-11d3-a022-006094eb75cb")
mob_classdef.register_optional_propertydef(int32_typedef, prop_id, 'AppCode')

# now you can set it
mastermob = f.create.MasterMob()
mastermob['AppCode'].value = 1

I initial thought is was th UsageCode on the mastermob, and need to be set to Usage_LowerLevel, but it seems to do nothing in avid.

hope that helps!

ding-hub commented 7 years ago

Awesome! Thanks. Now I just need to do the same but with an mxf link. I'm sure your link mxf example will come in handy.