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