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"
37 using namespace ASDCP;
38 using namespace ASDCP::MXF;
40 // a magic number identifying asdcplib
41 #ifndef WM_BUILD_NUMBER
42 #define WM_BUILD_NUMBER 0x4A48
48 ASDCP::h__Writer::h__Writer() : m_EssenceDescriptor(0), m_FramesWritten(0), m_StreamOffset(0)
52 ASDCP::h__Writer::~h__Writer()
57 const byte_t PictDD_Data[SMPTE_UL_LENGTH] = {
58 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x01,
59 0x01, 0x03, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00 };
61 const byte_t SoundDD_Data[SMPTE_UL_LENGTH] = {
62 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x01,
63 0x01, 0x03, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00 };
65 const byte_t TCDD_Data[SMPTE_UL_LENGTH] = {
66 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x01,
67 0x01, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00 };
69 const byte_t DMDD_Data[SMPTE_UL_LENGTH] = {
70 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x01,
71 0x01, 0x03, 0x02, 0x01, 0x10, 0x00, 0x00, 0x00 };
75 // add DMS CryptographicFramework entry to source package
77 AddDMScrypt(Partition& HeaderPart, SourcePackage& Package, WriterInfo& Descr, const UL& WrappingUL)
80 StaticTrack* NewTrack = new StaticTrack;
81 HeaderPart.AddChildObject(NewTrack);
82 Package.Tracks.push_back(NewTrack->InstanceUID);
83 NewTrack->TrackName = "Descriptive Track";
84 NewTrack->TrackID = 3;
86 Sequence* Seq = new Sequence;
87 HeaderPart.AddChildObject(Seq);
88 NewTrack->Sequence = Seq->InstanceUID;
89 Seq->DataDefinition = UL(DMDD_Data);
91 DMSegment* Segment = new DMSegment;
92 HeaderPart.AddChildObject(Segment);
93 Seq->StructuralComponents.push_back(Segment->InstanceUID);
94 Segment->EventComment = "AS-DCP KLV Encryption";
96 CryptographicFramework* CFW = new CryptographicFramework;
97 HeaderPart.AddChildObject(CFW);
98 Segment->DMFramework = CFW->InstanceUID;
100 CryptographicContext* Context = new CryptographicContext;
101 HeaderPart.AddChildObject(Context);
102 CFW->ContextSR = Context->InstanceUID;
104 Context->ContextID.Set(Descr.ContextID);
105 Context->SourceEssenceContainer = WrappingUL; // ??????
106 Context->CipherAlgorithm.Set(CipherAlgorithm_AES);
107 Context->MICAlgorithm.Set( Descr.UsesHMAC ? MICAlgorithm_HMAC_SHA1 : MICAlgorithm_NONE );
108 Context->CryptographicKeyID.Set(Descr.CryptographicKeyID);
113 ASDCP::h__Writer::WriteMXFHeader(const std::string& PackageLabel, const UL& WrappingUL,
114 const MXF::Rational& EditRate,
115 ui32_t TCFrameRate, ui32_t BytesPerEditUnit)
117 ASDCP_TEST_NULL(m_EssenceDescriptor);
119 m_HeaderPart.m_Primer.ClearTagList();
120 m_HeaderPart.m_Preface = new Preface;
121 m_HeaderPart.AddChildObject(m_HeaderPart.m_Preface);
123 // Set the Operational Pattern label -- we're just starting and have no RIP or index,
124 // so we tell the world by using OP1a
125 m_HeaderPart.m_Preface->OperationalPattern = UL(OP1aUL);
126 m_HeaderPart.OperationalPattern = m_HeaderPart.m_Preface->OperationalPattern;
127 m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(1, 0)); // First RIP Entry
132 Identification* Ident = new Identification;
133 m_HeaderPart.AddChildObject(Ident);
134 m_HeaderPart.m_Preface->Identifications.push_back(Ident->InstanceUID);
136 Ident->ThisGenerationUID.GenRandomValue();
137 Ident->CompanyName = m_Info.CompanyName.c_str();
138 Ident->ProductName = m_Info.ProductName.c_str();
139 Ident->VersionString = m_Info.ProductVersion.c_str();
140 Ident->ProductUID.Set(m_Info.ProductUUID);
141 // Ident->Platform = WM_PLATFORM;
142 Ident->ToolkitVersion.Release = VersionType::RL_DEVELOPMENT;
143 Ident->ToolkitVersion.Major = VERSION_MAJOR;
144 Ident->ToolkitVersion.Minor = VERSION_APIMINOR;
145 Ident->ToolkitVersion.Patch = VERSION_IMPMINOR;
146 Ident->ToolkitVersion.Build = WM_BUILD_NUMBER;
149 ContentStorage* Storage = new ContentStorage;
150 m_HeaderPart.AddChildObject(Storage);
151 m_HeaderPart.m_Preface->ContentStorage = Storage->InstanceUID;
153 EssenceContainerData* ECD = new EssenceContainerData;
154 m_HeaderPart.AddChildObject(ECD);
155 Storage->EssenceContainerData.push_back(ECD->InstanceUID);
163 PackageUMID.MakeUMID(0x0d); // Using mixed type. What is the actual type?
165 m_MaterialPackage = new MaterialPackage;
166 m_MaterialPackage->Name = "AS-DCP Material Package";
167 m_MaterialPackage->PackageUID = PackageUMID;
169 m_HeaderPart.AddChildObject(m_MaterialPackage);
170 Storage->Packages.push_back(m_MaterialPackage->InstanceUID);
173 Track* NewTrack = new Track;
174 m_HeaderPart.AddChildObject(NewTrack);
175 NewTrack->EditRate = EditRate;
176 m_MaterialPackage->Tracks.push_back(NewTrack->InstanceUID);
177 NewTrack->TrackID = 1;
178 NewTrack->TrackName = "Timecode Track";
180 Sequence* Seq = new Sequence;
181 m_HeaderPart.AddChildObject(Seq);
182 NewTrack->Sequence = Seq->InstanceUID;
183 Seq->DataDefinition = UL(TCDD_Data);
185 m_MPTimecode = new TimecodeComponent;
186 m_HeaderPart.AddChildObject(m_MPTimecode);
187 Seq->StructuralComponents.push_back(m_MPTimecode->InstanceUID);
188 m_MPTimecode->RoundedTimecodeBase = TCFrameRate;
189 m_MPTimecode->StartTimecode = ui64_C(0);
190 m_MPTimecode->DataDefinition = UL(TCDD_Data);
193 NewTrack = new Track;
194 m_HeaderPart.AddChildObject(NewTrack);
195 NewTrack->EditRate = EditRate;
196 m_MaterialPackage->Tracks.push_back(NewTrack->InstanceUID);
197 NewTrack->TrackID = 2;
198 NewTrack->TrackName = "Essence Track";
201 m_HeaderPart.AddChildObject(Seq);
202 NewTrack->Sequence = Seq->InstanceUID;
204 m_MPClip = new SourceClip;
205 m_HeaderPart.AddChildObject(m_MPClip);
206 Seq->StructuralComponents.push_back(m_MPClip->InstanceUID);
209 // File (Source) Package
211 UUID assetUUID(m_Info.AssetUUID);
212 PackageUMID.MakeUMID(0x0f, assetUUID);
214 m_FilePackage = new SourcePackage;
215 m_FilePackage->Name = PackageLabel.c_str();
216 m_FilePackage->PackageUID = PackageUMID;
217 ECD->LinkedPackageUID = PackageUMID;
218 m_MPClip->SourcePackageID = PackageUMID;
219 m_MPClip->SourceTrackID = 1;
221 m_HeaderPart.AddChildObject(m_FilePackage);
222 Storage->Packages.push_back(m_FilePackage->InstanceUID);
225 NewTrack = new Track;
226 m_HeaderPart.AddChildObject(NewTrack);
227 NewTrack->EditRate = EditRate;
228 m_FilePackage->Tracks.push_back(NewTrack->InstanceUID);
229 NewTrack->TrackID = 1;
230 NewTrack->TrackName = "Timecode Track";
233 m_HeaderPart.AddChildObject(Seq);
234 NewTrack->Sequence = Seq->InstanceUID;
235 Seq->DataDefinition = UL(TCDD_Data);
237 m_FPTimecode = new TimecodeComponent;
238 m_HeaderPart.AddChildObject(m_FPTimecode);
239 Seq->StructuralComponents.push_back(m_FPTimecode->InstanceUID);
240 m_FPTimecode->RoundedTimecodeBase = TCFrameRate;
241 m_FPTimecode->StartTimecode = ui64_C(86400); // 01:00:00:00
242 m_FPTimecode->DataDefinition = UL(TCDD_Data);
245 NewTrack = new Track;
246 m_HeaderPart.AddChildObject(NewTrack);
247 NewTrack->EditRate = EditRate;
248 m_FilePackage->Tracks.push_back(NewTrack->InstanceUID);
249 NewTrack->TrackID = 2;
250 NewTrack->TrackName = "Essence Track";
253 m_HeaderPart.AddChildObject(Seq);
254 NewTrack->Sequence = Seq->InstanceUID;
256 m_FPClip = new SourceClip;
257 m_HeaderPart.AddChildObject(m_FPClip);
258 Seq->StructuralComponents.push_back(m_FPClip->InstanceUID);
261 // Essence Descriptor
263 m_EssenceDescriptor->EssenceContainer = WrappingUL;
264 m_EssenceDescriptor->LinkedTrackID = NewTrack->TrackID;
266 // link the Material Package to the File Package ?????????????????????????????????
267 Seq->StructuralComponents.push_back(NewTrack->InstanceUID);
268 m_HeaderPart.m_Preface->PrimaryPackage = m_FilePackage->InstanceUID;
271 // Encryption Descriptor
273 if ( m_Info.EncryptedEssence )
275 UL CryptEssenceUL(WrappingUL_Data_Crypt);
276 m_HeaderPart.EssenceContainers.push_back(CryptEssenceUL);
277 m_HeaderPart.m_Preface->EssenceContainers.push_back(CryptEssenceUL);
278 m_HeaderPart.m_Preface->DMSchemes.push_back(UL(CryptoFrameworkUL_Data));
279 AddDMScrypt(m_HeaderPart, *m_FilePackage, m_Info, WrappingUL);
283 UL GCUL(GCMulti_Data);
284 m_HeaderPart.EssenceContainers.push_back(GCUL);
285 m_HeaderPart.EssenceContainers.push_back(WrappingUL);
286 m_HeaderPart.m_Preface->EssenceContainers = m_HeaderPart.EssenceContainers;
289 m_HeaderPart.AddChildObject(m_EssenceDescriptor);
290 m_FilePackage->Descriptor = m_EssenceDescriptor->InstanceUID;
292 // Write the header partition
293 Result_t result = m_HeaderPart.WriteToFile(m_File, HeaderPadding);
303 fpos_t ECoffset = m_File.Tell();
305 if ( BytesPerEditUnit == 0 )
306 m_FooterPart.SetIndexParamsVBR(&m_HeaderPart.m_Primer, EditRate, ECoffset);
308 m_FooterPart.SetIndexParamsCBR(&m_HeaderPart.m_Primer, BytesPerEditUnit, EditRate);
315 // standard method of writing a plaintext or encrypted frame
317 ASDCP::h__Writer::WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf, const byte_t* EssenceUL,
318 AESEncContext* Ctx, HMACContext* HMAC)
321 IntegrityPack IntPack;
323 byte_t overhead[128];
324 MemIOWriter Overhead(overhead, 128);
326 if ( FrameBuf.Size() == 0 )
328 DefaultLogSink().Error("Cannot write empty frame buffer\n");
329 return RESULT_EMPTY_FB;
332 if ( m_Info.EncryptedEssence )
335 return RESULT_CRYPT_CTX;
337 if ( m_Info.UsesHMAC && ! HMAC )
338 return RESULT_HMAC_CTX;
340 if ( FrameBuf.PlaintextOffset() > FrameBuf.Size() )
341 return RESULT_LARGE_PTO;
343 // encrypt the essence data (create encrypted source value)
344 result = EncryptFrameBuffer(FrameBuf, m_CtFrameBuf, Ctx);
347 if ( ASDCP_SUCCESS(result) && m_Info.UsesHMAC )
348 result = IntPack.CalcValues(m_CtFrameBuf, m_Info.AssetUUID, m_FramesWritten + 1, HMAC);
350 if ( ASDCP_SUCCESS(result) )
352 Overhead.WriteRaw((byte_t*)CryptEssenceUL_Data, klv_key_size);
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 += (klv_length_size * 3); // for empty intpack
362 Overhead.WriteBER(ETLength, klv_length_size); // write encrypted triplet length
363 Overhead.WriteBER(UUIDlen, klv_length_size); // write ContextID length
364 Overhead.WriteRaw(m_Info.ContextID, UUIDlen); // write ContextID
365 Overhead.WriteBER(sizeof(ui64_t), klv_length_size); // write PlaintextOffset length
366 Overhead.WriteUi64BE(FrameBuf.PlaintextOffset()); // write PlaintextOffset
367 Overhead.WriteBER(klv_key_size, klv_length_size); // write essence UL length
368 Overhead.WriteRaw((byte_t*)EssenceUL, klv_key_size); // write the essence UL
369 Overhead.WriteBER(sizeof(ui64_t), klv_length_size); // write SourceLength length
370 Overhead.WriteUi64BE(FrameBuf.Size()); // write SourceLength
371 Overhead.WriteBER(m_CtFrameBuf.Size(), klv_length_size); // write ESV length
373 result = m_File.Writev(Overhead.Data(), Overhead.Size());
376 if ( ASDCP_SUCCESS(result) )
378 m_StreamOffset += Overhead.Size();
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 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, klv_length_size);
402 result = m_File.Writev(HMACOverhead.Data(), HMACOverhead.Size());
403 m_StreamOffset += HMACOverhead.Size();
408 Overhead.WriteRaw((byte_t*)EssenceUL, klv_key_size);
409 Overhead.WriteBER(FrameBuf.Size(), klv_length_size);
410 result = m_File.Writev(Overhead.Data(), Overhead.Size());
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.Size() + 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
432 m_MPTimecode->Duration = m_FramesWritten;
433 m_MPClip->Duration = m_FramesWritten;
434 m_FPTimecode->Duration = m_FramesWritten;
435 m_FPClip->Duration = m_FramesWritten;
436 m_EssenceDescriptor->ContainerDuration = m_FramesWritten;
438 fpos_t here = m_File.Tell();
439 m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(1, here)); // Third RIP Entry
440 m_HeaderPart.FooterPartition = here;
441 m_HeaderPart.BodySID = 1;
442 m_HeaderPart.IndexSID = m_FooterPart.IndexSID;
443 m_HeaderPart.OperationalPattern = UL(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, HeaderPadding);