2 Copyright (c) 2005-2008, 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 //------------------------------------------------------------------------------------------
46 const ui32_t kl_length = ASDCP::SMPTE_UL_LENGTH + ASDCP::MXF_BER_LENGTH;
50 ASDCP::MXF::SeekToRIP(const Kumu::FileReader& Reader)
54 // go to the end - 4 bytes
55 Result_t result = Reader.Seek(0, Kumu::SP_END);
57 if ( ASDCP_SUCCESS(result) )
58 result = Reader.Tell(&end_pos);
60 if ( ASDCP_SUCCESS(result)
61 && end_pos < (SMPTE_UL_LENGTH+MXF_BER_LENGTH) )
62 result = RESULT_FAIL; // File is smaller than an empty packet!
64 if ( ASDCP_SUCCESS(result) )
65 result = Reader.Seek(end_pos - 4);
67 // get the ui32_t RIP length
69 byte_t intbuf[MXF_BER_LENGTH];
72 if ( ASDCP_SUCCESS(result) )
74 result = Reader.Read(intbuf, MXF_BER_LENGTH, &read_count);
76 if ( ASDCP_SUCCESS(result) && read_count != 4 )
80 if ( ASDCP_SUCCESS(result) )
82 rip_size = KM_i32_BE(Kumu::cp2i<ui32_t>(intbuf));
84 if ( rip_size > end_pos ) // RIP can't be bigger than the file
88 // reposition to start of RIP
89 if ( ASDCP_SUCCESS(result) )
90 result = Reader.Seek(end_pos - rip_size);
97 ASDCP::MXF::RIP::GetPairBySID(ui32_t SID, Pair& outPair) const
99 Array<Pair>::const_iterator pi = PairArray.begin();
100 for ( ; pi != PairArray.end(); pi++ )
102 if ( (*pi).BodySID == SID )
114 ASDCP::MXF::RIP::InitFromFile(const Kumu::FileReader& Reader)
116 Result_t result = KLVFilePacket::InitFromFile(Reader, Dict::ul(MDD_RandomIndexMetadata));
118 if ( ASDCP_SUCCESS(result) )
120 Kumu::MemIOReader MemRDR(m_ValueStart, m_ValueLength - 4);
121 result = PairArray.Unarchive(&MemRDR) ? RESULT_OK : RESULT_KLV_CODING;
124 if ( ASDCP_FAILURE(result) )
125 DefaultLogSink().Error("Failed to initialize RIP\n");
132 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, 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, false);
168 PairArray.Dump(stream, false);
171 //------------------------------------------------------------------------------------------
175 class ASDCP::MXF::Partition::h__PacketList
178 std::list<InterchangeObject*> m_List;
179 std::map<UUID, InterchangeObject*> m_Map;
182 while ( ! m_List.empty() )
184 delete m_List.back();
190 void AddPacket(InterchangeObject* ThePacket)
193 m_Map.insert(std::map<UUID, InterchangeObject*>::value_type(ThePacket->InstanceUID, ThePacket));
194 m_List.push_back(ThePacket);
198 Result_t GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
200 ASDCP_TEST_NULL(Object);
202 std::map<UUID, InterchangeObject*>::iterator mi = m_Map.find(ObjectID);
204 if ( mi == m_Map.end() )
210 *Object = (*mi).second;
215 Result_t GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
217 ASDCP_TEST_NULL(ObjectID);
218 ASDCP_TEST_NULL(Object);
219 std::list<InterchangeObject*>::iterator li;
222 for ( li = m_List.begin(); li != m_List.end(); li++ )
224 if ( (*li)->HasUL(ObjectID) )
235 Result_t GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
237 ASDCP_TEST_NULL(ObjectID);
238 std::list<InterchangeObject*>::iterator li;
240 for ( li = m_List.begin(); li != m_List.end(); li++ )
242 if ( (*li)->HasUL(ObjectID) )
243 ObjectList.push_back(*li);
246 return ObjectList.empty() ? RESULT_FAIL : RESULT_OK;
250 //------------------------------------------------------------------------------------------
254 ASDCP::MXF::Partition::Partition() :
255 MajorVersion(1), MinorVersion(2),
256 KAGSize(1), ThisPartition(0), PreviousPartition(0),
257 FooterPartition(0), HeaderByteCount(0), IndexByteCount(0), IndexSID(0),
258 BodyOffset(0), BodySID(0)
260 m_PacketList = new h__PacketList;
263 ASDCP::MXF::Partition::~Partition()
269 ASDCP::MXF::Partition::AddChildObject(InterchangeObject* Object)
273 if ( ! Object->InstanceUID.HasValue() )
274 GenRandomValue(Object->InstanceUID);
276 m_PacketList->AddPacket(Object);
281 ASDCP::MXF::Partition::InitFromFile(const Kumu::FileReader& Reader)
283 Result_t result = KLVFilePacket::InitFromFile(Reader);
285 // could be one of several values
287 if ( ASDCP_SUCCESS(result) )
289 Kumu::MemIOReader MemRDR(m_ValueStart, m_ValueLength);
290 result = RESULT_KLV_CODING;
292 if ( MemRDR.ReadUi16BE(&MajorVersion) )
293 if ( MemRDR.ReadUi16BE(&MinorVersion) )
294 if ( MemRDR.ReadUi32BE(&KAGSize) )
295 if ( MemRDR.ReadUi64BE(&ThisPartition) )
296 if ( MemRDR.ReadUi64BE(&PreviousPartition) )
297 if ( MemRDR.ReadUi64BE(&FooterPartition) )
298 if ( MemRDR.ReadUi64BE(&HeaderByteCount) )
299 if ( MemRDR.ReadUi64BE(&IndexByteCount) )
300 if ( MemRDR.ReadUi32BE(&IndexSID) )
301 if ( MemRDR.ReadUi64BE(&BodyOffset) )
302 if ( MemRDR.ReadUi32BE(&BodySID) )
303 if ( OperationalPattern.Unarchive(&MemRDR) )
304 if ( EssenceContainers.Unarchive(&MemRDR) )
308 if ( ASDCP_FAILURE(result) )
309 DefaultLogSink().Error("Failed to initialize Partition\n");
316 ASDCP::MXF::Partition::WriteToFile(Kumu::FileWriter& Writer, UL& PartitionLabel)
318 ASDCP::FrameBuffer Buffer;
319 Result_t result = Buffer.Capacity(1024);
321 if ( ASDCP_SUCCESS(result) )
323 Kumu::MemIOWriter MemWRT(Buffer.Data(), Buffer.Capacity());
324 result = RESULT_KLV_CODING;
325 if ( MemWRT.WriteUi16BE(MajorVersion) )
326 if ( MemWRT.WriteUi16BE(MinorVersion) )
327 if ( MemWRT.WriteUi32BE(KAGSize) )
328 if ( MemWRT.WriteUi64BE(ThisPartition) )
329 if ( MemWRT.WriteUi64BE(PreviousPartition) )
330 if ( MemWRT.WriteUi64BE(FooterPartition) )
331 if ( MemWRT.WriteUi64BE(HeaderByteCount) )
332 if ( MemWRT.WriteUi64BE(IndexByteCount) )
333 if ( MemWRT.WriteUi32BE(IndexSID) )
334 if ( MemWRT.WriteUi64BE(BodyOffset) )
335 if ( MemWRT.WriteUi32BE(BodySID) )
336 if ( OperationalPattern.Archive(&MemWRT) )
337 if ( EssenceContainers.Archive(&MemWRT) )
339 Buffer.Size(MemWRT.Length());
344 if ( ASDCP_SUCCESS(result) )
347 result = WriteKLToFile(Writer, PartitionLabel.Value(), Buffer.Size());
349 if ( ASDCP_SUCCESS(result) )
350 result = Writer.Write(Buffer.RoData(), Buffer.Size(), &write_count);
358 ASDCP::MXF::Partition::ArchiveSize()
361 + sizeof(ui16_t) + sizeof(ui16_t)
363 + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t)
368 + sizeof(ui32_t) + sizeof(ui32_t) + ( UUIDlen * EssenceContainers.size() ) );
373 ASDCP::MXF::Partition::Dump(FILE* stream)
375 char identbuf[IdentBufferLen];
380 KLVFilePacket::Dump(stream, false);
381 fprintf(stream, " MajorVersion = %hu\n", MajorVersion);
382 fprintf(stream, " MinorVersion = %hu\n", MinorVersion);
383 fprintf(stream, " KAGSize = %u\n", KAGSize);
384 fprintf(stream, " ThisPartition = %s\n", ui64sz(ThisPartition, identbuf));
385 fprintf(stream, " PreviousPartition = %s\n", ui64sz(PreviousPartition, identbuf));
386 fprintf(stream, " FooterPartition = %s\n", ui64sz(FooterPartition, identbuf));
387 fprintf(stream, " HeaderByteCount = %s\n", ui64sz(HeaderByteCount, identbuf));
388 fprintf(stream, " IndexByteCount = %s\n", ui64sz(IndexByteCount, identbuf));
389 fprintf(stream, " IndexSID = %u\n", IndexSID);
390 fprintf(stream, " BodyOffset = %s\n", ui64sz(BodyOffset, identbuf));
391 fprintf(stream, " BodySID = %u\n", BodySID);
392 fprintf(stream, " OperationalPattern = %s\n", OperationalPattern.EncodeString(identbuf, IdentBufferLen));
393 fputs("Essence Containers:\n", stream); EssenceContainers.Dump(stream, false);
397 //------------------------------------------------------------------------------------------
400 class ASDCP::MXF::Primer::h__PrimerLookup : public std::map<UL, TagValue>
403 void InitWithBatch(ASDCP::MXF::Batch<ASDCP::MXF::Primer::LocalTagEntry>& Batch)
405 ASDCP::MXF::Batch<ASDCP::MXF::Primer::LocalTagEntry>::iterator i = Batch.begin();
407 for ( ; i != Batch.end(); i++ )
408 insert(std::map<UL, TagValue>::value_type((*i).UL, (*i).Tag));
414 ASDCP::MXF::Primer::Primer() : m_LocalTag(0xff) {}
417 ASDCP::MXF::Primer::~Primer() {}
421 ASDCP::MXF::Primer::ClearTagList()
423 LocalTagEntryBatch.clear();
424 m_Lookup = new h__PrimerLookup;
429 ASDCP::MXF::Primer::InitFromBuffer(const byte_t* p, ui32_t l)
431 Result_t result = KLVPacket::InitFromBuffer(p, l, Dict::ul(MDD_Primer));
433 if ( ASDCP_SUCCESS(result) )
435 Kumu::MemIOReader MemRDR(m_ValueStart, m_ValueLength);
436 result = LocalTagEntryBatch.Unarchive(&MemRDR) ? RESULT_OK : RESULT_KLV_CODING;
439 if ( ASDCP_SUCCESS(result) )
441 m_Lookup = new h__PrimerLookup;
442 m_Lookup->InitWithBatch(LocalTagEntryBatch);
445 if ( ASDCP_FAILURE(result) )
446 DefaultLogSink().Error("Failed to initialize Primer\n");
453 ASDCP::MXF::Primer::WriteToFile(Kumu::FileWriter& Writer)
455 ASDCP::FrameBuffer Buffer;
456 Result_t result = Buffer.Capacity(128*1024);
458 if ( ASDCP_SUCCESS(result) )
459 result = WriteToBuffer(Buffer);
461 if ( ASDCP_SUCCESS(result) )
462 result = Writer.Write(Buffer.RoData(), Buffer.Size());
469 ASDCP::MXF::Primer::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
471 ASDCP::FrameBuffer LocalTagBuffer;
472 Kumu::MemIOWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length);
473 Result_t result = LocalTagEntryBatch.Archive(&MemWRT) ? RESULT_OK : RESULT_KLV_CODING;
475 if ( ASDCP_SUCCESS(result) )
477 ui32_t packet_length = MemWRT.Length();
478 result = WriteKLToBuffer(Buffer, Dict::ul(MDD_Primer), packet_length);
480 if ( ASDCP_SUCCESS(result) )
481 Buffer.Size(Buffer.Size() + packet_length);
489 ASDCP::MXF::Primer::InsertTag(const MDDEntry& Entry, ASDCP::TagValue& Tag)
493 std::map<UL, TagValue>::iterator i = m_Lookup->find(TestUL);
495 if ( i == m_Lookup->end() )
497 if ( Entry.tag.a == 0 && Entry.tag.b == 0 )
500 Tag.b = m_LocalTag--;
508 LocalTagEntry TmpEntry;
509 TmpEntry.UL = TestUL;
512 LocalTagEntryBatch.push_back(TmpEntry);
513 m_Lookup->insert(std::map<UL, TagValue>::value_type(TmpEntry.UL, TmpEntry.Tag));
525 ASDCP::MXF::Primer::TagForKey(const ASDCP::UL& Key, ASDCP::TagValue& Tag)
528 if ( m_Lookup.empty() )
530 DefaultLogSink().Error("Primer lookup is empty\n");
534 std::map<UL, TagValue>::iterator i = m_Lookup->find(Key);
536 if ( i == m_Lookup->end() )
545 ASDCP::MXF::Primer::Dump(FILE* stream)
547 char identbuf[IdentBufferLen];
552 KLVPacket::Dump(stream, false);
553 fprintf(stream, "Primer: %u %s\n",
554 (ui32_t)LocalTagEntryBatch.size(),
555 ( LocalTagEntryBatch.size() == 1 ? "entry" : "entries" ));
557 Batch<LocalTagEntry>::iterator i = LocalTagEntryBatch.begin();
558 for ( ; i != LocalTagEntryBatch.end(); i++ )
560 const MDDEntry* Entry = Dict::FindUL((*i).UL.Value());
561 fprintf(stream, " %s %s\n", (*i).EncodeString(identbuf, IdentBufferLen), (Entry ? Entry->name : "Unknown"));
566 //------------------------------------------------------------------------------------------
571 ASDCP::MXF::Preface::InitFromTLVSet(TLVReader& TLVSet)
573 Result_t result = InterchangeObject::InitFromTLVSet(TLVSet);
574 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, LastModifiedDate));
575 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi16(OBJ_READ_ARGS(Preface, Version));
576 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi32(OBJ_READ_ARGS(Preface, ObjectModelVersion));
577 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, PrimaryPackage));
578 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, Identifications));
579 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, ContentStorage));
580 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, OperationalPattern));
581 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, EssenceContainers));
582 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, DMSchemes));
588 ASDCP::MXF::Preface::WriteToTLVSet(TLVWriter& TLVSet)
590 Result_t result = InterchangeObject::WriteToTLVSet(TLVSet);
591 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, LastModifiedDate));
592 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi16(OBJ_WRITE_ARGS(Preface, Version));
593 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi32(OBJ_WRITE_ARGS(Preface, ObjectModelVersion));
594 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, PrimaryPackage));
595 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, Identifications));
596 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, ContentStorage));
597 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, OperationalPattern));
598 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, EssenceContainers));
599 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, DMSchemes));
605 ASDCP::MXF::Preface::InitFromBuffer(const byte_t* p, ui32_t l)
607 m_Typeinfo = &Dict::Type(MDD_Preface);
608 return InterchangeObject::InitFromBuffer(p, l);
613 ASDCP::MXF::Preface::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
615 m_Typeinfo = &Dict::Type(MDD_Preface);
616 return InterchangeObject::WriteToBuffer(Buffer);
621 ASDCP::MXF::Preface::Dump(FILE* stream)
623 char identbuf[IdentBufferLen];
628 InterchangeObject::Dump(stream);
629 fprintf(stream, " %22s = %s\n", "LastModifiedDate", LastModifiedDate.EncodeString(identbuf, IdentBufferLen));
630 fprintf(stream, " %22s = %hu\n", "Version", Version);
631 fprintf(stream, " %22s = %u\n", "ObjectModelVersion", ObjectModelVersion);
632 fprintf(stream, " %22s = %s\n", "PrimaryPackage", PrimaryPackage.EncodeHex(identbuf, IdentBufferLen));
633 fprintf(stream, " %22s:\n", "Identifications"); Identifications.Dump(stream);
634 fprintf(stream, " %22s = %s\n", "ContentStorage", ContentStorage.EncodeHex(identbuf, IdentBufferLen));
635 fprintf(stream, " %22s = %s\n", "OperationalPattern", OperationalPattern.EncodeString(identbuf, IdentBufferLen));
636 fprintf(stream, " %22s:\n", "EssenceContainers"); EssenceContainers.Dump(stream);
637 fprintf(stream, " %22s:\n", "DMSchemes"); DMSchemes.Dump(stream);
640 //------------------------------------------------------------------------------------------
643 ASDCP::MXF::OPAtomHeader::OPAtomHeader() : m_Preface(0), m_HasRIP(false) {}
644 ASDCP::MXF::OPAtomHeader::~OPAtomHeader() {}
648 ASDCP::MXF::OPAtomHeader::InitFromFile(const Kumu::FileReader& Reader)
651 Result_t result = SeekToRIP(Reader);
653 if ( ASDCP_SUCCESS(result) )
655 result = m_RIP.InitFromFile(Reader);
656 ui32_t test_s = m_RIP.PairArray.size();
658 if ( ASDCP_FAILURE(result) )
660 DefaultLogSink().Error("File contains no RIP\n");
663 else if ( test_s == 0 )
665 DefaultLogSink().Error("RIP contains no Pairs.\n");
666 result = RESULT_FORMAT;
670 if ( test_s < 2 || test_s > 3 )
672 // OP-Atom states that there will be either two or three partitions:
673 // one closed header and one closed footer with an optional body
674 DefaultLogSink().Warn("RIP count is not 2 or 3: %u\n", test_s);
679 if ( m_RIP.PairArray.front().ByteOffset != 0 )
681 DefaultLogSink().Error("First Partition in RIP is not at offset 0.\n");
682 result = RESULT_FORMAT;
687 if ( ASDCP_SUCCESS(result) )
688 result = Reader.Seek(0);
690 if ( ASDCP_SUCCESS(result) )
691 result = Partition::InitFromFile(Reader); // test UL and OP
693 if ( ASDCP_FAILURE(result) )
696 // is it really OP-Atom?
697 UL OPAtomUL(Dict::ul(MDD_OPAtom));
698 UL InteropOPAtomUL(Dict::ul(MDD_MXFInterop_OPAtom));
700 if ( ! ( OperationalPattern == OPAtomUL || OperationalPattern == InteropOPAtomUL ) )
702 char strbuf[IdentBufferLen];
703 const MDDEntry* Entry = Dict::FindUL(OperationalPattern.Value());
705 DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n", OperationalPattern.EncodeString(strbuf, IdentBufferLen));
707 DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n", Entry->name);
710 // slurp up the remainder of the header
711 if ( HeaderByteCount < 1024 )
712 DefaultLogSink().Warn("Improbably small HeaderByteCount value: %u\n", HeaderByteCount);
714 assert (HeaderByteCount <= 0xFFFFFFFFL);
715 result = m_Buffer.Capacity((ui32_t) HeaderByteCount);
717 if ( ASDCP_SUCCESS(result) )
720 result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
722 if ( ASDCP_FAILURE(result) )
725 if ( read_count != m_Buffer.Capacity() )
727 DefaultLogSink().Error("Short read of OP-Atom header metadata; wanted %u, got %u\n",
728 m_Buffer.Capacity(), read_count);
729 return RESULT_KLV_CODING;
733 const byte_t* p = m_Buffer.RoData();
734 const byte_t* end_p = p + m_Buffer.Capacity();
736 while ( ASDCP_SUCCESS(result) && p < end_p )
738 // parse the packets and index them by uid, discard KLVFill items
739 InterchangeObject* object = CreateObject(p);
742 object->m_Lookup = &m_Primer;
743 result = object->InitFromBuffer(p, end_p - p);
744 const byte_t* redo_p = p;
745 p += object->PacketLength();
746 // hexdump(p, object->PacketLength());
748 if ( ASDCP_SUCCESS(result) )
750 if ( object->IsA(Dict::ul(MDD_KLVFill)) )
754 else if ( object->IsA(Dict::ul(MDD_Primer)) )
757 result = m_Primer.InitFromBuffer(redo_p, end_p - redo_p);
761 m_PacketList->AddPacket(object);
763 if ( object->IsA(Dict::ul(MDD_Preface)) )
765 assert(m_Preface == 0);
766 m_Preface = (Preface*)object;
772 DefaultLogSink().Error("Error initializing packet\n");
781 ASDCP::MXF::OPAtomHeader::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
783 return m_PacketList->GetMDObjectByID(ObjectID, Object);
788 ASDCP::MXF::OPAtomHeader::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
790 InterchangeObject* TmpObject;
795 return m_PacketList->GetMDObjectByType(ObjectID, Object);
800 ASDCP::MXF::OPAtomHeader::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
802 return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
806 ASDCP::MXF::Identification*
807 ASDCP::MXF::OPAtomHeader::GetIdentification()
809 InterchangeObject* Object;
811 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object)) )
812 return (Identification*)Object;
818 ASDCP::MXF::SourcePackage*
819 ASDCP::MXF::OPAtomHeader::GetSourcePackage()
821 InterchangeObject* Object;
823 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object)) )
824 return (SourcePackage*)Object;
832 ASDCP::MXF::OPAtomHeader::WriteToFile(Kumu::FileWriter& Writer, ui32_t HeaderSize)
834 if ( m_Preface == 0 )
837 if ( HeaderSize < 4096 )
839 DefaultLogSink().Error("HeaderSize %u is too small. Must be >= 4096\n", HeaderSize);
843 ASDCP::FrameBuffer HeaderBuffer;
844 HeaderByteCount = HeaderSize - ArchiveSize();
845 assert (HeaderByteCount <= 0xFFFFFFFFL);
846 Result_t result = HeaderBuffer.Capacity((ui32_t) HeaderByteCount);
847 m_Preface->m_Lookup = &m_Primer;
849 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
850 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
852 InterchangeObject* object = *pl_i;
853 object->m_Lookup = &m_Primer;
855 ASDCP::FrameBuffer WriteWrapper;
856 WriteWrapper.SetData(HeaderBuffer.Data() + HeaderBuffer.Size(),
857 HeaderBuffer.Capacity() - HeaderBuffer.Size());
858 result = object->WriteToBuffer(WriteWrapper);
859 HeaderBuffer.Size(HeaderBuffer.Size() + WriteWrapper.Size());
862 if ( ASDCP_SUCCESS(result) )
864 UL TmpUL(Dict::ul(MDD_ClosedCompleteHeader));
865 result = Partition::WriteToFile(Writer, TmpUL);
868 if ( ASDCP_SUCCESS(result) )
869 result = m_Primer.WriteToFile(Writer);
871 if ( ASDCP_SUCCESS(result) )
874 Writer.Write(HeaderBuffer.RoData(), HeaderBuffer.Size(), &write_count);
875 assert(write_count == HeaderBuffer.Size());
879 if ( ASDCP_SUCCESS(result) )
881 Kumu::fpos_t pos = Writer.Tell();
883 if ( pos > (Kumu::fpos_t)HeaderByteCount )
885 char intbuf[IntBufferLen];
886 DefaultLogSink().Error("Header size %s exceeds specified value %u\n",
892 ASDCP::FrameBuffer NilBuf;
893 ui32_t klv_fill_length = HeaderSize - (ui32_t)pos;
895 if ( klv_fill_length < kl_length )
897 DefaultLogSink().Error("Remaining region too small for KLV Fill header\n");
901 klv_fill_length -= kl_length;
902 result = WriteKLToFile(Writer, Dict::ul(MDD_KLVFill), klv_fill_length);
904 if ( ASDCP_SUCCESS(result) )
905 result = NilBuf.Capacity(klv_fill_length);
907 if ( ASDCP_SUCCESS(result) )
909 memset(NilBuf.Data(), 0, klv_fill_length);
911 Writer.Write(NilBuf.RoData(), klv_fill_length, &write_count);
912 assert(write_count == klv_fill_length);
921 ASDCP::MXF::OPAtomHeader::Dump(FILE* stream)
926 Partition::Dump(stream);
927 m_Primer.Dump(stream);
929 if ( m_Preface == 0 )
930 fputs("No Preface loaded\n", stream);
932 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
933 for ( ; i != m_PacketList->m_List.end(); i++ )
937 //------------------------------------------------------------------------------------------
940 ASDCP::MXF::OPAtomIndexFooter::OPAtomIndexFooter() :
941 m_CurrentSegment(0), m_BytesPerEditUnit(0), m_BodySID(0),
942 m_ECOffset(0), m_Lookup(0)
948 ASDCP::MXF::OPAtomIndexFooter::~OPAtomIndexFooter() {}
952 ASDCP::MXF::OPAtomIndexFooter::InitFromFile(const Kumu::FileReader& Reader)
954 Result_t result = Partition::InitFromFile(Reader); // test UL and OP
956 // slurp up the remainder of the footer
959 if ( ASDCP_SUCCESS(result) )
961 assert (IndexByteCount <= 0xFFFFFFFFL);
962 result = m_Buffer.Capacity((ui32_t) IndexByteCount);
965 if ( ASDCP_SUCCESS(result) )
966 result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
968 if ( ASDCP_SUCCESS(result) && read_count != m_Buffer.Capacity() )
970 DefaultLogSink().Error("Short read of footer partition: got %u, expecting %u\n",
971 read_count, m_Buffer.Capacity());
975 const byte_t* p = m_Buffer.RoData();
976 const byte_t* end_p = p + m_Buffer.Capacity();
978 while ( ASDCP_SUCCESS(result) && p < end_p )
980 // parse the packets and index them by uid, discard KLVFill items
981 InterchangeObject* object = CreateObject(p);
984 object->m_Lookup = m_Lookup;
985 result = object->InitFromBuffer(p, end_p - p);
986 p += object->PacketLength();
988 if ( ASDCP_SUCCESS(result) )
990 m_PacketList->AddPacket(object);
994 DefaultLogSink().Error("Error initializing packet\n");
999 if ( ASDCP_FAILURE(result) )
1000 DefaultLogSink().Error("Failed to initialize OPAtomIndexFooter\n");
1007 ASDCP::MXF::OPAtomIndexFooter::WriteToFile(Kumu::FileWriter& Writer, ui64_t duration)
1009 ASDCP::FrameBuffer FooterBuffer;
1010 ui32_t footer_size = m_PacketList->m_List.size() * MaxIndexSegmentSize; // segment-count * max-segment-size
1011 Result_t result = FooterBuffer.Capacity(footer_size);
1012 ui32_t iseg_count = 0;
1014 if ( m_CurrentSegment != 0 )
1016 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1017 m_CurrentSegment = 0;
1020 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
1021 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
1023 if ( (*pl_i)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1026 IndexTableSegment* Segment = (IndexTableSegment*)(*pl_i);
1028 if ( m_BytesPerEditUnit != 0 )
1030 if ( iseg_count != 1 )
1031 return RESULT_STATE;
1033 Segment->IndexDuration = duration;
1037 InterchangeObject* object = *pl_i;
1038 object->m_Lookup = m_Lookup;
1040 ASDCP::FrameBuffer WriteWrapper;
1041 WriteWrapper.SetData(FooterBuffer.Data() + FooterBuffer.Size(),
1042 FooterBuffer.Capacity() - FooterBuffer.Size());
1043 result = object->WriteToBuffer(WriteWrapper);
1044 FooterBuffer.Size(FooterBuffer.Size() + WriteWrapper.Size());
1047 if ( ASDCP_SUCCESS(result) )
1049 IndexByteCount = FooterBuffer.Size();
1050 UL FooterUL(Dict::ul(MDD_CompleteFooter));
1051 result = Partition::WriteToFile(Writer, FooterUL);
1054 if ( ASDCP_SUCCESS(result) )
1056 ui32_t write_count = 0;
1057 result = Writer.Write(FooterBuffer.RoData(), FooterBuffer.Size(), &write_count);
1058 assert(write_count == FooterBuffer.Size());
1066 ASDCP::MXF::OPAtomIndexFooter::Dump(FILE* stream)
1071 Partition::Dump(stream);
1073 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
1074 for ( ; i != m_PacketList->m_List.end(); i++ )
1080 ASDCP::MXF::OPAtomIndexFooter::Lookup(ui32_t frame_num, IndexTableSegment::IndexEntry& Entry) const
1082 std::list<InterchangeObject*>::iterator li;
1083 for ( li = m_PacketList->m_List.begin(); li != m_PacketList->m_List.end(); li++ )
1085 if ( (*li)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1087 IndexTableSegment* Segment = (IndexTableSegment*)(*li);
1088 ui64_t start_pos = Segment->IndexStartPosition;
1090 if ( Segment->EditUnitByteCount > 0 )
1092 if ( m_PacketList->m_List.size() > 1 )
1093 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
1095 if ( ! Segment->IndexEntryArray.empty() )
1096 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
1098 Entry.StreamOffset = (ui64_t)frame_num * Segment->EditUnitByteCount;
1101 else if ( (ui64_t)frame_num >= start_pos
1102 && (ui64_t)frame_num < (start_pos + Segment->IndexDuration) )
1104 ui64_t tmp = frame_num - start_pos;
1105 assert(tmp <= 0xFFFFFFFFL);
1106 Entry = Segment->IndexEntryArray[(ui32_t) tmp];
1117 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const Rational& Rate)
1121 m_BytesPerEditUnit = size;
1124 IndexTableSegment* Index = new IndexTableSegment;
1125 AddChildObject(Index);
1126 Index->EditUnitByteCount = m_BytesPerEditUnit;
1127 Index->IndexEditRate = Rate;
1132 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsVBR(IPrimerLookup* lookup, const Rational& Rate, Kumu::fpos_t offset)
1136 m_BytesPerEditUnit = 0;
1138 m_ECOffset = offset;
1143 ASDCP::MXF::OPAtomIndexFooter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
1145 if ( m_BytesPerEditUnit != 0 ) // are we CBR? that's bad
1147 DefaultLogSink().Error("Call to PushIndexEntry() failed: index is CBR\n");
1151 // do we have an available segment?
1152 if ( m_CurrentSegment == 0 )
1153 { // no, set up a new segment
1154 m_CurrentSegment = new IndexTableSegment;
1155 assert(m_CurrentSegment);
1156 AddChildObject(m_CurrentSegment);
1157 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1158 m_CurrentSegment->IndexEditRate = m_EditRate;
1159 m_CurrentSegment->IndexStartPosition = 0;
1161 else if ( m_CurrentSegment->IndexEntryArray.size() >= CBRIndexEntriesPerSegment )
1162 { // no, this one is full, start another
1163 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1164 ui64_t StartPosition = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
1166 m_CurrentSegment = new IndexTableSegment;
1167 assert(m_CurrentSegment);
1168 AddChildObject(m_CurrentSegment);
1169 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1170 m_CurrentSegment->IndexEditRate = m_EditRate;
1171 m_CurrentSegment->IndexStartPosition = StartPosition;
1174 m_CurrentSegment->IndexEntryArray.push_back(Entry);
1177 //------------------------------------------------------------------------------------------
1182 ASDCP::MXF::InterchangeObject::InitFromTLVSet(TLVReader& TLVSet)
1184 Result_t result = TLVSet.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID));
1185 if ( ASDCP_SUCCESS(result) )
1186 result = TLVSet.ReadObject(OBJ_READ_ARGS(GenerationInterchangeObject, GenerationUID));
1192 ASDCP::MXF::InterchangeObject::WriteToTLVSet(TLVWriter& TLVSet)
1194 Result_t result = TLVSet.WriteObject(OBJ_WRITE_ARGS(InterchangeObject, InstanceUID));
1195 if ( ASDCP_SUCCESS(result) )
1196 result = TLVSet.WriteObject(OBJ_WRITE_ARGS(GenerationInterchangeObject, GenerationUID));
1202 ASDCP::MXF::InterchangeObject::InitFromBuffer(const byte_t* p, ui32_t l)
1205 Result_t result = RESULT_FALSE;
1207 if ( m_Typeinfo == 0 )
1209 result = KLVPacket::InitFromBuffer(p, l);
1213 result = KLVPacket::InitFromBuffer(p, l, m_Typeinfo->ul);
1215 if ( ASDCP_SUCCESS(result) )
1217 TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup);
1218 result = InitFromTLVSet(MemRDR);
1227 ASDCP::MXF::InterchangeObject::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
1229 if ( m_Typeinfo == 0 )
1230 return RESULT_STATE;
1232 TLVWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length, m_Lookup);
1233 Result_t result = WriteToTLVSet(MemWRT);
1235 if ( ASDCP_SUCCESS(result) )
1237 ui32_t packet_length = MemWRT.Length();
1238 result = WriteKLToBuffer(Buffer, m_Typeinfo->ul, packet_length);
1240 if ( ASDCP_SUCCESS(result) )
1241 Buffer.Size(Buffer.Size() + packet_length);
1249 ASDCP::MXF::InterchangeObject::Dump(FILE* stream)
1251 char identbuf[IdentBufferLen];
1253 fputc('\n', stream);
1254 KLVPacket::Dump(stream, false);
1255 fprintf(stream, " InstanceUID = %s\n", InstanceUID.EncodeHex(identbuf, IdentBufferLen));
1256 fprintf(stream, " GenerationUID = %s\n", GenerationUID.EncodeHex(identbuf, IdentBufferLen));
1261 ASDCP::MXF::InterchangeObject::IsA(const byte_t* label)
1263 if ( m_KLLength == 0 )
1266 return ( memcmp(label, m_KeyStart, SMPTE_UL_LENGTH) == 0 );
1270 //------------------------------------------------------------------------------------------
1273 typedef std::map<ASDCP::UL, ASDCP::MXF::MXFObjectFactory_t>FactoryMap_t;
1274 typedef FactoryMap_t::iterator FLi_t;
1277 class FactoryList : public FactoryMap_t
1286 Kumu::AutoMutex BlockLock(m_Lock);
1290 FLi_t Find(const byte_t* label) {
1291 Kumu::AutoMutex BlockLock(m_Lock);
1296 Kumu::AutoMutex BlockLock(m_Lock);
1300 void Insert(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory) {
1301 Kumu::AutoMutex BlockLock(m_Lock);
1302 insert(FactoryList::value_type(label, factory));
1307 static FactoryList s_FactoryList;
1308 static Kumu::Mutex s_InitLock;
1309 static bool s_TypesInit = false;
1314 ASDCP::MXF::SetObjectFactory(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory)
1316 s_FactoryList.Insert(label, factory);
1321 ASDCP::MXF::InterchangeObject*
1322 ASDCP::MXF::CreateObject(const byte_t* label)
1327 if ( ! s_TypesInit )
1329 Kumu::AutoMutex BlockLock(s_InitLock);
1331 if ( ! s_TypesInit )
1333 MXF::Metadata_InitTypes();
1338 FLi_t i = s_FactoryList.find(label);
1340 if ( i == s_FactoryList.end() )
1341 return new InterchangeObject;