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;
205 // for now we do not allow setting this value, so all files will be 'original'
206 m_MPClip->SourcePackageID = NilUMID;
207 m_MPClip->SourceTrackID = 0;
209 m_HeaderPart.AddChildObject(m_FilePackage);
210 Storage->Packages.push_back(m_FilePackage->InstanceUID);
213 NewTrack = new Track;
214 m_HeaderPart.AddChildObject(NewTrack);
215 NewTrack->EditRate = EditRate;
216 m_FilePackage->Tracks.push_back(NewTrack->InstanceUID);
217 NewTrack->TrackID = 1;
218 NewTrack->TrackName = "Timecode Track";
220 m_FPTCSequence = new Sequence;
221 m_HeaderPart.AddChildObject(m_FPTCSequence);
222 NewTrack->Sequence = m_FPTCSequence->InstanceUID;
223 m_FPTCSequence->DataDefinition = UL(Dict::ul(MDD_TimecodeDataDef));
225 m_FPTimecode = new TimecodeComponent;
226 m_HeaderPart.AddChildObject(m_FPTimecode);
227 m_FPTCSequence->StructuralComponents.push_back(m_FPTimecode->InstanceUID);
228 m_FPTimecode->RoundedTimecodeBase = TCFrameRate;
229 m_FPTimecode->StartTimecode = ui64_C(86400); // 01:00:00:00
230 m_FPTimecode->DataDefinition = UL(Dict::ul(MDD_TimecodeDataDef));
233 NewTrack = new Track;
234 m_HeaderPart.AddChildObject(NewTrack);
235 NewTrack->EditRate = EditRate;
236 m_FilePackage->Tracks.push_back(NewTrack->InstanceUID);
237 NewTrack->TrackID = 2;
238 NewTrack->TrackName = TrackName.c_str();
240 m_FPClSequence = new Sequence;
241 m_HeaderPart.AddChildObject(m_FPClSequence);
242 NewTrack->Sequence = m_FPClSequence->InstanceUID;
243 m_FPClSequence->DataDefinition = DataDefinition;
245 m_FPClip = new SourceClip;
246 m_HeaderPart.AddChildObject(m_FPClip);
247 m_FPClSequence->StructuralComponents.push_back(m_FPClip->InstanceUID);
248 m_FPClip->DataDefinition = DataDefinition;
251 // Essence Descriptor
253 m_EssenceDescriptor->EssenceContainer = WrappingUL;
254 m_EssenceDescriptor->LinkedTrackID = NewTrack->TrackID;
255 m_HeaderPart.m_Preface->PrimaryPackage = m_FilePackage->InstanceUID;
258 // Encryption Descriptor
260 if ( m_Info.EncryptedEssence )
262 UL CryptEssenceUL(Dict::ul(MDD_EncryptedContainerLabel));
263 m_HeaderPart.EssenceContainers.push_back(CryptEssenceUL);
264 m_HeaderPart.m_Preface->DMSchemes.push_back(UL(Dict::ul(MDD_CryptographicFrameworkLabel)));
265 AddDMScrypt(m_HeaderPart, *m_FilePackage, m_Info, WrappingUL);
269 m_HeaderPart.EssenceContainers.push_back(UL(Dict::ul(MDD_GCMulti)));
270 m_HeaderPart.EssenceContainers.push_back(WrappingUL);
273 m_HeaderPart.m_Preface->EssenceContainers = m_HeaderPart.EssenceContainers;
274 m_HeaderPart.AddChildObject(m_EssenceDescriptor);
275 m_FilePackage->Descriptor = m_EssenceDescriptor->InstanceUID;
277 // Write the header partition
278 Result_t result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
280 if ( ASDCP_SUCCESS(result) )
283 m_BodyPart.EssenceContainers = m_HeaderPart.EssenceContainers;
284 m_BodyPart.ThisPartition = m_File.Tell();
285 m_BodyPart.BodySID = 1;
286 UL OPAtomUL(Dict::ul(MDD_OPAtom));
288 if ( m_Info.LabelSetType == LS_MXF_INTEROP )
289 OPAtomUL.Set(Dict::ul(MDD_MXFInterop_OPAtom));
291 m_BodyPart.OperationalPattern = OPAtomUL;
292 m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(1, m_BodyPart.ThisPartition)); // Second RIP Entry
294 UL BodyUL(Dict::ul(MDD_ClosedCompleteBodyPartition));
295 result = m_BodyPart.WriteToFile(m_File, BodyUL);
298 if ( ASDCP_SUCCESS(result) )
301 Kumu::fpos_t ECoffset = m_File.Tell();
303 if ( BytesPerEditUnit == 0 )
304 m_FooterPart.SetIndexParamsVBR(&m_HeaderPart.m_Primer, EditRate, ECoffset);
306 m_FooterPart.SetIndexParamsCBR(&m_HeaderPart.m_Primer, BytesPerEditUnit, EditRate);
314 // standard method of writing a plaintext or encrypted frame
316 ASDCP::h__Writer::WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf, const byte_t* EssenceUL,
317 AESEncContext* Ctx, HMACContext* HMAC)
319 Result_t result = RESULT_OK;
320 IntegrityPack IntPack;
322 byte_t overhead[128];
323 Kumu::MemIOWriter Overhead(overhead, 128);
325 if ( FrameBuf.Size() == 0 )
327 DefaultLogSink().Error("Cannot write empty frame buffer\n");
328 return RESULT_EMPTY_FB;
331 if ( m_Info.EncryptedEssence )
334 return RESULT_CRYPT_CTX;
336 if ( m_Info.UsesHMAC && ! HMAC )
337 return RESULT_HMAC_CTX;
339 if ( FrameBuf.PlaintextOffset() > FrameBuf.Size() )
340 return RESULT_LARGE_PTO;
342 // encrypt the essence data (create encrypted source value)
343 result = EncryptFrameBuffer(FrameBuf, m_CtFrameBuf, Ctx);
346 if ( ASDCP_SUCCESS(result) && m_Info.UsesHMAC )
347 result = IntPack.CalcValues(m_CtFrameBuf, m_Info.AssetUUID, m_FramesWritten + 1, HMAC);
349 if ( ASDCP_SUCCESS(result) )
351 if ( m_Info.LabelSetType == LS_MXF_INTEROP )
352 Overhead.WriteRaw(Dict::ul(MDD_MXFInterop_CryptEssence), SMPTE_UL_LENGTH);
354 Overhead.WriteRaw(Dict::ul(MDD_CryptEssence), SMPTE_UL_LENGTH);
356 // construct encrypted triplet header
357 ui32_t ETLength = klv_cryptinfo_size + m_CtFrameBuf.Size();
359 if ( m_Info.UsesHMAC )
360 ETLength += klv_intpack_size;
362 ETLength += (MXF_BER_LENGTH * 3); // for empty intpack
364 Overhead.WriteBER(ETLength, MXF_BER_LENGTH); // write encrypted triplet length
365 Overhead.WriteBER(UUIDlen, MXF_BER_LENGTH); // write ContextID length
366 Overhead.WriteRaw(m_Info.ContextID, UUIDlen); // write ContextID
367 Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH); // write PlaintextOffset length
368 Overhead.WriteUi64BE(FrameBuf.PlaintextOffset()); // write PlaintextOffset
369 Overhead.WriteBER(SMPTE_UL_LENGTH, MXF_BER_LENGTH); // write essence UL length
370 Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH); // write the essence UL
371 Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH); // write SourceLength length
372 Overhead.WriteUi64BE(FrameBuf.Size()); // write SourceLength
373 Overhead.WriteBER(m_CtFrameBuf.Size(), MXF_BER_LENGTH); // write ESV length
375 result = m_File.Writev(Overhead.Data(), Overhead.Length());
378 if ( ASDCP_SUCCESS(result) )
380 m_StreamOffset += Overhead.Length();
381 // write encrypted source value
382 result = m_File.Writev((byte_t*)m_CtFrameBuf.RoData(), m_CtFrameBuf.Size());
385 if ( ASDCP_SUCCESS(result) )
387 m_StreamOffset += m_CtFrameBuf.Size();
389 byte_t hmoverhead[512];
390 Kumu::MemIOWriter HMACOverhead(hmoverhead, 512);
393 if ( m_Info.UsesHMAC )
395 HMACOverhead.WriteRaw(IntPack.Data, klv_intpack_size);
398 { // we still need the var-pack length values if the intpack is empty
399 for ( ui32_t i = 0; i < 3 ; i++ )
400 HMACOverhead.WriteBER(0, MXF_BER_LENGTH);
404 result = m_File.Writev(HMACOverhead.Data(), HMACOverhead.Length());
405 m_StreamOffset += HMACOverhead.Length();
410 Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH);
411 Overhead.WriteBER(FrameBuf.Size(), MXF_BER_LENGTH);
412 result = m_File.Writev(Overhead.Data(), Overhead.Length());
414 if ( ASDCP_SUCCESS(result) )
415 result = m_File.Writev((byte_t*)FrameBuf.RoData(), FrameBuf.Size());
417 if ( ASDCP_SUCCESS(result) )
418 m_StreamOffset += Overhead.Length() + FrameBuf.Size();
421 if ( ASDCP_SUCCESS(result) )
422 result = m_File.Writev();
428 // standard method of writing the header and footer of a completed MXF file
431 ASDCP::h__Writer::WriteMXFFooter()
433 // Set top-level file package correctly for OP-Atom
435 m_MPTCSequence->Duration = m_MPTimecode->Duration = m_MPClSequence->Duration = m_MPClip->Duration =
436 m_FPTCSequence->Duration = m_FPTimecode->Duration = m_FPClSequence->Duration = m_FPClip->Duration =
437 m_EssenceDescriptor->ContainerDuration = m_FramesWritten;
439 Kumu::fpos_t here = m_File.Tell();
440 m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(0, here)); // Third RIP Entry
441 m_HeaderPart.FooterPartition = here;
443 // re-label the partition
444 UL OPAtomUL(Dict::ul(MDD_OPAtom));
446 if ( m_Info.LabelSetType == LS_MXF_INTEROP )
447 OPAtomUL.Set(Dict::ul(MDD_MXFInterop_OPAtom));
449 m_HeaderPart.OperationalPattern = OPAtomUL;
450 m_HeaderPart.m_Preface->OperationalPattern = m_HeaderPart.OperationalPattern;
452 m_FooterPart.PreviousPartition = m_BodyPart.ThisPartition;
453 m_FooterPart.OperationalPattern = m_HeaderPart.OperationalPattern;
454 m_FooterPart.EssenceContainers = m_HeaderPart.EssenceContainers;
455 m_FooterPart.FooterPartition = here;
456 m_FooterPart.ThisPartition = here;
458 Result_t result = m_FooterPart.WriteToFile(m_File, m_FramesWritten);
460 if ( ASDCP_SUCCESS(result) )
461 result = m_HeaderPart.m_RIP.WriteToFile(m_File);
463 if ( ASDCP_SUCCESS(result) )
464 result = m_File.Seek(0);
466 if ( ASDCP_SUCCESS(result) )
467 result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);