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