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 // 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 Kumu::GenRandomValue(TmpID);
224 Object->InstanceUID = TmpID;
225 m_PacketList->AddPacket(Object);
230 ASDCP::MXF::Partition::InitFromFile(const Kumu::FileReader& Reader)
232 Result_t result = KLVFilePacket::InitFromFile(Reader);
234 // could be one of several values
236 if ( ASDCP_SUCCESS(result) )
238 Kumu::MemIOReader MemRDR(m_ValueStart, m_ValueLength);
239 result = RESULT_KLV_CODING;
241 if ( MemRDR.ReadUi16BE(&MajorVersion) )
242 if ( MemRDR.ReadUi16BE(&MinorVersion) )
243 if ( MemRDR.ReadUi32BE(&KAGSize) )
244 if ( MemRDR.ReadUi64BE(&ThisPartition) )
245 if ( MemRDR.ReadUi64BE(&PreviousPartition) )
246 if ( MemRDR.ReadUi64BE(&FooterPartition) )
247 if ( MemRDR.ReadUi64BE(&HeaderByteCount) )
248 if ( MemRDR.ReadUi64BE(&IndexByteCount) )
249 if ( MemRDR.ReadUi32BE(&IndexSID) )
250 if ( MemRDR.ReadUi64BE(&BodyOffset) )
251 if ( MemRDR.ReadUi32BE(&BodySID) )
252 if ( OperationalPattern.Unarchive(&MemRDR) )
253 if ( EssenceContainers.Unarchive(&MemRDR) )
257 if ( ASDCP_FAILURE(result) )
258 DefaultLogSink().Error("Failed to initialize Partition\n");
265 ASDCP::MXF::Partition::WriteToFile(Kumu::FileWriter& Writer, UL& PartitionLabel)
267 ASDCP::FrameBuffer Buffer;
268 Result_t result = Buffer.Capacity(1024);
270 if ( ASDCP_SUCCESS(result) )
272 Kumu::MemIOWriter MemWRT(Buffer.Data(), Buffer.Capacity());
273 result = RESULT_KLV_CODING;
274 if ( MemWRT.WriteUi16BE(MajorVersion) )
275 if ( MemWRT.WriteUi16BE(MinorVersion) )
276 if ( MemWRT.WriteUi32BE(KAGSize) )
277 if ( MemWRT.WriteUi64BE(ThisPartition) )
278 if ( MemWRT.WriteUi64BE(PreviousPartition) )
279 if ( MemWRT.WriteUi64BE(FooterPartition) )
280 if ( MemWRT.WriteUi64BE(HeaderByteCount) )
281 if ( MemWRT.WriteUi64BE(IndexByteCount) )
282 if ( MemWRT.WriteUi32BE(IndexSID) )
283 if ( MemWRT.WriteUi64BE(BodyOffset) )
284 if ( MemWRT.WriteUi32BE(BodySID) )
285 if ( OperationalPattern.Archive(&MemWRT) )
286 if ( EssenceContainers.Archive(&MemWRT) )
288 Buffer.Size(MemWRT.Length());
293 if ( ASDCP_SUCCESS(result) )
296 result = WriteKLToFile(Writer, PartitionLabel.Value(), Buffer.Size());
298 if ( ASDCP_SUCCESS(result) )
299 result = Writer.Write(Buffer.RoData(), Buffer.Size(), &write_count);
307 ASDCP::MXF::Partition::ArchiveSize()
310 + sizeof(ui16_t) + sizeof(ui16_t)
312 + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t)
317 + sizeof(ui32_t) + sizeof(ui32_t) + ( UUIDlen * EssenceContainers.size() ) );
322 ASDCP::MXF::Partition::Dump(FILE* stream)
324 char identbuf[IdentBufferLen];
329 KLVFilePacket::Dump(stream, false);
330 fprintf(stream, " MajorVersion = %hu\n", MajorVersion);
331 fprintf(stream, " MinorVersion = %hu\n", MinorVersion);
332 fprintf(stream, " KAGSize = %u\n", KAGSize);
333 fprintf(stream, " ThisPartition = %s\n", ui64sz(ThisPartition, identbuf));
334 fprintf(stream, " PreviousPartition = %s\n", ui64sz(PreviousPartition, identbuf));
335 fprintf(stream, " FooterPartition = %s\n", ui64sz(FooterPartition, identbuf));
336 fprintf(stream, " HeaderByteCount = %s\n", ui64sz(HeaderByteCount, identbuf));
337 fprintf(stream, " IndexByteCount = %s\n", ui64sz(IndexByteCount, identbuf));
338 fprintf(stream, " IndexSID = %u\n", IndexSID);
339 fprintf(stream, " BodyOffset = %s\n", ui64sz(BodyOffset, identbuf));
340 fprintf(stream, " BodySID = %u\n", BodySID);
341 fprintf(stream, " OperationalPattern = %s\n", OperationalPattern.EncodeString(identbuf, IdentBufferLen));
342 fputs("Essence Containers:\n", stream); EssenceContainers.Dump(stream, false);
344 fputs("==========================================================================\n", stream);
348 //------------------------------------------------------------------------------------------
351 class ASDCP::MXF::Primer::h__PrimerLookup : public std::map<UL, TagValue>
354 void InitWithBatch(ASDCP::MXF::Batch<ASDCP::MXF::Primer::LocalTagEntry>& Batch)
356 ASDCP::MXF::Batch<ASDCP::MXF::Primer::LocalTagEntry>::iterator i = Batch.begin();
358 for ( ; i != Batch.end(); i++ )
359 insert(std::map<UL, TagValue>::value_type((*i).UL, (*i).Tag));
365 ASDCP::MXF::Primer::Primer() : m_LocalTag(0xff) {}
368 ASDCP::MXF::Primer::~Primer() {}
372 ASDCP::MXF::Primer::ClearTagList()
374 LocalTagEntryBatch.clear();
375 m_Lookup = new h__PrimerLookup;
380 ASDCP::MXF::Primer::InitFromBuffer(const byte_t* p, ui32_t l)
382 Result_t result = KLVPacket::InitFromBuffer(p, l, Dict::ul(MDD_Primer));
384 if ( ASDCP_SUCCESS(result) )
386 Kumu::MemIOReader MemRDR(m_ValueStart, m_ValueLength);
387 result = LocalTagEntryBatch.Unarchive(&MemRDR) ? RESULT_OK : RESULT_KLV_CODING;
390 if ( ASDCP_SUCCESS(result) )
392 m_Lookup = new h__PrimerLookup;
393 m_Lookup->InitWithBatch(LocalTagEntryBatch);
396 if ( ASDCP_FAILURE(result) )
397 DefaultLogSink().Error("Failed to initialize Primer\n");
404 ASDCP::MXF::Primer::WriteToFile(Kumu::FileWriter& Writer)
406 ASDCP::FrameBuffer Buffer;
407 Result_t result = Buffer.Capacity(128*1024);
409 if ( ASDCP_SUCCESS(result) )
410 result = WriteToBuffer(Buffer);
412 if ( ASDCP_SUCCESS(result) )
413 result = Writer.Write(Buffer.RoData(), Buffer.Size());
420 ASDCP::MXF::Primer::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
422 ASDCP::FrameBuffer LocalTagBuffer;
423 Kumu::MemIOWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length);
424 Result_t result = LocalTagEntryBatch.Archive(&MemWRT) ? RESULT_OK : RESULT_KLV_CODING;
426 if ( ASDCP_SUCCESS(result) )
428 ui32_t packet_length = MemWRT.Length();
429 result = WriteKLToBuffer(Buffer, Dict::ul(MDD_Primer), packet_length);
431 if ( ASDCP_SUCCESS(result) )
432 Buffer.Size(Buffer.Size() + packet_length);
440 ASDCP::MXF::Primer::InsertTag(const MDDEntry& Entry, ASDCP::TagValue& Tag)
444 std::map<UL, TagValue>::iterator i = m_Lookup->find(TestUL);
446 if ( i == m_Lookup->end() )
448 if ( Entry.tag.a == 0 && Entry.tag.b == 0 )
451 Tag.b = m_LocalTag--;
459 LocalTagEntry TmpEntry;
460 TmpEntry.UL = TestUL;
463 LocalTagEntryBatch.push_back(TmpEntry);
464 m_Lookup->insert(std::map<UL, TagValue>::value_type(TmpEntry.UL, TmpEntry.Tag));
476 ASDCP::MXF::Primer::TagForKey(const ASDCP::UL& Key, ASDCP::TagValue& Tag)
479 if ( m_Lookup.empty() )
481 DefaultLogSink().Error("Primer lookup is empty\n");
485 std::map<UL, TagValue>::iterator i = m_Lookup->find(Key);
487 if ( i == m_Lookup->end() )
496 ASDCP::MXF::Primer::Dump(FILE* stream)
498 char identbuf[IdentBufferLen];
503 KLVPacket::Dump(stream, false);
504 fprintf(stream, "Primer: %u %s\n",
505 LocalTagEntryBatch.size(),
506 ( LocalTagEntryBatch.size() == 1 ? "entry" : "entries" ));
508 Batch<LocalTagEntry>::iterator i = LocalTagEntryBatch.begin();
509 for ( ; i != LocalTagEntryBatch.end(); i++ )
511 const MDDEntry* Entry = Dict::FindUL((*i).UL.Value());
512 fprintf(stream, " %s %s\n", (*i).EncodeString(identbuf, IdentBufferLen), (Entry ? Entry->name : "Unknown"));
515 fputs("==========================================================================\n", stream);
519 //------------------------------------------------------------------------------------------
524 ASDCP::MXF::Preface::InitFromTLVSet(TLVReader& TLVSet)
526 Result_t result = InterchangeObject::InitFromTLVSet(TLVSet);
527 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, LastModifiedDate));
528 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi16(OBJ_READ_ARGS(Preface, Version));
529 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi32(OBJ_READ_ARGS(Preface, ObjectModelVersion));
530 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, PrimaryPackage));
531 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, Identifications));
532 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, ContentStorage));
533 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, OperationalPattern));
534 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, EssenceContainers));
535 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, DMSchemes));
541 ASDCP::MXF::Preface::WriteToTLVSet(TLVWriter& TLVSet)
543 Result_t result = InterchangeObject::WriteToTLVSet(TLVSet);
544 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, LastModifiedDate));
545 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi16(OBJ_WRITE_ARGS(Preface, Version));
546 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi32(OBJ_WRITE_ARGS(Preface, ObjectModelVersion));
547 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, PrimaryPackage));
548 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, Identifications));
549 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, ContentStorage));
550 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, OperationalPattern));
551 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, EssenceContainers));
552 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, DMSchemes));
558 ASDCP::MXF::Preface::InitFromBuffer(const byte_t* p, ui32_t l)
560 m_Typeinfo = &Dict::Type(MDD_Preface);
561 return InterchangeObject::InitFromBuffer(p, l);
566 ASDCP::MXF::Preface::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
568 m_Typeinfo = &Dict::Type(MDD_Preface);
569 return InterchangeObject::WriteToBuffer(Buffer);
574 ASDCP::MXF::Preface::Dump(FILE* stream)
576 char identbuf[IdentBufferLen];
581 InterchangeObject::Dump(stream);
582 fprintf(stream, " %22s = %s\n", "LastModifiedDate", LastModifiedDate.EncodeString(identbuf, IdentBufferLen));
583 fprintf(stream, " %22s = %hu\n", "Version", Version);
584 fprintf(stream, " %22s = %u\n", "ObjectModelVersion", ObjectModelVersion);
585 fprintf(stream, " %22s = %s\n", "PrimaryPackage", PrimaryPackage.EncodeHex(identbuf, IdentBufferLen));
586 fprintf(stream, " %22s:\n", "Identifications"); Identifications.Dump(stream);
587 fprintf(stream, " %22s = %s\n", "ContentStorage", ContentStorage.EncodeHex(identbuf, IdentBufferLen));
588 fprintf(stream, " %22s = %s\n", "OperationalPattern", OperationalPattern.EncodeString(identbuf, IdentBufferLen));
589 fprintf(stream, " %22s:\n", "EssenceContainers"); EssenceContainers.Dump(stream);
590 fprintf(stream, " %22s:\n", "DMSchemes"); DMSchemes.Dump(stream);
593 //------------------------------------------------------------------------------------------
596 ASDCP::MXF::OPAtomHeader::OPAtomHeader() : m_Preface(0), m_HasRIP(false) {}
597 ASDCP::MXF::OPAtomHeader::~OPAtomHeader() {}
601 ASDCP::MXF::OPAtomHeader::InitFromFile(const Kumu::FileReader& Reader)
604 Result_t result = SeekToRIP(Reader);
606 if ( ASDCP_SUCCESS(result) )
608 result = m_RIP.InitFromFile(Reader);
609 ui32_t test_s = m_RIP.PairArray.size();
611 if ( ASDCP_FAILURE(result) )
613 DefaultLogSink().Error("File contains no RIP\n");
616 else if ( test_s == 0 )
618 DefaultLogSink().Error("RIP contains no Pairs.\n");
619 result = RESULT_FORMAT;
621 else if ( test_s < 2 || test_s > 3 )
623 // OP-Atom states that there will be either two or three partitions,
624 // one closed header and one closed footer with an optional body
625 DefaultLogSink().Error("RIP count is not 2 or 3: %u\n", test_s);
626 return RESULT_FORMAT;
634 if ( ASDCP_SUCCESS(result) )
636 Array<RIP::Pair>::iterator r_i = m_RIP.PairArray.begin();
638 if ( (*r_i).ByteOffset != 0 )
640 DefaultLogSink().Error("First Partition in RIP is not at offset 0.\n");
641 result = RESULT_FORMAT;
645 if ( ASDCP_SUCCESS(result) )
646 result = Reader.Seek(0);
648 if ( ASDCP_SUCCESS(result) )
649 result = Partition::InitFromFile(Reader); // test UL and OP
651 // is it really OP-Atom?
652 UL OPAtomUL(Dict::ul(MDD_OPAtom));
653 UL InteropOPAtomUL(Dict::ul(MDD_MXFInterop_OPAtom));
655 if ( ! ( OperationalPattern == OPAtomUL || OperationalPattern == InteropOPAtomUL ) )
657 char strbuf[IdentBufferLen];
658 const MDDEntry* Entry = Dict::FindUL(OperationalPattern.Value());
660 DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n", OperationalPattern.EncodeString(strbuf, IdentBufferLen));
662 DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n", Entry->name);
665 // slurp up the remainder of the header
666 if ( ASDCP_SUCCESS(result) )
668 if ( HeaderByteCount < 1024 )
669 DefaultLogSink().Warn("Improbably small HeaderByteCount value: %u\n", HeaderByteCount);
671 result = m_Buffer.Capacity(HeaderByteCount);
674 if ( ASDCP_SUCCESS(result) )
677 result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
679 if ( ASDCP_SUCCESS(result) && read_count != m_Buffer.Capacity() )
681 DefaultLogSink().Error("Short read of OP-Atom header metadata; wanted %u, got %u\n",
682 m_Buffer.Capacity(), read_count);
687 const byte_t* p = m_Buffer.RoData();
688 const byte_t* end_p = p + m_Buffer.Capacity();
690 while ( ASDCP_SUCCESS(result) && p < end_p )
692 // parse the packets and index them by uid, discard KLVFill items
693 InterchangeObject* object = CreateObject(p);
696 object->m_Lookup = &m_Primer;
697 result = object->InitFromBuffer(p, end_p - p);
698 const byte_t* redo_p = p;
699 p += object->PacketLength();
700 // hexdump(p, object->PacketLength());
702 if ( ASDCP_SUCCESS(result) )
704 if ( object->IsA(Dict::ul(MDD_KLVFill)) )
708 else if ( object->IsA(Dict::ul(MDD_Primer)) )
711 result = m_Primer.InitFromBuffer(redo_p, end_p - redo_p);
713 else if ( object->IsA(Dict::ul(MDD_Preface)) )
715 assert(m_Preface == 0);
716 m_Preface = (Preface*)object;
720 m_PacketList->AddPacket(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 );