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