477 lines
10 KiB
C++
477 lines
10 KiB
C++
#include "block.hpp"
|
|
#include "imkvreader.hpp"
|
|
#include "ebmlreadhelpers.hpp"
|
|
#include "cluster.hpp"
|
|
#include "segment.hpp"
|
|
#include "segmentinfo.hpp"
|
|
#include "mkvprofiler.hpp"
|
|
|
|
#include <stddef.h>
|
|
#include <assert.h>
|
|
#include <limits.h>
|
|
|
|
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<short>(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<long>(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<int>(m_frames.size()) < frame_count);
|
|
|
|
--fc;
|
|
}
|
|
|
|
assert(static_cast<int>(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<long>(frame_size);
|
|
|
|
m_frames.push_back(f);
|
|
|
|
if (static_cast<int>(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<long>(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<long>(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<int>(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<long>(frame_size);
|
|
size += curr.len; //contribution of this frame
|
|
|
|
m_frames.push_back(curr);
|
|
|
|
assert(static_cast<int>(m_frames.size()) < frame_count);
|
|
|
|
--fc;
|
|
}
|
|
|
|
// last frame
|
|
{
|
|
assert(pos <= stop);
|
|
assert(static_cast<int>(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<long>(frame_size);
|
|
|
|
m_frames.push_back(curr);
|
|
|
|
assert(static_cast<int>(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<unsigned char>(1 << 7)) != 0);
|
|
}
|
|
|
|
void Block::SetKey(bool bKey)
|
|
{
|
|
if (bKey)
|
|
m_flags |= static_cast<unsigned char>(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<Lacing>(value);
|
|
}
|
|
|
|
int Block::GetFrameCount() const
|
|
{
|
|
return static_cast<int>(m_frames.size());
|
|
}
|
|
|
|
const Block::Frame& Block::GetFrame(int idx) const
|
|
{
|
|
assert(idx >= 0);
|
|
assert(idx < static_cast<int>(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
|
|
|