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