Пример #1
0
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
Пример #2
0
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
Пример #3
0
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
Пример #5
0
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
Пример #6
0
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