2 Copyright (c) 2004-2013, 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_OP1aHeader = new ASDCP::MXF::OP1aHeader(sg_dict);
55 g_OPAtomIndexFooter = new ASDCP::MXF::OPAtomIndexFooter(sg_dict);
56 g_RIP = new ASDCP::MXF::RIP(sg_dict);
57 sg_DefaultMDTypesInit = true;
63 //------------------------------------------------------------------------------------------
67 ASDCP::h__ASDCPReader::h__ASDCPReader(const Dictionary& d) : MXF::TrackFileReader<OP1aHeader, OPAtomIndexFooter>(d), m_BodyPart(m_Dict) {}
68 ASDCP::h__ASDCPReader::~h__ASDCPReader() {}
71 // AS-DCP method of opening an MXF file for read
73 ASDCP::h__ASDCPReader::OpenMXFRead(const char* filename)
75 Result_t result = ASDCP::MXF::TrackFileReader<OP1aHeader, OPAtomIndexFooter>::OpenMXFRead(filename);
77 if ( KM_SUCCESS(result) )
78 result = ASDCP::MXF::TrackFileReader<OP1aHeader, OPAtomIndexFooter>::InitInfo();
80 if( KM_SUCCESS(result) )
83 InterchangeObject* Object;
85 m_Info.LabelSetType = LS_MXF_UNKNOWN;
87 if ( m_HeaderPart.OperationalPattern.ExactMatch(MXFInterop_OPAtom_Entry().ul) )
89 m_Info.LabelSetType = LS_MXF_INTEROP;
91 else if ( m_HeaderPart.OperationalPattern.ExactMatch(SMPTE_390_OPAtom_Entry().ul) )
93 m_Info.LabelSetType = LS_MXF_SMPTE;
97 char strbuf[IdentBufferLen];
98 const MDDEntry* Entry = m_Dict->FindUL(m_HeaderPart.OperationalPattern.Value());
102 DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n",
103 m_HeaderPart.OperationalPattern.EncodeString(strbuf, IdentBufferLen));
107 DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n", Entry->name);
112 if ( m_RIP.PairArray.size() < 2 )
114 // OP-Atom states that there will be either two or three partitions:
115 // one closed header and one closed footer with an optional body
116 // SMPTE 429-5 files may have many partitions, see SMPTE ST 410.
117 DefaultLogSink().Warn("RIP entry count is less than 2: %u\n", m_RIP.PairArray.size());
119 else if ( m_RIP.PairArray.size() > 2 )
121 // if this is a three partition file, go to the body
122 // partition and read the partition pack
123 Array<RIP::Pair>::iterator r_i = m_RIP.PairArray.begin();
125 m_File.Seek((*r_i).ByteOffset);
126 result = m_BodyPart.InitFromFile(m_File);
128 if( ASDCP_FAILURE(result) )
130 DefaultLogSink().Error("ASDCP::h__ASDCPReader::OpenMXFRead, m_BodyPart.InitFromFile failed\n");
133 else if ( m_RIP.PairArray.front().ByteOffset != 0 )
135 DefaultLogSink().Error("First Partition in RIP is not at offset 0.\n");
136 result = RESULT_FORMAT;
140 if ( KM_SUCCESS(result) )
142 m_HeaderPart.BodyOffset = m_File.Tell();
144 result = m_File.Seek(m_HeaderPart.FooterPartition);
146 if ( ASDCP_SUCCESS(result) )
148 m_IndexAccess.m_Lookup = &m_HeaderPart.m_Primer;
149 result = m_IndexAccess.InitFromFile(m_File);
153 m_File.Seek(m_HeaderPart.BodyOffset);
157 // AS-DCP method of reading a plaintext or encrypted frame
159 ASDCP::h__ASDCPReader::ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
160 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
162 return ASDCP::MXF::TrackFileReader<OP1aHeader, OPAtomIndexFooter>::ReadEKLVFrame(m_HeaderPart, FrameNum, FrameBuf,
163 EssenceUL, Ctx, HMAC);
167 ASDCP::h__ASDCPReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset,
168 i8_t& temporalOffset, i8_t& keyFrameOffset)
170 return ASDCP::MXF::TrackFileReader<OP1aHeader, OPAtomIndexFooter>::LocateFrame(m_HeaderPart, FrameNum,
171 streamOffset, temporalOffset, keyFrameOffset);
175 //------------------------------------------------------------------------------------------
181 ASDCP::KLReader::ReadKLFromFile(Kumu::FileReader& Reader)
184 ui32_t header_length = SMPTE_UL_LENGTH + MXF_BER_LENGTH;
185 Result_t result = Reader.Read(m_KeyBuf, header_length, &read_count);
187 if ( ASDCP_FAILURE(result) )
190 if ( read_count != header_length )
191 return RESULT_READFAIL;
193 const byte_t* ber_start = m_KeyBuf + SMPTE_UL_LENGTH;
195 if ( ( *ber_start & 0x80 ) == 0 )
197 DefaultLogSink().Error("BER encoding error.\n");
198 return RESULT_FORMAT;
201 ui8_t ber_size = ( *ber_start & 0x0f ) + 1;
205 DefaultLogSink().Error("BER size encoding error.\n");
206 return RESULT_FORMAT;
209 if ( ber_size < MXF_BER_LENGTH )
211 DefaultLogSink().Error("BER size %d shorter than AS-DCP minimum %d.\n",
212 ber_size, MXF_BER_LENGTH);
213 return RESULT_FORMAT;
216 if ( ber_size > MXF_BER_LENGTH )
218 ui32_t diff = ber_size - MXF_BER_LENGTH;
219 assert((SMPTE_UL_LENGTH + MXF_BER_LENGTH + diff) <= (SMPTE_UL_LENGTH * 2));
220 result = Reader.Read(m_KeyBuf + SMPTE_UL_LENGTH + MXF_BER_LENGTH, diff, &read_count);
222 if ( ASDCP_FAILURE(result) )
225 if ( read_count != diff )
226 return RESULT_READFAIL;
228 header_length += diff;
231 return InitFromBuffer(m_KeyBuf, header_length);
234 // base subroutine for reading a KLV packet, assumes file position is at the first byte of the packet
236 ASDCP::Read_EKLV_Packet(Kumu::FileReader& File, const ASDCP::Dictionary& Dict, const MXF::OP1aHeader& HeaderPart,
237 const ASDCP::WriterInfo& Info, Kumu::fpos_t& LastPosition, ASDCP::FrameBuffer& CtFrameBuf,
238 ui32_t FrameNum, ui32_t SequenceNum, ASDCP::FrameBuffer& FrameBuf,
239 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
242 Result_t result = Reader.ReadKLFromFile(File);
244 if ( KM_FAILURE(result) )
247 UL Key(Reader.Key());
248 ui64_t PacketLength = Reader.Length();
249 LastPosition = LastPosition + Reader.KLLength() + PacketLength;
251 if ( Key.MatchIgnoreStream(Dict.ul(MDD_CryptEssence)) ) // ignore the stream numbers
253 if ( ! Info.EncryptedEssence )
255 DefaultLogSink().Error("EKLV packet found, no Cryptographic Context in header.\n");
256 return RESULT_FORMAT;
259 // read encrypted triplet value into internal buffer
260 assert(PacketLength <= 0xFFFFFFFFL);
261 CtFrameBuf.Capacity((ui32_t) PacketLength);
263 result = File.Read(CtFrameBuf.Data(), (ui32_t) PacketLength, &read_count);
265 if ( ASDCP_FAILURE(result) )
268 if ( read_count != PacketLength )
270 DefaultLogSink().Error("read length is smaller than EKLV packet length.\n");
271 return RESULT_FORMAT;
274 CtFrameBuf.Size((ui32_t) PacketLength);
276 // should be const but mxflib::ReadBER is not
277 byte_t* ess_p = CtFrameBuf.Data();
279 // read context ID length
280 if ( ! Kumu::read_test_BER(&ess_p, UUIDlen) )
281 return RESULT_FORMAT;
283 // test the context ID
284 if ( memcmp(ess_p, Info.ContextID, UUIDlen) != 0 )
286 DefaultLogSink().Error("Packet's Cryptographic Context ID does not match the header.\n");
287 return RESULT_FORMAT;
291 // read PlaintextOffset length
292 if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
293 return RESULT_FORMAT;
295 ui32_t PlaintextOffset = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
296 ess_p += sizeof(ui64_t);
298 // read essence UL length
299 if ( ! Kumu::read_test_BER(&ess_p, SMPTE_UL_LENGTH) )
300 return RESULT_FORMAT;
303 if ( ! UL(ess_p).MatchIgnoreStream(EssenceUL) ) // ignore the stream number
305 char strbuf[IntBufferLen];
306 const MDDEntry* Entry = Dict.FindUL(Key.Value());
310 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
314 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
317 return RESULT_FORMAT;
320 ess_p += SMPTE_UL_LENGTH;
322 // read SourceLength length
323 if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
324 return RESULT_FORMAT;
326 ui32_t SourceLength = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
327 ess_p += sizeof(ui64_t);
328 assert(SourceLength);
330 if ( FrameBuf.Capacity() < SourceLength )
332 DefaultLogSink().Error("FrameBuf.Capacity: %u SourceLength: %u\n", FrameBuf.Capacity(), SourceLength);
333 return RESULT_SMALLBUF;
336 ui32_t esv_length = calc_esv_length(SourceLength, PlaintextOffset);
339 if ( ! Kumu::read_test_BER(&ess_p, esv_length) )
341 DefaultLogSink().Error("read_test_BER did not return %u\n", esv_length);
342 return RESULT_FORMAT;
345 ui32_t tmp_len = esv_length + (Info.UsesHMAC ? klv_intpack_size : 0);
347 if ( PacketLength < tmp_len )
349 DefaultLogSink().Error("Frame length is larger than EKLV packet length.\n");
350 return RESULT_FORMAT;
355 // wrap the pointer and length as a FrameBuffer for use by
356 // DecryptFrameBuffer() and TestValues()
357 FrameBuffer TmpWrapper;
358 TmpWrapper.SetData(ess_p, tmp_len);
359 TmpWrapper.Size(tmp_len);
360 TmpWrapper.SourceLength(SourceLength);
361 TmpWrapper.PlaintextOffset(PlaintextOffset);
363 result = DecryptFrameBuffer(TmpWrapper, FrameBuf, Ctx);
364 FrameBuf.FrameNumber(FrameNum);
366 // detect and test integrity pack
367 if ( ASDCP_SUCCESS(result) && Info.UsesHMAC && HMAC )
369 IntegrityPack IntPack;
370 result = IntPack.TestValues(TmpWrapper, Info.AssetUUID, SequenceNum, HMAC);
373 else // return ciphertext to caller
375 if ( FrameBuf.Capacity() < tmp_len )
377 char intbuf[IntBufferLen];
378 DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %s\n",
379 FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
380 return RESULT_SMALLBUF;
383 memcpy(FrameBuf.Data(), ess_p, tmp_len);
384 FrameBuf.Size(tmp_len);
385 FrameBuf.FrameNumber(FrameNum);
386 FrameBuf.SourceLength(SourceLength);
387 FrameBuf.PlaintextOffset(PlaintextOffset);
390 else if ( Key.MatchIgnoreStream(EssenceUL) ) // ignore the stream number
391 { // read plaintext frame
392 if ( FrameBuf.Capacity() < PacketLength )
394 char intbuf[IntBufferLen];
395 DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %s\n",
396 FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
397 return RESULT_SMALLBUF;
400 // read the data into the supplied buffer
402 assert(PacketLength <= 0xFFFFFFFFL);
403 result = File.Read(FrameBuf.Data(), (ui32_t) PacketLength, &read_count);
405 if ( ASDCP_FAILURE(result) )
408 if ( read_count != PacketLength )
410 char intbuf1[IntBufferLen];
411 char intbuf2[IntBufferLen];
412 DefaultLogSink().Error("read_count: %s != FrameLength: %s\n",
413 ui64sz(read_count, intbuf1),
414 ui64sz(PacketLength, intbuf2) );
416 return RESULT_READFAIL;
419 FrameBuf.FrameNumber(FrameNum);
420 FrameBuf.Size(read_count);
424 char strbuf[IntBufferLen];
425 const MDDEntry* Entry = Dict.FindUL(Key.Value());
429 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
433 DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
436 return RESULT_FORMAT;