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(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_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
222 if ( ! m_Info.EncryptedEssence )
224 DefaultLogSink().Error("EKLV packet found, no Cryptographic Context in header.\n");
225 return RESULT_FORMAT;
228 // read encrypted triplet value into internal buffer
229 assert(PacketLength <= 0xFFFFFFFFL);
230 m_CtFrameBuf.Capacity((ui32_t) PacketLength);
232 result = m_File.Read(m_CtFrameBuf.Data(), (ui32_t) PacketLength,
235 if ( ASDCP_FAILURE(result) )
238 if ( read_count != PacketLength )
240 DefaultLogSink().Error("read length is smaller than EKLV packet length.\n");
241 return RESULT_FORMAT;
244 m_CtFrameBuf.Size((ui32_t) PacketLength);
246 // should be const but mxflib::ReadBER is not
247 byte_t* ess_p = m_CtFrameBuf.Data();
249 // read context ID length
250 if ( ! Kumu::read_test_BER(&ess_p, UUIDlen) )
251 return RESULT_FORMAT;
253 // test the context ID
254 if ( memcmp(ess_p, m_Info.ContextID, UUIDlen) != 0 )
256 DefaultLogSink().Error("Packet's Cryptographic Context ID does not match the header.\n");
257 return RESULT_FORMAT;
261 // read PlaintextOffset length
262 if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
263 return RESULT_FORMAT;
265 ui32_t PlaintextOffset = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
266 ess_p += sizeof(ui64_t);
268 // read essence UL length
269 if ( ! Kumu::read_test_BER(&ess_p, SMPTE_UL_LENGTH) )
270 return RESULT_FORMAT;
273 if ( memcmp(ess_p, EssenceUL, SMPTE_UL_LENGTH - 1) != 0 ) // ignore the stream number
275 char strbuf[IntBufferLen];
276 const MDDEntry* Entry = m_Dict->FindUL(Key.Value());
278 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
280 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
281 return RESULT_FORMAT;
283 ess_p += SMPTE_UL_LENGTH;
285 // read SourceLength length
286 if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
287 return RESULT_FORMAT;
289 ui32_t SourceLength = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
290 ess_p += sizeof(ui64_t);
291 assert(SourceLength);
293 if ( FrameBuf.Capacity() < SourceLength )
295 DefaultLogSink().Error("FrameBuf.Capacity: %u SourceLength: %u\n", FrameBuf.Capacity(), SourceLength);
296 return RESULT_SMALLBUF;
299 ui32_t esv_length = calc_esv_length(SourceLength, PlaintextOffset);
302 if ( ! Kumu::read_test_BER(&ess_p, esv_length) )
304 DefaultLogSink().Error("read_test_BER did not return %u\n", esv_length);
305 return RESULT_FORMAT;
308 ui32_t tmp_len = esv_length + (m_Info.UsesHMAC ? klv_intpack_size : 0);
310 if ( PacketLength < tmp_len )
312 DefaultLogSink().Error("Frame length is larger than EKLV packet length.\n");
313 return RESULT_FORMAT;
318 // wrap the pointer and length as a FrameBuffer for use by
319 // DecryptFrameBuffer() and TestValues()
320 FrameBuffer TmpWrapper;
321 TmpWrapper.SetData(ess_p, tmp_len);
322 TmpWrapper.Size(tmp_len);
323 TmpWrapper.SourceLength(SourceLength);
324 TmpWrapper.PlaintextOffset(PlaintextOffset);
326 result = DecryptFrameBuffer(TmpWrapper, FrameBuf, Ctx);
327 FrameBuf.FrameNumber(FrameNum);
329 // detect and test integrity pack
330 if ( ASDCP_SUCCESS(result) && m_Info.UsesHMAC && HMAC )
332 IntegrityPack IntPack;
333 result = IntPack.TestValues(TmpWrapper, m_Info.AssetUUID, SequenceNum, HMAC);
336 else // return ciphertext to caller
338 if ( FrameBuf.Capacity() < tmp_len )
340 char intbuf[IntBufferLen];
341 DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %s\n",
342 FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
343 return RESULT_SMALLBUF;
346 memcpy(FrameBuf.Data(), ess_p, tmp_len);
347 FrameBuf.Size(tmp_len);
348 FrameBuf.SourceLength(SourceLength);
349 FrameBuf.PlaintextOffset(PlaintextOffset);
352 else if ( memcmp(Key.Value(), EssenceUL, Key.Size() - 1) == 0 ) // ignore the stream number
353 { // read plaintext frame
354 if ( FrameBuf.Capacity() < PacketLength )
356 char intbuf[IntBufferLen];
357 DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %s\n",
358 FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
359 return RESULT_SMALLBUF;
362 // read the data into the supplied buffer
364 assert(PacketLength <= 0xFFFFFFFFL);
365 result = m_File.Read(FrameBuf.Data(), (ui32_t) PacketLength, &read_count);
367 if ( ASDCP_FAILURE(result) )
370 if ( read_count != PacketLength )
372 char intbuf1[IntBufferLen];
373 char intbuf2[IntBufferLen];
374 DefaultLogSink().Error("read_count: %s != FrameLength: %s\n",
375 ui64sz(read_count, intbuf1),
376 ui64sz(PacketLength, intbuf2) );
378 return RESULT_READFAIL;
381 FrameBuf.FrameNumber(FrameNum);
382 FrameBuf.Size(read_count);
386 char strbuf[IntBufferLen];
387 const MDDEntry* Entry = m_Dict->FindUL(Key.Value());
389 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
391 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
392 return RESULT_FORMAT;