2 Copyright (c) 2005-2009, 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
286 if ( ASDCP_SUCCESS(result) )
287 result = ASDCP::MXF::Partition::InitFromBuffer(m_ValueStart, m_ValueLength);
294 ASDCP::MXF::Partition::InitFromBuffer(const byte_t* p, ui32_t l)
296 Kumu::MemIOReader MemRDR(p, l);
297 Result_t result = RESULT_KLV_CODING;
299 if ( MemRDR.ReadUi16BE(&MajorVersion) )
300 if ( MemRDR.ReadUi16BE(&MinorVersion) )
301 if ( MemRDR.ReadUi32BE(&KAGSize) )
302 if ( MemRDR.ReadUi64BE(&ThisPartition) )
303 if ( MemRDR.ReadUi64BE(&PreviousPartition) )
304 if ( MemRDR.ReadUi64BE(&FooterPartition) )
305 if ( MemRDR.ReadUi64BE(&HeaderByteCount) )
306 if ( MemRDR.ReadUi64BE(&IndexByteCount) )
307 if ( MemRDR.ReadUi32BE(&IndexSID) )
308 if ( MemRDR.ReadUi64BE(&BodyOffset) )
309 if ( MemRDR.ReadUi32BE(&BodySID) )
310 if ( OperationalPattern.Unarchive(&MemRDR) )
311 if ( EssenceContainers.Unarchive(&MemRDR) )
314 if ( ASDCP_FAILURE(result) )
315 DefaultLogSink().Error("Failed to initialize Partition\n");
322 ASDCP::MXF::Partition::WriteToFile(Kumu::FileWriter& Writer, UL& PartitionLabel)
324 ASDCP::FrameBuffer Buffer;
325 Result_t result = Buffer.Capacity(1024);
327 if ( ASDCP_SUCCESS(result) )
329 Kumu::MemIOWriter MemWRT(Buffer.Data(), Buffer.Capacity());
330 result = RESULT_KLV_CODING;
331 if ( MemWRT.WriteUi16BE(MajorVersion) )
332 if ( MemWRT.WriteUi16BE(MinorVersion) )
333 if ( MemWRT.WriteUi32BE(KAGSize) )
334 if ( MemWRT.WriteUi64BE(ThisPartition) )
335 if ( MemWRT.WriteUi64BE(PreviousPartition) )
336 if ( MemWRT.WriteUi64BE(FooterPartition) )
337 if ( MemWRT.WriteUi64BE(HeaderByteCount) )
338 if ( MemWRT.WriteUi64BE(IndexByteCount) )
339 if ( MemWRT.WriteUi32BE(IndexSID) )
340 if ( MemWRT.WriteUi64BE(BodyOffset) )
341 if ( MemWRT.WriteUi32BE(BodySID) )
342 if ( OperationalPattern.Archive(&MemWRT) )
343 if ( EssenceContainers.Archive(&MemWRT) )
345 Buffer.Size(MemWRT.Length());
350 if ( ASDCP_SUCCESS(result) )
353 result = WriteKLToFile(Writer, PartitionLabel.Value(), Buffer.Size());
355 if ( ASDCP_SUCCESS(result) )
356 result = Writer.Write(Buffer.RoData(), Buffer.Size(), &write_count);
364 ASDCP::MXF::Partition::ArchiveSize()
367 + sizeof(ui16_t) + sizeof(ui16_t)
369 + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t)
374 + sizeof(ui32_t) + sizeof(ui32_t) + ( UUIDlen * EssenceContainers.size() ) );
379 ASDCP::MXF::Partition::Dump(FILE* stream)
381 char identbuf[IdentBufferLen];
386 KLVFilePacket::Dump(stream, false);
387 fprintf(stream, " MajorVersion = %hu\n", MajorVersion);
388 fprintf(stream, " MinorVersion = %hu\n", MinorVersion);
389 fprintf(stream, " KAGSize = %u\n", KAGSize);
390 fprintf(stream, " ThisPartition = %s\n", ui64sz(ThisPartition, identbuf));
391 fprintf(stream, " PreviousPartition = %s\n", ui64sz(PreviousPartition, identbuf));
392 fprintf(stream, " FooterPartition = %s\n", ui64sz(FooterPartition, identbuf));
393 fprintf(stream, " HeaderByteCount = %s\n", ui64sz(HeaderByteCount, identbuf));
394 fprintf(stream, " IndexByteCount = %s\n", ui64sz(IndexByteCount, identbuf));
395 fprintf(stream, " IndexSID = %u\n", IndexSID);
396 fprintf(stream, " BodyOffset = %s\n", ui64sz(BodyOffset, identbuf));
397 fprintf(stream, " BodySID = %u\n", BodySID);
398 fprintf(stream, " OperationalPattern = %s\n", OperationalPattern.EncodeString(identbuf, IdentBufferLen));
399 fputs("Essence Containers:\n", stream); EssenceContainers.Dump(stream, false);
403 //------------------------------------------------------------------------------------------
406 class ASDCP::MXF::Primer::h__PrimerLookup : public std::map<UL, TagValue>
409 void InitWithBatch(ASDCP::MXF::Batch<ASDCP::MXF::Primer::LocalTagEntry>& Batch)
411 ASDCP::MXF::Batch<ASDCP::MXF::Primer::LocalTagEntry>::iterator i = Batch.begin();
413 for ( ; i != Batch.end(); i++ )
414 insert(std::map<UL, TagValue>::value_type((*i).UL, (*i).Tag));
420 ASDCP::MXF::Primer::Primer() : m_LocalTag(0xff) {}
423 ASDCP::MXF::Primer::~Primer() {}
427 ASDCP::MXF::Primer::ClearTagList()
429 LocalTagEntryBatch.clear();
430 m_Lookup = new h__PrimerLookup;
435 ASDCP::MXF::Primer::InitFromBuffer(const byte_t* p, ui32_t l)
437 Result_t result = KLVPacket::InitFromBuffer(p, l, Dict::ul(MDD_Primer));
439 if ( ASDCP_SUCCESS(result) )
441 Kumu::MemIOReader MemRDR(m_ValueStart, m_ValueLength);
442 result = LocalTagEntryBatch.Unarchive(&MemRDR) ? RESULT_OK : RESULT_KLV_CODING;
445 if ( ASDCP_SUCCESS(result) )
447 m_Lookup = new h__PrimerLookup;
448 m_Lookup->InitWithBatch(LocalTagEntryBatch);
451 if ( ASDCP_FAILURE(result) )
452 DefaultLogSink().Error("Failed to initialize Primer\n");
459 ASDCP::MXF::Primer::WriteToFile(Kumu::FileWriter& Writer)
461 ASDCP::FrameBuffer Buffer;
462 Result_t result = Buffer.Capacity(128*1024);
464 if ( ASDCP_SUCCESS(result) )
465 result = WriteToBuffer(Buffer);
467 if ( ASDCP_SUCCESS(result) )
468 result = Writer.Write(Buffer.RoData(), Buffer.Size());
475 ASDCP::MXF::Primer::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
477 ASDCP::FrameBuffer LocalTagBuffer;
478 Kumu::MemIOWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length);
479 Result_t result = LocalTagEntryBatch.Archive(&MemWRT) ? RESULT_OK : RESULT_KLV_CODING;
481 if ( ASDCP_SUCCESS(result) )
483 ui32_t packet_length = MemWRT.Length();
484 result = WriteKLToBuffer(Buffer, Dict::ul(MDD_Primer), packet_length);
486 if ( ASDCP_SUCCESS(result) )
487 Buffer.Size(Buffer.Size() + packet_length);
495 ASDCP::MXF::Primer::InsertTag(const MDDEntry& Entry, ASDCP::TagValue& Tag)
499 std::map<UL, TagValue>::iterator i = m_Lookup->find(TestUL);
501 if ( i == m_Lookup->end() )
503 if ( Entry.tag.a == 0 && Entry.tag.b == 0 )
506 Tag.b = m_LocalTag--;
514 LocalTagEntry TmpEntry;
515 TmpEntry.UL = TestUL;
518 LocalTagEntryBatch.push_back(TmpEntry);
519 m_Lookup->insert(std::map<UL, TagValue>::value_type(TmpEntry.UL, TmpEntry.Tag));
531 ASDCP::MXF::Primer::TagForKey(const ASDCP::UL& Key, ASDCP::TagValue& Tag)
534 if ( m_Lookup.empty() )
536 DefaultLogSink().Error("Primer lookup is empty\n");
540 std::map<UL, TagValue>::iterator i = m_Lookup->find(Key);
542 if ( i == m_Lookup->end() )
551 ASDCP::MXF::Primer::Dump(FILE* stream)
553 char identbuf[IdentBufferLen];
558 KLVPacket::Dump(stream, false);
559 fprintf(stream, "Primer: %u %s\n",
560 (ui32_t)LocalTagEntryBatch.size(),
561 ( LocalTagEntryBatch.size() == 1 ? "entry" : "entries" ));
563 Batch<LocalTagEntry>::iterator i = LocalTagEntryBatch.begin();
564 for ( ; i != LocalTagEntryBatch.end(); i++ )
566 const MDDEntry* Entry = Dict::FindUL((*i).UL.Value());
567 fprintf(stream, " %s %s\n", (*i).EncodeString(identbuf, IdentBufferLen), (Entry ? Entry->name : "Unknown"));
572 //------------------------------------------------------------------------------------------
577 ASDCP::MXF::Preface::InitFromTLVSet(TLVReader& TLVSet)
579 Result_t result = InterchangeObject::InitFromTLVSet(TLVSet);
580 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, LastModifiedDate));
581 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi16(OBJ_READ_ARGS(Preface, Version));
582 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi32(OBJ_READ_ARGS(Preface, ObjectModelVersion));
583 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, PrimaryPackage));
584 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, Identifications));
585 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, ContentStorage));
586 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, OperationalPattern));
587 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, EssenceContainers));
588 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, DMSchemes));
594 ASDCP::MXF::Preface::WriteToTLVSet(TLVWriter& TLVSet)
596 Result_t result = InterchangeObject::WriteToTLVSet(TLVSet);
597 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, LastModifiedDate));
598 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi16(OBJ_WRITE_ARGS(Preface, Version));
599 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi32(OBJ_WRITE_ARGS(Preface, ObjectModelVersion));
600 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, PrimaryPackage));
601 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, Identifications));
602 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, ContentStorage));
603 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, OperationalPattern));
604 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, EssenceContainers));
605 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, DMSchemes));
611 ASDCP::MXF::Preface::InitFromBuffer(const byte_t* p, ui32_t l)
613 m_Typeinfo = &Dict::Type(MDD_Preface);
614 return InterchangeObject::InitFromBuffer(p, l);
619 ASDCP::MXF::Preface::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
621 m_Typeinfo = &Dict::Type(MDD_Preface);
622 return InterchangeObject::WriteToBuffer(Buffer);
627 ASDCP::MXF::Preface::Dump(FILE* stream)
629 char identbuf[IdentBufferLen];
634 InterchangeObject::Dump(stream);
635 fprintf(stream, " %22s = %s\n", "LastModifiedDate", LastModifiedDate.EncodeString(identbuf, IdentBufferLen));
636 fprintf(stream, " %22s = %hu\n", "Version", Version);
637 fprintf(stream, " %22s = %u\n", "ObjectModelVersion", ObjectModelVersion);
638 fprintf(stream, " %22s = %s\n", "PrimaryPackage", PrimaryPackage.EncodeHex(identbuf, IdentBufferLen));
639 fprintf(stream, " %22s:\n", "Identifications"); Identifications.Dump(stream);
640 fprintf(stream, " %22s = %s\n", "ContentStorage", ContentStorage.EncodeHex(identbuf, IdentBufferLen));
641 fprintf(stream, " %22s = %s\n", "OperationalPattern", OperationalPattern.EncodeString(identbuf, IdentBufferLen));
642 fprintf(stream, " %22s:\n", "EssenceContainers"); EssenceContainers.Dump(stream);
643 fprintf(stream, " %22s:\n", "DMSchemes"); DMSchemes.Dump(stream);
646 //------------------------------------------------------------------------------------------
649 ASDCP::MXF::OPAtomHeader::OPAtomHeader() : m_Preface(0), m_HasRIP(false) {}
650 ASDCP::MXF::OPAtomHeader::~OPAtomHeader() {}
654 ASDCP::MXF::OPAtomHeader::InitFromFile(const Kumu::FileReader& Reader)
657 Result_t result = SeekToRIP(Reader);
659 if ( ASDCP_SUCCESS(result) )
661 result = m_RIP.InitFromFile(Reader);
662 ui32_t test_s = m_RIP.PairArray.size();
664 if ( ASDCP_FAILURE(result) )
666 DefaultLogSink().Error("File contains no RIP\n");
669 else if ( test_s == 0 )
671 DefaultLogSink().Error("RIP contains no Pairs.\n");
672 result = RESULT_FORMAT;
678 // OP-Atom states that there will be either two or three partitions:
679 // one closed header and one closed footer with an optional body
680 // SMPTE 429-5 files may have many partitions, see SMPTE 410M
681 DefaultLogSink().Warn("RIP count is less than 2: %u\n", test_s);
686 if ( m_RIP.PairArray.front().ByteOffset != 0 )
688 DefaultLogSink().Error("First Partition in RIP is not at offset 0.\n");
689 result = RESULT_FORMAT;
694 if ( ASDCP_SUCCESS(result) )
695 result = Reader.Seek(0);
697 if ( ASDCP_SUCCESS(result) )
698 result = Partition::InitFromFile(Reader); // test UL and OP
700 if ( ASDCP_FAILURE(result) )
703 // is it really OP-Atom?
704 UL OPAtomUL(Dict::ul(MDD_OPAtom));
705 UL InteropOPAtomUL(Dict::ul(MDD_MXFInterop_OPAtom));
707 if ( ! ( OperationalPattern == OPAtomUL || OperationalPattern == InteropOPAtomUL ) )
709 char strbuf[IdentBufferLen];
710 const MDDEntry* Entry = Dict::FindUL(OperationalPattern.Value());
712 DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n", OperationalPattern.EncodeString(strbuf, IdentBufferLen));
714 DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n", Entry->name);
717 // slurp up the remainder of the header
718 if ( HeaderByteCount < 1024 )
719 DefaultLogSink().Warn("Improbably small HeaderByteCount value: %u\n", HeaderByteCount);
721 assert (HeaderByteCount <= 0xFFFFFFFFL);
722 result = m_Buffer.Capacity((ui32_t) HeaderByteCount);
724 if ( ASDCP_SUCCESS(result) )
727 result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
729 if ( ASDCP_FAILURE(result) )
732 if ( read_count != m_Buffer.Capacity() )
734 DefaultLogSink().Error("Short read of OP-Atom header metadata; wanted %u, got %u\n",
735 m_Buffer.Capacity(), read_count);
736 return RESULT_KLV_CODING;
740 if ( ASDCP_SUCCESS(result) )
741 result = InitFromBuffer(m_Buffer.RoData(), m_Buffer.Capacity());
748 ASDCP::MXF::OPAtomHeader::InitFromPartitionBuffer(const byte_t* p, ui32_t l)
750 Result_t result = KLVPacket::InitFromBuffer(p, l);
752 if ( ASDCP_SUCCESS(result) )
753 result = Partition::InitFromBuffer(m_ValueStart, m_ValueLength); // test UL and OP
755 if ( ASDCP_SUCCESS(result) )
757 ui32_t pp_len = KLVPacket::PacketLength();
758 result = InitFromBuffer(p + pp_len, l - pp_len);
766 ASDCP::MXF::OPAtomHeader::InitFromBuffer(const byte_t* p, ui32_t l)
768 Result_t result = RESULT_OK;
769 const byte_t* end_p = p + l;
771 while ( ASDCP_SUCCESS(result) && p < end_p )
773 // parse the packets and index them by uid, discard KLVFill items
774 InterchangeObject* object = CreateObject(p);
777 object->m_Lookup = &m_Primer;
778 result = object->InitFromBuffer(p, end_p - p);
779 const byte_t* redo_p = p;
780 p += object->PacketLength();
781 // hexdump(p, object->PacketLength());
783 if ( ASDCP_SUCCESS(result) )
785 if ( object->IsA(Dict::ul(MDD_KLVFill)) )
789 else if ( object->IsA(Dict::ul(MDD_Primer)) )
792 result = m_Primer.InitFromBuffer(redo_p, end_p - redo_p);
796 m_PacketList->AddPacket(object);
798 if ( object->IsA(Dict::ul(MDD_Preface)) && m_Preface == 0 )
799 m_Preface = (Preface*)object;
804 DefaultLogSink().Error("Error initializing packet\n");
813 ASDCP::MXF::OPAtomHeader::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
815 return m_PacketList->GetMDObjectByID(ObjectID, Object);
820 ASDCP::MXF::OPAtomHeader::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
822 InterchangeObject* TmpObject;
827 return m_PacketList->GetMDObjectByType(ObjectID, Object);
832 ASDCP::MXF::OPAtomHeader::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
834 return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
838 ASDCP::MXF::Identification*
839 ASDCP::MXF::OPAtomHeader::GetIdentification()
841 InterchangeObject* Object;
843 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object)) )
844 return (Identification*)Object;
850 ASDCP::MXF::SourcePackage*
851 ASDCP::MXF::OPAtomHeader::GetSourcePackage()
853 InterchangeObject* Object;
855 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object)) )
856 return (SourcePackage*)Object;
864 ASDCP::MXF::OPAtomHeader::WriteToFile(Kumu::FileWriter& Writer, ui32_t HeaderSize)
866 if ( m_Preface == 0 )
869 if ( HeaderSize < 4096 )
871 DefaultLogSink().Error("HeaderSize %u is too small. Must be >= 4096\n", HeaderSize);
875 ASDCP::FrameBuffer HeaderBuffer;
876 HeaderByteCount = HeaderSize - ArchiveSize();
877 assert (HeaderByteCount <= 0xFFFFFFFFL);
878 Result_t result = HeaderBuffer.Capacity((ui32_t) HeaderByteCount);
879 m_Preface->m_Lookup = &m_Primer;
881 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
882 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
884 InterchangeObject* object = *pl_i;
885 object->m_Lookup = &m_Primer;
887 ASDCP::FrameBuffer WriteWrapper;
888 WriteWrapper.SetData(HeaderBuffer.Data() + HeaderBuffer.Size(),
889 HeaderBuffer.Capacity() - HeaderBuffer.Size());
890 result = object->WriteToBuffer(WriteWrapper);
891 HeaderBuffer.Size(HeaderBuffer.Size() + WriteWrapper.Size());
894 if ( ASDCP_SUCCESS(result) )
896 UL TmpUL(Dict::ul(MDD_ClosedCompleteHeader));
897 result = Partition::WriteToFile(Writer, TmpUL);
900 if ( ASDCP_SUCCESS(result) )
901 result = m_Primer.WriteToFile(Writer);
903 if ( ASDCP_SUCCESS(result) )
906 Writer.Write(HeaderBuffer.RoData(), HeaderBuffer.Size(), &write_count);
907 assert(write_count == HeaderBuffer.Size());
911 if ( ASDCP_SUCCESS(result) )
913 Kumu::fpos_t pos = Writer.Tell();
915 if ( pos > (Kumu::fpos_t)HeaderByteCount )
917 char intbuf[IntBufferLen];
918 DefaultLogSink().Error("Header size %s exceeds specified value %u\n",
924 ASDCP::FrameBuffer NilBuf;
925 ui32_t klv_fill_length = HeaderSize - (ui32_t)pos;
927 if ( klv_fill_length < kl_length )
929 DefaultLogSink().Error("Remaining region too small for KLV Fill header\n");
933 klv_fill_length -= kl_length;
934 result = WriteKLToFile(Writer, Dict::ul(MDD_KLVFill), klv_fill_length);
936 if ( ASDCP_SUCCESS(result) )
937 result = NilBuf.Capacity(klv_fill_length);
939 if ( ASDCP_SUCCESS(result) )
941 memset(NilBuf.Data(), 0, klv_fill_length);
943 Writer.Write(NilBuf.RoData(), klv_fill_length, &write_count);
944 assert(write_count == klv_fill_length);
953 ASDCP::MXF::OPAtomHeader::Dump(FILE* stream)
958 Partition::Dump(stream);
959 m_Primer.Dump(stream);
961 if ( m_Preface == 0 )
962 fputs("No Preface loaded\n", stream);
964 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
965 for ( ; i != m_PacketList->m_List.end(); i++ )
969 //------------------------------------------------------------------------------------------
972 ASDCP::MXF::OPAtomIndexFooter::OPAtomIndexFooter() :
973 m_CurrentSegment(0), m_BytesPerEditUnit(0), m_BodySID(0),
974 m_ECOffset(0), m_Lookup(0)
980 ASDCP::MXF::OPAtomIndexFooter::~OPAtomIndexFooter() {}
984 ASDCP::MXF::OPAtomIndexFooter::InitFromFile(const Kumu::FileReader& Reader)
986 Result_t result = Partition::InitFromFile(Reader); // test UL and OP
988 // slurp up the remainder of the footer
991 if ( ASDCP_SUCCESS(result) )
993 assert (IndexByteCount <= 0xFFFFFFFFL);
994 result = m_Buffer.Capacity((ui32_t) IndexByteCount);
997 if ( ASDCP_SUCCESS(result) )
998 result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
1000 if ( ASDCP_SUCCESS(result) && read_count != m_Buffer.Capacity() )
1002 DefaultLogSink().Error("Short read of footer partition: got %u, expecting %u\n",
1003 read_count, m_Buffer.Capacity());
1007 if ( ASDCP_SUCCESS(result) )
1008 result = InitFromBuffer(m_Buffer.RoData(), m_Buffer.Capacity());
1015 ASDCP::MXF::OPAtomIndexFooter::InitFromPartitionBuffer(const byte_t* p, ui32_t l)
1017 Result_t result = KLVPacket::InitFromBuffer(p, l);
1019 if ( ASDCP_SUCCESS(result) )
1020 result = Partition::InitFromBuffer(m_ValueStart, m_ValueLength); // test UL and OP
1022 if ( ASDCP_SUCCESS(result) )
1024 ui32_t pp_len = KLVPacket::PacketLength();
1025 result = InitFromBuffer(p + pp_len, l - pp_len);
1033 ASDCP::MXF::OPAtomIndexFooter::InitFromBuffer(const byte_t* p, ui32_t l)
1035 Result_t result = RESULT_OK;
1036 const byte_t* end_p = p + l;
1038 while ( ASDCP_SUCCESS(result) && p < end_p )
1040 // parse the packets and index them by uid, discard KLVFill items
1041 InterchangeObject* object = CreateObject(p);
1044 object->m_Lookup = m_Lookup;
1045 result = object->InitFromBuffer(p, end_p - p);
1046 p += object->PacketLength();
1048 if ( ASDCP_SUCCESS(result) )
1050 m_PacketList->AddPacket(object);
1054 DefaultLogSink().Error("Error initializing packet\n");
1059 if ( ASDCP_FAILURE(result) )
1060 DefaultLogSink().Error("Failed to initialize OPAtomIndexFooter\n");
1067 ASDCP::MXF::OPAtomIndexFooter::WriteToFile(Kumu::FileWriter& Writer, ui64_t duration)
1069 ASDCP::FrameBuffer FooterBuffer;
1070 ui32_t footer_size = m_PacketList->m_List.size() * MaxIndexSegmentSize; // segment-count * max-segment-size
1071 Result_t result = FooterBuffer.Capacity(footer_size);
1072 ui32_t iseg_count = 0;
1074 if ( m_CurrentSegment != 0 )
1076 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1077 m_CurrentSegment = 0;
1080 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
1081 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
1083 if ( (*pl_i)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1086 IndexTableSegment* Segment = (IndexTableSegment*)(*pl_i);
1088 if ( m_BytesPerEditUnit != 0 )
1090 if ( iseg_count != 1 )
1091 return RESULT_STATE;
1093 Segment->IndexDuration = duration;
1097 InterchangeObject* object = *pl_i;
1098 object->m_Lookup = m_Lookup;
1100 ASDCP::FrameBuffer WriteWrapper;
1101 WriteWrapper.SetData(FooterBuffer.Data() + FooterBuffer.Size(),
1102 FooterBuffer.Capacity() - FooterBuffer.Size());
1103 result = object->WriteToBuffer(WriteWrapper);
1104 FooterBuffer.Size(FooterBuffer.Size() + WriteWrapper.Size());
1107 if ( ASDCP_SUCCESS(result) )
1109 IndexByteCount = FooterBuffer.Size();
1110 UL FooterUL(Dict::ul(MDD_CompleteFooter));
1111 result = Partition::WriteToFile(Writer, FooterUL);
1114 if ( ASDCP_SUCCESS(result) )
1116 ui32_t write_count = 0;
1117 result = Writer.Write(FooterBuffer.RoData(), FooterBuffer.Size(), &write_count);
1118 assert(write_count == FooterBuffer.Size());
1126 ASDCP::MXF::OPAtomIndexFooter::Dump(FILE* stream)
1131 Partition::Dump(stream);
1133 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
1134 for ( ; i != m_PacketList->m_List.end(); i++ )
1140 ASDCP::MXF::OPAtomIndexFooter::Lookup(ui32_t frame_num, IndexTableSegment::IndexEntry& Entry) const
1142 std::list<InterchangeObject*>::iterator li;
1143 for ( li = m_PacketList->m_List.begin(); li != m_PacketList->m_List.end(); li++ )
1145 if ( (*li)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1147 IndexTableSegment* Segment = (IndexTableSegment*)(*li);
1148 ui64_t start_pos = Segment->IndexStartPosition;
1150 if ( Segment->EditUnitByteCount > 0 )
1152 if ( m_PacketList->m_List.size() > 1 )
1153 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
1155 if ( ! Segment->IndexEntryArray.empty() )
1156 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
1158 Entry.StreamOffset = (ui64_t)frame_num * Segment->EditUnitByteCount;
1161 else if ( (ui64_t)frame_num >= start_pos
1162 && (ui64_t)frame_num < (start_pos + Segment->IndexDuration) )
1164 ui64_t tmp = frame_num - start_pos;
1165 assert(tmp <= 0xFFFFFFFFL);
1166 Entry = Segment->IndexEntryArray[(ui32_t) tmp];
1177 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const Rational& Rate)
1181 m_BytesPerEditUnit = size;
1184 IndexTableSegment* Index = new IndexTableSegment;
1185 AddChildObject(Index);
1186 Index->EditUnitByteCount = m_BytesPerEditUnit;
1187 Index->IndexEditRate = Rate;
1192 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsVBR(IPrimerLookup* lookup, const Rational& Rate, Kumu::fpos_t offset)
1196 m_BytesPerEditUnit = 0;
1198 m_ECOffset = offset;
1203 ASDCP::MXF::OPAtomIndexFooter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
1205 if ( m_BytesPerEditUnit != 0 ) // are we CBR? that's bad
1207 DefaultLogSink().Error("Call to PushIndexEntry() failed: index is CBR\n");
1211 // do we have an available segment?
1212 if ( m_CurrentSegment == 0 )
1213 { // no, set up a new segment
1214 m_CurrentSegment = new IndexTableSegment;
1215 assert(m_CurrentSegment);
1216 AddChildObject(m_CurrentSegment);
1217 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1218 m_CurrentSegment->IndexEditRate = m_EditRate;
1219 m_CurrentSegment->IndexStartPosition = 0;
1221 else if ( m_CurrentSegment->IndexEntryArray.size() >= CBRIndexEntriesPerSegment )
1222 { // no, this one is full, start another
1223 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1224 ui64_t StartPosition = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
1226 m_CurrentSegment = new IndexTableSegment;
1227 assert(m_CurrentSegment);
1228 AddChildObject(m_CurrentSegment);
1229 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1230 m_CurrentSegment->IndexEditRate = m_EditRate;
1231 m_CurrentSegment->IndexStartPosition = StartPosition;
1234 m_CurrentSegment->IndexEntryArray.push_back(Entry);
1237 //------------------------------------------------------------------------------------------
1242 ASDCP::MXF::InterchangeObject::InitFromTLVSet(TLVReader& TLVSet)
1244 Result_t result = TLVSet.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID));
1245 if ( ASDCP_SUCCESS(result) )
1246 result = TLVSet.ReadObject(OBJ_READ_ARGS(GenerationInterchangeObject, GenerationUID));
1252 ASDCP::MXF::InterchangeObject::WriteToTLVSet(TLVWriter& TLVSet)
1254 Result_t result = TLVSet.WriteObject(OBJ_WRITE_ARGS(InterchangeObject, InstanceUID));
1255 if ( ASDCP_SUCCESS(result) )
1256 result = TLVSet.WriteObject(OBJ_WRITE_ARGS(GenerationInterchangeObject, GenerationUID));
1262 ASDCP::MXF::InterchangeObject::InitFromBuffer(const byte_t* p, ui32_t l)
1265 Result_t result = RESULT_FALSE;
1267 if ( m_Typeinfo == 0 )
1269 result = KLVPacket::InitFromBuffer(p, l);
1273 result = KLVPacket::InitFromBuffer(p, l, m_Typeinfo->ul);
1275 if ( ASDCP_SUCCESS(result) )
1277 TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup);
1278 result = InitFromTLVSet(MemRDR);
1287 ASDCP::MXF::InterchangeObject::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
1289 if ( m_Typeinfo == 0 )
1290 return RESULT_STATE;
1292 TLVWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length, m_Lookup);
1293 Result_t result = WriteToTLVSet(MemWRT);
1295 if ( ASDCP_SUCCESS(result) )
1297 ui32_t packet_length = MemWRT.Length();
1298 result = WriteKLToBuffer(Buffer, m_Typeinfo->ul, packet_length);
1300 if ( ASDCP_SUCCESS(result) )
1301 Buffer.Size(Buffer.Size() + packet_length);
1309 ASDCP::MXF::InterchangeObject::Dump(FILE* stream)
1311 char identbuf[IdentBufferLen];
1313 fputc('\n', stream);
1314 KLVPacket::Dump(stream, false);
1315 fprintf(stream, " InstanceUID = %s\n", InstanceUID.EncodeHex(identbuf, IdentBufferLen));
1316 fprintf(stream, " GenerationUID = %s\n", GenerationUID.EncodeHex(identbuf, IdentBufferLen));
1321 ASDCP::MXF::InterchangeObject::IsA(const byte_t* label)
1323 if ( m_KLLength == 0 )
1326 return ( memcmp(label, m_KeyStart, SMPTE_UL_LENGTH) == 0 );
1330 //------------------------------------------------------------------------------------------
1333 typedef std::map<ASDCP::UL, ASDCP::MXF::MXFObjectFactory_t>FactoryMap_t;
1334 typedef FactoryMap_t::iterator FLi_t;
1337 class FactoryList : public FactoryMap_t
1346 Kumu::AutoMutex BlockLock(m_Lock);
1350 FLi_t Find(const byte_t* label) {
1351 Kumu::AutoMutex BlockLock(m_Lock);
1356 Kumu::AutoMutex BlockLock(m_Lock);
1360 void Insert(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory) {
1361 Kumu::AutoMutex BlockLock(m_Lock);
1362 insert(FactoryList::value_type(label, factory));
1367 static FactoryList s_FactoryList;
1368 static Kumu::Mutex s_InitLock;
1369 static bool s_TypesInit = false;
1374 ASDCP::MXF::SetObjectFactory(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory)
1376 s_FactoryList.Insert(label, factory);
1381 ASDCP::MXF::InterchangeObject*
1382 ASDCP::MXF::CreateObject(const byte_t* label)
1387 if ( ! s_TypesInit )
1389 Kumu::AutoMutex BlockLock(s_InitLock);
1391 if ( ! s_TypesInit )
1393 MXF::Metadata_InitTypes();
1398 FLi_t i = s_FactoryList.find(label);
1400 if ( i == s_FactoryList.end() )
1401 return new InterchangeObject;