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;
615 ASDCP::MXF::Preface::InitFromTLVSet(TLVReader& TLVSet)
617 Result_t result = InterchangeObject::InitFromTLVSet(TLVSet);
618 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, LastModifiedDate));
619 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi16(OBJ_READ_ARGS(Preface, Version));
620 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi32(OBJ_READ_ARGS_OPT(Preface, ObjectModelVersion));
621 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS_OPT(Preface, PrimaryPackage));
622 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, Identifications));
623 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, ContentStorage));
624 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, OperationalPattern));
625 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, EssenceContainers));
626 if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, DMSchemes));
632 ASDCP::MXF::Preface::WriteToTLVSet(TLVWriter& TLVSet)
634 Result_t result = InterchangeObject::WriteToTLVSet(TLVSet);
635 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, LastModifiedDate));
636 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi16(OBJ_WRITE_ARGS(Preface, Version));
637 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi32(OBJ_WRITE_ARGS_OPT(Preface, ObjectModelVersion));
638 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS_OPT(Preface, PrimaryPackage));
639 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, Identifications));
640 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, ContentStorage));
641 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, OperationalPattern));
642 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, EssenceContainers));
643 if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, DMSchemes));
649 ASDCP::MXF::Preface::InitFromBuffer(const byte_t* p, ui32_t l)
651 return InterchangeObject::InitFromBuffer(p, l);
656 ASDCP::MXF::Preface::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
658 return InterchangeObject::WriteToBuffer(Buffer);
663 ASDCP::MXF::Preface::Dump(FILE* stream)
665 char identbuf[IdentBufferLen];
670 InterchangeObject::Dump(stream);
671 fprintf(stream, " %22s = %s\n", "LastModifiedDate", LastModifiedDate.EncodeString(identbuf, IdentBufferLen));
672 fprintf(stream, " %22s = %hu\n", "Version", Version);
674 if ( ! ObjectModelVersion.empty() )
675 fprintf(stream, " %22s = %u\n", "ObjectModelVersion", ObjectModelVersion.get());
677 if ( ! PrimaryPackage.empty() )
678 fprintf(stream, " %22s = %s\n", "PrimaryPackage", PrimaryPackage.get().EncodeHex(identbuf, IdentBufferLen));
680 fprintf(stream, " %22s:\n", "Identifications"); Identifications.Dump(stream);
681 fprintf(stream, " %22s = %s\n", "ContentStorage", ContentStorage.EncodeHex(identbuf, IdentBufferLen));
682 fprintf(stream, " %22s = %s\n", "OperationalPattern", OperationalPattern.EncodeString(identbuf, IdentBufferLen));
683 fprintf(stream, " %22s:\n", "EssenceContainers"); EssenceContainers.Dump(stream);
684 fprintf(stream, " %22s:\n", "DMSchemes"); DMSchemes.Dump(stream);
687 //------------------------------------------------------------------------------------------
690 ASDCP::MXF::OP1aHeader::OP1aHeader(const Dictionary*& d) : Partition(d), m_Dict(d), m_Primer(d), m_Preface(0) {}
691 ASDCP::MXF::OP1aHeader::~OP1aHeader() {}
695 ASDCP::MXF::OP1aHeader::InitFromFile(const Kumu::FileReader& Reader)
697 Result_t result = Partition::InitFromFile(Reader);
699 if ( ASDCP_FAILURE(result) )
702 if ( m_Dict == &DefaultCompositeDict() )
704 // select more explicit dictionary if one is available
705 if ( OperationalPattern.MatchExact(MXFInterop_OPAtom_Entry().ul) )
707 m_Dict = &DefaultInteropDict();
709 else if ( OperationalPattern.MatchExact(SMPTE_390_OPAtom_Entry().ul) )
711 m_Dict = &DefaultSMPTEDict();
715 // slurp up the remainder of the header
716 if ( HeaderByteCount == 0 )
718 DefaultLogSink().Warn("MXF file contents incomplete.\n");
719 return RESULT_KLV_CODING(__LINE__, __FILE__);
721 else if ( HeaderByteCount < 1024 )
723 DefaultLogSink().Warn("Improbably small HeaderByteCount value: %qu\n", HeaderByteCount);
725 else if (HeaderByteCount > ( 4 * Kumu::Megabyte ) )
727 DefaultLogSink().Warn("Improbably huge HeaderByteCount value: %qu\n", HeaderByteCount);
730 result = m_HeaderData.Capacity(Kumu::xmin(4*Kumu::Megabyte, static_cast<ui32_t>(HeaderByteCount)));
732 if ( ASDCP_SUCCESS(result) )
735 result = Reader.Read(m_HeaderData.Data(), m_HeaderData.Capacity(), &read_count);
737 if ( ASDCP_FAILURE(result) )
739 DefaultLogSink().Error("OP1aHeader::InitFromFile, read failed.\n");
743 if ( read_count != m_HeaderData.Capacity() )
745 DefaultLogSink().Error("Short read of OP-Atom header metadata; wanted %u, got %u.\n",
746 m_HeaderData.Capacity(), read_count);
747 return RESULT_KLV_CODING(__LINE__, __FILE__);
751 if ( ASDCP_SUCCESS(result) )
752 result = InitFromBuffer(m_HeaderData.RoData(), m_HeaderData.Capacity());
759 ASDCP::MXF::OP1aHeader::InitFromPartitionBuffer(const byte_t* p, ui32_t l)
761 Result_t result = KLVPacket::InitFromBuffer(p, l);
763 if ( ASDCP_SUCCESS(result) )
764 result = Partition::InitFromBuffer(m_ValueStart, m_ValueLength); // test UL and OP
766 if ( ASDCP_SUCCESS(result) )
768 ui32_t pp_len = KLVPacket::PacketLength();
769 result = InitFromBuffer(p + pp_len, l - pp_len);
777 ASDCP::MXF::OP1aHeader::InitFromBuffer(const byte_t* p, ui32_t l)
780 Result_t result = RESULT_OK;
781 const byte_t* end_p = p + l;
783 while ( ASDCP_SUCCESS(result) && p < end_p )
785 // parse the packets and index them by uid, discard KLVFill items
786 InterchangeObject* object = CreateObject(m_Dict, p);
789 object->m_Lookup = &m_Primer;
790 result = object->InitFromBuffer(p, end_p - p);
792 const byte_t* redo_p = p;
793 p += object->PacketLength();
795 if ( ASDCP_SUCCESS(result) )
797 if ( object->IsA(m_Dict->ul(MDD_KLVFill)) )
803 DefaultLogSink().Error("Fill item short read: %d.\n", p - end_p);
806 else if ( object->IsA(m_Dict->ul(MDD_Primer)) ) // TODO: only one primer should be found
809 result = m_Primer.InitFromBuffer(redo_p, end_p - redo_p);
813 m_PacketList->AddPacket(object); // takes ownership
815 if ( object->IsA(m_Dict->ul(MDD_Preface)) && m_Preface == 0 )
816 m_Preface = (Preface*)object;
821 DefaultLogSink().Error("Error initializing OP1a header packet.\n");
822 // Kumu::hexdump(p-object->PacketLength(), object->PacketLength());
831 ASDCP::MXF::OP1aHeader::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
833 return m_PacketList->GetMDObjectByID(ObjectID, Object);
838 ASDCP::MXF::OP1aHeader::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
840 InterchangeObject* TmpObject;
845 return m_PacketList->GetMDObjectByType(ObjectID, Object);
850 ASDCP::MXF::OP1aHeader::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
852 return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
856 ASDCP::MXF::Identification*
857 ASDCP::MXF::OP1aHeader::GetIdentification()
859 InterchangeObject* Object;
861 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object)) )
862 return (Identification*)Object;
868 ASDCP::MXF::SourcePackage*
869 ASDCP::MXF::OP1aHeader::GetSourcePackage()
871 InterchangeObject* Object;
873 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object)) )
874 return (SourcePackage*)Object;
881 ASDCP::MXF::OP1aHeader::WriteToFile(Kumu::FileWriter& Writer, ui32_t HeaderSize)
884 if ( m_Preface == 0 )
887 if ( HeaderSize < 4096 )
889 DefaultLogSink().Error("HeaderSize %u is too small. Must be >= 4096\n", HeaderSize);
893 ASDCP::FrameBuffer HeaderBuffer;
894 HeaderByteCount = HeaderSize - ArchiveSize();
895 assert (HeaderByteCount <= 0xFFFFFFFFL);
896 Result_t result = HeaderBuffer.Capacity((ui32_t) HeaderByteCount);
897 m_Preface->m_Lookup = &m_Primer;
899 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
900 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
902 InterchangeObject* object = *pl_i;
903 object->m_Lookup = &m_Primer;
905 ASDCP::FrameBuffer WriteWrapper;
906 WriteWrapper.SetData(HeaderBuffer.Data() + HeaderBuffer.Size(),
907 HeaderBuffer.Capacity() - HeaderBuffer.Size());
908 result = object->WriteToBuffer(WriteWrapper);
909 HeaderBuffer.Size(HeaderBuffer.Size() + WriteWrapper.Size());
912 if ( ASDCP_SUCCESS(result) )
914 UL TmpUL(m_Dict->ul(MDD_ClosedCompleteHeader));
915 result = Partition::WriteToFile(Writer, TmpUL);
918 if ( ASDCP_SUCCESS(result) )
919 result = m_Primer.WriteToFile(Writer);
921 if ( ASDCP_SUCCESS(result) )
924 Writer.Write(HeaderBuffer.RoData(), HeaderBuffer.Size(), &write_count);
925 assert(write_count == HeaderBuffer.Size());
929 if ( ASDCP_SUCCESS(result) )
931 Kumu::fpos_t pos = Writer.Tell();
933 if ( pos > (Kumu::fpos_t)HeaderByteCount )
935 char intbuf[IntBufferLen];
936 DefaultLogSink().Error("Header size %s exceeds specified value %u\n",
942 ASDCP::FrameBuffer NilBuf;
943 ui32_t klv_fill_length = HeaderSize - (ui32_t)pos;
945 if ( klv_fill_length < kl_length )
947 DefaultLogSink().Error("Remaining region too small for KLV Fill header\n");
951 klv_fill_length -= kl_length;
952 result = WriteKLToFile(Writer, m_Dict->ul(MDD_KLVFill), klv_fill_length);
954 if ( ASDCP_SUCCESS(result) )
955 result = NilBuf.Capacity(klv_fill_length);
957 if ( ASDCP_SUCCESS(result) )
959 memset(NilBuf.Data(), 0, klv_fill_length);
961 Writer.Write(NilBuf.RoData(), klv_fill_length, &write_count);
962 assert(write_count == klv_fill_length);
971 ASDCP::MXF::OP1aHeader::Dump(FILE* stream)
976 Partition::Dump(stream);
977 m_Primer.Dump(stream);
979 if ( m_Preface == 0 )
980 fputs("No Preface loaded\n", stream);
982 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
983 for ( ; i != m_PacketList->m_List.end(); i++ )
987 //------------------------------------------------------------------------------------------
990 ASDCP::MXF::OPAtomIndexFooter::OPAtomIndexFooter(const Dictionary*& d) :
991 Partition(d), m_Dict(d),
992 m_CurrentSegment(0), m_BytesPerEditUnit(0), m_BodySID(0),
993 m_ECOffset(0), m_Lookup(0)
999 ASDCP::MXF::OPAtomIndexFooter::~OPAtomIndexFooter() {}
1003 ASDCP::MXF::OPAtomIndexFooter::InitFromFile(const Kumu::FileReader& Reader)
1005 Result_t result = Partition::InitFromFile(Reader); // test UL and OP
1007 // slurp up the remainder of the footer
1008 ui32_t read_count = 0;
1010 if ( ASDCP_SUCCESS(result) && IndexByteCount > 0 )
1012 assert (IndexByteCount <= 0xFFFFFFFFL);
1013 // At this point, m_FooterData may not have been initialized
1014 // so it's capacity is zero and data pointer is NULL
1015 // However, if IndexByteCount is zero then the capacity
1016 // doesn't change and the data pointer is not set.
1017 result = m_FooterData.Capacity((ui32_t) IndexByteCount);
1019 if ( ASDCP_SUCCESS(result) )
1020 result = Reader.Read(m_FooterData.Data(), m_FooterData.Capacity(), &read_count);
1022 if ( ASDCP_SUCCESS(result) && read_count != m_FooterData.Capacity() )
1024 DefaultLogSink().Error("Short read of footer partition: got %u, expecting %u\n",
1025 read_count, m_FooterData.Capacity());
1028 else if( ASDCP_SUCCESS(result) && !m_FooterData.Data() )
1030 DefaultLogSink().Error( "Buffer for footer partition not created: IndexByteCount = %u\n",
1035 if ( ASDCP_SUCCESS(result) )
1036 result = InitFromBuffer(m_FooterData.RoData(), m_FooterData.Capacity());
1044 ASDCP::MXF::OPAtomIndexFooter::InitFromPartitionBuffer(const byte_t* p, ui32_t l)
1046 Result_t result = KLVPacket::InitFromBuffer(p, l);
1048 if ( ASDCP_SUCCESS(result) )
1049 result = Partition::InitFromBuffer(m_ValueStart, m_ValueLength); // test UL and OP
1051 if ( ASDCP_SUCCESS(result) )
1053 ui32_t pp_len = KLVPacket::PacketLength();
1054 result = InitFromBuffer(p + pp_len, l - pp_len);
1062 ASDCP::MXF::OPAtomIndexFooter::InitFromBuffer(const byte_t* p, ui32_t l)
1064 Result_t result = RESULT_OK;
1065 const byte_t* end_p = p + l;
1067 while ( ASDCP_SUCCESS(result) && p < end_p )
1069 // parse the packets and index them by uid, discard KLVFill items
1070 InterchangeObject* object = CreateObject(m_Dict, p);
1073 object->m_Lookup = m_Lookup;
1074 result = object->InitFromBuffer(p, end_p - p);
1075 p += object->PacketLength();
1077 if ( ASDCP_SUCCESS(result) )
1079 m_PacketList->AddPacket(object); // takes ownership
1083 DefaultLogSink().Error("Error initializing OPAtom footer packet.\n");
1088 if ( ASDCP_FAILURE(result) )
1090 DefaultLogSink().Error("Failed to initialize OPAtomIndexFooter.\n");
1098 ASDCP::MXF::OPAtomIndexFooter::WriteToFile(Kumu::FileWriter& Writer, ui64_t duration)
1101 ASDCP::FrameBuffer FooterBuffer;
1102 ui32_t footer_size = m_PacketList->m_List.size() * MaxIndexSegmentSize; // segment-count * max-segment-size
1103 Result_t result = FooterBuffer.Capacity(footer_size);
1104 ui32_t iseg_count = 0;
1106 if ( m_CurrentSegment != 0 )
1108 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1109 m_CurrentSegment = 0;
1112 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
1113 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
1115 IndexTableSegment *segment = dynamic_cast<IndexTableSegment*>(*pl_i);
1120 if ( m_BytesPerEditUnit != 0 )
1122 if ( iseg_count != 1 )
1123 return RESULT_STATE;
1125 segment->IndexDuration = duration;
1129 InterchangeObject* object = *pl_i;
1130 object->m_Lookup = m_Lookup;
1132 ASDCP::FrameBuffer WriteWrapper;
1133 WriteWrapper.SetData(FooterBuffer.Data() + FooterBuffer.Size(),
1134 FooterBuffer.Capacity() - FooterBuffer.Size());
1135 result = object->WriteToBuffer(WriteWrapper);
1136 FooterBuffer.Size(FooterBuffer.Size() + WriteWrapper.Size());
1139 if ( ASDCP_SUCCESS(result) )
1141 IndexByteCount = FooterBuffer.Size();
1142 UL FooterUL(m_Dict->ul(MDD_CompleteFooter));
1143 result = Partition::WriteToFile(Writer, FooterUL);
1146 if ( ASDCP_SUCCESS(result) )
1148 ui32_t write_count = 0;
1149 result = Writer.Write(FooterBuffer.RoData(), FooterBuffer.Size(), &write_count);
1150 assert(write_count == FooterBuffer.Size());
1158 ASDCP::MXF::OPAtomIndexFooter::Dump(FILE* stream)
1163 Partition::Dump(stream);
1165 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
1166 for ( ; i != m_PacketList->m_List.end(); i++ )
1171 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
1173 return m_PacketList->GetMDObjectByID(ObjectID, Object);
1178 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
1180 InterchangeObject* TmpObject;
1183 Object = &TmpObject;
1185 return m_PacketList->GetMDObjectByType(ObjectID, Object);
1190 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
1192 return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
1197 ASDCP::MXF::OPAtomIndexFooter::Lookup(ui32_t frame_num, IndexTableSegment::IndexEntry& Entry) const
1199 std::list<InterchangeObject*>::iterator li;
1200 for ( li = m_PacketList->m_List.begin(); li != m_PacketList->m_List.end(); li++ )
1202 IndexTableSegment *segment = dynamic_cast<IndexTableSegment*>(*li);
1206 ui64_t start_pos = segment->IndexStartPosition;
1208 if ( segment->EditUnitByteCount > 0 )
1210 if ( m_PacketList->m_List.size() > 1 )
1211 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
1213 if ( ! segment->IndexEntryArray.empty() )
1214 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
1216 Entry.StreamOffset = (ui64_t)frame_num * segment->EditUnitByteCount;
1219 else if ( (ui64_t)frame_num >= start_pos
1220 && (ui64_t)frame_num < (start_pos + segment->IndexDuration) )
1222 ui64_t tmp = frame_num - start_pos;
1223 assert(tmp <= 0xFFFFFFFFL);
1225 if ( tmp < segment->IndexEntryArray.size() )
1227 Entry = segment->IndexEntryArray[(ui32_t) tmp];
1232 DefaultLogSink().Error("Malformed index table segment, IndexDuration does not match entries.\n");
1243 ASDCP::MXF::OPAtomIndexFooter::SetDeltaParams(const IndexTableSegment::DeltaEntry& delta)
1245 m_DefaultDeltaEntry = delta;
1250 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const Rational& Rate)
1254 m_BytesPerEditUnit = size;
1257 IndexTableSegment* Index = new IndexTableSegment(m_Dict);
1258 AddChildObject(Index);
1259 Index->EditUnitByteCount = m_BytesPerEditUnit;
1260 Index->IndexEditRate = Rate;
1265 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsVBR(IPrimerLookup* lookup, const Rational& Rate, Kumu::fpos_t offset)
1269 m_BytesPerEditUnit = 0;
1271 m_ECOffset = offset;
1276 ASDCP::MXF::OPAtomIndexFooter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
1278 if ( m_BytesPerEditUnit != 0 ) // are we CBR? that's bad
1280 DefaultLogSink().Error("Call to PushIndexEntry() failed: index is CBR\n");
1284 // do we have an available segment?
1285 if ( m_CurrentSegment == 0 )
1286 { // no, set up a new segment
1287 m_CurrentSegment = new IndexTableSegment(m_Dict);
1288 assert(m_CurrentSegment);
1289 AddChildObject(m_CurrentSegment);
1290 m_CurrentSegment->DeltaEntryArray.push_back(m_DefaultDeltaEntry);
1291 m_CurrentSegment->IndexEditRate = m_EditRate;
1292 m_CurrentSegment->IndexStartPosition = 0;
1294 else if ( m_CurrentSegment->IndexEntryArray.size() >= CBRIndexEntriesPerSegment )
1295 { // no, this one is full, start another
1296 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1297 ui64_t StartPosition = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
1299 m_CurrentSegment = new IndexTableSegment(m_Dict);
1300 assert(m_CurrentSegment);
1301 AddChildObject(m_CurrentSegment);
1302 m_CurrentSegment->DeltaEntryArray.push_back(m_DefaultDeltaEntry);
1303 m_CurrentSegment->IndexEditRate = m_EditRate;
1304 m_CurrentSegment->IndexStartPosition = StartPosition;
1307 m_CurrentSegment->IndexEntryArray.push_back(Entry);
1310 //------------------------------------------------------------------------------------------
1315 ASDCP::MXF::InterchangeObject::Copy(const InterchangeObject& rhs)
1318 InstanceUID = rhs.InstanceUID;
1319 GenerationUID = rhs.GenerationUID;
1324 ASDCP::MXF::InterchangeObject::InitFromTLVSet(TLVReader& TLVSet)
1326 Result_t result = TLVSet.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID));
1327 if ( ASDCP_SUCCESS(result) )
1328 result = TLVSet.ReadObject(OBJ_READ_ARGS_OPT(GenerationInterchangeObject, GenerationUID));
1334 ASDCP::MXF::InterchangeObject::WriteToTLVSet(TLVWriter& TLVSet)
1336 Result_t result = TLVSet.WriteObject(OBJ_WRITE_ARGS(InterchangeObject, InstanceUID));
1337 if ( ASDCP_SUCCESS(result) )
1338 result = TLVSet.WriteObject(OBJ_WRITE_ARGS_OPT(GenerationInterchangeObject, GenerationUID));
1344 ASDCP::MXF::InterchangeObject::InitFromBuffer(const byte_t* p, ui32_t l)
1347 Result_t result = RESULT_FALSE;
1349 if ( m_UL.HasValue() )
1351 result = KLVPacket::InitFromBuffer(p, l, m_UL);
1353 if ( ASDCP_SUCCESS(result) )
1355 TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup);
1356 result = InitFromTLVSet(MemRDR);
1361 result = KLVPacket::InitFromBuffer(p, l);
1369 ASDCP::MXF::InterchangeObject::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
1371 if ( ! m_UL.HasValue() )
1372 return RESULT_STATE;
1374 TLVWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length, m_Lookup);
1375 Result_t result = WriteToTLVSet(MemWRT);
1377 if ( ASDCP_SUCCESS(result) )
1379 ui32_t packet_length = MemWRT.Length();
1380 result = WriteKLToBuffer(Buffer, packet_length);
1382 if ( ASDCP_SUCCESS(result) )
1383 Buffer.Size(Buffer.Size() + packet_length);
1391 ASDCP::MXF::InterchangeObject::Dump(FILE* stream)
1393 char identbuf[IdentBufferLen];
1395 fputc('\n', stream);
1396 KLVPacket::Dump(stream, *m_Dict, false);
1397 fprintf(stream, " InstanceUID = %s\n", InstanceUID.EncodeHex(identbuf, IdentBufferLen));
1399 if ( ! GenerationUID.empty() )
1400 fprintf(stream, " GenerationUID = %s\n", GenerationUID.get().EncodeHex(identbuf, IdentBufferLen));
1405 ASDCP::MXF::InterchangeObject::IsA(const byte_t* label)
1407 if ( m_KLLength == 0 || m_KeyStart == 0 )
1410 return ( memcmp(label, m_KeyStart, SMPTE_UL_LENGTH) == 0 );
1414 //------------------------------------------------------------------------------------------
1417 typedef std::map<ASDCP::UL, ASDCP::MXF::MXFObjectFactory_t>FactoryMap_t;
1418 typedef FactoryMap_t::iterator FLi_t;
1421 class FactoryList : public FactoryMap_t
1430 Kumu::AutoMutex BlockLock(m_Lock);
1434 FLi_t Find(const byte_t* label) {
1435 Kumu::AutoMutex BlockLock(m_Lock);
1440 Kumu::AutoMutex BlockLock(m_Lock);
1444 void Insert(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory) {
1445 Kumu::AutoMutex BlockLock(m_Lock);
1446 insert(FactoryList::value_type(label, factory));
1451 static FactoryList s_FactoryList;
1452 static Kumu::Mutex s_InitLock;
1453 static bool s_TypesInit = false;
1458 ASDCP::MXF::SetObjectFactory(const ASDCP::UL& label, ASDCP::MXF::MXFObjectFactory_t factory)
1460 s_FactoryList.Insert(label, factory);
1464 ASDCP::MXF::InterchangeObject*
1465 ASDCP::MXF::CreateObject(const Dictionary*& Dict, const UL& label)
1467 if ( ! s_TypesInit )
1469 Kumu::AutoMutex BlockLock(s_InitLock);
1471 if ( ! s_TypesInit )
1473 MXF::Metadata_InitTypes(Dict);
1478 FLi_t i = s_FactoryList.find(label.Value());
1480 if ( i == s_FactoryList.end() )
1481 return new InterchangeObject(Dict);
1483 return i->second(Dict);
1487 //------------------------------------------------------------------------------------------
1491 ASDCP::MXF::decode_mca_string(const std::string& s, const mca_label_map_t& labels, const Dictionary*& dict, const std::string& language,
1492 InterchangeObject_list_t& descriptor_list, ui32_t& channel_count)
1494 std::string symbol_buf;
1496 ASDCP::MXF::SoundfieldGroupLabelSubDescriptor *current_soundfield = 0, *prev_soundfield = 0;
1497 std::string::const_iterator i;
1499 for ( i = s.begin(); i != s.end(); ++i )
1503 if ( current_soundfield != 0 && symbol_buf.empty() )
1505 // appending to the existing soundfield group
1508 else if ( current_soundfield != 0 )
1510 DefaultLogSink().Error("Encountered '(', already processing a soundfield group.\n");
1513 else if ( symbol_buf.empty() )
1515 if ( prev_soundfield != 0 )
1517 current_soundfield = prev_soundfield;
1518 // appending to the existing soundfield group
1523 DefaultLogSink().Error("Encountered '(', without leading soundfield group symbol.\n");
1528 mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1530 if ( i == labels.end() )
1532 DefaultLogSink().Error("Unknown symbol: '%s'\n", symbol_buf.c_str());
1536 if ( i->second.ul.Value()[10] != 2 ) // magic depends on UL "Essence Facet" byte (see ST 428-12)
1538 DefaultLogSink().Error("Not a soundfield group symbol: '%s'\n", symbol_buf.c_str());
1542 current_soundfield = new ASDCP::MXF::SoundfieldGroupLabelSubDescriptor(dict);
1543 GenRandomValue(current_soundfield->MCALinkID);
1545 current_soundfield->MCATagSymbol = (i->second.requires_prefix ? "sg" : "") + i->first;
1546 current_soundfield->MCATagName = i->second.tag_name;
1547 current_soundfield->RFC5646SpokenLanguage = language;
1548 current_soundfield->MCALabelDictionaryID = i->second.ul;
1549 descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(current_soundfield));
1550 prev_soundfield = current_soundfield;
1553 else if ( *i == ')' )
1555 if ( current_soundfield == 0 )
1557 DefaultLogSink().Error("Encountered ')', not currently processing a soundfield group.\n");
1561 if ( symbol_buf.empty() )
1563 DefaultLogSink().Error("Soundfield group description contains no channels.\n");
1567 mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1569 if ( i == labels.end() )
1571 DefaultLogSink().Error("Unknown symbol: '%s'\n", symbol_buf.c_str());
1575 assert(current_soundfield);
1577 ASDCP::MXF::AudioChannelLabelSubDescriptor *channel_descr =
1578 new ASDCP::MXF::AudioChannelLabelSubDescriptor(dict);
1579 GenRandomValue(channel_descr->MCALinkID);
1581 channel_descr->SoundfieldGroupLinkID = current_soundfield->MCALinkID;
1582 channel_descr->MCAChannelID = channel_count++ + 1;
1583 channel_descr->MCATagSymbol = (i->second.requires_prefix ? "ch" : "") + i->first;
1584 channel_descr->MCATagName = i->second.tag_name;
1585 channel_descr->RFC5646SpokenLanguage = language;
1586 channel_descr->MCALabelDictionaryID = i->second.ul;
1587 descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(channel_descr));
1589 current_soundfield = 0;
1591 else if ( *i == ',' )
1593 if ( ! symbol_buf.empty() && ! symbol_buf.compare("-") )
1598 else if ( ! symbol_buf.empty() )
1600 mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1602 if ( i == labels.end() )
1604 DefaultLogSink().Error("Unknown symbol: '%s'\n", symbol_buf.c_str());
1608 if ( i->second.ul.Value()[10] != 1 ) // magic depends on UL "Essence Facet" byte (see ST 428-12)
1610 DefaultLogSink().Error("Not a channel symbol: '%s'\n", symbol_buf.c_str());
1614 ASDCP::MXF::AudioChannelLabelSubDescriptor *channel_descr =
1615 new ASDCP::MXF::AudioChannelLabelSubDescriptor(dict);
1616 GenRandomValue(channel_descr->MCALinkID);
1618 if ( current_soundfield != 0 )
1620 channel_descr->SoundfieldGroupLinkID = current_soundfield->MCALinkID;
1623 channel_descr->MCAChannelID = channel_count++ + 1;
1624 channel_descr->MCATagSymbol = (i->second.requires_prefix ? "ch" : "") + i->first;
1625 channel_descr->MCATagName = i->second.tag_name;
1626 channel_descr->RFC5646SpokenLanguage = language;
1627 channel_descr->MCALabelDictionaryID = i->second.ul;
1628 descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(channel_descr));
1632 else if ( *i == '-' || isalnum(*i) )
1636 else if ( ! isspace(*i) )
1638 DefaultLogSink().Error("Unexpected character '%c'.\n", *i);
1643 if ( ! symbol_buf.empty() && ! symbol_buf.compare("-") )
1647 else if ( ! symbol_buf.empty() )
1649 mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1651 if ( i == labels.end() )
1653 DefaultLogSink().Error("Unknown symbol: '%s'\n", symbol_buf.c_str());
1657 ASDCP::MXF::AudioChannelLabelSubDescriptor *channel_descr =
1658 new ASDCP::MXF::AudioChannelLabelSubDescriptor(dict);
1659 GenRandomValue(channel_descr->MCALinkID);
1661 if ( current_soundfield != 0 )
1663 channel_descr->SoundfieldGroupLinkID = current_soundfield->MCALinkID;
1666 channel_descr->MCAChannelID = channel_count++ + 1;
1667 channel_descr->MCATagSymbol = (i->second.requires_prefix ? "ch" : "") + i->first;
1668 channel_descr->MCATagName = i->second.tag_name;
1669 channel_descr->RFC5646SpokenLanguage = language;
1670 channel_descr->MCALabelDictionaryID = i->second.ul;
1671 descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(channel_descr));
1678 ASDCP::MXF::ASDCP_MCAConfigParser::ASDCP_MCAConfigParser(const Dictionary*& d) : m_Dict(d), m_ChannelCount(0)
1680 typedef mca_label_map_t::value_type pair;
1681 m_LabelMap.insert(pair("L", label_traits("Left" , true, m_Dict->ul(MDD_DCAudioChannel_L))));
1682 m_LabelMap.insert(pair("R", label_traits("Right" , true, m_Dict->ul(MDD_DCAudioChannel_R))));
1683 m_LabelMap.insert(pair("C", label_traits("Center" , true, m_Dict->ul(MDD_DCAudioChannel_C))));
1684 m_LabelMap.insert(pair("LFE", label_traits("LFE" , true, m_Dict->ul(MDD_DCAudioChannel_LFE))));
1685 m_LabelMap.insert(pair("Ls", label_traits("Left Surround" , true, m_Dict->ul(MDD_DCAudioChannel_Ls))));
1686 m_LabelMap.insert(pair("Rs", label_traits("Right Surround" , true, m_Dict->ul(MDD_DCAudioChannel_Rs))));
1687 m_LabelMap.insert(pair("Lss", label_traits("Left Side Surround" , true, m_Dict->ul(MDD_DCAudioChannel_Lss))));
1688 m_LabelMap.insert(pair("Rss", label_traits("Right Side Surround" , true, m_Dict->ul(MDD_DCAudioChannel_Rss))));
1689 m_LabelMap.insert(pair("Lrs", label_traits("Left Rear Surround" , true, m_Dict->ul(MDD_DCAudioChannel_Lrs))));
1690 m_LabelMap.insert(pair("Rrs", label_traits("Right Rear Surround" , true, m_Dict->ul(MDD_DCAudioChannel_Rrs))));
1691 m_LabelMap.insert(pair("Lc", label_traits("Left Center" , true, m_Dict->ul(MDD_DCAudioChannel_Lc))));
1692 m_LabelMap.insert(pair("Rc", label_traits("Right Center" , true, m_Dict->ul(MDD_DCAudioChannel_Rc))));
1693 m_LabelMap.insert(pair("Cs", label_traits("Center Surround" , true, m_Dict->ul(MDD_DCAudioChannel_Cs))));
1694 m_LabelMap.insert(pair("HI", label_traits("Hearing Impaired" , true, m_Dict->ul(MDD_DCAudioChannel_HI))));
1695 m_LabelMap.insert(pair("VIN", label_traits("Visually Impaired-Narrative" , true, m_Dict->ul(MDD_DCAudioChannel_VIN))));
1696 m_LabelMap.insert(pair("51", label_traits("5.1" , true, m_Dict->ul(MDD_DCAudioSoundfield_51))));
1697 m_LabelMap.insert(pair("71", label_traits("7.1DS" , true, m_Dict->ul(MDD_DCAudioSoundfield_71))));
1698 m_LabelMap.insert(pair("SDS", label_traits("7.1SDS" , true, m_Dict->ul(MDD_DCAudioSoundfield_SDS))));
1699 m_LabelMap.insert(pair("61", label_traits("6.1" , true, m_Dict->ul(MDD_DCAudioSoundfield_61))));
1700 m_LabelMap.insert(pair("M", label_traits("1.0 Monaural" , true, m_Dict->ul(MDD_DCAudioSoundfield_M))));
1701 m_LabelMap.insert(pair("DBOX", label_traits("D-BOX Motion Code Primary Stream" , false, m_Dict->ul(MDD_DBOXMotionCodePrimaryStream))));
1702 m_LabelMap.insert(pair("DBOX2", label_traits("D-BOX Motion Code Secondary Stream", false, m_Dict->ul(MDD_DBOXMotionCodeSecondaryStream))));
1707 ASDCP::MXF::ASDCP_MCAConfigParser::ChannelCount() const
1709 return m_ChannelCount;
1712 // 51(L,R,C,LFE,Ls,Rs),HI,VIN
1714 ASDCP::MXF::ASDCP_MCAConfigParser::DecodeString(const std::string& s, const std::string& language)
1716 return decode_mca_string(s, m_LabelMap, m_Dict, language, *this, m_ChannelCount);
1721 ASDCP::MXF::AS02_MCAConfigParser::AS02_MCAConfigParser(const Dictionary*& d) : ASDCP::MXF::ASDCP_MCAConfigParser(d)
1723 typedef mca_label_map_t::value_type pair;
1724 m_LabelMap.insert(pair("M1", label_traits("M1", true, m_Dict->ul(MDD_IMFAudioChannel_M1))));
1725 m_LabelMap.insert(pair("M2", label_traits("M2", true, m_Dict->ul(MDD_IMFAudioChannel_M2))));
1726 m_LabelMap.insert(pair("Lt", label_traits("Lt", true, m_Dict->ul(MDD_IMFAudioChannel_Lt))));
1727 m_LabelMap.insert(pair("Rt", label_traits("Rt", true, m_Dict->ul(MDD_IMFAudioChannel_Rt))));
1728 m_LabelMap.insert(pair("Lst", label_traits("Lst", true, m_Dict->ul(MDD_IMFAudioChannel_Lst))));
1729 m_LabelMap.insert(pair("Rst", label_traits("Rst", true, m_Dict->ul(MDD_IMFAudioChannel_Rst))));
1730 m_LabelMap.insert(pair("S", label_traits("S", true, m_Dict->ul(MDD_IMFAudioChannel_S))));
1731 m_LabelMap.insert(pair("ST", label_traits("ST", true, m_Dict->ul(MDD_IMFAudioSoundfield_ST))));
1732 m_LabelMap.insert(pair("DM", label_traits("DM", true, m_Dict->ul(MDD_IMFAudioSoundfield_DM))));
1733 m_LabelMap.insert(pair("DNS", label_traits("DNS", true, m_Dict->ul(MDD_IMFAudioSoundfield_DNS))));
1734 m_LabelMap.insert(pair("30", label_traits("30", true, m_Dict->ul(MDD_IMFAudioSoundfield_30))));
1735 m_LabelMap.insert(pair("40", label_traits("40", true, m_Dict->ul(MDD_IMFAudioSoundfield_40))));
1736 m_LabelMap.insert(pair("50", label_traits("50", true, m_Dict->ul(MDD_IMFAudioSoundfield_50))));
1737 m_LabelMap.insert(pair("60", label_traits("60", true, m_Dict->ul(MDD_IMFAudioSoundfield_60))));
1738 m_LabelMap.insert(pair("70", label_traits("70", true, m_Dict->ul(MDD_IMFAudioSoundfield_70))));
1739 m_LabelMap.insert(pair("LtRt", label_traits("LtRt",true, m_Dict->ul(MDD_IMFAudioSoundfield_LtRt))));
1740 m_LabelMap.insert(pair("51Ex", label_traits("51Ex",true, m_Dict->ul(MDD_IMFAudioSoundfield_51Ex))));
1741 m_LabelMap.insert(pair("HI", label_traits("HI", true, m_Dict->ul(MDD_IMFAudioSoundfield_HI))));
1742 m_LabelMap.insert(pair("VIN", label_traits("VIN", true, m_Dict->ul(MDD_IMFAudioSoundfield_VIN))));
1749 ASDCP::MXF::GetEditRateFromFP(ASDCP::MXF::OP1aHeader& header, ASDCP::Rational& edit_rate)
1751 bool has_first_item = false;
1753 MXF::InterchangeObject* temp_item;
1754 std::list<MXF::InterchangeObject*> temp_items;
1756 Result_t result = header.GetMDObjectsByType(DefaultCompositeDict().ul(MDD_SourcePackage), temp_items);
1758 if ( KM_FAILURE(result) )
1760 DefaultLogSink().Error("The MXF header does not contain a FilePackage item.\n");
1764 if ( temp_items.size() != 1 )
1766 DefaultLogSink().Error("The MXF header must contain one FilePackage item, found %d.\n", temp_items.size());
1771 MXF::Array<UUID>::const_iterator i;
1772 MXF::SourcePackage *source_package = dynamic_cast<MXF::SourcePackage*>(temp_items.front());
1773 assert(source_package);
1775 for ( i = source_package->Tracks.begin(); i != source_package->Tracks.end(); ++i )
1778 result = header.GetMDObjectByID(*i, &temp_item);
1780 if ( KM_FAILURE(result) )
1782 DefaultLogSink().Error("The MXF header is incomplete: strong reference %s leads nowhere.\n",
1783 i->EncodeHex(buf, 64));
1787 MXF::Track *track = dynamic_cast<MXF::Track*>(temp_item);
1791 DefaultLogSink().Error("The MXF header is incomplete: %s is not a Track item.\n",
1792 i->EncodeHex(buf, 64));
1797 result = header.GetMDObjectByID(track->Sequence, &temp_item);
1799 if ( KM_FAILURE(result) )
1801 DefaultLogSink().Error("The MXF header is incomplete: strong reference %s leads nowhere.\n",
1802 i->EncodeHex(buf, 64));
1806 MXF::Sequence *sequence = dynamic_cast<MXF::Sequence*>(temp_item);
1808 if ( sequence == 0 )
1810 DefaultLogSink().Error("The MXF header is incomplete: %s is not a Sequence item.\n",
1811 track->Sequence.get().EncodeHex(buf, 64));
1815 if ( sequence->StructuralComponents.size() != 1 )
1817 DefaultLogSink().Error("The Sequence item must contain one reference to an esence item, found %d.\n",
1818 sequence->StructuralComponents.size());
1823 result = header.GetMDObjectByID(sequence->StructuralComponents.front(), &temp_item);
1825 if ( KM_FAILURE(result) )
1827 DefaultLogSink().Error("The MXF header is incomplete: strong reference %s leads nowhere.\n",
1828 sequence->StructuralComponents.front().EncodeHex(buf, 64));
1832 if ( temp_item->IsA(DefaultCompositeDict().ul(MDD_SourceClip)) )
1834 MXF::SourceClip *source_clip = dynamic_cast<MXF::SourceClip*>(temp_item);
1836 if ( source_clip == 0 )
1838 DefaultLogSink().Error("The MXF header is incomplete: %s is not a SourceClip item.\n",
1839 sequence->StructuralComponents.front().EncodeHex(buf, 64));
1843 if ( ! has_first_item )
1845 edit_rate = track->EditRate;
1846 has_first_item = true;
1848 else if ( edit_rate != track->EditRate )
1850 DefaultLogSink().Error("The MXF header is incomplete: %s EditRate value does not match others in the file.\n",
1851 sequence->StructuralComponents.front().EncodeHex(buf, 64));
1855 else if ( ! temp_item->IsA(DefaultCompositeDict().ul(MDD_TimecodeComponent)) )
1857 DefaultLogSink().Error("Reference from Sequence to an unexpected type: %s.\n", temp_item->ObjectName());