release 1.1.13
[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     }
634
635   if ( ASDCP_SUCCESS(result) )
636     {
637       Array<RIP::Pair>::iterator r_i = m_RIP.PairArray.begin();
638       
639       if ( (*r_i).ByteOffset !=  0 )
640         {
641           DefaultLogSink().Error("First Partition in RIP is not at offset 0.\n");
642           result = RESULT_FORMAT;
643         }
644     }
645
646   if ( ASDCP_SUCCESS(result) )
647     result = Reader.Seek(0);
648
649   if ( ASDCP_SUCCESS(result) )
650     result = Partition::InitFromFile(Reader); // test UL and OP
651
652   // is it really OP-Atom?
653   UL OPAtomUL(Dict::ul(MDD_OPAtom));
654   UL InteropOPAtomUL(Dict::ul(MDD_MXFInterop_OPAtom));
655
656   if ( ! ( OperationalPattern == OPAtomUL  || OperationalPattern == InteropOPAtomUL ) )
657     {
658       char strbuf[IdentBufferLen];
659       const MDDEntry* Entry = Dict::FindUL(OperationalPattern.Value());
660       if ( Entry == 0 )
661         DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n", OperationalPattern.EncodeString(strbuf, IdentBufferLen));
662       else
663         DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n", Entry->name);
664     }
665
666   // slurp up the remainder of the header
667   if ( ASDCP_SUCCESS(result) )
668     {
669       if ( HeaderByteCount < 1024 )
670         DefaultLogSink().Warn("Improbably small HeaderByteCount value: %u\n", HeaderByteCount);
671
672       result = m_Buffer.Capacity(HeaderByteCount);
673     }
674
675   if ( ASDCP_SUCCESS(result) )
676     {
677       ui32_t read_count;
678       result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
679
680       if ( ASDCP_SUCCESS(result) && read_count != m_Buffer.Capacity() )
681         {
682           DefaultLogSink().Error("Short read of OP-Atom header metadata; wanted %u, got %u\n",
683                                  m_Buffer.Capacity(), read_count);
684           return RESULT_FAIL;
685         }
686     }
687
688   const byte_t* p = m_Buffer.RoData();
689   const byte_t* end_p = p + m_Buffer.Capacity();
690
691   while ( ASDCP_SUCCESS(result) && p < end_p )
692     {
693       // parse the packets and index them by uid, discard KLVFill items
694       InterchangeObject* object = CreateObject(p);
695       assert(object);
696
697       object->m_Lookup = &m_Primer;
698       result = object->InitFromBuffer(p, end_p - p);
699       const byte_t* redo_p = p;
700       p += object->PacketLength();
701       //      hexdump(p, object->PacketLength());
702
703       if ( ASDCP_SUCCESS(result) )
704         {
705           if ( object->IsA(Dict::ul(MDD_KLVFill)) )
706             {
707               delete object;
708             }
709           else if ( object->IsA(Dict::ul(MDD_Primer)) )
710             {
711               delete object;
712               result = m_Primer.InitFromBuffer(redo_p, end_p - redo_p);
713             }
714           else if ( object->IsA(Dict::ul(MDD_Preface)) )
715             {
716               assert(m_Preface == 0);
717               m_Preface = (Preface*)object;
718             }
719           else  
720             {
721               m_PacketList->AddPacket(object);
722             }
723         }
724       else
725         {
726           DefaultLogSink().Error("Error initializing packet\n");
727           delete object;
728         }
729     }
730
731   return result;
732 }
733
734 //
735 ASDCP::Result_t
736 ASDCP::MXF::OPAtomHeader::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
737 {
738   InterchangeObject* TmpObject;
739
740   if ( Object == 0 )
741     Object = &TmpObject;
742
743   return m_PacketList->GetMDObjectByType(ObjectID, Object);
744 }
745
746 //
747 ASDCP::MXF::Identification*
748 ASDCP::MXF::OPAtomHeader::GetIdentification()
749 {
750   InterchangeObject* Object;
751
752   if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object)) )
753     return (Identification*)Object;
754
755   return 0;
756 }
757
758 //
759 ASDCP::MXF::SourcePackage*
760 ASDCP::MXF::OPAtomHeader::GetSourcePackage()
761 {
762   InterchangeObject* Object;
763
764   if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object)) )
765     return (SourcePackage*)Object;
766
767   return 0;
768 }
769
770
771 //
772 ASDCP::Result_t
773 ASDCP::MXF::OPAtomHeader::WriteToFile(Kumu::FileWriter& Writer, ui32_t HeaderSize)
774 {
775   if ( m_Preface == 0 )
776     return RESULT_STATE;
777
778   if ( HeaderSize < 4096 ) 
779     {
780       DefaultLogSink().Error("HeaderSize %u is too small. Must be >= 4096\n", HeaderSize);
781       return RESULT_FAIL;
782     }
783
784   ASDCP::FrameBuffer HeaderBuffer;
785   HeaderByteCount = HeaderSize - ArchiveSize();
786   Result_t result = HeaderBuffer.Capacity(HeaderByteCount); 
787   m_Preface->m_Lookup = &m_Primer;
788
789   std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
790   for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
791     {
792       InterchangeObject* object = *pl_i;
793       object->m_Lookup = &m_Primer;
794
795       ASDCP::FrameBuffer WriteWrapper;
796       WriteWrapper.SetData(HeaderBuffer.Data() + HeaderBuffer.Size(),
797                            HeaderBuffer.Capacity() - HeaderBuffer.Size());
798       result = object->WriteToBuffer(WriteWrapper);
799       HeaderBuffer.Size(HeaderBuffer.Size() + WriteWrapper.Size());
800     }
801
802   if ( ASDCP_SUCCESS(result) )
803     {
804       UL TmpUL(Dict::ul(MDD_ClosedCompleteHeader));
805       result = Partition::WriteToFile(Writer, TmpUL);
806     }
807
808   if ( ASDCP_SUCCESS(result) )
809     result = m_Primer.WriteToFile(Writer);
810
811   if ( ASDCP_SUCCESS(result) )
812     {
813       ui32_t write_count;
814       Writer.Write(HeaderBuffer.RoData(), HeaderBuffer.Size(), &write_count);
815       assert(write_count == HeaderBuffer.Size());
816     }
817
818   // KLV Fill
819   if ( ASDCP_SUCCESS(result) )
820     {
821       Kumu::fpos_t pos = Writer.Tell();
822
823       if ( pos > (Kumu::fpos_t)HeaderByteCount )
824         {
825           char intbuf[IntBufferLen];
826           DefaultLogSink().Error("Header size %s exceeds specified value %u\n",
827                                  ui64sz(pos, intbuf),
828                                  HeaderSize);
829           return RESULT_FAIL;
830         }
831
832       ASDCP::FrameBuffer NilBuf;
833       ui32_t klv_fill_length = HeaderSize - (ui32_t)pos;
834
835       if ( klv_fill_length < kl_length )
836         {
837           DefaultLogSink().Error("Remaining region too small for KLV Fill header\n");
838           return RESULT_FAIL;
839         }
840
841       klv_fill_length -= kl_length;
842       result = WriteKLToFile(Writer, Dict::ul(MDD_KLVFill), klv_fill_length);
843
844       if ( ASDCP_SUCCESS(result) )
845         result = NilBuf.Capacity(klv_fill_length);
846
847       if ( ASDCP_SUCCESS(result) )
848         {
849           memset(NilBuf.Data(), 0, klv_fill_length);
850           ui32_t write_count;
851           Writer.Write(NilBuf.RoData(), klv_fill_length, &write_count);
852           assert(write_count == klv_fill_length);
853         }
854     }
855
856   return result;
857 }
858
859 //
860 void
861 ASDCP::MXF::OPAtomHeader::Dump(FILE* stream)
862 {
863   if ( stream == 0 )
864     stream = stderr;
865
866   if ( m_HasRIP )
867     m_RIP.Dump(stream);
868
869   Partition::Dump(stream);
870   m_Primer.Dump(stream);
871
872   if ( m_Preface == 0 )
873     fputs("No Preface loaded\n", stream);
874   else
875     m_Preface->Dump(stream);
876
877   std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
878   for ( ; i != m_PacketList->m_List.end(); i++ )
879     (*i)->Dump(stream);
880 }
881
882 //------------------------------------------------------------------------------------------
883 //
884
885 ASDCP::MXF::OPAtomIndexFooter::OPAtomIndexFooter() :
886   m_CurrentSegment(0), m_BytesPerEditUnit(0), m_BodySID(0),
887   m_ECOffset(0), m_Lookup(0)
888 {
889   BodySID = 0;
890   IndexSID = 129;
891 }
892
893 ASDCP::MXF::OPAtomIndexFooter::~OPAtomIndexFooter() {}
894
895
896 ASDCP::Result_t
897 ASDCP::MXF::OPAtomIndexFooter::InitFromFile(const Kumu::FileReader& Reader)
898 {
899   Result_t result = Partition::InitFromFile(Reader); // test UL and OP
900
901   // slurp up the remainder of the footer
902   ui32_t read_count;
903
904   if ( ASDCP_SUCCESS(result) )
905     result = m_Buffer.Capacity(IndexByteCount);
906
907   if ( ASDCP_SUCCESS(result) )
908     result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
909
910   if ( ASDCP_SUCCESS(result) && read_count != m_Buffer.Capacity() )
911     {
912       DefaultLogSink().Error("Short read of footer partition: got %u, expecting %u\n",
913                              read_count, m_Buffer.Capacity());
914       return RESULT_FAIL;
915     }
916
917   const byte_t* p = m_Buffer.RoData();
918   const byte_t* end_p = p + m_Buffer.Capacity();
919   
920   while ( ASDCP_SUCCESS(result) && p < end_p )
921     {
922       // parse the packets and index them by uid, discard KLVFill items
923       InterchangeObject* object = CreateObject(p);
924       assert(object);
925
926       object->m_Lookup = m_Lookup;
927       result = object->InitFromBuffer(p, end_p - p);
928       p += object->PacketLength();
929
930       if ( ASDCP_SUCCESS(result) )
931         {
932           m_PacketList->AddPacket(object);
933         }
934       else
935         {
936           DefaultLogSink().Error("Error initializing packet\n");
937           delete object;
938         }
939     }
940
941   if ( ASDCP_FAILURE(result) )
942     DefaultLogSink().Error("Failed to initialize OPAtomIndexFooter\n");
943
944   return result;
945 }
946
947 //
948 ASDCP::Result_t
949 ASDCP::MXF::OPAtomIndexFooter::WriteToFile(Kumu::FileWriter& Writer, ui64_t duration)
950 {
951   ASDCP::FrameBuffer FooterBuffer;
952   ui32_t   footer_size = m_PacketList->m_List.size() * MaxIndexSegmentSize; // segment-count * max-segment-size
953   Result_t result = FooterBuffer.Capacity(footer_size); 
954   ui32_t   iseg_count = 0;
955
956   if ( m_CurrentSegment != 0 )
957     {
958       m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
959       m_CurrentSegment = 0;
960     }
961
962   std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
963   for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
964     {
965       if ( (*pl_i)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
966         {
967           iseg_count++;
968           IndexTableSegment* Segment = (IndexTableSegment*)(*pl_i);
969
970           if ( m_BytesPerEditUnit != 0 )
971             {
972               if ( iseg_count != 1 )
973                 return RESULT_STATE;
974
975               Segment->IndexDuration = duration;
976             }
977         }
978
979       InterchangeObject* object = *pl_i;
980       object->m_Lookup = m_Lookup;
981
982       ASDCP::FrameBuffer WriteWrapper;
983       WriteWrapper.SetData(FooterBuffer.Data() + FooterBuffer.Size(),
984                            FooterBuffer.Capacity() - FooterBuffer.Size());
985       result = object->WriteToBuffer(WriteWrapper);
986       FooterBuffer.Size(FooterBuffer.Size() + WriteWrapper.Size());
987     }
988
989   if ( ASDCP_SUCCESS(result) )
990     {
991       IndexByteCount = FooterBuffer.Size();
992       UL FooterUL(Dict::ul(MDD_CompleteFooter));
993       result = Partition::WriteToFile(Writer, FooterUL);
994     }
995
996   if ( ASDCP_SUCCESS(result) )
997     {
998       ui32_t write_count;
999       Writer.Write(FooterBuffer.RoData(), FooterBuffer.Size(), &write_count);
1000       assert(write_count == FooterBuffer.Size());
1001     }
1002
1003   return result;
1004 }
1005
1006 //
1007 void
1008 ASDCP::MXF::OPAtomIndexFooter::Dump(FILE* stream)
1009 {
1010   if ( stream == 0 )
1011     stream = stderr;
1012
1013   Partition::Dump(stream);
1014
1015   std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
1016   for ( ; i != m_PacketList->m_List.end(); i++ )
1017     (*i)->Dump(stream);
1018 }
1019
1020 //
1021 ASDCP::Result_t
1022 ASDCP::MXF::OPAtomIndexFooter::Lookup(ui32_t frame_num, IndexTableSegment::IndexEntry& Entry)
1023 {
1024   std::list<InterchangeObject*>::iterator li;
1025   for ( li = m_PacketList->m_List.begin(); li != m_PacketList->m_List.end(); li++ )
1026     {
1027       if ( (*li)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1028         {
1029           IndexTableSegment* Segment = (IndexTableSegment*)(*li);
1030           ui64_t start_pos = Segment->IndexStartPosition;
1031
1032           if ( Segment->EditUnitByteCount > 0 )
1033             {
1034               if ( m_PacketList->m_List.size() > 1 )
1035                 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
1036
1037               if ( ! Segment->IndexEntryArray.empty() )
1038                 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
1039
1040               Entry.StreamOffset = (ui64_t)frame_num * Segment->EditUnitByteCount;
1041               return RESULT_OK;
1042             }
1043           else if ( (ui64_t)frame_num >= start_pos
1044                     && (ui64_t)frame_num < (start_pos + Segment->IndexDuration) )
1045             {
1046               Entry = Segment->IndexEntryArray[frame_num-start_pos];
1047               return RESULT_OK;
1048             }
1049         }
1050     }
1051
1052   return RESULT_FAIL;
1053 }
1054
1055 //
1056 void
1057 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const Rational& Rate)
1058 {
1059   assert(lookup);
1060   m_Lookup = lookup;
1061   m_BytesPerEditUnit = size;
1062   m_EditRate = Rate;
1063
1064   IndexTableSegment* Index = new IndexTableSegment;
1065   AddChildObject(Index);
1066   Index->EditUnitByteCount = m_BytesPerEditUnit;
1067   Index->IndexEditRate = Rate;
1068 }
1069
1070 //
1071 void
1072 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsVBR(IPrimerLookup* lookup, const Rational& Rate, Kumu::fpos_t offset)
1073 {
1074   assert(lookup);
1075   m_Lookup = lookup;
1076   m_BytesPerEditUnit = 0;
1077   m_EditRate = Rate;
1078   m_ECOffset = offset;
1079 }
1080
1081 //
1082 void
1083 ASDCP::MXF::OPAtomIndexFooter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
1084 {
1085   if ( m_BytesPerEditUnit != 0 )  // are we CBR? that's bad 
1086     {
1087       DefaultLogSink().Error("Call to PushIndexEntry() failed: index is CBR\n");
1088       return;
1089     }
1090
1091   // do we have an available segment?
1092   if ( m_CurrentSegment == 0 )
1093     { // no, set up a new segment
1094       m_CurrentSegment = new IndexTableSegment;
1095       assert(m_CurrentSegment);
1096       AddChildObject(m_CurrentSegment);
1097       m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1098       m_CurrentSegment->IndexEditRate = m_EditRate;
1099       m_CurrentSegment->IndexStartPosition = 0;
1100     }
1101   else if ( m_CurrentSegment->IndexEntryArray.size() >= CBRIndexEntriesPerSegment )
1102     { // no, this one is full, start another
1103       m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1104       ui64_t StartPosition = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
1105
1106       m_CurrentSegment = new IndexTableSegment;
1107       assert(m_CurrentSegment);
1108       AddChildObject(m_CurrentSegment);
1109       m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1110       m_CurrentSegment->IndexEditRate = m_EditRate;
1111       m_CurrentSegment->IndexStartPosition = StartPosition;
1112     }
1113
1114   m_CurrentSegment->IndexEntryArray.push_back(Entry);
1115 }
1116
1117 //------------------------------------------------------------------------------------------
1118 //
1119
1120 //
1121 ASDCP::Result_t
1122 ASDCP::MXF::InterchangeObject::InitFromTLVSet(TLVReader& TLVSet)
1123 {
1124   Result_t result = TLVSet.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID));
1125   if ( ASDCP_SUCCESS(result) )
1126     result = TLVSet.ReadObject(OBJ_READ_ARGS(GenerationInterchangeObject, GenerationUID));
1127   return result;
1128 }
1129
1130 //
1131 ASDCP::Result_t
1132 ASDCP::MXF::InterchangeObject::WriteToTLVSet(TLVWriter& TLVSet)
1133 {
1134   Result_t result = TLVSet.WriteObject(OBJ_WRITE_ARGS(InterchangeObject, InstanceUID));
1135   if ( ASDCP_SUCCESS(result) )
1136     result = TLVSet.WriteObject(OBJ_WRITE_ARGS(GenerationInterchangeObject, GenerationUID));
1137   return result;
1138 }
1139
1140 //
1141 ASDCP::Result_t
1142 ASDCP::MXF::InterchangeObject::InitFromBuffer(const byte_t* p, ui32_t l)
1143 {
1144   ASDCP_TEST_NULL(p);
1145   Result_t result = RESULT_FALSE;
1146
1147   if ( m_Typeinfo == 0 )
1148     {
1149       result = KLVPacket::InitFromBuffer(p, l);
1150     }
1151   else
1152     {
1153       result = KLVPacket::InitFromBuffer(p, l, m_Typeinfo->ul);
1154
1155       if ( ASDCP_SUCCESS(result) )
1156         {
1157           TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup);
1158           result = InitFromTLVSet(MemRDR);
1159         }
1160     }
1161   
1162   return result;
1163 }
1164
1165 //
1166 ASDCP::Result_t
1167 ASDCP::MXF::InterchangeObject::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
1168 {
1169   if ( m_Typeinfo == 0 )
1170     return RESULT_STATE;
1171
1172   TLVWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length, m_Lookup);
1173   Result_t result = WriteToTLVSet(MemWRT);
1174
1175   if ( ASDCP_SUCCESS(result) )
1176     {
1177       ui32_t packet_length = MemWRT.Length();
1178       result = WriteKLToBuffer(Buffer, m_Typeinfo->ul, packet_length);
1179
1180       if ( ASDCP_SUCCESS(result) )
1181         Buffer.Size(Buffer.Size() + packet_length);
1182     }
1183
1184   return result;
1185 }
1186
1187 //
1188 void
1189 ASDCP::MXF::InterchangeObject::Dump(FILE* stream)
1190 {
1191   char identbuf[IdentBufferLen];
1192
1193   fputc('\n', stream);
1194   KLVPacket::Dump(stream, false);
1195   fprintf(stream, "             InstanceUID = %s\n",  InstanceUID.EncodeHex(identbuf, IdentBufferLen));
1196   fprintf(stream, "           GenerationUID = %s\n",  GenerationUID.EncodeHex(identbuf, IdentBufferLen));
1197 }
1198
1199 //
1200 bool
1201 ASDCP::MXF::InterchangeObject::IsA(const byte_t* label)
1202 {
1203   if ( m_KLLength == 0 )
1204     return false;
1205
1206   return ( memcmp(label, m_KeyStart, SMPTE_UL_LENGTH) == 0 );
1207 }
1208
1209
1210 //
1211 // end MXF.cpp
1212 //