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 || OperationalPattern == InteropOPAtomUL ) )
710 char strbuf[IdentBufferLen];
711 const MDDEntry* Entry = m_Dict.FindUL(OperationalPattern.Value());
713 DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n", OperationalPattern.EncodeString(strbuf, IdentBufferLen));
715 DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n", Entry->name);
718 // slurp up the remainder of the header
719 if ( HeaderByteCount < 1024 )
720 DefaultLogSink().Warn("Improbably small HeaderByteCount value: %u\n", HeaderByteCount);
722 assert (HeaderByteCount <= 0xFFFFFFFFL);
723 result = m_Buffer.Capacity((ui32_t) HeaderByteCount);
725 if ( ASDCP_SUCCESS(result) )
728 result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
730 if ( ASDCP_FAILURE(result) )
733 if ( read_count != m_Buffer.Capacity() )
735 DefaultLogSink().Error("Short read of OP-Atom header metadata; wanted %u, got %u\n",
736 m_Buffer.Capacity(), read_count);
737 return RESULT_KLV_CODING;
741 if ( ASDCP_SUCCESS(result) )
742 result = InitFromBuffer(m_Buffer.RoData(), m_Buffer.Capacity());
749 ASDCP::MXF::OPAtomHeader::InitFromPartitionBuffer(const byte_t* p, ui32_t l)
751 Result_t result = KLVPacket::InitFromBuffer(p, l);
753 if ( ASDCP_SUCCESS(result) )
754 result = Partition::InitFromBuffer(m_ValueStart, m_ValueLength); // test UL and OP
756 if ( ASDCP_SUCCESS(result) )
758 ui32_t pp_len = KLVPacket::PacketLength();
759 result = InitFromBuffer(p + pp_len, l - pp_len);
767 ASDCP::MXF::OPAtomHeader::InitFromBuffer(const byte_t* p, ui32_t l)
769 Result_t result = RESULT_OK;
770 const byte_t* end_p = p + l;
772 while ( ASDCP_SUCCESS(result) && p < end_p )
774 // parse the packets and index them by uid, discard KLVFill items
775 InterchangeObject* object = CreateObject(m_Dict, p);
778 object->m_Lookup = &m_Primer;
779 result = object->InitFromBuffer(p, end_p - p);
780 const byte_t* redo_p = p;
781 p += object->PacketLength();
782 // hexdump(p, object->PacketLength());
784 if ( ASDCP_SUCCESS(result) )
786 if ( object->IsA(m_Dict.ul(MDD_KLVFill)) )
790 else if ( object->IsA(m_Dict.ul(MDD_Primer)) )
793 result = m_Primer.InitFromBuffer(redo_p, end_p - redo_p);
797 m_PacketList->AddPacket(object);
799 if ( object->IsA(m_Dict.ul(MDD_Preface)) && m_Preface == 0 )
800 m_Preface = (Preface*)object;
805 DefaultLogSink().Error("Error initializing packet\n");
814 ASDCP::MXF::OPAtomHeader::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
816 return m_PacketList->GetMDObjectByID(ObjectID, Object);
821 ASDCP::MXF::OPAtomHeader::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
823 InterchangeObject* TmpObject;
828 return m_PacketList->GetMDObjectByType(ObjectID, Object);
833 ASDCP::MXF::OPAtomHeader::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
835 return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
839 ASDCP::MXF::Identification*
840 ASDCP::MXF::OPAtomHeader::GetIdentification()
842 InterchangeObject* Object;
844 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object)) )
845 return (Identification*)Object;
851 ASDCP::MXF::SourcePackage*
852 ASDCP::MXF::OPAtomHeader::GetSourcePackage()
854 InterchangeObject* Object;
856 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object)) )
857 return (SourcePackage*)Object;
865 ASDCP::MXF::OPAtomHeader::WriteToFile(Kumu::FileWriter& Writer, ui32_t HeaderSize)
867 if ( m_Preface == 0 )
870 if ( HeaderSize < 4096 )
872 DefaultLogSink().Error("HeaderSize %u is too small. Must be >= 4096\n", HeaderSize);
876 ASDCP::FrameBuffer HeaderBuffer;
877 HeaderByteCount = HeaderSize - ArchiveSize();
878 assert (HeaderByteCount <= 0xFFFFFFFFL);
879 Result_t result = HeaderBuffer.Capacity((ui32_t) HeaderByteCount);
880 m_Preface->m_Lookup = &m_Primer;
882 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
883 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
885 InterchangeObject* object = *pl_i;
886 object->m_Lookup = &m_Primer;
888 ASDCP::FrameBuffer WriteWrapper;
889 WriteWrapper.SetData(HeaderBuffer.Data() + HeaderBuffer.Size(),
890 HeaderBuffer.Capacity() - HeaderBuffer.Size());
891 result = object->WriteToBuffer(WriteWrapper);
892 HeaderBuffer.Size(HeaderBuffer.Size() + WriteWrapper.Size());
895 if ( ASDCP_SUCCESS(result) )
897 UL TmpUL(m_Dict.ul(MDD_ClosedCompleteHeader));
898 result = Partition::WriteToFile(Writer, TmpUL);
901 if ( ASDCP_SUCCESS(result) )
902 result = m_Primer.WriteToFile(Writer);
904 if ( ASDCP_SUCCESS(result) )
907 Writer.Write(HeaderBuffer.RoData(), HeaderBuffer.Size(), &write_count);
908 assert(write_count == HeaderBuffer.Size());
912 if ( ASDCP_SUCCESS(result) )
914 Kumu::fpos_t pos = Writer.Tell();
916 if ( pos > (Kumu::fpos_t)HeaderByteCount )
918 char intbuf[IntBufferLen];
919 DefaultLogSink().Error("Header size %s exceeds specified value %u\n",
925 ASDCP::FrameBuffer NilBuf;
926 ui32_t klv_fill_length = HeaderSize - (ui32_t)pos;
928 if ( klv_fill_length < kl_length )
930 DefaultLogSink().Error("Remaining region too small for KLV Fill header\n");
934 klv_fill_length -= kl_length;
935 result = WriteKLToFile(Writer, m_Dict.ul(MDD_KLVFill), klv_fill_length);
937 if ( ASDCP_SUCCESS(result) )
938 result = NilBuf.Capacity(klv_fill_length);
940 if ( ASDCP_SUCCESS(result) )
942 memset(NilBuf.Data(), 0, klv_fill_length);
944 Writer.Write(NilBuf.RoData(), klv_fill_length, &write_count);
945 assert(write_count == klv_fill_length);
954 ASDCP::MXF::OPAtomHeader::Dump(FILE* stream)
959 Partition::Dump(stream);
960 m_Primer.Dump(stream);
962 if ( m_Preface == 0 )
963 fputs("No Preface loaded\n", stream);
965 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
966 for ( ; i != m_PacketList->m_List.end(); i++ )
970 //------------------------------------------------------------------------------------------
973 ASDCP::MXF::OPAtomIndexFooter::OPAtomIndexFooter(const Dictionary& d) :
974 Partition(d), m_Dict(d),
975 m_CurrentSegment(0), m_BytesPerEditUnit(0), m_BodySID(0),
976 m_ECOffset(0), m_Lookup(0)
982 ASDCP::MXF::OPAtomIndexFooter::~OPAtomIndexFooter() {}
986 ASDCP::MXF::OPAtomIndexFooter::InitFromFile(const Kumu::FileReader& Reader)
988 Result_t result = Partition::InitFromFile(Reader); // test UL and OP
990 // slurp up the remainder of the footer
993 if ( ASDCP_SUCCESS(result) )
995 assert (IndexByteCount <= 0xFFFFFFFFL);
996 result = m_Buffer.Capacity((ui32_t) IndexByteCount);
999 if ( ASDCP_SUCCESS(result) )
1000 result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
1002 if ( ASDCP_SUCCESS(result) && read_count != m_Buffer.Capacity() )
1004 DefaultLogSink().Error("Short read of footer partition: got %u, expecting %u\n",
1005 read_count, m_Buffer.Capacity());
1009 if ( ASDCP_SUCCESS(result) )
1010 result = InitFromBuffer(m_Buffer.RoData(), m_Buffer.Capacity());
1017 ASDCP::MXF::OPAtomIndexFooter::InitFromPartitionBuffer(const byte_t* p, ui32_t l)
1019 Result_t result = KLVPacket::InitFromBuffer(p, l);
1021 if ( ASDCP_SUCCESS(result) )
1022 result = Partition::InitFromBuffer(m_ValueStart, m_ValueLength); // test UL and OP
1024 if ( ASDCP_SUCCESS(result) )
1026 ui32_t pp_len = KLVPacket::PacketLength();
1027 result = InitFromBuffer(p + pp_len, l - pp_len);
1035 ASDCP::MXF::OPAtomIndexFooter::InitFromBuffer(const byte_t* p, ui32_t l)
1037 Result_t result = RESULT_OK;
1038 const byte_t* end_p = p + l;
1040 while ( ASDCP_SUCCESS(result) && p < end_p )
1042 // parse the packets and index them by uid, discard KLVFill items
1043 InterchangeObject* object = CreateObject(m_Dict, p);
1046 object->m_Lookup = m_Lookup;
1047 result = object->InitFromBuffer(p, end_p - p);
1048 p += object->PacketLength();
1050 if ( ASDCP_SUCCESS(result) )
1052 m_PacketList->AddPacket(object);
1056 DefaultLogSink().Error("Error initializing packet\n");
1061 if ( ASDCP_FAILURE(result) )
1062 DefaultLogSink().Error("Failed to initialize OPAtomIndexFooter\n");
1069 ASDCP::MXF::OPAtomIndexFooter::WriteToFile(Kumu::FileWriter& Writer, ui64_t duration)
1071 ASDCP::FrameBuffer FooterBuffer;
1072 ui32_t footer_size = m_PacketList->m_List.size() * MaxIndexSegmentSize; // segment-count * max-segment-size
1073 Result_t result = FooterBuffer.Capacity(footer_size);
1074 ui32_t iseg_count = 0;
1076 if ( m_CurrentSegment != 0 )
1078 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1079 m_CurrentSegment = 0;
1082 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
1083 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
1085 if ( (*pl_i)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1088 IndexTableSegment* Segment = (IndexTableSegment*)(*pl_i);
1090 if ( m_BytesPerEditUnit != 0 )
1092 if ( iseg_count != 1 )
1093 return RESULT_STATE;
1095 Segment->IndexDuration = duration;
1099 InterchangeObject* object = *pl_i;
1100 object->m_Lookup = m_Lookup;
1102 ASDCP::FrameBuffer WriteWrapper;
1103 WriteWrapper.SetData(FooterBuffer.Data() + FooterBuffer.Size(),
1104 FooterBuffer.Capacity() - FooterBuffer.Size());
1105 result = object->WriteToBuffer(WriteWrapper);
1106 FooterBuffer.Size(FooterBuffer.Size() + WriteWrapper.Size());
1109 if ( ASDCP_SUCCESS(result) )
1111 IndexByteCount = FooterBuffer.Size();
1112 UL FooterUL(m_Dict.ul(MDD_CompleteFooter));
1113 result = Partition::WriteToFile(Writer, FooterUL);
1116 if ( ASDCP_SUCCESS(result) )
1118 ui32_t write_count = 0;
1119 result = Writer.Write(FooterBuffer.RoData(), FooterBuffer.Size(), &write_count);
1120 assert(write_count == FooterBuffer.Size());
1128 ASDCP::MXF::OPAtomIndexFooter::Dump(FILE* stream)
1133 Partition::Dump(stream);
1135 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
1136 for ( ; i != m_PacketList->m_List.end(); i++ )
1142 ASDCP::MXF::OPAtomIndexFooter::Lookup(ui32_t frame_num, IndexTableSegment::IndexEntry& Entry) const
1144 std::list<InterchangeObject*>::iterator li;
1145 for ( li = m_PacketList->m_List.begin(); li != m_PacketList->m_List.end(); li++ )
1147 if ( (*li)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1149 IndexTableSegment* Segment = (IndexTableSegment*)(*li);
1150 ui64_t start_pos = Segment->IndexStartPosition;
1152 if ( Segment->EditUnitByteCount > 0 )
1154 if ( m_PacketList->m_List.size() > 1 )
1155 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
1157 if ( ! Segment->IndexEntryArray.empty() )
1158 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
1160 Entry.StreamOffset = (ui64_t)frame_num * Segment->EditUnitByteCount;
1163 else if ( (ui64_t)frame_num >= start_pos
1164 && (ui64_t)frame_num < (start_pos + Segment->IndexDuration) )
1166 ui64_t tmp = frame_num - start_pos;
1167 assert(tmp <= 0xFFFFFFFFL);
1168 Entry = Segment->IndexEntryArray[(ui32_t) tmp];
1179 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const Rational& Rate)
1183 m_BytesPerEditUnit = size;
1186 IndexTableSegment* Index = new IndexTableSegment(m_Dict);
1187 AddChildObject(Index);
1188 Index->EditUnitByteCount = m_BytesPerEditUnit;
1189 Index->IndexEditRate = Rate;
1194 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsVBR(IPrimerLookup* lookup, const Rational& Rate, Kumu::fpos_t offset)
1198 m_BytesPerEditUnit = 0;
1200 m_ECOffset = offset;
1205 ASDCP::MXF::OPAtomIndexFooter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
1207 if ( m_BytesPerEditUnit != 0 ) // are we CBR? that's bad
1209 DefaultLogSink().Error("Call to PushIndexEntry() failed: index is CBR\n");
1213 // do we have an available segment?
1214 if ( m_CurrentSegment == 0 )
1215 { // no, set up a new segment
1216 m_CurrentSegment = new IndexTableSegment(m_Dict);
1217 assert(m_CurrentSegment);
1218 AddChildObject(m_CurrentSegment);
1219 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1220 m_CurrentSegment->IndexEditRate = m_EditRate;
1221 m_CurrentSegment->IndexStartPosition = 0;
1223 else if ( m_CurrentSegment->IndexEntryArray.size() >= CBRIndexEntriesPerSegment )
1224 { // no, this one is full, start another
1225 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1226 ui64_t StartPosition = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
1228 m_CurrentSegment = new IndexTableSegment(m_Dict);
1229 assert(m_CurrentSegment);
1230 AddChildObject(m_CurrentSegment);
1231 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1232 m_CurrentSegment->IndexEditRate = m_EditRate;
1233 m_CurrentSegment->IndexStartPosition = StartPosition;
1236 m_CurrentSegment->IndexEntryArray.push_back(Entry);
1239 //------------------------------------------------------------------------------------------
1244 ASDCP::MXF::InterchangeObject::InitFromTLVSet(TLVReader& TLVSet)
1246 Result_t result = TLVSet.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID));
1247 if ( ASDCP_SUCCESS(result) )
1248 result = TLVSet.ReadObject(OBJ_READ_ARGS(GenerationInterchangeObject, GenerationUID));
1254 ASDCP::MXF::InterchangeObject::WriteToTLVSet(TLVWriter& TLVSet)
1256 Result_t result = TLVSet.WriteObject(OBJ_WRITE_ARGS(InterchangeObject, InstanceUID));
1257 if ( ASDCP_SUCCESS(result) )
1258 result = TLVSet.WriteObject(OBJ_WRITE_ARGS(GenerationInterchangeObject, GenerationUID));
1264 ASDCP::MXF::InterchangeObject::InitFromBuffer(const byte_t* p, ui32_t l)
1267 Result_t result = RESULT_FALSE;
1269 if ( m_Typeinfo == 0 )
1271 result = KLVPacket::InitFromBuffer(p, l);
1275 result = KLVPacket::InitFromBuffer(p, l, m_Typeinfo->ul);
1277 if ( ASDCP_SUCCESS(result) )
1279 TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup);
1280 result = InitFromTLVSet(MemRDR);
1289 ASDCP::MXF::InterchangeObject::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
1291 if ( m_Typeinfo == 0 )
1292 return RESULT_STATE;
1294 TLVWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length, m_Lookup);
1295 Result_t result = WriteToTLVSet(MemWRT);
1297 if ( ASDCP_SUCCESS(result) )
1299 ui32_t packet_length = MemWRT.Length();
1300 result = WriteKLToBuffer(Buffer, m_Typeinfo->ul, packet_length);
1302 if ( ASDCP_SUCCESS(result) )
1303 Buffer.Size(Buffer.Size() + packet_length);
1311 ASDCP::MXF::InterchangeObject::Dump(FILE* stream)
1313 char identbuf[IdentBufferLen];
1315 fputc('\n', stream);
1316 KLVPacket::Dump(stream, m_Dict, false);
1317 fprintf(stream, " InstanceUID = %s\n", InstanceUID.EncodeHex(identbuf, IdentBufferLen));
1318 fprintf(stream, " GenerationUID = %s\n", GenerationUID.EncodeHex(identbuf, IdentBufferLen));
1323 ASDCP::MXF::InterchangeObject::IsA(const byte_t* label)
1325 if ( m_KLLength == 0 )
1328 return ( memcmp(label, m_KeyStart, SMPTE_UL_LENGTH) == 0 );
1332 //------------------------------------------------------------------------------------------
1335 typedef std::map<ASDCP::UL, ASDCP::MXF::MXFObjectFactory_t>FactoryMap_t;
1336 typedef FactoryMap_t::iterator FLi_t;
1339 class FactoryList : public FactoryMap_t
1348 Kumu::AutoMutex BlockLock(m_Lock);
1352 FLi_t Find(const byte_t* label) {
1353 Kumu::AutoMutex BlockLock(m_Lock);
1358 Kumu::AutoMutex BlockLock(m_Lock);
1362 void Insert(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory) {
1363 Kumu::AutoMutex BlockLock(m_Lock);
1364 insert(FactoryList::value_type(label, factory));
1369 static FactoryList s_FactoryList;
1370 static Kumu::Mutex s_InitLock;
1371 static bool s_TypesInit = false;
1376 ASDCP::MXF::SetObjectFactory(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory)
1378 s_FactoryList.Insert(label, factory);
1382 ASDCP::MXF::InterchangeObject*
1383 ASDCP::MXF::CreateObject(const Dictionary& Dict, const UL& label)
1388 if ( ! s_TypesInit )
1390 Kumu::AutoMutex BlockLock(s_InitLock);
1392 if ( ! s_TypesInit )
1394 MXF::Metadata_InitTypes(Dict);
1399 FLi_t i = s_FactoryList.find(label);
1401 if ( i == s_FactoryList.end() )
1402 return new InterchangeObject(Dict);
1404 return i->second(Dict);