oops
[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   m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(0, 0)); // First RIP Entry
113
114   //
115   // Identification
116   //
117   Identification* Ident = new Identification;
118   m_HeaderPart.AddChildObject(Ident);
119   m_HeaderPart.m_Preface->Identifications.push_back(Ident->InstanceUID);
120
121   Kumu::GenRandomValue(Ident->ThisGenerationUID);
122   Ident->CompanyName = m_Info.CompanyName.c_str();
123   Ident->ProductName = m_Info.ProductName.c_str();
124   Ident->VersionString = m_Info.ProductVersion.c_str();
125   Ident->ProductUID.Set(m_Info.ProductUUID);
126   Ident->Platform = ASDCP_PLATFORM;
127   Ident->ToolkitVersion.Major = VERSION_MAJOR;
128   Ident->ToolkitVersion.Minor = VERSION_APIMINOR;
129   Ident->ToolkitVersion.Patch = VERSION_IMPMINOR;
130   Ident->ToolkitVersion.Build = ASDCP_BUILD_NUMBER;
131   Ident->ToolkitVersion.Release = VersionType::RL_RELEASE;
132
133   //
134   ContentStorage* Storage = new ContentStorage;
135   m_HeaderPart.AddChildObject(Storage);
136   m_HeaderPart.m_Preface->ContentStorage = Storage->InstanceUID;
137
138   EssenceContainerData* ECD = new EssenceContainerData;
139   m_HeaderPart.AddChildObject(ECD);
140   Storage->EssenceContainerData.push_back(ECD->InstanceUID);
141   ECD->IndexSID = 129;
142   ECD->BodySID = 1;
143
144   //
145   // Material Package
146   //
147   UMID PackageUMID;
148   PackageUMID.MakeUMID(0x0f); // unidentified essence
149   m_MaterialPackage = new MaterialPackage;
150   m_MaterialPackage->Name = "AS-DCP Material Package";
151   m_MaterialPackage->PackageUID = PackageUMID;
152   m_HeaderPart.AddChildObject(m_MaterialPackage);
153   Storage->Packages.push_back(m_MaterialPackage->InstanceUID);
154
155   // Timecode Track
156   Track* NewTrack = new Track;
157   m_HeaderPart.AddChildObject(NewTrack);
158   NewTrack->EditRate = EditRate;
159   m_MaterialPackage->Tracks.push_back(NewTrack->InstanceUID);
160   NewTrack->TrackID = 1;
161   NewTrack->TrackName = "Timecode Track";
162
163   m_MPTCSequence = new Sequence;
164   m_HeaderPart.AddChildObject(m_MPTCSequence);
165   NewTrack->Sequence = m_MPTCSequence->InstanceUID;
166   m_MPTCSequence->DataDefinition = UL(Dict::ul(MDD_TimecodeDataDef));
167
168   m_MPTimecode = new TimecodeComponent;
169   m_HeaderPart.AddChildObject(m_MPTimecode);
170   m_MPTCSequence->StructuralComponents.push_back(m_MPTimecode->InstanceUID);
171   m_MPTimecode->RoundedTimecodeBase = TCFrameRate;
172   m_MPTimecode->StartTimecode = ui64_C(0);
173   m_MPTimecode->DataDefinition = UL(Dict::ul(MDD_TimecodeDataDef));
174
175   // Essence Track
176   NewTrack = new Track;
177   m_HeaderPart.AddChildObject(NewTrack);
178   NewTrack->EditRate = EditRate;
179   m_MaterialPackage->Tracks.push_back(NewTrack->InstanceUID);
180   NewTrack->TrackID = 2;
181   NewTrack->TrackName = TrackName.c_str();
182
183   m_MPClSequence = new Sequence;
184   m_HeaderPart.AddChildObject(m_MPClSequence);
185   NewTrack->Sequence = m_MPClSequence->InstanceUID;
186   m_MPClSequence->DataDefinition = DataDefinition;
187
188   m_MPClip = new SourceClip;
189   m_HeaderPart.AddChildObject(m_MPClip);
190   m_MPClSequence->StructuralComponents.push_back(m_MPClip->InstanceUID);
191   m_MPClip->DataDefinition = DataDefinition;
192
193   //
194   // File (Source) Package
195   //
196   UUID assetUUID(m_Info.AssetUUID);
197   PackageUMID.MakeUMID(0x0f, assetUUID);
198
199   m_FilePackage = new SourcePackage;
200   m_FilePackage->Name = PackageLabel.c_str();
201   m_FilePackage->PackageUID = PackageUMID;
202   ECD->LinkedPackageUID = PackageUMID;
203
204   m_MPClip->SourcePackageID = PackageUMID;
205   m_MPClip->SourceTrackID = 2;
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   // for now we do not allow setting this value, so all files will be 'original'
249   m_FPClip->SourceTrackID = 0;
250   m_FPClip->SourcePackageID = NilUMID;
251
252   //
253   // Essence Descriptor
254   //
255   m_EssenceDescriptor->EssenceContainer = WrappingUL;
256   m_EssenceDescriptor->LinkedTrackID = NewTrack->TrackID;
257   m_HeaderPart.m_Preface->PrimaryPackage = m_FilePackage->InstanceUID;
258
259   //
260   // Encryption Descriptor
261   //
262   if ( m_Info.EncryptedEssence )
263     {
264       UL CryptEssenceUL(Dict::ul(MDD_EncryptedContainerLabel));
265       m_HeaderPart.EssenceContainers.push_back(CryptEssenceUL);
266       m_HeaderPart.m_Preface->DMSchemes.push_back(UL(Dict::ul(MDD_CryptographicFrameworkLabel)));
267       AddDMScrypt(m_HeaderPart, *m_FilePackage, m_Info, WrappingUL);
268     }
269   else
270     {
271       m_HeaderPart.EssenceContainers.push_back(UL(Dict::ul(MDD_GCMulti)));
272       m_HeaderPart.EssenceContainers.push_back(WrappingUL);
273     }
274
275   m_HeaderPart.m_Preface->EssenceContainers = m_HeaderPart.EssenceContainers;
276   m_HeaderPart.AddChildObject(m_EssenceDescriptor);
277
278   std::list<FileDescriptor*>::iterator sdli = m_EssenceSubDescriptorList.begin();
279   for ( ; sdli != m_EssenceSubDescriptorList.end(); sdli++ )
280     m_HeaderPart.AddChildObject(*sdli);
281
282   m_FilePackage->Descriptor = m_EssenceDescriptor->InstanceUID;
283
284   // Write the header partition
285   Result_t result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
286
287   // create a body partition of we're writing proper 429-3/OP-Atom
288   if ( ASDCP_SUCCESS(result) && m_Info.LabelSetType == LS_MXF_SMPTE )
289     {
290       // Body Partition
291       m_BodyPart.EssenceContainers = m_HeaderPart.EssenceContainers;
292       m_BodyPart.ThisPartition = m_File.Tell();
293       m_BodyPart.BodySID = 1;
294       UL OPAtomUL(Dict::ul(MDD_OPAtom));
295       m_BodyPart.OperationalPattern = OPAtomUL;
296       m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(1, m_BodyPart.ThisPartition)); // Second RIP Entry
297       
298       UL BodyUL(Dict::ul(MDD_ClosedCompleteBodyPartition));
299       result = m_BodyPart.WriteToFile(m_File, BodyUL);
300     }
301
302   if ( ASDCP_SUCCESS(result) )
303     {
304       // Index setup
305       Kumu::fpos_t ECoffset = m_File.Tell();
306
307       if ( BytesPerEditUnit == 0 )
308         m_FooterPart.SetIndexParamsVBR(&m_HeaderPart.m_Primer, EditRate, ECoffset);
309       else
310         m_FooterPart.SetIndexParamsCBR(&m_HeaderPart.m_Primer, BytesPerEditUnit, EditRate);
311     }
312
313   return result;
314 }
315
316
317
318 // standard method of writing a plaintext or encrypted frame
319 Result_t
320 ASDCP::h__Writer::WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf, const byte_t* EssenceUL,
321                                   AESEncContext* Ctx, HMACContext* HMAC)
322 {
323   Result_t result = RESULT_OK;
324   IntegrityPack IntPack;
325
326   byte_t overhead[128];
327   Kumu::MemIOWriter Overhead(overhead, 128);
328
329   if ( FrameBuf.Size() == 0 )
330     {
331       DefaultLogSink().Error("Cannot write empty frame buffer\n");
332       return RESULT_EMPTY_FB;
333     }
334
335   if ( m_Info.EncryptedEssence )
336     {
337       if ( ! Ctx )
338         return RESULT_CRYPT_CTX;
339
340       if ( m_Info.UsesHMAC && ! HMAC )
341         return RESULT_HMAC_CTX;
342
343       if ( FrameBuf.PlaintextOffset() > FrameBuf.Size() )
344         return RESULT_LARGE_PTO;
345
346       // encrypt the essence data (create encrypted source value)
347       result = EncryptFrameBuffer(FrameBuf, m_CtFrameBuf, Ctx);
348
349       // create HMAC
350       if ( ASDCP_SUCCESS(result) && m_Info.UsesHMAC )
351         result = IntPack.CalcValues(m_CtFrameBuf, m_Info.AssetUUID, m_FramesWritten + 1, HMAC);
352
353       if ( ASDCP_SUCCESS(result) )
354         { // write UL
355           if ( m_Info.LabelSetType == LS_MXF_INTEROP )
356             Overhead.WriteRaw(Dict::ul(MDD_MXFInterop_CryptEssence), SMPTE_UL_LENGTH);
357           else
358             Overhead.WriteRaw(Dict::ul(MDD_CryptEssence), SMPTE_UL_LENGTH);
359
360           // construct encrypted triplet header
361           ui32_t ETLength = klv_cryptinfo_size + m_CtFrameBuf.Size();
362
363           if ( m_Info.UsesHMAC )
364             ETLength += klv_intpack_size;
365           else
366             ETLength += (MXF_BER_LENGTH * 3); // for empty intpack
367
368           Overhead.WriteBER(ETLength, MXF_BER_LENGTH);                  // write encrypted triplet length
369           Overhead.WriteBER(UUIDlen, MXF_BER_LENGTH);                   // write ContextID length
370           Overhead.WriteRaw(m_Info.ContextID, UUIDlen);                  // write ContextID
371           Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH);            // write PlaintextOffset length
372           Overhead.WriteUi64BE(FrameBuf.PlaintextOffset());              // write PlaintextOffset
373           Overhead.WriteBER(SMPTE_UL_LENGTH, MXF_BER_LENGTH);              // write essence UL length
374           Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH);           // write the essence UL
375           Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH);            // write SourceLength length
376           Overhead.WriteUi64BE(FrameBuf.Size());                         // write SourceLength
377           Overhead.WriteBER(m_CtFrameBuf.Size(), MXF_BER_LENGTH);       // write ESV length
378
379           result = m_File.Writev(Overhead.Data(), Overhead.Length());
380         }
381
382       if ( ASDCP_SUCCESS(result) )
383         {
384           m_StreamOffset += Overhead.Length();
385           // write encrypted source value
386           result = m_File.Writev((byte_t*)m_CtFrameBuf.RoData(), m_CtFrameBuf.Size());
387         }
388
389       if ( ASDCP_SUCCESS(result) )
390         {
391           m_StreamOffset += m_CtFrameBuf.Size();
392
393           byte_t hmoverhead[512];
394           Kumu::MemIOWriter HMACOverhead(hmoverhead, 512);
395
396           // write the HMAC
397           if ( m_Info.UsesHMAC )
398             {
399               HMACOverhead.WriteRaw(IntPack.Data, klv_intpack_size);
400             }
401           else
402             { // we still need the var-pack length values if the intpack is empty
403               for ( ui32_t i = 0; i < 3 ; i++ )
404                 HMACOverhead.WriteBER(0, MXF_BER_LENGTH);
405             }
406
407           // write HMAC
408           result = m_File.Writev(HMACOverhead.Data(), HMACOverhead.Length());
409           m_StreamOffset += HMACOverhead.Length();
410         }
411     }
412   else
413     {
414       Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH);
415       Overhead.WriteBER(FrameBuf.Size(), MXF_BER_LENGTH);
416       result = m_File.Writev(Overhead.Data(), Overhead.Length());
417  
418       if ( ASDCP_SUCCESS(result) )
419         result = m_File.Writev((byte_t*)FrameBuf.RoData(), FrameBuf.Size());
420
421       if ( ASDCP_SUCCESS(result) )
422         m_StreamOffset += Overhead.Length() + FrameBuf.Size();
423     }
424
425   if ( ASDCP_SUCCESS(result) )
426     result = m_File.Writev();
427
428   return result;
429 }
430
431
432 // standard method of writing the header and footer of a completed MXF file
433 //
434 Result_t
435 ASDCP::h__Writer::WriteMXFFooter()
436 {
437   // Set top-level file package correctly for OP-Atom
438
439   m_MPTCSequence->Duration = m_MPTimecode->Duration = m_MPClSequence->Duration = m_MPClip->Duration = 
440     m_FPTCSequence->Duration = m_FPTimecode->Duration = m_FPClSequence->Duration = m_FPClip->Duration = 
441     m_EssenceDescriptor->ContainerDuration = m_FramesWritten;
442
443   Kumu::fpos_t here = m_File.Tell();
444   m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(0, here)); // Third RIP Entry
445   m_HeaderPart.FooterPartition = here;
446
447   // re-label the partition
448   UL OPAtomUL(Dict::ul(MDD_OPAtom));
449
450   if ( m_Info.LabelSetType == LS_MXF_INTEROP )
451     OPAtomUL.Set(Dict::ul(MDD_MXFInterop_OPAtom));
452   
453   m_HeaderPart.OperationalPattern = OPAtomUL;
454   m_HeaderPart.m_Preface->OperationalPattern = m_HeaderPart.OperationalPattern;
455
456   m_FooterPart.PreviousPartition = m_BodyPart.ThisPartition;
457   m_FooterPart.OperationalPattern = m_HeaderPart.OperationalPattern;
458   m_FooterPart.EssenceContainers = m_HeaderPart.EssenceContainers;
459   m_FooterPart.FooterPartition = here;
460   m_FooterPart.ThisPartition = here;
461
462   Result_t result = m_FooterPart.WriteToFile(m_File, m_FramesWritten);
463
464   if ( ASDCP_SUCCESS(result) )
465     result = m_HeaderPart.m_RIP.WriteToFile(m_File);
466
467   if ( ASDCP_SUCCESS(result) )
468     result = m_File.Seek(0);
469
470   if ( ASDCP_SUCCESS(result) )
471     result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
472
473   m_File.Close();
474   return result;
475 }
476
477 //
478 // end h__Writer.cpp
479 //