in AS02-land, don't twice-over instantiate objects declared already in AS_DCP_internal
[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           if ( (*ii)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
204             {
205               m_Duration += static_cast<IndexTableSegment*>(*ii)->IndexDuration;
206             }
207         }
208     }
209
210 #if 0
211   char identbuf[IdentBufferLen];
212   std::list<InterchangeObject*>::iterator j;
213   std::vector<ASDCP::MXF::IndexTableSegment::IndexEntry>::iterator k;
214   ui32_t entry_count = 0;
215
216   for ( j = m_PacketList->m_List.begin(); j != m_PacketList->m_List.end(); ++j )
217     {
218       assert(*j);
219       ASDCP::MXF::IndexTableSegment* segment = static_cast<ASDCP::MXF::IndexTableSegment*>(*j);
220
221       fprintf(stderr, "  --------------------------------------\n");
222       fprintf(stderr, "  IndexEditRate      = %d/%d\n",  segment->IndexEditRate.Numerator, segment->IndexEditRate.Denominator);
223       fprintf(stderr, "  IndexStartPosition = %s\n",  i64sz(segment->IndexStartPosition, identbuf));
224       fprintf(stderr, "  IndexDuration      = %s\n",  i64sz(segment->IndexDuration, identbuf));
225       fprintf(stderr, "  EditUnitByteCount  = %u\n",  segment->EditUnitByteCount);
226       fprintf(stderr, "  IndexSID           = %u\n",  segment->IndexSID);
227       fprintf(stderr, "  BodySID            = %u\n",  segment->BodySID);
228       fprintf(stderr, "  SliceCount         = %hu\n", segment->SliceCount);
229       fprintf(stderr, "  PosTableCount      = %hu\n", segment->PosTableCount);
230       fprintf(stderr, "  RtFileOffset       = %s\n",  i64sz(segment->RtFileOffset, identbuf));
231       fprintf(stderr, "  RtEntryOffset      = %s\n",  i64sz(segment->RtEntryOffset, identbuf));
232       fprintf(stderr, "  IndexEntryArray:\n");
233
234       for ( k = segment->IndexEntryArray.begin(); k != segment->IndexEntryArray.end(); ++k )
235         {
236           fprintf(stderr, "  0x%010qx\n", k->StreamOffset);
237           ++entry_count;
238         }
239     }
240
241   fprintf(stderr, "Actual entries: %d\n", entry_count);
242 #endif
243
244   return result;
245 }
246
247 //
248 ASDCP::Result_t
249 AS_02::MXF::AS02IndexReader::InitFromBuffer(const byte_t* p, ui32_t l, const ui64_t& body_offset, const ui64_t& essence_container_offset)
250 {
251   Result_t result = RESULT_OK;
252   const byte_t* end_p = p + l;
253
254   while ( KM_SUCCESS(result) && p < end_p )
255     {
256       // parse the packets and index them by uid, discard KLVFill items
257       InterchangeObject* object = CreateObject(m_Dict, p);
258       assert(object);
259
260       object->m_Lookup = m_Lookup;
261       result = object->InitFromBuffer(p, end_p - p);
262       p += object->PacketLength();
263
264       if ( KM_SUCCESS(result) )
265         {
266           if ( object->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
267             {
268               static_cast<IndexTableSegment*>(object)->RtFileOffset = essence_container_offset;
269               static_cast<IndexTableSegment*>(object)->RtEntryOffset = body_offset;
270               m_PacketList->AddPacket(object); // takes ownership
271             }
272           else
273             {
274               delete object;
275             }
276         }
277       else
278         {
279           DefaultLogSink().Error("Error initializing packet\n");
280           delete object;
281         }
282     }
283
284   if ( KM_FAILURE(result) )
285     DefaultLogSink().Error("Failed to initialize AS02IndexReader\n");
286
287   return result;
288 }
289
290 //
291 void
292 AS_02::MXF::AS02IndexReader::Dump(FILE* stream)
293 {
294   if ( stream == 0 )
295     stream = stderr;
296
297   std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
298   for ( ; i != m_PacketList->m_List.end(); ++i )
299     (*i)->Dump(stream);
300 }
301
302 //
303 Result_t
304 AS_02::MXF::AS02IndexReader::GetMDObjectByID(const UUID& object_id, InterchangeObject** Object)
305 {
306   return m_PacketList->GetMDObjectByID(object_id, Object);
307 }
308
309 //
310 Result_t
311 AS_02::MXF::AS02IndexReader::GetMDObjectByType(const byte_t* type_id, InterchangeObject** Object)
312 {
313   InterchangeObject* TmpObject;
314
315   if ( Object == 0 )
316     Object = &TmpObject;
317
318   return m_PacketList->GetMDObjectByType(type_id, Object);
319 }
320
321 //
322 Result_t
323 AS_02::MXF::AS02IndexReader::GetMDObjectsByType(const byte_t* ObjectID, std::list<ASDCP::MXF::InterchangeObject*>& ObjectList)
324 {
325   return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
326 }
327
328
329 //
330 ui32_t
331 AS_02::MXF::AS02IndexReader::GetDuration() const
332 {
333   return m_Duration;
334 }
335
336 //
337 Result_t
338 AS_02::MXF::AS02IndexReader::Lookup(ui32_t frame_num, ASDCP::MXF::IndexTableSegment::IndexEntry& Entry) const
339 {
340   std::list<InterchangeObject*>::iterator li;
341   for ( li = m_PacketList->m_List.begin(); li != m_PacketList->m_List.end(); li++ )
342     {
343       if ( (*li)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
344         {
345           IndexTableSegment* Segment = static_cast<IndexTableSegment*>(*li);
346           ui64_t start_pos = Segment->IndexStartPosition;
347
348           if ( Segment->EditUnitByteCount > 0 )
349             {
350               if ( m_PacketList->m_List.size() > 1 )
351                 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
352
353               if ( ! Segment->IndexEntryArray.empty() )
354                 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
355
356               Entry.StreamOffset = ((ui64_t)frame_num * Segment->EditUnitByteCount) + Segment->RtFileOffset;
357               return RESULT_OK;
358             }
359           else if ( (ui64_t)frame_num >= start_pos
360                     && (ui64_t)frame_num < (start_pos + Segment->IndexDuration) )
361             {
362               ui64_t tmp = frame_num - start_pos;
363               assert(tmp <= 0xFFFFFFFFL);
364               Entry = Segment->IndexEntryArray[(ui32_t) tmp];
365               Entry.StreamOffset = Entry.StreamOffset - Segment->RtEntryOffset + Segment->RtFileOffset;
366               return RESULT_OK;
367             }
368         }
369     }
370
371   return RESULT_FAIL;
372 }
373
374
375 //---------------------------------------------------------------------------------
376 //
377
378
379 AS_02::h__AS02Reader::h__AS02Reader(const ASDCP::Dictionary& d) : ASDCP::MXF::TrackFileReader<ASDCP::MXF::OP1aHeader, AS_02::MXF::AS02IndexReader>(d) {}
380 AS_02::h__AS02Reader::~h__AS02Reader() {}
381
382
383 // AS-DCP method of opening an MXF file for read
384 Result_t
385 AS_02::h__AS02Reader::OpenMXFRead(const char* filename)
386 {
387   bool has_header_essence = false;
388   Result_t result = ASDCP::MXF::TrackFileReader<OP1aHeader, AS_02::MXF::AS02IndexReader>::OpenMXFRead(filename);
389
390   if ( KM_SUCCESS(result) )
391     result = ASDCP::MXF::TrackFileReader<OP1aHeader, AS_02::MXF::AS02IndexReader>::InitInfo();
392
393   if( KM_SUCCESS(result) )
394     {
395       //
396       UL OP1a_ul(m_Dict->ul(MDD_OP1a));
397       InterchangeObject* Object;
398       m_Info.LabelSetType = LS_MXF_SMPTE;
399
400       if ( m_HeaderPart.OperationalPattern != OP1a_ul )
401         {
402           char strbuf[IdentBufferLen];
403           const MDDEntry* Entry = m_Dict->FindUL(m_HeaderPart.OperationalPattern.Value());
404
405           if ( Entry == 0 )
406             {
407               DefaultLogSink().Warn("Operational pattern is not OP-1a: %s\n",
408                                     m_HeaderPart.OperationalPattern.EncodeString(strbuf, IdentBufferLen));
409             }
410           else
411             {
412               DefaultLogSink().Warn("Operational pattern is not OP-1a: %s\n", Entry->name);
413             }
414         }
415
416       //
417       if ( m_RIP.PairArray.front().ByteOffset != 0 )
418         {
419           DefaultLogSink().Error("First Partition in RIP is not at offset 0.\n");
420           return RESULT_AS02_FORMAT;
421         }
422
423       Kumu::fpos_t first_partition_after_header = 0;
424       bool has_body_sid = false;
425       Array<RIP::Pair>::iterator r_i;
426
427       for ( r_i = m_RIP.PairArray.begin(); r_i != m_RIP.PairArray.end(); ++r_i )
428         {
429           if ( r_i->BodySID != 0 )
430             {
431               has_body_sid = true;
432             }
433
434           if ( first_partition_after_header == 0 && r_i->ByteOffset != 0 )
435             {
436               first_partition_after_header = r_i->ByteOffset;
437             }
438         }
439
440       // essence in header partition?
441       Kumu::fpos_t header_end = m_HeaderPart.HeaderByteCount + m_HeaderPart.ArchiveSize();
442       has_header_essence = header_end < first_partition_after_header;
443
444       if ( has_header_essence )
445         {
446           DefaultLogSink().Warn("File header partition contains essence data.\n");
447         }
448
449       if ( ! has_body_sid )
450         {
451           DefaultLogSink().Error("File contains no essence.\n");
452           return RESULT_AS02_FORMAT;
453         }
454     }
455
456   if ( KM_SUCCESS(result) )
457     {
458       m_IndexAccess.m_Lookup = &m_HeaderPart.m_Primer;
459       result = m_IndexAccess.InitFromFile(m_File, m_RIP, has_header_essence);
460     }
461
462   return result;
463 }
464
465 // AS-DCP method of reading a plaintext or encrypted frame
466 Result_t
467 AS_02::h__AS02Reader::ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
468                                      const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
469 {
470   return ASDCP::MXF::TrackFileReader<OP1aHeader, AS_02::MXF::AS02IndexReader>::ReadEKLVFrame(FrameNum, FrameBuf, EssenceUL, Ctx, HMAC);
471 }
472
473 //
474 // end h__02_Reader.cpp
475 //