bugfixes for release
[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
713             {
714               m_PacketList->AddPacket(object);
715
716               if ( object->IsA(Dict::ul(MDD_Preface)) )
717                 {
718                   assert(m_Preface == 0);
719                   m_Preface = (Preface*)object;
720                 }
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 //