2 Copyright (c) 2011-2015, 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 = %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");
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 )
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) )
370 ui64_t tmp = frame_num - start_pos;
371 assert(tmp <= 0xFFFFFFFFL);
372 Entry = segment->IndexEntryArray[(ui32_t) tmp];
373 Entry.StreamOffset = Entry.StreamOffset - segment->RtEntryOffset + segment->RtFileOffset;
379 DefaultLogSink().Error("AS_02::MXF::AS02IndexReader::Lookup FAILED: frame_num=%d\n", frame_num);
384 //---------------------------------------------------------------------------------
388 AS_02::h__AS02Reader::h__AS02Reader(const ASDCP::Dictionary& d) : ASDCP::MXF::TrackFileReader<ASDCP::MXF::OP1aHeader, AS_02::MXF::AS02IndexReader>(d) {}
389 AS_02::h__AS02Reader::~h__AS02Reader() {}
392 // AS-DCP method of opening an MXF file for read
394 AS_02::h__AS02Reader::OpenMXFRead(const char* filename)
396 bool has_header_essence = false;
397 Result_t result = ASDCP::MXF::TrackFileReader<OP1aHeader, AS_02::MXF::AS02IndexReader>::OpenMXFRead(filename);
399 if ( KM_SUCCESS(result) )
400 result = ASDCP::MXF::TrackFileReader<OP1aHeader, AS_02::MXF::AS02IndexReader>::InitInfo();
402 if( KM_SUCCESS(result) )
405 UL OP1a_ul(m_Dict->ul(MDD_OP1a));
406 InterchangeObject* Object;
407 m_Info.LabelSetType = LS_MXF_SMPTE;
409 if ( m_HeaderPart.OperationalPattern != OP1a_ul )
411 char strbuf[IdentBufferLen];
412 const MDDEntry* Entry = m_Dict->FindUL(m_HeaderPart.OperationalPattern.Value());
416 DefaultLogSink().Warn("Operational pattern is not OP-1a: %s\n",
417 m_HeaderPart.OperationalPattern.EncodeString(strbuf, IdentBufferLen));
421 DefaultLogSink().Warn("Operational pattern is not OP-1a: %s\n", Entry->name);
426 if ( m_RIP.PairArray.front().ByteOffset != 0 )
428 DefaultLogSink().Error("First Partition in RIP is not at offset 0.\n");
429 return RESULT_AS02_FORMAT;
432 Kumu::fpos_t first_partition_after_header = 0;
433 bool has_body_sid = false;
434 RIP::pair_iterator r_i;
436 for ( r_i = m_RIP.PairArray.begin(); r_i != m_RIP.PairArray.end(); ++r_i )
438 if ( r_i->BodySID != 0 )
443 if ( first_partition_after_header == 0 && r_i->ByteOffset != 0 )
445 first_partition_after_header = r_i->ByteOffset;
449 // essence in header partition?
450 Kumu::fpos_t header_end = m_HeaderPart.HeaderByteCount + m_HeaderPart.ArchiveSize();
451 has_header_essence = header_end < first_partition_after_header;
453 if ( has_header_essence )
455 DefaultLogSink().Warn("File header partition contains essence data.\n");
458 if ( ! has_body_sid )
460 DefaultLogSink().Error("File contains no essence.\n");
461 return RESULT_AS02_FORMAT;
465 if ( KM_SUCCESS(result) )
467 m_IndexAccess.m_Lookup = &m_HeaderPart.m_Primer;
468 result = m_IndexAccess.InitFromFile(m_File, m_RIP, has_header_essence);
474 // AS-DCP method of reading a plaintext or encrypted frame
476 AS_02::h__AS02Reader::ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
477 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
479 return ASDCP::MXF::TrackFileReader<OP1aHeader, AS_02::MXF::AS02IndexReader>::ReadEKLVFrame(FrameNum, FrameBuf, EssenceUL, Ctx, HMAC);
483 // end h__02_Reader.cpp