1 // =================================================================================================
2 // ADOBE SYSTEMS INCORPORATED
3 // Copyright 2002-2008 Adobe Systems Incorporated
4 // All Rights Reserved
5 //
6 // NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
7 // of the Adobe license agreement accompanying it.
8 // =================================================================================================
9 
10 #include <cstdlib>
11 
12 #include "XDCAM_Handler.hpp"
13 #include "XDCAM_Support.hpp"
14 #include "MD5.h"
15 
16 using namespace std;
17 
18 // =================================================================================================
19 /// \file XDCAM_Handler.cpp
20 /// \brief Folder format handler for XDCAM.
21 ///
22 /// This handler is for the XDCAM video format. This is a pseudo-package, visible files but with a very
23 /// well-defined layout and naming rules. There are 2 different layouts for XDCAM, called FAM and SAM.
24 /// The FAM layout is used by "normal" XDCAM devices. The SAM layout is used by XDCAM-EX devices.
25 ///
26 /// A typical FAM layout looks like:
27 ///
28 /// .../MyMovie/
29 /// 	INDEX.XML
30 /// 	DISCMETA.XML
31 /// 	MEDIAPRO.XML
32 /// 	GENERAL/
33 /// 		unknown files
34 /// 	CLIP/
35 /// 		C0001.MXF
36 /// 		C0001M01.XML
37 /// 		C0001M01.XMP
38 /// 		C0002.MXF
39 /// 		C0002M01.XML
40 /// 		C0002M01.XMP
41 /// 	SUB/
42 /// 		C0001S01.MXF
43 /// 		C0002S01.MXF
44 /// 	EDIT/
45 /// 		E0001E01.SMI
46 /// 		E0001M01.XML
47 /// 		E0002E01.SMI
48 /// 		E0002M01.XML
49 ///
50 /// A typical SAM layout looks like:
51 ///
52 /// .../MyMovie/
53 /// 	GENERAL/
54 /// 		unknown files
55 /// 	PROAV/
56 /// 		INDEX.XML
57 /// 		INDEX.BUP
58 /// 		DISCMETA.XML
59 /// 		DISCINFO.XML
60 /// 		DISCINFO.BUP
61 /// 		CLPR/
62 /// 			C0001/
63 /// 				C0001C01.SMI
64 /// 				C0001V01.MXF
65 /// 				C0001A01.MXF
66 /// 				C0001A02.MXF
67 /// 				C0001R01.BIM
68 /// 				C0001I01.PPN
69 /// 				C0001M01.XML
70 /// 				C0001M01.XMP
71 /// 				C0001S01.MXF
72 /// 			C0002/
73 /// 				...
74 /// 		EDTR/
75 /// 			E0001/
76 /// 				E0001E01.SMI
77 /// 				E0001M01.XML
78 /// 			E0002/
79 /// 				...
80 ///
81 /// Note that the Sony documentation uses the folder names "General", "Clip", "Sub", and "Edit". We
82 /// use all caps here. Common code has already shifted the names, we want to be case insensitive.
83 ///
84 /// From the user's point of view, .../MyMovie contains XDCAM stuff, in this case 2 clips whose raw
85 /// names are C0001 and C0002. There may be mapping information for nicer clip names to the raw
86 /// names, but that can be ignored for now. Each clip is stored as a collection of files, each file
87 /// holding some specific aspect of the clip's data.
88 ///
89 /// The XDCAM handler operates on clips. The path from the client of XMPFiles can be either a logical
90 /// clip path, like ".../MyMovie/C0001", or a full path to one of the files. In the latter case the
91 /// handler must figure out the intended clip, it must not blindly use the named file.
92 ///
93 /// Once the XDCAM structure and intended clip are identified, the handler only deals with the .XMP
94 /// and .XML files in the CLIP or CLPR/<clip> folders. The .XMP file, if present, contains the XMP
95 /// for the clip. The .XML file must be present to define the existance of the clip. It contains a
96 /// variety of information about the clip, including some legacy metadata.
97 ///
98 // =================================================================================================
99 
100 // =================================================================================================
101 // XDCAM_CheckFormat
102 // =================
103 //
104 // This version does fairly simple checks. The top level folder (.../MyMovie) must have exactly 1
105 // child, a folder called CONTENTS. This must have a subfolder called CLIP. It may also have
106 // subfolders called VIDEO, AUDIO, ICON, VOICE, and PROXY. Any mixture of these additional folders
107 // is allowed, but no other children are allowed in CONTENTS. The CLIP folder must contain a .XML
108 // file for the desired clip. The name checks are case insensitive.
109 //
110 // The state of the string parameters depends on the form of the path passed by the client. If the
111 // client passed a logical clip path, like ".../MyMovie/C0001", the parameters are:
112 //   rootPath   - ".../MyMovie"
113 //   gpName     - empty
114 //   parentName - empty
115 //   leafName   - "C0001"
116 //
117 // If the client passed a FAM file path, like ".../MyMovie/EDIT/E0001E01.SMI", they are:
118 //   rootPath   - "..."
119 //   gpName     - "MyMovie"
120 //   parentName - "EDIT"
121 //   leafName   - "E0001E01"
122 //
123 // If the client passed a SAM file path, like ".../MyMovie/PROAV/CLPR/C0001/C0001A02.MXF", they are:
124 //   rootPath   - ".../MyMovie/PROAV"
125 //   gpName     - "CLPR"
126 //   parentName - "C0001"
127 //   leafName   - "C0001A02"
128 //
129 // For both FAM and SAM the leading character of the leafName for an existing file might be coerced
130 // to 'C' to form the logical clip name. And suffix such as "M01" must be removed for FAM. We don't
131 // need to worry about that for SAM, that uses the <clip> folder name.
132 
133 // ! The FAM format supports general clip file names through an ALIAS.XML mapping file. The simple
134 // ! existence check has an edge case bug, left to be fixed later. If the ALIAS.XML file exists, but
135 // ! some of the clips still have "raw" names, and we're passed an existing file path in the EDIT
136 // ! folder, we will fail to do the leading 'E' to 'C' coercion. We might also erroneously remove a
137 // ! suffix from a mapped essence file with a name like ClipX01.MXF.
138 
139 // ! The common code has shifted the gpName, parentName, and leafName strings to upper case. It has
140 // ! also made sure that for a logical clip path the rootPath is an existing folder, and that the
141 // ! file exists for a full file path.
142 
XDCAM_CheckFormat(XMP_FileFormat format,const std::string & _rootPath,const std::string & _gpName,const std::string & parentName,const std::string & leafName,XMPFiles * parent)143 bool XDCAM_CheckFormat ( XMP_FileFormat format,
144 						 const std::string & _rootPath,
145 						 const std::string & _gpName,
146 						 const std::string & parentName,
147 						 const std::string & leafName,
148 						 XMPFiles * parent )
149 {
150 	std::string rootPath = _rootPath;	// ! Need tweaking in the existing file cases (FAM and SAM).
151 	std::string gpName   = _gpName;
152 
153 	bool isFAM = false;
154 
155 	std::string tempPath, childName;
156 	XMP_FolderInfo folderInfo;
157 
158 	std::string clipName = leafName;
159 
160 	// Do some basic checks on the root path and component names. Decide if this is FAM or SAM.
161 
162 	if ( gpName.empty() != parentName.empty() ) return false;	// Must be both empty or both non-empty.
163 
164 	if ( gpName.empty() ) {
165 
166 		// This is the logical clip path case. Just look for PROAV to see if this is FAM or SAM.
167 		if ( GetChildMode ( rootPath, "PROAV" ) != kFMode_IsFolder ) isFAM = true;
168 
169 	} else {
170 
171 		// This is the existing file case. See if this is FAM or SAM, tweak the clip name as needed.
172 
173 		if ( (parentName == "CLIP") || (parentName == "EDIT") || (parentName == "SUB") ) {
174 			isFAM = true;
175 		} else if ( (gpName != "CLPR") && (gpName != "EDTR") ) {
176 			return false;
177 		}
178 
179 		if ( isFAM ) {
180 
181 			// Put the proper root path together. Clean up the clip name if needed.
182 
183 			if ( ! rootPath.empty() ) rootPath += kDirChar;
184 			rootPath += gpName;
185 			gpName.erase();
186 
187 			if ( GetChildMode ( rootPath, "ALIAS.XML" ) != kFMode_IsFile ) {
188 				clipName[0] = 'C';	// ! See notes above about pending bug.
189 			}
190 
191 			if ( clipName.size() > 3 ) {
192 				size_t clipMid = clipName.size() - 3;
193 				char c1 = clipName[clipMid];
194 				char c2 = clipName[clipMid+1];
195 				char c3 = clipName[clipMid+2];
196 				if ( ('A' <= c1) && (c1 <= 'Z') &&
197 					 ('0' <= c2) && (c2 <= '9') && ('0' <= c3) && (c3 <= '9') ) {
198 					clipName.erase ( clipMid );
199 				}
200 			}
201 
202 		} else {
203 
204 			// Fix the clip name. Check for and strip the "PROAV" suffix on the root path.
205 
206 			clipName = parentName;	// ! We have a folder with the (almost) exact clip name.
207 			clipName[0] = 'C';
208 
209 			std::string proav;
210 			SplitLeafName ( &rootPath, &proav );
211 			MakeUpperCase ( &proav );
212 			if ( (rootPath.empty()) || (proav != "PROAV") ) return false;
213 
214 		}
215 
216 	}
217 
218 	// Make sure the general XDCAM package structure is legit. Set tempPath as a bogus path of the
219 	// form <root>/<FAM-or-SAM>/<clip>, e.g. ".../MyMovie/FAM/C0001". This is passed the handler via
220 	// the handlerTemp hackery.
221 
222 	if ( isFAM ) {
223 
224 		if ( (format != kXMP_XDCAM_FAMFile) && (format != kXMP_UnknownFile) ) return false;
225 
226 		tempPath = rootPath;
227 
228 		if ( GetChildMode ( tempPath, "INDEX.XML" ) != kFMode_IsFile ) return false;
229 		if ( GetChildMode ( tempPath, "DISCMETA.XML" ) != kFMode_IsFile ) return false;
230 		if ( GetChildMode ( tempPath, "MEDIAPRO.XML" ) != kFMode_IsFile ) return false;
231 
232 		tempPath += kDirChar;
233 		tempPath += "CLIP";
234 		tempPath += kDirChar;
235 		tempPath += clipName;
236 		tempPath += "M01.XML";
237 		if ( GetFileMode ( tempPath.c_str() ) != kFMode_IsFile ) return false;
238 
239 		tempPath = rootPath;
240 		tempPath += kDirChar;
241 		tempPath += "FAM";
242 		tempPath += kDirChar;
243 		tempPath += clipName;
244 
245 	} else {
246 
247 		if ( (format != kXMP_XDCAM_SAMFile) && (format != kXMP_UnknownFile) ) return false;
248 
249 		// We already know about the PROAV folder, just check below it.
250 
251 		tempPath = rootPath;
252 		tempPath += kDirChar;
253 		tempPath += "PROAV";
254 
255 		if ( GetChildMode ( tempPath, "INDEX.XML" ) != kFMode_IsFile ) return false;
256 		if ( GetChildMode ( tempPath, "DISCMETA.XML" ) != kFMode_IsFile ) return false;
257 		if ( GetChildMode ( tempPath, "DISCINFO.XML" ) != kFMode_IsFile ) return false;
258 		if ( GetChildMode ( tempPath, "CLPR" ) != kFMode_IsFolder ) return false;
259 
260 		tempPath += kDirChar;
261 		tempPath += "CLPR";
262 		tempPath += kDirChar;
263 		tempPath += clipName;
264 		if ( GetFileMode ( tempPath.c_str() ) != kFMode_IsFolder ) return false;
265 
266 		tempPath += kDirChar;
267 		tempPath += clipName;
268 		tempPath += "M01.XML";
269 		if ( GetFileMode ( tempPath.c_str() ) != kFMode_IsFile ) return false;
270 
271 		tempPath = rootPath;
272 		tempPath += kDirChar;
273 		tempPath += "SAM";
274 		tempPath += kDirChar;
275 		tempPath += clipName;
276 
277 	}
278 
279 	// Save the pseudo-path for the handler object. A bit of a hack, but the only way to get info
280 	// from here to there.
281 
282 	size_t pathLen = tempPath.size() + 1;	// Include a terminating nul.
283 	parent->handlerTemp = malloc ( pathLen );
284 	if ( parent->handlerTemp == 0 ) XMP_Throw ( "No memory for XDCAM clip info", kXMPErr_NoMemory );
285 	memcpy ( parent->handlerTemp, tempPath.c_str(), pathLen );	// AUDIT: Safe, allocated above.
286 
287 	return true;
288 
289 }	// XDCAM_CheckFormat
290 
291 // =================================================================================================
292 // XDCAM_MetaHandlerCTor
293 // =====================
294 
XDCAM_MetaHandlerCTor(XMPFiles * parent)295 XMPFileHandler * XDCAM_MetaHandlerCTor ( XMPFiles * parent )
296 {
297 	return new XDCAM_MetaHandler ( parent );
298 
299 }	// XDCAM_MetaHandlerCTor
300 
301 // =================================================================================================
302 // XDCAM_MetaHandler::XDCAM_MetaHandler
303 // ====================================
304 
XDCAM_MetaHandler(XMPFiles * _parent)305 XDCAM_MetaHandler::XDCAM_MetaHandler ( XMPFiles * _parent ) : isFAM(false), expat(0)
306 {
307 
308 	this->parent = _parent;	// Inherited, can't set in the prefix.
309 	this->handlerFlags = kXDCAM_HandlerFlags;
310 	this->stdCharForm  = kXMP_Char8Bit;
311 
312 	// Extract the root path, clip name, and FAM/SAM flag from handlerTemp.
313 
314 	XMP_Assert ( this->parent->handlerTemp != 0 );
315 
316 	this->rootPath.assign ( (char*) this->parent->handlerTemp );
317 	free ( this->parent->handlerTemp );
318 	this->parent->handlerTemp = 0;
319 
320 	SplitLeafName ( &this->rootPath, &this->clipName );
321 
322 	std::string temp;
323 	SplitLeafName ( &this->rootPath, &temp );
324 	XMP_Assert ( (temp == "FAM") || (temp == "SAM") );
325 	if ( temp == "FAM" ) this->isFAM = true;
326 	XMP_Assert ( this->isFAM ? (this->parent->format == kXMP_XDCAM_FAMFile) : (this->parent->format == kXMP_XDCAM_SAMFile) );
327 
328 }	// XDCAM_MetaHandler::XDCAM_MetaHandler
329 
330 // =================================================================================================
331 // XDCAM_MetaHandler::~XDCAM_MetaHandler
332 // =====================================
333 
~XDCAM_MetaHandler()334 XDCAM_MetaHandler::~XDCAM_MetaHandler()
335 {
336 
337 	this->CleanupLegacyXML();
338 	if ( this->parent->handlerTemp != 0 ) {
339 		free ( this->parent->handlerTemp );
340 		this->parent->handlerTemp = 0;
341 	}
342 
343 }	// XDCAM_MetaHandler::~XDCAM_MetaHandler
344 
345 // =================================================================================================
346 // XDCAM_MetaHandler::MakeClipFilePath
347 // ===================================
348 
MakeClipFilePath(std::string * path,XMP_StringPtr suffix)349 void XDCAM_MetaHandler::MakeClipFilePath ( std::string * path, XMP_StringPtr suffix )
350 {
351 
352 	*path = this->rootPath;
353 	*path += kDirChar;
354 
355 	if ( this->isFAM ) {
356 		*path += "CLIP";
357 	} else {
358 		*path += "PROAV";
359 		*path += kDirChar;
360 		*path += "CLPR";
361 		*path += kDirChar;
362 		*path += this->clipName;
363 	}
364 
365 	*path += kDirChar;
366 	*path += this->clipName;
367 	*path += suffix;
368 
369 }	// XDCAM_MetaHandler::MakeClipFilePath
370 
371 // =================================================================================================
372 // XDCAM_MetaHandler::MakeLegacyDigest
373 // ===================================
374 
375 // *** Early hack version.
376 
377 #define kHexDigits "0123456789ABCDEF"
378 
MakeLegacyDigest(std::string * digestStr)379 void XDCAM_MetaHandler::MakeLegacyDigest ( std::string * digestStr )
380 {
381 	digestStr->erase();
382 	if ( this->clipMetadata == 0 ) return;	// Bail if we don't have any legacy XML.
383 	XMP_Assert ( this->expat != 0 );
384 
385 	XMP_StringPtr xdcNS = this->xdcNS.c_str();
386 	XML_NodePtr legacyContext, legacyProp;
387 
388 	legacyContext = this->clipMetadata->GetNamedElement ( xdcNS, "Access" );
389 	if ( legacyContext == 0 ) return;
390 
391 	MD5_CTX    context;
392 	unsigned char digestBin [16];
393 	MD5Init ( &context );
394 
395 	legacyProp = legacyContext->GetNamedElement ( xdcNS, "Creator" );
396 	if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() && (! legacyProp->content.empty()) ) {
397 		const XML_Node * xmlValue = legacyProp->content[0];
398 		MD5Update ( &context, (XMP_Uns8*)xmlValue->value.c_str(), (unsigned int)xmlValue->value.size() );
399 	}
400 
401 	legacyProp = legacyContext->GetNamedElement ( xdcNS, "CreationDate" );
402 	if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() && (! legacyProp->content.empty()) ) {
403 		const XML_Node * xmlValue = legacyProp->content[0];
404 		MD5Update ( &context, (XMP_Uns8*)xmlValue->value.c_str(), (unsigned int)xmlValue->value.size() );
405 	}
406 
407 	legacyProp = legacyContext->GetNamedElement ( xdcNS, "LastUpdateDate" );
408 	if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() && (! legacyProp->content.empty()) ) {
409 		const XML_Node * xmlValue = legacyProp->content[0];
410 		MD5Update ( &context, (XMP_Uns8*)xmlValue->value.c_str(), (unsigned int)xmlValue->value.size() );
411 	}
412 
413 	MD5Final ( digestBin, &context );
414 
415 	char buffer [40];
416 	for ( int in = 0, out = 0; in < 16; in += 1, out += 2 ) {
417 		XMP_Uns8 byte = digestBin[in];
418 		buffer[out]   = kHexDigits [ byte >> 4 ];
419 		buffer[out+1] = kHexDigits [ byte & 0xF ];
420 	}
421 	buffer[32] = 0;
422 	digestStr->append ( buffer );
423 
424 }	// XDCAM_MetaHandler::MakeLegacyDigest
425 
426 // =================================================================================================
427 // P2_MetaHandler::CleanupLegacyXML
428 // ================================
429 
CleanupLegacyXML()430 void XDCAM_MetaHandler::CleanupLegacyXML()
431 {
432 
433 	if ( ! this->defaultNS.empty() ) {
434 		SXMPMeta::DeleteNamespace ( this->defaultNS.c_str() );
435 		this->defaultNS.erase();
436 	}
437 
438 	if ( this->expat != 0 ) { delete ( this->expat ); this->expat = 0; }
439 
440 	clipMetadata = 0;	// ! Was a pointer into the expat tree.
441 
442 }	// XDCAM_MetaHandler::CleanupLegacyXML
443 
444 // =================================================================================================
445 // XDCAM_MetaHandler::CacheFileData
446 // ================================
447 
CacheFileData()448 void XDCAM_MetaHandler::CacheFileData()
449 {
450 	XMP_Assert ( (! this->containsXMP) && (! this->containsTNail) );
451 
452 	// See if the clip's .XMP file exists.
453 
454 	std::string xmpPath;
455 	this->MakeClipFilePath ( &xmpPath, "M01.XMP" );
456 	if ( GetFileMode ( xmpPath.c_str() ) != kFMode_IsFile ) return;	// No XMP.
457 
458 	// Read the entire .XMP file.
459 
460 	char openMode = 'r';
461 	if ( this->parent->openFlags & kXMPFiles_OpenForUpdate ) openMode = 'w';
462 
463 	LFA_FileRef xmpFile = LFA_Open ( xmpPath.c_str(), openMode );
464 	if ( xmpFile == 0 ) return;	// The open failed.
465 
466 	XMP_Int64 xmpLen = LFA_Measure ( xmpFile );
467 	if ( xmpLen > 100*1024*1024 ) {
468 		XMP_Throw ( "XDCAM XMP is outrageously large", kXMPErr_InternalFailure );	// Sanity check.
469 	}
470 
471 	this->xmpPacket.erase();
472 	this->xmpPacket.reserve ( (size_t)xmpLen );
473 	this->xmpPacket.append ( (size_t)xmpLen, ' ' );
474 
475 	XMP_Int32 ioCount = LFA_Read ( xmpFile, (void*)this->xmpPacket.data(), (XMP_Int32)xmpLen, kLFA_RequireAll );
476 	XMP_Assert ( ioCount == xmpLen );
477 
478 	this->packetInfo.offset = 0;
479 	this->packetInfo.length = (XMP_Int32)xmpLen;
480 	FillPacketInfo ( this->xmpPacket, &this->packetInfo );
481 
482 	XMP_Assert ( this->parent->fileRef == 0 );
483 	if ( openMode == 'r' ) {
484 		LFA_Close ( xmpFile );
485 	} else {
486 		this->parent->fileRef = xmpFile;
487 	}
488 
489 	this->containsXMP = true;
490 
491 }	// XDCAM_MetaHandler::CacheFileData
492 
493 // =================================================================================================
494 // XDCAM_MetaHandler::ProcessXMP
495 // =============================
496 
ProcessXMP()497 void XDCAM_MetaHandler::ProcessXMP()
498 {
499 
500 	// Some versions of gcc can't tolerate goto's across declarations.
501 	// *** Better yet, avoid this cruft with self-cleaning objects.
502 	#define CleanupAndExit	\
503 		{																								\
504 			bool openForUpdate = XMP_OptionIsSet ( this->parent->openFlags, kXMPFiles_OpenForUpdate );	\
505 			if ( ! openForUpdate ) this->CleanupLegacyXML();											\
506 			return;																						\
507 		}
508 
509 	if ( this->processedXMP ) return;
510 	this->processedXMP = true;	// Make sure only called once.
511 
512 	if ( this->containsXMP ) {
513 		this->xmpObj.ParseFromBuffer ( this->xmpPacket.c_str(), (XMP_StringLen)this->xmpPacket.size() );
514 	}
515 
516 	// NonRealTimeMeta -> XMP by schema
517 	std::string xmlPath, umid;
518 	this->MakeClipFilePath ( &xmlPath, "M01.XML" );
519 
520 	// --------------------------------------------------------------
521 	// *** This is a minimal Q&D example of legacy metadata handling.
522 	// *** Hack: Special case trickery to detect and clean up default XML namespace usage.
523 
524 	bool haveDefaultNS = SXMPMeta::GetNamespaceURI ( "_dflt_", 0 );	// Is there already a default namespace?
525 
526 	AutoFile xmlFile;
527 	xmlFile.fileRef = LFA_Open ( xmlPath.c_str(), 'r' );
528 	if ( xmlFile.fileRef == 0 ) return;	// The open failed.
529 
530 	this->expat = XMP_NewExpatAdapter();
531 	if ( this->expat == 0 ) XMP_Throw ( "XDCAM_MetaHandler: Can't create Expat adapter", kXMPErr_NoMemory );
532 
533 	XMP_Uns8 buffer [64*1024];
534 	while ( true ) {
535 		XMP_Int32 ioCount = LFA_Read ( xmlFile.fileRef, buffer, sizeof(buffer) );
536 		if ( ioCount == 0 ) break;
537 		this->expat->ParseBuffer ( buffer, ioCount, false /* not the end */ );
538 	}
539 	this->expat->ParseBuffer ( 0, 0, true );	// End the parse.
540 
541 	LFA_Close ( xmlFile.fileRef );
542 	xmlFile.fileRef = 0;
543 
544 	if ( ! haveDefaultNS ) {
545 		// No prior default XML namespace. If there is one now, remember it and delete it when done.
546 		haveDefaultNS = SXMPMeta::GetNamespaceURI ( "_dflt_", &this->defaultNS );
547 		XMP_Assert ( haveDefaultNS == (! this->defaultNS.empty()) );
548 	}
549 
550 	// The root element should be NonRealTimeMeta in some namespace. Take whatever this file uses.
551 
552 	XML_Node & xmlTree = this->expat->tree;
553 	XML_NodePtr rootElem = 0;
554 
555 	for ( size_t i = 0, limit = xmlTree.content.size(); i < limit; ++i ) {
556 		if ( xmlTree.content[i]->kind == kElemNode ) {
557 			rootElem = xmlTree.content[i];
558 		}
559 	}
560 
561 	if ( rootElem == 0 ) CleanupAndExit
562 	XMP_StringPtr rootLocalName = rootElem->name.c_str() + rootElem->nsPrefixLen;
563 	if ( ! XMP_LitMatch ( rootLocalName, "NonRealTimeMeta" ) ) CleanupAndExit
564 
565 	this->legacyNS = rootElem->ns;
566 
567 	// Check the legacy digest.
568 
569 	XMP_StringPtr legacyNS = this->legacyNS.c_str();
570 
571 	this->clipMetadata = rootElem;	// ! Save the NonRealTimeMeta pointer for other use.
572 
573 	std::string oldDigest, newDigest;
574 	bool digestFound = this->xmpObj.GetStructField ( kXMP_NS_XMP, "NativeDigests", kXMP_NS_XMP, "XDCAM", &oldDigest, 0 );
575 	if ( digestFound ) {
576 		this->MakeLegacyDigest ( &newDigest );
577 		if ( oldDigest == newDigest ) CleanupAndExit
578 	}
579 
580 	// If we get here we need find and import the actual legacy elements using the current namespace.
581 	// Either there is no old digest in the XMP, or the digests differ. In the former case keep any
582 	// existing XMP, in the latter case take new legacy values.
583 
584 	this->containsXMP = XDCAM_Support::GetLegacyMetaData ( &this->xmpObj, rootElem, legacyNS, digestFound, umid );
585 
586 	CleanupAndExit
587 	#undef CleanupAndExit
588 
589 }	// XDCAM_MetaHandler::ProcessXMP
590 
591 // =================================================================================================
592 // XDCAM_MetaHandler::UpdateFile
593 // =============================
594 //
595 // Note that UpdateFile is only called from XMPFiles::CloseFile, so it is OK to close the file here.
596 
UpdateFile(bool doSafeUpdate)597 void XDCAM_MetaHandler::UpdateFile ( bool doSafeUpdate )
598 {
599 	if ( ! this->needsUpdate ) return;
600 	this->needsUpdate = false;	// Make sure only called once.
601 
602 	LFA_FileRef oldFile = 0;
603 	std::string filePath, tempPath;
604 
605 	// Update the internal legacy XML tree if we have one, and set the digest in the XMP.
606 
607 	bool updateLegacyXML = false;
608 
609 	if ( this->clipMetadata != 0 ) {
610 		updateLegacyXML = XDCAM_Support::SetLegacyMetaData ( this->clipMetadata, &this->xmpObj, this->legacyNS.c_str());
611 	}
612 
613 	std::string newDigest;
614 	this->MakeLegacyDigest ( &newDigest );
615 	this->xmpObj.SetStructField ( kXMP_NS_XMP, "NativeDigests", kXMP_NS_XMP, "XDCAM", newDigest.c_str(), kXMP_DeleteExisting );
616 	this->xmpObj.SerializeToBuffer ( &this->xmpPacket, this->GetSerializeOptions() );
617 
618 	// Update the legacy XML file if necessary.
619 
620 	if ( updateLegacyXML ) {
621 
622 		std::string legacyXML;
623 		this->expat->tree.Serialize ( &legacyXML );
624 
625 		this->MakeClipFilePath ( &filePath, "M01.XML" );
626 		oldFile = LFA_Open ( filePath.c_str(), 'w' );
627 
628 		if ( oldFile == 0 ) {
629 
630 			// The XML does not exist yet.
631 
632 			this->MakeClipFilePath ( &filePath, "M01.XML" );
633 			oldFile = LFA_Create ( filePath.c_str() );
634 			if ( oldFile == 0 ) XMP_Throw ( "Failure creating XDCAMEX legacy XML file", kXMPErr_ExternalFailure );
635 			LFA_Write ( oldFile, legacyXML.data(), (XMP_StringLen)legacyXML.size() );
636 			LFA_Close ( oldFile );
637 
638 		} else if ( ! doSafeUpdate ) {
639 
640 			// Over write the existing XML file.
641 
642 			LFA_Seek ( oldFile, 0, SEEK_SET );
643 			LFA_Truncate ( oldFile, 0 );
644 			LFA_Write ( oldFile, legacyXML.data(), (XMP_StringLen)legacyXML.size() );
645 			LFA_Close ( oldFile );
646 
647 		} else {
648 
649 			// Do a safe update.
650 
651 			// *** We really need an LFA_SwapFiles utility.
652 
653 			this->MakeClipFilePath ( &filePath, "M01.XML" );
654 
655 			CreateTempFile ( filePath, &tempPath );
656 			LFA_FileRef tempFile = LFA_Open ( tempPath.c_str(), 'w' );
657 			LFA_Write ( tempFile, legacyXML.data(), (XMP_StringLen)legacyXML.size() );
658 			LFA_Close ( tempFile );
659 
660 			LFA_Close ( oldFile );
661 			LFA_Delete ( filePath.c_str() );
662 			LFA_Rename ( tempPath.c_str(), filePath.c_str() );
663 
664 		}
665 
666 	}
667 
668 	oldFile = this->parent->fileRef;
669 
670 	if ( oldFile == 0 ) {
671 
672 		// The XMP does not exist yet.
673 
674 		std::string xmpPath;
675 		this->MakeClipFilePath ( &xmpPath, "M01.XMP" );
676 
677 		LFA_FileRef xmpFile = LFA_Create ( xmpPath.c_str() );
678 		if ( xmpFile == 0 ) XMP_Throw ( "Failure creating XDCAM XMP file", kXMPErr_ExternalFailure );
679 		LFA_Write ( xmpFile, this->xmpPacket.data(), (XMP_StringLen)this->xmpPacket.size() );
680 		LFA_Close ( xmpFile );
681 
682 	} else if ( ! doSafeUpdate ) {
683 
684 		// Over write the existing XMP file.
685 
686 		LFA_Seek ( oldFile, 0, SEEK_SET );
687 		LFA_Truncate ( oldFile, 0 );
688 		LFA_Write ( oldFile, this->xmpPacket.data(), (XMP_StringLen)this->xmpPacket.size() );
689 		LFA_Close ( oldFile );
690 
691 	} else {
692 
693 		// Do a safe update.
694 
695 		// *** We really need an LFA_SwapFiles utility.
696 
697 		std::string xmpPath, tempPath;
698 
699 		this->MakeClipFilePath ( &xmpPath, "M01.XMP" );
700 
701 		CreateTempFile ( xmpPath, &tempPath );
702 		LFA_FileRef tempFile = LFA_Open ( tempPath.c_str(), 'w' );
703 		LFA_Write ( tempFile, this->xmpPacket.data(), (XMP_StringLen)this->xmpPacket.size() );
704 		LFA_Close ( tempFile );
705 
706 		LFA_Close ( oldFile );
707 		LFA_Delete ( xmpPath.c_str() );
708 		LFA_Rename ( tempPath.c_str(), xmpPath.c_str() );
709 
710 	}
711 
712 	this->parent->fileRef = 0;
713 
714 }	// XDCAM_MetaHandler::UpdateFile
715 
716 // =================================================================================================
717 // XDCAM_MetaHandler::WriteFile
718 // ============================
719 
WriteFile(LFA_FileRef sourceRef,const std::string & sourcePath)720 void XDCAM_MetaHandler::WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath )
721 {
722 
723 	// ! WriteFile is not supposed to be called for handlers that own the file.
724 	XMP_Throw ( "XDCAM_MetaHandler::WriteFile should not be called", kXMPErr_InternalFailure );
725 
726 }	// XDCAM_MetaHandler::WriteFile
727 
728 // =================================================================================================
729