JD2022-TU1/main/extern/libwebm/custom/mkvparser/block.cpp

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