2 Copyright (c) 2005-2015, 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, PartitionPair& outPair) const
106 RIP::const_pair_iterator i;
107 for ( i = PairArray.begin(); i != PairArray.end(); ++i )
109 if ( i->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(__LINE__, __FILE__);
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(__LINE__, __FILE__);
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(__LINE__, __FILE__);
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(__LINE__, __FILE__);
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(__LINE__, __FILE__);
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(__LINE__, __FILE__);
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.insert(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->FindULAnyVersion((*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)
592 m_UL = m_Dict->Type(MDD_Preface).ul;
593 ObjectModelVersion = 0;
598 ASDCP::MXF::Preface::Copy(const Preface& rhs)
600 InterchangeObject::Copy(rhs);
602 LastModifiedDate = rhs.LastModifiedDate;
603 Version = rhs.Version;
604 ObjectModelVersion = rhs.ObjectModelVersion;
605 PrimaryPackage = rhs.PrimaryPackage;
606 Identifications = rhs.Identifications;
607 ContentStorage = rhs.ContentStorage;
608 OperationalPattern = rhs.OperationalPattern;
609 EssenceContainers = rhs.EssenceContainers;
610 DMSchemes = rhs.DMSchemes;
611 ApplicationSchemes = rhs.ApplicationSchemes;
612 ConformsToSpecifications = rhs.ConformsToSpecifications;
617 ASDCP::MXF::Preface::InitFromTLVSet(TLVReader& TLVSet)
619 Result_t result = InterchangeObject::InitFromTLVSet(TLVSet);
620 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, LastModifiedDate));
621 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi16(OBJ_READ_ARGS(Preface, Version));
622 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi32(OBJ_READ_ARGS_OPT(Preface, ObjectModelVersion));
623 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS_OPT(Preface, PrimaryPackage));
624 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, Identifications));
625 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, ContentStorage));
626 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, OperationalPattern));
627 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, EssenceContainers));
628 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, DMSchemes));
629 if ( ASDCP_SUCCESS(result) )
631 result = TLVSet.ReadObject(OBJ_READ_ARGS_OPT(Preface, ApplicationSchemes));
632 ApplicationSchemes.set_has_value( result == RESULT_OK);
634 if ( ASDCP_SUCCESS(result) )
636 result = TLVSet.ReadObject(OBJ_READ_ARGS_OPT(Preface, ConformsToSpecifications));
637 ConformsToSpecifications.set_has_value( result == RESULT_OK);
644 ASDCP::MXF::Preface::WriteToTLVSet(TLVWriter& TLVSet)
646 Result_t result = InterchangeObject::WriteToTLVSet(TLVSet);
647 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, LastModifiedDate));
648 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi16(OBJ_WRITE_ARGS(Preface, Version));
649 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi32(OBJ_WRITE_ARGS_OPT(Preface, ObjectModelVersion));
650 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS_OPT(Preface, PrimaryPackage));
651 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, Identifications));
652 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, ContentStorage));
653 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, OperationalPattern));
654 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, EssenceContainers));
655 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, DMSchemes));
656 if ( ASDCP_SUCCESS(result) && !ApplicationSchemes.empty() )
658 result = TLVSet.WriteObject(OBJ_WRITE_ARGS_OPT(Preface, ApplicationSchemes));
660 if ( ASDCP_SUCCESS(result) && !ConformsToSpecifications.empty())
662 result = TLVSet.WriteObject(OBJ_WRITE_ARGS_OPT(Preface, ConformsToSpecifications));
669 ASDCP::MXF::Preface::InitFromBuffer(const byte_t* p, ui32_t l)
671 return InterchangeObject::InitFromBuffer(p, l);
676 ASDCP::MXF::Preface::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
678 return InterchangeObject::WriteToBuffer(Buffer);
683 ASDCP::MXF::Preface::Dump(FILE* stream)
685 char identbuf[IdentBufferLen];
690 InterchangeObject::Dump(stream);
691 fprintf(stream, " %22s = %s\n", "LastModifiedDate", LastModifiedDate.EncodeString(identbuf, IdentBufferLen));
692 fprintf(stream, " %22s = %hu\n", "Version", Version);
694 if ( ! ObjectModelVersion.empty() )
695 fprintf(stream, " %22s = %u\n", "ObjectModelVersion", ObjectModelVersion.get());
697 if ( ! PrimaryPackage.empty() )
698 fprintf(stream, " %22s = %s\n", "PrimaryPackage", PrimaryPackage.get().EncodeHex(identbuf, IdentBufferLen));
700 fprintf(stream, " %22s:\n", "Identifications"); Identifications.Dump(stream);
701 fprintf(stream, " %22s = %s\n", "ContentStorage", ContentStorage.EncodeHex(identbuf, IdentBufferLen));
702 fprintf(stream, " %22s = %s\n", "OperationalPattern", OperationalPattern.EncodeString(identbuf, IdentBufferLen));
703 fprintf(stream, " %22s:\n", "EssenceContainers"); EssenceContainers.Dump(stream);
704 fprintf(stream, " %22s:\n", "DMSchemes"); DMSchemes.Dump(stream);
705 if ( ! ApplicationSchemes.empty() )
707 fprintf(stream, " %22s:\n", "ApplicationSchemes"); ApplicationSchemes.get().Dump(stream);
709 if ( ! ConformsToSpecifications.empty() )
711 fprintf(stream, " %22s:\n", "ConformsToSpecifications"); ConformsToSpecifications.get().Dump(stream);
715 //------------------------------------------------------------------------------------------
718 ASDCP::MXF::OP1aHeader::OP1aHeader(const Dictionary*& d) : Partition(d), m_Dict(d), m_Primer(d), m_Preface(0) {}
719 ASDCP::MXF::OP1aHeader::~OP1aHeader() {}
723 ASDCP::MXF::OP1aHeader::InitFromFile(const Kumu::FileReader& Reader)
725 Result_t result = Partition::InitFromFile(Reader);
727 if ( ASDCP_FAILURE(result) )
730 if ( m_Dict == &DefaultCompositeDict() )
732 // select more explicit dictionary if one is available
733 if ( OperationalPattern.MatchExact(MXFInterop_OPAtom_Entry().ul) )
735 m_Dict = &DefaultInteropDict();
737 else if ( OperationalPattern.MatchExact(SMPTE_390_OPAtom_Entry().ul) )
739 m_Dict = &DefaultSMPTEDict();
743 // slurp up the remainder of the header
744 if ( HeaderByteCount == 0 )
746 DefaultLogSink().Warn("MXF file contents incomplete.\n");
747 return RESULT_KLV_CODING(__LINE__, __FILE__);
749 else if ( HeaderByteCount < 1024 )
751 DefaultLogSink().Warn("Improbably small HeaderByteCount value: %qu\n", HeaderByteCount);
753 else if (HeaderByteCount > ( 4 * Kumu::Megabyte ) )
755 DefaultLogSink().Warn("Improbably huge HeaderByteCount value: %qu\n", HeaderByteCount);
758 result = m_HeaderData.Capacity(Kumu::xmin(4*Kumu::Megabyte, static_cast<ui32_t>(HeaderByteCount)));
760 if ( ASDCP_SUCCESS(result) )
763 result = Reader.Read(m_HeaderData.Data(), m_HeaderData.Capacity(), &read_count);
765 if ( ASDCP_FAILURE(result) )
767 DefaultLogSink().Error("OP1aHeader::InitFromFile, read failed.\n");
771 if ( read_count != m_HeaderData.Capacity() )
773 DefaultLogSink().Error("Short read of OP-Atom header metadata; wanted %u, got %u.\n",
774 m_HeaderData.Capacity(), read_count);
775 return RESULT_KLV_CODING(__LINE__, __FILE__);
779 if ( ASDCP_SUCCESS(result) )
780 result = InitFromBuffer(m_HeaderData.RoData(), m_HeaderData.Capacity());
787 ASDCP::MXF::OP1aHeader::InitFromPartitionBuffer(const byte_t* p, ui32_t l)
789 Result_t result = KLVPacket::InitFromBuffer(p, l);
791 if ( ASDCP_SUCCESS(result) )
792 result = Partition::InitFromBuffer(m_ValueStart, m_ValueLength); // test UL and OP
794 if ( ASDCP_SUCCESS(result) )
796 ui32_t pp_len = KLVPacket::PacketLength();
797 result = InitFromBuffer(p + pp_len, l - pp_len);
805 ASDCP::MXF::OP1aHeader::InitFromBuffer(const byte_t* p, ui32_t l)
808 Result_t result = RESULT_OK;
809 const byte_t* end_p = p + l;
811 while ( ASDCP_SUCCESS(result) && p < end_p )
813 // parse the packets and index them by uid, discard KLVFill items
814 InterchangeObject* object = CreateObject(m_Dict, p);
817 object->m_Lookup = &m_Primer;
818 result = object->InitFromBuffer(p, end_p - p);
820 const byte_t* redo_p = p;
821 p += object->PacketLength();
823 if ( ASDCP_SUCCESS(result) )
825 if ( object->IsA(m_Dict->ul(MDD_KLVFill)) )
831 DefaultLogSink().Error("Fill item short read: %d.\n", p - end_p);
834 else if ( object->IsA(m_Dict->ul(MDD_Primer)) ) // TODO: only one primer should be found
837 result = m_Primer.InitFromBuffer(redo_p, end_p - redo_p);
841 m_PacketList->AddPacket(object); // takes ownership
843 if ( object->IsA(m_Dict->ul(MDD_Preface)) && m_Preface == 0 )
844 m_Preface = (Preface*)object;
849 DefaultLogSink().Error("Error initializing OP1a header packet.\n");
850 // Kumu::hexdump(p-object->PacketLength(), object->PacketLength());
859 ASDCP::MXF::OP1aHeader::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
861 return m_PacketList->GetMDObjectByID(ObjectID, Object);
866 ASDCP::MXF::OP1aHeader::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
868 InterchangeObject* TmpObject;
873 return m_PacketList->GetMDObjectByType(ObjectID, Object);
878 ASDCP::MXF::OP1aHeader::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
880 return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
884 ASDCP::MXF::Identification*
885 ASDCP::MXF::OP1aHeader::GetIdentification()
887 InterchangeObject* Object;
889 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object)) )
890 return (Identification*)Object;
896 ASDCP::MXF::SourcePackage*
897 ASDCP::MXF::OP1aHeader::GetSourcePackage()
899 InterchangeObject* Object;
901 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object)) )
902 return (SourcePackage*)Object;
909 ASDCP::MXF::OP1aHeader::WriteToFile(Kumu::FileWriter& Writer, ui32_t HeaderSize)
912 if ( m_Preface == 0 )
915 if ( HeaderSize < 4096 )
917 DefaultLogSink().Error("HeaderSize %u is too small. Must be >= 4096\n", HeaderSize);
921 ASDCP::FrameBuffer HeaderBuffer;
922 HeaderByteCount = HeaderSize - ArchiveSize();
923 assert (HeaderByteCount <= 0xFFFFFFFFL);
924 Result_t result = HeaderBuffer.Capacity((ui32_t) HeaderByteCount);
925 m_Preface->m_Lookup = &m_Primer;
927 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
928 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
930 InterchangeObject* object = *pl_i;
931 object->m_Lookup = &m_Primer;
933 ASDCP::FrameBuffer WriteWrapper;
934 WriteWrapper.SetData(HeaderBuffer.Data() + HeaderBuffer.Size(),
935 HeaderBuffer.Capacity() - HeaderBuffer.Size());
936 result = object->WriteToBuffer(WriteWrapper);
937 HeaderBuffer.Size(HeaderBuffer.Size() + WriteWrapper.Size());
940 if ( ASDCP_SUCCESS(result) )
942 UL TmpUL(m_Dict->ul(MDD_ClosedCompleteHeader));
943 result = Partition::WriteToFile(Writer, TmpUL);
946 if ( ASDCP_SUCCESS(result) )
947 result = m_Primer.WriteToFile(Writer);
949 if ( ASDCP_SUCCESS(result) )
952 Writer.Write(HeaderBuffer.RoData(), HeaderBuffer.Size(), &write_count);
953 assert(write_count == HeaderBuffer.Size());
957 if ( ASDCP_SUCCESS(result) )
959 Kumu::fpos_t pos = Writer.Tell();
961 if ( pos > (Kumu::fpos_t)HeaderByteCount )
963 char intbuf[IntBufferLen];
964 DefaultLogSink().Error("Header size %s exceeds specified value %u\n",
970 ASDCP::FrameBuffer NilBuf;
971 ui32_t klv_fill_length = HeaderSize - (ui32_t)pos;
973 if ( klv_fill_length < kl_length )
975 DefaultLogSink().Error("Remaining region too small for KLV Fill header\n");
979 klv_fill_length -= kl_length;
980 result = WriteKLToFile(Writer, m_Dict->ul(MDD_KLVFill), klv_fill_length);
982 if ( ASDCP_SUCCESS(result) )
983 result = NilBuf.Capacity(klv_fill_length);
985 if ( ASDCP_SUCCESS(result) )
987 memset(NilBuf.Data(), 0, klv_fill_length);
989 Writer.Write(NilBuf.RoData(), klv_fill_length, &write_count);
990 assert(write_count == klv_fill_length);
999 ASDCP::MXF::OP1aHeader::Dump(FILE* stream)
1004 Partition::Dump(stream);
1005 m_Primer.Dump(stream);
1007 if ( m_Preface == 0 )
1008 fputs("No Preface loaded\n", stream);
1010 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
1011 for ( ; i != m_PacketList->m_List.end(); i++ )
1015 //------------------------------------------------------------------------------------------
1018 ASDCP::MXF::OPAtomIndexFooter::OPAtomIndexFooter(const Dictionary*& d) :
1019 Partition(d), m_Dict(d),
1020 m_CurrentSegment(0), m_BytesPerEditUnit(0), m_BodySID(0),
1021 m_ECOffset(0), m_Lookup(0)
1027 ASDCP::MXF::OPAtomIndexFooter::~OPAtomIndexFooter() {}
1031 ASDCP::MXF::OPAtomIndexFooter::InitFromFile(const Kumu::FileReader& Reader)
1033 Result_t result = Partition::InitFromFile(Reader); // test UL and OP
1035 // slurp up the remainder of the footer
1036 ui32_t read_count = 0;
1038 if ( ASDCP_SUCCESS(result) && IndexByteCount > 0 )
1040 assert (IndexByteCount <= 0xFFFFFFFFL);
1041 // At this point, m_FooterData may not have been initialized
1042 // so it's capacity is zero and data pointer is NULL
1043 // However, if IndexByteCount is zero then the capacity
1044 // doesn't change and the data pointer is not set.
1045 result = m_FooterData.Capacity((ui32_t) IndexByteCount);
1047 if ( ASDCP_SUCCESS(result) )
1048 result = Reader.Read(m_FooterData.Data(), m_FooterData.Capacity(), &read_count);
1050 if ( ASDCP_SUCCESS(result) && read_count != m_FooterData.Capacity() )
1052 DefaultLogSink().Error("Short read of footer partition: got %u, expecting %u\n",
1053 read_count, m_FooterData.Capacity());
1056 else if( ASDCP_SUCCESS(result) && !m_FooterData.Data() )
1058 DefaultLogSink().Error( "Buffer for footer partition not created: IndexByteCount = %u\n",
1063 if ( ASDCP_SUCCESS(result) )
1064 result = InitFromBuffer(m_FooterData.RoData(), m_FooterData.Capacity());
1072 ASDCP::MXF::OPAtomIndexFooter::InitFromPartitionBuffer(const byte_t* p, ui32_t l)
1074 Result_t result = KLVPacket::InitFromBuffer(p, l);
1076 if ( ASDCP_SUCCESS(result) )
1077 result = Partition::InitFromBuffer(m_ValueStart, m_ValueLength); // test UL and OP
1079 if ( ASDCP_SUCCESS(result) )
1081 ui32_t pp_len = KLVPacket::PacketLength();
1082 result = InitFromBuffer(p + pp_len, l - pp_len);
1090 ASDCP::MXF::OPAtomIndexFooter::InitFromBuffer(const byte_t* p, ui32_t l)
1092 Result_t result = RESULT_OK;
1093 const byte_t* end_p = p + l;
1095 while ( ASDCP_SUCCESS(result) && p < end_p )
1097 // parse the packets and index them by uid, discard KLVFill items
1098 InterchangeObject* object = CreateObject(m_Dict, p);
1101 object->m_Lookup = m_Lookup;
1102 result = object->InitFromBuffer(p, end_p - p);
1103 p += object->PacketLength();
1105 if ( ASDCP_SUCCESS(result) )
1107 m_PacketList->AddPacket(object); // takes ownership
1111 DefaultLogSink().Error("Error initializing OPAtom footer packet.\n");
1116 if ( ASDCP_FAILURE(result) )
1118 DefaultLogSink().Error("Failed to initialize OPAtomIndexFooter.\n");
1126 ASDCP::MXF::OPAtomIndexFooter::WriteToFile(Kumu::FileWriter& Writer, ui64_t duration)
1129 ASDCP::FrameBuffer FooterBuffer;
1130 ui32_t footer_size = m_PacketList->m_List.size() * MaxIndexSegmentSize; // segment-count * max-segment-size
1131 Result_t result = FooterBuffer.Capacity(footer_size);
1132 ui32_t iseg_count = 0;
1134 if ( m_CurrentSegment != 0 )
1136 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1137 m_CurrentSegment = 0;
1140 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
1141 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
1143 IndexTableSegment *segment = dynamic_cast<IndexTableSegment*>(*pl_i);
1148 if ( m_BytesPerEditUnit != 0 )
1150 if ( iseg_count != 1 )
1151 return RESULT_STATE;
1153 segment->IndexDuration = duration;
1157 InterchangeObject* object = *pl_i;
1158 object->m_Lookup = m_Lookup;
1160 ASDCP::FrameBuffer WriteWrapper;
1161 WriteWrapper.SetData(FooterBuffer.Data() + FooterBuffer.Size(),
1162 FooterBuffer.Capacity() - FooterBuffer.Size());
1163 result = object->WriteToBuffer(WriteWrapper);
1164 FooterBuffer.Size(FooterBuffer.Size() + WriteWrapper.Size());
1167 if ( ASDCP_SUCCESS(result) )
1169 IndexByteCount = FooterBuffer.Size();
1170 UL FooterUL(m_Dict->ul(MDD_CompleteFooter));
1171 result = Partition::WriteToFile(Writer, FooterUL);
1174 if ( ASDCP_SUCCESS(result) )
1176 ui32_t write_count = 0;
1177 result = Writer.Write(FooterBuffer.RoData(), FooterBuffer.Size(), &write_count);
1178 assert(write_count == FooterBuffer.Size());
1186 ASDCP::MXF::OPAtomIndexFooter::Dump(FILE* stream)
1191 Partition::Dump(stream);
1193 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
1194 for ( ; i != m_PacketList->m_List.end(); i++ )
1199 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
1201 return m_PacketList->GetMDObjectByID(ObjectID, Object);
1206 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
1208 InterchangeObject* TmpObject;
1211 Object = &TmpObject;
1213 return m_PacketList->GetMDObjectByType(ObjectID, Object);
1218 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
1220 return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
1225 ASDCP::MXF::OPAtomIndexFooter::Lookup(ui32_t frame_num, IndexTableSegment::IndexEntry& Entry) const
1227 std::list<InterchangeObject*>::iterator li;
1228 for ( li = m_PacketList->m_List.begin(); li != m_PacketList->m_List.end(); li++ )
1230 IndexTableSegment *segment = dynamic_cast<IndexTableSegment*>(*li);
1234 ui64_t start_pos = segment->IndexStartPosition;
1236 if ( segment->EditUnitByteCount > 0 )
1238 if ( m_PacketList->m_List.size() > 1 )
1239 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
1241 if ( ! segment->IndexEntryArray.empty() )
1242 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
1244 Entry.StreamOffset = (ui64_t)frame_num * segment->EditUnitByteCount;
1247 else if ( (ui64_t)frame_num >= start_pos
1248 && (ui64_t)frame_num < (start_pos + segment->IndexDuration) )
1250 ui64_t tmp = frame_num - start_pos;
1251 assert(tmp <= 0xFFFFFFFFL);
1253 if ( tmp < segment->IndexEntryArray.size() )
1255 Entry = segment->IndexEntryArray[(ui32_t) tmp];
1260 DefaultLogSink().Error("Malformed index table segment, IndexDuration does not match entries.\n");
1271 ASDCP::MXF::OPAtomIndexFooter::SetDeltaParams(const IndexTableSegment::DeltaEntry& delta)
1273 m_DefaultDeltaEntry = delta;
1278 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const Rational& Rate)
1282 m_BytesPerEditUnit = size;
1285 IndexTableSegment* Index = new IndexTableSegment(m_Dict);
1286 AddChildObject(Index);
1287 Index->EditUnitByteCount = m_BytesPerEditUnit;
1288 Index->IndexEditRate = Rate;
1293 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsVBR(IPrimerLookup* lookup, const Rational& Rate, Kumu::fpos_t offset)
1297 m_BytesPerEditUnit = 0;
1299 m_ECOffset = offset;
1304 ASDCP::MXF::OPAtomIndexFooter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
1306 if ( m_BytesPerEditUnit != 0 ) // are we CBR? that's bad
1308 DefaultLogSink().Error("Call to PushIndexEntry() failed: index is CBR\n");
1312 // do we have an available segment?
1313 if ( m_CurrentSegment == 0 )
1314 { // no, set up a new segment
1315 m_CurrentSegment = new IndexTableSegment(m_Dict);
1316 assert(m_CurrentSegment);
1317 AddChildObject(m_CurrentSegment);
1318 m_CurrentSegment->DeltaEntryArray.push_back(m_DefaultDeltaEntry);
1319 m_CurrentSegment->IndexEditRate = m_EditRate;
1320 m_CurrentSegment->IndexStartPosition = 0;
1322 else if ( m_CurrentSegment->IndexEntryArray.size() >= CBRIndexEntriesPerSegment )
1323 { // no, this one is full, start another
1324 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1325 ui64_t StartPosition = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
1327 m_CurrentSegment = new IndexTableSegment(m_Dict);
1328 assert(m_CurrentSegment);
1329 AddChildObject(m_CurrentSegment);
1330 m_CurrentSegment->DeltaEntryArray.push_back(m_DefaultDeltaEntry);
1331 m_CurrentSegment->IndexEditRate = m_EditRate;
1332 m_CurrentSegment->IndexStartPosition = StartPosition;
1335 m_CurrentSegment->IndexEntryArray.push_back(Entry);
1338 //------------------------------------------------------------------------------------------
1343 ASDCP::MXF::InterchangeObject::Copy(const InterchangeObject& rhs)
1346 InstanceUID = rhs.InstanceUID;
1347 GenerationUID = rhs.GenerationUID;
1352 ASDCP::MXF::InterchangeObject::InitFromTLVSet(TLVReader& TLVSet)
1354 Result_t result = TLVSet.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID));
1355 if ( ASDCP_SUCCESS(result) )
1356 result = TLVSet.ReadObject(OBJ_READ_ARGS_OPT(GenerationInterchangeObject, GenerationUID));
1362 ASDCP::MXF::InterchangeObject::WriteToTLVSet(TLVWriter& TLVSet)
1364 Result_t result = TLVSet.WriteObject(OBJ_WRITE_ARGS(InterchangeObject, InstanceUID));
1365 if ( ASDCP_SUCCESS(result) )
1366 result = TLVSet.WriteObject(OBJ_WRITE_ARGS_OPT(GenerationInterchangeObject, GenerationUID));
1372 ASDCP::MXF::InterchangeObject::InitFromBuffer(const byte_t* p, ui32_t l)
1375 Result_t result = RESULT_FALSE;
1377 if ( m_UL.HasValue() )
1379 result = KLVPacket::InitFromBuffer(p, l, m_UL);
1381 if ( ASDCP_SUCCESS(result) )
1383 TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup);
1384 result = InitFromTLVSet(MemRDR);
1389 result = KLVPacket::InitFromBuffer(p, l);
1397 ASDCP::MXF::InterchangeObject::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
1399 if ( ! m_UL.HasValue() )
1400 return RESULT_STATE;
1402 TLVWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length, m_Lookup);
1403 Result_t result = WriteToTLVSet(MemWRT);
1405 if ( ASDCP_SUCCESS(result) )
1407 ui32_t packet_length = MemWRT.Length();
1408 result = WriteKLToBuffer(Buffer, packet_length);
1410 if ( ASDCP_SUCCESS(result) )
1411 Buffer.Size(Buffer.Size() + packet_length);
1419 ASDCP::MXF::InterchangeObject::Dump(FILE* stream)
1421 char identbuf[IdentBufferLen];
1423 fputc('\n', stream);
1424 KLVPacket::Dump(stream, *m_Dict, false);
1425 fprintf(stream, " InstanceUID = %s\n", InstanceUID.EncodeHex(identbuf, IdentBufferLen));
1427 if ( ! GenerationUID.empty() )
1428 fprintf(stream, " GenerationUID = %s\n", GenerationUID.get().EncodeHex(identbuf, IdentBufferLen));
1433 ASDCP::MXF::InterchangeObject::IsA(const byte_t* label)
1435 if ( m_KLLength == 0 || m_KeyStart == 0 )
1438 return ( memcmp(label, m_KeyStart, SMPTE_UL_LENGTH) == 0 );
1442 //------------------------------------------------------------------------------------------
1445 typedef std::map<ASDCP::UL, ASDCP::MXF::MXFObjectFactory_t>FactoryMap_t;
1446 typedef FactoryMap_t::iterator FLi_t;
1449 class FactoryList : public FactoryMap_t
1458 Kumu::AutoMutex BlockLock(m_Lock);
1462 FLi_t Find(const byte_t* label) {
1463 Kumu::AutoMutex BlockLock(m_Lock);
1468 Kumu::AutoMutex BlockLock(m_Lock);
1472 void Insert(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory) {
1473 Kumu::AutoMutex BlockLock(m_Lock);
1474 insert(FactoryList::value_type(label, factory));
1479 static FactoryList s_FactoryList;
1480 static Kumu::Mutex s_InitLock;
1481 static bool s_TypesInit = false;
1486 ASDCP::MXF::SetObjectFactory(const ASDCP::UL& label, ASDCP::MXF::MXFObjectFactory_t factory)
1488 s_FactoryList.Insert(label, factory);
1492 ASDCP::MXF::InterchangeObject*
1493 ASDCP::MXF::CreateObject(const Dictionary*& Dict, const UL& label)
1495 if ( ! s_TypesInit )
1497 Kumu::AutoMutex BlockLock(s_InitLock);
1499 if ( ! s_TypesInit )
1501 MXF::Metadata_InitTypes(Dict);
1506 FLi_t i = s_FactoryList.find(label.Value());
1508 if ( i == s_FactoryList.end() )
1509 return new InterchangeObject(Dict);
1511 return i->second(Dict);
1515 //------------------------------------------------------------------------------------------
1519 ASDCP::MXF::decode_mca_string(const std::string& s, const mca_label_map_t& labels, const Dictionary*& dict, const std::string& language,
1520 InterchangeObject_list_t& descriptor_list, ui32_t& channel_count)
1522 std::string symbol_buf;
1524 ASDCP::MXF::SoundfieldGroupLabelSubDescriptor *current_soundfield = 0, *prev_soundfield = 0;
1525 std::string::const_iterator i;
1527 for ( i = s.begin(); i != s.end(); ++i )
1531 if ( current_soundfield != 0 && symbol_buf.empty() )
1533 // appending to the existing soundfield group
1536 else if ( current_soundfield != 0 )
1538 DefaultLogSink().Error("Encountered '(', already processing a soundfield group.\n");
1541 else if ( symbol_buf.empty() )
1543 if ( prev_soundfield != 0 )
1545 current_soundfield = prev_soundfield;
1546 // appending to the existing soundfield group
1551 DefaultLogSink().Error("Encountered '(', without leading soundfield group symbol.\n");
1556 mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1558 if ( i == labels.end() )
1560 DefaultLogSink().Error("Unknown symbol: '%s'\n", symbol_buf.c_str());
1564 if ( i->second.ul.Value()[10] != 2 ) // magic depends on UL "Essence Facet" byte (see ST 428-12)
1566 DefaultLogSink().Error("Not a soundfield group symbol: '%s'\n", symbol_buf.c_str());
1570 current_soundfield = new ASDCP::MXF::SoundfieldGroupLabelSubDescriptor(dict);
1571 GenRandomValue(current_soundfield->MCALinkID);
1573 current_soundfield->MCATagSymbol = (i->second.requires_prefix ? "sg" : "") + i->first;
1574 current_soundfield->MCATagName = i->second.tag_name;
1575 current_soundfield->RFC5646SpokenLanguage = language;
1576 current_soundfield->MCALabelDictionaryID = i->second.ul;
1577 descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(current_soundfield));
1578 prev_soundfield = current_soundfield;
1581 else if ( *i == ')' )
1583 if ( current_soundfield == 0 )
1585 DefaultLogSink().Error("Encountered ')', not currently processing a soundfield group.\n");
1589 if ( symbol_buf.empty() )
1591 DefaultLogSink().Error("Soundfield group description contains no channels.\n");
1595 mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1597 if ( i == labels.end() )
1599 DefaultLogSink().Error("Unknown symbol: '%s'\n", symbol_buf.c_str());
1603 assert(current_soundfield);
1605 ASDCP::MXF::AudioChannelLabelSubDescriptor *channel_descr =
1606 new ASDCP::MXF::AudioChannelLabelSubDescriptor(dict);
1607 GenRandomValue(channel_descr->MCALinkID);
1609 channel_descr->SoundfieldGroupLinkID = current_soundfield->MCALinkID;
1610 channel_descr->MCAChannelID = channel_count++ + 1;
1611 channel_descr->MCATagSymbol = (i->second.requires_prefix ? "ch" : "") + i->first;
1612 channel_descr->MCATagName = i->second.tag_name;
1613 channel_descr->RFC5646SpokenLanguage = language;
1614 channel_descr->MCALabelDictionaryID = i->second.ul;
1615 descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(channel_descr));
1617 current_soundfield = 0;
1619 else if ( *i == ',' )
1621 if ( ! symbol_buf.empty() && ! symbol_buf.compare("-") )
1626 else if ( ! symbol_buf.empty() )
1628 mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1630 if ( i == labels.end() )
1632 DefaultLogSink().Error("Unknown symbol: '%s'\n", symbol_buf.c_str());
1636 if ( i->second.ul.Value()[10] != 1 ) // magic depends on UL "Essence Facet" byte (see ST 428-12)
1638 DefaultLogSink().Error("Not a channel symbol: '%s'\n", symbol_buf.c_str());
1642 ASDCP::MXF::AudioChannelLabelSubDescriptor *channel_descr =
1643 new ASDCP::MXF::AudioChannelLabelSubDescriptor(dict);
1644 GenRandomValue(channel_descr->MCALinkID);
1646 if ( current_soundfield != 0 )
1648 channel_descr->SoundfieldGroupLinkID = current_soundfield->MCALinkID;
1651 channel_descr->MCAChannelID = channel_count++ + 1;
1652 channel_descr->MCATagSymbol = (i->second.requires_prefix ? "ch" : "") + i->first;
1653 channel_descr->MCATagName = i->second.tag_name;
1654 channel_descr->RFC5646SpokenLanguage = language;
1655 channel_descr->MCALabelDictionaryID = i->second.ul;
1656 descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(channel_descr));
1660 else if ( *i == '-' || isalnum(*i) )
1664 else if ( ! isspace(*i) )
1666 DefaultLogSink().Error("Unexpected character '%c'.\n", *i);
1671 if ( ! symbol_buf.empty() && ! symbol_buf.compare("-") )
1675 else if ( ! symbol_buf.empty() )
1677 mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1679 if ( i == labels.end() )
1681 DefaultLogSink().Error("Unknown symbol: '%s'\n", symbol_buf.c_str());
1685 ASDCP::MXF::AudioChannelLabelSubDescriptor *channel_descr =
1686 new ASDCP::MXF::AudioChannelLabelSubDescriptor(dict);
1687 GenRandomValue(channel_descr->MCALinkID);
1689 if ( current_soundfield != 0 )
1691 channel_descr->SoundfieldGroupLinkID = current_soundfield->MCALinkID;
1694 channel_descr->MCAChannelID = channel_count++ + 1;
1695 channel_descr->MCATagSymbol = (i->second.requires_prefix ? "ch" : "") + i->first;
1696 channel_descr->MCATagName = i->second.tag_name;
1697 channel_descr->RFC5646SpokenLanguage = language;
1698 channel_descr->MCALabelDictionaryID = i->second.ul;
1699 descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(channel_descr));
1706 ASDCP::MXF::ASDCP_MCAConfigParser::ASDCP_MCAConfigParser(const Dictionary*& d) : m_Dict(d), m_ChannelCount(0)
1708 typedef mca_label_map_t::value_type pair;
1709 m_LabelMap.insert(pair("L", label_traits("Left" , true, m_Dict->ul(MDD_DCAudioChannel_L))));
1710 m_LabelMap.insert(pair("R", label_traits("Right" , true, m_Dict->ul(MDD_DCAudioChannel_R))));
1711 m_LabelMap.insert(pair("C", label_traits("Center" , true, m_Dict->ul(MDD_DCAudioChannel_C))));
1712 m_LabelMap.insert(pair("LFE", label_traits("LFE" , true, m_Dict->ul(MDD_DCAudioChannel_LFE))));
1713 m_LabelMap.insert(pair("Ls", label_traits("Left Surround" , true, m_Dict->ul(MDD_DCAudioChannel_Ls))));
1714 m_LabelMap.insert(pair("Rs", label_traits("Right Surround" , true, m_Dict->ul(MDD_DCAudioChannel_Rs))));
1715 m_LabelMap.insert(pair("Lss", label_traits("Left Side Surround" , true, m_Dict->ul(MDD_DCAudioChannel_Lss))));
1716 m_LabelMap.insert(pair("Rss", label_traits("Right Side Surround" , true, m_Dict->ul(MDD_DCAudioChannel_Rss))));
1717 m_LabelMap.insert(pair("Lrs", label_traits("Left Rear Surround" , true, m_Dict->ul(MDD_DCAudioChannel_Lrs))));
1718 m_LabelMap.insert(pair("Rrs", label_traits("Right Rear Surround" , true, m_Dict->ul(MDD_DCAudioChannel_Rrs))));
1719 m_LabelMap.insert(pair("Lc", label_traits("Left Center" , true, m_Dict->ul(MDD_DCAudioChannel_Lc))));
1720 m_LabelMap.insert(pair("Rc", label_traits("Right Center" , true, m_Dict->ul(MDD_DCAudioChannel_Rc))));
1721 m_LabelMap.insert(pair("Cs", label_traits("Center Surround" , true, m_Dict->ul(MDD_DCAudioChannel_Cs))));
1722 m_LabelMap.insert(pair("HI", label_traits("Hearing Impaired" , true, m_Dict->ul(MDD_DCAudioChannel_HI))));
1723 m_LabelMap.insert(pair("VIN", label_traits("Visually Impaired-Narrative" , true, m_Dict->ul(MDD_DCAudioChannel_VIN))));
1724 m_LabelMap.insert(pair("51", label_traits("5.1" , true, m_Dict->ul(MDD_DCAudioSoundfield_51))));
1725 m_LabelMap.insert(pair("71", label_traits("7.1DS" , true, m_Dict->ul(MDD_DCAudioSoundfield_71))));
1726 m_LabelMap.insert(pair("SDS", label_traits("7.1SDS" , true, m_Dict->ul(MDD_DCAudioSoundfield_SDS))));
1727 m_LabelMap.insert(pair("61", label_traits("6.1" , true, m_Dict->ul(MDD_DCAudioSoundfield_61))));
1728 m_LabelMap.insert(pair("M", label_traits("1.0 Monaural" , true, m_Dict->ul(MDD_DCAudioSoundfield_M))));
1729 m_LabelMap.insert(pair("DBOX", label_traits("D-BOX Motion Code Primary Stream" , false, m_Dict->ul(MDD_DBOXMotionCodePrimaryStream))));
1730 m_LabelMap.insert(pair("DBOX2", label_traits("D-BOX Motion Code Secondary Stream", false, m_Dict->ul(MDD_DBOXMotionCodeSecondaryStream))));
1735 ASDCP::MXF::ASDCP_MCAConfigParser::ChannelCount() const
1737 return m_ChannelCount;
1740 // 51(L,R,C,LFE,Ls,Rs),HI,VIN
1742 ASDCP::MXF::ASDCP_MCAConfigParser::DecodeString(const std::string& s, const std::string& language)
1744 return decode_mca_string(s, m_LabelMap, m_Dict, language, *this, m_ChannelCount);
1747 // ST(L,R),DNS(NSC001,NSC002),-,VIN
1748 ASDCP::MXF::AS02_MCAConfigParser::AS02_MCAConfigParser(const Dictionary*& d) : ASDCP::MXF::ASDCP_MCAConfigParser(d)
1750 typedef mca_label_map_t::value_type pair;
1751 m_LabelMap.insert(pair("M1", label_traits("Mono One", true, m_Dict->ul(MDD_IMFAudioChannel_M1))));
1752 m_LabelMap.insert(pair("M2", label_traits("Mono Two", true, m_Dict->ul(MDD_IMFAudioChannel_M2))));
1753 m_LabelMap.insert(pair("Lt", label_traits("Left Total", true, m_Dict->ul(MDD_IMFAudioChannel_Lt))));
1754 m_LabelMap.insert(pair("Rt", label_traits("Right Total", true, m_Dict->ul(MDD_IMFAudioChannel_Rt))));
1755 m_LabelMap.insert(pair("Lst", label_traits("Left Surround Total", true, m_Dict->ul(MDD_IMFAudioChannel_Lst))));
1756 m_LabelMap.insert(pair("Rst", label_traits("Right Surround Total", true, m_Dict->ul(MDD_IMFAudioChannel_Rst))));
1757 m_LabelMap.insert(pair("S", label_traits("Surround", true, m_Dict->ul(MDD_IMFAudioChannel_S))));
1758 m_LabelMap.insert(pair("ST", label_traits("Standard Stereo", true, m_Dict->ul(MDD_IMFAudioSoundfield_ST))));
1759 m_LabelMap.insert(pair("DM", label_traits("Dual Mono", true, m_Dict->ul(MDD_IMFAudioSoundfield_DM))));
1760 m_LabelMap.insert(pair("DNS", label_traits("Discrete Numbered Sources", true, m_Dict->ul(MDD_IMFAudioSoundfield_DNS))));
1761 m_LabelMap.insert(pair("30", label_traits("3.0", true, m_Dict->ul(MDD_IMFAudioSoundfield_30))));
1762 m_LabelMap.insert(pair("40", label_traits("4.0", true, m_Dict->ul(MDD_IMFAudioSoundfield_40))));
1763 m_LabelMap.insert(pair("50", label_traits("5.0", true, m_Dict->ul(MDD_IMFAudioSoundfield_50))));
1764 m_LabelMap.insert(pair("60", label_traits("6.0", true, m_Dict->ul(MDD_IMFAudioSoundfield_60))));
1765 m_LabelMap.insert(pair("70", label_traits("7.0DS", true, m_Dict->ul(MDD_IMFAudioSoundfield_70))));
1766 m_LabelMap.insert(pair("LtRt", label_traits("Lt-Rt",true, m_Dict->ul(MDD_IMFAudioSoundfield_LtRt))));
1767 m_LabelMap.insert(pair("51Ex", label_traits("5.1EX",true, m_Dict->ul(MDD_IMFAudioSoundfield_51Ex))));
1768 m_LabelMap.insert(pair("HA", label_traits("Hearing Accessibility", true, m_Dict->ul(MDD_IMFAudioSoundfield_HI))));
1769 m_LabelMap.insert(pair("VA", label_traits("Visual Accessibility", true, m_Dict->ul(MDD_IMFAudioSoundfield_VIN))));
1771 // assemble the set of Numbered Source Channel labels
1772 char name_buf[64], symbol_buf[64];
1774 memcpy(ul_buf, m_Dict->ul(MDD_IMFNumberedSourceChannel), 16);
1776 for ( int i = 1; i < 128; ++i )
1778 snprintf(name_buf, 64, "Numbered Source Channel %03d", i);
1779 snprintf(symbol_buf, 64, "NSC%03d", i);
1781 m_LabelMap.insert(pair(symbol_buf, label_traits(name_buf, true, UL(ul_buf))));
1787 ASDCP::MXF::GetEditRateFromFP(ASDCP::MXF::OP1aHeader& header, ASDCP::Rational& edit_rate)
1789 bool has_first_item = false;
1791 MXF::InterchangeObject* temp_item;
1792 std::list<MXF::InterchangeObject*> temp_items;
1794 Result_t result = header.GetMDObjectsByType(DefaultCompositeDict().ul(MDD_SourcePackage), temp_items);
1796 if ( KM_FAILURE(result) )
1798 DefaultLogSink().Error("The MXF header does not contain a FilePackage item.\n");
1802 if ( temp_items.size() != 1 )
1804 DefaultLogSink().Error("The MXF header must contain one FilePackage item, found %d.\n", temp_items.size());
1809 MXF::Array<UUID>::const_iterator i;
1810 MXF::SourcePackage *source_package = dynamic_cast<MXF::SourcePackage*>(temp_items.front());
1811 assert(source_package);
1813 for ( i = source_package->Tracks.begin(); i != source_package->Tracks.end(); ++i )
1816 result = header.GetMDObjectByID(*i, &temp_item);
1818 if ( KM_FAILURE(result) )
1820 DefaultLogSink().Error("The MXF header is incomplete: strong reference %s leads nowhere.\n",
1821 i->EncodeHex(buf, 64));
1825 MXF::Track *track = dynamic_cast<MXF::Track*>(temp_item);
1829 DefaultLogSink().Error("The MXF header is incomplete: %s is not a Track item.\n",
1830 i->EncodeHex(buf, 64));
1835 result = header.GetMDObjectByID(track->Sequence, &temp_item);
1837 if ( KM_FAILURE(result) )
1839 DefaultLogSink().Error("The MXF header is incomplete: strong reference %s leads nowhere.\n",
1840 i->EncodeHex(buf, 64));
1844 MXF::Sequence *sequence = dynamic_cast<MXF::Sequence*>(temp_item);
1846 if ( sequence == 0 )
1848 DefaultLogSink().Error("The MXF header is incomplete: %s is not a Sequence item.\n",
1849 track->Sequence.get().EncodeHex(buf, 64));
1853 if ( sequence->StructuralComponents.size() != 1 )
1855 DefaultLogSink().Error("The Sequence item must contain one reference to an esence item, found %d.\n",
1856 sequence->StructuralComponents.size());
1861 result = header.GetMDObjectByID(sequence->StructuralComponents.front(), &temp_item);
1863 if ( KM_FAILURE(result) )
1865 DefaultLogSink().Error("The MXF header is incomplete: strong reference %s leads nowhere.\n",
1866 sequence->StructuralComponents.front().EncodeHex(buf, 64));
1870 if ( temp_item->IsA(DefaultCompositeDict().ul(MDD_SourceClip)) )
1872 MXF::SourceClip *source_clip = dynamic_cast<MXF::SourceClip*>(temp_item);
1874 if ( source_clip == 0 )
1876 DefaultLogSink().Error("The MXF header is incomplete: %s is not a SourceClip item.\n",
1877 sequence->StructuralComponents.front().EncodeHex(buf, 64));
1881 if ( ! has_first_item )
1883 edit_rate = track->EditRate;
1884 has_first_item = true;
1886 else if ( edit_rate != track->EditRate )
1888 DefaultLogSink().Error("The MXF header is incomplete: %s EditRate value does not match others in the file.\n",
1889 sequence->StructuralComponents.front().EncodeHex(buf, 64));
1893 else if ( ! temp_item->IsA(DefaultCompositeDict().ul(MDD_TimecodeComponent)) )
1895 DefaultLogSink().Error("Reference from Sequence to an unexpected type: %s.\n", temp_item->ObjectName());