Merge pull request #9 from dcbullock/master
[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_KLV_CODING(__LINE__, __FILE__);
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_KLV_CODING(__LINE__, __FILE__);
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_KLV_CODING(__LINE__, __FILE__);
1397             }
1398
1399           TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup);
1400           result = InitFromTLVSet(MemRDR);
1401         }
1402     }
1403   else
1404     {
1405       result = KLVPacket::InitFromBuffer(p, l);
1406     }
1407   
1408   return result;
1409 }
1410
1411 //
1412 ASDCP::Result_t
1413 ASDCP::MXF::InterchangeObject::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
1414 {
1415   if ( ! m_UL.HasValue() )
1416     return RESULT_STATE;
1417
1418   TLVWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length, m_Lookup);
1419   Result_t result = WriteToTLVSet(MemWRT);
1420
1421   if ( ASDCP_SUCCESS(result) )
1422     {
1423       ui32_t packet_length = MemWRT.Length();
1424       result = WriteKLToBuffer(Buffer, packet_length);
1425
1426       if ( ASDCP_SUCCESS(result) )
1427         Buffer.Size(Buffer.Size() + packet_length);
1428     }
1429
1430   return result;
1431 }
1432
1433 //
1434 void
1435 ASDCP::MXF::InterchangeObject::Dump(FILE* stream)
1436 {
1437   char identbuf[IdentBufferLen];
1438
1439   fputc('\n', stream);
1440   KLVPacket::Dump(stream, *m_Dict, false);
1441   fprintf(stream, "             InstanceUID = %s\n",  InstanceUID.EncodeHex(identbuf, IdentBufferLen));
1442
1443   if ( ! GenerationUID.empty() )
1444     fprintf(stream, "           GenerationUID = %s\n",  GenerationUID.get().EncodeHex(identbuf, IdentBufferLen));
1445 }
1446
1447 //
1448 bool
1449 ASDCP::MXF::InterchangeObject::IsA(const byte_t* label)
1450 {
1451   if ( m_KLLength == 0 || m_KeyStart == 0 )
1452     return false;
1453
1454   return ( memcmp(label, m_KeyStart, SMPTE_UL_LENGTH) == 0 );
1455 }
1456
1457
1458 //------------------------------------------------------------------------------------------
1459 struct FactoryCompareUL
1460 {
1461     bool operator()(const ASDCP::UL& lhs, const ASDCP::UL& rhs) const
1462     {
1463         ui32_t test_size = lhs.Size() < rhs.Size() ? lhs.Size() : rhs.Size();
1464
1465         for (ui32_t i = 0; i < test_size; i++)
1466         {
1467             if (i == 7) continue; // skip version to be symmetrical with UL::operator==
1468             if (lhs.Value()[i] != rhs.Value()[i])
1469                 return lhs.Value()[i] < rhs.Value()[i];
1470         }
1471
1472         return false;
1473     }
1474 };
1475
1476 typedef std::map<ASDCP::UL, ASDCP::MXF::MXFObjectFactory_t, FactoryCompareUL>FactoryMap_t;
1477 typedef FactoryMap_t::iterator FLi_t;
1478
1479 //
1480 class FactoryList : public FactoryMap_t
1481 {
1482   Kumu::Mutex m_Lock;
1483
1484 public:
1485   FactoryList() {}
1486   ~FactoryList() {}
1487
1488   bool Empty() {
1489     Kumu::AutoMutex BlockLock(m_Lock);
1490     return empty();
1491   }
1492
1493   FLi_t Find(const byte_t* label) {
1494     Kumu::AutoMutex BlockLock(m_Lock);
1495     return find(label);
1496   }
1497
1498   FLi_t End() {
1499     Kumu::AutoMutex BlockLock(m_Lock);
1500     return end();
1501   }
1502
1503   void Insert(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory) {
1504     Kumu::AutoMutex BlockLock(m_Lock);
1505     insert(FactoryList::value_type(label, factory));
1506   }
1507 };
1508
1509 //
1510 static FactoryList s_FactoryList;
1511 static Kumu::Mutex s_InitLock;
1512 static bool        s_TypesInit = false;
1513
1514
1515 //
1516 void
1517 ASDCP::MXF::SetObjectFactory(const ASDCP::UL& label, ASDCP::MXF::MXFObjectFactory_t factory)
1518 {
1519   s_FactoryList.Insert(label, factory);
1520 }
1521
1522 //
1523 ASDCP::MXF::InterchangeObject*
1524 ASDCP::MXF::CreateObject(const Dictionary*& Dict, const UL& label)
1525 {
1526   if ( ! s_TypesInit )
1527     {
1528       Kumu::AutoMutex BlockLock(s_InitLock);
1529
1530       if ( ! s_TypesInit )
1531         {
1532           MXF::Metadata_InitTypes(Dict);
1533           s_TypesInit = true;
1534         }
1535     }
1536
1537   FLi_t i = s_FactoryList.find(label);
1538
1539   if ( i == s_FactoryList.end() )
1540     return new InterchangeObject(Dict);
1541
1542   return i->second(Dict);
1543 }
1544
1545
1546 //------------------------------------------------------------------------------------------
1547
1548 //
1549 bool
1550 ASDCP::MXF::decode_mca_string(const std::string& s, const mca_label_map_t& labels, const Dictionary*& dict, const std::string& language,
1551                               InterchangeObject_list_t& descriptor_list, ui32_t& channel_count)
1552 {
1553   std::string symbol_buf;
1554   channel_count = 0;
1555   ASDCP::MXF::SoundfieldGroupLabelSubDescriptor *current_soundfield = 0, *prev_soundfield = 0;
1556   std::string::const_iterator i;
1557
1558   for ( i = s.begin(); i != s.end(); ++i )
1559     {
1560       if ( *i == '(' )
1561         {
1562           if ( current_soundfield != 0 && symbol_buf.empty() )
1563             {
1564               // appending to the existing soundfield group
1565               continue;
1566             }
1567           else if ( current_soundfield != 0 )
1568             {
1569               DefaultLogSink().Error("Encountered '(', already processing a soundfield group.\n");
1570               return false;
1571             }
1572           else if ( symbol_buf.empty() )
1573             {
1574               if ( prev_soundfield != 0 )
1575                 {
1576                   current_soundfield = prev_soundfield;
1577                   // appending to the existing soundfield group
1578                   continue;
1579                 }
1580               else
1581                 {
1582                   DefaultLogSink().Error("Encountered '(', without leading soundfield group symbol.\n");
1583                   return false;
1584                 }
1585             }
1586
1587           mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1588
1589           if ( i == labels.end() )
1590             {
1591               DefaultLogSink().Error("Unknown symbol: '%s'\n", symbol_buf.c_str());
1592               return false;
1593             }
1594       
1595           if ( i->second.ul.Value()[10] != 2 ) // magic depends on UL "Essence Facet" byte (see ST 428-12)
1596             {
1597               DefaultLogSink().Error("Not a soundfield group symbol: '%s'\n", symbol_buf.c_str());
1598               return false;
1599             }
1600
1601           current_soundfield = new ASDCP::MXF::SoundfieldGroupLabelSubDescriptor(dict);
1602           GenRandomValue(current_soundfield->MCALinkID);
1603
1604           current_soundfield->MCATagSymbol = (i->second.requires_prefix ? "sg" : "") + i->first;
1605           current_soundfield->MCATagName = i->second.tag_name;
1606           current_soundfield->RFC5646SpokenLanguage = language;
1607           current_soundfield->MCALabelDictionaryID = i->second.ul;
1608           descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(current_soundfield));
1609           prev_soundfield = current_soundfield;
1610           symbol_buf.clear();
1611         }
1612       else if ( *i == ')' )
1613         {
1614           if ( current_soundfield == 0 )
1615             {
1616               DefaultLogSink().Error("Encountered ')', not currently processing a soundfield group.\n");
1617               return false;
1618             }
1619
1620           if ( symbol_buf.empty() )
1621             {
1622               DefaultLogSink().Error("Soundfield group description contains no channels.\n");
1623               return false;
1624             }
1625
1626           mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1627       
1628           if ( i == labels.end() )
1629             {
1630               DefaultLogSink().Error("Unknown symbol: '%s'\n", symbol_buf.c_str());
1631               return false;
1632             }
1633
1634           assert(current_soundfield);
1635
1636           ASDCP::MXF::AudioChannelLabelSubDescriptor *channel_descr =
1637             new ASDCP::MXF::AudioChannelLabelSubDescriptor(dict);
1638           GenRandomValue(channel_descr->MCALinkID);
1639
1640           channel_descr->SoundfieldGroupLinkID = current_soundfield->MCALinkID;
1641           channel_descr->MCAChannelID = channel_count++ + 1;
1642           channel_descr->MCATagSymbol = (i->second.requires_prefix ? "ch" : "") + i->first;
1643           channel_descr->MCATagName = i->second.tag_name;
1644           channel_descr->RFC5646SpokenLanguage = language;
1645           channel_descr->MCALabelDictionaryID = i->second.ul;
1646           descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(channel_descr));
1647           symbol_buf.clear();
1648           current_soundfield = 0;
1649         }
1650       else if ( *i == ',' )
1651         {
1652           if ( ! symbol_buf.empty() && ! symbol_buf.compare("-") )
1653             {
1654               channel_count++;
1655               symbol_buf.clear();
1656             }
1657           else if ( ! symbol_buf.empty() )
1658             {
1659               mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1660
1661               if ( i == labels.end() )
1662                 {
1663                   DefaultLogSink().Error("Unknown symbol: '%s'\n", symbol_buf.c_str());
1664                   return false;
1665                 }
1666
1667               if ( i->second.ul.Value()[10] != 1 ) // magic depends on UL "Essence Facet" byte (see ST 428-12)
1668                 {
1669                   DefaultLogSink().Error("Not a channel symbol: '%s'\n", symbol_buf.c_str());
1670                   return false;
1671                 }
1672
1673               ASDCP::MXF::AudioChannelLabelSubDescriptor *channel_descr =
1674                 new ASDCP::MXF::AudioChannelLabelSubDescriptor(dict);
1675               GenRandomValue(channel_descr->MCALinkID);
1676
1677               if ( current_soundfield != 0 )
1678                 {
1679                   channel_descr->SoundfieldGroupLinkID = current_soundfield->MCALinkID;
1680                 }
1681
1682               channel_descr->MCAChannelID = channel_count++ + 1;
1683               channel_descr->MCATagSymbol = (i->second.requires_prefix ? "ch" : "") + i->first;
1684               channel_descr->MCATagName = i->second.tag_name;
1685               channel_descr->RFC5646SpokenLanguage = language;
1686               channel_descr->MCALabelDictionaryID = i->second.ul;
1687               descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(channel_descr));
1688               symbol_buf.clear();
1689             }
1690         }
1691       else if ( *i == '-' || isalnum(*i) )
1692         {
1693           symbol_buf += *i;
1694         }
1695       else if ( ! isspace(*i) )
1696         {
1697           DefaultLogSink().Error("Unexpected character '%c'.\n", *i);
1698           return false;
1699         }
1700     }
1701
1702   if ( ! symbol_buf.empty() && ! symbol_buf.compare("-")  )
1703     {
1704       channel_count++;
1705     }
1706   else if ( ! symbol_buf.empty() )
1707     {
1708       mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1709       
1710       if ( i == labels.end() )
1711         {
1712           DefaultLogSink().Error("Unknown symbol: '%s'\n", symbol_buf.c_str());
1713           return false;
1714         }
1715
1716       ASDCP::MXF::AudioChannelLabelSubDescriptor *channel_descr =
1717         new ASDCP::MXF::AudioChannelLabelSubDescriptor(dict);
1718       GenRandomValue(channel_descr->MCALinkID);
1719
1720       if ( current_soundfield != 0 )
1721         {
1722           channel_descr->SoundfieldGroupLinkID = current_soundfield->MCALinkID;
1723         }
1724
1725       channel_descr->MCAChannelID = channel_count++ + 1;
1726       channel_descr->MCATagSymbol = (i->second.requires_prefix ? "ch" : "") + i->first;
1727       channel_descr->MCATagName = i->second.tag_name;
1728       channel_descr->RFC5646SpokenLanguage = language;
1729       channel_descr->MCALabelDictionaryID = i->second.ul;
1730       descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(channel_descr));
1731     }
1732
1733   return true;
1734 }
1735
1736 //
1737 ASDCP::MXF::ASDCP_MCAConfigParser::ASDCP_MCAConfigParser(const Dictionary*& d) : m_Dict(d), m_ChannelCount(0)
1738 {
1739   typedef mca_label_map_t::value_type pair;
1740   m_LabelMap.insert(pair("L",     label_traits("Left"                              , true,  m_Dict->ul(MDD_DCAudioChannel_L))));
1741   m_LabelMap.insert(pair("R",     label_traits("Right"                             , true,  m_Dict->ul(MDD_DCAudioChannel_R))));
1742   m_LabelMap.insert(pair("C",     label_traits("Center"                            , true,  m_Dict->ul(MDD_DCAudioChannel_C))));
1743   m_LabelMap.insert(pair("LFE",   label_traits("LFE"                               , true,  m_Dict->ul(MDD_DCAudioChannel_LFE))));
1744   m_LabelMap.insert(pair("Ls",    label_traits("Left Surround"                     , true,  m_Dict->ul(MDD_DCAudioChannel_Ls))));
1745   m_LabelMap.insert(pair("Rs",    label_traits("Right Surround"                    , true,  m_Dict->ul(MDD_DCAudioChannel_Rs))));
1746   m_LabelMap.insert(pair("Lss",   label_traits("Left Side Surround"                , true,  m_Dict->ul(MDD_DCAudioChannel_Lss))));
1747   m_LabelMap.insert(pair("Rss",   label_traits("Right Side Surround"               , true,  m_Dict->ul(MDD_DCAudioChannel_Rss))));
1748   m_LabelMap.insert(pair("Lrs",   label_traits("Left Rear Surround"                , true,  m_Dict->ul(MDD_DCAudioChannel_Lrs))));
1749   m_LabelMap.insert(pair("Rrs",   label_traits("Right Rear Surround"               , true,  m_Dict->ul(MDD_DCAudioChannel_Rrs))));
1750   m_LabelMap.insert(pair("Lc",    label_traits("Left Center"                       , true,  m_Dict->ul(MDD_DCAudioChannel_Lc))));
1751   m_LabelMap.insert(pair("Rc",    label_traits("Right Center"                      , true,  m_Dict->ul(MDD_DCAudioChannel_Rc))));
1752   m_LabelMap.insert(pair("Cs",    label_traits("Center Surround"                   , true,  m_Dict->ul(MDD_DCAudioChannel_Cs))));
1753   m_LabelMap.insert(pair("HI",    label_traits("Hearing Impaired"                  , true,  m_Dict->ul(MDD_DCAudioChannel_HI))));
1754   m_LabelMap.insert(pair("VIN",   label_traits("Visually Impaired-Narrative"       , true,  m_Dict->ul(MDD_DCAudioChannel_VIN))));
1755   m_LabelMap.insert(pair("51",    label_traits("5.1"                               , true,  m_Dict->ul(MDD_DCAudioSoundfield_51))));
1756   m_LabelMap.insert(pair("71",    label_traits("7.1DS"                             , true,  m_Dict->ul(MDD_DCAudioSoundfield_71))));
1757   m_LabelMap.insert(pair("SDS",   label_traits("7.1SDS"                            , true,  m_Dict->ul(MDD_DCAudioSoundfield_SDS))));
1758   m_LabelMap.insert(pair("61",    label_traits("6.1"                               , true,  m_Dict->ul(MDD_DCAudioSoundfield_61))));
1759   m_LabelMap.insert(pair("M",     label_traits("1.0 Monaural"                      , true,  m_Dict->ul(MDD_DCAudioSoundfield_M))));
1760   m_LabelMap.insert(pair("FSKSync",   label_traits("FSK Sync"                      , true,  m_Dict->ul(MDD_DCAudioChannel_FSKSyncSignalChannel))));
1761   m_LabelMap.insert(pair("DBOX",  label_traits("D-BOX Motion Code Primary Stream"  , false, m_Dict->ul(MDD_DBOXMotionCodePrimaryStream))));
1762   m_LabelMap.insert(pair("DBOX2", label_traits("D-BOX Motion Code Secondary Stream", false, m_Dict->ul(MDD_DBOXMotionCodeSecondaryStream))));
1763 }
1764
1765 //
1766 ui32_t
1767 ASDCP::MXF::ASDCP_MCAConfigParser::ChannelCount() const
1768 {
1769   return m_ChannelCount;
1770 }
1771
1772 // 51(L,R,C,LFE,Ls,Rs),HI,VIN
1773 bool
1774 ASDCP::MXF::ASDCP_MCAConfigParser::DecodeString(const std::string& s, const std::string& language)
1775 {
1776   return decode_mca_string(s, m_LabelMap, m_Dict, language, *this, m_ChannelCount);
1777 }
1778
1779 // ST(L,R),DNS(NSC001,NSC002),-,VIN
1780 ASDCP::MXF::AS02_MCAConfigParser::AS02_MCAConfigParser(const Dictionary*& d) : ASDCP::MXF::ASDCP_MCAConfigParser(d)
1781 {
1782   typedef mca_label_map_t::value_type pair;
1783   m_LabelMap.insert(pair("M1",    label_traits("Mono One",  true,  m_Dict->ul(MDD_IMFAudioChannel_M1))));
1784   m_LabelMap.insert(pair("M2",    label_traits("Mono Two",  true,  m_Dict->ul(MDD_IMFAudioChannel_M2))));
1785   m_LabelMap.insert(pair("Lt",    label_traits("Left Total",  true,  m_Dict->ul(MDD_IMFAudioChannel_Lt))));
1786   m_LabelMap.insert(pair("Rt",    label_traits("Right Total",  true,  m_Dict->ul(MDD_IMFAudioChannel_Rt))));
1787   m_LabelMap.insert(pair("Lst",   label_traits("Left Surround Total", true,  m_Dict->ul(MDD_IMFAudioChannel_Lst))));
1788   m_LabelMap.insert(pair("Rst",   label_traits("Right Surround Total", true,  m_Dict->ul(MDD_IMFAudioChannel_Rst))));
1789   m_LabelMap.insert(pair("S",     label_traits("Surround",   true,  m_Dict->ul(MDD_IMFAudioChannel_S))));
1790   m_LabelMap.insert(pair("ST",    label_traits("Standard Stereo",  true,  m_Dict->ul(MDD_IMFAudioSoundfield_ST))));
1791   m_LabelMap.insert(pair("DM",    label_traits("Dual Mono",  true,  m_Dict->ul(MDD_IMFAudioSoundfield_DM))));
1792   m_LabelMap.insert(pair("DNS",   label_traits("Discrete Numbered Sources", true,  m_Dict->ul(MDD_IMFAudioSoundfield_DNS))));
1793   m_LabelMap.insert(pair("30",    label_traits("3.0",  true,  m_Dict->ul(MDD_IMFAudioSoundfield_30))));
1794   m_LabelMap.insert(pair("40",    label_traits("4.0",  true,  m_Dict->ul(MDD_IMFAudioSoundfield_40))));
1795   m_LabelMap.insert(pair("50",    label_traits("5.0",  true,  m_Dict->ul(MDD_IMFAudioSoundfield_50))));
1796   m_LabelMap.insert(pair("60",    label_traits("6.0",  true,  m_Dict->ul(MDD_IMFAudioSoundfield_60))));
1797   m_LabelMap.insert(pair("70",    label_traits("7.0DS",  true,  m_Dict->ul(MDD_IMFAudioSoundfield_70))));
1798   m_LabelMap.insert(pair("LtRt",  label_traits("Lt-Rt",true,  m_Dict->ul(MDD_IMFAudioSoundfield_LtRt))));
1799   m_LabelMap.insert(pair("51Ex",  label_traits("5.1EX",true,  m_Dict->ul(MDD_IMFAudioSoundfield_51Ex))));
1800   m_LabelMap.insert(pair("HA",    label_traits("Hearing Accessibility",  true,  m_Dict->ul(MDD_IMFAudioSoundfield_HI))));
1801   m_LabelMap.insert(pair("VA",   label_traits("Visual Accessibility", true,  m_Dict->ul(MDD_IMFAudioSoundfield_VIN))));
1802
1803   // assemble the  set of Numbered Source Channel labels
1804   char name_buf[64], symbol_buf[64];
1805   byte_t ul_buf[16];
1806   memcpy(ul_buf, m_Dict->ul(MDD_IMFNumberedSourceChannel), 16);
1807
1808   for ( int i = 1; i < 128; ++i )
1809     {
1810       snprintf(name_buf, 64, "Numbered Source Channel %03d", i);
1811       snprintf(symbol_buf, 64, "NSC%03d", i);
1812       ul_buf[13] = i;
1813       m_LabelMap.insert(pair(symbol_buf, label_traits(name_buf, true, UL(ul_buf))));
1814     }
1815 }
1816
1817 //
1818 bool
1819 ASDCP::MXF::GetEditRateFromFP(ASDCP::MXF::OP1aHeader& header, ASDCP::Rational& edit_rate)
1820 {
1821   bool has_first_item = false;
1822
1823   MXF::InterchangeObject* temp_item;
1824   std::list<MXF::InterchangeObject*> temp_items;
1825
1826   Result_t result = header.GetMDObjectsByType(DefaultCompositeDict().ul(MDD_SourcePackage), temp_items);
1827
1828   if ( KM_FAILURE(result) )
1829     {
1830       DefaultLogSink().Error("The MXF header does not contain a FilePackage item.\n");
1831       return false;
1832     }
1833
1834   if ( temp_items.size() != 1 )
1835     {
1836       DefaultLogSink().Error("The MXF header must contain one FilePackage item, found %d.\n", temp_items.size());
1837       return false;
1838     }
1839
1840   char buf[64];
1841   MXF::Array<UUID>::const_iterator i;
1842   MXF::SourcePackage *source_package = dynamic_cast<MXF::SourcePackage*>(temp_items.front());
1843   assert(source_package);
1844
1845   for ( i = source_package->Tracks.begin(); i != source_package->Tracks.end(); ++i )
1846     {
1847       // Track
1848       result = header.GetMDObjectByID(*i, &temp_item);
1849
1850       if ( KM_FAILURE(result) )
1851         {
1852           DefaultLogSink().Error("The MXF header is incomplete: strong reference %s leads nowhere.\n",
1853                                  i->EncodeHex(buf, 64));
1854           return false;
1855         }
1856
1857       MXF::Track *track = dynamic_cast<MXF::Track*>(temp_item);
1858
1859       if ( track == 0 )
1860         {
1861           DefaultLogSink().Error("The MXF header is incomplete: %s is not a Track item.\n",
1862                                  i->EncodeHex(buf, 64));
1863           return false;
1864         }
1865
1866       // Sequence
1867       result = header.GetMDObjectByID(track->Sequence, &temp_item);
1868
1869       if ( KM_FAILURE(result) )
1870         {
1871           DefaultLogSink().Error("The MXF header is incomplete: strong reference %s leads nowhere.\n",
1872                                  i->EncodeHex(buf, 64));
1873           return false;
1874         }
1875
1876       MXF::Sequence *sequence = dynamic_cast<MXF::Sequence*>(temp_item);
1877
1878       if ( sequence == 0 )
1879         {
1880           DefaultLogSink().Error("The MXF header is incomplete: %s is not a Sequence item.\n",
1881                                  track->Sequence.get().EncodeHex(buf, 64));
1882           return false;
1883         }
1884
1885       if ( sequence->StructuralComponents.size() != 1 )
1886         {
1887           DefaultLogSink().Error("The Sequence item must contain one reference to an esence item, found %d.\n",
1888                                  sequence->StructuralComponents.size());
1889           return false;
1890         }
1891
1892       // SourceClip
1893       result = header.GetMDObjectByID(sequence->StructuralComponents.front(), &temp_item);
1894
1895       if ( KM_FAILURE(result) )
1896         {
1897           DefaultLogSink().Error("The MXF header is incomplete: strong reference %s leads nowhere.\n",
1898                                  sequence->StructuralComponents.front().EncodeHex(buf, 64));
1899           return false;
1900         }
1901
1902       if ( temp_item->IsA(DefaultCompositeDict().ul(MDD_SourceClip)) )
1903         {
1904           MXF::SourceClip *source_clip = dynamic_cast<MXF::SourceClip*>(temp_item);
1905
1906           if ( source_clip == 0 )
1907             {
1908               DefaultLogSink().Error("The MXF header is incomplete: %s is not a SourceClip item.\n",
1909                                      sequence->StructuralComponents.front().EncodeHex(buf, 64));
1910               return false;
1911             }
1912
1913           if ( ! has_first_item )
1914             {
1915               edit_rate = track->EditRate;
1916               has_first_item = true;
1917             }
1918           else if ( edit_rate != track->EditRate )
1919             {
1920               DefaultLogSink().Error("The MXF header is incomplete: %s EditRate value does not match others in the file.\n",
1921                                      sequence->StructuralComponents.front().EncodeHex(buf, 64));
1922               return false;
1923             }
1924         }
1925       else if ( ! temp_item->IsA(DefaultCompositeDict().ul(MDD_TimecodeComponent)) )
1926         {
1927           DefaultLogSink().Error("Reference from Sequence to an unexpected type: %s.\n", temp_item->ObjectName());
1928           return false;
1929         }
1930     }
1931
1932   return true;
1933 }
1934
1935
1936 //
1937 // end MXF.cpp
1938 //