2 Copyright (c) 2005-2006, 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 //------------------------------------------------------------------------------------------
39 const ui32_t kl_length = ASDCP::SMPTE_UL_LENGTH + ASDCP::MXF_BER_LENGTH;
43 ASDCP::MXF::SeekToRIP(const Kumu::FileReader& Reader)
47 // go to the end - 4 bytes
48 Result_t result = Reader.Seek(0, Kumu::SP_END);
50 if ( ASDCP_SUCCESS(result) )
51 result = Reader.Tell(&end_pos);
53 if ( ASDCP_SUCCESS(result)
54 && end_pos < (SMPTE_UL_LENGTH+MXF_BER_LENGTH) )
55 result = RESULT_FAIL; // File is smaller than an empty packet!
57 if ( ASDCP_SUCCESS(result) )
58 result = Reader.Seek(end_pos - 4);
60 // get the ui32_t RIP length
62 byte_t intbuf[MXF_BER_LENGTH];
65 if ( ASDCP_SUCCESS(result) )
67 result = Reader.Read(intbuf, MXF_BER_LENGTH, &read_count);
69 if ( ASDCP_SUCCESS(result) && read_count != 4 )
73 if ( ASDCP_SUCCESS(result) )
75 rip_size = KM_i32_BE(Kumu::cp2i<ui32_t>(intbuf));
77 if ( rip_size > end_pos ) // RIP can't be bigger than the file
81 // reposition to start of RIP
82 if ( ASDCP_SUCCESS(result) )
83 result = Reader.Seek(end_pos - rip_size);
90 ASDCP::MXF::RIP::InitFromFile(const Kumu::FileReader& Reader)
92 Result_t result = KLVFilePacket::InitFromFile(Reader, Dict::ul(MDD_RandomIndexMetadata));
94 if ( ASDCP_SUCCESS(result) )
96 Kumu::MemIOReader MemRDR(m_ValueStart, m_ValueLength - 4);
97 result = PairArray.Unarchive(&MemRDR) ? RESULT_OK : RESULT_KLV_CODING;
100 if ( ASDCP_FAILURE(result) )
101 DefaultLogSink().Error("Failed to initialize RIP\n");
108 ASDCP::MXF::RIP::WriteToFile(Kumu::FileWriter& Writer)
110 ASDCP::FrameBuffer Buffer;
111 ui32_t RIPSize = ( PairArray.size() * (sizeof(ui32_t) + sizeof(ui64_t)) ) + 4;
112 Result_t result = Buffer.Capacity(RIPSize);
114 if ( ASDCP_SUCCESS(result) )
115 result = WriteKLToFile(Writer, Dict::ul(MDD_RandomIndexMetadata), RIPSize);
117 if ( ASDCP_SUCCESS(result) )
119 result = RESULT_KLV_CODING;
121 Kumu::MemIOWriter MemWRT(Buffer.Data(), Buffer.Capacity());
122 if ( PairArray.Archive(&MemWRT) )
123 if ( MemWRT.WriteUi32BE(RIPSize + 20) )
125 Buffer.Size(MemWRT.Length());
130 if ( ASDCP_SUCCESS(result) )
131 result = Writer.Write(Buffer.RoData(), Buffer.Size());
138 ASDCP::MXF::RIP::Dump(FILE* stream)
143 KLVFilePacket::Dump(stream, false);
144 PairArray.Dump(stream, false);
146 fputs("==========================================================================\n", stream);
149 //------------------------------------------------------------------------------------------
153 class ASDCP::MXF::Partition::h__PacketList
156 std::list<InterchangeObject*> m_List;
157 std::map<UUID, InterchangeObject*> m_Map;
160 while ( ! m_List.empty() )
162 delete m_List.back();
168 void AddPacket(InterchangeObject* ThePacket)
171 m_Map.insert(std::map<UUID, InterchangeObject*>::value_type(ThePacket->InstanceUID, ThePacket));
172 m_List.push_back(ThePacket);
176 Result_t GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
178 ASDCP_TEST_NULL(ObjectID);
179 ASDCP_TEST_NULL(Object);
180 std::list<InterchangeObject*>::iterator li;
183 for ( li = m_List.begin(); li != m_List.end(); li++ )
185 if ( (*li)->HasUL(ObjectID) )
196 //------------------------------------------------------------------------------------------
200 ASDCP::MXF::Partition::Partition() :
201 MajorVersion(1), MinorVersion(2),
202 KAGSize(1), ThisPartition(0), PreviousPartition(0),
203 FooterPartition(0), HeaderByteCount(0), IndexByteCount(0), IndexSID(0),
204 BodyOffset(0), BodySID(0)
206 m_PacketList = new h__PacketList;
209 ASDCP::MXF::Partition::~Partition()
215 ASDCP::MXF::Partition::AddChildObject(InterchangeObject* Object)
219 Kumu::GenRandomValue(TmpID);
220 Object->InstanceUID = TmpID;
221 m_PacketList->AddPacket(Object);
226 ASDCP::MXF::Partition::InitFromFile(const Kumu::FileReader& Reader)
228 Result_t result = KLVFilePacket::InitFromFile(Reader);
230 // could be one of several values
232 if ( ASDCP_SUCCESS(result) )
234 Kumu::MemIOReader MemRDR(m_ValueStart, m_ValueLength);
235 result = RESULT_KLV_CODING;
237 if ( MemRDR.ReadUi16BE(&MajorVersion) )
238 if ( MemRDR.ReadUi16BE(&MinorVersion) )
239 if ( MemRDR.ReadUi32BE(&KAGSize) )
240 if ( MemRDR.ReadUi64BE(&ThisPartition) )
241 if ( MemRDR.ReadUi64BE(&PreviousPartition) )
242 if ( MemRDR.ReadUi64BE(&FooterPartition) )
243 if ( MemRDR.ReadUi64BE(&HeaderByteCount) )
244 if ( MemRDR.ReadUi64BE(&IndexByteCount) )
245 if ( MemRDR.ReadUi32BE(&IndexSID) )
246 if ( MemRDR.ReadUi64BE(&BodyOffset) )
247 if ( MemRDR.ReadUi32BE(&BodySID) )
248 if ( OperationalPattern.Unarchive(&MemRDR) )
249 if ( EssenceContainers.Unarchive(&MemRDR) )
253 if ( ASDCP_FAILURE(result) )
254 DefaultLogSink().Error("Failed to initialize Partition\n");
261 ASDCP::MXF::Partition::WriteToFile(Kumu::FileWriter& Writer, UL& PartitionLabel)
263 ASDCP::FrameBuffer Buffer;
264 Result_t result = Buffer.Capacity(1024);
266 if ( ASDCP_SUCCESS(result) )
268 Kumu::MemIOWriter MemWRT(Buffer.Data(), Buffer.Capacity());
269 result = RESULT_KLV_CODING;
270 if ( MemWRT.WriteUi16BE(MajorVersion) )
271 if ( MemWRT.WriteUi16BE(MinorVersion) )
272 if ( MemWRT.WriteUi32BE(KAGSize) )
273 if ( MemWRT.WriteUi64BE(ThisPartition) )
274 if ( MemWRT.WriteUi64BE(PreviousPartition) )
275 if ( MemWRT.WriteUi64BE(FooterPartition) )
276 if ( MemWRT.WriteUi64BE(HeaderByteCount) )
277 if ( MemWRT.WriteUi64BE(IndexByteCount) )
278 if ( MemWRT.WriteUi32BE(IndexSID) )
279 if ( MemWRT.WriteUi64BE(BodyOffset) )
280 if ( MemWRT.WriteUi32BE(BodySID) )
281 if ( OperationalPattern.Archive(&MemWRT) )
282 if ( EssenceContainers.Archive(&MemWRT) )
284 Buffer.Size(MemWRT.Length());
289 if ( ASDCP_SUCCESS(result) )
292 result = WriteKLToFile(Writer, PartitionLabel.Value(), Buffer.Size());
294 if ( ASDCP_SUCCESS(result) )
295 result = Writer.Write(Buffer.RoData(), Buffer.Size(), &write_count);
303 ASDCP::MXF::Partition::ArchiveSize()
306 + sizeof(ui16_t) + sizeof(ui16_t)
308 + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t)
313 + sizeof(ui32_t) + sizeof(ui32_t) + ( UUIDlen * EssenceContainers.size() ) );
318 ASDCP::MXF::Partition::Dump(FILE* stream)
320 char identbuf[IdentBufferLen];
325 KLVFilePacket::Dump(stream, false);
326 fprintf(stream, " MajorVersion = %hu\n", MajorVersion);
327 fprintf(stream, " MinorVersion = %hu\n", MinorVersion);
328 fprintf(stream, " KAGSize = %u\n", KAGSize);
329 fprintf(stream, " ThisPartition = %s\n", ui64sz(ThisPartition, identbuf));
330 fprintf(stream, " PreviousPartition = %s\n", ui64sz(PreviousPartition, identbuf));
331 fprintf(stream, " FooterPartition = %s\n", ui64sz(FooterPartition, identbuf));
332 fprintf(stream, " HeaderByteCount = %s\n", ui64sz(HeaderByteCount, identbuf));
333 fprintf(stream, " IndexByteCount = %s\n", ui64sz(IndexByteCount, identbuf));
334 fprintf(stream, " IndexSID = %u\n", IndexSID);
335 fprintf(stream, " BodyOffset = %s\n", ui64sz(BodyOffset, identbuf));
336 fprintf(stream, " BodySID = %u\n", BodySID);
337 fprintf(stream, " OperationalPattern = %s\n", OperationalPattern.EncodeString(identbuf, IdentBufferLen));
338 fputs("Essence Containers:\n", stream); EssenceContainers.Dump(stream, false);
340 fputs("==========================================================================\n", stream);
344 //------------------------------------------------------------------------------------------
347 class ASDCP::MXF::Primer::h__PrimerLookup : public std::map<UL, TagValue>
350 void InitWithBatch(ASDCP::MXF::Batch<ASDCP::MXF::Primer::LocalTagEntry>& Batch)
352 ASDCP::MXF::Batch<ASDCP::MXF::Primer::LocalTagEntry>::iterator i = Batch.begin();
354 for ( ; i != Batch.end(); i++ )
355 insert(std::map<UL, TagValue>::value_type((*i).UL, (*i).Tag));
361 ASDCP::MXF::Primer::Primer() : m_LocalTag(0xff) {}
364 ASDCP::MXF::Primer::~Primer() {}
368 ASDCP::MXF::Primer::ClearTagList()
370 LocalTagEntryBatch.clear();
371 m_Lookup = new h__PrimerLookup;
376 ASDCP::MXF::Primer::InitFromBuffer(const byte_t* p, ui32_t l)
378 Result_t result = KLVPacket::InitFromBuffer(p, l, Dict::ul(MDD_Primer));
380 if ( ASDCP_SUCCESS(result) )
382 Kumu::MemIOReader MemRDR(m_ValueStart, m_ValueLength);
383 result = LocalTagEntryBatch.Unarchive(&MemRDR) ? RESULT_OK : RESULT_KLV_CODING;
386 if ( ASDCP_SUCCESS(result) )
388 m_Lookup = new h__PrimerLookup;
389 m_Lookup->InitWithBatch(LocalTagEntryBatch);
392 if ( ASDCP_FAILURE(result) )
393 DefaultLogSink().Error("Failed to initialize Primer\n");
400 ASDCP::MXF::Primer::WriteToFile(Kumu::FileWriter& Writer)
402 ASDCP::FrameBuffer Buffer;
403 Result_t result = Buffer.Capacity(128*1024);
405 if ( ASDCP_SUCCESS(result) )
406 result = WriteToBuffer(Buffer);
408 if ( ASDCP_SUCCESS(result) )
409 result = Writer.Write(Buffer.RoData(), Buffer.Size());
416 ASDCP::MXF::Primer::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
418 ASDCP::FrameBuffer LocalTagBuffer;
419 Kumu::MemIOWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length);
420 Result_t result = LocalTagEntryBatch.Archive(&MemWRT) ? RESULT_OK : RESULT_KLV_CODING;
422 if ( ASDCP_SUCCESS(result) )
424 ui32_t packet_length = MemWRT.Length();
425 result = WriteKLToBuffer(Buffer, Dict::ul(MDD_Primer), packet_length);
427 if ( ASDCP_SUCCESS(result) )
428 Buffer.Size(Buffer.Size() + packet_length);
436 ASDCP::MXF::Primer::InsertTag(const MDDEntry& Entry, ASDCP::TagValue& Tag)
440 std::map<UL, TagValue>::iterator i = m_Lookup->find(TestUL);
442 if ( i == m_Lookup->end() )
444 if ( Entry.tag.a == 0 && Entry.tag.b == 0 )
447 Tag.b = m_LocalTag--;
455 LocalTagEntry TmpEntry;
456 TmpEntry.UL = TestUL;
459 LocalTagEntryBatch.push_back(TmpEntry);
460 m_Lookup->insert(std::map<UL, TagValue>::value_type(TmpEntry.UL, TmpEntry.Tag));
472 ASDCP::MXF::Primer::TagForKey(const ASDCP::UL& Key, ASDCP::TagValue& Tag)
475 if ( m_Lookup.empty() )
477 DefaultLogSink().Error("Primer lookup is empty\n");
481 std::map<UL, TagValue>::iterator i = m_Lookup->find(Key);
483 if ( i == m_Lookup->end() )
492 ASDCP::MXF::Primer::Dump(FILE* stream)
494 char identbuf[IdentBufferLen];
499 KLVPacket::Dump(stream, false);
500 fprintf(stream, "Primer: %u %s\n",
501 LocalTagEntryBatch.size(),
502 ( LocalTagEntryBatch.size() == 1 ? "entry" : "entries" ));
504 Batch<LocalTagEntry>::iterator i = LocalTagEntryBatch.begin();
505 for ( ; i != LocalTagEntryBatch.end(); i++ )
507 const MDDEntry* Entry = Dict::FindUL((*i).UL.Value());
508 fprintf(stream, " %s %s\n", (*i).EncodeString(identbuf, IdentBufferLen), (Entry ? Entry->name : "Unknown"));
511 fputs("==========================================================================\n", stream);
515 //------------------------------------------------------------------------------------------
520 ASDCP::MXF::Preface::InitFromTLVSet(TLVReader& TLVSet)
522 Result_t result = InterchangeObject::InitFromTLVSet(TLVSet);
523 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, LastModifiedDate));
524 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi16(OBJ_READ_ARGS(Preface, Version));
525 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi32(OBJ_READ_ARGS(Preface, ObjectModelVersion));
526 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, PrimaryPackage));
527 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, Identifications));
528 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, ContentStorage));
529 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, OperationalPattern));
530 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, EssenceContainers));
531 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, DMSchemes));
537 ASDCP::MXF::Preface::WriteToTLVSet(TLVWriter& TLVSet)
539 Result_t result = InterchangeObject::WriteToTLVSet(TLVSet);
540 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, LastModifiedDate));
541 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi16(OBJ_WRITE_ARGS(Preface, Version));
542 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi32(OBJ_WRITE_ARGS(Preface, ObjectModelVersion));
543 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, PrimaryPackage));
544 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, Identifications));
545 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, ContentStorage));
546 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, OperationalPattern));
547 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, EssenceContainers));
548 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, DMSchemes));
554 ASDCP::MXF::Preface::InitFromBuffer(const byte_t* p, ui32_t l)
556 m_Typeinfo = &Dict::Type(MDD_Preface);
557 return InterchangeObject::InitFromBuffer(p, l);
562 ASDCP::MXF::Preface::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
564 m_Typeinfo = &Dict::Type(MDD_Preface);
565 return InterchangeObject::WriteToBuffer(Buffer);
570 ASDCP::MXF::Preface::Dump(FILE* stream)
572 char identbuf[IdentBufferLen];
577 InterchangeObject::Dump(stream);
578 fprintf(stream, " %22s = %s\n", "LastModifiedDate", LastModifiedDate.EncodeString(identbuf, IdentBufferLen));
579 fprintf(stream, " %22s = %hu\n", "Version", Version);
580 fprintf(stream, " %22s = %u\n", "ObjectModelVersion", ObjectModelVersion);
581 fprintf(stream, " %22s = %s\n", "PrimaryPackage", PrimaryPackage.EncodeHex(identbuf, IdentBufferLen));
582 fprintf(stream, " %22s:\n", "Identifications"); Identifications.Dump(stream);
583 fprintf(stream, " %22s = %s\n", "ContentStorage", ContentStorage.EncodeHex(identbuf, IdentBufferLen));
584 fprintf(stream, " %22s = %s\n", "OperationalPattern", OperationalPattern.EncodeString(identbuf, IdentBufferLen));
585 fprintf(stream, " %22s:\n", "EssenceContainers"); EssenceContainers.Dump(stream);
586 fprintf(stream, " %22s:\n", "DMSchemes"); DMSchemes.Dump(stream);
589 //------------------------------------------------------------------------------------------
592 ASDCP::MXF::OPAtomHeader::OPAtomHeader() : m_Preface(0), m_HasRIP(false) {}
593 ASDCP::MXF::OPAtomHeader::~OPAtomHeader() {}
597 ASDCP::MXF::OPAtomHeader::InitFromFile(const Kumu::FileReader& Reader)
600 Result_t result = SeekToRIP(Reader);
602 if ( ASDCP_SUCCESS(result) )
604 result = m_RIP.InitFromFile(Reader);
605 ui32_t test_s = m_RIP.PairArray.size();
607 if ( ASDCP_FAILURE(result) )
609 DefaultLogSink().Error("File contains no RIP\n");
612 else if ( test_s == 0 )
614 DefaultLogSink().Error("RIP contains no Pairs.\n");
615 result = RESULT_FORMAT;
617 else if ( test_s < 2 || test_s > 3 )
619 // OP-Atom states that there will be either two or three partitions,
620 // one closed header and one closed footer with an optional body
621 DefaultLogSink().Error("RIP count is not 2 or 3: %u\n", test_s);
622 return RESULT_FORMAT;
630 if ( ASDCP_SUCCESS(result) )
632 Array<RIP::Pair>::iterator r_i = m_RIP.PairArray.begin();
634 if ( (*r_i).ByteOffset != 0 )
636 DefaultLogSink().Error("First Partition in RIP is not at offset 0.\n");
637 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 // is it really OP-Atom?
648 UL OPAtomUL(Dict::ul(MDD_OPAtom));
649 UL InteropOPAtomUL(Dict::ul(MDD_MXFInterop_OPAtom));
651 if ( ! ( OperationalPattern == OPAtomUL || OperationalPattern == InteropOPAtomUL ) )
653 char strbuf[IdentBufferLen];
654 const MDDEntry* Entry = Dict::FindUL(OperationalPattern.Value());
656 DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n", OperationalPattern.EncodeString(strbuf, IdentBufferLen));
658 DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n", Entry->name);
661 // slurp up the remainder of the header
662 if ( ASDCP_SUCCESS(result) )
664 if ( HeaderByteCount < 1024 )
665 DefaultLogSink().Warn("Improbably small HeaderByteCount value: %u\n", HeaderByteCount);
667 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_SUCCESS(result) && read_count != m_Buffer.Capacity() )
677 DefaultLogSink().Error("Short read of OP-Atom header metadata; wanted %u, got %u\n",
678 m_Buffer.Capacity(), read_count);
683 const byte_t* p = m_Buffer.RoData();
684 const byte_t* end_p = p + m_Buffer.Capacity();
686 while ( ASDCP_SUCCESS(result) && p < end_p )
688 // parse the packets and index them by uid, discard KLVFill items
689 InterchangeObject* object = CreateObject(p);
692 object->m_Lookup = &m_Primer;
693 result = object->InitFromBuffer(p, end_p - p);
694 const byte_t* redo_p = p;
695 p += object->PacketLength();
696 // hexdump(p, object->PacketLength());
698 if ( ASDCP_SUCCESS(result) )
700 if ( object->IsA(Dict::ul(MDD_KLVFill)) )
704 else if ( object->IsA(Dict::ul(MDD_Primer)) )
707 result = m_Primer.InitFromBuffer(redo_p, end_p - redo_p);
709 else if ( object->IsA(Dict::ul(MDD_Preface)) )
711 assert(m_Preface == 0);
712 m_Preface = (Preface*)object;
716 m_PacketList->AddPacket(object);
721 DefaultLogSink().Error("Error initializing packet\n");
731 ASDCP::MXF::OPAtomHeader::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
733 InterchangeObject* TmpObject;
738 return m_PacketList->GetMDObjectByType(ObjectID, Object);
742 ASDCP::MXF::Identification*
743 ASDCP::MXF::OPAtomHeader::GetIdentification()
745 InterchangeObject* Object;
747 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object)) )
748 return (Identification*)Object;
754 ASDCP::MXF::SourcePackage*
755 ASDCP::MXF::OPAtomHeader::GetSourcePackage()
757 InterchangeObject* Object;
759 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object)) )
760 return (SourcePackage*)Object;
768 ASDCP::MXF::OPAtomHeader::WriteToFile(Kumu::FileWriter& Writer, ui32_t HeaderSize)
770 if ( m_Preface == 0 )
773 if ( HeaderSize < 4096 )
775 DefaultLogSink().Error("HeaderSize %u is too small. Must be >= 4096\n", HeaderSize);
779 ASDCP::FrameBuffer HeaderBuffer;
780 HeaderByteCount = HeaderSize - ArchiveSize();
781 Result_t result = HeaderBuffer.Capacity(HeaderByteCount);
782 m_Preface->m_Lookup = &m_Primer;
784 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
785 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
787 InterchangeObject* object = *pl_i;
788 object->m_Lookup = &m_Primer;
790 ASDCP::FrameBuffer WriteWrapper;
791 WriteWrapper.SetData(HeaderBuffer.Data() + HeaderBuffer.Size(),
792 HeaderBuffer.Capacity() - HeaderBuffer.Size());
793 result = object->WriteToBuffer(WriteWrapper);
794 HeaderBuffer.Size(HeaderBuffer.Size() + WriteWrapper.Size());
797 if ( ASDCP_SUCCESS(result) )
799 UL TmpUL(Dict::ul(MDD_ClosedCompleteHeader));
800 result = Partition::WriteToFile(Writer, TmpUL);
803 if ( ASDCP_SUCCESS(result) )
804 result = m_Primer.WriteToFile(Writer);
806 if ( ASDCP_SUCCESS(result) )
809 Writer.Write(HeaderBuffer.RoData(), HeaderBuffer.Size(), &write_count);
810 assert(write_count == HeaderBuffer.Size());
814 if ( ASDCP_SUCCESS(result) )
816 Kumu::fpos_t pos = Writer.Tell();
818 if ( pos > (Kumu::fpos_t)HeaderByteCount )
820 char intbuf[IntBufferLen];
821 DefaultLogSink().Error("Header size %s exceeds specified value %u\n",
827 ASDCP::FrameBuffer NilBuf;
828 ui32_t klv_fill_length = HeaderSize - (ui32_t)pos;
830 if ( klv_fill_length < kl_length )
832 DefaultLogSink().Error("Remaining region too small for KLV Fill header\n");
836 klv_fill_length -= kl_length;
837 result = WriteKLToFile(Writer, Dict::ul(MDD_KLVFill), klv_fill_length);
839 if ( ASDCP_SUCCESS(result) )
840 result = NilBuf.Capacity(klv_fill_length);
842 if ( ASDCP_SUCCESS(result) )
844 memset(NilBuf.Data(), 0, klv_fill_length);
846 Writer.Write(NilBuf.RoData(), klv_fill_length, &write_count);
847 assert(write_count == klv_fill_length);
856 ASDCP::MXF::OPAtomHeader::Dump(FILE* stream)
864 Partition::Dump(stream);
865 m_Primer.Dump(stream);
867 if ( m_Preface == 0 )
868 fputs("No Preface loaded\n", stream);
870 m_Preface->Dump(stream);
872 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
873 for ( ; i != m_PacketList->m_List.end(); i++ )
877 //------------------------------------------------------------------------------------------
880 ASDCP::MXF::OPAtomIndexFooter::OPAtomIndexFooter() :
881 m_CurrentSegment(0), m_BytesPerEditUnit(0), m_BodySID(0),
882 m_ECOffset(0), m_Lookup(0)
888 ASDCP::MXF::OPAtomIndexFooter::~OPAtomIndexFooter() {}
892 ASDCP::MXF::OPAtomIndexFooter::InitFromFile(const Kumu::FileReader& Reader)
894 Result_t result = Partition::InitFromFile(Reader); // test UL and OP
896 // slurp up the remainder of the footer
899 if ( ASDCP_SUCCESS(result) )
900 result = m_Buffer.Capacity(IndexByteCount);
902 if ( ASDCP_SUCCESS(result) )
903 result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
905 if ( ASDCP_SUCCESS(result) && read_count != m_Buffer.Capacity() )
907 DefaultLogSink().Error("Short read of footer partition: got %u, expecting %u\n",
908 read_count, m_Buffer.Capacity());
912 const byte_t* p = m_Buffer.RoData();
913 const byte_t* end_p = p + m_Buffer.Capacity();
915 while ( ASDCP_SUCCESS(result) && p < end_p )
917 // parse the packets and index them by uid, discard KLVFill items
918 InterchangeObject* object = CreateObject(p);
921 object->m_Lookup = m_Lookup;
922 result = object->InitFromBuffer(p, end_p - p);
923 p += object->PacketLength();
925 if ( ASDCP_SUCCESS(result) )
927 m_PacketList->AddPacket(object);
931 DefaultLogSink().Error("Error initializing packet\n");
936 if ( ASDCP_FAILURE(result) )
937 DefaultLogSink().Error("Failed to initialize OPAtomIndexFooter\n");
944 ASDCP::MXF::OPAtomIndexFooter::WriteToFile(Kumu::FileWriter& Writer, ui64_t duration)
946 ASDCP::FrameBuffer FooterBuffer;
947 ui32_t footer_size = m_PacketList->m_List.size() * MaxIndexSegmentSize; // segment-count * max-segment-size
948 Result_t result = FooterBuffer.Capacity(footer_size);
949 ui32_t iseg_count = 0;
951 if ( m_CurrentSegment != 0 )
953 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
954 m_CurrentSegment = 0;
957 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
958 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
960 if ( (*pl_i)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
963 IndexTableSegment* Segment = (IndexTableSegment*)(*pl_i);
965 if ( m_BytesPerEditUnit != 0 )
967 if ( iseg_count != 1 )
970 Segment->IndexDuration = duration;
974 InterchangeObject* object = *pl_i;
975 object->m_Lookup = m_Lookup;
977 ASDCP::FrameBuffer WriteWrapper;
978 WriteWrapper.SetData(FooterBuffer.Data() + FooterBuffer.Size(),
979 FooterBuffer.Capacity() - FooterBuffer.Size());
980 result = object->WriteToBuffer(WriteWrapper);
981 FooterBuffer.Size(FooterBuffer.Size() + WriteWrapper.Size());
984 if ( ASDCP_SUCCESS(result) )
986 IndexByteCount = FooterBuffer.Size();
987 UL FooterUL(Dict::ul(MDD_CompleteFooter));
988 result = Partition::WriteToFile(Writer, FooterUL);
991 if ( ASDCP_SUCCESS(result) )
994 Writer.Write(FooterBuffer.RoData(), FooterBuffer.Size(), &write_count);
995 assert(write_count == FooterBuffer.Size());
1003 ASDCP::MXF::OPAtomIndexFooter::Dump(FILE* stream)
1008 Partition::Dump(stream);
1010 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
1011 for ( ; i != m_PacketList->m_List.end(); i++ )
1017 ASDCP::MXF::OPAtomIndexFooter::Lookup(ui32_t frame_num, IndexTableSegment::IndexEntry& Entry)
1019 std::list<InterchangeObject*>::iterator li;
1020 for ( li = m_PacketList->m_List.begin(); li != m_PacketList->m_List.end(); li++ )
1022 if ( (*li)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1024 IndexTableSegment* Segment = (IndexTableSegment*)(*li);
1025 ui64_t start_pos = Segment->IndexStartPosition;
1027 if ( Segment->EditUnitByteCount > 0 )
1029 if ( m_PacketList->m_List.size() > 1 )
1030 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
1032 if ( ! Segment->IndexEntryArray.empty() )
1033 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
1035 Entry.StreamOffset = (ui64_t)frame_num * Segment->EditUnitByteCount;
1038 else if ( (ui64_t)frame_num >= start_pos
1039 && (ui64_t)frame_num < (start_pos + Segment->IndexDuration) )
1041 Entry = Segment->IndexEntryArray[frame_num-start_pos];
1052 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const Rational& Rate)
1056 m_BytesPerEditUnit = size;
1059 IndexTableSegment* Index = new IndexTableSegment;
1060 AddChildObject(Index);
1061 Index->EditUnitByteCount = m_BytesPerEditUnit;
1062 Index->IndexEditRate = Rate;
1067 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsVBR(IPrimerLookup* lookup, const Rational& Rate, Kumu::fpos_t offset)
1071 m_BytesPerEditUnit = 0;
1073 m_ECOffset = offset;
1078 ASDCP::MXF::OPAtomIndexFooter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
1080 if ( m_BytesPerEditUnit != 0 ) // are we CBR? that's bad
1082 DefaultLogSink().Error("Call to PushIndexEntry() failed: index is CBR\n");
1086 // do we have an available segment?
1087 if ( m_CurrentSegment == 0 )
1088 { // no, set up a new segment
1089 m_CurrentSegment = new IndexTableSegment;
1090 assert(m_CurrentSegment);
1091 AddChildObject(m_CurrentSegment);
1092 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1093 m_CurrentSegment->IndexEditRate = m_EditRate;
1094 m_CurrentSegment->IndexStartPosition = 0;
1096 else if ( m_CurrentSegment->IndexEntryArray.size() >= 1486 ) // 1486 gets us 16K packets
1097 { // no, this one is full, start another
1098 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1099 ui64_t StartPosition = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
1101 m_CurrentSegment = new IndexTableSegment;
1102 assert(m_CurrentSegment);
1103 AddChildObject(m_CurrentSegment);
1104 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1105 m_CurrentSegment->IndexEditRate = m_EditRate;
1106 m_CurrentSegment->IndexStartPosition = StartPosition;
1109 m_CurrentSegment->IndexEntryArray.push_back(Entry);
1112 //------------------------------------------------------------------------------------------
1117 ASDCP::MXF::InterchangeObject::InitFromTLVSet(TLVReader& TLVSet)
1119 Result_t result = TLVSet.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID));
1120 if ( ASDCP_SUCCESS(result) )
1121 result = TLVSet.ReadObject(OBJ_READ_ARGS(GenerationInterchangeObject, GenerationUID));
1127 ASDCP::MXF::InterchangeObject::WriteToTLVSet(TLVWriter& TLVSet)
1129 Result_t result = TLVSet.WriteObject(OBJ_WRITE_ARGS(InterchangeObject, InstanceUID));
1130 if ( ASDCP_SUCCESS(result) )
1131 result = TLVSet.WriteObject(OBJ_WRITE_ARGS(GenerationInterchangeObject, GenerationUID));
1137 ASDCP::MXF::InterchangeObject::InitFromBuffer(const byte_t* p, ui32_t l)
1140 Result_t result = RESULT_FALSE;
1142 if ( m_Typeinfo == 0 )
1144 result = KLVPacket::InitFromBuffer(p, l);
1148 result = KLVPacket::InitFromBuffer(p, l, m_Typeinfo->ul);
1150 if ( ASDCP_SUCCESS(result) )
1152 TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup);
1153 result = InitFromTLVSet(MemRDR);
1162 ASDCP::MXF::InterchangeObject::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
1164 if ( m_Typeinfo == 0 )
1165 return RESULT_STATE;
1167 TLVWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length, m_Lookup);
1168 Result_t result = WriteToTLVSet(MemWRT);
1170 if ( ASDCP_SUCCESS(result) )
1172 ui32_t packet_length = MemWRT.Length();
1173 result = WriteKLToBuffer(Buffer, m_Typeinfo->ul, packet_length);
1175 if ( ASDCP_SUCCESS(result) )
1176 Buffer.Size(Buffer.Size() + packet_length);
1184 ASDCP::MXF::InterchangeObject::Dump(FILE* stream)
1186 char identbuf[IdentBufferLen];
1188 fputc('\n', stream);
1189 KLVPacket::Dump(stream, false);
1190 fprintf(stream, " InstanceUID = %s\n", InstanceUID.EncodeHex(identbuf, IdentBufferLen));
1191 fprintf(stream, " GenerationUID = %s\n", GenerationUID.EncodeHex(identbuf, IdentBufferLen));
1196 ASDCP::MXF::InterchangeObject::IsA(const byte_t* label)
1198 if ( m_KLLength == 0 )
1201 return ( memcmp(label, m_KeyStart, SMPTE_UL_LENGTH) == 0 );