o Added support for SMPTE RDD 47 "ISXD Track File"
[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         {
404           Kumu::fpos_t start_offset = 0, end_offset = 0;
405           ui32_t sequence = 0;
406
407           // locate SID, record the offset
408           // Count the sequence length in because this is the sequence
409           // value needed to  complete the HMAC.
410           ASDCP::MXF::RIP::const_pair_iterator i;
411           for ( i = m_RIP.PairArray.begin(); i != m_RIP.PairArray.end(); ++i, ++sequence )
412             {
413               if ( sid == i->BodySID )
414                 {
415                   start_offset = i->ByteOffset;
416                 }
417               else if ( start_offset != 0 )
418                 {
419                   end_offset = i->ByteOffset;
420                   break;
421                 }
422             }
423
424           if ( start_offset == 0 || end_offset == 0 )
425             {
426               DefaultLogSink().Error("Body SID not found: %d.\n", sid);
427               return RESULT_NOT_FOUND;
428             }
429
430           // Read the Partition header and then read the payload.
431           Result_t result = m_File.Seek(start_offset);
432
433           if ( KM_SUCCESS(result) )
434             {
435               result = frame_buf.Capacity(end_offset-start_offset);
436             }
437     
438           if ( KM_SUCCESS(result) )
439             {
440               // read the partition header
441               ASDCP::MXF::Partition GSPart(m_Dict);
442               result = GSPart.InitFromFile(m_File);
443
444               if ( KM_SUCCESS(result) )
445                 {
446                   // check the SID
447                   if ( GSPart.BodySID != sid )
448                     {
449                       DefaultLogSink().Error("Generic stream partition Body SID differs: %s\n", sid);
450                       result = RESULT_FORMAT;
451                     }
452                   else
453                     {
454                       result = ReadEKLVPacket(0, sequence, frame_buf, m_Dict->ul(MDD_GenericStream_DataElement), 0, 0);
455                     }
456                 }
457             }
458   
459           return result;
460         }
461
462         //
463         void Close()
464         {
465           m_File.Close();
466         }
467       };
468       
469       //------------------------------------------------------------------------------------------
470       //
471
472       //
473       template <class ClipT>
474         struct TrackSet
475         {
476           MXF::Track*    Track;
477           MXF::Sequence* Sequence;
478           ClipT*         Clip;
479
480         TrackSet() : Track(0), Sequence(0), Clip(0) {}
481         };
482
483       //
484       template <class PackageT, class ClipT>
485         TrackSet<ClipT>
486         CreateTrackAndSequence(OP1aHeader& Header, PackageT& Package, const std::string TrackName,
487                                const MXF::Rational& clip_edit_rate, const UL& Definition, ui32_t TrackID, const Dictionary*& Dict)
488         {
489           TrackSet<ClipT> NewTrack;
490
491           NewTrack.Track = new Track(Dict);
492           Header.AddChildObject(NewTrack.Track);
493           NewTrack.Track->EditRate = clip_edit_rate;
494           Package.Tracks.push_back(NewTrack.Track->InstanceUID);
495           NewTrack.Track->TrackID = TrackID;
496           NewTrack.Track->TrackName = TrackName.c_str();
497
498           NewTrack.Sequence = new Sequence(Dict);
499           Header.AddChildObject(NewTrack.Sequence);
500           NewTrack.Track->Sequence = NewTrack.Sequence->InstanceUID;
501           NewTrack.Sequence->DataDefinition = Definition;
502
503           return NewTrack;
504         }
505
506       //
507       template <class PackageT>
508         TrackSet<TimecodeComponent>
509         CreateTimecodeTrack(OP1aHeader& Header, PackageT& Package,
510                             const MXF::Rational& tc_edit_rate, ui32_t tc_frame_rate, ui64_t TCStart, const Dictionary*& Dict)
511         {
512           assert(Dict);
513           UL TCUL(Dict->ul(MDD_TimecodeDataDef));
514
515           TrackSet<TimecodeComponent> NewTrack =
516             CreateTrackAndSequence<PackageT, TimecodeComponent>(Header, Package, "Timecode Track",
517                                                                 tc_edit_rate, TCUL, 1, Dict);
518
519           NewTrack.Clip = new TimecodeComponent(Dict);
520           Header.AddChildObject(NewTrack.Clip);
521           NewTrack.Sequence->StructuralComponents.push_back(NewTrack.Clip->InstanceUID);
522           NewTrack.Clip->RoundedTimecodeBase = tc_frame_rate;
523           NewTrack.Clip->StartTimecode = TCStart;
524           NewTrack.Clip->DataDefinition = TCUL;
525
526           return NewTrack;
527         }
528
529
530       // state machine for mxf writer
531       enum WriterState_t {
532         ST_BEGIN,   // waiting for Open()
533         ST_INIT,    // waiting for SetSourceStream()
534         ST_READY,   // ready to write frames
535         ST_RUNNING, // one or more frames written
536         ST_FINAL,   // index written, file closed
537         ST_MAX
538       };
539
540       // implementation of h__WriterState class Goto_* methods
541 #define Goto_body(s1,s2)                        \
542       if ( m_State != (s1) ) {                  \
543         return RESULT_STATE;                    \
544       }                                         \
545       m_State = (s2);                           \
546       return RESULT_OK
547       //
548       class h__WriterState
549       {
550         ASDCP_NO_COPY_CONSTRUCT(h__WriterState);
551
552       public:
553         WriterState_t m_State;
554       h__WriterState() : m_State(ST_BEGIN) {}
555         ~h__WriterState() {}
556
557         inline bool     Test_BEGIN()   { return m_State == ST_BEGIN; }
558         inline bool     Test_INIT()    { return m_State == ST_INIT; }
559         inline bool     Test_READY()   { return m_State == ST_READY;}
560         inline bool     Test_RUNNING() { return m_State == ST_RUNNING; }
561         inline bool     Test_FINAL()   { return m_State == ST_FINAL; }
562         inline Result_t Goto_INIT()    { Goto_body(ST_BEGIN,   ST_INIT); }
563         inline Result_t Goto_READY()   { Goto_body(ST_INIT,    ST_READY); }
564         inline Result_t Goto_RUNNING() { Goto_body(ST_READY,   ST_RUNNING); }
565         inline Result_t Goto_FINAL()   { Goto_body(ST_RUNNING, ST_FINAL); }
566       };
567
568       //------------------------------------------------------------------------------------------
569       //
570
571       //
572       template <class HeaderType>
573         class TrackFileWriter
574       {
575         KM_NO_COPY_CONSTRUCT(TrackFileWriter);
576         TrackFileWriter();
577
578       public:
579         const Dictionary*  m_Dict;
580         Kumu::FileWriter   m_File;
581         ui32_t             m_HeaderSize;
582         HeaderType         m_HeaderPart;
583         RIP                m_RIP;
584
585         MaterialPackage*   m_MaterialPackage;
586         SourcePackage*     m_FilePackage;
587         ContentStorage*    m_ContentStorage;
588
589         FileDescriptor*    m_EssenceDescriptor;
590         std::list<InterchangeObject*> m_EssenceSubDescriptorList;
591
592         ui32_t             m_FramesWritten;
593         ui64_t             m_StreamOffset;
594         ASDCP::FrameBuffer m_CtFrameBuf;
595         h__WriterState     m_State;
596         WriterInfo         m_Info;
597
598         typedef std::list<ui64_t*> DurationElementList_t;
599         DurationElementList_t m_DurationUpdateList;
600
601       TrackFileWriter(const Dictionary& d) :
602         m_Dict(&d), m_HeaderSize(0), m_HeaderPart(m_Dict), m_RIP(m_Dict),
603           m_MaterialPackage(0), m_FilePackage(0), m_ContentStorage(0),
604           m_EssenceDescriptor(0), m_FramesWritten(0), m_StreamOffset(0)
605           {
606             default_md_object_init();
607           }
608
609         virtual ~TrackFileWriter() {
610           Close();
611         }
612
613         const MXF::RIP& GetRIP() const { return m_RIP; }
614
615         void InitHeader(const MXFVersion& mxf_ver)
616         {
617           assert(m_Dict);
618           assert(m_EssenceDescriptor);
619  
620           m_HeaderPart.m_Primer.ClearTagList();
621           m_HeaderPart.m_Preface = new Preface(m_Dict);
622           m_HeaderPart.AddChildObject(m_HeaderPart.m_Preface);
623
624           // Set the Operational Pattern label -- we're just starting and have no RIP or index,
625           // so we tell the world by using OP1a
626           m_HeaderPart.m_Preface->OperationalPattern = UL(m_Dict->ul(MDD_OP1a));
627           m_HeaderPart.OperationalPattern = m_HeaderPart.m_Preface->OperationalPattern;
628
629           if ( mxf_ver == MXFVersion_2004 )
630             {
631               m_HeaderPart.MinorVersion = MXF_2004_MinorVersion;
632               m_HeaderPart.m_Preface->Version = ((MXF_ObjectModelVersion < 8) | MXF_2004_MinorVersion);
633               m_HeaderPart.m_Preface->ObjectModelVersion = MXF_ObjectModelVersion;
634             }
635           else
636             {
637               assert(mxf_ver == MXFVersion_2011);
638               m_HeaderPart.MinorVersion = MXF_2011_MinorVersion;
639               m_HeaderPart.m_Preface->Version = ((MXF_ObjectModelVersion < 8) | MXF_2011_MinorVersion);
640               m_HeaderPart.m_Preface->ObjectModelVersion = MXF_ObjectModelVersion;
641             }
642
643           // Identification
644           Identification* Ident = new Identification(m_Dict);
645           m_HeaderPart.AddChildObject(Ident);
646           m_HeaderPart.m_Preface->Identifications.push_back(Ident->InstanceUID);
647
648           Kumu::GenRandomValue(Ident->ThisGenerationUID);
649           Ident->CompanyName = m_Info.CompanyName.c_str();
650           Ident->ProductName = m_Info.ProductName.c_str();
651           Ident->VersionString = m_Info.ProductVersion.c_str();
652           Ident->ProductUID.Set(m_Info.ProductUUID);
653           Ident->Platform = ASDCP_PLATFORM;
654
655           std::vector<int> version = version_split(Version());
656
657           Ident->ToolkitVersion.Major = version[0];
658           Ident->ToolkitVersion.Minor = version[1];
659           Ident->ToolkitVersion.Patch = version[2];
660           Ident->ToolkitVersion.Build = ASDCP_BUILD_NUMBER;
661           Ident->ToolkitVersion.Release = VersionType::RL_RELEASE;
662         }
663
664         //
665         void AddSourceClip(const MXF::Rational& clip_edit_rate,
666                            const MXF::Rational& tc_edit_rate, ui32_t TCFrameRate,
667                            const std::string& TrackName, const UL& EssenceUL,
668                            const UL& DataDefinition, const std::string& PackageLabel)
669         {
670           if ( m_ContentStorage == 0 )
671             {
672               m_ContentStorage = new ContentStorage(m_Dict);
673               m_HeaderPart.AddChildObject(m_ContentStorage);
674               m_HeaderPart.m_Preface->ContentStorage = m_ContentStorage->InstanceUID;
675             }
676
677           EssenceContainerData* ECD = new EssenceContainerData(m_Dict);
678           m_HeaderPart.AddChildObject(ECD);
679           m_ContentStorage->EssenceContainerData.push_back(ECD->InstanceUID);
680           ECD->IndexSID = 129;
681           ECD->BodySID = 1;
682
683           UUID assetUUID(m_Info.AssetUUID);
684           UMID SourcePackageUMID, MaterialPackageUMID;
685           SourcePackageUMID.MakeUMID(0x0f, assetUUID);
686           MaterialPackageUMID.MakeUMID(0x0f); // unidentified essence
687
688           //
689           // Material Package
690           //
691           m_MaterialPackage = new MaterialPackage(m_Dict);
692           m_MaterialPackage->Name = "Material Package";
693           m_MaterialPackage->PackageUID = MaterialPackageUMID;
694           m_HeaderPart.AddChildObject(m_MaterialPackage);
695           m_ContentStorage->Packages.push_back(m_MaterialPackage->InstanceUID);
696
697           TrackSet<TimecodeComponent> MPTCTrack =
698             CreateTimecodeTrack<MaterialPackage>(m_HeaderPart, *m_MaterialPackage,
699                                                  tc_edit_rate, TCFrameRate, 0, m_Dict);
700
701           MPTCTrack.Sequence->Duration.set_has_value();
702           m_DurationUpdateList.push_back(&(MPTCTrack.Sequence->Duration.get()));
703           MPTCTrack.Clip->Duration.set_has_value();
704           m_DurationUpdateList.push_back(&(MPTCTrack.Clip->Duration.get()));
705
706           TrackSet<SourceClip> MPTrack =
707             CreateTrackAndSequence<MaterialPackage, SourceClip>(m_HeaderPart, *m_MaterialPackage,
708                                                                 TrackName, clip_edit_rate, DataDefinition,
709                                                                 2, m_Dict);
710           MPTrack.Sequence->Duration.set_has_value();
711           m_DurationUpdateList.push_back(&(MPTrack.Sequence->Duration.get()));
712
713           MPTrack.Clip = new SourceClip(m_Dict);
714           m_HeaderPart.AddChildObject(MPTrack.Clip);
715           MPTrack.Sequence->StructuralComponents.push_back(MPTrack.Clip->InstanceUID);
716           MPTrack.Clip->DataDefinition = DataDefinition;
717           MPTrack.Clip->SourcePackageID = SourcePackageUMID;
718           MPTrack.Clip->SourceTrackID = 2;
719
720           MPTrack.Clip->Duration.set_has_value();
721           m_DurationUpdateList.push_back(&(MPTrack.Clip->Duration.get()));
722
723   
724           //
725           // File (Source) Package
726           //
727           m_FilePackage = new SourcePackage(m_Dict);
728           m_FilePackage->Name = PackageLabel.c_str();
729           m_FilePackage->PackageUID = SourcePackageUMID;
730           ECD->LinkedPackageUID = SourcePackageUMID;
731
732           m_HeaderPart.AddChildObject(m_FilePackage);
733           m_ContentStorage->Packages.push_back(m_FilePackage->InstanceUID);
734
735           TrackSet<TimecodeComponent> FPTCTrack =
736             CreateTimecodeTrack<SourcePackage>(m_HeaderPart, *m_FilePackage,
737                                                tc_edit_rate, TCFrameRate, 0, m_Dict);
738
739           FPTCTrack.Sequence->Duration.set_has_value();
740           m_DurationUpdateList.push_back(&(FPTCTrack.Sequence->Duration.get()));
741           FPTCTrack.Clip->Duration.set_has_value();
742           m_DurationUpdateList.push_back(&(FPTCTrack.Clip->Duration.get()));
743
744           TrackSet<SourceClip> FPTrack =
745             CreateTrackAndSequence<SourcePackage, SourceClip>(m_HeaderPart, *m_FilePackage,
746                                                               TrackName, clip_edit_rate, DataDefinition,
747                                                               2, m_Dict);
748
749           FPTrack.Sequence->Duration.set_has_value();
750           m_DurationUpdateList.push_back(&(FPTrack.Sequence->Duration.get()));
751
752           // Consult ST 379:2004 Sec. 6.3, "Element to track relationship" to see where "12" comes from.
753           FPTrack.Track->TrackNumber = KM_i32_BE(Kumu::cp2i<ui32_t>((EssenceUL.Value() + 12)));
754
755           FPTrack.Clip = new SourceClip(m_Dict);
756           m_HeaderPart.AddChildObject(FPTrack.Clip);
757           FPTrack.Sequence->StructuralComponents.push_back(FPTrack.Clip->InstanceUID);
758           FPTrack.Clip->DataDefinition = DataDefinition;
759
760           // for now we do not allow setting this value, so all files will be 'original'
761           FPTrack.Clip->SourceTrackID = 0;
762           FPTrack.Clip->SourcePackageID = NilUMID;
763
764           FPTrack.Clip->Duration.set_has_value();
765           m_DurationUpdateList.push_back(&(FPTrack.Clip->Duration.get()));
766
767           m_EssenceDescriptor->LinkedTrackID = FPTrack.Track->TrackID;
768         }
769
770         //
771         void AddEssenceDescriptor(const UL& WrappingUL)
772         {
773           //
774           // Essence Descriptor
775           //
776           m_EssenceDescriptor->EssenceContainer = WrappingUL;
777           m_HeaderPart.m_Preface->PrimaryPackage = m_FilePackage->InstanceUID;
778
779           //
780           // Essence Descriptors
781           //
782           assert(m_Dict);
783           UL GenericContainerUL(m_Dict->ul(MDD_GCMulti));
784           m_HeaderPart.EssenceContainers.push_back(GenericContainerUL);
785
786           if ( m_Info.EncryptedEssence )
787             {
788               UL CryptEssenceUL(m_Dict->ul(MDD_EncryptedContainerLabel));
789               m_HeaderPart.EssenceContainers.push_back(CryptEssenceUL);
790               m_HeaderPart.m_Preface->DMSchemes.push_back(UL(m_Dict->ul(MDD_CryptographicFrameworkLabel)));
791               AddDmsCrypt(m_HeaderPart, *m_FilePackage, m_Info, WrappingUL, m_Dict);
792               //// TODO: fix DMSegment Duration value
793             }
794           else
795             {
796               m_HeaderPart.EssenceContainers.push_back(WrappingUL);
797             }
798
799           m_HeaderPart.m_Preface->EssenceContainers = m_HeaderPart.EssenceContainers;
800           m_HeaderPart.AddChildObject(m_EssenceDescriptor);
801
802           std::list<InterchangeObject*>::iterator sdli = m_EssenceSubDescriptorList.begin();
803           for ( ; sdli != m_EssenceSubDescriptorList.end(); sdli++ )
804             m_HeaderPart.AddChildObject(*sdli);
805
806           m_FilePackage->Descriptor = m_EssenceDescriptor->InstanceUID;
807         }
808
809         Result_t AddDmsGenericPartUtf8Text(const ASDCP::FrameBuffer& frame_buffer,
810                                            ASDCP::AESEncContext* enc = 0, ASDCP::HMACContext* hmac = 0)
811         {
812           Kumu::fpos_t previous_partition_offset = m_RIP.PairArray.back().ByteOffset;
813           Result_t result = AddDmsTrackGenericPartUtf8Text(m_File, m_HeaderPart, *m_FilePackage, m_RIP, m_Dict);
814
815           if ( KM_SUCCESS(result) )
816             {
817               // m_RIP now contains an entry (at the back) for the new generic stream
818               // (this entry was created during the call to AddDmsTrackGenericPartUtf8Text())
819               if ( m_File.Tell() != m_RIP.PairArray.back().ByteOffset )
820                 {
821                   DefaultLogSink().Error("File offset has moved since RIP modification. Unrecoverable error.\n");
822                   return RESULT_FAIL;
823                 }
824           
825               // create generic stream partition header
826               static UL GenericStream_DataElement(m_Dict->ul(MDD_GenericStream_DataElement));
827               ASDCP::MXF::Partition GSPart(m_Dict);
828
829               GSPart.MajorVersion = m_HeaderPart.MajorVersion;
830               GSPart.MinorVersion = m_HeaderPart.MinorVersion;
831               GSPart.ThisPartition = m_RIP.PairArray.back().ByteOffset;
832               GSPart.PreviousPartition = previous_partition_offset;
833               GSPart.OperationalPattern = m_HeaderPart.OperationalPattern;
834               GSPart.BodySID = m_RIP.PairArray.back().BodySID;
835               GSPart.EssenceContainers = m_HeaderPart.EssenceContainers;
836
837               static UL gs_part_ul(m_Dict->ul(MDD_GenericStreamPartition));
838               Result_t result = GSPart.WriteToFile(m_File, gs_part_ul);
839           
840               if ( KM_SUCCESS(result) )
841                 {
842                   result = Write_EKLV_Packet(m_File, *m_Dict, m_HeaderPart, m_Info, m_CtFrameBuf, m_FramesWritten,
843                                              m_StreamOffset, frame_buffer, GenericStream_DataElement.Value(), enc, hmac);
844                 }
845             }
846
847           return result;
848         }
849
850         //
851         void Close()
852         {
853           m_File.Close();
854         }
855
856       };
857       
858   }/// namespace MXF
859
860   //------------------------------------------------------------------------------------------
861   //
862
863   //
864   class h__ASDCPReader : public MXF::TrackFileReader<OP1aHeader, OPAtomIndexFooter>
865     {
866       ASDCP_NO_COPY_CONSTRUCT(h__ASDCPReader);
867       h__ASDCPReader();
868
869     public:
870       Partition m_BodyPart;
871
872       h__ASDCPReader(const Dictionary&);
873       virtual ~h__ASDCPReader();
874
875       Result_t OpenMXFRead(const std::string& filename);
876       Result_t ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
877                              const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC);
878       Result_t LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset,
879                            i8_t& temporalOffset, i8_t& keyFrameOffset);
880     };
881
882   //
883   class h__ASDCPWriter : public MXF::TrackFileWriter<OP1aHeader>
884     {
885       ASDCP_NO_COPY_CONSTRUCT(h__ASDCPWriter);
886       h__ASDCPWriter();
887
888     public:
889       Partition          m_BodyPart;
890       OPAtomIndexFooter  m_FooterPart;
891
892       h__ASDCPWriter(const Dictionary&);
893       virtual ~h__ASDCPWriter();
894
895       // all the above for a single source clip
896       Result_t WriteASDCPHeader(const std::string& PackageLabel, const UL& WrappingUL,
897                                 const std::string& TrackName, const UL& EssenceUL,
898                                 const UL& DataDefinition, const MXF::Rational& EditRate,
899                                 ui32_t TCFrameRate, ui32_t BytesPerEditUnit = 0);
900
901       Result_t CreateBodyPart(const MXF::Rational& EditRate, ui32_t BytesPerEditUnit = 0);
902       Result_t WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf,const byte_t* EssenceUL,
903                                AESEncContext* Ctx, HMACContext* HMAC);
904       Result_t WriteASDCPFooter();
905     };
906
907
908   // helper class for calculating Integrity Packs, used by WriteEKLVPacket() below.
909   //
910   class IntegrityPack
911     {
912     public:
913       byte_t Data[klv_intpack_size];
914
915       IntegrityPack() {
916         memset(Data, 0, klv_intpack_size);
917       }
918
919       ~IntegrityPack() {}
920
921       Result_t CalcValues(const ASDCP::FrameBuffer&, const byte_t* AssetID, ui32_t sequence, HMACContext* HMAC);
922       Result_t TestValues(const ASDCP::FrameBuffer&, const byte_t* AssetID, ui32_t sequence, HMACContext* HMAC);
923     };
924
925
926 } // namespace ASDCP
927
928 #endif // _AS_DCP_INTERNAL_H_
929
930
931 //
932 // end AS_DCP_internal.h
933 //