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);
169 //------------------------------------------------------------------------------------------
173 class ASDCP::MXF::Partition::h__PacketList
176 std::list<InterchangeObject*> m_List;
177 std::map<UUID, InterchangeObject*> m_Map;
180 while ( ! m_List.empty() )
182 delete m_List.back();
188 void AddPacket(InterchangeObject* ThePacket)
191 m_Map.insert(std::map<UUID, InterchangeObject*>::value_type(ThePacket->InstanceUID, ThePacket));
192 m_List.push_back(ThePacket);
196 Result_t GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
198 ASDCP_TEST_NULL(Object);
200 std::map<UUID, InterchangeObject*>::iterator mi = m_Map.find(ObjectID);
202 if ( mi == m_Map.end() )
208 *Object = (*mi).second;
213 Result_t GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
215 ASDCP_TEST_NULL(ObjectID);
216 ASDCP_TEST_NULL(Object);
217 std::list<InterchangeObject*>::iterator li;
220 for ( li = m_List.begin(); li != m_List.end(); li++ )
222 if ( (*li)->HasUL(ObjectID) )
233 Result_t GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
235 ASDCP_TEST_NULL(ObjectID);
236 std::list<InterchangeObject*>::iterator li;
238 for ( li = m_List.begin(); li != m_List.end(); li++ )
240 if ( (*li)->HasUL(ObjectID) )
241 ObjectList.push_back(*li);
244 return ObjectList.empty() ? RESULT_FAIL : RESULT_OK;
248 //------------------------------------------------------------------------------------------
252 ASDCP::MXF::Partition::Partition() :
253 MajorVersion(1), MinorVersion(2),
254 KAGSize(1), ThisPartition(0), PreviousPartition(0),
255 FooterPartition(0), HeaderByteCount(0), IndexByteCount(0), IndexSID(0),
256 BodyOffset(0), BodySID(0)
258 m_PacketList = new h__PacketList;
261 ASDCP::MXF::Partition::~Partition()
267 ASDCP::MXF::Partition::AddChildObject(InterchangeObject* Object)
271 if ( ! Object->InstanceUID.HasValue() )
272 GenRandomValue(Object->InstanceUID);
274 m_PacketList->AddPacket(Object);
279 ASDCP::MXF::Partition::InitFromFile(const Kumu::FileReader& Reader)
281 Result_t result = KLVFilePacket::InitFromFile(Reader);
283 // could be one of several values
285 if ( ASDCP_SUCCESS(result) )
287 Kumu::MemIOReader MemRDR(m_ValueStart, m_ValueLength);
288 result = RESULT_KLV_CODING;
290 if ( MemRDR.ReadUi16BE(&MajorVersion) )
291 if ( MemRDR.ReadUi16BE(&MinorVersion) )
292 if ( MemRDR.ReadUi32BE(&KAGSize) )
293 if ( MemRDR.ReadUi64BE(&ThisPartition) )
294 if ( MemRDR.ReadUi64BE(&PreviousPartition) )
295 if ( MemRDR.ReadUi64BE(&FooterPartition) )
296 if ( MemRDR.ReadUi64BE(&HeaderByteCount) )
297 if ( MemRDR.ReadUi64BE(&IndexByteCount) )
298 if ( MemRDR.ReadUi32BE(&IndexSID) )
299 if ( MemRDR.ReadUi64BE(&BodyOffset) )
300 if ( MemRDR.ReadUi32BE(&BodySID) )
301 if ( OperationalPattern.Unarchive(&MemRDR) )
302 if ( EssenceContainers.Unarchive(&MemRDR) )
306 if ( ASDCP_FAILURE(result) )
307 DefaultLogSink().Error("Failed to initialize Partition\n");
314 ASDCP::MXF::Partition::WriteToFile(Kumu::FileWriter& Writer, UL& PartitionLabel)
316 ASDCP::FrameBuffer Buffer;
317 Result_t result = Buffer.Capacity(1024);
319 if ( ASDCP_SUCCESS(result) )
321 Kumu::MemIOWriter MemWRT(Buffer.Data(), Buffer.Capacity());
322 result = RESULT_KLV_CODING;
323 if ( MemWRT.WriteUi16BE(MajorVersion) )
324 if ( MemWRT.WriteUi16BE(MinorVersion) )
325 if ( MemWRT.WriteUi32BE(KAGSize) )
326 if ( MemWRT.WriteUi64BE(ThisPartition) )
327 if ( MemWRT.WriteUi64BE(PreviousPartition) )
328 if ( MemWRT.WriteUi64BE(FooterPartition) )
329 if ( MemWRT.WriteUi64BE(HeaderByteCount) )
330 if ( MemWRT.WriteUi64BE(IndexByteCount) )
331 if ( MemWRT.WriteUi32BE(IndexSID) )
332 if ( MemWRT.WriteUi64BE(BodyOffset) )
333 if ( MemWRT.WriteUi32BE(BodySID) )
334 if ( OperationalPattern.Archive(&MemWRT) )
335 if ( EssenceContainers.Archive(&MemWRT) )
337 Buffer.Size(MemWRT.Length());
342 if ( ASDCP_SUCCESS(result) )
345 result = WriteKLToFile(Writer, PartitionLabel.Value(), Buffer.Size());
347 if ( ASDCP_SUCCESS(result) )
348 result = Writer.Write(Buffer.RoData(), Buffer.Size(), &write_count);
356 ASDCP::MXF::Partition::ArchiveSize()
359 + sizeof(ui16_t) + sizeof(ui16_t)
361 + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t)
366 + sizeof(ui32_t) + sizeof(ui32_t) + ( UUIDlen * EssenceContainers.size() ) );
371 ASDCP::MXF::Partition::Dump(FILE* stream)
373 char identbuf[IdentBufferLen];
378 KLVFilePacket::Dump(stream, false);
379 fprintf(stream, " MajorVersion = %hu\n", MajorVersion);
380 fprintf(stream, " MinorVersion = %hu\n", MinorVersion);
381 fprintf(stream, " KAGSize = %u\n", KAGSize);
382 fprintf(stream, " ThisPartition = %s\n", ui64sz(ThisPartition, identbuf));
383 fprintf(stream, " PreviousPartition = %s\n", ui64sz(PreviousPartition, identbuf));
384 fprintf(stream, " FooterPartition = %s\n", ui64sz(FooterPartition, identbuf));
385 fprintf(stream, " HeaderByteCount = %s\n", ui64sz(HeaderByteCount, identbuf));
386 fprintf(stream, " IndexByteCount = %s\n", ui64sz(IndexByteCount, identbuf));
387 fprintf(stream, " IndexSID = %u\n", IndexSID);
388 fprintf(stream, " BodyOffset = %s\n", ui64sz(BodyOffset, identbuf));
389 fprintf(stream, " BodySID = %u\n", BodySID);
390 fprintf(stream, " OperationalPattern = %s\n", OperationalPattern.EncodeString(identbuf, IdentBufferLen));
391 fputs("Essence Containers:\n", stream); EssenceContainers.Dump(stream, false);
395 //------------------------------------------------------------------------------------------
398 class ASDCP::MXF::Primer::h__PrimerLookup : public std::map<UL, TagValue>
401 void InitWithBatch(ASDCP::MXF::Batch<ASDCP::MXF::Primer::LocalTagEntry>& Batch)
403 ASDCP::MXF::Batch<ASDCP::MXF::Primer::LocalTagEntry>::iterator i = Batch.begin();
405 for ( ; i != Batch.end(); i++ )
406 insert(std::map<UL, TagValue>::value_type((*i).UL, (*i).Tag));
412 ASDCP::MXF::Primer::Primer() : m_LocalTag(0xff) {}
415 ASDCP::MXF::Primer::~Primer() {}
419 ASDCP::MXF::Primer::ClearTagList()
421 LocalTagEntryBatch.clear();
422 m_Lookup = new h__PrimerLookup;
427 ASDCP::MXF::Primer::InitFromBuffer(const byte_t* p, ui32_t l)
429 Result_t result = KLVPacket::InitFromBuffer(p, l, Dict::ul(MDD_Primer));
431 if ( ASDCP_SUCCESS(result) )
433 Kumu::MemIOReader MemRDR(m_ValueStart, m_ValueLength);
434 result = LocalTagEntryBatch.Unarchive(&MemRDR) ? RESULT_OK : RESULT_KLV_CODING;
437 if ( ASDCP_SUCCESS(result) )
439 m_Lookup = new h__PrimerLookup;
440 m_Lookup->InitWithBatch(LocalTagEntryBatch);
443 if ( ASDCP_FAILURE(result) )
444 DefaultLogSink().Error("Failed to initialize Primer\n");
451 ASDCP::MXF::Primer::WriteToFile(Kumu::FileWriter& Writer)
453 ASDCP::FrameBuffer Buffer;
454 Result_t result = Buffer.Capacity(128*1024);
456 if ( ASDCP_SUCCESS(result) )
457 result = WriteToBuffer(Buffer);
459 if ( ASDCP_SUCCESS(result) )
460 result = Writer.Write(Buffer.RoData(), Buffer.Size());
467 ASDCP::MXF::Primer::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
469 ASDCP::FrameBuffer LocalTagBuffer;
470 Kumu::MemIOWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length);
471 Result_t result = LocalTagEntryBatch.Archive(&MemWRT) ? RESULT_OK : RESULT_KLV_CODING;
473 if ( ASDCP_SUCCESS(result) )
475 ui32_t packet_length = MemWRT.Length();
476 result = WriteKLToBuffer(Buffer, Dict::ul(MDD_Primer), packet_length);
478 if ( ASDCP_SUCCESS(result) )
479 Buffer.Size(Buffer.Size() + packet_length);
487 ASDCP::MXF::Primer::InsertTag(const MDDEntry& Entry, ASDCP::TagValue& Tag)
491 std::map<UL, TagValue>::iterator i = m_Lookup->find(TestUL);
493 if ( i == m_Lookup->end() )
495 if ( Entry.tag.a == 0 && Entry.tag.b == 0 )
498 Tag.b = m_LocalTag--;
506 LocalTagEntry TmpEntry;
507 TmpEntry.UL = TestUL;
510 LocalTagEntryBatch.push_back(TmpEntry);
511 m_Lookup->insert(std::map<UL, TagValue>::value_type(TmpEntry.UL, TmpEntry.Tag));
523 ASDCP::MXF::Primer::TagForKey(const ASDCP::UL& Key, ASDCP::TagValue& Tag)
526 if ( m_Lookup.empty() )
528 DefaultLogSink().Error("Primer lookup is empty\n");
532 std::map<UL, TagValue>::iterator i = m_Lookup->find(Key);
534 if ( i == m_Lookup->end() )
543 ASDCP::MXF::Primer::Dump(FILE* stream)
545 char identbuf[IdentBufferLen];
550 KLVPacket::Dump(stream, false);
551 fprintf(stream, "Primer: %u %s\n",
552 (ui32_t)LocalTagEntryBatch.size(),
553 ( LocalTagEntryBatch.size() == 1 ? "entry" : "entries" ));
555 Batch<LocalTagEntry>::iterator i = LocalTagEntryBatch.begin();
556 for ( ; i != LocalTagEntryBatch.end(); i++ )
558 const MDDEntry* Entry = Dict::FindUL((*i).UL.Value());
559 fprintf(stream, " %s %s\n", (*i).EncodeString(identbuf, IdentBufferLen), (Entry ? Entry->name : "Unknown"));
564 //------------------------------------------------------------------------------------------
569 ASDCP::MXF::Preface::InitFromTLVSet(TLVReader& TLVSet)
571 Result_t result = InterchangeObject::InitFromTLVSet(TLVSet);
572 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, LastModifiedDate));
573 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi16(OBJ_READ_ARGS(Preface, Version));
574 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi32(OBJ_READ_ARGS(Preface, ObjectModelVersion));
575 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, PrimaryPackage));
576 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, Identifications));
577 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, ContentStorage));
578 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, OperationalPattern));
579 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, EssenceContainers));
580 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, DMSchemes));
586 ASDCP::MXF::Preface::WriteToTLVSet(TLVWriter& TLVSet)
588 Result_t result = InterchangeObject::WriteToTLVSet(TLVSet);
589 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, LastModifiedDate));
590 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi16(OBJ_WRITE_ARGS(Preface, Version));
591 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi32(OBJ_WRITE_ARGS(Preface, ObjectModelVersion));
592 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, PrimaryPackage));
593 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, Identifications));
594 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, ContentStorage));
595 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, OperationalPattern));
596 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, EssenceContainers));
597 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, DMSchemes));
603 ASDCP::MXF::Preface::InitFromBuffer(const byte_t* p, ui32_t l)
605 m_Typeinfo = &Dict::Type(MDD_Preface);
606 return InterchangeObject::InitFromBuffer(p, l);
611 ASDCP::MXF::Preface::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
613 m_Typeinfo = &Dict::Type(MDD_Preface);
614 return InterchangeObject::WriteToBuffer(Buffer);
619 ASDCP::MXF::Preface::Dump(FILE* stream)
621 char identbuf[IdentBufferLen];
626 InterchangeObject::Dump(stream);
627 fprintf(stream, " %22s = %s\n", "LastModifiedDate", LastModifiedDate.EncodeString(identbuf, IdentBufferLen));
628 fprintf(stream, " %22s = %hu\n", "Version", Version);
629 fprintf(stream, " %22s = %u\n", "ObjectModelVersion", ObjectModelVersion);
630 fprintf(stream, " %22s = %s\n", "PrimaryPackage", PrimaryPackage.EncodeHex(identbuf, IdentBufferLen));
631 fprintf(stream, " %22s:\n", "Identifications"); Identifications.Dump(stream);
632 fprintf(stream, " %22s = %s\n", "ContentStorage", ContentStorage.EncodeHex(identbuf, IdentBufferLen));
633 fprintf(stream, " %22s = %s\n", "OperationalPattern", OperationalPattern.EncodeString(identbuf, IdentBufferLen));
634 fprintf(stream, " %22s:\n", "EssenceContainers"); EssenceContainers.Dump(stream);
635 fprintf(stream, " %22s:\n", "DMSchemes"); DMSchemes.Dump(stream);
638 //------------------------------------------------------------------------------------------
641 ASDCP::MXF::OPAtomHeader::OPAtomHeader() : m_Preface(0), m_HasRIP(false) {}
642 ASDCP::MXF::OPAtomHeader::~OPAtomHeader() {}
646 ASDCP::MXF::OPAtomHeader::InitFromFile(const Kumu::FileReader& Reader)
649 Result_t result = SeekToRIP(Reader);
651 if ( ASDCP_SUCCESS(result) )
653 result = m_RIP.InitFromFile(Reader);
654 ui32_t test_s = m_RIP.PairArray.size();
656 if ( ASDCP_FAILURE(result) )
658 DefaultLogSink().Error("File contains no RIP\n");
661 else if ( test_s == 0 )
663 DefaultLogSink().Error("RIP contains no Pairs.\n");
664 result = RESULT_FORMAT;
668 if ( test_s < 2 || test_s > 3 )
670 // OP-Atom states that there will be either two or three partitions:
671 // one closed header and one closed footer with an optional body
672 DefaultLogSink().Warn("RIP count is not 2 or 3: %u\n", test_s);
677 if ( m_RIP.PairArray.front().ByteOffset != 0 )
679 DefaultLogSink().Error("First Partition in RIP is not at offset 0.\n");
680 result = RESULT_FORMAT;
685 if ( ASDCP_SUCCESS(result) )
686 result = Reader.Seek(0);
688 if ( ASDCP_SUCCESS(result) )
689 result = Partition::InitFromFile(Reader); // test UL and OP
691 if ( ASDCP_FAILURE(result) )
694 // is it really OP-Atom?
695 UL OPAtomUL(Dict::ul(MDD_OPAtom));
696 UL InteropOPAtomUL(Dict::ul(MDD_MXFInterop_OPAtom));
698 if ( ! ( OperationalPattern == OPAtomUL || OperationalPattern == InteropOPAtomUL ) )
700 char strbuf[IdentBufferLen];
701 const MDDEntry* Entry = Dict::FindUL(OperationalPattern.Value());
703 DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n", OperationalPattern.EncodeString(strbuf, IdentBufferLen));
705 DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n", Entry->name);
708 // slurp up the remainder of the header
709 if ( HeaderByteCount < 1024 )
710 DefaultLogSink().Warn("Improbably small HeaderByteCount value: %u\n", HeaderByteCount);
712 result = m_Buffer.Capacity(HeaderByteCount);
714 if ( ASDCP_SUCCESS(result) )
717 result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
719 if ( ASDCP_FAILURE(result) )
722 if ( read_count != m_Buffer.Capacity() )
724 DefaultLogSink().Error("Short read of OP-Atom header metadata; wanted %u, got %u\n",
725 m_Buffer.Capacity(), read_count);
726 return RESULT_KLV_CODING;
730 const byte_t* p = m_Buffer.RoData();
731 const byte_t* end_p = p + m_Buffer.Capacity();
733 while ( ASDCP_SUCCESS(result) && p < end_p )
735 // parse the packets and index them by uid, discard KLVFill items
736 InterchangeObject* object = CreateObject(p);
739 object->m_Lookup = &m_Primer;
740 result = object->InitFromBuffer(p, end_p - p);
741 const byte_t* redo_p = p;
742 p += object->PacketLength();
743 // hexdump(p, object->PacketLength());
745 if ( ASDCP_SUCCESS(result) )
747 if ( object->IsA(Dict::ul(MDD_KLVFill)) )
751 else if ( object->IsA(Dict::ul(MDD_Primer)) )
754 result = m_Primer.InitFromBuffer(redo_p, end_p - redo_p);
758 m_PacketList->AddPacket(object);
760 if ( object->IsA(Dict::ul(MDD_Preface)) )
762 assert(m_Preface == 0);
763 m_Preface = (Preface*)object;
769 DefaultLogSink().Error("Error initializing packet\n");
778 ASDCP::MXF::OPAtomHeader::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
780 return m_PacketList->GetMDObjectByID(ObjectID, Object);
785 ASDCP::MXF::OPAtomHeader::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
787 InterchangeObject* TmpObject;
792 return m_PacketList->GetMDObjectByType(ObjectID, Object);
797 ASDCP::MXF::OPAtomHeader::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
799 return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
803 ASDCP::MXF::Identification*
804 ASDCP::MXF::OPAtomHeader::GetIdentification()
806 InterchangeObject* Object;
808 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object)) )
809 return (Identification*)Object;
815 ASDCP::MXF::SourcePackage*
816 ASDCP::MXF::OPAtomHeader::GetSourcePackage()
818 InterchangeObject* Object;
820 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object)) )
821 return (SourcePackage*)Object;
829 ASDCP::MXF::OPAtomHeader::WriteToFile(Kumu::FileWriter& Writer, ui32_t HeaderSize)
831 if ( m_Preface == 0 )
834 if ( HeaderSize < 4096 )
836 DefaultLogSink().Error("HeaderSize %u is too small. Must be >= 4096\n", HeaderSize);
840 ASDCP::FrameBuffer HeaderBuffer;
841 HeaderByteCount = HeaderSize - ArchiveSize();
842 Result_t result = HeaderBuffer.Capacity(HeaderByteCount);
843 m_Preface->m_Lookup = &m_Primer;
845 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
846 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
848 InterchangeObject* object = *pl_i;
849 object->m_Lookup = &m_Primer;
851 ASDCP::FrameBuffer WriteWrapper;
852 WriteWrapper.SetData(HeaderBuffer.Data() + HeaderBuffer.Size(),
853 HeaderBuffer.Capacity() - HeaderBuffer.Size());
854 result = object->WriteToBuffer(WriteWrapper);
855 HeaderBuffer.Size(HeaderBuffer.Size() + WriteWrapper.Size());
858 if ( ASDCP_SUCCESS(result) )
860 UL TmpUL(Dict::ul(MDD_ClosedCompleteHeader));
861 result = Partition::WriteToFile(Writer, TmpUL);
864 if ( ASDCP_SUCCESS(result) )
865 result = m_Primer.WriteToFile(Writer);
867 if ( ASDCP_SUCCESS(result) )
870 Writer.Write(HeaderBuffer.RoData(), HeaderBuffer.Size(), &write_count);
871 assert(write_count == HeaderBuffer.Size());
875 if ( ASDCP_SUCCESS(result) )
877 Kumu::fpos_t pos = Writer.Tell();
879 if ( pos > (Kumu::fpos_t)HeaderByteCount )
881 char intbuf[IntBufferLen];
882 DefaultLogSink().Error("Header size %s exceeds specified value %u\n",
888 ASDCP::FrameBuffer NilBuf;
889 ui32_t klv_fill_length = HeaderSize - (ui32_t)pos;
891 if ( klv_fill_length < kl_length )
893 DefaultLogSink().Error("Remaining region too small for KLV Fill header\n");
897 klv_fill_length -= kl_length;
898 result = WriteKLToFile(Writer, Dict::ul(MDD_KLVFill), klv_fill_length);
900 if ( ASDCP_SUCCESS(result) )
901 result = NilBuf.Capacity(klv_fill_length);
903 if ( ASDCP_SUCCESS(result) )
905 memset(NilBuf.Data(), 0, klv_fill_length);
907 Writer.Write(NilBuf.RoData(), klv_fill_length, &write_count);
908 assert(write_count == klv_fill_length);
917 ASDCP::MXF::OPAtomHeader::Dump(FILE* stream)
922 Partition::Dump(stream);
923 m_Primer.Dump(stream);
925 if ( m_Preface == 0 )
926 fputs("No Preface loaded\n", stream);
928 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
929 for ( ; i != m_PacketList->m_List.end(); i++ )
933 //------------------------------------------------------------------------------------------
936 ASDCP::MXF::OPAtomIndexFooter::OPAtomIndexFooter() :
937 m_CurrentSegment(0), m_BytesPerEditUnit(0), m_BodySID(0),
938 m_ECOffset(0), m_Lookup(0)
944 ASDCP::MXF::OPAtomIndexFooter::~OPAtomIndexFooter() {}
948 ASDCP::MXF::OPAtomIndexFooter::InitFromFile(const Kumu::FileReader& Reader)
950 Result_t result = Partition::InitFromFile(Reader); // test UL and OP
952 // slurp up the remainder of the footer
955 if ( ASDCP_SUCCESS(result) )
956 result = m_Buffer.Capacity(IndexByteCount);
958 if ( ASDCP_SUCCESS(result) )
959 result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
961 if ( ASDCP_SUCCESS(result) && read_count != m_Buffer.Capacity() )
963 DefaultLogSink().Error("Short read of footer partition: got %u, expecting %u\n",
964 read_count, m_Buffer.Capacity());
968 const byte_t* p = m_Buffer.RoData();
969 const byte_t* end_p = p + m_Buffer.Capacity();
971 while ( ASDCP_SUCCESS(result) && p < end_p )
973 // parse the packets and index them by uid, discard KLVFill items
974 InterchangeObject* object = CreateObject(p);
977 object->m_Lookup = m_Lookup;
978 result = object->InitFromBuffer(p, end_p - p);
979 p += object->PacketLength();
981 if ( ASDCP_SUCCESS(result) )
983 m_PacketList->AddPacket(object);
987 DefaultLogSink().Error("Error initializing packet\n");
992 if ( ASDCP_FAILURE(result) )
993 DefaultLogSink().Error("Failed to initialize OPAtomIndexFooter\n");
1000 ASDCP::MXF::OPAtomIndexFooter::WriteToFile(Kumu::FileWriter& Writer, ui64_t duration)
1002 ASDCP::FrameBuffer FooterBuffer;
1003 ui32_t footer_size = m_PacketList->m_List.size() * MaxIndexSegmentSize; // segment-count * max-segment-size
1004 Result_t result = FooterBuffer.Capacity(footer_size);
1005 ui32_t iseg_count = 0;
1007 if ( m_CurrentSegment != 0 )
1009 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1010 m_CurrentSegment = 0;
1013 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
1014 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
1016 if ( (*pl_i)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1019 IndexTableSegment* Segment = (IndexTableSegment*)(*pl_i);
1021 if ( m_BytesPerEditUnit != 0 )
1023 if ( iseg_count != 1 )
1024 return RESULT_STATE;
1026 Segment->IndexDuration = duration;
1030 InterchangeObject* object = *pl_i;
1031 object->m_Lookup = m_Lookup;
1033 ASDCP::FrameBuffer WriteWrapper;
1034 WriteWrapper.SetData(FooterBuffer.Data() + FooterBuffer.Size(),
1035 FooterBuffer.Capacity() - FooterBuffer.Size());
1036 result = object->WriteToBuffer(WriteWrapper);
1037 FooterBuffer.Size(FooterBuffer.Size() + WriteWrapper.Size());
1040 if ( ASDCP_SUCCESS(result) )
1042 IndexByteCount = FooterBuffer.Size();
1043 UL FooterUL(Dict::ul(MDD_CompleteFooter));
1044 result = Partition::WriteToFile(Writer, FooterUL);
1047 if ( ASDCP_SUCCESS(result) )
1049 ui32_t write_count = 0;
1050 result = Writer.Write(FooterBuffer.RoData(), FooterBuffer.Size(), &write_count);
1051 assert(write_count == FooterBuffer.Size());
1059 ASDCP::MXF::OPAtomIndexFooter::Dump(FILE* stream)
1064 Partition::Dump(stream);
1066 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
1067 for ( ; i != m_PacketList->m_List.end(); i++ )
1073 ASDCP::MXF::OPAtomIndexFooter::Lookup(ui32_t frame_num, IndexTableSegment::IndexEntry& Entry) const
1075 std::list<InterchangeObject*>::iterator li;
1076 for ( li = m_PacketList->m_List.begin(); li != m_PacketList->m_List.end(); li++ )
1078 if ( (*li)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1080 IndexTableSegment* Segment = (IndexTableSegment*)(*li);
1081 ui64_t start_pos = Segment->IndexStartPosition;
1083 if ( Segment->EditUnitByteCount > 0 )
1085 if ( m_PacketList->m_List.size() > 1 )
1086 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
1088 if ( ! Segment->IndexEntryArray.empty() )
1089 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
1091 Entry.StreamOffset = (ui64_t)frame_num * Segment->EditUnitByteCount;
1094 else if ( (ui64_t)frame_num >= start_pos
1095 && (ui64_t)frame_num < (start_pos + Segment->IndexDuration) )
1097 Entry = Segment->IndexEntryArray[frame_num-start_pos];
1108 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const Rational& Rate)
1112 m_BytesPerEditUnit = size;
1115 IndexTableSegment* Index = new IndexTableSegment;
1116 AddChildObject(Index);
1117 Index->EditUnitByteCount = m_BytesPerEditUnit;
1118 Index->IndexEditRate = Rate;
1123 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsVBR(IPrimerLookup* lookup, const Rational& Rate, Kumu::fpos_t offset)
1127 m_BytesPerEditUnit = 0;
1129 m_ECOffset = offset;
1134 ASDCP::MXF::OPAtomIndexFooter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
1136 if ( m_BytesPerEditUnit != 0 ) // are we CBR? that's bad
1138 DefaultLogSink().Error("Call to PushIndexEntry() failed: index is CBR\n");
1142 // do we have an available segment?
1143 if ( m_CurrentSegment == 0 )
1144 { // no, set up a new segment
1145 m_CurrentSegment = new IndexTableSegment;
1146 assert(m_CurrentSegment);
1147 AddChildObject(m_CurrentSegment);
1148 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1149 m_CurrentSegment->IndexEditRate = m_EditRate;
1150 m_CurrentSegment->IndexStartPosition = 0;
1152 else if ( m_CurrentSegment->IndexEntryArray.size() >= CBRIndexEntriesPerSegment )
1153 { // no, this one is full, start another
1154 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1155 ui64_t StartPosition = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
1157 m_CurrentSegment = new IndexTableSegment;
1158 assert(m_CurrentSegment);
1159 AddChildObject(m_CurrentSegment);
1160 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1161 m_CurrentSegment->IndexEditRate = m_EditRate;
1162 m_CurrentSegment->IndexStartPosition = StartPosition;
1165 m_CurrentSegment->IndexEntryArray.push_back(Entry);
1168 //------------------------------------------------------------------------------------------
1173 ASDCP::MXF::InterchangeObject::InitFromTLVSet(TLVReader& TLVSet)
1175 Result_t result = TLVSet.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID));
1176 if ( ASDCP_SUCCESS(result) )
1177 result = TLVSet.ReadObject(OBJ_READ_ARGS(GenerationInterchangeObject, GenerationUID));
1183 ASDCP::MXF::InterchangeObject::WriteToTLVSet(TLVWriter& TLVSet)
1185 Result_t result = TLVSet.WriteObject(OBJ_WRITE_ARGS(InterchangeObject, InstanceUID));
1186 if ( ASDCP_SUCCESS(result) )
1187 result = TLVSet.WriteObject(OBJ_WRITE_ARGS(GenerationInterchangeObject, GenerationUID));
1193 ASDCP::MXF::InterchangeObject::InitFromBuffer(const byte_t* p, ui32_t l)
1196 Result_t result = RESULT_FALSE;
1198 if ( m_Typeinfo == 0 )
1200 result = KLVPacket::InitFromBuffer(p, l);
1204 result = KLVPacket::InitFromBuffer(p, l, m_Typeinfo->ul);
1206 if ( ASDCP_SUCCESS(result) )
1208 TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup);
1209 result = InitFromTLVSet(MemRDR);
1218 ASDCP::MXF::InterchangeObject::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
1220 if ( m_Typeinfo == 0 )
1221 return RESULT_STATE;
1223 TLVWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length, m_Lookup);
1224 Result_t result = WriteToTLVSet(MemWRT);
1226 if ( ASDCP_SUCCESS(result) )
1228 ui32_t packet_length = MemWRT.Length();
1229 result = WriteKLToBuffer(Buffer, m_Typeinfo->ul, packet_length);
1231 if ( ASDCP_SUCCESS(result) )
1232 Buffer.Size(Buffer.Size() + packet_length);
1240 ASDCP::MXF::InterchangeObject::Dump(FILE* stream)
1242 char identbuf[IdentBufferLen];
1244 fputc('\n', stream);
1245 KLVPacket::Dump(stream, false);
1246 fprintf(stream, " InstanceUID = %s\n", InstanceUID.EncodeHex(identbuf, IdentBufferLen));
1247 fprintf(stream, " GenerationUID = %s\n", GenerationUID.EncodeHex(identbuf, IdentBufferLen));
1252 ASDCP::MXF::InterchangeObject::IsA(const byte_t* label)
1254 if ( m_KLLength == 0 )
1257 return ( memcmp(label, m_KeyStart, SMPTE_UL_LENGTH) == 0 );
1261 //------------------------------------------------------------------------------------------
1264 typedef std::map<ASDCP::UL, ASDCP::MXF::MXFObjectFactory_t>FactoryMap_t;
1265 typedef FactoryMap_t::iterator FLi_t;
1268 class FactoryList : public FactoryMap_t
1277 Kumu::AutoMutex BlockLock(m_Lock);
1281 FLi_t Find(const byte_t* label) {
1282 Kumu::AutoMutex BlockLock(m_Lock);
1287 Kumu::AutoMutex BlockLock(m_Lock);
1291 void Insert(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory) {
1292 Kumu::AutoMutex BlockLock(m_Lock);
1293 insert(FactoryList::value_type(label, factory));
1298 static FactoryList s_FactoryList;
1299 static Kumu::Mutex s_InitLock;
1300 static bool s_TypesInit = false;
1305 ASDCP::MXF::SetObjectFactory(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory)
1307 s_FactoryList.Insert(label, factory);
1312 ASDCP::MXF::InterchangeObject*
1313 ASDCP::MXF::CreateObject(const byte_t* label)
1318 if ( ! s_TypesInit )
1320 Kumu::AutoMutex BlockLock(s_InitLock);
1322 if ( ! s_TypesInit )
1324 MXF::Metadata_InitTypes();
1329 FLi_t i = s_FactoryList.find(label);
1331 if ( i == s_FactoryList.end() )
1332 return new InterchangeObject;