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 result = m_Buffer.Capacity(HeaderByteCount);
716 if ( ASDCP_SUCCESS(result) )
719 result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
721 if ( ASDCP_FAILURE(result) )
724 if ( read_count != m_Buffer.Capacity() )
726 DefaultLogSink().Error("Short read of OP-Atom header metadata; wanted %u, got %u\n",
727 m_Buffer.Capacity(), read_count);
728 return RESULT_KLV_CODING;
732 const byte_t* p = m_Buffer.RoData();
733 const byte_t* end_p = p + m_Buffer.Capacity();
735 while ( ASDCP_SUCCESS(result) && p < end_p )
737 // parse the packets and index them by uid, discard KLVFill items
738 InterchangeObject* object = CreateObject(p);
741 object->m_Lookup = &m_Primer;
742 result = object->InitFromBuffer(p, end_p - p);
743 const byte_t* redo_p = p;
744 p += object->PacketLength();
745 // hexdump(p, object->PacketLength());
747 if ( ASDCP_SUCCESS(result) )
749 if ( object->IsA(Dict::ul(MDD_KLVFill)) )
753 else if ( object->IsA(Dict::ul(MDD_Primer)) )
756 result = m_Primer.InitFromBuffer(redo_p, end_p - redo_p);
760 m_PacketList->AddPacket(object);
762 if ( object->IsA(Dict::ul(MDD_Preface)) )
764 assert(m_Preface == 0);
765 m_Preface = (Preface*)object;
771 DefaultLogSink().Error("Error initializing packet\n");
780 ASDCP::MXF::OPAtomHeader::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
782 return m_PacketList->GetMDObjectByID(ObjectID, Object);
787 ASDCP::MXF::OPAtomHeader::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
789 InterchangeObject* TmpObject;
794 return m_PacketList->GetMDObjectByType(ObjectID, Object);
799 ASDCP::MXF::OPAtomHeader::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
801 return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
805 ASDCP::MXF::Identification*
806 ASDCP::MXF::OPAtomHeader::GetIdentification()
808 InterchangeObject* Object;
810 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object)) )
811 return (Identification*)Object;
817 ASDCP::MXF::SourcePackage*
818 ASDCP::MXF::OPAtomHeader::GetSourcePackage()
820 InterchangeObject* Object;
822 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object)) )
823 return (SourcePackage*)Object;
831 ASDCP::MXF::OPAtomHeader::WriteToFile(Kumu::FileWriter& Writer, ui32_t HeaderSize)
833 if ( m_Preface == 0 )
836 if ( HeaderSize < 4096 )
838 DefaultLogSink().Error("HeaderSize %u is too small. Must be >= 4096\n", HeaderSize);
842 ASDCP::FrameBuffer HeaderBuffer;
843 HeaderByteCount = HeaderSize - ArchiveSize();
844 Result_t result = HeaderBuffer.Capacity(HeaderByteCount);
845 m_Preface->m_Lookup = &m_Primer;
847 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
848 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
850 InterchangeObject* object = *pl_i;
851 object->m_Lookup = &m_Primer;
853 ASDCP::FrameBuffer WriteWrapper;
854 WriteWrapper.SetData(HeaderBuffer.Data() + HeaderBuffer.Size(),
855 HeaderBuffer.Capacity() - HeaderBuffer.Size());
856 result = object->WriteToBuffer(WriteWrapper);
857 HeaderBuffer.Size(HeaderBuffer.Size() + WriteWrapper.Size());
860 if ( ASDCP_SUCCESS(result) )
862 UL TmpUL(Dict::ul(MDD_ClosedCompleteHeader));
863 result = Partition::WriteToFile(Writer, TmpUL);
866 if ( ASDCP_SUCCESS(result) )
867 result = m_Primer.WriteToFile(Writer);
869 if ( ASDCP_SUCCESS(result) )
872 Writer.Write(HeaderBuffer.RoData(), HeaderBuffer.Size(), &write_count);
873 assert(write_count == HeaderBuffer.Size());
877 if ( ASDCP_SUCCESS(result) )
879 Kumu::fpos_t pos = Writer.Tell();
881 if ( pos > (Kumu::fpos_t)HeaderByteCount )
883 char intbuf[IntBufferLen];
884 DefaultLogSink().Error("Header size %s exceeds specified value %u\n",
890 ASDCP::FrameBuffer NilBuf;
891 ui32_t klv_fill_length = HeaderSize - (ui32_t)pos;
893 if ( klv_fill_length < kl_length )
895 DefaultLogSink().Error("Remaining region too small for KLV Fill header\n");
899 klv_fill_length -= kl_length;
900 result = WriteKLToFile(Writer, Dict::ul(MDD_KLVFill), klv_fill_length);
902 if ( ASDCP_SUCCESS(result) )
903 result = NilBuf.Capacity(klv_fill_length);
905 if ( ASDCP_SUCCESS(result) )
907 memset(NilBuf.Data(), 0, klv_fill_length);
909 Writer.Write(NilBuf.RoData(), klv_fill_length, &write_count);
910 assert(write_count == klv_fill_length);
919 ASDCP::MXF::OPAtomHeader::Dump(FILE* stream)
924 Partition::Dump(stream);
925 m_Primer.Dump(stream);
927 if ( m_Preface == 0 )
928 fputs("No Preface loaded\n", stream);
930 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
931 for ( ; i != m_PacketList->m_List.end(); i++ )
935 //------------------------------------------------------------------------------------------
938 ASDCP::MXF::OPAtomIndexFooter::OPAtomIndexFooter() :
939 m_CurrentSegment(0), m_BytesPerEditUnit(0), m_BodySID(0),
940 m_ECOffset(0), m_Lookup(0)
946 ASDCP::MXF::OPAtomIndexFooter::~OPAtomIndexFooter() {}
950 ASDCP::MXF::OPAtomIndexFooter::InitFromFile(const Kumu::FileReader& Reader)
952 Result_t result = Partition::InitFromFile(Reader); // test UL and OP
954 // slurp up the remainder of the footer
957 if ( ASDCP_SUCCESS(result) )
958 result = m_Buffer.Capacity(IndexByteCount);
960 if ( ASDCP_SUCCESS(result) )
961 result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
963 if ( ASDCP_SUCCESS(result) && read_count != m_Buffer.Capacity() )
965 DefaultLogSink().Error("Short read of footer partition: got %u, expecting %u\n",
966 read_count, m_Buffer.Capacity());
970 const byte_t* p = m_Buffer.RoData();
971 const byte_t* end_p = p + m_Buffer.Capacity();
973 while ( ASDCP_SUCCESS(result) && p < end_p )
975 // parse the packets and index them by uid, discard KLVFill items
976 InterchangeObject* object = CreateObject(p);
979 object->m_Lookup = m_Lookup;
980 result = object->InitFromBuffer(p, end_p - p);
981 p += object->PacketLength();
983 if ( ASDCP_SUCCESS(result) )
985 m_PacketList->AddPacket(object);
989 DefaultLogSink().Error("Error initializing packet\n");
994 if ( ASDCP_FAILURE(result) )
995 DefaultLogSink().Error("Failed to initialize OPAtomIndexFooter\n");
1002 ASDCP::MXF::OPAtomIndexFooter::WriteToFile(Kumu::FileWriter& Writer, ui64_t duration)
1004 ASDCP::FrameBuffer FooterBuffer;
1005 ui32_t footer_size = m_PacketList->m_List.size() * MaxIndexSegmentSize; // segment-count * max-segment-size
1006 Result_t result = FooterBuffer.Capacity(footer_size);
1007 ui32_t iseg_count = 0;
1009 if ( m_CurrentSegment != 0 )
1011 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1012 m_CurrentSegment = 0;
1015 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
1016 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
1018 if ( (*pl_i)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1021 IndexTableSegment* Segment = (IndexTableSegment*)(*pl_i);
1023 if ( m_BytesPerEditUnit != 0 )
1025 if ( iseg_count != 1 )
1026 return RESULT_STATE;
1028 Segment->IndexDuration = duration;
1032 InterchangeObject* object = *pl_i;
1033 object->m_Lookup = m_Lookup;
1035 ASDCP::FrameBuffer WriteWrapper;
1036 WriteWrapper.SetData(FooterBuffer.Data() + FooterBuffer.Size(),
1037 FooterBuffer.Capacity() - FooterBuffer.Size());
1038 result = object->WriteToBuffer(WriteWrapper);
1039 FooterBuffer.Size(FooterBuffer.Size() + WriteWrapper.Size());
1042 if ( ASDCP_SUCCESS(result) )
1044 IndexByteCount = FooterBuffer.Size();
1045 UL FooterUL(Dict::ul(MDD_CompleteFooter));
1046 result = Partition::WriteToFile(Writer, FooterUL);
1049 if ( ASDCP_SUCCESS(result) )
1051 ui32_t write_count = 0;
1052 result = Writer.Write(FooterBuffer.RoData(), FooterBuffer.Size(), &write_count);
1053 assert(write_count == FooterBuffer.Size());
1061 ASDCP::MXF::OPAtomIndexFooter::Dump(FILE* stream)
1066 Partition::Dump(stream);
1068 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
1069 for ( ; i != m_PacketList->m_List.end(); i++ )
1075 ASDCP::MXF::OPAtomIndexFooter::Lookup(ui32_t frame_num, IndexTableSegment::IndexEntry& Entry) const
1077 std::list<InterchangeObject*>::iterator li;
1078 for ( li = m_PacketList->m_List.begin(); li != m_PacketList->m_List.end(); li++ )
1080 if ( (*li)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1082 IndexTableSegment* Segment = (IndexTableSegment*)(*li);
1083 ui64_t start_pos = Segment->IndexStartPosition;
1085 if ( Segment->EditUnitByteCount > 0 )
1087 if ( m_PacketList->m_List.size() > 1 )
1088 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
1090 if ( ! Segment->IndexEntryArray.empty() )
1091 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
1093 Entry.StreamOffset = (ui64_t)frame_num * Segment->EditUnitByteCount;
1096 else if ( (ui64_t)frame_num >= start_pos
1097 && (ui64_t)frame_num < (start_pos + Segment->IndexDuration) )
1099 Entry = Segment->IndexEntryArray[frame_num-start_pos];
1110 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const Rational& Rate)
1114 m_BytesPerEditUnit = size;
1117 IndexTableSegment* Index = new IndexTableSegment;
1118 AddChildObject(Index);
1119 Index->EditUnitByteCount = m_BytesPerEditUnit;
1120 Index->IndexEditRate = Rate;
1125 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsVBR(IPrimerLookup* lookup, const Rational& Rate, Kumu::fpos_t offset)
1129 m_BytesPerEditUnit = 0;
1131 m_ECOffset = offset;
1136 ASDCP::MXF::OPAtomIndexFooter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
1138 if ( m_BytesPerEditUnit != 0 ) // are we CBR? that's bad
1140 DefaultLogSink().Error("Call to PushIndexEntry() failed: index is CBR\n");
1144 // do we have an available segment?
1145 if ( m_CurrentSegment == 0 )
1146 { // no, set up a new segment
1147 m_CurrentSegment = new IndexTableSegment;
1148 assert(m_CurrentSegment);
1149 AddChildObject(m_CurrentSegment);
1150 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1151 m_CurrentSegment->IndexEditRate = m_EditRate;
1152 m_CurrentSegment->IndexStartPosition = 0;
1154 else if ( m_CurrentSegment->IndexEntryArray.size() >= CBRIndexEntriesPerSegment )
1155 { // no, this one is full, start another
1156 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1157 ui64_t StartPosition = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
1159 m_CurrentSegment = new IndexTableSegment;
1160 assert(m_CurrentSegment);
1161 AddChildObject(m_CurrentSegment);
1162 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1163 m_CurrentSegment->IndexEditRate = m_EditRate;
1164 m_CurrentSegment->IndexStartPosition = StartPosition;
1167 m_CurrentSegment->IndexEntryArray.push_back(Entry);
1170 //------------------------------------------------------------------------------------------
1175 ASDCP::MXF::InterchangeObject::InitFromTLVSet(TLVReader& TLVSet)
1177 Result_t result = TLVSet.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID));
1178 if ( ASDCP_SUCCESS(result) )
1179 result = TLVSet.ReadObject(OBJ_READ_ARGS(GenerationInterchangeObject, GenerationUID));
1185 ASDCP::MXF::InterchangeObject::WriteToTLVSet(TLVWriter& TLVSet)
1187 Result_t result = TLVSet.WriteObject(OBJ_WRITE_ARGS(InterchangeObject, InstanceUID));
1188 if ( ASDCP_SUCCESS(result) )
1189 result = TLVSet.WriteObject(OBJ_WRITE_ARGS(GenerationInterchangeObject, GenerationUID));
1195 ASDCP::MXF::InterchangeObject::InitFromBuffer(const byte_t* p, ui32_t l)
1198 Result_t result = RESULT_FALSE;
1200 if ( m_Typeinfo == 0 )
1202 result = KLVPacket::InitFromBuffer(p, l);
1206 result = KLVPacket::InitFromBuffer(p, l, m_Typeinfo->ul);
1208 if ( ASDCP_SUCCESS(result) )
1210 TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup);
1211 result = InitFromTLVSet(MemRDR);
1220 ASDCP::MXF::InterchangeObject::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
1222 if ( m_Typeinfo == 0 )
1223 return RESULT_STATE;
1225 TLVWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length, m_Lookup);
1226 Result_t result = WriteToTLVSet(MemWRT);
1228 if ( ASDCP_SUCCESS(result) )
1230 ui32_t packet_length = MemWRT.Length();
1231 result = WriteKLToBuffer(Buffer, m_Typeinfo->ul, packet_length);
1233 if ( ASDCP_SUCCESS(result) )
1234 Buffer.Size(Buffer.Size() + packet_length);
1242 ASDCP::MXF::InterchangeObject::Dump(FILE* stream)
1244 char identbuf[IdentBufferLen];
1246 fputc('\n', stream);
1247 KLVPacket::Dump(stream, false);
1248 fprintf(stream, " InstanceUID = %s\n", InstanceUID.EncodeHex(identbuf, IdentBufferLen));
1249 fprintf(stream, " GenerationUID = %s\n", GenerationUID.EncodeHex(identbuf, IdentBufferLen));
1254 ASDCP::MXF::InterchangeObject::IsA(const byte_t* label)
1256 if ( m_KLLength == 0 )
1259 return ( memcmp(label, m_KeyStart, SMPTE_UL_LENGTH) == 0 );
1263 //------------------------------------------------------------------------------------------
1266 typedef std::map<ASDCP::UL, ASDCP::MXF::MXFObjectFactory_t>FactoryMap_t;
1267 typedef FactoryMap_t::iterator FLi_t;
1270 class FactoryList : public FactoryMap_t
1279 Kumu::AutoMutex BlockLock(m_Lock);
1283 FLi_t Find(const byte_t* label) {
1284 Kumu::AutoMutex BlockLock(m_Lock);
1289 Kumu::AutoMutex BlockLock(m_Lock);
1293 void Insert(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory) {
1294 Kumu::AutoMutex BlockLock(m_Lock);
1295 insert(FactoryList::value_type(label, factory));
1300 static FactoryList s_FactoryList;
1301 static Kumu::Mutex s_InitLock;
1302 static bool s_TypesInit = false;
1307 ASDCP::MXF::SetObjectFactory(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory)
1309 s_FactoryList.Insert(label, factory);
1314 ASDCP::MXF::InterchangeObject*
1315 ASDCP::MXF::CreateObject(const byte_t* label)
1320 if ( ! s_TypesInit )
1322 Kumu::AutoMutex BlockLock(s_InitLock);
1324 if ( ! s_TypesInit )
1326 MXF::Metadata_InitTypes();
1331 FLi_t i = s_FactoryList.find(label);
1333 if ( i == s_FactoryList.end() )
1334 return new InterchangeObject;