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 < 1024 )
718 DefaultLogSink().Warn("Improbably small HeaderByteCount value: %qu\n", HeaderByteCount);
720 else if (HeaderByteCount > ( 4 * Kumu::Megabyte ) )
722 DefaultLogSink().Warn("Improbably huge HeaderByteCount value: %qu\n", HeaderByteCount);
725 result = m_HeaderData.Capacity(Kumu::xmin(4*Kumu::Megabyte, static_cast<ui32_t>(HeaderByteCount)));
727 if ( ASDCP_SUCCESS(result) )
730 result = Reader.Read(m_HeaderData.Data(), m_HeaderData.Capacity(), &read_count);
732 if ( ASDCP_FAILURE(result) )
734 DefaultLogSink().Error("OP1aHeader::InitFromFile, Read failed\n");
738 if ( read_count != m_HeaderData.Capacity() )
740 DefaultLogSink().Error("Short read of OP-Atom header metadata; wanted %u, got %u.\n",
741 m_HeaderData.Capacity(), read_count);
742 return RESULT_KLV_CODING(__LINE__, __FILE__);
746 if ( ASDCP_SUCCESS(result) )
747 result = InitFromBuffer(m_HeaderData.RoData(), m_HeaderData.Capacity());
754 ASDCP::MXF::OP1aHeader::InitFromPartitionBuffer(const byte_t* p, ui32_t l)
756 Result_t result = KLVPacket::InitFromBuffer(p, l);
758 if ( ASDCP_SUCCESS(result) )
759 result = Partition::InitFromBuffer(m_ValueStart, m_ValueLength); // test UL and OP
761 if ( ASDCP_SUCCESS(result) )
763 ui32_t pp_len = KLVPacket::PacketLength();
764 result = InitFromBuffer(p + pp_len, l - pp_len);
772 ASDCP::MXF::OP1aHeader::InitFromBuffer(const byte_t* p, ui32_t l)
775 Result_t result = RESULT_OK;
776 const byte_t* end_p = p + l;
778 while ( ASDCP_SUCCESS(result) && p < end_p )
780 // parse the packets and index them by uid, discard KLVFill items
781 InterchangeObject* object = CreateObject(m_Dict, p);
784 object->m_Lookup = &m_Primer;
785 result = object->InitFromBuffer(p, end_p - p);
787 const byte_t* redo_p = p;
788 p += object->PacketLength();
790 if ( ASDCP_SUCCESS(result) )
792 if ( object->IsA(m_Dict->ul(MDD_KLVFill)) )
798 DefaultLogSink().Error("Fill item short read: %d.\n", p - end_p);
801 else if ( object->IsA(m_Dict->ul(MDD_Primer)) ) // TODO: only one primer should be found
804 result = m_Primer.InitFromBuffer(redo_p, end_p - redo_p);
808 m_PacketList->AddPacket(object); // takes ownership
810 if ( object->IsA(m_Dict->ul(MDD_Preface)) && m_Preface == 0 )
811 m_Preface = (Preface*)object;
816 DefaultLogSink().Error("Error initializing OP1a header packet.\n");
817 // Kumu::hexdump(p-object->PacketLength(), object->PacketLength());
826 ASDCP::MXF::OP1aHeader::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
828 return m_PacketList->GetMDObjectByID(ObjectID, Object);
833 ASDCP::MXF::OP1aHeader::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
835 InterchangeObject* TmpObject;
840 return m_PacketList->GetMDObjectByType(ObjectID, Object);
845 ASDCP::MXF::OP1aHeader::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
847 return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
851 ASDCP::MXF::Identification*
852 ASDCP::MXF::OP1aHeader::GetIdentification()
854 InterchangeObject* Object;
856 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object)) )
857 return (Identification*)Object;
863 ASDCP::MXF::SourcePackage*
864 ASDCP::MXF::OP1aHeader::GetSourcePackage()
866 InterchangeObject* Object;
868 if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object)) )
869 return (SourcePackage*)Object;
876 ASDCP::MXF::OP1aHeader::WriteToFile(Kumu::FileWriter& Writer, ui32_t HeaderSize)
879 if ( m_Preface == 0 )
882 if ( HeaderSize < 4096 )
884 DefaultLogSink().Error("HeaderSize %u is too small. Must be >= 4096\n", HeaderSize);
888 ASDCP::FrameBuffer HeaderBuffer;
889 HeaderByteCount = HeaderSize - ArchiveSize();
890 assert (HeaderByteCount <= 0xFFFFFFFFL);
891 Result_t result = HeaderBuffer.Capacity((ui32_t) HeaderByteCount);
892 m_Preface->m_Lookup = &m_Primer;
894 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
895 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
897 InterchangeObject* object = *pl_i;
898 object->m_Lookup = &m_Primer;
900 ASDCP::FrameBuffer WriteWrapper;
901 WriteWrapper.SetData(HeaderBuffer.Data() + HeaderBuffer.Size(),
902 HeaderBuffer.Capacity() - HeaderBuffer.Size());
903 result = object->WriteToBuffer(WriteWrapper);
904 HeaderBuffer.Size(HeaderBuffer.Size() + WriteWrapper.Size());
907 if ( ASDCP_SUCCESS(result) )
909 UL TmpUL(m_Dict->ul(MDD_ClosedCompleteHeader));
910 result = Partition::WriteToFile(Writer, TmpUL);
913 if ( ASDCP_SUCCESS(result) )
914 result = m_Primer.WriteToFile(Writer);
916 if ( ASDCP_SUCCESS(result) )
919 Writer.Write(HeaderBuffer.RoData(), HeaderBuffer.Size(), &write_count);
920 assert(write_count == HeaderBuffer.Size());
924 if ( ASDCP_SUCCESS(result) )
926 Kumu::fpos_t pos = Writer.Tell();
928 if ( pos > (Kumu::fpos_t)HeaderByteCount )
930 char intbuf[IntBufferLen];
931 DefaultLogSink().Error("Header size %s exceeds specified value %u\n",
937 ASDCP::FrameBuffer NilBuf;
938 ui32_t klv_fill_length = HeaderSize - (ui32_t)pos;
940 if ( klv_fill_length < kl_length )
942 DefaultLogSink().Error("Remaining region too small for KLV Fill header\n");
946 klv_fill_length -= kl_length;
947 result = WriteKLToFile(Writer, m_Dict->ul(MDD_KLVFill), klv_fill_length);
949 if ( ASDCP_SUCCESS(result) )
950 result = NilBuf.Capacity(klv_fill_length);
952 if ( ASDCP_SUCCESS(result) )
954 memset(NilBuf.Data(), 0, klv_fill_length);
956 Writer.Write(NilBuf.RoData(), klv_fill_length, &write_count);
957 assert(write_count == klv_fill_length);
966 ASDCP::MXF::OP1aHeader::Dump(FILE* stream)
971 Partition::Dump(stream);
972 m_Primer.Dump(stream);
974 if ( m_Preface == 0 )
975 fputs("No Preface loaded\n", stream);
977 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
978 for ( ; i != m_PacketList->m_List.end(); i++ )
982 //------------------------------------------------------------------------------------------
985 ASDCP::MXF::OPAtomIndexFooter::OPAtomIndexFooter(const Dictionary*& d) :
986 Partition(d), m_Dict(d),
987 m_CurrentSegment(0), m_BytesPerEditUnit(0), m_BodySID(0),
988 m_ECOffset(0), m_Lookup(0)
994 ASDCP::MXF::OPAtomIndexFooter::~OPAtomIndexFooter() {}
998 ASDCP::MXF::OPAtomIndexFooter::InitFromFile(const Kumu::FileReader& Reader)
1000 Result_t result = Partition::InitFromFile(Reader); // test UL and OP
1002 // slurp up the remainder of the footer
1003 ui32_t read_count = 0;
1005 if ( ASDCP_SUCCESS(result) && IndexByteCount > 0 )
1007 assert (IndexByteCount <= 0xFFFFFFFFL);
1008 // At this point, m_FooterData may not have been initialized
1009 // so it's capacity is zero and data pointer is NULL
1010 // However, if IndexByteCount is zero then the capacity
1011 // doesn't change and the data pointer is not set.
1012 result = m_FooterData.Capacity((ui32_t) IndexByteCount);
1014 if ( ASDCP_SUCCESS(result) )
1015 result = Reader.Read(m_FooterData.Data(), m_FooterData.Capacity(), &read_count);
1017 if ( ASDCP_SUCCESS(result) && read_count != m_FooterData.Capacity() )
1019 DefaultLogSink().Error("Short read of footer partition: got %u, expecting %u\n",
1020 read_count, m_FooterData.Capacity());
1023 else if( ASDCP_SUCCESS(result) && !m_FooterData.Data() )
1025 DefaultLogSink().Error( "Buffer for footer partition not created: IndexByteCount = %u\n",
1030 if ( ASDCP_SUCCESS(result) )
1031 result = InitFromBuffer(m_FooterData.RoData(), m_FooterData.Capacity());
1039 ASDCP::MXF::OPAtomIndexFooter::InitFromPartitionBuffer(const byte_t* p, ui32_t l)
1041 Result_t result = KLVPacket::InitFromBuffer(p, l);
1043 if ( ASDCP_SUCCESS(result) )
1044 result = Partition::InitFromBuffer(m_ValueStart, m_ValueLength); // test UL and OP
1046 if ( ASDCP_SUCCESS(result) )
1048 ui32_t pp_len = KLVPacket::PacketLength();
1049 result = InitFromBuffer(p + pp_len, l - pp_len);
1057 ASDCP::MXF::OPAtomIndexFooter::InitFromBuffer(const byte_t* p, ui32_t l)
1059 Result_t result = RESULT_OK;
1060 const byte_t* end_p = p + l;
1062 while ( ASDCP_SUCCESS(result) && p < end_p )
1064 // parse the packets and index them by uid, discard KLVFill items
1065 InterchangeObject* object = CreateObject(m_Dict, p);
1068 object->m_Lookup = m_Lookup;
1069 result = object->InitFromBuffer(p, end_p - p);
1070 p += object->PacketLength();
1072 if ( ASDCP_SUCCESS(result) )
1074 m_PacketList->AddPacket(object); // takes ownership
1078 DefaultLogSink().Error("Error initializing OPAtom footer packet.\n");
1083 if ( ASDCP_FAILURE(result) )
1085 DefaultLogSink().Error("Failed to initialize OPAtomIndexFooter.\n");
1093 ASDCP::MXF::OPAtomIndexFooter::WriteToFile(Kumu::FileWriter& Writer, ui64_t duration)
1096 ASDCP::FrameBuffer FooterBuffer;
1097 ui32_t footer_size = m_PacketList->m_List.size() * MaxIndexSegmentSize; // segment-count * max-segment-size
1098 Result_t result = FooterBuffer.Capacity(footer_size);
1099 ui32_t iseg_count = 0;
1101 if ( m_CurrentSegment != 0 )
1103 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1104 m_CurrentSegment = 0;
1107 std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
1108 for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
1110 IndexTableSegment *segment = dynamic_cast<IndexTableSegment*>(*pl_i);
1115 if ( m_BytesPerEditUnit != 0 )
1117 if ( iseg_count != 1 )
1118 return RESULT_STATE;
1120 segment->IndexDuration = duration;
1124 InterchangeObject* object = *pl_i;
1125 object->m_Lookup = m_Lookup;
1127 ASDCP::FrameBuffer WriteWrapper;
1128 WriteWrapper.SetData(FooterBuffer.Data() + FooterBuffer.Size(),
1129 FooterBuffer.Capacity() - FooterBuffer.Size());
1130 result = object->WriteToBuffer(WriteWrapper);
1131 FooterBuffer.Size(FooterBuffer.Size() + WriteWrapper.Size());
1134 if ( ASDCP_SUCCESS(result) )
1136 IndexByteCount = FooterBuffer.Size();
1137 UL FooterUL(m_Dict->ul(MDD_CompleteFooter));
1138 result = Partition::WriteToFile(Writer, FooterUL);
1141 if ( ASDCP_SUCCESS(result) )
1143 ui32_t write_count = 0;
1144 result = Writer.Write(FooterBuffer.RoData(), FooterBuffer.Size(), &write_count);
1145 assert(write_count == FooterBuffer.Size());
1153 ASDCP::MXF::OPAtomIndexFooter::Dump(FILE* stream)
1158 Partition::Dump(stream);
1160 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
1161 for ( ; i != m_PacketList->m_List.end(); i++ )
1166 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
1168 return m_PacketList->GetMDObjectByID(ObjectID, Object);
1173 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
1175 InterchangeObject* TmpObject;
1178 Object = &TmpObject;
1180 return m_PacketList->GetMDObjectByType(ObjectID, Object);
1185 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
1187 return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
1192 ASDCP::MXF::OPAtomIndexFooter::Lookup(ui32_t frame_num, IndexTableSegment::IndexEntry& Entry) const
1194 std::list<InterchangeObject*>::iterator li;
1195 for ( li = m_PacketList->m_List.begin(); li != m_PacketList->m_List.end(); li++ )
1197 IndexTableSegment *segment = dynamic_cast<IndexTableSegment*>(*li);
1201 ui64_t start_pos = segment->IndexStartPosition;
1203 if ( segment->EditUnitByteCount > 0 )
1205 if ( m_PacketList->m_List.size() > 1 )
1206 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
1208 if ( ! segment->IndexEntryArray.empty() )
1209 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
1211 Entry.StreamOffset = (ui64_t)frame_num * segment->EditUnitByteCount;
1214 else if ( (ui64_t)frame_num >= start_pos
1215 && (ui64_t)frame_num < (start_pos + segment->IndexDuration) )
1217 ui64_t tmp = frame_num - start_pos;
1218 assert(tmp <= 0xFFFFFFFFL);
1220 if ( tmp < segment->IndexEntryArray.size() )
1222 Entry = segment->IndexEntryArray[(ui32_t) tmp];
1227 DefaultLogSink().Error("Malformed index table segment, IndexDuration does not match entries.\n");
1238 ASDCP::MXF::OPAtomIndexFooter::SetDeltaParams(const IndexTableSegment::DeltaEntry& delta)
1240 m_DefaultDeltaEntry = delta;
1245 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const Rational& Rate)
1249 m_BytesPerEditUnit = size;
1252 IndexTableSegment* Index = new IndexTableSegment(m_Dict);
1253 AddChildObject(Index);
1254 Index->EditUnitByteCount = m_BytesPerEditUnit;
1255 Index->IndexEditRate = Rate;
1260 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsVBR(IPrimerLookup* lookup, const Rational& Rate, Kumu::fpos_t offset)
1264 m_BytesPerEditUnit = 0;
1266 m_ECOffset = offset;
1271 ASDCP::MXF::OPAtomIndexFooter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
1273 if ( m_BytesPerEditUnit != 0 ) // are we CBR? that's bad
1275 DefaultLogSink().Error("Call to PushIndexEntry() failed: index is CBR\n");
1279 // do we have an available segment?
1280 if ( m_CurrentSegment == 0 )
1281 { // no, set up a new segment
1282 m_CurrentSegment = new IndexTableSegment(m_Dict);
1283 assert(m_CurrentSegment);
1284 AddChildObject(m_CurrentSegment);
1285 m_CurrentSegment->DeltaEntryArray.push_back(m_DefaultDeltaEntry);
1286 m_CurrentSegment->IndexEditRate = m_EditRate;
1287 m_CurrentSegment->IndexStartPosition = 0;
1289 else if ( m_CurrentSegment->IndexEntryArray.size() >= CBRIndexEntriesPerSegment )
1290 { // no, this one is full, start another
1291 m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1292 ui64_t StartPosition = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
1294 m_CurrentSegment = new IndexTableSegment(m_Dict);
1295 assert(m_CurrentSegment);
1296 AddChildObject(m_CurrentSegment);
1297 m_CurrentSegment->DeltaEntryArray.push_back(m_DefaultDeltaEntry);
1298 m_CurrentSegment->IndexEditRate = m_EditRate;
1299 m_CurrentSegment->IndexStartPosition = StartPosition;
1302 m_CurrentSegment->IndexEntryArray.push_back(Entry);
1305 //------------------------------------------------------------------------------------------
1310 ASDCP::MXF::InterchangeObject::Copy(const InterchangeObject& rhs)
1313 InstanceUID = rhs.InstanceUID;
1314 GenerationUID = rhs.GenerationUID;
1319 ASDCP::MXF::InterchangeObject::InitFromTLVSet(TLVReader& TLVSet)
1321 Result_t result = TLVSet.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID));
1322 if ( ASDCP_SUCCESS(result) )
1323 result = TLVSet.ReadObject(OBJ_READ_ARGS_OPT(GenerationInterchangeObject, GenerationUID));
1329 ASDCP::MXF::InterchangeObject::WriteToTLVSet(TLVWriter& TLVSet)
1331 Result_t result = TLVSet.WriteObject(OBJ_WRITE_ARGS(InterchangeObject, InstanceUID));
1332 if ( ASDCP_SUCCESS(result) )
1333 result = TLVSet.WriteObject(OBJ_WRITE_ARGS_OPT(GenerationInterchangeObject, GenerationUID));
1339 ASDCP::MXF::InterchangeObject::InitFromBuffer(const byte_t* p, ui32_t l)
1342 Result_t result = RESULT_FALSE;
1344 if ( m_UL.HasValue() )
1346 result = KLVPacket::InitFromBuffer(p, l, m_UL);
1348 if ( ASDCP_SUCCESS(result) )
1350 TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup);
1351 result = InitFromTLVSet(MemRDR);
1356 result = KLVPacket::InitFromBuffer(p, l);
1364 ASDCP::MXF::InterchangeObject::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
1366 if ( ! m_UL.HasValue() )
1367 return RESULT_STATE;
1369 TLVWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length, m_Lookup);
1370 Result_t result = WriteToTLVSet(MemWRT);
1372 if ( ASDCP_SUCCESS(result) )
1374 ui32_t packet_length = MemWRT.Length();
1375 result = WriteKLToBuffer(Buffer, packet_length);
1377 if ( ASDCP_SUCCESS(result) )
1378 Buffer.Size(Buffer.Size() + packet_length);
1386 ASDCP::MXF::InterchangeObject::Dump(FILE* stream)
1388 char identbuf[IdentBufferLen];
1390 fputc('\n', stream);
1391 KLVPacket::Dump(stream, *m_Dict, false);
1392 fprintf(stream, " InstanceUID = %s\n", InstanceUID.EncodeHex(identbuf, IdentBufferLen));
1394 if ( ! GenerationUID.empty() )
1395 fprintf(stream, " GenerationUID = %s\n", GenerationUID.get().EncodeHex(identbuf, IdentBufferLen));
1400 ASDCP::MXF::InterchangeObject::IsA(const byte_t* label)
1402 if ( m_KLLength == 0 || m_KeyStart == 0 )
1405 return ( memcmp(label, m_KeyStart, SMPTE_UL_LENGTH) == 0 );
1409 //------------------------------------------------------------------------------------------
1412 typedef std::map<ASDCP::UL, ASDCP::MXF::MXFObjectFactory_t>FactoryMap_t;
1413 typedef FactoryMap_t::iterator FLi_t;
1416 class FactoryList : public FactoryMap_t
1425 Kumu::AutoMutex BlockLock(m_Lock);
1429 FLi_t Find(const byte_t* label) {
1430 Kumu::AutoMutex BlockLock(m_Lock);
1435 Kumu::AutoMutex BlockLock(m_Lock);
1439 void Insert(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory) {
1440 Kumu::AutoMutex BlockLock(m_Lock);
1441 insert(FactoryList::value_type(label, factory));
1446 static FactoryList s_FactoryList;
1447 static Kumu::Mutex s_InitLock;
1448 static bool s_TypesInit = false;
1453 ASDCP::MXF::SetObjectFactory(const ASDCP::UL& label, ASDCP::MXF::MXFObjectFactory_t factory)
1455 s_FactoryList.Insert(label, factory);
1459 ASDCP::MXF::InterchangeObject*
1460 ASDCP::MXF::CreateObject(const Dictionary*& Dict, const UL& label)
1462 if ( ! s_TypesInit )
1464 Kumu::AutoMutex BlockLock(s_InitLock);
1466 if ( ! s_TypesInit )
1468 MXF::Metadata_InitTypes(Dict);
1473 FLi_t i = s_FactoryList.find(label.Value());
1475 if ( i == s_FactoryList.end() )
1476 return new InterchangeObject(Dict);
1478 return i->second(Dict);
1482 //------------------------------------------------------------------------------------------
1486 ASDCP::MXF::decode_mca_string(const std::string& s, const mca_label_map_t& labels, const Dictionary*& dict, const std::string& language,
1487 InterchangeObject_list_t& descriptor_list, ui32_t& channel_count)
1489 std::string symbol_buf;
1491 ASDCP::MXF::SoundfieldGroupLabelSubDescriptor *current_soundfield = 0;
1492 std::string::const_iterator i;
1494 for ( i = s.begin(); i != s.end(); ++i )
1498 if ( current_soundfield != 0 )
1500 DefaultLogSink().Error("Encountered '(', already processing a soundfield group.\n");
1504 if ( symbol_buf.empty() )
1506 DefaultLogSink().Error("Encountered '(', without leading soundfield group symbol.\n");
1510 mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1512 if ( i == labels.end() )
1514 DefaultLogSink().Error("Unknown symbol: '%s'\n", symbol_buf.c_str());
1518 if ( i->second.ul.Value()[10] != 2 ) // magic depends on UL "Essence Facet" byte (see ST 428-12)
1520 DefaultLogSink().Error("Not a soundfield group symbol: '%s'\n", symbol_buf.c_str());
1524 current_soundfield = new ASDCP::MXF::SoundfieldGroupLabelSubDescriptor(dict);
1525 GenRandomValue(current_soundfield->MCALinkID);
1527 current_soundfield->MCATagSymbol = (i->second.requires_prefix ? "sg" : "") + i->first;
1528 current_soundfield->MCATagName = i->second.tag_name;
1529 current_soundfield->RFC5646SpokenLanguage = language;
1530 current_soundfield->MCALabelDictionaryID = i->second.ul;
1531 descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(current_soundfield));
1534 else if ( *i == ')' )
1536 if ( current_soundfield == 0 )
1538 DefaultLogSink().Error("Encountered ')', not currently processing a soundfield group.\n");
1542 if ( symbol_buf.empty() )
1544 DefaultLogSink().Error("Soundfield group description contains no channels.\n");
1548 mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1550 if ( i == labels.end() )
1552 DefaultLogSink().Error("Unknown symbol: '%s'\n", symbol_buf.c_str());
1556 assert(current_soundfield);
1558 ASDCP::MXF::AudioChannelLabelSubDescriptor *channel_descr =
1559 new ASDCP::MXF::AudioChannelLabelSubDescriptor(dict);
1560 GenRandomValue(channel_descr->MCALinkID);
1562 channel_descr->SoundfieldGroupLinkID = current_soundfield->MCALinkID;
1563 channel_descr->MCAChannelID = channel_count++ + 1;
1564 channel_descr->MCATagSymbol = (i->second.requires_prefix ? "ch" : "") + i->first;
1565 channel_descr->MCATagName = i->second.tag_name;
1566 channel_descr->RFC5646SpokenLanguage = language;
1567 channel_descr->MCALabelDictionaryID = i->second.ul;
1568 descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(channel_descr));
1570 current_soundfield = 0;
1572 else if ( *i == ',' )
1574 if ( ! symbol_buf.empty() && ! symbol_buf.compare("-") )
1579 else if ( ! symbol_buf.empty() )
1581 mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1583 if ( i == labels.end() )
1585 DefaultLogSink().Error("Unknown symbol: '%s'\n", symbol_buf.c_str());
1589 if ( i->second.ul.Value()[10] != 1 ) // magic depends on UL "Essence Facet" byte (see ST 428-12)
1591 DefaultLogSink().Error("Not a channel symbol: '%s'\n", symbol_buf.c_str());
1595 ASDCP::MXF::AudioChannelLabelSubDescriptor *channel_descr =
1596 new ASDCP::MXF::AudioChannelLabelSubDescriptor(dict);
1597 GenRandomValue(channel_descr->MCALinkID);
1599 if ( current_soundfield != 0 )
1601 channel_descr->SoundfieldGroupLinkID = current_soundfield->MCALinkID;
1604 channel_descr->MCAChannelID = channel_count++ + 1;
1605 channel_descr->MCATagSymbol = (i->second.requires_prefix ? "ch" : "") + i->first;
1606 channel_descr->MCATagName = i->second.tag_name;
1607 channel_descr->RFC5646SpokenLanguage = language;
1608 channel_descr->MCALabelDictionaryID = i->second.ul;
1609 descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(channel_descr));
1613 else if ( *i == '-' || isalnum(*i) )
1617 else if ( ! isspace(*i) )
1619 DefaultLogSink().Error("Unexpected character '%c'.\n", *i);
1624 if ( ! symbol_buf.empty() && ! symbol_buf.compare("-") )
1628 else if ( ! symbol_buf.empty() )
1630 mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1632 if ( i == labels.end() )
1634 DefaultLogSink().Error("Unknown symbol: '%s'\n", symbol_buf.c_str());
1638 ASDCP::MXF::AudioChannelLabelSubDescriptor *channel_descr =
1639 new ASDCP::MXF::AudioChannelLabelSubDescriptor(dict);
1640 GenRandomValue(channel_descr->MCALinkID);
1642 if ( current_soundfield != 0 )
1644 channel_descr->SoundfieldGroupLinkID = current_soundfield->MCALinkID;
1647 channel_descr->MCAChannelID = channel_count++ + 1;
1648 channel_descr->MCATagSymbol = (i->second.requires_prefix ? "ch" : "") + i->first;
1649 channel_descr->MCATagName = i->second.tag_name;
1650 channel_descr->RFC5646SpokenLanguage = language;
1651 channel_descr->MCALabelDictionaryID = i->second.ul;
1652 descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(channel_descr));
1659 ASDCP::MXF::ASDCP_MCAConfigParser::ASDCP_MCAConfigParser(const Dictionary*& d) : m_Dict(d), m_ChannelCount(0)
1661 typedef mca_label_map_t::value_type pair;
1662 m_LabelMap.insert(pair("L", label_traits("Left" , true, m_Dict->ul(MDD_DCAudioChannel_L))));
1663 m_LabelMap.insert(pair("R", label_traits("Right" , true, m_Dict->ul(MDD_DCAudioChannel_R))));
1664 m_LabelMap.insert(pair("C", label_traits("Center" , true, m_Dict->ul(MDD_DCAudioChannel_C))));
1665 m_LabelMap.insert(pair("LFE", label_traits("LFE" , true, m_Dict->ul(MDD_DCAudioChannel_LFE))));
1666 m_LabelMap.insert(pair("Ls", label_traits("Left Surround" , true, m_Dict->ul(MDD_DCAudioChannel_Ls))));
1667 m_LabelMap.insert(pair("Rs", label_traits("Right Surround" , true, m_Dict->ul(MDD_DCAudioChannel_Rs))));
1668 m_LabelMap.insert(pair("Lss", label_traits("Left Side Surround" , true, m_Dict->ul(MDD_DCAudioChannel_Lss))));
1669 m_LabelMap.insert(pair("Rss", label_traits("Right Side Surround" , true, m_Dict->ul(MDD_DCAudioChannel_Rss))));
1670 m_LabelMap.insert(pair("Lrs", label_traits("Left Rear Surround" , true, m_Dict->ul(MDD_DCAudioChannel_Lrs))));
1671 m_LabelMap.insert(pair("Rrs", label_traits("Right Rear Surround" , true, m_Dict->ul(MDD_DCAudioChannel_Rrs))));
1672 m_LabelMap.insert(pair("Lc", label_traits("Left Center" , true, m_Dict->ul(MDD_DCAudioChannel_Lc))));
1673 m_LabelMap.insert(pair("Rc", label_traits("Right Center" , true, m_Dict->ul(MDD_DCAudioChannel_Rc))));
1674 m_LabelMap.insert(pair("Cs", label_traits("Center Surround" , true, m_Dict->ul(MDD_DCAudioChannel_Cs))));
1675 m_LabelMap.insert(pair("HI", label_traits("Hearing Impaired" , true, m_Dict->ul(MDD_DCAudioChannel_HI))));
1676 m_LabelMap.insert(pair("VIN", label_traits("Visually Impaired-Narrative" , true, m_Dict->ul(MDD_DCAudioChannel_VIN))));
1677 m_LabelMap.insert(pair("51", label_traits("5.1" , true, m_Dict->ul(MDD_DCAudioSoundfield_51))));
1678 m_LabelMap.insert(pair("71", label_traits("7.1DS" , true, m_Dict->ul(MDD_DCAudioSoundfield_71))));
1679 m_LabelMap.insert(pair("SDS", label_traits("7.1SDS" , true, m_Dict->ul(MDD_DCAudioSoundfield_SDS))));
1680 m_LabelMap.insert(pair("61", label_traits("6.1" , true, m_Dict->ul(MDD_DCAudioSoundfield_61))));
1681 m_LabelMap.insert(pair("M", label_traits("1.0 Monaural" , true, m_Dict->ul(MDD_DCAudioSoundfield_M))));
1682 m_LabelMap.insert(pair("DBOX", label_traits("D-BOX Motion Code Primary Stream" , false, m_Dict->ul(MDD_DBOXMotionCodePrimaryStream))));
1683 m_LabelMap.insert(pair("DBOX2", label_traits("D-BOX Motion Code Secondary Stream", false, m_Dict->ul(MDD_DBOXMotionCodeSecondaryStream))));
1688 ASDCP::MXF::ASDCP_MCAConfigParser::ChannelCount() const
1690 return m_ChannelCount;
1693 // 51(L,R,C,LFE,Ls,Rs),HI,VIN
1695 ASDCP::MXF::ASDCP_MCAConfigParser::DecodeString(const std::string& s, const std::string& language)
1697 return decode_mca_string(s, m_LabelMap, m_Dict, language, *this, m_ChannelCount);
1702 ASDCP::MXF::AS02_MCAConfigParser::AS02_MCAConfigParser(const Dictionary*& d) : ASDCP::MXF::ASDCP_MCAConfigParser(d)
1704 typedef mca_label_map_t::value_type pair;
1705 m_LabelMap.insert(pair("M1", label_traits("M1", true, m_Dict->ul(MDD_IMFAudioChannel_M1))));
1706 m_LabelMap.insert(pair("M2", label_traits("M2", true, m_Dict->ul(MDD_IMFAudioChannel_M2))));
1707 m_LabelMap.insert(pair("Lt", label_traits("Lt", true, m_Dict->ul(MDD_IMFAudioChannel_Lt))));
1708 m_LabelMap.insert(pair("Rt", label_traits("Rt", true, m_Dict->ul(MDD_IMFAudioChannel_Rt))));
1709 m_LabelMap.insert(pair("Lst", label_traits("Lst", true, m_Dict->ul(MDD_IMFAudioChannel_Lst))));
1710 m_LabelMap.insert(pair("Rst", label_traits("Rst", true, m_Dict->ul(MDD_IMFAudioChannel_Rst))));
1711 m_LabelMap.insert(pair("S", label_traits("S", true, m_Dict->ul(MDD_IMFAudioChannel_S))));
1712 m_LabelMap.insert(pair("ST", label_traits("ST", true, m_Dict->ul(MDD_IMFAudioSoundfield_ST))));
1713 m_LabelMap.insert(pair("DM", label_traits("DM", true, m_Dict->ul(MDD_IMFAudioSoundfield_DM))));
1714 m_LabelMap.insert(pair("DNS", label_traits("DNS", true, m_Dict->ul(MDD_IMFAudioSoundfield_DNS))));
1715 m_LabelMap.insert(pair("30", label_traits("30", true, m_Dict->ul(MDD_IMFAudioSoundfield_30))));
1716 m_LabelMap.insert(pair("40", label_traits("40", true, m_Dict->ul(MDD_IMFAudioSoundfield_40))));
1717 m_LabelMap.insert(pair("50", label_traits("50", true, m_Dict->ul(MDD_IMFAudioSoundfield_50))));
1718 m_LabelMap.insert(pair("60", label_traits("60", true, m_Dict->ul(MDD_IMFAudioSoundfield_60))));
1719 m_LabelMap.insert(pair("70", label_traits("70", true, m_Dict->ul(MDD_IMFAudioSoundfield_70))));
1720 m_LabelMap.insert(pair("LtRt", label_traits("LtRt",true, m_Dict->ul(MDD_IMFAudioSoundfield_LtRt))));
1721 m_LabelMap.insert(pair("51Ex", label_traits("51Ex",true, m_Dict->ul(MDD_IMFAudioSoundfield_51Ex))));
1722 m_LabelMap.insert(pair("HI", label_traits("HI", true, m_Dict->ul(MDD_IMFAudioSoundfield_HI))));
1723 m_LabelMap.insert(pair("VIN", label_traits("VIN", true, m_Dict->ul(MDD_IMFAudioSoundfield_VIN))));
1730 ASDCP::MXF::GetEditRateFromFP(ASDCP::MXF::OP1aHeader& header, ASDCP::Rational& edit_rate)
1732 bool has_first_item = false;
1734 MXF::InterchangeObject* temp_item;
1735 std::list<MXF::InterchangeObject*> temp_items;
1737 Result_t result = header.GetMDObjectsByType(DefaultCompositeDict().ul(MDD_SourcePackage), temp_items);
1739 if ( KM_FAILURE(result) )
1741 DefaultLogSink().Error("The MXF header does not contain a FilePackage item.\n");
1745 if ( temp_items.size() != 1 )
1747 DefaultLogSink().Error("The MXF header must contain one FilePackage item, found %d.\n", temp_items.size());
1752 MXF::Array<UUID>::const_iterator i;
1753 MXF::SourcePackage *source_package = dynamic_cast<MXF::SourcePackage*>(temp_items.front());
1754 assert(source_package);
1756 for ( i = source_package->Tracks.begin(); i != source_package->Tracks.end(); ++i )
1759 result = header.GetMDObjectByID(*i, &temp_item);
1761 if ( KM_FAILURE(result) )
1763 DefaultLogSink().Error("The MXF header is incomplete: strong reference %s leads nowhere.\n",
1764 i->EncodeHex(buf, 64));
1768 MXF::Track *track = dynamic_cast<MXF::Track*>(temp_item);
1772 DefaultLogSink().Error("The MXF header is incomplete: %s is not a Track item.\n",
1773 i->EncodeHex(buf, 64));
1778 result = header.GetMDObjectByID(track->Sequence, &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::Sequence *sequence = dynamic_cast<MXF::Sequence*>(temp_item);
1789 if ( sequence == 0 )
1791 DefaultLogSink().Error("The MXF header is incomplete: %s is not a Sequence item.\n",
1792 track->Sequence.get().EncodeHex(buf, 64));
1796 if ( sequence->StructuralComponents.size() != 1 )
1798 DefaultLogSink().Error("The Sequence item must contain one reference to an esence item, found %d.\n",
1799 sequence->StructuralComponents.size());
1804 result = header.GetMDObjectByID(sequence->StructuralComponents.front(), &temp_item);
1806 if ( KM_FAILURE(result) )
1808 DefaultLogSink().Error("The MXF header is incomplete: strong reference %s leads nowhere.\n",
1809 sequence->StructuralComponents.front().EncodeHex(buf, 64));
1813 if ( temp_item->IsA(DefaultCompositeDict().ul(MDD_SourceClip)) )
1815 MXF::SourceClip *source_clip = dynamic_cast<MXF::SourceClip*>(temp_item);
1817 if ( source_clip == 0 )
1819 DefaultLogSink().Error("The MXF header is incomplete: %s is not a SourceClip item.\n",
1820 sequence->StructuralComponents.front().EncodeHex(buf, 64));
1824 if ( ! has_first_item )
1826 edit_rate = track->EditRate;
1827 has_first_item = true;
1829 else if ( edit_rate != track->EditRate )
1831 DefaultLogSink().Error("The MXF header is incomplete: %s EditRate value does not match others in the file.\n",
1832 sequence->StructuralComponents.front().EncodeHex(buf, 64));
1836 else if ( ! temp_item->IsA(DefaultCompositeDict().ul(MDD_TimecodeComponent)) )
1838 DefaultLogSink().Error("Reference from Sequence to an unexpected type: %s.\n", temp_item->ObjectName());