big change rollup
[asdcplib.git] / src / AS_DCP_internal.h
1 /*
2 Copyright (c) 2004-2012, 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 std;
42 using namespace ASDCP;
43 using namespace ASDCP::MXF;
44
45 #ifdef DEFAULT_MD_DECL
46 ASDCP::MXF::OPAtomHeader *g_OPAtomHeader;
47 ASDCP::MXF::OPAtomIndexFooter *g_OPAtomIndexFooter;
48 #else
49 extern MXF::OPAtomHeader *g_OPAtomHeader;
50 extern MXF::OPAtomIndexFooter *g_OPAtomIndexFooter;
51 #endif
52
53
54 namespace ASDCP
55 {
56   void default_md_object_init();
57
58   //
59   static std::vector<int>
60     version_split(const char* str)
61   {
62     std::vector<int> result;
63     const char* pstr = str;
64     const char* r = strchr(pstr, '.');
65     
66     while ( r != 0 )
67       {
68         assert(r >= pstr);
69         if ( r > pstr )
70           result.push_back(atoi(pstr));
71
72         pstr = r + 1;
73         r = strchr(pstr, '.');
74       }
75
76     if( strlen(pstr) > 0 )
77       result.push_back(atoi(pstr));
78
79     assert(result.size() == 3);
80     return result;
81   }
82
83   // constant values used to calculate KLV and EKLV packet sizes
84   static const ui32_t klv_cryptinfo_size =
85     MXF_BER_LENGTH
86     + UUIDlen /* ContextID */
87     + MXF_BER_LENGTH
88     + sizeof(ui64_t) /* PlaintextOffset */
89     + MXF_BER_LENGTH
90     + SMPTE_UL_LENGTH /* SourceKey */
91     + MXF_BER_LENGTH
92     + sizeof(ui64_t) /* SourceLength */
93     + MXF_BER_LENGTH /* ESV length */ ;
94
95   static const ui32_t klv_intpack_size =
96     MXF_BER_LENGTH
97     + UUIDlen /* TrackFileID */
98     + MXF_BER_LENGTH
99     + sizeof(ui64_t) /* SequenceNumber */
100     + MXF_BER_LENGTH
101     + 20; /* HMAC length*/
102
103   // calculate size of encrypted essence with IV, CheckValue, and padding
104   inline ui32_t
105     calc_esv_length(ui32_t source_length, ui32_t plaintext_offset)
106     {
107       ui32_t ct_size = source_length - plaintext_offset;
108       ui32_t diff = ct_size % CBC_BLOCK_SIZE;
109       ui32_t block_size = ct_size - diff;
110       return plaintext_offset + block_size + (CBC_BLOCK_SIZE * 3);
111     }
112
113   // the check value for EKLV packets
114   // CHUKCHUKCHUKCHUK
115   static const byte_t ESV_CheckValue[CBC_BLOCK_SIZE] =
116   { 0x43, 0x48, 0x55, 0x4b, 0x43, 0x48, 0x55, 0x4b,
117     0x43, 0x48, 0x55, 0x4b, 0x43, 0x48, 0x55, 0x4b };
118
119   //------------------------------------------------------------------------------------------
120   //
121
122   Result_t MD_to_WriterInfo(MXF::Identification*, WriterInfo&);
123   Result_t MD_to_CryptoInfo(MXF::CryptographicContext*, WriterInfo&, const Dictionary&);
124   Result_t EncryptFrameBuffer(const ASDCP::FrameBuffer&, ASDCP::FrameBuffer&, AESEncContext*);
125   Result_t DecryptFrameBuffer(const ASDCP::FrameBuffer&, ASDCP::FrameBuffer&, AESDecContext*);
126   Result_t PCM_ADesc_to_MD(PCM::AudioDescriptor& ADesc, ASDCP::MXF::WaveAudioDescriptor* ADescObj);
127   Result_t MD_to_PCM_ADesc(ASDCP::MXF::WaveAudioDescriptor* ADescObj, PCM::AudioDescriptor& ADesc);
128   void     AddDMScrypt(Partition& HeaderPart, SourcePackage& Package,
129                        WriterInfo& Descr, const UL& WrappingUL, const Dictionary*& Dict);
130
131   Result_t Read_EKLV_Packet(Kumu::FileReader& File, const ASDCP::Dictionary& Dict, const MXF::OPAtomHeader& HeaderPart,
132                             const ASDCP::WriterInfo& Info, Kumu::fpos_t& LastPosition, ASDCP::FrameBuffer& CtFrameBuf,
133                             ui32_t FrameNum, ui32_t SequenceNum, ASDCP::FrameBuffer& FrameBuf,
134                             const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC);
135   
136   //
137  class KLReader : public ASDCP::KLVPacket
138     {
139       ASDCP_NO_COPY_CONSTRUCT(KLReader);
140       byte_t m_KeyBuf[SMPTE_UL_LENGTH*2];
141
142     public:
143       KLReader() {}
144       ~KLReader() {}
145
146       inline const byte_t* Key() { return m_KeyBuf; }
147       inline const ui64_t  Length() { return m_ValueLength; }
148       inline const ui64_t  KLLength() { return m_KLLength; }
149       
150       Result_t ReadKLFromFile(Kumu::FileReader& Reader);
151     };
152
153   namespace MXF
154   {
155       //---------------------------------------------------------------------------------
156       //
157
158     ///      void default_md_object_init();
159
160       template <class HeaderType, class FooterType>
161       class TrackFileReader
162       {
163         KM_NO_COPY_CONSTRUCT(TrackFileReader);
164         TrackFileReader();
165
166       public:
167         const Dictionary*  m_Dict;
168         Kumu::FileReader   m_File;
169         HeaderType         m_HeaderPart;
170         FooterType         m_FooterPart;
171         WriterInfo         m_Info;
172         ASDCP::FrameBuffer m_CtFrameBuf;
173         Kumu::fpos_t       m_LastPosition;
174
175       TrackFileReader(const Dictionary& d) :
176         m_HeaderPart(m_Dict), m_FooterPart(m_Dict), m_Dict(&d)
177           {
178             default_md_object_init();
179           }
180
181         virtual ~TrackFileReader() {
182           Close();
183         }
184
185         Result_t InitInfo()
186         {
187           assert(m_Dict);
188           InterchangeObject* Object;
189
190           // Identification
191           Result_t result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object);
192
193           // Writer Info and SourcePackage
194           if ( KM_SUCCESS(result) )
195             {
196               MD_to_WriterInfo((Identification*)Object, m_Info);
197               result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object);
198             }
199
200           if ( KM_SUCCESS(result) )
201             {
202               SourcePackage* SP = (SourcePackage*)Object;
203               memcpy(m_Info.AssetUUID, SP->PackageUID.Value() + 16, UUIDlen);
204             }
205
206           // optional CryptographicContext
207           if ( KM_SUCCESS(result) )
208             {
209               Result_t cr_result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(CryptographicContext), &Object);
210               
211               if ( KM_SUCCESS(cr_result) )
212                 MD_to_CryptoInfo((CryptographicContext*)Object, m_Info, *m_Dict);
213             }
214
215           return result;
216         }
217
218         //
219         Result_t OpenMXFRead(const char* filename)
220         {
221           m_LastPosition = 0;
222           Result_t result = m_File.OpenRead(filename);
223
224           if ( KM_SUCCESS(result) )
225             result = m_HeaderPart.InitFromFile(m_File);
226           
227           return result;
228         }
229
230         // positions file before reading
231         Result_t ReadEKLVFrame(const ASDCP::MXF::Partition& CurrentPartition,
232                                ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
233                                const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
234         {
235           // look up frame index node
236           IndexTableSegment::IndexEntry TmpEntry;
237
238           if ( KM_FAILURE(m_FooterPart.Lookup(FrameNum, TmpEntry)) )
239             {
240               DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
241               return RESULT_RANGE;
242             }
243
244           // get frame position and go read the frame's key and length
245           Kumu::fpos_t FilePosition = CurrentPartition.BodyOffset + TmpEntry.StreamOffset;
246           Result_t result = RESULT_OK;
247
248           if ( FilePosition != m_LastPosition )
249             {
250               m_LastPosition = FilePosition;
251               result = m_File.Seek(FilePosition);
252             }
253
254           if( KM_SUCCESS(result) )
255             result = ReadEKLVPacket(FrameNum, FrameNum + 1, FrameBuf, EssenceUL, Ctx, HMAC);
256
257           return result;
258         }
259
260         // reads from current position
261         Result_t ReadEKLVPacket(ui32_t FrameNum, ui32_t SequenceNum, ASDCP::FrameBuffer& FrameBuf,
262                                 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
263         {
264           assert(m_Dict);
265           return Read_EKLV_Packet(m_File, *m_Dict, m_HeaderPart, m_Info, m_LastPosition, m_CtFrameBuf,
266                                   FrameNum, SequenceNum, FrameBuf, EssenceUL, Ctx, HMAC);
267         }
268
269         //
270         void     Close() {
271           m_File.Close();
272         }
273       };
274
275   }/// namespace MXF
276
277   //
278   class h__ASDCPReader : public MXF::TrackFileReader<OPAtomHeader, OPAtomIndexFooter>
279     {
280       ASDCP_NO_COPY_CONSTRUCT(h__ASDCPReader);
281       h__ASDCPReader();
282
283     public:
284       Partition m_BodyPart;
285
286       h__ASDCPReader(const Dictionary&);
287       virtual ~h__ASDCPReader();
288
289       Result_t OpenMXFRead(const char* filename);
290       Result_t InitInfo();
291       Result_t InitMXFIndex();
292       Result_t ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
293                              const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC);
294     };
295
296
297   // state machine for mxf writer
298   enum WriterState_t {
299     ST_BEGIN,   // waiting for Open()
300     ST_INIT,    // waiting for SetSourceStream()
301     ST_READY,   // ready to write frames
302     ST_RUNNING, // one or more frames written
303     ST_FINAL,   // index written, file closed
304   };
305
306   // implementation of h__WriterState class Goto_* methods
307 #define Goto_body(s1,s2) if ( m_State != (s1) ) \
308                            return RESULT_STATE; \
309                          m_State = (s2); \
310                          return RESULT_OK
311   //
312   class h__WriterState
313     {
314       ASDCP_NO_COPY_CONSTRUCT(h__WriterState);
315
316     public:
317       WriterState_t m_State;
318       h__WriterState() : m_State(ST_BEGIN) {}
319       ~h__WriterState() {}
320
321       inline bool     Test_BEGIN()   { return m_State == ST_BEGIN; }
322       inline bool     Test_INIT()    { return m_State == ST_INIT; }
323       inline bool     Test_READY()   { return m_State == ST_READY;}
324       inline bool     Test_RUNNING() { return m_State == ST_RUNNING; }
325       inline bool     Test_FINAL()   { return m_State == ST_FINAL; }
326       inline Result_t Goto_INIT()    { Goto_body(ST_BEGIN,   ST_INIT); }
327       inline Result_t Goto_READY()   { Goto_body(ST_INIT,    ST_READY); }
328       inline Result_t Goto_RUNNING() { Goto_body(ST_READY,   ST_RUNNING); }
329       inline Result_t Goto_FINAL()   { Goto_body(ST_RUNNING, ST_FINAL); }
330     };
331
332   typedef std::list<ui64_t*> DurationElementList_t;
333
334   //
335   class h__Writer
336     {
337       ASDCP_NO_COPY_CONSTRUCT(h__Writer);
338       h__Writer();
339
340     public:
341       const Dictionary*  m_Dict;
342       Kumu::FileWriter   m_File;
343       ui32_t             m_HeaderSize;
344       OPAtomHeader       m_HeaderPart;
345       Partition          m_BodyPart;
346       OPAtomIndexFooter  m_FooterPart;
347       ui64_t             m_EssenceStart;
348
349       MaterialPackage*   m_MaterialPackage;
350       SourcePackage*     m_FilePackage;
351
352       FileDescriptor*    m_EssenceDescriptor;
353       std::list<InterchangeObject*> m_EssenceSubDescriptorList;
354
355       ui32_t             m_FramesWritten;
356       ui64_t             m_StreamOffset;
357       ASDCP::FrameBuffer m_CtFrameBuf;
358       h__WriterState     m_State;
359       WriterInfo         m_Info;
360       DurationElementList_t m_DurationUpdateList;
361
362       h__Writer(const Dictionary&);
363       virtual ~h__Writer();
364
365       void InitHeader();
366       void AddSourceClip(const MXF::Rational& EditRate, ui32_t TCFrameRate,
367                          const std::string& TrackName, const UL& EssenceUL,
368                          const UL& DataDefinition, const std::string& PackageLabel);
369       void AddDMSegment(const MXF::Rational& EditRate, ui32_t TCFrameRate,
370                         const std::string& TrackName, const UL& DataDefinition,
371                         const std::string& PackageLabel);
372       void AddEssenceDescriptor(const UL& WrappingUL);
373       Result_t CreateBodyPart(const MXF::Rational& EditRate, ui32_t BytesPerEditUnit = 0);
374
375       // all the above for a single source clip
376       Result_t WriteMXFHeader(const std::string& PackageLabel, const UL& WrappingUL,
377                               const std::string& TrackName, const UL& EssenceUL,
378                               const UL& DataDefinition, const MXF::Rational& EditRate,
379                               ui32_t TCFrameRate, ui32_t BytesPerEditUnit = 0);
380
381       Result_t WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf,
382                                const byte_t* EssenceUL, AESEncContext* Ctx, HMACContext* HMAC);
383
384       Result_t WriteMXFFooter();
385
386    };
387
388
389   // helper class for calculating Integrity Packs, used by WriteEKLVPacket() below.
390   //
391   class IntegrityPack
392     {
393     public:
394       byte_t Data[klv_intpack_size];
395   
396       IntegrityPack() {
397         memset(Data, 0, klv_intpack_size);
398       }
399
400       ~IntegrityPack() {}
401   
402       Result_t CalcValues(const ASDCP::FrameBuffer&, const byte_t* AssetID, ui32_t sequence, HMACContext* HMAC);
403       Result_t TestValues(const ASDCP::FrameBuffer&, const byte_t* AssetID, ui32_t sequence, HMACContext* HMAC);
404     };
405
406
407 } // namespace ASDCP
408
409 #endif // _AS_DCP_INTERNAL_H_
410
411
412 //
413 // end AS_DCP_internal.h
414 //