1.1.9 release a
[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
205   // for now we do not allow setting this value, so all files will be 'original'
206   m_MPClip->SourcePackageID = NilUMID;
207   m_MPClip->SourceTrackID = 0;
208
209   m_HeaderPart.AddChildObject(m_FilePackage);
210   Storage->Packages.push_back(m_FilePackage->InstanceUID);
211
212   // Timecode Track
213   NewTrack = new Track;
214   m_HeaderPart.AddChildObject(NewTrack);
215   NewTrack->EditRate = EditRate;
216   m_FilePackage->Tracks.push_back(NewTrack->InstanceUID);
217   NewTrack->TrackID = 1;
218   NewTrack->TrackName = "Timecode Track";
219
220   m_FPTCSequence = new Sequence;
221   m_HeaderPart.AddChildObject(m_FPTCSequence);
222   NewTrack->Sequence = m_FPTCSequence->InstanceUID;
223   m_FPTCSequence->DataDefinition = UL(Dict::ul(MDD_TimecodeDataDef));
224
225   m_FPTimecode = new TimecodeComponent;
226   m_HeaderPart.AddChildObject(m_FPTimecode);
227   m_FPTCSequence->StructuralComponents.push_back(m_FPTimecode->InstanceUID);
228   m_FPTimecode->RoundedTimecodeBase = TCFrameRate;
229   m_FPTimecode->StartTimecode = ui64_C(86400); // 01:00:00:00
230   m_FPTimecode->DataDefinition = UL(Dict::ul(MDD_TimecodeDataDef));
231
232   // Essence Track
233   NewTrack = new Track;
234   m_HeaderPart.AddChildObject(NewTrack);
235   NewTrack->EditRate = EditRate;
236   m_FilePackage->Tracks.push_back(NewTrack->InstanceUID);
237   NewTrack->TrackID = 2;
238   NewTrack->TrackName = TrackName.c_str();
239
240   m_FPClSequence = new Sequence;
241   m_HeaderPart.AddChildObject(m_FPClSequence);
242   NewTrack->Sequence = m_FPClSequence->InstanceUID;
243   m_FPClSequence->DataDefinition = DataDefinition;
244
245   m_FPClip = new SourceClip;
246   m_HeaderPart.AddChildObject(m_FPClip);
247   m_FPClSequence->StructuralComponents.push_back(m_FPClip->InstanceUID);
248   m_FPClip->DataDefinition = DataDefinition;
249
250   //
251   // Essence Descriptor
252   //
253   m_EssenceDescriptor->EssenceContainer = WrappingUL;
254   m_EssenceDescriptor->LinkedTrackID = NewTrack->TrackID;
255   m_HeaderPart.m_Preface->PrimaryPackage = m_FilePackage->InstanceUID;
256
257   //
258   // Encryption Descriptor
259   //
260   if ( m_Info.EncryptedEssence )
261     {
262       UL CryptEssenceUL(Dict::ul(MDD_EncryptedContainerLabel));
263       m_HeaderPart.EssenceContainers.push_back(CryptEssenceUL);
264       m_HeaderPart.m_Preface->DMSchemes.push_back(UL(Dict::ul(MDD_CryptographicFrameworkLabel)));
265       AddDMScrypt(m_HeaderPart, *m_FilePackage, m_Info, WrappingUL);
266     }
267   else
268     {
269       m_HeaderPart.EssenceContainers.push_back(UL(Dict::ul(MDD_GCMulti)));
270       m_HeaderPart.EssenceContainers.push_back(WrappingUL);
271     }
272
273   m_HeaderPart.m_Preface->EssenceContainers = m_HeaderPart.EssenceContainers;
274   m_HeaderPart.AddChildObject(m_EssenceDescriptor);
275   m_FilePackage->Descriptor = m_EssenceDescriptor->InstanceUID;
276
277   // Write the header partition
278   Result_t result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
279
280   if ( ASDCP_SUCCESS(result) )
281     {
282       // Body Partition
283       m_BodyPart.EssenceContainers = m_HeaderPart.EssenceContainers;
284       m_BodyPart.ThisPartition = m_File.Tell();
285       m_BodyPart.BodySID = 1;
286       UL OPAtomUL(Dict::ul(MDD_OPAtom));
287
288       if ( m_Info.LabelSetType == LS_MXF_INTEROP )
289         OPAtomUL.Set(Dict::ul(MDD_MXFInterop_OPAtom));
290
291       m_BodyPart.OperationalPattern = OPAtomUL;
292       m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(1, m_BodyPart.ThisPartition)); // Second RIP Entry
293       
294       UL BodyUL(Dict::ul(MDD_ClosedCompleteBodyPartition));
295       result = m_BodyPart.WriteToFile(m_File, BodyUL);
296     }
297
298   if ( ASDCP_SUCCESS(result) )
299     {
300       // Index setup
301       Kumu::fpos_t ECoffset = m_File.Tell();
302
303       if ( BytesPerEditUnit == 0 )
304         m_FooterPart.SetIndexParamsVBR(&m_HeaderPart.m_Primer, EditRate, ECoffset);
305       else
306         m_FooterPart.SetIndexParamsCBR(&m_HeaderPart.m_Primer, BytesPerEditUnit, EditRate);
307     }
308
309   return result;
310 }
311
312
313
314 // standard method of writing a plaintext or encrypted frame
315 Result_t
316 ASDCP::h__Writer::WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf, const byte_t* EssenceUL,
317                                   AESEncContext* Ctx, HMACContext* HMAC)
318 {
319   Result_t result = RESULT_OK;
320   IntegrityPack IntPack;
321
322   byte_t overhead[128];
323   Kumu::MemIOWriter Overhead(overhead, 128);
324
325   if ( FrameBuf.Size() == 0 )
326     {
327       DefaultLogSink().Error("Cannot write empty frame buffer\n");
328       return RESULT_EMPTY_FB;
329     }
330
331   if ( m_Info.EncryptedEssence )
332     {
333       if ( ! Ctx )
334         return RESULT_CRYPT_CTX;
335
336       if ( m_Info.UsesHMAC && ! HMAC )
337         return RESULT_HMAC_CTX;
338
339       if ( FrameBuf.PlaintextOffset() > FrameBuf.Size() )
340         return RESULT_LARGE_PTO;
341
342       // encrypt the essence data (create encrypted source value)
343       result = EncryptFrameBuffer(FrameBuf, m_CtFrameBuf, Ctx);
344
345       // create HMAC
346       if ( ASDCP_SUCCESS(result) && m_Info.UsesHMAC )
347         result = IntPack.CalcValues(m_CtFrameBuf, m_Info.AssetUUID, m_FramesWritten + 1, HMAC);
348
349       if ( ASDCP_SUCCESS(result) )
350         { // write UL
351           if ( m_Info.LabelSetType == LS_MXF_INTEROP )
352             Overhead.WriteRaw(Dict::ul(MDD_MXFInterop_CryptEssence), SMPTE_UL_LENGTH);
353           else
354             Overhead.WriteRaw(Dict::ul(MDD_CryptEssence), SMPTE_UL_LENGTH);
355
356           // construct encrypted triplet header
357           ui32_t ETLength = klv_cryptinfo_size + m_CtFrameBuf.Size();
358
359           if ( m_Info.UsesHMAC )
360             ETLength += klv_intpack_size;
361           else
362             ETLength += (MXF_BER_LENGTH * 3); // for empty intpack
363
364           Overhead.WriteBER(ETLength, MXF_BER_LENGTH);                  // write encrypted triplet length
365           Overhead.WriteBER(UUIDlen, MXF_BER_LENGTH);                   // write ContextID length
366           Overhead.WriteRaw(m_Info.ContextID, UUIDlen);                  // write ContextID
367           Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH);            // write PlaintextOffset length
368           Overhead.WriteUi64BE(FrameBuf.PlaintextOffset());              // write PlaintextOffset
369           Overhead.WriteBER(SMPTE_UL_LENGTH, MXF_BER_LENGTH);              // write essence UL length
370           Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH);           // write the essence UL
371           Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH);            // write SourceLength length
372           Overhead.WriteUi64BE(FrameBuf.Size());                         // write SourceLength
373           Overhead.WriteBER(m_CtFrameBuf.Size(), MXF_BER_LENGTH);       // write ESV length
374
375           result = m_File.Writev(Overhead.Data(), Overhead.Length());
376         }
377
378       if ( ASDCP_SUCCESS(result) )
379         {
380           m_StreamOffset += Overhead.Length();
381           // write encrypted source value
382           result = m_File.Writev((byte_t*)m_CtFrameBuf.RoData(), m_CtFrameBuf.Size());
383         }
384
385       if ( ASDCP_SUCCESS(result) )
386         {
387           m_StreamOffset += m_CtFrameBuf.Size();
388
389           byte_t hmoverhead[512];
390           Kumu::MemIOWriter HMACOverhead(hmoverhead, 512);
391
392           // write the HMAC
393           if ( m_Info.UsesHMAC )
394             {
395               HMACOverhead.WriteRaw(IntPack.Data, klv_intpack_size);
396             }
397           else
398             { // we still need the var-pack length values if the intpack is empty
399               for ( ui32_t i = 0; i < 3 ; i++ )
400                 HMACOverhead.WriteBER(0, MXF_BER_LENGTH);
401             }
402
403           // write HMAC
404           result = m_File.Writev(HMACOverhead.Data(), HMACOverhead.Length());
405           m_StreamOffset += HMACOverhead.Length();
406         }
407     }
408   else
409     {
410       Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH);
411       Overhead.WriteBER(FrameBuf.Size(), MXF_BER_LENGTH);
412       result = m_File.Writev(Overhead.Data(), Overhead.Length());
413  
414       if ( ASDCP_SUCCESS(result) )
415         result = m_File.Writev((byte_t*)FrameBuf.RoData(), FrameBuf.Size());
416
417       if ( ASDCP_SUCCESS(result) )
418         m_StreamOffset += Overhead.Length() + FrameBuf.Size();
419     }
420
421   if ( ASDCP_SUCCESS(result) )
422     result = m_File.Writev();
423
424   return result;
425 }
426
427
428 // standard method of writing the header and footer of a completed MXF file
429 //
430 Result_t
431 ASDCP::h__Writer::WriteMXFFooter()
432 {
433   // Set top-level file package correctly for OP-Atom
434
435   m_MPTCSequence->Duration = m_MPTimecode->Duration = m_MPClSequence->Duration = m_MPClip->Duration = 
436     m_FPTCSequence->Duration = m_FPTimecode->Duration = m_FPClSequence->Duration = m_FPClip->Duration = 
437     m_EssenceDescriptor->ContainerDuration = m_FramesWritten;
438
439   Kumu::fpos_t here = m_File.Tell();
440   m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(0, here)); // Third RIP Entry
441   m_HeaderPart.FooterPartition = here;
442
443   // re-label the partition
444   UL OPAtomUL(Dict::ul(MDD_OPAtom));
445
446   if ( m_Info.LabelSetType == LS_MXF_INTEROP )
447     OPAtomUL.Set(Dict::ul(MDD_MXFInterop_OPAtom));
448   
449   m_HeaderPart.OperationalPattern = OPAtomUL;
450   m_HeaderPart.m_Preface->OperationalPattern = m_HeaderPart.OperationalPattern;
451
452   m_FooterPart.PreviousPartition = m_BodyPart.ThisPartition;
453   m_FooterPart.OperationalPattern = m_HeaderPart.OperationalPattern;
454   m_FooterPart.EssenceContainers = m_HeaderPart.EssenceContainers;
455   m_FooterPart.FooterPartition = here;
456   m_FooterPart.ThisPartition = here;
457
458   Result_t result = m_FooterPart.WriteToFile(m_File, m_FramesWritten);
459
460   if ( ASDCP_SUCCESS(result) )
461     result = m_HeaderPart.m_RIP.WriteToFile(m_File);
462
463   if ( ASDCP_SUCCESS(result) )
464     result = m_File.Seek(0);
465
466   if ( ASDCP_SUCCESS(result) )
467     result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
468
469   m_File.Close();
470   return result;
471 }
472
473 //
474 // end h__Writer.cpp
475 //