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(d), m_BodyPart(d), m_FooterPart(d), m_Dict(d), m_EssenceStart(0)
44 ASDCP::h__Reader::~h__Reader()
50 ASDCP::h__Reader::Close()
55 //------------------------------------------------------------------------------------------
60 ASDCP::h__Reader::InitInfo()
62 InterchangeObject* Object;
64 m_Info.LabelSetType = LS_MXF_UNKNOWN;
65 UL OPAtomUL(m_Dict.ul(MDD_OPAtom));
66 UL Interop_OPAtomUL(m_Dict.ul(MDD_MXFInterop_OPAtom));
68 if ( m_HeaderPart.OperationalPattern == Interop_OPAtomUL )
69 m_Info.LabelSetType = LS_MXF_INTEROP;
70 else if ( m_HeaderPart.OperationalPattern == OPAtomUL )
71 m_Info.LabelSetType = LS_MXF_SMPTE;
74 Result_t result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object);
76 if( ASDCP_SUCCESS(result) )
77 MD_to_WriterInfo((Identification*)Object, m_Info);
80 if( ASDCP_SUCCESS(result) )
81 result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object);
83 if( ASDCP_SUCCESS(result) )
85 SourcePackage* SP = (SourcePackage*)Object;
86 memcpy(m_Info.AssetUUID, SP->PackageUID.Value() + 16, UUIDlen);
89 // optional CryptographicContext
90 if( ASDCP_SUCCESS(result) )
92 Result_t cr_result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(CryptographicContext), &Object);
94 if( ASDCP_SUCCESS(cr_result) )
95 MD_to_CryptoInfo((CryptographicContext*)Object, m_Info, m_Dict);
102 // standard method of opening an MXF file for read
104 ASDCP::h__Reader::OpenMXFRead(const char* filename)
107 Result_t result = m_File.OpenRead(filename);
109 if ( ASDCP_SUCCESS(result) )
110 result = m_HeaderPart.InitFromFile(m_File);
112 if ( ASDCP_SUCCESS(result) )
114 // if this is a three partition file, go to the body
115 // partition and read the partition pack
116 if ( m_HeaderPart.m_RIP.PairArray.size() > 2 )
118 Array<RIP::Pair>::iterator r_i = m_HeaderPart.m_RIP.PairArray.begin();
120 m_File.Seek((*r_i).ByteOffset);
122 result = m_BodyPart.InitFromFile(m_File);
125 m_EssenceStart = m_File.Tell();
132 // standard method of populating the in-memory index
134 ASDCP::h__Reader::InitMXFIndex()
136 if ( ! m_File.IsOpen() )
139 Result_t result = m_File.Seek(m_HeaderPart.FooterPartition);
141 if ( ASDCP_SUCCESS(result) )
143 m_FooterPart.m_Lookup = &m_HeaderPart.m_Primer;
144 result = m_FooterPart.InitFromFile(m_File);
147 if ( ASDCP_SUCCESS(result) )
148 m_File.Seek(m_EssenceStart);
155 ASDCP::KLReader::ReadKLFromFile(Kumu::FileReader& Reader)
158 ui32_t header_length = SMPTE_UL_LENGTH + MXF_BER_LENGTH;
159 Result_t result = Reader.Read(m_KeyBuf, header_length, &read_count);
161 if ( ASDCP_SUCCESS(result) )
163 if ( read_count != header_length )
164 result = RESULT_READFAIL;
167 result = InitFromBuffer(m_KeyBuf, header_length);
173 // standard method of reading a plaintext or encrypted frame
175 ASDCP::h__Reader::ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
176 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
178 // look up frame index node
179 IndexTableSegment::IndexEntry TmpEntry;
181 if ( ASDCP_FAILURE(m_FooterPart.Lookup(FrameNum, TmpEntry)) )
183 DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
187 // get frame position and go read the frame's key and length
188 Kumu::fpos_t FilePosition = m_EssenceStart + TmpEntry.StreamOffset;
189 Result_t result = RESULT_OK;
191 if ( FilePosition != m_LastPosition )
193 m_LastPosition = FilePosition;
194 result = m_File.Seek(FilePosition);
197 if( ASDCP_SUCCESS(result) )
198 result = ReadEKLVPacket(FrameNum, FrameNum + 1, FrameBuf, EssenceUL, Ctx, HMAC);
205 ASDCP::h__Reader::ReadEKLVPacket(ui32_t FrameNum, ui32_t SequenceNum, ASDCP::FrameBuffer& FrameBuf,
206 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
209 Result_t 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(), m_Dict.ul(MDD_CryptEssence), Key.Size() - 1) == 0 // ignore the stream numbers
219 || memcmp(Key.Value(), m_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 assert(PacketLength <= 0xFFFFFFFFL);
229 m_CtFrameBuf.Capacity((ui32_t) PacketLength);
231 result = m_File.Read(m_CtFrameBuf.Data(), (ui32_t) PacketLength,
234 if ( ASDCP_FAILURE(result) )
237 if ( read_count != PacketLength )
239 DefaultLogSink().Error("read length is smaller than EKLV packet length.\n");
240 return RESULT_FORMAT;
243 m_CtFrameBuf.Size((ui32_t) PacketLength);
245 // should be const but mxflib::ReadBER is not
246 byte_t* ess_p = m_CtFrameBuf.Data();
248 // read context ID length
249 if ( ! Kumu::read_test_BER(&ess_p, UUIDlen) )
250 return RESULT_FORMAT;
252 // test the context ID
253 if ( memcmp(ess_p, m_Info.ContextID, UUIDlen) != 0 )
255 DefaultLogSink().Error("Packet's Cryptographic Context ID does not match the header.\n");
256 return RESULT_FORMAT;
260 // read PlaintextOffset length
261 if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
262 return RESULT_FORMAT;
264 ui32_t PlaintextOffset = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
265 ess_p += sizeof(ui64_t);
267 // read essence UL length
268 if ( ! Kumu::read_test_BER(&ess_p, SMPTE_UL_LENGTH) )
269 return RESULT_FORMAT;
272 if ( memcmp(ess_p, EssenceUL, SMPTE_UL_LENGTH - 1) != 0 ) // ignore the stream number
274 char strbuf[IntBufferLen];
275 const MDDEntry* Entry = m_Dict.FindUL(Key.Value());
277 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
279 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
280 return RESULT_FORMAT;
282 ess_p += SMPTE_UL_LENGTH;
284 // read SourceLength length
285 if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
286 return RESULT_FORMAT;
288 ui32_t SourceLength = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
289 ess_p += sizeof(ui64_t);
290 assert(SourceLength);
292 if ( FrameBuf.Capacity() < SourceLength )
294 DefaultLogSink().Error("FrameBuf.Capacity: %u SourceLength: %u\n", FrameBuf.Capacity(), SourceLength);
295 return RESULT_SMALLBUF;
298 ui32_t esv_length = calc_esv_length(SourceLength, PlaintextOffset);
301 if ( ! Kumu::read_test_BER(&ess_p, esv_length) )
303 DefaultLogSink().Error("read_test_BER did not return %u\n", esv_length);
304 return RESULT_FORMAT;
307 ui32_t tmp_len = esv_length + (m_Info.UsesHMAC ? klv_intpack_size : 0);
309 if ( PacketLength < tmp_len )
311 DefaultLogSink().Error("Frame length is larger than EKLV packet length.\n");
312 return RESULT_FORMAT;
317 // wrap the pointer and length as a FrameBuffer for use by
318 // DecryptFrameBuffer() and TestValues()
319 FrameBuffer TmpWrapper;
320 TmpWrapper.SetData(ess_p, tmp_len);
321 TmpWrapper.Size(tmp_len);
322 TmpWrapper.SourceLength(SourceLength);
323 TmpWrapper.PlaintextOffset(PlaintextOffset);
325 result = DecryptFrameBuffer(TmpWrapper, FrameBuf, Ctx);
326 FrameBuf.FrameNumber(FrameNum);
328 // detect and test integrity pack
329 if ( ASDCP_SUCCESS(result) && m_Info.UsesHMAC && HMAC )
331 IntegrityPack IntPack;
332 result = IntPack.TestValues(TmpWrapper, m_Info.AssetUUID, SequenceNum, HMAC);
335 else // return ciphertext to caller
337 if ( FrameBuf.Capacity() < tmp_len )
339 char intbuf[IntBufferLen];
340 DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %s\n",
341 FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
342 return RESULT_SMALLBUF;
345 memcpy(FrameBuf.Data(), ess_p, tmp_len);
346 FrameBuf.Size(tmp_len);
347 FrameBuf.SourceLength(SourceLength);
348 FrameBuf.PlaintextOffset(PlaintextOffset);
351 else if ( memcmp(Key.Value(), EssenceUL, Key.Size() - 1) == 0 ) // ignore the stream number
352 { // read plaintext frame
353 if ( FrameBuf.Capacity() < PacketLength )
355 char intbuf[IntBufferLen];
356 DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %s\n",
357 FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
358 return RESULT_SMALLBUF;
361 // read the data into the supplied buffer
363 assert(PacketLength <= 0xFFFFFFFFL);
364 result = m_File.Read(FrameBuf.Data(), (ui32_t) PacketLength, &read_count);
366 if ( ASDCP_FAILURE(result) )
369 if ( read_count != PacketLength )
371 char intbuf1[IntBufferLen];
372 char intbuf2[IntBufferLen];
373 DefaultLogSink().Error("read_count: %s != FrameLength: %s\n",
374 ui64sz(read_count, intbuf1),
375 ui64sz(PacketLength, intbuf2) );
377 return RESULT_READFAIL;
380 FrameBuf.FrameNumber(FrameNum);
381 FrameBuf.Size(read_count);
385 char strbuf[IntBufferLen];
386 const MDDEntry* Entry = m_Dict.FindUL(Key.Value());
388 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
390 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
391 return RESULT_FORMAT;