diff options
author | Hubert Figuière <hub@figuiere.net> | 2015-03-06 11:11:01 +0100 |
---|---|---|
committer | Hubert Figuière <hub@figuiere.net> | 2015-03-06 11:11:01 +0100 |
commit | 606a7df73750084a36fe69651e7b672333a76412 (patch) | |
tree | e337e53680715d69be570ccfcf8757ca898ea124 /XMPFiles | |
parent | 4652015fe779e12fb06ff8fa56bf70e373cd3894 (diff) |
Update to XMP SDK CC 2014.12
Diffstat (limited to 'XMPFiles')
54 files changed, 4535 insertions, 1034 deletions
diff --git a/XMPFiles/build/CMakeLists.txt b/XMPFiles/build/CMakeLists.txt index 28dc36a..76bdae0 100644 --- a/XMPFiles/build/CMakeLists.txt +++ b/XMPFiles/build/CMakeLists.txt @@ -39,6 +39,10 @@ set(XMP_THIS_PROJECT_RELATIVEPATH "../..") include(${CMAKE_CURRENT_SOURCE_DIR}/${XMP_THIS_PROJECT_RELATIVEPATH}/build/XMP_Config.cmake) + +set(TP_ZUID_PATH "${XMPROOT_DIR}/third-party/zuid/interfaces") +set(LIB_ADOBEXMP XMPCore) + # ============================================================================== # platform specific config # ============================================================================== @@ -69,314 +73,5 @@ else(UNIX) endif(WIN32) endif(UNIX) -# ============================================================================== -# For convenience we define the sources as a variable. You can add -# header files and cpp/c files and CMake will sort them out -# ============================================================================== - -#file (GLOB INTERNAL_HEADER_FILES ${PRODUCT_ROOT}/source/*.hpp ${PRODUCT_ROOT}/source/*.incl_cpp ${PRODUCT_ROOT}/build/*.h) -file (GLOB INTERNAL_HEADER_COMMONCODE_NMDS ${PRODUCT_ROOT}/XMPFiles/source/NativeMetadataSupport/*.h) -source_group("Header Files\\Internal Headers\\Common Code\\NativeMetadataSupport" FILES ${INTERNAL_HEADER_COMMONCODE_NMDS}) - -list (APPEND INTERNAL_HEADER_COMMONCODE - ${XMPROOT_DIR}/source/Endian.h - ${XMPROOT_DIR}/build/XMP_BuildInfo.h - ${SOURCE_ROOT}/XMPFiles.hpp - ${SOURCE_ROOT}/XMPFiles_Impl.hpp - ) -source_group("Header Files\\Internal Headers\\Common Code" FILES ${INTERNAL_HEADER_COMMONCODE}) - -file (GLOB INTERNAL_HEADER_FILEHANDLERS ${SOURCE_ROOT}/FileHandlers/*.hpp) -list (REMOVE_ITEM INTERNAL_HEADER_FILEHANDLERS - ${SOURCE_ROOT}/FileHandlers/AIFF_Handler.hpp - ${SOURCE_ROOT}/FileHandlers/GIF_Handler.hpp - ) -source_group("Header Files\\Internal Headers\\File Handlers" FILES ${INTERNAL_HEADER_FILEHANDLERS}) - -file (GLOB INTERNAL_HEADER_FORMATSUPPORT_AIFF ${SOURCE_ROOT}/FormatSupport/AIFF/*.h) -source_group("Header Files\\Internal Headers\\Format Support\\AIFF" FILES ${INTERNAL_HEADER_FORMATSUPPORT_AIFF}) - -file (GLOB INTERNAL_HEADER_FORMATSUPPORT_IFF ${SOURCE_ROOT}/FormatSupport/IFF/*.h) -source_group("Header Files\\Internal Headers\\Format Support\\IFF" FILES ${INTERNAL_HEADER_FORMATSUPPORT_IFF}) - -file (GLOB INTERNAL_HEADER_FORMATSUPPORT_WAVE ${SOURCE_ROOT}/FormatSupport/WAVE/*.h) -source_group("Header Files\\Internal Headers\\Format Support\\WAVE" FILES ${INTERNAL_HEADER_FORMATSUPPORT_WAVE}) - -file (GLOB INTERNAL_HEADER_FORMATSUPPORT ${SOURCE_ROOT}/FormatSupport/*.hpp) -list (REMOVE_ITEM INTERNAL_HEADER_FORMATSUPPORT - ${SOURCE_ROOT}/FormatSupport/GIF_Support.hpp - ) -list (REMOVE_ITEM INTERNAL_HEADER_FORMATSUPPORT -# ${SOURCE_ROOT}/source/MD5.cpp - ${XMPROOT_DIR}/source/UnicodeConversions.cpp - ) -source_group("Header Files\\Internal Headers\\Format Support" FILES ${INTERNAL_HEADER_FORMATSUPPORT}) - -file (GLOB INTERNAL_HEADER_PLUGINHANDLER ${SOURCE_ROOT}/PluginHandler/*.h) -source_group("Header Files\\Internal Headers\\PluginHandler" FILES ${INTERNAL_HEADER_PLUGINHANDLER}) - -list (APPEND PUBLIC_HEADER_CLIENTGLUE - ${XMPROOT_DIR}/public/include/client-glue/TXMPFiles.incl_cpp - ${XMPROOT_DIR}/public/include/client-glue/WXMP_Common.hpp - ${XMPROOT_DIR}/public/include/client-glue/WXMPFiles.hpp - ) -source_group("Header Files\\Public Headers\\Client Glue" FILES ${PUBLIC_HEADER_CLIENTGLUE}) -list (APPEND PUBLIC_HEADER - ${XMPROOT_DIR}/public/include/TXMPFiles.hpp - ${XMPROOT_DIR}/public/include/TXMPIterator.hpp - ${XMPROOT_DIR}/public/include/TXMPMeta.hpp - ${XMPROOT_DIR}/public/include/TXMPUtils.hpp - ${XMPROOT_DIR}/public/include/XMP.hpp - ${XMPROOT_DIR}/public/include/XMP.incl_cpp - ${XMPROOT_DIR}/public/include/XMP_Const.h - ${XMPROOT_DIR}/public/include/XMP_Environment.h - ${XMPROOT_DIR}/public/include/XMP_IO.hpp - ${XMPROOT_DIR}/public/include/XMP_Version.h - ) -source_group("Header Files\\Public Headers" FILES ${PUBLIC_HEADER}) - -file (GLOB HEADERFILES_THIRDPARTY_ZLIB ${XMPROOT_DIR}/third-party/zlib/*.h) -list (REMOVE_ITEM HEADERFILES_THIRDPARTY_ZLIB - ${CMAKE_CURRENT_SOURCE_DIR}/${XMPROOT_DIR}/third-party/zlib/gzguts.h - ) -source_group("Header Files\\ThirdParty\\zlib" FILES ${HEADERFILES_THIRDPARTY_ZLIB}) - -list (APPEND HEADERFILES - ${XMPROOT_DIR}/source/Host_IO.hpp - ${XMPROOT_DIR}/source/XIO.hpp - ${XMPROOT_DIR}/source/IOUtils.hpp - ${XMPROOT_DIR}/source/XMPFiles_IO.hpp - ) -source_group("Header Files" FILES ${HEADERFILES}) - -#resource files -file (GLOB RESOURCE_FILES ${RESOURCE_ROOT}/${XMP_PLATFORM_SHORT}/${TARGET_NAME}.*) -if(WIN32 AND ${XMP_BUILD_STATIC}) - list (REMOVE_ITEM RESOURCE_FILES ${RESOURCE_ROOT}/${XMP_PLATFORM_SHORT}/${TARGET_NAME}.rc) -endif() -source_group("Resource Files" FILES ${RESOURCE_FILES}) - -#source files -file (GLOB SOURCEFILES_COMMONCODE_NMDS ${SOURCE_ROOT}/NativeMetadataSupport/*.cpp) -source_group("Source Files\\Common Code\\NativeMetadataSupport" FILES ${SOURCEFILES_COMMONCODE_NMDS}) - -list (APPEND SOURCEFILES_COMMONCODE - ${XMPROOT_DIR}/third-party/zuid/interfaces/MD5.cpp - ${XMPROOT_DIR}/source/UnicodeConversions.cpp - ${SOURCE_ROOT}/HandlerRegistry.cpp - ${XMPROOT_DIR}/source/PerfUtils.cpp - ${SOURCE_ROOT}/WXMPFiles.cpp - ${XMPROOT_DIR}/source/XIO.cpp - ${XMPROOT_DIR}/source/IOUtils.cpp - ${XMPROOT_DIR}/source/XML_Node.cpp - ${XMPROOT_DIR}/source/XMP_LibUtils.cpp - ${XMPROOT_DIR}/source/XMP_ProgressTracker.cpp - ${SOURCE_ROOT}/XMPFiles.cpp - ${SOURCE_ROOT}/XMPFiles_Impl.cpp - ${XMPROOT_DIR}/source/XMPFiles_IO.cpp - ) -if(UNIX) - list(APPEND SOURCEFILES_COMMONCODE ${XMPROOT_DIR}/source/Host_IO-POSIX.cpp) -else() - list(APPEND SOURCEFILES_COMMONCODE ${XMPROOT_DIR}/source/Host_IO-Win.cpp) -endif() -source_group("Source Files\\Common Code" FILES ${SOURCEFILES_COMMONCODE}) - -file (GLOB SOURCEFILES_FILEHANDLERS ${SOURCE_ROOT}/FileHandlers/*.cpp) -list (APPEND SOURCEFILES_FILEHANDLERS - ${SOURCE_ROOT}/FileHandlers/AIFF_Handler.hpp - ) -list (REMOVE_ITEM SOURCEFILES_FILEHANDLERS - ${SOURCE_ROOT}/FileHandlers/GIF_Handler.cpp - ) -source_group("Source Files\\File Handlers" FILES ${SOURCEFILES_FILEHANDLERS}) - -file (GLOB SOURCEFILES_FORMATSUPPORT_AIFF ${SOURCE_ROOT}/FormatSupport/AIFF/*.cpp) -source_group("Source Files\\Format Support\\AIFF" FILES ${SOURCEFILES_FORMATSUPPORT_AIFF}) - -file (GLOB SOURCEFILES_FORMATSUPPORT_IFF ${SOURCE_ROOT}/FormatSupport/IFF/*.cpp) -source_group("Source Files\\Format Support\\IFF" FILES ${SOURCEFILES_FORMATSUPPORT_IFF}) - -file (GLOB SOURCEFILES_FORMATSUPPORT_WAVE ${SOURCE_ROOT}/FormatSupport/WAVE/*.cpp) -source_group("Source Files\\Format Support\\WAVE" FILES ${SOURCEFILES_FORMATSUPPORT_WAVE}) - -file (GLOB SOURCEFILES_FORMATSUPPORT ${SOURCE_ROOT}/FormatSupport/*.cpp) -list (REMOVE_ITEM SOURCEFILES_FORMATSUPPORT - ${SOURCE_ROOT}/FormatSupport/GIF_Support.cpp - ) -source_group("Source Files\\Format Support" FILES ${SOURCEFILES_FORMATSUPPORT}) - -file (GLOB SOURCEFILES_PLUGINHANDLER ${SOURCE_ROOT}/PluginHandler/*.cpp) -list (REMOVE_ITEM SOURCEFILES_PLUGINHANDLER - ${SOURCE_ROOT}/PluginHandler/OS_Utils_Linux.cpp - ${SOURCE_ROOT}/PluginHandler/OS_Utils_WIN.cpp - ${SOURCE_ROOT}/PluginHandler/OS_Utils_Mac.cpp - ) -if (UNIX) - if (APPLE) - list (APPEND SOURCEFILES_PLUGINHANDLER ${SOURCE_ROOT}/PluginHandler/OS_Utils_Mac.cpp) - else() - list (APPEND SOURCEFILES_PLUGINHANDLER ${SOURCE_ROOT}/PluginHandler/OS_Utils_Linux.cpp) - endif() -else() - list (APPEND SOURCEFILES_PLUGINHANDLER ${SOURCE_ROOT}/PluginHandler/OS_Utils_WIN.cpp) -endif() - -source_group("Source Files\\PluginHandler" FILES ${SOURCEFILES_PLUGINHANDLER}) - -list (APPEND HEADERFILES_THIRDPARTY_ZLIB - ${XMPROOT_DIR}/third-party/zlib/adler32.c - ${XMPROOT_DIR}/third-party/zlib/compress.c - ${XMPROOT_DIR}/third-party/zlib/crc32.c - ${XMPROOT_DIR}/third-party/zlib/deflate.c - ${XMPROOT_DIR}/third-party/zlib/infback.c - ${XMPROOT_DIR}/third-party/zlib/inffast.c - ${XMPROOT_DIR}/third-party/zlib/inflate.c - ${XMPROOT_DIR}/third-party/zlib/inftrees.c - ${XMPROOT_DIR}/third-party/zlib/trees.c - ${XMPROOT_DIR}/third-party/zlib/uncompr.c - ${XMPROOT_DIR}/third-party/zlib/zutil.c - ) -source_group("Source Files\\ThirdParty\\zlib" FILES ${HEADERFILES_THIRDPARTY_ZLIB}) - -list(APPEND SOURCE_FILES - ${INTERNAL_HEADER_COMMONCODE_NMDS} - ${INTERNAL_HEADER_COMMONCODE} - ${INTERNAL_HEADER_FILEHANDLERS} - ${INTERNAL_HEADER_FORMATSUPPORT_AIFF} - ${INTERNAL_HEADER_FORMATSUPPORT_IFF} - ${INTERNAL_HEADER_FORMATSUPPORT_WAVE} - ${INTERNAL_HEADER_FORMATSUPPORT} - ${INTERNAL_HEADER_PLUGINHANDLER} - ${PUBLIC_HEADER_CLIENTGLUE} - ${PUBLIC_HEADER} - ${HEADERFILES_THIRDPARTY_ZLIB} - ${HEADERFILES} - ${RESOURCE_FILES} - ${SOURCEFILES_COMMONCODE_NMDS} - ${SOURCEFILES_COMMONCODE} - ${SOURCEFILES_FILEHANDLERS} - ${SOURCEFILES_FORMATSUPPORT_AIFF} - ${SOURCEFILES_FORMATSUPPORT_IFF} - ${SOURCEFILES_FORMATSUPPORT_WAVE} - ${SOURCEFILES_FORMATSUPPORT} - ${SOURCEFILES_PLUGINHANDLER} - ${HEADERFILES_THIRDPARTY_ZLIB} - ) - -# include directories -include_directories(${XMPROOT_DIR}) -include_directories(${XMPROOT_DIR}/public/include) -include_directories(${XMPROOT_DIR}/third-party/expat/zlib) -include_directories(${XMPROOT_DIR}/XMPFilesPlugins/api/source) -include_directories(${RESOURCE_ROOT}/${XMP_PLATFORM_SHORT}) - -#additional link directory -set(LIB_ADOBEXMP XMPCore) -link_directories(${OUTPUT_DIR}) - -# ============================================================================== -# Define what to do, lib, exe, etc -if (UNIX AND APPLE AND NOT ${XMP_BUILD_STATIC}) - # preprocess Info.plist - add_custom_target(${TARGET_NAME}InfoPlist - COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR} - COMMAND if [ $(CONFIGURATION) != Debug ]; then - ${GCCTOOL} -E -P -x c ${RESOURCE_ROOT}/${XMP_PLATFORM_SHORT}/${TARGET_NAME}.plist - -F${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/ - -DPRODUCT_NAME=${TARGET_NAME} -DMAC_ENV=1 -DNDEBUG=1 - -include ${RESOURCE_ROOT}/${XMP_PLATFORM_SHORT}/${TARGET_NAME}PList.h - -o ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/Info.plist - \; else - ${GCCTOOL} -E -P -x c ${RESOURCE_ROOT}/${XMP_PLATFORM_SHORT}/${TARGET_NAME}.plist - -F${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/ - -DPRODUCT_NAME=${TARGET_NAME} -DMAC_ENV=1 -DDEBUG=1 - -include ${RESOURCE_ROOT}/${XMP_PLATFORM_SHORT}/${TARGET_NAME}PList.h - -o ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/Info.plist - \; fi - COMMAND rm -f ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/../CMakeFiles/${TARGET_NAME}.dir/Info.plist - COMMAND cp ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/Info.plist ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/../CMakeFiles/${TARGET_NAME}.dir/Info.plist - COMMENT "Preprocessing Info-plist" - ) - set(DEPENDENCY_LIST "ALL:${TARGET_NAME}InfoPlist" "DLL:XMPCore") -else () - set(DEPENDENCY_LIST "DLL:XMPCore") -endif() - -AddLibraryAndDependencies(${TARGET_NAME} ${XMP_BUILD_STATIC} YES "SHARED" SOURCE_FILES DEPENDENCY_LIST) - -# ============================================================================== -# Link dependencies - -if(WIN32) - target_link_libraries( - ${TARGET_NAME} - ${LIB_ADOBEXMP} - ${XMP_PLATFORM_LINK} - ) -else(WIN32) - if(UNIX AND NOT APPLE) - target_link_libraries( - ${TARGET_NAME} - ${LIB_ADOBEXMP} - ) - endif() -endif() - -set(FRAMEWORK_LIST "Mac:CoreFoundation" "Mac:CoreServices" "Mac:${LIB_ADOBEXMP}" "Mac:${XMP_PLATFORM_LINK}") -AddMacFramework(${TARGET_NAME} FRAMEWORK_LIST) - -if(UNIX) - if (NOT APPLE) - SetWinLinkFlags(${TARGET_NAME} "-Xlinker --version-script -Xlinker \"${RESOURCE_ROOT}/${XMP_PLATFORM_SHORT}/${TARGET_NAME}.exp\"" "") - else() - set_target_properties(${TARGET_NAME} PROPERTIES BUILD_WITH_INSTALL_RPATH ON INSTALL_NAME_DIR "@executable_path/../Frameworks") - SetWinLinkFlags(${TARGET_NAME} "-exported_symbols_list \"${RESOURCE_ROOT}/${XMP_PLATFORM_SHORT}/${TARGET_NAME}.exp\"" "${XMPFILES_LIB}") - endif() -else() - SetWinLinkFlags(${TARGET_NAME} "" "${XMPFILES_LIB}") -endif() - -set_target_properties(${TARGET_NAME} PROPERTIES OUTPUT_NAME ${XMPFILES_LIB}) - -# ============================================================================== -# Define output for this project -SetOutputPath(${OUTPUT_DIR} 0) - -# ============================================================================== -# Post build -# ============================================================================== - -if(UNIX) - #hack for unix to rename the output static library. cmake add lib and extenstion as .a, rename it - if (NOT APPLE) - if (${XMP_BUILD_STATIC}) - add_custom_command (TARGET ${TARGET_NAME} - POST_BUILD - COMMAND mv ${OUTPUT_DIR}/lib${XMPFILES_LIB}.a ${OUTPUT_DIR}/${XMPFILES_LIB}.ar - ) - else() - if((${CMAKE_BUILD_TYPE} MATCHES "Debug") OR (${CMAKE_BUILD_TYPE} MATCHES "debug") ) - add_custom_command (TARGET ${TARGET_NAME} - POST_BUILD - COMMAND ls -l ${OUTPUT_DIR}/lib${XMPFILES_LIB}.so - ) - else() - add_custom_command (TARGET ${TARGET_NAME} - POST_BUILD - COMMAND strip ${OUTPUT_DIR}/lib${XMPFILES_LIB}.so - COMMAND ls -l ${OUTPUT_DIR}/lib${XMPFILES_LIB}.so - ) - endif() - endif() - endif() - -else() - set_target_properties(${TARGET_NAME} PROPERTIES PROJECT_LABEL ${PROJECT_LABEL_STR}) - -endif() -message (STATUS "===========================================================================") -message (STATUS " ${PROJECT_NAME} ") -message (STATUS "===========================================================================") -message (STATUS " OUTPUT_DIR = ${OUTPUT_DIR}") +include(${CMAKE_CURRENT_SOURCE_DIR}/${XMP_THIS_PROJECT_RELATIVEPATH}/XMPFiles/build/CMakeListsCommon.txt)
\ No newline at end of file diff --git a/XMPFiles/build/CMakeListsCommon.txt b/XMPFiles/build/CMakeListsCommon.txt new file mode 100644 index 0000000..2f116d0 --- /dev/null +++ b/XMPFiles/build/CMakeListsCommon.txt @@ -0,0 +1,318 @@ + +# ============================================================================== +# For convenience we define the sources as a variable. You can add +# header files and cpp/c files and CMake will sort them out +# ============================================================================== + +#file (GLOB INTERNAL_HEADER_FILES ${PRODUCT_ROOT}/source/*.hpp ${PRODUCT_ROOT}/source/*.incl_cpp ${PRODUCT_ROOT}/build/*.h) +file (GLOB INTERNAL_HEADER_COMMONCODE_NMDS ${PRODUCT_ROOT}/XMPFiles/source/NativeMetadataSupport/*.h) +source_group("Header Files\\Internal Headers\\Common Code\\NativeMetadataSupport" FILES ${INTERNAL_HEADER_COMMONCODE_NMDS}) + +list (APPEND INTERNAL_HEADER_COMMONCODE + ${XMPROOT_DIR}/source/Endian.h + ${XMPROOT_DIR}/source/SafeStringAPIs.h + ${XMPROOT_DIR}/source/SafeTypes.h + ${XMPROOT_DIR}/source/SuppressSAL.h + ${XMPROOT_DIR}/build/XMP_BuildInfo.h + ${SOURCE_ROOT}/XMPFiles.hpp + ${SOURCE_ROOT}/XMPFiles_Impl.hpp + ) +source_group("Header Files\\Internal Headers\\Common Code" FILES ${INTERNAL_HEADER_COMMONCODE}) + +file (GLOB INTERNAL_HEADER_FILEHANDLERS ${SOURCE_ROOT}/FileHandlers/*.hpp) +list (REMOVE_ITEM INTERNAL_HEADER_FILEHANDLERS + ${SOURCE_ROOT}/FileHandlers/AIFF_Handler.hpp + ${SOURCE_ROOT}/FileHandlers/GIF_Handler.hpp + ) +source_group("Header Files\\Internal Headers\\File Handlers" FILES ${INTERNAL_HEADER_FILEHANDLERS}) + +file (GLOB INTERNAL_HEADER_FORMATSUPPORT_AIFF ${SOURCE_ROOT}/FormatSupport/AIFF/*.h) +source_group("Header Files\\Internal Headers\\Format Support\\AIFF" FILES ${INTERNAL_HEADER_FORMATSUPPORT_AIFF}) + +file (GLOB INTERNAL_HEADER_FORMATSUPPORT_IFF ${SOURCE_ROOT}/FormatSupport/IFF/*.h) +source_group("Header Files\\Internal Headers\\Format Support\\IFF" FILES ${INTERNAL_HEADER_FORMATSUPPORT_IFF}) + +file (GLOB INTERNAL_HEADER_FORMATSUPPORT_WAVE ${SOURCE_ROOT}/FormatSupport/WAVE/*.h) +source_group("Header Files\\Internal Headers\\Format Support\\WAVE" FILES ${INTERNAL_HEADER_FORMATSUPPORT_WAVE}) + +file (GLOB INTERNAL_HEADER_FORMATSUPPORT ${SOURCE_ROOT}/FormatSupport/*.hpp) +list (REMOVE_ITEM INTERNAL_HEADER_FORMATSUPPORT + ${SOURCE_ROOT}/FormatSupport/GIF_Support.hpp + ) +list (REMOVE_ITEM INTERNAL_HEADER_FORMATSUPPORT +# ${SOURCE_ROOT}/source/MD5.cpp + ${XMPROOT_DIR}/source/UnicodeConversions.cpp + ) +source_group("Header Files\\Internal Headers\\Format Support" FILES ${INTERNAL_HEADER_FORMATSUPPORT}) + +file (GLOB INTERNAL_HEADER_PLUGINHANDLER ${SOURCE_ROOT}/PluginHandler/*.h) +source_group("Header Files\\Internal Headers\\PluginHandler" FILES ${INTERNAL_HEADER_PLUGINHANDLER}) + +list (APPEND PUBLIC_HEADER_CLIENTGLUE + ${XMPROOT_DIR}/public/include/client-glue/TXMPFiles.incl_cpp + ${XMPROOT_DIR}/public/include/client-glue/WXMP_Common.hpp + ${XMPROOT_DIR}/public/include/client-glue/WXMPFiles.hpp + ) +source_group("Header Files\\Public Headers\\Client Glue" FILES ${PUBLIC_HEADER_CLIENTGLUE}) +list (APPEND PUBLIC_HEADER + ${XMPROOT_DIR}/public/include/TXMPFiles.hpp + ${XMPROOT_DIR}/public/include/TXMPIterator.hpp + ${XMPROOT_DIR}/public/include/TXMPMeta.hpp + ${XMPROOT_DIR}/public/include/TXMPUtils.hpp + ${XMPROOT_DIR}/public/include/XMP.hpp + ${XMPROOT_DIR}/public/include/XMP.incl_cpp + ${XMPROOT_DIR}/public/include/XMP_Const.h + ${XMPROOT_DIR}/public/include/XMP_Environment.h + ${XMPROOT_DIR}/public/include/XMP_IO.hpp + ${XMPROOT_DIR}/public/include/XMP_Version.h + ) +source_group("Header Files\\Public Headers" FILES ${PUBLIC_HEADER}) + +file (GLOB HEADERFILES_THIRDPARTY_ZLIB ${XMPROOT_DIR}/third-party/zlib/*.h) +list (REMOVE_ITEM HEADERFILES_THIRDPARTY_ZLIB + ${CMAKE_CURRENT_SOURCE_DIR}/${XMPROOT_DIR}/third-party/zlib/gzguts.h + ) +source_group("Header Files\\ThirdParty\\zlib" FILES ${HEADERFILES_THIRDPARTY_ZLIB}) + +list (APPEND HEADERFILES + ${XMPROOT_DIR}/source/Host_IO.hpp + ${XMPROOT_DIR}/source/XIO.hpp + ${XMPROOT_DIR}/source/IOUtils.hpp + ${XMPROOT_DIR}/source/XMPFiles_IO.hpp + ) +source_group("Header Files" FILES ${HEADERFILES}) + +#resource files +file (GLOB RESOURCE_FILES ${RESOURCE_ROOT}/${XMP_PLATFORM_SHORT}/${TARGET_NAME}.*) +if(WIN32 AND ${XMP_BUILD_STATIC}) + list (REMOVE_ITEM RESOURCE_FILES ${RESOURCE_ROOT}/${XMP_PLATFORM_SHORT}/${TARGET_NAME}.rc) +endif() +source_group("Resource Files" FILES ${RESOURCE_FILES}) + +#source files +file (GLOB SOURCEFILES_COMMONCODE_NMDS ${SOURCE_ROOT}/NativeMetadataSupport/*.cpp) +source_group("Source Files\\Common Code\\NativeMetadataSupport" FILES ${SOURCEFILES_COMMONCODE_NMDS}) + +list (APPEND SOURCEFILES_COMMONCODE + ${TP_ZUID_PATH}/MD5.cpp + ${XMPROOT_DIR}/source/UnicodeConversions.cpp + ${SOURCE_ROOT}/HandlerRegistry.cpp + ${XMPROOT_DIR}/source/SafeStringAPIs.cpp + ${XMPROOT_DIR}/source/PerfUtils.cpp + ${SOURCE_ROOT}/WXMPFiles.cpp + ${XMPROOT_DIR}/source/XIO.cpp + ${XMPROOT_DIR}/source/IOUtils.cpp + ${XMPROOT_DIR}/source/XML_Node.cpp + ${XMPROOT_DIR}/source/XMP_LibUtils.cpp + ${XMPROOT_DIR}/source/XMP_ProgressTracker.cpp + ${SOURCE_ROOT}/XMPFiles.cpp + ${SOURCE_ROOT}/XMPFiles_Impl.cpp + ${XMPROOT_DIR}/source/XMPFiles_IO.cpp + ) +if(UNIX) + list(APPEND SOURCEFILES_COMMONCODE ${XMPROOT_DIR}/source/Host_IO-POSIX.cpp) +else() + list(APPEND SOURCEFILES_COMMONCODE ${XMPROOT_DIR}/source/Host_IO-Win.cpp) +endif() +source_group("Source Files\\Common Code" FILES ${SOURCEFILES_COMMONCODE}) + +file (GLOB SOURCEFILES_FILEHANDLERS ${SOURCE_ROOT}/FileHandlers/*.cpp) +list (APPEND SOURCEFILES_FILEHANDLERS + ${SOURCE_ROOT}/FileHandlers/AIFF_Handler.hpp + ) +list (REMOVE_ITEM SOURCEFILES_FILEHANDLERS + ${SOURCE_ROOT}/FileHandlers/GIF_Handler.cpp + ) +source_group("Source Files\\File Handlers" FILES ${SOURCEFILES_FILEHANDLERS}) + +file (GLOB SOURCEFILES_FORMATSUPPORT_AIFF ${SOURCE_ROOT}/FormatSupport/AIFF/*.cpp) +source_group("Source Files\\Format Support\\AIFF" FILES ${SOURCEFILES_FORMATSUPPORT_AIFF}) + +file (GLOB SOURCEFILES_FORMATSUPPORT_IFF ${SOURCE_ROOT}/FormatSupport/IFF/*.cpp) +source_group("Source Files\\Format Support\\IFF" FILES ${SOURCEFILES_FORMATSUPPORT_IFF}) + +file (GLOB SOURCEFILES_FORMATSUPPORT_WAVE ${SOURCE_ROOT}/FormatSupport/WAVE/*.cpp) +source_group("Source Files\\Format Support\\WAVE" FILES ${SOURCEFILES_FORMATSUPPORT_WAVE}) + +file (GLOB SOURCEFILES_FORMATSUPPORT ${SOURCE_ROOT}/FormatSupport/*.cpp) +list (REMOVE_ITEM SOURCEFILES_FORMATSUPPORT + ${SOURCE_ROOT}/FormatSupport/GIF_Support.cpp + ) +source_group("Source Files\\Format Support" FILES ${SOURCEFILES_FORMATSUPPORT}) + +if (NOT APPLE_IOS) + file (GLOB SOURCEFILES_PLUGINHANDLER ${SOURCE_ROOT}/PluginHandler/*.cpp) + list (REMOVE_ITEM SOURCEFILES_PLUGINHANDLER + ${SOURCE_ROOT}/PluginHandler/OS_Utils_Linux.cpp + ${SOURCE_ROOT}/PluginHandler/OS_Utils_WIN.cpp + ${SOURCE_ROOT}/PluginHandler/OS_Utils_Mac.cpp + ) + if (UNIX) + if (APPLE) + list (APPEND SOURCEFILES_PLUGINHANDLER ${SOURCE_ROOT}/PluginHandler/OS_Utils_Mac.cpp) + else() + list (APPEND SOURCEFILES_PLUGINHANDLER ${SOURCE_ROOT}/PluginHandler/OS_Utils_Linux.cpp) + endif() + else() + list (APPEND SOURCEFILES_PLUGINHANDLER ${SOURCE_ROOT}/PluginHandler/OS_Utils_WIN.cpp) + endif() + + source_group("Source Files\\PluginHandler" FILES ${SOURCEFILES_PLUGINHANDLER}) +endif() + +list (APPEND HEADERFILES_THIRDPARTY_ZLIB + ${XMPROOT_DIR}/third-party/zlib/adler32.c + ${XMPROOT_DIR}/third-party/zlib/compress.c + ${XMPROOT_DIR}/third-party/zlib/crc32.c + ${XMPROOT_DIR}/third-party/zlib/deflate.c + ${XMPROOT_DIR}/third-party/zlib/infback.c + ${XMPROOT_DIR}/third-party/zlib/inffast.c + ${XMPROOT_DIR}/third-party/zlib/inflate.c + ${XMPROOT_DIR}/third-party/zlib/inftrees.c + ${XMPROOT_DIR}/third-party/zlib/trees.c + ${XMPROOT_DIR}/third-party/zlib/uncompr.c + ${XMPROOT_DIR}/third-party/zlib/zutil.c + ) +source_group("Source Files\\ThirdParty\\zlib" FILES ${HEADERFILES_THIRDPARTY_ZLIB}) + +list(APPEND SOURCE_FILES + ${INTERNAL_HEADER_COMMONCODE_NMDS} + ${INTERNAL_HEADER_COMMONCODE} + ${INTERNAL_HEADER_FILEHANDLERS} + ${INTERNAL_HEADER_FORMATSUPPORT_AIFF} + ${INTERNAL_HEADER_FORMATSUPPORT_IFF} + ${INTERNAL_HEADER_FORMATSUPPORT_WAVE} + ${INTERNAL_HEADER_FORMATSUPPORT} + ${INTERNAL_HEADER_PLUGINHANDLER} + ${PUBLIC_HEADER_CLIENTGLUE} + ${PUBLIC_HEADER} + ${HEADERFILES_THIRDPARTY_ZLIB} + ${HEADERFILES} + ${RESOURCE_FILES} + ${SOURCEFILES_COMMONCODE_NMDS} + ${SOURCEFILES_COMMONCODE} + ${SOURCEFILES_FILEHANDLERS} + ${SOURCEFILES_FORMATSUPPORT_AIFF} + ${SOURCEFILES_FORMATSUPPORT_IFF} + ${SOURCEFILES_FORMATSUPPORT_WAVE} + ${SOURCEFILES_FORMATSUPPORT} + ${SOURCEFILES_PLUGINHANDLER} + ${HEADERFILES_THIRDPARTY_ZLIB} + ) + +# include directories +include_directories(${XMPROOT_DIR}) +include_directories(${XMPROOT_DIR}/public/include) +include_directories(${XMPROOT_DIR}/third-party/expat/zlib) +include_directories(${XMPROOT_DIR}/XMPFilesPlugins/api/source) +include_directories(${RESOURCE_ROOT}/${XMP_PLATFORM_SHORT}) + +#additional link directory +link_directories(${OUTPUT_DIR}) + +# ============================================================================== +# Define what to do, lib, exe, etc +if (UNIX AND APPLE AND NOT ${XMP_BUILD_STATIC}) + # preprocess Info.plist + add_custom_target(${TARGET_NAME}InfoPlist + COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR} + COMMAND if [ $(CONFIGURATION) != Debug ]; then + ${GCCTOOL} -E -P -x c ${RESOURCE_ROOT}/${XMP_PLATFORM_SHORT}/${TARGET_NAME}.plist + -F${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/ + -DPRODUCT_NAME=${TARGET_NAME} -DMAC_ENV=1 -DNDEBUG=1 + -include ${RESOURCE_ROOT}/${XMP_PLATFORM_SHORT}/${TARGET_NAME}PList.h + -o ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/Info.plist + \; else + ${GCCTOOL} -E -P -x c ${RESOURCE_ROOT}/${XMP_PLATFORM_SHORT}/${TARGET_NAME}.plist + -F${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/ + -DPRODUCT_NAME=${TARGET_NAME} -DMAC_ENV=1 -DDEBUG=1 + -include ${RESOURCE_ROOT}/${XMP_PLATFORM_SHORT}/${TARGET_NAME}PList.h + -o ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/Info.plist + \; fi + COMMAND rm -f ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/../CMakeFiles/${TARGET_NAME}.dir/Info.plist + COMMAND cp ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/Info.plist ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/../CMakeFiles/${TARGET_NAME}.dir/Info.plist + COMMENT "Preprocessing Info-plist" + ) + set(DEPENDENCY_LIST "ALL:${TARGET_NAME}InfoPlist" "DLL:XMPCore") +else () + set(DEPENDENCY_LIST "DLL:XMPCore") +endif() + +AddLibraryAndDependencies(${TARGET_NAME} ${XMP_BUILD_STATIC} YES "SHARED" SOURCE_FILES DEPENDENCY_LIST) + +# ============================================================================== +# Link dependencies + +if(WIN32) + target_link_libraries( + ${TARGET_NAME} + ${LIB_ADOBEXMP} + ${XMP_PLATFORM_LINK} + ) +else(WIN32) + if(UNIX AND NOT APPLE) + target_link_libraries( + ${TARGET_NAME} + ${LIB_ADOBEXMP} + ) + endif() +endif() + +set(FRAMEWORK_LIST "Mac:CoreFoundation" "Mac:CoreServices" "Mac:${LIB_ADOBEXMP}" "Mac:${XMP_PLATFORM_LINK}") +AddMacFramework(${TARGET_NAME} FRAMEWORK_LIST) + +if(UNIX) + if (NOT APPLE) + SetPlatformLinkFlags(${TARGET_NAME} "-Xlinker --version-script -Xlinker \"${RESOURCE_ROOT}/${XMP_PLATFORM_SHORT}/${TARGET_NAME}.exp\"" "") + else() + set_target_properties(${TARGET_NAME} PROPERTIES BUILD_WITH_INSTALL_RPATH ON INSTALL_NAME_DIR "@executable_path/../Frameworks") + SetPlatformLinkFlags(${TARGET_NAME} "-exported_symbols_list \"${RESOURCE_ROOT}/${XMP_PLATFORM_SHORT}/${TARGET_NAME}.exp\"" "${XMPFILES_LIB}") + endif() +else() + SetPlatformLinkFlags(${TARGET_NAME} "" "${XMPFILES_LIB}") +endif() + +set_target_properties(${TARGET_NAME} PROPERTIES OUTPUT_NAME ${XMPFILES_LIB}) + +# ============================================================================== +# Define output for this project +SetOutputPath(${OUTPUT_DIR} 0) + +# ============================================================================== +# Post build +# ============================================================================== + +if(UNIX) + #hack for unix to rename the output static library. cmake add lib and extenstion as .a, rename it + if (NOT APPLE) + if (${XMP_BUILD_STATIC}) + add_custom_command (TARGET ${TARGET_NAME} + POST_BUILD + COMMAND mv ${OUTPUT_DIR}/lib${XMPFILES_LIB}.a ${OUTPUT_DIR}/${XMPFILES_LIB}.ar + ) + else() + if((${CMAKE_BUILD_TYPE} MATCHES "Debug") OR (${CMAKE_BUILD_TYPE} MATCHES "debug") ) + add_custom_command (TARGET ${TARGET_NAME} + POST_BUILD + COMMAND ls -l ${OUTPUT_DIR}/lib${XMPFILES_LIB}.so + ) + else() + add_custom_command (TARGET ${TARGET_NAME} + POST_BUILD + COMMAND strip ${OUTPUT_DIR}/lib${XMPFILES_LIB}.so + COMMAND ls -l ${OUTPUT_DIR}/lib${XMPFILES_LIB}.so + ) + endif() + endif() + endif() + + +else() + set_target_properties(${TARGET_NAME} PROPERTIES PROJECT_LABEL ${PROJECT_LABEL_STR}) + +endif() + +message (STATUS "===========================================================================") +message (STATUS " ${PROJECT_NAME} ") +message (STATUS "===========================================================================") +message (STATUS " OUTPUT_DIR = ${OUTPUT_DIR}") diff --git a/XMPFiles/source/FileHandlers/AVCHD_Handler.cpp b/XMPFiles/source/FileHandlers/AVCHD_Handler.cpp index a58594f..95d4f1b 100644 --- a/XMPFiles/source/FileHandlers/AVCHD_Handler.cpp +++ b/XMPFiles/source/FileHandlers/AVCHD_Handler.cpp @@ -1979,7 +1979,7 @@ bool AVCHD_MetaHandler::GetFileModDate ( XMP_DateTime * modDate ) ok = this->MakeClipInfoPath ( &fullPath, ".clpi", true /* checkFile */ ); if ( ok ) ok = Host_IO::GetModifyDate ( fullPath.c_str(), &oneDate ); if ( ok ) { - if ( (! haveDate) || (*modDate < oneDate) ) *modDate = oneDate; + if ( *modDate < oneDate ) *modDate = oneDate; haveDate = true; } @@ -2270,7 +2270,7 @@ void AVCHD_MetaHandler::ProcessXMP() if ( has2_2pulldown ) xmpValue = "29.97p"; else - xmpValue = has3_2pulldown ? "23.98p" : "59.94i"; + xmpValue = "59.94i"; break; default: break; diff --git a/XMPFiles/source/FileHandlers/JPEG_Handler.cpp b/XMPFiles/source/FileHandlers/JPEG_Handler.cpp index ea025bd..bdc5505 100644 --- a/XMPFiles/source/FileHandlers/JPEG_Handler.cpp +++ b/XMPFiles/source/FileHandlers/JPEG_Handler.cpp @@ -276,6 +276,9 @@ void JPEG_MetaHandler::CacheFileData() static const size_t kBufferSize = 64*1024; // Enough for maximum segment contents. XMP_Uns8 buffer [kBufferSize]; + psirContents.clear(); + exifContents.clear(); + XMP_AbortProc abortProc = this->parent->abortProc; void * abortArg = this->parent->abortArg; const bool checkAbort = (abortProc != 0); @@ -333,7 +336,7 @@ void JPEG_MetaHandler::CacheFileData() size_t psirLen = contentLen - kPSIRSignatureLength; fileRef->Seek ( (contentOrigin + kPSIRSignatureLength), kXMP_SeekFromStart ); fileRef->ReadAll ( buffer, psirLen ); - this->psirContents.assign ( (char*)buffer, psirLen ); + this->psirContents.append( (char *) buffer, psirLen ); continue; // Move on to the next marker. } @@ -354,7 +357,7 @@ void JPEG_MetaHandler::CacheFileData() size_t exifLen = contentLen - kExifSignatureLength; fileRef->Seek ( (contentOrigin + kExifSignatureLength), kXMP_SeekFromStart ); fileRef->ReadAll ( buffer, exifLen ); - this->exifContents.assign ( (char*)buffer, exifLen ); + this->exifContents.append ( (char*)buffer, exifLen ); continue; // Move on to the next marker. } @@ -535,8 +538,10 @@ void JPEG_MetaHandler::ProcessXMP() XMP_Assert ( (this->psirMgr == 0) && (this->iptcMgr == 0) ); // ProcessTNail might create the exifMgr. - bool readOnly = ((this->parent->openFlags & kXMPFiles_OpenForUpdate) == 0); - + bool readOnly = false; + if ( this->parent ){ + readOnly = ((this->parent->openFlags & kXMPFiles_OpenForUpdate) == 0); + } if ( readOnly ) { if ( this->exifMgr == 0 ) this->exifMgr = new TIFF_MemoryReader(); this->psirMgr = new PSIR_MemoryReader(); @@ -826,19 +831,16 @@ void JPEG_MetaHandler::WriteTempFile ( XMP_IO* tempRef ) void* exifPtr; XMP_Uns32 exifLen = this->exifMgr->UpdateMemoryStream ( &exifPtr ); if ( exifLen > kExifMaxDataLength ) exifLen = this->exifMgr->UpdateMemoryStream ( &exifPtr, true /* compact */ ); - if ( exifLen > kExifMaxDataLength ) { - // XMP_Throw ( "Overflow of Exif APP1 data", kXMPErr_BadJPEG ); ** Used to throw, now rewrite original Exif. - exifPtr = (void*)this->exifContents.c_str(); - exifLen = this->exifContents.size(); - } - if ( exifLen > 0 ) { - first4 = MakeUns32BE ( 0xFFE10000 + 2 + kExifSignatureLength + exifLen ); + while ( exifLen > 0 ) { + XMP_Uns32 count = std::min ( exifLen, (XMP_Uns32) kExifMaxDataLength ); + first4 = MakeUns32BE ( 0xFFE10000 + 2 + kExifSignatureLength + count ); tempRef->Write ( &first4, 4 ); tempRef->Write ( kExifSignatureString, kExifSignatureLength ); - tempRef->Write ( exifPtr, exifLen ); + tempRef->Write ( exifPtr, count ); + exifPtr = (XMP_Uns8 *) exifPtr + count; + exifLen -= count; } - } // Write the new XMP APP1 marker segment, with possible extension marker segments. @@ -878,25 +880,23 @@ void JPEG_MetaHandler::WriteTempFile ( XMP_IO* tempRef ) } - // Write the new PSIR APP13 marker segment. - + // Write the new PSIR APP13 marker segments. if ( this->psirMgr != 0 ) { void* psirPtr; XMP_Uns32 psirLen = this->psirMgr->UpdateMemoryResources ( &psirPtr ); - if ( psirLen > kPSIRMaxDataLength ) XMP_Throw ( "Overflow of PSIR APP13 data", kXMPErr_BadJPEG ); - - if ( psirLen > 0 ) { - first4 = MakeUns32BE ( 0xFFED0000 + 2 + kPSIRSignatureLength + psirLen ); + while ( psirLen > 0 ) { + XMP_Uns32 count = std::min ( psirLen, (XMP_Uns32) kPSIRMaxDataLength ); + first4 = MakeUns32BE ( 0xFFED0000 + 2 + kPSIRSignatureLength + count ); tempRef->Write ( &first4, 4 ); tempRef->Write ( kPSIRSignatureString, kPSIRSignatureLength ); - tempRef->Write ( psirPtr, psirLen ); + tempRef->Write ( psirPtr, count ); + psirPtr = (XMP_Uns8 *) psirPtr + count; + psirLen -= count; } - } // Copy remaining marker segments, skipping old metadata, to the first SOS marker or to EOI. - origRef->Seek ( -2, kXMP_SeekFromCurrent ); // Back up to the marker from the end of the APP0 copy loop. while ( true ) { diff --git a/XMPFiles/source/FileHandlers/MP3_Handler.cpp b/XMPFiles/source/FileHandlers/MP3_Handler.cpp index a568c1a..3215e72 100644 --- a/XMPFiles/source/FileHandlers/MP3_Handler.cpp +++ b/XMPFiles/source/FileHandlers/MP3_Handler.cpp @@ -119,6 +119,20 @@ bool MP3_CheckFormat ( XMP_FileFormat format, MP3_MetaHandler::MP3_MetaHandler ( XMPFiles * _parent ) { + this->oldTagSize = 0; + this->oldPadding = 0; + this->oldFramesSize = 0; + this->newTagSize = 0; + this->newPadding = 0; + this->newFramesSize = 0; + this->tagIsDirty = false; + this->mustShift = false; + this->majorVersion = 2.3; + this->minorVersion = 2.3; + this->hasID3Tag = false; + this->hasFooter = false; + this->extHeaderSize = 0; + this->hasExtHeader = false; this->parent = _parent; this->handlerFlags = kMP3_HandlerFlags; this->stdCharForm = kXMP_Char8Bit; @@ -423,23 +437,26 @@ void MP3_MetaHandler::ProcessXMP() }//for reconProps // import DateTime - XMP_DateTime oldDateTime; - xmpObj.GetProperty_Date ( kXMP_NS_XMP, "CreateDate", &oldDateTime, 0 ); + XMP_DateTime oldDateTime; + bool haveNewDateTime = newDateTime.year != 0 ; + if ( xmpObj.GetProperty_Date ( kXMP_NS_XMP, "CreateDate", &oldDateTime, 0 ) ) + { + + haveNewDateTime = haveNewDateTime && + ( (newDateTime.year != oldDateTime.year) || + ( (newDateTime.month != 0 ) && ( (newDateTime.day != oldDateTime.day) || (newDateTime.month != oldDateTime.month) ) ) || + ( newDateTime.hasTime && ( (newDateTime.hour != oldDateTime.hour) || (newDateTime.minute != oldDateTime.minute) ) ) ); + } + // NOTE: no further validation nessesary the function "SetProperty_Date" will care about validating date and time + // any exception will be caught and block import + try { + if ( haveNewDateTime ) { + this->xmpObj.SetProperty_Date ( kXMP_NS_XMP, "CreateDate", newDateTime ); + } + } catch ( ... ) { + // Dont import invalid dates from ID3 + } - // NOTE: no further validation nessesary the function "SetProperty_Date" will care about validating date and time - // any exception will be caught and block import - try { - bool haveNewDateTime = (newDateTime.year != 0) && - ( (newDateTime.year != oldDateTime.year) || - ( (newDateTime.month != 0 ) && ( (newDateTime.day != oldDateTime.day) || (newDateTime.month != oldDateTime.month) ) ) || - ( newDateTime.hasTime && ( (newDateTime.hour != oldDateTime.minute) || (newDateTime.hour != oldDateTime.minute) ) ) ); - if ( haveNewDateTime ) { - this->xmpObj.SetProperty_Date ( kXMP_NS_XMP, "CreateDate", newDateTime ); - } - } catch ( ... ) { - // Dont import invalid dates from ID3 - } - } // very important to avoid multiple runs! (in which case I'd need to clean certain diff --git a/XMPFiles/source/FileHandlers/MPEG4_Handler.cpp b/XMPFiles/source/FileHandlers/MPEG4_Handler.cpp index 2dfe088..905f19f 100644 --- a/XMPFiles/source/FileHandlers/MPEG4_Handler.cpp +++ b/XMPFiles/source/FileHandlers/MPEG4_Handler.cpp @@ -24,6 +24,7 @@ #include "source/XMP_ProgressTracker.hpp" #include "source/UnicodeConversions.hpp" #include "third-party/zuid/interfaces/MD5.h" +#include <math.h> #if XMP_WinBuild #pragma warning ( disable : 4996 ) // '...' was declared deprecated @@ -142,6 +143,31 @@ static inline XMP_StringPtr Lookup3LetterLang ( XMP_StringPtr lang2 ) return ""; } + +#define IsTolerableBoxChar(ch) ( ((0x20 <= (ch)) && ((ch) <= 0x7E)) || ((ch) == 0xA9) ) + +static inline bool IsTolerableBox ( XMP_Uns32 boxType ) +{ + // Make sure the box type is 4 ASCII characters or 0xA9 (MacRoman copyright). + XMP_Uns8 b1 = (XMP_Uns8) (boxType >> 24); + XMP_Uns8 b2 = (XMP_Uns8) ((boxType >> 16) & 0xFF); + XMP_Uns8 b3 = (XMP_Uns8) ((boxType >> 8) & 0xFF); + XMP_Uns8 b4 = (XMP_Uns8) (boxType & 0xFF); + bool ok = IsTolerableBoxChar(b1) && IsTolerableBoxChar(b2) && + IsTolerableBoxChar(b3) && IsTolerableBoxChar(b4); + return ok; +} + +static inline bool IsXMPUUID ( XMP_IO * fileRef,XMP_Uns64 contentSize, bool unmovedFilePtr=false ) +{ + if ( contentSize < 16 ) return false; + XMP_Uns8 uuid [16]; + fileRef->ReadAll ( uuid, 16 ); + if (unmovedFilePtr) fileRef->Seek ( -16, kXMP_SeekFromCurrent ); + if ( memcmp ( uuid, ISOMedia::k_xmpUUID, 16 ) != 0 ) return false; // Check for the XMP GUID. + return true; +} + // ================================================================================================= // MPEG4_CheckFormat // ================= @@ -184,8 +210,6 @@ bool MPEG4_CheckFormat ( XMP_FileFormat format, XMP_Uns64 fileSize, nextOffset; ISOMedia::BoxInfo currBox; - #define IsTolerableBoxChar(ch) ( ((0x20 <= (ch)) && ((ch) <= 0x7E)) || ((ch) == 0xA9) ) - XMP_AbortProc abortProc = parent->abortProc; void * abortArg = parent->abortArg; const bool checkAbort = (abortProc != 0); @@ -256,14 +280,7 @@ bool MPEG4_CheckFormat ( XMP_FileFormat format, while ( currBox.boxType != ISOMedia::k_moov ) { if ( ! IsClassicQuickTimeBox ( currBox.boxType ) ) { - // Make sure the box type is 4 ASCII characters or 0xA9 (MacRoman copyright). - XMP_Uns8 b1 = (XMP_Uns8) (currBox.boxType >> 24); - XMP_Uns8 b2 = (XMP_Uns8) ((currBox.boxType >> 16) & 0xFF); - XMP_Uns8 b3 = (XMP_Uns8) ((currBox.boxType >> 8) & 0xFF); - XMP_Uns8 b4 = (XMP_Uns8) (currBox.boxType & 0xFF); - bool ok = IsTolerableBoxChar(b1) && IsTolerableBoxChar(b2) && - IsTolerableBoxChar(b3) && IsTolerableBoxChar(b4); - if ( ! ok ) return false; + if ( ! IsTolerableBox(currBox.boxType) ) return false; } if ( nextOffset >= fileSize ) return false; if ( checkAbort && abortProc(abortArg) ) { @@ -1721,7 +1738,6 @@ static QTErrorMode CheckAtomList ( XMP_IO* qtFile, XMP_Int64 spanSize, int nesti if ( spanSize != 0 ) { qtFile->Seek ( spanSize, kXMP_SeekFromCurrent ); // ! Skip the trailing garbage of this span. status = kBadQT_SmallInner; - if ( spanSize >= 8 ) status = kBadQT_LargeInner; if ( nesting == 0 ) status += 2; // Convert to "outer". } @@ -1733,16 +1749,28 @@ static QTErrorMode CheckAtomList ( XMP_IO* qtFile, XMP_Int64 spanSize, int nesti // AttemptFileRepair // ================= -static void AttemptFileRepair ( XMP_IO* qtFile, XMP_Int64 fileSpace, QTErrorMode status ) +static void AttemptFileRepair ( XMP_IO* qtFile, XMP_Int64 fileSpace, QTErrorMode status, GenericErrorCallback * ec ) { switch ( status ) { case kBadQT_NoError : return; // Sanity check. case kBadQT_SmallInner : return; // Fixed in normal update code for the 'udta' box. - case kBadQT_LargeInner : XMP_Throw ( "Can't repair QuickTime file", kXMPErr_BadFileFormat ); - case kBadQT_SmallOuter : break; // Truncate file below. - case kBadQT_LargeOuter : break; // Truncate file below. - default : XMP_Throw ( "Invalid QuickTime error mode", kXMPErr_InternalFailure ); + case kBadQT_LargeInner : + { + XMP_Error error ( kXMPErr_BadFileFormat,"Can't repair QuickTime file" ); + XMPFileHandler::NotifyClient(ec, kXMPErrSev_FileFatal, error); + break;// will never be here + } + case kBadQT_SmallOuter : // Truncate file below. + case kBadQT_LargeOuter : // Truncate file below. + { + break; + } + default : + { + XMP_Error error ( kXMPErr_InternalFailure, "Invalid QuickTime error mode" ); + XMPFileHandler::NotifyClient(ec, kXMPErrSev_FileFatal, error); + } } AtomInfo info; @@ -1760,19 +1788,37 @@ static void AttemptFileRepair ( XMP_IO* qtFile, XMP_Int64 fileSpace, QTErrorMode if ( info.hasLargeSize ) headerSize = 16; if ( atomStatus != kBadQT_NoError ) break; - if ( (info.atomSize < headerSize) || (info.atomSize > fileSpace) ) break; + // If the atom size is less than header -case of kBadQT_SmallOuter + // If the atom size is more than left filespace -case of kBadQT_LargeOuter + if ( (info.atomSize < headerSize) || (info.atomSize > fileSpace ) ) break; XMP_Int64 dataSize = info.atomSize - headerSize; qtFile->Seek ( dataSize, kXMP_SeekFromCurrent ); } + // Truncate only if the last box type was XMP boxes + // Refrain from truncating a known box as it + // might have some useful data which is incomplete + if ( fileSpace < 8 || + ! ISOMedia::IsKnownBoxType ( info.atomType ) || + ((info.atomType == ISOMedia::k_uuid) && IsXMPUUID(qtFile,info.atomSize-headerSize,true)) || + (info.atomType == ISOMedia::k_XMP_) + ){ + + XMP_Error error ( kXMPErr_BadFileFormat,"Truncate outer EOF Garbage" ); + XMPFileHandler::NotifyClient(ec, kXMPErrSev_Recoverable, error); + // Truncate the file. If fileSpace >= 8 then the loop exited early due to a bad atom, seek back + // to the atom's start. Otherwise, the loop exited because no more atoms are possible, no seek. - // Truncate the file. If fileSpace >= 8 then the loop exited early due to a bad atom, seek back - // to the atom's start. Otherwise, the loop exited because no more atoms are possible, no seek. + if ( fileSpace >= 8 ) qtFile->Seek ( -headerSize, kXMP_SeekFromCurrent ); + XMP_Int64 currPos = qtFile->Offset(); + qtFile->Truncate ( currPos ); + } + else{ - if ( fileSpace >= 8 ) qtFile->Seek ( -headerSize, kXMP_SeekFromCurrent ); - XMP_Int64 currPos = qtFile->Offset(); - qtFile->Truncate ( currPos ); + XMP_Error error ( kXMPErr_BadFileFormat,"Missing box Data at EOF" ); + XMPFileHandler::NotifyClient(ec, kXMPErrSev_FileFatal, error); + } } // AttemptFileRepair @@ -1780,7 +1826,7 @@ static void AttemptFileRepair ( XMP_IO* qtFile, XMP_Int64 fileSpace, QTErrorMode // CheckQTFileStructure // ==================== -static void CheckQTFileStructure ( XMPFileHandler * thiz, bool doRepair ) +static void CheckQTFileStructure ( XMPFileHandler * thiz,bool doRepair, GenericErrorCallback * ec ) { XMPFiles * parent = thiz->parent; XMP_IO* fileRef = parent->ioRef; @@ -1793,13 +1839,13 @@ static void CheckQTFileStructure ( XMPFileHandler * thiz, bool doRepair ) if ( status != kBadQT_NoError ) { if ( doRepair || (status == kBadQT_SmallInner) || (status == kBadQT_SmallOuter) ) { - AttemptFileRepair ( fileRef, fileSize, status ); // Will throw if the attempt fails. + AttemptFileRepair ( fileRef, fileSize, status,ec ); // Will throw if the attempt fails. } else if ( status != kBadQT_SmallInner ) { - XMP_Throw ( "Ill-formed QuickTime file", kXMPErr_BadFileFormat ); - } else { - return; // ! Ignore these, QT seems to be able to handle them. - // *** Might want to throw for check-only, ignore when repairing. - } + //don't truncate Large Outer EOF garbage unless the client wants it to + // Clients can pass their intent by setting the flag kXMPFiles_OpenRepairFile + XMP_Error error ( kXMPErr_BadFileFormat,"Ill-formed QuickTime file" ); + XMPFileHandler::NotifyClient(ec, kXMPErrSev_FileFatal, error); + } } } // CheckQTFileStructure; @@ -1946,7 +1992,7 @@ typedef std::vector<SpaceInfo> FreeSpaceList; static void CreateFreeSpaceList ( XMP_IO* fileRef, XMP_Uns64 fileSize, XMP_Uns64 oldOffset, XMP_Uns32 oldSize, FreeSpaceList * spaceList ) { - XMP_Uns64 boxPos, boxNext, adjacentFree; + XMP_Uns64 boxPos=0, boxNext=0, adjacentFree=0; ISOMedia::BoxInfo currBox; fileRef->Rewind(); @@ -2002,8 +2048,8 @@ void MPEG4_MetaHandler::CacheFileData() const bool isUpdate = XMP_OptionIsSet ( openFlags, kXMPFiles_OpenForUpdate ); const bool doRepair = XMP_OptionIsSet ( openFlags, kXMPFiles_OpenRepairFile ); - if ( isUpdate && (parent->format == kXMP_MOVFile) ) { - CheckQTFileStructure ( this, doRepair ); // Will throw for failure. + if ( isUpdate ) { + CheckQTFileStructure ( this, doRepair, &parent->errorCallback ); // Will throw for failure. } // Cache the top level 'moov' and 'uuid'/XMP boxes. @@ -2045,13 +2091,7 @@ void MPEG4_MetaHandler::CacheFileData() moovFound = true; if ( uuidFound ) break; // Exit the loop when both are found. - } else if ( (! uuidFound) && (currBox.boxType == ISOMedia::k_uuid) ) { - - if ( currBox.contentSize < 16 ) continue; - - XMP_Uns8 uuid [16]; - fileRef->ReadAll ( uuid, 16 ); - if ( memcmp ( uuid, ISOMedia::k_xmpUUID, 16 ) != 0 ) continue; // Check for the XMP GUID. + } else if ( (! uuidFound) && (currBox.boxType == ISOMedia::k_uuid) && IsXMPUUID(fileRef,currBox.contentSize) ) { XMP_Uns64 fullUuidSize = currBox.headerSize + currBox.contentSize; if ( fullUuidSize > moovBoxSizeLimit ) { // From here on we know 32-bit offsets are safe. @@ -2073,7 +2113,10 @@ void MPEG4_MetaHandler::CacheFileData() } - if ( (! moovFound) && (! moovIgnored) ) XMP_Throw ( "No 'moov' box", kXMPErr_BadFileFormat ); + if ( (! moovFound) && (! moovIgnored) ){ + XMP_Error error ( kXMPErr_BadFileFormat,"No 'moov' box" ); + XMPFileHandler::NotifyClient(&parent->errorCallback, kXMPErrSev_FileFatal, error); + } } // MPEG4_MetaHandler::CacheFileData @@ -2110,7 +2153,10 @@ void MPEG4_MetaHandler::ProcessXMP() // Parse the cached 'moov' subtree, parse the preferred XMP. - if ( this->moovMgr.fullSubtree.empty() ) XMP_Throw ( "No 'moov' box", kXMPErr_BadFileFormat ); + if ( this->moovMgr.fullSubtree.empty() ) { + XMP_Error error ( kXMPErr_BadFileFormat,"No 'moov' box" ); + XMPFileHandler::NotifyClient(&parent->errorCallback, kXMPErrSev_FileFatal, error); + } this->moovMgr.ParseMemoryTree ( this->fileMode ); if ( (this->xmpBoxPos == 0) || (! haveISOFile) ) { @@ -2168,6 +2214,7 @@ void MPEG4_MetaHandler::ProcessXMP() if ( mvhdFound ) this->containsXMP |= ImportMVHDItems ( mvhdInfo, &this->xmpObj ); if ( cprtFound ) this->containsXMP |= ImportISOCopyrights ( cprtBoxes, &this->xmpObj ); + if ( tmcdFound ) this->containsXMP |= ImportTimecodeItems ( this->tmcdInfo, this->tradQTMgr, &this->xmpObj ); } else { // This is a QuickTime file, either traditional or modern. if ( mvhdFound ) this->containsXMP |= ImportMVHDItems ( mvhdInfo, &this->xmpObj ); @@ -2251,11 +2298,16 @@ bool MPEG4_MetaHandler::ParseTimecodeTrack() XMP_Uns32 stsdEntryFormat = GetUns32BE ( &stsdRawEntry->format ); if ( stsdEntryFormat != ISOMedia::k_tmcd ) return false; + // If frame duration is zero it means tmcd sample is invalid + if(GetUns32BE(&stsdRawEntry->frameDuration)==0) + return false; + this->tmcdInfo.timeScale = GetUns32BE ( &stsdRawEntry->timeScale ); this->tmcdInfo.frameDuration = GetUns32BE ( &stsdRawEntry->frameDuration ); double floatCount = (double)this->tmcdInfo.timeScale / (double)this->tmcdInfo.frameDuration; XMP_Uns8 expectedCount = (XMP_Uns8) (floatCount + 0.5); + if( expectedCount == 0 ) return false; if ( expectedCount != stsdRawEntry->frameCount ) { double countRatio = (double)stsdRawEntry->frameCount / (double)expectedCount; this->tmcdInfo.timeScale = (XMP_Uns32) (((double)this->tmcdInfo.timeScale * countRatio) + 0.5); @@ -2541,6 +2593,305 @@ void MPEG4_MetaHandler::UpdateTopLevelBox ( XMP_Uns64 oldOffset, XMP_Uns32 oldSi } // MPEG4_MetaHandler::UpdateTopLevelBox // ================================================================================================= +// AdjustOffset +// ============ +// +// A utility for OptimizeFileLayout, adjusts a 'stco' or 'co64' table entry for the new layout. The +// map is keyed by the original box's last content offset, so that map.lower_bound does what we want. + +struct LayoutInfo { + XMP_Uns32 boxType; + XMP_Uns64 boxSize; // The full size, including the header. + XMP_Uns64 oldOffset, newOffset; + LayoutInfo() : boxType(0), boxSize(0), oldOffset(0), newOffset(0) {}; + LayoutInfo ( XMP_Uns32 type, XMP_Uns64 size, XMP_Uns64 offset ) + : boxType(type), boxSize(size), oldOffset(offset), newOffset(0) {}; +}; + +typedef std::vector < LayoutInfo > LayoutVector; +typedef std::map < XMP_Uns64, LayoutInfo* > LayoutMap; + +static XMP_Uns64 AdjustOffset ( XMP_Uns64 oldOffset, const LayoutMap & newMap , GenericErrorCallback * ec) +{ + + LayoutMap::const_iterator mapEntry = newMap.lower_bound ( oldOffset ); + if ( (mapEntry == newMap.end()) || (oldOffset < mapEntry->second->oldOffset) ) { + XMP_Error error ( kXMPErr_BadFileFormat,"Offset from 'stco' or 'co64' is not into kept box" ); + XMPFileHandler::NotifyClient(ec, kXMPErrSev_FileFatal, error); + } + + XMP_Assert ( (mapEntry->second->oldOffset <= oldOffset) && + (oldOffset <= (mapEntry->second->oldOffset + mapEntry->second->boxSize)) ); + + return mapEntry->second->newOffset + (oldOffset - mapEntry->second->oldOffset); + +} // AdjustOffset + +// ================================================================================================= +// MPEG4_MetaHandler::OptimizeFileLayout +// ===================================== +// +// Make sure the file is acceptable for streaming use: the 'moov' and XMP 'uuid' boxes must be +// before any 'mdat' box, other top level boxes after 'mdat' are accepted. If the file needs +// optimization, it is fully rewritten in this order: 'ftyp' (if ISO), 'moov', XMP 'uuid', other +// non-'mdat', all 'mdat' boxes. Top level 'free' and 'skip' boxes will be removed. Offsets in the +// 'stco' and 'co64' boxes will be adjusted. + +void MPEG4_MetaHandler::OptimizeFileLayout() +{ + XMP_IO* originalFile = this->parent->ioRef; + XMP_Uns64 originalSize = originalFile->Length(); + + XMP_AbortProc abortProc = parent->abortProc; + void * abortArg = parent->abortArg; + const bool checkAbort = (abortProc != 0); + + XMP_Uns64 currPos, nextPos; + ISOMedia::BoxInfo currBox; + + size_t boxCount = 0; + size_t moovIndex = 0, xmpIndex = 0; + + // Go through the top level boxes to see if the file layout needs to be optimized. Look until + // we find both the 'moov' and XMP 'uuid' boxes, saving their relative index in the file. + + bool needsOptimization = false; + bool moovFound = false, xmpFound = false, mdatFound = false; + + for ( currPos = 0; currPos < originalSize; currPos = nextPos ) { + + nextPos = ISOMedia::GetBoxInfo ( originalFile, currPos, originalSize, &currBox ); + if ( (currBox.boxType == ISOMedia::k_free) || + (currBox.boxType == ISOMedia::k_skip) || + (currBox.boxType == ISOMedia::k_wide) ) continue; + + ++boxCount; // ! Must be counted for all, continue statements below skip an end of loop increment. + + if ( currBox.boxType == ISOMedia::k_mdat ) { + + mdatFound = true; + XMP_Assert ( (! moovFound) | (! xmpFound) ); // The other cases should be exiting. + + } else if ( currBox.boxType == ISOMedia::k_moov ) { + + moovFound = true; + moovIndex = boxCount-1; // Need later for optimization. + needsOptimization = mdatFound; + if ( xmpFound ) break; // Don't need to look further. + + } else if ( currBox.boxType == ISOMedia::k_uuid && IsXMPUUID(originalFile,currBox.contentSize) ) { + + xmpFound = true; + xmpIndex = boxCount-1; // Need later for optimization. + needsOptimization = mdatFound; + if ( moovFound ) break; // Don't need to look further. + + } + + } + + if ( ! needsOptimization ) return; + + // The file needs to be optimized. Make sure that a file over 4 GB has 'co64', not 'stco' boxes. + // These are needed to hold 64-bit offsets. We don't go to the effort of changing from 'stco' + // to 'co64', the file needs to be OK from the start. (Yes, this eliminates a marginal case of + // a file growing beyond 4 GB due to metadata growth.) + + if ( originalSize >= 0xFFFFFFFF ) { + + MOOV_Manager::BoxRef moovRef, trakRef, tempRef, stcoRef; + MOOV_Manager::BoxInfo boxInfo; + + moovRef = this->moovMgr.GetBox ( "moov", &boxInfo ); + XMP_Enforce ( moovRef != 0 ); + + for ( size_t i = 0, limit = boxInfo.childCount; i < limit; ++i ) { + + trakRef = this->moovMgr.GetNthChild ( moovRef, i, &boxInfo ); + if ( boxInfo.boxType != ISOMedia::k_trak ) continue; + + tempRef = this->moovMgr.GetTypeChild ( trakRef, ISOMedia::k_mdia, 0 ); + if ( tempRef == 0 ) continue; + tempRef = this->moovMgr.GetTypeChild ( tempRef, ISOMedia::k_minf, 0 ); + if ( tempRef == 0 ) continue; + tempRef = this->moovMgr.GetTypeChild ( tempRef, ISOMedia::k_stbl, 0 ); + if ( tempRef == 0 ) continue; + + stcoRef = this->moovMgr.GetTypeChild ( tempRef, ISOMedia::k_stco, 0 ); + if ( stcoRef != 0 ) { + XMP_Error error ( kXMPErr_BadFileFormat,"Large MPEG-4 file must use 'co64' boxes" ); + XMPFileHandler::NotifyClient(&parent->errorCallback, kXMPErrSev_FileFatal, error); + } + + } + + } + + // Build a vector of info for the top level boxes, ignoring 'free', 'skip', and 'wide' boxes. + // Then determine the new offsets and create a map keyed by the new offset. + + // ! The box indices saved in the prior loop must match those in the vector built here! + + LayoutVector fileBoxes; + LayoutMap optLayout; + + for ( currPos = 0; currPos < originalSize; currPos = nextPos ) { + nextPos = ISOMedia::GetBoxInfo ( originalFile, currPos, originalSize, &currBox ); + if ( (currBox.boxType == ISOMedia::k_free) || + (currBox.boxType == ISOMedia::k_skip) || + (currBox.boxType == ISOMedia::k_wide) ) continue; + --boxCount; // For sanity check below. + fileBoxes.push_back ( LayoutInfo ( currBox.boxType, (currBox.headerSize + currBox.contentSize), currPos ) ); + } + + XMP_Assert ( boxCount == 0 ); // Must get the same count in both loops. + XMP_Assert ( fileBoxes.size() >= 2 ); // At least 'mdat', and 'moov' or XMP 'uuid'. + XMP_Assert ( (!moovFound) || (fileBoxes[moovIndex].boxType == ISOMedia::k_moov) ); + XMP_Assert ( (!xmpFound) || (fileBoxes[xmpIndex].boxType == ISOMedia::k_uuid) ); + + size_t currIndex = 0, limit = fileBoxes.size(); + XMP_Uns64 newSize = 0; + + if ( fileBoxes[0].boxType == ISOMedia::k_ftyp ) { + optLayout.insert ( optLayout.end(), LayoutMap::value_type ( 0, &fileBoxes[0] ) ); + newSize = fileBoxes[0].boxSize; + currIndex = 1; // Keep the 'ftyp' box in front. + } + + if ( moovFound ) { + optLayout.insert ( optLayout.end(), LayoutMap::value_type ( newSize, &fileBoxes[moovIndex] ) ); + fileBoxes[moovIndex].newOffset = newSize; + newSize += fileBoxes[moovIndex].boxSize; + } + + if ( xmpFound ) { + optLayout.insert ( optLayout.end(), LayoutMap::value_type ( newSize, &fileBoxes[xmpIndex] ) ); + fileBoxes[xmpIndex].newOffset = newSize; + newSize += fileBoxes[xmpIndex].boxSize; + } + + for ( ; currIndex < limit; ++currIndex ) { // Add all of the other non-'mdat' boxes to the map. + if ( moovFound && (currIndex == moovIndex) ) continue; + if ( xmpFound && (currIndex == xmpIndex) ) continue; + if ( fileBoxes[currIndex].boxType == ISOMedia::k_mdat ) continue; + optLayout.insert ( optLayout.end(), LayoutMap::value_type ( newSize, &fileBoxes[currIndex] ) ); + fileBoxes[currIndex].newOffset = newSize; + newSize += fileBoxes[currIndex].boxSize; + } + + for ( currIndex = 0; currIndex < limit; ++currIndex ) { // Add all of the 'mdat' boxes to the map. + if ( fileBoxes[currIndex].boxType != ISOMedia::k_mdat ) continue; + optLayout.insert ( optLayout.end(), LayoutMap::value_type ( newSize, &fileBoxes[currIndex] ) ); + fileBoxes[currIndex].newOffset = newSize; + newSize += fileBoxes[currIndex].boxSize; + } + + // Adjust the progress tracking if necessary. + + XMP_ProgressTracker * progressTracker = this->parent->progressTracker; + if ( progressTracker != 0 ) { + XMP_Assert ( progressTracker->WorkInProgress() ); + progressTracker->AddTotalWork ( (float) newSize ); + } + + // Create a temp file for the optimized layout, write it, update the offset tables. + + XMP_IO* tempFile = originalFile->DeriveTemp(); + XMP_Enforce ( tempFile != 0 ); + + // Iterate the map and write the new layout. + + LayoutMap::iterator layoutPos = optLayout.begin(); + LayoutMap::iterator layoutEnd = optLayout.end(); + + for ( ; layoutPos != layoutEnd; ++layoutPos ) { + LayoutInfo * currBox = layoutPos->second; + XMP_Assert ( (XMP_Int64)currBox->newOffset == tempFile->Length() ); + originalFile->Seek ( currBox->oldOffset, kXMP_SeekFromStart ); + XIO::Copy ( originalFile, tempFile, currBox->boxSize, abortProc, abortArg ); + } + + // Update the offset tables in the temp file. Create a layout map ordered by the last actual + // offset of the old box's content to enable fast lookup within AdjustOffset. + + LayoutMap oldEndMap; + for ( size_t i = 0, limit = fileBoxes.size(); i < limit; ++i ) { + XMP_Uns64 oldEnd = fileBoxes[i].oldOffset + fileBoxes[i].boxSize - 1; // ! Want the last actual offset! + oldEndMap.insert ( oldEndMap.end(), LayoutMap::value_type ( oldEnd, &fileBoxes[i] ) ); + } + + MOOV_Manager::BoxRef moovRef, trakRef, tempRef, stcoRef, co64Ref; + MOOV_Manager::BoxInfo boxInfo; + + moovRef = this->moovMgr.GetBox ( "moov", &boxInfo ); + XMP_Enforce ( moovRef != 0 ); + + for ( size_t i = 0, limit = boxInfo.childCount; i < limit; ++i ) { + + trakRef = this->moovMgr.GetNthChild ( moovRef, i, &boxInfo ); + if ( boxInfo.boxType != ISOMedia::k_trak ) continue; + + tempRef = this->moovMgr.GetTypeChild ( trakRef, ISOMedia::k_mdia, 0 ); + if ( tempRef == 0 ) continue; + tempRef = this->moovMgr.GetTypeChild ( tempRef, ISOMedia::k_minf, 0 ); + if ( tempRef == 0 ) continue; + tempRef = this->moovMgr.GetTypeChild ( tempRef, ISOMedia::k_stbl, 0 ); + if ( tempRef == 0 ) continue; + + co64Ref = 0; + XMP_Uns32 entrySize = 4; + stcoRef = this->moovMgr.GetTypeChild ( tempRef, ISOMedia::k_stco, &boxInfo ); + if ( stcoRef == 0 ) { + co64Ref = this->moovMgr.GetTypeChild ( tempRef, ISOMedia::k_co64, &boxInfo ); + if ( co64Ref == 0 ) continue; + entrySize = 8; + } + + XMP_Uns32 offsetCount = GetUns32BE ( boxInfo.content + 4 ); + if ( boxInfo.contentSize < (4+4 + entrySize*offsetCount) ) { + XMP_Error error ( kXMPErr_BadFileFormat, "Bad 'stco' size or count" ); + XMPFileHandler::NotifyClient(&parent->errorCallback, kXMPErrSev_FileFatal, error); + } + + if ( stcoRef != 0 ) { + + XMP_Uns64 stcoTableOffset = fileBoxes[moovIndex].newOffset + + (XMP_Uns64) this->moovMgr.GetParsedOffset ( stcoRef ) + + (XMP_Uns64) this->moovMgr.GetHeaderSize ( stcoRef ) + 4+4; + tempFile->Seek ( stcoTableOffset, kXMP_SeekFromStart ); + + XMP_Uns32 * rawOldU32 = (XMP_Uns32*) (boxInfo.content + 4+4); + for ( XMP_Uns32 i = 0; i < offsetCount; ++i, ++rawOldU32 ) { + XMP_Uns64 newOffset = AdjustOffset ( (XMP_Uns64)GetUns32BE(rawOldU32), oldEndMap,&parent->errorCallback ); + XMP_Uns32 u32 = MakeUns32BE ( (XMP_Uns32)newOffset ); + tempFile->Write ( &u32, 4 ); + } + + } else { + + XMP_Uns64 co64TableOffset = fileBoxes[moovIndex].newOffset + + (XMP_Uns64) this->moovMgr.GetParsedOffset ( co64Ref ) + + (XMP_Uns64) this->moovMgr.GetHeaderSize ( co64Ref ) + 4+4; + tempFile->Seek ( co64TableOffset, kXMP_SeekFromStart ); + + XMP_Uns64 * rawOldU64 = (XMP_Uns64*) (boxInfo.content + 4+4); + for ( XMP_Uns32 i = 0; i < offsetCount; ++i, ++rawOldU64 ) { + XMP_Uns64 newOffset = AdjustOffset ( GetUns64BE(rawOldU64), oldEndMap,&parent->errorCallback ); + XMP_Uns64 u64 = MakeUns64BE ( newOffset ); + tempFile->Write ( &u64, 8 ); + } + + } + + } + + // Swap the temp and original files. + + originalFile->AbsorbTemp(); + +} // MPEG4_MetaHandler::OptimizeFileLayout + +// ================================================================================================= // MPEG4_MetaHandler::UpdateFile // ============================= // @@ -2553,7 +2904,15 @@ void MPEG4_MetaHandler::UpdateTopLevelBox ( XMP_Uns64 oldOffset, XMP_Uns32 oldSi void MPEG4_MetaHandler::UpdateFile ( bool doSafeUpdate ) { + + bool optimizeFileLayout = false; + if ( this->parent) + { + optimizeFileLayout = XMP_OptionIsSet ( this->parent->openFlags, kXMPFiles_OptimizeFileLayout ); + } + if ( ! this->needsUpdate ) { // If needsUpdate is set then at least the XMP changed. + if ( optimizeFileLayout ) this->OptimizeFileLayout(); return; } @@ -2660,6 +3019,9 @@ void MPEG4_MetaHandler::UpdateFile ( bool doSafeUpdate ) } + // Finally, optimize the file layout if asked. + if ( optimizeFileLayout ) this->OptimizeFileLayout(); + if ( localProgressTracking ) progressTracker->WorkComplete(); } // MPEG4_MetaHandler::UpdateFile @@ -2698,4 +3060,4 @@ void MPEG4_MetaHandler::WriteTempFile ( XMP_IO* tempRef ) } // MPEG4_MetaHandler::WriteTempFile -// ================================================================================================= +// =================================================================================================
\ No newline at end of file diff --git a/XMPFiles/source/FileHandlers/MPEG4_Handler.hpp b/XMPFiles/source/FileHandlers/MPEG4_Handler.hpp index 49c749f..9ad06bb 100644 --- a/XMPFiles/source/FileHandlers/MPEG4_Handler.hpp +++ b/XMPFiles/source/FileHandlers/MPEG4_Handler.hpp @@ -78,6 +78,8 @@ private: void UpdateTopLevelBox ( XMP_Uns64 oldOffset, XMP_Uns32 oldSize, const XMP_Uns8 * newBox, XMP_Uns32 newSize ); + void OptimizeFileLayout(); + XMP_Uns8 fileMode; bool havePreferredXMP; XMP_Uns64 xmpBoxPos; // The file offset of the XMP box (the size field, not the content). diff --git a/XMPFiles/source/FileHandlers/P2_Handler.cpp b/XMPFiles/source/FileHandlers/P2_Handler.cpp index 8e692e9..4a2e508 100644 --- a/XMPFiles/source/FileHandlers/P2_Handler.cpp +++ b/XMPFiles/source/FileHandlers/P2_Handler.cpp @@ -18,10 +18,11 @@ #include "source/IOUtils.hpp" #include "XMPFiles/source/FileHandlers/P2_Handler.hpp" +#include "XMPFiles/source/FormatSupport/P2_Support.hpp" #include "XMPFiles/source/FormatSupport/PackageFormat_Support.hpp" -#include "third-party/zuid/interfaces/MD5.h" #include <cmath> +#include <sstream> using namespace std; @@ -263,7 +264,7 @@ XMPFileHandler * P2_MetaHandlerCTor ( XMPFiles * parent ) // P2_MetaHandler::P2_MetaHandler // ============================== -P2_MetaHandler::P2_MetaHandler ( XMPFiles * _parent ) : expat(0), clipMetadata(0), clipContent(0) +P2_MetaHandler::P2_MetaHandler ( XMPFiles * _parent ) { this->parent = _parent; // Inherited, can't set in the prefix. @@ -283,6 +284,30 @@ P2_MetaHandler::P2_MetaHandler ( XMPFiles * _parent ) : expat(0), clipMetadata(0 XIO::SplitLeafName ( &this->rootPath, &this->clipName ); + std::string xmlPath; + if ( this->MakeClipFilePath ( &xmlPath, ".XML", true ) ) + { + try + { + + p2ClipManager.ProcessClip(xmlPath); + std::string* clipnm = p2ClipManager.GetManagedClip()->GetClipName(); + if ( clipnm !=0 ) + { + std::string newpath,leafname; + newpath = p2ClipManager.GetManagedClip()->GetXMPFilePath(); + XIO::SplitLeafName(&newpath,&leafname); + if ( leafname == std::string(*clipnm+ ".XMP") ) + { + this->clipName=*clipnm; + } + } + } + catch(...) + { + } + } + } // P2_MetaHandler::P2_MetaHandler // ================================================================================================= @@ -292,7 +317,6 @@ P2_MetaHandler::P2_MetaHandler ( XMPFiles * _parent ) : expat(0), clipMetadata(0 P2_MetaHandler::~P2_MetaHandler() { - this->CleanupLegacyXML(); if ( this->parent->tempPtr != 0 ) { free ( this->parent->tempPtr ); this->parent->tempPtr = 0; @@ -314,79 +338,36 @@ bool P2_MetaHandler::MakeClipFilePath ( std::string * path, XMP_StringPtr suffix } // P2_MetaHandler::MakeClipFilePath -// ================================================================================================= -// P2_MetaHandler::CleanupLegacyXML -// ================================ - -void P2_MetaHandler::CleanupLegacyXML() -{ - if ( this->expat != 0 ) { delete ( this->expat ); this->expat = 0; } - - clipMetadata = 0; // ! Was a pointer into the expat tree. - clipContent = 0; // ! Was a pointer into the expat tree. - -} // P2_MetaHandler::CleanupLegacyXML // ================================================================================================= -// P2_MetaHandler::DigestLegacyItem -// ================================ - -void P2_MetaHandler::DigestLegacyItem ( MD5_CTX & md5Context, XML_NodePtr legacyContext, XMP_StringPtr legacyPropName ) -{ - XML_NodePtr legacyProp = legacyContext->GetNamedElement ( this->p2NS.c_str(), legacyPropName ); - - if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() && (! legacyProp->content.empty()) ) { - const XML_Node * xmlValue = legacyProp->content[0]; - MD5Update ( &md5Context, (XMP_Uns8*)xmlValue->value.c_str(), (unsigned int)xmlValue->value.size() ); - } - -} // P2_MetaHandler::DigestLegacyItem - -// ================================================================================================= -// P2_MetaHandler::DigestLegacyRelations -// ===================================== +// P2_MetaHandler::SetXMPPropertyFromLegacyXML +// =========================================== -void P2_MetaHandler::DigestLegacyRelations ( MD5_CTX & md5Context ) +void P2_MetaHandler::SetXMPPropertyFromLegacyXML ( bool digestFound, + XMP_VarString* refContext, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + bool isLocalized ) { - XMP_StringPtr p2NS = this->p2NS.c_str(); - XML_Node * legacyContext = this->clipContent->GetNamedElement ( p2NS, "Relation" ); - if ( legacyContext != 0 ) { - - this->DigestLegacyItem ( md5Context, legacyContext, "GlobalShotID" ); - XML_Node * legacyConnectionContext = legacyContext = this->clipContent->GetNamedElement ( p2NS, "Connection" ); - - if ( legacyConnectionContext != 0 ) { - - legacyContext = legacyConnectionContext->GetNamedElement ( p2NS, "Top" ); - - if ( legacyContext != 0 ) { - this->DigestLegacyItem ( md5Context, legacyContext, "GlobalClipID" ); - } - - legacyContext = legacyConnectionContext->GetNamedElement ( p2NS, "Previous" ); - - if ( legacyContext != 0 ) { - this->DigestLegacyItem ( md5Context, legacyContext, "GlobalClipID" ); - } - - legacyContext = legacyConnectionContext->GetNamedElement ( p2NS, "Next" ); + if ( digestFound || (! this->xmpObj.DoesPropertyExist ( schemaNS, propName )) ) { - if ( legacyContext != 0 ) { - this->DigestLegacyItem ( md5Context, legacyContext, "GlobalClipID" ); + if ( refContext !=0 ) + { + if ( isLocalized ) { + this->xmpObj.SetLocalizedText ( schemaNS, propName, "", "x-default", refContext->c_str(), kXMP_DeleteExisting ); + } else { + this->xmpObj.SetProperty ( schemaNS, propName, refContext->c_str(), kXMP_DeleteExisting ); } - + this->containsXMP = true; } - } - -} // P2_MetaHandler::DigestLegacyRelations +} // P2_MetaHandler::SetXMPPropertyFromLegacyXML // ================================================================================================= // P2_MetaHandler::SetXMPPropertyFromLegacyXML // =========================================== - void P2_MetaHandler::SetXMPPropertyFromLegacyXML ( bool digestFound, XML_NodePtr legacyContext, XMP_StringPtr schemaNS, @@ -394,89 +375,58 @@ void P2_MetaHandler::SetXMPPropertyFromLegacyXML ( bool digestFound, XMP_StringPtr legacyPropName, bool isLocalized ) { + XMP_StringPtr p2NS = this->p2ClipManager.GetManagedClip()->GetP2RootNode()->ns.c_str(); + XML_NodePtr legacyProp = legacyContext->GetNamedElement ( p2NS, legacyPropName ); - if ( digestFound || (! this->xmpObj.DoesPropertyExist ( schemaNS, propName )) ) { - - XMP_StringPtr p2NS = this->p2NS.c_str(); - XML_NodePtr legacyProp = legacyContext->GetNamedElement ( p2NS, legacyPropName ); + if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() ) { + XMP_StringPtr legacyValue = legacyProp->GetLeafContentValue(); - if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() ) { + if ( ( legacyValue != 0 ) && + ( ( *legacyValue != 0 ) || (! this->xmpObj.DoesPropertyExist ( schemaNS, propName )) )) { if ( isLocalized ) { - this->xmpObj.SetLocalizedText ( schemaNS, propName, "", "x-default", legacyProp->GetLeafContentValue(), kXMP_DeleteExisting ); + this->xmpObj.SetLocalizedText ( schemaNS, propName, "", "x-default", legacyValue, kXMP_DeleteExisting ); } else { - this->xmpObj.SetProperty ( schemaNS, propName, legacyProp->GetLeafContentValue(), kXMP_DeleteExisting ); + this->xmpObj.SetProperty ( schemaNS, propName, legacyValue, kXMP_DeleteExisting ); } this->containsXMP = true; } - } -} // P2_MetaHandler::SetXMPPropertyFromLegacyXML - +} // ================================================================================================= // P2_MetaHandler::SetRelationsFromLegacyXML // ========================================= void P2_MetaHandler::SetRelationsFromLegacyXML ( bool digestFound ) { - XMP_StringPtr p2NS = this->p2NS.c_str(); - XML_NodePtr legacyRelationContext = this->clipContent->GetNamedElement ( p2NS, "Relation" ); - - // P2 Relation blocks are optional -- they're only present when a clip is part of a multi-clip shot. - if ( legacyRelationContext != 0 ) { + P2_Clip* p2Clip=this->p2ClipManager.GetManagedClip(); - if ( digestFound || (! this->xmpObj.DoesPropertyExist ( kXMP_NS_DC, "relation" )) ) { - - XML_NodePtr legacyProp = legacyRelationContext->GetNamedElement ( p2NS, "GlobalShotID" ); - std::string relationString; - - if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() ) { + if ( digestFound || (! this->xmpObj.DoesPropertyExist ( kXMP_NS_DC, "relation" )) ) { + + XMP_VarString* globalShotId = p2Clip->GetShotId() ; + std::string relationString ; + if ( ( globalShotId != 0 ) ) { - this->xmpObj.DeleteProperty ( kXMP_NS_DC, "relation" ); - relationString = std::string("globalShotID:") + legacyProp->GetLeafContentValue(); + this->xmpObj.DeleteProperty ( kXMP_NS_DC, "relation" ); + relationString = std::string("globalShotID:") + *globalShotId ; + this->xmpObj.AppendArrayItem ( kXMP_NS_DC, "relation", kXMP_PropArrayIsUnordered, relationString ); + this->containsXMP = true; + + XMP_VarString* topId = p2Clip->GetTopClipId() ; + if ( topId != 0 ) { + relationString = std::string("topGlobalClipID:") + *topId ; + this->xmpObj.AppendArrayItem ( kXMP_NS_DC, "relation", kXMP_PropArrayIsUnordered, relationString ); + } + XMP_VarString* prevId = p2Clip->GetPreviousClipId() ; + if ( prevId != 0 ) { + relationString = std::string("previousGlobalClipID:") + *prevId ; + this->xmpObj.AppendArrayItem ( kXMP_NS_DC, "relation", kXMP_PropArrayIsUnordered, relationString ); + } + XMP_VarString* nextId = p2Clip->GetNextClipId() ; + if ( nextId != 0 ) { + relationString = std::string("nextGlobalClipID:") + *nextId ; this->xmpObj.AppendArrayItem ( kXMP_NS_DC, "relation", kXMP_PropArrayIsUnordered, relationString ); - this->containsXMP = true; - - XML_NodePtr legacyConnectionContext = legacyRelationContext->GetNamedElement ( p2NS, "Connection" ); - - if ( legacyConnectionContext != 0 ) { - - XML_NodePtr legacyContext = legacyConnectionContext->GetNamedElement ( p2NS, "Top" ); - - if ( legacyContext != 0 ) { - legacyProp = legacyContext->GetNamedElement ( p2NS, "GlobalClipID" ); - - if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() ) { - relationString = std::string("topGlobalClipID:") + legacyProp->GetLeafContentValue(); - this->xmpObj.AppendArrayItem ( kXMP_NS_DC, "relation", kXMP_PropArrayIsUnordered, relationString ); - } - } - - legacyContext = legacyConnectionContext->GetNamedElement ( p2NS, "Previous" ); - - if ( legacyContext != 0 ) { - legacyProp = legacyContext->GetNamedElement ( p2NS, "GlobalClipID" ); - - if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() ) { - relationString = std::string("previousGlobalClipID:") + legacyProp->GetLeafContentValue(); - this->xmpObj.AppendArrayItem ( kXMP_NS_DC, "relation", kXMP_PropArrayIsUnordered, relationString ); - } - } - - legacyContext = legacyConnectionContext->GetNamedElement ( p2NS, "Next" ); - - if ( legacyContext != 0 ) { - legacyProp = legacyContext->GetNamedElement ( p2NS, "GlobalClipID" ); - - if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() ) { - relationString = std::string("nextGlobalClipID:") + legacyProp->GetLeafContentValue(); - this->xmpObj.AppendArrayItem ( kXMP_NS_DC, "relation", kXMP_PropArrayIsUnordered, relationString ); - } - } - - } - } } @@ -491,8 +441,9 @@ void P2_MetaHandler::SetRelationsFromLegacyXML ( bool digestFound ) void P2_MetaHandler::SetAudioInfoFromLegacyXML ( bool digestFound ) { - XMP_StringPtr p2NS = this->p2NS.c_str(); - XML_NodePtr legacyAudioContext = this->clipContent->GetNamedElement ( p2NS, "EssenceList" ); + P2_Clip* p2Clip = this->p2ClipManager.GetManagedClip() ; + XMP_StringPtr p2NS = p2Clip->GetP2RootNode()->ns.c_str(); + XML_NodePtr legacyAudioContext = p2Clip->GetEssenceListNode(); if ( legacyAudioContext != 0 ) { @@ -537,8 +488,9 @@ void P2_MetaHandler::SetAudioInfoFromLegacyXML ( bool digestFound ) void P2_MetaHandler::SetVideoInfoFromLegacyXML ( bool digestFound ) { - XMP_StringPtr p2NS = this->p2NS.c_str(); - XML_NodePtr legacyVideoContext = this->clipContent->GetNamedElement ( p2NS, "EssenceList" ); + P2_Clip* p2Clip = this->p2ClipManager.GetManagedClip() ; + XMP_StringPtr p2NS = p2Clip->GetP2RootNode()->ns.c_str(); + XML_NodePtr legacyVideoContext = p2Clip->GetEssenceListNode(); if ( legacyVideoContext != 0 ) { @@ -562,20 +514,21 @@ void P2_MetaHandler::SetDurationFromLegacyXML ( bool digestFound ) { if ( digestFound || (! this->xmpObj.DoesPropertyExist ( kXMP_NS_DM, "duration" )) ) { + + P2_SpannedClip* p2Clip=this->p2ClipManager.GetSpannedClip(); + XMP_Uns32 dur = p2Clip->GetDuration(); + XMP_VarString* editunit= p2Clip->GetEditUnit(); - XMP_StringPtr p2NS = this->p2NS.c_str(); - XML_NodePtr legacyDurationProp = this->clipContent->GetNamedElement ( p2NS, "Duration" ); - XML_NodePtr legacyEditUnitProp = this->clipContent->GetNamedElement ( p2NS, "EditUnit" ); - - if ( (legacyDurationProp != 0) && ( legacyEditUnitProp != 0 ) && - legacyDurationProp->IsLeafContentNode() && legacyEditUnitProp->IsLeafContentNode() ) { + if ( ( dur != 0) && ( editunit != 0 ) ) { + ostringstream duration; + duration<<dur; this->xmpObj.DeleteProperty ( kXMP_NS_DM, "duration" ); this->xmpObj.SetStructField ( kXMP_NS_DM, "duration", - kXMP_NS_DM, "value", legacyDurationProp->GetLeafContentValue() ); + kXMP_NS_DM, "value", duration.str().c_str() ); this->xmpObj.SetStructField ( kXMP_NS_DM, "duration", - kXMP_NS_DM, "scale", legacyEditUnitProp->GetLeafContentValue() ); + kXMP_NS_DM, "scale", editunit->c_str() ); this->containsXMP = true; } @@ -593,8 +546,9 @@ void P2_MetaHandler::SetVideoFrameInfoFromLegacyXML ( XML_NodePtr legacyVideoCon // Map the P2 Codec field to various dynamic media schema fields. if ( digestFound || (! this->xmpObj.DoesPropertyExist ( kXMP_NS_DM, "videoFrameSize" )) ) { - - XMP_StringPtr p2NS = this->p2NS.c_str(); + + P2_Clip* p2Clip = this->p2ClipManager.GetManagedClip() ; + XMP_StringPtr p2NS = p2Clip->GetP2RootNode()->ns.c_str(); XML_NodePtr legacyProp = legacyVideoContext->GetNamedElement ( p2NS, "Codec" ); if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() ) { @@ -631,7 +585,8 @@ void P2_MetaHandler::SetVideoFrameInfoFromLegacyXML ( XML_NodePtr legacyVideoCon // This is AVC-Intra footage. The framerate and PAR depend on the "class" attribute in the P2 XML. const XMP_StringPtr codecClass = legacyProp->GetAttrValue( "Class" ); - + if ( codecClass != 0 ) + dmVideoCompressor = "AVC-Intra"; // initializing with default value if ( XMP_LitMatch ( codecClass, "100" ) ) { dmVideoCompressor = "AVC-Intra 100"; @@ -735,7 +690,8 @@ void P2_MetaHandler::SetStartTimecodeFromLegacyXML ( XML_NodePtr legacyVideoCont // Translate start timecode to the format specified by the dynamic media schema. if ( digestFound || (! this->xmpObj.DoesPropertyExist ( kXMP_NS_DM, "startTimecode" )) ) { - XMP_StringPtr p2NS = this->p2NS.c_str(); + P2_Clip* p2Clip = this->p2ClipManager.GetManagedClip() ; + XMP_StringPtr p2NS = p2Clip->GetP2RootNode()->ns.c_str(); XML_NodePtr legacyProp = legacyVideoContext->GetNamedElement ( p2NS, "StartTimecode" ); if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() ) { @@ -821,7 +777,8 @@ void P2_MetaHandler::SetGPSPropertyFromLegacyXML ( XML_NodePtr legacyLocationCo if ( digestFound || (! this->xmpObj.DoesPropertyExist ( kXMP_NS_EXIF, propName )) ) { - XMP_StringPtr p2NS = this->p2NS.c_str(); + P2_Clip* p2Clip = this->p2ClipManager.GetManagedClip() ; + XMP_StringPtr p2NS = p2Clip->GetP2RootNode()->ns.c_str(); XML_NodePtr legacyGPSProp = legacyLocationContext->GetNamedElement ( p2NS, legacyPropName ); if ( ( legacyGPSProp != 0 ) && legacyGPSProp->IsLeafContentNode() ) { @@ -866,7 +823,8 @@ void P2_MetaHandler::SetAltitudeFromLegacyXML ( XML_NodePtr legacyLocationConte if ( digestFound || (! this->xmpObj.DoesPropertyExist ( kXMP_NS_EXIF, "GPSAltitude" )) ) { - XMP_StringPtr p2NS = this->p2NS.c_str(); + P2_Clip* p2Clip = this->p2ClipManager.GetManagedClip() ; + XMP_StringPtr p2NS = p2Clip->GetP2RootNode()->ns.c_str(); XML_NodePtr legacyAltitudeProp = legacyLocationContext->GetNamedElement ( p2NS, "Altitude" ); if ( ( legacyAltitudeProp != 0 ) && legacyAltitudeProp->IsLeafContentNode() ) { @@ -913,22 +871,32 @@ void P2_MetaHandler::SetAltitudeFromLegacyXML ( XML_NodePtr legacyLocationConte XML_Node * P2_MetaHandler::ForceChildElement ( XML_Node * parent, XMP_StringPtr localName, XMP_Int32 indent , XMP_Bool insertAtFront ) { XML_Node * wsNodeBefore, * wsNodeAfter; - XML_Node * childNode = parent->GetNamedElement ( this->p2NS.c_str(), localName ); + P2_Clip* p2Clip = this->p2ClipManager.GetManagedClip() ; + XMP_StringPtr p2NS = p2Clip->GetP2RootNode()->ns.c_str(); + XML_Node * childNode = parent->GetNamedElement ( p2NS, localName ); // if ( childNode == 0 ) { // The indenting is a hack, assuming existing 2 spaces per level. - - wsNodeBefore = new XML_Node ( parent, "", kCDataNode ); - wsNodeBefore->value = " "; // Add 2 spaces to the existing WS before the parent's close tag. - - childNode = new XML_Node ( parent, localName, kElemNode ); - childNode->ns = parent->ns; - childNode->nsPrefixLen = parent->nsPrefixLen; - childNode->name.insert ( 0, parent->name, 0, parent->nsPrefixLen ); - - wsNodeAfter = new XML_Node ( parent, "", kCDataNode ); + try { + wsNodeBefore = new XML_Node ( parent, "", kCDataNode ); + wsNodeBefore->value = " "; // Add 2 spaces to the existing WS before the parent's close tag. + + childNode = new XML_Node ( parent, localName, kElemNode ); + childNode->ns = parent->ns; + childNode->nsPrefixLen = parent->nsPrefixLen; + childNode->name.insert ( 0, parent->name, 0, parent->nsPrefixLen ); + + wsNodeAfter = new XML_Node ( parent, "", kCDataNode ); + } catch (...) { + if (wsNodeBefore) + delete wsNodeBefore; + if (childNode) + delete childNode; + + throw; + } wsNodeAfter->value = '\n'; for ( ; indent > 1; --indent ) wsNodeAfter->value += " "; // Indent less 1, to "outdent" the parent's close. @@ -959,184 +927,22 @@ XML_Node * P2_MetaHandler::ForceChildElement ( XML_Node * parent, XMP_StringPtr } // P2_MetaHandler::ForceChildElement // ================================================================================================= -// P2_MetaHandler::MakeLegacyDigest -// ================================= - -// *** Early hack version. - -#define kHexDigits "0123456789ABCDEF" - -void P2_MetaHandler::MakeLegacyDigest ( std::string * digestStr ) -{ - digestStr->erase(); - if ( this->clipMetadata == 0 ) return; // Bail if we don't have any legacy XML. - XMP_Assert ( this->expat != 0 ); - - XMP_StringPtr p2NS = this->p2NS.c_str(); - XML_NodePtr legacyContext; - MD5_CTX md5Context; - unsigned char digestBin [16]; - MD5Init ( &md5Context ); - - legacyContext = this->clipContent; - this->DigestLegacyItem ( md5Context, legacyContext, "ClipName" ); - this->DigestLegacyItem ( md5Context, legacyContext, "GlobalClipID" ); - this->DigestLegacyItem ( md5Context, legacyContext, "Duration" ); - this->DigestLegacyItem ( md5Context, legacyContext, "EditUnit" ); - this->DigestLegacyRelations ( md5Context ); - - legacyContext = this->clipContent->GetNamedElement ( p2NS, "EssenceList" ); - - if ( legacyContext != 0 ) { - - XML_NodePtr videoContext = legacyContext->GetNamedElement ( p2NS, "Video" ); - - if ( videoContext != 0 ) { - this->DigestLegacyItem ( md5Context, videoContext, "AspectRatio" ); - this->DigestLegacyItem ( md5Context, videoContext, "Codec" ); - this->DigestLegacyItem ( md5Context, videoContext, "FrameRate" ); - this->DigestLegacyItem ( md5Context, videoContext, "StartTimecode" ); - } - - XML_NodePtr audioContext = legacyContext->GetNamedElement ( p2NS, "Audio" ); - - if ( audioContext != 0 ) { - this->DigestLegacyItem ( md5Context, audioContext, "SamplingRate" ); - this->DigestLegacyItem ( md5Context, audioContext, "BitsPerSample" ); - } - - } - - legacyContext = this->clipMetadata; - this->DigestLegacyItem ( md5Context, legacyContext, "UserClipName" ); - this->DigestLegacyItem ( md5Context, legacyContext, "ShotMark" ); - - legacyContext = this->clipMetadata->GetNamedElement ( p2NS, "Access" ); - /* Rather return than create the digest because the "Access" element is listed as "required" in the P2 spec. - So a P2 file without an "Access" element does not follow the spec and might be corrupt.*/ - if ( legacyContext == 0 ) return; - - this->DigestLegacyItem ( md5Context, legacyContext, "Creator" ); - this->DigestLegacyItem ( md5Context, legacyContext, "CreationDate" ); - this->DigestLegacyItem ( md5Context, legacyContext, "LastUpdateDate" ); - - legacyContext = this->clipMetadata->GetNamedElement ( p2NS, "Shoot" ); - - if ( legacyContext != 0 ) { - this->DigestLegacyItem ( md5Context, legacyContext, "Shooter" ); - - legacyContext = legacyContext->GetNamedElement ( p2NS, "Location" ); - - if ( legacyContext != 0 ) { - this->DigestLegacyItem ( md5Context, legacyContext, "PlaceName" ); - this->DigestLegacyItem ( md5Context, legacyContext, "Longitude" ); - this->DigestLegacyItem ( md5Context, legacyContext, "Latitude" ); - this->DigestLegacyItem ( md5Context, legacyContext, "Altitude" ); - } - } - - legacyContext = this->clipMetadata->GetNamedElement ( p2NS, "Scenario" ); - - if ( legacyContext != 0 ) { - this->DigestLegacyItem ( md5Context, legacyContext, "SceneNo." ); - this->DigestLegacyItem ( md5Context, legacyContext, "TakeNo." ); - } - - legacyContext = this->clipMetadata->GetNamedElement ( p2NS, "Device" ); - - if ( legacyContext != 0 ) { - this->DigestLegacyItem ( md5Context, legacyContext, "Manufacturer" ); - this->DigestLegacyItem ( md5Context, legacyContext, "SerialNo." ); - this->DigestLegacyItem ( md5Context, legacyContext, "ModelName" ); - } - - MD5Final ( digestBin, &md5Context ); - - char buffer [40]; - for ( int in = 0, out = 0; in < 16; in += 1, out += 2 ) { - XMP_Uns8 byte = digestBin[in]; - buffer[out] = kHexDigits [ byte >> 4 ]; - buffer[out+1] = kHexDigits [ byte & 0xF ]; - } - buffer[32] = 0; - digestStr->append ( buffer ); - -} // P2_MetaHandler::MakeLegacyDigest - -// ================================================================================================= -// P2_MetaHandler::GetFileModDate -// ============================== - -static inline bool operator< ( const XMP_DateTime & left, const XMP_DateTime & right ) { - int compare = SXMPUtils::CompareDateTime ( left, right ); - return (compare < 0); -} - -bool P2_MetaHandler::GetFileModDate ( XMP_DateTime * modDate ) -{ - - // The P2 locations of metadata: - // CONTENTS/ - // CLIP/ - // 0001AB.XML - // 0001AB.XMP - - bool ok, haveDate = false; - std::string fullPath; - XMP_DateTime oneDate, junkDate; - if ( modDate == 0 ) modDate = &junkDate; - - ok = this->MakeClipFilePath ( &fullPath, ".XML", true /* checkFile */ ); - if ( ok ) ok = Host_IO::GetModifyDate ( fullPath.c_str(), &oneDate ); - if ( ok ) { - if ( (! haveDate) || (*modDate < oneDate) ) *modDate = oneDate; - haveDate = true; - } - - ok = this->MakeClipFilePath ( &fullPath, ".XMP", true /* checkFile */ ); - if ( ok ) ok = Host_IO::GetModifyDate ( fullPath.c_str(), &oneDate ); - if ( ok ) { - if ( (! haveDate) || (*modDate < oneDate) ) *modDate = oneDate; - haveDate = true; - } - - return haveDate; - -} // P2_MetaHandler::GetFileModDate - -// ================================================================================================= -// P2_MetaHandler::FillMetadataFiles -// ================================= -void P2_MetaHandler::FillMetadataFiles ( std::vector<std::string>* metadataFiles ) -{ - std::string noExtPath, filePath; - - noExtPath = rootPath + kDirChar + "CONTENTS" + kDirChar + "CLIP" + kDirChar + clipName; - - filePath = noExtPath + ".XMP"; - metadataFiles->push_back ( filePath ); - filePath = noExtPath + ".XML"; - metadataFiles->push_back ( filePath ); - -} // FillMetadataFiles_P2 - -// ================================================================================================= // P2_MetaHandler::IsMetadataWritable // ======================================= bool P2_MetaHandler::IsMetadataWritable ( ) { - std::vector<std::string> metadataFiles; - FillMetadataFiles(&metadataFiles); - std::vector<std::string>::iterator itr = metadataFiles.begin(); + std::string noExtPath, filePath; + noExtPath = rootPath + kDirChar + "CONTENTS" + kDirChar + "CLIP" + kDirChar + this->clipName ; + filePath = noExtPath + ".XMP"; // Check whether sidecar is writable, if not then check if it can be created. - bool xmpWritable = Host_IO::Writable( itr->c_str(), true ); + bool writable = Host_IO::Writable( filePath.c_str(), true ); + filePath = noExtPath + ".XML"; // Check if legacy XML is writable. - bool xmlWritable = Host_IO::Writable( (++itr)->c_str(), false ); - return (xmlWritable && xmpWritable); + writable &= Host_IO::Writable( filePath.c_str(), false ); + return writable; }// P2_MetaHandler::IsMetadataWritable - // ================================================================================================= // P2_MetaHandler::FillAssociatedResources // ====================================== @@ -1158,47 +964,54 @@ void P2_MetaHandler::FillAssociatedResources ( std::vector<std::string> * resour // PROXY/ // XXXXXX.MP4 // XXXXXX.BIN - XMP_VarString contentsPath = this->rootPath + kDirChar + "CONTENTS" + kDirChar; XMP_VarString path; //Add RootPath path = this->rootPath + kDirChar; PackageFormat_Support::AddResourceIfExists(resourceList, path); - - std::string clipPathNoExt = contentsPath + "CLIP" + kDirChar + this->clipName; - // Get the files present inside CLIP folder. - path = clipPathNoExt + ".XML"; - PackageFormat_Support::AddResourceIfExists(resourceList, path); - path = clipPathNoExt + ".XMP"; - PackageFormat_Support::AddResourceIfExists(resourceList, path); - - // Get the files present inside VIDEO folder. - path = contentsPath + "VIDEO" + kDirChar + this->clipName + ".MXF"; - PackageFormat_Support::AddResourceIfExists(resourceList, path); - - // Get the files present inside AUDIO folder. - path = contentsPath + "AUDIO" + kDirChar; - XMP_VarString regExp; - regExp = "^" + this->clipName + "\\d\\d.MXF$"; - IOUtils::GetMatchingChildren ( *resourceList, path, regExp, false, true, true ); - - // Get the files present inside ICON folder. - path = contentsPath + "ICON" + kDirChar + this->clipName + ".BMP"; - PackageFormat_Support::AddResourceIfExists(resourceList, path); - - // Get the files present inside VOICE folder. - path = contentsPath + "VOICE" + kDirChar; - regExp = "^" + clipName + "\\d\\d.WAV$"; - IOUtils::GetMatchingChildren ( *resourceList, path, regExp, false, true, true ); - - // Get the files present inside PROXY folder. - std::string proxyPathNoExt = contentsPath + "PROXY" + kDirChar + this->clipName; - - path = proxyPathNoExt + ".MP4"; - PackageFormat_Support::AddResourceIfExists(resourceList, path); - path = proxyPathNoExt + ".BIN"; - PackageFormat_Support::AddResourceIfExists(resourceList, path); + P2_SpannedClip* p2SpanClip=p2ClipManager.GetSpannedClip(); + if ( ! p2SpanClip ) return ; + std::vector<std::string> clipNameList; + p2SpanClip->GetAllClipNames ( clipNameList ); + std::vector<std::string>::iterator iter = clipNameList.begin(); + for(; iter!=clipNameList.end(); iter++) + { + + std::string clipPathNoExt = contentsPath + "CLIP" + kDirChar + *iter; + // Get the files present inside CLIP folder. + path = clipPathNoExt + ".XML"; + PackageFormat_Support::AddResourceIfExists(resourceList, path); + path = clipPathNoExt + ".XMP"; + PackageFormat_Support::AddResourceIfExists(resourceList, path); + + // Get the files present inside VIDEO folder. + path = contentsPath + "VIDEO" + kDirChar + *iter + ".MXF"; + PackageFormat_Support::AddResourceIfExists(resourceList, path); + + // Get the files present inside AUDIO folder. + path = contentsPath + "AUDIO" + kDirChar; + XMP_VarString regExp; + regExp = "^" + *iter + "\\d\\d.MXF$"; + IOUtils::GetMatchingChildren ( *resourceList, path, regExp, false, true, true ); + + // Get the files present inside ICON folder. + path = contentsPath + "ICON" + kDirChar + *iter + ".BMP"; + PackageFormat_Support::AddResourceIfExists(resourceList, path); + + // Get the files present inside VOICE folder. + path = contentsPath + "VOICE" + kDirChar; + regExp = "^" + *iter + "\\d\\d.WAV$"; + IOUtils::GetMatchingChildren ( *resourceList, path, regExp, false, true, true ); + + // Get the files present inside PROXY folder. + std::string proxyPathNoExt = contentsPath + "PROXY" + kDirChar + *iter; + + path = proxyPathNoExt + ".MP4"; + PackageFormat_Support::AddResourceIfExists(resourceList, path); + path = proxyPathNoExt + ".BIN"; + PackageFormat_Support::AddResourceIfExists(resourceList, path); + } } // P2_MetaHandler::FillAssociatedResources // ================================================================================================= @@ -1216,6 +1029,7 @@ void P2_MetaHandler::CacheFileData() // Make sure the clip's .XMP file exists. std::string xmpPath; + this->MakeClipFilePath ( &xmpPath, ".XMP" ); if ( ! Host_IO::Exists ( xmpPath.c_str() ) ) return; // No XMP. @@ -1253,96 +1067,42 @@ void P2_MetaHandler::CacheFileData() void P2_MetaHandler::ProcessXMP() { - - // Some versions of gcc can't tolerate goto's across declarations. - // *** Better yet, avoid this cruft with self-cleaning objects. - #define CleanupAndExit \ - { \ - bool openForUpdate = XMP_OptionIsSet ( this->parent->openFlags, kXMPFiles_OpenForUpdate ); \ - if ( ! openForUpdate ) this->CleanupLegacyXML(); \ - return; \ - } - if ( this->processedXMP ) return; this->processedXMP = true; // Make sure only called once. if ( this->containsXMP ) { this->xmpObj.ParseFromBuffer ( this->xmpPacket.c_str(), (XMP_StringLen)this->xmpPacket.size() ); } - - std::string xmlPath; - this->MakeClipFilePath ( &xmlPath, ".XML" ); - - Host_IO::FileRef hostRef = Host_IO::Open ( xmlPath.c_str(), Host_IO::openReadOnly ); - if ( hostRef == Host_IO::noFileRef ) return; // The open failed. - XMPFiles_IO xmlFile ( hostRef, xmlPath.c_str(), Host_IO::openReadOnly ); - - this->expat = XMP_NewExpatAdapter ( ExpatAdapter::kUseLocalNamespaces ); - if ( this->expat == 0 ) XMP_Throw ( "P2_MetaHandler: Can't create Expat adapter", kXMPErr_NoMemory ); - - XMP_Uns8 buffer [64*1024]; - while ( true ) { - XMP_Int32 ioCount = xmlFile.Read ( buffer, sizeof(buffer) ); - if ( ioCount == 0 ) break; - this->expat->ParseBuffer ( buffer, ioCount, false /* not the end */ ); - } - this->expat->ParseBuffer ( 0, 0, true ); // End the parse. - - xmlFile.Close(); - - // The root element should be P2Main in some namespace. At least 2 different namespaces are in - // use (ending in "v3.0" and "v3.1"). Take whatever this file uses. - - XML_Node & xmlTree = this->expat->tree; - XML_NodePtr rootElem = 0; - - for ( size_t i = 0, limit = xmlTree.content.size(); i < limit; ++i ) { - if ( xmlTree.content[i]->kind == kElemNode ) { - rootElem = xmlTree.content[i]; - } - } - - if ( rootElem == 0 ) CleanupAndExit - XMP_StringPtr rootLocalName = rootElem->name.c_str() + rootElem->nsPrefixLen; - if ( ! XMP_LitMatch ( rootLocalName, "P2Main" ) ) CleanupAndExit - - this->p2NS = rootElem->ns; - - // Now find ClipMetadata element and check the legacy digest. - - XMP_StringPtr p2NS = this->p2NS.c_str(); - XML_NodePtr legacyContext, legacyProp; - - legacyContext = rootElem->GetNamedElement ( p2NS, "ClipContent" ); - if ( legacyContext == 0 ) CleanupAndExit - - this->clipContent = legacyContext; // ! Save the ClipContext pointer for other use. - - legacyContext = legacyContext->GetNamedElement ( p2NS, "ClipMetadata" ); - if ( legacyContext == 0 ) CleanupAndExit - - this->clipMetadata = legacyContext; // ! Save the ClipMetadata pointer for other use. - + + XML_NodePtr legacyContext, clipMetadata, legacyProp; + if ( ! this->p2ClipManager.IsValidP2() ) return; + P2_Clip* p2Clip=this->p2ClipManager.GetManagedClip(); + XMP_StringPtr p2NS = p2Clip->GetP2RootNode()->ns.c_str(); std::string oldDigest, newDigest; bool digestFound = this->xmpObj.GetStructField ( kXMP_NS_XMP, "NativeDigests", kXMP_NS_XMP, "P2", &oldDigest, 0 ); if ( digestFound ) { - this->MakeLegacyDigest ( &newDigest ); - if ( oldDigest == newDigest ) CleanupAndExit + p2Clip->CreateDigest ( &newDigest ); + if ( oldDigest == newDigest ) return; } // If we get here we need find and import the actual legacy elements using the current namespace. // Either there is no old digest in the XMP, or the digests differ. In the former case keep any // existing XMP, in the latter case take new legacy values. - this->SetXMPPropertyFromLegacyXML ( digestFound, this->clipContent, kXMP_NS_DC, "title", "ClipName", true ); - this->SetXMPPropertyFromLegacyXML ( digestFound, this->clipContent, kXMP_NS_DC, "identifier", "GlobalClipID", false ); + std::string clipTitle= p2Clip->GetClipTitle();// needed for successful Mac Builds + this->SetXMPPropertyFromLegacyXML ( digestFound, &clipTitle , kXMP_NS_DC, "title", true ); + if ( p2Clip->IsValidClip() ) + this->SetXMPPropertyFromLegacyXML ( digestFound, p2Clip->GetClipId(), kXMP_NS_DC, "identifier", false ); this->SetDurationFromLegacyXML (digestFound ); this->SetRelationsFromLegacyXML ( digestFound ); - this->SetXMPPropertyFromLegacyXML ( digestFound, this->clipMetadata, kXMP_NS_DM, "shotName", "UserClipName", false ); + clipMetadata = p2Clip->GetClipMetadataNode(); + if ( clipMetadata == 0 ) return; + this->SetXMPPropertyFromLegacyXML ( digestFound,p2Clip->GetClipMetadataNode(), kXMP_NS_DM, "shotName", "UserClipName", false ); this->SetAudioInfoFromLegacyXML ( digestFound ); this->SetVideoInfoFromLegacyXML ( digestFound ); - legacyContext = this->clipMetadata->GetNamedElement ( p2NS, "Access" ); - if ( legacyContext == 0 ) CleanupAndExit + + legacyContext = clipMetadata->GetNamedElement ( p2NS, "Access" ); + if ( legacyContext == 0 ) return; if ( digestFound || (! this->xmpObj.DoesPropertyExist ( kXMP_NS_DC, "creator" )) ) { legacyProp = legacyContext->GetNamedElement ( p2NS, "Creator" ); @@ -1358,7 +1118,7 @@ void P2_MetaHandler::ProcessXMP() this->SetXMPPropertyFromLegacyXML ( digestFound, legacyContext, kXMP_NS_XMP, "ModifyDate", "LastUpdateDate", false ); if ( digestFound || (! this->xmpObj.DoesPropertyExist ( kXMP_NS_DM, "good" )) ) { - legacyProp = this->clipMetadata->GetNamedElement ( p2NS, "ShotMark" ); + legacyProp = clipMetadata->GetNamedElement ( p2NS, "ShotMark" ); if ( (legacyProp == 0) || (! legacyProp->IsLeafContentNode()) ) { this->xmpObj.DeleteProperty ( kXMP_NS_DM, "good" ); } else { @@ -1375,7 +1135,7 @@ void P2_MetaHandler::ProcessXMP() } } - legacyContext = this->clipMetadata->GetNamedElement ( p2NS, "Shoot" ); + legacyContext = clipMetadata->GetNamedElement ( p2NS, "Shoot" ); if ( legacyContext != 0 ) { this->SetXMPPropertyFromLegacyXML ( digestFound, legacyContext, kXMP_NS_TIFF, "Artist", "Shooter", false ); legacyContext = legacyContext->GetNamedElement ( p2NS, "Location" ); @@ -1388,21 +1148,20 @@ void P2_MetaHandler::ProcessXMP() this->SetAltitudeFromLegacyXML ( legacyContext, digestFound ); } - legacyContext = this->clipMetadata->GetNamedElement ( p2NS, "Device" ); + legacyContext = clipMetadata->GetNamedElement ( p2NS, "Device" ); if ( legacyContext != 0 ) { this->SetXMPPropertyFromLegacyXML ( digestFound, legacyContext, kXMP_NS_TIFF, "Make", "Manufacturer", false ); this->SetXMPPropertyFromLegacyXML ( digestFound, legacyContext, kXMP_NS_EXIF_Aux, "SerialNumber", "SerialNo.", false ); this->SetXMPPropertyFromLegacyXML ( digestFound, legacyContext, kXMP_NS_TIFF, "Model", "ModelName", false ); } - legacyContext = this->clipMetadata->GetNamedElement ( p2NS, "Scenario" ); + legacyContext = clipMetadata->GetNamedElement ( p2NS, "Scenario" ); if ( legacyContext != 0 ) { this->SetXMPPropertyFromLegacyXML ( digestFound, legacyContext, kXMP_NS_DM, "scene", "SceneNo.", false ); this->SetXMPPropertyFromLegacyXML ( digestFound, legacyContext, kXMP_NS_DM, "takeNumber", "TakeNo.", false ); } - CleanupAndExit - #undef CleanupAndExit + return; } // P2_MetaHandler::ProcessXMP @@ -1439,47 +1198,51 @@ void P2_MetaHandler::UpdateFile ( bool doSafeUpdate ) // Update the internal legacy XML tree if we have one, and set the digest in the XMP. bool updateLegacyXML = false; + P2_Clip* p2Clip = 0; + XML_NodePtr clipMetadata = 0; + if ( this->p2ClipManager.IsValidP2() ) + { + p2Clip=this->p2ClipManager.GetManagedClip(); + clipMetadata = p2Clip->GetClipMetadataNode(); + if ( clipMetadata != 0 ) { - if ( this->clipMetadata != 0 ) { - - XMP_Assert ( this->expat != 0 ); + bool xmpFound; + std::string xmpValue; + XML_Node * xmlNode; - bool xmpFound; - std::string xmpValue; - XML_Node * xmlNode; + xmpFound = this->xmpObj.GetLocalizedText ( kXMP_NS_DC, "title", "", "x-default", 0, &xmpValue, 0 ); - xmpFound = this->xmpObj.GetLocalizedText ( kXMP_NS_DC, "title", "", "x-default", 0, &xmpValue, 0 ); + if ( xmpFound && p2Clip->GetClipContentNode()) { - if ( xmpFound ) { + xmlNode = this->ForceChildElement ( p2Clip->GetClipContentNode(), "ClipName", 3, false ); - xmlNode = this->ForceChildElement ( this->clipContent, "ClipName", 3, false ); + if ( xmpValue != xmlNode->GetLeafContentValue() ) { + xmlNode->SetLeafContentValue ( xmpValue.c_str() ); + updateLegacyXML = true; + } - if ( xmpValue != xmlNode->GetLeafContentValue() ) { - xmlNode->SetLeafContentValue ( xmpValue.c_str() ); - updateLegacyXML = true; } - } - - xmpFound = this->xmpObj.GetArrayItem ( kXMP_NS_DC, "creator", 1, &xmpValue, 0 ); + xmpFound = this->xmpObj.GetArrayItem ( kXMP_NS_DC, "creator", 1, &xmpValue, 0 ); - if ( xmpFound ) { - xmlNode = this->ForceChildElement ( this->clipMetadata, "Access", 3, false ); + if ( xmpFound ) { + xmlNode = this->ForceChildElement ( clipMetadata , "Access", 3, false ); - // "Creator" must be first child of "Access" node else Panasonic P2 Viewer gives an error. - xmlNode = this->ForceChildElement ( xmlNode, "Creator", 4 , true); - if ( xmpValue != xmlNode->GetLeafContentValue() ) { - xmlNode->SetLeafContentValue ( xmpValue.c_str() ); - updateLegacyXML = true; + // "Creator" must be first child of "Access" node else Panasonic P2 Viewer gives an error. + xmlNode = this->ForceChildElement ( xmlNode, "Creator", 4 , true); + if ( xmpValue != xmlNode->GetLeafContentValue() ) { + xmlNode->SetLeafContentValue ( xmpValue.c_str() ); + updateLegacyXML = true; + } } + } + std::string newDigest; + this->p2ClipManager.GetManagedClip()->CreateDigest ( &newDigest ); + this->xmpObj.SetStructField ( kXMP_NS_XMP, "NativeDigests", kXMP_NS_XMP, "P2", newDigest.c_str(), kXMP_DeleteExisting ); } - std::string newDigest; - this->MakeLegacyDigest ( &newDigest ); - this->xmpObj.SetStructField ( kXMP_NS_XMP, "NativeDigests", kXMP_NS_XMP, "P2", newDigest.c_str(), kXMP_DeleteExisting ); - this->xmpObj.SerializeToBuffer ( &this->xmpPacket, this->GetSerializeOptions() ); // ----------------------------------------------------------------------- @@ -1512,8 +1275,8 @@ void P2_MetaHandler::UpdateFile ( bool doSafeUpdate ) dummy attribute with this namespace to clipContent/clipMetadata (whichever is non-null) before serializing the XML tree. We are also undoing it below after serialization.*/ - XML_Node *parentNode = AddXSINamespace(this->clipContent, this->clipMetadata); - this->expat->tree.Serialize ( &legacyXML ); + XML_Node *parentNode = AddXSINamespace(p2Clip->GetClipContentNode(), clipMetadata); + p2Clip->SerializeP2ClipContent ( legacyXML ); if(parentNode){ // Remove the dummy attribute added to clipContent/clipMetadata. delete parentNode->attrs[parentNode->attrs.size()-1]; diff --git a/XMPFiles/source/FileHandlers/P2_Handler.hpp b/XMPFiles/source/FileHandlers/P2_Handler.hpp index 513e9ea..5e492ee 100644 --- a/XMPFiles/source/FileHandlers/P2_Handler.hpp +++ b/XMPFiles/source/FileHandlers/P2_Handler.hpp @@ -17,6 +17,7 @@ #include "source/ExpatAdapter.hpp" #include "third-party/zuid/interfaces/MD5.h" +#include "XMPFiles/source/FormatSupport/P2_Support.hpp" // ================================================================================================= /// \file P2_Handler.hpp @@ -52,8 +53,6 @@ class P2_MetaHandler : public XMPFileHandler { public: - bool GetFileModDate ( XMP_DateTime * modDate ); - void FillMetadataFiles ( std::vector<std::string> * metadataFiles ); void FillAssociatedResources ( std::vector<std::string> * resourceList ); bool IsMetadataWritable ( ); @@ -71,11 +70,10 @@ public: private: - P2_MetaHandler() : expat(0), clipMetadata(0), clipContent(0) {}; // Hidden on purpose. + P2_MetaHandler() {}; // Hidden on purpose. bool MakeClipFilePath ( std::string * path, XMP_StringPtr suffix, bool checkFile = false ); void MakeLegacyDigest ( std::string * digestStr ); - void CleanupLegacyXML(); void DigestLegacyItem ( MD5_CTX & md5Context, XML_NodePtr legacyContext, XMP_StringPtr legacyPropName ); void DigestLegacyRelations ( MD5_CTX & md5Context ); @@ -86,6 +84,11 @@ private: XMP_StringPtr propName, XMP_StringPtr legacyPropName, bool isLocalized ); + void SetXMPPropertyFromLegacyXML ( bool digestFound, + XMP_VarString* refContext, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + bool isLocalized ); void SetRelationsFromLegacyXML ( bool digestFound ); void SetAudioInfoFromLegacyXML ( bool digestFound ); @@ -99,11 +102,9 @@ private: XML_Node * ForceChildElement ( XML_Node * parent, XMP_StringPtr localName, XMP_Int32 indent, XMP_Bool insertAtFront ); - std::string rootPath, clipName, p2NS; + std::string rootPath , clipName; - ExpatAdapter * expat; - XML_Node * clipMetadata; // ! Don't delete, points into the Expat tree. - XML_Node * clipContent; // ! Don't delete, points into the Expat tree. + P2_Manager p2ClipManager; }; // P2_MetaHandler diff --git a/XMPFiles/source/FileHandlers/PSD_Handler.cpp b/XMPFiles/source/FileHandlers/PSD_Handler.cpp index 89e1cc6..1db6ec9 100644 --- a/XMPFiles/source/FileHandlers/PSD_Handler.cpp +++ b/XMPFiles/source/FileHandlers/PSD_Handler.cpp @@ -81,7 +81,7 @@ XMPFileHandler * PSD_MetaHandlerCTor ( XMPFiles * parent ) // PSD_MetaHandler::PSD_MetaHandler // ================================ -PSD_MetaHandler::PSD_MetaHandler ( XMPFiles * _parent ) : iptcMgr(0), exifMgr(0), skipReconcile(false) +PSD_MetaHandler::PSD_MetaHandler ( XMPFiles * _parent ) : iptcMgr(0), exifMgr(0), skipReconcile(false),imageWidth(0),imageHeight(0) { this->parent = _parent; this->handlerFlags = kPSD_HandlerFlags; @@ -185,7 +185,9 @@ void PSD_MetaHandler::ProcessXMP() // Set up everything for the legacy import, but don't do it yet. This lets us do a forced legacy // import if the XMP packet gets parsing errors. - bool readOnly = ((this->parent->openFlags & kXMPFiles_OpenForUpdate) == 0); + bool readOnly = false; + if ( this->parent ) + readOnly = ((this->parent->openFlags & kXMPFiles_OpenForUpdate) == 0); if ( readOnly ) { this->iptcMgr = new IPTC_Reader(); diff --git a/XMPFiles/source/FileHandlers/SonyHDV_Handler.cpp b/XMPFiles/source/FileHandlers/SonyHDV_Handler.cpp index 9d9a9d4..17e5baf 100644 --- a/XMPFiles/source/FileHandlers/SonyHDV_Handler.cpp +++ b/XMPFiles/source/FileHandlers/SonyHDV_Handler.cpp @@ -713,7 +713,7 @@ bool SonyHDV_MetaHandler::GetFileModDate ( XMP_DateTime * modDate ) ok = this->MakeIndexFilePath ( fullPath, this->rootPath, this->clipName ); if ( ok ) ok = Host_IO::GetModifyDate ( fullPath.c_str(), &oneDate ); if ( ok ) { - if ( (! haveDate) || (*modDate < oneDate) ) *modDate = oneDate; + if ( *modDate < oneDate ) *modDate = oneDate; haveDate = true; } diff --git a/XMPFiles/source/FileHandlers/UCF_Handler.cpp b/XMPFiles/source/FileHandlers/UCF_Handler.cpp index 8e1e1ff..d304bc2 100644 --- a/XMPFiles/source/FileHandlers/UCF_Handler.cpp +++ b/XMPFiles/source/FileHandlers/UCF_Handler.cpp @@ -155,6 +155,32 @@ bool UCF_CheckFormat ( XMP_FileFormat format, UCF_MetaHandler::UCF_MetaHandler ( XMPFiles * _parent ) { + this->cdx2 = 0 ; + this->z = 0; + this->z2 = 0; + this->h = 0; + this->h2 = 0; + this->al = 0; + this->bl = 0; + this->xl = 0; + this->x2l = 0; + this->cdl = 0; + this->cd2l = 0; + this->cdxl = 0; + this->cdx2l = 0; + this->z2l = 0; + this->hl = 0; + this->fl = 0; + this->f2l = 0; + this->numCF = 0; + this->numCF2 = 0; + this->wasCompressed = false; + this->compressXMP = false; + this->inPlacePossible = false; + this->uncomprPacketLen = 0; + this->uncomprPacketStr = NULL; + this->finalPacketStr = NULL; + this->finalPacketLen = 0; this->parent = _parent; this->handlerFlags = kUCF_HandlerFlags; this->stdCharForm = kXMP_Char8Bit; @@ -480,10 +506,6 @@ void UCF_MetaHandler::CacheFileData() XMP_Enforce( file->ReadAll ( (char*)packetStr, sizeUncompressed ) ); break; } - default: - { - XMP_Throw("illegal zip compression method (not none, not flate)",kXMPErr_BadFileFormat); - } } this->containsXMP = true; // do this last, after all possible failure/execptions } diff --git a/XMPFiles/source/FileHandlers/UCF_Handler.hpp b/XMPFiles/source/FileHandlers/UCF_Handler.hpp index a9b9b55..3140c23 100644 --- a/XMPFiles/source/FileHandlers/UCF_Handler.hpp +++ b/XMPFiles/source/FileHandlers/UCF_Handler.hpp @@ -548,7 +548,7 @@ private: //// WRITE BACK REAL 64 BIT VALUES, CREATE EXTRA FIELD /////////////// //may only wipe extra field after obtaining all Info from it if (extraField) delete extraField; - extraFieldLen=0; + extraFieldLen=0; if ( ( sizeUncompressed > 0xffffffff ) || ( sizeCompressed > 0xffffffff ) || @@ -593,7 +593,7 @@ private: file ->Write ( fields , FIXED_SIZE ); if (filenameLen) file->Write ( filename , filenameLen ); if (extraFieldLen) file->Write ( extraField , extraFieldLen ); - if (commentLen) file->Write ( extraField , extraFieldLen ); + if (commentLen) file->Write ( comment , commentLen ); } void setXMPFilename() diff --git a/XMPFiles/source/FileHandlers/WAVE_Handler.cpp b/XMPFiles/source/FileHandlers/WAVE_Handler.cpp index 876234e..1695013 100644 --- a/XMPFiles/source/FileHandlers/WAVE_Handler.cpp +++ b/XMPFiles/source/FileHandlers/WAVE_Handler.cpp @@ -118,6 +118,8 @@ const ChunkIdentifier WAVE_MetaHandler::kRIFFCart[2] = { { kChunk_RIFF, kType_WA // cr8r is not yet required for WAVE // RIFF:WAVE/Cr8r // const ChunkIdentifier WAVE_MetaHandler::kWAVECr8r[2] = { { kChunk_RIFF, kType_WAVE }, { kChunk_Cr8r, kType_NONE } }; +// RIFF:WAVE/iXML +const ChunkIdentifier WAVE_MetaHandler::kRIFFiXML[2] = { { kChunk_RIFF, kType_WAVE }, { kChunk_iXML, kType_NONE } }; // RF64:WAVE/PMX_ const ChunkIdentifier WAVE_MetaHandler::kRF64XMP[2] = { { kChunk_RF64, kType_WAVE }, { kChunk_XMP, kType_NONE} }; // RF64:WAVE/LIST:INFO @@ -131,6 +133,7 @@ const ChunkIdentifier WAVE_MetaHandler::kRF64Cart[2] = { { kChunk_RF64, kType_WA // cr8r is not yet required for WAVE // RF64:WAVE/Cr8r // const ChunkIdentifier WAVE_MetaHandler::kRF64Cr8r[2] = { { kChunk_RF64, kType_WAVE }, { kChunk_Cr8r, kType_NONE } }; +const ChunkIdentifier WAVE_MetaHandler::kRF64iXML[2] = { { kChunk_RF64, kType_WAVE }, { kChunk_iXML, kType_NONE } }; // ================================================================================================= // WAVE_MetaHandler::WAVE_MetaHandler @@ -138,9 +141,9 @@ const ChunkIdentifier WAVE_MetaHandler::kRF64Cart[2] = { { kChunk_RF64, kType_WA WAVE_MetaHandler::WAVE_MetaHandler ( XMPFiles * _parent ) : mChunkBehavior(NULL), mChunkController(NULL), - mINFOMeta(), mBEXTMeta(), mCartMeta(), mDISPMeta(), + mINFOMeta(), mBEXTMeta(), mCartMeta(), mDISPMeta(), miXMLMeta(), mXMPChunk(NULL), mINFOChunk(NULL), - mBEXTChunk(NULL), mCartChunk(NULL), mDISPChunk(NULL) + mBEXTChunk(NULL), mCartChunk(NULL), mDISPChunk(NULL), miXMLChunk(NULL) { this->parent = _parent; this->handlerFlags = kWAVE_HandlerFlags; @@ -148,6 +151,7 @@ WAVE_MetaHandler::WAVE_MetaHandler ( XMPFiles * _parent ) this->mChunkBehavior = new WAVEBehavior(); this->mChunkController = new ChunkController( mChunkBehavior, false ); + miXMLMeta.SetErrorCallback( &parent->errorCallback ); } // WAVE_MetaHandler::WAVE_MetaHandler @@ -197,6 +201,7 @@ void WAVE_MetaHandler::CacheFileData() mWAVEXMPChunkPath.append( kRIFFXMP, SizeOfCIArray(kRIFFXMP) ); mWAVEInfoChunkPath.append( kRIFFInfo, SizeOfCIArray(kRIFFInfo) ); mWAVEDispChunkPath.append( kRIFFDisp, SizeOfCIArray(kRIFFDisp) ); + mWAVEiXMLChunkPath.append( kRIFFiXML, SizeOfCIArray(kRIFFiXML) ); mWAVEBextChunkPath.append( kRIFFBext, SizeOfCIArray(kRIFFBext) ); mWAVECartChunkPath.append( kRIFFCart, SizeOfCIArray(kRIFFCart) ); // cr8r is not yet required for WAVE @@ -207,6 +212,7 @@ void WAVE_MetaHandler::CacheFileData() mWAVEXMPChunkPath.append( kRF64XMP, SizeOfCIArray(kRF64XMP) ); mWAVEInfoChunkPath.append( kRF64Info, SizeOfCIArray(kRF64Info) ); mWAVEDispChunkPath.append( kRF64Disp, SizeOfCIArray(kRF64Disp) ); + mWAVEiXMLChunkPath.append( kRF64iXML, SizeOfCIArray(kRF64iXML) ); mWAVEBextChunkPath.append( kRF64Bext, SizeOfCIArray(kRF64Bext) ); mWAVECartChunkPath.append( kRF64Cart, SizeOfCIArray(kRF64Cart) ); // cr8r is not yet required for WAVE @@ -216,6 +222,7 @@ void WAVE_MetaHandler::CacheFileData() mChunkController->addChunkPath( mWAVEXMPChunkPath ); mChunkController->addChunkPath( mWAVEInfoChunkPath ); mChunkController->addChunkPath( mWAVEDispChunkPath ); + mChunkController->addChunkPath( mWAVEiXMLChunkPath ); mChunkController->addChunkPath( mWAVEBextChunkPath ); mChunkController->addChunkPath( mWAVECartChunkPath ); // cr8r is not yet required for WAVE @@ -341,8 +348,17 @@ void WAVE_MetaHandler::ProcessXMP() // mCr8rMeta.parse( buffer, size ); //} + // Parse iXML legacy chunk + miXMLChunk = mChunkController->getChunk( mWAVEiXMLChunkPath, true ); + if( miXMLChunk != NULL ) + { + size = miXMLChunk->getData( &buffer ); + miXMLMeta.parse( buffer, size ); + } + // app legacy to the metadata list metaSet.append( &mINFOMeta ); + metaSet.append( &miXMLMeta ); metaSet.append( &mBEXTMeta ); metaSet.append( &mCartMeta ); metaSet.append( &mDISPMeta ); @@ -380,6 +396,7 @@ void WAVE_MetaHandler::UpdateFile ( bool doSafeUpdate ) WAVEReconcile recon; metaSet.append( &mINFOMeta ); + metaSet.append( &miXMLMeta ); metaSet.append( &mBEXTMeta ); metaSet.append( &mCartMeta ); metaSet.append( &mDISPMeta ); @@ -415,6 +432,11 @@ void WAVE_MetaHandler::UpdateFile ( bool doSafeUpdate ) //{ // updateLegacyChunk( &mCr8rChunk, kChunk_Cr8r, kType_NONE, mCr8rMeta ); //} + + if ( miXMLMeta.hasChanged( )) + { + updateLegacyChunk( &miXMLChunk, kChunk_iXML, kType_NONE, miXMLMeta ); + } } //update/create XMP chunk diff --git a/XMPFiles/source/FileHandlers/WAVE_Handler.hpp b/XMPFiles/source/FileHandlers/WAVE_Handler.hpp index f8cc31b..29c1c86 100644 --- a/XMPFiles/source/FileHandlers/WAVE_Handler.hpp +++ b/XMPFiles/source/FileHandlers/WAVE_Handler.hpp @@ -18,6 +18,7 @@ #include "XMPFiles/source/FormatSupport/IFF/IChunkData.h" #include "source/Endian.h" #include "XMPFiles/source/FormatSupport/IFF/ChunkPath.h" +#include "XMPFiles/source/FormatSupport/WAVE/iXMLMetadata.h" #include "XMPFiles/source/FormatSupport/WAVE/BEXTMetadata.h" #include "XMPFiles/source/FormatSupport/WAVE/CartMetadata.h" #include "XMPFiles/source/FormatSupport/WAVE/DISPMetadata.h" @@ -110,6 +111,7 @@ private: BEXTMetadata mBEXTMeta; CartMetadata mCartMeta; DISPMetadata mDISPMeta; + iXMLMetadata miXMLMeta; // cr8r is not yet required for WAVE // Cr8rMetadata mCr8rMeta; @@ -121,6 +123,7 @@ private: IChunkData *mBEXTChunk; IChunkData *mCartChunk; IChunkData *mDISPChunk; + IChunkData *miXMLChunk; // cr8r is not yet required for WAVE // IChunkData *mCr8rChunk; @@ -133,7 +136,7 @@ private: static const ChunkIdentifier kRIFFDisp[2]; static const ChunkIdentifier kRIFFBext[2]; static const ChunkIdentifier kRIFFCart[2]; - + static const ChunkIdentifier kRIFFiXML[2]; // cr8r is not yet required for WAVE // static const ChunkIdentifier kWAVECr8r[2]; @@ -143,7 +146,7 @@ private: static const ChunkIdentifier kRF64Disp[2]; static const ChunkIdentifier kRF64Bext[2]; static const ChunkIdentifier kRF64Cart[2]; - + static const ChunkIdentifier kRF64iXML[2]; // cr8r is not yet required for WAVE // static const ChunkIdentifier kRF64Cr8r[2]; @@ -162,6 +165,9 @@ private: /** Path to cart chunk */ ChunkPath mWAVECartChunkPath; + /** Path to IXML chunk */ + ChunkPath mWAVEiXMLChunkPath; + //cr8r is not yet required for WAVE ///** Path to Cr8r chunk */ //const ChunkPath mWAVECr8rChunkPath; diff --git a/XMPFiles/source/FileHandlers/XDCAMEX_Handler.cpp b/XMPFiles/source/FileHandlers/XDCAMEX_Handler.cpp index 9005fe8..826cc0b 100644 --- a/XMPFiles/source/FileHandlers/XDCAMEX_Handler.cpp +++ b/XMPFiles/source/FileHandlers/XDCAMEX_Handler.cpp @@ -388,7 +388,7 @@ bool XDCAMEX_MetaHandler::GetFileModDate ( XMP_DateTime * modDate ) ok = this->MakeMediaproPath ( &fullPath, true /* checkFile */ ); if ( ok ) ok = Host_IO::GetModifyDate ( fullPath.c_str(), &oneDate ); if ( ok ) { - if ( (! haveDate) || (*modDate < oneDate) ) *modDate = oneDate; + if ( *modDate < oneDate ) *modDate = oneDate; haveDate = true; } diff --git a/XMPFiles/source/FileHandlers/XDCAM_Handler.cpp b/XMPFiles/source/FileHandlers/XDCAM_Handler.cpp index f455a8c..2a05be9 100644 --- a/XMPFiles/source/FileHandlers/XDCAM_Handler.cpp +++ b/XMPFiles/source/FileHandlers/XDCAM_Handler.cpp @@ -488,20 +488,13 @@ void XDCAM_MetaHandler::SetSidecarPath() ( ( GetUns32BE(&buffer[12]) & 0xFFFF00FF ) == 0x01020000 ) ) { - // If cached MXF file name is present then use it otherwise - // side car generated on case insensitive OS may not be read on case sensitive OS. - // For example, if file name is X.MXF then windows says X.mxf is same as X.MXF so - // we may land up generating side car name as X.mxf.xmp which will not be read on - // Mac which will search specifically for X.MXF.xmp - XMP_VarString filePath = this->parent->GetFilePath(); - XMP_VarString ext; - XIO::SplitFileExtension(&filePath, &ext); - if(ext == "MXF" || ext == "mxf") - { - this->sidecarPath = this->parent->GetFilePath() + ".xmp"; - } - else + std::string pathtomxfclip=Host_IO::GetCasePreservedName(mxfFilePath); + if ( pathtomxfclip != "" ) { + std::string ext; + XIO::SplitFileExtension( &pathtomxfclip, &ext , false); + ext="."+ext; + MakeClipFilePath ( &mxfFilePath , ext.c_str() , false ); this->sidecarPath = mxfFilePath + ".xmp"; } } @@ -524,7 +517,7 @@ void XDCAM_MetaHandler::SetSidecarPath() // XDCAM_MetaHandler::XDCAM_MetaHandler // ==================================== -XDCAM_MetaHandler::XDCAM_MetaHandler ( XMPFiles * _parent ) : isFAM(false), expat(0) +XDCAM_MetaHandler::XDCAM_MetaHandler ( XMPFiles * _parent ) : isFAM(false), expat(0),clipMetadata(NULL) { this->parent = _parent; // Inherited, can't set in the prefix. @@ -737,7 +730,7 @@ bool XDCAM_MetaHandler::GetFileModDate ( XMP_DateTime * modDate ) ok = MakeMediaproPath ( &mediaproPath, true /* checkFile */ ); if ( ok ) ok = Host_IO::GetModifyDate ( mediaproPath.c_str(), &oneDate ); if ( ok ) { - if ( (! haveDate) || (*modDate < oneDate) ) *modDate = oneDate; + if ( (! haveDate) ) *modDate = oneDate; haveDate = true; } @@ -870,7 +863,7 @@ bool XDCAM_MetaHandler::IsClipsPlanning ( std::string clipUmid , XMP_StringPtr p { XML_NodePtr materialNode = mgNode->GetNamedElement( nameSpace, "Material" ); XMP_StringPtr materialType = materialNode->GetAttrValue ( "type" ); - if ( XMP_LitMatch( materialType , "clip" ) ) + if ( materialType && XMP_LitMatch( materialType , "clip" ) ) { XMP_StringPtr umidValue = materialNode->GetAttrValue ( "umidRef" ); if ( umidValue != 0 && XMP_LitMatch( umidValue , clipUmid.c_str() ) ) diff --git a/XMPFiles/source/FormatSupport/IFF/ChunkController.cpp b/XMPFiles/source/FormatSupport/IFF/ChunkController.cpp index 2a5a322..fc7c2c6 100644 --- a/XMPFiles/source/FormatSupport/IFF/ChunkController.cpp +++ b/XMPFiles/source/FormatSupport/IFF/ChunkController.cpp @@ -50,6 +50,8 @@ ChunkController::ChunkController( IChunkBehavior* chunkBehavior, XMP_Bool bigEnd ChunkController::~ChunkController() { + XMP_Validate( mRoot != NULL, "ERROR inserting Chunk. mRoot is NULL.", kXMPErr_InternalFailure ); + XMP_Assert(dynamic_cast<Chunk*>(mRoot) == static_cast<Chunk*>(mRoot)); delete dynamic_cast<Chunk*>(mRoot); } @@ -104,7 +106,9 @@ void ChunkController::parseChunks( XMP_IO* stream, ChunkPath& currentPath, XMP_O XMP_Bool isRoot = (parent == mRoot); XMP_Uns64 parseLimit = mFileSize; XMP_Uns32 chunkCnt = 0; - + + XMP_Validate( mRoot != NULL, "ERROR inserting Chunk. mRoot is NULL.", kXMPErr_InternalFailure ); + XMP_Assert(dynamic_cast<Chunk*>(mRoot) == static_cast<Chunk*>(mRoot)); parent = ( parent == NULL ? dynamic_cast<Chunk*>(mRoot) : parent ); // @@ -592,6 +596,8 @@ void ChunkController::findChunks( const ChunkPath& path, ChunkPath& currentPath, void ChunkController::cleanupTree() { + XMP_Validate( mRoot != NULL, "ERROR inserting Chunk. mRoot is NULL.", kXMPErr_InternalFailure ); + XMP_Assert(dynamic_cast<Chunk*>(mRoot) == static_cast<Chunk*>(mRoot)); delete dynamic_cast<Chunk*>(mRoot); mRoot = Chunk::createChunk(*mEndian); } @@ -659,6 +665,8 @@ IChunkData* ChunkController::createChunk( XMP_Uns32 id, XMP_Uns32 type /*= kType void ChunkController::insertChunk( IChunkData* chunk ) { XMP_Validate( chunk != NULL, "ERROR inserting Chunk. Chunk is NULL.", kXMPErr_InternalFailure ); + XMP_Assert(dynamic_cast<Chunk*>(chunk) == static_cast<Chunk*>(chunk)); + Chunk* ch = dynamic_cast<Chunk*>(chunk); mChunkBehavior->insertChunk( *mRoot, *ch ); // sets OriginalSize = Size / OriginalOffset = Offset diff --git a/XMPFiles/source/FormatSupport/IFF/ChunkPath.h b/XMPFiles/source/FormatSupport/IFF/ChunkPath.h index c0f149b..30ed3c0 100644 --- a/XMPFiles/source/FormatSupport/IFF/ChunkPath.h +++ b/XMPFiles/source/FormatSupport/IFF/ChunkPath.h @@ -57,6 +57,7 @@ enum { kChunk_bext = 0x62657874, kChunk_cart = 0x63617274, kChunk_ds64 = 0x64733634, + kChunk_iXML = 0x69584D4C, // AIFF kChunk_APPL = 0x4150504C, diff --git a/XMPFiles/source/FormatSupport/IPTC_Support.cpp b/XMPFiles/source/FormatSupport/IPTC_Support.cpp index e8fda45..a6ff864 100644 --- a/XMPFiles/source/FormatSupport/IPTC_Support.cpp +++ b/XMPFiles/source/FormatSupport/IPTC_Support.cpp @@ -337,14 +337,14 @@ size_t IPTC_Manager::GetDataSet_UTF8 ( XMP_Uns8 dsNum, std::string * utf8Str, si void IPTC_Manager::DisposeLooseValue ( DataSetInfo & dsInfo ) { - if ( dsInfo.dataLen == 0 ) return; + if ( dsInfo.dataLen == 0 || dsInfo.dataPtr == NULL ) return; XMP_Uns8* dataBegin = this->iptcContent; XMP_Uns8* dataEnd = dataBegin + this->iptcLength; if ( ((XMP_Uns8*)dsInfo.dataPtr < dataBegin) || ((XMP_Uns8*)dsInfo.dataPtr >= dataEnd) ) { free ( (void*) dsInfo.dataPtr ); - dsInfo.dataPtr = 0; + dsInfo.dataPtr = NULL; } } // IPTC_Manager::DisposeLooseValue diff --git a/XMPFiles/source/FormatSupport/ISOBaseMedia_Support.cpp b/XMPFiles/source/FormatSupport/ISOBaseMedia_Support.cpp index 670b8fb..d4a8076 100644 --- a/XMPFiles/source/FormatSupport/ISOBaseMedia_Support.cpp +++ b/XMPFiles/source/FormatSupport/ISOBaseMedia_Support.cpp @@ -22,6 +22,25 @@ namespace ISOMedia { +typedef std::set<XMP_Uns32> KnownBoxList; +static KnownBoxList boxList; +#define ISOboxType(x,y) boxList.insert(y) +#define SEPARATOR ; + bool IsKnownBoxType(XMP_Uns32 boxType) { + if (boxList.empty()){ + ISOBoxList ISOBoxPrivateList ; + } + if (boxList.find(boxType)!=boxList.end()){ + return true; + } + return false; + } + void TerminateGlobals() + { + boxList.clear(); + } +#undef ISOboxType +#undef SEPARATOR static BoxInfo voidInfo; // ================================================================================================= diff --git a/XMPFiles/source/FormatSupport/ISOBaseMedia_Support.hpp b/XMPFiles/source/FormatSupport/ISOBaseMedia_Support.hpp index dd2fbea..728293f 100644 --- a/XMPFiles/source/FormatSupport/ISOBaseMedia_Support.hpp +++ b/XMPFiles/source/FormatSupport/ISOBaseMedia_Support.hpp @@ -1,5 +1,5 @@ #ifndef __ISOBaseMedia_Support_hpp__ -#define __ISOBaseMedia_Support_hpp__ 1 +#define __ISOBaseMedia_Support_hpp__ 1 // ================================================================================================= // ADOBE SYSTEMS INCORPORATED @@ -10,13 +10,15 @@ // of the Adobe license agreement accompanying it. // ================================================================================================= -#include "public/include/XMP_Environment.h" // ! This must be the first include. +#include "public/include/XMP_Environment.h" // ! This must be the first include. #include "public/include/XMP_Const.h" #include "public/include/XMP_IO.hpp" #include "XMPFiles/source/XMPFiles_Impl.hpp" +#include <set> + // ================================================================================================= /// \file ISOBaseMedia_Support.hpp /// \brief XMPFiles support for the ISO Base Media File Format. @@ -27,82 +29,95 @@ namespace ISOMedia { +#define ISOBoxList \ + ISOboxType(k_ftyp,0x66747970UL)SEPARATOR /* File header Box, no version/flags.*/ \ + \ + ISOboxType(k_mp41,0x6D703431UL)SEPARATOR /* Compatible brand codes*/ \ + ISOboxType(k_mp42,0x6D703432UL)SEPARATOR \ + ISOboxType(k_f4v ,0x66347620UL)SEPARATOR \ + ISOboxType(k_avc1,0x61766331UL)SEPARATOR \ + ISOboxType(k_qt ,0x71742020UL)SEPARATOR \ + \ + ISOboxType(k_moov,0x6D6F6F76UL)SEPARATOR /* Container Box, no version/flags. */ \ + ISOboxType(k_mvhd,0x6D766864UL)SEPARATOR /* Data FullBox, has version/flags. */ \ + ISOboxType(k_hdlr,0x68646C72UL)SEPARATOR \ + ISOboxType(k_udta,0x75647461UL)SEPARATOR /* Container Box, no version/flags. */ \ + ISOboxType(k_cprt,0x63707274UL)SEPARATOR /* Data FullBox, has version/flags. */ \ + ISOboxType(k_uuid,0x75756964UL)SEPARATOR /* Data Box, no version/flags. */ \ + ISOboxType(k_free,0x66726565UL)SEPARATOR /* Free space Box, no version/flags.*/ \ + ISOboxType(k_mdat,0x6D646174UL)SEPARATOR /* Media data Box, no version/flags.*/ \ + \ + ISOboxType(k_trak,0x7472616BUL)SEPARATOR /* Types for the QuickTime timecode track.*/ \ + ISOboxType(k_tkhd,0x746B6864UL)SEPARATOR \ + ISOboxType(k_edts,0x65647473UL)SEPARATOR \ + ISOboxType(k_elst,0x656C7374UL)SEPARATOR \ + ISOboxType(k_mdia,0x6D646961UL)SEPARATOR \ + ISOboxType(k_mdhd,0x6D646864UL)SEPARATOR \ + ISOboxType(k_tmcd,0x746D6364UL)SEPARATOR \ + ISOboxType(k_mhlr,0x6D686C72UL)SEPARATOR \ + ISOboxType(k_minf,0x6D696E66UL)SEPARATOR \ + ISOboxType(k_stbl,0x7374626CUL)SEPARATOR \ + ISOboxType(k_stsd,0x73747364UL)SEPARATOR \ + ISOboxType(k_stsc,0x73747363UL)SEPARATOR \ + ISOboxType(k_stco,0x7374636FUL)SEPARATOR \ + ISOboxType(k_co64,0x636F3634UL)SEPARATOR \ + ISOboxType(k_dinf,0x64696E66UL)SEPARATOR \ + ISOboxType(k_dref,0x64726566UL)SEPARATOR \ + ISOboxType(k_alis,0x616C6973UL)SEPARATOR \ + \ + ISOboxType(k_meta,0x6D657461UL)SEPARATOR /* Types for the iTunes metadata boxes.*/ \ + ISOboxType(k_ilst,0x696C7374UL)SEPARATOR \ + ISOboxType(k_mdir,0x6D646972UL)SEPARATOR \ + ISOboxType(k_mean,0x6D65616EUL)SEPARATOR \ + ISOboxType(k_name,0x6E616D65UL)SEPARATOR \ + ISOboxType(k_data,0x64617461UL)SEPARATOR \ + ISOboxType(k_hyphens,0x2D2D2D2DUL)SEPARATOR \ + \ + ISOboxType(k_skip,0x736B6970UL)SEPARATOR /* Additional classic QuickTime top level boxes.*/ \ + ISOboxType(k_wide,0x77696465UL)SEPARATOR \ + ISOboxType(k_pnot,0x706E6F74UL)SEPARATOR \ + \ + ISOboxType(k_XMP_,0x584D505FUL) /* The QuickTime variant XMP box.*/ + +#define ISOBoxPrivateList +#define ISOboxType(x,y) x=y +#define SEPARATOR , enum { - k_ftyp = 0x66747970UL, // File header Box, no version/flags. - - k_mp41 = 0x6D703431UL, // Compatible brand codes - k_mp42 = 0x6D703432UL, - k_f4v = 0x66347620UL, - k_avc1 = 0x61766331UL, - k_qt = 0x71742020UL, - - k_moov = 0x6D6F6F76UL, // Container Box, no version/flags. - k_mvhd = 0x6D766864UL, // Data FullBox, has version/flags. - k_hdlr = 0x68646C72UL, - k_udta = 0x75647461UL, // Container Box, no version/flags. - k_cprt = 0x63707274UL, // Data FullBox, has version/flags. - k_uuid = 0x75756964UL, // Data Box, no version/flags. - k_free = 0x66726565UL, // Free space Box, no version/flags. - k_mdat = 0x6D646174UL, // Media data Box, no version/flags. - - k_trak = 0x7472616BUL, // Types for the QuickTime timecode track. - k_tkhd = 0x746B6864UL, - k_edts = 0x65647473UL, - k_elst = 0x656C7374UL, - k_mdia = 0x6D646961UL, - k_mdhd = 0x6D646864UL, - k_tmcd = 0x746D6364UL, - k_mhlr = 0x6D686C72UL, - k_minf = 0x6D696E66UL, - k_stbl = 0x7374626CUL, - k_stsd = 0x73747364UL, - k_stsc = 0x73747363UL, - k_stco = 0x7374636FUL, - k_co64 = 0x636F3634UL, - k_dinf = 0x64696E66UL, - k_dref = 0x64726566UL, - k_alis = 0x616C6973UL, - - k_meta = 0x6D657461UL, // Types for the iTunes metadata boxes. - k_ilst = 0x696C7374UL, - k_mdir = 0x6D646972UL, - k_mean = 0x6D65616EUL, - k_name = 0x6E616D65UL, - k_data = 0x64617461UL, - k_hyphens = 0x2D2D2D2DUL, - - k_skip = 0x736B6970UL, // Additional classic QuickTime top level boxes. - k_wide = 0x77696465UL, - k_pnot = 0x706E6F74UL, - - k_XMP_ = 0x584D505FUL // The QuickTime variant XMP box. + ISOBoxList + ISOBoxPrivateList }; +#undef ISOboxType +#undef SEPARATOR + + + bool IsKnownBoxType(XMP_Uns32 boxType) ; + void TerminateGlobals(); static XMP_Uns32 k_xmpUUID [4] = { MakeUns32BE ( 0xBE7ACFCBUL ), - MakeUns32BE ( 0x97A942E8UL ), - MakeUns32BE ( 0x9C719994UL ), - MakeUns32BE ( 0x91E3AFACUL ) }; + MakeUns32BE ( 0x97A942E8UL ), + MakeUns32BE ( 0x9C719994UL ), + MakeUns32BE ( 0x91E3AFACUL ) }; struct BoxInfo { - XMP_Uns32 boxType; // In memory as native endian! - XMP_Uns32 headerSize; // Normally 8 or 16, less than 8 if available space is too small. - XMP_Uns64 contentSize; // Always the real size, never 0 for "to EoF". + XMP_Uns32 boxType; // In memory as native endian! + XMP_Uns32 headerSize; // Normally 8 or 16, less than 8 if available space is too small. + XMP_Uns64 contentSize; // Always the real size, never 0 for "to EoF". BoxInfo() : boxType(0), headerSize(0), contentSize(0) {}; }; // Get basic info about a box in memory, returning a pointer to the following box. const XMP_Uns8 * GetBoxInfo ( const XMP_Uns8 * boxPtr, const XMP_Uns8 * boxLimit, - BoxInfo * info, bool throwErrors = false ); + BoxInfo * info, bool throwErrors = false ); // Get basic info about a box in a file, returning the offset of the following box. The I/O // pointer is left at the start of the box's content. Returns the offset of the following box. XMP_Uns64 GetBoxInfo ( XMP_IO* fileRef, const XMP_Uns64 boxOffset, const XMP_Uns64 boxLimit, - BoxInfo * info, bool doSeek = true, bool throwErrors = false ); + BoxInfo * info, bool doSeek = true, bool throwErrors = false ); -// XMP_Uns32 CountChildBoxes ( XMP_IO* fileRef, const XMP_Uns64 childOffset, const XMP_Uns64 childLimit ); + // XMP_Uns32 CountChildBoxes ( XMP_IO* fileRef, const XMP_Uns64 childOffset, const XMP_Uns64 childLimit ); -} // namespace ISO_Media +} // namespace ISO_Media // ================================================================================================= -#endif // __ISOBaseMedia_Support_hpp__ +#endif // __ISOBaseMedia_Support_hpp__ diff --git a/XMPFiles/source/FormatSupport/MOOV_Support.hpp b/XMPFiles/source/FormatSupport/MOOV_Support.hpp index 1dace2a..7d34ca2 100644 --- a/XMPFiles/source/FormatSupport/MOOV_Support.hpp +++ b/XMPFiles/source/FormatSupport/MOOV_Support.hpp @@ -166,9 +166,9 @@ public: #pragma pack( pop ) -#if SUNOS_SPARC +#if SUNOS_SPARC || XMP_IOS_ARM #pragma pack( ) -#endif //#if SUNOS_SPARC +#endif //#if SUNOS_SPARC || XMP_IOS_ARM // --------------------------------------------------------------------------------------------- diff --git a/XMPFiles/source/FormatSupport/P2_Support.cpp b/XMPFiles/source/FormatSupport/P2_Support.cpp new file mode 100644 index 0000000..a5336d5 --- /dev/null +++ b/XMPFiles/source/FormatSupport/P2_Support.cpp @@ -0,0 +1,566 @@ + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2014 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "public/include/XMP_Environment.h" // ! XMP_Environment.h must be the first included header. + +#include "public/include/XMP_Const.h" +#include "public/include/XMP_IO.hpp" + +#include "XMPFiles/source/XMPFiles_Impl.hpp" +#include "source/XMPFiles_IO.hpp" +#include "source/XIO.hpp" +#include "source/ExpatAdapter.hpp" + +#include "source/IOUtils.hpp" + +#include "XMPFiles/source/FormatSupport/P2_Support.hpp" +#include "third-party/zuid/interfaces/MD5.h" +#include <sstream> + +P2_Clip::P2_Clip(const std::string & p2ClipMetadataFilePath) + try :p2XMLParser(0),p2Root(0),headContentCached(false) + ,p2ClipContent(0),filePath(p2ClipMetadataFilePath) +{ + Host_IO::FileRef hostRef = Host_IO::Open ( p2ClipMetadataFilePath.c_str(), Host_IO::openReadOnly ); + XMPFiles_IO xmlFile ( hostRef, p2ClipMetadataFilePath.c_str(), Host_IO::openReadOnly ); + CreateExpatParser(xmlFile); + xmlFile.Close(); +} +catch(...) +{ + DestroyExpatParser(); + throw; +} + +P2_Clip::~P2_Clip() +{ + DestroyExpatParser(); +} + +void P2_Clip::CreateExpatParser(XMPFiles_IO &xmlFile) +{ + this->p2XMLParser = XMP_NewExpatAdapter ( ExpatAdapter::kUseLocalNamespaces ); + if ( this->p2XMLParser == 0 ) XMP_Throw ( "P2_MetaHandler: Can't create Expat adapter", kXMPErr_NoMemory ); + + XMP_Uns8 buffer [64*1024]; + while ( true ) { + XMP_Int32 ioCount = xmlFile.Read ( buffer, sizeof(buffer) ); + if ( ioCount == 0 ) break; + this->p2XMLParser->ParseBuffer ( buffer, ioCount, false /* not the end */ ); + } + this->p2XMLParser->ParseBuffer ( 0, 0, true ); +} + +void P2_Clip::DestroyExpatParser() +{ + delete this->p2XMLParser; + this->p2XMLParser = 0; + p2Root=0; + headContent.reset(); + headContentCached = false; +} + +XML_NodePtr P2_Clip::GetP2RootNode() +{ + if (p2Root!=0) return p2Root; + // The root element should be P2Main in some namespace. At least 2 different namespaces are in + // use (ending in "v3.0" and "v3.1"). Take whatever this file uses. + + XML_Node & xmlTree = this->p2XMLParser->tree; + XML_NodePtr rootElem = 0; + + for ( size_t i = 0, limit = xmlTree.content.size(); i < limit; ++i ) { + if ( xmlTree.content[i]->kind == kElemNode ) { + rootElem = xmlTree.content[i]; + } + } + + if ( rootElem == 0 ) return 0; + XMP_StringPtr rootLocalName = rootElem->name.c_str() + rootElem->nsPrefixLen; + if ( ! XMP_LitMatch ( rootLocalName, "P2Main" ) ) return 0; + + this->p2Root = rootElem; + return p2Root; +} +static void GetElementLocation(XML_NodePtr p2node,std::string*& elemLoc ) +{ + if ( p2node != 0 && p2node->IsLeafContentNode() ) + { + elemLoc= p2node->GetLeafContentPtr(); + } +} + +static void GetElementValue(XML_NodePtr p2node, XMP_Uns32 &value) +{ + if ( p2node != 0 && p2node->IsLeafContentNode() ) + { + value =atoi(p2node->GetLeafContentValue()); + } +} +void P2_Clip::CacheClipContent() +{ + if (headContentCached) return; + headContentCached = true; + XMP_StringPtr p2NameSpace=GetP2RootNode()->ns.c_str(); + p2ClipContent = GetP2RootNode()->GetNamedElement ( p2NameSpace, "ClipContent" ); + if ( p2ClipContent == 0 ) return; + XML_NodePtr p2node; + + p2node= p2ClipContent->GetNamedElement ( p2NameSpace, "GlobalClipID" ); + GetElementLocation(p2node,headContent.clipId ); + + p2node= p2ClipContent->GetNamedElement ( p2NameSpace, "ClipName" ); + GetElementLocation(p2node,headContent.clipTitle ); + + p2node= p2ClipContent->GetNamedElement ( p2NameSpace, "Duration" ); + GetElementValue(p2node,headContent.duration ); + + p2node= p2ClipContent->GetNamedElement ( p2NameSpace, "EditUnit" ); + GetElementLocation(p2node,headContent.scaleUnit ); + + headContent.clipMetadata= p2ClipContent->GetNamedElement ( p2NameSpace, "ClipMetadata" ); + headContent.essenceList= p2ClipContent->GetNamedElement ( p2NameSpace, "EssenceList" ); + + p2node= p2ClipContent->GetNamedElement ( p2NameSpace, "Relation" ); + if ( p2node != 0 ) + { + XML_NodePtr p2Offset= p2node->GetNamedElement ( p2NameSpace, "OffsetInShot" ); + GetElementValue(p2Offset,headContent.OffsetInShot ); + p2Offset= p2node->GetNamedElement ( p2NameSpace, "GlobalShotID" ); + GetElementLocation(p2Offset,headContent.shotId ); + XML_NodePtr p2connection= p2node->GetNamedElement ( p2NameSpace, "Connection" ); + if ( p2node != 0 ) + { + p2node= p2connection->GetNamedElement ( p2NameSpace, "Top" ); + if ( p2node != 0 ) + { + p2node= p2node->GetNamedElement ( p2NameSpace, "GlobalClipID" ); + GetElementLocation(p2node,headContent.topClipId ); + } + p2node= p2connection->GetNamedElement ( p2NameSpace, "Next" ); + if ( p2node != 0 ) + { + p2node= p2node->GetNamedElement ( p2NameSpace, "GlobalClipID" ); + GetElementLocation(p2node,headContent.nextClipId ); + } + p2node= p2connection->GetNamedElement ( p2NameSpace, "Previous" ); + if ( p2node != 0 ) + { + p2node= p2node->GetNamedElement ( p2NameSpace, "GlobalClipID" ); + GetElementLocation(p2node,headContent.prevClipId ); + } + } + } +} + +bool P2_Clip::IsValidClip() +{ + this->CacheClipContent(); + return headContent.clipId != 0; +} +bool P2_Clip::IsSpannedClip() +{ + return IsValidClip() && headContent.topClipId != 0 &&( headContent.prevClipId != 0 || headContent.nextClipId!=0 ); + +} + +bool P2_Clip::IsTopClip() +{ + return IsValidClip() && headContent.topClipId != 0 && *(headContent.topClipId) == *(headContent.clipId); +} + +XMP_Uns32 P2_Clip::GetOffsetInShot() +{ + this->CacheClipContent(); + return this->headContent.OffsetInShot; +} + +XMP_Uns32 P2_Clip::GetDuration() +{ + this->CacheClipContent(); + return this->headContent.duration; +} + +std::string* P2_Clip::GetClipId() +{ + this->CacheClipContent(); + return this->headContent.clipId; +} + +std::string* P2_Clip::GetClipName() +{ + if ( this->clipName == "" ) + { + std::string tempPath = this->filePath; + XIO::SplitLeafName(&tempPath, &this->clipName); + std::string ext; + XIO::SplitFileExtension(&this->clipName, &ext); + } + return &this->clipName; +} + +std::string P2_Clip::GetClipTitle() +{ + this->CacheClipContent(); + if ( ! this->headContent.clipTitle ) return std::string(""); + return *this->headContent.clipTitle; +} +std::string* P2_Clip::GetNextClipId() +{ + this->CacheClipContent(); + return this->headContent.nextClipId; +} + +std::string* P2_Clip::GetPreviousClipId() +{ + this->CacheClipContent(); + return this->headContent.prevClipId; +} + +std::string* P2_Clip::GetTopClipId() +{ + this->CacheClipContent(); + return this->headContent.topClipId; +} + +std::string* P2_Clip::GetShotId() +{ + this->CacheClipContent(); + return this->headContent.shotId; +} + +std::string* P2_Clip::GetEditUnit() +{ + this->CacheClipContent(); + return this->headContent.scaleUnit; +} + +XML_NodePtr P2_Clip::GetClipContentNode() +{ + this->CacheClipContent(); + return this->p2ClipContent; +} + +XML_NodePtr P2_Clip::GetClipMetadataNode() +{ + this->CacheClipContent(); + return this->headContent.clipMetadata; +} + +XML_NodePtr P2_Clip::GetEssenceListNode() +{ + this->CacheClipContent(); + return this->headContent.essenceList; +} + +std::string P2_Clip::GetXMPFilePath() +{ + std::string ClipMetadataPath = this->GetClipPath(); + std::string ignoreext; + XIO::SplitFileExtension(&ClipMetadataPath,&ignoreext); + return ClipMetadataPath+ ".XMP"; +} + +void P2_Clip::CreateDigest ( std::string * digestStr ) +{ + return; +} + +void P2_Clip::SerializeP2ClipContent(std::string& xmlContentData) +{ + this->p2XMLParser->tree.Serialize ( &xmlContentData ); +} + + +P2_SpannedClip::P2_SpannedClip(const std::string & p2ClipMetadataFilePath): + P2_Clip(p2ClipMetadataFilePath) +{ + P2_Clip* p2Clip= dynamic_cast<P2_Clip*>(this); + spannedP2Clip.insert(p2Clip); + if (p2Clip->GetClipId()) + addedClipIds.insert(*p2Clip->GetClipId()); +} + +bool P2_SpannedClip::AddIfRelated(P2_Clip* newClip) +{ + std::string* tClipId = newClip->GetTopClipId(); + if( tClipId != 0 && *(tClipId)==*this->GetTopClipId() && + newClip->IsValidClip() && addedClipIds.find(*newClip->GetClipId()) == addedClipIds.end()) + { + spannedP2Clip.insert(newClip); + addedClipIds.insert(*newClip->GetClipId()); + return true; + } + return false; +} + +bool P2_SpannedClip::IsComplete() const +{ + RelatedP2ClipList::iterator iter=spannedP2Clip.begin(); + if (! (*iter)->IsTopClip() ) return false; + std::string* next=(*iter)->GetNextClipId(); + while(++iter != spannedP2Clip.end() && + next != 0 && (*iter)->IsValidClip() && + *next == *( (*iter)->GetClipId() ) + ) + next = (*iter)->GetNextClipId(); + if ( iter != spannedP2Clip.end() || next != 0 ) + { + iter=spannedP2Clip.begin(); + std::string* prev= (*iter)->GetClipId(); + while(++iter != spannedP2Clip.end() && + prev != 0 && (*iter)->GetPreviousClipId() !=0 && + *prev == *( (*iter)->GetPreviousClipId() ) + ) + prev= (*iter)->GetClipId(); + if ( iter != spannedP2Clip.end() ) return false; + } + return true; +} + +std::string P2_SpannedClip::GetXMPFilePath() +{ + if ( this->IsComplete() ) + { + std::string ClipMetadataPath = (*spannedP2Clip.begin())->GetClipPath(); + std::string ignoreext; + XIO::SplitFileExtension(&ClipMetadataPath,&ignoreext); + return ClipMetadataPath+ ".XMP"; + }else + { + return P2_Clip::GetXMPFilePath(); + } +} + +void P2_SpannedClip::DigestElement( MD5_CTX & md5Context, XML_NodePtr legacyContext, XMP_StringPtr legacyPropName ) +{ + XML_NodePtr legacyProp = legacyContext->GetNamedElement ( this->GetP2RootNode()->ns.c_str(), legacyPropName ); + + if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() && (! legacyProp->content.empty()) ) { + const XML_Node * xmlValue = legacyProp->content[0]; + MD5Update ( &md5Context, (XMP_Uns8*)xmlValue->value.c_str(), (unsigned int)xmlValue->value.size() ); + } + +} // P2_MetaHandler::DigestLegacyItem +#define kHexDigits "0123456789ABCDEF" + +void P2_SpannedClip::CreateDigest ( std::string * digestStr ) +{ + digestStr->erase(); + if ( this->headContent.clipMetadata == 0 ) return; // Bail if we don't have any legacy XML. + + XMP_StringPtr p2NS = this->GetP2RootNode()->ns.c_str(); + XML_NodePtr legacyContext; + MD5_CTX md5Context; + unsigned char digestBin [16]; + MD5Init ( &md5Context ); + + MD5Update ( &md5Context, (XMP_Uns8*)this->GetClipTitle().c_str(), (unsigned int)this->GetClipTitle().size() ); + if ( headContent.clipId ) + MD5Update ( &md5Context, (XMP_Uns8*)headContent.clipId->c_str(), (unsigned int)headContent.clipId->size() ); + + XMP_Uns32 totalDuration=this->GetDuration(); + std::ostringstream ostr; + ostr << totalDuration; + if ( totalDuration ) + MD5Update ( &md5Context, (XMP_Uns8*)ostr.str().c_str(), (unsigned int)ostr.str().size() ); + if ( headContent.scaleUnit ) + MD5Update ( &md5Context, (XMP_Uns8*)headContent.scaleUnit->c_str(), (unsigned int)headContent.scaleUnit->size() ); + + if ( headContent.shotId ) + MD5Update ( &md5Context, (XMP_Uns8*)headContent.shotId->c_str(), (unsigned int)headContent.shotId->size() ); + if ( headContent.topClipId ) + MD5Update ( &md5Context, (XMP_Uns8*)headContent.topClipId->c_str(), (unsigned int)headContent.topClipId->size() ); + if ( headContent.prevClipId ) + MD5Update ( &md5Context, (XMP_Uns8*)headContent.prevClipId->c_str(), (unsigned int)headContent.prevClipId->size() ); + if ( headContent.nextClipId ) + MD5Update ( &md5Context, (XMP_Uns8*)headContent.nextClipId->c_str(), (unsigned int)headContent.nextClipId->size() ); + + if ( this->headContent.essenceList != 0 ) { + + XML_NodePtr videoContext = this->headContent.essenceList->GetNamedElement ( p2NS, "Video" ); + + if ( videoContext != 0 ) { + this->DigestElement ( md5Context, videoContext, "AspectRatio" ); + this->DigestElement ( md5Context, videoContext, "Codec" ); + this->DigestElement ( md5Context, videoContext, "FrameRate" ); + this->DigestElement ( md5Context, videoContext, "StartTimecode" ); + } + + XML_NodePtr audioContext = this->headContent.essenceList->GetNamedElement ( p2NS, "Audio" ); + + if ( audioContext != 0 ) { + this->DigestElement ( md5Context, audioContext, "SamplingRate" ); + this->DigestElement ( md5Context, audioContext, "BitsPerSample" ); + } + + } + + legacyContext = this->headContent.clipMetadata; + this->DigestElement ( md5Context, legacyContext, "UserClipName" ); + this->DigestElement ( md5Context, legacyContext, "ShotMark" ); + + legacyContext = this->headContent.clipMetadata->GetNamedElement ( p2NS, "Access" ); + /* Rather return than create the digest because the "Access" element is listed as "required" in the P2 spec. + So a P2 file without an "Access" element does not follow the spec and might be corrupt.*/ + if ( legacyContext == 0 ) return; + + this->DigestElement ( md5Context, legacyContext, "Creator" ); + this->DigestElement ( md5Context, legacyContext, "CreationDate" ); + this->DigestElement ( md5Context, legacyContext, "LastUpdateDate" ); + + legacyContext = this->headContent.clipMetadata->GetNamedElement ( p2NS, "Shoot" ); + + if ( legacyContext != 0 ) { + this->DigestElement ( md5Context, legacyContext, "Shooter" ); + + legacyContext = legacyContext->GetNamedElement ( p2NS, "Location" ); + + if ( legacyContext != 0 ) { + this->DigestElement ( md5Context, legacyContext, "PlaceName" ); + this->DigestElement ( md5Context, legacyContext, "Longitude" ); + this->DigestElement ( md5Context, legacyContext, "Latitude" ); + this->DigestElement ( md5Context, legacyContext, "Altitude" ); + } + } + + legacyContext = this->headContent.clipMetadata->GetNamedElement ( p2NS, "Scenario" ); + + if ( legacyContext != 0 ) { + this->DigestElement ( md5Context, legacyContext, "SceneNo." ); + this->DigestElement ( md5Context, legacyContext, "TakeNo." ); + } + + legacyContext = this->headContent.clipMetadata->GetNamedElement ( p2NS, "Device" ); + + if ( legacyContext != 0 ) { + this->DigestElement ( md5Context, legacyContext, "Manufacturer" ); + this->DigestElement ( md5Context, legacyContext, "SerialNo." ); + this->DigestElement ( md5Context, legacyContext, "ModelName" ); + } + + MD5Final ( digestBin, &md5Context ); + + char buffer [40]; + for ( int in = 0, out = 0; in < 16; in += 1, out += 2 ) { + XMP_Uns8 byte = digestBin[in]; + buffer[out] = kHexDigits [ byte >> 4 ]; + buffer[out+1] = kHexDigits [ byte & 0xF ]; + } + buffer[32] = 0; + digestStr->append ( buffer ); + +} + +P2_SpannedClip::~P2_SpannedClip() +{ + RelatedP2ClipList::iterator iter = spannedP2Clip.begin(); + for(;iter!=spannedP2Clip.end();iter++) + { + if (GetClipPath() != (*iter)->GetClipPath()) + delete *iter; + } + spannedP2Clip.clear(); +} + +P2_Clip* P2_SpannedClip::TopP2Clip() +{ + if ( this->IsComplete() && spannedP2Clip.size() > 1 ) + { + return *spannedP2Clip.begin(); + } + return this; +} + +XMP_Uns32 P2_SpannedClip::GetDuration() +{ + if ( IsComplete() ) + { + RelatedP2ClipList::iterator iter = this->spannedP2Clip.begin(); + XMP_Uns32 totalDuration=0; + for(;iter!=spannedP2Clip.end();iter++) + totalDuration+=(*iter)->GetDuration(); + return totalDuration; + } + return P2_Clip::GetDuration(); +} + +void P2_SpannedClip::GetAllClipNames(std::vector <std::string> & clipNameList) +{ + clipNameList.clear(); + if ( IsComplete() ) + { + RelatedP2ClipList::iterator iter = this->spannedP2Clip.begin(); + for(;iter!=spannedP2Clip.end();iter++) + clipNameList.push_back(*( (*iter)->GetClipName() ) ); + }else + { + clipNameList.push_back(*( this->GetClipName() ) ); + } +} + +P2_Manager::P2_Manager():spannedClips(0) +{ + +} + +P2_Manager::~P2_Manager() +{ + delete this->spannedClips; + this->spannedClips = 0; +} + +void P2_Manager::ProcessClip(std::string & clipPath) +{ + this->spannedClips = new P2_SpannedClip(clipPath); + if ( this->spannedClips->IsSpannedClip()) + { + std::string clipFolder,filename,regExp; + XMP_StringVector clipFileList,regExpVec; + clipFolder=clipPath; + XIO::SplitLeafName ( &clipFolder, &filename ); + regExp = "^\\d\\d\\d\\d\\d\\d.XML$"; + regExpVec.push_back ( regExp ); + regExp = "^\\d\\d\\d\\d\\W\\W.XML$"; + regExpVec.push_back ( regExp ); + regExp = "^\\d\\d\\d\\d\\d\\W.XML$"; + regExpVec.push_back ( regExp ); + regExp = "^\\d\\d\\d\\d\\W\\d.XML$"; + regExpVec.push_back ( regExp ); + IOUtils::GetMatchingChildren ( clipFileList, clipFolder, regExpVec, false, true, true ); + for(XMP_StringVector::iterator iter=clipFileList.begin(); + iter!=clipFileList.end();iter++) + { + P2_Clip * tempClip= new P2_Clip(*iter); + if ( ! spannedClips->AddIfRelated(tempClip) ) + delete tempClip; + } + if(spannedClips->IsComplete()) + { + return; + } + } +} + +bool P2_Manager::IsValidP2() +{ + return spannedClips!= 0; +} + +P2_Clip* P2_Manager::GetManagedClip() +{ + return spannedClips->TopP2Clip(); +} + +P2_SpannedClip* P2_Manager::GetSpannedClip() +{ + return spannedClips; +} + diff --git a/XMPFiles/source/FormatSupport/P2_Support.hpp b/XMPFiles/source/FormatSupport/P2_Support.hpp new file mode 100644 index 0000000..e16faea --- /dev/null +++ b/XMPFiles/source/FormatSupport/P2_Support.hpp @@ -0,0 +1,135 @@ +#ifndef __P2_Support_hpp__ +#define __P2_Support_hpp__ 1 + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2014 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "public/include/XMP_Environment.h" +#include "public/include/XMP_Const.h" + +#include "XMPFiles/source/XMPFiles_Impl.hpp" +#include "source/XIO.hpp" +#include "third-party/zuid/interfaces/MD5.h" +#include <set> + +class P2_Clip { +public: + P2_Clip(const std::string & p2ClipMetadataFilePath); + bool IsSpannedClip() ; + bool IsTopClip() ; + bool IsValidClip() ; + virtual void CreateDigest ( std::string * digestStr ); + XMP_Uns32 GetOffsetInShot(); + XMP_Uns32 GetDuration(); + std::string* GetClipName(); + std::string GetClipTitle(); + std::string* GetClipId(); + std::string* GetNextClipId(); + std::string* GetPreviousClipId(); + std::string* GetTopClipId(); + std::string* GetShotId(); + std::string* GetEditUnit(); + std::string GetClipPath(){return filePath;} + virtual std::string GetXMPFilePath(); + XML_NodePtr GetClipContentNode(); + XML_NodePtr GetClipMetadataNode(); + XML_NodePtr GetEssenceListNode(); + XML_NodePtr GetP2RootNode() ; + void SerializeP2ClipContent(std::string& xmlContentData) ; + virtual ~P2_Clip(); +protected: + class ClipContent + { + public: + ClipContent():clipId(0),scaleUnit(0), + duration(0),OffsetInShot(0),topClipId(0),nextClipId(0), + prevClipId(0),shotId(0),clipMetadata(0),essenceList(0),clipTitle(0){} + std::string* clipTitle; + std::string* clipId; + std::string* scaleUnit; + XMP_Uns32 duration; + XMP_Uns32 OffsetInShot; + std::string* topClipId; + std::string* nextClipId; + std::string* prevClipId; + std::string* shotId; + XML_NodePtr clipMetadata; + XML_NodePtr essenceList; + void reset(){*this=ClipContent();} + }; + ClipContent headContent; +private: + void DestroyExpatParser(); + void CreateExpatParser(XMPFiles_IO &xmlFile); + void CacheClipContent(); + + bool headContentCached; + ExpatAdapter * p2XMLParser; + XML_NodePtr p2Root; + XML_NodePtr p2ClipContent; + std::string filePath; + std::string clipName; + +}; // class P2_Clip +struct P2SpannedClip_Order +{ + bool operator()( P2_Clip* lhs, P2_Clip* rhs) + { + return lhs->GetOffsetInShot() < rhs->GetOffsetInShot(); + } + +}; + +class P2_SpannedClip : public P2_Clip{ +public: + P2_SpannedClip(const std::string & p2ClipMetadataFilePath); + bool AddIfRelated(P2_Clip* openedClip); + bool IsComplete()const; + XMP_Uns32 GetDuration(); + P2_Clip* TopP2Clip() ; + std::string GetXMPFilePath(); + void CreateDigest ( std::string * digestStr ); + void GetAllClipNames(std::vector <std::string> & clipNameList); + virtual ~P2_SpannedClip(); +private: + P2_SpannedClip(const P2_SpannedClip &); + P2_SpannedClip operator=(const P2_SpannedClip &); + + void DigestElement( MD5_CTX & md5Context, XML_NodePtr legacyContext, XMP_StringPtr legacyPropName ); + + typedef std::multiset<P2_Clip*,P2SpannedClip_Order> RelatedP2ClipList; + std::set<std::string> addedClipIds; + RelatedP2ClipList spannedP2Clip; + +}; // class P2_SpannedClip + +// ================================================================================================= +class P2_Manager { +public: + P2_Manager(); + void ProcessClip(std::string & clipPath); + P2_Clip* GetManagedClip(); + P2_SpannedClip* GetSpannedClip(); + bool IsValidP2(); + ~P2_Manager(); + +private: + + P2_SpannedClip* spannedClips; + +}; // class P2_Manager + + +// ================================================================================================= + + + +// ================================================================================================= + +#endif // __P2_Support_hpp__ diff --git a/XMPFiles/source/FormatSupport/PostScript_Support.cpp b/XMPFiles/source/FormatSupport/PostScript_Support.cpp index a0c13d2..fec55fb 100644 --- a/XMPFiles/source/FormatSupport/PostScript_Support.cpp +++ b/XMPFiles/source/FormatSupport/PostScript_Support.cpp @@ -148,10 +148,8 @@ inline static bool SearchBBoxInTrailer(XMP_IO* fileRef,IOBuffer& ioBuf) //skip chars till newline if ( ! PostScript_Support::SkipUntilNewline( fileRef, ioBuf ) ) return false; } - if (!bboxfoundintrailer) - return false; - else - break; + + break; } else if ( CheckBytes ( ioBuf.ptr, Uns8Ptr(kPSContainsBeginDocString.c_str()), kPSContainsBeginDocString.length() ) ) { @@ -267,7 +265,8 @@ bool PostScript_Support::IsValidPSFile(XMP_IO* fileRef,XMP_FileFormat &format format=kXMP_PostScriptFile; //return true if no "EPSF-" is found as it is a valid PS atleast if ( ! CheckBytes ( ioBuf.ptr, Uns8Ptr("EPSF-"), 5 ) ) return true; - } + + }//intentional fall through for further checking of unknown files case kXMP_EPSFile: { diff --git a/XMPFiles/source/FormatSupport/QuickTime_Support.cpp b/XMPFiles/source/FormatSupport/QuickTime_Support.cpp index 5dde0c5..8e2d45a 100644 --- a/XMPFiles/source/FormatSupport/QuickTime_Support.cpp +++ b/XMPFiles/source/FormatSupport/QuickTime_Support.cpp @@ -12,6 +12,9 @@ #if XMP_MacBuild #include <CoreServices/CoreServices.h> +#elif XMP_iOSBuild + #include <CoreFoundation/CoreFoundation.h> + #include "XMPFiles/source/FormatSupport/MacScriptExtracts.h" #else #include "XMPFiles/source/FormatSupport/MacScriptExtracts.h" #endif @@ -354,7 +357,7 @@ static const char * kMacToXMPLang_128_151 [24] = { #if XMP_WinBuild -static UINT kMacScriptToWinCP[34] = { +static UINT kMacScriptToWinCP[33] = { /* smRoman (0) */ 10000, // There don't seem to be symbolic constants. /* smJapanese (1) */ 10001, // From http://msdn.microsoft.com/en-us/library/dd317756(VS.85).aspx /* smTradChinese (2) */ 10002, @@ -383,8 +386,7 @@ static UINT kMacScriptToWinCP[34] = { /* smSimpChinese (25) */ 10008, /* smTibetan (26) */ 0, /* smMongolian (27) */ 0, - /* smEthiopic (28) */ 0, - /* smGeez (28) */ 0, + /* smEthiopic/smGeez (28) */ 0, /* smCentralEuroRoman (29) */ 10029, /* smVietnamese (30) */ 0, /* smExtArabic (31) */ 0, @@ -502,6 +504,156 @@ static UINT kMacToWinCP_0_94 [95] = { #endif + +#if XMP_iOSBuild + +static XMP_Uns32 kMacScriptToIOSEncodingCF[33] = { + /* smRoman (0) */ kCFStringEncodingMacRoman, + /* smJapanese (1) */ kCFStringEncodingMacJapanese, + /* smTradChinese (2) */ kCFStringEncodingMacChineseTrad, + /* smKorean (3) */ kCFStringEncodingMacKorean, + /* smArabic (4) */ kCFStringEncodingMacArabic, + /* smHebrew (5) */ kCFStringEncodingMacHebrew, + /* smGreek (6) */ kCFStringEncodingMacGreek, + /* smCyrillic (7) */ kCFStringEncodingMacCyrillic, + /* smRSymbol (8) */ kCFStringEncodingMacSymbol, + /* smDevanagari (9) */ kCFStringEncodingMacDevanagari, + /* smGurmukhi (10) */ kCFStringEncodingMacGurmukhi, + /* smGujarati (11) */ kCFStringEncodingMacGujarati, + /* smOriya (12) */ kCFStringEncodingMacOriya, + /* smBengali (13) */ kCFStringEncodingMacBengali, + /* smTamil (14) */ kCFStringEncodingMacTamil, + /* smTelugu (15) */ kCFStringEncodingMacTelugu, + /* smKannada (16) */ kCFStringEncodingMacKannada, + /* smMalayalam (17) */ kCFStringEncodingMacMalayalam, + /* smSinhalese (18) */ kCFStringEncodingMacSinhalese, + /* smBurmese (19) */ kCFStringEncodingMacBurmese, + /* smKhmer (20) */ kCFStringEncodingMacKhmer, + /* smThai (21) */ kCFStringEncodingMacThai, + /* smLao (22) */ kCFStringEncodingMacLaotian, + /* smGeorgian (23) */ kCFStringEncodingMacGeorgian, + /* smArmenian (24) */ kCFStringEncodingMacArmenian, + /* smSimpChinese (25) */ kCFStringEncodingMacChineseSimp, + /* smTibetan (26) */ kCFStringEncodingMacTibetan, + /* smMongolian (27) */ kCFStringEncodingMacMongolian, + /* smEthiopic/smGeez (28) */ kCFStringEncodingMacEthiopic, + /* smCentralEuroRoman (29) */ kCFStringEncodingMacCentralEurRoman, + /* smVietnamese (30) */ kCFStringEncodingMacVietnamese, + /* smExtArabic (31) */ kCFStringEncodingMacExtArabic, + /* smUninterp (32) */ kCFStringEncodingMacVT100 +}; // kMacScriptToIOSEncodingCF + +static XMP_Uns32 kMacToIOSEncodingCF_0_94 [95] = { + + /* langEnglish (0) */ kCFStringEncodingMacRoman, + /* langFrench (1) */ kCFStringEncodingMacRoman, + /* langGerman (2) */ kCFStringEncodingMacRoman, + /* langItalian (3) */ kCFStringEncodingMacRoman, + /* langDutch (4) */ kCFStringEncodingMacRoman, + /* langSwedish (5) */ kCFStringEncodingMacRoman, + /* langSpanish (6) */ kCFStringEncodingMacRoman, + /* langDanish (7) */ kCFStringEncodingMacRoman, + /* langPortuguese (8) */ kCFStringEncodingMacRoman, + /* langNorwegian (9) */ kCFStringEncodingMacRoman, + + /* langHebrew (10) */ kCFStringEncodingMacHebrew, + /* langJapanese (11) */ kCFStringEncodingMacJapanese, + /* langArabic (12) */ kCFStringEncodingMacArabic, + /* langFinnish (13) */ kCFStringEncodingMacRoman, + /* langGreek (14) */ kCFStringEncodingMacGreek, + /* langIcelandic (15) */ kCFStringEncodingMacIcelandic, + /* langMaltese (16) */ kCFStringEncodingMacRoman, + /* langTurkish (17) */ kCFStringEncodingMacTurkish, + /* langCroatian (18) */ kCFStringEncodingMacCroatian, + /* langTradChinese (19) */ kCFStringEncodingMacChineseTrad, + + /* langUrdu (20) */ kCFStringEncodingMacArabic, + /* langHindi (21) */ kCFStringEncodingMacDevanagari, + /* langThai (22) */ kCFStringEncodingMacThai, + /* langKorean (23) */ kCFStringEncodingMacKorean, + /* langLithuanian (24) */ kCFStringEncodingMacCentralEurRoman, + /* langPolish (25) */ kCFStringEncodingMacCentralEurRoman, + /* langHungarian (26) */ kCFStringEncodingMacCentralEurRoman, + /* langEstonian (27) */ kCFStringEncodingMacCentralEurRoman, + /* langLatvian (28) */ kCFStringEncodingMacCentralEurRoman, + /* langSami (29) */ kCFStringEncodingInvalidId, + + /* langFaroese (30) */ kCFStringEncodingMacRoman, + /* langFarsi (31) */ kCFStringEncodingMacFarsi, + /* langRussian (32) */ kCFStringEncodingMacCyrillic, + /* langSimpChinese (33) */ kCFStringEncodingMacChineseSimp, + /* langFlemish (34) */ kCFStringEncodingMacRoman, + /* langIrishGaelic (35) */ kCFStringEncodingMacRoman, + /* langAlbanian (36) */ kCFStringEncodingMacRoman, + /* langRomanian (37) */ kCFStringEncodingMacRomanian, + /* langCzech (38) */ kCFStringEncodingMacCentralEurRoman, + /* langSlovak (39) */ kCFStringEncodingMacCentralEurRoman, + + /* langSlovenian (40) */ kCFStringEncodingMacRoman, + /* langYiddish (41) */ kCFStringEncodingMacHebrew, + /* langSerbian (42) */ kCFStringEncodingMacCyrillic, + /* langMacedonian (43) */ kCFStringEncodingMacCyrillic, + /* langBulgarian (44) */ kCFStringEncodingMacCyrillic, + /* langUkrainian (45) */ kCFStringEncodingMacUkrainian, + /* langBelorussian (46) */ kCFStringEncodingMacCyrillic, + /* langUzbek (47) */ kCFStringEncodingMacCyrillic, + /* langKazakh (48) */ kCFStringEncodingMacCyrillic, + /* langAzerbaijani (49) */ kCFStringEncodingMacCyrillic, + + /* langAzerbaijanAr (50) */ kCFStringEncodingMacArabic, + /* langArmenian (51) */ kCFStringEncodingMacArmenian, + /* langGeorgian (52) */ kCFStringEncodingMacGeorgian, + /* langMoldavian (53) */ kCFStringEncodingMacCyrillic, + /* langKirghiz (54) */ kCFStringEncodingMacCyrillic, + /* langTajiki (55) */ kCFStringEncodingMacCyrillic, + /* langTurkmen (56) */ kCFStringEncodingMacCyrillic, + /* langMongolian (57) */ kCFStringEncodingMacMongolian, + /* langMongolianCyr (58) */ kCFStringEncodingMacCyrillic, + /* langPashto (59) */ kCFStringEncodingMacArabic, + + /* langKurdish (60) */ kCFStringEncodingMacArabic, + /* langKashmiri (61) */ kCFStringEncodingMacArabic, + /* langSindhi (62) */ kCFStringEncodingMacArabic, + /* langTibetan (63) */ kCFStringEncodingMacTibetan, + /* langNepali (64) */ kCFStringEncodingMacDevanagari, + /* langSanskrit (65) */ kCFStringEncodingMacDevanagari, + /* langMarathi (66) */ kCFStringEncodingMacDevanagari, + /* langBengali (67) */ kCFStringEncodingMacBengali, + /* langAssamese (68) */ kCFStringEncodingMacBengali, + /* langGujarati (69) */ kCFStringEncodingMacGujarati, + + /* langPunjabi (70) */ kCFStringEncodingMacGurmukhi, + /* langOriya (71) */ kCFStringEncodingMacOriya, + /* langMalayalam (72) */ kCFStringEncodingMacMalayalam, + /* langKannada (73) */ kCFStringEncodingMacKannada, + /* langTamil (74) */ kCFStringEncodingMacTamil, + /* langTelugu (75) */ kCFStringEncodingMacTelugu, + /* langSinhalese (76) */ kCFStringEncodingMacSinhalese, + /* langBurmese (77) */ kCFStringEncodingMacBurmese, + /* langKhmer (78) */ kCFStringEncodingMacKhmer, + /* langLao (79) */ kCFStringEncodingMacLaotian, + + /* langVietnamese (80) */ kCFStringEncodingMacVietnamese, + /* langIndonesian (81) */ kCFStringEncodingMacRoman, + /* langTagalog (82) */ kCFStringEncodingMacRoman, + /* langMalayRoman (83) */ kCFStringEncodingMacRoman, + /* langMalayArabic (84) */ kCFStringEncodingMacArabic, + /* langAmharic (85) */ kCFStringEncodingMacEthiopic, + /* langTigrinya (86) */ kCFStringEncodingMacEthiopic, + /* langOromo (87) */ kCFStringEncodingMacEthiopic, + /* langSomali (88) */ kCFStringEncodingMacRoman, + /* langSwahili (89) */ kCFStringEncodingMacRoman, + + /* langKinyarwanda (90) */ kCFStringEncodingMacRoman, + /* langRundi (91) */ kCFStringEncodingMacRoman, + /* langNyanja (92) */ kCFStringEncodingMacRoman, + /* langMalagasy (93) */ kCFStringEncodingMacRoman, + /* langEsperanto (94) */ kCFStringEncodingMacRoman + +}; // kMacToIOSEncodingCF_0_94 + +#endif + // ================================================================================================= // GetMacScript // ============ @@ -513,13 +665,35 @@ static XMP_Uns16 GetMacScript ( XMP_Uns16 macLang ) if ( macLang <= 94 ) { macScript = kMacLangToScript_0_94[macLang]; } else if ( (128 <= macLang) && (macLang <= 151) ) { - macScript = kMacLangToScript_0_94[macLang-128]; + macScript = kMacLangToScript_128_151[macLang-128]; } return macScript; } // GetMacScript + +#if XMP_iOSBuild +// ================================================================================================= +// GetIOSEncodingCF +// ======== + +static XMP_Uns32 GetIOSEncodingCF ( XMP_Uns16 macLang ) +{ + XMP_Uns32 encCF = kCFStringEncodingInvalidId; + + if ( macLang <= 94 ) encCF = kMacToIOSEncodingCF_0_94[macLang]; + + if ( encCF == kCFStringEncodingInvalidId || !CFStringIsEncodingAvailable(encCF)) { + XMP_Uns16 macScript = GetMacScript ( macLang ); + if ( macScript != kNoMacScript ) encCF = kMacScriptToIOSEncodingCF[macScript]; + } + + return encCF; + +} // GetIOSEncodingCF +#endif + // ================================================================================================= // GetWinCP // ======== @@ -669,7 +843,10 @@ bool ConvertToMacLang ( const std::string & utf8Value, XMP_Uns16 macLang, std::s #elif XMP_WinBuild UINT winCP = GetWinCP ( macLang ); ReconcileUtils::UTF8ToWinEncoding ( winCP, (XMP_Uns8*)utf8Value.c_str(), utf8Value.size(), macValue ); - #endif + #elif XMP_iOSBuild + XMP_Uns32 iosEncCF = GetIOSEncodingCF(macLang); + ReconcileUtils::IOSConvertEncoding(kCFStringEncodingUTF8, iosEncCF, (XMP_Uns8*)utf8Value.c_str(), utf8Value.size(), macValue); + #endif return true; @@ -692,7 +869,10 @@ bool ConvertFromMacLang ( const std::string & macValue, XMP_Uns16 macLang, std:: #elif XMP_WinBuild UINT winCP = GetWinCP ( macLang ); ReconcileUtils::WinEncodingToUTF8 ( winCP, (XMP_Uns8*)macValue.c_str(), macValue.size(), utf8Value ); - #endif + #elif XMP_iOSBuild + XMP_Uns32 iosEncCF = GetIOSEncodingCF(macLang); + ReconcileUtils::IOSConvertEncoding(iosEncCF, kCFStringEncodingUTF8, (XMP_Uns8*)macValue.c_str(), macValue.size(), utf8Value); +#endif return true; @@ -988,7 +1168,7 @@ void TradQT_Manager::ExportLangAltXMP ( XMP_Uns32 id, const SXMPMeta & xmp, XMP_ for ( XMP_Index xmpIndex = 1; xmpIndex <= xmpCount; ++xmpIndex ) { // ! XMP index starts at 1! SXMPUtils::ComposeArrayItemPath ( ns, langArray, xmpIndex, &xmpPath ); - xmp.GetProperty ( ns, xmpPath.c_str(), &xmpValue, 0 ); + if ( !xmp.GetProperty ( ns, xmpPath.c_str(), &xmpValue, 0 ) ) continue; xmp.GetQualifier ( ns, xmpPath.c_str(), kXMP_NS_XML, "lang", &xmpLang, 0 ); if ( xmpLang == "x-default" ) continue; diff --git a/XMPFiles/source/FormatSupport/RIFF.cpp b/XMPFiles/source/FormatSupport/RIFF.cpp index dcf55f2..4d9a0c1 100644 --- a/XMPFiles/source/FormatSupport/RIFF.cpp +++ b/XMPFiles/source/FormatSupport/RIFF.cpp @@ -137,6 +137,7 @@ Chunk* getChunk ( ContainerChunk* parent, RIFF_MetaHandler* handler ) // ad hoc creation Chunk::Chunk( ContainerChunk* parent, ChunkType c, XMP_Uns32 id ) { + this->hasChange = false; this->chunkType = c; // base class assumption this->parent = parent; this->id = id; diff --git a/XMPFiles/source/FormatSupport/ReconcileTIFF.cpp b/XMPFiles/source/FormatSupport/ReconcileTIFF.cpp index 5aa4435..e43a1e5 100644 --- a/XMPFiles/source/FormatSupport/ReconcileTIFF.cpp +++ b/XMPFiles/source/FormatSupport/ReconcileTIFF.cpp @@ -469,12 +469,12 @@ ImportSingleTIFF_SRational ( const TIFF_Manager::TagInfo & tagInfo, const bool n { try { // Don't let errors with one stop the others. -#if SUNOS_SPARC +#if SUNOS_SPARC || XMP_IOS_ARM XMP_Uns32 binPtr[2]; memcpy(&binPtr, tagInfo.dataPtr, sizeof(XMP_Uns32)*2); #else XMP_Uns32 * binPtr = (XMP_Uns32*)tagInfo.dataPtr; -#endif //#if SUNOS_SPARC +#endif //#if SUNOS_SPARC || XMP_IOS_ARM XMP_Int32 binNum = GetUns32AsIs ( &binPtr[0] ); XMP_Int32 binDenom = GetUns32AsIs ( &binPtr[1] ); if ( ! nativeEndian ) { @@ -1289,7 +1289,7 @@ static void ImportTIFF_Date ( const TIFF_Manager & tiff, const TIFF_Manager::TagInfo & dateInfo, SXMPMeta * xmp, const char * xmpNS, const char * xmpProp ) { - XMP_Uns16 secID; + XMP_Uns16 secID = 0; switch ( dateInfo.id ) { case kTIFF_DateTime : secID = kTIFF_SubSecTime; break; case kTIFF_DateTimeOriginal : secID = kTIFF_SubSecTimeOriginal; break; @@ -2584,7 +2584,8 @@ ExportArrayTIFF ( TIFF_Manager * tiff, XMP_Uns8 ifd, const TIFF_MappingToXMP & m XMP_Uns32 num, denom; for ( size_t i = 1; i <= arraySize; ++i, rationalPtr += 2 ) { SXMPUtils::ComposeArrayItemPath ( xmpNS, xmpArray, (XMP_Index)i, &itemPath ); - xmp.GetProperty ( xmpNS, itemPath.c_str(), &xmpValue, 0 ); + bool isPropoerty = xmp.GetProperty ( xmpNS, itemPath.c_str(), &xmpValue, 0 ); + if ( ! isPropoerty ) return; bool ok = DecodeRational ( xmpValue.c_str(), &num, &denom ); if ( ! ok ) return; if ( ! nativeEndian ) { num = Flip4 ( num ); denom = Flip4 ( denom ); } @@ -2680,7 +2681,7 @@ static void ExportTIFF_Date ( const SXMPMeta & xmp, const char * xmpNS, const char * xmpProp, TIFF_Manager * tiff, XMP_Uns16 mainID ) { XMP_Uns8 mainIFD = kTIFF_ExifIFD; - XMP_Uns16 fracID; + XMP_Uns16 fracID=0; switch ( mainID ) { case kTIFF_DateTime : mainIFD = kTIFF_PrimaryIFD; fracID = kTIFF_SubSecTime; break; case kTIFF_DateTimeOriginal : fracID = kTIFF_SubSecTimeOriginal; break; @@ -3086,7 +3087,7 @@ static void ExportTIFF_PhotographicSensitivity ( SXMPMeta * xmp, TIFF_Manager * TIFF_Manager::TagInfo tagInfo; std::string xmpValue; XMP_OptionBits flags; - XMP_Int32 binValue; + XMP_Int32 binValue = 0; bool haveOldExif = true; // Default to old Exif if no version tag. diff --git a/XMPFiles/source/FormatSupport/Reconcile_Impl.cpp b/XMPFiles/source/FormatSupport/Reconcile_Impl.cpp index 9dcef3d..ca0e1a0 100644 --- a/XMPFiles/source/FormatSupport/Reconcile_Impl.cpp +++ b/XMPFiles/source/FormatSupport/Reconcile_Impl.cpp @@ -17,6 +17,8 @@ #if XMP_WinBuild #elif XMP_MacBuild #include <CoreServices/CoreServices.h> +#elif XMP_iOSBuild + #include <CoreFoundation/CoreFoundation.h> #endif // ================================================================================================= @@ -201,6 +203,13 @@ void ReconcileUtils::UTF8ToLocal ( const void * _utf8Ptr, size_t utf8Len, std::s #elif XMP_UNIXBuild XMP_Throw ( "Generic UNIX does not have conversions between local and Unicode", kXMPErr_Unavailable ); + + #elif XMP_iOSBuild + + IOSConvertEncoding(kCFStringEncodingUTF8, CFStringGetSystemEncoding(), utf8Ptr, utf8Len, local); + + + #endif @@ -371,6 +380,11 @@ void ReconcileUtils::LocalToUTF8 ( const void * _localPtr, size_t localLen, std: #elif XMP_UNIXBuild XMP_Throw ( "Generic UNIX does not have conversions between local and Unicode", kXMPErr_Unavailable ); + + #elif XMP_iOSBuild + + IOSConvertEncoding(CFStringGetSystemEncoding(), kCFStringEncodingUTF8, localPtr, localLen, utf8); + #endif @@ -429,3 +443,29 @@ void ReconcileUtils::NativeToUTF8( const std::string & input, std::string & outp output = input; } } // ReconcileUtils::NativeToUTF8 + + +#if XMP_iOSBuild + void ReconcileUtils::IOSConvertEncoding(XMP_Uns32 srcEncoding, XMP_Uns32 destEncoding, const XMP_Uns8 * inputPtr, size_t inputLen, std::string * output) + { + if(srcEncoding == kCFStringEncodingInvalidId || destEncoding == kCFStringEncodingInvalidId || + !CFStringIsEncodingAvailable(srcEncoding) || !CFStringIsEncodingAvailable(destEncoding)) + return; + CFStringRef cStrRef = CFStringCreateWithBytesNoCopy(NULL, inputPtr, inputLen, srcEncoding, false, kCFAllocatorNull); + if(cStrRef == NULL) + return; + CFRange inputRange = CFRangeMake(0, CFStringGetLength(cStrRef)); + const size_t kBufferLen = 1000; + while(inputRange.length > 0) + { + XMP_Uns8 buffer[kBufferLen]; + CFIndex charsWritten; + CFIndex charsProcessed = CFStringGetBytes(cStrRef, inputRange, destEncoding, 0, FALSE, buffer, kBufferLen, &charsWritten); + if (charsProcessed == 0) break; + output->append(reinterpret_cast<const char*>(&buffer[0]), charsWritten); + inputRange.location += charsProcessed; + inputRange.length -= charsProcessed; + } + CFRelease(cStrRef); + } +#endif diff --git a/XMPFiles/source/FormatSupport/Reconcile_Impl.hpp b/XMPFiles/source/FormatSupport/Reconcile_Impl.hpp index ea5d407..4efcb3f 100644 --- a/XMPFiles/source/FormatSupport/Reconcile_Impl.hpp +++ b/XMPFiles/source/FormatSupport/Reconcile_Impl.hpp @@ -58,6 +58,8 @@ namespace ReconcileUtils { #elif XMP_MacBuild void UTF8ToMacEncoding ( XMP_Uns16 macScript, XMP_Uns16 macLang, const XMP_Uns8 * utf8Ptr, size_t utf8Len, std::string * host ); void MacEncodingToUTF8 ( XMP_Uns16 macScript, XMP_Uns16 macLang, const XMP_Uns8 * hostPtr, size_t hostLen, std::string * utf8 ); + #elif XMP_iOSBuild + void IOSConvertEncoding(XMP_Uns32 srcEncoding, XMP_Uns32 destEncoding, const XMP_Uns8 * inputPtr, size_t inputLen, std::string * output); #endif }; // ReconcileUtils diff --git a/XMPFiles/source/FormatSupport/TIFF_MemoryReader.cpp b/XMPFiles/source/FormatSupport/TIFF_MemoryReader.cpp index 05cd87f..a3b6ace 100644 --- a/XMPFiles/source/FormatSupport/TIFF_MemoryReader.cpp +++ b/XMPFiles/source/FormatSupport/TIFF_MemoryReader.cpp @@ -93,7 +93,7 @@ void TIFF_MemoryReader::SortIFD ( TweakedIFDInfo* thisIFD ) } else { // Move the out of order entry to position j+1, move the middle of the array down. - #if ! SUNOS_SPARC + #if ! (SUNOS_SPARC || XMP_IOS_ARM) TweakedIFDEntry temp = ifdEntries[i]; ++j; // ! So the insertion index becomes j. memcpy ( &ifdEntries[j+1], &ifdEntries[j], 12*(i-j) ); // AUDIT: Safe, moving less than i entries to a location before i. @@ -655,7 +655,7 @@ XMP_Uns32 TIFF_MemoryReader::ProcessOneIFD ( XMP_Uns32 ifdOffset, XMP_Uns8 ifd ) if ( (GetUns16AsIs(&thisEntry->type) < kTIFF_ByteType) || (GetUns16AsIs(&thisEntry->type) > kTIFF_LastType) ) continue; // Bad type, skip this tag. - #if ! SUNOS_SPARC + #if ! (SUNOS_SPARC || XMP_IOS_ARM) thisEntry->bytes *= (XMP_Uns32)kTIFF_TypeSizes[thisEntry->type]; if ( thisEntry->bytes > 4 ) { diff --git a/XMPFiles/source/FormatSupport/TIFF_Support.hpp b/XMPFiles/source/FormatSupport/TIFF_Support.hpp index 002376b..b43fe42 100644 --- a/XMPFiles/source/FormatSupport/TIFF_Support.hpp +++ b/XMPFiles/source/FormatSupport/TIFF_Support.hpp @@ -25,11 +25,11 @@ #include "source/Endian.h" -#if SUNOS_SPARC +#if SUNOS_SPARC || XMP_IOS_ARM static const IEndian &IE = BigEndian::getInstance(); #else static const IEndian &IE = LittleEndian::getInstance(); -#endif //#if SUNOS_SPARC +#endif //#if SUNOS_SPARC || XMP_IOS_ARM // ================================================================================================= /// \file TIFF_Support.hpp diff --git a/XMPFiles/source/FormatSupport/TimeConversionUtils.cpp b/XMPFiles/source/FormatSupport/TimeConversionUtils.cpp new file mode 100644 index 0000000..2dbb459 --- /dev/null +++ b/XMPFiles/source/FormatSupport/TimeConversionUtils.cpp @@ -0,0 +1,599 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2014 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "public/include/XMP_Environment.h" // ! XMP_Environment.h must be the first included header. +#include "public/include/XMP_Const.h" + +#include "XMPFiles/source/FormatSupport/TimeConversionUtils.hpp" + +#include <string> +#include <sstream> +#include <iomanip> +#include <cmath> + +namespace TimeConversionUtils { + + void DropFrameToHMSF( + XMP_Int64 inFrames, + XMP_Int64 inTimecodeFPS, + XMP_Uns32& outHours, + XMP_Uns32& outMinutes, + XMP_Uns32& outSeconds, + XMP_Uns32& outFrames) + { + XMP_Assert((inTimecodeFPS == 30) || (inTimecodeFPS == 60)); // No other drop frame rates are known at this time. + + XMP_Int64 rateAdjustmentFactor = inTimecodeFPS / 30; + XMP_Int64 framesPerHour = (30 * 3600 - 108) * rateAdjustmentFactor; + XMP_Int64 framesPer10Minutes = (30 * 600 - 18) * rateAdjustmentFactor; + XMP_Int64 framesPerMinute = 30 * 60 * rateAdjustmentFactor; + XMP_Int64 framesPerSecond = 30 * rateAdjustmentFactor; + XMP_Int64 dropsPerMinute = 2 * rateAdjustmentFactor; + + XMP_Int64 currentFrames = inFrames; + XMP_Int64 framesLeft = currentFrames; + if (currentFrames < 0) + { + framesLeft = -currentFrames; + } + if (framesLeft >= framesPerHour) + { + outHours = static_cast<XMP_Int32>(framesLeft / framesPerHour); + framesLeft = framesLeft % framesPerHour; + } + if (framesLeft >= framesPer10Minutes) + { + outMinutes = static_cast<XMP_Int32>(framesLeft / framesPer10Minutes) * 10; + framesLeft = framesLeft % framesPer10Minutes; + } + if (framesLeft >= framesPerMinute) + { + XMP_Int64 remainingDropMinutes = static_cast<XMP_Int64>((framesLeft - framesPerMinute) / + (framesPerMinute - dropsPerMinute)); + ++remainingDropMinutes; + + outMinutes += static_cast<XMP_Int32>(remainingDropMinutes); + framesLeft -= ((framesPerMinute - dropsPerMinute) * remainingDropMinutes); + } + if (framesLeft >= framesPerSecond) + { + outSeconds = static_cast<XMP_Int32>(framesLeft / framesPerSecond); + } + outFrames = static_cast<XMP_Int32>(framesLeft % framesPerSecond); + } + + bool ConvertSamplesToTimecode( + std::string & outTimecode, + XMP_Int64 inSamples, + XMP_Uns64 inSampleRate, + XMP_Int64 inTimecodeFPS, + bool inIsDrop, + bool inIsNoDrop, + bool inShowOnlyFrames = false, + bool inOnlyShowSeconds = false , + bool inNoZeroPrefix = false , + bool inShowFractional = false , + bool inNoHours = false ) + { + if (!(inIsDrop ? !inIsNoDrop : true)) + { + XMP_Assert( !(inIsDrop ? !inIsNoDrop : true) ); + return false; + } + + if (inSampleRate == 0) + { + outTimecode = "00:00:00:00"; + return true; + } + + std::string possibleNegStr; + if (inSamples < 0) + { + inSamples *= -1; + possibleNegStr = "-"; + } + + XMP_Int64 rateNumerator = inTimecodeFPS; + XMP_Int64 rateDenominator = 1; + if (inIsDrop || inIsNoDrop) + { + rateNumerator = 1000 * inTimecodeFPS; + rateDenominator = 1001; + } + + XMP_Int64 frameNumber = (inSamples * rateNumerator) / (inSampleRate * rateDenominator); + XMP_Int64 hundredthsOfFrames = ((inSamples * rateNumerator * 100) / (inSampleRate * rateDenominator)) % 100; + + std::stringstream stream; + double fSamples = static_cast<double>(inSamples); + double fSampleRate = static_cast<double>(inSampleRate); + + if (inIsDrop) + { + if (inShowOnlyFrames) + { + double fAdjustmentFactor = static_cast<double>(inTimecodeFPS) / 30.0; + double fCorrectionRatio = (600.0 * static_cast<double>(inTimecodeFPS) / 1.001) / (17982.0 * fAdjustmentFactor); + double fValue = fSamples * fCorrectionRatio / fSampleRate; + + // "%ld" + stream << static_cast<int>(fValue * 29.97 * fAdjustmentFactor); + } + else + { + XMP_Uns32 hours = 0; + XMP_Uns32 minutes = 0; + XMP_Uns32 seconds = 0; + XMP_Uns32 frames = 0; + + DropFrameToHMSF( + frameNumber, + inTimecodeFPS, + hours, + minutes, + seconds, + frames); + + hours = hours % 24; + // "%02d;%02d;%02d;%02d" + stream << possibleNegStr << std::setfill('0') << std::setw(2) << static_cast<int>(hours) + << ";" + << std::setfill('0') << std::setw(2) << static_cast<int>(minutes) + << ";" + << std::setfill('0') << std::setw(2) << static_cast<int>(seconds) + << ";" + << std::setfill('0') << std::setw(2) << static_cast<int>(frames); + possibleNegStr.clear(); + } + } + else + { + if (inShowOnlyFrames) + { + // "%ld" + stream << static_cast<int>(frameNumber); + } + else + { + XMP_Int64 framesPerMinute = inTimecodeFPS * 60; + XMP_Int64 framesPerHour = framesPerMinute * 60; + + XMP_Int64 iHours = frameNumber / framesPerHour; + frameNumber %= framesPerHour; + XMP_Int64 mins = frameNumber / framesPerMinute; + frameNumber %= framesPerMinute; + XMP_Int64 seconds = frameNumber / inTimecodeFPS; + XMP_Int64 ss = frameNumber % inTimecodeFPS; + XMP_Int64 s = seconds; + + if (inNoHours) + { + mins += iHours * 60; + iHours = 0; + } + + if (((iHours) || (!inNoZeroPrefix)) && (!inNoHours)) + { + iHours = iHours % 24; + // "%02ld:" + stream << possibleNegStr << std::setfill('0') << std::setw(2) << static_cast<int>(iHours) + << ":"; + possibleNegStr.clear(); + } + + if ((iHours) || (!inNoZeroPrefix)) + { + // "%02ld:" + stream << possibleNegStr << std::setfill('0') << std::setw(2) << static_cast<int>(mins) + << ":"; + possibleNegStr.clear(); + } + else if (mins) + { + // "%ld:" + stream << possibleNegStr << static_cast<int>(mins) + << ":"; + possibleNegStr.clear(); + } + + if (inOnlyShowSeconds) + { + // "%02ld" + stream << possibleNegStr << std::setfill('0') << std::setw(2) << static_cast<int>(s); + possibleNegStr.clear(); + } + else + { + if ((iHours) || (mins) || (!inNoZeroPrefix)) + { + // "%02ld:" + stream << possibleNegStr << std::setfill('0') << std::setw(2) << static_cast<int>(s) + << ":"; + possibleNegStr.clear(); + } + else if (s) + { + // "%ld:" + stream << possibleNegStr << static_cast<int>(s) + << ":"; + possibleNegStr.clear(); + } + + if ((iHours) || (mins) || (s) || (!inNoZeroPrefix)) + { + if (inTimecodeFPS <= 10) + { + // "%01ld" + stream << possibleNegStr << std::setfill('0') << std::setw(1) << static_cast<int>(ss); + possibleNegStr.clear(); + } + else if ((inTimecodeFPS <= 100)) + { + // "%02ld" + stream << possibleNegStr << std::setfill('0') << std::setw(2) << static_cast<int>(ss); + possibleNegStr.clear(); + } + else if (inTimecodeFPS <= 1000) + { + // "%03ld" + stream << possibleNegStr << std::setfill('0') << std::setw(3) << static_cast<int>(ss); + possibleNegStr.clear(); + } + else + { + // "%04ld" + stream << possibleNegStr << std::setfill('0') << std::setw(4) << static_cast<int>(ss); + possibleNegStr.clear(); + } + } + else + { + // "%ld" + stream << possibleNegStr << static_cast<int>(ss); + possibleNegStr.clear(); + } + + if (inShowFractional) + { + // ".%02d" + stream << possibleNegStr << "." + << std::setfill('0') << std::setw(2) << static_cast<int>(hundredthsOfFrames); + possibleNegStr.clear(); + } + } + } + } + + outTimecode = stream.str(); + + return true; + } + + bool ConvertSamplesToSMPTETimecode( + std::string & outTimecode, + XMP_Int64 inSamples, + XMP_Uns64 inSampleRate, + const std::string & inTimecodeFormat ) + { + bool result = false; + + if ( inTimecodeFormat.compare( "24Timecode" ) == 0 ) { + result = ConvertSamplesToTimecode( outTimecode, inSamples, inSampleRate, 24, false, false ); + } else if ( inTimecodeFormat.compare( "25Timecode" ) == 0 ) { + result = ConvertSamplesToTimecode( outTimecode, inSamples, inSampleRate, 25, false, false ); + } else if ( inTimecodeFormat.compare( "2997DropTimecode") == 0 ) { + result = ConvertSamplesToTimecode( outTimecode, inSamples, inSampleRate, 30, true, false ); + } else if ( inTimecodeFormat.compare( "2997NonDropTimecode") == 0 ) { + result = ConvertSamplesToTimecode( outTimecode, inSamples, inSampleRate, 30, false, true ); + } else if ( inTimecodeFormat.compare( "30Timecode" ) == 0 ) { + result = ConvertSamplesToTimecode( outTimecode, inSamples, inSampleRate, 30, false, false ); + } else if ( inTimecodeFormat.compare( "50Timecode" ) == 0 ) { + result = ConvertSamplesToTimecode( outTimecode, inSamples, inSampleRate, 50, false, false ); + } else if ( inTimecodeFormat.compare( "5994DropTimecode" ) == 0 ) { + result = ConvertSamplesToTimecode( outTimecode, inSamples, inSampleRate, 60, true, false ); + } else if ( inTimecodeFormat.compare( "5994NonDropTimecode" ) == 0 ) { + result = ConvertSamplesToTimecode( outTimecode, inSamples, inSampleRate, 60, false, true ); + } else if ( inTimecodeFormat.compare( "60Timecode" ) == 0 ) { + result = ConvertSamplesToTimecode( outTimecode, inSamples, inSampleRate, 60, false, false ); + } else if ( inTimecodeFormat.compare( "23976Timecode" ) == 0 ) { + result = ConvertSamplesToTimecode(outTimecode, inSamples, inSampleRate, 24, false, true); + } + return result; + } + + bool StringToNumber( + XMP_Int32 & outNumber, + const std::string & inString ) + { + bool numberFound = false; + outNumber = 0; + for ( size_t i = 0, endIndex = inString.size(); i < endIndex; i++ ) { + XMP_Int32 digit = inString[i] - '0'; + if ( digit >= 0 && digit <= 9 ) { + outNumber *= 10; + outNumber += digit; + numberFound = true; + } else { + return numberFound; + } + } + return numberFound; + } + + void ParseTimeCodeString( + const std::string & inTimecode, + XMP_Int32 & outHours, + XMP_Int32 & outMinutes, + XMP_Int32 & outSeconds, + XMP_Int32 & outFrames, + XMP_Int32 & outFractionalFrameNumerator, + XMP_Int32 & outFractionalFrameDenominator ) + { + XMP_Int32 m1 = 0; + XMP_Int32 m2 = 0; + XMP_Int32 m3 = 0; + XMP_Int32 m4 = 0; + XMP_Int32 m5 = 0; + bool hasFoundDecimal = false; + XMP_Int32 digitCount = 0; + + outFractionalFrameNumerator = 0; + outFractionalFrameDenominator = 1; + + std::string::const_iterator iter = inTimecode.begin(); + std::string::const_iterator iterEnd = inTimecode.end(); + + while (1) + { // Skip leading white space + while ( iter != iterEnd && (*iter < '0' || *iter > '9') ) + { + if (*iter == '.') + hasFoundDecimal = true; + iter++; + } // hh:mm:ss:ff.ddd + if (iter == iterEnd) + break; + + if (!hasFoundDecimal) + { + // get MSB digits + StringToNumber(m1, std::string(iter, iterEnd)); + + // Skip the digits + while (iter != iterEnd && (*iter >= '0' && *iter <= '9')) + iter++; + + // Skip the white space, note if "." or ":" ("." signifies decimal portion of frame) + while ( iter != iterEnd && (*iter < '0' || *iter > '9')) + { + if (*iter == '.') + hasFoundDecimal = true; + iter++; + } + + if (iter == iterEnd) + break; + } + // shift and scan next MSB digits + + if (!hasFoundDecimal) + { + m2 = m1; + digitCount = static_cast< XMP_Int32 >( iterEnd - iter ); + StringToNumber(m1, std::string(iter, iterEnd)); + + // Skip the digits + while ( iter != iterEnd && (*iter >= '0' && *iter <= '9') ) + iter++; + + // Skip the white space, note if "." or ":" ("." signifies + // decimal portion of frame) + while (iter != iterEnd && (*iter < '0' || *iter > '9')) + { + if (*iter == '.') + hasFoundDecimal = true; + iter++; + } + } + + if (iter == iterEnd) + break; + + m3 = m2; + m2 = m1; + digitCount = static_cast< XMP_Int32 >( iterEnd - iter ); + StringToNumber(m1, std::string(iter, iterEnd)); + if (hasFoundDecimal) + break; + + while (iter != iterEnd && (*iter >= '0' && *iter <= '9')) + iter++; + + // Skip the white space, note if "." or ":" ("." signifies decimal portion of frame) + while (iter != iterEnd && (*iter < '0' || *iter > '9')) + { + if (*iter == '.') + hasFoundDecimal = true; + iter++; + } + if (iter == iterEnd) + break; + + m4 = m3; + m3 = m2; + m2 = m1; + digitCount = static_cast< XMP_Int32 >( iterEnd - iter ); + StringToNumber(m1, std::string(iter, iterEnd)); + if (hasFoundDecimal) + break; + + while (iter != iterEnd && (*iter >= '0' && *iter <= '9')) + { + iter++; + } + + // Skip the white space, note if "." or ":" ("." signifies decimal portion of frame) + while (iter != iterEnd && (*iter < '0' || *iter > '9')) + { + if (*iter == '.') + hasFoundDecimal = true; + iter++; + } + + if (iter == iterEnd) + break; + + m5 = m4; + m4 = m3; + m3 = m2; + m2 = m1; + digitCount = static_cast< XMP_Int32 >( iterEnd - iter ); + StringToNumber(m1, std::string(iter, iterEnd)); + break; + } + + if (hasFoundDecimal) + { + outFractionalFrameDenominator = static_cast<XMP_Int32>(pow(10.0, digitCount) + 0.5); + outFractionalFrameNumerator = m1; + m1 = m2; + m2 = m3; + m3 = m4; + m4 = m5; + m5 = 0; + } + outHours = m4; + outMinutes = m3; + outSeconds = m2; + outFrames = m1; + } + + bool ConvertTimecodeToSamples( + XMP_Int64 & outSamples, + const std::string & inTimecode, + XMP_Uns64 inSampleRate, + XMP_Int64 inTimecodeFPS, + bool inNTSC, + bool inDropFrame) + { + /// @TODO: Ensure that negative and >64-bit values are OK and work as expected. + + if (inTimecode.empty()) + { + outSamples = static_cast<XMP_Int64>(-1); + return true; + } + + XMP_Int32 hours; + XMP_Int32 minutes; + XMP_Int32 seconds; + XMP_Int32 frames; + XMP_Int32 fractionalFrameNumerator; + XMP_Int32 fractionalFrameDenominator; + + ParseTimeCodeString(inTimecode, hours, minutes, seconds, frames, fractionalFrameNumerator, fractionalFrameDenominator); + + XMP_Int64 framesPerSecond = inTimecodeFPS; + XMP_Int64 framesPerMinute = framesPerSecond * 60; + XMP_Int64 framesPerHour = framesPerMinute * 60; + XMP_Int64 wholeFrames = 0; + XMP_Int64 frameRateNumerator = inTimecodeFPS; + XMP_Int64 frameRateDenominator = 1; + + if (inNTSC) + { + frameRateNumerator = 1000 * inTimecodeFPS; + frameRateDenominator = 1001; + } + + if (inDropFrame) + { + XMP_Int64 frameGroupDropped = 2 * inTimecodeFPS / 30; // 2 or 4 frames dropped at a time. + XMP_Int64 framesPerHourDropped = 108 * inTimecodeFPS / 30; + framesPerHour -= framesPerHourDropped; + XMP_Int64 framesPerTenMinutes = framesPerHour / 6; + XMP_Assert( framesPerHour % 6 == 0 ); //, "Drop frame not supported on the given frame rate." + XMP_Int64 framesDroppedWithinTheLeastTenMinutes = 0; + if (minutes % 10 != 0) + { + if ((seconds == 0) && (frames < frameGroupDropped)) + { + frames = static_cast<XMP_Int32>(frameGroupDropped); // Make sure invalid strings snap to the next higher valid frame. + } + framesDroppedWithinTheLeastTenMinutes = (minutes % 10) * frameGroupDropped; + } + wholeFrames = hours * framesPerHour + (minutes / 10) * framesPerTenMinutes + (minutes % 10) * framesPerMinute + seconds * framesPerSecond + frames - framesDroppedWithinTheLeastTenMinutes; + } + else + { + wholeFrames = hours * framesPerHour + minutes * framesPerMinute + seconds * framesPerSecond + frames; + } + + if (frameRateNumerator * fractionalFrameDenominator == 0) + { + XMP_Assert( "Divide by zero in ConvertTimecodeToSamples" ); + outSamples = 0; + return true; + } + + // + // (frame count / frames per second) * samples per second = sample count. + // + // ((frame count + fractionalFrameNumerator / fractionalFrameDenominator) * samples per second / frames per second) = sample count. + // or in integer math: + // ((frame count * fractionalFrameDenominator + fractionalFrameNumerator) * samples per second / (frames per second * fractionalFrameDenominator)) = sample count. + // with rounding correction to give us the first sample contained entirely in the frame: + + // There is a non-zero probability of rolling over this integer arithmetic. + double integerFailsafeNumerator = ((static_cast<double>(wholeFrames) * static_cast<double>(fractionalFrameDenominator) + fractionalFrameNumerator) * static_cast<double>(frameRateDenominator) * static_cast<double>(inSampleRate) + (frameRateNumerator * fractionalFrameDenominator - 1)); + if (integerFailsafeNumerator > static_cast<double>(0x7000000000000000LL)) + { + outSamples = static_cast<XMP_Int64>(integerFailsafeNumerator / (static_cast<double>(frameRateNumerator) * static_cast<double>(fractionalFrameDenominator))); + } + else + { + outSamples = ((wholeFrames * fractionalFrameDenominator + fractionalFrameNumerator) * frameRateDenominator * inSampleRate + (frameRateNumerator * fractionalFrameDenominator - 1)) / (frameRateNumerator * fractionalFrameDenominator); + } + return true; + } + + bool ConvertSMPTETimecodeToSamples( + XMP_Int64 & outSamples, + const std::string & inTimecode, + XMP_Uns64 inSampleRate, + const std::string & inTimecodeFormat ) + { + bool result = false; + + if ( inTimecodeFormat.compare( "24Timecode" ) == 0 ) { + result = ConvertTimecodeToSamples( outSamples, inTimecode, inSampleRate, 24, false, false ); + } else if ( inTimecodeFormat.compare( "25Timecode" ) == 0 ) { + result = ConvertTimecodeToSamples( outSamples, inTimecode, inSampleRate, 25, false, false ); + } else if ( inTimecodeFormat.compare( "2997DropTimecode") == 0 ) { + result = ConvertTimecodeToSamples( outSamples, inTimecode, inSampleRate, 30, true, true ); + } else if ( inTimecodeFormat.compare( "2997NonDropTimecode") == 0 ) { + result = ConvertTimecodeToSamples( outSamples, inTimecode, inSampleRate, 30, true, false ); + } else if ( inTimecodeFormat.compare( "30Timecode" ) == 0 ) { + result = ConvertTimecodeToSamples( outSamples, inTimecode, inSampleRate, 30, false, false ); + } else if ( inTimecodeFormat.compare( "50Timecode" ) == 0 ) { + result = ConvertTimecodeToSamples( outSamples, inTimecode, inSampleRate, 50, false, false ); + } else if ( inTimecodeFormat.compare( "5994DropTimecode" ) == 0 ) { + result = ConvertTimecodeToSamples( outSamples, inTimecode, inSampleRate, 60, true, true ); + } else if ( inTimecodeFormat.compare( "5994NonDropTimecode" ) == 0 ) { + result = ConvertTimecodeToSamples( outSamples, inTimecode, inSampleRate, 60, true, false ); + } else if ( inTimecodeFormat.compare( "60Timecode" ) == 0 ) { + result = ConvertTimecodeToSamples( outSamples, inTimecode, inSampleRate, 60, false, false ); + } else if ( inTimecodeFormat.compare( "23976Timecode" ) == 0 ) { + result = ConvertTimecodeToSamples( outSamples, inTimecode, inSampleRate, 24, true, false ); + } + return result; + } + +} diff --git a/XMPFiles/source/FormatSupport/TimeConversionUtils.hpp b/XMPFiles/source/FormatSupport/TimeConversionUtils.hpp new file mode 100644 index 0000000..bf7ed04 --- /dev/null +++ b/XMPFiles/source/FormatSupport/TimeConversionUtils.hpp @@ -0,0 +1,35 @@ +#ifndef TimeConversionUtils_h__ +#define TimeConversionUtils_h__ 1 + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2014 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "public/include/XMP_Environment.h" // ! XMP_Environment.h must be the first included header. +#include "public/include/XMP_Const.h" + +#include "XMPFiles/source/XMPFiles_Impl.hpp" + +namespace TimeConversionUtils { + bool ConvertSamplesToSMPTETimecode( + std::string & outTimecode, + XMP_Int64 inSamples, + XMP_Uns64 inSampleRate, + const std::string & inTimecodeFormat ); + + bool ConvertSMPTETimecodeToSamples( + XMP_Int64 & outSamples, + const std::string & inTimecode, + XMP_Uns64 inSampleRate, + const std::string & inTimecodeFormat + ); + + +}; + +#endif // TimeConversionUtils_h__ diff --git a/XMPFiles/source/FormatSupport/WAVE/Cr8rMetadata.cpp b/XMPFiles/source/FormatSupport/WAVE/Cr8rMetadata.cpp index 277924f..d085aca 100644 --- a/XMPFiles/source/FormatSupport/WAVE/Cr8rMetadata.cpp +++ b/XMPFiles/source/FormatSupport/WAVE/Cr8rMetadata.cpp @@ -79,8 +79,6 @@ void Cr8rMetadata::parse( const XMP_Uns8* chunkData, XMP_Uns64 size ) { if( size >= kCr8rSizeFix ) { - const LittleEndian& LE = LittleEndian::getInstance(); - Cr8rBoxContent cr8r; memset( &cr8r, 0, kCr8rSizeFix ); diff --git a/XMPFiles/source/FormatSupport/WAVE/INFOMetadata.cpp b/XMPFiles/source/FormatSupport/WAVE/INFOMetadata.cpp index 6597f2a..335acd7 100644 --- a/XMPFiles/source/FormatSupport/WAVE/INFOMetadata.cpp +++ b/XMPFiles/source/FormatSupport/WAVE/INFOMetadata.cpp @@ -189,6 +189,8 @@ XMP_Uns64 INFOMetadata::serialize( XMP_Uns8** outBuffer ) // chunk id // chunk size // + XMP_Validate( iter->second != NULL, "ERROR inserting serialize. iter->second is NULL.", kXMPErr_InternalFailure ); + XMP_Assert(dynamic_cast<TValueObject<std::string>*>(iter->second) == static_cast<TValueObject<std::string>*>(iter->second)); TValueObject<std::string>* strObj = dynamic_cast<TValueObject<std::string>*>(iter->second); std::string value = strObj->getValue(); XMP_Uns32 id = iter->first; diff --git a/XMPFiles/source/FormatSupport/WAVE/PrmLMetadata.cpp b/XMPFiles/source/FormatSupport/WAVE/PrmLMetadata.cpp index 477c31e..87d29fb 100644 --- a/XMPFiles/source/FormatSupport/WAVE/PrmLMetadata.cpp +++ b/XMPFiles/source/FormatSupport/WAVE/PrmLMetadata.cpp @@ -76,8 +76,6 @@ void PrmLMetadata::parse( const XMP_Uns8* chunkData, XMP_Uns64 size ) { if( size >= kPrmlSizeFix ) { - const LittleEndian& LE = LittleEndian::getInstance(); - PrmlBoxContent prml; memset( &prml, 0, kPrmlSizeFix ); diff --git a/XMPFiles/source/FormatSupport/WAVE/WAVEBehavior.cpp b/XMPFiles/source/FormatSupport/WAVE/WAVEBehavior.cpp index 1e98f4f..60ce5ae 100644 --- a/XMPFiles/source/FormatSupport/WAVE/WAVEBehavior.cpp +++ b/XMPFiles/source/FormatSupport/WAVE/WAVEBehavior.cpp @@ -431,7 +431,7 @@ WAVEBehavior::DS64* WAVEBehavior::getDS64( IChunkContainer& tree, XMP_IO* stream ds64 = NULL; } - if( ds64 != NULL && ds64->getID() == kChunk_ds64 ) + if( rf64 != NULL && ds64 != NULL && ds64->getID() == kChunk_ds64 ) { // // Successfully read 'ds64' chunk. @@ -439,7 +439,6 @@ WAVEBehavior::DS64* WAVEBehavior::getDS64( IChunkContainer& tree, XMP_IO* stream // add chunk to the 'RF64' chunk // ds64->cacheChunkData( stream ); - rf64->appendChild( ds64, false ); } else @@ -600,7 +599,7 @@ bool WAVEBehavior::parseDS64Chunk( const Chunk& ds64Chunk, WAVEBehavior::DS64& d const XMP_Uns8* data; XMP_Uns64 size = ds64Chunk.getData(&data); - memset( &ds64, 0, kMinimumDS64ChunkSize ); + memset( &ds64, 0, kMinimumDS64ChunkSize); // // copy fix input data into RF64 block (except chunk size table) diff --git a/XMPFiles/source/FormatSupport/WAVE/WAVEReconcile.cpp b/XMPFiles/source/FormatSupport/WAVE/WAVEReconcile.cpp index 3798f3d..8c3067b 100644 --- a/XMPFiles/source/FormatSupport/WAVE/WAVEReconcile.cpp +++ b/XMPFiles/source/FormatSupport/WAVE/WAVEReconcile.cpp @@ -15,6 +15,9 @@ #include "XMPFiles/source/FormatSupport/WAVE/INFOMetadata.h" #include "XMPFiles/source/FormatSupport/WAVE/BEXTMetadata.h" #include "XMPFiles/source/FormatSupport/WAVE/CartMetadata.h" +#include "XMPFiles/source/FormatSupport/WAVE/iXMLMetadata.h" + +#include "XMPFiles/source/FormatSupport/TimeConversionUtils.hpp" // cr8r is not yet required for WAVE //#include "Cr8rMetadata.h" @@ -27,19 +30,69 @@ using namespace IFF_RIFF; // ************** legacy Mappings ***************** // +static const char * kBWF_description = "description"; +static const char * kBWF_originator = "originator"; +static const char * kBWF_originatorReference = "originatorReference"; +static const char * kBWF_originationDate = "originationDate"; +static const char * kBWF_originationTime = "originationTime"; +static const char * kBWF_timeReference = "timeReference"; +static const char * kBWF_version = "version"; +static const char * kBWF_umid = "umid"; +static const char * kBWF_codingHistory = "codingHistory"; +static const char * kBWF_timeStampSampleRate = "timeSampleRate"; // its transient should be deleted at the end. + static const MetadataPropertyInfo kBextProperties[] = { -// XMP NS XMP Property Name Native Metadata Identifier Native Datatype XMP Datatype Delete Priority ExportPolicy - { kXMP_NS_BWF, "description", BEXTMetadata::kDescription, kNativeType_StrLocal, kXMPType_Simple, true, false, kExport_Always }, // bext:description <-> BEXT:Description - { kXMP_NS_BWF, "originator", BEXTMetadata::kOriginator, kNativeType_StrLocal, kXMPType_Simple, true, false, kExport_Always }, // bext:originator <-> BEXT:originator - { kXMP_NS_BWF, "originatorReference", BEXTMetadata::kOriginatorReference, kNativeType_StrLocal, kXMPType_Simple, true, false, kExport_Always }, // bext:OriginatorReference <-> BEXT:OriginatorReference - { kXMP_NS_BWF, "originationDate", BEXTMetadata::kOriginationDate, kNativeType_StrLocal, kXMPType_Simple, true, false, kExport_Always }, // bext:originationDate <-> BEXT:originationDate - { kXMP_NS_BWF, "originationTime", BEXTMetadata::kOriginationTime, kNativeType_StrLocal, kXMPType_Simple, true, false, kExport_Always }, // bext:originationTime <-> BEXT:originationTime - { kXMP_NS_BWF, "timeReference", BEXTMetadata::kTimeReference, kNativeType_Uns64, kXMPType_Simple, true, false, kExport_Always }, // bext:timeReference <-> BEXT:TimeReferenceLow + BEXT:TimeReferenceHigh +// XMP NS XMP Property Name Native Metadata Identifier Native Datatype XMP Datatype Delete Priority ExportPolicy + { kXMP_NS_BWF, kBWF_description, BEXTMetadata::kDescription, kNativeType_StrLocal, kXMPType_Simple, false, false, kExport_Always }, // bext:description <-> BEXT:Description + { kXMP_NS_BWF, kBWF_originator, BEXTMetadata::kOriginator, kNativeType_StrLocal, kXMPType_Simple, false, false, kExport_Always }, // bext:originator <-> BEXT:originator + { kXMP_NS_BWF, kBWF_originatorReference, BEXTMetadata::kOriginatorReference, kNativeType_StrLocal, kXMPType_Simple, false, false, kExport_Always }, // bext:OriginatorReference <-> BEXT:OriginatorReference + { kXMP_NS_BWF, kBWF_originationDate, BEXTMetadata::kOriginationDate, kNativeType_StrLocal, kXMPType_Simple, false, false, kExport_Always }, // bext:originationDate <-> BEXT:originationDate + { kXMP_NS_BWF, kBWF_originationTime, BEXTMetadata::kOriginationTime, kNativeType_StrLocal, kXMPType_Simple, false, false, kExport_Always }, // bext:originationTime <-> BEXT:originationTime + { kXMP_NS_BWF, kBWF_timeReference, BEXTMetadata::kTimeReference, kNativeType_Uns64, kXMPType_Simple, false, false, kExport_Always }, // bext:timeReference <-> BEXT:TimeReferenceLow + BEXT:TimeReferenceHigh // Special case: On export BEXT:version is always written as 1 - { kXMP_NS_BWF, "version", BEXTMetadata::kVersion, kNativeType_Uns16, kXMPType_Simple, true, false, kExport_Never }, // bext:version <-> BEXT:version + { kXMP_NS_BWF, kBWF_version, BEXTMetadata::kVersion, kNativeType_Uns16, kXMPType_Simple, false, false, kExport_Never }, // bext:version <-> BEXT:version // special case: bext:umid <-> BEXT:UMID - { kXMP_NS_BWF, "codingHistory", BEXTMetadata::kCodingHistory, kNativeType_StrLocal, kXMPType_Simple, true, false, kExport_Always }, // bext:codingHistory <-> BEXT:codingHistory + { kXMP_NS_BWF, kBWF_codingHistory, BEXTMetadata::kCodingHistory, kNativeType_StrLocal, kXMPType_Simple, false, false, kExport_Always }, // bext:codingHistory <-> BEXT:codingHistory + { NULL } +}; + +static const char * kDM_takeNumber = "takeNumber"; +static const char * kDM_audioSampleType = "audioSampleType"; +static const char * kDM_scene = "scene"; +static const char * kDM_tapeName = "tapeName"; +static const char * kDM_logComment = "logComment"; +static const char * kDM_projectName = "projectName"; +static const char * kDM_audioSampleRate = "audioSampleRate"; +static const char * kDM_startTimecode = "startTimecode"; +static const char * kDM_timeFormat = "timeFormat"; +static const char * kDM_timeValue = "timeValue"; +static const char * kDM_good = "good"; + +static const MetadataPropertyInfo kiXMLProperties[] = +{ +// XMP NS XMP Property Name Native Metadata Identifier Native Datatype XMP Datatype Delete Priority ExportPolicy + { kXMP_NS_DM, kDM_tapeName, iXMLMetadata::kTape, kNativeType_StrUTF8, kXMPType_Simple, false, false, kExport_Always }, //xmpDM:tapeName <-> iXML:TAPE + { kXMP_NS_DM, kDM_takeNumber, iXMLMetadata::kTake, kNativeType_Uns64, kXMPType_Simple, false, false, kExport_Always }, //xmpDM:tapeName <-> iXML:TAPE + { kXMP_NS_DM, kDM_scene, iXMLMetadata::kScene, kNativeType_StrUTF8, kXMPType_Simple, false, false, kExport_Always }, //xmpDM:scene <-> iXML:SCENE + { kXMP_NS_DM, kDM_logComment, iXMLMetadata::kNote, kNativeType_StrUTF8, kXMPType_Simple, false, false, kExport_Always }, //xmpDM:logComment <-> iXML:NOTE + { kXMP_NS_DM, kDM_projectName, iXMLMetadata::kProject, kNativeType_StrUTF8, kXMPType_Simple, false, false, kExport_Always }, //xmpDM:project <-> iXML:PROJECT + { kXMP_NS_DM, kDM_good, iXMLMetadata::kCircled, kNativeType_Bool, kXMPType_Simple, false, false, kExport_Always }, //xmpDM:good <-> iXML:CIRCLED + { kXMP_NS_DM, kDM_audioSampleRate, iXMLMetadata::kFileSampleRate, kNativeType_Uns64, kXMPType_Simple, false, false, kExport_Always }, // xmpDM:audioSampleRate <-> iXML:FILE_SAMPLE_RATE + // special case for AudioBitDepth // xmpDM:audioSampleType <-> iXML:AUDIO_BIT_DEPTH + { kXMP_NS_BWF, kBWF_description, iXMLMetadata::kBWFDescription, kNativeType_StrUTF8, kXMPType_Simple, true, false, kExport_Always }, // bext:description <-> iXML:BWF_DESCRIPTION + { kXMP_NS_BWF, kBWF_originator, iXMLMetadata::kBWFOriginator, kNativeType_StrUTF8, kXMPType_Simple, true, false, kExport_Always }, // bext:originator <-> iXML:BWF_ORIGINATOR + { kXMP_NS_BWF, kBWF_originatorReference, iXMLMetadata::kBWFOriginatorReference, kNativeType_StrUTF8, kXMPType_Simple, true, false, kExport_Always }, // bext:OriginatorReference <-> iXML:BWF_ORIGINATOR_REFERENCE + { kXMP_NS_BWF, kBWF_originationDate, iXMLMetadata::kBWFOriginationDate, kNativeType_StrUTF8, kXMPType_Simple, true, false, kExport_Always }, // bext:originationDate <-> iXML:BWF_ORIGINATION_DATE + { kXMP_NS_BWF, kBWF_originationTime, iXMLMetadata::kBWFOriginationTime, kNativeType_StrUTF8, kXMPType_Simple, true, false, kExport_Always }, // bext:originationTime <-> iXML:BWF_ORIGINATION_TIME + // special case for timeReference // bext:timeReference <-> iXML:BWF_TIME_REFERENCE_LOW and iXML:BWF_TIME_REFERENCE_HIGH + // Special case: On export BEXT:version is always written as 1 + { kXMP_NS_BWF, kBWF_version, iXMLMetadata::kBWFVersion, kNativeType_Uns64, kXMPType_Simple, true, false, kExport_Never }, // bext:version <-> iXML:BWF_VERSION + { kXMP_NS_BWF, kBWF_codingHistory, iXMLMetadata::kBWFHistory, kNativeType_StrUTF8, kXMPType_Simple, true, false, kExport_Always }, // bext:codingHistory <-> iXML:BWF_CODING_HISTORY + { kXMP_NS_BWF, kBWF_umid, iXMLMetadata::kBWFUMID, kNativeType_StrASCII, kXMPType_Simple, true, false, kExport_Always }, // bext:codingHistory <-> iXML:BWF_CODING_HISTORY + // special case for timeReference // bext:timeReference <-> iXML:TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_HO and iXML:TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_HI + // special case for startTimeCode // xmpDM:startTimecode <-> iXML:TIMECODE_RATE, iXML:TIMECODE_FLAG and bext:timeReference. + { kXMP_NS_BWF, kBWF_timeStampSampleRate, iXMLMetadata::kTimeStampSampleRate, kNativeType_Uns64, kXMPType_Simple, false, false, kExport_NoDelete }, // bext::timeStampSampleRate <-> iXML { NULL } }; @@ -140,6 +193,14 @@ XMP_Bool WAVEReconcile::importToXMP( SXMPMeta& outXMP, const MetadataSet& inMeta if ( ! ignoreLocalText ) { + // + // Import iXML + // + iXMLMetadata * iXMLMeta = inMetaData.get<iXMLMetadata>(); + if ( iXMLMeta != NULL ) { + changed |= IReconcile::importNativeToXMP( outXMP, *iXMLMeta, kiXMLProperties, false ); + changed |= exportSpecialiXMLToXMP( *iXMLMeta, outXMP ); + } // // Import BEXT @@ -291,6 +352,45 @@ XMP_Bool WAVEReconcile::importToXMP( SXMPMeta& outXMP, const MetadataSet& inMeta } } + // do timecode calculations + if ( outXMP.DoesPropertyExist( kXMP_NS_BWF, kBWF_timeReference ) && + outXMP.DoesPropertyExist( kXMP_NS_BWF, kDM_timeFormat ) && + outXMP.DoesPropertyExist( kXMP_NS_BWF, kBWF_timeStampSampleRate ) ) + { + std::string xmpValue; + XMP_Int64 sampleRate = 0; + XMP_Uns64 nSamples = 0; + std::string timeFormat; + bool ok = outXMP.GetProperty( kXMP_NS_BWF, kBWF_timeReference, &xmpValue, 0 ); + + if ( ok ) + { + int count; + char nextCh; + const char * strValue = xmpValue.c_str(); + count = sscanf ( strValue, "%llu%c", &nSamples, &nextCh ); + + if ( count != 1 ) ok = false; + } + if ( ok ) + ok = outXMP.GetProperty_Int64( kXMP_NS_BWF, kBWF_timeStampSampleRate, &sampleRate, 0 ); + if ( ok ) + ok = outXMP.GetProperty( kXMP_NS_BWF, kDM_timeFormat, &timeFormat, 0 ); + + if ( ok && sampleRate != 0 && timeFormat.size() > 0 ) { + // compute time code from all the information we have + std::string timecode; + if ( TimeConversionUtils::ConvertSamplesToSMPTETimecode( timecode, nSamples, sampleRate, timeFormat ) ) { + outXMP.SetStructField( kXMP_NS_DM, kDM_startTimecode, kXMP_NS_DM, kDM_timeValue, timecode, 0 ); + outXMP.SetStructField( kXMP_NS_DM, kDM_startTimecode, kXMP_NS_DM, kDM_timeFormat, timeFormat, 0 ); + } + } + } + + // delete transient properties + outXMP.DeleteProperty( kXMP_NS_BWF, kBWF_timeStampSampleRate ); + outXMP.DeleteProperty( kXMP_NS_BWF, kDM_timeFormat ); + return changed; } // importToXMP @@ -310,9 +410,55 @@ XMP_Bool WAVEReconcile::exportFromXMP( MetadataSet& outMetaData, SXMPMeta& inXMP changed |= IReconcile::exportXMPToNative( *dispMeta, inXMP, kDISPProperties ); } - + PropertyList propertiesToBeRemovedFromXMPMeta; if ( ! ignoreLocalText ) { + bool removeAllPropertiesinBextNameSpace = false; + // + // Export iXML + // + iXMLMetadata *iXMLMeta = outMetaData.get<iXMLMetadata>(); + if (iXMLMeta != NULL) + { + IReconcile::exportXMPToNative( *iXMLMeta, inXMP, kiXMLProperties, &propertiesToBeRemovedFromXMPMeta ); + exportSpecialXMPToiXML( inXMP, *iXMLMeta, propertiesToBeRemovedFromXMPMeta ); + changed |= iXMLMeta->hasChanged(); + removeAllPropertiesinBextNameSpace = true; + // for maintaing backward compatibility we don't want to remove properties from xmp packet. + propertiesToBeRemovedFromXMPMeta.clear(); + + // update time code value + if ( iXMLMeta->valueExists( iXMLMetadata::kTimeStampSampleRate ) && + iXMLMeta->valueExists( iXMLMetadata::kTimeStampSampleSinceMidnightHigh ) && + iXMLMeta->valueExists( iXMLMetadata::kTimeStampSampleSinceMidnightLow ) && + iXMLMeta->valueExists( iXMLMetadata::kTimeCodeRate ) ) + { + if ( iXMLMeta->valueChanged( iXMLMetadata::kTimeStampSampleRate ) || + iXMLMeta->valueChanged( iXMLMetadata::kTimeStampSampleSinceMidnightHigh ) || + iXMLMeta->valueChanged( iXMLMetadata::kTimeStampSampleSinceMidnightLow ) || + iXMLMeta->valueChanged( iXMLMetadata::kTimeCodeRate ) || + ( iXMLMeta->valueExists( iXMLMetadata::kTimeCodeFlag ) && iXMLMeta->valueChanged( iXMLMetadata::kTimeCodeFlag ) ) ) + { + // update the time code value + XMP_Int64 sampleRate = iXMLMeta->getValue<XMP_Uns64>( iXMLMetadata::kTimeStampSampleRate ); + XMP_Int64 nSamples = 0; + std::string timeFormat; + bool ok = inXMP.GetProperty_Int64( kXMP_NS_BWF, kBWF_timeReference, &nSamples, 0 ); + if ( ok ) + ok = inXMP.GetStructField( kXMP_NS_DM, kDM_startTimecode, kXMP_NS_DM, kDM_timeFormat, &timeFormat, 0 ); + + if ( ok && sampleRate != 0 && timeFormat.size() > 0 ) { + // compute time code from all the information we have + std::string timecode; + if ( TimeConversionUtils::ConvertSamplesToSMPTETimecode( timecode, nSamples, sampleRate, timeFormat ) ) { + inXMP.SetStructField( kXMP_NS_DM, kDM_startTimecode, kXMP_NS_DM, kDM_timeValue, timecode, 0 ); + } + } + + } + } + } + // // Export BEXT // @@ -355,12 +501,13 @@ XMP_Bool WAVEReconcile::exportFromXMP( MetadataSet& outMetaData, SXMPMeta& inXMP bextMeta->deleteValue(BEXTMetadata::kVersion); } - // Remove BWF properties from the XMP - SXMPUtils::RemoveProperties(&inXMP, kXMP_NS_BWF, NULL, kXMPUtil_DoAllProperties ); + removeAllPropertiesinBextNameSpace = true; changed |= bextMeta->hasChanged(); } + if ( removeAllPropertiesinBextNameSpace ) + SXMPUtils::RemoveProperties(&inXMP, kXMP_NS_BWF, NULL, kXMPUtil_DoAllProperties ); // // Export cart @@ -486,6 +633,11 @@ XMP_Bool WAVEReconcile::exportFromXMP( MetadataSet& outMetaData, SXMPMeta& inXMP // inXMP.DeleteProperty( kXMP_NS_WAV, "NativeDigest" ); + for ( PropertyList::iterator it = propertiesToBeRemovedFromXMPMeta.begin(), last = propertiesToBeRemovedFromXMPMeta.end(); it != last; it++ ) + { + inXMP.DeleteProperty( it->first, it->second ); + } + return changed; } // exportFromXMP @@ -574,3 +726,342 @@ bool WAVEReconcile::stringToFOURCC ( std::string input, XMP_Uns32 &output ) return result; } + +struct iXMLAudioSampleTypeMapping +{ + const char * xmpStringValue; + XMP_Uns64 ixmlIntValue; +}; + +iXMLAudioSampleTypeMapping ixmlAudioSampleTypeMappings[] = { + { "8Int", 8 }, + { "16Int", 16 }, + { "24Int", 24 }, + { "32Float", 32 }, +}; + +struct iXMLTimeCodeRateAndFlagMapping +{ + const char * xmpStringValue; + const char * ixmlTimeCodeRateValue; + const char * ixmlTimeCodeFlagValue; +}; + +iXMLTimeCodeRateAndFlagMapping ixmlTimeCodeRateAndFlagMappings[] = { + { "24Timecode", "24/1", "NDF" }, + { "25Timecode", "25/1", "NDF" }, + { "2997DropTimecode", "30000/1001", "DF" }, + { "2997NonDropTimecode", "30000/1001", "NDF" }, + { "30Timecode", "30/1", "NDF" }, + { "50Timecode", "50/1", "NDF" }, + { "5994DropTimecode", "60000/1001", "DF" }, + { "5994NonDropTimecode", "60000/1001", "NDF" }, + { "60Timecode", "60/1", "NDF" }, + { "23976Timecode", "24000/1001", "NDF" }, +}; + +void IFF_RIFF::WAVEReconcile::exportSpecialXMPToiXML( SXMPMeta & inXMP, IMetadata & outNativeMeta, PropertyList & propertiesToBeDeleted ) +{ + std::string sXmpValue; + XMP_Int64 iXMPValue; + + // special case for NoGood and Circled // xmpDM:good ,-> iXML:NO_GOOD and iXML:CIRCLED + + // special case for AudioBitDepth // xmpDM:audioSampleType <-> iXML:AUDIO_BIT_DEPTH + bool deleteNativeEntry = false; + try + { + if ( inXMP.GetProperty( kXMP_NS_DM, kDM_audioSampleType, &sXmpValue, 0 ) ) + { + bool matchingValueFound = false; + XMP_Uns64 ixmlValue = 0; + for ( size_t i = 0, total = sizeof(ixmlAudioSampleTypeMappings )/sizeof( iXMLAudioSampleTypeMapping); i < total; i++ ) + { + if ( sXmpValue.compare( ixmlAudioSampleTypeMappings[i].xmpStringValue ) == 0 ) + { + ixmlValue = ixmlAudioSampleTypeMappings[i].ixmlIntValue; + matchingValueFound = true; + break; + } + } + + if ( matchingValueFound ) + { + outNativeMeta.setValue< XMP_Uns64 >( iXMLMetadata::kAudioBitDepth, ixmlValue ); + propertiesToBeDeleted.push_back( std::make_pair( kXMP_NS_DM, kDM_audioSampleType ) ); + deleteNativeEntry = false; + } + else + { + deleteNativeEntry = true; + } + } + else + { + deleteNativeEntry = true; + } + + if ( deleteNativeEntry ) + { + if ( outNativeMeta.valueExists( iXMLMetadata::kAudioBitDepth ) ) + { + XMP_Uns64 ixmlValue = outNativeMeta.getValue< XMP_Uns64 >( iXMLMetadata::kAudioBitDepth ); + bool validValue = false; + for ( size_t i = 0, total = sizeof(ixmlAudioSampleTypeMappings )/sizeof( iXMLAudioSampleTypeMapping); i < total; i++ ) + { + if ( ixmlValue == ixmlAudioSampleTypeMappings[i].ixmlIntValue ) + { + validValue = true; + break; + } + } + if ( validValue ) + outNativeMeta.deleteValue( iXMLMetadata::kAudioBitDepth ); + } + } + } + catch ( ... ) + { + // do nothing + } + + // Special case: On export BEXT:version is always written as 1 + try + { + if ( inXMP.GetProperty( kXMP_NS_BWF, "version", NULL, 0 ) ) + { + outNativeMeta.setValue< XMP_Uns64 >( iXMLMetadata::kBWFVersion, 1 ); + } + else + { + outNativeMeta.deleteValue( iXMLMetadata::kBWFVersion ); + } + } + catch( ... ) + { + // do nothing + } + + // special case for xmpDM:startTimecode\xmpDM:timeFormat + try + { + if ( inXMP.GetStructField( kXMP_NS_DM, kDM_startTimecode, kXMP_NS_DM, kDM_timeFormat, &sXmpValue, 0 ) ) + { + bool matchingValueFound = false; + const char * ixmlValueForTimeCodeRate = NULL; + const char * ixmlValueForTimeCodeFlag = NULL; + for ( size_t i = 0, total = sizeof(ixmlTimeCodeRateAndFlagMappings)/sizeof(iXMLTimeCodeRateAndFlagMapping); i < total; i++ ) + { + if ( sXmpValue.compare( ixmlTimeCodeRateAndFlagMappings[i].xmpStringValue ) == 0 ) + { + ixmlValueForTimeCodeRate = ixmlTimeCodeRateAndFlagMappings[i].ixmlTimeCodeRateValue; + ixmlValueForTimeCodeFlag = ixmlTimeCodeRateAndFlagMappings[i].ixmlTimeCodeFlagValue; + matchingValueFound = true; + deleteNativeEntry = false; + break; + } + } + + if ( matchingValueFound ) + { + outNativeMeta.setValue< std::string >( iXMLMetadata::kTimeCodeRate, ixmlValueForTimeCodeRate ); + outNativeMeta.setValue< std::string >( iXMLMetadata::kTimeCodeFlag, ixmlValueForTimeCodeFlag ); + } + else + { + deleteNativeEntry = true; + } + } + else + { + deleteNativeEntry = true; + } + + if ( deleteNativeEntry ) + { + if ( outNativeMeta.valueExists( iXMLMetadata::kTimeCodeRate ) ) + { + std::string ixmlValueForTimecodeRate = outNativeMeta.getValue< std::string >( iXMLMetadata::kTimeCodeRate ); + bool validValue = false; + for ( size_t i = 0, total = sizeof(ixmlTimeCodeRateAndFlagMappings)/sizeof(iXMLTimeCodeRateAndFlagMapping); i < total; i++ ) + { + if ( ixmlValueForTimecodeRate.compare( ixmlTimeCodeRateAndFlagMappings[i].ixmlTimeCodeRateValue ) == 0 ) + { + validValue = true; + break; + } + } + if ( validValue ) + { + outNativeMeta.deleteValue( iXMLMetadata::kTimeCodeRate ); + outNativeMeta.deleteValue( iXMLMetadata::kTimeCodeFlag ); + } + } + } + } + catch( ... ) + { + // do nothing + } + + // special case for timeReference // bext:timeReference <-> iXML:BWF_TIME_REFERENCE_LOW and iXML:BWF_TIME_REFERENCE_HIGH + bool bextTimeReferenceDataAvailable = false; + XMP_Uns32 bextLowValue = 0; + XMP_Uns32 bextHighValue = 0; + bool timeCodeDataAvailable = false; + XMP_Uns32 tcLowValue = 0; + XMP_Uns32 tcHighValue = 0; + + try + { + if ( inXMP.GetProperty_Int64( kXMP_NS_BWF, "timeReference", &iXMPValue, 0 ) ) + { + bextTimeReferenceDataAvailable = true; + XMP_Uns64 uXmpValue = ( XMP_Uns64 )( iXMPValue ); + bextLowValue = ( XMP_Uns32 ) uXmpValue; + bextHighValue = ( XMP_Uns32 ) ( uXmpValue >> 32 ); + } + } + catch ( ... ) + { + // do nothing + } + +#if 0 // reverse calculation from timecode to samples can result in different number of samples. So ignoring for time being + if ( outNativeMeta.valueExists( iXMLMetadata::kTimeStampSampleRate ) ) + { + try + { + std::string timeFormat, timeValue; + if ( inXMP.GetStructField( kXMP_NS_DM, kDM_startTimecode, kXMP_NS_DM, kDM_timeValue, &timeValue, 0 ) && + inXMP.GetStructField( kXMP_NS_DM, kDM_startTimecode, kXMP_NS_DM, kDM_timeFormat, &timeFormat, 0) ) + { + XMP_Int64 nSamples; + if ( TimeConversionUtils::ConvertSMPTETimecodeToSamples( nSamples, timeValue, + outNativeMeta.getValue< XMP_Uns64 >( iXMLMetadata::kTimeStampSampleRate ), timeFormat ) ) + { + timeCodeDataAvailable = true; + XMP_Uns64 uXmpValue = ( XMP_Uns64 )( nSamples ); + tcLowValue = ( XMP_Uns32 ) uXmpValue; + tcHighValue = ( XMP_Uns32 ) ( uXmpValue >> 32 ); + } + + } + } + catch ( ... ) + { + // do nothing + } + } + + if ( bextTimeReferenceDataAvailable && timeCodeDataAvailable ) + { + // pick the one which is different, if both different pick the bext one + XMP_Uns64 + } + else if ( bextTimeReferenceDataAvailable ) + { + } + else if ( timeCodeDataAvailable ) + { + } + else // none of the bextTimeReference and timeCode data is available + { + outNativeMeta.deleteValue( iXMLMetadata::kTimeStampSampleSinceMidnightHigh ); + outNativeMeta.deleteValue( iXMLMetadata::kTimeStampSampleSinceMidnightLow ); + outNativeMeta.deleteValue( iXMLMetadata::kBWFTimeReferenceHigh ); + outNativeMeta.deleteValue( iXMLMetadata::kBWFTimeReferenceLow ); + } +#endif + + if ( bextTimeReferenceDataAvailable ) { + outNativeMeta.setValue< XMP_Uns64 >( iXMLMetadata::kBWFTimeReferenceHigh, bextHighValue ); + outNativeMeta.setValue< XMP_Uns64 >( iXMLMetadata::kBWFTimeReferenceLow, bextLowValue ); + outNativeMeta.setValue< XMP_Uns64 >( iXMLMetadata::kTimeStampSampleSinceMidnightHigh, bextHighValue ); + outNativeMeta.setValue< XMP_Uns64 >( iXMLMetadata::kTimeStampSampleSinceMidnightLow, bextLowValue ); + } else { + outNativeMeta.deleteValue( iXMLMetadata::kTimeStampSampleSinceMidnightHigh ); + outNativeMeta.deleteValue( iXMLMetadata::kTimeStampSampleSinceMidnightLow ); + outNativeMeta.deleteValue( iXMLMetadata::kBWFTimeReferenceHigh ); + outNativeMeta.deleteValue( iXMLMetadata::kBWFTimeReferenceLow ); + } +} + +bool IFF_RIFF::WAVEReconcile::exportSpecialiXMLToXMP( IMetadata & inNativeMeta, SXMPMeta & outXMP ) +{ + bool changed = false; + // special case for AudioBitDepth // xmpDM:audioSampleType <-> iXML:AUDIO_BIT_DEPTH + if ( inNativeMeta.valueExists( iXMLMetadata::kAudioBitDepth ) ) { + XMP_Uns64 ixmlValue = inNativeMeta.getValue< XMP_Uns64 >( iXMLMetadata::kAudioBitDepth); + const char * xmpValue = NULL; + bool matchingValueFound = false; + + for ( size_t i = 0, total = sizeof(ixmlAudioSampleTypeMappings)/sizeof(iXMLAudioSampleTypeMapping); i < total; i++ ) + { + if ( ixmlAudioSampleTypeMappings[i].ixmlIntValue == ixmlValue ) + { + xmpValue = ixmlAudioSampleTypeMappings[i].xmpStringValue; + matchingValueFound = true; + break; + } + } + if ( matchingValueFound ) + { + outXMP.SetProperty( kXMP_NS_DM, kDM_audioSampleType, xmpValue ); + changed = true; + } + } + + // special case for timeReference // bext:timeReference <-> iXML:TIME_SAMPLES_SINCE_MIDNIGHT_LO and iXML:TIME_SAMPLES_SINCE_MIDNIGHT_HI + if ( inNativeMeta.valueExists( iXMLMetadata::kTimeStampSampleSinceMidnightHigh ) && inNativeMeta.valueExists( iXMLMetadata::kTimeStampSampleSinceMidnightLow ) ) { + XMP_Uns64 combinedValue = inNativeMeta.getValue< XMP_Uns64 >( iXMLMetadata::kTimeStampSampleSinceMidnightHigh ); + combinedValue = combinedValue << 32; + combinedValue += inNativeMeta.getValue< XMP_Uns64 >( iXMLMetadata::kTimeStampSampleSinceMidnightLow ); + std::string strValue; + SXMPUtils::ConvertFromInt64( combinedValue, "%llu", &strValue ); + outXMP.SetProperty( kXMP_NS_BWF, "timeReference", strValue ); + changed = true; + } + + // special case for timeReference // bext:timeReference <-> iXML:BWF_TIME_REFERENCE_LOW and iXML:BWF_TIME_REFERENCE_HIGH + if ( inNativeMeta.valueExists( iXMLMetadata::kBWFTimeReferenceHigh ) && inNativeMeta.valueExists( iXMLMetadata::kBWFTimeReferenceLow ) ) { + XMP_Uns64 combinedValue = inNativeMeta.getValue< XMP_Uns64 >( iXMLMetadata::kBWFTimeReferenceHigh ); + combinedValue = combinedValue << 32; + combinedValue += inNativeMeta.getValue< XMP_Uns64 >( iXMLMetadata::kBWFTimeReferenceLow ); + std::string strValue; + SXMPUtils::ConvertFromInt64( combinedValue, "%llu", &strValue ); + outXMP.SetProperty( kXMP_NS_BWF, "timeReference", strValue ); + changed = true; + } + + // special case for xmpDM:startTimecode\xmpDM:timeFormat + if ( inNativeMeta.valueExists( iXMLMetadata::kTimeCodeRate ) ) + { + std::string ixmlTimecodeRateValue = inNativeMeta.getValue<std::string>( iXMLMetadata::kTimeCodeRate ); + std::string ixmlTimecodeFlagValue = "NDF"; + const char * xmpValue = NULL; + bool matchingValueFound = false; + if ( inNativeMeta.valueExists( iXMLMetadata::kTimeCodeFlag ) ) + { + ixmlTimecodeFlagValue = inNativeMeta.getValue<std::string>( iXMLMetadata::kTimeCodeFlag ); + } + + for ( size_t i = 0, total = sizeof(ixmlTimeCodeRateAndFlagMappings)/sizeof(iXMLTimeCodeRateAndFlagMapping); i < total; i++ ) + { + if ( ( ixmlTimecodeRateValue.compare(ixmlTimeCodeRateAndFlagMappings[i].ixmlTimeCodeRateValue) == 0 ) && + ( ixmlTimecodeFlagValue.compare(ixmlTimeCodeRateAndFlagMappings[i].ixmlTimeCodeFlagValue) == 0 ) ) + { + xmpValue = ixmlTimeCodeRateAndFlagMappings[i].xmpStringValue; + matchingValueFound = true; + break; + } + } + if ( matchingValueFound ) + { + outXMP.SetProperty( kXMP_NS_BWF, kDM_timeFormat, xmpValue ); + changed = true; + } + } + + return changed; +} diff --git a/XMPFiles/source/FormatSupport/WAVE/WAVEReconcile.h b/XMPFiles/source/FormatSupport/WAVE/WAVEReconcile.h index 44a4ef3..7f0c2b8 100644 --- a/XMPFiles/source/FormatSupport/WAVE/WAVEReconcile.h +++ b/XMPFiles/source/FormatSupport/WAVE/WAVEReconcile.h @@ -59,6 +59,12 @@ private: * convert a 4 character string to XPM_Uns32 (FOURCC) */ static bool stringToFOURCC ( std::string input, XMP_Uns32 &output ); + + // export all the properties requiring special conversion from inXMP into iXMLMetadata. + static void exportSpecialXMPToiXML( SXMPMeta & inXMP, IMetadata & outNativeMeta, PropertyList & propertiesToBeDeleted ); + + // export all the properties requiring special conversion from iXMLMetadata into inXMP. + static bool exportSpecialiXMLToXMP( IMetadata & inNativeMeta, SXMPMeta & outXMP ); }; } diff --git a/XMPFiles/source/FormatSupport/WAVE/iXMLMetadata.cpp b/XMPFiles/source/FormatSupport/WAVE/iXMLMetadata.cpp new file mode 100644 index 0000000..5df7439 --- /dev/null +++ b/XMPFiles/source/FormatSupport/WAVE/iXMLMetadata.cpp @@ -0,0 +1,815 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2014 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "public/include/XMP_Environment.h" // ! XMP_Environment.h must be the first included header. +#include "public/include/XMP_Const.h" + +#include "XMPFiles/source/FormatSupport/WAVE/iXMLMetadata.h" +#include "source/Endian.h" + +#include "source/ExpatAdapter.hpp" +#include "XMPFiles/source/XMPFiles_Impl.hpp" +#include <algorithm> + +namespace IFF_RIFF { + + static const char * tagNames[ iXMLMetadata::kLastEntry ] = { + "TAPE", //kTape, // std::string + "TAKE", //kTake, // XMP_Uns64 + "SCENE", //kScene, // std::string + "NOTE", //kNote, // std::string + "PROJECT", //kProject, // std::string + "NO_GOOD", //kNoGood, // bool( true/false ) + "FILE_SAMPLE_RATE", //kFileSampleRate, // XMP_Uns64 + "AUDIO_BIT_DEPTH", //kAudioBitDepth, // XMP_Uns64 + "CIRCLED", //kCircled, // bool( true/false ) + "BWF_DESCRIPTION", //kBWFDescription, // std::string( 256 ) + "BWF_ORIGINATOR", //kBWFOriginator, // std::string( 32 ) + "BWF_ORIGINATOR_REFERENCE", //kBWFOriginatorReference, // std::string( 32 ) + "BWF_ORIGINATION_DATE", //kBWFOriginationDate, // std::string( 10 ) + "BWF_ORIGINATION_TIME", //kBWFOriginationTime, // std::string( 8 ) + "BWF_TIME_REFERENCE_LOW", //kBWFTimeReferenceLow, // XMP_Uns32 + "BWF_TIME_REFERENCE_HIGH", //kBWFTimeReferenceHigh, // XMP_Uns32 + "BWF_VERSION", //kBWFVersion, // XMP_Uns16 + "BWF_UMID", //kBWFUMID, // std::string[64] + "BWF_CODING_HISTORY", //kBWFHistory, // std::string + "TIMECODE_FLAG", //kTimeCodeFlag, // std::string[DF/NDF] + "TIMECODE_RATE", //kTimeCodeRate, // std::string + "TIMESTAMP_SAMPLE_RATE", //kTimeStampSampleRate, // XMP_Uns64 + "TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_LO", //kTimeStampSampleSinceMidnightLow, // XMP_Uns32 + "TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_HI", //kTimeStampSampleSinceMidnightHi, // XMP_Uns32 + }; + + static const char * rootTagName = "BWFXML"; + static const char * speedTagName = "SPEED"; + static const char * bextTagName = "BEXT"; + + iXMLMetadata::iXMLMetadata() + : mRootNode( NULL ) + , mExpatAdapter( NULL ) + , mErrorCallback( NULL ) + , mExtraSpaceSize( 1024 ) {} + + iXMLMetadata::~iXMLMetadata() { + if ( mExpatAdapter ) { + mRootNode = NULL; + } + delete mExpatAdapter; + delete mRootNode; + mExpatAdapter = NULL; + } + +#define MAX_SZ ((size_t)~((size_t)0)) + + void iXMLMetadata::parse( const XMP_Uns8 * chunkData, XMP_Uns64 size ) { + if ( chunkData == NULL || size == 0 ) { + XMP_Error error( kXMPErr_BadBlockFormat, "iXML Metadata reconciliation failure: iXML chunk is not well formed" ); + NotifyClient( kXMPErrSev_Recoverable, error ); + return; + } + + mExpatAdapter = XMP_NewExpatAdapter ( ExpatAdapter::kUseLocalNamespaces ); + if ( mExpatAdapter == 0 ) XMP_Throw ( "iXMLMetadata: Can't create Expat adapter", kXMPErr_NoMemory ); + mExpatAdapter->SetErrorCallback( mErrorCallback ); + + try { + XMP_Uns64 parsedSize = 0; + while ( parsedSize < size ) { + XMP_Uns64 currentSize = std::min<XMP_Uns64>( size - parsedSize, MAX_SZ ); + mExpatAdapter->ParseBuffer( chunkData + parsedSize, (size_t) currentSize, false ); + parsedSize += currentSize; + } + mExpatAdapter->ParseBuffer( 0, 0, true ); + } catch( ... ) { + XMP_Error error( kXMPErr_BadBlockFormat, "iXML Metadata reconciliation failure: iXML chunk is not well formed" ); + NotifyClient( kXMPErrSev_Recoverable, error ); + return; + } + + // read the particular nodes we are interested in and store in the map + // Get the root node + + // Get the root node of the XML tree. + + XML_Node & xmlTree = mExpatAdapter->tree; + + for ( size_t i = 0, limit = xmlTree.content.size(); i < limit; ++i ) { + if ( xmlTree.content[i]->kind == kElemNode ) { + mRootNode = xmlTree.content[i]; + break; + } + } + + if ( mRootNode == NULL ) { + XMP_Error error( kXMPErr_BadBlockFormat, "iXML Metadata reconciliation failure: No Root Element present in iXML chunk" ); + NotifyClient( kXMPErrSev_Recoverable, error ); + return; + } + + XMP_StringPtr rootLocalName = mRootNode->name.c_str() + mRootNode->nsPrefixLen; + + if ( ! XMP_LitMatch ( rootLocalName, rootTagName ) ) { + XMP_Error error( kXMPErr_BadBlockFormat, "iXML Metadata reconciliation failure: Unexpected Root Element present in iXML chunk" ); + NotifyClient( kXMPErrSev_Recoverable, error ); + return; + } + + XMP_StringPtr ns = mRootNode->ns.c_str(); + + XML_NodePtr currentNode( NULL ); + + ParseAndSetProperties(); + resetChanges(); + } + + XMP_Uns64 iXMLMetadata::serialize( XMP_Uns8** buffer ) { + *buffer = NULL; + if ( mRootNode == NULL ) { + mRootNode = new XML_Node( NULL, rootTagName, kElemNode ); + if ( mRootNode == NULL ) { + XMP_Error error( kXMPErr_NoMemory, "iXML Metadata reconciliation failure: Can't create Root Node" ); + NotifyClient( kXMPErrSev_OperationFatal, error ); + return 0; + } + } + + + // Create SPEED and bext node if required + XML_Node * node = mRootNode->GetNamedElement( "", speedTagName ); + if ( node == NULL ) { + node = new XML_Node( mRootNode, speedTagName, kElemNode ); + if ( node == NULL ) { + XMP_Error error( kXMPErr_NoMemory, "iXML Metadata reconciliation failure: Can't create Speed Node" ); + NotifyClient( kXMPErrSev_OperationFatal, error ); + return 0; + } + mRootNode->content.push_back( node ); + } + + node = mRootNode->GetNamedElement( "", bextTagName ); + if ( node == NULL ) { + node = new XML_Node( mRootNode, bextTagName, kElemNode ); + if ( node == NULL ) { + XMP_Error error( kXMPErr_NoMemory, "iXML Metadata reconciliation failure: Can't create Bext Node" ); + NotifyClient( kXMPErrSev_OperationFatal, error ); + return 0; + } + mRootNode->content.push_back( node ); + } + + UpdateProperties(); + + // get the SPEED and bext node and remove them if empty + if ( node->content.size() == 0 ) + RemoveXMLNode( mRootNode, bextTagName ); + node = mRootNode->GetNamedElement( "", speedTagName ); + if ( node->content.size() == 0 ) + RemoveXMLNode( mRootNode, speedTagName ); + node = NULL; + + std::string strBuffer; + mRootNode->Serialize( &strBuffer ); + + // move the contents of the string into the new bigger buffer + size_t newSize = strBuffer.size() + mExtraSpaceSize; + XMP_Uns8 * newBuffer = new XMP_Uns8[ newSize ]; + memset( newBuffer, 0x20, newSize ); + memcpy( newBuffer, "<\?xml version=\"1.0\" encoding=\"UTF-8\"\?>\n", 39 ); + memcpy( newBuffer + 39, strBuffer.c_str(), strBuffer.size() ); + + *buffer = newBuffer; + + return newSize; + } + + bool iXMLMetadata::isEmptyValue( XMP_Uns32 id, ValueObject& valueObj ) { + bool ret = true; + + switch( id ) + { + case kTape: + case kScene: + case kNote: + case kProject: + case kBWFDescription: + case kBWFOriginator: + case kBWFOriginatorReference: + case kBWFOriginationDate: + case kBWFOriginationTime: + case kBWFHistory: + case kBWFUMID: + case kTimeCodeFlag: + case kTimeCodeRate: + { + TValueObject<std::string>* strObj = dynamic_cast<TValueObject<std::string>*>(&valueObj); + + ret = ( strObj == NULL || ( strObj != NULL && strObj->getValue().empty() ) ); + } + break; + + case kTake: + case kFileSampleRate: + case kAudioBitDepth: + case kBWFTimeReferenceLow: + case kBWFTimeReferenceHigh: + case kBWFVersion: + case kTimeStampSampleRate: + case kTimeStampSampleSinceMidnightLow: + case kTimeStampSampleSinceMidnightHigh: + ret = false; + break; + + case kNoGood: + case kCircled: + ret = false; + break; + + default: + ret = true; + } + + return ret; + } + + void iXMLMetadata::ParseAndSetStringProperty( XML_Node * parentNode, XMP_Uns32 id ) { + std::string nodeValue = ParseStringValue( parentNode, id ); + if ( nodeValue.size() > 0 ) { + this->setValue< std::string >( id, nodeValue ); + } + } + + void iXMLMetadata::SetErrorCallback( GenericErrorCallback * errorCallback ) { + mErrorCallback = errorCallback; + } + + void iXMLMetadata::NotifyClient( XMP_ErrorSeverity severity, XMP_Error & error ) { + XMPFileHandler::NotifyClient( mErrorCallback, severity, error ); + } + + static XMP_Uns64 ConvertStringToUns64( const std::string & strValue ) { + int count; + char nextCh; + XMP_Uns64 result; + + count = sscanf ( strValue.c_str(), "%llu%c", &result, &nextCh ); + if ( count != 1 ) XMP_Throw ( "Invalid integer string", kXMPErr_BadParam ); + + return result; + } + + void iXMLMetadata::ParseAndSetIntegerProperty( XML_Node * parentNode, XMP_Uns32 id ) { + std::string strValue = ParseStringValue( parentNode, id ); + + if ( strValue.size() > 0 ) { + XMP_Uns64 uValue; + try { + uValue = ConvertStringToUns64( strValue ); + } catch( ... ) { + // some nodes like tape can be non integer also. Treat it as warning + XMP_Error error( kXMPErr_BadFileFormat, "iXML Metadata reconciliation failure: node is supposed to have integer value" ); + NotifyClient( kXMPErrSev_Recoverable, error ); + return; + } + this->setValue< XMP_Uns64 >( id, uValue ); + } + } + + void iXMLMetadata::ParseAndSetBoolProperty( XML_Node * parentNode, XMP_Uns32 id ) { + std::string strValue = ParseStringValue( parentNode, id ); + if ( strValue.size() > 0 ) { + if ( strValue.compare( "TRUE" ) == 0 ) + this->setValue< bool >( id, true ); + else if ( strValue.compare( "FALSE" ) == 0 ) + this->setValue< bool >( id, false ); + else { + XMP_Error error( kXMPErr_BadBlockFormat, "iXML Metadata reconciliation failure: invalid boolean value present" ); + NotifyClient( kXMPErrSev_Recoverable, error ); + } + } + } + + void iXMLMetadata::ParseAndSetProperties() { + + // top level properties + ParseAndSetStringProperty( mRootNode, kTape ); + ParseAndSetIntegerProperty( mRootNode, kTake ); + ParseAndSetStringProperty( mRootNode, kScene ); + ParseAndSetStringProperty( mRootNode, kNote ); + ParseAndSetStringProperty( mRootNode, kProject ); + ParseAndSetBoolProperty( mRootNode, kNoGood ); + ParseAndSetBoolProperty( mRootNode, kCircled ); + + // speed node children + XML_Node * speedNode = mRootNode->GetNamedElement( "", speedTagName ); + if ( speedNode ) { + ParseAndSetIntegerProperty( speedNode, kFileSampleRate ); + ParseAndSetIntegerProperty( speedNode, kAudioBitDepth ); + ParseAndSetStringProperty( speedNode, kTimeCodeFlag ); + ParseAndSetStringProperty( speedNode, kTimeCodeRate ); + ParseAndSetIntegerProperty( speedNode, kTimeStampSampleRate ); + ParseAndSetIntegerProperty( speedNode, kTimeStampSampleSinceMidnightLow ); + ParseAndSetIntegerProperty( speedNode, kTimeStampSampleSinceMidnightHigh ); + } + + // bext node children + XML_Node * bextNode = mRootNode->GetNamedElement( "", bextTagName ); + if ( bextNode ) { + ParseAndSetStringProperty( bextNode, kBWFDescription ); + ParseAndSetStringProperty( bextNode, kBWFOriginator ); + ParseAndSetStringProperty( bextNode, kBWFOriginatorReference ); + ParseAndSetStringProperty( bextNode, kBWFOriginationDate ); + ParseAndSetStringProperty( bextNode, kBWFOriginationTime ); + ParseAndSetIntegerProperty( bextNode, kBWFTimeReferenceLow ); + ParseAndSetIntegerProperty( bextNode, kBWFTimeReferenceHigh ); + ParseAndSetIntegerProperty( bextNode, kBWFVersion ); + ParseAndSetStringProperty( bextNode, kBWFHistory ); + ParseAndSetStringProperty( bextNode, kBWFUMID ); + } + } + + void iXMLMetadata::UpdateStringProperty( XML_Node * parentNode, XMP_Uns32 id ) { + if ( valueExists( id ) ) { + std::string value; + try { + value = this->getValue< std::string >( id ); + } catch ( ... ) { + XMP_Error e1( kXMPErr_BadValue, "iXML Metadata reconciliation failure: expected the value to be of string type" ); + NotifyClient( kXMPErrSev_Recoverable, e1 ); + return; + } + UpdateXMLNode( parentNode, tagNames[ id ], value ); + } else { + RemoveXMLNode( parentNode, tagNames[ id ] ); + } + } + + void iXMLMetadata::UpdateXMLNode( XML_Node * parentNode, const char * localName, const std::string & value ) { + XML_Node * node = parentNode->GetNamedElement( "", localName ); + + if ( node == NULL ) { + node = new XML_Node( parentNode, localName, kElemNode ); + if ( node == NULL ) { + XMP_Error error( kXMPErr_NoMemory, "Unable to create new objects" ); + NotifyClient( kXMPErrSev_OperationFatal, error ); + return; + } + parentNode->content.push_back( node ); + } + + if ( node->IsLeafContentNode() == false ) { + XMP_Error error( kXMPErr_BadBlockFormat, "iXML Metadata reconciliation failure: node was supposed to be a leaf node" ); + NotifyClient( kXMPErrSev_Recoverable, error ); + node->RemoveContent(); + } + node->SetLeafContentValue( value.c_str() ); + } + + void iXMLMetadata::RemoveXMLNode( XML_Node * parentNode, const char * localName ) { + XML_Node * node = parentNode->GetNamedElement( "", localName ); + if ( node ) { + // find the position + XML_NodeVector::iterator it = std::find( parentNode->content.begin(), parentNode->content.end(), node ); + XMP_Assert( it != parentNode->content.end() ); + parentNode->content.erase( it ); + delete node; + } + } + +#if XMP_WinBuild + #define snprintf _snprintf +#endif + static std::string ConvertUns64ToString( XMP_Uns64 uValue ) { + char buffer[64]; + + snprintf( buffer, sizeof( buffer ), "%llu", uValue ); + std::string str( buffer ); + return str; + } + + void iXMLMetadata::UpdateIntegerProperty( XML_Node * parentNode, XMP_Uns32 id ) { + if ( valueExists( id ) ) { + XMP_Uns64 uValue; + try { + uValue = this->getValue< XMP_Uns64 >( id ); + } catch ( ... ) { + XMP_Error error( kXMPErr_BadValue, "iXML Metadata reconciliation failure: expected the value to be of XMP_Uns64 type" ); + NotifyClient( kXMPErrSev_Recoverable, error ); + return; + } + std::string strValue = ConvertUns64ToString( uValue ); + UpdateXMLNode( parentNode, tagNames[ id ], strValue ); + } else { + RemoveXMLNode( parentNode, tagNames[ id ] ); + } + } + + void iXMLMetadata::UpdateBoolProperty( XML_Node * parentNode, XMP_Uns32 id ) { + if ( valueExists( id ) ) { + bool value; + + try { + value = this->getValue< bool >( id ); + } catch ( ... ) { + XMP_Error error( kXMPErr_BadValue, "iXML Metadata reconciliation failure: expected the value to be of bool type" ); + NotifyClient( kXMPErrSev_Recoverable, error ); + return; + } + + std::string strValue; + if ( value ) strValue = "TRUE"; + else strValue = "FALSE"; + + UpdateXMLNode( parentNode, tagNames[ id ], strValue ); + } else { + RemoveXMLNode( parentNode, tagNames[ id ] ); + } + } + + void iXMLMetadata::UpdateProperties() { + // top level properties + UpdateStringProperty( mRootNode, kTape ); + UpdateIntegerProperty( mRootNode, kTake ); + UpdateStringProperty( mRootNode, kScene ); + UpdateStringProperty( mRootNode, kNote ); + UpdateStringProperty( mRootNode, kProject ); + UpdateBoolProperty( mRootNode, kNoGood ); + UpdateBoolProperty( mRootNode, kCircled ); + + // speed node children + XML_Node * speedNode = mRootNode->GetNamedElement( "", speedTagName ); + if ( speedNode ) { + UpdateIntegerProperty( speedNode, kFileSampleRate ); + UpdateIntegerProperty( speedNode, kAudioBitDepth ); + UpdateStringProperty( speedNode, kTimeCodeFlag ); + UpdateStringProperty( speedNode, kTimeCodeRate ); + UpdateIntegerProperty( speedNode, kTimeStampSampleRate ); + UpdateIntegerProperty( speedNode, kTimeStampSampleSinceMidnightLow ); + UpdateIntegerProperty( speedNode, kTimeStampSampleSinceMidnightHigh ); + } + + // bext node children + XML_Node * bextNode = mRootNode->GetNamedElement( "", bextTagName ); + if ( bextNode ) { + UpdateStringProperty( bextNode, kBWFDescription ); + UpdateStringProperty( bextNode, kBWFOriginator ); + UpdateStringProperty( bextNode, kBWFOriginatorReference ); + UpdateStringProperty( bextNode, kBWFOriginationDate ); + UpdateStringProperty( bextNode, kBWFOriginationTime ); + UpdateIntegerProperty( bextNode, kBWFTimeReferenceLow ); + UpdateIntegerProperty( bextNode, kBWFTimeReferenceHigh ); + UpdateIntegerProperty( bextNode, kBWFVersion ); + UpdateStringProperty( bextNode, kBWFHistory ); + UpdateStringProperty( bextNode, kBWFUMID ); + } + + } + + bool iXMLMetadata::valueValid( XMP_Uns32 id, ValueObject * valueObj ) { + switch( id ) { + case kTape: + return validateStringSize( valueObj ); + break; + + case kTake: + return validateInt( valueObj ); + break; + + case kScene: + return validateStringSize( valueObj ); + break; + + case kNote: + return validateStringSize( valueObj ); + break; + + case kProject: + return validateStringSize( valueObj ); + break; + + case kNoGood: + return validateBool( valueObj ); + break; + + case kFileSampleRate: + return validateInt( valueObj ); + break; + + case kAudioBitDepth: + return validateInt( valueObj ); + break; + + case kCircled: + return validateBool( valueObj ); + break; + + case kBWFDescription: + case kBWFOriginator: + case kBWFOriginatorReference: + // string length can be anything but while setting it will be trimmed. + return validateStringSize( valueObj ); + break; + + case kBWFOriginationDate: + return validateDate( valueObj ); + break; + + case kBWFOriginationTime: + return validateTime( valueObj ); + break; + + case kBWFTimeReferenceLow: + case kBWFTimeReferenceHigh: + return validateInt( valueObj, 0, Max_XMP_Uns32 ); + break; + + case kBWFVersion: + return validateInt( valueObj, 0, Max_XMP_Uns16 ); + break; + + case kBWFUMID: + return validateUMID( valueObj ); + break; + + case kBWFHistory: + return validateStringSize( valueObj ); + break; + + case kTimeCodeFlag: + return validateTimeCodeFlag( valueObj ); + break; + + case kTimeCodeRate: + return validateRational( valueObj ); + break; + + case kTimeStampSampleRate: + return validateInt( valueObj ); + break; + + case kTimeStampSampleSinceMidnightHigh: + case kTimeStampSampleSinceMidnightLow: + return validateInt( valueObj, 0, Max_XMP_Uns32 ); + break; + + default: + return false; + break; + } + } + + void iXMLMetadata::valueModify( XMP_Uns32 id, ValueObject * value ) { + switch( id ) { + case kBWFDescription: + shortenString( value, 256 ); + break; + + case kBWFOriginator: + shortenString( value, 32 ); + break; + + case kBWFOriginatorReference: + shortenString( value, 32 ); + break; + + case kBWFUMID: + shortenString( value, 128 ); + break; + + default: + // do nothing + break; + } + } + + bool iXMLMetadata::validateStringSize( ValueObject * value, size_t minSize /*= 1*/, size_t maxSize /*= std::string::npos */ ) { + TValueObject<std::string>* strObj = dynamic_cast<TValueObject<std::string>*>(value); + if ( strObj ) { + const std::string * strPtr = &strObj->getValue(); + size_t sizeOfString = strPtr->size(); + if ( sizeOfString < minSize ) { + XMP_Error error( kXMPErr_BadValue, "iXML Metadata reconciliation failure: length of string is less than expected" ); + NotifyClient( kXMPErrSev_Recoverable, error ); + return false; + } else if ( sizeOfString > maxSize ) { + XMP_Error error( kXMPErr_BadBlockFormat, "iXML Metadata reconciliation failure: length of string is more than expected" ); + NotifyClient( kXMPErrSev_Recoverable, error); + return false; + } else { + return true; + } + } else { // object is not of string type + XMP_Error error( kXMPErr_BadValue, "iXML Metadata reconciliation failure: expected string value" ); + NotifyClient( kXMPErrSev_Recoverable, error ); + return false; + } + return false; + } + + bool iXMLMetadata::validateInt( ValueObject * value, XMP_Uns64 minValue /*= 0*/, XMP_Uns64 maxValue /*= Max_XMP_Uns64*/ ) { + TValueObject< XMP_Uns64 > * valuePtr = dynamic_cast< TValueObject< XMP_Uns64 > * > ( value ); + + if ( valuePtr ) { + XMP_Uns64 uValue = valuePtr->getValue(); + if ( uValue < minValue ) { + XMP_Error error( kXMPErr_BadBlockFormat, "iXML Metadata reconciliation failure: node integer value is less than allowed" ); + NotifyClient( kXMPErrSev_Recoverable, error ); + return false; + } else if ( uValue > maxValue ) { + XMP_Error error( kXMPErr_BadBlockFormat, "iXML Metadata reconciliation failure: node integer value is more than allowed" ); + NotifyClient( kXMPErrSev_Recoverable, error ); + return false; + } else { + return true; + } + } else { + XMP_Error error( kXMPErr_BadValue, "iXML Metadata reconciliation failure: expected XMP_Uns64 value" ); + NotifyClient( kXMPErrSev_Recoverable, error ); + return false; + } + } + + bool iXMLMetadata::validateBool( ValueObject * value ) { + // just check typecasts is possible or not + TValueObject< bool > * boolValuePtr = dynamic_cast< TValueObject< bool > * > ( value ); + if ( boolValuePtr ) { + return true; + } else { + XMP_Error error( kXMPErr_BadValue, "iXML Metadata reconciliation failure: expected bool value" ); + NotifyClient( kXMPErrSev_Recoverable, error ); + return false; + } + } + + void iXMLMetadata::shortenString( ValueObject * value, size_t lengthOfString ) { + TValueObject< std::string > * strObj = dynamic_cast< TValueObject< std::string > * >( value ); + if ( strObj ) { + const std::string * strPtr = &strObj->getValue(); + size_t sizeOfString = strPtr->size(); + if ( sizeOfString > lengthOfString ) { + std::string newString; + newString.append( *strPtr, 0, lengthOfString ); + strObj->setValue( newString ); + } + } + } + + static bool charIsNumber( const char & ch ) { + if ( ch >= '0' && ch <= '9' ) + return true; + else + return false; + } + + bool iXMLMetadata::validateDate( ValueObject * value ) { + bool stringOK = validateStringSize( value, 10, 10 ); + if ( stringOK ) { + TValueObject< std::string > * strObj = dynamic_cast< TValueObject< std::string > * >( value ); + const std::string * strPtr = &strObj->getValue(); + // check 0,1,2,3,5,6,8,9 elements are integer + for ( size_t i = 0; i < 10; i++ ) { + if ( i == 4 || i == 7 ) + continue; + stringOK = charIsNumber( strPtr->operator[]( i ) ); + if ( !stringOK ) { + XMP_Error error( kXMPErr_BadValue, "iXML Metadata reconciliation failure: expected a number character" ); + NotifyClient( kXMPErrSev_Recoverable, error ); + return false; + } + } + return stringOK; + } + return false; + } + + bool iXMLMetadata::validateTime( ValueObject * value ) { + bool stringOK = validateStringSize( value, 8, 8 ); + if ( stringOK ) { + TValueObject< std::string > * strObj = dynamic_cast< TValueObject< std::string > * >( value ); + const std::string * strPtr = &strObj->getValue(); + // check 0,1,3,4,6,7 elements are integer + for ( size_t i = 0; i < 8; i++ ) { + if ( i == 2 || i == 5 ) + continue; + stringOK = charIsNumber( strPtr->operator[]( i ) ); + if ( !stringOK ) { + XMP_Error error( kXMPErr_BadValue, "iXML Metadata reconciliation failure: expected a number character" ); + NotifyClient( kXMPErrSev_Recoverable, error ); + return false; + } + } + return stringOK; + } + return false; + } + + static bool charIsHexDigit( const char & ch ) { + if ( charIsNumber( ch ) ) { + return true; + } else { + if ( ch >= 'A' && ch <= 'F' ) + return true; + else if ( ch >= 'a' && ch <= 'f' ) + return true; + else + return false; + } + } + + bool iXMLMetadata::validateUMID( ValueObject * value ) { + bool stringOK = validateStringSize( value ); + if ( stringOK ) { + // max size to consider is 128 + TValueObject< std::string > * strObj = dynamic_cast< TValueObject< std::string > * >( value ); + const std::string * strPtr = &strObj->getValue(); + size_t effectiveLength = strPtr->size(); + if ( effectiveLength > 128 ) + effectiveLength = 128; + + // first check length needs to even + if ( effectiveLength % 2 == 0 ) { + for ( size_t i = 0; i < effectiveLength; i++ ) { + stringOK = charIsHexDigit( strPtr->operator[]( i ) ); + if ( !stringOK ) { + XMP_Error error( kXMPErr_BadValue, "iXML Metadata reconciliation failure: expected a hex character" ); + NotifyClient( kXMPErrSev_Recoverable, error ); + return false; + } + } + return stringOK; + } else { + XMP_Error error( kXMPErr_BadValue, "iXML Metadata reconciliation failure: expected the hex string length to be even" ); + NotifyClient( kXMPErrSev_Recoverable, error ); + return false; + } + } + return false; + } + + std::string iXMLMetadata::ParseStringValue( XML_Node * parentNode, XMP_Uns32 id ) { + std::string nodeValue; + XML_Node * node = parentNode->GetNamedElement( "", tagNames[ id ] ); + if ( node ) { + if ( node->IsLeafContentNode() && node->content.size() != 0 ) { + size_t lengthOfValue = node->content[0]->value.size(); + if ( lengthOfValue > 0 ) { + nodeValue = node->content[0]->value; + } + } else { + XMP_Error error( kXMPErr_BadBlockFormat, "iXML Metadata reconciliation failure: node was supposed to be a leaf node" ); + NotifyClient( kXMPErrSev_Recoverable, error ); + } + } + return nodeValue; + } + + bool iXMLMetadata::validateTimeCodeFlag( ValueObject * value ) { + bool returnValue = validateStringSize( value, 2, 3 ); + if ( returnValue ) { + TValueObject<std::string>* strObj = dynamic_cast<TValueObject<std::string>*>(value); + if ( strObj ) { + const std::string * strPtr = &strObj->getValue(); + if ( strPtr->compare( "DF" ) == 0 ) + return true; + else if ( strPtr->compare( "NDF" ) == 0 ) + return true; + } + } + return false; + } + + bool iXMLMetadata::validateRational( ValueObject * value ) { + bool returnValue = validateStringSize( value, 3 ); + if ( returnValue ) { + TValueObject<std::string>* strObj = dynamic_cast<TValueObject<std::string>*>(value); + if ( strObj ) { + const std::string * strPtr = &strObj->getValue(); + size_t posOfSlash = strPtr->find( "/" ); + if ( posOfSlash == std::string::npos || posOfSlash == strPtr->size() - 1 || posOfSlash == 0 ) { + XMP_Error error( kXMPErr_BadValue, "iXML Metadata reconciliation failure: node value was supposed to be in a fractional format" ); + NotifyClient( kXMPErrSev_Recoverable, error ); + return false; + } + + for ( size_t i = 0; i < strPtr->size(); i++ ) { + if ( i == posOfSlash ) + continue; + returnValue = charIsNumber( strPtr->operator[]( i ) ); + if ( ! returnValue ) { + XMP_Error error( kXMPErr_BadValue, "iXML Metadata reconciliation failure: expected a number character" ); + NotifyClient( kXMPErrSev_Recoverable, error ); + return false; + } + } + return returnValue; + } + } + return false; + } + +} diff --git a/XMPFiles/source/FormatSupport/WAVE/iXMLMetadata.h b/XMPFiles/source/FormatSupport/WAVE/iXMLMetadata.h new file mode 100644 index 0000000..815fe85 --- /dev/null +++ b/XMPFiles/source/FormatSupport/WAVE/iXMLMetadata.h @@ -0,0 +1,157 @@ +#ifndef __iXMLMetadata_h__ +#define __iXMLMetadata_h__ 1 + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2014 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "public/include/XMP_Environment.h" // ! XMP_Environment.h must be the first included header. +#include "public/include/XMP_Const.h" +#include "public/include/XMP_IO.hpp" + +#include "XMPFiles/source/NativeMetadataSupport/IMetadata.h" + +class ExpatAdapter; +class XML_Node; + +namespace IFF_RIFF { + + /** + * iXML Metadata model. + * Implements the IMetadata interface + */ + class iXMLMetadata : public IMetadata + { + public: + enum + { + kTape, // std::string + kTake, // XMP_Uns64 + kScene, // std::string + kNote, // std::string + kProject, // std::string + kNoGood, // bool( true/false ) + kFileSampleRate, // XMP_Uns64 + kAudioBitDepth, // XMP_Uns64 + kCircled, // bool( true/false ) + kBWFDescription, // std::string + kBWFOriginator, // std::string + kBWFOriginatorReference, // std::string + kBWFOriginationDate, // std::string + kBWFOriginationTime, // std::string + kBWFTimeReferenceLow, // XMP_Uns32 + kBWFTimeReferenceHigh, // XMP_Uns32 + kBWFVersion, // XMP_Uns16 + kBWFUMID, // std::string[64] + kBWFHistory, // std::string + kTimeCodeFlag, // std::string[DF/NDF] + kTimeCodeRate, // std::string + kTimeStampSampleRate, // XMP_Uns64 + kTimeStampSampleSinceMidnightLow, // XMP_Uns32 + kTimeStampSampleSinceMidnightHigh, // XMP_Uns32 + kLastEntry + }; + + public: + /** + *ctor/dtor + */ + iXMLMetadata(); + ~iXMLMetadata(); + + /** + * Parses the given memory block and creates a data model representation + * The implementation expects that the memory block is the data area of + * the iXML chunk. + * Throws exceptions if parsing is not possible + * + * @param input The byte buffer to parse + * @param size Size of the given byte buffer + */ + void parse( const XMP_Uns8* chunkData, XMP_Uns64 size ); + + /** + * See IMetadata::parse( const LFA_FileRef input ) + */ + void parse( XMP_IO* input ) { IMetadata::parse( input ); } + + /** + * Serializes the data model to a memory block. + * The memory block will be the data area of a iXML chunk. + * Throws exceptions if serializing is not possible + * + * @param buffer Buffer that gets filled with serialized data + * @param size Size of passed in buffer + * + * @return Size of serialized data (might be smaller than buffer size) + */ + XMP_Uns64 serialize( XMP_Uns8** buffer ); + + void SetErrorCallback( GenericErrorCallback * errorCallback ); + + void SetExtraSpaceSize( size_t size ) { mExtraSpaceSize = size; } + + size_t GetExtraSpaceSize() const { return mExtraSpaceSize; } + + protected: + /** + * @see IMetadata::isEmptyValue + */ + virtual bool isEmptyValue( XMP_Uns32 id, ValueObject& valueObj ); + + /** + * @see iMetadata::valueValid + */ + virtual bool valueValid( XMP_Uns32 id, ValueObject * valueObj ); + + /** + * @see IMetadata::valueModify + */ + virtual void valueModify( XMP_Uns32 id, ValueObject * value ); + + void ParseAndSetProperties(); + void UpdateProperties(); + + std::string ParseStringValue( XML_Node * parentNode, XMP_Uns32 id ); + void ParseAndSetStringProperty( XML_Node * parentNode, XMP_Uns32 id ); + void ParseAndSetIntegerProperty( XML_Node * parentNode, XMP_Uns32 id ); + void ParseAndSetBoolProperty( XML_Node * parentNode, XMP_Uns32 id ); + + void UpdateStringProperty( XML_Node * parentNode, XMP_Uns32 id ); + void UpdateIntegerProperty( XML_Node * parentNode, XMP_Uns32 id ); + void UpdateBoolProperty( XML_Node * parentNode, XMP_Uns32 id ); + + void UpdateXMLNode( XML_Node * parentNode, const char * localName, const std::string & value ); + void RemoveXMLNode( XML_Node * parentNode, const char * localName ); + + void NotifyClient( XMP_ErrorSeverity severity, XMP_Error & error ); + + bool validateStringSize( ValueObject * value, size_t minSize = 1, size_t maxSize = std::string::npos ); + bool validateInt( ValueObject * value, XMP_Uns64 minValue = 0, XMP_Uns64 maxValue = Max_XMP_Uns64 ); + bool validateBool( ValueObject * value ); + void shortenString( ValueObject * value, size_t lengthOfString ); + bool validateDate( ValueObject * value ); + bool validateTime( ValueObject * value ); + bool validateUMID( ValueObject * value ); + bool validateTimeCodeFlag( ValueObject * value ); + bool validateRational( ValueObject * value ); + + private: + // Operators hidden on purpose + iXMLMetadata( const iXMLMetadata& ) {}; + iXMLMetadata& operator=( const iXMLMetadata& ) { return *this; }; + + ExpatAdapter * mExpatAdapter; + XML_Node * mRootNode; + GenericErrorCallback * mErrorCallback; + size_t mExtraSpaceSize; + }; + +} + +#endif // __iXMLMetadata_h__ diff --git a/XMPFiles/source/NativeMetadataSupport/IMetadata.cpp b/XMPFiles/source/NativeMetadataSupport/IMetadata.cpp index 40a864e..2a3560c 100644 --- a/XMPFiles/source/NativeMetadataSupport/IMetadata.cpp +++ b/XMPFiles/source/NativeMetadataSupport/IMetadata.cpp @@ -204,3 +204,28 @@ bool IMetadata::valueChanged( XMP_Uns32 id ) const return false; } + +//----------------------------------------------------------------------------- +// +// IMetadata::valueValid(...) +// +// Purpose: Return true if the value for the passed identifier is valid +// +//----------------------------------------------------------------------------- + +bool IMetadata::valueValid( XMP_Uns32 id, ValueObject *value ) +{ + return true; +} + +//----------------------------------------------------------------------------- +// +// IMetadata::valueValid(...) +// +// Purpose: Return true if the value for the passed identifier is valid +// +//----------------------------------------------------------------------------- +void IMetadata::valueModify(XMP_Uns32 id, ValueObject *value) +{ + return; +}
\ No newline at end of file diff --git a/XMPFiles/source/NativeMetadataSupport/IMetadata.h b/XMPFiles/source/NativeMetadataSupport/IMetadata.h index 1a66c86..b85239b 100644 --- a/XMPFiles/source/NativeMetadataSupport/IMetadata.h +++ b/XMPFiles/source/NativeMetadataSupport/IMetadata.h @@ -149,7 +149,7 @@ public: * @param id Identifier of value */ virtual bool valueChanged( XMP_Uns32 id ) const; - + protected: /** * Is the value of the passed ValueObject that belongs to the given id "empty"? @@ -164,6 +164,20 @@ protected: */ virtual bool isEmptyValue( XMP_Uns32 id, ValueObject& valueObj ) = 0; + /** + * Validates the value for the passed identifier + * @param id Identifier of value + * @param value pointer to value object + */ + virtual bool valueValid( XMP_Uns32 id, ValueObject * value ); + + /** + * Modify the value for the passed identifier + * @param id Identifier of value + * @param value pointer to value object + */ + virtual void valueModify( XMP_Uns32 id, ValueObject * value ); + private: // Operators hidden on purpose IMetadata( const IMetadata& ) {}; @@ -191,9 +205,13 @@ template<class T> void IMetadata::setValue( XMP_Uns32 id, const T& value ) // valueObj = dynamic_cast<TValueObject<T>*>( iterator->second ); - if( valueObj != NULL ) + if( valueObj != NULL ) { - valueObj->setValue( value ); + TValueObject< T > newValueObj( value ); + if ( valueValid( id, &newValueObj ) ) { + valueModify( id, &newValueObj ); + valueObj->setValue( newValueObj.getValue() ); + } } else { @@ -206,10 +224,14 @@ template<class T> void IMetadata::setValue( XMP_Uns32 id, const T& value ) // value doesn't exists yet and is not "empty" // so add a new value to the map // - valueObj = new TValueObject<T>( value ); - mValues[id] = valueObj; - - mDirty = true; // the new created value isn't dirty, but the container becomes dirty + TValueObject< T > newValueObj( value ); + if ( this->valueValid( id, &newValueObj ) ) + { + this->valueModify( id, &newValueObj ); + valueObj = new TValueObject<T>( newValueObj.getValue() ); + mValues[id] = valueObj; + mDirty = true; // the new created value isn't dirty, but the container becomes dirty + } } // diff --git a/XMPFiles/source/NativeMetadataSupport/IReconcile.cpp b/XMPFiles/source/NativeMetadataSupport/IReconcile.cpp index dd34725..b7d4405 100644 --- a/XMPFiles/source/NativeMetadataSupport/IReconcile.cpp +++ b/XMPFiles/source/NativeMetadataSupport/IReconcile.cpp @@ -141,6 +141,12 @@ bool IReconcile::importNativeToXMP( SXMPMeta& outXMP, const IMetadata& nativeMet } break; + case kNativeType_Bool: + { + SXMPUtils::ConvertFromBool( nativeMeta.getValue<bool>( propertyInfo[index].mMetadataID ), &xmpValue ); + } + break; + default: { XMP_Throw( "Unknown native data type", kXMPErr_InternalFailure ); @@ -217,7 +223,7 @@ bool IReconcile::importNativeToXMP( SXMPMeta& outXMP, const IMetadata& nativeMet // //----------------------------------------------------------------------------- -bool IReconcile::exportXMPToNative( IMetadata& outNativeMeta, SXMPMeta& inXMP, const MetadataPropertyInfo* propertyInfo ) +bool IReconcile::exportXMPToNative( IMetadata& outNativeMeta, SXMPMeta& inXMP, const MetadataPropertyInfo* propertyInfo, PropertyList * propertiesExportedSuccessfully /*= NULL*/ ) { std::string xmpValue; XMP_Uns32 index = 0; @@ -275,6 +281,8 @@ bool IReconcile::exportXMPToNative( IMetadata& outNativeMeta, SXMPMeta& inXMP, c std::string ascii; convertToASCII( xmpValue, ascii ); outNativeMeta.setValue<std::string>( propertyInfo[index].mMetadataID, ascii ); + if ( propertiesExportedSuccessfully ) + propertiesExportedSuccessfully->push_back( std::make_pair( propertyInfo[index].mXMPSchemaNS, propertyInfo[index].mXMPPropName ) ); } break; @@ -282,6 +290,8 @@ bool IReconcile::exportXMPToNative( IMetadata& outNativeMeta, SXMPMeta& inXMP, c case kNativeType_StrUTF8: { outNativeMeta.setValue<std::string>( propertyInfo[index].mMetadataID, xmpValue ); + if ( propertiesExportedSuccessfully ) + propertiesExportedSuccessfully->push_back( std::make_pair( propertyInfo[index].mXMPSchemaNS, propertyInfo[index].mXMPPropName ) ); } break; @@ -293,6 +303,8 @@ bool IReconcile::exportXMPToNative( IMetadata& outNativeMeta, SXMPMeta& inXMP, c { ReconcileUtils::UTF8ToLocal( xmpValue.c_str(), xmpValue.size(), &value ); outNativeMeta.setValue<std::string>( propertyInfo[index].mMetadataID, value ); + if ( propertiesExportedSuccessfully ) + propertiesExportedSuccessfully->push_back( std::make_pair( propertyInfo[index].mXMPSchemaNS, propertyInfo[index].mXMPPropName ) ); } catch( XMP_Error& e ) { @@ -330,6 +342,8 @@ bool IReconcile::exportXMPToNative( IMetadata& outNativeMeta, SXMPMeta& inXMP, c if( ! error && value >= 0 ) { outNativeMeta.setValue<XMP_Uns64>( propertyInfo[index].mMetadataID, static_cast<XMP_Uns64>(value) ); + if ( propertiesExportedSuccessfully ) + propertiesExportedSuccessfully->push_back( std::make_pair( propertyInfo[index].mXMPSchemaNS, propertyInfo[index].mXMPPropName ) ); } } break; @@ -358,6 +372,8 @@ bool IReconcile::exportXMPToNative( IMetadata& outNativeMeta, SXMPMeta& inXMP, c if( ! error && value >= 0 ) { outNativeMeta.setValue<XMP_Uns32>( propertyInfo[index].mMetadataID, static_cast<XMP_Uns32>(value) ); + if ( propertiesExportedSuccessfully ) + propertiesExportedSuccessfully->push_back( std::make_pair( propertyInfo[index].mXMPSchemaNS, propertyInfo[index].mXMPPropName ) ); } } break; @@ -386,6 +402,8 @@ bool IReconcile::exportXMPToNative( IMetadata& outNativeMeta, SXMPMeta& inXMP, c if( ! error ) { outNativeMeta.setValue<XMP_Int32>( propertyInfo[index].mMetadataID, static_cast<XMP_Int32>(value) ); + if ( propertiesExportedSuccessfully ) + propertiesExportedSuccessfully->push_back( std::make_pair( propertyInfo[index].mXMPSchemaNS, propertyInfo[index].mXMPPropName ) ); } } break; @@ -414,6 +432,38 @@ bool IReconcile::exportXMPToNative( IMetadata& outNativeMeta, SXMPMeta& inXMP, c if( ! error && value >= 0 ) { outNativeMeta.setValue<XMP_Uns16>( propertyInfo[index].mMetadataID, static_cast<XMP_Uns16>(value) ); + if ( propertiesExportedSuccessfully ) + propertiesExportedSuccessfully->push_back( std::make_pair( propertyInfo[index].mXMPSchemaNS, propertyInfo[index].mXMPPropName ) ); + } + } + break; + + case kNativeType_Bool: + { + bool value; + bool error = false; + + try + { + value = SXMPUtils::ConvertToBool( xmpValue ); + } + catch( XMP_Error& e ) + { + if ( e.GetID() != kXMPErr_BadParam ) + { + // rethrow exception if it wasn't caused by an + // invalid parameter for the conversion + throw e; + } + error = true; + } + + // Only write the value if it could be converted to a number and has a positive value + if( ! error ) + { + outNativeMeta.setValue<bool>( propertyInfo[index].mMetadataID, value ); + if ( propertiesExportedSuccessfully ) + propertiesExportedSuccessfully->push_back( std::make_pair( propertyInfo[index].mXMPSchemaNS, propertyInfo[index].mXMPPropName ) ); } } break; diff --git a/XMPFiles/source/NativeMetadataSupport/IReconcile.h b/XMPFiles/source/NativeMetadataSupport/IReconcile.h index af8646d..16a0109 100644 --- a/XMPFiles/source/NativeMetadataSupport/IReconcile.h +++ b/XMPFiles/source/NativeMetadataSupport/IReconcile.h @@ -42,7 +42,8 @@ enum MetadataPropertyType kNativeType_Uns64, kNativeType_Uns32, kNativeType_Int32, - kNativeType_Uns16 + kNativeType_Uns16, + kNativeType_Bool }; /** Types that describe how an XMP property is exported to native Metadata */ @@ -71,6 +72,8 @@ struct MetadataPropertyInfo class IReconcile { public: + typedef std::vector< std::pair< XMP_StringPtr, XMP_StringPtr > > PropertyList; + virtual ~IReconcile() {}; /** * Reconciles metadata from legacy formats into XMP. @@ -128,7 +131,7 @@ protected: @return true if any native metadata value were changed */ - static bool exportXMPToNative( IMetadata& outNativeMeta, SXMPMeta& inXMP, const MetadataPropertyInfo* propertyInfo ); + static bool exportXMPToNative( IMetadata& outNativeMeta, SXMPMeta& inXMP, const MetadataPropertyInfo* propertyInfo, PropertyList * propertiesExportedSuccessfully = NULL ); // Converts input string to an ascii output string // - terminates at first 0 diff --git a/XMPFiles/source/PluginHandler/FileHandler.h b/XMPFiles/source/PluginHandler/FileHandler.h index 8d68258..ca43ced 100644 --- a/XMPFiles/source/PluginHandler/FileHandler.h +++ b/XMPFiles/source/PluginHandler/FileHandler.h @@ -51,7 +51,7 @@ class FileHandler public: FileHandler(std::string & uid, XMP_OptionBits handlerFlags, FileHandlerType type, ModuleSharedPtr module): - mVersion(0), mUID(uid), mHandlerFlags(handlerFlags), mOverwrite(false), mType(type), mModule(module) {} + mVersion(0), mUID(uid), mHandlerFlags(handlerFlags), mOverwrite(false), mType(type), mModule(module),mSerializeOption(0) {} virtual ~FileHandler(){} diff --git a/XMPFiles/source/PluginHandler/Module.cpp b/XMPFiles/source/PluginHandler/Module.cpp index 595c17d..d76589c 100644 --- a/XMPFiles/source/PluginHandler/Module.cpp +++ b/XMPFiles/source/PluginHandler/Module.cpp @@ -246,10 +246,6 @@ bool Module::loadInternal() errorMsg = "Plugin initialization failed."; } } - else - { - errorMsg = "Missing plugin entry point in plugin"; - } } if( mLoaded != kModuleLoaded ) diff --git a/XMPFiles/source/PluginHandler/PluginManager.cpp b/XMPFiles/source/PluginHandler/PluginManager.cpp index 3802c9b..0f47300 100644 --- a/XMPFiles/source/PluginHandler/PluginManager.cpp +++ b/XMPFiles/source/PluginHandler/PluginManager.cpp @@ -385,7 +385,6 @@ void PluginManager::initialize( const std::string& pluginDir, const std::string& { try { - HandlerRegistry & hdlrReg = HandlerRegistry::getInstance(); if( msPluginManager == 0 ) msPluginManager = new PluginManager( pluginDir, plugins ); msPluginManager->initializeHostAPI(); @@ -817,10 +816,7 @@ void PluginManager::initializeHostAPI() break; } - if( hostAPI != NULL ) - { - msPluginManager->mHostAPIs[ hostAPI->mVersion ] = hostAPI; - } + msPluginManager->mHostAPIs[ hostAPI->mVersion ] = hostAPI; } } diff --git a/XMPFiles/source/XMPFiles.cpp b/XMPFiles/source/XMPFiles.cpp index 32ea41f..ae8f701 100644 --- a/XMPFiles/source/XMPFiles.cpp +++ b/XMPFiles/source/XMPFiles.cpp @@ -26,11 +26,16 @@ #endif #include "XMPFiles/source/FormatSupport/ID3_Support.hpp" +#include "XMPFiles/source/FormatSupport/ISOBaseMedia_Support.hpp" #if EnablePacketScanning #include "XMPFiles/source/FileHandlers/Scanner_Handler.hpp" #endif +#if EnableGenericHandling + #include "XMPFiles/source/FileHandlers/Generic_Handler.hpp" +#endif + // ================================================================================================= /// \file XMPFiles.cpp /// \brief High level support to access metadata in files of interest to Adobe applications. @@ -93,6 +98,10 @@ const char * kXMPFiles_EmbeddedCopyright = kXMPFilesName " " kXMP_CopyrightStr; (CheckFileFormatProc)0, Scanner_MetaHandlerCTor ); #endif +#if EnableGenericHandling + static XMPFileHandlerInfo kGenericHandlerInfo ( kXMP_UnknownFile, kGeneric_HandlerFlags, + (CheckFileFormatProc)0, Generic_MetaHandlerCTor ); +#endif // ================================================================================================= /* class-static */ @@ -291,6 +300,7 @@ XMPFiles::Terminate() SXMPMeta::Terminate(); // Just in case the client does not. ID3_Support::TerminateGlobals(); + ISOMedia::TerminateGlobals(); Terminate_LibUtils(); #if UseGlobalLibraryLock & (! XMP_StaticBuild ) @@ -302,6 +312,9 @@ XMPFiles::Terminate() xmpFilesLog = stderr; #endif + // reset static variables + sDefaultErrorCallback.Clear(); + sProgressDefault.Clear(); XMP_FILES_STATIC_END1 ( kXMPErrSev_ProcessFatal ) } // XMPFiles::Terminate @@ -380,9 +393,9 @@ XMPFiles::GetFormatInfo ( XMP_FileFormat format, XMP_OptionBits * flags /* = 0 */ ) { XMP_FILES_STATIC_START - return HandlerRegistry::getInstance().getFormatInfo ( format, flags ); + return HandlerRegistry::getInstance().getFormatInfo ( format, flags ); XMP_FILES_STATIC_END1 ( kXMPErrSev_OperationFatal ) - return false; + return false; } // XMPFiles::GetFormatInfo @@ -487,11 +500,12 @@ static XMPFileHandlerInfo* CreateFileHandlerInfo ( XMPFiles* dummyParent, XMP_FileFormat * format, XMP_OptionBits options, + XMP_Bool& excluded, const XMPFiles::ErrorCallbackInfo * _errorCallbackInfoPtr = NULL ) { Host_IO::FileMode clientMode; std::string fileExt; // Used to check for excluded files. - bool excluded = FileIsExcluded ( dummyParent->GetFilePath().c_str(), &fileExt, &clientMode, &sDefaultErrorCallback ); // ! Fills in fileExt and clientMode. + excluded = FileIsExcluded ( dummyParent->GetFilePath().c_str(), &fileExt, &clientMode, &sDefaultErrorCallback ); // ! Fills in fileExt and clientMode. if ( excluded ) return 0; XMPFileHandlerInfo * handlerInfo = 0; @@ -533,7 +547,26 @@ XMPFiles::GetFileModDate ( XMP_StringPtr clientPath, XMPFileHandlerInfo * handlerInfo = 0; - handlerInfo = CreateFileHandlerInfo ( &dummyParent, format, options, &sDefaultErrorCallback ); + XMP_Bool excluded=false; + handlerInfo = CreateFileHandlerInfo ( &dummyParent, format, options, excluded, &sDefaultErrorCallback ); +#if EnableGenericHandling +#if GenericHandlingAlwaysOn + XMP_OptionBits oldOptions = options; + options |= kXMPFiles_OpenUseGenericHandler; +#endif + if (handlerInfo == 0 && !excluded + && (options & kXMPFiles_OpenUseGenericHandler) ) + { + Host_IO::FileMode fileMode = Host_IO::GetFileMode( clientPath ); + if ( fileMode == Host_IO::kFMode_DoesNotExist ) + return false; + + handlerInfo = &kGenericHandlerInfo; + } +#if GenericHandlingAlwaysOn + options = oldOptions; +#endif +#endif if ( handlerInfo == 0 ) return false; // ------------------------------------------------------------------------- @@ -556,7 +589,7 @@ XMPFiles::GetFileModDate ( XMP_StringPtr clientPath, for ( size_t index = 0; index < countRes ; ++index ){ XMP_StringPtr curFilePath = resourceList[index].c_str(); if( Host_IO::GetFileMode ( curFilePath ) != Host_IO::kFMode_IsFile ) continue;// only interested in files - Host_IO::GetModifyDate ( curFilePath, &lastModDate ); + if (!Host_IO::GetModifyDate ( curFilePath, &lastModDate ) ) continue; if ( ! ok || ( SXMPUtils::CompareDateTime ( *modDate , lastModDate ) < 0 ) ) { *modDate = lastModDate; @@ -602,7 +635,26 @@ XMPFiles::GetAssociatedResources ( dummyParent.SetFilePath ( filePath ); XMPFileHandlerInfo * handlerInfo = 0; - handlerInfo = CreateFileHandlerInfo ( &dummyParent, &format, options, &sDefaultErrorCallback ); + XMP_Bool excluded=false; + handlerInfo = CreateFileHandlerInfo ( &dummyParent, &format, options, excluded, &sDefaultErrorCallback ); +#if EnableGenericHandling +#if GenericHandlingAlwaysOn + XMP_OptionBits oldOptions = options; + options |= kXMPFiles_OpenUseGenericHandler; +#endif + if (handlerInfo == 0 && !excluded + && (options & kXMPFiles_OpenUseGenericHandler) ) + { + Host_IO::FileMode fileMode = Host_IO::GetFileMode( filePath ); + if ( fileMode == Host_IO::kFMode_DoesNotExist ) + return false; + + handlerInfo = &kGenericHandlerInfo; + } +#if GenericHandlingAlwaysOn + options = oldOptions; +#endif +#endif if ( handlerInfo == 0 ) return false; // ------------------------------------------------------------------------- @@ -650,7 +702,26 @@ XMPFiles::IsMetadataWritable ( dummyParent.SetFilePath ( filePath ); XMPFileHandlerInfo * handlerInfo = 0; - handlerInfo = CreateFileHandlerInfo ( &dummyParent, &format, options, &sDefaultErrorCallback ); + XMP_Bool excluded=false; + handlerInfo = CreateFileHandlerInfo ( &dummyParent, &format, options, excluded, &sDefaultErrorCallback ); +#if EnableGenericHandling +#if GenericHandlingAlwaysOn + XMP_OptionBits oldOptions = options; + options |= kXMPFiles_OpenUseGenericHandler; +#endif + if (handlerInfo == 0 && !excluded + && (options & kXMPFiles_OpenUseGenericHandler)) + { + Host_IO::FileMode fileMode = Host_IO::GetFileMode( filePath ); + if ( fileMode == Host_IO::kFMode_DoesNotExist ) + return false; + + handlerInfo = &kGenericHandlerInfo; + } +#if GenericHandlingAlwaysOn + options = oldOptions; +#endif +#endif if ( handlerInfo == 0 ) return false; if ( writable == 0 ) { @@ -670,8 +741,9 @@ XMPFiles::IsMetadataWritable ( try { *writable = ConvertBoolToXMP_Bool( dummyParent.handler->IsMetadataWritable() ); - } - catch ( XMP_Error& error ) { + } catch ( XMP_Error& error ) { + delete dummyParent.handler; + dummyParent.handler = 0; if ( error.GetID() == kXMPErr_Unimplemented ) { XMP_FILES_STATIC_NOTIFY_ERROR ( &sDefaultErrorCallback, filePath, kXMPErrSev_Recoverable, error ); return false; @@ -679,8 +751,10 @@ XMPFiles::IsMetadataWritable ( throw; } } - delete dummyParent.handler; - dummyParent.handler = 0; + if ( dummyParent.handler ) { + delete dummyParent.handler; + dummyParent.handler = 0; + } XMP_FILES_STATIC_END2 ( filePath, kXMPErrSev_OperationFatal ) return true; } // XMPFiles::IsMetadataWritable @@ -699,6 +773,10 @@ DoOpenFile ( XMPFiles * thiz, openFlags &= ~kXMPFiles_ForceGivenHandler; // Don't allow this flag for OpenFile. + if ( (openFlags & kXMPFiles_OptimizeFileLayout) && (! (openFlags & kXMPFiles_OpenForUpdate)) ) { + XMP_Throw ( "OptimizeFileLayout requires OpenForUpdate", kXMPErr_BadParam ); + } + if ( thiz->handler != 0 ) XMP_Throw ( "File already open", kXMPErr_BadParam ); CloseLocalFile ( thiz ); // Sanity checks if prior call failed. @@ -728,6 +806,24 @@ DoOpenFile ( XMPFiles * thiz, if ( ! (openFlags & kXMPFiles_OpenUsePacketScanning) ) { handlerInfo = HandlerRegistry::getInstance().selectSmartHandler( thiz, clientPath, format, openFlags ); +#if EnableGenericHandling +#if GenericHandlingAlwaysOn + XMP_OptionBits oldOpenFlags = openFlags; + openFlags |= kXMPFiles_OpenUseGenericHandler; +#endif + if (handlerInfo==0 + && !(openFlags & kXMPFiles_OpenStrictly) + && (openFlags & kXMPFiles_OpenUseGenericHandler)) + { + if ( clientMode == Host_IO::kFMode_DoesNotExist ) + return false; + + handlerInfo = &kGenericHandlerInfo; + } +#if GenericHandlingAlwaysOn + openFlags = oldOpenFlags; +#endif +#endif } #if ! EnablePacketScanning @@ -754,11 +850,21 @@ DoOpenFile ( XMPFiles * thiz, } if ( openFlags & kXMPFiles_OpenUseSmartHandler ) { + CloseLocalFile ( thiz ); XMP_Error error ( kXMPErr_NoFileHandler, "XMPFiles: No smart file handler available to handle file" ); XMP_FILES_STATIC_NOTIFY_ERROR ( &thiz->errorCallback, clientPath, kXMPErrSev_Recoverable, error ); return false; } +#if EnableGenericHandling + if ( openFlags & kXMPFiles_OpenUseGenericHandler ) { + CloseLocalFile ( thiz ); + XMP_Error error ( kXMPErr_NoFileHandler, "XMPFiles: Generic handler not available to handle file" ); + XMP_FILES_STATIC_NOTIFY_ERROR ( &thiz->errorCallback, clientPath, kXMPErrSev_Recoverable, error ); + return false; + } +#endif + if ( openFlags & kXMPFiles_OpenLimitedScanning ) { bool scanningOK = false; for ( size_t i = 0; kKnownScannedFiles[i] != 0; ++i ) { @@ -793,6 +899,12 @@ DoOpenFile ( XMPFiles * thiz, thiz->handler = handler; try { + if ( !readOnly && handlerFlags & kXMPFiles_FolderBasedFormat ) { + bool isMetadataWritable = handler->IsMetadataWritable(); + if ( !isMetadataWritable ) { + XMP_Throw ( "Open, file permission error", kXMPErr_FilePermission ); + } + } handler->CacheFileData(); } catch ( ... ) { delete thiz->handler; @@ -824,6 +936,9 @@ static bool DoOpenFile( XMPFiles* thiz, openFlags &= ~kXMPFiles_ForceGivenHandler; // Don't allow this flag for OpenFile. + if ( (openFlags & kXMPFiles_OptimizeFileLayout) && (! (openFlags & kXMPFiles_OpenForUpdate)) ) { + XMP_Throw ( "OptimizeFileLayout requires OpenForUpdate", kXMPErr_BadParam ); + } if ( thiz->handler != 0 ) XMP_Throw ( "File already open", kXMPErr_BadParam ); @@ -950,6 +1065,8 @@ XMPFiles::CloseFile ( XMP_OptionBits closeFlags /* = 0 */ ) if ( this->handler == 0 ) return; // Return if there is no open file (not an error). bool needsUpdate = this->handler->needsUpdate; + bool optimizeFileLayout = XMP_OptionIsSet ( this->openFlags, kXMPFiles_OptimizeFileLayout ); + XMP_OptionBits handlerFlags = this->handler->handlerFlags; // Decide if we're doing a safe update. If so, make sure the handler supports it. All handlers @@ -982,6 +1099,8 @@ XMPFiles::CloseFile ( XMP_OptionBits closeFlags /* = 0 */ ) // Close the file without doing common crash-safe writing. The handler might do it. + needsUpdate |= optimizeFileLayout; + if ( needsUpdate ) { #if GatherPerformanceData sAPIPerf->back().extraInfo += ", direct update"; @@ -1057,7 +1176,10 @@ XMPFiles::CloseFile ( XMP_OptionBits closeFlags /* = 0 */ ) // *** Don't delete the temp or copy files, not sure which is best. try { - if ( this->handler != 0 ) delete this->handler; + if ( this->handler != 0 ) { + delete this->handler; + this->handler = 0; + } } catch ( ... ) { /* Do nothing, throw the outer exception later. */ } if ( this->ioRef ) this->ioRef->DeleteTemp(); diff --git a/XMPFiles/source/XMPFiles.hpp b/XMPFiles/source/XMPFiles.hpp index 70ab020..e3ecb8b 100644 --- a/XMPFiles/source/XMPFiles.hpp +++ b/XMPFiles/source/XMPFiles.hpp @@ -17,7 +17,7 @@ #include "public/include/XMP.hpp" #include "public/include/XMP_IO.hpp" - +#include "source/SafeStringAPIs.h" #include "source/XMP_ProgressTracker.hpp" class XMPFileHandler; diff --git a/XMPFiles/source/XMPFiles_Impl.cpp b/XMPFiles/source/XMPFiles_Impl.cpp index 471c48d..68fa99f 100644 --- a/XMPFiles/source/XMPFiles_Impl.cpp +++ b/XMPFiles/source/XMPFiles_Impl.cpp @@ -502,3 +502,18 @@ XMP_OptionBits XMPFileHandler::GetSerializeOptions() } // XMPFileHandler::GetSerializeOptions // ================================================================================================= +// XMPFileHandler::NotifyClient +// =================================== +// +// Generic function for all the file handlers to replace existing exception throws +// +void XMPFileHandler::NotifyClient(GenericErrorCallback * errCBptr, XMP_ErrorSeverity severity, XMP_Error & error) +{ + if (errCBptr) + errCBptr->NotifyClient( severity, error ); + else { + if ( severity != kXMPErrSev_Recoverable ) + throw error; + } +} +// ================================================================================================= diff --git a/XMPFiles/source/XMPFiles_Impl.hpp b/XMPFiles/source/XMPFiles_Impl.hpp index 6e21c23..72d82e7 100644 --- a/XMPFiles/source/XMPFiles_Impl.hpp +++ b/XMPFiles/source/XMPFiles_Impl.hpp @@ -302,6 +302,8 @@ public: virtual void UpdateFile ( bool doSafeUpdate ) = 0; virtual void WriteTempFile ( XMP_IO* tempRef ) = 0; + static void NotifyClient(GenericErrorCallback * errCBptr, XMP_ErrorSeverity severity, XMP_Error & error); + // ! Leave the data members public so common code can see them. XMPFiles * parent; // Let's the handler see the file info. |