void P2_MetaHandler::CacheFileData() { XMP_Assert ( ! this->containsXMP ); if ( this->parent->UsesClientIO() ) { XMP_Throw ( "P2 cannot be used with client-managed I/O", kXMPErr_InternalFailure ); } // Make sure the clip's .XMP file exists. std::string xmpPath; this->MakeClipFilePath ( &xmpPath, ".XMP" ); if ( ! Host_IO::Exists ( xmpPath.c_str() ) ) return; // No XMP. // Read the entire .XMP file. We know the XMP exists, New_XMPFiles_IO is supposed to return 0 // only if the file does not exist. bool readOnly = XMP_OptionIsClear ( this->parent->openFlags, kXMPFiles_OpenForUpdate ); XMP_Assert ( this->parent->ioRef == 0 ); XMPFiles_IO* xmpFile = XMPFiles_IO::New_XMPFiles_IO ( xmpPath.c_str(), readOnly ); if ( xmpFile == 0 ) XMP_Throw ( "P2 XMP file open failure", kXMPErr_InternalFailure ); this->parent->ioRef = xmpFile; XMP_Int64 xmpLen = xmpFile->Length(); if ( xmpLen > 100*1024*1024 ) { XMP_Throw ( "P2 XMP is outrageously large", kXMPErr_InternalFailure ); // Sanity check. } this->xmpPacket.erase(); this->xmpPacket.append ( (size_t)xmpLen, ' ' ); /*XMP_Int32 ioCount =*/ xmpFile->ReadAll ( (void*)this->xmpPacket.data(), (XMP_Int32)xmpLen ); this->packetInfo.offset = 0; this->packetInfo.length = (XMP_Int32)xmpLen; FillPacketInfo ( this->xmpPacket, &this->packetInfo ); this->containsXMP = true; } // P2_MetaHandler::CacheFileData
void MPEG2_MetaHandler::CacheFileData() { bool readOnly = (! (this->parent->openFlags & kXMPFiles_OpenForUpdate)); if ( this->parent->UsesClientIO() ) { XMP_Throw ( "MPEG2 cannot be used with client-managed I/O", kXMPErr_InternalFailure ); } this->containsXMP = false; this->processedXMP = true; // Whatever we do here is all that we do for XMPFiles::OpenFile. // Try to open the sidecar XMP file. Tolerate an open failure, there might not be any XMP. // Note that MPEG2_CheckFormat can't save the sidecar path because the handler doesn't exist then. if ( ! Host_IO::Exists ( this->sidecarPath.c_str() ) ) return; // OK to not have XMP. XMPFiles_IO * localFile = XMPFiles_IO::New_XMPFiles_IO ( this->sidecarPath.c_str(), readOnly ); if ( localFile == 0 ) XMP_Throw ( "Failure opening MPEG-2 XMP file", kXMPErr_ExternalFailure ); this->parent->ioRef = localFile; // Extract the sidecar's contents and parse. this->packetInfo.offset = 0; // We take the whole sidecar file. this->packetInfo.length = (XMP_Int32) localFile->Length(); if ( this->packetInfo.length > 0 ) { this->xmpPacket.assign ( this->packetInfo.length, ' ' ); localFile->ReadAll ( (void*)this->xmpPacket.c_str(), this->packetInfo.length ); this->xmpObj.ParseFromBuffer ( this->xmpPacket.c_str(), (XMP_StringLen)this->xmpPacket.size() ); this->containsXMP = true; } if ( readOnly ) { localFile->Close(); delete localFile; this->parent->ioRef = 0; } } // MPEG2_MetaHandler::CacheFileData
static bool ReadIDXFile ( const std::string& idxPath, const std::string& clipName, SXMPMeta* xmpObj, bool& containsXMP, MD5_CTX* md5Context, bool digestFound ) { bool result = true; containsXMP = false; if ( clipName.size() != 25 ) return false; try { Host_IO::FileRef hostRef = Host_IO::Open ( idxPath.c_str(), Host_IO::openReadOnly ); if ( hostRef == Host_IO::noFileRef ) return false; // The open failed. XMPFiles_IO idxFile ( hostRef, idxPath.c_str(), Host_IO::openReadOnly ); struct SHDV_HeaderBlock { char mHeader[8]; unsigned char mValidFlag; unsigned char mReserved; unsigned char mECCTB; unsigned char mSignalMode; unsigned char mFileThousands; unsigned char mFileHundreds; unsigned char mFileTens; unsigned char mFileUnits; }; SHDV_HeaderBlock hdvHeaderBlock; memset ( &hdvHeaderBlock, 0, sizeof(SHDV_HeaderBlock) ); idxFile.ReadAll ( hdvHeaderBlock.mHeader, 8 ); idxFile.ReadAll ( &hdvHeaderBlock.mValidFlag, 1 ); idxFile.ReadAll ( &hdvHeaderBlock.mReserved, 1 ); idxFile.ReadAll ( &hdvHeaderBlock.mECCTB, 1 ); idxFile.ReadAll ( &hdvHeaderBlock.mSignalMode, 1 ); idxFile.ReadAll ( &hdvHeaderBlock.mFileThousands, 1 ); idxFile.ReadAll ( &hdvHeaderBlock.mFileHundreds, 1 ); idxFile.ReadAll ( &hdvHeaderBlock.mFileTens, 1 ); idxFile.ReadAll ( &hdvHeaderBlock.mFileUnits, 1 ); const int fileCount = (hdvHeaderBlock.mFileThousands - '0') * 1000 + (hdvHeaderBlock.mFileHundreds - '0') * 100 + (hdvHeaderBlock.mFileTens - '0') * 10 + (hdvHeaderBlock.mFileUnits - '0'); // Read file info block. struct SHDV_FileBlock { char mDT[2]; unsigned char mFileNameYear; unsigned char mFileNameMonth; unsigned char mFileNameDay; unsigned char mFileNameHour; unsigned char mFileNameMinute; unsigned char mFileNameSecond; unsigned char mStartTimeCode[4]; unsigned char mTotalFrame[4]; }; SHDV_FileBlock hdvFileBlock; memset ( &hdvFileBlock, 0, sizeof(SHDV_FileBlock) ); char filenameBuffer[256]; std::string fileDateAndTime = clipName.substr(8); bool foundFileBlock = false; for ( int i=0; ((i < fileCount) && (! foundFileBlock)); ++i ) { idxFile.ReadAll ( hdvFileBlock.mDT, 2 ); idxFile.ReadAll ( &hdvFileBlock.mFileNameYear, 1 ); idxFile.ReadAll ( &hdvFileBlock.mFileNameMonth, 1 ); idxFile.ReadAll ( &hdvFileBlock.mFileNameDay, 1 ); idxFile.ReadAll ( &hdvFileBlock.mFileNameHour, 1 ); idxFile.ReadAll ( &hdvFileBlock.mFileNameMinute, 1 ); idxFile.ReadAll ( &hdvFileBlock.mFileNameSecond, 1 ); idxFile.ReadAll ( &hdvFileBlock.mStartTimeCode, 4 ); idxFile.ReadAll ( &hdvFileBlock.mTotalFrame, 4 ); // Compose file name we expect from file contents and break out on match. sprintf ( filenameBuffer, "%02d-%02d-%02d_%02d%02d%02d", hdvFileBlock.mFileNameYear + 2000, hdvFileBlock.mFileNameMonth, hdvFileBlock.mFileNameDay, hdvFileBlock.mFileNameHour, hdvFileBlock.mFileNameMinute, hdvFileBlock.mFileNameSecond ); foundFileBlock = (fileDateAndTime==filenameBuffer); } idxFile.Close(); if ( ! foundFileBlock ) return false; // If digest calculation requested, calculate it and return. if ( md5Context != 0 ) { MD5Update ( md5Context, (XMP_Uns8*)(&hdvHeaderBlock), sizeof(SHDV_HeaderBlock) ); MD5Update ( md5Context, (XMP_Uns8*)(&hdvFileBlock), sizeof(SHDV_FileBlock) ); } // The xmpObj parameter must be provided in order to extract XMP if ( xmpObj == 0 ) return (md5Context != 0); // Standard def? const bool isSD = ((hdvHeaderBlock.mSignalMode == 0x80) || (hdvHeaderBlock.mSignalMode == 0)); // Progressive vs interlaced extracted from high bit of ECCTB byte const bool clipIsProgressive = ((hdvHeaderBlock.mECCTB & 0x80) != 0); // Lowest three bits contain frame rate information const int sfr = (hdvHeaderBlock.mECCTB & 7) + (clipIsProgressive ? 0 : 8); // Sample scale and sample size. int clipSampleScale = 0; int clipSampleSize = 0; std::string frameRate; // Frame rate switch ( sfr ) { case 0 : break; // Not valid in spec, but it's happening in test files. case 1 : clipSampleScale = 24000; clipSampleSize = 1001; frameRate = "23.98p"; break; case 3 : clipSampleScale = 25; clipSampleSize = 1; frameRate = "25p"; break; case 4 : clipSampleScale = 30000; clipSampleSize = 1001; frameRate = "29.97p"; break; case 11 : clipSampleScale = 25; clipSampleSize = 1; frameRate = "50i"; break; case 12 : clipSampleScale = 30000; clipSampleSize = 1001; frameRate = "59.94i"; break; } containsXMP = true; // Frame size and PAR for HD (not clear on SD yet). std::string xmpString; XMP_StringPtr xmpValue = 0; if ( ! isSD ) { if ( digestFound || (! xmpObj->DoesPropertyExist ( kXMP_NS_DM, "videoFrameSize" )) ) { xmpValue = "1440"; xmpObj->GetStructField ( kXMP_NS_DM, "videoFrameSize", kXMP_NS_DM, "w", &xmpString, 0 ); if ( xmpString != xmpValue ) { xmpObj->SetStructField ( kXMP_NS_DM, "videoFrameSize", kXMP_NS_XMP_Dimensions, "w", xmpValue, 0 ); } xmpValue = "1080"; xmpObj->GetStructField ( kXMP_NS_DM, "videoFrameSize", kXMP_NS_DM, "h", &xmpString, 0 ); if ( xmpString != xmpValue ) { xmpObj->SetStructField ( kXMP_NS_DM, "videoFrameSize", kXMP_NS_XMP_Dimensions, "h", xmpValue, 0 ); } xmpValue = "pixels"; xmpObj->GetStructField ( kXMP_NS_DM, "videoFrameSize", kXMP_NS_DM, "unit", &xmpString, 0 ); if ( xmpString != xmpValue ) { xmpObj->SetStructField ( kXMP_NS_DM, "videoFrameSize", kXMP_NS_XMP_Dimensions, "unit", xmpValue, 0 ); } } xmpValue = "4/3"; if ( digestFound || (! xmpObj->DoesPropertyExist ( kXMP_NS_DM, "videoPixelAspectRatio" )) ) { xmpObj->SetProperty ( kXMP_NS_DM, "videoPixelAspectRatio", xmpValue, kXMP_DeleteExisting ); } } // Sample size and scale. if ( clipSampleScale != 0 ) { char buffer[255]; if ( digestFound || (! xmpObj->DoesPropertyExist ( kXMP_NS_DM, "startTimeScale" )) ) { sprintf(buffer, "%d", clipSampleScale); xmpValue = buffer; xmpObj->SetProperty ( kXMP_NS_DM, "startTimeScale", xmpValue, kXMP_DeleteExisting ); } if ( digestFound || (! xmpObj->DoesPropertyExist ( kXMP_NS_DM, "startTimeSampleSize" )) ) { sprintf(buffer, "%d", clipSampleSize); xmpValue = buffer; xmpObj->SetProperty ( kXMP_NS_DM, "startTimeSampleSize", xmpValue, kXMP_DeleteExisting ); } if ( digestFound || (! xmpObj->DoesPropertyExist ( kXMP_NS_DM, "duration" )) ) { const int frameCount = (hdvFileBlock.mTotalFrame[0] << 24) + (hdvFileBlock.mTotalFrame[1] << 16) + (hdvFileBlock.mTotalFrame[2] << 8) + hdvFileBlock.mTotalFrame[3]; sprintf ( buffer, "%d", frameCount ); xmpValue = buffer; xmpObj->SetStructField ( kXMP_NS_DM, "duration", kXMP_NS_DM, "value", xmpValue, 0 ); sprintf ( buffer, "%d/%d", clipSampleSize, clipSampleScale ); xmpValue = buffer; xmpObj->SetStructField ( kXMP_NS_DM, "duration", kXMP_NS_DM, "scale", xmpValue, 0 ); } } // Time Code. if ( digestFound || (! xmpObj->DoesPropertyExist ( kXMP_NS_DM, "startTimecode" )) ) { if ( (clipSampleScale != 0) && (clipSampleSize != 0) ) { const bool dropFrame = ( (0x40 & hdvFileBlock.mStartTimeCode[0]) != 0 ) && ( sfr == 4 || sfr == 12 ); const char chDF = dropFrame ? ';' : ':'; const int tcFrames = ExtractTimeCodeByte ( hdvFileBlock.mStartTimeCode[0], 0x30 ); const int tcSeconds = ExtractTimeCodeByte ( hdvFileBlock.mStartTimeCode[1], 0x70 ); const int tcMinutes = ExtractTimeCodeByte ( hdvFileBlock.mStartTimeCode[2], 0x70 ); const int tcHours = ExtractTimeCodeByte ( hdvFileBlock.mStartTimeCode[3], 0x30 ); // HH:MM:SS:FF or HH;MM;SS;FF char timecode[256]; sprintf ( timecode, "%02d%c%02d%c%02d%c%02d", tcHours, chDF, tcMinutes, chDF, tcSeconds, chDF, tcFrames ); std::string sonyTimeString = timecode; xmpObj->GetStructField ( kXMP_NS_DM, "startTimecode", kXMP_NS_DM, "timeValue", &xmpString, 0 ); if ( xmpString != sonyTimeString ) { xmpObj->SetStructField ( kXMP_NS_DM, "startTimecode", kXMP_NS_DM, "timeValue", sonyTimeString, 0 ); std::string timeFormat; if ( clipSampleSize == 1 ) { // 24, 25, 40, 50, 60 switch ( clipSampleScale ) { case 24 : timeFormat = "24"; break; case 25 : timeFormat = "25"; break; case 50 : timeFormat = "50"; break; default : XMP_Assert ( false ); } timeFormat += "Timecode"; } else { // 23.976, 29.97, 59.94 XMP_Assert ( clipSampleSize == 1001 ); switch ( clipSampleScale ) { case 24000 : timeFormat = "23976"; break; case 30000 : timeFormat = "2997"; break; case 60000 : timeFormat = "5994"; break; default : XMP_Assert( false ); break; } timeFormat += dropFrame ? "DropTimecode" : "NonDropTimecode"; } xmpObj->SetStructField ( kXMP_NS_DM, "startTimecode", kXMP_NS_DM, "timeFormat", timeFormat, 0 ); } } } if ( digestFound || (! xmpObj->DoesPropertyExist ( kXMP_NS_DM, "CreateDate" )) ) { // Clip has date and time in the case of DT (otherwise date and time haven't been set). bool clipHasDate = ((hdvFileBlock.mDT[0] == 'D') && (hdvFileBlock.mDT[1] == 'T')); // Creation date if ( clipHasDate ) { // YYYY-MM-DDThh:mm:ssZ char date[256]; sprintf ( date, "%4d-%02d-%02dT%02d:%02d:%02dZ", hdvFileBlock.mFileNameYear + 2000, hdvFileBlock.mFileNameMonth, hdvFileBlock.mFileNameDay, hdvFileBlock.mFileNameHour, hdvFileBlock.mFileNameMinute, hdvFileBlock.mFileNameSecond ); XMP_StringPtr xmpDate = date; xmpObj->SetProperty ( kXMP_NS_XMP, "CreateDate", xmpDate, kXMP_DeleteExisting ); } } // Frame rate. if ( digestFound || (! xmpObj->DoesPropertyExist ( kXMP_NS_DM, "videoFrameRate" )) ) { if ( frameRate.size() != 0 ) { xmpString = frameRate; xmpObj->SetProperty ( kXMP_NS_DM, "videoFrameRate", xmpString, kXMP_DeleteExisting ); } } } catch ( ... ) { result = false; } return result; } // ReadIDXFile