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