2 Copyright (c) 2004-2010, 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(const Dictionary& d) :
40 m_HeaderPart(m_Dict), m_BodyPart(m_Dict), m_FooterPart(m_Dict), m_Dict(&d), m_EssenceStart(0)
44 ASDCP::h__Reader::~h__Reader()
50 ASDCP::h__Reader::Close()
55 //------------------------------------------------------------------------------------------
60 ASDCP::h__Reader::InitInfo()
63 InterchangeObject* Object;
65 m_Info.LabelSetType = LS_MXF_UNKNOWN;
67 if ( m_HeaderPart.OperationalPattern.ExactMatch(MXFInterop_OPAtom_Entry().ul) )
68 m_Info.LabelSetType = LS_MXF_INTEROP;
69 else if ( m_HeaderPart.OperationalPattern.ExactMatch(SMPTE_390_OPAtom_Entry().ul) )
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, *m_Dict);
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 ( ASDCP_SUCCESS(result) )
113 // if this is a three partition file, go to the body
114 // partition and read the partition pack
115 if ( m_HeaderPart.m_RIP.PairArray.size() > 2 )
117 Array<RIP::Pair>::iterator r_i = m_HeaderPart.m_RIP.PairArray.begin();
119 m_File.Seek((*r_i).ByteOffset);
121 result = m_BodyPart.InitFromFile(m_File);
124 m_EssenceStart = m_File.Tell();
131 // standard method of populating the in-memory index
133 ASDCP::h__Reader::InitMXFIndex()
135 if ( ! m_File.IsOpen() )
138 Result_t result = m_File.Seek(m_HeaderPart.FooterPartition);
140 if ( ASDCP_SUCCESS(result) )
142 m_FooterPart.m_Lookup = &m_HeaderPart.m_Primer;
143 result = m_FooterPart.InitFromFile(m_File);
146 if ( ASDCP_SUCCESS(result) )
147 m_File.Seek(m_EssenceStart);
154 ASDCP::KLReader::ReadKLFromFile(Kumu::FileReader& Reader)
157 ui32_t header_length = SMPTE_UL_LENGTH + MXF_BER_LENGTH;
158 Result_t result = Reader.Read(m_KeyBuf, header_length, &read_count);
160 if ( ASDCP_FAILURE(result) )
163 if ( read_count != header_length )
164 return RESULT_READFAIL;
166 const byte_t* ber_start = m_KeyBuf + SMPTE_UL_LENGTH;
168 if ( ( *ber_start & 0x80 ) == 0 )
170 DefaultLogSink().Error("BER encoding error.\n");
171 return RESULT_FORMAT;
174 ui8_t ber_size = ( *ber_start & 0x0f ) + 1;
178 DefaultLogSink().Error("BER size encoding error.\n");
179 return RESULT_FORMAT;
182 if ( ber_size < MXF_BER_LENGTH )
184 DefaultLogSink().Error("BER size %d shorter than AS-DCP minimum %d.\n",
185 ber_size, MXF_BER_LENGTH);
186 return RESULT_FORMAT;
189 if ( ber_size > MXF_BER_LENGTH )
191 ui32_t diff = ber_size - MXF_BER_LENGTH;
192 assert((SMPTE_UL_LENGTH + MXF_BER_LENGTH + diff) <= (SMPTE_UL_LENGTH * 2));
193 result = Reader.Read(m_KeyBuf + SMPTE_UL_LENGTH + MXF_BER_LENGTH, diff, &read_count);
195 if ( ASDCP_FAILURE(result) )
198 if ( read_count != diff )
199 return RESULT_READFAIL;
201 header_length += diff;
204 return InitFromBuffer(m_KeyBuf, header_length);
207 // standard method of reading a plaintext or encrypted frame
209 ASDCP::h__Reader::ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
210 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
212 // look up frame index node
213 IndexTableSegment::IndexEntry TmpEntry;
215 if ( ASDCP_FAILURE(m_FooterPart.Lookup(FrameNum, TmpEntry)) )
217 DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
221 // get frame position and go read the frame's key and length
222 Kumu::fpos_t FilePosition = m_EssenceStart + TmpEntry.StreamOffset;
223 Result_t result = RESULT_OK;
225 if ( FilePosition != m_LastPosition )
227 m_LastPosition = FilePosition;
228 result = m_File.Seek(FilePosition);
231 if( ASDCP_SUCCESS(result) )
232 result = ReadEKLVPacket(FrameNum, FrameNum + 1, FrameBuf, EssenceUL, Ctx, HMAC);
239 ASDCP::h__Reader::ReadEKLVPacket(ui32_t FrameNum, ui32_t SequenceNum, ASDCP::FrameBuffer& FrameBuf,
240 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
243 Result_t result = Reader.ReadKLFromFile(m_File);
245 if ( ASDCP_FAILURE(result) )
248 UL Key(Reader.Key());
249 ui64_t PacketLength = Reader.Length();
250 m_LastPosition = m_LastPosition + Reader.KLLength() + PacketLength;
253 if ( memcmp(Key.Value(), m_Dict->ul(MDD_CryptEssence), Key.Size() - 1) == 0 ) // ignore the stream numbers
255 if ( ! m_Info.EncryptedEssence )
257 DefaultLogSink().Error("EKLV packet found, no Cryptographic Context in header.\n");
258 return RESULT_FORMAT;
261 // read encrypted triplet value into internal buffer
262 assert(PacketLength <= 0xFFFFFFFFL);
263 m_CtFrameBuf.Capacity((ui32_t) PacketLength);
265 result = m_File.Read(m_CtFrameBuf.Data(), (ui32_t) PacketLength,
268 if ( ASDCP_FAILURE(result) )
271 if ( read_count != PacketLength )
273 DefaultLogSink().Error("read length is smaller than EKLV packet length.\n");
274 return RESULT_FORMAT;
277 m_CtFrameBuf.Size((ui32_t) PacketLength);
279 // should be const but mxflib::ReadBER is not
280 byte_t* ess_p = m_CtFrameBuf.Data();
282 // read context ID length
283 if ( ! Kumu::read_test_BER(&ess_p, UUIDlen) )
284 return RESULT_FORMAT;
286 // test the context ID
287 if ( memcmp(ess_p, m_Info.ContextID, UUIDlen) != 0 )
289 DefaultLogSink().Error("Packet's Cryptographic Context ID does not match the header.\n");
290 return RESULT_FORMAT;
294 // read PlaintextOffset length
295 if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
296 return RESULT_FORMAT;
298 ui32_t PlaintextOffset = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
299 ess_p += sizeof(ui64_t);
301 // read essence UL length
302 if ( ! Kumu::read_test_BER(&ess_p, SMPTE_UL_LENGTH) )
303 return RESULT_FORMAT;
306 if ( memcmp(ess_p, EssenceUL, SMPTE_UL_LENGTH - 1) != 0 ) // ignore the stream number
308 char strbuf[IntBufferLen];
309 const MDDEntry* Entry = m_Dict->FindUL(Key.Value());
311 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
313 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
314 return RESULT_FORMAT;
316 ess_p += SMPTE_UL_LENGTH;
318 // read SourceLength length
319 if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
320 return RESULT_FORMAT;
322 ui32_t SourceLength = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
323 ess_p += sizeof(ui64_t);
324 assert(SourceLength);
326 if ( FrameBuf.Capacity() < SourceLength )
328 DefaultLogSink().Error("FrameBuf.Capacity: %u SourceLength: %u\n", FrameBuf.Capacity(), SourceLength);
329 return RESULT_SMALLBUF;
332 ui32_t esv_length = calc_esv_length(SourceLength, PlaintextOffset);
335 if ( ! Kumu::read_test_BER(&ess_p, esv_length) )
337 DefaultLogSink().Error("read_test_BER did not return %u\n", esv_length);
338 return RESULT_FORMAT;
341 ui32_t tmp_len = esv_length + (m_Info.UsesHMAC ? klv_intpack_size : 0);
343 if ( PacketLength < tmp_len )
345 DefaultLogSink().Error("Frame length is larger than EKLV packet length.\n");
346 return RESULT_FORMAT;
351 // wrap the pointer and length as a FrameBuffer for use by
352 // DecryptFrameBuffer() and TestValues()
353 FrameBuffer TmpWrapper;
354 TmpWrapper.SetData(ess_p, tmp_len);
355 TmpWrapper.Size(tmp_len);
356 TmpWrapper.SourceLength(SourceLength);
357 TmpWrapper.PlaintextOffset(PlaintextOffset);
359 result = DecryptFrameBuffer(TmpWrapper, FrameBuf, Ctx);
360 FrameBuf.FrameNumber(FrameNum);
362 // detect and test integrity pack
363 if ( ASDCP_SUCCESS(result) && m_Info.UsesHMAC && HMAC )
365 IntegrityPack IntPack;
366 result = IntPack.TestValues(TmpWrapper, m_Info.AssetUUID, SequenceNum, HMAC);
369 else // return ciphertext to caller
371 if ( FrameBuf.Capacity() < tmp_len )
373 char intbuf[IntBufferLen];
374 DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %s\n",
375 FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
376 return RESULT_SMALLBUF;
379 memcpy(FrameBuf.Data(), ess_p, tmp_len);
380 FrameBuf.Size(tmp_len);
381 FrameBuf.FrameNumber(FrameNum);
382 FrameBuf.SourceLength(SourceLength);
383 FrameBuf.PlaintextOffset(PlaintextOffset);
386 else if ( memcmp(Key.Value(), EssenceUL, Key.Size() - 1) == 0 ) // ignore the stream number
387 { // read plaintext frame
388 if ( FrameBuf.Capacity() < PacketLength )
390 char intbuf[IntBufferLen];
391 DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %s\n",
392 FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
393 return RESULT_SMALLBUF;
396 // read the data into the supplied buffer
398 assert(PacketLength <= 0xFFFFFFFFL);
399 result = m_File.Read(FrameBuf.Data(), (ui32_t) PacketLength, &read_count);
401 if ( ASDCP_FAILURE(result) )
404 if ( read_count != PacketLength )
406 char intbuf1[IntBufferLen];
407 char intbuf2[IntBufferLen];
408 DefaultLogSink().Error("read_count: %s != FrameLength: %s\n",
409 ui64sz(read_count, intbuf1),
410 ui64sz(PacketLength, intbuf2) );
412 return RESULT_READFAIL;
415 FrameBuf.FrameNumber(FrameNum);
416 FrameBuf.Size(read_count);
420 char strbuf[IntBufferLen];
421 const MDDEntry* Entry = m_Dict->FindUL(Key.Value());
423 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
425 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
426 return RESULT_FORMAT;