2 Copyright (c) 2004-2006, John Hurst
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
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.
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.
27 /*! \file h__Writer.cpp
29 \brief MXF file writer base class
32 #include "AS_DCP_internal.h"
39 using namespace ASDCP;
40 using namespace ASDCP::MXF;
43 ASDCP::h__Writer::h__Writer() : m_FramesWritten(0), m_StreamOffset(0)
47 ASDCP::h__Writer::~h__Writer()
51 // standard method of writing the header of a new MXF file
53 ASDCP::h__Writer::WriteMXFHeader(EssenceType_t EssenceType, ASDCP::Rational& EditRate,
54 ui32_t TCFrameRate, ui32_t BytesPerEditUnit)
57 // write the stream metadata
58 m_Metadata = new Metadata();
60 assert(m_Metadata->m_Object);
62 if ( m_Info.EncryptedEssence )
64 UL DMSUL(CryptoFrameworkUL_Data);
65 m_Metadata->AddDMScheme(DMSUL);
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);
73 // Build the Material Package
74 // DRAGONS: We should really try and determine the UMID type rather than cop-out!
76 PackageUMID.MakeUMID(0x0d); // mixed type
78 m_MaterialPackage = m_Metadata->AddMaterialPackage("AS-DCP Material Package", PackageUMID);
79 m_Metadata->SetPrimaryPackage(m_MaterialPackage); // This will be overwritten for OP-Atom
81 TrackPtr MPTimecodeTrack = m_MaterialPackage->AddTimecodeTrack(EditRate);
82 m_MPTimecode = MPTimecodeTrack->AddTimecodeComponent(TCFrameRate, 0, 0);
84 TrackPtr FPTimecodeTrack = 0;
85 mxflib::UUID assetUUID(m_Info.AssetUUID);
89 switch ( EssenceType )
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);
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);
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);
112 default: return RESULT_RAW_ESS;
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) );
120 // Add a single Component to this Track of the Material Package
121 m_MPClip = m_MPTrack->AddSourceClip();
123 // Add a single Component to this Track of the File Package
124 m_FPClip = m_FPTrack->AddSourceClip();
125 const byte_t* SourceEssenceContainerLabel = 0;
128 if ( m_Info.EncryptedEssence )
130 switch ( EssenceType )
133 SourceEssenceContainerLabel = WrappingUL_Data_MPEG2_VES;
137 SourceEssenceContainerLabel = WrappingUL_Data_JPEG_2000;
140 case ESS_PCM_24b_48k:
141 SourceEssenceContainerLabel = WrappingUL_Data_PCM_24b_48k;
145 return RESULT_RAW_ESS;
149 mem_ptr<UL> WrappingUL;
150 switch ( EssenceType )
153 WrappingUL = new UL(WrappingUL_Data_MPEG2_VES); // memchk TESTED
157 WrappingUL = new UL(WrappingUL_Data_JPEG_2000); // memchk TESTED
160 case ESS_PCM_24b_48k:
161 WrappingUL = new UL(WrappingUL_Data_PCM_24b_48k); // memchk TESTED
165 return RESULT_RAW_ESS;
167 assert(!WrappingUL.empty());
168 m_EssenceDescriptor->SetValue("EssenceContainer", DataChunk(klv_key_size, WrappingUL->GetValue()));
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);
174 UL CryptEssenceUL(WrappingUL_Data_Crypt);
176 if ( m_Info.EncryptedEssence )
178 m_Metadata->AddEssenceType(CryptEssenceUL);
182 UL GCUL(GCMulti_Data);
183 m_Metadata->AddEssenceType(GCUL);
184 m_Metadata->AddEssenceType(*WrappingUL);
187 // Link the MP to the FP
188 m_MPClip->MakeLink(m_FPTrack, 0);
191 // ** Write out the header **
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);
199 m_HeaderPart->AddMetadata(m_Metadata);
201 // Build an Ident set describing us and link into the metadata
202 MDObject* Ident = new MDObject("Identification");
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()));
210 // TODO: get Oliver to show me how this works
211 // Ident->SetString("ToolkitVersion", ?);
213 m_Metadata->UpdateGenerations(*Ident);
215 if ( m_Info.EncryptedEssence )
216 AddDMScrypt(m_FilePackage, m_Info, SourceEssenceContainerLabel);
218 // Write the header partition
219 m_File->WritePartition(*m_HeaderPart, HeaderPadding);
222 switch ( EssenceType )
226 m_IndexMan = new IndexManager(0, 0);
227 m_IndexMan->SetPosTableIndex(0, -1);
230 case ESS_PCM_24b_48k:
231 m_IndexMan = new IndexManager(0, BytesPerEditUnit);
238 m_IndexMan->SetBodySID(1);
239 m_IndexMan->SetIndexSID(129);
240 m_IndexMan->SetEditRate(EditRate);
246 // standard method of writing a plaintext or encrypted frame
248 ASDCP::h__Writer::WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf, const byte_t* EssenceUL,
249 AESEncContext* Ctx, HMACContext* HMAC)
252 IntegrityPack IntPack;
254 byte_t overhead[128];
255 MemIOWriter Overhead(overhead, 128);
257 if ( FrameBuf.Size() == 0 )
259 DefaultLogSink().Error("Cannot write empty frame buffer\n");
260 return RESULT_EMPTY_FB;
263 if ( m_Info.EncryptedEssence )
266 return RESULT_CRYPT_CTX;
268 if ( m_Info.UsesHMAC && ! HMAC )
269 return RESULT_HMAC_CTX;
271 if ( FrameBuf.PlaintextOffset() > FrameBuf.Size() )
272 return RESULT_LARGE_PTO;
274 // encrypt the essence data (create encrypted source value)
275 result = EncryptFrameBuffer(FrameBuf, m_CtFrameBuf, Ctx);
278 if ( ASDCP_SUCCESS(result) && m_Info.UsesHMAC )
279 result = IntPack.CalcValues(m_CtFrameBuf, m_Info.AssetUUID, m_FramesWritten + 1, HMAC);
281 if ( ASDCP_SUCCESS(result) )
283 Overhead.WriteRaw((byte_t*)CryptEssenceUL_Data, klv_key_size);
285 // construct encrypted triplet header
286 ui32_t ETLength = klv_cryptinfo_size + m_CtFrameBuf.Size();
288 if ( m_Info.UsesHMAC )
289 ETLength += klv_intpack_size;
291 ETLength += (klv_length_size * 3); // for empty intpack
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
304 result = m_File.Writev(Overhead.Data(), Overhead.Size());
307 if ( ASDCP_SUCCESS(result) )
309 m_StreamOffset += Overhead.Size();
310 // write encrypted source value
311 result = m_File.Writev((byte_t*)m_CtFrameBuf.RoData(), m_CtFrameBuf.Size());
314 if ( ASDCP_SUCCESS(result) )
316 m_StreamOffset += m_CtFrameBuf.Size();
318 byte_t hmoverhead[512];
319 MemIOWriter HMACOverhead(hmoverhead, 512);
322 if ( m_Info.UsesHMAC )
324 HMACOverhead.WriteRaw(IntPack.Data, klv_intpack_size);
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);
333 result = m_File.Writev(HMACOverhead.Data(), HMACOverhead.Size());
334 m_StreamOffset += HMACOverhead.Size();
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());
343 if ( ASDCP_SUCCESS(result) )
344 result = m_File.Writev((byte_t*)FrameBuf.RoData(), FrameBuf.Size());
346 if ( ASDCP_SUCCESS(result) )
347 m_StreamOffset += Overhead.Size() + FrameBuf.Size();
350 if ( ASDCP_SUCCESS(result) )
351 result = m_File.Writev();
357 // standard method of writing the header and footer of a completed MXF file
360 ASDCP::h__Writer::WriteMXFFooter(EssenceType_t EssenceType)
364 DataChunk IndexChunk;
367 // Find all essence container data sets so we can update "IndexSID"
368 MDObjectListPtr ECDataSets = 0;
369 MDObject* Ptr = (*m_Metadata)["ContentStorage"];
371 Ptr = Ptr->GetLink();
373 Ptr = (*Ptr)["EssenceContainerData"];
375 ECDataSets = Ptr->ChildList("EssenceContainer");
377 // ** Handle full index tables next **
378 // ***********************************
380 // Make an index table containing all available entries
381 IndexTablePtr Index = m_IndexMan->MakeIndex();
382 m_IndexMan->AddEntriesToIndex(Index);
384 // Write the index table
385 Index->WriteIndex(IndexChunk);
387 // Record the IndexSID for when the index is written
388 IndexSID = Index->IndexSID;
390 // Update IndexSID in essence container data set
391 MDObjectList::iterator ECD_it = ECDataSets->begin();
392 while(ECD_it != ECDataSets->end())
394 if((*ECD_it)->GetLink())
396 if((*ECD_it)->GetLink()->GetUint("BodySID") == m_IndexMan->GetBodySID())
398 (*ECD_it)->GetLink()->SetUint("IndexSID", m_IndexMan->GetIndexSID());
406 // If we are writing OP-Atom this is the first place we can claim it
407 m_Metadata->SetOP(OPAtomUL);
409 // Set top-level file package correctly for OP-Atom
410 m_Metadata->SetPrimaryPackage(m_FilePackage);
412 m_Metadata->SetTime();
413 m_MPTimecode->SetDuration(m_FramesWritten);
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);
420 // Turn the header or body partition into a footer
421 m_HeaderPart->ChangeType("CompleteFooter");
422 m_HeaderPart->SetUint("IndexSID", IndexSID);
424 // Make sure any new sets are linked in
425 m_HeaderPart->UpdateMetadata(m_Metadata);
427 // Actually write the footer
428 m_File.WritePartitionWithIndex(*m_HeaderPart, &IndexChunk, false);
434 // ** Update the header **
436 // For generalized OPs update the value of "FooterPartition" in the header pack
437 // For OP-Atom re-write the entire header
439 ASDCP::fpos_t FooterPos = m_HeaderPart->GetUint64("FooterPartition");
442 m_HeaderPart->ChangeType("ClosedCompleteHeader");
443 m_HeaderPart->SetUint64("FooterPartition", FooterPos);
444 m_HeaderPart->SetUint64("BodySID", 1);
446 m_File.ReWritePartition(*m_HeaderPart);