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