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