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);
154 ASDCP::KLReader::ReadKLFromFile(Kumu::FileReader& Reader)
157 ui32_t header_length = SMPTE_UL_LENGTH + MXF_BER_LENGTH;
158 Result_t result = Reader.Read(m_KeyBuf, header_length, &read_count);
160 if ( ASDCP_SUCCESS(result) )
162 if ( read_count != header_length )
163 result = RESULT_READFAIL;
166 result = InitFromBuffer(m_KeyBuf, header_length);
172 // standard method of reading a plaintext or encrypted frame
174 ASDCP::h__Reader::ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
175 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
177 // look up frame index node
178 IndexTableSegment::IndexEntry TmpEntry;
180 if ( ASDCP_FAILURE(m_FooterPart.Lookup(FrameNum, TmpEntry)) )
182 DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
186 // get frame position and go read the frame's key and length
187 Kumu::fpos_t FilePosition = m_EssenceStart + TmpEntry.StreamOffset;
188 Result_t result = RESULT_OK;
190 if ( FilePosition != m_LastPosition )
192 m_LastPosition = FilePosition;
193 result = m_File.Seek(FilePosition);
196 if( ASDCP_SUCCESS(result) )
197 result = ReadEKLVPacket(FrameNum, FrameNum + 1, FrameBuf, EssenceUL, Ctx, HMAC);
204 ASDCP::h__Reader::ReadEKLVPacket(ui32_t FrameNum, ui32_t SequenceNum, ASDCP::FrameBuffer& FrameBuf,
205 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
208 Result_t result = Reader.ReadKLFromFile(m_File);
210 if ( ASDCP_FAILURE(result) )
213 UL Key(Reader.Key());
214 ui64_t PacketLength = Reader.Length();
215 m_LastPosition = m_LastPosition + Reader.KLLength() + PacketLength;
217 if ( memcmp(Key.Value(), Dict::ul(MDD_CryptEssence), Key.Size() - 1) == 0 // ignore the stream numbers
218 || memcmp(Key.Value(), Dict::ul(MDD_MXFInterop_CryptEssence), Key.Size() - 1) == 0 )
220 if ( ! m_Info.EncryptedEssence )
222 DefaultLogSink().Error("EKLV packet found, no Cryptographic Context in header.\n");
223 return RESULT_FORMAT;
226 // read encrypted triplet value into internal buffer
227 assert(PacketLength <= 0xFFFFFFFFL);
228 m_CtFrameBuf.Capacity((ui32_t) PacketLength);
230 result = m_File.Read(m_CtFrameBuf.Data(), (ui32_t) PacketLength,
233 if ( ASDCP_FAILURE(result) )
236 if ( read_count != PacketLength )
238 DefaultLogSink().Error("read length is smaller than EKLV packet length.\n");
239 return RESULT_FORMAT;
242 m_CtFrameBuf.Size((ui32_t) PacketLength);
244 // should be const but mxflib::ReadBER is not
245 byte_t* ess_p = m_CtFrameBuf.Data();
247 // read context ID length
248 if ( ! Kumu::read_test_BER(&ess_p, UUIDlen) )
249 return RESULT_FORMAT;
251 // test the context ID
252 if ( memcmp(ess_p, m_Info.ContextID, UUIDlen) != 0 )
254 DefaultLogSink().Error("Packet's Cryptographic Context ID does not match the header.\n");
255 return RESULT_FORMAT;
259 // read PlaintextOffset length
260 if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
261 return RESULT_FORMAT;
263 ui32_t PlaintextOffset = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
264 ess_p += sizeof(ui64_t);
266 // read essence UL length
267 if ( ! Kumu::read_test_BER(&ess_p, SMPTE_UL_LENGTH) )
268 return RESULT_FORMAT;
271 if ( memcmp(ess_p, EssenceUL, SMPTE_UL_LENGTH - 1) != 0 ) // ignore the stream number
273 char strbuf[IntBufferLen];
274 const MDDEntry* Entry = Dict::FindUL(Key.Value());
276 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
278 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
279 return RESULT_FORMAT;
281 ess_p += SMPTE_UL_LENGTH;
283 // read SourceLength length
284 if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
285 return RESULT_FORMAT;
287 ui32_t SourceLength = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
288 ess_p += sizeof(ui64_t);
289 assert(SourceLength);
291 if ( FrameBuf.Capacity() < SourceLength )
293 DefaultLogSink().Error("FrameBuf.Capacity: %u SourceLength: %u\n", FrameBuf.Capacity(), SourceLength);
294 return RESULT_SMALLBUF;
297 ui32_t esv_length = calc_esv_length(SourceLength, PlaintextOffset);
300 if ( ! Kumu::read_test_BER(&ess_p, esv_length) )
302 DefaultLogSink().Error("read_test_BER did not return %u\n", esv_length);
303 return RESULT_FORMAT;
306 ui32_t tmp_len = esv_length + (m_Info.UsesHMAC ? klv_intpack_size : 0);
308 if ( PacketLength < tmp_len )
310 DefaultLogSink().Error("Frame length is larger than EKLV packet length.\n");
311 return RESULT_FORMAT;
316 // wrap the pointer and length as a FrameBuffer for use by
317 // DecryptFrameBuffer() and TestValues()
318 FrameBuffer TmpWrapper;
319 TmpWrapper.SetData(ess_p, tmp_len);
320 TmpWrapper.Size(tmp_len);
321 TmpWrapper.SourceLength(SourceLength);
322 TmpWrapper.PlaintextOffset(PlaintextOffset);
324 result = DecryptFrameBuffer(TmpWrapper, FrameBuf, Ctx);
325 FrameBuf.FrameNumber(FrameNum);
327 // detect and test integrity pack
328 if ( ASDCP_SUCCESS(result) && m_Info.UsesHMAC && HMAC )
330 IntegrityPack IntPack;
331 result = IntPack.TestValues(TmpWrapper, m_Info.AssetUUID, SequenceNum, HMAC);
334 else // return ciphertext to caller
336 if ( FrameBuf.Capacity() < tmp_len )
338 char intbuf[IntBufferLen];
339 DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %s\n",
340 FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
341 return RESULT_SMALLBUF;
344 memcpy(FrameBuf.Data(), ess_p, tmp_len);
345 FrameBuf.Size(tmp_len);
346 FrameBuf.SourceLength(SourceLength);
347 FrameBuf.PlaintextOffset(PlaintextOffset);
350 else if ( memcmp(Key.Value(), EssenceUL, Key.Size() - 1) == 0 ) // ignore the stream number
351 { // read plaintext frame
352 if ( FrameBuf.Capacity() < PacketLength )
354 char intbuf[IntBufferLen];
355 DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %s\n",
356 FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
357 return RESULT_SMALLBUF;
360 // read the data into the supplied buffer
362 assert(PacketLength <= 0xFFFFFFFFL);
363 result = m_File.Read(FrameBuf.Data(), (ui32_t) PacketLength, &read_count);
365 if ( ASDCP_FAILURE(result) )
368 if ( read_count != PacketLength )
370 char intbuf1[IntBufferLen];
371 char intbuf2[IntBufferLen];
372 DefaultLogSink().Error("read_count: %s != FrameLength: %s\n",
373 ui64sz(read_count, intbuf1),
374 ui64sz(PacketLength, intbuf2) );
376 return RESULT_READFAIL;
379 FrameBuf.FrameNumber(FrameNum);
380 FrameBuf.Size(read_count);
384 char strbuf[IntBufferLen];
385 const MDDEntry* Entry = Dict::FindUL(Key.Value());
387 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
389 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
390 return RESULT_FORMAT;