45b029886241e16511c38dba5001333b66ffb646
[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 < 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
1220               if ( tmp < segment->IndexEntryArray.size() )
1221                 {
1222                   Entry = segment->IndexEntryArray[(ui32_t) tmp];
1223                   return RESULT_OK;
1224                 }
1225               else
1226                 {
1227                   DefaultLogSink().Error("Malformed index table segment, IndexDuration does not match entries.\n");
1228                 }
1229             }
1230         }
1231     }
1232
1233   return RESULT_FAIL;
1234 }
1235
1236 //
1237 void
1238 ASDCP::MXF::OPAtomIndexFooter::SetDeltaParams(const IndexTableSegment::DeltaEntry& delta)
1239 {
1240   m_DefaultDeltaEntry = delta;
1241 }
1242
1243 //
1244 void
1245 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const Rational& Rate)
1246 {
1247   assert(lookup);
1248   m_Lookup = lookup;
1249   m_BytesPerEditUnit = size;
1250   m_EditRate = Rate;
1251
1252   IndexTableSegment* Index = new IndexTableSegment(m_Dict);
1253   AddChildObject(Index);
1254   Index->EditUnitByteCount = m_BytesPerEditUnit;
1255   Index->IndexEditRate = Rate;
1256 }
1257
1258 //
1259 void
1260 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsVBR(IPrimerLookup* lookup, const Rational& Rate, Kumu::fpos_t offset)
1261 {
1262   assert(lookup);
1263   m_Lookup = lookup;
1264   m_BytesPerEditUnit = 0;
1265   m_EditRate = Rate;
1266   m_ECOffset = offset;
1267 }
1268
1269 //
1270 void
1271 ASDCP::MXF::OPAtomIndexFooter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
1272 {
1273   if ( m_BytesPerEditUnit != 0 )  // are we CBR? that's bad 
1274     {
1275       DefaultLogSink().Error("Call to PushIndexEntry() failed: index is CBR\n");
1276       return;
1277     }
1278
1279   // do we have an available segment?
1280   if ( m_CurrentSegment == 0 )
1281     { // no, set up a new segment
1282       m_CurrentSegment = new IndexTableSegment(m_Dict);
1283       assert(m_CurrentSegment);
1284       AddChildObject(m_CurrentSegment);
1285       m_CurrentSegment->DeltaEntryArray.push_back(m_DefaultDeltaEntry);
1286       m_CurrentSegment->IndexEditRate = m_EditRate;
1287       m_CurrentSegment->IndexStartPosition = 0;
1288     }
1289   else if ( m_CurrentSegment->IndexEntryArray.size() >= CBRIndexEntriesPerSegment )
1290     { // no, this one is full, start another
1291       m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1292       ui64_t StartPosition = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
1293
1294       m_CurrentSegment = new IndexTableSegment(m_Dict);
1295       assert(m_CurrentSegment);
1296       AddChildObject(m_CurrentSegment);
1297       m_CurrentSegment->DeltaEntryArray.push_back(m_DefaultDeltaEntry);
1298       m_CurrentSegment->IndexEditRate = m_EditRate;
1299       m_CurrentSegment->IndexStartPosition = StartPosition;
1300     }
1301
1302   m_CurrentSegment->IndexEntryArray.push_back(Entry);
1303 }
1304
1305 //------------------------------------------------------------------------------------------
1306 //
1307
1308 //
1309 void
1310 ASDCP::MXF::InterchangeObject::Copy(const InterchangeObject& rhs)
1311 {
1312   m_UL = rhs.m_UL;
1313   InstanceUID = rhs.InstanceUID;
1314   GenerationUID = rhs.GenerationUID;
1315 }
1316
1317 //
1318 ASDCP::Result_t
1319 ASDCP::MXF::InterchangeObject::InitFromTLVSet(TLVReader& TLVSet)
1320 {
1321   Result_t result = TLVSet.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID));
1322   if ( ASDCP_SUCCESS(result) )
1323     result = TLVSet.ReadObject(OBJ_READ_ARGS_OPT(GenerationInterchangeObject, GenerationUID));
1324   return result;
1325 }
1326
1327 //
1328 ASDCP::Result_t
1329 ASDCP::MXF::InterchangeObject::WriteToTLVSet(TLVWriter& TLVSet)
1330 {
1331   Result_t result = TLVSet.WriteObject(OBJ_WRITE_ARGS(InterchangeObject, InstanceUID));
1332   if ( ASDCP_SUCCESS(result) )
1333     result = TLVSet.WriteObject(OBJ_WRITE_ARGS_OPT(GenerationInterchangeObject, GenerationUID));
1334   return result;
1335 }
1336
1337 //
1338 ASDCP::Result_t
1339 ASDCP::MXF::InterchangeObject::InitFromBuffer(const byte_t* p, ui32_t l)
1340 {
1341   ASDCP_TEST_NULL(p);
1342   Result_t result = RESULT_FALSE;
1343
1344   if ( m_UL.HasValue() )
1345     {
1346       result = KLVPacket::InitFromBuffer(p, l, m_UL);
1347
1348       if ( ASDCP_SUCCESS(result) )
1349         {
1350           TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup);
1351           result = InitFromTLVSet(MemRDR);
1352         }
1353     }
1354   else
1355     {
1356       result = KLVPacket::InitFromBuffer(p, l);
1357     }
1358   
1359   return result;
1360 }
1361
1362 //
1363 ASDCP::Result_t
1364 ASDCP::MXF::InterchangeObject::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
1365 {
1366   if ( ! m_UL.HasValue() )
1367     return RESULT_STATE;
1368
1369   TLVWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length, m_Lookup);
1370   Result_t result = WriteToTLVSet(MemWRT);
1371
1372   if ( ASDCP_SUCCESS(result) )
1373     {
1374       ui32_t packet_length = MemWRT.Length();
1375       result = WriteKLToBuffer(Buffer, packet_length);
1376
1377       if ( ASDCP_SUCCESS(result) )
1378         Buffer.Size(Buffer.Size() + packet_length);
1379     }
1380
1381   return result;
1382 }
1383
1384 //
1385 void
1386 ASDCP::MXF::InterchangeObject::Dump(FILE* stream)
1387 {
1388   char identbuf[IdentBufferLen];
1389
1390   fputc('\n', stream);
1391   KLVPacket::Dump(stream, *m_Dict, false);
1392   fprintf(stream, "             InstanceUID = %s\n",  InstanceUID.EncodeHex(identbuf, IdentBufferLen));
1393
1394   if ( ! GenerationUID.empty() )
1395     fprintf(stream, "           GenerationUID = %s\n",  GenerationUID.get().EncodeHex(identbuf, IdentBufferLen));
1396 }
1397
1398 //
1399 bool
1400 ASDCP::MXF::InterchangeObject::IsA(const byte_t* label)
1401 {
1402   if ( m_KLLength == 0 || m_KeyStart == 0 )
1403     return false;
1404
1405   return ( memcmp(label, m_KeyStart, SMPTE_UL_LENGTH) == 0 );
1406 }
1407
1408
1409 //------------------------------------------------------------------------------------------
1410
1411
1412 typedef std::map<ASDCP::UL, ASDCP::MXF::MXFObjectFactory_t>FactoryMap_t;
1413 typedef FactoryMap_t::iterator FLi_t;
1414
1415 //
1416 class FactoryList : public FactoryMap_t
1417 {
1418   Kumu::Mutex m_Lock;
1419
1420 public:
1421   FactoryList() {}
1422   ~FactoryList() {}
1423
1424   bool Empty() {
1425     Kumu::AutoMutex BlockLock(m_Lock);
1426     return empty();
1427   }
1428
1429   FLi_t Find(const byte_t* label) {
1430     Kumu::AutoMutex BlockLock(m_Lock);
1431     return find(label);
1432   }
1433
1434   FLi_t End() {
1435     Kumu::AutoMutex BlockLock(m_Lock);
1436     return end();
1437   }
1438
1439   void Insert(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory) {
1440     Kumu::AutoMutex BlockLock(m_Lock);
1441     insert(FactoryList::value_type(label, factory));
1442   }
1443 };
1444
1445 //
1446 static FactoryList s_FactoryList;
1447 static Kumu::Mutex s_InitLock;
1448 static bool        s_TypesInit = false;
1449
1450
1451 //
1452 void
1453 ASDCP::MXF::SetObjectFactory(const ASDCP::UL& label, ASDCP::MXF::MXFObjectFactory_t factory)
1454 {
1455   s_FactoryList.Insert(label, factory);
1456 }
1457
1458 //
1459 ASDCP::MXF::InterchangeObject*
1460 ASDCP::MXF::CreateObject(const Dictionary*& Dict, const UL& label)
1461 {
1462   if ( ! s_TypesInit )
1463     {
1464       Kumu::AutoMutex BlockLock(s_InitLock);
1465
1466       if ( ! s_TypesInit )
1467         {
1468           MXF::Metadata_InitTypes(Dict);
1469           s_TypesInit = true;
1470         }
1471     }
1472
1473   FLi_t i = s_FactoryList.find(label.Value());
1474
1475   if ( i == s_FactoryList.end() )
1476     return new InterchangeObject(Dict);
1477
1478   return i->second(Dict);
1479 }
1480
1481
1482 //------------------------------------------------------------------------------------------
1483
1484 //
1485 bool
1486 ASDCP::MXF::decode_mca_string(const std::string& s, const mca_label_map_t& labels, const Dictionary*& dict, const std::string& language,
1487                               InterchangeObject_list_t& descriptor_list, ui32_t& channel_count)
1488 {
1489   std::string symbol_buf;
1490   channel_count = 0;
1491   ASDCP::MXF::SoundfieldGroupLabelSubDescriptor *current_soundfield = 0;
1492   std::string::const_iterator i;
1493
1494   for ( i = s.begin(); i != s.end(); ++i )
1495     {
1496       if ( *i == '(' )
1497         {
1498           if ( current_soundfield != 0 )
1499             {
1500               DefaultLogSink().Error("Encountered '(', already processing a soundfield group.\n");
1501               return false;
1502             }
1503
1504           if ( symbol_buf.empty() )
1505             {
1506               DefaultLogSink().Error("Encountered '(', without leading soundfield group symbol.\n");
1507               return false;
1508             }
1509
1510           mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1511       
1512           if ( i == labels.end() )
1513             {
1514               DefaultLogSink().Error("Unknown symbol: '%s'\n", symbol_buf.c_str());
1515               return false;
1516             }
1517       
1518           if ( i->second.ul.Value()[10] != 2 ) // magic depends on UL "Essence Facet" byte (see ST 428-12)
1519             {
1520               DefaultLogSink().Error("Not a soundfield group symbol: '%s'\n", symbol_buf.c_str());
1521               return false;
1522             }
1523
1524           current_soundfield = new ASDCP::MXF::SoundfieldGroupLabelSubDescriptor(dict);
1525           GenRandomValue(current_soundfield->MCALinkID);
1526
1527           current_soundfield->MCATagSymbol = (i->second.requires_prefix ? "sg" : "") + i->first;
1528           current_soundfield->MCATagName = i->second.tag_name;
1529           current_soundfield->RFC5646SpokenLanguage = language;
1530           current_soundfield->MCALabelDictionaryID = i->second.ul;
1531           descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(current_soundfield));
1532           symbol_buf.clear();
1533         }
1534       else if ( *i == ')' )
1535         {
1536           if ( current_soundfield == 0 )
1537             {
1538               DefaultLogSink().Error("Encountered ')', not currently processing a soundfield group.\n");
1539               return false;
1540             }
1541
1542           if ( symbol_buf.empty() )
1543             {
1544               DefaultLogSink().Error("Soundfield group description contains no channels.\n");
1545               return false;
1546             }
1547
1548           mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1549       
1550           if ( i == labels.end() )
1551             {
1552               DefaultLogSink().Error("Unknown symbol: '%s'\n", symbol_buf.c_str());
1553               return false;
1554             }
1555
1556           assert(current_soundfield);
1557
1558           ASDCP::MXF::AudioChannelLabelSubDescriptor *channel_descr =
1559             new ASDCP::MXF::AudioChannelLabelSubDescriptor(dict);
1560           GenRandomValue(channel_descr->MCALinkID);
1561
1562           channel_descr->SoundfieldGroupLinkID = current_soundfield->MCALinkID;
1563           channel_descr->MCAChannelID = channel_count++ + 1;
1564           channel_descr->MCATagSymbol = (i->second.requires_prefix ? "ch" : "") + i->first;
1565           channel_descr->MCATagName = i->second.tag_name;
1566           channel_descr->RFC5646SpokenLanguage = language;
1567           channel_descr->MCALabelDictionaryID = i->second.ul;
1568           descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(channel_descr));
1569           symbol_buf.clear();
1570           current_soundfield = 0;
1571         }
1572       else if ( *i == ',' )
1573         {
1574           if ( ! symbol_buf.empty() && ! symbol_buf.compare("-") )
1575             {
1576               channel_count++;
1577               symbol_buf.clear();
1578             }
1579           else if ( ! symbol_buf.empty() )
1580             {
1581               mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1582
1583               if ( i == labels.end() )
1584                 {
1585                   DefaultLogSink().Error("Unknown symbol: '%s'\n", symbol_buf.c_str());
1586                   return false;
1587                 }
1588
1589               if ( i->second.ul.Value()[10] != 1 ) // magic depends on UL "Essence Facet" byte (see ST 428-12)
1590                 {
1591                   DefaultLogSink().Error("Not a channel symbol: '%s'\n", symbol_buf.c_str());
1592                   return false;
1593                 }
1594
1595               ASDCP::MXF::AudioChannelLabelSubDescriptor *channel_descr =
1596                 new ASDCP::MXF::AudioChannelLabelSubDescriptor(dict);
1597               GenRandomValue(channel_descr->MCALinkID);
1598
1599               if ( current_soundfield != 0 )
1600                 {
1601                   channel_descr->SoundfieldGroupLinkID = current_soundfield->MCALinkID;
1602                 }
1603
1604               channel_descr->MCAChannelID = channel_count++ + 1;
1605               channel_descr->MCATagSymbol = (i->second.requires_prefix ? "ch" : "") + i->first;
1606               channel_descr->MCATagName = i->second.tag_name;
1607               channel_descr->RFC5646SpokenLanguage = language;
1608               channel_descr->MCALabelDictionaryID = i->second.ul;
1609               descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(channel_descr));
1610               symbol_buf.clear();
1611             }
1612         }
1613       else if ( *i == '-' || isalnum(*i) )
1614         {
1615           symbol_buf += *i;
1616         }
1617       else if ( ! isspace(*i) )
1618         {
1619           DefaultLogSink().Error("Unexpected character '%c'.\n", *i);
1620           return false;
1621         }
1622     }
1623
1624   if ( ! symbol_buf.empty() && ! symbol_buf.compare("-")  )
1625     {
1626       channel_count++;
1627     }
1628   else if ( ! symbol_buf.empty() )
1629     {
1630       mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1631       
1632       if ( i == labels.end() )
1633         {
1634           DefaultLogSink().Error("Unknown symbol: '%s'\n", symbol_buf.c_str());
1635           return false;
1636         }
1637
1638       ASDCP::MXF::AudioChannelLabelSubDescriptor *channel_descr =
1639         new ASDCP::MXF::AudioChannelLabelSubDescriptor(dict);
1640       GenRandomValue(channel_descr->MCALinkID);
1641
1642       if ( current_soundfield != 0 )
1643         {
1644           channel_descr->SoundfieldGroupLinkID = current_soundfield->MCALinkID;
1645         }
1646
1647       channel_descr->MCAChannelID = channel_count++ + 1;
1648       channel_descr->MCATagSymbol = (i->second.requires_prefix ? "ch" : "") + i->first;
1649       channel_descr->MCATagName = i->second.tag_name;
1650       channel_descr->RFC5646SpokenLanguage = language;
1651       channel_descr->MCALabelDictionaryID = i->second.ul;
1652       descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(channel_descr));
1653     }
1654
1655   return true;
1656 }
1657
1658 //
1659 ASDCP::MXF::ASDCP_MCAConfigParser::ASDCP_MCAConfigParser(const Dictionary*& d) : m_Dict(d), m_ChannelCount(0)
1660 {
1661   typedef mca_label_map_t::value_type pair;
1662   m_LabelMap.insert(pair("L",     label_traits("Left"                              , true,  m_Dict->ul(MDD_DCAudioChannel_L))));
1663   m_LabelMap.insert(pair("R",     label_traits("Right"                             , true,  m_Dict->ul(MDD_DCAudioChannel_R))));
1664   m_LabelMap.insert(pair("C",     label_traits("Center"                            , true,  m_Dict->ul(MDD_DCAudioChannel_C))));
1665   m_LabelMap.insert(pair("LFE",   label_traits("LFE"                               , true,  m_Dict->ul(MDD_DCAudioChannel_LFE))));
1666   m_LabelMap.insert(pair("Ls",    label_traits("Left Surround"                     , true,  m_Dict->ul(MDD_DCAudioChannel_Ls))));
1667   m_LabelMap.insert(pair("Rs",    label_traits("Right Surround"                    , true,  m_Dict->ul(MDD_DCAudioChannel_Rs))));
1668   m_LabelMap.insert(pair("Lss",   label_traits("Left Side Surround"                , true,  m_Dict->ul(MDD_DCAudioChannel_Lss))));
1669   m_LabelMap.insert(pair("Rss",   label_traits("Right Side Surround"               , true,  m_Dict->ul(MDD_DCAudioChannel_Rss))));
1670   m_LabelMap.insert(pair("Lrs",   label_traits("Left Rear Surround"                , true,  m_Dict->ul(MDD_DCAudioChannel_Lrs))));
1671   m_LabelMap.insert(pair("Rrs",   label_traits("Right Rear Surround"               , true,  m_Dict->ul(MDD_DCAudioChannel_Rrs))));
1672   m_LabelMap.insert(pair("Lc",    label_traits("Left Center"                       , true,  m_Dict->ul(MDD_DCAudioChannel_Lc))));
1673   m_LabelMap.insert(pair("Rc",    label_traits("Right Center"                      , true,  m_Dict->ul(MDD_DCAudioChannel_Rc))));
1674   m_LabelMap.insert(pair("Cs",    label_traits("Center Surround"                   , true,  m_Dict->ul(MDD_DCAudioChannel_Cs))));
1675   m_LabelMap.insert(pair("HI",    label_traits("Hearing Impaired"                  , true,  m_Dict->ul(MDD_DCAudioChannel_HI))));
1676   m_LabelMap.insert(pair("VIN",   label_traits("Visually Impaired-Narrative"       , true,  m_Dict->ul(MDD_DCAudioChannel_VIN))));
1677   m_LabelMap.insert(pair("51",    label_traits("5.1"                               , true,  m_Dict->ul(MDD_DCAudioSoundfield_51))));
1678   m_LabelMap.insert(pair("71",    label_traits("7.1DS"                             , true,  m_Dict->ul(MDD_DCAudioSoundfield_71))));
1679   m_LabelMap.insert(pair("SDS",   label_traits("7.1SDS"                            , true,  m_Dict->ul(MDD_DCAudioSoundfield_SDS))));
1680   m_LabelMap.insert(pair("61",    label_traits("6.1"                               , true,  m_Dict->ul(MDD_DCAudioSoundfield_61))));
1681   m_LabelMap.insert(pair("M",     label_traits("1.0 Monaural"                      , true,  m_Dict->ul(MDD_DCAudioSoundfield_M))));
1682   m_LabelMap.insert(pair("DBOX",  label_traits("D-BOX Motion Code Primary Stream"  , false, m_Dict->ul(MDD_DBOXMotionCodePrimaryStream))));
1683   m_LabelMap.insert(pair("DBOX2", label_traits("D-BOX Motion Code Secondary Stream", false, m_Dict->ul(MDD_DBOXMotionCodeSecondaryStream))));
1684 }
1685
1686 //
1687 ui32_t
1688 ASDCP::MXF::ASDCP_MCAConfigParser::ChannelCount() const
1689 {
1690   return m_ChannelCount;
1691 }
1692
1693 // 51(L,R,C,LFE,Ls,Rs),HI,VIN
1694 bool
1695 ASDCP::MXF::ASDCP_MCAConfigParser::DecodeString(const std::string& s, const std::string& language)
1696 {
1697   return decode_mca_string(s, m_LabelMap, m_Dict, language, *this, m_ChannelCount);
1698 }
1699
1700
1701
1702 ASDCP::MXF::AS02_MCAConfigParser::AS02_MCAConfigParser(const Dictionary*& d) : ASDCP::MXF::ASDCP_MCAConfigParser(d)
1703 {
1704   typedef mca_label_map_t::value_type pair;
1705   m_LabelMap.insert(pair("M1",    label_traits("M1",  true,  m_Dict->ul(MDD_IMFAudioChannel_M1))));
1706   m_LabelMap.insert(pair("M2",    label_traits("M2",  true,  m_Dict->ul(MDD_IMFAudioChannel_M2))));
1707   m_LabelMap.insert(pair("Lt",    label_traits("Lt",  true,  m_Dict->ul(MDD_IMFAudioChannel_Lt))));
1708   m_LabelMap.insert(pair("Rt",    label_traits("Rt",  true,  m_Dict->ul(MDD_IMFAudioChannel_Rt))));
1709   m_LabelMap.insert(pair("Lst",   label_traits("Lst", true,  m_Dict->ul(MDD_IMFAudioChannel_Lst))));
1710   m_LabelMap.insert(pair("Rst",   label_traits("Rst", true,  m_Dict->ul(MDD_IMFAudioChannel_Rst))));
1711   m_LabelMap.insert(pair("S",     label_traits("S",   true,  m_Dict->ul(MDD_IMFAudioChannel_S))));
1712   m_LabelMap.insert(pair("ST",    label_traits("ST",  true,  m_Dict->ul(MDD_IMFAudioSoundfield_ST))));
1713   m_LabelMap.insert(pair("DM",    label_traits("DM",  true,  m_Dict->ul(MDD_IMFAudioSoundfield_DM))));
1714   m_LabelMap.insert(pair("DNS",   label_traits("DNS", true,  m_Dict->ul(MDD_IMFAudioSoundfield_DNS))));
1715   m_LabelMap.insert(pair("30",    label_traits("30",  true,  m_Dict->ul(MDD_IMFAudioSoundfield_30))));
1716   m_LabelMap.insert(pair("40",    label_traits("40",  true,  m_Dict->ul(MDD_IMFAudioSoundfield_40))));
1717   m_LabelMap.insert(pair("50",    label_traits("50",  true,  m_Dict->ul(MDD_IMFAudioSoundfield_50))));
1718   m_LabelMap.insert(pair("60",    label_traits("60",  true,  m_Dict->ul(MDD_IMFAudioSoundfield_60))));
1719   m_LabelMap.insert(pair("70",    label_traits("70",  true,  m_Dict->ul(MDD_IMFAudioSoundfield_70))));
1720   m_LabelMap.insert(pair("LtRt",  label_traits("LtRt",true,  m_Dict->ul(MDD_IMFAudioSoundfield_LtRt))));
1721   m_LabelMap.insert(pair("51Ex",  label_traits("51Ex",true,  m_Dict->ul(MDD_IMFAudioSoundfield_51Ex))));
1722   m_LabelMap.insert(pair("HI",    label_traits("HI",  true,  m_Dict->ul(MDD_IMFAudioSoundfield_HI))));
1723   m_LabelMap.insert(pair("VIN",   label_traits("VIN", true,  m_Dict->ul(MDD_IMFAudioSoundfield_VIN))));
1724 }
1725
1726
1727
1728 //
1729 bool
1730 ASDCP::MXF::GetEditRateFromFP(ASDCP::MXF::OP1aHeader& header, ASDCP::Rational& edit_rate)
1731 {
1732   bool has_first_item = false;
1733
1734   MXF::InterchangeObject* temp_item;
1735   std::list<MXF::InterchangeObject*> temp_items;
1736
1737   Result_t result = header.GetMDObjectsByType(DefaultCompositeDict().ul(MDD_SourcePackage), temp_items);
1738
1739   if ( KM_FAILURE(result) )
1740     {
1741       DefaultLogSink().Error("The MXF header does not contain a FilePackage item.\n");
1742       return false;
1743     }
1744
1745   if ( temp_items.size() != 1 )
1746     {
1747       DefaultLogSink().Error("The MXF header must contain one FilePackage item, found %d.\n", temp_items.size());
1748       return false;
1749     }
1750
1751   char buf[64];
1752   MXF::Array<UUID>::const_iterator i;
1753   MXF::SourcePackage *source_package = dynamic_cast<MXF::SourcePackage*>(temp_items.front());
1754   assert(source_package);
1755
1756   for ( i = source_package->Tracks.begin(); i != source_package->Tracks.end(); ++i )
1757     {
1758       // Track
1759       result = header.GetMDObjectByID(*i, &temp_item);
1760
1761       if ( KM_FAILURE(result) )
1762         {
1763           DefaultLogSink().Error("The MXF header is incomplete: strong reference %s leads nowhere.\n",
1764                                  i->EncodeHex(buf, 64));
1765           return false;
1766         }
1767
1768       MXF::Track *track = dynamic_cast<MXF::Track*>(temp_item);
1769
1770       if ( track == 0 )
1771         {
1772           DefaultLogSink().Error("The MXF header is incomplete: %s is not a Track item.\n",
1773                                  i->EncodeHex(buf, 64));
1774           return false;
1775         }
1776
1777       // Sequence
1778       result = header.GetMDObjectByID(track->Sequence, &temp_item);
1779
1780       if ( KM_FAILURE(result) )
1781         {
1782           DefaultLogSink().Error("The MXF header is incomplete: strong reference %s leads nowhere.\n",
1783                                  i->EncodeHex(buf, 64));
1784           return false;
1785         }
1786
1787       MXF::Sequence *sequence = dynamic_cast<MXF::Sequence*>(temp_item);
1788
1789       if ( sequence == 0 )
1790         {
1791           DefaultLogSink().Error("The MXF header is incomplete: %s is not a Sequence item.\n",
1792                                  track->Sequence.get().EncodeHex(buf, 64));
1793           return false;
1794         }
1795
1796       if ( sequence->StructuralComponents.size() != 1 )
1797         {
1798           DefaultLogSink().Error("The Sequence item must contain one reference to an esence item, found %d.\n",
1799                                  sequence->StructuralComponents.size());
1800           return false;
1801         }
1802
1803       // SourceClip
1804       result = header.GetMDObjectByID(sequence->StructuralComponents.front(), &temp_item);
1805
1806       if ( KM_FAILURE(result) )
1807         {
1808           DefaultLogSink().Error("The MXF header is incomplete: strong reference %s leads nowhere.\n",
1809                                  sequence->StructuralComponents.front().EncodeHex(buf, 64));
1810           return false;
1811         }
1812
1813       if ( temp_item->IsA(DefaultCompositeDict().ul(MDD_SourceClip)) )
1814         {
1815           MXF::SourceClip *source_clip = dynamic_cast<MXF::SourceClip*>(temp_item);
1816
1817           if ( source_clip == 0 )
1818             {
1819               DefaultLogSink().Error("The MXF header is incomplete: %s is not a SourceClip item.\n",
1820                                      sequence->StructuralComponents.front().EncodeHex(buf, 64));
1821               return false;
1822             }
1823
1824           if ( ! has_first_item )
1825             {
1826               edit_rate = track->EditRate;
1827               has_first_item = true;
1828             }
1829           else if ( edit_rate != track->EditRate )
1830             {
1831               DefaultLogSink().Error("The MXF header is incomplete: %s EditRate value does not match others in the file.\n",
1832                                      sequence->StructuralComponents.front().EncodeHex(buf, 64));
1833               return false;
1834             }
1835         }
1836       else if ( ! temp_item->IsA(DefaultCompositeDict().ul(MDD_TimecodeComponent)) )
1837         {
1838           DefaultLogSink().Error("Reference from Sequence to an unexpected type: %s.\n", temp_item->ObjectName());
1839           return false;
1840         }
1841     }
1842
1843   return true;
1844 }
1845
1846
1847 //
1848 // end MXF.cpp
1849 //