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_RELEASE;
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 m_MPClip->SourcePackageID = PackageUMID;
206 m_MPClip->SourceTrackID = 2;
208 m_HeaderPart.AddChildObject(m_FilePackage);
209 Storage->Packages.push_back(m_FilePackage->InstanceUID);
212 NewTrack = new Track;
213 m_HeaderPart.AddChildObject(NewTrack);
214 NewTrack->EditRate = EditRate;
215 m_FilePackage->Tracks.push_back(NewTrack->InstanceUID);
216 NewTrack->TrackID = 1;
217 NewTrack->TrackName = "Timecode Track";
219 m_FPTCSequence = new Sequence;
220 m_HeaderPart.AddChildObject(m_FPTCSequence);
221 NewTrack->Sequence = m_FPTCSequence->InstanceUID;
222 m_FPTCSequence->DataDefinition = UL(Dict::ul(MDD_TimecodeDataDef));
224 m_FPTimecode = new TimecodeComponent;
225 m_HeaderPart.AddChildObject(m_FPTimecode);
226 m_FPTCSequence->StructuralComponents.push_back(m_FPTimecode->InstanceUID);
227 m_FPTimecode->RoundedTimecodeBase = TCFrameRate;
228 m_FPTimecode->StartTimecode = ui64_C(86400); // 01:00:00:00
229 m_FPTimecode->DataDefinition = UL(Dict::ul(MDD_TimecodeDataDef));
232 NewTrack = new Track;
233 m_HeaderPart.AddChildObject(NewTrack);
234 NewTrack->EditRate = EditRate;
235 m_FilePackage->Tracks.push_back(NewTrack->InstanceUID);
236 NewTrack->TrackID = 2;
237 NewTrack->TrackName = TrackName.c_str();
239 m_FPClSequence = new Sequence;
240 m_HeaderPart.AddChildObject(m_FPClSequence);
241 NewTrack->Sequence = m_FPClSequence->InstanceUID;
242 m_FPClSequence->DataDefinition = DataDefinition;
244 m_FPClip = new SourceClip;
245 m_HeaderPart.AddChildObject(m_FPClip);
246 m_FPClSequence->StructuralComponents.push_back(m_FPClip->InstanceUID);
247 m_FPClip->DataDefinition = DataDefinition;
249 // for now we do not allow setting this value, so all files will be 'original'
250 m_FPClip->SourceTrackID = 0;
251 m_FPClip->SourcePackageID = NilUMID;
254 // Essence Descriptor
256 m_EssenceDescriptor->EssenceContainer = WrappingUL;
257 m_EssenceDescriptor->LinkedTrackID = NewTrack->TrackID;
258 m_HeaderPart.m_Preface->PrimaryPackage = m_FilePackage->InstanceUID;
261 // Encryption Descriptor
263 if ( m_Info.EncryptedEssence )
265 UL CryptEssenceUL(Dict::ul(MDD_EncryptedContainerLabel));
266 m_HeaderPart.EssenceContainers.push_back(CryptEssenceUL);
267 m_HeaderPart.m_Preface->DMSchemes.push_back(UL(Dict::ul(MDD_CryptographicFrameworkLabel)));
268 AddDMScrypt(m_HeaderPart, *m_FilePackage, m_Info, WrappingUL);
272 m_HeaderPart.EssenceContainers.push_back(UL(Dict::ul(MDD_GCMulti)));
273 m_HeaderPart.EssenceContainers.push_back(WrappingUL);
276 m_HeaderPart.m_Preface->EssenceContainers = m_HeaderPart.EssenceContainers;
277 m_HeaderPart.AddChildObject(m_EssenceDescriptor);
278 m_FilePackage->Descriptor = m_EssenceDescriptor->InstanceUID;
280 // Write the header partition
281 Result_t result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
283 if ( ASDCP_SUCCESS(result) )
286 m_BodyPart.EssenceContainers = m_HeaderPart.EssenceContainers;
287 m_BodyPart.ThisPartition = m_File.Tell();
288 m_BodyPart.BodySID = 1;
289 UL OPAtomUL(Dict::ul(MDD_OPAtom));
291 if ( m_Info.LabelSetType == LS_MXF_INTEROP )
292 OPAtomUL.Set(Dict::ul(MDD_MXFInterop_OPAtom));
294 m_BodyPart.OperationalPattern = OPAtomUL;
295 m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(1, m_BodyPart.ThisPartition)); // Second RIP Entry
297 UL BodyUL(Dict::ul(MDD_ClosedCompleteBodyPartition));
298 result = m_BodyPart.WriteToFile(m_File, BodyUL);
301 if ( ASDCP_SUCCESS(result) )
304 Kumu::fpos_t ECoffset = m_File.Tell();
306 if ( BytesPerEditUnit == 0 )
307 m_FooterPart.SetIndexParamsVBR(&m_HeaderPart.m_Primer, EditRate, ECoffset);
309 m_FooterPart.SetIndexParamsCBR(&m_HeaderPart.m_Primer, BytesPerEditUnit, EditRate);
317 // standard method of writing a plaintext or encrypted frame
319 ASDCP::h__Writer::WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf, const byte_t* EssenceUL,
320 AESEncContext* Ctx, HMACContext* HMAC)
322 Result_t result = RESULT_OK;
323 IntegrityPack IntPack;
325 byte_t overhead[128];
326 Kumu::MemIOWriter Overhead(overhead, 128);
328 if ( FrameBuf.Size() == 0 )
330 DefaultLogSink().Error("Cannot write empty frame buffer\n");
331 return RESULT_EMPTY_FB;
334 if ( m_Info.EncryptedEssence )
337 return RESULT_CRYPT_CTX;
339 if ( m_Info.UsesHMAC && ! HMAC )
340 return RESULT_HMAC_CTX;
342 if ( FrameBuf.PlaintextOffset() > FrameBuf.Size() )
343 return RESULT_LARGE_PTO;
345 // encrypt the essence data (create encrypted source value)
346 result = EncryptFrameBuffer(FrameBuf, m_CtFrameBuf, Ctx);
349 if ( ASDCP_SUCCESS(result) && m_Info.UsesHMAC )
350 result = IntPack.CalcValues(m_CtFrameBuf, m_Info.AssetUUID, m_FramesWritten + 1, HMAC);
352 if ( ASDCP_SUCCESS(result) )
354 if ( m_Info.LabelSetType == LS_MXF_INTEROP )
355 Overhead.WriteRaw(Dict::ul(MDD_MXFInterop_CryptEssence), SMPTE_UL_LENGTH);
357 Overhead.WriteRaw(Dict::ul(MDD_CryptEssence), SMPTE_UL_LENGTH);
359 // construct encrypted triplet header
360 ui32_t ETLength = klv_cryptinfo_size + m_CtFrameBuf.Size();
362 if ( m_Info.UsesHMAC )
363 ETLength += klv_intpack_size;
365 ETLength += (MXF_BER_LENGTH * 3); // for empty intpack
367 Overhead.WriteBER(ETLength, MXF_BER_LENGTH); // write encrypted triplet length
368 Overhead.WriteBER(UUIDlen, MXF_BER_LENGTH); // write ContextID length
369 Overhead.WriteRaw(m_Info.ContextID, UUIDlen); // write ContextID
370 Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH); // write PlaintextOffset length
371 Overhead.WriteUi64BE(FrameBuf.PlaintextOffset()); // write PlaintextOffset
372 Overhead.WriteBER(SMPTE_UL_LENGTH, MXF_BER_LENGTH); // write essence UL length
373 Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH); // write the essence UL
374 Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH); // write SourceLength length
375 Overhead.WriteUi64BE(FrameBuf.Size()); // write SourceLength
376 Overhead.WriteBER(m_CtFrameBuf.Size(), MXF_BER_LENGTH); // write ESV length
378 result = m_File.Writev(Overhead.Data(), Overhead.Length());
381 if ( ASDCP_SUCCESS(result) )
383 m_StreamOffset += Overhead.Length();
384 // write encrypted source value
385 result = m_File.Writev((byte_t*)m_CtFrameBuf.RoData(), m_CtFrameBuf.Size());
388 if ( ASDCP_SUCCESS(result) )
390 m_StreamOffset += m_CtFrameBuf.Size();
392 byte_t hmoverhead[512];
393 Kumu::MemIOWriter HMACOverhead(hmoverhead, 512);
396 if ( m_Info.UsesHMAC )
398 HMACOverhead.WriteRaw(IntPack.Data, klv_intpack_size);
401 { // we still need the var-pack length values if the intpack is empty
402 for ( ui32_t i = 0; i < 3 ; i++ )
403 HMACOverhead.WriteBER(0, MXF_BER_LENGTH);
407 result = m_File.Writev(HMACOverhead.Data(), HMACOverhead.Length());
408 m_StreamOffset += HMACOverhead.Length();
413 Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH);
414 Overhead.WriteBER(FrameBuf.Size(), MXF_BER_LENGTH);
415 result = m_File.Writev(Overhead.Data(), Overhead.Length());
417 if ( ASDCP_SUCCESS(result) )
418 result = m_File.Writev((byte_t*)FrameBuf.RoData(), FrameBuf.Size());
420 if ( ASDCP_SUCCESS(result) )
421 m_StreamOffset += Overhead.Length() + FrameBuf.Size();
424 if ( ASDCP_SUCCESS(result) )
425 result = m_File.Writev();
431 // standard method of writing the header and footer of a completed MXF file
434 ASDCP::h__Writer::WriteMXFFooter()
436 // Set top-level file package correctly for OP-Atom
438 m_MPTCSequence->Duration = m_MPTimecode->Duration = m_MPClSequence->Duration = m_MPClip->Duration =
439 m_FPTCSequence->Duration = m_FPTimecode->Duration = m_FPClSequence->Duration = m_FPClip->Duration =
440 m_EssenceDescriptor->ContainerDuration = m_FramesWritten;
442 Kumu::fpos_t here = m_File.Tell();
443 m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(0, here)); // Third RIP Entry
444 m_HeaderPart.FooterPartition = here;
446 // re-label the partition
447 UL OPAtomUL(Dict::ul(MDD_OPAtom));
449 if ( m_Info.LabelSetType == LS_MXF_INTEROP )
450 OPAtomUL.Set(Dict::ul(MDD_MXFInterop_OPAtom));
452 m_HeaderPart.OperationalPattern = OPAtomUL;
453 m_HeaderPart.m_Preface->OperationalPattern = m_HeaderPart.OperationalPattern;
455 m_FooterPart.PreviousPartition = m_BodyPart.ThisPartition;
456 m_FooterPart.OperationalPattern = m_HeaderPart.OperationalPattern;
457 m_FooterPart.EssenceContainers = m_HeaderPart.EssenceContainers;
458 m_FooterPart.FooterPartition = here;
459 m_FooterPart.ThisPartition = here;
461 Result_t result = m_FooterPart.WriteToFile(m_File, m_FramesWritten);
463 if ( ASDCP_SUCCESS(result) )
464 result = m_HeaderPart.m_RIP.WriteToFile(m_File);
466 if ( ASDCP_SUCCESS(result) )
467 result = m_File.Seek(0);
469 if ( ASDCP_SUCCESS(result) )
470 result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);