added aiff reader
[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           Overhead.WriteRaw(Dict::ul(MDD_CryptEssence), SMPTE_UL_LENGTH);
340
341           // construct encrypted triplet header
342           ui32_t ETLength = klv_cryptinfo_size + m_CtFrameBuf.Size();
343
344           if ( m_Info.UsesHMAC )
345             ETLength += klv_intpack_size;
346           else
347             ETLength += (MXF_BER_LENGTH * 3); // for empty intpack
348
349           Overhead.WriteBER(ETLength, MXF_BER_LENGTH);                  // write encrypted triplet length
350           Overhead.WriteBER(UUIDlen, MXF_BER_LENGTH);                   // write ContextID length
351           Overhead.WriteRaw(m_Info.ContextID, UUIDlen);                  // write ContextID
352           Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH);            // write PlaintextOffset length
353           Overhead.WriteUi64BE(FrameBuf.PlaintextOffset());              // write PlaintextOffset
354           Overhead.WriteBER(SMPTE_UL_LENGTH, MXF_BER_LENGTH);              // write essence UL length
355           Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH);           // write the essence UL
356           Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH);            // write SourceLength length
357           Overhead.WriteUi64BE(FrameBuf.Size());                         // write SourceLength
358           Overhead.WriteBER(m_CtFrameBuf.Size(), MXF_BER_LENGTH);       // write ESV length
359
360           result = m_File.Writev(Overhead.Data(), Overhead.Size());
361         }
362
363       if ( ASDCP_SUCCESS(result) )
364         {
365           m_StreamOffset += Overhead.Size();
366           // write encrypted source value
367           result = m_File.Writev((byte_t*)m_CtFrameBuf.RoData(), m_CtFrameBuf.Size());
368         }
369
370       if ( ASDCP_SUCCESS(result) )
371         {
372           m_StreamOffset += m_CtFrameBuf.Size();
373
374           byte_t hmoverhead[512];
375           MemIOWriter HMACOverhead(hmoverhead, 512);
376
377           // write the HMAC
378           if ( m_Info.UsesHMAC )
379             {
380               HMACOverhead.WriteRaw(IntPack.Data, klv_intpack_size);
381             }
382           else
383             { // we still need the var-pack length values if the intpack is empty
384               for ( ui32_t i = 0; i < 3 ; i++ )
385                 HMACOverhead.WriteBER(0, MXF_BER_LENGTH);
386             }
387
388           // write HMAC
389           result = m_File.Writev(HMACOverhead.Data(), HMACOverhead.Size());
390           m_StreamOffset += HMACOverhead.Size();
391         }
392     }
393   else
394     {
395       Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH);
396       Overhead.WriteBER(FrameBuf.Size(), MXF_BER_LENGTH);
397       result = m_File.Writev(Overhead.Data(), Overhead.Size());
398  
399       if ( ASDCP_SUCCESS(result) )
400         result = m_File.Writev((byte_t*)FrameBuf.RoData(), FrameBuf.Size());
401
402       if ( ASDCP_SUCCESS(result) )
403         m_StreamOffset += Overhead.Size() + FrameBuf.Size();
404     }
405
406   if ( ASDCP_SUCCESS(result) )
407     result = m_File.Writev();
408
409   return result;
410 }
411
412
413 // standard method of writing the header and footer of a completed MXF file
414 //
415 Result_t
416 ASDCP::h__Writer::WriteMXFFooter()
417 {
418   // Set top-level file package correctly for OP-Atom
419   m_MPTCSequence->Duration = m_FramesWritten;
420   m_MPTimecode->Duration = m_FramesWritten;
421   m_MPClSequence->Duration = m_FramesWritten;
422   m_MPClip->Duration = m_FramesWritten;
423   m_FPTCSequence->Duration = m_FramesWritten;
424   m_FPTimecode->Duration = m_FramesWritten;
425   m_FPClSequence->Duration = m_FramesWritten;
426   m_FPClip->Duration = m_FramesWritten;
427   m_EssenceDescriptor->ContainerDuration = m_FramesWritten;
428
429   fpos_t here = m_File.Tell();
430   m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(1, here)); // Third RIP Entry
431   m_HeaderPart.FooterPartition = here;
432   m_HeaderPart.BodySID = 1;
433   //  m_HeaderPart.IndexSID = m_FooterPart.IndexSID;
434   m_HeaderPart.OperationalPattern = UL(Dict::ul(MDD_OPAtom));
435   m_HeaderPart.m_Preface->OperationalPattern = m_HeaderPart.OperationalPattern;
436
437   m_FooterPart.OperationalPattern = m_HeaderPart.OperationalPattern;
438   m_FooterPart.EssenceContainers = m_HeaderPart.EssenceContainers;
439   m_FooterPart.FooterPartition = here;
440   m_FooterPart.ThisPartition = here;
441
442   Result_t result = m_FooterPart.WriteToFile(m_File, m_FramesWritten);
443
444   if ( ASDCP_SUCCESS(result) )
445     result = m_HeaderPart.m_RIP.WriteToFile(m_File);
446
447   if ( ASDCP_SUCCESS(result) )
448     result = m_File.Seek(0);
449
450   if ( ASDCP_SUCCESS(result) )
451     result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
452
453   m_File.Close();
454   return result;
455 }
456
457 //
458 // end h__Writer.cpp
459 //