2 Copyright (c) 2011-2018, 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(d), 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 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;
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().Debug("The index assembler is ignoring BodySID %d.\n", i->BodySID);
100 reader.Seek(i->ByteOffset);
101 ASDCP::MXF::Partition *this_partition = new ASDCP::MXF::Partition(m_Dict);
102 assert(this_partition);
104 result = this_partition->InitFromFile(reader);
106 if ( KM_FAILURE(result) )
108 delete this_partition;
112 if ( this_partition->BodySID != i->BodySID )
114 DefaultLogSink().Error("Partition BodySID %d does not match RIP BodySID %d.\n",
115 this_partition->BodySID, i->BodySID);
118 body_part_array.push_back(0);
119 body_part_array.back().set(this_partition);
122 if ( body_part_array.empty() )
124 DefaultLogSink().Error("File has no partitions with essence data.\n");
125 return RESULT_AS02_FORMAT;
128 body_part_iter = body_part_array.begin();
130 for ( i = rip.PairArray.begin(); KM_SUCCESS(result) && i != rip.PairArray.end(); ++i )
132 reader.Seek(i->ByteOffset);
133 ASDCP::MXF::Partition plain_part(m_Dict);
134 result = plain_part.InitFromFile(reader);
136 if ( KM_FAILURE(result) )
139 if ( plain_part.IndexByteCount > 0 )
141 if ( body_part_iter == body_part_array.end() )
143 DefaultLogSink().Error("Index and Body partitions do not match.\n");
147 if ( plain_part.ThisPartition == plain_part.FooterPartition )
149 DefaultLogSink().Warn("File footer partition contains index data.\n");
152 // slurp up the remainder of the partition
153 ui32_t read_count = 0;
155 assert (plain_part.IndexByteCount <= 0xFFFFFFFFL);
156 ui32_t bytes_this_partition = (ui32_t)plain_part.IndexByteCount;
158 result = m_IndexSegmentData.Capacity(m_IndexSegmentData.Length() + bytes_this_partition);
160 if ( KM_SUCCESS(result) )
161 result = reader.Read(m_IndexSegmentData.Data() + m_IndexSegmentData.Length(),
162 bytes_this_partition, &read_count);
164 if ( KM_SUCCESS(result) && read_count != bytes_this_partition )
166 DefaultLogSink().Error("Short read of index partition: got %u, expecting %u\n",
167 read_count, bytes_this_partition);
168 return RESULT_AS02_FORMAT;
171 if ( KM_SUCCESS(result) )
173 ui64_t current_body_offset = 0;
174 ui64_t current_ec_offset = 0;
175 assert(body_part_iter != body_part_array.end());
177 assert(!body_part_iter->empty());
178 ASDCP::MXF::Partition *tmp_partition = body_part_iter->get();
180 if ( has_header_essence && tmp_partition->ThisPartition == 0 )
182 current_body_offset = 0;
183 current_ec_offset = tmp_partition->HeaderByteCount + tmp_partition->ArchiveSize();
187 current_body_offset = tmp_partition->BodyOffset;
188 current_ec_offset += tmp_partition->ThisPartition + tmp_partition->ArchiveSize();
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);
198 if ( KM_SUCCESS(result) )
200 std::list<InterchangeObject*>::const_iterator ii;
202 for ( ii = m_PacketList->m_List.begin(); ii != m_PacketList->m_List.end(); ++ii )
204 IndexTableSegment *segment = dynamic_cast<IndexTableSegment*>(*ii);
208 m_Duration += segment->IndexDuration;
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;
219 for ( j = m_PacketList->m_List.begin(); j != m_PacketList->m_List.end(); ++j )
222 ASDCP::MXF::IndexTableSegment* segment = static_cast<ASDCP::MXF::IndexTableSegment*>(*j);
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");
237 for ( k = segment->IndexEntryArray.begin(); k != segment->IndexEntryArray.end(); ++k )
239 fprintf(stderr, " 0x%010qx\n", k->StreamOffset);
244 fprintf(stderr, "Actual entries: %d\n", entry_count);
252 AS_02::MXF::AS02IndexReader::InitFromBuffer(const byte_t* p, ui32_t l, const ui64_t& body_offset, const ui64_t& essence_container_offset)
254 Result_t result = RESULT_OK;
255 const byte_t* end_p = p + l;
257 while ( KM_SUCCESS(result) && p < end_p )
259 // parse the packets and index them by uid, discard KLVFill items
260 InterchangeObject* object = CreateObject(m_Dict, p);
263 object->m_Lookup = m_Lookup;
264 result = object->InitFromBuffer(p, end_p - p);
265 p += object->PacketLength();
267 if ( KM_SUCCESS(result) )
269 IndexTableSegment *segment = dynamic_cast<IndexTableSegment*>(object);
273 segment->RtFileOffset = essence_container_offset;
274 segment->RtEntryOffset = body_offset;
275 m_PacketList->AddPacket(object); // takes ownership
284 DefaultLogSink().Error("Error initializing index segment packet.\n");
289 if ( KM_FAILURE(result) )
291 DefaultLogSink().Error("Failed to initialize AS02IndexReader.\n");
299 AS_02::MXF::AS02IndexReader::Dump(FILE* stream)
304 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
305 for ( ; i != m_PacketList->m_List.end(); ++i )
311 AS_02::MXF::AS02IndexReader::GetMDObjectByID(const UUID& object_id, InterchangeObject** Object)
313 return m_PacketList->GetMDObjectByID(object_id, Object);
318 AS_02::MXF::AS02IndexReader::GetMDObjectByType(const byte_t* type_id, InterchangeObject** Object)
320 InterchangeObject* TmpObject;
325 return m_PacketList->GetMDObjectByType(type_id, Object);
330 AS_02::MXF::AS02IndexReader::GetMDObjectsByType(const byte_t* ObjectID, std::list<ASDCP::MXF::InterchangeObject*>& ObjectList)
332 return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
338 AS_02::MXF::AS02IndexReader::GetDuration() const
345 AS_02::MXF::AS02IndexReader::Lookup(ui32_t frame_num, ASDCP::MXF::IndexTableSegment::IndexEntry& Entry) const
347 std::list<InterchangeObject*>::iterator i;
348 for ( i = m_PacketList->m_List.begin(); i != m_PacketList->m_List.end(); ++i )
350 IndexTableSegment *segment = dynamic_cast<IndexTableSegment*>(*i);
354 ui64_t start_pos = segment->IndexStartPosition;
356 if ( segment->EditUnitByteCount > 0 ) // CBR
358 if ( m_PacketList->m_List.size() > 1 )
359 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
361 if ( ! segment->IndexEntryArray.empty() )
362 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
364 Entry.StreamOffset = ((ui64_t)frame_num * segment->EditUnitByteCount) + segment->RtFileOffset;
367 else if ( (ui64_t)frame_num >= start_pos
368 && (ui64_t)frame_num < (start_pos + segment->IndexDuration) ) // VBR in segments
370 ui64_t tmp = frame_num - start_pos;
371 assert(tmp <= 0xFFFFFFFFL);
373 if ( tmp < segment->IndexEntryArray.size() )
375 Entry = segment->IndexEntryArray[(ui32_t) tmp];
376 Entry.StreamOffset = Entry.StreamOffset - segment->RtEntryOffset + segment->RtFileOffset;
381 DefaultLogSink().Error("Malformed index table segment, IndexDuration does not match entries.\n");
387 DefaultLogSink().Error("AS_02::MXF::AS02IndexReader::Lookup FAILED: frame_num=%d\n", frame_num);
392 //---------------------------------------------------------------------------------
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() {}
400 // AS-DCP method of opening an MXF file for read
402 AS_02::h__AS02Reader::OpenMXFRead(const std::string& filename)
404 bool has_header_essence = false;
405 Result_t result = ASDCP::MXF::TrackFileReader<OP1aHeader, AS_02::MXF::AS02IndexReader>::OpenMXFRead(filename);
407 if ( KM_SUCCESS(result) )
408 result = ASDCP::MXF::TrackFileReader<OP1aHeader, AS_02::MXF::AS02IndexReader>::InitInfo();
410 if( KM_SUCCESS(result) )
413 UL OP1a_ul(m_Dict->ul(MDD_OP1a));
414 m_Info.LabelSetType = LS_MXF_SMPTE;
416 if ( m_HeaderPart.OperationalPattern != OP1a_ul )
418 char strbuf[IdentBufferLen];
419 const MDDEntry* Entry = m_Dict->FindULAnyVersion(m_HeaderPart.OperationalPattern.Value());
423 DefaultLogSink().Warn("Operational pattern is not OP-1a: %s\n",
424 m_HeaderPart.OperationalPattern.EncodeString(strbuf, IdentBufferLen));
428 DefaultLogSink().Warn("Operational pattern is not OP-1a: %s\n", Entry->name);
433 if ( ! m_RIP.PairArray.empty() )
435 if ( m_RIP.PairArray.front().ByteOffset != 0 )
437 DefaultLogSink().Error("First Partition in RIP is not at offset 0.\n");
438 return RESULT_AS02_FORMAT;
442 Kumu::fpos_t first_partition_after_header = 0;
443 bool has_body_sid = false;
444 RIP::pair_iterator r_i;
446 for ( r_i = m_RIP.PairArray.begin(); r_i != m_RIP.PairArray.end(); ++r_i )
448 if ( r_i->BodySID != 0 )
453 if ( first_partition_after_header == 0 && r_i->ByteOffset != 0 )
455 first_partition_after_header = r_i->ByteOffset;
459 // essence in header partition?
460 Kumu::fpos_t header_end = m_HeaderPart.HeaderByteCount + m_HeaderPart.ArchiveSize();
461 has_header_essence = header_end < first_partition_after_header;
463 if ( has_header_essence )
465 DefaultLogSink().Warn("File header partition contains essence data.\n");
468 if ( ! has_body_sid )
470 DefaultLogSink().Error("File contains no essence.\n");
471 return RESULT_AS02_FORMAT;
475 if ( KM_SUCCESS(result) )
477 m_IndexAccess.m_Lookup = &m_HeaderPart.m_Primer;
478 result = m_IndexAccess.InitFromFile(m_File, m_RIP, has_header_essence);
484 // AS-DCP method of reading a plaintext or encrypted frame
486 AS_02::h__AS02Reader::ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
487 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
489 return ASDCP::MXF::TrackFileReader<OP1aHeader, AS_02::MXF::AS02IndexReader>::ReadEKLVFrame(FrameNum, FrameBuf, EssenceUL, Ctx, HMAC);
493 // end h__02_Reader.cpp