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