2 Copyright (c) 2005-2009, 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.
36 using Kumu::DefaultLogSink;
37 using Kumu::GenRandomValue;
39 // index segments must be < 64K
40 // NOTE: this value may too high if advanced index entry elements are used.
41 const ui32_t CBRIndexEntriesPerSegment = 5000;
43 //------------------------------------------------------------------------------------------
46 const ui32_t kl_length = ASDCP::SMPTE_UL_LENGTH + ASDCP::MXF_BER_LENGTH;
50 ASDCP::MXF::SeekToRIP(const Kumu::FileReader& Reader)
54 // go to the end - 4 bytes
55 Result_t result = Reader.Seek(0, Kumu::SP_END);
57 if ( ASDCP_SUCCESS(result) )
58 result = Reader.Tell(&end_pos);
60 if ( ASDCP_SUCCESS(result)
61 && end_pos < (SMPTE_UL_LENGTH+MXF_BER_LENGTH) )
62 result = RESULT_FAIL; // File is smaller than an empty packet!
64 if ( ASDCP_SUCCESS(result) )
65 result = Reader.Seek(end_pos - 4);
67 // get the ui32_t RIP length
69 byte_t intbuf[MXF_BER_LENGTH];
72 if ( ASDCP_SUCCESS(result) )
74 result = Reader.Read(intbuf, MXF_BER_LENGTH, &read_count);
76 if ( ASDCP_SUCCESS(result) && read_count != 4 )
80 if ( ASDCP_SUCCESS(result) )
82 rip_size = KM_i32_BE(Kumu::cp2i<ui32_t>(intbuf));
84 if ( rip_size > end_pos ) // RIP can't be bigger than the file
88 // reposition to start of RIP
89 if ( ASDCP_SUCCESS(result) )
90 result = Reader.Seek(end_pos - rip_size);
97 ASDCP::MXF::RIP::GetPairBySID(ui32_t SID, Pair& outPair) const
99 Array<Pair>::const_iterator pi = PairArray.begin();
100 for ( ; pi != PairArray.end(); pi++ )
102 if ( (*pi).BodySID == SID )
114 ASDCP::MXF::RIP::InitFromFile(const Kumu::FileReader& Reader)
116 Result_t result = KLVFilePacket::InitFromFile(Reader, m_Dict->ul(MDD_RandomIndexMetadata));
118 if ( ASDCP_SUCCESS(result) )
120 Kumu::MemIOReader MemRDR(m_ValueStart, m_ValueLength - 4);
121 result = PairArray.Unarchive(&MemRDR) ? RESULT_OK : RESULT_KLV_CODING;
124 if ( ASDCP_FAILURE(result) )
125 DefaultLogSink().Error("Failed to initialize RIP\n");
132 ASDCP::MXF::RIP::WriteToFile(Kumu::FileWriter& Writer)
134 ASDCP::FrameBuffer Buffer;
135 ui32_t RIPSize = ( PairArray.size() * (sizeof(ui32_t) + sizeof(ui64_t)) ) + 4;
136 Result_t result = Buffer.Capacity(RIPSize);
138 if ( ASDCP_SUCCESS(result) )
139 result = WriteKLToFile(Writer, m_Dict->ul(MDD_RandomIndexMetadata), RIPSize);
141 if ( ASDCP_SUCCESS(result) )
143 result = RESULT_KLV_CODING;
145 Kumu::MemIOWriter MemWRT(Buffer.Data(), Buffer.Capacity());
146 if ( PairArray.Archive(&MemWRT) )
147 if ( MemWRT.WriteUi32BE(RIPSize + 20) )
149 Buffer.Size(MemWRT.Length());
154 if ( ASDCP_SUCCESS(result) )
155 result = Writer.Write(Buffer.RoData(), Buffer.Size());
162 ASDCP::MXF::RIP::Dump(FILE* stream)
167 KLVFilePacket::Dump(stream, *m_Dict, false);
168 PairArray.Dump(stream, false);
171 //------------------------------------------------------------------------------------------
175 class ASDCP::MXF::Partition::h__PacketList
178 std::list<InterchangeObject*> m_List;
179 std::map<UUID, InterchangeObject*> m_Map;
182 while ( ! m_List.empty() )
184 delete m_List.back();
190 void AddPacket(InterchangeObject* ThePacket)
193 m_Map.insert(std::map<UUID, InterchangeObject*>::value_type(ThePacket->InstanceUID, ThePacket));
194 m_List.push_back(ThePacket);
198 Result_t GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
200 ASDCP_TEST_NULL(Object);
202 std::map<UUID, InterchangeObject*>::iterator mi = m_Map.find(ObjectID);
204 if ( mi == m_Map.end() )
210 *Object = (*mi).second;
215 Result_t GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
217 ASDCP_TEST_NULL(ObjectID);
218 ASDCP_TEST_NULL(Object);
219 std::list<InterchangeObject*>::iterator li;
222 for ( li = m_List.begin(); li != m_List.end(); li++ )
224 if ( (*li)->HasUL(ObjectID) )
235 Result_t GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
237 ASDCP_TEST_NULL(ObjectID);
238 std::list<InterchangeObject*>::iterator li;
240 for ( li = m_List.begin(); li != m_List.end(); li++ )
242 if ( (*li)->HasUL(ObjectID) )
243 ObjectList.push_back(*li);
246 return ObjectList.empty() ? RESULT_FAIL : RESULT_OK;
250 //------------------------------------------------------------------------------------------
254 ASDCP::MXF::Partition::Partition(const Dictionary*& d) :
256 MajorVersion(1), MinorVersion(2),
257 KAGSize(1), ThisPartition(0), PreviousPartition(0),
258 FooterPartition(0), HeaderByteCount(0), IndexByteCount(0), IndexSID(0),
259 BodyOffset(0), BodySID(0)
261 m_PacketList = new h__PacketList;
264 ASDCP::MXF::Partition::~Partition()
270 ASDCP::MXF::Partition::AddChildObject(InterchangeObject* Object)
274 if ( ! Object->InstanceUID.HasValue() )
275 GenRandomValue(Object->InstanceUID);
277 m_PacketList->AddPacket(Object);
282 ASDCP::MXF::Partition::InitFromFile(const Kumu::FileReader& Reader)
284 Result_t result = KLVFilePacket::InitFromFile(Reader);
286 // could be one of several values
287 if ( ASDCP_SUCCESS(result) )
288 result = ASDCP::MXF::Partition::InitFromBuffer(m_ValueStart, m_ValueLength);
295 ASDCP::MXF::Partition::InitFromBuffer(const byte_t* p, ui32_t l)
297 Kumu::MemIOReader MemRDR(p, l);
298 Result_t result = RESULT_KLV_CODING;
300 if ( MemRDR.ReadUi16BE(&MajorVersion) )
301 if ( MemRDR.ReadUi16BE(&MinorVersion) )
302 if ( MemRDR.ReadUi32BE(&KAGSize) )
303 if ( MemRDR.ReadUi64BE(&ThisPartition) )
304 if ( MemRDR.ReadUi64BE(&PreviousPartition) )
305 if ( MemRDR.ReadUi64BE(&FooterPartition) )
306 if ( MemRDR.ReadUi64BE(&HeaderByteCount) )
307 if ( MemRDR.ReadUi64BE(&IndexByteCount) )
308 if ( MemRDR.ReadUi32BE(&IndexSID) )
309 if ( MemRDR.ReadUi64BE(&BodyOffset) )
310 if ( MemRDR.ReadUi32BE(&BodySID) )
311 if ( OperationalPattern.Unarchive(&MemRDR) )
312 if ( EssenceContainers.Unarchive(&MemRDR) )
315 if ( ASDCP_FAILURE(result) )
316 DefaultLogSink().Error("Failed to initialize Partition\n");
323 ASDCP::MXF::Partition::WriteToFile(Kumu::FileWriter& Writer, UL& PartitionLabel)
325 ASDCP::FrameBuffer Buffer;
326 Result_t result = Buffer.Capacity(1024);
328 if ( ASDCP_SUCCESS(result) )
330 Kumu::MemIOWriter MemWRT(Buffer.Data(), Buffer.Capacity());
331 result = RESULT_KLV_CODING;
332 if ( MemWRT.WriteUi16BE(MajorVersion) )
333 if ( MemWRT.WriteUi16BE(MinorVersion) )
334 if ( MemWRT.WriteUi32BE(KAGSize) )
335 if ( MemWRT.WriteUi64BE(ThisPartition) )
336 if ( MemWRT.WriteUi64BE(PreviousPartition) )
337 if ( MemWRT.WriteUi64BE(FooterPartition) )
338 if ( MemWRT.WriteUi64BE(HeaderByteCount) )
339 if ( MemWRT.WriteUi64BE(IndexByteCount) )
340 if ( MemWRT.WriteUi32BE(IndexSID) )
341 if ( MemWRT.WriteUi64BE(BodyOffset) )
342 if ( MemWRT.WriteUi32BE(BodySID) )
343 if ( OperationalPattern.Archive(&MemWRT) )
344 if ( EssenceContainers.Archive(&MemWRT) )
346 Buffer.Size(MemWRT.Length());
351 if ( ASDCP_SUCCESS(result) )
354 result = WriteKLToFile(Writer, PartitionLabel.Value(), Buffer.Size());
356 if ( ASDCP_SUCCESS(result) )
357 result = Writer.Write(Buffer.RoData(), Buffer.Size(), &write_count);
365 ASDCP::MXF::Partition::ArchiveSize()
368 + sizeof(ui16_t) + sizeof(ui16_t)
370 + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t)
375 + sizeof(ui32_t) + sizeof(ui32_t) + ( UUIDlen * EssenceContainers.size() ) );
380 ASDCP::MXF::Partition::Dump(FILE* stream)
382 char identbuf[IdentBufferLen];
387 KLVFilePacket::Dump(stream, *m_Dict, false);
388 fprintf(stream, " MajorVersion = %hu\n", MajorVersion);
389 fprintf(stream, " MinorVersion = %hu\n", MinorVersion);
390 fprintf(stream, " KAGSize = %u\n", KAGSize);
391 fprintf(stream, " ThisPartition = %s\n", ui64sz(ThisPartition, identbuf));
392 fprintf(stream, " PreviousPartition = %s\n", ui64sz(PreviousPartition, identbuf));
393 fprintf(stream, " FooterPartition = %s\n", ui64sz(FooterPartition, identbuf));
394 fprintf(stream, " HeaderByteCount = %s\n", ui64sz(HeaderByteCount, identbuf));
395 fprintf(stream, " IndexByteCount = %s\n", ui64sz(IndexByteCount, identbuf));
396 fprintf(stream, " IndexSID = %u\n", IndexSID);
397 fprintf(stream, " BodyOffset = %s\n", ui64sz(BodyOffset, identbuf));
398 fprintf(stream, " BodySID = %u\n", BodySID);
399 fprintf(stream, " OperationalPattern = %s\n", OperationalPattern.EncodeString(identbuf, IdentBufferLen));
400 fputs("Essence Containers:\n", stream); EssenceContainers.Dump(stream);
404 //------------------------------------------------------------------------------------------
407 class ASDCP::MXF::Primer::h__PrimerLookup : public std::map<UL, TagValue>
410 void InitWithBatch(ASDCP::MXF::Batch<ASDCP::MXF::Primer::LocalTagEntry>& Batch)
412 ASDCP::MXF::Batch<ASDCP::MXF::Primer::LocalTagEntry>::iterator i = Batch.begin();
414 for ( ; i != Batch.end(); i++ )
415 insert(std::map<UL, TagValue>::value_type((*i).UL, (*i).Tag));
421 ASDCP::MXF::Primer::Primer(const Dictionary*& d) : m_LocalTag(0xff), m_Dict(d) {}
424 ASDCP::MXF::Primer::~Primer() {}
428 ASDCP::MXF::Primer::ClearTagList()
430 LocalTagEntryBatch.clear();
431 m_Lookup = new h__PrimerLookup;
436 ASDCP::MXF::Primer::InitFromBuffer(const byte_t* p, ui32_t l)
438 Result_t result = KLVPacket::InitFromBuffer(p, l, m_Dict->ul(MDD_Primer));
440 if ( ASDCP_SUCCESS(result) )
442 Kumu::MemIOReader MemRDR(m_ValueStart, m_ValueLength);
443 result = LocalTagEntryBatch.Unarchive(&MemRDR) ? RESULT_OK : RESULT_KLV_CODING;
446 if ( ASDCP_SUCCESS(result) )
448 m_Lookup = new h__PrimerLookup;
449 m_Lookup->InitWithBatch(LocalTagEntryBatch);
452 if ( ASDCP_FAILURE(result) )
453 DefaultLogSink().Error("Failed to initialize Primer\n");
460 ASDCP::MXF::Primer::WriteToFile(Kumu::FileWriter& Writer)
462 ASDCP::FrameBuffer Buffer;
463 Result_t result = Buffer.Capacity(128*1024);
465 if ( ASDCP_SUCCESS(result) )
466 result = WriteToBuffer(Buffer);
468 if ( ASDCP_SUCCESS(result) )
469 result = Writer.Write(Buffer.RoData(), Buffer.Size());
476 ASDCP::MXF::Primer::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
478 ASDCP::FrameBuffer LocalTagBuffer;
479 Kumu::MemIOWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length);
480 Result_t result = LocalTagEntryBatch.Archive(&MemWRT) ? RESULT_OK : RESULT_KLV_CODING;
482 if ( ASDCP_SUCCESS(result) )
484 ui32_t packet_length = MemWRT.Length();
485 result = WriteKLToBuffer(Buffer, m_Dict->ul(MDD_Primer), packet_length);
487 if ( ASDCP_SUCCESS(result) )
488 Buffer.Size(Buffer.Size() + packet_length);
496 ASDCP::MXF::Primer::InsertTag(const MDDEntry& Entry, ASDCP::TagValue& Tag)
500 std::map<UL, TagValue>::iterator i = m_Lookup->find(TestUL);
502 if ( i == m_Lookup->end() )
504 if ( Entry.tag.a == 0 && Entry.tag.b == 0 )
507 Tag.b = m_LocalTag--;
515 LocalTagEntry TmpEntry;
516 TmpEntry.UL = TestUL;
519 LocalTagEntryBatch.push_back(TmpEntry);
520 m_Lookup->insert(std::map<UL, TagValue>::value_type(TmpEntry.UL, TmpEntry.Tag));
532 ASDCP::MXF::Primer::TagForKey(const ASDCP::UL& Key, ASDCP::TagValue& Tag)
535 if ( m_Lookup.empty() )
537 DefaultLogSink().Error("Primer lookup is empty\n");
541 std::map<UL, TagValue>::iterator i = m_Lookup->find(Key);
543 if ( i == m_Lookup->end() )
552 ASDCP::MXF::Primer::Dump(FILE* stream)
554 char identbuf[IdentBufferLen];
559 KLVPacket::Dump(stream, *m_Dict, false);
560 fprintf(stream, "Primer: %u %s\n",
561 (ui32_t)LocalTagEntryBatch.size(),
562 ( LocalTagEntryBatch.size() == 1 ? "entry" : "entries" ));
564 Batch<LocalTagEntry>::iterator i = LocalTagEntryBatch.begin();
565 for ( ; i != LocalTagEntryBatch.end(); i++ )
567 const MDDEntry* Entry = m_Dict->FindUL((*i).UL.Value());
568 fprintf(stream, " %s %s\n", (*i).EncodeString(identbuf, IdentBufferLen), (Entry ? Entry->name : "Unknown"));
573 //------------------------------------------------------------------------------------------
578 ASDCP::MXF::Preface::InitFromTLVSet(TLVReader& TLVSet)
580 Result_t result = InterchangeObject::InitFromTLVSet(TLVSet);
581 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, LastModifiedDate));
582 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi16(OBJ_READ_ARGS(Preface, Version));
583 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi32(OBJ_READ_ARGS(Preface, ObjectModelVersion));
584 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, PrimaryPackage));
585 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, Identifications));
586 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, ContentStorage));
587 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, OperationalPattern));
588 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, EssenceContainers));
589 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, DMSchemes));
595 ASDCP::MXF::Preface::WriteToTLVSet(TLVWriter& TLVSet)
597 Result_t result = InterchangeObject::WriteToTLVSet(TLVSet);
598 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, LastModifiedDate));
599 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi16(OBJ_WRITE_ARGS(Preface, Version));
600 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi32(OBJ_WRITE_ARGS(Preface, ObjectModelVersion));
601 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, PrimaryPackage));
602 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, Identifications));
603 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, ContentStorage));
604 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, OperationalPattern));
605 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, EssenceContainers));
606 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, DMSchemes));
612 ASDCP::MXF::Preface::InitFromBuffer(const byte_t* p, ui32_t l)
614 m_Typeinfo = &(m_Dict->Type(MDD_Preface));
615 return InterchangeObject::InitFromBuffer(p, l);
620 ASDCP::MXF::Preface::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
622 m_Typeinfo = &(m_Dict->Type(MDD_Preface));
623 return InterchangeObject::WriteToBuffer(Buffer);
628 ASDCP::MXF::Preface::Dump(FILE* stream)
630 char identbuf[IdentBufferLen];
635 InterchangeObject::Dump(stream);
636 fprintf(stream, " %22s = %s\n", "LastModifiedDate", LastModifiedDate.EncodeString(identbuf, IdentBufferLen));
637 fprintf(stream, " %22s = %hu\n", "Version", Version);
638 fprintf(stream, " %22s = %u\n", "ObjectModelVersion", ObjectModelVersion);
639 fprintf(stream, " %22s = %s\n", "PrimaryPackage", PrimaryPackage.EncodeHex(identbuf, IdentBufferLen));
640 fprintf(stream, " %22s:\n", "Identifications"); Identifications.Dump(stream);
641 fprintf(stream, " %22s = %s\n", "ContentStorage", ContentStorage.EncodeHex(identbuf, IdentBufferLen));
642 fprintf(stream, " %22s = %s\n", "OperationalPattern", OperationalPattern.EncodeString(identbuf, IdentBufferLen));
643 fprintf(stream, " %22s:\n", "EssenceContainers"); EssenceContainers.Dump(stream);
644 fprintf(stream, " %22s:\n", "DMSchemes"); DMSchemes.Dump(stream);
647 //------------------------------------------------------------------------------------------
650 ASDCP::MXF::OPAtomHeader::OPAtomHeader(const Dictionary*& d) : Partition(d), m_Dict(d), m_RIP(d), m_Primer(d), m_Preface(0), m_HasRIP(false) {}
651 ASDCP::MXF::OPAtomHeader::~OPAtomHeader() {}
655 ASDCP::MXF::OPAtomHeader::InitFromFile(const Kumu::FileReader& Reader)
658 Result_t result = SeekToRIP(Reader);
660 if ( ASDCP_SUCCESS(result) )
662 result = m_RIP.InitFromFile(Reader);
663 ui32_t test_s = m_RIP.PairArray.size();
665 if ( ASDCP_FAILURE(result) )
667 DefaultLogSink().Error("File contains no RIP\n");
670 else if ( test_s == 0 )
672 DefaultLogSink().Error("RIP contains no Pairs.\n");
673 result = RESULT_FORMAT;
679 // OP-Atom states that there will be either two or three partitions:
680 // one closed header and one closed footer with an optional body
681 // SMPTE 429-5 files may have many partitions, see SMPTE 410M
682 DefaultLogSink().Warn("RIP count is less than 2: %u\n", test_s);
687 if ( m_RIP.PairArray.front().ByteOffset != 0 )
689 DefaultLogSink().Error("First Partition in RIP is not at offset 0.\n");
690 result = RESULT_FORMAT;
695 if ( ASDCP_SUCCESS(result) )
696 result = Reader.Seek(0);
698 if ( ASDCP_SUCCESS(result) )
699 result = Partition::InitFromFile(Reader); // test UL and OP
701 if ( ASDCP_FAILURE(result) )
704 // is it really OP-Atom?
705 UL OPAtomUL(m_Dict->ul(MDD_OPAtom));
706 UL InteropOPAtomUL(m_Dict->ul(MDD_MXFInterop_OPAtom));
708 if ( OperationalPattern == OPAtomUL ) // SMPTE
710 if ( m_Dict == &DefaultCompositeDict() )
711 m_Dict = &DefaultSMPTEDict();
713 else if ( OperationalPattern == InteropOPAtomUL ) // Interop
715 if ( m_Dict == &DefaultCompositeDict() )
716 m_Dict = &DefaultInteropDict();
720 char strbuf[IdentBufferLen];
721 const MDDEntry* Entry = m_Dict->FindUL(OperationalPattern.Value());
723 DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n",
724 OperationalPattern.EncodeString(strbuf, IdentBufferLen));
726 DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n", Entry->name);
729 // slurp up the remainder of the header
730 if ( HeaderByteCount < 1024 )
731 DefaultLogSink().Warn("Improbably small HeaderByteCount value: %u\n", HeaderByteCount);
733 assert (HeaderByteCount <= 0xFFFFFFFFL);
734 result = m_Buffer.Capacity((ui32_t) HeaderByteCount);
736 if ( ASDCP_SUCCESS(result) )
739 result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
741 if ( ASDCP_FAILURE(result) )
744 if ( read_count != m_Buffer.Capacity() )
746 DefaultLogSink().Error("Short read of OP-Atom header metadata; wanted %u, got %u\n",
747 m_Buffer.Capacity(), read_count);
748 return RESULT_KLV_CODING;
752 if ( ASDCP_SUCCESS(result) )
753 result = InitFromBuffer(m_Buffer.RoData(), m_Buffer.Capacity());
760 ASDCP::MXF::OPAtomHeader::InitFromPartitionBuffer(const byte_t* p, ui32_t l)
762 Result_t result = KLVPacket::InitFromBuffer(p, l);
764 if ( ASDCP_SUCCESS(result) )
765 result = Partition::InitFromBuffer(m_ValueStart, m_ValueLength); // test UL and OP
767 if ( ASDCP_SUCCESS(result) )
769 ui32_t pp_len = KLVPacket::PacketLength();
770 result = InitFromBuffer(p + pp_len, l - pp_len);
778 ASDCP::MXF::OPAtomHeader::InitFromBuffer(const byte_t* p, ui32_t l)
780 Result_t result = RESULT_OK;
781 const byte_t* end_p = p + l;
783 while ( ASDCP_SUCCESS(result) && p < end_p )
785 // parse the packets and index them by uid, discard KLVFill items
786 InterchangeObject* object = CreateObject(m_Dict, p);
789 object->m_Lookup = &m_Primer;
790 result = object->InitFromBuffer(p, end_p - p);
791 const byte_t* redo_p = p;
792 p += object->PacketLength();
793 // hexdump(p, object->PacketLength());
795 if ( ASDCP_SUCCESS(result) )
797 if ( object->IsA(m_Dict->ul(MDD_KLVFill)) )
801 else if ( object->IsA(m_Dict->ul(MDD_Primer)) )
804 result = m_Primer.InitFromBuffer(redo_p, end_p - redo_p);
808 m_PacketList->AddPacket(object);
810 if ( object->IsA(m_Dict->ul(MDD_Preface)) && m_Preface == 0 )
811 m_Preface = (Preface*)object;
816 DefaultLogSink().Error("Error initializing packet\n");
825 ASDCP::MXF::OPAtomHeader::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
827 return m_PacketList->GetMDObjectByID(ObjectID, Object);
832 ASDCP::MXF::OPAtomHeader::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
834 InterchangeObject* TmpObject;
839 return m_PacketList->GetMDObjectByType(ObjectID, Object);
844 ASDCP::MXF::OPAtomHeader::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
846 return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
850 ASDCP::MXF::Identification*
851 ASDCP::MXF::OPAtomHeader::GetIdentification()
853 InterchangeObject* Object;
855 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object)) )
856 return (Identification*)Object;
862 ASDCP::MXF::SourcePackage*
863 ASDCP::MXF::OPAtomHeader::GetSourcePackage()
865 InterchangeObject* Object;
867 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object)) )
868 return (SourcePackage*)Object;
876 ASDCP::MXF::OPAtomHeader::WriteToFile(Kumu::FileWriter& Writer, ui32_t HeaderSize)
878 if ( m_Preface == 0 )
881 if ( HeaderSize < 4096 )
883 DefaultLogSink().Error("HeaderSize %u is too small. Must be >= 4096\n", HeaderSize);
887 ASDCP::FrameBuffer HeaderBuffer;
888 HeaderByteCount = HeaderSize - ArchiveSize();
889 assert (HeaderByteCount <= 0xFFFFFFFFL);
890 Result_t result = HeaderBuffer.Capacity((ui32_t) HeaderByteCount);
891 m_Preface->m_Lookup = &m_Primer;
893 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
894 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
896 InterchangeObject* object = *pl_i;
897 object->m_Lookup = &m_Primer;
899 ASDCP::FrameBuffer WriteWrapper;
900 WriteWrapper.SetData(HeaderBuffer.Data() + HeaderBuffer.Size(),
901 HeaderBuffer.Capacity() - HeaderBuffer.Size());
902 result = object->WriteToBuffer(WriteWrapper);
903 HeaderBuffer.Size(HeaderBuffer.Size() + WriteWrapper.Size());
906 if ( ASDCP_SUCCESS(result) )
908 UL TmpUL(m_Dict->ul(MDD_ClosedCompleteHeader));
909 result = Partition::WriteToFile(Writer, TmpUL);
912 if ( ASDCP_SUCCESS(result) )
913 result = m_Primer.WriteToFile(Writer);
915 if ( ASDCP_SUCCESS(result) )
918 Writer.Write(HeaderBuffer.RoData(), HeaderBuffer.Size(), &write_count);
919 assert(write_count == HeaderBuffer.Size());
923 if ( ASDCP_SUCCESS(result) )
925 Kumu::fpos_t pos = Writer.Tell();
927 if ( pos > (Kumu::fpos_t)HeaderByteCount )
929 char intbuf[IntBufferLen];
930 DefaultLogSink().Error("Header size %s exceeds specified value %u\n",
936 ASDCP::FrameBuffer NilBuf;
937 ui32_t klv_fill_length = HeaderSize - (ui32_t)pos;
939 if ( klv_fill_length < kl_length )
941 DefaultLogSink().Error("Remaining region too small for KLV Fill header\n");
945 klv_fill_length -= kl_length;
946 result = WriteKLToFile(Writer, m_Dict->ul(MDD_KLVFill), klv_fill_length);
948 if ( ASDCP_SUCCESS(result) )
949 result = NilBuf.Capacity(klv_fill_length);
951 if ( ASDCP_SUCCESS(result) )
953 memset(NilBuf.Data(), 0, klv_fill_length);
955 Writer.Write(NilBuf.RoData(), klv_fill_length, &write_count);
956 assert(write_count == klv_fill_length);
965 ASDCP::MXF::OPAtomHeader::Dump(FILE* stream)
970 Partition::Dump(stream);
971 m_Primer.Dump(stream);
973 if ( m_Preface == 0 )
974 fputs("No Preface loaded\n", stream);
976 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
977 for ( ; i != m_PacketList->m_List.end(); i++ )
981 //------------------------------------------------------------------------------------------
984 ASDCP::MXF::OPAtomIndexFooter::OPAtomIndexFooter(const Dictionary*& d) :
985 Partition(d), m_Dict(d),
986 m_CurrentSegment(0), m_BytesPerEditUnit(0), m_BodySID(0),
987 m_ECOffset(0), m_Lookup(0)
993 ASDCP::MXF::OPAtomIndexFooter::~OPAtomIndexFooter() {}
997 ASDCP::MXF::OPAtomIndexFooter::InitFromFile(const Kumu::FileReader& Reader)
999 Result_t result = Partition::InitFromFile(Reader); // test UL and OP
1001 // slurp up the remainder of the footer
1004 if ( ASDCP_SUCCESS(result) )
1006 assert (IndexByteCount <= 0xFFFFFFFFL);
1007 result = m_Buffer.Capacity((ui32_t) IndexByteCount);
1010 if ( ASDCP_SUCCESS(result) )
1011 result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
1013 if ( ASDCP_SUCCESS(result) && read_count != m_Buffer.Capacity() )
1015 DefaultLogSink().Error("Short read of footer partition: got %u, expecting %u\n",
1016 read_count, m_Buffer.Capacity());
1020 if ( ASDCP_SUCCESS(result) )
1021 result = InitFromBuffer(m_Buffer.RoData(), m_Buffer.Capacity());
1028 ASDCP::MXF::OPAtomIndexFooter::InitFromPartitionBuffer(const byte_t* p, ui32_t l)
1030 Result_t result = KLVPacket::InitFromBuffer(p, l);
1032 if ( ASDCP_SUCCESS(result) )
1033 result = Partition::InitFromBuffer(m_ValueStart, m_ValueLength); // test UL and OP
1035 if ( ASDCP_SUCCESS(result) )
1037 ui32_t pp_len = KLVPacket::PacketLength();
1038 result = InitFromBuffer(p + pp_len, l - pp_len);
1046 ASDCP::MXF::OPAtomIndexFooter::InitFromBuffer(const byte_t* p, ui32_t l)
1048 Result_t result = RESULT_OK;
1049 const byte_t* end_p = p + l;
1051 while ( ASDCP_SUCCESS(result) && p < end_p )
1053 // parse the packets and index them by uid, discard KLVFill items
1054 InterchangeObject* object = CreateObject(m_Dict, p);
1057 object->m_Lookup = m_Lookup;
1058 result = object->InitFromBuffer(p, end_p - p);
1059 p += object->PacketLength();
1061 if ( ASDCP_SUCCESS(result) )
1063 m_PacketList->AddPacket(object);
1067 DefaultLogSink().Error("Error initializing packet\n");
1072 if ( ASDCP_FAILURE(result) )
1073 DefaultLogSink().Error("Failed to initialize OPAtomIndexFooter\n");
1080 ASDCP::MXF::OPAtomIndexFooter::WriteToFile(Kumu::FileWriter& Writer, ui64_t duration)
1082 ASDCP::FrameBuffer FooterBuffer;
1083 ui32_t footer_size = m_PacketList->m_List.size() * MaxIndexSegmentSize; // segment-count * max-segment-size
1084 Result_t result = FooterBuffer.Capacity(footer_size);
1085 ui32_t iseg_count = 0;
1087 if ( m_CurrentSegment != 0 )
1089 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1090 m_CurrentSegment = 0;
1093 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
1094 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
1096 if ( (*pl_i)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1099 IndexTableSegment* Segment = (IndexTableSegment*)(*pl_i);
1101 if ( m_BytesPerEditUnit != 0 )
1103 if ( iseg_count != 1 )
1104 return RESULT_STATE;
1106 Segment->IndexDuration = duration;
1110 InterchangeObject* object = *pl_i;
1111 object->m_Lookup = m_Lookup;
1113 ASDCP::FrameBuffer WriteWrapper;
1114 WriteWrapper.SetData(FooterBuffer.Data() + FooterBuffer.Size(),
1115 FooterBuffer.Capacity() - FooterBuffer.Size());
1116 result = object->WriteToBuffer(WriteWrapper);
1117 FooterBuffer.Size(FooterBuffer.Size() + WriteWrapper.Size());
1120 if ( ASDCP_SUCCESS(result) )
1122 IndexByteCount = FooterBuffer.Size();
1123 UL FooterUL(m_Dict->ul(MDD_CompleteFooter));
1124 result = Partition::WriteToFile(Writer, FooterUL);
1127 if ( ASDCP_SUCCESS(result) )
1129 ui32_t write_count = 0;
1130 result = Writer.Write(FooterBuffer.RoData(), FooterBuffer.Size(), &write_count);
1131 assert(write_count == FooterBuffer.Size());
1139 ASDCP::MXF::OPAtomIndexFooter::Dump(FILE* stream)
1144 Partition::Dump(stream);
1146 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
1147 for ( ; i != m_PacketList->m_List.end(); i++ )
1153 ASDCP::MXF::OPAtomIndexFooter::Lookup(ui32_t frame_num, IndexTableSegment::IndexEntry& Entry) const
1155 std::list<InterchangeObject*>::iterator li;
1156 for ( li = m_PacketList->m_List.begin(); li != m_PacketList->m_List.end(); li++ )
1158 if ( (*li)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1160 IndexTableSegment* Segment = (IndexTableSegment*)(*li);
1161 ui64_t start_pos = Segment->IndexStartPosition;
1163 if ( Segment->EditUnitByteCount > 0 )
1165 if ( m_PacketList->m_List.size() > 1 )
1166 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
1168 if ( ! Segment->IndexEntryArray.empty() )
1169 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
1171 Entry.StreamOffset = (ui64_t)frame_num * Segment->EditUnitByteCount;
1174 else if ( (ui64_t)frame_num >= start_pos
1175 && (ui64_t)frame_num < (start_pos + Segment->IndexDuration) )
1177 ui64_t tmp = frame_num - start_pos;
1178 assert(tmp <= 0xFFFFFFFFL);
1179 Entry = Segment->IndexEntryArray[(ui32_t) tmp];
1190 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const Rational& Rate)
1194 m_BytesPerEditUnit = size;
1197 IndexTableSegment* Index = new IndexTableSegment(m_Dict);
1198 AddChildObject(Index);
1199 Index->EditUnitByteCount = m_BytesPerEditUnit;
1200 Index->IndexEditRate = Rate;
1205 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsVBR(IPrimerLookup* lookup, const Rational& Rate, Kumu::fpos_t offset)
1209 m_BytesPerEditUnit = 0;
1211 m_ECOffset = offset;
1216 ASDCP::MXF::OPAtomIndexFooter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
1218 if ( m_BytesPerEditUnit != 0 ) // are we CBR? that's bad
1220 DefaultLogSink().Error("Call to PushIndexEntry() failed: index is CBR\n");
1224 // do we have an available segment?
1225 if ( m_CurrentSegment == 0 )
1226 { // no, set up a new segment
1227 m_CurrentSegment = new IndexTableSegment(m_Dict);
1228 assert(m_CurrentSegment);
1229 AddChildObject(m_CurrentSegment);
1230 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1231 m_CurrentSegment->IndexEditRate = m_EditRate;
1232 m_CurrentSegment->IndexStartPosition = 0;
1234 else if ( m_CurrentSegment->IndexEntryArray.size() >= CBRIndexEntriesPerSegment )
1235 { // no, this one is full, start another
1236 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1237 ui64_t StartPosition = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
1239 m_CurrentSegment = new IndexTableSegment(m_Dict);
1240 assert(m_CurrentSegment);
1241 AddChildObject(m_CurrentSegment);
1242 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1243 m_CurrentSegment->IndexEditRate = m_EditRate;
1244 m_CurrentSegment->IndexStartPosition = StartPosition;
1247 m_CurrentSegment->IndexEntryArray.push_back(Entry);
1250 //------------------------------------------------------------------------------------------
1255 ASDCP::MXF::InterchangeObject::InitFromTLVSet(TLVReader& TLVSet)
1257 Result_t result = TLVSet.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID));
1258 if ( ASDCP_SUCCESS(result) )
1259 result = TLVSet.ReadObject(OBJ_READ_ARGS(GenerationInterchangeObject, GenerationUID));
1265 ASDCP::MXF::InterchangeObject::WriteToTLVSet(TLVWriter& TLVSet)
1267 Result_t result = TLVSet.WriteObject(OBJ_WRITE_ARGS(InterchangeObject, InstanceUID));
1268 if ( ASDCP_SUCCESS(result) )
1269 result = TLVSet.WriteObject(OBJ_WRITE_ARGS(GenerationInterchangeObject, GenerationUID));
1275 ASDCP::MXF::InterchangeObject::InitFromBuffer(const byte_t* p, ui32_t l)
1278 Result_t result = RESULT_FALSE;
1280 if ( m_Typeinfo == 0 )
1282 result = KLVPacket::InitFromBuffer(p, l);
1286 result = KLVPacket::InitFromBuffer(p, l, m_Typeinfo->ul);
1288 if ( ASDCP_SUCCESS(result) )
1290 TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup);
1291 result = InitFromTLVSet(MemRDR);
1300 ASDCP::MXF::InterchangeObject::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
1302 if ( m_Typeinfo == 0 )
1303 return RESULT_STATE;
1305 TLVWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length, m_Lookup);
1306 Result_t result = WriteToTLVSet(MemWRT);
1308 if ( ASDCP_SUCCESS(result) )
1310 ui32_t packet_length = MemWRT.Length();
1311 result = WriteKLToBuffer(Buffer, m_Typeinfo->ul, packet_length);
1313 if ( ASDCP_SUCCESS(result) )
1314 Buffer.Size(Buffer.Size() + packet_length);
1322 ASDCP::MXF::InterchangeObject::Dump(FILE* stream)
1324 char identbuf[IdentBufferLen];
1326 fputc('\n', stream);
1327 KLVPacket::Dump(stream, *m_Dict, false);
1328 fprintf(stream, " InstanceUID = %s\n", InstanceUID.EncodeHex(identbuf, IdentBufferLen));
1329 fprintf(stream, " GenerationUID = %s\n", GenerationUID.EncodeHex(identbuf, IdentBufferLen));
1334 ASDCP::MXF::InterchangeObject::IsA(const byte_t* label)
1336 if ( m_KLLength == 0 )
1339 return ( memcmp(label, m_KeyStart, SMPTE_UL_LENGTH) == 0 );
1343 //------------------------------------------------------------------------------------------
1346 typedef std::map<ASDCP::UL, ASDCP::MXF::MXFObjectFactory_t>FactoryMap_t;
1347 typedef FactoryMap_t::iterator FLi_t;
1350 class FactoryList : public FactoryMap_t
1359 Kumu::AutoMutex BlockLock(m_Lock);
1363 FLi_t Find(const byte_t* label) {
1364 Kumu::AutoMutex BlockLock(m_Lock);
1369 Kumu::AutoMutex BlockLock(m_Lock);
1373 void Insert(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory) {
1374 Kumu::AutoMutex BlockLock(m_Lock);
1375 insert(FactoryList::value_type(label, factory));
1380 static FactoryList s_FactoryList;
1381 static Kumu::Mutex s_InitLock;
1382 static bool s_TypesInit = false;
1387 ASDCP::MXF::SetObjectFactory(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory)
1389 s_FactoryList.Insert(label, factory);
1393 ASDCP::MXF::InterchangeObject*
1394 ASDCP::MXF::CreateObject(const Dictionary*& Dict, const UL& label)
1396 if ( ! s_TypesInit )
1398 Kumu::AutoMutex BlockLock(s_InitLock);
1400 if ( ! s_TypesInit )
1402 MXF::Metadata_InitTypes(Dict);
1407 FLi_t i = s_FactoryList.find(label.Value());
1409 if ( i == s_FactoryList.end() )
1410 return new InterchangeObject(Dict);
1412 return i->second(Dict);