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