broken build, adding write
[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 #include "MDD.h"
37 #include <assert.h>
38
39 using namespace ASDCP;
40 using namespace ASDCP::MXF;
41
42
43 ASDCP::h__Writer::h__Writer() : m_FramesWritten(0), m_StreamOffset(0)
44 {
45 }
46
47 ASDCP::h__Writer::~h__Writer()
48 {
49 }
50
51 // standard method of writing the header of a new MXF file
52 Result_t
53 ASDCP::h__Writer::WriteMXFHeader(EssenceType_t EssenceType, ASDCP::Rational& EditRate,
54                           ui32_t TCFrameRate, ui32_t BytesPerEditUnit)
55 {
56 #if 0
57   // write the stream metadata
58   m_Metadata = new Metadata();
59   assert(m_Metadata);
60   assert(m_Metadata->m_Object);
61
62   if ( m_Info.EncryptedEssence )
63     {
64       UL DMSUL(CryptoFrameworkUL_Data);
65       m_Metadata->AddDMScheme(DMSUL);
66     }
67
68   // Set the OP label
69   // If we are writing OP-Atom we write the header as OP1a initially as another process
70   // may try to read the file before it is complete and then it will NOT be a valid OP-Atom file
71   m_Metadata->SetOP(OP1aUL);
72
73   // Build the Material Package
74   // DRAGONS: We should really try and determine the UMID type rather than cop-out!
75   UMID PackageUMID;
76   PackageUMID.MakeUMID(0x0d); // mixed type
77
78   m_MaterialPackage = m_Metadata->AddMaterialPackage("AS-DCP Material Package", PackageUMID);
79   m_Metadata->SetPrimaryPackage(m_MaterialPackage);     // This will be overwritten for OP-Atom
80   
81   TrackPtr MPTimecodeTrack = m_MaterialPackage->AddTimecodeTrack(EditRate);
82   m_MPTimecode = MPTimecodeTrack->AddTimecodeComponent(TCFrameRate, 0, 0);
83
84   TrackPtr FPTimecodeTrack = 0;
85   mxflib::UUID assetUUID(m_Info.AssetUUID);
86
87   UMID EssenceUMID;
88   
89   switch ( EssenceType )
90     {
91     case ESS_MPEG2_VES:
92       PackageUMID.MakeUMID(0x0f, assetUUID);
93       m_FilePackage = m_Metadata->AddFilePackage(1, MPEG_PACKAGE_LABEL, PackageUMID);
94       m_MPTrack = m_MaterialPackage->AddPictureTrack(EditRate);
95       m_FPTrack = m_FilePackage->AddPictureTrack(0, EditRate);
96       break;
97           
98     case ESS_JPEG_2000:
99       PackageUMID.MakeUMID(0x0f, assetUUID);
100       m_FilePackage = m_Metadata->AddFilePackage(1, JP2K_PACKAGE_LABEL, PackageUMID);
101       m_MPTrack = m_MaterialPackage->AddPictureTrack(EditRate);
102       m_FPTrack = m_FilePackage->AddPictureTrack(0, EditRate);
103       break;
104           
105     case ESS_PCM_24b_48k:
106       PackageUMID.MakeUMID(0x0f, assetUUID);
107       m_FilePackage = m_Metadata->AddFilePackage(1, PCM_PACKAGE_LABEL, PackageUMID);
108       m_MPTrack = m_MaterialPackage->AddSoundTrack(EditRate);
109       m_FPTrack = m_FilePackage->AddSoundTrack(0, EditRate);
110       break;
111
112     default: return RESULT_RAW_ESS;
113     }
114
115   // Add an essence element
116   FPTimecodeTrack = m_FilePackage->AddTimecodeTrack(EditRate);
117   m_FPTimecode = FPTimecodeTrack->AddTimecodeComponent(TCFrameRate, 0/* NDF */,
118                                                        tc_to_frames(TCFrameRate, 1, 0, 0, 0) );
119   
120   // Add a single Component to this Track of the Material Package
121   m_MPClip = m_MPTrack->AddSourceClip();
122   
123   // Add a single Component to this Track of the File Package
124   m_FPClip = m_FPTrack->AddSourceClip();
125   const byte_t* SourceEssenceContainerLabel = 0;
126
127   // Frame wrapping
128   if ( m_Info.EncryptedEssence )
129     {
130       switch ( EssenceType )
131         {
132         case ESS_MPEG2_VES:
133           SourceEssenceContainerLabel = WrappingUL_Data_MPEG2_VES;
134           break;
135           
136         case ESS_JPEG_2000:
137           SourceEssenceContainerLabel = WrappingUL_Data_JPEG_2000;
138           break;
139             
140         case ESS_PCM_24b_48k:
141           SourceEssenceContainerLabel = WrappingUL_Data_PCM_24b_48k;
142           break;
143           
144         default:
145           return RESULT_RAW_ESS;
146         }
147     }
148
149   mem_ptr<UL> WrappingUL;
150   switch ( EssenceType )
151     {
152     case ESS_MPEG2_VES:
153       WrappingUL = new UL(WrappingUL_Data_MPEG2_VES); // memchk TESTED
154       break;
155         
156     case ESS_JPEG_2000:
157       WrappingUL = new UL(WrappingUL_Data_JPEG_2000); // memchk TESTED
158       break;
159         
160     case ESS_PCM_24b_48k:
161       WrappingUL = new UL(WrappingUL_Data_PCM_24b_48k); // memchk TESTED
162       break;
163       
164     default:
165       return RESULT_RAW_ESS;
166     }
167   assert(!WrappingUL.empty());
168   m_EssenceDescriptor->SetValue("EssenceContainer", DataChunk(klv_key_size, WrappingUL->GetValue()));
169   
170   // Write a File Descriptor only on the internally ref'ed Track 
171   m_EssenceDescriptor->SetUint("LinkedTrackID", m_FPTrack->GetUint("TrackID"));
172   m_FilePackage->AddChild("Descriptor")->MakeLink(*m_EssenceDescriptor);
173
174   UL CryptEssenceUL(WrappingUL_Data_Crypt);
175
176   if ( m_Info.EncryptedEssence )
177     {
178       m_Metadata->AddEssenceType(CryptEssenceUL);
179     }
180   else
181     {
182       UL GCUL(GCMulti_Data);
183       m_Metadata->AddEssenceType(GCUL);
184       m_Metadata->AddEssenceType(*WrappingUL);
185     }
186
187   // Link the MP to the FP
188   m_MPClip->MakeLink(m_FPTrack, 0);
189
190   //
191   // ** Write out the header **
192   //
193
194   m_HeaderPart = new Partition("OpenHeader");
195   assert(m_HeaderPart);
196   m_HeaderPart->SetKAG(1);                      // Everything else can stay at default
197   m_HeaderPart->SetUint("BodySID", 1);
198   
199   m_HeaderPart->AddMetadata(m_Metadata);
200
201   // Build an Ident set describing us and link into the metadata
202   MDObject* Ident = new MDObject("Identification");
203   assert(Ident);
204   Ident->SetString("CompanyName", m_Info.CompanyName);
205   Ident->SetString("ProductName", m_Info.ProductName);
206   Ident->SetString("VersionString", m_Info.ProductVersion);
207   UUID ProductUID(m_Info.ProductUUID);
208   Ident->SetValue("ProductUID", DataChunk(UUIDlen, ProductUID.GetValue()));
209
210   // TODO: get Oliver to show me how this works
211   //      Ident->SetString("ToolkitVersion", ?);
212
213   m_Metadata->UpdateGenerations(*Ident);
214
215   if ( m_Info.EncryptedEssence )
216     AddDMScrypt(m_FilePackage, m_Info, SourceEssenceContainerLabel);
217
218   // Write the header partition
219   m_File->WritePartition(*m_HeaderPart, HeaderPadding);
220       
221   // set up the index
222   switch ( EssenceType )
223     {
224     case ESS_MPEG2_VES:
225     case ESS_JPEG_2000:
226       m_IndexMan = new IndexManager(0, 0);
227       m_IndexMan->SetPosTableIndex(0, -1);
228       break;
229
230     case ESS_PCM_24b_48k:
231       m_IndexMan = new IndexManager(0, BytesPerEditUnit);
232       break;
233
234     case ESS_UNKNOWN:
235       return RESULT_INIT;
236     }
237
238   m_IndexMan->SetBodySID(1);
239   m_IndexMan->SetIndexSID(129);
240   m_IndexMan->SetEditRate(EditRate);
241 #endif
242
243   return RESULT_OK;
244 }
245
246 // standard method of writing a plaintext or encrypted frame
247 Result_t
248 ASDCP::h__Writer::WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf, const byte_t* EssenceUL,
249                                   AESEncContext* Ctx, HMACContext* HMAC)
250 {
251   Result_t result;
252   IntegrityPack IntPack;
253
254   byte_t overhead[128];
255   MemIOWriter Overhead(overhead, 128);
256
257   if ( FrameBuf.Size() == 0 )
258     {
259       DefaultLogSink().Error("Cannot write empty frame buffer\n");
260       return RESULT_EMPTY_FB;
261     }
262
263   if ( m_Info.EncryptedEssence )
264     {
265       if ( ! Ctx )
266         return RESULT_CRYPT_CTX;
267
268       if ( m_Info.UsesHMAC && ! HMAC )
269         return RESULT_HMAC_CTX;
270
271       if ( FrameBuf.PlaintextOffset() > FrameBuf.Size() )
272         return RESULT_LARGE_PTO;
273
274       // encrypt the essence data (create encrypted source value)
275       result = EncryptFrameBuffer(FrameBuf, m_CtFrameBuf, Ctx);
276
277       // create HMAC
278       if ( ASDCP_SUCCESS(result) && m_Info.UsesHMAC )
279         result = IntPack.CalcValues(m_CtFrameBuf, m_Info.AssetUUID, m_FramesWritten + 1, HMAC);
280
281       if ( ASDCP_SUCCESS(result) )
282         { // write UL
283           Overhead.WriteRaw((byte_t*)CryptEssenceUL_Data, klv_key_size);
284
285           // construct encrypted triplet header
286           ui32_t ETLength = klv_cryptinfo_size + m_CtFrameBuf.Size();
287
288           if ( m_Info.UsesHMAC )
289             ETLength += klv_intpack_size;
290           else
291             ETLength += (klv_length_size * 3); // for empty intpack
292
293           Overhead.WriteBER(ETLength, klv_length_size);                  // write encrypted triplet length
294           Overhead.WriteBER(UUIDlen, klv_length_size);                   // write ContextID length
295           Overhead.WriteRaw(m_Info.ContextID, UUIDlen);                  // write ContextID
296           Overhead.WriteBER(sizeof(ui64_t), klv_length_size);            // write PlaintextOffset length
297           Overhead.WriteUi64BE(FrameBuf.PlaintextOffset());              // write PlaintextOffset
298           Overhead.WriteBER(klv_key_size, klv_length_size);              // write essence UL length
299           Overhead.WriteRaw((byte_t*)EssenceUL, klv_key_size);           // write the essence UL
300           Overhead.WriteBER(sizeof(ui64_t), klv_length_size);            // write SourceLength length
301           Overhead.WriteUi64BE(FrameBuf.Size());                         // write SourceLength
302           Overhead.WriteBER(m_CtFrameBuf.Size(), klv_length_size);       // write ESV length
303
304           result = m_File.Writev(Overhead.Data(), Overhead.Size());
305         }
306
307       if ( ASDCP_SUCCESS(result) )
308         {
309           m_StreamOffset += Overhead.Size();
310           // write encrypted source value
311           result = m_File.Writev((byte_t*)m_CtFrameBuf.RoData(), m_CtFrameBuf.Size());
312         }
313
314       if ( ASDCP_SUCCESS(result) )
315         {
316           m_StreamOffset += m_CtFrameBuf.Size();
317
318           byte_t hmoverhead[512];
319           MemIOWriter HMACOverhead(hmoverhead, 512);
320
321           // write the HMAC
322           if ( m_Info.UsesHMAC )
323             {
324               HMACOverhead.WriteRaw(IntPack.Data, klv_intpack_size);
325             }
326           else
327             { // we still need the var-pack length values if the intpack is empty
328               for ( ui32_t i = 0; i < 3 ; i++ )
329                 HMACOverhead.WriteBER(0, klv_length_size);
330             }
331
332           // write HMAC
333           result = m_File.Writev(HMACOverhead.Data(), HMACOverhead.Size());
334           m_StreamOffset += HMACOverhead.Size();
335         }
336     }
337   else
338     {
339       Overhead.WriteRaw((byte_t*)EssenceUL, klv_key_size);
340       Overhead.WriteBER(FrameBuf.Size(), klv_length_size);
341       result = m_File.Writev(Overhead.Data(), Overhead.Size());
342  
343       if ( ASDCP_SUCCESS(result) )
344         result = m_File.Writev((byte_t*)FrameBuf.RoData(), FrameBuf.Size());
345
346       if ( ASDCP_SUCCESS(result) )
347         m_StreamOffset += Overhead.Size() + FrameBuf.Size();
348     }
349
350   if ( ASDCP_SUCCESS(result) )
351     result = m_File.Writev();
352
353   return result;
354 }
355
356
357 // standard method of writing the header and footer of a completed MXF file
358 //
359 Result_t
360 ASDCP::h__Writer::WriteMXFFooter(EssenceType_t EssenceType)
361 {
362 #if 0
363   // write the index
364   DataChunk IndexChunk;
365   ui32_t IndexSID = 0;
366
367   // Find all essence container data sets so we can update "IndexSID"
368   MDObjectListPtr ECDataSets = 0;
369   MDObject* Ptr = (*m_Metadata)["ContentStorage"];
370   if ( Ptr )
371     Ptr = Ptr->GetLink();
372   if ( Ptr )
373     Ptr = (*Ptr)["EssenceContainerData"];
374   if ( Ptr )
375     ECDataSets = Ptr->ChildList("EssenceContainer");
376
377   // ** Handle full index tables next **
378   // ***********************************
379       
380   // Make an index table containing all available entries
381   IndexTablePtr Index = m_IndexMan->MakeIndex();
382   m_IndexMan->AddEntriesToIndex(Index);
383       
384   // Write the index table
385   Index->WriteIndex(IndexChunk);
386                                 
387   // Record the IndexSID for when the index is written
388   IndexSID = Index->IndexSID;
389
390   // Update IndexSID in essence container data set
391   MDObjectList::iterator ECD_it = ECDataSets->begin();
392   while(ECD_it != ECDataSets->end())
393     {
394       if((*ECD_it)->GetLink())
395         {
396           if((*ECD_it)->GetLink()->GetUint("BodySID") == m_IndexMan->GetBodySID())
397             {
398               (*ECD_it)->GetLink()->SetUint("IndexSID", m_IndexMan->GetIndexSID());
399               break;
400             }
401         }
402
403       ECD_it++;
404     }
405
406   // If we are writing OP-Atom this is the first place we can claim it
407   m_Metadata->SetOP(OPAtomUL);
408   
409   // Set top-level file package correctly for OP-Atom
410   m_Metadata->SetPrimaryPackage(m_FilePackage);
411   
412   m_Metadata->SetTime();
413   m_MPTimecode->SetDuration(m_FramesWritten);
414
415   m_MPClip->SetDuration(m_FramesWritten);
416   m_FPTimecode->SetDuration(m_FramesWritten);
417   m_FPClip->SetDuration(m_FramesWritten);
418   m_EssenceDescriptor->SetInt64("ContainerDuration", m_FramesWritten);
419
420   // Turn the header or body partition into a footer
421   m_HeaderPart->ChangeType("CompleteFooter");
422   m_HeaderPart->SetUint("IndexSID",  IndexSID);
423
424   // Make sure any new sets are linked in
425   m_HeaderPart->UpdateMetadata(m_Metadata);
426
427   // Actually write the footer
428   m_File.WritePartitionWithIndex(*m_HeaderPart, &IndexChunk, false);
429
430   // Add a RIP
431   m_File.WriteRIP();
432
433   //
434   // ** Update the header ** 
435   //
436   // For generalized OPs update the value of "FooterPartition" in the header pack
437   // For OP-Atom re-write the entire header
438   //
439   ASDCP::fpos_t FooterPos = m_HeaderPart->GetUint64("FooterPartition");
440   m_File.Seek(0);
441
442   m_HeaderPart->ChangeType("ClosedCompleteHeader");
443   m_HeaderPart->SetUint64("FooterPartition", FooterPos);
444   m_HeaderPart->SetUint64("BodySID", 1);
445
446   m_File.ReWritePartition(*m_HeaderPart);
447   m_File.Close();
448 #endif
449   return RESULT_OK;
450 }
451
452 //
453 // end h__Writer.cpp
454 //