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.
34 using Kumu::DefaultLogSink;
36 // index segments must be < 64K
37 // NOTE: this value may too high if advanced index entry elements are used.
38 const ui32_t CBRIndexEntriesPerSegment = 5000;
40 //------------------------------------------------------------------------------------------
43 const ui32_t kl_length = ASDCP::SMPTE_UL_LENGTH + ASDCP::MXF_BER_LENGTH;
47 ASDCP::MXF::SeekToRIP(const Kumu::FileReader& Reader)
51 // go to the end - 4 bytes
52 Result_t result = Reader.Seek(0, Kumu::SP_END);
54 if ( ASDCP_SUCCESS(result) )
55 result = Reader.Tell(&end_pos);
57 if ( ASDCP_SUCCESS(result)
58 && end_pos < (SMPTE_UL_LENGTH+MXF_BER_LENGTH) )
59 result = RESULT_FAIL; // File is smaller than an empty packet!
61 if ( ASDCP_SUCCESS(result) )
62 result = Reader.Seek(end_pos - 4);
64 // get the ui32_t RIP length
66 byte_t intbuf[MXF_BER_LENGTH];
69 if ( ASDCP_SUCCESS(result) )
71 result = Reader.Read(intbuf, MXF_BER_LENGTH, &read_count);
73 if ( ASDCP_SUCCESS(result) && read_count != 4 )
77 if ( ASDCP_SUCCESS(result) )
79 rip_size = KM_i32_BE(Kumu::cp2i<ui32_t>(intbuf));
81 if ( rip_size > end_pos ) // RIP can't be bigger than the file
85 // reposition to start of RIP
86 if ( ASDCP_SUCCESS(result) )
87 result = Reader.Seek(end_pos - rip_size);
94 ASDCP::MXF::RIP::InitFromFile(const Kumu::FileReader& Reader)
96 Result_t result = KLVFilePacket::InitFromFile(Reader, Dict::ul(MDD_RandomIndexMetadata));
98 if ( ASDCP_SUCCESS(result) )
100 Kumu::MemIOReader MemRDR(m_ValueStart, m_ValueLength - 4);
101 result = PairArray.Unarchive(&MemRDR) ? RESULT_OK : RESULT_KLV_CODING;
104 if ( ASDCP_FAILURE(result) )
105 DefaultLogSink().Error("Failed to initialize RIP\n");
112 ASDCP::MXF::RIP::WriteToFile(Kumu::FileWriter& Writer)
114 ASDCP::FrameBuffer Buffer;
115 ui32_t RIPSize = ( PairArray.size() * (sizeof(ui32_t) + sizeof(ui64_t)) ) + 4;
116 Result_t result = Buffer.Capacity(RIPSize);
118 if ( ASDCP_SUCCESS(result) )
119 result = WriteKLToFile(Writer, Dict::ul(MDD_RandomIndexMetadata), RIPSize);
121 if ( ASDCP_SUCCESS(result) )
123 result = RESULT_KLV_CODING;
125 Kumu::MemIOWriter MemWRT(Buffer.Data(), Buffer.Capacity());
126 if ( PairArray.Archive(&MemWRT) )
127 if ( MemWRT.WriteUi32BE(RIPSize + 20) )
129 Buffer.Size(MemWRT.Length());
134 if ( ASDCP_SUCCESS(result) )
135 result = Writer.Write(Buffer.RoData(), Buffer.Size());
142 ASDCP::MXF::RIP::Dump(FILE* stream)
147 KLVFilePacket::Dump(stream, false);
148 PairArray.Dump(stream, false);
150 fputs("==========================================================================\n", stream);
153 //------------------------------------------------------------------------------------------
157 class ASDCP::MXF::Partition::h__PacketList
160 std::list<InterchangeObject*> m_List;
161 std::map<UUID, InterchangeObject*> m_Map;
164 while ( ! m_List.empty() )
166 delete m_List.back();
172 void AddPacket(InterchangeObject* ThePacket)
175 m_Map.insert(std::map<UUID, InterchangeObject*>::value_type(ThePacket->InstanceUID, ThePacket));
176 m_List.push_back(ThePacket);
180 Result_t GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
182 ASDCP_TEST_NULL(ObjectID);
183 ASDCP_TEST_NULL(Object);
184 std::list<InterchangeObject*>::iterator li;
187 for ( li = m_List.begin(); li != m_List.end(); li++ )
189 if ( (*li)->HasUL(ObjectID) )
200 //------------------------------------------------------------------------------------------
204 ASDCP::MXF::Partition::Partition() :
205 MajorVersion(1), MinorVersion(2),
206 KAGSize(1), ThisPartition(0), PreviousPartition(0),
207 FooterPartition(0), HeaderByteCount(0), IndexByteCount(0), IndexSID(0),
208 BodyOffset(0), BodySID(0)
210 m_PacketList = new h__PacketList;
213 ASDCP::MXF::Partition::~Partition()
219 ASDCP::MXF::Partition::AddChildObject(InterchangeObject* Object)
223 if ( ! Object->InstanceUID.HasValue() )
224 GenRandomValue(Object->InstanceUID);
226 m_PacketList->AddPacket(Object);
231 ASDCP::MXF::Partition::InitFromFile(const Kumu::FileReader& Reader)
233 Result_t result = KLVFilePacket::InitFromFile(Reader);
235 // could be one of several values
237 if ( ASDCP_SUCCESS(result) )
239 Kumu::MemIOReader MemRDR(m_ValueStart, m_ValueLength);
240 result = RESULT_KLV_CODING;
242 if ( MemRDR.ReadUi16BE(&MajorVersion) )
243 if ( MemRDR.ReadUi16BE(&MinorVersion) )
244 if ( MemRDR.ReadUi32BE(&KAGSize) )
245 if ( MemRDR.ReadUi64BE(&ThisPartition) )
246 if ( MemRDR.ReadUi64BE(&PreviousPartition) )
247 if ( MemRDR.ReadUi64BE(&FooterPartition) )
248 if ( MemRDR.ReadUi64BE(&HeaderByteCount) )
249 if ( MemRDR.ReadUi64BE(&IndexByteCount) )
250 if ( MemRDR.ReadUi32BE(&IndexSID) )
251 if ( MemRDR.ReadUi64BE(&BodyOffset) )
252 if ( MemRDR.ReadUi32BE(&BodySID) )
253 if ( OperationalPattern.Unarchive(&MemRDR) )
254 if ( EssenceContainers.Unarchive(&MemRDR) )
258 if ( ASDCP_FAILURE(result) )
259 DefaultLogSink().Error("Failed to initialize Partition\n");
266 ASDCP::MXF::Partition::WriteToFile(Kumu::FileWriter& Writer, UL& PartitionLabel)
268 ASDCP::FrameBuffer Buffer;
269 Result_t result = Buffer.Capacity(1024);
271 if ( ASDCP_SUCCESS(result) )
273 Kumu::MemIOWriter MemWRT(Buffer.Data(), Buffer.Capacity());
274 result = RESULT_KLV_CODING;
275 if ( MemWRT.WriteUi16BE(MajorVersion) )
276 if ( MemWRT.WriteUi16BE(MinorVersion) )
277 if ( MemWRT.WriteUi32BE(KAGSize) )
278 if ( MemWRT.WriteUi64BE(ThisPartition) )
279 if ( MemWRT.WriteUi64BE(PreviousPartition) )
280 if ( MemWRT.WriteUi64BE(FooterPartition) )
281 if ( MemWRT.WriteUi64BE(HeaderByteCount) )
282 if ( MemWRT.WriteUi64BE(IndexByteCount) )
283 if ( MemWRT.WriteUi32BE(IndexSID) )
284 if ( MemWRT.WriteUi64BE(BodyOffset) )
285 if ( MemWRT.WriteUi32BE(BodySID) )
286 if ( OperationalPattern.Archive(&MemWRT) )
287 if ( EssenceContainers.Archive(&MemWRT) )
289 Buffer.Size(MemWRT.Length());
294 if ( ASDCP_SUCCESS(result) )
297 result = WriteKLToFile(Writer, PartitionLabel.Value(), Buffer.Size());
299 if ( ASDCP_SUCCESS(result) )
300 result = Writer.Write(Buffer.RoData(), Buffer.Size(), &write_count);
308 ASDCP::MXF::Partition::ArchiveSize()
311 + sizeof(ui16_t) + sizeof(ui16_t)
313 + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t)
318 + sizeof(ui32_t) + sizeof(ui32_t) + ( UUIDlen * EssenceContainers.size() ) );
323 ASDCP::MXF::Partition::Dump(FILE* stream)
325 char identbuf[IdentBufferLen];
330 KLVFilePacket::Dump(stream, false);
331 fprintf(stream, " MajorVersion = %hu\n", MajorVersion);
332 fprintf(stream, " MinorVersion = %hu\n", MinorVersion);
333 fprintf(stream, " KAGSize = %u\n", KAGSize);
334 fprintf(stream, " ThisPartition = %s\n", ui64sz(ThisPartition, identbuf));
335 fprintf(stream, " PreviousPartition = %s\n", ui64sz(PreviousPartition, identbuf));
336 fprintf(stream, " FooterPartition = %s\n", ui64sz(FooterPartition, identbuf));
337 fprintf(stream, " HeaderByteCount = %s\n", ui64sz(HeaderByteCount, identbuf));
338 fprintf(stream, " IndexByteCount = %s\n", ui64sz(IndexByteCount, identbuf));
339 fprintf(stream, " IndexSID = %u\n", IndexSID);
340 fprintf(stream, " BodyOffset = %s\n", ui64sz(BodyOffset, identbuf));
341 fprintf(stream, " BodySID = %u\n", BodySID);
342 fprintf(stream, " OperationalPattern = %s\n", OperationalPattern.EncodeString(identbuf, IdentBufferLen));
343 fputs("Essence Containers:\n", stream); EssenceContainers.Dump(stream, false);
345 fputs("==========================================================================\n", stream);
349 //------------------------------------------------------------------------------------------
352 class ASDCP::MXF::Primer::h__PrimerLookup : public std::map<UL, TagValue>
355 void InitWithBatch(ASDCP::MXF::Batch<ASDCP::MXF::Primer::LocalTagEntry>& Batch)
357 ASDCP::MXF::Batch<ASDCP::MXF::Primer::LocalTagEntry>::iterator i = Batch.begin();
359 for ( ; i != Batch.end(); i++ )
360 insert(std::map<UL, TagValue>::value_type((*i).UL, (*i).Tag));
366 ASDCP::MXF::Primer::Primer() : m_LocalTag(0xff) {}
369 ASDCP::MXF::Primer::~Primer() {}
373 ASDCP::MXF::Primer::ClearTagList()
375 LocalTagEntryBatch.clear();
376 m_Lookup = new h__PrimerLookup;
381 ASDCP::MXF::Primer::InitFromBuffer(const byte_t* p, ui32_t l)
383 Result_t result = KLVPacket::InitFromBuffer(p, l, Dict::ul(MDD_Primer));
385 if ( ASDCP_SUCCESS(result) )
387 Kumu::MemIOReader MemRDR(m_ValueStart, m_ValueLength);
388 result = LocalTagEntryBatch.Unarchive(&MemRDR) ? RESULT_OK : RESULT_KLV_CODING;
391 if ( ASDCP_SUCCESS(result) )
393 m_Lookup = new h__PrimerLookup;
394 m_Lookup->InitWithBatch(LocalTagEntryBatch);
397 if ( ASDCP_FAILURE(result) )
398 DefaultLogSink().Error("Failed to initialize Primer\n");
405 ASDCP::MXF::Primer::WriteToFile(Kumu::FileWriter& Writer)
407 ASDCP::FrameBuffer Buffer;
408 Result_t result = Buffer.Capacity(128*1024);
410 if ( ASDCP_SUCCESS(result) )
411 result = WriteToBuffer(Buffer);
413 if ( ASDCP_SUCCESS(result) )
414 result = Writer.Write(Buffer.RoData(), Buffer.Size());
421 ASDCP::MXF::Primer::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
423 ASDCP::FrameBuffer LocalTagBuffer;
424 Kumu::MemIOWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length);
425 Result_t result = LocalTagEntryBatch.Archive(&MemWRT) ? RESULT_OK : RESULT_KLV_CODING;
427 if ( ASDCP_SUCCESS(result) )
429 ui32_t packet_length = MemWRT.Length();
430 result = WriteKLToBuffer(Buffer, Dict::ul(MDD_Primer), packet_length);
432 if ( ASDCP_SUCCESS(result) )
433 Buffer.Size(Buffer.Size() + packet_length);
441 ASDCP::MXF::Primer::InsertTag(const MDDEntry& Entry, ASDCP::TagValue& Tag)
445 std::map<UL, TagValue>::iterator i = m_Lookup->find(TestUL);
447 if ( i == m_Lookup->end() )
449 if ( Entry.tag.a == 0 && Entry.tag.b == 0 )
452 Tag.b = m_LocalTag--;
460 LocalTagEntry TmpEntry;
461 TmpEntry.UL = TestUL;
464 LocalTagEntryBatch.push_back(TmpEntry);
465 m_Lookup->insert(std::map<UL, TagValue>::value_type(TmpEntry.UL, TmpEntry.Tag));
477 ASDCP::MXF::Primer::TagForKey(const ASDCP::UL& Key, ASDCP::TagValue& Tag)
480 if ( m_Lookup.empty() )
482 DefaultLogSink().Error("Primer lookup is empty\n");
486 std::map<UL, TagValue>::iterator i = m_Lookup->find(Key);
488 if ( i == m_Lookup->end() )
497 ASDCP::MXF::Primer::Dump(FILE* stream)
499 char identbuf[IdentBufferLen];
504 KLVPacket::Dump(stream, false);
505 fprintf(stream, "Primer: %u %s\n",
506 LocalTagEntryBatch.size(),
507 ( LocalTagEntryBatch.size() == 1 ? "entry" : "entries" ));
509 Batch<LocalTagEntry>::iterator i = LocalTagEntryBatch.begin();
510 for ( ; i != LocalTagEntryBatch.end(); i++ )
512 const MDDEntry* Entry = Dict::FindUL((*i).UL.Value());
513 fprintf(stream, " %s %s\n", (*i).EncodeString(identbuf, IdentBufferLen), (Entry ? Entry->name : "Unknown"));
516 fputs("==========================================================================\n", stream);
520 //------------------------------------------------------------------------------------------
525 ASDCP::MXF::Preface::InitFromTLVSet(TLVReader& TLVSet)
527 Result_t result = InterchangeObject::InitFromTLVSet(TLVSet);
528 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, LastModifiedDate));
529 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi16(OBJ_READ_ARGS(Preface, Version));
530 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi32(OBJ_READ_ARGS(Preface, ObjectModelVersion));
531 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, PrimaryPackage));
532 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, Identifications));
533 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, ContentStorage));
534 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, OperationalPattern));
535 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, EssenceContainers));
536 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, DMSchemes));
542 ASDCP::MXF::Preface::WriteToTLVSet(TLVWriter& TLVSet)
544 Result_t result = InterchangeObject::WriteToTLVSet(TLVSet);
545 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, LastModifiedDate));
546 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi16(OBJ_WRITE_ARGS(Preface, Version));
547 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi32(OBJ_WRITE_ARGS(Preface, ObjectModelVersion));
548 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, PrimaryPackage));
549 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, Identifications));
550 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, ContentStorage));
551 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, OperationalPattern));
552 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, EssenceContainers));
553 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, DMSchemes));
559 ASDCP::MXF::Preface::InitFromBuffer(const byte_t* p, ui32_t l)
561 m_Typeinfo = &Dict::Type(MDD_Preface);
562 return InterchangeObject::InitFromBuffer(p, l);
567 ASDCP::MXF::Preface::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
569 m_Typeinfo = &Dict::Type(MDD_Preface);
570 return InterchangeObject::WriteToBuffer(Buffer);
575 ASDCP::MXF::Preface::Dump(FILE* stream)
577 char identbuf[IdentBufferLen];
582 InterchangeObject::Dump(stream);
583 fprintf(stream, " %22s = %s\n", "LastModifiedDate", LastModifiedDate.EncodeString(identbuf, IdentBufferLen));
584 fprintf(stream, " %22s = %hu\n", "Version", Version);
585 fprintf(stream, " %22s = %u\n", "ObjectModelVersion", ObjectModelVersion);
586 fprintf(stream, " %22s = %s\n", "PrimaryPackage", PrimaryPackage.EncodeHex(identbuf, IdentBufferLen));
587 fprintf(stream, " %22s:\n", "Identifications"); Identifications.Dump(stream);
588 fprintf(stream, " %22s = %s\n", "ContentStorage", ContentStorage.EncodeHex(identbuf, IdentBufferLen));
589 fprintf(stream, " %22s = %s\n", "OperationalPattern", OperationalPattern.EncodeString(identbuf, IdentBufferLen));
590 fprintf(stream, " %22s:\n", "EssenceContainers"); EssenceContainers.Dump(stream);
591 fprintf(stream, " %22s:\n", "DMSchemes"); DMSchemes.Dump(stream);
594 //------------------------------------------------------------------------------------------
597 ASDCP::MXF::OPAtomHeader::OPAtomHeader() : m_Preface(0), m_HasRIP(false) {}
598 ASDCP::MXF::OPAtomHeader::~OPAtomHeader() {}
602 ASDCP::MXF::OPAtomHeader::InitFromFile(const Kumu::FileReader& Reader)
605 Result_t result = SeekToRIP(Reader);
607 if ( ASDCP_SUCCESS(result) )
609 result = m_RIP.InitFromFile(Reader);
610 ui32_t test_s = m_RIP.PairArray.size();
612 if ( ASDCP_FAILURE(result) )
614 DefaultLogSink().Error("File contains no RIP\n");
617 else if ( test_s == 0 )
619 DefaultLogSink().Error("RIP contains no Pairs.\n");
620 result = RESULT_FORMAT;
622 else if ( test_s < 2 || test_s > 3 )
624 // OP-Atom states that there will be either two or three partitions,
625 // one closed header and one closed footer with an optional body
626 DefaultLogSink().Error("RIP count is not 2 or 3: %u\n", test_s);
627 return RESULT_FORMAT;
633 if ( m_RIP.PairArray.front().ByteOffset != 0 )
635 DefaultLogSink().Error("First Partition in RIP is not at offset 0.\n");
636 result = RESULT_FORMAT;
641 if ( ASDCP_SUCCESS(result) )
642 result = Reader.Seek(0);
644 if ( ASDCP_SUCCESS(result) )
645 result = Partition::InitFromFile(Reader); // test UL and OP
647 if ( ASDCP_FAILURE(result) )
650 // is it really OP-Atom?
651 UL OPAtomUL(Dict::ul(MDD_OPAtom));
652 UL InteropOPAtomUL(Dict::ul(MDD_MXFInterop_OPAtom));
654 if ( ! ( OperationalPattern == OPAtomUL || OperationalPattern == InteropOPAtomUL ) )
656 char strbuf[IdentBufferLen];
657 const MDDEntry* Entry = Dict::FindUL(OperationalPattern.Value());
659 DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n", OperationalPattern.EncodeString(strbuf, IdentBufferLen));
661 DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n", Entry->name);
664 // slurp up the remainder of the header
665 if ( HeaderByteCount < 1024 )
666 DefaultLogSink().Warn("Improbably small HeaderByteCount value: %u\n", HeaderByteCount);
668 result = m_Buffer.Capacity(HeaderByteCount);
670 if ( ASDCP_SUCCESS(result) )
673 result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
675 if ( ASDCP_FAILURE(result) )
678 if ( read_count != m_Buffer.Capacity() )
680 DefaultLogSink().Error("Short read of OP-Atom header metadata; wanted %u, got %u\n",
681 m_Buffer.Capacity(), read_count);
682 return RESULT_KLV_CODING;
686 const byte_t* p = m_Buffer.RoData();
687 const byte_t* end_p = p + m_Buffer.Capacity();
689 while ( ASDCP_SUCCESS(result) && p < end_p )
691 // parse the packets and index them by uid, discard KLVFill items
692 InterchangeObject* object = CreateObject(p);
695 object->m_Lookup = &m_Primer;
696 result = object->InitFromBuffer(p, end_p - p);
697 const byte_t* redo_p = p;
698 p += object->PacketLength();
699 // hexdump(p, object->PacketLength());
701 if ( ASDCP_SUCCESS(result) )
703 if ( object->IsA(Dict::ul(MDD_KLVFill)) )
707 else if ( object->IsA(Dict::ul(MDD_Primer)) )
710 result = m_Primer.InitFromBuffer(redo_p, end_p - redo_p);
712 else if ( object->IsA(Dict::ul(MDD_Preface)) )
714 assert(m_Preface == 0);
715 m_Preface = (Preface*)object;
719 m_PacketList->AddPacket(object);
724 DefaultLogSink().Error("Error initializing packet\n");
734 ASDCP::MXF::OPAtomHeader::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
736 InterchangeObject* TmpObject;
741 return m_PacketList->GetMDObjectByType(ObjectID, Object);
745 ASDCP::MXF::Identification*
746 ASDCP::MXF::OPAtomHeader::GetIdentification()
748 InterchangeObject* Object;
750 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object)) )
751 return (Identification*)Object;
757 ASDCP::MXF::SourcePackage*
758 ASDCP::MXF::OPAtomHeader::GetSourcePackage()
760 InterchangeObject* Object;
762 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object)) )
763 return (SourcePackage*)Object;
771 ASDCP::MXF::OPAtomHeader::WriteToFile(Kumu::FileWriter& Writer, ui32_t HeaderSize)
773 if ( m_Preface == 0 )
776 if ( HeaderSize < 4096 )
778 DefaultLogSink().Error("HeaderSize %u is too small. Must be >= 4096\n", HeaderSize);
782 ASDCP::FrameBuffer HeaderBuffer;
783 HeaderByteCount = HeaderSize - ArchiveSize();
784 Result_t result = HeaderBuffer.Capacity(HeaderByteCount);
785 m_Preface->m_Lookup = &m_Primer;
787 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
788 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
790 InterchangeObject* object = *pl_i;
791 object->m_Lookup = &m_Primer;
793 ASDCP::FrameBuffer WriteWrapper;
794 WriteWrapper.SetData(HeaderBuffer.Data() + HeaderBuffer.Size(),
795 HeaderBuffer.Capacity() - HeaderBuffer.Size());
796 result = object->WriteToBuffer(WriteWrapper);
797 HeaderBuffer.Size(HeaderBuffer.Size() + WriteWrapper.Size());
800 if ( ASDCP_SUCCESS(result) )
802 UL TmpUL(Dict::ul(MDD_ClosedCompleteHeader));
803 result = Partition::WriteToFile(Writer, TmpUL);
806 if ( ASDCP_SUCCESS(result) )
807 result = m_Primer.WriteToFile(Writer);
809 if ( ASDCP_SUCCESS(result) )
812 Writer.Write(HeaderBuffer.RoData(), HeaderBuffer.Size(), &write_count);
813 assert(write_count == HeaderBuffer.Size());
817 if ( ASDCP_SUCCESS(result) )
819 Kumu::fpos_t pos = Writer.Tell();
821 if ( pos > (Kumu::fpos_t)HeaderByteCount )
823 char intbuf[IntBufferLen];
824 DefaultLogSink().Error("Header size %s exceeds specified value %u\n",
830 ASDCP::FrameBuffer NilBuf;
831 ui32_t klv_fill_length = HeaderSize - (ui32_t)pos;
833 if ( klv_fill_length < kl_length )
835 DefaultLogSink().Error("Remaining region too small for KLV Fill header\n");
839 klv_fill_length -= kl_length;
840 result = WriteKLToFile(Writer, Dict::ul(MDD_KLVFill), klv_fill_length);
842 if ( ASDCP_SUCCESS(result) )
843 result = NilBuf.Capacity(klv_fill_length);
845 if ( ASDCP_SUCCESS(result) )
847 memset(NilBuf.Data(), 0, klv_fill_length);
849 Writer.Write(NilBuf.RoData(), klv_fill_length, &write_count);
850 assert(write_count == klv_fill_length);
859 ASDCP::MXF::OPAtomHeader::Dump(FILE* stream)
867 Partition::Dump(stream);
868 m_Primer.Dump(stream);
870 if ( m_Preface == 0 )
871 fputs("No Preface loaded\n", stream);
873 m_Preface->Dump(stream);
875 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
876 for ( ; i != m_PacketList->m_List.end(); i++ )
880 //------------------------------------------------------------------------------------------
883 ASDCP::MXF::OPAtomIndexFooter::OPAtomIndexFooter() :
884 m_CurrentSegment(0), m_BytesPerEditUnit(0), m_BodySID(0),
885 m_ECOffset(0), m_Lookup(0)
891 ASDCP::MXF::OPAtomIndexFooter::~OPAtomIndexFooter() {}
895 ASDCP::MXF::OPAtomIndexFooter::InitFromFile(const Kumu::FileReader& Reader)
897 Result_t result = Partition::InitFromFile(Reader); // test UL and OP
899 // slurp up the remainder of the footer
902 if ( ASDCP_SUCCESS(result) )
903 result = m_Buffer.Capacity(IndexByteCount);
905 if ( ASDCP_SUCCESS(result) )
906 result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
908 if ( ASDCP_SUCCESS(result) && read_count != m_Buffer.Capacity() )
910 DefaultLogSink().Error("Short read of footer partition: got %u, expecting %u\n",
911 read_count, m_Buffer.Capacity());
915 const byte_t* p = m_Buffer.RoData();
916 const byte_t* end_p = p + m_Buffer.Capacity();
918 while ( ASDCP_SUCCESS(result) && p < end_p )
920 // parse the packets and index them by uid, discard KLVFill items
921 InterchangeObject* object = CreateObject(p);
924 object->m_Lookup = m_Lookup;
925 result = object->InitFromBuffer(p, end_p - p);
926 p += object->PacketLength();
928 if ( ASDCP_SUCCESS(result) )
930 m_PacketList->AddPacket(object);
934 DefaultLogSink().Error("Error initializing packet\n");
939 if ( ASDCP_FAILURE(result) )
940 DefaultLogSink().Error("Failed to initialize OPAtomIndexFooter\n");
947 ASDCP::MXF::OPAtomIndexFooter::WriteToFile(Kumu::FileWriter& Writer, ui64_t duration)
949 ASDCP::FrameBuffer FooterBuffer;
950 ui32_t footer_size = m_PacketList->m_List.size() * MaxIndexSegmentSize; // segment-count * max-segment-size
951 Result_t result = FooterBuffer.Capacity(footer_size);
952 ui32_t iseg_count = 0;
954 if ( m_CurrentSegment != 0 )
956 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
957 m_CurrentSegment = 0;
960 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
961 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
963 if ( (*pl_i)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
966 IndexTableSegment* Segment = (IndexTableSegment*)(*pl_i);
968 if ( m_BytesPerEditUnit != 0 )
970 if ( iseg_count != 1 )
973 Segment->IndexDuration = duration;
977 InterchangeObject* object = *pl_i;
978 object->m_Lookup = m_Lookup;
980 ASDCP::FrameBuffer WriteWrapper;
981 WriteWrapper.SetData(FooterBuffer.Data() + FooterBuffer.Size(),
982 FooterBuffer.Capacity() - FooterBuffer.Size());
983 result = object->WriteToBuffer(WriteWrapper);
984 FooterBuffer.Size(FooterBuffer.Size() + WriteWrapper.Size());
987 if ( ASDCP_SUCCESS(result) )
989 IndexByteCount = FooterBuffer.Size();
990 UL FooterUL(Dict::ul(MDD_CompleteFooter));
991 result = Partition::WriteToFile(Writer, FooterUL);
994 if ( ASDCP_SUCCESS(result) )
997 Writer.Write(FooterBuffer.RoData(), FooterBuffer.Size(), &write_count);
998 assert(write_count == FooterBuffer.Size());
1006 ASDCP::MXF::OPAtomIndexFooter::Dump(FILE* stream)
1011 Partition::Dump(stream);
1013 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
1014 for ( ; i != m_PacketList->m_List.end(); i++ )
1020 ASDCP::MXF::OPAtomIndexFooter::Lookup(ui32_t frame_num, IndexTableSegment::IndexEntry& Entry)
1022 std::list<InterchangeObject*>::iterator li;
1023 for ( li = m_PacketList->m_List.begin(); li != m_PacketList->m_List.end(); li++ )
1025 if ( (*li)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1027 IndexTableSegment* Segment = (IndexTableSegment*)(*li);
1028 ui64_t start_pos = Segment->IndexStartPosition;
1030 if ( Segment->EditUnitByteCount > 0 )
1032 if ( m_PacketList->m_List.size() > 1 )
1033 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
1035 if ( ! Segment->IndexEntryArray.empty() )
1036 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
1038 Entry.StreamOffset = (ui64_t)frame_num * Segment->EditUnitByteCount;
1041 else if ( (ui64_t)frame_num >= start_pos
1042 && (ui64_t)frame_num < (start_pos + Segment->IndexDuration) )
1044 Entry = Segment->IndexEntryArray[frame_num-start_pos];
1055 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const Rational& Rate)
1059 m_BytesPerEditUnit = size;
1062 IndexTableSegment* Index = new IndexTableSegment;
1063 AddChildObject(Index);
1064 Index->EditUnitByteCount = m_BytesPerEditUnit;
1065 Index->IndexEditRate = Rate;
1070 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsVBR(IPrimerLookup* lookup, const Rational& Rate, Kumu::fpos_t offset)
1074 m_BytesPerEditUnit = 0;
1076 m_ECOffset = offset;
1081 ASDCP::MXF::OPAtomIndexFooter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
1083 if ( m_BytesPerEditUnit != 0 ) // are we CBR? that's bad
1085 DefaultLogSink().Error("Call to PushIndexEntry() failed: index is CBR\n");
1089 // do we have an available segment?
1090 if ( m_CurrentSegment == 0 )
1091 { // no, set up a new segment
1092 m_CurrentSegment = new IndexTableSegment;
1093 assert(m_CurrentSegment);
1094 AddChildObject(m_CurrentSegment);
1095 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1096 m_CurrentSegment->IndexEditRate = m_EditRate;
1097 m_CurrentSegment->IndexStartPosition = 0;
1099 else if ( m_CurrentSegment->IndexEntryArray.size() >= CBRIndexEntriesPerSegment )
1100 { // no, this one is full, start another
1101 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1102 ui64_t StartPosition = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
1104 m_CurrentSegment = new IndexTableSegment;
1105 assert(m_CurrentSegment);
1106 AddChildObject(m_CurrentSegment);
1107 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1108 m_CurrentSegment->IndexEditRate = m_EditRate;
1109 m_CurrentSegment->IndexStartPosition = StartPosition;
1112 m_CurrentSegment->IndexEntryArray.push_back(Entry);
1115 //------------------------------------------------------------------------------------------
1120 ASDCP::MXF::InterchangeObject::InitFromTLVSet(TLVReader& TLVSet)
1122 Result_t result = TLVSet.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID));
1123 if ( ASDCP_SUCCESS(result) )
1124 result = TLVSet.ReadObject(OBJ_READ_ARGS(GenerationInterchangeObject, GenerationUID));
1130 ASDCP::MXF::InterchangeObject::WriteToTLVSet(TLVWriter& TLVSet)
1132 Result_t result = TLVSet.WriteObject(OBJ_WRITE_ARGS(InterchangeObject, InstanceUID));
1133 if ( ASDCP_SUCCESS(result) )
1134 result = TLVSet.WriteObject(OBJ_WRITE_ARGS(GenerationInterchangeObject, GenerationUID));
1140 ASDCP::MXF::InterchangeObject::InitFromBuffer(const byte_t* p, ui32_t l)
1143 Result_t result = RESULT_FALSE;
1145 if ( m_Typeinfo == 0 )
1147 result = KLVPacket::InitFromBuffer(p, l);
1151 result = KLVPacket::InitFromBuffer(p, l, m_Typeinfo->ul);
1153 if ( ASDCP_SUCCESS(result) )
1155 TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup);
1156 result = InitFromTLVSet(MemRDR);
1165 ASDCP::MXF::InterchangeObject::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
1167 if ( m_Typeinfo == 0 )
1168 return RESULT_STATE;
1170 TLVWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length, m_Lookup);
1171 Result_t result = WriteToTLVSet(MemWRT);
1173 if ( ASDCP_SUCCESS(result) )
1175 ui32_t packet_length = MemWRT.Length();
1176 result = WriteKLToBuffer(Buffer, m_Typeinfo->ul, packet_length);
1178 if ( ASDCP_SUCCESS(result) )
1179 Buffer.Size(Buffer.Size() + packet_length);
1187 ASDCP::MXF::InterchangeObject::Dump(FILE* stream)
1189 char identbuf[IdentBufferLen];
1191 fputc('\n', stream);
1192 KLVPacket::Dump(stream, false);
1193 fprintf(stream, " InstanceUID = %s\n", InstanceUID.EncodeHex(identbuf, IdentBufferLen));
1194 fprintf(stream, " GenerationUID = %s\n", GenerationUID.EncodeHex(identbuf, IdentBufferLen));
1199 ASDCP::MXF::InterchangeObject::IsA(const byte_t* label)
1201 if ( m_KLLength == 0 )
1204 return ( memcmp(label, m_KeyStart, SMPTE_UL_LENGTH) == 0 );