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