| // 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 |