//! Scan a metadata object for strong references in sub-objects and add those to this partition void Partition::AddMetadataSubs(MDObjectPtr &NewObject, bool ForceFirst) { MDObjectULList::iterator it = NewObject->begin(); MDObjectULList::iterator itend = NewObject->end(); while(it != itend) { if((*it).second->GetRefType() == DICT_REF_STRONG) { MDObjectPtr Link = (*it).second->GetLink(); if(Link) { AddMetadata(Link, ForceFirst); // Prevent the new item being top-level (which it may be as we are not added yet) // DRAGONS: There is surely a better way than this!! TopLevelMetadata.remove(Link); } } else if(!((*it).second->empty())) { AddMetadataSubs((*it).second, ForceFirst); } it++; } }
/*! DRAGONS: We use the current UTF16String GetString trait to ensure that we always have the correct handling, * even if the user wants these strings handled differently (i.e. we do it their way!) */ std::list<std::string> SplitStringArray(const MDObjectPtr &Array) { std::list<std::string> Ret; // Quit early if an invalid parameter if(!Array) return Ret; // Build a working string static MDTypePtr ValType = MDType::Find("UTF16String"); MDObjectPtr Value = ValType ? new MDObject(ValType) : NULL; if(!Value) { error("Can't build UTF16String value required by SplitStringArray() - need this type to be defined in the dictionary file\n"); return Ret; } // Assemble the data value (which may be an array of sub-values) DataChunkPtr Data = Array->PutData(); // Get a pointer to the start of the data UInt8 *pData = Data->Data; // The number of bytes of data to split size_t BytesLeft = Data->Size; while(BytesLeft > 1) { // Find the end of the current string size_t Len = 0; UInt8 *p = pData; while(BytesLeft > 1) { BytesLeft -= 2; Len += 2; UInt16 Char = GetU16(p); p += 2; // End when we find a null if(Char == 0) break; } // Copy this string to the working value Value->SetValue(pData, Len); // Read out the traits-formatted version of the string Ret.push_back(Value->GetString()); // Move the pointer forward to the next value after the string terminator pData = p; } return Ret; }
/*! DRAGONS: We use the current UTF16String SetString trait to ensure that we always have the correct handling, * even if the user wants these strings handled differently (i.e. we do it their way!) */ void SetStringArray(MDObjectPtr &Array, const std::list<std::string> &Strings) { // Abort if we are send a NULL pointer if(!Array) return; // Don't bother if we have nothing to do if(Strings.empty()) return; // Build a working string static MDTypePtr ValType = MDType::Find("UTF16String"); MDObjectPtr Value = ValType ? new MDObject(ValType) : NULL; if(!Value) { error("Can't build UTF16String value required by SetStringArray() - need this type to be defined in the dictionary file\n"); return; } // Get a buffer in which to build the final string array, make it quite granular as it will keep growing DataChunkPtr Buffer = new DataChunk(); Buffer->SetGranularity(16 * 1024); std::list<std::string>::const_iterator it = Strings.begin(); while(it != Strings.end()) { Value->SetString(*it); DataChunkPtr ThisString = Value->PutData(); Buffer->Append(ThisString); // Add terminator if required, i.e. if the sub-string we just added did not end in a zero UInt8 *p = &ThisString->Data[ThisString->Size]; // Terminate if either of the last two bytes in the current buffer is non-zero - or if the string was too short to have a terminator if((ThisString->Size) < 2 || ((*(--p) != 0) || (*(--p) != 0))) { const UInt8 Term[2] = { 0, 0}; Buffer->Set(2, Term, Buffer->Size); } it++; } // Set the value from this buffer Array->SetValue(Buffer); }
//! Read any index table segments from a file MDObjectListPtr mxflib::Partition::ReadIndex(MXFFilePtr File, UInt64 Size) { MDObjectListPtr Ret = new MDObjectList; while(Size) { UInt64 Location = File->Tell(); UInt64 Bytes; MDObjectPtr NewIndex = File->ReadObject(NULL); if(NewIndex) { if((NewIndex->Name() == "IndexTableSegment") || (NewIndex->Name() == "V10IndexTableSegment")) { Ret->push_back(NewIndex); Bytes = File->Tell() - Location; } else if( NewIndex->IsA(KLVFill_UL) ) { // Skip over the filler Bytes = File->Tell() - Location; } else { error("Expected to find an IndexTableSegment - found %s at %s\n", NewIndex->FullName().c_str(), NewIndex->GetSourceLocation().c_str()); break; } } else { error("Error reading IndexTableSegment at 0x%s in %s\n", Int64toHexString(Location,8).c_str(), File->Name.c_str()); break; } if(Bytes > Size) break; Size -= Bytes; } return Ret; }
/*! \note This call will modify properties SampleRate, DataStart and DataSize */ MDObjectPtr mxflib::WAVE_PCM_EssenceSubParser::BuildWaveAudioDescriptor(FileHandle InFile, UInt64 Start /*=0*/) { const unsigned int ID_RIFF = 0x52494646; //! "RIFF" const unsigned int ID_fmt = 0x666d7420; //! "fmt " const unsigned int ID_data = 0x64617461; //! "data" MDObjectPtr Ret; FileSeek(InFile, Start); U32Pair Header = ReadRIFFHeader(InFile); // Can't build a descriptor if it isn't a RIFF file! if(Header.first != ID_RIFF) return Ret; if(Header.second < 4) return Ret; // Read the RIFF file type (always 4 bytes) DataChunkPtr ChunkData = FileReadChunk(InFile, 4); // Can't build a descriptor if it isn't a WAVE file! if(memcmp(ChunkData->Data, "WAVE", 4) != 0) return Ret; // Scan the chunks within the RIFF file // DRAGONS: To do this properly we would check the file size in the RIFF chunk // DRAGONS: "LIST" chunks are "sets" and are not yet supported for(;;) { Header = ReadRIFFHeader(InFile); // End of file? if((Header.first == 0) && (Header.second == 0)) break; if(Header.first == ID_fmt) { ChunkData = FileReadChunk(InFile, Header.second); if(ChunkData->Size < 16) return Ret; UInt16 AudioFormat = GetU16_LE(&ChunkData->Data[0]); if(AudioFormat != 1) return Ret; Ret = new MDObject(WaveAudioDescriptor_UL); if(!Ret) return Ret; // Set the sample rate char Buffer[32]; SampleRate = GetU32_LE(&ChunkData->Data[4]); sprintf(Buffer, "%d/1", SampleRate); Ret->SetString(SampleRate_UL, Buffer); Ret->SetString(AudioSamplingRate_UL, Buffer); // Must assume not locked! Ret->SetUInt(Locked_UL, 0); // Set channel count UInt16 Chan = GetU16_LE(&ChunkData->Data[2]); Ret->SetUInt(ChannelCount_UL, Chan); // Set quantization bits UInt16 Quant = GetU16_LE(&ChunkData->Data[14]); Ret->SetUInt(QuantizationBits_UL, Quant); // Calculate the number of bytes per sample SampleSize = ((Quant+7) / 8) * Chan; // Set the block alignment Ret->SetUInt(BlockAlign_UL, GetU16_LE(&ChunkData->Data[12])); // Set the byte-rate Ret->SetUInt(AvgBps_UL, GetU32_LE(&ChunkData->Data[8])); } else if(Header.first == ID_data) { // Record the location of the audio data DataStart = FileTell(InFile); DataSize = Header.second; // ...and skip the chunk value FileSeek(InFile, FileTell(InFile) + Header.second); } else { // Skip the chunk value FileSeek(InFile, FileTell(InFile) + Header.second); } } return Ret; }
/*! Note that any strongly linked objects are also added */ void mxflib::Partition::AddMetadata(MDObjectPtr NewObject, bool ForceFirst /*=false*/) { // Start out without a target bool has_target = false; // Start out not (strong) reffed bool linked = false; // Add us to the list of all items - done last if forcing first as the child items will get added first if(!ForceFirst) AllMetadata.push_back(NewObject); // Add this object to the ref target list if it is one. At the same time any objects // linked from this object (before this function was called) are added as well // Note: although nothing currently does it it is theoretically possible to // have more than one target entry in a set MDObjectULList::iterator it = NewObject->begin(); while(it != NewObject->end()) { ClassRef RefType = (*it).second->GetRefType(); if(RefType == ClassRefTarget) { if((*it).second->Value->GetData().Size != 16) { error("Metadata Object \"%s/%s\" should be a reference target (a UUID), but has size %d\n", NewObject->Name().c_str(), (*it).second->Name().c_str(), (*it).second->Value->GetData().Size); } else { has_target = true; UUIDPtr ID = new UUID((*it).second->Value->PutData()->Data); RefTargets.insert(std::map<UUID, MDObjectPtr>::value_type(*ID, NewObject)); // Try and satisfy all refs to this set for(;;) { std::multimap<UUID, MDObjectPtr>::iterator mit = UnmatchedRefs.find(*ID); // Exit when no more refs to this object if(mit == UnmatchedRefs.end()) break; // Sanity check! if((*mit).second->GetLink()) { error("Internal error - %s at 0x%s in UnmatchedRefs but already linked!\n", (*mit).second->FullName().c_str() , Int64toHexString((*mit).second->GetLocation(), 8).c_str()); } // Make the link (*mit).second->SetLink(NewObject); // If we are the tagert of a strong ref we won't get added to the top level if((*mit).second->GetRefType() == DICT_REF_STRONG) linked = true; // Remove from the unmatched refs map UnmatchedRefs.erase(mit); // loop for any more refs to this set } } } else if(RefType == ClassRefStrong) { MDObjectPtr Link = (*it).second->GetLink(); if(Link) { AddMetadata(Link, ForceFirst); // Prevent the new item being top-level (which it may be as we are not added yet) // DRAGONS: There is surely a better way than this!! TopLevelMetadata.remove(Link); } // If this item is not a link, it may contain links else if((*it).second->size()) { MDObject::iterator subit = (*it).second->begin(); while(subit != (*it).second->end()) { Link = (*subit).second->GetLink(); if(Link) { AddMetadata(Link, ForceFirst); // Prevent the new item being top-level (which it may be as we are not added yet) // DRAGONS: There is surely a better way than this!! TopLevelMetadata.remove(Link); } subit++; } } } else if(!((*it).second->empty())) { AddMetadataSubs((*it).second, ForceFirst); } it++; } // Add any forced-first items after thier children if(ForceFirst) AllMetadata.push_front(NewObject); // If we are not yet (strong) reffed then we are top level if(!linked) { if(ForceFirst) TopLevelMetadata.push_front(NewObject); else TopLevelMetadata.push_back(NewObject); } // Satisfy, or record as un-matched, all outgoing references ProcessChildRefs(NewObject); }
/*! \note The value of "Size" does not include the size of any filler before * the primer, but the return value does * \return The number of bytes read (<b>including</b> any preceeding filler) */ Length mxflib::Partition::ReadMetadata(MXFFilePtr File, Length Size) { Length Bytes = 0; Length FillerBytes = 0; // Clear any existing metadata, including the primer ClearMetadata(false); // Quick return for NULL metadata if(Size == 0) return 0; // Record the position of the current item Position Location = File->Tell(); // Check for a leading filler item { ULPtr FirstUL = File->ReadKey(); if(!FirstUL) { error("Error reading first KLV after %s at 0x%s in %s\n", FullName().c_str(), Int64toHexString(GetLocation(),8).c_str(), GetSource().c_str()); return 0; } if(FirstUL->Matches(KLVFill_UL)) { // Skip over the filler, recording how far we went Position NewLocation = File->ReadBER(); NewLocation += File->Tell(); FillerBytes = NewLocation - Location; Location = NewLocation; } } // Sanity check the size for this platform // TODO: Should we fix this so that we can read larger metadata - but if so where in memory would we put it if its > 4GB on a 32-bit machine? if((sizeof(size_t) < 8) && Size > 0xffffffff) { error("Maximum read size on this platform is 4Gbytes - However, requested to read metadata at 0x%s which has size of 0x%s\n", Int64toHexString(Location,8).c_str(), Int64toHexString(Size,8).c_str()); return 0; } // Read enough bytes for the metadata File->Seek(Location); DataChunkPtr Data = File->Read(static_cast<size_t>(Size)); if(Data->Size != static_cast<size_t>(Size)) { error("Header Metadata starting at 0x%s should contain 0x%s bytes, but only 0x%s could be read\n", Int64toHexString(Location,8).c_str(), Int64toHexString(Size,8).c_str(), Int64toHexString(Data->Size,8).c_str()); Size = static_cast<Length>(Data->Size); } // Start of data buffer const UInt8 *BuffPtr = Data->Data; while(Size) { Length BytesAtItemStart = Bytes; if(Size < 16) { error("Less than 16-bytes of header metadata available after reading 0x%s bytes at 0x%s in file \"%s\"\n", Int64toHexString(Bytes, 8).c_str(), Int64toHexString(File->Tell(),8).c_str(), File->Name.c_str() ); break; } /* // Sanity check the keys if((BuffPtr[0] != 6) || (BuffPtr[1] != 0x0e)) { error("Invalid KLV key found at 0x%s in file \"%s\"\n", Int64toHexString(File->Tell(),8).c_str(), File->Name.c_str() ); break; } */ // Build an object (it may come back as an "unknown") ULPtr NewUL = new UL(BuffPtr); /* If we are loading metadictionaries, we do so when we first read the Preface key */ if(Feature(FeatureLoadMetadict)) { if(NewUL->Matches(Preface_UL)) { LoadMetadict(); } } MDObjectPtr NewItem = new MDObject(NewUL); mxflib_assert(NewItem); BuffPtr += 16; Size -= 16; Bytes += 16; if(Size < 1) { error("Incomplete BER length at 0x%s in file \"%s\"\n", Int64toHexString(File->Tell(),8).c_str(), File->Name.c_str() ); break; } Length Len = *BuffPtr++; Size--; Bytes++; if(Len >= 0x80) { UInt32 i = (UInt32)Len & 0x7f; if(Size < i) { error("Incomplete BER length at 0x%s in \"%s\"\n", Int64toHexString(File->Tell(),8).c_str(), File->Name.c_str() ); break; } Len = 0; while(i--) { Len = ((Len<<8) + *(BuffPtr++)); Size--; Bytes++; } } // DRAGONS: KLV Size limit!! if(Len > 0xffffffff) { error("Current implementation KLV size limit of 0xffffffff bytes exceeded at 0x%s in file \"%s\"\n", Int64toHexString(Location + Bytes,8).c_str(), File->Name.c_str() ); break; } if(Size < Len) { error("KLV length is %s but available data size is only %s after reading 0x%s of header metadata at 0x%s in \"%s\"\n", UInt64toString(Len).c_str(), UInt64toString(Size).c_str(), Int64toHexString(Bytes, 8).c_str(), Int64toHexString(Location + Bytes,8).c_str(), File->Name.c_str() ); // Try reading what we have Len = Size; } // Check for the primer until we have found it if(!PartitionPrimer) { if(NewItem->IsA(Primer_UL)) { PartitionPrimer = new Primer; UInt32 ThisBytes = PartitionPrimer->ReadValue(BuffPtr, (UInt32)Len); Size -= ThisBytes; Bytes += ThisBytes; BuffPtr += ThisBytes; // Skip further processing for the primer continue; } } // Skip any filler items if(NewItem->IsA(KLVFill_UL)) { Size -= Len; Bytes += Len; BuffPtr += Len; // Don't add the filler continue; } if(Len) { NewItem->SetParent(File, BytesAtItemStart + Location,(UInt32)( Bytes - BytesAtItemStart)); NewItem->ReadValue(BuffPtr,(UInt32) Len, PartitionPrimer); // Skip total length, not just the length actually consumed Size -= Len; Bytes += Len; BuffPtr += Len; } AddMetadata(NewItem); } return Bytes + FillerBytes; }
//! Load any metadictionaties that are in the list of currently loaded objects bool mxflib::Partition::LoadMetadict(void) { bool Ret = true; /* Search for the metadictionary */ MDObjectList::iterator it = AllMetadata.begin(); while(it != AllMetadata.end()) { if((*it)->IsA(MetaDictionary_UL)) { /* We need to set up a symbol space for this metadictionary At the moment we attach it to one named with the instance ID of this metadictionary item */ std::string SymSpaceName = (*it)->GetString(InstanceUID_UL); // If there was no InstanceUID, add a random symspace if(SymSpaceName.empty()) SymSpaceName = RandomUL()->GetString(); // See if we already have this symbol space (may have already loaded a copy of this metadictionary) - if not, build it SymbolSpacePtr SymSpace = SymbolSpace::FindSymbolSpace(SymSpaceName); if(!SymSpace) SymSpace = new SymbolSpace(SymSpaceName); // Load the metdictionary and update the running status if(!LoadMetadictionary(*it, SymSpace)) Ret = false; } else if((*it)->IsA(Root_UL)) { // FIXME: Use UL when ready //MDObjectPtr RootExtensions = (*it)->Child(RootExtensions_UL); MDObjectPtr RootExtensions = (*it)->Child("RootExtensions"); if(RootExtensions) { MDObject::iterator Ext_it = RootExtensions->begin(); while(Ext_it != RootExtensions->end()) { // Get the actual data group MDObjectPtr ExtensionGroup = (*Ext_it).second->GetRef(); if(!ExtensionGroup) { error("Broken link in ExtensionGroup reference at %s\n", (*Ext_it).second->GetSourceLocation().c_str()); } else { /* We need to set up a symbol space for this metadictionary */ // FIXME: Use UL when ready //std::string SymSpaceName = ExtensionGroup->GetString(SymbolSpace_UL); std::string SymSpaceName = ExtensionGroup->GetString("SymbolSpace"); // If there was no SymbolSpace property, add a random symspace if(SymSpaceName.empty()) SymSpaceName = RandomUL()->GetString(); // See if we already have this symbol space (may have already loaded a copy of this metadictionary) - if not, build it SymbolSpacePtr SymSpace = SymbolSpace::FindSymbolSpace(SymSpaceName); if(!SymSpace) SymSpace = new SymbolSpace(SymSpaceName); // Load the metdictionary and update the running status if(!LoadMetadictionary(ExtensionGroup, SymSpace)) Ret = false; } Ext_it++; } } } it++; } return Ret; }
//! Satisfy, or record as un-matched, all outgoing references void mxflib::Partition::ProcessChildRefs(MDObjectPtr ThisObject) { MDObjectULList::iterator it = ThisObject->begin(); while(it != ThisObject->end()) { // Only try to match references if not already matched if(!(*it).second->GetLink()) { ClassRef Ref = (*it).second->GetRefType(); if(IsRefSource(Ref)) { if(!(*it).second->Value) { if(Ref != ClassRefGlobal) { error("Metadata Object \"%s/%s\" should be a reference source (a UUID), but has no valid value\n", ThisObject->Name().c_str(), (*it).second->Name().c_str()); } } // Container for child items else if((*it).second->Value->GetData().Size == 0) { // Recurse to add refs for our children if((*it).second->size()) ProcessChildRefs((*it).second); } else if((*it).second->Value->GetData().Size != 16) { if(Ref == ClassRefGlobal) { error("Metadata Object \"%s/%s\" should be a global reference (a UL or UUID), but has size %d\n", ThisObject->Name().c_str(), (*it).second->Name().c_str(), (*it).second->Value->GetData().Size); } else { error("Metadata Object \"%s/%s\" should be a reference source (a UUID), but has size %d\n", ThisObject->Name().c_str(), (*it).second->Name().c_str(), (*it).second->Value->GetData().Size); } } else { UUIDPtr ID = new UUID((*it).second->Value->PutData()->Data); std::map<UUID, MDObjectPtr>::iterator mit = RefTargets.find(*ID); if(mit == RefTargets.end()) { // Not matched yet, so add to the list of outstanding refs UnmatchedRefs.insert(std::multimap<UUID, MDObjectPtr>::value_type(*ID, (*it).second)); } else { // Make the link (*it).second->SetLink((*mit).second); // If we have made a strong ref, remove the target from the top level if(Ref == DICT_REF_STRONG) TopLevelMetadata.remove((*mit).second); } } } } it++; } }
//! Dump an object and any physical or logical children void DumpObject(MDObjectPtr Object, std::string Prefix) { if(DumpLocation) printf("0x%s : ", Int64toHexString(Object->GetLocation(),8).c_str()); if(Object->IsModified()) printf("%s%s is *MODIFIED*\n", Object->FullName().c_str(), Prefix.c_str() ); #ifdef OPTION3ENABLED if(ShowBaseline) { if(!Object->IsBaseline()) { if(Object->GetBaselineUL()) { MDOTypePtr BaselineClass = MDOType::Find(Object->GetBaselineUL()); if(BaselineClass) { printf("%sBaseline: %s\n", Prefix.c_str(), BaselineClass->Name().c_str()); } else { printf("%sNote: Current dictionary does not contain a set with the baseline UL used to wrap this non-baseline class\n", Prefix.c_str()); printf("%sBaseline: %s\n", Prefix.c_str(), Object->GetBaselineUL()->GetString().c_str()); } Prefix += " "; } else { printf("%sNote: Current dictionary flags this class as non-baseline, but it is not wrapped in a baseline class\n", Prefix.c_str()); } } else { if(Object->GetBaselineUL()) { printf("%sNote: Current dictionary flags this class as baseline, but it is wrapped as a non-baseline class\n", Prefix.c_str()); MDOTypePtr BaselineClass = MDOType::Find(Object->GetBaselineUL()); if(BaselineClass) { printf("%sBaseline: %s\n", Prefix.c_str(), BaselineClass->Name().c_str()); } else { printf("%sNote: Current dictionary does not contain a set with the baseline UL used to wrap this non-baseline class\n", Prefix.c_str()); printf("%sBaseline: %s\n", Prefix.c_str(), Object->GetBaselineUL()->GetString().c_str()); } Prefix += " "; } } } #endif // OPTION3ENABLED if(Object->GetLink()) { if(Object->GetRefType() == ClassRefStrong) { printf("%s%s = %s\n", Prefix.c_str(), Object->Name().c_str(), Object->GetString().c_str()); if(DumpLocation) printf("0x%s : ", Int64toHexString(Object->GetLocation(),8).c_str()); printf("%s%s -> Strong Reference to %s\n", Prefix.c_str(), Object->Name().c_str(), Object->GetLink()->Name().c_str()); DumpObject(Object->GetLink(), Prefix + " "); } else if(Object->GetRefType() == ClassRefGlobal) { if(FollowGlobals) { printf("%s%s = %s\n", Prefix.c_str(), Object->Name().c_str(), Object->GetString().c_str()); if(DumpLocation) printf("0x%s : ", Int64toHexString(Object->GetLocation(),8).c_str()); printf("%s%s -> Global Reference to %s\n", Prefix.c_str(), Object->Name().c_str(), Object->GetLink()->Name().c_str()); DumpObject(Object->GetLink(), Prefix + " "); } else { printf("%s%s -> Global Reference to %s, %s\n", Prefix.c_str(), Object->Name().c_str(), Object->GetLink()->Name().c_str(), Object->GetString().c_str()); } } else if(Object->GetRefType() == ClassRefMeta) { std::string TargetName = Object->GetLink()->GetString(MetaDefinitionName_UL, Object->GetLink()->Name()); printf("%s%s -> MetaDictionary Reference to %s %s\n", Prefix.c_str(), Object->Name().c_str(), TargetName.c_str(), Object->GetString().c_str()); } else if(Object->GetRefType() == ClassRefDict) { std::string TargetName = Object->GetLink()->GetString(DefinitionObjectName_UL, Object->GetLink()->Name()); printf("%s%s -> Dictionary Reference to %s %s\n", Prefix.c_str(), Object->Name().c_str(), TargetName.c_str(), Object->GetString().c_str()); } else { printf("%s%s -> Weak Reference to %s %s\n", Prefix.c_str(), Object->Name().c_str(), Object->GetLink()->Name().c_str(), Object->GetString().c_str()); } } else { if(Object->IsDValue()) { printf("%s%s = <Unknown>\n", Prefix.c_str(), Object->Name().c_str()); } else { // Check first for values that are not reference batches if(Object->IsAValue()) { //if(Object->Name().find("Unknown") == std::string::npos) printf("%s%s = %s\n", Prefix.c_str(), Object->Name().c_str(), Object->GetString().c_str()); //else printf("%s%s\n", Prefix.c_str(), Object->Name().c_str()); if(Object->GetRefType() == ClassRefMeta) printf("%s%s is an unsatisfied MetaRef\n", Prefix.c_str(), Object->Name().c_str()); else if(Object->GetRefType() == ClassRefDict) printf("%s%s is an unsatisfied DictRef\n", Prefix.c_str(), Object->Name().c_str()); } else { printf("%s%s\n", Prefix.c_str(), Object->Name().c_str()); MDObjectULList::iterator it = Object->begin(); if(!SortedDump) { /* Dump Objects in the order stored */ while(it != Object->end()) { DumpObject((*it).second, Prefix + " "); it++; } } else { /* Dump Objects in alphabetical order - to allow easier file comparisons */ std::multimap<std::string, MDObjectPtr> ChildMap; std::multimap<std::string, MDObjectPtr>::iterator CM_Iter; while(it != Object->end()) { ChildMap.insert(std::multimap<std::string, MDObjectPtr>::value_type((*it).second->Name(), (*it).second)); it++; } CM_Iter = ChildMap.begin(); while(CM_Iter != ChildMap.end()) { DumpObject((*CM_Iter).second, Prefix + " "); CM_Iter++; } } } } } return; }
//! Do the main processing (less any pause before exit) int main_process(int argc, char *argv[]) { printf("Dump an MXF file using MXFLib\n"); std::string DictName = "dict.xml"; std::list<std::string> SuppDicts; int num_options = 0; for(int i=1; i<argc; i++) { if(argv[i][0] == '-') { num_options++; if((argv[i][1] == 'a') || (argv[i][1] == 'A')) SortedDump = true; else if((argv[i][1] == 'v') || (argv[i][1] == 'V')) DebugMode = true; else if((argv[i][1] == 'i') || (argv[i][1] == 'I')) FullIndex = true; else if((argv[i][1] == 'c') || (argv[i][1] == 'C')) { if(argv[i][2] == '0') DumpIDs = false; else CheckDump = true; } else if((argv[i][1] == 'b') || (argv[i][1] == 'B')) FullBody = true; // else if((argv[i][1] == 'g') || (argv[i][1] == 'G')) // FollowGlobals = true; else if((argv[i][1] == 'l') || (argv[i][1] == 'L')) DumpLocation = true; #ifdef OPTION3ENABLED else if((argv[i][1] == 'o') || (argv[i][1] == 'O')) ShowBaseline = true; #endif // OPTION3ENABLED else if((argv[i][1] == 'd') || (argv[i][1] == 'D')) { int Start = 2; if((Start == 2) && ((argv[i][Start] == 'd') || (argv[i][Start] == 'D'))) Start++; if((argv[i][Start] == '=') || (argv[i][Start] == ':')) Start++; if(argv[i][Start]) SuppDicts.push_back(&argv[i][Start]); else if(argc > (i+1)) { SuppDicts.push_back(argv[++i]); num_options++; } } else if((argv[i][1] == 'u') || (argv[i][1] == 'U')) { MDObject::SetParseDark(true); } else if((argv[i][1] == 'x') || (argv[i][1] == 'X')) { if(argv[i][2] == '0') SetLabelFormat(LabelFormatHex); else if(argv[i][2] == '1') SetLabelFormat(LabelFormatTextHex); else SetLabelFormat(LabelFormatTextHexMask); } else if(tolower(argv[i][1]) == 't') { SetFeature(FeatureLoadMetadict); printf("Loading metadictionary contents from file\n"); if(argv[i][2] == '1') { BootstrapDict = true; printf("Starting with only a minimum compiled-in dictionary\n"); } } else if((argv[i][1] == 'm') || (argv[i][1] == 'M')) { int Start = 2; if((argv[i][Start] == '=') || (argv[i][Start] == ':')) Start++; if(argv[i][Start]) DictName = &argv[i][Start]; else if(argc > (i+1)) { DictName = argv[++i]; num_options++; } #ifdef COMPILED_DICT UseCompiledDict = false; #endif // COMPILED_DICT } else if((argv[i][1] == 'z') || (argv[i][1] == 'Z')) PauseBeforeExit = true; } } if (argc - num_options < 2) { printf("\nUsage: %s [options] <filename>\n\n", argv[0]); printf("Options: -a Dump sub-items alpha-sorted\n"); printf(" -b Dump body partitions (rather than just header and footer)\n"); printf(" -c Check dump (produce simple counts for automated testing)\n"); printf(" -c0 Don't dump UUID, UMID and Timestamp values (for comparing files)\n"); printf(" -dd <dict> Load supplementary dictionary (also -d for legacy)\n"); printf(" -g Follow global references (if linked)\n"); printf(" -i Dump full index tables (can be lengthy)\n"); printf(" -l Show the location (byte offset) of metadata items dumped\n"); #ifdef COMPILED_DICT printf(" -m <dict> Specify main dictionary (instead of compile-time version)\n"); #else printf(" -m <dict> Specify main dictionary (instead of dict.xml)\n"); #endif // COMPILED_DICT #ifdef OPTION3ENABLED printf(" -o Show baseline UL for sets with ObjectClass property\n"); #endif // OPTION3ENABLED printf(" -t Load metadictionary contents from file\n"); printf(" -t1 Load metadictionary and start from a minimal subset\n"); printf(" -u Attempt to parse unknown or 'dark' sets\n"); printf(" -v Verbose mode - shows lots of debug info\n"); printf(" -x0 Always show labels as hex data\n"); printf(" -x1 Append hex data to labels\n"); printf(" -x2 or -x Append hex data to labels if 'fuzzy' matching used\n"); printf(" -z Pause for input before final exit\n"); return 1; } if(BootstrapDict) { printf("- using a minimal compile-time dictionary and extending from metadictionary\n"); LoadDictionary(BootDict); } else { if( UseCompiledDict ) { printf("- using compile-time dictionary\n"); LoadDictionary(DictData); } else { printf("- using dictionary %s\n", DictName.c_str()); LoadDictionary(DictName); } } std::list<std::string>::iterator DictIt = SuppDicts.begin(); while(DictIt != SuppDicts.end()) { LoadDictionary(*DictIt); DictIt++; } // If we are NOT dumping IDs replace thier traits if(!DumpIDs) { UpdateTraitsMapping("Internal-UUID", new MessageTraits("{UUID}")); UpdateTraitsMapping("UUID", new MessageTraits("{UUID}")); UpdateTraitsMapping("UMID", new MessageTraits("{UMID}")); UpdateTraitsMapping("Timestamp", new MessageTraits("{Timestamp}")); } MXFFilePtr TestFile = new MXFFile; if (! TestFile->Open(argv[num_options+1], true)) { perror(argv[num_options+1]); return 1; } // Get a RIP (however possible) TestFile->GetRIP(); unsigned int PartitionNumber = 0; RIP::iterator it = TestFile->FileRIP.begin(); while(it != TestFile->FileRIP.end()) { PartitionNumber++; if(CheckDump) printf("\nPartition for BodySID 0x%04x\n", (*it).second->BodySID); else printf("\nPartition at 0x%s is for BodySID 0x%04x\n", Int64toHexString((*it).second->ByteOffset,8).c_str(), (*it).second->BodySID); // Only dump header and footer unless asked for all partitions if(FullBody || (PartitionNumber == 1) || (PartitionNumber == TestFile->FileRIP.size())) { TestFile->Seek((*it).second->ByteOffset); PartitionPtr ThisPartition = TestFile->ReadPartition(); if(ThisPartition) { if(CheckDump) { if(ThisPartition->ReadMetadata() == 0) { printf("No header metadata in this partition\n"); } else { printf(" Top level count = %d\n", (int)ThisPartition->TopLevelMetadata.size()); printf(" Set/Pack count = %d\n", (int)ThisPartition->AllMetadata.size()); size_t Count = 0; MDObjectList::iterator it = ThisPartition->AllMetadata.begin(); while(it != ThisPartition->AllMetadata.end()) { Count += (*it)->size(); it++; } printf(" Sub item count = %d\n", (int)Count); } // Read any index table segments! MDObjectListPtr Segments = ThisPartition->ReadIndex(); if(Segments->empty()) { printf("No index table in this partition\n"); } else { IndexTablePtr Table = new IndexTable; MDObjectList::iterator it = Segments->begin(); while(it != Segments->end()) { // Summarize this new segment UInt32 Streams = 1; MDObjectPtr DeltaEntryArray = (*it)[DeltaEntryArray_UL]; if(DeltaEntryArray && DeltaEntryArray->GetType()->size()) { Streams = static_cast<UInt32>(DeltaEntryArray->size() / DeltaEntryArray->GetType()->size()); if(Streams == 0) Streams = 1; // Fix for bad DeltaEntryArray } Position Start = (*it)->GetInt64(IndexStartPosition_UL); Length Duration = (*it)->GetInt64(IndexDuration_UL); UInt32 IndexSID = (*it)->GetUInt(IndexSID_UL); UInt32 BodySID = (*it)->GetUInt(BodySID_UL); if(Duration == 0) printf("CBR Index Table Segment (covering whole Essence Container) :\n"); else printf("\nIndex Table Segment (first edit unit = %s, duration = %s) :\n", Int64toString(Start).c_str(), Int64toString(Duration).c_str()); printf(" Indexing BodySID 0x%04x from IndexSID 0x%04x\n", BodySID, IndexSID); it++; } } } else { // Don't dump the last partition unless it is a footer or we are dumping all // DRAGONS: What is the PartitionNumber != RIP-Size thing about? if(FullBody || (PartitionNumber == 1) || (PartitionNumber != TestFile->FileRIP.size()) || (ThisPartition->IsA(CompleteFooter_UL)) || (ThisPartition->IsA(Footer_UL)) ) { DumpObject(ThisPartition->Object,""); if(ThisPartition->ReadMetadata() == 0) { printf("No header metadata in this partition\n"); } else { printf("\nHeader Metadata:\n"); MDObjectList::iterator it2 = ThisPartition->TopLevelMetadata.begin(); while(it2 != ThisPartition->TopLevelMetadata.end()) { DumpObject(*it2," "); it2++; } printf("\n"); } // Read any index table segments! MDObjectListPtr Segments = ThisPartition->ReadIndex(); if(Segments->empty()) { printf("No index table in this partition\n"); } else { IndexTablePtr Table = new IndexTable; MDObjectList::iterator it = Segments->begin(); while(it != Segments->end()) { Table->AddSegment(*it); // Demonstrate this new segment UInt32 Streams = 1; MDObjectPtr DeltaEntryArray = (*it)[DeltaEntryArray_UL]; if(DeltaEntryArray) { Streams = static_cast<UInt32>(DeltaEntryArray->size()); if(Streams == 0) Streams = 1; // Fix for bad DeltaEntryArray } Position Start = (*it)->GetInt64(IndexStartPosition_UL); Length Duration = (*it)->GetInt64(IndexDuration_UL); UInt32 IndexSID = (*it)->GetUInt(IndexSID_UL); UInt32 BodySID = (*it)->GetUInt(BodySID_UL); if(Duration == 0) printf("CBR Index Table Segment (covering whole Essence Container) :\n"); else printf("\nIndex Table Segment (first edit unit = %s, duration = %s) :\n", Int64toString(Start).c_str(), Int64toString(Duration).c_str()); printf(" Indexing BodySID 0x%04x from IndexSID 0x%04x\n", BodySID, IndexSID); if(Duration < 1) Duration = 6; // Could be CBR if(!FullIndex && Duration > 35) Duration = 35; // Don't go mad! int i; printf( "\n Bytestream Order:\n" ); for(i=0; i<Duration; i++) { UInt32 j; for(j=0; j<Streams; j++) { IndexPosPtr Pos = Table->Lookup(Start + i,j,false); printf(" EditUnit %3s for stream %d is at 0x%s", Int64toString(Start + i).c_str(), j, Int64toHexString(Pos->Location,8).c_str()); printf(", TempOffset=%d", Pos->TemporalOffset); printf(", Flags=%02x", Pos->Flags); if(Pos->Exact) printf(" *Exact*\n"); else printf("\n"); } } printf( "\n Presentation Order:\n" ); for(i=0; i<Duration; i++) { UInt32 j; for(j=0; j<Streams; j++) { IndexPosPtr Pos = Table->Lookup(Start + i,j); printf(" EditUnit %3s for stream %d is at 0x%s", Int64toString(Start + i).c_str(), j, Int64toHexString(Pos->Location,8).c_str()); printf(", TempOffset=%d", Pos->TemporalOffset); printf(", Flags=%02x", Pos->Flags); /// printf(", Keyframe is at 0x%s", Int64toHexString(Pos->KeyLocation,8).c_str() ); if(Pos->Exact) printf(" *Exact*\n"); else if(Pos->OtherPos) printf(" (Location of un-reordered position %s)\n", Int64toString(Pos->ThisPos).c_str()); else printf("\n"); } } it++; } } } } } } it++; } if(TestFile->ReadRIP()) { printf("\nRead RIP\n"); PartitionInfoMap::iterator it = TestFile->FileRIP.begin(); while(it != TestFile->FileRIP.end()) { if(CheckDump) printf(" BodySID 0x%04x", (*it).second->BodySID); else printf(" BodySID 0x%04x is at 0x%s", (*it).second->BodySID, Int64toHexString((*it).second->ByteOffset,8).c_str()); if((*it).second->ThePartition) printf(" type %s\n", (*it).second->ThePartition->Name().c_str()); else printf(" and is not loaded\n"); it++; } } if(TestFile->ScanRIP()) { printf("\nScanned RIP\n"); PartitionInfoMap::iterator it = TestFile->FileRIP.begin(); while(it != TestFile->FileRIP.end()) { if(CheckDump) printf(" BodySID 0x%04x", (*it).second->BodySID); else printf(" BodySID 0x%04x is at 0x%s", (*it).second->BodySID, Int64toHexString((*it).second->ByteOffset,8).c_str()); if((*it).second->ThePartition) printf(" type %s\n", (*it).second->ThePartition->Name().c_str()); else printf(" and is not loaded\n"); it++; } } /* if(TestFile->BuildRIP()) { printf("\nBuilt RIP\n"); PartitionInfoList::iterator it = TestFile->FileRIP.begin(); while(it != TestFile->FileRIP.end()) { printf(" BodySID 0x%04x is at 0x%s", (*it)->BodySID, Int64toHexString((*it)->ByteOffset,8).c_str()); if((*it)->ThePartition) printf(" type %s\n", (*it)->ThePartition->Name().c_str()); else printf(" and is not loaded\n"); it++; } } */ TestFile->Close(); /* PrimerPtr NewPrimer = new Primer; unsigned char Key[16] = { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x04, 0x04, 0x06, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00 }; Tag ThisTag = NewPrimer->Lookup(new UL(Key)); printf("Tag = %s\n", Tag2String(ThisTag).c_str()); Key[11] = 5; ThisTag = NewPrimer->Lookup(new UL(Key)); printf("NewTag = %s\n", Tag2String(ThisTag).c_str()); ThisTag = NewPrimer->Lookup(new UL(Key)); printf("NewTag = %s\n", Tag2String(ThisTag).c_str()); Key[12] = 1; ThisTag = NewPrimer->Lookup(new UL(Key)); printf("NewTag = %s\n", Tag2String(ThisTag).c_str()); */ return 0; }
//! Get the offset to add to lines in field 2 int ANCVBISource::Field2Offset(void) { if(F2Offset >= 0) return F2Offset; MDObjectPtr Descriptor = MasterSource->GetDescriptor(); if(!Descriptor) { error("EssenceDescriptor not defined for master source of ANCVBISource before calling Field2Offset()\n"); F2Offset = 0; return F2Offset; } // If this is a multpile descriptor, locate the video // DRAGONS: If we can't find a picture descriptor we will drop through with the MultipleDescriptor and give a "does not have a VideoLineMap" error if(Descriptor->IsA(MultipleDescriptor_UL)) { MDObject::iterator it = Descriptor->begin(); while(it != Descriptor->end()) { if((*it).second->IsA(GenericPictureEssenceDescriptor_UL)) { Descriptor = (*it).second; break; } it++; } } /* Check if this is interlaced essence */ if(Descriptor->IsDValue(FrameLayout_UL)) { warning("EssenceDescriptor for ANCVBISource does not have a valid FrameLayout\n"); F2Offset = 0; return F2Offset; } if(Descriptor->GetInt(FrameLayout_UL) != 1) { F2Offset = 0; return F2Offset; } /* Calculate F1 to F2 distance from Video Line Map */ MDObjectPtr VideoLineMap = Descriptor->Child(VideoLineMap_UL); if(!VideoLineMap) { error("EssenceDescriptor for ANCVBISource does not have a valid VideoLineMap\n"); F2Offset = 0; return F2Offset; } MDObjectPtr F1Entry = VideoLineMap->Child(0); MDObjectPtr F2Entry = VideoLineMap->Child(1); if((!F1Entry) || (!F2Entry)) { error("EssenceDescriptor for ANCVBISource does not have a valid VideoLineMap\n"); F2Offset = 0; return F2Offset; } F2Offset = static_cast<int>(F2Entry->GetInt() - F1Entry->GetInt()); return F2Offset; }
/*! \return A smart pointer to the new WrappingConfig for this source as a sub-stream of the specified master, or NULL if not a valid combination */ EssenceParser::WrappingConfigPtr ANCVBISource::MakeWrappingConfig(WrappingConfigPtr MasterCfg) { EssenceParser::WrappingConfigPtr SubCfg; /* First we validate our requirements */ // Only valid for frame wrapping if(MasterCfg->WrapOpt->ThisWrapType != WrappingOption::Frame) return SubCfg; // Not valid if we have no line sources if(Sources.empty()) return SubCfg; /* Now check each line source is happy */ // TODO: Can we build a sub-set version where we only include those line sources that are happy? std::string Description; ANCVBILineSourceList::iterator it = Sources.begin(); while(it != Sources.end()) { std::string ThisDesc = (*it)->ValidateConfig(MasterCfg); if(ThisDesc.empty()) return SubCfg; if(!Description.empty()) Description += ", plus "; Description += ThisDesc; it++; } /* Requested wrapping is valid, build the new config */ SubCfg = new EssenceParser::WrappingConfig(); SubCfg->Parser = MasterCfg->Parser; SubCfg->WrapOpt = new WrappingOption(); SubCfg->WrapOpt->Handler = SubCfg->Parser; SubCfg->WrapOpt->Name = ""; SubCfg->WrapOpt->Description = Description; SubCfg->WrapOpt->GCEssenceType = GetGCEssenceType(); SubCfg->WrapOpt->GCElementType = GetGCElementType(); SubCfg->WrapOpt->ThisWrapType = MasterCfg->WrapOpt->ThisWrapType; SubCfg->WrapOpt->CanSlave = false; SubCfg->WrapOpt->CanIndex = false; SubCfg->WrapOpt->CBRIndex = false; SubCfg->WrapOpt->BERSize = 4; SubCfg->WrapOpt->BytesPerEditUnit = 0; SubCfg->WrapOpt->WrappingUL = GetWrappingUL(); SubCfg->EssenceDescriptor = new MDObject(ANCDataDescriptor_UL); MDObjectPtr SampleRate = SubCfg->EssenceDescriptor->AddChild(SampleRate_UL); SampleRate->SetInt("Numerator", MasterCfg->EditRate.Numerator); SampleRate->SetInt("Denominator", MasterCfg->EditRate.Denominator); SubCfg->EssenceDescriptor->SetValue(EssenceContainer_UL, DataChunk(16,SubCfg->WrapOpt->WrappingUL->GetValue())); SubCfg->Stream = 0; SubCfg->EditRate = MasterCfg->EditRate; SubCfg->StartTimecode = 0; return SubCfg; }