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