Reverting const accessor for class optional_property
[asdcplib.git] / src / h__Reader.cpp
1 /*
2 Copyright (c) 2004-2015, John Hurst
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
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.
15
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.
26 */
27 /*! \file    h__Reader.cpp
28     \version $Id$
29     \brief   MXF file reader base class
30 */
31
32 #define DEFAULT_MD_DECL
33 #include "AS_DCP_internal.h"
34 #include "KLV.h"
35
36 using namespace ASDCP;
37 using namespace ASDCP::MXF;
38
39 static Kumu::Mutex sg_DefaultMDInitLock;
40 static bool        sg_DefaultMDTypesInit = false;
41 static const ASDCP::Dictionary *sg_dict = 0;
42
43 //
44 void
45 ASDCP::default_md_object_init()
46 {
47   if ( ! sg_DefaultMDTypesInit )
48     {
49       Kumu::AutoMutex BlockLock(sg_DefaultMDInitLock);
50
51       if ( ! sg_DefaultMDTypesInit )
52         {
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;
58         }
59     }
60 }
61
62
63 //------------------------------------------------------------------------------------------
64 //
65
66 //
67 ASDCP::h__ASDCPReader::h__ASDCPReader(const Dictionary& d) : MXF::TrackFileReader<OP1aHeader, OPAtomIndexFooter>(d), m_BodyPart(m_Dict) {}
68 ASDCP::h__ASDCPReader::~h__ASDCPReader() {}
69
70
71 // AS-DCP method of opening an MXF file for read
72 Result_t
73 ASDCP::h__ASDCPReader::OpenMXFRead(const std::string& filename)
74 {
75   Result_t result = ASDCP::MXF::TrackFileReader<OP1aHeader, OPAtomIndexFooter>::OpenMXFRead(filename);
76
77   if ( KM_SUCCESS(result) )
78     result = ASDCP::MXF::TrackFileReader<OP1aHeader, OPAtomIndexFooter>::InitInfo();
79
80   if( KM_SUCCESS(result) )
81     {
82       //
83       InterchangeObject* Object;
84
85       m_Info.LabelSetType = LS_MXF_UNKNOWN;
86
87       if ( m_HeaderPart.OperationalPattern.MatchExact(MXFInterop_OPAtom_Entry().ul) )
88         {
89           m_Info.LabelSetType = LS_MXF_INTEROP;
90         }
91       else if ( m_HeaderPart.OperationalPattern.MatchExact(SMPTE_390_OPAtom_Entry().ul) )
92         {
93           m_Info.LabelSetType = LS_MXF_SMPTE;
94         }
95       else
96         {
97           char strbuf[IdentBufferLen];
98           const MDDEntry* Entry = m_Dict->FindULExact(m_HeaderPart.OperationalPattern.Value());
99
100           if ( Entry == 0 )
101             {
102               DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n",
103                                     m_HeaderPart.OperationalPattern.EncodeString(strbuf, IdentBufferLen));
104             }
105           else
106             {
107               DefaultLogSink().Warn("Operational pattern is not OP-Atom: %s\n", Entry->name);
108             }
109         }
110
111       if ( !m_RIP.PairArray.empty() && m_RIP.PairArray.front().ByteOffset != 0 )
112         {
113           DefaultLogSink().Error("First Partition in RIP is not at offset 0.\n");
114           result = RESULT_FORMAT;
115         }
116
117       //
118       if ( m_RIP.PairArray.size() < 2 )
119         {
120           // OP-Atom states that there will be either two or three partitions:
121           // one closed header and one closed footer with an optional body
122           // SMPTE 429-5 files may have many partitions, see SMPTE ST 410.
123           DefaultLogSink().Warn("RIP entry count is less than 2: %u\n", m_RIP.PairArray.size());
124         }
125       else if ( m_RIP.PairArray.size() > 2 )
126         {
127           // if this is a three partition file, go to the body
128           // partition and read the partition pack
129           RIP::const_pair_iterator r_i = m_RIP.PairArray.begin();
130           r_i++;
131           m_File.Seek((*r_i).ByteOffset);
132           result = m_BodyPart.InitFromFile(m_File);
133
134           if( ASDCP_FAILURE(result) )
135             {
136               DefaultLogSink().Error("ASDCP::h__ASDCPReader::OpenMXFRead, m_BodyPart.InitFromFile failed\n");
137             }
138         }
139     }
140
141   if ( KM_SUCCESS(result) )
142     {
143       // this position will be at either
144       //     a) the spot in the header partition where essence units appear, or
145       //     b) right after the body partition header (where essence units appear)
146       m_HeaderPart.BodyOffset = m_File.Tell();
147
148       result = m_File.Seek(m_HeaderPart.FooterPartition);
149
150       if ( ASDCP_SUCCESS(result) )
151         {
152           m_IndexAccess.m_Lookup = &m_HeaderPart.m_Primer;
153           result = m_IndexAccess.InitFromFile(m_File);
154         }
155     }
156
157   m_File.Seek(m_HeaderPart.BodyOffset);
158   return result;
159 }
160
161 // AS-DCP method of reading a plaintext or encrypted frame
162 Result_t
163 ASDCP::h__ASDCPReader::ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
164                                      const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
165 {
166   return ASDCP::MXF::TrackFileReader<OP1aHeader, OPAtomIndexFooter>::ReadEKLVFrame(m_HeaderPart.BodyOffset, FrameNum, FrameBuf,
167                                                                                      EssenceUL, Ctx, HMAC);
168 }
169
170 Result_t
171 ASDCP::h__ASDCPReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset,
172                            i8_t& temporalOffset, i8_t& keyFrameOffset)
173 {
174   return ASDCP::MXF::TrackFileReader<OP1aHeader, OPAtomIndexFooter>::LocateFrame(m_HeaderPart.BodyOffset, FrameNum,
175                                                                                    streamOffset, temporalOffset, keyFrameOffset);
176 }
177
178
179 //------------------------------------------------------------------------------------------
180 //
181
182
183 //
184 Result_t
185 ASDCP::KLReader::ReadKLFromFile(Kumu::FileReader& Reader)
186 {
187   ui32_t read_count;
188   ui32_t header_length = SMPTE_UL_LENGTH + MXF_BER_LENGTH;
189   Result_t result = Reader.Read(m_KeyBuf, header_length, &read_count);
190
191   if ( ASDCP_FAILURE(result) )
192     return result;
193
194   if ( read_count != header_length )
195     return RESULT_READFAIL;
196
197   const byte_t* ber_start = m_KeyBuf + SMPTE_UL_LENGTH;
198
199   if ( ( *ber_start & 0x80 ) == 0 )
200     {
201       DefaultLogSink().Error("BER encoding error.\n");
202       return RESULT_FORMAT;
203     }
204
205   ui8_t ber_size = ( *ber_start & 0x0f ) + 1;
206
207   if ( ber_size > 9 )
208     {
209       DefaultLogSink().Error("BER size encoding error.\n");
210       return RESULT_FORMAT;
211     }
212
213   if ( ber_size < MXF_BER_LENGTH )
214     {
215       DefaultLogSink().Error("BER size %d shorter than AS-DCP/AS-02 minimum %d.\n",
216                              ber_size, MXF_BER_LENGTH);
217       return RESULT_FORMAT;
218     }
219
220   if ( ber_size > MXF_BER_LENGTH )
221     {
222       ui32_t diff = ber_size - MXF_BER_LENGTH;
223       assert((SMPTE_UL_LENGTH + MXF_BER_LENGTH + diff) <= (SMPTE_UL_LENGTH * 2));
224       result = Reader.Read(m_KeyBuf + SMPTE_UL_LENGTH + MXF_BER_LENGTH, diff, &read_count);
225
226       if ( ASDCP_FAILURE(result) )
227         return result;
228
229       if ( read_count != diff )
230         return RESULT_READFAIL;
231
232       header_length += diff;
233     }
234
235   return InitFromBuffer(m_KeyBuf, header_length);
236 }
237
238
239 //------------------------------------------------------------------------------------------
240 //
241
242
243 // base subroutine for reading a KLV packet, assumes file position is at the first byte of the packet
244 Result_t
245 ASDCP::Read_EKLV_Packet(Kumu::FileReader& File, const ASDCP::Dictionary& Dict,
246                         const ASDCP::WriterInfo& Info, Kumu::fpos_t& LastPosition, ASDCP::FrameBuffer& CtFrameBuf,
247                         ui32_t FrameNum, ui32_t SequenceNum, ASDCP::FrameBuffer& FrameBuf,
248                         const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
249 {
250   KLReader Reader;
251   Result_t result = Reader.ReadKLFromFile(File);
252
253   if ( KM_FAILURE(result) )
254     return result;
255
256   UL Key(Reader.Key());
257   ui64_t PacketLength = Reader.Length();
258   LastPosition = LastPosition + Reader.KLLength() + PacketLength;
259
260   if ( Key.MatchIgnoreStream(Dict.ul(MDD_CryptEssence)) )  // ignore the stream numbers
261     {
262       if ( ! Info.EncryptedEssence )
263         {
264           DefaultLogSink().Error("EKLV packet found, no Cryptographic Context in header.\n");
265           return RESULT_FORMAT;
266         }
267
268       // read encrypted triplet value into internal buffer
269       assert(PacketLength <= 0xFFFFFFFFL);
270       CtFrameBuf.Capacity((ui32_t) PacketLength);
271       ui32_t read_count;
272       result = File.Read(CtFrameBuf.Data(), (ui32_t) PacketLength, &read_count);
273
274       if ( ASDCP_FAILURE(result) )
275         return result;
276
277       if ( read_count != PacketLength )
278         {
279           DefaultLogSink().Error("read length is smaller than EKLV packet length.\n");
280           return RESULT_FORMAT;
281         }
282
283       CtFrameBuf.Size((ui32_t) PacketLength);
284
285       // should be const but mxflib::ReadBER is not
286       byte_t* ess_p = CtFrameBuf.Data();
287
288       // read context ID length
289       if ( ! Kumu::read_test_BER(&ess_p, UUIDlen) )
290         return RESULT_FORMAT;
291
292       // test the context ID
293       if ( memcmp(ess_p, Info.ContextID, UUIDlen) != 0 )
294         {
295           DefaultLogSink().Error("Packet's Cryptographic Context ID does not match the header.\n");
296           return RESULT_FORMAT;
297         }
298       ess_p += UUIDlen;
299
300       // read PlaintextOffset length
301       if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
302         return RESULT_FORMAT;
303
304       ui32_t PlaintextOffset = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
305       ess_p += sizeof(ui64_t);
306
307       // read essence UL length
308       if ( ! Kumu::read_test_BER(&ess_p, SMPTE_UL_LENGTH) )
309         return RESULT_FORMAT;
310
311       // test essence UL
312       if ( ! UL(ess_p).MatchIgnoreStream(EssenceUL) ) // ignore the stream number
313         {
314           char strbuf[IntBufferLen];
315           const MDDEntry* Entry = Dict.FindULAnyVersion(Key.Value());
316
317           if ( Entry == 0 )
318             {
319               DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
320             }
321           else
322             {
323               DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
324             }
325
326           return RESULT_FORMAT;
327         }
328
329       ess_p += SMPTE_UL_LENGTH;
330
331       // read SourceLength length
332       if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
333         return RESULT_FORMAT;
334
335       ui32_t SourceLength = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
336       ess_p += sizeof(ui64_t);
337       assert(SourceLength);
338           
339       if ( FrameBuf.Capacity() < SourceLength )
340         {
341           DefaultLogSink().Error("FrameBuf.Capacity: %u SourceLength: %u\n", FrameBuf.Capacity(), SourceLength);
342           return RESULT_SMALLBUF;
343         }
344
345       ui32_t esv_length = calc_esv_length(SourceLength, PlaintextOffset);
346
347       // read ESV length
348       if ( ! Kumu::read_test_BER(&ess_p, esv_length) )
349         {
350           DefaultLogSink().Error("read_test_BER did not return %u\n", esv_length);
351           return RESULT_FORMAT;
352         }
353
354       ui32_t tmp_len = esv_length + (Info.UsesHMAC ? klv_intpack_size : 0);
355
356       if ( PacketLength < tmp_len )
357         {
358           DefaultLogSink().Error("Frame length is larger than EKLV packet length.\n");
359           return RESULT_FORMAT;
360         }
361
362       if ( Ctx )
363         {
364           // wrap the pointer and length as a FrameBuffer for use by
365           // DecryptFrameBuffer() and TestValues()
366           FrameBuffer TmpWrapper;
367           TmpWrapper.SetData(ess_p, tmp_len);
368           TmpWrapper.Size(tmp_len);
369           TmpWrapper.SourceLength(SourceLength);
370           TmpWrapper.PlaintextOffset(PlaintextOffset);
371
372           result = DecryptFrameBuffer(TmpWrapper, FrameBuf, Ctx);
373           FrameBuf.FrameNumber(FrameNum);
374   
375           // detect and test integrity pack
376           if ( ASDCP_SUCCESS(result) && Info.UsesHMAC && HMAC )
377             {
378               IntegrityPack IntPack;
379               result = IntPack.TestValues(TmpWrapper, Info.AssetUUID, SequenceNum, HMAC);
380             }
381         }
382       else // return ciphertext to caller
383         {
384           if ( FrameBuf.Capacity() < tmp_len )
385             {
386               char intbuf[IntBufferLen];
387               DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %s\n",
388                                      FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
389               return RESULT_SMALLBUF;
390             }
391
392           memcpy(FrameBuf.Data(), ess_p, tmp_len);
393           FrameBuf.Size(tmp_len);
394           FrameBuf.FrameNumber(FrameNum);
395           FrameBuf.SourceLength(SourceLength);
396           FrameBuf.PlaintextOffset(PlaintextOffset);
397         }
398     }
399   else if ( Key.MatchIgnoreStream(EssenceUL) ) // ignore the stream number
400     { // read plaintext frame
401        if ( FrameBuf.Capacity() < PacketLength )
402         {
403           char intbuf[IntBufferLen];
404           DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %s\n",
405                                  FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
406           return RESULT_SMALLBUF;
407         }
408
409       // read the data into the supplied buffer
410       ui32_t read_count;
411       assert(PacketLength <= 0xFFFFFFFFL);
412       result = File.Read(FrameBuf.Data(), (ui32_t) PacketLength, &read_count);
413           
414       if ( ASDCP_FAILURE(result) )
415         return result;
416
417       if ( read_count != PacketLength )
418         {
419           char intbuf1[IntBufferLen];
420           char intbuf2[IntBufferLen];
421           DefaultLogSink().Error("read_count: %s != FrameLength: %s\n",
422                                  ui64sz(read_count, intbuf1),
423                                  ui64sz(PacketLength, intbuf2) );
424           
425           return RESULT_READFAIL;
426         }
427
428       FrameBuf.FrameNumber(FrameNum);
429       FrameBuf.Size(read_count);
430     }
431   else
432     {
433       char strbuf[IntBufferLen];
434       const MDDEntry* Entry = Dict.FindULAnyVersion(Key.Value());
435
436       if ( Entry == 0 )
437         {
438           DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
439         }
440       else
441         {
442           DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
443         }
444
445       return RESULT_FORMAT;
446     }
447
448   return result;
449 }
450
451
452 //
453 // end h__Reader.cpp
454 //