o Fixed missing-index-partion bugs for AS-02 files.
[asdcplib.git] / src / MXF.cpp
1 /*
2 Copyright (c) 2005-2013, 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 "Metadata.h"
34 #include <KM_log.h>
35
36 using Kumu::DefaultLogSink;
37 using Kumu::GenRandomValue;
38
39 // index segments must be < 64K
40 // NOTE: this value may too high if advanced index entry elements are used.
41 const ui32_t CBRIndexEntriesPerSegment = 5000;
42
43 //------------------------------------------------------------------------------------------
44 //
45
46 //
47 ASDCP::Result_t
48 ASDCP::MXF::SeekToRIP(const Kumu::FileReader& Reader)
49 {
50   Kumu::fpos_t end_pos;
51
52   // go to the end - 4 bytes
53   Result_t result = Reader.Seek(0, Kumu::SP_END);
54
55   if ( ASDCP_SUCCESS(result) )
56     result = Reader.Tell(&end_pos);
57
58   if ( ASDCP_SUCCESS(result)
59        && end_pos < (SMPTE_UL_LENGTH+MXF_BER_LENGTH) )
60     {
61       DefaultLogSink().Error("File is smaller than an empty KLV packet.\n");
62       result = RESULT_FAIL;
63     }
64
65   if ( ASDCP_SUCCESS(result) )
66     result = Reader.Seek(end_pos - 4);
67
68   // get the ui32_t RIP length
69   ui32_t read_count;
70   byte_t intbuf[MXF_BER_LENGTH];
71   ui32_t rip_size = 0;
72
73   if ( ASDCP_SUCCESS(result) )
74     {
75       result = Reader.Read(intbuf, MXF_BER_LENGTH, &read_count);
76
77       if ( ASDCP_SUCCESS(result) && read_count != 4 )
78         {
79           DefaultLogSink().Error("RIP contains fewer than four bytes.\n");
80           result = RESULT_FAIL;
81         }
82     }
83
84   if ( ASDCP_SUCCESS(result) )
85     {
86       rip_size = KM_i32_BE(Kumu::cp2i<ui32_t>(intbuf));
87
88       if ( rip_size > end_pos ) // RIP can't be bigger than the file
89         {
90           DefaultLogSink().Error("RIP size impossibly large.\n");
91           return RESULT_FAIL;
92         }
93     }
94
95   // reposition to start of RIP
96   if ( ASDCP_SUCCESS(result) )
97     result = Reader.Seek(end_pos - rip_size);
98
99   return result;
100 }
101
102 //
103 ASDCP::Result_t
104 ASDCP::MXF::RIP::GetPairBySID(ui32_t SID, Pair& outPair) const
105 {
106   Array<Pair>::const_iterator pi = PairArray.begin();
107   for ( ; pi != PairArray.end(); pi++ )
108     {
109       if ( (*pi).BodySID == SID )
110         {
111           outPair = *pi;
112           return RESULT_OK;
113         }
114     }
115
116   return RESULT_FAIL;
117 }
118
119 //
120 ASDCP::Result_t
121 ASDCP::MXF::RIP::InitFromFile(const Kumu::FileReader& Reader)
122 {
123   assert(m_Dict);
124   Result_t result = KLVFilePacket::InitFromFile(Reader, m_Dict->ul(MDD_RandomIndexMetadata));
125
126   if ( ASDCP_SUCCESS(result) )
127     {
128       Kumu::MemIOReader MemRDR(m_ValueStart, m_ValueLength - 4);
129       result = PairArray.Unarchive(&MemRDR) ? RESULT_OK : RESULT_KLV_CODING;
130     }
131
132   if ( ASDCP_FAILURE(result) )
133     DefaultLogSink().Error("Failed to initialize RIP\n");
134
135   return result;
136 }
137
138 //
139 ASDCP::Result_t
140 ASDCP::MXF::RIP::WriteToFile(Kumu::FileWriter& Writer)
141 {
142   assert(m_Dict);
143   ASDCP::FrameBuffer Buffer;
144   ui32_t RIPSize = ( PairArray.size() * (sizeof(ui32_t) + sizeof(ui64_t)) ) + 4;
145   Result_t result = Buffer.Capacity(RIPSize);
146
147   if ( ASDCP_SUCCESS(result) )
148     result = WriteKLToFile(Writer, m_Dict->ul(MDD_RandomIndexMetadata), RIPSize);
149
150   if ( ASDCP_SUCCESS(result) )
151     {
152       result = RESULT_KLV_CODING;
153
154       Kumu::MemIOWriter MemWRT(Buffer.Data(), Buffer.Capacity());
155       if ( PairArray.Archive(&MemWRT) )
156         if ( MemWRT.WriteUi32BE(RIPSize + 20) )
157           {
158             Buffer.Size(MemWRT.Length());
159             result = RESULT_OK;
160           }
161     }
162
163   if ( ASDCP_SUCCESS(result) )
164     result = Writer.Write(Buffer.RoData(), Buffer.Size());
165
166   return result;
167 }
168
169 //
170 void
171 ASDCP::MXF::RIP::Dump(FILE* stream)
172 {
173   if ( stream == 0 )
174     stream = stderr;
175
176   KLVFilePacket::Dump(stream, *m_Dict, false);
177   PairArray.Dump(stream, false);
178 }
179
180 //------------------------------------------------------------------------------------------
181 //
182
183 //
184 ASDCP::MXF::Partition::PacketList::~PacketList() {
185   while ( ! m_List.empty() )
186     {
187       delete m_List.back();
188       m_List.pop_back();
189     }
190 }
191
192 //
193 void
194 ASDCP::MXF::Partition::PacketList::AddPacket(InterchangeObject* ThePacket) // takes ownership
195 {
196   assert(ThePacket);
197   m_Map.insert(std::map<UUID, InterchangeObject*>::value_type(ThePacket->InstanceUID, ThePacket));
198   m_List.push_back(ThePacket);
199 }
200
201 //
202 ASDCP::Result_t
203 ASDCP::MXF::Partition::PacketList::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
204 {
205   ASDCP_TEST_NULL(Object);
206
207   std::map<UUID, InterchangeObject*>::iterator mi = m_Map.find(ObjectID);
208   
209   if ( mi == m_Map.end() )
210     {
211       *Object = 0;
212       return RESULT_FAIL;
213     }
214
215   *Object = (*mi).second;
216   return RESULT_OK;
217 }
218
219 //
220 ASDCP::Result_t
221 ASDCP::MXF::Partition::PacketList::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
222 {
223   ASDCP_TEST_NULL(ObjectID);
224   ASDCP_TEST_NULL(Object);
225   std::list<InterchangeObject*>::iterator li;
226   *Object = 0;
227
228   for ( li = m_List.begin(); li != m_List.end(); li++ )
229     {
230       if ( (*li)->HasUL(ObjectID) )
231         {
232           *Object = *li;
233           return RESULT_OK;
234         }
235     }
236
237   return RESULT_FAIL;
238 }
239
240 //
241 ASDCP::Result_t
242 ASDCP::MXF::Partition::PacketList::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
243 {
244   ASDCP_TEST_NULL(ObjectID);
245   std::list<InterchangeObject*>::iterator li;
246
247   for ( li = m_List.begin(); li != m_List.end(); li++ )
248     {
249       if ( (*li)->HasUL(ObjectID) )
250         ObjectList.push_back(*li);
251     }
252
253   return ObjectList.empty() ? RESULT_FAIL : RESULT_OK;
254 }
255
256 //------------------------------------------------------------------------------------------
257 //
258
259
260 ASDCP::MXF::Partition::Partition(const Dictionary*& d) :
261   m_Dict(d),
262   MajorVersion(1), MinorVersion(2),
263   KAGSize(1), ThisPartition(0), PreviousPartition(0),
264   FooterPartition(0), HeaderByteCount(0), IndexByteCount(0), IndexSID(0),
265   BodyOffset(0), BodySID(0)
266 {
267   m_PacketList = new PacketList;
268 }
269
270 ASDCP::MXF::Partition::~Partition()
271 {
272 }
273
274 // takes ownership
275 void
276 ASDCP::MXF::Partition::AddChildObject(InterchangeObject* Object)
277 {
278   assert(Object);
279
280   if ( ! Object->InstanceUID.HasValue() )
281     GenRandomValue(Object->InstanceUID);
282
283   m_PacketList->AddPacket(Object);
284 }
285
286 //
287 ASDCP::Result_t
288 ASDCP::MXF::Partition::InitFromFile(const Kumu::FileReader& Reader)
289 {
290   Result_t result = KLVFilePacket::InitFromFile(Reader);
291   // test the UL
292   // could be one of several values
293   if ( ASDCP_SUCCESS(result) )
294     result = ASDCP::MXF::Partition::InitFromBuffer(m_ValueStart, m_ValueLength);
295   
296   return result;
297 }
298
299 //
300 ASDCP::Result_t
301 ASDCP::MXF::Partition::InitFromBuffer(const byte_t* p, ui32_t l)
302 {
303   Kumu::MemIOReader MemRDR(p, l);
304   Result_t result = RESULT_KLV_CODING;
305
306   if ( MemRDR.ReadUi16BE(&MajorVersion) )
307     if ( MemRDR.ReadUi16BE(&MinorVersion) )
308       if ( MemRDR.ReadUi32BE(&KAGSize) )
309         if ( MemRDR.ReadUi64BE(&ThisPartition) )
310           if ( MemRDR.ReadUi64BE(&PreviousPartition) )
311             if ( MemRDR.ReadUi64BE(&FooterPartition) )
312               if ( MemRDR.ReadUi64BE(&HeaderByteCount) )
313                 if ( MemRDR.ReadUi64BE(&IndexByteCount) )
314                   if ( MemRDR.ReadUi32BE(&IndexSID) )
315                     if ( MemRDR.ReadUi64BE(&BodyOffset) )
316                       if ( MemRDR.ReadUi32BE(&BodySID) )
317                         if ( OperationalPattern.Unarchive(&MemRDR) )
318                           if ( EssenceContainers.Unarchive(&MemRDR) )
319                             result = RESULT_OK;
320
321   if ( ASDCP_FAILURE(result) )
322     DefaultLogSink().Error("Failed to initialize Partition\n");
323
324   return result;
325 }
326
327 //
328 ASDCP::Result_t
329 ASDCP::MXF::Partition::WriteToFile(Kumu::FileWriter& Writer, UL& PartitionLabel)
330 {
331   ASDCP::FrameBuffer Buffer;
332   Result_t result = Buffer.Capacity(1024);
333
334   if ( ASDCP_SUCCESS(result) )
335     {
336       Kumu::MemIOWriter MemWRT(Buffer.Data(), Buffer.Capacity());
337       result = RESULT_KLV_CODING;
338       if ( MemWRT.WriteUi16BE(MajorVersion) )
339         if ( MemWRT.WriteUi16BE(MinorVersion) )
340           if ( MemWRT.WriteUi32BE(KAGSize) )
341             if ( MemWRT.WriteUi64BE(ThisPartition) )
342               if ( MemWRT.WriteUi64BE(PreviousPartition) )
343                 if ( MemWRT.WriteUi64BE(FooterPartition) )
344                   if ( MemWRT.WriteUi64BE(HeaderByteCount) )
345                     if ( MemWRT.WriteUi64BE(IndexByteCount) )
346                       if ( MemWRT.WriteUi32BE(IndexSID) )
347                         if ( MemWRT.WriteUi64BE(BodyOffset) )
348                           if ( MemWRT.WriteUi32BE(BodySID) )
349                             if ( OperationalPattern.Archive(&MemWRT) )
350                               if ( EssenceContainers.Archive(&MemWRT) )
351                                 {
352                                   Buffer.Size(MemWRT.Length());
353                                   result = RESULT_OK;
354                                 }
355     }
356
357   if ( ASDCP_SUCCESS(result) )
358     {
359       ui32_t write_count;
360       result = WriteKLToFile(Writer, PartitionLabel.Value(), Buffer.Size());
361
362       if ( ASDCP_SUCCESS(result) )
363         result = Writer.Write(Buffer.RoData(), Buffer.Size(), &write_count);
364     }
365
366   return result;
367 }
368
369 //
370 ui32_t
371 ASDCP::MXF::Partition::ArchiveSize()
372 {
373   return ( kl_length
374            + sizeof(ui16_t) + sizeof(ui16_t)
375            + sizeof(ui32_t)
376            + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t) + sizeof(ui64_t)
377            + sizeof(ui32_t)
378            + sizeof(ui64_t)
379            + sizeof(ui32_t)
380            + SMPTE_UL_LENGTH
381            + sizeof(ui32_t) + sizeof(ui32_t) + ( UUIDlen * EssenceContainers.size() ) );
382 }
383
384 //
385 void
386 ASDCP::MXF::Partition::Dump(FILE* stream)
387 {
388   char identbuf[IdentBufferLen];
389
390   if ( stream == 0 )
391     stream = stderr;
392
393   KLVFilePacket::Dump(stream, *m_Dict, false);
394   fprintf(stream, "  MajorVersion       = %hu\n", MajorVersion);
395   fprintf(stream, "  MinorVersion       = %hu\n", MinorVersion);
396   fprintf(stream, "  KAGSize            = %u\n",  KAGSize);
397   fprintf(stream, "  ThisPartition      = %s\n",  ui64sz(ThisPartition, identbuf));
398   fprintf(stream, "  PreviousPartition  = %s\n",  ui64sz(PreviousPartition, identbuf));
399   fprintf(stream, "  FooterPartition    = %s\n",  ui64sz(FooterPartition, identbuf));
400   fprintf(stream, "  HeaderByteCount    = %s\n",  ui64sz(HeaderByteCount, identbuf));
401   fprintf(stream, "  IndexByteCount     = %s\n",  ui64sz(IndexByteCount, identbuf));
402   fprintf(stream, "  IndexSID           = %u\n",  IndexSID);
403   fprintf(stream, "  BodyOffset         = %s\n",  ui64sz(BodyOffset, identbuf));
404   fprintf(stream, "  BodySID            = %u\n",  BodySID);
405   fprintf(stream, "  OperationalPattern = %s\n",  OperationalPattern.EncodeString(identbuf, IdentBufferLen));
406   fputs("Essence Containers:\n", stream); EssenceContainers.Dump(stream);
407 }
408
409
410 //------------------------------------------------------------------------------------------
411 //
412
413 class ASDCP::MXF::Primer::h__PrimerLookup : public std::map<UL, TagValue>
414 {
415 public:
416   void InitWithBatch(ASDCP::MXF::Batch<ASDCP::MXF::Primer::LocalTagEntry>& Batch)
417   {
418     ASDCP::MXF::Batch<ASDCP::MXF::Primer::LocalTagEntry>::iterator i = Batch.begin();
419
420     for ( ; i != Batch.end(); i++ )
421       insert(std::map<UL, TagValue>::value_type((*i).UL, (*i).Tag));
422   }
423 };
424
425
426 //
427 ASDCP::MXF::Primer::Primer(const Dictionary*& d) : m_LocalTag(0xff), m_Dict(d) {
428   m_UL = m_Dict->ul(MDD_Primer);
429 }
430
431 //
432 ASDCP::MXF::Primer::~Primer() {}
433
434 //
435 void
436 ASDCP::MXF::Primer::ClearTagList()
437 {
438   LocalTagEntryBatch.clear();
439   m_Lookup = new h__PrimerLookup;
440 }
441
442 //
443 ASDCP::Result_t
444 ASDCP::MXF::Primer::InitFromBuffer(const byte_t* p, ui32_t l)
445 {
446   assert(m_Dict);
447   Result_t result = KLVPacket::InitFromBuffer(p, l, m_Dict->ul(MDD_Primer));
448
449   if ( ASDCP_SUCCESS(result) )
450     {
451       Kumu::MemIOReader MemRDR(m_ValueStart, m_ValueLength);
452       result = LocalTagEntryBatch.Unarchive(&MemRDR) ? RESULT_OK : RESULT_KLV_CODING;
453     }
454
455   if ( ASDCP_SUCCESS(result) )
456     {
457       m_Lookup = new h__PrimerLookup;
458       m_Lookup->InitWithBatch(LocalTagEntryBatch);
459     }
460
461   if ( ASDCP_FAILURE(result) )
462     DefaultLogSink().Error("Failed to initialize Primer\n");
463
464   return result;
465 }
466
467 //
468 ASDCP::Result_t
469 ASDCP::MXF::Primer::WriteToFile(Kumu::FileWriter& Writer)
470 {
471   ASDCP::FrameBuffer Buffer;
472   Result_t result = Buffer.Capacity(128*1024);
473
474   if ( ASDCP_SUCCESS(result) )
475     result = WriteToBuffer(Buffer);
476
477   if ( ASDCP_SUCCESS(result) )
478   result = Writer.Write(Buffer.RoData(), Buffer.Size());
479
480   return result;
481 }
482
483 //
484 ASDCP::Result_t
485 ASDCP::MXF::Primer::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
486 {
487   assert(m_Dict);
488   ASDCP::FrameBuffer LocalTagBuffer;
489   Kumu::MemIOWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length);
490   Result_t result = LocalTagEntryBatch.Archive(&MemWRT) ? RESULT_OK : RESULT_KLV_CODING;
491
492   if ( ASDCP_SUCCESS(result) )
493     {
494       ui32_t packet_length = MemWRT.Length();
495       result = WriteKLToBuffer(Buffer, packet_length);
496
497       if ( ASDCP_SUCCESS(result) )
498         Buffer.Size(Buffer.Size() + packet_length);
499     }
500
501   return result;
502 }
503
504 //
505 ASDCP::Result_t
506 ASDCP::MXF::Primer::InsertTag(const MDDEntry& Entry, ASDCP::TagValue& Tag)
507 {
508   assert(m_Lookup);
509   UL TestUL(Entry.ul);
510   std::map<UL, TagValue>::iterator i = m_Lookup->find(TestUL);
511
512   if ( i == m_Lookup->end() )
513     {
514       if ( Entry.tag.a == 0 && Entry.tag.b == 0 )
515         {
516           Tag.a = 0xff;
517           Tag.b = m_LocalTag--;
518         }
519       else
520         {
521           Tag.a = Entry.tag.a;
522           Tag.b = Entry.tag.b;
523         }
524
525       LocalTagEntry TmpEntry;
526       TmpEntry.UL = TestUL;
527       TmpEntry.Tag = Tag;
528
529       LocalTagEntryBatch.push_back(TmpEntry);
530       m_Lookup->insert(std::map<UL, TagValue>::value_type(TmpEntry.UL, TmpEntry.Tag));
531     }
532   else
533     {
534       Tag = (*i).second;
535     }
536    
537   return RESULT_OK;
538 }
539
540 //
541 ASDCP::Result_t
542 ASDCP::MXF::Primer::TagForKey(const ASDCP::UL& Key, ASDCP::TagValue& Tag)
543 {
544   assert(m_Lookup);
545   if ( m_Lookup.empty() )
546     {
547       DefaultLogSink().Error("Primer lookup is empty\n");
548       return RESULT_FAIL;
549     }
550
551   std::map<UL, TagValue>::iterator i = m_Lookup->find(Key);
552
553   if ( i == m_Lookup->end() )
554     return RESULT_FALSE;
555
556   Tag = (*i).second;
557   return RESULT_OK;
558 }
559
560 //
561 void
562 ASDCP::MXF::Primer::Dump(FILE* stream)
563 {
564   assert(m_Dict);
565   char identbuf[IdentBufferLen];
566
567   if ( stream == 0 )
568     stream = stderr;
569
570   KLVPacket::Dump(stream, *m_Dict, false);
571   fprintf(stream, "Primer: %u %s\n",
572           (ui32_t)LocalTagEntryBatch.size(),
573           ( LocalTagEntryBatch.size() == 1 ? "entry" : "entries" ));
574   
575   Batch<LocalTagEntry>::iterator i = LocalTagEntryBatch.begin();
576   for ( ; i != LocalTagEntryBatch.end(); i++ )
577     {
578       const MDDEntry* Entry = m_Dict->FindUL((*i).UL.Value());
579       fprintf(stream, "  %s %s\n", (*i).EncodeString(identbuf, IdentBufferLen), (Entry ? Entry->name : "Unknown"));
580     }
581 }
582
583
584 //------------------------------------------------------------------------------------------
585 //
586
587 //
588 ASDCP::MXF::Preface::Preface(const Dictionary*& d) :
589   InterchangeObject(d), m_Dict(d), Version(258)
590 {
591   assert(m_Dict);
592   m_UL = m_Dict->Type(MDD_Preface).ul;
593   ObjectModelVersion = 0;
594 }
595
596 //
597 void
598 ASDCP::MXF::Preface::Copy(const Preface& rhs)
599 {
600   InterchangeObject::Copy(rhs);
601
602   LastModifiedDate = rhs.LastModifiedDate;
603   Version = rhs.Version;
604   ObjectModelVersion = rhs.ObjectModelVersion;
605   PrimaryPackage = rhs.PrimaryPackage;
606   Identifications = rhs.Identifications;
607   ContentStorage = rhs.ContentStorage;
608   OperationalPattern = rhs.OperationalPattern;
609   EssenceContainers = rhs.EssenceContainers;
610   DMSchemes = rhs.DMSchemes;
611 }
612
613 //
614 ASDCP::Result_t
615 ASDCP::MXF::Preface::InitFromTLVSet(TLVReader& TLVSet)
616 {
617   Result_t result = InterchangeObject::InitFromTLVSet(TLVSet);
618   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, LastModifiedDate));
619   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi16(OBJ_READ_ARGS(Preface, Version));
620   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi32(OBJ_READ_ARGS_OPT(Preface, ObjectModelVersion));
621   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS_OPT(Preface, PrimaryPackage));
622   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, Identifications));
623   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, ContentStorage));
624   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, OperationalPattern));
625   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, EssenceContainers));
626   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(Preface, DMSchemes));
627   return result;
628 }
629
630 //
631 ASDCP::Result_t
632 ASDCP::MXF::Preface::WriteToTLVSet(TLVWriter& TLVSet)
633 {
634   Result_t result = InterchangeObject::WriteToTLVSet(TLVSet);
635   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, LastModifiedDate));
636   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteUi16(OBJ_WRITE_ARGS(Preface, Version));
637   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteUi32(OBJ_WRITE_ARGS_OPT(Preface, ObjectModelVersion));
638   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteObject(OBJ_WRITE_ARGS_OPT(Preface, PrimaryPackage));
639   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, Identifications));
640   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, ContentStorage));
641   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, OperationalPattern));
642   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, EssenceContainers));
643   if ( ASDCP_SUCCESS(result) )  result = TLVSet.WriteObject(OBJ_WRITE_ARGS(Preface, DMSchemes));
644   return result;
645 }
646
647 //
648 ASDCP::Result_t
649 ASDCP::MXF::Preface::InitFromBuffer(const byte_t* p, ui32_t l)
650 {
651   return InterchangeObject::InitFromBuffer(p, l);
652 }
653
654 //
655 ASDCP::Result_t
656 ASDCP::MXF::Preface::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
657 {
658   return InterchangeObject::WriteToBuffer(Buffer);
659 }
660
661 //
662 void
663 ASDCP::MXF::Preface::Dump(FILE* stream)
664 {
665   char identbuf[IdentBufferLen];
666
667   if ( stream == 0 )
668     stream = stderr;
669
670   InterchangeObject::Dump(stream);
671   fprintf(stream, "  %22s = %s\n",  "LastModifiedDate", LastModifiedDate.EncodeString(identbuf, IdentBufferLen));
672   fprintf(stream, "  %22s = %hu\n", "Version", Version);
673
674   if ( ! ObjectModelVersion.empty() )
675     fprintf(stream, "  %22s = %u\n",  "ObjectModelVersion", ObjectModelVersion.get());
676
677   if ( ! PrimaryPackage.empty() )
678     fprintf(stream, "  %22s = %s\n",  "PrimaryPackage", PrimaryPackage.get().EncodeHex(identbuf, IdentBufferLen));
679
680   fprintf(stream, "  %22s:\n", "Identifications");  Identifications.Dump(stream);
681   fprintf(stream, "  %22s = %s\n",  "ContentStorage", ContentStorage.EncodeHex(identbuf, IdentBufferLen));
682   fprintf(stream, "  %22s = %s\n",  "OperationalPattern", OperationalPattern.EncodeString(identbuf, IdentBufferLen));
683   fprintf(stream, "  %22s:\n", "EssenceContainers");  EssenceContainers.Dump(stream);
684   fprintf(stream, "  %22s:\n", "DMSchemes");  DMSchemes.Dump(stream);
685 }
686
687 //------------------------------------------------------------------------------------------
688 //
689
690 ASDCP::MXF::OP1aHeader::OP1aHeader(const Dictionary*& d) : Partition(d), m_Dict(d), m_Primer(d), m_Preface(0) {}
691 ASDCP::MXF::OP1aHeader::~OP1aHeader() {}
692
693 //
694 ASDCP::Result_t
695 ASDCP::MXF::OP1aHeader::InitFromFile(const Kumu::FileReader& Reader)
696 {
697   Result_t result = Partition::InitFromFile(Reader);
698
699   if ( ASDCP_FAILURE(result) )
700     return result;
701
702   if ( m_Dict == &DefaultCompositeDict() )
703     {
704       // select more explicit dictionary if one is available
705       if ( OperationalPattern.ExactMatch(MXFInterop_OPAtom_Entry().ul) )
706         {
707           m_Dict = &DefaultInteropDict();
708         }
709       else if ( OperationalPattern.ExactMatch(SMPTE_390_OPAtom_Entry().ul) )
710         {
711           m_Dict = &DefaultSMPTEDict();
712         }
713     }
714
715   // slurp up the remainder of the header
716   if ( HeaderByteCount < 1024 )
717     {
718       DefaultLogSink().Warn("Improbably small HeaderByteCount value: %qu\n", HeaderByteCount);
719     }
720   else if (HeaderByteCount > ( 4 * Kumu::Megabyte ) )
721     {
722       DefaultLogSink().Warn("Improbably huge HeaderByteCount value: %qu\n", HeaderByteCount);
723     }
724   
725   result = m_HeaderData.Capacity(Kumu::xmin(4*Kumu::Megabyte, static_cast<ui32_t>(HeaderByteCount)));
726
727   if ( ASDCP_SUCCESS(result) )
728     {
729       ui32_t read_count;
730       result = Reader.Read(m_HeaderData.Data(), m_HeaderData.Capacity(), &read_count);
731
732       if ( ASDCP_FAILURE(result) )
733         {
734           DefaultLogSink().Error("OP1aHeader::InitFromFile, Read failed\n");
735           return result;
736         }
737
738       if ( read_count != m_HeaderData.Capacity() )
739         {
740           DefaultLogSink().Error("Short read of OP-Atom header metadata; wanted %u, got %u\n",
741                                  m_HeaderData.Capacity(), read_count);
742           return RESULT_KLV_CODING;
743         }
744     }
745
746   if ( ASDCP_SUCCESS(result) )
747     result = InitFromBuffer(m_HeaderData.RoData(), m_HeaderData.Capacity());
748
749   return result;
750 }
751
752 //
753 ASDCP::Result_t
754 ASDCP::MXF::OP1aHeader::InitFromPartitionBuffer(const byte_t* p, ui32_t l)
755 {
756   Result_t result = KLVPacket::InitFromBuffer(p, l);
757
758   if ( ASDCP_SUCCESS(result) )
759     result = Partition::InitFromBuffer(m_ValueStart, m_ValueLength); // test UL and OP
760
761   if ( ASDCP_SUCCESS(result) )
762     {
763       ui32_t pp_len = KLVPacket::PacketLength();
764       result = InitFromBuffer(p + pp_len, l - pp_len);
765     }
766
767   return result;
768 }
769
770 //
771 ASDCP::Result_t
772 ASDCP::MXF::OP1aHeader::InitFromBuffer(const byte_t* p, ui32_t l)
773 {
774   assert(m_Dict);
775   Result_t result = RESULT_OK;
776   const byte_t* end_p = p + l;
777
778   while ( ASDCP_SUCCESS(result) && p < end_p )
779     {
780       // parse the packets and index them by uid, discard KLVFill items
781       InterchangeObject* object = CreateObject(m_Dict, p);
782       assert(object);
783
784       object->m_Lookup = &m_Primer;
785       result = object->InitFromBuffer(p, end_p - p);
786       const byte_t* redo_p = p;
787       p += object->PacketLength();
788       //      hexdump(p, object->PacketLength());
789
790       if ( ASDCP_SUCCESS(result) )
791         {
792           if ( object->IsA(m_Dict->ul(MDD_KLVFill)) )
793             {
794               delete object;
795
796               if ( p > end_p )
797                 {
798                   DefaultLogSink().Error("Fill item short read: %d.\n", p - end_p);
799                 }
800             }
801           else if ( object->IsA(m_Dict->ul(MDD_Primer)) ) // TODO: only one primer should be found
802             {
803               delete object;
804               result = m_Primer.InitFromBuffer(redo_p, end_p - redo_p);
805             }
806           else
807             {
808               m_PacketList->AddPacket(object); // takes ownership
809
810               if ( object->IsA(m_Dict->ul(MDD_Preface)) && m_Preface == 0 )
811                 m_Preface = (Preface*)object;
812             }
813         }
814       else
815         {
816           DefaultLogSink().Error("Error initializing packet\n");
817           delete object;
818         }
819     }
820
821   return result;
822 }
823
824 ASDCP::Result_t
825 ASDCP::MXF::OP1aHeader::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
826 {
827   return m_PacketList->GetMDObjectByID(ObjectID, Object);
828 }
829
830 //
831 ASDCP::Result_t
832 ASDCP::MXF::OP1aHeader::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
833 {
834   InterchangeObject* TmpObject;
835
836   if ( Object == 0 )
837     Object = &TmpObject;
838
839   return m_PacketList->GetMDObjectByType(ObjectID, Object);
840 }
841
842 //
843 ASDCP::Result_t
844 ASDCP::MXF::OP1aHeader::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
845 {
846   return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
847 }
848
849 //
850 ASDCP::MXF::Identification*
851 ASDCP::MXF::OP1aHeader::GetIdentification()
852 {
853   InterchangeObject* Object;
854
855   if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object)) )
856     return (Identification*)Object;
857
858   return 0;
859 }
860
861 //
862 ASDCP::MXF::SourcePackage*
863 ASDCP::MXF::OP1aHeader::GetSourcePackage()
864 {
865   InterchangeObject* Object;
866
867   if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object)) )
868     return (SourcePackage*)Object;
869
870   return 0;
871 }
872
873 //
874 ASDCP::Result_t
875 ASDCP::MXF::OP1aHeader::WriteToFile(Kumu::FileWriter& Writer, ui32_t HeaderSize)
876 {
877   assert(m_Dict);
878   if ( m_Preface == 0 )
879     return RESULT_STATE;
880
881   if ( HeaderSize < 4096 ) 
882     {
883       DefaultLogSink().Error("HeaderSize %u is too small. Must be >= 4096\n", HeaderSize);
884       return RESULT_FAIL;
885     }
886
887   ASDCP::FrameBuffer HeaderBuffer;
888   HeaderByteCount = HeaderSize - ArchiveSize();
889   assert (HeaderByteCount <= 0xFFFFFFFFL);
890   Result_t result = HeaderBuffer.Capacity((ui32_t) HeaderByteCount); 
891   m_Preface->m_Lookup = &m_Primer;
892
893   std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
894   for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
895     {
896       InterchangeObject* object = *pl_i;
897       object->m_Lookup = &m_Primer;
898
899       ASDCP::FrameBuffer WriteWrapper;
900       WriteWrapper.SetData(HeaderBuffer.Data() + HeaderBuffer.Size(),
901                            HeaderBuffer.Capacity() - HeaderBuffer.Size());
902       result = object->WriteToBuffer(WriteWrapper);
903       HeaderBuffer.Size(HeaderBuffer.Size() + WriteWrapper.Size());
904     }
905
906   if ( ASDCP_SUCCESS(result) )
907     {
908       UL TmpUL(m_Dict->ul(MDD_ClosedCompleteHeader));
909       result = Partition::WriteToFile(Writer, TmpUL);
910     }
911
912   if ( ASDCP_SUCCESS(result) )
913     result = m_Primer.WriteToFile(Writer);
914
915   if ( ASDCP_SUCCESS(result) )
916     {
917       ui32_t write_count;
918       Writer.Write(HeaderBuffer.RoData(), HeaderBuffer.Size(), &write_count);
919       assert(write_count == HeaderBuffer.Size());
920     }
921
922   // KLV Fill
923   if ( ASDCP_SUCCESS(result) )
924     {
925       Kumu::fpos_t pos = Writer.Tell();
926
927       if ( pos > (Kumu::fpos_t)HeaderByteCount )
928         {
929           char intbuf[IntBufferLen];
930           DefaultLogSink().Error("Header size %s exceeds specified value %u\n",
931                                  ui64sz(pos, intbuf),
932                                  HeaderSize);
933           return RESULT_FAIL;
934         }
935
936       ASDCP::FrameBuffer NilBuf;
937       ui32_t klv_fill_length = HeaderSize - (ui32_t)pos;
938
939       if ( klv_fill_length < kl_length )
940         {
941           DefaultLogSink().Error("Remaining region too small for KLV Fill header\n");
942           return RESULT_FAIL;
943         }
944
945       klv_fill_length -= kl_length;
946       result = WriteKLToFile(Writer, m_Dict->ul(MDD_KLVFill), klv_fill_length);
947
948       if ( ASDCP_SUCCESS(result) )
949         result = NilBuf.Capacity(klv_fill_length);
950
951       if ( ASDCP_SUCCESS(result) )
952         {
953           memset(NilBuf.Data(), 0, klv_fill_length);
954           ui32_t write_count;
955           Writer.Write(NilBuf.RoData(), klv_fill_length, &write_count);
956           assert(write_count == klv_fill_length);
957         }
958     }
959
960   return result;
961 }
962
963 //
964 void
965 ASDCP::MXF::OP1aHeader::Dump(FILE* stream)
966 {
967   if ( stream == 0 )
968     stream = stderr;
969
970   Partition::Dump(stream);
971   m_Primer.Dump(stream);
972
973   if ( m_Preface == 0 )
974     fputs("No Preface loaded\n", stream);
975
976   std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
977   for ( ; i != m_PacketList->m_List.end(); i++ )
978     (*i)->Dump(stream);
979 }
980
981 //------------------------------------------------------------------------------------------
982 //
983
984 ASDCP::MXF::OPAtomIndexFooter::OPAtomIndexFooter(const Dictionary*& d) :
985   Partition(d), m_Dict(d),
986   m_CurrentSegment(0), m_BytesPerEditUnit(0), m_BodySID(0),
987   m_ECOffset(0), m_Lookup(0)
988 {
989   BodySID = 0;
990   IndexSID = 129;
991 }
992
993 ASDCP::MXF::OPAtomIndexFooter::~OPAtomIndexFooter() {}
994
995 //
996 ASDCP::Result_t
997 ASDCP::MXF::OPAtomIndexFooter::InitFromFile(const Kumu::FileReader& Reader)
998 {
999   Result_t result = Partition::InitFromFile(Reader); // test UL and OP
1000
1001   // slurp up the remainder of the footer
1002   ui32_t read_count = 0;
1003
1004   if ( ASDCP_SUCCESS(result) && IndexByteCount > 0 )
1005     {
1006       assert (IndexByteCount <= 0xFFFFFFFFL);
1007       // At this point, m_FooterData may not have been initialized
1008       // so it's capacity is zero and data pointer is NULL
1009       // However, if IndexByteCount is zero then the capacity
1010       // doesn't change and the data pointer is not set.
1011       result = m_FooterData.Capacity((ui32_t) IndexByteCount);
1012
1013       if ( ASDCP_SUCCESS(result) )
1014         result = Reader.Read(m_FooterData.Data(), m_FooterData.Capacity(), &read_count);
1015
1016       if ( ASDCP_SUCCESS(result) && read_count != m_FooterData.Capacity() )
1017         {
1018           DefaultLogSink().Error("Short read of footer partition: got %u, expecting %u\n",
1019                                  read_count, m_FooterData.Capacity());
1020           return RESULT_FAIL;
1021         }
1022       else if( ASDCP_SUCCESS(result) && !m_FooterData.Data() )
1023         {
1024           DefaultLogSink().Error( "Buffer for footer partition not created: IndexByteCount = %u\n",
1025                                   IndexByteCount );
1026           return RESULT_FAIL;
1027         }
1028
1029       if ( ASDCP_SUCCESS(result) )
1030         result = InitFromBuffer(m_FooterData.RoData(), m_FooterData.Capacity());
1031     }
1032
1033   return result;
1034 }
1035
1036 //
1037 ASDCP::Result_t
1038 ASDCP::MXF::OPAtomIndexFooter::InitFromPartitionBuffer(const byte_t* p, ui32_t l)
1039 {
1040   Result_t result = KLVPacket::InitFromBuffer(p, l);
1041
1042   if ( ASDCP_SUCCESS(result) )
1043     result = Partition::InitFromBuffer(m_ValueStart, m_ValueLength); // test UL and OP
1044
1045   if ( ASDCP_SUCCESS(result) )
1046     {
1047       ui32_t pp_len = KLVPacket::PacketLength();
1048       result = InitFromBuffer(p + pp_len, l - pp_len);
1049     }
1050
1051   return result;
1052 }
1053
1054 //
1055 ASDCP::Result_t
1056 ASDCP::MXF::OPAtomIndexFooter::InitFromBuffer(const byte_t* p, ui32_t l)
1057 {
1058   Result_t result = RESULT_OK;
1059   const byte_t* end_p = p + l;
1060   
1061   while ( ASDCP_SUCCESS(result) && p < end_p )
1062     {
1063       // parse the packets and index them by uid, discard KLVFill items
1064       InterchangeObject* object = CreateObject(m_Dict, p);
1065       assert(object);
1066
1067       object->m_Lookup = m_Lookup;
1068       result = object->InitFromBuffer(p, end_p - p);
1069       p += object->PacketLength();
1070
1071       if ( ASDCP_SUCCESS(result) )
1072         {
1073           m_PacketList->AddPacket(object); // takes ownership
1074         }
1075       else
1076         {
1077           DefaultLogSink().Error("Error initializing packet\n");
1078           delete object;
1079         }
1080     }
1081
1082   if ( ASDCP_FAILURE(result) )
1083     DefaultLogSink().Error("Failed to initialize OPAtomIndexFooter\n");
1084
1085   return result;
1086 }
1087
1088 //
1089 ASDCP::Result_t
1090 ASDCP::MXF::OPAtomIndexFooter::WriteToFile(Kumu::FileWriter& Writer, ui64_t duration)
1091 {
1092   assert(m_Dict);
1093   ASDCP::FrameBuffer FooterBuffer;
1094   ui32_t   footer_size = m_PacketList->m_List.size() * MaxIndexSegmentSize; // segment-count * max-segment-size
1095   Result_t result = FooterBuffer.Capacity(footer_size); 
1096   ui32_t   iseg_count = 0;
1097
1098   if ( m_CurrentSegment != 0 )
1099     {
1100       m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1101       m_CurrentSegment = 0;
1102     }
1103
1104   std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
1105   for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
1106     {
1107       IndexTableSegment *segment = dynamic_cast<IndexTableSegment*>(*pl_i);
1108
1109       if ( segment != 0 )
1110         {
1111           iseg_count++;
1112           if ( m_BytesPerEditUnit != 0 )
1113             {
1114               if ( iseg_count != 1 )
1115                 return RESULT_STATE;
1116
1117               segment->IndexDuration = duration;
1118             }
1119         }
1120
1121       InterchangeObject* object = *pl_i;
1122       object->m_Lookup = m_Lookup;
1123
1124       ASDCP::FrameBuffer WriteWrapper;
1125       WriteWrapper.SetData(FooterBuffer.Data() + FooterBuffer.Size(),
1126                            FooterBuffer.Capacity() - FooterBuffer.Size());
1127       result = object->WriteToBuffer(WriteWrapper);
1128       FooterBuffer.Size(FooterBuffer.Size() + WriteWrapper.Size());
1129     }
1130
1131   if ( ASDCP_SUCCESS(result) )
1132     {
1133       IndexByteCount = FooterBuffer.Size();
1134       UL FooterUL(m_Dict->ul(MDD_CompleteFooter));
1135       result = Partition::WriteToFile(Writer, FooterUL);
1136     }
1137
1138   if ( ASDCP_SUCCESS(result) )
1139     {
1140       ui32_t write_count = 0;
1141       result = Writer.Write(FooterBuffer.RoData(), FooterBuffer.Size(), &write_count);
1142       assert(write_count == FooterBuffer.Size());
1143     }
1144
1145   return result;
1146 }
1147
1148 //
1149 void
1150 ASDCP::MXF::OPAtomIndexFooter::Dump(FILE* stream)
1151 {
1152   if ( stream == 0 )
1153     stream = stderr;
1154
1155   Partition::Dump(stream);
1156
1157   std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
1158   for ( ; i != m_PacketList->m_List.end(); i++ )
1159     (*i)->Dump(stream);
1160 }
1161
1162 ASDCP::Result_t
1163 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
1164 {
1165   return m_PacketList->GetMDObjectByID(ObjectID, Object);
1166 }
1167
1168 //
1169 ASDCP::Result_t
1170 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
1171 {
1172   InterchangeObject* TmpObject;
1173
1174   if ( Object == 0 )
1175     Object = &TmpObject;
1176
1177   return m_PacketList->GetMDObjectByType(ObjectID, Object);
1178 }
1179
1180 //
1181 ASDCP::Result_t
1182 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
1183 {
1184   return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
1185 }
1186
1187 //
1188 ASDCP::Result_t
1189 ASDCP::MXF::OPAtomIndexFooter::Lookup(ui32_t frame_num, IndexTableSegment::IndexEntry& Entry) const
1190 {
1191   std::list<InterchangeObject*>::iterator li;
1192   for ( li = m_PacketList->m_List.begin(); li != m_PacketList->m_List.end(); li++ )
1193     {
1194       IndexTableSegment *segment = dynamic_cast<IndexTableSegment*>(*li);
1195
1196       if ( segment != 0 )
1197         {
1198           ui64_t start_pos = segment->IndexStartPosition;
1199
1200           if ( segment->EditUnitByteCount > 0 )
1201             {
1202               if ( m_PacketList->m_List.size() > 1 )
1203                 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
1204
1205               if ( ! segment->IndexEntryArray.empty() )
1206                 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
1207
1208               Entry.StreamOffset = (ui64_t)frame_num * segment->EditUnitByteCount;
1209               return RESULT_OK;
1210             }
1211           else if ( (ui64_t)frame_num >= start_pos
1212                     && (ui64_t)frame_num < (start_pos + segment->IndexDuration) )
1213             {
1214               ui64_t tmp = frame_num - start_pos;
1215               assert(tmp <= 0xFFFFFFFFL);
1216               Entry = segment->IndexEntryArray[(ui32_t) tmp];
1217               return RESULT_OK;
1218             }
1219         }
1220     }
1221
1222   return RESULT_FAIL;
1223 }
1224
1225 //
1226 void
1227 ASDCP::MXF::OPAtomIndexFooter::SetDeltaParams(const IndexTableSegment::DeltaEntry& delta)
1228 {
1229   m_DefaultDeltaEntry = delta;
1230 }
1231
1232 //
1233 void
1234 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const Rational& Rate)
1235 {
1236   assert(lookup);
1237   m_Lookup = lookup;
1238   m_BytesPerEditUnit = size;
1239   m_EditRate = Rate;
1240
1241   IndexTableSegment* Index = new IndexTableSegment(m_Dict);
1242   AddChildObject(Index);
1243   Index->EditUnitByteCount = m_BytesPerEditUnit;
1244   Index->IndexEditRate = Rate;
1245 }
1246
1247 //
1248 void
1249 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsVBR(IPrimerLookup* lookup, const Rational& Rate, Kumu::fpos_t offset)
1250 {
1251   assert(lookup);
1252   m_Lookup = lookup;
1253   m_BytesPerEditUnit = 0;
1254   m_EditRate = Rate;
1255   m_ECOffset = offset;
1256 }
1257
1258 //
1259 void
1260 ASDCP::MXF::OPAtomIndexFooter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
1261 {
1262   if ( m_BytesPerEditUnit != 0 )  // are we CBR? that's bad 
1263     {
1264       DefaultLogSink().Error("Call to PushIndexEntry() failed: index is CBR\n");
1265       return;
1266     }
1267
1268   // do we have an available segment?
1269   if ( m_CurrentSegment == 0 )
1270     { // no, set up a new segment
1271       m_CurrentSegment = new IndexTableSegment(m_Dict);
1272       assert(m_CurrentSegment);
1273       AddChildObject(m_CurrentSegment);
1274       m_CurrentSegment->DeltaEntryArray.push_back(m_DefaultDeltaEntry);
1275       m_CurrentSegment->IndexEditRate = m_EditRate;
1276       m_CurrentSegment->IndexStartPosition = 0;
1277     }
1278   else if ( m_CurrentSegment->IndexEntryArray.size() >= CBRIndexEntriesPerSegment )
1279     { // no, this one is full, start another
1280       m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1281       ui64_t StartPosition = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
1282
1283       m_CurrentSegment = new IndexTableSegment(m_Dict);
1284       assert(m_CurrentSegment);
1285       AddChildObject(m_CurrentSegment);
1286       m_CurrentSegment->DeltaEntryArray.push_back(m_DefaultDeltaEntry);
1287       m_CurrentSegment->IndexEditRate = m_EditRate;
1288       m_CurrentSegment->IndexStartPosition = StartPosition;
1289     }
1290
1291   m_CurrentSegment->IndexEntryArray.push_back(Entry);
1292 }
1293
1294 //------------------------------------------------------------------------------------------
1295 //
1296
1297 //
1298 void
1299 ASDCP::MXF::InterchangeObject::Copy(const InterchangeObject& rhs)
1300 {
1301   m_UL = rhs.m_UL;
1302   InstanceUID = rhs.InstanceUID;
1303   GenerationUID = rhs.GenerationUID;
1304 }
1305
1306 //
1307 ASDCP::Result_t
1308 ASDCP::MXF::InterchangeObject::InitFromTLVSet(TLVReader& TLVSet)
1309 {
1310   Result_t result = TLVSet.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID));
1311   if ( ASDCP_SUCCESS(result) )
1312     result = TLVSet.ReadObject(OBJ_READ_ARGS_OPT(GenerationInterchangeObject, GenerationUID));
1313   return result;
1314 }
1315
1316 //
1317 ASDCP::Result_t
1318 ASDCP::MXF::InterchangeObject::WriteToTLVSet(TLVWriter& TLVSet)
1319 {
1320   Result_t result = TLVSet.WriteObject(OBJ_WRITE_ARGS(InterchangeObject, InstanceUID));
1321   if ( ASDCP_SUCCESS(result) )
1322     result = TLVSet.WriteObject(OBJ_WRITE_ARGS_OPT(GenerationInterchangeObject, GenerationUID));
1323   return result;
1324 }
1325
1326 //
1327 ASDCP::Result_t
1328 ASDCP::MXF::InterchangeObject::InitFromBuffer(const byte_t* p, ui32_t l)
1329 {
1330   ASDCP_TEST_NULL(p);
1331   Result_t result = RESULT_FALSE;
1332
1333   if ( m_UL.HasValue() )
1334     {
1335       result = KLVPacket::InitFromBuffer(p, l, m_UL);
1336
1337       if ( ASDCP_SUCCESS(result) )
1338         {
1339           TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup);
1340           result = InitFromTLVSet(MemRDR);
1341         }
1342     }
1343   else
1344     {
1345       result = KLVPacket::InitFromBuffer(p, l);
1346     }
1347   
1348   return result;
1349 }
1350
1351 //
1352 ASDCP::Result_t
1353 ASDCP::MXF::InterchangeObject::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
1354 {
1355   if ( ! m_UL.HasValue() )
1356     return RESULT_STATE;
1357
1358   TLVWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length, m_Lookup);
1359   Result_t result = WriteToTLVSet(MemWRT);
1360
1361   if ( ASDCP_SUCCESS(result) )
1362     {
1363       ui32_t packet_length = MemWRT.Length();
1364       result = WriteKLToBuffer(Buffer, packet_length);
1365
1366       if ( ASDCP_SUCCESS(result) )
1367         Buffer.Size(Buffer.Size() + packet_length);
1368     }
1369
1370   return result;
1371 }
1372
1373 //
1374 void
1375 ASDCP::MXF::InterchangeObject::Dump(FILE* stream)
1376 {
1377   char identbuf[IdentBufferLen];
1378
1379   fputc('\n', stream);
1380   KLVPacket::Dump(stream, *m_Dict, false);
1381   fprintf(stream, "             InstanceUID = %s\n",  InstanceUID.EncodeHex(identbuf, IdentBufferLen));
1382
1383   if ( ! GenerationUID.empty() )
1384     fprintf(stream, "           GenerationUID = %s\n",  GenerationUID.get().EncodeHex(identbuf, IdentBufferLen));
1385 }
1386
1387 //
1388 bool
1389 ASDCP::MXF::InterchangeObject::IsA(const byte_t* label)
1390 {
1391   if ( m_KLLength == 0 || m_KeyStart == 0 )
1392     return false;
1393
1394   return ( memcmp(label, m_KeyStart, SMPTE_UL_LENGTH) == 0 );
1395 }
1396
1397
1398 //------------------------------------------------------------------------------------------
1399
1400
1401 typedef std::map<ASDCP::UL, ASDCP::MXF::MXFObjectFactory_t>FactoryMap_t;
1402 typedef FactoryMap_t::iterator FLi_t;
1403
1404 //
1405 class FactoryList : public FactoryMap_t
1406 {
1407   Kumu::Mutex m_Lock;
1408
1409 public:
1410   FactoryList() {}
1411   ~FactoryList() {}
1412
1413   bool Empty() {
1414     Kumu::AutoMutex BlockLock(m_Lock);
1415     return empty();
1416   }
1417
1418   FLi_t Find(const byte_t* label) {
1419     Kumu::AutoMutex BlockLock(m_Lock);
1420     return find(label);
1421   }
1422
1423   FLi_t End() {
1424     Kumu::AutoMutex BlockLock(m_Lock);
1425     return end();
1426   }
1427
1428   void Insert(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory) {
1429     Kumu::AutoMutex BlockLock(m_Lock);
1430     insert(FactoryList::value_type(label, factory));
1431   }
1432 };
1433
1434 //
1435 static FactoryList s_FactoryList;
1436 static Kumu::Mutex s_InitLock;
1437 static bool        s_TypesInit = false;
1438
1439
1440 //
1441 void
1442 ASDCP::MXF::SetObjectFactory(const ASDCP::UL& label, ASDCP::MXF::MXFObjectFactory_t factory)
1443 {
1444   s_FactoryList.Insert(label, factory);
1445 }
1446
1447 //
1448 ASDCP::MXF::InterchangeObject*
1449 ASDCP::MXF::CreateObject(const Dictionary*& Dict, const UL& label)
1450 {
1451   if ( ! s_TypesInit )
1452     {
1453       Kumu::AutoMutex BlockLock(s_InitLock);
1454
1455       if ( ! s_TypesInit )
1456         {
1457           MXF::Metadata_InitTypes(Dict);
1458           s_TypesInit = true;
1459         }
1460     }
1461
1462   FLi_t i = s_FactoryList.find(label.Value());
1463
1464   if ( i == s_FactoryList.end() )
1465     return new InterchangeObject(Dict);
1466
1467   return i->second(Dict);
1468 }
1469
1470
1471 //------------------------------------------------------------------------------------------
1472
1473 //
1474 bool
1475 ASDCP::MXF::decode_mca_string(const std::string& s, const mca_label_map_t& labels, const Dictionary*& dict, const std::string& language,
1476                               InterchangeObject_list_t& descriptor_list, ui32_t& channel_count)
1477 {
1478   std::string symbol_buf;
1479   channel_count = 0;
1480   ASDCP::MXF::SoundfieldGroupLabelSubDescriptor *current_soundfield = 0;
1481   std::string::const_iterator i;
1482
1483   for ( i = s.begin(); i != s.end(); ++i )
1484     {
1485       if ( *i == '(' )
1486         {
1487           if ( current_soundfield != 0 )
1488             {
1489               DefaultLogSink().Error("Encountered '(', already processing a soundfield group.\n");
1490               return false;
1491             }
1492
1493           if ( symbol_buf.empty() )
1494             {
1495               DefaultLogSink().Error("Encountered '(', without leading soundfield group symbol.\n");
1496               return false;
1497             }
1498
1499           mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1500       
1501           if ( i == labels.end() )
1502             {
1503               DefaultLogSink().Error("Unknown symbol: '%s'\n", symbol_buf.c_str());
1504               return false;
1505             }
1506       
1507           if ( i->second.Value()[10] != 2 ) // magic depends on UL "Essence Facet" byte (see ST 428-12)
1508             {
1509               DefaultLogSink().Error("Not a soundfield group symbol: '%s'\n", symbol_buf.c_str());
1510               return false;
1511             }
1512
1513           current_soundfield = new ASDCP::MXF::SoundfieldGroupLabelSubDescriptor(dict);
1514
1515           GenRandomValue(current_soundfield->InstanceUID);
1516           GenRandomValue(current_soundfield->MCALinkID);
1517           current_soundfield->MCATagSymbol = "sg" + i->first;
1518           current_soundfield->MCATagName = i->first;
1519           current_soundfield->RFC5646SpokenLanguage = language;
1520           current_soundfield->MCALabelDictionaryID = i->second;
1521           descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(current_soundfield));
1522           symbol_buf.clear();
1523         }
1524       else if ( *i == ')' )
1525         {
1526           if ( current_soundfield == 0 )
1527             {
1528               DefaultLogSink().Error("Encountered ')', not currently processing a soundfield group.\n");
1529               return false;
1530             }
1531
1532           if ( symbol_buf.empty() )
1533             {
1534               DefaultLogSink().Error("Soundfield group description contains no channels.\n");
1535               return false;
1536             }
1537
1538           mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1539       
1540           if ( i == labels.end() )
1541             {
1542               DefaultLogSink().Error("Unknown symbol: '%s'\n", symbol_buf.c_str());
1543               return false;
1544             }
1545
1546           ASDCP::MXF::AudioChannelLabelSubDescriptor *channel_descr =
1547             new ASDCP::MXF::AudioChannelLabelSubDescriptor(dict);
1548
1549           GenRandomValue(channel_descr->InstanceUID);
1550           assert(current_soundfield);
1551           channel_descr->SoundfieldGroupLinkID = current_soundfield->MCALinkID;
1552           channel_descr->MCAChannelID = channel_count++;
1553           channel_descr->MCATagSymbol = "ch" + i->first;
1554           channel_descr->MCATagName = i->first;
1555           channel_descr->RFC5646SpokenLanguage = language;
1556           channel_descr->MCALabelDictionaryID = i->second;
1557           descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(channel_descr));
1558           symbol_buf.clear();
1559           current_soundfield = 0;
1560         }
1561       else if ( *i == ',' )
1562         {
1563           if ( ! symbol_buf.empty() )
1564             {
1565               mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1566
1567               if ( i == labels.end() )
1568                 {
1569                   DefaultLogSink().Error("Unknown symbol: '%s'\n", symbol_buf.c_str());
1570                   return false;
1571                 }
1572
1573               if ( i->second.Value()[10] != 1 ) // magic depends on UL "Essence Facet" byte (see ST 428-12)
1574                 {
1575                   DefaultLogSink().Error("Not a channel symbol: '%s'\n", symbol_buf.c_str());
1576                   return false;
1577                 }
1578
1579               ASDCP::MXF::AudioChannelLabelSubDescriptor *channel_descr =
1580                 new ASDCP::MXF::AudioChannelLabelSubDescriptor(dict);
1581
1582               GenRandomValue(channel_descr->InstanceUID);
1583
1584               if ( current_soundfield != 0 )
1585                 {
1586                   channel_descr->SoundfieldGroupLinkID = current_soundfield->MCALinkID;
1587                 }
1588
1589               channel_descr->MCAChannelID = channel_count++;
1590               channel_descr->MCATagSymbol = "ch" + i->first;
1591               channel_descr->MCATagName = i->first;
1592               channel_descr->RFC5646SpokenLanguage = language;
1593               channel_descr->MCALabelDictionaryID = i->second;
1594               descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(channel_descr));
1595               symbol_buf.clear();
1596             }
1597         }
1598       else if ( isalnum(*i) )
1599         {
1600           symbol_buf += *i;
1601         }
1602       else if ( ! isspace(*i) )
1603         {
1604           DefaultLogSink().Error("Unexpected character '%c'.\n", *i);
1605           return false;
1606         }
1607     }
1608
1609   if ( ! symbol_buf.empty() )
1610     {
1611       mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1612       
1613       if ( i == labels.end() )
1614         {
1615           DefaultLogSink().Error("Unknown symbol: '%s'\n", symbol_buf.c_str());
1616           return false;
1617         }
1618
1619       ASDCP::MXF::AudioChannelLabelSubDescriptor *channel_descr =
1620         new ASDCP::MXF::AudioChannelLabelSubDescriptor(dict);
1621
1622       GenRandomValue(channel_descr->InstanceUID);
1623
1624       if ( current_soundfield != 0 )
1625         {
1626           channel_descr->SoundfieldGroupLinkID = current_soundfield->MCALinkID;
1627         }
1628
1629       channel_descr->MCAChannelID = channel_count++;
1630       channel_descr->MCATagSymbol = "ch" + i->first;
1631       channel_descr->MCATagName = i->first;
1632       channel_descr->RFC5646SpokenLanguage = language;
1633       channel_descr->MCALabelDictionaryID = i->second;
1634       descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(channel_descr));
1635     }
1636
1637   return true;
1638 }
1639
1640 //
1641 ASDCP::MXF::ASDCP_MCAConfigParser::ASDCP_MCAConfigParser(const Dictionary*& d) : m_Dict(d), m_ChannelCount(0)
1642 {
1643   m_LabelMap.insert(mca_label_map_t::value_type("L", m_Dict->ul(MDD_DCAudioChannel_L)));
1644   m_LabelMap.insert(mca_label_map_t::value_type("R", m_Dict->ul(MDD_DCAudioChannel_R)));
1645   m_LabelMap.insert(mca_label_map_t::value_type("C", m_Dict->ul(MDD_DCAudioChannel_C)));
1646   m_LabelMap.insert(mca_label_map_t::value_type("LFE", m_Dict->ul(MDD_DCAudioChannel_LFE)));
1647   m_LabelMap.insert(mca_label_map_t::value_type("Ls", m_Dict->ul(MDD_DCAudioChannel_Ls)));
1648   m_LabelMap.insert(mca_label_map_t::value_type("Rs", m_Dict->ul(MDD_DCAudioChannel_Rs)));
1649   m_LabelMap.insert(mca_label_map_t::value_type("Lss", m_Dict->ul(MDD_DCAudioChannel_Lss)));
1650   m_LabelMap.insert(mca_label_map_t::value_type("Rss", m_Dict->ul(MDD_DCAudioChannel_Rss)));
1651   m_LabelMap.insert(mca_label_map_t::value_type("Lrs", m_Dict->ul(MDD_DCAudioChannel_Lrs)));
1652   m_LabelMap.insert(mca_label_map_t::value_type("Rrs", m_Dict->ul(MDD_DCAudioChannel_Rrs)));
1653   m_LabelMap.insert(mca_label_map_t::value_type("Lc", m_Dict->ul(MDD_DCAudioChannel_Lc)));
1654   m_LabelMap.insert(mca_label_map_t::value_type("Rc", m_Dict->ul(MDD_DCAudioChannel_Rc)));
1655   m_LabelMap.insert(mca_label_map_t::value_type("Cs", m_Dict->ul(MDD_DCAudioChannel_Cs)));
1656   m_LabelMap.insert(mca_label_map_t::value_type("HI", m_Dict->ul(MDD_DCAudioChannel_HI)));
1657   m_LabelMap.insert(mca_label_map_t::value_type("VIN", m_Dict->ul(MDD_DCAudioChannel_VIN)));
1658   m_LabelMap.insert(mca_label_map_t::value_type("51", m_Dict->ul(MDD_DCAudioSoundfield_51)));
1659   m_LabelMap.insert(mca_label_map_t::value_type("71", m_Dict->ul(MDD_DCAudioSoundfield_71)));
1660   m_LabelMap.insert(mca_label_map_t::value_type("SDS", m_Dict->ul(MDD_DCAudioSoundfield_SDS)));
1661   m_LabelMap.insert(mca_label_map_t::value_type("61", m_Dict->ul(MDD_DCAudioSoundfield_61)));
1662   m_LabelMap.insert(mca_label_map_t::value_type("M", m_Dict->ul(MDD_DCAudioSoundfield_M)));
1663 }
1664
1665 //
1666 ui32_t
1667 ASDCP::MXF::ASDCP_MCAConfigParser::ChannelCount() const
1668 {
1669   return m_ChannelCount;
1670 }
1671
1672 // 51(L,R,C,LFE,Ls,Rs),HI,VIN
1673 bool
1674 ASDCP::MXF::ASDCP_MCAConfigParser::DecodeString(const std::string& s, const std::string& language)
1675 {
1676   return decode_mca_string(s, m_LabelMap, m_Dict, language, *this, m_ChannelCount);
1677 }
1678
1679
1680
1681 ASDCP::MXF::AS02_MCAConfigParser::AS02_MCAConfigParser(const Dictionary*& d) : ASDCP::MXF::ASDCP_MCAConfigParser(d)
1682 {
1683   m_LabelMap.insert(mca_label_map_t::value_type("M1", m_Dict->ul(MDD_IMFAudioChannel_M1)));
1684   m_LabelMap.insert(mca_label_map_t::value_type("M2", m_Dict->ul(MDD_IMFAudioChannel_M2)));
1685   m_LabelMap.insert(mca_label_map_t::value_type("Lt", m_Dict->ul(MDD_IMFAudioChannel_Lt)));
1686   m_LabelMap.insert(mca_label_map_t::value_type("Rt", m_Dict->ul(MDD_IMFAudioChannel_Rt)));
1687   m_LabelMap.insert(mca_label_map_t::value_type("Lst", m_Dict->ul(MDD_IMFAudioChannel_Lst)));
1688   m_LabelMap.insert(mca_label_map_t::value_type("Rst", m_Dict->ul(MDD_IMFAudioChannel_Rst)));
1689   m_LabelMap.insert(mca_label_map_t::value_type("S", m_Dict->ul(MDD_IMFAudioChannel_S)));
1690   m_LabelMap.insert(mca_label_map_t::value_type("ST", m_Dict->ul(MDD_IMFAudioSoundfield_ST)));
1691   m_LabelMap.insert(mca_label_map_t::value_type("DM", m_Dict->ul(MDD_IMFAudioSoundfield_DM)));
1692   m_LabelMap.insert(mca_label_map_t::value_type("DNS", m_Dict->ul(MDD_IMFAudioSoundfield_DNS)));
1693   m_LabelMap.insert(mca_label_map_t::value_type("30", m_Dict->ul(MDD_IMFAudioSoundfield_30)));
1694   m_LabelMap.insert(mca_label_map_t::value_type("40", m_Dict->ul(MDD_IMFAudioSoundfield_40)));
1695   m_LabelMap.insert(mca_label_map_t::value_type("50", m_Dict->ul(MDD_IMFAudioSoundfield_50)));
1696   m_LabelMap.insert(mca_label_map_t::value_type("60", m_Dict->ul(MDD_IMFAudioSoundfield_60)));
1697   m_LabelMap.insert(mca_label_map_t::value_type("70", m_Dict->ul(MDD_IMFAudioSoundfield_70)));
1698   m_LabelMap.insert(mca_label_map_t::value_type("LtRt", m_Dict->ul(MDD_IMFAudioSoundfield_LtRt)));
1699   m_LabelMap.insert(mca_label_map_t::value_type("51Ex", m_Dict->ul(MDD_IMFAudioSoundfield_51Ex)));
1700   m_LabelMap.insert(mca_label_map_t::value_type("HI", m_Dict->ul(MDD_IMFAudioSoundfield_HI)));
1701   m_LabelMap.insert(mca_label_map_t::value_type("VIN", m_Dict->ul(MDD_IMFAudioSoundfield_VIN)));
1702 }
1703
1704 //
1705 // end MXF.cpp
1706 //