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(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 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().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 packet\n");
289 if ( KM_FAILURE(result) )
290 DefaultLogSink().Error("Failed to initialize AS02IndexReader\n");
297 AS_02::MXF::AS02IndexReader::Dump(FILE* stream)
302 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
303 for ( ; i != m_PacketList->m_List.end(); ++i )
309 AS_02::MXF::AS02IndexReader::GetMDObjectByID(const UUID& object_id, InterchangeObject** Object)
311 return m_PacketList->GetMDObjectByID(object_id, Object);
316 AS_02::MXF::AS02IndexReader::GetMDObjectByType(const byte_t* type_id, InterchangeObject** Object)
318 InterchangeObject* TmpObject;
323 return m_PacketList->GetMDObjectByType(type_id, Object);
328 AS_02::MXF::AS02IndexReader::GetMDObjectsByType(const byte_t* ObjectID, std::list<ASDCP::MXF::InterchangeObject*>& ObjectList)
330 return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
336 AS_02::MXF::AS02IndexReader::GetDuration() const
343 AS_02::MXF::AS02IndexReader::Lookup(ui32_t frame_num, ASDCP::MXF::IndexTableSegment::IndexEntry& Entry) const
345 std::list<InterchangeObject*>::iterator i;
346 for ( i = m_PacketList->m_List.begin(); i != m_PacketList->m_List.end(); ++i )
348 IndexTableSegment *segment = dynamic_cast<IndexTableSegment*>(*i);
352 ui64_t start_pos = segment->IndexStartPosition;
354 if ( segment->EditUnitByteCount > 0 )
356 if ( m_PacketList->m_List.size() > 1 )
357 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
359 if ( ! segment->IndexEntryArray.empty() )
360 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
362 Entry.StreamOffset = ((ui64_t)frame_num * segment->EditUnitByteCount) + segment->RtFileOffset;
365 else if ( (ui64_t)frame_num >= start_pos
366 && (ui64_t)frame_num < (start_pos + segment->IndexDuration) )
368 ui64_t tmp = frame_num - start_pos;
369 assert(tmp <= 0xFFFFFFFFL);
370 Entry = segment->IndexEntryArray[(ui32_t) tmp];
371 Entry.StreamOffset = Entry.StreamOffset - segment->RtEntryOffset + segment->RtFileOffset;
377 DefaultLogSink().Error("AS_02::MXF::AS02IndexReader::Lookup FAILED: frame_num=%d\n", frame_num);
382 //---------------------------------------------------------------------------------
386 AS_02::h__AS02Reader::h__AS02Reader(const ASDCP::Dictionary& d) : ASDCP::MXF::TrackFileReader<ASDCP::MXF::OP1aHeader, AS_02::MXF::AS02IndexReader>(d) {}
387 AS_02::h__AS02Reader::~h__AS02Reader() {}
390 // AS-DCP method of opening an MXF file for read
392 AS_02::h__AS02Reader::OpenMXFRead(const char* filename)
394 bool has_header_essence = false;
395 Result_t result = ASDCP::MXF::TrackFileReader<OP1aHeader, AS_02::MXF::AS02IndexReader>::OpenMXFRead(filename);
397 if ( KM_SUCCESS(result) )
398 result = ASDCP::MXF::TrackFileReader<OP1aHeader, AS_02::MXF::AS02IndexReader>::InitInfo();
400 if( KM_SUCCESS(result) )
403 UL OP1a_ul(m_Dict->ul(MDD_OP1a));
404 InterchangeObject* Object;
405 m_Info.LabelSetType = LS_MXF_SMPTE;
407 if ( m_HeaderPart.OperationalPattern != OP1a_ul )
409 char strbuf[IdentBufferLen];
410 const MDDEntry* Entry = m_Dict->FindUL(m_HeaderPart.OperationalPattern.Value());
414 DefaultLogSink().Warn("Operational pattern is not OP-1a: %s\n",
415 m_HeaderPart.OperationalPattern.EncodeString(strbuf, IdentBufferLen));
419 DefaultLogSink().Warn("Operational pattern is not OP-1a: %s\n", Entry->name);
424 if ( m_RIP.PairArray.front().ByteOffset != 0 )
426 DefaultLogSink().Error("First Partition in RIP is not at offset 0.\n");
427 return RESULT_AS02_FORMAT;
430 Kumu::fpos_t first_partition_after_header = 0;
431 bool has_body_sid = false;
432 Array<RIP::Pair>::iterator r_i;
434 for ( r_i = m_RIP.PairArray.begin(); r_i != m_RIP.PairArray.end(); ++r_i )
436 if ( r_i->BodySID != 0 )
441 if ( first_partition_after_header == 0 && r_i->ByteOffset != 0 )
443 first_partition_after_header = r_i->ByteOffset;
447 // essence in header partition?
448 Kumu::fpos_t header_end = m_HeaderPart.HeaderByteCount + m_HeaderPart.ArchiveSize();
449 has_header_essence = header_end < first_partition_after_header;
451 if ( has_header_essence )
453 DefaultLogSink().Warn("File header partition contains essence data.\n");
456 if ( ! has_body_sid )
458 DefaultLogSink().Error("File contains no essence.\n");
459 return RESULT_AS02_FORMAT;
463 if ( KM_SUCCESS(result) )
465 m_IndexAccess.m_Lookup = &m_HeaderPart.m_Primer;
466 result = m_IndexAccess.InitFromFile(m_File, m_RIP, has_header_essence);
472 // AS-DCP method of reading a plaintext or encrypted frame
474 AS_02::h__AS02Reader::ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
475 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
477 return ASDCP::MXF::TrackFileReader<OP1aHeader, AS_02::MXF::AS02IndexReader>::ReadEKLVFrame(FrameNum, FrameBuf, EssenceUL, Ctx, HMAC);
481 // end h__02_Reader.cpp