2 Copyright (c) 2005-2012, 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)
117 Result_t result = KLVFilePacket::InitFromFile(Reader, m_Dict->ul(MDD_RandomIndexMetadata));
119 if ( ASDCP_SUCCESS(result) )
121 Kumu::MemIOReader MemRDR(m_ValueStart, m_ValueLength - 4);
122 result = PairArray.Unarchive(&MemRDR) ? RESULT_OK : RESULT_KLV_CODING;
125 if ( ASDCP_FAILURE(result) )
126 DefaultLogSink().Error("Failed to initialize RIP\n");
133 ASDCP::MXF::RIP::WriteToFile(Kumu::FileWriter& Writer)
136 ASDCP::FrameBuffer Buffer;
137 ui32_t RIPSize = ( PairArray.size() * (sizeof(ui32_t) + sizeof(ui64_t)) ) + 4;
138 Result_t result = Buffer.Capacity(RIPSize);
140 if ( ASDCP_SUCCESS(result) )
141 result = WriteKLToFile(Writer, m_Dict->ul(MDD_RandomIndexMetadata), RIPSize);
143 if ( ASDCP_SUCCESS(result) )
145 result = RESULT_KLV_CODING;
147 Kumu::MemIOWriter MemWRT(Buffer.Data(), Buffer.Capacity());
148 if ( PairArray.Archive(&MemWRT) )
149 if ( MemWRT.WriteUi32BE(RIPSize + 20) )
151 Buffer.Size(MemWRT.Length());
156 if ( ASDCP_SUCCESS(result) )
157 result = Writer.Write(Buffer.RoData(), Buffer.Size());
164 ASDCP::MXF::RIP::Dump(FILE* stream)
169 KLVFilePacket::Dump(stream, *m_Dict, false);
170 PairArray.Dump(stream, false);
173 //------------------------------------------------------------------------------------------
177 class ASDCP::MXF::Partition::h__PacketList
180 std::list<InterchangeObject*> m_List;
181 std::map<UUID, InterchangeObject*> m_Map;
184 while ( ! m_List.empty() )
186 delete m_List.back();
192 void AddPacket(InterchangeObject* ThePacket) // takes ownership
195 m_Map.insert(std::map<UUID, InterchangeObject*>::value_type(ThePacket->InstanceUID, ThePacket));
196 m_List.push_back(ThePacket);
200 Result_t GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
202 ASDCP_TEST_NULL(Object);
204 std::map<UUID, InterchangeObject*>::iterator mi = m_Map.find(ObjectID);
206 if ( mi == m_Map.end() )
212 *Object = (*mi).second;
217 Result_t GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
219 ASDCP_TEST_NULL(ObjectID);
220 ASDCP_TEST_NULL(Object);
221 std::list<InterchangeObject*>::iterator li;
224 for ( li = m_List.begin(); li != m_List.end(); li++ )
226 if ( (*li)->HasUL(ObjectID) )
237 Result_t GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
239 ASDCP_TEST_NULL(ObjectID);
240 std::list<InterchangeObject*>::iterator li;
242 for ( li = m_List.begin(); li != m_List.end(); li++ )
244 if ( (*li)->HasUL(ObjectID) )
245 ObjectList.push_back(*li);
248 return ObjectList.empty() ? RESULT_FAIL : RESULT_OK;
252 //------------------------------------------------------------------------------------------
256 ASDCP::MXF::Partition::Partition(const Dictionary*& d) :
258 MajorVersion(1), MinorVersion(2),
259 KAGSize(1), ThisPartition(0), PreviousPartition(0),
260 FooterPartition(0), HeaderByteCount(0), IndexByteCount(0), IndexSID(0),
261 BodyOffset(0), BodySID(0)
263 m_PacketList = new h__PacketList;
266 ASDCP::MXF::Partition::~Partition()
272 ASDCP::MXF::Partition::AddChildObject(InterchangeObject* Object)
276 if ( ! Object->InstanceUID.HasValue() )
277 GenRandomValue(Object->InstanceUID);
279 m_PacketList->AddPacket(Object);
284 ASDCP::MXF::Partition::InitFromFile(const Kumu::FileReader& Reader)
286 Result_t result = KLVFilePacket::InitFromFile(Reader);
288 // could be one of several values
289 if ( ASDCP_SUCCESS(result) )
290 result = ASDCP::MXF::Partition::InitFromBuffer(m_ValueStart, m_ValueLength);
297 ASDCP::MXF::Partition::InitFromBuffer(const byte_t* p, ui32_t l)
299 Kumu::MemIOReader MemRDR(p, l);
300 Result_t result = RESULT_KLV_CODING;
302 if ( MemRDR.ReadUi16BE(&MajorVersion) )
303 if ( MemRDR.ReadUi16BE(&MinorVersion) )
304 if ( MemRDR.ReadUi32BE(&KAGSize) )
305 if ( MemRDR.ReadUi64BE(&ThisPartition) )
306 if ( MemRDR.ReadUi64BE(&PreviousPartition) )
307 if ( MemRDR.ReadUi64BE(&FooterPartition) )
308 if ( MemRDR.ReadUi64BE(&HeaderByteCount) )
309 if ( MemRDR.ReadUi64BE(&IndexByteCount) )
310 if ( MemRDR.ReadUi32BE(&IndexSID) )
311 if ( MemRDR.ReadUi64BE(&BodyOffset) )
312 if ( MemRDR.ReadUi32BE(&BodySID) )
313 if ( OperationalPattern.Unarchive(&MemRDR) )
314 if ( EssenceContainers.Unarchive(&MemRDR) )
317 if ( ASDCP_FAILURE(result) )
318 DefaultLogSink().Error("Failed to initialize Partition\n");
325 ASDCP::MXF::Partition::WriteToFile(Kumu::FileWriter& Writer, UL& PartitionLabel)
327 ASDCP::FrameBuffer Buffer;
328 Result_t result = Buffer.Capacity(1024);
330 if ( ASDCP_SUCCESS(result) )
332 Kumu::MemIOWriter MemWRT(Buffer.Data(), Buffer.Capacity());
333 result = RESULT_KLV_CODING;
334 if ( MemWRT.WriteUi16BE(MajorVersion) )
335 if ( MemWRT.WriteUi16BE(MinorVersion) )
336 if ( MemWRT.WriteUi32BE(KAGSize) )
337 if ( MemWRT.WriteUi64BE(ThisPartition) )
338 if ( MemWRT.WriteUi64BE(PreviousPartition) )
339 if ( MemWRT.WriteUi64BE(FooterPartition) )
340 if ( MemWRT.WriteUi64BE(HeaderByteCount) )
341 if ( MemWRT.WriteUi64BE(IndexByteCount) )
342 if ( MemWRT.WriteUi32BE(IndexSID) )
343 if ( MemWRT.WriteUi64BE(BodyOffset) )
344 if ( MemWRT.WriteUi32BE(BodySID) )
345 if ( OperationalPattern.Archive(&MemWRT) )
346 if ( EssenceContainers.Archive(&MemWRT) )
348 Buffer.Size(MemWRT.Length());
353 if ( ASDCP_SUCCESS(result) )
356 result = WriteKLToFile(Writer, PartitionLabel.Value(), Buffer.Size());
358 if ( ASDCP_SUCCESS(result) )
359 result = Writer.Write(Buffer.RoData(), Buffer.Size(), &write_count);
367 ASDCP::MXF::Partition::ArchiveSize()
370 + sizeof(ui16_t) + sizeof(ui16_t)
372 + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t)
377 + sizeof(ui32_t) + sizeof(ui32_t) + ( UUIDlen * EssenceContainers.size() ) );
382 ASDCP::MXF::Partition::Dump(FILE* stream)
384 char identbuf[IdentBufferLen];
389 KLVFilePacket::Dump(stream, *m_Dict, false);
390 fprintf(stream, " MajorVersion = %hu\n", MajorVersion);
391 fprintf(stream, " MinorVersion = %hu\n", MinorVersion);
392 fprintf(stream, " KAGSize = %u\n", KAGSize);
393 fprintf(stream, " ThisPartition = %s\n", ui64sz(ThisPartition, identbuf));
394 fprintf(stream, " PreviousPartition = %s\n", ui64sz(PreviousPartition, identbuf));
395 fprintf(stream, " FooterPartition = %s\n", ui64sz(FooterPartition, identbuf));
396 fprintf(stream, " HeaderByteCount = %s\n", ui64sz(HeaderByteCount, identbuf));
397 fprintf(stream, " IndexByteCount = %s\n", ui64sz(IndexByteCount, identbuf));
398 fprintf(stream, " IndexSID = %u\n", IndexSID);
399 fprintf(stream, " BodyOffset = %s\n", ui64sz(BodyOffset, identbuf));
400 fprintf(stream, " BodySID = %u\n", BodySID);
401 fprintf(stream, " OperationalPattern = %s\n", OperationalPattern.EncodeString(identbuf, IdentBufferLen));
402 fputs("Essence Containers:\n", stream); EssenceContainers.Dump(stream);
406 //------------------------------------------------------------------------------------------
409 class ASDCP::MXF::Primer::h__PrimerLookup : public std::map<UL, TagValue>
412 void InitWithBatch(ASDCP::MXF::Batch<ASDCP::MXF::Primer::LocalTagEntry>& Batch)
414 ASDCP::MXF::Batch<ASDCP::MXF::Primer::LocalTagEntry>::iterator i = Batch.begin();
416 for ( ; i != Batch.end(); i++ )
417 insert(std::map<UL, TagValue>::value_type((*i).UL, (*i).Tag));
423 ASDCP::MXF::Primer::Primer(const Dictionary*& d) : m_LocalTag(0xff), m_Dict(d) {
424 m_UL = m_Dict->ul(MDD_Primer);
428 ASDCP::MXF::Primer::~Primer() {}
432 ASDCP::MXF::Primer::ClearTagList()
434 LocalTagEntryBatch.clear();
435 m_Lookup = new h__PrimerLookup;
440 ASDCP::MXF::Primer::InitFromBuffer(const byte_t* p, ui32_t l)
443 Result_t result = KLVPacket::InitFromBuffer(p, l, m_Dict->ul(MDD_Primer));
445 if ( ASDCP_SUCCESS(result) )
447 Kumu::MemIOReader MemRDR(m_ValueStart, m_ValueLength);
448 result = LocalTagEntryBatch.Unarchive(&MemRDR) ? RESULT_OK : RESULT_KLV_CODING;
451 if ( ASDCP_SUCCESS(result) )
453 m_Lookup = new h__PrimerLookup;
454 m_Lookup->InitWithBatch(LocalTagEntryBatch);
457 if ( ASDCP_FAILURE(result) )
458 DefaultLogSink().Error("Failed to initialize Primer\n");
465 ASDCP::MXF::Primer::WriteToFile(Kumu::FileWriter& Writer)
467 ASDCP::FrameBuffer Buffer;
468 Result_t result = Buffer.Capacity(128*1024);
470 if ( ASDCP_SUCCESS(result) )
471 result = WriteToBuffer(Buffer);
473 if ( ASDCP_SUCCESS(result) )
474 result = Writer.Write(Buffer.RoData(), Buffer.Size());
481 ASDCP::MXF::Primer::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
484 ASDCP::FrameBuffer LocalTagBuffer;
485 Kumu::MemIOWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length);
486 Result_t result = LocalTagEntryBatch.Archive(&MemWRT) ? RESULT_OK : RESULT_KLV_CODING;
488 if ( ASDCP_SUCCESS(result) )
490 ui32_t packet_length = MemWRT.Length();
491 result = WriteKLToBuffer(Buffer, packet_length);
493 if ( ASDCP_SUCCESS(result) )
494 Buffer.Size(Buffer.Size() + packet_length);
502 ASDCP::MXF::Primer::InsertTag(const MDDEntry& Entry, ASDCP::TagValue& Tag)
506 std::map<UL, TagValue>::iterator i = m_Lookup->find(TestUL);
508 if ( i == m_Lookup->end() )
510 if ( Entry.tag.a == 0 && Entry.tag.b == 0 )
513 Tag.b = m_LocalTag--;
521 LocalTagEntry TmpEntry;
522 TmpEntry.UL = TestUL;
525 LocalTagEntryBatch.push_back(TmpEntry);
526 m_Lookup->insert(std::map<UL, TagValue>::value_type(TmpEntry.UL, TmpEntry.Tag));
538 ASDCP::MXF::Primer::TagForKey(const ASDCP::UL& Key, ASDCP::TagValue& Tag)
541 if ( m_Lookup.empty() )
543 DefaultLogSink().Error("Primer lookup is empty\n");
547 std::map<UL, TagValue>::iterator i = m_Lookup->find(Key);
549 if ( i == m_Lookup->end() )
558 ASDCP::MXF::Primer::Dump(FILE* stream)
561 char identbuf[IdentBufferLen];
566 KLVPacket::Dump(stream, *m_Dict, false);
567 fprintf(stream, "Primer: %u %s\n",
568 (ui32_t)LocalTagEntryBatch.size(),
569 ( LocalTagEntryBatch.size() == 1 ? "entry" : "entries" ));
571 Batch<LocalTagEntry>::iterator i = LocalTagEntryBatch.begin();
572 for ( ; i != LocalTagEntryBatch.end(); i++ )
574 const MDDEntry* Entry = m_Dict->FindUL((*i).UL.Value());
575 fprintf(stream, " %s %s\n", (*i).EncodeString(identbuf, IdentBufferLen), (Entry ? Entry->name : "Unknown"));
580 //------------------------------------------------------------------------------------------
584 ASDCP::MXF::Preface::Preface(const Dictionary*& d) :
585 InterchangeObject(d), m_Dict(d), Version(258), ObjectModelVersion(0)
588 m_UL = m_Dict->Type(MDD_Preface).ul;
593 ASDCP::MXF::Preface::Copy(const Preface& rhs)
595 InterchangeObject::Copy(rhs);
597 LastModifiedDate = rhs.LastModifiedDate;
598 Version = rhs.Version;
599 ObjectModelVersion = rhs.ObjectModelVersion;
600 PrimaryPackage = rhs.PrimaryPackage;
601 Identifications = rhs.Identifications;
602 ContentStorage = rhs.ContentStorage;
603 OperationalPattern = rhs.OperationalPattern;
604 EssenceContainers = rhs.EssenceContainers;
605 DMSchemes = rhs.DMSchemes;
610 ASDCP::MXF::Preface::InitFromTLVSet(TLVReader& TLVSet)
612 Result_t result = InterchangeObject::InitFromTLVSet(TLVSet);
613 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, LastModifiedDate));
614 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi16(OBJ_READ_ARGS(Preface, Version));
615 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi32(OBJ_READ_ARGS(Preface, ObjectModelVersion));
616 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, PrimaryPackage));
617 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, Identifications));
618 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, ContentStorage));
619 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, OperationalPattern));
620 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, EssenceContainers));
621 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, DMSchemes));
627 ASDCP::MXF::Preface::WriteToTLVSet(TLVWriter& TLVSet)
629 Result_t result = InterchangeObject::WriteToTLVSet(TLVSet);
630 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, LastModifiedDate));
631 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi16(OBJ_WRITE_ARGS(Preface, Version));
632 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi32(OBJ_WRITE_ARGS(Preface, ObjectModelVersion));
633 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, PrimaryPackage));
634 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, Identifications));
635 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, ContentStorage));
636 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, OperationalPattern));
637 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, EssenceContainers));
638 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, DMSchemes));
644 ASDCP::MXF::Preface::InitFromBuffer(const byte_t* p, ui32_t l)
646 return InterchangeObject::InitFromBuffer(p, l);
651 ASDCP::MXF::Preface::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
653 return InterchangeObject::WriteToBuffer(Buffer);
658 ASDCP::MXF::Preface::Dump(FILE* stream)
660 char identbuf[IdentBufferLen];
665 InterchangeObject::Dump(stream);
666 fprintf(stream, " %22s = %s\n", "LastModifiedDate", LastModifiedDate.EncodeString(identbuf, IdentBufferLen));
667 fprintf(stream, " %22s = %hu\n", "Version", Version);
668 fprintf(stream, " %22s = %u\n", "ObjectModelVersion", ObjectModelVersion);
669 fprintf(stream, " %22s = %s\n", "PrimaryPackage", PrimaryPackage.EncodeHex(identbuf, IdentBufferLen));
670 fprintf(stream, " %22s:\n", "Identifications"); Identifications.Dump(stream);
671 fprintf(stream, " %22s = %s\n", "ContentStorage", ContentStorage.EncodeHex(identbuf, IdentBufferLen));
672 fprintf(stream, " %22s = %s\n", "OperationalPattern", OperationalPattern.EncodeString(identbuf, IdentBufferLen));
673 fprintf(stream, " %22s:\n", "EssenceContainers"); EssenceContainers.Dump(stream);
674 fprintf(stream, " %22s:\n", "DMSchemes"); DMSchemes.Dump(stream);
677 //------------------------------------------------------------------------------------------
680 ASDCP::MXF::OPAtomHeader::OPAtomHeader(const Dictionary*& d) : Partition(d), m_Dict(d), m_RIP(d), m_Primer(d), m_Preface(0), m_HasRIP(false) {}
681 ASDCP::MXF::OPAtomHeader::~OPAtomHeader() {}
685 ASDCP::MXF::OPAtomHeader::InitFromFile(const Kumu::FileReader& Reader)
688 Result_t result = SeekToRIP(Reader);
690 if ( ASDCP_SUCCESS(result) )
692 result = m_RIP.InitFromFile(Reader);
693 ui32_t test_s = m_RIP.PairArray.size();
695 if ( ASDCP_FAILURE(result) )
697 DefaultLogSink().Error("File contains no RIP\n");
700 else if ( test_s == 0 )
702 DefaultLogSink().Error("RIP contains no Pairs.\n");
703 result = RESULT_FORMAT;
709 // OP-Atom states that there will be either two or three partitions:
710 // one closed header and one closed footer with an optional body
711 // SMPTE 429-5 files may have many partitions, see SMPTE 410M
712 DefaultLogSink().Warn("RIP count is less than 2: %u\n", test_s);
717 if ( m_RIP.PairArray.front().ByteOffset != 0 )
719 DefaultLogSink().Error("First Partition in RIP is not at offset 0.\n");
720 result = RESULT_FORMAT;
725 if ( ASDCP_SUCCESS(result) )
726 result = Reader.Seek(0);
728 if ( ASDCP_SUCCESS(result) )
729 result = Partition::InitFromFile(Reader); // test UL and OP
731 if ( ASDCP_FAILURE(result) )
734 // is it really OP-Atom?
736 UL OPAtomUL(SMPTE_390_OPAtom_Entry().ul);
737 UL InteropOPAtomUL(MXFInterop_OPAtom_Entry().ul);
739 if ( OperationalPattern.ExactMatch(OPAtomUL) ) // SMPTE
741 if ( m_Dict == &DefaultCompositeDict() )
742 m_Dict = &DefaultSMPTEDict();
744 else if ( OperationalPattern.ExactMatch(InteropOPAtomUL) ) // Interop
746 if ( m_Dict == &DefaultCompositeDict() )
747 m_Dict = &DefaultInteropDict();
751 char strbuf[IdentBufferLen];
752 const MDDEntry* Entry = m_Dict->FindUL(OperationalPattern.Value());
754 DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n",
755 OperationalPattern.EncodeString(strbuf, IdentBufferLen));
757 DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n", Entry->name);
760 // slurp up the remainder of the header
761 if ( HeaderByteCount < 1024 )
762 DefaultLogSink().Warn("Improbably small HeaderByteCount value: %u\n", HeaderByteCount);
764 assert (HeaderByteCount <= 0xFFFFFFFFL);
765 result = m_Buffer.Capacity((ui32_t) HeaderByteCount);
767 if ( ASDCP_SUCCESS(result) )
770 result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
772 if ( ASDCP_FAILURE(result) )
775 if ( read_count != m_Buffer.Capacity() )
777 DefaultLogSink().Error("Short read of OP-Atom header metadata; wanted %u, got %u\n",
778 m_Buffer.Capacity(), read_count);
779 return RESULT_KLV_CODING;
783 if ( ASDCP_SUCCESS(result) )
784 result = InitFromBuffer(m_Buffer.RoData(), m_Buffer.Capacity());
791 ASDCP::MXF::OPAtomHeader::InitFromPartitionBuffer(const byte_t* p, ui32_t l)
793 Result_t result = KLVPacket::InitFromBuffer(p, l);
795 if ( ASDCP_SUCCESS(result) )
796 result = Partition::InitFromBuffer(m_ValueStart, m_ValueLength); // test UL and OP
798 if ( ASDCP_SUCCESS(result) )
800 ui32_t pp_len = KLVPacket::PacketLength();
801 result = InitFromBuffer(p + pp_len, l - pp_len);
809 ASDCP::MXF::OPAtomHeader::InitFromBuffer(const byte_t* p, ui32_t l)
812 Result_t result = RESULT_OK;
813 const byte_t* end_p = p + l;
815 while ( ASDCP_SUCCESS(result) && p < end_p )
817 // parse the packets and index them by uid, discard KLVFill items
818 InterchangeObject* object = CreateObject(m_Dict, p);
821 object->m_Lookup = &m_Primer;
822 result = object->InitFromBuffer(p, end_p - p);
823 const byte_t* redo_p = p;
824 p += object->PacketLength();
825 // hexdump(p, object->PacketLength());
827 if ( ASDCP_SUCCESS(result) )
829 if ( object->IsA(m_Dict->ul(MDD_KLVFill)) )
833 else if ( object->IsA(m_Dict->ul(MDD_Primer)) ) // TODO: only one primer should be found
836 result = m_Primer.InitFromBuffer(redo_p, end_p - redo_p);
840 m_PacketList->AddPacket(object); // takes ownership
842 if ( object->IsA(m_Dict->ul(MDD_Preface)) && m_Preface == 0 )
843 m_Preface = (Preface*)object;
848 DefaultLogSink().Error("Error initializing packet\n");
857 ASDCP::MXF::OPAtomHeader::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
859 return m_PacketList->GetMDObjectByID(ObjectID, Object);
864 ASDCP::MXF::OPAtomHeader::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
866 InterchangeObject* TmpObject;
871 return m_PacketList->GetMDObjectByType(ObjectID, Object);
876 ASDCP::MXF::OPAtomHeader::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
878 return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
882 ASDCP::MXF::Identification*
883 ASDCP::MXF::OPAtomHeader::GetIdentification()
885 InterchangeObject* Object;
887 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object)) )
888 return (Identification*)Object;
894 ASDCP::MXF::SourcePackage*
895 ASDCP::MXF::OPAtomHeader::GetSourcePackage()
897 InterchangeObject* Object;
899 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object)) )
900 return (SourcePackage*)Object;
908 ASDCP::MXF::OPAtomHeader::WriteToFile(Kumu::FileWriter& Writer, ui32_t HeaderSize)
911 if ( m_Preface == 0 )
914 if ( HeaderSize < 4096 )
916 DefaultLogSink().Error("HeaderSize %u is too small. Must be >= 4096\n", HeaderSize);
920 ASDCP::FrameBuffer HeaderBuffer;
921 HeaderByteCount = HeaderSize - ArchiveSize();
922 assert (HeaderByteCount <= 0xFFFFFFFFL);
923 Result_t result = HeaderBuffer.Capacity((ui32_t) HeaderByteCount);
924 m_Preface->m_Lookup = &m_Primer;
926 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
927 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
929 InterchangeObject* object = *pl_i;
930 object->m_Lookup = &m_Primer;
932 ASDCP::FrameBuffer WriteWrapper;
933 WriteWrapper.SetData(HeaderBuffer.Data() + HeaderBuffer.Size(),
934 HeaderBuffer.Capacity() - HeaderBuffer.Size());
935 result = object->WriteToBuffer(WriteWrapper);
936 HeaderBuffer.Size(HeaderBuffer.Size() + WriteWrapper.Size());
939 if ( ASDCP_SUCCESS(result) )
941 UL TmpUL(m_Dict->ul(MDD_ClosedCompleteHeader));
942 result = Partition::WriteToFile(Writer, TmpUL);
945 if ( ASDCP_SUCCESS(result) )
946 result = m_Primer.WriteToFile(Writer);
948 if ( ASDCP_SUCCESS(result) )
951 Writer.Write(HeaderBuffer.RoData(), HeaderBuffer.Size(), &write_count);
952 assert(write_count == HeaderBuffer.Size());
956 if ( ASDCP_SUCCESS(result) )
958 Kumu::fpos_t pos = Writer.Tell();
960 if ( pos > (Kumu::fpos_t)HeaderByteCount )
962 char intbuf[IntBufferLen];
963 DefaultLogSink().Error("Header size %s exceeds specified value %u\n",
969 ASDCP::FrameBuffer NilBuf;
970 ui32_t klv_fill_length = HeaderSize - (ui32_t)pos;
972 if ( klv_fill_length < kl_length )
974 DefaultLogSink().Error("Remaining region too small for KLV Fill header\n");
978 klv_fill_length -= kl_length;
979 result = WriteKLToFile(Writer, m_Dict->ul(MDD_KLVFill), klv_fill_length);
981 if ( ASDCP_SUCCESS(result) )
982 result = NilBuf.Capacity(klv_fill_length);
984 if ( ASDCP_SUCCESS(result) )
986 memset(NilBuf.Data(), 0, klv_fill_length);
988 Writer.Write(NilBuf.RoData(), klv_fill_length, &write_count);
989 assert(write_count == klv_fill_length);
998 ASDCP::MXF::OPAtomHeader::Dump(FILE* stream)
1003 Partition::Dump(stream);
1004 m_Primer.Dump(stream);
1006 if ( m_Preface == 0 )
1007 fputs("No Preface loaded\n", stream);
1009 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
1010 for ( ; i != m_PacketList->m_List.end(); i++ )
1014 //------------------------------------------------------------------------------------------
1017 ASDCP::MXF::OPAtomIndexFooter::OPAtomIndexFooter(const Dictionary*& d) :
1018 Partition(d), m_Dict(d),
1019 m_CurrentSegment(0), m_BytesPerEditUnit(0), m_BodySID(0),
1020 m_ECOffset(0), m_Lookup(0)
1026 ASDCP::MXF::OPAtomIndexFooter::~OPAtomIndexFooter() {}
1030 ASDCP::MXF::OPAtomIndexFooter::InitFromFile(const Kumu::FileReader& Reader)
1032 Result_t result = Partition::InitFromFile(Reader); // test UL and OP
1034 // slurp up the remainder of the footer
1037 if ( ASDCP_SUCCESS(result) )
1039 assert (IndexByteCount <= 0xFFFFFFFFL);
1040 result = m_Buffer.Capacity((ui32_t) IndexByteCount);
1043 if ( ASDCP_SUCCESS(result) )
1044 result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
1046 if ( ASDCP_SUCCESS(result) && read_count != m_Buffer.Capacity() )
1048 DefaultLogSink().Error("Short read of footer partition: got %u, expecting %u\n",
1049 read_count, m_Buffer.Capacity());
1053 if ( ASDCP_SUCCESS(result) )
1054 result = InitFromBuffer(m_Buffer.RoData(), m_Buffer.Capacity());
1061 ASDCP::MXF::OPAtomIndexFooter::InitFromPartitionBuffer(const byte_t* p, ui32_t l)
1063 Result_t result = KLVPacket::InitFromBuffer(p, l);
1065 if ( ASDCP_SUCCESS(result) )
1066 result = Partition::InitFromBuffer(m_ValueStart, m_ValueLength); // test UL and OP
1068 if ( ASDCP_SUCCESS(result) )
1070 ui32_t pp_len = KLVPacket::PacketLength();
1071 result = InitFromBuffer(p + pp_len, l - pp_len);
1079 ASDCP::MXF::OPAtomIndexFooter::InitFromBuffer(const byte_t* p, ui32_t l)
1081 Result_t result = RESULT_OK;
1082 const byte_t* end_p = p + l;
1084 while ( ASDCP_SUCCESS(result) && p < end_p )
1086 // parse the packets and index them by uid, discard KLVFill items
1087 InterchangeObject* object = CreateObject(m_Dict, p);
1090 object->m_Lookup = m_Lookup;
1091 result = object->InitFromBuffer(p, end_p - p);
1092 p += object->PacketLength();
1094 if ( ASDCP_SUCCESS(result) )
1096 m_PacketList->AddPacket(object); // takes ownership
1100 DefaultLogSink().Error("Error initializing packet\n");
1105 if ( ASDCP_FAILURE(result) )
1106 DefaultLogSink().Error("Failed to initialize OPAtomIndexFooter\n");
1113 ASDCP::MXF::OPAtomIndexFooter::WriteToFile(Kumu::FileWriter& Writer, ui64_t duration)
1116 ASDCP::FrameBuffer FooterBuffer;
1117 ui32_t footer_size = m_PacketList->m_List.size() * MaxIndexSegmentSize; // segment-count * max-segment-size
1118 Result_t result = FooterBuffer.Capacity(footer_size);
1119 ui32_t iseg_count = 0;
1121 if ( m_CurrentSegment != 0 )
1123 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1124 m_CurrentSegment = 0;
1127 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
1128 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
1130 if ( (*pl_i)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1133 IndexTableSegment* Segment = (IndexTableSegment*)(*pl_i);
1135 if ( m_BytesPerEditUnit != 0 )
1137 if ( iseg_count != 1 )
1138 return RESULT_STATE;
1140 Segment->IndexDuration = duration;
1144 InterchangeObject* object = *pl_i;
1145 object->m_Lookup = m_Lookup;
1147 ASDCP::FrameBuffer WriteWrapper;
1148 WriteWrapper.SetData(FooterBuffer.Data() + FooterBuffer.Size(),
1149 FooterBuffer.Capacity() - FooterBuffer.Size());
1150 result = object->WriteToBuffer(WriteWrapper);
1151 FooterBuffer.Size(FooterBuffer.Size() + WriteWrapper.Size());
1154 if ( ASDCP_SUCCESS(result) )
1156 IndexByteCount = FooterBuffer.Size();
1157 UL FooterUL(m_Dict->ul(MDD_CompleteFooter));
1158 result = Partition::WriteToFile(Writer, FooterUL);
1161 if ( ASDCP_SUCCESS(result) )
1163 ui32_t write_count = 0;
1164 result = Writer.Write(FooterBuffer.RoData(), FooterBuffer.Size(), &write_count);
1165 assert(write_count == FooterBuffer.Size());
1173 ASDCP::MXF::OPAtomIndexFooter::Dump(FILE* stream)
1178 Partition::Dump(stream);
1180 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
1181 for ( ; i != m_PacketList->m_List.end(); i++ )
1187 ASDCP::MXF::OPAtomIndexFooter::Lookup(ui32_t frame_num, IndexTableSegment::IndexEntry& Entry) const
1189 std::list<InterchangeObject*>::iterator li;
1190 for ( li = m_PacketList->m_List.begin(); li != m_PacketList->m_List.end(); li++ )
1192 if ( (*li)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1194 IndexTableSegment* Segment = (IndexTableSegment*)(*li);
1195 ui64_t start_pos = Segment->IndexStartPosition;
1197 if ( Segment->EditUnitByteCount > 0 )
1199 if ( m_PacketList->m_List.size() > 1 )
1200 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
1202 if ( ! Segment->IndexEntryArray.empty() )
1203 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
1205 Entry.StreamOffset = (ui64_t)frame_num * Segment->EditUnitByteCount;
1208 else if ( (ui64_t)frame_num >= start_pos
1209 && (ui64_t)frame_num < (start_pos + Segment->IndexDuration) )
1211 ui64_t tmp = frame_num - start_pos;
1212 assert(tmp <= 0xFFFFFFFFL);
1213 Entry = Segment->IndexEntryArray[(ui32_t) tmp];
1224 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const Rational& Rate)
1228 m_BytesPerEditUnit = size;
1231 IndexTableSegment* Index = new IndexTableSegment(m_Dict);
1232 AddChildObject(Index);
1233 Index->EditUnitByteCount = m_BytesPerEditUnit;
1234 Index->IndexEditRate = Rate;
1239 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsVBR(IPrimerLookup* lookup, const Rational& Rate, Kumu::fpos_t offset)
1243 m_BytesPerEditUnit = 0;
1245 m_ECOffset = offset;
1250 ASDCP::MXF::OPAtomIndexFooter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
1252 if ( m_BytesPerEditUnit != 0 ) // are we CBR? that's bad
1254 DefaultLogSink().Error("Call to PushIndexEntry() failed: index is CBR\n");
1258 // do we have an available segment?
1259 if ( m_CurrentSegment == 0 )
1260 { // no, set up a new segment
1261 m_CurrentSegment = new IndexTableSegment(m_Dict);
1262 assert(m_CurrentSegment);
1263 AddChildObject(m_CurrentSegment);
1264 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1265 m_CurrentSegment->IndexEditRate = m_EditRate;
1266 m_CurrentSegment->IndexStartPosition = 0;
1268 else if ( m_CurrentSegment->IndexEntryArray.size() >= CBRIndexEntriesPerSegment )
1269 { // no, this one is full, start another
1270 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1271 ui64_t StartPosition = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
1273 m_CurrentSegment = new IndexTableSegment(m_Dict);
1274 assert(m_CurrentSegment);
1275 AddChildObject(m_CurrentSegment);
1276 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1277 m_CurrentSegment->IndexEditRate = m_EditRate;
1278 m_CurrentSegment->IndexStartPosition = StartPosition;
1281 m_CurrentSegment->IndexEntryArray.push_back(Entry);
1284 //------------------------------------------------------------------------------------------
1289 ASDCP::MXF::InterchangeObject::Copy(const InterchangeObject& rhs)
1292 InstanceUID = rhs.InstanceUID;
1293 GenerationUID = rhs.GenerationUID;
1298 ASDCP::MXF::InterchangeObject::InitFromTLVSet(TLVReader& TLVSet)
1300 Result_t result = TLVSet.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID));
1301 if ( ASDCP_SUCCESS(result) )
1302 result = TLVSet.ReadObject(OBJ_READ_ARGS(GenerationInterchangeObject, GenerationUID));
1308 ASDCP::MXF::InterchangeObject::WriteToTLVSet(TLVWriter& TLVSet)
1310 Result_t result = TLVSet.WriteObject(OBJ_WRITE_ARGS(InterchangeObject, InstanceUID));
1311 if ( ASDCP_SUCCESS(result) )
1312 result = TLVSet.WriteObject(OBJ_WRITE_ARGS(GenerationInterchangeObject, GenerationUID));
1318 ASDCP::MXF::InterchangeObject::InitFromBuffer(const byte_t* p, ui32_t l)
1321 Result_t result = RESULT_FALSE;
1323 if ( m_UL.HasValue() )
1325 result = KLVPacket::InitFromBuffer(p, l, m_UL);
1327 if ( ASDCP_SUCCESS(result) )
1329 TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup);
1330 result = InitFromTLVSet(MemRDR);
1335 result = KLVPacket::InitFromBuffer(p, l);
1343 ASDCP::MXF::InterchangeObject::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
1345 if ( ! m_UL.HasValue() )
1346 return RESULT_STATE;
1348 TLVWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length, m_Lookup);
1349 Result_t result = WriteToTLVSet(MemWRT);
1351 if ( ASDCP_SUCCESS(result) )
1353 ui32_t packet_length = MemWRT.Length();
1354 result = WriteKLToBuffer(Buffer, packet_length);
1356 if ( ASDCP_SUCCESS(result) )
1357 Buffer.Size(Buffer.Size() + packet_length);
1365 ASDCP::MXF::InterchangeObject::Dump(FILE* stream)
1367 char identbuf[IdentBufferLen];
1369 fputc('\n', stream);
1370 KLVPacket::Dump(stream, *m_Dict, false);
1371 fprintf(stream, " InstanceUID = %s\n", InstanceUID.EncodeHex(identbuf, IdentBufferLen));
1372 fprintf(stream, " GenerationUID = %s\n", GenerationUID.EncodeHex(identbuf, IdentBufferLen));
1377 ASDCP::MXF::InterchangeObject::IsA(const byte_t* label)
1379 if ( m_KLLength == 0 )
1382 return ( memcmp(label, m_KeyStart, SMPTE_UL_LENGTH) == 0 );
1386 //------------------------------------------------------------------------------------------
1389 typedef std::map<ASDCP::UL, ASDCP::MXF::MXFObjectFactory_t>FactoryMap_t;
1390 typedef FactoryMap_t::iterator FLi_t;
1393 class FactoryList : public FactoryMap_t
1402 Kumu::AutoMutex BlockLock(m_Lock);
1406 FLi_t Find(const byte_t* label) {
1407 Kumu::AutoMutex BlockLock(m_Lock);
1412 Kumu::AutoMutex BlockLock(m_Lock);
1416 void Insert(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory) {
1417 Kumu::AutoMutex BlockLock(m_Lock);
1418 insert(FactoryList::value_type(label, factory));
1423 static FactoryList s_FactoryList;
1424 static Kumu::Mutex s_InitLock;
1425 static bool s_TypesInit = false;
1430 ASDCP::MXF::SetObjectFactory(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory)
1432 s_FactoryList.Insert(label, factory);
1436 ASDCP::MXF::InterchangeObject*
1437 ASDCP::MXF::CreateObject(const Dictionary*& Dict, const UL& label)
1439 if ( ! s_TypesInit )
1441 Kumu::AutoMutex BlockLock(s_InitLock);
1443 if ( ! s_TypesInit )
1445 MXF::Metadata_InitTypes(Dict);
1450 FLi_t i = s_FactoryList.find(label.Value());
1452 if ( i == s_FactoryList.end() )
1453 return new InterchangeObject(Dict);
1455 return i->second(Dict);