compatibility fixes (and a stupidbug)
[asdcplib.git] / src / h__Writer.cpp
1 /*
2 Copyright (c) 2004-2006, 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 // a magic number identifying asdcplib
39 #ifndef ASDCP_BUILD_NUMBER
40 #define ASDCP_BUILD_NUMBER 0x6A68
41 #endif
42
43
44
45 //
46 ASDCP::h__Writer::h__Writer() :
47   m_HeaderSize(0), m_EssenceStart(0),
48   m_MaterialPackage(0), m_MPTCSequence(0), m_MPTimecode(0), m_MPClSequence(0), m_MPClip(0),
49   m_FilePackage(0), m_FPTCSequence(0), m_FPTimecode(0), m_FPClSequence(0), m_FPClip(0),
50   m_EssenceDescriptor(0), m_FramesWritten(0), m_StreamOffset(0)
51 {
52 }
53
54 ASDCP::h__Writer::~h__Writer()
55 {
56 }
57
58
59 //
60 // add DMS CryptographicFramework entry to source package
61 void
62 AddDMScrypt(Partition& HeaderPart, SourcePackage& Package, WriterInfo& Descr, const UL& WrappingUL)
63 {
64   // Essence Track
65   StaticTrack* NewTrack = new StaticTrack;
66   HeaderPart.AddChildObject(NewTrack);
67   Package.Tracks.push_back(NewTrack->InstanceUID);
68   NewTrack->TrackName = "Descriptive Track";
69   NewTrack->TrackID = 3;
70
71   Sequence* Seq = new Sequence;
72   HeaderPart.AddChildObject(Seq);
73   NewTrack->Sequence = Seq->InstanceUID;
74   Seq->DataDefinition = UL(Dict::ul(MDD_DescriptiveMetaDataDef));
75
76   DMSegment* Segment = new DMSegment;
77   HeaderPart.AddChildObject(Segment);
78   Seq->StructuralComponents.push_back(Segment->InstanceUID);
79   Segment->EventComment = "AS-DCP KLV Encryption";
80   
81   CryptographicFramework* CFW = new CryptographicFramework;
82   HeaderPart.AddChildObject(CFW);
83   Segment->DMFramework = CFW->InstanceUID;
84
85   CryptographicContext* Context = new CryptographicContext;
86   HeaderPart.AddChildObject(Context);
87   CFW->ContextSR = Context->InstanceUID;
88
89   Context->ContextID.Set(Descr.ContextID);
90   Context->SourceEssenceContainer = WrappingUL; // ??????
91   Context->CipherAlgorithm.Set(Dict::ul(MDD_CipherAlgorithm_AES));
92   Context->MICAlgorithm.Set( Descr.UsesHMAC ? Dict::ul(MDD_MICAlgorithm_HMAC_SHA1) : Dict::ul(MDD_MICAlgorithm_NONE) );
93   Context->CryptographicKeyID.Set(Descr.CryptographicKeyID);
94 }
95
96 //
97 Result_t
98 ASDCP::h__Writer::WriteMXFHeader(const std::string& PackageLabel, const UL& WrappingUL,
99                                  const std::string& TrackName, const UL& DataDefinition,
100                                  const MXF::Rational& EditRate,
101                                  ui32_t TCFrameRate, ui32_t BytesPerEditUnit)
102 {
103   ASDCP_TEST_NULL(m_EssenceDescriptor);
104
105   m_HeaderPart.m_Primer.ClearTagList();
106   m_HeaderPart.m_Preface = new Preface;
107   m_HeaderPart.AddChildObject(m_HeaderPart.m_Preface);
108
109   // Set the Operational Pattern label -- we're just starting and have no RIP or index,
110   // so we tell the world by using OP1a
111   m_HeaderPart.m_Preface->OperationalPattern = UL(Dict::ul(MDD_OP1a));
112   m_HeaderPart.OperationalPattern = m_HeaderPart.m_Preface->OperationalPattern;
113   m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(0, 0)); // First RIP Entry
114
115   //
116   // Identification
117   //
118   Identification* Ident = new Identification;
119   m_HeaderPart.AddChildObject(Ident);
120   m_HeaderPart.m_Preface->Identifications.push_back(Ident->InstanceUID);
121
122   Kumu::GenRandomValue(Ident->ThisGenerationUID);
123   Ident->CompanyName = m_Info.CompanyName.c_str();
124   Ident->ProductName = m_Info.ProductName.c_str();
125   Ident->VersionString = m_Info.ProductVersion.c_str();
126   Ident->ProductUID.Set(m_Info.ProductUUID);
127   Ident->Platform = ASDCP_PLATFORM;
128   Ident->ToolkitVersion.Major = VERSION_MAJOR;
129   Ident->ToolkitVersion.Minor = VERSION_APIMINOR;
130   Ident->ToolkitVersion.Patch = VERSION_IMPMINOR;
131   Ident->ToolkitVersion.Build = ASDCP_BUILD_NUMBER;
132   Ident->ToolkitVersion.Release = VersionType::RL_DEVELOPMENT;
133
134   //
135   ContentStorage* Storage = new ContentStorage;
136   m_HeaderPart.AddChildObject(Storage);
137   m_HeaderPart.m_Preface->ContentStorage = Storage->InstanceUID;
138
139   EssenceContainerData* ECD = new EssenceContainerData;
140   m_HeaderPart.AddChildObject(ECD);
141   Storage->EssenceContainerData.push_back(ECD->InstanceUID);
142   ECD->IndexSID = 129;
143   ECD->BodySID = 1;
144
145   //
146   // Material Package
147   //
148   UMID PackageUMID;
149   PackageUMID.MakeUMID(0x0f); // unidentified essence
150   m_MaterialPackage = new MaterialPackage;
151   m_MaterialPackage->Name = "AS-DCP Material Package";
152   m_MaterialPackage->PackageUID = PackageUMID;
153   m_HeaderPart.AddChildObject(m_MaterialPackage);
154   Storage->Packages.push_back(m_MaterialPackage->InstanceUID);
155
156   // Timecode Track
157   Track* NewTrack = new Track;
158   m_HeaderPart.AddChildObject(NewTrack);
159   NewTrack->EditRate = EditRate;
160   m_MaterialPackage->Tracks.push_back(NewTrack->InstanceUID);
161   NewTrack->TrackID = 1;
162   NewTrack->TrackName = "Timecode Track";
163
164   m_MPTCSequence = new Sequence;
165   m_HeaderPart.AddChildObject(m_MPTCSequence);
166   NewTrack->Sequence = m_MPTCSequence->InstanceUID;
167   m_MPTCSequence->DataDefinition = UL(Dict::ul(MDD_TimecodeDataDef));
168
169   m_MPTimecode = new TimecodeComponent;
170   m_HeaderPart.AddChildObject(m_MPTimecode);
171   m_MPTCSequence->StructuralComponents.push_back(m_MPTimecode->InstanceUID);
172   m_MPTimecode->RoundedTimecodeBase = TCFrameRate;
173   m_MPTimecode->StartTimecode = ui64_C(0);
174   m_MPTimecode->DataDefinition = UL(Dict::ul(MDD_TimecodeDataDef));
175
176   // Essence Track
177   NewTrack = new Track;
178   m_HeaderPart.AddChildObject(NewTrack);
179   NewTrack->EditRate = EditRate;
180   m_MaterialPackage->Tracks.push_back(NewTrack->InstanceUID);
181   NewTrack->TrackID = 2;
182   NewTrack->TrackName = TrackName.c_str();
183
184   m_MPClSequence = new Sequence;
185   m_HeaderPart.AddChildObject(m_MPClSequence);
186   NewTrack->Sequence = m_MPClSequence->InstanceUID;
187   m_MPClSequence->DataDefinition = DataDefinition;
188
189   m_MPClip = new SourceClip;
190   m_HeaderPart.AddChildObject(m_MPClip);
191   m_MPClSequence->StructuralComponents.push_back(m_MPClip->InstanceUID);
192   m_MPClip->DataDefinition = DataDefinition;
193
194   //
195   // File (Source) Package
196   //
197   UUID assetUUID(m_Info.AssetUUID);
198   PackageUMID.MakeUMID(0x0f, assetUUID);
199
200   m_FilePackage = new SourcePackage;
201   m_FilePackage->Name = PackageLabel.c_str();
202   m_FilePackage->PackageUID = PackageUMID;
203   ECD->LinkedPackageUID = PackageUMID;
204   m_MPClip->SourcePackageID = PackageUMID;
205   m_MPClip->SourceTrackID = 1;
206
207   m_HeaderPart.AddChildObject(m_FilePackage);
208   Storage->Packages.push_back(m_FilePackage->InstanceUID);
209
210   // Timecode Track
211   NewTrack = new Track;
212   m_HeaderPart.AddChildObject(NewTrack);
213   NewTrack->EditRate = EditRate;
214   m_FilePackage->Tracks.push_back(NewTrack->InstanceUID);
215   NewTrack->TrackID = 1;
216   NewTrack->TrackName = "Timecode Track";
217
218   m_FPTCSequence = new Sequence;
219   m_HeaderPart.AddChildObject(m_FPTCSequence);
220   NewTrack->Sequence = m_FPTCSequence->InstanceUID;
221   m_FPTCSequence->DataDefinition = UL(Dict::ul(MDD_TimecodeDataDef));
222
223   m_FPTimecode = new TimecodeComponent;
224   m_HeaderPart.AddChildObject(m_FPTimecode);
225   m_FPTCSequence->StructuralComponents.push_back(m_FPTimecode->InstanceUID);
226   m_FPTimecode->RoundedTimecodeBase = TCFrameRate;
227   m_FPTimecode->StartTimecode = ui64_C(86400); // 01:00:00:00
228   m_FPTimecode->DataDefinition = UL(Dict::ul(MDD_TimecodeDataDef));
229
230   // Essence Track
231   NewTrack = new Track;
232   m_HeaderPart.AddChildObject(NewTrack);
233   NewTrack->EditRate = EditRate;
234   m_FilePackage->Tracks.push_back(NewTrack->InstanceUID);
235   NewTrack->TrackID = 2;
236   NewTrack->TrackName = TrackName.c_str();
237
238   m_FPClSequence = new Sequence;
239   m_HeaderPart.AddChildObject(m_FPClSequence);
240   NewTrack->Sequence = m_FPClSequence->InstanceUID;
241   m_FPClSequence->DataDefinition = DataDefinition;
242
243   m_FPClip = new SourceClip;
244   m_HeaderPart.AddChildObject(m_FPClip);
245   m_FPClSequence->StructuralComponents.push_back(m_FPClip->InstanceUID);
246   m_FPClip->DataDefinition = DataDefinition;
247
248   //
249   // Essence Descriptor
250   //
251   m_EssenceDescriptor->EssenceContainer = WrappingUL;
252   m_EssenceDescriptor->LinkedTrackID = NewTrack->TrackID;
253   m_HeaderPart.m_Preface->PrimaryPackage = m_FilePackage->InstanceUID;
254
255   //
256   // Encryption Descriptor
257   //
258   if ( m_Info.EncryptedEssence )
259     {
260       UL CryptEssenceUL(Dict::ul(MDD_EncryptedContainerLabel));
261       m_HeaderPart.EssenceContainers.push_back(CryptEssenceUL);
262       m_HeaderPart.m_Preface->DMSchemes.push_back(UL(Dict::ul(MDD_CryptographicFrameworkLabel)));
263       AddDMScrypt(m_HeaderPart, *m_FilePackage, m_Info, WrappingUL);
264     }
265   else
266     {
267       m_HeaderPart.EssenceContainers.push_back(UL(Dict::ul(MDD_GCMulti)));
268       m_HeaderPart.EssenceContainers.push_back(WrappingUL);
269     }
270
271   m_HeaderPart.m_Preface->EssenceContainers = m_HeaderPart.EssenceContainers;
272   m_HeaderPart.AddChildObject(m_EssenceDescriptor);
273   m_FilePackage->Descriptor = m_EssenceDescriptor->InstanceUID;
274
275   // Write the header partition
276   Result_t result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
277
278   if ( ASDCP_SUCCESS(result) )
279     {
280       // Body Partition
281       m_BodyPart.EssenceContainers = m_HeaderPart.EssenceContainers;
282       m_BodyPart.ThisPartition = m_File.Tell();
283       m_BodyPart.BodySID = 1;
284       UL OPAtomUL(Dict::ul(MDD_OPAtom));
285
286       if ( m_Info.LabelSetType == LS_MXF_INTEROP )
287         OPAtomUL.Set(Dict::ul(MDD_MXFInterop_OPAtom));
288
289       m_BodyPart.OperationalPattern = OPAtomUL;
290       m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(1, m_BodyPart.ThisPartition)); // Second RIP Entry
291       
292       UL BodyUL(Dict::ul(MDD_ClosedCompleteBodyPartition));
293       result = m_BodyPart.WriteToFile(m_File, BodyUL);
294     }
295
296   if ( ASDCP_SUCCESS(result) )
297     {
298       // Index setup
299       Kumu::fpos_t ECoffset = m_File.Tell();
300
301       if ( BytesPerEditUnit == 0 )
302         m_FooterPart.SetIndexParamsVBR(&m_HeaderPart.m_Primer, EditRate, ECoffset);
303       else
304         m_FooterPart.SetIndexParamsCBR(&m_HeaderPart.m_Primer, BytesPerEditUnit, EditRate);
305     }
306
307   return result;
308 }
309
310
311
312 // standard method of writing a plaintext or encrypted frame
313 Result_t
314 ASDCP::h__Writer::WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf, const byte_t* EssenceUL,
315                                   AESEncContext* Ctx, HMACContext* HMAC)
316 {
317   Result_t result = RESULT_OK;
318   IntegrityPack IntPack;
319
320   byte_t overhead[128];
321   Kumu::MemIOWriter Overhead(overhead, 128);
322
323   if ( FrameBuf.Size() == 0 )
324     {
325       DefaultLogSink().Error("Cannot write empty frame buffer\n");
326       return RESULT_EMPTY_FB;
327     }
328
329   if ( m_Info.EncryptedEssence )
330     {
331       if ( ! Ctx )
332         return RESULT_CRYPT_CTX;
333
334       if ( m_Info.UsesHMAC && ! HMAC )
335         return RESULT_HMAC_CTX;
336
337       if ( FrameBuf.PlaintextOffset() > FrameBuf.Size() )
338         return RESULT_LARGE_PTO;
339
340       // encrypt the essence data (create encrypted source value)
341       result = EncryptFrameBuffer(FrameBuf, m_CtFrameBuf, Ctx);
342
343       // create HMAC
344       if ( ASDCP_SUCCESS(result) && m_Info.UsesHMAC )
345         result = IntPack.CalcValues(m_CtFrameBuf, m_Info.AssetUUID, m_FramesWritten + 1, HMAC);
346
347       if ( ASDCP_SUCCESS(result) )
348         { // write UL
349           if ( m_Info.LabelSetType == LS_MXF_INTEROP )
350             Overhead.WriteRaw(Dict::ul(MDD_MXFInterop_CryptEssence), SMPTE_UL_LENGTH);
351           else
352             Overhead.WriteRaw(Dict::ul(MDD_CryptEssence), SMPTE_UL_LENGTH);
353
354           // construct encrypted triplet header
355           ui32_t ETLength = klv_cryptinfo_size + m_CtFrameBuf.Size();
356
357           if ( m_Info.UsesHMAC )
358             ETLength += klv_intpack_size;
359           else
360             ETLength += (MXF_BER_LENGTH * 3); // for empty intpack
361
362           Overhead.WriteBER(ETLength, MXF_BER_LENGTH);                  // write encrypted triplet length
363           Overhead.WriteBER(UUIDlen, MXF_BER_LENGTH);                   // write ContextID length
364           Overhead.WriteRaw(m_Info.ContextID, UUIDlen);                  // write ContextID
365           Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH);            // write PlaintextOffset length
366           Overhead.WriteUi64BE(FrameBuf.PlaintextOffset());              // write PlaintextOffset
367           Overhead.WriteBER(SMPTE_UL_LENGTH, MXF_BER_LENGTH);              // write essence UL length
368           Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH);           // write the essence UL
369           Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH);            // write SourceLength length
370           Overhead.WriteUi64BE(FrameBuf.Size());                         // write SourceLength
371           Overhead.WriteBER(m_CtFrameBuf.Size(), MXF_BER_LENGTH);       // write ESV length
372
373           result = m_File.Writev(Overhead.Data(), Overhead.Length());
374         }
375
376       if ( ASDCP_SUCCESS(result) )
377         {
378           m_StreamOffset += Overhead.Length();
379           // write encrypted source value
380           result = m_File.Writev((byte_t*)m_CtFrameBuf.RoData(), m_CtFrameBuf.Size());
381         }
382
383       if ( ASDCP_SUCCESS(result) )
384         {
385           m_StreamOffset += m_CtFrameBuf.Size();
386
387           byte_t hmoverhead[512];
388           Kumu::MemIOWriter HMACOverhead(hmoverhead, 512);
389
390           // write the HMAC
391           if ( m_Info.UsesHMAC )
392             {
393               HMACOverhead.WriteRaw(IntPack.Data, klv_intpack_size);
394             }
395           else
396             { // we still need the var-pack length values if the intpack is empty
397               for ( ui32_t i = 0; i < 3 ; i++ )
398                 HMACOverhead.WriteBER(0, MXF_BER_LENGTH);
399             }
400
401           // write HMAC
402           result = m_File.Writev(HMACOverhead.Data(), HMACOverhead.Length());
403           m_StreamOffset += HMACOverhead.Length();
404         }
405     }
406   else
407     {
408       Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH);
409       Overhead.WriteBER(FrameBuf.Size(), MXF_BER_LENGTH);
410       result = m_File.Writev(Overhead.Data(), Overhead.Length());
411  
412       if ( ASDCP_SUCCESS(result) )
413         result = m_File.Writev((byte_t*)FrameBuf.RoData(), FrameBuf.Size());
414
415       if ( ASDCP_SUCCESS(result) )
416         m_StreamOffset += Overhead.Length() + FrameBuf.Size();
417     }
418
419   if ( ASDCP_SUCCESS(result) )
420     result = m_File.Writev();
421
422   return result;
423 }
424
425
426 // standard method of writing the header and footer of a completed MXF file
427 //
428 Result_t
429 ASDCP::h__Writer::WriteMXFFooter()
430 {
431   // Set top-level file package correctly for OP-Atom
432
433   m_MPTCSequence->Duration = m_MPTimecode->Duration = m_MPClSequence->Duration = m_MPClip->Duration = 
434     m_FPTCSequence->Duration = m_FPTimecode->Duration = m_FPClSequence->Duration = m_FPClip->Duration = 
435     m_EssenceDescriptor->ContainerDuration = m_FramesWritten;
436
437   Kumu::fpos_t here = m_File.Tell();
438   m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(0, here)); // Third RIP Entry
439   m_HeaderPart.FooterPartition = here;
440
441   // re-label the partition
442   UL OPAtomUL(Dict::ul(MDD_OPAtom));
443
444   if ( m_Info.LabelSetType == LS_MXF_INTEROP )
445     OPAtomUL.Set(Dict::ul(MDD_MXFInterop_OPAtom));
446   
447   m_HeaderPart.OperationalPattern = OPAtomUL;
448   m_HeaderPart.m_Preface->OperationalPattern = m_HeaderPart.OperationalPattern;
449
450   m_FooterPart.PreviousPartition = m_BodyPart.ThisPartition;
451   m_FooterPart.OperationalPattern = m_HeaderPart.OperationalPattern;
452   m_FooterPart.EssenceContainers = m_HeaderPart.EssenceContainers;
453   m_FooterPart.FooterPartition = here;
454   m_FooterPart.ThisPartition = here;
455
456   Result_t result = m_FooterPart.WriteToFile(m_File, m_FramesWritten);
457
458   if ( ASDCP_SUCCESS(result) )
459     result = m_HeaderPart.m_RIP.WriteToFile(m_File);
460
461   if ( ASDCP_SUCCESS(result) )
462     result = m_File.Seek(0);
463
464   if ( ASDCP_SUCCESS(result) )
465     result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
466
467   m_File.Close();
468   return result;
469 }
470
471 //
472 // end h__Writer.cpp
473 //