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
28 \version $Id: h__Writer.cpp,v 1.59 2015/10/09 23:41:11 jhurst Exp $
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, std::string* hash)
173 return Write_EKLV_Packet(m_File, *m_Dict, m_HeaderPart, m_Info, m_CtFrameBuf, m_FramesWritten,
174 m_StreamOffset, FrameBuf, EssenceUL, Ctx, HMAC, hash);
178 ASDCP::h__ASDCPWriter::FakeWriteEKLVPacket(int size)
180 m_StreamOffset += size;
181 m_File.Seek(size, Kumu::SP_POS);
186 // standard method of writing the header and footer of a completed MXF file
189 ASDCP::h__ASDCPWriter::WriteASDCPFooter()
191 // update all Duration properties
192 DurationElementList_t::iterator dli = m_DurationUpdateList.begin();
194 for (; dli != m_DurationUpdateList.end(); ++dli )
196 **dli = m_FramesWritten;
199 m_EssenceDescriptor->ContainerDuration = m_FramesWritten;
200 m_FooterPart.PreviousPartition = m_RIP.PairArray.back().ByteOffset;
202 Kumu::fpos_t here = m_File.Tell();
203 m_RIP.PairArray.push_back(RIP::PartitionPair(0, here)); // Last RIP Entry
204 m_HeaderPart.FooterPartition = here;
207 // re-label the header partition, set the footer
208 UL OPAtomUL(m_Dict->ul(MDD_OPAtom));
209 m_HeaderPart.OperationalPattern = OPAtomUL;
210 m_HeaderPart.m_Preface->OperationalPattern = OPAtomUL;
211 m_FooterPart.OperationalPattern = OPAtomUL;
213 m_FooterPart.EssenceContainers = m_HeaderPart.EssenceContainers;
214 m_FooterPart.FooterPartition = here;
215 m_FooterPart.ThisPartition = here;
217 Result_t result = m_FooterPart.WriteToFile(m_File, m_FramesWritten);
219 if ( ASDCP_SUCCESS(result) )
220 result = m_RIP.WriteToFile(m_File);
222 if ( ASDCP_SUCCESS(result) )
223 result = m_File.Seek(0);
225 if ( ASDCP_SUCCESS(result) )
226 result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
233 //------------------------------------------------------------------------------------------
237 // standard method of writing a plaintext or encrypted frame
239 ASDCP::Write_EKLV_Packet(Kumu::FileWriter& File, const ASDCP::Dictionary& Dict, const MXF::OP1aHeader&,
240 const ASDCP::WriterInfo& Info, ASDCP::FrameBuffer& CtFrameBuf, ui32_t& FramesWritten,
241 ui64_t & StreamOffset, const ASDCP::FrameBuffer& FrameBuf, const byte_t* EssenceUL,
242 AESEncContext* Ctx, HMACContext* HMAC, std::string* hash)
244 Result_t result = RESULT_OK;
245 IntegrityPack IntPack;
252 byte_t overhead[128];
253 Kumu::MemIOWriter Overhead(overhead, 128);
255 if ( FrameBuf.Size() == 0 )
257 DefaultLogSink().Error("Cannot write empty frame buffer\n");
258 return RESULT_EMPTY_FB;
261 if ( Info.EncryptedEssence )
264 return RESULT_CRYPT_CTX;
266 if ( Info.UsesHMAC && ! HMAC )
267 return RESULT_HMAC_CTX;
269 if ( FrameBuf.PlaintextOffset() > FrameBuf.Size() )
270 return RESULT_LARGE_PTO;
272 // encrypt the essence data (create encrypted source value)
273 result = EncryptFrameBuffer(FrameBuf, CtFrameBuf, Ctx);
276 if ( ASDCP_SUCCESS(result) && Info.UsesHMAC )
277 result = IntPack.CalcValues(CtFrameBuf, Info.AssetUUID, FramesWritten + 1, HMAC);
279 if ( ASDCP_SUCCESS(result) )
281 Overhead.WriteRaw(Dict.ul(MDD_CryptEssence), SMPTE_UL_LENGTH);
283 // construct encrypted triplet header
284 ui32_t ETLength = klv_cryptinfo_size + CtFrameBuf.Size();
285 ui32_t BER_length = MXF_BER_LENGTH;
288 ETLength += klv_intpack_size;
290 ETLength += (MXF_BER_LENGTH * 3); // for empty intpack
292 if ( ETLength > 0x00ffffff ) // Need BER integer longer than MXF_BER_LENGTH bytes
294 BER_length = Kumu::get_BER_length_for_value(ETLength);
296 // the packet is longer by the difference in expected vs. actual BER length
297 ETLength += BER_length - MXF_BER_LENGTH;
299 if ( BER_length == 0 )
300 result = RESULT_KLV_CODING;
303 if ( ASDCP_SUCCESS(result) )
305 if ( ! ( Overhead.WriteBER(ETLength, BER_length) // write encrypted triplet length
306 && Overhead.WriteBER(UUIDlen, MXF_BER_LENGTH) // write ContextID length
307 && Overhead.WriteRaw(Info.ContextID, UUIDlen) // write ContextID
308 && Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH) // write PlaintextOffset length
309 && Overhead.WriteUi64BE(FrameBuf.PlaintextOffset()) // write PlaintextOffset
310 && Overhead.WriteBER(SMPTE_UL_LENGTH, MXF_BER_LENGTH) // write essence UL length
311 && Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH) // write the essence UL
312 && Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH) // write SourceLength length
313 && Overhead.WriteUi64BE(FrameBuf.Size()) // write SourceLength
314 && Overhead.WriteBER(CtFrameBuf.Size(), BER_length) ) ) // write ESV length
316 result = RESULT_KLV_CODING;
320 if ( ASDCP_SUCCESS(result) )
321 result = File.Writev(Overhead.Data(), Overhead.Length());
324 if ( ASDCP_SUCCESS(result) )
326 StreamOffset += Overhead.Length();
327 // write encrypted source value
328 result = File.Writev((byte_t*)CtFrameBuf.RoData(), CtFrameBuf.Size());
331 if ( ASDCP_SUCCESS(result) )
333 StreamOffset += CtFrameBuf.Size();
335 byte_t hmoverhead[512];
336 Kumu::MemIOWriter HMACOverhead(hmoverhead, 512);
341 HMACOverhead.WriteRaw(IntPack.Data, klv_intpack_size);
344 { // we still need the var-pack length values if the intpack is empty
345 for ( ui32_t i = 0; i < 3 ; i++ )
346 HMACOverhead.WriteBER(0, MXF_BER_LENGTH);
350 result = File.Writev(HMACOverhead.Data(), HMACOverhead.Length());
351 StreamOffset += HMACOverhead.Length();
356 ui32_t BER_length = MXF_BER_LENGTH;
358 if ( FrameBuf.Size() > 0x00ffffff ) // Need BER integer longer than MXF_BER_LENGTH bytes
360 BER_length = Kumu::get_BER_length_for_value(FrameBuf.Size());
362 if ( BER_length == 0 )
363 result = RESULT_KLV_CODING;
366 Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH);
367 Overhead.WriteBER(FrameBuf.Size(), BER_length);
369 if ( ASDCP_SUCCESS(result) )
370 result = File.Writev(Overhead.Data(), Overhead.Length());
372 if ( ASDCP_SUCCESS(result) )
373 result = File.Writev((byte_t*)FrameBuf.RoData(), FrameBuf.Size());
375 if ( ASDCP_SUCCESS(result) )
376 StreamOffset += Overhead.Length() + FrameBuf.Size();
379 if ( ASDCP_SUCCESS(result) )
380 result = File.Writev();
384 *hash = File.StopHashing();