working multi-dict
[asdcplib.git] / src / MXF.cpp
1 /*
2 Copyright (c) 2005-2009, 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 const ui32_t kl_length = ASDCP::SMPTE_UL_LENGTH + ASDCP::MXF_BER_LENGTH;
47
48 //
49 ASDCP::Result_t
50 ASDCP::MXF::SeekToRIP(const Kumu::FileReader& Reader)
51 {
52   Kumu::fpos_t end_pos;
53
54   // go to the end - 4 bytes
55   Result_t result = Reader.Seek(0, Kumu::SP_END);
56
57   if ( ASDCP_SUCCESS(result) )
58     result = Reader.Tell(&end_pos);
59
60   if ( ASDCP_SUCCESS(result)
61        && end_pos < (SMPTE_UL_LENGTH+MXF_BER_LENGTH) )
62     result = RESULT_FAIL;  // File is smaller than an empty packet!
63
64   if ( ASDCP_SUCCESS(result) )
65     result = Reader.Seek(end_pos - 4);
66
67   // get the ui32_t RIP length
68   ui32_t read_count;
69   byte_t intbuf[MXF_BER_LENGTH];
70   ui32_t rip_size = 0;
71
72   if ( ASDCP_SUCCESS(result) )
73     {
74       result = Reader.Read(intbuf, MXF_BER_LENGTH, &read_count);
75
76       if ( ASDCP_SUCCESS(result) && read_count != 4 )
77         result = RESULT_FAIL;
78     }
79
80   if ( ASDCP_SUCCESS(result) )
81     {
82       rip_size = KM_i32_BE(Kumu::cp2i<ui32_t>(intbuf));
83
84       if ( rip_size > end_pos ) // RIP can't be bigger than the file
85         return RESULT_FAIL;
86     }
87
88   // reposition to start of RIP
89   if ( ASDCP_SUCCESS(result) )
90     result = Reader.Seek(end_pos - rip_size);
91
92   return result;
93 }
94
95 //
96 ASDCP::Result_t
97 ASDCP::MXF::RIP::GetPairBySID(ui32_t SID, Pair& outPair) const
98 {
99   Array<Pair>::const_iterator pi = PairArray.begin();
100   for ( ; pi != PairArray.end(); pi++ )
101     {
102       if ( (*pi).BodySID == SID )
103         {
104           outPair = *pi;
105           return RESULT_OK;
106         }
107     }
108
109   return RESULT_FAIL;
110 }
111
112 //
113 ASDCP::Result_t
114 ASDCP::MXF::RIP::InitFromFile(const Kumu::FileReader& Reader)
115 {
116   Result_t result = KLVFilePacket::InitFromFile(Reader, m_Dict->ul(MDD_RandomIndexMetadata));
117
118   if ( ASDCP_SUCCESS(result) )
119     {
120       Kumu::MemIOReader MemRDR(m_ValueStart, m_ValueLength - 4);
121       result = PairArray.Unarchive(&MemRDR) ? RESULT_OK : RESULT_KLV_CODING;
122     }
123
124   if ( ASDCP_FAILURE(result) )
125     DefaultLogSink().Error("Failed to initialize RIP\n");
126
127   return result;
128 }
129
130 //
131 ASDCP::Result_t
132 ASDCP::MXF::RIP::WriteToFile(Kumu::FileWriter& Writer)
133 {
134   ASDCP::FrameBuffer Buffer;
135   ui32_t RIPSize = ( PairArray.size() * (sizeof(ui32_t) + sizeof(ui64_t)) ) + 4;
136   Result_t result = Buffer.Capacity(RIPSize);
137
138   if ( ASDCP_SUCCESS(result) )
139     result = WriteKLToFile(Writer, m_Dict->ul(MDD_RandomIndexMetadata), RIPSize);
140
141   if ( ASDCP_SUCCESS(result) )
142     {
143       result = RESULT_KLV_CODING;
144
145       Kumu::MemIOWriter MemWRT(Buffer.Data(), Buffer.Capacity());
146       if ( PairArray.Archive(&MemWRT) )
147         if ( MemWRT.WriteUi32BE(RIPSize + 20) )
148           {
149             Buffer.Size(MemWRT.Length());
150             result = RESULT_OK;
151           }
152     }
153
154   if ( ASDCP_SUCCESS(result) )
155     result = Writer.Write(Buffer.RoData(), Buffer.Size());
156
157   return result;
158 }
159
160 //
161 void
162 ASDCP::MXF::RIP::Dump(FILE* stream)
163 {
164   if ( stream == 0 )
165     stream = stderr;
166
167   KLVFilePacket::Dump(stream, *m_Dict, false);
168   PairArray.Dump(stream, false);
169 }
170
171 //------------------------------------------------------------------------------------------
172 //
173
174 //
175 class ASDCP::MXF::Partition::h__PacketList
176 {
177 public:
178   std::list<InterchangeObject*> m_List;
179   std::map<UUID, InterchangeObject*> m_Map;
180
181   ~h__PacketList() {
182     while ( ! m_List.empty() )
183       {
184         delete m_List.back();
185         m_List.pop_back();
186       }
187   }
188
189   //
190   void AddPacket(InterchangeObject* ThePacket)
191   {
192     assert(ThePacket);
193     m_Map.insert(std::map<UUID, InterchangeObject*>::value_type(ThePacket->InstanceUID, ThePacket));
194     m_List.push_back(ThePacket);
195   }
196
197   //
198   Result_t GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
199   {
200     ASDCP_TEST_NULL(Object);
201
202     std::map<UUID, InterchangeObject*>::iterator mi = m_Map.find(ObjectID);
203
204     if ( mi == m_Map.end() )
205       {
206         *Object = 0;
207         return RESULT_FAIL;
208       }
209
210     *Object = (*mi).second;
211     return RESULT_OK;
212   }
213
214   //
215   Result_t GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
216   {
217     ASDCP_TEST_NULL(ObjectID);
218     ASDCP_TEST_NULL(Object);
219     std::list<InterchangeObject*>::iterator li;
220     *Object = 0;
221
222     for ( li = m_List.begin(); li != m_List.end(); li++ )
223       {
224         if ( (*li)->HasUL(ObjectID) )
225           {
226             *Object = *li;
227             return RESULT_OK;
228           }
229       }
230
231     return RESULT_FAIL;
232   }
233
234   //
235   Result_t GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
236   {
237     ASDCP_TEST_NULL(ObjectID);
238     std::list<InterchangeObject*>::iterator li;
239
240     for ( li = m_List.begin(); li != m_List.end(); li++ )
241       {
242         if ( (*li)->HasUL(ObjectID) )
243           ObjectList.push_back(*li);
244       }
245
246     return ObjectList.empty() ? RESULT_FAIL : RESULT_OK;
247   }
248 };
249
250 //------------------------------------------------------------------------------------------
251 //
252
253
254 ASDCP::MXF::Partition::Partition(const Dictionary*& d) :
255   m_Dict(d),
256   MajorVersion(1), MinorVersion(2),
257   KAGSize(1), ThisPartition(0), PreviousPartition(0),
258   FooterPartition(0), HeaderByteCount(0), IndexByteCount(0), IndexSID(0),
259   BodyOffset(0), BodySID(0)
260 {
261   m_PacketList = new h__PacketList;
262 }
263
264 ASDCP::MXF::Partition::~Partition()
265 {
266 }
267
268 //
269 void
270 ASDCP::MXF::Partition::AddChildObject(InterchangeObject* Object)
271 {
272   assert(Object);
273
274   if ( ! Object->InstanceUID.HasValue() )
275     GenRandomValue(Object->InstanceUID);
276
277   m_PacketList->AddPacket(Object);
278 }
279
280 //
281 ASDCP::Result_t
282 ASDCP::MXF::Partition::InitFromFile(const Kumu::FileReader& Reader)
283 {
284   Result_t result = KLVFilePacket::InitFromFile(Reader);
285   // test the UL
286   // could be one of several values
287   if ( ASDCP_SUCCESS(result) )
288     result = ASDCP::MXF::Partition::InitFromBuffer(m_ValueStart, m_ValueLength);
289   
290   return result;
291 }
292
293 //
294 ASDCP::Result_t
295 ASDCP::MXF::Partition::InitFromBuffer(const byte_t* p, ui32_t l)
296 {
297   Kumu::MemIOReader MemRDR(p, l);
298   Result_t result = RESULT_KLV_CODING;
299
300   if ( MemRDR.ReadUi16BE(&MajorVersion) )
301     if ( MemRDR.ReadUi16BE(&MinorVersion) )
302       if ( MemRDR.ReadUi32BE(&KAGSize) )
303         if ( MemRDR.ReadUi64BE(&ThisPartition) )
304           if ( MemRDR.ReadUi64BE(&PreviousPartition) )
305             if ( MemRDR.ReadUi64BE(&FooterPartition) )
306               if ( MemRDR.ReadUi64BE(&HeaderByteCount) )
307                 if ( MemRDR.ReadUi64BE(&IndexByteCount) )
308                   if ( MemRDR.ReadUi32BE(&IndexSID) )
309                     if ( MemRDR.ReadUi64BE(&BodyOffset) )
310                       if ( MemRDR.ReadUi32BE(&BodySID) )
311                         if ( OperationalPattern.Unarchive(&MemRDR) )
312                           if ( EssenceContainers.Unarchive(&MemRDR) )
313                             result = RESULT_OK;
314
315   if ( ASDCP_FAILURE(result) )
316     DefaultLogSink().Error("Failed to initialize Partition\n");
317
318   return result;
319 }
320
321 //
322 ASDCP::Result_t
323 ASDCP::MXF::Partition::WriteToFile(Kumu::FileWriter& Writer, UL& PartitionLabel)
324 {
325   ASDCP::FrameBuffer Buffer;
326   Result_t result = Buffer.Capacity(1024);
327
328   if ( ASDCP_SUCCESS(result) )
329     {
330       Kumu::MemIOWriter MemWRT(Buffer.Data(), Buffer.Capacity());
331       result = RESULT_KLV_CODING;
332       if ( MemWRT.WriteUi16BE(MajorVersion) )
333         if ( MemWRT.WriteUi16BE(MinorVersion) )
334           if ( MemWRT.WriteUi32BE(KAGSize) )
335             if ( MemWRT.WriteUi64BE(ThisPartition) )
336               if ( MemWRT.WriteUi64BE(PreviousPartition) )
337                 if ( MemWRT.WriteUi64BE(FooterPartition) )
338                   if ( MemWRT.WriteUi64BE(HeaderByteCount) )
339                     if ( MemWRT.WriteUi64BE(IndexByteCount) )
340                       if ( MemWRT.WriteUi32BE(IndexSID) )
341                         if ( MemWRT.WriteUi64BE(BodyOffset) )
342                           if ( MemWRT.WriteUi32BE(BodySID) )
343                             if ( OperationalPattern.Archive(&MemWRT) )
344                               if ( EssenceContainers.Archive(&MemWRT) )
345                                 {
346                                   Buffer.Size(MemWRT.Length());
347                                   result = RESULT_OK;
348                                 }
349     }
350
351   if ( ASDCP_SUCCESS(result) )
352     {
353       ui32_t write_count;
354       result = WriteKLToFile(Writer, PartitionLabel.Value(), Buffer.Size());
355
356       if ( ASDCP_SUCCESS(result) )
357         result = Writer.Write(Buffer.RoData(), Buffer.Size(), &write_count);
358     }
359
360   return result;
361 }
362
363 //
364 ui32_t
365 ASDCP::MXF::Partition::ArchiveSize()
366 {
367   return ( kl_length
368            + sizeof(ui16_t) + sizeof(ui16_t)
369            + sizeof(ui32_t)
370            + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t)
371            + sizeof(ui32_t)
372            + sizeof(ui64_t)
373            + sizeof(ui32_t)
374            + SMPTE_UL_LENGTH
375            + sizeof(ui32_t) + sizeof(ui32_t) + ( UUIDlen * EssenceContainers.size() ) );
376 }
377
378 //
379 void
380 ASDCP::MXF::Partition::Dump(FILE* stream)
381 {
382   char identbuf[IdentBufferLen];
383
384   if ( stream == 0 )
385     stream = stderr;
386
387   KLVFilePacket::Dump(stream, *m_Dict, false);
388   fprintf(stream, "  MajorVersion       = %hu\n", MajorVersion);
389   fprintf(stream, "  MinorVersion       = %hu\n", MinorVersion);
390   fprintf(stream, "  KAGSize            = %u\n",  KAGSize);
391   fprintf(stream, "  ThisPartition      = %s\n",  ui64sz(ThisPartition, identbuf));
392   fprintf(stream, "  PreviousPartition  = %s\n",  ui64sz(PreviousPartition, identbuf));
393   fprintf(stream, "  FooterPartition    = %s\n",  ui64sz(FooterPartition, identbuf));
394   fprintf(stream, "  HeaderByteCount    = %s\n",  ui64sz(HeaderByteCount, identbuf));
395   fprintf(stream, "  IndexByteCount     = %s\n",  ui64sz(IndexByteCount, identbuf));
396   fprintf(stream, "  IndexSID           = %u\n",  IndexSID);
397   fprintf(stream, "  BodyOffset         = %s\n",  ui64sz(BodyOffset, identbuf));
398   fprintf(stream, "  BodySID            = %u\n",  BodySID);
399   fprintf(stream, "  OperationalPattern = %s\n",  OperationalPattern.EncodeString(identbuf, IdentBufferLen));
400   fputs("Essence Containers:\n", stream); EssenceContainers.Dump(stream);
401 }
402
403
404 //------------------------------------------------------------------------------------------
405 //
406
407 class ASDCP::MXF::Primer::h__PrimerLookup : public std::map<UL, TagValue>
408 {
409 public:
410   void InitWithBatch(ASDCP::MXF::Batch<ASDCP::MXF::Primer::LocalTagEntry>& Batch)
411   {
412     ASDCP::MXF::Batch<ASDCP::MXF::Primer::LocalTagEntry>::iterator i = Batch.begin();
413
414     for ( ; i != Batch.end(); i++ )
415       insert(std::map<UL, TagValue>::value_type((*i).UL, (*i).Tag));
416   }
417 };
418
419
420 //
421 ASDCP::MXF::Primer::Primer(const Dictionary*& d) : m_LocalTag(0xff), m_Dict(d) {}
422
423 //
424 ASDCP::MXF::Primer::~Primer() {}
425
426 //
427 void
428 ASDCP::MXF::Primer::ClearTagList()
429 {
430   LocalTagEntryBatch.clear();
431   m_Lookup = new h__PrimerLookup;
432 }
433
434 //
435 ASDCP::Result_t
436 ASDCP::MXF::Primer::InitFromBuffer(const byte_t* p, ui32_t l)
437 {
438   Result_t result = KLVPacket::InitFromBuffer(p, l, m_Dict->ul(MDD_Primer));
439
440   if ( ASDCP_SUCCESS(result) )
441     {
442       Kumu::MemIOReader MemRDR(m_ValueStart, m_ValueLength);
443       result = LocalTagEntryBatch.Unarchive(&MemRDR) ? RESULT_OK : RESULT_KLV_CODING;
444     }
445
446   if ( ASDCP_SUCCESS(result) )
447     {
448       m_Lookup = new h__PrimerLookup;
449       m_Lookup->InitWithBatch(LocalTagEntryBatch);
450     }
451
452   if ( ASDCP_FAILURE(result) )
453     DefaultLogSink().Error("Failed to initialize Primer\n");
454
455   return result;
456 }
457
458 //
459 ASDCP::Result_t
460 ASDCP::MXF::Primer::WriteToFile(Kumu::FileWriter& Writer)
461 {
462   ASDCP::FrameBuffer Buffer;
463   Result_t result = Buffer.Capacity(128*1024);
464
465   if ( ASDCP_SUCCESS(result) )
466     result = WriteToBuffer(Buffer);
467
468   if ( ASDCP_SUCCESS(result) )
469   result = Writer.Write(Buffer.RoData(), Buffer.Size());
470
471   return result;
472 }
473
474 //
475 ASDCP::Result_t
476 ASDCP::MXF::Primer::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
477 {
478   ASDCP::FrameBuffer LocalTagBuffer;
479   Kumu::MemIOWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length);
480   Result_t result = LocalTagEntryBatch.Archive(&MemWRT) ? RESULT_OK : RESULT_KLV_CODING;
481
482   if ( ASDCP_SUCCESS(result) )
483     {
484       ui32_t packet_length = MemWRT.Length();
485       result = WriteKLToBuffer(Buffer, m_Dict->ul(MDD_Primer), packet_length);
486
487       if ( ASDCP_SUCCESS(result) )
488         Buffer.Size(Buffer.Size() + packet_length);
489     }
490
491   return result;
492 }
493
494 //
495 ASDCP::Result_t
496 ASDCP::MXF::Primer::InsertTag(const MDDEntry& Entry, ASDCP::TagValue& Tag)
497 {
498   assert(m_Lookup);
499   UL TestUL(Entry.ul);
500   std::map<UL, TagValue>::iterator i = m_Lookup->find(TestUL);
501
502   if ( i == m_Lookup->end() )
503     {
504       if ( Entry.tag.a == 0 && Entry.tag.b == 0 )
505         {
506           Tag.a = 0xff;
507           Tag.b = m_LocalTag--;
508         }
509       else
510         {
511           Tag.a = Entry.tag.a;
512           Tag.b = Entry.tag.b;
513         }
514
515       LocalTagEntry TmpEntry;
516       TmpEntry.UL = TestUL;
517       TmpEntry.Tag = Tag;
518
519       LocalTagEntryBatch.push_back(TmpEntry);
520       m_Lookup->insert(std::map<UL, TagValue>::value_type(TmpEntry.UL, TmpEntry.Tag));
521     }
522   else
523     {
524       Tag = (*i).second;
525     }
526    
527   return RESULT_OK;
528 }
529
530 //
531 ASDCP::Result_t
532 ASDCP::MXF::Primer::TagForKey(const ASDCP::UL& Key, ASDCP::TagValue& Tag)
533 {
534   assert(m_Lookup);
535   if ( m_Lookup.empty() )
536     {
537       DefaultLogSink().Error("Primer lookup is empty\n");
538       return RESULT_FAIL;
539     }
540
541   std::map<UL, TagValue>::iterator i = m_Lookup->find(Key);
542
543   if ( i == m_Lookup->end() )
544     return RESULT_FALSE;
545
546   Tag = (*i).second;
547   return RESULT_OK;
548 }
549
550 //
551 void
552 ASDCP::MXF::Primer::Dump(FILE* stream)
553 {
554   char identbuf[IdentBufferLen];
555
556   if ( stream == 0 )
557     stream = stderr;
558
559   KLVPacket::Dump(stream, *m_Dict, false);
560   fprintf(stream, "Primer: %u %s\n",
561           (ui32_t)LocalTagEntryBatch.size(),
562           ( LocalTagEntryBatch.size() == 1 ? "entry" : "entries" ));
563   
564   Batch<LocalTagEntry>::iterator i = LocalTagEntryBatch.begin();
565   for ( ; i != LocalTagEntryBatch.end(); i++ )
566     {
567       const MDDEntry* Entry = m_Dict->FindUL((*i).UL.Value());
568       fprintf(stream, "  %s %s\n", (*i).EncodeString(identbuf, IdentBufferLen), (Entry ? Entry->name : "Unknown"));
569     }
570 }
571
572
573 //------------------------------------------------------------------------------------------
574 //
575
576 //
577 ASDCP::Result_t
578 ASDCP::MXF::Preface::InitFromTLVSet(TLVReader& TLVSet)
579 {
580   Result_t result = InterchangeObject::InitFromTLVSet(TLVSet);
581   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, LastModifiedDate));
582   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi16(OBJ_READ_ARGS(Preface, Version));
583   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi32(OBJ_READ_ARGS(Preface, ObjectModelVersion));
584   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, PrimaryPackage));
585   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, Identifications));
586   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, ContentStorage));
587   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, OperationalPattern));
588   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, EssenceContainers));
589   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, DMSchemes));
590   return result;
591 }
592
593 //
594 ASDCP::Result_t
595 ASDCP::MXF::Preface::WriteToTLVSet(TLVWriter& TLVSet)
596 {
597   Result_t result = InterchangeObject::WriteToTLVSet(TLVSet);
598   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, LastModifiedDate));
599   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteUi16(OBJ_WRITE_ARGS(Preface, Version));
600   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteUi32(OBJ_WRITE_ARGS(Preface, ObjectModelVersion));
601   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, PrimaryPackage));
602   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, Identifications));
603   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, ContentStorage));
604   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, OperationalPattern));
605   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, EssenceContainers));
606   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, DMSchemes));
607   return result;
608 }
609
610 //
611 ASDCP::Result_t
612 ASDCP::MXF::Preface::InitFromBuffer(const byte_t* p, ui32_t l)
613 {
614   m_Typeinfo = &(m_Dict->Type(MDD_Preface));
615   return InterchangeObject::InitFromBuffer(p, l);
616 }
617
618 //
619 ASDCP::Result_t
620 ASDCP::MXF::Preface::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
621 {
622   m_Typeinfo = &(m_Dict->Type(MDD_Preface));
623   return InterchangeObject::WriteToBuffer(Buffer);
624 }
625
626 //
627 void
628 ASDCP::MXF::Preface::Dump(FILE* stream)
629 {
630   char identbuf[IdentBufferLen];
631
632   if ( stream == 0 )
633     stream = stderr;
634
635   InterchangeObject::Dump(stream);
636   fprintf(stream, "  %22s = %s\n",  "LastModifiedDate", LastModifiedDate.EncodeString(identbuf, IdentBufferLen));
637   fprintf(stream, "  %22s = %hu\n", "Version", Version);
638   fprintf(stream, "  %22s = %u\n",  "ObjectModelVersion", ObjectModelVersion);
639   fprintf(stream, "  %22s = %s\n",  "PrimaryPackage", PrimaryPackage.EncodeHex(identbuf, IdentBufferLen));
640   fprintf(stream, "  %22s:\n", "Identifications");  Identifications.Dump(stream);
641   fprintf(stream, "  %22s = %s\n",  "ContentStorage", ContentStorage.EncodeHex(identbuf, IdentBufferLen));
642   fprintf(stream, "  %22s = %s\n",  "OperationalPattern", OperationalPattern.EncodeString(identbuf, IdentBufferLen));
643   fprintf(stream, "  %22s:\n", "EssenceContainers");  EssenceContainers.Dump(stream);
644   fprintf(stream, "  %22s:\n", "DMSchemes");  DMSchemes.Dump(stream);
645 }
646
647 //------------------------------------------------------------------------------------------
648 //
649
650 ASDCP::MXF::OPAtomHeader::OPAtomHeader(const Dictionary*& d) : Partition(d), m_Dict(d), m_RIP(d), m_Primer(d), m_Preface(0), m_HasRIP(false) {}
651 ASDCP::MXF::OPAtomHeader::~OPAtomHeader() {}
652
653 //
654 ASDCP::Result_t
655 ASDCP::MXF::OPAtomHeader::InitFromFile(const Kumu::FileReader& Reader)
656 {
657   m_HasRIP = false;
658   Result_t result = SeekToRIP(Reader);
659
660   if ( ASDCP_SUCCESS(result) )
661     {
662       result = m_RIP.InitFromFile(Reader);
663       ui32_t test_s = m_RIP.PairArray.size();
664
665       if ( ASDCP_FAILURE(result) )
666         {
667           DefaultLogSink().Error("File contains no RIP\n");
668           result = RESULT_OK;
669         }
670       else if ( test_s == 0 )
671         {
672           DefaultLogSink().Error("RIP contains no Pairs.\n");
673           result = RESULT_FORMAT;
674         }
675       else
676         {
677           if ( test_s < 2 )
678             {
679               // OP-Atom states that there will be either two or three partitions:
680               // one closed header and one closed footer with an optional body
681               // SMPTE 429-5 files may have many partitions, see SMPTE 410M
682               DefaultLogSink().Warn("RIP count is less than 2: %u\n", test_s);
683             }
684
685           m_HasRIP = true;
686       
687           if ( m_RIP.PairArray.front().ByteOffset !=  0 )
688             {
689               DefaultLogSink().Error("First Partition in RIP is not at offset 0.\n");
690               result = RESULT_FORMAT;
691             }
692         }
693     }
694
695   if ( ASDCP_SUCCESS(result) )
696     result = Reader.Seek(0);
697
698   if ( ASDCP_SUCCESS(result) )
699     result = Partition::InitFromFile(Reader); // test UL and OP
700
701   if ( ASDCP_FAILURE(result) )
702     return result;
703
704   // is it really OP-Atom?
705   UL OPAtomUL(m_Dict->ul(MDD_OPAtom));
706   UL InteropOPAtomUL(m_Dict->ul(MDD_MXFInterop_OPAtom));
707
708   if ( OperationalPattern == OPAtomUL ) // SMPTE
709     {
710       if ( m_Dict == &DefaultCompositeDict() )
711         m_Dict = &DefaultSMPTEDict();
712     }
713   else if ( OperationalPattern == InteropOPAtomUL ) // Interop
714     {
715       if ( m_Dict == &DefaultCompositeDict() )
716         m_Dict = &DefaultInteropDict();
717     }
718   else
719     {
720       char strbuf[IdentBufferLen];
721       const MDDEntry* Entry = m_Dict->FindUL(OperationalPattern.Value());
722       if ( Entry == 0 )
723         DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n",
724                               OperationalPattern.EncodeString(strbuf, IdentBufferLen));
725       else
726         DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n", Entry->name);
727     }
728
729   // slurp up the remainder of the header
730   if ( HeaderByteCount < 1024 )
731     DefaultLogSink().Warn("Improbably small HeaderByteCount value: %u\n", HeaderByteCount);
732
733   assert (HeaderByteCount <= 0xFFFFFFFFL);
734   result = m_Buffer.Capacity((ui32_t) HeaderByteCount);
735
736   if ( ASDCP_SUCCESS(result) )
737     {
738       ui32_t read_count;
739       result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
740
741       if ( ASDCP_FAILURE(result) )
742         return result;
743
744       if ( read_count != m_Buffer.Capacity() )
745         {
746           DefaultLogSink().Error("Short read of OP-Atom header metadata; wanted %u, got %u\n",
747                                  m_Buffer.Capacity(), read_count);
748           return RESULT_KLV_CODING;
749         }
750     }
751
752   if ( ASDCP_SUCCESS(result) )
753     result = InitFromBuffer(m_Buffer.RoData(), m_Buffer.Capacity());
754
755   return result;
756 }
757
758 //
759 ASDCP::Result_t
760 ASDCP::MXF::OPAtomHeader::InitFromPartitionBuffer(const byte_t* p, ui32_t l)
761 {
762   Result_t result = KLVPacket::InitFromBuffer(p, l);
763
764   if ( ASDCP_SUCCESS(result) )
765     result = Partition::InitFromBuffer(m_ValueStart, m_ValueLength); // test UL and OP
766
767   if ( ASDCP_SUCCESS(result) )
768     {
769       ui32_t pp_len = KLVPacket::PacketLength();
770       result = InitFromBuffer(p + pp_len, l - pp_len);
771     }
772
773   return result;
774 }
775
776 //
777 ASDCP::Result_t
778 ASDCP::MXF::OPAtomHeader::InitFromBuffer(const byte_t* p, ui32_t l)
779 {
780   Result_t result = RESULT_OK;
781   const byte_t* end_p = p + l;
782
783   while ( ASDCP_SUCCESS(result) && p < end_p )
784     {
785       // parse the packets and index them by uid, discard KLVFill items
786       InterchangeObject* object = CreateObject(m_Dict, p);
787       assert(object);
788
789       object->m_Lookup = &m_Primer;
790       result = object->InitFromBuffer(p, end_p - p);
791       const byte_t* redo_p = p;
792       p += object->PacketLength();
793       //      hexdump(p, object->PacketLength());
794
795       if ( ASDCP_SUCCESS(result) )
796         {
797           if ( object->IsA(m_Dict->ul(MDD_KLVFill)) )
798             {
799               delete object;
800             }
801           else if ( object->IsA(m_Dict->ul(MDD_Primer)) )
802             {
803               delete object;
804               result = m_Primer.InitFromBuffer(redo_p, end_p - redo_p);
805             }
806           else
807             {
808               m_PacketList->AddPacket(object);
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 packet\n");
817           delete object;
818         }
819     }
820
821   return result;
822 }
823
824 ASDCP::Result_t
825 ASDCP::MXF::OPAtomHeader::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
826 {
827   return m_PacketList->GetMDObjectByID(ObjectID, Object);
828 }
829
830 //
831 ASDCP::Result_t
832 ASDCP::MXF::OPAtomHeader::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
833 {
834   InterchangeObject* TmpObject;
835
836   if ( Object == 0 )
837     Object = &TmpObject;
838
839   return m_PacketList->GetMDObjectByType(ObjectID, Object);
840 }
841
842 //
843 ASDCP::Result_t
844 ASDCP::MXF::OPAtomHeader::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
845 {
846   return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
847 }
848
849 //
850 ASDCP::MXF::Identification*
851 ASDCP::MXF::OPAtomHeader::GetIdentification()
852 {
853   InterchangeObject* Object;
854
855   if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object)) )
856     return (Identification*)Object;
857
858   return 0;
859 }
860
861 //
862 ASDCP::MXF::SourcePackage*
863 ASDCP::MXF::OPAtomHeader::GetSourcePackage()
864 {
865   InterchangeObject* Object;
866
867   if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object)) )
868     return (SourcePackage*)Object;
869
870   return 0;
871 }
872
873
874 //
875 ASDCP::Result_t
876 ASDCP::MXF::OPAtomHeader::WriteToFile(Kumu::FileWriter& Writer, ui32_t HeaderSize)
877 {
878   if ( m_Preface == 0 )
879     return RESULT_STATE;
880
881   if ( HeaderSize < 4096 ) 
882     {
883       DefaultLogSink().Error("HeaderSize %u is too small. Must be >= 4096\n", HeaderSize);
884       return RESULT_FAIL;
885     }
886
887   ASDCP::FrameBuffer HeaderBuffer;
888   HeaderByteCount = HeaderSize - ArchiveSize();
889   assert (HeaderByteCount <= 0xFFFFFFFFL);
890   Result_t result = HeaderBuffer.Capacity((ui32_t) HeaderByteCount); 
891   m_Preface->m_Lookup = &m_Primer;
892
893   std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
894   for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
895     {
896       InterchangeObject* object = *pl_i;
897       object->m_Lookup = &m_Primer;
898
899       ASDCP::FrameBuffer WriteWrapper;
900       WriteWrapper.SetData(HeaderBuffer.Data() + HeaderBuffer.Size(),
901                            HeaderBuffer.Capacity() - HeaderBuffer.Size());
902       result = object->WriteToBuffer(WriteWrapper);
903       HeaderBuffer.Size(HeaderBuffer.Size() + WriteWrapper.Size());
904     }
905
906   if ( ASDCP_SUCCESS(result) )
907     {
908       UL TmpUL(m_Dict->ul(MDD_ClosedCompleteHeader));
909       result = Partition::WriteToFile(Writer, TmpUL);
910     }
911
912   if ( ASDCP_SUCCESS(result) )
913     result = m_Primer.WriteToFile(Writer);
914
915   if ( ASDCP_SUCCESS(result) )
916     {
917       ui32_t write_count;
918       Writer.Write(HeaderBuffer.RoData(), HeaderBuffer.Size(), &write_count);
919       assert(write_count == HeaderBuffer.Size());
920     }
921
922   // KLV Fill
923   if ( ASDCP_SUCCESS(result) )
924     {
925       Kumu::fpos_t pos = Writer.Tell();
926
927       if ( pos > (Kumu::fpos_t)HeaderByteCount )
928         {
929           char intbuf[IntBufferLen];
930           DefaultLogSink().Error("Header size %s exceeds specified value %u\n",
931                                  ui64sz(pos, intbuf),
932                                  HeaderSize);
933           return RESULT_FAIL;
934         }
935
936       ASDCP::FrameBuffer NilBuf;
937       ui32_t klv_fill_length = HeaderSize - (ui32_t)pos;
938
939       if ( klv_fill_length < kl_length )
940         {
941           DefaultLogSink().Error("Remaining region too small for KLV Fill header\n");
942           return RESULT_FAIL;
943         }
944
945       klv_fill_length -= kl_length;
946       result = WriteKLToFile(Writer, m_Dict->ul(MDD_KLVFill), klv_fill_length);
947
948       if ( ASDCP_SUCCESS(result) )
949         result = NilBuf.Capacity(klv_fill_length);
950
951       if ( ASDCP_SUCCESS(result) )
952         {
953           memset(NilBuf.Data(), 0, klv_fill_length);
954           ui32_t write_count;
955           Writer.Write(NilBuf.RoData(), klv_fill_length, &write_count);
956           assert(write_count == klv_fill_length);
957         }
958     }
959
960   return result;
961 }
962
963 //
964 void
965 ASDCP::MXF::OPAtomHeader::Dump(FILE* stream)
966 {
967   if ( stream == 0 )
968     stream = stderr;
969
970   Partition::Dump(stream);
971   m_Primer.Dump(stream);
972
973   if ( m_Preface == 0 )
974     fputs("No Preface loaded\n", stream);
975
976   std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
977   for ( ; i != m_PacketList->m_List.end(); i++ )
978     (*i)->Dump(stream);
979 }
980
981 //------------------------------------------------------------------------------------------
982 //
983
984 ASDCP::MXF::OPAtomIndexFooter::OPAtomIndexFooter(const Dictionary*& d) :
985   Partition(d), m_Dict(d),
986   m_CurrentSegment(0), m_BytesPerEditUnit(0), m_BodySID(0),
987   m_ECOffset(0), m_Lookup(0)
988 {
989   BodySID = 0;
990   IndexSID = 129;
991 }
992
993 ASDCP::MXF::OPAtomIndexFooter::~OPAtomIndexFooter() {}
994
995 //
996 ASDCP::Result_t
997 ASDCP::MXF::OPAtomIndexFooter::InitFromFile(const Kumu::FileReader& Reader)
998 {
999   Result_t result = Partition::InitFromFile(Reader); // test UL and OP
1000
1001   // slurp up the remainder of the footer
1002   ui32_t read_count;
1003
1004   if ( ASDCP_SUCCESS(result) )
1005     {
1006       assert (IndexByteCount <= 0xFFFFFFFFL);
1007       result = m_Buffer.Capacity((ui32_t) IndexByteCount);
1008     }
1009
1010   if ( ASDCP_SUCCESS(result) )
1011     result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
1012
1013   if ( ASDCP_SUCCESS(result) && read_count != m_Buffer.Capacity() )
1014     {
1015       DefaultLogSink().Error("Short read of footer partition: got %u, expecting %u\n",
1016                              read_count, m_Buffer.Capacity());
1017       return RESULT_FAIL;
1018     }
1019
1020   if ( ASDCP_SUCCESS(result) )
1021     result = InitFromBuffer(m_Buffer.RoData(), m_Buffer.Capacity());
1022
1023   return result;
1024 }
1025
1026 //
1027 ASDCP::Result_t
1028 ASDCP::MXF::OPAtomIndexFooter::InitFromPartitionBuffer(const byte_t* p, ui32_t l)
1029 {
1030   Result_t result = KLVPacket::InitFromBuffer(p, l);
1031
1032   if ( ASDCP_SUCCESS(result) )
1033     result = Partition::InitFromBuffer(m_ValueStart, m_ValueLength); // test UL and OP
1034
1035   if ( ASDCP_SUCCESS(result) )
1036     {
1037       ui32_t pp_len = KLVPacket::PacketLength();
1038       result = InitFromBuffer(p + pp_len, l - pp_len);
1039     }
1040
1041   return result;
1042 }
1043
1044 //
1045 ASDCP::Result_t
1046 ASDCP::MXF::OPAtomIndexFooter::InitFromBuffer(const byte_t* p, ui32_t l)
1047 {
1048   Result_t result = RESULT_OK;
1049   const byte_t* end_p = p + l;
1050   
1051   while ( ASDCP_SUCCESS(result) && p < end_p )
1052     {
1053       // parse the packets and index them by uid, discard KLVFill items
1054       InterchangeObject* object = CreateObject(m_Dict, p);
1055       assert(object);
1056
1057       object->m_Lookup = m_Lookup;
1058       result = object->InitFromBuffer(p, end_p - p);
1059       p += object->PacketLength();
1060
1061       if ( ASDCP_SUCCESS(result) )
1062         {
1063           m_PacketList->AddPacket(object);
1064         }
1065       else
1066         {
1067           DefaultLogSink().Error("Error initializing packet\n");
1068           delete object;
1069         }
1070     }
1071
1072   if ( ASDCP_FAILURE(result) )
1073     DefaultLogSink().Error("Failed to initialize OPAtomIndexFooter\n");
1074
1075   return result;
1076 }
1077
1078 //
1079 ASDCP::Result_t
1080 ASDCP::MXF::OPAtomIndexFooter::WriteToFile(Kumu::FileWriter& Writer, ui64_t duration)
1081 {
1082   ASDCP::FrameBuffer FooterBuffer;
1083   ui32_t   footer_size = m_PacketList->m_List.size() * MaxIndexSegmentSize; // segment-count * max-segment-size
1084   Result_t result = FooterBuffer.Capacity(footer_size); 
1085   ui32_t   iseg_count = 0;
1086
1087   if ( m_CurrentSegment != 0 )
1088     {
1089       m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1090       m_CurrentSegment = 0;
1091     }
1092
1093   std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
1094   for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
1095     {
1096       if ( (*pl_i)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1097         {
1098           iseg_count++;
1099           IndexTableSegment* Segment = (IndexTableSegment*)(*pl_i);
1100
1101           if ( m_BytesPerEditUnit != 0 )
1102             {
1103               if ( iseg_count != 1 )
1104                 return RESULT_STATE;
1105
1106               Segment->IndexDuration = duration;
1107             }
1108         }
1109
1110       InterchangeObject* object = *pl_i;
1111       object->m_Lookup = m_Lookup;
1112
1113       ASDCP::FrameBuffer WriteWrapper;
1114       WriteWrapper.SetData(FooterBuffer.Data() + FooterBuffer.Size(),
1115                            FooterBuffer.Capacity() - FooterBuffer.Size());
1116       result = object->WriteToBuffer(WriteWrapper);
1117       FooterBuffer.Size(FooterBuffer.Size() + WriteWrapper.Size());
1118     }
1119
1120   if ( ASDCP_SUCCESS(result) )
1121     {
1122       IndexByteCount = FooterBuffer.Size();
1123       UL FooterUL(m_Dict->ul(MDD_CompleteFooter));
1124       result = Partition::WriteToFile(Writer, FooterUL);
1125     }
1126
1127   if ( ASDCP_SUCCESS(result) )
1128     {
1129       ui32_t write_count = 0;
1130       result = Writer.Write(FooterBuffer.RoData(), FooterBuffer.Size(), &write_count);
1131       assert(write_count == FooterBuffer.Size());
1132     }
1133
1134   return result;
1135 }
1136
1137 //
1138 void
1139 ASDCP::MXF::OPAtomIndexFooter::Dump(FILE* stream)
1140 {
1141   if ( stream == 0 )
1142     stream = stderr;
1143
1144   Partition::Dump(stream);
1145
1146   std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
1147   for ( ; i != m_PacketList->m_List.end(); i++ )
1148     (*i)->Dump(stream);
1149 }
1150
1151 //
1152 ASDCP::Result_t
1153 ASDCP::MXF::OPAtomIndexFooter::Lookup(ui32_t frame_num, IndexTableSegment::IndexEntry& Entry) const
1154 {
1155   std::list<InterchangeObject*>::iterator li;
1156   for ( li = m_PacketList->m_List.begin(); li != m_PacketList->m_List.end(); li++ )
1157     {
1158       if ( (*li)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1159         {
1160           IndexTableSegment* Segment = (IndexTableSegment*)(*li);
1161           ui64_t start_pos = Segment->IndexStartPosition;
1162
1163           if ( Segment->EditUnitByteCount > 0 )
1164             {
1165               if ( m_PacketList->m_List.size() > 1 )
1166                 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
1167
1168               if ( ! Segment->IndexEntryArray.empty() )
1169                 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
1170
1171               Entry.StreamOffset = (ui64_t)frame_num * Segment->EditUnitByteCount;
1172               return RESULT_OK;
1173             }
1174           else if ( (ui64_t)frame_num >= start_pos
1175                     && (ui64_t)frame_num < (start_pos + Segment->IndexDuration) )
1176             {
1177               ui64_t tmp = frame_num - start_pos;
1178               assert(tmp <= 0xFFFFFFFFL);
1179               Entry = Segment->IndexEntryArray[(ui32_t) tmp];
1180               return RESULT_OK;
1181             }
1182         }
1183     }
1184
1185   return RESULT_FAIL;
1186 }
1187
1188 //
1189 void
1190 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const Rational& Rate)
1191 {
1192   assert(lookup);
1193   m_Lookup = lookup;
1194   m_BytesPerEditUnit = size;
1195   m_EditRate = Rate;
1196
1197   IndexTableSegment* Index = new IndexTableSegment(m_Dict);
1198   AddChildObject(Index);
1199   Index->EditUnitByteCount = m_BytesPerEditUnit;
1200   Index->IndexEditRate = Rate;
1201 }
1202
1203 //
1204 void
1205 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsVBR(IPrimerLookup* lookup, const Rational& Rate, Kumu::fpos_t offset)
1206 {
1207   assert(lookup);
1208   m_Lookup = lookup;
1209   m_BytesPerEditUnit = 0;
1210   m_EditRate = Rate;
1211   m_ECOffset = offset;
1212 }
1213
1214 //
1215 void
1216 ASDCP::MXF::OPAtomIndexFooter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
1217 {
1218   if ( m_BytesPerEditUnit != 0 )  // are we CBR? that's bad 
1219     {
1220       DefaultLogSink().Error("Call to PushIndexEntry() failed: index is CBR\n");
1221       return;
1222     }
1223
1224   // do we have an available segment?
1225   if ( m_CurrentSegment == 0 )
1226     { // no, set up a new segment
1227       m_CurrentSegment = new IndexTableSegment(m_Dict);
1228       assert(m_CurrentSegment);
1229       AddChildObject(m_CurrentSegment);
1230       m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1231       m_CurrentSegment->IndexEditRate = m_EditRate;
1232       m_CurrentSegment->IndexStartPosition = 0;
1233     }
1234   else if ( m_CurrentSegment->IndexEntryArray.size() >= CBRIndexEntriesPerSegment )
1235     { // no, this one is full, start another
1236       m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1237       ui64_t StartPosition = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
1238
1239       m_CurrentSegment = new IndexTableSegment(m_Dict);
1240       assert(m_CurrentSegment);
1241       AddChildObject(m_CurrentSegment);
1242       m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1243       m_CurrentSegment->IndexEditRate = m_EditRate;
1244       m_CurrentSegment->IndexStartPosition = StartPosition;
1245     }
1246
1247   m_CurrentSegment->IndexEntryArray.push_back(Entry);
1248 }
1249
1250 //------------------------------------------------------------------------------------------
1251 //
1252
1253 //
1254 ASDCP::Result_t
1255 ASDCP::MXF::InterchangeObject::InitFromTLVSet(TLVReader& TLVSet)
1256 {
1257   Result_t result = TLVSet.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID));
1258   if ( ASDCP_SUCCESS(result) )
1259     result = TLVSet.ReadObject(OBJ_READ_ARGS(GenerationInterchangeObject, GenerationUID));
1260   return result;
1261 }
1262
1263 //
1264 ASDCP::Result_t
1265 ASDCP::MXF::InterchangeObject::WriteToTLVSet(TLVWriter& TLVSet)
1266 {
1267   Result_t result = TLVSet.WriteObject(OBJ_WRITE_ARGS(InterchangeObject, InstanceUID));
1268   if ( ASDCP_SUCCESS(result) )
1269     result = TLVSet.WriteObject(OBJ_WRITE_ARGS(GenerationInterchangeObject, GenerationUID));
1270   return result;
1271 }
1272
1273 //
1274 ASDCP::Result_t
1275 ASDCP::MXF::InterchangeObject::InitFromBuffer(const byte_t* p, ui32_t l)
1276 {
1277   ASDCP_TEST_NULL(p);
1278   Result_t result = RESULT_FALSE;
1279
1280   if ( m_Typeinfo == 0 )
1281     {
1282       result = KLVPacket::InitFromBuffer(p, l);
1283     }
1284   else
1285     {
1286       result = KLVPacket::InitFromBuffer(p, l, m_Typeinfo->ul);
1287
1288       if ( ASDCP_SUCCESS(result) )
1289         {
1290           TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup);
1291           result = InitFromTLVSet(MemRDR);
1292         }
1293     }
1294   
1295   return result;
1296 }
1297
1298 //
1299 ASDCP::Result_t
1300 ASDCP::MXF::InterchangeObject::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
1301 {
1302   if ( m_Typeinfo == 0 )
1303     return RESULT_STATE;
1304
1305   TLVWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length, m_Lookup);
1306   Result_t result = WriteToTLVSet(MemWRT);
1307
1308   if ( ASDCP_SUCCESS(result) )
1309     {
1310       ui32_t packet_length = MemWRT.Length();
1311       result = WriteKLToBuffer(Buffer, m_Typeinfo->ul, packet_length);
1312
1313       if ( ASDCP_SUCCESS(result) )
1314         Buffer.Size(Buffer.Size() + packet_length);
1315     }
1316
1317   return result;
1318 }
1319
1320 //
1321 void
1322 ASDCP::MXF::InterchangeObject::Dump(FILE* stream)
1323 {
1324   char identbuf[IdentBufferLen];
1325
1326   fputc('\n', stream);
1327   KLVPacket::Dump(stream, *m_Dict, false);
1328   fprintf(stream, "             InstanceUID = %s\n",  InstanceUID.EncodeHex(identbuf, IdentBufferLen));
1329   fprintf(stream, "           GenerationUID = %s\n",  GenerationUID.EncodeHex(identbuf, IdentBufferLen));
1330 }
1331
1332 //
1333 bool
1334 ASDCP::MXF::InterchangeObject::IsA(const byte_t* label)
1335 {
1336   if ( m_KLLength == 0 )
1337     return false;
1338
1339   return ( memcmp(label, m_KeyStart, SMPTE_UL_LENGTH) == 0 );
1340 }
1341
1342
1343 //------------------------------------------------------------------------------------------
1344
1345
1346 typedef std::map<ASDCP::UL, ASDCP::MXF::MXFObjectFactory_t>FactoryMap_t;
1347 typedef FactoryMap_t::iterator FLi_t;
1348
1349 //
1350 class FactoryList : public FactoryMap_t
1351 {
1352   Kumu::Mutex m_Lock;
1353
1354 public:
1355   FactoryList() {}
1356   ~FactoryList() {}
1357
1358   bool Empty() {
1359     Kumu::AutoMutex BlockLock(m_Lock);
1360     return empty();
1361   }
1362
1363   FLi_t Find(const byte_t* label) {
1364     Kumu::AutoMutex BlockLock(m_Lock);
1365     return find(label);
1366   }
1367
1368   FLi_t End() {
1369     Kumu::AutoMutex BlockLock(m_Lock);
1370     return end();
1371   }
1372
1373   void Insert(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory) {
1374     Kumu::AutoMutex BlockLock(m_Lock);
1375     insert(FactoryList::value_type(label, factory));
1376   }
1377 };
1378
1379 //
1380 static FactoryList s_FactoryList;
1381 static Kumu::Mutex s_InitLock;
1382 static bool        s_TypesInit = false;
1383
1384
1385 //
1386 void
1387 ASDCP::MXF::SetObjectFactory(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory)
1388 {
1389   s_FactoryList.Insert(label, factory);
1390 }
1391
1392 //
1393 ASDCP::MXF::InterchangeObject*
1394 ASDCP::MXF::CreateObject(const Dictionary*& Dict, const UL& label)
1395 {
1396   if ( ! s_TypesInit )
1397     {
1398       Kumu::AutoMutex BlockLock(s_InitLock);
1399
1400       if ( ! s_TypesInit )
1401         {
1402           MXF::Metadata_InitTypes(Dict);
1403           s_TypesInit = true;
1404         }
1405     }
1406
1407   FLi_t i = s_FactoryList.find(label.Value());
1408
1409   if ( i == s_FactoryList.end() )
1410     return new InterchangeObject(Dict);
1411
1412   return i->second(Dict);
1413 }
1414
1415
1416 //
1417 // end MXF.cpp
1418 //