summaryrefslogtreecommitdiff
path: root/source/XMPFiles/FileHandlers/XDCAM_Handler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/XMPFiles/FileHandlers/XDCAM_Handler.cpp')
-rw-r--r--source/XMPFiles/FileHandlers/XDCAM_Handler.cpp726
1 files changed, 726 insertions, 0 deletions
diff --git a/source/XMPFiles/FileHandlers/XDCAM_Handler.cpp b/source/XMPFiles/FileHandlers/XDCAM_Handler.cpp
new file mode 100644
index 0000000..6ccfcab
--- /dev/null
+++ b/source/XMPFiles/FileHandlers/XDCAM_Handler.cpp
@@ -0,0 +1,726 @@
+// =================================================================================================
+// ADOBE SYSTEMS INCORPORATED
+// Copyright 2002-2008 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 "XDCAM_Handler.hpp"
+#include "XDCAM_Support.hpp"
+#include "MD5.h"
+
+using namespace std;
+
+// =================================================================================================
+/// \file XDCAM_Handler.cpp
+/// \brief Folder format handler for XDCAM.
+///
+/// This handler is for the XDCAM video format. This is a pseudo-package, visible files but with a very
+/// well-defined layout and naming rules. There are 2 different layouts for XDCAM, called FAM and SAM.
+/// The FAM layout is used by "normal" XDCAM devices. The SAM layout is used by XDCAM-EX devices.
+///
+/// A typical FAM layout looks like:
+///
+/// .../MyMovie/
+/// INDEX.XML
+/// DISCMETA.XML
+/// MEDIAPRO.XML
+/// GENERAL/
+/// unknown files
+/// CLIP/
+/// C0001.MXF
+/// C0001M01.XML
+/// C0001M01.XMP
+/// C0002.MXF
+/// C0002M01.XML
+/// C0002M01.XMP
+/// SUB/
+/// C0001S01.MXF
+/// C0002S01.MXF
+/// EDIT/
+/// E0001E01.SMI
+/// E0001M01.XML
+/// E0002E01.SMI
+/// E0002M01.XML
+///
+/// A typical SAM layout looks like:
+///
+/// .../MyMovie/
+/// GENERAL/
+/// unknown files
+/// PROAV/
+/// INDEX.XML
+/// INDEX.BUP
+/// DISCMETA.XML
+/// DISCINFO.XML
+/// DISCINFO.BUP
+/// CLPR/
+/// C0001/
+/// C0001C01.SMI
+/// C0001V01.MXF
+/// C0001A01.MXF
+/// C0001A02.MXF
+/// C0001R01.BIM
+/// C0001I01.PPN
+/// C0001M01.XML
+/// C0001M01.XMP
+/// C0001S01.MXF
+/// C0002/
+/// ...
+/// EDTR/
+/// E0001/
+/// E0001E01.SMI
+/// E0001M01.XML
+/// E0002/
+/// ...
+///
+/// Note that the Sony documentation uses the folder names "General", "Clip", "Sub", and "Edit". We
+/// use all caps here. Common code has already shifted the names, we want to be case insensitive.
+///
+/// From the user's point of view, .../MyMovie contains XDCAM stuff, in this case 2 clips whose raw
+/// names are C0001 and C0002. There may be mapping information for nicer clip names to the raw
+/// names, but that can be ignored for now. Each clip is stored as a collection of files, each file
+/// holding some specific aspect of the clip's data.
+///
+/// The XDCAM handler operates on clips. The path from the client of XMPFiles can be either a logical
+/// clip path, like ".../MyMovie/C0001", or a full path to one of the files. In the latter case the
+/// handler must figure out the intended clip, it must not blindly use the named file.
+///
+/// Once the XDCAM structure and intended clip are identified, the handler only deals with the .XMP
+/// and .XML files in the CLIP or CLPR/<clip> folders. The .XMP file, if present, contains the XMP
+/// for the clip. The .XML file must be present to define the existance of the clip. It contains a
+/// variety of information about the clip, including some legacy metadata.
+///
+// =================================================================================================
+
+// =================================================================================================
+// XDCAM_CheckFormat
+// =================
+//
+// This version does fairly simple checks. The top level folder (.../MyMovie) must have exactly 1
+// child, a folder called CONTENTS. This must have a subfolder called CLIP. It may also have
+// subfolders called VIDEO, AUDIO, ICON, VOICE, and PROXY. Any mixture of these additional folders
+// is allowed, but no other children are allowed in CONTENTS. The CLIP folder must contain a .XML
+// file for the desired clip. The name checks are case insensitive.
+//
+// The state of the string parameters depends on the form of the path passed by the client. If the
+// client passed a logical clip path, like ".../MyMovie/C0001", the parameters are:
+// rootPath - ".../MyMovie"
+// gpName - empty
+// parentName - empty
+// leafName - "C0001"
+//
+// If the client passed a FAM file path, like ".../MyMovie/EDIT/E0001E01.SMI", they are:
+// rootPath - "..."
+// gpName - "MyMovie"
+// parentName - "EDIT"
+// leafName - "E0001E01"
+//
+// If the client passed a SAM file path, like ".../MyMovie/PROAV/CLPR/C0001/C0001A02.MXF", they are:
+// rootPath - ".../MyMovie/PROAV"
+// gpName - "CLPR"
+// parentName - "C0001"
+// leafName - "C0001A02"
+//
+// For both FAM and SAM the leading character of the leafName for an existing file might be coerced
+// to 'C' to form the logical clip name. And suffix such as "M01" must be removed for FAM. We don't
+// need to worry about that for SAM, that uses the <clip> folder name.
+
+// ! The FAM format supports general clip file names through an ALIAS.XML mapping file. The simple
+// ! existence check has an edge case bug, left to be fixed later. If the ALIAS.XML file exists, but
+// ! some of the clips still have "raw" names, and we're passed an existing file path in the EDIT
+// ! folder, we will fail to do the leading 'E' to 'C' coercion. We might also erroneously remove a
+// ! suffix from a mapped essence file with a name like ClipX01.MXF.
+
+// ! The common code has shifted the gpName, parentName, and leafName strings to upper case. It has
+// ! also made sure that for a logical clip path the rootPath is an existing folder, and that the
+// ! file exists for a full file path.
+
+bool XDCAM_CheckFormat ( XMP_FileFormat format,
+ const std::string & _rootPath,
+ const std::string & _gpName,
+ const std::string & parentName,
+ const std::string & leafName,
+ XMPFiles * parent )
+{
+ std::string rootPath = _rootPath; // ! Need tweaking in the existing file cases (FAM and SAM).
+ std::string gpName = _gpName;
+
+ bool isFAM = false;
+
+ std::string tempPath, childName;
+ XMP_FolderInfo folderInfo;
+
+ std::string clipName = leafName;
+
+ // Do some basic checks on the root path and component names. Decide if this is FAM or SAM.
+
+ if ( gpName.empty() != parentName.empty() ) return false; // Must be both empty or both non-empty.
+
+ if ( gpName.empty() ) {
+
+ // This is the logical clip path case. Just look for PROAV to see if this is FAM or SAM.
+ if ( GetChildMode ( rootPath, "PROAV" ) != kFMode_IsFolder ) isFAM = true;
+
+ } else {
+
+ // This is the existing file case. See if this is FAM or SAM, tweak the clip name as needed.
+
+ if ( (parentName == "CLIP") || (parentName == "EDIT") || (parentName == "SUB") ) {
+ isFAM = true;
+ } else if ( (gpName != "CLPR") && (gpName != "EDTR") ) {
+ return false;
+ }
+
+ if ( isFAM ) {
+
+ // Put the proper root path together. Clean up the clip name if needed.
+
+ if ( ! rootPath.empty() ) rootPath += kDirChar;
+ rootPath += gpName;
+ gpName.erase();
+
+ if ( GetChildMode ( rootPath, "ALIAS.XML" ) != kFMode_IsFile ) {
+ clipName[0] = 'C'; // ! See notes above about pending bug.
+ }
+
+ if ( clipName.size() > 3 ) {
+ size_t clipMid = clipName.size() - 3;
+ char c1 = clipName[clipMid];
+ char c2 = clipName[clipMid+1];
+ char c3 = clipName[clipMid+2];
+ if ( ('A' <= c1) && (c1 <= 'Z') &&
+ ('0' <= c2) && (c2 <= '9') && ('0' <= c3) && (c3 <= '9') ) {
+ clipName.erase ( clipMid );
+ }
+ }
+
+ } else {
+
+ // Fix the clip name. Check for and strip the "PROAV" suffix on the root path.
+
+ clipName = parentName; // ! We have a folder with the (almost) exact clip name.
+ clipName[0] = 'C';
+
+ std::string proav;
+ SplitLeafName ( &rootPath, &proav );
+ MakeUpperCase ( &proav );
+ if ( (rootPath.empty()) || (proav != "PROAV") ) return false;
+
+ }
+
+ }
+
+ // Make sure the general XDCAM package structure is legit. Set tempPath as a bogus path of the
+ // form <root>/<FAM-or-SAM>/<clip>, e.g. ".../MyMovie/FAM/C0001". This is passed the handler via
+ // the handlerTemp hackery.
+
+ if ( isFAM ) {
+
+ if ( (format != kXMP_XDCAM_FAMFile) && (format != kXMP_UnknownFile) ) return false;
+
+ tempPath = rootPath;
+
+ if ( GetChildMode ( tempPath, "INDEX.XML" ) != kFMode_IsFile ) return false;
+ if ( GetChildMode ( tempPath, "DISCMETA.XML" ) != kFMode_IsFile ) return false;
+ if ( GetChildMode ( tempPath, "MEDIAPRO.XML" ) != kFMode_IsFile ) return false;
+
+ tempPath += kDirChar;
+ tempPath += "CLIP";
+ tempPath += kDirChar;
+ tempPath += clipName;
+ tempPath += "M01.XML";
+ if ( GetFileMode ( tempPath.c_str() ) != kFMode_IsFile ) return false;
+
+ tempPath = rootPath;
+ tempPath += kDirChar;
+ tempPath += "FAM";
+ tempPath += kDirChar;
+ tempPath += clipName;
+
+ } else {
+
+ if ( (format != kXMP_XDCAM_SAMFile) && (format != kXMP_UnknownFile) ) return false;
+
+ // We already know about the PROAV folder, just check below it.
+
+ tempPath = rootPath;
+ tempPath += kDirChar;
+ tempPath += "PROAV";
+
+ if ( GetChildMode ( tempPath, "INDEX.XML" ) != kFMode_IsFile ) return false;
+ if ( GetChildMode ( tempPath, "DISCMETA.XML" ) != kFMode_IsFile ) return false;
+ if ( GetChildMode ( tempPath, "DISCINFO.XML" ) != kFMode_IsFile ) return false;
+ if ( GetChildMode ( tempPath, "CLPR" ) != kFMode_IsFolder ) return false;
+
+ tempPath += kDirChar;
+ tempPath += "CLPR";
+ tempPath += kDirChar;
+ tempPath += clipName;
+ if ( GetFileMode ( tempPath.c_str() ) != kFMode_IsFolder ) return false;
+
+ tempPath += kDirChar;
+ tempPath += clipName;
+ tempPath += "M01.XML";
+ if ( GetFileMode ( tempPath.c_str() ) != kFMode_IsFile ) return false;
+
+ tempPath = rootPath;
+ tempPath += kDirChar;
+ tempPath += "SAM";
+ tempPath += kDirChar;
+ tempPath += clipName;
+
+ }
+
+ // Save the pseudo-path for the handler object. A bit of a hack, but the only way to get info
+ // from here to there.
+
+ size_t pathLen = tempPath.size() + 1; // Include a terminating nul.
+ parent->handlerTemp = malloc ( pathLen );
+ if ( parent->handlerTemp == 0 ) XMP_Throw ( "No memory for XDCAM clip info", kXMPErr_NoMemory );
+ memcpy ( parent->handlerTemp, tempPath.c_str(), pathLen ); // AUDIT: Safe, allocated above.
+
+ return true;
+
+} // XDCAM_CheckFormat
+
+// =================================================================================================
+// XDCAM_MetaHandlerCTor
+// =====================
+
+XMPFileHandler * XDCAM_MetaHandlerCTor ( XMPFiles * parent )
+{
+ return new XDCAM_MetaHandler ( parent );
+
+} // XDCAM_MetaHandlerCTor
+
+// =================================================================================================
+// XDCAM_MetaHandler::XDCAM_MetaHandler
+// ====================================
+
+XDCAM_MetaHandler::XDCAM_MetaHandler ( XMPFiles * _parent ) : isFAM(false), expat(0)
+{
+
+ this->parent = _parent; // Inherited, can't set in the prefix.
+ this->handlerFlags = kXDCAM_HandlerFlags;
+ this->stdCharForm = kXMP_Char8Bit;
+
+ // Extract the root path, clip name, and FAM/SAM flag from handlerTemp.
+
+ XMP_Assert ( this->parent->handlerTemp != 0 );
+
+ this->rootPath.assign ( (char*) this->parent->handlerTemp );
+ free ( this->parent->handlerTemp );
+ this->parent->handlerTemp = 0;
+
+ SplitLeafName ( &this->rootPath, &this->clipName );
+
+ std::string temp;
+ SplitLeafName ( &this->rootPath, &temp );
+ XMP_Assert ( (temp == "FAM") || (temp == "SAM") );
+ if ( temp == "FAM" ) this->isFAM = true;
+ XMP_Assert ( this->isFAM ? (this->parent->format == kXMP_XDCAM_FAMFile) : (this->parent->format == kXMP_XDCAM_SAMFile) );
+
+} // XDCAM_MetaHandler::XDCAM_MetaHandler
+
+// =================================================================================================
+// XDCAM_MetaHandler::~XDCAM_MetaHandler
+// =====================================
+
+XDCAM_MetaHandler::~XDCAM_MetaHandler()
+{
+
+ this->CleanupLegacyXML();
+ if ( this->parent->handlerTemp != 0 ) {
+ free ( this->parent->handlerTemp );
+ this->parent->handlerTemp = 0;
+ }
+
+} // XDCAM_MetaHandler::~XDCAM_MetaHandler
+
+// =================================================================================================
+// XDCAM_MetaHandler::MakeClipFilePath
+// ===================================
+
+void XDCAM_MetaHandler::MakeClipFilePath ( std::string * path, XMP_StringPtr suffix )
+{
+
+ *path = this->rootPath;
+ *path += kDirChar;
+
+ if ( this->isFAM ) {
+ *path += "CLIP";
+ } else {
+ *path += "PROAV";
+ *path += kDirChar;
+ *path += "CLPR";
+ *path += kDirChar;
+ *path += this->clipName;
+ }
+
+ *path += kDirChar;
+ *path += this->clipName;
+ *path += suffix;
+
+} // XDCAM_MetaHandler::MakeClipFilePath
+
+// =================================================================================================
+// XDCAM_MetaHandler::MakeLegacyDigest
+// ===================================
+
+// *** Early hack version.
+
+#define kHexDigits "0123456789ABCDEF"
+
+void XDCAM_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 xdcNS = this->xdcNS.c_str();
+ XML_NodePtr legacyContext, legacyProp;
+
+ legacyContext = this->clipMetadata->GetNamedElement ( xdcNS, "Access" );
+ if ( legacyContext == 0 ) return;
+
+ MD5_CTX context;
+ unsigned char digestBin [16];
+ MD5Init ( &context );
+
+ legacyProp = legacyContext->GetNamedElement ( xdcNS, "Creator" );
+ if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() && (! legacyProp->content.empty()) ) {
+ const XML_Node * xmlValue = legacyProp->content[0];
+ MD5Update ( &context, (XMP_Uns8*)xmlValue->value.c_str(), (unsigned int)xmlValue->value.size() );
+ }
+
+ legacyProp = legacyContext->GetNamedElement ( xdcNS, "CreationDate" );
+ if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() && (! legacyProp->content.empty()) ) {
+ const XML_Node * xmlValue = legacyProp->content[0];
+ MD5Update ( &context, (XMP_Uns8*)xmlValue->value.c_str(), (unsigned int)xmlValue->value.size() );
+ }
+
+ legacyProp = legacyContext->GetNamedElement ( xdcNS, "LastUpdateDate" );
+ if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() && (! legacyProp->content.empty()) ) {
+ const XML_Node * xmlValue = legacyProp->content[0];
+ MD5Update ( &context, (XMP_Uns8*)xmlValue->value.c_str(), (unsigned int)xmlValue->value.size() );
+ }
+
+ MD5Final ( digestBin, &context );
+
+ 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 );
+
+} // XDCAM_MetaHandler::MakeLegacyDigest
+
+// =================================================================================================
+// P2_MetaHandler::CleanupLegacyXML
+// ================================
+
+void XDCAM_MetaHandler::CleanupLegacyXML()
+{
+
+ if ( ! this->defaultNS.empty() ) {
+ SXMPMeta::DeleteNamespace ( this->defaultNS.c_str() );
+ this->defaultNS.erase();
+ }
+
+ if ( this->expat != 0 ) { delete ( this->expat ); this->expat = 0; }
+
+ clipMetadata = 0; // ! Was a pointer into the expat tree.
+
+} // XDCAM_MetaHandler::CleanupLegacyXML
+
+// =================================================================================================
+// XDCAM_MetaHandler::CacheFileData
+// ================================
+
+void XDCAM_MetaHandler::CacheFileData()
+{
+ XMP_Assert ( (! this->containsXMP) && (! this->containsTNail) );
+
+ // See if the clip's .XMP file exists.
+
+ std::string xmpPath;
+ this->MakeClipFilePath ( &xmpPath, "M01.XMP" );
+ if ( GetFileMode ( xmpPath.c_str() ) != kFMode_IsFile ) return; // No XMP.
+
+ // Read the entire .XMP file.
+
+ char openMode = 'r';
+ if ( this->parent->openFlags & kXMPFiles_OpenForUpdate ) openMode = 'w';
+
+ LFA_FileRef xmpFile = LFA_Open ( xmpPath.c_str(), openMode );
+ if ( xmpFile == 0 ) return; // The open failed.
+
+ XMP_Int64 xmpLen = LFA_Measure ( xmpFile );
+ if ( xmpLen > 100*1024*1024 ) {
+ XMP_Throw ( "XDCAM XMP is outrageously large", kXMPErr_InternalFailure ); // Sanity check.
+ }
+
+ this->xmpPacket.erase();
+ this->xmpPacket.reserve ( (size_t)xmpLen );
+ this->xmpPacket.append ( (size_t)xmpLen, ' ' );
+
+ XMP_Int32 ioCount = LFA_Read ( xmpFile, (void*)this->xmpPacket.data(), (XMP_Int32)xmpLen, kLFA_RequireAll );
+ XMP_Assert ( ioCount == xmpLen );
+
+ this->packetInfo.offset = 0;
+ this->packetInfo.length = (XMP_Int32)xmpLen;
+ FillPacketInfo ( this->xmpPacket, &this->packetInfo );
+
+ XMP_Assert ( this->parent->fileRef == 0 );
+ if ( openMode == 'r' ) {
+ LFA_Close ( xmpFile );
+ } else {
+ this->parent->fileRef = xmpFile;
+ }
+
+ this->containsXMP = true;
+
+} // XDCAM_MetaHandler::CacheFileData
+
+// =================================================================================================
+// XDCAM_MetaHandler::ProcessXMP
+// =============================
+
+void XDCAM_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() );
+ }
+
+ // NonRealTimeMeta -> XMP by schema
+ std::string xmlPath, umid;
+ this->MakeClipFilePath ( &xmlPath, "M01.XML" );
+
+ // --------------------------------------------------------------
+ // *** This is a minimal Q&D example of legacy metadata handling.
+ // *** Hack: Special case trickery to detect and clean up default XML namespace usage.
+
+ bool haveDefaultNS = SXMPMeta::GetNamespaceURI ( "_dflt_", 0 ); // Is there already a default namespace?
+
+ AutoFile xmlFile;
+ xmlFile.fileRef = LFA_Open ( xmlPath.c_str(), 'r' );
+ if ( xmlFile.fileRef == 0 ) return; // The open failed.
+
+ this->expat = XMP_NewExpatAdapter();
+ if ( this->expat == 0 ) XMP_Throw ( "XDCAM_MetaHandler: Can't create Expat adapter", kXMPErr_NoMemory );
+
+ XMP_Uns8 buffer [64*1024];
+ while ( true ) {
+ XMP_Int32 ioCount = LFA_Read ( xmlFile.fileRef, 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.
+
+ LFA_Close ( xmlFile.fileRef );
+ xmlFile.fileRef = 0;
+
+ if ( ! haveDefaultNS ) {
+ // No prior default XML namespace. If there is one now, remember it and delete it when done.
+ haveDefaultNS = SXMPMeta::GetNamespaceURI ( "_dflt_", &this->defaultNS );
+ XMP_Assert ( haveDefaultNS == (! this->defaultNS.empty()) );
+ }
+
+ // The root element should be NonRealTimeMeta in some namespace. 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, "NonRealTimeMeta" ) ) CleanupAndExit
+
+ this->legacyNS = rootElem->ns;
+
+ // Check the legacy digest.
+
+ XMP_StringPtr legacyNS = this->legacyNS.c_str();
+
+ this->clipMetadata = rootElem; // ! Save the NonRealTimeMeta pointer for other use.
+
+ std::string oldDigest, newDigest;
+ bool digestFound = this->xmpObj.GetStructField ( kXMP_NS_XMP, "NativeDigests", kXMP_NS_XMP, "XDCAM", &oldDigest, 0 );
+ if ( digestFound ) {
+ this->MakeLegacyDigest ( &newDigest );
+ if ( oldDigest == newDigest ) CleanupAndExit
+ }
+
+ // 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->containsXMP = XDCAM_Support::GetLegacyMetaData ( &this->xmpObj, rootElem, legacyNS, digestFound, umid );
+
+ CleanupAndExit
+ #undef CleanupAndExit
+
+} // XDCAM_MetaHandler::ProcessXMP
+
+// =================================================================================================
+// XDCAM_MetaHandler::UpdateFile
+// =============================
+//
+// Note that UpdateFile is only called from XMPFiles::CloseFile, so it is OK to close the file here.
+
+void XDCAM_MetaHandler::UpdateFile ( bool doSafeUpdate )
+{
+ if ( ! this->needsUpdate ) return;
+ this->needsUpdate = false; // Make sure only called once.
+
+ LFA_FileRef oldFile = 0;
+ std::string filePath, tempPath;
+
+ // Update the internal legacy XML tree if we have one, and set the digest in the XMP.
+
+ bool updateLegacyXML = false;
+
+ if ( this->clipMetadata != 0 ) {
+ updateLegacyXML = XDCAM_Support::SetLegacyMetaData ( this->clipMetadata, &this->xmpObj, this->legacyNS.c_str());
+ }
+
+ std::string newDigest;
+ this->MakeLegacyDigest ( &newDigest );
+ this->xmpObj.SetStructField ( kXMP_NS_XMP, "NativeDigests", kXMP_NS_XMP, "XDCAM", newDigest.c_str(), kXMP_DeleteExisting );
+ this->xmpObj.SerializeToBuffer ( &this->xmpPacket, this->GetSerializeOptions() );
+
+ // Update the legacy XML file if necessary.
+
+ if ( updateLegacyXML ) {
+
+ std::string legacyXML;
+ this->expat->tree.Serialize ( &legacyXML );
+
+ this->MakeClipFilePath ( &filePath, "M01.XML" );
+ oldFile = LFA_Open ( filePath.c_str(), 'w' );
+
+ if ( oldFile == 0 ) {
+
+ // The XML does not exist yet.
+
+ this->MakeClipFilePath ( &filePath, "M01.XML" );
+ oldFile = LFA_Create ( filePath.c_str() );
+ if ( oldFile == 0 ) XMP_Throw ( "Failure creating XDCAMEX legacy XML file", kXMPErr_ExternalFailure );
+ LFA_Write ( oldFile, legacyXML.data(), (XMP_StringLen)legacyXML.size() );
+ LFA_Close ( oldFile );
+
+ } else if ( ! doSafeUpdate ) {
+
+ // Over write the existing XML file.
+
+ LFA_Seek ( oldFile, 0, SEEK_SET );
+ LFA_Truncate ( oldFile, 0 );
+ LFA_Write ( oldFile, legacyXML.data(), (XMP_StringLen)legacyXML.size() );
+ LFA_Close ( oldFile );
+
+ } else {
+
+ // Do a safe update.
+
+ // *** We really need an LFA_SwapFiles utility.
+
+ this->MakeClipFilePath ( &filePath, "M01.XML" );
+
+ CreateTempFile ( filePath, &tempPath );
+ LFA_FileRef tempFile = LFA_Open ( tempPath.c_str(), 'w' );
+ LFA_Write ( tempFile, legacyXML.data(), (XMP_StringLen)legacyXML.size() );
+ LFA_Close ( tempFile );
+
+ LFA_Close ( oldFile );
+ LFA_Delete ( filePath.c_str() );
+ LFA_Rename ( tempPath.c_str(), filePath.c_str() );
+
+ }
+
+ }
+
+ oldFile = this->parent->fileRef;
+
+ if ( oldFile == 0 ) {
+
+ // The XMP does not exist yet.
+
+ std::string xmpPath;
+ this->MakeClipFilePath ( &xmpPath, "M01.XMP" );
+
+ LFA_FileRef xmpFile = LFA_Create ( xmpPath.c_str() );
+ if ( xmpFile == 0 ) XMP_Throw ( "Failure creating XDCAM XMP file", kXMPErr_ExternalFailure );
+ LFA_Write ( xmpFile, this->xmpPacket.data(), (XMP_StringLen)this->xmpPacket.size() );
+ LFA_Close ( xmpFile );
+
+ } else if ( ! doSafeUpdate ) {
+
+ // Over write the existing XMP file.
+
+ LFA_Seek ( oldFile, 0, SEEK_SET );
+ LFA_Truncate ( oldFile, 0 );
+ LFA_Write ( oldFile, this->xmpPacket.data(), (XMP_StringLen)this->xmpPacket.size() );
+ LFA_Close ( oldFile );
+
+ } else {
+
+ // Do a safe update.
+
+ // *** We really need an LFA_SwapFiles utility.
+
+ std::string xmpPath, tempPath;
+
+ this->MakeClipFilePath ( &xmpPath, "M01.XMP" );
+
+ CreateTempFile ( xmpPath, &tempPath );
+ LFA_FileRef tempFile = LFA_Open ( tempPath.c_str(), 'w' );
+ LFA_Write ( tempFile, this->xmpPacket.data(), (XMP_StringLen)this->xmpPacket.size() );
+ LFA_Close ( tempFile );
+
+ LFA_Close ( oldFile );
+ LFA_Delete ( xmpPath.c_str() );
+ LFA_Rename ( tempPath.c_str(), xmpPath.c_str() );
+
+ }
+
+ this->parent->fileRef = 0;
+
+} // XDCAM_MetaHandler::UpdateFile
+
+// =================================================================================================
+// XDCAM_MetaHandler::WriteFile
+// ============================
+
+void XDCAM_MetaHandler::WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath )
+{
+
+ // ! WriteFile is not supposed to be called for handlers that own the file.
+ XMP_Throw ( "XDCAM_MetaHandler::WriteFile should not be called", kXMPErr_InternalFailure );
+
+} // XDCAM_MetaHandler::WriteFile
+
+// =================================================================================================