2 Copyright (c) 2005-2007, 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.
35 using Kumu::DefaultLogSink;
37 // index segments must be < 64K
38 // NOTE: this value may too high if advanced index entry elements are used.
39 const ui32_t CBRIndexEntriesPerSegment = 5000;
41 //------------------------------------------------------------------------------------------
44 const ui32_t kl_length = ASDCP::SMPTE_UL_LENGTH + ASDCP::MXF_BER_LENGTH;
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)
114 Result_t result = KLVFilePacket::InitFromFile(Reader, Dict::ul(MDD_RandomIndexMetadata));
116 if ( ASDCP_SUCCESS(result) )
118 Kumu::MemIOReader MemRDR(m_ValueStart, m_ValueLength - 4);
119 result = PairArray.Unarchive(&MemRDR) ? RESULT_OK : RESULT_KLV_CODING;
122 if ( ASDCP_FAILURE(result) )
123 DefaultLogSink().Error("Failed to initialize RIP\n");
130 ASDCP::MXF::RIP::WriteToFile(Kumu::FileWriter& Writer)
132 ASDCP::FrameBuffer Buffer;
133 ui32_t RIPSize = ( PairArray.size() * (sizeof(ui32_t) + sizeof(ui64_t)) ) + 4;
134 Result_t result = Buffer.Capacity(RIPSize);
136 if ( ASDCP_SUCCESS(result) )
137 result = WriteKLToFile(Writer, Dict::ul(MDD_RandomIndexMetadata), RIPSize);
139 if ( ASDCP_SUCCESS(result) )
141 result = RESULT_KLV_CODING;
143 Kumu::MemIOWriter MemWRT(Buffer.Data(), Buffer.Capacity());
144 if ( PairArray.Archive(&MemWRT) )
145 if ( MemWRT.WriteUi32BE(RIPSize + 20) )
147 Buffer.Size(MemWRT.Length());
152 if ( ASDCP_SUCCESS(result) )
153 result = Writer.Write(Buffer.RoData(), Buffer.Size());
160 ASDCP::MXF::RIP::Dump(FILE* stream)
165 KLVFilePacket::Dump(stream, false);
166 PairArray.Dump(stream, false);
168 fputs("==========================================================================\n", stream);
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);
395 fputs("==========================================================================\n", stream);
399 //------------------------------------------------------------------------------------------
402 class ASDCP::MXF::Primer::h__PrimerLookup : public std::map<UL, TagValue>
405 void InitWithBatch(ASDCP::MXF::Batch<ASDCP::MXF::Primer::LocalTagEntry>& Batch)
407 ASDCP::MXF::Batch<ASDCP::MXF::Primer::LocalTagEntry>::iterator i = Batch.begin();
409 for ( ; i != Batch.end(); i++ )
410 insert(std::map<UL, TagValue>::value_type((*i).UL, (*i).Tag));
416 ASDCP::MXF::Primer::Primer() : m_LocalTag(0xff) {}
419 ASDCP::MXF::Primer::~Primer() {}
423 ASDCP::MXF::Primer::ClearTagList()
425 LocalTagEntryBatch.clear();
426 m_Lookup = new h__PrimerLookup;
431 ASDCP::MXF::Primer::InitFromBuffer(const byte_t* p, ui32_t l)
433 Result_t result = KLVPacket::InitFromBuffer(p, l, Dict::ul(MDD_Primer));
435 if ( ASDCP_SUCCESS(result) )
437 Kumu::MemIOReader MemRDR(m_ValueStart, m_ValueLength);
438 result = LocalTagEntryBatch.Unarchive(&MemRDR) ? RESULT_OK : RESULT_KLV_CODING;
441 if ( ASDCP_SUCCESS(result) )
443 m_Lookup = new h__PrimerLookup;
444 m_Lookup->InitWithBatch(LocalTagEntryBatch);
447 if ( ASDCP_FAILURE(result) )
448 DefaultLogSink().Error("Failed to initialize Primer\n");
455 ASDCP::MXF::Primer::WriteToFile(Kumu::FileWriter& Writer)
457 ASDCP::FrameBuffer Buffer;
458 Result_t result = Buffer.Capacity(128*1024);
460 if ( ASDCP_SUCCESS(result) )
461 result = WriteToBuffer(Buffer);
463 if ( ASDCP_SUCCESS(result) )
464 result = Writer.Write(Buffer.RoData(), Buffer.Size());
471 ASDCP::MXF::Primer::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
473 ASDCP::FrameBuffer LocalTagBuffer;
474 Kumu::MemIOWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length);
475 Result_t result = LocalTagEntryBatch.Archive(&MemWRT) ? RESULT_OK : RESULT_KLV_CODING;
477 if ( ASDCP_SUCCESS(result) )
479 ui32_t packet_length = MemWRT.Length();
480 result = WriteKLToBuffer(Buffer, Dict::ul(MDD_Primer), packet_length);
482 if ( ASDCP_SUCCESS(result) )
483 Buffer.Size(Buffer.Size() + packet_length);
491 ASDCP::MXF::Primer::InsertTag(const MDDEntry& Entry, ASDCP::TagValue& Tag)
495 std::map<UL, TagValue>::iterator i = m_Lookup->find(TestUL);
497 if ( i == m_Lookup->end() )
499 if ( Entry.tag.a == 0 && Entry.tag.b == 0 )
502 Tag.b = m_LocalTag--;
510 LocalTagEntry TmpEntry;
511 TmpEntry.UL = TestUL;
514 LocalTagEntryBatch.push_back(TmpEntry);
515 m_Lookup->insert(std::map<UL, TagValue>::value_type(TmpEntry.UL, TmpEntry.Tag));
527 ASDCP::MXF::Primer::TagForKey(const ASDCP::UL& Key, ASDCP::TagValue& Tag)
530 if ( m_Lookup.empty() )
532 DefaultLogSink().Error("Primer lookup is empty\n");
536 std::map<UL, TagValue>::iterator i = m_Lookup->find(Key);
538 if ( i == m_Lookup->end() )
547 ASDCP::MXF::Primer::Dump(FILE* stream)
549 char identbuf[IdentBufferLen];
554 KLVPacket::Dump(stream, false);
555 fprintf(stream, "Primer: %u %s\n",
556 (ui32_t)LocalTagEntryBatch.size(),
557 ( LocalTagEntryBatch.size() == 1 ? "entry" : "entries" ));
559 Batch<LocalTagEntry>::iterator i = LocalTagEntryBatch.begin();
560 for ( ; i != LocalTagEntryBatch.end(); i++ )
562 const MDDEntry* Entry = Dict::FindUL((*i).UL.Value());
563 fprintf(stream, " %s %s\n", (*i).EncodeString(identbuf, IdentBufferLen), (Entry ? Entry->name : "Unknown"));
566 fputs("==========================================================================\n", stream);
570 //------------------------------------------------------------------------------------------
575 ASDCP::MXF::Preface::InitFromTLVSet(TLVReader& TLVSet)
577 Result_t result = InterchangeObject::InitFromTLVSet(TLVSet);
578 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, LastModifiedDate));
579 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi16(OBJ_READ_ARGS(Preface, Version));
580 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi32(OBJ_READ_ARGS(Preface, ObjectModelVersion));
581 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, PrimaryPackage));
582 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, Identifications));
583 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, ContentStorage));
584 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, OperationalPattern));
585 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, EssenceContainers));
586 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, DMSchemes));
592 ASDCP::MXF::Preface::WriteToTLVSet(TLVWriter& TLVSet)
594 Result_t result = InterchangeObject::WriteToTLVSet(TLVSet);
595 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, LastModifiedDate));
596 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi16(OBJ_WRITE_ARGS(Preface, Version));
597 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi32(OBJ_WRITE_ARGS(Preface, ObjectModelVersion));
598 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, PrimaryPackage));
599 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, Identifications));
600 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, ContentStorage));
601 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, OperationalPattern));
602 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, EssenceContainers));
603 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, DMSchemes));
609 ASDCP::MXF::Preface::InitFromBuffer(const byte_t* p, ui32_t l)
611 m_Typeinfo = &Dict::Type(MDD_Preface);
612 return InterchangeObject::InitFromBuffer(p, l);
617 ASDCP::MXF::Preface::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
619 m_Typeinfo = &Dict::Type(MDD_Preface);
620 return InterchangeObject::WriteToBuffer(Buffer);
625 ASDCP::MXF::Preface::Dump(FILE* stream)
627 char identbuf[IdentBufferLen];
632 InterchangeObject::Dump(stream);
633 fprintf(stream, " %22s = %s\n", "LastModifiedDate", LastModifiedDate.EncodeString(identbuf, IdentBufferLen));
634 fprintf(stream, " %22s = %hu\n", "Version", Version);
635 fprintf(stream, " %22s = %u\n", "ObjectModelVersion", ObjectModelVersion);
636 fprintf(stream, " %22s = %s\n", "PrimaryPackage", PrimaryPackage.EncodeHex(identbuf, IdentBufferLen));
637 fprintf(stream, " %22s:\n", "Identifications"); Identifications.Dump(stream);
638 fprintf(stream, " %22s = %s\n", "ContentStorage", ContentStorage.EncodeHex(identbuf, IdentBufferLen));
639 fprintf(stream, " %22s = %s\n", "OperationalPattern", OperationalPattern.EncodeString(identbuf, IdentBufferLen));
640 fprintf(stream, " %22s:\n", "EssenceContainers"); EssenceContainers.Dump(stream);
641 fprintf(stream, " %22s:\n", "DMSchemes"); DMSchemes.Dump(stream);
644 //------------------------------------------------------------------------------------------
647 ASDCP::MXF::OPAtomHeader::OPAtomHeader() : m_Preface(0), m_HasRIP(false) {}
648 ASDCP::MXF::OPAtomHeader::~OPAtomHeader() {}
652 ASDCP::MXF::OPAtomHeader::InitFromFile(const Kumu::FileReader& Reader)
655 Result_t result = SeekToRIP(Reader);
657 if ( ASDCP_SUCCESS(result) )
659 result = m_RIP.InitFromFile(Reader);
660 ui32_t test_s = m_RIP.PairArray.size();
662 if ( ASDCP_FAILURE(result) )
664 DefaultLogSink().Error("File contains no RIP\n");
667 else if ( test_s == 0 )
669 DefaultLogSink().Error("RIP contains no Pairs.\n");
670 result = RESULT_FORMAT;
674 if ( test_s < 2 || test_s > 3 )
676 // OP-Atom states that there will be either two or three partitions:
677 // one closed header and one closed footer with an optional body
678 DefaultLogSink().Warn("RIP count is not 2 or 3: %u\n", test_s);
683 if ( m_RIP.PairArray.front().ByteOffset != 0 )
685 DefaultLogSink().Error("First Partition in RIP is not at offset 0.\n");
686 result = RESULT_FORMAT;
691 if ( ASDCP_SUCCESS(result) )
692 result = Reader.Seek(0);
694 if ( ASDCP_SUCCESS(result) )
695 result = Partition::InitFromFile(Reader); // test UL and OP
697 if ( ASDCP_FAILURE(result) )
700 // is it really OP-Atom?
701 UL OPAtomUL(Dict::ul(MDD_OPAtom));
702 UL InteropOPAtomUL(Dict::ul(MDD_MXFInterop_OPAtom));
704 if ( ! ( OperationalPattern == OPAtomUL || OperationalPattern == InteropOPAtomUL ) )
706 char strbuf[IdentBufferLen];
707 const MDDEntry* Entry = Dict::FindUL(OperationalPattern.Value());
709 DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n", OperationalPattern.EncodeString(strbuf, IdentBufferLen));
711 DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n", Entry->name);
714 // slurp up the remainder of the header
715 if ( HeaderByteCount < 1024 )
716 DefaultLogSink().Warn("Improbably small HeaderByteCount value: %u\n", HeaderByteCount);
718 result = m_Buffer.Capacity(HeaderByteCount);
720 if ( ASDCP_SUCCESS(result) )
723 result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
725 if ( ASDCP_FAILURE(result) )
728 if ( read_count != m_Buffer.Capacity() )
730 DefaultLogSink().Error("Short read of OP-Atom header metadata; wanted %u, got %u\n",
731 m_Buffer.Capacity(), read_count);
732 return RESULT_KLV_CODING;
736 const byte_t* p = m_Buffer.RoData();
737 const byte_t* end_p = p + m_Buffer.Capacity();
739 while ( ASDCP_SUCCESS(result) && p < end_p )
741 // parse the packets and index them by uid, discard KLVFill items
742 InterchangeObject* object = CreateObject(p);
745 object->m_Lookup = &m_Primer;
746 result = object->InitFromBuffer(p, end_p - p);
747 const byte_t* redo_p = p;
748 p += object->PacketLength();
749 // hexdump(p, object->PacketLength());
751 if ( ASDCP_SUCCESS(result) )
753 if ( object->IsA(Dict::ul(MDD_KLVFill)) )
757 else if ( object->IsA(Dict::ul(MDD_Primer)) )
760 result = m_Primer.InitFromBuffer(redo_p, end_p - redo_p);
764 m_PacketList->AddPacket(object);
766 if ( object->IsA(Dict::ul(MDD_Preface)) )
768 assert(m_Preface == 0);
769 m_Preface = (Preface*)object;
775 DefaultLogSink().Error("Error initializing packet\n");
784 ASDCP::MXF::OPAtomHeader::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
786 return m_PacketList->GetMDObjectByID(ObjectID, Object);
791 ASDCP::MXF::OPAtomHeader::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
793 InterchangeObject* TmpObject;
798 return m_PacketList->GetMDObjectByType(ObjectID, Object);
803 ASDCP::MXF::OPAtomHeader::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
805 return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
809 ASDCP::MXF::Identification*
810 ASDCP::MXF::OPAtomHeader::GetIdentification()
812 InterchangeObject* Object;
814 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object)) )
815 return (Identification*)Object;
821 ASDCP::MXF::SourcePackage*
822 ASDCP::MXF::OPAtomHeader::GetSourcePackage()
824 InterchangeObject* Object;
826 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object)) )
827 return (SourcePackage*)Object;
835 ASDCP::MXF::OPAtomHeader::WriteToFile(Kumu::FileWriter& Writer, ui32_t HeaderSize)
837 if ( m_Preface == 0 )
840 if ( HeaderSize < 4096 )
842 DefaultLogSink().Error("HeaderSize %u is too small. Must be >= 4096\n", HeaderSize);
846 ASDCP::FrameBuffer HeaderBuffer;
847 HeaderByteCount = HeaderSize - ArchiveSize();
848 Result_t result = HeaderBuffer.Capacity(HeaderByteCount);
849 m_Preface->m_Lookup = &m_Primer;
851 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
852 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
854 InterchangeObject* object = *pl_i;
855 object->m_Lookup = &m_Primer;
857 ASDCP::FrameBuffer WriteWrapper;
858 WriteWrapper.SetData(HeaderBuffer.Data() + HeaderBuffer.Size(),
859 HeaderBuffer.Capacity() - HeaderBuffer.Size());
860 result = object->WriteToBuffer(WriteWrapper);
861 HeaderBuffer.Size(HeaderBuffer.Size() + WriteWrapper.Size());
864 if ( ASDCP_SUCCESS(result) )
866 UL TmpUL(Dict::ul(MDD_ClosedCompleteHeader));
867 result = Partition::WriteToFile(Writer, TmpUL);
870 if ( ASDCP_SUCCESS(result) )
871 result = m_Primer.WriteToFile(Writer);
873 if ( ASDCP_SUCCESS(result) )
876 Writer.Write(HeaderBuffer.RoData(), HeaderBuffer.Size(), &write_count);
877 assert(write_count == HeaderBuffer.Size());
881 if ( ASDCP_SUCCESS(result) )
883 Kumu::fpos_t pos = Writer.Tell();
885 if ( pos > (Kumu::fpos_t)HeaderByteCount )
887 char intbuf[IntBufferLen];
888 DefaultLogSink().Error("Header size %s exceeds specified value %u\n",
894 ASDCP::FrameBuffer NilBuf;
895 ui32_t klv_fill_length = HeaderSize - (ui32_t)pos;
897 if ( klv_fill_length < kl_length )
899 DefaultLogSink().Error("Remaining region too small for KLV Fill header\n");
903 klv_fill_length -= kl_length;
904 result = WriteKLToFile(Writer, Dict::ul(MDD_KLVFill), klv_fill_length);
906 if ( ASDCP_SUCCESS(result) )
907 result = NilBuf.Capacity(klv_fill_length);
909 if ( ASDCP_SUCCESS(result) )
911 memset(NilBuf.Data(), 0, klv_fill_length);
913 Writer.Write(NilBuf.RoData(), klv_fill_length, &write_count);
914 assert(write_count == klv_fill_length);
923 ASDCP::MXF::OPAtomHeader::Dump(FILE* stream)
931 Partition::Dump(stream);
932 m_Primer.Dump(stream);
934 if ( m_Preface == 0 )
935 fputs("No Preface loaded\n", stream);
937 m_Preface->Dump(stream);
939 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
940 for ( ; i != m_PacketList->m_List.end(); i++ )
944 //------------------------------------------------------------------------------------------
947 ASDCP::MXF::OPAtomIndexFooter::OPAtomIndexFooter() :
948 m_CurrentSegment(0), m_BytesPerEditUnit(0), m_BodySID(0),
949 m_ECOffset(0), m_Lookup(0)
955 ASDCP::MXF::OPAtomIndexFooter::~OPAtomIndexFooter() {}
959 ASDCP::MXF::OPAtomIndexFooter::InitFromFile(const Kumu::FileReader& Reader)
961 Result_t result = Partition::InitFromFile(Reader); // test UL and OP
963 // slurp up the remainder of the footer
966 if ( ASDCP_SUCCESS(result) )
967 result = m_Buffer.Capacity(IndexByteCount);
969 if ( ASDCP_SUCCESS(result) )
970 result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
972 if ( ASDCP_SUCCESS(result) && read_count != m_Buffer.Capacity() )
974 DefaultLogSink().Error("Short read of footer partition: got %u, expecting %u\n",
975 read_count, m_Buffer.Capacity());
979 const byte_t* p = m_Buffer.RoData();
980 const byte_t* end_p = p + m_Buffer.Capacity();
982 while ( ASDCP_SUCCESS(result) && p < end_p )
984 // parse the packets and index them by uid, discard KLVFill items
985 InterchangeObject* object = CreateObject(p);
988 object->m_Lookup = m_Lookup;
989 result = object->InitFromBuffer(p, end_p - p);
990 p += object->PacketLength();
992 if ( ASDCP_SUCCESS(result) )
994 m_PacketList->AddPacket(object);
998 DefaultLogSink().Error("Error initializing packet\n");
1003 if ( ASDCP_FAILURE(result) )
1004 DefaultLogSink().Error("Failed to initialize OPAtomIndexFooter\n");
1011 ASDCP::MXF::OPAtomIndexFooter::WriteToFile(Kumu::FileWriter& Writer, ui64_t duration)
1013 ASDCP::FrameBuffer FooterBuffer;
1014 ui32_t footer_size = m_PacketList->m_List.size() * MaxIndexSegmentSize; // segment-count * max-segment-size
1015 Result_t result = FooterBuffer.Capacity(footer_size);
1016 ui32_t iseg_count = 0;
1018 if ( m_CurrentSegment != 0 )
1020 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1021 m_CurrentSegment = 0;
1024 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
1025 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
1027 if ( (*pl_i)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1030 IndexTableSegment* Segment = (IndexTableSegment*)(*pl_i);
1032 if ( m_BytesPerEditUnit != 0 )
1034 if ( iseg_count != 1 )
1035 return RESULT_STATE;
1037 Segment->IndexDuration = duration;
1041 InterchangeObject* object = *pl_i;
1042 object->m_Lookup = m_Lookup;
1044 ASDCP::FrameBuffer WriteWrapper;
1045 WriteWrapper.SetData(FooterBuffer.Data() + FooterBuffer.Size(),
1046 FooterBuffer.Capacity() - FooterBuffer.Size());
1047 result = object->WriteToBuffer(WriteWrapper);
1048 FooterBuffer.Size(FooterBuffer.Size() + WriteWrapper.Size());
1051 if ( ASDCP_SUCCESS(result) )
1053 IndexByteCount = FooterBuffer.Size();
1054 UL FooterUL(Dict::ul(MDD_CompleteFooter));
1055 result = Partition::WriteToFile(Writer, FooterUL);
1058 if ( ASDCP_SUCCESS(result) )
1060 ui32_t write_count = 0;
1061 result = Writer.Write(FooterBuffer.RoData(), FooterBuffer.Size(), &write_count);
1062 assert(write_count == FooterBuffer.Size());
1070 ASDCP::MXF::OPAtomIndexFooter::Dump(FILE* stream)
1075 Partition::Dump(stream);
1077 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
1078 for ( ; i != m_PacketList->m_List.end(); i++ )
1084 ASDCP::MXF::OPAtomIndexFooter::Lookup(ui32_t frame_num, IndexTableSegment::IndexEntry& Entry)
1086 std::list<InterchangeObject*>::iterator li;
1087 for ( li = m_PacketList->m_List.begin(); li != m_PacketList->m_List.end(); li++ )
1089 if ( (*li)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1091 IndexTableSegment* Segment = (IndexTableSegment*)(*li);
1092 ui64_t start_pos = Segment->IndexStartPosition;
1094 if ( Segment->EditUnitByteCount > 0 )
1096 if ( m_PacketList->m_List.size() > 1 )
1097 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
1099 if ( ! Segment->IndexEntryArray.empty() )
1100 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
1102 Entry.StreamOffset = (ui64_t)frame_num * Segment->EditUnitByteCount;
1105 else if ( (ui64_t)frame_num >= start_pos
1106 && (ui64_t)frame_num < (start_pos + Segment->IndexDuration) )
1108 Entry = Segment->IndexEntryArray[frame_num-start_pos];
1119 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const Rational& Rate)
1123 m_BytesPerEditUnit = size;
1126 IndexTableSegment* Index = new IndexTableSegment;
1127 AddChildObject(Index);
1128 Index->EditUnitByteCount = m_BytesPerEditUnit;
1129 Index->IndexEditRate = Rate;
1134 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsVBR(IPrimerLookup* lookup, const Rational& Rate, Kumu::fpos_t offset)
1138 m_BytesPerEditUnit = 0;
1140 m_ECOffset = offset;
1145 ASDCP::MXF::OPAtomIndexFooter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
1147 if ( m_BytesPerEditUnit != 0 ) // are we CBR? that's bad
1149 DefaultLogSink().Error("Call to PushIndexEntry() failed: index is CBR\n");
1153 // do we have an available segment?
1154 if ( m_CurrentSegment == 0 )
1155 { // no, set up a new segment
1156 m_CurrentSegment = new IndexTableSegment;
1157 assert(m_CurrentSegment);
1158 AddChildObject(m_CurrentSegment);
1159 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1160 m_CurrentSegment->IndexEditRate = m_EditRate;
1161 m_CurrentSegment->IndexStartPosition = 0;
1163 else if ( m_CurrentSegment->IndexEntryArray.size() >= CBRIndexEntriesPerSegment )
1164 { // no, this one is full, start another
1165 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1166 ui64_t StartPosition = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
1168 m_CurrentSegment = new IndexTableSegment;
1169 assert(m_CurrentSegment);
1170 AddChildObject(m_CurrentSegment);
1171 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1172 m_CurrentSegment->IndexEditRate = m_EditRate;
1173 m_CurrentSegment->IndexStartPosition = StartPosition;
1176 m_CurrentSegment->IndexEntryArray.push_back(Entry);
1179 //------------------------------------------------------------------------------------------
1184 ASDCP::MXF::InterchangeObject::InitFromTLVSet(TLVReader& TLVSet)
1186 Result_t result = TLVSet.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID));
1187 if ( ASDCP_SUCCESS(result) )
1188 result = TLVSet.ReadObject(OBJ_READ_ARGS(GenerationInterchangeObject, GenerationUID));
1194 ASDCP::MXF::InterchangeObject::WriteToTLVSet(TLVWriter& TLVSet)
1196 Result_t result = TLVSet.WriteObject(OBJ_WRITE_ARGS(InterchangeObject, InstanceUID));
1197 if ( ASDCP_SUCCESS(result) )
1198 result = TLVSet.WriteObject(OBJ_WRITE_ARGS(GenerationInterchangeObject, GenerationUID));
1204 ASDCP::MXF::InterchangeObject::InitFromBuffer(const byte_t* p, ui32_t l)
1207 Result_t result = RESULT_FALSE;
1209 if ( m_Typeinfo == 0 )
1211 result = KLVPacket::InitFromBuffer(p, l);
1215 result = KLVPacket::InitFromBuffer(p, l, m_Typeinfo->ul);
1217 if ( ASDCP_SUCCESS(result) )
1219 TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup);
1220 result = InitFromTLVSet(MemRDR);
1229 ASDCP::MXF::InterchangeObject::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
1231 if ( m_Typeinfo == 0 )
1232 return RESULT_STATE;
1234 TLVWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length, m_Lookup);
1235 Result_t result = WriteToTLVSet(MemWRT);
1237 if ( ASDCP_SUCCESS(result) )
1239 ui32_t packet_length = MemWRT.Length();
1240 result = WriteKLToBuffer(Buffer, m_Typeinfo->ul, packet_length);
1242 if ( ASDCP_SUCCESS(result) )
1243 Buffer.Size(Buffer.Size() + packet_length);
1251 ASDCP::MXF::InterchangeObject::Dump(FILE* stream)
1253 char identbuf[IdentBufferLen];
1255 fputc('\n', stream);
1256 KLVPacket::Dump(stream, false);
1257 fprintf(stream, " InstanceUID = %s\n", InstanceUID.EncodeHex(identbuf, IdentBufferLen));
1258 fprintf(stream, " GenerationUID = %s\n", GenerationUID.EncodeHex(identbuf, IdentBufferLen));
1263 ASDCP::MXF::InterchangeObject::IsA(const byte_t* label)
1265 if ( m_KLLength == 0 )
1268 return ( memcmp(label, m_KeyStart, SMPTE_UL_LENGTH) == 0 );
1272 //------------------------------------------------------------------------------------------
1275 typedef std::map<ASDCP::UL, ASDCP::MXF::MXFObjectFactory_t>FactoryMap_t;
1276 typedef FactoryMap_t::iterator FLi_t;
1279 class FactoryList : public FactoryMap_t
1288 Kumu::AutoMutex BlockLock(m_Lock);
1292 FLi_t Find(const byte_t* label) {
1293 Kumu::AutoMutex BlockLock(m_Lock);
1298 Kumu::AutoMutex BlockLock(m_Lock);
1302 void Insert(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory) {
1303 Kumu::AutoMutex BlockLock(m_Lock);
1304 insert(FactoryList::value_type(label, factory));
1309 static FactoryList s_FactoryList;
1310 static Kumu::Mutex s_InitLock;
1311 static bool s_TypesInit = false;
1316 ASDCP::MXF::SetObjectFactory(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory)
1318 s_FactoryList.Insert(label, factory);
1323 ASDCP::MXF::InterchangeObject*
1324 ASDCP::MXF::CreateObject(const byte_t* label)
1329 if ( ! s_TypesInit )
1331 Kumu::AutoMutex BlockLock(s_InitLock);
1333 if ( ! s_TypesInit )
1335 MXF::Metadata_InitTypes();
1340 FLi_t i = s_FactoryList.find(label);
1342 if ( i == s_FactoryList.end() )
1343 return new InterchangeObject;