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"
36 using namespace ASDCP;
37 using namespace ASDCP::MXF;
39 // a magic number identifying asdcplib
40 #ifndef ASDCP_BUILD_NUMBER
41 #define ASDCP_BUILD_NUMBER 0x4A48
47 ASDCP::h__Writer::h__Writer() :
48 m_HeaderSize(0), m_EssenceStart(0),
49 m_MaterialPackage(0), m_MPTCSequence(0), m_MPTimecode(0), m_MPClSequence(0), m_MPClip(0),
50 m_FilePackage(0), m_FPTCSequence(0), m_FPTimecode(0), m_FPClSequence(0), m_FPClip(0),
51 m_EssenceDescriptor(0), m_FramesWritten(0), m_StreamOffset(0)
55 ASDCP::h__Writer::~h__Writer()
61 // add DMS CryptographicFramework entry to source package
63 AddDMScrypt(Partition& HeaderPart, SourcePackage& Package, WriterInfo& Descr, const UL& WrappingUL)
66 StaticTrack* NewTrack = new StaticTrack;
67 HeaderPart.AddChildObject(NewTrack);
68 Package.Tracks.push_back(NewTrack->InstanceUID);
69 NewTrack->TrackName = "Descriptive Track";
70 NewTrack->TrackID = 3;
72 Sequence* Seq = new Sequence;
73 HeaderPart.AddChildObject(Seq);
74 NewTrack->Sequence = Seq->InstanceUID;
75 Seq->DataDefinition = UL(Dict::ul(MDD_DescriptiveMetaDataDef));
77 DMSegment* Segment = new DMSegment;
78 HeaderPart.AddChildObject(Segment);
79 Seq->StructuralComponents.push_back(Segment->InstanceUID);
80 Segment->EventComment = "AS-DCP KLV Encryption";
82 CryptographicFramework* CFW = new CryptographicFramework;
83 HeaderPart.AddChildObject(CFW);
84 Segment->DMFramework = CFW->InstanceUID;
86 CryptographicContext* Context = new CryptographicContext;
87 HeaderPart.AddChildObject(Context);
88 CFW->ContextSR = Context->InstanceUID;
90 Context->ContextID.Set(Descr.ContextID);
91 Context->SourceEssenceContainer = WrappingUL; // ??????
92 Context->CipherAlgorithm.Set(Dict::ul(MDD_CipherAlgorithm_AES));
93 Context->MICAlgorithm.Set( Descr.UsesHMAC ? Dict::ul(MDD_MICAlgorithm_HMAC_SHA1) : Dict::ul(MDD_MICAlgorithm_NONE) );
94 Context->CryptographicKeyID.Set(Descr.CryptographicKeyID);
99 ASDCP::h__Writer::WriteMXFHeader(const std::string& PackageLabel, const UL& WrappingUL,
100 const std::string& TrackName, const UL& DataDefinition,
101 const MXF::Rational& EditRate,
102 ui32_t TCFrameRate, ui32_t BytesPerEditUnit)
104 ASDCP_TEST_NULL(m_EssenceDescriptor);
106 m_HeaderPart.m_Primer.ClearTagList();
107 m_HeaderPart.m_Preface = new Preface;
108 m_HeaderPart.AddChildObject(m_HeaderPart.m_Preface);
110 // Set the Operational Pattern label -- we're just starting and have no RIP or index,
111 // so we tell the world by using OP1a
112 m_HeaderPart.m_Preface->OperationalPattern = UL(Dict::ul(MDD_OP1a));
113 m_HeaderPart.OperationalPattern = m_HeaderPart.m_Preface->OperationalPattern;
114 m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(1, 0)); // First RIP Entry
119 Identification* Ident = new Identification;
120 m_HeaderPart.AddChildObject(Ident);
121 m_HeaderPart.m_Preface->Identifications.push_back(Ident->InstanceUID);
123 Ident->ThisGenerationUID.GenRandomValue();
124 Ident->CompanyName = m_Info.CompanyName.c_str();
125 Ident->ProductName = m_Info.ProductName.c_str();
126 Ident->VersionString = m_Info.ProductVersion.c_str();
127 Ident->ProductUID.Set(m_Info.ProductUUID);
128 // Ident->Platform = "Foonix"; // ASDCP_PLATFORM;
129 Ident->ToolkitVersion.Major = VERSION_MAJOR;
130 Ident->ToolkitVersion.Minor = VERSION_APIMINOR;
131 Ident->ToolkitVersion.Patch = VERSION_IMPMINOR;
132 Ident->ToolkitVersion.Build = ASDCP_BUILD_NUMBER;
133 Ident->ToolkitVersion.Release = VersionType::RL_DEVELOPMENT;
136 ContentStorage* Storage = new ContentStorage;
137 m_HeaderPart.AddChildObject(Storage);
138 m_HeaderPart.m_Preface->ContentStorage = Storage->InstanceUID;
140 EssenceContainerData* ECD = new EssenceContainerData;
141 m_HeaderPart.AddChildObject(ECD);
142 Storage->EssenceContainerData.push_back(ECD->InstanceUID);
150 PackageUMID.MakeUMID(0x0f); // unidentified essence
152 m_MaterialPackage = new MaterialPackage;
153 m_MaterialPackage->Name = "AS-DCP Material Package";
154 m_MaterialPackage->PackageUID = PackageUMID;
156 m_HeaderPart.AddChildObject(m_MaterialPackage);
157 Storage->Packages.push_back(m_MaterialPackage->InstanceUID);
160 Track* NewTrack = new Track;
161 m_HeaderPart.AddChildObject(NewTrack);
162 NewTrack->EditRate = EditRate;
163 m_MaterialPackage->Tracks.push_back(NewTrack->InstanceUID);
164 NewTrack->TrackID = 1;
165 NewTrack->TrackName = "Timecode Track";
167 m_MPTCSequence = new Sequence;
168 m_HeaderPart.AddChildObject(m_MPTCSequence);
169 NewTrack->Sequence = m_MPTCSequence->InstanceUID;
170 m_MPTCSequence->DataDefinition = UL(Dict::ul(MDD_TimecodeDataDef));
172 m_MPTimecode = new TimecodeComponent;
173 m_HeaderPart.AddChildObject(m_MPTimecode);
174 m_MPTCSequence->StructuralComponents.push_back(m_MPTimecode->InstanceUID);
175 m_MPTimecode->RoundedTimecodeBase = TCFrameRate;
176 m_MPTimecode->StartTimecode = ui64_C(0);
177 m_MPTimecode->DataDefinition = UL(Dict::ul(MDD_TimecodeDataDef));
180 NewTrack = new Track;
181 m_HeaderPart.AddChildObject(NewTrack);
182 NewTrack->EditRate = EditRate;
183 m_MaterialPackage->Tracks.push_back(NewTrack->InstanceUID);
184 NewTrack->TrackID = 2;
185 NewTrack->TrackName = TrackName.c_str();
187 m_MPClSequence = new Sequence;
188 m_HeaderPart.AddChildObject(m_MPClSequence);
189 NewTrack->Sequence = m_MPClSequence->InstanceUID;
190 m_MPClSequence->DataDefinition = DataDefinition;
192 m_MPClip = new SourceClip;
193 m_HeaderPart.AddChildObject(m_MPClip);
194 m_MPClSequence->StructuralComponents.push_back(m_MPClip->InstanceUID);
195 m_MPClip->DataDefinition = DataDefinition;
198 // File (Source) Package
200 UUID assetUUID(m_Info.AssetUUID);
201 PackageUMID.MakeUMID(0x0f, assetUUID);
203 m_FilePackage = new SourcePackage;
204 m_FilePackage->Name = PackageLabel.c_str();
205 m_FilePackage->PackageUID = PackageUMID;
206 ECD->LinkedPackageUID = PackageUMID;
207 m_MPClip->SourcePackageID = PackageUMID;
208 m_MPClip->SourceTrackID = 1;
210 m_HeaderPart.AddChildObject(m_FilePackage);
211 Storage->Packages.push_back(m_FilePackage->InstanceUID);
214 NewTrack = new Track;
215 m_HeaderPart.AddChildObject(NewTrack);
216 NewTrack->EditRate = EditRate;
217 m_FilePackage->Tracks.push_back(NewTrack->InstanceUID);
218 NewTrack->TrackID = 1;
219 NewTrack->TrackName = "Timecode Track";
221 m_FPTCSequence = new Sequence;
222 m_HeaderPart.AddChildObject(m_FPTCSequence);
223 NewTrack->Sequence = m_FPTCSequence->InstanceUID;
224 m_FPTCSequence->DataDefinition = UL(Dict::ul(MDD_TimecodeDataDef));
226 m_FPTimecode = new TimecodeComponent;
227 m_HeaderPart.AddChildObject(m_FPTimecode);
228 m_FPTCSequence->StructuralComponents.push_back(m_FPTimecode->InstanceUID);
229 m_FPTimecode->RoundedTimecodeBase = TCFrameRate;
230 m_FPTimecode->StartTimecode = ui64_C(86400); // 01:00:00:00
231 m_FPTimecode->DataDefinition = UL(Dict::ul(MDD_TimecodeDataDef));
234 NewTrack = new Track;
235 m_HeaderPart.AddChildObject(NewTrack);
236 NewTrack->EditRate = EditRate;
237 m_FilePackage->Tracks.push_back(NewTrack->InstanceUID);
238 NewTrack->TrackID = 2;
239 NewTrack->TrackName = TrackName.c_str();
241 m_FPClSequence = new Sequence;
242 m_HeaderPart.AddChildObject(m_FPClSequence);
243 NewTrack->Sequence = m_FPClSequence->InstanceUID;
244 m_FPClSequence->DataDefinition = DataDefinition;
246 m_FPClip = new SourceClip;
247 m_HeaderPart.AddChildObject(m_FPClip);
248 m_FPClSequence->StructuralComponents.push_back(m_FPClip->InstanceUID);
249 m_FPClip->DataDefinition = DataDefinition;
252 // Essence Descriptor
254 m_EssenceDescriptor->EssenceContainer = WrappingUL;
255 m_EssenceDescriptor->LinkedTrackID = NewTrack->TrackID;
256 m_HeaderPart.m_Preface->PrimaryPackage = m_FilePackage->InstanceUID;
259 // Encryption Descriptor
261 if ( m_Info.EncryptedEssence )
263 UL CryptEssenceUL(Dict::ul(MDD_EncryptedContainerLabel));
264 m_HeaderPart.EssenceContainers.push_back(CryptEssenceUL);
265 m_HeaderPart.m_Preface->EssenceContainers.push_back(CryptEssenceUL);
266 m_HeaderPart.m_Preface->DMSchemes.push_back(UL(Dict::ul(MDD_CryptographicFrameworkLabel)));
267 AddDMScrypt(m_HeaderPart, *m_FilePackage, m_Info, WrappingUL);
271 m_HeaderPart.EssenceContainers.push_back(UL(Dict::ul(MDD_GCMulti)));
272 m_HeaderPart.EssenceContainers.push_back(WrappingUL);
273 m_HeaderPart.m_Preface->EssenceContainers = m_HeaderPart.EssenceContainers;
276 m_HeaderPart.AddChildObject(m_EssenceDescriptor);
277 m_FilePackage->Descriptor = m_EssenceDescriptor->InstanceUID;
279 // Write the header partition
280 Result_t result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
290 fpos_t ECoffset = m_File.Tell();
292 if ( BytesPerEditUnit == 0 )
293 m_FooterPart.SetIndexParamsVBR(&m_HeaderPart.m_Primer, EditRate, ECoffset);
295 m_FooterPart.SetIndexParamsCBR(&m_HeaderPart.m_Primer, BytesPerEditUnit, EditRate);
302 // standard method of writing a plaintext or encrypted frame
304 ASDCP::h__Writer::WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf, const byte_t* EssenceUL,
305 AESEncContext* Ctx, HMACContext* HMAC)
308 IntegrityPack IntPack;
310 byte_t overhead[128];
311 MemIOWriter Overhead(overhead, 128);
313 if ( FrameBuf.Size() == 0 )
315 DefaultLogSink().Error("Cannot write empty frame buffer\n");
316 return RESULT_EMPTY_FB;
319 if ( m_Info.EncryptedEssence )
322 return RESULT_CRYPT_CTX;
324 if ( m_Info.UsesHMAC && ! HMAC )
325 return RESULT_HMAC_CTX;
327 if ( FrameBuf.PlaintextOffset() > FrameBuf.Size() )
328 return RESULT_LARGE_PTO;
330 // encrypt the essence data (create encrypted source value)
331 result = EncryptFrameBuffer(FrameBuf, m_CtFrameBuf, Ctx);
334 if ( ASDCP_SUCCESS(result) && m_Info.UsesHMAC )
335 result = IntPack.CalcValues(m_CtFrameBuf, m_Info.AssetUUID, m_FramesWritten + 1, HMAC);
337 if ( ASDCP_SUCCESS(result) )
339 Overhead.WriteRaw(Dict::ul(MDD_CryptEssence), SMPTE_UL_LENGTH);
341 // construct encrypted triplet header
342 ui32_t ETLength = klv_cryptinfo_size + m_CtFrameBuf.Size();
344 if ( m_Info.UsesHMAC )
345 ETLength += klv_intpack_size;
347 ETLength += (MXF_BER_LENGTH * 3); // for empty intpack
349 Overhead.WriteBER(ETLength, MXF_BER_LENGTH); // write encrypted triplet length
350 Overhead.WriteBER(UUIDlen, MXF_BER_LENGTH); // write ContextID length
351 Overhead.WriteRaw(m_Info.ContextID, UUIDlen); // write ContextID
352 Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH); // write PlaintextOffset length
353 Overhead.WriteUi64BE(FrameBuf.PlaintextOffset()); // write PlaintextOffset
354 Overhead.WriteBER(SMPTE_UL_LENGTH, MXF_BER_LENGTH); // write essence UL length
355 Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH); // write the essence UL
356 Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH); // write SourceLength length
357 Overhead.WriteUi64BE(FrameBuf.Size()); // write SourceLength
358 Overhead.WriteBER(m_CtFrameBuf.Size(), MXF_BER_LENGTH); // write ESV length
360 result = m_File.Writev(Overhead.Data(), Overhead.Size());
363 if ( ASDCP_SUCCESS(result) )
365 m_StreamOffset += Overhead.Size();
366 // write encrypted source value
367 result = m_File.Writev((byte_t*)m_CtFrameBuf.RoData(), m_CtFrameBuf.Size());
370 if ( ASDCP_SUCCESS(result) )
372 m_StreamOffset += m_CtFrameBuf.Size();
374 byte_t hmoverhead[512];
375 MemIOWriter HMACOverhead(hmoverhead, 512);
378 if ( m_Info.UsesHMAC )
380 HMACOverhead.WriteRaw(IntPack.Data, klv_intpack_size);
383 { // we still need the var-pack length values if the intpack is empty
384 for ( ui32_t i = 0; i < 3 ; i++ )
385 HMACOverhead.WriteBER(0, MXF_BER_LENGTH);
389 result = m_File.Writev(HMACOverhead.Data(), HMACOverhead.Size());
390 m_StreamOffset += HMACOverhead.Size();
395 Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH);
396 Overhead.WriteBER(FrameBuf.Size(), MXF_BER_LENGTH);
397 result = m_File.Writev(Overhead.Data(), Overhead.Size());
399 if ( ASDCP_SUCCESS(result) )
400 result = m_File.Writev((byte_t*)FrameBuf.RoData(), FrameBuf.Size());
402 if ( ASDCP_SUCCESS(result) )
403 m_StreamOffset += Overhead.Size() + FrameBuf.Size();
406 if ( ASDCP_SUCCESS(result) )
407 result = m_File.Writev();
413 // standard method of writing the header and footer of a completed MXF file
416 ASDCP::h__Writer::WriteMXFFooter()
418 // Set top-level file package correctly for OP-Atom
419 m_MPTCSequence->Duration = m_FramesWritten;
420 m_MPTimecode->Duration = m_FramesWritten;
421 m_MPClSequence->Duration = m_FramesWritten;
422 m_MPClip->Duration = m_FramesWritten;
423 m_FPTCSequence->Duration = m_FramesWritten;
424 m_FPTimecode->Duration = m_FramesWritten;
425 m_FPClSequence->Duration = m_FramesWritten;
426 m_FPClip->Duration = m_FramesWritten;
427 m_EssenceDescriptor->ContainerDuration = m_FramesWritten;
429 fpos_t here = m_File.Tell();
430 m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(1, here)); // Third RIP Entry
431 m_HeaderPart.FooterPartition = here;
432 m_HeaderPart.BodySID = 1;
433 // m_HeaderPart.IndexSID = m_FooterPart.IndexSID;
434 m_HeaderPart.OperationalPattern = UL(Dict::ul(MDD_OPAtom));
435 m_HeaderPart.m_Preface->OperationalPattern = m_HeaderPart.OperationalPattern;
437 m_FooterPart.OperationalPattern = m_HeaderPart.OperationalPattern;
438 m_FooterPart.EssenceContainers = m_HeaderPart.EssenceContainers;
439 m_FooterPart.FooterPartition = here;
440 m_FooterPart.ThisPartition = here;
442 Result_t result = m_FooterPart.WriteToFile(m_File, m_FramesWritten);
444 if ( ASDCP_SUCCESS(result) )
445 result = m_HeaderPart.m_RIP.WriteToFile(m_File);
447 if ( ASDCP_SUCCESS(result) )
448 result = m_File.Seek(0);
450 if ( ASDCP_SUCCESS(result) )
451 result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);