oops.
[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       if ( (*pl_i)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1103         {
1104           iseg_count++;
1105           IndexTableSegment* Segment = (IndexTableSegment*)(*pl_i);
1106
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       if ( (*li)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
1190         {
1191           IndexTableSegment* Segment = (IndexTableSegment*)(*li);
1192           ui64_t start_pos = Segment->IndexStartPosition;
1193
1194           if ( Segment->EditUnitByteCount > 0 )
1195             {
1196               if ( m_PacketList->m_List.size() > 1 )
1197                 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
1198
1199               if ( ! Segment->IndexEntryArray.empty() )
1200                 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
1201
1202               Entry.StreamOffset = (ui64_t)frame_num * Segment->EditUnitByteCount;
1203               return RESULT_OK;
1204             }
1205           else if ( (ui64_t)frame_num >= start_pos
1206                     && (ui64_t)frame_num < (start_pos + Segment->IndexDuration) )
1207             {
1208               ui64_t tmp = frame_num - start_pos;
1209               assert(tmp <= 0xFFFFFFFFL);
1210               Entry = Segment->IndexEntryArray[(ui32_t) tmp];
1211               return RESULT_OK;
1212             }
1213         }
1214     }
1215
1216   return RESULT_FAIL;
1217 }
1218
1219 //
1220 void
1221 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const Rational& Rate)
1222 {
1223   assert(lookup);
1224   m_Lookup = lookup;
1225   m_BytesPerEditUnit = size;
1226   m_EditRate = Rate;
1227
1228   IndexTableSegment* Index = new IndexTableSegment(m_Dict);
1229   AddChildObject(Index);
1230   Index->EditUnitByteCount = m_BytesPerEditUnit;
1231   Index->IndexEditRate = Rate;
1232 }
1233
1234 //
1235 void
1236 ASDCP::MXF::OPAtomIndexFooter::SetIndexParamsVBR(IPrimerLookup* lookup, const Rational& Rate, Kumu::fpos_t offset)
1237 {
1238   assert(lookup);
1239   m_Lookup = lookup;
1240   m_BytesPerEditUnit = 0;
1241   m_EditRate = Rate;
1242   m_ECOffset = offset;
1243 }
1244
1245 //
1246 void
1247 ASDCP::MXF::OPAtomIndexFooter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
1248 {
1249   if ( m_BytesPerEditUnit != 0 )  // are we CBR? that's bad 
1250     {
1251       DefaultLogSink().Error("Call to PushIndexEntry() failed: index is CBR\n");
1252       return;
1253     }
1254
1255   // do we have an available segment?
1256   if ( m_CurrentSegment == 0 )
1257     { // no, set up a new segment
1258       m_CurrentSegment = new IndexTableSegment(m_Dict);
1259       assert(m_CurrentSegment);
1260       AddChildObject(m_CurrentSegment);
1261       m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1262       m_CurrentSegment->IndexEditRate = m_EditRate;
1263       m_CurrentSegment->IndexStartPosition = 0;
1264     }
1265   else if ( m_CurrentSegment->IndexEntryArray.size() >= CBRIndexEntriesPerSegment )
1266     { // no, this one is full, start another
1267       m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
1268       ui64_t StartPosition = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
1269
1270       m_CurrentSegment = new IndexTableSegment(m_Dict);
1271       assert(m_CurrentSegment);
1272       AddChildObject(m_CurrentSegment);
1273       m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
1274       m_CurrentSegment->IndexEditRate = m_EditRate;
1275       m_CurrentSegment->IndexStartPosition = StartPosition;
1276     }
1277
1278   m_CurrentSegment->IndexEntryArray.push_back(Entry);
1279 }
1280
1281 //------------------------------------------------------------------------------------------
1282 //
1283
1284 //
1285 void
1286 ASDCP::MXF::InterchangeObject::Copy(const InterchangeObject& rhs)
1287 {
1288   m_UL = rhs.m_UL;
1289   InstanceUID = rhs.InstanceUID;
1290   GenerationUID = rhs.GenerationUID;
1291 }
1292
1293 //
1294 ASDCP::Result_t
1295 ASDCP::MXF::InterchangeObject::InitFromTLVSet(TLVReader& TLVSet)
1296 {
1297   Result_t result = TLVSet.ReadObject(OBJ_READ_ARGS(InterchangeObject, InstanceUID));
1298   if ( ASDCP_SUCCESS(result) )
1299     result = TLVSet.ReadObject(OBJ_READ_ARGS_OPT(GenerationInterchangeObject, GenerationUID));
1300   return result;
1301 }
1302
1303 //
1304 ASDCP::Result_t
1305 ASDCP::MXF::InterchangeObject::WriteToTLVSet(TLVWriter& TLVSet)
1306 {
1307   Result_t result = TLVSet.WriteObject(OBJ_WRITE_ARGS(InterchangeObject, InstanceUID));
1308   if ( ASDCP_SUCCESS(result) )
1309     result = TLVSet.WriteObject(OBJ_WRITE_ARGS_OPT(GenerationInterchangeObject, GenerationUID));
1310   return result;
1311 }
1312
1313 //
1314 ASDCP::Result_t
1315 ASDCP::MXF::InterchangeObject::InitFromBuffer(const byte_t* p, ui32_t l)
1316 {
1317   ASDCP_TEST_NULL(p);
1318   Result_t result = RESULT_FALSE;
1319
1320   if ( m_UL.HasValue() )
1321     {
1322       result = KLVPacket::InitFromBuffer(p, l, m_UL);
1323
1324       if ( ASDCP_SUCCESS(result) )
1325         {
1326           TLVReader MemRDR(m_ValueStart, m_ValueLength, m_Lookup);
1327           result = InitFromTLVSet(MemRDR);
1328         }
1329     }
1330   else
1331     {
1332       result = KLVPacket::InitFromBuffer(p, l);
1333     }
1334   
1335   return result;
1336 }
1337
1338 //
1339 ASDCP::Result_t
1340 ASDCP::MXF::InterchangeObject::WriteToBuffer(ASDCP::FrameBuffer& Buffer)
1341 {
1342   if ( ! m_UL.HasValue() )
1343     return RESULT_STATE;
1344
1345   TLVWriter MemWRT(Buffer.Data() + kl_length, Buffer.Capacity() - kl_length, m_Lookup);
1346   Result_t result = WriteToTLVSet(MemWRT);
1347
1348   if ( ASDCP_SUCCESS(result) )
1349     {
1350       ui32_t packet_length = MemWRT.Length();
1351       result = WriteKLToBuffer(Buffer, packet_length);
1352
1353       if ( ASDCP_SUCCESS(result) )
1354         Buffer.Size(Buffer.Size() + packet_length);
1355     }
1356
1357   return result;
1358 }
1359
1360 //
1361 void
1362 ASDCP::MXF::InterchangeObject::Dump(FILE* stream)
1363 {
1364   char identbuf[IdentBufferLen];
1365
1366   fputc('\n', stream);
1367   KLVPacket::Dump(stream, *m_Dict, false);
1368   fprintf(stream, "             InstanceUID = %s\n",  InstanceUID.EncodeHex(identbuf, IdentBufferLen));
1369
1370   if ( ! GenerationUID.empty() )
1371     fprintf(stream, "           GenerationUID = %s\n",  GenerationUID.get().EncodeHex(identbuf, IdentBufferLen));
1372 }
1373
1374 //
1375 bool
1376 ASDCP::MXF::InterchangeObject::IsA(const byte_t* label)
1377 {
1378   if ( m_KLLength == 0 )
1379     return false;
1380
1381   return ( memcmp(label, m_KeyStart, SMPTE_UL_LENGTH) == 0 );
1382 }
1383
1384
1385 //------------------------------------------------------------------------------------------
1386
1387
1388 typedef std::map<ASDCP::UL, ASDCP::MXF::MXFObjectFactory_t>FactoryMap_t;
1389 typedef FactoryMap_t::iterator FLi_t;
1390
1391 //
1392 class FactoryList : public FactoryMap_t
1393 {
1394   Kumu::Mutex m_Lock;
1395
1396 public:
1397   FactoryList() {}
1398   ~FactoryList() {}
1399
1400   bool Empty() {
1401     Kumu::AutoMutex BlockLock(m_Lock);
1402     return empty();
1403   }
1404
1405   FLi_t Find(const byte_t* label) {
1406     Kumu::AutoMutex BlockLock(m_Lock);
1407     return find(label);
1408   }
1409
1410   FLi_t End() {
1411     Kumu::AutoMutex BlockLock(m_Lock);
1412     return end();
1413   }
1414
1415   void Insert(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory) {
1416     Kumu::AutoMutex BlockLock(m_Lock);
1417     insert(FactoryList::value_type(label, factory));
1418   }
1419 };
1420
1421 //
1422 static FactoryList s_FactoryList;
1423 static Kumu::Mutex s_InitLock;
1424 static bool        s_TypesInit = false;
1425
1426
1427 //
1428 void
1429 ASDCP::MXF::SetObjectFactory(ASDCP::UL label, ASDCP::MXF::MXFObjectFactory_t factory)
1430 {
1431   s_FactoryList.Insert(label, factory);
1432 }
1433
1434 //
1435 ASDCP::MXF::InterchangeObject*
1436 ASDCP::MXF::CreateObject(const Dictionary*& Dict, const UL& label)
1437 {
1438   if ( ! s_TypesInit )
1439     {
1440       Kumu::AutoMutex BlockLock(s_InitLock);
1441
1442       if ( ! s_TypesInit )
1443         {
1444           MXF::Metadata_InitTypes(Dict);
1445           s_TypesInit = true;
1446         }
1447     }
1448
1449   FLi_t i = s_FactoryList.find(label.Value());
1450
1451   if ( i == s_FactoryList.end() )
1452     return new InterchangeObject(Dict);
1453
1454   return i->second(Dict);
1455 }
1456
1457
1458 //------------------------------------------------------------------------------------------
1459
1460 //
1461 bool
1462 ASDCP::MXF::decode_mca_string(const std::string& s, const mca_label_map_t& labels, const Dictionary& dict, const std::string& language,
1463                               InterchangeObject_list_t& descriptor_list, ui32_t& channel_count)
1464 {
1465   const Dictionary *dictp = &dict;
1466   std::string symbol_buf;
1467   channel_count = 0;
1468   ASDCP::MXF::SoundfieldGroupLabelSubDescriptor *current_soundfield = 0;
1469   std::string::const_iterator i;
1470
1471   for ( i = s.begin(); i != s.end(); ++i )
1472     {
1473       if ( *i == '(' )
1474         {
1475           if ( current_soundfield != 0 )
1476             {
1477               fprintf(stderr, "Encountered '(', already processing a soundfield group.\n");
1478               return false;
1479             }
1480
1481           if ( symbol_buf.empty() )
1482             {
1483               fprintf(stderr, "Encountered '(', without leading soundfield group symbol.\n");
1484               return false;
1485             }
1486
1487           mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1488       
1489           if ( i == labels.end() )
1490             {
1491               fprintf(stderr, "Unknown symbol: '%s'\n", symbol_buf.c_str());
1492               return false;
1493             }
1494       
1495           if ( i->second.Value()[10] != 2 ) // magic depends on UL "Essence Facet" byte (see ST 428-12)
1496             {
1497               fprintf(stderr, "Not a soundfield group symbol: '%s'\n", symbol_buf.c_str());
1498               return false;
1499             }
1500
1501           current_soundfield = new ASDCP::MXF::SoundfieldGroupLabelSubDescriptor(dictp);
1502
1503           GenRandomValue(current_soundfield->InstanceUID);
1504           GenRandomValue(current_soundfield->MCALinkID);
1505           current_soundfield->MCATagSymbol = "sg" + i->first;
1506           current_soundfield->MCATagName = i->first;
1507           current_soundfield->RFC5646SpokenLanguage = language;
1508           current_soundfield->MCALabelDictionaryID = i->second;
1509           descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(current_soundfield));
1510           symbol_buf.clear();
1511         }
1512       else if ( *i == ')' )
1513         {
1514           if ( current_soundfield == 0 )
1515             {
1516               fprintf(stderr, "Encountered ')', not currently processing a soundfield group.\n");
1517               return false;
1518             }
1519
1520           if ( symbol_buf.empty() )
1521             {
1522               fprintf(stderr, "Soundfield group description contains no channels.\n");
1523               return false;
1524             }
1525
1526           mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1527       
1528           if ( i == labels.end() )
1529             {
1530               fprintf(stderr, "Unknown symbol: '%s'\n", symbol_buf.c_str());
1531               return false;
1532             }
1533
1534           ASDCP::MXF::AudioChannelLabelSubDescriptor *channel_descr =
1535             new ASDCP::MXF::AudioChannelLabelSubDescriptor(dictp);
1536
1537           GenRandomValue(channel_descr->InstanceUID);
1538           assert(current_soundfield);
1539           channel_descr->MCALinkID = current_soundfield->MCALinkID;
1540           channel_descr->MCAChannelID = channel_count++;
1541           channel_descr->MCATagSymbol = "ch" + i->first;
1542           channel_descr->MCATagName = i->first;
1543           channel_descr->RFC5646SpokenLanguage = language;
1544           channel_descr->MCALabelDictionaryID = i->second;
1545           descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(channel_descr));
1546           symbol_buf.clear();
1547           current_soundfield = 0;
1548         }
1549       else if ( *i == ',' )
1550         {
1551           if ( ! symbol_buf.empty() )
1552             {
1553               mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1554
1555               if ( i == labels.end() )
1556                 {
1557                   fprintf(stderr, "Unknown symbol: '%s'\n", symbol_buf.c_str());
1558                   return false;
1559                 }
1560
1561               if ( i->second.Value()[10] != 1 ) // magic depends on UL "Essence Facet" byte (see ST 428-12)
1562                 {
1563                   fprintf(stderr, "Not a channel symbol: '%s'\n", symbol_buf.c_str());
1564                   return false;
1565                 }
1566
1567               ASDCP::MXF::AudioChannelLabelSubDescriptor *channel_descr =
1568                 new ASDCP::MXF::AudioChannelLabelSubDescriptor(dictp);
1569
1570               GenRandomValue(channel_descr->InstanceUID);
1571
1572               if ( current_soundfield != 0 )
1573                 {
1574                   channel_descr->MCALinkID = current_soundfield->MCALinkID;
1575                 }
1576
1577               channel_descr->MCAChannelID = channel_count++;
1578               channel_descr->MCATagSymbol = "ch" + i->first;
1579               channel_descr->MCATagName = i->first;
1580               channel_descr->RFC5646SpokenLanguage = language;
1581               channel_descr->MCALabelDictionaryID = i->second;
1582               descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(channel_descr));
1583               symbol_buf.clear();
1584             }
1585         }
1586       else if ( isalnum(*i) )
1587         {
1588           symbol_buf += *i;
1589         }
1590       else if ( ! isspace(*i) )
1591         {
1592           fprintf(stderr, "Unexpected character '%c'.\n", *i);
1593           return false;
1594         }
1595     }
1596
1597   if ( ! symbol_buf.empty() )
1598     {
1599       mca_label_map_t::const_iterator i = labels.find(symbol_buf);
1600       
1601       if ( i == labels.end() )
1602         {
1603           fprintf(stderr, "Unknown symbol: '%s'\n", symbol_buf.c_str());
1604           return false;
1605         }
1606
1607       ASDCP::MXF::AudioChannelLabelSubDescriptor *channel_descr =
1608         new ASDCP::MXF::AudioChannelLabelSubDescriptor(dictp);
1609
1610       GenRandomValue(channel_descr->InstanceUID);
1611
1612       if ( current_soundfield != 0 )
1613         {
1614           channel_descr->MCALinkID = current_soundfield->MCALinkID;
1615         }
1616
1617       channel_descr->MCAChannelID = channel_count++;
1618       channel_descr->MCATagSymbol = "ch" + i->first;
1619       channel_descr->MCATagName = i->first;
1620       channel_descr->RFC5646SpokenLanguage = language;
1621       channel_descr->MCALabelDictionaryID = i->second;
1622       descriptor_list.push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(channel_descr));
1623     }
1624
1625   return true;
1626 }
1627
1628
1629 ASDCP::MXF::ASDCP_MCAConfigParser::ASDCP_MCAConfigParser(const Dictionary*& d) : m_Dict(d), m_ChannelCount(0)
1630 {
1631   m_LabelMap.insert(mca_label_map_t::value_type("L", m_Dict->ul(MDD_DCAudioChannel_L)));
1632   m_LabelMap.insert(mca_label_map_t::value_type("R", m_Dict->ul(MDD_DCAudioChannel_R)));
1633   m_LabelMap.insert(mca_label_map_t::value_type("C", m_Dict->ul(MDD_DCAudioChannel_C)));
1634   m_LabelMap.insert(mca_label_map_t::value_type("LFE", m_Dict->ul(MDD_DCAudioChannel_LFE)));
1635   m_LabelMap.insert(mca_label_map_t::value_type("Ls", m_Dict->ul(MDD_DCAudioChannel_Ls)));
1636   m_LabelMap.insert(mca_label_map_t::value_type("Rs", m_Dict->ul(MDD_DCAudioChannel_Rs)));
1637   m_LabelMap.insert(mca_label_map_t::value_type("Lss", m_Dict->ul(MDD_DCAudioChannel_Lss)));
1638   m_LabelMap.insert(mca_label_map_t::value_type("Rss", m_Dict->ul(MDD_DCAudioChannel_Rss)));
1639   m_LabelMap.insert(mca_label_map_t::value_type("Lrs", m_Dict->ul(MDD_DCAudioChannel_Lrs)));
1640   m_LabelMap.insert(mca_label_map_t::value_type("Rrs", m_Dict->ul(MDD_DCAudioChannel_Rrs)));
1641   m_LabelMap.insert(mca_label_map_t::value_type("Lc", m_Dict->ul(MDD_DCAudioChannel_Lc)));
1642   m_LabelMap.insert(mca_label_map_t::value_type("Rc", m_Dict->ul(MDD_DCAudioChannel_Rc)));
1643   m_LabelMap.insert(mca_label_map_t::value_type("Cs", m_Dict->ul(MDD_DCAudioChannel_Cs)));
1644   m_LabelMap.insert(mca_label_map_t::value_type("HI", m_Dict->ul(MDD_DCAudioChannel_HI)));
1645   m_LabelMap.insert(mca_label_map_t::value_type("VIN", m_Dict->ul(MDD_DCAudioChannel_VIN)));
1646   m_LabelMap.insert(mca_label_map_t::value_type("51", m_Dict->ul(MDD_DCAudioSoundfield_51)));
1647   m_LabelMap.insert(mca_label_map_t::value_type("71", m_Dict->ul(MDD_DCAudioSoundfield_71)));
1648   m_LabelMap.insert(mca_label_map_t::value_type("SDS", m_Dict->ul(MDD_DCAudioSoundfield_SDS)));
1649   m_LabelMap.insert(mca_label_map_t::value_type("61", m_Dict->ul(MDD_DCAudioSoundfield_61)));
1650   m_LabelMap.insert(mca_label_map_t::value_type("M", m_Dict->ul(MDD_DCAudioSoundfield_M)));
1651 }
1652
1653 //
1654 ui32_t
1655 ASDCP::MXF::ASDCP_MCAConfigParser::ChannelCount() const
1656 {
1657   return m_ChannelCount;
1658 }
1659
1660 // 51(L,R,C,LFE,Ls,Rs),HI,VIN
1661 bool
1662 ASDCP::MXF::ASDCP_MCAConfigParser::DecodeString(const std::string& s, const std::string& language)
1663 {
1664   return decode_mca_string(s, m_LabelMap, *m_Dict, language, *this, m_ChannelCount);
1665 }
1666
1667
1668
1669 ASDCP::MXF::AS02_MCAConfigParser::AS02_MCAConfigParser(const Dictionary*& d) : ASDCP::MXF::ASDCP_MCAConfigParser(d)
1670 {
1671   m_LabelMap.insert(mca_label_map_t::value_type("M1", m_Dict->ul(MDD_IMFAudioChannel_M1)));
1672   m_LabelMap.insert(mca_label_map_t::value_type("M2", m_Dict->ul(MDD_IMFAudioChannel_M2)));
1673   m_LabelMap.insert(mca_label_map_t::value_type("Lt", m_Dict->ul(MDD_IMFAudioChannel_Lt)));
1674   m_LabelMap.insert(mca_label_map_t::value_type("Rt", m_Dict->ul(MDD_IMFAudioChannel_Rt)));
1675   m_LabelMap.insert(mca_label_map_t::value_type("Lst", m_Dict->ul(MDD_IMFAudioChannel_Lst)));
1676   m_LabelMap.insert(mca_label_map_t::value_type("Rst", m_Dict->ul(MDD_IMFAudioChannel_Rst)));
1677   m_LabelMap.insert(mca_label_map_t::value_type("S", m_Dict->ul(MDD_IMFAudioChannel_S)));
1678   m_LabelMap.insert(mca_label_map_t::value_type("ST", m_Dict->ul(MDD_IMFAudioSoundfield_ST)));
1679   m_LabelMap.insert(mca_label_map_t::value_type("DM", m_Dict->ul(MDD_IMFAudioSoundfield_DM)));
1680   m_LabelMap.insert(mca_label_map_t::value_type("DNS", m_Dict->ul(MDD_IMFAudioSoundfield_DNS)));
1681   m_LabelMap.insert(mca_label_map_t::value_type("30", m_Dict->ul(MDD_IMFAudioSoundfield_30)));
1682   m_LabelMap.insert(mca_label_map_t::value_type("40", m_Dict->ul(MDD_IMFAudioSoundfield_40)));
1683   m_LabelMap.insert(mca_label_map_t::value_type("50", m_Dict->ul(MDD_IMFAudioSoundfield_50)));
1684   m_LabelMap.insert(mca_label_map_t::value_type("60", m_Dict->ul(MDD_IMFAudioSoundfield_60)));
1685   m_LabelMap.insert(mca_label_map_t::value_type("70", m_Dict->ul(MDD_IMFAudioSoundfield_70)));
1686   m_LabelMap.insert(mca_label_map_t::value_type("LtRt", m_Dict->ul(MDD_IMFAudioSoundfield_LtRt)));
1687   m_LabelMap.insert(mca_label_map_t::value_type("51Ex", m_Dict->ul(MDD_IMFAudioSoundfield_51Ex)));
1688   m_LabelMap.insert(mca_label_map_t::value_type("HI", m_Dict->ul(MDD_IMFAudioSoundfield_HI)));
1689   m_LabelMap.insert(mca_label_map_t::value_type("VIN", m_Dict->ul(MDD_IMFAudioSoundfield_VIN)));
1690 }
1691
1692 //
1693 // end MXF.cpp
1694 //