2 Copyright (c) 2011-2013, Robert Scheler, Heiko Sparenberg Fraunhofer IIS,
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions
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.
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.
29 /*! \file h__02_Reader.cpp
31 \brief MXF file reader base class
34 #define DEFAULT_02_MD_DECL
35 #include "AS_02_internal.h"
37 using namespace ASDCP;
38 using namespace ASDCP::MXF;
40 static Kumu::Mutex sg_DefaultMDInitLock;
41 static bool sg_DefaultMDTypesInit = false;
42 static const ASDCP::Dictionary *sg_dict;
46 AS_02::default_md_object_init()
48 if ( ! sg_DefaultMDTypesInit )
50 Kumu::AutoMutex BlockLock(sg_DefaultMDInitLock);
52 if ( ! sg_DefaultMDTypesInit )
54 sg_dict = &DefaultSMPTEDict();
55 g_AS02IndexReader = new AS_02::MXF::AS02IndexReader(sg_dict);
56 sg_DefaultMDTypesInit = true;
62 //---------------------------------------------------------------------------------
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) {}
70 AS_02::MXF::AS02IndexReader::~AS02IndexReader() {}
74 AS_02::MXF::AS02IndexReader::InitFromFile(const Kumu::FileReader& reader, const ASDCP::MXF::RIP& rip, const bool has_header_essence)
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;
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;
84 // create a list of body parts and index parts
85 for ( i = rip.PairArray.begin(); KM_SUCCESS(result) && i != rip.PairArray.end(); ++i )
87 if ( i->BodySID == 0 )
90 if ( first_body_sid == 0 )
92 first_body_sid = i->BodySID;
94 else if ( i->BodySID != first_body_sid )
96 DefaultLogSink().Error("RIP contains multipls BodySID identities.\n");
99 reader.Seek(i->ByteOffset);
100 ASDCP::MXF::Partition *this_partition = new ASDCP::MXF::Partition(m_Dict);
101 assert(this_partition);
103 result = this_partition->InitFromFile(reader);
105 if ( KM_FAILURE(result) )
107 delete this_partition;
111 if ( this_partition->BodySID != i->BodySID )
113 DefaultLogSink().Error("Partition BodySID %d does not match RIP BodySID %d.\n",
114 this_partition->BodySID, i->BodySID);
117 body_part_array.push_back(0);
118 body_part_array.back().set(this_partition);
121 if ( body_part_array.empty() )
123 DefaultLogSink().Error("File has no partitions with essence data.\n");
124 return RESULT_AS02_FORMAT;
127 body_part_iter = body_part_array.begin();
129 for ( i = rip.PairArray.begin(); KM_SUCCESS(result) && i != rip.PairArray.end(); ++i )
131 reader.Seek(i->ByteOffset);
132 ASDCP::MXF::Partition plain_part(m_Dict);
133 result = plain_part.InitFromFile(reader);
135 if ( KM_FAILURE(result) )
138 if ( plain_part.IndexByteCount > 0 )
140 if ( body_part_iter == body_part_array.end() )
142 DefaultLogSink().Error("Index and Body partitions do not match.\n");
146 if ( plain_part.ThisPartition == plain_part.FooterPartition )
148 DefaultLogSink().Warn("File footer partition contains index data.\n");
151 // slurp up the remainder of the partition
152 ui32_t read_count = 0;
154 assert (plain_part.IndexByteCount <= 0xFFFFFFFFL);
155 ui32_t bytes_this_partition = (ui32_t)plain_part.IndexByteCount;
157 result = m_IndexSegmentData.Capacity(m_IndexSegmentData.Length() + bytes_this_partition);
159 if ( KM_SUCCESS(result) )
160 result = reader.Read(m_IndexSegmentData.Data() + m_IndexSegmentData.Length(),
161 bytes_this_partition, &read_count);
163 if ( KM_SUCCESS(result) && read_count != bytes_this_partition )
165 DefaultLogSink().Error("Short read of index partition: got %u, expecting %u\n",
166 read_count, bytes_this_partition);
167 return RESULT_AS02_FORMAT;
170 if ( KM_SUCCESS(result) )
172 ui64_t current_body_offset = 0;
173 ui64_t current_ec_offset = 0;
174 assert(body_part_iter != body_part_array.end());
176 assert(!body_part_iter->empty());
177 ASDCP::MXF::Partition *tmp_partition = body_part_iter->get();
179 if ( has_header_essence && tmp_partition->ThisPartition == 0 )
181 current_body_offset = 0;
182 current_ec_offset = tmp_partition->HeaderByteCount + tmp_partition->ArchiveSize();
186 current_body_offset = tmp_partition->BodyOffset;
187 current_ec_offset += tmp_partition->ThisPartition + tmp_partition->ArchiveSize();
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);
197 if ( KM_SUCCESS(result) )
199 std::list<InterchangeObject*>::const_iterator ii;
201 for ( ii = m_PacketList->m_List.begin(); ii != m_PacketList->m_List.end(); ++ii )
203 IndexTableSegment *segment = dynamic_cast<IndexTableSegment*>(*ii);
207 m_Duration += segment->IndexDuration;
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;
218 for ( j = m_PacketList->m_List.begin(); j != m_PacketList->m_List.end(); ++j )
221 ASDCP::MXF::IndexTableSegment* segment = static_cast<ASDCP::MXF::IndexTableSegment*>(*j);
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");
236 for ( k = segment->IndexEntryArray.begin(); k != segment->IndexEntryArray.end(); ++k )
238 fprintf(stderr, " 0x%010qx\n", k->StreamOffset);
243 fprintf(stderr, "Actual entries: %d\n", entry_count);
251 AS_02::MXF::AS02IndexReader::InitFromBuffer(const byte_t* p, ui32_t l, const ui64_t& body_offset, const ui64_t& essence_container_offset)
253 Result_t result = RESULT_OK;
254 const byte_t* end_p = p + l;
256 while ( KM_SUCCESS(result) && p < end_p )
258 // parse the packets and index them by uid, discard KLVFill items
259 InterchangeObject* object = CreateObject(m_Dict, p);
262 object->m_Lookup = m_Lookup;
263 result = object->InitFromBuffer(p, end_p - p);
264 p += object->PacketLength();
266 if ( KM_SUCCESS(result) )
268 IndexTableSegment *segment = dynamic_cast<IndexTableSegment*>(object);
272 segment->RtFileOffset = essence_container_offset;
273 segment->RtEntryOffset = body_offset;
274 m_PacketList->AddPacket(object); // takes ownership
283 DefaultLogSink().Error("Error initializing packet\n");
288 if ( KM_FAILURE(result) )
289 DefaultLogSink().Error("Failed to initialize AS02IndexReader\n");
296 AS_02::MXF::AS02IndexReader::Dump(FILE* stream)
301 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
302 for ( ; i != m_PacketList->m_List.end(); ++i )
308 AS_02::MXF::AS02IndexReader::GetMDObjectByID(const UUID& object_id, InterchangeObject** Object)
310 return m_PacketList->GetMDObjectByID(object_id, Object);
315 AS_02::MXF::AS02IndexReader::GetMDObjectByType(const byte_t* type_id, InterchangeObject** Object)
317 InterchangeObject* TmpObject;
322 return m_PacketList->GetMDObjectByType(type_id, Object);
327 AS_02::MXF::AS02IndexReader::GetMDObjectsByType(const byte_t* ObjectID, std::list<ASDCP::MXF::InterchangeObject*>& ObjectList)
329 return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
335 AS_02::MXF::AS02IndexReader::GetDuration() const
342 AS_02::MXF::AS02IndexReader::Lookup(ui32_t frame_num, ASDCP::MXF::IndexTableSegment::IndexEntry& Entry) const
344 std::list<InterchangeObject*>::iterator i;
345 for ( i = m_PacketList->m_List.begin(); i != m_PacketList->m_List.end(); ++i )
347 IndexTableSegment *segment = dynamic_cast<IndexTableSegment*>(*i);
351 ui64_t start_pos = segment->IndexStartPosition;
353 if ( segment->EditUnitByteCount > 0 )
355 if ( m_PacketList->m_List.size() > 1 )
356 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
358 if ( ! segment->IndexEntryArray.empty() )
359 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
361 Entry.StreamOffset = ((ui64_t)frame_num * segment->EditUnitByteCount) + segment->RtFileOffset;
364 else if ( (ui64_t)frame_num >= start_pos
365 && (ui64_t)frame_num < (start_pos + segment->IndexDuration) )
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;
376 DefaultLogSink().Error("AS_02::MXF::AS02IndexReader::Lookup FAILED: frame_num=%d\n", frame_num);
381 //---------------------------------------------------------------------------------
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() {}
389 // AS-DCP method of opening an MXF file for read
391 AS_02::h__AS02Reader::OpenMXFRead(const char* filename)
393 bool has_header_essence = false;
394 Result_t result = ASDCP::MXF::TrackFileReader<OP1aHeader, AS_02::MXF::AS02IndexReader>::OpenMXFRead(filename);
396 if ( KM_SUCCESS(result) )
397 result = ASDCP::MXF::TrackFileReader<OP1aHeader, AS_02::MXF::AS02IndexReader>::InitInfo();
399 if( KM_SUCCESS(result) )
402 UL OP1a_ul(m_Dict->ul(MDD_OP1a));
403 InterchangeObject* Object;
404 m_Info.LabelSetType = LS_MXF_SMPTE;
406 if ( m_HeaderPart.OperationalPattern != OP1a_ul )
408 char strbuf[IdentBufferLen];
409 const MDDEntry* Entry = m_Dict->FindUL(m_HeaderPart.OperationalPattern.Value());
413 DefaultLogSink().Warn("Operational pattern is not OP-1a: %s\n",
414 m_HeaderPart.OperationalPattern.EncodeString(strbuf, IdentBufferLen));
418 DefaultLogSink().Warn("Operational pattern is not OP-1a: %s\n", Entry->name);
423 if ( m_RIP.PairArray.front().ByteOffset != 0 )
425 DefaultLogSink().Error("First Partition in RIP is not at offset 0.\n");
426 return RESULT_AS02_FORMAT;
429 Kumu::fpos_t first_partition_after_header = 0;
430 bool has_body_sid = false;
431 Array<RIP::Pair>::iterator r_i;
433 for ( r_i = m_RIP.PairArray.begin(); r_i != m_RIP.PairArray.end(); ++r_i )
435 if ( r_i->BodySID != 0 )
440 if ( first_partition_after_header == 0 && r_i->ByteOffset != 0 )
442 first_partition_after_header = r_i->ByteOffset;
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;
450 if ( has_header_essence )
452 DefaultLogSink().Warn("File header partition contains essence data.\n");
455 if ( ! has_body_sid )
457 DefaultLogSink().Error("File contains no essence.\n");
458 return RESULT_AS02_FORMAT;
462 if ( KM_SUCCESS(result) )
464 m_IndexAccess.m_Lookup = &m_HeaderPart.m_Primer;
465 result = m_IndexAccess.InitFromFile(m_File, m_RIP, has_header_essence);
471 // AS-DCP method of reading a plaintext or encrypted frame
473 AS_02::h__AS02Reader::ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
474 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
476 return ASDCP::MXF::TrackFileReader<OP1aHeader, AS_02::MXF::AS02IndexReader>::ReadEKLVFrame(FrameNum, FrameBuf, EssenceUL, Ctx, HMAC);
480 // end h__02_Reader.cpp