2 Copyright (c) 2004-2015, 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;
40 ASDCP::derive_timecode_rate_from_edit_rate(const ASDCP::Rational& edit_rate)
42 return floor(0.5 + edit_rate.Quotient());
46 // add DMS CryptographicFramework entry to source package
48 ASDCP::AddDMScrypt(Partition& HeaderPart, SourcePackage& Package,
49 WriterInfo& Descr, const UL& WrappingUL, const Dictionary*& Dict)
53 StaticTrack* NewTrack = new StaticTrack(Dict);
54 HeaderPart.AddChildObject(NewTrack);
55 Package.Tracks.push_back(NewTrack->InstanceUID);
56 NewTrack->TrackName = "Descriptive Track";
57 NewTrack->TrackID = 3;
59 Sequence* Seq = new Sequence(Dict);
60 HeaderPart.AddChildObject(Seq);
61 NewTrack->Sequence = Seq->InstanceUID;
62 Seq->DataDefinition = UL(Dict->ul(MDD_DescriptiveMetaDataDef));
64 DMSegment* Segment = new DMSegment(Dict);
65 HeaderPart.AddChildObject(Segment);
66 Seq->StructuralComponents.push_back(Segment->InstanceUID);
67 Segment->EventComment = "AS-DCP KLV Encryption";
69 CryptographicFramework* CFW = new CryptographicFramework(Dict);
70 HeaderPart.AddChildObject(CFW);
71 Segment->DMFramework = CFW->InstanceUID;
73 CryptographicContext* Context = new CryptographicContext(Dict);
74 HeaderPart.AddChildObject(Context);
75 CFW->ContextSR = Context->InstanceUID;
77 Context->ContextID.Set(Descr.ContextID);
78 Context->SourceEssenceContainer = WrappingUL; // ??????
79 Context->CipherAlgorithm.Set(Dict->ul(MDD_CipherAlgorithm_AES));
80 Context->MICAlgorithm.Set( Descr.UsesHMAC ? Dict->ul(MDD_MICAlgorithm_HMAC_SHA1) : Dict->ul(MDD_MICAlgorithm_NONE) );
81 Context->CryptographicKeyID.Set(Descr.CryptographicKeyID);
87 ASDCP::h__ASDCPWriter::h__ASDCPWriter(const Dictionary& d) :
88 MXF::TrackFileWriter<OP1aHeader>(d), m_BodyPart(m_Dict), m_FooterPart(m_Dict) {}
90 ASDCP::h__ASDCPWriter::~h__ASDCPWriter() {}
95 ASDCP::h__ASDCPWriter::CreateBodyPart(const MXF::Rational& EditRate, ui32_t BytesPerEditUnit)
98 Result_t result = RESULT_OK;
100 // create a body partition if we're writing proper 429-3/OP-Atom
101 if ( m_Info.LabelSetType == LS_MXF_SMPTE )
104 m_BodyPart.EssenceContainers = m_HeaderPart.EssenceContainers;
105 m_BodyPart.ThisPartition = m_File.Tell();
106 m_BodyPart.BodySID = 1;
107 UL OPAtomUL(m_Dict->ul(MDD_OPAtom));
108 m_BodyPart.OperationalPattern = OPAtomUL;
109 m_RIP.PairArray.push_back(RIP::PartitionPair(1, m_BodyPart.ThisPartition)); // Second RIP Entry
111 UL BodyUL(m_Dict->ul(MDD_ClosedCompleteBodyPartition));
112 result = m_BodyPart.WriteToFile(m_File, BodyUL);
116 m_HeaderPart.BodySID = 1;
119 if ( ASDCP_SUCCESS(result) )
122 Kumu::fpos_t ECoffset = m_File.Tell();
123 m_FooterPart.IndexSID = 129;
125 if ( BytesPerEditUnit == 0 )
127 m_FooterPart.SetIndexParamsVBR(&m_HeaderPart.m_Primer, EditRate, ECoffset);
131 m_FooterPart.SetIndexParamsCBR(&m_HeaderPart.m_Primer, BytesPerEditUnit, EditRate);
140 ASDCP::h__ASDCPWriter::WriteASDCPHeader(const std::string& PackageLabel, const UL& WrappingUL,
141 const std::string& TrackName, const UL& EssenceUL, const UL& DataDefinition,
142 const MXF::Rational& EditRate, ui32_t TCFrameRate, ui32_t BytesPerEditUnit)
147 if ( m_Info.LabelSetType == LS_MXF_SMPTE ) // ERK
149 m_RIP.PairArray.push_back(RIP::PartitionPair(0, 0)); // 3-part, no essence in header
153 m_RIP.PairArray.push_back(RIP::PartitionPair(1, 0)); // 2-part, essence in header
156 // timecode rate and essence rate are the same
157 AddSourceClip(EditRate, EditRate, TCFrameRate, TrackName, EssenceUL, DataDefinition, PackageLabel);
158 AddEssenceDescriptor(WrappingUL);
160 Result_t result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
162 if ( KM_SUCCESS(result) )
163 result = CreateBodyPart(EditRate, BytesPerEditUnit);
170 ASDCP::h__ASDCPWriter::WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf,const byte_t* EssenceUL,
171 AESEncContext* Ctx, HMACContext* HMAC)
173 return Write_EKLV_Packet(m_File, *m_Dict, m_HeaderPart, m_Info, m_CtFrameBuf, m_FramesWritten,
174 m_StreamOffset, FrameBuf, EssenceUL, Ctx, HMAC);
177 // standard method of writing the header and footer of a completed MXF file
180 ASDCP::h__ASDCPWriter::WriteASDCPFooter()
182 // update all Duration properties
183 DurationElementList_t::iterator dli = m_DurationUpdateList.begin();
185 for (; dli != m_DurationUpdateList.end(); ++dli )
187 **dli = m_FramesWritten;
190 m_EssenceDescriptor->ContainerDuration = m_FramesWritten;
191 m_FooterPart.PreviousPartition = m_RIP.PairArray.back().ByteOffset;
193 Kumu::fpos_t here = m_File.Tell();
194 m_RIP.PairArray.push_back(RIP::PartitionPair(0, here)); // Last RIP Entry
195 m_HeaderPart.FooterPartition = here;
198 // re-label the header partition, set the footer
199 UL OPAtomUL(m_Dict->ul(MDD_OPAtom));
200 m_HeaderPart.OperationalPattern = OPAtomUL;
201 m_HeaderPart.m_Preface->OperationalPattern = OPAtomUL;
202 m_FooterPart.OperationalPattern = OPAtomUL;
204 m_FooterPart.EssenceContainers = m_HeaderPart.EssenceContainers;
205 m_FooterPart.FooterPartition = here;
206 m_FooterPart.ThisPartition = here;
208 Result_t result = m_FooterPart.WriteToFile(m_File, m_FramesWritten);
210 if ( ASDCP_SUCCESS(result) )
211 result = m_RIP.WriteToFile(m_File);
213 if ( ASDCP_SUCCESS(result) )
214 result = m_File.Seek(0);
216 if ( ASDCP_SUCCESS(result) )
217 result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
224 //------------------------------------------------------------------------------------------
228 // standard method of writing a plaintext or encrypted frame
230 ASDCP::Write_EKLV_Packet(Kumu::FileWriter& File, const ASDCP::Dictionary& Dict, const MXF::OP1aHeader& HeaderPart,
231 const ASDCP::WriterInfo& Info, ASDCP::FrameBuffer& CtFrameBuf, ui32_t& FramesWritten,
232 ui64_t & StreamOffset, const ASDCP::FrameBuffer& FrameBuf, const byte_t* EssenceUL,
233 AESEncContext* Ctx, HMACContext* HMAC)
235 Result_t result = RESULT_OK;
236 IntegrityPack IntPack;
238 byte_t overhead[128];
239 Kumu::MemIOWriter Overhead(overhead, 128);
241 if ( FrameBuf.Size() == 0 )
243 DefaultLogSink().Error("Cannot write empty frame buffer\n");
244 return RESULT_EMPTY_FB;
247 if ( Info.EncryptedEssence )
250 return RESULT_CRYPT_CTX;
252 if ( Info.UsesHMAC && ! HMAC )
253 return RESULT_HMAC_CTX;
255 if ( FrameBuf.PlaintextOffset() > FrameBuf.Size() )
256 return RESULT_LARGE_PTO;
258 // encrypt the essence data (create encrypted source value)
259 result = EncryptFrameBuffer(FrameBuf, CtFrameBuf, Ctx);
262 if ( ASDCP_SUCCESS(result) && Info.UsesHMAC )
263 result = IntPack.CalcValues(CtFrameBuf, Info.AssetUUID, FramesWritten + 1, HMAC);
265 if ( ASDCP_SUCCESS(result) )
267 Overhead.WriteRaw(Dict.ul(MDD_CryptEssence), SMPTE_UL_LENGTH);
269 // construct encrypted triplet header
270 ui32_t ETLength = klv_cryptinfo_size + CtFrameBuf.Size();
271 ui32_t BER_length = MXF_BER_LENGTH;
274 ETLength += klv_intpack_size;
276 ETLength += (MXF_BER_LENGTH * 3); // for empty intpack
278 if ( ETLength > 0x00ffffff ) // Need BER integer longer than MXF_BER_LENGTH bytes
280 BER_length = Kumu::get_BER_length_for_value(ETLength);
282 // the packet is longer by the difference in expected vs. actual BER length
283 ETLength += BER_length - MXF_BER_LENGTH;
285 if ( BER_length == 0 )
286 result = RESULT_KLV_CODING;
289 if ( ASDCP_SUCCESS(result) )
291 if ( ! ( Overhead.WriteBER(ETLength, BER_length) // write encrypted triplet length
292 && Overhead.WriteBER(UUIDlen, MXF_BER_LENGTH) // write ContextID length
293 && Overhead.WriteRaw(Info.ContextID, UUIDlen) // write ContextID
294 && Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH) // write PlaintextOffset length
295 && Overhead.WriteUi64BE(FrameBuf.PlaintextOffset()) // write PlaintextOffset
296 && Overhead.WriteBER(SMPTE_UL_LENGTH, MXF_BER_LENGTH) // write essence UL length
297 && Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH) // write the essence UL
298 && Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH) // write SourceLength length
299 && Overhead.WriteUi64BE(FrameBuf.Size()) // write SourceLength
300 && Overhead.WriteBER(CtFrameBuf.Size(), BER_length) ) ) // write ESV length
302 result = RESULT_KLV_CODING;
306 if ( ASDCP_SUCCESS(result) )
307 result = File.Writev(Overhead.Data(), Overhead.Length());
310 if ( ASDCP_SUCCESS(result) )
312 StreamOffset += Overhead.Length();
313 // write encrypted source value
314 result = File.Writev((byte_t*)CtFrameBuf.RoData(), CtFrameBuf.Size());
317 if ( ASDCP_SUCCESS(result) )
319 StreamOffset += CtFrameBuf.Size();
321 byte_t hmoverhead[512];
322 Kumu::MemIOWriter HMACOverhead(hmoverhead, 512);
327 HMACOverhead.WriteRaw(IntPack.Data, klv_intpack_size);
330 { // we still need the var-pack length values if the intpack is empty
331 for ( ui32_t i = 0; i < 3 ; i++ )
332 HMACOverhead.WriteBER(0, MXF_BER_LENGTH);
336 result = File.Writev(HMACOverhead.Data(), HMACOverhead.Length());
337 StreamOffset += HMACOverhead.Length();
342 ui32_t BER_length = MXF_BER_LENGTH;
344 if ( FrameBuf.Size() > 0x00ffffff ) // Need BER integer longer than MXF_BER_LENGTH bytes
346 BER_length = Kumu::get_BER_length_for_value(FrameBuf.Size());
348 if ( BER_length == 0 )
349 result = RESULT_KLV_CODING;
352 Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH);
353 Overhead.WriteBER(FrameBuf.Size(), BER_length);
355 if ( ASDCP_SUCCESS(result) )
356 result = File.Writev(Overhead.Data(), Overhead.Length());
358 if ( ASDCP_SUCCESS(result) )
359 result = File.Writev((byte_t*)FrameBuf.RoData(), FrameBuf.Size());
361 if ( ASDCP_SUCCESS(result) )
362 StreamOffset += Overhead.Length() + FrameBuf.Size();
365 if ( ASDCP_SUCCESS(result) )
366 result = File.Writev();