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