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