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