7f042eebfee95fc298578589af04eb0f6db79390
[asdcplib.git] / src / MXF.cpp
1 /*
2 Copyright (c) 2005-2012, 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 KLV empty 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 ASDCP::Result_t
104 ASDCP::MXF::RIP::GetPairBySID(ui32_t SID, Pair& outPair) const
105 {
106   Array<Pair>::const_iterator pi = PairArray.begin();
107   for ( ; pi != PairArray.end(); pi++ )
108     {
109       if ( (*pi).BodySID == SID )
110         {
111           outPair = *pi;
112           return RESULT_OK;
113         }
114     }
115
116   return RESULT_FAIL;
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;
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;
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;
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;
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;
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;
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.push_back(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), ObjectModelVersion(0)
590 {
591   assert(m_Dict);
592   m_UL = m_Dict->Type(MDD_Preface).ul;
593 }
594
595 //
596 void
597 ASDCP::MXF::Preface::Copy(const Preface& rhs)
598 {
599   InterchangeObject::Copy(rhs);
600
601   LastModifiedDate = rhs.LastModifiedDate;
602   Version = rhs.Version;
603   ObjectModelVersion = rhs.ObjectModelVersion;
604   PrimaryPackage = rhs.PrimaryPackage;
605   Identifications = rhs.Identifications;
606   ContentStorage = rhs.ContentStorage;
607   OperationalPattern = rhs.OperationalPattern;
608   EssenceContainers = rhs.EssenceContainers;
609   DMSchemes = rhs.DMSchemes;
610 }
611
612 //
613 ASDCP::Result_t
614 ASDCP::MXF::Preface::InitFromTLVSet(TLVReader& TLVSet)
615 {
616   Result_t result = InterchangeObject::InitFromTLVSet(TLVSet);
617   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, LastModifiedDate));
618   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi16(OBJ_READ_ARGS(Preface, Version));
619   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi32(OBJ_READ_ARGS(Preface, ObjectModelVersion));
620   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, PrimaryPackage));
621   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, Identifications));
622   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, ContentStorage));
623   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, OperationalPattern));
624   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, EssenceContainers));
625   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, DMSchemes));
626   return result;
627 }
628
629 //
630 ASDCP::Result_t
631 ASDCP::MXF::Preface::WriteToTLVSet(TLVWriter& TLVSet)
632 {
633   Result_t result = InterchangeObject::WriteToTLVSet(TLVSet);
634   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, LastModifiedDate));
635   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteUi16(OBJ_WRITE_ARGS(Preface, Version));
636   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteUi32(OBJ_WRITE_ARGS(Preface, ObjectModelVersion));
637   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, PrimaryPackage));
638   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, Identifications));
639   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, ContentStorage));
640   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, OperationalPattern));
641   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, EssenceContainers));
642   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, DMSchemes));
643   return result;
644 }
645
646 //
647 ASDCP::Result_t
648 ASDCP::MXF::Preface::InitFromBuffer(const byte_t* p, ui32_t l)
649 {
650   return InterchangeObject::InitFromBuffer(p, l);
651 }
652
653 //
654 ASDCP::Result_t
655 ASDCP::MXF::Preface::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
656 {
657   return InterchangeObject::WriteToBuffer(Buffer);
658 }
659
660 //
661 void
662 ASDCP::MXF::Preface::Dump(FILE* stream)
663 {
664   char identbuf[IdentBufferLen];
665
666   if ( stream == 0 )
667     stream = stderr;
668
669   InterchangeObject::Dump(stream);
670   fprintf(stream, "  %22s = %s\n",  "LastModifiedDate", LastModifiedDate.EncodeString(identbuf, IdentBufferLen));
671   fprintf(stream, "  %22s = %hu\n", "Version", Version);
672   fprintf(stream, "  %22s = %u\n",  "ObjectModelVersion", ObjectModelVersion);
673   fprintf(stream, "  %22s = %s\n",  "PrimaryPackage", PrimaryPackage.EncodeHex(identbuf, IdentBufferLen));
674   fprintf(stream, "  %22s:\n", "Identifications");  Identifications.Dump(stream);
675   fprintf(stream, "  %22s = %s\n",  "ContentStorage", ContentStorage.EncodeHex(identbuf, IdentBufferLen));
676   fprintf(stream, "  %22s = %s\n",  "OperationalPattern", OperationalPattern.EncodeString(identbuf, IdentBufferLen));
677   fprintf(stream, "  %22s:\n", "EssenceContainers");  EssenceContainers.Dump(stream);
678   fprintf(stream, "  %22s:\n", "DMSchemes");  DMSchemes.Dump(stream);
679 }
680
681 //------------------------------------------------------------------------------------------
682 //
683
684 ASDCP::MXF::OPAtomHeader::OPAtomHeader(const Dictionary*& d) : Partition(d), m_Dict(d), m_RIP(d), m_Primer(d), m_Preface(0), m_HasRIP(false) {}
685 ASDCP::MXF::OPAtomHeader::~OPAtomHeader() {}
686
687 //
688 ASDCP::Result_t
689 ASDCP::MXF::OPAtomHeader::InitFromFile(const Kumu::FileReader& Reader)
690 {
691   m_HasRIP = false;
692   Result_t result = SeekToRIP(Reader);
693
694   if ( ASDCP_SUCCESS(result) )
695     {
696       result = m_RIP.InitFromFile(Reader);
697       ui32_t test_s = m_RIP.PairArray.size();
698
699       if ( ASDCP_FAILURE(result) )
700         {
701           DefaultLogSink().Error("File contains no RIP\n");
702           result = RESULT_OK;
703         }
704       else if ( test_s == 0 )
705         {
706           DefaultLogSink().Error("RIP contains no Pairs.\n");
707           result = RESULT_FORMAT;
708         }
709       else
710         {
711           if ( test_s < 2 )
712             {
713               // OP-Atom states that there will be either two or three partitions:
714               // one closed header and one closed footer with an optional body
715               // SMPTE 429-5 files may have many partitions, see SMPTE 410M
716               DefaultLogSink().Warn("RIP count is less than 2: %u\n", test_s);
717             }
718
719           m_HasRIP = true;
720       
721           if ( m_RIP.PairArray.front().ByteOffset !=  0 )
722             {
723               DefaultLogSink().Error("First Partition in RIP is not at offset 0.\n");
724               result = RESULT_FORMAT;
725             }
726         }
727     }
728
729   if ( ASDCP_SUCCESS(result) )
730     result = Reader.Seek(0);
731
732   if ( ASDCP_SUCCESS(result) )
733     result = Partition::InitFromFile(Reader); // test UL and OP
734
735   if ( ASDCP_FAILURE(result) )
736     return result;
737
738   // is it really OP-Atom?
739   assert(m_Dict);
740   UL OPAtomUL(SMPTE_390_OPAtom_Entry().ul);
741   UL InteropOPAtomUL(MXFInterop_OPAtom_Entry().ul);
742
743   if ( OperationalPattern.ExactMatch(OPAtomUL) ) // SMPTE
744     {
745       if ( m_Dict == &DefaultCompositeDict() )
746         m_Dict = &DefaultSMPTEDict();
747     }
748   else if ( OperationalPattern.ExactMatch(InteropOPAtomUL) ) // Interop
749     {
750       if ( m_Dict == &DefaultCompositeDict() )
751         m_Dict = &DefaultInteropDict();
752     }
753   else
754     {
755       char strbuf[IdentBufferLen];
756       const MDDEntry* Entry = m_Dict->FindUL(OperationalPattern.Value());
757       if ( Entry == 0 )
758         DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n",
759                               OperationalPattern.EncodeString(strbuf, IdentBufferLen));
760       else
761         DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n", Entry->name);
762     }
763
764   // slurp up the remainder of the header
765   if ( HeaderByteCount < 1024 )
766     DefaultLogSink().Warn("Improbably small HeaderByteCount value: %u\n", HeaderByteCount);
767
768   assert (HeaderByteCount <= 0xFFFFFFFFL);
769   result = m_Buffer.Capacity((ui32_t) HeaderByteCount);
770
771   if ( ASDCP_SUCCESS(result) )
772     {
773       ui32_t read_count;
774       result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
775
776       if ( ASDCP_FAILURE(result) )
777         return result;
778
779       if ( read_count != m_Buffer.Capacity() )
780         {
781           DefaultLogSink().Error("Short read of OP-Atom header metadata; wanted %u, got %u\n",
782                                  m_Buffer.Capacity(), read_count);
783           return RESULT_KLV_CODING;
784         }
785     }
786
787   if ( ASDCP_SUCCESS(result) )
788     result = InitFromBuffer(m_Buffer.RoData(), m_Buffer.Capacity());
789
790   return result;
791 }
792
793 //
794 ASDCP::Result_t
795 ASDCP::MXF::OPAtomHeader::InitFromPartitionBuffer(const byte_t* p, ui32_t l)
796 {
797   Result_t result = KLVPacket::InitFromBuffer(p, l);
798
799   if ( ASDCP_SUCCESS(result) )
800     result = Partition::InitFromBuffer(m_ValueStart, m_ValueLength); // test UL and OP
801
802   if ( ASDCP_SUCCESS(result) )
803     {
804       ui32_t pp_len = KLVPacket::PacketLength();
805       result = InitFromBuffer(p + pp_len, l - pp_len);
806     }
807
808   return result;
809 }
810
811 //
812 ASDCP::Result_t
813 ASDCP::MXF::OPAtomHeader::InitFromBuffer(const byte_t* p, ui32_t l)
814 {
815   assert(m_Dict);
816   Result_t result = RESULT_OK;
817   const byte_t* end_p = p + l;
818
819   while ( ASDCP_SUCCESS(result) && p < end_p )
820     {
821       // parse the packets and index them by uid, discard KLVFill items
822       InterchangeObject* object = CreateObject(m_Dict, p);
823       assert(object);
824
825       object->m_Lookup = &m_Primer;
826       result = object->InitFromBuffer(p, end_p - p);
827       const byte_t* redo_p = p;
828       p += object->PacketLength();
829       //      hexdump(p, object->PacketLength());
830
831       if ( ASDCP_SUCCESS(result) )
832         {
833           if ( object->IsA(m_Dict->ul(MDD_KLVFill)) )
834             {
835               delete object;
836             }
837           else if ( object->IsA(m_Dict->ul(MDD_Primer)) ) // TODO: only one primer should be found
838             {
839               delete object;
840               result = m_Primer.InitFromBuffer(redo_p, end_p - redo_p);
841             }
842           else
843             {
844               m_PacketList->AddPacket(object); // takes ownership
845
846               if ( object->IsA(m_Dict->ul(MDD_Preface)) && m_Preface == 0 )
847                 m_Preface = (Preface*)object;
848             }
849         }
850       else
851         {
852           DefaultLogSink().Error("Error initializing packet\n");
853           delete object;
854         }
855     }
856
857   return result;
858 }
859
860 ASDCP::Result_t
861 ASDCP::MXF::OPAtomHeader::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
862 {
863   return m_PacketList->GetMDObjectByID(ObjectID, Object);
864 }
865
866 //
867 ASDCP::Result_t
868 ASDCP::MXF::OPAtomHeader::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
869 {
870   InterchangeObject* TmpObject;
871
872   if ( Object == 0 )
873     Object = &TmpObject;
874
875   return m_PacketList->GetMDObjectByType(ObjectID, Object);
876 }
877
878 //
879 ASDCP::Result_t
880 ASDCP::MXF::OPAtomHeader::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
881 {
882   return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
883 }
884
885 //
886 ASDCP::MXF::Identification*
887 ASDCP::MXF::OPAtomHeader::GetIdentification()
888 {
889   InterchangeObject* Object;
890
891   if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object)) )
892     return (Identification*)Object;
893
894   return 0;
895 }
896
897 //
898 ASDCP::MXF::SourcePackage*
899 ASDCP::MXF::OPAtomHeader::GetSourcePackage()
900 {
901   InterchangeObject* Object;
902
903   if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object)) )
904     return (SourcePackage*)Object;
905
906   return 0;
907 }
908
909 //
910 ASDCP::MXF::RIP&
911 ASDCP::MXF::OPAtomHeader::GetRIP() { return m_RIP; }
912
913 //
914 ASDCP::Result_t
915 ASDCP::MXF::OPAtomHeader::WriteToFile(Kumu::FileWriter& Writer, ui32_t HeaderSize)
916 {
917   assert(m_Dict);
918   if ( m_Preface == 0 )
919     return RESULT_STATE;
920
921   if ( HeaderSize < 4096 ) 
922     {
923       DefaultLogSink().Error("HeaderSize %u is too small. Must be >= 4096\n", HeaderSize);
924       return RESULT_FAIL;
925     }
926
927   ASDCP::FrameBuffer HeaderBuffer;
928   HeaderByteCount = HeaderSize - ArchiveSize();
929   assert (HeaderByteCount <= 0xFFFFFFFFL);
930   Result_t result = HeaderBuffer.Capacity((ui32_t) HeaderByteCount); 
931   m_Preface->m_Lookup = &m_Primer;
932
933   std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
934   for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
935     {
936       InterchangeObject* object = *pl_i;
937       object->m_Lookup = &m_Primer;
938
939       ASDCP::FrameBuffer WriteWrapper;
940       WriteWrapper.SetData(HeaderBuffer.Data() + HeaderBuffer.Size(),
941                            HeaderBuffer.Capacity() - HeaderBuffer.Size());
942       result = object->WriteToBuffer(WriteWrapper);
943       HeaderBuffer.Size(HeaderBuffer.Size() + WriteWrapper.Size());
944     }
945
946   if ( ASDCP_SUCCESS(result) )
947     {
948       UL TmpUL(m_Dict->ul(MDD_ClosedCompleteHeader));
949       result = Partition::WriteToFile(Writer, TmpUL);
950     }
951
952   if ( ASDCP_SUCCESS(result) )
953     result = m_Primer.WriteToFile(Writer);
954
955   if ( ASDCP_SUCCESS(result) )
956     {
957       ui32_t write_count;
958       Writer.Write(HeaderBuffer.RoData(), HeaderBuffer.Size(), &write_count);
959       assert(write_count == HeaderBuffer.Size());
960     }
961
962   // KLV Fill
963   if ( ASDCP_SUCCESS(result) )
964     {
965       Kumu::fpos_t pos = Writer.Tell();
966
967       if ( pos > (Kumu::fpos_t)HeaderByteCount )
968         {
969           char intbuf[IntBufferLen];
970           DefaultLogSink().Error("Header size %s exceeds specified value %u\n",
971                                  ui64sz(pos, intbuf),
972                                  HeaderSize);
973           return RESULT_FAIL;
974         }
975
976       ASDCP::FrameBuffer NilBuf;
977       ui32_t klv_fill_length = HeaderSize - (ui32_t)pos;
978
979       if ( klv_fill_length < kl_length )
980         {
981           DefaultLogSink().Error("Remaining region too small for KLV Fill header\n");
982           return RESULT_FAIL;
983         }
984
985       klv_fill_length -= kl_length;
986       result = WriteKLToFile(Writer, m_Dict->ul(MDD_KLVFill), klv_fill_length);
987
988       if ( ASDCP_SUCCESS(result) )
989         result = NilBuf.Capacity(klv_fill_length);
990
991       if ( ASDCP_SUCCESS(result) )
992         {
993           memset(NilBuf.Data(), 0, klv_fill_length);
994           ui32_t write_count;
995           Writer.Write(NilBuf.RoData(), klv_fill_length, &write_count);
996           assert(write_count == klv_fill_length);
997         }
998     }
999
1000   return result;
1001 }
1002
1003 //
1004 void
1005 ASDCP::MXF::OPAtomHeader::Dump(FILE* stream)
1006 {
1007   if ( stream == 0 )
1008     stream = stderr;
1009
1010   Partition::Dump(stream);
1011   m_Primer.Dump(stream);
1012
1013   if ( m_Preface == 0 )
1014     fputs("No Preface loaded\n", stream);
1015
1016   std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
1017   for ( ; i != m_PacketList->m_List.end(); i++ )
1018     (*i)->Dump(stream);
1019 }
1020
1021 //------------------------------------------------------------------------------------------
1022 //
1023
1024 ASDCP::MXF::OPAtomIndexFooter::OPAtomIndexFooter(const Dictionary*& d) :
1025   Partition(d), m_Dict(d),
1026   m_CurrentSegment(0), m_BytesPerEditUnit(0), m_BodySID(0),
1027   m_ECOffset(0), m_Lookup(0)
1028 {
1029   BodySID = 0;
1030   IndexSID = 129;
1031 }
1032
1033 ASDCP::MXF::OPAtomIndexFooter::~OPAtomIndexFooter() {}
1034
1035 //
1036 ASDCP::Result_t
1037 ASDCP::MXF::OPAtomIndexFooter::InitFromFile(const Kumu::FileReader& Reader)
1038 {
1039   Result_t result = Partition::InitFromFile(Reader); // test UL and OP
1040
1041   // slurp up the remainder of the footer
1042   ui32_t read_count;
1043
1044   if ( ASDCP_SUCCESS(result) )
1045     {
1046       assert (IndexByteCount <= 0xFFFFFFFFL);
1047       result = m_Buffer.Capacity((ui32_t) IndexByteCount);
1048     }
1049
1050   if ( ASDCP_SUCCESS(result) )
1051     result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
1052
1053   if ( ASDCP_SUCCESS(result) && read_count != m_Buffer.Capacity() )
1054     {
1055       DefaultLogSink().Error("Short read of footer partition: got %u, expecting %u\n",
1056                              read_count, m_Buffer.Capacity());
1057       return RESULT_FAIL;
1058     }
1059
1060   if ( ASDCP_SUCCESS(result) )
1061     result = InitFromBuffer(m_Buffer.RoData(), m_Buffer.Capacity());
1062
1063   return result;
1064 }
1065
1066 //
1067 ASDCP::Result_t
1068 ASDCP::MXF::OPAtomIndexFooter::InitFromPartitionBuffer(const byte_t* p, ui32_t l)
1069 {
1070   Result_t result = KLVPacket::InitFromBuffer(p, l);
1071
1072   if ( ASDCP_SUCCESS(result) )
1073     result = Partition::InitFromBuffer(m_ValueStart, m_ValueLength); // test UL and OP
1074
1075   if ( ASDCP_SUCCESS(result) )
1076     {
1077       ui32_t pp_len = KLVPacket::PacketLength();
1078       result = InitFromBuffer(p + pp_len, l - pp_len);
1079     }
1080
1081   return result;
1082 }
1083
1084 //
1085 ASDCP::Result_t
1086 ASDCP::MXF::OPAtomIndexFooter::InitFromBuffer(const byte_t* p, ui32_t l)
1087 {
1088   Result_t result = RESULT_OK;
1089   const byte_t* end_p = p + l;
1090   
1091   while ( ASDCP_SUCCESS(result) && p < end_p )
1092     {
1093       // parse the packets and index them by uid, discard KLVFill items
1094       InterchangeObject* object = CreateObject(m_Dict, p);
1095       assert(object);
1096
1097       object->m_Lookup = m_Lookup;
1098       result = object->InitFromBuffer(p, end_p - p);
1099       p += object->PacketLength();
1100
1101       if ( ASDCP_SUCCESS(result) )
1102         {
1103           m_PacketList->AddPacket(object); // takes ownership
1104         }
1105       else
1106         {
1107           DefaultLogSink().Error("Error initializing packet\n");
1108           delete object;
1109         }
1110     }
1111
1112   if ( ASDCP_FAILURE(result) )
1113     DefaultLogSink().Error("Failed to initialize OPAtomIndexFooter\n");
1114
1115   return result;
1116 }
1117
1118 //
1119 ASDCP::Result_t
1120 ASDCP::MXF::OPAtomIndexFooter::WriteToFile(Kumu::FileWriter& Writer, ui64_t duration)
1121 {
1122   assert(m_Dict);
1123   ASDCP::FrameBuffer FooterBuffer;
1124   ui32_t   footer_size = m_PacketList->m_List.size() * MaxIndexSegmentSize; // segment-count * max-segment-size
1125   Result_t result = FooterBuffer.Capacity(footer_size); 
1126   ui32_t   iseg_count = 0;
1127
1128   if ( m_CurrentSegment != 0 )
1129     {
1130       m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1131       m_CurrentSegment = 0;
1132     }
1133
1134   std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
1135   for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
1136     {
1137       if ( (*pl_i)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1138         {
1139           iseg_count++;
1140           IndexTableSegment* Segment = (IndexTableSegment*)(*pl_i);
1141
1142           if ( m_BytesPerEditUnit != 0 )
1143             {
1144               if ( iseg_count != 1 )
1145                 return RESULT_STATE;
1146
1147               Segment->IndexDuration = duration;
1148             }
1149         }
1150
1151       InterchangeObject* object = *pl_i;
1152       object->m_Lookup = m_Lookup;
1153
1154       ASDCP::FrameBuffer WriteWrapper;
1155       WriteWrapper.SetData(FooterBuffer.Data() + FooterBuffer.Size(),
1156                            FooterBuffer.Capacity() - FooterBuffer.Size());
1157       result = object->WriteToBuffer(WriteWrapper);
1158       FooterBuffer.Size(FooterBuffer.Size() + WriteWrapper.Size());
1159     }
1160
1161   if ( ASDCP_SUCCESS(result) )
1162     {
1163       IndexByteCount = FooterBuffer.Size();
1164       UL FooterUL(m_Dict->ul(MDD_CompleteFooter));
1165       result = Partition::WriteToFile(Writer, FooterUL);
1166     }
1167
1168   if ( ASDCP_SUCCESS(result) )
1169     {
1170       ui32_t write_count = 0;
1171       result = Writer.Write(FooterBuffer.RoData(), FooterBuffer.Size(), &write_count);
1172       assert(write_count == FooterBuffer.Size());
1173     }
1174
1175   return result;
1176 }
1177
1178 //
1179 void
1180 ASDCP::MXF::OPAtomIndexFooter::Dump(FILE* stream)
1181 {
1182   if ( stream == 0 )
1183     stream = stderr;
1184
1185   Partition::Dump(stream);
1186
1187   std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
1188   for ( ; i != m_PacketList->m_List.end(); i++ )
1189     (*i)->Dump(stream);
1190 }
1191
1192 ASDCP::Result_t
1193 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
1194 {
1195   return m_PacketList->GetMDObjectByID(ObjectID, Object);
1196 }
1197
1198 //
1199 ASDCP::Result_t
1200 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
1201 {
1202   InterchangeObject* TmpObject;
1203
1204   if ( Object == 0 )
1205     Object = &TmpObject;
1206
1207   return m_PacketList->GetMDObjectByType(ObjectID, Object);
1208 }
1209
1210 //
1211 ASDCP::Result_t
1212 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
1213 {
1214   return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
1215 }
1216
1217 //
1218 ASDCP::Result_t
1219 ASDCP::MXF::OPAtomIndexFooter::Lookup(ui32_t frame_num, IndexTableSegment::IndexEntry& Entry) const
1220 {
1221   std::list<InterchangeObject*>::iterator li;
1222   for ( li = m_PacketList->m_List.begin(); li != m_PacketList->m_List.end(); li++ )
1223     {
1224       if ( (*li)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1225         {
1226           IndexTableSegment* Segment = (IndexTableSegment*)(*li);
1227           ui64_t start_pos = Segment->IndexStartPosition;
1228
1229           if ( Segment->EditUnitByteCount > 0 )
1230             {
1231               if ( m_PacketList->m_List.size() > 1 )
1232                 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
1233
1234               if ( ! Segment->IndexEntryArray.empty() )
1235                 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
1236
1237               Entry.StreamOffset = (ui64_t)frame_num * Segment->EditUnitByteCount;
1238               return RESULT_OK;
1239             }
1240           else if ( (ui64_t)frame_num >= start_pos
1241                     && (ui64_t)frame_num < (start_pos + Segment->IndexDuration) )
1242             {
1243               ui64_t tmp = frame_num - start_pos;
1244               assert(tmp <= 0xFFFFFFFFL);
1245               Entry = Segment->IndexEntryArray[(ui32_t) tmp];
1246               return RESULT_OK;
1247             }
1248         }
1249     }
1250
1251   return RESULT_FAIL;
1252 }
1253
1254 //
1255 void
1256 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const Rational& Rate)
1257 {
1258   assert(lookup);
1259   m_Lookup = lookup;
1260   m_BytesPerEditUnit = size;
1261   m_EditRate = Rate;
1262
1263   IndexTableSegment* Index = new IndexTableSegment(m_Dict);
1264   AddChildObject(Index);
1265   Index->EditUnitByteCount = m_BytesPerEditUnit;
1266   Index->IndexEditRate = Rate;
1267 }
1268
1269 //
1270 void
1271 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsVBR(IPrimerLookup* lookup, const Rational& Rate, Kumu::fpos_t offset)
1272 {
1273   assert(lookup);
1274   m_Lookup = lookup;
1275   m_BytesPerEditUnit = 0;
1276   m_EditRate = Rate;
1277   m_ECOffset = offset;
1278 }
1279
1280 //
1281 void
1282 ASDCP::MXF::OPAtomIndexFooter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
1283 {
1284   if ( m_BytesPerEditUnit != 0 )  // are we CBR? that's bad 
1285     {
1286       DefaultLogSink().Error("Call to PushIndexEntry() failed: index is CBR\n");
1287       return;
1288     }
1289
1290   // do we have an available segment?
1291   if ( m_CurrentSegment == 0 )
1292     { // no, set up a new segment
1293       m_CurrentSegment = new IndexTableSegment(m_Dict);
1294       assert(m_CurrentSegment);
1295       AddChildObject(m_CurrentSegment);
1296       m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1297       m_CurrentSegment->IndexEditRate = m_EditRate;
1298       m_CurrentSegment->IndexStartPosition = 0;
1299     }
1300   else if ( m_CurrentSegment->IndexEntryArray.size() >= CBRIndexEntriesPerSegment )
1301     { // no, this one is full, start another
1302       m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1303       ui64_t StartPosition = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
1304
1305       m_CurrentSegment = new IndexTableSegment(m_Dict);
1306       assert(m_CurrentSegment);
1307       AddChildObject(m_CurrentSegment);
1308       m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1309       m_CurrentSegment->IndexEditRate = m_EditRate;
1310       m_CurrentSegment->IndexStartPosition = StartPosition;
1311     }
1312
1313   m_CurrentSegment->IndexEntryArray.push_back(Entry);
1314 }
1315
1316 //------------------------------------------------------------------------------------------
1317 //
1318
1319 //
1320 void
1321 ASDCP::MXF::InterchangeObject::Copy(const InterchangeObject& rhs)
1322 {
1323   m_UL = rhs.m_UL;
1324   InstanceUID = rhs.InstanceUID;
1325   GenerationUID = rhs.GenerationUID;
1326 }
1327
1328 //
1329 ASDCP::Result_t
1330 ASDCP::MXF::InterchangeObject::InitFromTLVSet(TLVReader& TLVSet)
1331 {
1332   Result_t result = TLVSet.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID));
1333   if ( ASDCP_SUCCESS(result) )
1334     result = TLVSet.ReadObject(OBJ_READ_ARGS(GenerationInterchangeObject, GenerationUID));
1335   return result;
1336 }
1337
1338 //
1339 ASDCP::Result_t
1340 ASDCP::MXF::InterchangeObject::WriteToTLVSet(TLVWriter& TLVSet)
1341 {
1342   Result_t result = TLVSet.WriteObject(OBJ_WRITE_ARGS(InterchangeObject, InstanceUID));
1343   if ( ASDCP_SUCCESS(result) )
1344     result = TLVSet.WriteObject(OBJ_WRITE_ARGS(GenerationInterchangeObject, GenerationUID));
1345   return result;
1346 }
1347
1348 //
1349 ASDCP::Result_t
1350 ASDCP::MXF::InterchangeObject::InitFromBuffer(const byte_t* p, ui32_t l)
1351 {
1352   ASDCP_TEST_NULL(p);
1353   Result_t result = RESULT_FALSE;
1354
1355   if ( m_UL.HasValue() )
1356     {
1357       result = KLVPacket::InitFromBuffer(p, l, m_UL);
1358
1359       if ( ASDCP_SUCCESS(result) )
1360         {
1361           TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup);
1362           result = InitFromTLVSet(MemRDR);
1363         }
1364     }
1365   else
1366     {
1367       result = KLVPacket::InitFromBuffer(p, l);
1368     }
1369   
1370   return result;
1371 }
1372
1373 //
1374 ASDCP::Result_t
1375 ASDCP::MXF::InterchangeObject::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
1376 {
1377   if ( ! m_UL.HasValue() )
1378     return RESULT_STATE;
1379
1380   TLVWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length, m_Lookup);
1381   Result_t result = WriteToTLVSet(MemWRT);
1382
1383   if ( ASDCP_SUCCESS(result) )
1384     {
1385       ui32_t packet_length = MemWRT.Length();
1386       result = WriteKLToBuffer(Buffer, packet_length);
1387
1388       if ( ASDCP_SUCCESS(result) )
1389         Buffer.Size(Buffer.Size() + packet_length);
1390     }
1391
1392   return result;
1393 }
1394
1395 //
1396 void
1397 ASDCP::MXF::InterchangeObject::Dump(FILE* stream)
1398 {
1399   char identbuf[IdentBufferLen];
1400
1401   fputc('\n', stream);
1402   KLVPacket::Dump(stream, *m_Dict, false);
1403   fprintf(stream, "             InstanceUID = %s\n",  InstanceUID.EncodeHex(identbuf, IdentBufferLen));
1404   fprintf(stream, "           GenerationUID = %s\n",  GenerationUID.EncodeHex(identbuf, IdentBufferLen));
1405 }
1406
1407 //
1408 bool
1409 ASDCP::MXF::InterchangeObject::IsA(const byte_t* label)
1410 {
1411   if ( m_KLLength == 0 )
1412     return false;
1413
1414   return ( memcmp(label, m_KeyStart, SMPTE_UL_LENGTH) == 0 );
1415 }
1416
1417
1418 //------------------------------------------------------------------------------------------
1419
1420
1421 typedef std::map<ASDCP::UL, ASDCP::MXF::MXFObjectFactory_t>FactoryMap_t;
1422 typedef FactoryMap_t::iterator FLi_t;
1423
1424 //
1425 class FactoryList : public FactoryMap_t
1426 {
1427   Kumu::Mutex m_Lock;
1428
1429 public:
1430   FactoryList() {}
1431   ~FactoryList() {}
1432
1433   bool Empty() {
1434     Kumu::AutoMutex BlockLock(m_Lock);
1435     return empty();
1436   }
1437
1438   FLi_t Find(const byte_t* label) {
1439     Kumu::AutoMutex BlockLock(m_Lock);
1440     return find(label);
1441   }
1442
1443   FLi_t End() {
1444     Kumu::AutoMutex BlockLock(m_Lock);
1445     return end();
1446   }
1447
1448   void Insert(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory) {
1449     Kumu::AutoMutex BlockLock(m_Lock);
1450     insert(FactoryList::value_type(label, factory));
1451   }
1452 };
1453
1454 //
1455 static FactoryList s_FactoryList;
1456 static Kumu::Mutex s_InitLock;
1457 static bool        s_TypesInit = false;
1458
1459
1460 //
1461 void
1462 ASDCP::MXF::SetObjectFactory(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory)
1463 {
1464   s_FactoryList.Insert(label, factory);
1465 }
1466
1467 //
1468 ASDCP::MXF::InterchangeObject*
1469 ASDCP::MXF::CreateObject(const Dictionary*& Dict, const UL& label)
1470 {
1471   if ( ! s_TypesInit )
1472     {
1473       Kumu::AutoMutex BlockLock(s_InitLock);
1474
1475       if ( ! s_TypesInit )
1476         {
1477           MXF::Metadata_InitTypes(Dict);
1478           s_TypesInit = true;
1479         }
1480     }
1481
1482   FLi_t i = s_FactoryList.find(label.Value());
1483
1484   if ( i == s_FactoryList.end() )
1485     return new InterchangeObject(Dict);
1486
1487   return i->second(Dict);
1488 }
1489
1490
1491 //
1492 // end MXF.cpp
1493 //