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;
66 UL OPAtomUL(SMPTE_390_OPAtom_Entry().ul);
67 UL Interop_OPAtomUL(MXFInterop_OPAtom_Entry().ul);
69 if ( m_HeaderPart.OperationalPattern == Interop_OPAtomUL )
70 m_Info.LabelSetType = LS_MXF_INTEROP;
71 else if ( m_HeaderPart.OperationalPattern == OPAtomUL )
72 m_Info.LabelSetType = LS_MXF_SMPTE;
75 Result_t result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object);
77 if( ASDCP_SUCCESS(result) )
78 MD_to_WriterInfo((Identification*)Object, m_Info);
81 if( ASDCP_SUCCESS(result) )
82 result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object);
84 if( ASDCP_SUCCESS(result) )
86 SourcePackage* SP = (SourcePackage*)Object;
87 memcpy(m_Info.AssetUUID, SP->PackageUID.Value() + 16, UUIDlen);
90 // optional CryptographicContext
91 if( ASDCP_SUCCESS(result) )
93 Result_t cr_result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(CryptographicContext), &Object);
95 if( ASDCP_SUCCESS(cr_result) )
96 MD_to_CryptoInfo((CryptographicContext*)Object, m_Info, *m_Dict);
103 // standard method of opening an MXF file for read
105 ASDCP::h__Reader::OpenMXFRead(const char* filename)
108 Result_t result = m_File.OpenRead(filename);
110 if ( ASDCP_SUCCESS(result) )
111 result = m_HeaderPart.InitFromFile(m_File);
113 if ( ASDCP_SUCCESS(result) )
115 // if this is a three partition file, go to the body
116 // partition and read the partition pack
117 if ( m_HeaderPart.m_RIP.PairArray.size() > 2 )
119 Array<RIP::Pair>::iterator r_i = m_HeaderPart.m_RIP.PairArray.begin();
121 m_File.Seek((*r_i).ByteOffset);
123 result = m_BodyPart.InitFromFile(m_File);
126 m_EssenceStart = m_File.Tell();
133 // standard method of populating the in-memory index
135 ASDCP::h__Reader::InitMXFIndex()
137 if ( ! m_File.IsOpen() )
140 Result_t result = m_File.Seek(m_HeaderPart.FooterPartition);
142 if ( ASDCP_SUCCESS(result) )
144 m_FooterPart.m_Lookup = &m_HeaderPart.m_Primer;
145 result = m_FooterPart.InitFromFile(m_File);
148 if ( ASDCP_SUCCESS(result) )
149 m_File.Seek(m_EssenceStart);
156 ASDCP::KLReader::ReadKLFromFile(Kumu::FileReader& Reader)
159 ui32_t header_length = SMPTE_UL_LENGTH + MXF_BER_LENGTH;
160 Result_t result = Reader.Read(m_KeyBuf, header_length, &read_count);
162 if ( ASDCP_FAILURE(result) )
165 if ( read_count != header_length )
166 return RESULT_READFAIL;
168 const byte_t* ber_start = m_KeyBuf + SMPTE_UL_LENGTH;
170 if ( ( *ber_start & 0x80 ) == 0 )
172 DefaultLogSink().Error("BER encoding error.\n");
173 return RESULT_FORMAT;
176 ui8_t ber_size = ( *ber_start & 0x0f ) + 1;
180 DefaultLogSink().Error("BER size encoding error.\n");
181 return RESULT_FORMAT;
184 if ( ber_size < MXF_BER_LENGTH )
186 DefaultLogSink().Error("BER size %d shorter than AS-DCP minimum %d.\n",
187 ber_size, MXF_BER_LENGTH);
188 return RESULT_FORMAT;
191 if ( ber_size > MXF_BER_LENGTH )
193 ui32_t diff = ber_size - MXF_BER_LENGTH;
194 assert((SMPTE_UL_LENGTH + MXF_BER_LENGTH + diff) <= (SMPTE_UL_LENGTH * 2));
195 result = Reader.Read(m_KeyBuf + SMPTE_UL_LENGTH + MXF_BER_LENGTH, diff, &read_count);
197 if ( ASDCP_FAILURE(result) )
200 if ( read_count != diff )
201 return RESULT_READFAIL;
203 header_length += diff;
206 return InitFromBuffer(m_KeyBuf, header_length);
209 // standard method of reading a plaintext or encrypted frame
211 ASDCP::h__Reader::ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
212 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
214 // look up frame index node
215 IndexTableSegment::IndexEntry TmpEntry;
217 if ( ASDCP_FAILURE(m_FooterPart.Lookup(FrameNum, TmpEntry)) )
219 DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
223 // get frame position and go read the frame's key and length
224 Kumu::fpos_t FilePosition = m_EssenceStart + TmpEntry.StreamOffset;
225 Result_t result = RESULT_OK;
227 if ( FilePosition != m_LastPosition )
229 m_LastPosition = FilePosition;
230 result = m_File.Seek(FilePosition);
233 if( ASDCP_SUCCESS(result) )
234 result = ReadEKLVPacket(FrameNum, FrameNum + 1, FrameBuf, EssenceUL, Ctx, HMAC);
241 ASDCP::h__Reader::ReadEKLVPacket(ui32_t FrameNum, ui32_t SequenceNum, ASDCP::FrameBuffer& FrameBuf,
242 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
245 Result_t result = Reader.ReadKLFromFile(m_File);
247 if ( ASDCP_FAILURE(result) )
250 UL Key(Reader.Key());
251 ui64_t PacketLength = Reader.Length();
252 m_LastPosition = m_LastPosition + Reader.KLLength() + PacketLength;
255 if ( memcmp(Key.Value(), m_Dict->ul(MDD_CryptEssence), Key.Size() - 1) == 0 ) // ignore the stream numbers
257 if ( ! m_Info.EncryptedEssence )
259 DefaultLogSink().Error("EKLV packet found, no Cryptographic Context in header.\n");
260 return RESULT_FORMAT;
263 // read encrypted triplet value into internal buffer
264 assert(PacketLength <= 0xFFFFFFFFL);
265 m_CtFrameBuf.Capacity((ui32_t) PacketLength);
267 result = m_File.Read(m_CtFrameBuf.Data(), (ui32_t) PacketLength,
270 if ( ASDCP_FAILURE(result) )
273 if ( read_count != PacketLength )
275 DefaultLogSink().Error("read length is smaller than EKLV packet length.\n");
276 return RESULT_FORMAT;
279 m_CtFrameBuf.Size((ui32_t) PacketLength);
281 // should be const but mxflib::ReadBER is not
282 byte_t* ess_p = m_CtFrameBuf.Data();
284 // read context ID length
285 if ( ! Kumu::read_test_BER(&ess_p, UUIDlen) )
286 return RESULT_FORMAT;
288 // test the context ID
289 if ( memcmp(ess_p, m_Info.ContextID, UUIDlen) != 0 )
291 DefaultLogSink().Error("Packet's Cryptographic Context ID does not match the header.\n");
292 return RESULT_FORMAT;
296 // read PlaintextOffset length
297 if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
298 return RESULT_FORMAT;
300 ui32_t PlaintextOffset = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
301 ess_p += sizeof(ui64_t);
303 // read essence UL length
304 if ( ! Kumu::read_test_BER(&ess_p, SMPTE_UL_LENGTH) )
305 return RESULT_FORMAT;
308 if ( memcmp(ess_p, EssenceUL, SMPTE_UL_LENGTH - 1) != 0 ) // ignore the stream number
310 char strbuf[IntBufferLen];
311 const MDDEntry* Entry = m_Dict->FindUL(Key.Value());
313 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
315 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
316 return RESULT_FORMAT;
318 ess_p += SMPTE_UL_LENGTH;
320 // read SourceLength length
321 if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
322 return RESULT_FORMAT;
324 ui32_t SourceLength = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
325 ess_p += sizeof(ui64_t);
326 assert(SourceLength);
328 if ( FrameBuf.Capacity() < SourceLength )
330 DefaultLogSink().Error("FrameBuf.Capacity: %u SourceLength: %u\n", FrameBuf.Capacity(), SourceLength);
331 return RESULT_SMALLBUF;
334 ui32_t esv_length = calc_esv_length(SourceLength, PlaintextOffset);
337 if ( ! Kumu::read_test_BER(&ess_p, esv_length) )
339 DefaultLogSink().Error("read_test_BER did not return %u\n", esv_length);
340 return RESULT_FORMAT;
343 ui32_t tmp_len = esv_length + (m_Info.UsesHMAC ? klv_intpack_size : 0);
345 if ( PacketLength < tmp_len )
347 DefaultLogSink().Error("Frame length is larger than EKLV packet length.\n");
348 return RESULT_FORMAT;
353 // wrap the pointer and length as a FrameBuffer for use by
354 // DecryptFrameBuffer() and TestValues()
355 FrameBuffer TmpWrapper;
356 TmpWrapper.SetData(ess_p, tmp_len);
357 TmpWrapper.Size(tmp_len);
358 TmpWrapper.SourceLength(SourceLength);
359 TmpWrapper.PlaintextOffset(PlaintextOffset);
361 result = DecryptFrameBuffer(TmpWrapper, FrameBuf, Ctx);
362 FrameBuf.FrameNumber(FrameNum);
364 // detect and test integrity pack
365 if ( ASDCP_SUCCESS(result) && m_Info.UsesHMAC && HMAC )
367 IntegrityPack IntPack;
368 result = IntPack.TestValues(TmpWrapper, m_Info.AssetUUID, SequenceNum, HMAC);
371 else // return ciphertext to caller
373 if ( FrameBuf.Capacity() < tmp_len )
375 char intbuf[IntBufferLen];
376 DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %s\n",
377 FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
378 return RESULT_SMALLBUF;
381 memcpy(FrameBuf.Data(), ess_p, tmp_len);
382 FrameBuf.Size(tmp_len);
383 FrameBuf.FrameNumber(FrameNum);
384 FrameBuf.SourceLength(SourceLength);
385 FrameBuf.PlaintextOffset(PlaintextOffset);
388 else if ( memcmp(Key.Value(), EssenceUL, Key.Size() - 1) == 0 ) // ignore the stream number
389 { // read plaintext frame
390 if ( FrameBuf.Capacity() < PacketLength )
392 char intbuf[IntBufferLen];
393 DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %s\n",
394 FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
395 return RESULT_SMALLBUF;
398 // read the data into the supplied buffer
400 assert(PacketLength <= 0xFFFFFFFFL);
401 result = m_File.Read(FrameBuf.Data(), (ui32_t) PacketLength, &read_count);
403 if ( ASDCP_FAILURE(result) )
406 if ( read_count != PacketLength )
408 char intbuf1[IntBufferLen];
409 char intbuf2[IntBufferLen];
410 DefaultLogSink().Error("read_count: %s != FrameLength: %s\n",
411 ui64sz(read_count, intbuf1),
412 ui64sz(PacketLength, intbuf2) );
414 return RESULT_READFAIL;
417 FrameBuf.FrameNumber(FrameNum);
418 FrameBuf.Size(read_count);
422 char strbuf[IntBufferLen];
423 const MDDEntry* Entry = m_Dict->FindUL(Key.Value());
425 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
427 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
428 return RESULT_FORMAT;