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() > 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);
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::ReadEKLVFrame(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 Kumu::fpos_t FilePosition = m_EssenceStart + TmpEntry.StreamOffset;
199 Result_t result = RESULT_OK;
201 if ( FilePosition != m_LastPosition )
203 m_LastPosition = FilePosition;
204 result = m_File.Seek(FilePosition);
207 if( ASDCP_SUCCESS(result) )
208 result = ReadEKLVPacket(FrameNum, FrameBuf, EssenceUL, Ctx, HMAC);
215 ASDCP::h__Reader::ReadEKLVPacket(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
216 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
219 Result_t result = Reader.ReadKLFromFile(m_File);
221 if ( ASDCP_FAILURE(result) )
224 UL Key(Reader.Key());
225 ui64_t PacketLength = Reader.Length();
226 m_LastPosition = m_LastPosition + Reader.KLLength() + PacketLength;
228 if ( memcmp(Key.Value(), Dict::ul(MDD_CryptEssence), Key.Size() - 1) == 0 // ignore the stream numbers
229 || memcmp(Key.Value(), Dict::ul(MDD_MXFInterop_CryptEssence), Key.Size() - 1) == 0 )
231 if ( ! m_Info.EncryptedEssence )
233 DefaultLogSink().Error("EKLV packet found, no Cryptographic Context in header.\n");
234 return RESULT_FORMAT;
237 // read encrypted triplet value into internal buffer
238 m_CtFrameBuf.Capacity(PacketLength);
240 result = m_File.Read(m_CtFrameBuf.Data(), PacketLength, &read_count);
242 if ( ASDCP_FAILURE(result) )
245 if ( read_count != PacketLength )
247 DefaultLogSink().Error("read length is smaller than EKLV packet length.\n");
248 return RESULT_FORMAT;
251 m_CtFrameBuf.Size(PacketLength);
253 // should be const but mxflib::ReadBER is not
254 byte_t* ess_p = m_CtFrameBuf.Data();
256 // read context ID length
257 if ( ! Kumu::read_test_BER(&ess_p, UUIDlen) )
258 return RESULT_FORMAT;
260 // test the context ID
261 if ( memcmp(ess_p, m_Info.ContextID, UUIDlen) != 0 )
263 DefaultLogSink().Error("Packet's Cryptographic Context ID does not match the header.\n");
264 return RESULT_FORMAT;
268 // read PlaintextOffset length
269 if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
270 return RESULT_FORMAT;
272 ui32_t PlaintextOffset = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
273 ess_p += sizeof(ui64_t);
275 // read essence UL length
276 if ( ! Kumu::read_test_BER(&ess_p, SMPTE_UL_LENGTH) )
277 return RESULT_FORMAT;
280 if ( memcmp(ess_p, EssenceUL, SMPTE_UL_LENGTH - 1) != 0 ) // ignore the stream number
282 char strbuf[IntBufferLen];
283 const MDDEntry* Entry = Dict::FindUL(Key.Value());
285 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
287 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
288 return RESULT_FORMAT;
290 ess_p += SMPTE_UL_LENGTH;
292 // read SourceLength length
293 if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
294 return RESULT_FORMAT;
296 ui32_t SourceLength = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
297 ess_p += sizeof(ui64_t);
298 assert(SourceLength);
300 if ( FrameBuf.Capacity() < SourceLength )
302 DefaultLogSink().Error("FrameBuf.Capacity: %u SourceLength: %u\n", FrameBuf.Capacity(), SourceLength);
303 return RESULT_SMALLBUF;
306 ui32_t esv_length = calc_esv_length(SourceLength, PlaintextOffset);
309 if ( ! Kumu::read_test_BER(&ess_p, esv_length) )
311 DefaultLogSink().Error("read_test_BER did not return %u\n", esv_length);
312 return RESULT_FORMAT;
315 ui32_t tmp_len = esv_length + (m_Info.UsesHMAC ? klv_intpack_size : 0);
317 if ( PacketLength < tmp_len )
319 DefaultLogSink().Error("Frame length is larger than EKLV packet length.\n");
320 return RESULT_FORMAT;
325 // wrap the pointer and length as a FrameBuffer for use by
326 // DecryptFrameBuffer() and TestValues()
327 FrameBuffer TmpWrapper;
328 TmpWrapper.SetData(ess_p, tmp_len);
329 TmpWrapper.Size(tmp_len);
330 TmpWrapper.SourceLength(SourceLength);
331 TmpWrapper.PlaintextOffset(PlaintextOffset);
333 result = DecryptFrameBuffer(TmpWrapper, FrameBuf, Ctx);
334 FrameBuf.FrameNumber(FrameNum);
336 // detect and test integrity pack
337 if ( ASDCP_SUCCESS(result) && m_Info.UsesHMAC && HMAC )
339 IntegrityPack IntPack;
340 result = IntPack.TestValues(TmpWrapper, m_Info.AssetUUID, FrameNum + 1, HMAC);
343 else // return ciphertext to caller
345 if ( FrameBuf.Capacity() < tmp_len )
347 char intbuf[IntBufferLen];
348 DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %s\n",
349 FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
350 return RESULT_SMALLBUF;
353 memcpy(FrameBuf.Data(), ess_p, tmp_len);
354 FrameBuf.Size(tmp_len);
355 FrameBuf.SourceLength(SourceLength);
356 FrameBuf.PlaintextOffset(PlaintextOffset);
359 else if ( memcmp(Key.Value(), EssenceUL, Key.Size() - 1) == 0 ) // ignore the stream number
360 { // read plaintext frame
361 if ( FrameBuf.Capacity() < PacketLength )
363 char intbuf[IntBufferLen];
364 DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %s\n",
365 FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
366 return RESULT_SMALLBUF;
369 // read the data into the supplied buffer
371 result = m_File.Read(FrameBuf.Data(), PacketLength, &read_count);
373 if ( ASDCP_FAILURE(result) )
376 if ( read_count != PacketLength )
378 char intbuf1[IntBufferLen];
379 char intbuf2[IntBufferLen];
380 DefaultLogSink().Error("read_count: %s != FrameLength: %s\n",
381 ui64sz(read_count, intbuf1),
382 ui64sz(PacketLength, intbuf2) );
384 return RESULT_READFAIL;
387 FrameBuf.FrameNumber(FrameNum);
388 FrameBuf.Size(read_count);
392 char strbuf[IntBufferLen];
393 const MDDEntry* Entry = Dict::FindUL(Key.Value());
395 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
397 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
398 return RESULT_FORMAT;