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 if ( m_Info.LabelSetType == LS_MXF_INTEROP )
340 Overhead.WriteRaw(Dict::ul(MDD_MXFInterop_CryptEssence), SMPTE_UL_LENGTH);
342 Overhead.WriteRaw(Dict::ul(MDD_CryptEssence), SMPTE_UL_LENGTH);
344 // construct encrypted triplet header
345 ui32_t ETLength = klv_cryptinfo_size + m_CtFrameBuf.Size();
347 if ( m_Info.UsesHMAC )
348 ETLength += klv_intpack_size;
350 ETLength += (MXF_BER_LENGTH * 3); // for empty intpack
352 Overhead.WriteBER(ETLength, MXF_BER_LENGTH); // write encrypted triplet length
353 Overhead.WriteBER(UUIDlen, MXF_BER_LENGTH); // write ContextID length
354 Overhead.WriteRaw(m_Info.ContextID, UUIDlen); // write ContextID
355 Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH); // write PlaintextOffset length
356 Overhead.WriteUi64BE(FrameBuf.PlaintextOffset()); // write PlaintextOffset
357 Overhead.WriteBER(SMPTE_UL_LENGTH, MXF_BER_LENGTH); // write essence UL length
358 Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH); // write the essence UL
359 Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH); // write SourceLength length
360 Overhead.WriteUi64BE(FrameBuf.Size()); // write SourceLength
361 Overhead.WriteBER(m_CtFrameBuf.Size(), MXF_BER_LENGTH); // write ESV length
363 result = m_File.Writev(Overhead.Data(), Overhead.Size());
366 if ( ASDCP_SUCCESS(result) )
368 m_StreamOffset += Overhead.Size();
369 // write encrypted source value
370 result = m_File.Writev((byte_t*)m_CtFrameBuf.RoData(), m_CtFrameBuf.Size());
373 if ( ASDCP_SUCCESS(result) )
375 m_StreamOffset += m_CtFrameBuf.Size();
377 byte_t hmoverhead[512];
378 MemIOWriter HMACOverhead(hmoverhead, 512);
381 if ( m_Info.UsesHMAC )
383 HMACOverhead.WriteRaw(IntPack.Data, klv_intpack_size);
386 { // we still need the var-pack length values if the intpack is empty
387 for ( ui32_t i = 0; i < 3 ; i++ )
388 HMACOverhead.WriteBER(0, MXF_BER_LENGTH);
392 result = m_File.Writev(HMACOverhead.Data(), HMACOverhead.Size());
393 m_StreamOffset += HMACOverhead.Size();
398 Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH);
399 Overhead.WriteBER(FrameBuf.Size(), MXF_BER_LENGTH);
400 result = m_File.Writev(Overhead.Data(), Overhead.Size());
402 if ( ASDCP_SUCCESS(result) )
403 result = m_File.Writev((byte_t*)FrameBuf.RoData(), FrameBuf.Size());
405 if ( ASDCP_SUCCESS(result) )
406 m_StreamOffset += Overhead.Size() + FrameBuf.Size();
409 if ( ASDCP_SUCCESS(result) )
410 result = m_File.Writev();
416 // standard method of writing the header and footer of a completed MXF file
419 ASDCP::h__Writer::WriteMXFFooter()
421 // Set top-level file package correctly for OP-Atom
422 m_MPTCSequence->Duration = m_FramesWritten;
423 m_MPTimecode->Duration = m_FramesWritten;
424 m_MPClSequence->Duration = m_FramesWritten;
425 m_MPClip->Duration = m_FramesWritten;
426 m_FPTCSequence->Duration = m_FramesWritten;
427 m_FPTimecode->Duration = m_FramesWritten;
428 m_FPClSequence->Duration = m_FramesWritten;
429 m_FPClip->Duration = m_FramesWritten;
430 m_EssenceDescriptor->ContainerDuration = m_FramesWritten;
432 fpos_t here = m_File.Tell();
433 m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(1, here)); // Third RIP Entry
434 m_HeaderPart.FooterPartition = here;
435 m_HeaderPart.BodySID = 1;
437 // re-label the partition
438 UL OPAtomUL(Dict::ul(MDD_OPAtom));
440 if ( m_Info.LabelSetType == LS_MXF_INTEROP )
441 OPAtomUL.Set(Dict::ul(MDD_MXFInterop_OPAtom));
443 m_HeaderPart.OperationalPattern = OPAtomUL;
444 m_HeaderPart.m_Preface->OperationalPattern = m_HeaderPart.OperationalPattern;
446 m_FooterPart.OperationalPattern = m_HeaderPart.OperationalPattern;
447 m_FooterPart.EssenceContainers = m_HeaderPart.EssenceContainers;
448 m_FooterPart.FooterPartition = here;
449 m_FooterPart.ThisPartition = here;
451 Result_t result = m_FooterPart.WriteToFile(m_File, m_FramesWritten);
453 if ( ASDCP_SUCCESS(result) )
454 result = m_HeaderPart.m_RIP.WriteToFile(m_File);
456 if ( ASDCP_SUCCESS(result) )
457 result = m_File.Seek(0);
459 if ( ASDCP_SUCCESS(result) )
460 result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);