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