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