2 Copyright (c) 2004-2009, 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(m_Dict->ul(MDD_OPAtom));
67 UL Interop_OPAtomUL(m_Dict->ul(MDD_MXFInterop_OPAtom));
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_SUCCESS(result) )
164 if ( read_count != header_length )
165 result = RESULT_READFAIL;
168 result = InitFromBuffer(m_KeyBuf, header_length);
174 // standard method of reading a plaintext or encrypted frame
176 ASDCP::h__Reader::ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
177 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
179 // look up frame index node
180 IndexTableSegment::IndexEntry TmpEntry;
182 if ( ASDCP_FAILURE(m_FooterPart.Lookup(FrameNum, TmpEntry)) )
184 DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
188 // get frame position and go read the frame's key and length
189 Kumu::fpos_t FilePosition = m_EssenceStart + TmpEntry.StreamOffset;
190 Result_t result = RESULT_OK;
192 if ( FilePosition != m_LastPosition )
194 m_LastPosition = FilePosition;
195 result = m_File.Seek(FilePosition);
198 if( ASDCP_SUCCESS(result) )
199 result = ReadEKLVPacket(FrameNum, FrameNum + 1, FrameBuf, EssenceUL, Ctx, HMAC);
206 ASDCP::h__Reader::ReadEKLVPacket(ui32_t FrameNum, ui32_t SequenceNum, ASDCP::FrameBuffer& FrameBuf,
207 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
210 Result_t result = Reader.ReadKLFromFile(m_File);
212 if ( ASDCP_FAILURE(result) )
215 UL Key(Reader.Key());
216 ui64_t PacketLength = Reader.Length();
217 m_LastPosition = m_LastPosition + Reader.KLLength() + PacketLength;
220 if ( memcmp(Key.Value(), m_Dict->ul(MDD_CryptEssence), Key.Size() - 1) == 0 // ignore the stream numbers
221 || memcmp(Key.Value(), m_Dict->ul(MDD_MXFInterop_CryptEssence), Key.Size() - 1) == 0 )
223 if ( ! m_Info.EncryptedEssence )
225 DefaultLogSink().Error("EKLV packet found, no Cryptographic Context in header.\n");
226 return RESULT_FORMAT;
229 // read encrypted triplet value into internal buffer
230 assert(PacketLength <= 0xFFFFFFFFL);
231 m_CtFrameBuf.Capacity((ui32_t) PacketLength);
233 result = m_File.Read(m_CtFrameBuf.Data(), (ui32_t) PacketLength,
236 if ( ASDCP_FAILURE(result) )
239 if ( read_count != PacketLength )
241 DefaultLogSink().Error("read length is smaller than EKLV packet length.\n");
242 return RESULT_FORMAT;
245 m_CtFrameBuf.Size((ui32_t) PacketLength);
247 // should be const but mxflib::ReadBER is not
248 byte_t* ess_p = m_CtFrameBuf.Data();
250 // read context ID length
251 if ( ! Kumu::read_test_BER(&ess_p, UUIDlen) )
252 return RESULT_FORMAT;
254 // test the context ID
255 if ( memcmp(ess_p, m_Info.ContextID, UUIDlen) != 0 )
257 DefaultLogSink().Error("Packet's Cryptographic Context ID does not match the header.\n");
258 return RESULT_FORMAT;
262 // read PlaintextOffset length
263 if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
264 return RESULT_FORMAT;
266 ui32_t PlaintextOffset = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
267 ess_p += sizeof(ui64_t);
269 // read essence UL length
270 if ( ! Kumu::read_test_BER(&ess_p, SMPTE_UL_LENGTH) )
271 return RESULT_FORMAT;
274 if ( memcmp(ess_p, EssenceUL, SMPTE_UL_LENGTH - 1) != 0 ) // ignore the stream number
276 char strbuf[IntBufferLen];
277 const MDDEntry* Entry = m_Dict->FindUL(Key.Value());
279 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
281 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
282 return RESULT_FORMAT;
284 ess_p += SMPTE_UL_LENGTH;
286 // read SourceLength length
287 if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
288 return RESULT_FORMAT;
290 ui32_t SourceLength = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
291 ess_p += sizeof(ui64_t);
292 assert(SourceLength);
294 if ( FrameBuf.Capacity() < SourceLength )
296 DefaultLogSink().Error("FrameBuf.Capacity: %u SourceLength: %u\n", FrameBuf.Capacity(), SourceLength);
297 return RESULT_SMALLBUF;
300 ui32_t esv_length = calc_esv_length(SourceLength, PlaintextOffset);
303 if ( ! Kumu::read_test_BER(&ess_p, esv_length) )
305 DefaultLogSink().Error("read_test_BER did not return %u\n", esv_length);
306 return RESULT_FORMAT;
309 ui32_t tmp_len = esv_length + (m_Info.UsesHMAC ? klv_intpack_size : 0);
311 if ( PacketLength < tmp_len )
313 DefaultLogSink().Error("Frame length is larger than EKLV packet length.\n");
314 return RESULT_FORMAT;
319 // wrap the pointer and length as a FrameBuffer for use by
320 // DecryptFrameBuffer() and TestValues()
321 FrameBuffer TmpWrapper;
322 TmpWrapper.SetData(ess_p, tmp_len);
323 TmpWrapper.Size(tmp_len);
324 TmpWrapper.SourceLength(SourceLength);
325 TmpWrapper.PlaintextOffset(PlaintextOffset);
327 result = DecryptFrameBuffer(TmpWrapper, FrameBuf, Ctx);
328 FrameBuf.FrameNumber(FrameNum);
330 // detect and test integrity pack
331 if ( ASDCP_SUCCESS(result) && m_Info.UsesHMAC && HMAC )
333 IntegrityPack IntPack;
334 result = IntPack.TestValues(TmpWrapper, m_Info.AssetUUID, SequenceNum, HMAC);
337 else // return ciphertext to caller
339 if ( FrameBuf.Capacity() < tmp_len )
341 char intbuf[IntBufferLen];
342 DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %s\n",
343 FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
344 return RESULT_SMALLBUF;
347 memcpy(FrameBuf.Data(), ess_p, tmp_len);
348 FrameBuf.Size(tmp_len);
349 FrameBuf.SourceLength(SourceLength);
350 FrameBuf.PlaintextOffset(PlaintextOffset);
353 else if ( memcmp(Key.Value(), EssenceUL, Key.Size() - 1) == 0 ) // ignore the stream number
354 { // read plaintext frame
355 if ( FrameBuf.Capacity() < PacketLength )
357 char intbuf[IntBufferLen];
358 DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %s\n",
359 FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
360 return RESULT_SMALLBUF;
363 // read the data into the supplied buffer
365 assert(PacketLength <= 0xFFFFFFFFL);
366 result = m_File.Read(FrameBuf.Data(), (ui32_t) PacketLength, &read_count);
368 if ( ASDCP_FAILURE(result) )
371 if ( read_count != PacketLength )
373 char intbuf1[IntBufferLen];
374 char intbuf2[IntBufferLen];
375 DefaultLogSink().Error("read_count: %s != FrameLength: %s\n",
376 ui64sz(read_count, intbuf1),
377 ui64sz(PacketLength, intbuf2) );
379 return RESULT_READFAIL;
382 FrameBuf.FrameNumber(FrameNum);
383 FrameBuf.Size(read_count);
387 char strbuf[IntBufferLen];
388 const MDDEntry* Entry = m_Dict->FindUL(Key.Value());
390 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
392 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
393 return RESULT_FORMAT;