4290974d675bf13c23a4cbe24c0b664fc1dbea84
[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
162 // standard method of writing the header and footer of a completed MXF file
163 //
164 Result_t
165 ASDCP::h__ASDCPWriter::WriteASDCPFooter()
166 {
167   // update all Duration properties
168   DurationElementList_t::iterator dli = m_DurationUpdateList.begin();
169
170   for (; dli != m_DurationUpdateList.end(); ++dli )
171     {
172       **dli = m_FramesWritten;
173     }
174
175   m_EssenceDescriptor->ContainerDuration = m_FramesWritten;
176   m_FooterPart.PreviousPartition = m_RIP.PairArray.back().ByteOffset;
177
178   Kumu::fpos_t here = m_File.Tell();
179   m_RIP.PairArray.push_back(RIP::Pair(0, here)); // Last RIP Entry
180   m_HeaderPart.FooterPartition = here;
181
182   assert(m_Dict);
183   // re-label the header partition, set the footer
184   UL OPAtomUL(m_Dict->ul(MDD_OPAtom));
185   m_HeaderPart.OperationalPattern = OPAtomUL;
186   m_HeaderPart.m_Preface->OperationalPattern = OPAtomUL;
187   m_FooterPart.OperationalPattern = OPAtomUL;
188
189   m_FooterPart.EssenceContainers = m_HeaderPart.EssenceContainers;
190   m_FooterPart.FooterPartition = here;
191   m_FooterPart.ThisPartition = here;
192
193   Result_t result = m_FooterPart.WriteToFile(m_File, m_FramesWritten);
194
195   if ( ASDCP_SUCCESS(result) )
196     result = m_RIP.WriteToFile(m_File);
197
198   if ( ASDCP_SUCCESS(result) )
199     result = m_File.Seek(0);
200
201   if ( ASDCP_SUCCESS(result) )
202     result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
203
204   m_File.Close();
205   return result;
206 }
207
208
209 //------------------------------------------------------------------------------------------
210 //
211
212
213 // standard method of writing a plaintext or encrypted frame
214 Result_t
215 ASDCP::Write_EKLV_Packet(Kumu::FileWriter& File, const ASDCP::Dictionary& Dict, const MXF::OP1aHeader& HeaderPart,
216                          const ASDCP::WriterInfo& Info, ASDCP::FrameBuffer& CtFrameBuf, ui32_t& FramesWritten,
217                          ui64_t & StreamOffset, const ASDCP::FrameBuffer& FrameBuf, const byte_t* EssenceUL,
218                          AESEncContext* Ctx, HMACContext* HMAC)
219 {
220   Result_t result = RESULT_OK;
221   IntegrityPack IntPack;
222
223   byte_t overhead[128];
224   Kumu::MemIOWriter Overhead(overhead, 128);
225
226   if ( FrameBuf.Size() == 0 )
227     {
228       DefaultLogSink().Error("Cannot write empty frame buffer\n");
229       return RESULT_EMPTY_FB;
230     }
231
232   if ( Info.EncryptedEssence )
233     {
234       if ( ! Ctx )
235         return RESULT_CRYPT_CTX;
236
237       if ( Info.UsesHMAC && ! HMAC )
238         return RESULT_HMAC_CTX;
239
240       if ( FrameBuf.PlaintextOffset() > FrameBuf.Size() )
241         return RESULT_LARGE_PTO;
242
243       // encrypt the essence data (create encrypted source value)
244       result = EncryptFrameBuffer(FrameBuf, CtFrameBuf, Ctx);
245
246       // create HMAC
247       if ( ASDCP_SUCCESS(result) && Info.UsesHMAC )
248         result = IntPack.CalcValues(CtFrameBuf, Info.AssetUUID, FramesWritten + 1, HMAC);
249
250       if ( ASDCP_SUCCESS(result) )
251         { // write UL
252           Overhead.WriteRaw(Dict.ul(MDD_CryptEssence), SMPTE_UL_LENGTH);
253
254           // construct encrypted triplet header
255           ui32_t ETLength = klv_cryptinfo_size + CtFrameBuf.Size();
256           ui32_t BER_length = MXF_BER_LENGTH;
257
258           if ( Info.UsesHMAC )
259             ETLength += klv_intpack_size;
260           else
261             ETLength += (MXF_BER_LENGTH * 3); // for empty intpack
262
263           if ( ETLength > 0x00ffffff ) // Need BER integer longer than MXF_BER_LENGTH bytes
264             {
265               BER_length = Kumu::get_BER_length_for_value(ETLength);
266
267               // the packet is longer by the difference in expected vs. actual BER length
268               ETLength += BER_length - MXF_BER_LENGTH;
269
270               if ( BER_length == 0 )
271                 result = RESULT_KLV_CODING;
272             }
273
274           if ( ASDCP_SUCCESS(result) )
275             {
276               if ( ! ( Overhead.WriteBER(ETLength, BER_length)                      // write encrypted triplet length
277                        && Overhead.WriteBER(UUIDlen, MXF_BER_LENGTH)                // write ContextID length
278                        && Overhead.WriteRaw(Info.ContextID, UUIDlen)              // write ContextID
279                        && Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH)         // write PlaintextOffset length
280                        && Overhead.WriteUi64BE(FrameBuf.PlaintextOffset())          // write PlaintextOffset
281                        && Overhead.WriteBER(SMPTE_UL_LENGTH, MXF_BER_LENGTH)        // write essence UL length
282                        && Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH)    // write the essence UL
283                        && Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH)         // write SourceLength length
284                        && Overhead.WriteUi64BE(FrameBuf.Size())                     // write SourceLength
285                        && Overhead.WriteBER(CtFrameBuf.Size(), BER_length) ) )    // write ESV length
286                 {
287                   result = RESULT_KLV_CODING;
288                 }
289             }
290
291           if ( ASDCP_SUCCESS(result) )
292             result = File.Writev(Overhead.Data(), Overhead.Length());
293         }
294
295       if ( ASDCP_SUCCESS(result) )
296         {
297           StreamOffset += Overhead.Length();
298           // write encrypted source value
299           result = File.Writev((byte_t*)CtFrameBuf.RoData(), CtFrameBuf.Size());
300         }
301
302       if ( ASDCP_SUCCESS(result) )
303         {
304           StreamOffset += CtFrameBuf.Size();
305
306           byte_t hmoverhead[512];
307           Kumu::MemIOWriter HMACOverhead(hmoverhead, 512);
308
309           // write the HMAC
310           if ( Info.UsesHMAC )
311             {
312               HMACOverhead.WriteRaw(IntPack.Data, klv_intpack_size);
313             }
314           else
315             { // we still need the var-pack length values if the intpack is empty
316               for ( ui32_t i = 0; i < 3 ; i++ )
317                 HMACOverhead.WriteBER(0, MXF_BER_LENGTH);
318             }
319
320           // write HMAC
321           result = File.Writev(HMACOverhead.Data(), HMACOverhead.Length());
322           StreamOffset += HMACOverhead.Length();
323         }
324     }
325   else
326     {
327       ui32_t BER_length = MXF_BER_LENGTH;
328
329       if ( FrameBuf.Size() > 0x00ffffff ) // Need BER integer longer than MXF_BER_LENGTH bytes
330         {
331           BER_length = Kumu::get_BER_length_for_value(FrameBuf.Size());
332
333           if ( BER_length == 0 )
334             result = RESULT_KLV_CODING;
335         }
336
337       Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH);
338       Overhead.WriteBER(FrameBuf.Size(), BER_length);
339
340       if ( ASDCP_SUCCESS(result) )
341         result = File.Writev(Overhead.Data(), Overhead.Length());
342  
343       if ( ASDCP_SUCCESS(result) )
344         result = File.Writev((byte_t*)FrameBuf.RoData(), FrameBuf.Size());
345
346       if ( ASDCP_SUCCESS(result) )
347         StreamOffset += Overhead.Length() + FrameBuf.Size();
348     }
349
350   if ( ASDCP_SUCCESS(result) )
351     result = File.Writev();
352
353   return result;
354 }
355
356 //
357 // end h__Writer.cpp
358 //