/* Copyright (c) MediaArea.net SARL. All Rights Reserved. * * Use of this source code is governed by a BSD-style license that can * be found in the License.html file in the root of the source tree. */ //--------------------------------------------------------------------------- // Pre-compilation #include "MediaInfo/PreComp.h" #ifdef __BORLANDC__ #pragma hdrstop #endif //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- #include "MediaInfo/Setup.h" //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- #if defined(MEDIAINFO_DCP_YES) //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- #include "MediaInfo/Multiple/File_DcpCpl.h" #include "MediaInfo/Multiple/File_DcpAm.h" #include "MediaInfo/MediaInfo.h" #include "MediaInfo/MediaInfo_Config_MediaInfo.h" #include "MediaInfo/Multiple/File__ReferenceFilesHelper.h" #include "MediaInfo/XmlUtils.h" #if defined(MEDIAINFO_REFERENCES_YES) #include "ZenLib/File.h" #endif //defined(MEDIAINFO_REFERENCES_YES) #include "ZenLib/FileName.h" #include "tinyxml2.h" #include #include using namespace tinyxml2; using namespace std; //--------------------------------------------------------------------------- namespace MediaInfoLib { //--------------------------------------------------------------------------- extern string Jpeg2000_Rsiz(int16u Rsiz); //--------------------------------------------------------------------------- //*************************************************************************** // Constructor/Destructor //*************************************************************************** //--------------------------------------------------------------------------- File_DcpCpl::File_DcpCpl() :File__Analyze() { #if MEDIAINFO_EVENTS ParserIDs[0]=MediaInfo_Parser_DcpCpl; StreamIDs_Width[0]=sizeof(size_t)*2; #endif //MEDIAINFO_EVENTS #if MEDIAINFO_DEMUX Demux_EventWasSent_Accept_Specific=true; #endif //MEDIAINFO_DEMUX //PKL PKL_Pos = (size_t)-1; } //--------------------------------------------------------------------------- static bool IsSmpteSt2067_2(const char* Ns) { return Ns && (!strcmp(Ns, "http://www.smpte-ra.org/schemas/2067-2/2013") || !strcmp(Ns, "http://www.smpte-ra.org/schemas/2067-2/XXXX")); //Some muxers use XXXX instead of year } //--------------------------------------------------------------------------- static bool IsSmpteSt2067_3(const char* Ns) { return Ns && (!strcmp(Ns, "http://www.smpte-ra.org/schemas/2067-3/2013") || !strcmp(Ns, "http://www.smpte-ra.org/schemas/2067-3/2016") || !strcmp(Ns, "http://www.smpte-ra.org/schemas/2067-3/XXXX")); //Some muxers use XXXX instead of year } //*************************************************************************** // Buffer - File header //*************************************************************************** //--------------------------------------------------------------------------- bool File_DcpCpl::FileHeader_Begin() { XMLDocument document; if (!FileHeader_Begin_XML(document)) return false; XMLElement* Root=document.FirstChildElement(); const char *NameSpace; if (!Root || strcmp(LocalName(Root, NameSpace), "CompositionPlaylist")) { Reject("DcpCpl"); return false; } bool IsDcp=false, IsImf=false; if (!strcmp(NameSpace, "http://www.digicine.com/PROTO-ASDCP-CPL-20040511#") || !strcmp(NameSpace, "http://www.smpte-ra.org/schemas/429-7/2006/CPL")) { IsDcp=true; } else if (IsSmpteSt2067_3(NameSpace)) { IsImf=true; } else { Reject("DcpCpl"); return false; } Accept("DcpCpl"); Fill(Stream_General, 0, General_Format, IsDcp?"DCP CPL":"IMF CPL"); #if defined(MEDIAINFO_REFERENCES_YES) Config->File_ID_OnlyRoot_Set(false); ReferenceFiles_Accept(this, Config); vector AssetCountPerReel; //Parsing main elements for (XMLElement* CompositionPlaylist_Item=Root->FirstChildElement(); CompositionPlaylist_Item; CompositionPlaylist_Item=CompositionPlaylist_Item->NextSiblingElement()) { const char* CompositionPlaylist_Item_Value=CompositionPlaylist_Item->Value(); if (!CompositionPlaylist_Item_Value) continue; //CompositionTimecode if (IsImf && MatchQName(CompositionPlaylist_Item, "CompositionTimecode", NameSpace)) { sequence* Sequence=new sequence; Sequence->StreamKind=Stream_Other; Sequence->Infos["Type"]=__T("Time code"); Sequence->Infos["Format"]=__T("CPL TC"); Sequence->Infos["TimeCode_Stripped"]=__T("Yes"); bool IsDropFrame=false; for (XMLElement* CompositionTimecode_Item=CompositionPlaylist_Item->FirstChildElement(); CompositionTimecode_Item; CompositionTimecode_Item=CompositionTimecode_Item->NextSiblingElement()) { const char* Text=CompositionTimecode_Item->GetText(); if (!Text) continue; const char *CtItemNs, *CtItemName = LocalName(CompositionTimecode_Item, CtItemNs); if (!CtItemNs || strcmp(CtItemNs, NameSpace)) continue; // item has wrong namespace //TimecodeDropFrame if (!strcmp(CtItemName, "TimecodeDropFrame")) { if (strcmp(Text, "") && strcmp(Text, "0")) IsDropFrame=true; } //TimecodeRate if (!strcmp(CtItemName, "TimecodeRate")) Sequence->Infos["FrameRate"].From_UTF8(Text); //TimecodeStartAddress if (!strcmp(CtItemName, "TimecodeStartAddress")) Sequence->Infos["TimeCode_FirstFrame"].From_UTF8(Text); } //Adaptation if (IsDropFrame) { std::map::iterator Info=Sequence->Infos.find("TimeCode_FirstFrame"); if (Info!=Sequence->Infos.end() && Info->second.size()>=11 && Info->second[8]!=__T(';')) Info->second[8]=__T(';'); } Sequence->StreamID=ReferenceFiles->Sequences_Size()+1; ReferenceFiles->AddSequence(Sequence); Stream_Prepare(Stream_Other); Fill(Stream_Other, StreamPos_Last, Other_ID, Sequence->StreamID); for (std::map::iterator Info=Sequence->Infos.begin(); Info!=Sequence->Infos.end(); ++Info) Fill(Stream_Other, StreamPos_Last, Info->first.c_str(), Info->second); } #if MEDIAINFO_ADVANCED //EssenceDescriptorList if (IsImf && !strcmp(CompositionPlaylist_Item_Value, "EssenceDescriptorList")) { for (XMLElement* EssenceDescriptorList_Item=CompositionPlaylist_Item->FirstChildElement(); EssenceDescriptorList_Item; EssenceDescriptorList_Item=EssenceDescriptorList_Item->NextSiblingElement()) { const char* EssenceDescriptorList_Item_Value=EssenceDescriptorList_Item->Value(); if (!EssenceDescriptorList_Item_Value) continue; //TimecodeDropFrame if (!strcmp(EssenceDescriptorList_Item_Value, "EssenceDescriptor")) { string Id; descriptor* Descriptor=new descriptor; for (XMLElement* EssenceDescriptor_Item=EssenceDescriptorList_Item->FirstChildElement(); EssenceDescriptor_Item; EssenceDescriptor_Item=EssenceDescriptor_Item->NextSiblingElement()) { const char* EssenceDescriptor_Item_Value=EssenceDescriptor_Item->Value(); const char* EssenceDescriptor_Item_Text=EssenceDescriptor_Item->GetText(); if (!EssenceDescriptor_Item_Value) continue; //Id if (EssenceDescriptor_Item_Text && !strcmp(EssenceDescriptor_Item_Value, "Id")) Id=EssenceDescriptor_Item_Text; //CDCIDescriptor if (!strcmp(EssenceDescriptor_Item_Value, "m:RGBADescriptor") || !strcmp(EssenceDescriptor_Item_Value, "m:CDCIDescriptor")) { for (XMLElement* Descriptor_Item=EssenceDescriptor_Item->FirstChildElement(); Descriptor_Item; Descriptor_Item=Descriptor_Item->NextSiblingElement()) { const char* Descriptor_Item_Value=Descriptor_Item->Value(); if (!Descriptor_Item_Value) continue; //SubDescriptors if (!strcmp(Descriptor_Item_Value, "m:SubDescriptors")) { for (XMLElement* SubDescriptors_Item=Descriptor_Item->FirstChildElement(); SubDescriptors_Item; SubDescriptors_Item=SubDescriptors_Item->NextSiblingElement()) { const char* SubDescriptors_Item_Value=SubDescriptors_Item->Value(); if (!SubDescriptors_Item_Value) continue; descriptor* SubDescriptor=new descriptor; //JPEG2000PictureSubDescriptor if (!strcmp(SubDescriptors_Item_Value, "m:JPEG2000PictureSubDescriptor")) { for (XMLElement* JPEG2000PictureSubDescriptor_Item=SubDescriptors_Item->FirstChildElement(); JPEG2000PictureSubDescriptor_Item; JPEG2000PictureSubDescriptor_Item=JPEG2000PictureSubDescriptor_Item->NextSiblingElement()) { const char* JPEG2000PictureSubDescriptor_Item_Value=JPEG2000PictureSubDescriptor_Item->Value(); const char* JPEG2000PictureSubDescriptor_Item_Text=JPEG2000PictureSubDescriptor_Item->GetText(); if (!JPEG2000PictureSubDescriptor_Item_Value || !JPEG2000PictureSubDescriptor_Item_Text) continue; //Xsiz if (!strcmp(JPEG2000PictureSubDescriptor_Item_Value, "m:Rsiz")) { SubDescriptor->Jpeg2000_Rsiz=(int16u)atoi(JPEG2000PictureSubDescriptor_Item_Text); } } } Descriptor->SubDescriptors.push_back(SubDescriptor); } } } } } if (!Id.empty()) EssenceDescriptorList[Id]=Descriptor; else delete Descriptor; // Can not be associated } } } #endif //MEDIAINFO_ADVANCED //ReelList / SegmentList if (MatchQName(CompositionPlaylist_Item, IsDcp?"ReelList":"SegmentList", NameSpace)) { for (XMLElement* ReelList_Item=CompositionPlaylist_Item->FirstChildElement(); ReelList_Item; ReelList_Item=ReelList_Item->NextSiblingElement()) { //Reel if (MatchQName(ReelList_Item, IsDcp?"Reel":"Segment", NameSpace)) { size_t AssetCount=0; for (XMLElement* Reel_Item=ReelList_Item->FirstChildElement(); Reel_Item; Reel_Item=Reel_Item->NextSiblingElement()) { //AssetList if (MatchQName(Reel_Item, IsDcp?"AssetList":"SequenceList", NameSpace)) { for (XMLElement* AssetList_Item=Reel_Item->FirstChildElement(); AssetList_Item; AssetList_Item=AssetList_Item->NextSiblingElement()) { const char *AlItemNs, *AlItemName = LocalName(AssetList_Item, AlItemNs); if (!AlItemNs) continue; //File //if ((IsDcp && (!strcmp(AssetList_Item->Value(), "MainPicture") || !strcmp(AssetList_Item->Value(), "MainSound"))) // || (IsImf && (!strcmp(AssetList_Item->Value(), "cc:MainImageSequence") || !strcmp(AssetList_Item->Value(), "cc:MainImage")))) if (strcmp(AlItemName, "MarkerSequence")) //Ignoring MarkerSequence for the moment. TODO: check what to do with MarkerSequence { sequence* Sequence=new sequence; Ztring Asset_Id; if (IsDcp && !strcmp(NameSpace, AlItemNs)) { if (!strcmp(AlItemName, "MainPicture")) Sequence->StreamKind=Stream_Video; else if (!strcmp(AlItemName, "MainSound")) Sequence->StreamKind=Stream_Audio; else if (!strcmp(AlItemName, "MainSubtitle")) Sequence->StreamKind=Stream_Text; } else if (IsImf && IsSmpteSt2067_2(AlItemNs)) { if (!strcmp(AlItemName, "MainImageSequence")) Sequence->StreamKind=Stream_Video; else if (!strcmp(AlItemName, "MainAudioSequence")) Sequence->StreamKind=Stream_Audio; } for (XMLElement* File_Item=AssetList_Item->FirstChildElement(); File_Item; File_Item=File_Item->NextSiblingElement()) { //Id if (MatchQName(File_Item, "Id", NameSpace) && Asset_Id.empty()) Asset_Id.From_UTF8(File_Item->GetText()); //ResourceList if (IsImf && MatchQName(File_Item, "ResourceList", NameSpace)) { for (XMLElement* ResourceList_Item=File_Item->FirstChildElement(); ResourceList_Item; ResourceList_Item=ResourceList_Item->NextSiblingElement()) { //Resource if (MatchQName(ResourceList_Item, "Resource", NameSpace)) { Ztring Resource_Id; resource* Resource=new resource; for (XMLElement* Resource_Item=ResourceList_Item->FirstChildElement(); Resource_Item; Resource_Item=Resource_Item->NextSiblingElement()) { const char* ResText=Resource_Item->GetText(); if (!ResText) continue; const char *ResItemNs, *ResItemName = LocalName(Resource_Item, ResItemNs); if (!ResItemNs || strcmp(ResItemNs, NameSpace)) continue; // item has wrong namespace //EditRate if (!strcmp(ResItemName, "EditRate")) { Resource->EditRate=atof(ResText); const char* EditRate2=strchr(ResText, ' '); if (EditRate2!=NULL) { float64 EditRate2f=atof(EditRate2); if (EditRate2f) Resource->EditRate/=EditRate2f; } } //EntryPoint if (!strcmp(ResItemName, "EntryPoint")) { Resource->IgnoreEditsBefore=atoi(ResText); if (Resource->IgnoreEditsAfter!=(int64u)-1) Resource->IgnoreEditsAfter+=Resource->IgnoreEditsBefore; } //Id if (!strcmp(ResItemName, "Id") && Resource_Id.empty()) Resource_Id.From_UTF8(ResText); //SourceDuration if (!strcmp(ResItemName, "SourceDuration")) Resource->IgnoreEditsAfter=Resource->IgnoreEditsBefore+atoi(ResText); #if MEDIAINFO_ADVANCED //SourceEncoding if (!strcmp(ResItemName, "SourceEncoding")) Resource->SourceEncodings.push_back(ResText); #endif //MEDIAINFO_ADVANCED //TrackFileId if (!strcmp(ResItemName, "TrackFileId")) Resource->FileNames.push_back(Ztring().From_UTF8(ResText)); } if (Resource->FileNames.empty()) Resource->FileNames.push_back(Resource_Id); Sequence->AddResource(Resource); } } } } if (Sequence->Resources.empty()) { resource* Resource=new resource; Resource->FileNames.push_back(Asset_Id); Sequence->AddResource(Resource); } Sequence->StreamID=ReferenceFiles->Sequences_Size()+1; ReferenceFiles->AddSequence(Sequence); AssetCount++; } } } } AssetCountPerReel.push_back(AssetCount); } } } } ReferenceFiles->DetectSameReels(AssetCountPerReel); //Getting files names FileName Directory(File_Name); Ztring DirPath = Directory.Path_Get(); if (!DirPath.empty()) DirPath += PathSeparator; Ztring Assetmap_FileName=DirPath+__T("ASSETMAP.xml"); bool IsOk=false; if (File::Exists(Assetmap_FileName)) IsOk=true; else { Assetmap_FileName.resize(Assetmap_FileName.size()-4); //Old fashion, without ".xml" if (File::Exists(Assetmap_FileName)) IsOk=true; } if (IsOk) { MediaInfo_Internal MI; MI.Option(__T("File_KeepInfo"), __T("1")); Ztring ParseSpeed_Save=MI.Option(__T("ParseSpeed_Get"), __T("")); Ztring Demux_Save=MI.Option(__T("Demux_Get"), __T("")); MI.Option(__T("ParseSpeed"), __T("0")); MI.Option(__T("Demux"), Ztring()); MI.Option(__T("File_IsReferenced"), __T("1")); size_t MiOpenResult=MI.Open(Assetmap_FileName); MI.Option(__T("ParseSpeed"), ParseSpeed_Save); //This is a global value, need to reset it. TODO: local value MI.Option(__T("Demux"), Demux_Save); //This is a global value, need to reset it. TODO: local value if (MiOpenResult && (MI.Get(Stream_General, 0, General_Format)==__T("DCP AM") || MI.Get(Stream_General, 0, General_Format)==__T("IMF AM"))) { MergeFromAm(((File_DcpAm*)MI.Info)->Streams); } } ReferenceFiles->FilesForStorage=true; #if MEDIAINFO_ADVANCED for (std::map::iterator EssenceDescriptor = EssenceDescriptorList.begin(); EssenceDescriptor != EssenceDescriptorList.end(); ++EssenceDescriptor) for (std::vector::iterator SubDescriptor = EssenceDescriptor->second->SubDescriptors.begin(); SubDescriptor != EssenceDescriptor->second->SubDescriptors.end(); ++SubDescriptor) ReferenceFiles->UpdateMetaDataFromSourceEncoding(EssenceDescriptor->first, "Format_Profile", Jpeg2000_Rsiz((*SubDescriptor)->Jpeg2000_Rsiz)); #endif //MEDIAINFO_ADVANCED #endif //MEDIAINFO_REFERENCES_YES //All should be OK... Element_Offset=File_Size; return true; } //*************************************************************************** // Infos //*************************************************************************** //--------------------------------------------------------------------------- #if defined(MEDIAINFO_REFERENCES_YES) void File_DcpCpl::MergeFromAm (File_DcpPkl::streams &StreamsToMerge) { for (File_DcpPkl::streams::iterator StreamToMerge=StreamsToMerge.begin(); StreamToMerge!=StreamsToMerge.end(); ++StreamToMerge) if (!StreamToMerge->ChunkList.empty()) // Note: ChunkLists with more than 1 file are not yet supported) ReferenceFiles->UpdateFileName(Ztring().From_UTF8(StreamToMerge->Id), Ztring().From_UTF8(StreamToMerge->ChunkList[0].Path)); } #endif //MEDIAINFO_REFERENCES_YES } //NameSpace #endif //MEDIAINFO_DCP_YES