o Added options -c -n, -q to as-02-wrap (IMF "color system")
[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 }
612
613 //
614 ASDCP::Result_t
615 ASDCP::MXF::Preface::InitFromTLVSet(TLVReader& TLVSet)
616 {
617   Result_t result = InterchangeObject::InitFromTLVSet(TLVSet);
618   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, LastModifiedDate));
619   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi16(OBJ_READ_ARGS(Preface, Version));
620   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi32(OBJ_READ_ARGS_OPT(Preface, ObjectModelVersion));
621   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS_OPT(Preface, PrimaryPackage));
622   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, Identifications));
623   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, ContentStorage));
624   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, OperationalPattern));
625   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, EssenceContainers));
626   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, DMSchemes));
627   return result;
628 }
629
630 //
631 ASDCP::Result_t
632 ASDCP::MXF::Preface::WriteToTLVSet(TLVWriter& TLVSet)
633 {
634   Result_t result = InterchangeObject::WriteToTLVSet(TLVSet);
635   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, LastModifiedDate));
636   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteUi16(OBJ_WRITE_ARGS(Preface, Version));
637   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteUi32(OBJ_WRITE_ARGS_OPT(Preface, ObjectModelVersion));
638   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteObject(OBJ_WRITE_ARGS_OPT(Preface, PrimaryPackage));
639   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, Identifications));
640   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, ContentStorage));
641   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, OperationalPattern));
642   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, EssenceContainers));
643   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, DMSchemes));
644   return result;
645 }
646
647 //
648 ASDCP::Result_t
649 ASDCP::MXF::Preface::InitFromBuffer(const byte_t* p, ui32_t l)
650 {
651   return InterchangeObject::InitFromBuffer(p, l);
652 }
653
654 //
655 ASDCP::Result_t
656 ASDCP::MXF::Preface::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
657 {
658   return InterchangeObject::WriteToBuffer(Buffer);
659 }
660
661 //
662 void
663 ASDCP::MXF::Preface::Dump(FILE* stream)
664 {
665   char identbuf[IdentBufferLen];
666
667   if ( stream == 0 )
668     stream = stderr;
669
670   InterchangeObject::Dump(stream);
671   fprintf(stream, "  %22s = %s\n",  "LastModifiedDate", LastModifiedDate.EncodeString(identbuf, IdentBufferLen));
672   fprintf(stream, "  %22s = %hu\n", "Version", Version);
673
674   if ( ! ObjectModelVersion.empty() )
675     fprintf(stream, "  %22s = %u\n",  "ObjectModelVersion", ObjectModelVersion.get());
676
677   if ( ! PrimaryPackage.empty() )
678     fprintf(stream, "  %22s = %s\n",  "PrimaryPackage", PrimaryPackage.get().EncodeHex(identbuf, IdentBufferLen));
679
680   fprintf(stream, "  %22s:\n", "Identifications");  Identifications.Dump(stream);
681   fprintf(stream, "  %22s = %s\n",  "ContentStorage", ContentStorage.EncodeHex(identbuf, IdentBufferLen));
682   fprintf(stream, "  %22s = %s\n",  "OperationalPattern", OperationalPattern.EncodeString(identbuf, IdentBufferLen));
683   fprintf(stream, "  %22s:\n", "EssenceContainers");  EssenceContainers.Dump(stream);
684   fprintf(stream, "  %22s:\n", "DMSchemes");  DMSchemes.Dump(stream);
685 }
686
687 //------------------------------------------------------------------------------------------
688 //
689
690 ASDCP::MXF::OP1aHeader::OP1aHeader(const Dictionary*& d) : Partition(d), m_Dict(d), m_Primer(d), m_Preface(0) {}
691 ASDCP::MXF::OP1aHeader::~OP1aHeader() {}
692
693 //
694 ASDCP::Result_t
695 ASDCP::MXF::OP1aHeader::InitFromFile(const Kumu::FileReader& Reader)
696 {
697   Result_t result = Partition::InitFromFile(Reader);
698
699   if ( ASDCP_FAILURE(result) )
700     return result;
701
702   if ( m_Dict == &DefaultCompositeDict() )
703     {
704       // select more explicit dictionary if one is available
705       if ( OperationalPattern.MatchExact(MXFInterop_OPAtom_Entry().ul) )
706         {
707           m_Dict = &DefaultInteropDict();
708         }
709       else if ( OperationalPattern.MatchExact(SMPTE_390_OPAtom_Entry().ul) )
710         {
711           m_Dict = &DefaultSMPTEDict();
712         }
713     }
714
715   // slurp up the remainder of the header
716   if ( HeaderByteCount == 0 )
717     {
718       DefaultLogSink().Warn("MXF file contents incomplete.\n");
719       return RESULT_KLV_CODING(__LINE__, __FILE__);
720     }
721   else if ( HeaderByteCount < 1024 )
722     {
723       DefaultLogSink().Warn("Improbably small HeaderByteCount value: %qu\n", HeaderByteCount);
724     }
725   else if (HeaderByteCount > ( 4 * Kumu::Megabyte ) )
726     {
727       DefaultLogSink().Warn("Improbably huge HeaderByteCount value: %qu\n", HeaderByteCount);
728     }
729   
730   result = m_HeaderData.Capacity(Kumu::xmin(4*Kumu::Megabyte, static_cast<ui32_t>(HeaderByteCount)));
731
732   if ( ASDCP_SUCCESS(result) )
733     {
734       ui32_t read_count;
735       result = Reader.Read(m_HeaderData.Data(), m_HeaderData.Capacity(), &read_count);
736
737       if ( ASDCP_FAILURE(result) )
738         {
739           DefaultLogSink().Error("OP1aHeader::InitFromFile, read failed.\n");
740           return result;
741         }
742
743       if ( read_count != m_HeaderData.Capacity() )
744         {
745           DefaultLogSink().Error("Short read of OP-Atom header metadata; wanted %u, got %u.\n",
746                                  m_HeaderData.Capacity(), read_count);
747           return RESULT_KLV_CODING(__LINE__, __FILE__);
748         }
749     }
750
751   if ( ASDCP_SUCCESS(result) )
752     result = InitFromBuffer(m_HeaderData.RoData(), m_HeaderData.Capacity());
753
754   return result;
755 }
756
757 //
758 ASDCP::Result_t
759 ASDCP::MXF::OP1aHeader::InitFromPartitionBuffer(const byte_t* p, ui32_t l)
760 {
761   Result_t result = KLVPacket::InitFromBuffer(p, l);
762
763   if ( ASDCP_SUCCESS(result) )
764     result = Partition::InitFromBuffer(m_ValueStart, m_ValueLength); // test UL and OP
765
766   if ( ASDCP_SUCCESS(result) )
767     {
768       ui32_t pp_len = KLVPacket::PacketLength();
769       result = InitFromBuffer(p + pp_len, l - pp_len);
770     }
771
772   return result;
773 }
774
775 //
776 ASDCP::Result_t
777 ASDCP::MXF::OP1aHeader::InitFromBuffer(const byte_t* p, ui32_t l)
778 {
779   assert(m_Dict);
780   Result_t result = RESULT_OK;
781   const byte_t* end_p = p + l;
782
783   while ( ASDCP_SUCCESS(result) && p < end_p )
784     {
785       // parse the packets and index them by uid, discard KLVFill items
786       InterchangeObject* object = CreateObject(m_Dict, p);
787       assert(object);
788
789       object->m_Lookup = &m_Primer;
790       result = object->InitFromBuffer(p, end_p - p);
791
792       const byte_t* redo_p = p;
793       p += object->PacketLength();
794
795       if ( ASDCP_SUCCESS(result) )
796         {
797           if ( object->IsA(m_Dict->ul(MDD_KLVFill)) )
798             {
799               delete object;
800
801               if ( p > end_p )
802                 {
803                   DefaultLogSink().Error("Fill item short read: %d.\n", p - end_p);
804                 }
805             }
806           else if ( object->IsA(m_Dict->ul(MDD_Primer)) ) // TODO: only one primer should be found
807             {
808               delete object;
809               result = m_Primer.InitFromBuffer(redo_p, end_p - redo_p);
810             }
811           else
812             {
813               m_PacketList->AddPacket(object); // takes ownership
814
815               if ( object->IsA(m_Dict->ul(MDD_Preface)) && m_Preface == 0 )
816                 m_Preface = (Preface*)object;
817             }
818         }
819       else
820         {
821           DefaultLogSink().Error("Error initializing OP1a header packet.\n");
822           //      Kumu::hexdump(p-object->PacketLength(), object->PacketLength());
823           delete object;
824         }
825     }
826
827   return result;
828 }
829
830 ASDCP::Result_t
831 ASDCP::MXF::OP1aHeader::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
832 {
833   return m_PacketList->GetMDObjectByID(ObjectID, Object);
834 }
835
836 //
837 ASDCP::Result_t
838 ASDCP::MXF::OP1aHeader::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
839 {
840   InterchangeObject* TmpObject;
841
842   if ( Object == 0 )
843     Object = &TmpObject;
844
845   return m_PacketList->GetMDObjectByType(ObjectID, Object);
846 }
847
848 //
849 ASDCP::Result_t
850 ASDCP::MXF::OP1aHeader::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
851 {
852   return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
853 }
854
855 //
856 ASDCP::MXF::Identification*
857 ASDCP::MXF::OP1aHeader::GetIdentification()
858 {
859   InterchangeObject* Object;
860
861   if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object)) )
862     return (Identification*)Object;
863
864   return 0;
865 }
866
867 //
868 ASDCP::MXF::SourcePackage*
869 ASDCP::MXF::OP1aHeader::GetSourcePackage()
870 {
871   InterchangeObject* Object;
872
873   if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object)) )
874     return (SourcePackage*)Object;
875
876   return 0;
877 }
878
879 //
880 ASDCP::Result_t
881 ASDCP::MXF::OP1aHeader::WriteToFile(Kumu::FileWriter& Writer, ui32_t HeaderSize)
882 {
883   assert(m_Dict);
884   if ( m_Preface == 0 )
885     return RESULT_STATE;
886
887   if ( HeaderSize < 4096 ) 
888     {
889       DefaultLogSink().Error("HeaderSize %u is too small. Must be >= 4096\n", HeaderSize);
890       return RESULT_PARAM;
891     }
892
893   ASDCP::FrameBuffer HeaderBuffer;
894   HeaderByteCount = HeaderSize - ArchiveSize();
895   assert (HeaderByteCount <= 0xFFFFFFFFL);
896   Result_t result = HeaderBuffer.Capacity((ui32_t) HeaderByteCount); 
897   m_Preface->m_Lookup = &m_Primer;
898
899   std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
900   for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
901     {
902       InterchangeObject* object = *pl_i;
903       object->m_Lookup = &m_Primer;
904
905       ASDCP::FrameBuffer WriteWrapper;
906       WriteWrapper.SetData(HeaderBuffer.Data() + HeaderBuffer.Size(),
907                            HeaderBuffer.Capacity() - HeaderBuffer.Size());
908       result = object->WriteToBuffer(WriteWrapper);
909       HeaderBuffer.Size(HeaderBuffer.Size() + WriteWrapper.Size());
910     }
911
912   if ( ASDCP_SUCCESS(result) )
913     {
914       UL TmpUL(m_Dict->ul(MDD_ClosedCompleteHeader));
915       result = Partition::WriteToFile(Writer, TmpUL);
916     }
917
918   if ( ASDCP_SUCCESS(result) )
919     result = m_Primer.WriteToFile(Writer);
920
921   if ( ASDCP_SUCCESS(result) )
922     {
923       ui32_t write_count;
924       Writer.Write(HeaderBuffer.RoData(), HeaderBuffer.Size(), &write_count);
925       assert(write_count == HeaderBuffer.Size());
926     }
927
928   // KLV Fill
929   if ( ASDCP_SUCCESS(result) )
930     {
931       Kumu::fpos_t pos = Writer.Tell();
932
933       if ( pos > (Kumu::fpos_t)HeaderByteCount )
934         {
935           char intbuf[IntBufferLen];
936           DefaultLogSink().Error("Header size %s exceeds specified value %u\n",
937                                  ui64sz(pos, intbuf),
938                                  HeaderSize);
939           return RESULT_FAIL;
940         }
941
942       ASDCP::FrameBuffer NilBuf;
943       ui32_t klv_fill_length = HeaderSize - (ui32_t)pos;
944
945       if ( klv_fill_length < kl_length )
946         {
947           DefaultLogSink().Error("Remaining region too small for KLV Fill header\n");
948           return RESULT_FAIL;
949         }
950
951       klv_fill_length -= kl_length;
952       result = WriteKLToFile(Writer, m_Dict->ul(MDD_KLVFill), klv_fill_length);
953
954       if ( ASDCP_SUCCESS(result) )
955         result = NilBuf.Capacity(klv_fill_length);
956
957       if ( ASDCP_SUCCESS(result) )
958         {
959           memset(NilBuf.Data(), 0, klv_fill_length);
960           ui32_t write_count;
961           Writer.Write(NilBuf.RoData(), klv_fill_length, &write_count);
962           assert(write_count == klv_fill_length);
963         }
964     }
965
966   return result;
967 }
968
969 //
970 void
971 ASDCP::MXF::OP1aHeader::Dump(FILE* stream)
972 {
973   if ( stream == 0 )
974     stream = stderr;
975
976   Partition::Dump(stream);
977   m_Primer.Dump(stream);
978
979   if ( m_Preface == 0 )
980     fputs("No Preface loaded\n", stream);
981
982   std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
983   for ( ; i != m_PacketList->m_List.end(); i++ )
984     (*i)->Dump(stream);
985 }
986
987 //------------------------------------------------------------------------------------------
988 //
989
990 ASDCP::MXF::OPAtomIndexFooter::OPAtomIndexFooter(const Dictionary*& d) :
991   Partition(d), m_Dict(d),
992   m_CurrentSegment(0), m_BytesPerEditUnit(0), m_BodySID(0),
993   m_ECOffset(0), m_Lookup(0)
994 {
995   BodySID = 0;
996   IndexSID = 129;
997 }
998
999 ASDCP::MXF::OPAtomIndexFooter::~OPAtomIndexFooter() {}
1000
1001 //
1002 ASDCP::Result_t
1003 ASDCP::MXF::OPAtomIndexFooter::InitFromFile(const Kumu::FileReader& Reader)
1004 {
1005   Result_t result = Partition::InitFromFile(Reader); // test UL and OP
1006
1007   // slurp up the remainder of the footer
1008   ui32_t read_count = 0;
1009
1010   if ( ASDCP_SUCCESS(result) && IndexByteCount > 0 )
1011     {
1012       assert (IndexByteCount <= 0xFFFFFFFFL);
1013       // At this point, m_FooterData may not have been initialized
1014       // so it's capacity is zero and data pointer is NULL
1015       // However, if IndexByteCount is zero then the capacity
1016       // doesn't change and the data pointer is not set.
1017       result = m_FooterData.Capacity((ui32_t) IndexByteCount);
1018
1019       if ( ASDCP_SUCCESS(result) )
1020         result = Reader.Read(m_FooterData.Data(), m_FooterData.Capacity(), &read_count);
1021
1022       if ( ASDCP_SUCCESS(result) && read_count != m_FooterData.Capacity() )
1023         {
1024           DefaultLogSink().Error("Short read of footer partition: got %u, expecting %u\n",
1025                                  read_count, m_FooterData.Capacity());
1026           return RESULT_FAIL;
1027         }
1028       else if( ASDCP_SUCCESS(result) && !m_FooterData.Data() )
1029         {
1030           DefaultLogSink().Error( "Buffer for footer partition not created: IndexByteCount = %u\n",
1031                                   IndexByteCount );
1032           return RESULT_FAIL;
1033         }
1034
1035       if ( ASDCP_SUCCESS(result) )
1036         result = InitFromBuffer(m_FooterData.RoData(), m_FooterData.Capacity());
1037     }
1038
1039   return result;
1040 }
1041
1042 //
1043 ASDCP::Result_t
1044 ASDCP::MXF::OPAtomIndexFooter::InitFromPartitionBuffer(const byte_t* p, ui32_t l)
1045 {
1046   Result_t result = KLVPacket::InitFromBuffer(p, l);
1047
1048   if ( ASDCP_SUCCESS(result) )
1049     result = Partition::InitFromBuffer(m_ValueStart, m_ValueLength); // test UL and OP
1050
1051   if ( ASDCP_SUCCESS(result) )
1052     {
1053       ui32_t pp_len = KLVPacket::PacketLength();
1054       result = InitFromBuffer(p + pp_len, l - pp_len);
1055     }
1056
1057   return result;
1058 }
1059
1060 //
1061 ASDCP::Result_t
1062 ASDCP::MXF::OPAtomIndexFooter::InitFromBuffer(const byte_t* p, ui32_t l)
1063 {
1064   Result_t result = RESULT_OK;
1065   const byte_t* end_p = p + l;
1066   
1067   while ( ASDCP_SUCCESS(result) && p < end_p )
1068     {
1069       // parse the packets and index them by uid, discard KLVFill items
1070       InterchangeObject* object = CreateObject(m_Dict, p);
1071       assert(object);
1072
1073       object->m_Lookup = m_Lookup;
1074       result = object->InitFromBuffer(p, end_p - p);
1075       p += object->PacketLength();
1076
1077       if ( ASDCP_SUCCESS(result) )
1078         {
1079           m_PacketList->AddPacket(object); // takes ownership
1080         }
1081       else
1082         {
1083           DefaultLogSink().Error("Error initializing OPAtom footer packet.\n");
1084           delete object;
1085         }
1086     }
1087
1088   if ( ASDCP_FAILURE(result) )
1089     {
1090       DefaultLogSink().Error("Failed to initialize OPAtomIndexFooter.\n");
1091     }
1092
1093   return result;
1094 }
1095
1096 //
1097 ASDCP::Result_t
1098 ASDCP::MXF::OPAtomIndexFooter::WriteToFile(Kumu::FileWriter& Writer, ui64_t duration)
1099 {
1100   assert(m_Dict);
1101   ASDCP::FrameBuffer FooterBuffer;
1102   ui32_t   footer_size = m_PacketList->m_List.size() * MaxIndexSegmentSize; // segment-count * max-segment-size
1103   Result_t result = FooterBuffer.Capacity(footer_size); 
1104   ui32_t   iseg_count = 0;
1105
1106   if ( m_CurrentSegment != 0 )
1107     {
1108       m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1109       m_CurrentSegment = 0;
1110     }
1111
1112   std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
1113   for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
1114     {
1115       IndexTableSegment *segment = dynamic_cast<IndexTableSegment*>(*pl_i);
1116
1117       if ( segment != 0 )
1118         {
1119           iseg_count++;
1120           if ( m_BytesPerEditUnit != 0 )
1121             {
1122               if ( iseg_count != 1 )
1123                 return RESULT_STATE;
1124
1125               segment->IndexDuration = duration;
1126             }
1127         }
1128
1129       InterchangeObject* object = *pl_i;
1130       object->m_Lookup = m_Lookup;
1131
1132       ASDCP::FrameBuffer WriteWrapper;
1133       WriteWrapper.SetData(FooterBuffer.Data() + FooterBuffer.Size(),
1134                            FooterBuffer.Capacity() - FooterBuffer.Size());
1135       result = object->WriteToBuffer(WriteWrapper);
1136       FooterBuffer.Size(FooterBuffer.Size() + WriteWrapper.Size());
1137     }
1138
1139   if ( ASDCP_SUCCESS(result) )
1140     {
1141       IndexByteCount = FooterBuffer.Size();
1142       UL FooterUL(m_Dict->ul(MDD_CompleteFooter));
1143       result = Partition::WriteToFile(Writer, FooterUL);
1144     }
1145
1146   if ( ASDCP_SUCCESS(result) )
1147     {
1148       ui32_t write_count = 0;
1149       result = Writer.Write(FooterBuffer.RoData(), FooterBuffer.Size(), &write_count);
1150       assert(write_count == FooterBuffer.Size());
1151     }
1152
1153   return result;
1154 }
1155
1156 //
1157 void
1158 ASDCP::MXF::OPAtomIndexFooter::Dump(FILE* stream)
1159 {
1160   if ( stream == 0 )
1161     stream = stderr;
1162
1163   Partition::Dump(stream);
1164
1165   std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
1166   for ( ; i != m_PacketList->m_List.end(); i++ )
1167     (*i)->Dump(stream);
1168 }
1169
1170 ASDCP::Result_t
1171 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
1172 {
1173   return m_PacketList->GetMDObjectByID(ObjectID, Object);
1174 }
1175
1176 //
1177 ASDCP::Result_t
1178 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
1179 {
1180   InterchangeObject* TmpObject;
1181
1182   if ( Object == 0 )
1183     Object = &TmpObject;
1184
1185   return m_PacketList->GetMDObjectByType(ObjectID, Object);
1186 }
1187
1188 //
1189 ASDCP::Result_t
1190 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
1191 {
1192   return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
1193 }
1194
1195 //
1196 ASDCP::Result_t
1197 ASDCP::MXF::OPAtomIndexFooter::Lookup(ui32_t frame_num, IndexTableSegment::IndexEntry& Entry) const
1198 {
1199   std::list<InterchangeObject*>::iterator li;
1200   for ( li = m_PacketList->m_List.begin(); li != m_PacketList->m_List.end(); li++ )
1201     {
1202       IndexTableSegment *segment = dynamic_cast<IndexTableSegment*>(*li);
1203
1204       if ( segment != 0 )
1205         {
1206           ui64_t start_pos = segment->IndexStartPosition;
1207
1208           if ( segment->EditUnitByteCount > 0 )
1209             {
1210               if ( m_PacketList->m_List.size() > 1 )
1211                 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
1212
1213               if ( ! segment->IndexEntryArray.empty() )
1214                 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
1215
1216               Entry.StreamOffset = (ui64_t)frame_num * segment->EditUnitByteCount;
1217               return RESULT_OK;
1218             }
1219           else if ( (ui64_t)frame_num >= start_pos
1220                     && (ui64_t)frame_num < (start_pos + segment->IndexDuration) )
1221             {
1222               ui64_t tmp = frame_num - start_pos;
1223               assert(tmp <= 0xFFFFFFFFL);
1224
1225               if ( tmp < segment->IndexEntryArray.size() )
1226                 {
1227                   Entry = segment->IndexEntryArray[(ui32_t) tmp];
1228                   return RESULT_OK;
1229                 }
1230               else
1231                 {
1232                   DefaultLogSink().Error("Malformed index table segment, IndexDuration does not match entries.\n");
1233                 }
1234             }
1235         }
1236     }
1237
1238   return RESULT_FAIL;
1239 }
1240
1241 //
1242 void
1243 ASDCP::MXF::OPAtomIndexFooter::SetDeltaParams(const IndexTableSegment::DeltaEntry& delta)
1244 {
1245   m_DefaultDeltaEntry = delta;
1246 }
1247
1248 //
1249 void
1250 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const Rational& Rate)
1251 {
1252   assert(lookup);
1253   m_Lookup = lookup;
1254   m_BytesPerEditUnit = size;
1255   m_EditRate = Rate;
1256
1257   IndexTableSegment* Index = new IndexTableSegment(m_Dict);
1258   AddChildObject(Index);
1259   Index->EditUnitByteCount = m_BytesPerEditUnit;
1260   Index->IndexEditRate = Rate;
1261 }
1262
1263 //
1264 void
1265 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsVBR(IPrimerLookup* lookup, const Rational& Rate, Kumu::fpos_t offset)
1266 {
1267   assert(lookup);
1268   m_Lookup = lookup;
1269   m_BytesPerEditUnit = 0;
1270   m_EditRate = Rate;
1271   m_ECOffset = offset;
1272 }
1273
1274 //
1275 void
1276 ASDCP::MXF::OPAtomIndexFooter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
1277 {
1278   if ( m_BytesPerEditUnit != 0 )  // are we CBR? that's bad 
1279     {
1280       DefaultLogSink().Error("Call to PushIndexEntry() failed: index is CBR\n");
1281       return;
1282     }
1283
1284   // do we have an available segment?
1285   if ( m_CurrentSegment == 0 )
1286     { // no, set up a new segment
1287       m_CurrentSegment = new IndexTableSegment(m_Dict);
1288       assert(m_CurrentSegment);
1289       AddChildObject(m_CurrentSegment);
1290       m_CurrentSegment->DeltaEntryArray.push_back(m_DefaultDeltaEntry);
1291       m_CurrentSegment->IndexEditRate = m_EditRate;
1292       m_CurrentSegment->IndexStartPosition = 0;
1293     }
1294   else if ( m_CurrentSegment->IndexEntryArray.size() >= CBRIndexEntriesPerSegment )
1295     { // no, this one is full, start another
1296       m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1297       ui64_t StartPosition = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
1298
1299       m_CurrentSegment = new IndexTableSegment(m_Dict);
1300       assert(m_CurrentSegment);
1301       AddChildObject(m_CurrentSegment);
1302       m_CurrentSegment->DeltaEntryArray.push_back(m_DefaultDeltaEntry);
1303       m_CurrentSegment->IndexEditRate = m_EditRate;
1304       m_CurrentSegment->IndexStartPosition = StartPosition;
1305     }
1306
1307   m_CurrentSegment->IndexEntryArray.push_back(Entry);
1308 }
1309
1310 //------------------------------------------------------------------------------------------
1311 //
1312
1313 //
1314 void
1315 ASDCP::MXF::InterchangeObject::Copy(const InterchangeObject& rhs)
1316 {
1317   m_UL = rhs.m_UL;
1318   InstanceUID = rhs.InstanceUID;
1319   GenerationUID = rhs.GenerationUID;
1320 }
1321
1322 //
1323 ASDCP::Result_t
1324 ASDCP::MXF::InterchangeObject::InitFromTLVSet(TLVReader& TLVSet)
1325 {
1326   Result_t result = TLVSet.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID));
1327   if ( ASDCP_SUCCESS(result) )
1328     result = TLVSet.ReadObject(OBJ_READ_ARGS_OPT(GenerationInterchangeObject, GenerationUID));
1329   return result;
1330 }
1331
1332 //
1333 ASDCP::Result_t
1334 ASDCP::MXF::InterchangeObject::WriteToTLVSet(TLVWriter& TLVSet)
1335 {
1336   Result_t result = TLVSet.WriteObject(OBJ_WRITE_ARGS(InterchangeObject, InstanceUID));
1337   if ( ASDCP_SUCCESS(result) )
1338     result = TLVSet.WriteObject(OBJ_WRITE_ARGS_OPT(GenerationInterchangeObject, GenerationUID));
1339   return result;
1340 }
1341
1342 //
1343 ASDCP::Result_t
1344 ASDCP::MXF::InterchangeObject::InitFromBuffer(const byte_t* p, ui32_t l)
1345 {
1346   ASDCP_TEST_NULL(p);
1347   Result_t result = RESULT_FALSE;
1348
1349   if ( m_UL.HasValue() )
1350     {
1351       result = KLVPacket::InitFromBuffer(p, l, m_UL);
1352
1353       if ( ASDCP_SUCCESS(result) )
1354         {
1355           TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup);
1356           result = InitFromTLVSet(MemRDR);
1357         }
1358     }
1359   else
1360     {
1361       result = KLVPacket::InitFromBuffer(p, l);
1362     }
1363   
1364   return result;
1365 }
1366
1367 //
1368 ASDCP::Result_t
1369 ASDCP::MXF::InterchangeObject::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
1370 {
1371   if ( ! m_UL.HasValue() )
1372     return RESULT_STATE;
1373
1374   TLVWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length, m_Lookup);
1375   Result_t result = WriteToTLVSet(MemWRT);
1376
1377   if ( ASDCP_SUCCESS(result) )
1378     {
1379       ui32_t packet_length = MemWRT.Length();
1380       result = WriteKLToBuffer(Buffer, packet_length);
1381
1382       if ( ASDCP_SUCCESS(result) )
1383         Buffer.Size(Buffer.Size() + packet_length);
1384     }
1385
1386   return result;
1387 }
1388
1389 //
1390 void
1391 ASDCP::MXF::InterchangeObject::Dump(FILE* stream)
1392 {
1393   char identbuf[IdentBufferLen];
1394
1395   fputc('\n', stream);
1396   KLVPacket::Dump(stream, *m_Dict, false);
1397   fprintf(stream, "             InstanceUID = %s\n",  InstanceUID.EncodeHex(identbuf, IdentBufferLen));
1398
1399   if ( ! GenerationUID.empty() )
1400     fprintf(stream, "           GenerationUID = %s\n",  GenerationUID.get().EncodeHex(identbuf, IdentBufferLen));
1401 }
1402
1403 //
1404 bool
1405 ASDCP::MXF::InterchangeObject::IsA(const byte_t* label)
1406 {
1407   if ( m_KLLength == 0 || m_KeyStart == 0 )
1408     return false;
1409
1410   return ( memcmp(label, m_KeyStart, SMPTE_UL_LENGTH) == 0 );
1411 }
1412
1413
1414 //------------------------------------------------------------------------------------------
1415
1416
1417 typedef std::map<ASDCP::UL, ASDCP::MXF::MXFObjectFactory_t>FactoryMap_t;
1418 typedef FactoryMap_t::iterator FLi_t;
1419
1420 //
1421 class FactoryList : public FactoryMap_t
1422 {
1423   Kumu::Mutex m_Lock;
1424
1425 public:
1426   FactoryList() {}
1427   ~FactoryList() {}
1428
1429   bool Empty() {
1430     Kumu::AutoMutex BlockLock(m_Lock);
1431     return empty();
1432   }
1433
1434   FLi_t Find(const byte_t* label) {
1435     Kumu::AutoMutex BlockLock(m_Lock);
1436     return find(label);
1437   }
1438
1439   FLi_t End() {
1440     Kumu::AutoMutex BlockLock(m_Lock);
1441     return end();
1442   }
1443
1444   void Insert(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory) {
1445     Kumu::AutoMutex BlockLock(m_Lock);
1446     insert(FactoryList::value_type(label, factory));
1447   }
1448 };
1449
1450 //
1451 static FactoryList s_FactoryList;
1452 static Kumu::Mutex s_InitLock;
1453 static bool        s_TypesInit = false;
1454
1455
1456 //
1457 void
1458 ASDCP::MXF::SetObjectFactory(const ASDCP::UL& label, ASDCP::MXF::MXFObjectFactory_t factory)
1459 {
1460   s_FactoryList.Insert(label, factory);
1461 }
1462
1463 //
1464 ASDCP::MXF::InterchangeObject*
1465 ASDCP::MXF::CreateObject(const Dictionary*& Dict, const UL& label)
1466 {
1467   if ( ! s_TypesInit )
1468     {
1469       Kumu::AutoMutex BlockLock(s_InitLock);
1470
1471       if ( ! s_TypesInit )
1472         {
1473           MXF::Metadata_InitTypes(Dict);
1474           s_TypesInit = true;
1475         }
1476     }
1477
1478   FLi_t i = s_FactoryList.find(label.Value());
1479
1480   if ( i == s_FactoryList.end() )
1481     return new InterchangeObject(Dict);
1482
1483   return i->second(Dict);
1484 }
1485
1486
1487 //------------------------------------------------------------------------------------------
1488
1489 //
1490 bool
1491 ASDCP::MXF::decode_mca_string(const std::string& s, const mca_label_map_t& labels, const Dictionary*& dict, const std::string& language,
1492                               InterchangeObject_list_t& descriptor_list, ui32_t& channel_count)
1493 {
1494   std::string symbol_buf;
1495   channel_count = 0;
1496   ASDCP::MXF::SoundfieldGroupLabelSubDescriptor *current_soundfield = 0;
1497   std::string::const_iterator i;
1498
1499   for ( i = s.begin(); i != s.end(); ++i )
1500     {
1501       if ( *i == '(' )
1502         {
1503           if ( current_soundfield != 0 )
1504             {
1505               DefaultLogSink().Error("Encountered '(', already processing a soundfield group.\n");
1506               return false;
1507             }
1508
1509           if ( symbol_buf.empty() )
1510             {
1511               DefaultLogSink().Error("Encountered '(', without leading soundfield group symbol.\n");
1512               return false;
1513             }
1514
1515           mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1516       
1517           if ( i == labels.end() )
1518             {
1519               DefaultLogSink().Error("Unknown symbol: '%s'\n", symbol_buf.c_str());
1520               return false;
1521             }
1522       
1523           if ( i->second.ul.Value()[10] != 2 ) // magic depends on UL "Essence Facet" byte (see ST 428-12)
1524             {
1525               DefaultLogSink().Error("Not a soundfield group symbol: '%s'\n", symbol_buf.c_str());
1526               return false;
1527             }
1528
1529           current_soundfield = new ASDCP::MXF::SoundfieldGroupLabelSubDescriptor(dict);
1530           GenRandomValue(current_soundfield->MCALinkID);
1531
1532           current_soundfield->MCATagSymbol = (i->second.requires_prefix ? "sg" : "") + i->first;
1533           current_soundfield->MCATagName = i->second.tag_name;
1534           current_soundfield->RFC5646SpokenLanguage = language;
1535           current_soundfield->MCALabelDictionaryID = i->second.ul;
1536           descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(current_soundfield));
1537           symbol_buf.clear();
1538         }
1539       else if ( *i == ')' )
1540         {
1541           if ( current_soundfield == 0 )
1542             {
1543               DefaultLogSink().Error("Encountered ')', not currently processing a soundfield group.\n");
1544               return false;
1545             }
1546
1547           if ( symbol_buf.empty() )
1548             {
1549               DefaultLogSink().Error("Soundfield group description contains no channels.\n");
1550               return false;
1551             }
1552
1553           mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1554       
1555           if ( i == labels.end() )
1556             {
1557               DefaultLogSink().Error("Unknown symbol: '%s'\n", symbol_buf.c_str());
1558               return false;
1559             }
1560
1561           assert(current_soundfield);
1562
1563           ASDCP::MXF::AudioChannelLabelSubDescriptor *channel_descr =
1564             new ASDCP::MXF::AudioChannelLabelSubDescriptor(dict);
1565           GenRandomValue(channel_descr->MCALinkID);
1566
1567           channel_descr->SoundfieldGroupLinkID = current_soundfield->MCALinkID;
1568           channel_descr->MCAChannelID = channel_count++ + 1;
1569           channel_descr->MCATagSymbol = (i->second.requires_prefix ? "ch" : "") + i->first;
1570           channel_descr->MCATagName = i->second.tag_name;
1571           channel_descr->RFC5646SpokenLanguage = language;
1572           channel_descr->MCALabelDictionaryID = i->second.ul;
1573           descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(channel_descr));
1574           symbol_buf.clear();
1575           current_soundfield = 0;
1576         }
1577       else if ( *i == ',' )
1578         {
1579           if ( ! symbol_buf.empty() && ! symbol_buf.compare("-") )
1580             {
1581               channel_count++;
1582               symbol_buf.clear();
1583             }
1584           else if ( ! symbol_buf.empty() )
1585             {
1586               mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1587
1588               if ( i == labels.end() )
1589                 {
1590                   DefaultLogSink().Error("Unknown symbol: '%s'\n", symbol_buf.c_str());
1591                   return false;
1592                 }
1593
1594               if ( i->second.ul.Value()[10] != 1 ) // magic depends on UL "Essence Facet" byte (see ST 428-12)
1595                 {
1596                   DefaultLogSink().Error("Not a channel symbol: '%s'\n", symbol_buf.c_str());
1597                   return false;
1598                 }
1599
1600               ASDCP::MXF::AudioChannelLabelSubDescriptor *channel_descr =
1601                 new ASDCP::MXF::AudioChannelLabelSubDescriptor(dict);
1602               GenRandomValue(channel_descr->MCALinkID);
1603
1604               if ( current_soundfield != 0 )
1605                 {
1606                   channel_descr->SoundfieldGroupLinkID = current_soundfield->MCALinkID;
1607                 }
1608
1609               channel_descr->MCAChannelID = channel_count++ + 1;
1610               channel_descr->MCATagSymbol = (i->second.requires_prefix ? "ch" : "") + i->first;
1611               channel_descr->MCATagName = i->second.tag_name;
1612               channel_descr->RFC5646SpokenLanguage = language;
1613               channel_descr->MCALabelDictionaryID = i->second.ul;
1614               descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(channel_descr));
1615               symbol_buf.clear();
1616             }
1617         }
1618       else if ( *i == '-' || isalnum(*i) )
1619         {
1620           symbol_buf += *i;
1621         }
1622       else if ( ! isspace(*i) )
1623         {
1624           DefaultLogSink().Error("Unexpected character '%c'.\n", *i);
1625           return false;
1626         }
1627     }
1628
1629   if ( ! symbol_buf.empty() && ! symbol_buf.compare("-")  )
1630     {
1631       channel_count++;
1632     }
1633   else if ( ! symbol_buf.empty() )
1634     {
1635       mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1636       
1637       if ( i == labels.end() )
1638         {
1639           DefaultLogSink().Error("Unknown symbol: '%s'\n", symbol_buf.c_str());
1640           return false;
1641         }
1642
1643       ASDCP::MXF::AudioChannelLabelSubDescriptor *channel_descr =
1644         new ASDCP::MXF::AudioChannelLabelSubDescriptor(dict);
1645       GenRandomValue(channel_descr->MCALinkID);
1646
1647       if ( current_soundfield != 0 )
1648         {
1649           channel_descr->SoundfieldGroupLinkID = current_soundfield->MCALinkID;
1650         }
1651
1652       channel_descr->MCAChannelID = channel_count++ + 1;
1653       channel_descr->MCATagSymbol = (i->second.requires_prefix ? "ch" : "") + i->first;
1654       channel_descr->MCATagName = i->second.tag_name;
1655       channel_descr->RFC5646SpokenLanguage = language;
1656       channel_descr->MCALabelDictionaryID = i->second.ul;
1657       descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(channel_descr));
1658     }
1659
1660   return true;
1661 }
1662
1663 //
1664 ASDCP::MXF::ASDCP_MCAConfigParser::ASDCP_MCAConfigParser(const Dictionary*& d) : m_Dict(d), m_ChannelCount(0)
1665 {
1666   typedef mca_label_map_t::value_type pair;
1667   m_LabelMap.insert(pair("L",     label_traits("Left"                              , true,  m_Dict->ul(MDD_DCAudioChannel_L))));
1668   m_LabelMap.insert(pair("R",     label_traits("Right"                             , true,  m_Dict->ul(MDD_DCAudioChannel_R))));
1669   m_LabelMap.insert(pair("C",     label_traits("Center"                            , true,  m_Dict->ul(MDD_DCAudioChannel_C))));
1670   m_LabelMap.insert(pair("LFE",   label_traits("LFE"                               , true,  m_Dict->ul(MDD_DCAudioChannel_LFE))));
1671   m_LabelMap.insert(pair("Ls",    label_traits("Left Surround"                     , true,  m_Dict->ul(MDD_DCAudioChannel_Ls))));
1672   m_LabelMap.insert(pair("Rs",    label_traits("Right Surround"                    , true,  m_Dict->ul(MDD_DCAudioChannel_Rs))));
1673   m_LabelMap.insert(pair("Lss",   label_traits("Left Side Surround"                , true,  m_Dict->ul(MDD_DCAudioChannel_Lss))));
1674   m_LabelMap.insert(pair("Rss",   label_traits("Right Side Surround"               , true,  m_Dict->ul(MDD_DCAudioChannel_Rss))));
1675   m_LabelMap.insert(pair("Lrs",   label_traits("Left Rear Surround"                , true,  m_Dict->ul(MDD_DCAudioChannel_Lrs))));
1676   m_LabelMap.insert(pair("Rrs",   label_traits("Right Rear Surround"               , true,  m_Dict->ul(MDD_DCAudioChannel_Rrs))));
1677   m_LabelMap.insert(pair("Lc",    label_traits("Left Center"                       , true,  m_Dict->ul(MDD_DCAudioChannel_Lc))));
1678   m_LabelMap.insert(pair("Rc",    label_traits("Right Center"                      , true,  m_Dict->ul(MDD_DCAudioChannel_Rc))));
1679   m_LabelMap.insert(pair("Cs",    label_traits("Center Surround"                   , true,  m_Dict->ul(MDD_DCAudioChannel_Cs))));
1680   m_LabelMap.insert(pair("HI",    label_traits("Hearing Impaired"                  , true,  m_Dict->ul(MDD_DCAudioChannel_HI))));
1681   m_LabelMap.insert(pair("VIN",   label_traits("Visually Impaired-Narrative"       , true,  m_Dict->ul(MDD_DCAudioChannel_VIN))));
1682   m_LabelMap.insert(pair("51",    label_traits("5.1"                               , true,  m_Dict->ul(MDD_DCAudioSoundfield_51))));
1683   m_LabelMap.insert(pair("71",    label_traits("7.1DS"                             , true,  m_Dict->ul(MDD_DCAudioSoundfield_71))));
1684   m_LabelMap.insert(pair("SDS",   label_traits("7.1SDS"                            , true,  m_Dict->ul(MDD_DCAudioSoundfield_SDS))));
1685   m_LabelMap.insert(pair("61",    label_traits("6.1"                               , true,  m_Dict->ul(MDD_DCAudioSoundfield_61))));
1686   m_LabelMap.insert(pair("M",     label_traits("1.0 Monaural"                      , true,  m_Dict->ul(MDD_DCAudioSoundfield_M))));
1687   m_LabelMap.insert(pair("DBOX",  label_traits("D-BOX Motion Code Primary Stream"  , false, m_Dict->ul(MDD_DBOXMotionCodePrimaryStream))));
1688   m_LabelMap.insert(pair("DBOX2", label_traits("D-BOX Motion Code Secondary Stream", false, m_Dict->ul(MDD_DBOXMotionCodeSecondaryStream))));
1689 }
1690
1691 //
1692 ui32_t
1693 ASDCP::MXF::ASDCP_MCAConfigParser::ChannelCount() const
1694 {
1695   return m_ChannelCount;
1696 }
1697
1698 // 51(L,R,C,LFE,Ls,Rs),HI,VIN
1699 bool
1700 ASDCP::MXF::ASDCP_MCAConfigParser::DecodeString(const std::string& s, const std::string& language)
1701 {
1702   return decode_mca_string(s, m_LabelMap, m_Dict, language, *this, m_ChannelCount);
1703 }
1704
1705
1706
1707 ASDCP::MXF::AS02_MCAConfigParser::AS02_MCAConfigParser(const Dictionary*& d) : ASDCP::MXF::ASDCP_MCAConfigParser(d)
1708 {
1709   typedef mca_label_map_t::value_type pair;
1710   m_LabelMap.insert(pair("M1",    label_traits("M1",  true,  m_Dict->ul(MDD_IMFAudioChannel_M1))));
1711   m_LabelMap.insert(pair("M2",    label_traits("M2",  true,  m_Dict->ul(MDD_IMFAudioChannel_M2))));
1712   m_LabelMap.insert(pair("Lt",    label_traits("Lt",  true,  m_Dict->ul(MDD_IMFAudioChannel_Lt))));
1713   m_LabelMap.insert(pair("Rt",    label_traits("Rt",  true,  m_Dict->ul(MDD_IMFAudioChannel_Rt))));
1714   m_LabelMap.insert(pair("Lst",   label_traits("Lst", true,  m_Dict->ul(MDD_IMFAudioChannel_Lst))));
1715   m_LabelMap.insert(pair("Rst",   label_traits("Rst", true,  m_Dict->ul(MDD_IMFAudioChannel_Rst))));
1716   m_LabelMap.insert(pair("S",     label_traits("S",   true,  m_Dict->ul(MDD_IMFAudioChannel_S))));
1717   m_LabelMap.insert(pair("ST",    label_traits("ST",  true,  m_Dict->ul(MDD_IMFAudioSoundfield_ST))));
1718   m_LabelMap.insert(pair("DM",    label_traits("DM",  true,  m_Dict->ul(MDD_IMFAudioSoundfield_DM))));
1719   m_LabelMap.insert(pair("DNS",   label_traits("DNS", true,  m_Dict->ul(MDD_IMFAudioSoundfield_DNS))));
1720   m_LabelMap.insert(pair("30",    label_traits("30",  true,  m_Dict->ul(MDD_IMFAudioSoundfield_30))));
1721   m_LabelMap.insert(pair("40",    label_traits("40",  true,  m_Dict->ul(MDD_IMFAudioSoundfield_40))));
1722   m_LabelMap.insert(pair("50",    label_traits("50",  true,  m_Dict->ul(MDD_IMFAudioSoundfield_50))));
1723   m_LabelMap.insert(pair("60",    label_traits("60",  true,  m_Dict->ul(MDD_IMFAudioSoundfield_60))));
1724   m_LabelMap.insert(pair("70",    label_traits("70",  true,  m_Dict->ul(MDD_IMFAudioSoundfield_70))));
1725   m_LabelMap.insert(pair("LtRt",  label_traits("LtRt",true,  m_Dict->ul(MDD_IMFAudioSoundfield_LtRt))));
1726   m_LabelMap.insert(pair("51Ex",  label_traits("51Ex",true,  m_Dict->ul(MDD_IMFAudioSoundfield_51Ex))));
1727   m_LabelMap.insert(pair("HI",    label_traits("HI",  true,  m_Dict->ul(MDD_IMFAudioSoundfield_HI))));
1728   m_LabelMap.insert(pair("VIN",   label_traits("VIN", true,  m_Dict->ul(MDD_IMFAudioSoundfield_VIN))));
1729 }
1730
1731
1732
1733 //
1734 bool
1735 ASDCP::MXF::GetEditRateFromFP(ASDCP::MXF::OP1aHeader& header, ASDCP::Rational& edit_rate)
1736 {
1737   bool has_first_item = false;
1738
1739   MXF::InterchangeObject* temp_item;
1740   std::list<MXF::InterchangeObject*> temp_items;
1741
1742   Result_t result = header.GetMDObjectsByType(DefaultCompositeDict().ul(MDD_SourcePackage), temp_items);
1743
1744   if ( KM_FAILURE(result) )
1745     {
1746       DefaultLogSink().Error("The MXF header does not contain a FilePackage item.\n");
1747       return false;
1748     }
1749
1750   if ( temp_items.size() != 1 )
1751     {
1752       DefaultLogSink().Error("The MXF header must contain one FilePackage item, found %d.\n", temp_items.size());
1753       return false;
1754     }
1755
1756   char buf[64];
1757   MXF::Array<UUID>::const_iterator i;
1758   MXF::SourcePackage *source_package = dynamic_cast<MXF::SourcePackage*>(temp_items.front());
1759   assert(source_package);
1760
1761   for ( i = source_package->Tracks.begin(); i != source_package->Tracks.end(); ++i )
1762     {
1763       // Track
1764       result = header.GetMDObjectByID(*i, &temp_item);
1765
1766       if ( KM_FAILURE(result) )
1767         {
1768           DefaultLogSink().Error("The MXF header is incomplete: strong reference %s leads nowhere.\n",
1769                                  i->EncodeHex(buf, 64));
1770           return false;
1771         }
1772
1773       MXF::Track *track = dynamic_cast<MXF::Track*>(temp_item);
1774
1775       if ( track == 0 )
1776         {
1777           DefaultLogSink().Error("The MXF header is incomplete: %s is not a Track item.\n",
1778                                  i->EncodeHex(buf, 64));
1779           return false;
1780         }
1781
1782       // Sequence
1783       result = header.GetMDObjectByID(track->Sequence, &temp_item);
1784
1785       if ( KM_FAILURE(result) )
1786         {
1787           DefaultLogSink().Error("The MXF header is incomplete: strong reference %s leads nowhere.\n",
1788                                  i->EncodeHex(buf, 64));
1789           return false;
1790         }
1791
1792       MXF::Sequence *sequence = dynamic_cast<MXF::Sequence*>(temp_item);
1793
1794       if ( sequence == 0 )
1795         {
1796           DefaultLogSink().Error("The MXF header is incomplete: %s is not a Sequence item.\n",
1797                                  track->Sequence.get().EncodeHex(buf, 64));
1798           return false;
1799         }
1800
1801       if ( sequence->StructuralComponents.size() != 1 )
1802         {
1803           DefaultLogSink().Error("The Sequence item must contain one reference to an esence item, found %d.\n",
1804                                  sequence->StructuralComponents.size());
1805           return false;
1806         }
1807
1808       // SourceClip
1809       result = header.GetMDObjectByID(sequence->StructuralComponents.front(), &temp_item);
1810
1811       if ( KM_FAILURE(result) )
1812         {
1813           DefaultLogSink().Error("The MXF header is incomplete: strong reference %s leads nowhere.\n",
1814                                  sequence->StructuralComponents.front().EncodeHex(buf, 64));
1815           return false;
1816         }
1817
1818       if ( temp_item->IsA(DefaultCompositeDict().ul(MDD_SourceClip)) )
1819         {
1820           MXF::SourceClip *source_clip = dynamic_cast<MXF::SourceClip*>(temp_item);
1821
1822           if ( source_clip == 0 )
1823             {
1824               DefaultLogSink().Error("The MXF header is incomplete: %s is not a SourceClip item.\n",
1825                                      sequence->StructuralComponents.front().EncodeHex(buf, 64));
1826               return false;
1827             }
1828
1829           if ( ! has_first_item )
1830             {
1831               edit_rate = track->EditRate;
1832               has_first_item = true;
1833             }
1834           else if ( edit_rate != track->EditRate )
1835             {
1836               DefaultLogSink().Error("The MXF header is incomplete: %s EditRate value does not match others in the file.\n",
1837                                      sequence->StructuralComponents.front().EncodeHex(buf, 64));
1838               return false;
1839             }
1840         }
1841       else if ( ! temp_item->IsA(DefaultCompositeDict().ul(MDD_TimecodeComponent)) )
1842         {
1843           DefaultLogSink().Error("Reference from Sequence to an unexpected type: %s.\n", temp_item->ObjectName());
1844           return false;
1845         }
1846     }
1847
1848   return true;
1849 }
1850
1851
1852 //
1853 // end MXF.cpp
1854 //