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