ae45e0780b8c42d4bc477fb790899becbd81eb99
[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 #define ASDCP_DECLARE_MDD
33 #include "MDD.h"
34 #include "MXF.h"
35 #include <hex_utils.h>
36
37 //------------------------------------------------------------------------------------------
38 //
39
40 const ui32_t kl_length = ASDCP::SMPTE_UL_LENGTH + ASDCP::MXF_BER_LENGTH;
41
42 const byte_t mdd_key[] = { 0x06, 0x0e, 0x2b, 0x34 };
43
44 //
45 const ASDCP::MDDEntry*
46 ASDCP::GetMDDEntry(const byte_t* ul_buf)
47 {
48   ui32_t t_idx = 0;
49   ui32_t k_idx = 8;
50
51   // must be a pointer to a SMPTE UL
52   if ( ul_buf == 0 || memcmp(mdd_key, ul_buf, 4) != 0 )
53     return 0;
54
55   // advance to first matching element
56   // TODO: optimize using binary search
57   while ( s_MDD_Table[t_idx].ul != 0
58           && s_MDD_Table[t_idx].ul[k_idx] != ul_buf[k_idx] )
59     t_idx++;
60
61   if ( s_MDD_Table[t_idx].ul == 0 )
62     return 0;
63
64   // match successive elements
65   while ( s_MDD_Table[t_idx].ul != 0
66           && k_idx < SMPTE_UL_LENGTH - 1
67           && s_MDD_Table[t_idx].ul[k_idx] == ul_buf[k_idx] )
68     {
69       if ( s_MDD_Table[t_idx].ul[k_idx+1] == ul_buf[k_idx+1] )
70         {
71           k_idx++;
72         }
73       else
74         {
75           while ( s_MDD_Table[t_idx].ul != 0
76                   && s_MDD_Table[t_idx].ul[k_idx] == ul_buf[k_idx]
77                   && s_MDD_Table[t_idx].ul[k_idx+1] != ul_buf[k_idx+1] )
78             t_idx++;
79               
80           while ( s_MDD_Table[t_idx].ul[k_idx] != ul_buf[k_idx] )
81             k_idx--;
82         }
83     }
84
85   return (s_MDD_Table[t_idx].ul == 0 ? 0 : &s_MDD_Table[t_idx]);
86 }
87
88 //------------------------------------------------------------------------------------------
89 //
90
91 //
92 ASDCP::Result_t
93 ASDCP::MXF::SeekToRIP(const ASDCP::FileReader& Reader)
94 {
95   ASDCP::fpos_t end_pos;
96
97   // go to the end - 4 bytes
98   Result_t result = Reader.Seek(0, ASDCP::SP_END);
99
100   if ( ASDCP_SUCCESS(result) )
101     result = Reader.Tell(&end_pos);
102
103   if ( ASDCP_SUCCESS(result)
104        && end_pos < (SMPTE_UL_LENGTH+MXF_BER_LENGTH) )
105     result = RESULT_FAIL;  // File is smaller than an empty packet!
106
107   if ( ASDCP_SUCCESS(result) )
108     result = Reader.Seek(end_pos - 4);
109
110   // get the ui32_t RIP length
111   ui32_t read_count;
112   byte_t intbuf[MXF_BER_LENGTH];
113   ui32_t rip_size = 0;
114
115   if ( ASDCP_SUCCESS(result) )
116     {
117       result = Reader.Read(intbuf, MXF_BER_LENGTH, &read_count);
118
119       if ( ASDCP_SUCCESS(result) && read_count != 4 )
120         result = RESULT_FAIL;
121     }
122
123   if ( ASDCP_SUCCESS(result) )
124     {
125       rip_size = ASDCP_i32_BE(cp2i<ui32_t>(intbuf));
126
127       if ( rip_size > end_pos ) // RIP can't be bigger than the file
128         return RESULT_FAIL;
129     }
130
131   // reposition to start of RIP
132   if ( ASDCP_SUCCESS(result) )
133     result = Reader.Seek(end_pos - rip_size);
134
135   return result;
136 }
137
138 //
139 ASDCP::Result_t
140 ASDCP::MXF::RIP::InitFromFile(const ASDCP::FileReader& Reader)
141 {
142   Result_t result = KLVFilePacket::InitFromFile(Reader, s_MDD_Table[MDDindex_RandomIndexMetadata].ul);
143
144   if ( ASDCP_SUCCESS(result) )
145     {
146       MemIOReader MemRDR(m_ValueStart, m_ValueLength - 4);
147       result =  PairArray.ReadFrom(MemRDR);
148     }
149
150   if ( ASDCP_FAILURE(result) )
151     DefaultLogSink().Error("Failed to initialize RIP\n");
152
153   return result;
154 }
155
156 //
157 ASDCP::Result_t
158 ASDCP::MXF::RIP::WriteToFile(ASDCP::FileWriter& Writer)
159 {
160   Result_t result = WriteKLToFile(Writer, s_MDD_Table[MDDindex_RandomIndexMetadata].ul, 0);
161   return result;
162 }
163
164 //
165 void
166 ASDCP::MXF::RIP::Dump(FILE* stream)
167 {
168   if ( stream == 0 )
169     stream = stderr;
170
171   KLVFilePacket::Dump(stream, false);
172   PairArray.Dump(stream, false);
173
174   fputs("==========================================================================\n", stream);
175 }
176
177 //------------------------------------------------------------------------------------------
178 //
179
180 //
181 ASDCP::Result_t
182 ASDCP::MXF::Partition::InitFromFile(const ASDCP::FileReader& Reader)
183 {
184   Result_t result = KLVFilePacket::InitFromFile(Reader);
185   // test the UL
186   // could be one of several values
187
188   if ( ASDCP_SUCCESS(result) )
189     {
190       MemIOReader MemRDR(m_ValueStart, m_ValueLength);
191       result = MemRDR.ReadUi16BE(&MajorVersion);
192       if ( ASDCP_SUCCESS(result) )  result = MemRDR.ReadUi16BE(&MinorVersion);
193       if ( ASDCP_SUCCESS(result) )  result = MemRDR.ReadUi32BE(&KAGSize);
194       if ( ASDCP_SUCCESS(result) )  result = MemRDR.ReadUi64BE(&ThisPartition);
195       if ( ASDCP_SUCCESS(result) )  result = MemRDR.ReadUi64BE(&PreviousPartition);
196       if ( ASDCP_SUCCESS(result) )  result = MemRDR.ReadUi64BE(&FooterPartition);
197       if ( ASDCP_SUCCESS(result) )  result = MemRDR.ReadUi64BE(&HeaderByteCount);
198       if ( ASDCP_SUCCESS(result) )  result = MemRDR.ReadUi64BE(&IndexByteCount);
199       if ( ASDCP_SUCCESS(result) )  result = MemRDR.ReadUi32BE(&IndexSID);
200       if ( ASDCP_SUCCESS(result) )  result = MemRDR.ReadUi64BE(&BodyOffset);
201       if ( ASDCP_SUCCESS(result) )  result = MemRDR.ReadUi32BE(&BodySID);
202       if ( ASDCP_SUCCESS(result) )  result = OperationalPattern.ReadFrom(MemRDR);
203       if ( ASDCP_SUCCESS(result) )  result = EssenceContainers.ReadFrom(MemRDR);
204     }
205
206   if ( ASDCP_FAILURE(result) )
207     DefaultLogSink().Error("Failed to initialize Partition\n");
208
209   return result;
210 }
211
212 //
213 ASDCP::Result_t
214 ASDCP::MXF::Partition::WriteToFile(ASDCP::FileWriter& Writer)
215 {
216   Result_t result = m_Buffer.Capacity(1024);
217
218   if ( ASDCP_SUCCESS(result) )
219     {
220       MemIOWriter MemWRT(m_Buffer.Data(), m_Buffer.Capacity());
221       result = MemWRT.WriteUi16BE(MajorVersion);
222       if ( ASDCP_SUCCESS(result) )  result = MemWRT.WriteUi16BE(MinorVersion);
223       if ( ASDCP_SUCCESS(result) )  result = MemWRT.WriteUi32BE(KAGSize);
224       if ( ASDCP_SUCCESS(result) )  result = MemWRT.WriteUi64BE(ThisPartition);
225       if ( ASDCP_SUCCESS(result) )  result = MemWRT.WriteUi64BE(PreviousPartition);
226       if ( ASDCP_SUCCESS(result) )  result = MemWRT.WriteUi64BE(FooterPartition);
227       if ( ASDCP_SUCCESS(result) )  result = MemWRT.WriteUi64BE(HeaderByteCount);
228       if ( ASDCP_SUCCESS(result) )  result = MemWRT.WriteUi64BE(IndexByteCount);
229       if ( ASDCP_SUCCESS(result) )  result = MemWRT.WriteUi32BE(IndexSID);
230       if ( ASDCP_SUCCESS(result) )  result = MemWRT.WriteUi64BE(BodyOffset);
231       if ( ASDCP_SUCCESS(result) )  result = MemWRT.WriteUi32BE(BodySID);
232       if ( ASDCP_SUCCESS(result) )  result = OperationalPattern.WriteTo(MemWRT);
233       if ( ASDCP_SUCCESS(result) )  result = EssenceContainers.WriteTo(MemWRT);
234       if ( ASDCP_SUCCESS(result) )  m_Buffer.Size(MemWRT.Size());
235     }
236
237   if ( ASDCP_SUCCESS(result) )
238     {
239       ui32_t write_count; // this is subclassed, so the UL is only right some of the time
240       result = WriteKLToFile(Writer, s_MDD_Table[MDDindex_ClosedCompleteHeader].ul, m_Buffer.Size());
241
242       if ( ASDCP_SUCCESS(result) )
243         result = Writer.Write(m_Buffer.RoData(), m_Buffer.Size(), &write_count);
244     }
245
246   return result;
247 }
248
249 //
250 void
251 ASDCP::MXF::Partition::Dump(FILE* stream)
252 {
253   char identbuf[IdentBufferLen];
254   char intbuf[IntBufferLen];
255
256   if ( stream == 0 )
257     stream = stderr;
258
259   KLVFilePacket::Dump(stream, false);
260   fprintf(stream, "  MajorVersion       = %hu\n", MajorVersion);
261   fprintf(stream, "  MinorVersion       = %hu\n", MinorVersion);
262   fprintf(stream, "  KAGSize            = %lu\n", KAGSize);
263   fprintf(stream, "  ThisPartition      = %s\n",  ui64sz(ThisPartition, intbuf));
264   fprintf(stream, "  PreviousPartition  = %s\n",  ui64sz(PreviousPartition, intbuf));
265   fprintf(stream, "  FooterPartition    = %s\n",  ui64sz(FooterPartition, intbuf));
266   fprintf(stream, "  HeaderByteCount    = %s\n",  ui64sz(HeaderByteCount, intbuf));
267   fprintf(stream, "  IndexByteCount     = %s\n",  ui64sz(IndexByteCount, intbuf));
268   fprintf(stream, "  IndexSID           = %lu\n", IndexSID);
269   fprintf(stream, "  BodyOffset         = %s\n",  ui64sz(BodyOffset, intbuf));
270   fprintf(stream, "  BodySID            = %lu\n", BodySID);
271   fprintf(stream, "  OperationalPattern = %s\n",  OperationalPattern.ToString(identbuf));
272   fputs("Essence Containers:\n", stream); EssenceContainers.Dump(stream, false);
273
274   fputs("==========================================================================\n", stream);
275 }
276
277
278 //------------------------------------------------------------------------------------------
279 //
280
281 class ASDCP::MXF::Primer::h__PrimerLookup : public std::map<UL, TagValue>
282 {
283 public:
284   void InitWithBatch(ASDCP::MXF::Batch<ASDCP::MXF::Primer::LocalTagEntry>& Batch)
285   {
286     ASDCP::MXF::Batch<ASDCP::MXF::Primer::LocalTagEntry>::iterator i = Batch.begin();
287
288     for ( ; i != Batch.end(); i++ )
289       insert(std::map<UL, TagValue>::value_type((*i).UL, (*i).Tag));
290   }
291 };
292
293
294 //
295 ASDCP::MXF::Primer::Primer() {}
296
297 //
298 ASDCP::MXF::Primer::~Primer() {}
299
300 //
301 void
302 ASDCP::MXF::Primer::ClearTagList()
303 {
304   LocalTagEntryBatch.clear();
305   m_Lookup = new h__PrimerLookup;
306 }
307
308 //
309 ASDCP::Result_t
310 ASDCP::MXF::Primer::InitFromBuffer(const byte_t* p, ui32_t l)
311 {
312   Result_t result = KLVPacket::InitFromBuffer(p, l, s_MDD_Table[MDDindex_Primer].ul);
313
314   if ( ASDCP_SUCCESS(result) )
315     {
316       MemIOReader MemRDR(m_ValueStart, m_ValueLength);
317       result = LocalTagEntryBatch.ReadFrom(MemRDR);
318     }
319
320   if ( ASDCP_SUCCESS(result) )
321     {
322       m_Lookup = new h__PrimerLookup;
323       m_Lookup->InitWithBatch(LocalTagEntryBatch);
324     }
325
326   if ( ASDCP_FAILURE(result) )
327     DefaultLogSink().Error("Failed to initialize Primer\n");
328
329   return result;
330 }
331
332 //
333 ASDCP::Result_t
334 ASDCP::MXF::Primer::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
335 {
336   MemIOWriter MemWRT(Buffer.Data(), Buffer.Capacity());
337   Result_t result = LocalTagEntryBatch.WriteTo(MemWRT);
338   Buffer.Size(MemWRT.Size());
339 #if 0
340   if ( ASDCP_SUCCESS(result) )
341     {
342       ui32_t write_count;
343       result = WriteKLToFile(Writer, s_MDD_Table[MDDindex_Primer].ul, Buffer.Size());
344
345       if ( ASDCP_SUCCESS(result) )
346         result = Writer.Write(Buffer.RoData(), Buffer.Size(), &write_count);
347     }
348 #endif
349
350   return result;
351 }
352
353 //
354 ASDCP::Result_t
355 ASDCP::MXF::Primer::InsertTag(const ASDCP::UL& Key, ASDCP::TagValue& Tag)
356 {
357   assert(m_Lookup);
358
359   std::map<UL, TagValue>::iterator i = m_Lookup->find(Key);
360
361   if ( i == m_Lookup->end() )
362     {
363       const MDDEntry* mdde = GetMDDEntry(Key.Value());
364       assert(mdde);
365
366       LocalTagEntry TmpEntry;
367       TmpEntry.UL = Key;
368       TmpEntry.Tag = mdde->tag;
369
370       LocalTagEntryBatch.push_back(TmpEntry);
371       m_Lookup->insert(std::map<UL, TagValue>::value_type(TmpEntry.UL, TmpEntry.Tag));
372     }
373    
374   return RESULT_OK;
375 }
376
377 //
378 ASDCP::Result_t
379 ASDCP::MXF::Primer::TagForKey(const ASDCP::UL& Key, ASDCP::TagValue& Tag)
380 {
381   assert(m_Lookup);
382   if ( m_Lookup.empty() )
383     {
384       DefaultLogSink().Error("Primer lookup is empty\n");
385       return RESULT_FAIL;
386     }
387
388   std::map<UL, TagValue>::iterator i = m_Lookup->find(Key);
389
390   if ( i == m_Lookup->end() )
391     return RESULT_FALSE;
392
393   Tag = (*i).second;
394   return RESULT_OK;
395 }
396
397 //
398 void
399 ASDCP::MXF::Primer::Dump(FILE* stream)
400 {
401   char identbuf[IdentBufferLen];
402
403   if ( stream == 0 )
404     stream = stderr;
405
406   KLVPacket::Dump(stream, false);
407   fprintf(stream, "Primer: %lu %s\n",
408           LocalTagEntryBatch.ItemCount,
409           ( LocalTagEntryBatch.ItemCount == 1 ? "entry" : "entries" ));
410   
411   Batch<LocalTagEntry>::iterator i = LocalTagEntryBatch.begin();
412   for ( ; i != LocalTagEntryBatch.end(); i++ )
413     {
414       const MDDEntry* Entry = GetMDDEntry((*i).UL.Value());
415       fprintf(stream, "  %s %s\n", (*i).ToString(identbuf), (Entry ? Entry->name : "Unknown"));
416     }
417
418   fputs("==========================================================================\n", stream);
419 }
420
421
422 //------------------------------------------------------------------------------------------
423 //
424
425 //
426 ASDCP::Result_t
427 ASDCP::MXF::Preface::InitFromBuffer(const byte_t* p, ui32_t l)
428 {
429   ASDCP_TEST_NULL(p);
430
431   Result_t result = KLVPacket::InitFromBuffer(p, l, s_MDD_Table[MDDindex_Preface].ul);
432
433   if ( ASDCP_SUCCESS(result) )
434     {
435       TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup);
436
437       result = MemRDR.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID));
438       if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(GenerationInterchangeObject, GenerationUID));
439       if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(Preface, LastModifiedDate));
440       if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi16(OBJ_READ_ARGS(Preface, Version));
441       if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadUi32(OBJ_READ_ARGS(Preface, ObjectModelVersion));
442       if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(Preface, PrimaryPackage));
443       if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(Preface, Identifications));
444       if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(Preface, ContentStorage));
445       if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(Preface, OperationalPattern));
446       if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(Preface, EssenceContainers));
447       if ( ASDCP_SUCCESS(result) ) result = MemRDR.ReadObject(OBJ_READ_ARGS(Preface, DMSchemes));
448     }
449
450   if ( ASDCP_FAILURE(result) )
451     DefaultLogSink().Error("Failed to initialize Preface\n");
452
453   return result;
454 }
455
456 //
457 ASDCP::Result_t
458 ASDCP::MXF::Preface::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
459 {
460   TLVWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length, m_Lookup);
461   Result_t result = MemWRT.WriteObject(OBJ_WRITE_ARGS(InterchangeObject, InstanceUID));
462   if ( ASDCP_SUCCESS(result) )  result = MemWRT.WriteObject(OBJ_WRITE_ARGS(GenerationInterchangeObject, GenerationUID));
463   if ( ASDCP_SUCCESS(result) )  result = MemWRT.WriteObject(OBJ_WRITE_ARGS(Preface, LastModifiedDate));
464   if ( ASDCP_SUCCESS(result) )  result = MemWRT.WriteUi16(OBJ_WRITE_ARGS(Preface, Version));
465   if ( ASDCP_SUCCESS(result) )  result = MemWRT.WriteUi32(OBJ_WRITE_ARGS(Preface, ObjectModelVersion));
466   if ( ASDCP_SUCCESS(result) )  result = MemWRT.WriteObject(OBJ_WRITE_ARGS(Preface, PrimaryPackage));
467   if ( ASDCP_SUCCESS(result) )  result = MemWRT.WriteObject(OBJ_WRITE_ARGS(Preface, Identifications));
468   if ( ASDCP_SUCCESS(result) )  result = MemWRT.WriteObject(OBJ_WRITE_ARGS(Preface, ContentStorage));
469   if ( ASDCP_SUCCESS(result) )  result = MemWRT.WriteObject(OBJ_WRITE_ARGS(Preface, OperationalPattern));
470   if ( ASDCP_SUCCESS(result) )  result = MemWRT.WriteObject(OBJ_WRITE_ARGS(Preface, EssenceContainers));
471   if ( ASDCP_SUCCESS(result) )  result = MemWRT.WriteObject(OBJ_WRITE_ARGS(Preface, DMSchemes));
472
473   if ( ASDCP_SUCCESS(result) )
474     {
475       ui32_t packet_length = MemWRT.Size();
476       result = WriteKLToBuffer(Buffer, s_MDD_Table[MDDindex_Preface].ul, packet_length);
477
478       if ( ASDCP_SUCCESS(result) )
479         Buffer.Size(Buffer.Size() + packet_length);
480     }
481
482   return result;
483 }
484
485 //
486 void
487 ASDCP::MXF::Preface::Dump(FILE* stream)
488 {
489   char identbuf[IdentBufferLen];
490
491   if ( stream == 0 )
492     stream = stderr;
493
494   KLVPacket::Dump(stream, false);
495   fprintf(stream, "  InstanceUID        = %s\n",  InstanceUID.ToString(identbuf));
496   fprintf(stream, "  GenerationUID      = %s\n",  GenerationUID.ToString(identbuf));
497   fprintf(stream, "  LastModifiedDate   = %s\n",  LastModifiedDate.ToString(identbuf));
498   fprintf(stream, "  Version            = %hu\n", Version);
499   fprintf(stream, "  ObjectModelVersion = %lu\n", ObjectModelVersion);
500   fprintf(stream, "  PrimaryPackage     = %s\n",  PrimaryPackage.ToString(identbuf));
501   fprintf(stream, "  Identifications:\n");  Identifications.Dump(stream);
502   fprintf(stream, "  ContentStorage     = %s\n",  ContentStorage.ToString(identbuf));
503   fprintf(stream, "  OperationalPattern = %s\n",  OperationalPattern.ToString(identbuf));
504   fprintf(stream, "  EssenceContainers:\n");  EssenceContainers.Dump(stream);
505   fprintf(stream, "  DMSchemes:\n");  DMSchemes.Dump(stream);
506
507   fputs("==========================================================================\n", stream);
508 }
509
510 //------------------------------------------------------------------------------------------
511 //
512
513 //
514 class ASDCP::MXF::h__PacketList
515 {
516 public:
517   std::list<InterchangeObject*> m_List;
518   std::map<UL, InterchangeObject*> m_Map;
519
520   ~h__PacketList() {
521     while ( ! m_List.empty() )
522       {
523         delete m_List.back();
524         m_List.pop_back();
525       }
526   }
527
528   //
529   void AddPacket(InterchangeObject* ThePacket)
530   {
531     assert(ThePacket);
532     m_Map.insert(std::map<UID, InterchangeObject*>::value_type(ThePacket->InstanceUID, ThePacket));
533     m_List.push_back(ThePacket);
534   }
535
536   //
537   Result_t GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
538   {
539     ASDCP_TEST_NULL(ObjectID);
540     ASDCP_TEST_NULL(Object);
541     std::list<InterchangeObject*>::iterator li;
542     *Object = 0;
543
544     for ( li = m_List.begin(); li != m_List.end(); li++ )
545       {
546         if ( (*li)->HasUL(ObjectID) )
547           {
548             *Object = *li;
549             return RESULT_OK;
550           }
551       }
552
553     return RESULT_FAIL;
554   }
555 };
556
557 //------------------------------------------------------------------------------------------
558 //
559
560 ASDCP::MXF::OPAtomHeader::OPAtomHeader() : m_Preface(0), m_HasRIP(false)
561 {
562   m_PacketList = new h__PacketList;
563 }
564
565
566 ASDCP::MXF::OPAtomHeader::~OPAtomHeader()
567 {
568 }
569
570
571 ASDCP::Result_t
572 ASDCP::MXF::OPAtomHeader::InitFromFile(const ASDCP::FileReader& Reader)
573 {
574   m_HasRIP = false;
575   Result_t result = SeekToRIP(Reader);
576
577   if ( ASDCP_SUCCESS(result) )
578     {
579       result = m_RIP.InitFromFile(Reader);
580
581       if ( ASDCP_FAILURE(result) )
582         {
583           DefaultLogSink().Error("File contains no RIP\n");
584           result = RESULT_OK;
585         }
586       else
587         {
588           m_HasRIP = true;
589         }
590     }
591
592   if ( ASDCP_SUCCESS(result) )
593     result = Reader.Seek(0);
594
595   if ( ASDCP_SUCCESS(result) )
596     result = Partition::InitFromFile(Reader); // test UL and OP
597
598   // slurp up the remainder of the header
599   ui32_t read_count;
600
601   if ( ASDCP_SUCCESS(result) )
602     {
603       ui32_t buf_len = HeaderByteCount;
604       result = m_Buffer.Capacity(buf_len);
605     }
606
607   if ( ASDCP_SUCCESS(result) )
608     result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
609
610   if ( ASDCP_SUCCESS(result) && read_count != m_Buffer.Capacity() )
611     {
612       DefaultLogSink().Error("Short read of OP-Atom header metadata; wanted %lu, got %lu\n",
613                              m_Buffer.Capacity(), read_count);
614       return RESULT_FAIL;
615     }
616
617   const byte_t* p = m_Buffer.RoData();
618   const byte_t* end_p = p + m_Buffer.Capacity();
619
620   while ( ASDCP_SUCCESS(result) && p < end_p )
621     {
622       // parse the packets and index them by uid, discard KLVFill items
623       InterchangeObject* object = CreateObject(p);
624       assert(object);
625
626       object->m_Lookup = &m_Primer;
627       result = object->InitFromBuffer(p, end_p - p);
628       const byte_t* redo_p = p;
629       p += object->PacketLength();
630       //      hexdump(p, object->PacketLength());
631
632       if ( ASDCP_SUCCESS(result) )
633         {
634           if ( object->IsA(s_MDD_Table[MDDindex_KLVFill].ul) )
635             {
636               delete object;
637             }
638           else if ( object->IsA(s_MDD_Table[MDDindex_Primer].ul) )
639             {
640               delete object;
641               result = m_Primer.InitFromBuffer(redo_p, end_p - redo_p);
642             }
643           else if ( object->IsA(s_MDD_Table[MDDindex_Preface].ul) )
644             {
645               m_Preface = object;
646             }
647           else  
648             {
649               m_PacketList->AddPacket(object);
650             }
651         }
652       else
653         {
654           DefaultLogSink().Error("Error initializing packet\n");
655           delete object;
656         }
657     }
658
659   return result;
660 }
661
662 //
663 ASDCP::Result_t
664 ASDCP::MXF::OPAtomHeader::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
665 {
666   InterchangeObject* TmpObject;
667
668   if ( Object == 0 )
669     Object = &TmpObject;
670
671   return m_PacketList->GetMDObjectByType(ObjectID, Object);
672 }
673
674 //
675 ASDCP::MXF::Identification*
676 ASDCP::MXF::OPAtomHeader::GetIdentification()
677 {
678   InterchangeObject* Object;
679
680   if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object)) )
681     return (Identification*)Object;
682
683   return 0;
684 }
685
686 //
687 ASDCP::MXF::SourcePackage*
688 ASDCP::MXF::OPAtomHeader::GetSourcePackage()
689 {
690   InterchangeObject* Object;
691
692   if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object)) )
693     return (SourcePackage*)Object;
694
695   return 0;
696 }
697
698 //
699 ASDCP::Result_t
700 ASDCP::MXF::OPAtomHeader::WriteToFile(ASDCP::FileWriter& Writer, ui32_t HeaderSize)
701 {
702   if ( HeaderSize < 4096 ) 
703     {
704       DefaultLogSink().Error("HeaderSize %lu is too small. Must be >= 4096\n");
705       return RESULT_FAIL;
706     }
707
708   ASDCP::FrameBuffer HeaderBuffer;
709
710   Result_t result = HeaderBuffer.Capacity(HeaderSize);
711   HeaderByteCount = HeaderSize;
712
713   if ( ASDCP_SUCCESS(result) )
714     {
715       assert(m_Preface);
716       m_Preface->m_Lookup = &m_Primer;
717       result = m_Preface->WriteToBuffer(HeaderBuffer);
718     }
719 #if 0
720   std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
721   for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
722     {
723       InterchangeObject* object = *pl_i;
724       object->m_Lookup = &m_Primer;
725       result = object->WriteToBuffer(HeaderBuffer);
726     }
727 #endif
728   if ( ASDCP_SUCCESS(result) )
729     result = Partition::WriteToFile(Writer);
730
731   //  if ( ASDCP_SUCCESS(result) )
732     //    result = m_Primer.WriteToFile(Writer);
733
734   if ( ASDCP_SUCCESS(result) )
735     {
736       ui32_t write_count;
737       Writer.Write(HeaderBuffer.RoData(), HeaderBuffer.Size(), &write_count);
738       assert(write_count == HeaderBuffer.Size());
739     }
740
741   // KLV Fill
742   if ( ASDCP_SUCCESS(result) )
743     {
744       ASDCP::fpos_t pos = Writer.Tell();
745
746       if ( pos > HeaderSize )
747         {
748           char intbuf[IntBufferLen];
749           DefaultLogSink().Error("Header size %s exceeds specified value %lu\n",
750                                  ui64sz(pos, intbuf),
751                                  HeaderSize);
752           return RESULT_FAIL;
753         }
754
755       ASDCP::FrameBuffer NilBuf;
756       ui32_t klv_fill_length = HeaderSize - (ui32_t)pos;
757
758       if ( klv_fill_length < kl_length )
759         {
760           DefaultLogSink().Error("Remaining region too small for KLV Fill header\n");
761           return RESULT_FAIL;
762         }
763
764       klv_fill_length -= kl_length;
765       result = WriteKLToFile(Writer, s_MDD_Table[MDDindex_KLVFill].ul, klv_fill_length);
766
767       if ( ASDCP_SUCCESS(result) )
768         result = NilBuf.Capacity(klv_fill_length);
769
770       if ( ASDCP_SUCCESS(result) )
771         {
772           memset(NilBuf.Data(), 0, klv_fill_length);
773           ui32_t write_count;
774           Writer.Write(NilBuf.RoData(), klv_fill_length, &write_count);
775           assert(write_count == klv_fill_length);
776         }
777     }
778
779   return result;
780 }
781
782 //
783 void
784 ASDCP::MXF::OPAtomHeader::Dump(FILE* stream)
785 {
786   if ( stream == 0 )
787     stream = stderr;
788
789   if ( m_HasRIP )
790     m_RIP.Dump(stream);
791
792   Partition::Dump(stream);
793   m_Primer.Dump(stream);
794   assert(m_Preface);
795   m_Preface->Dump(stream);
796
797   std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
798   for ( ; i != m_PacketList->m_List.end(); i++ )
799     (*i)->Dump(stream);
800 }
801
802 //------------------------------------------------------------------------------------------
803 //
804
805 ASDCP::MXF::OPAtomIndexFooter::OPAtomIndexFooter() : m_Lookup(0)
806 {
807   m_PacketList = new h__PacketList;
808 }
809
810
811 ASDCP::MXF::OPAtomIndexFooter::~OPAtomIndexFooter()
812 {
813 }
814
815
816 ASDCP::Result_t
817 ASDCP::MXF::OPAtomIndexFooter::InitFromFile(const ASDCP::FileReader& Reader)
818 {
819   Result_t result = Partition::InitFromFile(Reader); // test UL and OP
820
821   // slurp up the remainder of the footer
822   ui32_t read_count;
823
824   if ( ASDCP_SUCCESS(result) )
825     result = m_Buffer.Capacity(IndexByteCount);
826
827   if ( ASDCP_SUCCESS(result) )
828     result = Reader.Read(m_Buffer.Data(), m_Buffer.Capacity(), &read_count);
829
830   if ( ASDCP_SUCCESS(result) && read_count != m_Buffer.Capacity() )
831     {
832       DefaultLogSink().Error("Short read of footer partition: got %lu, expecting %lu\n",
833                              read_count, m_Buffer.Capacity());
834       return RESULT_FAIL;
835     }
836
837   const byte_t* p = m_Buffer.RoData();
838   const byte_t* end_p = p + m_Buffer.Capacity();
839   
840   while ( ASDCP_SUCCESS(result) && p < end_p )
841     {
842       // parse the packets and index them by uid, discard KLVFill items
843       InterchangeObject* object = CreateObject(p);
844       assert(object);
845
846       object->m_Lookup = m_Lookup;
847       result = object->InitFromBuffer(p, end_p - p);
848       p += object->PacketLength();
849
850       if ( ASDCP_SUCCESS(result) )
851         {
852           m_PacketList->AddPacket(object);
853         }
854       else
855         {
856           DefaultLogSink().Error("Error initializing packet\n");
857           delete object;
858         }
859     }
860
861   if ( ASDCP_FAILURE(result) )
862     DefaultLogSink().Error("Failed to initialize OPAtomIndexFooter\n");
863
864   return result;
865 }
866
867 //
868 ASDCP::Result_t
869 ASDCP::MXF::OPAtomIndexFooter::WriteToFile(ASDCP::FileWriter& Writer)
870 {
871   Result_t result = WriteKLToFile(Writer, s_MDD_Table[MDDindex_CompleteFooter].ul, 0);
872   return result;
873 }
874
875 //
876 void
877 ASDCP::MXF::OPAtomIndexFooter::Dump(FILE* stream)
878 {
879   if ( stream == 0 )
880     stream = stderr;
881
882   Partition::Dump(stream);
883
884   std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
885   for ( ; i != m_PacketList->m_List.end(); i++ )
886     (*i)->Dump(stream);
887 }
888
889 //
890 ASDCP::Result_t
891 ASDCP::MXF::OPAtomIndexFooter::Lookup(ui32_t frame_num, IndexTableSegment::IndexEntry& Entry)
892 {
893   std::list<InterchangeObject*>::iterator li;
894   for ( li = m_PacketList->m_List.begin(); li != m_PacketList->m_List.end(); li++ )
895     {
896       if ( (*li)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
897         {
898           IndexTableSegment* Segment = (IndexTableSegment*)(*li);
899           ui64_t start_pos = Segment->IndexStartPosition;
900
901           if ( Segment->EditUnitByteCount > 0 )
902             {
903               if ( m_PacketList->m_List.size() > 1 )
904                 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
905
906               if ( ! Segment->IndexEntryArray.empty() )
907                 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
908
909               Entry.StreamOffset = (ui64_t)frame_num * Segment->EditUnitByteCount;
910               return RESULT_OK;
911             }
912           else if ( (ui64_t)frame_num >= start_pos
913                     && (ui64_t)frame_num < (start_pos + Segment->IndexDuration) )
914             {
915               Entry = Segment->IndexEntryArray[frame_num-start_pos];
916               return RESULT_OK;
917             }
918         }
919     }
920
921   return RESULT_FAIL;
922 }
923
924
925 //------------------------------------------------------------------------------------------
926 //
927
928 //
929 ASDCP::Result_t
930 ASDCP::MXF::InterchangeObject::InitFromTLVSet(TLVReader& TLVSet)
931 {
932   Result_t result = TLVSet.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID));
933   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(GenerationInterchangeObject, GenerationUID));
934   return result;
935 }
936
937 //
938 ASDCP::Result_t
939 ASDCP::MXF::InterchangeObject::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
940 {
941   if ( Buffer.Capacity() < (Buffer.Size() + m_KLLength + m_ValueLength) )
942     {
943       DefaultLogSink().Error("InterchangeObject::WriteToBuffer: Buffer too small\n");
944       Dump();
945       return RESULT_READFAIL;
946     }
947
948   Result_t result = WriteKLToBuffer(Buffer, m_KeyStart, m_ValueLength);
949
950   if ( ASDCP_SUCCESS(result) )
951     {
952       memcpy(Buffer.Data() + Buffer.Size(), m_ValueStart, m_ValueLength);
953       Buffer.Size(Buffer.Size() + m_ValueLength);
954     }
955
956   return result;
957 }
958
959 //
960 void
961 ASDCP::MXF::InterchangeObject::Dump(FILE* stream)
962 {
963   char identbuf[IdentBufferLen];
964
965   fputc('\n', stream);
966   KLVPacket::Dump(stream, false);
967   fprintf(stream, "             InstanceUID = %s\n",  InstanceUID.ToString(identbuf));
968   fprintf(stream, "           GenerationUID = %s\n",  GenerationUID.ToString(identbuf));
969 }
970
971 //
972 bool
973 ASDCP::MXF::InterchangeObject::IsA(const byte_t* label)
974 {
975   if ( m_KLLength == 0 )
976     return false;
977
978   return ( memcmp(label, m_KeyStart, SMPTE_UL_LENGTH) == 0 );
979 }
980
981
982 //
983 // end MXF.cpp
984 //