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.
28 \version $Id: MXF.cpp,v 1.62 2012/03/05 13:11:47 jhurst Exp $
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;
907 ASDCP::MXF::OPAtomHeader::GetRIP() { return m_RIP; }
911 ASDCP::MXF::OPAtomHeader::WriteToFile(Kumu::FileWriter& Writer, ui32_t HeaderSize)
914 if ( m_Preface == 0 )
917 if ( HeaderSize < 4096 )
919 DefaultLogSink().Error("HeaderSize %u is too small. Must be >= 4096\n", HeaderSize);
923 ASDCP::FrameBuffer HeaderBuffer;
924 HeaderByteCount = HeaderSize - ArchiveSize();
925 assert (HeaderByteCount <= 0xFFFFFFFFL);
926 Result_t result = HeaderBuffer.Capacity((ui32_t) HeaderByteCount);
927 m_Preface->m_Lookup = &m_Primer;
929 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
930 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
932 InterchangeObject* object = *pl_i;
933 object->m_Lookup = &m_Primer;
935 ASDCP::FrameBuffer WriteWrapper;
936 WriteWrapper.SetData(HeaderBuffer.Data() + HeaderBuffer.Size(),
937 HeaderBuffer.Capacity() - HeaderBuffer.Size());
938 result = object->WriteToBuffer(WriteWrapper);
939 HeaderBuffer.Size(HeaderBuffer.Size() + WriteWrapper.Size());
942 if ( ASDCP_SUCCESS(result) )
944 UL TmpUL(m_Dict->ul(MDD_ClosedCompleteHeader));
945 result = Partition::WriteToFile(Writer, TmpUL);
948 if ( ASDCP_SUCCESS(result) )
949 result = m_Primer.WriteToFile(Writer);
951 if ( ASDCP_SUCCESS(result) )
954 Writer.Write(HeaderBuffer.RoData(), HeaderBuffer.Size(), &write_count);
955 assert(write_count == HeaderBuffer.Size());
959 if ( ASDCP_SUCCESS(result) )
961 Kumu::fpos_t pos = Writer.Tell();
963 if ( pos > (Kumu::fpos_t)HeaderByteCount )
965 char intbuf[IntBufferLen];
966 DefaultLogSink().Error("Header size %s exceeds specified value %u\n",
972 ASDCP::FrameBuffer NilBuf;
973 ui32_t klv_fill_length = HeaderSize - (ui32_t)pos;
975 if ( klv_fill_length < kl_length )
977 DefaultLogSink().Error("Remaining region too small for KLV Fill header\n");
981 klv_fill_length -= kl_length;
982 result = WriteKLToFile(Writer, m_Dict->ul(MDD_KLVFill), klv_fill_length);
984 if ( ASDCP_SUCCESS(result) )
985 result = NilBuf.Capacity(klv_fill_length);
987 if ( ASDCP_SUCCESS(result) )
989 memset(NilBuf.Data(), 0, klv_fill_length);
991 Writer.Write(NilBuf.RoData(), klv_fill_length, &write_count);
992 assert(write_count == klv_fill_length);
1001 ASDCP::MXF::OPAtomHeader::Dump(FILE* stream)
1006 Partition::Dump(stream);
1007 m_Primer.Dump(stream);
1009 if ( m_Preface == 0 )
1010 fputs("No Preface loaded\n", stream);
1012 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
1013 for ( ; i != m_PacketList->m_List.end(); i++ )
1017 //------------------------------------------------------------------------------------------
1020 ASDCP::MXF::OPAtomIndexFooter::OPAtomIndexFooter(const Dictionary*& d) :
1022 m_CurrentSegment(0), m_BytesPerEditUnit(0), m_BodySID(0), m_Dict(d),
1023 m_ECOffset(0), m_Lookup(0)
1029 ASDCP::MXF::OPAtomIndexFooter::~OPAtomIndexFooter() {}
1033 ASDCP::MXF::OPAtomIndexFooter::InitFromFile(const Kumu::FileReader& Reader)
1035 Result_t result = Partition::InitFromFile(Reader); // test UL and OP
1037 // slurp up the remainder of the footer
1040 if ( ASDCP_SUCCESS(result) )
1042 assert (IndexByteCount <= 0xFFFFFFFFL);
1043 result = m_Buffer.Capacity((ui32_t) IndexByteCount);
1046 if ( ASDCP_SUCCESS(result) )
1047 result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
1049 if ( ASDCP_SUCCESS(result) && read_count != m_Buffer.Capacity() )
1051 DefaultLogSink().Error("Short read of footer partition: got %u, expecting %u\n",
1052 read_count, m_Buffer.Capacity());
1056 if ( ASDCP_SUCCESS(result) )
1057 result = InitFromBuffer(m_Buffer.RoData(), m_Buffer.Capacity());
1064 ASDCP::MXF::OPAtomIndexFooter::InitFromPartitionBuffer(const byte_t* p, ui32_t l)
1066 Result_t result = KLVPacket::InitFromBuffer(p, l);
1068 if ( ASDCP_SUCCESS(result) )
1069 result = Partition::InitFromBuffer(m_ValueStart, m_ValueLength); // test UL and OP
1071 if ( ASDCP_SUCCESS(result) )
1073 ui32_t pp_len = KLVPacket::PacketLength();
1074 result = InitFromBuffer(p + pp_len, l - pp_len);
1082 ASDCP::MXF::OPAtomIndexFooter::InitFromBuffer(const byte_t* p, ui32_t l)
1084 Result_t result = RESULT_OK;
1085 const byte_t* end_p = p + l;
1087 while ( ASDCP_SUCCESS(result) && p < end_p )
1089 // parse the packets and index them by uid, discard KLVFill items
1090 InterchangeObject* object = CreateObject(m_Dict, p);
1093 object->m_Lookup = m_Lookup;
1094 result = object->InitFromBuffer(p, end_p - p);
1095 p += object->PacketLength();
1097 if ( ASDCP_SUCCESS(result) )
1099 m_PacketList->AddPacket(object); // takes ownership
1103 DefaultLogSink().Error("Error initializing packet\n");
1108 if ( ASDCP_FAILURE(result) )
1109 DefaultLogSink().Error("Failed to initialize OPAtomIndexFooter\n");
1116 ASDCP::MXF::OPAtomIndexFooter::WriteToFile(Kumu::FileWriter& Writer, ui64_t duration)
1119 ASDCP::FrameBuffer FooterBuffer;
1120 ui32_t footer_size = m_PacketList->m_List.size() * MaxIndexSegmentSize; // segment-count * max-segment-size
1121 Result_t result = FooterBuffer.Capacity(footer_size);
1122 ui32_t iseg_count = 0;
1124 if ( m_CurrentSegment != 0 )
1126 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1127 m_CurrentSegment = 0;
1130 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
1131 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
1133 if ( (*pl_i)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1136 IndexTableSegment* Segment = (IndexTableSegment*)(*pl_i);
1138 if ( m_BytesPerEditUnit != 0 )
1140 if ( iseg_count != 1 )
1141 return RESULT_STATE;
1143 Segment->IndexDuration = duration;
1147 InterchangeObject* object = *pl_i;
1148 object->m_Lookup = m_Lookup;
1150 ASDCP::FrameBuffer WriteWrapper;
1151 WriteWrapper.SetData(FooterBuffer.Data() + FooterBuffer.Size(),
1152 FooterBuffer.Capacity() - FooterBuffer.Size());
1153 result = object->WriteToBuffer(WriteWrapper);
1154 FooterBuffer.Size(FooterBuffer.Size() + WriteWrapper.Size());
1157 if ( ASDCP_SUCCESS(result) )
1159 IndexByteCount = FooterBuffer.Size();
1160 UL FooterUL(m_Dict->ul(MDD_CompleteFooter));
1161 result = Partition::WriteToFile(Writer, FooterUL);
1164 if ( ASDCP_SUCCESS(result) )
1166 ui32_t write_count = 0;
1167 result = Writer.Write(FooterBuffer.RoData(), FooterBuffer.Size(), &write_count);
1168 assert(write_count == FooterBuffer.Size());
1176 ASDCP::MXF::OPAtomIndexFooter::Dump(FILE* stream)
1181 Partition::Dump(stream);
1183 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
1184 for ( ; i != m_PacketList->m_List.end(); i++ )
1189 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
1191 return m_PacketList->GetMDObjectByID(ObjectID, Object);
1196 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
1198 InterchangeObject* TmpObject;
1201 Object = &TmpObject;
1203 return m_PacketList->GetMDObjectByType(ObjectID, Object);
1208 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
1210 return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
1215 ASDCP::MXF::OPAtomIndexFooter::Lookup(ui32_t frame_num, IndexTableSegment::IndexEntry& Entry) const
1217 std::list<InterchangeObject*>::iterator li;
1218 for ( li = m_PacketList->m_List.begin(); li != m_PacketList->m_List.end(); li++ )
1220 if ( (*li)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1222 IndexTableSegment* Segment = (IndexTableSegment*)(*li);
1223 ui64_t start_pos = Segment->IndexStartPosition;
1225 if ( Segment->EditUnitByteCount > 0 )
1227 if ( m_PacketList->m_List.size() > 1 )
1228 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
1230 if ( ! Segment->IndexEntryArray.empty() )
1231 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
1233 Entry.StreamOffset = (ui64_t)frame_num * Segment->EditUnitByteCount;
1236 else if ( (ui64_t)frame_num >= start_pos
1237 && (ui64_t)frame_num < (start_pos + Segment->IndexDuration) )
1239 ui64_t tmp = frame_num - start_pos;
1240 assert(tmp <= 0xFFFFFFFFL);
1241 Entry = Segment->IndexEntryArray[(ui32_t) tmp];
1252 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const Rational& Rate)
1256 m_BytesPerEditUnit = size;
1259 IndexTableSegment* Index = new IndexTableSegment(m_Dict);
1260 AddChildObject(Index);
1261 Index->EditUnitByteCount = m_BytesPerEditUnit;
1262 Index->IndexEditRate = Rate;
1267 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsVBR(IPrimerLookup* lookup, const Rational& Rate, Kumu::fpos_t offset)
1271 m_BytesPerEditUnit = 0;
1273 m_ECOffset = offset;
1278 ASDCP::MXF::OPAtomIndexFooter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
1280 if ( m_BytesPerEditUnit != 0 ) // are we CBR? that's bad
1282 DefaultLogSink().Error("Call to PushIndexEntry() failed: index is CBR\n");
1286 // do we have an available segment?
1287 if ( m_CurrentSegment == 0 )
1288 { // no, set up a new segment
1289 m_CurrentSegment = new IndexTableSegment(m_Dict);
1290 assert(m_CurrentSegment);
1291 AddChildObject(m_CurrentSegment);
1292 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1293 m_CurrentSegment->IndexEditRate = m_EditRate;
1294 m_CurrentSegment->IndexStartPosition = 0;
1296 else if ( m_CurrentSegment->IndexEntryArray.size() >= CBRIndexEntriesPerSegment )
1297 { // no, this one is full, start another
1298 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1299 ui64_t StartPosition = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
1301 m_CurrentSegment = new IndexTableSegment(m_Dict);
1302 assert(m_CurrentSegment);
1303 AddChildObject(m_CurrentSegment);
1304 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1305 m_CurrentSegment->IndexEditRate = m_EditRate;
1306 m_CurrentSegment->IndexStartPosition = StartPosition;
1309 m_CurrentSegment->IndexEntryArray.push_back(Entry);
1312 //------------------------------------------------------------------------------------------
1317 ASDCP::MXF::InterchangeObject::Copy(const InterchangeObject& rhs)
1320 InstanceUID = rhs.InstanceUID;
1321 GenerationUID = rhs.GenerationUID;
1326 ASDCP::MXF::InterchangeObject::InitFromTLVSet(TLVReader& TLVSet)
1328 Result_t result = TLVSet.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID));
1329 if ( ASDCP_SUCCESS(result) )
1330 result = TLVSet.ReadObject(OBJ_READ_ARGS(GenerationInterchangeObject, GenerationUID));
1336 ASDCP::MXF::InterchangeObject::WriteToTLVSet(TLVWriter& TLVSet)
1338 Result_t result = TLVSet.WriteObject(OBJ_WRITE_ARGS(InterchangeObject, InstanceUID));
1339 if ( ASDCP_SUCCESS(result) )
1340 result = TLVSet.WriteObject(OBJ_WRITE_ARGS(GenerationInterchangeObject, GenerationUID));
1346 ASDCP::MXF::InterchangeObject::InitFromBuffer(const byte_t* p, ui32_t l)
1349 Result_t result = RESULT_FALSE;
1351 if ( m_UL.HasValue() )
1353 result = KLVPacket::InitFromBuffer(p, l, m_UL);
1355 if ( ASDCP_SUCCESS(result) )
1357 TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup);
1358 result = InitFromTLVSet(MemRDR);
1363 result = KLVPacket::InitFromBuffer(p, l);
1371 ASDCP::MXF::InterchangeObject::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
1373 if ( ! m_UL.HasValue() )
1374 return RESULT_STATE;
1376 TLVWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length, m_Lookup);
1377 Result_t result = WriteToTLVSet(MemWRT);
1379 if ( ASDCP_SUCCESS(result) )
1381 ui32_t packet_length = MemWRT.Length();
1382 result = WriteKLToBuffer(Buffer, packet_length);
1384 if ( ASDCP_SUCCESS(result) )
1385 Buffer.Size(Buffer.Size() + packet_length);
1393 ASDCP::MXF::InterchangeObject::Dump(FILE* stream)
1395 char identbuf[IdentBufferLen];
1397 fputc('\n', stream);
1398 KLVPacket::Dump(stream, *m_Dict, false);
1399 fprintf(stream, " InstanceUID = %s\n", InstanceUID.EncodeHex(identbuf, IdentBufferLen));
1400 fprintf(stream, " GenerationUID = %s\n", GenerationUID.EncodeHex(identbuf, IdentBufferLen));
1405 ASDCP::MXF::InterchangeObject::IsA(const byte_t* label)
1407 if ( m_KLLength == 0 )
1410 return ( memcmp(label, m_KeyStart, SMPTE_UL_LENGTH) == 0 );
1414 //------------------------------------------------------------------------------------------
1417 typedef std::map<ASDCP::UL, ASDCP::MXF::MXFObjectFactory_t>FactoryMap_t;
1418 typedef FactoryMap_t::iterator FLi_t;
1421 class FactoryList : public FactoryMap_t
1430 Kumu::AutoMutex BlockLock(m_Lock);
1434 FLi_t Find(const byte_t* label) {
1435 Kumu::AutoMutex BlockLock(m_Lock);
1440 Kumu::AutoMutex BlockLock(m_Lock);
1444 void Insert(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory) {
1445 Kumu::AutoMutex BlockLock(m_Lock);
1446 insert(FactoryList::value_type(label, factory));
1451 static FactoryList s_FactoryList;
1452 static Kumu::Mutex s_InitLock;
1453 static bool s_TypesInit = false;
1458 ASDCP::MXF::SetObjectFactory(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory)
1460 s_FactoryList.Insert(label, factory);
1464 ASDCP::MXF::InterchangeObject*
1465 ASDCP::MXF::CreateObject(const Dictionary*& Dict, const UL& label)
1467 if ( ! s_TypesInit )
1469 Kumu::AutoMutex BlockLock(s_InitLock);
1471 if ( ! s_TypesInit )
1473 MXF::Metadata_InitTypes(Dict);
1478 FLi_t i = s_FactoryList.find(label.Value());
1480 if ( i == s_FactoryList.end() )
1481 return new InterchangeObject(Dict);
1483 return i->second(Dict);