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