#include "block.hpp" #include "imkvreader.hpp" #include "ebmlreadhelpers.hpp" #include "cluster.hpp" #include "segment.hpp" #include "segmentinfo.hpp" #include "mkvprofiler.hpp" #include #include #include namespace mkvparser { Block::Block(long long start, long long size_) : m_start(start), m_size(size_), m_track(0), m_timecode(-1), m_flags(0) { } Block::~Block() { } long Block::Parse(const Cluster* pCluster) { PROFILER_SCOPED_EVENT("webm::Block::Parse"); if (pCluster == NULL) return -1; if (pCluster->GetSegment() == NULL) return -1; assert(m_start >= 0); assert(m_size >= 0); assert(m_track <= 0); assert(m_frames.empty()); long long pos = m_start; const long long stop = m_start + m_size; long len; IMkvReader* const pReader = pCluster->GetSegment()->GetReader(); m_track = ReadUInt(pReader, pos, len); if (m_track <= 0) return E_FILE_FORMAT_INVALID; if ((pos + len) > stop) return E_FILE_FORMAT_INVALID; pos += len; //consume track number if ((stop - pos) < 2) return E_FILE_FORMAT_INVALID; long status; long long value; status = UnserializeInt(pReader, pos, 2, value); if (status) return E_FILE_FORMAT_INVALID; if (value < SHRT_MIN) return E_FILE_FORMAT_INVALID; if (value > SHRT_MAX) return E_FILE_FORMAT_INVALID; m_timecode = static_cast(value); pos += 2; if ((stop - pos) <= 0) return E_FILE_FORMAT_INVALID; status = pReader->Read(pos, 1, &m_flags); if (status) return E_FILE_FORMAT_INVALID; const int lacing = int(m_flags & 0x06) >> 1; ++pos; //consume flags byte if (lacing == 0) //no lacing { if (pos > stop) return E_FILE_FORMAT_INVALID; m_frames.push_back(Frame()); Frame& f = m_frames[0]; f.pos = pos; const long long frame_size = stop - pos; if (frame_size > LONG_MAX) return E_FILE_FORMAT_INVALID; f.len = static_cast(frame_size); return 0; //success } if (pos >= stop) return E_FILE_FORMAT_INVALID; unsigned char biased_count; status = pReader->Read(pos, 1, &biased_count); if (status) return E_FILE_FORMAT_INVALID; ++pos; //consume frame count assert(pos <= stop); int frame_count = int(biased_count) + 1; m_frames.reserve(frame_count); if (lacing == 1) //Xiph { long size = 0; int fc = frame_count; while (fc > 1) { long frame_size = 0; for (;;) { unsigned char val; if (pos >= stop) return E_FILE_FORMAT_INVALID; status = pReader->Read(pos, 1, &val); if (status) return E_FILE_FORMAT_INVALID; ++pos; //consume xiph size byte frame_size += val; if (val < 255) break; } Frame f; f.pos = 0; //patch later f.len = frame_size; size += frame_size; //contribution of this frame m_frames.push_back(f); assert(static_cast(m_frames.size()) < frame_count); --fc; } assert(static_cast(m_frames.size()) < frame_count); assert(pos <= stop); // last frame { Frame f; f.pos = 0; //patch later const long long total_size = stop - pos; if (total_size < size) return E_FILE_FORMAT_INVALID; const long long frame_size = total_size - size; if (frame_size > LONG_MAX) return E_FILE_FORMAT_INVALID; f.len = static_cast(frame_size); m_frames.push_back(f); if (static_cast(m_frames.size()) != frame_count) return E_FILE_FORMAT_INVALID; } // update positions FrameVect_t::iterator first = m_frames.begin(); FrameVect_t::iterator last = m_frames.end(); for(; first != last; ++first) { Frame& f = *first; assert((pos + f.len) <= stop); f.pos = pos; pos += f.len; } assert(pos == stop); } else if (lacing == 2) //fixed-size lacing { const long long total_size = stop - pos; if ((total_size % frame_count) != 0) return E_FILE_FORMAT_INVALID; const long long frame_size = total_size / frame_count; if (frame_size > LONG_MAX) return E_FILE_FORMAT_INVALID; for (int i = 0; i < frame_count; ++i) { assert((pos + frame_size) <= stop); Frame f; f.pos = pos; f.len = static_cast(frame_size); m_frames.push_back(f); pos += frame_size; } assert(pos == stop); } else { assert(lacing == 3); //EBML lacing if (pos >= stop) return E_FILE_FORMAT_INVALID; long size = 0; int fc = frame_count; long long frame_size = ReadUInt(pReader, pos, len); if (frame_size < 0) return E_FILE_FORMAT_INVALID; if (frame_size > LONG_MAX) return E_FILE_FORMAT_INVALID; if ((pos + len) > stop) return E_FILE_FORMAT_INVALID; pos += len; //consume length of size of first frame if ((pos + frame_size) > stop) return E_FILE_FORMAT_INVALID; // first frame { Frame curr; curr.pos = 0; //patch later curr.len = static_cast(frame_size); size += curr.len; //contribution of this frame m_frames.push_back(curr); } --fc; while (fc > 1) { if (pos >= stop) return E_FILE_FORMAT_INVALID; assert(static_cast(m_frames.size()) < frame_count); assert(m_frames.back().len == frame_size); Frame curr; curr.pos = 0; //patch later const long long delta_size_ = ReadUInt(pReader, pos, len); if (delta_size_ < 0) return E_FILE_FORMAT_INVALID; if ((pos + len) > stop) return E_FILE_FORMAT_INVALID; pos += len; //consume length of (delta) size assert(pos <= stop); const int exp = 7*len - 1; const long long bias = (1LL << exp) - 1LL; const long long delta_size = delta_size_ - bias; frame_size += delta_size; if (frame_size < 0) return E_FILE_FORMAT_INVALID; if (frame_size > LONG_MAX) return E_FILE_FORMAT_INVALID; curr.len = static_cast(frame_size); size += curr.len; //contribution of this frame m_frames.push_back(curr); assert(static_cast(m_frames.size()) < frame_count); --fc; } // last frame { assert(pos <= stop); assert(static_cast(m_frames.size()) < frame_count); assert(m_frames.back().len == frame_size); Frame curr; curr.pos = 0; //patch later const long long total_size = stop - pos; if (total_size < size) return E_FILE_FORMAT_INVALID; frame_size = total_size - size; if (frame_size > LONG_MAX) return E_FILE_FORMAT_INVALID; curr.len = static_cast(frame_size); m_frames.push_back(curr); assert(static_cast(m_frames.size()) == frame_count); } FrameVect_t::iterator first = m_frames.begin(); FrameVect_t::iterator last = m_frames.end(); for( ; first != last; ++first) { Frame& f = *first; assert((pos + f.len) <= stop); f.pos = pos; pos += f.len; } assert(pos == stop); } return 0; //success } long long Block::GetTimeCode(const Cluster* pCluster) const { if (pCluster == 0) return m_timecode; const long long tc0 = pCluster->GetTimeCode(); assert(tc0 >= 0); const long long tc = tc0 + m_timecode; return tc; //unscaled timecode units } long long Block::GetTime(const Cluster* pCluster) const { assert(pCluster); const long long tc = GetTimeCode(pCluster); const Segment* const pSegment = pCluster->GetSegment(); const SegmentInfo* const pInfo = pSegment->GetInfo(); assert(pInfo); const long long scale = pInfo->GetTimeCodeScale(); assert(scale >= 1); const long long ns = tc * scale; return ns; } long long Block::GetTrackNumber() const { return m_track; } bool Block::IsKey() const { return ((m_flags & static_cast(1 << 7)) != 0); } void Block::SetKey(bool bKey) { if (bKey) m_flags |= static_cast(1 << 7); else m_flags &= 0x7F; } bool Block::IsInvisible() const { return bool(int(m_flags & 0x08) != 0); } Block::Lacing Block::GetLacing() const { const int value = int(m_flags & 0x06) >> 1; return static_cast(value); } int Block::GetFrameCount() const { return static_cast(m_frames.size()); } const Block::Frame& Block::GetFrame(int idx) const { assert(idx >= 0); assert(idx < static_cast(m_frames.size())); const Frame& f = m_frames[idx]; assert(f.pos > 0); assert(f.len > 0); return f; } long Block::Frame::Read(IMkvReader* pReader, unsigned char* buf) const { assert(pReader); assert(buf); const long status = pReader->Read(pos, len, buf); return status; } long Block::Frame::BeginLockRead(IMkvReader* _reader, const unsigned char*& _buf, long& _len) const { assert(_reader); const long status = _reader->BeginLockRead(pos, len, _buf); _len = len; return status; } long Block::Frame::EndLockRead(IMkvReader* _reader, const unsigned char* _buf) const { assert(_reader); assert(_buf); const long status = _reader->EndLockRead(_buf); return status; } } // namespace mkvparser