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