2 Copyright (c) 2005-2012, 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) )
60 result = RESULT_FAIL; // File is smaller than an empty packet!
62 if ( ASDCP_SUCCESS(result) )
63 result = Reader.Seek(end_pos - 4);
65 // get the ui32_t RIP length
67 byte_t intbuf[MXF_BER_LENGTH];
70 if ( ASDCP_SUCCESS(result) )
72 result = Reader.Read(intbuf, MXF_BER_LENGTH, &read_count);
74 if ( ASDCP_SUCCESS(result) && read_count != 4 )
78 if ( ASDCP_SUCCESS(result) )
80 rip_size = KM_i32_BE(Kumu::cp2i<ui32_t>(intbuf));
82 if ( rip_size > end_pos ) // RIP can't be bigger than the file
86 // reposition to start of RIP
87 if ( ASDCP_SUCCESS(result) )
88 result = Reader.Seek(end_pos - rip_size);
95 ASDCP::MXF::RIP::GetPairBySID(ui32_t SID, Pair& outPair) const
97 Array<Pair>::const_iterator pi = PairArray.begin();
98 for ( ; pi != PairArray.end(); pi++ )
100 if ( (*pi).BodySID == SID )
112 ASDCP::MXF::RIP::InitFromFile(const Kumu::FileReader& Reader)
115 Result_t result = KLVFilePacket::InitFromFile(Reader, m_Dict->ul(MDD_RandomIndexMetadata));
117 if ( ASDCP_SUCCESS(result) )
119 Kumu::MemIOReader MemRDR(m_ValueStart, m_ValueLength - 4);
120 result = PairArray.Unarchive(&MemRDR) ? RESULT_OK : RESULT_KLV_CODING;
123 if ( ASDCP_FAILURE(result) )
124 DefaultLogSink().Error("Failed to initialize RIP\n");
131 ASDCP::MXF::RIP::WriteToFile(Kumu::FileWriter& Writer)
134 ASDCP::FrameBuffer Buffer;
135 ui32_t RIPSize = ( PairArray.size() * (sizeof(ui32_t) + sizeof(ui64_t)) ) + 4;
136 Result_t result = Buffer.Capacity(RIPSize);
138 if ( ASDCP_SUCCESS(result) )
139 result = WriteKLToFile(Writer, m_Dict->ul(MDD_RandomIndexMetadata), RIPSize);
141 if ( ASDCP_SUCCESS(result) )
143 result = RESULT_KLV_CODING;
145 Kumu::MemIOWriter MemWRT(Buffer.Data(), Buffer.Capacity());
146 if ( PairArray.Archive(&MemWRT) )
147 if ( MemWRT.WriteUi32BE(RIPSize + 20) )
149 Buffer.Size(MemWRT.Length());
154 if ( ASDCP_SUCCESS(result) )
155 result = Writer.Write(Buffer.RoData(), Buffer.Size());
162 ASDCP::MXF::RIP::Dump(FILE* stream)
167 KLVFilePacket::Dump(stream, *m_Dict, false);
168 PairArray.Dump(stream, false);
171 //------------------------------------------------------------------------------------------
175 ASDCP::MXF::Partition::PacketList::~PacketList() {
176 while ( ! m_List.empty() )
178 delete m_List.back();
185 ASDCP::MXF::Partition::PacketList::AddPacket(InterchangeObject* ThePacket) // takes ownership
188 m_Map.insert(std::map<UUID, InterchangeObject*>::value_type(ThePacket->InstanceUID, ThePacket));
189 m_List.push_back(ThePacket);
194 ASDCP::MXF::Partition::PacketList::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
196 ASDCP_TEST_NULL(Object);
198 std::map<UUID, InterchangeObject*>::iterator mi = m_Map.find(ObjectID);
200 if ( mi == m_Map.end() )
206 *Object = (*mi).second;
212 ASDCP::MXF::Partition::PacketList::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
214 ASDCP_TEST_NULL(ObjectID);
215 ASDCP_TEST_NULL(Object);
216 std::list<InterchangeObject*>::iterator li;
219 for ( li = m_List.begin(); li != m_List.end(); li++ )
221 if ( (*li)->HasUL(ObjectID) )
233 ASDCP::MXF::Partition::PacketList::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
235 ASDCP_TEST_NULL(ObjectID);
236 std::list<InterchangeObject*>::iterator li;
238 for ( li = m_List.begin(); li != m_List.end(); li++ )
240 if ( (*li)->HasUL(ObjectID) )
241 ObjectList.push_back(*li);
244 return ObjectList.empty() ? RESULT_FAIL : RESULT_OK;
247 //------------------------------------------------------------------------------------------
251 ASDCP::MXF::Partition::Partition(const Dictionary*& d) :
253 MajorVersion(1), MinorVersion(2),
254 KAGSize(1), ThisPartition(0), PreviousPartition(0),
255 FooterPartition(0), HeaderByteCount(0), IndexByteCount(0), IndexSID(0),
256 BodyOffset(0), BodySID(0)
258 m_PacketList = new PacketList;
261 ASDCP::MXF::Partition::~Partition()
267 ASDCP::MXF::Partition::AddChildObject(InterchangeObject* Object)
271 if ( ! Object->InstanceUID.HasValue() )
272 GenRandomValue(Object->InstanceUID);
274 m_PacketList->AddPacket(Object);
279 ASDCP::MXF::Partition::InitFromFile(const Kumu::FileReader& Reader)
281 Result_t result = KLVFilePacket::InitFromFile(Reader);
283 // could be one of several values
284 if ( ASDCP_SUCCESS(result) )
285 result = ASDCP::MXF::Partition::InitFromBuffer(m_ValueStart, m_ValueLength);
292 ASDCP::MXF::Partition::InitFromBuffer(const byte_t* p, ui32_t l)
294 Kumu::MemIOReader MemRDR(p, l);
295 Result_t result = RESULT_KLV_CODING;
297 if ( MemRDR.ReadUi16BE(&MajorVersion) )
298 if ( MemRDR.ReadUi16BE(&MinorVersion) )
299 if ( MemRDR.ReadUi32BE(&KAGSize) )
300 if ( MemRDR.ReadUi64BE(&ThisPartition) )
301 if ( MemRDR.ReadUi64BE(&PreviousPartition) )
302 if ( MemRDR.ReadUi64BE(&FooterPartition) )
303 if ( MemRDR.ReadUi64BE(&HeaderByteCount) )
304 if ( MemRDR.ReadUi64BE(&IndexByteCount) )
305 if ( MemRDR.ReadUi32BE(&IndexSID) )
306 if ( MemRDR.ReadUi64BE(&BodyOffset) )
307 if ( MemRDR.ReadUi32BE(&BodySID) )
308 if ( OperationalPattern.Unarchive(&MemRDR) )
309 if ( EssenceContainers.Unarchive(&MemRDR) )
312 if ( ASDCP_FAILURE(result) )
313 DefaultLogSink().Error("Failed to initialize Partition\n");
320 ASDCP::MXF::Partition::WriteToFile(Kumu::FileWriter& Writer, UL& PartitionLabel)
322 ASDCP::FrameBuffer Buffer;
323 Result_t result = Buffer.Capacity(1024);
325 if ( ASDCP_SUCCESS(result) )
327 Kumu::MemIOWriter MemWRT(Buffer.Data(), Buffer.Capacity());
328 result = RESULT_KLV_CODING;
329 if ( MemWRT.WriteUi16BE(MajorVersion) )
330 if ( MemWRT.WriteUi16BE(MinorVersion) )
331 if ( MemWRT.WriteUi32BE(KAGSize) )
332 if ( MemWRT.WriteUi64BE(ThisPartition) )
333 if ( MemWRT.WriteUi64BE(PreviousPartition) )
334 if ( MemWRT.WriteUi64BE(FooterPartition) )
335 if ( MemWRT.WriteUi64BE(HeaderByteCount) )
336 if ( MemWRT.WriteUi64BE(IndexByteCount) )
337 if ( MemWRT.WriteUi32BE(IndexSID) )
338 if ( MemWRT.WriteUi64BE(BodyOffset) )
339 if ( MemWRT.WriteUi32BE(BodySID) )
340 if ( OperationalPattern.Archive(&MemWRT) )
341 if ( EssenceContainers.Archive(&MemWRT) )
343 Buffer.Size(MemWRT.Length());
348 if ( ASDCP_SUCCESS(result) )
351 result = WriteKLToFile(Writer, PartitionLabel.Value(), Buffer.Size());
353 if ( ASDCP_SUCCESS(result) )
354 result = Writer.Write(Buffer.RoData(), Buffer.Size(), &write_count);
362 ASDCP::MXF::Partition::ArchiveSize()
365 + sizeof(ui16_t) + sizeof(ui16_t)
367 + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t)
372 + sizeof(ui32_t) + sizeof(ui32_t) + ( UUIDlen * EssenceContainers.size() ) );
377 ASDCP::MXF::Partition::Dump(FILE* stream)
379 char identbuf[IdentBufferLen];
384 KLVFilePacket::Dump(stream, *m_Dict, false);
385 fprintf(stream, " MajorVersion = %hu\n", MajorVersion);
386 fprintf(stream, " MinorVersion = %hu\n", MinorVersion);
387 fprintf(stream, " KAGSize = %u\n", KAGSize);
388 fprintf(stream, " ThisPartition = %s\n", ui64sz(ThisPartition, identbuf));
389 fprintf(stream, " PreviousPartition = %s\n", ui64sz(PreviousPartition, identbuf));
390 fprintf(stream, " FooterPartition = %s\n", ui64sz(FooterPartition, identbuf));
391 fprintf(stream, " HeaderByteCount = %s\n", ui64sz(HeaderByteCount, identbuf));
392 fprintf(stream, " IndexByteCount = %s\n", ui64sz(IndexByteCount, identbuf));
393 fprintf(stream, " IndexSID = %u\n", IndexSID);
394 fprintf(stream, " BodyOffset = %s\n", ui64sz(BodyOffset, identbuf));
395 fprintf(stream, " BodySID = %u\n", BodySID);
396 fprintf(stream, " OperationalPattern = %s\n", OperationalPattern.EncodeString(identbuf, IdentBufferLen));
397 fputs("Essence Containers:\n", stream); EssenceContainers.Dump(stream);
401 //------------------------------------------------------------------------------------------
404 class ASDCP::MXF::Primer::h__PrimerLookup : public std::map<UL, TagValue>
407 void InitWithBatch(ASDCP::MXF::Batch<ASDCP::MXF::Primer::LocalTagEntry>& Batch)
409 ASDCP::MXF::Batch<ASDCP::MXF::Primer::LocalTagEntry>::iterator i = Batch.begin();
411 for ( ; i != Batch.end(); i++ )
412 insert(std::map<UL, TagValue>::value_type((*i).UL, (*i).Tag));
418 ASDCP::MXF::Primer::Primer(const Dictionary*& d) : m_LocalTag(0xff), m_Dict(d) {
419 m_UL = m_Dict->ul(MDD_Primer);
423 ASDCP::MXF::Primer::~Primer() {}
427 ASDCP::MXF::Primer::ClearTagList()
429 LocalTagEntryBatch.clear();
430 m_Lookup = new h__PrimerLookup;
435 ASDCP::MXF::Primer::InitFromBuffer(const byte_t* p, ui32_t l)
438 Result_t result = KLVPacket::InitFromBuffer(p, l, m_Dict->ul(MDD_Primer));
440 if ( ASDCP_SUCCESS(result) )
442 Kumu::MemIOReader MemRDR(m_ValueStart, m_ValueLength);
443 result = LocalTagEntryBatch.Unarchive(&MemRDR) ? RESULT_OK : RESULT_KLV_CODING;
446 if ( ASDCP_SUCCESS(result) )
448 m_Lookup = new h__PrimerLookup;
449 m_Lookup->InitWithBatch(LocalTagEntryBatch);
452 if ( ASDCP_FAILURE(result) )
453 DefaultLogSink().Error("Failed to initialize Primer\n");
460 ASDCP::MXF::Primer::WriteToFile(Kumu::FileWriter& Writer)
462 ASDCP::FrameBuffer Buffer;
463 Result_t result = Buffer.Capacity(128*1024);
465 if ( ASDCP_SUCCESS(result) )
466 result = WriteToBuffer(Buffer);
468 if ( ASDCP_SUCCESS(result) )
469 result = Writer.Write(Buffer.RoData(), Buffer.Size());
476 ASDCP::MXF::Primer::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
479 ASDCP::FrameBuffer LocalTagBuffer;
480 Kumu::MemIOWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length);
481 Result_t result = LocalTagEntryBatch.Archive(&MemWRT) ? RESULT_OK : RESULT_KLV_CODING;
483 if ( ASDCP_SUCCESS(result) )
485 ui32_t packet_length = MemWRT.Length();
486 result = WriteKLToBuffer(Buffer, packet_length);
488 if ( ASDCP_SUCCESS(result) )
489 Buffer.Size(Buffer.Size() + packet_length);
497 ASDCP::MXF::Primer::InsertTag(const MDDEntry& Entry, ASDCP::TagValue& Tag)
501 std::map<UL, TagValue>::iterator i = m_Lookup->find(TestUL);
503 if ( i == m_Lookup->end() )
505 if ( Entry.tag.a == 0 && Entry.tag.b == 0 )
508 Tag.b = m_LocalTag--;
516 LocalTagEntry TmpEntry;
517 TmpEntry.UL = TestUL;
520 LocalTagEntryBatch.push_back(TmpEntry);
521 m_Lookup->insert(std::map<UL, TagValue>::value_type(TmpEntry.UL, TmpEntry.Tag));
533 ASDCP::MXF::Primer::TagForKey(const ASDCP::UL& Key, ASDCP::TagValue& Tag)
536 if ( m_Lookup.empty() )
538 DefaultLogSink().Error("Primer lookup is empty\n");
542 std::map<UL, TagValue>::iterator i = m_Lookup->find(Key);
544 if ( i == m_Lookup->end() )
553 ASDCP::MXF::Primer::Dump(FILE* stream)
556 char identbuf[IdentBufferLen];
561 KLVPacket::Dump(stream, *m_Dict, false);
562 fprintf(stream, "Primer: %u %s\n",
563 (ui32_t)LocalTagEntryBatch.size(),
564 ( LocalTagEntryBatch.size() == 1 ? "entry" : "entries" ));
566 Batch<LocalTagEntry>::iterator i = LocalTagEntryBatch.begin();
567 for ( ; i != LocalTagEntryBatch.end(); i++ )
569 const MDDEntry* Entry = m_Dict->FindUL((*i).UL.Value());
570 fprintf(stream, " %s %s\n", (*i).EncodeString(identbuf, IdentBufferLen), (Entry ? Entry->name : "Unknown"));
575 //------------------------------------------------------------------------------------------
579 ASDCP::MXF::Preface::Preface(const Dictionary*& d) :
580 InterchangeObject(d), m_Dict(d), Version(258), ObjectModelVersion(0)
583 m_UL = m_Dict->Type(MDD_Preface).ul;
588 ASDCP::MXF::Preface::Copy(const Preface& rhs)
590 InterchangeObject::Copy(rhs);
592 LastModifiedDate = rhs.LastModifiedDate;
593 Version = rhs.Version;
594 ObjectModelVersion = rhs.ObjectModelVersion;
595 PrimaryPackage = rhs.PrimaryPackage;
596 Identifications = rhs.Identifications;
597 ContentStorage = rhs.ContentStorage;
598 OperationalPattern = rhs.OperationalPattern;
599 EssenceContainers = rhs.EssenceContainers;
600 DMSchemes = rhs.DMSchemes;
605 ASDCP::MXF::Preface::InitFromTLVSet(TLVReader& TLVSet)
607 Result_t result = InterchangeObject::InitFromTLVSet(TLVSet);
608 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, LastModifiedDate));
609 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi16(OBJ_READ_ARGS(Preface, Version));
610 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi32(OBJ_READ_ARGS(Preface, ObjectModelVersion));
611 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, PrimaryPackage));
612 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, Identifications));
613 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, ContentStorage));
614 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, OperationalPattern));
615 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, EssenceContainers));
616 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, DMSchemes));
622 ASDCP::MXF::Preface::WriteToTLVSet(TLVWriter& TLVSet)
624 Result_t result = InterchangeObject::WriteToTLVSet(TLVSet);
625 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, LastModifiedDate));
626 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi16(OBJ_WRITE_ARGS(Preface, Version));
627 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi32(OBJ_WRITE_ARGS(Preface, ObjectModelVersion));
628 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, PrimaryPackage));
629 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, Identifications));
630 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, ContentStorage));
631 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, OperationalPattern));
632 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, EssenceContainers));
633 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, DMSchemes));
639 ASDCP::MXF::Preface::InitFromBuffer(const byte_t* p, ui32_t l)
641 return InterchangeObject::InitFromBuffer(p, l);
646 ASDCP::MXF::Preface::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
648 return InterchangeObject::WriteToBuffer(Buffer);
653 ASDCP::MXF::Preface::Dump(FILE* stream)
655 char identbuf[IdentBufferLen];
660 InterchangeObject::Dump(stream);
661 fprintf(stream, " %22s = %s\n", "LastModifiedDate", LastModifiedDate.EncodeString(identbuf, IdentBufferLen));
662 fprintf(stream, " %22s = %hu\n", "Version", Version);
663 fprintf(stream, " %22s = %u\n", "ObjectModelVersion", ObjectModelVersion);
664 fprintf(stream, " %22s = %s\n", "PrimaryPackage", PrimaryPackage.EncodeHex(identbuf, IdentBufferLen));
665 fprintf(stream, " %22s:\n", "Identifications"); Identifications.Dump(stream);
666 fprintf(stream, " %22s = %s\n", "ContentStorage", ContentStorage.EncodeHex(identbuf, IdentBufferLen));
667 fprintf(stream, " %22s = %s\n", "OperationalPattern", OperationalPattern.EncodeString(identbuf, IdentBufferLen));
668 fprintf(stream, " %22s:\n", "EssenceContainers"); EssenceContainers.Dump(stream);
669 fprintf(stream, " %22s:\n", "DMSchemes"); DMSchemes.Dump(stream);
672 //------------------------------------------------------------------------------------------
675 ASDCP::MXF::OPAtomHeader::OPAtomHeader(const Dictionary*& d) : Partition(d), m_Dict(d), m_RIP(d), m_Primer(d), m_Preface(0), m_HasRIP(false) {}
676 ASDCP::MXF::OPAtomHeader::~OPAtomHeader() {}
680 ASDCP::MXF::OPAtomHeader::InitFromFile(const Kumu::FileReader& Reader)
683 Result_t result = SeekToRIP(Reader);
685 if ( ASDCP_SUCCESS(result) )
687 result = m_RIP.InitFromFile(Reader);
688 ui32_t test_s = m_RIP.PairArray.size();
690 if ( ASDCP_FAILURE(result) )
692 DefaultLogSink().Error("File contains no RIP\n");
695 else if ( test_s == 0 )
697 DefaultLogSink().Error("RIP contains no Pairs.\n");
698 result = RESULT_FORMAT;
704 // OP-Atom states that there will be either two or three partitions:
705 // one closed header and one closed footer with an optional body
706 // SMPTE 429-5 files may have many partitions, see SMPTE 410M
707 DefaultLogSink().Warn("RIP count is less than 2: %u\n", test_s);
712 if ( m_RIP.PairArray.front().ByteOffset != 0 )
714 DefaultLogSink().Error("First Partition in RIP is not at offset 0.\n");
715 result = RESULT_FORMAT;
720 if ( ASDCP_SUCCESS(result) )
721 result = Reader.Seek(0);
723 if ( ASDCP_SUCCESS(result) )
724 result = Partition::InitFromFile(Reader); // test UL and OP
726 if ( ASDCP_FAILURE(result) )
729 // is it really OP-Atom?
731 UL OPAtomUL(SMPTE_390_OPAtom_Entry().ul);
732 UL InteropOPAtomUL(MXFInterop_OPAtom_Entry().ul);
734 if ( OperationalPattern.ExactMatch(OPAtomUL) ) // SMPTE
736 if ( m_Dict == &DefaultCompositeDict() )
737 m_Dict = &DefaultSMPTEDict();
739 else if ( OperationalPattern.ExactMatch(InteropOPAtomUL) ) // Interop
741 if ( m_Dict == &DefaultCompositeDict() )
742 m_Dict = &DefaultInteropDict();
746 char strbuf[IdentBufferLen];
747 const MDDEntry* Entry = m_Dict->FindUL(OperationalPattern.Value());
749 DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n",
750 OperationalPattern.EncodeString(strbuf, IdentBufferLen));
752 DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n", Entry->name);
755 // slurp up the remainder of the header
756 if ( HeaderByteCount < 1024 )
757 DefaultLogSink().Warn("Improbably small HeaderByteCount value: %u\n", HeaderByteCount);
759 assert (HeaderByteCount <= 0xFFFFFFFFL);
760 result = m_Buffer.Capacity((ui32_t) HeaderByteCount);
762 if ( ASDCP_SUCCESS(result) )
765 result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
767 if ( ASDCP_FAILURE(result) )
770 if ( read_count != m_Buffer.Capacity() )
772 DefaultLogSink().Error("Short read of OP-Atom header metadata; wanted %u, got %u\n",
773 m_Buffer.Capacity(), read_count);
774 return RESULT_KLV_CODING;
778 if ( ASDCP_SUCCESS(result) )
779 result = InitFromBuffer(m_Buffer.RoData(), m_Buffer.Capacity());
786 ASDCP::MXF::OPAtomHeader::InitFromPartitionBuffer(const byte_t* p, ui32_t l)
788 Result_t result = KLVPacket::InitFromBuffer(p, l);
790 if ( ASDCP_SUCCESS(result) )
791 result = Partition::InitFromBuffer(m_ValueStart, m_ValueLength); // test UL and OP
793 if ( ASDCP_SUCCESS(result) )
795 ui32_t pp_len = KLVPacket::PacketLength();
796 result = InitFromBuffer(p + pp_len, l - pp_len);
804 ASDCP::MXF::OPAtomHeader::InitFromBuffer(const byte_t* p, ui32_t l)
807 Result_t result = RESULT_OK;
808 const byte_t* end_p = p + l;
810 while ( ASDCP_SUCCESS(result) && p < end_p )
812 // parse the packets and index them by uid, discard KLVFill items
813 InterchangeObject* object = CreateObject(m_Dict, p);
816 object->m_Lookup = &m_Primer;
817 result = object->InitFromBuffer(p, end_p - p);
818 const byte_t* redo_p = p;
819 p += object->PacketLength();
820 // hexdump(p, object->PacketLength());
822 if ( ASDCP_SUCCESS(result) )
824 if ( object->IsA(m_Dict->ul(MDD_KLVFill)) )
828 else if ( object->IsA(m_Dict->ul(MDD_Primer)) ) // TODO: only one primer should be found
831 result = m_Primer.InitFromBuffer(redo_p, end_p - redo_p);
835 m_PacketList->AddPacket(object); // takes ownership
837 if ( object->IsA(m_Dict->ul(MDD_Preface)) && m_Preface == 0 )
838 m_Preface = (Preface*)object;
843 DefaultLogSink().Error("Error initializing packet\n");
852 ASDCP::MXF::OPAtomHeader::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
854 return m_PacketList->GetMDObjectByID(ObjectID, Object);
859 ASDCP::MXF::OPAtomHeader::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
861 InterchangeObject* TmpObject;
866 return m_PacketList->GetMDObjectByType(ObjectID, Object);
871 ASDCP::MXF::OPAtomHeader::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
873 return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
877 ASDCP::MXF::Identification*
878 ASDCP::MXF::OPAtomHeader::GetIdentification()
880 InterchangeObject* Object;
882 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object)) )
883 return (Identification*)Object;
889 ASDCP::MXF::SourcePackage*
890 ASDCP::MXF::OPAtomHeader::GetSourcePackage()
892 InterchangeObject* Object;
894 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object)) )
895 return (SourcePackage*)Object;
902 ASDCP::MXF::OPAtomHeader::GetRIP() { return m_RIP; }
906 ASDCP::MXF::OPAtomHeader::WriteToFile(Kumu::FileWriter& Writer, ui32_t HeaderSize)
909 if ( m_Preface == 0 )
912 if ( HeaderSize < 4096 )
914 DefaultLogSink().Error("HeaderSize %u is too small. Must be >= 4096\n", HeaderSize);
918 ASDCP::FrameBuffer HeaderBuffer;
919 HeaderByteCount = HeaderSize - ArchiveSize();
920 assert (HeaderByteCount <= 0xFFFFFFFFL);
921 Result_t result = HeaderBuffer.Capacity((ui32_t) HeaderByteCount);
922 m_Preface->m_Lookup = &m_Primer;
924 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
925 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
927 InterchangeObject* object = *pl_i;
928 object->m_Lookup = &m_Primer;
930 ASDCP::FrameBuffer WriteWrapper;
931 WriteWrapper.SetData(HeaderBuffer.Data() + HeaderBuffer.Size(),
932 HeaderBuffer.Capacity() - HeaderBuffer.Size());
933 result = object->WriteToBuffer(WriteWrapper);
934 HeaderBuffer.Size(HeaderBuffer.Size() + WriteWrapper.Size());
937 if ( ASDCP_SUCCESS(result) )
939 UL TmpUL(m_Dict->ul(MDD_ClosedCompleteHeader));
940 result = Partition::WriteToFile(Writer, TmpUL);
943 if ( ASDCP_SUCCESS(result) )
944 result = m_Primer.WriteToFile(Writer);
946 if ( ASDCP_SUCCESS(result) )
949 Writer.Write(HeaderBuffer.RoData(), HeaderBuffer.Size(), &write_count);
950 assert(write_count == HeaderBuffer.Size());
954 if ( ASDCP_SUCCESS(result) )
956 Kumu::fpos_t pos = Writer.Tell();
958 if ( pos > (Kumu::fpos_t)HeaderByteCount )
960 char intbuf[IntBufferLen];
961 DefaultLogSink().Error("Header size %s exceeds specified value %u\n",
967 ASDCP::FrameBuffer NilBuf;
968 ui32_t klv_fill_length = HeaderSize - (ui32_t)pos;
970 if ( klv_fill_length < kl_length )
972 DefaultLogSink().Error("Remaining region too small for KLV Fill header\n");
976 klv_fill_length -= kl_length;
977 result = WriteKLToFile(Writer, m_Dict->ul(MDD_KLVFill), klv_fill_length);
979 if ( ASDCP_SUCCESS(result) )
980 result = NilBuf.Capacity(klv_fill_length);
982 if ( ASDCP_SUCCESS(result) )
984 memset(NilBuf.Data(), 0, klv_fill_length);
986 Writer.Write(NilBuf.RoData(), klv_fill_length, &write_count);
987 assert(write_count == klv_fill_length);
996 ASDCP::MXF::OPAtomHeader::Dump(FILE* stream)
1001 Partition::Dump(stream);
1002 m_Primer.Dump(stream);
1004 if ( m_Preface == 0 )
1005 fputs("No Preface loaded\n", stream);
1007 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
1008 for ( ; i != m_PacketList->m_List.end(); i++ )
1012 //------------------------------------------------------------------------------------------
1015 ASDCP::MXF::OPAtomIndexFooter::OPAtomIndexFooter(const Dictionary*& d) :
1016 Partition(d), m_Dict(d),
1017 m_CurrentSegment(0), m_BytesPerEditUnit(0), m_BodySID(0),
1018 m_ECOffset(0), m_Lookup(0)
1024 ASDCP::MXF::OPAtomIndexFooter::~OPAtomIndexFooter() {}
1028 ASDCP::MXF::OPAtomIndexFooter::InitFromFile(const Kumu::FileReader& Reader)
1030 Result_t result = Partition::InitFromFile(Reader); // test UL and OP
1032 // slurp up the remainder of the footer
1035 if ( ASDCP_SUCCESS(result) )
1037 assert (IndexByteCount <= 0xFFFFFFFFL);
1038 result = m_Buffer.Capacity((ui32_t) IndexByteCount);
1041 if ( ASDCP_SUCCESS(result) )
1042 result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
1044 if ( ASDCP_SUCCESS(result) && read_count != m_Buffer.Capacity() )
1046 DefaultLogSink().Error("Short read of footer partition: got %u, expecting %u\n",
1047 read_count, m_Buffer.Capacity());
1051 if ( ASDCP_SUCCESS(result) )
1052 result = InitFromBuffer(m_Buffer.RoData(), m_Buffer.Capacity());
1059 ASDCP::MXF::OPAtomIndexFooter::InitFromPartitionBuffer(const byte_t* p, ui32_t l)
1061 Result_t result = KLVPacket::InitFromBuffer(p, l);
1063 if ( ASDCP_SUCCESS(result) )
1064 result = Partition::InitFromBuffer(m_ValueStart, m_ValueLength); // test UL and OP
1066 if ( ASDCP_SUCCESS(result) )
1068 ui32_t pp_len = KLVPacket::PacketLength();
1069 result = InitFromBuffer(p + pp_len, l - pp_len);
1077 ASDCP::MXF::OPAtomIndexFooter::InitFromBuffer(const byte_t* p, ui32_t l)
1079 Result_t result = RESULT_OK;
1080 const byte_t* end_p = p + l;
1082 while ( ASDCP_SUCCESS(result) && p < end_p )
1084 // parse the packets and index them by uid, discard KLVFill items
1085 InterchangeObject* object = CreateObject(m_Dict, p);
1088 object->m_Lookup = m_Lookup;
1089 result = object->InitFromBuffer(p, end_p - p);
1090 p += object->PacketLength();
1092 if ( ASDCP_SUCCESS(result) )
1094 m_PacketList->AddPacket(object); // takes ownership
1098 DefaultLogSink().Error("Error initializing packet\n");
1103 if ( ASDCP_FAILURE(result) )
1104 DefaultLogSink().Error("Failed to initialize OPAtomIndexFooter\n");
1111 ASDCP::MXF::OPAtomIndexFooter::WriteToFile(Kumu::FileWriter& Writer, ui64_t duration)
1114 ASDCP::FrameBuffer FooterBuffer;
1115 ui32_t footer_size = m_PacketList->m_List.size() * MaxIndexSegmentSize; // segment-count * max-segment-size
1116 Result_t result = FooterBuffer.Capacity(footer_size);
1117 ui32_t iseg_count = 0;
1119 if ( m_CurrentSegment != 0 )
1121 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1122 m_CurrentSegment = 0;
1125 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
1126 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
1128 if ( (*pl_i)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1131 IndexTableSegment* Segment = (IndexTableSegment*)(*pl_i);
1133 if ( m_BytesPerEditUnit != 0 )
1135 if ( iseg_count != 1 )
1136 return RESULT_STATE;
1138 Segment->IndexDuration = duration;
1142 InterchangeObject* object = *pl_i;
1143 object->m_Lookup = m_Lookup;
1145 ASDCP::FrameBuffer WriteWrapper;
1146 WriteWrapper.SetData(FooterBuffer.Data() + FooterBuffer.Size(),
1147 FooterBuffer.Capacity() - FooterBuffer.Size());
1148 result = object->WriteToBuffer(WriteWrapper);
1149 FooterBuffer.Size(FooterBuffer.Size() + WriteWrapper.Size());
1152 if ( ASDCP_SUCCESS(result) )
1154 IndexByteCount = FooterBuffer.Size();
1155 UL FooterUL(m_Dict->ul(MDD_CompleteFooter));
1156 result = Partition::WriteToFile(Writer, FooterUL);
1159 if ( ASDCP_SUCCESS(result) )
1161 ui32_t write_count = 0;
1162 result = Writer.Write(FooterBuffer.RoData(), FooterBuffer.Size(), &write_count);
1163 assert(write_count == FooterBuffer.Size());
1171 ASDCP::MXF::OPAtomIndexFooter::Dump(FILE* stream)
1176 Partition::Dump(stream);
1178 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
1179 for ( ; i != m_PacketList->m_List.end(); i++ )
1184 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
1186 return m_PacketList->GetMDObjectByID(ObjectID, Object);
1191 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
1193 InterchangeObject* TmpObject;
1196 Object = &TmpObject;
1198 return m_PacketList->GetMDObjectByType(ObjectID, Object);
1203 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
1205 return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
1210 ASDCP::MXF::OPAtomIndexFooter::Lookup(ui32_t frame_num, IndexTableSegment::IndexEntry& Entry) const
1212 std::list<InterchangeObject*>::iterator li;
1213 for ( li = m_PacketList->m_List.begin(); li != m_PacketList->m_List.end(); li++ )
1215 if ( (*li)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1217 IndexTableSegment* Segment = (IndexTableSegment*)(*li);
1218 ui64_t start_pos = Segment->IndexStartPosition;
1220 if ( Segment->EditUnitByteCount > 0 )
1222 if ( m_PacketList->m_List.size() > 1 )
1223 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
1225 if ( ! Segment->IndexEntryArray.empty() )
1226 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
1228 Entry.StreamOffset = (ui64_t)frame_num * Segment->EditUnitByteCount;
1231 else if ( (ui64_t)frame_num >= start_pos
1232 && (ui64_t)frame_num < (start_pos + Segment->IndexDuration) )
1234 ui64_t tmp = frame_num - start_pos;
1235 assert(tmp <= 0xFFFFFFFFL);
1236 Entry = Segment->IndexEntryArray[(ui32_t) tmp];
1247 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const Rational& Rate)
1251 m_BytesPerEditUnit = size;
1254 IndexTableSegment* Index = new IndexTableSegment(m_Dict);
1255 AddChildObject(Index);
1256 Index->EditUnitByteCount = m_BytesPerEditUnit;
1257 Index->IndexEditRate = Rate;
1262 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsVBR(IPrimerLookup* lookup, const Rational& Rate, Kumu::fpos_t offset)
1266 m_BytesPerEditUnit = 0;
1268 m_ECOffset = offset;
1273 ASDCP::MXF::OPAtomIndexFooter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
1275 if ( m_BytesPerEditUnit != 0 ) // are we CBR? that's bad
1277 DefaultLogSink().Error("Call to PushIndexEntry() failed: index is CBR\n");
1281 // do we have an available segment?
1282 if ( m_CurrentSegment == 0 )
1283 { // no, set up a new segment
1284 m_CurrentSegment = new IndexTableSegment(m_Dict);
1285 assert(m_CurrentSegment);
1286 AddChildObject(m_CurrentSegment);
1287 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1288 m_CurrentSegment->IndexEditRate = m_EditRate;
1289 m_CurrentSegment->IndexStartPosition = 0;
1291 else if ( m_CurrentSegment->IndexEntryArray.size() >= CBRIndexEntriesPerSegment )
1292 { // no, this one is full, start another
1293 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1294 ui64_t StartPosition = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
1296 m_CurrentSegment = new IndexTableSegment(m_Dict);
1297 assert(m_CurrentSegment);
1298 AddChildObject(m_CurrentSegment);
1299 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1300 m_CurrentSegment->IndexEditRate = m_EditRate;
1301 m_CurrentSegment->IndexStartPosition = StartPosition;
1304 m_CurrentSegment->IndexEntryArray.push_back(Entry);
1307 //------------------------------------------------------------------------------------------
1312 ASDCP::MXF::InterchangeObject::Copy(const InterchangeObject& rhs)
1315 InstanceUID = rhs.InstanceUID;
1316 GenerationUID = rhs.GenerationUID;
1321 ASDCP::MXF::InterchangeObject::InitFromTLVSet(TLVReader& TLVSet)
1323 Result_t result = TLVSet.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID));
1324 if ( ASDCP_SUCCESS(result) )
1325 result = TLVSet.ReadObject(OBJ_READ_ARGS(GenerationInterchangeObject, GenerationUID));
1331 ASDCP::MXF::InterchangeObject::WriteToTLVSet(TLVWriter& TLVSet)
1333 Result_t result = TLVSet.WriteObject(OBJ_WRITE_ARGS(InterchangeObject, InstanceUID));
1334 if ( ASDCP_SUCCESS(result) )
1335 result = TLVSet.WriteObject(OBJ_WRITE_ARGS(GenerationInterchangeObject, GenerationUID));
1341 ASDCP::MXF::InterchangeObject::InitFromBuffer(const byte_t* p, ui32_t l)
1344 Result_t result = RESULT_FALSE;
1346 if ( m_UL.HasValue() )
1348 result = KLVPacket::InitFromBuffer(p, l, m_UL);
1350 if ( ASDCP_SUCCESS(result) )
1352 TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup);
1353 result = InitFromTLVSet(MemRDR);
1358 result = KLVPacket::InitFromBuffer(p, l);
1366 ASDCP::MXF::InterchangeObject::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
1368 if ( ! m_UL.HasValue() )
1369 return RESULT_STATE;
1371 TLVWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length, m_Lookup);
1372 Result_t result = WriteToTLVSet(MemWRT);
1374 if ( ASDCP_SUCCESS(result) )
1376 ui32_t packet_length = MemWRT.Length();
1377 result = WriteKLToBuffer(Buffer, packet_length);
1379 if ( ASDCP_SUCCESS(result) )
1380 Buffer.Size(Buffer.Size() + packet_length);
1388 ASDCP::MXF::InterchangeObject::Dump(FILE* stream)
1390 char identbuf[IdentBufferLen];
1392 fputc('\n', stream);
1393 KLVPacket::Dump(stream, *m_Dict, false);
1394 fprintf(stream, " InstanceUID = %s\n", InstanceUID.EncodeHex(identbuf, IdentBufferLen));
1395 fprintf(stream, " GenerationUID = %s\n", GenerationUID.EncodeHex(identbuf, IdentBufferLen));
1400 ASDCP::MXF::InterchangeObject::IsA(const byte_t* label)
1402 if ( m_KLLength == 0 )
1405 return ( memcmp(label, m_KeyStart, SMPTE_UL_LENGTH) == 0 );
1409 //------------------------------------------------------------------------------------------
1412 typedef std::map<ASDCP::UL, ASDCP::MXF::MXFObjectFactory_t>FactoryMap_t;
1413 typedef FactoryMap_t::iterator FLi_t;
1416 class FactoryList : public FactoryMap_t
1425 Kumu::AutoMutex BlockLock(m_Lock);
1429 FLi_t Find(const byte_t* label) {
1430 Kumu::AutoMutex BlockLock(m_Lock);
1435 Kumu::AutoMutex BlockLock(m_Lock);
1439 void Insert(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory) {
1440 Kumu::AutoMutex BlockLock(m_Lock);
1441 insert(FactoryList::value_type(label, factory));
1446 static FactoryList s_FactoryList;
1447 static Kumu::Mutex s_InitLock;
1448 static bool s_TypesInit = false;
1453 ASDCP::MXF::SetObjectFactory(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory)
1455 s_FactoryList.Insert(label, factory);
1459 ASDCP::MXF::InterchangeObject*
1460 ASDCP::MXF::CreateObject(const Dictionary*& Dict, const UL& label)
1462 if ( ! s_TypesInit )
1464 Kumu::AutoMutex BlockLock(s_InitLock);
1466 if ( ! s_TypesInit )
1468 MXF::Metadata_InitTypes(Dict);
1473 FLi_t i = s_FactoryList.find(label.Value());
1475 if ( i == s_FactoryList.end() )
1476 return new InterchangeObject(Dict);
1478 return i->second(Dict);