// 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 #include #include #include #include #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 Status BasicBlockParser::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 Status BasicBlockParser::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(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(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(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(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 bool BasicBlockParser::WasSkipped() const { return state_ == State::kSkipping; } template class BasicBlockParser; template class BasicBlockParser; } // namespace webm