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 if ( (*ii)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
205 m_Duration += static_cast<IndexTableSegment*>(*ii)->IndexDuration;
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;
216 for ( j = m_PacketList->m_List.begin(); j != m_PacketList->m_List.end(); ++j )
219 ASDCP::MXF::IndexTableSegment* segment = static_cast<ASDCP::MXF::IndexTableSegment*>(*j);
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");
234 for ( k = segment->IndexEntryArray.begin(); k != segment->IndexEntryArray.end(); ++k )
236 fprintf(stderr, " 0x%010qx\n", k->StreamOffset);
241 fprintf(stderr, "Actual entries: %d\n", entry_count);
249 AS_02::MXF::AS02IndexReader::InitFromBuffer(const byte_t* p, ui32_t l, const ui64_t& body_offset, const ui64_t& essence_container_offset)
251 Result_t result = RESULT_OK;
252 const byte_t* end_p = p + l;
254 while ( KM_SUCCESS(result) && p < end_p )
256 // parse the packets and index them by uid, discard KLVFill items
257 InterchangeObject* object = CreateObject(m_Dict, p);
260 object->m_Lookup = m_Lookup;
261 result = object->InitFromBuffer(p, end_p - p);
262 p += object->PacketLength();
264 if ( KM_SUCCESS(result) )
266 if ( object->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
268 static_cast<IndexTableSegment*>(object)->RtFileOffset = essence_container_offset;
269 static_cast<IndexTableSegment*>(object)->RtEntryOffset = body_offset;
270 m_PacketList->AddPacket(object); // takes ownership
279 DefaultLogSink().Error("Error initializing packet\n");
284 if ( KM_FAILURE(result) )
285 DefaultLogSink().Error("Failed to initialize AS02IndexReader\n");
292 AS_02::MXF::AS02IndexReader::Dump(FILE* stream)
297 std::list<InterchangeObject*>::iterator i = m_PacketList->m_List.begin();
298 for ( ; i != m_PacketList->m_List.end(); ++i )
304 AS_02::MXF::AS02IndexReader::GetMDObjectByID(const UUID& object_id, InterchangeObject** Object)
306 return m_PacketList->GetMDObjectByID(object_id, Object);
311 AS_02::MXF::AS02IndexReader::GetMDObjectByType(const byte_t* type_id, InterchangeObject** Object)
313 InterchangeObject* TmpObject;
318 return m_PacketList->GetMDObjectByType(type_id, Object);
323 AS_02::MXF::AS02IndexReader::GetMDObjectsByType(const byte_t* ObjectID, std::list<ASDCP::MXF::InterchangeObject*>& ObjectList)
325 return m_PacketList->GetMDObjectsByType(ObjectID, ObjectList);
331 AS_02::MXF::AS02IndexReader::GetDuration() const
338 AS_02::MXF::AS02IndexReader::Lookup(ui32_t frame_num, ASDCP::MXF::IndexTableSegment::IndexEntry& Entry) const
340 std::list<InterchangeObject*>::iterator li;
341 for ( li = m_PacketList->m_List.begin(); li != m_PacketList->m_List.end(); li++ )
343 if ( (*li)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
345 IndexTableSegment* Segment = static_cast<IndexTableSegment*>(*li);
346 ui64_t start_pos = Segment->IndexStartPosition;
348 if ( Segment->EditUnitByteCount > 0 )
350 if ( m_PacketList->m_List.size() > 1 )
351 DefaultLogSink().Error("Unexpected multiple IndexTableSegment in CBR file\n");
353 if ( ! Segment->IndexEntryArray.empty() )
354 DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
356 Entry.StreamOffset = ((ui64_t)frame_num * Segment->EditUnitByteCount) + Segment->RtFileOffset;
359 else if ( (ui64_t)frame_num >= start_pos
360 && (ui64_t)frame_num < (start_pos + Segment->IndexDuration) )
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;
375 //---------------------------------------------------------------------------------
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() {}
383 // AS-DCP method of opening an MXF file for read
385 AS_02::h__AS02Reader::OpenMXFRead(const char* filename)
387 bool has_header_essence = false;
388 Result_t result = ASDCP::MXF::TrackFileReader<OP1aHeader, AS_02::MXF::AS02IndexReader>::OpenMXFRead(filename);
390 if ( KM_SUCCESS(result) )
391 result = ASDCP::MXF::TrackFileReader<OP1aHeader, AS_02::MXF::AS02IndexReader>::InitInfo();
393 if( KM_SUCCESS(result) )
396 UL OP1a_ul(m_Dict->ul(MDD_OP1a));
397 InterchangeObject* Object;
398 m_Info.LabelSetType = LS_MXF_SMPTE;
400 if ( m_HeaderPart.OperationalPattern != OP1a_ul )
402 char strbuf[IdentBufferLen];
403 const MDDEntry* Entry = m_Dict->FindUL(m_HeaderPart.OperationalPattern.Value());
407 DefaultLogSink().Warn("Operational pattern is not OP-1a: %s\n",
408 m_HeaderPart.OperationalPattern.EncodeString(strbuf, IdentBufferLen));
412 DefaultLogSink().Warn("Operational pattern is not OP-1a: %s\n", Entry->name);
417 if ( m_RIP.PairArray.front().ByteOffset != 0 )
419 DefaultLogSink().Error("First Partition in RIP is not at offset 0.\n");
420 return RESULT_AS02_FORMAT;
423 Kumu::fpos_t first_partition_after_header = 0;
424 bool has_body_sid = false;
425 Array<RIP::Pair>::iterator r_i;
427 for ( r_i = m_RIP.PairArray.begin(); r_i != m_RIP.PairArray.end(); ++r_i )
429 if ( r_i->BodySID != 0 )
434 if ( first_partition_after_header == 0 && r_i->ByteOffset != 0 )
436 first_partition_after_header = r_i->ByteOffset;
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;
444 if ( has_header_essence )
446 DefaultLogSink().Warn("File header partition contains essence data.\n");
449 if ( ! has_body_sid )
451 DefaultLogSink().Error("File contains no essence.\n");
452 return RESULT_AS02_FORMAT;
456 if ( KM_SUCCESS(result) )
458 m_IndexAccess.m_Lookup = &m_HeaderPart.m_Primer;
459 result = m_IndexAccess.InitFromFile(m_File, m_RIP, has_header_essence);
465 // AS-DCP method of reading a plaintext or encrypted frame
467 AS_02::h__AS02Reader::ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
468 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
470 return ASDCP::MXF::TrackFileReader<OP1aHeader, AS_02::MXF::AS02IndexReader>::ReadEKLVFrame(FrameNum, FrameBuf, EssenceUL, Ctx, HMAC);
474 // end h__02_Reader.cpp