Modify to test email notice.
[asdcplib.git] / src / h__02_Reader.cpp
1 /*
2 Copyright (c) 2011-2018, Robert Scheler, Heiko Sparenberg Fraunhofer IIS,
3 John Hurst
4
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions
9 are met:
10 1. Redistributions of source code must retain the above copyright
11    notice, this list of conditions and the following disclaimer.
12 2. Redistributions in binary form must reproduce the above copyright
13    notice, this list of conditions and the following disclaimer in the
14    documentation and/or other materials provided with the distribution.
15 3. The name of the author may not be used to endorse or promote products
16    derived from this software without specific prior written permission.
17
18 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */ 
29 /*! \file    h__02_Reader.cpp
30   \version $Id$
31   \brief   MXF file reader base class
32 */
33
34 #define DEFAULT_02_MD_DECL
35 #include "AS_02_internal.h"
36
37 using namespace ASDCP;
38 using namespace ASDCP::MXF;
39
40 static Kumu::Mutex sg_DefaultMDInitLock;
41 static bool        sg_DefaultMDTypesInit = false;
42 static const ASDCP::Dictionary *sg_dict;
43
44 //
45 void
46 AS_02::default_md_object_init()
47 {
48   if ( ! sg_DefaultMDTypesInit )
49     {
50       Kumu::AutoMutex BlockLock(sg_DefaultMDInitLock);
51
52       if ( ! sg_DefaultMDTypesInit )
53         {
54           sg_dict = &DefaultSMPTEDict();
55           g_AS02IndexReader = new AS_02::MXF::AS02IndexReader(sg_dict);
56           sg_DefaultMDTypesInit = true;
57         }
58     }
59 }
60
61
62 //---------------------------------------------------------------------------------
63 //
64
65     
66 AS_02::MXF::AS02IndexReader::AS02IndexReader(const ASDCP::Dictionary*& d) :
67   m_Duration(0), m_BytesPerEditUnit(0),
68   ASDCP::MXF::Partition(d), m_Dict(d) {}
69
70 AS_02::MXF::AS02IndexReader::~AS02IndexReader() {}
71
72 //    
73 Result_t
74 AS_02::MXF::AS02IndexReader::InitFromFile(const Kumu::FileReader& reader, const ASDCP::MXF::RIP& rip, const bool has_header_essence)
75 {
76   typedef std::list<Kumu::mem_ptr<ASDCP::MXF::Partition> > body_part_array_t;
77   body_part_array_t body_part_array;
78   body_part_array_t::const_iterator body_part_iter;
79
80   RIP::const_pair_iterator i;
81   Result_t result = m_IndexSegmentData.Capacity(128*Kumu::Kilobyte); // will be grown if needed
82   ui32_t first_body_sid = 0;
83
84   // create a list of body parts and index parts
85   for ( i = rip.PairArray.begin(); KM_SUCCESS(result) && i != rip.PairArray.end(); ++i )
86     {
87       if ( i->BodySID == 0 )
88         continue;
89
90       if ( first_body_sid == 0 )
91         {
92           first_body_sid = i->BodySID;
93         }
94       else if ( i->BodySID != first_body_sid )
95         {
96           //      DefaultLogSink().Debug("The index assembler is ignoring BodySID %d.\n", i->BodySID);
97           continue;
98         }
99
100       reader.Seek(i->ByteOffset);
101       ASDCP::MXF::Partition *this_partition = new ASDCP::MXF::Partition(m_Dict);
102       assert(this_partition);
103
104       result = this_partition->InitFromFile(reader);
105
106       if ( KM_FAILURE(result) )
107         {
108           delete this_partition;
109           return result;
110         }
111
112       if ( this_partition->BodySID != i->BodySID )
113         {
114           DefaultLogSink().Error("Partition BodySID %d does not match RIP BodySID %d.\n",
115                                  this_partition->BodySID, i->BodySID);
116         }
117
118       body_part_array.push_back(0);
119       body_part_array.back().set(this_partition);
120     }
121
122   if ( body_part_array.empty() )
123     {
124       DefaultLogSink().Error("File has no partitions with essence data.\n");
125       return RESULT_AS02_FORMAT;
126     }
127
128   body_part_iter = body_part_array.begin();
129
130   for ( i = rip.PairArray.begin(); KM_SUCCESS(result) && i != rip.PairArray.end(); ++i )
131     {
132       reader.Seek(i->ByteOffset);
133       ASDCP::MXF::Partition plain_part(m_Dict);
134       result = plain_part.InitFromFile(reader);
135
136       if ( KM_FAILURE(result) )
137         return result;
138
139       if ( plain_part.IndexByteCount > 0 )
140         {
141           if ( body_part_iter == body_part_array.end() )
142             {
143               DefaultLogSink().Error("Index and Body partitions do not match.\n");
144               break;
145             }
146
147           if ( plain_part.ThisPartition == plain_part.FooterPartition )
148             {
149               DefaultLogSink().Warn("File footer partition contains index data.\n");
150             }
151
152           // slurp up the remainder of the partition
153           ui32_t read_count = 0;
154
155           assert (plain_part.IndexByteCount <= 0xFFFFFFFFL);
156           ui32_t bytes_this_partition = (ui32_t)plain_part.IndexByteCount;
157
158           result = m_IndexSegmentData.Capacity(m_IndexSegmentData.Length() + bytes_this_partition);
159
160           if ( KM_SUCCESS(result) )
161             result = reader.Read(m_IndexSegmentData.Data() + m_IndexSegmentData.Length(),
162                                  bytes_this_partition, &read_count);
163
164           if ( KM_SUCCESS(result) && read_count != bytes_this_partition )
165             {
166               DefaultLogSink().Error("Short read of index partition: got %u, expecting %u\n",
167                                      read_count, bytes_this_partition);
168               return RESULT_AS02_FORMAT;
169             }
170
171           if ( KM_SUCCESS(result) )
172             {
173               ui64_t current_body_offset = 0;
174               ui64_t current_ec_offset = 0;
175               assert(body_part_iter != body_part_array.end());
176
177               assert(!body_part_iter->empty());
178               ASDCP::MXF::Partition *tmp_partition = body_part_iter->get();
179
180               if ( has_header_essence && tmp_partition->ThisPartition == 0 )
181                 {
182                   current_body_offset = 0;
183                   current_ec_offset = tmp_partition->HeaderByteCount + tmp_partition->ArchiveSize();
184                 }
185               else
186                 {
187                   current_body_offset = tmp_partition->BodyOffset;
188                   current_ec_offset += tmp_partition->ThisPartition + tmp_partition->ArchiveSize();
189                 }
190
191               result = InitFromBuffer(m_IndexSegmentData.RoData() + m_IndexSegmentData.Length(), bytes_this_partition, current_body_offset, current_ec_offset);
192               m_IndexSegmentData.Length(m_IndexSegmentData.Length() + bytes_this_partition);
193               ++body_part_iter;
194             }
195         }
196     }
197
198   if ( KM_SUCCESS(result) )
199     {
200       std::list<InterchangeObject*>::const_iterator ii;
201   
202       for ( ii = m_PacketList->m_List.begin(); ii != m_PacketList->m_List.end(); ++ii )
203         {
204           IndexTableSegment *segment = dynamic_cast<IndexTableSegment*>(*ii);
205
206           if ( segment != 0 )
207             {
208               m_Duration += segment->IndexDuration;
209             }
210         }
211     }
212
213 #if 0
214   char identbuf[IdentBufferLen];
215   std::list<InterchangeObject*>::iterator j;
216   std::vector<ASDCP::MXF::IndexTableSegment::IndexEntry>::iterator k;
217   ui32_t entry_count = 0;
218
219   for ( j = m_PacketList->m_List.begin(); j != m_PacketList->m_List.end(); ++j )
220     {
221       assert(*j);
222       ASDCP::MXF::IndexTableSegment* segment = static_cast<ASDCP::MXF::IndexTableSegment*>(*j);
223
224       fprintf(stderr, "  --------------------------------------\n");
225       fprintf(stderr, "  IndexEditRate      = %d/%d\n",  segment->IndexEditRate.Numerator, segment->IndexEditRate.Denominator);
226       fprintf(stderr, "  IndexStartPosition = %s\n",  i64sz(segment->IndexStartPosition, identbuf));
227       fprintf(stderr, "  IndexDuration      = %s\n",  i64sz(segment->IndexDuration, identbuf));
228       fprintf(stderr, "  EditUnitByteCount  = %u\n",  segment->EditUnitByteCount);
229       fprintf(stderr, "  IndexSID           = %u\n",  segment->IndexSID);
230       fprintf(stderr, "  BodySID            = %u\n",  segment->BodySID);
231       fprintf(stderr, "  SliceCount         = %hhu\n", segment->SliceCount);
232       fprintf(stderr, "  PosTableCount      = %hhu\n", segment->PosTableCount);
233       fprintf(stderr, "  RtFileOffset       = %s\n",  i64sz(segment->RtFileOffset, identbuf));
234       fprintf(stderr, "  RtEntryOffset      = %s\n",  i64sz(segment->RtEntryOffset, identbuf));
235       fprintf(stderr, "  IndexEntryArray:\n");
236
237       for ( k = segment->IndexEntryArray.begin(); k != segment->IndexEntryArray.end(); ++k )
238         {
239           fprintf(stderr, "  0x%010qx\n", k->StreamOffset);
240           ++entry_count;
241         }
242     }
243
244   fprintf(stderr, "Actual entries: %d\n", entry_count);
245 #endif
246
247   return result;
248 }
249
250 //
251 ASDCP::Result_t
252 AS_02::MXF::AS02IndexReader::InitFromBuffer(const byte_t* p, ui32_t l, const ui64_t& body_offset, const ui64_t& essence_container_offset)
253 {
254   Result_t result = RESULT_OK;
255   const byte_t* end_p = p + l;
256
257   while ( KM_SUCCESS(result) && p < end_p )
258     {
259       // parse the packets and index them by uid, discard KLVFill items
260       InterchangeObject* object = CreateObject(m_Dict, p);
261       assert(object);
262
263       object->m_Lookup = m_Lookup;
264       result = object->InitFromBuffer(p, end_p - p);
265       p += object->PacketLength();
266
267       if ( KM_SUCCESS(result) )
268         {
269           IndexTableSegment *segment = dynamic_cast<IndexTableSegment*>(object);
270
271           if ( segment != 0 )
272             {
273               segment->RtFileOffset = essence_container_offset;
274               segment->RtEntryOffset = body_offset;
275               m_PacketList->AddPacket(object); // takes ownership
276             }
277           else
278             {
279               delete object;
280             }
281         }
282       else
283         {
284           DefaultLogSink().Error("Error initializing index segment packet.\n");
285           delete object;
286         }
287     }
288
289   if ( KM_FAILURE(result) )
290     {
291       DefaultLogSink().Error("Failed to initialize AS02IndexReader.\n");
292     }
293
294   return result;
295 }
296
297 //
298 void
299 AS_02::MXF::AS02IndexReader::Dump(FILE* stream)
300 {
301   if ( stream == 0 )
302     stream = stderr;
303
304   std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
305   for ( ; i != m_PacketList->m_List.end(); ++i )
306     (*i)->Dump(stream);
307 }
308
309 //
310 Result_t
311 AS_02::MXF::AS02IndexReader::GetMDObjectByID(const UUID& object_id, InterchangeObject** Object)
312 {
313   return m_PacketList->GetMDObjectByID(object_id, Object);
314 }
315
316 //
317 Result_t
318 AS_02::MXF::AS02IndexReader::GetMDObjectByType(const byte_t* type_id, InterchangeObject** Object)
319 {
320   InterchangeObject* TmpObject;
321
322   if ( Object == 0 )
323     Object = &TmpObject;
324
325   return m_PacketList->GetMDObjectByType(type_id, Object);
326 }
327
328 //
329 Result_t
330 AS_02::MXF::AS02IndexReader::GetMDObjectsByType(const byte_t* ObjectID, std::list<ASDCP::MXF::InterchangeObject*>& ObjectList)
331 {
332   return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
333 }
334
335
336 //
337 ui32_t
338 AS_02::MXF::AS02IndexReader::GetDuration() const
339 {
340   return m_Duration;
341 }
342
343 //
344 Result_t
345 AS_02::MXF::AS02IndexReader::Lookup(ui32_t frame_num, ASDCP::MXF::IndexTableSegment::IndexEntry& Entry) const
346 {
347   std::list<InterchangeObject*>::iterator i;
348   for ( i = m_PacketList->m_List.begin(); i != m_PacketList->m_List.end(); ++i )
349     {
350       IndexTableSegment *segment = dynamic_cast<IndexTableSegment*>(*i);
351
352       if ( segment != 0 )
353         {
354           ui64_t start_pos = segment->IndexStartPosition;
355
356           if ( segment->EditUnitByteCount > 0 ) // CBR
357             {
358               if ( m_PacketList->m_List.size() > 1 )
359                 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
360
361               if ( ! segment->IndexEntryArray.empty() )
362                 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
363
364               Entry.StreamOffset = ((ui64_t)frame_num * segment->EditUnitByteCount) + segment->RtFileOffset;
365               return RESULT_OK;
366             }
367           else if ( (ui64_t)frame_num >= start_pos
368                     && (ui64_t)frame_num < (start_pos + segment->IndexDuration) ) // VBR in segments
369             {
370               ui64_t tmp = frame_num - start_pos;
371               assert(tmp <= 0xFFFFFFFFL);
372
373               if ( tmp < segment->IndexEntryArray.size() )
374                 {
375                   Entry = segment->IndexEntryArray[(ui32_t) tmp];
376                   Entry.StreamOffset = Entry.StreamOffset - segment->RtEntryOffset + segment->RtFileOffset;
377                   return RESULT_OK;
378                 }
379               else
380                 {
381                   DefaultLogSink().Error("Malformed index table segment, IndexDuration does not match entries.\n");
382                 }
383             }
384         }
385     }
386
387   DefaultLogSink().Error("AS_02::MXF::AS02IndexReader::Lookup FAILED: frame_num=%d\n", frame_num);
388   return RESULT_FAIL;
389 }
390
391
392 //---------------------------------------------------------------------------------
393 //
394
395
396 AS_02::h__AS02Reader::h__AS02Reader(const ASDCP::Dictionary& d) : ASDCP::MXF::TrackFileReader<ASDCP::MXF::OP1aHeader, AS_02::MXF::AS02IndexReader>(d) {}
397 AS_02::h__AS02Reader::~h__AS02Reader() {}
398
399
400 // AS-DCP method of opening an MXF file for read
401 Result_t
402 AS_02::h__AS02Reader::OpenMXFRead(const std::string& filename)
403 {
404   bool has_header_essence = false;
405   Result_t result = ASDCP::MXF::TrackFileReader<OP1aHeader, AS_02::MXF::AS02IndexReader>::OpenMXFRead(filename);
406
407   if ( KM_SUCCESS(result) )
408     result = ASDCP::MXF::TrackFileReader<OP1aHeader, AS_02::MXF::AS02IndexReader>::InitInfo();
409
410   if( KM_SUCCESS(result) )
411     {
412       //
413       UL OP1a_ul(m_Dict->ul(MDD_OP1a));
414       m_Info.LabelSetType = LS_MXF_SMPTE;
415
416       if ( m_HeaderPart.OperationalPattern != OP1a_ul )
417         {
418           char strbuf[IdentBufferLen];
419           const MDDEntry* Entry = m_Dict->FindULAnyVersion(m_HeaderPart.OperationalPattern.Value());
420
421           if ( Entry == 0 )
422             {
423               DefaultLogSink().Warn("Operational pattern is not OP-1a: %s\n",
424                                     m_HeaderPart.OperationalPattern.EncodeString(strbuf, IdentBufferLen));
425             }
426           else
427             {
428               DefaultLogSink().Warn("Operational pattern is not OP-1a: %s\n", Entry->name);
429             }
430         }
431
432       //
433       if ( m_RIP.PairArray.front().ByteOffset != 0 )
434         {
435           DefaultLogSink().Error("First Partition in RIP is not at offset 0.\n");
436           return RESULT_AS02_FORMAT;
437         }
438
439       Kumu::fpos_t first_partition_after_header = 0;
440       bool has_body_sid = false;
441       RIP::pair_iterator r_i;
442
443       for ( r_i = m_RIP.PairArray.begin(); r_i != m_RIP.PairArray.end(); ++r_i )
444         {
445           if ( r_i->BodySID != 0 )
446             {
447               has_body_sid = true;
448             }
449
450           if ( first_partition_after_header == 0 && r_i->ByteOffset != 0 )
451             {
452               first_partition_after_header = r_i->ByteOffset;
453             }
454         }
455
456       // essence in header partition?
457       Kumu::fpos_t header_end = m_HeaderPart.HeaderByteCount + m_HeaderPart.ArchiveSize();
458       has_header_essence = header_end < first_partition_after_header;
459
460       if ( has_header_essence )
461         {
462           DefaultLogSink().Warn("File header partition contains essence data.\n");
463         }
464
465       if ( ! has_body_sid )
466         {
467           DefaultLogSink().Error("File contains no essence.\n");
468           return RESULT_AS02_FORMAT;
469         }
470     }
471
472   if ( KM_SUCCESS(result) )
473     {
474       m_IndexAccess.m_Lookup = &m_HeaderPart.m_Primer;
475       result = m_IndexAccess.InitFromFile(m_File, m_RIP, has_header_essence);
476     }
477
478   return result;
479 }
480
481 // AS-DCP method of reading a plaintext or encrypted frame
482 Result_t
483 AS_02::h__AS02Reader::ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
484                                      const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
485 {
486   return ASDCP::MXF::TrackFileReader<OP1aHeader, AS_02::MXF::AS02IndexReader>::ReadEKLVFrame(FrameNum, FrameBuf, EssenceUL, Ctx, HMAC);
487 }
488
489 //
490 // end h__02_Reader.cpp
491 //