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