diff --git a/libs/aaf/AAFIEssenceFile.c b/libs/aaf/AAFIEssenceFile.c index baa29af6f6..59cbc4a3f5 100644 --- a/libs/aaf/AAFIEssenceFile.c +++ b/libs/aaf/AAFIEssenceFile.c @@ -117,7 +117,7 @@ aafi_build_unique_audio_essence_name (AAF_Iface* aafi, aafiAudioEssenceFile* aud } char* -aafi_locate_external_essence_file (AAF_Iface* aafi, const char* original_uri_filepath, const char* search_location) +aafi_locate_external_essence_file (AAF_Iface* aafi, const char* original_uri_filepath, const char* commonPathPart, const char* search_location) { /* * Absolute Uniform Resource Locator (URL) complying with RFC 1738 or relative @@ -131,17 +131,18 @@ aafi_locate_external_essence_file (AAF_Iface* aafi, const char* original_uri_fil char* local_filepath = NULL; char* aaf_path = NULL; - const char* foundpath = NULL; char* retpath = NULL; + const char* foundpath = NULL; - struct uri* uri = NULL; + struct uri* uri = NULL; + struct uri* commonPathPartURI = NULL; if (original_uri_filepath == NULL) { error ("Cant locate a NULL filepath"); goto err; } - debug ("Original URI : %s", original_uri_filepath); + debug ("Encoded file URI : %s", original_uri_filepath); uri = laaf_uri_parse (original_uri_filepath, URI_OPT_DECODE_ALL, aafi->log); @@ -155,54 +156,95 @@ aafi_locate_external_essence_file (AAF_Iface* aafi, const char* original_uri_fil goto err; } - debug ("Decoded URI's path : %s", uri->path); + debug ("Decoded file URI : %s", uri->path); - /* extract relative path to essence file : "/" */ - - const char* relativeEssencePath = NULL; - const char* essenceFileName = NULL; - - int sepcount = 0; - char* p = uri->path + strlen (uri->path); - - while (p > uri->path) { - if (*p == '/') { /* parsing URI, so will always be '/' as separator character */ - sepcount++; - if (sepcount == 1) { - essenceFileName = (p + 1); - } else if (sepcount == 2) { - relativeEssencePath = (p + 1); - break; - } - } - p--; - } - - if (!relativeEssencePath) { - error ("Could not retrieve relative file path out of URI : %s", uri->path); - goto err; - } - - if (!essenceFileName) { - error ("Could not retrieve file name out of URI : %s", uri->path); - goto err; - } - - debug ("Essence filename : %s", essenceFileName); + const char* local_path = NULL; if (search_location) { - /* - * "/" - */ + local_path = search_location; + } else { + /* extract local path to AAF file */ + aaf_path = laaf_util_c99strdup (aafi->aafd->cfbd->file); - local_filepath = laaf_util_build_path ("/", search_location, essenceFileName, NULL); - - if (!local_filepath) { - error ("Could not build search filepath"); + if (!aaf_path) { + error ("Could not duplicate AAF filepath : %s", aafi->aafd->cfbd->file); goto err; } - debug ("Search filepath : %s", local_filepath); + char* p = aaf_path + strlen (aaf_path); + + while (p > aaf_path) { + if (IS_DIR_SEP (*p)) { + *p = 0x00; + break; + } + p--; + } + + local_path = aaf_path; + } + + if (commonPathPart && *commonPathPart != 0x00) { + /* + * commonPathPart stores the first part of the filepath URI, which is common + * to all essence file. + * + * file:///localhost/C:/data/path/media/essence_a.wav + * file:///localhost/C:/data/path/media/essence_b.wav + * file:///localhost/C:/data/path/media/subdir/essence_c.wav + * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + */ + + debug ("Encoded commonPathPartURI : %s", commonPathPart); + + commonPathPartURI = laaf_uri_parse (commonPathPart, URI_OPT_DECODE_ALL, aafi->log); + + if (commonPathPartURI == NULL) { + error ("Could not parse commonPathPartURI"); + goto err; + } + + if (commonPathPartURI->path == NULL) { + error ("Could not retrieve out of commonPathPartURI"); + goto err; + } + + debug ("Decoded commonPathPartURI : %s", commonPathPartURI->path); + + /* + * Most of the time, the last common directory in path will be the top + * directory for essence media so we want to try it. + * + * file:///localhost/C:/data/path/media/essence_a.wav + * file:///localhost/C:/data/path/media/essence_b.wav + * file:///localhost/C:/data/path/media/subdir/essence_c.wav + * ^^^^^^^ + */ + + const char* relativeEssencePathWCommonDir = NULL; + + int sepcount = 0; + char* p = commonPathPartURI->path + strlen (commonPathPartURI->path); + + while (p > commonPathPartURI->path) { + if (*p == '/') { /* parsing URI, so will always be '/' as separator character */ + sepcount++; + if (sepcount == 2) { + relativeEssencePathWCommonDir = uri->path + (p - commonPathPartURI->path); + break; + } + } + p--; + } + + local_filepath = laaf_util_build_path (DIR_SEP_STR, local_path, relativeEssencePathWCommonDir, NULL); + + if (!local_filepath) { + error ("Could not build filepath"); + goto err; + } + + debug ("Trying relativeEssencePathWCommonDir : %s", local_filepath); if (laaf_util_file_exists (local_filepath) == 1) { foundpath = local_filepath; @@ -213,17 +255,19 @@ aafi_locate_external_essence_file (AAF_Iface* aafi, const char* original_uri_fil local_filepath = NULL; /* - * "//" + * Try without the last common directory. */ - local_filepath = laaf_util_build_path ("/", search_location, relativeEssencePath, NULL); + const char* relativeEssencePathWoCommonDir = uri->path + strlen (commonPathPartURI->path); + + local_filepath = laaf_util_build_path (DIR_SEP_STR, local_path, relativeEssencePathWoCommonDir, NULL); if (!local_filepath) { - error ("Could not build search filepath"); + error ("Could not build filepath"); goto err; } - debug ("Search filepath : %s", local_filepath); + debug ("Trying relativeEssencePathWoCommonDir : %s", local_filepath); if (laaf_util_file_exists (local_filepath) == 1) { foundpath = local_filepath; @@ -232,6 +276,42 @@ aafi_locate_external_essence_file (AAF_Iface* aafi, const char* original_uri_fil free (local_filepath); local_filepath = NULL; + } else { + /* + * If no commonPathPart was provided (eg. if AAF file has only one source file) + * then we take the full URI path to essence file and try each subpath starting + * from the end (filename). + * + * /essence_a.wav + * /media/essence_a.wav + * /path/media/essence_a.wav + * /data/path/media/essence_a.wav + */ + + char* p = uri->path + strlen (uri->path); + + while (p > uri->path) { + if (*p == '/') { /* parsing URI, so will always be '/' as separator character */ + + local_filepath = laaf_util_build_path (DIR_SEP_STR, local_path, p, NULL); + + if (!local_filepath) { + error ("Could not build filepath"); + goto err; + } + + debug ("Trying filepath : %s", local_filepath); + + if (laaf_util_file_exists (local_filepath) == 1) { + foundpath = local_filepath; + goto found; + } + + free (local_filepath); + local_filepath = NULL; + } + p--; + } } /* Try raw essence's URI, just in case... */ @@ -248,76 +328,6 @@ aafi_locate_external_essence_file (AAF_Iface* aafi, const char* original_uri_fil goto found; } - /* - * Try to locate essence file from the AAF file location. - * - * e.g. - * AAF filepath : /home/user/AAFFile.aaf - * + Essence URI : file://localhost/C:/Users/user/Desktop/AudioFiles/essence.wav - * = /home/user/AudioFiles/essence.file - */ - - /* extract path to AAF file */ - - aaf_path = laaf_util_c99strdup (aafi->aafd->cfbd->file); - - if (!aaf_path) { - error ("Could not duplicate AAF filepath : %s", aafi->aafd->cfbd->file); - goto err; - } - - p = aaf_path + strlen (aaf_path); - - while (p > aaf_path) { - if (IS_DIR_SEP (*p)) { - *p = 0x00; - break; - } - p--; - } - - /* - * "/" - */ - - local_filepath = laaf_util_build_path (DIR_SEP_STR, aaf_path, essenceFileName, NULL); - - if (!local_filepath) { - error ("Could not build filepath"); - goto err; - } - - debug ("AAF relative filepath : %s", local_filepath); - - if (laaf_util_file_exists (local_filepath) == 1) { - foundpath = local_filepath; - goto found; - } - - free (local_filepath); - local_filepath = NULL; - - /* - * "//" - */ - - local_filepath = laaf_util_build_path (DIR_SEP_STR, aaf_path, relativeEssencePath, NULL); - - if (!local_filepath) { - error ("Could not build filepath"); - goto err; - } - - debug ("AAF relative sub filepath : %s", local_filepath); - - if (laaf_util_file_exists (local_filepath) == 1) { - foundpath = local_filepath; - goto found; - } - - free (local_filepath); - local_filepath = NULL; - debug ("File not found"); found: @@ -336,7 +346,7 @@ found: goto err; } - debug ("File found at : %s", foundpath); + debug ("File found : %s", foundpath); } goto end; @@ -346,6 +356,7 @@ err: end: laaf_uri_free (uri); + laaf_uri_free (commonPathPartURI); free (local_filepath); free (aaf_path); @@ -774,10 +785,10 @@ aafi_parse_audio_essence (AAF_Iface* aafi, aafiAudioEssenceFile* audioEssenceFil laaf_util_is_fileext (audioEssenceFile->usable_file_path, "aif") || laaf_util_is_fileext (audioEssenceFile->usable_file_path, "aiff") || laaf_util_is_fileext (audioEssenceFile->usable_file_path, "aifc")) { - fp = fopen (audioEssenceFile->usable_file_path, "rb"); + fp = laaf_util_fopen_utf8 (audioEssenceFile->usable_file_path, "rb"); if (fp == NULL) { - error ("Could not open external audio essence file for reading : %s", audioEssenceFile->usable_file_path); + error ("Could not open external audio essence file for reading (%i: %s) : %s", errno, strerror (errno), audioEssenceFile->usable_file_path); goto err; } diff --git a/libs/aaf/AAFIParser.c b/libs/aaf/AAFIParser.c index 38cfe06c6f..9eed6be78e 100644 --- a/libs/aaf/AAFIParser.c +++ b/libs/aaf/AAFIParser.c @@ -3078,12 +3078,91 @@ aafi_retrieveData (AAF_Iface* aafi) /* Post processing */ - aafiAudioEssenceFile* audioEssenceFile = NULL; + char* commonPathPart = NULL; + + int externalAudioEssenceFileCount = 0; + aafiAudioEssenceFile* audioEssenceFile = NULL; AAFI_foreachAudioEssenceFile (aafi, audioEssenceFile) { if (!audioEssenceFile->is_embedded) { - audioEssenceFile->usable_file_path = aafi_locate_external_essence_file (aafi, audioEssenceFile->original_file_path, aafi->ctx.options.media_location); + if (!audioEssenceFile->original_file_path) { + continue; + } + + externalAudioEssenceFileCount++; + + if (!commonPathPart) { + int rc = laaf_util_snprintf_realloc (&commonPathPart, 0, 0, "%s", audioEssenceFile->original_file_path); + + if (rc < 0) { + error ("Failed to set commonPathPart"); + free (commonPathPart); + commonPathPart = NULL; + } + + // printf("Firstinit: %s\n", commonPathPart); + + continue; + } + + char* a = commonPathPart; + char* b = audioEssenceFile->original_file_path; + + char* aEnd = a + strlen (a); + char* bEnd = b + strlen (b); + + while (a < aEnd && b < bEnd) { + if (*a != *b) { + // printf("%c != %c\n", *a, *b ); + break; + } + a++; + b++; + } + + // printf("Then: %s %s\n", commonPathPart, audioEssenceFile->original_file_path); + + if (a < aEnd) { + *a = 0x00; + } + + // audioEssenceFile->usable_file_path = aafi_locate_external_essence_file( aafi, audioEssenceFile->original_file_path, aafi->ctx.options.media_location ); + // + // if ( audioEssenceFile->usable_file_path == NULL ) { + // warning( "Could not locate external audio essence file '%s'", audioEssenceFile->original_file_path ); + // } + } + } + + if (externalAudioEssenceFileCount <= 1) { + // printf("commonPathPart: %s\n", commonPathPart ); + free (commonPathPart); + commonPathPart = NULL; + } else if (commonPathPart && *commonPathPart != 0x00) { + /* + * Sometimes, all essence file names share a common prefix (Logic Pro). + * That's why we need to trim from the end, up to the last dir sep. + */ + + char* p = commonPathPart + strlen (commonPathPart); + + while (p > commonPathPart) { + if (*p == '/') { /* parsing URI, so will always be '/' as separator character */ + break; + } else { + *p = 0x00; + } + p--; + } + } + + audioEssenceFile = NULL; + + AAFI_foreachAudioEssenceFile (aafi, audioEssenceFile) + { + if (!audioEssenceFile->is_embedded) { + audioEssenceFile->usable_file_path = aafi_locate_external_essence_file (aafi, audioEssenceFile->original_file_path, commonPathPart, aafi->ctx.options.media_location); if (audioEssenceFile->usable_file_path == NULL) { warning ("Could not locate external audio essence file '%s'", audioEssenceFile->original_file_path); @@ -3136,7 +3215,7 @@ aafi_retrieveData (AAF_Iface* aafi) continue; } - videoEssenceFile->usable_file_path = aafi_locate_external_essence_file (aafi, videoEssenceFile->original_file_path, aafi->ctx.options.media_location); + videoEssenceFile->usable_file_path = aafi_locate_external_essence_file (aafi, videoEssenceFile->original_file_path, commonPathPart, aafi->ctx.options.media_location); if (videoEssenceFile->usable_file_path == NULL) { error ("Could not locate external video essence file '%s'", videoEssenceFile->original_file_path); @@ -3144,6 +3223,8 @@ aafi_retrieveData (AAF_Iface* aafi) } } + free (commonPathPart); + aafPosition_t trackEnd = 0; aafiAudioTrack* audioTrack = NULL; diff --git a/libs/aaf/AAFIface.c b/libs/aaf/AAFIface.c index 31e0245c1e..27042d3d87 100644 --- a/libs/aaf/AAFIface.c +++ b/libs/aaf/AAFIface.c @@ -400,6 +400,76 @@ aafi_convertUnitUint64 (aafPosition_t value, aafRational_t* valueEditRate, aafRa return (uint64_t)((double)value * (destEditRateFloat / valueEditRateFloat)); } +aafPosition_t +aafi_getClipLength (AAF_Iface* aafi, aafiAudioClip* audioClip, aafRational_t* samplerate, char* isBeyondSource) +{ + if (!audioClip) { + return 0; + } + + uint64_t smallestFileLength = 0; + aafiAudioEssenceFile* smallestEssenceFile = NULL; + + /* + * Common samplerate used for comparison, with a high resolution. Although it + * is very unlikely that each essence file composing the clip has a different + * samplerate, it is still possible... + */ + + aafRational_t base_samplerate = { 192000, 1 }; + + aafiAudioEssencePointer* essencePointer = NULL; + AAFI_foreachEssencePointer (audioClip->essencePointerList, essencePointer) + { + uint64_t fileLen = aafi_convertUnitUint64 (essencePointer->essenceFile->length, essencePointer->essenceFile->samplerateRational, &base_samplerate); + + if (!smallestFileLength || fileLen < smallestFileLength) { + smallestEssenceFile = essencePointer->essenceFile; + smallestFileLength = fileLen; + } + } + + if (!smallestEssenceFile || !smallestFileLength) { + if (!smallestEssenceFile) { + warning ("Clip has no source essence file"); + } else if (smallestFileLength == 0) { + warning ("Could not check source file length (0 or not set)"); + } + + if (samplerate) { + return aafi_convertUnit (audioClip->len, audioClip->track->edit_rate, samplerate); + } + + return aafi_convertUnit (audioClip->len, audioClip->track->edit_rate, smallestEssenceFile->samplerateRational); + } + + aafPosition_t clipLen = 0; + aafPosition_t fileLen = 0; + + if (samplerate) { + clipLen = aafi_convertUnit (audioClip->len, audioClip->track->edit_rate, samplerate); + fileLen = aafi_convertUnit (smallestEssenceFile->length, smallestEssenceFile->samplerateRational, samplerate); + } else { + clipLen = aafi_convertUnit (audioClip->len, audioClip->track->edit_rate, smallestEssenceFile->samplerateRational); + fileLen = smallestEssenceFile->length; + } + + if (clipLen > fileLen) { + if (isBeyondSource) { + *isBeyondSource = 1; + } + + warning ("Clip length (%li) is beyond the end of the smallest clip source file (%li). Using file length instead.", clipLen, fileLen); + return fileLen; + } + + if (isBeyondSource) { + *isBeyondSource = 0; + } + + return clipLen; +} + int aafi_removeTimelineItem (AAF_Iface* aafi, aafiTimelineItem* timelineItem) { diff --git a/libs/aaf/LibCFB.c b/libs/aaf/LibCFB.c index 0e6351eea8..6365c11d84 100644 --- a/libs/aaf/LibCFB.c +++ b/libs/aaf/LibCFB.c @@ -458,22 +458,7 @@ cfb_openFile (CFB_Data* cfbd) return -1; } -#ifdef _WIN32 - - wchar_t* wfile = laaf_util_windows_utf8toutf16 (cfbd->file); - - if (!wfile) { - error ("Unable to convert filepath to wide string : %s", cfbd->file); - return -1; - } - - cfbd->fp = _wfopen (wfile, L"rb"); - - free (wfile); - -#else - cfbd->fp = fopen (cfbd->file, "rb"); -#endif + cfbd->fp = laaf_util_fopen_utf8 (cfbd->file, "rb"); if (!cfbd->fp) { error ("%s.", strerror (errno)); diff --git a/libs/aaf/aaf/AAFCore.h b/libs/aaf/aaf/AAFCore.h index f8f078fe23..99daf3eb22 100644 --- a/libs/aaf/aaf/AAFCore.h +++ b/libs/aaf/aaf/AAFCore.h @@ -556,19 +556,19 @@ typedef struct _aafData { */ #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]) + ((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. diff --git a/libs/aaf/aaf/AAFIEssenceFile.h b/libs/aaf/aaf/AAFIEssenceFile.h index d87504e5a7..5572816aed 100644 --- a/libs/aaf/aaf/AAFIEssenceFile.h +++ b/libs/aaf/aaf/AAFIEssenceFile.h @@ -36,7 +36,7 @@ #include "aaf/AAFIface.h" char* -aafi_locate_external_essence_file (AAF_Iface* aafi, const char* original_uri_filepath, const char* search_location); +aafi_locate_external_essence_file (AAF_Iface* aafi, const char* original_uri_filepath, const char* commonPathPart, const char* search_location); /** * Extract audio essence file. diff --git a/libs/aaf/aaf/AAFIface.h b/libs/aaf/aaf/AAFIface.h index 27eb60f3fa..905b893d5a 100644 --- a/libs/aaf/aaf/AAFIface.h +++ b/libs/aaf/aaf/AAFIface.h @@ -988,6 +988,9 @@ aafi_convertUnit (aafPosition_t value, aafRational_t* valueEditRate, aafRational uint64_t aafi_convertUnitUint64 (aafPosition_t value, aafRational_t* valueEditRate, aafRational_t* destEditRate); +aafPosition_t +aafi_getClipLength (AAF_Iface* aafi, aafiAudioClip* audioClip, aafRational_t* samplerate, char* isBeyondSource); + int aafi_removeTimelineItem (AAF_Iface* aafi, aafiTimelineItem* timelineItem); diff --git a/libs/aaf/aaf/utils.h b/libs/aaf/aaf/utils.h index 7a5d594dce..7b1baee916 100644 --- a/libs/aaf/aaf/utils.h +++ b/libs/aaf/aaf/utils.h @@ -90,6 +90,9 @@ laaf_util_windows_utf16toutf8 (const wchar_t* wstr); int laaf_util_file_exists (const char* filepath); +FILE* +laaf_util_fopen_utf8 (const char* filepath, const char* mode); + char* laaf_util_clean_filename (char* filename); diff --git a/libs/aaf/aaf/version.h b/libs/aaf/aaf/version.h index 7751018042..c149b907ab 100644 --- a/libs/aaf/aaf/version.h +++ b/libs/aaf/aaf/version.h @@ -1,2 +1,2 @@ #pragma once -#define LIBAAF_VERSION "v1.0-11-gb04c547" +#define LIBAAF_VERSION "v1.0-22-gfab4651" diff --git a/libs/aaf/utils.c b/libs/aaf/utils.c index 5df26b19ba..71240eab99 100644 --- a/libs/aaf/utils.c +++ b/libs/aaf/utils.c @@ -513,6 +513,43 @@ laaf_util_file_exists (const char* filepath) return 0; } +FILE* +laaf_util_fopen_utf8 (const char* filepath, const char* mode) +{ + FILE* fp = NULL; + + if (!filepath) { + return NULL; + } + +#ifdef _WIN32 + wchar_t* wfile = laaf_util_windows_utf8toutf16 (filepath); + + if (!wfile) { + // error( "Unable to convert filepath to wide string : %s", cfbd->file ); + return NULL; + } + + const wchar_t* wmode = NULL; + + if (strcmp (mode, "rb") == 0) { + wmode = L"rb"; + } else if (strcmp (mode, "wb") == 0) { + wmode = L"wb"; + } else { + return NULL; + } + + fp = _wfopen (wfile, wmode); + + free (wfile); +#else + fp = fopen (filepath, mode); +#endif + + return fp; +} + static int utf8CodeLen (const uint16_t* u16Code) {