Corrected rgba_MaxRef default from 1024 to 1023. Corrected partition_space from 10...
[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(m_Dict), 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().Error("RIP contains multipls BodySID identities.\n");
97         }
98
99       reader.Seek(i->ByteOffset);
100       ASDCP::MXF::Partition *this_partition = new ASDCP::MXF::Partition(m_Dict);
101       assert(this_partition);
102
103       result = this_partition->InitFromFile(reader);
104
105       if ( KM_FAILURE(result) )
106         {
107           delete this_partition;
108           return result;
109         }
110
111       if ( this_partition->BodySID != i->BodySID )
112         {
113           DefaultLogSink().Error("Partition BodySID %d does not match RIP BodySID %d.\n",
114                                  this_partition->BodySID, i->BodySID);
115         }
116
117       body_part_array.push_back(0);
118       body_part_array.back().set(this_partition);
119     }
120
121   if ( body_part_array.empty() )
122     {
123       DefaultLogSink().Error("File has no partitions with essence data.\n");
124       return RESULT_AS02_FORMAT;
125     }
126
127   body_part_iter = body_part_array.begin();
128
129   for ( i = rip.PairArray.begin(); KM_SUCCESS(result) && i != rip.PairArray.end(); ++i )
130     {
131       reader.Seek(i->ByteOffset);
132       ASDCP::MXF::Partition plain_part(m_Dict);
133       result = plain_part.InitFromFile(reader);
134
135       if ( KM_FAILURE(result) )
136         return result;
137
138       if ( plain_part.IndexByteCount > 0 )
139         {
140           if ( body_part_iter == body_part_array.end() )
141             {
142               DefaultLogSink().Error("Index and Body partitions do not match.\n");
143               break;
144             }
145
146           if ( plain_part.ThisPartition == plain_part.FooterPartition )
147             {
148               DefaultLogSink().Warn("File footer partition contains index data.\n");
149             }
150
151           // slurp up the remainder of the partition
152           ui32_t read_count = 0;
153
154           assert (plain_part.IndexByteCount <= 0xFFFFFFFFL);
155           ui32_t bytes_this_partition = (ui32_t)plain_part.IndexByteCount;
156
157           result = m_IndexSegmentData.Capacity(m_IndexSegmentData.Length() + bytes_this_partition);
158
159           if ( KM_SUCCESS(result) )
160             result = reader.Read(m_IndexSegmentData.Data() + m_IndexSegmentData.Length(),
161                                  bytes_this_partition, &read_count);
162
163           if ( KM_SUCCESS(result) && read_count != bytes_this_partition )
164             {
165               DefaultLogSink().Error("Short read of index partition: got %u, expecting %u\n",
166                                      read_count, bytes_this_partition);
167               return RESULT_AS02_FORMAT;
168             }
169
170           if ( KM_SUCCESS(result) )
171             {
172               ui64_t current_body_offset = 0;
173               ui64_t current_ec_offset = 0;
174               assert(body_part_iter != body_part_array.end());
175
176               assert(!body_part_iter->empty());
177               ASDCP::MXF::Partition *tmp_partition = body_part_iter->get();
178
179               if ( has_header_essence && tmp_partition->ThisPartition == 0 )
180                 {
181                   current_body_offset = 0;
182                   current_ec_offset = tmp_partition->HeaderByteCount + tmp_partition->ArchiveSize();
183                 }
184               else
185                 {
186                   current_body_offset = tmp_partition->BodyOffset;
187                   current_ec_offset += tmp_partition->ThisPartition + tmp_partition->ArchiveSize();
188                 }
189
190               result = InitFromBuffer(m_IndexSegmentData.RoData() + m_IndexSegmentData.Length(), bytes_this_partition, current_body_offset, current_ec_offset);
191               m_IndexSegmentData.Length(m_IndexSegmentData.Length() + bytes_this_partition);
192               ++body_part_iter;
193             }
194         }
195     }
196
197   if ( KM_SUCCESS(result) )
198     {
199       std::list<InterchangeObject*>::const_iterator ii;
200   
201       for ( ii = m_PacketList->m_List.begin(); ii != m_PacketList->m_List.end(); ++ii )
202         {
203           IndexTableSegment *segment = dynamic_cast<IndexTableSegment*>(*ii);
204
205           if ( segment != 0 )
206             {
207               m_Duration += segment->IndexDuration;
208             }
209         }
210     }
211
212 #if 0
213   char identbuf[IdentBufferLen];
214   std::list<InterchangeObject*>::iterator j;
215   std::vector<ASDCP::MXF::IndexTableSegment::IndexEntry>::iterator k;
216   ui32_t entry_count = 0;
217
218   for ( j = m_PacketList->m_List.begin(); j != m_PacketList->m_List.end(); ++j )
219     {
220       assert(*j);
221       ASDCP::MXF::IndexTableSegment* segment = static_cast<ASDCP::MXF::IndexTableSegment*>(*j);
222
223       fprintf(stderr, "  --------------------------------------\n");
224       fprintf(stderr, "  IndexEditRate      = %d/%d\n",  segment->IndexEditRate.Numerator, segment->IndexEditRate.Denominator);
225       fprintf(stderr, "  IndexStartPosition = %s\n",  i64sz(segment->IndexStartPosition, identbuf));
226       fprintf(stderr, "  IndexDuration      = %s\n",  i64sz(segment->IndexDuration, identbuf));
227       fprintf(stderr, "  EditUnitByteCount  = %u\n",  segment->EditUnitByteCount);
228       fprintf(stderr, "  IndexSID           = %u\n",  segment->IndexSID);
229       fprintf(stderr, "  BodySID            = %u\n",  segment->BodySID);
230       fprintf(stderr, "  SliceCount         = %hu\n", segment->SliceCount);
231       fprintf(stderr, "  PosTableCount      = %hu\n", segment->PosTableCount);
232       fprintf(stderr, "  RtFileOffset       = %s\n",  i64sz(segment->RtFileOffset, identbuf));
233       fprintf(stderr, "  RtEntryOffset      = %s\n",  i64sz(segment->RtEntryOffset, identbuf));
234       fprintf(stderr, "  IndexEntryArray:\n");
235
236       for ( k = segment->IndexEntryArray.begin(); k != segment->IndexEntryArray.end(); ++k )
237         {
238           fprintf(stderr, "  0x%010qx\n", k->StreamOffset);
239           ++entry_count;
240         }
241     }
242
243   fprintf(stderr, "Actual entries: %d\n", entry_count);
244 #endif
245
246   return result;
247 }
248
249 //
250 ASDCP::Result_t
251 AS_02::MXF::AS02IndexReader::InitFromBuffer(const byte_t* p, ui32_t l, const ui64_t& body_offset, const ui64_t& essence_container_offset)
252 {
253   Result_t result = RESULT_OK;
254   const byte_t* end_p = p + l;
255
256   while ( KM_SUCCESS(result) && p < end_p )
257     {
258       // parse the packets and index them by uid, discard KLVFill items
259       InterchangeObject* object = CreateObject(m_Dict, p);
260       assert(object);
261
262       object->m_Lookup = m_Lookup;
263       result = object->InitFromBuffer(p, end_p - p);
264       p += object->PacketLength();
265
266       if ( KM_SUCCESS(result) )
267         {
268           IndexTableSegment *segment = dynamic_cast<IndexTableSegment*>(object);
269
270           if ( segment != 0 )
271             {
272               segment->RtFileOffset = essence_container_offset;
273               segment->RtEntryOffset = body_offset;
274               m_PacketList->AddPacket(object); // takes ownership
275             }
276           else
277             {
278               delete object;
279             }
280         }
281       else
282         {
283           DefaultLogSink().Error("Error initializing packet\n");
284           delete object;
285         }
286     }
287
288   if ( KM_FAILURE(result) )
289     DefaultLogSink().Error("Failed to initialize AS02IndexReader\n");
290
291   return result;
292 }
293
294 //
295 void
296 AS_02::MXF::AS02IndexReader::Dump(FILE* stream)
297 {
298   if ( stream == 0 )
299     stream = stderr;
300
301   std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
302   for ( ; i != m_PacketList->m_List.end(); ++i )
303     (*i)->Dump(stream);
304 }
305
306 //
307 Result_t
308 AS_02::MXF::AS02IndexReader::GetMDObjectByID(const UUID& object_id, InterchangeObject** Object)
309 {
310   return m_PacketList->GetMDObjectByID(object_id, Object);
311 }
312
313 //
314 Result_t
315 AS_02::MXF::AS02IndexReader::GetMDObjectByType(const byte_t* type_id, InterchangeObject** Object)
316 {
317   InterchangeObject* TmpObject;
318
319   if ( Object == 0 )
320     Object = &TmpObject;
321
322   return m_PacketList->GetMDObjectByType(type_id, Object);
323 }
324
325 //
326 Result_t
327 AS_02::MXF::AS02IndexReader::GetMDObjectsByType(const byte_t* ObjectID, std::list<ASDCP::MXF::InterchangeObject*>& ObjectList)
328 {
329   return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
330 }
331
332
333 //
334 ui32_t
335 AS_02::MXF::AS02IndexReader::GetDuration() const
336 {
337   return m_Duration;
338 }
339
340 //
341 Result_t
342 AS_02::MXF::AS02IndexReader::Lookup(ui32_t frame_num, ASDCP::MXF::IndexTableSegment::IndexEntry& Entry) const
343 {
344   std::list<InterchangeObject*>::iterator i;
345   for ( i = m_PacketList->m_List.begin(); i != m_PacketList->m_List.end(); ++i )
346     {
347       IndexTableSegment *segment = dynamic_cast<IndexTableSegment*>(*i);
348
349       if ( segment != 0 )
350         {
351           ui64_t start_pos = segment->IndexStartPosition;
352
353           if ( segment->EditUnitByteCount > 0 )
354             {
355               if ( m_PacketList->m_List.size() > 1 )
356                 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
357
358               if ( ! segment->IndexEntryArray.empty() )
359                 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
360
361               Entry.StreamOffset = ((ui64_t)frame_num * segment->EditUnitByteCount) + segment->RtFileOffset;
362               return RESULT_OK;
363             }
364           else if ( (ui64_t)frame_num >= start_pos
365                     && (ui64_t)frame_num < (start_pos + segment->IndexDuration) )
366             {
367               ui64_t tmp = frame_num - start_pos;
368               assert(tmp <= 0xFFFFFFFFL);
369               Entry = segment->IndexEntryArray[(ui32_t) tmp];
370               Entry.StreamOffset = Entry.StreamOffset - segment->RtEntryOffset + segment->RtFileOffset;
371               return RESULT_OK;
372             }
373         }
374     }
375
376   DefaultLogSink().Error("AS_02::MXF::AS02IndexReader::Lookup FAILED: frame_num=%d\n", frame_num);
377   return RESULT_FAIL;
378 }
379
380
381 //---------------------------------------------------------------------------------
382 //
383
384
385 AS_02::h__AS02Reader::h__AS02Reader(const ASDCP::Dictionary& d) : ASDCP::MXF::TrackFileReader<ASDCP::MXF::OP1aHeader, AS_02::MXF::AS02IndexReader>(d) {}
386 AS_02::h__AS02Reader::~h__AS02Reader() {}
387
388
389 // AS-DCP method of opening an MXF file for read
390 Result_t
391 AS_02::h__AS02Reader::OpenMXFRead(const char* filename)
392 {
393   bool has_header_essence = false;
394   Result_t result = ASDCP::MXF::TrackFileReader<OP1aHeader, AS_02::MXF::AS02IndexReader>::OpenMXFRead(filename);
395
396   if ( KM_SUCCESS(result) )
397     result = ASDCP::MXF::TrackFileReader<OP1aHeader, AS_02::MXF::AS02IndexReader>::InitInfo();
398
399   if( KM_SUCCESS(result) )
400     {
401       //
402       UL OP1a_ul(m_Dict->ul(MDD_OP1a));
403       InterchangeObject* Object;
404       m_Info.LabelSetType = LS_MXF_SMPTE;
405
406       if ( m_HeaderPart.OperationalPattern != OP1a_ul )
407         {
408           char strbuf[IdentBufferLen];
409           const MDDEntry* Entry = m_Dict->FindUL(m_HeaderPart.OperationalPattern.Value());
410
411           if ( Entry == 0 )
412             {
413               DefaultLogSink().Warn("Operational pattern is not OP-1a: %s\n",
414                                     m_HeaderPart.OperationalPattern.EncodeString(strbuf, IdentBufferLen));
415             }
416           else
417             {
418               DefaultLogSink().Warn("Operational pattern is not OP-1a: %s\n", Entry->name);
419             }
420         }
421
422       //
423       if ( m_RIP.PairArray.front().ByteOffset != 0 )
424         {
425           DefaultLogSink().Error("First Partition in RIP is not at offset 0.\n");
426           return RESULT_AS02_FORMAT;
427         }
428
429       Kumu::fpos_t first_partition_after_header = 0;
430       bool has_body_sid = false;
431       Array<RIP::Pair>::iterator r_i;
432
433       for ( r_i = m_RIP.PairArray.begin(); r_i != m_RIP.PairArray.end(); ++r_i )
434         {
435           if ( r_i->BodySID != 0 )
436             {
437               has_body_sid = true;
438             }
439
440           if ( first_partition_after_header == 0 && r_i->ByteOffset != 0 )
441             {
442               first_partition_after_header = r_i->ByteOffset;
443             }
444         }
445
446       // essence in header partition?
447       Kumu::fpos_t header_end = m_HeaderPart.HeaderByteCount + m_HeaderPart.ArchiveSize();
448       has_header_essence = header_end < first_partition_after_header;
449
450       if ( has_header_essence )
451         {
452           DefaultLogSink().Warn("File header partition contains essence data.\n");
453         }
454
455       if ( ! has_body_sid )
456         {
457           DefaultLogSink().Error("File contains no essence.\n");
458           return RESULT_AS02_FORMAT;
459         }
460     }
461
462   if ( KM_SUCCESS(result) )
463     {
464       m_IndexAccess.m_Lookup = &m_HeaderPart.m_Primer;
465       result = m_IndexAccess.InitFromFile(m_File, m_RIP, has_header_essence);
466     }
467
468   return result;
469 }
470
471 // AS-DCP method of reading a plaintext or encrypted frame
472 Result_t
473 AS_02::h__AS02Reader::ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
474                                      const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
475 {
476   return ASDCP::MXF::TrackFileReader<OP1aHeader, AS_02::MXF::AS02IndexReader>::ReadEKLVFrame(FrameNum, FrameBuf, EssenceUL, Ctx, HMAC);
477 }
478
479 //
480 // end h__02_Reader.cpp
481 //