1 // =================================================================================================
2 // ADOBE SYSTEMS INCORPORATED
3 // Copyright 2007 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 "public/include/XMP_Environment.h"	// ! XMP_Environment.h must be the first included header.
11 
12 #include "public/include/XMP_Const.h"
13 #include "public/include/XMP_IO.hpp"
14 
15 #include "XMPFiles/source/XMPFiles_Impl.hpp"
16 #include "source/XMPFiles_IO.hpp"
17 #include "source/XIO.hpp"
18 #include "source/IOUtils.hpp"
19 
20 #include "XMPFiles/source/FileHandlers/XDCAM_Handler.hpp"
21 #include "XMPFiles/source/FormatSupport/XDCAM_Support.hpp"
22 #include "XMPFiles/source/FormatSupport/PackageFormat_Support.hpp"
23 #include "third-party/zuid/interfaces/MD5.h"
24 
25 using namespace std;
26 
27 // =================================================================================================
28 /// \file XDCAM_Handler.cpp
29 /// \brief Folder format handler for XDCAM.
30 ///
31 /// This handler is for the XDCAM video format. This is a pseudo-package, visible files but with a very
32 /// well-defined layout and naming rules. There are 2 different layouts for XDCAM, called FAM and SAM.
33 /// The FAM layout is used by "normal" XDCAM devices. The SAM layout is used by XDCAM-EX devices.
34 ///
35 /// A typical FAM layout looks like (note mixed case for the nested folders):
36 ///
37 /// .../MyMovie/
38 /// 	INDEX.XML
39 /// 	DISCMETA.XML
40 /// 	MEDIAPRO.XML
41 /// 	General/
42 /// 		unknown files
43 /// 	Clip/
44 /// 		C0001.MXF
45 /// 		C0001M01.XML
46 /// 		C0001M01.XMP
47 /// 		C0002.MXF
48 /// 		C0002M01.XML
49 /// 		C0002M01.XMP
50 /// 	Sub/
51 /// 		C0001S01.MXF
52 /// 		C0002S01.MXF
53 /// 	Edit/
54 /// 		E0001E01.SMI
55 /// 		E0001M01.XML
56 /// 		E0002E01.SMI
57 /// 		E0002M01.XML
58 ///
59 /// A typical FAM XMPilot layout looks like (note mixed case for the nested folders):
60 ///
61 /// .../MyMovie/
62 /// 	DISCMETA.XML
63 /// 	MEDIAPRO.XML
64 /// 	General/
65 /// 	Clip/
66 /// 		Office_0001.MXF
67 /// 		Office_0001M01.XML
68 /// 		Office_0001M01.XMP
69 /// 		Office_0002.MXF
70 /// 		Office_0002M01.XML
71 /// 		Office_0002M01.XMP
72 /// 	Sub/
73 /// 		Office_0001S01.MXF
74 /// 		Office_0002S01.MXF
75 /// 	Edit/
76 ///     UserData/
77 ///         unknown files
78 ///
79 /// A typical FAM XDCAM Memory SxS layout looks like (note mixed case for the nested folders):
80 ///
81 /// .../MyMovie/
82 /// 	DISCMETA.XML
83 /// 	MEDIAPRO.XML
84 ///     CUEUP.XML
85 /// 	General/
86 /// 	Clip/
87 /// 		C0001.MXF
88 /// 		C0001M01.XML
89 /// 		C0001M01.XMP
90 /// 		C0001R01.BIM
91 /// 		C0002.MXF
92 /// 		C0002M01.XML
93 /// 		C0002M01.XMP
94 /// 		C0001R01.BIM
95 /// 	Sub/
96 /// 		C0001S01.MXF
97 /// 		C0002S01.MXF
98 /// 	Edit/
99 ///		Take/
100 /// 		T0001.SMI
101 /// 		T0001M01.XML
102 ///     UserData/
103 ///
104 /// A typical SAM layout looks like:
105 ///
106 /// .../MyMovie/
107 /// 	GENERAL/
108 /// 		unknown files
109 /// 	PROAV/
110 /// 		INDEX.XML
111 /// 		INDEX.BUP
112 /// 		DISCMETA.XML
113 /// 		DISCINFO.XML
114 /// 		DISCINFO.BUP
115 /// 		CLPR/
116 /// 			C0001/
117 /// 				C0001C01.SMI
118 /// 				C0001V01.MXF
119 /// 				C0001A01.MXF
120 /// 				C0001A02.MXF
121 /// 				C0001R01.BIM
122 /// 				C0001I01.PPN
123 /// 				C0001M01.XML
124 /// 				C0001M01.XMP
125 /// 				C0001S01.MXF
126 /// 			C0002/
127 /// 				...
128 /// 		EDTR/
129 /// 			E0001/
130 /// 				E0001E01.SMI
131 /// 				E0001M01.XML
132 /// 			E0002/
133 /// 				...
134 ///
135 /// Note that the Sony documentation uses the folder names "General", "Clip", "Sub", and "Edit". We
136 /// use all caps here. Common code has already shifted the names, we want to be case insensitive.
137 ///
138 /// From the user's point of view, .../MyMovie contains XDCAM stuff, in this case 2 clips whose raw
139 /// names are C0001 and C0002. There may be mapping information for nicer clip names to the raw
140 /// names, but that can be ignored for now. Each clip is stored as a collection of files, each file
141 /// holding some specific aspect of the clip's data.
142 ///
143 /// The XDCAM handler operates on clips. The path from the client of XMPFiles can be either a logical
144 /// clip path, like ".../MyMovie/C0001", or a full path to one of the files. In the latter case the
145 /// handler must figure out the intended clip, it must not blindly use the named file.
146 ///
147 /// Once the XDCAM structure and intended clip are identified, the handler only deals with the .XMP
148 /// and .XML files in the CLIP or CLPR/<clip> folders. The .XMP file, if present, contains the XMP
149 /// for the clip. The .XML file must be present to define the existance of the clip. It contains a
150 /// variety of information about the clip, including some legacy metadata.
151 ///
152 // =================================================================================================
153 
154 // =================================================================================================
155 // XDCAM_CheckFormat
156 // =================
157 //
158 // This version does fairly simple checks. The top level folder (.../MyMovie) must have exactly 1
159 // child, a folder called CONTENTS. This must have a subfolder called CLIP. It may also have
160 // subfolders called VIDEO, AUDIO, ICON, VOICE, and PROXY. Any mixture of these additional folders
161 // is allowed, but no other children are allowed in CONTENTS. The CLIP folder must contain a .XML
162 // file for the desired clip. The name checks are case insensitive.
163 //
164 // The state of the string parameters depends on the form of the path passed by the client. If the
165 // client passed a logical clip path, like ".../MyMovie/C0001", the parameters are:
166 //   rootPath   - ".../MyMovie"
167 //   gpName     - empty
168 //   parentName - empty
169 //   leafName   - "C0001"
170 //
171 // If the client passed a FAM file path, like ".../MyMovie/Edit/E0001E01.SMI", they are:
172 //   rootPath   - "..."
173 //   gpName     - "MyMovie"
174 //   parentName - "EDIT"	(common code has shifted the case)
175 //   leafName   - "E0001E01"
176 //
177 // If the client passed a SAM file path, like ".../MyMovie/PROAV/CLPR/C0001/C0001A02.MXF", they are:
178 //   rootPath   - ".../MyMovie/PROAV"
179 //   gpName     - "CLPR"
180 //   parentName - "C0001"
181 //   leafName   - "C0001A02"
182 //
183 // For both FAM and SAM the leading character of the leafName for an existing file might be coerced
184 // to 'C' to form the logical clip name. And suffix such as "M01" must be removed for FAM. We don't
185 // need to worry about that for SAM, that uses the <clip> folder name.
186 
187 // ! The FAM format supports general clip file names through an ALIAS.XML mapping file. The simple
188 // ! existence check has an edge case bug, left to be fixed later. If the ALIAS.XML file exists, but
189 // ! some of the clips still have "raw" names, and we're passed an existing file path in the EDIT
190 // ! folder, we will fail to do the leading 'E' to 'C' coercion. We might also erroneously remove a
191 // ! suffix from a mapped essence file with a name like ClipX01.MXF.
192 
193 // ! The common code has shifted the gpName, parentName, and leafName strings to uppercase. It has
194 // ! also made sure that for a logical clip path the rootPath is an existing folder, and that the
195 // ! file exists for a full file path.
196 
197 // =================================================================================================
198 // XDCAM_MetaHandler::XDCAM_MetaHandler
199 // ====================================
XDCAM_MetaHandler(XMPFiles * _parent)200 XDCAM_MetaHandler::XDCAM_MetaHandler ( XMPFiles * _parent ) : expat(0), clipMetadata(NULL)
201 {
202 
203 	this->parent = _parent;	// Inherited, can't set in the prefix.
204 	this->stdCharForm  = kXMP_Char8Bit;
205 
206 }	// XDCAM_MetaHandler::XDCAM_MetaHandler
207 
208 // =================================================================================================
209 // XDCAM_MetaHandler::~XDCAM_MetaHandler
210 // =====================================
211 
~XDCAM_MetaHandler()212 XDCAM_MetaHandler::~XDCAM_MetaHandler()
213 {
214 
215 	this->CleanupLegacyXML();
216 	if ( this->parent->tempPtr != 0 ) {
217 		free ( this->parent->tempPtr );
218 		this->parent->tempPtr = 0;
219 	}
220 
221 }	// XDCAM_MetaHandler::~XDCAM_MetaHandler
222 
223 // =================================================================================================
224 // XDCAM_MetaHandler::MakeMediaproPath
225 // ===================================
226 
MakeMediaproPath(std::string * path,bool checkFile)227 bool XDCAM_MetaHandler::MakeMediaproPath ( std::string * path, bool checkFile /* = false */ )
228 {
229 
230 	*path = this->rootPath;
231 	*path += kDirChar;
232 	*path += "MEDIAPRO.XML";
233 
234 	if ( ! checkFile ) return true;
235 	return Host_IO::Exists ( path->c_str() );
236 
237 }	// XDCAM_MetaHandler::MakeMediaproPath
238 
239 // =================================================================================================
240 // XDCAM_MetaHandler::MakeLegacyDigest
241 // ===================================
242 
243 // *** Early hack version.
244 
245 #define kHexDigits "0123456789ABCDEF"
246 
247 // =================================================================================================
248 // XDCAM_MetaHandler::MakeLegacyDigest
249 // ================================
250 
MakeLegacyDigest(std::string * digestStr)251 void XDCAM_MetaHandler::MakeLegacyDigest ( std::string * digestStr )
252 {
253 	digestStr->erase();
254 	if ( this->clipMetadata == 0 ) return;	// Bail if we don't have any legacy XML.
255 	XMP_Assert ( this->expat != 0 );
256 
257 	XMP_StringPtr xdcNS_ = this->xdcNS.c_str();
258 	XML_NodePtr legacyContext, legacyProp;
259 
260 	legacyContext = this->clipMetadata->GetNamedElement ( xdcNS_, "Access" );
261 	if ( legacyContext == 0 ) return;
262 
263 	MD5_CTX    context;
264 	unsigned char digestBin [16];
265 	MD5Init ( &context );
266 
267 	legacyProp = legacyContext->GetNamedElement ( xdcNS_, "Creator" );
268 	if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() && (! legacyProp->content.empty()) ) {
269 		const XML_Node * xmlValue = legacyProp->content[0];
270 		MD5Update ( &context, (XMP_Uns8*)xmlValue->value.c_str(), (unsigned int)xmlValue->value.size() );
271 	}
272 
273 	legacyProp = legacyContext->GetNamedElement ( xdcNS_, "CreationDate" );
274 	if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() && (! legacyProp->content.empty()) ) {
275 		const XML_Node * xmlValue = legacyProp->content[0];
276 		MD5Update ( &context, (XMP_Uns8*)xmlValue->value.c_str(), (unsigned int)xmlValue->value.size() );
277 	}
278 
279 	legacyProp = legacyContext->GetNamedElement ( xdcNS_, "LastUpdateDate" );
280 	if ( (legacyProp != 0) && legacyProp->IsLeafContentNode() && (! legacyProp->content.empty()) ) {
281 		const XML_Node * xmlValue = legacyProp->content[0];
282 		MD5Update ( &context, (XMP_Uns8*)xmlValue->value.c_str(), (unsigned int)xmlValue->value.size() );
283 	}
284 
285 	MD5Final ( digestBin, &context );
286 
287 	char buffer [40];
288 	for ( int in = 0, out = 0; in < 16; in += 1, out += 2 ) {
289 		XMP_Uns8 byte = digestBin[in];
290 		buffer[out]   = kHexDigits [ byte >> 4 ];
291 		buffer[out+1] = kHexDigits [ byte & 0xF ];
292 	}
293 	buffer[32] = 0;
294 	digestStr->append ( buffer );
295 
296 }	// XDCAM_MetaHandler::MakeLegacyDigest
297 
298 // =================================================================================================
299 // XDCAM_MetaHandler::CleanupLegacyXML
300 // ================================
301 
CleanupLegacyXML()302 void XDCAM_MetaHandler::CleanupLegacyXML()
303 {
304 
305 	if ( this->expat != 0 ) { delete ( this->expat ); this->expat = 0; }
306 
307 	clipMetadata = 0;	// ! Was a pointer into the expat tree.
308 
309 }	// XDCAM_MetaHandler::CleanupLegacyXML
310 
311 // =================================================================================================
312 // XDCAM_MetaHandler::readXMLFile
313 // ================================
314 
readXMLFile(XMP_StringPtr filePath,ExpatAdapter * & expat_)315 void  XDCAM_MetaHandler::readXMLFile( XMP_StringPtr filePath, ExpatAdapter* &expat_ )
316 {
317 	Host_IO::FileRef hostRef = Host_IO::Open ( filePath, Host_IO::openReadOnly );
318 	if ( hostRef == Host_IO::noFileRef ) return;	// The open failed.
319 	XMPFiles_IO xmlFile ( hostRef, filePath, Host_IO::openReadOnly );
320 
321 	expat_ = XMP_NewExpatAdapter ( ExpatAdapter::kUseLocalNamespaces );
322 	if ( expat_ == 0 ) XMP_Throw ( "XDCAM_MetaHandler: Can't create Expat adapter", kXMPErr_NoMemory );
323 
324 	XMP_Uns8 buffer [64*1024];
325 	while ( true ) {
326 		XMP_Int32 ioCount = xmlFile.Read ( buffer, sizeof(buffer) );
327 		if ( ioCount == 0 ) break;
328 		expat_->ParseBuffer ( buffer, ioCount, false /* not the end */ );
329 	}
330 	expat_->ParseBuffer ( 0, 0, true );	// End the parse.
331 
332 	xmlFile.Close();
333 }
334 
335 // =================================================================================================
336 // XDCAM_MetaHandler::GetFileModDate
337 // =================================
338 
operator <(const XMP_DateTime & left,const XMP_DateTime & right)339 static inline bool operator< ( const XMP_DateTime & left, const XMP_DateTime & right ) {
340 	int compare = SXMPUtils::CompareDateTime ( left, right );
341 	return (compare < 0);
342 }
343 
344 // =================================================================================================
345 // XDCAM_MetaHandler::GetFileModDate
346 // ================================
347 
GetFileModDate(XMP_DateTime * modDate)348 bool XDCAM_MetaHandler::GetFileModDate ( XMP_DateTime * modDate )
349 {
350 
351 	// Modify date is found in the increasing priority order
352 	//
353 	// MEDIAPRO.XML
354 	// Non-Real time metadata file
355 	// XMP file
356 
357 	bool ok, haveDate = false;
358 	std::string fullPath;
359 	XMP_DateTime oneDate, junkDate;
360 	if ( modDate == 0 ) modDate = &junkDate;
361 
362 	// MEDIAPRO.XML
363 	std::string mediaproPath;
364 	ok = MakeMediaproPath ( &mediaproPath, true /* checkFile */ );
365 	if ( ok ) ok = Host_IO::GetModifyDate ( mediaproPath.c_str(), &oneDate );
366 	if ( ok ) {
367 		if ( (! haveDate) ) *modDate = oneDate;
368 		haveDate = true;
369 	}
370 
371 	// Non-Real time metadata file
372 	ok = Host_IO::Exists( this->mNRTFilePath.c_str() );
373 	//ok = this->MakeClipFilePath ( &fullPath, "M01.XML", true /* checkFile */ );
374 	if ( ok ) ok = Host_IO::GetModifyDate ( this->mNRTFilePath.c_str(), &oneDate );
375 	if ( ok ) {
376 		if ( (! haveDate) || (*modDate < oneDate) ) *modDate = oneDate;
377 		haveDate = true;
378 	}
379 
380 	// XMP file
381 	ok = Host_IO::Exists( this->sidecarPath.c_str() );
382 	//ok = this->MakeClipFilePath ( &fullPath, "M01.XMP", true /* checkFile */ );
383 	if ( ok ) ok = Host_IO::GetModifyDate ( this->sidecarPath.c_str(), &oneDate );
384 	if ( ok ) {
385 		if ( (! haveDate) || (*modDate < oneDate) ) *modDate = oneDate;
386 		haveDate = true;
387 	}
388 
389 	return haveDate;
390 
391 }	// XDCAM_MetaHandler::GetFileModDate
392 
393 // =================================================================================================
394 // XDCAM_MetaHandler::RefersClipUmid
395 // ==================================
RefersClipUmid(std::string clipUmid,XMP_StringPtr editInfoPath)396 bool XDCAM_MetaHandler::RefersClipUmid ( std::string clipUmid , XMP_StringPtr editInfoPath )
397 {
398 	ExpatAdapter* editInfoExpat = 0 ;
399 	XMP_StringPtr nameSpace = 0 ;
400 	try {
401 		readXMLFile( editInfoPath, editInfoExpat );
402 		if ( editInfoExpat != 0 )
403 		{
404 			XML_Node & xmlTree = editInfoExpat->tree;
405 			XML_NodePtr rootElem = 0;
406 
407 			for ( size_t i = 0, limit = xmlTree.content.size(); i < limit; ++i ) {
408 				if ( xmlTree.content[i]->kind == kElemNode ) {
409 					rootElem = xmlTree.content[i];
410 				}
411 			}
412 			if ( rootElem != 0 )
413 			{
414 				XMP_StringPtr rootLocalName = rootElem->name.c_str() + rootElem->nsPrefixLen;
415 
416 				if ( XMP_LitMatch ( rootLocalName, "smil" ) )
417 				{
418 					nameSpace = rootElem->ns.c_str() ;
419 					size_t noOfBodyElements = rootElem->CountNamedElements ( nameSpace, "body" ) ;
420 					while( noOfBodyElements-- )
421 					{
422 						XML_NodePtr bodyNode = rootElem->GetNamedElement(  nameSpace, "body" );
423 						size_t noOfParElements = bodyNode->CountNamedElements ( nameSpace, "par" ) ;
424 						while( noOfParElements-- )
425 						{
426 							XML_NodePtr parNode = bodyNode->GetNamedElement(  nameSpace, "par" );
427 							size_t noOfRefElements = parNode->CountNamedElements ( nameSpace, "ref" ) ;
428 							size_t whichElem = 0;
429 							while( noOfRefElements-- )
430 							{
431 								XML_NodePtr refNode = parNode->GetNamedElement(  nameSpace, "ref" ,whichElem++ );
432 								XMP_StringPtr umidValue = refNode->GetAttrValue ( "src" );
433 								if ( umidValue != 0 &&
434 										( XMP_LitMatch( umidValue , clipUmid.c_str() ) ||
435 											( strlen(umidValue) > 15 && XMP_LitMatch( &umidValue[15] , clipUmid.c_str() ) )
436 										)
437 									)
438 									{
439 									delete ( editInfoExpat ) ;
440 									return true;
441 								}
442 							}
443 						}
444 					}
445 				}
446 			}
447 		}
448 
449 	} catch ( ... ) {
450 	}
451 	delete ( editInfoExpat ) ;
452 	return false;
453 } // XDCAM_MetaHandler::RefersClipUmid
454 
455 // =================================================================================================
456 // XDCAM_MetaHandler::IsMetadataWritable
457 // =======================================
IsMetadataWritable()458 bool XDCAM_MetaHandler::IsMetadataWritable ( )
459 {
460 	std::vector<std::string> metadataFiles;
461 	FillMetadataFiles(&metadataFiles);
462 	std::vector<std::string>::iterator itr = metadataFiles.begin();
463 	// Check whether sidecar is writable, if not then check if it can be created.
464 	bool xmpWritable = Host_IO::Writable( itr->c_str(), true );
465 	// Check for legacy metadata file.
466 	bool xmlWritable = Host_IO::Writable( (++itr)->c_str(), false );
467 	return ( xmlWritable && xmpWritable );
468 }// XDCAM_MetaHandler::IsMetadataWritable
469 
470 // =================================================================================================
471 // XDCAM_MetaHandler::FillMetadataFiles
472 // ====================================
FillMetadataFiles(std::vector<std::string> * metadataFiles)473 void XDCAM_MetaHandler::FillMetadataFiles ( std::vector<std::string> * metadataFiles )
474 {
475 	metadataFiles->push_back( this->sidecarPath );
476 	metadataFiles->push_back( this->mNRTFilePath );
477 
478 }	// XDCAM_MetaHandler::FillMetadataFiles
479 
480 // =================================================================================================
481 // XDCAM_MetaHandler::CacheFileData
482 // ================================
483 
CacheFileData()484 void XDCAM_MetaHandler::CacheFileData()
485 {
486 	XMP_Assert ( ! this->containsXMP );
487 
488 	if ( this->parent->UsesClientIO() ) {
489 		XMP_Throw ( "XDCAM cannot be used with client-managed I/O", kXMPErr_InternalFailure );
490 	}
491 
492 	// See if the clip's .XMP file exists.
493 	if ( ! Host_IO::Exists ( this->sidecarPath.c_str() ) ) return;	// No XMP.
494 
495 	// Read the entire .XMP file. We know the XMP exists, New_XMPFiles_IO is supposed to return 0
496 	// only if the file does not exist.
497 
498 	bool readOnly = XMP_OptionIsClear ( this->parent->openFlags, kXMPFiles_OpenForUpdate );
499 
500 	XMP_Assert ( this->parent->ioRef == 0 );
501 
502 	XMPFiles_IO* xmpFile =  XMPFiles_IO::New_XMPFiles_IO ( this->sidecarPath.c_str(), readOnly );
503 	if ( xmpFile == 0 ) XMP_Throw ( "XDCAM XMP file open failure", kXMPErr_InternalFailure );
504 	this->parent->ioRef = xmpFile;
505 
506 	XMP_Int64 xmpLen = xmpFile->Length();
507 	if ( xmpLen > 100*1024*1024 ) {
508 		XMP_Throw ( "XDCAM XMP is outrageously large", kXMPErr_InternalFailure );	// Sanity check.
509 	}
510 
511 	this->xmpPacket.erase();
512 	this->xmpPacket.append ( (size_t)xmpLen, ' ' );
513 
514 	/*XMP_Int32 ioCount =*/ xmpFile->ReadAll ( (void*)this->xmpPacket.data(), (XMP_Int32)xmpLen );
515 
516 	this->packetInfo.offset = 0;
517 	this->packetInfo.length = (XMP_Int32)xmpLen;
518 	FillPacketInfo ( this->xmpPacket, &this->packetInfo );
519 
520 	this->containsXMP = true;
521 
522 }	// XDCAM_MetaHandler::CacheFileData
523 
524 // =================================================================================================
525 // XDCAM_MetaHandler::GetMediaProMetadata
526 // ======================================
527 
528 // =================================================================================================
529 // XDCAM_MetaHandler::ProcessXMP
530 // =============================
531 
ProcessXMP()532 void XDCAM_MetaHandler::ProcessXMP()
533 {
534 
535 	// Some versions of gcc can't tolerate goto's across declarations.
536 	// *** Better yet, avoid this cruft with self-cleaning objects.
537 	#define CleanupAndExit	\
538 		{																								\
539 			bool openForUpdate = XMP_OptionIsSet ( this->parent->openFlags, kXMPFiles_OpenForUpdate );	\
540 			if ( ! openForUpdate ) this->CleanupLegacyXML();											\
541 			return;																						\
542 		}
543 
544 	if ( this->processedXMP ) return;
545 	this->processedXMP = true;	// Make sure only called once.
546 
547 	if ( this->containsXMP ) {
548 		this->xmpObj.ParseFromBuffer ( this->xmpPacket.c_str(), (XMP_StringLen)this->xmpPacket.size() );
549 	}
550 
551 	// NonRealTimeMeta -> XMP by schema
552 	std::string xmlPath = this->mNRTFilePath;
553 	std::string umid;
554 
555 	readXMLFile( xmlPath.c_str(), this->expat );
556 	if ( this->expat == 0 ) return;
557 
558 	// The root element should be NonRealTimeMeta in some namespace. Take whatever this file uses.
559 
560 	XML_Node & xmlTree = this->expat->tree;
561 	XML_NodePtr rootElem = 0;
562 
563 	for ( size_t i = 0, limit = xmlTree.content.size(); i < limit; ++i ) {
564 		if ( xmlTree.content[i]->kind == kElemNode ) {
565 			rootElem = xmlTree.content[i];
566 		}
567 	}
568 
569 	if ( rootElem == 0 ) CleanupAndExit
570 	XMP_StringPtr rootLocalName = rootElem->name.c_str() + rootElem->nsPrefixLen;
571 	if ( ! XMP_LitMatch ( rootLocalName, "NonRealTimeMeta" ) ) CleanupAndExit
572 
573 	this->legacyNS = rootElem->ns;
574 
575 	// Check the legacy digest.
576 
577 	XMP_StringPtr legacyNS_ = this->legacyNS.c_str();
578 
579 	this->clipMetadata = rootElem;	// ! Save the NonRealTimeMeta pointer for other use.
580 
581 	std::string oldDigest, newDigest;
582 	bool digestFound = this->xmpObj.GetStructField ( kXMP_NS_XMP, "NativeDigests", kXMP_NS_XMP, "XDCAM", &oldDigest, 0 );
583 	if ( digestFound ) {
584 		this->MakeLegacyDigest ( &newDigest );
585 		if ( oldDigest == newDigest ) CleanupAndExit
586 	}
587 
588 	// If we get here we need find and import the actual legacy elements using the current namespace.
589 	// Either there is no old digest in the XMP, or the digests differ. In the former case keep any
590 	// existing XMP, in the latter case take new legacy values.
591 
592 	this->containsXMP = XDCAM_Support::GetLegacyMetadata ( &this->xmpObj, rootElem, legacyNS_, digestFound, umid );
593 	this->containsXMP |= GetMediaProMetadata ( &this->xmpObj, umid, digestFound );
594 
595 	CleanupAndExit
596 	#undef CleanupAndExit
597 
598 }	// XDCAM_MetaHandler::ProcessXMP
599 
600 // =================================================================================================
601 // XDCAM_MetaHandler::UpdateFile
602 // =============================
603 //
604 // Note that UpdateFile is only called from XMPFiles::CloseFile, so it is OK to close the file here.
605 
UpdateFile(bool doSafeUpdate)606 void XDCAM_MetaHandler::UpdateFile ( bool doSafeUpdate )
607 {
608 	if ( ! this->needsUpdate ) return;
609 	this->needsUpdate = false;	// Make sure only called once.
610 
611 	XMP_Assert ( this->parent->UsesLocalIO() );
612 
613 	// Update the internal legacy XML tree if we have one, and set the digest in the XMP.
614 
615 	bool updateLegacyXML = false;
616 
617 	if ( this->clipMetadata != 0 ) {
618 		updateLegacyXML = XDCAM_Support::SetLegacyMetadata ( this->clipMetadata, &this->xmpObj, this->legacyNS.c_str());
619 	}
620 
621 	std::string newDigest;
622 	this->MakeLegacyDigest ( &newDigest );
623 	this->xmpObj.SetStructField ( kXMP_NS_XMP, "NativeDigests", kXMP_NS_XMP, "XDCAM", newDigest.c_str(), kXMP_DeleteExisting );
624 	this->xmpObj.SerializeToBuffer ( &this->xmpPacket, this->GetSerializeOptions() );
625 
626 	// -----------------------------------------------------------------------
627 	// Update the XMP file first, don't let legacy XML failures block the XMP.
628 
629 
630 
631 	bool haveXMP = Host_IO::Exists ( this->sidecarPath.c_str() );
632 	if ( ! haveXMP ) {
633 		XMP_Assert ( this->parent->ioRef == 0 );
634 		Host_IO::Create ( this->sidecarPath.c_str() );
635 		this->parent->ioRef = XMPFiles_IO::New_XMPFiles_IO ( this->sidecarPath.c_str(), Host_IO::openReadWrite );
636 		if ( this->parent->ioRef == 0 ) XMP_Throw ( "Failure opening XDCAM XMP file", kXMPErr_ExternalFailure );
637 	}
638 
639 	XMP_IO* xmpFile = this->parent->ioRef;
640 	XMP_Assert ( xmpFile != 0 );
641 	XIO::ReplaceTextFile ( xmpFile, this->xmpPacket, (haveXMP & doSafeUpdate) );
642 
643 	// --------------------------------------------
644 	// Now update the legacy XML file if necessary.
645 
646 	if ( updateLegacyXML ) {
647 
648 		std::string legacyXML, xmlPath;
649 		this->expat->tree.Serialize ( &legacyXML );
650 		xmlPath = this->mNRTFilePath;
651 
652 		bool haveXML = Host_IO::Exists ( xmlPath.c_str() );
653 		if ( ! haveXML ) Host_IO::Create ( xmlPath.c_str() );
654 
655 		Host_IO::FileRef hostRef = Host_IO::Open ( xmlPath.c_str(), Host_IO::openReadWrite );
656 		if ( hostRef == Host_IO::noFileRef ) XMP_Throw ( "Failure opening XDCAM XML file", kXMPErr_ExternalFailure );
657 		XMPFiles_IO origXML ( hostRef, xmlPath.c_str(), Host_IO::openReadWrite );
658 		XIO::ReplaceTextFile ( &origXML, legacyXML, (haveXML & doSafeUpdate) );
659 		origXML.Close();
660 
661 	}
662 
663 }	// XDCAM_MetaHandler::UpdateFile
664 
665 // =================================================================================================
666 // XDCAM_MetaHandler::WriteTempFile
667 // ================================
668 
WriteTempFile(XMP_IO *)669 void XDCAM_MetaHandler::WriteTempFile ( XMP_IO* /*tempRef*/ )
670 {
671 
672 	// ! WriteTempFile is not supposed to be called for handlers that own the file.
673 	XMP_Throw ( "XDCAM_MetaHandler::WriteTempFile should not be called", kXMPErr_InternalFailure );
674 
675 }	// XDCAM_MetaHandler::WriteTempFile
676 
677 // =================================================================================================
678