Fix crashes from AFL run
[asdcplib.git] / src / MXF.cpp
1 /*
2 Copyright (c) 2005-2015, John Hurst
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
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.
15
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.
26 */
27 /*! \file    MXF.cpp
28     \version $Id$
29     \brief   MXF objects
30 */
31
32 #include "MXF.h"
33 #include "Metadata.h"
34 #include <KM_log.h>
35
36 using Kumu::DefaultLogSink;
37 using Kumu::GenRandomValue;
38
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;
42
43 //------------------------------------------------------------------------------------------
44 //
45
46 //
47 ASDCP::Result_t
48 ASDCP::MXF::SeekToRIP(const Kumu::FileReader& Reader)
49 {
50   Kumu::fpos_t end_pos;
51
52   // go to the end - 4 bytes
53   Result_t result = Reader.Seek(0, Kumu::SP_END);
54
55   if ( ASDCP_SUCCESS(result) )
56     result = Reader.Tell(&end_pos);
57
58   if ( ASDCP_SUCCESS(result)
59        && end_pos < (SMPTE_UL_LENGTH+MXF_BER_LENGTH) )
60     {
61       DefaultLogSink().Error("File is smaller than an empty KLV packet.\n");
62       result = RESULT_FAIL;
63     }
64
65   if ( ASDCP_SUCCESS(result) )
66     result = Reader.Seek(end_pos - 4);
67
68   // get the ui32_t RIP length
69   ui32_t read_count;
70   byte_t intbuf[MXF_BER_LENGTH];
71   ui32_t rip_size = 0;
72
73   if ( ASDCP_SUCCESS(result) )
74     {
75       result = Reader.Read(intbuf, MXF_BER_LENGTH, &read_count);
76
77       if ( ASDCP_SUCCESS(result) && read_count != 4 )
78         {
79           DefaultLogSink().Error("RIP contains fewer than four bytes.\n");
80           result = RESULT_FAIL;
81         }
82     }
83
84   if ( ASDCP_SUCCESS(result) )
85     {
86       rip_size = KM_i32_BE(Kumu::cp2i<ui32_t>(intbuf));
87
88       if ( rip_size > end_pos ) // RIP can't be bigger than the file
89         {
90           DefaultLogSink().Error("RIP size impossibly large.\n");
91           return RESULT_FAIL;
92         }
93     }
94
95   // reposition to start of RIP
96   if ( ASDCP_SUCCESS(result) )
97     result = Reader.Seek(end_pos - rip_size);
98
99   return result;
100 }
101
102 //
103 bool
104 ASDCP::MXF::RIP::GetPairBySID(ui32_t SID, PartitionPair& outPair) const
105 {
106   RIP::const_pair_iterator i;
107   for ( i = PairArray.begin(); i != PairArray.end(); ++i )
108     {
109       if ( i->BodySID == SID )
110         {
111           outPair = *i;
112           return true;
113         }
114     }
115
116   return false;
117 }
118
119 //
120 ASDCP::Result_t
121 ASDCP::MXF::RIP::InitFromFile(const Kumu::FileReader& Reader)
122 {
123   assert(m_Dict);
124   Result_t result = KLVFilePacket::InitFromFile(Reader, m_Dict->ul(MDD_RandomIndexMetadata));
125
126   if ( ASDCP_SUCCESS(result) )
127     {
128       if (m_ValueLength < 4)
129       {
130         DefaultLogSink().Error("RIP is too short.\n");
131         return RESULT_FAIL;
132       }
133       Kumu::MemIOReader MemRDR(m_ValueStart, m_ValueLength - 4);
134       result = PairArray.Unarchive(&MemRDR) ? RESULT_OK : RESULT_KLV_CODING(__LINE__, __FILE__);
135     }
136
137   if ( ASDCP_FAILURE(result) )
138     DefaultLogSink().Error("Failed to initialize RIP.\n");
139
140   return result;
141 }
142
143 //
144 ASDCP::Result_t
145 ASDCP::MXF::RIP::WriteToFile(Kumu::FileWriter& Writer)
146 {
147   assert(m_Dict);
148   ASDCP::FrameBuffer Buffer;
149   ui32_t RIPSize = ( PairArray.size() * (sizeof(ui32_t) + sizeof(ui64_t)) ) + 4;
150   Result_t result = Buffer.Capacity(RIPSize);
151
152   if ( ASDCP_SUCCESS(result) )
153     result = WriteKLToFile(Writer, m_Dict->ul(MDD_RandomIndexMetadata), RIPSize);
154
155   if ( ASDCP_SUCCESS(result) )
156     {
157       result = RESULT_KLV_CODING(__LINE__, __FILE__);
158
159       Kumu::MemIOWriter MemWRT(Buffer.Data(), Buffer.Capacity());
160       if ( PairArray.Archive(&MemWRT) )
161         if ( MemWRT.WriteUi32BE(RIPSize + 20) )
162           {
163             Buffer.Size(MemWRT.Length());
164             result = RESULT_OK;
165           }
166     }
167
168   if ( ASDCP_SUCCESS(result) )
169     result = Writer.Write(Buffer.RoData(), Buffer.Size());
170
171   return result;
172 }
173
174 //
175 void
176 ASDCP::MXF::RIP::Dump(FILE* stream)
177 {
178   if ( stream == 0 )
179     stream = stderr;
180
181   KLVFilePacket::Dump(stream, *m_Dict, false);
182   PairArray.Dump(stream, false);
183 }
184
185 //------------------------------------------------------------------------------------------
186 //
187
188 //
189 ASDCP::MXF::Partition::PacketList::~PacketList() {
190   while ( ! m_List.empty() )
191     {
192       delete m_List.back();
193       m_List.pop_back();
194     }
195 }
196
197 //
198 void
199 ASDCP::MXF::Partition::PacketList::AddPacket(InterchangeObject* ThePacket) // takes ownership
200 {
201   assert(ThePacket);
202   m_Map.insert(std::map<UUID, InterchangeObject*>::value_type(ThePacket->InstanceUID, ThePacket));
203   m_List.push_back(ThePacket);
204 }
205
206 //
207 ASDCP::Result_t
208 ASDCP::MXF::Partition::PacketList::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
209 {
210   ASDCP_TEST_NULL(Object);
211
212   std::map<UUID, InterchangeObject*>::iterator mi = m_Map.find(ObjectID);
213   
214   if ( mi == m_Map.end() )
215     {
216       *Object = 0;
217       return RESULT_FAIL;
218     }
219
220   *Object = (*mi).second;
221   return RESULT_OK;
222 }
223
224 //
225 ASDCP::Result_t
226 ASDCP::MXF::Partition::PacketList::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
227 {
228   ASDCP_TEST_NULL(ObjectID);
229   ASDCP_TEST_NULL(Object);
230   std::list<InterchangeObject*>::iterator li;
231   *Object = 0;
232
233   for ( li = m_List.begin(); li != m_List.end(); li++ )
234     {
235       if ( (*li)->HasUL(ObjectID) )
236         {
237           *Object = *li;
238           return RESULT_OK;
239         }
240     }
241
242   return RESULT_FAIL;
243 }
244
245 //
246 ASDCP::Result_t
247 ASDCP::MXF::Partition::PacketList::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
248 {
249   ASDCP_TEST_NULL(ObjectID);
250   std::list<InterchangeObject*>::iterator li;
251
252   for ( li = m_List.begin(); li != m_List.end(); li++ )
253     {
254       if ( (*li)->HasUL(ObjectID) )
255         ObjectList.push_back(*li);
256     }
257
258   return ObjectList.empty() ? RESULT_FAIL : RESULT_OK;
259 }
260
261 //------------------------------------------------------------------------------------------
262 //
263
264
265 ASDCP::MXF::Partition::Partition(const Dictionary*& d) :
266   m_Dict(d),
267   MajorVersion(1), MinorVersion(2),
268   KAGSize(1), ThisPartition(0), PreviousPartition(0),
269   FooterPartition(0), HeaderByteCount(0), IndexByteCount(0), IndexSID(0),
270   BodyOffset(0), BodySID(0)
271 {
272   m_PacketList = new PacketList;
273 }
274
275 ASDCP::MXF::Partition::~Partition()
276 {
277 }
278
279 // takes ownership
280 void
281 ASDCP::MXF::Partition::AddChildObject(InterchangeObject* Object)
282 {
283   assert(Object);
284
285   if ( ! Object->InstanceUID.HasValue() )
286     GenRandomValue(Object->InstanceUID);
287
288   m_PacketList->AddPacket(Object);
289 }
290
291 //
292 ASDCP::Result_t
293 ASDCP::MXF::Partition::InitFromFile(const Kumu::FileReader& Reader)
294 {
295   Result_t result = KLVFilePacket::InitFromFile(Reader);
296   // test the UL
297   // could be one of several values
298   if ( ASDCP_SUCCESS(result) )
299     result = ASDCP::MXF::Partition::InitFromBuffer(m_ValueStart, m_ValueLength);
300   
301   return result;
302 }
303
304 //
305 ASDCP::Result_t
306 ASDCP::MXF::Partition::InitFromBuffer(const byte_t* p, ui32_t l)
307 {
308   Kumu::MemIOReader MemRDR(p, l);
309   Result_t result = RESULT_KLV_CODING(__LINE__, __FILE__);
310
311   if ( MemRDR.ReadUi16BE(&MajorVersion) )
312     if ( MemRDR.ReadUi16BE(&MinorVersion) )
313       if ( MemRDR.ReadUi32BE(&KAGSize) )
314         if ( MemRDR.ReadUi64BE(&ThisPartition) )
315           if ( MemRDR.ReadUi64BE(&PreviousPartition) )
316             if ( MemRDR.ReadUi64BE(&FooterPartition) )
317               if ( MemRDR.ReadUi64BE(&HeaderByteCount) )
318                 if ( MemRDR.ReadUi64BE(&IndexByteCount) )
319                   if ( MemRDR.ReadUi32BE(&IndexSID) )
320                     if ( MemRDR.ReadUi64BE(&BodyOffset) )
321                       if ( MemRDR.ReadUi32BE(&BodySID) )
322                         if ( OperationalPattern.Unarchive(&MemRDR) )
323                           if ( EssenceContainers.Unarchive(&MemRDR) )
324                             result = RESULT_OK;
325
326   if ( ASDCP_FAILURE(result) )
327     DefaultLogSink().Error("Failed to initialize Partition.\n");
328
329   return result;
330 }
331
332 //
333 ASDCP::Result_t
334 ASDCP::MXF::Partition::WriteToFile(Kumu::FileWriter& Writer, UL& PartitionLabel)
335 {
336   ASDCP::FrameBuffer Buffer;
337   Result_t result = Buffer.Capacity(1024);
338
339   if ( ASDCP_SUCCESS(result) )
340     {
341       Kumu::MemIOWriter MemWRT(Buffer.Data(), Buffer.Capacity());
342       result = RESULT_KLV_CODING(__LINE__, __FILE__);
343       if ( MemWRT.WriteUi16BE(MajorVersion) )
344         if ( MemWRT.WriteUi16BE(MinorVersion) )
345           if ( MemWRT.WriteUi32BE(KAGSize) )
346             if ( MemWRT.WriteUi64BE(ThisPartition) )
347               if ( MemWRT.WriteUi64BE(PreviousPartition) )
348                 if ( MemWRT.WriteUi64BE(FooterPartition) )
349                   if ( MemWRT.WriteUi64BE(HeaderByteCount) )
350                     if ( MemWRT.WriteUi64BE(IndexByteCount) )
351                       if ( MemWRT.WriteUi32BE(IndexSID) )
352                         if ( MemWRT.WriteUi64BE(BodyOffset) )
353                           if ( MemWRT.WriteUi32BE(BodySID) )
354                             if ( OperationalPattern.Archive(&MemWRT) )
355                               if ( EssenceContainers.Archive(&MemWRT) )
356                                 {
357                                   Buffer.Size(MemWRT.Length());
358                                   result = RESULT_OK;
359                                 }
360     }
361
362   if ( ASDCP_SUCCESS(result) )
363     {
364       ui32_t write_count;
365       result = WriteKLToFile(Writer, PartitionLabel.Value(), Buffer.Size());
366
367       if ( ASDCP_SUCCESS(result) )
368         result = Writer.Write(Buffer.RoData(), Buffer.Size(), &write_count);
369     }
370
371   return result;
372 }
373
374 //
375 ui32_t
376 ASDCP::MXF::Partition::ArchiveSize()
377 {
378   return ( kl_length
379            + sizeof(ui16_t) + sizeof(ui16_t)
380            + sizeof(ui32_t)
381            + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t)
382            + sizeof(ui32_t)
383            + sizeof(ui64_t)
384            + sizeof(ui32_t)
385            + SMPTE_UL_LENGTH
386            + sizeof(ui32_t) + sizeof(ui32_t) + ( UUIDlen * EssenceContainers.size() ) );
387 }
388
389 //
390 void
391 ASDCP::MXF::Partition::Dump(FILE* stream)
392 {
393   char identbuf[IdentBufferLen];
394
395   if ( stream == 0 )
396     stream = stderr;
397
398   KLVFilePacket::Dump(stream, *m_Dict, false);
399   fprintf(stream, "  MajorVersion       = %hu\n", MajorVersion);
400   fprintf(stream, "  MinorVersion       = %hu\n", MinorVersion);
401   fprintf(stream, "  KAGSize            = %u\n",  KAGSize);
402   fprintf(stream, "  ThisPartition      = %s\n",  ui64sz(ThisPartition, identbuf));
403   fprintf(stream, "  PreviousPartition  = %s\n",  ui64sz(PreviousPartition, identbuf));
404   fprintf(stream, "  FooterPartition    = %s\n",  ui64sz(FooterPartition, identbuf));
405   fprintf(stream, "  HeaderByteCount    = %s\n",  ui64sz(HeaderByteCount, identbuf));
406   fprintf(stream, "  IndexByteCount     = %s\n",  ui64sz(IndexByteCount, identbuf));
407   fprintf(stream, "  IndexSID           = %u\n",  IndexSID);
408   fprintf(stream, "  BodyOffset         = %s\n",  ui64sz(BodyOffset, identbuf));
409   fprintf(stream, "  BodySID            = %u\n",  BodySID);
410   fprintf(stream, "  OperationalPattern = %s\n",  OperationalPattern.EncodeString(identbuf, IdentBufferLen));
411   fputs("Essence Containers:\n", stream); EssenceContainers.Dump(stream);
412 }
413
414
415 //------------------------------------------------------------------------------------------
416 //
417
418 class ASDCP::MXF::Primer::h__PrimerLookup : public std::map<UL, TagValue>
419 {
420 public:
421   void InitWithBatch(ASDCP::MXF::Batch<ASDCP::MXF::Primer::LocalTagEntry>& Batch)
422   {
423     ASDCP::MXF::Batch<ASDCP::MXF::Primer::LocalTagEntry>::iterator i = Batch.begin();
424
425     for ( ; i != Batch.end(); i++ )
426       insert(std::map<UL, TagValue>::value_type((*i).UL, (*i).Tag));
427   }
428 };
429
430
431 //
432 ASDCP::MXF::Primer::Primer(const Dictionary*& d) : m_LocalTag(0xff), m_Dict(d) {
433   m_UL = m_Dict->ul(MDD_Primer);
434 }
435
436 //
437 ASDCP::MXF::Primer::~Primer() {}
438
439 //
440 void
441 ASDCP::MXF::Primer::ClearTagList()
442 {
443   LocalTagEntryBatch.clear();
444   m_Lookup = new h__PrimerLookup;
445 }
446
447 //
448 ASDCP::Result_t
449 ASDCP::MXF::Primer::InitFromBuffer(const byte_t* p, ui32_t l)
450 {
451   assert(m_Dict);
452   Result_t result = KLVPacket::InitFromBuffer(p, l, m_Dict->ul(MDD_Primer));
453
454   if ( ASDCP_SUCCESS(result) )
455     {
456       if (m_ValueStart + m_ValueLength > p + l)
457       {
458         DefaultLogSink().Error("Primer entry too long.\n");
459         return RESULT_FAIL;
460       }
461       Kumu::MemIOReader MemRDR(m_ValueStart, m_ValueLength);
462       result = LocalTagEntryBatch.Unarchive(&MemRDR) ? RESULT_OK : RESULT_KLV_CODING(__LINE__, __FILE__);
463     }
464
465   if ( ASDCP_SUCCESS(result) )
466     {
467       m_Lookup = new h__PrimerLookup;
468       m_Lookup->InitWithBatch(LocalTagEntryBatch);
469     }
470
471   if ( ASDCP_FAILURE(result) )
472     DefaultLogSink().Error("Failed to initialize Primer.\n");
473
474   return result;
475 }
476
477 //
478 ASDCP::Result_t
479 ASDCP::MXF::Primer::WriteToFile(Kumu::FileWriter& Writer)
480 {
481   ASDCP::FrameBuffer Buffer;
482   Result_t result = Buffer.Capacity(128*1024);
483
484   if ( ASDCP_SUCCESS(result) )
485     result = WriteToBuffer(Buffer);
486
487   if ( ASDCP_SUCCESS(result) )
488   result = Writer.Write(Buffer.RoData(), Buffer.Size());
489
490   return result;
491 }
492
493 //
494 ASDCP::Result_t
495 ASDCP::MXF::Primer::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
496 {
497   assert(m_Dict);
498   ASDCP::FrameBuffer LocalTagBuffer;
499   Kumu::MemIOWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length);
500   Result_t result = LocalTagEntryBatch.Archive(&MemWRT) ? RESULT_OK : RESULT_KLV_CODING(__LINE__, __FILE__);
501
502   if ( ASDCP_SUCCESS(result) )
503     {
504       ui32_t packet_length = MemWRT.Length();
505       result = WriteKLToBuffer(Buffer, packet_length);
506
507       if ( ASDCP_SUCCESS(result) )
508         Buffer.Size(Buffer.Size() + packet_length);
509     }
510
511   return result;
512 }
513
514 //
515 ASDCP::Result_t
516 ASDCP::MXF::Primer::InsertTag(const MDDEntry& Entry, ASDCP::TagValue& Tag)
517 {
518   assert(m_Lookup);
519   UL TestUL(Entry.ul);
520   std::map<UL, TagValue>::iterator i = m_Lookup->find(TestUL);
521
522   if ( i == m_Lookup->end() )
523     {
524       if ( Entry.tag.a == 0 && Entry.tag.b == 0 )
525         {
526           Tag.a = 0xff;
527           Tag.b = m_LocalTag--;
528         }
529       else
530         {
531           Tag.a = Entry.tag.a;
532           Tag.b = Entry.tag.b;
533         }
534
535       LocalTagEntry TmpEntry;
536       TmpEntry.UL = TestUL;
537       TmpEntry.Tag = Tag;
538
539       LocalTagEntryBatch.insert(TmpEntry);
540       m_Lookup->insert(std::map<UL, TagValue>::value_type(TmpEntry.UL, TmpEntry.Tag));
541     }
542   else
543     {
544       Tag = (*i).second;
545     }
546    
547   return RESULT_OK;
548 }
549
550 //
551 ASDCP::Result_t
552 ASDCP::MXF::Primer::TagForKey(const ASDCP::UL& Key, ASDCP::TagValue& Tag)
553 {
554   assert(m_Lookup);
555   if ( m_Lookup.empty() )
556     {
557       DefaultLogSink().Error("Primer lookup is empty\n");
558       return RESULT_FAIL;
559     }
560
561   std::map<UL, TagValue>::iterator i = m_Lookup->find(Key);
562
563   if ( i == m_Lookup->end() )
564     return RESULT_FALSE;
565
566   Tag = (*i).second;
567   return RESULT_OK;
568 }
569
570 //
571 void
572 ASDCP::MXF::Primer::Dump(FILE* stream)
573 {
574   assert(m_Dict);
575   char identbuf[IdentBufferLen];
576
577   if ( stream == 0 )
578     stream = stderr;
579
580   KLVPacket::Dump(stream, *m_Dict, false);
581   fprintf(stream, "Primer: %u %s\n",
582           (ui32_t)LocalTagEntryBatch.size(),
583           ( LocalTagEntryBatch.size() == 1 ? "entry" : "entries" ));
584   
585   Batch<LocalTagEntry>::iterator i = LocalTagEntryBatch.begin();
586   for ( ; i != LocalTagEntryBatch.end(); i++ )
587     {
588       const MDDEntry* Entry = m_Dict->FindULAnyVersion((*i).UL.Value());
589       fprintf(stream, "  %s %s\n", (*i).EncodeString(identbuf, IdentBufferLen), (Entry ? Entry->name : "Unknown"));
590     }
591 }
592
593
594 //------------------------------------------------------------------------------------------
595 //
596
597 //
598 ASDCP::MXF::Preface::Preface(const Dictionary*& d) :
599   InterchangeObject(d), m_Dict(d), Version(258)
600 {
601   assert(m_Dict);
602   m_UL = m_Dict->Type(MDD_Preface).ul;
603   ObjectModelVersion = 0;
604 }
605
606 //
607 void
608 ASDCP::MXF::Preface::Copy(const Preface& rhs)
609 {
610   InterchangeObject::Copy(rhs);
611
612   LastModifiedDate = rhs.LastModifiedDate;
613   Version = rhs.Version;
614   ObjectModelVersion = rhs.ObjectModelVersion;
615   PrimaryPackage = rhs.PrimaryPackage;
616   Identifications = rhs.Identifications;
617   ContentStorage = rhs.ContentStorage;
618   OperationalPattern = rhs.OperationalPattern;
619   EssenceContainers = rhs.EssenceContainers;
620   DMSchemes = rhs.DMSchemes;
621   ApplicationSchemes = rhs.ApplicationSchemes;
622   ConformsToSpecifications = rhs.ConformsToSpecifications;
623 }
624
625 //
626 ASDCP::Result_t
627 ASDCP::MXF::Preface::InitFromTLVSet(TLVReader& TLVSet)
628 {
629   Result_t result = InterchangeObject::InitFromTLVSet(TLVSet);
630   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, LastModifiedDate));
631   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi16(OBJ_READ_ARGS(Preface, Version));
632   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi32(OBJ_READ_ARGS_OPT(Preface, ObjectModelVersion));
633   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS_OPT(Preface, PrimaryPackage));
634   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, Identifications));
635   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, ContentStorage));
636   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, OperationalPattern));
637   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, EssenceContainers));
638   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, DMSchemes));
639   if ( ASDCP_SUCCESS(result) ) 
640     {
641       result = TLVSet.ReadObject(OBJ_READ_ARGS_OPT(Preface, ApplicationSchemes));
642       ApplicationSchemes.set_has_value( result == RESULT_OK);
643     }
644   if ( ASDCP_SUCCESS(result) )
645     {
646       result = TLVSet.ReadObject(OBJ_READ_ARGS_OPT(Preface, ConformsToSpecifications));
647       ConformsToSpecifications.set_has_value( result == RESULT_OK);
648     }
649   return result;
650 }
651
652 //
653 ASDCP::Result_t
654 ASDCP::MXF::Preface::WriteToTLVSet(TLVWriter& TLVSet)
655 {
656   Result_t result = InterchangeObject::WriteToTLVSet(TLVSet);
657   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, LastModifiedDate));
658   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteUi16(OBJ_WRITE_ARGS(Preface, Version));
659   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteUi32(OBJ_WRITE_ARGS_OPT(Preface, ObjectModelVersion));
660   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteObject(OBJ_WRITE_ARGS_OPT(Preface, PrimaryPackage));
661   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, Identifications));
662   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, ContentStorage));
663   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, OperationalPattern));
664   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, EssenceContainers));
665   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, DMSchemes));
666   if ( ASDCP_SUCCESS(result) && !ApplicationSchemes.empty() )
667     {
668       result = TLVSet.WriteObject(OBJ_WRITE_ARGS_OPT(Preface, ApplicationSchemes));
669     }
670   if ( ASDCP_SUCCESS(result) && !ConformsToSpecifications.empty())
671     {
672       result = TLVSet.WriteObject(OBJ_WRITE_ARGS_OPT(Preface, ConformsToSpecifications));
673     }
674   return result;
675 }
676
677 //
678 ASDCP::Result_t
679 ASDCP::MXF::Preface::InitFromBuffer(const byte_t* p, ui32_t l)
680 {
681   return InterchangeObject::InitFromBuffer(p, l);
682 }
683
684 //
685 ASDCP::Result_t
686 ASDCP::MXF::Preface::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
687 {
688   return InterchangeObject::WriteToBuffer(Buffer);
689 }
690
691 //
692 void
693 ASDCP::MXF::Preface::Dump(FILE* stream)
694 {
695   char identbuf[IdentBufferLen];
696
697   if ( stream == 0 )
698     stream = stderr;
699
700   InterchangeObject::Dump(stream);
701   fprintf(stream, "  %22s = %s\n",  "LastModifiedDate", LastModifiedDate.EncodeString(identbuf, IdentBufferLen));
702   fprintf(stream, "  %22s = %hu\n", "Version", Version);
703
704   if ( ! ObjectModelVersion.empty() )
705     fprintf(stream, "  %22s = %u\n",  "ObjectModelVersion", ObjectModelVersion.get());
706
707   if ( ! PrimaryPackage.empty() )
708     fprintf(stream, "  %22s = %s\n",  "PrimaryPackage", PrimaryPackage.get().EncodeHex(identbuf, IdentBufferLen));
709
710   fprintf(stream, "  %22s:\n", "Identifications");  Identifications.Dump(stream);
711   fprintf(stream, "  %22s = %s\n",  "ContentStorage", ContentStorage.EncodeHex(identbuf, IdentBufferLen));
712   fprintf(stream, "  %22s = %s\n",  "OperationalPattern", OperationalPattern.EncodeString(identbuf, IdentBufferLen));
713   fprintf(stream, "  %22s:\n", "EssenceContainers");  EssenceContainers.Dump(stream);
714   fprintf(stream, "  %22s:\n", "DMSchemes");  DMSchemes.Dump(stream);
715   if ( ! ApplicationSchemes.empty() )
716     {
717       fprintf(stream, "  %22s:\n", "ApplicationSchemes");  ApplicationSchemes.get().Dump(stream);
718     }
719   if ( ! ConformsToSpecifications.empty() )
720     {
721       fprintf(stream, "  %22s:\n", "ConformsToSpecifications");  ConformsToSpecifications.get().Dump(stream);
722     }
723 }
724
725 //------------------------------------------------------------------------------------------
726 //
727
728 ASDCP::MXF::OP1aHeader::OP1aHeader(const Dictionary*& d) : Partition(d), m_Dict(d), m_Primer(d), m_Preface(0) {}
729 ASDCP::MXF::OP1aHeader::~OP1aHeader() {}
730
731 //
732 ASDCP::Result_t
733 ASDCP::MXF::OP1aHeader::InitFromFile(const Kumu::FileReader& Reader)
734 {
735   Result_t result = Partition::InitFromFile(Reader);
736
737   if ( ASDCP_FAILURE(result) )
738     return result;
739
740   if ( m_Dict == &DefaultCompositeDict() )
741     {
742       // select more explicit dictionary if one is available
743       if ( OperationalPattern.MatchExact(MXFInterop_OPAtom_Entry().ul) )
744         {
745           m_Dict = &DefaultInteropDict();
746         }
747       else if ( OperationalPattern.MatchExact(SMPTE_390_OPAtom_Entry().ul) )
748         {
749           m_Dict = &DefaultSMPTEDict();
750         }
751     }
752
753   // slurp up the remainder of the header
754   if ( HeaderByteCount == 0 )
755     {
756       DefaultLogSink().Warn("MXF file contents incomplete.\n");
757       return RESULT_KLV_CODING(__LINE__, __FILE__);
758     }
759   else if ( HeaderByteCount < 1024 )
760     {
761       DefaultLogSink().Warn("Improbably small HeaderByteCount value: %qu\n", HeaderByteCount);
762     }
763   else if (HeaderByteCount > ( 4 * Kumu::Megabyte ) )
764     {
765       DefaultLogSink().Warn("Improbably huge HeaderByteCount value: %qu\n", HeaderByteCount);
766     }
767   
768   result = m_HeaderData.Capacity(Kumu::xmin(4*Kumu::Megabyte, static_cast<ui32_t>(HeaderByteCount)));
769
770   if ( ASDCP_SUCCESS(result) )
771     {
772       ui32_t read_count;
773       result = Reader.Read(m_HeaderData.Data(), m_HeaderData.Capacity(), &read_count);
774
775       if ( ASDCP_FAILURE(result) )
776         {
777           DefaultLogSink().Error("OP1aHeader::InitFromFile, read failed.\n");
778           return result;
779         }
780
781       if ( read_count != m_HeaderData.Capacity() )
782         {
783           DefaultLogSink().Error("Short read of OP-Atom header metadata; wanted %u, got %u.\n",
784                                  m_HeaderData.Capacity(), read_count);
785           return RESULT_KLV_CODING(__LINE__, __FILE__);
786         }
787     }
788
789   if ( ASDCP_SUCCESS(result) )
790     result = InitFromBuffer(m_HeaderData.RoData(), m_HeaderData.Capacity());
791
792   return result;
793 }
794
795 //
796 ASDCP::Result_t
797 ASDCP::MXF::OP1aHeader::InitFromPartitionBuffer(const byte_t* p, ui32_t l)
798 {
799   Result_t result = KLVPacket::InitFromBuffer(p, l);
800
801   if ( ASDCP_SUCCESS(result) )
802     result = Partition::InitFromBuffer(m_ValueStart, m_ValueLength); // test UL and OP
803
804   if ( ASDCP_SUCCESS(result) )
805     {
806       ui32_t pp_len = KLVPacket::PacketLength();
807       result = InitFromBuffer(p + pp_len, l - pp_len);
808     }
809
810   return result;
811 }
812
813 //
814 ASDCP::Result_t
815 ASDCP::MXF::OP1aHeader::InitFromBuffer(const byte_t* p, ui32_t l)
816 {
817   assert(m_Dict);
818   Result_t result = RESULT_OK;
819   const byte_t* end_p = p + l;
820
821   while ( ASDCP_SUCCESS(result) && p < end_p )
822     {
823       // parse the packets and index them by uid, discard KLVFill items
824       InterchangeObject* object = CreateObject(m_Dict, p);
825       assert(object);
826
827       object->m_Lookup = &m_Primer;
828       result = object->InitFromBuffer(p, end_p - p);
829
830       const byte_t* redo_p = p;
831       p += object->PacketLength();
832
833       if ( ASDCP_SUCCESS(result) )
834         {
835           if ( object->IsA(m_Dict->ul(MDD_KLVFill)) )
836             {
837               delete object;
838
839               if ( p > end_p )
840                 {
841                   DefaultLogSink().Error("Fill item short read: %d.\n", p - end_p);
842                 }
843             }
844           else if ( object->IsA(m_Dict->ul(MDD_Primer)) ) // TODO: only one primer should be found
845             {
846               delete object;
847               result = m_Primer.InitFromBuffer(redo_p, end_p - redo_p);
848             }
849           else
850             {
851               m_PacketList->AddPacket(object); // takes ownership
852
853               if ( object->IsA(m_Dict->ul(MDD_Preface)) && m_Preface == 0 )
854                 m_Preface = (Preface*)object;
855             }
856         }
857       else
858         {
859           DefaultLogSink().Error("Error initializing OP1a header packet.\n");
860           //      Kumu::hexdump(p-object->PacketLength(), object->PacketLength());
861           delete object;
862         }
863     }
864
865   return result;
866 }
867
868 ASDCP::Result_t
869 ASDCP::MXF::OP1aHeader::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
870 {
871   return m_PacketList->GetMDObjectByID(ObjectID, Object);
872 }
873
874 //
875 ASDCP::Result_t
876 ASDCP::MXF::OP1aHeader::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
877 {
878   InterchangeObject* TmpObject;
879
880   if ( Object == 0 )
881     Object = &TmpObject;
882
883   return m_PacketList->GetMDObjectByType(ObjectID, Object);
884 }
885
886 //
887 ASDCP::Result_t
888 ASDCP::MXF::OP1aHeader::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
889 {
890   return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
891 }
892
893 //
894 ASDCP::MXF::Identification*
895 ASDCP::MXF::OP1aHeader::GetIdentification()
896 {
897   InterchangeObject* Object;
898
899   if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object)) )
900     return (Identification*)Object;
901
902   return 0;
903 }
904
905 //
906 ASDCP::MXF::SourcePackage*
907 ASDCP::MXF::OP1aHeader::GetSourcePackage()
908 {
909   InterchangeObject* Object;
910
911   if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object)) )
912     return (SourcePackage*)Object;
913
914   return 0;
915 }
916
917 //
918 ASDCP::Result_t
919 ASDCP::MXF::OP1aHeader::WriteToFile(Kumu::FileWriter& Writer, ui32_t HeaderSize)
920 {
921   assert(m_Dict);
922   if ( m_Preface == 0 )
923     return RESULT_STATE;
924
925   if ( HeaderSize < 4096 ) 
926     {
927       DefaultLogSink().Error("HeaderSize %u is too small. Must be >= 4096\n", HeaderSize);
928       return RESULT_PARAM;
929     }
930
931   ASDCP::FrameBuffer HeaderBuffer;
932   HeaderByteCount = HeaderSize - ArchiveSize();
933   assert (HeaderByteCount <= 0xFFFFFFFFL);
934   Result_t result = HeaderBuffer.Capacity((ui32_t) HeaderByteCount); 
935   m_Preface->m_Lookup = &m_Primer;
936
937   std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
938   for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
939     {
940       InterchangeObject* object = *pl_i;
941       object->m_Lookup = &m_Primer;
942
943       ASDCP::FrameBuffer WriteWrapper;
944       WriteWrapper.SetData(HeaderBuffer.Data() + HeaderBuffer.Size(),
945                            HeaderBuffer.Capacity() - HeaderBuffer.Size());
946       result = object->WriteToBuffer(WriteWrapper);
947       HeaderBuffer.Size(HeaderBuffer.Size() + WriteWrapper.Size());
948     }
949
950   if ( ASDCP_SUCCESS(result) )
951     {
952       UL TmpUL(m_Dict->ul(MDD_ClosedCompleteHeader));
953       result = Partition::WriteToFile(Writer, TmpUL);
954     }
955
956   if ( ASDCP_SUCCESS(result) )
957     result = m_Primer.WriteToFile(Writer);
958
959   if ( ASDCP_SUCCESS(result) )
960     {
961       ui32_t write_count;
962       Writer.Write(HeaderBuffer.RoData(), HeaderBuffer.Size(), &write_count);
963       assert(write_count == HeaderBuffer.Size());
964     }
965
966   // KLV Fill
967   if ( ASDCP_SUCCESS(result) )
968     {
969       Kumu::fpos_t pos = Writer.Tell();
970
971       if ( pos > (Kumu::fpos_t)HeaderByteCount )
972         {
973           char intbuf[IntBufferLen];
974           DefaultLogSink().Error("Header size %s exceeds specified value %u\n",
975                                  ui64sz(pos, intbuf),
976                                  HeaderSize);
977           return RESULT_FAIL;
978         }
979
980       ASDCP::FrameBuffer NilBuf;
981       ui32_t klv_fill_length = HeaderSize - (ui32_t)pos;
982
983       if ( klv_fill_length < kl_length )
984         {
985           DefaultLogSink().Error("Remaining region too small for KLV Fill header\n");
986           return RESULT_FAIL;
987         }
988
989       klv_fill_length -= kl_length;
990       result = WriteKLToFile(Writer, m_Dict->ul(MDD_KLVFill), klv_fill_length);
991
992       if ( ASDCP_SUCCESS(result) )
993         result = NilBuf.Capacity(klv_fill_length);
994
995       if ( ASDCP_SUCCESS(result) )
996         {
997           memset(NilBuf.Data(), 0, klv_fill_length);
998           ui32_t write_count;
999           Writer.Write(NilBuf.RoData(), klv_fill_length, &write_count);
1000           assert(write_count == klv_fill_length);
1001         }
1002     }
1003
1004   return result;
1005 }
1006
1007 //
1008 void
1009 ASDCP::MXF::OP1aHeader::Dump(FILE* stream)
1010 {
1011   if ( stream == 0 )
1012     stream = stderr;
1013
1014   Partition::Dump(stream);
1015   m_Primer.Dump(stream);
1016
1017   if ( m_Preface == 0 )
1018     fputs("No Preface loaded\n", stream);
1019
1020   std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
1021   for ( ; i != m_PacketList->m_List.end(); i++ )
1022     (*i)->Dump(stream);
1023 }
1024
1025 //------------------------------------------------------------------------------------------
1026 //
1027
1028 ASDCP::MXF::OPAtomIndexFooter::OPAtomIndexFooter(const Dictionary*& d) :
1029   Partition(d), m_Dict(d),
1030   m_CurrentSegment(0), m_BytesPerEditUnit(0), m_BodySID(0),
1031   m_ECOffset(0), m_Lookup(0)
1032 {
1033   BodySID = 0;
1034   IndexSID = 129;
1035 }
1036
1037 ASDCP::MXF::OPAtomIndexFooter::~OPAtomIndexFooter() {}
1038
1039 //
1040 ASDCP::Result_t
1041 ASDCP::MXF::OPAtomIndexFooter::InitFromFile(const Kumu::FileReader& Reader)
1042 {
1043   Result_t result = Partition::InitFromFile(Reader); // test UL and OP
1044
1045   // slurp up the remainder of the footer
1046   ui32_t read_count = 0;
1047
1048   if ( ASDCP_SUCCESS(result) && IndexByteCount > 0 )
1049     {
1050       assert (IndexByteCount <= 0xFFFFFFFFL);
1051       // At this point, m_FooterData may not have been initialized
1052       // so it's capacity is zero and data pointer is NULL
1053       // However, if IndexByteCount is zero then the capacity
1054       // doesn't change and the data pointer is not set.
1055       result = m_FooterData.Capacity((ui32_t) IndexByteCount);
1056
1057       if ( ASDCP_SUCCESS(result) )
1058         result = Reader.Read(m_FooterData.Data(), m_FooterData.Capacity(), &read_count);
1059
1060       if ( ASDCP_SUCCESS(result) && read_count != m_FooterData.Capacity() )
1061         {
1062           DefaultLogSink().Error("Short read of footer partition: got %u, expecting %u\n",
1063                                  read_count, m_FooterData.Capacity());
1064           return RESULT_FAIL;
1065         }
1066       else if( ASDCP_SUCCESS(result) && !m_FooterData.Data() )
1067         {
1068           DefaultLogSink().Error( "Buffer for footer partition not created: IndexByteCount = %u\n",
1069                                   IndexByteCount );
1070           return RESULT_FAIL;
1071         }
1072
1073       if ( ASDCP_SUCCESS(result) )
1074         result = InitFromBuffer(m_FooterData.RoData(), m_FooterData.Capacity());
1075     }
1076
1077   return result;
1078 }
1079
1080 //
1081 ASDCP::Result_t
1082 ASDCP::MXF::OPAtomIndexFooter::InitFromPartitionBuffer(const byte_t* p, ui32_t l)
1083 {
1084   Result_t result = KLVPacket::InitFromBuffer(p, l);
1085
1086   if ( ASDCP_SUCCESS(result) )
1087     result = Partition::InitFromBuffer(m_ValueStart, m_ValueLength); // test UL and OP
1088
1089   if ( ASDCP_SUCCESS(result) )
1090     {
1091       ui32_t pp_len = KLVPacket::PacketLength();
1092       result = InitFromBuffer(p + pp_len, l - pp_len);
1093     }
1094
1095   return result;
1096 }
1097
1098 //
1099 ASDCP::Result_t
1100 ASDCP::MXF::OPAtomIndexFooter::InitFromBuffer(const byte_t* p, ui32_t l)
1101 {
1102   Result_t result = RESULT_OK;
1103   const byte_t* end_p = p + l;
1104   
1105   while ( ASDCP_SUCCESS(result) && p < end_p )
1106     {
1107       // parse the packets and index them by uid, discard KLVFill items
1108       InterchangeObject* object = CreateObject(m_Dict, p);
1109       assert(object);
1110
1111       object->m_Lookup = m_Lookup;
1112       result = object->InitFromBuffer(p, end_p - p);
1113       p += object->PacketLength();
1114
1115       if ( ASDCP_SUCCESS(result) )
1116         {
1117           m_PacketList->AddPacket(object); // takes ownership
1118         }
1119       else
1120         {
1121           DefaultLogSink().Error("Error initializing OPAtom footer packet.\n");
1122           delete object;
1123         }
1124     }
1125
1126   if ( ASDCP_FAILURE(result) )
1127     {
1128       DefaultLogSink().Error("Failed to initialize OPAtomIndexFooter.\n");
1129     }
1130
1131   return result;
1132 }
1133
1134 //
1135 ASDCP::Result_t
1136 ASDCP::MXF::OPAtomIndexFooter::WriteToFile(Kumu::FileWriter& Writer, ui64_t duration)
1137 {
1138   assert(m_Dict);
1139   ASDCP::FrameBuffer FooterBuffer;
1140   ui32_t   footer_size = m_PacketList->m_List.size() * MaxIndexSegmentSize; // segment-count * max-segment-size
1141   Result_t result = FooterBuffer.Capacity(footer_size); 
1142   ui32_t   iseg_count = 0;
1143
1144   if ( m_CurrentSegment != 0 )
1145     {
1146       m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1147       m_CurrentSegment = 0;
1148     }
1149
1150   std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
1151   for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
1152     {
1153       IndexTableSegment *segment = dynamic_cast<IndexTableSegment*>(*pl_i);
1154
1155       if ( segment != 0 )
1156         {
1157           iseg_count++;
1158           if ( m_BytesPerEditUnit != 0 )
1159             {
1160               if ( iseg_count != 1 )
1161                 return RESULT_STATE;
1162
1163               segment->IndexDuration = duration;
1164             }
1165         }
1166
1167       InterchangeObject* object = *pl_i;
1168       object->m_Lookup = m_Lookup;
1169
1170       ASDCP::FrameBuffer WriteWrapper;
1171       WriteWrapper.SetData(FooterBuffer.Data() + FooterBuffer.Size(),
1172                            FooterBuffer.Capacity() - FooterBuffer.Size());
1173       result = object->WriteToBuffer(WriteWrapper);
1174       FooterBuffer.Size(FooterBuffer.Size() + WriteWrapper.Size());
1175     }
1176
1177   if ( ASDCP_SUCCESS(result) )
1178     {
1179       IndexByteCount = FooterBuffer.Size();
1180       UL FooterUL(m_Dict->ul(MDD_CompleteFooter));
1181       result = Partition::WriteToFile(Writer, FooterUL);
1182     }
1183
1184   if ( ASDCP_SUCCESS(result) )
1185     {
1186       ui32_t write_count = 0;
1187       result = Writer.Write(FooterBuffer.RoData(), FooterBuffer.Size(), &write_count);
1188       assert(write_count == FooterBuffer.Size());
1189     }
1190
1191   return result;
1192 }
1193
1194 //
1195 void
1196 ASDCP::MXF::OPAtomIndexFooter::Dump(FILE* stream)
1197 {
1198   if ( stream == 0 )
1199     stream = stderr;
1200
1201   Partition::Dump(stream);
1202
1203   std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
1204   for ( ; i != m_PacketList->m_List.end(); i++ )
1205     (*i)->Dump(stream);
1206 }
1207
1208 ASDCP::Result_t
1209 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
1210 {
1211   return m_PacketList->GetMDObjectByID(ObjectID, Object);
1212 }
1213
1214 //
1215 ASDCP::Result_t
1216 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
1217 {
1218   InterchangeObject* TmpObject;
1219
1220   if ( Object == 0 )
1221     Object = &TmpObject;
1222
1223   return m_PacketList->GetMDObjectByType(ObjectID, Object);
1224 }
1225
1226 //
1227 ASDCP::Result_t
1228 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
1229 {
1230   return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
1231 }
1232
1233 //
1234 ASDCP::Result_t
1235 ASDCP::MXF::OPAtomIndexFooter::Lookup(ui32_t frame_num, IndexTableSegment::IndexEntry& Entry) const
1236 {
1237   std::list<InterchangeObject*>::iterator li;
1238   for ( li = m_PacketList->m_List.begin(); li != m_PacketList->m_List.end(); li++ )
1239     {
1240       IndexTableSegment *segment = dynamic_cast<IndexTableSegment*>(*li);
1241
1242       if ( segment != 0 )
1243         {
1244           ui64_t start_pos = segment->IndexStartPosition;
1245
1246           if ( segment->EditUnitByteCount > 0 )
1247             {
1248               if ( m_PacketList->m_List.size() > 1 )
1249                 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
1250
1251               if ( ! segment->IndexEntryArray.empty() )
1252                 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
1253
1254               Entry.StreamOffset = (ui64_t)frame_num * segment->EditUnitByteCount;
1255               return RESULT_OK;
1256             }
1257           else if ( (ui64_t)frame_num >= start_pos
1258                     && (ui64_t)frame_num < (start_pos + segment->IndexDuration) )
1259             {
1260               ui64_t tmp = frame_num - start_pos;
1261               assert(tmp <= 0xFFFFFFFFL);
1262
1263               if ( tmp < segment->IndexEntryArray.size() )
1264                 {
1265                   Entry = segment->IndexEntryArray[(ui32_t) tmp];
1266                   return RESULT_OK;
1267                 }
1268               else
1269                 {
1270                   DefaultLogSink().Error("Malformed index table segment, IndexDuration does not match entries.\n");
1271                 }
1272             }
1273         }
1274     }
1275
1276   return RESULT_FAIL;
1277 }
1278
1279 //
1280 void
1281 ASDCP::MXF::OPAtomIndexFooter::SetDeltaParams(const IndexTableSegment::DeltaEntry& delta)
1282 {
1283   m_DefaultDeltaEntry = delta;
1284 }
1285
1286 //
1287 void
1288 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const Rational& Rate)
1289 {
1290   assert(lookup);
1291   m_Lookup = lookup;
1292   m_BytesPerEditUnit = size;
1293   m_EditRate = Rate;
1294
1295   IndexTableSegment* Index = new IndexTableSegment(m_Dict);
1296   AddChildObject(Index);
1297   Index->EditUnitByteCount = m_BytesPerEditUnit;
1298   Index->IndexEditRate = Rate;
1299 }
1300
1301 //
1302 void
1303 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsVBR(IPrimerLookup* lookup, const Rational& Rate, Kumu::fpos_t offset)
1304 {
1305   assert(lookup);
1306   m_Lookup = lookup;
1307   m_BytesPerEditUnit = 0;
1308   m_EditRate = Rate;
1309   m_ECOffset = offset;
1310 }
1311
1312 //
1313 void
1314 ASDCP::MXF::OPAtomIndexFooter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
1315 {
1316   if ( m_BytesPerEditUnit != 0 )  // are we CBR? that's bad 
1317     {
1318       DefaultLogSink().Error("Call to PushIndexEntry() failed: index is CBR\n");
1319       return;
1320     }
1321
1322   // do we have an available segment?
1323   if ( m_CurrentSegment == 0 )
1324     { // no, set up a new segment
1325       m_CurrentSegment = new IndexTableSegment(m_Dict);
1326       assert(m_CurrentSegment);
1327       AddChildObject(m_CurrentSegment);
1328       m_CurrentSegment->DeltaEntryArray.push_back(m_DefaultDeltaEntry);
1329       m_CurrentSegment->IndexEditRate = m_EditRate;
1330       m_CurrentSegment->IndexStartPosition = 0;
1331     }
1332   else if ( m_CurrentSegment->IndexEntryArray.size() >= CBRIndexEntriesPerSegment )
1333     { // no, this one is full, start another
1334       m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1335       ui64_t StartPosition = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
1336
1337       m_CurrentSegment = new IndexTableSegment(m_Dict);
1338       assert(m_CurrentSegment);
1339       AddChildObject(m_CurrentSegment);
1340       m_CurrentSegment->DeltaEntryArray.push_back(m_DefaultDeltaEntry);
1341       m_CurrentSegment->IndexEditRate = m_EditRate;
1342       m_CurrentSegment->IndexStartPosition = StartPosition;
1343     }
1344
1345   m_CurrentSegment->IndexEntryArray.push_back(Entry);
1346 }
1347
1348 //------------------------------------------------------------------------------------------
1349 //
1350
1351 //
1352 void
1353 ASDCP::MXF::InterchangeObject::Copy(const InterchangeObject& rhs)
1354 {
1355   m_UL = rhs.m_UL;
1356   InstanceUID = rhs.InstanceUID;
1357   GenerationUID = rhs.GenerationUID;
1358 }
1359
1360 //
1361 ASDCP::Result_t
1362 ASDCP::MXF::InterchangeObject::InitFromTLVSet(TLVReader& TLVSet)
1363 {
1364   Result_t result = TLVSet.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID));
1365   if ( ASDCP_SUCCESS(result) )
1366     result = TLVSet.ReadObject(OBJ_READ_ARGS_OPT(GenerationInterchangeObject, GenerationUID));
1367   return result;
1368 }
1369
1370 //
1371 ASDCP::Result_t
1372 ASDCP::MXF::InterchangeObject::WriteToTLVSet(TLVWriter& TLVSet)
1373 {
1374   Result_t result = TLVSet.WriteObject(OBJ_WRITE_ARGS(InterchangeObject, InstanceUID));
1375   if ( ASDCP_SUCCESS(result) )
1376     result = TLVSet.WriteObject(OBJ_WRITE_ARGS_OPT(GenerationInterchangeObject, GenerationUID));
1377   return result;
1378 }
1379
1380 //
1381 ASDCP::Result_t
1382 ASDCP::MXF::InterchangeObject::InitFromBuffer(const byte_t* p, ui32_t l)
1383 {
1384   ASDCP_TEST_NULL(p);
1385   Result_t result = RESULT_FALSE;
1386
1387   if ( m_UL.HasValue() )
1388     {
1389       result = KLVPacket::InitFromBuffer(p, l, m_UL);
1390
1391       if ( ASDCP_SUCCESS(result) )
1392         {
1393     if (m_ValueStart + m_ValueLength > p  + l)
1394     {
1395       DefaultLogSink().Error("Interchange Object value extends past buffer length.\n");
1396       return RESULT_FAIL;
1397     }
1398           TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup);
1399           result = InitFromTLVSet(MemRDR);
1400         }
1401     }
1402   else
1403     {
1404       result = KLVPacket::InitFromBuffer(p, l);
1405     }
1406   
1407   return result;
1408 }
1409
1410 //
1411 ASDCP::Result_t
1412 ASDCP::MXF::InterchangeObject::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
1413 {
1414   if ( ! m_UL.HasValue() )
1415     return RESULT_STATE;
1416
1417   TLVWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length, m_Lookup);
1418   Result_t result = WriteToTLVSet(MemWRT);
1419
1420   if ( ASDCP_SUCCESS(result) )
1421     {
1422       ui32_t packet_length = MemWRT.Length();
1423       result = WriteKLToBuffer(Buffer, packet_length);
1424
1425       if ( ASDCP_SUCCESS(result) )
1426         Buffer.Size(Buffer.Size() + packet_length);
1427     }
1428
1429   return result;
1430 }
1431
1432 //
1433 void
1434 ASDCP::MXF::InterchangeObject::Dump(FILE* stream)
1435 {
1436   char identbuf[IdentBufferLen];
1437
1438   fputc('\n', stream);
1439   KLVPacket::Dump(stream, *m_Dict, false);
1440   fprintf(stream, "             InstanceUID = %s\n",  InstanceUID.EncodeHex(identbuf, IdentBufferLen));
1441
1442   if ( ! GenerationUID.empty() )
1443     fprintf(stream, "           GenerationUID = %s\n",  GenerationUID.get().EncodeHex(identbuf, IdentBufferLen));
1444 }
1445
1446 //
1447 bool
1448 ASDCP::MXF::InterchangeObject::IsA(const byte_t* label)
1449 {
1450   if ( m_KLLength == 0 || m_KeyStart == 0 )
1451     return false;
1452
1453   return ( memcmp(label, m_KeyStart, SMPTE_UL_LENGTH) == 0 );
1454 }
1455
1456
1457 //------------------------------------------------------------------------------------------
1458 struct FactoryCompareUL
1459 {
1460     bool operator()(const ASDCP::UL& lhs, const ASDCP::UL& rhs) const
1461     {
1462         ui32_t test_size = lhs.Size() < rhs.Size() ? lhs.Size() : rhs.Size();
1463
1464         for (ui32_t i = 0; i < test_size; i++)
1465         {
1466             if (i == 7) continue; // skip version to be symmetrical with UL::operator==
1467             if (lhs.Value()[i] != rhs.Value()[i])
1468                 return lhs.Value()[i] < rhs.Value()[i];
1469         }
1470
1471         return false;
1472     }
1473 };
1474
1475 typedef std::map<ASDCP::UL, ASDCP::MXF::MXFObjectFactory_t, FactoryCompareUL>FactoryMap_t;
1476 typedef FactoryMap_t::iterator FLi_t;
1477
1478 //
1479 class FactoryList : public FactoryMap_t
1480 {
1481   Kumu::Mutex m_Lock;
1482
1483 public:
1484   FactoryList() {}
1485   ~FactoryList() {}
1486
1487   bool Empty() {
1488     Kumu::AutoMutex BlockLock(m_Lock);
1489     return empty();
1490   }
1491
1492   FLi_t Find(const byte_t* label) {
1493     Kumu::AutoMutex BlockLock(m_Lock);
1494     return find(label);
1495   }
1496
1497   FLi_t End() {
1498     Kumu::AutoMutex BlockLock(m_Lock);
1499     return end();
1500   }
1501
1502   void Insert(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory) {
1503     Kumu::AutoMutex BlockLock(m_Lock);
1504     insert(FactoryList::value_type(label, factory));
1505   }
1506 };
1507
1508 //
1509 static FactoryList s_FactoryList;
1510 static Kumu::Mutex s_InitLock;
1511 static bool        s_TypesInit = false;
1512
1513
1514 //
1515 void
1516 ASDCP::MXF::SetObjectFactory(const ASDCP::UL& label, ASDCP::MXF::MXFObjectFactory_t factory)
1517 {
1518   s_FactoryList.Insert(label, factory);
1519 }
1520
1521 //
1522 ASDCP::MXF::InterchangeObject*
1523 ASDCP::MXF::CreateObject(const Dictionary*& Dict, const UL& label)
1524 {
1525   if ( ! s_TypesInit )
1526     {
1527       Kumu::AutoMutex BlockLock(s_InitLock);
1528
1529       if ( ! s_TypesInit )
1530         {
1531           MXF::Metadata_InitTypes(Dict);
1532           s_TypesInit = true;
1533         }
1534     }
1535
1536   FLi_t i = s_FactoryList.find(label);
1537
1538   if ( i == s_FactoryList.end() )
1539     return new InterchangeObject(Dict);
1540
1541   return i->second(Dict);
1542 }
1543
1544
1545 //------------------------------------------------------------------------------------------
1546
1547 //
1548 bool
1549 ASDCP::MXF::decode_mca_string(const std::string& s, const mca_label_map_t& labels, const Dictionary*& dict, const std::string& language,
1550                               InterchangeObject_list_t& descriptor_list, ui32_t& channel_count)
1551 {
1552   std::string symbol_buf;
1553   channel_count = 0;
1554   ASDCP::MXF::SoundfieldGroupLabelSubDescriptor *current_soundfield = 0, *prev_soundfield = 0;
1555   std::string::const_iterator i;
1556
1557   for ( i = s.begin(); i != s.end(); ++i )
1558     {
1559       if ( *i == '(' )
1560         {
1561           if ( current_soundfield != 0 && symbol_buf.empty() )
1562             {
1563               // appending to the existing soundfield group
1564               continue;
1565             }
1566           else if ( current_soundfield != 0 )
1567             {
1568               DefaultLogSink().Error("Encountered '(', already processing a soundfield group.\n");
1569               return false;
1570             }
1571           else if ( symbol_buf.empty() )
1572             {
1573               if ( prev_soundfield != 0 )
1574                 {
1575                   current_soundfield = prev_soundfield;
1576                   // appending to the existing soundfield group
1577                   continue;
1578                 }
1579               else
1580                 {
1581                   DefaultLogSink().Error("Encountered '(', without leading soundfield group symbol.\n");
1582                   return false;
1583                 }
1584             }
1585
1586           mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1587
1588           if ( i == labels.end() )
1589             {
1590               DefaultLogSink().Error("Unknown symbol: '%s'\n", symbol_buf.c_str());
1591               return false;
1592             }
1593       
1594           if ( i->second.ul.Value()[10] != 2 ) // magic depends on UL "Essence Facet" byte (see ST 428-12)
1595             {
1596               DefaultLogSink().Error("Not a soundfield group symbol: '%s'\n", symbol_buf.c_str());
1597               return false;
1598             }
1599
1600           current_soundfield = new ASDCP::MXF::SoundfieldGroupLabelSubDescriptor(dict);
1601           GenRandomValue(current_soundfield->MCALinkID);
1602
1603           current_soundfield->MCATagSymbol = (i->second.requires_prefix ? "sg" : "") + i->first;
1604           current_soundfield->MCATagName = i->second.tag_name;
1605           current_soundfield->RFC5646SpokenLanguage = language;
1606           current_soundfield->MCALabelDictionaryID = i->second.ul;
1607           descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(current_soundfield));
1608           prev_soundfield = current_soundfield;
1609           symbol_buf.clear();
1610         }
1611       else if ( *i == ')' )
1612         {
1613           if ( current_soundfield == 0 )
1614             {
1615               DefaultLogSink().Error("Encountered ')', not currently processing a soundfield group.\n");
1616               return false;
1617             }
1618
1619           if ( symbol_buf.empty() )
1620             {
1621               DefaultLogSink().Error("Soundfield group description contains no channels.\n");
1622               return false;
1623             }
1624
1625           mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1626       
1627           if ( i == labels.end() )
1628             {
1629               DefaultLogSink().Error("Unknown symbol: '%s'\n", symbol_buf.c_str());
1630               return false;
1631             }
1632
1633           assert(current_soundfield);
1634
1635           ASDCP::MXF::AudioChannelLabelSubDescriptor *channel_descr =
1636             new ASDCP::MXF::AudioChannelLabelSubDescriptor(dict);
1637           GenRandomValue(channel_descr->MCALinkID);
1638
1639           channel_descr->SoundfieldGroupLinkID = current_soundfield->MCALinkID;
1640           channel_descr->MCAChannelID = channel_count++ + 1;
1641           channel_descr->MCATagSymbol = (i->second.requires_prefix ? "ch" : "") + i->first;
1642           channel_descr->MCATagName = i->second.tag_name;
1643           channel_descr->RFC5646SpokenLanguage = language;
1644           channel_descr->MCALabelDictionaryID = i->second.ul;
1645           descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(channel_descr));
1646           symbol_buf.clear();
1647           current_soundfield = 0;
1648         }
1649       else if ( *i == ',' )
1650         {
1651           if ( ! symbol_buf.empty() && ! symbol_buf.compare("-") )
1652             {
1653               channel_count++;
1654               symbol_buf.clear();
1655             }
1656           else if ( ! symbol_buf.empty() )
1657             {
1658               mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1659
1660               if ( i == labels.end() )
1661                 {
1662                   DefaultLogSink().Error("Unknown symbol: '%s'\n", symbol_buf.c_str());
1663                   return false;
1664                 }
1665
1666               if ( i->second.ul.Value()[10] != 1 ) // magic depends on UL "Essence Facet" byte (see ST 428-12)
1667                 {
1668                   DefaultLogSink().Error("Not a channel symbol: '%s'\n", symbol_buf.c_str());
1669                   return false;
1670                 }
1671
1672               ASDCP::MXF::AudioChannelLabelSubDescriptor *channel_descr =
1673                 new ASDCP::MXF::AudioChannelLabelSubDescriptor(dict);
1674               GenRandomValue(channel_descr->MCALinkID);
1675
1676               if ( current_soundfield != 0 )
1677                 {
1678                   channel_descr->SoundfieldGroupLinkID = current_soundfield->MCALinkID;
1679                 }
1680
1681               channel_descr->MCAChannelID = channel_count++ + 1;
1682               channel_descr->MCATagSymbol = (i->second.requires_prefix ? "ch" : "") + i->first;
1683               channel_descr->MCATagName = i->second.tag_name;
1684               channel_descr->RFC5646SpokenLanguage = language;
1685               channel_descr->MCALabelDictionaryID = i->second.ul;
1686               descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(channel_descr));
1687               symbol_buf.clear();
1688             }
1689         }
1690       else if ( *i == '-' || isalnum(*i) )
1691         {
1692           symbol_buf += *i;
1693         }
1694       else if ( ! isspace(*i) )
1695         {
1696           DefaultLogSink().Error("Unexpected character '%c'.\n", *i);
1697           return false;
1698         }
1699     }
1700
1701   if ( ! symbol_buf.empty() && ! symbol_buf.compare("-")  )
1702     {
1703       channel_count++;
1704     }
1705   else if ( ! symbol_buf.empty() )
1706     {
1707       mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1708       
1709       if ( i == labels.end() )
1710         {
1711           DefaultLogSink().Error("Unknown symbol: '%s'\n", symbol_buf.c_str());
1712           return false;
1713         }
1714
1715       ASDCP::MXF::AudioChannelLabelSubDescriptor *channel_descr =
1716         new ASDCP::MXF::AudioChannelLabelSubDescriptor(dict);
1717       GenRandomValue(channel_descr->MCALinkID);
1718
1719       if ( current_soundfield != 0 )
1720         {
1721           channel_descr->SoundfieldGroupLinkID = current_soundfield->MCALinkID;
1722         }
1723
1724       channel_descr->MCAChannelID = channel_count++ + 1;
1725       channel_descr->MCATagSymbol = (i->second.requires_prefix ? "ch" : "") + i->first;
1726       channel_descr->MCATagName = i->second.tag_name;
1727       channel_descr->RFC5646SpokenLanguage = language;
1728       channel_descr->MCALabelDictionaryID = i->second.ul;
1729       descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(channel_descr));
1730     }
1731
1732   return true;
1733 }
1734
1735 //
1736 ASDCP::MXF::ASDCP_MCAConfigParser::ASDCP_MCAConfigParser(const Dictionary*& d) : m_Dict(d), m_ChannelCount(0)
1737 {
1738   typedef mca_label_map_t::value_type pair;
1739   m_LabelMap.insert(pair("L",     label_traits("Left"                              , true,  m_Dict->ul(MDD_DCAudioChannel_L))));
1740   m_LabelMap.insert(pair("R",     label_traits("Right"                             , true,  m_Dict->ul(MDD_DCAudioChannel_R))));
1741   m_LabelMap.insert(pair("C",     label_traits("Center"                            , true,  m_Dict->ul(MDD_DCAudioChannel_C))));
1742   m_LabelMap.insert(pair("LFE",   label_traits("LFE"                               , true,  m_Dict->ul(MDD_DCAudioChannel_LFE))));
1743   m_LabelMap.insert(pair("Ls",    label_traits("Left Surround"                     , true,  m_Dict->ul(MDD_DCAudioChannel_Ls))));
1744   m_LabelMap.insert(pair("Rs",    label_traits("Right Surround"                    , true,  m_Dict->ul(MDD_DCAudioChannel_Rs))));
1745   m_LabelMap.insert(pair("Lss",   label_traits("Left Side Surround"                , true,  m_Dict->ul(MDD_DCAudioChannel_Lss))));
1746   m_LabelMap.insert(pair("Rss",   label_traits("Right Side Surround"               , true,  m_Dict->ul(MDD_DCAudioChannel_Rss))));
1747   m_LabelMap.insert(pair("Lrs",   label_traits("Left Rear Surround"                , true,  m_Dict->ul(MDD_DCAudioChannel_Lrs))));
1748   m_LabelMap.insert(pair("Rrs",   label_traits("Right Rear Surround"               , true,  m_Dict->ul(MDD_DCAudioChannel_Rrs))));
1749   m_LabelMap.insert(pair("Lc",    label_traits("Left Center"                       , true,  m_Dict->ul(MDD_DCAudioChannel_Lc))));
1750   m_LabelMap.insert(pair("Rc",    label_traits("Right Center"                      , true,  m_Dict->ul(MDD_DCAudioChannel_Rc))));
1751   m_LabelMap.insert(pair("Cs",    label_traits("Center Surround"                   , true,  m_Dict->ul(MDD_DCAudioChannel_Cs))));
1752   m_LabelMap.insert(pair("HI",    label_traits("Hearing Impaired"                  , true,  m_Dict->ul(MDD_DCAudioChannel_HI))));
1753   m_LabelMap.insert(pair("VIN",   label_traits("Visually Impaired-Narrative"       , true,  m_Dict->ul(MDD_DCAudioChannel_VIN))));
1754   m_LabelMap.insert(pair("51",    label_traits("5.1"                               , true,  m_Dict->ul(MDD_DCAudioSoundfield_51))));
1755   m_LabelMap.insert(pair("71",    label_traits("7.1DS"                             , true,  m_Dict->ul(MDD_DCAudioSoundfield_71))));
1756   m_LabelMap.insert(pair("SDS",   label_traits("7.1SDS"                            , true,  m_Dict->ul(MDD_DCAudioSoundfield_SDS))));
1757   m_LabelMap.insert(pair("61",    label_traits("6.1"                               , true,  m_Dict->ul(MDD_DCAudioSoundfield_61))));
1758   m_LabelMap.insert(pair("M",     label_traits("1.0 Monaural"                      , true,  m_Dict->ul(MDD_DCAudioSoundfield_M))));
1759   m_LabelMap.insert(pair("FSKSync",   label_traits("FSK Sync"                      , true,  m_Dict->ul(MDD_DCAudioChannel_FSKSyncSignalChannel))));
1760   m_LabelMap.insert(pair("DBOX",  label_traits("D-BOX Motion Code Primary Stream"  , false, m_Dict->ul(MDD_DBOXMotionCodePrimaryStream))));
1761   m_LabelMap.insert(pair("DBOX2", label_traits("D-BOX Motion Code Secondary Stream", false, m_Dict->ul(MDD_DBOXMotionCodeSecondaryStream))));
1762 }
1763
1764 //
1765 ui32_t
1766 ASDCP::MXF::ASDCP_MCAConfigParser::ChannelCount() const
1767 {
1768   return m_ChannelCount;
1769 }
1770
1771 // 51(L,R,C,LFE,Ls,Rs),HI,VIN
1772 bool
1773 ASDCP::MXF::ASDCP_MCAConfigParser::DecodeString(const std::string& s, const std::string& language)
1774 {
1775   return decode_mca_string(s, m_LabelMap, m_Dict, language, *this, m_ChannelCount);
1776 }
1777
1778 // ST(L,R),DNS(NSC001,NSC002),-,VIN
1779 ASDCP::MXF::AS02_MCAConfigParser::AS02_MCAConfigParser(const Dictionary*& d) : ASDCP::MXF::ASDCP_MCAConfigParser(d)
1780 {
1781   typedef mca_label_map_t::value_type pair;
1782   m_LabelMap.insert(pair("M1",    label_traits("Mono One",  true,  m_Dict->ul(MDD_IMFAudioChannel_M1))));
1783   m_LabelMap.insert(pair("M2",    label_traits("Mono Two",  true,  m_Dict->ul(MDD_IMFAudioChannel_M2))));
1784   m_LabelMap.insert(pair("Lt",    label_traits("Left Total",  true,  m_Dict->ul(MDD_IMFAudioChannel_Lt))));
1785   m_LabelMap.insert(pair("Rt",    label_traits("Right Total",  true,  m_Dict->ul(MDD_IMFAudioChannel_Rt))));
1786   m_LabelMap.insert(pair("Lst",   label_traits("Left Surround Total", true,  m_Dict->ul(MDD_IMFAudioChannel_Lst))));
1787   m_LabelMap.insert(pair("Rst",   label_traits("Right Surround Total", true,  m_Dict->ul(MDD_IMFAudioChannel_Rst))));
1788   m_LabelMap.insert(pair("S",     label_traits("Surround",   true,  m_Dict->ul(MDD_IMFAudioChannel_S))));
1789   m_LabelMap.insert(pair("ST",    label_traits("Standard Stereo",  true,  m_Dict->ul(MDD_IMFAudioSoundfield_ST))));
1790   m_LabelMap.insert(pair("DM",    label_traits("Dual Mono",  true,  m_Dict->ul(MDD_IMFAudioSoundfield_DM))));
1791   m_LabelMap.insert(pair("DNS",   label_traits("Discrete Numbered Sources", true,  m_Dict->ul(MDD_IMFAudioSoundfield_DNS))));
1792   m_LabelMap.insert(pair("30",    label_traits("3.0",  true,  m_Dict->ul(MDD_IMFAudioSoundfield_30))));
1793   m_LabelMap.insert(pair("40",    label_traits("4.0",  true,  m_Dict->ul(MDD_IMFAudioSoundfield_40))));
1794   m_LabelMap.insert(pair("50",    label_traits("5.0",  true,  m_Dict->ul(MDD_IMFAudioSoundfield_50))));
1795   m_LabelMap.insert(pair("60",    label_traits("6.0",  true,  m_Dict->ul(MDD_IMFAudioSoundfield_60))));
1796   m_LabelMap.insert(pair("70",    label_traits("7.0DS",  true,  m_Dict->ul(MDD_IMFAudioSoundfield_70))));
1797   m_LabelMap.insert(pair("LtRt",  label_traits("Lt-Rt",true,  m_Dict->ul(MDD_IMFAudioSoundfield_LtRt))));
1798   m_LabelMap.insert(pair("51Ex",  label_traits("5.1EX",true,  m_Dict->ul(MDD_IMFAudioSoundfield_51Ex))));
1799   m_LabelMap.insert(pair("HA",    label_traits("Hearing Accessibility",  true,  m_Dict->ul(MDD_IMFAudioSoundfield_HI))));
1800   m_LabelMap.insert(pair("VA",   label_traits("Visual Accessibility", true,  m_Dict->ul(MDD_IMFAudioSoundfield_VIN))));
1801
1802   // assemble the  set of Numbered Source Channel labels
1803   char name_buf[64], symbol_buf[64];
1804   byte_t ul_buf[16];
1805   memcpy(ul_buf, m_Dict->ul(MDD_IMFNumberedSourceChannel), 16);
1806
1807   for ( int i = 1; i < 128; ++i )
1808     {
1809       snprintf(name_buf, 64, "Numbered Source Channel %03d", i);
1810       snprintf(symbol_buf, 64, "NSC%03d", i);
1811       ul_buf[13] = i;
1812       m_LabelMap.insert(pair(symbol_buf, label_traits(name_buf, true, UL(ul_buf))));
1813     }
1814 }
1815
1816 //
1817 bool
1818 ASDCP::MXF::GetEditRateFromFP(ASDCP::MXF::OP1aHeader& header, ASDCP::Rational& edit_rate)
1819 {
1820   bool has_first_item = false;
1821
1822   MXF::InterchangeObject* temp_item;
1823   std::list<MXF::InterchangeObject*> temp_items;
1824
1825   Result_t result = header.GetMDObjectsByType(DefaultCompositeDict().ul(MDD_SourcePackage), temp_items);
1826
1827   if ( KM_FAILURE(result) )
1828     {
1829       DefaultLogSink().Error("The MXF header does not contain a FilePackage item.\n");
1830       return false;
1831     }
1832
1833   if ( temp_items.size() != 1 )
1834     {
1835       DefaultLogSink().Error("The MXF header must contain one FilePackage item, found %d.\n", temp_items.size());
1836       return false;
1837     }
1838
1839   char buf[64];
1840   MXF::Array<UUID>::const_iterator i;
1841   MXF::SourcePackage *source_package = dynamic_cast<MXF::SourcePackage*>(temp_items.front());
1842   assert(source_package);
1843
1844   for ( i = source_package->Tracks.begin(); i != source_package->Tracks.end(); ++i )
1845     {
1846       // Track
1847       result = header.GetMDObjectByID(*i, &temp_item);
1848
1849       if ( KM_FAILURE(result) )
1850         {
1851           DefaultLogSink().Error("The MXF header is incomplete: strong reference %s leads nowhere.\n",
1852                                  i->EncodeHex(buf, 64));
1853           return false;
1854         }
1855
1856       MXF::Track *track = dynamic_cast<MXF::Track*>(temp_item);
1857
1858       if ( track == 0 )
1859         {
1860           DefaultLogSink().Error("The MXF header is incomplete: %s is not a Track item.\n",
1861                                  i->EncodeHex(buf, 64));
1862           return false;
1863         }
1864
1865       // Sequence
1866       result = header.GetMDObjectByID(track->Sequence, &temp_item);
1867
1868       if ( KM_FAILURE(result) )
1869         {
1870           DefaultLogSink().Error("The MXF header is incomplete: strong reference %s leads nowhere.\n",
1871                                  i->EncodeHex(buf, 64));
1872           return false;
1873         }
1874
1875       MXF::Sequence *sequence = dynamic_cast<MXF::Sequence*>(temp_item);
1876
1877       if ( sequence == 0 )
1878         {
1879           DefaultLogSink().Error("The MXF header is incomplete: %s is not a Sequence item.\n",
1880                                  track->Sequence.get().EncodeHex(buf, 64));
1881           return false;
1882         }
1883
1884       if ( sequence->StructuralComponents.size() != 1 )
1885         {
1886           DefaultLogSink().Error("The Sequence item must contain one reference to an esence item, found %d.\n",
1887                                  sequence->StructuralComponents.size());
1888           return false;
1889         }
1890
1891       // SourceClip
1892       result = header.GetMDObjectByID(sequence->StructuralComponents.front(), &temp_item);
1893
1894       if ( KM_FAILURE(result) )
1895         {
1896           DefaultLogSink().Error("The MXF header is incomplete: strong reference %s leads nowhere.\n",
1897                                  sequence->StructuralComponents.front().EncodeHex(buf, 64));
1898           return false;
1899         }
1900
1901       if ( temp_item->IsA(DefaultCompositeDict().ul(MDD_SourceClip)) )
1902         {
1903           MXF::SourceClip *source_clip = dynamic_cast<MXF::SourceClip*>(temp_item);
1904
1905           if ( source_clip == 0 )
1906             {
1907               DefaultLogSink().Error("The MXF header is incomplete: %s is not a SourceClip item.\n",
1908                                      sequence->StructuralComponents.front().EncodeHex(buf, 64));
1909               return false;
1910             }
1911
1912           if ( ! has_first_item )
1913             {
1914               edit_rate = track->EditRate;
1915               has_first_item = true;
1916             }
1917           else if ( edit_rate != track->EditRate )
1918             {
1919               DefaultLogSink().Error("The MXF header is incomplete: %s EditRate value does not match others in the file.\n",
1920                                      sequence->StructuralComponents.front().EncodeHex(buf, 64));
1921               return false;
1922             }
1923         }
1924       else if ( ! temp_item->IsA(DefaultCompositeDict().ul(MDD_TimecodeComponent)) )
1925         {
1926           DefaultLogSink().Error("Reference from Sequence to an unexpected type: %s.\n", temp_item->ObjectName());
1927           return false;
1928         }
1929     }
1930
1931   return true;
1932 }
1933
1934
1935 //
1936 // end MXF.cpp
1937 //