Fix comparisons between signed and unsigned values.
[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 ui64_t  Length() { return m_ValueLength; }
200       inline 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_Dict(&d), m_HeaderPart(m_Dict), m_IndexAccess(m_Dict), m_RIP(m_Dict)
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
253               if ( ASDCP_FAILURE(result) )
254                 {
255                   DefaultLogSink().Error("File contains no RIP\n");
256                 }
257               else if ( m_RIP.PairArray.empty() )
258                 {
259                   DefaultLogSink().Error("RIP contains no Pairs.\n");
260                 }
261             }
262           else
263             {
264               DefaultLogSink().Error("TrackFileReader::OpenMXFRead, SeekToRIP failed\n");
265             }
266
267           m_File.Seek(0);
268           result = m_HeaderPart.InitFromFile(m_File);
269
270           if ( KM_FAILURE(result) )
271             {
272               DefaultLogSink().Error("TrackFileReader::OpenMXFRead, header init failed\n");
273             }
274
275           return result;
276         }
277
278         //
279         Result_t InitInfo()
280         {
281           assert(m_Dict);
282           InterchangeObject* Object;
283
284           // Identification
285           Result_t result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object);
286
287           // Writer Info and SourcePackage
288           if ( KM_SUCCESS(result) )
289             {
290               MD_to_WriterInfo((Identification*)Object, m_Info);
291               result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object);
292             }
293
294           if ( KM_SUCCESS(result) )
295             {
296               SourcePackage* SP = (SourcePackage*)Object;
297               memcpy(m_Info.AssetUUID, SP->PackageUID.Value() + 16, UUIDlen);
298             }
299
300           // optional CryptographicContext
301           if ( KM_SUCCESS(result) )
302             {
303               Result_t cr_result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(CryptographicContext), &Object);
304
305               if ( KM_SUCCESS(cr_result) )
306                 MD_to_CryptoInfo((CryptographicContext*)Object, m_Info, *m_Dict);
307             }
308
309           return result;
310         }
311
312         // positions file before reading
313         // allows external control of index offset
314         Result_t ReadEKLVFrame(const ui64_t& body_offset,
315                                ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
316                                const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
317         {
318           // look up frame index node
319           IndexTableSegment::IndexEntry TmpEntry;
320
321           if ( KM_FAILURE(m_IndexAccess.Lookup(FrameNum, TmpEntry)) )
322             {
323               DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
324               return RESULT_RANGE;
325             }
326
327           // get relative frame position, apply offset and go read the frame's key and length
328           Kumu::fpos_t FilePosition = body_offset + TmpEntry.StreamOffset;
329           Result_t result = RESULT_OK;
330
331           if ( FilePosition != m_LastPosition )
332             {
333               m_LastPosition = FilePosition;
334               result = m_File.Seek(FilePosition);
335             }
336
337           if ( KM_SUCCESS(result) )
338             result = ReadEKLVPacket(FrameNum, FrameNum + 1, FrameBuf, EssenceUL, Ctx, HMAC);
339
340           return result;
341         }
342
343         // positions file before reading
344         // assumes "processed" index entries have absolute positions
345         Result_t ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
346                                const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
347         {
348           // look up frame index node
349           IndexTableSegment::IndexEntry TmpEntry;
350
351           if ( KM_FAILURE(m_IndexAccess.Lookup(FrameNum, TmpEntry)) )
352             {
353               DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
354               return RESULT_RANGE;
355             }
356
357           // get absolute frame position and go read the frame's key and length
358           Result_t result = RESULT_OK;
359
360           if ( static_cast<Kumu::fpos_t>(TmpEntry.StreamOffset) != m_LastPosition )
361             {
362               m_LastPosition = TmpEntry.StreamOffset;
363               result = m_File.Seek(TmpEntry.StreamOffset);
364             }
365
366           if ( KM_SUCCESS(result) )
367             result = ReadEKLVPacket(FrameNum, FrameNum + 1, FrameBuf, EssenceUL, Ctx, HMAC);
368
369           return result;
370         }
371
372         // reads from current position
373         Result_t ReadEKLVPacket(ui32_t FrameNum, ui32_t SequenceNum, ASDCP::FrameBuffer& FrameBuf,
374                                 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
375         {
376           assert(m_Dict);
377           return Read_EKLV_Packet(m_File, *m_Dict, m_Info, m_LastPosition, m_CtFrameBuf,
378                                   FrameNum, SequenceNum, FrameBuf, EssenceUL, Ctx, HMAC);
379         }
380
381         // Get the position of a frame from a track file
382         Result_t LocateFrame(const ui64_t& body_offset,
383                              ui32_t FrameNum, Kumu::fpos_t& streamOffset,
384                              i8_t& temporalOffset, i8_t& keyFrameOffset)
385         {
386           // look up frame index node
387           IndexTableSegment::IndexEntry TmpEntry;
388
389           if ( KM_FAILURE(m_IndexAccess.Lookup(FrameNum, TmpEntry)) )
390             {
391               DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
392               return RESULT_RANGE;
393             }
394
395           // get frame position, temporal offset, and key frame ofset
396           streamOffset = body_offset + TmpEntry.StreamOffset;
397           temporalOffset = TmpEntry.TemporalOffset;
398           keyFrameOffset = TmpEntry.KeyFrameOffset;
399           
400           return RESULT_OK;
401         }
402
403         // Reads a Generic Stream Partition payload. Returns RESULT_FORMAT if the SID is
404         // not present in the  RIP, or if the actual partition at ByteOffset does not have
405         // a matching BodySID value. Encryption is not currently supported.
406         Result_t ReadGenericStreamPartitionPayload(const ui32_t sid, ASDCP::FrameBuffer& frame_buf,
407                                                    AESDecContext* Ctx, HMACContext* HMAC)
408         {
409           Kumu::fpos_t start_offset = 0, end_offset = 0;
410           ui32_t sequence = 0;
411
412           // locate SID, record the offset
413           // Count the sequence length in because this is the sequence
414           // value needed to  complete the HMAC.
415           ASDCP::MXF::RIP::const_pair_iterator i;
416           for ( i = m_RIP.PairArray.begin(); i != m_RIP.PairArray.end(); ++i)
417             {
418               if ( sid == i->BodySID )
419                 {
420                   assert( start_offset == 0);
421                   start_offset = i->ByteOffset;
422                 }
423               else if ( start_offset != 0 )
424                 {
425                   end_offset = i->ByteOffset;
426                   break;
427                 }
428
429               if ( i->BodySID > 0 )
430                 {
431                   ++sequence;
432                 }
433             }
434
435           if ( start_offset == 0 || end_offset == 0 )
436             {
437               DefaultLogSink().Error("Body SID not found: %d.\n", sid);
438               return RESULT_NOT_FOUND;
439             }
440
441           // Read the Partition header and then read the payload.
442           Result_t result = m_File.Seek(start_offset);
443
444           if ( KM_SUCCESS(result) )
445             {
446               result = frame_buf.Capacity(end_offset-start_offset);
447             }
448     
449           if ( KM_SUCCESS(result) )
450             {
451               // read the partition header
452               ASDCP::MXF::Partition GSPart(m_Dict);
453               result = GSPart.InitFromFile(m_File);
454
455               if ( KM_SUCCESS(result) )
456                 {
457                   // check the SID
458                   if ( GSPart.BodySID != sid )
459                     {
460                       DefaultLogSink().Error("Generic stream partition Body SID differs: %s\n", sid);
461                       result = RESULT_FORMAT;
462                     }
463                   else
464                     {
465                       result = ReadEKLVPacket(0, sequence, frame_buf, m_Dict->ul(MDD_GenericStream_DataElement), Ctx, HMAC);
466                     }
467                 }
468             }
469   
470           return result;
471         }
472
473         //
474         void Close()
475         {
476           m_File.Close();
477         }
478       };
479       
480       //------------------------------------------------------------------------------------------
481       //
482
483       //
484       template <class ClipT>
485         struct TrackSet
486         {
487           MXF::Track*    Track;
488           MXF::Sequence* Sequence;
489           ClipT*         Clip;
490
491         TrackSet() : Track(0), Sequence(0), Clip(0) {}
492         };
493
494       //
495       template <class PackageT, class ClipT>
496         TrackSet<ClipT>
497         CreateTrackAndSequence(OP1aHeader& Header, PackageT& Package, const std::string TrackName,
498                                const MXF::Rational& clip_edit_rate, const UL& Definition, ui32_t TrackID, const Dictionary*& Dict)
499         {
500           TrackSet<ClipT> NewTrack;
501
502           NewTrack.Track = new Track(Dict);
503           Header.AddChildObject(NewTrack.Track);
504           NewTrack.Track->EditRate = clip_edit_rate;
505           Package.Tracks.push_back(NewTrack.Track->InstanceUID);
506           NewTrack.Track->TrackID = TrackID;
507           NewTrack.Track->TrackName = TrackName.c_str();
508
509           NewTrack.Sequence = new Sequence(Dict);
510           Header.AddChildObject(NewTrack.Sequence);
511           NewTrack.Track->Sequence = NewTrack.Sequence->InstanceUID;
512           NewTrack.Sequence->DataDefinition = Definition;
513
514           return NewTrack;
515         }
516
517       //
518       template <class PackageT>
519         TrackSet<TimecodeComponent>
520         CreateTimecodeTrack(OP1aHeader& Header, PackageT& Package,
521                             const MXF::Rational& tc_edit_rate, ui32_t tc_frame_rate, ui64_t TCStart, const Dictionary*& Dict)
522         {
523           assert(Dict);
524           UL TCUL(Dict->ul(MDD_TimecodeDataDef));
525
526           TrackSet<TimecodeComponent> NewTrack =
527             CreateTrackAndSequence<PackageT, TimecodeComponent>(Header, Package, "Timecode Track",
528                                                                 tc_edit_rate, TCUL, 1, Dict);
529
530           NewTrack.Clip = new TimecodeComponent(Dict);
531           Header.AddChildObject(NewTrack.Clip);
532           NewTrack.Sequence->StructuralComponents.push_back(NewTrack.Clip->InstanceUID);
533           NewTrack.Clip->RoundedTimecodeBase = tc_frame_rate;
534           NewTrack.Clip->StartTimecode = TCStart;
535           NewTrack.Clip->DataDefinition = TCUL;
536
537           return NewTrack;
538         }
539
540
541       // state machine for mxf writer
542       enum WriterState_t {
543         ST_BEGIN,   // waiting for Open()
544         ST_INIT,    // waiting for SetSourceStream()
545         ST_READY,   // ready to write frames
546         ST_RUNNING, // one or more frames written
547         ST_FINAL,   // index written, file closed
548         ST_MAX
549       };
550
551       // implementation of h__WriterState class Goto_* methods
552 #define Goto_body(s1,s2)                        \
553       if ( m_State != (s1) ) {                  \
554         return RESULT_STATE;                    \
555       }                                         \
556       m_State = (s2);                           \
557       return RESULT_OK
558       //
559       class h__WriterState
560       {
561         ASDCP_NO_COPY_CONSTRUCT(h__WriterState);
562
563       public:
564         WriterState_t m_State;
565       h__WriterState() : m_State(ST_BEGIN) {}
566         ~h__WriterState() {}
567
568         inline bool     Test_BEGIN()   { return m_State == ST_BEGIN; }
569         inline bool     Test_INIT()    { return m_State == ST_INIT; }
570         inline bool     Test_READY()   { return m_State == ST_READY;}
571         inline bool     Test_RUNNING() { return m_State == ST_RUNNING; }
572         inline bool     Test_FINAL()   { return m_State == ST_FINAL; }
573         inline Result_t Goto_INIT()    { Goto_body(ST_BEGIN,   ST_INIT); }
574         inline Result_t Goto_READY()   { Goto_body(ST_INIT,    ST_READY); }
575         inline Result_t Goto_RUNNING() { Goto_body(ST_READY,   ST_RUNNING); }
576         inline Result_t Goto_FINAL()   { Goto_body(ST_RUNNING, ST_FINAL); }
577       };
578
579       //------------------------------------------------------------------------------------------
580       //
581
582       //
583       template <class HeaderType>
584         class TrackFileWriter
585       {
586         KM_NO_COPY_CONSTRUCT(TrackFileWriter);
587         TrackFileWriter();
588
589       public:
590         const Dictionary*  m_Dict;
591         Kumu::FileWriter   m_File;
592         ui32_t             m_HeaderSize;
593         HeaderType         m_HeaderPart;
594         RIP                m_RIP;
595
596         MaterialPackage*   m_MaterialPackage;
597         SourcePackage*     m_FilePackage;
598         ContentStorage*    m_ContentStorage;
599
600         FileDescriptor*    m_EssenceDescriptor;
601         std::list<InterchangeObject*> m_EssenceSubDescriptorList;
602
603         ui32_t             m_FramesWritten;
604         ui64_t             m_StreamOffset;
605         ASDCP::FrameBuffer m_CtFrameBuf;
606         h__WriterState     m_State;
607         WriterInfo         m_Info;
608
609         typedef std::list<ui64_t*> DurationElementList_t;
610         DurationElementList_t m_DurationUpdateList;
611
612       TrackFileWriter(const Dictionary& d) :
613         m_Dict(&d), m_HeaderSize(0), m_HeaderPart(m_Dict), m_RIP(m_Dict),
614           m_MaterialPackage(0), m_FilePackage(0), m_ContentStorage(0),
615           m_EssenceDescriptor(0), m_FramesWritten(0), m_StreamOffset(0)
616           {
617             default_md_object_init();
618           }
619
620         virtual ~TrackFileWriter() {
621           Close();
622         }
623
624         const MXF::RIP& GetRIP() const { return m_RIP; }
625
626         void InitHeader(const MXFVersion& mxf_ver)
627         {
628           assert(m_Dict);
629           assert(m_EssenceDescriptor);
630  
631           m_HeaderPart.m_Primer.ClearTagList();
632           m_HeaderPart.m_Preface = new Preface(m_Dict);
633           m_HeaderPart.AddChildObject(m_HeaderPart.m_Preface);
634
635           // Set the Operational Pattern label -- we're just starting and have no RIP or index,
636           // so we tell the world by using OP1a
637           m_HeaderPart.m_Preface->OperationalPattern = UL(m_Dict->ul(MDD_OP1a));
638           m_HeaderPart.OperationalPattern = m_HeaderPart.m_Preface->OperationalPattern;
639
640           if ( mxf_ver == MXFVersion_2004 )
641             {
642               m_HeaderPart.MinorVersion = MXF_2004_MinorVersion;
643               m_HeaderPart.m_Preface->Version = ((MXF_ObjectModelVersion << 8) | MXF_2004_MinorVersion);
644               m_HeaderPart.m_Preface->ObjectModelVersion = MXF_ObjectModelVersion;
645             }
646           else
647             {
648               assert(mxf_ver == MXFVersion_2011);
649               m_HeaderPart.MinorVersion = MXF_2011_MinorVersion;
650               m_HeaderPart.m_Preface->Version = ((MXF_ObjectModelVersion << 8) | MXF_2011_MinorVersion);
651               m_HeaderPart.m_Preface->ObjectModelVersion = MXF_ObjectModelVersion;
652             }
653
654           // Identification
655           Identification* Ident = new Identification(m_Dict);
656           m_HeaderPart.AddChildObject(Ident);
657           m_HeaderPart.m_Preface->Identifications.push_back(Ident->InstanceUID);
658
659           Kumu::GenRandomValue(Ident->ThisGenerationUID);
660           Ident->CompanyName = m_Info.CompanyName.c_str();
661           Ident->ProductName = m_Info.ProductName.c_str();
662           Ident->VersionString = m_Info.ProductVersion.c_str();
663           Ident->ProductUID.Set(m_Info.ProductUUID);
664           Ident->Platform = ASDCP_PLATFORM;
665
666           std::vector<int> version = version_split(Version());
667
668           Ident->ToolkitVersion.Major = version[0];
669           Ident->ToolkitVersion.Minor = version[1];
670           Ident->ToolkitVersion.Patch = version[2];
671           Ident->ToolkitVersion.Build = ASDCP_BUILD_NUMBER;
672           Ident->ToolkitVersion.Release = VersionType::RL_RELEASE;
673         }
674
675         //
676         void AddSourceClip(const MXF::Rational& clip_edit_rate,
677                            const MXF::Rational& tc_edit_rate, ui32_t TCFrameRate,
678                            const std::string& TrackName, const UL& EssenceUL,
679                            const UL& DataDefinition, const std::string& PackageLabel)
680         {
681           if ( m_ContentStorage == 0 )
682             {
683               m_ContentStorage = new ContentStorage(m_Dict);
684               m_HeaderPart.AddChildObject(m_ContentStorage);
685               m_HeaderPart.m_Preface->ContentStorage = m_ContentStorage->InstanceUID;
686             }
687
688           EssenceContainerData* ECD = new EssenceContainerData(m_Dict);
689           m_HeaderPart.AddChildObject(ECD);
690           m_ContentStorage->EssenceContainerData.push_back(ECD->InstanceUID);
691           ECD->IndexSID = 129;
692           ECD->BodySID = 1;
693
694           UUID assetUUID(m_Info.AssetUUID);
695           UMID SourcePackageUMID, MaterialPackageUMID;
696           SourcePackageUMID.MakeUMID(0x0f, assetUUID);
697           MaterialPackageUMID.MakeUMID(0x0f); // unidentified essence
698
699           //
700           // Material Package
701           //
702           m_MaterialPackage = new MaterialPackage(m_Dict);
703           m_MaterialPackage->Name = "Material Package";
704           m_MaterialPackage->PackageUID = MaterialPackageUMID;
705           m_HeaderPart.AddChildObject(m_MaterialPackage);
706           m_ContentStorage->Packages.push_back(m_MaterialPackage->InstanceUID);
707
708           TrackSet<TimecodeComponent> MPTCTrack =
709             CreateTimecodeTrack<MaterialPackage>(m_HeaderPart, *m_MaterialPackage,
710                                                  tc_edit_rate, TCFrameRate, 0, m_Dict);
711
712           MPTCTrack.Sequence->Duration.set_has_value();
713           m_DurationUpdateList.push_back(&(MPTCTrack.Sequence->Duration.get()));
714           MPTCTrack.Clip->Duration.set_has_value();
715           m_DurationUpdateList.push_back(&(MPTCTrack.Clip->Duration.get()));
716
717           TrackSet<SourceClip> MPTrack =
718             CreateTrackAndSequence<MaterialPackage, SourceClip>(m_HeaderPart, *m_MaterialPackage,
719                                                                 TrackName, clip_edit_rate, DataDefinition,
720                                                                 2, m_Dict);
721           MPTrack.Sequence->Duration.set_has_value();
722           m_DurationUpdateList.push_back(&(MPTrack.Sequence->Duration.get()));
723
724           MPTrack.Clip = new SourceClip(m_Dict);
725           m_HeaderPart.AddChildObject(MPTrack.Clip);
726           MPTrack.Sequence->StructuralComponents.push_back(MPTrack.Clip->InstanceUID);
727           MPTrack.Clip->DataDefinition = DataDefinition;
728           MPTrack.Clip->SourcePackageID = SourcePackageUMID;
729           MPTrack.Clip->SourceTrackID = 2;
730
731           MPTrack.Clip->Duration.set_has_value();
732           m_DurationUpdateList.push_back(&(MPTrack.Clip->Duration.get()));
733
734   
735           //
736           // File (Source) Package
737           //
738           m_FilePackage = new SourcePackage(m_Dict);
739           m_FilePackage->Name = PackageLabel.c_str();
740           m_FilePackage->PackageUID = SourcePackageUMID;
741           ECD->LinkedPackageUID = SourcePackageUMID;
742
743           m_HeaderPart.AddChildObject(m_FilePackage);
744           m_ContentStorage->Packages.push_back(m_FilePackage->InstanceUID);
745
746           TrackSet<TimecodeComponent> FPTCTrack =
747             CreateTimecodeTrack<SourcePackage>(m_HeaderPart, *m_FilePackage,
748                                                tc_edit_rate, TCFrameRate, 0, m_Dict);
749
750           FPTCTrack.Sequence->Duration.set_has_value();
751           m_DurationUpdateList.push_back(&(FPTCTrack.Sequence->Duration.get()));
752           FPTCTrack.Clip->Duration.set_has_value();
753           m_DurationUpdateList.push_back(&(FPTCTrack.Clip->Duration.get()));
754
755           TrackSet<SourceClip> FPTrack =
756             CreateTrackAndSequence<SourcePackage, SourceClip>(m_HeaderPart, *m_FilePackage,
757                                                               TrackName, clip_edit_rate, DataDefinition,
758                                                               2, m_Dict);
759
760           FPTrack.Sequence->Duration.set_has_value();
761           m_DurationUpdateList.push_back(&(FPTrack.Sequence->Duration.get()));
762
763           // Consult ST 379:2004 Sec. 6.3, "Element to track relationship" to see where "12" comes from.
764           FPTrack.Track->TrackNumber = KM_i32_BE(Kumu::cp2i<ui32_t>((EssenceUL.Value() + 12)));
765
766           FPTrack.Clip = new SourceClip(m_Dict);
767           m_HeaderPart.AddChildObject(FPTrack.Clip);
768           FPTrack.Sequence->StructuralComponents.push_back(FPTrack.Clip->InstanceUID);
769           FPTrack.Clip->DataDefinition = DataDefinition;
770
771           // for now we do not allow setting this value, so all files will be 'original'
772           FPTrack.Clip->SourceTrackID = 0;
773           FPTrack.Clip->SourcePackageID = NilUMID;
774
775           FPTrack.Clip->Duration.set_has_value();
776           m_DurationUpdateList.push_back(&(FPTrack.Clip->Duration.get()));
777
778           m_EssenceDescriptor->LinkedTrackID = FPTrack.Track->TrackID;
779         }
780
781         //
782         void AddEssenceDescriptor(const UL& WrappingUL)
783         {
784           //
785           // Essence Descriptor
786           //
787           m_EssenceDescriptor->EssenceContainer = WrappingUL;
788           m_HeaderPart.m_Preface->PrimaryPackage = m_FilePackage->InstanceUID;
789
790           //
791           // Essence Descriptors
792           //
793           assert(m_Dict);
794 #ifndef ASDCP_GCMULTI_PATCH
795           UL GenericContainerUL(m_Dict->ul(MDD_GCMulti));
796           m_HeaderPart.EssenceContainers.push_back(GenericContainerUL);
797 #endif
798
799           if ( m_Info.EncryptedEssence )
800             {
801               UL CryptEssenceUL(m_Dict->ul(MDD_EncryptedContainerLabel));
802               m_HeaderPart.EssenceContainers.push_back(CryptEssenceUL);
803               m_HeaderPart.m_Preface->DMSchemes.push_back(UL(m_Dict->ul(MDD_CryptographicFrameworkLabel)));
804               AddDmsCrypt(m_HeaderPart, *m_FilePackage, m_Info, WrappingUL, m_Dict);
805               //// TODO: fix DMSegment Duration value
806             }
807           else
808             {
809               m_HeaderPart.EssenceContainers.push_back(WrappingUL);
810             }
811
812           m_HeaderPart.m_Preface->EssenceContainers = m_HeaderPart.EssenceContainers;
813           m_HeaderPart.AddChildObject(m_EssenceDescriptor);
814
815           std::list<InterchangeObject*>::iterator sdli = m_EssenceSubDescriptorList.begin();
816           for ( ; sdli != m_EssenceSubDescriptorList.end(); sdli++ )
817             m_HeaderPart.AddChildObject(*sdli);
818
819           m_FilePackage->Descriptor = m_EssenceDescriptor->InstanceUID;
820         }
821
822         Result_t AddDmsGenericPartUtf8Text(const ASDCP::FrameBuffer& frame_buffer,
823                                            ASDCP::AESEncContext* enc = 0, ASDCP::HMACContext* hmac = 0)
824         {
825           Kumu::fpos_t previous_partition_offset = m_RIP.PairArray.back().ByteOffset;
826           Result_t result = AddDmsTrackGenericPartUtf8Text(m_File, m_HeaderPart, *m_FilePackage, m_RIP, m_Dict);
827
828           if ( KM_SUCCESS(result) )
829             {
830               // m_RIP now contains an entry (at the back) for the new generic stream
831               // (this entry was created during the call to AddDmsTrackGenericPartUtf8Text())
832               if ( m_File.Tell() != m_RIP.PairArray.back().ByteOffset )
833                 {
834                   DefaultLogSink().Error("File offset has moved since RIP modification. Unrecoverable error.\n");
835                   return RESULT_FAIL;
836                 }
837           
838               // create generic stream partition header
839               static UL GenericStream_DataElement(m_Dict->ul(MDD_GenericStream_DataElement));
840               ASDCP::MXF::Partition GSPart(m_Dict);
841
842               GSPart.MajorVersion = m_HeaderPart.MajorVersion;
843               GSPart.MinorVersion = m_HeaderPart.MinorVersion;
844               GSPart.ThisPartition = m_RIP.PairArray.back().ByteOffset;
845               GSPart.PreviousPartition = previous_partition_offset;
846               GSPart.OperationalPattern = m_HeaderPart.OperationalPattern;
847               GSPart.BodySID = m_RIP.PairArray.back().BodySID;
848               GSPart.EssenceContainers = m_HeaderPart.EssenceContainers;
849
850               static UL gs_part_ul(m_Dict->ul(MDD_GenericStreamPartition));
851               Result_t result = GSPart.WriteToFile(m_File, gs_part_ul);
852           
853               if ( KM_SUCCESS(result) )
854                 {
855                   result = Write_EKLV_Packet(m_File, *m_Dict, m_HeaderPart, m_Info, m_CtFrameBuf, m_FramesWritten,
856                                              m_StreamOffset, frame_buffer, GenericStream_DataElement.Value(),
857                                              MXF_BER_LENGTH, enc, hmac);
858                 }
859             }
860
861           return result;
862         }
863
864         //
865         void Close()
866         {
867           m_File.Close();
868         }
869
870       };
871       
872   }/// namespace MXF
873
874   //------------------------------------------------------------------------------------------
875   //
876
877   //
878   class h__ASDCPReader : public MXF::TrackFileReader<OP1aHeader, OPAtomIndexFooter>
879     {
880       ASDCP_NO_COPY_CONSTRUCT(h__ASDCPReader);
881       h__ASDCPReader();
882
883     public:
884       Partition m_BodyPart;
885
886       h__ASDCPReader(const Dictionary&);
887       virtual ~h__ASDCPReader();
888
889       Result_t OpenMXFRead(const std::string& filename);
890       Result_t ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
891                              const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC);
892       Result_t LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset,
893                            i8_t& temporalOffset, i8_t& keyFrameOffset);
894     };
895
896   //
897   class h__ASDCPWriter : public MXF::TrackFileWriter<OP1aHeader>
898     {
899       ASDCP_NO_COPY_CONSTRUCT(h__ASDCPWriter);
900       h__ASDCPWriter();
901
902     public:
903       Partition          m_BodyPart;
904       OPAtomIndexFooter  m_FooterPart;
905
906       h__ASDCPWriter(const Dictionary&);
907       virtual ~h__ASDCPWriter();
908
909       // all the above for a single source clip
910       Result_t WriteASDCPHeader(const std::string& PackageLabel, const UL& WrappingUL,
911                                 const std::string& TrackName, const UL& EssenceUL,
912                                 const UL& DataDefinition, const MXF::Rational& EditRate,
913                                 ui32_t TCFrameRate, ui32_t BytesPerEditUnit = 0);
914
915       Result_t CreateBodyPart(const MXF::Rational& EditRate, ui32_t BytesPerEditUnit = 0);
916       Result_t WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf,const byte_t* EssenceUL,
917                                const ui32_t& MinEssenceElementBerLength,
918                                AESEncContext* Ctx, HMACContext* HMAC);
919       Result_t WriteASDCPFooter();
920     };
921
922
923   // helper class for calculating Integrity Packs, used by WriteEKLVPacket() below.
924   //
925   class IntegrityPack
926     {
927     public:
928       byte_t Data[klv_intpack_size];
929
930       IntegrityPack() {
931         memset(Data, 0, klv_intpack_size);
932       }
933
934       ~IntegrityPack() {}
935
936       Result_t CalcValues(const ASDCP::FrameBuffer&, const byte_t* AssetID, ui32_t sequence, HMACContext* HMAC);
937       Result_t TestValues(const ASDCP::FrameBuffer&, const byte_t* AssetID, ui32_t sequence, HMACContext* HMAC);
938     };
939
940
941 } // namespace ASDCP
942
943 #endif // _AS_DCP_INTERNAL_H_
944
945
946 //
947 // end AS_DCP_internal.h
948 //