version revv
[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 ASDCP::MXF::RIP&
907 ASDCP::MXF::OPAtomHeader::GetRIP() { return m_RIP; }
908
909 //
910 ASDCP::Result_t
911 ASDCP::MXF::OPAtomHeader::WriteToFile(Kumu::FileWriter& Writer, ui32_t HeaderSize)
912 {
913   assert(m_Dict);
914   if ( m_Preface == 0 )
915     return RESULT_STATE;
916
917   if ( HeaderSize < 4096 ) 
918     {
919       DefaultLogSink().Error("HeaderSize %u is too small. Must be >= 4096\n", HeaderSize);
920       return RESULT_FAIL;
921     }
922
923   ASDCP::FrameBuffer HeaderBuffer;
924   HeaderByteCount = HeaderSize - ArchiveSize();
925   assert (HeaderByteCount <= 0xFFFFFFFFL);
926   Result_t result = HeaderBuffer.Capacity((ui32_t) HeaderByteCount); 
927   m_Preface->m_Lookup = &m_Primer;
928
929   std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
930   for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
931     {
932       InterchangeObject* object = *pl_i;
933       object->m_Lookup = &m_Primer;
934
935       ASDCP::FrameBuffer WriteWrapper;
936       WriteWrapper.SetData(HeaderBuffer.Data() + HeaderBuffer.Size(),
937                            HeaderBuffer.Capacity() - HeaderBuffer.Size());
938       result = object->WriteToBuffer(WriteWrapper);
939       HeaderBuffer.Size(HeaderBuffer.Size() + WriteWrapper.Size());
940     }
941
942   if ( ASDCP_SUCCESS(result) )
943     {
944       UL TmpUL(m_Dict->ul(MDD_ClosedCompleteHeader));
945       result = Partition::WriteToFile(Writer, TmpUL);
946     }
947
948   if ( ASDCP_SUCCESS(result) )
949     result = m_Primer.WriteToFile(Writer);
950
951   if ( ASDCP_SUCCESS(result) )
952     {
953       ui32_t write_count;
954       Writer.Write(HeaderBuffer.RoData(), HeaderBuffer.Size(), &write_count);
955       assert(write_count == HeaderBuffer.Size());
956     }
957
958   // KLV Fill
959   if ( ASDCP_SUCCESS(result) )
960     {
961       Kumu::fpos_t pos = Writer.Tell();
962
963       if ( pos > (Kumu::fpos_t)HeaderByteCount )
964         {
965           char intbuf[IntBufferLen];
966           DefaultLogSink().Error("Header size %s exceeds specified value %u\n",
967                                  ui64sz(pos, intbuf),
968                                  HeaderSize);
969           return RESULT_FAIL;
970         }
971
972       ASDCP::FrameBuffer NilBuf;
973       ui32_t klv_fill_length = HeaderSize - (ui32_t)pos;
974
975       if ( klv_fill_length < kl_length )
976         {
977           DefaultLogSink().Error("Remaining region too small for KLV Fill header\n");
978           return RESULT_FAIL;
979         }
980
981       klv_fill_length -= kl_length;
982       result = WriteKLToFile(Writer, m_Dict->ul(MDD_KLVFill), klv_fill_length);
983
984       if ( ASDCP_SUCCESS(result) )
985         result = NilBuf.Capacity(klv_fill_length);
986
987       if ( ASDCP_SUCCESS(result) )
988         {
989           memset(NilBuf.Data(), 0, klv_fill_length);
990           ui32_t write_count;
991           Writer.Write(NilBuf.RoData(), klv_fill_length, &write_count);
992           assert(write_count == klv_fill_length);
993         }
994     }
995
996   return result;
997 }
998
999 //
1000 void
1001 ASDCP::MXF::OPAtomHeader::Dump(FILE* stream)
1002 {
1003   if ( stream == 0 )
1004     stream = stderr;
1005
1006   Partition::Dump(stream);
1007   m_Primer.Dump(stream);
1008
1009   if ( m_Preface == 0 )
1010     fputs("No Preface loaded\n", stream);
1011
1012   std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
1013   for ( ; i != m_PacketList->m_List.end(); i++ )
1014     (*i)->Dump(stream);
1015 }
1016
1017 //------------------------------------------------------------------------------------------
1018 //
1019
1020 ASDCP::MXF::OPAtomIndexFooter::OPAtomIndexFooter(const Dictionary*& d) :
1021   Partition(d), m_Dict(d),
1022   m_CurrentSegment(0), m_BytesPerEditUnit(0), m_BodySID(0),
1023   m_ECOffset(0), m_Lookup(0)
1024 {
1025   BodySID = 0;
1026   IndexSID = 129;
1027 }
1028
1029 ASDCP::MXF::OPAtomIndexFooter::~OPAtomIndexFooter() {}
1030
1031 //
1032 ASDCP::Result_t
1033 ASDCP::MXF::OPAtomIndexFooter::InitFromFile(const Kumu::FileReader& Reader)
1034 {
1035   Result_t result = Partition::InitFromFile(Reader); // test UL and OP
1036
1037   // slurp up the remainder of the footer
1038   ui32_t read_count;
1039
1040   if ( ASDCP_SUCCESS(result) )
1041     {
1042       assert (IndexByteCount <= 0xFFFFFFFFL);
1043       result = m_Buffer.Capacity((ui32_t) IndexByteCount);
1044     }
1045
1046   if ( ASDCP_SUCCESS(result) )
1047     result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
1048
1049   if ( ASDCP_SUCCESS(result) && read_count != m_Buffer.Capacity() )
1050     {
1051       DefaultLogSink().Error("Short read of footer partition: got %u, expecting %u\n",
1052                              read_count, m_Buffer.Capacity());
1053       return RESULT_FAIL;
1054     }
1055
1056   if ( ASDCP_SUCCESS(result) )
1057     result = InitFromBuffer(m_Buffer.RoData(), m_Buffer.Capacity());
1058
1059   return result;
1060 }
1061
1062 //
1063 ASDCP::Result_t
1064 ASDCP::MXF::OPAtomIndexFooter::InitFromPartitionBuffer(const byte_t* p, ui32_t l)
1065 {
1066   Result_t result = KLVPacket::InitFromBuffer(p, l);
1067
1068   if ( ASDCP_SUCCESS(result) )
1069     result = Partition::InitFromBuffer(m_ValueStart, m_ValueLength); // test UL and OP
1070
1071   if ( ASDCP_SUCCESS(result) )
1072     {
1073       ui32_t pp_len = KLVPacket::PacketLength();
1074       result = InitFromBuffer(p + pp_len, l - pp_len);
1075     }
1076
1077   return result;
1078 }
1079
1080 //
1081 ASDCP::Result_t
1082 ASDCP::MXF::OPAtomIndexFooter::InitFromBuffer(const byte_t* p, ui32_t l)
1083 {
1084   Result_t result = RESULT_OK;
1085   const byte_t* end_p = p + l;
1086   
1087   while ( ASDCP_SUCCESS(result) && p < end_p )
1088     {
1089       // parse the packets and index them by uid, discard KLVFill items
1090       InterchangeObject* object = CreateObject(m_Dict, p);
1091       assert(object);
1092
1093       object->m_Lookup = m_Lookup;
1094       result = object->InitFromBuffer(p, end_p - p);
1095       p += object->PacketLength();
1096
1097       if ( ASDCP_SUCCESS(result) )
1098         {
1099           m_PacketList->AddPacket(object); // takes ownership
1100         }
1101       else
1102         {
1103           DefaultLogSink().Error("Error initializing packet\n");
1104           delete object;
1105         }
1106     }
1107
1108   if ( ASDCP_FAILURE(result) )
1109     DefaultLogSink().Error("Failed to initialize OPAtomIndexFooter\n");
1110
1111   return result;
1112 }
1113
1114 //
1115 ASDCP::Result_t
1116 ASDCP::MXF::OPAtomIndexFooter::WriteToFile(Kumu::FileWriter& Writer, ui64_t duration)
1117 {
1118   assert(m_Dict);
1119   ASDCP::FrameBuffer FooterBuffer;
1120   ui32_t   footer_size = m_PacketList->m_List.size() * MaxIndexSegmentSize; // segment-count * max-segment-size
1121   Result_t result = FooterBuffer.Capacity(footer_size); 
1122   ui32_t   iseg_count = 0;
1123
1124   if ( m_CurrentSegment != 0 )
1125     {
1126       m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1127       m_CurrentSegment = 0;
1128     }
1129
1130   std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
1131   for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
1132     {
1133       if ( (*pl_i)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1134         {
1135           iseg_count++;
1136           IndexTableSegment* Segment = (IndexTableSegment*)(*pl_i);
1137
1138           if ( m_BytesPerEditUnit != 0 )
1139             {
1140               if ( iseg_count != 1 )
1141                 return RESULT_STATE;
1142
1143               Segment->IndexDuration = duration;
1144             }
1145         }
1146
1147       InterchangeObject* object = *pl_i;
1148       object->m_Lookup = m_Lookup;
1149
1150       ASDCP::FrameBuffer WriteWrapper;
1151       WriteWrapper.SetData(FooterBuffer.Data() + FooterBuffer.Size(),
1152                            FooterBuffer.Capacity() - FooterBuffer.Size());
1153       result = object->WriteToBuffer(WriteWrapper);
1154       FooterBuffer.Size(FooterBuffer.Size() + WriteWrapper.Size());
1155     }
1156
1157   if ( ASDCP_SUCCESS(result) )
1158     {
1159       IndexByteCount = FooterBuffer.Size();
1160       UL FooterUL(m_Dict->ul(MDD_CompleteFooter));
1161       result = Partition::WriteToFile(Writer, FooterUL);
1162     }
1163
1164   if ( ASDCP_SUCCESS(result) )
1165     {
1166       ui32_t write_count = 0;
1167       result = Writer.Write(FooterBuffer.RoData(), FooterBuffer.Size(), &write_count);
1168       assert(write_count == FooterBuffer.Size());
1169     }
1170
1171   return result;
1172 }
1173
1174 //
1175 void
1176 ASDCP::MXF::OPAtomIndexFooter::Dump(FILE* stream)
1177 {
1178   if ( stream == 0 )
1179     stream = stderr;
1180
1181   Partition::Dump(stream);
1182
1183   std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
1184   for ( ; i != m_PacketList->m_List.end(); i++ )
1185     (*i)->Dump(stream);
1186 }
1187
1188 ASDCP::Result_t
1189 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
1190 {
1191   return m_PacketList->GetMDObjectByID(ObjectID, Object);
1192 }
1193
1194 //
1195 ASDCP::Result_t
1196 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
1197 {
1198   InterchangeObject* TmpObject;
1199
1200   if ( Object == 0 )
1201     Object = &TmpObject;
1202
1203   return m_PacketList->GetMDObjectByType(ObjectID, Object);
1204 }
1205
1206 //
1207 ASDCP::Result_t
1208 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
1209 {
1210   return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
1211 }
1212
1213 //
1214 ASDCP::Result_t
1215 ASDCP::MXF::OPAtomIndexFooter::Lookup(ui32_t frame_num, IndexTableSegment::IndexEntry& Entry) const
1216 {
1217   std::list<InterchangeObject*>::iterator li;
1218   for ( li = m_PacketList->m_List.begin(); li != m_PacketList->m_List.end(); li++ )
1219     {
1220       if ( (*li)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1221         {
1222           IndexTableSegment* Segment = (IndexTableSegment*)(*li);
1223           ui64_t start_pos = Segment->IndexStartPosition;
1224
1225           if ( Segment->EditUnitByteCount > 0 )
1226             {
1227               if ( m_PacketList->m_List.size() > 1 )
1228                 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
1229
1230               if ( ! Segment->IndexEntryArray.empty() )
1231                 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
1232
1233               Entry.StreamOffset = (ui64_t)frame_num * Segment->EditUnitByteCount;
1234               return RESULT_OK;
1235             }
1236           else if ( (ui64_t)frame_num >= start_pos
1237                     && (ui64_t)frame_num < (start_pos + Segment->IndexDuration) )
1238             {
1239               ui64_t tmp = frame_num - start_pos;
1240               assert(tmp <= 0xFFFFFFFFL);
1241               Entry = Segment->IndexEntryArray[(ui32_t) tmp];
1242               return RESULT_OK;
1243             }
1244         }
1245     }
1246
1247   return RESULT_FAIL;
1248 }
1249
1250 //
1251 void
1252 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const Rational& Rate)
1253 {
1254   assert(lookup);
1255   m_Lookup = lookup;
1256   m_BytesPerEditUnit = size;
1257   m_EditRate = Rate;
1258
1259   IndexTableSegment* Index = new IndexTableSegment(m_Dict);
1260   AddChildObject(Index);
1261   Index->EditUnitByteCount = m_BytesPerEditUnit;
1262   Index->IndexEditRate = Rate;
1263 }
1264
1265 //
1266 void
1267 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsVBR(IPrimerLookup* lookup, const Rational& Rate, Kumu::fpos_t offset)
1268 {
1269   assert(lookup);
1270   m_Lookup = lookup;
1271   m_BytesPerEditUnit = 0;
1272   m_EditRate = Rate;
1273   m_ECOffset = offset;
1274 }
1275
1276 //
1277 void
1278 ASDCP::MXF::OPAtomIndexFooter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
1279 {
1280   if ( m_BytesPerEditUnit != 0 )  // are we CBR? that's bad 
1281     {
1282       DefaultLogSink().Error("Call to PushIndexEntry() failed: index is CBR\n");
1283       return;
1284     }
1285
1286   // do we have an available segment?
1287   if ( m_CurrentSegment == 0 )
1288     { // no, set up a new segment
1289       m_CurrentSegment = new IndexTableSegment(m_Dict);
1290       assert(m_CurrentSegment);
1291       AddChildObject(m_CurrentSegment);
1292       m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1293       m_CurrentSegment->IndexEditRate = m_EditRate;
1294       m_CurrentSegment->IndexStartPosition = 0;
1295     }
1296   else if ( m_CurrentSegment->IndexEntryArray.size() >= CBRIndexEntriesPerSegment )
1297     { // no, this one is full, start another
1298       m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1299       ui64_t StartPosition = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
1300
1301       m_CurrentSegment = new IndexTableSegment(m_Dict);
1302       assert(m_CurrentSegment);
1303       AddChildObject(m_CurrentSegment);
1304       m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1305       m_CurrentSegment->IndexEditRate = m_EditRate;
1306       m_CurrentSegment->IndexStartPosition = StartPosition;
1307     }
1308
1309   m_CurrentSegment->IndexEntryArray.push_back(Entry);
1310 }
1311
1312 //------------------------------------------------------------------------------------------
1313 //
1314
1315 //
1316 void
1317 ASDCP::MXF::InterchangeObject::Copy(const InterchangeObject& rhs)
1318 {
1319   m_UL = rhs.m_UL;
1320   InstanceUID = rhs.InstanceUID;
1321   GenerationUID = rhs.GenerationUID;
1322 }
1323
1324 //
1325 ASDCP::Result_t
1326 ASDCP::MXF::InterchangeObject::InitFromTLVSet(TLVReader& TLVSet)
1327 {
1328   Result_t result = TLVSet.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID));
1329   if ( ASDCP_SUCCESS(result) )
1330     result = TLVSet.ReadObject(OBJ_READ_ARGS(GenerationInterchangeObject, GenerationUID));
1331   return result;
1332 }
1333
1334 //
1335 ASDCP::Result_t
1336 ASDCP::MXF::InterchangeObject::WriteToTLVSet(TLVWriter& TLVSet)
1337 {
1338   Result_t result = TLVSet.WriteObject(OBJ_WRITE_ARGS(InterchangeObject, InstanceUID));
1339   if ( ASDCP_SUCCESS(result) )
1340     result = TLVSet.WriteObject(OBJ_WRITE_ARGS(GenerationInterchangeObject, GenerationUID));
1341   return result;
1342 }
1343
1344 //
1345 ASDCP::Result_t
1346 ASDCP::MXF::InterchangeObject::InitFromBuffer(const byte_t* p, ui32_t l)
1347 {
1348   ASDCP_TEST_NULL(p);
1349   Result_t result = RESULT_FALSE;
1350
1351   if ( m_UL.HasValue() )
1352     {
1353       result = KLVPacket::InitFromBuffer(p, l, m_UL);
1354
1355       if ( ASDCP_SUCCESS(result) )
1356         {
1357           TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup);
1358           result = InitFromTLVSet(MemRDR);
1359         }
1360     }
1361   else
1362     {
1363       result = KLVPacket::InitFromBuffer(p, l);
1364     }
1365   
1366   return result;
1367 }
1368
1369 //
1370 ASDCP::Result_t
1371 ASDCP::MXF::InterchangeObject::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
1372 {
1373   if ( ! m_UL.HasValue() )
1374     return RESULT_STATE;
1375
1376   TLVWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length, m_Lookup);
1377   Result_t result = WriteToTLVSet(MemWRT);
1378
1379   if ( ASDCP_SUCCESS(result) )
1380     {
1381       ui32_t packet_length = MemWRT.Length();
1382       result = WriteKLToBuffer(Buffer, packet_length);
1383
1384       if ( ASDCP_SUCCESS(result) )
1385         Buffer.Size(Buffer.Size() + packet_length);
1386     }
1387
1388   return result;
1389 }
1390
1391 //
1392 void
1393 ASDCP::MXF::InterchangeObject::Dump(FILE* stream)
1394 {
1395   char identbuf[IdentBufferLen];
1396
1397   fputc('\n', stream);
1398   KLVPacket::Dump(stream, *m_Dict, false);
1399   fprintf(stream, "             InstanceUID = %s\n",  InstanceUID.EncodeHex(identbuf, IdentBufferLen));
1400   fprintf(stream, "           GenerationUID = %s\n",  GenerationUID.EncodeHex(identbuf, IdentBufferLen));
1401 }
1402
1403 //
1404 bool
1405 ASDCP::MXF::InterchangeObject::IsA(const byte_t* label)
1406 {
1407   if ( m_KLLength == 0 )
1408     return false;
1409
1410   return ( memcmp(label, m_KeyStart, SMPTE_UL_LENGTH) == 0 );
1411 }
1412
1413
1414 //------------------------------------------------------------------------------------------
1415
1416
1417 typedef std::map<ASDCP::UL, ASDCP::MXF::MXFObjectFactory_t>FactoryMap_t;
1418 typedef FactoryMap_t::iterator FLi_t;
1419
1420 //
1421 class FactoryList : public FactoryMap_t
1422 {
1423   Kumu::Mutex m_Lock;
1424
1425 public:
1426   FactoryList() {}
1427   ~FactoryList() {}
1428
1429   bool Empty() {
1430     Kumu::AutoMutex BlockLock(m_Lock);
1431     return empty();
1432   }
1433
1434   FLi_t Find(const byte_t* label) {
1435     Kumu::AutoMutex BlockLock(m_Lock);
1436     return find(label);
1437   }
1438
1439   FLi_t End() {
1440     Kumu::AutoMutex BlockLock(m_Lock);
1441     return end();
1442   }
1443
1444   void Insert(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory) {
1445     Kumu::AutoMutex BlockLock(m_Lock);
1446     insert(FactoryList::value_type(label, factory));
1447   }
1448 };
1449
1450 //
1451 static FactoryList s_FactoryList;
1452 static Kumu::Mutex s_InitLock;
1453 static bool        s_TypesInit = false;
1454
1455
1456 //
1457 void
1458 ASDCP::MXF::SetObjectFactory(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory)
1459 {
1460   s_FactoryList.Insert(label, factory);
1461 }
1462
1463 //
1464 ASDCP::MXF::InterchangeObject*
1465 ASDCP::MXF::CreateObject(const Dictionary*& Dict, const UL& label)
1466 {
1467   if ( ! s_TypesInit )
1468     {
1469       Kumu::AutoMutex BlockLock(s_InitLock);
1470
1471       if ( ! s_TypesInit )
1472         {
1473           MXF::Metadata_InitTypes(Dict);
1474           s_TypesInit = true;
1475         }
1476     }
1477
1478   FLi_t i = s_FactoryList.find(label.Value());
1479
1480   if ( i == s_FactoryList.end() )
1481     return new InterchangeObject(Dict);
1482
1483   return i->second(Dict);
1484 }
1485
1486
1487 //
1488 // end MXF.cpp
1489 //