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) )
61 DefaultLogSink().Error("File is smaller than an KLV empty 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, Pair& outPair) const
106 Array<Pair>::const_iterator pi = PairArray.begin();
107 for ( ; pi != PairArray.end(); pi++ )
109 if ( (*pi).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 Kumu::MemIOReader MemRDR(m_ValueStart, m_ValueLength - 4);
129 result = PairArray.Unarchive(&MemRDR) ? RESULT_OK : RESULT_KLV_CODING;
132 if ( ASDCP_FAILURE(result) )
133 DefaultLogSink().Error("Failed to initialize RIP\n");
140 ASDCP::MXF::RIP::WriteToFile(Kumu::FileWriter& Writer)
143 ASDCP::FrameBuffer Buffer;
144 ui32_t RIPSize = ( PairArray.size() * (sizeof(ui32_t) + sizeof(ui64_t)) ) + 4;
145 Result_t result = Buffer.Capacity(RIPSize);
147 if ( ASDCP_SUCCESS(result) )
148 result = WriteKLToFile(Writer, m_Dict->ul(MDD_RandomIndexMetadata), RIPSize);
150 if ( ASDCP_SUCCESS(result) )
152 result = RESULT_KLV_CODING;
154 Kumu::MemIOWriter MemWRT(Buffer.Data(), Buffer.Capacity());
155 if ( PairArray.Archive(&MemWRT) )
156 if ( MemWRT.WriteUi32BE(RIPSize + 20) )
158 Buffer.Size(MemWRT.Length());
163 if ( ASDCP_SUCCESS(result) )
164 result = Writer.Write(Buffer.RoData(), Buffer.Size());
171 ASDCP::MXF::RIP::Dump(FILE* stream)
176 KLVFilePacket::Dump(stream, *m_Dict, false);
177 PairArray.Dump(stream, false);
180 //------------------------------------------------------------------------------------------
184 ASDCP::MXF::Partition::PacketList::~PacketList() {
185 while ( ! m_List.empty() )
187 delete m_List.back();
194 ASDCP::MXF::Partition::PacketList::AddPacket(InterchangeObject* ThePacket) // takes ownership
197 m_Map.insert(std::map<UUID, InterchangeObject*>::value_type(ThePacket->InstanceUID, ThePacket));
198 m_List.push_back(ThePacket);
203 ASDCP::MXF::Partition::PacketList::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
205 ASDCP_TEST_NULL(Object);
207 std::map<UUID, InterchangeObject*>::iterator mi = m_Map.find(ObjectID);
209 if ( mi == m_Map.end() )
215 *Object = (*mi).second;
221 ASDCP::MXF::Partition::PacketList::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
223 ASDCP_TEST_NULL(ObjectID);
224 ASDCP_TEST_NULL(Object);
225 std::list<InterchangeObject*>::iterator li;
228 for ( li = m_List.begin(); li != m_List.end(); li++ )
230 if ( (*li)->HasUL(ObjectID) )
242 ASDCP::MXF::Partition::PacketList::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
244 ASDCP_TEST_NULL(ObjectID);
245 std::list<InterchangeObject*>::iterator li;
247 for ( li = m_List.begin(); li != m_List.end(); li++ )
249 if ( (*li)->HasUL(ObjectID) )
250 ObjectList.push_back(*li);
253 return ObjectList.empty() ? RESULT_FAIL : RESULT_OK;
256 //------------------------------------------------------------------------------------------
260 ASDCP::MXF::Partition::Partition(const Dictionary*& d) :
262 MajorVersion(1), MinorVersion(2),
263 KAGSize(1), ThisPartition(0), PreviousPartition(0),
264 FooterPartition(0), HeaderByteCount(0), IndexByteCount(0), IndexSID(0),
265 BodyOffset(0), BodySID(0)
267 m_PacketList = new PacketList;
270 ASDCP::MXF::Partition::~Partition()
276 ASDCP::MXF::Partition::AddChildObject(InterchangeObject* Object)
280 if ( ! Object->InstanceUID.HasValue() )
281 GenRandomValue(Object->InstanceUID);
283 m_PacketList->AddPacket(Object);
288 ASDCP::MXF::Partition::InitFromFile(const Kumu::FileReader& Reader)
290 Result_t result = KLVFilePacket::InitFromFile(Reader);
292 // could be one of several values
293 if ( ASDCP_SUCCESS(result) )
294 result = ASDCP::MXF::Partition::InitFromBuffer(m_ValueStart, m_ValueLength);
301 ASDCP::MXF::Partition::InitFromBuffer(const byte_t* p, ui32_t l)
303 Kumu::MemIOReader MemRDR(p, l);
304 Result_t result = RESULT_KLV_CODING;
306 if ( MemRDR.ReadUi16BE(&MajorVersion) )
307 if ( MemRDR.ReadUi16BE(&MinorVersion) )
308 if ( MemRDR.ReadUi32BE(&KAGSize) )
309 if ( MemRDR.ReadUi64BE(&ThisPartition) )
310 if ( MemRDR.ReadUi64BE(&PreviousPartition) )
311 if ( MemRDR.ReadUi64BE(&FooterPartition) )
312 if ( MemRDR.ReadUi64BE(&HeaderByteCount) )
313 if ( MemRDR.ReadUi64BE(&IndexByteCount) )
314 if ( MemRDR.ReadUi32BE(&IndexSID) )
315 if ( MemRDR.ReadUi64BE(&BodyOffset) )
316 if ( MemRDR.ReadUi32BE(&BodySID) )
317 if ( OperationalPattern.Unarchive(&MemRDR) )
318 if ( EssenceContainers.Unarchive(&MemRDR) )
321 if ( ASDCP_FAILURE(result) )
322 DefaultLogSink().Error("Failed to initialize Partition\n");
329 ASDCP::MXF::Partition::WriteToFile(Kumu::FileWriter& Writer, UL& PartitionLabel)
331 ASDCP::FrameBuffer Buffer;
332 Result_t result = Buffer.Capacity(1024);
334 if ( ASDCP_SUCCESS(result) )
336 Kumu::MemIOWriter MemWRT(Buffer.Data(), Buffer.Capacity());
337 result = RESULT_KLV_CODING;
338 if ( MemWRT.WriteUi16BE(MajorVersion) )
339 if ( MemWRT.WriteUi16BE(MinorVersion) )
340 if ( MemWRT.WriteUi32BE(KAGSize) )
341 if ( MemWRT.WriteUi64BE(ThisPartition) )
342 if ( MemWRT.WriteUi64BE(PreviousPartition) )
343 if ( MemWRT.WriteUi64BE(FooterPartition) )
344 if ( MemWRT.WriteUi64BE(HeaderByteCount) )
345 if ( MemWRT.WriteUi64BE(IndexByteCount) )
346 if ( MemWRT.WriteUi32BE(IndexSID) )
347 if ( MemWRT.WriteUi64BE(BodyOffset) )
348 if ( MemWRT.WriteUi32BE(BodySID) )
349 if ( OperationalPattern.Archive(&MemWRT) )
350 if ( EssenceContainers.Archive(&MemWRT) )
352 Buffer.Size(MemWRT.Length());
357 if ( ASDCP_SUCCESS(result) )
360 result = WriteKLToFile(Writer, PartitionLabel.Value(), Buffer.Size());
362 if ( ASDCP_SUCCESS(result) )
363 result = Writer.Write(Buffer.RoData(), Buffer.Size(), &write_count);
371 ASDCP::MXF::Partition::ArchiveSize()
374 + sizeof(ui16_t) + sizeof(ui16_t)
376 + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t)
381 + sizeof(ui32_t) + sizeof(ui32_t) + ( UUIDlen * EssenceContainers.size() ) );
386 ASDCP::MXF::Partition::Dump(FILE* stream)
388 char identbuf[IdentBufferLen];
393 KLVFilePacket::Dump(stream, *m_Dict, false);
394 fprintf(stream, " MajorVersion = %hu\n", MajorVersion);
395 fprintf(stream, " MinorVersion = %hu\n", MinorVersion);
396 fprintf(stream, " KAGSize = %u\n", KAGSize);
397 fprintf(stream, " ThisPartition = %s\n", ui64sz(ThisPartition, identbuf));
398 fprintf(stream, " PreviousPartition = %s\n", ui64sz(PreviousPartition, identbuf));
399 fprintf(stream, " FooterPartition = %s\n", ui64sz(FooterPartition, identbuf));
400 fprintf(stream, " HeaderByteCount = %s\n", ui64sz(HeaderByteCount, identbuf));
401 fprintf(stream, " IndexByteCount = %s\n", ui64sz(IndexByteCount, identbuf));
402 fprintf(stream, " IndexSID = %u\n", IndexSID);
403 fprintf(stream, " BodyOffset = %s\n", ui64sz(BodyOffset, identbuf));
404 fprintf(stream, " BodySID = %u\n", BodySID);
405 fprintf(stream, " OperationalPattern = %s\n", OperationalPattern.EncodeString(identbuf, IdentBufferLen));
406 fputs("Essence Containers:\n", stream); EssenceContainers.Dump(stream);
410 //------------------------------------------------------------------------------------------
413 class ASDCP::MXF::Primer::h__PrimerLookup : public std::map<UL, TagValue>
416 void InitWithBatch(ASDCP::MXF::Batch<ASDCP::MXF::Primer::LocalTagEntry>& Batch)
418 ASDCP::MXF::Batch<ASDCP::MXF::Primer::LocalTagEntry>::iterator i = Batch.begin();
420 for ( ; i != Batch.end(); i++ )
421 insert(std::map<UL, TagValue>::value_type((*i).UL, (*i).Tag));
427 ASDCP::MXF::Primer::Primer(const Dictionary*& d) : m_LocalTag(0xff), m_Dict(d) {
428 m_UL = m_Dict->ul(MDD_Primer);
432 ASDCP::MXF::Primer::~Primer() {}
436 ASDCP::MXF::Primer::ClearTagList()
438 LocalTagEntryBatch.clear();
439 m_Lookup = new h__PrimerLookup;
444 ASDCP::MXF::Primer::InitFromBuffer(const byte_t* p, ui32_t l)
447 Result_t result = KLVPacket::InitFromBuffer(p, l, m_Dict->ul(MDD_Primer));
449 if ( ASDCP_SUCCESS(result) )
451 Kumu::MemIOReader MemRDR(m_ValueStart, m_ValueLength);
452 result = LocalTagEntryBatch.Unarchive(&MemRDR) ? RESULT_OK : RESULT_KLV_CODING;
455 if ( ASDCP_SUCCESS(result) )
457 m_Lookup = new h__PrimerLookup;
458 m_Lookup->InitWithBatch(LocalTagEntryBatch);
461 if ( ASDCP_FAILURE(result) )
462 DefaultLogSink().Error("Failed to initialize Primer\n");
469 ASDCP::MXF::Primer::WriteToFile(Kumu::FileWriter& Writer)
471 ASDCP::FrameBuffer Buffer;
472 Result_t result = Buffer.Capacity(128*1024);
474 if ( ASDCP_SUCCESS(result) )
475 result = WriteToBuffer(Buffer);
477 if ( ASDCP_SUCCESS(result) )
478 result = Writer.Write(Buffer.RoData(), Buffer.Size());
485 ASDCP::MXF::Primer::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
488 ASDCP::FrameBuffer LocalTagBuffer;
489 Kumu::MemIOWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length);
490 Result_t result = LocalTagEntryBatch.Archive(&MemWRT) ? RESULT_OK : RESULT_KLV_CODING;
492 if ( ASDCP_SUCCESS(result) )
494 ui32_t packet_length = MemWRT.Length();
495 result = WriteKLToBuffer(Buffer, packet_length);
497 if ( ASDCP_SUCCESS(result) )
498 Buffer.Size(Buffer.Size() + packet_length);
506 ASDCP::MXF::Primer::InsertTag(const MDDEntry& Entry, ASDCP::TagValue& Tag)
510 std::map<UL, TagValue>::iterator i = m_Lookup->find(TestUL);
512 if ( i == m_Lookup->end() )
514 if ( Entry.tag.a == 0 && Entry.tag.b == 0 )
517 Tag.b = m_LocalTag--;
525 LocalTagEntry TmpEntry;
526 TmpEntry.UL = TestUL;
529 LocalTagEntryBatch.push_back(TmpEntry);
530 m_Lookup->insert(std::map<UL, TagValue>::value_type(TmpEntry.UL, TmpEntry.Tag));
542 ASDCP::MXF::Primer::TagForKey(const ASDCP::UL& Key, ASDCP::TagValue& Tag)
545 if ( m_Lookup.empty() )
547 DefaultLogSink().Error("Primer lookup is empty\n");
551 std::map<UL, TagValue>::iterator i = m_Lookup->find(Key);
553 if ( i == m_Lookup->end() )
562 ASDCP::MXF::Primer::Dump(FILE* stream)
565 char identbuf[IdentBufferLen];
570 KLVPacket::Dump(stream, *m_Dict, false);
571 fprintf(stream, "Primer: %u %s\n",
572 (ui32_t)LocalTagEntryBatch.size(),
573 ( LocalTagEntryBatch.size() == 1 ? "entry" : "entries" ));
575 Batch<LocalTagEntry>::iterator i = LocalTagEntryBatch.begin();
576 for ( ; i != LocalTagEntryBatch.end(); i++ )
578 const MDDEntry* Entry = m_Dict->FindUL((*i).UL.Value());
579 fprintf(stream, " %s %s\n", (*i).EncodeString(identbuf, IdentBufferLen), (Entry ? Entry->name : "Unknown"));
584 //------------------------------------------------------------------------------------------
588 ASDCP::MXF::Preface::Preface(const Dictionary*& d) :
589 InterchangeObject(d), m_Dict(d), Version(258), ObjectModelVersion(0)
592 m_UL = m_Dict->Type(MDD_Preface).ul;
597 ASDCP::MXF::Preface::Copy(const Preface& rhs)
599 InterchangeObject::Copy(rhs);
601 LastModifiedDate = rhs.LastModifiedDate;
602 Version = rhs.Version;
603 ObjectModelVersion = rhs.ObjectModelVersion;
604 PrimaryPackage = rhs.PrimaryPackage;
605 Identifications = rhs.Identifications;
606 ContentStorage = rhs.ContentStorage;
607 OperationalPattern = rhs.OperationalPattern;
608 EssenceContainers = rhs.EssenceContainers;
609 DMSchemes = rhs.DMSchemes;
614 ASDCP::MXF::Preface::InitFromTLVSet(TLVReader& TLVSet)
616 Result_t result = InterchangeObject::InitFromTLVSet(TLVSet);
617 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, LastModifiedDate));
618 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi16(OBJ_READ_ARGS(Preface, Version));
619 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi32(OBJ_READ_ARGS(Preface, ObjectModelVersion));
620 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, PrimaryPackage));
621 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, Identifications));
622 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, ContentStorage));
623 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, OperationalPattern));
624 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, EssenceContainers));
625 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, DMSchemes));
631 ASDCP::MXF::Preface::WriteToTLVSet(TLVWriter& TLVSet)
633 Result_t result = InterchangeObject::WriteToTLVSet(TLVSet);
634 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, LastModifiedDate));
635 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi16(OBJ_WRITE_ARGS(Preface, Version));
636 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi32(OBJ_WRITE_ARGS(Preface, ObjectModelVersion));
637 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, PrimaryPackage));
638 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, Identifications));
639 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, ContentStorage));
640 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, OperationalPattern));
641 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, EssenceContainers));
642 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, DMSchemes));
648 ASDCP::MXF::Preface::InitFromBuffer(const byte_t* p, ui32_t l)
650 return InterchangeObject::InitFromBuffer(p, l);
655 ASDCP::MXF::Preface::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
657 return InterchangeObject::WriteToBuffer(Buffer);
662 ASDCP::MXF::Preface::Dump(FILE* stream)
664 char identbuf[IdentBufferLen];
669 InterchangeObject::Dump(stream);
670 fprintf(stream, " %22s = %s\n", "LastModifiedDate", LastModifiedDate.EncodeString(identbuf, IdentBufferLen));
671 fprintf(stream, " %22s = %hu\n", "Version", Version);
672 fprintf(stream, " %22s = %u\n", "ObjectModelVersion", ObjectModelVersion);
673 fprintf(stream, " %22s = %s\n", "PrimaryPackage", PrimaryPackage.EncodeHex(identbuf, IdentBufferLen));
674 fprintf(stream, " %22s:\n", "Identifications"); Identifications.Dump(stream);
675 fprintf(stream, " %22s = %s\n", "ContentStorage", ContentStorage.EncodeHex(identbuf, IdentBufferLen));
676 fprintf(stream, " %22s = %s\n", "OperationalPattern", OperationalPattern.EncodeString(identbuf, IdentBufferLen));
677 fprintf(stream, " %22s:\n", "EssenceContainers"); EssenceContainers.Dump(stream);
678 fprintf(stream, " %22s:\n", "DMSchemes"); DMSchemes.Dump(stream);
681 //------------------------------------------------------------------------------------------
684 ASDCP::MXF::OPAtomHeader::OPAtomHeader(const Dictionary*& d) : Partition(d), m_Dict(d), m_RIP(d), m_Primer(d), m_Preface(0), m_HasRIP(false) {}
685 ASDCP::MXF::OPAtomHeader::~OPAtomHeader() {}
689 ASDCP::MXF::OPAtomHeader::InitFromFile(const Kumu::FileReader& Reader)
692 Result_t result = SeekToRIP(Reader);
694 if ( ASDCP_SUCCESS(result) )
696 result = m_RIP.InitFromFile(Reader);
697 ui32_t test_s = m_RIP.PairArray.size();
699 if ( ASDCP_FAILURE(result) )
701 DefaultLogSink().Error("File contains no RIP\n");
704 else if ( test_s == 0 )
706 DefaultLogSink().Error("RIP contains no Pairs.\n");
707 result = RESULT_FORMAT;
713 // OP-Atom states that there will be either two or three partitions:
714 // one closed header and one closed footer with an optional body
715 // SMPTE 429-5 files may have many partitions, see SMPTE 410M
716 DefaultLogSink().Warn("RIP count is less than 2: %u\n", test_s);
721 if ( m_RIP.PairArray.front().ByteOffset != 0 )
723 DefaultLogSink().Error("First Partition in RIP is not at offset 0.\n");
724 result = RESULT_FORMAT;
729 if ( ASDCP_SUCCESS(result) )
730 result = Reader.Seek(0);
732 if ( ASDCP_SUCCESS(result) )
733 result = Partition::InitFromFile(Reader); // test UL and OP
735 if ( ASDCP_FAILURE(result) )
738 // is it really OP-Atom?
740 UL OPAtomUL(SMPTE_390_OPAtom_Entry().ul);
741 UL InteropOPAtomUL(MXFInterop_OPAtom_Entry().ul);
743 if ( OperationalPattern.ExactMatch(OPAtomUL) ) // SMPTE
745 if ( m_Dict == &DefaultCompositeDict() )
746 m_Dict = &DefaultSMPTEDict();
748 else if ( OperationalPattern.ExactMatch(InteropOPAtomUL) ) // Interop
750 if ( m_Dict == &DefaultCompositeDict() )
751 m_Dict = &DefaultInteropDict();
755 char strbuf[IdentBufferLen];
756 const MDDEntry* Entry = m_Dict->FindUL(OperationalPattern.Value());
758 DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n",
759 OperationalPattern.EncodeString(strbuf, IdentBufferLen));
761 DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n", Entry->name);
764 // slurp up the remainder of the header
765 if ( HeaderByteCount < 1024 )
766 DefaultLogSink().Warn("Improbably small HeaderByteCount value: %u\n", HeaderByteCount);
768 assert (HeaderByteCount <= 0xFFFFFFFFL);
769 result = m_Buffer.Capacity((ui32_t) HeaderByteCount);
771 if ( ASDCP_SUCCESS(result) )
774 result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
776 if ( ASDCP_FAILURE(result) )
779 if ( read_count != m_Buffer.Capacity() )
781 DefaultLogSink().Error("Short read of OP-Atom header metadata; wanted %u, got %u\n",
782 m_Buffer.Capacity(), read_count);
783 return RESULT_KLV_CODING;
787 if ( ASDCP_SUCCESS(result) )
788 result = InitFromBuffer(m_Buffer.RoData(), m_Buffer.Capacity());
795 ASDCP::MXF::OPAtomHeader::InitFromPartitionBuffer(const byte_t* p, ui32_t l)
797 Result_t result = KLVPacket::InitFromBuffer(p, l);
799 if ( ASDCP_SUCCESS(result) )
800 result = Partition::InitFromBuffer(m_ValueStart, m_ValueLength); // test UL and OP
802 if ( ASDCP_SUCCESS(result) )
804 ui32_t pp_len = KLVPacket::PacketLength();
805 result = InitFromBuffer(p + pp_len, l - pp_len);
813 ASDCP::MXF::OPAtomHeader::InitFromBuffer(const byte_t* p, ui32_t l)
816 Result_t result = RESULT_OK;
817 const byte_t* end_p = p + l;
819 while ( ASDCP_SUCCESS(result) && p < end_p )
821 // parse the packets and index them by uid, discard KLVFill items
822 InterchangeObject* object = CreateObject(m_Dict, p);
825 object->m_Lookup = &m_Primer;
826 result = object->InitFromBuffer(p, end_p - p);
827 const byte_t* redo_p = p;
828 p += object->PacketLength();
829 // hexdump(p, object->PacketLength());
831 if ( ASDCP_SUCCESS(result) )
833 if ( object->IsA(m_Dict->ul(MDD_KLVFill)) )
837 else if ( object->IsA(m_Dict->ul(MDD_Primer)) ) // TODO: only one primer should be found
840 result = m_Primer.InitFromBuffer(redo_p, end_p - redo_p);
844 m_PacketList->AddPacket(object); // takes ownership
846 if ( object->IsA(m_Dict->ul(MDD_Preface)) && m_Preface == 0 )
847 m_Preface = (Preface*)object;
852 DefaultLogSink().Error("Error initializing packet\n");
861 ASDCP::MXF::OPAtomHeader::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
863 return m_PacketList->GetMDObjectByID(ObjectID, Object);
868 ASDCP::MXF::OPAtomHeader::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
870 InterchangeObject* TmpObject;
875 return m_PacketList->GetMDObjectByType(ObjectID, Object);
880 ASDCP::MXF::OPAtomHeader::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
882 return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
886 ASDCP::MXF::Identification*
887 ASDCP::MXF::OPAtomHeader::GetIdentification()
889 InterchangeObject* Object;
891 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object)) )
892 return (Identification*)Object;
898 ASDCP::MXF::SourcePackage*
899 ASDCP::MXF::OPAtomHeader::GetSourcePackage()
901 InterchangeObject* Object;
903 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object)) )
904 return (SourcePackage*)Object;
911 ASDCP::MXF::OPAtomHeader::GetRIP() { return m_RIP; }
915 ASDCP::MXF::OPAtomHeader::WriteToFile(Kumu::FileWriter& Writer, ui32_t HeaderSize)
918 if ( m_Preface == 0 )
921 if ( HeaderSize < 4096 )
923 DefaultLogSink().Error("HeaderSize %u is too small. Must be >= 4096\n", HeaderSize);
927 ASDCP::FrameBuffer HeaderBuffer;
928 HeaderByteCount = HeaderSize - ArchiveSize();
929 assert (HeaderByteCount <= 0xFFFFFFFFL);
930 Result_t result = HeaderBuffer.Capacity((ui32_t) HeaderByteCount);
931 m_Preface->m_Lookup = &m_Primer;
933 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
934 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
936 InterchangeObject* object = *pl_i;
937 object->m_Lookup = &m_Primer;
939 ASDCP::FrameBuffer WriteWrapper;
940 WriteWrapper.SetData(HeaderBuffer.Data() + HeaderBuffer.Size(),
941 HeaderBuffer.Capacity() - HeaderBuffer.Size());
942 result = object->WriteToBuffer(WriteWrapper);
943 HeaderBuffer.Size(HeaderBuffer.Size() + WriteWrapper.Size());
946 if ( ASDCP_SUCCESS(result) )
948 UL TmpUL(m_Dict->ul(MDD_ClosedCompleteHeader));
949 result = Partition::WriteToFile(Writer, TmpUL);
952 if ( ASDCP_SUCCESS(result) )
953 result = m_Primer.WriteToFile(Writer);
955 if ( ASDCP_SUCCESS(result) )
958 Writer.Write(HeaderBuffer.RoData(), HeaderBuffer.Size(), &write_count);
959 assert(write_count == HeaderBuffer.Size());
963 if ( ASDCP_SUCCESS(result) )
965 Kumu::fpos_t pos = Writer.Tell();
967 if ( pos > (Kumu::fpos_t)HeaderByteCount )
969 char intbuf[IntBufferLen];
970 DefaultLogSink().Error("Header size %s exceeds specified value %u\n",
976 ASDCP::FrameBuffer NilBuf;
977 ui32_t klv_fill_length = HeaderSize - (ui32_t)pos;
979 if ( klv_fill_length < kl_length )
981 DefaultLogSink().Error("Remaining region too small for KLV Fill header\n");
985 klv_fill_length -= kl_length;
986 result = WriteKLToFile(Writer, m_Dict->ul(MDD_KLVFill), klv_fill_length);
988 if ( ASDCP_SUCCESS(result) )
989 result = NilBuf.Capacity(klv_fill_length);
991 if ( ASDCP_SUCCESS(result) )
993 memset(NilBuf.Data(), 0, klv_fill_length);
995 Writer.Write(NilBuf.RoData(), klv_fill_length, &write_count);
996 assert(write_count == klv_fill_length);
1005 ASDCP::MXF::OPAtomHeader::Dump(FILE* stream)
1010 Partition::Dump(stream);
1011 m_Primer.Dump(stream);
1013 if ( m_Preface == 0 )
1014 fputs("No Preface loaded\n", stream);
1016 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
1017 for ( ; i != m_PacketList->m_List.end(); i++ )
1021 //------------------------------------------------------------------------------------------
1024 ASDCP::MXF::OPAtomIndexFooter::OPAtomIndexFooter(const Dictionary*& d) :
1025 Partition(d), m_Dict(d),
1026 m_CurrentSegment(0), m_BytesPerEditUnit(0), m_BodySID(0),
1027 m_ECOffset(0), m_Lookup(0)
1033 ASDCP::MXF::OPAtomIndexFooter::~OPAtomIndexFooter() {}
1037 ASDCP::MXF::OPAtomIndexFooter::InitFromFile(const Kumu::FileReader& Reader)
1039 Result_t result = Partition::InitFromFile(Reader); // test UL and OP
1041 // slurp up the remainder of the footer
1044 if ( ASDCP_SUCCESS(result) )
1046 assert (IndexByteCount <= 0xFFFFFFFFL);
1047 result = m_Buffer.Capacity((ui32_t) IndexByteCount);
1050 if ( ASDCP_SUCCESS(result) )
1051 result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
1053 if ( ASDCP_SUCCESS(result) && read_count != m_Buffer.Capacity() )
1055 DefaultLogSink().Error("Short read of footer partition: got %u, expecting %u\n",
1056 read_count, m_Buffer.Capacity());
1060 if ( ASDCP_SUCCESS(result) )
1061 result = InitFromBuffer(m_Buffer.RoData(), m_Buffer.Capacity());
1068 ASDCP::MXF::OPAtomIndexFooter::InitFromPartitionBuffer(const byte_t* p, ui32_t l)
1070 Result_t result = KLVPacket::InitFromBuffer(p, l);
1072 if ( ASDCP_SUCCESS(result) )
1073 result = Partition::InitFromBuffer(m_ValueStart, m_ValueLength); // test UL and OP
1075 if ( ASDCP_SUCCESS(result) )
1077 ui32_t pp_len = KLVPacket::PacketLength();
1078 result = InitFromBuffer(p + pp_len, l - pp_len);
1086 ASDCP::MXF::OPAtomIndexFooter::InitFromBuffer(const byte_t* p, ui32_t l)
1088 Result_t result = RESULT_OK;
1089 const byte_t* end_p = p + l;
1091 while ( ASDCP_SUCCESS(result) && p < end_p )
1093 // parse the packets and index them by uid, discard KLVFill items
1094 InterchangeObject* object = CreateObject(m_Dict, p);
1097 object->m_Lookup = m_Lookup;
1098 result = object->InitFromBuffer(p, end_p - p);
1099 p += object->PacketLength();
1101 if ( ASDCP_SUCCESS(result) )
1103 m_PacketList->AddPacket(object); // takes ownership
1107 DefaultLogSink().Error("Error initializing packet\n");
1112 if ( ASDCP_FAILURE(result) )
1113 DefaultLogSink().Error("Failed to initialize OPAtomIndexFooter\n");
1120 ASDCP::MXF::OPAtomIndexFooter::WriteToFile(Kumu::FileWriter& Writer, ui64_t duration)
1123 ASDCP::FrameBuffer FooterBuffer;
1124 ui32_t footer_size = m_PacketList->m_List.size() * MaxIndexSegmentSize; // segment-count * max-segment-size
1125 Result_t result = FooterBuffer.Capacity(footer_size);
1126 ui32_t iseg_count = 0;
1128 if ( m_CurrentSegment != 0 )
1130 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1131 m_CurrentSegment = 0;
1134 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
1135 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
1137 if ( (*pl_i)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1140 IndexTableSegment* Segment = (IndexTableSegment*)(*pl_i);
1142 if ( m_BytesPerEditUnit != 0 )
1144 if ( iseg_count != 1 )
1145 return RESULT_STATE;
1147 Segment->IndexDuration = duration;
1151 InterchangeObject* object = *pl_i;
1152 object->m_Lookup = m_Lookup;
1154 ASDCP::FrameBuffer WriteWrapper;
1155 WriteWrapper.SetData(FooterBuffer.Data() + FooterBuffer.Size(),
1156 FooterBuffer.Capacity() - FooterBuffer.Size());
1157 result = object->WriteToBuffer(WriteWrapper);
1158 FooterBuffer.Size(FooterBuffer.Size() + WriteWrapper.Size());
1161 if ( ASDCP_SUCCESS(result) )
1163 IndexByteCount = FooterBuffer.Size();
1164 UL FooterUL(m_Dict->ul(MDD_CompleteFooter));
1165 result = Partition::WriteToFile(Writer, FooterUL);
1168 if ( ASDCP_SUCCESS(result) )
1170 ui32_t write_count = 0;
1171 result = Writer.Write(FooterBuffer.RoData(), FooterBuffer.Size(), &write_count);
1172 assert(write_count == FooterBuffer.Size());
1180 ASDCP::MXF::OPAtomIndexFooter::Dump(FILE* stream)
1185 Partition::Dump(stream);
1187 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
1188 for ( ; i != m_PacketList->m_List.end(); i++ )
1193 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
1195 return m_PacketList->GetMDObjectByID(ObjectID, Object);
1200 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
1202 InterchangeObject* TmpObject;
1205 Object = &TmpObject;
1207 return m_PacketList->GetMDObjectByType(ObjectID, Object);
1212 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
1214 return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
1219 ASDCP::MXF::OPAtomIndexFooter::Lookup(ui32_t frame_num, IndexTableSegment::IndexEntry& Entry) const
1221 std::list<InterchangeObject*>::iterator li;
1222 for ( li = m_PacketList->m_List.begin(); li != m_PacketList->m_List.end(); li++ )
1224 if ( (*li)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1226 IndexTableSegment* Segment = (IndexTableSegment*)(*li);
1227 ui64_t start_pos = Segment->IndexStartPosition;
1229 if ( Segment->EditUnitByteCount > 0 )
1231 if ( m_PacketList->m_List.size() > 1 )
1232 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
1234 if ( ! Segment->IndexEntryArray.empty() )
1235 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
1237 Entry.StreamOffset = (ui64_t)frame_num * Segment->EditUnitByteCount;
1240 else if ( (ui64_t)frame_num >= start_pos
1241 && (ui64_t)frame_num < (start_pos + Segment->IndexDuration) )
1243 ui64_t tmp = frame_num - start_pos;
1244 assert(tmp <= 0xFFFFFFFFL);
1245 Entry = Segment->IndexEntryArray[(ui32_t) tmp];
1256 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const Rational& Rate)
1260 m_BytesPerEditUnit = size;
1263 IndexTableSegment* Index = new IndexTableSegment(m_Dict);
1264 AddChildObject(Index);
1265 Index->EditUnitByteCount = m_BytesPerEditUnit;
1266 Index->IndexEditRate = Rate;
1271 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsVBR(IPrimerLookup* lookup, const Rational& Rate, Kumu::fpos_t offset)
1275 m_BytesPerEditUnit = 0;
1277 m_ECOffset = offset;
1282 ASDCP::MXF::OPAtomIndexFooter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
1284 if ( m_BytesPerEditUnit != 0 ) // are we CBR? that's bad
1286 DefaultLogSink().Error("Call to PushIndexEntry() failed: index is CBR\n");
1290 // do we have an available segment?
1291 if ( m_CurrentSegment == 0 )
1292 { // no, set up a new segment
1293 m_CurrentSegment = new IndexTableSegment(m_Dict);
1294 assert(m_CurrentSegment);
1295 AddChildObject(m_CurrentSegment);
1296 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1297 m_CurrentSegment->IndexEditRate = m_EditRate;
1298 m_CurrentSegment->IndexStartPosition = 0;
1300 else if ( m_CurrentSegment->IndexEntryArray.size() >= CBRIndexEntriesPerSegment )
1301 { // no, this one is full, start another
1302 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1303 ui64_t StartPosition = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
1305 m_CurrentSegment = new IndexTableSegment(m_Dict);
1306 assert(m_CurrentSegment);
1307 AddChildObject(m_CurrentSegment);
1308 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1309 m_CurrentSegment->IndexEditRate = m_EditRate;
1310 m_CurrentSegment->IndexStartPosition = StartPosition;
1313 m_CurrentSegment->IndexEntryArray.push_back(Entry);
1316 //------------------------------------------------------------------------------------------
1321 ASDCP::MXF::InterchangeObject::Copy(const InterchangeObject& rhs)
1324 InstanceUID = rhs.InstanceUID;
1325 GenerationUID = rhs.GenerationUID;
1330 ASDCP::MXF::InterchangeObject::InitFromTLVSet(TLVReader& TLVSet)
1332 Result_t result = TLVSet.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID));
1333 if ( ASDCP_SUCCESS(result) )
1334 result = TLVSet.ReadObject(OBJ_READ_ARGS(GenerationInterchangeObject, GenerationUID));
1340 ASDCP::MXF::InterchangeObject::WriteToTLVSet(TLVWriter& TLVSet)
1342 Result_t result = TLVSet.WriteObject(OBJ_WRITE_ARGS(InterchangeObject, InstanceUID));
1343 if ( ASDCP_SUCCESS(result) )
1344 result = TLVSet.WriteObject(OBJ_WRITE_ARGS(GenerationInterchangeObject, GenerationUID));
1350 ASDCP::MXF::InterchangeObject::InitFromBuffer(const byte_t* p, ui32_t l)
1353 Result_t result = RESULT_FALSE;
1355 if ( m_UL.HasValue() )
1357 result = KLVPacket::InitFromBuffer(p, l, m_UL);
1359 if ( ASDCP_SUCCESS(result) )
1361 TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup);
1362 result = InitFromTLVSet(MemRDR);
1367 result = KLVPacket::InitFromBuffer(p, l);
1375 ASDCP::MXF::InterchangeObject::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
1377 if ( ! m_UL.HasValue() )
1378 return RESULT_STATE;
1380 TLVWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length, m_Lookup);
1381 Result_t result = WriteToTLVSet(MemWRT);
1383 if ( ASDCP_SUCCESS(result) )
1385 ui32_t packet_length = MemWRT.Length();
1386 result = WriteKLToBuffer(Buffer, packet_length);
1388 if ( ASDCP_SUCCESS(result) )
1389 Buffer.Size(Buffer.Size() + packet_length);
1397 ASDCP::MXF::InterchangeObject::Dump(FILE* stream)
1399 char identbuf[IdentBufferLen];
1401 fputc('\n', stream);
1402 KLVPacket::Dump(stream, *m_Dict, false);
1403 fprintf(stream, " InstanceUID = %s\n", InstanceUID.EncodeHex(identbuf, IdentBufferLen));
1404 fprintf(stream, " GenerationUID = %s\n", GenerationUID.EncodeHex(identbuf, IdentBufferLen));
1409 ASDCP::MXF::InterchangeObject::IsA(const byte_t* label)
1411 if ( m_KLLength == 0 )
1414 return ( memcmp(label, m_KeyStart, SMPTE_UL_LENGTH) == 0 );
1418 //------------------------------------------------------------------------------------------
1421 typedef std::map<ASDCP::UL, ASDCP::MXF::MXFObjectFactory_t>FactoryMap_t;
1422 typedef FactoryMap_t::iterator FLi_t;
1425 class FactoryList : public FactoryMap_t
1434 Kumu::AutoMutex BlockLock(m_Lock);
1438 FLi_t Find(const byte_t* label) {
1439 Kumu::AutoMutex BlockLock(m_Lock);
1444 Kumu::AutoMutex BlockLock(m_Lock);
1448 void Insert(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory) {
1449 Kumu::AutoMutex BlockLock(m_Lock);
1450 insert(FactoryList::value_type(label, factory));
1455 static FactoryList s_FactoryList;
1456 static Kumu::Mutex s_InitLock;
1457 static bool s_TypesInit = false;
1462 ASDCP::MXF::SetObjectFactory(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory)
1464 s_FactoryList.Insert(label, factory);
1468 ASDCP::MXF::InterchangeObject*
1469 ASDCP::MXF::CreateObject(const Dictionary*& Dict, const UL& label)
1471 if ( ! s_TypesInit )
1473 Kumu::AutoMutex BlockLock(s_InitLock);
1475 if ( ! s_TypesInit )
1477 MXF::Metadata_InitTypes(Dict);
1482 FLi_t i = s_FactoryList.find(label.Value());
1484 if ( i == s_FactoryList.end() )
1485 return new InterchangeObject(Dict);
1487 return i->second(Dict);