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__Reader.cpp
29 \brief MXF file reader base class
32 #include "AS_DCP_internal.h"
35 using namespace ASDCP;
36 using namespace ASDCP::MXF;
39 ASDCP::h__Reader::h__Reader() : m_EssenceStart(0)
43 ASDCP::h__Reader::~h__Reader()
49 ASDCP::h__Reader::Close()
54 //------------------------------------------------------------------------------------------
59 ASDCP::h__Reader::InitInfo()
61 InterchangeObject* Object;
63 m_Info.LabelSetType = LS_MXF_UNKNOWN;
64 UL OPAtomUL(Dict::ul(MDD_OPAtom));
65 UL Interop_OPAtomUL(Dict::ul(MDD_MXFInterop_OPAtom));
67 if ( m_HeaderPart.OperationalPattern == Interop_OPAtomUL )
68 m_Info.LabelSetType = LS_MXF_INTEROP;
69 else if ( m_HeaderPart.OperationalPattern == OPAtomUL )
70 m_Info.LabelSetType = LS_MXF_SMPTE;
73 Result_t result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object);
75 if( ASDCP_SUCCESS(result) )
76 MD_to_WriterInfo((Identification*)Object, m_Info);
79 if( ASDCP_SUCCESS(result) )
80 result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object);
82 if( ASDCP_SUCCESS(result) )
84 SourcePackage* SP = (SourcePackage*)Object;
85 memcpy(m_Info.AssetUUID, SP->PackageUID.Value() + 16, UUIDlen);
88 // optional CryptographicContext
89 if( ASDCP_SUCCESS(result) )
91 Result_t cr_result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(CryptographicContext), &Object);
93 if( ASDCP_SUCCESS(cr_result) )
94 MD_to_CryptoInfo((CryptographicContext*)Object, m_Info);
101 // standard method of opening an MXF file for read
103 ASDCP::h__Reader::OpenMXFRead(const char* filename)
106 Result_t result = m_File.OpenRead(filename);
108 if ( ASDCP_SUCCESS(result) )
109 result = m_HeaderPart.InitFromFile(m_File);
111 // if this is a three partition file, go to the body
112 // partition and read off the partition pack
113 if ( m_HeaderPart.m_RIP.PairArray.size() == 3 )
115 Array<RIP::Pair>::iterator r_i = m_HeaderPart.m_RIP.PairArray.begin();
117 m_File.Seek((*r_i).ByteOffset);
119 result = m_BodyPart.InitFromFile(m_File);
122 m_EssenceStart = m_File.Tell();
127 // standard method of populating the in-memory index
129 ASDCP::h__Reader::InitMXFIndex()
131 if ( ! m_File.IsOpen() )
134 Result_t result = m_File.Seek(m_HeaderPart.FooterPartition);
136 if ( ASDCP_SUCCESS(result) )
138 m_FooterPart.m_Lookup = &m_HeaderPart.m_Primer;
139 result = m_FooterPart.InitFromFile(m_File);
142 if ( ASDCP_SUCCESS(result) )
143 m_File.Seek(m_EssenceStart);
149 class KLReader : public ASDCP::KLVPacket
151 ASDCP_NO_COPY_CONSTRUCT(KLReader);
158 inline const byte_t* Key() { return m_KeyBuf; }
159 inline const ui64_t Length() { return m_ValueLength; }
160 inline const ui64_t KLLength() { return m_KLLength; }
162 Result_t ReadKLFromFile(Kumu::FileReader& Reader)
165 ui32_t header_length = SMPTE_UL_LENGTH + MXF_BER_LENGTH;
166 Result_t result = Reader.Read(m_KeyBuf, header_length, &read_count);
168 if ( read_count != header_length )
169 return RESULT_READFAIL;
171 if ( ASDCP_SUCCESS(result) )
172 result = InitFromBuffer(m_KeyBuf, header_length);
179 // standard method of reading a plaintext or encrypted frame
181 ASDCP::h__Reader::ReadEKLVPacket(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
182 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
184 // look up frame index node
185 IndexTableSegment::IndexEntry TmpEntry;
187 if ( ASDCP_FAILURE(m_FooterPart.Lookup(FrameNum, TmpEntry)) )
189 DefaultLogSink().Error("Frame value out of range: %lu\n", FrameNum);
193 // get frame position and go read the frame's key and length
194 Result_t result = RESULT_OK;
196 Kumu::fpos_t FilePosition = m_EssenceStart + TmpEntry.StreamOffset;
198 if ( FilePosition != m_LastPosition )
200 m_LastPosition = FilePosition;
201 result = m_File.Seek(FilePosition);
204 if ( ASDCP_SUCCESS(result) )
205 result = Reader.ReadKLFromFile(m_File);
207 if ( ASDCP_FAILURE(result) )
210 UL Key(Reader.Key());
211 ui64_t PacketLength = Reader.Length();
212 m_LastPosition = m_LastPosition + Reader.KLLength() + PacketLength;
214 if ( memcmp(Key.Value(), Dict::ul(MDD_CryptEssence), Key.Size() - 1) == 0 // ignore the stream numbers
215 || memcmp(Key.Value(), Dict::ul(MDD_MXFInterop_CryptEssence), Key.Size() - 1) == 0 )
217 if ( ! m_Info.EncryptedEssence )
219 DefaultLogSink().Error("EKLV packet found, no Cryptographic Context in header.\n");
220 return RESULT_FORMAT;
223 // read encrypted triplet value into internal buffer
224 m_CtFrameBuf.Capacity(PacketLength);
226 result = m_File.Read(m_CtFrameBuf.Data(), PacketLength, &read_count);
228 if ( ASDCP_FAILURE(result) )
231 if ( read_count != PacketLength )
233 DefaultLogSink().Error("read length is smaller than EKLV packet length.\n");
234 return RESULT_FORMAT;
237 m_CtFrameBuf.Size(PacketLength);
239 // should be const but mxflib::ReadBER is not
240 byte_t* ess_p = m_CtFrameBuf.Data();
242 // read context ID length
243 if ( ! Kumu::read_test_BER(&ess_p, UUIDlen) )
244 return RESULT_FORMAT;
246 // test the context ID
247 if ( memcmp(ess_p, m_Info.ContextID, UUIDlen) != 0 )
249 DefaultLogSink().Error("Packet's Cryptographic Context ID does not match the header.\n");
250 return RESULT_FORMAT;
254 // read PlaintextOffset length
255 if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
256 return RESULT_FORMAT;
258 ui32_t PlaintextOffset = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
259 ess_p += sizeof(ui64_t);
261 // read essence UL length
262 if ( ! Kumu::read_test_BER(&ess_p, SMPTE_UL_LENGTH) )
263 return RESULT_FORMAT;
266 if ( memcmp(ess_p, EssenceUL, SMPTE_UL_LENGTH - 1) != 0 ) // ignore the stream number
268 char strbuf[IntBufferLen];
269 const MDDEntry* Entry = Dict::FindUL(Key.Value());
271 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
273 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
274 return RESULT_FORMAT;
276 ess_p += SMPTE_UL_LENGTH;
278 // read SourceLength length
279 if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
280 return RESULT_FORMAT;
282 ui32_t SourceLength = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
283 ess_p += sizeof(ui64_t);
284 assert(SourceLength);
286 if ( FrameBuf.Capacity() < SourceLength )
288 DefaultLogSink().Error("FrameBuf.Capacity: %lu SourceLength: %lu\n", FrameBuf.Capacity(), SourceLength);
289 return RESULT_SMALLBUF;
292 ui32_t esv_length = calc_esv_length(SourceLength, PlaintextOffset);
295 if ( ! Kumu::read_test_BER(&ess_p, esv_length) )
297 DefaultLogSink().Error("read_test_BER did not return %lu\n", esv_length);
298 return RESULT_FORMAT;
301 ui32_t tmp_len = esv_length + (m_Info.UsesHMAC ? klv_intpack_size : 0);
303 if ( PacketLength < tmp_len )
305 DefaultLogSink().Error("Frame length is larger than EKLV packet length.\n");
306 return RESULT_FORMAT;
311 // wrap the pointer and length as a FrameBuffer for use by
312 // DecryptFrameBuffer() and TestValues()
313 FrameBuffer TmpWrapper;
314 TmpWrapper.SetData(ess_p, tmp_len);
315 TmpWrapper.Size(tmp_len);
316 TmpWrapper.SourceLength(SourceLength);
317 TmpWrapper.PlaintextOffset(PlaintextOffset);
319 result = DecryptFrameBuffer(TmpWrapper, FrameBuf, Ctx);
320 FrameBuf.FrameNumber(FrameNum);
322 // detect and test integrity pack
323 if ( ASDCP_SUCCESS(result) && m_Info.UsesHMAC && HMAC )
325 IntegrityPack IntPack;
326 result = IntPack.TestValues(TmpWrapper, m_Info.AssetUUID, FrameNum + 1, HMAC);
329 else // return ciphertext to caller
331 if ( FrameBuf.Capacity() < tmp_len )
332 return RESULT_SMALLBUF;
334 memcpy(FrameBuf.Data(), ess_p, tmp_len);
335 FrameBuf.Size(tmp_len);
336 FrameBuf.SourceLength(SourceLength);
337 FrameBuf.PlaintextOffset(PlaintextOffset);
340 else if ( memcmp(Key.Value(), EssenceUL, Key.Size() - 1) == 0 ) // ignore the stream number
341 { // read plaintext frame
342 if ( FrameBuf.Capacity() < PacketLength )
344 char intbuf[IntBufferLen];
345 DefaultLogSink().Error("FrameBuf.Capacity: %lu FrameLength: %s\n",
346 FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
347 return RESULT_SMALLBUF;
350 // read the data into the supplied buffer
352 result = m_File.Read(FrameBuf.Data(), PacketLength, &read_count);
354 if ( ASDCP_FAILURE(result) )
357 if ( read_count != PacketLength )
359 char intbuf1[IntBufferLen];
360 char intbuf2[IntBufferLen];
361 DefaultLogSink().Error("read_count: %s != FrameLength: %s\n",
362 ui64sz(read_count, intbuf1),
363 ui64sz(PacketLength, intbuf2) );
365 return RESULT_READFAIL;
368 FrameBuf.FrameNumber(FrameNum);
369 FrameBuf.Size(read_count);
373 char strbuf[IntBufferLen];
374 const MDDEntry* Entry = Dict::FindUL(Key.Value());
376 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
378 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
379 return RESULT_FORMAT;