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