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(WriterInfo& Info)
61 InterchangeObject* Object;
64 Result_t result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object);
66 if( ASDCP_SUCCESS(result) )
67 MD_to_WriterInfo((Identification*)Object, m_Info);
70 if( ASDCP_SUCCESS(result) )
71 result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object);
73 if( ASDCP_SUCCESS(result) )
75 SourcePackage* SP = (SourcePackage*)Object;
76 memcpy(Info.AssetUUID, SP->PackageUID.Value() + 16, UUIDlen);
79 // optional CryptographicContext
80 if( ASDCP_SUCCESS(result) )
82 Result_t cr_result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(CryptographicContext), &Object);
84 if( ASDCP_SUCCESS(cr_result) )
85 MD_to_CryptoInfo((CryptographicContext*)Object, m_Info);
92 // standard method of opening an MXF file for read
94 ASDCP::h__Reader::OpenMXFRead(const char* filename)
97 Result_t result = m_File.OpenRead(filename);
99 if ( ASDCP_SUCCESS(result) )
100 result = m_HeaderPart.InitFromFile(m_File);
102 // if this is a three partition file, go to the body
103 // partition and read off the partition pack
104 if ( m_HeaderPart.m_RIP.PairArray.size() == 3 )
106 DefaultLogSink().Error("RIP count is 3: must write code...\n");
107 return RESULT_FORMAT;
109 // TODO: check the partition pack to make sure it is
110 // really a body with a single essence container
112 m_EssenceStart = m_File.Tell();
118 // standard method of populating the in-memory index
120 ASDCP::h__Reader::InitMXFIndex()
122 if ( ! m_File.IsOpen() )
125 Result_t result = m_File.Seek(m_HeaderPart.FooterPartition);
127 if ( ASDCP_SUCCESS(result) )
129 m_FooterPart.m_Lookup = &m_HeaderPart.m_Primer;
130 result = m_FooterPart.InitFromFile(m_File);
133 if ( ASDCP_SUCCESS(result) )
134 m_File.Seek(m_EssenceStart);
140 // standard method of reading a plaintext or encrypted frame
142 ASDCP::h__Reader::ReadEKLVPacket(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
143 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
145 // look up frame index node
146 IndexTableSegment::IndexEntry TmpEntry;
148 if ( ASDCP_FAILURE(m_FooterPart.Lookup(FrameNum, TmpEntry)) )
150 DefaultLogSink().Error("Frame value out of range: %lu\n", FrameNum);
154 // get frame position and go read the frame's key and length
155 Result_t result = RESULT_OK;
156 ASDCP::KLVReader Reader;
157 ASDCP::fpos_t FilePosition = m_EssenceStart + TmpEntry.StreamOffset;
159 if ( FilePosition != m_LastPosition )
161 m_LastPosition = FilePosition;
162 result = m_File.Seek(FilePosition);
165 if ( ASDCP_SUCCESS(result) )
166 result = Reader.ReadKLFromFile(m_File);
168 if ( ASDCP_FAILURE(result) )
171 UL Key(Reader.Key());
172 UL CryptEssenceUL(Dict::ul(MDD_CryptEssence));
173 ui64_t PacketLength = Reader.Length();
174 m_LastPosition = m_LastPosition + Reader.KLLength() + PacketLength;
176 if ( Key == CryptEssenceUL )
178 if ( ! m_Info.EncryptedEssence )
180 DefaultLogSink().Error("EKLV packet found, no Cryptographic Context in header.\n");
181 return RESULT_FORMAT;
184 // read encrypted triplet value into internal buffer
185 m_CtFrameBuf.Capacity(PacketLength);
187 result = m_File.Read(m_CtFrameBuf.Data(), PacketLength, &read_count);
189 if ( ASDCP_FAILURE(result) )
192 if ( read_count != PacketLength )
194 DefaultLogSink().Error("read length is smaller than EKLV packet length.\n");
195 return RESULT_FORMAT;
198 m_CtFrameBuf.Size(PacketLength);
200 // should be const but mxflib::ReadBER is not
201 byte_t* ess_p = m_CtFrameBuf.Data();
203 // read context ID length
204 if ( ! read_test_BER(&ess_p, UUIDlen) )
205 return RESULT_FORMAT;
207 // test the context ID
208 if ( memcmp(ess_p, m_Info.ContextID, UUIDlen) != 0 )
210 DefaultLogSink().Error("Packet's Cryptographic Context ID does not match the header.\n");
211 return RESULT_FORMAT;
215 // read PlaintextOffset length
216 if ( ! read_test_BER(&ess_p, sizeof(ui64_t)) )
217 return RESULT_FORMAT;
219 ui32_t PlaintextOffset = (ui32_t)ASDCP_i64_BE(cp2i<ui64_t>(ess_p));
220 ess_p += sizeof(ui64_t);
222 // read essence UL length
223 if ( ! read_test_BER(&ess_p, SMPTE_UL_LENGTH) )
224 return RESULT_FORMAT;
226 // TODO: test essence UL
227 ess_p += SMPTE_UL_LENGTH;
229 // read SourceLength length
230 if ( ! read_test_BER(&ess_p, sizeof(ui64_t)) )
231 return RESULT_FORMAT;
233 ui32_t SourceLength = (ui32_t)ASDCP_i64_BE(cp2i<ui64_t>(ess_p));
234 ess_p += sizeof(ui64_t);
235 assert(SourceLength);
237 if ( FrameBuf.Capacity() < SourceLength )
239 DefaultLogSink().Error("FrameBuf.Capacity: %lu SourceLength: %lu\n", FrameBuf.Capacity(), SourceLength);
240 return RESULT_SMALLBUF;
243 ui32_t esv_length = calc_esv_length(SourceLength, PlaintextOffset);
246 if ( ! read_test_BER(&ess_p, esv_length) )
248 DefaultLogSink().Error("read_test_BER did not return %lu\n", esv_length);
249 return RESULT_FORMAT;
252 ui32_t tmp_len = esv_length + (m_Info.UsesHMAC ? klv_intpack_size : 0);
254 if ( PacketLength < tmp_len )
256 DefaultLogSink().Error("Frame length is larger than EKLV packet length.\n");
257 return RESULT_FORMAT;
262 // wrap the pointer and length as a FrameBuffer for use by
263 // DecryptFrameBuffer() and TestValues()
264 FrameBuffer TmpWrapper;
265 TmpWrapper.SetData(ess_p, tmp_len);
266 TmpWrapper.Size(tmp_len);
267 TmpWrapper.SourceLength(SourceLength);
268 TmpWrapper.PlaintextOffset(PlaintextOffset);
270 result = DecryptFrameBuffer(TmpWrapper, FrameBuf, Ctx);
271 FrameBuf.FrameNumber(FrameNum);
273 // detect and test integrity pack
274 if ( ASDCP_SUCCESS(result) && m_Info.UsesHMAC && HMAC )
276 IntegrityPack IntPack;
277 result = IntPack.TestValues(TmpWrapper, m_Info.AssetUUID, FrameNum + 1, HMAC);
280 else // return ciphertext to caller
282 if ( FrameBuf.Capacity() < tmp_len )
283 return RESULT_SMALLBUF;
285 memcpy(FrameBuf.Data(), ess_p, tmp_len);
286 FrameBuf.Size(tmp_len);
287 FrameBuf.SourceLength(SourceLength);
288 FrameBuf.PlaintextOffset(PlaintextOffset);
291 else if ( Key == EssenceUL )
292 { // read plaintext frame
293 if ( FrameBuf.Capacity() < PacketLength )
295 char intbuf[IntBufferLen];
296 DefaultLogSink().Error("FrameBuf.Capacity: %lu FrameLength: %s\n",
297 FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
298 return RESULT_SMALLBUF;
301 // read the data into the supplied buffer
303 result = m_File.Read(FrameBuf.Data(), PacketLength, &read_count);
305 if ( ASDCP_FAILURE(result) )
308 if ( read_count != PacketLength )
310 char intbuf1[IntBufferLen];
311 char intbuf2[IntBufferLen];
312 DefaultLogSink().Error("read_count: %s != FrameLength: %s\n",
313 ui64sz(read_count, intbuf1),
314 ui64sz(PacketLength, intbuf2) );
316 return RESULT_READFAIL;
319 FrameBuf.FrameNumber(FrameNum);
320 FrameBuf.Size(read_count);
324 char strbuf[IntBufferLen];
325 const MDDEntry* Entry = Dict::FindUL(Key.Value());
327 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.ToString(strbuf));
329 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
330 return RESULT_FORMAT;