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