2 Copyright (c) 2005-2013, 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 //------------------------------------------------------------------------------------------
48 ASDCP::MXF::SeekToRIP(const Kumu::FileReader& Reader)
52 // go to the end - 4 bytes
53 Result_t result = Reader.Seek(0, Kumu::SP_END);
55 if ( ASDCP_SUCCESS(result) )
56 result = Reader.Tell(&end_pos);
58 if ( ASDCP_SUCCESS(result)
59 && end_pos < (SMPTE_UL_LENGTH+MXF_BER_LENGTH) )
61 DefaultLogSink().Error("File is smaller than an empty KLV packet.\n");
65 if ( ASDCP_SUCCESS(result) )
66 result = Reader.Seek(end_pos - 4);
68 // get the ui32_t RIP length
70 byte_t intbuf[MXF_BER_LENGTH];
73 if ( ASDCP_SUCCESS(result) )
75 result = Reader.Read(intbuf, MXF_BER_LENGTH, &read_count);
77 if ( ASDCP_SUCCESS(result) && read_count != 4 )
79 DefaultLogSink().Error("RIP contains fewer than four bytes.\n");
84 if ( ASDCP_SUCCESS(result) )
86 rip_size = KM_i32_BE(Kumu::cp2i<ui32_t>(intbuf));
88 if ( rip_size > end_pos ) // RIP can't be bigger than the file
90 DefaultLogSink().Error("RIP size impossibly large.\n");
95 // reposition to start of RIP
96 if ( ASDCP_SUCCESS(result) )
97 result = Reader.Seek(end_pos - rip_size);
104 ASDCP::MXF::RIP::GetPairBySID(ui32_t SID, Pair& outPair) const
106 Array<Pair>::const_iterator pi = PairArray.begin();
107 for ( ; pi != PairArray.end(); pi++ )
109 if ( (*pi).BodySID == SID )
121 ASDCP::MXF::RIP::InitFromFile(const Kumu::FileReader& Reader)
124 Result_t result = KLVFilePacket::InitFromFile(Reader, m_Dict->ul(MDD_RandomIndexMetadata));
126 if ( ASDCP_SUCCESS(result) )
128 Kumu::MemIOReader MemRDR(m_ValueStart, m_ValueLength - 4);
129 result = PairArray.Unarchive(&MemRDR) ? RESULT_OK : RESULT_KLV_CODING;
132 if ( ASDCP_FAILURE(result) )
133 DefaultLogSink().Error("Failed to initialize RIP\n");
140 ASDCP::MXF::RIP::WriteToFile(Kumu::FileWriter& Writer)
143 ASDCP::FrameBuffer Buffer;
144 ui32_t RIPSize = ( PairArray.size() * (sizeof(ui32_t) + sizeof(ui64_t)) ) + 4;
145 Result_t result = Buffer.Capacity(RIPSize);
147 if ( ASDCP_SUCCESS(result) )
148 result = WriteKLToFile(Writer, m_Dict->ul(MDD_RandomIndexMetadata), RIPSize);
150 if ( ASDCP_SUCCESS(result) )
152 result = RESULT_KLV_CODING;
154 Kumu::MemIOWriter MemWRT(Buffer.Data(), Buffer.Capacity());
155 if ( PairArray.Archive(&MemWRT) )
156 if ( MemWRT.WriteUi32BE(RIPSize + 20) )
158 Buffer.Size(MemWRT.Length());
163 if ( ASDCP_SUCCESS(result) )
164 result = Writer.Write(Buffer.RoData(), Buffer.Size());
171 ASDCP::MXF::RIP::Dump(FILE* stream)
176 KLVFilePacket::Dump(stream, *m_Dict, false);
177 PairArray.Dump(stream, false);
180 //------------------------------------------------------------------------------------------
184 ASDCP::MXF::Partition::PacketList::~PacketList() {
185 while ( ! m_List.empty() )
187 delete m_List.back();
194 ASDCP::MXF::Partition::PacketList::AddPacket(InterchangeObject* ThePacket) // takes ownership
197 m_Map.insert(std::map<UUID, InterchangeObject*>::value_type(ThePacket->InstanceUID, ThePacket));
198 m_List.push_back(ThePacket);
203 ASDCP::MXF::Partition::PacketList::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
205 ASDCP_TEST_NULL(Object);
207 std::map<UUID, InterchangeObject*>::iterator mi = m_Map.find(ObjectID);
209 if ( mi == m_Map.end() )
215 *Object = (*mi).second;
221 ASDCP::MXF::Partition::PacketList::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
223 ASDCP_TEST_NULL(ObjectID);
224 ASDCP_TEST_NULL(Object);
225 std::list<InterchangeObject*>::iterator li;
228 for ( li = m_List.begin(); li != m_List.end(); li++ )
230 if ( (*li)->HasUL(ObjectID) )
242 ASDCP::MXF::Partition::PacketList::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
244 ASDCP_TEST_NULL(ObjectID);
245 std::list<InterchangeObject*>::iterator li;
247 for ( li = m_List.begin(); li != m_List.end(); li++ )
249 if ( (*li)->HasUL(ObjectID) )
250 ObjectList.push_back(*li);
253 return ObjectList.empty() ? RESULT_FAIL : RESULT_OK;
256 //------------------------------------------------------------------------------------------
260 ASDCP::MXF::Partition::Partition(const Dictionary*& d) :
262 MajorVersion(1), MinorVersion(2),
263 KAGSize(1), ThisPartition(0), PreviousPartition(0),
264 FooterPartition(0), HeaderByteCount(0), IndexByteCount(0), IndexSID(0),
265 BodyOffset(0), BodySID(0)
267 m_PacketList = new PacketList;
270 ASDCP::MXF::Partition::~Partition()
276 ASDCP::MXF::Partition::AddChildObject(InterchangeObject* Object)
280 if ( ! Object->InstanceUID.HasValue() )
281 GenRandomValue(Object->InstanceUID);
283 m_PacketList->AddPacket(Object);
288 ASDCP::MXF::Partition::InitFromFile(const Kumu::FileReader& Reader)
290 Result_t result = KLVFilePacket::InitFromFile(Reader);
292 // could be one of several values
293 if ( ASDCP_SUCCESS(result) )
294 result = ASDCP::MXF::Partition::InitFromBuffer(m_ValueStart, m_ValueLength);
301 ASDCP::MXF::Partition::InitFromBuffer(const byte_t* p, ui32_t l)
303 Kumu::MemIOReader MemRDR(p, l);
304 Result_t result = RESULT_KLV_CODING;
306 if ( MemRDR.ReadUi16BE(&MajorVersion) )
307 if ( MemRDR.ReadUi16BE(&MinorVersion) )
308 if ( MemRDR.ReadUi32BE(&KAGSize) )
309 if ( MemRDR.ReadUi64BE(&ThisPartition) )
310 if ( MemRDR.ReadUi64BE(&PreviousPartition) )
311 if ( MemRDR.ReadUi64BE(&FooterPartition) )
312 if ( MemRDR.ReadUi64BE(&HeaderByteCount) )
313 if ( MemRDR.ReadUi64BE(&IndexByteCount) )
314 if ( MemRDR.ReadUi32BE(&IndexSID) )
315 if ( MemRDR.ReadUi64BE(&BodyOffset) )
316 if ( MemRDR.ReadUi32BE(&BodySID) )
317 if ( OperationalPattern.Unarchive(&MemRDR) )
318 if ( EssenceContainers.Unarchive(&MemRDR) )
321 if ( ASDCP_FAILURE(result) )
322 DefaultLogSink().Error("Failed to initialize Partition\n");
329 ASDCP::MXF::Partition::WriteToFile(Kumu::FileWriter& Writer, UL& PartitionLabel)
331 ASDCP::FrameBuffer Buffer;
332 Result_t result = Buffer.Capacity(1024);
334 if ( ASDCP_SUCCESS(result) )
336 Kumu::MemIOWriter MemWRT(Buffer.Data(), Buffer.Capacity());
337 result = RESULT_KLV_CODING;
338 if ( MemWRT.WriteUi16BE(MajorVersion) )
339 if ( MemWRT.WriteUi16BE(MinorVersion) )
340 if ( MemWRT.WriteUi32BE(KAGSize) )
341 if ( MemWRT.WriteUi64BE(ThisPartition) )
342 if ( MemWRT.WriteUi64BE(PreviousPartition) )
343 if ( MemWRT.WriteUi64BE(FooterPartition) )
344 if ( MemWRT.WriteUi64BE(HeaderByteCount) )
345 if ( MemWRT.WriteUi64BE(IndexByteCount) )
346 if ( MemWRT.WriteUi32BE(IndexSID) )
347 if ( MemWRT.WriteUi64BE(BodyOffset) )
348 if ( MemWRT.WriteUi32BE(BodySID) )
349 if ( OperationalPattern.Archive(&MemWRT) )
350 if ( EssenceContainers.Archive(&MemWRT) )
352 Buffer.Size(MemWRT.Length());
357 if ( ASDCP_SUCCESS(result) )
360 result = WriteKLToFile(Writer, PartitionLabel.Value(), Buffer.Size());
362 if ( ASDCP_SUCCESS(result) )
363 result = Writer.Write(Buffer.RoData(), Buffer.Size(), &write_count);
371 ASDCP::MXF::Partition::ArchiveSize()
374 + sizeof(ui16_t) + sizeof(ui16_t)
376 + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t)
381 + sizeof(ui32_t) + sizeof(ui32_t) + ( UUIDlen * EssenceContainers.size() ) );
386 ASDCP::MXF::Partition::Dump(FILE* stream)
388 char identbuf[IdentBufferLen];
393 KLVFilePacket::Dump(stream, *m_Dict, false);
394 fprintf(stream, " MajorVersion = %hu\n", MajorVersion);
395 fprintf(stream, " MinorVersion = %hu\n", MinorVersion);
396 fprintf(stream, " KAGSize = %u\n", KAGSize);
397 fprintf(stream, " ThisPartition = %s\n", ui64sz(ThisPartition, identbuf));
398 fprintf(stream, " PreviousPartition = %s\n", ui64sz(PreviousPartition, identbuf));
399 fprintf(stream, " FooterPartition = %s\n", ui64sz(FooterPartition, identbuf));
400 fprintf(stream, " HeaderByteCount = %s\n", ui64sz(HeaderByteCount, identbuf));
401 fprintf(stream, " IndexByteCount = %s\n", ui64sz(IndexByteCount, identbuf));
402 fprintf(stream, " IndexSID = %u\n", IndexSID);
403 fprintf(stream, " BodyOffset = %s\n", ui64sz(BodyOffset, identbuf));
404 fprintf(stream, " BodySID = %u\n", BodySID);
405 fprintf(stream, " OperationalPattern = %s\n", OperationalPattern.EncodeString(identbuf, IdentBufferLen));
406 fputs("Essence Containers:\n", stream); EssenceContainers.Dump(stream);
410 //------------------------------------------------------------------------------------------
413 class ASDCP::MXF::Primer::h__PrimerLookup : public std::map<UL, TagValue>
416 void InitWithBatch(ASDCP::MXF::Batch<ASDCP::MXF::Primer::LocalTagEntry>& Batch)
418 ASDCP::MXF::Batch<ASDCP::MXF::Primer::LocalTagEntry>::iterator i = Batch.begin();
420 for ( ; i != Batch.end(); i++ )
421 insert(std::map<UL, TagValue>::value_type((*i).UL, (*i).Tag));
427 ASDCP::MXF::Primer::Primer(const Dictionary*& d) : m_LocalTag(0xff), m_Dict(d) {
428 m_UL = m_Dict->ul(MDD_Primer);
432 ASDCP::MXF::Primer::~Primer() {}
436 ASDCP::MXF::Primer::ClearTagList()
438 LocalTagEntryBatch.clear();
439 m_Lookup = new h__PrimerLookup;
444 ASDCP::MXF::Primer::InitFromBuffer(const byte_t* p, ui32_t l)
447 Result_t result = KLVPacket::InitFromBuffer(p, l, m_Dict->ul(MDD_Primer));
449 if ( ASDCP_SUCCESS(result) )
451 Kumu::MemIOReader MemRDR(m_ValueStart, m_ValueLength);
452 result = LocalTagEntryBatch.Unarchive(&MemRDR) ? RESULT_OK : RESULT_KLV_CODING;
455 if ( ASDCP_SUCCESS(result) )
457 m_Lookup = new h__PrimerLookup;
458 m_Lookup->InitWithBatch(LocalTagEntryBatch);
461 if ( ASDCP_FAILURE(result) )
462 DefaultLogSink().Error("Failed to initialize Primer\n");
469 ASDCP::MXF::Primer::WriteToFile(Kumu::FileWriter& Writer)
471 ASDCP::FrameBuffer Buffer;
472 Result_t result = Buffer.Capacity(128*1024);
474 if ( ASDCP_SUCCESS(result) )
475 result = WriteToBuffer(Buffer);
477 if ( ASDCP_SUCCESS(result) )
478 result = Writer.Write(Buffer.RoData(), Buffer.Size());
485 ASDCP::MXF::Primer::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
488 ASDCP::FrameBuffer LocalTagBuffer;
489 Kumu::MemIOWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length);
490 Result_t result = LocalTagEntryBatch.Archive(&MemWRT) ? RESULT_OK : RESULT_KLV_CODING;
492 if ( ASDCP_SUCCESS(result) )
494 ui32_t packet_length = MemWRT.Length();
495 result = WriteKLToBuffer(Buffer, packet_length);
497 if ( ASDCP_SUCCESS(result) )
498 Buffer.Size(Buffer.Size() + packet_length);
506 ASDCP::MXF::Primer::InsertTag(const MDDEntry& Entry, ASDCP::TagValue& Tag)
510 std::map<UL, TagValue>::iterator i = m_Lookup->find(TestUL);
512 if ( i == m_Lookup->end() )
514 if ( Entry.tag.a == 0 && Entry.tag.b == 0 )
517 Tag.b = m_LocalTag--;
525 LocalTagEntry TmpEntry;
526 TmpEntry.UL = TestUL;
529 LocalTagEntryBatch.push_back(TmpEntry);
530 m_Lookup->insert(std::map<UL, TagValue>::value_type(TmpEntry.UL, TmpEntry.Tag));
542 ASDCP::MXF::Primer::TagForKey(const ASDCP::UL& Key, ASDCP::TagValue& Tag)
545 if ( m_Lookup.empty() )
547 DefaultLogSink().Error("Primer lookup is empty\n");
551 std::map<UL, TagValue>::iterator i = m_Lookup->find(Key);
553 if ( i == m_Lookup->end() )
562 ASDCP::MXF::Primer::Dump(FILE* stream)
565 char identbuf[IdentBufferLen];
570 KLVPacket::Dump(stream, *m_Dict, false);
571 fprintf(stream, "Primer: %u %s\n",
572 (ui32_t)LocalTagEntryBatch.size(),
573 ( LocalTagEntryBatch.size() == 1 ? "entry" : "entries" ));
575 Batch<LocalTagEntry>::iterator i = LocalTagEntryBatch.begin();
576 for ( ; i != LocalTagEntryBatch.end(); i++ )
578 const MDDEntry* Entry = m_Dict->FindUL((*i).UL.Value());
579 fprintf(stream, " %s %s\n", (*i).EncodeString(identbuf, IdentBufferLen), (Entry ? Entry->name : "Unknown"));
584 //------------------------------------------------------------------------------------------
588 ASDCP::MXF::Preface::Preface(const Dictionary*& d) :
589 InterchangeObject(d), m_Dict(d), Version(258), ObjectModelVersion(0)
592 m_UL = m_Dict->Type(MDD_Preface).ul;
597 ASDCP::MXF::Preface::Copy(const Preface& rhs)
599 InterchangeObject::Copy(rhs);
601 LastModifiedDate = rhs.LastModifiedDate;
602 Version = rhs.Version;
603 ObjectModelVersion = rhs.ObjectModelVersion;
604 PrimaryPackage = rhs.PrimaryPackage;
605 Identifications = rhs.Identifications;
606 ContentStorage = rhs.ContentStorage;
607 OperationalPattern = rhs.OperationalPattern;
608 EssenceContainers = rhs.EssenceContainers;
609 DMSchemes = rhs.DMSchemes;
614 ASDCP::MXF::Preface::InitFromTLVSet(TLVReader& TLVSet)
616 Result_t result = InterchangeObject::InitFromTLVSet(TLVSet);
617 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, LastModifiedDate));
618 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi16(OBJ_READ_ARGS(Preface, Version));
619 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi32(OBJ_READ_ARGS(Preface, ObjectModelVersion));
620 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, PrimaryPackage));
621 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, Identifications));
622 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, ContentStorage));
623 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, OperationalPattern));
624 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, EssenceContainers));
625 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, DMSchemes));
631 ASDCP::MXF::Preface::WriteToTLVSet(TLVWriter& TLVSet)
633 Result_t result = InterchangeObject::WriteToTLVSet(TLVSet);
634 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, LastModifiedDate));
635 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi16(OBJ_WRITE_ARGS(Preface, Version));
636 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi32(OBJ_WRITE_ARGS(Preface, ObjectModelVersion));
637 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, PrimaryPackage));
638 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, Identifications));
639 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, ContentStorage));
640 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, OperationalPattern));
641 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, EssenceContainers));
642 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, DMSchemes));
648 ASDCP::MXF::Preface::InitFromBuffer(const byte_t* p, ui32_t l)
650 return InterchangeObject::InitFromBuffer(p, l);
655 ASDCP::MXF::Preface::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
657 return InterchangeObject::WriteToBuffer(Buffer);
662 ASDCP::MXF::Preface::Dump(FILE* stream)
664 char identbuf[IdentBufferLen];
669 InterchangeObject::Dump(stream);
670 fprintf(stream, " %22s = %s\n", "LastModifiedDate", LastModifiedDate.EncodeString(identbuf, IdentBufferLen));
671 fprintf(stream, " %22s = %hu\n", "Version", Version);
672 fprintf(stream, " %22s = %u\n", "ObjectModelVersion", ObjectModelVersion);
673 fprintf(stream, " %22s = %s\n", "PrimaryPackage", PrimaryPackage.EncodeHex(identbuf, IdentBufferLen));
674 fprintf(stream, " %22s:\n", "Identifications"); Identifications.Dump(stream);
675 fprintf(stream, " %22s = %s\n", "ContentStorage", ContentStorage.EncodeHex(identbuf, IdentBufferLen));
676 fprintf(stream, " %22s = %s\n", "OperationalPattern", OperationalPattern.EncodeString(identbuf, IdentBufferLen));
677 fprintf(stream, " %22s:\n", "EssenceContainers"); EssenceContainers.Dump(stream);
678 fprintf(stream, " %22s:\n", "DMSchemes"); DMSchemes.Dump(stream);
681 //------------------------------------------------------------------------------------------
684 ASDCP::MXF::OP1aHeader::OP1aHeader(const Dictionary*& d) : Partition(d), m_Dict(d), m_Primer(d), m_Preface(0) {}
685 ASDCP::MXF::OP1aHeader::~OP1aHeader() {}
689 ASDCP::MXF::OP1aHeader::InitFromFile(const Kumu::FileReader& Reader)
691 Result_t result = result = Partition::InitFromFile(Reader);
693 if ( ASDCP_FAILURE(result) )
696 if ( m_Dict == &DefaultCompositeDict() )
698 // select more explicit dictionary if one is available
699 if ( OperationalPattern.ExactMatch(MXFInterop_OPAtom_Entry().ul) )
701 m_Dict = &DefaultInteropDict();
703 else if ( OperationalPattern.ExactMatch(SMPTE_390_OPAtom_Entry().ul) )
705 m_Dict = &DefaultSMPTEDict();
709 // slurp up the remainder of the header
710 if ( HeaderByteCount < 1024 )
711 DefaultLogSink().Warn("Improbably small HeaderByteCount value: %u\n", HeaderByteCount);
713 assert (HeaderByteCount <= 0xFFFFFFFFL);
714 result = m_HeaderData.Capacity((ui32_t)HeaderByteCount);
716 if ( ASDCP_SUCCESS(result) )
719 result = Reader.Read(m_HeaderData.Data(), m_HeaderData.Capacity(), &read_count);
721 if ( ASDCP_FAILURE(result) )
723 DefaultLogSink().Error("OP1aHeader::InitFromFile, Read failed\n");
727 if ( read_count != m_HeaderData.Capacity() )
729 DefaultLogSink().Error("Short read of OP-Atom header metadata; wanted %u, got %u\n",
730 m_HeaderData.Capacity(), read_count);
731 return RESULT_KLV_CODING;
735 if ( ASDCP_SUCCESS(result) )
736 result = InitFromBuffer(m_HeaderData.RoData(), m_HeaderData.Capacity());
743 ASDCP::MXF::OP1aHeader::InitFromPartitionBuffer(const byte_t* p, ui32_t l)
745 Result_t result = KLVPacket::InitFromBuffer(p, l);
747 if ( ASDCP_SUCCESS(result) )
748 result = Partition::InitFromBuffer(m_ValueStart, m_ValueLength); // test UL and OP
750 if ( ASDCP_SUCCESS(result) )
752 ui32_t pp_len = KLVPacket::PacketLength();
753 result = InitFromBuffer(p + pp_len, l - pp_len);
761 ASDCP::MXF::OP1aHeader::InitFromBuffer(const byte_t* p, ui32_t l)
764 Result_t result = RESULT_OK;
765 const byte_t* end_p = p + l;
767 while ( ASDCP_SUCCESS(result) && p < end_p )
769 // parse the packets and index them by uid, discard KLVFill items
770 InterchangeObject* object = CreateObject(m_Dict, p);
773 object->m_Lookup = &m_Primer;
774 result = object->InitFromBuffer(p, end_p - p);
775 const byte_t* redo_p = p;
776 p += object->PacketLength();
777 // hexdump(p, object->PacketLength());
779 if ( ASDCP_SUCCESS(result) )
781 if ( object->IsA(m_Dict->ul(MDD_KLVFill)) )
785 else if ( object->IsA(m_Dict->ul(MDD_Primer)) ) // TODO: only one primer should be found
788 result = m_Primer.InitFromBuffer(redo_p, end_p - redo_p);
792 m_PacketList->AddPacket(object); // takes ownership
794 if ( object->IsA(m_Dict->ul(MDD_Preface)) && m_Preface == 0 )
795 m_Preface = (Preface*)object;
800 DefaultLogSink().Error("Error initializing packet\n");
809 ASDCP::MXF::OP1aHeader::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
811 return m_PacketList->GetMDObjectByID(ObjectID, Object);
816 ASDCP::MXF::OP1aHeader::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
818 InterchangeObject* TmpObject;
823 return m_PacketList->GetMDObjectByType(ObjectID, Object);
828 ASDCP::MXF::OP1aHeader::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
830 return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
834 ASDCP::MXF::Identification*
835 ASDCP::MXF::OP1aHeader::GetIdentification()
837 InterchangeObject* Object;
839 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object)) )
840 return (Identification*)Object;
846 ASDCP::MXF::SourcePackage*
847 ASDCP::MXF::OP1aHeader::GetSourcePackage()
849 InterchangeObject* Object;
851 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object)) )
852 return (SourcePackage*)Object;
859 ASDCP::MXF::OP1aHeader::WriteToFile(Kumu::FileWriter& Writer, ui32_t HeaderSize)
862 if ( m_Preface == 0 )
865 if ( HeaderSize < 4096 )
867 DefaultLogSink().Error("HeaderSize %u is too small. Must be >= 4096\n", HeaderSize);
871 ASDCP::FrameBuffer HeaderBuffer;
872 HeaderByteCount = HeaderSize - ArchiveSize();
873 assert (HeaderByteCount <= 0xFFFFFFFFL);
874 Result_t result = HeaderBuffer.Capacity((ui32_t) HeaderByteCount);
875 m_Preface->m_Lookup = &m_Primer;
877 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
878 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
880 InterchangeObject* object = *pl_i;
881 object->m_Lookup = &m_Primer;
883 ASDCP::FrameBuffer WriteWrapper;
884 WriteWrapper.SetData(HeaderBuffer.Data() + HeaderBuffer.Size(),
885 HeaderBuffer.Capacity() - HeaderBuffer.Size());
886 result = object->WriteToBuffer(WriteWrapper);
887 HeaderBuffer.Size(HeaderBuffer.Size() + WriteWrapper.Size());
890 if ( ASDCP_SUCCESS(result) )
892 UL TmpUL(m_Dict->ul(MDD_ClosedCompleteHeader));
893 result = Partition::WriteToFile(Writer, TmpUL);
896 if ( ASDCP_SUCCESS(result) )
897 result = m_Primer.WriteToFile(Writer);
899 if ( ASDCP_SUCCESS(result) )
902 Writer.Write(HeaderBuffer.RoData(), HeaderBuffer.Size(), &write_count);
903 assert(write_count == HeaderBuffer.Size());
907 if ( ASDCP_SUCCESS(result) )
909 Kumu::fpos_t pos = Writer.Tell();
911 if ( pos > (Kumu::fpos_t)HeaderByteCount )
913 char intbuf[IntBufferLen];
914 DefaultLogSink().Error("Header size %s exceeds specified value %u\n",
920 ASDCP::FrameBuffer NilBuf;
921 ui32_t klv_fill_length = HeaderSize - (ui32_t)pos;
923 if ( klv_fill_length < kl_length )
925 DefaultLogSink().Error("Remaining region too small for KLV Fill header\n");
929 klv_fill_length -= kl_length;
930 result = WriteKLToFile(Writer, m_Dict->ul(MDD_KLVFill), klv_fill_length);
932 if ( ASDCP_SUCCESS(result) )
933 result = NilBuf.Capacity(klv_fill_length);
935 if ( ASDCP_SUCCESS(result) )
937 memset(NilBuf.Data(), 0, klv_fill_length);
939 Writer.Write(NilBuf.RoData(), klv_fill_length, &write_count);
940 assert(write_count == klv_fill_length);
949 ASDCP::MXF::OP1aHeader::Dump(FILE* stream)
954 Partition::Dump(stream);
955 m_Primer.Dump(stream);
957 if ( m_Preface == 0 )
958 fputs("No Preface loaded\n", stream);
960 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
961 for ( ; i != m_PacketList->m_List.end(); i++ )
965 //------------------------------------------------------------------------------------------
968 ASDCP::MXF::OPAtomIndexFooter::OPAtomIndexFooter(const Dictionary*& d) :
969 Partition(d), m_Dict(d),
970 m_CurrentSegment(0), m_BytesPerEditUnit(0), m_BodySID(0),
971 m_ECOffset(0), m_Lookup(0)
977 ASDCP::MXF::OPAtomIndexFooter::~OPAtomIndexFooter() {}
981 ASDCP::MXF::OPAtomIndexFooter::InitFromFile(const Kumu::FileReader& Reader)
983 Result_t result = Partition::InitFromFile(Reader); // test UL and OP
985 // slurp up the remainder of the footer
986 ui32_t read_count = 0;
988 if ( ASDCP_SUCCESS(result) && IndexByteCount > 0 )
990 assert (IndexByteCount <= 0xFFFFFFFFL);
991 // At this point, m_FooterData may not have been initialized
992 // so it's capacity is zero and data pointer is NULL
993 // However, if IndexByteCount is zero then the capacity
994 // doesn't change and the data pointer is not set.
995 result = m_FooterData.Capacity((ui32_t) IndexByteCount);
997 if ( ASDCP_SUCCESS(result) )
998 result = Reader.Read(m_FooterData.Data(), m_FooterData.Capacity(), &read_count);
1000 if ( ASDCP_SUCCESS(result) && read_count != m_FooterData.Capacity() )
1002 DefaultLogSink().Error("Short read of footer partition: got %u, expecting %u\n",
1003 read_count, m_FooterData.Capacity());
1006 else if( ASDCP_SUCCESS(result) && !m_FooterData.Data() )
1008 DefaultLogSink().Error( "Buffer for footer partition not created: IndexByteCount = %u\n",
1013 if ( ASDCP_SUCCESS(result) )
1014 result = InitFromBuffer(m_FooterData.RoData(), m_FooterData.Capacity());
1022 ASDCP::MXF::OPAtomIndexFooter::InitFromPartitionBuffer(const byte_t* p, ui32_t l)
1024 Result_t result = KLVPacket::InitFromBuffer(p, l);
1026 if ( ASDCP_SUCCESS(result) )
1027 result = Partition::InitFromBuffer(m_ValueStart, m_ValueLength); // test UL and OP
1029 if ( ASDCP_SUCCESS(result) )
1031 ui32_t pp_len = KLVPacket::PacketLength();
1032 result = InitFromBuffer(p + pp_len, l - pp_len);
1040 ASDCP::MXF::OPAtomIndexFooter::InitFromBuffer(const byte_t* p, ui32_t l)
1042 Result_t result = RESULT_OK;
1043 const byte_t* end_p = p + l;
1045 while ( ASDCP_SUCCESS(result) && p < end_p )
1047 // parse the packets and index them by uid, discard KLVFill items
1048 InterchangeObject* object = CreateObject(m_Dict, p);
1051 object->m_Lookup = m_Lookup;
1052 result = object->InitFromBuffer(p, end_p - p);
1053 p += object->PacketLength();
1055 if ( ASDCP_SUCCESS(result) )
1057 m_PacketList->AddPacket(object); // takes ownership
1061 DefaultLogSink().Error("Error initializing packet\n");
1066 if ( ASDCP_FAILURE(result) )
1067 DefaultLogSink().Error("Failed to initialize OPAtomIndexFooter\n");
1074 ASDCP::MXF::OPAtomIndexFooter::WriteToFile(Kumu::FileWriter& Writer, ui64_t duration)
1077 ASDCP::FrameBuffer FooterBuffer;
1078 ui32_t footer_size = m_PacketList->m_List.size() * MaxIndexSegmentSize; // segment-count * max-segment-size
1079 Result_t result = FooterBuffer.Capacity(footer_size);
1080 ui32_t iseg_count = 0;
1082 if ( m_CurrentSegment != 0 )
1084 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1085 m_CurrentSegment = 0;
1088 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
1089 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
1091 if ( (*pl_i)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1094 IndexTableSegment* Segment = (IndexTableSegment*)(*pl_i);
1096 if ( m_BytesPerEditUnit != 0 )
1098 if ( iseg_count != 1 )
1099 return RESULT_STATE;
1101 Segment->IndexDuration = duration;
1105 InterchangeObject* object = *pl_i;
1106 object->m_Lookup = m_Lookup;
1108 ASDCP::FrameBuffer WriteWrapper;
1109 WriteWrapper.SetData(FooterBuffer.Data() + FooterBuffer.Size(),
1110 FooterBuffer.Capacity() - FooterBuffer.Size());
1111 result = object->WriteToBuffer(WriteWrapper);
1112 FooterBuffer.Size(FooterBuffer.Size() + WriteWrapper.Size());
1115 if ( ASDCP_SUCCESS(result) )
1117 IndexByteCount = FooterBuffer.Size();
1118 UL FooterUL(m_Dict->ul(MDD_CompleteFooter));
1119 result = Partition::WriteToFile(Writer, FooterUL);
1122 if ( ASDCP_SUCCESS(result) )
1124 ui32_t write_count = 0;
1125 result = Writer.Write(FooterBuffer.RoData(), FooterBuffer.Size(), &write_count);
1126 assert(write_count == FooterBuffer.Size());
1134 ASDCP::MXF::OPAtomIndexFooter::Dump(FILE* stream)
1139 Partition::Dump(stream);
1141 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
1142 for ( ; i != m_PacketList->m_List.end(); i++ )
1147 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
1149 return m_PacketList->GetMDObjectByID(ObjectID, Object);
1154 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
1156 InterchangeObject* TmpObject;
1159 Object = &TmpObject;
1161 return m_PacketList->GetMDObjectByType(ObjectID, Object);
1166 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
1168 return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
1173 ASDCP::MXF::OPAtomIndexFooter::Lookup(ui32_t frame_num, IndexTableSegment::IndexEntry& Entry) const
1175 std::list<InterchangeObject*>::iterator li;
1176 for ( li = m_PacketList->m_List.begin(); li != m_PacketList->m_List.end(); li++ )
1178 if ( (*li)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1180 IndexTableSegment* Segment = (IndexTableSegment*)(*li);
1181 ui64_t start_pos = Segment->IndexStartPosition;
1183 if ( Segment->EditUnitByteCount > 0 )
1185 if ( m_PacketList->m_List.size() > 1 )
1186 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
1188 if ( ! Segment->IndexEntryArray.empty() )
1189 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
1191 Entry.StreamOffset = (ui64_t)frame_num * Segment->EditUnitByteCount;
1194 else if ( (ui64_t)frame_num >= start_pos
1195 && (ui64_t)frame_num < (start_pos + Segment->IndexDuration) )
1197 ui64_t tmp = frame_num - start_pos;
1198 assert(tmp <= 0xFFFFFFFFL);
1199 Entry = Segment->IndexEntryArray[(ui32_t) tmp];
1210 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const Rational& Rate)
1214 m_BytesPerEditUnit = size;
1217 IndexTableSegment* Index = new IndexTableSegment(m_Dict);
1218 AddChildObject(Index);
1219 Index->EditUnitByteCount = m_BytesPerEditUnit;
1220 Index->IndexEditRate = Rate;
1225 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsVBR(IPrimerLookup* lookup, const Rational& Rate, Kumu::fpos_t offset)
1229 m_BytesPerEditUnit = 0;
1231 m_ECOffset = offset;
1236 ASDCP::MXF::OPAtomIndexFooter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
1238 if ( m_BytesPerEditUnit != 0 ) // are we CBR? that's bad
1240 DefaultLogSink().Error("Call to PushIndexEntry() failed: index is CBR\n");
1244 // do we have an available segment?
1245 if ( m_CurrentSegment == 0 )
1246 { // no, set up a new segment
1247 m_CurrentSegment = new IndexTableSegment(m_Dict);
1248 assert(m_CurrentSegment);
1249 AddChildObject(m_CurrentSegment);
1250 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1251 m_CurrentSegment->IndexEditRate = m_EditRate;
1252 m_CurrentSegment->IndexStartPosition = 0;
1254 else if ( m_CurrentSegment->IndexEntryArray.size() >= CBRIndexEntriesPerSegment )
1255 { // no, this one is full, start another
1256 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1257 ui64_t StartPosition = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
1259 m_CurrentSegment = new IndexTableSegment(m_Dict);
1260 assert(m_CurrentSegment);
1261 AddChildObject(m_CurrentSegment);
1262 m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1263 m_CurrentSegment->IndexEditRate = m_EditRate;
1264 m_CurrentSegment->IndexStartPosition = StartPosition;
1267 m_CurrentSegment->IndexEntryArray.push_back(Entry);
1270 //------------------------------------------------------------------------------------------
1275 ASDCP::MXF::InterchangeObject::Copy(const InterchangeObject& rhs)
1278 InstanceUID = rhs.InstanceUID;
1279 GenerationUID = rhs.GenerationUID;
1284 ASDCP::MXF::InterchangeObject::InitFromTLVSet(TLVReader& TLVSet)
1286 Result_t result = TLVSet.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID));
1287 if ( ASDCP_SUCCESS(result) )
1288 result = TLVSet.ReadObject(OBJ_READ_ARGS(GenerationInterchangeObject, GenerationUID));
1294 ASDCP::MXF::InterchangeObject::WriteToTLVSet(TLVWriter& TLVSet)
1296 Result_t result = TLVSet.WriteObject(OBJ_WRITE_ARGS(InterchangeObject, InstanceUID));
1297 if ( ASDCP_SUCCESS(result) )
1298 result = TLVSet.WriteObject(OBJ_WRITE_ARGS(GenerationInterchangeObject, GenerationUID));
1304 ASDCP::MXF::InterchangeObject::InitFromBuffer(const byte_t* p, ui32_t l)
1307 Result_t result = RESULT_FALSE;
1309 if ( m_UL.HasValue() )
1311 result = KLVPacket::InitFromBuffer(p, l, m_UL);
1313 if ( ASDCP_SUCCESS(result) )
1315 TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup);
1316 result = InitFromTLVSet(MemRDR);
1321 result = KLVPacket::InitFromBuffer(p, l);
1329 ASDCP::MXF::InterchangeObject::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
1331 if ( ! m_UL.HasValue() )
1332 return RESULT_STATE;
1334 TLVWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length, m_Lookup);
1335 Result_t result = WriteToTLVSet(MemWRT);
1337 if ( ASDCP_SUCCESS(result) )
1339 ui32_t packet_length = MemWRT.Length();
1340 result = WriteKLToBuffer(Buffer, packet_length);
1342 if ( ASDCP_SUCCESS(result) )
1343 Buffer.Size(Buffer.Size() + packet_length);
1351 ASDCP::MXF::InterchangeObject::Dump(FILE* stream)
1353 char identbuf[IdentBufferLen];
1355 fputc('\n', stream);
1356 KLVPacket::Dump(stream, *m_Dict, false);
1357 fprintf(stream, " InstanceUID = %s\n", InstanceUID.EncodeHex(identbuf, IdentBufferLen));
1358 fprintf(stream, " GenerationUID = %s\n", GenerationUID.EncodeHex(identbuf, IdentBufferLen));
1363 ASDCP::MXF::InterchangeObject::IsA(const byte_t* label)
1365 if ( m_KLLength == 0 )
1368 return ( memcmp(label, m_KeyStart, SMPTE_UL_LENGTH) == 0 );
1372 //------------------------------------------------------------------------------------------
1375 typedef std::map<ASDCP::UL, ASDCP::MXF::MXFObjectFactory_t>FactoryMap_t;
1376 typedef FactoryMap_t::iterator FLi_t;
1379 class FactoryList : public FactoryMap_t
1388 Kumu::AutoMutex BlockLock(m_Lock);
1392 FLi_t Find(const byte_t* label) {
1393 Kumu::AutoMutex BlockLock(m_Lock);
1398 Kumu::AutoMutex BlockLock(m_Lock);
1402 void Insert(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory) {
1403 Kumu::AutoMutex BlockLock(m_Lock);
1404 insert(FactoryList::value_type(label, factory));
1409 static FactoryList s_FactoryList;
1410 static Kumu::Mutex s_InitLock;
1411 static bool s_TypesInit = false;
1416 ASDCP::MXF::SetObjectFactory(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory)
1418 s_FactoryList.Insert(label, factory);
1422 ASDCP::MXF::InterchangeObject*
1423 ASDCP::MXF::CreateObject(const Dictionary*& Dict, const UL& label)
1425 if ( ! s_TypesInit )
1427 Kumu::AutoMutex BlockLock(s_InitLock);
1429 if ( ! s_TypesInit )
1431 MXF::Metadata_InitTypes(Dict);
1436 FLi_t i = s_FactoryList.find(label.Value());
1438 if ( i == s_FactoryList.end() )
1439 return new InterchangeObject(Dict);
1441 return i->second(Dict);