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 ( 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() == 3 )
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);
153 class KLReader : public ASDCP::KLVPacket
155 ASDCP_NO_COPY_CONSTRUCT(KLReader);
162 inline const byte_t* Key() { return m_KeyBuf; }
163 inline const ui64_t Length() { return m_ValueLength; }
164 inline const ui64_t KLLength() { return m_KLLength; }
166 Result_t ReadKLFromFile(Kumu::FileReader& Reader)
169 ui32_t header_length = SMPTE_UL_LENGTH + MXF_BER_LENGTH;
170 Result_t result = Reader.Read(m_KeyBuf, header_length, &read_count);
172 if ( read_count != header_length )
173 return RESULT_READFAIL;
175 if ( ASDCP_SUCCESS(result) )
176 result = InitFromBuffer(m_KeyBuf, header_length);
183 // standard method of reading a plaintext or encrypted frame
185 ASDCP::h__Reader::ReadEKLVPacket(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
186 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
188 // look up frame index node
189 IndexTableSegment::IndexEntry TmpEntry;
191 if ( ASDCP_FAILURE(m_FooterPart.Lookup(FrameNum, TmpEntry)) )
193 DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
197 // get frame position and go read the frame's key and length
198 Result_t result = RESULT_OK;
200 Kumu::fpos_t FilePosition = m_EssenceStart + TmpEntry.StreamOffset;
202 if ( FilePosition != m_LastPosition )
204 m_LastPosition = FilePosition;
205 result = m_File.Seek(FilePosition);
208 if ( ASDCP_SUCCESS(result) )
209 result = Reader.ReadKLFromFile(m_File);
211 if ( ASDCP_FAILURE(result) )
214 UL Key(Reader.Key());
215 ui64_t PacketLength = Reader.Length();
216 m_LastPosition = m_LastPosition + Reader.KLLength() + PacketLength;
218 if ( memcmp(Key.Value(), Dict::ul(MDD_CryptEssence), Key.Size() - 1) == 0 // ignore the stream numbers
219 || memcmp(Key.Value(), Dict::ul(MDD_MXFInterop_CryptEssence), Key.Size() - 1) == 0 )
221 if ( ! m_Info.EncryptedEssence )
223 DefaultLogSink().Error("EKLV packet found, no Cryptographic Context in header.\n");
224 return RESULT_FORMAT;
227 // read encrypted triplet value into internal buffer
228 m_CtFrameBuf.Capacity(PacketLength);
230 result = m_File.Read(m_CtFrameBuf.Data(), PacketLength, &read_count);
232 if ( ASDCP_FAILURE(result) )
235 if ( read_count != PacketLength )
237 DefaultLogSink().Error("read length is smaller than EKLV packet length.\n");
238 return RESULT_FORMAT;
241 m_CtFrameBuf.Size(PacketLength);
243 // should be const but mxflib::ReadBER is not
244 byte_t* ess_p = m_CtFrameBuf.Data();
246 // read context ID length
247 if ( ! Kumu::read_test_BER(&ess_p, UUIDlen) )
248 return RESULT_FORMAT;
250 // test the context ID
251 if ( memcmp(ess_p, m_Info.ContextID, UUIDlen) != 0 )
253 DefaultLogSink().Error("Packet's Cryptographic Context ID does not match the header.\n");
254 return RESULT_FORMAT;
258 // read PlaintextOffset length
259 if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
260 return RESULT_FORMAT;
262 ui32_t PlaintextOffset = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
263 ess_p += sizeof(ui64_t);
265 // read essence UL length
266 if ( ! Kumu::read_test_BER(&ess_p, SMPTE_UL_LENGTH) )
267 return RESULT_FORMAT;
270 if ( memcmp(ess_p, EssenceUL, SMPTE_UL_LENGTH - 1) != 0 ) // ignore the stream number
272 char strbuf[IntBufferLen];
273 const MDDEntry* Entry = Dict::FindUL(Key.Value());
275 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
277 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
278 return RESULT_FORMAT;
280 ess_p += SMPTE_UL_LENGTH;
282 // read SourceLength length
283 if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
284 return RESULT_FORMAT;
286 ui32_t SourceLength = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
287 ess_p += sizeof(ui64_t);
288 assert(SourceLength);
290 if ( FrameBuf.Capacity() < SourceLength )
292 DefaultLogSink().Error("FrameBuf.Capacity: %u SourceLength: %u\n", FrameBuf.Capacity(), SourceLength);
293 return RESULT_SMALLBUF;
296 ui32_t esv_length = calc_esv_length(SourceLength, PlaintextOffset);
299 if ( ! Kumu::read_test_BER(&ess_p, esv_length) )
301 DefaultLogSink().Error("read_test_BER did not return %u\n", esv_length);
302 return RESULT_FORMAT;
305 ui32_t tmp_len = esv_length + (m_Info.UsesHMAC ? klv_intpack_size : 0);
307 if ( PacketLength < tmp_len )
309 DefaultLogSink().Error("Frame length is larger than EKLV packet length.\n");
310 return RESULT_FORMAT;
315 // wrap the pointer and length as a FrameBuffer for use by
316 // DecryptFrameBuffer() and TestValues()
317 FrameBuffer TmpWrapper;
318 TmpWrapper.SetData(ess_p, tmp_len);
319 TmpWrapper.Size(tmp_len);
320 TmpWrapper.SourceLength(SourceLength);
321 TmpWrapper.PlaintextOffset(PlaintextOffset);
323 result = DecryptFrameBuffer(TmpWrapper, FrameBuf, Ctx);
324 FrameBuf.FrameNumber(FrameNum);
326 // detect and test integrity pack
327 if ( ASDCP_SUCCESS(result) && m_Info.UsesHMAC && HMAC )
329 IntegrityPack IntPack;
330 result = IntPack.TestValues(TmpWrapper, m_Info.AssetUUID, FrameNum + 1, HMAC);
333 else // return ciphertext to caller
335 if ( FrameBuf.Capacity() < tmp_len )
337 char intbuf[IntBufferLen];
338 DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %s\n",
339 FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
340 return RESULT_SMALLBUF;
343 memcpy(FrameBuf.Data(), ess_p, tmp_len);
344 FrameBuf.Size(tmp_len);
345 FrameBuf.SourceLength(SourceLength);
346 FrameBuf.PlaintextOffset(PlaintextOffset);
349 else if ( memcmp(Key.Value(), EssenceUL, Key.Size() - 1) == 0 ) // ignore the stream number
350 { // read plaintext frame
351 if ( FrameBuf.Capacity() < PacketLength )
353 char intbuf[IntBufferLen];
354 DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %s\n",
355 FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
356 return RESULT_SMALLBUF;
359 // read the data into the supplied buffer
361 result = m_File.Read(FrameBuf.Data(), PacketLength, &read_count);
363 if ( ASDCP_FAILURE(result) )
366 if ( read_count != PacketLength )
368 char intbuf1[IntBufferLen];
369 char intbuf2[IntBufferLen];
370 DefaultLogSink().Error("read_count: %s != FrameLength: %s\n",
371 ui64sz(read_count, intbuf1),
372 ui64sz(PacketLength, intbuf2) );
374 return RESULT_READFAIL;
377 FrameBuf.FrameNumber(FrameNum);
378 FrameBuf.Size(read_count);
382 char strbuf[IntBufferLen];
383 const MDDEntry* Entry = Dict::FindUL(Key.Value());
385 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
387 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
388 return RESULT_FORMAT;