/* * Copyright (C) 2017-2023 Adrien Gesta-Fline * * This file is part of libAAF. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /** * @file LibAAF/AAFIface/AAFIParser.c * @brief AAF processing * @author Adrien Gesta-Fline * @version 0.1 * @date 27 june 2018 * * @ingroup AAFIface * @addtogroup AAFIface * * * * * @{ */ #include #include #include #include #include #include #include #include "aaf/AAFDump.h" #include "aaf/AAFIAudioFiles.h" #include "aaf/AAFIParser.h" #include "aaf/AAFIface.h" #include "aaf/AAFToText.h" #include "aaf/debug.h" #include "aaf/ProTools.h" #include "aaf/Resolve.h" #include "aaf/AAFDefs/AAFClassDefUIDs.h" #include "aaf/AAFDefs/AAFPropertyIDs.h" // #include "aaf/AAFDefs/AAFCompressionDefs.h" #include "aaf/AAFDefs/AAFDataDefs.h" #include "aaf/AAFDefs/AAFExtEnum.h" #include "aaf/AAFDefs/AAFInterpolatorDefs.h" #include "aaf/AAFDefs/AAFOperationDefs.h" #include "aaf/AAFDefs/AAFParameterDefs.h" #include "aaf/AAFDefs/AAFTypeDefUIDs.h" // #include "aaf/AAFDefs/AAFFileKinds.h" #include "aaf/AAFDefs/AAFOPDefs.h" // #include "aaf/AAFDefs/AAFContainerDefs.h" #include "aaf/utils.h" #define debug(...) \ _dbg (aafi->dbg, aafi, DEBUG_SRC_ID_AAF_IFACE, VERB_DEBUG, __VA_ARGS__) #define warning(...) \ _dbg (aafi->dbg, aafi, DEBUG_SRC_ID_AAF_IFACE, VERB_WARNING, __VA_ARGS__) #define error(...) \ _dbg (aafi->dbg, aafi, DEBUG_SRC_ID_AAF_IFACE, VERB_ERROR, __VA_ARGS__) // #define trace( ... ) // _dbg( aafi->dbg, aafi, LIB_AAF_IFACE_TRACE, 0, __VA_ARGS__ ) static aafRational_t AAFI_DEFAULT_TC_EDIT_RATE = { 25, 1 }; #define RESET_CONTEXT(ctx) \ /*ctx.MobSlot = NULL;*/ \ ctx.current_track = NULL; \ /*ctx.current_pos = 0;*/ \ ctx.current_transition = NULL; \ ctx.current_clip_gain = NULL; \ ctx.current_clip_automation = NULL; \ ctx.current_essence = NULL; \ ctx.current_clip = NULL; \ ctx.current_clip_is_muted = 0; \ ctx.current_clip_is_combined = 0; \ ctx.current_combined_clip_total_channel = 0; \ ctx.current_combined_clip_channel_num = 0; // ctx.current_track_is_multichannel = 0; // ctx.current_multichannel_track_channel = 0; // ctx.current_multichannel_track_clip_length = 0; // static void aafi_trace_obj( AAF_Iface *aafi, aafObject *Obj, char *color ); static wchar_t* build_unique_audiofilename (AAF_Iface* aafi, aafiAudioEssence* audioEssence); static wchar_t* build_unique_videofilename (AAF_Iface* aafi, aafiVideoEssence* videoEssence); /* TODO move to AAFCore.c */ static aafObject* get_Object_Ancestor (AAF_Iface* aafi, aafObject* Obj, const aafUID_t* ClassID); static aafUID_t* get_Component_DataDefinition (AAF_Iface* aafi, aafObject* Component); // static aafUID_t * get_FileDescriptor_ContainerFormat( AAF_Iface *aafi, aafObject *FileDescriptor ); static aafUID_t* get_OperationGroup_OperationIdentification (AAF_Iface* aafi, aafObject* OperationGroup); static aafUID_t* get_Parameter_InterpolationIdentification (AAF_Iface* aafi, aafObject* Parameter); static aafObject* get_EssenceData_By_MobID (AAF_Iface* aafi, aafMobID_t* MobID); // static aafiAudioEssence * getAudioEssenceBySourceMobID( AAF_Iface *aafi, aafMobID_t *sourceMobID ); // static aafiVideoEssence * getVideoEssenceBySourceMobID( AAF_Iface *aafi, aafMobID_t *sourceMobID ); static int parse_DigitalImageDescriptor (AAF_Iface* aafi, aafObject* DIDescriptor, td* __ptd); static int parse_CDCIDescriptor (AAF_Iface* aafi, aafObject* CDCIDescriptor, td* __ptd); static int parse_EssenceDescriptor (AAF_Iface* aafi, aafObject* EssenceDesc, td* __ptd); static int parse_PCMDescriptor (AAF_Iface* aafi, aafObject* PCMDescriptor, td* __ptd); static int parse_WAVEDescriptor (AAF_Iface* aafi, aafObject* WAVEDescriptor, td* __ptd); static int parse_AIFCDescriptor (AAF_Iface* aafi, aafObject* AIFCDescriptor, td* __ptd); static int parse_Locator (AAF_Iface* aafi, aafObject* Locator, td* __ptd); static int parse_NetworkLocator (AAF_Iface* aafi, aafObject* NetworkLocator, td* __ptd); static int parse_EssenceData (AAF_Iface* aafi, aafObject* EssenceData, td* __ptd); static int parse_Component (AAF_Iface* aafi, aafObject* Component, td* __ptd); static int parse_Transition (AAF_Iface* aafi, aafObject* Transition, td* __ptd); static int parse_NestedScope (AAF_Iface* aafi, aafObject* NestedScope, td* __ptd); static int parse_Filler (AAF_Iface* aafi, aafObject* Filler, td* __ptd); static int parse_Sequence (AAF_Iface* aafi, aafObject* Sequence, td* __ptd); static int parse_Timecode (AAF_Iface* aafi, aafObject* Timecode, td* __ptd); static int parse_OperationGroup (AAF_Iface* aafi, aafObject* OpGroup, td* __ptd); static int parse_SourceClip (AAF_Iface* aafi, aafObject* SourceClip, td* __ptd); static int parse_Selector (AAF_Iface* aafi, aafObject* Selector, td* __ptd); static int parse_Parameter (AAF_Iface* aafi, aafObject* Parameter, td* __ptd); static int parse_ConstantValue (AAF_Iface* aafi, aafObject* ConstantValue, td* __ptd); static int parse_VaryingValue (AAF_Iface* aafi, aafObject* VaryingValue, td* __ptd); static int retrieve_ControlPoints (AAF_Iface* aafi, aafObject* Points, aafRational_t* times[], aafRational_t* values[]); static int parse_Mob (AAF_Iface* aafi, aafObject* Mob); static int parse_CompositionMob (AAF_Iface* aafi, aafObject* CompoMob, td* __ptd); static int parse_SourceMob (AAF_Iface* aafi, aafObject* SourceMob, td* __ptd); static int parse_MobSlot (AAF_Iface* aafi, aafObject* MobSlot, td* __ptd); static void xplore_StrongObjectReferenceVector (AAF_Iface* aafi, aafObject* ObjCollection, td* __ptd); static void xplore_StrongObjectReferenceVector (AAF_Iface* aafi, aafObject* ObjCollection, td* __ptd) { struct trace_dump __td; __td_set (__td, __ptd, 0); // aaf_dump_ObjectProperties( aafi->aafd, ComponentAttributeList ); struct dbg* dbg = aafi->dbg; aafObject* Obj = NULL; aaf_foreach_ObjectInSet (&Obj, ObjCollection, NULL) { // aaf_dump_ObjectProperties( aafi->aafd, ObjCollection ); int offset = 0; /* TODO implement retrieve_TaggedValue() */ if (aaf_get_property (Obj, PID_TaggedValue_Name) && aaf_get_property (Obj, PID_TaggedValue_Value)) { wchar_t* name = aaf_get_propertyValue (Obj, PID_TaggedValue_Name, &AAFTypeID_String); aafIndirect_t* indirect = aaf_get_propertyValue (Obj, PID_TaggedValue_Value, &AAFTypeID_Indirect); if (aafUIDCmp (&indirect->TypeDef, &AAFTypeID_Int32)) { int32_t* indirectValue = aaf_get_indirectValue (aafi->aafd, indirect, &AAFTypeID_Int32); offset += laaf_util_snprintf_realloc (&dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, "Tagged | Name: %ls%*s Value (%ls) : %i\n", name, 56 - (int)wcslen (name), " ", aaft_TypeIDToText (&indirect->TypeDef), *indirectValue); } else if (aafUIDCmp (&indirect->TypeDef, &AAFTypeID_String)) { wchar_t* indirectValue = aaf_get_indirectValue (aafi->aafd, indirect, &AAFTypeID_String); offset += laaf_util_snprintf_realloc (&dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, "Tagged | Name: %ls%*s Value (%ls) : %ls\n", name, 56 - (int)wcslen (name), " ", aaft_TypeIDToText (&indirect->TypeDef), indirectValue); free (indirectValue); } else { offset += laaf_util_snprintf_realloc (&dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, "Tagged | Name: %ls%*s Value (%s%ls%s) : %sUNKNOWN_TYPE%s\n", name, 56 - (int)wcslen (name), " ", ANSI_COLOR_RED, aaft_TypeIDToText (&indirect->TypeDef), ANSI_COLOR_RESET, ANSI_COLOR_RED, ANSI_COLOR_RESET); } dbg->debug_callback (dbg, (void*)aafi, DEBUG_SRC_ID_DUMP, 0, "", "", 0, dbg->_dbg_msg, dbg->user); free (name); } else { dbg->debug_callback (dbg, (void*)aafi, DEBUG_SRC_ID_DUMP, 0, "", "", 0, dbg->_dbg_msg, dbg->user); offset = 0; aaf_dump_ObjectProperties (aafi->aafd, Obj); } } } void aafi_dump_obj (AAF_Iface* aafi, aafObject* Obj, struct trace_dump* __td, int state, int line, const char* fmt, ...) { if (aafi->ctx.options.trace == 0) return; /* Print caller line number */ struct dbg* dbg = aafi->dbg; int offset = 0; if (Obj) { switch (state) { case TD_ERROR: offset += laaf_util_snprintf_realloc (&dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, "%s", ANSI_COLOR_RED); break; case TD_WARNING: offset += laaf_util_snprintf_realloc (&dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, "%s", ANSI_COLOR_YELLOW); break; case TD_NOT_SUPPORTED: offset += laaf_util_snprintf_realloc (&dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, "%s", ANSI_COLOR_ORANGE); break; default: offset += laaf_util_snprintf_realloc (&dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, "%s", ANSI_COLOR_DARKGREY); break; } offset += laaf_util_snprintf_realloc (&dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, "%05i", line); } else { offset += laaf_util_snprintf_realloc (&dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, " "); } offset += laaf_util_snprintf_realloc (&dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, "%s%ls%s", ANSI_COLOR_DARKGREY, L"\u2502", ANSI_COLOR_RESET); // │ /* Print padding and vertical lines */ if (__td->lv > 0) { for (int i = 0; i < __td->lv; i++) { /* current level iteration has more than one entry remaining in loop */ if (__td->ll[i] > 1) { /* next level iteration is current trace */ if (i + 1 == __td->lv) { if (Obj) { offset += laaf_util_snprintf_realloc (&dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, "%ls", L"\u251c\u2500\u2500\u25fb "); // ├──◻ } else { offset += laaf_util_snprintf_realloc (&dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, "%ls", L"\u2502 "); // │ } } else { offset += laaf_util_snprintf_realloc (&dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, "%ls", L"\u2502 "); // │ } } else if (i + 1 == __td->lv && Obj) { offset += laaf_util_snprintf_realloc (&dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, "%ls", L"\u2514\u2500\u2500\u25fb "); // └──◻ } else { offset += laaf_util_snprintf_realloc (&dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, " "); } } } if (Obj) { switch (state) { case TD_ERROR: offset += laaf_util_snprintf_realloc (&dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, "%s", ANSI_COLOR_RED); break; case TD_WARNING: offset += laaf_util_snprintf_realloc (&dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, "%s", ANSI_COLOR_YELLOW); break; case TD_NOT_SUPPORTED: offset += laaf_util_snprintf_realloc (&dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, "%s", ANSI_COLOR_ORANGE); break; case TD_INFO: case TD_OK: if (__td->sub) offset += laaf_util_snprintf_realloc (&dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, "%s", ANSI_COLOR_DARKGREY); else offset += laaf_util_snprintf_realloc (&dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, "%s", ANSI_COLOR_CYAN); break; } offset += laaf_util_snprintf_realloc (&dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, "%ls ", aaft_ClassIDToText (aafi->aafd, Obj->Class->ID)); offset += laaf_util_snprintf_realloc (&dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, "%s", ANSI_COLOR_RESET); if (aafUIDCmp (Obj->Class->ID, &AAFClassID_TimelineMobSlot) && aafUIDCmp (Obj->Parent->Class->ID, &AAFClassID_CompositionMob)) { aafObject* Segment = aaf_get_propertyValue (Obj, PID_MobSlot_Segment, &AAFTypeID_SegmentStrongReference); aafUID_t* DataDefinition = get_Component_DataDefinition (aafi, Segment); wchar_t* name = aaf_get_propertyValue (Obj, PID_MobSlot_SlotName, &AAFTypeID_String); uint32_t* slotID = aaf_get_propertyValue (Obj, PID_MobSlot_SlotID, &AAFTypeID_UInt32); uint32_t* trackNo = aaf_get_propertyValue (Obj, PID_MobSlot_PhysicalTrackNumber, &AAFTypeID_UInt32); offset += laaf_util_snprintf_realloc (&dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, "[slot:%s%i%s track:%s%i%s] (DataDef : %s%ls%s) %s%ls ", ANSI_COLOR_BOLD, (slotID) ? (int)(*slotID) : -1, ANSI_COLOR_RESET, ANSI_COLOR_BOLD, (trackNo) ? (int)(*trackNo) : -1, ANSI_COLOR_RESET, ANSI_COLOR_DARKGREY, aaft_DataDefToText (aafi->aafd, DataDefinition), ANSI_COLOR_RESET, (name[0] != 0x00) ? ": " : "", (name) ? name : L""); free (name); } else if (aafUIDCmp (Obj->Class->ID, &AAFClassID_CompositionMob) || aafUIDCmp (Obj->Class->ID, &AAFClassID_MasterMob) || aafUIDCmp (Obj->Class->ID, &AAFClassID_SourceMob)) { aafUID_t* usageCode = aaf_get_propertyValue (Obj, PID_Mob_UsageCode, &AAFTypeID_UsageType); wchar_t* name = aaf_get_propertyValue (Obj, PID_Mob_Name, &AAFTypeID_String); offset += laaf_util_snprintf_realloc (&dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, "(UsageCode: %s%ls%s) %s%ls", ANSI_COLOR_DARKGREY, aaft_UsageCodeToText (usageCode), ANSI_COLOR_RESET, (name && name[0] != 0x00) ? ": " : "", (name) ? name : L""); free (name); } else if (aafUIDCmp (Obj->Class->ID, &AAFClassID_OperationGroup)) { aafUID_t* OperationIdentification = get_OperationGroup_OperationIdentification (aafi, Obj); offset += laaf_util_snprintf_realloc (&dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, "(OpIdent: %s%ls%s) ", ANSI_COLOR_DARKGREY, aaft_OperationDefToText (aafi->aafd, OperationIdentification), ANSI_COLOR_RESET); } // else if ( aafUIDCmp( Obj->Class->ID, &AAFClassID_TapeDescriptor ) || // aafUIDCmp( Obj->Class->ID, &AAFClassID_FilmDescriptor ) || // aafUIDCmp( Obj->Class->ID, &AAFClassID_CDCIDescriptor ) || // aafUIDCmp( Obj->Class->ID, &AAFClassID_RGBADescriptor ) || // aafUIDCmp( Obj->Class->ID, &AAFClassID_TIFFDescriptor ) || // aafUIDCmp( Obj->Class->ID, &AAFClassID_SoundDescriptor ) || // aafUIDCmp( Obj->Class->ID, &AAFClassID_PCMDescriptor ) || // aafUIDCmp( Obj->Class->ID, &AAFClassID_AES3PCMDescriptor ) || // aafUIDCmp( Obj->Class->ID, &AAFClassID_WAVEDescriptor ) || // aafUIDCmp( Obj->Class->ID, &AAFClassID_AIFCDescriptor ) ) // { // aafUID_t *ContainerFormat = get_FileDescriptor_ContainerFormat( aafi, Obj ); // offset += laaf_util_snprintf_realloc( &dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, "(ContainerIdent : \x1b[38;5;242m%ls\x1b[0m)", aaft_ContainerToText(ContainerFormat) ); // } if (state == TD_ERROR) { offset += laaf_util_snprintf_realloc (&dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, ": %s", ANSI_COLOR_RED); } else if (state == TD_INFO) { offset += laaf_util_snprintf_realloc (&dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, ": %s", ANSI_COLOR_CYAN); } va_list args; va_start (args, fmt); offset += laaf_util_vsnprintf_realloc (&dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, fmt, &args); va_end (args); // va_list args; // va_list args2; // va_copy( args2, args ); // // va_start( args2, fmt ); // // vprintf(fmt, args); // // offset += laaf_util_vsnprintf_realloc( &dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, fmt, args ); // int needed = vsnprintf( NULL, 0, fmt, args2 ) + 1; // if ( needed >= dbg->_dbg_msg_size + offset ) { // char *p = realloc( dbg->_dbg_msg, offset+needed ); // if (p) { // dbg->_dbg_msg = p; // } else { // /* TODO: realloc() faillure */ // // free(*str); // // *str = NULL; // // *size = 0; // // return -1; // } // } // va_end( args2 ); // // va_start( args, fmt ); // // vprintf( fmt, args ); // offset += vsnprintf( dbg->_dbg_msg+offset, dbg->_dbg_msg_size-offset, fmt, args ); // va_end( args ); if (state == TD_ERROR || state == TD_INFO) { offset += laaf_util_snprintf_realloc (&dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, "."); } if (state == TD_NOT_SUPPORTED || (aafi->ctx.options.trace_class && wcscmp (aaft_ClassIDToText (aafi->aafd, Obj->Class->ID), aafi->ctx.options.trace_class) == 0)) { offset += laaf_util_snprintf_realloc (&dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, "\n%s", (state == TD_NOT_SUPPORTED) ? ANSI_COLOR_ORANGE : ""); // offset += laaf_util_snprintf_realloc( &dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, "CFB Object Dump : %ls\n", aaf_get_ObjectPath( Obj ) ); // offset += laaf_util_snprintf_realloc( &dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, "=================\n" ); // cfb_dump_node( aafi->aafd->cfbd, Obj->Node, 1 ); offset += laaf_util_snprintf_realloc (&dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, "Properties Dump (%ls)\n", aaf_get_ObjectPath (Obj)); offset += laaf_util_snprintf_realloc (&dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, "===============\n\n"); // aaf_dump_nodeStreamProperties( aafi->aafd, Obj->Node ); // dbg->debug_callback( dbg, (void*)aafi, DEBUG_SRC_ID_TRACE, 0, "", "", 0, dbg->_dbg_msg, dbg->user ); // // offset = 0; // aaf_dump_ObjectProperties( aafi->aafd, Obj ); } else { aafProperty* Prop = NULL; int hasUnknownProps = 0; for (Prop = Obj->Properties; Prop != NULL; Prop = Prop->next) { if (Prop->def->meta) { // offset += laaf_util_snprintf_realloc( &dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, "\n"); if (aafi->ctx.options.trace_meta) { // aaf_dump_ObjectProperties( aafi->aafd, Obj ); // if ( Prop->pid == 0xffca ) { if (Prop->sf == SF_STRONG_OBJECT_REFERENCE_VECTOR) { offset += laaf_util_snprintf_realloc (&dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, "\n"); offset += laaf_util_snprintf_realloc (&dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, " >>> (0x%04x) %ls (%ls)\n", Prop->pid, aaft_PIDToText (aafi->aafd, Prop->pid), aaft_StoredFormToText (Prop->sf) /*AUIDToText( &Prop->def->type ),*/ /*aaft_TypeIDToText( &(Prop->def->type) )*/); void* propValue = aaf_get_propertyValue (Obj, Prop->pid, &AAFUID_NULL); xplore_StrongObjectReferenceVector (aafi, propValue, __td); // DUMP_OBJ_NO_SUPPORT( aafi, propValue, __td ); } else { offset += laaf_util_snprintf_realloc (&dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, "\n"); aaf_dump_ObjectProperty (aafi->aafd, Prop); } } else { offset += laaf_util_snprintf_realloc (&dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, "%s%s %ls[0x%04x]", ANSI_COLOR_RESET, (!hasUnknownProps) ? " (MetaProps:" : "", aaft_PIDToText (aafi->aafd, Prop->pid), Prop->pid); // laaf_util_dump_hex( Prop->val, Prop->len ); hasUnknownProps++; } } } if (aafi->ctx.options.trace_meta == 0 && hasUnknownProps) { offset += laaf_util_snprintf_realloc (&dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, ")"); } } offset += laaf_util_snprintf_realloc (&dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, "%s", ANSI_COLOR_RESET); } // offset += laaf_util_snprintf_realloc( &dbg->_dbg_msg, &dbg->_dbg_msg_size, offset, "\n" ); dbg->debug_callback (dbg, (void*)aafi, DEBUG_SRC_ID_TRACE, 0, "", "", 0, dbg->_dbg_msg, dbg->user); /* if end of branch, print one line padding */ if (Obj && (__td->eob || state == TD_ERROR)) aafi_dump_obj (aafi, NULL, __td, 0, -1, ""); } void aafi_dump_obj_no_support (AAF_Iface* aafi, aafObject* Obj, struct trace_dump* __td, int line) { // aafUID_t *DataDefinition = NULL; if (aafUIDCmp (Obj->Class->ID, &AAFClassID_TimelineMobSlot) && aafUIDCmp (Obj->Parent->Class->ID, &AAFClassID_CompositionMob)) { /* this part is handled by aafi_dump_obj() already. */ aafi_dump_obj (aafi, Obj, __td, TD_NOT_SUPPORTED, line, ""); return; // aafObject *Segment = aaf_get_propertyValue( Obj, PID_MobSlot_Segment, &AAFTypeID_SegmentStrongReference ); // // if ( Segment != NULL ) /* req */ { // DataDefinition = get_Component_DataDefinition( aafi, Segment ); // } // } // else { // DataDefinition = get_Component_DataDefinition( aafi, Obj ); } // DataDefinition = get_Component_DataDefinition( aafi, Obj ); aafi_dump_obj (aafi, Obj, __td, TD_NOT_SUPPORTED, line, ""); // aafi_dump_obj( aafi, Obj, __td, WARNING, line, "%s%ls%s", // // aaft_ClassIDToText( aafi->aafd, Obj->Class->ID ), // ((DataDefinition) ? "(Segment DataDefinition: \x1b[38;5;242m" : ""), // ((DataDefinition) ? aaft_DataDefToText( aafi->aafd, DataDefinition ) : L""), // ((DataDefinition) ? ") \x1b[0m" : "") ); } /* #define DUMP_OBJ( aafi, Obj, __td ) \ aafi_dump_obj( aafi, Obj, __td, OK, __LINE__, "" ); #define DUMP_OBJ_ERROR( aafi, Obj, __td, ... ) \ (__td)->eob = 1; \ aafi_dump_obj( aafi, Obj, __td, ERROR, __LINE__, __VA_ARGS__ ); #define DUMP_OBJ_WARNING( aafi, Obj, __td, ... ) \ aafi_dump_obj( aafi, Obj, __td, WARNING, __LINE__, __VA_ARGS__ ); #define DUMP_OBJ_INFO( aafi, Obj, __td, ... ) \ aafi_dump_obj( aafi, Obj, __td, OK, __LINE__, __VA_ARGS__ ); #define DUMP_OBJ_NO_SUPPORT( aafi, Obj, __td ) \ (__td)->eob = 1; \ aafi_dump_obj_no_support( aafi, Obj, __td, __LINE__ ); \ // aaf_dump_ObjectProperties( aafi->aafd, Obj ); */ static wchar_t* build_unique_audiofilename (AAF_Iface* aafi, aafiAudioEssence* audioEssence) { wchar_t* unique = NULL; size_t unique_size = 0; size_t file_name_len = 0; if (audioEssence->file_name) { file_name_len = wcslen (audioEssence->file_name); unique_size = file_name_len + 1 + 4; // +4 = "_001" unique_size = (unique_size < AAFUID_PRINTED_LEN + 1) ? AAFUID_PRINTED_LEN + 1 : unique_size; // debug("%lu, %lu", file_name_len, unique_size); unique = malloc (sizeof (wchar_t) * unique_size); if (unique == NULL) { error ("Could not allocate memory : %s", strerror (errno)); return NULL; } if (swprintf (unique, unique_size, L"%" WPRIws, audioEssence->file_name) < 0) { error ("Could not prepare unique filename"); return NULL; } } else { file_name_len = strlen ("unknown"); unique_size = file_name_len + 1 + 4; // +4 = "_001" unique_size = (unique_size < AAFUID_PRINTED_LEN + 1) ? AAFUID_PRINTED_LEN + 1 : unique_size; unique = malloc (sizeof (wchar_t) * unique_size); if (unique == NULL) { error ("Could not allocate memory : %s", strerror (errno)); return NULL; } if (swprintf (unique, unique_size, L"unknown") < 0) { error ("Could not prepare unique filename"); return NULL; } } // debug( "%ls", unique ); aafiAudioEssence* ae = NULL; if (aafi->ctx.options.forbid_nonlatin_filenames && laaf_util_wstr_contains_nonlatin (unique)) { aafUID_t* uuid = &(audioEssence->sourceMobID->material); int rc = swprintf (unique, unique_size, L"%08x-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x", uuid->Data1, uuid->Data2, uuid->Data3, uuid->Data4[0], uuid->Data4[1], uuid->Data4[2], uuid->Data4[3], uuid->Data4[4], uuid->Data4[5], uuid->Data4[6], uuid->Data4[7]); if (rc < 0) { error ("Failed to set unique filename with SourceMobID UID"); free (unique); return NULL; } audioEssence->unique_file_name = unique; return unique; } int index = 0; foreachEssence (ae, aafi->Audio->Essences) { if (ae->unique_file_name != NULL && wcscmp (ae->unique_file_name, unique) == 0) { if (swprintf (unique + file_name_len, (unique_size - file_name_len), L"_%i", ++index) < 0) { error ("Failed to increment unique filename"); free (unique); return NULL; } ae = aafi->Audio->Essences; // check again // debug( "%ls", unique ); } } audioEssence->unique_file_name = unique; // debug( "%ls", audioEssence->wunique_file_name ); return unique; } static wchar_t* build_unique_videofilename (AAF_Iface* aafi, aafiVideoEssence* videoEssence) { /* TODO 1024 should be a macro ! */ wchar_t* unique = calloc (sizeof (wchar_t), 1024); size_t file_name_len = wcslen (videoEssence->file_name); // debug( "%i", file_name_len ); memcpy (unique, videoEssence->file_name, (file_name_len + 1) * sizeof (wchar_t)); // debug( "%ls", unique ); aafiVideoEssence* ve = NULL; if (1) { size_t i = 0; for (; i < file_name_len; i++) { /* if char is out of the Basic Latin range */ if (unique[i] > 0xff) { // debug( "MobID : %ls", aaft_MobIDToText( videoEssence->sourceMobID ) ); aafUID_t* uuid = &(videoEssence->sourceMobID->material); swprintf (unique, 1024, L"%08x-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x", uuid->Data1, uuid->Data2, uuid->Data3, uuid->Data4[0], uuid->Data4[1], uuid->Data4[2], uuid->Data4[3], uuid->Data4[4], uuid->Data4[5], uuid->Data4[6], uuid->Data4[7]); videoEssence->unique_file_name = unique; return unique; } } } int id = 0; foreachEssence (ve, aafi->Video->Essences) { if (ve->unique_file_name != NULL && wcscmp (ve->unique_file_name, unique) == 0) { swprintf (unique + file_name_len, (1024 - file_name_len), L"_%i", ++id); // debug( "%ls", unique ); ve = aafi->Video->Essences; // check again } } videoEssence->unique_file_name = unique; // debug( "%ls", videoEssence->wunique_file_name ); return unique; } static aafObject* get_Object_Ancestor (AAF_Iface* aafi, aafObject* Obj, const aafUID_t* ClassID) { (void)aafi; /* * NOTE : AAFClassID_ContentStorage is the container of Mob and EssenceData, * not of Identification, Dictionary and MetaDictionary. If needed, the func * should work for them too thanks to Obj != NULL. */ for (; Obj != NULL && aafUIDCmp (Obj->Class->ID, &AAFClassID_ContentStorage) == 0; Obj = Obj->Parent) { if (aafUIDCmp (Obj->Class->ID, ClassID)) return Obj; /* Also work with abstract class */ else if (aafUIDCmp (ClassID, &AAFClassID_Mob) && (aafUIDCmp (Obj->Class->ID, &AAFClassID_CompositionMob) || aafUIDCmp (Obj->Class->ID, &AAFClassID_MasterMob) || aafUIDCmp (Obj->Class->ID, &AAFClassID_SourceMob))) return Obj; else if (aafUIDCmp (ClassID, &AAFClassID_MobSlot) && (aafUIDCmp (Obj->Class->ID, &AAFClassID_TimelineMobSlot) || aafUIDCmp (Obj->Class->ID, &AAFClassID_StaticMobSlot) || aafUIDCmp (Obj->Class->ID, &AAFClassID_EventMobSlot))) return Obj; } return NULL; } /* **************************************************************************** * D i c t i o n a r y * ****************************************************************************/ static aafUID_t* get_Component_DataDefinition (AAF_Iface* aafi, aafObject* Component) { aafWeakRef_t* weakRef = aaf_get_propertyValue (Component, PID_Component_DataDefinition, &AAFTypeID_DataDefinitionWeakReference); if (weakRef == NULL) { warning ("Missing Component::DataDefinition."); return NULL; } aafObject* DataDefinition = aaf_get_ObjectByWeakRef (aafi->aafd->DataDefinition, weakRef); if (DataDefinition == NULL) { warning ("Could not retrieve WeakRef from Dictionary::DataDefinition."); return NULL; } aafUID_t* DataIdentification = aaf_get_propertyValue (DataDefinition, PID_DefinitionObject_Identification, &AAFTypeID_AUID); if (DataIdentification == NULL) { warning ("Missing DataDefinition's DefinitionObject::Identification."); return NULL; } return DataIdentification; } // static aafUID_t * get_FileDescriptor_ContainerFormat( AAF_Iface *aafi, aafObject *FileDescriptor ) // { // aafWeakRef_t *ContainerDefWeakRef = aaf_get_propertyValue( FileDescriptor, PID_FileDescriptor_ContainerFormat ); // // if ( ContainerDefWeakRef == NULL ) { // warning( "Missing FileDescriptor::ContainerFormat." ); // return NULL; // } // // aafObject *ContainerDefinition = aaf_get_ObjectByWeakRef( aafi->aafd->ContainerDefinition, ContainerDefWeakRef ); // // if ( ContainerDefinition == NULL ) { // warning( "Could not retrieve WeakRef from Dictionary::ContainerDefinitions." ); // return NULL; // } // // // aafUID_t *ContainerIdentification = aaf_get_propertyValue( ContainerDefinition, PID_DefinitionObject_Identification ); // // if ( ContainerIdentification == NULL ) { // warning( "Missing ContainerDefinition's DefinitionObject::Identification." ); // return NULL; // } // // // return ContainerIdentification; // } static aafUID_t* get_OperationGroup_OperationIdentification (AAF_Iface* aafi, aafObject* OperationGroup) { aafWeakRef_t* OperationDefWeakRef = aaf_get_propertyValue (OperationGroup, PID_OperationGroup_Operation, &AAFTypeID_OperationDefinitionWeakReference); if (OperationDefWeakRef == NULL) { error ("Missing OperationGroup::Operation."); return NULL; } aafObject* OperationDefinition = aaf_get_ObjectByWeakRef (aafi->aafd->OperationDefinition, OperationDefWeakRef); if (OperationDefinition == NULL) { error ("Could not retrieve OperationDefinition from dictionary."); return NULL; } aafUID_t* OperationIdentification = aaf_get_propertyValue (OperationDefinition, PID_DefinitionObject_Identification, &AAFTypeID_AUID); if (OperationIdentification == NULL) { error ("Missing DefinitionObject::Identification."); return NULL; } return OperationIdentification; } /* TODO not parameter ? VaryingValue ? */ static aafUID_t* get_Parameter_InterpolationIdentification (AAF_Iface* aafi, aafObject* Parameter) { aafWeakRef_t* InterpolationDefWeakRef = aaf_get_propertyValue (Parameter, PID_VaryingValue_Interpolation, &AAFTypeID_InterpolationDefinitionWeakReference); if (InterpolationDefWeakRef == NULL) { error ("Missing Parameter::Interpolation."); return NULL; } aafObject* InterpolationDefinition = aaf_get_ObjectByWeakRef (aafi->aafd->InterpolationDefinition, InterpolationDefWeakRef); if (InterpolationDefinition == NULL) { error ("Could not find InterpolationDefinition."); return NULL; } aafUID_t* InterpolationIdentification = aaf_get_propertyValue (InterpolationDefinition, PID_DefinitionObject_Identification, &AAFTypeID_AUID); if (InterpolationIdentification == NULL) { error ("Missing Parameter DefinitionObject::Identification."); return NULL; } return InterpolationIdentification; } static aafObject* get_EssenceData_By_MobID (AAF_Iface* aafi, aafMobID_t* MobID) { aafMobID_t* DataMobID = NULL; aafObject* EssenceData = NULL; for (EssenceData = aafi->aafd->EssenceData; EssenceData != NULL; EssenceData = EssenceData->next) { DataMobID = aaf_get_propertyValue (EssenceData, PID_EssenceData_MobID, &AAFTypeID_MobIDType); if (aafMobIDCmp (DataMobID, MobID)) break; } return EssenceData; } // /* TODO is this SourceMobID or SourceID (masterMobID) ??? */ // static aafiAudioEssence * getAudioEssenceBySourceMobID( AAF_Iface *aafi, aafMobID_t *sourceMobID ) // { // aafiAudioEssence * audioEssence = NULL; // // // for ( audioEssence = aafi->Audio->Essences; audioEssence != NULL; audioEssence = audioEssence->next ) { // if ( aafMobIDCmp( audioEssence->masterMobID, sourceMobID ) ) // break; // } // // // return audioEssence; // } // /* TODO is this SourceMobID or SourceID (masterMobID) ??? */ // static aafiVideoEssence * getVideoEssenceBySourceMobID( AAF_Iface *aafi, aafMobID_t *sourceMobID ) // { // aafiVideoEssence * videoEssence = NULL; // // // debug( "%p", aafi->Video->tc ); // debug( "%p", aafi->Video->Essences ); // debug( "%ls", aaft_MobIDToText( sourceMobID ) ); // // // for ( videoEssence = aafi->Video->Essences; videoEssence != NULL; videoEssence = videoEssence->next ) { // if ( aafMobIDCmp( videoEssence->masterMobID, sourceMobID ) ) // break; // } // // // return videoEssence; // } /* **************************************************************************** * E s s e n c e D e s c r i p t o r * **************************************************************************** * * EssenceDescriptor (abs) * | * |--> FileDescriptor (abs) * | | * | |--> WAVEDescriptor * | |--> AIFCDescriptor * | |--> SoundDescriptor * | | | * | | `--> PCMDescriptor * | | * | `--> DigitalImageDescriptor (abs) * | | * | `--> CDCIDescriptor * | * | * |--> PhysicalDescriptor * `--> TapeDescriptor */ static int parse_EssenceDescriptor (AAF_Iface* aafi, aafObject* EssenceDesc, td* __ptd) { struct trace_dump __td; __td_set (__td, __ptd, 0); if (aafUIDCmp (EssenceDesc->Class->ID, &AAFClassID_PCMDescriptor)) { parse_PCMDescriptor (aafi, EssenceDesc, &__td); } else if (aafUIDCmp (EssenceDesc->Class->ID, &AAFClassID_WAVEDescriptor)) { parse_WAVEDescriptor (aafi, EssenceDesc, &__td); } else if (aafUIDCmp (EssenceDesc->Class->ID, &AAFClassID_AIFCDescriptor)) { parse_AIFCDescriptor (aafi, EssenceDesc, &__td); } else if (aafUIDCmp (EssenceDesc->Class->ID, &AAFClassID_SoundDescriptor)) { /* Compressed Audio (MP3, AAC ?). Not encountered yet */ __td.lv++; DUMP_OBJ_NO_SUPPORT (aafi, EssenceDesc, &__td); __td.lv--; } else if (aafUIDCmp (EssenceDesc->Class->ID, &AAFClassID_AES3PCMDescriptor)) { /* Not described in specs, not encountered yet. */ __td.lv++; DUMP_OBJ_NO_SUPPORT (aafi, EssenceDesc, &__td); __td.lv--; } else if (aafUIDCmp (EssenceDesc->Class->ID, &AAFClassID_MultipleDescriptor)) { /* * A MultipleDescriptor contains a vector of FileDescriptor objects and is * used when the file source consists of multiple tracks of essence (e.g MXF). * Each essence track is described by a MobSlots object in the SourceMob and a * FileDescriptor object. The FileDescriptor is linked to the MobSlot by * setting the FileDescriptor::LinkedSlotID property equal to the * MobSlot::SlotID property. * * -> test.aaf */ __td.lv++; DUMP_OBJ_NO_SUPPORT (aafi, EssenceDesc, &__td); __td.lv--; } else if (aafUIDCmp (EssenceDesc->Class->ID, &AAFClassID_CDCIDescriptor)) { parse_CDCIDescriptor (aafi, EssenceDesc, &__td); } else { __td.lv++; DUMP_OBJ_NO_SUPPORT (aafi, EssenceDesc, &__td); __td.lv--; } /* * Locators are a property of EssenceDescriptor. The property holds a vector of * Locators object, that should provide information to help find a file that * contains the essence (WAV, MXF, etc.) or to help find the physical media. * * A Locator can either be a NetworkLocator or a TextLocator. * * A NetworkLocator holds a URLString property : * * p.41 : Absolute Uniform Resource Locator (URL) complying with RFC 1738 or relative * Uniform Resource Identifier (URI) complying with RFC 2396 for file containing * the essence. If it is a relative URI, the base URI is determined from the URI * of the AAF file itself. * Informative note: A valid URL or URI uses a constrained character set and uses * the / character as the path separator. */ aafObject* Locator = NULL; aafObject* Locators = aaf_get_propertyValue (EssenceDesc, PID_EssenceDescriptor_Locator, &AAFTypeID_LocatorStrongReferenceVector); /* opt */ __td.lv++; int i = 0; aaf_foreach_ObjectInSet (&Locator, Locators, NULL) { /* TODO retrieve all locators, then when searching file, try all parsed locators. */ __td.ll[__td.lv] = (Locators->Header->_entryCount > 1) ? (Locators->Header->_entryCount - i++) : 0; parse_Locator (aafi, Locator, &__td); } return 0; } static int parse_DigitalImageDescriptor (AAF_Iface* aafi, aafObject* DIDescriptor, td* __ptd) { struct trace_dump __td; __td_set (__td, __ptd, 0); /* TODO parse and save content to videoEssence */ aafiVideoEssence* videoEssence = aafi->ctx.current_video_essence; //(aafiVideoEssence*)aafi->ctx.current_essence; if (videoEssence == NULL) { DUMP_OBJ_ERROR (aafi, DIDescriptor, &__td, "aafi->ctx.current_video_essence not set"); return -1; } aafRational_t* framerate = aaf_get_propertyValue (DIDescriptor, PID_FileDescriptor_SampleRate, &AAFTypeID_Rational); if (framerate == NULL) { DUMP_OBJ_ERROR (aafi, DIDescriptor, &__td, "Missing PID_FileDescriptor_SampleRate"); return -1; } videoEssence->framerate = framerate; uint32_t* storedHeight = aaf_get_propertyValue (DIDescriptor, PID_DigitalImageDescriptor_StoredHeight, &AAFTypeID_UInt32); if (storedHeight == NULL) { DUMP_OBJ_ERROR (aafi, DIDescriptor, &__td, "Missing PID_DigitalImageDescriptor_StoredHeight"); return -1; } // debug( "storedHeight : %u", *storedHeight ); uint32_t* storedWidth = aaf_get_propertyValue (DIDescriptor, PID_DigitalImageDescriptor_StoredWidth, &AAFTypeID_UInt32); if (storedWidth == NULL) { DUMP_OBJ_ERROR (aafi, DIDescriptor, &__td, "Missing PID_DigitalImageDescriptor_StoredWidth"); return -1; } // debug( "storedWidth : %u", *storedWidth ); uint32_t* displayHeight = aaf_get_propertyValue (DIDescriptor, PID_DigitalImageDescriptor_DisplayHeight, &AAFTypeID_UInt32); if (displayHeight == NULL) { DUMP_OBJ_ERROR (aafi, DIDescriptor, &__td, "Missing PID_DigitalImageDescriptor_DisplayHeight"); return -1; } // debug( "displayHeight : %u", *displayHeight ); uint32_t* displayWidth = aaf_get_propertyValue (DIDescriptor, PID_DigitalImageDescriptor_DisplayWidth, &AAFTypeID_UInt32); if (displayWidth == NULL) { DUMP_OBJ_ERROR (aafi, DIDescriptor, &__td, "Missing PID_DigitalImageDescriptor_DisplayWidth"); return -1; } // debug( "displayWidth : %u", *displayWidth ); aafRational_t* imageAspectRatio = aaf_get_propertyValue (DIDescriptor, PID_DigitalImageDescriptor_ImageAspectRatio, &AAFTypeID_Rational); if (imageAspectRatio == NULL) { DUMP_OBJ_ERROR (aafi, DIDescriptor, &__td, "Missing PID_DigitalImageDescriptor_ImageAspectRatio"); return -1; } // debug( "imageAspectRatio : %i/%i", imageAspectRatio->numerator, imageAspectRatio->denominator ); return 0; } static int parse_CDCIDescriptor (AAF_Iface* aafi, aafObject* CDCIDescriptor, td* __ptd) { struct trace_dump __td; __td_set (__td, __ptd, 1); if (!aaf_get_property (CDCIDescriptor, PID_EssenceDescriptor_Locator)) __td.eob = 1; /* TODO parse CDCI class */ int rc = parse_DigitalImageDescriptor (aafi, CDCIDescriptor, __ptd); if (!rc) DUMP_OBJ (aafi, CDCIDescriptor, &__td); return rc; } static int parse_PCMDescriptor (AAF_Iface* aafi, aafObject* PCMDescriptor, td* __ptd) { struct trace_dump __td; __td_set (__td, __ptd, 1); if (!aaf_get_property (PCMDescriptor, PID_EssenceDescriptor_Locator)) __td.eob = 1; aafiAudioEssence* audioEssence = (aafiAudioEssence*)aafi->ctx.current_essence; if (audioEssence == NULL) { DUMP_OBJ_ERROR (aafi, PCMDescriptor, &__td, "aafi->ctx.current_essence not set"); return -1; } audioEssence->type = AAFI_ESSENCE_TYPE_PCM; /* Duration of the essence in sample units (not edit units !) */ aafPosition_t* length = aaf_get_propertyValue (PCMDescriptor, PID_FileDescriptor_Length, &AAFTypeID_PositionType); if (length == NULL) { /* req */ DUMP_OBJ_ERROR (aafi, PCMDescriptor, &__td, "Missing PID_FileDescriptor_Length"); return -1; } audioEssence->length = *length; uint32_t* channels = aaf_get_propertyValue (PCMDescriptor, PID_SoundDescriptor_Channels, &AAFTypeID_UInt32); if (channels == NULL) { /* req */ DUMP_OBJ_ERROR (aafi, PCMDescriptor, &__td, "Missing PID_SoundDescriptor_Channels"); return -1; } audioEssence->channels = *channels; aafRational_t* samplerate = aaf_get_propertyValue (PCMDescriptor, PID_FileDescriptor_SampleRate, &AAFTypeID_Rational); if (samplerate == NULL) { /* req */ DUMP_OBJ_ERROR (aafi, PCMDescriptor, &__td, "Missing PID_FileDescriptor_SampleRate"); return -1; } if (samplerate->denominator != 1) { DUMP_OBJ_ERROR (aafi, PCMDescriptor, &__td, "PID_FileDescriptor_SampleRate should be integer but is %i/%i", samplerate->numerator, samplerate->denominator); return -1; } audioEssence->samplerate = samplerate->numerator; // if ( aafi->Audio->samplerate >= 0 ) { /* Set global AAF SampleRate, if it equals preceding. Otherwise set to -1 */ // aafi->Audio->samplerate = ( aafi->Audio->samplerate == 0 || aafi->Audio->samplerate == *samplerate ) ? *samplerate : (unsigned)-1; // } uint32_t* samplesize = aaf_get_propertyValue (PCMDescriptor, PID_SoundDescriptor_QuantizationBits, &AAFTypeID_UInt32); // uint32_t in AAF std if (samplesize == NULL) { /* req */ DUMP_OBJ_ERROR (aafi, PCMDescriptor, &__td, "Missing PID_SoundDescriptor_QuantizationBits"); return -1; } if (*samplesize >= (1 << 15)) { DUMP_OBJ_ERROR (aafi, PCMDescriptor, &__td, "PID_SoundDescriptor_QuantizationBits value error : %u", *samplesize); return -1; } audioEssence->samplesize = (int16_t)*samplesize; if (aafi->Audio->samplesize >= 0) { /* Set global AAF SampleSize, if it equals preceding. Otherwise set to -1 */ aafi->Audio->samplesize = (aafi->Audio->samplesize == 0 || (uint16_t)aafi->Audio->samplesize == audioEssence->samplesize) ? audioEssence->samplesize : -1; } /* TODO parse the rest of the class */ DUMP_OBJ (aafi, PCMDescriptor, &__td); return 0; } static int parse_WAVEDescriptor (AAF_Iface* aafi, aafObject* WAVEDescriptor, td* __ptd) { struct trace_dump __td; __td_set (__td, __ptd, 1); if (!aaf_get_property (WAVEDescriptor, PID_EssenceDescriptor_Locator)) __td.eob = 1; aafiAudioEssence* audioEssence = (aafiAudioEssence*)aafi->ctx.current_essence; if (audioEssence == NULL) { DUMP_OBJ_ERROR (aafi, WAVEDescriptor, &__td, "aafi->ctx.current_essence not set"); return -1; } audioEssence->type = AAFI_ESSENCE_TYPE_WAVE; aafProperty* summary = aaf_get_property (WAVEDescriptor, PID_WAVEDescriptor_Summary); if (summary == NULL) { DUMP_OBJ_ERROR (aafi, WAVEDescriptor, &__td, "Missing PID_WAVEDescriptor_Summary"); return -1; } audioEssence->summary = summary; /* * NOTE : Summary is parsed later in "post-processing" aafi_retrieveData(), * to be sure clips and essences are linked, so we are able to fallback on * essence stream in case summary does not contain the full header part. * * TODO parse it here */ DUMP_OBJ (aafi, WAVEDescriptor, &__td); return 0; } static int parse_AIFCDescriptor (AAF_Iface* aafi, aafObject* AIFCDescriptor, td* __ptd) { struct trace_dump __td; __td_set (__td, __ptd, 1); if (!aaf_get_property (AIFCDescriptor, PID_EssenceDescriptor_Locator)) __td.eob = 1; aafiAudioEssence* audioEssence = (aafiAudioEssence*)aafi->ctx.current_essence; if (audioEssence == NULL) { DUMP_OBJ_ERROR (aafi, AIFCDescriptor, &__td, "aafi->ctx.current_essence not set"); return -1; } audioEssence->type = AAFI_ESSENCE_TYPE_AIFC; aafProperty* summary = aaf_get_property (AIFCDescriptor, PID_AIFCDescriptor_Summary); if (summary == NULL) { DUMP_OBJ_ERROR (aafi, AIFCDescriptor, &__td, "Missing PID_AIFCDescriptor_Summary"); return -1; } audioEssence->summary = summary; /* * NOTE : Summary is parsed later in "post-processing" aafi_retrieveData(), * to be sure clips and essences are linked, so we are able to fallback on * essence stream in case summary does not contain the full header part. */ DUMP_OBJ (aafi, AIFCDescriptor, &__td); return 0; } /* * Locator (abs) * | * ,---------------. * | | * NetworkLocator TextLocator */ static int parse_Locator (AAF_Iface* aafi, aafObject* Locator, td* __ptd) { struct trace_dump __td; __td_set (__td, __ptd, 0); if (aafUIDCmp (Locator->Class->ID, &AAFClassID_NetworkLocator)) { parse_NetworkLocator (aafi, Locator, &__td); } else if (aafUIDCmp (Locator->Class->ID, &AAFClassID_TextLocator)) { /* * A TextLocator object provides information to the user to help locate the file * containing the essence or to locate the physical media. The TextLocator is not * intended for applications to use without user intervention. * * TODO what to do with those ??? * never encountered anyway.. */ __td.eob = 1; __td.lv++; DUMP_OBJ_NO_SUPPORT (aafi, Locator, &__td); // wchar_t *name = aaf_get_propertyValue( Locator, PID_TextLocator_Name, &AAFTypeID_String ); // warning( "Got an AAFClassID_TextLocator : \"%ls\"", name ); // free( name ); } else { __td.eob = 1; __td.lv++; DUMP_OBJ_NO_SUPPORT (aafi, Locator, &__td); } return 0; } static int parse_NetworkLocator (AAF_Iface* aafi, aafObject* NetworkLocator, td* __ptd) { struct trace_dump __td; __td_set (__td, __ptd, 1); __td.eob = 1; /* * This holds an URI pointing to the essence file, when it is not embedded. * However, sometimes it holds an URI to the AAF file itself when essence is * embedded so it is not a valid way to test if essence is embedded or not. */ wchar_t* original_file_path = aaf_get_propertyValue (NetworkLocator, PID_NetworkLocator_URLString, &AAFTypeID_String); if (original_file_path == NULL) { /* req */ DUMP_OBJ_ERROR (aafi, NetworkLocator, &__td, "Missing PID_NetworkLocator_URLString"); return -1; } // uriDecodeWString( original_file_path, NULL ); // wurl_decode( original_file_path, original_file_path ); // TODO : What about URIParser lib ?! /* TODO find a better way to check if we're parsing audio */ if (aafi->ctx.current_essence) { aafi->ctx.current_essence->original_file_path = original_file_path; } else if (aafi->ctx.current_video_essence) { aafi->ctx.current_video_essence->original_file_path = original_file_path; } else { DUMP_OBJ_ERROR (aafi, NetworkLocator, &__td, "aafi->ctx.current_essence AND aafi->ctx.current_video_essence not set"); return -1; } DUMP_OBJ_INFO (aafi, NetworkLocator, &__td, ": %ls", original_file_path); return 0; } static int parse_EssenceData (AAF_Iface* aafi, aafObject* EssenceData, td* __ptd) { struct trace_dump __td; __td_set (__td, __ptd, 1); __td.eob = 1; aafiAudioEssence* audioEssence = (aafiAudioEssence*)aafi->ctx.current_essence; if (audioEssence == NULL) { DUMP_OBJ_ERROR (aafi, EssenceData, &__td, "aafi->ctx.current_essence not set"); return -1; } /* * The EssenceData::Data property has the stored form SF_DATA_STREAM, so * it holds the name of the Data stream, which should be located at * /Path/To/EssenceData/DataStream */ wchar_t* StreamName = aaf_get_propertyValue (EssenceData, PID_EssenceData_Data, &AAFTypeID_String); if (StreamName == NULL) { DUMP_OBJ_ERROR (aafi, EssenceData, &__td, "Missing PID_EssenceData_Data"); return -1; } wchar_t DataPath[CFB_PATH_NAME_SZ]; memset (DataPath, 0x00, sizeof (DataPath)); wchar_t* path = aaf_get_ObjectPath (EssenceData); swprintf (DataPath, CFB_PATH_NAME_SZ, L"%" WPRIws L"/%" WPRIws, path, StreamName); free (StreamName); cfbNode* DataNode = cfb_getNodeByPath (aafi->aafd->cfbd, DataPath, 0); if (DataNode == NULL) { DUMP_OBJ_ERROR (aafi, EssenceData, &__td, "Could not retrieve Data stream node %ls", DataPath); return -1; } audioEssence->node = DataNode; audioEssence->is_embedded = 1; /* TODO to be set elsewhere ? */ /* disable raw data byte length, because we want it to be the exact audio length in samples */ // uint64_t dataLen = cfb_getNodeStreamLen( aafi->aafd->cfbd, DataNode ); // // if ( dataLen == 0 ) { // DUMP_OBJ_WARNING( aafi, EssenceData, &__td, "Got 0 Bytes Data stream length" ); // return -1; // } // else { // DUMP_OBJ( aafi, EssenceData, &__td ); // } // // /* NOTE Might be tweaked by aafi_parse_audio_summary() */ // audioEssence->length = dataLen; return 0; } /* **************************************************************************** * C o m p o n e n t * **************************************************************************** * * Component (abs) * | * ,-----------. * | | * Transition Segment (abs) * | * |--> Sequence * |--> Filler * |--> TimeCode * |--> OperationGroup * `--> SourceReference (abs) * | * `--> SourceClip */ static int parse_Component (AAF_Iface* aafi, aafObject* Component, td* __ptd) { struct trace_dump __td; __td_set (__td, __ptd, 0); if (aafUIDCmp (Component->Class->ID, &AAFClassID_Transition)) { /* * A Transition between a Filler and a SourceClip sets a Fade In. * A Transition between a SourceClip and a Filler sets a Fade Out. * A Transition between two SourceClips sets a Cross-Fade. * * Since the Transition applies to the elements that suround it in * the Sequence, the OperationGroup::InputSegments is then left unused. */ parse_Transition (aafi, Component, &__td); } else { aafi_parse_Segment (aafi, Component, &__td); } return 0; } static int parse_Transition (AAF_Iface* aafi, aafObject* Transition, td* __ptd) { struct trace_dump __td; __td_set (__td, __ptd, 1); aafUID_t* DataDefinition = get_Component_DataDefinition (aafi, Transition); if (DataDefinition == NULL) { /* req */ DUMP_OBJ_ERROR (aafi, Transition, &__td, "Could not retrieve DataDefinition"); return -1; } if (!aafUIDCmp (DataDefinition, &AAFDataDef_Sound) && !aafUIDCmp (DataDefinition, &AAFDataDef_LegacySound)) { DUMP_OBJ_ERROR (aafi, Transition, &__td, "Current implementation only supports Transition inside Audio Tracks"); return -1; } int64_t* length = aaf_get_propertyValue (Transition, PID_Component_Length, &AAFTypeID_LengthType); if (length == NULL) { DUMP_OBJ_ERROR (aafi, Transition, &__td, "Missing PID_Component_Length"); return -1; } int flags = 0; if (Transition->prev != NULL && aafUIDCmp (Transition->prev->Class->ID, &AAFClassID_Filler)) { flags |= AAFI_TRANS_FADE_IN; } else if (Transition->next != NULL && aafUIDCmp (Transition->next->Class->ID, &AAFClassID_Filler)) { flags |= AAFI_TRANS_FADE_OUT; } else if (Transition->next != NULL && aafUIDCmp (Transition->next->Class->ID, &AAFClassID_Filler) == 0 && Transition->prev != NULL && aafUIDCmp (Transition->prev->Class->ID, &AAFClassID_Filler) == 0) { flags |= AAFI_TRANS_XFADE; } else { DUMP_OBJ_ERROR (aafi, Transition, &__td, "Could not guess if type is FadeIn, FadeOut or xFade"); return -1; } aafiTimelineItem* Item = aafi_newTimelineItem (aafi, aafi->ctx.current_track, AAFI_TRANS); aafiTransition* Trans = Item->data; //(aafiTransition*)&Item->data; Trans->len = *length; Trans->flags = flags; int missing_cutpt = 0; aafPosition_t* cut_point = aaf_get_propertyValue (Transition, PID_Transition_CutPoint, &AAFTypeID_PositionType); if (cut_point == NULL) { /* req */ // DUMP_OBJ_WARNING( aafi, Transition, &__td, "Missing PID_Transition_CutPoint : Setting to Trans->len/2" ); missing_cutpt = 1; Trans->cut_pt = Trans->len / 2; // set default cutpoint to the middle of transition } else { Trans->cut_pt = *cut_point; } aafObject* OpGroup = aaf_get_propertyValue (Transition, PID_Transition_OperationGroup, &AAFTypeID_OperationGroupStrongReference); if (OpGroup != NULL) { /* req */ if (missing_cutpt) { DUMP_OBJ_WARNING (aafi, Transition, &__td, "Missing PID_Transition_CutPoint : Setting to Trans->len/2"); } else { DUMP_OBJ (aafi, Transition, &__td); } /* * Don't handle parse_OperationGroup() return code, since it should * always fallback to default in case of failure. */ aafi->ctx.current_transition = Trans; parse_OperationGroup (aafi, OpGroup, &__td); aafi->ctx.current_transition = NULL; } else { /* Setting fade to default */ __td.eob = 1; if (missing_cutpt) { DUMP_OBJ_WARNING (aafi, Transition, &__td, "Missing PID_Transition_CutPoint AND PID_Transition_OperationGroup : Setting to Trans->len/2; Linear"); } else { DUMP_OBJ_WARNING (aafi, Transition, &__td, "Missing PID_Transition_OperationGroup : Setting to Linear interpolation"); } Trans->flags |= (AAFI_INTERPOL_LINEAR | AAFI_TRANS_SINGLE_CURVE); Trans->time_a = calloc (2, sizeof (aafRational_t)); Trans->value_a = calloc (2, sizeof (aafRational_t)); Trans->time_a[0].numerator = 0; Trans->time_a[0].denominator = 0; Trans->time_a[1].numerator = 1; Trans->time_a[1].denominator = 1; if (Trans->flags & AAFI_TRANS_FADE_IN || Trans->flags & AAFI_TRANS_XFADE) { Trans->value_a[0].numerator = 0; Trans->value_a[0].denominator = 0; Trans->value_a[1].numerator = 1; Trans->value_a[1].denominator = 1; } else if (Trans->flags & AAFI_TRANS_FADE_OUT) { Trans->value_a[0].numerator = 1; Trans->value_a[0].denominator = 1; Trans->value_a[1].numerator = 0; Trans->value_a[1].denominator = 0; } } aafi->ctx.current_track->current_pos -= *length; return 0; } static int parse_NestedScope (AAF_Iface* aafi, aafObject* NestedScope, td* __ptd) { struct trace_dump __td; __td_set (__td, __ptd, 1); aafObject* Slot = NULL; aafObject* Slots = aaf_get_propertyValue (NestedScope, PID_NestedScope_Slots, &AAFUID_NULL); if (Slots == NULL) { DUMP_OBJ_ERROR (aafi, NestedScope, &__td, "Missing PID_NestedScope_Slots"); return -1; } DUMP_OBJ (aafi, NestedScope, &__td); int i = 0; aaf_foreach_ObjectInSet (&Slot, Slots, NULL) { __td.ll[__td.lv] = (Slots->Header->_entryCount > 1) ? (Slots->Header->_entryCount - i++) : 0; //(MobSlot->next) ? 1 : 0; aafi_parse_Segment (aafi, Slot, &__td); } /* TODO should we take aafi_parse_Segment() return code into account ? */ return 0; } int aafi_parse_Segment (AAF_Iface* aafi, aafObject* Segment, td* __ptd) { struct trace_dump __td; __td_set (__td, __ptd, 0); if (aafUIDCmp (Segment->Class->ID, &AAFClassID_Sequence)) { return parse_Sequence (aafi, Segment, &__td); } else if (aafUIDCmp (Segment->Class->ID, &AAFClassID_SourceClip)) { return parse_SourceClip (aafi, Segment, &__td); } else if (aafUIDCmp (Segment->Class->ID, &AAFClassID_OperationGroup)) { return parse_OperationGroup (aafi, Segment, &__td); } else if (aafUIDCmp (Segment->Class->ID, &AAFClassID_Filler)) { return parse_Filler (aafi, Segment, &__td); } else if (aafUIDCmp (Segment->Class->ID, &AAFClassID_Selector)) { return parse_Selector (aafi, Segment, &__td); } else if (aafUIDCmp (Segment->Class->ID, &AAFClassID_NestedScope)) { return parse_NestedScope (aafi, Segment, &__td); } else if (aafUIDCmp (Segment->Class->ID, &AAFClassID_Timecode)) { /* * TODO can contain sequence ? other Timecode SMPTE .. */ return parse_Timecode (aafi, Segment, &__td); } else if (aafUIDCmp (Segment->Class->ID, &AAFClassID_DescriptiveMarker)) { if (resolve_AAF (aafi)) { resolve_parse_aafObject_DescriptiveMarker (aafi, Segment, &__td); } else { __td.lv++; DUMP_OBJ_NO_SUPPORT (aafi, Segment, &__td); return -1; } } else if (aafUIDCmp (Segment->Class->ID, &AAFClassID_EssenceGroup)) { /* * Should provide support for multiple essences representing the same * source material with different resolution, compression, codec, etc. * * TODO To be tested with Avid and rendered effects. */ __td.lv++; DUMP_OBJ_NO_SUPPORT (aafi, Segment, &__td); return -1; } else { __td.lv++; DUMP_OBJ_NO_SUPPORT (aafi, Segment, &__td); } return 0; } static int parse_Filler (AAF_Iface* aafi, aafObject* Filler, td* __ptd) { struct trace_dump __td; __td_set (__td, __ptd, 1); __td.eob = 1; aafUID_t* DataDefinition = get_Component_DataDefinition (aafi, Filler); if (DataDefinition == NULL) { /* req */ DUMP_OBJ_ERROR (aafi, Filler, &__td, "Could not retrieve DataDefinition"); return -1; } if (aafUIDCmp (Filler->Parent->Class->ID, &AAFClassID_TimelineMobSlot)) { /* * Just an empty track, do nothing. */ } else if (aafUIDCmp (Filler->Parent->Class->ID, &AAFClassID_Sequence) || aafUIDCmp (Filler->Parent->Class->ID, &AAFClassID_Selector)) { /* * This represents an empty space on the timeline, between two clips * which is Component::Length long. */ int64_t* length = aaf_get_propertyValue (Filler, PID_Component_Length, &AAFTypeID_LengthType); if (length == NULL) { /* probably req for Filler */ DUMP_OBJ_ERROR (aafi, Filler, &__td, "Missing PID_Component_Length"); return -1; } if (aafUIDCmp (DataDefinition, &AAFDataDef_Sound) || aafUIDCmp (DataDefinition, &AAFDataDef_LegacySound)) { aafi->ctx.current_track->current_pos += *length; } else if (aafUIDCmp (DataDefinition, &AAFDataDef_Picture) || aafUIDCmp (DataDefinition, &AAFDataDef_LegacyPicture)) { aafi->Video->Tracks->current_pos += *length; } } else { DUMP_OBJ_NO_SUPPORT (aafi, Filler, &__td); return -1; } DUMP_OBJ (aafi, Filler, &__td); return 0; } static int parse_Sequence (AAF_Iface* aafi, aafObject* Sequence, td* __ptd) { struct trace_dump __td; __td_set (__td, __ptd, 1); aafObject* Component = NULL; aafObject* Components = aaf_get_propertyValue (Sequence, PID_Sequence_Components, &AAFTypeID_ComponentStrongReferenceVector); if (Components == NULL) { /* req */ DUMP_OBJ_ERROR (aafi, Sequence, &__td, "Missing PID_Sequence_Components"); return -1; } DUMP_OBJ (aafi, Sequence, &__td); int i = 0; aaf_foreach_ObjectInSet (&Component, Components, NULL) { __td.ll[__td.lv] = (Components->Header->_entryCount > 1) ? (Components->Header->_entryCount - i++) : 0; //(MobSlot->next) ? 1 : 0; parse_Component (aafi, Component, &__td); } return 0; } static int parse_Timecode (AAF_Iface* aafi, aafObject* Timecode, td* __ptd) { struct trace_dump __td; __td_set (__td, __ptd, 1); __td.eob = 1; aafPosition_t* tc_start = aaf_get_propertyValue (Timecode, PID_Timecode_Start, &AAFTypeID_PositionType); if (tc_start == NULL) { DUMP_OBJ_ERROR (aafi, Timecode, &__td, "Missing PID_Timecode_Start"); return -1; } uint16_t* tc_fps = aaf_get_propertyValue (Timecode, PID_Timecode_FPS, &AAFTypeID_UInt16); if (tc_fps == NULL) { DUMP_OBJ_ERROR (aafi, Timecode, &__td, "Missing PID_Timecode_FPS"); return -1; } uint8_t* tc_drop = aaf_get_propertyValue (Timecode, PID_Timecode_Drop, &AAFTypeID_UInt8); if (tc_drop == NULL) { DUMP_OBJ_ERROR (aafi, Timecode, &__td, "Missing PID_Timecode_Drop"); return -1; } /* TODO this should be retrieved directly from TimelineMobSlot */ aafObject* ParentMobSlot = get_Object_Ancestor (aafi, Timecode, &AAFClassID_MobSlot); if (ParentMobSlot == NULL) { DUMP_OBJ_ERROR (aafi, Timecode, &__td, "Could not retrieve parent MobSlot"); return -1; } aafRational_t* tc_edit_rate = aaf_get_propertyValue (ParentMobSlot, PID_TimelineMobSlot_EditRate, &AAFTypeID_Rational); if (tc_edit_rate == NULL) { DUMP_OBJ_ERROR (aafi, Timecode, &__td, "Missing parent MobSlot PID_TimelineMobSlot_EditRate"); return -1; } if (aafi->Timecode) { DUMP_OBJ_WARNING (aafi, Timecode, &__td, "Timecode was already set, ignoring (%lu, %u fps)", *tc_start, *tc_fps); return -1; } /* TODO allocate in specific function */ aafiTimecode* tc = calloc (sizeof (aafiTimecode), sizeof (unsigned char)); if (tc == NULL) { DUMP_OBJ_ERROR (aafi, Timecode, &__td, "calloc() : %s", strerror (errno)); return -1; } tc->start = *tc_start; tc->fps = *tc_fps; tc->drop = *tc_drop; tc->edit_rate = tc_edit_rate; aafi->Timecode = tc; DUMP_OBJ (aafi, Timecode, &__td); return 0; } static int parse_OperationGroup (AAF_Iface* aafi, aafObject* OpGroup, td* __ptd) { struct trace_dump __td; __td_set (__td, __ptd, 1); if (!aaf_get_property (OpGroup, PID_OperationGroup_InputSegments) && !aaf_get_property (OpGroup, PID_OperationGroup_Parameters)) { __td.eob = 1; } aafObject* ParentMob = get_Object_Ancestor (aafi, OpGroup, &AAFClassID_Mob); if (ParentMob == NULL) { DUMP_OBJ_ERROR (aafi, OpGroup, &__td, "Could not retrieve parent Mob"); return -1; } if (!aafUIDCmp (ParentMob->Class->ID, &AAFClassID_CompositionMob)) { DUMP_OBJ_ERROR (aafi, OpGroup, &__td, "OperationGroup parser is currently implemented for AAFClassID_CompositionMob children only"); return -1; } aafUID_t* OperationIdentification = get_OperationGroup_OperationIdentification (aafi, OpGroup); /* PRINT OPERATIONDEFINITIONS */ // aafObject * Parameters = aaf_get_propertyValue( OpGroup, PID_OperationGroup_Parameters ); // // if ( Parameters ) { // aafObject * Param = NULL; // // aaf_foreach_ObjectInSet( &Param, Parameters, NULL ) { // aafUID_t *ParamDef = aaf_get_propertyValue( Param, PID_Parameter_Definition, &AAFTypeID_AUID ); // debug( " OpDef %ls (%ls) | %ls", aaft_OperationDefToText(aafi->aafd, OperationIdentification), AUIDToText( ParamDef ), aaft_ParameterToText( aafi->aafd, ParamDef ) ); // } // } // if ( aafUIDCmp( aafi->ctx.Mob->Class->ID, &AAFClassID_MasterMob ) ) { // // /* // * TODO: This was seen in the spec, but never encountered in real world. // */ // // aafi_trace_obj( aafi, Segment, ANSI_COLOR_RED ); // error( "MobSlot::Segment > OperationGroup Not implemented yet." ); // return -1; // // } int rc = 0; if (aafUIDCmp (OpGroup->Parent->Class->ID, &AAFClassID_Transition)) { aafiTransition* Trans = aafi->ctx.current_transition; if (aafUIDCmp (OperationIdentification, &AAFOperationDef_MonoAudioDissolve)) { /* * Mono Audio Dissolve (Fade, Cross Fade) * * The same parameter (curve/level) is applied to the outgoing fade on first * clip (if any) and to the incoming fade on second clip (if any). */ // __td.eob = 1; Trans->flags |= AAFI_TRANS_SINGLE_CURVE; int set_default = 0; aafObject* Param = NULL; aafObject* Parameters = aaf_get_propertyValue (OpGroup, PID_OperationGroup_Parameters, &AAFTypeID_ParameterStrongReferenceVector); /* opt */ if (Parameters) { /* Retrieve AAFParameterDef_Level parameter */ aaf_foreach_ObjectInSet (&Param, Parameters, NULL) { aafUID_t* ParamDef = aaf_get_propertyValue (Param, PID_Parameter_Definition, &AAFTypeID_AUID); if (aafUIDCmp (ParamDef, &AAFParameterDef_Level)) break; } } else { // DUMP_OBJ_WARNING( aafi, OpGroup, &__td, "Missing PID_OperationGroup_Parameters: Falling back to Linear" ); set_default = 1; } if (Param) { DUMP_OBJ (aafi, OpGroup, &__td); if (aaf_get_property (OpGroup, PID_OperationGroup_InputSegments)) { __td.ll[__td.lv] = 2; } if (parse_Parameter (aafi, Param, &__td) < 0) { set_default = 1; } __td.ll[__td.lv] = 0; } else { /* * Do not notify exception since this case is standard compliant : * * ParameterDef_Level (optional; default is a VaryingValue object with * two control points: Value 0 at time 0, and value 1 at time 1) */ __td.eob = 1; DUMP_OBJ (aafi, OpGroup, &__td); // DUMP_OBJ_WARNING( aafi, OpGroup, &__td, "Missing Parameter AAFParameterDef_Level: Setting to Linear" ); set_default = 1; } if (set_default) { /* * ParameterDef_Level (optional; default is a VaryingValue object * with two control points: Value 0 at time 0, and value 1 at time 1) * * This is also a fallback in case of parse_Parameter() failure. */ Trans->flags |= AAFI_INTERPOL_LINEAR; Trans->time_a = calloc (2, sizeof (aafRational_t)); Trans->value_a = calloc (2, sizeof (aafRational_t)); Trans->time_a[0].numerator = 0; Trans->time_a[0].denominator = 0; Trans->time_a[1].numerator = 1; Trans->time_a[1].denominator = 1; if (Trans->flags & AAFI_TRANS_FADE_IN || Trans->flags & AAFI_TRANS_XFADE) { Trans->value_a[0].numerator = 0; Trans->value_a[0].denominator = 0; Trans->value_a[1].numerator = 1; Trans->value_a[1].denominator = 1; } else if (Trans->flags & AAFI_TRANS_FADE_OUT) { Trans->value_a[0].numerator = 1; Trans->value_a[0].denominator = 1; Trans->value_a[1].numerator = 0; Trans->value_a[1].denominator = 0; } } } else if (aafUIDCmp (OperationIdentification, &AAFOperationDef_TwoParameterMonoAudioDissolve)) { DUMP_OBJ_NO_SUPPORT (aafi, OpGroup, &__td); /* Two distinct parameters are used for the outgoing and incoming fades. */ } else if (aafUIDCmp (OperationIdentification, &AAFOperationDef_StereoAudioDissolve)) { DUMP_OBJ_NO_SUPPORT (aafi, OpGroup, &__td); /* TODO Unknown usage and implementation */ } else { DUMP_OBJ_NO_SUPPORT (aafi, OpGroup, &__td); } } else if (aafUIDCmp (OperationIdentification, &AAFOperationDef_AudioChannelCombiner)) { DUMP_OBJ (aafi, OpGroup, &__td); aafObject* InputSegment = NULL; aafObject* InputSegments = aaf_get_propertyValue (OpGroup, PID_OperationGroup_InputSegments, &AAFTypeID_SegmentStrongReferenceVector); __td.ll[__td.lv] = InputSegments->Header->_entryCount; aafi->ctx.current_clip_is_combined = 1; aafi->ctx.current_combined_clip_total_channel = InputSegments->Header->_entryCount; aafi->ctx.current_combined_clip_channel_num = 0; aaf_foreach_ObjectInSet (&InputSegment, InputSegments, NULL) { aafi_parse_Segment (aafi, InputSegment, &__td); aafi->ctx.current_combined_clip_channel_num++; __td.ll[__td.lv]--; } /* * Sets the track format. */ aafiAudioTrack* current_track = (aafiAudioTrack*)aafi->ctx.current_track; aafiTrackFormat_e track_format = AAFI_TRACK_FORMAT_UNKNOWN; if (aafi->ctx.current_combined_clip_total_channel == 2) { track_format = AAFI_TRACK_FORMAT_STEREO; } else if (aafi->ctx.current_combined_clip_total_channel == 6) { track_format = AAFI_TRACK_FORMAT_5_1; } else if (aafi->ctx.current_combined_clip_total_channel == 8) { track_format = AAFI_TRACK_FORMAT_7_1; } else { DUMP_OBJ_ERROR (aafi, OpGroup, &__td, "Unknown track format (%u)", aafi->ctx.current_combined_clip_total_channel); /* * Reset multichannel track context. */ aafi->ctx.current_clip_is_combined = 0; aafi->ctx.current_combined_clip_total_channel = 0; aafi->ctx.current_combined_clip_channel_num = 0; return -1; } if (current_track->format != AAFI_TRACK_FORMAT_NOT_SET && current_track->format != track_format) { DUMP_OBJ_ERROR (aafi, OpGroup, &__td, "Track format (%u) does not match current clip (%u)", current_track->format, track_format); /* * Reset multichannel track context. */ aafi->ctx.current_clip_is_combined = 0; aafi->ctx.current_combined_clip_total_channel = 0; aafi->ctx.current_combined_clip_channel_num = 0; return -1; } current_track->format = track_format; /* * Reset multichannel track context. */ aafi->ctx.current_clip_is_combined = 0; aafi->ctx.current_combined_clip_total_channel = 0; aafi->ctx.current_combined_clip_channel_num = 0; // return; } else if (aafUIDCmp (OperationIdentification, &AAFOperationDef_MonoAudioGain)) { aafObject* Param = NULL; aafObject* Parameters = aaf_get_propertyValue (OpGroup, PID_OperationGroup_Parameters, &AAFTypeID_ParameterStrongReferenceVector); if (Parameters == NULL) { DUMP_OBJ_ERROR (aafi, OpGroup, &__td, "Missing PID_OperationGroup_Parameters"); rc = -1; goto end; // we still have to parse segments } /* Retrieve AAFParameterDef_Amplitude parameter */ aaf_foreach_ObjectInSet (&Param, Parameters, NULL) { aafUID_t* ParamDef = aaf_get_propertyValue (Param, PID_Parameter_Definition, &AAFTypeID_AUID); if (aafUIDCmp (ParamDef, &AAFParameterDef_Amplitude)) break; } if (Param == NULL) { DUMP_OBJ_ERROR (aafi, OpGroup, &__td, "Missing Parameter ParameterDef_Amplitude"); rc = -1; goto end; // we still have to parse segments } DUMP_OBJ (aafi, OpGroup, &__td); __td.ll[__td.lv] = 2; rc = parse_Parameter (aafi, Param, &__td); // if ( rc == 0 ) { // DUMP_OBJ( aafi, OpGroup, &__td ); // } // else { // DUMP_OBJ_ERROR( aafi, OpGroup, &__td, "Failed parsing parameter" ); // } } else if (aafUIDCmp (OperationIdentification, &AAFOperationDef_StereoAudioGain)) { DUMP_OBJ_NO_SUPPORT (aafi, OpGroup, &__td); /* TODO Unknown usage and implementation */ } else if (aafUIDCmp (OperationIdentification, &AAFOperationDef_MonoAudioPan)) { /* TODO Should Only be Track-based (first Segment of TimelineMobSlot.) */ /* * We have to loop because of custom Parameters. * Seen in AVID Media Composer AAFs (test.aaf). TODO ParamDef PanVol_IsTrimGainEffect ? */ aafObject* Param = NULL; aafObject* Parameters = aaf_get_propertyValue (OpGroup, PID_OperationGroup_Parameters, &AAFTypeID_ParameterStrongReferenceVector); if (Parameters == NULL) { DUMP_OBJ_ERROR (aafi, OpGroup, &__td, "Missing PID_OperationGroup_Parameters"); rc = -1; goto end; // we still have to parse segments } /* Retrieve AAFParameterDef_Pan parameter */ aaf_foreach_ObjectInSet (&Param, Parameters, NULL) { aafUID_t* ParamDef = aaf_get_propertyValue (Param, PID_Parameter_Definition, &AAFTypeID_AUID); if (aafUIDCmp (ParamDef, &AAFParameterDef_Pan)) break; } if (Param == NULL) { DUMP_OBJ_ERROR (aafi, OpGroup, &__td, "Missing Parameter ParameterDef_Amplitude"); rc = -1; goto end; // we still have to parse segments } DUMP_OBJ (aafi, OpGroup, &__td); __td.ll[__td.lv] = 2; rc = parse_Parameter (aafi, Param, &__td); // if ( rc == 0 ) { // DUMP_OBJ( aafi, OpGroup, &__td ); // } // else { // DUMP_OBJ_ERROR( aafi, OpGroup, &__td, "Failed parsing parameter" ); // } } else if (aafUIDCmp (OperationIdentification, &AAFOperationDef_MonoAudioMixdown)) { DUMP_OBJ_NO_SUPPORT (aafi, OpGroup, &__td); /* TODO Unknown usage and implementation */ } else { DUMP_OBJ_NO_SUPPORT (aafi, OpGroup, &__td); } end: /* * Parses Segments in the OperationGroup::InputSegments, only if * OperationGroup is not a Transition as a Transition has no InputSegments, * and not an AudioChannelCombiner as they were already parsed. */ if (aafUIDCmp (OpGroup->Parent->Class->ID, &AAFClassID_Transition) == 0 && aafUIDCmp (OperationIdentification, &AAFOperationDef_AudioChannelCombiner) == 0) { aafObject* InputSegment = NULL; aafObject* InputSegments = aaf_get_propertyValue (OpGroup, PID_OperationGroup_InputSegments, &AAFTypeID_SegmentStrongReferenceVector); int i = 0; __td.ll[__td.lv] = (InputSegments) ? InputSegments->Header->_entryCount : 0; aaf_foreach_ObjectInSet (&InputSegment, InputSegments, NULL) { __td.ll[__td.lv] = __td.ll[__td.lv] - i++; aafi_parse_Segment (aafi, InputSegment, &__td); } } /* End of current OperationGroup context. */ aafObject* Obj = OpGroup; for (; Obj != NULL && aafUIDCmp (Obj->Class->ID, &AAFClassID_ContentStorage) == 0; Obj = Obj->Parent) if (!aafUIDCmp (Obj->Class->ID, &AAFClassID_OperationGroup)) break; if (aafUIDCmp (OperationIdentification, &AAFOperationDef_MonoAudioGain)) { if (!aafUIDCmp (Obj->Class->ID, &AAFClassID_TimelineMobSlot)) { if (aafi->ctx.clips_using_gain == 0) { aafi_freeAudioGain (aafi->ctx.current_clip_gain); } if (aafi->ctx.clips_using_automation == 0) { aafi_freeAudioGain (aafi->ctx.current_clip_automation); } /* Clip-based Gain */ aafi->ctx.current_clip_is_muted = 0; aafi->ctx.current_clip_gain = NULL; aafi->ctx.current_clip_automation = NULL; aafi->ctx.clips_using_gain = 0; aafi->ctx.clips_using_automation = 0; } // free( aafi->ctx.current_track->gain ); // aafi->ctx.current_track->gain = NULL; } return rc; } static int parse_SourceClip (AAF_Iface* aafi, aafObject* SourceClip, td* __ptd) { struct trace_dump __td; __td_set (__td, __ptd, 1); __td.hc = 1; // link to MasterMob, SourceMob aafUID_t* DataDefinition = get_Component_DataDefinition (aafi, SourceClip); if (DataDefinition == NULL) { /* req */ DUMP_OBJ_ERROR (aafi, SourceClip, &__td, "Could not retrieve DataDefinition"); return -1; } aafObject* ParentMob = get_Object_Ancestor (aafi, SourceClip, &AAFClassID_Mob); if (ParentMob == NULL) { DUMP_OBJ_ERROR (aafi, SourceClip, &__td, "Could not retrieve parent Mob"); return -1; } aafMobID_t* parentMobID = aaf_get_propertyValue (ParentMob, PID_Mob_MobID, &AAFTypeID_MobIDType); if (parentMobID == NULL) { /* req */ DUMP_OBJ_ERROR (aafi, SourceClip, &__td, "Missing parent Mob PID_Mob_MobID"); return -1; } aafMobID_t* sourceID = aaf_get_propertyValue (SourceClip, PID_SourceReference_SourceID, &AAFTypeID_MobIDType); if (sourceID == NULL) { /* opt */ /* NOTE: PID_SourceReference_SourceID is optionnal, there might be none. */ // DUMP_OBJ_ERROR( aafi, SourceClip, &__td, "Missing PID_SourceReference_SourceID" ); // return -1; } uint32_t* SourceMobSlotID = aaf_get_propertyValue (SourceClip, PID_SourceReference_SourceMobSlotID, &AAFTypeID_UInt32); if (SourceMobSlotID == NULL) { /* req */ DUMP_OBJ_ERROR (aafi, SourceClip, &__td, "Missing PID_SourceReference_SourceMobSlotID"); return -1; } /* * TODO: handle SourceReference::MonoSourceSlotIDs and associated conditional rules. * (Multi-channels) */ aafObject* refMob = NULL; aafObject* refMobSlot = NULL; if (sourceID == NULL) { /* * p.49 : To create a SourceReference that refers to a MobSlot within * the same Mob as the SourceReference, omit the SourceID property. * * [SourceID] Identifies the Mob being referenced. If the property has a * value 0, it means that the Mob owning the SourceReference describes * the original source. * * TODO: in that case, is MobSlots NULL ? */ // sourceID = parentMobID; // refMob = ParentMob; } else { refMob = aaf_get_MobByID (aafi->aafd->Mobs, sourceID); if (refMob == NULL) { DUMP_OBJ_ERROR (aafi, SourceClip, &__td, "Could not retrieve target Mob by ID : %ls", aaft_MobIDToText (sourceID)); return -1; } aafObject* refMobSlots = aaf_get_propertyValue (refMob, PID_Mob_Slots, &AAFTypeID_MobSlotStrongReferenceVector); if (refMobSlots == NULL) { DUMP_OBJ_ERROR (aafi, SourceClip, &__td, "Missing target Mob PID_Mob_Slots"); return -1; } refMobSlot = aaf_get_MobSlotBySlotID (refMobSlots, *SourceMobSlotID); if (refMobSlot == NULL) { /* TODO check if there is a workaround : * AAFInfo --aaf-clips '/home/agfline/Developpement/libaaf_testfiles/ADP/ADP_STTRACK_CLIPGAIN_TRACKGAIN_XFADE_NOOPTONEXPORT.aaf' */ DUMP_OBJ_ERROR (aafi, SourceClip, &__td, "Could not retrieve target MobSlot ID : %u", *SourceMobSlotID); return -1; } } /* *** Clip *** */ if (aafUIDCmp (ParentMob->Class->ID, &AAFClassID_CompositionMob)) { // DUMP_OBJ( aafi, SourceClip, &__td ); int64_t* length = aaf_get_propertyValue (SourceClip, PID_Component_Length, &AAFTypeID_LengthType); if (length == NULL) { DUMP_OBJ_ERROR (aafi, SourceClip, &__td, "Missing PID_Component_Length"); return -1; } int64_t* startTime = aaf_get_propertyValue (SourceClip, PID_SourceClip_StartTime, &AAFTypeID_PositionType); if (startTime == NULL) { DUMP_OBJ_ERROR (aafi, SourceClip, &__td, "Missing PID_SourceClip_StartTime"); return -1; } struct aafiContext ctxBackup; aafUID_t* CurrentUsageCode = aaf_get_propertyValue (ParentMob, PID_Mob_UsageCode, &AAFTypeID_UsageType); if (CurrentUsageCode == NULL) { /* NOTE: PID_Mob_UsageCode is optionnal, there might be none. */ // DUMP_OBJ_ERROR( aafi, SourceClip, &__td, "Missing PID_Mob_UsageCode" ); // return -1; } // if ( aafUIDCmp( aafi->aafd->Header.OperationalPattern, &AAFOPDef_EditProtocol ) ) { // if ( (CurrentUsageCode && aafUIDCmp( CurrentUsageCode, &AAFUsage_SubClip )) || CurrentUsageCode == NULL ) { // aafi_trace_obj( aafi, SourceClip, ANSI_COLOR_YELLOW ); // } // else { // aafi_trace_obj( aafi, SourceClip, ANSI_COLOR_MAGENTA ); // } /* * If SourceClip points to a CompositionMob instead of a MasterMob, we * are at the begining (or inside) a derivation chain. */ if (aafUIDCmp (refMob->Class->ID, &AAFClassID_CompositionMob)) { // debug( "REF TO SUBCLIP" ); // // debug( "SourceClip::SourceID : %ls", aaft_MobIDToText( sourceID ) ); // debug( "CurrentMob::MobID : %ls", aaft_MobIDToText( parentMobID ) ); // debug( "SourceClip::SourceMobSlotID : %i", *SourceMobSlotID ); // debug( "UsageCode : %ls", aaft_UsageCodeToText( UsageCode ) ); if (refMobSlot == NULL) { /* TODO isn't it already checked above ? */ DUMP_OBJ_ERROR (aafi, SourceClip, &__td, "Missing target MobSlot"); return -1; } DUMP_OBJ (aafi, SourceClip, &__td); /* Only to print trace */ __td.lv++; DUMP_OBJ (aafi, refMob, &__td); // __td.lv++; memcpy (&ctxBackup, &(aafi->ctx), sizeof (struct aafiContext)); RESET_CONTEXT (aafi->ctx); aafi->ctx.current_track = ctxBackup.current_track; aafi->ctx.is_inside_derivation_chain = 1; parse_MobSlot (aafi, refMobSlot, &__td); void* new_clip = aafi->ctx.current_clip; memcpy (&(aafi->ctx), &ctxBackup, sizeof (struct aafiContext)); if (aafUIDCmp (DataDefinition, &AAFDataDef_Sound) || aafUIDCmp (DataDefinition, &AAFDataDef_LegacySound)) { aafi->ctx.current_clip = (aafiAudioClip*)new_clip; if (new_clip && aafUIDCmp (CurrentUsageCode, &AAFUsage_TopLevel)) { /* * All derivation chain calls ended. * * We came back at level zero of parse_SourceClip() nested calls, so * the clip and its source was added, we only have to set its length, * offset and gain with correct values. * * TODO: aafi->current_clip pointer to new_clip instead ? */ ((aafiAudioClip*)new_clip)->len = *length; ((aafiAudioClip*)new_clip)->essence_offset = *startTime; ((aafiAudioClip*)new_clip)->gain = aafi->ctx.current_clip_gain; ((aafiAudioClip*)new_clip)->automation = aafi->ctx.current_clip_automation; ((aafiAudioClip*)new_clip)->mute = aafi->ctx.current_clip_is_muted; aafi->ctx.clips_using_gain++; aafi->ctx.clips_using_automation++; aafi->ctx.current_track->current_pos += ((aafiAudioClip*)new_clip)->len; } } else if (aafUIDCmp (DataDefinition, &AAFDataDef_Picture) || aafUIDCmp (DataDefinition, &AAFDataDef_LegacyPicture)) { if (new_clip && aafUIDCmp (CurrentUsageCode, &AAFUsage_TopLevel)) { /* * All derivation chain calls ended. * * We came back at level zero of parse_SourceClip() nested calls, so * the clip and its source was added, we only have to set its length, * offset and gain with correct values. */ ((aafiVideoClip*)new_clip)->len = *length; ((aafiVideoClip*)new_clip)->essence_offset = *startTime; aafi->Video->Tracks->current_pos += ((aafiVideoClip*)new_clip)->len; } } return 0; } else if (aafUIDCmp (refMob->Class->ID, &AAFClassID_MasterMob)) { /* * We are inside the derivation chain and we reached the SourceClip * pointing to MasterMob (the audio essence). * * Thus, we can add the clip and parse the audio essence normaly. */ } } if (aafUIDCmp (DataDefinition, &AAFDataDef_Sound) || aafUIDCmp (DataDefinition, &AAFDataDef_LegacySound)) { // if ( *length == 1 ) { // /* // * If length equals 1 EditUnit, the clip is probably a padding for "Media Composer Compatibility". // * Therefore, we don't need it. // * // * TODO BUT this could also be some rendered fade.. we should find a way to distinguish between the two. // */ // // // aaf_dump_ObjectProperties( aafi->aafd, SourceClip ); // // warning( "Got a 1 EU length clip, probably some NLE compatibility padding : Skipping." ); // // // // if ( aafi->ctx.current_track_is_multichannel == 0 ) { // aafi->ctx.current_pos += *length; // } // else { // aafi->ctx.current_multichannel_track_clip_length = *length; // } // // return -1; // } if (aafi->ctx.current_clip_is_combined && aafi->ctx.current_combined_clip_channel_num > 0) { /* * Parsing multichannel audio clip (AAFOperationDef_AudioChannelCombiner) * We already parsed first SourceClip in AAFOperationDef_AudioChannelCombiner. * We just have to check everything match for all clips left (each clip represents a channel) */ if (aafi->ctx.current_clip->len != *length) { DUMP_OBJ_ERROR (aafi, SourceClip, &__td, "SourceClip length does not match first one in AAFOperationDef_AudioChannelCombiner"); return -1; } if (!aafMobIDCmp (aafi->ctx.current_clip->masterMobID, sourceID)) { DUMP_OBJ_ERROR (aafi, SourceClip, &__td, "SourceClip SourceID does not match first one in AAFOperationDef_AudioChannelCombiner"); return -1; } DUMP_OBJ (aafi, SourceClip, &__td); return 0; } /* * Create new clip, only if we are parsing a single mono clip, or if * we are parsing the first SourceClip describing a multichannel clip * inside an AAFOperationDef_AudioChannelCombiner */ aafiTimelineItem* item = aafi_newTimelineItem (aafi, aafi->ctx.current_track, AAFI_AUDIO_CLIP); aafiAudioClip* audioClip = item->data; //(aafiAudioClip*)&item->data; aafi->ctx.clips_using_gain++; aafi->ctx.clips_using_automation++; audioClip->gain = aafi->ctx.current_clip_gain; audioClip->automation = aafi->ctx.current_clip_automation; audioClip->mute = aafi->ctx.current_clip_is_muted; audioClip->pos = aafi->ctx.current_track->current_pos; audioClip->len = *length; audioClip->essence_offset = *startTime; aafi->ctx.current_clip = audioClip; /* * p.49 : To create a SourceReference that refers to a MobSlot within * the same Mob as the SourceReference, omit the SourceID property. * * NOTE: This should not happen here because The "CompositionMob > SourceClip::SourceID" * should always point to the corresponding "MasterMob", that is a different Mob. */ // if ( aafMobIDCmp( aafi->ctx.current_clip->masterMobID, sourceID ) ) { // debug( "SAME_SOURCE_ID : %ls", AUIDToText(sourceID) ); // } else { // debug( "DIFFERENT_SOURCE_ID : %ls", AUIDToText(sourceID) ); // } audioClip->masterMobID = sourceID; // if ( audioClip->masterMobID == NULL ) { // audioClip->masterMobID = aaf_get_propertyValue( ParentMob, PID_Mob_MobID, &AAFTypeID_MobIDType ); // warning( "Missing SourceReference::SourceID, retrieving from parent Mob." ); // } if (!aafi->ctx.is_inside_derivation_chain) { /* * We DO NOT update current pos when SourceClip belongs to a sub CompositionMob * because in that case, current pos was already updated by initial SourceClip * pointing to AAFClassID_CompositionMob 04606│├──◻ AAFClassID_TimelineMobSlot [slot:16 track:8] (DataDef : AAFDataDef_LegacySound) 02064││ └──◻ AAFClassID_Sequence 02037││ ├──◻ AAFClassID_Filler ││ │ 02502││ ├──◻ AAFClassID_OperationGroup (OpIdent: AAFOperationDef_MonoAudioGain) (MetaProps: ComponentAttributeList[0xffcc]) 03780││ │ ├──◻ AAFClassID_ConstantValue POS UPDATED HERE --> └──◻ AAFClassID_SourceClip 02842││ │ └──◻ AAFClassID_CompositionMob (UsageCode: AAFUsage_AdjustedClip) : Islamic Call to Prayer - Amazing Adhan by Edris Aslami.mp3.new.01 (MetaProps: MobAttributeList[0xfff9] ConvertFrameRate[0xfff8]) 04606││ │ └──◻ AAFClassID_TimelineMobSlot [slot:2 track:2] (DataDef : AAFDataDef_LegacySound) 02502││ │ └──◻ AAFClassID_OperationGroup (OpIdent: AAFOperationDef_MonoAudioGain) 03780││ │ ├──◻ AAFClassID_ConstantValue POS NOT UPDATED HERE ------------------> └──◻ AAFClassID_SourceClip 03085││ │ └──◻ AAFClassID_MasterMob (UsageCode: n/a) : Islamic Call to Prayer - Amazing Adhan by Edris Aslami.mp3.new.01 (MetaProps: AppCode[0xfffa]) 04705││ │ └──◻ AAFClassID_TimelineMobSlot 03305││ │ └──◻ AAFClassID_SourceClip 04412││ │ ├──◻ AAFClassID_SourceMob (UsageCode: n/a) : Islamic Call to Prayer - Amazing Adhan by Edris Aslami.mp3 (MetaProps: MobAttributeList[0xfff9]) 01400││ │ │ └──◻ AAFClassID_WAVEDescriptor 01555││ │ │ └──◻ AAFClassID_NetworkLocator : file:///MEDIA2/2199_Rapport_Astellas Main Content/audio/AX TEST.aaf ││ │ │ 01800││ ├──◻ AAFClassID_Transition 02283││ │ └──◻ AAFClassID_OperationGroup (OpIdent: AAFOperationDef_MonoAudioDissolve) (MetaProps: ComponentAttributeList[0xffcc]) 03934││ │ └──◻ AAFClassID_VaryingValue ││ │ 02502││ ├──◻ AAFClassID_OperationGroup (OpIdent: AAFOperationDef_MonoAudioGain) (MetaProps: ComponentAttributeList[0xffcc]) 03780││ │ ├──◻ AAFClassID_ConstantValue 02836││ │ └──◻ AAFClassID_SourceClip 02842││ │ └──◻ AAFClassID_CompositionMob (UsageCode: AAFUsage_AdjustedClip) : Islamic Call to Prayer - Amazing Adhan by Edris Aslami.mp3.new.01 (MetaProps: MobAttributeList[0xfff9] ConvertFrameRate[0xfff8]) 04606││ │ └──◻ AAFClassID_TimelineMobSlot [slot:2 track:2] (DataDef : AAFDataDef_LegacySound) 02502││ │ └──◻ AAFClassID_OperationGroup (OpIdent: AAFOperationDef_MonoAudioGain) 03780││ │ ├──◻ AAFClassID_ConstantValue 03080││ │ └──◻ AAFClassID_SourceClip 03085││ │ └──◻ AAFClassID_MasterMob (UsageCode: n/a) : Islamic Call to Prayer - Amazing Adhan by Edris Aslami.mp3.new.01 (MetaProps: AppCode[0xfffa]) 04705││ │ └──◻ AAFClassID_TimelineMobSlot 03270││ │ └──◻ AAFClassID_SourceClip Essence already parsed: Linking with Islamic Call to Prayer - Amazing Adhan by Edris Aslami.mp3.new.01 ││ │ 02037││ └──◻ AAFClassID_Filler */ aafi->ctx.current_track->current_pos += audioClip->len; } if (aafi->ctx.current_clip_is_combined == 0) { if (aafi->ctx.current_track->format != AAFI_TRACK_FORMAT_NOT_SET && aafi->ctx.current_track->format != AAFI_TRACK_FORMAT_MONO) { DUMP_OBJ_ERROR (aafi, SourceClip, &__td, "Track format (%u) does not match current clip (%u)", aafi->ctx.current_track->format, AAFI_TRACK_FORMAT_MONO); } else { aafi->ctx.current_track->format = AAFI_TRACK_FORMAT_MONO; } } if (aafUIDCmp (refMob->Class->ID, &AAFClassID_MasterMob)) { if (refMobSlot == NULL) { /* TODO isn't it already checked above ? */ DUMP_OBJ_ERROR (aafi, SourceClip, &__td, "Missing target MobSlot"); return -1; } DUMP_OBJ (aafi, SourceClip, &__td); /* Only to print trace */ __td.lv++; DUMP_OBJ (aafi, refMob, &__td); memcpy (&ctxBackup, &(aafi->ctx), sizeof (struct aafiContext)); RESET_CONTEXT (aafi->ctx); aafi->ctx.current_track = ctxBackup.current_track; aafi->ctx.current_clip = audioClip; parse_MobSlot (aafi, refMobSlot, &__td); memcpy (&(aafi->ctx), &ctxBackup, sizeof (struct aafiContext)); } else { DUMP_OBJ_ERROR (aafi, SourceClip, &__td, "RefMob isn't MasterMob : %ls", aaft_ClassIDToText (aafi->aafd, refMob->Class->ID)); // parse_CompositionMob( ) return -1; } } else if (aafUIDCmp (DataDefinition, &AAFDataDef_Picture) || aafUIDCmp (DataDefinition, &AAFDataDef_LegacyPicture)) { if (aafi->Video->Tracks->Items) { DUMP_OBJ_ERROR (aafi, SourceClip, &__td, "Current implementation supports only one video clip"); return -1; } /* Add the new clip */ aafiTimelineItem* item = aafi_newTimelineItem (aafi, aafi->Video->Tracks, AAFI_VIDEO_CLIP); aafiVideoClip* videoClip = item->data; //(aafiVideoClip*)&item->data; videoClip->pos = aafi->Video->Tracks->current_pos; videoClip->len = *length; videoClip->essence_offset = *startTime; /* * p.49 : To create a SourceReference that refers to a MobSlot within * the same Mob as the SourceReference, omit the SourceID property. * * NOTE: This should not happen here because The "CompositionMob > SourceClip::SourceID" * should always point to the corresponding "MasterMob", that is a different Mob. */ videoClip->masterMobID = sourceID; // if ( videoClip->masterMobID == NULL ) { // videoClip->masterMobID = aaf_get_propertyValue( ParentMob, PID_Mob_MobID, &AAFTypeID_MobIDType ); // warning( "Missing SourceReference::SourceID, retrieving from parent Mob." ); // } if (!aafUIDCmp (aafi->aafd->Header.OperationalPattern, &AAFOPDef_EditProtocol) || aafUIDCmp (CurrentUsageCode, &AAFUsage_TopLevel)) { /* * NOTE for AAFOPDef_EditProtocol only : * * If SourceClip belongs to a TopLevel Mob, we can update position. * Otherwise, it means we are inside a derivation chain ( ie: TopLevelCompositionMob -> SourceClip -> SubLevel:CompositionMob -> SourceClip ) * and the clip length is not the good one. In that case, position is updated above. */ aafi->Video->Tracks->current_pos += videoClip->len; } aafi->ctx.current_video_clip = videoClip; if (aafUIDCmp (refMob->Class->ID, &AAFClassID_MasterMob)) { if (refMobSlot == NULL) { /* TODO isn't it already checked above ? */ DUMP_OBJ_ERROR (aafi, SourceClip, &__td, "Missing target MobSlot"); return -1; } DUMP_OBJ (aafi, SourceClip, &__td); /* Only to print trace */ __td.lv++; DUMP_OBJ (aafi, refMob, &__td); // memcpy( &ctxBackup, &(aafi->ctx), sizeof(struct aafiContext) ); // // RESET_CONTEXT( aafi->ctx ); parse_MobSlot (aafi, refMobSlot, &__td); // memcpy( &(aafi->ctx), &ctxBackup, sizeof(struct aafiContext) ); } } } /* *** Essence *** */ else if (aafUIDCmp (ParentMob->Class->ID, &AAFClassID_MasterMob)) { aafMobID_t* masterMobID = aaf_get_propertyValue (ParentMob, PID_Mob_MobID, &AAFTypeID_MobIDType); if (masterMobID == NULL) { DUMP_OBJ_ERROR (aafi, SourceClip, &__td, "Could not retrieve parent Mob PID_Mob_MobID"); return -1; } aafObject* ParentMobSlot = get_Object_Ancestor (aafi, SourceClip, &AAFClassID_MobSlot); if (ParentMobSlot == NULL) { DUMP_OBJ_ERROR (aafi, SourceClip, &__td, "Could not retrieve parent MobSlot"); return -1; } uint32_t* masterMobSlotID = aaf_get_propertyValue (ParentMobSlot, PID_MobSlot_SlotID, &AAFTypeID_UInt32); // uint32_t *essenceChannelNum = aaf_get_propertyValue( ParentMobSlot, PID_MobSlot_PhysicalTrackNumber, &AAFTypeID_UInt32 ); // // if ( essenceChannelNum == NULL ) { /* opt */ // debug( "PhysicalTrackNumber: NOT SET" ); // } else { // debug( "PhysicalTrackNumber: %u", *essenceChannelNum ); // } if (aafUIDCmp (DataDefinition, &AAFDataDef_Sound) || aafUIDCmp (DataDefinition, &AAFDataDef_LegacySound)) { /* Check if this Essence has already been retrieved */ // int slotID = MobSlot->Entry->_localKey; // aafObject *Obj = aaf_get_MobByID( aafi->aafd, SourceID ); // debug( "SourceMobID : %ls", aaft_MobIDToText(SourceID) ); // debug( "MasterMobID : %ls", aaft_MobIDToText(mobID) ); if (!aafi->ctx.current_clip) { DUMP_OBJ_ERROR (aafi, SourceClip, &__td, "aafi->ctx.current_clip not set"); return -1; } aafiAudioEssence* audioEssence = NULL; foreachEssence (audioEssence, aafi->Audio->Essences) { if (aafMobIDCmp (audioEssence->sourceMobID, sourceID) && audioEssence->sourceMobSlotID == (unsigned)*SourceMobSlotID) { /* Essence already retrieved */ aafi->ctx.current_clip->Essence = audioEssence; __td.eob = 1; DUMP_OBJ_INFO (aafi, SourceClip, &__td, "Essence already parsed: Linking with %ls", audioEssence->file_name); return 0; } } /* new Essence, carry on. */ audioEssence = aafi_newAudioEssence (aafi); aafi->ctx.current_essence = audioEssence; audioEssence->masterMobSlotID = *masterMobSlotID; audioEssence->masterMobID = masterMobID; audioEssence->file_name = aaf_get_propertyValue (ParentMob, PID_Mob_Name, &AAFTypeID_String); if (audioEssence->file_name == NULL) { debug ("Missing MasterMob::PID_Mob_Name (essence file name)"); } /* * p.49 : To create a SourceReference that refers to a MobSlot within * the same Mob as the SourceReference, omit the SourceID property. */ audioEssence->sourceMobSlotID = *SourceMobSlotID; audioEssence->sourceMobID = sourceID; // if ( audioEssence->sourceMobID == NULL ) { // audioEssence->sourceMobID = aaf_get_propertyValue( ParentMob, PID_Mob_MobID, &AAFTypeID_MobIDType ); // warning( "Could not retrieve SourceReference::SourceID, retrieving from parent Mob." ); // } DUMP_OBJ (aafi, SourceClip, &__td); aafObject* SourceMob = aaf_get_MobByID (aafi->aafd->Mobs, audioEssence->sourceMobID); if (SourceMob == NULL) { DUMP_OBJ_ERROR (aafi, SourceClip, &__td, "Could not retrieve SourceMob by ID : %ls", aaft_MobIDToText (audioEssence->sourceMobID)); return -1; } audioEssence->SourceMob = SourceMob; aafObject* EssenceData = get_EssenceData_By_MobID (aafi, audioEssence->sourceMobID); if (EssenceData) __td.ll[__td.lv] = 2; parse_SourceMob (aafi, SourceMob, &__td); __td.ll[__td.lv] = 0; if (EssenceData == NULL) { /* * It means essence is not embedded. */ // return -1; } else { parse_EssenceData (aafi, EssenceData, &__td); } audioEssence->unique_file_name = build_unique_audiofilename (aafi, audioEssence); aafi->ctx.current_clip->Essence = audioEssence; // aafi_trace_obj( aafi, SourceClip, ANSI_COLOR_MAGENTA ); } else if (aafUIDCmp (DataDefinition, &AAFDataDef_Picture) || aafUIDCmp (DataDefinition, &AAFDataDef_LegacyPicture)) { /* Check if this Essence has already been retrieved */ // int slotID = MobSlot->Entry->_localKey; // aafObject *Obj = aaf_get_MobByID( aafi->aafd, sourceID ); // debug( "SourceMobID : %ls", aaft_MobIDToText(sourceID) ); // debug( "MasterMobID : %ls", aaft_MobIDToText(mobID) ); if (!aafi->ctx.current_video_clip) { DUMP_OBJ_ERROR (aafi, SourceClip, &__td, "aafi->ctx.current_video_clip not set"); return -1; } aafiVideoEssence* videoEssence = NULL; foreachEssence (videoEssence, aafi->Video->Essences) { if (aafMobIDCmp (videoEssence->sourceMobID, sourceID) && videoEssence->sourceMobSlotID == (unsigned)*SourceMobSlotID) { /* Essence already retrieved */ aafi->ctx.current_video_clip->Essence = videoEssence; return 0; } } /* new Essence, carry on. */ videoEssence = aafi_newVideoEssence (aafi); aafi->ctx.current_video_clip->Essence = videoEssence; videoEssence->masterMobSlotID = *masterMobSlotID; videoEssence->masterMobID = masterMobID; videoEssence->file_name = aaf_get_propertyValue (ParentMob, PID_Mob_Name, &AAFTypeID_String); /* * p.49 : To create a SourceReference that refers to a MobSlot within * the same Mob as the SourceReference, omit the SourceID property. */ videoEssence->sourceMobSlotID = *SourceMobSlotID; videoEssence->sourceMobID = sourceID; // if ( audioEssence->sourceMobID == NULL ) { // audioEssence->sourceMobID = aaf_get_propertyValue( ParentMob, PID_Mob_MobID, &AAFTypeID_MobIDType ); // warning( "Could not retrieve SourceReference::SourceID, retrieving from parent Mob." ); // } aafi->ctx.current_video_essence = videoEssence; DUMP_OBJ (aafi, SourceClip, &__td); aafObject* SourceMob = aaf_get_MobByID (aafi->aafd->Mobs, videoEssence->sourceMobID); if (SourceMob == NULL) { DUMP_OBJ_ERROR (aafi, SourceClip, &__td, "Could not retrieve SourceMob by ID : %ls", aaft_MobIDToText (videoEssence->sourceMobID)); return -1; } videoEssence->SourceMob = SourceMob; parse_SourceMob (aafi, SourceMob, &__td); aafObject* EssenceData = get_EssenceData_By_MobID (aafi, videoEssence->sourceMobID); if (EssenceData == NULL) { /* * It means essence is not embedded. */ // return -1; } else { parse_EssenceData (aafi, EssenceData, &__td); } videoEssence->unique_file_name = build_unique_videofilename (aafi, videoEssence); // aafi_trace_obj( aafi, SourceClip, ANSI_COLOR_MAGENTA ); // debug( "Master MOB SOURCE CLIP" ); // // aafiVideoEssence *videoEssence = aafi_newVideoEssence( aafi ); // // aafi->ctx.current_essence = (aafiVideoEssence*)videoEssence; // // // videoEssence->file_name = aaf_get_propertyValue( /*aafi->ctx.*/ParentMob, PID_Mob_Name, &AAFTypeID_String ); // // // videoEssence->masterMobID = aaf_get_propertyValue( /*aafi->ctx.*/ParentMob, PID_Mob_MobID, &AAFTypeID_MobIDType ); // // if ( videoEssence->masterMobID == NULL ) { // aafi_trace_obj( aafi, SourceClip, ANSI_COLOR_RED ); // error( "Could not retrieve Mob::MobID." ); // return -1; // } // // /* // * p.49 : To create a SourceReference that refers to a MobSlot within // * the same Mob as the SourceReference, omit the SourceID property. // */ // // videoEssence->sourceMobID = SourceID; //aaf_get_propertyValue( SourceClip, PID_SourceReference_SourceID, &AAFTypeID_MobIDType ); // // if ( videoEssence->sourceMobID == NULL ) { // // aafObject *Mob = NULL; // // // // for ( Mob = SourceClip; Mob != NULL; Mob = Mob->Parent ) { // // if ( aafUIDCmp( Mob->Class->ID, &AAFClassID_MasterMob ) ) // // break; // // } // // videoEssence->sourceMobID = aaf_get_propertyValue( ParentMob, PID_Mob_MobID, &AAFTypeID_MobIDType ); // // warning( "Could not retrieve SourceReference::SourceID, retrieving from parent Mob." ); // } // // // // aafObject *SourceMob = aaf_get_MobByID( aafi->aafd->Mobs, videoEssence->sourceMobID ); // // if ( SourceMob == NULL ) { // aafi_trace_obj( aafi, SourceClip, ANSI_COLOR_RED ); // error( "Could not retrieve SourceMob." ); // return -1; // } // // videoEssence->SourceMob = SourceMob; // // // // // parse_SourceMob( aafi, SourceMob ); // // // /* TODO the following must be moved to parse_SourceMob() !!! */ // // aafObject *EssenceDesc = aaf_get_propertyValue( SourceMob, PID_SourceMob_EssenceDescription, &AAFTypeID_EssenceDescriptorStrongReference ); // // if ( EssenceDesc == NULL ) { // aafi_trace_obj( aafi, SourceClip, ANSI_COLOR_RED ); // error( "Could not retrieve EssenceDesc." ); // return -1; // } // // // // TODO // parse_EssenceDescriptor( aafi, EssenceDesc ); // // // // videoEssence->unique_file_name = build_unique_videofilename( aafi, videoEssence ); // // // // /* NOTE since multiple clips can point to the same MasterMob, we have to loop. */ // // aafiVideoTrack * videoTrack = NULL; // aafiTimelineItem * videoItem = NULL; // // foreach_videoTrack( videoTrack, aafi ) { // foreach_Item( videoItem, videoTrack ) { // if ( videoItem->type != AAFI_VIDEO_CLIP ) { // continue; // } // // aafiVideoClip *videoClip = (aafiVideoClip*)&videoItem->data; // // if ( aafMobIDCmp( videoClip->masterMobID, videoEssence->masterMobID ) ) { // debug( "FOUND VIDEO ESSENCE CLIP !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" ); // videoClip->Essence = videoEssence; // } // } // } // // aafi_trace_obj( aafi, SourceClip, ANSI_COLOR_MAGENTA ); // } } else { DUMP_OBJ_NO_SUPPORT (aafi, SourceClip, &__td); return -1; } return 0; } static int parse_Selector (AAF_Iface* aafi, aafObject* Selector, td* __ptd) { /* * The Selector class is a sub-class of the Segment class. * * The Selector class provides the value of a single Segment (PID_Selector_Selected) * while preserving references to unused alternatives (PID_Selector_Alternates) */ struct trace_dump __td; __td_set (__td, __ptd, 1); if (resolve_AAF (aafi)) { return resolve_parse_aafObject_Selector (aafi, Selector, &__td); } aafObject* Selected = aaf_get_propertyValue (Selector, PID_Selector_Selected, &AAFTypeID_SegmentStrongReference); if (Selected == NULL) { /* req */ DUMP_OBJ_ERROR (aafi, Selector, &__td, "Missing PID_Selector_Selected"); return -1; } // aafObject *Alternate = NULL; aafObject* Alternates = aaf_get_propertyValue (Selector, PID_Selector_Alternates, &AAFTypeID_SegmentStrongReferenceVector); if (Alternates == NULL) { /* opt */ // DUMP_OBJ_WARNING( aafi, Selector, &__td, "Missing PID_Selector_Alternates" ); } DUMP_OBJ (aafi, Selector, &__td); /* without specific software implementation we stick to Selected and forget about Alternates */ return aafi_parse_Segment (aafi, Selected, &__td); } /* * Parameter (abs) * | * ,--------------. * | | * ConstantValue VaryingValue * * * A Parameter object shall be owned by an OperationGroup object. */ static int parse_Parameter (AAF_Iface* aafi, aafObject* Parameter, td* __ptd) { struct trace_dump __td; __td_set (__td, __ptd, 0); if (aafUIDCmp (Parameter->Class->ID, &AAFClassID_ConstantValue)) { return parse_ConstantValue (aafi, Parameter, &__td); } else if (aafUIDCmp (Parameter->Class->ID, &AAFClassID_VaryingValue)) { return parse_VaryingValue (aafi, Parameter, &__td); } return -1; } static int parse_ConstantValue (AAF_Iface* aafi, aafObject* ConstantValue, td* __ptd) { struct trace_dump __td; __td_set (__td, __ptd, 1); // __td.sub = 1; if (!aaf_get_propertyValue (ConstantValue->Parent, PID_OperationGroup_InputSegments, &AAFTypeID_SegmentStrongReferenceVector)) { __td.eob = 1; } aafUID_t* ParamDef = aaf_get_propertyValue (ConstantValue, PID_Parameter_Definition, &AAFTypeID_AUID); if (ParamDef == NULL) { /* req */ DUMP_OBJ_ERROR (aafi, ConstantValue, &__td, "Missing PID_Parameter_Definition"); return -1; } aafUID_t* OperationIdentification = get_OperationGroup_OperationIdentification (aafi, ConstantValue->Parent); if (OperationIdentification == NULL) { DUMP_OBJ_ERROR (aafi, ConstantValue, &__td, "Could not retrieve OperationIdentification"); return -1; } if (aafUIDCmp (OperationIdentification, &AAFOperationDef_MonoAudioGain) && aafUIDCmp (ParamDef, &AAFParameterDef_Amplitude)) { aafIndirect_t* Indirect = aaf_get_propertyValue (ConstantValue, PID_ConstantValue_Value, &AAFTypeID_Indirect); if (Indirect == NULL) { DUMP_OBJ_ERROR (aafi, ConstantValue, &__td, "Missing PID_ConstantValue_Value or wrong AAFTypeID"); return -1; } aafRational_t* multiplier = aaf_get_indirectValue (aafi->aafd, Indirect, &AAFTypeID_Rational); if (multiplier == NULL) { DUMP_OBJ_ERROR (aafi, ConstantValue, &__td, "Could not retrieve Indirect value for PID_ConstantValue_Value"); return -1; } aafiAudioGain* Gain = calloc (sizeof (aafiAudioGain), sizeof (unsigned char)); Gain->pts_cnt = 1; Gain->value = calloc (1, sizeof (aafRational_t*)); Gain->flags |= AAFI_AUDIO_GAIN_CONSTANT; memcpy (&Gain->value[0], multiplier, sizeof (aafRational_t)); /* * Loop through ancestors to find out who is the parent of OperationGroup. * If it belongs to TimelineMobSlot, that means the Parameter is attached * to a Track. If it belongs to a Component, the Parameter is attached to * a clip. * * NOTE: We can't just check the Parent since we can have nested OperationGroups * providing different effects like Pan, Gain, CustomFx.. Therefore looping * is required. */ aafObject* Obj = ConstantValue->Parent; // Start with the last OperationGroup for (; Obj != NULL && aafUIDCmp (Obj->Class->ID, &AAFClassID_ContentStorage) == 0; Obj = Obj->Parent) if (!aafUIDCmp (Obj->Class->ID, &AAFClassID_OperationGroup)) break; if (aafUIDCmp (Obj->Class->ID, &AAFClassID_TimelineMobSlot)) { /* Track-based Gain */ aafi->ctx.current_track->gain = Gain; } else { /* Clip-based Gain */ if (aafi->ctx.current_clip_gain) { DUMP_OBJ_ERROR (aafi, ConstantValue, &__td, "Clip gain was already set : +%05.1lf dB", 20 * log10 (aafRationalToFloat (aafi->ctx.current_clip_gain->value[0]))); // for ( int i = 0; i < aafi->ctx.current_clip_gain->pts_cnt; i++ ) { // debug( " VaryingValue: _time: %f _value: %f", // aafRationalToFloat( aafi->ctx.current_clip_gain->time[i] ), // aafRationalToFloat( aafi->ctx.current_clip_gain->value[i] ) ); // } aafi_freeAudioGain (Gain); return -1; } else { aafi->ctx.current_clip_gain = Gain; aafi->ctx.clips_using_gain = 0; } } DUMP_OBJ (aafi, ConstantValue, &__td); } else if (aafUIDCmp (OperationIdentification, &AAFOperationDef_MonoAudioPan) && aafUIDCmp (ParamDef, &AAFParameterDef_Pan)) { /* * Pan automation shall be track-based. If an application has a different * native representation (e.g., clip-based pan), it shall convert to and * from its native representation when exporting and importing the composition. */ aafIndirect_t* Indirect = aaf_get_propertyValue (ConstantValue, PID_ConstantValue_Value, &AAFTypeID_Indirect); if (Indirect == NULL) { DUMP_OBJ_ERROR (aafi, ConstantValue, &__td, "Missing PID_ConstantValue_Value or wrong AAFTypeID"); return -1; } aafRational_t* multiplier = aaf_get_indirectValue (aafi->aafd, Indirect, &AAFTypeID_Rational); if (multiplier == NULL) { DUMP_OBJ_ERROR (aafi, ConstantValue, &__td, "Could not retrieve Indirect value for PID_ConstantValue_Value"); return -1; } // if ( multiplier == NULL ) { // DUMP_OBJ_ERROR( aafi, ConstantValue, &__td, "Missing PID_ConstantValue_Value or wrong AAFTypeID" ); // return -1; // } aafiAudioPan* Pan = calloc (sizeof (aafiAudioPan), sizeof (unsigned char)); Pan->pts_cnt = 1; Pan->value = calloc (1, sizeof (aafRational_t*)); Pan->flags |= AAFI_AUDIO_GAIN_CONSTANT; memcpy (&Pan->value[0], multiplier, sizeof (aafRational_t)); /* Pan is Track-based only. */ aafi->ctx.current_track->pan = Pan; DUMP_OBJ (aafi, ConstantValue, &__td); } else { DUMP_OBJ_NO_SUPPORT (aafi, ConstantValue, &__td); } return 0; } static int parse_VaryingValue (AAF_Iface* aafi, aafObject* VaryingValue, td* __ptd) { struct trace_dump __td; __td_set (__td, __ptd, 1); // __td.sub = 1; if (!aaf_get_propertyValue (VaryingValue->Parent, PID_OperationGroup_InputSegments, &AAFTypeID_SegmentStrongReferenceVector)) { __td.eob = 1; } aafUID_t* ParamDef = aaf_get_propertyValue (VaryingValue, PID_Parameter_Definition, &AAFTypeID_AUID); if (ParamDef == NULL) { /* req */ DUMP_OBJ_ERROR (aafi, VaryingValue, &__td, "Missing PID_Parameter_Definition"); return -1; } aafUID_t* OperationIdentification = get_OperationGroup_OperationIdentification (aafi, VaryingValue->Parent); if (OperationIdentification == NULL) { DUMP_OBJ_ERROR (aafi, VaryingValue, &__td, "Could not retrieve OperationIdentification"); return -1; } aafiInterpolation_e interpolation = 0; aafUID_t* InterpolationIdentification = get_Parameter_InterpolationIdentification (aafi, VaryingValue); if (InterpolationIdentification == NULL) { DUMP_OBJ_WARNING (aafi, VaryingValue, &__td, "Could not retrieve InterpolationIdentification: Setting to Linear"); interpolation = AAFI_INTERPOL_LINEAR; } else if (aafUIDCmp (InterpolationIdentification, &AAFInterpolationDef_None)) { interpolation = AAFI_INTERPOL_NONE; } else if (aafUIDCmp (InterpolationIdentification, &AAFInterpolationDef_Linear)) { interpolation = AAFI_INTERPOL_LINEAR; } else if (aafUIDCmp (InterpolationIdentification, &AAFInterpolationDef_Power)) { interpolation = AAFI_INTERPOL_POWER; } else if (aafUIDCmp (InterpolationIdentification, &AAFInterpolationDef_Constant)) { interpolation = AAFI_INTERPOL_CONSTANT; } else if (aafUIDCmp (InterpolationIdentification, &AAFInterpolationDef_BSpline)) { interpolation = AAFI_INTERPOL_BSPLINE; } else if (aafUIDCmp (InterpolationIdentification, &AAFInterpolationDef_Log)) { interpolation = AAFI_INTERPOL_LOG; } else { DUMP_OBJ_WARNING (aafi, VaryingValue, &__td, "Unknown value for InterpolationIdentification: Falling back to Linear"); interpolation = AAFI_INTERPOL_LINEAR; } aafObject* Points = aaf_get_propertyValue (VaryingValue, PID_VaryingValue_PointList, &AAFTypeID_ControlPointStrongReferenceVector); if (Points == NULL) { /* * Some files like the ProTools and LogicPro break standard by having no * PointList entry for AAFOperationDef_MonoAudioGain. */ DUMP_OBJ_WARNING (aafi, VaryingValue, &__td, "Missing PID_VaryingValue_PointList or list is empty"); return -1; } // if ( aafUIDCmp( VaryingValue->Parent->Parent->Class->ID, &AAFClassID_Transition ) ) if (aafUIDCmp (OperationIdentification, &AAFOperationDef_MonoAudioDissolve) && aafUIDCmp (ParamDef, &AAFParameterDef_Level)) { aafiTransition* Trans = aafi->ctx.current_transition; Trans->flags |= interpolation; Trans->pts_cnt_a = retrieve_ControlPoints (aafi, Points, &(Trans->time_a), &(Trans->value_a)); if (Trans->pts_cnt_a < 0) { /* In that case, parse_OperationGroup() will set transition to default. */ DUMP_OBJ_ERROR (aafi, VaryingValue, &__td, "Could not retrieve ControlPoints"); return -1; } // for ( int i = 0; i < Trans->pts_cnt_a; i++ ) { // debug( "time_%i : %i/%i value_%i : %i/%i", i, Trans->time_a[i].numerator, Trans->time_a[i].denominator, i, Trans->value_a[i].numerator, Trans->value_a[i].denominator ); // } DUMP_OBJ (aafi, VaryingValue, &__td); } else if (aafUIDCmp (OperationIdentification, &AAFOperationDef_MonoAudioGain) && aafUIDCmp (ParamDef, &AAFParameterDef_Amplitude)) { aafiAudioGain* Gain = calloc (sizeof (aafiAudioGain), sizeof (unsigned char)); Gain->flags |= interpolation; Gain->pts_cnt = retrieve_ControlPoints (aafi, Points, &Gain->time, &Gain->value); if (Gain->pts_cnt < 0) { DUMP_OBJ_ERROR (aafi, VaryingValue, &__td, "Could not retrieve ControlPoints"); free (Gain); return -1; } // for ( int i = 0; i < Gain->pts_cnt; i++ ) { // debug( "time_%i : %i/%i value_%i : %i/%i", i, Gain->time[i].numerator, Gain->time[i].denominator, i, Gain->value[i].numerator, Gain->value[i].denominator ); // } /* If gain has 2 ControlPoints with both the same value, it means * we have a flat gain curve. So we can assume constant gain here. */ if (Gain->pts_cnt == 2 && (Gain->value[0].numerator == Gain->value[1].numerator) && (Gain->value[0].denominator == Gain->value[1].denominator)) { if (aafRationalToFloat (Gain->value[0]) == 1.0f) { /* * gain is null, skip it. Skipping it allows not to set a useless gain then miss the real clip gain later (Resolve 18.5.AAF) */ aafi_freeAudioGain (Gain); return -1; } Gain->flags |= AAFI_AUDIO_GAIN_CONSTANT; } else { Gain->flags |= AAFI_AUDIO_GAIN_VARIABLE; } /* * Loop through ancestors to find out who is the parent of OperationGroup. * If it belongs to TimelineMobSlot, that means the Parameter is attached * to a Track. If it belongs to a Component, the Parameter is attached to * a clip. * * NOTE: We can't just check the Parent since we can have nested OperationGroups * providing different effects like Pan, Gain, CustomFx.. Therefore looping * is required. */ aafObject* Obj = VaryingValue->Parent; // Start with the last OperationGroup for (; Obj != NULL && aafUIDCmp (Obj->Class->ID, &AAFClassID_ContentStorage) == 0; Obj = Obj->Parent) if (!aafUIDCmp (Obj->Class->ID, &AAFClassID_OperationGroup)) break; if (aafUIDCmp (Obj->Class->ID, &AAFClassID_TimelineMobSlot)) { /* Track-based Gain */ if (aafi->ctx.current_track->gain) { DUMP_OBJ_ERROR (aafi, VaryingValue, &__td, "Track Gain was already set"); aafi_freeAudioGain (Gain); return -1; } else { aafi->ctx.current_track->gain = Gain; DUMP_OBJ (aafi, VaryingValue, &__td); } } else { /* Clip-based Gain */ if (Gain->flags & AAFI_AUDIO_GAIN_CONSTANT) { if (aafi->ctx.current_clip_gain) { DUMP_OBJ_ERROR (aafi, VaryingValue, &__td, "Clip gain was already set"); aafi_freeAudioGain (Gain); return -1; } else { aafi->ctx.current_clip_gain = Gain; aafi->ctx.clips_using_gain = 0; } } else { if (aafi->ctx.current_clip_automation) { DUMP_OBJ_ERROR (aafi, VaryingValue, &__td, "Clip automation was already set"); aafi_freeAudioGain (Gain); return -1; } else { aafi->ctx.current_clip_automation = Gain; aafi->ctx.clips_using_automation = 0; } } } } else if (aafUIDCmp (OperationIdentification, &AAFOperationDef_MonoAudioPan) && aafUIDCmp (ParamDef, &AAFParameterDef_Pan)) { /* * Pan automation shall be track-based. If an application has a different * native representation (e.g., clip-based pan), it shall convert to and * from its native representation when exporting and importing the composition. */ aafiAudioPan* Pan = calloc (sizeof (aafiAudioPan), sizeof (unsigned char)); Pan->flags |= AAFI_AUDIO_GAIN_VARIABLE; Pan->flags |= interpolation; Pan->pts_cnt = retrieve_ControlPoints (aafi, Points, &Pan->time, &Pan->value); if (Pan->pts_cnt < 0) { DUMP_OBJ_ERROR (aafi, VaryingValue, &__td, "Could not retrieve ControlPoints"); free (Pan); return -1; } // for ( int i = 0; i < Gain->pts_cnt; i++ ) { // debug( "time_%i : %i/%i value_%i : %i/%i", i, Gain->time[i].numerator, Gain->time[i].denominator, i, Gain->value[i].numerator, Gain->value[i].denominator ); // } /* If Pan has 2 ControlPoints with both the same value, it means * we have a constant Pan curve. So we can assume constant Pan here. */ if (Pan->pts_cnt == 2 && (Pan->value[0].numerator == Pan->value[1].numerator) && (Pan->value[0].denominator == Pan->value[1].denominator)) { // if ( aafRationalToFloat(Gain->value[0]) == 1.0f ) { // /* // * Pan is null, skip it. Skipping it allows not to set a useless gain then miss the real clip gain later (Resolve 18.5.AAF) // */ // aafi_freeAudioGain( Gain ); // return -1; // } Pan->flags |= AAFI_AUDIO_GAIN_CONSTANT; } else { Pan->flags |= AAFI_AUDIO_GAIN_VARIABLE; } if (aafi->ctx.current_track->pan) { DUMP_OBJ_ERROR (aafi, VaryingValue, &__td, "Track Pan was already set"); aafi_freeAudioGain (Pan); return -1; } else { aafi->ctx.current_track->pan = Pan; DUMP_OBJ (aafi, VaryingValue, &__td); } } else { DUMP_OBJ (aafi, VaryingValue, &__td); } return 0; } static int retrieve_ControlPoints (AAF_Iface* aafi, aafObject* Points, aafRational_t* times[], aafRational_t* values[]) { *times = calloc (Points->Header->_entryCount, sizeof (aafRational_t)); *values = calloc (Points->Header->_entryCount, sizeof (aafRational_t)); aafObject* Point = NULL; unsigned int i = 0; aaf_foreach_ObjectInSet (&Point, Points, &AAFClassID_ControlPoint) { aafRational_t* time = aaf_get_propertyValue (Point, PID_ControlPoint_Time, &AAFTypeID_Rational); if (time == NULL) { // aafi_trace_obj( aafi, Points, ANSI_COLOR_RED ); error ("Missing ControlPoint::Time."); free (*times); *times = NULL; free (*values); *values = NULL; return -1; } // aafRational_t *value = aaf_get_propertyIndirectValue( Point, PID_ControlPoint_Value, &AAFTypeID_Rational ); aafIndirect_t* Indirect = aaf_get_propertyValue (Point, PID_ControlPoint_Value, &AAFTypeID_Indirect); if (Indirect == NULL) { // DUMP_OBJ_ERROR( aafi, ConstantValue, &__td, "Missing PID_ConstantValue_Value or wrong AAFTypeID" ); error ("Missing ControlPoint::Value or wrong AAFTypeID"); free (*times); *times = NULL; free (*values); *values = NULL; return -1; } aafRational_t* value = aaf_get_indirectValue (aafi->aafd, Indirect, &AAFTypeID_Rational); if (value == NULL) { // aafi_trace_obj( aafi, Points, ANSI_COLOR_RED ); error ("Could not retrieve Indirect value for PID_ControlPoint_Value"); free (*times); *times = NULL; free (*values); *values = NULL; return -1; } memcpy ((*times + i), time, sizeof (aafRational_t)); memcpy ((*values + i), value, sizeof (aafRational_t)); i++; } if (Points->Header->_entryCount != i) { // aafi_trace_obj( aafi, Points, ANSI_COLOR_YELLOW ); warning ("Points _entryCount (%i) does not match iteration (%i).", Points->Header->_entryCount, i); return i; } // aafi_trace_obj( aafi, Points, ANSI_COLOR_MAGENTA ); return Points->Header->_entryCount; } /* **************************************************************************** * M o b * **************************************************************************** * Mob (abs) * | * |--> CompositionMob * |--> MasterMob * `--> SourceMob */ static int parse_Mob (AAF_Iface* aafi, aafObject* Mob) { // int dl = DUMP_INIT(aafi); // __td(NULL); static td __td; __td.fn = __LINE__; __td.pfn = 0; __td.lv = 0; __td.ll = calloc (1024, sizeof (int)); /* TODO free */ __td.ll[0] = 0; // aafi->ctx.trace_leveloop = __td.ll; // keep track of __td.ll for free aafObject* MobSlots = aaf_get_propertyValue (Mob, PID_Mob_Slots, &AAFTypeID_MobSlotStrongReferenceVector); if (MobSlots == NULL) { /* req */ DUMP_OBJ_ERROR (aafi, Mob, &__td, "Missing PID_Mob_Slots"); free (__td.ll); return -1; } if (aafUIDCmp (Mob->Class->ID, &AAFClassID_CompositionMob)) { aafUID_t* UsageCode = aaf_get_propertyValue (Mob, PID_Mob_UsageCode, &AAFTypeID_UsageType); if (aafUIDCmp (UsageCode, &AAFUsage_AdjustedClip)) { DUMP_OBJ_ERROR (aafi, Mob, &__td, "Skipping AAFUsage_AdjustedClip"); return -1; } parse_CompositionMob (aafi, Mob, &__td); } else if (aafUIDCmp (Mob->Class->ID, &AAFClassID_MasterMob)) { DUMP_OBJ (aafi, Mob, &__td); } else if (aafUIDCmp (Mob->Class->ID, &AAFClassID_SourceMob)) { DUMP_OBJ (aafi, Mob, &__td); } /* * Loops through MobSlots */ aafObject* MobSlot = NULL; int i = 0; aaf_foreach_ObjectInSet (&MobSlot, MobSlots, NULL) { __td.ll[__td.lv] = MobSlots->Header->_entryCount - i++; parse_MobSlot (aafi, MobSlot, &__td); } free (__td.ll); return 0; } static int parse_CompositionMob (AAF_Iface* aafi, aafObject* CompoMob, td* __ptd) { struct trace_dump __td; __td_set (__td, __ptd, 0); DUMP_OBJ (aafi, CompoMob, &__td); aafi->compositionName = aaf_get_propertyValue (CompoMob, PID_Mob_Name, &AAFTypeID_String); /* opt */ aafObject* UserComment = NULL; aafObject* UserComments = aaf_get_propertyValue (CompoMob, PID_Mob_UserComments, &AAFTypeID_TaggedValueStrongReferenceVector); /* opt */ aaf_foreach_ObjectInSet (&UserComment, UserComments, NULL) { /* TODO: implement retrieve_TaggedValue() ? */ wchar_t* name = aaf_get_propertyValue (UserComment, PID_TaggedValue_Name, &AAFTypeID_String); if (name == NULL) { /* req */ DUMP_OBJ_ERROR (aafi, UserComment, &__td, "Missing PID_TaggedValue_Name"); continue; } aafIndirect_t* Indirect = aaf_get_propertyValue (UserComment, PID_TaggedValue_Value, &AAFTypeID_Indirect); if (Indirect == NULL) { DUMP_OBJ_ERROR (aafi, UserComment, &__td, "Missing PID_TaggedValue_Value"); continue; } wchar_t* text = aaf_get_indirectValue (aafi->aafd, Indirect, &AAFTypeID_String); if (text == NULL) { DUMP_OBJ_ERROR (aafi, UserComment, &__td, "Could not retrieve Indirect value for PID_TaggedValue_Value"); continue; } aafiUserComment* Comment = aafi_newUserComment (aafi, &aafi->Comments); Comment->name = name; Comment->text = text; } return 0; } static int parse_SourceMob (AAF_Iface* aafi, aafObject* SourceMob, td* __ptd) { struct trace_dump __td; __td_set (__td, __ptd, 1); __td.hc = 1; /* TODO find a better way to check if we're parsing audio */ // aafUID_t *DataDefinition = get_Component_DataDefinition( aafi, SourceMob ); // // if ( DataDefinition == NULL ) /* req */ { // error( "Could not retrieve MobSlot::Segment DataDefinition." ); // return -1; // } // // // // if ( aafUIDCmp( DataDefinition, &AAFDataDef_Sound ) || // aafUIDCmp( DataDefinition, &AAFDataDef_LegacySound ) ) if (aafi->ctx.current_essence) { if (aafi->ctx.current_essence == NULL) { DUMP_OBJ_ERROR (aafi, SourceMob, &__td, "ctx->current_essence no set"); return -1; } aafiAudioEssence* audioEssence = (aafiAudioEssence*)aafi->ctx.current_essence; aafMobID_t* MobID = aaf_get_propertyValue (SourceMob, PID_Mob_MobID, &AAFTypeID_MobIDType); if (MobID == NULL) { DUMP_OBJ_ERROR (aafi, SourceMob, &__td, "Missing PID_Mob_MobID"); return -1; } memcpy (audioEssence->umid, MobID, sizeof (aafMobID_t)); aafTimeStamp_t* CreationTime = aaf_get_propertyValue (SourceMob, PID_Mob_CreationTime, &AAFTypeID_TimeStamp); if (CreationTime == NULL) { DUMP_OBJ_ERROR (aafi, SourceMob, &__td, "Missing PID_Mob_CreationTime"); return -1; } snprintf (audioEssence->originationDate, sizeof (audioEssence->originationDate), "%04u:%02u:%02u", (CreationTime->date.year <= 9999) ? CreationTime->date.year : 0, (CreationTime->date.month <= 99) ? CreationTime->date.month : 0, (CreationTime->date.day <= 99) ? CreationTime->date.day : 0); snprintf (audioEssence->originationTime, sizeof (audioEssence->originationTime), "%02u:%02u:%02u", (CreationTime->time.hour <= 99) ? CreationTime->time.hour : 0, (CreationTime->time.minute <= 99) ? CreationTime->time.minute : 0, (CreationTime->time.second <= 99) ? CreationTime->time.second : 0); } aafObject* EssenceDesc = aaf_get_propertyValue (SourceMob, PID_SourceMob_EssenceDescription, &AAFTypeID_EssenceDescriptorStrongReference); if (EssenceDesc == NULL) { DUMP_OBJ_ERROR (aafi, SourceMob, &__td, "Could not retrieve EssenceDescription"); return -1; } DUMP_OBJ (aafi, SourceMob, &__td); parse_EssenceDescriptor (aafi, EssenceDesc, &__td); return 0; } static aafiAudioTrack* get_audio_track_by_tracknumber (AAF_Iface* aafi, int tracknumber) { aafiAudioTrack* audioTrack = NULL; int count = 0; foreach_audioTrack (audioTrack, aafi) { if (++count == tracknumber) return audioTrack; } return NULL; } /* **************************************************************************** * M o b S l o t * **************************************************************************** * * MobSlot (abs) * | * |--> TimelineMobSlot * |--> EventMobSlot * `--> StaticMobSlot */ static int parse_MobSlot (AAF_Iface* aafi, aafObject* MobSlot, td* __ptd) { // debug( "MS : %p", __ptd->ll ); struct trace_dump __td; __td_set (__td, __ptd, 1); __td.hc = 1; aafObject* Segment = aaf_get_propertyValue (MobSlot, PID_MobSlot_Segment, &AAFTypeID_SegmentStrongReference); if (Segment == NULL) { /* req */ DUMP_OBJ_ERROR (aafi, MobSlot, &__td, "Missing PID_MobSlot_Segment"); return -1; } aafUID_t* DataDefinition = get_Component_DataDefinition (aafi, Segment); if (DataDefinition == NULL) { /* req */ DUMP_OBJ_ERROR (aafi, MobSlot, &__td, "Could not retrieve DataDefinition"); return -1; } aafPosition_t session_end = 0; if (aafUIDCmp (MobSlot->Class->ID, &AAFClassID_TimelineMobSlot)) { /* * Each TimelineMobSlot represents a track, either audio or video. * * The Timeline MobSlot::Segment should hold a Sequence of Components. * This Sequence represents the timeline track. Therefore, each SourceClip * contained in the Sequence::Components represents a clip on the timeline. * * CompositionMob can have TimelineMobSlots, StaticMobSlots, EventMobSlots */ aafRational_t* edit_rate = aaf_get_propertyValue (MobSlot, PID_TimelineMobSlot_EditRate, &AAFTypeID_Rational); if (edit_rate == NULL) { /* req */ DUMP_OBJ_ERROR (aafi, MobSlot, &__td, "Missing PID_TimelineMobSlot_EditRate"); return -1; } if (aafUIDCmp (/*aafi->ctx.*/ MobSlot->Parent->Class->ID, &AAFClassID_CompositionMob)) { /* * There should be only one Composition, since a CompositionMob represents the overall * composition (i.e project). Observations on files confirm that. * * However, the AAF Edit Protocol says that there could be multiple CompositionMobs * (Mob::UsageCode TopLevel), containing other CompositionMobs (Mob::UsageCode LowerLevel). * This was not encountered yet, even on Avid exports with AAF_EditProtocol enabled. * * TODO: update note * TODO: implement multiple TopLevel compositions support */ if (aafUIDCmp (DataDefinition, &AAFDataDef_Sound) || aafUIDCmp (DataDefinition, &AAFDataDef_LegacySound)) { /* * p.11 : In a CompositionMob or MasterMob, PhysicalTrackNumber is the * output channel number that the MobSlot should be routed to when played. */ if (!aafi->ctx.is_inside_derivation_chain) { uint32_t tracknumber = 0; uint32_t* track_num = aaf_get_propertyValue (MobSlot, PID_MobSlot_PhysicalTrackNumber, &AAFTypeID_UInt32); if (track_num == NULL) { /* opt */ tracknumber = aafi->Audio->track_count + 1; } else { tracknumber = *track_num; } aafiAudioTrack* track = get_audio_track_by_tracknumber (aafi, tracknumber); if (!track) { track = aafi_newAudioTrack (aafi); } track->number = tracknumber; aafi->Audio->track_count += 1; aafi->ctx.current_track = track; track->name = aaf_get_propertyValue (MobSlot, PID_MobSlot_SlotName, &AAFTypeID_String); track->edit_rate = edit_rate; } // aaf_dump_ObjectProperties( aafi->aafd, MobSlot ); /* * The following seems to be ProTools proprietary. * If a track is multi-channel, it specifies its format : 2 (stereo), 6 (5.1) or 8 (7.1). * * In the current implementation we don't need this. We guess the format at the OperationGroup level with the * AAFOperationDef_AudioChannelCombiner OperationDefinition, which also looks to be ProTools specific. */ // aafPID_t PIDTimelineMobAttributeList = aaf_get_PropertyIDByName( aafi->aafd, "TimelineMobAttributeList" ); // // aafPID_t PIDTimelineMobAttributeList = aaf_get_PropertyIDByName( aafi->aafd, "MobAttributeList" ); // // if ( PIDTimelineMobAttributeList != 0 ) { // aafObject *TaggedValues = aaf_get_propertyValue( aafi->ctx.MobSlot, PIDTimelineMobAttributeList ); // aafObject *TaggedValue = NULL; // // aaf_foreach_ObjectInSet( &TaggedValue, TaggedValues, NULL ) { // char *name = aaf_get_propertyValueText( TaggedValue, PID_TaggedValue_Name ); // // debug( "TaggedValue %s", name ); // // if ( strncmp( "_TRACK_FORMAT", name, 13 ) == 0 ) { // uint32_t *format = (uint32_t*)aaf_get_propertyIndirectValue( TaggedValue, PID_TaggedValue_Value ); // // if ( format != NULL ) // aafi->ctx.current_track->format = *format; // // debug( "Format : %u", aafi->ctx.current_track->format ); // } // // free( name ); // } // } DUMP_OBJ (aafi, MobSlot, &__td); /* Reset timeline position */ // aafi->ctx.current_track->current_pos = 0; aafi_parse_Segment (aafi, Segment, &__td); /* update session_end if needed */ // session_end = ( aafi->ctx.current_pos > session_end ) ? aafi->ctx.current_pos : session_end; // debug( "AAFIParser 4286: Current pos : %lu\n", aafi->ctx.current_track->current_pos ); session_end = (aafi->ctx.current_track->current_pos > session_end) ? aafi->ctx.current_track->current_pos : session_end; // debug( "SESSIon_end : %li", session_end ); } else if (aafUIDCmp (DataDefinition, &AAFDataDef_Timecode) || aafUIDCmp (DataDefinition, &AAFDataDef_LegacyTimecode)) { DUMP_OBJ (aafi, MobSlot, &__td); aafi_parse_Segment (aafi, Segment, &__td); } else if (aafUIDCmp (DataDefinition, &AAFDataDef_Picture) || aafUIDCmp (DataDefinition, &AAFDataDef_LegacyPicture)) { // debug( "%ls", aaft_ClassIDToText(aafi->aafd, Segment->Class->ID) ); /* ADP NESTED SCOPE */ // aafObject *NSSegment = NULL; // aafObject *NSSegments = aaf_get_propertyValue( Segment, PID_NestedScope_Slots ); // // aaf_foreach_ObjectInSet( &NSSegment, NSSegments, NULL ) { // aaf_dump_ObjectProperties( aafi->aafd, NSSegment ); // } if (aafi->Video->Tracks) { DUMP_OBJ_ERROR (aafi, MobSlot, &__td, "Current implementation supports only one video track"); return -1; } /* * p.11 : In a CompositionMob or MasterMob, PhysicalTrackNumber is the output channel number that the * MobSlot should be routed to when played. */ uint32_t tracknumber = 0; uint32_t* track_num = aaf_get_propertyValue (MobSlot, PID_MobSlot_PhysicalTrackNumber, &AAFTypeID_UInt32); if (track_num == NULL) { /* opt */ tracknumber = 1; /* Current implementation supports only one video track. */ } else { tracknumber = *track_num; } aafiVideoTrack* track = aafi_newVideoTrack (aafi); track->number = tracknumber; track->name = aaf_get_propertyValue (MobSlot, PID_MobSlot_SlotName, &AAFTypeID_String); track->edit_rate = edit_rate; DUMP_OBJ (aafi, MobSlot, &__td); aafi_parse_Segment (aafi, Segment, &__td); /* update session_end if needed */ // session_end = ( aafi->ctx.current_track->current_pos > session_end ) ? aafi->ctx.current_track->current_pos : session_end; } else { DUMP_OBJ_NO_SUPPORT (aafi, MobSlot, &__td); } } else if (aafUIDCmp (MobSlot->Parent->Class->ID, &AAFClassID_MasterMob)) { /* Retrieve Essences */ // if ( aafUIDCmp( DataDefinition, &AAFDataDef_Sound ) || // aafUIDCmp( DataDefinition, &AAFDataDef_LegacySound ) ) // { DUMP_OBJ (aafi, MobSlot, &__td); aafi_parse_Segment (aafi, Segment, &__td); // } // else if ( aafUIDCmp( DataDefinition, &AAFDataDef_Picture ) || // aafUIDCmp( DataDefinition, &AAFDataDef_LegacyPicture ) ) // { // aafi_parse_Segment( aafi, Segment ); // // retrieve_EssenceData( aafi ); // } // else { // aafi_trace_obj( aafi, MobSlot, ANSI_COLOR_YELLOW ); // debug( "%ls", aaft_DataDefToText( aafi->aafd, DataDefinition ) ); // } } else if (aafUIDCmp (MobSlot->Parent->Class->ID, &AAFClassID_SourceMob)) { if (aafi->ctx.current_essence != NULL) { aafiAudioEssence* audioEssence = (aafiAudioEssence*)aafi->ctx.current_essence; aafPosition_t* Origin = aaf_get_propertyValue (MobSlot, PID_TimelineMobSlot_Origin, &AAFTypeID_PositionType); if (Origin == NULL) { /* req */ DUMP_OBJ_ERROR (aafi, MobSlot, &__td, "Missing PID_TimelineMobSlot_Origin"); return -1; } audioEssence->timeReference = *Origin; audioEssence->mobSlotEditRate = edit_rate; DUMP_OBJ (aafi, MobSlot, &__td); } else { DUMP_OBJ_ERROR (aafi, MobSlot, &__td, "aafi->ctx.current_essence no set"); } } else { /* Not in CompositionMob, MasterMob, or SourceMob. Can not happen. */ DUMP_OBJ_NO_SUPPORT (aafi, MobSlot, &__td); } } else if (aafUIDCmp (MobSlot->Class->ID, &AAFClassID_EventMobSlot)) { aafRational_t* edit_rate = aaf_get_propertyValue (MobSlot, PID_EventMobSlot_EditRate, &AAFTypeID_Rational); if (edit_rate == NULL) { /* req */ DUMP_OBJ_ERROR (aafi, MobSlot, &__td, "Missing PID_EventMobSlot_EditRate"); return -1; } aafi->ctx.current_markers_edit_rate = edit_rate; DUMP_OBJ (aafi, MobSlot, &__td); return aafi_parse_Segment (aafi, Segment, &__td); } else { /* Not AAFClassID_TimelineMobSlot */ DUMP_OBJ_NO_SUPPORT (aafi, MobSlot, &__td); } /* TODO implement global (audio and video) session start and end */ // if ( aafi->ctx.current_tree_type == AAFI_TREE_TYPE_AUDIO ) if (session_end > 0 && aafi->Timecode && aafi->Timecode->end < session_end) { aafi->Timecode->end = session_end; } // if ( aafi->ctx.current_tree_type == AAFI_TREE_TYPE_VIDEO ) // if ( session_end > 0 && aafi->Video->tc ) // aafi->Video->tc->end = session_end; // else // error( "MISSING aafiTimecode !" ); return 0; } int aafi_retrieveData (AAF_Iface* aafi) { aafObject* Mob = NULL; aaf_foreach_ObjectInSet (&Mob, aafi->aafd->Mobs, &AAFClassID_CompositionMob) { aafUID_t* UsageCode = aaf_get_propertyValue (Mob, PID_Mob_UsageCode, &AAFTypeID_UsageType); if (aafUIDCmp (aafi->aafd->Header.OperationalPattern, &AAFOPDef_EditProtocol) && !aafUIDCmp (UsageCode, &AAFUsage_TopLevel)) { /* * If we run against AAFOPDef_EditProtocol, we process only TopLevels CompositionMobs. * If there is more than one, we have multiple Compositions in a single AAF. */ // aafi_trace_obj( aafi, Mob, ANSI_COLOR_RED ); // // aaf_dump_ObjectProperties( aafi->aafd, aafi->ctx.Mob ); // // aafObject *MobSlots = aaf_get_propertyValue( aafi->ctx.Mob, PID_Mob_Slots, &AAFTypeID_MobSlotStrongReferenceVector ); // aafObject *MobSlot = NULL; // uint32_t SlotID = 0; // // aaf_foreach_ObjectInSet( &MobSlot, MobSlots, NULL ) { // aaf_dump_ObjectProperties( aafi->aafd, MobSlot ); // } continue; } RESET_CONTEXT (aafi->ctx); parse_Mob (aafi, Mob); } // aafiAudioTrack *audioTrack = NULL; // aafiTimelineItem *audioItem = NULL; // // aafiAudioClip *audioClip = NULL; // // // uint32_t i = 0; // // foreach_audioTrack( audioTrack, aafi ) { // foreach_Item( audioItem, audioTrack ) { // // if ( audioItem->type == AAFI_TRANS ) { // continue; // } // // aafiAudioClip *audioClip = (aafiAudioClip*)&audioItem->data; // // if ( audioClip->masterMobID && !audioClip->Essence ) { // debug( "E m p t y C l i p" ); // // aafObject *Mob = NULL; // // aaf_foreach_ObjectInSet( &Mob, aafi->aafd->Mobs, NULL ) { // /* loops through Mobs */ // aafUID_t *UsageCode = aaf_get_propertyValue( Mob, PID_Mob_UsageCode, &AAFTypeID_ ); // // aafMobID_t *MobID = aaf_get_propertyValue( Mob, PID_Mob_MobID, &AAFTypeID_MobIDType ); // // if ( !aafMobIDCmp( MobID, audioClip->masterMobID ) ) { // continue; // } // // // aaf_dump_ObjectProperties( aafi->aafd, Mob ); // debug( "Clip SourceID : %ls", aaft_MobIDToText(MobID) ); // // debug( "PointedMob ClassID : %ls", aaft_ClassIDToText(aafi->aafd, Mob->Class->ID) ); // debug( "PointedMob UsageCd : %ls", aaft_UsageCodeToText(UsageCode) ); // debug( "PointedMob Name : %ls", aaf_get_propertyValue(Mob, PID_Mob_Name, &AAFTypeID_String) ); // // // // aafObject *MobSlots = aaf_get_propertyValue( Mob, PID_Mob_Slots, &AAFTypeID_MobSlotStrongReferenceVector ); // aafObject *MobSlot = NULL; // int SlotID = 1; // aaf_foreach_ObjectInSet( &MobSlot, MobSlots, NULL ) { // debug( " SlotID %u", SlotID ); // // aafObject *Segment = aaf_get_propertyValue( MobSlot, PID_MobSlot_Segment, &AAFTypeID_SegmentStrongReference ); // // if ( Segment == NULL ) { // error( "Missing MobSlot::Segment." ); // return -1; // } // // // aafUID_t *DataDefinition = get_Component_DataDefinition( aafi, Segment ); // // if ( DataDefinition == NULL ) { // error( "Could not retrieve MobSlot::Segment DataDefinition." ); // return -1; // } // // // CURRENTPOINTER // // debug( " Segment : %ls", aaft_ClassIDToText( aafi->aafd, Segment->Class->ID ) ); // debug( " DataDefinition : %ls", aaft_DataDefToText(aafi->aafd, DataDefinition) ); // // // // // // // if ( aafUIDCmp( Segment->Class->ID, &AAFClassID_SourceClip ) ) { // aafMobID_t *SourceID = aaf_get_propertyValue( Segment, PID_SourceReference_SourceID, &AAFTypeID_MobIDType ); // debug( " SourceID : %ls", aaft_MobIDToText(sourceID) ); // } // // SlotID++; // } // // // } // } // } // } // aaf_foreach_ObjectInSet( &(aafi->ctx.Mob), aafi->aafd->Mobs, &AAFClassID_SourceMob ) { // // aafObject *MobSlots = aaf_get_propertyValue( aafi->ctx.Mob, PID_Mob_Slots, &AAFTypeID_MobSlotStrongReferenceVector ); // // aaf_foreach_ObjectInSet( &(aafi->ctx.MobSlot), MobSlots, NULL ) { // // /* // * Check if the SourceMob was parsed. // * If it was not, we can print the trace. // * // * NOTE We do it after the main loop, so we make sure all MasterMobs was parsed. // */ // // aafObject *Segment = aaf_get_propertyValue( aafi->ctx.MobSlot, PID_MobSlot_Segment, &AAFTypeID_SegmentStrongReference ); // // aafUID_t *DataDefinition = get_Component_DataDefinition( aafi, Segment ); // // aafMobID_t *MobID = aaf_get_propertyValue( aafi->ctx.Mob, PID_Mob_MobID, &AAFTypeID_MobIDType ); // // aafiAudioEssence *audioEssence = NULL; // // foreachEssence( audioEssence, aafi->Audio->Essences ) { // if ( aafMobIDCmp( MobID, audioEssence->sourceMobID ) ) // break; // } // // if ( audioEssence == NULL ) { // aafi_trace_obj( aafi, aafi->ctx.MobSlot, ANSI_COLOR_YELLOW ); // debug( "%ls", aaft_DataDefToText( aafi->aafd, DataDefinition ) ); // } // // } // } if (aafi->Timecode == NULL) { warning ("No timecode found in file. Setting to 00:00:00:00 @ 25fps"); aafiTimecode* tc = calloc (sizeof (aafiTimecode), sizeof (unsigned char)); if (tc == NULL) { error ("calloc() : %s", strerror (errno)); return -1; } tc->start = 0; tc->fps = 25; tc->drop = 0; tc->edit_rate = &AAFI_DEFAULT_TC_EDIT_RATE; aafi->Timecode = tc; } /* aafi->Audio->tc->end is set to composition duration. Add tc->start to set composition end time */ if (aafi->Timecode && aafi->Timecode->end) { aafi->Timecode->end += aafi->Timecode->start; } /* Post processing */ /* TODO move to parse_*() */ /* Parse summary descriptor (WAVE/AIFC) if any */ aafiAudioEssence* audioEssence = NULL; foreachEssence (audioEssence, aafi->Audio->Essences) { /* TODO: rename (not only summary, can be external file too) */ aafi_parse_audio_summary (aafi, audioEssence); /* TODO : check samplerate / samplesize proportions accross essences, and choose the most used values as composition values */ if (aafi->Audio->samplerate == 0 || aafi->Audio->samplerate == audioEssence->samplerate) { aafi->Audio->samplerate = audioEssence->samplerate; } else { // warning( "audioEssence '%ls' has different samplerate : %u", audioEssence->file_name, audioEssence->samplerate ); } if (aafi->Audio->samplesize == 0 || aafi->Audio->samplesize == audioEssence->samplesize) { aafi->Audio->samplesize = audioEssence->samplesize; } else { // warning( "audioEssence '%ls' has different samplesize : %i", audioEssence->file_name, audioEssence->samplesize ); } } aafiAudioTrack* audioTrack = NULL; foreach_audioTrack (audioTrack, aafi) { // aafiTimelineItem *audioItem = NULL; // aafiAudioClip *audioClip = NULL; // // if ( audioTrack->format > 1 ) { // // foreach_Item( audioItem, audioTrack ) { // // if ( audioItem->type == AAFI_TRANS ) { // continue; // } // // audioClip = (aafiAudioClip*)&audioItem->data; // } // } if (audioTrack->current_pos > aafi->Audio->length) { aafi->Audio->length = audioTrack->current_pos; aafi->Audio->length_editRate.numerator = audioTrack->edit_rate->numerator; aafi->Audio->length_editRate.denominator = audioTrack->edit_rate->denominator; } } aafiVideoTrack* videoTrack = NULL; foreach_videoTrack (videoTrack, aafi) { if (videoTrack->current_pos > aafi->Video->length) { aafi->Video->length = videoTrack->current_pos; aafi->Video->length_editRate.numerator = videoTrack->edit_rate->numerator; aafi->Video->length_editRate.denominator = videoTrack->edit_rate->denominator; } } if (aafi->Audio->length > aafi->Video->length) { aafi->compositionLength = aafi->Audio->length; aafi->compositionLength_editRate.numerator = aafi->Audio->length_editRate.numerator; aafi->compositionLength_editRate.denominator = aafi->Audio->length_editRate.denominator; } else { aafi->compositionLength = aafi->Video->length; aafi->compositionLength_editRate.numerator = aafi->Video->length_editRate.numerator; aafi->compositionLength_editRate.denominator = aafi->Video->length_editRate.denominator; } aafi->compositionStart = aafi->Timecode->start; aafi->compositionStart_editRate.numerator = aafi->Timecode->edit_rate->numerator; aafi->compositionStart_editRate.denominator = aafi->Timecode->edit_rate->denominator; if (protools_AAF (aafi)) { protools_post_processing (aafi); } return 0; } /** * @} */