diff --git a/libs/aaf/AAFCore.c b/libs/aaf/AAFCore.c index 04bbfb5c91..f4947fabb8 100644 --- a/libs/aaf/AAFCore.c +++ b/libs/aaf/AAFCore.c @@ -880,43 +880,43 @@ parse_Header (AAF_Data* aafd) if (ByteOrder == NULL) { warning ("Missing Header::ByteOrder."); + } else { + aafd->Header.ByteOrder = *ByteOrder; } - aafd->Header.ByteOrder = *ByteOrder; - aafTimeStamp_t* LastModified = aaf_get_propertyValue (Header, PID_Header_LastModified, &AAFTypeID_TimeStamp); if (LastModified == NULL) { warning ("Missing Header::LastModified."); + } else { + aafd->Header.LastModified = LastModified; } - aafd->Header.LastModified = LastModified; - aafVersionType_t* Version = aaf_get_propertyValue (Header, PID_Header_Version, &AAFTypeID_VersionType); if (Version == NULL) { warning ("Missing Header::Version."); + } else { + aafd->Header.Version = Version; } - aafd->Header.Version = Version; - uint32_t* ObjectModelVersion = aaf_get_propertyValue (Header, PID_Header_ObjectModelVersion, &AAFTypeID_UInt32); if (ObjectModelVersion == NULL) { warning ("Missing Header::ObjectModelVersion."); + } else { + aafd->Header.ObjectModelVersion = *ObjectModelVersion; } - aafd->Header.ObjectModelVersion = *ObjectModelVersion; - const aafUID_t* OperationalPattern = aaf_get_propertyValue (Header, PID_Header_OperationalPattern, &AAFTypeID_AUID); if (OperationalPattern == NULL) { warning ("Missing Header::OperationalPattern."); - OperationalPattern = (const aafUID_t*)&AUID_NULL; + aafd->Header.OperationalPattern = (const aafUID_t*)&AUID_NULL; + } else { + aafd->Header.OperationalPattern = OperationalPattern; } - aafd->Header.OperationalPattern = OperationalPattern; - return 0; } @@ -934,74 +934,74 @@ parse_Identification (AAF_Data* aafd) if (Company == NULL) { warning ("Missing Identification::CompanyName."); + } else { + aafd->Identification.CompanyName = Company; } - aafd->Identification.CompanyName = Company; - wchar_t* ProductName = aaf_get_propertyValue (Identif, PID_Identification_ProductName, &AAFTypeID_String); if (ProductName == NULL) { warning ("Missing Identification::ProductName."); + } else { + aafd->Identification.ProductName = ProductName; } - aafd->Identification.ProductName = ProductName; - aafProductVersion_t* ProductVersion = aaf_get_propertyValue (Identif, PID_Identification_ProductVersion, &AAFTypeID_ProductVersion); if (ProductVersion == NULL) { warning ("Missing Identification::ProductVersion."); + } else { + aafd->Identification.ProductVersion = ProductVersion; } - aafd->Identification.ProductVersion = ProductVersion; - wchar_t* ProductVersionString = aaf_get_propertyValue (Identif, PID_Identification_ProductVersionString, &AAFTypeID_String); if (ProductVersionString == NULL) { warning ("Missing Identification::ProductVersionString."); + } else { + aafd->Identification.ProductVersionString = ProductVersionString; } - aafd->Identification.ProductVersionString = ProductVersionString; - aafUID_t* ProductID = aaf_get_propertyValue (Identif, PID_Identification_ProductID, &AAFTypeID_AUID); if (ProductID == NULL) { warning ("Missing Identification::ProductID."); + } else { + aafd->Identification.ProductID = ProductID; } - aafd->Identification.ProductID = ProductID; - aafTimeStamp_t* Date = aaf_get_propertyValue (Identif, PID_Identification_Date, &AAFTypeID_TimeStamp); if (Date == NULL) { warning ("Missing Identification::Date."); + } else { + aafd->Identification.Date = Date; } - aafd->Identification.Date = Date; - aafProductVersion_t* ToolkitVersion = aaf_get_propertyValue (Identif, PID_Identification_ToolkitVersion, &AAFTypeID_ProductVersion); if (ToolkitVersion == NULL) { warning ("Missing Identification::ToolkitVersion."); + } else { + aafd->Identification.ToolkitVersion = ToolkitVersion; } - aafd->Identification.ToolkitVersion = ToolkitVersion; - wchar_t* Platform = aaf_get_propertyValue (Identif, PID_Identification_Platform, &AAFTypeID_String); if (Platform == NULL) { warning ("Missing Identification::Platform."); + } else { + aafd->Identification.Platform = Platform; } - aafd->Identification.Platform = Platform; - aafUID_t* GenerationAUID = aaf_get_propertyValue (Identif, PID_Identification_GenerationAUID, &AAFTypeID_AUID); if (GenerationAUID == NULL) { warning ("Missing Identification::GenerationAUID."); + } else { + aafd->Identification.GenerationAUID = GenerationAUID; } - aafd->Identification.GenerationAUID = GenerationAUID; - return 0; } diff --git a/libs/aaf/AAFIAudioFiles.c b/libs/aaf/AAFIAudioFiles.c index ef2ca6f05d..5018a8d7a4 100644 --- a/libs/aaf/AAFIAudioFiles.c +++ b/libs/aaf/AAFIAudioFiles.c @@ -69,7 +69,7 @@ embeddedAudioDataReaderCallback (unsigned char* buf, size_t offset, size_t reqLe static size_t externalAudioDataReaderCallback (unsigned char* buf, size_t offset, size_t reqLen, void* user1, void* user2, void* user3); -char* +wchar_t* aafi_locate_external_essence_file (AAF_Iface* aafi, const wchar_t* original_uri_filepath, const char* search_location) { /* @@ -82,10 +82,11 @@ aafi_locate_external_essence_file (AAF_Iface* aafi, const wchar_t* original_uri_ * uses the / character as the path separator. */ - char* uri_filepath = NULL; - char* local_filepath = NULL; - char* aaf_path = NULL; - char* retpath = NULL; + char* uri_filepath = NULL; + char* local_filepath = NULL; + char* aaf_path = NULL; + char* foundpath = NULL; + wchar_t* retpath = NULL; struct uri* uri = NULL; @@ -94,23 +95,14 @@ aafi_locate_external_essence_file (AAF_Iface* aafi, const wchar_t* original_uri_ goto err; } - size_t uri_filepath_len = wcslen (original_uri_filepath) + 1; - - uri_filepath = malloc (uri_filepath_len); + uri_filepath = laaf_util_wstr2str (original_uri_filepath); if (uri_filepath == NULL) { - error ("Could not allocate memory : %s", strerror (errno)); + error ("Could not convert original_uri_filepath from wstr to str : %ls", original_uri_filepath); goto err; } - int reqlen = snprintf (uri_filepath, uri_filepath_len, "%ls", original_uri_filepath); - - if (reqlen < 0 || (unsigned)reqlen >= uri_filepath_len) { - error ("Failed converting wide char URI filepath to byte char%s", (reqlen < 0) ? " : encoding error" : ""); - goto err; - } - - // debug( "Original URI filepath : %s", uri_filepath ); + // debug( "Original URI : %s", uri_filepath ); uri = uriParse (uri_filepath, URI_OPT_DECODE_ALL, aafi->dbg); @@ -124,7 +116,7 @@ aafi_locate_external_essence_file (AAF_Iface* aafi, const wchar_t* original_uri_ goto err; } - // debug( "Decoded URI's filepath : %s", uri->path ); + // debug( "Decoded URI's path : %s", uri->path ); /* extract relative path to essence file : "/" */ @@ -164,7 +156,7 @@ aafi_locate_external_essence_file (AAF_Iface* aafi, const wchar_t* original_uri_ if (access (local_filepath, F_OK) != -1) { // debug( "FOUND: %s", local_filepath ); - retpath = local_filepath; + foundpath = local_filepath; goto found; } @@ -186,7 +178,7 @@ aafi_locate_external_essence_file (AAF_Iface* aafi, const wchar_t* original_uri_ if (access (local_filepath, F_OK) != -1) { // debug( "FOUND: %s", local_filepath ); - retpath = local_filepath; + foundpath = local_filepath; goto found; } @@ -198,7 +190,7 @@ aafi_locate_external_essence_file (AAF_Iface* aafi, const wchar_t* original_uri_ if (access (uri_filepath, F_OK) != -1) { // debug( "FOUND: %s", uri_filepath ); - retpath = uri_filepath; + foundpath = uri_filepath; goto found; } @@ -206,7 +198,7 @@ aafi_locate_external_essence_file (AAF_Iface* aafi, const wchar_t* original_uri_ if (access (uri->path, F_OK) != -1) { // debug( "FOUND: %s", uri->path ); - retpath = uri->path; + foundpath = uri->path; goto found; } @@ -265,7 +257,7 @@ aafi_locate_external_essence_file (AAF_Iface* aafi, const wchar_t* original_uri_ if (access (local_filepath, F_OK) != -1) { // debug( "FOUND: %s", filepath ); - retpath = local_filepath; + foundpath = local_filepath; goto found; } @@ -287,7 +279,7 @@ aafi_locate_external_essence_file (AAF_Iface* aafi, const wchar_t* original_uri_ if (access (local_filepath, F_OK) != -1) { // debug( "FOUND: %s", filepath ); - retpath = local_filepath; + foundpath = local_filepath; goto found; } @@ -297,7 +289,15 @@ aafi_locate_external_essence_file (AAF_Iface* aafi, const wchar_t* original_uri_ // debug("File not found"); found: - retpath = laaf_util_c99strdup (retpath); + if (foundpath) { + retpath = laaf_util_str2wstr (foundpath); + + if (retpath == NULL) { + error ("Could not convert foundpath from str to wstr : %s", foundpath); + goto err; + } + } + goto end; err: @@ -414,7 +414,7 @@ aafi_extract_audio_essence (AAF_Iface* aafi, aafiAudioEssence* audioEssence, con memset (&wavBext, 0x00, sizeof (wavBext)); memcpy (wavBext.umid, audioEssence->sourceMobID, sizeof (aafMobID_t)); if (audioEssence->mobSlotEditRate) { - wavBext.time_reference = eu2sample (audioEssence->samplerate, audioEssence->mobSlotEditRate, audioEssence->timeReference); + wavBext.time_reference = laaf_util_converUnit (audioEssence->timeReference, audioEssence->mobSlotEditRate, audioEssence->samplerateRational); } if (datasz >= (uint32_t)-1) { @@ -443,10 +443,10 @@ aafi_extract_audio_essence (AAF_Iface* aafi, aafiAudioEssence* audioEssence, con goto err; } - reqlen = swprintf (audioEssence->usable_file_path, strlen (filepath) + 1, L"%" WPRIs, filepath); + audioEssence->usable_file_path = laaf_util_str2wstr (filepath); - if (reqlen < 0) { - error ("Failed setting usable_file_path"); + if (audioEssence->usable_file_path == NULL) { + error ("Could not convert usable_file_path from str to wstr : %s", filepath); goto err; } @@ -473,94 +473,118 @@ end: } int -aafi_parse_audio_summary (AAF_Iface* aafi, aafiAudioEssence* audioEssence) +aafi_parse_audio_essence (AAF_Iface* aafi, aafiAudioEssence* audioEssence) { - // laaf_util_dump_hex( audioEssence->summary->val, audioEssence->summary->len ); - - int rc = 0; - char* externalFilePath = NULL; - FILE* fp = NULL; + // aafi->dbg->_dbg_msg_pos += laaf_util_dump_hex( audioEssence->summary->val, audioEssence->summary->len, &aafi->dbg->_dbg_msg, &aafi->dbg->_dbg_msg_size, aafi->dbg->_dbg_msg_pos ); + int rc = 0; + char* externalFilePath = NULL; + FILE* fp = NULL; struct RIFFAudioFile RIFFAudioFile; - if (audioEssence->is_embedded) { - if (audioEssence->summary == NULL) { - warning ("TODO: Audio essence has no summary. TODO: Should try essence data stream ?"); + /* try audioEssence->summary first, for both embedded and external */ + + if (audioEssence->summary) { + rc = riff_parseAudioFile (&RIFFAudioFile, RIFF_PARSE_AAF_SUMMARY, &embeddedAudioDataReaderCallback, audioEssence->summary->val, &audioEssence->summary->len, aafi, aafi->dbg); + + if (rc < 0) { + warning ("Could not parse essence summary of %ls", audioEssence->file_name); + + if (audioEssence->is_embedded) { + return -1; + } + } else { + audioEssence->channels = RIFFAudioFile.channels; + audioEssence->samplerate = RIFFAudioFile.sampleRate; + audioEssence->samplesize = RIFFAudioFile.sampleSize; + audioEssence->length = RIFFAudioFile.sampleCount; + + audioEssence->samplerateRational->numerator = audioEssence->samplerate; + audioEssence->samplerateRational->denominator = 1; + + return 0; + } + } else if (audioEssence->is_embedded) { + if (audioEssence->type != AAFI_ESSENCE_TYPE_PCM) { + warning ("TODO: Embedded audio essence has no summary. Should we try essence data stream ?"); + } + + return -1; + } else if (!audioEssence->usable_file_path) { + // warning( "Can't parse a missing external essence file" ); + return -1; + } + + if (laaf_util_fop_is_wstr_fileext (audioEssence->usable_file_path, L"wav") || + laaf_util_fop_is_wstr_fileext (audioEssence->usable_file_path, L"wave") || + laaf_util_fop_is_wstr_fileext (audioEssence->usable_file_path, L"aif") || + laaf_util_fop_is_wstr_fileext (audioEssence->usable_file_path, L"aiff") || + laaf_util_fop_is_wstr_fileext (audioEssence->usable_file_path, L"aifc")) { + externalFilePath = laaf_util_wstr2str (audioEssence->usable_file_path); + + if (externalFilePath == NULL) { + error ("Could not convert usable_file_path from wstr to str : %ls", audioEssence->usable_file_path); goto err; } - /* - * Adobe Premiere Pro, embedded mp3/mp4 files converted to PCM/AIFF on export, AAFClassID_AIFCDescriptor, 'COMM' is valid. - * ______________________________ Hex Dump ______________________________ - * - * 46 4f 52 4d 00 00 00 32 41 49 46 43 43 4f 4d 4d | FORM...2 AIFCCOMM - * 00 00 00 26 00 01 00 00 00 00 00 10 40 0e bb 80 | ........ ........ - * 00 00 00 00 00 00 4e 4f 4e 45 0e 4e 6f 74 20 43 | ......NO NE.Not.C - * 6f 6d 70 72 65 73 73 65 64 00 | ompresse d. - * ______________________________________________________________________ - */ + fp = fopen (externalFilePath, "rb"); - // laaf_util_dump_hex( audioEssence->summary->val, audioEssence->summary->len ); + if (fp == NULL) { + error ("Could not open external audio essence file for reading : %s", externalFilePath); + goto err; + } - rc = riff_parseAudioFile (&RIFFAudioFile, RIFF_PARSE_ONLY_HEADER, &embeddedAudioDataReaderCallback, audioEssence->summary->val, &audioEssence->summary->len, aafi, aafi->dbg); + rc = riff_parseAudioFile (&RIFFAudioFile, 0, &externalAudioDataReaderCallback, fp, externalFilePath, aafi, aafi->dbg); if (rc < 0) { - warning ("TODO: Could not parse embedded essence summary. Should try essence data stream ?"); + error ("Failed parsing external audio essence file : %s", externalFilePath); goto err; } + if (audioEssence->channels > 0 && audioEssence->channels != RIFFAudioFile.channels) { + warning ("%ls : summary channel count (%i) mismatch located file (%i)", audioEssence->usable_file_path, audioEssence->channels, RIFFAudioFile.channels); + } + + if (audioEssence->samplerate > 0 && audioEssence->samplerate != RIFFAudioFile.sampleRate) { + warning ("%ls : summary samplerate (%i) mismatch located file (%i)", audioEssence->usable_file_path, audioEssence->samplerate, RIFFAudioFile.sampleRate); + } + + if (audioEssence->samplesize > 0 && audioEssence->samplesize != RIFFAudioFile.sampleSize) { + warning ("%ls : summary samplesize (%i) mismatch located file (%i)", audioEssence->usable_file_path, audioEssence->samplesize, RIFFAudioFile.sampleSize); + } + + if (audioEssence->length > 0 && audioEssence->length != RIFFAudioFile.sampleCount) { + warning ("%ls : summary samplecount (%" PRIi64 ") mismatch located file (%" PRIi64 ")", audioEssence->usable_file_path, audioEssence->length, RIFFAudioFile.sampleCount); + } + audioEssence->channels = RIFFAudioFile.channels; audioEssence->samplerate = RIFFAudioFile.sampleRate; audioEssence->samplesize = RIFFAudioFile.sampleSize; - audioEssence->length = RIFFAudioFile.duration; + audioEssence->length = RIFFAudioFile.sampleCount; + + audioEssence->samplerateRational->numerator = audioEssence->samplerate; + audioEssence->samplerateRational->denominator = 1; } else { - /* TODO: can external essence have audioEssence->summary too ? If mp3 (Resolve 18.5.aaf) ? */ + /* + * should be considered as a non-pcm audio format + * +│ 04317│├──◻ AAFClassID_TimelineMobSlot [slot:6 track:4] (DataDef : AAFDataDef_Sound) : Audio 4 - Layered Audio Editing +│ 01943││ └──◻ AAFClassID_Sequence +│ 02894││ └──◻ AAFClassID_SourceClip +│ 02899││ └──◻ AAFClassID_MasterMob (UsageCode: n/a) : speech-sample +│ 04405││ └──◻ AAFClassID_TimelineMobSlot [slot:1 track:1] (DataDef : AAFDataDef_Sound) +│ 03104││ └──◻ AAFClassID_SourceClip +│ 04140││ └──◻ AAFClassID_SourceMob (UsageCode: n/a) : speech-sample +│ 01287││ └──◻ AAFClassID_PCMDescriptor +│ 01477││ └──◻ AAFClassID_NetworkLocator : file:///C:/Users/user/Desktop/libAAF/test/res/speech-sample.mp3 + * + */ - externalFilePath = aafi_locate_external_essence_file (aafi, audioEssence->original_file_path, aafi->ctx.options.media_location); + audioEssence->type = AAFI_ESSENCE_TYPE_UNK; - if (externalFilePath == NULL) { - error ("Could not locate external audio essence file '%ls'", audioEssence->original_file_path); - return -1; - } - - audioEssence->usable_file_path = malloc ((strlen (externalFilePath) + 1) * sizeof (wchar_t)); - - if (audioEssence->usable_file_path == NULL) { - error ("Could not allocate memory : %s", strerror (errno)); - goto err; - } - - rc = swprintf (audioEssence->usable_file_path, strlen (externalFilePath) + 1, L"%" WPRIs, externalFilePath); - - if (rc < 0) { - error ("Failed setting usable_file_path"); - goto err; - } - - if (laaf_util_fop_is_wstr_fileext (audioEssence->original_file_path, L"wav") || - laaf_util_fop_is_wstr_fileext (audioEssence->original_file_path, L"wave") || - laaf_util_fop_is_wstr_fileext (audioEssence->original_file_path, L"aif") || - laaf_util_fop_is_wstr_fileext (audioEssence->original_file_path, L"aiff") || - laaf_util_fop_is_wstr_fileext (audioEssence->original_file_path, L"aifc")) { - fp = fopen (externalFilePath, "rb"); - - if (fp == NULL) { - error ("Could not open external audio essence file for reading : %s", externalFilePath); - goto err; - } - - rc = riff_parseAudioFile (&RIFFAudioFile, RIFF_PARSE_ONLY_HEADER, &externalAudioDataReaderCallback, fp, externalFilePath, aafi, aafi->dbg); - - if (rc < 0) { - error ("Failed parsing external audio essence file : %s", externalFilePath); - goto err; - } - - audioEssence->channels = RIFFAudioFile.channels; - audioEssence->samplerate = RIFFAudioFile.sampleRate; - audioEssence->samplesize = RIFFAudioFile.sampleSize; - audioEssence->length = RIFFAudioFile.duration; - } + // /* clears any wrong data previously retrieved out of AAFClassID_PCMDescriptor */ + // audioEssence->samplerate = 0; + // audioEssence->samplesize = 0; } rc = 0; @@ -586,12 +610,12 @@ embeddedAudioDataReaderCallback (unsigned char* buf, size_t offset, size_t reqLe size_t datasz = *(size_t*)user2; AAF_Iface* aafi = (AAF_Iface*)user3; - if (offset >= datasz) { + if (offset > datasz) { error ("Requested data starts beyond data length"); return -1; } - if (offset + reqLen >= datasz) { + if (offset + reqLen > datasz) { reqLen = datasz - (offset + reqLen); } diff --git a/libs/aaf/AAFIParser.c b/libs/aaf/AAFIParser.c index 42ed3f20a3..15fb0f964b 100644 --- a/libs/aaf/AAFIParser.c +++ b/libs/aaf/AAFIParser.c @@ -35,6 +35,7 @@ */ #include +#include #include #include #include @@ -94,8 +95,8 @@ static aafRational_t AAFI_DEFAULT_TC_EDIT_RATE = { 25, 1 }; 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_combined_clip_channel_num = 0; \ + ctx.current_combined_clip_forced_length = 0; // ctx.current_track_is_multichannel = 0; // ctx.current_multichannel_track_channel = 0; // ctx.current_multichannel_track_clip_length = 0; @@ -313,8 +314,7 @@ aafi_dump_obj (AAF_Iface* aafi, aafObject* Obj, struct trace_dump* __td, int sta DBG_BUFFER_WRITE (dbg, "%s", ANSI_COLOR_RESET (dbg)); - if (aafUIDCmp (Obj->Class->ID, &AAFClassID_TimelineMobSlot) && - aafUIDCmp (Obj->Parent->Class->ID, &AAFClassID_CompositionMob)) { + if (aafUIDCmp (Obj->Class->ID, &AAFClassID_TimelineMobSlot)) { 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); @@ -331,7 +331,7 @@ aafi_dump_obj (AAF_Iface* aafi, aafObject* Obj, struct trace_dump* __td, int sta ANSI_COLOR_DARKGREY (dbg), aaft_DataDefToText (aafi->aafd, DataDefinition), ANSI_COLOR_RESET (dbg), - (name[0] != 0x00) ? ": " : "", (name) ? name : L""); + (name && name[0] != 0x00) ? ": " : "", (name) ? name : L""); free (name); } else if (aafUIDCmp (Obj->Class->ID, &AAFClassID_CompositionMob) || @@ -350,10 +350,41 @@ aafi_dump_obj (AAF_Iface* aafi, aafObject* Obj, struct trace_dump* __td, int sta } else if (aafUIDCmp (Obj->Class->ID, &AAFClassID_OperationGroup)) { aafUID_t* OperationIdentification = get_OperationGroup_OperationIdentification (aafi, Obj); - DBG_BUFFER_WRITE (dbg, "(OpIdent: %s%ls%s) ", + int64_t* length = aaf_get_propertyValue (Obj, PID_Component_Length, &AAFTypeID_LengthType); + + DBG_BUFFER_WRITE (dbg, "(OpIdent: %s%ls%s; Length: %s%li%s) ", ANSI_COLOR_DARKGREY (dbg), aaft_OperationDefToText (aafi->aafd, OperationIdentification), + ANSI_COLOR_RESET (dbg), + ANSI_COLOR_DARKGREY (dbg), + (length) ? *length : -1, ANSI_COLOR_RESET (dbg)); + } else if (aafUIDCmp (Obj->Class->ID, &AAFClassID_Sequence) || + aafUIDCmp (Obj->Class->ID, &AAFClassID_Filler) || + aafUIDCmp (Obj->Class->ID, &AAFClassID_SourceClip) || + aafUIDCmp (Obj->Class->ID, &AAFClassID_Selector) || + aafUIDCmp (Obj->Class->ID, &AAFClassID_Transition)) { + int64_t* length = aaf_get_propertyValue (Obj, PID_Component_Length, &AAFTypeID_LengthType); + + DBG_BUFFER_WRITE (dbg, "(Length: %s%li%s) ", + ANSI_COLOR_DARKGREY (dbg), + (length) ? *length : -1, + ANSI_COLOR_RESET (dbg)); + } else if (aafUIDCmp (Obj->Class->ID, &AAFClassID_ConstantValue)) { + aafIndirect_t* Indirect = aaf_get_propertyValue (Obj, PID_ConstantValue_Value, &AAFTypeID_Indirect); + + if (Indirect) { + aafRational_t* multiplier = aaf_get_indirectValue (aafi->aafd, Indirect, &AAFTypeID_Rational); + + if (multiplier) { + DBG_BUFFER_WRITE (dbg, "(Value: %s%i/%i%s %+05.1lf dB) ", + ANSI_COLOR_DARKGREY (dbg), + multiplier->numerator, + multiplier->denominator, + 20 * log10 (aafRationalToFloat (*multiplier)), + ANSI_COLOR_RESET (dbg)); + } + } } // else if ( aafUIDCmp( Obj->Class->ID, &AAFClassID_TapeDescriptor ) || // aafUIDCmp( Obj->Class->ID, &AAFClassID_FilmDescriptor ) || @@ -714,14 +745,26 @@ get_Object_Ancestor (AAF_Iface* aafi, aafObject* Obj, const aafUID_t* ClassID) * 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)) + while (Obj != NULL && !aafUIDCmp (Obj->Class->ID, &AAFClassID_ContentStorage)) { + if (aafUIDCmp (ClassID, Obj->Class->ID)) { 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))) + } + + /* Works also with abstract class */ + + if (aafUIDCmp (ClassID, &AAFClassID_Mob) && (aafUIDCmp (Obj->Class->ID, &AAFClassID_MasterMob) || + aafUIDCmp (Obj->Class->ID, &AAFClassID_SourceMob) || + aafUIDCmp (Obj->Class->ID, &AAFClassID_CompositionMob))) { 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))) + } + + 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; + } + + Obj = Obj->Parent; } return NULL; @@ -760,7 +803,7 @@ get_Component_DataDefinition (AAF_Iface* aafi, aafObject* Component) // static aafUID_t * get_FileDescriptor_ContainerFormat( AAF_Iface *aafi, aafObject *FileDescriptor ) // { -// aafWeakRef_t *ContainerDefWeakRef = aaf_get_propertyValue( FileDescriptor, PID_FileDescriptor_ContainerFormat ); +// aafWeakRef_t *ContainerDefWeakRef = aaf_get_propertyValue( FileDescriptor, PID_FileDescriptor_ContainerFormat, &AAFTypeID_ClassDefinitionWeakReference ); // // if ( ContainerDefWeakRef == NULL ) { // warning( "Missing FileDescriptor::ContainerFormat." ); @@ -775,7 +818,7 @@ get_Component_DataDefinition (AAF_Iface* aafi, aafObject* Component) // } // // -// aafUID_t *ContainerIdentification = aaf_get_propertyValue( ContainerDefinition, PID_DefinitionObject_Identification ); +// aafUID_t *ContainerIdentification = aaf_get_propertyValue( ContainerDefinition, PID_DefinitionObject_Identification, &AAFTypeID_AUID ); // // if ( ContainerIdentification == NULL ) { // warning( "Missing ContainerDefinition's DefinitionObject::Identification." ); @@ -1148,12 +1191,9 @@ parse_PCMDescriptor (AAF_Iface* aafi, aafObject* PCMDescriptor, td* __ptd) 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; - // } + audioEssence->samplerate = samplerate->numerator; + audioEssence->samplerateRational->numerator = samplerate->numerator; + audioEssence->samplerateRational->denominator = samplerate->denominator; uint32_t* samplesize = aaf_get_propertyValue (PCMDescriptor, PID_SoundDescriptor_QuantizationBits, &AAFTypeID_UInt32); // uint32_t in AAF std @@ -1321,9 +1361,6 @@ parse_NetworkLocator (AAF_Iface* aafi, aafObject* NetworkLocator, td* __ptd) 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) { @@ -1961,6 +1998,18 @@ parse_OperationGroup (AAF_Iface* aafi, aafObject* OpGroup, td* __ptd) 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; + aafi->ctx.current_combined_clip_forced_length = 0; + + if (resolve_AAF (aafi)) { + /* + * This is clearly a violation of the standard (p 57). When Davinci Resolve + * exports multichannel clips, it does not set SourceClip::Length correctly. + * Insted, it's more like some sort of frame-rounded value which dosn't match + * the timeline. However, the correct value is set to OperationGroup::length. + */ + int64_t* length = aaf_get_propertyValue (OpGroup, PID_Component_Length, &AAFTypeID_LengthType); + aafi->ctx.current_combined_clip_forced_length = (length) ? *length : 0; + } aaf_foreach_ObjectInSet (&InputSegment, InputSegments, NULL) { @@ -1994,6 +2043,7 @@ parse_OperationGroup (AAF_Iface* aafi, aafObject* OpGroup, td* __ptd) aafi->ctx.current_clip_is_combined = 0; aafi->ctx.current_combined_clip_total_channel = 0; aafi->ctx.current_combined_clip_channel_num = 0; + aafi->ctx.current_combined_clip_forced_length = 0; return -1; } @@ -2009,6 +2059,7 @@ parse_OperationGroup (AAF_Iface* aafi, aafObject* OpGroup, td* __ptd) aafi->ctx.current_clip_is_combined = 0; aafi->ctx.current_combined_clip_total_channel = 0; aafi->ctx.current_combined_clip_channel_num = 0; + aafi->ctx.current_combined_clip_forced_length = 0; return -1; } @@ -2022,6 +2073,7 @@ parse_OperationGroup (AAF_Iface* aafi, aafObject* OpGroup, td* __ptd) aafi->ctx.current_clip_is_combined = 0; aafi->ctx.current_combined_clip_total_channel = 0; aafi->ctx.current_combined_clip_channel_num = 0; + aafi->ctx.current_combined_clip_forced_length = 0; // return; } else if (aafUIDCmp (OperationIdentification, &AAFOperationDef_MonoAudioGain)) { @@ -2064,8 +2116,8 @@ parse_OperationGroup (AAF_Iface* aafi, aafObject* OpGroup, td* __ptd) // } } else if (aafUIDCmp (OperationIdentification, &AAFOperationDef_StereoAudioGain)) { + /* Unknown usage and implementation, not encountered yet */ 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.) */ @@ -2222,8 +2274,9 @@ parse_SourceClip (AAF_Iface* aafi, aafObject* SourceClip, td* __ptd) * (Multi-channels) */ - aafObject* refMob = NULL; - aafObject* refMobSlot = NULL; + aafObject* targetMob = NULL; + aafObject* targetMobSlot = NULL; + aafUID_t* targetMobUsageCode = NULL; if (sourceID == NULL) { /* @@ -2238,25 +2291,27 @@ parse_SourceClip (AAF_Iface* aafi, aafObject* SourceClip, td* __ptd) */ // sourceID = parentMobID; - // refMob = ParentMob; + // targetMob = ParentMob; } else { - refMob = aaf_get_MobByID (aafi->aafd->Mobs, sourceID); + targetMob = aaf_get_MobByID (aafi->aafd->Mobs, sourceID); - if (refMob == NULL) { + if (targetMob == 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); + targetMobUsageCode = aaf_get_propertyValue (targetMob, PID_Mob_UsageCode, &AAFTypeID_UsageType); - if (refMobSlots == NULL) { + aafObject* targetMobSlots = aaf_get_propertyValue (targetMob, PID_Mob_Slots, &AAFTypeID_MobSlotStrongReferenceVector); + + if (targetMobSlots == NULL) { DUMP_OBJ_ERROR (aafi, SourceClip, &__td, "Missing target Mob PID_Mob_Slots"); return -1; } - refMobSlot = aaf_get_MobSlotBySlotID (refMobSlots, *SourceMobSlotID); + targetMobSlot = aaf_get_MobSlotBySlotID (targetMobSlots, *SourceMobSlotID); - if (refMobSlot == NULL) { + if (targetMobSlot == NULL) { /* TODO check if there is a workaround : * AAFInfo --aaf-clips '/home/agfline/Developpement/libaaf_testfiles/ADP/ADP_STTRACK_CLIPGAIN_TRACKGAIN_XFADE_NOOPTONEXPORT.aaf' */ @@ -2308,25 +2363,12 @@ parse_SourceClip (AAF_Iface* aafi, aafObject* SourceClip, td* __ptd) * 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; - } - + if (aafUIDCmp (targetMob->Class->ID, &AAFClassID_CompositionMob)) { DUMP_OBJ (aafi, SourceClip, &__td); /* Only to print trace */ __td.lv++; - DUMP_OBJ (aafi, refMob, &__td); + DUMP_OBJ (aafi, targetMob, &__td); // __td.lv++; memcpy (&ctxBackup, &(aafi->ctx), sizeof (struct aafiContext)); @@ -2336,7 +2378,7 @@ parse_SourceClip (AAF_Iface* aafi, aafObject* SourceClip, td* __ptd) aafi->ctx.current_track = ctxBackup.current_track; aafi->ctx.is_inside_derivation_chain = 1; - parse_MobSlot (aafi, refMobSlot, &__td); + parse_MobSlot (aafi, targetMobSlot, &__td); void* new_clip = aafi->ctx.current_clip; @@ -2357,7 +2399,7 @@ parse_SourceClip (AAF_Iface* aafi, aafObject* SourceClip, td* __ptd) * TODO: aafi->current_clip pointer to new_clip instead ? */ - ((aafiAudioClip*)new_clip)->len = *length; + ((aafiAudioClip*)new_clip)->len = (aafi->ctx.current_combined_clip_forced_length) ? aafi->ctx.current_combined_clip_forced_length : *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; @@ -2378,7 +2420,7 @@ parse_SourceClip (AAF_Iface* aafi, aafObject* SourceClip, td* __ptd) * offset and gain with correct values. */ - ((aafiVideoClip*)new_clip)->len = *length; + ((aafiVideoClip*)new_clip)->len = (aafi->ctx.current_combined_clip_forced_length) ? aafi->ctx.current_combined_clip_forced_length : *length; ((aafiVideoClip*)new_clip)->essence_offset = *startTime; aafi->Video->Tracks->current_pos += ((aafiVideoClip*)new_clip)->len; @@ -2387,7 +2429,7 @@ parse_SourceClip (AAF_Iface* aafi, aafObject* SourceClip, td* __ptd) return 0; - } else if (aafUIDCmp (refMob->Class->ID, &AAFClassID_MasterMob)) { + } else if (aafUIDCmp (targetMob->Class->ID, &AAFClassID_MasterMob)) { /* * We are inside the derivation chain and we reached the SourceClip * pointing to MasterMob (the audio essence). @@ -2431,65 +2473,60 @@ parse_SourceClip (AAF_Iface* aafi, aafObject* SourceClip, td* __ptd) * We just have to check everything match for all clips left (each clip represents a channel) */ - if (aafi->ctx.current_clip->len != *length) { + if (aafi->ctx.current_combined_clip_forced_length == 0 && 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; - } + if (aafMobIDCmp (aafi->ctx.current_clip->essencePointerList->essence->masterMobID, sourceID)) { + /* + * Clip channel rely on the same audio file source (single multichannel file) + * + * Assume that all clip channels will point to the same multichannel essence file, in the right order. + * (Davinci Resolve multichannel clips) + */ - DUMP_OBJ (aafi, SourceClip, &__td); - return 0; + aafi->ctx.current_clip->essencePointerList->essenceChannel = 0; + + 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 - */ + aafiAudioClip* audioClip = NULL; - 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) { + if (!aafi->ctx.current_clip_is_combined || (aafi->ctx.current_clip_is_combined && aafi->ctx.current_combined_clip_channel_num == 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); + + 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 = (aafi->ctx.current_combined_clip_forced_length) ? aafi->ctx.current_combined_clip_forced_length : *length; + + audioClip->essence_offset = *startTime; + + aafi->ctx.current_clip = audioClip; + } else { + /* clip is multichannel and we are parsing SourceClip channel > first channel */ + audioClip = aafi->ctx.current_clip; + } + + if (!aafi->ctx.is_inside_derivation_chain && + (!aafi->ctx.current_clip_is_combined || (aafi->ctx.current_clip_is_combined && aafi->ctx.current_combined_clip_channel_num == 0))) { + /* + * We update ONLY ONCE when SourceClip belongs to a OpGroup AAFOperationDef_AudioChannelCombiner. + * * 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 @@ -2509,30 +2546,13 @@ 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 +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 + */ - aafi->ctx.current_track->current_pos += audioClip->len; + aafi->ctx.current_track->current_pos += (aafi->ctx.current_combined_clip_forced_length) ? aafi->ctx.current_combined_clip_forced_length : audioClip->len; } if (aafi->ctx.current_clip_is_combined == 0) { @@ -2544,8 +2564,8 @@ POS NOT UPDATED HERE ------------------> └──◻ AAFClassID_SourceClip } } - if (aafUIDCmp (refMob->Class->ID, &AAFClassID_MasterMob)) { - if (refMobSlot == NULL) { + if (aafUIDCmp (targetMob->Class->ID, &AAFClassID_MasterMob)) { + if (targetMobSlot == NULL) { /* TODO isn't it already checked above ? */ DUMP_OBJ_ERROR (aafi, SourceClip, &__td, "Missing target MobSlot"); return -1; @@ -2555,22 +2575,22 @@ POS NOT UPDATED HERE ------------------> └──◻ AAFClassID_SourceClip /* Only to print trace */ __td.lv++; - DUMP_OBJ (aafi, refMob, &__td); + DUMP_OBJ (aafi, targetMob, &__td); memcpy (&ctxBackup, &(aafi->ctx), sizeof (struct aafiContext)); - RESET_CONTEXT (aafi->ctx); + /* TODO: Commented out to avoid reset of ctx.current_clip_is_combined */ + // RESET_CONTEXT( aafi->ctx ); aafi->ctx.current_track = ctxBackup.current_track; aafi->ctx.current_clip = audioClip; - parse_MobSlot (aafi, refMobSlot, &__td); + /* retrieve essence */ + parse_MobSlot (aafi, targetMobSlot, &__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( ) + DUMP_OBJ_ERROR (aafi, SourceClip, &__td, "Targeted Mob isn't MasterMob : %ls", aaft_ClassIDToText (aafi->aafd, targetMob->Class->ID)); return -1; } @@ -2633,8 +2653,8 @@ POS NOT UPDATED HERE ------------------> └──◻ AAFClassID_SourceClip aafi->ctx.current_video_clip = videoClip; - if (aafUIDCmp (refMob->Class->ID, &AAFClassID_MasterMob)) { - if (refMobSlot == NULL) { + if (aafUIDCmp (targetMob->Class->ID, &AAFClassID_MasterMob)) { + if (targetMobSlot == NULL) { /* TODO isn't it already checked above ? */ DUMP_OBJ_ERROR (aafi, SourceClip, &__td, "Missing target MobSlot"); return -1; @@ -2644,18 +2664,18 @@ POS NOT UPDATED HERE ------------------> └──◻ AAFClassID_SourceClip /* Only to print trace */ __td.lv++; - DUMP_OBJ (aafi, refMob, &__td); + DUMP_OBJ (aafi, targetMob, &__td); // memcpy( &ctxBackup, &(aafi->ctx), sizeof(struct aafiContext) ); // // RESET_CONTEXT( aafi->ctx ); - parse_MobSlot (aafi, refMobSlot, &__td); + parse_MobSlot (aafi, targetMobSlot, &__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)); + DUMP_OBJ_ERROR (aafi, SourceClip, &__td, "Targeted mob isn't MasterMob : %ls", aaft_ClassIDToText (aafi->aafd, targetMob->Class->ID)); // parse_CompositionMob( ) return -1; } @@ -2681,38 +2701,28 @@ POS NOT UPDATED HERE ------------------> └──◻ AAFClassID_SourceClip 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 ); - // } + uint32_t* essenceChannelNum = aaf_get_propertyValue (ParentMobSlot, PID_MobSlot_PhysicalTrackNumber, &AAFTypeID_UInt32); 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; } + /* + * Check if this Essence has already been retrieved + */ + 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); + __td.eob = 1; + DUMP_OBJ_WARNING (aafi, SourceClip, &__td, "Essence already parsed: Linking with %ls", audioEssence->file_name); + + aafi->ctx.current_clip->essencePointerList = aafi_newAudioEssencePointer (aafi, &aafi->ctx.current_clip->essencePointerList, audioEssence, essenceChannelNum); return 0; } } @@ -2721,20 +2731,17 @@ POS NOT UPDATED HERE ------------------> └──◻ AAFClassID_SourceClip 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); + 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. + * 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; @@ -2745,6 +2752,8 @@ POS NOT UPDATED HERE ------------------> └──◻ AAFClassID_SourceClip // warning( "Could not retrieve SourceReference::SourceID, retrieving from parent Mob." ); // } + aafi->ctx.current_essence = audioEssence; + DUMP_OBJ (aafi, SourceClip, &__td); aafObject* SourceMob = aaf_get_MobByID (aafi->aafd->Mobs, audioEssence->sourceMobID); @@ -2776,10 +2785,7 @@ POS NOT UPDATED HERE ------------------> └──◻ AAFClassID_SourceClip audioEssence->unique_file_name = build_unique_audiofilename (aafi, audioEssence); - aafi->ctx.current_clip->Essence = audioEssence; - - // aafi_trace_obj( aafi, SourceClip, ANSI_COLOR_MAGENTA ); - + aafi->ctx.current_clip->essencePointerList = aafi_newAudioEssencePointer (aafi, &aafi->ctx.current_clip->essencePointerList, audioEssence, essenceChannelNum); } else if (aafUIDCmp (DataDefinition, &AAFDataDef_Picture) || aafUIDCmp (DataDefinition, &AAFDataDef_LegacyPicture)) { /* @@ -3052,6 +3058,9 @@ parse_Parameter (AAF_Iface* aafi, aafObject* Parameter, td* __ptd) return parse_ConstantValue (aafi, Parameter, &__td); } else if (aafUIDCmp (Parameter->Class->ID, &AAFClassID_VaryingValue)) { return parse_VaryingValue (aafi, Parameter, &__td); + } else { + __td_set (__td, __ptd, 1); + DUMP_OBJ_ERROR (aafi, Parameter, &__td, "Parameter is neither of class Constant nor Varying : %ls", aaft_ClassIDToText (aafi->aafd, Parameter->Class->ID)); } return -1; @@ -3298,8 +3307,22 @@ parse_VaryingValue (AAF_Iface* aafi, aafObject* VaryingValue, td* __ptd) (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) + * Skipping any 1:1 gain allows not to miss any other actual gain (eg. Resolve 18.5.AAF) + * + │ 02412││ ├──◻ AAFClassID_OperationGroup (OpIdent: AAFOperationDef_MonoAudioGain; Length: 284630) + err │ 03839││ │ ├──◻ AAFClassID_VaryingValue : : Value is continuous 1:1 (0db), skipping it. + │ ││ │ │ + │ 02412││ │ └──◻ AAFClassID_OperationGroup (OpIdent: AAFOperationDef_MonoAudioGain; Length: 284630) + │ 03660││ │ ├──◻ AAFClassID_ConstantValue (Value: 6023/536870912 -99.0 dB) + │ 02983││ │ └──◻ AAFClassID_SourceClip (Length: 284630) + │ 02988││ │ └──◻ AAFClassID_MasterMob (UsageCode: n/a) : speech-sample.mp3 - -100db + │ 04553││ │ └──◻ AAFClassID_TimelineMobSlot [slot:1 track:1] (DataDef : AAFDataDef_Sound) + │ 03193││ │ └──◻ AAFClassID_SourceClip (Length: 284630) + │ 04297││ │ └──◻ AAFClassID_SourceMob (UsageCode: n/a) : speech-sample.mp3 - -100db + │ 01342││ │ └──◻ AAFClassID_PCMDescriptor + │ 01529││ │ └──◻ AAFClassID_NetworkLocator : file:///C:/Users/user/Desktop/libAAF/test/res/speech-sample.mp3 */ + DUMP_OBJ_ERROR (aafi, VaryingValue, &__td, "Value is continuous 1:1 (0db), skipping it"); aafi_freeAudioGain (Gain); return -1; } @@ -3346,6 +3369,7 @@ parse_VaryingValue (AAF_Iface* aafi, aafObject* VaryingValue, td* __ptd) } else { aafi->ctx.current_clip_gain = Gain; aafi->ctx.clips_using_gain = 0; + DUMP_OBJ (aafi, VaryingValue, &__td); } } else { if (aafi->ctx.current_clip_automation) { @@ -3355,6 +3379,7 @@ parse_VaryingValue (AAF_Iface* aafi, aafObject* VaryingValue, td* __ptd) } else { aafi->ctx.current_clip_automation = Gain; aafi->ctx.clips_using_automation = 0; + DUMP_OBJ (aafi, VaryingValue, &__td); } } } @@ -3513,12 +3538,13 @@ parse_Mob (AAF_Iface* aafi, aafObject* Mob) __td.ll[0] = 0; // aafi->ctx.trace_leveloop = __td.ll; // keep track of __td.ll for free + int rc = 0; + 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; + goto err; } if (aafUIDCmp (Mob->Class->ID, &AAFClassID_CompositionMob)) { @@ -3526,7 +3552,7 @@ parse_Mob (AAF_Iface* aafi, aafObject* Mob) if (aafUIDCmp (UsageCode, &AAFUsage_AdjustedClip)) { DUMP_OBJ_ERROR (aafi, Mob, &__td, "Skipping AAFUsage_AdjustedClip"); - return -1; + goto err; } parse_CompositionMob (aafi, Mob, &__td); @@ -3550,9 +3576,15 @@ parse_Mob (AAF_Iface* aafi, aafObject* Mob) parse_MobSlot (aafi, MobSlot, &__td); } + goto end; + +err: + rc = -1; + +end: free (__td.ll); - return 0; + return rc; } static int @@ -3723,8 +3755,6 @@ parse_MobSlot (AAF_Iface* aafi, aafObject* MobSlot, td* __ptd) return -1; } - aafPosition_t session_end = 0; - if (aafUIDCmp (MobSlot->Class->ID, &AAFClassID_TimelineMobSlot)) { /* * Each TimelineMobSlot represents a track, either audio or video. @@ -3831,14 +3861,6 @@ parse_MobSlot (AAF_Iface* aafi, aafObject* MobSlot, td* __ptd) // 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); @@ -3955,29 +3977,37 @@ parse_MobSlot (AAF_Iface* aafi, aafObject* MobSlot, td* __ptd) 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; + /* this __td is only here for debug/error, normal trace is printed from parse_Mob() */ + static td __td; + __td.fn = __LINE__; + __td.pfn = 0; + __td.lv = 0; + __td.ll = calloc (1024, sizeof (int)); + __td.ll[0] = 0; - aaf_foreach_ObjectInSet (&Mob, aafi->aafd->Mobs, &AAFClassID_CompositionMob) + int compositionMobParsed = 0; + aafObject* Mob = NULL; + + aaf_foreach_ObjectInSet (&Mob, aafi->aafd->Mobs, NULL) { + if (aafUIDCmp (Mob->Class->ID, &AAFClassID_MasterMob) || + aafUIDCmp (Mob->Class->ID, &AAFClassID_SourceMob)) { + // DUMP_OBJ_WARNING( aafi, Mob, &__td, "PRINTS FOR DEBUG ONLY: Will be parsed later" ); + continue; + } + + if (!aafUIDCmp (Mob->Class->ID, &AAFClassID_CompositionMob)) { + /* there should not be anything other than MasterMob, SourceMob or CompositionMob */ + DUMP_OBJ_NO_SUPPORT (aafi, Mob, &__td); + continue; + } + aafUID_t* UsageCode = aaf_get_propertyValue (Mob, PID_Mob_UsageCode, &AAFTypeID_UsageType); if (aafUIDCmp (aafi->aafd->Header.OperationalPattern, &AAFOPDef_EditProtocol) && @@ -3986,146 +4016,27 @@ aafi_retrieveData (AAF_Iface* aafi) * 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. */ + continue; + } - // 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 ); - // } - + if (compositionMobParsed) { + DUMP_OBJ_ERROR (aafi, Mob, &__td, "Multiple top level CompositionMob not supported yet"); continue; } RESET_CONTEXT (aafi->ctx); parse_Mob (aafi, Mob); + + if (aafUIDCmp (UsageCode, &AAFUsage_TopLevel)) { + compositionMobParsed = 1; + } } - // 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 ) ); - // } - // - // } - // } + free (__td.ll); if (aafi->Timecode == NULL) { + /* TODO, shouldn't we leave aafi->Timecode as NULL ? */ warning ("No timecode found in file. Setting to 00:00:00:00 @ 25fps"); aafiTimecode* tc = calloc (sizeof (aafiTimecode), sizeof (unsigned char)); @@ -4143,37 +4054,55 @@ aafi_retrieveData (AAF_Iface* aafi) 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) { - if (audioEssence->type != AAFI_ESSENCE_TYPE_PCM) { - /* TODO: rename (not only summary, can be external file too) */ - aafi_parse_audio_summary (aafi, audioEssence); + if (!audioEssence->is_embedded) { + audioEssence->usable_file_path = aafi_locate_external_essence_file (aafi, audioEssence->original_file_path, aafi->ctx.options.media_location); + + if (audioEssence->usable_file_path == NULL) { + error ("Could not locate external audio essence file '%ls'", audioEssence->original_file_path); + } } - /* 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 (audioEssence->summary || audioEssence->usable_file_path) { + aafi_parse_audio_essence (aafi, audioEssence); + } + } + + /* + * Define AAF samplerate and samplesize with the most used values accross all audio essences. + */ + + uint32_t maxOccurence = 0; + + foreachEssence (audioEssence, aafi->Audio->Essences) + { + uint32_t count = 1; + aafiAudioEssence* ae = NULL; + + if (audioEssence->samplerate == aafi->Audio->samplerate && + audioEssence->samplesize == aafi->Audio->samplesize) { + continue; } - 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 ); + foreachEssence (ae, audioEssence->next) + { + if (audioEssence->samplerate == ae->samplerate && audioEssence->samplesize == ae->samplesize) { + count++; + } + } + + debug ("Essence count @ %u Hz / %u bits : %i", audioEssence->samplerate, audioEssence->samplesize, count); + + if (count > maxOccurence) { + maxOccurence = count; + aafi->Audio->samplesize = audioEssence->samplesize; + aafi->Audio->samplerate = audioEssence->samplerate; + aafi->Audio->samplerateRational = audioEssence->samplerateRational; } } @@ -4185,57 +4114,42 @@ aafi_retrieveData (AAF_Iface* aafi) continue; } - char* externalFilePath = aafi_locate_external_essence_file (aafi, videoEssence->original_file_path, aafi->ctx.options.media_location); - - if (externalFilePath == NULL) { - error ("Could not locate external audio essence file '%ls'", videoEssence->original_file_path); - continue; - } - - videoEssence->usable_file_path = malloc ((strlen (externalFilePath) + 1) * sizeof (wchar_t)); + videoEssence->usable_file_path = aafi_locate_external_essence_file (aafi, videoEssence->original_file_path, aafi->ctx.options.media_location); if (videoEssence->usable_file_path == NULL) { - error ("Could not allocate memory : %s", strerror (errno)); - free (externalFilePath); + error ("Could not locate external video essence file '%ls'", videoEssence->original_file_path); continue; } - - int rc = swprintf (videoEssence->usable_file_path, strlen (externalFilePath) + 1, L"%" WPRIs, externalFilePath); - - if (rc < 0) { - error ("Failed setting usable_file_path"); - free (externalFilePath); - free (videoEssence->usable_file_path); - videoEssence->usable_file_path = NULL; - continue; - } - - free (externalFilePath); } + aafPosition_t trackEnd = 0; 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 (aafi->compositionLength_editRate) { + trackEnd = laaf_util_converUnit (audioTrack->current_pos, audioTrack->edit_rate, aafi->compositionLength_editRate); + } else { + trackEnd = audioTrack->current_pos; + } - 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; + if (trackEnd > aafi->compositionLength) { + debug ("Setting compositionLength with audio track \"%ls\" (%u) : %" PRIi64, audioTrack->name, audioTrack->number, audioTrack->current_pos); + aafi->compositionLength = audioTrack->current_pos; + aafi->compositionLength_editRate = audioTrack->edit_rate; + } + + aafiTimelineItem* audioItem = NULL; + aafiAudioClip* audioClip = NULL; + + foreach_Item (audioItem, audioTrack) + { + if (audioItem->type == AAFI_TRANS) { + continue; + } + + audioClip = (aafiAudioClip*)audioItem->data; + audioClip->channels = aafi_getAudioEssencePointerChannelCount (audioClip->essencePointerList); } } @@ -4243,26 +4157,21 @@ aafi_retrieveData (AAF_Iface* aafi) 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->compositionLength_editRate) { + trackEnd = laaf_util_converUnit (videoTrack->current_pos, videoTrack->edit_rate, aafi->compositionLength_editRate); + } else { + trackEnd = videoTrack->current_pos; + } + + if (trackEnd > aafi->compositionLength) { + debug ("Setting compositionLength with video track \"%ls\" (%u) : %" PRIi64, videoTrack->name, videoTrack->number, videoTrack->current_pos); + aafi->compositionLength = videoTrack->current_pos; + aafi->compositionLength_editRate = videoTrack->edit_rate; } } - 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; + aafi->compositionStart = aafi->Timecode->start; + aafi->compositionStart_editRate = aafi->Timecode->edit_rate; if (protools_AAF (aafi)) { protools_post_processing (aafi); diff --git a/libs/aaf/AAFIface.c b/libs/aaf/AAFIface.c index 84c0b7d8b3..34d53b9e1c 100644 --- a/libs/aaf/AAFIface.c +++ b/libs/aaf/AAFIface.c @@ -75,47 +75,37 @@ aafi_alloc (AAF_Data* aafd) aafi->dbg = laaf_new_debug (); if (aafi->dbg == NULL) { - return NULL; + goto err; } aafi->Audio = calloc (sizeof (aafiAudio), sizeof (unsigned char)); if (aafi->Audio == NULL) { - return NULL; + goto err; } - aafi->Audio->Essences = NULL; - aafi->Audio->samplerate = 0; - aafi->Audio->samplesize = 0; - aafi->Audio->Tracks = NULL; - aafi->Audio->track_count = 0; - aafi->Audio->length = 0; - aafi->Video = calloc (sizeof (aafiVideo), sizeof (unsigned char)); if (aafi->Video == NULL) { - return NULL; + goto err; } - aafi->Video->Essences = NULL; - aafi->Video->Tracks = NULL; - aafi->Video->length = 0; - if (aafd != NULL) { aafi->aafd = aafd; } else { aafi->aafd = aaf_alloc (aafi->dbg); + + if (aafi->aafd == NULL) { + goto err; + } } - aafi->Markers = NULL; - - aafi->compositionName = NULL; - - aafi->ctx.is_inside_derivation_chain = 0; - aafi->ctx.options.forbid_nonlatin_filenames = 0; - aafi->ctx.options.trace = 0; - return aafi; + +err: + aafi_release (&aafi); + + return NULL; } void @@ -174,17 +164,16 @@ aafi_set_option_str (AAF_Iface* aafi, const char* optname, const char* val) aafi->ctx.options.dump_class_aaf_properties = NULL; } - if (val == NULL) + if (val == NULL) { return 0; + } - aafi->ctx.options.dump_class_aaf_properties = malloc ((strlen (val) + 1) * sizeof (wchar_t)); + aafi->ctx.options.dump_class_aaf_properties = laaf_util_str2wstr (val); if (aafi->ctx.options.dump_class_aaf_properties == NULL) { return -1; } - swprintf (aafi->ctx.options.dump_class_aaf_properties, strlen (val) + 1, L"%" WPRIs, val); - return 0; } else if (strcmp (optname, "dump_class_raw_properties") == 0) { if (aafi->ctx.options.dump_class_raw_properties) { @@ -192,17 +181,16 @@ aafi_set_option_str (AAF_Iface* aafi, const char* optname, const char* val) aafi->ctx.options.dump_class_raw_properties = NULL; } - if (val == NULL) + if (val == NULL) { return 0; + } - aafi->ctx.options.dump_class_raw_properties = malloc ((strlen (val) + 1) * sizeof (wchar_t)); + aafi->ctx.options.dump_class_raw_properties = laaf_util_str2wstr (val); if (aafi->ctx.options.dump_class_raw_properties == NULL) { return -1; } - swprintf (aafi->ctx.options.dump_class_raw_properties, strlen (val) + 1, L"%" WPRIs, val); - return 0; } @@ -212,8 +200,9 @@ aafi_set_option_str (AAF_Iface* aafi, const char* optname, const char* val) void aafi_release (AAF_Iface** aafi) { - if (*aafi == NULL) + if (*aafi == NULL) { return; + } aaf_release (&(*aafi)->aafd); @@ -541,6 +530,20 @@ aafi_freeAudioClip (aafiAudioClip* audioClip) if (audioClip->automation != NULL) { aafi_freeAudioGain (audioClip->automation); } + + aafi_freeAudioEssencePointer (audioClip->essencePointerList); +} + +void +aafi_freeAudioEssencePointer (aafiAudioEssencePointer* essencePointer) +{ + aafiAudioEssencePointer* next = NULL; + + while (essencePointer) { + next = essencePointer->next; + free (essencePointer); + essencePointer = next; + } } void @@ -649,12 +652,9 @@ aafi_newAudioTrack (AAF_Iface* aafi) return NULL; } - track->Audio = aafi->Audio; - track->format = AAFI_TRACK_FORMAT_NOT_SET; - track->pan = NULL; - track->gain = NULL; - track->current_pos = 0; - track->next = NULL; + track->Audio = aafi->Audio; + track->format = AAFI_TRACK_FORMAT_NOT_SET; + track->next = NULL; /* Add to track list */ @@ -718,9 +718,8 @@ aafi_newVideoTrack (AAF_Iface* aafi) return NULL; } - track->Video = aafi->Video; - track->current_pos = 0; - track->next = NULL; + track->Video = aafi->Video; + track->next = NULL; /* Add to track list */ @@ -776,20 +775,52 @@ aafi_newAudioEssence (AAF_Iface* aafi) return NULL; } - audioEssence->next = aafi->Audio->Essences; + audioEssence->samplerateRational = malloc (sizeof (aafRational_t)); - audioEssence->original_file_path = NULL; - audioEssence->usable_file_path = NULL; - audioEssence->file_name = NULL; - audioEssence->unique_file_name = NULL; - audioEssence->clip_count = 0; - audioEssence->user = NULL; + if (audioEssence->samplerateRational == NULL) { + return NULL; + } + + audioEssence->samplerateRational->numerator = 1; + audioEssence->samplerateRational->denominator = 1; + + audioEssence->next = aafi->Audio->Essences; aafi->Audio->Essences = audioEssence; return audioEssence; } +aafiAudioEssencePointer* +aafi_newAudioEssencePointer (AAF_Iface* aafi, aafiAudioEssencePointer** list, aafiAudioEssence* audioEssence, uint32_t* essenceChannelNum) +{ + aafiAudioEssencePointer* essencePointer = calloc (sizeof (aafiAudioEssencePointer), sizeof (char)); + + if (essencePointer == NULL) { + error ("%s.", strerror (errno)); + return NULL; + } + + essencePointer->aafi = aafi; + essencePointer->essence = audioEssence; + essencePointer->essenceChannel = (essenceChannelNum) ? *essenceChannelNum : 0; + + if (*list) { + aafiAudioEssencePointer* last = *list; + while (last->next != NULL) { + last = last->next; + } + last->next = essencePointer; + } else { + *list = essencePointer; + + essencePointer->aafiNext = aafi->Audio->essencePointerList; + aafi->Audio->essencePointerList = essencePointer; + } + + return *list; +} + void aafi_freeAudioEssences (aafiAudioEssence** audioEssence) { @@ -818,6 +849,10 @@ aafi_freeAudioEssences (aafiAudioEssence** audioEssence) free ((*audioEssence)->unique_file_name); } + if ((*audioEssence)->samplerateRational != NULL) { + free ((*audioEssence)->samplerateRational); + } + free (*audioEssence); } @@ -836,11 +871,6 @@ aafi_newVideoEssence (AAF_Iface* aafi) videoEssence->next = aafi->Video->Essences; - videoEssence->original_file_path = NULL; - videoEssence->usable_file_path = NULL; - videoEssence->file_name = NULL; - videoEssence->unique_file_name = NULL; - aafi->Video->Essences = videoEssence; return videoEssence; @@ -880,6 +910,35 @@ aafi_freeVideoEssences (aafiVideoEssence** videoEssence) *videoEssence = NULL; } +int +aafi_getAudioEssencePointerChannelCount (aafiAudioEssencePointer* essencePointerList) +{ + /* + * If essencePointerList holds a single multichannel essence file and if + * essencePointer->essenceChannel is set, then clip is mono and audio comes + * from essencePointer->essenceChannel of essencePointer->essence file. + * + * If essencePointerList holds a single multichannel essence file and if + * essencePointer->essenceChannel is null, then clip is multichannel and + * clip channel count equals essence->channels. + * + * If essencePointerList holds multiple pointers to multiple essence files, + * then each file should be mono and describe a clip channel. Thus, clip + * channel count equals pointers count. + */ + + int essencePointerCount = 0; + aafiAudioEssencePointer* essencePointer = NULL; + + AAFI_foreachAudioEssencePointer (essencePointer, essencePointerList) + { + essencePointerCount++; + } + + return (essencePointerCount > 1) ? essencePointerCount : (essencePointerList->essenceChannel) ? 1 + : essencePointerList->essence->channels; +} + /** * @} */ diff --git a/libs/aaf/ProTools.c b/libs/aaf/ProTools.c index 6eb5b4b396..df1227c538 100644 --- a/libs/aaf/ProTools.c +++ b/libs/aaf/ProTools.c @@ -177,7 +177,7 @@ replace_clipfade_with_fade (AAF_Iface* aafi, aafiTimelineItem* Item) if (Item->next->type == AAFI_AUDIO_CLIP) { nextClip = (aafiAudioClip*)Item->next->data; - if (is_sample_accurate_edit (nextClip->Essence->file_name)) { + if (is_sample_accurate_edit (nextClip->essencePointerList->essence->file_name)) { if (Item->next->next != NULL) { nextClip = (aafiAudioClip*)Item->next->next->data; @@ -281,7 +281,7 @@ protools_post_processing (AAF_Iface* aafi /*, enum protools_options flags*/) aafiAudioClip* audioClip = (aafiAudioClip*)audioItem->data; - wchar_t* clipName = audioClip->Essence->file_name; + wchar_t* clipName = audioClip->essencePointerList->essence->file_name; if ((aafi->ctx.options.protools & PROTOOLS_REPLACE_CLIP_FADES) && is_rendered_fade (clipName)) { replace_clipfade_with_fade (aafi, audioItem); diff --git a/libs/aaf/RIFFParser.c b/libs/aaf/RIFFParser.c index 3ecc06d331..40abff154c 100644 --- a/libs/aaf/RIFFParser.c +++ b/libs/aaf/RIFFParser.c @@ -124,7 +124,7 @@ riff_parseAudioFile (struct RIFFAudioFile* RIFFAudioFile, enum RIFF_PARSER_FLAGS size_t bytesRead = readerCallback ((unsigned char*)&riff, 0, sizeof (riff), user1, user2, user3); if (bytesRead < sizeof (riff)) { - error ("Could not read file"); + error ("Could not read file header"); return -1; } @@ -152,8 +152,6 @@ riff_parseAudioFile (struct RIFFAudioFile* RIFFAudioFile, enum RIFF_PARSER_FLAGS return -1; } - // debug( "%.4s %.4s (%u bytes)", riff.ckid, riff.format, riff.cksz ); - size_t filesize = riff.cksz + sizeof (chunk); size_t pos = sizeof (struct riffHeaderChunk); @@ -161,7 +159,7 @@ riff_parseAudioFile (struct RIFFAudioFile* RIFFAudioFile, enum RIFF_PARSER_FLAGS bytesRead = readerCallback ((unsigned char*)&chunk, pos, sizeof (chunk), user1, user2, user3); if (bytesRead < sizeof (chunk)) { - error ("Could not read chunk @ %" PRIu64 " (%" PRIu64 " bytes returned)", pos, bytesRead); + error ("Could not read chunk \"%.4s\" @ %" PRIu64 " (%" PRIu64 " bytes returned)", chunk.ckid, pos, bytesRead); break; } @@ -169,7 +167,7 @@ riff_parseAudioFile (struct RIFFAudioFile* RIFFAudioFile, enum RIFF_PARSER_FLAGS chunk.cksz = BE2LE32 (chunk.cksz); } - // debug( "Got chunk : %.4s (%u bytes)", chunk.ckid, chunk.cksz ); + debug ("Got chunk \"%.4s\" (%u bytes) @ %" PRIu64 " (%" PRIu64 " bytes returned)", chunk.ckid, chunk.cksz, pos, bytesRead); if (!be) { /* WAVE */ @@ -181,6 +179,11 @@ riff_parseAudioFile (struct RIFFAudioFile* RIFFAudioFile, enum RIFF_PARSER_FLAGS bytesRead = readerCallback ((unsigned char*)&wavFmtChunk, pos, sizeof (wavFmtChunk), user1, user2, user3); + if (bytesRead < sizeof (wavFmtChunk)) { + error ("Could not read chunk \"%.4s\" content @ %" PRIu64 " (%" PRIu64 " bytes returned)", chunk.ckid, pos, bytesRead); + break; + } + RIFFAudioFile->channels = wavFmtChunk.channels; RIFFAudioFile->sampleSize = wavFmtChunk.bits_per_sample; RIFFAudioFile->sampleRate = wavFmtChunk.samples_per_sec; @@ -193,7 +196,11 @@ riff_parseAudioFile (struct RIFFAudioFile* RIFFAudioFile, enum RIFF_PARSER_FLAGS chunk.ckid[2] == 't' && chunk.ckid[3] == 'a') { if (RIFFAudioFile->channels > 0 && RIFFAudioFile->sampleSize > 0) { - RIFFAudioFile->duration = chunk.cksz / RIFFAudioFile->channels / (RIFFAudioFile->sampleSize / 8); + RIFFAudioFile->sampleCount = chunk.cksz / RIFFAudioFile->channels / (RIFFAudioFile->sampleSize / 8); + } + + if (flags & RIFF_PARSE_AAF_SUMMARY) { + return 0; } } } else { /* AIFF */ @@ -206,29 +213,40 @@ riff_parseAudioFile (struct RIFFAudioFile* RIFFAudioFile, enum RIFF_PARSER_FLAGS bytesRead = readerCallback ((unsigned char*)&aiffCOMMChunk, pos, sizeof (aiffCOMMChunk), user1, user2, user3); - RIFFAudioFile->channels = BE2LE16 (aiffCOMMChunk.numChannels); - RIFFAudioFile->sampleSize = BE2LE16 (aiffCOMMChunk.sampleSize); - RIFFAudioFile->sampleRate = beExtended2leUint32 (aiffCOMMChunk.sampleRate); - RIFFAudioFile->duration = BE2LE32 (aiffCOMMChunk.numSampleFrames); + if (bytesRead < sizeof (aiffCOMMChunk)) { + error ("Could not read chunk \"%.4s\" content @ %" PRIu64 " (%" PRIu64 " bytes returned)", chunk.ckid, pos, bytesRead); + break; + } + + RIFFAudioFile->channels = BE2LE16 (aiffCOMMChunk.numChannels); + RIFFAudioFile->sampleSize = BE2LE16 (aiffCOMMChunk.sampleSize); + RIFFAudioFile->sampleRate = beExtended2leUint32 (aiffCOMMChunk.sampleRate); + RIFFAudioFile->sampleCount = BE2LE32 (aiffCOMMChunk.numSampleFrames); if (flags & RIFF_PARSE_ONLY_HEADER) { return 0; } - } - /* - * We don't care about AIFF "SSND" chunk because we already know duration - * from "COMM". Could we double check validity of duration by checking - * "SSND" chunk size, like we do with WAV "DATA" chunk ? is it possible - * with AAF audio file summary ? - */ + } else if (chunk.ckid[0] == 'S' && + chunk.ckid[1] == 'S' && + chunk.ckid[2] == 'N' && + chunk.ckid[3] == 'D') { + /* + * Samplecount should be already set with numSampleFrames in COMM chunk. + * However in AAF (AIFCDescriptor::Summary), numSampleFrames is often null, + * so we must extract samplecount out of SSND chunk, like we do with wav DATA chunk. + */ + uint64_t sampleCount = chunk.cksz / RIFFAudioFile->channels / (RIFFAudioFile->sampleSize / 8); - // else - // if ( chunk.ckid[0] == 'S' && - // chunk.ckid[1] == 'S' && - // chunk.ckid[2] == 'N' && - // chunk.ckid[3] == 'D' ) - // { - // } + if (RIFFAudioFile->sampleCount > 0 && RIFFAudioFile->sampleCount != sampleCount) { + debug ("Sample count retrieved from COMM chunk (%" PRIu64 ") does not match SSND chunk (%" PRIu64 ")", RIFFAudioFile->sampleCount, sampleCount); + } + + RIFFAudioFile->sampleCount = sampleCount; + + if (flags & RIFF_PARSE_AAF_SUMMARY) { + return 0; + } + } } pos += chunk.cksz + sizeof (chunk); diff --git a/libs/aaf/aaf/AAFIAudioFiles.h b/libs/aaf/aaf/AAFIAudioFiles.h index c27007dc78..9c3ffe2925 100644 --- a/libs/aaf/aaf/AAFIAudioFiles.h +++ b/libs/aaf/aaf/AAFIAudioFiles.h @@ -25,13 +25,13 @@ #include "aaf/AAFIface.h" -char* +wchar_t* aafi_locate_external_essence_file (AAF_Iface* aafi, const wchar_t* original_file_path, const char* search_location); int aafi_extract_audio_essence (AAF_Iface* aafi, aafiAudioEssence* audioEssence, const char* outfilepath, const wchar_t* forcedFileName); int -aafi_parse_audio_summary (AAF_Iface* aafi, aafiAudioEssence* audioEssence); +aafi_parse_audio_essence (AAF_Iface* aafi, aafiAudioEssence* audioEssence); #endif // !__AAFIAudioFiles_h__ diff --git a/libs/aaf/aaf/AAFIface.h b/libs/aaf/aaf/AAFIface.h index ffc0ad3c0d..993f926c26 100644 --- a/libs/aaf/aaf/AAFIface.h +++ b/libs/aaf/aaf/AAFIface.h @@ -42,6 +42,7 @@ enum aafiEssenceType { AAFI_ESSENCE_TYPE_WAVE = 0x02, AAFI_ESSENCE_TYPE_AIFC = 0x03, AAFI_ESSENCE_TYPE_BWAV = 0x04, + AAFI_ESSENCE_TYPE_UNK = 0xff, /* non-pcm */ }; /** @@ -234,15 +235,20 @@ typedef struct aafiAudioGain { typedef struct aafiAudioGain aafiAudioPan; typedef struct aafiAudioEssence { - wchar_t* original_file_path; // NetworkLocator::URLString the original URI hold in AAF + wchar_t* original_file_path; // NetworkLocator::URLString the original external essence URI holded in AAF wchar_t* usable_file_path; // Holds a real usable file path, once an embedded essence has been extracted, or once en external essence has been found. wchar_t* file_name; // MasterMob::Name the original file name. Might be NULL if MasterMob has no name. One should always use unique_file_name which is guaranted to be set. wchar_t* unique_file_name; // unique name generated from file_name. Sometimes, multiple files share the same names so this unique name should be used on export. - uint16_t clip_count; // number of clips with this essence + uint16_t clip_count; // number of clips using this essence - /* total samples for 1 channel (no matter channel count). (duration / sampleRate) = duration in seconds */ - uint64_t length; // Length of Essence Data + /* + * total samples for 1 channel (no matter channel count). + * Might be retrieved from FileDescriptor::Length property, + * or from WAV/AIFF summary or file : + * (data chunk size / channels / samplesize / 8) + */ + uint64_t length; cfbNode* node; // The node holding the audio stream if embedded @@ -255,14 +261,18 @@ typedef struct aafiAudioEssence { enum aafiEssenceType type; // depends on PCMDescriptor WAVEDescriptor AIFCDescriptor + /* + * is only set if FileSourceMob contains EssenceData + */ uint8_t is_embedded; aafProperty* summary; // WAVEDescriptor AIFCDescriptor // uint32_t format; - uint32_t samplerate; - int16_t samplesize; - int16_t channels; + uint32_t samplerate; + aafRational_t* samplerateRational; // eg. { 48000, 1 } + int16_t samplesize; + int16_t channels; aafRational_t* mobSlotEditRate; @@ -277,10 +287,22 @@ typedef struct aafiAudioEssence { void* user; // TODO peakEnveloppe - struct aafiAudioEssence* next; - + struct aafiAudioEssence* next; // aafi->Audio->essences } aafiAudioEssence; +typedef struct aafiAudioEssencePointer { + aafiAudioEssence* essence; // single essence, not list ! + int essenceChannel; // channel selector inside multichannel essence. If zero, then all essence channels must be used. + + void* user; + + struct aafiAudioEssencePointer* next; // audioClip->essenceGroup + struct aafiAudioEssencePointer* aafiNext; // aafi->Audio->essenceGroup + + struct AAF_Iface* aafi; + +} aafiAudioEssencePointer; + typedef struct aafiVideoEssence { wchar_t* original_file_path; // NetworkLocator::URLString should point to original essence file if external (and in some cases, points to the AAF itself if internal..) wchar_t* usable_file_path; // TODO, not that used.. to be tweaked. ---- Holds the file path, once the essence has been exported, copied or linked. @@ -318,8 +340,8 @@ struct aafiVideoTrack; typedef struct aafiAudioClip { struct aafiAudioTrack* track; - aafiAudioEssence* Essence; - + int channels; // channel count of clip (might be different of essence->channels) + aafiAudioEssencePointer* essencePointerList; /* * Some editors (like Resolve) support automation attached to a clip AND a fixed value clip gain */ @@ -338,22 +360,26 @@ typedef struct aafiAudioClip { * Start position in source file, set from SourceClip::StartTime * * « Specifies the offset from the origin of the referenced Mob MobSlot in edit units - * determined by the SourceClip object’s context. + * determined by the SourceClip object’s context. » * - * A SourceClip’s StartTime and Length values are in edit units determined by the slot - * owning the SourceClip. + * « A SourceClip’s StartTime and Length values are in edit units determined by the slot + * owning the SourceClip. » - * Informative note: If the SourceClip references a MobSlot that specifies a different + * « Informative note: If the SourceClip references a MobSlot that specifies a different * edit rate than the MobSlot owning the SourceClip, the StartTime and Length are in * edit units of the slot owning the SourceClip, and not edit units of the referenced slot.» */ - aafPosition_t essence_offset; /* in edit unit, edit rate definition is aafiAudioTrack->edit_rate */ + /* + * set with CompoMob's SourceClip::StartTime. In the case of an OperationGroup(AudioChannelCombiner), + * There is one SourceClip per audio channel. So even though it's very unlikely, there could possibly + * be one essence_offset per channel. + * Value is in edit unit, edit rate definition is aafiAudioTrack->edit_rate + */ + aafPosition_t essence_offset; struct aafiTimelineItem* Item; // Corresponding timeline item, currently used in ardour to retrieve fades/x-fades - aafMobID_t* masterMobID; // MobID of the associated MasterMob (PID_SourceReference_SourceID) - } aafiAudioClip; typedef struct aafiVideoClip { @@ -403,12 +429,6 @@ typedef struct aafiTimecode { aafPosition_t start; - /** - * Timecode end in EditUnit. (session end) - */ - - aafPosition_t end; - /** * Frame per second. */ @@ -561,17 +581,17 @@ typedef struct aafiAudio { */ aafPosition_t start; - aafPosition_t length; - aafRational_t length_editRate; - int64_t samplerate; - int16_t samplesize; + int16_t samplesize; + int64_t samplerate; + aafRational_t* samplerateRational; // eg. { 48000, 1 } /** * Holds the Essence list. */ - aafiAudioEssence* Essences; + aafiAudioEssence* Essences; + aafiAudioEssencePointer* essencePointerList; /** * Holds the Track list. @@ -588,8 +608,6 @@ typedef struct aafiVideo { */ aafPosition_t start; - aafPosition_t length; - aafRational_t length_editRate; /** * Holds the Essence list. @@ -654,10 +672,10 @@ typedef struct aafiContext { aafiVideoClip* current_video_clip; int current_clip_is_muted; - int current_clip_is_combined; // Inside OperationGroup::AAFOperationDef_AudioChannelCombiner - int current_combined_clip_total_channel; - int current_combined_clip_channel_num; // current SourceClip represents channel num - + int current_clip_is_combined; // Inside OperationGroup::AAFOperationDef_AudioChannelCombiner + int current_combined_clip_total_channel; + int current_combined_clip_channel_num; // current SourceClip represents channel num + aafPosition_t current_combined_clip_forced_length; /* Transition */ aafiTransition* current_transition; @@ -713,11 +731,11 @@ typedef struct AAF_Iface { wchar_t* compositionName; - aafPosition_t compositionStart; // set from aafi->Timecode->start - aafRational_t compositionStart_editRate; + aafPosition_t compositionStart; // sets from aafi->Timecode->start + aafRational_t* compositionStart_editRate; - aafPosition_t compositionLength; - aafRational_t compositionLength_editRate; + aafPosition_t compositionLength; // sets from the longest audio or video track->current_pos + aafRational_t* compositionLength_editRate; /* might be NULL if file empty ! */ aafiUserComment* Comments; @@ -740,30 +758,18 @@ typedef struct AAF_Iface { item != NULL; \ item = item->next) +#define AAFI_foreachAudioEssencePointerInFile(essencePointer, aafi) \ + for (essencePointer = aafi->Audio->essencePointerList; essencePointer != NULL; essencePointer = essencePointer->aafiNext) + +#define AAFI_foreachAudioEssencePointer(essencePointer, essencePtrList) \ + for (essencePointer = essencePtrList; essencePointer != NULL; essencePointer = essencePointer->next) + #define foreachEssence(essence, essenceList) \ for (essence = essenceList; essence != NULL; essence = essence->next) #define foreachMarker(marker, aafi) \ for (marker = aafi->Markers; marker != NULL; marker = marker->next) -#define aeDuration_h(audioEssence) \ - ((audioEssence->samplerate == 0) ? 0 : ((uint16_t) (audioEssence->length / audioEssence->samplerate / (audioEssence->samplesize / 8)) / 3600)) - -#define aeDuration_m(audioEssence) \ - ((audioEssence->samplerate == 0) ? 0 : ((uint16_t) (audioEssence->length / audioEssence->samplerate / (audioEssence->samplesize / 8)) % 3600 / 60)) - -#define aeDuration_s(audioEssence) \ - ((audioEssence->samplerate == 0) ? 0 : ((uint16_t) (audioEssence->length / audioEssence->samplerate / (audioEssence->samplesize / 8)) % 3600 % 60)) - -#define aeDuration_ms(audioEssence) \ - ((audioEssence->samplerate == 0) ? 0 : ((uint16_t) (audioEssence->length / (audioEssence->samplerate / 1000) / (audioEssence->samplesize / 8)) % 3600000 % 60000 % 1000)) - -#define convertEditUnit(val, fromRate, toRate) \ - (int64_t) ((val) * (aafRationalToFloat ((toRate)) * (1 / aafRationalToFloat ((fromRate))))) - -#define eu2sample(samplerate, edit_rate, val) \ - (int64_t) (val * (samplerate * (1 / aafRationalToFloat ((*edit_rate))))) - void aafi_set_debug (AAF_Iface* aafi, verbosityLevel_e v, int ansicolor, FILE* fp, void (*callback) (struct dbg* dbg, void* ctxdata, int lib, int type, const char* srcfile, const char* srcfunc, int lineno, const char* msg, void* user), void* user); @@ -823,6 +829,9 @@ aafi_freeAudioPan (aafiAudioPan* pan); void aafi_freeAudioClip (aafiAudioClip* audioClip); +void +aafi_freeAudioEssencePointer (aafiAudioEssencePointer* audioEssenceGroupEntry); + void aafi_freeTimelineItem (aafiTimelineItem** item); @@ -841,6 +850,9 @@ aafi_freeTransition (aafiTransition* trans); aafiAudioEssence* aafi_newAudioEssence (AAF_Iface* aafi); +aafiAudioEssencePointer* +aafi_newAudioEssencePointer (AAF_Iface* aafi, aafiAudioEssencePointer** list, aafiAudioEssence* audioEssence, uint32_t* essenceChannelNum); + void aafi_freeAudioEssences (aafiAudioEssence** essences); @@ -850,6 +862,9 @@ aafi_newVideoEssence (AAF_Iface* aafi); void aafi_freeVideoEssences (aafiVideoEssence** videoEssence); +int +aafi_getAudioEssencePointerChannelCount (aafiAudioEssencePointer* essencePointerList); + /** * @} */ diff --git a/libs/aaf/aaf/RIFFParser.h b/libs/aaf/aaf/RIFFParser.h index 2cd57ecba6..c35b20acae 100644 --- a/libs/aaf/aaf/RIFFParser.h +++ b/libs/aaf/aaf/RIFFParser.h @@ -33,6 +33,7 @@ enum RIFF_PARSER_FLAGS { RIFF_PARSE_ONLY_HEADER = (1 << 0), + RIFF_PARSE_AAF_SUMMARY = (1 << 1), }; struct RIFFAudioFile { @@ -40,7 +41,7 @@ struct RIFFAudioFile { uint32_t sampleRate; uint16_t sampleSize; uint16_t channels; - uint64_t duration; /* total samples for 1 channel (no matter channel count). (duration / sampleRate) = duration in seconds */ + uint64_t sampleCount; /* total samples for 1 channel (no matter channel count). (sampleCount / sampleRate) = duration in seconds */ }; PACK (struct riffHeaderChunk { diff --git a/libs/aaf/aaf/utils.h b/libs/aaf/aaf/utils.h index 45aca28293..d68bdbed4f 100644 --- a/libs/aaf/aaf/utils.h +++ b/libs/aaf/aaf/utils.h @@ -21,6 +21,7 @@ #ifndef __utils_h__ #define __utils_h__ +#include "aaf/AAFTypes.h" #include #include @@ -35,9 +36,13 @@ extern "C" { * swprintf() specific string format identifiers * https://learn.microsoft.com/en-us/cpp/c-runtime-library/format-specification-syntax-printf-and-wprintf-functions?view=msvc-170#type */ -#define WPRIs L"S" // char* +#define WPRIs L"S" // char* +#ifdef XBUILD_WIN #define WPRIws L"s" // wchar_t* #else +#define WPRIws L"ls" // wchar_t* +#endif +#else #define DIR_SEP '/' #define DIR_SEP_STR "/" /* @@ -62,6 +67,15 @@ extern "C" { #define ANSI_COLOR_BOLD(dbg) (((dbg)->ansicolor) ? "\x1b[1m" : "") #define ANSI_COLOR_RESET(dbg) (((dbg)->ansicolor) ? "\x1b[0m" : "") +aafPosition_t +laaf_util_converUnit (aafPosition_t value, aafRational_t* valueEditRate, aafRational_t* destEditRate); + +char* +laaf_util_wstr2str (const wchar_t* wstr); + +wchar_t* +laaf_util_str2wstr (const char* str); + int laaf_util_wstr_contains_nonlatin (const wchar_t* str); diff --git a/libs/aaf/aaf/version.h b/libs/aaf/aaf/version.h index b506942c7b..157e2fc31b 100644 --- a/libs/aaf/aaf/version.h +++ b/libs/aaf/aaf/version.h @@ -1,2 +1,2 @@ #pragma once -#define LIBAAF_VERSION "v0.5-2-g4dfb754" +#define LIBAAF_VERSION "v0.6-45-g9171e40" diff --git a/libs/aaf/utils.c b/libs/aaf/utils.c index 0aa47af3c1..568029031f 100644 --- a/libs/aaf/utils.c +++ b/libs/aaf/utils.c @@ -29,6 +29,81 @@ #define BUILD_PATH_DEFAULT_BUF_SIZE 1024 +aafPosition_t +laaf_util_converUnit (aafPosition_t value, aafRational_t* valueEditRate, aafRational_t* destEditRate) +{ + if (!valueEditRate || !destEditRate) { + return value; + } + + if (valueEditRate->numerator == destEditRate->numerator && + valueEditRate->denominator == destEditRate->denominator) { + /* same rate, no conversion */ + return value; + } + + double valueEditRateFloat = ((valueEditRate->denominator == 0) ? 0.0 : ((float)valueEditRate->numerator / valueEditRate->denominator)); + double destEditRateFloat = ((destEditRate->denominator == 0) ? 0.0 : ((float)destEditRate->numerator / destEditRate->denominator)); + + if (valueEditRateFloat == 0) { + return 0; + } + + return value * (destEditRateFloat / valueEditRateFloat); +} + +char* +laaf_util_wstr2str (const wchar_t* wstr) +{ + if (wstr == NULL) { + return NULL; + } + + size_t strsz = wcslen (wstr) + 1; + char* str = malloc (strsz); + + if (str == NULL) { + // error( "Could not allocate memory : %s", strerror(errno) ); + return NULL; + } + + int rc = snprintf (str, strsz, "%ls", wstr); + + if (rc < 0 || (unsigned)rc >= strsz) { + // error( "Failed converting wide char str to byte char str%s", (reqlen < 0) ? " : encoding error" : "" ); + free (str); + return NULL; + } + + return str; +} + +wchar_t* +laaf_util_str2wstr (const char* str) +{ + if (str == NULL) { + return NULL; + } + + size_t strsz = strlen (str) + 1; + wchar_t* wstr = malloc (strsz * sizeof (wchar_t)); + + if (str == NULL) { + // error( "Could not allocate memory : %s", strerror(errno) ); + return NULL; + } + + int rc = swprintf (wstr, strsz, L"%" WPRIs, str); + + if (rc < 0 || (unsigned)rc >= strsz) { + // error( "Failed converting byte char str to wide char str%s", (reqlen < 0) ? " : encoding error" : "" ); + free (wstr); + return NULL; + } + + return wstr; +} + int laaf_util_wstr_contains_nonlatin (const wchar_t* str) { diff --git a/libs/aaf/wscript b/libs/aaf/wscript index ff9980efca..0ce2822d98 100644 --- a/libs/aaf/wscript +++ b/libs/aaf/wscript @@ -38,7 +38,7 @@ def options(opt): def configure(conf): if conf.is_defined('USE_EXTERNAL_LIBS'): - autowaf.check_pkg(conf, 'libaaf', uselib_store='LIBAAF', mandatory=True) + autowaf.check_pkg(conf, 'libaaf', uselib_store='LIBAAF', mandatory=True, atleast_version='0.6.0') def build(bld): if bld.is_defined('USE_EXTERNAL_LIBS'):