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