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);
714 m_PacketList->AddPacket(object);
716 if ( object->IsA(Dict::ul(MDD_Preface)) )
718 assert(m_Preface == 0);
719 m_Preface = (Preface*)object;
725 DefaultLogSink().Error("Error initializing packet\n");
735 ASDCP::MXF::OPAtomHeader::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
737 InterchangeObject* TmpObject;
742 return m_PacketList->GetMDObjectByType(ObjectID, Object);
746 ASDCP::MXF::Identification*
747 ASDCP::MXF::OPAtomHeader::GetIdentification()
749 InterchangeObject* Object;
751 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object)) )
752 return (Identification*)Object;
758 ASDCP::MXF::SourcePackage*
759 ASDCP::MXF::OPAtomHeader::GetSourcePackage()
761 InterchangeObject* Object;
763 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object)) )
764 return (SourcePackage*)Object;
772 ASDCP::MXF::OPAtomHeader::WriteToFile(Kumu::FileWriter& Writer, ui32_t HeaderSize)
774 if ( m_Preface == 0 )
777 if ( HeaderSize < 4096 )
779 DefaultLogSink().Error("HeaderSize %u is too small. Must be >= 4096\n", HeaderSize);
783 ASDCP::FrameBuffer HeaderBuffer;
784 HeaderByteCount = HeaderSize - ArchiveSize();
785 Result_t result = HeaderBuffer.Capacity(HeaderByteCount);
786 m_Preface->m_Lookup = &m_Primer;
788 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
789 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
791 InterchangeObject* object = *pl_i;
792 object->m_Lookup = &m_Primer;
794 ASDCP::FrameBuffer WriteWrapper;
795 WriteWrapper.SetData(HeaderBuffer.Data() + HeaderBuffer.Size(),
796 HeaderBuffer.Capacity() - HeaderBuffer.Size());
797 result = object->WriteToBuffer(WriteWrapper);
798 HeaderBuffer.Size(HeaderBuffer.Size() + WriteWrapper.Size());
801 if ( ASDCP_SUCCESS(result) )
803 UL TmpUL(Dict::ul(MDD_ClosedCompleteHeader));
804 result = Partition::WriteToFile(Writer, TmpUL);
807 if ( ASDCP_SUCCESS(result) )
808 result = m_Primer.WriteToFile(Writer);
810 if ( ASDCP_SUCCESS(result) )
813 Writer.Write(HeaderBuffer.RoData(), HeaderBuffer.Size(), &write_count);
814 assert(write_count == HeaderBuffer.Size());
818 if ( ASDCP_SUCCESS(result) )
820 Kumu::fpos_t pos = Writer.Tell();
822 if ( pos > (Kumu::fpos_t)HeaderByteCount )
824 char intbuf[IntBufferLen];
825 DefaultLogSink().Error("Header size %s exceeds specified value %u\n",
831 ASDCP::FrameBuffer NilBuf;
832 ui32_t klv_fill_length = HeaderSize - (ui32_t)pos;
834 if ( klv_fill_length < kl_length )
836 DefaultLogSink().Error("Remaining region too small for KLV Fill header\n");
840 klv_fill_length -= kl_length;
841 result = WriteKLToFile(Writer, Dict::ul(MDD_KLVFill), klv_fill_length);
843 if ( ASDCP_SUCCESS(result) )
844 result = NilBuf.Capacity(klv_fill_length);
846 if ( ASDCP_SUCCESS(result) )
848 memset(NilBuf.Data(), 0, klv_fill_length);
850 Writer.Write(NilBuf.RoData(), klv_fill_length, &write_count);
851 assert(write_count == klv_fill_length);
860 ASDCP::MXF::OPAtomHeader::Dump(FILE* stream)
868 Partition::Dump(stream);
869 m_Primer.Dump(stream);
871 if ( m_Preface == 0 )
872 fputs("No Preface loaded\n", stream);
874 m_Preface->Dump(stream);
876 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
877 for ( ; i != m_PacketList->m_List.end(); i++ )
881 //------------------------------------------------------------------------------------------
884 ASDCP::MXF::OPAtomIndexFooter::OPAtomIndexFooter() :
885 m_CurrentSegment(0), m_BytesPerEditUnit(0), m_BodySID(0),
886 m_ECOffset(0), m_Lookup(0)
892 ASDCP::MXF::OPAtomIndexFooter::~OPAtomIndexFooter() {}
896 ASDCP::MXF::OPAtomIndexFooter::InitFromFile(const Kumu::FileReader& Reader)
898 Result_t result = Partition::InitFromFile(Reader); // test UL and OP
900 // slurp up the remainder of the footer
903 if ( ASDCP_SUCCESS(result) )
904 result = m_Buffer.Capacity(IndexByteCount);
906 if ( ASDCP_SUCCESS(result) )
907 result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
909 if ( ASDCP_SUCCESS(result) && read_count != m_Buffer.Capacity() )
911 DefaultLogSink().Error("Short read of footer partition: got %u, expecting %u\n",
912 read_count, m_Buffer.Capacity());
916 const byte_t* p = m_Buffer.RoData();
917 const byte_t* end_p = p + m_Buffer.Capacity();
919 while ( ASDCP_SUCCESS(result) && p < end_p )
921 // parse the packets and index them by uid, discard KLVFill items
922 InterchangeObject* object = CreateObject(p);
925 object->m_Lookup = m_Lookup;
926 result = object->InitFromBuffer(p, end_p - p);
927 p += object->PacketLength();
929 if ( ASDCP_SUCCESS(result) )
931 m_PacketList->AddPacket(object);
935 DefaultLogSink().Error("Error initializing packet\n");
940 if ( ASDCP_FAILURE(result) )
941 DefaultLogSink().Error("Failed to initialize OPAtomIndexFooter\n");
948 ASDCP::MXF::OPAtomIndexFooter::WriteToFile(Kumu::FileWriter& Writer, ui64_t duration)
950 ASDCP::FrameBuffer FooterBuffer;
951 ui32_t footer_size = m_PacketList->m_List.size() * MaxIndexSegmentSize; // segment-count * max-segment-size
952 Result_t result = FooterBuffer.Capacity(footer_size);
953 ui32_t iseg_count = 0;
955 if ( m_CurrentSegment != 0 )
957 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
958 m_CurrentSegment = 0;
961 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
962 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
964 if ( (*pl_i)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
967 IndexTableSegment* Segment = (IndexTableSegment*)(*pl_i);
969 if ( m_BytesPerEditUnit != 0 )
971 if ( iseg_count != 1 )
974 Segment->IndexDuration = duration;
978 InterchangeObject* object = *pl_i;
979 object->m_Lookup = m_Lookup;
981 ASDCP::FrameBuffer WriteWrapper;
982 WriteWrapper.SetData(FooterBuffer.Data() + FooterBuffer.Size(),
983 FooterBuffer.Capacity() - FooterBuffer.Size());
984 result = object->WriteToBuffer(WriteWrapper);
985 FooterBuffer.Size(FooterBuffer.Size() + WriteWrapper.Size());
988 if ( ASDCP_SUCCESS(result) )
990 IndexByteCount = FooterBuffer.Size();
991 UL FooterUL(Dict::ul(MDD_CompleteFooter));
992 result = Partition::WriteToFile(Writer, FooterUL);
995 if ( ASDCP_SUCCESS(result) )
998 Writer.Write(FooterBuffer.RoData(), FooterBuffer.Size(), &write_count);
999 assert(write_count == FooterBuffer.Size());
1007 ASDCP::MXF::OPAtomIndexFooter::Dump(FILE* stream)
1012 Partition::Dump(stream);
1014 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
1015 for ( ; i != m_PacketList->m_List.end(); i++ )
1021 ASDCP::MXF::OPAtomIndexFooter::Lookup(ui32_t frame_num, IndexTableSegment::IndexEntry& Entry)
1023 std::list<InterchangeObject*>::iterator li;
1024 for ( li = m_PacketList->m_List.begin(); li != m_PacketList->m_List.end(); li++ )
1026 if ( (*li)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1028 IndexTableSegment* Segment = (IndexTableSegment*)(*li);
1029 ui64_t start_pos = Segment->IndexStartPosition;
1031 if ( Segment->EditUnitByteCount > 0 )
1033 if ( m_PacketList->m_List.size() > 1 )
1034 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
1036 if ( ! Segment->IndexEntryArray.empty() )
1037 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
1039 Entry.StreamOffset = (ui64_t)frame_num * Segment->EditUnitByteCount;
1042 else if ( (ui64_t)frame_num >= start_pos
1043 && (ui64_t)frame_num < (start_pos + Segment->IndexDuration) )
1045 Entry = Segment->IndexEntryArray[frame_num-start_pos];
1056 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const Rational& Rate)
1060 m_BytesPerEditUnit = size;
1063 IndexTableSegment* Index = new IndexTableSegment;
1064 AddChildObject(Index);
1065 Index->EditUnitByteCount = m_BytesPerEditUnit;
1066 Index->IndexEditRate = Rate;
1071 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsVBR(IPrimerLookup* lookup, const Rational& Rate, Kumu::fpos_t offset)
1075 m_BytesPerEditUnit = 0;
1077 m_ECOffset = offset;
1082 ASDCP::MXF::OPAtomIndexFooter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
1084 if ( m_BytesPerEditUnit != 0 ) // are we CBR? that's bad
1086 DefaultLogSink().Error("Call to PushIndexEntry() failed: index is CBR\n");
1090 // do we have an available segment?
1091 if ( m_CurrentSegment == 0 )
1092 { // no, set up a new segment
1093 m_CurrentSegment = new IndexTableSegment;
1094 assert(m_CurrentSegment);
1095 AddChildObject(m_CurrentSegment);
1096 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1097 m_CurrentSegment->IndexEditRate = m_EditRate;
1098 m_CurrentSegment->IndexStartPosition = 0;
1100 else if ( m_CurrentSegment->IndexEntryArray.size() >= CBRIndexEntriesPerSegment )
1101 { // no, this one is full, start another
1102 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1103 ui64_t StartPosition = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
1105 m_CurrentSegment = new IndexTableSegment;
1106 assert(m_CurrentSegment);
1107 AddChildObject(m_CurrentSegment);
1108 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1109 m_CurrentSegment->IndexEditRate = m_EditRate;
1110 m_CurrentSegment->IndexStartPosition = StartPosition;
1113 m_CurrentSegment->IndexEntryArray.push_back(Entry);
1116 //------------------------------------------------------------------------------------------
1121 ASDCP::MXF::InterchangeObject::InitFromTLVSet(TLVReader& TLVSet)
1123 Result_t result = TLVSet.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID));
1124 if ( ASDCP_SUCCESS(result) )
1125 result = TLVSet.ReadObject(OBJ_READ_ARGS(GenerationInterchangeObject, GenerationUID));
1131 ASDCP::MXF::InterchangeObject::WriteToTLVSet(TLVWriter& TLVSet)
1133 Result_t result = TLVSet.WriteObject(OBJ_WRITE_ARGS(InterchangeObject, InstanceUID));
1134 if ( ASDCP_SUCCESS(result) )
1135 result = TLVSet.WriteObject(OBJ_WRITE_ARGS(GenerationInterchangeObject, GenerationUID));
1141 ASDCP::MXF::InterchangeObject::InitFromBuffer(const byte_t* p, ui32_t l)
1144 Result_t result = RESULT_FALSE;
1146 if ( m_Typeinfo == 0 )
1148 result = KLVPacket::InitFromBuffer(p, l);
1152 result = KLVPacket::InitFromBuffer(p, l, m_Typeinfo->ul);
1154 if ( ASDCP_SUCCESS(result) )
1156 TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup);
1157 result = InitFromTLVSet(MemRDR);
1166 ASDCP::MXF::InterchangeObject::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
1168 if ( m_Typeinfo == 0 )
1169 return RESULT_STATE;
1171 TLVWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length, m_Lookup);
1172 Result_t result = WriteToTLVSet(MemWRT);
1174 if ( ASDCP_SUCCESS(result) )
1176 ui32_t packet_length = MemWRT.Length();
1177 result = WriteKLToBuffer(Buffer, m_Typeinfo->ul, packet_length);
1179 if ( ASDCP_SUCCESS(result) )
1180 Buffer.Size(Buffer.Size() + packet_length);
1188 ASDCP::MXF::InterchangeObject::Dump(FILE* stream)
1190 char identbuf[IdentBufferLen];
1192 fputc('\n', stream);
1193 KLVPacket::Dump(stream, false);
1194 fprintf(stream, " InstanceUID = %s\n", InstanceUID.EncodeHex(identbuf, IdentBufferLen));
1195 fprintf(stream, " GenerationUID = %s\n", GenerationUID.EncodeHex(identbuf, IdentBufferLen));
1200 ASDCP::MXF::InterchangeObject::IsA(const byte_t* label)
1202 if ( m_KLLength == 0 )
1205 return ( memcmp(label, m_KeyStart, SMPTE_UL_LENGTH) == 0 );