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)
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)
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) {}
426 ASDCP::MXF::Primer::~Primer() {}
430 ASDCP::MXF::Primer::ClearTagList()
432 LocalTagEntryBatch.clear();
433 m_Lookup = new h__PrimerLookup;
438 ASDCP::MXF::Primer::InitFromBuffer(const byte_t* p, ui32_t l)
441 Result_t result = KLVPacket::InitFromBuffer(p, l, m_Dict->ul(MDD_Primer));
443 if ( ASDCP_SUCCESS(result) )
445 Kumu::MemIOReader MemRDR(m_ValueStart, m_ValueLength);
446 result = LocalTagEntryBatch.Unarchive(&MemRDR) ? RESULT_OK : RESULT_KLV_CODING;
449 if ( ASDCP_SUCCESS(result) )
451 m_Lookup = new h__PrimerLookup;
452 m_Lookup->InitWithBatch(LocalTagEntryBatch);
455 if ( ASDCP_FAILURE(result) )
456 DefaultLogSink().Error("Failed to initialize Primer\n");
463 ASDCP::MXF::Primer::WriteToFile(Kumu::FileWriter& Writer)
465 ASDCP::FrameBuffer Buffer;
466 Result_t result = Buffer.Capacity(128*1024);
468 if ( ASDCP_SUCCESS(result) )
469 result = WriteToBuffer(Buffer);
471 if ( ASDCP_SUCCESS(result) )
472 result = Writer.Write(Buffer.RoData(), Buffer.Size());
479 ASDCP::MXF::Primer::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
482 ASDCP::FrameBuffer LocalTagBuffer;
483 Kumu::MemIOWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length);
484 Result_t result = LocalTagEntryBatch.Archive(&MemWRT) ? RESULT_OK : RESULT_KLV_CODING;
486 if ( ASDCP_SUCCESS(result) )
488 ui32_t packet_length = MemWRT.Length();
489 result = WriteKLToBuffer(Buffer, m_Dict->ul(MDD_Primer), packet_length);
491 if ( ASDCP_SUCCESS(result) )
492 Buffer.Size(Buffer.Size() + packet_length);
500 ASDCP::MXF::Primer::InsertTag(const MDDEntry& Entry, ASDCP::TagValue& Tag)
504 std::map<UL, TagValue>::iterator i = m_Lookup->find(TestUL);
506 if ( i == m_Lookup->end() )
508 if ( Entry.tag.a == 0 && Entry.tag.b == 0 )
511 Tag.b = m_LocalTag--;
519 LocalTagEntry TmpEntry;
520 TmpEntry.UL = TestUL;
523 LocalTagEntryBatch.push_back(TmpEntry);
524 m_Lookup->insert(std::map<UL, TagValue>::value_type(TmpEntry.UL, TmpEntry.Tag));
536 ASDCP::MXF::Primer::TagForKey(const ASDCP::UL& Key, ASDCP::TagValue& Tag)
539 if ( m_Lookup.empty() )
541 DefaultLogSink().Error("Primer lookup is empty\n");
545 std::map<UL, TagValue>::iterator i = m_Lookup->find(Key);
547 if ( i == m_Lookup->end() )
556 ASDCP::MXF::Primer::Dump(FILE* stream)
559 char identbuf[IdentBufferLen];
564 KLVPacket::Dump(stream, *m_Dict, false);
565 fprintf(stream, "Primer: %u %s\n",
566 (ui32_t)LocalTagEntryBatch.size(),
567 ( LocalTagEntryBatch.size() == 1 ? "entry" : "entries" ));
569 Batch<LocalTagEntry>::iterator i = LocalTagEntryBatch.begin();
570 for ( ; i != LocalTagEntryBatch.end(); i++ )
572 const MDDEntry* Entry = m_Dict->FindUL((*i).UL.Value());
573 fprintf(stream, " %s %s\n", (*i).EncodeString(identbuf, IdentBufferLen), (Entry ? Entry->name : "Unknown"));
578 //------------------------------------------------------------------------------------------
583 ASDCP::MXF::Preface::InitFromTLVSet(TLVReader& TLVSet)
585 Result_t result = InterchangeObject::InitFromTLVSet(TLVSet);
586 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, LastModifiedDate));
587 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi16(OBJ_READ_ARGS(Preface, Version));
588 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi32(OBJ_READ_ARGS(Preface, ObjectModelVersion));
589 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, PrimaryPackage));
590 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, Identifications));
591 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, ContentStorage));
592 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, OperationalPattern));
593 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, EssenceContainers));
594 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, DMSchemes));
600 ASDCP::MXF::Preface::WriteToTLVSet(TLVWriter& TLVSet)
602 Result_t result = InterchangeObject::WriteToTLVSet(TLVSet);
603 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, LastModifiedDate));
604 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi16(OBJ_WRITE_ARGS(Preface, Version));
605 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi32(OBJ_WRITE_ARGS(Preface, ObjectModelVersion));
606 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, PrimaryPackage));
607 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, Identifications));
608 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, ContentStorage));
609 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, OperationalPattern));
610 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, EssenceContainers));
611 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, DMSchemes));
617 ASDCP::MXF::Preface::InitFromBuffer(const byte_t* p, ui32_t l)
620 m_Typeinfo = &(m_Dict->Type(MDD_Preface));
621 return InterchangeObject::InitFromBuffer(p, l);
626 ASDCP::MXF::Preface::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
629 m_Typeinfo = &(m_Dict->Type(MDD_Preface));
630 return InterchangeObject::WriteToBuffer(Buffer);
635 ASDCP::MXF::Preface::Dump(FILE* stream)
637 char identbuf[IdentBufferLen];
642 InterchangeObject::Dump(stream);
643 fprintf(stream, " %22s = %s\n", "LastModifiedDate", LastModifiedDate.EncodeString(identbuf, IdentBufferLen));
644 fprintf(stream, " %22s = %hu\n", "Version", Version);
645 fprintf(stream, " %22s = %u\n", "ObjectModelVersion", ObjectModelVersion);
646 fprintf(stream, " %22s = %s\n", "PrimaryPackage", PrimaryPackage.EncodeHex(identbuf, IdentBufferLen));
647 fprintf(stream, " %22s:\n", "Identifications"); Identifications.Dump(stream);
648 fprintf(stream, " %22s = %s\n", "ContentStorage", ContentStorage.EncodeHex(identbuf, IdentBufferLen));
649 fprintf(stream, " %22s = %s\n", "OperationalPattern", OperationalPattern.EncodeString(identbuf, IdentBufferLen));
650 fprintf(stream, " %22s:\n", "EssenceContainers"); EssenceContainers.Dump(stream);
651 fprintf(stream, " %22s:\n", "DMSchemes"); DMSchemes.Dump(stream);
654 //------------------------------------------------------------------------------------------
657 ASDCP::MXF::OPAtomHeader::OPAtomHeader(const Dictionary*& d) : Partition(d), m_Dict(d), m_RIP(d), m_Primer(d), m_Preface(0), m_HasRIP(false) {}
658 ASDCP::MXF::OPAtomHeader::~OPAtomHeader() {}
662 ASDCP::MXF::OPAtomHeader::InitFromFile(const Kumu::FileReader& Reader)
665 Result_t result = SeekToRIP(Reader);
667 if ( ASDCP_SUCCESS(result) )
669 result = m_RIP.InitFromFile(Reader);
670 ui32_t test_s = m_RIP.PairArray.size();
672 if ( ASDCP_FAILURE(result) )
674 DefaultLogSink().Error("File contains no RIP\n");
677 else if ( test_s == 0 )
679 DefaultLogSink().Error("RIP contains no Pairs.\n");
680 result = RESULT_FORMAT;
686 // OP-Atom states that there will be either two or three partitions:
687 // one closed header and one closed footer with an optional body
688 // SMPTE 429-5 files may have many partitions, see SMPTE 410M
689 DefaultLogSink().Warn("RIP count is less than 2: %u\n", test_s);
694 if ( m_RIP.PairArray.front().ByteOffset != 0 )
696 DefaultLogSink().Error("First Partition in RIP is not at offset 0.\n");
697 result = RESULT_FORMAT;
702 if ( ASDCP_SUCCESS(result) )
703 result = Reader.Seek(0);
705 if ( ASDCP_SUCCESS(result) )
706 result = Partition::InitFromFile(Reader); // test UL and OP
708 if ( ASDCP_FAILURE(result) )
711 // is it really OP-Atom?
713 UL OPAtomUL(SMPTE_390_OPAtom_Entry().ul);
714 UL InteropOPAtomUL(MXFInterop_OPAtom_Entry().ul);
716 if ( OperationalPattern == OPAtomUL ) // SMPTE
718 if ( m_Dict == &DefaultCompositeDict() )
719 m_Dict = &DefaultSMPTEDict();
721 else if ( OperationalPattern == InteropOPAtomUL ) // Interop
723 if ( m_Dict == &DefaultCompositeDict() )
724 m_Dict = &DefaultInteropDict();
728 char strbuf[IdentBufferLen];
729 const MDDEntry* Entry = m_Dict->FindUL(OperationalPattern.Value());
731 DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n",
732 OperationalPattern.EncodeString(strbuf, IdentBufferLen));
734 DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n", Entry->name);
737 // slurp up the remainder of the header
738 if ( HeaderByteCount < 1024 )
739 DefaultLogSink().Warn("Improbably small HeaderByteCount value: %u\n", HeaderByteCount);
741 assert (HeaderByteCount <= 0xFFFFFFFFL);
742 result = m_Buffer.Capacity((ui32_t) HeaderByteCount);
744 if ( ASDCP_SUCCESS(result) )
747 result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
749 if ( ASDCP_FAILURE(result) )
752 if ( read_count != m_Buffer.Capacity() )
754 DefaultLogSink().Error("Short read of OP-Atom header metadata; wanted %u, got %u\n",
755 m_Buffer.Capacity(), read_count);
756 return RESULT_KLV_CODING;
760 if ( ASDCP_SUCCESS(result) )
761 result = InitFromBuffer(m_Buffer.RoData(), m_Buffer.Capacity());
768 ASDCP::MXF::OPAtomHeader::InitFromPartitionBuffer(const byte_t* p, ui32_t l)
770 Result_t result = KLVPacket::InitFromBuffer(p, l);
772 if ( ASDCP_SUCCESS(result) )
773 result = Partition::InitFromBuffer(m_ValueStart, m_ValueLength); // test UL and OP
775 if ( ASDCP_SUCCESS(result) )
777 ui32_t pp_len = KLVPacket::PacketLength();
778 result = InitFromBuffer(p + pp_len, l - pp_len);
786 ASDCP::MXF::OPAtomHeader::InitFromBuffer(const byte_t* p, ui32_t l)
789 Result_t result = RESULT_OK;
790 const byte_t* end_p = p + l;
792 while ( ASDCP_SUCCESS(result) && p < end_p )
794 // parse the packets and index them by uid, discard KLVFill items
795 InterchangeObject* object = CreateObject(m_Dict, p);
798 object->m_Lookup = &m_Primer;
799 result = object->InitFromBuffer(p, end_p - p);
800 const byte_t* redo_p = p;
801 p += object->PacketLength();
802 // hexdump(p, object->PacketLength());
804 if ( ASDCP_SUCCESS(result) )
806 if ( object->IsA(m_Dict->ul(MDD_KLVFill)) )
810 else if ( object->IsA(m_Dict->ul(MDD_Primer)) ) // TODO: only one primer should be found
813 result = m_Primer.InitFromBuffer(redo_p, end_p - redo_p);
817 m_PacketList->AddPacket(object);
819 if ( object->IsA(m_Dict->ul(MDD_Preface)) && m_Preface == 0 )
820 m_Preface = (Preface*)object;
825 DefaultLogSink().Error("Error initializing packet\n");
834 ASDCP::MXF::OPAtomHeader::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
836 return m_PacketList->GetMDObjectByID(ObjectID, Object);
841 ASDCP::MXF::OPAtomHeader::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
843 InterchangeObject* TmpObject;
848 return m_PacketList->GetMDObjectByType(ObjectID, Object);
853 ASDCP::MXF::OPAtomHeader::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
855 return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
859 ASDCP::MXF::Identification*
860 ASDCP::MXF::OPAtomHeader::GetIdentification()
862 InterchangeObject* Object;
864 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object)) )
865 return (Identification*)Object;
871 ASDCP::MXF::SourcePackage*
872 ASDCP::MXF::OPAtomHeader::GetSourcePackage()
874 InterchangeObject* Object;
876 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object)) )
877 return (SourcePackage*)Object;
885 ASDCP::MXF::OPAtomHeader::WriteToFile(Kumu::FileWriter& Writer, ui32_t HeaderSize)
888 if ( m_Preface == 0 )
891 if ( HeaderSize < 4096 )
893 DefaultLogSink().Error("HeaderSize %u is too small. Must be >= 4096\n", HeaderSize);
897 ASDCP::FrameBuffer HeaderBuffer;
898 HeaderByteCount = HeaderSize - ArchiveSize();
899 assert (HeaderByteCount <= 0xFFFFFFFFL);
900 Result_t result = HeaderBuffer.Capacity((ui32_t) HeaderByteCount);
901 m_Preface->m_Lookup = &m_Primer;
903 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
904 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
906 InterchangeObject* object = *pl_i;
907 object->m_Lookup = &m_Primer;
909 ASDCP::FrameBuffer WriteWrapper;
910 WriteWrapper.SetData(HeaderBuffer.Data() + HeaderBuffer.Size(),
911 HeaderBuffer.Capacity() - HeaderBuffer.Size());
912 result = object->WriteToBuffer(WriteWrapper);
913 HeaderBuffer.Size(HeaderBuffer.Size() + WriteWrapper.Size());
916 if ( ASDCP_SUCCESS(result) )
918 UL TmpUL(m_Dict->ul(MDD_ClosedCompleteHeader));
919 result = Partition::WriteToFile(Writer, TmpUL);
922 if ( ASDCP_SUCCESS(result) )
923 result = m_Primer.WriteToFile(Writer);
925 if ( ASDCP_SUCCESS(result) )
928 Writer.Write(HeaderBuffer.RoData(), HeaderBuffer.Size(), &write_count);
929 assert(write_count == HeaderBuffer.Size());
933 if ( ASDCP_SUCCESS(result) )
935 Kumu::fpos_t pos = Writer.Tell();
937 if ( pos > (Kumu::fpos_t)HeaderByteCount )
939 char intbuf[IntBufferLen];
940 DefaultLogSink().Error("Header size %s exceeds specified value %u\n",
946 ASDCP::FrameBuffer NilBuf;
947 ui32_t klv_fill_length = HeaderSize - (ui32_t)pos;
949 if ( klv_fill_length < kl_length )
951 DefaultLogSink().Error("Remaining region too small for KLV Fill header\n");
955 klv_fill_length -= kl_length;
956 result = WriteKLToFile(Writer, m_Dict->ul(MDD_KLVFill), klv_fill_length);
958 if ( ASDCP_SUCCESS(result) )
959 result = NilBuf.Capacity(klv_fill_length);
961 if ( ASDCP_SUCCESS(result) )
963 memset(NilBuf.Data(), 0, klv_fill_length);
965 Writer.Write(NilBuf.RoData(), klv_fill_length, &write_count);
966 assert(write_count == klv_fill_length);
975 ASDCP::MXF::OPAtomHeader::Dump(FILE* stream)
980 Partition::Dump(stream);
981 m_Primer.Dump(stream);
983 if ( m_Preface == 0 )
984 fputs("No Preface loaded\n", stream);
986 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
987 for ( ; i != m_PacketList->m_List.end(); i++ )
991 //------------------------------------------------------------------------------------------
994 ASDCP::MXF::OPAtomIndexFooter::OPAtomIndexFooter(const Dictionary*& d) :
995 Partition(d), m_Dict(d),
996 m_CurrentSegment(0), m_BytesPerEditUnit(0), m_BodySID(0),
997 m_ECOffset(0), m_Lookup(0)
1003 ASDCP::MXF::OPAtomIndexFooter::~OPAtomIndexFooter() {}
1007 ASDCP::MXF::OPAtomIndexFooter::InitFromFile(const Kumu::FileReader& Reader)
1009 Result_t result = Partition::InitFromFile(Reader); // test UL and OP
1011 // slurp up the remainder of the footer
1014 if ( ASDCP_SUCCESS(result) )
1016 assert (IndexByteCount <= 0xFFFFFFFFL);
1017 result = m_Buffer.Capacity((ui32_t) IndexByteCount);
1020 if ( ASDCP_SUCCESS(result) )
1021 result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
1023 if ( ASDCP_SUCCESS(result) && read_count != m_Buffer.Capacity() )
1025 DefaultLogSink().Error("Short read of footer partition: got %u, expecting %u\n",
1026 read_count, m_Buffer.Capacity());
1030 if ( ASDCP_SUCCESS(result) )
1031 result = InitFromBuffer(m_Buffer.RoData(), m_Buffer.Capacity());
1038 ASDCP::MXF::OPAtomIndexFooter::InitFromPartitionBuffer(const byte_t* p, ui32_t l)
1040 Result_t result = KLVPacket::InitFromBuffer(p, l);
1042 if ( ASDCP_SUCCESS(result) )
1043 result = Partition::InitFromBuffer(m_ValueStart, m_ValueLength); // test UL and OP
1045 if ( ASDCP_SUCCESS(result) )
1047 ui32_t pp_len = KLVPacket::PacketLength();
1048 result = InitFromBuffer(p + pp_len, l - pp_len);
1056 ASDCP::MXF::OPAtomIndexFooter::InitFromBuffer(const byte_t* p, ui32_t l)
1058 Result_t result = RESULT_OK;
1059 const byte_t* end_p = p + l;
1061 while ( ASDCP_SUCCESS(result) && p < end_p )
1063 // parse the packets and index them by uid, discard KLVFill items
1064 InterchangeObject* object = CreateObject(m_Dict, p);
1067 object->m_Lookup = m_Lookup;
1068 result = object->InitFromBuffer(p, end_p - p);
1069 p += object->PacketLength();
1071 if ( ASDCP_SUCCESS(result) )
1073 m_PacketList->AddPacket(object);
1077 DefaultLogSink().Error("Error initializing packet\n");
1082 if ( ASDCP_FAILURE(result) )
1083 DefaultLogSink().Error("Failed to initialize OPAtomIndexFooter\n");
1090 ASDCP::MXF::OPAtomIndexFooter::WriteToFile(Kumu::FileWriter& Writer, ui64_t duration)
1093 ASDCP::FrameBuffer FooterBuffer;
1094 ui32_t footer_size = m_PacketList->m_List.size() * MaxIndexSegmentSize; // segment-count * max-segment-size
1095 Result_t result = FooterBuffer.Capacity(footer_size);
1096 ui32_t iseg_count = 0;
1098 if ( m_CurrentSegment != 0 )
1100 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1101 m_CurrentSegment = 0;
1104 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
1105 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
1107 if ( (*pl_i)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1110 IndexTableSegment* Segment = (IndexTableSegment*)(*pl_i);
1112 if ( m_BytesPerEditUnit != 0 )
1114 if ( iseg_count != 1 )
1115 return RESULT_STATE;
1117 Segment->IndexDuration = duration;
1121 InterchangeObject* object = *pl_i;
1122 object->m_Lookup = m_Lookup;
1124 ASDCP::FrameBuffer WriteWrapper;
1125 WriteWrapper.SetData(FooterBuffer.Data() + FooterBuffer.Size(),
1126 FooterBuffer.Capacity() - FooterBuffer.Size());
1127 result = object->WriteToBuffer(WriteWrapper);
1128 FooterBuffer.Size(FooterBuffer.Size() + WriteWrapper.Size());
1131 if ( ASDCP_SUCCESS(result) )
1133 IndexByteCount = FooterBuffer.Size();
1134 UL FooterUL(m_Dict->ul(MDD_CompleteFooter));
1135 result = Partition::WriteToFile(Writer, FooterUL);
1138 if ( ASDCP_SUCCESS(result) )
1140 ui32_t write_count = 0;
1141 result = Writer.Write(FooterBuffer.RoData(), FooterBuffer.Size(), &write_count);
1142 assert(write_count == FooterBuffer.Size());
1150 ASDCP::MXF::OPAtomIndexFooter::Dump(FILE* stream)
1155 Partition::Dump(stream);
1157 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
1158 for ( ; i != m_PacketList->m_List.end(); i++ )
1164 ASDCP::MXF::OPAtomIndexFooter::Lookup(ui32_t frame_num, IndexTableSegment::IndexEntry& Entry) const
1166 std::list<InterchangeObject*>::iterator li;
1167 for ( li = m_PacketList->m_List.begin(); li != m_PacketList->m_List.end(); li++ )
1169 if ( (*li)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1171 IndexTableSegment* Segment = (IndexTableSegment*)(*li);
1172 ui64_t start_pos = Segment->IndexStartPosition;
1174 if ( Segment->EditUnitByteCount > 0 )
1176 if ( m_PacketList->m_List.size() > 1 )
1177 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
1179 if ( ! Segment->IndexEntryArray.empty() )
1180 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
1182 Entry.StreamOffset = (ui64_t)frame_num * Segment->EditUnitByteCount;
1185 else if ( (ui64_t)frame_num >= start_pos
1186 && (ui64_t)frame_num < (start_pos + Segment->IndexDuration) )
1188 ui64_t tmp = frame_num - start_pos;
1189 assert(tmp <= 0xFFFFFFFFL);
1190 Entry = Segment->IndexEntryArray[(ui32_t) tmp];
1201 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const Rational& Rate)
1205 m_BytesPerEditUnit = size;
1208 IndexTableSegment* Index = new IndexTableSegment(m_Dict);
1209 AddChildObject(Index);
1210 Index->EditUnitByteCount = m_BytesPerEditUnit;
1211 Index->IndexEditRate = Rate;
1216 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsVBR(IPrimerLookup* lookup, const Rational& Rate, Kumu::fpos_t offset)
1220 m_BytesPerEditUnit = 0;
1222 m_ECOffset = offset;
1227 ASDCP::MXF::OPAtomIndexFooter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
1229 if ( m_BytesPerEditUnit != 0 ) // are we CBR? that's bad
1231 DefaultLogSink().Error("Call to PushIndexEntry() failed: index is CBR\n");
1235 // do we have an available segment?
1236 if ( m_CurrentSegment == 0 )
1237 { // no, set up a new segment
1238 m_CurrentSegment = new IndexTableSegment(m_Dict);
1239 assert(m_CurrentSegment);
1240 AddChildObject(m_CurrentSegment);
1241 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1242 m_CurrentSegment->IndexEditRate = m_EditRate;
1243 m_CurrentSegment->IndexStartPosition = 0;
1245 else if ( m_CurrentSegment->IndexEntryArray.size() >= CBRIndexEntriesPerSegment )
1246 { // no, this one is full, start another
1247 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1248 ui64_t StartPosition = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
1250 m_CurrentSegment = new IndexTableSegment(m_Dict);
1251 assert(m_CurrentSegment);
1252 AddChildObject(m_CurrentSegment);
1253 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1254 m_CurrentSegment->IndexEditRate = m_EditRate;
1255 m_CurrentSegment->IndexStartPosition = StartPosition;
1258 m_CurrentSegment->IndexEntryArray.push_back(Entry);
1261 //------------------------------------------------------------------------------------------
1266 ASDCP::MXF::InterchangeObject::InitFromTLVSet(TLVReader& TLVSet)
1268 Result_t result = TLVSet.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID));
1269 if ( ASDCP_SUCCESS(result) )
1270 result = TLVSet.ReadObject(OBJ_READ_ARGS(GenerationInterchangeObject, GenerationUID));
1276 ASDCP::MXF::InterchangeObject::WriteToTLVSet(TLVWriter& TLVSet)
1278 Result_t result = TLVSet.WriteObject(OBJ_WRITE_ARGS(InterchangeObject, InstanceUID));
1279 if ( ASDCP_SUCCESS(result) )
1280 result = TLVSet.WriteObject(OBJ_WRITE_ARGS(GenerationInterchangeObject, GenerationUID));
1286 ASDCP::MXF::InterchangeObject::InitFromBuffer(const byte_t* p, ui32_t l)
1289 Result_t result = RESULT_FALSE;
1291 if ( m_Typeinfo == 0 )
1293 result = KLVPacket::InitFromBuffer(p, l);
1297 result = KLVPacket::InitFromBuffer(p, l, m_Typeinfo->ul);
1299 if ( ASDCP_SUCCESS(result) )
1301 TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup);
1302 result = InitFromTLVSet(MemRDR);
1311 ASDCP::MXF::InterchangeObject::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
1313 if ( m_Typeinfo == 0 )
1314 return RESULT_STATE;
1316 TLVWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length, m_Lookup);
1317 Result_t result = WriteToTLVSet(MemWRT);
1319 if ( ASDCP_SUCCESS(result) )
1321 ui32_t packet_length = MemWRT.Length();
1322 result = WriteKLToBuffer(Buffer, m_Typeinfo->ul, packet_length);
1324 if ( ASDCP_SUCCESS(result) )
1325 Buffer.Size(Buffer.Size() + packet_length);
1333 ASDCP::MXF::InterchangeObject::Dump(FILE* stream)
1335 char identbuf[IdentBufferLen];
1337 fputc('\n', stream);
1338 KLVPacket::Dump(stream, *m_Dict, false);
1339 fprintf(stream, " InstanceUID = %s\n", InstanceUID.EncodeHex(identbuf, IdentBufferLen));
1340 fprintf(stream, " GenerationUID = %s\n", GenerationUID.EncodeHex(identbuf, IdentBufferLen));
1345 ASDCP::MXF::InterchangeObject::IsA(const byte_t* label)
1347 if ( m_KLLength == 0 )
1350 return ( memcmp(label, m_KeyStart, SMPTE_UL_LENGTH) == 0 );
1354 //------------------------------------------------------------------------------------------
1357 typedef std::map<ASDCP::UL, ASDCP::MXF::MXFObjectFactory_t>FactoryMap_t;
1358 typedef FactoryMap_t::iterator FLi_t;
1361 class FactoryList : public FactoryMap_t
1370 Kumu::AutoMutex BlockLock(m_Lock);
1374 FLi_t Find(const byte_t* label) {
1375 Kumu::AutoMutex BlockLock(m_Lock);
1380 Kumu::AutoMutex BlockLock(m_Lock);
1384 void Insert(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory) {
1385 Kumu::AutoMutex BlockLock(m_Lock);
1386 insert(FactoryList::value_type(label, factory));
1391 static FactoryList s_FactoryList;
1392 static Kumu::Mutex s_InitLock;
1393 static bool s_TypesInit = false;
1398 ASDCP::MXF::SetObjectFactory(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory)
1400 s_FactoryList.Insert(label, factory);
1404 ASDCP::MXF::InterchangeObject*
1405 ASDCP::MXF::CreateObject(const Dictionary*& Dict, const UL& label)
1407 if ( ! s_TypesInit )
1409 Kumu::AutoMutex BlockLock(s_InitLock);
1411 if ( ! s_TypesInit )
1413 MXF::Metadata_InitTypes(Dict);
1418 FLi_t i = s_FactoryList.find(label.Value());
1420 if ( i == s_FactoryList.end() )
1421 return new InterchangeObject(Dict);
1423 return i->second(Dict);