2 Copyright (c) 2004-2012, 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 #define DEFAULT_MD_DECL
33 #include "AS_DCP_internal.h"
36 using namespace ASDCP;
37 using namespace ASDCP::MXF;
39 static Kumu::Mutex sg_DefaultMDInitLock;
40 static bool sg_DefaultMDTypesInit = false;
41 static const ASDCP::Dictionary *sg_dict;
45 ASDCP::default_md_object_init()
47 if ( ! sg_DefaultMDTypesInit )
49 Kumu::AutoMutex BlockLock(sg_DefaultMDInitLock);
51 if ( ! sg_DefaultMDTypesInit )
53 sg_dict = &DefaultSMPTEDict();
54 g_OPAtomHeader = new ASDCP::MXF::OPAtomHeader(sg_dict);
55 g_OPAtomIndexFooter = new ASDCP::MXF::OPAtomIndexFooter(sg_dict);
56 sg_DefaultMDTypesInit = true;
62 //------------------------------------------------------------------------------------------
66 ASDCP::h__ASDCPReader::h__ASDCPReader(const Dictionary& d) : MXF::TrackFileReader<OPAtomHeader, OPAtomIndexFooter>(d), m_BodyPart(m_Dict) {}
67 ASDCP::h__ASDCPReader::~h__ASDCPReader() {}
70 // AS-DCP method of opening an MXF file for read
72 ASDCP::h__ASDCPReader::OpenMXFRead(const char* filename)
74 Result_t result = ASDCP::MXF::TrackFileReader<OPAtomHeader, OPAtomIndexFooter>::OpenMXFRead(filename);
76 if ( KM_SUCCESS(result) )
78 // if this is a three partition file, go to the body
79 // partition and read the partition pack
80 if ( m_HeaderPart.m_RIP.PairArray.size() > 2 )
82 Array<RIP::Pair>::iterator r_i = m_HeaderPart.m_RIP.PairArray.begin();
84 m_File.Seek((*r_i).ByteOffset);
85 result = m_BodyPart.InitFromFile(m_File);
89 if ( KM_SUCCESS(result) )
90 m_HeaderPart.BodyOffset = m_File.Tell();
97 ASDCP::h__ASDCPReader::InitInfo()
99 Result_t result = ASDCP::MXF::TrackFileReader<OPAtomHeader, OPAtomIndexFooter>::InitInfo();
101 if( KM_SUCCESS(result) )
103 InterchangeObject* Object;
105 m_Info.LabelSetType = LS_MXF_UNKNOWN;
107 if ( m_HeaderPart.OperationalPattern.ExactMatch(MXFInterop_OPAtom_Entry().ul) )
109 m_Info.LabelSetType = LS_MXF_INTEROP;
111 else if ( m_HeaderPart.OperationalPattern.ExactMatch(SMPTE_390_OPAtom_Entry().ul) )
113 m_Info.LabelSetType = LS_MXF_SMPTE;
120 // AS-DCP method of populating the in-memory index
122 ASDCP::h__ASDCPReader::InitMXFIndex()
124 if ( ! m_File.IsOpen() )
127 Result_t result = m_File.Seek(m_HeaderPart.FooterPartition);
129 if ( ASDCP_SUCCESS(result) )
131 m_FooterPart.m_Lookup = &m_HeaderPart.m_Primer;
132 result = m_FooterPart.InitFromFile(m_File);
135 if ( ASDCP_SUCCESS(result) )
136 m_File.Seek(m_HeaderPart.BodyOffset);
142 // AS-DCP method of reading a plaintext or encrypted frame
144 ASDCP::h__ASDCPReader::ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
145 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
147 return ASDCP::MXF::TrackFileReader<OPAtomHeader, OPAtomIndexFooter>::ReadEKLVFrame(m_HeaderPart, FrameNum, FrameBuf,
148 EssenceUL, Ctx, HMAC);
152 //------------------------------------------------------------------------------------------
158 ASDCP::KLReader::ReadKLFromFile(Kumu::FileReader& Reader)
161 ui32_t header_length = SMPTE_UL_LENGTH + MXF_BER_LENGTH;
162 Result_t result = Reader.Read(m_KeyBuf, header_length, &read_count);
164 if ( ASDCP_FAILURE(result) )
167 if ( read_count != header_length )
168 return RESULT_READFAIL;
170 const byte_t* ber_start = m_KeyBuf + SMPTE_UL_LENGTH;
172 if ( ( *ber_start & 0x80 ) == 0 )
174 DefaultLogSink().Error("BER encoding error.\n");
175 return RESULT_FORMAT;
178 ui8_t ber_size = ( *ber_start & 0x0f ) + 1;
182 DefaultLogSink().Error("BER size encoding error.\n");
183 return RESULT_FORMAT;
186 if ( ber_size < MXF_BER_LENGTH )
188 DefaultLogSink().Error("BER size %d shorter than AS-DCP minimum %d.\n",
189 ber_size, MXF_BER_LENGTH);
190 return RESULT_FORMAT;
193 if ( ber_size > MXF_BER_LENGTH )
195 ui32_t diff = ber_size - MXF_BER_LENGTH;
196 assert((SMPTE_UL_LENGTH + MXF_BER_LENGTH + diff) <= (SMPTE_UL_LENGTH * 2));
197 result = Reader.Read(m_KeyBuf + SMPTE_UL_LENGTH + MXF_BER_LENGTH, diff, &read_count);
199 if ( ASDCP_FAILURE(result) )
202 if ( read_count != diff )
203 return RESULT_READFAIL;
205 header_length += diff;
208 return InitFromBuffer(m_KeyBuf, header_length);
211 // base subroutine for reading a KLV packet, assumes file position is at the first byte of the packet
213 ASDCP::Read_EKLV_Packet(Kumu::FileReader& File, const ASDCP::Dictionary& Dict, const MXF::OPAtomHeader& HeaderPart,
214 const ASDCP::WriterInfo& Info, Kumu::fpos_t& LastPosition, ASDCP::FrameBuffer& CtFrameBuf,
215 ui32_t FrameNum, ui32_t SequenceNum, ASDCP::FrameBuffer& FrameBuf,
216 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
219 Result_t result = Reader.ReadKLFromFile(File);
221 if ( KM_FAILURE(result) )
224 UL Key(Reader.Key());
225 ui64_t PacketLength = Reader.Length();
226 LastPosition = LastPosition + Reader.KLLength() + PacketLength;
228 if ( Key.MatchIgnoreStream(Dict.ul(MDD_CryptEssence)) ) // ignore the stream numbers
230 if ( ! Info.EncryptedEssence )
232 DefaultLogSink().Error("EKLV packet found, no Cryptographic Context in header.\n");
233 return RESULT_FORMAT;
236 // read encrypted triplet value into internal buffer
237 assert(PacketLength <= 0xFFFFFFFFL);
238 CtFrameBuf.Capacity((ui32_t) PacketLength);
240 result = File.Read(CtFrameBuf.Data(), (ui32_t) 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 CtFrameBuf.Size((ui32_t) PacketLength);
253 // should be const but mxflib::ReadBER is not
254 byte_t* ess_p = 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, 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 ( ! UL(ess_p).MatchIgnoreStream(EssenceUL) ) // 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 + (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) && Info.UsesHMAC && HMAC )
339 IntegrityPack IntPack;
340 result = IntPack.TestValues(TmpWrapper, Info.AssetUUID, SequenceNum, 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.FrameNumber(FrameNum);
356 FrameBuf.SourceLength(SourceLength);
357 FrameBuf.PlaintextOffset(PlaintextOffset);
360 else if ( Key.MatchIgnoreStream(EssenceUL) ) // ignore the stream number
361 { // read plaintext frame
362 if ( FrameBuf.Capacity() < PacketLength )
364 char intbuf[IntBufferLen];
365 DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %s\n",
366 FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
367 return RESULT_SMALLBUF;
370 // read the data into the supplied buffer
372 assert(PacketLength <= 0xFFFFFFFFL);
373 result = File.Read(FrameBuf.Data(), (ui32_t) PacketLength, &read_count);
375 if ( ASDCP_FAILURE(result) )
378 if ( read_count != PacketLength )
380 char intbuf1[IntBufferLen];
381 char intbuf2[IntBufferLen];
382 DefaultLogSink().Error("read_count: %s != FrameLength: %s\n",
383 ui64sz(read_count, intbuf1),
384 ui64sz(PacketLength, intbuf2) );
386 return RESULT_READFAIL;
389 FrameBuf.FrameNumber(FrameNum);
390 FrameBuf.Size(read_count);
394 char strbuf[IntBufferLen];
395 const MDDEntry* Entry = Dict.FindUL(Key.Value());
397 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
399 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
401 return RESULT_FORMAT;