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