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