oi
[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  || OperationalPattern == InteropOPAtomUL ) )
709     {
710       char strbuf[IdentBufferLen];
711       const MDDEntry* Entry = m_Dict.FindUL(OperationalPattern.Value());
712       if ( Entry == 0 )
713         DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n", OperationalPattern.EncodeString(strbuf, IdentBufferLen));
714       else
715         DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n", Entry->name);
716     }
717
718   // slurp up the remainder of the header
719   if ( HeaderByteCount < 1024 )
720     DefaultLogSink().Warn("Improbably small HeaderByteCount value: %u\n", HeaderByteCount);
721
722   assert (HeaderByteCount <= 0xFFFFFFFFL);
723   result = m_Buffer.Capacity((ui32_t) HeaderByteCount);
724
725   if ( ASDCP_SUCCESS(result) )
726     {
727       ui32_t read_count;
728       result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
729
730       if ( ASDCP_FAILURE(result) )
731         return result;
732
733       if ( read_count != m_Buffer.Capacity() )
734         {
735           DefaultLogSink().Error("Short read of OP-Atom header metadata; wanted %u, got %u\n",
736                                  m_Buffer.Capacity(), read_count);
737           return RESULT_KLV_CODING;
738         }
739     }
740
741   if ( ASDCP_SUCCESS(result) )
742     result = InitFromBuffer(m_Buffer.RoData(), m_Buffer.Capacity());
743
744   return result;
745 }
746
747 //
748 ASDCP::Result_t
749 ASDCP::MXF::OPAtomHeader::InitFromPartitionBuffer(const byte_t* p, ui32_t l)
750 {
751   Result_t result = KLVPacket::InitFromBuffer(p, l);
752
753   if ( ASDCP_SUCCESS(result) )
754     result = Partition::InitFromBuffer(m_ValueStart, m_ValueLength); // test UL and OP
755
756   if ( ASDCP_SUCCESS(result) )
757     {
758       ui32_t pp_len = KLVPacket::PacketLength();
759       result = InitFromBuffer(p + pp_len, l - pp_len);
760     }
761
762   return result;
763 }
764
765 //
766 ASDCP::Result_t
767 ASDCP::MXF::OPAtomHeader::InitFromBuffer(const byte_t* p, ui32_t l)
768 {
769   Result_t result = RESULT_OK;
770   const byte_t* end_p = p + l;
771
772   while ( ASDCP_SUCCESS(result) && p < end_p )
773     {
774       // parse the packets and index them by uid, discard KLVFill items
775       InterchangeObject* object = CreateObject(m_Dict, p);
776       assert(object);
777
778       object->m_Lookup = &m_Primer;
779       result = object->InitFromBuffer(p, end_p - p);
780       const byte_t* redo_p = p;
781       p += object->PacketLength();
782       //      hexdump(p, object->PacketLength());
783
784       if ( ASDCP_SUCCESS(result) )
785         {
786           if ( object->IsA(m_Dict.ul(MDD_KLVFill)) )
787             {
788               delete object;
789             }
790           else if ( object->IsA(m_Dict.ul(MDD_Primer)) )
791             {
792               delete object;
793               result = m_Primer.InitFromBuffer(redo_p, end_p - redo_p);
794             }
795           else
796             {
797               m_PacketList->AddPacket(object);
798
799               if ( object->IsA(m_Dict.ul(MDD_Preface)) && m_Preface == 0 )
800                 m_Preface = (Preface*)object;
801             }
802         }
803       else
804         {
805           DefaultLogSink().Error("Error initializing packet\n");
806           delete object;
807         }
808     }
809
810   return result;
811 }
812
813 ASDCP::Result_t
814 ASDCP::MXF::OPAtomHeader::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
815 {
816   return m_PacketList->GetMDObjectByID(ObjectID, Object);
817 }
818
819 //
820 ASDCP::Result_t
821 ASDCP::MXF::OPAtomHeader::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
822 {
823   InterchangeObject* TmpObject;
824
825   if ( Object == 0 )
826     Object = &TmpObject;
827
828   return m_PacketList->GetMDObjectByType(ObjectID, Object);
829 }
830
831 //
832 ASDCP::Result_t
833 ASDCP::MXF::OPAtomHeader::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
834 {
835   return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
836 }
837
838 //
839 ASDCP::MXF::Identification*
840 ASDCP::MXF::OPAtomHeader::GetIdentification()
841 {
842   InterchangeObject* Object;
843
844   if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object)) )
845     return (Identification*)Object;
846
847   return 0;
848 }
849
850 //
851 ASDCP::MXF::SourcePackage*
852 ASDCP::MXF::OPAtomHeader::GetSourcePackage()
853 {
854   InterchangeObject* Object;
855
856   if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object)) )
857     return (SourcePackage*)Object;
858
859   return 0;
860 }
861
862
863 //
864 ASDCP::Result_t
865 ASDCP::MXF::OPAtomHeader::WriteToFile(Kumu::FileWriter& Writer, ui32_t HeaderSize)
866 {
867   if ( m_Preface == 0 )
868     return RESULT_STATE;
869
870   if ( HeaderSize < 4096 ) 
871     {
872       DefaultLogSink().Error("HeaderSize %u is too small. Must be >= 4096\n", HeaderSize);
873       return RESULT_FAIL;
874     }
875
876   ASDCP::FrameBuffer HeaderBuffer;
877   HeaderByteCount = HeaderSize - ArchiveSize();
878   assert (HeaderByteCount <= 0xFFFFFFFFL);
879   Result_t result = HeaderBuffer.Capacity((ui32_t) HeaderByteCount); 
880   m_Preface->m_Lookup = &m_Primer;
881
882   std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
883   for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
884     {
885       InterchangeObject* object = *pl_i;
886       object->m_Lookup = &m_Primer;
887
888       ASDCP::FrameBuffer WriteWrapper;
889       WriteWrapper.SetData(HeaderBuffer.Data() + HeaderBuffer.Size(),
890                            HeaderBuffer.Capacity() - HeaderBuffer.Size());
891       result = object->WriteToBuffer(WriteWrapper);
892       HeaderBuffer.Size(HeaderBuffer.Size() + WriteWrapper.Size());
893     }
894
895   if ( ASDCP_SUCCESS(result) )
896     {
897       UL TmpUL(m_Dict.ul(MDD_ClosedCompleteHeader));
898       result = Partition::WriteToFile(Writer, TmpUL);
899     }
900
901   if ( ASDCP_SUCCESS(result) )
902     result = m_Primer.WriteToFile(Writer);
903
904   if ( ASDCP_SUCCESS(result) )
905     {
906       ui32_t write_count;
907       Writer.Write(HeaderBuffer.RoData(), HeaderBuffer.Size(), &write_count);
908       assert(write_count == HeaderBuffer.Size());
909     }
910
911   // KLV Fill
912   if ( ASDCP_SUCCESS(result) )
913     {
914       Kumu::fpos_t pos = Writer.Tell();
915
916       if ( pos > (Kumu::fpos_t)HeaderByteCount )
917         {
918           char intbuf[IntBufferLen];
919           DefaultLogSink().Error("Header size %s exceeds specified value %u\n",
920                                  ui64sz(pos, intbuf),
921                                  HeaderSize);
922           return RESULT_FAIL;
923         }
924
925       ASDCP::FrameBuffer NilBuf;
926       ui32_t klv_fill_length = HeaderSize - (ui32_t)pos;
927
928       if ( klv_fill_length < kl_length )
929         {
930           DefaultLogSink().Error("Remaining region too small for KLV Fill header\n");
931           return RESULT_FAIL;
932         }
933
934       klv_fill_length -= kl_length;
935       result = WriteKLToFile(Writer, m_Dict.ul(MDD_KLVFill), klv_fill_length);
936
937       if ( ASDCP_SUCCESS(result) )
938         result = NilBuf.Capacity(klv_fill_length);
939
940       if ( ASDCP_SUCCESS(result) )
941         {
942           memset(NilBuf.Data(), 0, klv_fill_length);
943           ui32_t write_count;
944           Writer.Write(NilBuf.RoData(), klv_fill_length, &write_count);
945           assert(write_count == klv_fill_length);
946         }
947     }
948
949   return result;
950 }
951
952 //
953 void
954 ASDCP::MXF::OPAtomHeader::Dump(FILE* stream)
955 {
956   if ( stream == 0 )
957     stream = stderr;
958
959   Partition::Dump(stream);
960   m_Primer.Dump(stream);
961
962   if ( m_Preface == 0 )
963     fputs("No Preface loaded\n", stream);
964
965   std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
966   for ( ; i != m_PacketList->m_List.end(); i++ )
967     (*i)->Dump(stream);
968 }
969
970 //------------------------------------------------------------------------------------------
971 //
972
973 ASDCP::MXF::OPAtomIndexFooter::OPAtomIndexFooter(const Dictionary& d) :
974   Partition(d), m_Dict(d),
975   m_CurrentSegment(0), m_BytesPerEditUnit(0), m_BodySID(0),
976   m_ECOffset(0), m_Lookup(0)
977 {
978   BodySID = 0;
979   IndexSID = 129;
980 }
981
982 ASDCP::MXF::OPAtomIndexFooter::~OPAtomIndexFooter() {}
983
984 //
985 ASDCP::Result_t
986 ASDCP::MXF::OPAtomIndexFooter::InitFromFile(const Kumu::FileReader& Reader)
987 {
988   Result_t result = Partition::InitFromFile(Reader); // test UL and OP
989
990   // slurp up the remainder of the footer
991   ui32_t read_count;
992
993   if ( ASDCP_SUCCESS(result) )
994     {
995       assert (IndexByteCount <= 0xFFFFFFFFL);
996       result = m_Buffer.Capacity((ui32_t) IndexByteCount);
997     }
998
999   if ( ASDCP_SUCCESS(result) )
1000     result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
1001
1002   if ( ASDCP_SUCCESS(result) && read_count != m_Buffer.Capacity() )
1003     {
1004       DefaultLogSink().Error("Short read of footer partition: got %u, expecting %u\n",
1005                              read_count, m_Buffer.Capacity());
1006       return RESULT_FAIL;
1007     }
1008
1009   if ( ASDCP_SUCCESS(result) )
1010     result = InitFromBuffer(m_Buffer.RoData(), m_Buffer.Capacity());
1011
1012   return result;
1013 }
1014
1015 //
1016 ASDCP::Result_t
1017 ASDCP::MXF::OPAtomIndexFooter::InitFromPartitionBuffer(const byte_t* p, ui32_t l)
1018 {
1019   Result_t result = KLVPacket::InitFromBuffer(p, l);
1020
1021   if ( ASDCP_SUCCESS(result) )
1022     result = Partition::InitFromBuffer(m_ValueStart, m_ValueLength); // test UL and OP
1023
1024   if ( ASDCP_SUCCESS(result) )
1025     {
1026       ui32_t pp_len = KLVPacket::PacketLength();
1027       result = InitFromBuffer(p + pp_len, l - pp_len);
1028     }
1029
1030   return result;
1031 }
1032
1033 //
1034 ASDCP::Result_t
1035 ASDCP::MXF::OPAtomIndexFooter::InitFromBuffer(const byte_t* p, ui32_t l)
1036 {
1037   Result_t result = RESULT_OK;
1038   const byte_t* end_p = p + l;
1039   
1040   while ( ASDCP_SUCCESS(result) && p < end_p )
1041     {
1042       // parse the packets and index them by uid, discard KLVFill items
1043       InterchangeObject* object = CreateObject(m_Dict, p);
1044       assert(object);
1045
1046       object->m_Lookup = m_Lookup;
1047       result = object->InitFromBuffer(p, end_p - p);
1048       p += object->PacketLength();
1049
1050       if ( ASDCP_SUCCESS(result) )
1051         {
1052           m_PacketList->AddPacket(object);
1053         }
1054       else
1055         {
1056           DefaultLogSink().Error("Error initializing packet\n");
1057           delete object;
1058         }
1059     }
1060
1061   if ( ASDCP_FAILURE(result) )
1062     DefaultLogSink().Error("Failed to initialize OPAtomIndexFooter\n");
1063
1064   return result;
1065 }
1066
1067 //
1068 ASDCP::Result_t
1069 ASDCP::MXF::OPAtomIndexFooter::WriteToFile(Kumu::FileWriter& Writer, ui64_t duration)
1070 {
1071   ASDCP::FrameBuffer FooterBuffer;
1072   ui32_t   footer_size = m_PacketList->m_List.size() * MaxIndexSegmentSize; // segment-count * max-segment-size
1073   Result_t result = FooterBuffer.Capacity(footer_size); 
1074   ui32_t   iseg_count = 0;
1075
1076   if ( m_CurrentSegment != 0 )
1077     {
1078       m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1079       m_CurrentSegment = 0;
1080     }
1081
1082   std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
1083   for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
1084     {
1085       if ( (*pl_i)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1086         {
1087           iseg_count++;
1088           IndexTableSegment* Segment = (IndexTableSegment*)(*pl_i);
1089
1090           if ( m_BytesPerEditUnit != 0 )
1091             {
1092               if ( iseg_count != 1 )
1093                 return RESULT_STATE;
1094
1095               Segment->IndexDuration = duration;
1096             }
1097         }
1098
1099       InterchangeObject* object = *pl_i;
1100       object->m_Lookup = m_Lookup;
1101
1102       ASDCP::FrameBuffer WriteWrapper;
1103       WriteWrapper.SetData(FooterBuffer.Data() + FooterBuffer.Size(),
1104                            FooterBuffer.Capacity() - FooterBuffer.Size());
1105       result = object->WriteToBuffer(WriteWrapper);
1106       FooterBuffer.Size(FooterBuffer.Size() + WriteWrapper.Size());
1107     }
1108
1109   if ( ASDCP_SUCCESS(result) )
1110     {
1111       IndexByteCount = FooterBuffer.Size();
1112       UL FooterUL(m_Dict.ul(MDD_CompleteFooter));
1113       result = Partition::WriteToFile(Writer, FooterUL);
1114     }
1115
1116   if ( ASDCP_SUCCESS(result) )
1117     {
1118       ui32_t write_count = 0;
1119       result = Writer.Write(FooterBuffer.RoData(), FooterBuffer.Size(), &write_count);
1120       assert(write_count == FooterBuffer.Size());
1121     }
1122
1123   return result;
1124 }
1125
1126 //
1127 void
1128 ASDCP::MXF::OPAtomIndexFooter::Dump(FILE* stream)
1129 {
1130   if ( stream == 0 )
1131     stream = stderr;
1132
1133   Partition::Dump(stream);
1134
1135   std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
1136   for ( ; i != m_PacketList->m_List.end(); i++ )
1137     (*i)->Dump(stream);
1138 }
1139
1140 //
1141 ASDCP::Result_t
1142 ASDCP::MXF::OPAtomIndexFooter::Lookup(ui32_t frame_num, IndexTableSegment::IndexEntry& Entry) const
1143 {
1144   std::list<InterchangeObject*>::iterator li;
1145   for ( li = m_PacketList->m_List.begin(); li != m_PacketList->m_List.end(); li++ )
1146     {
1147       if ( (*li)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1148         {
1149           IndexTableSegment* Segment = (IndexTableSegment*)(*li);
1150           ui64_t start_pos = Segment->IndexStartPosition;
1151
1152           if ( Segment->EditUnitByteCount > 0 )
1153             {
1154               if ( m_PacketList->m_List.size() > 1 )
1155                 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
1156
1157               if ( ! Segment->IndexEntryArray.empty() )
1158                 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
1159
1160               Entry.StreamOffset = (ui64_t)frame_num * Segment->EditUnitByteCount;
1161               return RESULT_OK;
1162             }
1163           else if ( (ui64_t)frame_num >= start_pos
1164                     && (ui64_t)frame_num < (start_pos + Segment->IndexDuration) )
1165             {
1166               ui64_t tmp = frame_num - start_pos;
1167               assert(tmp <= 0xFFFFFFFFL);
1168               Entry = Segment->IndexEntryArray[(ui32_t) tmp];
1169               return RESULT_OK;
1170             }
1171         }
1172     }
1173
1174   return RESULT_FAIL;
1175 }
1176
1177 //
1178 void
1179 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const Rational& Rate)
1180 {
1181   assert(lookup);
1182   m_Lookup = lookup;
1183   m_BytesPerEditUnit = size;
1184   m_EditRate = Rate;
1185
1186   IndexTableSegment* Index = new IndexTableSegment(m_Dict);
1187   AddChildObject(Index);
1188   Index->EditUnitByteCount = m_BytesPerEditUnit;
1189   Index->IndexEditRate = Rate;
1190 }
1191
1192 //
1193 void
1194 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsVBR(IPrimerLookup* lookup, const Rational& Rate, Kumu::fpos_t offset)
1195 {
1196   assert(lookup);
1197   m_Lookup = lookup;
1198   m_BytesPerEditUnit = 0;
1199   m_EditRate = Rate;
1200   m_ECOffset = offset;
1201 }
1202
1203 //
1204 void
1205 ASDCP::MXF::OPAtomIndexFooter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
1206 {
1207   if ( m_BytesPerEditUnit != 0 )  // are we CBR? that's bad 
1208     {
1209       DefaultLogSink().Error("Call to PushIndexEntry() failed: index is CBR\n");
1210       return;
1211     }
1212
1213   // do we have an available segment?
1214   if ( m_CurrentSegment == 0 )
1215     { // no, set up a new segment
1216       m_CurrentSegment = new IndexTableSegment(m_Dict);
1217       assert(m_CurrentSegment);
1218       AddChildObject(m_CurrentSegment);
1219       m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1220       m_CurrentSegment->IndexEditRate = m_EditRate;
1221       m_CurrentSegment->IndexStartPosition = 0;
1222     }
1223   else if ( m_CurrentSegment->IndexEntryArray.size() >= CBRIndexEntriesPerSegment )
1224     { // no, this one is full, start another
1225       m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1226       ui64_t StartPosition = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
1227
1228       m_CurrentSegment = new IndexTableSegment(m_Dict);
1229       assert(m_CurrentSegment);
1230       AddChildObject(m_CurrentSegment);
1231       m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1232       m_CurrentSegment->IndexEditRate = m_EditRate;
1233       m_CurrentSegment->IndexStartPosition = StartPosition;
1234     }
1235
1236   m_CurrentSegment->IndexEntryArray.push_back(Entry);
1237 }
1238
1239 //------------------------------------------------------------------------------------------
1240 //
1241
1242 //
1243 ASDCP::Result_t
1244 ASDCP::MXF::InterchangeObject::InitFromTLVSet(TLVReader& TLVSet)
1245 {
1246   Result_t result = TLVSet.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID));
1247   if ( ASDCP_SUCCESS(result) )
1248     result = TLVSet.ReadObject(OBJ_READ_ARGS(GenerationInterchangeObject, GenerationUID));
1249   return result;
1250 }
1251
1252 //
1253 ASDCP::Result_t
1254 ASDCP::MXF::InterchangeObject::WriteToTLVSet(TLVWriter& TLVSet)
1255 {
1256   Result_t result = TLVSet.WriteObject(OBJ_WRITE_ARGS(InterchangeObject, InstanceUID));
1257   if ( ASDCP_SUCCESS(result) )
1258     result = TLVSet.WriteObject(OBJ_WRITE_ARGS(GenerationInterchangeObject, GenerationUID));
1259   return result;
1260 }
1261
1262 //
1263 ASDCP::Result_t
1264 ASDCP::MXF::InterchangeObject::InitFromBuffer(const byte_t* p, ui32_t l)
1265 {
1266   ASDCP_TEST_NULL(p);
1267   Result_t result = RESULT_FALSE;
1268
1269   if ( m_Typeinfo == 0 )
1270     {
1271       result = KLVPacket::InitFromBuffer(p, l);
1272     }
1273   else
1274     {
1275       result = KLVPacket::InitFromBuffer(p, l, m_Typeinfo->ul);
1276
1277       if ( ASDCP_SUCCESS(result) )
1278         {
1279           TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup);
1280           result = InitFromTLVSet(MemRDR);
1281         }
1282     }
1283   
1284   return result;
1285 }
1286
1287 //
1288 ASDCP::Result_t
1289 ASDCP::MXF::InterchangeObject::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
1290 {
1291   if ( m_Typeinfo == 0 )
1292     return RESULT_STATE;
1293
1294   TLVWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length, m_Lookup);
1295   Result_t result = WriteToTLVSet(MemWRT);
1296
1297   if ( ASDCP_SUCCESS(result) )
1298     {
1299       ui32_t packet_length = MemWRT.Length();
1300       result = WriteKLToBuffer(Buffer, m_Typeinfo->ul, packet_length);
1301
1302       if ( ASDCP_SUCCESS(result) )
1303         Buffer.Size(Buffer.Size() + packet_length);
1304     }
1305
1306   return result;
1307 }
1308
1309 //
1310 void
1311 ASDCP::MXF::InterchangeObject::Dump(FILE* stream)
1312 {
1313   char identbuf[IdentBufferLen];
1314
1315   fputc('\n', stream);
1316   KLVPacket::Dump(stream, m_Dict, false);
1317   fprintf(stream, "             InstanceUID = %s\n",  InstanceUID.EncodeHex(identbuf, IdentBufferLen));
1318   fprintf(stream, "           GenerationUID = %s\n",  GenerationUID.EncodeHex(identbuf, IdentBufferLen));
1319 }
1320
1321 //
1322 bool
1323 ASDCP::MXF::InterchangeObject::IsA(const byte_t* label)
1324 {
1325   if ( m_KLLength == 0 )
1326     return false;
1327
1328   return ( memcmp(label, m_KeyStart, SMPTE_UL_LENGTH) == 0 );
1329 }
1330
1331
1332 //------------------------------------------------------------------------------------------
1333
1334
1335 typedef std::map<ASDCP::UL, ASDCP::MXF::MXFObjectFactory_t>FactoryMap_t;
1336 typedef FactoryMap_t::iterator FLi_t;
1337
1338 //
1339 class FactoryList : public FactoryMap_t
1340 {
1341   Kumu::Mutex m_Lock;
1342
1343 public:
1344   FactoryList() {}
1345   ~FactoryList() {}
1346
1347   bool Empty() {
1348     Kumu::AutoMutex BlockLock(m_Lock);
1349     return empty();
1350   }
1351
1352   FLi_t Find(const byte_t* label) {
1353     Kumu::AutoMutex BlockLock(m_Lock);
1354     return find(label);
1355   }
1356
1357   FLi_t End() {
1358     Kumu::AutoMutex BlockLock(m_Lock);
1359     return end();
1360   }
1361
1362   void Insert(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory) {
1363     Kumu::AutoMutex BlockLock(m_Lock);
1364     insert(FactoryList::value_type(label, factory));
1365   }
1366 };
1367
1368 //
1369 static FactoryList s_FactoryList;
1370 static Kumu::Mutex s_InitLock;
1371 static bool        s_TypesInit = false;
1372
1373
1374 //
1375 void
1376 ASDCP::MXF::SetObjectFactory(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory)
1377 {
1378   s_FactoryList.Insert(label, factory);
1379 }
1380
1381 //
1382 ASDCP::MXF::InterchangeObject*
1383 ASDCP::MXF::CreateObject(const Dictionary& Dict, const UL& label)
1384 {
1385   if ( ! s_TypesInit )
1386     {
1387       Kumu::AutoMutex BlockLock(s_InitLock);
1388
1389       if ( ! s_TypesInit )
1390         {
1391           MXF::Metadata_InitTypes(Dict);
1392           s_TypesInit = true;
1393         }
1394     }
1395
1396   FLi_t i = s_FactoryList.find(label.Value());
1397
1398   if ( i == s_FactoryList.end() )
1399     return new InterchangeObject(Dict);
1400
1401   return i->second(Dict);
1402 }
1403
1404
1405 //
1406 // end MXF.cpp
1407 //