o Moved personal dev environment from older gcc to newer clang. Many small changes...
[asdcplib.git] / src / h__02_Reader.cpp
1 /*
2 Copyright (c) 2011-2013, 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   ASDCP::MXF::Array<ASDCP::MXF::RIP::Pair>::const_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         = %hu\n", segment->SliceCount);
232       fprintf(stderr, "  PosTableCount      = %hu\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 packet\n");
285           delete object;
286         }
287     }
288
289   if ( KM_FAILURE(result) )
290     DefaultLogSink().Error("Failed to initialize AS02IndexReader\n");
291
292   return result;
293 }
294
295 //
296 void
297 AS_02::MXF::AS02IndexReader::Dump(FILE* stream)
298 {
299   if ( stream == 0 )
300     stream = stderr;
301
302   std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
303   for ( ; i != m_PacketList->m_List.end(); ++i )
304     (*i)->Dump(stream);
305 }
306
307 //
308 Result_t
309 AS_02::MXF::AS02IndexReader::GetMDObjectByID(const UUID& object_id, InterchangeObject** Object)
310 {
311   return m_PacketList->GetMDObjectByID(object_id, Object);
312 }
313
314 //
315 Result_t
316 AS_02::MXF::AS02IndexReader::GetMDObjectByType(const byte_t* type_id, InterchangeObject** Object)
317 {
318   InterchangeObject* TmpObject;
319
320   if ( Object == 0 )
321     Object = &TmpObject;
322
323   return m_PacketList->GetMDObjectByType(type_id, Object);
324 }
325
326 //
327 Result_t
328 AS_02::MXF::AS02IndexReader::GetMDObjectsByType(const byte_t* ObjectID, std::list<ASDCP::MXF::InterchangeObject*>& ObjectList)
329 {
330   return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
331 }
332
333
334 //
335 ui32_t
336 AS_02::MXF::AS02IndexReader::GetDuration() const
337 {
338   return m_Duration;
339 }
340
341 //
342 Result_t
343 AS_02::MXF::AS02IndexReader::Lookup(ui32_t frame_num, ASDCP::MXF::IndexTableSegment::IndexEntry& Entry) const
344 {
345   std::list<InterchangeObject*>::iterator i;
346   for ( i = m_PacketList->m_List.begin(); i != m_PacketList->m_List.end(); ++i )
347     {
348       IndexTableSegment *segment = dynamic_cast<IndexTableSegment*>(*i);
349
350       if ( segment != 0 )
351         {
352           ui64_t start_pos = segment->IndexStartPosition;
353
354           if ( segment->EditUnitByteCount > 0 )
355             {
356               if ( m_PacketList->m_List.size() > 1 )
357                 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
358
359               if ( ! segment->IndexEntryArray.empty() )
360                 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
361
362               Entry.StreamOffset = ((ui64_t)frame_num * segment->EditUnitByteCount) + segment->RtFileOffset;
363               return RESULT_OK;
364             }
365           else if ( (ui64_t)frame_num >= start_pos
366                     && (ui64_t)frame_num < (start_pos + segment->IndexDuration) )
367             {
368               ui64_t tmp = frame_num - start_pos;
369               assert(tmp <= 0xFFFFFFFFL);
370               Entry = segment->IndexEntryArray[(ui32_t) tmp];
371               Entry.StreamOffset = Entry.StreamOffset - segment->RtEntryOffset + segment->RtFileOffset;
372               return RESULT_OK;
373             }
374         }
375     }
376
377   DefaultLogSink().Error("AS_02::MXF::AS02IndexReader::Lookup FAILED: frame_num=%d\n", frame_num);
378   return RESULT_FAIL;
379 }
380
381
382 //---------------------------------------------------------------------------------
383 //
384
385
386 AS_02::h__AS02Reader::h__AS02Reader(const ASDCP::Dictionary& d) : ASDCP::MXF::TrackFileReader<ASDCP::MXF::OP1aHeader, AS_02::MXF::AS02IndexReader>(d) {}
387 AS_02::h__AS02Reader::~h__AS02Reader() {}
388
389
390 // AS-DCP method of opening an MXF file for read
391 Result_t
392 AS_02::h__AS02Reader::OpenMXFRead(const char* filename)
393 {
394   bool has_header_essence = false;
395   Result_t result = ASDCP::MXF::TrackFileReader<OP1aHeader, AS_02::MXF::AS02IndexReader>::OpenMXFRead(filename);
396
397   if ( KM_SUCCESS(result) )
398     result = ASDCP::MXF::TrackFileReader<OP1aHeader, AS_02::MXF::AS02IndexReader>::InitInfo();
399
400   if( KM_SUCCESS(result) )
401     {
402       //
403       UL OP1a_ul(m_Dict->ul(MDD_OP1a));
404       InterchangeObject* Object;
405       m_Info.LabelSetType = LS_MXF_SMPTE;
406
407       if ( m_HeaderPart.OperationalPattern != OP1a_ul )
408         {
409           char strbuf[IdentBufferLen];
410           const MDDEntry* Entry = m_Dict->FindUL(m_HeaderPart.OperationalPattern.Value());
411
412           if ( Entry == 0 )
413             {
414               DefaultLogSink().Warn("Operational pattern is not OP-1a: %s\n",
415                                     m_HeaderPart.OperationalPattern.EncodeString(strbuf, IdentBufferLen));
416             }
417           else
418             {
419               DefaultLogSink().Warn("Operational pattern is not OP-1a: %s\n", Entry->name);
420             }
421         }
422
423       //
424       if ( m_RIP.PairArray.front().ByteOffset != 0 )
425         {
426           DefaultLogSink().Error("First Partition in RIP is not at offset 0.\n");
427           return RESULT_AS02_FORMAT;
428         }
429
430       Kumu::fpos_t first_partition_after_header = 0;
431       bool has_body_sid = false;
432       Array<RIP::Pair>::iterator r_i;
433
434       for ( r_i = m_RIP.PairArray.begin(); r_i != m_RIP.PairArray.end(); ++r_i )
435         {
436           if ( r_i->BodySID != 0 )
437             {
438               has_body_sid = true;
439             }
440
441           if ( first_partition_after_header == 0 && r_i->ByteOffset != 0 )
442             {
443               first_partition_after_header = r_i->ByteOffset;
444             }
445         }
446
447       // essence in header partition?
448       Kumu::fpos_t header_end = m_HeaderPart.HeaderByteCount + m_HeaderPart.ArchiveSize();
449       has_header_essence = header_end < first_partition_after_header;
450
451       if ( has_header_essence )
452         {
453           DefaultLogSink().Warn("File header partition contains essence data.\n");
454         }
455
456       if ( ! has_body_sid )
457         {
458           DefaultLogSink().Error("File contains no essence.\n");
459           return RESULT_AS02_FORMAT;
460         }
461     }
462
463   if ( KM_SUCCESS(result) )
464     {
465       m_IndexAccess.m_Lookup = &m_HeaderPart.m_Primer;
466       result = m_IndexAccess.InitFromFile(m_File, m_RIP, has_header_essence);
467     }
468
469   return result;
470 }
471
472 // AS-DCP method of reading a plaintext or encrypted frame
473 Result_t
474 AS_02::h__AS02Reader::ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
475                                      const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
476 {
477   return ASDCP::MXF::TrackFileReader<OP1aHeader, AS_02::MXF::AS02IndexReader>::ReadEKLVFrame(FrameNum, FrameBuf, EssenceUL, Ctx, HMAC);
478 }
479
480 //
481 // end h__02_Reader.cpp
482 //