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;
114 if ( m_Info.LabelSetType == LS_MXF_SMPTE )
115 m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(0, 0)); // 3-part, no essence in header
117 m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(1, 0)); // 2-part, essence in header
122 Identification* Ident = new Identification;
123 m_HeaderPart.AddChildObject(Ident);
124 m_HeaderPart.m_Preface->Identifications.push_back(Ident->InstanceUID);
126 Kumu::GenRandomValue(Ident->ThisGenerationUID);
127 Ident->CompanyName = m_Info.CompanyName.c_str();
128 Ident->ProductName = m_Info.ProductName.c_str();
129 Ident->VersionString = m_Info.ProductVersion.c_str();
130 Ident->ProductUID.Set(m_Info.ProductUUID);
131 Ident->Platform = ASDCP_PLATFORM;
132 Ident->ToolkitVersion.Major = VERSION_MAJOR;
133 Ident->ToolkitVersion.Minor = VERSION_APIMINOR;
134 Ident->ToolkitVersion.Patch = VERSION_IMPMINOR;
135 Ident->ToolkitVersion.Build = ASDCP_BUILD_NUMBER;
136 Ident->ToolkitVersion.Release = VersionType::RL_RELEASE;
139 ContentStorage* Storage = new ContentStorage;
140 m_HeaderPart.AddChildObject(Storage);
141 m_HeaderPart.m_Preface->ContentStorage = Storage->InstanceUID;
143 EssenceContainerData* ECD = new EssenceContainerData;
144 m_HeaderPart.AddChildObject(ECD);
145 Storage->EssenceContainerData.push_back(ECD->InstanceUID);
153 PackageUMID.MakeUMID(0x0f); // unidentified essence
154 m_MaterialPackage = new MaterialPackage;
155 m_MaterialPackage->Name = "AS-DCP Material Package";
156 m_MaterialPackage->PackageUID = PackageUMID;
157 m_HeaderPart.AddChildObject(m_MaterialPackage);
158 Storage->Packages.push_back(m_MaterialPackage->InstanceUID);
161 Track* NewTrack = new Track;
162 m_HeaderPart.AddChildObject(NewTrack);
163 NewTrack->EditRate = EditRate;
164 m_MaterialPackage->Tracks.push_back(NewTrack->InstanceUID);
165 NewTrack->TrackID = 1;
166 NewTrack->TrackName = "Timecode Track";
168 m_MPTCSequence = new Sequence;
169 m_HeaderPart.AddChildObject(m_MPTCSequence);
170 NewTrack->Sequence = m_MPTCSequence->InstanceUID;
171 m_MPTCSequence->DataDefinition = UL(Dict::ul(MDD_TimecodeDataDef));
173 m_MPTimecode = new TimecodeComponent;
174 m_HeaderPart.AddChildObject(m_MPTimecode);
175 m_MPTCSequence->StructuralComponents.push_back(m_MPTimecode->InstanceUID);
176 m_MPTimecode->RoundedTimecodeBase = TCFrameRate;
177 m_MPTimecode->StartTimecode = ui64_C(0);
178 m_MPTimecode->DataDefinition = UL(Dict::ul(MDD_TimecodeDataDef));
181 NewTrack = new Track;
182 m_HeaderPart.AddChildObject(NewTrack);
183 NewTrack->EditRate = EditRate;
184 m_MaterialPackage->Tracks.push_back(NewTrack->InstanceUID);
185 NewTrack->TrackID = 2;
186 NewTrack->TrackName = TrackName.c_str();
188 m_MPClSequence = new Sequence;
189 m_HeaderPart.AddChildObject(m_MPClSequence);
190 NewTrack->Sequence = m_MPClSequence->InstanceUID;
191 m_MPClSequence->DataDefinition = DataDefinition;
193 m_MPClip = new SourceClip;
194 m_HeaderPart.AddChildObject(m_MPClip);
195 m_MPClSequence->StructuralComponents.push_back(m_MPClip->InstanceUID);
196 m_MPClip->DataDefinition = DataDefinition;
199 // File (Source) Package
201 UUID assetUUID(m_Info.AssetUUID);
202 PackageUMID.MakeUMID(0x0f, assetUUID);
204 m_FilePackage = new SourcePackage;
205 m_FilePackage->Name = PackageLabel.c_str();
206 m_FilePackage->PackageUID = PackageUMID;
207 ECD->LinkedPackageUID = PackageUMID;
209 m_MPClip->SourcePackageID = PackageUMID;
210 m_MPClip->SourceTrackID = 2;
212 m_HeaderPart.AddChildObject(m_FilePackage);
213 Storage->Packages.push_back(m_FilePackage->InstanceUID);
216 NewTrack = new Track;
217 m_HeaderPart.AddChildObject(NewTrack);
218 NewTrack->EditRate = EditRate;
219 m_FilePackage->Tracks.push_back(NewTrack->InstanceUID);
220 NewTrack->TrackID = 1;
221 NewTrack->TrackName = "Timecode Track";
223 m_FPTCSequence = new Sequence;
224 m_HeaderPart.AddChildObject(m_FPTCSequence);
225 NewTrack->Sequence = m_FPTCSequence->InstanceUID;
226 m_FPTCSequence->DataDefinition = UL(Dict::ul(MDD_TimecodeDataDef));
228 m_FPTimecode = new TimecodeComponent;
229 m_HeaderPart.AddChildObject(m_FPTimecode);
230 m_FPTCSequence->StructuralComponents.push_back(m_FPTimecode->InstanceUID);
231 m_FPTimecode->RoundedTimecodeBase = TCFrameRate;
232 m_FPTimecode->StartTimecode = ui64_C(86400); // 01:00:00:00
233 m_FPTimecode->DataDefinition = UL(Dict::ul(MDD_TimecodeDataDef));
236 NewTrack = new Track;
237 m_HeaderPart.AddChildObject(NewTrack);
238 NewTrack->EditRate = EditRate;
239 m_FilePackage->Tracks.push_back(NewTrack->InstanceUID);
240 NewTrack->TrackID = 2;
241 NewTrack->TrackName = TrackName.c_str();
243 m_FPClSequence = new Sequence;
244 m_HeaderPart.AddChildObject(m_FPClSequence);
245 NewTrack->Sequence = m_FPClSequence->InstanceUID;
246 m_FPClSequence->DataDefinition = DataDefinition;
248 m_FPClip = new SourceClip;
249 m_HeaderPart.AddChildObject(m_FPClip);
250 m_FPClSequence->StructuralComponents.push_back(m_FPClip->InstanceUID);
251 m_FPClip->DataDefinition = DataDefinition;
253 // for now we do not allow setting this value, so all files will be 'original'
254 m_FPClip->SourceTrackID = 0;
255 m_FPClip->SourcePackageID = NilUMID;
258 // Essence Descriptor
260 m_EssenceDescriptor->EssenceContainer = WrappingUL;
261 m_EssenceDescriptor->LinkedTrackID = NewTrack->TrackID;
262 m_HeaderPart.m_Preface->PrimaryPackage = m_FilePackage->InstanceUID;
265 // Encryption Descriptor
267 if ( m_Info.EncryptedEssence )
269 UL CryptEssenceUL(Dict::ul(MDD_EncryptedContainerLabel));
270 m_HeaderPart.EssenceContainers.push_back(CryptEssenceUL);
271 m_HeaderPart.m_Preface->DMSchemes.push_back(UL(Dict::ul(MDD_CryptographicFrameworkLabel)));
272 AddDMScrypt(m_HeaderPart, *m_FilePackage, m_Info, WrappingUL);
276 m_HeaderPart.EssenceContainers.push_back(UL(Dict::ul(MDD_GCMulti)));
277 m_HeaderPart.EssenceContainers.push_back(WrappingUL);
280 m_HeaderPart.m_Preface->EssenceContainers = m_HeaderPart.EssenceContainers;
281 m_HeaderPart.AddChildObject(m_EssenceDescriptor);
283 std::list<FileDescriptor*>::iterator sdli = m_EssenceSubDescriptorList.begin();
284 for ( ; sdli != m_EssenceSubDescriptorList.end(); sdli++ )
285 m_HeaderPart.AddChildObject(*sdli);
287 m_FilePackage->Descriptor = m_EssenceDescriptor->InstanceUID;
289 // Write the header partition
290 Result_t result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
292 // create a body partition of we're writing proper 429-3/OP-Atom
293 if ( ASDCP_SUCCESS(result) && m_Info.LabelSetType == LS_MXF_SMPTE )
296 m_BodyPart.EssenceContainers = m_HeaderPart.EssenceContainers;
297 m_BodyPart.ThisPartition = m_File.Tell();
298 m_BodyPart.BodySID = 1;
299 UL OPAtomUL(Dict::ul(MDD_OPAtom));
300 m_BodyPart.OperationalPattern = OPAtomUL;
301 m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(1, m_BodyPart.ThisPartition)); // Second RIP Entry
303 UL BodyUL(Dict::ul(MDD_ClosedCompleteBodyPartition));
304 result = m_BodyPart.WriteToFile(m_File, BodyUL);
308 m_HeaderPart.BodySID = 1;
311 if ( ASDCP_SUCCESS(result) )
314 Kumu::fpos_t ECoffset = m_File.Tell();
315 m_FooterPart.IndexSID = 129;
317 if ( BytesPerEditUnit == 0 )
318 m_FooterPart.SetIndexParamsVBR(&m_HeaderPart.m_Primer, EditRate, ECoffset);
320 m_FooterPart.SetIndexParamsCBR(&m_HeaderPart.m_Primer, BytesPerEditUnit, EditRate);
328 // standard method of writing a plaintext or encrypted frame
330 ASDCP::h__Writer::WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf, const byte_t* EssenceUL,
331 AESEncContext* Ctx, HMACContext* HMAC)
333 Result_t result = RESULT_OK;
334 IntegrityPack IntPack;
336 byte_t overhead[128];
337 Kumu::MemIOWriter Overhead(overhead, 128);
339 if ( FrameBuf.Size() == 0 )
341 DefaultLogSink().Error("Cannot write empty frame buffer\n");
342 return RESULT_EMPTY_FB;
345 if ( m_Info.EncryptedEssence )
348 return RESULT_CRYPT_CTX;
350 if ( m_Info.UsesHMAC && ! HMAC )
351 return RESULT_HMAC_CTX;
353 if ( FrameBuf.PlaintextOffset() > FrameBuf.Size() )
354 return RESULT_LARGE_PTO;
356 // encrypt the essence data (create encrypted source value)
357 result = EncryptFrameBuffer(FrameBuf, m_CtFrameBuf, Ctx);
360 if ( ASDCP_SUCCESS(result) && m_Info.UsesHMAC )
361 result = IntPack.CalcValues(m_CtFrameBuf, m_Info.AssetUUID, m_FramesWritten + 1, HMAC);
363 if ( ASDCP_SUCCESS(result) )
365 if ( m_Info.LabelSetType == LS_MXF_INTEROP )
366 Overhead.WriteRaw(Dict::ul(MDD_MXFInterop_CryptEssence), SMPTE_UL_LENGTH);
368 Overhead.WriteRaw(Dict::ul(MDD_CryptEssence), SMPTE_UL_LENGTH);
370 // construct encrypted triplet header
371 ui32_t ETLength = klv_cryptinfo_size + m_CtFrameBuf.Size();
373 if ( m_Info.UsesHMAC )
374 ETLength += klv_intpack_size;
376 ETLength += (MXF_BER_LENGTH * 3); // for empty intpack
378 Overhead.WriteBER(ETLength, MXF_BER_LENGTH); // write encrypted triplet length
379 Overhead.WriteBER(UUIDlen, MXF_BER_LENGTH); // write ContextID length
380 Overhead.WriteRaw(m_Info.ContextID, UUIDlen); // write ContextID
381 Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH); // write PlaintextOffset length
382 Overhead.WriteUi64BE(FrameBuf.PlaintextOffset()); // write PlaintextOffset
383 Overhead.WriteBER(SMPTE_UL_LENGTH, MXF_BER_LENGTH); // write essence UL length
384 Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH); // write the essence UL
385 Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH); // write SourceLength length
386 Overhead.WriteUi64BE(FrameBuf.Size()); // write SourceLength
387 Overhead.WriteBER(m_CtFrameBuf.Size(), MXF_BER_LENGTH); // write ESV length
389 result = m_File.Writev(Overhead.Data(), Overhead.Length());
392 if ( ASDCP_SUCCESS(result) )
394 m_StreamOffset += Overhead.Length();
395 // write encrypted source value
396 result = m_File.Writev((byte_t*)m_CtFrameBuf.RoData(), m_CtFrameBuf.Size());
399 if ( ASDCP_SUCCESS(result) )
401 m_StreamOffset += m_CtFrameBuf.Size();
403 byte_t hmoverhead[512];
404 Kumu::MemIOWriter HMACOverhead(hmoverhead, 512);
407 if ( m_Info.UsesHMAC )
409 HMACOverhead.WriteRaw(IntPack.Data, klv_intpack_size);
412 { // we still need the var-pack length values if the intpack is empty
413 for ( ui32_t i = 0; i < 3 ; i++ )
414 HMACOverhead.WriteBER(0, MXF_BER_LENGTH);
418 result = m_File.Writev(HMACOverhead.Data(), HMACOverhead.Length());
419 m_StreamOffset += HMACOverhead.Length();
424 Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH);
425 Overhead.WriteBER(FrameBuf.Size(), MXF_BER_LENGTH);
426 result = m_File.Writev(Overhead.Data(), Overhead.Length());
428 if ( ASDCP_SUCCESS(result) )
429 result = m_File.Writev((byte_t*)FrameBuf.RoData(), FrameBuf.Size());
431 if ( ASDCP_SUCCESS(result) )
432 m_StreamOffset += Overhead.Length() + FrameBuf.Size();
435 if ( ASDCP_SUCCESS(result) )
436 result = m_File.Writev();
442 // standard method of writing the header and footer of a completed MXF file
445 ASDCP::h__Writer::WriteMXFFooter()
447 // Set top-level file package correctly for OP-Atom
449 m_MPTCSequence->Duration = m_MPTimecode->Duration = m_MPClSequence->Duration = m_MPClip->Duration =
450 m_FPTCSequence->Duration = m_FPTimecode->Duration = m_FPClSequence->Duration = m_FPClip->Duration =
451 m_EssenceDescriptor->ContainerDuration = m_FramesWritten;
453 Kumu::fpos_t here = m_File.Tell();
454 m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(0, here)); // Last RIP Entry
455 m_HeaderPart.FooterPartition = here;
457 // re-label the partition
458 UL OPAtomUL(Dict::ul(MDD_OPAtom));
460 if ( m_Info.LabelSetType == LS_MXF_INTEROP )
461 OPAtomUL.Set(Dict::ul(MDD_MXFInterop_OPAtom));
463 m_HeaderPart.OperationalPattern = OPAtomUL;
464 m_HeaderPart.m_Preface->OperationalPattern = m_HeaderPart.OperationalPattern;
466 if ( m_Info.LabelSetType == LS_MXF_SMPTE )
467 m_FooterPart.PreviousPartition = m_BodyPart.ThisPartition;
469 m_FooterPart.PreviousPartition = m_HeaderPart.ThisPartition;
471 m_FooterPart.OperationalPattern = m_HeaderPart.OperationalPattern;
472 m_FooterPart.EssenceContainers = m_HeaderPart.EssenceContainers;
473 m_FooterPart.FooterPartition = here;
474 m_FooterPart.ThisPartition = here;
476 Result_t result = m_FooterPart.WriteToFile(m_File, m_FramesWritten);
478 if ( ASDCP_SUCCESS(result) )
479 result = m_HeaderPart.m_RIP.WriteToFile(m_File);
481 if ( ASDCP_SUCCESS(result) )
482 result = m_File.Seek(0);
484 if ( ASDCP_SUCCESS(result) )
485 result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);