void MPEG2_MetaHandler::UpdateFile ( bool doSafeUpdate ) { if ( ! this->needsUpdate ) return; XMP_Assert ( this->parent->UsesLocalIO() ); if ( this->parent->ioRef == 0 ) { XMP_Assert ( ! Host_IO::Exists ( this->sidecarPath.c_str() ) ); Host_IO::Create ( this->sidecarPath.c_str() ); this->parent->ioRef = XMPFiles_IO::New_XMPFiles_IO ( this->sidecarPath.c_str(), Host_IO::openReadWrite ); if ( this->parent->ioRef == 0 ) XMP_Throw ( "Failure opening MPEG-2 XMP file", kXMPErr_ExternalFailure ); } XMP_IO* fileRef = this->parent->ioRef; XMP_Assert ( fileRef != 0 ); XIO::ReplaceTextFile ( fileRef, this->xmpPacket, doSafeUpdate ); XMPFiles_IO* localFile = (XMPFiles_IO*)fileRef; localFile->Close(); delete localFile; this->parent->ioRef = 0; this->needsUpdate = false; } // MPEG2_MetaHandler::UpdateFile
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
void XMPFiles_IO::AbsorbTemp() { XMP_FILESIO_START XMP_Assert ( this->fileRef != Host_IO::noFileRef ); XMPFiles_IO * temp = this->derivedTemp; if ( temp == 0 ) { XMP_Throw ( "XMPFiles_IO::AbsorbTemp, no temp to absorb", kXMPErr_InternalFailure ); } XMP_Assert ( temp->isTemp ); this->Close(); temp->Close(); Host_IO::SwapData ( this->filePath.c_str(), temp->filePath.c_str() ); this->DeleteTemp(); this->fileRef = Host_IO::Open ( this->filePath.c_str(), Host_IO::openReadWrite ); this->currLength = Host_IO::Length ( this->fileRef ); this->currOffset = 0; XMP_FILESIO_END1 ( kXMPErrSev_FileFatal ) } // XMPFiles_IO::AbsorbTemp
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
void P2_MetaHandler::UpdateFile ( bool doSafeUpdate ) { if ( ! this->needsUpdate ) return; this->needsUpdate = false; // Make sure only called once. XMP_Assert ( this->parent->UsesLocalIO() ); // Update the internal legacy XML tree if we have one, and set the digest in the XMP. bool updateLegacyXML = false; P2_Clip* p2Clip = 0; XML_NodePtr clipMetadata = 0; if ( this->p2ClipManager.IsValidP2() ) { p2Clip=this->p2ClipManager.GetManagedClip(); clipMetadata = p2Clip->GetClipMetadataNode(); if ( clipMetadata != 0 ) { bool xmpFound; std::string xmpValue; XML_Node * xmlNode; xmpFound = this->xmpObj.GetLocalizedText ( kXMP_NS_DC, "title", "", "x-default", 0, &xmpValue, 0 ); if ( xmpFound && p2Clip->GetClipContentNode()) { xmlNode = this->ForceChildElement ( p2Clip->GetClipContentNode(), "ClipName", 3, false ); if ( xmpValue != xmlNode->GetLeafContentValue() ) { xmlNode->SetLeafContentValue ( xmpValue.c_str() ); updateLegacyXML = true; } } xmpFound = this->xmpObj.GetArrayItem ( kXMP_NS_DC, "creator", 1, &xmpValue, 0 ); if ( xmpFound ) { xmlNode = this->ForceChildElement ( clipMetadata , "Access", 3, false ); // "Creator" must be first child of "Access" node else Panasonic P2 Viewer gives an error. xmlNode = this->ForceChildElement ( xmlNode, "Creator", 4 , true); if ( xmpValue != xmlNode->GetLeafContentValue() ) { xmlNode->SetLeafContentValue ( xmpValue.c_str() ); updateLegacyXML = true; } } } // Half the startTimeCode frame number value in XML if require so std::string xmpStartTimeCode; bool isTimecodeExists = this->xmpObj.GetStructField(kXMP_NS_DM, "startTimecode", kXMP_NS_DM, "timeValue", &xmpStartTimeCode, 0); if (isTimecodeExists) { std::string frameFormat; this->xmpObj.GetStructField(kXMP_NS_DM, "startTimecode", kXMP_NS_DM, "timeFormat", &frameFormat, 0); if (frameFormat == "50Timecode" || frameFormat == "5994DropTimecode" || frameFormat == "5994NonDropTimecode") { p2Clip = this->p2ClipManager.GetManagedClip(); XMP_StringPtr p2NS = p2Clip->GetP2RootNode()->ns.c_str(); XML_NodePtr legacyVideoContext = p2Clip->GetEssenceListNode(); if (legacyVideoContext != 0) { legacyVideoContext = legacyVideoContext->GetNamedElement(p2NS, "Video"); XML_NodePtr legacyProp = legacyVideoContext->GetNamedElement(p2NS, "StartTimecode"); if ((legacyProp != 0) && legacyProp->IsLeafContentNode()) { AdjustTimeCode( xmpStartTimeCode, true ); if (xmpStartTimeCode != legacyProp->GetLeafContentValue()) { legacyProp->SetLeafContentValue(xmpStartTimeCode.c_str()); updateLegacyXML = true; } } } } } std::string newDigest; this->p2ClipManager.GetManagedClip()->CreateDigest ( &newDigest ); this->xmpObj.SetStructField ( kXMP_NS_XMP, "NativeDigests", kXMP_NS_XMP, "P2", newDigest.c_str(), kXMP_DeleteExisting ); } this->xmpObj.SerializeToBuffer ( &this->xmpPacket, this->GetSerializeOptions() ); // ----------------------------------------------------------------------- // Update the XMP file first, don't let legacy XML failures block the XMP. std::string xmpPath; this->MakeClipFilePath ( &xmpPath, ".XMP" ); bool haveXMP = Host_IO::Exists ( xmpPath.c_str() ); if ( ! haveXMP ) { XMP_Assert ( this->parent->ioRef == 0 ); Host_IO::Create ( xmpPath.c_str() ); this->parent->ioRef = XMPFiles_IO::New_XMPFiles_IO ( xmpPath.c_str(), Host_IO::openReadWrite ); if ( this->parent->ioRef == 0 ) XMP_Throw ( "Failure opening P2 XMP file", kXMPErr_ExternalFailure ); } XMP_IO* xmpFile = this->parent->ioRef; XMP_Assert ( xmpFile != 0 ); XIO::ReplaceTextFile ( xmpFile, this->xmpPacket, (haveXMP & doSafeUpdate) ); // -------------------------------------------- // Now update the legacy XML file if necessary. if ( updateLegacyXML ) { std::string legacyXML, xmlPath; /*bug # 3217688: xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance namespace must be defined at the root node "P2Main" in legacy XML else Panasonic P2 Viewer gives an error. So we are adding a dummy attribute with this namespace to clipContent/clipMetadata (whichever is non-null) before serializing the XML tree. We are also undoing it below after serialization.*/ XML_Node *parentNode = AddXSINamespace(p2Clip->GetClipContentNode(), clipMetadata); p2Clip->SerializeP2ClipContent ( legacyXML ); if(parentNode){ // Remove the dummy attribute added to clipContent/clipMetadata. delete parentNode->attrs[parentNode->attrs.size()-1]; parentNode->attrs.pop_back(); } this->MakeClipFilePath ( &xmlPath, ".XML" ); bool haveXML = Host_IO::Exists ( xmlPath.c_str() ); if ( ! haveXML ) Host_IO::Create ( xmlPath.c_str() ); Host_IO::FileRef hostRef = Host_IO::Open ( xmlPath.c_str(), Host_IO::openReadWrite ); if ( hostRef == Host_IO::noFileRef ) XMP_Throw ( "Failure opening P2 legacy XML file", kXMPErr_ExternalFailure ); XMPFiles_IO origXML ( hostRef, xmlPath.c_str(), Host_IO::openReadWrite ); XIO::ReplaceTextFile ( &origXML, legacyXML, (haveXML & doSafeUpdate) ); origXML.Close(); } } // P2_MetaHandler::UpdateFile