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()
59 // add DMS CryptographicFramework entry to source package
61 AddDMScrypt(Partition& HeaderPart, SourcePackage& Package, WriterInfo& Descr, const UL& WrappingUL)
64 StaticTrack* NewTrack = new StaticTrack;
65 HeaderPart.AddChildObject(NewTrack);
66 Package.Tracks.push_back(NewTrack->InstanceUID);
67 NewTrack->TrackName = "Descriptive Track";
68 NewTrack->TrackID = 3;
70 Sequence* Seq = new Sequence;
71 HeaderPart.AddChildObject(Seq);
72 NewTrack->Sequence = Seq->InstanceUID;
73 Seq->DataDefinition = UL(Dict::ul(MDD_DescriptiveMetaDataDef));
75 DMSegment* Segment = new DMSegment;
76 HeaderPart.AddChildObject(Segment);
77 Seq->StructuralComponents.push_back(Segment->InstanceUID);
78 Segment->EventComment = "AS-DCP KLV Encryption";
80 CryptographicFramework* CFW = new CryptographicFramework;
81 HeaderPart.AddChildObject(CFW);
82 Segment->DMFramework = CFW->InstanceUID;
84 CryptographicContext* Context = new CryptographicContext;
85 HeaderPart.AddChildObject(Context);
86 CFW->ContextSR = Context->InstanceUID;
88 Context->ContextID.Set(Descr.ContextID);
89 Context->SourceEssenceContainer = WrappingUL; // ??????
90 Context->CipherAlgorithm.Set(Dict::ul(MDD_CipherAlgorithm_AES));
91 Context->MICAlgorithm.Set( Descr.UsesHMAC ? Dict::ul(MDD_MICAlgorithm_HMAC_SHA1) : Dict::ul(MDD_MICAlgorithm_NONE) );
92 Context->CryptographicKeyID.Set(Descr.CryptographicKeyID);
97 ASDCP::h__Writer::WriteMXFHeader(const std::string& PackageLabel, const UL& WrappingUL,
98 const std::string& TrackName, const UL& DataDefinition,
99 const MXF::Rational& EditRate,
100 ui32_t TCFrameRate, ui32_t BytesPerEditUnit)
102 ASDCP_TEST_NULL(m_EssenceDescriptor);
104 m_HeaderPart.m_Primer.ClearTagList();
105 m_HeaderPart.m_Preface = new Preface;
106 m_HeaderPart.AddChildObject(m_HeaderPart.m_Preface);
108 // Set the Operational Pattern label -- we're just starting and have no RIP or index,
109 // so we tell the world by using OP1a
110 m_HeaderPart.m_Preface->OperationalPattern = UL(Dict::ul(MDD_OP1a));
111 m_HeaderPart.OperationalPattern = m_HeaderPart.m_Preface->OperationalPattern;
112 m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(0, 0)); // First RIP Entry
117 Identification* Ident = new Identification;
118 m_HeaderPart.AddChildObject(Ident);
119 m_HeaderPart.m_Preface->Identifications.push_back(Ident->InstanceUID);
121 Kumu::GenRandomValue(Ident->ThisGenerationUID);
122 Ident->CompanyName = m_Info.CompanyName.c_str();
123 Ident->ProductName = m_Info.ProductName.c_str();
124 Ident->VersionString = m_Info.ProductVersion.c_str();
125 Ident->ProductUID.Set(m_Info.ProductUUID);
126 Ident->Platform = ASDCP_PLATFORM;
127 Ident->ToolkitVersion.Major = VERSION_MAJOR;
128 Ident->ToolkitVersion.Minor = VERSION_APIMINOR;
129 Ident->ToolkitVersion.Patch = VERSION_IMPMINOR;
130 Ident->ToolkitVersion.Build = ASDCP_BUILD_NUMBER;
131 Ident->ToolkitVersion.Release = VersionType::RL_RELEASE;
134 ContentStorage* Storage = new ContentStorage;
135 m_HeaderPart.AddChildObject(Storage);
136 m_HeaderPart.m_Preface->ContentStorage = Storage->InstanceUID;
138 EssenceContainerData* ECD = new EssenceContainerData;
139 m_HeaderPart.AddChildObject(ECD);
140 Storage->EssenceContainerData.push_back(ECD->InstanceUID);
148 PackageUMID.MakeUMID(0x0f); // unidentified essence
149 m_MaterialPackage = new MaterialPackage;
150 m_MaterialPackage->Name = "AS-DCP Material Package";
151 m_MaterialPackage->PackageUID = PackageUMID;
152 m_HeaderPart.AddChildObject(m_MaterialPackage);
153 Storage->Packages.push_back(m_MaterialPackage->InstanceUID);
156 Track* NewTrack = new Track;
157 m_HeaderPart.AddChildObject(NewTrack);
158 NewTrack->EditRate = EditRate;
159 m_MaterialPackage->Tracks.push_back(NewTrack->InstanceUID);
160 NewTrack->TrackID = 1;
161 NewTrack->TrackName = "Timecode Track";
163 m_MPTCSequence = new Sequence;
164 m_HeaderPart.AddChildObject(m_MPTCSequence);
165 NewTrack->Sequence = m_MPTCSequence->InstanceUID;
166 m_MPTCSequence->DataDefinition = UL(Dict::ul(MDD_TimecodeDataDef));
168 m_MPTimecode = new TimecodeComponent;
169 m_HeaderPart.AddChildObject(m_MPTimecode);
170 m_MPTCSequence->StructuralComponents.push_back(m_MPTimecode->InstanceUID);
171 m_MPTimecode->RoundedTimecodeBase = TCFrameRate;
172 m_MPTimecode->StartTimecode = ui64_C(0);
173 m_MPTimecode->DataDefinition = UL(Dict::ul(MDD_TimecodeDataDef));
176 NewTrack = new Track;
177 m_HeaderPart.AddChildObject(NewTrack);
178 NewTrack->EditRate = EditRate;
179 m_MaterialPackage->Tracks.push_back(NewTrack->InstanceUID);
180 NewTrack->TrackID = 2;
181 NewTrack->TrackName = TrackName.c_str();
183 m_MPClSequence = new Sequence;
184 m_HeaderPart.AddChildObject(m_MPClSequence);
185 NewTrack->Sequence = m_MPClSequence->InstanceUID;
186 m_MPClSequence->DataDefinition = DataDefinition;
188 m_MPClip = new SourceClip;
189 m_HeaderPart.AddChildObject(m_MPClip);
190 m_MPClSequence->StructuralComponents.push_back(m_MPClip->InstanceUID);
191 m_MPClip->DataDefinition = DataDefinition;
194 // File (Source) Package
196 UUID assetUUID(m_Info.AssetUUID);
197 PackageUMID.MakeUMID(0x0f, assetUUID);
199 m_FilePackage = new SourcePackage;
200 m_FilePackage->Name = PackageLabel.c_str();
201 m_FilePackage->PackageUID = PackageUMID;
202 ECD->LinkedPackageUID = PackageUMID;
204 m_MPClip->SourcePackageID = PackageUMID;
205 m_MPClip->SourceTrackID = 2;
207 m_HeaderPart.AddChildObject(m_FilePackage);
208 Storage->Packages.push_back(m_FilePackage->InstanceUID);
211 NewTrack = new Track;
212 m_HeaderPart.AddChildObject(NewTrack);
213 NewTrack->EditRate = EditRate;
214 m_FilePackage->Tracks.push_back(NewTrack->InstanceUID);
215 NewTrack->TrackID = 1;
216 NewTrack->TrackName = "Timecode Track";
218 m_FPTCSequence = new Sequence;
219 m_HeaderPart.AddChildObject(m_FPTCSequence);
220 NewTrack->Sequence = m_FPTCSequence->InstanceUID;
221 m_FPTCSequence->DataDefinition = UL(Dict::ul(MDD_TimecodeDataDef));
223 m_FPTimecode = new TimecodeComponent;
224 m_HeaderPart.AddChildObject(m_FPTimecode);
225 m_FPTCSequence->StructuralComponents.push_back(m_FPTimecode->InstanceUID);
226 m_FPTimecode->RoundedTimecodeBase = TCFrameRate;
227 m_FPTimecode->StartTimecode = ui64_C(86400); // 01:00:00:00
228 m_FPTimecode->DataDefinition = UL(Dict::ul(MDD_TimecodeDataDef));
231 NewTrack = new Track;
232 m_HeaderPart.AddChildObject(NewTrack);
233 NewTrack->EditRate = EditRate;
234 m_FilePackage->Tracks.push_back(NewTrack->InstanceUID);
235 NewTrack->TrackID = 2;
236 NewTrack->TrackName = TrackName.c_str();
238 m_FPClSequence = new Sequence;
239 m_HeaderPart.AddChildObject(m_FPClSequence);
240 NewTrack->Sequence = m_FPClSequence->InstanceUID;
241 m_FPClSequence->DataDefinition = DataDefinition;
243 m_FPClip = new SourceClip;
244 m_HeaderPart.AddChildObject(m_FPClip);
245 m_FPClSequence->StructuralComponents.push_back(m_FPClip->InstanceUID);
246 m_FPClip->DataDefinition = DataDefinition;
248 // for now we do not allow setting this value, so all files will be 'original'
249 m_FPClip->SourceTrackID = 0;
250 m_FPClip->SourcePackageID = NilUMID;
253 // Essence Descriptor
255 m_EssenceDescriptor->EssenceContainer = WrappingUL;
256 m_EssenceDescriptor->LinkedTrackID = NewTrack->TrackID;
257 m_HeaderPart.m_Preface->PrimaryPackage = m_FilePackage->InstanceUID;
260 // Encryption Descriptor
262 if ( m_Info.EncryptedEssence )
264 UL CryptEssenceUL(Dict::ul(MDD_EncryptedContainerLabel));
265 m_HeaderPart.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);
275 m_HeaderPart.m_Preface->EssenceContainers = m_HeaderPart.EssenceContainers;
276 m_HeaderPart.AddChildObject(m_EssenceDescriptor);
278 std::list<FileDescriptor*>::iterator sdli = m_EssenceSubDescriptorList.begin();
279 for ( ; sdli != m_EssenceSubDescriptorList.end(); sdli++ )
280 m_HeaderPart.AddChildObject(*sdli);
282 m_FilePackage->Descriptor = m_EssenceDescriptor->InstanceUID;
284 // Write the header partition
285 Result_t result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
287 // create a body partition of we're writing proper 429-3/OP-Atom
288 if ( ASDCP_SUCCESS(result) && m_Info.LabelSetType == LS_MXF_SMPTE )
291 m_BodyPart.EssenceContainers = m_HeaderPart.EssenceContainers;
292 m_BodyPart.ThisPartition = m_File.Tell();
293 m_BodyPart.BodySID = 1;
294 UL OPAtomUL(Dict::ul(MDD_OPAtom));
295 m_BodyPart.OperationalPattern = OPAtomUL;
296 m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(1, m_BodyPart.ThisPartition)); // Second RIP Entry
298 UL BodyUL(Dict::ul(MDD_ClosedCompleteBodyPartition));
299 result = m_BodyPart.WriteToFile(m_File, BodyUL);
302 if ( ASDCP_SUCCESS(result) )
305 Kumu::fpos_t ECoffset = m_File.Tell();
307 if ( BytesPerEditUnit == 0 )
308 m_FooterPart.SetIndexParamsVBR(&m_HeaderPart.m_Primer, EditRate, ECoffset);
310 m_FooterPart.SetIndexParamsCBR(&m_HeaderPart.m_Primer, BytesPerEditUnit, EditRate);
318 // standard method of writing a plaintext or encrypted frame
320 ASDCP::h__Writer::WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf, const byte_t* EssenceUL,
321 AESEncContext* Ctx, HMACContext* HMAC)
323 Result_t result = RESULT_OK;
324 IntegrityPack IntPack;
326 byte_t overhead[128];
327 Kumu::MemIOWriter Overhead(overhead, 128);
329 if ( FrameBuf.Size() == 0 )
331 DefaultLogSink().Error("Cannot write empty frame buffer\n");
332 return RESULT_EMPTY_FB;
335 if ( m_Info.EncryptedEssence )
338 return RESULT_CRYPT_CTX;
340 if ( m_Info.UsesHMAC && ! HMAC )
341 return RESULT_HMAC_CTX;
343 if ( FrameBuf.PlaintextOffset() > FrameBuf.Size() )
344 return RESULT_LARGE_PTO;
346 // encrypt the essence data (create encrypted source value)
347 result = EncryptFrameBuffer(FrameBuf, m_CtFrameBuf, Ctx);
350 if ( ASDCP_SUCCESS(result) && m_Info.UsesHMAC )
351 result = IntPack.CalcValues(m_CtFrameBuf, m_Info.AssetUUID, m_FramesWritten + 1, HMAC);
353 if ( ASDCP_SUCCESS(result) )
355 if ( m_Info.LabelSetType == LS_MXF_INTEROP )
356 Overhead.WriteRaw(Dict::ul(MDD_MXFInterop_CryptEssence), SMPTE_UL_LENGTH);
358 Overhead.WriteRaw(Dict::ul(MDD_CryptEssence), SMPTE_UL_LENGTH);
360 // construct encrypted triplet header
361 ui32_t ETLength = klv_cryptinfo_size + m_CtFrameBuf.Size();
363 if ( m_Info.UsesHMAC )
364 ETLength += klv_intpack_size;
366 ETLength += (MXF_BER_LENGTH * 3); // for empty intpack
368 Overhead.WriteBER(ETLength, MXF_BER_LENGTH); // write encrypted triplet length
369 Overhead.WriteBER(UUIDlen, MXF_BER_LENGTH); // write ContextID length
370 Overhead.WriteRaw(m_Info.ContextID, UUIDlen); // write ContextID
371 Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH); // write PlaintextOffset length
372 Overhead.WriteUi64BE(FrameBuf.PlaintextOffset()); // write PlaintextOffset
373 Overhead.WriteBER(SMPTE_UL_LENGTH, MXF_BER_LENGTH); // write essence UL length
374 Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH); // write the essence UL
375 Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH); // write SourceLength length
376 Overhead.WriteUi64BE(FrameBuf.Size()); // write SourceLength
377 Overhead.WriteBER(m_CtFrameBuf.Size(), MXF_BER_LENGTH); // write ESV length
379 result = m_File.Writev(Overhead.Data(), Overhead.Length());
382 if ( ASDCP_SUCCESS(result) )
384 m_StreamOffset += Overhead.Length();
385 // write encrypted source value
386 result = m_File.Writev((byte_t*)m_CtFrameBuf.RoData(), m_CtFrameBuf.Size());
389 if ( ASDCP_SUCCESS(result) )
391 m_StreamOffset += m_CtFrameBuf.Size();
393 byte_t hmoverhead[512];
394 Kumu::MemIOWriter HMACOverhead(hmoverhead, 512);
397 if ( m_Info.UsesHMAC )
399 HMACOverhead.WriteRaw(IntPack.Data, klv_intpack_size);
402 { // we still need the var-pack length values if the intpack is empty
403 for ( ui32_t i = 0; i < 3 ; i++ )
404 HMACOverhead.WriteBER(0, MXF_BER_LENGTH);
408 result = m_File.Writev(HMACOverhead.Data(), HMACOverhead.Length());
409 m_StreamOffset += HMACOverhead.Length();
414 Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH);
415 Overhead.WriteBER(FrameBuf.Size(), MXF_BER_LENGTH);
416 result = m_File.Writev(Overhead.Data(), Overhead.Length());
418 if ( ASDCP_SUCCESS(result) )
419 result = m_File.Writev((byte_t*)FrameBuf.RoData(), FrameBuf.Size());
421 if ( ASDCP_SUCCESS(result) )
422 m_StreamOffset += Overhead.Length() + FrameBuf.Size();
425 if ( ASDCP_SUCCESS(result) )
426 result = m_File.Writev();
432 // standard method of writing the header and footer of a completed MXF file
435 ASDCP::h__Writer::WriteMXFFooter()
437 // Set top-level file package correctly for OP-Atom
439 m_MPTCSequence->Duration = m_MPTimecode->Duration = m_MPClSequence->Duration = m_MPClip->Duration =
440 m_FPTCSequence->Duration = m_FPTimecode->Duration = m_FPClSequence->Duration = m_FPClip->Duration =
441 m_EssenceDescriptor->ContainerDuration = m_FramesWritten;
443 Kumu::fpos_t here = m_File.Tell();
444 m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(0, here)); // Third RIP Entry
445 m_HeaderPart.FooterPartition = here;
447 // re-label the partition
448 UL OPAtomUL(Dict::ul(MDD_OPAtom));
450 if ( m_Info.LabelSetType == LS_MXF_INTEROP )
451 OPAtomUL.Set(Dict::ul(MDD_MXFInterop_OPAtom));
453 m_HeaderPart.OperationalPattern = OPAtomUL;
454 m_HeaderPart.m_Preface->OperationalPattern = m_HeaderPart.OperationalPattern;
456 m_FooterPart.PreviousPartition = m_BodyPart.ThisPartition;
457 m_FooterPart.OperationalPattern = m_HeaderPart.OperationalPattern;
458 m_FooterPart.EssenceContainers = m_HeaderPart.EssenceContainers;
459 m_FooterPart.FooterPartition = here;
460 m_FooterPart.ThisPartition = here;
462 Result_t result = m_FooterPart.WriteToFile(m_File, m_FramesWritten);
464 if ( ASDCP_SUCCESS(result) )
465 result = m_HeaderPart.m_RIP.WriteToFile(m_File);
467 if ( ASDCP_SUCCESS(result) )
468 result = m_File.Seek(0);
470 if ( ASDCP_SUCCESS(result) )
471 result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);