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 m_CtFrameBuf.Capacity(PacketLength);
229 result = m_File.Read(m_CtFrameBuf.Data(), PacketLength, &read_count);
231 if ( ASDCP_FAILURE(result) )
234 if ( read_count != PacketLength )
236 DefaultLogSink().Error("read length is smaller than EKLV packet length.\n");
237 return RESULT_FORMAT;
240 m_CtFrameBuf.Size(PacketLength);
242 // should be const but mxflib::ReadBER is not
243 byte_t* ess_p = m_CtFrameBuf.Data();
245 // read context ID length
246 if ( ! Kumu::read_test_BER(&ess_p, UUIDlen) )
247 return RESULT_FORMAT;
249 // test the context ID
250 if ( memcmp(ess_p, m_Info.ContextID, UUIDlen) != 0 )
252 DefaultLogSink().Error("Packet's Cryptographic Context ID does not match the header.\n");
253 return RESULT_FORMAT;
257 // read PlaintextOffset length
258 if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
259 return RESULT_FORMAT;
261 ui32_t PlaintextOffset = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
262 ess_p += sizeof(ui64_t);
264 // read essence UL length
265 if ( ! Kumu::read_test_BER(&ess_p, SMPTE_UL_LENGTH) )
266 return RESULT_FORMAT;
269 if ( memcmp(ess_p, EssenceUL, SMPTE_UL_LENGTH - 1) != 0 ) // ignore the stream number
271 char strbuf[IntBufferLen];
272 const MDDEntry* Entry = Dict::FindUL(Key.Value());
274 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
276 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
277 return RESULT_FORMAT;
279 ess_p += SMPTE_UL_LENGTH;
281 // read SourceLength length
282 if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
283 return RESULT_FORMAT;
285 ui32_t SourceLength = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
286 ess_p += sizeof(ui64_t);
287 assert(SourceLength);
289 if ( FrameBuf.Capacity() < SourceLength )
291 DefaultLogSink().Error("FrameBuf.Capacity: %u SourceLength: %u\n", FrameBuf.Capacity(), SourceLength);
292 return RESULT_SMALLBUF;
295 ui32_t esv_length = calc_esv_length(SourceLength, PlaintextOffset);
298 if ( ! Kumu::read_test_BER(&ess_p, esv_length) )
300 DefaultLogSink().Error("read_test_BER did not return %u\n", esv_length);
301 return RESULT_FORMAT;
304 ui32_t tmp_len = esv_length + (m_Info.UsesHMAC ? klv_intpack_size : 0);
306 if ( PacketLength < tmp_len )
308 DefaultLogSink().Error("Frame length is larger than EKLV packet length.\n");
309 return RESULT_FORMAT;
314 // wrap the pointer and length as a FrameBuffer for use by
315 // DecryptFrameBuffer() and TestValues()
316 FrameBuffer TmpWrapper;
317 TmpWrapper.SetData(ess_p, tmp_len);
318 TmpWrapper.Size(tmp_len);
319 TmpWrapper.SourceLength(SourceLength);
320 TmpWrapper.PlaintextOffset(PlaintextOffset);
322 result = DecryptFrameBuffer(TmpWrapper, FrameBuf, Ctx);
323 FrameBuf.FrameNumber(FrameNum);
325 // detect and test integrity pack
326 if ( ASDCP_SUCCESS(result) && m_Info.UsesHMAC && HMAC )
328 IntegrityPack IntPack;
329 result = IntPack.TestValues(TmpWrapper, m_Info.AssetUUID, SequenceNum, HMAC);
332 else // return ciphertext to caller
334 if ( FrameBuf.Capacity() < tmp_len )
336 char intbuf[IntBufferLen];
337 DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %s\n",
338 FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
339 return RESULT_SMALLBUF;
342 memcpy(FrameBuf.Data(), ess_p, tmp_len);
343 FrameBuf.Size(tmp_len);
344 FrameBuf.SourceLength(SourceLength);
345 FrameBuf.PlaintextOffset(PlaintextOffset);
348 else if ( memcmp(Key.Value(), EssenceUL, Key.Size() - 1) == 0 ) // ignore the stream number
349 { // read plaintext frame
350 if ( FrameBuf.Capacity() < PacketLength )
352 char intbuf[IntBufferLen];
353 DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %s\n",
354 FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
355 return RESULT_SMALLBUF;
358 // read the data into the supplied buffer
360 result = m_File.Read(FrameBuf.Data(), PacketLength, &read_count);
362 if ( ASDCP_FAILURE(result) )
365 if ( read_count != PacketLength )
367 char intbuf1[IntBufferLen];
368 char intbuf2[IntBufferLen];
369 DefaultLogSink().Error("read_count: %s != FrameLength: %s\n",
370 ui64sz(read_count, intbuf1),
371 ui64sz(PacketLength, intbuf2) );
373 return RESULT_READFAIL;
376 FrameBuf.FrameNumber(FrameNum);
377 FrameBuf.Size(read_count);
381 char strbuf[IntBufferLen];
382 const MDDEntry* Entry = Dict::FindUL(Key.Value());
384 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
386 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
387 return RESULT_FORMAT;