fixing reserved symbol conflict with AS_02_USE (USE_AS_02)
[asdcplib.git] / src / h__Writer.cpp
1 /*
2 Copyright (c) 2004-2013, 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    h__Writer.cpp
28     \version $Id$
29     \brief   MXF file writer base class
30 */
31
32 #include "AS_DCP_internal.h"
33 #include "KLV.h"
34
35 using namespace ASDCP;
36 using namespace ASDCP::MXF;
37
38 //
39 // add DMS CryptographicFramework entry to source package
40 void
41 ASDCP::AddDMScrypt(Partition& HeaderPart, SourcePackage& Package,
42                    WriterInfo& Descr, const UL& WrappingUL, const Dictionary*& Dict)
43 {
44   assert(Dict);
45   // Essence Track
46   StaticTrack* NewTrack = new StaticTrack(Dict);
47   HeaderPart.AddChildObject(NewTrack);
48   Package.Tracks.push_back(NewTrack->InstanceUID);
49   NewTrack->TrackName = "Descriptive Track";
50   NewTrack->TrackID = 3;
51
52   Sequence* Seq = new Sequence(Dict);
53   HeaderPart.AddChildObject(Seq);
54   NewTrack->Sequence = Seq->InstanceUID;
55   Seq->DataDefinition = UL(Dict->ul(MDD_DescriptiveMetaDataDef));
56
57   DMSegment* Segment = new DMSegment(Dict);
58   HeaderPart.AddChildObject(Segment);
59   Seq->StructuralComponents.push_back(Segment->InstanceUID);
60   Segment->EventComment = "AS-DCP KLV Encryption";
61   
62   CryptographicFramework* CFW = new CryptographicFramework(Dict);
63   HeaderPart.AddChildObject(CFW);
64   Segment->DMFramework = CFW->InstanceUID;
65
66   CryptographicContext* Context = new CryptographicContext(Dict);
67   HeaderPart.AddChildObject(Context);
68   CFW->ContextSR = Context->InstanceUID;
69
70   Context->ContextID.Set(Descr.ContextID);
71   Context->SourceEssenceContainer = WrappingUL; // ??????
72   Context->CipherAlgorithm.Set(Dict->ul(MDD_CipherAlgorithm_AES));
73   Context->MICAlgorithm.Set( Descr.UsesHMAC ? Dict->ul(MDD_MICAlgorithm_HMAC_SHA1) : Dict->ul(MDD_MICAlgorithm_NONE) );
74   Context->CryptographicKeyID.Set(Descr.CryptographicKeyID);
75 }
76
77
78
79 //
80 ASDCP::h__ASDCPWriter::h__ASDCPWriter(const Dictionary& d) :
81   MXF::TrackFileWriter<OP1aHeader>(d), m_BodyPart(m_Dict), m_FooterPart(m_Dict) {}
82
83 ASDCP::h__ASDCPWriter::~h__ASDCPWriter() {}
84
85
86 //
87 Result_t
88 ASDCP::h__ASDCPWriter::CreateBodyPart(const MXF::Rational& EditRate, ui32_t BytesPerEditUnit)
89 {
90   assert(m_Dict);
91   Result_t result = RESULT_OK;
92
93   // create a body partition if we're writing proper 429-3/OP-Atom
94   if ( m_Info.LabelSetType == LS_MXF_SMPTE )
95     {
96       // Body Partition
97       m_BodyPart.EssenceContainers = m_HeaderPart.EssenceContainers;
98       m_BodyPart.ThisPartition = m_File.Tell();
99       m_BodyPart.BodySID = 1;
100       UL OPAtomUL(m_Dict->ul(MDD_OPAtom));
101       m_BodyPart.OperationalPattern = OPAtomUL;
102       m_RIP.PairArray.push_back(RIP::Pair(1, m_BodyPart.ThisPartition)); // Second RIP Entry
103       
104       UL BodyUL(m_Dict->ul(MDD_ClosedCompleteBodyPartition));
105       result = m_BodyPart.WriteToFile(m_File, BodyUL);
106     }
107   else
108     {
109       m_HeaderPart.BodySID = 1;
110     }
111
112   if ( ASDCP_SUCCESS(result) )
113     {
114       // Index setup
115       Kumu::fpos_t ECoffset = m_File.Tell();
116       m_FooterPart.IndexSID = 129;
117
118       if ( BytesPerEditUnit == 0 )
119         {
120           m_FooterPart.SetIndexParamsVBR(&m_HeaderPart.m_Primer, EditRate, ECoffset);
121         }
122       else
123         {
124           m_FooterPart.SetIndexParamsCBR(&m_HeaderPart.m_Primer, BytesPerEditUnit, EditRate);
125         }
126     }
127
128   return result;
129 }
130
131 //
132 Result_t
133 ASDCP::h__ASDCPWriter::WriteASDCPHeader(const std::string& PackageLabel, const UL& WrappingUL,
134                                         const std::string& TrackName, const UL& EssenceUL, const UL& DataDefinition,
135                                         const MXF::Rational& EditRate, ui32_t TCFrameRate, ui32_t BytesPerEditUnit)
136 {
137   InitHeader();
138
139   // First RIP Entry
140   if ( m_Info.LabelSetType == LS_MXF_SMPTE )  // ERK
141     {
142       m_RIP.PairArray.push_back(RIP::Pair(0, 0)); // 3-part, no essence in header
143     }
144   else
145     {
146       m_RIP.PairArray.push_back(RIP::Pair(1, 0)); // 2-part, essence in header
147     }
148
149   AddSourceClip(EditRate, TCFrameRate, TrackName, EssenceUL, DataDefinition, PackageLabel);
150   AddEssenceDescriptor(WrappingUL);
151
152   Result_t result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
153
154   if ( KM_SUCCESS(result) )
155     result = CreateBodyPart(EditRate, BytesPerEditUnit);
156
157   return result;
158 }
159
160 //
161 Result_t
162 ASDCP::h__ASDCPWriter::WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf,const byte_t* EssenceUL,
163                                        AESEncContext* Ctx, HMACContext* HMAC)
164 {
165   return Write_EKLV_Packet(m_File, *m_Dict, m_HeaderPart, m_Info, m_CtFrameBuf, m_FramesWritten,
166                            m_StreamOffset, FrameBuf, EssenceUL, Ctx, HMAC);
167 }
168
169 // standard method of writing the header and footer of a completed MXF file
170 //
171 Result_t
172 ASDCP::h__ASDCPWriter::WriteASDCPFooter()
173 {
174   // update all Duration properties
175   DurationElementList_t::iterator dli = m_DurationUpdateList.begin();
176
177   for (; dli != m_DurationUpdateList.end(); ++dli )
178     {
179       **dli = m_FramesWritten;
180     }
181
182   m_EssenceDescriptor->ContainerDuration = m_FramesWritten;
183   m_FooterPart.PreviousPartition = m_RIP.PairArray.back().ByteOffset;
184
185   Kumu::fpos_t here = m_File.Tell();
186   m_RIP.PairArray.push_back(RIP::Pair(0, here)); // Last RIP Entry
187   m_HeaderPart.FooterPartition = here;
188
189   assert(m_Dict);
190   // re-label the header partition, set the footer
191   UL OPAtomUL(m_Dict->ul(MDD_OPAtom));
192   m_HeaderPart.OperationalPattern = OPAtomUL;
193   m_HeaderPart.m_Preface->OperationalPattern = OPAtomUL;
194   m_FooterPart.OperationalPattern = OPAtomUL;
195
196   m_FooterPart.EssenceContainers = m_HeaderPart.EssenceContainers;
197   m_FooterPart.FooterPartition = here;
198   m_FooterPart.ThisPartition = here;
199
200   Result_t result = m_FooterPart.WriteToFile(m_File, m_FramesWritten);
201
202   if ( ASDCP_SUCCESS(result) )
203     result = m_RIP.WriteToFile(m_File);
204
205   if ( ASDCP_SUCCESS(result) )
206     result = m_File.Seek(0);
207
208   if ( ASDCP_SUCCESS(result) )
209     result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
210
211   m_File.Close();
212   return result;
213 }
214
215
216 //------------------------------------------------------------------------------------------
217 //
218
219
220 // standard method of writing a plaintext or encrypted frame
221 Result_t
222 ASDCP::Write_EKLV_Packet(Kumu::FileWriter& File, const ASDCP::Dictionary& Dict, const MXF::OP1aHeader& HeaderPart,
223                          const ASDCP::WriterInfo& Info, ASDCP::FrameBuffer& CtFrameBuf, ui32_t& FramesWritten,
224                          ui64_t & StreamOffset, const ASDCP::FrameBuffer& FrameBuf, const byte_t* EssenceUL,
225                          AESEncContext* Ctx, HMACContext* HMAC)
226 {
227   Result_t result = RESULT_OK;
228   IntegrityPack IntPack;
229
230   byte_t overhead[128];
231   Kumu::MemIOWriter Overhead(overhead, 128);
232
233   if ( FrameBuf.Size() == 0 )
234     {
235       DefaultLogSink().Error("Cannot write empty frame buffer\n");
236       return RESULT_EMPTY_FB;
237     }
238
239   if ( Info.EncryptedEssence )
240     {
241       if ( ! Ctx )
242         return RESULT_CRYPT_CTX;
243
244       if ( Info.UsesHMAC && ! HMAC )
245         return RESULT_HMAC_CTX;
246
247       if ( FrameBuf.PlaintextOffset() > FrameBuf.Size() )
248         return RESULT_LARGE_PTO;
249
250       // encrypt the essence data (create encrypted source value)
251       result = EncryptFrameBuffer(FrameBuf, CtFrameBuf, Ctx);
252
253       // create HMAC
254       if ( ASDCP_SUCCESS(result) && Info.UsesHMAC )
255         result = IntPack.CalcValues(CtFrameBuf, Info.AssetUUID, FramesWritten + 1, HMAC);
256
257       if ( ASDCP_SUCCESS(result) )
258         { // write UL
259           Overhead.WriteRaw(Dict.ul(MDD_CryptEssence), SMPTE_UL_LENGTH);
260
261           // construct encrypted triplet header
262           ui32_t ETLength = klv_cryptinfo_size + CtFrameBuf.Size();
263           ui32_t BER_length = MXF_BER_LENGTH;
264
265           if ( Info.UsesHMAC )
266             ETLength += klv_intpack_size;
267           else
268             ETLength += (MXF_BER_LENGTH * 3); // for empty intpack
269
270           if ( ETLength > 0x00ffffff ) // Need BER integer longer than MXF_BER_LENGTH bytes
271             {
272               BER_length = Kumu::get_BER_length_for_value(ETLength);
273
274               // the packet is longer by the difference in expected vs. actual BER length
275               ETLength += BER_length - MXF_BER_LENGTH;
276
277               if ( BER_length == 0 )
278                 result = RESULT_KLV_CODING;
279             }
280
281           if ( ASDCP_SUCCESS(result) )
282             {
283               if ( ! ( Overhead.WriteBER(ETLength, BER_length)                      // write encrypted triplet length
284                        && Overhead.WriteBER(UUIDlen, MXF_BER_LENGTH)                // write ContextID length
285                        && Overhead.WriteRaw(Info.ContextID, UUIDlen)              // write ContextID
286                        && Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH)         // write PlaintextOffset length
287                        && Overhead.WriteUi64BE(FrameBuf.PlaintextOffset())          // write PlaintextOffset
288                        && Overhead.WriteBER(SMPTE_UL_LENGTH, MXF_BER_LENGTH)        // write essence UL length
289                        && Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH)    // write the essence UL
290                        && Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH)         // write SourceLength length
291                        && Overhead.WriteUi64BE(FrameBuf.Size())                     // write SourceLength
292                        && Overhead.WriteBER(CtFrameBuf.Size(), BER_length) ) )    // write ESV length
293                 {
294                   result = RESULT_KLV_CODING;
295                 }
296             }
297
298           if ( ASDCP_SUCCESS(result) )
299             result = File.Writev(Overhead.Data(), Overhead.Length());
300         }
301
302       if ( ASDCP_SUCCESS(result) )
303         {
304           StreamOffset += Overhead.Length();
305           // write encrypted source value
306           result = File.Writev((byte_t*)CtFrameBuf.RoData(), CtFrameBuf.Size());
307         }
308
309       if ( ASDCP_SUCCESS(result) )
310         {
311           StreamOffset += CtFrameBuf.Size();
312
313           byte_t hmoverhead[512];
314           Kumu::MemIOWriter HMACOverhead(hmoverhead, 512);
315
316           // write the HMAC
317           if ( Info.UsesHMAC )
318             {
319               HMACOverhead.WriteRaw(IntPack.Data, klv_intpack_size);
320             }
321           else
322             { // we still need the var-pack length values if the intpack is empty
323               for ( ui32_t i = 0; i < 3 ; i++ )
324                 HMACOverhead.WriteBER(0, MXF_BER_LENGTH);
325             }
326
327           // write HMAC
328           result = File.Writev(HMACOverhead.Data(), HMACOverhead.Length());
329           StreamOffset += HMACOverhead.Length();
330         }
331     }
332   else
333     {
334       ui32_t BER_length = MXF_BER_LENGTH;
335
336       if ( FrameBuf.Size() > 0x00ffffff ) // Need BER integer longer than MXF_BER_LENGTH bytes
337         {
338           BER_length = Kumu::get_BER_length_for_value(FrameBuf.Size());
339
340           if ( BER_length == 0 )
341             result = RESULT_KLV_CODING;
342         }
343
344       Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH);
345       Overhead.WriteBER(FrameBuf.Size(), BER_length);
346
347       if ( ASDCP_SUCCESS(result) )
348         result = File.Writev(Overhead.Data(), Overhead.Length());
349  
350       if ( ASDCP_SUCCESS(result) )
351         result = File.Writev((byte_t*)FrameBuf.RoData(), FrameBuf.Size());
352
353       if ( ASDCP_SUCCESS(result) )
354         StreamOffset += Overhead.Length() + FrameBuf.Size();
355     }
356
357   if ( ASDCP_SUCCESS(result) )
358     result = File.Writev();
359
360   return result;
361 }
362
363 //
364 // end h__Writer.cpp
365 //