o Added preliminary support for timed-text wrapping for AS-02. This
[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 = 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     DefaultLogSink().Warn("Improbably small HeaderByteCount value: %u\n", HeaderByteCount);
718
719   assert (HeaderByteCount <= 0xFFFFFFFFL);
720   result = m_HeaderData.Capacity((ui32_t)HeaderByteCount);
721
722   if ( ASDCP_SUCCESS(result) )
723     {
724       ui32_t read_count;
725       result = Reader.Read(m_HeaderData.Data(), m_HeaderData.Capacity(), &read_count);
726
727       if ( ASDCP_FAILURE(result) )
728         {
729           DefaultLogSink().Error("OP1aHeader::InitFromFile, Read failed\n");
730           return result;
731         }
732
733       if ( read_count != m_HeaderData.Capacity() )
734         {
735           DefaultLogSink().Error("Short read of OP-Atom header metadata; wanted %u, got %u\n",
736                                  m_HeaderData.Capacity(), read_count);
737           return RESULT_KLV_CODING;
738         }
739     }
740
741   if ( ASDCP_SUCCESS(result) )
742     result = InitFromBuffer(m_HeaderData.RoData(), m_HeaderData.Capacity());
743
744   return result;
745 }
746
747 //
748 ASDCP::Result_t
749 ASDCP::MXF::OP1aHeader::InitFromPartitionBuffer(const byte_t* p, ui32_t l)
750 {
751   Result_t result = KLVPacket::InitFromBuffer(p, l);
752
753   if ( ASDCP_SUCCESS(result) )
754     result = Partition::InitFromBuffer(m_ValueStart, m_ValueLength); // test UL and OP
755
756   if ( ASDCP_SUCCESS(result) )
757     {
758       ui32_t pp_len = KLVPacket::PacketLength();
759       result = InitFromBuffer(p + pp_len, l - pp_len);
760     }
761
762   return result;
763 }
764
765 //
766 ASDCP::Result_t
767 ASDCP::MXF::OP1aHeader::InitFromBuffer(const byte_t* p, ui32_t l)
768 {
769   assert(m_Dict);
770   Result_t result = RESULT_OK;
771   const byte_t* end_p = p + l;
772
773   while ( ASDCP_SUCCESS(result) && p < end_p )
774     {
775       // parse the packets and index them by uid, discard KLVFill items
776       InterchangeObject* object = CreateObject(m_Dict, p);
777       assert(object);
778
779       object->m_Lookup = &m_Primer;
780       result = object->InitFromBuffer(p, end_p - p);
781       const byte_t* redo_p = p;
782       p += object->PacketLength();
783       //      hexdump(p, object->PacketLength());
784
785       if ( ASDCP_SUCCESS(result) )
786         {
787           if ( object->IsA(m_Dict->ul(MDD_KLVFill)) )
788             {
789               delete object;
790
791               if ( p > end_p )
792                 {
793                   DefaultLogSink().Error("Fill item short read: %d.\n", p - end_p);
794                 }
795             }
796           else if ( object->IsA(m_Dict->ul(MDD_Primer)) ) // TODO: only one primer should be found
797             {
798               delete object;
799               result = m_Primer.InitFromBuffer(redo_p, end_p - redo_p);
800             }
801           else
802             {
803               m_PacketList->AddPacket(object); // takes ownership
804
805               if ( object->IsA(m_Dict->ul(MDD_Preface)) && m_Preface == 0 )
806                 m_Preface = (Preface*)object;
807             }
808         }
809       else
810         {
811           DefaultLogSink().Error("Error initializing packet\n");
812           delete object;
813         }
814     }
815
816   return result;
817 }
818
819 ASDCP::Result_t
820 ASDCP::MXF::OP1aHeader::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
821 {
822   return m_PacketList->GetMDObjectByID(ObjectID, Object);
823 }
824
825 //
826 ASDCP::Result_t
827 ASDCP::MXF::OP1aHeader::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
828 {
829   InterchangeObject* TmpObject;
830
831   if ( Object == 0 )
832     Object = &TmpObject;
833
834   return m_PacketList->GetMDObjectByType(ObjectID, Object);
835 }
836
837 //
838 ASDCP::Result_t
839 ASDCP::MXF::OP1aHeader::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
840 {
841   return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
842 }
843
844 //
845 ASDCP::MXF::Identification*
846 ASDCP::MXF::OP1aHeader::GetIdentification()
847 {
848   InterchangeObject* Object;
849
850   if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object)) )
851     return (Identification*)Object;
852
853   return 0;
854 }
855
856 //
857 ASDCP::MXF::SourcePackage*
858 ASDCP::MXF::OP1aHeader::GetSourcePackage()
859 {
860   InterchangeObject* Object;
861
862   if ( ASDCP_SUCCESS(GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object)) )
863     return (SourcePackage*)Object;
864
865   return 0;
866 }
867
868 //
869 ASDCP::Result_t
870 ASDCP::MXF::OP1aHeader::WriteToFile(Kumu::FileWriter& Writer, ui32_t HeaderSize)
871 {
872   assert(m_Dict);
873   if ( m_Preface == 0 )
874     return RESULT_STATE;
875
876   if ( HeaderSize < 4096 ) 
877     {
878       DefaultLogSink().Error("HeaderSize %u is too small. Must be >= 4096\n", HeaderSize);
879       return RESULT_FAIL;
880     }
881
882   ASDCP::FrameBuffer HeaderBuffer;
883   HeaderByteCount = HeaderSize - ArchiveSize();
884   assert (HeaderByteCount <= 0xFFFFFFFFL);
885   Result_t result = HeaderBuffer.Capacity((ui32_t) HeaderByteCount); 
886   m_Preface->m_Lookup = &m_Primer;
887
888   std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
889   for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
890     {
891       InterchangeObject* object = *pl_i;
892       object->m_Lookup = &m_Primer;
893
894       ASDCP::FrameBuffer WriteWrapper;
895       WriteWrapper.SetData(HeaderBuffer.Data() + HeaderBuffer.Size(),
896                            HeaderBuffer.Capacity() - HeaderBuffer.Size());
897       result = object->WriteToBuffer(WriteWrapper);
898       HeaderBuffer.Size(HeaderBuffer.Size() + WriteWrapper.Size());
899     }
900
901   if ( ASDCP_SUCCESS(result) )
902     {
903       UL TmpUL(m_Dict->ul(MDD_ClosedCompleteHeader));
904       result = Partition::WriteToFile(Writer, TmpUL);
905     }
906
907   if ( ASDCP_SUCCESS(result) )
908     result = m_Primer.WriteToFile(Writer);
909
910   if ( ASDCP_SUCCESS(result) )
911     {
912       ui32_t write_count;
913       Writer.Write(HeaderBuffer.RoData(), HeaderBuffer.Size(), &write_count);
914       assert(write_count == HeaderBuffer.Size());
915     }
916
917   // KLV Fill
918   if ( ASDCP_SUCCESS(result) )
919     {
920       Kumu::fpos_t pos = Writer.Tell();
921
922       if ( pos > (Kumu::fpos_t)HeaderByteCount )
923         {
924           char intbuf[IntBufferLen];
925           DefaultLogSink().Error("Header size %s exceeds specified value %u\n",
926                                  ui64sz(pos, intbuf),
927                                  HeaderSize);
928           return RESULT_FAIL;
929         }
930
931       ASDCP::FrameBuffer NilBuf;
932       ui32_t klv_fill_length = HeaderSize - (ui32_t)pos;
933
934       if ( klv_fill_length < kl_length )
935         {
936           DefaultLogSink().Error("Remaining region too small for KLV Fill header\n");
937           return RESULT_FAIL;
938         }
939
940       klv_fill_length -= kl_length;
941       result = WriteKLToFile(Writer, m_Dict->ul(MDD_KLVFill), klv_fill_length);
942
943       if ( ASDCP_SUCCESS(result) )
944         result = NilBuf.Capacity(klv_fill_length);
945
946       if ( ASDCP_SUCCESS(result) )
947         {
948           memset(NilBuf.Data(), 0, klv_fill_length);
949           ui32_t write_count;
950           Writer.Write(NilBuf.RoData(), klv_fill_length, &write_count);
951           assert(write_count == klv_fill_length);
952         }
953     }
954
955   return result;
956 }
957
958 //
959 void
960 ASDCP::MXF::OP1aHeader::Dump(FILE* stream)
961 {
962   if ( stream == 0 )
963     stream = stderr;
964
965   Partition::Dump(stream);
966   m_Primer.Dump(stream);
967
968   if ( m_Preface == 0 )
969     fputs("No Preface loaded\n", stream);
970
971   std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
972   for ( ; i != m_PacketList->m_List.end(); i++ )
973     (*i)->Dump(stream);
974 }
975
976 //------------------------------------------------------------------------------------------
977 //
978
979 ASDCP::MXF::OPAtomIndexFooter::OPAtomIndexFooter(const Dictionary*& d) :
980   Partition(d), m_Dict(d),
981   m_CurrentSegment(0), m_BytesPerEditUnit(0), m_BodySID(0),
982   m_ECOffset(0), m_Lookup(0)
983 {
984   BodySID = 0;
985   IndexSID = 129;
986 }
987
988 ASDCP::MXF::OPAtomIndexFooter::~OPAtomIndexFooter() {}
989
990 //
991 ASDCP::Result_t
992 ASDCP::MXF::OPAtomIndexFooter::InitFromFile(const Kumu::FileReader& Reader)
993 {
994   Result_t result = Partition::InitFromFile(Reader); // test UL and OP
995
996   // slurp up the remainder of the footer
997   ui32_t read_count = 0;
998
999   if ( ASDCP_SUCCESS(result) && IndexByteCount > 0 )
1000     {
1001       assert (IndexByteCount <= 0xFFFFFFFFL);
1002       // At this point, m_FooterData may not have been initialized
1003       // so it's capacity is zero and data pointer is NULL
1004       // However, if IndexByteCount is zero then the capacity
1005       // doesn't change and the data pointer is not set.
1006       result = m_FooterData.Capacity((ui32_t) IndexByteCount);
1007
1008       if ( ASDCP_SUCCESS(result) )
1009         result = Reader.Read(m_FooterData.Data(), m_FooterData.Capacity(), &read_count);
1010
1011       if ( ASDCP_SUCCESS(result) && read_count != m_FooterData.Capacity() )
1012         {
1013           DefaultLogSink().Error("Short read of footer partition: got %u, expecting %u\n",
1014                                  read_count, m_FooterData.Capacity());
1015           return RESULT_FAIL;
1016         }
1017       else if( ASDCP_SUCCESS(result) && !m_FooterData.Data() )
1018         {
1019           DefaultLogSink().Error( "Buffer for footer partition not created: IndexByteCount = %u\n",
1020                                   IndexByteCount );
1021           return RESULT_FAIL;
1022         }
1023
1024       if ( ASDCP_SUCCESS(result) )
1025         result = InitFromBuffer(m_FooterData.RoData(), m_FooterData.Capacity());
1026     }
1027
1028   return result;
1029 }
1030
1031 //
1032 ASDCP::Result_t
1033 ASDCP::MXF::OPAtomIndexFooter::InitFromPartitionBuffer(const byte_t* p, ui32_t l)
1034 {
1035   Result_t result = KLVPacket::InitFromBuffer(p, l);
1036
1037   if ( ASDCP_SUCCESS(result) )
1038     result = Partition::InitFromBuffer(m_ValueStart, m_ValueLength); // test UL and OP
1039
1040   if ( ASDCP_SUCCESS(result) )
1041     {
1042       ui32_t pp_len = KLVPacket::PacketLength();
1043       result = InitFromBuffer(p + pp_len, l - pp_len);
1044     }
1045
1046   return result;
1047 }
1048
1049 //
1050 ASDCP::Result_t
1051 ASDCP::MXF::OPAtomIndexFooter::InitFromBuffer(const byte_t* p, ui32_t l)
1052 {
1053   Result_t result = RESULT_OK;
1054   const byte_t* end_p = p + l;
1055   
1056   while ( ASDCP_SUCCESS(result) && p < end_p )
1057     {
1058       // parse the packets and index them by uid, discard KLVFill items
1059       InterchangeObject* object = CreateObject(m_Dict, p);
1060       assert(object);
1061
1062       object->m_Lookup = m_Lookup;
1063       result = object->InitFromBuffer(p, end_p - p);
1064       p += object->PacketLength();
1065
1066       if ( ASDCP_SUCCESS(result) )
1067         {
1068           m_PacketList->AddPacket(object); // takes ownership
1069         }
1070       else
1071         {
1072           DefaultLogSink().Error("Error initializing packet\n");
1073           delete object;
1074         }
1075     }
1076
1077   if ( ASDCP_FAILURE(result) )
1078     DefaultLogSink().Error("Failed to initialize OPAtomIndexFooter\n");
1079
1080   return result;
1081 }
1082
1083 //
1084 ASDCP::Result_t
1085 ASDCP::MXF::OPAtomIndexFooter::WriteToFile(Kumu::FileWriter& Writer, ui64_t duration)
1086 {
1087   assert(m_Dict);
1088   ASDCP::FrameBuffer FooterBuffer;
1089   ui32_t   footer_size = m_PacketList->m_List.size() * MaxIndexSegmentSize; // segment-count * max-segment-size
1090   Result_t result = FooterBuffer.Capacity(footer_size); 
1091   ui32_t   iseg_count = 0;
1092
1093   if ( m_CurrentSegment != 0 )
1094     {
1095       m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1096       m_CurrentSegment = 0;
1097     }
1098
1099   std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
1100   for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
1101     {
1102       IndexTableSegment *segment = dynamic_cast<IndexTableSegment*>(*pl_i);
1103
1104       if ( segment != 0 )
1105         {
1106           iseg_count++;
1107           if ( m_BytesPerEditUnit != 0 )
1108             {
1109               if ( iseg_count != 1 )
1110                 return RESULT_STATE;
1111
1112               segment->IndexDuration = duration;
1113             }
1114         }
1115
1116       InterchangeObject* object = *pl_i;
1117       object->m_Lookup = m_Lookup;
1118
1119       ASDCP::FrameBuffer WriteWrapper;
1120       WriteWrapper.SetData(FooterBuffer.Data() + FooterBuffer.Size(),
1121                            FooterBuffer.Capacity() - FooterBuffer.Size());
1122       result = object->WriteToBuffer(WriteWrapper);
1123       FooterBuffer.Size(FooterBuffer.Size() + WriteWrapper.Size());
1124     }
1125
1126   if ( ASDCP_SUCCESS(result) )
1127     {
1128       IndexByteCount = FooterBuffer.Size();
1129       UL FooterUL(m_Dict->ul(MDD_CompleteFooter));
1130       result = Partition::WriteToFile(Writer, FooterUL);
1131     }
1132
1133   if ( ASDCP_SUCCESS(result) )
1134     {
1135       ui32_t write_count = 0;
1136       result = Writer.Write(FooterBuffer.RoData(), FooterBuffer.Size(), &write_count);
1137       assert(write_count == FooterBuffer.Size());
1138     }
1139
1140   return result;
1141 }
1142
1143 //
1144 void
1145 ASDCP::MXF::OPAtomIndexFooter::Dump(FILE* stream)
1146 {
1147   if ( stream == 0 )
1148     stream = stderr;
1149
1150   Partition::Dump(stream);
1151
1152   std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
1153   for ( ; i != m_PacketList->m_List.end(); i++ )
1154     (*i)->Dump(stream);
1155 }
1156
1157 ASDCP::Result_t
1158 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectByID(const UUID& ObjectID, InterchangeObject** Object)
1159 {
1160   return m_PacketList->GetMDObjectByID(ObjectID, Object);
1161 }
1162
1163 //
1164 ASDCP::Result_t
1165 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectByType(const byte_t* ObjectID, InterchangeObject** Object)
1166 {
1167   InterchangeObject* TmpObject;
1168
1169   if ( Object == 0 )
1170     Object = &TmpObject;
1171
1172   return m_PacketList->GetMDObjectByType(ObjectID, Object);
1173 }
1174
1175 //
1176 ASDCP::Result_t
1177 ASDCP::MXF::OPAtomIndexFooter::GetMDObjectsByType(const byte_t* ObjectID, std::list<InterchangeObject*>& ObjectList)
1178 {
1179   return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
1180 }
1181
1182 //
1183 ASDCP::Result_t
1184 ASDCP::MXF::OPAtomIndexFooter::Lookup(ui32_t frame_num, IndexTableSegment::IndexEntry& Entry) const
1185 {
1186   std::list<InterchangeObject*>::iterator li;
1187   for ( li = m_PacketList->m_List.begin(); li != m_PacketList->m_List.end(); li++ )
1188     {
1189       IndexTableSegment *segment = dynamic_cast<IndexTableSegment*>(*li);
1190
1191       if ( segment != 0 )
1192         {
1193           ui64_t start_pos = segment->IndexStartPosition;
1194
1195           if ( segment->EditUnitByteCount > 0 )
1196             {
1197               if ( m_PacketList->m_List.size() > 1 )
1198                 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
1199
1200               if ( ! segment->IndexEntryArray.empty() )
1201                 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
1202
1203               Entry.StreamOffset = (ui64_t)frame_num * segment->EditUnitByteCount;
1204               return RESULT_OK;
1205             }
1206           else if ( (ui64_t)frame_num >= start_pos
1207                     && (ui64_t)frame_num < (start_pos + segment->IndexDuration) )
1208             {
1209               ui64_t tmp = frame_num - start_pos;
1210               assert(tmp <= 0xFFFFFFFFL);
1211               Entry = segment->IndexEntryArray[(ui32_t) tmp];
1212               return RESULT_OK;
1213             }
1214         }
1215     }
1216
1217   return RESULT_FAIL;
1218 }
1219
1220 //
1221 void
1222 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const Rational& Rate)
1223 {
1224   assert(lookup);
1225   m_Lookup = lookup;
1226   m_BytesPerEditUnit = size;
1227   m_EditRate = Rate;
1228
1229   IndexTableSegment* Index = new IndexTableSegment(m_Dict);
1230   AddChildObject(Index);
1231   Index->EditUnitByteCount = m_BytesPerEditUnit;
1232   Index->IndexEditRate = Rate;
1233 }
1234
1235 //
1236 void
1237 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsVBR(IPrimerLookup* lookup, const Rational& Rate, Kumu::fpos_t offset)
1238 {
1239   assert(lookup);
1240   m_Lookup = lookup;
1241   m_BytesPerEditUnit = 0;
1242   m_EditRate = Rate;
1243   m_ECOffset = offset;
1244 }
1245
1246 //
1247 void
1248 ASDCP::MXF::OPAtomIndexFooter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
1249 {
1250   if ( m_BytesPerEditUnit != 0 )  // are we CBR? that's bad 
1251     {
1252       DefaultLogSink().Error("Call to PushIndexEntry() failed: index is CBR\n");
1253       return;
1254     }
1255
1256   // do we have an available segment?
1257   if ( m_CurrentSegment == 0 )
1258     { // no, set up a new segment
1259       m_CurrentSegment = new IndexTableSegment(m_Dict);
1260       assert(m_CurrentSegment);
1261       AddChildObject(m_CurrentSegment);
1262       m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1263       m_CurrentSegment->IndexEditRate = m_EditRate;
1264       m_CurrentSegment->IndexStartPosition = 0;
1265     }
1266   else if ( m_CurrentSegment->IndexEntryArray.size() >= CBRIndexEntriesPerSegment )
1267     { // no, this one is full, start another
1268       m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1269       ui64_t StartPosition = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
1270
1271       m_CurrentSegment = new IndexTableSegment(m_Dict);
1272       assert(m_CurrentSegment);
1273       AddChildObject(m_CurrentSegment);
1274       m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1275       m_CurrentSegment->IndexEditRate = m_EditRate;
1276       m_CurrentSegment->IndexStartPosition = StartPosition;
1277     }
1278
1279   m_CurrentSegment->IndexEntryArray.push_back(Entry);
1280 }
1281
1282 //------------------------------------------------------------------------------------------
1283 //
1284
1285 //
1286 void
1287 ASDCP::MXF::InterchangeObject::Copy(const InterchangeObject& rhs)
1288 {
1289   m_UL = rhs.m_UL;
1290   InstanceUID = rhs.InstanceUID;
1291   GenerationUID = rhs.GenerationUID;
1292 }
1293
1294 //
1295 ASDCP::Result_t
1296 ASDCP::MXF::InterchangeObject::InitFromTLVSet(TLVReader& TLVSet)
1297 {
1298   Result_t result = TLVSet.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID));
1299   if ( ASDCP_SUCCESS(result) )
1300     result = TLVSet.ReadObject(OBJ_READ_ARGS_OPT(GenerationInterchangeObject, GenerationUID));
1301   return result;
1302 }
1303
1304 //
1305 ASDCP::Result_t
1306 ASDCP::MXF::InterchangeObject::WriteToTLVSet(TLVWriter& TLVSet)
1307 {
1308   Result_t result = TLVSet.WriteObject(OBJ_WRITE_ARGS(InterchangeObject, InstanceUID));
1309   if ( ASDCP_SUCCESS(result) )
1310     result = TLVSet.WriteObject(OBJ_WRITE_ARGS_OPT(GenerationInterchangeObject, GenerationUID));
1311   return result;
1312 }
1313
1314 //
1315 ASDCP::Result_t
1316 ASDCP::MXF::InterchangeObject::InitFromBuffer(const byte_t* p, ui32_t l)
1317 {
1318   ASDCP_TEST_NULL(p);
1319   Result_t result = RESULT_FALSE;
1320
1321   if ( m_UL.HasValue() )
1322     {
1323       result = KLVPacket::InitFromBuffer(p, l, m_UL);
1324
1325       if ( ASDCP_SUCCESS(result) )
1326         {
1327           TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup);
1328           result = InitFromTLVSet(MemRDR);
1329         }
1330     }
1331   else
1332     {
1333       result = KLVPacket::InitFromBuffer(p, l);
1334     }
1335   
1336   return result;
1337 }
1338
1339 //
1340 ASDCP::Result_t
1341 ASDCP::MXF::InterchangeObject::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
1342 {
1343   if ( ! m_UL.HasValue() )
1344     return RESULT_STATE;
1345
1346   TLVWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length, m_Lookup);
1347   Result_t result = WriteToTLVSet(MemWRT);
1348
1349   if ( ASDCP_SUCCESS(result) )
1350     {
1351       ui32_t packet_length = MemWRT.Length();
1352       result = WriteKLToBuffer(Buffer, packet_length);
1353
1354       if ( ASDCP_SUCCESS(result) )
1355         Buffer.Size(Buffer.Size() + packet_length);
1356     }
1357
1358   return result;
1359 }
1360
1361 //
1362 void
1363 ASDCP::MXF::InterchangeObject::Dump(FILE* stream)
1364 {
1365   char identbuf[IdentBufferLen];
1366
1367   fputc('\n', stream);
1368   KLVPacket::Dump(stream, *m_Dict, false);
1369   fprintf(stream, "             InstanceUID = %s\n",  InstanceUID.EncodeHex(identbuf, IdentBufferLen));
1370
1371   if ( ! GenerationUID.empty() )
1372     fprintf(stream, "           GenerationUID = %s\n",  GenerationUID.get().EncodeHex(identbuf, IdentBufferLen));
1373 }
1374
1375 //
1376 bool
1377 ASDCP::MXF::InterchangeObject::IsA(const byte_t* label)
1378 {
1379   if ( m_KLLength == 0 || m_KeyStart == 0 )
1380     return false;
1381
1382   return ( memcmp(label, m_KeyStart, SMPTE_UL_LENGTH) == 0 );
1383 }
1384
1385
1386 //------------------------------------------------------------------------------------------
1387
1388
1389 typedef std::map<ASDCP::UL, ASDCP::MXF::MXFObjectFactory_t>FactoryMap_t;
1390 typedef FactoryMap_t::iterator FLi_t;
1391
1392 //
1393 class FactoryList : public FactoryMap_t
1394 {
1395   Kumu::Mutex m_Lock;
1396
1397 public:
1398   FactoryList() {}
1399   ~FactoryList() {}
1400
1401   bool Empty() {
1402     Kumu::AutoMutex BlockLock(m_Lock);
1403     return empty();
1404   }
1405
1406   FLi_t Find(const byte_t* label) {
1407     Kumu::AutoMutex BlockLock(m_Lock);
1408     return find(label);
1409   }
1410
1411   FLi_t End() {
1412     Kumu::AutoMutex BlockLock(m_Lock);
1413     return end();
1414   }
1415
1416   void Insert(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory) {
1417     Kumu::AutoMutex BlockLock(m_Lock);
1418     insert(FactoryList::value_type(label, factory));
1419   }
1420 };
1421
1422 //
1423 static FactoryList s_FactoryList;
1424 static Kumu::Mutex s_InitLock;
1425 static bool        s_TypesInit = false;
1426
1427
1428 //
1429 void
1430 ASDCP::MXF::SetObjectFactory(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory)
1431 {
1432   s_FactoryList.Insert(label, factory);
1433 }
1434
1435 //
1436 ASDCP::MXF::InterchangeObject*
1437 ASDCP::MXF::CreateObject(const Dictionary*& Dict, const UL& label)
1438 {
1439   if ( ! s_TypesInit )
1440     {
1441       Kumu::AutoMutex BlockLock(s_InitLock);
1442
1443       if ( ! s_TypesInit )
1444         {
1445           MXF::Metadata_InitTypes(Dict);
1446           s_TypesInit = true;
1447         }
1448     }
1449
1450   FLi_t i = s_FactoryList.find(label.Value());
1451
1452   if ( i == s_FactoryList.end() )
1453     return new InterchangeObject(Dict);
1454
1455   return i->second(Dict);
1456 }
1457
1458
1459 //------------------------------------------------------------------------------------------
1460
1461 //
1462 bool
1463 ASDCP::MXF::decode_mca_string(const std::string& s, const mca_label_map_t& labels, const Dictionary& dict, const std::string& language,
1464                               InterchangeObject_list_t& descriptor_list, ui32_t& channel_count)
1465 {
1466   const Dictionary *dictp = &dict;
1467   std::string symbol_buf;
1468   channel_count = 0;
1469   ASDCP::MXF::SoundfieldGroupLabelSubDescriptor *current_soundfield = 0;
1470   std::string::const_iterator i;
1471
1472   for ( i = s.begin(); i != s.end(); ++i )
1473     {
1474       if ( *i == '(' )
1475         {
1476           if ( current_soundfield != 0 )
1477             {
1478               fprintf(stderr, "Encountered '(', already processing a soundfield group.\n");
1479               return false;
1480             }
1481
1482           if ( symbol_buf.empty() )
1483             {
1484               fprintf(stderr, "Encountered '(', without leading soundfield group symbol.\n");
1485               return false;
1486             }
1487
1488           mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1489       
1490           if ( i == labels.end() )
1491             {
1492               fprintf(stderr, "Unknown symbol: '%s'\n", symbol_buf.c_str());
1493               return false;
1494             }
1495       
1496           if ( i->second.Value()[10] != 2 ) // magic depends on UL "Essence Facet" byte (see ST 428-12)
1497             {
1498               fprintf(stderr, "Not a soundfield group symbol: '%s'\n", symbol_buf.c_str());
1499               return false;
1500             }
1501
1502           current_soundfield = new ASDCP::MXF::SoundfieldGroupLabelSubDescriptor(dictp);
1503
1504           GenRandomValue(current_soundfield->InstanceUID);
1505           GenRandomValue(current_soundfield->MCALinkID);
1506           current_soundfield->MCATagSymbol = "sg" + i->first;
1507           current_soundfield->MCATagName = i->first;
1508           current_soundfield->RFC5646SpokenLanguage = language;
1509           current_soundfield->MCALabelDictionaryID = i->second;
1510           descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(current_soundfield));
1511           symbol_buf.clear();
1512         }
1513       else if ( *i == ')' )
1514         {
1515           if ( current_soundfield == 0 )
1516             {
1517               fprintf(stderr, "Encountered ')', not currently processing a soundfield group.\n");
1518               return false;
1519             }
1520
1521           if ( symbol_buf.empty() )
1522             {
1523               fprintf(stderr, "Soundfield group description contains no channels.\n");
1524               return false;
1525             }
1526
1527           mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1528       
1529           if ( i == labels.end() )
1530             {
1531               fprintf(stderr, "Unknown symbol: '%s'\n", symbol_buf.c_str());
1532               return false;
1533             }
1534
1535           ASDCP::MXF::AudioChannelLabelSubDescriptor *channel_descr =
1536             new ASDCP::MXF::AudioChannelLabelSubDescriptor(dictp);
1537
1538           GenRandomValue(channel_descr->InstanceUID);
1539           assert(current_soundfield);
1540           channel_descr->MCALinkID = current_soundfield->MCALinkID;
1541           channel_descr->MCAChannelID = channel_count++;
1542           channel_descr->MCATagSymbol = "ch" + i->first;
1543           channel_descr->MCATagName = i->first;
1544           channel_descr->RFC5646SpokenLanguage = language;
1545           channel_descr->MCALabelDictionaryID = i->second;
1546           descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(channel_descr));
1547           symbol_buf.clear();
1548           current_soundfield = 0;
1549         }
1550       else if ( *i == ',' )
1551         {
1552           if ( ! symbol_buf.empty() )
1553             {
1554               mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1555
1556               if ( i == labels.end() )
1557                 {
1558                   fprintf(stderr, "Unknown symbol: '%s'\n", symbol_buf.c_str());
1559                   return false;
1560                 }
1561
1562               if ( i->second.Value()[10] != 1 ) // magic depends on UL "Essence Facet" byte (see ST 428-12)
1563                 {
1564                   fprintf(stderr, "Not a channel symbol: '%s'\n", symbol_buf.c_str());
1565                   return false;
1566                 }
1567
1568               ASDCP::MXF::AudioChannelLabelSubDescriptor *channel_descr =
1569                 new ASDCP::MXF::AudioChannelLabelSubDescriptor(dictp);
1570
1571               GenRandomValue(channel_descr->InstanceUID);
1572
1573               if ( current_soundfield != 0 )
1574                 {
1575                   channel_descr->MCALinkID = current_soundfield->MCALinkID;
1576                 }
1577
1578               channel_descr->MCAChannelID = channel_count++;
1579               channel_descr->MCATagSymbol = "ch" + i->first;
1580               channel_descr->MCATagName = i->first;
1581               channel_descr->RFC5646SpokenLanguage = language;
1582               channel_descr->MCALabelDictionaryID = i->second;
1583               descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(channel_descr));
1584               symbol_buf.clear();
1585             }
1586         }
1587       else if ( isalnum(*i) )
1588         {
1589           symbol_buf += *i;
1590         }
1591       else if ( ! isspace(*i) )
1592         {
1593           fprintf(stderr, "Unexpected character '%c'.\n", *i);
1594           return false;
1595         }
1596     }
1597
1598   if ( ! symbol_buf.empty() )
1599     {
1600       mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1601       
1602       if ( i == labels.end() )
1603         {
1604           fprintf(stderr, "Unknown symbol: '%s'\n", symbol_buf.c_str());
1605           return false;
1606         }
1607
1608       ASDCP::MXF::AudioChannelLabelSubDescriptor *channel_descr =
1609         new ASDCP::MXF::AudioChannelLabelSubDescriptor(dictp);
1610
1611       GenRandomValue(channel_descr->InstanceUID);
1612
1613       if ( current_soundfield != 0 )
1614         {
1615           channel_descr->MCALinkID = current_soundfield->MCALinkID;
1616         }
1617
1618       channel_descr->MCAChannelID = channel_count++;
1619       channel_descr->MCATagSymbol = "ch" + i->first;
1620       channel_descr->MCATagName = i->first;
1621       channel_descr->RFC5646SpokenLanguage = language;
1622       channel_descr->MCALabelDictionaryID = i->second;
1623       descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(channel_descr));
1624     }
1625
1626   return true;
1627 }
1628
1629
1630 ASDCP::MXF::ASDCP_MCAConfigParser::ASDCP_MCAConfigParser(const Dictionary*& d) : m_Dict(d), m_ChannelCount(0)
1631 {
1632   m_LabelMap.insert(mca_label_map_t::value_type("L", m_Dict->ul(MDD_DCAudioChannel_L)));
1633   m_LabelMap.insert(mca_label_map_t::value_type("R", m_Dict->ul(MDD_DCAudioChannel_R)));
1634   m_LabelMap.insert(mca_label_map_t::value_type("C", m_Dict->ul(MDD_DCAudioChannel_C)));
1635   m_LabelMap.insert(mca_label_map_t::value_type("LFE", m_Dict->ul(MDD_DCAudioChannel_LFE)));
1636   m_LabelMap.insert(mca_label_map_t::value_type("Ls", m_Dict->ul(MDD_DCAudioChannel_Ls)));
1637   m_LabelMap.insert(mca_label_map_t::value_type("Rs", m_Dict->ul(MDD_DCAudioChannel_Rs)));
1638   m_LabelMap.insert(mca_label_map_t::value_type("Lss", m_Dict->ul(MDD_DCAudioChannel_Lss)));
1639   m_LabelMap.insert(mca_label_map_t::value_type("Rss", m_Dict->ul(MDD_DCAudioChannel_Rss)));
1640   m_LabelMap.insert(mca_label_map_t::value_type("Lrs", m_Dict->ul(MDD_DCAudioChannel_Lrs)));
1641   m_LabelMap.insert(mca_label_map_t::value_type("Rrs", m_Dict->ul(MDD_DCAudioChannel_Rrs)));
1642   m_LabelMap.insert(mca_label_map_t::value_type("Lc", m_Dict->ul(MDD_DCAudioChannel_Lc)));
1643   m_LabelMap.insert(mca_label_map_t::value_type("Rc", m_Dict->ul(MDD_DCAudioChannel_Rc)));
1644   m_LabelMap.insert(mca_label_map_t::value_type("Cs", m_Dict->ul(MDD_DCAudioChannel_Cs)));
1645   m_LabelMap.insert(mca_label_map_t::value_type("HI", m_Dict->ul(MDD_DCAudioChannel_HI)));
1646   m_LabelMap.insert(mca_label_map_t::value_type("VIN", m_Dict->ul(MDD_DCAudioChannel_VIN)));
1647   m_LabelMap.insert(mca_label_map_t::value_type("51", m_Dict->ul(MDD_DCAudioSoundfield_51)));
1648   m_LabelMap.insert(mca_label_map_t::value_type("71", m_Dict->ul(MDD_DCAudioSoundfield_71)));
1649   m_LabelMap.insert(mca_label_map_t::value_type("SDS", m_Dict->ul(MDD_DCAudioSoundfield_SDS)));
1650   m_LabelMap.insert(mca_label_map_t::value_type("61", m_Dict->ul(MDD_DCAudioSoundfield_61)));
1651   m_LabelMap.insert(mca_label_map_t::value_type("M", m_Dict->ul(MDD_DCAudioSoundfield_M)));
1652 }
1653
1654 //
1655 ui32_t
1656 ASDCP::MXF::ASDCP_MCAConfigParser::ChannelCount() const
1657 {
1658   return m_ChannelCount;
1659 }
1660
1661 // 51(L,R,C,LFE,Ls,Rs),HI,VIN
1662 bool
1663 ASDCP::MXF::ASDCP_MCAConfigParser::DecodeString(const std::string& s, const std::string& language)
1664 {
1665   return decode_mca_string(s, m_LabelMap, *m_Dict, language, *this, m_ChannelCount);
1666 }
1667
1668
1669
1670 ASDCP::MXF::AS02_MCAConfigParser::AS02_MCAConfigParser(const Dictionary*& d) : ASDCP::MXF::ASDCP_MCAConfigParser(d)
1671 {
1672   m_LabelMap.insert(mca_label_map_t::value_type("M1", m_Dict->ul(MDD_IMFAudioChannel_M1)));
1673   m_LabelMap.insert(mca_label_map_t::value_type("M2", m_Dict->ul(MDD_IMFAudioChannel_M2)));
1674   m_LabelMap.insert(mca_label_map_t::value_type("Lt", m_Dict->ul(MDD_IMFAudioChannel_Lt)));
1675   m_LabelMap.insert(mca_label_map_t::value_type("Rt", m_Dict->ul(MDD_IMFAudioChannel_Rt)));
1676   m_LabelMap.insert(mca_label_map_t::value_type("Lst", m_Dict->ul(MDD_IMFAudioChannel_Lst)));
1677   m_LabelMap.insert(mca_label_map_t::value_type("Rst", m_Dict->ul(MDD_IMFAudioChannel_Rst)));
1678   m_LabelMap.insert(mca_label_map_t::value_type("S", m_Dict->ul(MDD_IMFAudioChannel_S)));
1679   m_LabelMap.insert(mca_label_map_t::value_type("ST", m_Dict->ul(MDD_IMFAudioSoundfield_ST)));
1680   m_LabelMap.insert(mca_label_map_t::value_type("DM", m_Dict->ul(MDD_IMFAudioSoundfield_DM)));
1681   m_LabelMap.insert(mca_label_map_t::value_type("DNS", m_Dict->ul(MDD_IMFAudioSoundfield_DNS)));
1682   m_LabelMap.insert(mca_label_map_t::value_type("30", m_Dict->ul(MDD_IMFAudioSoundfield_30)));
1683   m_LabelMap.insert(mca_label_map_t::value_type("40", m_Dict->ul(MDD_IMFAudioSoundfield_40)));
1684   m_LabelMap.insert(mca_label_map_t::value_type("50", m_Dict->ul(MDD_IMFAudioSoundfield_50)));
1685   m_LabelMap.insert(mca_label_map_t::value_type("60", m_Dict->ul(MDD_IMFAudioSoundfield_60)));
1686   m_LabelMap.insert(mca_label_map_t::value_type("70", m_Dict->ul(MDD_IMFAudioSoundfield_70)));
1687   m_LabelMap.insert(mca_label_map_t::value_type("LtRt", m_Dict->ul(MDD_IMFAudioSoundfield_LtRt)));
1688   m_LabelMap.insert(mca_label_map_t::value_type("51Ex", m_Dict->ul(MDD_IMFAudioSoundfield_51Ex)));
1689   m_LabelMap.insert(mca_label_map_t::value_type("HI", m_Dict->ul(MDD_IMFAudioSoundfield_HI)));
1690   m_LabelMap.insert(mca_label_map_t::value_type("VIN", m_Dict->ul(MDD_IMFAudioSoundfield_VIN)));
1691 }
1692
1693 //
1694 // end MXF.cpp
1695 //