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 // standard method of writing the header and footer of a completed MXF file
165 ASDCP::h__ASDCPWriter::WriteASDCPFooter()
167 // update all Duration properties
168 DurationElementList_t::iterator dli = m_DurationUpdateList.begin();
170 for (; dli != m_DurationUpdateList.end(); ++dli )
172 **dli = m_FramesWritten;
175 m_EssenceDescriptor->ContainerDuration = m_FramesWritten;
176 m_FooterPart.PreviousPartition = m_RIP.PairArray.back().ByteOffset;
178 Kumu::fpos_t here = m_File.Tell();
179 m_RIP.PairArray.push_back(RIP::Pair(0, here)); // Last RIP Entry
180 m_HeaderPart.FooterPartition = here;
183 // re-label the header partition, set the footer
184 UL OPAtomUL(m_Dict->ul(MDD_OPAtom));
185 m_HeaderPart.OperationalPattern = OPAtomUL;
186 m_HeaderPart.m_Preface->OperationalPattern = OPAtomUL;
187 m_FooterPart.OperationalPattern = OPAtomUL;
189 m_FooterPart.EssenceContainers = m_HeaderPart.EssenceContainers;
190 m_FooterPart.FooterPartition = here;
191 m_FooterPart.ThisPartition = here;
193 Result_t result = m_FooterPart.WriteToFile(m_File, m_FramesWritten);
195 if ( ASDCP_SUCCESS(result) )
196 result = m_RIP.WriteToFile(m_File);
198 if ( ASDCP_SUCCESS(result) )
199 result = m_File.Seek(0);
201 if ( ASDCP_SUCCESS(result) )
202 result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
209 //------------------------------------------------------------------------------------------
213 // standard method of writing a plaintext or encrypted frame
215 ASDCP::Write_EKLV_Packet(Kumu::FileWriter& File, const ASDCP::Dictionary& Dict, const MXF::OP1aHeader& HeaderPart,
216 const ASDCP::WriterInfo& Info, ASDCP::FrameBuffer& CtFrameBuf, ui32_t& FramesWritten,
217 ui64_t & StreamOffset, const ASDCP::FrameBuffer& FrameBuf, const byte_t* EssenceUL,
218 AESEncContext* Ctx, HMACContext* HMAC)
220 Result_t result = RESULT_OK;
221 IntegrityPack IntPack;
223 byte_t overhead[128];
224 Kumu::MemIOWriter Overhead(overhead, 128);
226 if ( FrameBuf.Size() == 0 )
228 DefaultLogSink().Error("Cannot write empty frame buffer\n");
229 return RESULT_EMPTY_FB;
232 if ( Info.EncryptedEssence )
235 return RESULT_CRYPT_CTX;
237 if ( Info.UsesHMAC && ! HMAC )
238 return RESULT_HMAC_CTX;
240 if ( FrameBuf.PlaintextOffset() > FrameBuf.Size() )
241 return RESULT_LARGE_PTO;
243 // encrypt the essence data (create encrypted source value)
244 result = EncryptFrameBuffer(FrameBuf, CtFrameBuf, Ctx);
247 if ( ASDCP_SUCCESS(result) && Info.UsesHMAC )
248 result = IntPack.CalcValues(CtFrameBuf, Info.AssetUUID, FramesWritten + 1, HMAC);
250 if ( ASDCP_SUCCESS(result) )
252 Overhead.WriteRaw(Dict.ul(MDD_CryptEssence), SMPTE_UL_LENGTH);
254 // construct encrypted triplet header
255 ui32_t ETLength = klv_cryptinfo_size + CtFrameBuf.Size();
256 ui32_t BER_length = MXF_BER_LENGTH;
259 ETLength += klv_intpack_size;
261 ETLength += (MXF_BER_LENGTH * 3); // for empty intpack
263 if ( ETLength > 0x00ffffff ) // Need BER integer longer than MXF_BER_LENGTH bytes
265 BER_length = Kumu::get_BER_length_for_value(ETLength);
267 // the packet is longer by the difference in expected vs. actual BER length
268 ETLength += BER_length - MXF_BER_LENGTH;
270 if ( BER_length == 0 )
271 result = RESULT_KLV_CODING;
274 if ( ASDCP_SUCCESS(result) )
276 if ( ! ( Overhead.WriteBER(ETLength, BER_length) // write encrypted triplet length
277 && Overhead.WriteBER(UUIDlen, MXF_BER_LENGTH) // write ContextID length
278 && Overhead.WriteRaw(Info.ContextID, UUIDlen) // write ContextID
279 && Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH) // write PlaintextOffset length
280 && Overhead.WriteUi64BE(FrameBuf.PlaintextOffset()) // write PlaintextOffset
281 && Overhead.WriteBER(SMPTE_UL_LENGTH, MXF_BER_LENGTH) // write essence UL length
282 && Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH) // write the essence UL
283 && Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH) // write SourceLength length
284 && Overhead.WriteUi64BE(FrameBuf.Size()) // write SourceLength
285 && Overhead.WriteBER(CtFrameBuf.Size(), BER_length) ) ) // write ESV length
287 result = RESULT_KLV_CODING;
291 if ( ASDCP_SUCCESS(result) )
292 result = File.Writev(Overhead.Data(), Overhead.Length());
295 if ( ASDCP_SUCCESS(result) )
297 StreamOffset += Overhead.Length();
298 // write encrypted source value
299 result = File.Writev((byte_t*)CtFrameBuf.RoData(), CtFrameBuf.Size());
302 if ( ASDCP_SUCCESS(result) )
304 StreamOffset += CtFrameBuf.Size();
306 byte_t hmoverhead[512];
307 Kumu::MemIOWriter HMACOverhead(hmoverhead, 512);
312 HMACOverhead.WriteRaw(IntPack.Data, klv_intpack_size);
315 { // we still need the var-pack length values if the intpack is empty
316 for ( ui32_t i = 0; i < 3 ; i++ )
317 HMACOverhead.WriteBER(0, MXF_BER_LENGTH);
321 result = File.Writev(HMACOverhead.Data(), HMACOverhead.Length());
322 StreamOffset += HMACOverhead.Length();
327 ui32_t BER_length = MXF_BER_LENGTH;
329 if ( FrameBuf.Size() > 0x00ffffff ) // Need BER integer longer than MXF_BER_LENGTH bytes
331 BER_length = Kumu::get_BER_length_for_value(FrameBuf.Size());
333 if ( BER_length == 0 )
334 result = RESULT_KLV_CODING;
337 Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH);
338 Overhead.WriteBER(FrameBuf.Size(), BER_length);
340 if ( ASDCP_SUCCESS(result) )
341 result = File.Writev(Overhead.Data(), Overhead.Length());
343 if ( ASDCP_SUCCESS(result) )
344 result = File.Writev((byte_t*)FrameBuf.RoData(), FrameBuf.Size());
346 if ( ASDCP_SUCCESS(result) )
347 StreamOffset += Overhead.Length() + FrameBuf.Size();
350 if ( ASDCP_SUCCESS(result) )
351 result = File.Writev();