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