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