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;
635 if ( ASDCP_SUCCESS(result) )
637 Array<RIP::Pair>::iterator r_i = m_RIP.PairArray.begin();
639 if ( (*r_i).ByteOffset != 0 )
641 DefaultLogSink().Error("First Partition in RIP is not at offset 0.\n");
642 result = RESULT_FORMAT;
646 if ( ASDCP_SUCCESS(result) )
647 result = Reader.Seek(0);
649 if ( ASDCP_SUCCESS(result) )
650 result = Partition::InitFromFile(Reader); // test UL and OP
652 // is it really OP-Atom?
653 UL OPAtomUL(Dict::ul(MDD_OPAtom));
654 UL InteropOPAtomUL(Dict::ul(MDD_MXFInterop_OPAtom));
656 if ( ! ( OperationalPattern == OPAtomUL || OperationalPattern == InteropOPAtomUL ) )
658 char strbuf[IdentBufferLen];
659 const MDDEntry* Entry = Dict::FindUL(OperationalPattern.Value());
661 DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n", OperationalPattern.EncodeString(strbuf, IdentBufferLen));
663 DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n", Entry->name);
666 // slurp up the remainder of the header
667 if ( ASDCP_SUCCESS(result) )
669 if ( HeaderByteCount < 1024 )
670 DefaultLogSink().Warn("Improbably small HeaderByteCount value: %u\n", HeaderByteCount);
672 result = m_Buffer.Capacity(HeaderByteCount);
675 if ( ASDCP_SUCCESS(result) )
678 result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
680 if ( ASDCP_SUCCESS(result) && read_count != m_Buffer.Capacity() )
682 DefaultLogSink().Error("Short read of OP-Atom header metadata; wanted %u, got %u\n",
683 m_Buffer.Capacity(), read_count);
688 const byte_t* p = m_Buffer.RoData();
689 const byte_t* end_p = p + m_Buffer.Capacity();
691 while ( ASDCP_SUCCESS(result) && p < end_p )
693 // parse the packets and index them by uid, discard KLVFill items
694 InterchangeObject* object = CreateObject(p);
697 object->m_Lookup = &m_Primer;
698 result = object->InitFromBuffer(p, end_p - p);
699 const byte_t* redo_p = p;
700 p += object->PacketLength();
701 // hexdump(p, object->PacketLength());
703 if ( ASDCP_SUCCESS(result) )
705 if ( object->IsA(Dict::ul(MDD_KLVFill)) )
709 else if ( object->IsA(Dict::ul(MDD_Primer)) )
712 result = m_Primer.InitFromBuffer(redo_p, end_p - redo_p);
714 else if ( object->IsA(Dict::ul(MDD_Preface)) )
716 assert(m_Preface == 0);
717 m_Preface = (Preface*)object;
721 m_PacketList->AddPacket(object);
726 DefaultLogSink().Error("Error initializing packet\n");
736 ASDCP::MXF::OPAtomHeader::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
738 InterchangeObject* TmpObject;
743 return m_PacketList->GetMDObjectByType(ObjectID, Object);
747 ASDCP::MXF::Identification*
748 ASDCP::MXF::OPAtomHeader::GetIdentification()
750 InterchangeObject* Object;
752 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object)) )
753 return (Identification*)Object;
759 ASDCP::MXF::SourcePackage*
760 ASDCP::MXF::OPAtomHeader::GetSourcePackage()
762 InterchangeObject* Object;
764 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object)) )
765 return (SourcePackage*)Object;
773 ASDCP::MXF::OPAtomHeader::WriteToFile(Kumu::FileWriter& Writer, ui32_t HeaderSize)
775 if ( m_Preface == 0 )
778 if ( HeaderSize < 4096 )
780 DefaultLogSink().Error("HeaderSize %u is too small. Must be >= 4096\n", HeaderSize);
784 ASDCP::FrameBuffer HeaderBuffer;
785 HeaderByteCount = HeaderSize - ArchiveSize();
786 Result_t result = HeaderBuffer.Capacity(HeaderByteCount);
787 m_Preface->m_Lookup = &m_Primer;
789 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
790 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
792 InterchangeObject* object = *pl_i;
793 object->m_Lookup = &m_Primer;
795 ASDCP::FrameBuffer WriteWrapper;
796 WriteWrapper.SetData(HeaderBuffer.Data() + HeaderBuffer.Size(),
797 HeaderBuffer.Capacity() - HeaderBuffer.Size());
798 result = object->WriteToBuffer(WriteWrapper);
799 HeaderBuffer.Size(HeaderBuffer.Size() + WriteWrapper.Size());
802 if ( ASDCP_SUCCESS(result) )
804 UL TmpUL(Dict::ul(MDD_ClosedCompleteHeader));
805 result = Partition::WriteToFile(Writer, TmpUL);
808 if ( ASDCP_SUCCESS(result) )
809 result = m_Primer.WriteToFile(Writer);
811 if ( ASDCP_SUCCESS(result) )
814 Writer.Write(HeaderBuffer.RoData(), HeaderBuffer.Size(), &write_count);
815 assert(write_count == HeaderBuffer.Size());
819 if ( ASDCP_SUCCESS(result) )
821 Kumu::fpos_t pos = Writer.Tell();
823 if ( pos > (Kumu::fpos_t)HeaderByteCount )
825 char intbuf[IntBufferLen];
826 DefaultLogSink().Error("Header size %s exceeds specified value %u\n",
832 ASDCP::FrameBuffer NilBuf;
833 ui32_t klv_fill_length = HeaderSize - (ui32_t)pos;
835 if ( klv_fill_length < kl_length )
837 DefaultLogSink().Error("Remaining region too small for KLV Fill header\n");
841 klv_fill_length -= kl_length;
842 result = WriteKLToFile(Writer, Dict::ul(MDD_KLVFill), klv_fill_length);
844 if ( ASDCP_SUCCESS(result) )
845 result = NilBuf.Capacity(klv_fill_length);
847 if ( ASDCP_SUCCESS(result) )
849 memset(NilBuf.Data(), 0, klv_fill_length);
851 Writer.Write(NilBuf.RoData(), klv_fill_length, &write_count);
852 assert(write_count == klv_fill_length);
861 ASDCP::MXF::OPAtomHeader::Dump(FILE* stream)
869 Partition::Dump(stream);
870 m_Primer.Dump(stream);
872 if ( m_Preface == 0 )
873 fputs("No Preface loaded\n", stream);
875 m_Preface->Dump(stream);
877 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
878 for ( ; i != m_PacketList->m_List.end(); i++ )
882 //------------------------------------------------------------------------------------------
885 ASDCP::MXF::OPAtomIndexFooter::OPAtomIndexFooter() :
886 m_CurrentSegment(0), m_BytesPerEditUnit(0), m_BodySID(0),
887 m_ECOffset(0), m_Lookup(0)
893 ASDCP::MXF::OPAtomIndexFooter::~OPAtomIndexFooter() {}
897 ASDCP::MXF::OPAtomIndexFooter::InitFromFile(const Kumu::FileReader& Reader)
899 Result_t result = Partition::InitFromFile(Reader); // test UL and OP
901 // slurp up the remainder of the footer
904 if ( ASDCP_SUCCESS(result) )
905 result = m_Buffer.Capacity(IndexByteCount);
907 if ( ASDCP_SUCCESS(result) )
908 result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
910 if ( ASDCP_SUCCESS(result) && read_count != m_Buffer.Capacity() )
912 DefaultLogSink().Error("Short read of footer partition: got %u, expecting %u\n",
913 read_count, m_Buffer.Capacity());
917 const byte_t* p = m_Buffer.RoData();
918 const byte_t* end_p = p + m_Buffer.Capacity();
920 while ( ASDCP_SUCCESS(result) && p < end_p )
922 // parse the packets and index them by uid, discard KLVFill items
923 InterchangeObject* object = CreateObject(p);
926 object->m_Lookup = m_Lookup;
927 result = object->InitFromBuffer(p, end_p - p);
928 p += object->PacketLength();
930 if ( ASDCP_SUCCESS(result) )
932 m_PacketList->AddPacket(object);
936 DefaultLogSink().Error("Error initializing packet\n");
941 if ( ASDCP_FAILURE(result) )
942 DefaultLogSink().Error("Failed to initialize OPAtomIndexFooter\n");
949 ASDCP::MXF::OPAtomIndexFooter::WriteToFile(Kumu::FileWriter& Writer, ui64_t duration)
951 ASDCP::FrameBuffer FooterBuffer;
952 ui32_t footer_size = m_PacketList->m_List.size() * MaxIndexSegmentSize; // segment-count * max-segment-size
953 Result_t result = FooterBuffer.Capacity(footer_size);
954 ui32_t iseg_count = 0;
956 if ( m_CurrentSegment != 0 )
958 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
959 m_CurrentSegment = 0;
962 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
963 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
965 if ( (*pl_i)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
968 IndexTableSegment* Segment = (IndexTableSegment*)(*pl_i);
970 if ( m_BytesPerEditUnit != 0 )
972 if ( iseg_count != 1 )
975 Segment->IndexDuration = duration;
979 InterchangeObject* object = *pl_i;
980 object->m_Lookup = m_Lookup;
982 ASDCP::FrameBuffer WriteWrapper;
983 WriteWrapper.SetData(FooterBuffer.Data() + FooterBuffer.Size(),
984 FooterBuffer.Capacity() - FooterBuffer.Size());
985 result = object->WriteToBuffer(WriteWrapper);
986 FooterBuffer.Size(FooterBuffer.Size() + WriteWrapper.Size());
989 if ( ASDCP_SUCCESS(result) )
991 IndexByteCount = FooterBuffer.Size();
992 UL FooterUL(Dict::ul(MDD_CompleteFooter));
993 result = Partition::WriteToFile(Writer, FooterUL);
996 if ( ASDCP_SUCCESS(result) )
999 Writer.Write(FooterBuffer.RoData(), FooterBuffer.Size(), &write_count);
1000 assert(write_count == FooterBuffer.Size());
1008 ASDCP::MXF::OPAtomIndexFooter::Dump(FILE* stream)
1013 Partition::Dump(stream);
1015 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
1016 for ( ; i != m_PacketList->m_List.end(); i++ )
1022 ASDCP::MXF::OPAtomIndexFooter::Lookup(ui32_t frame_num, IndexTableSegment::IndexEntry& Entry)
1024 std::list<InterchangeObject*>::iterator li;
1025 for ( li = m_PacketList->m_List.begin(); li != m_PacketList->m_List.end(); li++ )
1027 if ( (*li)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1029 IndexTableSegment* Segment = (IndexTableSegment*)(*li);
1030 ui64_t start_pos = Segment->IndexStartPosition;
1032 if ( Segment->EditUnitByteCount > 0 )
1034 if ( m_PacketList->m_List.size() > 1 )
1035 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
1037 if ( ! Segment->IndexEntryArray.empty() )
1038 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
1040 Entry.StreamOffset = (ui64_t)frame_num * Segment->EditUnitByteCount;
1043 else if ( (ui64_t)frame_num >= start_pos
1044 && (ui64_t)frame_num < (start_pos + Segment->IndexDuration) )
1046 Entry = Segment->IndexEntryArray[frame_num-start_pos];
1057 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const Rational& Rate)
1061 m_BytesPerEditUnit = size;
1064 IndexTableSegment* Index = new IndexTableSegment;
1065 AddChildObject(Index);
1066 Index->EditUnitByteCount = m_BytesPerEditUnit;
1067 Index->IndexEditRate = Rate;
1072 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsVBR(IPrimerLookup* lookup, const Rational& Rate, Kumu::fpos_t offset)
1076 m_BytesPerEditUnit = 0;
1078 m_ECOffset = offset;
1083 ASDCP::MXF::OPAtomIndexFooter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
1085 if ( m_BytesPerEditUnit != 0 ) // are we CBR? that's bad
1087 DefaultLogSink().Error("Call to PushIndexEntry() failed: index is CBR\n");
1091 // do we have an available segment?
1092 if ( m_CurrentSegment == 0 )
1093 { // no, set up a new segment
1094 m_CurrentSegment = new IndexTableSegment;
1095 assert(m_CurrentSegment);
1096 AddChildObject(m_CurrentSegment);
1097 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1098 m_CurrentSegment->IndexEditRate = m_EditRate;
1099 m_CurrentSegment->IndexStartPosition = 0;
1101 else if ( m_CurrentSegment->IndexEntryArray.size() >= CBRIndexEntriesPerSegment )
1102 { // no, this one is full, start another
1103 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1104 ui64_t StartPosition = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
1106 m_CurrentSegment = new IndexTableSegment;
1107 assert(m_CurrentSegment);
1108 AddChildObject(m_CurrentSegment);
1109 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1110 m_CurrentSegment->IndexEditRate = m_EditRate;
1111 m_CurrentSegment->IndexStartPosition = StartPosition;
1114 m_CurrentSegment->IndexEntryArray.push_back(Entry);
1117 //------------------------------------------------------------------------------------------
1122 ASDCP::MXF::InterchangeObject::InitFromTLVSet(TLVReader& TLVSet)
1124 Result_t result = TLVSet.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID));
1125 if ( ASDCP_SUCCESS(result) )
1126 result = TLVSet.ReadObject(OBJ_READ_ARGS(GenerationInterchangeObject, GenerationUID));
1132 ASDCP::MXF::InterchangeObject::WriteToTLVSet(TLVWriter& TLVSet)
1134 Result_t result = TLVSet.WriteObject(OBJ_WRITE_ARGS(InterchangeObject, InstanceUID));
1135 if ( ASDCP_SUCCESS(result) )
1136 result = TLVSet.WriteObject(OBJ_WRITE_ARGS(GenerationInterchangeObject, GenerationUID));
1142 ASDCP::MXF::InterchangeObject::InitFromBuffer(const byte_t* p, ui32_t l)
1145 Result_t result = RESULT_FALSE;
1147 if ( m_Typeinfo == 0 )
1149 result = KLVPacket::InitFromBuffer(p, l);
1153 result = KLVPacket::InitFromBuffer(p, l, m_Typeinfo->ul);
1155 if ( ASDCP_SUCCESS(result) )
1157 TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup);
1158 result = InitFromTLVSet(MemRDR);
1167 ASDCP::MXF::InterchangeObject::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
1169 if ( m_Typeinfo == 0 )
1170 return RESULT_STATE;
1172 TLVWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length, m_Lookup);
1173 Result_t result = WriteToTLVSet(MemWRT);
1175 if ( ASDCP_SUCCESS(result) )
1177 ui32_t packet_length = MemWRT.Length();
1178 result = WriteKLToBuffer(Buffer, m_Typeinfo->ul, packet_length);
1180 if ( ASDCP_SUCCESS(result) )
1181 Buffer.Size(Buffer.Size() + packet_length);
1189 ASDCP::MXF::InterchangeObject::Dump(FILE* stream)
1191 char identbuf[IdentBufferLen];
1193 fputc('\n', stream);
1194 KLVPacket::Dump(stream, false);
1195 fprintf(stream, " InstanceUID = %s\n", InstanceUID.EncodeHex(identbuf, IdentBufferLen));
1196 fprintf(stream, " GenerationUID = %s\n", GenerationUID.EncodeHex(identbuf, IdentBufferLen));
1201 ASDCP::MXF::InterchangeObject::IsA(const byte_t* label)
1203 if ( m_KLLength == 0 )
1206 return ( memcmp(label, m_KeyStart, SMPTE_UL_LENGTH) == 0 );