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