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"
35 using namespace ASDCP;
36 using namespace ASDCP::MXF;
38 // a magic number identifying asdcplib
39 #ifndef ASDCP_BUILD_NUMBER
40 #define ASDCP_BUILD_NUMBER 0x6A68
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)
54 ASDCP::h__Writer::~h__Writer()
60 // add DMS CryptographicFramework entry to source package
62 AddDMScrypt(Partition& HeaderPart, SourcePackage& Package, WriterInfo& Descr, const UL& WrappingUL)
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;
71 Sequence* Seq = new Sequence;
72 HeaderPart.AddChildObject(Seq);
73 NewTrack->Sequence = Seq->InstanceUID;
74 Seq->DataDefinition = UL(Dict::ul(MDD_DescriptiveMetaDataDef));
76 DMSegment* Segment = new DMSegment;
77 HeaderPart.AddChildObject(Segment);
78 Seq->StructuralComponents.push_back(Segment->InstanceUID);
79 Segment->EventComment = "AS-DCP KLV Encryption";
81 CryptographicFramework* CFW = new CryptographicFramework;
82 HeaderPart.AddChildObject(CFW);
83 Segment->DMFramework = CFW->InstanceUID;
85 CryptographicContext* Context = new CryptographicContext;
86 HeaderPart.AddChildObject(Context);
87 CFW->ContextSR = Context->InstanceUID;
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);
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)
103 ASDCP_TEST_NULL(m_EssenceDescriptor);
105 m_HeaderPart.m_Primer.ClearTagList();
106 m_HeaderPart.m_Preface = new Preface;
107 m_HeaderPart.AddChildObject(m_HeaderPart.m_Preface);
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
118 Identification* Ident = new Identification;
119 m_HeaderPart.AddChildObject(Ident);
120 m_HeaderPart.m_Preface->Identifications.push_back(Ident->InstanceUID);
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_DEVELOPMENT;
135 ContentStorage* Storage = new ContentStorage;
136 m_HeaderPart.AddChildObject(Storage);
137 m_HeaderPart.m_Preface->ContentStorage = Storage->InstanceUID;
139 EssenceContainerData* ECD = new EssenceContainerData;
140 m_HeaderPart.AddChildObject(ECD);
141 Storage->EssenceContainerData.push_back(ECD->InstanceUID);
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);
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";
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));
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));
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();
184 m_MPClSequence = new Sequence;
185 m_HeaderPart.AddChildObject(m_MPClSequence);
186 NewTrack->Sequence = m_MPClSequence->InstanceUID;
187 m_MPClSequence->DataDefinition = DataDefinition;
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;
195 // File (Source) Package
197 UUID assetUUID(m_Info.AssetUUID);
198 PackageUMID.MakeUMID(0x0f, assetUUID);
200 m_FilePackage = new SourcePackage;
201 m_FilePackage->Name = PackageLabel.c_str();
202 m_FilePackage->PackageUID = PackageUMID;
203 ECD->LinkedPackageUID = PackageUMID;
204 m_MPClip->SourcePackageID = PackageUMID;
205 m_MPClip->SourceTrackID = 1;
207 m_HeaderPart.AddChildObject(m_FilePackage);
208 Storage->Packages.push_back(m_FilePackage->InstanceUID);
211 NewTrack = new Track;
212 m_HeaderPart.AddChildObject(NewTrack);
213 NewTrack->EditRate = EditRate;
214 m_FilePackage->Tracks.push_back(NewTrack->InstanceUID);
215 NewTrack->TrackID = 1;
216 NewTrack->TrackName = "Timecode Track";
218 m_FPTCSequence = new Sequence;
219 m_HeaderPart.AddChildObject(m_FPTCSequence);
220 NewTrack->Sequence = m_FPTCSequence->InstanceUID;
221 m_FPTCSequence->DataDefinition = UL(Dict::ul(MDD_TimecodeDataDef));
223 m_FPTimecode = new TimecodeComponent;
224 m_HeaderPart.AddChildObject(m_FPTimecode);
225 m_FPTCSequence->StructuralComponents.push_back(m_FPTimecode->InstanceUID);
226 m_FPTimecode->RoundedTimecodeBase = TCFrameRate;
227 m_FPTimecode->StartTimecode = ui64_C(86400); // 01:00:00:00
228 m_FPTimecode->DataDefinition = UL(Dict::ul(MDD_TimecodeDataDef));
231 NewTrack = new Track;
232 m_HeaderPart.AddChildObject(NewTrack);
233 NewTrack->EditRate = EditRate;
234 m_FilePackage->Tracks.push_back(NewTrack->InstanceUID);
235 NewTrack->TrackID = 2;
236 NewTrack->TrackName = TrackName.c_str();
238 m_FPClSequence = new Sequence;
239 m_HeaderPart.AddChildObject(m_FPClSequence);
240 NewTrack->Sequence = m_FPClSequence->InstanceUID;
241 m_FPClSequence->DataDefinition = DataDefinition;
243 m_FPClip = new SourceClip;
244 m_HeaderPart.AddChildObject(m_FPClip);
245 m_FPClSequence->StructuralComponents.push_back(m_FPClip->InstanceUID);
246 m_FPClip->DataDefinition = DataDefinition;
249 // Essence Descriptor
251 m_EssenceDescriptor->EssenceContainer = WrappingUL;
252 m_EssenceDescriptor->LinkedTrackID = NewTrack->TrackID;
253 m_HeaderPart.m_Preface->PrimaryPackage = m_FilePackage->InstanceUID;
256 // Encryption Descriptor
258 if ( m_Info.EncryptedEssence )
260 UL CryptEssenceUL(Dict::ul(MDD_EncryptedContainerLabel));
261 m_HeaderPart.EssenceContainers.push_back(CryptEssenceUL);
262 m_HeaderPart.m_Preface->DMSchemes.push_back(UL(Dict::ul(MDD_CryptographicFrameworkLabel)));
263 AddDMScrypt(m_HeaderPart, *m_FilePackage, m_Info, WrappingUL);
267 m_HeaderPart.EssenceContainers.push_back(UL(Dict::ul(MDD_GCMulti)));
268 m_HeaderPart.EssenceContainers.push_back(WrappingUL);
271 m_HeaderPart.m_Preface->EssenceContainers = m_HeaderPart.EssenceContainers;
272 m_HeaderPart.AddChildObject(m_EssenceDescriptor);
273 m_FilePackage->Descriptor = m_EssenceDescriptor->InstanceUID;
275 // Write the header partition
276 Result_t result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
278 if ( ASDCP_SUCCESS(result) )
281 m_BodyPart.EssenceContainers = m_HeaderPart.EssenceContainers;
282 m_BodyPart.ThisPartition = m_File.Tell();
283 m_BodyPart.BodySID = 1;
284 UL OPAtomUL(Dict::ul(MDD_OPAtom));
286 if ( m_Info.LabelSetType == LS_MXF_INTEROP )
287 OPAtomUL.Set(Dict::ul(MDD_MXFInterop_OPAtom));
289 m_BodyPart.OperationalPattern = OPAtomUL;
290 m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(1, m_BodyPart.ThisPartition)); // Second RIP Entry
292 UL BodyUL(Dict::ul(MDD_ClosedCompleteBodyPartition));
293 result = m_BodyPart.WriteToFile(m_File, BodyUL);
296 if ( ASDCP_SUCCESS(result) )
299 Kumu::fpos_t ECoffset = m_File.Tell();
301 if ( BytesPerEditUnit == 0 )
302 m_FooterPart.SetIndexParamsVBR(&m_HeaderPart.m_Primer, EditRate, ECoffset);
304 m_FooterPart.SetIndexParamsCBR(&m_HeaderPart.m_Primer, BytesPerEditUnit, EditRate);
312 // standard method of writing a plaintext or encrypted frame
314 ASDCP::h__Writer::WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf, const byte_t* EssenceUL,
315 AESEncContext* Ctx, HMACContext* HMAC)
317 Result_t result = RESULT_OK;
318 IntegrityPack IntPack;
320 byte_t overhead[128];
321 Kumu::MemIOWriter Overhead(overhead, 128);
323 if ( FrameBuf.Size() == 0 )
325 DefaultLogSink().Error("Cannot write empty frame buffer\n");
326 return RESULT_EMPTY_FB;
329 if ( m_Info.EncryptedEssence )
332 return RESULT_CRYPT_CTX;
334 if ( m_Info.UsesHMAC && ! HMAC )
335 return RESULT_HMAC_CTX;
337 if ( FrameBuf.PlaintextOffset() > FrameBuf.Size() )
338 return RESULT_LARGE_PTO;
340 // encrypt the essence data (create encrypted source value)
341 result = EncryptFrameBuffer(FrameBuf, m_CtFrameBuf, Ctx);
344 if ( ASDCP_SUCCESS(result) && m_Info.UsesHMAC )
345 result = IntPack.CalcValues(m_CtFrameBuf, m_Info.AssetUUID, m_FramesWritten + 1, HMAC);
347 if ( ASDCP_SUCCESS(result) )
349 if ( m_Info.LabelSetType == LS_MXF_INTEROP )
350 Overhead.WriteRaw(Dict::ul(MDD_MXFInterop_CryptEssence), SMPTE_UL_LENGTH);
352 Overhead.WriteRaw(Dict::ul(MDD_CryptEssence), SMPTE_UL_LENGTH);
354 // construct encrypted triplet header
355 ui32_t ETLength = klv_cryptinfo_size + m_CtFrameBuf.Size();
357 if ( m_Info.UsesHMAC )
358 ETLength += klv_intpack_size;
360 ETLength += (MXF_BER_LENGTH * 3); // for empty intpack
362 Overhead.WriteBER(ETLength, MXF_BER_LENGTH); // write encrypted triplet length
363 Overhead.WriteBER(UUIDlen, MXF_BER_LENGTH); // write ContextID length
364 Overhead.WriteRaw(m_Info.ContextID, UUIDlen); // write ContextID
365 Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH); // write PlaintextOffset length
366 Overhead.WriteUi64BE(FrameBuf.PlaintextOffset()); // write PlaintextOffset
367 Overhead.WriteBER(SMPTE_UL_LENGTH, MXF_BER_LENGTH); // write essence UL length
368 Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH); // write the essence UL
369 Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH); // write SourceLength length
370 Overhead.WriteUi64BE(FrameBuf.Size()); // write SourceLength
371 Overhead.WriteBER(m_CtFrameBuf.Size(), MXF_BER_LENGTH); // write ESV length
373 result = m_File.Writev(Overhead.Data(), Overhead.Length());
376 if ( ASDCP_SUCCESS(result) )
378 m_StreamOffset += Overhead.Length();
379 // write encrypted source value
380 result = m_File.Writev((byte_t*)m_CtFrameBuf.RoData(), m_CtFrameBuf.Size());
383 if ( ASDCP_SUCCESS(result) )
385 m_StreamOffset += m_CtFrameBuf.Size();
387 byte_t hmoverhead[512];
388 Kumu::MemIOWriter HMACOverhead(hmoverhead, 512);
391 if ( m_Info.UsesHMAC )
393 HMACOverhead.WriteRaw(IntPack.Data, klv_intpack_size);
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, MXF_BER_LENGTH);
402 result = m_File.Writev(HMACOverhead.Data(), HMACOverhead.Length());
403 m_StreamOffset += HMACOverhead.Length();
408 Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH);
409 Overhead.WriteBER(FrameBuf.Size(), MXF_BER_LENGTH);
410 result = m_File.Writev(Overhead.Data(), Overhead.Length());
412 if ( ASDCP_SUCCESS(result) )
413 result = m_File.Writev((byte_t*)FrameBuf.RoData(), FrameBuf.Size());
415 if ( ASDCP_SUCCESS(result) )
416 m_StreamOffset += Overhead.Length() + FrameBuf.Size();
419 if ( ASDCP_SUCCESS(result) )
420 result = m_File.Writev();
426 // standard method of writing the header and footer of a completed MXF file
429 ASDCP::h__Writer::WriteMXFFooter()
431 // Set top-level file package correctly for OP-Atom
433 m_MPTCSequence->Duration = m_MPTimecode->Duration = m_MPClSequence->Duration = m_MPClip->Duration =
434 m_FPTCSequence->Duration = m_FPTimecode->Duration = m_FPClSequence->Duration = m_FPClip->Duration =
435 m_EssenceDescriptor->ContainerDuration = m_FramesWritten;
437 Kumu::fpos_t here = m_File.Tell();
438 m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(0, here)); // Third RIP Entry
439 m_HeaderPart.FooterPartition = here;
441 // re-label the partition
442 UL OPAtomUL(Dict::ul(MDD_OPAtom));
444 if ( m_Info.LabelSetType == LS_MXF_INTEROP )
445 OPAtomUL.Set(Dict::ul(MDD_MXFInterop_OPAtom));
447 m_HeaderPart.OperationalPattern = OPAtomUL;
448 m_HeaderPart.m_Preface->OperationalPattern = m_HeaderPart.OperationalPattern;
450 m_FooterPart.PreviousPartition = m_BodyPart.ThisPartition;
451 m_FooterPart.OperationalPattern = m_HeaderPart.OperationalPattern;
452 m_FooterPart.EssenceContainers = m_HeaderPart.EssenceContainers;
453 m_FooterPart.FooterPartition = here;
454 m_FooterPart.ThisPartition = here;
456 Result_t result = m_FooterPart.WriteToFile(m_File, m_FramesWritten);
458 if ( ASDCP_SUCCESS(result) )
459 result = m_HeaderPart.m_RIP.WriteToFile(m_File);
461 if ( ASDCP_SUCCESS(result) )
462 result = m_File.Seek(0);
464 if ( ASDCP_SUCCESS(result) )
465 result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);