blob: b851f51f5ee2ad0085348068315b201a650937c1 [file] [log] [blame]
// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
#include "src/block_parser.h"
#include <cassert>
#include <cstdint>
#include <numeric>
#include <type_traits>
#include <vector>
#include "src/parser_utils.h"
#include "webm/element.h"
namespace webm {
namespace {
// The ParseBasicBlockFlags functions parse extra flag bits into the block,
// depending on the type of block that is being parsed.
void ParseBasicBlockFlags(std::uint8_t /* flags */, Block* /* block */) {
// Block has no extra flags that aren't already handled.
}
void ParseBasicBlockFlags(std::uint8_t flags, SimpleBlock* block) {
block->is_key_frame = (0x80 & flags) != 0;
block->is_discardable = (0x01 & flags) != 0;
}
// The BasicBlockBegin functions call the Callback event handler and get the
// correct action for the parser, depending on the type of block that is being
// parsed.
Status BasicBlockBegin(const ElementMetadata& metadata, const Block& block,
Callback* callback, Action* action) {
return callback->OnBlockBegin(metadata, block, action);
}
Status BasicBlockBegin(const ElementMetadata& metadata,
const SimpleBlock& block, Callback* callback,
Action* action) {
return callback->OnSimpleBlockBegin(metadata, block, action);
}
// The BasicBlockEnd functions call the Callback event handler depending on the
// type of block that is being parsed.
Status BasicBlockEnd(const ElementMetadata& metadata, const Block& block,
Callback* callback) {
return callback->OnBlockEnd(metadata, block);
}
Status BasicBlockEnd(const ElementMetadata& metadata, const SimpleBlock& block,
Callback* callback) {
return callback->OnSimpleBlockEnd(metadata, block);
}
} // namespace
template <typename T>
Status BasicBlockParser<T>::Init(const ElementMetadata& metadata,
std::uint64_t max_size) {
assert(metadata.size == kUnknownElementSize || metadata.size <= max_size);
if (metadata.size == kUnknownElementSize || metadata.size < 5) {
return Status(Status::kInvalidElementSize);
}
*this = {};
frame_metadata_.parent_element = metadata;
return Status(Status::kOkCompleted);
}
template <typename T>
Status BasicBlockParser<T>::Feed(Callback* callback, Reader* reader,
std::uint64_t* num_bytes_read) {
assert(callback != nullptr);
assert(reader != nullptr);
assert(num_bytes_read != nullptr);
*num_bytes_read = 0;
Status status;
std::uint64_t local_num_bytes_read;
while (true) {
switch (state_) {
case State::kReadingHeader: {
status = header_parser_.Feed(callback, reader, &local_num_bytes_read);
*num_bytes_read += local_num_bytes_read;
header_bytes_read_ += local_num_bytes_read;
if (!status.completed_ok()) {
return status;
}
value_.track_number = header_parser_.value().track_number;
value_.timecode = header_parser_.value().timecode;
std::uint8_t flags = header_parser_.value().flags;
value_.is_visible = (0x08 & flags) == 0;
value_.lacing = static_cast<Lacing>(flags & 0x06);
ParseBasicBlockFlags(flags, &value_);
if (value_.lacing == Lacing::kNone) {
value_.num_frames = 1;
state_ = State::kGettingAction;
} else {
state_ = State::kReadingLaceCount;
}
continue;
}
case State::kReadingLaceCount: {
assert(lace_sizes_.empty());
std::uint8_t lace_count;
status = ReadByte(reader, &lace_count);
if (!status.completed_ok()) {
return status;
}
++*num_bytes_read;
++header_bytes_read_;
// Lace count is stored as (count - 1).
value_.num_frames = lace_count + 1;
state_ = State::kGettingAction;
continue;
}
case State::kGettingAction: {
Action action = Action::kRead;
status = BasicBlockBegin(frame_metadata_.parent_element, value_,
callback, &action);
if (!status.completed_ok()) {
return status;
}
if (action == Action::kSkip) {
state_ = State::kSkipping;
} else if (value_.lacing == Lacing::kNone || value_.num_frames == 1) {
state_ = State::kValidatingSize;
} else if (value_.lacing == Lacing::kXiph) {
state_ = State::kReadingXiphLaceSizes;
} else if (value_.lacing == Lacing::kEbml) {
state_ = State::kReadingFirstEbmlLaceSize;
} else {
state_ = State::kCalculatingFixedLaceSizes;
}
continue;
}
case State::kReadingXiphLaceSizes:
assert(value_.num_frames > 0);
while (static_cast<int>(lace_sizes_.size()) < value_.num_frames - 1) {
std::uint8_t byte;
do {
status = ReadByte(reader, &byte);
if (!status.completed_ok()) {
return status;
}
++*num_bytes_read;
++header_bytes_read_;
xiph_lace_size_ += byte;
} while (byte == 255);
lace_sizes_.push_back(xiph_lace_size_);
xiph_lace_size_ = 0;
}
state_ = State::kValidatingSize;
continue;
case State::kReadingFirstEbmlLaceSize:
assert(value_.num_frames > 0);
assert(lace_sizes_.empty());
status = uint_parser_.Feed(callback, reader, &local_num_bytes_read);
*num_bytes_read += local_num_bytes_read;
header_bytes_read_ += local_num_bytes_read;
if (!status.completed_ok()) {
return status;
}
lace_sizes_.push_back(uint_parser_.value());
uint_parser_ = {};
state_ = State::kReadingEbmlLaceSizes;
continue;
case State::kReadingEbmlLaceSizes:
assert(value_.num_frames > 0);
assert(!lace_sizes_.empty());
while (static_cast<int>(lace_sizes_.size()) < value_.num_frames - 1) {
status = uint_parser_.Feed(callback, reader, &local_num_bytes_read);
*num_bytes_read += local_num_bytes_read;
header_bytes_read_ += local_num_bytes_read;
if (!status.completed_ok()) {
return status;
}
constexpr std::uint64_t one = 1; // Prettier than a static_cast.
std::uint64_t offset =
(one << (uint_parser_.encoded_length() * 7 - 1)) - 1;
lace_sizes_.push_back(lace_sizes_.back() + uint_parser_.value() -
offset);
uint_parser_ = {};
}
state_ = State::kValidatingSize;
continue;
case State::kCalculatingFixedLaceSizes: {
assert(value_.num_frames > 0);
assert(lace_sizes_.empty());
if (header_bytes_read_ >= frame_metadata_.parent_element.size) {
return Status(Status::kInvalidElementValue);
}
std::uint64_t laced_data_size =
frame_metadata_.parent_element.size - header_bytes_read_;
std::uint64_t frame_size = laced_data_size / value_.num_frames;
if (laced_data_size % value_.num_frames != 0) {
return Status(Status::kInvalidElementValue);
}
lace_sizes_.resize(value_.num_frames, frame_size);
frame_metadata_.position =
frame_metadata_.parent_element.position + header_bytes_read_;
frame_metadata_.size = frame_size;
state_ = State::kReadingFrames;
continue;
}
case State::kValidatingSize: {
std::uint64_t sum = std::accumulate(
lace_sizes_.begin(), lace_sizes_.end(), header_bytes_read_);
if (sum >= frame_metadata_.parent_element.size) {
return Status(Status::kInvalidElementValue);
}
lace_sizes_.push_back(frame_metadata_.parent_element.size - sum);
frame_metadata_.position = frame_metadata_.parent_element.position +
frame_metadata_.parent_element.header_size +
header_bytes_read_;
frame_metadata_.size = lace_sizes_[0];
state_ = State::kReadingFrames;
continue;
}
case State::kSkipping:
do {
// Skip the remaining part of the header and all of the frames.
status = reader->Skip(
frame_metadata_.parent_element.size - header_bytes_read_,
&local_num_bytes_read);
*num_bytes_read += local_num_bytes_read;
header_bytes_read_ += local_num_bytes_read;
} while (status.code == Status::kOkPartial);
return status;
case State::kReadingFrames:
assert(value_.num_frames > 0);
assert(static_cast<int>(lace_sizes_.size()) == value_.num_frames);
for (; current_lace_ < lace_sizes_.size(); ++current_lace_) {
const std::uint64_t original = lace_sizes_[current_lace_];
status = callback->OnFrame(frame_metadata_, reader,
&lace_sizes_[current_lace_]);
*num_bytes_read += original - lace_sizes_[current_lace_];
if (!status.completed_ok()) {
return status;
}
assert(lace_sizes_[current_lace_] == 0);
if (current_lace_ + 1 < lace_sizes_.size()) {
frame_metadata_.position += frame_metadata_.size;
frame_metadata_.size = lace_sizes_[current_lace_ + 1];
}
}
state_ = State::kDone;
continue;
case State::kDone:
return BasicBlockEnd(frame_metadata_.parent_element, value_, callback);
}
}
}
template <typename T>
bool BasicBlockParser<T>::WasSkipped() const {
return state_ == State::kSkipping;
}
template class BasicBlockParser<Block>;
template class BasicBlockParser<SimpleBlock>;
} // namespace webm