2 Copyright (c) 2004-2013, 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;
39 // add DMS CryptographicFramework entry to source package
41 ASDCP::AddDMScrypt(Partition& HeaderPart, SourcePackage& Package,
42 WriterInfo& Descr, const UL& WrappingUL, const Dictionary*& Dict)
46 StaticTrack* NewTrack = new StaticTrack(Dict);
47 HeaderPart.AddChildObject(NewTrack);
48 Package.Tracks.push_back(NewTrack->InstanceUID);
49 NewTrack->TrackName = "Descriptive Track";
50 NewTrack->TrackID = 3;
52 Sequence* Seq = new Sequence(Dict);
53 HeaderPart.AddChildObject(Seq);
54 NewTrack->Sequence = Seq->InstanceUID;
55 Seq->DataDefinition = UL(Dict->ul(MDD_DescriptiveMetaDataDef));
57 DMSegment* Segment = new DMSegment(Dict);
58 HeaderPart.AddChildObject(Segment);
59 Seq->StructuralComponents.push_back(Segment->InstanceUID);
60 Segment->EventComment = "AS-DCP KLV Encryption";
62 CryptographicFramework* CFW = new CryptographicFramework(Dict);
63 HeaderPart.AddChildObject(CFW);
64 Segment->DMFramework = CFW->InstanceUID;
66 CryptographicContext* Context = new CryptographicContext(Dict);
67 HeaderPart.AddChildObject(Context);
68 CFW->ContextSR = Context->InstanceUID;
70 Context->ContextID.Set(Descr.ContextID);
71 Context->SourceEssenceContainer = WrappingUL; // ??????
72 Context->CipherAlgorithm.Set(Dict->ul(MDD_CipherAlgorithm_AES));
73 Context->MICAlgorithm.Set( Descr.UsesHMAC ? Dict->ul(MDD_MICAlgorithm_HMAC_SHA1) : Dict->ul(MDD_MICAlgorithm_NONE) );
74 Context->CryptographicKeyID.Set(Descr.CryptographicKeyID);
80 ASDCP::h__ASDCPWriter::h__ASDCPWriter(const Dictionary& d) :
81 MXF::TrackFileWriter<OP1aHeader>(d), m_BodyPart(m_Dict), m_FooterPart(m_Dict) {}
83 ASDCP::h__ASDCPWriter::~h__ASDCPWriter() {}
88 ASDCP::h__ASDCPWriter::CreateBodyPart(const MXF::Rational& EditRate, ui32_t BytesPerEditUnit)
91 Result_t result = RESULT_OK;
93 // create a body partition if we're writing proper 429-3/OP-Atom
94 if ( m_Info.LabelSetType == LS_MXF_SMPTE )
97 m_BodyPart.EssenceContainers = m_HeaderPart.EssenceContainers;
98 m_BodyPart.ThisPartition = m_File.Tell();
99 m_BodyPart.BodySID = 1;
100 UL OPAtomUL(m_Dict->ul(MDD_OPAtom));
101 m_BodyPart.OperationalPattern = OPAtomUL;
102 m_RIP.PairArray.push_back(RIP::Pair(1, m_BodyPart.ThisPartition)); // Second RIP Entry
104 UL BodyUL(m_Dict->ul(MDD_ClosedCompleteBodyPartition));
105 result = m_BodyPart.WriteToFile(m_File, BodyUL);
109 m_HeaderPart.BodySID = 1;
112 if ( ASDCP_SUCCESS(result) )
115 Kumu::fpos_t ECoffset = m_File.Tell();
116 m_FooterPart.IndexSID = 129;
118 if ( BytesPerEditUnit == 0 )
120 m_FooterPart.SetIndexParamsVBR(&m_HeaderPart.m_Primer, EditRate, ECoffset);
124 m_FooterPart.SetIndexParamsCBR(&m_HeaderPart.m_Primer, BytesPerEditUnit, EditRate);
133 ASDCP::h__ASDCPWriter::WriteASDCPHeader(const std::string& PackageLabel, const UL& WrappingUL,
134 const std::string& TrackName, const UL& EssenceUL, const UL& DataDefinition,
135 const MXF::Rational& EditRate, ui32_t TCFrameRate, ui32_t BytesPerEditUnit)
140 if ( m_Info.LabelSetType == LS_MXF_SMPTE ) // ERK
142 m_RIP.PairArray.push_back(RIP::Pair(0, 0)); // 3-part, no essence in header
146 m_RIP.PairArray.push_back(RIP::Pair(1, 0)); // 2-part, essence in header
149 AddSourceClip(EditRate, TCFrameRate, TrackName, EssenceUL, DataDefinition, PackageLabel);
150 AddEssenceDescriptor(WrappingUL);
152 Result_t result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
154 if ( KM_SUCCESS(result) )
155 result = CreateBodyPart(EditRate, BytesPerEditUnit);
162 ASDCP::h__ASDCPWriter::WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf,const byte_t* EssenceUL,
163 AESEncContext* Ctx, HMACContext* HMAC)
165 return Write_EKLV_Packet(m_File, *m_Dict, m_HeaderPart, m_Info, m_CtFrameBuf, m_FramesWritten,
166 m_StreamOffset, FrameBuf, EssenceUL, Ctx, HMAC);
169 // standard method of writing the header and footer of a completed MXF file
172 ASDCP::h__ASDCPWriter::WriteASDCPFooter()
174 // update all Duration properties
175 DurationElementList_t::iterator dli = m_DurationUpdateList.begin();
177 for (; dli != m_DurationUpdateList.end(); ++dli )
179 **dli = m_FramesWritten;
182 m_EssenceDescriptor->ContainerDuration = m_FramesWritten;
183 m_FooterPart.PreviousPartition = m_RIP.PairArray.back().ByteOffset;
185 Kumu::fpos_t here = m_File.Tell();
186 m_RIP.PairArray.push_back(RIP::Pair(0, here)); // Last RIP Entry
187 m_HeaderPart.FooterPartition = here;
190 // re-label the header partition, set the footer
191 UL OPAtomUL(m_Dict->ul(MDD_OPAtom));
192 m_HeaderPart.OperationalPattern = OPAtomUL;
193 m_HeaderPart.m_Preface->OperationalPattern = OPAtomUL;
194 m_FooterPart.OperationalPattern = OPAtomUL;
196 m_FooterPart.EssenceContainers = m_HeaderPart.EssenceContainers;
197 m_FooterPart.FooterPartition = here;
198 m_FooterPart.ThisPartition = here;
200 Result_t result = m_FooterPart.WriteToFile(m_File, m_FramesWritten);
202 if ( ASDCP_SUCCESS(result) )
203 result = m_RIP.WriteToFile(m_File);
205 if ( ASDCP_SUCCESS(result) )
206 result = m_File.Seek(0);
208 if ( ASDCP_SUCCESS(result) )
209 result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
216 //------------------------------------------------------------------------------------------
220 // standard method of writing a plaintext or encrypted frame
222 ASDCP::Write_EKLV_Packet(Kumu::FileWriter& File, const ASDCP::Dictionary& Dict, const MXF::OP1aHeader& HeaderPart,
223 const ASDCP::WriterInfo& Info, ASDCP::FrameBuffer& CtFrameBuf, ui32_t& FramesWritten,
224 ui64_t & StreamOffset, const ASDCP::FrameBuffer& FrameBuf, const byte_t* EssenceUL,
225 AESEncContext* Ctx, HMACContext* HMAC)
227 Result_t result = RESULT_OK;
228 IntegrityPack IntPack;
230 byte_t overhead[128];
231 Kumu::MemIOWriter Overhead(overhead, 128);
233 if ( FrameBuf.Size() == 0 )
235 DefaultLogSink().Error("Cannot write empty frame buffer\n");
236 return RESULT_EMPTY_FB;
239 if ( Info.EncryptedEssence )
242 return RESULT_CRYPT_CTX;
244 if ( Info.UsesHMAC && ! HMAC )
245 return RESULT_HMAC_CTX;
247 if ( FrameBuf.PlaintextOffset() > FrameBuf.Size() )
248 return RESULT_LARGE_PTO;
250 // encrypt the essence data (create encrypted source value)
251 result = EncryptFrameBuffer(FrameBuf, CtFrameBuf, Ctx);
254 if ( ASDCP_SUCCESS(result) && Info.UsesHMAC )
255 result = IntPack.CalcValues(CtFrameBuf, Info.AssetUUID, FramesWritten + 1, HMAC);
257 if ( ASDCP_SUCCESS(result) )
259 Overhead.WriteRaw(Dict.ul(MDD_CryptEssence), SMPTE_UL_LENGTH);
261 // construct encrypted triplet header
262 ui32_t ETLength = klv_cryptinfo_size + CtFrameBuf.Size();
263 ui32_t BER_length = MXF_BER_LENGTH;
266 ETLength += klv_intpack_size;
268 ETLength += (MXF_BER_LENGTH * 3); // for empty intpack
270 if ( ETLength > 0x00ffffff ) // Need BER integer longer than MXF_BER_LENGTH bytes
272 BER_length = Kumu::get_BER_length_for_value(ETLength);
274 // the packet is longer by the difference in expected vs. actual BER length
275 ETLength += BER_length - MXF_BER_LENGTH;
277 if ( BER_length == 0 )
278 result = RESULT_KLV_CODING;
281 if ( ASDCP_SUCCESS(result) )
283 if ( ! ( Overhead.WriteBER(ETLength, BER_length) // write encrypted triplet length
284 && Overhead.WriteBER(UUIDlen, MXF_BER_LENGTH) // write ContextID length
285 && Overhead.WriteRaw(Info.ContextID, UUIDlen) // write ContextID
286 && Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH) // write PlaintextOffset length
287 && Overhead.WriteUi64BE(FrameBuf.PlaintextOffset()) // write PlaintextOffset
288 && Overhead.WriteBER(SMPTE_UL_LENGTH, MXF_BER_LENGTH) // write essence UL length
289 && Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH) // write the essence UL
290 && Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH) // write SourceLength length
291 && Overhead.WriteUi64BE(FrameBuf.Size()) // write SourceLength
292 && Overhead.WriteBER(CtFrameBuf.Size(), BER_length) ) ) // write ESV length
294 result = RESULT_KLV_CODING;
298 if ( ASDCP_SUCCESS(result) )
299 result = File.Writev(Overhead.Data(), Overhead.Length());
302 if ( ASDCP_SUCCESS(result) )
304 StreamOffset += Overhead.Length();
305 // write encrypted source value
306 result = File.Writev((byte_t*)CtFrameBuf.RoData(), CtFrameBuf.Size());
309 if ( ASDCP_SUCCESS(result) )
311 StreamOffset += CtFrameBuf.Size();
313 byte_t hmoverhead[512];
314 Kumu::MemIOWriter HMACOverhead(hmoverhead, 512);
319 HMACOverhead.WriteRaw(IntPack.Data, klv_intpack_size);
322 { // we still need the var-pack length values if the intpack is empty
323 for ( ui32_t i = 0; i < 3 ; i++ )
324 HMACOverhead.WriteBER(0, MXF_BER_LENGTH);
328 result = File.Writev(HMACOverhead.Data(), HMACOverhead.Length());
329 StreamOffset += HMACOverhead.Length();
334 ui32_t BER_length = MXF_BER_LENGTH;
336 if ( FrameBuf.Size() > 0x00ffffff ) // Need BER integer longer than MXF_BER_LENGTH bytes
338 BER_length = Kumu::get_BER_length_for_value(FrameBuf.Size());
340 if ( BER_length == 0 )
341 result = RESULT_KLV_CODING;
344 Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH);
345 Overhead.WriteBER(FrameBuf.Size(), BER_length);
347 if ( ASDCP_SUCCESS(result) )
348 result = File.Writev(Overhead.Data(), Overhead.Length());
350 if ( ASDCP_SUCCESS(result) )
351 result = File.Writev((byte_t*)FrameBuf.RoData(), FrameBuf.Size());
353 if ( ASDCP_SUCCESS(result) )
354 StreamOffset += Overhead.Length() + FrameBuf.Size();
357 if ( ASDCP_SUCCESS(result) )
358 result = File.Writev();