83272c09b431a8861ab0a0355abc0172d186d961
[asdcplib.git] / src / AS_DCP_internal.h
1 /*
2 Copyright (c) 2004-2018, 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    AS_DCP_internal.h
28     \version $Id$
29     \brief   AS-DCP library, non-public common elements
30 */
31
32 #ifndef _AS_DCP_INTERNAL_H_
33 #define _AS_DCP_INTERNAL_H_
34
35 #include <KM_platform.h>
36 #include <KM_util.h>
37 #include <KM_log.h>
38 #include "Metadata.h"
39
40 using Kumu::DefaultLogSink;
41 using namespace ASDCP;
42 using namespace ASDCP::MXF;
43
44 // a magic number identifying asdcplib
45 #ifndef ASDCP_BUILD_NUMBER
46 #define ASDCP_BUILD_NUMBER 0x6A68
47 #endif
48
49
50 // uncomment to remove MXFGCGenericEssenceMultipleMappings from your AS-02 files
51 // #define ASDCP_GCMULTI_PATCH
52
53
54 #ifdef DEFAULT_MD_DECL
55 ASDCP::MXF::OP1aHeader *g_OP1aHeader;
56 ASDCP::MXF::OPAtomIndexFooter *g_OPAtomIndexFooter;
57 ASDCP::MXF::RIP *g_RIP;
58 #else
59 extern MXF::OP1aHeader *g_OP1aHeader;
60 extern MXF::OPAtomIndexFooter *g_OPAtomIndexFooter;
61 extern MXF::RIP *g_RIP;
62 #endif
63
64
65 namespace ASDCP
66 {
67
68   //
69   static std::vector<int>
70     version_split(const char* str)
71   {
72     std::vector<int> result;
73     const char* pstr = str;
74     const char* r = strchr(pstr, '.');
75
76     while ( r != 0 )
77       {
78         assert(r >= pstr);
79         if ( r > pstr )
80           result.push_back(strtol(pstr, 0, 10));
81
82         pstr = r + 1;
83         r = strchr(pstr, '.');
84       }
85
86     if( strlen(pstr) > 0 )
87       result.push_back(strtol(pstr, 0, 10));
88
89     assert(result.size() == 3);
90     return result;
91   }
92
93   // constant values used to calculate KLV and EKLV packet sizes
94   static const ui32_t klv_cryptinfo_size =
95     MXF_BER_LENGTH
96     + UUIDlen /* ContextID */
97     + MXF_BER_LENGTH
98     + sizeof(ui64_t) /* PlaintextOffset */
99     + MXF_BER_LENGTH
100     + SMPTE_UL_LENGTH /* SourceKey */
101     + MXF_BER_LENGTH
102     + sizeof(ui64_t) /* SourceLength */
103     + MXF_BER_LENGTH /* ESV length */ ;
104
105   static const ui32_t klv_intpack_size =
106     MXF_BER_LENGTH
107     + UUIDlen /* TrackFileID */
108     + MXF_BER_LENGTH
109     + sizeof(ui64_t) /* SequenceNumber */
110     + MXF_BER_LENGTH
111     + 20; /* HMAC length*/
112
113   // calculate size of encrypted essence with IV, CheckValue, and padding
114   inline ui32_t
115     calc_esv_length(ui32_t source_length, ui32_t plaintext_offset)
116     {
117       ui32_t ct_size = source_length - plaintext_offset;
118       ui32_t diff = ct_size % CBC_BLOCK_SIZE;
119       ui32_t block_size = ct_size - diff;
120       return plaintext_offset + block_size + (CBC_BLOCK_SIZE * 3);
121     }
122
123   // the check value for EKLV packets
124   // CHUKCHUKCHUKCHUK
125   static const byte_t ESV_CheckValue[CBC_BLOCK_SIZE] =
126   { 0x43, 0x48, 0x55, 0x4b, 0x43, 0x48, 0x55, 0x4b,
127     0x43, 0x48, 0x55, 0x4b, 0x43, 0x48, 0x55, 0x4b };
128
129   // Version of MXF spec to which an MXF file conforms
130   enum MXFVersion
131   {
132     MXFVersion_2004,
133     MXFVersion_2011,
134     MXFVersion_MAX
135   };
136
137   // version numbers from the MXF spec, to be written into files
138   
139   ui8_t const MXF_ObjectModelVersion = 1;
140   ui8_t const MXF_2004_MinorVersion = 2;
141   ui8_t const MXF_2011_MinorVersion = 3;
142             
143
144   //------------------------------------------------------------------------------------------
145   //
146
147   ui32_t derive_timecode_rate_from_edit_rate(const ASDCP::Rational& edit_rate);
148
149   Result_t MD_to_WriterInfo(MXF::Identification*, WriterInfo&);
150   Result_t MD_to_CryptoInfo(MXF::CryptographicContext*, WriterInfo&, const Dictionary&);
151
152   Result_t EncryptFrameBuffer(const ASDCP::FrameBuffer&, ASDCP::FrameBuffer&, AESEncContext*);
153   Result_t DecryptFrameBuffer(const ASDCP::FrameBuffer&, ASDCP::FrameBuffer&, AESDecContext*);
154
155   Result_t MD_to_JP2K_PDesc(const ASDCP::MXF::GenericPictureEssenceDescriptor&  EssenceDescriptor,
156                             const ASDCP::MXF::JPEG2000PictureSubDescriptor& EssenceSubDescriptor,
157                             const ASDCP::Rational& EditRate, const ASDCP::Rational& SampleRate,
158                             ASDCP::JP2K::PictureDescriptor& PDesc);
159
160   Result_t JP2K_PDesc_to_MD(const JP2K::PictureDescriptor& PDesc,
161                             const ASDCP::Dictionary& dict,
162                             ASDCP::MXF::GenericPictureEssenceDescriptor& EssenceDescriptor,
163                             ASDCP::MXF::JPEG2000PictureSubDescriptor& EssenceSubDescriptor);
164
165   Result_t PCM_ADesc_to_MD(PCM::AudioDescriptor& ADesc, ASDCP::MXF::WaveAudioDescriptor* ADescObj);
166   Result_t MD_to_PCM_ADesc(ASDCP::MXF::WaveAudioDescriptor* ADescObj, PCM::AudioDescriptor& ADesc);
167
168   void     AddDmsCrypt(Partition& HeaderPart, SourcePackage& Package,
169                        WriterInfo& Descr, const UL& WrappingUL, const Dictionary*& Dict);
170
171   Result_t AddDmsTrackGenericPartUtf8Text(Kumu::FileWriter&, ASDCP::MXF::OP1aHeader&, SourcePackage&,
172                                           ASDCP::MXF::RIP&, const Dictionary*&);
173   //
174   Result_t WriteGenericStreamPartition(Kumu::FileWriter&, ASDCP::MXF::OP1aHeader&, ASDCP::MXF::RIP&, const Dictionary*&,
175                                        const ASDCP::FrameBuffer&, ASDCP::AESEncContext* = 0, ASDCP::HMACContext* = 0);
176   
177   Result_t Read_EKLV_Packet(Kumu::FileReader& File, const ASDCP::Dictionary& Dict,
178                             const ASDCP::WriterInfo& Info, Kumu::fpos_t& LastPosition, ASDCP::FrameBuffer& CtFrameBuf,
179                             ui32_t FrameNum, ui32_t SequenceNum, ASDCP::FrameBuffer& FrameBuf,
180                             const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC);
181
182   Result_t Write_EKLV_Packet(Kumu::FileWriter& File, const ASDCP::Dictionary& Dict, const MXF::OP1aHeader& HeaderPart,
183                              const ASDCP::WriterInfo& Info, ASDCP::FrameBuffer& CtFrameBuf, ui32_t& FramesWritten,
184                              ui64_t & StreamOffset, const ASDCP::FrameBuffer& FrameBuf, const byte_t* EssenceUL,
185                              const ui32_t& MinEssenceElementBerLength,
186                              AESEncContext* Ctx, HMACContext* HMAC);
187
188   //
189  class KLReader : public ASDCP::KLVPacket
190     {
191       ASDCP_NO_COPY_CONSTRUCT(KLReader);
192       byte_t m_KeyBuf[SMPTE_UL_LENGTH*2];
193
194     public:
195       KLReader() {}
196       ~KLReader() {}
197
198       inline const byte_t* Key() { return m_KeyBuf; }
199       inline const ui64_t  Length() { return m_ValueLength; }
200       inline const ui64_t  KLLength() { return m_KLLength; }
201
202       Result_t ReadKLFromFile(Kumu::FileReader& Reader);
203     };
204
205   namespace MXF
206   {
207       //---------------------------------------------------------------------------------
208       //
209
210     ///      void default_md_object_init();
211
212       template <class HeaderType, class IndexAccessType>
213       class TrackFileReader
214       {
215         KM_NO_COPY_CONSTRUCT(TrackFileReader);
216         TrackFileReader();
217
218       public:
219         const Dictionary*  m_Dict;
220         Kumu::FileReader   m_File;
221         HeaderType         m_HeaderPart;
222         IndexAccessType    m_IndexAccess;
223         RIP                m_RIP;
224         WriterInfo         m_Info;
225         ASDCP::FrameBuffer m_CtFrameBuf;
226         Kumu::fpos_t       m_LastPosition;
227
228       TrackFileReader(const Dictionary& d) :
229         m_HeaderPart(m_Dict), m_IndexAccess(m_Dict), m_RIP(m_Dict), m_Dict(&d)
230           {
231             default_md_object_init();
232           }
233
234         virtual ~TrackFileReader() {
235           Close();
236         }
237
238         const MXF::RIP& GetRIP() const { return m_RIP; }
239
240         //
241         Result_t OpenMXFRead(const std::string& filename)
242         {
243           m_LastPosition = 0;
244           Result_t result = m_File.OpenRead(filename);
245
246           if ( ASDCP_SUCCESS(result) )
247             result = SeekToRIP(m_File);
248
249           if ( ASDCP_SUCCESS(result) )
250             {
251               result = m_RIP.InitFromFile(m_File);
252               ui32_t test_s = (ui32_t)m_RIP.PairArray.size();
253
254               if ( ASDCP_FAILURE(result) )
255                 {
256                   DefaultLogSink().Error("File contains no RIP\n");
257                 }
258               else if ( m_RIP.PairArray.empty() )
259                 {
260                   DefaultLogSink().Error("RIP contains no Pairs.\n");
261                 }
262             }
263           else
264             {
265               DefaultLogSink().Error("TrackFileReader::OpenMXFRead, SeekToRIP failed\n");
266             }
267
268           m_File.Seek(0);
269           result = m_HeaderPart.InitFromFile(m_File);
270
271           if ( KM_FAILURE(result) )
272             {
273               DefaultLogSink().Error("TrackFileReader::OpenMXFRead, header init failed\n");
274             }
275
276           return result;
277         }
278
279         //
280         Result_t InitInfo()
281         {
282           assert(m_Dict);
283           InterchangeObject* Object;
284
285           // Identification
286           Result_t result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object);
287
288           // Writer Info and SourcePackage
289           if ( KM_SUCCESS(result) )
290             {
291               MD_to_WriterInfo((Identification*)Object, m_Info);
292               result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object);
293             }
294
295           if ( KM_SUCCESS(result) )
296             {
297               SourcePackage* SP = (SourcePackage*)Object;
298               memcpy(m_Info.AssetUUID, SP->PackageUID.Value() + 16, UUIDlen);
299             }
300
301           // optional CryptographicContext
302           if ( KM_SUCCESS(result) )
303             {
304               Result_t cr_result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(CryptographicContext), &Object);
305
306               if ( KM_SUCCESS(cr_result) )
307                 MD_to_CryptoInfo((CryptographicContext*)Object, m_Info, *m_Dict);
308             }
309
310           return result;
311         }
312
313         // positions file before reading
314         // allows external control of index offset
315         Result_t ReadEKLVFrame(const ui64_t& body_offset,
316                                ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
317                                const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
318         {
319           // look up frame index node
320           IndexTableSegment::IndexEntry TmpEntry;
321
322           if ( KM_FAILURE(m_IndexAccess.Lookup(FrameNum, TmpEntry)) )
323             {
324               DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
325               return RESULT_RANGE;
326             }
327
328           // get relative frame position, apply offset and go read the frame's key and length
329           Kumu::fpos_t FilePosition = body_offset + TmpEntry.StreamOffset;
330           Result_t result = RESULT_OK;
331
332           if ( FilePosition != m_LastPosition )
333             {
334               m_LastPosition = FilePosition;
335               result = m_File.Seek(FilePosition);
336             }
337
338           if ( KM_SUCCESS(result) )
339             result = ReadEKLVPacket(FrameNum, FrameNum + 1, FrameBuf, EssenceUL, Ctx, HMAC);
340
341           return result;
342         }
343
344         // positions file before reading
345         // assumes "processed" index entries have absolute positions
346         Result_t ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
347                                const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
348         {
349           // look up frame index node
350           IndexTableSegment::IndexEntry TmpEntry;
351
352           if ( KM_FAILURE(m_IndexAccess.Lookup(FrameNum, TmpEntry)) )
353             {
354               DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
355               return RESULT_RANGE;
356             }
357
358           // get absolute frame position and go read the frame's key and length
359           Result_t result = RESULT_OK;
360
361           if ( TmpEntry.StreamOffset != m_LastPosition )
362             {
363               m_LastPosition = TmpEntry.StreamOffset;
364               result = m_File.Seek(TmpEntry.StreamOffset);
365             }
366
367           if ( KM_SUCCESS(result) )
368             result = ReadEKLVPacket(FrameNum, FrameNum + 1, FrameBuf, EssenceUL, Ctx, HMAC);
369
370           return result;
371         }
372
373         // reads from current position
374         Result_t ReadEKLVPacket(ui32_t FrameNum, ui32_t SequenceNum, ASDCP::FrameBuffer& FrameBuf,
375                                 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
376         {
377           assert(m_Dict);
378           return Read_EKLV_Packet(m_File, *m_Dict, m_Info, m_LastPosition, m_CtFrameBuf,
379                                   FrameNum, SequenceNum, FrameBuf, EssenceUL, Ctx, HMAC);
380         }
381
382         // Get the position of a frame from a track file
383         Result_t LocateFrame(const ui64_t& body_offset,
384                              ui32_t FrameNum, Kumu::fpos_t& streamOffset,
385                              i8_t& temporalOffset, i8_t& keyFrameOffset)
386         {
387           // look up frame index node
388           IndexTableSegment::IndexEntry TmpEntry;
389
390           if ( KM_FAILURE(m_IndexAccess.Lookup(FrameNum, TmpEntry)) )
391             {
392               DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
393               return RESULT_RANGE;
394             }
395
396           // get frame position, temporal offset, and key frame ofset
397           streamOffset = body_offset + TmpEntry.StreamOffset;
398           temporalOffset = TmpEntry.TemporalOffset;
399           keyFrameOffset = TmpEntry.KeyFrameOffset;
400           
401           return RESULT_OK;
402         }
403
404         // Reads a Generic Stream Partition payload. Returns RESULT_FORMAT if the SID is
405         // not present in the  RIP, or if the actual partition at ByteOffset does not have
406         // a matching BodySID value. Encryption is not currently supported.
407         Result_t ReadGenericStreamPartitionPayload(const ui32_t sid, ASDCP::FrameBuffer& frame_buf,
408                                                    AESDecContext* Ctx, HMACContext* HMAC)
409         {
410           Kumu::fpos_t start_offset = 0, end_offset = 0;
411           ui32_t sequence = 0;
412
413           // locate SID, record the offset
414           // Count the sequence length in because this is the sequence
415           // value needed to  complete the HMAC.
416           ASDCP::MXF::RIP::const_pair_iterator i;
417           for ( i = m_RIP.PairArray.begin(); i != m_RIP.PairArray.end(); ++i, ++sequence )
418             {
419               if ( sid == i->BodySID )
420                 {
421                   start_offset = i->ByteOffset;
422                 }
423               else if ( start_offset != 0 )
424                 {
425                   end_offset = i->ByteOffset;
426                   break;
427                 }
428             }
429
430           if ( start_offset == 0 || end_offset == 0 )
431             {
432               DefaultLogSink().Error("Body SID not found: %d.\n", sid);
433               return RESULT_NOT_FOUND;
434             }
435
436           // Read the Partition header and then read the payload.
437           Result_t result = m_File.Seek(start_offset);
438
439           if ( KM_SUCCESS(result) )
440             {
441               result = frame_buf.Capacity(end_offset-start_offset);
442             }
443     
444           if ( KM_SUCCESS(result) )
445             {
446               // read the partition header
447               ASDCP::MXF::Partition GSPart(m_Dict);
448               result = GSPart.InitFromFile(m_File);
449
450               if ( KM_SUCCESS(result) )
451                 {
452                   // check the SID
453                   if ( GSPart.BodySID != sid )
454                     {
455                       DefaultLogSink().Error("Generic stream partition Body SID differs: %s\n", sid);
456                       result = RESULT_FORMAT;
457                     }
458                   else
459                     {
460                       result = ReadEKLVPacket(0, sequence, frame_buf, m_Dict->ul(MDD_GenericStream_DataElement), Ctx, HMAC);
461                     }
462                 }
463             }
464   
465           return result;
466         }
467
468         //
469         void Close()
470         {
471           m_File.Close();
472         }
473       };
474       
475       //------------------------------------------------------------------------------------------
476       //
477
478       //
479       template <class ClipT>
480         struct TrackSet
481         {
482           MXF::Track*    Track;
483           MXF::Sequence* Sequence;
484           ClipT*         Clip;
485
486         TrackSet() : Track(0), Sequence(0), Clip(0) {}
487         };
488
489       //
490       template <class PackageT, class ClipT>
491         TrackSet<ClipT>
492         CreateTrackAndSequence(OP1aHeader& Header, PackageT& Package, const std::string TrackName,
493                                const MXF::Rational& clip_edit_rate, const UL& Definition, ui32_t TrackID, const Dictionary*& Dict)
494         {
495           TrackSet<ClipT> NewTrack;
496
497           NewTrack.Track = new Track(Dict);
498           Header.AddChildObject(NewTrack.Track);
499           NewTrack.Track->EditRate = clip_edit_rate;
500           Package.Tracks.push_back(NewTrack.Track->InstanceUID);
501           NewTrack.Track->TrackID = TrackID;
502           NewTrack.Track->TrackName = TrackName.c_str();
503
504           NewTrack.Sequence = new Sequence(Dict);
505           Header.AddChildObject(NewTrack.Sequence);
506           NewTrack.Track->Sequence = NewTrack.Sequence->InstanceUID;
507           NewTrack.Sequence->DataDefinition = Definition;
508
509           return NewTrack;
510         }
511
512       //
513       template <class PackageT>
514         TrackSet<TimecodeComponent>
515         CreateTimecodeTrack(OP1aHeader& Header, PackageT& Package,
516                             const MXF::Rational& tc_edit_rate, ui32_t tc_frame_rate, ui64_t TCStart, const Dictionary*& Dict)
517         {
518           assert(Dict);
519           UL TCUL(Dict->ul(MDD_TimecodeDataDef));
520
521           TrackSet<TimecodeComponent> NewTrack =
522             CreateTrackAndSequence<PackageT, TimecodeComponent>(Header, Package, "Timecode Track",
523                                                                 tc_edit_rate, TCUL, 1, Dict);
524
525           NewTrack.Clip = new TimecodeComponent(Dict);
526           Header.AddChildObject(NewTrack.Clip);
527           NewTrack.Sequence->StructuralComponents.push_back(NewTrack.Clip->InstanceUID);
528           NewTrack.Clip->RoundedTimecodeBase = tc_frame_rate;
529           NewTrack.Clip->StartTimecode = TCStart;
530           NewTrack.Clip->DataDefinition = TCUL;
531
532           return NewTrack;
533         }
534
535
536       // state machine for mxf writer
537       enum WriterState_t {
538         ST_BEGIN,   // waiting for Open()
539         ST_INIT,    // waiting for SetSourceStream()
540         ST_READY,   // ready to write frames
541         ST_RUNNING, // one or more frames written
542         ST_FINAL,   // index written, file closed
543         ST_MAX
544       };
545
546       // implementation of h__WriterState class Goto_* methods
547 #define Goto_body(s1,s2)                        \
548       if ( m_State != (s1) ) {                  \
549         return RESULT_STATE;                    \
550       }                                         \
551       m_State = (s2);                           \
552       return RESULT_OK
553       //
554       class h__WriterState
555       {
556         ASDCP_NO_COPY_CONSTRUCT(h__WriterState);
557
558       public:
559         WriterState_t m_State;
560       h__WriterState() : m_State(ST_BEGIN) {}
561         ~h__WriterState() {}
562
563         inline bool     Test_BEGIN()   { return m_State == ST_BEGIN; }
564         inline bool     Test_INIT()    { return m_State == ST_INIT; }
565         inline bool     Test_READY()   { return m_State == ST_READY;}
566         inline bool     Test_RUNNING() { return m_State == ST_RUNNING; }
567         inline bool     Test_FINAL()   { return m_State == ST_FINAL; }
568         inline Result_t Goto_INIT()    { Goto_body(ST_BEGIN,   ST_INIT); }
569         inline Result_t Goto_READY()   { Goto_body(ST_INIT,    ST_READY); }
570         inline Result_t Goto_RUNNING() { Goto_body(ST_READY,   ST_RUNNING); }
571         inline Result_t Goto_FINAL()   { Goto_body(ST_RUNNING, ST_FINAL); }
572       };
573
574       //------------------------------------------------------------------------------------------
575       //
576
577       //
578       template <class HeaderType>
579         class TrackFileWriter
580       {
581         KM_NO_COPY_CONSTRUCT(TrackFileWriter);
582         TrackFileWriter();
583
584       public:
585         const Dictionary*  m_Dict;
586         Kumu::FileWriter   m_File;
587         ui32_t             m_HeaderSize;
588         HeaderType         m_HeaderPart;
589         RIP                m_RIP;
590
591         MaterialPackage*   m_MaterialPackage;
592         SourcePackage*     m_FilePackage;
593         ContentStorage*    m_ContentStorage;
594
595         FileDescriptor*    m_EssenceDescriptor;
596         std::list<InterchangeObject*> m_EssenceSubDescriptorList;
597
598         ui32_t             m_FramesWritten;
599         ui64_t             m_StreamOffset;
600         ASDCP::FrameBuffer m_CtFrameBuf;
601         h__WriterState     m_State;
602         WriterInfo         m_Info;
603
604         typedef std::list<ui64_t*> DurationElementList_t;
605         DurationElementList_t m_DurationUpdateList;
606
607       TrackFileWriter(const Dictionary& d) :
608         m_Dict(&d), m_HeaderSize(0), m_HeaderPart(m_Dict), m_RIP(m_Dict),
609           m_MaterialPackage(0), m_FilePackage(0), m_ContentStorage(0),
610           m_EssenceDescriptor(0), m_FramesWritten(0), m_StreamOffset(0)
611           {
612             default_md_object_init();
613           }
614
615         virtual ~TrackFileWriter() {
616           Close();
617         }
618
619         const MXF::RIP& GetRIP() const { return m_RIP; }
620
621         void InitHeader(const MXFVersion& mxf_ver)
622         {
623           assert(m_Dict);
624           assert(m_EssenceDescriptor);
625  
626           m_HeaderPart.m_Primer.ClearTagList();
627           m_HeaderPart.m_Preface = new Preface(m_Dict);
628           m_HeaderPart.AddChildObject(m_HeaderPart.m_Preface);
629
630           // Set the Operational Pattern label -- we're just starting and have no RIP or index,
631           // so we tell the world by using OP1a
632           m_HeaderPart.m_Preface->OperationalPattern = UL(m_Dict->ul(MDD_OP1a));
633           m_HeaderPart.OperationalPattern = m_HeaderPart.m_Preface->OperationalPattern;
634
635           if ( mxf_ver == MXFVersion_2004 )
636             {
637               m_HeaderPart.MinorVersion = MXF_2004_MinorVersion;
638               m_HeaderPart.m_Preface->Version = ((MXF_ObjectModelVersion << 8) | MXF_2004_MinorVersion);
639               m_HeaderPart.m_Preface->ObjectModelVersion = MXF_ObjectModelVersion;
640             }
641           else
642             {
643               assert(mxf_ver == MXFVersion_2011);
644               m_HeaderPart.MinorVersion = MXF_2011_MinorVersion;
645               m_HeaderPart.m_Preface->Version = ((MXF_ObjectModelVersion << 8) | MXF_2011_MinorVersion);
646               m_HeaderPart.m_Preface->ObjectModelVersion = MXF_ObjectModelVersion;
647             }
648
649           // Identification
650           Identification* Ident = new Identification(m_Dict);
651           m_HeaderPart.AddChildObject(Ident);
652           m_HeaderPart.m_Preface->Identifications.push_back(Ident->InstanceUID);
653
654           Kumu::GenRandomValue(Ident->ThisGenerationUID);
655           Ident->CompanyName = m_Info.CompanyName.c_str();
656           Ident->ProductName = m_Info.ProductName.c_str();
657           Ident->VersionString = m_Info.ProductVersion.c_str();
658           Ident->ProductUID.Set(m_Info.ProductUUID);
659           Ident->Platform = ASDCP_PLATFORM;
660
661           std::vector<int> version = version_split(Version());
662
663           Ident->ToolkitVersion.Major = version[0];
664           Ident->ToolkitVersion.Minor = version[1];
665           Ident->ToolkitVersion.Patch = version[2];
666           Ident->ToolkitVersion.Build = ASDCP_BUILD_NUMBER;
667           Ident->ToolkitVersion.Release = VersionType::RL_RELEASE;
668         }
669
670         //
671         void AddSourceClip(const MXF::Rational& clip_edit_rate,
672                            const MXF::Rational& tc_edit_rate, ui32_t TCFrameRate,
673                            const std::string& TrackName, const UL& EssenceUL,
674                            const UL& DataDefinition, const std::string& PackageLabel)
675         {
676           if ( m_ContentStorage == 0 )
677             {
678               m_ContentStorage = new ContentStorage(m_Dict);
679               m_HeaderPart.AddChildObject(m_ContentStorage);
680               m_HeaderPart.m_Preface->ContentStorage = m_ContentStorage->InstanceUID;
681             }
682
683           EssenceContainerData* ECD = new EssenceContainerData(m_Dict);
684           m_HeaderPart.AddChildObject(ECD);
685           m_ContentStorage->EssenceContainerData.push_back(ECD->InstanceUID);
686           ECD->IndexSID = 129;
687           ECD->BodySID = 1;
688
689           UUID assetUUID(m_Info.AssetUUID);
690           UMID SourcePackageUMID, MaterialPackageUMID;
691           SourcePackageUMID.MakeUMID(0x0f, assetUUID);
692           MaterialPackageUMID.MakeUMID(0x0f); // unidentified essence
693
694           //
695           // Material Package
696           //
697           m_MaterialPackage = new MaterialPackage(m_Dict);
698           m_MaterialPackage->Name = "Material Package";
699           m_MaterialPackage->PackageUID = MaterialPackageUMID;
700           m_HeaderPart.AddChildObject(m_MaterialPackage);
701           m_ContentStorage->Packages.push_back(m_MaterialPackage->InstanceUID);
702
703           TrackSet<TimecodeComponent> MPTCTrack =
704             CreateTimecodeTrack<MaterialPackage>(m_HeaderPart, *m_MaterialPackage,
705                                                  tc_edit_rate, TCFrameRate, 0, m_Dict);
706
707           MPTCTrack.Sequence->Duration.set_has_value();
708           m_DurationUpdateList.push_back(&(MPTCTrack.Sequence->Duration.get()));
709           MPTCTrack.Clip->Duration.set_has_value();
710           m_DurationUpdateList.push_back(&(MPTCTrack.Clip->Duration.get()));
711
712           TrackSet<SourceClip> MPTrack =
713             CreateTrackAndSequence<MaterialPackage, SourceClip>(m_HeaderPart, *m_MaterialPackage,
714                                                                 TrackName, clip_edit_rate, DataDefinition,
715                                                                 2, m_Dict);
716           MPTrack.Sequence->Duration.set_has_value();
717           m_DurationUpdateList.push_back(&(MPTrack.Sequence->Duration.get()));
718
719           MPTrack.Clip = new SourceClip(m_Dict);
720           m_HeaderPart.AddChildObject(MPTrack.Clip);
721           MPTrack.Sequence->StructuralComponents.push_back(MPTrack.Clip->InstanceUID);
722           MPTrack.Clip->DataDefinition = DataDefinition;
723           MPTrack.Clip->SourcePackageID = SourcePackageUMID;
724           MPTrack.Clip->SourceTrackID = 2;
725
726           MPTrack.Clip->Duration.set_has_value();
727           m_DurationUpdateList.push_back(&(MPTrack.Clip->Duration.get()));
728
729   
730           //
731           // File (Source) Package
732           //
733           m_FilePackage = new SourcePackage(m_Dict);
734           m_FilePackage->Name = PackageLabel.c_str();
735           m_FilePackage->PackageUID = SourcePackageUMID;
736           ECD->LinkedPackageUID = SourcePackageUMID;
737
738           m_HeaderPart.AddChildObject(m_FilePackage);
739           m_ContentStorage->Packages.push_back(m_FilePackage->InstanceUID);
740
741           TrackSet<TimecodeComponent> FPTCTrack =
742             CreateTimecodeTrack<SourcePackage>(m_HeaderPart, *m_FilePackage,
743                                                tc_edit_rate, TCFrameRate, 0, m_Dict);
744
745           FPTCTrack.Sequence->Duration.set_has_value();
746           m_DurationUpdateList.push_back(&(FPTCTrack.Sequence->Duration.get()));
747           FPTCTrack.Clip->Duration.set_has_value();
748           m_DurationUpdateList.push_back(&(FPTCTrack.Clip->Duration.get()));
749
750           TrackSet<SourceClip> FPTrack =
751             CreateTrackAndSequence<SourcePackage, SourceClip>(m_HeaderPart, *m_FilePackage,
752                                                               TrackName, clip_edit_rate, DataDefinition,
753                                                               2, m_Dict);
754
755           FPTrack.Sequence->Duration.set_has_value();
756           m_DurationUpdateList.push_back(&(FPTrack.Sequence->Duration.get()));
757
758           // Consult ST 379:2004 Sec. 6.3, "Element to track relationship" to see where "12" comes from.
759           FPTrack.Track->TrackNumber = KM_i32_BE(Kumu::cp2i<ui32_t>((EssenceUL.Value() + 12)));
760
761           FPTrack.Clip = new SourceClip(m_Dict);
762           m_HeaderPart.AddChildObject(FPTrack.Clip);
763           FPTrack.Sequence->StructuralComponents.push_back(FPTrack.Clip->InstanceUID);
764           FPTrack.Clip->DataDefinition = DataDefinition;
765
766           // for now we do not allow setting this value, so all files will be 'original'
767           FPTrack.Clip->SourceTrackID = 0;
768           FPTrack.Clip->SourcePackageID = NilUMID;
769
770           FPTrack.Clip->Duration.set_has_value();
771           m_DurationUpdateList.push_back(&(FPTrack.Clip->Duration.get()));
772
773           m_EssenceDescriptor->LinkedTrackID = FPTrack.Track->TrackID;
774         }
775
776         //
777         void AddEssenceDescriptor(const UL& WrappingUL)
778         {
779           //
780           // Essence Descriptor
781           //
782           m_EssenceDescriptor->EssenceContainer = WrappingUL;
783           m_HeaderPart.m_Preface->PrimaryPackage = m_FilePackage->InstanceUID;
784
785           //
786           // Essence Descriptors
787           //
788           assert(m_Dict);
789 #ifndef ASDCP_GCMULTI_PATCH
790           UL GenericContainerUL(m_Dict->ul(MDD_GCMulti));
791           m_HeaderPart.EssenceContainers.push_back(GenericContainerUL);
792 #endif
793
794           if ( m_Info.EncryptedEssence )
795             {
796               UL CryptEssenceUL(m_Dict->ul(MDD_EncryptedContainerLabel));
797               m_HeaderPart.EssenceContainers.push_back(CryptEssenceUL);
798               m_HeaderPart.m_Preface->DMSchemes.push_back(UL(m_Dict->ul(MDD_CryptographicFrameworkLabel)));
799               AddDmsCrypt(m_HeaderPart, *m_FilePackage, m_Info, WrappingUL, m_Dict);
800               //// TODO: fix DMSegment Duration value
801             }
802           else
803             {
804               m_HeaderPart.EssenceContainers.push_back(WrappingUL);
805             }
806
807           m_HeaderPart.m_Preface->EssenceContainers = m_HeaderPart.EssenceContainers;
808           m_HeaderPart.AddChildObject(m_EssenceDescriptor);
809
810           std::list<InterchangeObject*>::iterator sdli = m_EssenceSubDescriptorList.begin();
811           for ( ; sdli != m_EssenceSubDescriptorList.end(); sdli++ )
812             m_HeaderPart.AddChildObject(*sdli);
813
814           m_FilePackage->Descriptor = m_EssenceDescriptor->InstanceUID;
815         }
816
817         Result_t AddDmsGenericPartUtf8Text(const ASDCP::FrameBuffer& frame_buffer,
818                                            ASDCP::AESEncContext* enc = 0, ASDCP::HMACContext* hmac = 0)
819         {
820           Kumu::fpos_t previous_partition_offset = m_RIP.PairArray.back().ByteOffset;
821           Result_t result = AddDmsTrackGenericPartUtf8Text(m_File, m_HeaderPart, *m_FilePackage, m_RIP, m_Dict);
822
823           if ( KM_SUCCESS(result) )
824             {
825               // m_RIP now contains an entry (at the back) for the new generic stream
826               // (this entry was created during the call to AddDmsTrackGenericPartUtf8Text())
827               if ( m_File.Tell() != m_RIP.PairArray.back().ByteOffset )
828                 {
829                   DefaultLogSink().Error("File offset has moved since RIP modification. Unrecoverable error.\n");
830                   return RESULT_FAIL;
831                 }
832           
833               // create generic stream partition header
834               static UL GenericStream_DataElement(m_Dict->ul(MDD_GenericStream_DataElement));
835               ASDCP::MXF::Partition GSPart(m_Dict);
836
837               GSPart.MajorVersion = m_HeaderPart.MajorVersion;
838               GSPart.MinorVersion = m_HeaderPart.MinorVersion;
839               GSPart.ThisPartition = m_RIP.PairArray.back().ByteOffset;
840               GSPart.PreviousPartition = previous_partition_offset;
841               GSPart.OperationalPattern = m_HeaderPart.OperationalPattern;
842               GSPart.BodySID = m_RIP.PairArray.back().BodySID;
843               GSPart.EssenceContainers = m_HeaderPart.EssenceContainers;
844
845               static UL gs_part_ul(m_Dict->ul(MDD_GenericStreamPartition));
846               Result_t result = GSPart.WriteToFile(m_File, gs_part_ul);
847           
848               if ( KM_SUCCESS(result) )
849                 {
850                   result = Write_EKLV_Packet(m_File, *m_Dict, m_HeaderPart, m_Info, m_CtFrameBuf, m_FramesWritten,
851                                              m_StreamOffset, frame_buffer, GenericStream_DataElement.Value(),
852                                              MXF_BER_LENGTH, enc, hmac);
853                 }
854             }
855
856           return result;
857         }
858
859         //
860         void Close()
861         {
862           m_File.Close();
863         }
864
865       };
866       
867   }/// namespace MXF
868
869   //------------------------------------------------------------------------------------------
870   //
871
872   //
873   class h__ASDCPReader : public MXF::TrackFileReader<OP1aHeader, OPAtomIndexFooter>
874     {
875       ASDCP_NO_COPY_CONSTRUCT(h__ASDCPReader);
876       h__ASDCPReader();
877
878     public:
879       Partition m_BodyPart;
880
881       h__ASDCPReader(const Dictionary&);
882       virtual ~h__ASDCPReader();
883
884       Result_t OpenMXFRead(const std::string& filename);
885       Result_t ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
886                              const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC);
887       Result_t LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset,
888                            i8_t& temporalOffset, i8_t& keyFrameOffset);
889     };
890
891   //
892   class h__ASDCPWriter : public MXF::TrackFileWriter<OP1aHeader>
893     {
894       ASDCP_NO_COPY_CONSTRUCT(h__ASDCPWriter);
895       h__ASDCPWriter();
896
897     public:
898       Partition          m_BodyPart;
899       OPAtomIndexFooter  m_FooterPart;
900
901       h__ASDCPWriter(const Dictionary&);
902       virtual ~h__ASDCPWriter();
903
904       // all the above for a single source clip
905       Result_t WriteASDCPHeader(const std::string& PackageLabel, const UL& WrappingUL,
906                                 const std::string& TrackName, const UL& EssenceUL,
907                                 const UL& DataDefinition, const MXF::Rational& EditRate,
908                                 ui32_t TCFrameRate, ui32_t BytesPerEditUnit = 0);
909
910       Result_t CreateBodyPart(const MXF::Rational& EditRate, ui32_t BytesPerEditUnit = 0);
911       Result_t WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf,const byte_t* EssenceUL,
912                                const ui32_t& MinEssenceElementBerLength,
913                                AESEncContext* Ctx, HMACContext* HMAC);
914       Result_t WriteASDCPFooter();
915     };
916
917
918   // helper class for calculating Integrity Packs, used by WriteEKLVPacket() below.
919   //
920   class IntegrityPack
921     {
922     public:
923       byte_t Data[klv_intpack_size];
924
925       IntegrityPack() {
926         memset(Data, 0, klv_intpack_size);
927       }
928
929       ~IntegrityPack() {}
930
931       Result_t CalcValues(const ASDCP::FrameBuffer&, const byte_t* AssetID, ui32_t sequence, HMACContext* HMAC);
932       Result_t TestValues(const ASDCP::FrameBuffer&, const byte_t* AssetID, ui32_t sequence, HMACContext* HMAC);
933     };
934
935
936 } // namespace ASDCP
937
938 #endif // _AS_DCP_INTERNAL_H_
939
940
941 //
942 // end AS_DCP_internal.h
943 //