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