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