1 // =================================================================================================
2 // ADOBE SYSTEMS INCORPORATED
3 // Copyright 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 "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/AVCHD_Handler.hpp"
21 #include "XMPFiles/source/FormatSupport/PackageFormat_Support.hpp"
22 
23 #include "source/UnicodeConversions.hpp"
24 #include "third-party/zuid/interfaces/MD5.h"
25 
26 using namespace std;
27 
28 // AVCHD maker ID values. Panasonic has confirmed their Maker ID with us, the others come from examining
29 // sample data files.
30 #define kMakerIDPanasonic 0x103
31 #define kMakerIDSony 0x108
32 #define kMakerIDCanon 0x1011
33 
34 // =================================================================================================
35 /// \file AVCHD_Handler.cpp
36 /// \brief Folder format handler for AVCHD.
37 ///
38 /// This handler is for the AVCHD video format.
39 ///
40 /// A typical AVCHD layout looks like:
41 ///
42 ///     BDMV/
43 ///         index.bdmv
44 ///         MovieObject.bdmv
45 ///         PLAYLIST/
46 ///				00000.mpls
47 ///             00001.mpls
48 ///         STREAM/
49 ///				00000.m2ts
50 ///             00001.m2ts
51 ///         CLIPINF/
52 ///				00000.clpi
53 ///             00001.clpi
54 ///         BACKUP/
55 ///
56 // =================================================================================================
57 
58 // =================================================================================================
59 
60 // AVCHD Format. Book 1: Playback System Basic Specifications V 1.01. p. 76
61 
62 struct AVCHD_blkProgramInfo
63 {
64 	XMP_Uns32	mLength;
65 	XMP_Uns8	mReserved1[2];
66 	XMP_Uns32	mSPNProgramSequenceStart;
67 	XMP_Uns16	mProgramMapPID;
68 	XMP_Uns8	mNumberOfStreamsInPS;
69 	XMP_Uns8	mReserved2;
70 
71 	// Video stream.
72 	struct
73 	{
74 		XMP_Uns8	mPresent;
75 		XMP_Uns8	mVideoFormat;
76 		XMP_Uns8	mFrameRate;
77 		XMP_Uns8	mAspectRatio;
78 		XMP_Uns8	mCCFlag;
79 	} mVideoStream;
80 
81 	// Audio stream.
82 	struct
83 	{
84 		XMP_Uns8	mPresent;
85 		XMP_Uns8	mAudioPresentationType;
86 		XMP_Uns8	mSamplingFrequency;
87 		XMP_Uns8	mAudioLanguageCode[4];
88 	} mAudioStream;
89 
90 	// Pverlay bitmap stream.
91 	struct
92 	{
93 		XMP_Uns8	mPresent;
94 		XMP_Uns8	mOBLanguageCode[4];
95 	} mOverlayBitmapStream;
96 
97 	// Menu bitmap stream.
98 	struct
99 	{
100 		XMP_Uns8	mPresent;
101 		XMP_Uns8	mBMLanguageCode[4];
102 	} mMenuBitmapStream;
103 
104 };
105 
106 // AVCHD Format, Panasonic proprietary PRO_PlayListMark block
107 
108 struct AVCCAM_blkProPlayListMark
109 {
110 	XMP_Uns8	mPresent;
111 	XMP_Uns8	mProTagID;
112 	XMP_Uns8	mFillItem1;
113 	XMP_Uns16	mLength;
114 	XMP_Uns8	mMarkType;
115 
116 	// Entry mark
117 	struct
118 	{
119 		XMP_Uns8	mGlobalClipID[32];
120 		XMP_Uns8	mStartTimeCode[4];
121 		XMP_Uns8	mStreamTimecodeInfo;
122 		XMP_Uns8	mStartBinaryGroup[4];
123 		XMP_Uns8	mLastUpdateTimeZone;
124 		XMP_Uns8	mLastUpdateDate[7];
125 		XMP_Uns16	mFillItem;
126 	} mEntryMark;
127 
128 	// Shot Mark
129 	struct
130 	{
131 		XMP_Uns8	mPresent;
132 		XMP_Uns8	mShotMark;
133 		XMP_Uns8	mFillItem[3];
134 	} mShotMark;
135 
136 	// Access
137 	struct
138 	{
139 		XMP_Uns8	mPresent;
140 		XMP_Uns8	mCreatorCharacterSet;
141 		XMP_Uns8	mCreatorLength;
142 		XMP_Uns8	mCreator[32];
143 		XMP_Uns8	mLastUpdatePersonCharacterSet;
144 		XMP_Uns8	mLastUpdatePersonLength;
145 		XMP_Uns8	mLastUpdatePerson[32];
146 	} mAccess;
147 
148 	// Device
149 	struct
150 	{
151 		XMP_Uns8	mPresent;
152 		XMP_Uns16	mMakerID;
153 		XMP_Uns16	mMakerModelCode;
154 		XMP_Uns8	mSerialNoCharacterCode;
155 		XMP_Uns8	mSerialNoLength;
156 		XMP_Uns8	mSerialNo[24];
157 		XMP_Uns16	mFillItem;
158 	} mDevice;
159 
160 	// Shoot
161 	struct
162 	{
163 		XMP_Uns8	mPresent;
164 		XMP_Uns8	mShooterCharacterSet;
165 		XMP_Uns8	mShooterLength;
166 		XMP_Uns8	mShooter[32];
167 		XMP_Uns8	mStartDateTimeZone;
168 		XMP_Uns8	mStartDate[7];
169 		XMP_Uns8	mEndDateTimeZone;
170 		XMP_Uns8	mEndDate[7];
171 		XMP_Uns16	mFillItem;
172 	} mShoot;
173 
174 	// Location
175 	struct
176 	{
177 		XMP_Uns8	mPresent;
178 		XMP_Uns8	mSource;
179 		XMP_Uns32	mGPSLatitudeRef;
180 		XMP_Uns32	mGPSLatitude1;
181 		XMP_Uns32	mGPSLatitude2;
182 		XMP_Uns32	mGPSLatitude3;
183 		XMP_Uns32	mGPSLongitudeRef;
184 		XMP_Uns32	mGPSLongitude1;
185 		XMP_Uns32	mGPSLongitude2;
186 		XMP_Uns32	mGPSLongitude3;
187 		XMP_Uns32	mGPSAltitudeRef;
188 		XMP_Uns32	mGPSAltitude;
189 		XMP_Uns8	mPlaceNameCharacterSet;
190 		XMP_Uns8	mPlaceNameLength;
191 		XMP_Uns8	mPlaceName[64];
192 		XMP_Uns8	mFillItem;
193 	} mLocation;
194 };
195 
196 // AVCHD Format, Panasonic proprietary extension data (AVCCAM)
197 
198 struct AVCCAM_Pro_PlayListInfo
199 {
200 	XMP_Uns8					mPresent;
201 	XMP_Uns8					mTagID;
202 	XMP_Uns8					mTagVersion;
203 	XMP_Uns16					mFillItem1;
204 	XMP_Uns32					mLength;
205 	XMP_Uns16					mNumberOfPlayListMarks;
206 	XMP_Uns16					mFillItem2;
207 
208 	// Although a playlist may contain multiple marks, we only store the one that corresponds to
209 	// the clip/shot of interest.
210 	AVCCAM_blkProPlayListMark	mPlayListMark;
211 };
212 
213 // AVCHD Format, Panasonic proprietary extension data (AVCCAM)
214 
215 struct AVCHD_blkPanasonicPrivateData
216 {
217 	XMP_Uns8					mPresent;
218 	XMP_Uns16					mNumberOfData;
219 	XMP_Uns16					mReserved;
220 
221 	struct
222 	{
223 		XMP_Uns8	mPresent;
224 		XMP_Uns8	mTagID;
225 		XMP_Uns8	mTagVersion;
226 		XMP_Uns16	mTagLength;
227 		XMP_Uns8	mProfessionalMetaID[16];
228 	} mProMetaIDBlock;
229 
230 	struct
231 	{
232 		XMP_Uns8	mPresent;
233 		XMP_Uns8	mTagID;
234 		XMP_Uns8	mTagVersion;
235 		XMP_Uns16	mTagLength;
236 		XMP_Uns8	mGlobalClipID[32];
237 		XMP_Uns8	mStartTimecode[4];
238 		XMP_Uns32	mStartBinaryGroup;
239 	} mProClipIDBlock;
240 
241 	AVCCAM_Pro_PlayListInfo		mProPlaylistInfoBlock;
242 };
243 
244 // AVCHD Format. Book 2: Recording Extension Specifications, section 4.2.4.2. plus Panasonic extensions
245 
246 struct AVCHD_blkMakersPrivateData
247 {
248 	XMP_Uns8						mPresent;
249 	XMP_Uns32						mLength;
250 	XMP_Uns32						mDataBlockStartAddress;
251 	XMP_Uns8						mReserved[3];
252 	XMP_Uns8						mNumberOfMakerEntries;
253 	XMP_Uns16						mMakerID;
254 	XMP_Uns16						mMakerModelCode;
255 	AVCHD_blkPanasonicPrivateData	mPanasonicPrivateData;
256 };
257 
258 // AVCHD Format. Book 2: Recording Extension Specifications, section 4.4.2.1
259 
260 struct AVCHD_blkClipInfoExt
261 {
262 	XMP_Uns32	mLength;
263 	XMP_Uns16	mMakerID;
264 	XMP_Uns16	mMakerModelCode;
265 };
266 
267 // AVCHD Format. Book 2: Recording Extension Specifications, section 4.4.1.2
268 
269 struct AVCHD_blkClipExtensionData
270 {
271 	XMP_Uns8					mPresent;
272 	XMP_Uns8					mTypeIndicator[4];
273 	XMP_Uns8					mReserved1[4];
274 	XMP_Uns32					mProgramInfoExtStartAddress;
275 	XMP_Uns32					mMakersPrivateDataStartAddress;
276 
277 	AVCHD_blkClipInfoExt		mClipInfoExt;
278 	AVCHD_blkMakersPrivateData	mMakersPrivateData;
279 };
280 
281 // AVCHD Format. Book 2: Recording Extension Specifications, section 4.3.3.1 -- although each playlist
282 // may contain a list of these, we only record the one that matches our target shot/clip.
283 
284 struct AVCHD_blkPlayListMarkExt
285 {
286 	XMP_Uns32	mLength;
287 	XMP_Uns16	mNumberOfPlaylistMarks;
288 	bool		mPresent;
289 	XMP_Uns16	mMakerID;
290 	XMP_Uns16	mMakerModelCode;
291 	XMP_Uns8	mReserved1[3];
292 	XMP_Uns8	mFlags;			// bit 0: MarkWriteProtectFlag, bits 1-2: pulldown
293 	XMP_Uns16	mRefToMarkThumbnailIndex;
294 	XMP_Uns8	mBlkTimezone;
295 	XMP_Uns8	mRecordDataAndTime[7];
296 	XMP_Uns8	mMarkCharacterSet;
297 	XMP_Uns8	mMarkNameLength;
298 	XMP_Uns8	mMarkName[24];
299 	XMP_Uns8	mMakersInformation[16];
300 	XMP_Uns8	mBlkTimecode[4];
301 	XMP_Uns16	mReserved2;
302 };
303 
304 // AVCHD Format. Book 2: Recording Extension Specifications, section 4.3.2.1
305 
306 struct AVCHD_blkPlaylistMeta
307 {
308 	XMP_Uns32	mLength;
309 	XMP_Uns16	mMakerID;
310 	XMP_Uns16	mMakerModelCode;
311 	XMP_Uns32	mReserved1;
312 	XMP_Uns16	mRefToMenuThumbnailIndex;
313 	XMP_Uns8	mBlkTimezone;
314 	XMP_Uns8	mRecordDataAndTime[7];
315 	XMP_Uns8	mReserved2;
316 	XMP_Uns8	mPlaylistCharacterSet;
317 	XMP_Uns8	mPlaylistNameLength;
318 	XMP_Uns8	mPlaylistName[255];
319 };
320 
321 // AVCHD Format. Book 2: Recording Extension Specifications, section 4.3.1.2
322 
323 struct AVCHD_blkPlayListExtensionData
324 {
325 	XMP_Uns8					mPresent;
326 	char						mTypeIndicator[4];
327 	XMP_Uns8					mReserved[4];
328 	XMP_Uns32					mPlayListMarkExtStartAddress;
329 	XMP_Uns32					mMakersPrivateDataStartAddress;
330 
331 	AVCHD_blkPlaylistMeta		mPlaylistMeta;
332 	AVCHD_blkPlayListMarkExt	mPlaylistMarkExt;
333 	AVCHD_blkMakersPrivateData	mMakersPrivateData;
334 };
335 
336 // AVCHD Format. Book 1: Playback System Basic Specifications V 1.01. p. 38
337 struct AVCHD_blkExtensionData
338 {
339 	XMP_Uns32	mLength;
340 	XMP_Uns32	mDataBlockStartAddress;
341 	XMP_Uns8	mReserved[3];
342 	XMP_Uns8	mNumberOfDataEntries;
343 
344 	struct AVCHD_blkExtDataEntry
345 	{
346 		XMP_Uns16	mExtDataType;
347 		XMP_Uns16	mExtDataVersion;
348 		XMP_Uns32	mExtDataStartAddress;
349 		XMP_Uns32	mExtDataLength;
350 	} mExtDataEntry;
351 };
352 
353 // Simple container for the various AVCHD legacy metadata structures we care about for an AVCHD clip
354 
355 struct AVCHD_LegacyMetadata
356 {
357 	AVCHD_blkProgramInfo			mProgramInfo;
358 	AVCHD_blkClipExtensionData		mClipExtensionData;
359 	AVCHD_blkPlayListExtensionData	mPlaylistExtensionData;
360 };
361 
362 // =================================================================================================
363 // MakeLeafPath
364 // ============
365 
MakeLeafPath(std::string * path,XMP_StringPtr root,XMP_StringPtr group,XMP_StringPtr clip,XMP_StringPtr suffix,bool checkFile=false)366 static bool MakeLeafPath ( std::string * path, XMP_StringPtr root, XMP_StringPtr group,
367 						   XMP_StringPtr clip, XMP_StringPtr suffix, bool checkFile = false )
368 {
369 	size_t partialLen;
370 
371 	*path = root;
372 	*path += kDirChar;
373 	*path += "BDMV";
374 	*path += kDirChar;
375 	*path += group;
376 	*path += kDirChar;
377 	*path += clip;
378 	partialLen = path->size();
379 	*path += suffix;
380 
381 	if ( ! checkFile ) return true;
382 	if ( Host_IO::GetFileMode ( path->c_str() ) == Host_IO::kFMode_IsFile ) return true;
383 
384 	// Convert the suffix to uppercase and try again. Even on Mac/Win, in case a remote file system is sensitive.
385 	for ( char* chPtr = ((char*)path->c_str() + partialLen); *chPtr != 0; ++chPtr ) {
386 		if ( (0x61 <= *chPtr) && (*chPtr <= 0x7A) ) *chPtr -= 0x20;
387 	}
388 	if ( Host_IO::GetFileMode ( path->c_str() ) == Host_IO::kFMode_IsFile ) return true;
389 
390 	if ( XMP_LitMatch ( suffix, ".clpi" ) ) {	// Special case of ".cpi" for the clip file.
391 
392 		path->erase ( partialLen );
393 		*path += ".cpi";
394 		if ( Host_IO::GetFileMode ( path->c_str() ) == Host_IO::kFMode_IsFile ) return true;
395 
396 		path->erase ( partialLen );
397 		*path += ".CPI";
398 		if ( Host_IO::GetFileMode ( path->c_str() ) == Host_IO::kFMode_IsFile ) return true;
399 
400 	} else if ( XMP_LitMatch ( suffix, ".mpls" ) ) {	// Special case of ".mpl" for the playlist file.
401 
402 		path->erase ( partialLen );
403 		*path += ".mpl";
404 		if ( Host_IO::GetFileMode ( path->c_str() ) == Host_IO::kFMode_IsFile ) return true;
405 
406 		path->erase ( partialLen );
407 		*path += ".MPL";
408 		if ( Host_IO::GetFileMode ( path->c_str() ) == Host_IO::kFMode_IsFile ) return true;
409 
410 	} else if ( XMP_LitMatch ( suffix, ".m2ts" ) ) {	// Special case of ".mts" for the stream file.
411 
412 		path->erase ( partialLen );
413 		*path += ".mts";
414 		if ( Host_IO::GetFileMode ( path->c_str() ) == Host_IO::kFMode_IsFile ) return true;
415 
416 		path->erase ( partialLen );
417 		*path += ".MTS";
418 		if ( Host_IO::GetFileMode ( path->c_str() ) == Host_IO::kFMode_IsFile ) return true;
419 
420 	}
421 
422 	// Still not found, revert to the original suffix.
423 	path->erase ( partialLen );
424 	*path += suffix;
425 	return false;
426 
427 }	// MakeLeafPath
428 
429 // =================================================================================================
430 // AVCHD_CheckFormat
431 // =================
432 //
433 // This version checks for the presence of a top level BPAV directory, and the required files and
434 // directories immediately within it. The CLIPINF, PLAYLIST, and STREAM subfolders are required, as
435 // are the index.bdmv and MovieObject.bdmv files.
436 //
437 // The state of the string parameters depends on the form of the path passed by the client. If the
438 // client passed a logical clip path, like ".../MyMovie/00001", the parameters are:
439 //   rootPath   - ".../MyMovie"
440 //   gpName     - empty
441 //   parentName - empty
442 //   leafName   - "00001"
443 // If the client passed a full file path, like ".../MyMovie/BDMV/CLIPINF/00001.clpi", they are:
444 //   rootPath   - ".../MyMovie"
445 //   gpName     - "BDMV"
446 //   parentName - "CLIPINF" or "PALYLIST" or "STREAM"
447 //   leafName   - "00001"
448 
449 // ! The common code has shifted the gpName, parentName, and leafName strings to upper case. It has
450 // ! also made sure that for a logical clip path the rootPath is an existing folder, and that the
451 // ! file exists for a full file path.
452 
453 // ! Using explicit '/' as a separator when creating paths, it works on Windows.
454 
455 // ! Sample files show that the ".bdmv" extension can sometimes be ".bdm". Allow either.
456 
AVCHD_CheckFormat(XMP_FileFormat,const std::string & rootPath,const std::string & gpName,const std::string & parentName,const std::string & leafName,XMPFiles * parent)457 bool AVCHD_CheckFormat ( XMP_FileFormat /*format*/,
458 						 const std::string & rootPath,
459 						 const std::string & gpName,
460 						 const std::string & parentName,
461 						 const std::string & leafName,
462 						 XMPFiles * parent )
463 {
464 	if ( gpName.empty() != parentName.empty() ) return false;	// Must be both empty or both non-empty.
465 
466 	if ( ! gpName.empty() ) {
467 		if ( gpName != "BDMV" ) return false;
468 		if ( (parentName != "CLIPINF") && (parentName != "PLAYLIST") && (parentName != "STREAM") ) return false;
469 	}
470 
471 	// Check the rest of the required general structure. Look for both ".bdmv" and ".bmd" extensions.
472 
473 	std::string bdmvPath ( rootPath );
474 	bdmvPath += kDirChar;
475 	bdmvPath += "BDMV";
476 
477 	if ( Host_IO::GetChildMode ( bdmvPath.c_str(), "CLIPINF" ) != Host_IO::kFMode_IsFolder ) return false;
478 	if ( Host_IO::GetChildMode ( bdmvPath.c_str(), "PLAYLIST" ) != Host_IO::kFMode_IsFolder ) return false;
479 	if ( Host_IO::GetChildMode ( bdmvPath.c_str(), "STREAM" ) != Host_IO::kFMode_IsFolder ) return false;
480 
481 	if ( (Host_IO::GetChildMode ( bdmvPath.c_str(), "index.bdmv" ) != Host_IO::kFMode_IsFile) &&
482 		 (Host_IO::GetChildMode ( bdmvPath.c_str(), "index.bdm" ) != Host_IO::kFMode_IsFile) &&
483 		 (Host_IO::GetChildMode ( bdmvPath.c_str(), "INDEX.BDMV" ) != Host_IO::kFMode_IsFile) &&	// Some usage is all caps.
484 		 (Host_IO::GetChildMode ( bdmvPath.c_str(), "INDEX.BDM" ) != Host_IO::kFMode_IsFile) ) return false;
485 
486 	if ( (Host_IO::GetChildMode ( bdmvPath.c_str(), "MovieObject.bdmv" ) != Host_IO::kFMode_IsFile) &&
487 		 (Host_IO::GetChildMode ( bdmvPath.c_str(), "MovieObj.bdm" ) != Host_IO::kFMode_IsFile) &&
488 		 (Host_IO::GetChildMode ( bdmvPath.c_str(), "MOVIEOBJECT.BDMV" ) != Host_IO::kFMode_IsFile) &&	// Some usage is all caps.
489 		 (Host_IO::GetChildMode ( bdmvPath.c_str(), "MOVIEOBJ.BDM" ) != Host_IO::kFMode_IsFile) ) return false;
490 
491 
492 	// Make sure the .clpi file exists.
493 	std::string tempPath;
494 	bool foundClpi = MakeLeafPath ( &tempPath, rootPath.c_str(), "CLIPINF", leafName.c_str(), ".clpi", true /* checkFile */ );
495 	if ( ! foundClpi ) return false;
496 
497 	// And now save the pseudo path for the handler object.
498 	tempPath = rootPath;
499 	tempPath += kDirChar;
500 	tempPath += leafName;
501 	size_t pathLen = tempPath.size() + 1;	// Include a terminating nul.
502 	parent->tempPtr = malloc ( pathLen );
503 	if ( parent->tempPtr == 0 ) XMP_Throw ( "No memory for AVCHD clip info", kXMPErr_NoMemory );
504 	memcpy ( parent->tempPtr, tempPath.c_str(), pathLen );
505 
506 	return true;
507 
508 }	// AVCHD_CheckFormat
509 
510 // =================================================================================================
511 
CreatePseudoClipPath(const std::string & clientPath)512 static void* CreatePseudoClipPath ( const std::string & clientPath ) {
513 
514 	// Used to create the clip pseudo path when the CheckFormat function is skipped.
515 
516 	std::string pseudoPath = clientPath;
517 
518 	size_t pathLen;
519 	void* tempPtr = 0;
520 
521 	if ( Host_IO::Exists ( pseudoPath.c_str() ) ) {
522 
523 		// The client passed a physical path. The logical clip name is the leaf name, with the
524 		// extension removed. There are no extra suffixes on AVCHD files. The movie root path ends
525 		// two levels up.
526 
527 		std::string clipName, ignored;
528 
529 		XIO::SplitLeafName ( &pseudoPath, &clipName );	// Extract the logical clip name.
530 		XIO::SplitFileExtension ( &clipName, &ignored );
531 
532 		XIO::SplitLeafName ( &pseudoPath, &ignored );	// Remove the 2 intermediate folder levels.
533 		XIO::SplitLeafName ( &pseudoPath, &ignored );
534 
535 		pseudoPath += kDirChar;
536 		pseudoPath += clipName;
537 
538 	}
539 
540 	pathLen = pseudoPath.size() + 1;	// Include a terminating nul.
541 	tempPtr = malloc ( pathLen );
542 	if ( tempPtr == 0 ) XMP_Throw ( "No memory for AVCHD clip info", kXMPErr_NoMemory );
543 	memcpy ( tempPtr, pseudoPath.c_str(), pathLen );
544 
545 	return tempPtr;
546 
547 }	// CreatePseudoClipPath
548 
549 // =================================================================================================
550 // ReadAVCHDProgramInfo
551 // ====================
552 
ReadAVCHDProgramInfo(XMPFiles_IO & cpiFile,AVCHD_blkProgramInfo & avchdProgramInfo)553 static bool ReadAVCHDProgramInfo ( XMPFiles_IO & cpiFile, AVCHD_blkProgramInfo& avchdProgramInfo )
554 {
555 	avchdProgramInfo.mLength = XIO::ReadUns32_BE ( &cpiFile );
556 	cpiFile.ReadAll ( avchdProgramInfo.mReserved1, 2 );
557 	avchdProgramInfo.mSPNProgramSequenceStart = XIO::ReadUns32_BE ( &cpiFile );
558 	avchdProgramInfo.mProgramMapPID = XIO::ReadUns16_BE ( &cpiFile );
559 	cpiFile.ReadAll ( &avchdProgramInfo.mNumberOfStreamsInPS, 1 );
560 	cpiFile.ReadAll ( &avchdProgramInfo.mReserved2, 1 );
561 
562 	for ( int i=0; i<avchdProgramInfo.mNumberOfStreamsInPS; ++i ) {
563 
564 		XMP_Uns8	length = 0;
565 		XMP_Uns8	streamCodingType = 0;
566 
567 		// Unused. But we shall read it.
568 		/*streamPID =*/ XIO::ReadUns16_BE ( &cpiFile );
569 		cpiFile.ReadAll ( &length, 1 );
570 
571 		XMP_Int64 pos = cpiFile.Offset();
572 
573 		cpiFile.ReadAll ( &streamCodingType, 1 );
574 
575 		switch ( streamCodingType ) {
576 
577 			case 0x1B	: // Video stream case.
578 				{
579 					XMP_Uns8 videoFormatAndFrameRate;
580 					cpiFile.ReadAll ( &videoFormatAndFrameRate, 1 );
581 					avchdProgramInfo.mVideoStream.mVideoFormat	= videoFormatAndFrameRate >> 4;    // hi 4 bits
582 					avchdProgramInfo.mVideoStream.mFrameRate	= videoFormatAndFrameRate & 0x0f;  // lo 4 bits
583 
584 					XMP_Uns8 aspectRatioAndReserved = 0;
585 					cpiFile.ReadAll ( &aspectRatioAndReserved, 1 );
586 					avchdProgramInfo.mVideoStream.mAspectRatio	 = aspectRatioAndReserved >> 4; // hi 4 bits
587 
588 					XMP_Uns8 ccFlag = 0;
589 					cpiFile.ReadAll ( &ccFlag, 1 );
590 					avchdProgramInfo.mVideoStream.mCCFlag = ccFlag;
591 
592 					avchdProgramInfo.mVideoStream.mPresent = 1;
593 				}
594 				break;
595 
596 			case 0x80   : // Fall through.
597 			case 0x81	: // Audio stream case.
598 				{
599 					XMP_Uns8 audioPresentationTypeAndFrequency = 0;
600 					cpiFile.ReadAll ( &audioPresentationTypeAndFrequency, 1 );
601 
602 					avchdProgramInfo.mAudioStream.mAudioPresentationType = audioPresentationTypeAndFrequency >> 4;    // hi 4 bits
603 					avchdProgramInfo.mAudioStream.mSamplingFrequency	 = audioPresentationTypeAndFrequency & 0x0f;  // lo 4 bits
604 
605 					cpiFile.ReadAll ( avchdProgramInfo.mAudioStream.mAudioLanguageCode, 3 );
606 					avchdProgramInfo.mAudioStream.mAudioLanguageCode[3] = 0;
607 
608 					avchdProgramInfo.mAudioStream.mPresent = 1;
609 				}
610 				break;
611 
612 			case 0x90	: // Overlay bitmap stream case.
613 				cpiFile.ReadAll ( &avchdProgramInfo.mOverlayBitmapStream.mOBLanguageCode, 3 );
614 				avchdProgramInfo.mOverlayBitmapStream.mOBLanguageCode[3] = 0;
615 				avchdProgramInfo.mOverlayBitmapStream.mPresent = 1;
616 				break;
617 
618 			case 0x91   : // Menu bitmap stream.
619 				cpiFile.ReadAll ( &avchdProgramInfo.mMenuBitmapStream.mBMLanguageCode, 3 );
620 				avchdProgramInfo.mMenuBitmapStream.mBMLanguageCode[3] = 0;
621 				avchdProgramInfo.mMenuBitmapStream.mPresent = 1;
622 				break;
623 
624 			default :
625 				break;
626 
627 		}
628 
629 		cpiFile.Seek ( pos + length, kXMP_SeekFromStart );
630 
631 	}
632 
633 	return true;
634 }
635 
636 // =================================================================================================
637 // ReadAVCHDExtensionData
638 // ======================
639 
ReadAVCHDExtensionData(XMPFiles_IO & cpiFile,AVCHD_blkExtensionData & extensionDataHeader)640 static bool ReadAVCHDExtensionData ( XMPFiles_IO & cpiFile, AVCHD_blkExtensionData& extensionDataHeader )
641 {
642 	extensionDataHeader.mLength = XIO::ReadUns32_BE ( &cpiFile );
643 
644 	if ( extensionDataHeader.mLength == 0 ) {
645 		// Nothing to read
646 		return true;
647 	}
648 
649 	extensionDataHeader.mDataBlockStartAddress = XIO::ReadUns32_BE ( &cpiFile );
650 	cpiFile.ReadAll ( extensionDataHeader.mReserved, 3 );
651 	cpiFile.ReadAll ( &extensionDataHeader.mNumberOfDataEntries, 1 );
652 
653 	if ( extensionDataHeader.mNumberOfDataEntries != 1 ) {
654 		// According to AVCHD Format. Book1. v. 1.01. p 38, "This field shall be set to 1 in this format."
655 		return false;
656 	}
657 
658 	extensionDataHeader.mExtDataEntry.mExtDataType = XIO::ReadUns16_BE ( &cpiFile );
659 	extensionDataHeader.mExtDataEntry.mExtDataVersion = XIO::ReadUns16_BE ( &cpiFile );
660 	extensionDataHeader.mExtDataEntry.mExtDataStartAddress = XIO::ReadUns32_BE ( &cpiFile );
661 	extensionDataHeader.mExtDataEntry.mExtDataLength = XIO::ReadUns32_BE ( &cpiFile );
662 
663 	if ( extensionDataHeader.mExtDataEntry.mExtDataType != 0x1000 ) {
664 		// According to AVCHD Format. Book1. v. 1.01. p 38, "If the metadata is for an AVCHD application,
665 		// this value shall be set to 'Ox1OOO'."
666 		return false;
667 	}
668 
669 	return true;
670 }
671 
672 // =================================================================================================
673 // ReadAVCCAMProMetaID
674 // ===================
675 //
676 // Read Panasonic's proprietary PRO_MetaID block
677 
ReadAVCCAMProMetaID(XMPFiles_IO & cpiFile,XMP_Uns8 tagID,AVCHD_blkPanasonicPrivateData & extensionDataHeader)678 static bool ReadAVCCAMProMetaID ( XMPFiles_IO & cpiFile, XMP_Uns8 tagID, AVCHD_blkPanasonicPrivateData& extensionDataHeader )
679 {
680 	extensionDataHeader.mPresent = 1;
681 	extensionDataHeader.mProMetaIDBlock.mPresent = 1;
682 	extensionDataHeader.mProMetaIDBlock.mTagID = tagID;
683 	cpiFile.ReadAll ( &extensionDataHeader.mProMetaIDBlock.mTagVersion, 1);
684 	extensionDataHeader.mProMetaIDBlock.mTagLength = XIO::ReadUns16_BE ( &cpiFile );
685 	cpiFile.ReadAll ( &extensionDataHeader.mProMetaIDBlock.mProfessionalMetaID, 16);
686 
687 	return true;
688 }
689 
690 // =================================================================================================
691 // ReadAVCCAMProClipInfo
692 // =====================
693 //
694 // Read Panasonic's proprietary PRO_ClipInfo block.
695 
ReadAVCCAMProClipInfo(XMPFiles_IO & cpiFile,XMP_Uns8 tagID,AVCHD_blkPanasonicPrivateData & extensionDataHeader)696 static bool ReadAVCCAMProClipInfo ( XMPFiles_IO & cpiFile, XMP_Uns8 tagID, AVCHD_blkPanasonicPrivateData& extensionDataHeader )
697 {
698 	extensionDataHeader.mPresent = 1;
699 	extensionDataHeader.mProClipIDBlock.mPresent = 1;
700 	extensionDataHeader.mProClipIDBlock.mTagID = tagID;
701 	cpiFile.ReadAll ( &extensionDataHeader.mProClipIDBlock.mTagVersion, 1);
702 	extensionDataHeader.mProClipIDBlock.mTagLength = XIO::ReadUns16_BE ( &cpiFile );
703 	cpiFile.ReadAll ( &extensionDataHeader.mProClipIDBlock.mGlobalClipID, 32);
704 	cpiFile.ReadAll ( &extensionDataHeader.mProClipIDBlock.mStartTimecode, 4 );
705 	extensionDataHeader.mProClipIDBlock.mStartBinaryGroup = XIO::ReadUns32_BE ( &cpiFile );
706 
707 	return true;
708 }
709 
710 // =================================================================================================
711 // ReadAVCCAM_blkPRO_ShotMark
712 // ==========================
713 //
714 // Read Panasonic's proprietary PRO_ShotMark block.
715 
ReadAVCCAM_blkPRO_ShotMark(XMPFiles_IO & mplFile,AVCCAM_blkProPlayListMark & proMark)716 static bool ReadAVCCAM_blkPRO_ShotMark ( XMPFiles_IO & mplFile, AVCCAM_blkProPlayListMark& proMark )
717 {
718 	proMark.mShotMark.mPresent = 1;
719 	mplFile.ReadAll ( &proMark.mShotMark.mShotMark, 1);
720 	mplFile.ReadAll ( &proMark.mShotMark.mFillItem, 3);
721 
722 	return true;
723 }
724 
725 // =================================================================================================
726 // ReadAVCCAM_blkPRO_Access
727 // ========================
728 //
729 // Read Panasonic's proprietary PRO_Access block.
730 
ReadAVCCAM_blkPRO_Access(XMPFiles_IO & mplFile,AVCCAM_blkProPlayListMark & proMark)731 static bool ReadAVCCAM_blkPRO_Access ( XMPFiles_IO & mplFile, AVCCAM_blkProPlayListMark& proMark )
732 {
733 	proMark.mAccess.mPresent = 1;
734 	mplFile.ReadAll ( &proMark.mAccess.mCreatorCharacterSet, 1 );
735 	mplFile.ReadAll ( &proMark.mAccess.mCreatorLength, 1 );
736 	mplFile.ReadAll ( &proMark.mAccess.mCreator, 32 );
737 	mplFile.ReadAll ( &proMark.mAccess.mLastUpdatePersonCharacterSet, 1 );
738 	mplFile.ReadAll ( &proMark.mAccess.mLastUpdatePersonLength, 1 );
739 	mplFile.ReadAll ( &proMark.mAccess.mLastUpdatePerson, 32 );
740 
741 	return true;
742 }
743 
744 // =================================================================================================
745 // ReadAVCCAM_blkPRO_Device
746 // ========================
747 //
748 // Read Panasonic's proprietary PRO_Device block.
749 
ReadAVCCAM_blkPRO_Device(XMPFiles_IO & mplFile,AVCCAM_blkProPlayListMark & proMark)750 static bool ReadAVCCAM_blkPRO_Device ( XMPFiles_IO & mplFile, AVCCAM_blkProPlayListMark& proMark )
751 {
752 	proMark.mDevice.mPresent = 1;
753 	proMark.mDevice.mMakerID = XIO::ReadUns16_BE ( &mplFile );
754 	proMark.mDevice.mMakerModelCode = XIO::ReadUns16_BE ( &mplFile );
755 	mplFile.ReadAll ( &proMark.mDevice.mSerialNoCharacterCode, 1 );
756 	mplFile.ReadAll ( &proMark.mDevice.mSerialNoLength, 1 );
757 	mplFile.ReadAll ( &proMark.mDevice.mSerialNo, 24 );
758 	mplFile.ReadAll ( &proMark.mDevice.mFillItem, 2 );
759 
760 	return true;
761 }
762 
763 // =================================================================================================
764 // ReadAVCCAM_blkPRO_Shoot
765 // =======================
766 //
767 // Read Panasonic's proprietary PRO_Shoot block.
768 
ReadAVCCAM_blkPRO_Shoot(XMPFiles_IO & mplFile,AVCCAM_blkProPlayListMark & proMark)769 static bool ReadAVCCAM_blkPRO_Shoot ( XMPFiles_IO & mplFile, AVCCAM_blkProPlayListMark& proMark )
770 {
771 	proMark.mShoot.mPresent = 1;
772 	mplFile.ReadAll ( &proMark.mShoot.mShooterCharacterSet, 1 );
773 	mplFile.ReadAll ( &proMark.mShoot.mShooterLength, 1 );
774 	mplFile.ReadAll ( &proMark.mShoot.mShooter, 32 );
775 	mplFile.ReadAll ( &proMark.mShoot.mStartDateTimeZone, 1 );
776 	mplFile.ReadAll ( &proMark.mShoot.mStartDate, 7 );
777 	mplFile.ReadAll ( &proMark.mShoot.mEndDateTimeZone, 1 );
778 	mplFile.ReadAll ( &proMark.mShoot.mEndDate, 7 );
779 	mplFile.ReadAll ( &proMark.mShoot.mFillItem, 2 );
780 
781 	return true;
782 }
783 
784 // =================================================================================================
785 // ReadAVCCAM_blkPRO_Location
786 // ==========================
787 //
788 // Read Panasonic's proprietary PRO_Location block.
789 
ReadAVCCAM_blkPRO_Location(XMPFiles_IO & mplFile,AVCCAM_blkProPlayListMark & proMark)790 static bool ReadAVCCAM_blkPRO_Location ( XMPFiles_IO & mplFile, AVCCAM_blkProPlayListMark& proMark )
791 {
792 	proMark.mLocation.mPresent = 1;
793 	mplFile.ReadAll ( &proMark.mLocation.mSource, 1 );
794 	proMark.mLocation.mGPSLatitudeRef = XIO::ReadUns32_BE ( &mplFile );
795 	proMark.mLocation.mGPSLatitude1 = XIO::ReadUns32_BE ( &mplFile );
796 	proMark.mLocation.mGPSLatitude2 = XIO::ReadUns32_BE ( &mplFile );
797 	proMark.mLocation.mGPSLatitude3 = XIO::ReadUns32_BE ( &mplFile );
798 	proMark.mLocation.mGPSLongitudeRef = XIO::ReadUns32_BE ( &mplFile );
799 	proMark.mLocation.mGPSLongitude1 = XIO::ReadUns32_BE ( &mplFile );
800 	proMark.mLocation.mGPSLongitude2 = XIO::ReadUns32_BE ( &mplFile );
801 	proMark.mLocation.mGPSLongitude3 = XIO::ReadUns32_BE ( &mplFile );
802 	proMark.mLocation.mGPSAltitudeRef = XIO::ReadUns32_BE ( &mplFile );
803 	proMark.mLocation.mGPSAltitude = XIO::ReadUns32_BE ( &mplFile );
804 	mplFile.ReadAll ( &proMark.mLocation.mPlaceNameCharacterSet, 1 );
805 	mplFile.ReadAll ( &proMark.mLocation.mPlaceNameLength, 1 );
806 	mplFile.ReadAll ( &proMark.mLocation.mPlaceName, 64 );
807 	mplFile.ReadAll ( &proMark.mLocation.mFillItem, 1 );
808 
809 	return true;
810 }
811 
812 // =================================================================================================
813 // ReadAVCCAMProPlaylistInfo
814 // =========================
815 //
816 // Read Panasonic's proprietary PRO_PlayListInfo block.
817 
ReadAVCCAMProPlaylistInfo(XMPFiles_IO & mplFile,XMP_Uns8 tagID,XMP_Uns16 playlistMarkID,AVCHD_blkPanasonicPrivateData & extensionDataHeader)818 static bool ReadAVCCAMProPlaylistInfo ( XMPFiles_IO & mplFile,
819 										XMP_Uns8 tagID,
820 										XMP_Uns16 playlistMarkID,
821 										AVCHD_blkPanasonicPrivateData& extensionDataHeader )
822 {
823 	AVCCAM_Pro_PlayListInfo& playlistBlock = extensionDataHeader.mProPlaylistInfoBlock;
824 
825 	playlistBlock.mTagID = tagID;
826 	mplFile.ReadAll ( &playlistBlock.mTagVersion, 1);
827 	mplFile.ReadAll ( &playlistBlock.mFillItem1, 2);
828 	playlistBlock.mLength = XIO::ReadUns32_BE ( &mplFile );
829 	playlistBlock.mNumberOfPlayListMarks = XIO::ReadUns16_BE ( &mplFile );
830 	mplFile.ReadAll ( &playlistBlock.mFillItem2, 2);
831 
832 	if ( playlistBlock.mNumberOfPlayListMarks == 0 ) return true;
833 
834 	extensionDataHeader.mPresent = 1;
835 
836 	XMP_Uns64 blockStart = 0;
837 
838 	for ( int i = 0; i < playlistBlock.mNumberOfPlayListMarks; ++i ) {
839 		AVCCAM_blkProPlayListMark& currMark = playlistBlock.mPlayListMark;
840 
841 		mplFile.ReadAll ( &currMark.mProTagID, 1);
842 		mplFile.ReadAll ( &currMark.mFillItem1, 1);
843 		currMark.mLength = XIO::ReadUns16_BE ( &mplFile );
844 		blockStart = mplFile.Offset();
845 		mplFile.ReadAll ( &currMark.mMarkType, 1 );
846 
847 		if ( ( currMark.mProTagID == 0x40 ) && ( currMark.mMarkType == 0x01 ) ) {
848 			mplFile.ReadAll ( &currMark.mEntryMark.mGlobalClipID, 32);
849 
850 			// skip marks for different clips
851 			if ( i == playlistMarkID ) {
852 				playlistBlock.mPresent = 1;
853 				currMark.mPresent = 1;
854 				mplFile.ReadAll ( &currMark.mEntryMark.mStartTimeCode, 4);
855 				mplFile.ReadAll ( &currMark.mEntryMark.mStreamTimecodeInfo, 1);
856 				mplFile.ReadAll ( &currMark.mEntryMark.mStartBinaryGroup, 4);
857 				mplFile.ReadAll ( &currMark.mEntryMark.mLastUpdateTimeZone, 1);
858 				mplFile.ReadAll ( &currMark.mEntryMark.mLastUpdateDate, 7);
859 				mplFile.ReadAll ( &currMark.mEntryMark.mFillItem, 2);
860 
861 				XMP_Uns64 currPos = mplFile.Offset();
862 				XMP_Uns8 blockTag = 0;
863 				XMP_Uns8 blockFill;
864 				XMP_Uns16 blockLength = 0;
865 
866 				while ( currPos < ( blockStart + currMark.mLength ) ) {
867 					mplFile.ReadAll ( &blockTag, 1);
868 					mplFile.ReadAll ( &blockFill, 1);
869 					blockLength = XIO::ReadUns16_BE ( &mplFile );
870 					currPos += 4;
871 
872 					switch ( blockTag ) {
873 						case 0x20:
874 							if ( ! ReadAVCCAM_blkPRO_ShotMark ( mplFile, currMark ) ) return false;
875 							break;
876 
877 
878 						case 0x21:
879 							if ( ! ReadAVCCAM_blkPRO_Access ( mplFile, currMark ) ) return false;
880 							break;
881 
882 						case 0x22:
883 							if ( ! ReadAVCCAM_blkPRO_Device ( mplFile, currMark ) ) return false;
884 							break;
885 
886 						case 0x23:
887 							if ( ! ReadAVCCAM_blkPRO_Shoot ( mplFile, currMark ) ) return false;
888 							break;
889 
890 						case 0x24:
891 							if (! ReadAVCCAM_blkPRO_Location ( mplFile, currMark ) ) return false;
892 							break;
893 
894 						default : break;
895 					}
896 
897 					currPos += blockLength;
898 					mplFile.Seek ( currPos, kXMP_SeekFromStart );
899 				}
900 			}
901 		}
902 
903 		mplFile.Seek ( blockStart + currMark.mLength, kXMP_SeekFromStart );
904 	}
905 
906 	return true;
907 }
908 
909 // =================================================================================================
910 // ReadAVCCAMMakersPrivateData
911 // ===========================
912 //
913 // Read Panasonic's implementation of an AVCCAM "Maker's Private Data" block. Panasonic calls their
914 // extensions "AVCCAM."
915 
ReadAVCCAMMakersPrivateData(XMPFiles_IO & fileRef,XMP_Uns16 playlistMarkID,AVCHD_blkPanasonicPrivateData & avccamPrivateData)916 static bool ReadAVCCAMMakersPrivateData ( XMPFiles_IO & fileRef,
917 										  XMP_Uns16 playlistMarkID,
918 										  AVCHD_blkPanasonicPrivateData& avccamPrivateData )
919 {
920 	/*const XMP_Uns64 blockStart =*/ fileRef.Offset();
921 
922 	avccamPrivateData.mNumberOfData = XIO::ReadUns16_BE ( &fileRef );
923 	fileRef.ReadAll ( &avccamPrivateData.mReserved, 2 );
924 
925 	for (int i = 0; i < avccamPrivateData.mNumberOfData; ++i) {
926 		const XMP_Uns8 tagID = XIO::ReadUns8 ( &fileRef );
927 
928 		switch ( tagID ) {
929 			case 0xe0:	ReadAVCCAMProMetaID ( fileRef, tagID, avccamPrivateData );
930 				break;
931 			case 0xe2:	ReadAVCCAMProClipInfo( fileRef, tagID, avccamPrivateData );
932 				break;
933 			case 0xf0:	ReadAVCCAMProPlaylistInfo( fileRef, tagID, playlistMarkID, avccamPrivateData );
934 				break;
935 
936 			default:
937 				// Ignore any blocks we don't now or care about
938 				break;
939 		}
940 	}
941 
942 	return true;
943 }
944 
945 // =================================================================================================
946 // ReadAVCHDMakersPrivateData
947 // ==========================
948 //
949 // AVCHD Format. Book 2: Recording Extension Specifications, section 4.2.4.2.
950 
ReadAVCHDMakersPrivateData(XMPFiles_IO & mplFile,XMP_Uns16 playlistMarkID,AVCHD_blkMakersPrivateData & avchdLegacyData)951 static bool ReadAVCHDMakersPrivateData ( XMPFiles_IO & mplFile,
952 										 XMP_Uns16 playlistMarkID,
953 										 AVCHD_blkMakersPrivateData& avchdLegacyData )
954 {
955 	const XMP_Uns64 blockStart = mplFile.Offset();
956 
957 	avchdLegacyData.mLength = XIO::ReadUns32_BE ( &mplFile );
958 
959 	if ( avchdLegacyData.mLength == 0 ) return false;
960 
961 	avchdLegacyData.mPresent = 1;
962 	avchdLegacyData.mDataBlockStartAddress = XIO::ReadUns32_BE ( &mplFile );
963 	mplFile.ReadAll ( &avchdLegacyData.mReserved, 3 );
964 	mplFile.ReadAll ( &avchdLegacyData.mNumberOfMakerEntries, 1 );
965 
966 	if ( avchdLegacyData.mNumberOfMakerEntries == 0 ) return true;
967 
968 	XMP_Uns16 makerID;
969 	XMP_Uns16 makerModelCode;
970 	XMP_Uns32 mpdStartAddress;
971 
972 	for ( int i = 0; i < avchdLegacyData.mNumberOfMakerEntries; ++i ) {
973 		makerID = XIO::ReadUns16_BE ( &mplFile );
974 		makerModelCode = XIO::ReadUns16_BE ( &mplFile );
975 		mpdStartAddress = XIO::ReadUns32_BE ( &mplFile );
976 		// Unused bu we must read it.
977 		/*mpdLength =*/ XIO::ReadUns32_BE ( &mplFile );
978 
979 		// We only have documentation for Panasonic's Maker's Private Data blocks, so we'll ignore everyone else's
980 		if ( makerID == kMakerIDPanasonic ) {
981 			avchdLegacyData.mMakerID = makerID;
982 			avchdLegacyData.mMakerModelCode = makerModelCode;
983 			mplFile.Seek ( blockStart + mpdStartAddress, kXMP_SeekFromStart );
984 
985 			if (! ReadAVCCAMMakersPrivateData ( mplFile, playlistMarkID, avchdLegacyData.mPanasonicPrivateData ) ) return false;
986 		}
987 	}
988 
989 	return true;
990 }
991 
992 // =================================================================================================
993 // ReadAVCHDClipExtensionData
994 // ==========================
995 
ReadAVCHDClipExtensionData(XMPFiles_IO & cpiFile,AVCHD_blkClipExtensionData & avchdExtensionData)996 static bool ReadAVCHDClipExtensionData ( XMPFiles_IO & cpiFile, AVCHD_blkClipExtensionData& avchdExtensionData )
997 {
998 	const XMP_Int64 extensionBlockStart = cpiFile.Offset();
999 	AVCHD_blkExtensionData extensionDataHeader;
1000 
1001 	if ( ! ReadAVCHDExtensionData ( cpiFile, extensionDataHeader ) ) {
1002 		 return false;
1003 	}
1004 
1005 	if ( extensionDataHeader.mLength == 0 ) {
1006 		return true;
1007 	}
1008 
1009 	const XMP_Int64 dataBlockStart = extensionBlockStart + extensionDataHeader.mDataBlockStartAddress;
1010 
1011 	cpiFile.Seek ( dataBlockStart, kXMP_SeekFromStart );
1012 	cpiFile.ReadAll ( avchdExtensionData.mTypeIndicator, 4 );
1013 
1014 	if ( strncmp ( reinterpret_cast<const char*>( avchdExtensionData.mTypeIndicator ), "CLEX", 4 ) != 0 ) return false;
1015 
1016 	avchdExtensionData.mPresent = 1;
1017 	cpiFile.ReadAll ( avchdExtensionData.mReserved1, 4 );
1018 	avchdExtensionData.mProgramInfoExtStartAddress = XIO::ReadUns32_BE ( &cpiFile );
1019 	avchdExtensionData.mMakersPrivateDataStartAddress = XIO::ReadUns32_BE ( &cpiFile );
1020 
1021 	// read Clip info extension
1022 	cpiFile.Seek ( dataBlockStart + 40, kXMP_SeekFromStart );
1023 	avchdExtensionData.mClipInfoExt.mLength = XIO::ReadUns32_BE ( &cpiFile );
1024 	avchdExtensionData.mClipInfoExt.mMakerID = XIO::ReadUns16_BE ( &cpiFile );
1025 	avchdExtensionData.mClipInfoExt.mMakerModelCode = XIO::ReadUns16_BE ( &cpiFile );
1026 
1027 	if ( avchdExtensionData.mMakersPrivateDataStartAddress == 0 )  return true;
1028 
1029 	if ( avchdExtensionData.mClipInfoExt.mMakerID == kMakerIDPanasonic ) {
1030 		// Read Maker's Private Data block -- we only have Panasonic's definition for their AVCCAM models
1031 		// at this point, so we'll ignore the block if its from a different manufacturer.
1032 		cpiFile.Seek ( dataBlockStart + avchdExtensionData.mMakersPrivateDataStartAddress, kXMP_SeekFromStart );
1033 
1034 		if ( ! ReadAVCHDMakersPrivateData ( cpiFile, 0, avchdExtensionData.mMakersPrivateData ) ) {
1035 			return false;
1036 		}
1037 	}
1038 
1039 	return true;
1040 }
1041 
1042 // =================================================================================================
1043 // AVCHD_PlaylistContainsClip
1044 // ==========================
1045 //
1046 // Returns true of the specified AVCHD playlist block references the specified clip, or false if not.
1047 
AVCHD_PlaylistContainsClip(XMPFiles_IO & mplFile,XMP_Uns16 & playItemID,const std::string & strClipName)1048 static bool AVCHD_PlaylistContainsClip ( XMPFiles_IO & mplFile, XMP_Uns16& playItemID, const std::string& strClipName )
1049 {
1050 	// Read clip header. ( AVCHD Format. Book1. v. 1.01. p 45 )
1051 	struct AVCHD_blkPlayList
1052 	{
1053 		XMP_Uns32	mLength;
1054 		XMP_Uns16	mReserved;
1055 		XMP_Uns16	mNumberOfPlayItems;
1056 		XMP_Uns16	mNumberOfSubPaths;
1057 	};
1058 
1059 	AVCHD_blkPlayList blkPlayList;
1060 	blkPlayList.mLength = XIO::ReadUns32_BE ( &mplFile );
1061 	mplFile.ReadAll ( &blkPlayList.mReserved, 2 );
1062 	blkPlayList.mNumberOfPlayItems = XIO::ReadUns16_BE ( &mplFile );
1063 	blkPlayList.mNumberOfSubPaths = XIO::ReadUns16_BE ( &mplFile );
1064 
1065 	// Search the play items. ( AVCHD Format. Book1. v. 1.01. p 47 )
1066 	struct AVCHD_blkPlayItem
1067 	{
1068 		XMP_Uns16	mLength;
1069 		char		mClipInformationFileName[5];
1070 		// Note: remaining fields omitted because we don't care about them
1071 	};
1072 
1073 	AVCHD_blkPlayItem currPlayItem;
1074 	XMP_Uns64 blockStart = 0;
1075 	for ( playItemID = 0; playItemID < blkPlayList.mNumberOfPlayItems; ++playItemID ) {
1076 		currPlayItem.mLength = XIO::ReadUns16_BE ( &mplFile );
1077 
1078 		// mLength is measured from the end of mLength, not the start of the block ( AVCHD Format. Book1. v. 1.01. p 47 )
1079 		blockStart = mplFile.Offset();
1080 		mplFile.ReadAll ( currPlayItem.mClipInformationFileName, 5 );
1081 
1082 		if ( strncmp ( strClipName.c_str(), currPlayItem.mClipInformationFileName, 5 ) == 0 ) return true;
1083 
1084 		mplFile.Seek ( blockStart + currPlayItem.mLength, kXMP_SeekFromStart );
1085 	}
1086 
1087 	return false;
1088 }
1089 
1090 // =================================================================================================
1091 // ReadAVCHDPlaylistMetadataBlock
1092 // ==============================
1093 
ReadAVCHDPlaylistMetadataBlock(XMPFiles_IO & mplFile,AVCHD_blkPlaylistMeta & avchdLegacyData)1094 static bool ReadAVCHDPlaylistMetadataBlock ( XMPFiles_IO & mplFile,
1095 											 AVCHD_blkPlaylistMeta& avchdLegacyData )
1096 {
1097 	avchdLegacyData.mLength = XIO::ReadUns32_BE ( &mplFile );
1098 
1099 	if ( avchdLegacyData.mLength < sizeof ( AVCHD_blkPlaylistMeta ) ) return false;
1100 
1101 	avchdLegacyData.mMakerID = XIO::ReadUns16_BE ( &mplFile );
1102 	avchdLegacyData.mMakerModelCode = XIO::ReadUns16_BE ( &mplFile );
1103 	mplFile.ReadAll ( &avchdLegacyData.mReserved1, 4 );
1104 	avchdLegacyData.mRefToMenuThumbnailIndex = XIO::ReadUns16_BE ( &mplFile );
1105 	mplFile.ReadAll ( &avchdLegacyData.mBlkTimezone, 1 );
1106 	mplFile.ReadAll ( &avchdLegacyData.mRecordDataAndTime, 7 );
1107 	mplFile.ReadAll ( &avchdLegacyData.mReserved2, 1 );
1108 	mplFile.ReadAll ( &avchdLegacyData.mPlaylistCharacterSet, 1 );
1109 	mplFile.ReadAll ( &avchdLegacyData.mPlaylistNameLength, 1 );
1110 	mplFile.ReadAll ( &avchdLegacyData.mPlaylistName, avchdLegacyData.mPlaylistNameLength );
1111 
1112 	return true;
1113 }
1114 
1115 // =================================================================================================
1116 // ReadAVCHDPlaylistMarkExtension
1117 // ==============================
1118 
ReadAVCHDPlaylistMarkExtension(XMPFiles_IO & mplFile,XMP_Uns16 playlistMarkID,AVCHD_blkPlayListMarkExt & avchdLegacyData)1119 static bool ReadAVCHDPlaylistMarkExtension ( XMPFiles_IO & mplFile,
1120 											 XMP_Uns16 playlistMarkID,
1121 											 AVCHD_blkPlayListMarkExt& avchdLegacyData )
1122 {
1123 	avchdLegacyData.mLength = XIO::ReadUns32_BE ( &mplFile );
1124 
1125 	if ( avchdLegacyData.mLength == 0 ) return false;
1126 
1127 	avchdLegacyData.mNumberOfPlaylistMarks = XIO::ReadUns16_BE ( &mplFile );
1128 
1129 	if ( avchdLegacyData.mNumberOfPlaylistMarks <= playlistMarkID ) return true;
1130 
1131 	// Number of bytes in blkMarkExtension, AVCHD Book 2, section 4.3.3.1
1132 	const XMP_Uns64 markExtensionSize = 66;
1133 
1134 	// Entries in the mark extension block correspond one-to-one with entries in
1135 	// blkPlaylistMark, so we'll only read the one that corresponds to the
1136 	// chosen clip.
1137 	const XMP_Uns64 markOffset = markExtensionSize * playlistMarkID;
1138 
1139 	avchdLegacyData.mPresent = 1;
1140 	mplFile.Seek ( markOffset, kXMP_SeekFromCurrent );
1141 	avchdLegacyData.mMakerID = XIO::ReadUns16_BE ( &mplFile );
1142 	avchdLegacyData.mMakerModelCode = XIO::ReadUns16_BE ( &mplFile );
1143 	mplFile.ReadAll ( &avchdLegacyData.mReserved1, 3 );
1144 	mplFile.ReadAll ( &avchdLegacyData.mFlags, 1 );
1145 	avchdLegacyData.mRefToMarkThumbnailIndex = XIO::ReadUns16_BE ( &mplFile );
1146 	mplFile.ReadAll ( &avchdLegacyData.mBlkTimezone, 1 );
1147 	mplFile.ReadAll ( &avchdLegacyData.mRecordDataAndTime, 7 );
1148 	mplFile.ReadAll ( &avchdLegacyData.mMarkCharacterSet, 1 );
1149 	mplFile.ReadAll ( &avchdLegacyData.mMarkNameLength, 1 );
1150 	mplFile.ReadAll ( &avchdLegacyData.mMarkName, 24 );
1151 	mplFile.ReadAll ( &avchdLegacyData.mMakersInformation, 16 );
1152 	mplFile.ReadAll ( &avchdLegacyData.mBlkTimecode, 4 );
1153 	mplFile.ReadAll ( &avchdLegacyData.mReserved2, 2 );
1154 
1155 	return true;
1156 }
1157 
1158 // =================================================================================================
1159 // ReadAVCHDPlaylistMarkID
1160 // =======================
1161 //
1162 // Read the playlist mark block to find the ID of the playlist mark that matches the specified
1163 // playlist item.
1164 
ReadAVCHDPlaylistMarkID(XMPFiles_IO & mplFile,XMP_Uns16 playItemID,XMP_Uns16 & markID)1165 static bool ReadAVCHDPlaylistMarkID ( XMPFiles_IO & mplFile,
1166 									  XMP_Uns16 playItemID,
1167 									  XMP_Uns16& markID )
1168 {
1169 	XMP_Uns32 length = XIO::ReadUns32_BE ( &mplFile );
1170 	XMP_Uns16 numberOfPlayListMarks = XIO::ReadUns16_BE ( &mplFile );
1171 
1172 	if ( length == 0 ) return false;
1173 
1174 	XMP_Uns8 reserved;
1175 	XMP_Uns8 markType;
1176 	XMP_Uns16 refToPlayItemID;
1177 
1178 	for ( int i = 0; i < numberOfPlayListMarks; ++i ) {
1179 		mplFile.ReadAll ( &reserved, 1 );
1180 		mplFile.ReadAll ( &markType, 1 );
1181 		refToPlayItemID = XIO::ReadUns16_BE ( &mplFile );
1182 
1183 		if ( ( markType == 0x01 ) && ( refToPlayItemID == playItemID ) ) {
1184 			markID = i;
1185 			return true;
1186 		}
1187 
1188 		mplFile.Seek ( 10, kXMP_SeekFromCurrent );
1189 	}
1190 
1191 	return false;
1192 }
1193 
1194 // =================================================================================================
1195 // ReadAVCHDPlaylistExtensionData
1196 // ==============================
1197 
ReadAVCHDPlaylistExtensionData(XMPFiles_IO & mplFile,AVCHD_LegacyMetadata & avchdLegacyData,XMP_Uns16 playlistMarkID)1198 static bool ReadAVCHDPlaylistExtensionData ( XMPFiles_IO & mplFile,
1199 											 AVCHD_LegacyMetadata& avchdLegacyData,
1200 											 XMP_Uns16 playlistMarkID )
1201 {
1202 	const XMP_Int64 extensionBlockStart = mplFile.Offset();
1203 	AVCHD_blkExtensionData extensionDataHeader;
1204 
1205 	if ( ! ReadAVCHDExtensionData ( mplFile, extensionDataHeader ) ) {
1206 		 return false;
1207 	}
1208 
1209 	if ( extensionDataHeader.mLength == 0 ) {
1210 		return true;
1211 	}
1212 
1213 	const XMP_Int64 dataBlockStart = extensionBlockStart + extensionDataHeader.mDataBlockStartAddress;
1214 	AVCHD_blkPlayListExtensionData& extensionData = avchdLegacyData.mPlaylistExtensionData;
1215 	const int reserved2Len = 24;
1216 
1217 	mplFile.Seek ( dataBlockStart, kXMP_SeekFromStart );
1218 	mplFile.ReadAll ( extensionData.mTypeIndicator, 4 );
1219 
1220 	if ( strncmp ( extensionData.mTypeIndicator, "PLEX", 4 ) != 0 ) return false;
1221 
1222 	extensionData.mPresent = true;
1223 	mplFile.ReadAll ( extensionData.mReserved, 4 );
1224 	extensionData.mPlayListMarkExtStartAddress = XIO::ReadUns32_BE ( &mplFile );
1225 	extensionData.mMakersPrivateDataStartAddress = XIO::ReadUns32_BE ( &mplFile );
1226 	mplFile.Seek ( reserved2Len, kXMP_SeekFromCurrent );
1227 
1228 	if ( ! ReadAVCHDPlaylistMetadataBlock ( mplFile, extensionData.mPlaylistMeta ) ) return false;
1229 
1230 	mplFile.Seek ( dataBlockStart + extensionData.mPlayListMarkExtStartAddress, kXMP_SeekFromStart );
1231 
1232 	if ( ! ReadAVCHDPlaylistMarkExtension ( mplFile, playlistMarkID, extensionData.mPlaylistMarkExt ) ) return false;
1233 
1234 	if ( extensionData.mMakersPrivateDataStartAddress > 0 ) {
1235 
1236 		// return true here because all the data is already read successfully except the maker's private data and more
1237 		// specifically of panasonic. So if the relevant panasonic data is not present we just skip it.
1238 		// Assumption here is that if its not present in ClipExtension then it will not be in Playlist extension
1239 		if ( ! avchdLegacyData.mClipExtensionData.mMakersPrivateData.mPanasonicPrivateData.mPresent ) return true;
1240 
1241 		mplFile.Seek ( dataBlockStart + extensionData.mMakersPrivateDataStartAddress, kXMP_SeekFromStart );
1242 
1243 		// Here private data was found.If the data was panasonic private data and we were unable to read it ,
1244 		// Return false
1245 		if ( ! ReadAVCHDMakersPrivateData ( mplFile, playlistMarkID, extensionData.mMakersPrivateData ) ) return false;
1246 
1247 	}
1248 
1249 	return true;
1250 }
1251 
1252 // =================================================================================================
1253 // ReadAVCHDLegacyClipFile
1254 // =======================
1255 //
1256 // Read the legacy metadata stored in an AVCHD .CPI file.
1257 
ReadAVCHDLegacyClipFile(const std::string & strPath,AVCHD_LegacyMetadata & avchdLegacyData)1258 static bool ReadAVCHDLegacyClipFile ( const std::string& strPath, AVCHD_LegacyMetadata& avchdLegacyData )
1259 {
1260 	bool success = false;
1261 
1262 	try {
1263 
1264 		Host_IO::FileRef hostRef = Host_IO::Open ( strPath.c_str(), Host_IO::openReadOnly );
1265 		if ( hostRef == Host_IO::noFileRef ) return false;	// The open failed.
1266 		XMPFiles_IO cpiFile ( hostRef, strPath.c_str(), Host_IO::openReadOnly );
1267 
1268 		memset ( &avchdLegacyData, 0, sizeof(AVCHD_LegacyMetadata) );
1269 
1270 		// Read clip header. ( AVCHD Format. Book1. v. 1.01. p 64 )
1271 		struct AVCHD_ClipInfoHeader
1272 		{
1273 			char		mTypeIndicator[4];
1274 			char		mTypeIndicator2[4];
1275 			XMP_Uns32	mSequenceInfoStartAddress;
1276 			XMP_Uns32	mProgramInfoStartAddress;
1277 			XMP_Uns32	mCPIStartAddress;
1278 			XMP_Uns32	mClipMarkStartAddress;
1279 			XMP_Uns32	mExtensionDataStartAddress;
1280 			XMP_Uns8	mReserved[12];
1281 		};
1282 
1283 		// Read the AVCHD header.
1284 		AVCHD_ClipInfoHeader avchdHeader;
1285 		cpiFile.ReadAll ( avchdHeader.mTypeIndicator,  4 );
1286 		cpiFile.ReadAll ( avchdHeader.mTypeIndicator2, 4 );
1287 
1288 		if ( strncmp ( avchdHeader.mTypeIndicator, "HDMV", 4 ) != 0 ) return false;
1289 		if ( strncmp ( avchdHeader.mTypeIndicator2, "0100", 4 ) != 0 ) return false;
1290 
1291 		avchdHeader.mSequenceInfoStartAddress	= XIO::ReadUns32_BE ( &cpiFile );
1292 		avchdHeader.mProgramInfoStartAddress	= XIO::ReadUns32_BE ( &cpiFile );
1293 		avchdHeader.mCPIStartAddress			= XIO::ReadUns32_BE ( &cpiFile );
1294 		avchdHeader.mClipMarkStartAddress		= XIO::ReadUns32_BE ( &cpiFile );
1295 		avchdHeader.mExtensionDataStartAddress	= XIO::ReadUns32_BE ( &cpiFile );
1296 		cpiFile.ReadAll ( avchdHeader.mReserved, 12 );
1297 
1298 		// Seek to the program header. (AVCHD Format. Book1. v. 1.01. p 77 )
1299 		cpiFile.Seek ( avchdHeader.mProgramInfoStartAddress, kXMP_SeekFromStart );
1300 
1301 		// Read the program info block
1302 		success = ReadAVCHDProgramInfo ( cpiFile, avchdLegacyData.mProgramInfo );
1303 
1304 		if ( success && ( avchdHeader.mExtensionDataStartAddress != 0 ) ) {
1305 			// Seek to the program header. (AVCHD Format. Book1. v. 1.01. p 77 )
1306 			cpiFile.Seek ( avchdHeader.mExtensionDataStartAddress, kXMP_SeekFromStart );
1307 			success = ReadAVCHDClipExtensionData ( cpiFile, avchdLegacyData.mClipExtensionData );
1308 		}
1309 
1310 	} catch ( ... ) {
1311 
1312 		return false;
1313 
1314 	}
1315 
1316 	return success;
1317 
1318 }	// ReadAVCHDLegacyClipFile
1319 
1320 // =================================================================================================
1321 // ReadAVCHDLegacyPlaylistFile
1322 // ===========================
1323 //
1324 // Read the legacy metadata stored in an AVCHD .MPL file.
1325 
ReadAVCHDLegacyPlaylistFile(const std::string & mplPath,const std::string & strClipName,AVCHD_LegacyMetadata & avchdLegacyData)1326 static bool ReadAVCHDLegacyPlaylistFile ( const std::string& mplPath,
1327 										  const std::string& strClipName,
1328 										  AVCHD_LegacyMetadata& avchdLegacyData )
1329 {
1330 
1331 #if 1
1332 	bool success = false;
1333 
1334 	try {
1335 
1336 		Host_IO::FileRef hostRef = Host_IO::Open ( mplPath.c_str(), Host_IO::openReadOnly );
1337 		if ( hostRef == Host_IO::noFileRef ) return false;	// The open failed.
1338 		XMPFiles_IO mplFile ( hostRef, mplPath.c_str(), Host_IO::openReadOnly );
1339 
1340 		// Read playlist header. ( AVCHD Format. Book1. v. 1.01. p 43 )
1341 		struct AVCHD_PlaylistFileHeader {
1342 			char		mTypeIndicator[4];
1343 			char		mTypeIndicator2[4];
1344 			XMP_Uns32	mPlaylistStartAddress;
1345 			XMP_Uns32	mPlaylistMarkStartAddress;
1346 			XMP_Uns32	mExtensionDataStartAddress;
1347 		};
1348 
1349 		// Read the AVCHD playlist file header.
1350 		AVCHD_PlaylistFileHeader avchdHeader;
1351 		mplFile.ReadAll ( avchdHeader.mTypeIndicator,  4 );
1352 		mplFile.ReadAll ( avchdHeader.mTypeIndicator2,  4 );
1353 
1354 		if ( strncmp ( avchdHeader.mTypeIndicator, "MPLS", 4 ) != 0 ) return false;
1355 		if ( strncmp ( avchdHeader.mTypeIndicator2, "0100", 4 ) != 0 ) return false;
1356 
1357 		avchdHeader.mPlaylistStartAddress		= XIO::ReadUns32_BE ( &mplFile );
1358 		avchdHeader.mPlaylistMarkStartAddress	= XIO::ReadUns32_BE ( &mplFile );
1359 		avchdHeader.mExtensionDataStartAddress	= XIO::ReadUns32_BE ( &mplFile );
1360 
1361 		if ( avchdHeader.mExtensionDataStartAddress == 0 ) return false;
1362 
1363 		// Seek to the start of the Playlist block. (AVCHD Format. Book1. v. 1.01. p 45 )
1364 		mplFile.Seek ( avchdHeader.mPlaylistStartAddress, kXMP_SeekFromStart );
1365 
1366 		XMP_Uns16 playItemID = 0xFFFF;
1367 		XMP_Uns16 playlistMarkID = 0xFFFF;
1368 
1369 		if ( AVCHD_PlaylistContainsClip ( mplFile, playItemID, strClipName ) ) {
1370 			mplFile.Seek ( avchdHeader.mPlaylistMarkStartAddress, kXMP_SeekFromStart );
1371 			if ( ! ReadAVCHDPlaylistMarkID ( mplFile, playItemID, playlistMarkID ) ) return false;
1372 			mplFile.Seek ( avchdHeader.mExtensionDataStartAddress, kXMP_SeekFromStart );
1373 			success = ReadAVCHDPlaylistExtensionData ( mplFile, avchdLegacyData, playlistMarkID );
1374 		}
1375 
1376 	} catch ( ... ) {
1377 
1378 		success = false;
1379 
1380 	}
1381 
1382 	return success;
1383 
1384 #else
1385 
1386 	bool success = false;
1387 	std::string mplPath;
1388 	char playlistName [10];
1389 	const int rootPlaylistNum = atoi(strClipName.c_str());
1390 
1391 	// Find the corresponding .MPL file -- because of clip spanning the .MPL name may not match the .CPI name for
1392 	// a given clip -- we need to open .MPL files and look for one that contains a reference to the clip name. To speed
1393 	// up the search we'll start with the playlist with the same number/name as the clip and search backwards. Assuming
1394 	// this directory was generated by a camera, the clip numbers will increase sequentially across the playlist files,
1395 	// though one playlist file may reference more than one clip.
1396 	for ( int i = rootPlaylistNum; i >= 0; --i ) {
1397 
1398 		sprintf ( playlistName, "%05d", i );
1399 
1400 		if ( MakeLeafPath ( &mplPath, strRootPath.c_str(), "PLAYLIST", playlistName, ".mpl", true /* checkFile */ ) ) {
1401 
1402 			try {
1403 
1404 				Host_IO::FileRef hostRef = Host_IO::Open ( mplPath.c_str(), Host_IO::openReadOnly );
1405 				if ( hostRef == Host_IO::noFileRef ) return false;	// The open failed.
1406 				XMPFiles_IO mplFile ( hostRef, mplPath.c_str(), Host_IO::openReadOnly );
1407 
1408 				// Read playlist header. ( AVCHD Format. Book1. v. 1.01. p 43 )
1409 				struct AVCHD_PlaylistFileHeader
1410 				{
1411 					char		mTypeIndicator[4];
1412 					char		mTypeIndicator2[4];
1413 					XMP_Uns32	mPlaylistStartAddress;
1414 					XMP_Uns32	mPlaylistMarkStartAddress;
1415 					XMP_Uns32	mExtensionDataStartAddress;
1416 				};
1417 
1418 				// Read the AVCHD playlist file header.
1419 				AVCHD_PlaylistFileHeader avchdHeader;
1420 				mplFile.ReadAll ( avchdHeader.mTypeIndicator,  4 );
1421 				mplFile.ReadAll ( avchdHeader.mTypeIndicator2, 4 );
1422 
1423 				if ( strncmp ( avchdHeader.mTypeIndicator, "MPLS", 4 ) != 0 ) return false;
1424 				if ( strncmp ( avchdHeader.mTypeIndicator2, "0100", 4 ) != 0 ) return false;
1425 
1426 				avchdHeader.mPlaylistStartAddress		= XIO::ReadUns32_BE ( &mplFile );
1427 				avchdHeader.mPlaylistMarkStartAddress	= XIO::ReadUns32_BE ( &mplFile );
1428 				avchdHeader.mExtensionDataStartAddress	= XIO::ReadUns32_BE ( &mplFile );
1429 
1430 				if ( avchdHeader.mExtensionDataStartAddress == 0 ) return false;
1431 
1432 				// Seek to the start of the Playlist block. (AVCHD Format. Book1. v. 1.01. p 45 )
1433 				mplFile.Seek ( avchdHeader.mPlaylistStartAddress, kXMP_SeekFromStart );
1434 
1435 				XMP_Uns16 playItemID = 0xFFFF;
1436 				XMP_Uns16 playlistMarkID = 0xFFFF;
1437 
1438 				if ( AVCHD_PlaylistContainsClip ( mplFile, playItemID, strClipName ) ) {
1439 					mplFile.Seek ( avchdHeader.mPlaylistMarkStartAddress, kXMP_SeekFromStart );
1440 
1441 					if ( ! ReadAVCHDPlaylistMarkID ( mplFile, playItemID, playlistMarkID ) ) return false;
1442 
1443 					mplFile.Seek ( avchdHeader.mExtensionDataStartAddress, kXMP_SeekFromStart );
1444 					success = ReadAVCHDPlaylistExtensionData ( mplFile, avchdLegacyData, playlistMarkID );
1445 				}
1446 
1447 			} catch ( ... ) {
1448 
1449 				return false;
1450 
1451 			}
1452 		}
1453 
1454 	}
1455 
1456 	return success;
1457 
1458 #endif
1459 
1460 }	// ReadAVCHDLegacyPlaylistFile
1461 
1462 // =================================================================================================
1463 // FindAVCHDLegacyPlaylistFile
1464 // ===========================
1465 //
1466 // Find and read the legacy metadata stored in an AVCHD .MPL file.
1467 
FindAVCHDLegacyPlaylistFile(const std::string & strRootPath,const std::string & strClipName,AVCHD_LegacyMetadata & avchdLegacyData,std::string & mplPath)1468 static bool FindAVCHDLegacyPlaylistFile ( const std::string& strRootPath,
1469 										  const std::string& strClipName,
1470 										  AVCHD_LegacyMetadata& avchdLegacyData,
1471 										  std::string &mplPath )
1472 {
1473 	bool success = false;
1474 
1475 	// Find the corresponding .MPL file -- because of clip spanning the .MPL name may not match the
1476 	// .CPI name for a given clip -- we need to open .MPL files and look for one that contains a
1477 	// reference to the clip name. To speed up the search we'll start with the playlist with the
1478 	// same number/name as the clip, and if that fails look into other playlist files in the
1479 	// directory. One playlist file may reference more than one clip.
1480 
1481 	if ( MakeLeafPath ( &mplPath, strRootPath.c_str(), "PLAYLIST", strClipName.c_str(), ".mpl", true /* checkFile */ ) ) {
1482 		success = ReadAVCHDLegacyPlaylistFile ( mplPath, strClipName, avchdLegacyData );
1483 	}
1484 
1485 	if ( ! success ) {
1486 
1487 		std::string playlistPath = strRootPath;
1488 		playlistPath += kDirChar;
1489 		playlistPath += "BDMV";
1490 		playlistPath += kDirChar;
1491 		playlistPath += "PLAYLIST";
1492 		playlistPath += kDirChar;
1493 
1494 		std::string childName;
1495 
1496 		if ( Host_IO::GetFileMode ( playlistPath.c_str() ) == Host_IO::kFMode_IsFolder ) {
1497 
1498 			Host_IO::AutoFolder af;
1499 			af.folder = Host_IO::OpenFolder ( playlistPath.c_str() );
1500 			if ( af.folder == Host_IO::noFolderRef ) return false;
1501 
1502 			while ( (! success) && Host_IO::GetNextChild ( af.folder, &childName ) &&
1503 					(childName.find(".mpl") || childName.find(".MPL")) ) {
1504 				mplPath = playlistPath + childName;
1505 				if ( Host_IO::GetFileMode ( mplPath.c_str() ) == Host_IO::kFMode_IsFile ) {
1506 					success = ReadAVCHDLegacyPlaylistFile ( mplPath, strClipName, avchdLegacyData );
1507 				}
1508 
1509 			}
1510 
1511 			af.Close();
1512 
1513 		}
1514 
1515 	}
1516 
1517 	return success;
1518 
1519 }	// FindAVCHDLegacyPlaylistFile
1520 
1521 // =================================================================================================
1522 // ReadAVCHDLegacyMetadata
1523 // =======================
1524 //
1525 // Read the legacy metadata stored in an AVCHD .CPI file.
1526 
ReadAVCHDLegacyMetadata(const std::string & strPath,const std::string & strRootPath,const std::string & strClipName,AVCHD_LegacyMetadata & avchdLegacyData,std::string & mplFile)1527 static bool ReadAVCHDLegacyMetadata ( const std::string& strPath,
1528 									  const std::string& strRootPath,
1529 									  const std::string& strClipName,
1530 									  AVCHD_LegacyMetadata& avchdLegacyData,
1531 									  std::string&	mplFile)
1532 {
1533 	bool success = ReadAVCHDLegacyClipFile ( strPath, avchdLegacyData );
1534 
1535 	if ( success && avchdLegacyData.mClipExtensionData.mPresent ) {
1536 		success = FindAVCHDLegacyPlaylistFile ( strRootPath, strClipName, avchdLegacyData, mplFile );
1537 	}
1538 
1539 	return success;
1540 
1541 }	// ReadAVCHDLegacyMetadata
1542 
1543 // =================================================================================================
1544 // AVCCAM_SetXMPStartTimecode
1545 // ==========================
1546 
AVCCAM_SetXMPStartTimecode(SXMPMeta & xmpObj,const XMP_Uns8 * avccamTimecode,XMP_Uns8 avchdFrameRate)1547 static void AVCCAM_SetXMPStartTimecode ( SXMPMeta& xmpObj, const XMP_Uns8* avccamTimecode, XMP_Uns8 avchdFrameRate )
1548 {
1549 	//	Timecode in SMPTE 12M format, according to Panasonic's documentation
1550 	if ( *reinterpret_cast<const XMP_Uns32*>( avccamTimecode ) == 0xFFFFFFFF ) {
1551 		// 0xFFFFFFFF means timecode not specified
1552 		return;
1553 	}
1554 
1555 	/*const XMP_Uns8 isColor = ( avccamTimecode[0] >> 7 ) & 0x01;*/
1556 	const XMP_Uns8 isDropFrame = ( avccamTimecode[0] >> 6 ) & 0x01;
1557 	const XMP_Uns8 frameTens = ( avccamTimecode[0] >> 4 ) & 0x03;
1558 	const XMP_Uns8 frameUnits = avccamTimecode[0] & 0x0f;
1559 	const XMP_Uns8 secondTens = ( avccamTimecode[1] >> 4 ) & 0x07;
1560 	const XMP_Uns8 secondUnits = avccamTimecode[1] & 0x0f;
1561 	const XMP_Uns8 minuteTens = ( avccamTimecode[2] >> 4 ) & 0x07;
1562 	const XMP_Uns8 minuteUnits = avccamTimecode[2] & 0x0f;
1563 	const XMP_Uns8 hourTens = ( avccamTimecode[3] >> 4 ) & 0x03;
1564 	const XMP_Uns8 hourUnits = avccamTimecode[3] & 0x0f;
1565 	char tcSeparator = ':';
1566 	const char* dmTimeFormat = NULL;
1567 	const char* dmTimeScale = NULL;
1568 	const char* dmTimeSampleSize = NULL;
1569 
1570 	switch ( avchdFrameRate ) {
1571 		case 1 :
1572 			// 23.976i
1573 			dmTimeFormat = "23976Timecode";
1574 			dmTimeScale = "24000";
1575 			dmTimeSampleSize = "1001";
1576 			break;
1577 
1578 		case 2 :
1579 			// 24p
1580 			dmTimeFormat = "24Timecode";
1581 			dmTimeScale = "24";
1582 			dmTimeSampleSize = "1";
1583 			break;
1584 
1585 		case 3 :
1586 		case 6 :
1587 			// 50i or 25p
1588 			dmTimeFormat = "25Timecode";
1589 			dmTimeScale = "25";
1590 			dmTimeSampleSize = "1";
1591 			break;
1592 
1593 		case 4 :
1594 		case 7 :
1595 			// 29.97p or 59.94i
1596 			if ( isDropFrame ) {
1597 				dmTimeFormat = "2997DropTimecode";
1598 				tcSeparator = ';';
1599 			} else {
1600 				dmTimeFormat = "2997NonDropTimecode";
1601 			}
1602 
1603 			dmTimeScale = "30000";
1604 			dmTimeSampleSize = "1001";
1605 
1606 			break;
1607 	}
1608 
1609 	if ( dmTimeFormat != NULL ) {
1610 		char timecodeBuff [16];
1611 
1612 		sprintf ( timecodeBuff, "%d%d%c%d%d%c%d%d%c%d%d", hourTens, hourUnits, tcSeparator,
1613 			minuteTens, minuteUnits, tcSeparator, secondTens, secondUnits, tcSeparator, frameTens, frameUnits);
1614 
1615 		xmpObj.SetProperty( kXMP_NS_DM, "startTimeScale", dmTimeScale, kXMP_DeleteExisting );
1616 		xmpObj.SetProperty( kXMP_NS_DM, "startTimeSampleSize", dmTimeSampleSize, kXMP_DeleteExisting );
1617 		xmpObj.SetStructField ( kXMP_NS_DM, "startTimecode", kXMP_NS_DM, "timeValue", timecodeBuff, 0 );
1618 		xmpObj.SetStructField ( kXMP_NS_DM, "startTimecode", kXMP_NS_DM, "timeFormat", dmTimeFormat, 0 );
1619 	}
1620 }
1621 
1622 // =================================================================================================
1623 // AVCHD_SetXMPMakeAndModel
1624 // ========================
1625 
AVCHD_SetXMPMakeAndModel(SXMPMeta & xmpObj,const AVCHD_blkClipExtensionData & clipExtData)1626 static bool AVCHD_SetXMPMakeAndModel ( SXMPMeta& xmpObj, const AVCHD_blkClipExtensionData& clipExtData )
1627 {
1628 	if ( ! clipExtData.mPresent ) return false;
1629 
1630 	XMP_StringPtr xmpValue = 0;
1631 
1632 	// Set the Make. Use a hex string for unknown makes.
1633 	{
1634 		char hexMakeNumber [7];
1635 
1636 		switch ( clipExtData.mClipInfoExt.mMakerID ) {
1637 			case kMakerIDCanon : xmpValue = "Canon";			break;
1638 			case kMakerIDPanasonic : xmpValue = "Panasonic";	break;
1639 			case kMakerIDSony : xmpValue = "Sony";				break;
1640 			default :
1641 				std::sprintf ( hexMakeNumber, "0x%04x", clipExtData.mClipInfoExt.mMakerID );
1642 				xmpValue = hexMakeNumber;
1643 
1644 				break;
1645 		}
1646 
1647 		xmpObj.SetProperty ( kXMP_NS_TIFF, "Make", xmpValue, kXMP_DeleteExisting );
1648 	}
1649 
1650 	// Set the Model number. Use a hex string for unknown model numbers so they can still be distinguished.
1651 	{
1652 		char hexModelNumber [7];
1653 
1654 		xmpValue = 0;
1655 
1656 		switch ( clipExtData.mClipInfoExt.mMakerID  ) {
1657 			case kMakerIDCanon :
1658 				switch ( clipExtData.mClipInfoExt.mMakerModelCode ) {
1659 					case 0x1000 : xmpValue = "HR10";		break;
1660 					case 0x2000 : xmpValue = "HG10";		break;
1661 					case 0x2001 : xmpValue = "HG21";		break;
1662 					case 0x3000 : xmpValue = "HF100";		break;
1663 					case 0x3003 : xmpValue = "HF S10";		break;
1664 					default :								break;
1665 				}
1666 				break;
1667 
1668 			case kMakerIDPanasonic :
1669 				switch ( clipExtData.mClipInfoExt.mMakerModelCode ) {
1670 					case 0x0202 : xmpValue = "HD-writer";				break;
1671 					case 0x0400 : xmpValue = "AG-HSC1U";				break;
1672 					case 0x0401 : xmpValue = "AG-HMC70";				break;
1673 					case 0x0410 : xmpValue = "AG-HMC150";				break;
1674 					case 0x0411 : xmpValue = "AG-HMC40";				break;
1675 					case 0x0412 : xmpValue = "AG-HMC80";				break;
1676 					case 0x0413 : xmpValue = "AG-3DA1";					break;
1677 					case 0x0414 : xmpValue = "AG-AF100";				break;
1678 					case 0x0450 : xmpValue = "AG-HMR10";				break;
1679 					case 0x0451 : xmpValue = "AJ-YCX250";				break;
1680 					case 0x0452 : xmpValue = "AG-MDR15";				break;
1681 					case 0x0490 : xmpValue = "AVCCAM Restorer";			break;
1682 					case 0x0491 : xmpValue = "AVCCAM Viewer";			break;
1683 					case 0x0492 : xmpValue = "AVCCAM Viewer for Mac";	break;
1684 					default :											break;
1685 				}
1686 
1687 			break;
1688 
1689 			default : break;
1690 		}
1691 
1692 		if ( ( xmpValue == 0 ) && ( clipExtData.mClipInfoExt.mMakerID != kMakerIDSony ) ) {
1693 			// Panasonic has said that if we don't have a string for the model number, they'd like to see the code
1694 			// anyway. We'll do the same for every manufacturer except Sony, who have said that they use
1695 			// the same model number for multiple cameras.
1696 			std::sprintf ( hexModelNumber, "0x%04x", clipExtData.mClipInfoExt.mMakerModelCode );
1697 			xmpValue = hexModelNumber;
1698 		}
1699 
1700 		if ( xmpValue != 0 ) xmpObj.SetProperty ( kXMP_NS_TIFF, "Model", xmpValue, kXMP_DeleteExisting );
1701 	}
1702 
1703 	return true;
1704 }
1705 
1706 // =================================================================================================
1707 // AVCHD_StringFieldToXMP
1708 // ======================
1709 
AVCHD_StringFieldToXMP(XMP_Uns8 avchdLength,XMP_Uns8 avchdCharacterSet,const XMP_Uns8 * avchdField,XMP_Uns8 avchdFieldSize)1710 static std::string AVCHD_StringFieldToXMP ( XMP_Uns8 avchdLength,
1711 											XMP_Uns8 avchdCharacterSet,
1712 											const XMP_Uns8* avchdField,
1713 											XMP_Uns8 avchdFieldSize )
1714 {
1715 	std::string xmpString;
1716 
1717 	if ( avchdCharacterSet == 0x02 ) {
1718 		// UTF-16, Big Endian
1719 		UTF8Unit utf8Name [512];
1720 		const XMP_Uns8 avchdMaxChars = ( avchdFieldSize / 2);
1721 		size_t utf16Read;
1722 		size_t utf8Written;
1723 
1724 		// The spec doesn't say whether AVCHD length fields count bytes or characters, so we'll
1725 		// clamp to the max number of UTF-16 characters just in case.
1726 		const int stringLength = ( avchdLength > avchdMaxChars ) ? avchdMaxChars : avchdLength;
1727 
1728 		UTF16BE_to_UTF8 ( reinterpret_cast<const UTF16Unit*> ( avchdField ), stringLength,
1729 						  utf8Name, 512, &utf16Read, &utf8Written );
1730 		xmpString.assign ( reinterpret_cast<const char*> ( utf8Name ), utf8Written );
1731 	} else {
1732 		// AVCHD supports many character encodings, but UTF-8 (0x01) and ASCII (0x90) are the only ones I've
1733 		// seen in the wild at this point. We'll treat the other character sets as UTF-8 on the assumption that
1734 		// at least a few characters will come across, and something is better than nothing.
1735 		const int stringLength = ( avchdLength > avchdFieldSize ) ? avchdFieldSize : avchdLength;
1736 
1737 		xmpString.assign ( reinterpret_cast<const char*> ( avchdField ), stringLength );
1738 	}
1739 
1740 	return xmpString;
1741 }
1742 
1743 // =================================================================================================
1744 // AVCHD_SetXMPShotName
1745 // ====================
1746 
AVCHD_SetXMPShotName(SXMPMeta & xmpObj,const AVCHD_blkPlayListMarkExt & markExt,const std::string &)1747 static void AVCHD_SetXMPShotName ( SXMPMeta& xmpObj, const AVCHD_blkPlayListMarkExt& markExt, const std::string& /*strClipName*/ )
1748 {
1749 	if ( markExt.mPresent ) {
1750 		const std::string shotName = AVCHD_StringFieldToXMP ( markExt.mMarkNameLength, markExt.mMarkCharacterSet, markExt.mMarkName, 24 );
1751 
1752 		if ( ! shotName.empty() ) xmpObj.SetProperty ( kXMP_NS_DC, "shotName", shotName.c_str(), kXMP_DeleteExisting );
1753 	}
1754 }
1755 
1756 // =================================================================================================
1757 // BytesToHex
1758 // ==========
1759 
1760 #define kHexDigits "0123456789ABCDEF"
1761 
BytesToHex(const XMP_Uns8 * inClipIDBytes,int inNumBytes)1762 static std::string BytesToHex ( const XMP_Uns8* inClipIDBytes, int inNumBytes )
1763 {
1764 	const int numChars = ( inNumBytes * 2 );
1765 	std::string hexStr;
1766 
1767 	hexStr.reserve(numChars);
1768 
1769 	for ( int i = 0; i < inNumBytes; ++i ) {
1770 		const XMP_Uns8 byte = inClipIDBytes[i];
1771 		hexStr.push_back ( kHexDigits [byte >> 4] );
1772 		hexStr.push_back ( kHexDigits [byte & 0xF] );
1773 	}
1774 
1775 	return hexStr;
1776 }
1777 
1778 // =================================================================================================
1779 // AVCHD_DateFieldToXMP
1780 // ====================
1781 //
1782 // AVCHD Format Book 2, section 4.2.2.2.
1783 
AVCHD_DateFieldToXMP(XMP_Uns8 avchdTimeZone,const XMP_Uns8 * avchdDateTime)1784 static std::string AVCHD_DateFieldToXMP ( XMP_Uns8 avchdTimeZone, const XMP_Uns8* avchdDateTime )
1785 {
1786 	/* const XMP_Uns8 daylightSavingsTime = ( avchdTimeZone >> 6 ) & 0x01; */
1787 	const XMP_Uns8 timezoneSign = ( avchdTimeZone >> 5 ) & 0x01;
1788 	const XMP_Uns8 timezoneValue = ( avchdTimeZone >> 1 ) & 0x0F;
1789 	const XMP_Uns8 halfHourFlag = avchdTimeZone & 0x01;
1790 	int utcOffsetHours = 0;
1791 	unsigned int utcOffsetMinutes = 0;
1792 
1793 	// It's not entirely clear how to interpret the daylightSavingsTime flag from the documentation -- my best
1794 	// guess is that it should only be used if trying to display local time, not the UTC-relative time that
1795 	// XMP specifies.
1796 	if ( timezoneValue != 0xF ) {
1797 		utcOffsetHours = timezoneSign ? -timezoneValue : timezoneValue;
1798 		utcOffsetMinutes = 30 * halfHourFlag;
1799 	}
1800 
1801 	char dateBuff [40];
1802 
1803 	sprintf ( dateBuff,
1804 			  "%01d%01d%01d%01d-%01d%01d-%01d%01dT%01d%01d:%01d%01d:%01d%01d%+02d:%02d",
1805 			  (avchdDateTime[0] >> 4), (avchdDateTime[0] & 0x0F),
1806 			  (avchdDateTime[1] >> 4), (avchdDateTime[1] & 0x0F),
1807 			  (avchdDateTime[2] >> 4), (avchdDateTime[2] & 0x0F),
1808 			  (avchdDateTime[3] >> 4), (avchdDateTime[3] & 0x0F),
1809 			  (avchdDateTime[4] >> 4), (avchdDateTime[4] & 0x0F),
1810 			  (avchdDateTime[5] >> 4), (avchdDateTime[5] & 0x0F),
1811 			  (avchdDateTime[6] >> 4), (avchdDateTime[6] & 0x0F),
1812 			  utcOffsetHours, utcOffsetMinutes );
1813 
1814 	return std::string(dateBuff);
1815 }
1816 
1817 // =================================================================================================
1818 // AVCHD_MetaHandlerCTor
1819 // =====================
1820 
AVCHD_MetaHandlerCTor(XMPFiles * parent)1821 XMPFileHandler * AVCHD_MetaHandlerCTor ( XMPFiles * parent )
1822 {
1823 	return new AVCHD_MetaHandler ( parent );
1824 
1825 }	// AVCHD_MetaHandlerCTor
1826 
1827 // =================================================================================================
1828 // AVCHD_MetaHandler::AVCHD_MetaHandler
1829 // ====================================
1830 
AVCHD_MetaHandler(XMPFiles * _parent)1831 AVCHD_MetaHandler::AVCHD_MetaHandler ( XMPFiles * _parent )
1832 {
1833 	this->parent = _parent;	// Inherited, can't set in the prefix.
1834 	this->handlerFlags = kAVCHD_HandlerFlags;
1835 	this->stdCharForm  = kXMP_Char8Bit;
1836 
1837 	// Extract the root path and clip name.
1838 
1839 	if ( this->parent->tempPtr == 0 ) {
1840 		// The CheckFormat call might have been skipped.
1841 		this->parent->tempPtr = CreatePseudoClipPath ( this->parent->GetFilePath() );
1842 	}
1843 
1844 	this->rootPath.assign ( (char*) this->parent->tempPtr );
1845 	free ( this->parent->tempPtr );
1846 	this->parent->tempPtr = 0;
1847 
1848 	XIO::SplitLeafName ( &this->rootPath, &this->clipName );
1849 
1850 }	// AVCHD_MetaHandler::AVCHD_MetaHandler
1851 
1852 // =================================================================================================
1853 // AVCHD_MetaHandler::~AVCHD_MetaHandler
1854 // =====================================
1855 
~AVCHD_MetaHandler()1856 AVCHD_MetaHandler::~AVCHD_MetaHandler()
1857 {
1858 
1859 	if ( this->parent->tempPtr != 0 ) {
1860 		free ( this->parent->tempPtr );
1861 		this->parent->tempPtr = 0;
1862 	}
1863 
1864 }	// AVCHD_MetaHandler::~AVCHD_MetaHandler
1865 
1866 // =================================================================================================
1867 // AVCHD_MetaHandler::MakeClipInfoPath
1868 // ===================================
1869 
MakeClipInfoPath(std::string * path,XMP_StringPtr suffix,bool checkFile) const1870 bool AVCHD_MetaHandler::MakeClipInfoPath ( std::string * path, XMP_StringPtr suffix, bool checkFile /* = false */ ) const
1871 {
1872 	return MakeLeafPath ( path, this->rootPath.c_str(), "CLIPINF", this->clipName.c_str(), suffix, checkFile );
1873 }	// AVCHD_MetaHandler::MakeClipInfoPath
1874 
1875 // =================================================================================================
1876 // AVCHD_MetaHandler::MakeClipStreamPath
1877 // =====================================
1878 
MakeClipStreamPath(std::string * path,XMP_StringPtr suffix,bool checkFile) const1879 bool AVCHD_MetaHandler::MakeClipStreamPath ( std::string * path, XMP_StringPtr suffix, bool checkFile /* = false */ ) const
1880 {
1881 	return MakeLeafPath ( path, this->rootPath.c_str(), "STREAM", this->clipName.c_str(), suffix, checkFile );
1882 }	// AVCHD_MetaHandler::MakeClipStreamPath
1883 
1884 // =================================================================================================
1885 // AVCHD_MetaHandler::MakePlaylistPath
1886 // =====================================
1887 
MakePlaylistPath(std::string * path,XMP_StringPtr suffix,bool checkFile) const1888 bool AVCHD_MetaHandler::MakePlaylistPath ( std::string * path, XMP_StringPtr suffix, bool checkFile /* = false */ ) const
1889 {
1890 	return MakeLeafPath ( path, this->rootPath.c_str(), "PLAYLIST", this->clipName.c_str(), suffix, checkFile );
1891 }	// AVCHD_MetaHandler::MakePlaylistPath
1892 
1893 // =================================================================================================
1894 // AVCHD_MetaHandler::MakeLegacyDigest
1895 // ===================================
1896 
MakeLegacyDigest(std::string * digestStr)1897 void AVCHD_MetaHandler::MakeLegacyDigest ( std::string * digestStr )
1898 {
1899 	std::string strClipPath;
1900 	std::string strPlaylistPath;
1901 	std::vector<XMP_Uns8> legacyBuff;
1902 
1903 	bool ok = this->MakeClipInfoPath ( &strClipPath, ".clpi", true /* checkFile */ );
1904 	if ( ! ok ) return;
1905 
1906 	ok = this->MakePlaylistPath ( &strPlaylistPath, ".mpls", true /* checkFile */ );
1907 	if ( ! ok ) return;
1908 
1909 	try {
1910 		{
1911 			Host_IO::FileRef hostRef = Host_IO::Open ( strClipPath.c_str(), Host_IO::openReadOnly );
1912 			if ( hostRef == Host_IO::noFileRef ) return;	// The open failed.
1913 			XMPFiles_IO cpiFile ( hostRef, strClipPath.c_str(), Host_IO::openReadOnly );
1914 
1915 			// Read at most the first 2k of data from the cpi file to use in the digest
1916 			// (every CPI file I've seen is less than 1k).
1917 			const XMP_Int64 cpiLen = cpiFile.Length();
1918 			const XMP_Int64 buffLen = (cpiLen <= 2048) ? cpiLen : 2048;
1919 
1920 			legacyBuff.resize ( (unsigned int) buffLen );
1921 			cpiFile.ReadAll ( &(legacyBuff[0]),  static_cast<XMP_Int32> ( buffLen ) );
1922 		}
1923 
1924 		{
1925 			Host_IO::FileRef hostRef = Host_IO::Open ( strPlaylistPath.c_str(), Host_IO::openReadOnly );
1926 			if ( hostRef == Host_IO::noFileRef ) return;	// The open failed.
1927 			XMPFiles_IO mplFile ( hostRef, strPlaylistPath.c_str(), Host_IO::openReadOnly );
1928 
1929 			// Read at most the first 2k of data from the cpi file to use in the digest
1930 			// (every playlist file I've seen is less than 1k).
1931 			const XMP_Int64 mplLen = mplFile.Length();
1932 			const XMP_Int64 buffLen = (mplLen <= 2048) ? mplLen : 2048;
1933 			const XMP_Int64 clipBuffLen = legacyBuff.size();
1934 
1935 			legacyBuff.resize ( (unsigned int) (clipBuffLen + buffLen) );
1936 			mplFile.ReadAll ( &( legacyBuff [(unsigned int)clipBuffLen] ),  (XMP_Int32)buffLen );
1937 		}
1938 
1939 	} catch (...) {
1940 		return;
1941 	}
1942 
1943 	MD5_CTX context;
1944 	unsigned char digestBin [16];
1945 
1946 	MD5Init ( &context );
1947 	MD5Update ( &context, (XMP_Uns8*)&(legacyBuff[0]), (unsigned int) legacyBuff.size() );
1948 	MD5Final ( digestBin, &context );
1949 
1950 	*digestStr = BytesToHex ( digestBin, 16 );
1951 }	// AVCHD_MetaHandler::MakeLegacyDigest
1952 
1953 // =================================================================================================
1954 // AVCHD_MetaHandler::GetFileModDate
1955 // =================================
1956 
operator <(const XMP_DateTime & left,const XMP_DateTime & right)1957 static inline bool operator< ( const XMP_DateTime & left, const XMP_DateTime & right ) {
1958 	int compare = SXMPUtils::CompareDateTime ( left, right );
1959 	return (compare < 0);
1960 }
1961 
GetFileModDate(XMP_DateTime * modDate)1962 bool AVCHD_MetaHandler::GetFileModDate ( XMP_DateTime * modDate )
1963 {
1964 
1965 	// The AVCHD locations of metadata:
1966 	//	BDMV/
1967 	//		CLIPINF/
1968 	//			00001.clpi
1969 	//		PLAYLIST/
1970 	//			00001.mpls
1971 	//		STREAM/
1972 	//			00001.xmp
1973 
1974 	bool ok, haveDate = false;
1975 	std::string fullPath;
1976 	XMP_DateTime oneDate, junkDate;
1977 	if ( modDate == 0 ) modDate = &junkDate;
1978 
1979 	ok = this->MakeClipInfoPath ( &fullPath, ".clpi", true /* checkFile */ );
1980 	if ( ok ) ok = Host_IO::GetModifyDate ( fullPath.c_str(), &oneDate );
1981 	if ( ok ) {
1982 		if ( *modDate < oneDate ) *modDate = oneDate;
1983 		haveDate = true;
1984 	}
1985 
1986 	ok = this->MakePlaylistPath ( &fullPath, ".mpls", true /* checkFile */ );
1987 	if ( ok ) ok = Host_IO::GetModifyDate ( fullPath.c_str(), &oneDate );
1988 	if ( ok ) {
1989 		if ( (! haveDate) || (*modDate < oneDate) ) *modDate = oneDate;
1990 		haveDate = true;
1991 	}
1992 
1993 	ok = this->MakeClipStreamPath ( &fullPath, ".xmp", true /* checkFile */ );
1994 	if ( ok ) ok = Host_IO::GetModifyDate ( fullPath.c_str(), &oneDate );
1995 	if ( ok ) {
1996 		if ( (! haveDate) || (*modDate < oneDate) ) *modDate = oneDate;
1997 		haveDate = true;
1998 	}
1999 
2000 	return haveDate;
2001 
2002 }	// AVCHD_MetaHandler::GetFileModDate
2003 
2004 // =================================================================================================
2005 // AVCHD_MetaHandler::FillMetadataFiles
2006 // ================================
FillMetadataFiles(std::vector<std::string> * metadataFiles)2007 void AVCHD_MetaHandler::FillMetadataFiles ( std::vector<std::string> * metadataFiles)
2008 {
2009 	std::string noExtPath, filePath, altPath;
2010 
2011 	noExtPath = rootPath + kDirChar + "BDMV" + kDirChar + "STREAM" + kDirChar + clipName;
2012 	filePath  = noExtPath + ".xmp";
2013 	if ( ! Host_IO::Exists ( filePath.c_str() ) ) {
2014 		altPath = noExtPath + ".XMP";
2015 		if ( Host_IO::Exists ( altPath.c_str() ) ) filePath = altPath;
2016 	}
2017 	metadataFiles->push_back ( filePath );
2018 
2019 	noExtPath = rootPath + kDirChar + "BDMV" + kDirChar + "CLIPINF" + kDirChar + clipName;
2020 	filePath  = noExtPath + ".clpi";
2021 	if ( ! Host_IO::Exists ( filePath.c_str() ) ) {
2022 		altPath = noExtPath + ".CLPI";
2023 		if ( ! Host_IO::Exists ( altPath.c_str() ) ) altPath = noExtPath + ".cpi";
2024 		if ( ! Host_IO::Exists ( altPath.c_str() ) ) altPath = noExtPath + ".CPI";
2025 		if ( Host_IO::Exists ( altPath.c_str() ) ) filePath = altPath;
2026 	}
2027 	metadataFiles->push_back ( filePath );
2028 
2029 }	// 	FillMetadataFiles_AVCHD
2030 
2031 // =================================================================================================
2032 // AVCHD_MetaHandler::IsMetadataWritable
2033 // =======================================
2034 
IsMetadataWritable()2035 bool AVCHD_MetaHandler::IsMetadataWritable ( )
2036 {
2037 	std::vector<std::string> metadataFiles;
2038 	FillMetadataFiles(&metadataFiles);
2039 	std::vector<std::string>::iterator itr = metadataFiles.begin();
2040 	// Check whether sidecar is writable, if not then check if it can be created.
2041 	return Host_IO::Writable( itr->c_str(), true );
2042 }// AVCHD_MetaHandler::IsMetadataWritable
2043 
2044 // =================================================================================================
2045 // AVCHD_MetaHandler::FillAssociatedResources
2046 // ======================================
FillAssociatedResources(std::vector<std::string> * resourceList)2047 void AVCHD_MetaHandler::FillAssociatedResources ( std::vector<std::string> * resourceList )
2048 {
2049 	/// The possible associated resources:
2050 	///   BDMV/
2051 	///         index.bdmv
2052 	///         MovieObject.bdmv
2053 	///         PLAYLIST/
2054 	///				xxxxx.mpls
2055 	///         STREAM/
2056 	///				zzzzz.m2ts
2057 	///             zzzzz.xmp
2058 	///         CLIPINF/
2059 	///				zzzzz.clpi
2060 	///         BACKUP/
2061 	//    xxxxx is a five digit playlist name
2062 	//    zzzzz is a five digit clip name
2063 	//
2064 	std::string bdmvPath = rootPath + kDirChar + "BDMV" + kDirChar;
2065 	std::string filePath, clipInfoPath;
2066 	//Add RootPath
2067 	filePath = rootPath + kDirChar;
2068 	PackageFormat_Support::AddResourceIfExists( resourceList, filePath );
2069 	// Add existing files under the folder "BDMV"
2070 	filePath = bdmvPath + "index.bdmv";
2071 	if ( ! PackageFormat_Support::AddResourceIfExists ( resourceList, filePath ) ) {
2072 		filePath = bdmvPath + "INDEX.BDMV";
2073 		if ( ! PackageFormat_Support::AddResourceIfExists ( resourceList, filePath ) ) {
2074 			filePath = bdmvPath + "index.bdm";
2075 			if ( ! PackageFormat_Support::AddResourceIfExists ( resourceList, filePath ) ) {
2076 				filePath = bdmvPath + "INDEX.BDM";
2077 				PackageFormat_Support::AddResourceIfExists ( resourceList, filePath );
2078 			}
2079 		}
2080 	}
2081 	filePath = bdmvPath + "MovieObject.bdmv";
2082 	if ( ! PackageFormat_Support::AddResourceIfExists ( resourceList, filePath ) ) {
2083 		filePath = bdmvPath + "MOVIEOBJECT.BDMV";
2084 		if ( ! PackageFormat_Support::AddResourceIfExists ( resourceList, filePath ) ) {
2085 			filePath = bdmvPath + "MovieObj.bdm";
2086 			if ( ! PackageFormat_Support::AddResourceIfExists ( resourceList, filePath ) ) {
2087 				filePath = bdmvPath + "MOVIEOBJ.BDM";
2088 				PackageFormat_Support::AddResourceIfExists ( resourceList, filePath );
2089 			}
2090 		}
2091 	}
2092 
2093 
2094 	if ( MakeClipInfoPath ( &filePath, ".clpi", true /* checkFile */ ) ) {
2095 		PackageFormat_Support::AddResourceIfExists ( resourceList, filePath );
2096 		clipInfoPath = filePath;
2097 	}
2098 	else {
2099 		filePath = bdmvPath + "CLIPINF" + kDirChar ;
2100 		PackageFormat_Support::AddResourceIfExists ( resourceList, filePath );
2101 	}
2102 
2103 	bool addedStreamDir=false;
2104 	if ( MakeClipStreamPath ( &filePath, ".xmp", true /* checkFile */ ) ) {
2105 		PackageFormat_Support::AddResourceIfExists ( resourceList, filePath );
2106 		addedStreamDir = true;
2107 	}
2108 
2109 
2110 	if ( MakeClipStreamPath ( &filePath, ".m2ts", true /* checkFile */ ) ) {
2111 		PackageFormat_Support::AddResourceIfExists ( resourceList, filePath );
2112 	}
2113 	else if ( ! addedStreamDir ) {
2114 		filePath = bdmvPath + "STREAM" + kDirChar ;
2115 		PackageFormat_Support::AddResourceIfExists ( resourceList, filePath );
2116 	}
2117 
2118 	AVCHD_LegacyMetadata avchdLegacyData;
2119 	if ( ReadAVCHDLegacyMetadata ( clipInfoPath, this->rootPath, this->clipName, avchdLegacyData, filePath ) ) {
2120 		PackageFormat_Support::AddResourceIfExists ( resourceList, filePath );
2121 	}
2122 	else {
2123 		filePath = bdmvPath + "PLAYLIST" + kDirChar ;
2124 		PackageFormat_Support::AddResourceIfExists ( resourceList, filePath );
2125 	}
2126 }
2127 
2128 // =================================================================================================
2129 // AVCHD_MetaHandler::CacheFileData
2130 // ================================
2131 
CacheFileData()2132 void AVCHD_MetaHandler::CacheFileData()
2133 {
2134 	XMP_Assert ( ! this->containsXMP );
2135 
2136 	if ( this->parent->UsesClientIO() ) {
2137 		XMP_Throw ( "AVCHD cannot be used with client-managed I/O", kXMPErr_InternalFailure );
2138 	}
2139 
2140 	// See if the clip's .XMP file exists.
2141 
2142 	std::string xmpPath;
2143 	bool found = this->MakeClipStreamPath ( &xmpPath, ".xmp", true /* checkFile */ );
2144 	if ( ! found ) return;	// No XMP.
2145 	XMP_Assert ( Host_IO::Exists ( xmpPath.c_str() ) );	// MakeClipStreamPath should ensure this.
2146 
2147 	// Read the entire .XMP file. We know the XMP exists, New_XMPFiles_IO is supposed to return 0
2148 	// only if the file does not exist.
2149 
2150 	bool readOnly = XMP_OptionIsClear ( this->parent->openFlags, kXMPFiles_OpenForUpdate );
2151 
2152 	XMP_Assert ( this->parent->ioRef == 0 );
2153 	XMPFiles_IO* xmpFile =  XMPFiles_IO::New_XMPFiles_IO ( xmpPath.c_str(), readOnly );
2154 	if ( xmpFile == 0 ) XMP_Throw ( "AVCHD XMP file open failure", kXMPErr_InternalFailure );
2155 	this->parent->ioRef = xmpFile;
2156 
2157 	XMP_Int64 xmpLen = xmpFile->Length();
2158 	if ( xmpLen > 100*1024*1024 ) {
2159 		XMP_Throw ( "AVCHD XMP is outrageously large", kXMPErr_InternalFailure );	// Sanity check.
2160 	}
2161 
2162 	this->xmpPacket.erase();
2163 	this->xmpPacket.append ( (size_t ) xmpLen, ' ' );
2164 
2165 	/*XMP_Int32 ioCount =*/ xmpFile->ReadAll ( (void*)this->xmpPacket.data(), (XMP_Int32)xmpLen );
2166 
2167 	this->packetInfo.offset = 0;
2168 	this->packetInfo.length = (XMP_Int32)xmpLen;
2169 	FillPacketInfo ( this->xmpPacket, &this->packetInfo );
2170 
2171 	this->containsXMP = true;
2172 
2173 }	// AVCHD_MetaHandler::CacheFileData
2174 
2175 // =================================================================================================
2176 // AVCHD_MetaHandler::ProcessXMP
2177 // =============================
2178 
ProcessXMP()2179 void AVCHD_MetaHandler::ProcessXMP()
2180 {
2181 	if ( this->processedXMP ) return;
2182 	this->processedXMP = true;	// Make sure only called once.
2183 
2184 	if ( this->containsXMP ) {
2185 		this->xmpObj.ParseFromBuffer ( this->xmpPacket.c_str(), (XMP_StringLen)this->xmpPacket.size() );
2186 	}
2187 
2188 	// read clip info
2189 	AVCHD_LegacyMetadata avchdLegacyData;
2190 	std::string strPath,mplfile;
2191 
2192 	bool ok = this->MakeClipInfoPath ( &strPath, ".clpi", true /* checkFile */ );
2193 	if ( ok ) ReadAVCHDLegacyMetadata ( strPath, this->rootPath, this->clipName, avchdLegacyData , mplfile);
2194 	if ( ! ok ) return;
2195 
2196 	const AVCHD_blkPlayListMarkExt& markExt = avchdLegacyData.mPlaylistExtensionData.mPlaylistMarkExt;
2197 	XMP_Uns8 pulldownFlag = 0;
2198 
2199 	if ( markExt.mPresent ) {
2200 		const std::string dateString = AVCHD_DateFieldToXMP ( markExt.mBlkTimezone, markExt.mRecordDataAndTime );
2201 
2202 		if ( ! dateString.empty() ) this->xmpObj.SetProperty ( kXMP_NS_DM, "shotDate", dateString.c_str(), kXMP_DeleteExisting );
2203 		AVCHD_SetXMPShotName ( this->xmpObj, markExt, this->clipName );
2204 		AVCCAM_SetXMPStartTimecode ( this->xmpObj, markExt.mBlkTimecode, avchdLegacyData.mProgramInfo.mVideoStream.mFrameRate );
2205 		pulldownFlag = (markExt.mFlags >> 1) & 0x03;  // bits 1 and 2
2206 	}
2207 
2208 	// Video Stream. AVCHD Format v. 1.01 p. 78
2209 
2210 	const bool has2_2pulldown = (pulldownFlag == 0x01);
2211 	const bool has3_2pulldown = (pulldownFlag == 0x10);
2212 	XMP_StringPtr xmpValue = 0;
2213 
2214 	if ( avchdLegacyData.mProgramInfo.mVideoStream.mPresent ) {
2215 
2216 		// XMP videoFrameSize.
2217 		xmpValue = 0;
2218 		int frameIndex = -1;
2219 		bool isProgressiveHD = false;
2220 		const char* frameWidth[4]	= { "720", "720", "1280", "1920" };
2221 		const char* frameHeight[4]	= { "480", "576", "720",  "1080" };
2222 
2223 		switch ( avchdLegacyData.mProgramInfo.mVideoStream.mVideoFormat ) {
2224 			case 1 : frameIndex = 0;							break; // 480i
2225 			case 2 : frameIndex = 1;							break; // 576i
2226 			case 3 : frameIndex = 0;							break; // 480p
2227 			case 4 : frameIndex = 3;							break; // 1080i
2228 			case 5 : frameIndex = 2; isProgressiveHD = true;	break; // 720p
2229 			case 6 : frameIndex = 3; isProgressiveHD = true;	break; // 1080p
2230 			default: break;
2231 		}
2232 
2233 		if ( frameIndex != -1 ) {
2234 			xmpValue = frameWidth[frameIndex];
2235 			this->xmpObj.SetStructField ( kXMP_NS_DM, "videoFrameSize", kXMP_NS_XMP_Dimensions, "w", xmpValue, 0 );
2236 			xmpValue = frameHeight[frameIndex];
2237 			this->xmpObj.SetStructField ( kXMP_NS_DM, "videoFrameSize", kXMP_NS_XMP_Dimensions, "h", xmpValue, 0 );
2238 			xmpValue = "pixels";
2239 			this->xmpObj.SetStructField ( kXMP_NS_DM, "videoFrameSize", kXMP_NS_XMP_Dimensions, "unit", xmpValue, 0 );
2240 		}
2241 
2242 		// XMP videoFrameRate. The logic below seems pretty tortured, but matches "Table 4-4 pulldown" on page 31 of Book 2 of the AVCHD
2243 		// spec, if you interepret "frame_mbs_only_flag" as "isProgressiveHD", "frame-rate [Hz]" as the frame rate encoded in
2244 		// mVideoStream.mFrameRate, and "Video Scan Type" as the desired xmp output value. The algorithm produces correct results for
2245 		// all the AVCHD media I've tested.
2246 		xmpValue = 0;
2247 		if ( isProgressiveHD ) {
2248 
2249 			switch ( avchdLegacyData.mProgramInfo.mVideoStream.mFrameRate ) {
2250 				case 1 : xmpValue = "23.98p";								break;   // "23.976"
2251 				case 2 : xmpValue = "24p";									break;   // "24"
2252 				case 3 : xmpValue = "25p";									break;   // "25"
2253 				case 4 : xmpValue = has2_2pulldown ? "29.97p" : "59.94p";	break;   // "29.97"
2254 				case 6 : xmpValue = has2_2pulldown ? "25p" : "50p";			break;   // "50"
2255 				case 7 :															 // "59.94"
2256 					if ( has2_2pulldown )
2257 						xmpValue = "29.97p";
2258 					else
2259 						xmpValue = has3_2pulldown ? "23.98p" : "59.94p";
2260 
2261 					break;
2262 				default: break;
2263 			}
2264 
2265 		} else {
2266 
2267 			switch ( avchdLegacyData.mProgramInfo.mVideoStream.mFrameRate ) {
2268 				case 3 : xmpValue = has2_2pulldown ? "25p" : "50i";			break;  // "25" (but 1080p25 is reported as 1080i25 with 2:2 pulldown...)
2269 				case 4 :															// "29.97"
2270 					if ( has2_2pulldown )
2271 						xmpValue = "29.97p";
2272 					else
2273 						xmpValue = "59.94i";
2274 
2275 					break;
2276 				default: break;
2277 			}
2278 
2279 		}
2280 
2281 		if ( xmpValue != 0 ) {
2282 			this->xmpObj.SetProperty ( kXMP_NS_DM, "videoFrameRate", xmpValue, kXMP_DeleteExisting );
2283 		}
2284 
2285 		this->containsXMP = true;
2286 
2287 	}
2288 
2289 	// Audio Stream.
2290 	if ( avchdLegacyData.mProgramInfo.mAudioStream.mPresent ) {
2291 
2292 		xmpValue = 0;
2293 		switch ( avchdLegacyData.mProgramInfo.mAudioStream.mAudioPresentationType ) {
2294 			case 1  : xmpValue = "Mono"; break;
2295 			case 3  : xmpValue = "Stereo"; break;
2296 			default : break;
2297 		}
2298 		if ( xmpValue != 0 ) {
2299 			this->xmpObj.SetProperty ( kXMP_NS_DM, "audioChannelType", xmpValue, kXMP_DeleteExisting );
2300 		}
2301 
2302 		xmpValue = 0;
2303 		switch ( avchdLegacyData.mProgramInfo.mAudioStream.mSamplingFrequency ) {
2304 			case 1  : xmpValue = "48000"; break;
2305 			case 4  : xmpValue = "96000"; break;
2306 			case 5  : xmpValue = "192000"; break;
2307 			default : break;
2308 		}
2309 		if ( xmpValue != 0 ) {
2310 			this->xmpObj.SetProperty ( kXMP_NS_DM, "audioSampleRate", xmpValue, kXMP_DeleteExisting );
2311 		}
2312 
2313 		this->containsXMP = true;
2314 	}
2315 
2316 	// Proprietary vendor extensions
2317 	if ( AVCHD_SetXMPMakeAndModel ( this->xmpObj, avchdLegacyData.mClipExtensionData ) ) this->containsXMP = true;
2318 
2319 	this->xmpObj.SetProperty ( kXMP_NS_DM, "title", this->clipName.c_str(), kXMP_DeleteExisting );
2320 	this->containsXMP = true;
2321 
2322 	if ( avchdLegacyData.mClipExtensionData.mMakersPrivateData.mPresent &&
2323 		 ( avchdLegacyData.mClipExtensionData.mClipInfoExt.mMakerID == kMakerIDPanasonic ) ) {
2324 
2325 		const AVCHD_blkPanasonicPrivateData& panasonicClipData = avchdLegacyData.mClipExtensionData.mMakersPrivateData.mPanasonicPrivateData;
2326 
2327 		if ( panasonicClipData.mProClipIDBlock.mPresent ) {
2328 			const std::string globalClipIDString = BytesToHex ( panasonicClipData.mProClipIDBlock.mGlobalClipID, 32 );
2329 
2330 			this->xmpObj.SetProperty ( kXMP_NS_DC, "identifier", globalClipIDString.c_str(), kXMP_DeleteExisting );
2331 		}
2332 
2333 		const AVCHD_blkPanasonicPrivateData& panasonicPlaylistData =
2334 			avchdLegacyData.mPlaylistExtensionData.mMakersPrivateData.mPanasonicPrivateData;
2335 
2336 		if ( panasonicPlaylistData.mProPlaylistInfoBlock.mPlayListMark.mPresent ) {
2337 			const AVCCAM_blkProPlayListMark& playlistMark = panasonicPlaylistData.mProPlaylistInfoBlock.mPlayListMark;
2338 
2339 			if ( playlistMark.mShotMark.mPresent ) {
2340 				// Unlike P2, where "shotmark" is a boolean, Panasonic treats their AVCCAM shotmark as a bit field with
2341 				// 8 user-definable bits. For now we're going to treat any bit being set as xmpDM::good == true, and all
2342 				// bits being clear as xmpDM::good == false.
2343 				const bool isGood = ( playlistMark.mShotMark.mShotMark != 0 );
2344 
2345 				xmpObj.SetProperty_Bool ( kXMP_NS_DM, "good",  isGood, kXMP_DeleteExisting );
2346 			}
2347 
2348 			if ( playlistMark.mAccess.mPresent && ( playlistMark.mAccess.mCreatorLength > 0 ) ) {
2349 				const std::string creatorString = AVCHD_StringFieldToXMP (
2350 					playlistMark.mAccess.mCreatorLength, playlistMark.mAccess.mCreatorCharacterSet, playlistMark.mAccess.mCreator, 32 ) ;
2351 
2352 				if ( ! creatorString.empty() ) {
2353 					xmpObj.DeleteProperty ( kXMP_NS_DC, "creator" );
2354 					xmpObj.AppendArrayItem ( kXMP_NS_DC, "creator", kXMP_PropArrayIsOrdered, creatorString.c_str() );
2355 				}
2356 			}
2357 
2358 			if ( playlistMark.mDevice.mPresent && ( playlistMark.mDevice.mSerialNoLength > 0 ) ) {
2359 				const std::string serialNoString = AVCHD_StringFieldToXMP (
2360 					playlistMark.mDevice.mSerialNoLength, playlistMark.mDevice.mSerialNoCharacterCode, playlistMark.mDevice.mSerialNo, 24 ) ;
2361 
2362 				if ( ! serialNoString.empty() ) xmpObj.SetProperty ( kXMP_NS_EXIF_Aux, "SerialNumber", serialNoString.c_str(), kXMP_DeleteExisting );
2363 			}
2364 
2365 			if ( playlistMark.mLocation.mPresent && ( playlistMark.mLocation.mPlaceNameLength > 0 ) ) {
2366 				const std::string placeNameString = AVCHD_StringFieldToXMP (
2367 					playlistMark.mLocation.mPlaceNameLength, playlistMark.mLocation.mPlaceNameCharacterSet, playlistMark.mLocation.mPlaceName, 64 ) ;
2368 
2369 				if ( ! placeNameString.empty() ) xmpObj.SetProperty ( kXMP_NS_DM, "shotLocation", placeNameString.c_str(), kXMP_DeleteExisting );
2370 			}
2371 		}
2372 	}
2373 
2374 }	// AVCHD_MetaHandler::ProcessXMP
2375 
2376 // =================================================================================================
2377 // AVCHD_MetaHandler::UpdateFile
2378 // =============================
2379 //
2380 // Note that UpdateFile is only called from XMPFiles::CloseFile, so it is OK to close the file here.
2381 
UpdateFile(bool doSafeUpdate)2382 void AVCHD_MetaHandler::UpdateFile ( bool doSafeUpdate )
2383 {
2384 	if ( ! this->needsUpdate ) return;
2385 	this->needsUpdate = false;	// Make sure only called once.
2386 
2387 	XMP_Assert ( this->parent->UsesLocalIO() );
2388 
2389 	std::string newDigest;
2390 	this->MakeLegacyDigest ( &newDigest );
2391 	this->xmpObj.SetStructField ( kXMP_NS_XMP, "NativeDigests", kXMP_NS_XMP, "AVCHD", newDigest.c_str(), kXMP_DeleteExisting );
2392 
2393 	this->xmpObj.SerializeToBuffer ( &this->xmpPacket, this->GetSerializeOptions() );
2394 
2395 	std::string xmpPath;
2396 	this->MakeClipStreamPath ( &xmpPath, ".xmp" );
2397 
2398 	bool haveXMP = Host_IO::Exists ( xmpPath.c_str() );
2399 	if ( ! haveXMP ) {
2400 		XMP_Assert ( this->parent->ioRef == 0 );
2401 		Host_IO::Create ( xmpPath.c_str() );
2402 		this->parent->ioRef = XMPFiles_IO::New_XMPFiles_IO ( xmpPath.c_str(), Host_IO::openReadWrite );
2403 		if ( this->parent->ioRef == 0 ) XMP_Throw ( "Failure opening AVCHD XMP file", kXMPErr_ExternalFailure );
2404 	}
2405 
2406 	XMP_IO* xmpFile = this->parent->ioRef;
2407 	XMP_Assert ( xmpFile != 0 );
2408 	XIO::ReplaceTextFile ( xmpFile, this->xmpPacket, (haveXMP & doSafeUpdate) );
2409 
2410 }	// AVCHD_MetaHandler::UpdateFile
2411 
2412 // =================================================================================================
2413 // AVCHD_MetaHandler::WriteTempFile
2414 // ================================
2415 
WriteTempFile(XMP_IO *)2416 void AVCHD_MetaHandler::WriteTempFile ( XMP_IO* /*tempRef*/ )
2417 {
2418 
2419 	// ! WriteTempFile is not supposed to be called for handlers that own the file.
2420 	XMP_Throw ( "AVCHD_MetaHandler::WriteTempFile should not be called", kXMPErr_InternalFailure );
2421 
2422 }	// AVCHD_MetaHandler::WriteTempFile
2423 
2424 // =================================================================================================
2425