2 Copyright (c) 2005-2015, John Hurst
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
8 1. Redistributions of source code must retain the above copyright
9 notice, this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13 3. The name of the author may not be used to endorse or promote products
14 derived from this software without specific prior written permission.
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 using Kumu::DefaultLogSink;
37 using Kumu::GenRandomValue;
39 // index segments must be < 64K
40 // NOTE: this value may too high if advanced index entry elements are used.
41 const ui32_t CBRIndexEntriesPerSegment = 5000;
43 //------------------------------------------------------------------------------------------
48 ASDCP::MXF::SeekToRIP(const Kumu::FileReader& Reader)
52 // go to the end - 4 bytes
53 Result_t result = Reader.Seek(0, Kumu::SP_END);
55 if ( ASDCP_SUCCESS(result) )
56 result = Reader.Tell(&end_pos);
58 if ( ASDCP_SUCCESS(result)
59 && end_pos < (SMPTE_UL_LENGTH+MXF_BER_LENGTH) )
61 DefaultLogSink().Error("File is smaller than an empty KLV packet.\n");
65 if ( ASDCP_SUCCESS(result) )
66 result = Reader.Seek(end_pos - 4);
68 // get the ui32_t RIP length
70 byte_t intbuf[MXF_BER_LENGTH];
73 if ( ASDCP_SUCCESS(result) )
75 result = Reader.Read(intbuf, MXF_BER_LENGTH, &read_count);
77 if ( ASDCP_SUCCESS(result) && read_count != 4 )
79 DefaultLogSink().Error("RIP contains fewer than four bytes.\n");
84 if ( ASDCP_SUCCESS(result) )
86 rip_size = KM_i32_BE(Kumu::cp2i<ui32_t>(intbuf));
88 if ( rip_size > end_pos ) // RIP can't be bigger than the file
90 DefaultLogSink().Error("RIP size impossibly large.\n");
95 // reposition to start of RIP
96 if ( ASDCP_SUCCESS(result) )
97 result = Reader.Seek(end_pos - rip_size);
104 ASDCP::MXF::RIP::GetPairBySID(ui32_t SID, PartitionPair& outPair) const
106 RIP::const_pair_iterator i;
107 for ( i = PairArray.begin(); i != PairArray.end(); ++i )
109 if ( i->BodySID == SID )
121 ASDCP::MXF::RIP::InitFromFile(const Kumu::FileReader& Reader)
124 Result_t result = KLVFilePacket::InitFromFile(Reader, m_Dict->ul(MDD_RandomIndexMetadata));
126 if ( ASDCP_SUCCESS(result) )
128 if (m_ValueLength < 4)
130 DefaultLogSink().Error("RIP is too short.\n");
131 return RESULT_KLV_CODING(__LINE__, __FILE__);
133 Kumu::MemIOReader MemRDR(m_ValueStart, m_ValueLength - 4);
134 result = PairArray.Unarchive(&MemRDR) ? RESULT_OK : RESULT_KLV_CODING(__LINE__, __FILE__);
137 if ( ASDCP_FAILURE(result) )
138 DefaultLogSink().Error("Failed to initialize RIP.\n");
145 ASDCP::MXF::RIP::WriteToFile(Kumu::FileWriter& Writer)
148 ASDCP::FrameBuffer Buffer;
149 ui32_t RIPSize = ( PairArray.size() * (sizeof(ui32_t) + sizeof(ui64_t)) ) + 4;
150 Result_t result = Buffer.Capacity(RIPSize);
152 if ( ASDCP_SUCCESS(result) )
153 result = WriteKLToFile(Writer, m_Dict->ul(MDD_RandomIndexMetadata), RIPSize);
155 if ( ASDCP_SUCCESS(result) )
157 result = RESULT_KLV_CODING(__LINE__, __FILE__);
159 Kumu::MemIOWriter MemWRT(Buffer.Data(), Buffer.Capacity());
160 if ( PairArray.Archive(&MemWRT) )
161 if ( MemWRT.WriteUi32BE(RIPSize + 20) )
163 Buffer.Size(MemWRT.Length());
168 if ( ASDCP_SUCCESS(result) )
169 result = Writer.Write(Buffer.RoData(), Buffer.Size());
176 ASDCP::MXF::RIP::Dump(FILE* stream)
181 KLVFilePacket::Dump(stream, *m_Dict, false);
182 PairArray.Dump(stream, false);
185 //------------------------------------------------------------------------------------------
189 ASDCP::MXF::Partition::PacketList::~PacketList() {
190 while ( ! m_List.empty() )
192 delete m_List.back();
199 ASDCP::MXF::Partition::PacketList::AddPacket(InterchangeObject* ThePacket) // takes ownership
202 m_Map.insert(std::map<UUID, InterchangeObject*>::value_type(ThePacket->InstanceUID, ThePacket));
203 m_List.push_back(ThePacket);
208 ASDCP::MXF::Partition::PacketList::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
210 ASDCP_TEST_NULL(Object);
212 std::map<UUID, InterchangeObject*>::iterator mi = m_Map.find(ObjectID);
214 if ( mi == m_Map.end() )
220 *Object = (*mi).second;
226 ASDCP::MXF::Partition::PacketList::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
228 ASDCP_TEST_NULL(ObjectID);
229 ASDCP_TEST_NULL(Object);
230 std::list<InterchangeObject*>::iterator li;
233 for ( li = m_List.begin(); li != m_List.end(); li++ )
235 if ( (*li)->HasUL(ObjectID) )
247 ASDCP::MXF::Partition::PacketList::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
249 ASDCP_TEST_NULL(ObjectID);
250 std::list<InterchangeObject*>::iterator li;
252 for ( li = m_List.begin(); li != m_List.end(); li++ )
254 if ( (*li)->HasUL(ObjectID) )
255 ObjectList.push_back(*li);
258 return ObjectList.empty() ? RESULT_FAIL : RESULT_OK;
261 //------------------------------------------------------------------------------------------
265 ASDCP::MXF::Partition::Partition(const Dictionary*& d) :
267 MajorVersion(1), MinorVersion(2),
268 KAGSize(1), ThisPartition(0), PreviousPartition(0),
269 FooterPartition(0), HeaderByteCount(0), IndexByteCount(0), IndexSID(0),
270 BodyOffset(0), BodySID(0)
272 m_PacketList = new PacketList;
275 ASDCP::MXF::Partition::~Partition()
281 ASDCP::MXF::Partition::AddChildObject(InterchangeObject* Object)
285 if ( ! Object->InstanceUID.HasValue() )
286 GenRandomValue(Object->InstanceUID);
288 m_PacketList->AddPacket(Object);
293 ASDCP::MXF::Partition::InitFromFile(const Kumu::FileReader& Reader)
295 Result_t result = KLVFilePacket::InitFromFile(Reader);
297 // could be one of several values
298 if ( ASDCP_SUCCESS(result) )
299 result = ASDCP::MXF::Partition::InitFromBuffer(m_ValueStart, m_ValueLength);
306 ASDCP::MXF::Partition::InitFromBuffer(const byte_t* p, ui32_t l)
308 Kumu::MemIOReader MemRDR(p, l);
309 Result_t result = RESULT_KLV_CODING(__LINE__, __FILE__);
311 if ( MemRDR.ReadUi16BE(&MajorVersion) )
312 if ( MemRDR.ReadUi16BE(&MinorVersion) )
313 if ( MemRDR.ReadUi32BE(&KAGSize) )
314 if ( MemRDR.ReadUi64BE(&ThisPartition) )
315 if ( MemRDR.ReadUi64BE(&PreviousPartition) )
316 if ( MemRDR.ReadUi64BE(&FooterPartition) )
317 if ( MemRDR.ReadUi64BE(&HeaderByteCount) )
318 if ( MemRDR.ReadUi64BE(&IndexByteCount) )
319 if ( MemRDR.ReadUi32BE(&IndexSID) )
320 if ( MemRDR.ReadUi64BE(&BodyOffset) )
321 if ( MemRDR.ReadUi32BE(&BodySID) )
322 if ( OperationalPattern.Unarchive(&MemRDR) )
323 if ( EssenceContainers.Unarchive(&MemRDR) )
326 if ( ASDCP_FAILURE(result) )
327 DefaultLogSink().Error("Failed to initialize Partition.\n");
334 ASDCP::MXF::Partition::WriteToFile(Kumu::FileWriter& Writer, UL& PartitionLabel)
336 ASDCP::FrameBuffer Buffer;
337 Result_t result = Buffer.Capacity(1024);
339 if ( ASDCP_SUCCESS(result) )
341 Kumu::MemIOWriter MemWRT(Buffer.Data(), Buffer.Capacity());
342 result = RESULT_KLV_CODING(__LINE__, __FILE__);
343 if ( MemWRT.WriteUi16BE(MajorVersion) )
344 if ( MemWRT.WriteUi16BE(MinorVersion) )
345 if ( MemWRT.WriteUi32BE(KAGSize) )
346 if ( MemWRT.WriteUi64BE(ThisPartition) )
347 if ( MemWRT.WriteUi64BE(PreviousPartition) )
348 if ( MemWRT.WriteUi64BE(FooterPartition) )
349 if ( MemWRT.WriteUi64BE(HeaderByteCount) )
350 if ( MemWRT.WriteUi64BE(IndexByteCount) )
351 if ( MemWRT.WriteUi32BE(IndexSID) )
352 if ( MemWRT.WriteUi64BE(BodyOffset) )
353 if ( MemWRT.WriteUi32BE(BodySID) )
354 if ( OperationalPattern.Archive(&MemWRT) )
355 if ( EssenceContainers.Archive(&MemWRT) )
357 Buffer.Size(MemWRT.Length());
362 if ( ASDCP_SUCCESS(result) )
365 result = WriteKLToFile(Writer, PartitionLabel.Value(), Buffer.Size());
367 if ( ASDCP_SUCCESS(result) )
368 result = Writer.Write(Buffer.RoData(), Buffer.Size(), &write_count);
376 ASDCP::MXF::Partition::ArchiveSize()
379 + sizeof(ui16_t) + sizeof(ui16_t)
381 + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t)
386 + sizeof(ui32_t) + sizeof(ui32_t) + ( UUIDlen * EssenceContainers.size() ) );
391 ASDCP::MXF::Partition::Dump(FILE* stream)
393 char identbuf[IdentBufferLen];
398 KLVFilePacket::Dump(stream, *m_Dict, false);
399 fprintf(stream, " MajorVersion = %hu\n", MajorVersion);
400 fprintf(stream, " MinorVersion = %hu\n", MinorVersion);
401 fprintf(stream, " KAGSize = %u\n", KAGSize);
402 fprintf(stream, " ThisPartition = %s\n", ui64sz(ThisPartition, identbuf));
403 fprintf(stream, " PreviousPartition = %s\n", ui64sz(PreviousPartition, identbuf));
404 fprintf(stream, " FooterPartition = %s\n", ui64sz(FooterPartition, identbuf));
405 fprintf(stream, " HeaderByteCount = %s\n", ui64sz(HeaderByteCount, identbuf));
406 fprintf(stream, " IndexByteCount = %s\n", ui64sz(IndexByteCount, identbuf));
407 fprintf(stream, " IndexSID = %u\n", IndexSID);
408 fprintf(stream, " BodyOffset = %s\n", ui64sz(BodyOffset, identbuf));
409 fprintf(stream, " BodySID = %u\n", BodySID);
410 fprintf(stream, " OperationalPattern = %s\n", OperationalPattern.EncodeString(identbuf, IdentBufferLen));
411 fputs("Essence Containers:\n", stream); EssenceContainers.Dump(stream);
415 //------------------------------------------------------------------------------------------
418 class ASDCP::MXF::Primer::h__PrimerLookup : public std::map<UL, TagValue>
421 void InitWithBatch(ASDCP::MXF::Batch<ASDCP::MXF::Primer::LocalTagEntry>& Batch)
423 ASDCP::MXF::Batch<ASDCP::MXF::Primer::LocalTagEntry>::iterator i = Batch.begin();
425 for ( ; i != Batch.end(); i++ )
426 insert(std::map<UL, TagValue>::value_type((*i).UL, (*i).Tag));
432 ASDCP::MXF::Primer::Primer(const Dictionary*& d) : m_LocalTag(0xff), m_Dict(d) {
433 m_UL = m_Dict->ul(MDD_Primer);
437 ASDCP::MXF::Primer::~Primer() {}
441 ASDCP::MXF::Primer::ClearTagList()
443 LocalTagEntryBatch.clear();
444 m_Lookup = new h__PrimerLookup;
449 ASDCP::MXF::Primer::InitFromBuffer(const byte_t* p, ui32_t l)
452 Result_t result = KLVPacket::InitFromBuffer(p, l, m_Dict->ul(MDD_Primer));
454 if ( ASDCP_SUCCESS(result) )
456 if (m_ValueStart + m_ValueLength > p + l)
458 DefaultLogSink().Error("Primer entry too long.\n");
459 return RESULT_KLV_CODING(__LINE__, __FILE__);
461 Kumu::MemIOReader MemRDR(m_ValueStart, m_ValueLength);
462 result = LocalTagEntryBatch.Unarchive(&MemRDR) ? RESULT_OK : RESULT_KLV_CODING(__LINE__, __FILE__);
465 if ( ASDCP_SUCCESS(result) )
467 m_Lookup = new h__PrimerLookup;
468 m_Lookup->InitWithBatch(LocalTagEntryBatch);
471 if ( ASDCP_FAILURE(result) )
472 DefaultLogSink().Error("Failed to initialize Primer.\n");
479 ASDCP::MXF::Primer::WriteToFile(Kumu::FileWriter& Writer)
481 ASDCP::FrameBuffer Buffer;
482 Result_t result = Buffer.Capacity(128*1024);
484 if ( ASDCP_SUCCESS(result) )
485 result = WriteToBuffer(Buffer);
487 if ( ASDCP_SUCCESS(result) )
488 result = Writer.Write(Buffer.RoData(), Buffer.Size());
495 ASDCP::MXF::Primer::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
498 ASDCP::FrameBuffer LocalTagBuffer;
499 Kumu::MemIOWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length);
500 Result_t result = LocalTagEntryBatch.Archive(&MemWRT) ? RESULT_OK : RESULT_KLV_CODING(__LINE__, __FILE__);
502 if ( ASDCP_SUCCESS(result) )
504 ui32_t packet_length = MemWRT.Length();
505 result = WriteKLToBuffer(Buffer, packet_length);
507 if ( ASDCP_SUCCESS(result) )
508 Buffer.Size(Buffer.Size() + packet_length);
516 ASDCP::MXF::Primer::InsertTag(const MDDEntry& Entry, ASDCP::TagValue& Tag)
520 std::map<UL, TagValue>::iterator i = m_Lookup->find(TestUL);
522 if ( i == m_Lookup->end() )
524 if ( Entry.tag.a == 0 && Entry.tag.b == 0 )
527 Tag.b = m_LocalTag--;
535 LocalTagEntry TmpEntry;
536 TmpEntry.UL = TestUL;
539 LocalTagEntryBatch.insert(TmpEntry);
540 m_Lookup->insert(std::map<UL, TagValue>::value_type(TmpEntry.UL, TmpEntry.Tag));
552 ASDCP::MXF::Primer::TagForKey(const ASDCP::UL& Key, ASDCP::TagValue& Tag)
555 if ( m_Lookup.empty() )
557 DefaultLogSink().Error("Primer lookup is empty\n");
561 std::map<UL, TagValue>::iterator i = m_Lookup->find(Key);
563 if ( i == m_Lookup->end() )
572 ASDCP::MXF::Primer::Dump(FILE* stream)
575 char identbuf[IdentBufferLen];
580 KLVPacket::Dump(stream, *m_Dict, false);
581 fprintf(stream, "Primer: %u %s\n",
582 (ui32_t)LocalTagEntryBatch.size(),
583 ( LocalTagEntryBatch.size() == 1 ? "entry" : "entries" ));
585 Batch<LocalTagEntry>::iterator i = LocalTagEntryBatch.begin();
586 for ( ; i != LocalTagEntryBatch.end(); i++ )
588 const MDDEntry* Entry = m_Dict->FindULAnyVersion((*i).UL.Value());
589 fprintf(stream, " %s %s\n", (*i).EncodeString(identbuf, IdentBufferLen), (Entry ? Entry->name : "Unknown"));
594 //------------------------------------------------------------------------------------------
598 ASDCP::MXF::Preface::Preface(const Dictionary*& d) :
599 InterchangeObject(d), m_Dict(d), Version(258)
602 m_UL = m_Dict->Type(MDD_Preface).ul;
603 ObjectModelVersion = 0;
608 ASDCP::MXF::Preface::Copy(const Preface& rhs)
610 InterchangeObject::Copy(rhs);
612 LastModifiedDate = rhs.LastModifiedDate;
613 Version = rhs.Version;
614 ObjectModelVersion = rhs.ObjectModelVersion;
615 PrimaryPackage = rhs.PrimaryPackage;
616 Identifications = rhs.Identifications;
617 ContentStorage = rhs.ContentStorage;
618 OperationalPattern = rhs.OperationalPattern;
619 EssenceContainers = rhs.EssenceContainers;
620 DMSchemes = rhs.DMSchemes;
621 ApplicationSchemes = rhs.ApplicationSchemes;
622 ConformsToSpecifications = rhs.ConformsToSpecifications;
627 ASDCP::MXF::Preface::InitFromTLVSet(TLVReader& TLVSet)
629 Result_t result = InterchangeObject::InitFromTLVSet(TLVSet);
630 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, LastModifiedDate));
631 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi16(OBJ_READ_ARGS(Preface, Version));
632 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi32(OBJ_READ_ARGS_OPT(Preface, ObjectModelVersion));
633 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS_OPT(Preface, PrimaryPackage));
634 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, Identifications));
635 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, ContentStorage));
636 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, OperationalPattern));
637 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, EssenceContainers));
638 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, DMSchemes));
639 if ( ASDCP_SUCCESS(result) )
641 result = TLVSet.ReadObject(OBJ_READ_ARGS_OPT(Preface, ApplicationSchemes));
642 ApplicationSchemes.set_has_value( result == RESULT_OK);
644 if ( ASDCP_SUCCESS(result) )
646 result = TLVSet.ReadObject(OBJ_READ_ARGS_OPT(Preface, ConformsToSpecifications));
647 ConformsToSpecifications.set_has_value( result == RESULT_OK);
654 ASDCP::MXF::Preface::WriteToTLVSet(TLVWriter& TLVSet)
656 Result_t result = InterchangeObject::WriteToTLVSet(TLVSet);
657 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, LastModifiedDate));
658 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi16(OBJ_WRITE_ARGS(Preface, Version));
659 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi32(OBJ_WRITE_ARGS_OPT(Preface, ObjectModelVersion));
660 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS_OPT(Preface, PrimaryPackage));
661 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, Identifications));
662 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, ContentStorage));
663 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, OperationalPattern));
664 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, EssenceContainers));
665 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, DMSchemes));
666 if ( ASDCP_SUCCESS(result) && !ApplicationSchemes.empty() )
668 result = TLVSet.WriteObject(OBJ_WRITE_ARGS_OPT(Preface, ApplicationSchemes));
670 if ( ASDCP_SUCCESS(result) && !ConformsToSpecifications.empty())
672 result = TLVSet.WriteObject(OBJ_WRITE_ARGS_OPT(Preface, ConformsToSpecifications));
679 ASDCP::MXF::Preface::InitFromBuffer(const byte_t* p, ui32_t l)
681 return InterchangeObject::InitFromBuffer(p, l);
686 ASDCP::MXF::Preface::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
688 return InterchangeObject::WriteToBuffer(Buffer);
693 ASDCP::MXF::Preface::Dump(FILE* stream)
695 char identbuf[IdentBufferLen];
700 InterchangeObject::Dump(stream);
701 fprintf(stream, " %22s = %s\n", "LastModifiedDate", LastModifiedDate.EncodeString(identbuf, IdentBufferLen));
702 fprintf(stream, " %22s = %hu\n", "Version", Version);
704 if ( ! ObjectModelVersion.empty() )
705 fprintf(stream, " %22s = %u\n", "ObjectModelVersion", ObjectModelVersion.get());
707 if ( ! PrimaryPackage.empty() )
708 fprintf(stream, " %22s = %s\n", "PrimaryPackage", PrimaryPackage.get().EncodeHex(identbuf, IdentBufferLen));
710 fprintf(stream, " %22s:\n", "Identifications"); Identifications.Dump(stream);
711 fprintf(stream, " %22s = %s\n", "ContentStorage", ContentStorage.EncodeHex(identbuf, IdentBufferLen));
712 fprintf(stream, " %22s = %s\n", "OperationalPattern", OperationalPattern.EncodeString(identbuf, IdentBufferLen));
713 fprintf(stream, " %22s:\n", "EssenceContainers"); EssenceContainers.Dump(stream);
714 fprintf(stream, " %22s:\n", "DMSchemes"); DMSchemes.Dump(stream);
715 if ( ! ApplicationSchemes.empty() )
717 fprintf(stream, " %22s:\n", "ApplicationSchemes"); ApplicationSchemes.get().Dump(stream);
719 if ( ! ConformsToSpecifications.empty() )
721 fprintf(stream, " %22s:\n", "ConformsToSpecifications"); ConformsToSpecifications.get().Dump(stream);
725 //------------------------------------------------------------------------------------------
728 ASDCP::MXF::OP1aHeader::OP1aHeader(const Dictionary*& d) : Partition(d), m_Dict(d), m_Primer(d), m_Preface(0) {}
729 ASDCP::MXF::OP1aHeader::~OP1aHeader() {}
733 ASDCP::MXF::OP1aHeader::InitFromFile(const Kumu::FileReader& Reader)
735 Result_t result = Partition::InitFromFile(Reader);
737 if ( ASDCP_FAILURE(result) )
740 if ( m_Dict == &DefaultCompositeDict() )
742 // select more explicit dictionary if one is available
743 if ( OperationalPattern.MatchExact(MXFInterop_OPAtom_Entry().ul) )
745 m_Dict = &DefaultInteropDict();
747 else if ( OperationalPattern.MatchExact(SMPTE_390_OPAtom_Entry().ul) )
749 m_Dict = &DefaultSMPTEDict();
753 // slurp up the remainder of the header
754 if ( HeaderByteCount == 0 )
756 DefaultLogSink().Warn("MXF file contents incomplete.\n");
757 return RESULT_KLV_CODING(__LINE__, __FILE__);
759 else if ( HeaderByteCount < 1024 )
761 DefaultLogSink().Warn("Improbably small HeaderByteCount value: %qu\n", HeaderByteCount);
763 else if (HeaderByteCount > ( 4 * Kumu::Megabyte ) )
765 DefaultLogSink().Warn("Improbably huge HeaderByteCount value: %qu\n", HeaderByteCount);
768 result = m_HeaderData.Capacity(Kumu::xmin(4*Kumu::Megabyte, static_cast<ui32_t>(HeaderByteCount)));
770 if ( ASDCP_SUCCESS(result) )
773 result = Reader.Read(m_HeaderData.Data(), m_HeaderData.Capacity(), &read_count);
775 if ( ASDCP_FAILURE(result) )
777 DefaultLogSink().Error("OP1aHeader::InitFromFile, read failed.\n");
781 if ( read_count != m_HeaderData.Capacity() )
783 DefaultLogSink().Error("Short read of OP-Atom header metadata; wanted %u, got %u.\n",
784 m_HeaderData.Capacity(), read_count);
785 return RESULT_KLV_CODING(__LINE__, __FILE__);
789 if ( ASDCP_SUCCESS(result) )
790 result = InitFromBuffer(m_HeaderData.RoData(), m_HeaderData.Capacity());
797 ASDCP::MXF::OP1aHeader::InitFromPartitionBuffer(const byte_t* p, ui32_t l)
799 Result_t result = KLVPacket::InitFromBuffer(p, l);
801 if ( ASDCP_SUCCESS(result) )
802 result = Partition::InitFromBuffer(m_ValueStart, m_ValueLength); // test UL and OP
804 if ( ASDCP_SUCCESS(result) )
806 ui32_t pp_len = KLVPacket::PacketLength();
807 result = InitFromBuffer(p + pp_len, l - pp_len);
815 ASDCP::MXF::OP1aHeader::InitFromBuffer(const byte_t* p, ui32_t l)
818 Result_t result = RESULT_OK;
819 const byte_t* end_p = p + l;
821 while ( ASDCP_SUCCESS(result) && p < end_p )
823 // parse the packets and index them by uid, discard KLVFill items
824 InterchangeObject* object = CreateObject(m_Dict, p);
827 object->m_Lookup = &m_Primer;
828 result = object->InitFromBuffer(p, end_p - p);
830 const byte_t* redo_p = p;
831 p += object->PacketLength();
833 if ( ASDCP_SUCCESS(result) )
835 if ( object->IsA(m_Dict->ul(MDD_KLVFill)) )
841 DefaultLogSink().Error("Fill item short read: %d.\n", p - end_p);
844 else if ( object->IsA(m_Dict->ul(MDD_Primer)) ) // TODO: only one primer should be found
847 result = m_Primer.InitFromBuffer(redo_p, end_p - redo_p);
851 m_PacketList->AddPacket(object); // takes ownership
853 if ( object->IsA(m_Dict->ul(MDD_Preface)) && m_Preface == 0 )
854 m_Preface = (Preface*)object;
859 DefaultLogSink().Error("Error initializing OP1a header packet.\n");
860 // Kumu::hexdump(p-object->PacketLength(), object->PacketLength());
869 ASDCP::MXF::OP1aHeader::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
871 return m_PacketList->GetMDObjectByID(ObjectID, Object);
876 ASDCP::MXF::OP1aHeader::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
878 InterchangeObject* TmpObject;
883 return m_PacketList->GetMDObjectByType(ObjectID, Object);
888 ASDCP::MXF::OP1aHeader::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
890 return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
894 ASDCP::MXF::Identification*
895 ASDCP::MXF::OP1aHeader::GetIdentification()
897 InterchangeObject* Object;
899 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object)) )
900 return (Identification*)Object;
906 ASDCP::MXF::SourcePackage*
907 ASDCP::MXF::OP1aHeader::GetSourcePackage()
909 InterchangeObject* Object;
911 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object)) )
912 return (SourcePackage*)Object;
919 ASDCP::MXF::OP1aHeader::WriteToFile(Kumu::FileWriter& Writer, ui32_t HeaderSize)
922 if ( m_Preface == 0 )
925 if ( HeaderSize < 4096 )
927 DefaultLogSink().Error("HeaderSize %u is too small. Must be >= 4096\n", HeaderSize);
931 ASDCP::FrameBuffer HeaderBuffer;
932 HeaderByteCount = HeaderSize - ArchiveSize();
933 assert (HeaderByteCount <= 0xFFFFFFFFL);
934 Result_t result = HeaderBuffer.Capacity((ui32_t) HeaderByteCount);
935 m_Preface->m_Lookup = &m_Primer;
937 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
938 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
940 InterchangeObject* object = *pl_i;
941 object->m_Lookup = &m_Primer;
943 ASDCP::FrameBuffer WriteWrapper;
944 WriteWrapper.SetData(HeaderBuffer.Data() + HeaderBuffer.Size(),
945 HeaderBuffer.Capacity() - HeaderBuffer.Size());
946 result = object->WriteToBuffer(WriteWrapper);
947 HeaderBuffer.Size(HeaderBuffer.Size() + WriteWrapper.Size());
950 if ( ASDCP_SUCCESS(result) )
952 UL TmpUL(m_Dict->ul(MDD_ClosedCompleteHeader));
953 result = Partition::WriteToFile(Writer, TmpUL);
956 if ( ASDCP_SUCCESS(result) )
957 result = m_Primer.WriteToFile(Writer);
959 if ( ASDCP_SUCCESS(result) )
962 Writer.Write(HeaderBuffer.RoData(), HeaderBuffer.Size(), &write_count);
963 assert(write_count == HeaderBuffer.Size());
967 if ( ASDCP_SUCCESS(result) )
969 Kumu::fpos_t pos = Writer.Tell();
971 if ( pos > (Kumu::fpos_t)HeaderByteCount )
973 char intbuf[IntBufferLen];
974 DefaultLogSink().Error("Header size %s exceeds specified value %u\n",
980 ASDCP::FrameBuffer NilBuf;
981 ui32_t klv_fill_length = HeaderSize - (ui32_t)pos;
983 if ( klv_fill_length < kl_length )
985 DefaultLogSink().Error("Remaining region too small for KLV Fill header\n");
989 klv_fill_length -= kl_length;
990 result = WriteKLToFile(Writer, m_Dict->ul(MDD_KLVFill), klv_fill_length);
992 if ( ASDCP_SUCCESS(result) )
993 result = NilBuf.Capacity(klv_fill_length);
995 if ( ASDCP_SUCCESS(result) )
997 memset(NilBuf.Data(), 0, klv_fill_length);
999 Writer.Write(NilBuf.RoData(), klv_fill_length, &write_count);
1000 assert(write_count == klv_fill_length);
1009 ASDCP::MXF::OP1aHeader::Dump(FILE* stream)
1014 Partition::Dump(stream);
1015 m_Primer.Dump(stream);
1017 if ( m_Preface == 0 )
1018 fputs("No Preface loaded\n", stream);
1020 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
1021 for ( ; i != m_PacketList->m_List.end(); i++ )
1025 //------------------------------------------------------------------------------------------
1028 ASDCP::MXF::OPAtomIndexFooter::OPAtomIndexFooter(const Dictionary*& d) :
1029 Partition(d), m_Dict(d),
1030 m_CurrentSegment(0), m_BytesPerEditUnit(0), m_BodySID(0),
1031 m_ECOffset(0), m_Lookup(0)
1037 ASDCP::MXF::OPAtomIndexFooter::~OPAtomIndexFooter() {}
1041 ASDCP::MXF::OPAtomIndexFooter::InitFromFile(const Kumu::FileReader& Reader)
1043 Result_t result = Partition::InitFromFile(Reader); // test UL and OP
1045 // slurp up the remainder of the footer
1046 ui32_t read_count = 0;
1048 if ( ASDCP_SUCCESS(result) && IndexByteCount > 0 )
1050 assert (IndexByteCount <= 0xFFFFFFFFL);
1051 // At this point, m_FooterData may not have been initialized
1052 // so it's capacity is zero and data pointer is NULL
1053 // However, if IndexByteCount is zero then the capacity
1054 // doesn't change and the data pointer is not set.
1055 result = m_FooterData.Capacity((ui32_t) IndexByteCount);
1057 if ( ASDCP_SUCCESS(result) )
1058 result = Reader.Read(m_FooterData.Data(), m_FooterData.Capacity(), &read_count);
1060 if ( ASDCP_SUCCESS(result) && read_count != m_FooterData.Capacity() )
1062 DefaultLogSink().Error("Short read of footer partition: got %u, expecting %u\n",
1063 read_count, m_FooterData.Capacity());
1066 else if( ASDCP_SUCCESS(result) && !m_FooterData.Data() )
1068 DefaultLogSink().Error( "Buffer for footer partition not created: IndexByteCount = %u\n",
1073 if ( ASDCP_SUCCESS(result) )
1074 result = InitFromBuffer(m_FooterData.RoData(), m_FooterData.Capacity());
1082 ASDCP::MXF::OPAtomIndexFooter::InitFromPartitionBuffer(const byte_t* p, ui32_t l)
1084 Result_t result = KLVPacket::InitFromBuffer(p, l);
1086 if ( ASDCP_SUCCESS(result) )
1087 result = Partition::InitFromBuffer(m_ValueStart, m_ValueLength); // test UL and OP
1089 if ( ASDCP_SUCCESS(result) )
1091 ui32_t pp_len = KLVPacket::PacketLength();
1092 result = InitFromBuffer(p + pp_len, l - pp_len);
1100 ASDCP::MXF::OPAtomIndexFooter::InitFromBuffer(const byte_t* p, ui32_t l)
1102 Result_t result = RESULT_OK;
1103 const byte_t* end_p = p + l;
1105 while ( ASDCP_SUCCESS(result) && p < end_p )
1107 // parse the packets and index them by uid, discard KLVFill items
1108 InterchangeObject* object = CreateObject(m_Dict, p);
1111 object->m_Lookup = m_Lookup;
1112 result = object->InitFromBuffer(p, end_p - p);
1113 p += object->PacketLength();
1115 if ( ASDCP_SUCCESS(result) )
1117 m_PacketList->AddPacket(object); // takes ownership
1121 DefaultLogSink().Error("Error initializing OPAtom footer packet.\n");
1126 if ( ASDCP_FAILURE(result) )
1128 DefaultLogSink().Error("Failed to initialize OPAtomIndexFooter.\n");
1136 ASDCP::MXF::OPAtomIndexFooter::WriteToFile(Kumu::FileWriter& Writer, ui64_t duration)
1139 ASDCP::FrameBuffer FooterBuffer;
1140 ui32_t footer_size = m_PacketList->m_List.size() * MaxIndexSegmentSize; // segment-count * max-segment-size
1141 Result_t result = FooterBuffer.Capacity(footer_size);
1142 ui32_t iseg_count = 0;
1144 if ( m_CurrentSegment != 0 )
1146 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1147 m_CurrentSegment = 0;
1150 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
1151 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
1153 IndexTableSegment *segment = dynamic_cast<IndexTableSegment*>(*pl_i);
1158 if ( m_BytesPerEditUnit != 0 )
1160 if ( iseg_count != 1 )
1161 return RESULT_STATE;
1163 segment->IndexDuration = duration;
1167 InterchangeObject* object = *pl_i;
1168 object->m_Lookup = m_Lookup;
1170 ASDCP::FrameBuffer WriteWrapper;
1171 WriteWrapper.SetData(FooterBuffer.Data() + FooterBuffer.Size(),
1172 FooterBuffer.Capacity() - FooterBuffer.Size());
1173 result = object->WriteToBuffer(WriteWrapper);
1174 FooterBuffer.Size(FooterBuffer.Size() + WriteWrapper.Size());
1177 if ( ASDCP_SUCCESS(result) )
1179 IndexByteCount = FooterBuffer.Size();
1180 UL FooterUL(m_Dict->ul(MDD_CompleteFooter));
1181 result = Partition::WriteToFile(Writer, FooterUL);
1184 if ( ASDCP_SUCCESS(result) )
1186 ui32_t write_count = 0;
1187 result = Writer.Write(FooterBuffer.RoData(), FooterBuffer.Size(), &write_count);
1188 assert(write_count == FooterBuffer.Size());
1196 ASDCP::MXF::OPAtomIndexFooter::Dump(FILE* stream)
1201 Partition::Dump(stream);
1203 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
1204 for ( ; i != m_PacketList->m_List.end(); i++ )
1209 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
1211 return m_PacketList->GetMDObjectByID(ObjectID, Object);
1216 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
1218 InterchangeObject* TmpObject;
1221 Object = &TmpObject;
1223 return m_PacketList->GetMDObjectByType(ObjectID, Object);
1228 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
1230 return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
1235 ASDCP::MXF::OPAtomIndexFooter::Lookup(ui32_t frame_num, IndexTableSegment::IndexEntry& Entry) const
1237 std::list<InterchangeObject*>::iterator li;
1238 for ( li = m_PacketList->m_List.begin(); li != m_PacketList->m_List.end(); li++ )
1240 IndexTableSegment *segment = dynamic_cast<IndexTableSegment*>(*li);
1244 ui64_t start_pos = segment->IndexStartPosition;
1246 if ( segment->EditUnitByteCount > 0 )
1248 if ( m_PacketList->m_List.size() > 1 )
1249 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
1251 if ( ! segment->IndexEntryArray.empty() )
1252 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
1254 Entry.StreamOffset = (ui64_t)frame_num * segment->EditUnitByteCount;
1257 else if ( (ui64_t)frame_num >= start_pos
1258 && (ui64_t)frame_num < (start_pos + segment->IndexDuration) )
1260 ui64_t tmp = frame_num - start_pos;
1261 assert(tmp <= 0xFFFFFFFFL);
1263 if ( tmp < segment->IndexEntryArray.size() )
1265 Entry = segment->IndexEntryArray[(ui32_t) tmp];
1270 DefaultLogSink().Error("Malformed index table segment, IndexDuration does not match entries.\n");
1281 ASDCP::MXF::OPAtomIndexFooter::SetDeltaParams(const IndexTableSegment::DeltaEntry& delta)
1283 m_DefaultDeltaEntry = delta;
1288 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const Rational& Rate)
1292 m_BytesPerEditUnit = size;
1295 IndexTableSegment* Index = new IndexTableSegment(m_Dict);
1296 AddChildObject(Index);
1297 Index->EditUnitByteCount = m_BytesPerEditUnit;
1298 Index->IndexEditRate = Rate;
1303 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsVBR(IPrimerLookup* lookup, const Rational& Rate, Kumu::fpos_t offset)
1307 m_BytesPerEditUnit = 0;
1309 m_ECOffset = offset;
1314 ASDCP::MXF::OPAtomIndexFooter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
1316 if ( m_BytesPerEditUnit != 0 ) // are we CBR? that's bad
1318 DefaultLogSink().Error("Call to PushIndexEntry() failed: index is CBR\n");
1322 // do we have an available segment?
1323 if ( m_CurrentSegment == 0 )
1324 { // no, set up a new segment
1325 m_CurrentSegment = new IndexTableSegment(m_Dict);
1326 assert(m_CurrentSegment);
1327 AddChildObject(m_CurrentSegment);
1328 m_CurrentSegment->DeltaEntryArray.push_back(m_DefaultDeltaEntry);
1329 m_CurrentSegment->IndexEditRate = m_EditRate;
1330 m_CurrentSegment->IndexStartPosition = 0;
1332 else if ( m_CurrentSegment->IndexEntryArray.size() >= CBRIndexEntriesPerSegment )
1333 { // no, this one is full, start another
1334 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1335 ui64_t StartPosition = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
1337 m_CurrentSegment = new IndexTableSegment(m_Dict);
1338 assert(m_CurrentSegment);
1339 AddChildObject(m_CurrentSegment);
1340 m_CurrentSegment->DeltaEntryArray.push_back(m_DefaultDeltaEntry);
1341 m_CurrentSegment->IndexEditRate = m_EditRate;
1342 m_CurrentSegment->IndexStartPosition = StartPosition;
1345 m_CurrentSegment->IndexEntryArray.push_back(Entry);
1348 //------------------------------------------------------------------------------------------
1353 ASDCP::MXF::InterchangeObject::Copy(const InterchangeObject& rhs)
1356 InstanceUID = rhs.InstanceUID;
1357 GenerationUID = rhs.GenerationUID;
1362 ASDCP::MXF::InterchangeObject::InitFromTLVSet(TLVReader& TLVSet)
1364 Result_t result = TLVSet.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID));
1365 if ( ASDCP_SUCCESS(result) )
1366 result = TLVSet.ReadObject(OBJ_READ_ARGS_OPT(GenerationInterchangeObject, GenerationUID));
1372 ASDCP::MXF::InterchangeObject::WriteToTLVSet(TLVWriter& TLVSet)
1374 Result_t result = TLVSet.WriteObject(OBJ_WRITE_ARGS(InterchangeObject, InstanceUID));
1375 if ( ASDCP_SUCCESS(result) )
1376 result = TLVSet.WriteObject(OBJ_WRITE_ARGS_OPT(GenerationInterchangeObject, GenerationUID));
1382 ASDCP::MXF::InterchangeObject::InitFromBuffer(const byte_t* p, ui32_t l)
1385 Result_t result = RESULT_FALSE;
1387 if ( m_UL.HasValue() )
1389 result = KLVPacket::InitFromBuffer(p, l, m_UL);
1391 if ( ASDCP_SUCCESS(result) )
1393 if ( ( m_ValueStart + m_ValueLength ) > ( p + l ) )
1395 DefaultLogSink().Error("Interchange Object value extends past buffer length.\n");
1396 return RESULT_KLV_CODING(__LINE__, __FILE__);
1399 TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup);
1400 result = InitFromTLVSet(MemRDR);
1405 result = KLVPacket::InitFromBuffer(p, l);
1413 ASDCP::MXF::InterchangeObject::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
1415 if ( ! m_UL.HasValue() )
1416 return RESULT_STATE;
1418 TLVWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length, m_Lookup);
1419 Result_t result = WriteToTLVSet(MemWRT);
1421 if ( ASDCP_SUCCESS(result) )
1423 ui32_t packet_length = MemWRT.Length();
1424 result = WriteKLToBuffer(Buffer, packet_length);
1426 if ( ASDCP_SUCCESS(result) )
1427 Buffer.Size(Buffer.Size() + packet_length);
1435 ASDCP::MXF::InterchangeObject::Dump(FILE* stream)
1437 char identbuf[IdentBufferLen];
1439 fputc('\n', stream);
1440 KLVPacket::Dump(stream, *m_Dict, false);
1441 fprintf(stream, " InstanceUID = %s\n", InstanceUID.EncodeHex(identbuf, IdentBufferLen));
1443 if ( ! GenerationUID.empty() )
1444 fprintf(stream, " GenerationUID = %s\n", GenerationUID.get().EncodeHex(identbuf, IdentBufferLen));
1449 ASDCP::MXF::InterchangeObject::IsA(const byte_t* label)
1451 if ( m_KLLength == 0 || m_KeyStart == 0 )
1454 return ( memcmp(label, m_KeyStart, SMPTE_UL_LENGTH) == 0 );
1458 //------------------------------------------------------------------------------------------
1459 struct FactoryCompareUL
1461 bool operator()(const ASDCP::UL& lhs, const ASDCP::UL& rhs) const
1463 ui32_t test_size = lhs.Size() < rhs.Size() ? lhs.Size() : rhs.Size();
1465 for (ui32_t i = 0; i < test_size; i++)
1467 if (i == 7) continue; // skip version to be symmetrical with UL::operator==
1468 if (lhs.Value()[i] != rhs.Value()[i])
1469 return lhs.Value()[i] < rhs.Value()[i];
1476 typedef std::map<ASDCP::UL, ASDCP::MXF::MXFObjectFactory_t, FactoryCompareUL>FactoryMap_t;
1477 typedef FactoryMap_t::iterator FLi_t;
1480 class FactoryList : public FactoryMap_t
1489 Kumu::AutoMutex BlockLock(m_Lock);
1493 FLi_t Find(const byte_t* label) {
1494 Kumu::AutoMutex BlockLock(m_Lock);
1499 Kumu::AutoMutex BlockLock(m_Lock);
1503 void Insert(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory) {
1504 Kumu::AutoMutex BlockLock(m_Lock);
1505 insert(FactoryList::value_type(label, factory));
1510 static FactoryList s_FactoryList;
1511 static Kumu::Mutex s_InitLock;
1512 static bool s_TypesInit = false;
1517 ASDCP::MXF::SetObjectFactory(const ASDCP::UL& label, ASDCP::MXF::MXFObjectFactory_t factory)
1519 s_FactoryList.Insert(label, factory);
1523 ASDCP::MXF::InterchangeObject*
1524 ASDCP::MXF::CreateObject(const Dictionary*& Dict, const UL& label)
1526 if ( ! s_TypesInit )
1528 Kumu::AutoMutex BlockLock(s_InitLock);
1530 if ( ! s_TypesInit )
1532 MXF::Metadata_InitTypes(Dict);
1537 FLi_t i = s_FactoryList.find(label);
1539 if ( i == s_FactoryList.end() )
1540 return new InterchangeObject(Dict);
1542 return i->second(Dict);
1546 //------------------------------------------------------------------------------------------
1550 ASDCP::MXF::decode_mca_string(const std::string& s, const mca_label_map_t& labels, const Dictionary*& dict, const std::string& language,
1551 InterchangeObject_list_t& descriptor_list, ui32_t& channel_count)
1553 std::string symbol_buf;
1555 ASDCP::MXF::SoundfieldGroupLabelSubDescriptor *current_soundfield = 0, *prev_soundfield = 0;
1556 std::string::const_iterator i;
1558 for ( i = s.begin(); i != s.end(); ++i )
1562 if ( current_soundfield != 0 && symbol_buf.empty() )
1564 // appending to the existing soundfield group
1567 else if ( current_soundfield != 0 )
1569 DefaultLogSink().Error("Encountered '(', already processing a soundfield group.\n");
1572 else if ( symbol_buf.empty() )
1574 if ( prev_soundfield != 0 )
1576 current_soundfield = prev_soundfield;
1577 // appending to the existing soundfield group
1582 DefaultLogSink().Error("Encountered '(', without leading soundfield group symbol.\n");
1587 mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1589 if ( i == labels.end() )
1591 DefaultLogSink().Error("Unknown symbol: '%s'\n", symbol_buf.c_str());
1595 if ( i->second.ul.Value()[10] != 2 ) // magic depends on UL "Essence Facet" byte (see ST 428-12)
1597 DefaultLogSink().Error("Not a soundfield group symbol: '%s'\n", symbol_buf.c_str());
1601 current_soundfield = new ASDCP::MXF::SoundfieldGroupLabelSubDescriptor(dict);
1602 GenRandomValue(current_soundfield->MCALinkID);
1604 current_soundfield->MCATagSymbol = (i->second.requires_prefix ? "sg" : "") + i->first;
1605 current_soundfield->MCATagName = i->second.tag_name;
1606 current_soundfield->RFC5646SpokenLanguage = language;
1607 current_soundfield->MCALabelDictionaryID = i->second.ul;
1608 descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(current_soundfield));
1609 prev_soundfield = current_soundfield;
1612 else if ( *i == ')' )
1614 if ( current_soundfield == 0 )
1616 DefaultLogSink().Error("Encountered ')', not currently processing a soundfield group.\n");
1620 if ( symbol_buf.empty() )
1622 DefaultLogSink().Error("Soundfield group description contains no channels.\n");
1626 mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1628 if ( i == labels.end() )
1630 DefaultLogSink().Error("Unknown symbol: '%s'\n", symbol_buf.c_str());
1634 assert(current_soundfield);
1636 ASDCP::MXF::AudioChannelLabelSubDescriptor *channel_descr =
1637 new ASDCP::MXF::AudioChannelLabelSubDescriptor(dict);
1638 GenRandomValue(channel_descr->MCALinkID);
1640 channel_descr->SoundfieldGroupLinkID = current_soundfield->MCALinkID;
1641 channel_descr->MCAChannelID = channel_count++ + 1;
1642 channel_descr->MCATagSymbol = (i->second.requires_prefix ? "ch" : "") + i->first;
1643 channel_descr->MCATagName = i->second.tag_name;
1644 channel_descr->RFC5646SpokenLanguage = language;
1645 channel_descr->MCALabelDictionaryID = i->second.ul;
1646 descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(channel_descr));
1648 current_soundfield = 0;
1650 else if ( *i == ',' )
1652 if ( ! symbol_buf.empty() && ! symbol_buf.compare("-") )
1657 else if ( ! symbol_buf.empty() )
1659 mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1661 if ( i == labels.end() )
1663 DefaultLogSink().Error("Unknown symbol: '%s'\n", symbol_buf.c_str());
1667 if ( i->second.ul.Value()[10] != 1 ) // magic depends on UL "Essence Facet" byte (see ST 428-12)
1669 DefaultLogSink().Error("Not a channel symbol: '%s'\n", symbol_buf.c_str());
1673 ASDCP::MXF::AudioChannelLabelSubDescriptor *channel_descr =
1674 new ASDCP::MXF::AudioChannelLabelSubDescriptor(dict);
1675 GenRandomValue(channel_descr->MCALinkID);
1677 if ( current_soundfield != 0 )
1679 channel_descr->SoundfieldGroupLinkID = current_soundfield->MCALinkID;
1682 channel_descr->MCAChannelID = channel_count++ + 1;
1683 channel_descr->MCATagSymbol = (i->second.requires_prefix ? "ch" : "") + i->first;
1684 channel_descr->MCATagName = i->second.tag_name;
1685 channel_descr->RFC5646SpokenLanguage = language;
1686 channel_descr->MCALabelDictionaryID = i->second.ul;
1687 descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(channel_descr));
1691 else if ( *i == '-' || isalnum(*i) )
1695 else if ( ! isspace(*i) )
1697 DefaultLogSink().Error("Unexpected character '%c'.\n", *i);
1702 if ( ! symbol_buf.empty() && ! symbol_buf.compare("-") )
1706 else if ( ! symbol_buf.empty() )
1708 mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1710 if ( i == labels.end() )
1712 DefaultLogSink().Error("Unknown symbol: '%s'\n", symbol_buf.c_str());
1716 ASDCP::MXF::AudioChannelLabelSubDescriptor *channel_descr =
1717 new ASDCP::MXF::AudioChannelLabelSubDescriptor(dict);
1718 GenRandomValue(channel_descr->MCALinkID);
1720 if ( current_soundfield != 0 )
1722 channel_descr->SoundfieldGroupLinkID = current_soundfield->MCALinkID;
1725 channel_descr->MCAChannelID = channel_count++ + 1;
1726 channel_descr->MCATagSymbol = (i->second.requires_prefix ? "ch" : "") + i->first;
1727 channel_descr->MCATagName = i->second.tag_name;
1728 channel_descr->RFC5646SpokenLanguage = language;
1729 channel_descr->MCALabelDictionaryID = i->second.ul;
1730 descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(channel_descr));
1737 ASDCP::MXF::ASDCP_MCAConfigParser::ASDCP_MCAConfigParser(const Dictionary*& d) : m_Dict(d), m_ChannelCount(0)
1739 typedef mca_label_map_t::value_type pair;
1740 m_LabelMap.insert(pair("L", label_traits("Left" , true, m_Dict->ul(MDD_DCAudioChannel_L))));
1741 m_LabelMap.insert(pair("R", label_traits("Right" , true, m_Dict->ul(MDD_DCAudioChannel_R))));
1742 m_LabelMap.insert(pair("C", label_traits("Center" , true, m_Dict->ul(MDD_DCAudioChannel_C))));
1743 m_LabelMap.insert(pair("LFE", label_traits("LFE" , true, m_Dict->ul(MDD_DCAudioChannel_LFE))));
1744 m_LabelMap.insert(pair("Ls", label_traits("Left Surround" , true, m_Dict->ul(MDD_DCAudioChannel_Ls))));
1745 m_LabelMap.insert(pair("Rs", label_traits("Right Surround" , true, m_Dict->ul(MDD_DCAudioChannel_Rs))));
1746 m_LabelMap.insert(pair("Lss", label_traits("Left Side Surround" , true, m_Dict->ul(MDD_DCAudioChannel_Lss))));
1747 m_LabelMap.insert(pair("Rss", label_traits("Right Side Surround" , true, m_Dict->ul(MDD_DCAudioChannel_Rss))));
1748 m_LabelMap.insert(pair("Lrs", label_traits("Left Rear Surround" , true, m_Dict->ul(MDD_DCAudioChannel_Lrs))));
1749 m_LabelMap.insert(pair("Rrs", label_traits("Right Rear Surround" , true, m_Dict->ul(MDD_DCAudioChannel_Rrs))));
1750 m_LabelMap.insert(pair("Lc", label_traits("Left Center" , true, m_Dict->ul(MDD_DCAudioChannel_Lc))));
1751 m_LabelMap.insert(pair("Rc", label_traits("Right Center" , true, m_Dict->ul(MDD_DCAudioChannel_Rc))));
1752 m_LabelMap.insert(pair("Cs", label_traits("Center Surround" , true, m_Dict->ul(MDD_DCAudioChannel_Cs))));
1753 m_LabelMap.insert(pair("HI", label_traits("Hearing Impaired" , true, m_Dict->ul(MDD_DCAudioChannel_HI))));
1754 m_LabelMap.insert(pair("VIN", label_traits("Visually Impaired-Narrative" , true, m_Dict->ul(MDD_DCAudioChannel_VIN))));
1755 m_LabelMap.insert(pair("51", label_traits("5.1" , true, m_Dict->ul(MDD_DCAudioSoundfield_51))));
1756 m_LabelMap.insert(pair("71", label_traits("7.1DS" , true, m_Dict->ul(MDD_DCAudioSoundfield_71))));
1757 m_LabelMap.insert(pair("SDS", label_traits("7.1SDS" , true, m_Dict->ul(MDD_DCAudioSoundfield_SDS))));
1758 m_LabelMap.insert(pair("61", label_traits("6.1" , true, m_Dict->ul(MDD_DCAudioSoundfield_61))));
1759 m_LabelMap.insert(pair("M", label_traits("1.0 Monaural" , true, m_Dict->ul(MDD_DCAudioSoundfield_M))));
1760 m_LabelMap.insert(pair("FSKSync", label_traits("FSK Sync" , true, m_Dict->ul(MDD_DCAudioChannel_FSKSyncSignalChannel))));
1761 m_LabelMap.insert(pair("DBOX", label_traits("D-BOX Motion Code Primary Stream" , false, m_Dict->ul(MDD_DBOXMotionCodePrimaryStream))));
1762 m_LabelMap.insert(pair("DBOX2", label_traits("D-BOX Motion Code Secondary Stream", false, m_Dict->ul(MDD_DBOXMotionCodeSecondaryStream))));
1763 m_LabelMap.insert(pair("SLVS", label_traits("Sign Language Video Stream" , false, m_Dict->ul(MDD_AudioChannelSLVS))));
1768 ASDCP::MXF::ASDCP_MCAConfigParser::ChannelCount() const
1770 return m_ChannelCount;
1773 // 51(L,R,C,LFE,Ls,Rs),HI,VIN
1775 ASDCP::MXF::ASDCP_MCAConfigParser::DecodeString(const std::string& s, const std::string& language)
1777 return decode_mca_string(s, m_LabelMap, m_Dict, language, *this, m_ChannelCount);
1780 // ST(L,R),DNS(NSC001,NSC002),-,VIN
1781 ASDCP::MXF::AS02_MCAConfigParser::AS02_MCAConfigParser(const Dictionary*& d) : ASDCP::MXF::ASDCP_MCAConfigParser(d)
1783 typedef mca_label_map_t::value_type pair;
1784 m_LabelMap.insert(pair("M1", label_traits("Mono One", true, m_Dict->ul(MDD_IMFAudioChannel_M1))));
1785 m_LabelMap.insert(pair("M2", label_traits("Mono Two", true, m_Dict->ul(MDD_IMFAudioChannel_M2))));
1786 m_LabelMap.insert(pair("Lt", label_traits("Left Total", true, m_Dict->ul(MDD_IMFAudioChannel_Lt))));
1787 m_LabelMap.insert(pair("Rt", label_traits("Right Total", true, m_Dict->ul(MDD_IMFAudioChannel_Rt))));
1788 m_LabelMap.insert(pair("Lst", label_traits("Left Surround Total", true, m_Dict->ul(MDD_IMFAudioChannel_Lst))));
1789 m_LabelMap.insert(pair("Rst", label_traits("Right Surround Total", true, m_Dict->ul(MDD_IMFAudioChannel_Rst))));
1790 m_LabelMap.insert(pair("S", label_traits("Surround", true, m_Dict->ul(MDD_IMFAudioChannel_S))));
1791 m_LabelMap.insert(pair("ST", label_traits("Standard Stereo", true, m_Dict->ul(MDD_IMFAudioSoundfield_ST))));
1792 m_LabelMap.insert(pair("DM", label_traits("Dual Mono", true, m_Dict->ul(MDD_IMFAudioSoundfield_DM))));
1793 m_LabelMap.insert(pair("DNS", label_traits("Discrete Numbered Sources", true, m_Dict->ul(MDD_IMFAudioSoundfield_DNS))));
1794 m_LabelMap.insert(pair("30", label_traits("3.0", true, m_Dict->ul(MDD_IMFAudioSoundfield_30))));
1795 m_LabelMap.insert(pair("40", label_traits("4.0", true, m_Dict->ul(MDD_IMFAudioSoundfield_40))));
1796 m_LabelMap.insert(pair("50", label_traits("5.0", true, m_Dict->ul(MDD_IMFAudioSoundfield_50))));
1797 m_LabelMap.insert(pair("60", label_traits("6.0", true, m_Dict->ul(MDD_IMFAudioSoundfield_60))));
1798 m_LabelMap.insert(pair("70", label_traits("7.0DS", true, m_Dict->ul(MDD_IMFAudioSoundfield_70))));
1799 m_LabelMap.insert(pair("LtRt", label_traits("Lt-Rt",true, m_Dict->ul(MDD_IMFAudioSoundfield_LtRt))));
1800 m_LabelMap.insert(pair("51Ex", label_traits("5.1EX",true, m_Dict->ul(MDD_IMFAudioSoundfield_51Ex))));
1801 m_LabelMap.insert(pair("HA", label_traits("Hearing Accessibility", true, m_Dict->ul(MDD_IMFAudioSoundfield_HI))));
1802 m_LabelMap.insert(pair("VA", label_traits("Visual Accessibility", true, m_Dict->ul(MDD_IMFAudioSoundfield_VIN))));
1804 // assemble the set of Numbered Source Channel labels
1805 char name_buf[64], symbol_buf[64];
1807 memcpy(ul_buf, m_Dict->ul(MDD_IMFNumberedSourceChannel), 16);
1809 for ( int i = 1; i < 128; ++i )
1811 snprintf(name_buf, 64, "Numbered Source Channel %03d", i);
1812 snprintf(symbol_buf, 64, "NSC%03d", i);
1814 m_LabelMap.insert(pair(symbol_buf, label_traits(name_buf, true, UL(ul_buf))));
1820 ASDCP::MXF::GetEditRateFromFP(ASDCP::MXF::OP1aHeader& header, ASDCP::Rational& edit_rate)
1822 bool has_first_item = false;
1824 MXF::InterchangeObject* temp_item;
1825 std::list<MXF::InterchangeObject*> temp_items;
1827 Result_t result = header.GetMDObjectsByType(DefaultCompositeDict().ul(MDD_SourcePackage), temp_items);
1829 if ( KM_FAILURE(result) )
1831 DefaultLogSink().Error("The MXF header does not contain a FilePackage item.\n");
1835 if ( temp_items.size() != 1 )
1837 DefaultLogSink().Error("The MXF header must contain one FilePackage item, found %d.\n", temp_items.size());
1842 MXF::Array<UUID>::const_iterator i;
1843 MXF::SourcePackage *source_package = dynamic_cast<MXF::SourcePackage*>(temp_items.front());
1844 assert(source_package);
1846 for ( i = source_package->Tracks.begin(); i != source_package->Tracks.end(); ++i )
1849 result = header.GetMDObjectByID(*i, &temp_item);
1851 if ( KM_FAILURE(result) )
1853 DefaultLogSink().Error("The MXF header is incomplete: strong reference %s leads nowhere.\n",
1854 i->EncodeHex(buf, 64));
1858 MXF::Track *track = dynamic_cast<MXF::Track*>(temp_item);
1862 DefaultLogSink().Error("The MXF header is incomplete: %s is not a Track item.\n",
1863 i->EncodeHex(buf, 64));
1868 result = header.GetMDObjectByID(track->Sequence, &temp_item);
1870 if ( KM_FAILURE(result) )
1872 DefaultLogSink().Error("The MXF header is incomplete: strong reference %s leads nowhere.\n",
1873 i->EncodeHex(buf, 64));
1877 MXF::Sequence *sequence = dynamic_cast<MXF::Sequence*>(temp_item);
1879 if ( sequence == 0 )
1881 DefaultLogSink().Error("The MXF header is incomplete: %s is not a Sequence item.\n",
1882 track->Sequence.get().EncodeHex(buf, 64));
1886 if ( sequence->StructuralComponents.size() != 1 )
1888 DefaultLogSink().Error("The Sequence item must contain one reference to an esence item, found %d.\n",
1889 sequence->StructuralComponents.size());
1894 result = header.GetMDObjectByID(sequence->StructuralComponents.front(), &temp_item);
1896 if ( KM_FAILURE(result) )
1898 DefaultLogSink().Error("The MXF header is incomplete: strong reference %s leads nowhere.\n",
1899 sequence->StructuralComponents.front().EncodeHex(buf, 64));
1903 if ( temp_item->IsA(DefaultCompositeDict().ul(MDD_SourceClip)) )
1905 MXF::SourceClip *source_clip = dynamic_cast<MXF::SourceClip*>(temp_item);
1907 if ( source_clip == 0 )
1909 DefaultLogSink().Error("The MXF header is incomplete: %s is not a SourceClip item.\n",
1910 sequence->StructuralComponents.front().EncodeHex(buf, 64));
1914 if ( ! has_first_item )
1916 edit_rate = track->EditRate;
1917 has_first_item = true;
1919 else if ( edit_rate != track->EditRate )
1921 DefaultLogSink().Error("The MXF header is incomplete: %s EditRate value does not match others in the file.\n",
1922 sequence->StructuralComponents.front().EncodeHex(buf, 64));
1926 else if ( ! temp_item->IsA(DefaultCompositeDict().ul(MDD_TimecodeComponent)) )
1928 DefaultLogSink().Error("Reference from Sequence to an unexpected type: %s.\n", temp_item->ObjectName());