/* * Copyright (C) 2017-2024 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. */ #ifndef __AAFCore_h__ #define __AAFCore_h__ /** * @file AAFCore/AAFCore.h * @brief Retrieves the AAF Objects Tree from the Compound File Binary. * @author Adrien Gesta-Fline * @version 0.1 * @date 04 october 2017 * * @ingroup AAFCore * @addtogroup AAFCore * @brief Retrieves the AAF Objects Tree from the Compound File Binary. * * The AAF file structure is based on the Compound File Binary, implemented by LibCFB * and defined at https://www.amwa.tv/projects/MS-03.shtml * * The specifications of the low-level AAF can be found at https://amwa.tv/projects/MS-02.shtml * * The specifications of the overall AAF and standard Classes / Properties can be found at * https://amwa.tv/projects/MS-01.shtml * * This is the core of the libAAF library. AAFCore is intendend to : * - Define the standard AAF classes and properties at run time, * - retrieve potential custom classes and properties out of the MetaDictionary, * - retrieve the entire object tree and their properties out of the CFB Tree, * - provide functions to navigate in the tree and access objects and properties. * * Therefore, AAFCore makes a bridge between the low-level Compound File Binary and the * high level AAFIface, which is used to interpret objects and thus retrieve usable data. * * Even though AAFCore can be used as is, it is recommended for complex operations * like essences and clips retrieval, to use AAFIface. * * ### Usage * * In order to use libAAF, you should start by allocating AAF_Data with aaf_alloc(), then * you can load a file by calling aaf_load_file(). aaf_load_file() will not load the entire * file to memory, but will instead parse the CFB Tree and set AAF_Data accordingly. * * @code * AAF_Data *aafd = aaf_alloc(); * aaf_load_file( aafd, "./path/to/file.aaf" ); * @endcode * * Then, you can access the object tree thanks to the access functions, or use the AAFIface * to extract usable data through a convenient interface. * * @code * // TODO * @endcode * * Once you're done, you can close the file and free the AAF_Data structure by calling * aaf_release( &aafd ); * * @code * aaf_release( &aafd ); * @endcode * * @{ */ #include "aaf/AAFTypes.h" #include "aaf/LibCFB.h" /** * Possible values for aafPropertyDef.isReq. */ enum aafPropertyIsRequired_e { PROP_REQUIRED = 1, PROP_REQ = 1, PROP_OPTIONAL = 0, PROP_OPT = 0 }; /** * This structure defines a Class property, with a property ID * and tells if that property is either #REQUIRED or #OPTIONAL. * This structure is to be added to an aafClass.Properties list * with the attachNewProperty() macro, and to be pointed to by * the aafProperty.def field of each parsed property. */ typedef struct aafPropertyDefinition { /** * Specifies the property ID. * * All the standard IDs can be found in AAFDefs/AAFPropertyIDs.h. */ uint16_t pid; /** * Tells if that property is either #REQUIRED or #OPTIONAL. */ aafBoolean_t isReq; aafBoolean_t meta; char* name; /* * Looks like nobody cares about AAF standard TypeDefinition. All observed files * had incorrect values for Type's Name and Identification, even Avid's files. * Thus, PDef->type should NOT be trusted. * * TODO: Should be set by attachNewProperty() in AAFClass.c */ aafUID_t type; /** * Pointer to the next aafPropertyDef in the list. */ struct aafPropertyDefinition* next; } aafPropertyDef; /** * Possible values for aafClass.isConcrete. */ enum aafClassIsConcrete_e { ABSTRACT = 0, ABS = 0, CONCRETE = 1, CON = 1 }; /** * This structure defines an AAF Class. * * An AAF Class can be identified thanks to its ClassID. * * An AAF Class holds some properties. * * An AAF Class can inherit other Classes properties from * its parents. * * An AAF Class can be either a #CONCRETE or an #ABSTRACT * Class. It is a #CONCRETE Class if it can be directly * retrieved from the Compound File Nodes Tree as an object. * Else, it is an #ABSTRACT Class and has to be inherited by * another Class for its properties to be retrieved. */ typedef struct aafclass { /** * The ClassID of the Class. When parsing, the ClassID * correspond to the cfbNode._clsId. * Note that the CLSID and aafUID_t types are binary * compatible. * * All the standard ClassIDs can be found in AAFDefs/AAFClassDefUIDs.h. */ const aafUID_t* ID; /** * Tells if the Class is either #CONCRETE or #ABSTRACT. * * A Class is #CONCRETE if it can be retrieved as an * object. Else, a Class is #ABSTRACT if it can't be * directly retrieved and can only be inherited by * another Class, so only its members can be retrieved. */ aafBoolean_t isConcrete; /** * Pointer to a list of aafPropertyDef structs. * * These aafPropertyDef define the properties * owned by the Class. */ aafPropertyDef* Properties; /** * Pointer to the Parent Class. If this Class has no * Parent (is root), then the pointer shall be NULL. */ struct aafclass* Parent; aafBoolean_t meta; /* name is set at runtime */ char* name; /** * Pointer to the next Class in the AAF_Data.Class list. */ struct aafclass* next; } aafClass; /** * This structure represents a property once it has been parsed. * * This structure is to be added to an aafObject.Properties list. */ typedef struct aafProperty { /** * Specifies the property ID. The pid shall be the same as in the * #def aafPropertyDef structure. * * All the standard IDs can be found in AAFDefs/AAFPropertyIDs.h. */ uint16_t pid; /** * The _storedForm identifies the “type” of representation chosen * for this property. This field describes how the property value * should be interpreted. Note that the stored form described here * is not the data type of the property value, rather it is the * type of external representation employed. The data type of a * given property value is implied by the property ID. * * Can take one of the value from #aafStoredForm_e enum. */ uint16_t sf; /** * Holds a pointer to the corresponding property definition * aafPropertyDef struct. */ aafPropertyDef* def; /** * The length, in bytes, of the #val property value. */ uint16_t len; /** * The actual property value, of #len length. */ void* val; /** * Pointer to the next property in an aafObject.Properties list. */ struct aafProperty* next; } aafProperty; /** * This structure represents an AAF Object, once it has been parsed. * * This structure is to be added to the AAF_Data.Objects list. * * Each aafObject correspond to a Compound File Tree Node. * * Each aafObject property correspond to a property entry in this * Node/properties stream. */ typedef struct aafObject { /** * Pointer to the corresponding Class, in the AAF_Data.Class list. */ aafClass* Class; /** * Pointer to the corresponding Node in the Compound File Tree. */ cfbNode* Node; /** * UTF-8 name of the Node in the Compound File Tree, set from cfbNode._ab. */ char* Name; /** * Pointer to an aafProperty list. This list holds the retrieved * Object properties. */ aafProperty* Properties; /** * Pointer to an aafStrongRefSetHeader_t struct. * * This pointer keeps track of the Index Header, when the * Object belongs to either a Set or a Vector, else, it shall * remain NULL. * * Here we can use an aafStrongRefSetHeader_t struct to hold * an aafStrongRefVectorHeader_t, because both structs begin * with the same bytes, exept the first one is bigger. So in * case of a Vector, the remaining bytes will simply remain * NULL. */ aafStrongRefSetHeader_t* Header; /** * Pointer to an aafStrongRefSetEntry_t struct. * * This pointer keeps track of the Index Entry, when the * Object belongs to either a Set or a Vector, else, it shall * remain NULL. * * Here we can use an aafStrongRefSetEntry_t struct to hold * an aafStrongRefVectorEntry_t, because both structs begin * with the same bytes, exept the first one is bigger. So in * case of a Vector, the remaining bytes will simply remain * NULL. */ aafStrongRefSetEntry_t* Entry; /** * Pointer to the Parent Object, that is the upper "Node" in * the Compound File Tree. */ struct aafObject* Parent; /** * Pointer to the next Object in Set/Vector */ struct aafObject* next; struct aafObject* prev; /** * Pointer to the next Object in the AAF_Data.Objects list. */ struct aafObject* nextObj; /* keeps track of aafd, mostly to access aafd->verb without having to pass aafd to some functions */ struct _aafData* aafd; } aafObject; /** * This structure is the main structure when using LibAAF. * * It holds a pointer to the CFB_Data structure for file * access, a pointer to the AAF Classes list, a pointer to * the AAF Objects list and a bunch of pointers to some * key Objects in the AAF Objects Tree called shortcuts. * * Those shortcuts allows to quickly retrieve some AAF * key Objects without the need to parse the tree everytime. */ typedef struct _aafData { /** * Pointer to the LibCFB CFB_Data structure. */ CFB_Data* cfbd; /** * Pointer to the AAF Classes list. */ aafClass* Classes; /** * Pointer to the AAF Object list. * * @note This list is intended to keep track of all the allocated Objects, not for * parsing. For tree access, the AAF_Data.Root pointer should be used. */ aafObject* Objects; struct Header { aafObject* obj; int16_t ByteOrder; aafTimeStamp_t* LastModified; aafVersionType_t* Version; uint32_t ObjectModelVersion; const aafUID_t* OperationalPattern; // EssenceContainers; TODO AUIDSet_t // DescriptiveSchemes: TODO AUIDSet_t } Header; struct Identification { aafObject* obj; char* CompanyName; char* ProductName; aafProductVersion_t* ProductVersion; char* ProductVersionString; aafUID_t* ProductID; aafTimeStamp_t* Date; aafProductVersion_t* ToolkitVersion; char* Platform; aafUID_t* GenerationAUID; } Identification; /** * Pointer to the first Root Object, that is to the the top of the Tree. */ aafObject* Root; /** * (Shortcut) pointer to the Header Object in the Tree. */ // aafObject *Header; /** * (Shortcut) pointer to the MetaDictionary Object in the Tree. */ aafObject* MetaDictionary; /** * (Shortcut) pointer to the ClassDefinition Object in the Tree. */ aafObject* ClassDefinition; /** * (Shortcut) pointer to the TypeDefinition Object in the Tree. */ aafObject* TypeDefinition; /** * (Shortcut) pointer to the Identification Object in the Tree. */ // aafObject *Identification; /** * (Shortcut) pointer to the Content Object in the Tree. */ aafObject* Content; /** * (Shortcut) pointer to the Dictionary Object in the Tree. */ aafObject* Dictionary; /** * (Shortcut) pointer to the Mobs Object in the Tree. */ aafObject* Mobs; /** * (Shortcut) pointer to the EssenceData Object in the Tree. */ aafObject* EssenceData; /** * (Shortcut) pointer to the OperationDefinition Object in the Tree. */ aafObject* OperationDefinition; /** * (Shortcut) pointer to the ParameterDefinition Object in the Tree. */ aafObject* ParameterDefinition; /** * (Shortcut) pointer to the DataDefinition Object in the Tree. */ aafObject* DataDefinition; /** * (Shortcut) pointer to the PluginDefinition Object in the Tree. */ aafObject* PluginDefinition; /** * (Shortcut) pointer to the CodecDefinition Object in the Tree. */ aafObject* CodecDefinition; /** * (Shortcut) pointer to the ContainerDefinition Object in the Tree. */ aafObject* ContainerDefinition; /** * (Shortcut) pointer to the InterpolationDefinition Object in the Tree. */ aafObject* InterpolationDefinition; /** * (Shortcut) pointer to the KLVDataDefinition Object in the Tree. */ aafObject* KLVDataDefinition; /** * (Shortcut) pointer to the TaggedValueDefinition Object in the Tree. */ aafObject* TaggedValueDefinition; struct aafLog* log; } AAF_Data; /** * Compares two aafUID_t, returns 1 if equal or 0 otherwise. * https://github.com/Ardour/ardour/pull/805#issuecomment-1595788696 */ #define aafUIDCmp(auid1, auid2) \ ((auid1) != NULL && \ (auid2) != NULL && \ (auid1)->Data1 == (auid2)->Data1 && \ (auid1)->Data2 == (auid2)->Data2 && \ (auid1)->Data3 == (auid2)->Data3 && \ (auid1)->Data4[0] == (auid2)->Data4[0] && \ (auid1)->Data4[1] == (auid2)->Data4[1] && \ (auid1)->Data4[2] == (auid2)->Data4[2] && \ (auid1)->Data4[3] == (auid2)->Data4[3] && \ (auid1)->Data4[4] == (auid2)->Data4[4] && \ (auid1)->Data4[5] == (auid2)->Data4[5] && \ (auid1)->Data4[6] == (auid2)->Data4[6] && \ (auid1)->Data4[7] == (auid2)->Data4[7]) /** * Compares two aafMobID_t, returns 1 if equal or 0 otherwise. */ #define aafMobIDCmp(mobID1, mobID2) \ ((mobID1) != NULL && (mobID2) != NULL && memcmp ((mobID1), (mobID2), sizeof (aafMobID_t)) == 0) /** * Compares two aafSlotID_t, returns 1 if equal or 0 otherwise. * NOTE: is unused. */ #define aafSlotIDCmp(slotID1, slotID2) \ ((slotID1) == (slotID2)) /** * Converts an aafRational_t to a float number. */ #define aafRationalToFloat(r) \ (((r).denominator == 0) ? 0 : ((float)(r).numerator / (float)(r).denominator)) #define aafRationalToDouble(r) \ (((r).denominator == 0) ? 0 : ((double)(r).numerator / (r).denominator)) /** * Converts an aafRational_t to a int64 number. * NOTE: is unused. */ #define aafRationalToint64(r) \ (((r).denominator == 0) ? 0 : (int64_t)((r).numerator / (r).denominator)) /** * Loops through each aafPropertyIndexEntry_t of a "properties" node stream. * * @param Header Pointer to the stream's aafPropertyIndexHeader_t struct. * @param Entry Pointer that will receive each aafPropertyIndexEntry_t struct. * @param Value Pointer to each property's data value, of aafPropertyIndexEntry_t._length * bytes length. * @param i uint32_t iterator. */ #define foreachPropertyEntry(propStream, Header, Entry, Value, valueOffset, i) \ for (valueOffset = sizeof (aafPropertyIndexHeader_t) + (Header._entryCount * sizeof (aafPropertyIndexEntry_t)), \ i = 0; \ i < Header._entryCount && \ memcpy (&Entry, (propStream + ((sizeof (aafPropertyIndexHeader_t)) + (sizeof (aafPropertyIndexEntry_t) * i))), sizeof (aafPropertyIndexEntry_t)) && \ (Value = propStream + valueOffset); \ valueOffset += Entry._length, \ i++) /** * @name Initialisation functions * @{ */ /** * Allocates a new AAF_Data structure. * * @return A pointer to the newly allocated structure. */ AAF_Data* aaf_alloc (struct aafLog* log); /** * Loads an AAF file and sets the AAF_Data sructure accordingly. * * @param aafd Pointer to the AAF_Data structure. * @param file Pointer to a null terminated string holding the filepath. * * @return 0 on success\n * 1 on failure */ int aaf_load_file (AAF_Data* aafd, const char* file); /** * @} */ /** * @name Release function * @{ */ /** * Releases the CFB_Data structure, fclose() the file and frees the AAF_Data structure. * * @param aafd Pointer to the AAF_Data structure. */ void aaf_release (AAF_Data** aafd); /** * @} */ /** * @name Access functions * @{ */ /** * Retrieves, for a given Object, its path in the Compound File Binary Tree. * * @note It is the caller responsability to free the returned pointer. * * @param Obj Pointer to the aafObject. * * @return Pointer to a null-terminated string holding the Object's path. */ char* aaf_get_ObjectPath (aafObject* Obj); /** * Resolves a given WeakReference from a given Dictionary (a Set or Vector list of * aafObjects), and returns a pointer to the corresponding Object if it was found. * * @param ref Pointer to the WeakReference to search for. * @param list Pointer to the aafObject list from which the Reference will be searched for. * * @return A pointer to the aafObject if found,\n * NULL otherwise. * */ aafObject* aaf_get_ObjectByWeakRef (aafObject* list, aafWeakRef_t* ref); aafUID_t* aaf_get_InterpolationIdentificationByWeakRef (AAF_Data* aafd, aafWeakRef_t* InterpolationDefWeakRef); aafUID_t* aaf_get_OperationIdentificationByWeakRef (AAF_Data* aafd, aafWeakRef_t* OperationDefWeakRef); aafUID_t* aaf_get_ContainerIdentificationByWeakRef (AAF_Data* aafd, aafWeakRef_t* ContainerDefWeakRef); aafUID_t* aaf_get_DataIdentificationByWeakRef (AAF_Data* aafd, aafWeakRef_t* DataDefWeakRef); aafObject* aaf_get_ObjectAncestor (aafObject* Obj, const aafUID_t* ClassID); int aaf_ObjectInheritsClass (aafObject* Obj, const aafUID_t* classID); /** * Retrieves a Mob Object by its given MobID. * * @param Mobs Pointer to the Mob Object list. * @param MobID Pointer to the MobID where're looking for. * * @return A pointer to the Mob aafObject if found,\n * NULL otherwise. */ aafObject* aaf_get_MobByID (aafObject* Mobs, aafMobID_t* MobID); aafObject* aaf_get_MobSlotBySlotID (aafObject* MobSlots, aafSlotID_t SlotID); aafObject* aaf_get_EssenceDataByMobID (AAF_Data* aafd, aafMobID_t* MobID); /** * Loops through each aafObject of a list, that is of a Set or Vector. It is also * possible to filter the returned Object by ClassID. * * This function should be used as the conditional expression of a while loop. The * aaf_foreach_ObjectInSet() is an implementation of this. * * @param Obj Pointer to pointer to an aafObject structure that will receive each * Object of the list. * @param head Pointer to the first Object of the list. * @param filter Pointer to a ClassID to use as a filter, shall be NULL if unused. * * @return 1 if has another Object,\n * 0 if has no more Object. */ int _aaf_foreach_ObjectInSet (aafObject** Obj, aafObject* head, const aafUID_t* filter); /** * Convenience macro, that implements the _aaf_foreach_ObjectInSet() function. This should * be used istead of directly calling _aaf_foreach_ObjectInSet(). */ #define AAF_foreach_ObjectInSet(Obj, head, filter) \ while (_aaf_foreach_ObjectInSet (Obj, head, filter)) void* aaf_get_TaggedValueByName (AAF_Data* aafd, aafObject* TaggedValueVector, const char* name, const aafUID_t* type); /** * Retrieves an Object property by ID. * * @param Obj Pointer to the Object to get the property from. * @param pid Index of the requested property. * * @return A pointer to the property if found,\n * NULL otherwise. */ aafProperty* aaf_get_property (aafObject* Obj, aafPID_t pid); aafUID_t* aaf_get_ParamDefIDByName (AAF_Data* aafd, const char* name); /** * Retrieves a Property ID by its name. * * @param aafd Pointer to the AAF_Data structure. * @param name Name of the property to look for. * * @return The PID of the property if it was found\n * 0 otherwise. */ aafPID_t aaf_get_PropertyIDByName (AAF_Data* aafd, const char* name); aafUID_t* aaf_get_OperationDefIDByName (AAF_Data* aafd, const char* OpDefName); /** * Retrieves an Object property by ID, and returns its value. * * Special case is the StrongReference, where the function returns * the Object directly, instead of its reference. * * Function performs a type check before it returns. * * Caller must free the returned value, only if property is of type * AAFTypeID_String. * * @param Obj Pointer to the Object to get the property from. * @param pid Index of the requested property. * * @return A pointer to the property's value if found,\n * NULL otherwise. */ void* aaf_get_propertyValue (aafObject* Obj, aafPID_t pid, const aafUID_t* typeID); /** * Safely get an Indirect value, after it was retrieved using aaf_get_propertyValue(). * Function checks value type and in case of AAFTypeID_String, performs allocation * and UTF8 conversion. * * Caller must free the returned value, only if Indirect is of type * AAFTypeID_String. * * @param aafd Pointer to the AAF_Data structure. * @param Indirect Pointer to the Indirect structure. * @param typeDef Type definition expected from the Indirect. * * @return A pointer to the Indirect value, or a pointer to an allocated char if Indirect is AAFTypeID_String\n * NULL in case of error. */ void* aaf_get_indirectValue (AAF_Data* aafd, aafIndirect_t* Indirect, const aafUID_t* typeDef); /** * @} */ /** * @} */ #endif // ! __AAFCore_h__