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;
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 // SMPTE 429-5 files may have many partitions, see SMPTE 410M
675 DefaultLogSink().Warn("RIP count is less than 2: %u\n", test_s);
680 if ( m_RIP.PairArray.front().ByteOffset != 0 )
682 DefaultLogSink().Error("First Partition in RIP is not at offset 0.\n");
683 result = RESULT_FORMAT;
688 if ( ASDCP_SUCCESS(result) )
689 result = Reader.Seek(0);
691 if ( ASDCP_SUCCESS(result) )
692 result = Partition::InitFromFile(Reader); // test UL and OP
694 if ( ASDCP_FAILURE(result) )
697 // is it really OP-Atom?
698 UL OPAtomUL(Dict::ul(MDD_OPAtom));
699 UL InteropOPAtomUL(Dict::ul(MDD_MXFInterop_OPAtom));
701 if ( ! ( OperationalPattern == OPAtomUL || OperationalPattern == InteropOPAtomUL ) )
703 char strbuf[IdentBufferLen];
704 const MDDEntry* Entry = Dict::FindUL(OperationalPattern.Value());
706 DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n", OperationalPattern.EncodeString(strbuf, IdentBufferLen));
708 DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n", Entry->name);
711 // slurp up the remainder of the header
712 if ( HeaderByteCount < 1024 )
713 DefaultLogSink().Warn("Improbably small HeaderByteCount value: %u\n", HeaderByteCount);
715 assert (HeaderByteCount <= 0xFFFFFFFFL);
716 result = m_Buffer.Capacity((ui32_t) HeaderByteCount);
718 if ( ASDCP_SUCCESS(result) )
721 result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
723 if ( ASDCP_FAILURE(result) )
726 if ( read_count != m_Buffer.Capacity() )
728 DefaultLogSink().Error("Short read of OP-Atom header metadata; wanted %u, got %u\n",
729 m_Buffer.Capacity(), read_count);
730 return RESULT_KLV_CODING;
734 const byte_t* p = m_Buffer.RoData();
735 const byte_t* end_p = p + m_Buffer.Capacity();
737 while ( ASDCP_SUCCESS(result) && p < end_p )
739 // parse the packets and index them by uid, discard KLVFill items
740 InterchangeObject* object = CreateObject(p);
743 object->m_Lookup = &m_Primer;
744 result = object->InitFromBuffer(p, end_p - p);
745 const byte_t* redo_p = p;
746 p += object->PacketLength();
747 // hexdump(p, object->PacketLength());
749 if ( ASDCP_SUCCESS(result) )
751 if ( object->IsA(Dict::ul(MDD_KLVFill)) )
755 else if ( object->IsA(Dict::ul(MDD_Primer)) )
758 result = m_Primer.InitFromBuffer(redo_p, end_p - redo_p);
762 m_PacketList->AddPacket(object);
764 if ( object->IsA(Dict::ul(MDD_Preface)) )
766 assert(m_Preface == 0);
767 m_Preface = (Preface*)object;
773 DefaultLogSink().Error("Error initializing packet\n");
782 ASDCP::MXF::OPAtomHeader::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
784 return m_PacketList->GetMDObjectByID(ObjectID, Object);
789 ASDCP::MXF::OPAtomHeader::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
791 InterchangeObject* TmpObject;
796 return m_PacketList->GetMDObjectByType(ObjectID, Object);
801 ASDCP::MXF::OPAtomHeader::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
803 return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
807 ASDCP::MXF::Identification*
808 ASDCP::MXF::OPAtomHeader::GetIdentification()
810 InterchangeObject* Object;
812 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object)) )
813 return (Identification*)Object;
819 ASDCP::MXF::SourcePackage*
820 ASDCP::MXF::OPAtomHeader::GetSourcePackage()
822 InterchangeObject* Object;
824 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object)) )
825 return (SourcePackage*)Object;
833 ASDCP::MXF::OPAtomHeader::WriteToFile(Kumu::FileWriter& Writer, ui32_t HeaderSize)
835 if ( m_Preface == 0 )
838 if ( HeaderSize < 4096 )
840 DefaultLogSink().Error("HeaderSize %u is too small. Must be >= 4096\n", HeaderSize);
844 ASDCP::FrameBuffer HeaderBuffer;
845 HeaderByteCount = HeaderSize - ArchiveSize();
846 assert (HeaderByteCount <= 0xFFFFFFFFL);
847 Result_t result = HeaderBuffer.Capacity((ui32_t) HeaderByteCount);
848 m_Preface->m_Lookup = &m_Primer;
850 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
851 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
853 InterchangeObject* object = *pl_i;
854 object->m_Lookup = &m_Primer;
856 ASDCP::FrameBuffer WriteWrapper;
857 WriteWrapper.SetData(HeaderBuffer.Data() + HeaderBuffer.Size(),
858 HeaderBuffer.Capacity() - HeaderBuffer.Size());
859 result = object->WriteToBuffer(WriteWrapper);
860 HeaderBuffer.Size(HeaderBuffer.Size() + WriteWrapper.Size());
863 if ( ASDCP_SUCCESS(result) )
865 UL TmpUL(Dict::ul(MDD_ClosedCompleteHeader));
866 result = Partition::WriteToFile(Writer, TmpUL);
869 if ( ASDCP_SUCCESS(result) )
870 result = m_Primer.WriteToFile(Writer);
872 if ( ASDCP_SUCCESS(result) )
875 Writer.Write(HeaderBuffer.RoData(), HeaderBuffer.Size(), &write_count);
876 assert(write_count == HeaderBuffer.Size());
880 if ( ASDCP_SUCCESS(result) )
882 Kumu::fpos_t pos = Writer.Tell();
884 if ( pos > (Kumu::fpos_t)HeaderByteCount )
886 char intbuf[IntBufferLen];
887 DefaultLogSink().Error("Header size %s exceeds specified value %u\n",
893 ASDCP::FrameBuffer NilBuf;
894 ui32_t klv_fill_length = HeaderSize - (ui32_t)pos;
896 if ( klv_fill_length < kl_length )
898 DefaultLogSink().Error("Remaining region too small for KLV Fill header\n");
902 klv_fill_length -= kl_length;
903 result = WriteKLToFile(Writer, Dict::ul(MDD_KLVFill), klv_fill_length);
905 if ( ASDCP_SUCCESS(result) )
906 result = NilBuf.Capacity(klv_fill_length);
908 if ( ASDCP_SUCCESS(result) )
910 memset(NilBuf.Data(), 0, klv_fill_length);
912 Writer.Write(NilBuf.RoData(), klv_fill_length, &write_count);
913 assert(write_count == klv_fill_length);
922 ASDCP::MXF::OPAtomHeader::Dump(FILE* stream)
927 Partition::Dump(stream);
928 m_Primer.Dump(stream);
930 if ( m_Preface == 0 )
931 fputs("No Preface loaded\n", stream);
933 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
934 for ( ; i != m_PacketList->m_List.end(); i++ )
938 //------------------------------------------------------------------------------------------
941 ASDCP::MXF::OPAtomIndexFooter::OPAtomIndexFooter() :
942 m_CurrentSegment(0), m_BytesPerEditUnit(0), m_BodySID(0),
943 m_ECOffset(0), m_Lookup(0)
949 ASDCP::MXF::OPAtomIndexFooter::~OPAtomIndexFooter() {}
953 ASDCP::MXF::OPAtomIndexFooter::InitFromFile(const Kumu::FileReader& Reader)
955 Result_t result = Partition::InitFromFile(Reader); // test UL and OP
957 // slurp up the remainder of the footer
960 if ( ASDCP_SUCCESS(result) )
962 assert (IndexByteCount <= 0xFFFFFFFFL);
963 result = m_Buffer.Capacity((ui32_t) IndexByteCount);
966 if ( ASDCP_SUCCESS(result) )
967 result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
969 if ( ASDCP_SUCCESS(result) && read_count != m_Buffer.Capacity() )
971 DefaultLogSink().Error("Short read of footer partition: got %u, expecting %u\n",
972 read_count, m_Buffer.Capacity());
976 const byte_t* p = m_Buffer.RoData();
977 const byte_t* end_p = p + m_Buffer.Capacity();
979 while ( ASDCP_SUCCESS(result) && p < end_p )
981 // parse the packets and index them by uid, discard KLVFill items
982 InterchangeObject* object = CreateObject(p);
985 object->m_Lookup = m_Lookup;
986 result = object->InitFromBuffer(p, end_p - p);
987 p += object->PacketLength();
989 if ( ASDCP_SUCCESS(result) )
991 m_PacketList->AddPacket(object);
995 DefaultLogSink().Error("Error initializing packet\n");
1000 if ( ASDCP_FAILURE(result) )
1001 DefaultLogSink().Error("Failed to initialize OPAtomIndexFooter\n");
1008 ASDCP::MXF::OPAtomIndexFooter::WriteToFile(Kumu::FileWriter& Writer, ui64_t duration)
1010 ASDCP::FrameBuffer FooterBuffer;
1011 ui32_t footer_size = m_PacketList->m_List.size() * MaxIndexSegmentSize; // segment-count * max-segment-size
1012 Result_t result = FooterBuffer.Capacity(footer_size);
1013 ui32_t iseg_count = 0;
1015 if ( m_CurrentSegment != 0 )
1017 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1018 m_CurrentSegment = 0;
1021 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
1022 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
1024 if ( (*pl_i)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1027 IndexTableSegment* Segment = (IndexTableSegment*)(*pl_i);
1029 if ( m_BytesPerEditUnit != 0 )
1031 if ( iseg_count != 1 )
1032 return RESULT_STATE;
1034 Segment->IndexDuration = duration;
1038 InterchangeObject* object = *pl_i;
1039 object->m_Lookup = m_Lookup;
1041 ASDCP::FrameBuffer WriteWrapper;
1042 WriteWrapper.SetData(FooterBuffer.Data() + FooterBuffer.Size(),
1043 FooterBuffer.Capacity() - FooterBuffer.Size());
1044 result = object->WriteToBuffer(WriteWrapper);
1045 FooterBuffer.Size(FooterBuffer.Size() + WriteWrapper.Size());
1048 if ( ASDCP_SUCCESS(result) )
1050 IndexByteCount = FooterBuffer.Size();
1051 UL FooterUL(Dict::ul(MDD_CompleteFooter));
1052 result = Partition::WriteToFile(Writer, FooterUL);
1055 if ( ASDCP_SUCCESS(result) )
1057 ui32_t write_count = 0;
1058 result = Writer.Write(FooterBuffer.RoData(), FooterBuffer.Size(), &write_count);
1059 assert(write_count == FooterBuffer.Size());
1067 ASDCP::MXF::OPAtomIndexFooter::Dump(FILE* stream)
1072 Partition::Dump(stream);
1074 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
1075 for ( ; i != m_PacketList->m_List.end(); i++ )
1081 ASDCP::MXF::OPAtomIndexFooter::Lookup(ui32_t frame_num, IndexTableSegment::IndexEntry& Entry) const
1083 std::list<InterchangeObject*>::iterator li;
1084 for ( li = m_PacketList->m_List.begin(); li != m_PacketList->m_List.end(); li++ )
1086 if ( (*li)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1088 IndexTableSegment* Segment = (IndexTableSegment*)(*li);
1089 ui64_t start_pos = Segment->IndexStartPosition;
1091 if ( Segment->EditUnitByteCount > 0 )
1093 if ( m_PacketList->m_List.size() > 1 )
1094 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
1096 if ( ! Segment->IndexEntryArray.empty() )
1097 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
1099 Entry.StreamOffset = (ui64_t)frame_num * Segment->EditUnitByteCount;
1102 else if ( (ui64_t)frame_num >= start_pos
1103 && (ui64_t)frame_num < (start_pos + Segment->IndexDuration) )
1105 ui64_t tmp = frame_num - start_pos;
1106 assert(tmp <= 0xFFFFFFFFL);
1107 Entry = Segment->IndexEntryArray[(ui32_t) tmp];
1118 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const Rational& Rate)
1122 m_BytesPerEditUnit = size;
1125 IndexTableSegment* Index = new IndexTableSegment;
1126 AddChildObject(Index);
1127 Index->EditUnitByteCount = m_BytesPerEditUnit;
1128 Index->IndexEditRate = Rate;
1133 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsVBR(IPrimerLookup* lookup, const Rational& Rate, Kumu::fpos_t offset)
1137 m_BytesPerEditUnit = 0;
1139 m_ECOffset = offset;
1144 ASDCP::MXF::OPAtomIndexFooter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
1146 if ( m_BytesPerEditUnit != 0 ) // are we CBR? that's bad
1148 DefaultLogSink().Error("Call to PushIndexEntry() failed: index is CBR\n");
1152 // do we have an available segment?
1153 if ( m_CurrentSegment == 0 )
1154 { // no, set up a new segment
1155 m_CurrentSegment = new IndexTableSegment;
1156 assert(m_CurrentSegment);
1157 AddChildObject(m_CurrentSegment);
1158 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1159 m_CurrentSegment->IndexEditRate = m_EditRate;
1160 m_CurrentSegment->IndexStartPosition = 0;
1162 else if ( m_CurrentSegment->IndexEntryArray.size() >= CBRIndexEntriesPerSegment )
1163 { // no, this one is full, start another
1164 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1165 ui64_t StartPosition = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
1167 m_CurrentSegment = new IndexTableSegment;
1168 assert(m_CurrentSegment);
1169 AddChildObject(m_CurrentSegment);
1170 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1171 m_CurrentSegment->IndexEditRate = m_EditRate;
1172 m_CurrentSegment->IndexStartPosition = StartPosition;
1175 m_CurrentSegment->IndexEntryArray.push_back(Entry);
1178 //------------------------------------------------------------------------------------------
1183 ASDCP::MXF::InterchangeObject::InitFromTLVSet(TLVReader& TLVSet)
1185 Result_t result = TLVSet.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID));
1186 if ( ASDCP_SUCCESS(result) )
1187 result = TLVSet.ReadObject(OBJ_READ_ARGS(GenerationInterchangeObject, GenerationUID));
1193 ASDCP::MXF::InterchangeObject::WriteToTLVSet(TLVWriter& TLVSet)
1195 Result_t result = TLVSet.WriteObject(OBJ_WRITE_ARGS(InterchangeObject, InstanceUID));
1196 if ( ASDCP_SUCCESS(result) )
1197 result = TLVSet.WriteObject(OBJ_WRITE_ARGS(GenerationInterchangeObject, GenerationUID));
1203 ASDCP::MXF::InterchangeObject::InitFromBuffer(const byte_t* p, ui32_t l)
1206 Result_t result = RESULT_FALSE;
1208 if ( m_Typeinfo == 0 )
1210 result = KLVPacket::InitFromBuffer(p, l);
1214 result = KLVPacket::InitFromBuffer(p, l, m_Typeinfo->ul);
1216 if ( ASDCP_SUCCESS(result) )
1218 TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup);
1219 result = InitFromTLVSet(MemRDR);
1228 ASDCP::MXF::InterchangeObject::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
1230 if ( m_Typeinfo == 0 )
1231 return RESULT_STATE;
1233 TLVWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length, m_Lookup);
1234 Result_t result = WriteToTLVSet(MemWRT);
1236 if ( ASDCP_SUCCESS(result) )
1238 ui32_t packet_length = MemWRT.Length();
1239 result = WriteKLToBuffer(Buffer, m_Typeinfo->ul, packet_length);
1241 if ( ASDCP_SUCCESS(result) )
1242 Buffer.Size(Buffer.Size() + packet_length);
1250 ASDCP::MXF::InterchangeObject::Dump(FILE* stream)
1252 char identbuf[IdentBufferLen];
1254 fputc('\n', stream);
1255 KLVPacket::Dump(stream, false);
1256 fprintf(stream, " InstanceUID = %s\n", InstanceUID.EncodeHex(identbuf, IdentBufferLen));
1257 fprintf(stream, " GenerationUID = %s\n", GenerationUID.EncodeHex(identbuf, IdentBufferLen));
1262 ASDCP::MXF::InterchangeObject::IsA(const byte_t* label)
1264 if ( m_KLLength == 0 )
1267 return ( memcmp(label, m_KeyStart, SMPTE_UL_LENGTH) == 0 );
1271 //------------------------------------------------------------------------------------------
1274 typedef std::map<ASDCP::UL, ASDCP::MXF::MXFObjectFactory_t>FactoryMap_t;
1275 typedef FactoryMap_t::iterator FLi_t;
1278 class FactoryList : public FactoryMap_t
1287 Kumu::AutoMutex BlockLock(m_Lock);
1291 FLi_t Find(const byte_t* label) {
1292 Kumu::AutoMutex BlockLock(m_Lock);
1297 Kumu::AutoMutex BlockLock(m_Lock);
1301 void Insert(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory) {
1302 Kumu::AutoMutex BlockLock(m_Lock);
1303 insert(FactoryList::value_type(label, factory));
1308 static FactoryList s_FactoryList;
1309 static Kumu::Mutex s_InitLock;
1310 static bool s_TypesInit = false;
1315 ASDCP::MXF::SetObjectFactory(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory)
1317 s_FactoryList.Insert(label, factory);
1322 ASDCP::MXF::InterchangeObject*
1323 ASDCP::MXF::CreateObject(const byte_t* label)
1328 if ( ! s_TypesInit )
1330 Kumu::AutoMutex BlockLock(s_InitLock);
1332 if ( ! s_TypesInit )
1334 MXF::Metadata_InitTypes();
1339 FLi_t i = s_FactoryList.find(label);
1341 if ( i == s_FactoryList.end() )
1342 return new InterchangeObject;