diff options
Diffstat (limited to 'src/obu_parser.cc')
-rw-r--r-- | src/obu_parser.cc | 2885 |
1 files changed, 2885 insertions, 0 deletions
diff --git a/src/obu_parser.cc b/src/obu_parser.cc new file mode 100644 index 0000000..bbf00ed --- /dev/null +++ b/src/obu_parser.cc @@ -0,0 +1,2885 @@ +// Copyright 2019 The libgav1 Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "src/obu_parser.h" + +#include <algorithm> +#include <array> +#include <cassert> +#include <climits> +#include <cstddef> +#include <cstdint> +#include <cstring> + +#include "src/buffer_pool.h" +#include "src/decoder_impl.h" +#include "src/motion_vector.h" +#include "src/utils/common.h" +#include "src/utils/logging.h" + +namespace libgav1 { +namespace { + +// 5.9.16. +// Find the smallest value of k such that block_size << k is greater than or +// equal to target. +// +// NOTE: TileLog2(block_size, target) is equal to +// CeilLog2(ceil((double)target / block_size)) +// where the division is a floating-point number division. (This equality holds +// even when |target| is equal to 0.) In the special case of block_size == 1, +// TileLog2(1, target) is equal to CeilLog2(target). +int TileLog2(int block_size, int target) { + int k = 0; + for (; (block_size << k) < target; ++k) { + } + return k; +} + +void ParseBitStreamLevel(BitStreamLevel* const level, uint8_t level_bits) { + level->major = kMinimumMajorBitstreamLevel + (level_bits >> 2); + level->minor = level_bits & 3; +} + +// This function assumes loop_filter is zero-initialized, so only it needs to +// set the nonzero default values. +void SetDefaultRefDeltas(LoopFilter* const loop_filter) { + loop_filter->ref_deltas[kReferenceFrameIntra] = 1; + loop_filter->ref_deltas[kReferenceFrameGolden] = -1; + loop_filter->ref_deltas[kReferenceFrameAlternate] = -1; + loop_filter->ref_deltas[kReferenceFrameAlternate2] = -1; +} + +bool InTemporalLayer(int operating_point_idc, int temporal_id) { + return ((operating_point_idc >> temporal_id) & 1) != 0; +} + +bool InSpatialLayer(int operating_point_idc, int spatial_id) { + return ((operating_point_idc >> (spatial_id + 8)) & 1) != 0; +} + +// Returns the index of the last nonzero byte in the |data| buffer of |size| +// bytes. If there is no nonzero byte in the |data| buffer, returns -1. +int GetLastNonzeroByteIndex(const uint8_t* data, size_t size) { + // Scan backward for a nonzero byte. + if (size > INT_MAX) return -1; + int i = static_cast<int>(size) - 1; + while (i >= 0 && data[i] == 0) { + --i; + } + return i; +} + +// A cleanup helper class that releases the frame buffer reference held in +// |frame| in the destructor. +class RefCountedBufferPtrCleanup { + public: + explicit RefCountedBufferPtrCleanup(RefCountedBufferPtr* frame) + : frame_(*frame) {} + + // Not copyable or movable. + RefCountedBufferPtrCleanup(const RefCountedBufferPtrCleanup&) = delete; + RefCountedBufferPtrCleanup& operator=(const RefCountedBufferPtrCleanup&) = + delete; + + ~RefCountedBufferPtrCleanup() { frame_ = nullptr; } + + private: + RefCountedBufferPtr& frame_; +}; + +} // namespace + +bool ObuSequenceHeader::ParametersChanged(const ObuSequenceHeader& old) const { + // Note that the operating_parameters field is not compared per Section 7.5: + // Within a particular coded video sequence, the contents of + // sequence_header_obu must be bit-identical each time the sequence header + // appears except for the contents of operating_parameters_info. + return memcmp(this, &old, + offsetof(ObuSequenceHeader, operating_parameters)) != 0; +} + +// Macros to avoid repeated error checks in the parser code. +#define OBU_LOG_AND_RETURN_FALSE \ + do { \ + LIBGAV1_DLOG(ERROR, "%s:%d (%s): Not enough bits.", __FILE__, __LINE__, \ + __func__); \ + return false; \ + } while (false) +#define OBU_PARSER_FAIL \ + do { \ + if (scratch == -1) { \ + OBU_LOG_AND_RETURN_FALSE; \ + } \ + } while (false) +#define OBU_READ_BIT_OR_FAIL \ + scratch = bit_reader_->ReadBit(); \ + OBU_PARSER_FAIL +#define OBU_READ_LITERAL_OR_FAIL(n) \ + scratch = bit_reader_->ReadLiteral(n); \ + OBU_PARSER_FAIL +#define OBU_READ_UVLC_OR_FAIL(x) \ + do { \ + if (!bit_reader_->ReadUvlc(&(x))) { \ + OBU_LOG_AND_RETURN_FALSE; \ + } \ + } while (false) + +bool ObuParser::ParseColorConfig(ObuSequenceHeader* sequence_header) { + int64_t scratch; + ColorConfig* const color_config = &sequence_header->color_config; + OBU_READ_BIT_OR_FAIL; + const auto high_bitdepth = static_cast<bool>(scratch); + if (sequence_header->profile == kProfile2 && high_bitdepth) { + OBU_READ_BIT_OR_FAIL; + const auto is_twelve_bit = static_cast<bool>(scratch); + color_config->bitdepth = is_twelve_bit ? 12 : 10; + } else { + color_config->bitdepth = high_bitdepth ? 10 : 8; + } + if (sequence_header->profile == kProfile1) { + color_config->is_monochrome = false; + } else { + OBU_READ_BIT_OR_FAIL; + color_config->is_monochrome = static_cast<bool>(scratch); + } + OBU_READ_BIT_OR_FAIL; + const auto color_description_present_flag = static_cast<bool>(scratch); + if (color_description_present_flag) { + OBU_READ_LITERAL_OR_FAIL(8); + color_config->color_primary = static_cast<ColorPrimary>(scratch); + OBU_READ_LITERAL_OR_FAIL(8); + color_config->transfer_characteristics = + static_cast<TransferCharacteristics>(scratch); + OBU_READ_LITERAL_OR_FAIL(8); + color_config->matrix_coefficients = + static_cast<MatrixCoefficients>(scratch); + } else { + color_config->color_primary = kColorPrimaryUnspecified; + color_config->transfer_characteristics = + kTransferCharacteristicsUnspecified; + color_config->matrix_coefficients = kMatrixCoefficientsUnspecified; + } + if (color_config->is_monochrome) { + OBU_READ_BIT_OR_FAIL; + color_config->color_range = static_cast<ColorRange>(scratch); + // Set subsampling_x and subsampling_y to 1 for monochrome. This makes it + // easy to allow monochrome to be supported in profile 0. Profile 0 + // requires subsampling_x and subsampling_y to be 1. + color_config->subsampling_x = 1; + color_config->subsampling_y = 1; + color_config->chroma_sample_position = kChromaSamplePositionUnknown; + } else { + if (color_config->color_primary == kColorPrimaryBt709 && + color_config->transfer_characteristics == + kTransferCharacteristicsSrgb && + color_config->matrix_coefficients == kMatrixCoefficientsIdentity) { + color_config->color_range = kColorRangeFull; + color_config->subsampling_x = 0; + color_config->subsampling_y = 0; + // YUV 4:4:4 is only allowed in profile 1, or profile 2 with bit depth 12. + // See the table at the beginning of Section 6.4.1. + if (sequence_header->profile != kProfile1 && + (sequence_header->profile != kProfile2 || + color_config->bitdepth != 12)) { + LIBGAV1_DLOG(ERROR, + "YUV 4:4:4 is not allowed in profile %d for bitdepth %d.", + sequence_header->profile, color_config->bitdepth); + return false; + } + } else { + OBU_READ_BIT_OR_FAIL; + color_config->color_range = static_cast<ColorRange>(scratch); + if (sequence_header->profile == kProfile0) { + color_config->subsampling_x = 1; + color_config->subsampling_y = 1; + } else if (sequence_header->profile == kProfile1) { + color_config->subsampling_x = 0; + color_config->subsampling_y = 0; + } else { + if (color_config->bitdepth == 12) { + OBU_READ_BIT_OR_FAIL; + color_config->subsampling_x = scratch; + if (color_config->subsampling_x == 1) { + OBU_READ_BIT_OR_FAIL; + color_config->subsampling_y = scratch; + } else { + color_config->subsampling_y = 0; + } + } else { + color_config->subsampling_x = 1; + color_config->subsampling_y = 0; + } + } + if (color_config->subsampling_x == 1 && + color_config->subsampling_y == 1) { + OBU_READ_LITERAL_OR_FAIL(2); + color_config->chroma_sample_position = + static_cast<ChromaSamplePosition>(scratch); + } + } + OBU_READ_BIT_OR_FAIL; + color_config->separate_uv_delta_q = static_cast<bool>(scratch); + } + if (color_config->matrix_coefficients == kMatrixCoefficientsIdentity && + (color_config->subsampling_x != 0 || color_config->subsampling_y != 0)) { + LIBGAV1_DLOG(ERROR, + "matrix_coefficients is MC_IDENTITY, but subsampling_x (%d) " + "and subsampling_y (%d) are not both 0.", + color_config->subsampling_x, color_config->subsampling_y); + return false; + } + return true; +} + +bool ObuParser::ParseTimingInfo(ObuSequenceHeader* sequence_header) { + int64_t scratch; + OBU_READ_BIT_OR_FAIL; + sequence_header->timing_info_present_flag = static_cast<bool>(scratch); + if (!sequence_header->timing_info_present_flag) return true; + TimingInfo* const info = &sequence_header->timing_info; + OBU_READ_LITERAL_OR_FAIL(32); + info->num_units_in_tick = static_cast<uint32_t>(scratch); + if (info->num_units_in_tick == 0) { + LIBGAV1_DLOG(ERROR, "num_units_in_tick is 0."); + return false; + } + OBU_READ_LITERAL_OR_FAIL(32); + info->time_scale = static_cast<uint32_t>(scratch); + if (info->time_scale == 0) { + LIBGAV1_DLOG(ERROR, "time_scale is 0."); + return false; + } + OBU_READ_BIT_OR_FAIL; + info->equal_picture_interval = static_cast<bool>(scratch); + if (info->equal_picture_interval) { + OBU_READ_UVLC_OR_FAIL(info->num_ticks_per_picture); + ++info->num_ticks_per_picture; + } + return true; +} + +bool ObuParser::ParseDecoderModelInfo(ObuSequenceHeader* sequence_header) { + if (!sequence_header->timing_info_present_flag) return true; + int64_t scratch; + OBU_READ_BIT_OR_FAIL; + sequence_header->decoder_model_info_present_flag = static_cast<bool>(scratch); + if (!sequence_header->decoder_model_info_present_flag) return true; + DecoderModelInfo* const info = &sequence_header->decoder_model_info; + OBU_READ_LITERAL_OR_FAIL(5); + info->encoder_decoder_buffer_delay_length = 1 + scratch; + OBU_READ_LITERAL_OR_FAIL(32); + info->num_units_in_decoding_tick = static_cast<uint32_t>(scratch); + OBU_READ_LITERAL_OR_FAIL(5); + info->buffer_removal_time_length = 1 + scratch; + OBU_READ_LITERAL_OR_FAIL(5); + info->frame_presentation_time_length = 1 + scratch; + return true; +} + +bool ObuParser::ParseOperatingParameters(ObuSequenceHeader* sequence_header, + int index) { + int64_t scratch; + OBU_READ_BIT_OR_FAIL; + sequence_header->decoder_model_present_for_operating_point[index] = + static_cast<bool>(scratch); + if (!sequence_header->decoder_model_present_for_operating_point[index]) { + return true; + } + OperatingParameters* const params = &sequence_header->operating_parameters; + OBU_READ_LITERAL_OR_FAIL( + sequence_header->decoder_model_info.encoder_decoder_buffer_delay_length); + params->decoder_buffer_delay[index] = static_cast<uint32_t>(scratch); + OBU_READ_LITERAL_OR_FAIL( + sequence_header->decoder_model_info.encoder_decoder_buffer_delay_length); + params->encoder_buffer_delay[index] = static_cast<uint32_t>(scratch); + OBU_READ_BIT_OR_FAIL; + params->low_delay_mode_flag[index] = static_cast<bool>(scratch); + return true; +} + +bool ObuParser::ParseSequenceHeader(bool seen_frame_header) { + ObuSequenceHeader sequence_header = {}; + int64_t scratch; + OBU_READ_LITERAL_OR_FAIL(3); + if (scratch >= kMaxProfiles) { + LIBGAV1_DLOG(ERROR, "Invalid profile: %d.", static_cast<int>(scratch)); + return false; + } + sequence_header.profile = static_cast<BitstreamProfile>(scratch); + OBU_READ_BIT_OR_FAIL; + sequence_header.still_picture = static_cast<bool>(scratch); + OBU_READ_BIT_OR_FAIL; + sequence_header.reduced_still_picture_header = static_cast<bool>(scratch); + if (sequence_header.reduced_still_picture_header) { + if (!sequence_header.still_picture) { + LIBGAV1_DLOG( + ERROR, "reduced_still_picture_header is 1, but still_picture is 0."); + return false; + } + sequence_header.operating_points = 1; + sequence_header.operating_point_idc[0] = 0; + OBU_READ_LITERAL_OR_FAIL(5); + ParseBitStreamLevel(&sequence_header.level[0], scratch); + } else { + if (!ParseTimingInfo(&sequence_header) || + !ParseDecoderModelInfo(&sequence_header)) { + return false; + } + OBU_READ_BIT_OR_FAIL; + const auto initial_display_delay_present_flag = static_cast<bool>(scratch); + OBU_READ_LITERAL_OR_FAIL(5); + sequence_header.operating_points = static_cast<int>(1 + scratch); + if (operating_point_ >= sequence_header.operating_points) { + LIBGAV1_DLOG( + ERROR, + "Invalid operating point: %d (valid range is [0,%d] inclusive).", + operating_point_, sequence_header.operating_points - 1); + return false; + } + for (int i = 0; i < sequence_header.operating_points; ++i) { + OBU_READ_LITERAL_OR_FAIL(12); + sequence_header.operating_point_idc[i] = static_cast<int>(scratch); + for (int j = 0; j < i; ++j) { + if (sequence_header.operating_point_idc[i] == + sequence_header.operating_point_idc[j]) { + LIBGAV1_DLOG(ERROR, + "operating_point_idc[%d] (%d) is equal to " + "operating_point_idc[%d] (%d).", + i, sequence_header.operating_point_idc[i], j, + sequence_header.operating_point_idc[j]); + return false; + } + } + OBU_READ_LITERAL_OR_FAIL(5); + ParseBitStreamLevel(&sequence_header.level[i], scratch); + if (sequence_header.level[i].major > 3) { + OBU_READ_BIT_OR_FAIL; + sequence_header.tier[i] = scratch; + } + if (sequence_header.decoder_model_info_present_flag && + !ParseOperatingParameters(&sequence_header, i)) { + return false; + } + if (initial_display_delay_present_flag) { + OBU_READ_BIT_OR_FAIL; + if (static_cast<bool>(scratch)) { + OBU_READ_LITERAL_OR_FAIL(4); + sequence_header.initial_display_delay[i] = 1 + scratch; + } + } + } + } + OBU_READ_LITERAL_OR_FAIL(4); + sequence_header.frame_width_bits = 1 + scratch; + OBU_READ_LITERAL_OR_FAIL(4); + sequence_header.frame_height_bits = 1 + scratch; + OBU_READ_LITERAL_OR_FAIL(sequence_header.frame_width_bits); + sequence_header.max_frame_width = static_cast<int32_t>(1 + scratch); + OBU_READ_LITERAL_OR_FAIL(sequence_header.frame_height_bits); + sequence_header.max_frame_height = static_cast<int32_t>(1 + scratch); + if (!sequence_header.reduced_still_picture_header) { + OBU_READ_BIT_OR_FAIL; + sequence_header.frame_id_numbers_present = static_cast<bool>(scratch); + } + if (sequence_header.frame_id_numbers_present) { + OBU_READ_LITERAL_OR_FAIL(4); + sequence_header.delta_frame_id_length_bits = 2 + scratch; + OBU_READ_LITERAL_OR_FAIL(3); + sequence_header.frame_id_length_bits = + sequence_header.delta_frame_id_length_bits + 1 + scratch; + // Section 6.8.2: It is a requirement of bitstream conformance that the + // number of bits needed to read display_frame_id does not exceed 16. This + // is equivalent to the constraint that idLen <= 16. + if (sequence_header.frame_id_length_bits > 16) { + LIBGAV1_DLOG(ERROR, "Invalid frame_id_length_bits: %d.", + sequence_header.frame_id_length_bits); + return false; + } + } + OBU_READ_BIT_OR_FAIL; + sequence_header.use_128x128_superblock = static_cast<bool>(scratch); + OBU_READ_BIT_OR_FAIL; + sequence_header.enable_filter_intra = static_cast<bool>(scratch); + OBU_READ_BIT_OR_FAIL; + sequence_header.enable_intra_edge_filter = static_cast<bool>(scratch); + if (sequence_header.reduced_still_picture_header) { + sequence_header.force_screen_content_tools = kSelectScreenContentTools; + sequence_header.force_integer_mv = kSelectIntegerMv; + } else { + OBU_READ_BIT_OR_FAIL; + sequence_header.enable_interintra_compound = static_cast<bool>(scratch); + OBU_READ_BIT_OR_FAIL; + sequence_header.enable_masked_compound = static_cast<bool>(scratch); + OBU_READ_BIT_OR_FAIL; + sequence_header.enable_warped_motion = static_cast<bool>(scratch); + OBU_READ_BIT_OR_FAIL; + sequence_header.enable_dual_filter = static_cast<bool>(scratch); + OBU_READ_BIT_OR_FAIL; + sequence_header.enable_order_hint = static_cast<bool>(scratch); + if (sequence_header.enable_order_hint) { + OBU_READ_BIT_OR_FAIL; + sequence_header.enable_jnt_comp = static_cast<bool>(scratch); + OBU_READ_BIT_OR_FAIL; + sequence_header.enable_ref_frame_mvs = static_cast<bool>(scratch); + } + OBU_READ_BIT_OR_FAIL; + sequence_header.choose_screen_content_tools = static_cast<bool>(scratch); + if (sequence_header.choose_screen_content_tools) { + sequence_header.force_screen_content_tools = kSelectScreenContentTools; + } else { + OBU_READ_BIT_OR_FAIL; + sequence_header.force_screen_content_tools = scratch; + } + if (sequence_header.force_screen_content_tools > 0) { + OBU_READ_BIT_OR_FAIL; + sequence_header.choose_integer_mv = static_cast<bool>(scratch); + if (sequence_header.choose_integer_mv) { + sequence_header.force_integer_mv = kSelectIntegerMv; + } else { + OBU_READ_BIT_OR_FAIL; + sequence_header.force_integer_mv = scratch; + } + } else { + sequence_header.force_integer_mv = kSelectIntegerMv; + } + if (sequence_header.enable_order_hint) { + OBU_READ_LITERAL_OR_FAIL(3); + sequence_header.order_hint_bits = 1 + scratch; + sequence_header.order_hint_shift_bits = + Mod32(32 - sequence_header.order_hint_bits); + } + } + OBU_READ_BIT_OR_FAIL; + sequence_header.enable_superres = static_cast<bool>(scratch); + OBU_READ_BIT_OR_FAIL; + sequence_header.enable_cdef = static_cast<bool>(scratch); + OBU_READ_BIT_OR_FAIL; + sequence_header.enable_restoration = static_cast<bool>(scratch); + if (!ParseColorConfig(&sequence_header)) return false; + OBU_READ_BIT_OR_FAIL; + sequence_header.film_grain_params_present = static_cast<bool>(scratch); + // Compare new sequence header with old sequence header. + if (has_sequence_header_ && + sequence_header.ParametersChanged(sequence_header_)) { + // Between the frame header OBU and the last tile group OBU of the frame, + // do not allow the sequence header to change. + if (seen_frame_header) { + LIBGAV1_DLOG(ERROR, "Sequence header changed in the middle of a frame."); + return false; + } + decoder_state_.ClearReferenceFrames(); + } + sequence_header_ = sequence_header; + has_sequence_header_ = true; + // Section 6.4.1: It is a requirement of bitstream conformance that if + // OperatingPointIdc is equal to 0, then obu_extension_flag is equal to 0 for + // all OBUs that follow this sequence header until the next sequence header. + extension_disallowed_ = + (sequence_header_.operating_point_idc[operating_point_] == 0); + return true; +} + +// Marks reference frames as invalid for referencing when they are too far in +// the past to be referenced by the frame id mechanism. +void ObuParser::MarkInvalidReferenceFrames() { + // The current lower bound of the frame ids for reference frames. + int lower_bound = decoder_state_.current_frame_id - + (1 << sequence_header_.delta_frame_id_length_bits); + // True if lower_bound is smaller than current_frame_id. False if lower_bound + // wraps around (in modular arithmetic) to the other side of current_frame_id. + bool lower_bound_is_smaller = true; + if (lower_bound <= 0) { + lower_bound += 1 << sequence_header_.frame_id_length_bits; + lower_bound_is_smaller = false; + } + for (int i = 0; i < kNumReferenceFrameTypes; ++i) { + const uint16_t reference_frame_id = decoder_state_.reference_frame_id[i]; + if (lower_bound_is_smaller) { + if (reference_frame_id > decoder_state_.current_frame_id || + reference_frame_id < lower_bound) { + decoder_state_.reference_valid[i] = false; + } + } else { + if (reference_frame_id > decoder_state_.current_frame_id && + reference_frame_id < lower_bound) { + decoder_state_.reference_valid[i] = false; + } + } + } +} + +bool ObuParser::ParseFrameSizeAndRenderSize() { + int64_t scratch; + // Frame Size. + if (frame_header_.frame_size_override_flag) { + OBU_READ_LITERAL_OR_FAIL(sequence_header_.frame_width_bits); + frame_header_.width = static_cast<int32_t>(1 + scratch); + OBU_READ_LITERAL_OR_FAIL(sequence_header_.frame_height_bits); + frame_header_.height = static_cast<int32_t>(1 + scratch); + if (frame_header_.width > sequence_header_.max_frame_width || + frame_header_.height > sequence_header_.max_frame_height) { + LIBGAV1_DLOG(ERROR, + "Frame dimensions are larger than the maximum values"); + return false; + } + } else { + frame_header_.width = sequence_header_.max_frame_width; + frame_header_.height = sequence_header_.max_frame_height; + } + if (!ParseSuperResParametersAndComputeImageSize()) return false; + + // Render Size. + OBU_READ_BIT_OR_FAIL; + frame_header_.render_and_frame_size_different = static_cast<bool>(scratch); + if (frame_header_.render_and_frame_size_different) { + OBU_READ_LITERAL_OR_FAIL(16); + frame_header_.render_width = static_cast<int32_t>(1 + scratch); + OBU_READ_LITERAL_OR_FAIL(16); + frame_header_.render_height = static_cast<int32_t>(1 + scratch); + } else { + frame_header_.render_width = frame_header_.upscaled_width; + frame_header_.render_height = frame_header_.height; + } + + return true; +} + +bool ObuParser::ParseSuperResParametersAndComputeImageSize() { + int64_t scratch; + // SuperRes. + frame_header_.upscaled_width = frame_header_.width; + frame_header_.use_superres = false; + if (sequence_header_.enable_superres) { + OBU_READ_BIT_OR_FAIL; + frame_header_.use_superres = static_cast<bool>(scratch); + } + if (frame_header_.use_superres) { + OBU_READ_LITERAL_OR_FAIL(3); + // 9 is the smallest value for the denominator. + frame_header_.superres_scale_denominator = scratch + 9; + frame_header_.width = + (frame_header_.upscaled_width * kSuperResScaleNumerator + + (frame_header_.superres_scale_denominator / 2)) / + frame_header_.superres_scale_denominator; + } else { + frame_header_.superres_scale_denominator = kSuperResScaleNumerator; + } + assert(frame_header_.width != 0); + assert(frame_header_.height != 0); + // Check if multiplying upscaled_width by height would overflow. + assert(frame_header_.upscaled_width >= frame_header_.width); + if (frame_header_.upscaled_width > INT32_MAX / frame_header_.height) { + LIBGAV1_DLOG(ERROR, "Frame dimensions too big: width=%d height=%d.", + frame_header_.width, frame_header_.height); + return false; + } + frame_header_.columns4x4 = ((frame_header_.width + 7) >> 3) << 1; + frame_header_.rows4x4 = ((frame_header_.height + 7) >> 3) << 1; + return true; +} + +bool ObuParser::ValidateInterFrameSize() const { + for (int index : frame_header_.reference_frame_index) { + const RefCountedBuffer* reference_frame = + decoder_state_.reference_frame[index].get(); + if (2 * frame_header_.width < reference_frame->upscaled_width() || + 2 * frame_header_.height < reference_frame->frame_height() || + frame_header_.width > 16 * reference_frame->upscaled_width() || + frame_header_.height > 16 * reference_frame->frame_height()) { + LIBGAV1_DLOG(ERROR, + "Invalid inter frame size: width=%d, height=%d. Reference " + "frame: index=%d, upscaled width=%d, height=%d.", + frame_header_.width, frame_header_.height, index, + reference_frame->upscaled_width(), + reference_frame->frame_height()); + return false; + } + } + return true; +} + +bool ObuParser::ParseReferenceOrderHint() { + if (!frame_header_.error_resilient_mode || + !sequence_header_.enable_order_hint) { + return true; + } + int64_t scratch; + for (int i = 0; i < kNumReferenceFrameTypes; ++i) { + OBU_READ_LITERAL_OR_FAIL(sequence_header_.order_hint_bits); + frame_header_.reference_order_hint[i] = scratch; + if (frame_header_.reference_order_hint[i] != + decoder_state_.reference_order_hint[i]) { + decoder_state_.reference_valid[i] = false; + } + } + return true; +} + +// static +int ObuParser::FindLatestBackwardReference( + const int current_frame_hint, + const std::array<int, kNumReferenceFrameTypes>& shifted_order_hints, + const std::array<bool, kNumReferenceFrameTypes>& used_frame) { + int ref = -1; + int latest_order_hint = INT_MIN; + for (int i = 0; i < kNumReferenceFrameTypes; ++i) { + const int hint = shifted_order_hints[i]; + if (!used_frame[i] && hint >= current_frame_hint && + hint >= latest_order_hint) { + ref = i; + latest_order_hint = hint; + } + } + return ref; +} + +// static +int ObuParser::FindEarliestBackwardReference( + const int current_frame_hint, + const std::array<int, kNumReferenceFrameTypes>& shifted_order_hints, + const std::array<bool, kNumReferenceFrameTypes>& used_frame) { + int ref = -1; + int earliest_order_hint = INT_MAX; + for (int i = 0; i < kNumReferenceFrameTypes; ++i) { + const int hint = shifted_order_hints[i]; + if (!used_frame[i] && hint >= current_frame_hint && + hint < earliest_order_hint) { + ref = i; + earliest_order_hint = hint; + } + } + return ref; +} + +// static +int ObuParser::FindLatestForwardReference( + const int current_frame_hint, + const std::array<int, kNumReferenceFrameTypes>& shifted_order_hints, + const std::array<bool, kNumReferenceFrameTypes>& used_frame) { + int ref = -1; + int latest_order_hint = INT_MIN; + for (int i = 0; i < kNumReferenceFrameTypes; ++i) { + const int hint = shifted_order_hints[i]; + if (!used_frame[i] && hint < current_frame_hint && + hint >= latest_order_hint) { + ref = i; + latest_order_hint = hint; + } + } + return ref; +} + +// static +int ObuParser::FindReferenceWithSmallestOutputOrder( + const std::array<int, kNumReferenceFrameTypes>& shifted_order_hints) { + int ref = -1; + int earliest_order_hint = INT_MAX; + for (int i = 0; i < kNumReferenceFrameTypes; ++i) { + const int hint = shifted_order_hints[i]; + if (hint < earliest_order_hint) { + ref = i; + earliest_order_hint = hint; + } + } + return ref; +} + +// Computes the elements in the frame_header_.reference_frame_index array +// based on: +// * the syntax elements last_frame_idx and gold_frame_idx, and +// * the values stored within the decoder_state_.reference_order_hint array +// (these values represent the least significant bits of the expected output +// order of the frames). +// +// Frame type: { +// libgav1_name spec_name int +// kReferenceFrameLast, LAST_FRAME 1 +// kReferenceFrameLast2, LAST2_FRAME 2 +// kReferenceFrameLast3, LAST3_FRAME 3 +// kReferenceFrameGolden, GOLDEN_FRAME 4 +// kReferenceFrameBackward, BWDREF_FRAME 5 +// kReferenceFrameAlternate2, ALTREF2_FRAME 6 +// kReferenceFrameAlternate, ALTREF_FRAME 7 +// } +// +// A typical case of a group of pictures (frames) in display order: +// (However, more complex cases are possibly allowed in terms of +// bitstream conformance.) +// +// | | | | | | | | +// | | | | | | | | +// | | | | | | | | +// | | | | | | | | +// +// 4 3 2 1 current_frame 5 6 7 +// +bool ObuParser::SetFrameReferences(const int8_t last_frame_idx, + const int8_t gold_frame_idx) { + // Set the ref_frame_idx entries for kReferenceFrameLast and + // kReferenceFrameGolden to last_frame_idx and gold_frame_idx. Initialize + // the other entries to -1. + for (int8_t& reference_frame_index : frame_header_.reference_frame_index) { + reference_frame_index = -1; + } + frame_header_ + .reference_frame_index[kReferenceFrameLast - kReferenceFrameLast] = + last_frame_idx; + frame_header_ + .reference_frame_index[kReferenceFrameGolden - kReferenceFrameLast] = + gold_frame_idx; + + // used_frame records which reference frames have been used. + std::array<bool, kNumReferenceFrameTypes> used_frame; + used_frame.fill(false); + used_frame[last_frame_idx] = true; + used_frame[gold_frame_idx] = true; + + assert(sequence_header_.order_hint_bits >= 1); + const int current_frame_hint = 1 << (sequence_header_.order_hint_bits - 1); + // shifted_order_hints contains the expected output order shifted such that + // the current frame has hint equal to current_frame_hint. + std::array<int, kNumReferenceFrameTypes> shifted_order_hints; + for (int i = 0; i < kNumReferenceFrameTypes; ++i) { + const int relative_distance = GetRelativeDistance( + decoder_state_.reference_order_hint[i], frame_header_.order_hint, + sequence_header_.order_hint_shift_bits); + shifted_order_hints[i] = current_frame_hint + relative_distance; + } + + // The expected output orders for kReferenceFrameLast and + // kReferenceFrameGolden. + const int last_order_hint = shifted_order_hints[last_frame_idx]; + const int gold_order_hint = shifted_order_hints[gold_frame_idx]; + + // Section 7.8: It is a requirement of bitstream conformance that + // lastOrderHint and goldOrderHint are strictly less than curFrameHint. + if (last_order_hint >= current_frame_hint || + gold_order_hint >= current_frame_hint) { + return false; + } + + // Find a backward reference to the frame with highest output order. If + // found, set the kReferenceFrameAlternate reference to that backward + // reference. + int ref = FindLatestBackwardReference(current_frame_hint, shifted_order_hints, + used_frame); + if (ref >= 0) { + frame_header_ + .reference_frame_index[kReferenceFrameAlternate - kReferenceFrameLast] = + ref; + used_frame[ref] = true; + } + + // Find a backward reference to the closest frame. If found, set the + // kReferenceFrameBackward reference to that backward reference. + ref = FindEarliestBackwardReference(current_frame_hint, shifted_order_hints, + used_frame); + if (ref >= 0) { + frame_header_ + .reference_frame_index[kReferenceFrameBackward - kReferenceFrameLast] = + ref; + used_frame[ref] = true; + } + + // Set the kReferenceFrameAlternate2 reference to the next closest backward + // reference. + ref = FindEarliestBackwardReference(current_frame_hint, shifted_order_hints, + used_frame); + if (ref >= 0) { + frame_header_.reference_frame_index[kReferenceFrameAlternate2 - + kReferenceFrameLast] = ref; + used_frame[ref] = true; + } + + // The remaining references are set to be forward references in + // reverse chronological order. + static constexpr ReferenceFrameType + kRefFrameList[kNumInterReferenceFrameTypes - 2] = { + kReferenceFrameLast2, kReferenceFrameLast3, kReferenceFrameBackward, + kReferenceFrameAlternate2, kReferenceFrameAlternate}; + for (const ReferenceFrameType ref_frame : kRefFrameList) { + if (frame_header_.reference_frame_index[ref_frame - kReferenceFrameLast] < + 0) { + ref = FindLatestForwardReference(current_frame_hint, shifted_order_hints, + used_frame); + if (ref >= 0) { + frame_header_.reference_frame_index[ref_frame - kReferenceFrameLast] = + ref; + used_frame[ref] = true; + } + } + } + + // Finally, any remaining references are set to the reference frame with + // smallest output order. + ref = FindReferenceWithSmallestOutputOrder(shifted_order_hints); + assert(ref >= 0); + for (int8_t& reference_frame_index : frame_header_.reference_frame_index) { + if (reference_frame_index < 0) { + reference_frame_index = ref; + } + } + + return true; +} + +bool ObuParser::ParseLoopFilterParameters() { + LoopFilter* const loop_filter = &frame_header_.loop_filter; + if (frame_header_.coded_lossless || frame_header_.allow_intrabc) { + SetDefaultRefDeltas(loop_filter); + return true; + } + // IsIntraFrame implies kPrimaryReferenceNone. + assert(!IsIntraFrame(frame_header_.frame_type) || + frame_header_.primary_reference_frame == kPrimaryReferenceNone); + if (frame_header_.primary_reference_frame == kPrimaryReferenceNone) { + // Part of the setup_past_independence() function in the spec. It is not + // necessary to set loop_filter->delta_enabled to true. See + // https://crbug.com/aomedia/2305. + SetDefaultRefDeltas(loop_filter); + } else { + // Part of the load_previous() function in the spec. + const int prev_frame_index = + frame_header_ + .reference_frame_index[frame_header_.primary_reference_frame]; + const RefCountedBuffer* prev_frame = + decoder_state_.reference_frame[prev_frame_index].get(); + loop_filter->ref_deltas = prev_frame->loop_filter_ref_deltas(); + loop_filter->mode_deltas = prev_frame->loop_filter_mode_deltas(); + } + int64_t scratch; + for (int i = 0; i < 2; ++i) { + OBU_READ_LITERAL_OR_FAIL(6); + loop_filter->level[i] = scratch; + } + if (!sequence_header_.color_config.is_monochrome && + (loop_filter->level[0] != 0 || loop_filter->level[1] != 0)) { + for (int i = 2; i < 4; ++i) { + OBU_READ_LITERAL_OR_FAIL(6); + loop_filter->level[i] = scratch; + } + } + OBU_READ_LITERAL_OR_FAIL(3); + loop_filter->sharpness = scratch; + OBU_READ_BIT_OR_FAIL; + loop_filter->delta_enabled = static_cast<bool>(scratch); + if (loop_filter->delta_enabled) { + OBU_READ_BIT_OR_FAIL; + loop_filter->delta_update = static_cast<bool>(scratch); + if (loop_filter->delta_update) { + for (auto& ref_delta : loop_filter->ref_deltas) { + OBU_READ_BIT_OR_FAIL; + const auto update_ref_delta = static_cast<bool>(scratch); + if (update_ref_delta) { + int scratch_int; + if (!bit_reader_->ReadInverseSignedLiteral(6, &scratch_int)) { + LIBGAV1_DLOG(ERROR, "Not enough bits."); + return false; + } + ref_delta = scratch_int; + } + } + for (auto& mode_delta : loop_filter->mode_deltas) { + OBU_READ_BIT_OR_FAIL; + const auto update_mode_delta = static_cast<bool>(scratch); + if (update_mode_delta) { + int scratch_int; + if (!bit_reader_->ReadInverseSignedLiteral(6, &scratch_int)) { + LIBGAV1_DLOG(ERROR, "Not enough bits."); + return false; + } + mode_delta = scratch_int; + } + } + } + } else { + loop_filter->delta_update = false; + } + return true; +} + +bool ObuParser::ParseDeltaQuantizer(int8_t* const delta) { + int64_t scratch; + *delta = 0; + OBU_READ_BIT_OR_FAIL; + const auto delta_coded = static_cast<bool>(scratch); + if (delta_coded) { + int scratch_int; + if (!bit_reader_->ReadInverseSignedLiteral(6, &scratch_int)) { + LIBGAV1_DLOG(ERROR, "Not enough bits."); + return false; + } + *delta = scratch_int; + } + return true; +} + +bool ObuParser::ParseQuantizerParameters() { + int64_t scratch; + QuantizerParameters* const quantizer = &frame_header_.quantizer; + OBU_READ_LITERAL_OR_FAIL(8); + quantizer->base_index = scratch; + if (!ParseDeltaQuantizer(&quantizer->delta_dc[kPlaneY])) return false; + if (!sequence_header_.color_config.is_monochrome) { + bool diff_uv_delta = false; + if (sequence_header_.color_config.separate_uv_delta_q) { + OBU_READ_BIT_OR_FAIL; + diff_uv_delta = static_cast<bool>(scratch); + } + if (!ParseDeltaQuantizer(&quantizer->delta_dc[kPlaneU]) || + !ParseDeltaQuantizer(&quantizer->delta_ac[kPlaneU])) { + return false; + } + if (diff_uv_delta) { + if (!ParseDeltaQuantizer(&quantizer->delta_dc[kPlaneV]) || + !ParseDeltaQuantizer(&quantizer->delta_ac[kPlaneV])) { + return false; + } + } else { + quantizer->delta_dc[kPlaneV] = quantizer->delta_dc[kPlaneU]; + quantizer->delta_ac[kPlaneV] = quantizer->delta_ac[kPlaneU]; + } + } + OBU_READ_BIT_OR_FAIL; + quantizer->use_matrix = static_cast<bool>(scratch); + if (quantizer->use_matrix) { + OBU_READ_LITERAL_OR_FAIL(4); + quantizer->matrix_level[kPlaneY] = scratch; + OBU_READ_LITERAL_OR_FAIL(4); + quantizer->matrix_level[kPlaneU] = scratch; + if (sequence_header_.color_config.separate_uv_delta_q) { + OBU_READ_LITERAL_OR_FAIL(4); + quantizer->matrix_level[kPlaneV] = scratch; + } else { + quantizer->matrix_level[kPlaneV] = quantizer->matrix_level[kPlaneU]; + } + } + return true; +} + +// This method implements the following functions in the spec: +// - segmentation_params() +// - part of setup_past_independence(): Set the FeatureData and FeatureEnabled +// arrays to all 0. +// - part of load_previous(): Call load_segmentation_params(). +// +// A careful analysis of the spec shows the part of setup_past_independence() +// can be optimized away and the part of load_previous() only needs to be +// invoked under a specific condition. Although the logic looks different from +// the spec, it is equivalent and more efficient. +bool ObuParser::ParseSegmentationParameters() { + int64_t scratch; + Segmentation* const segmentation = &frame_header_.segmentation; + OBU_READ_BIT_OR_FAIL; + segmentation->enabled = static_cast<bool>(scratch); + if (!segmentation->enabled) return true; + if (frame_header_.primary_reference_frame == kPrimaryReferenceNone) { + segmentation->update_map = true; + segmentation->update_data = true; + } else { + OBU_READ_BIT_OR_FAIL; + segmentation->update_map = static_cast<bool>(scratch); + if (segmentation->update_map) { + OBU_READ_BIT_OR_FAIL; + segmentation->temporal_update = static_cast<bool>(scratch); + } + OBU_READ_BIT_OR_FAIL; + segmentation->update_data = static_cast<bool>(scratch); + if (!segmentation->update_data) { + // Part of the load_previous() function in the spec. + const int prev_frame_index = + frame_header_ + .reference_frame_index[frame_header_.primary_reference_frame]; + decoder_state_.reference_frame[prev_frame_index] + ->GetSegmentationParameters(segmentation); + return true; + } + } + for (int8_t i = 0; i < kMaxSegments; ++i) { + for (int8_t j = 0; j < kSegmentFeatureMax; ++j) { + OBU_READ_BIT_OR_FAIL; + segmentation->feature_enabled[i][j] = static_cast<bool>(scratch); + if (segmentation->feature_enabled[i][j]) { + if (Segmentation::FeatureSigned(static_cast<SegmentFeature>(j))) { + int scratch_int; + if (!bit_reader_->ReadInverseSignedLiteral( + kSegmentationFeatureBits[j], &scratch_int)) { + LIBGAV1_DLOG(ERROR, "Not enough bits."); + return false; + } + segmentation->feature_data[i][j] = + Clip3(scratch_int, -kSegmentationFeatureMaxValues[j], + kSegmentationFeatureMaxValues[j]); + } else { + if (kSegmentationFeatureBits[j] > 0) { + OBU_READ_LITERAL_OR_FAIL(kSegmentationFeatureBits[j]); + segmentation->feature_data[i][j] = Clip3( + static_cast<int>(scratch), 0, kSegmentationFeatureMaxValues[j]); + } else { + segmentation->feature_data[i][j] = 0; + } + } + segmentation->last_active_segment_id = i; + if (j >= kSegmentFeatureReferenceFrame) { + segmentation->segment_id_pre_skip = true; + } + } + } + } + return true; +} + +bool ObuParser::ParseQuantizerIndexDeltaParameters() { + int64_t scratch; + if (frame_header_.quantizer.base_index > 0) { + OBU_READ_BIT_OR_FAIL; + frame_header_.delta_q.present = static_cast<bool>(scratch); + if (frame_header_.delta_q.present) { + OBU_READ_LITERAL_OR_FAIL(2); + frame_header_.delta_q.scale = scratch; + } + } + return true; +} + +bool ObuParser::ParseLoopFilterDeltaParameters() { + int64_t scratch; + if (frame_header_.delta_q.present) { + if (!frame_header_.allow_intrabc) { + OBU_READ_BIT_OR_FAIL; + frame_header_.delta_lf.present = static_cast<bool>(scratch); + } + if (frame_header_.delta_lf.present) { + OBU_READ_LITERAL_OR_FAIL(2); + frame_header_.delta_lf.scale = scratch; + OBU_READ_BIT_OR_FAIL; + frame_header_.delta_lf.multi = static_cast<bool>(scratch); + } + } + return true; +} + +void ObuParser::ComputeSegmentLosslessAndQIndex() { + frame_header_.coded_lossless = true; + Segmentation* const segmentation = &frame_header_.segmentation; + const QuantizerParameters* const quantizer = &frame_header_.quantizer; + for (int i = 0; i < kMaxSegments; ++i) { + segmentation->qindex[i] = + GetQIndex(*segmentation, i, quantizer->base_index); + segmentation->lossless[i] = + segmentation->qindex[i] == 0 && quantizer->delta_dc[kPlaneY] == 0 && + quantizer->delta_dc[kPlaneU] == 0 && + quantizer->delta_ac[kPlaneU] == 0 && + quantizer->delta_dc[kPlaneV] == 0 && quantizer->delta_ac[kPlaneV] == 0; + if (!segmentation->lossless[i]) frame_header_.coded_lossless = false; + // The spec calls for setting up a two-dimensional SegQMLevel array here. + // We avoid the SegQMLevel array by using segmentation->lossless[i] and + // quantizer->matrix_level[plane] directly in the reconstruct process of + // Section 7.12.3. + } + frame_header_.upscaled_lossless = + frame_header_.coded_lossless && + frame_header_.width == frame_header_.upscaled_width; +} + +bool ObuParser::ParseCdefParameters() { + const int coeff_shift = sequence_header_.color_config.bitdepth - 8; + if (frame_header_.coded_lossless || frame_header_.allow_intrabc || + !sequence_header_.enable_cdef) { + frame_header_.cdef.damping = 3 + coeff_shift; + return true; + } + Cdef* const cdef = &frame_header_.cdef; + int64_t scratch; + OBU_READ_LITERAL_OR_FAIL(2); + cdef->damping = scratch + 3 + coeff_shift; + OBU_READ_LITERAL_OR_FAIL(2); + cdef->bits = scratch; + for (int i = 0; i < (1 << cdef->bits); ++i) { + OBU_READ_LITERAL_OR_FAIL(4); + cdef->y_primary_strength[i] = scratch << coeff_shift; + OBU_READ_LITERAL_OR_FAIL(2); + cdef->y_secondary_strength[i] = scratch; + if (cdef->y_secondary_strength[i] == 3) ++cdef->y_secondary_strength[i]; + cdef->y_secondary_strength[i] <<= coeff_shift; + if (sequence_header_.color_config.is_monochrome) continue; + OBU_READ_LITERAL_OR_FAIL(4); + cdef->uv_primary_strength[i] = scratch << coeff_shift; + OBU_READ_LITERAL_OR_FAIL(2); + cdef->uv_secondary_strength[i] = scratch; + if (cdef->uv_secondary_strength[i] == 3) ++cdef->uv_secondary_strength[i]; + cdef->uv_secondary_strength[i] <<= coeff_shift; + } + return true; +} + +bool ObuParser::ParseLoopRestorationParameters() { + if (frame_header_.upscaled_lossless || frame_header_.allow_intrabc || + !sequence_header_.enable_restoration) { + return true; + } + int64_t scratch; + bool uses_loop_restoration = false; + bool uses_chroma_loop_restoration = false; + LoopRestoration* const loop_restoration = &frame_header_.loop_restoration; + const int num_planes = sequence_header_.color_config.is_monochrome + ? kMaxPlanesMonochrome + : kMaxPlanes; + for (int i = 0; i < num_planes; ++i) { + OBU_READ_LITERAL_OR_FAIL(2); + loop_restoration->type[i] = static_cast<LoopRestorationType>(scratch); + if (loop_restoration->type[i] != kLoopRestorationTypeNone) { + uses_loop_restoration = true; + if (i > 0) uses_chroma_loop_restoration = true; + } + } + if (uses_loop_restoration) { + uint8_t unit_shift; + if (sequence_header_.use_128x128_superblock) { + OBU_READ_BIT_OR_FAIL; + unit_shift = scratch + 1; + } else { + OBU_READ_BIT_OR_FAIL; + unit_shift = scratch; + if (unit_shift != 0) { + OBU_READ_BIT_OR_FAIL; + const uint8_t unit_extra_shift = scratch; + unit_shift += unit_extra_shift; + } + } + loop_restoration->unit_size_log2[kPlaneY] = 6 + unit_shift; + uint8_t uv_shift = 0; + if (sequence_header_.color_config.subsampling_x != 0 && + sequence_header_.color_config.subsampling_y != 0 && + uses_chroma_loop_restoration) { + OBU_READ_BIT_OR_FAIL; + uv_shift = scratch; + } + loop_restoration->unit_size_log2[kPlaneU] = + loop_restoration->unit_size_log2[kPlaneV] = + loop_restoration->unit_size_log2[0] - uv_shift; + } + return true; +} + +bool ObuParser::ParseTxModeSyntax() { + if (frame_header_.coded_lossless) { + frame_header_.tx_mode = kTxModeOnly4x4; + return true; + } + int64_t scratch; + OBU_READ_BIT_OR_FAIL; + frame_header_.tx_mode = (scratch == 1) ? kTxModeSelect : kTxModeLargest; + return true; +} + +bool ObuParser::ParseFrameReferenceModeSyntax() { + int64_t scratch; + if (!IsIntraFrame(frame_header_.frame_type)) { + OBU_READ_BIT_OR_FAIL; + frame_header_.reference_mode_select = static_cast<bool>(scratch); + } + return true; +} + +bool ObuParser::IsSkipModeAllowed() { + if (IsIntraFrame(frame_header_.frame_type) || + !frame_header_.reference_mode_select || + !sequence_header_.enable_order_hint) { + return false; + } + // Identify the nearest forward and backward references. + int forward_index = -1; + int backward_index = -1; + int forward_hint = -1; + int backward_hint = -1; + for (int i = 0; i < kNumInterReferenceFrameTypes; ++i) { + const unsigned int reference_hint = + decoder_state_ + .reference_order_hint[frame_header_.reference_frame_index[i]]; + // TODO(linfengz): |relative_distance| equals + // current_frame_->reference_info()-> + // relative_distance_from[i + kReferenceFrameLast]; + // However, the unit test ObuParserTest.SkipModeParameters() would fail. + // Will figure out how to initialize |current_frame_.reference_info_| in the + // RefCountedBuffer later. + const int relative_distance = + GetRelativeDistance(reference_hint, frame_header_.order_hint, + sequence_header_.order_hint_shift_bits); + if (relative_distance < 0) { + if (forward_index < 0 || + GetRelativeDistance(reference_hint, forward_hint, + sequence_header_.order_hint_shift_bits) > 0) { + forward_index = i; + forward_hint = reference_hint; + } + } else if (relative_distance > 0) { + if (backward_index < 0 || + GetRelativeDistance(reference_hint, backward_hint, + sequence_header_.order_hint_shift_bits) < 0) { + backward_index = i; + backward_hint = reference_hint; + } + } + } + if (forward_index < 0) return false; + if (backward_index >= 0) { + // Bidirectional prediction. + frame_header_.skip_mode_frame[0] = static_cast<ReferenceFrameType>( + kReferenceFrameLast + std::min(forward_index, backward_index)); + frame_header_.skip_mode_frame[1] = static_cast<ReferenceFrameType>( + kReferenceFrameLast + std::max(forward_index, backward_index)); + return true; + } + // Forward prediction only. Identify the second nearest forward reference. + int second_forward_index = -1; + int second_forward_hint = -1; + for (int i = 0; i < kNumInterReferenceFrameTypes; ++i) { + const unsigned int reference_hint = + decoder_state_ + .reference_order_hint[frame_header_.reference_frame_index[i]]; + if (GetRelativeDistance(reference_hint, forward_hint, + sequence_header_.order_hint_shift_bits) < 0) { + if (second_forward_index < 0 || + GetRelativeDistance(reference_hint, second_forward_hint, + sequence_header_.order_hint_shift_bits) > 0) { + second_forward_index = i; + second_forward_hint = reference_hint; + } + } + } + if (second_forward_index < 0) return false; + frame_header_.skip_mode_frame[0] = static_cast<ReferenceFrameType>( + kReferenceFrameLast + std::min(forward_index, second_forward_index)); + frame_header_.skip_mode_frame[1] = static_cast<ReferenceFrameType>( + kReferenceFrameLast + std::max(forward_index, second_forward_index)); + return true; +} + +bool ObuParser::ParseSkipModeParameters() { + if (!IsSkipModeAllowed()) return true; + int64_t scratch; + OBU_READ_BIT_OR_FAIL; + frame_header_.skip_mode_present = static_cast<bool>(scratch); + return true; +} + +// Sets frame_header_.global_motion[ref].params[index]. +bool ObuParser::ParseGlobalParamSyntax( + int ref, int index, + const std::array<GlobalMotion, kNumReferenceFrameTypes>& + prev_global_motions) { + GlobalMotion* const global_motion = &frame_header_.global_motion[ref]; + const GlobalMotion* const prev_global_motion = &prev_global_motions[ref]; + int abs_bits = kGlobalMotionAlphaBits; + int precision_bits = kGlobalMotionAlphaPrecisionBits; + if (index < 2) { + if (global_motion->type == kGlobalMotionTransformationTypeTranslation) { + const auto high_precision_mv_factor = + static_cast<int>(!frame_header_.allow_high_precision_mv); + abs_bits = kGlobalMotionTranslationOnlyBits - high_precision_mv_factor; + precision_bits = + kGlobalMotionTranslationOnlyPrecisionBits - high_precision_mv_factor; + } else { + abs_bits = kGlobalMotionTranslationBits; + precision_bits = kGlobalMotionTranslationPrecisionBits; + } + } + const int precision_diff = kWarpedModelPrecisionBits - precision_bits; + const int round = (index % 3 == 2) ? 1 << kWarpedModelPrecisionBits : 0; + const int sub = (index % 3 == 2) ? 1 << precision_bits : 0; + const int mx = 1 << abs_bits; + const int reference = + (prev_global_motion->params[index] >> precision_diff) - sub; + int scratch; + if (!bit_reader_->DecodeSignedSubexpWithReference( + -mx, mx + 1, reference, kGlobalMotionReadControl, &scratch)) { + LIBGAV1_DLOG(ERROR, "Not enough bits."); + return false; + } + global_motion->params[index] = LeftShift(scratch, precision_diff) + round; + return true; +} + +bool ObuParser::ParseGlobalMotionParameters() { + for (int ref = kReferenceFrameLast; ref <= kReferenceFrameAlternate; ++ref) { + frame_header_.global_motion[ref].type = + kGlobalMotionTransformationTypeIdentity; + for (int i = 0; i < 6; ++i) { + frame_header_.global_motion[ref].params[i] = + (i % 3 == 2) ? 1 << kWarpedModelPrecisionBits : 0; + } + } + if (IsIntraFrame(frame_header_.frame_type)) return true; + const std::array<GlobalMotion, kNumReferenceFrameTypes>* prev_global_motions = + nullptr; + if (frame_header_.primary_reference_frame == kPrimaryReferenceNone) { + // Part of the setup_past_independence() function in the spec. The value + // that the spec says PrevGmParams[ref][i] should be set to is exactly + // the value frame_header_.global_motion[ref].params[i] is set to by the + // for loop above. Therefore prev_global_motions can simply point to + // frame_header_.global_motion. + prev_global_motions = &frame_header_.global_motion; + } else { + // Part of the load_previous() function in the spec. + const int prev_frame_index = + frame_header_ + .reference_frame_index[frame_header_.primary_reference_frame]; + prev_global_motions = + &decoder_state_.reference_frame[prev_frame_index]->GlobalMotions(); + } + for (int ref = kReferenceFrameLast; ref <= kReferenceFrameAlternate; ++ref) { + GlobalMotion* const global_motion = &frame_header_.global_motion[ref]; + int64_t scratch; + OBU_READ_BIT_OR_FAIL; + const auto is_global = static_cast<bool>(scratch); + if (is_global) { + OBU_READ_BIT_OR_FAIL; + const auto is_rot_zoom = static_cast<bool>(scratch); + if (is_rot_zoom) { + global_motion->type = kGlobalMotionTransformationTypeRotZoom; + } else { + OBU_READ_BIT_OR_FAIL; + const auto is_translation = static_cast<bool>(scratch); + global_motion->type = is_translation + ? kGlobalMotionTransformationTypeTranslation + : kGlobalMotionTransformationTypeAffine; + } + } else { + global_motion->type = kGlobalMotionTransformationTypeIdentity; + } + if (global_motion->type >= kGlobalMotionTransformationTypeRotZoom) { + if (!ParseGlobalParamSyntax(ref, 2, *prev_global_motions) || + !ParseGlobalParamSyntax(ref, 3, *prev_global_motions)) { + return false; + } + if (global_motion->type == kGlobalMotionTransformationTypeAffine) { + if (!ParseGlobalParamSyntax(ref, 4, *prev_global_motions) || + !ParseGlobalParamSyntax(ref, 5, *prev_global_motions)) { + return false; + } + } else { + global_motion->params[4] = -global_motion->params[3]; + global_motion->params[5] = global_motion->params[2]; + } + } + if (global_motion->type >= kGlobalMotionTransformationTypeTranslation) { + if (!ParseGlobalParamSyntax(ref, 0, *prev_global_motions) || + !ParseGlobalParamSyntax(ref, 1, *prev_global_motions)) { + return false; + } + } + } + return true; +} + +bool ObuParser::ParseFilmGrainParameters() { + if (!sequence_header_.film_grain_params_present || + (!frame_header_.show_frame && !frame_header_.showable_frame)) { + // frame_header_.film_grain_params is already zero-initialized. + return true; + } + + FilmGrainParams& film_grain_params = frame_header_.film_grain_params; + int64_t scratch; + OBU_READ_BIT_OR_FAIL; + film_grain_params.apply_grain = static_cast<bool>(scratch); + if (!film_grain_params.apply_grain) { + // film_grain_params is already zero-initialized. + return true; + } + + OBU_READ_LITERAL_OR_FAIL(16); + film_grain_params.grain_seed = static_cast<int>(scratch); + film_grain_params.update_grain = true; + if (frame_header_.frame_type == kFrameInter) { + OBU_READ_BIT_OR_FAIL; + film_grain_params.update_grain = static_cast<bool>(scratch); + } + if (!film_grain_params.update_grain) { + OBU_READ_LITERAL_OR_FAIL(3); + film_grain_params.reference_index = static_cast<int>(scratch); + bool found = false; + for (const auto index : frame_header_.reference_frame_index) { + if (film_grain_params.reference_index == index) { + found = true; + break; + } + } + if (!found) { + static_assert(sizeof(frame_header_.reference_frame_index) / + sizeof(frame_header_.reference_frame_index[0]) == + 7, + ""); + LIBGAV1_DLOG(ERROR, + "Invalid value for film_grain_params_ref_idx (%d). " + "ref_frame_idx = {%d, %d, %d, %d, %d, %d, %d}", + film_grain_params.reference_index, + frame_header_.reference_frame_index[0], + frame_header_.reference_frame_index[1], + frame_header_.reference_frame_index[2], + frame_header_.reference_frame_index[3], + frame_header_.reference_frame_index[4], + frame_header_.reference_frame_index[5], + frame_header_.reference_frame_index[6]); + return false; + } + const RefCountedBuffer* grain_params_reference_frame = + decoder_state_.reference_frame[film_grain_params.reference_index].get(); + if (grain_params_reference_frame == nullptr) { + LIBGAV1_DLOG(ERROR, "Buffer %d does not contain a decoded frame", + film_grain_params.reference_index); + return false; + } + const int temp_grain_seed = film_grain_params.grain_seed; + const bool temp_update_grain = film_grain_params.update_grain; + const int temp_reference_index = film_grain_params.reference_index; + film_grain_params = grain_params_reference_frame->film_grain_params(); + film_grain_params.grain_seed = temp_grain_seed; + film_grain_params.update_grain = temp_update_grain; + film_grain_params.reference_index = temp_reference_index; + return true; + } + + OBU_READ_LITERAL_OR_FAIL(4); + film_grain_params.num_y_points = scratch; + if (film_grain_params.num_y_points > 14) { + LIBGAV1_DLOG(ERROR, "Invalid value for num_y_points (%d).", + film_grain_params.num_y_points); + return false; + } + for (int i = 0; i < film_grain_params.num_y_points; ++i) { + OBU_READ_LITERAL_OR_FAIL(8); + film_grain_params.point_y_value[i] = scratch; + if (i != 0 && film_grain_params.point_y_value[i - 1] >= + film_grain_params.point_y_value[i]) { + LIBGAV1_DLOG(ERROR, "point_y_value[%d] (%d) >= point_y_value[%d] (%d).", + i - 1, film_grain_params.point_y_value[i - 1], i, + film_grain_params.point_y_value[i]); + return false; + } + OBU_READ_LITERAL_OR_FAIL(8); + film_grain_params.point_y_scaling[i] = scratch; + } + if (sequence_header_.color_config.is_monochrome) { + film_grain_params.chroma_scaling_from_luma = false; + } else { + OBU_READ_BIT_OR_FAIL; + film_grain_params.chroma_scaling_from_luma = static_cast<bool>(scratch); + } + if (sequence_header_.color_config.is_monochrome || + film_grain_params.chroma_scaling_from_luma || + (sequence_header_.color_config.subsampling_x == 1 && + sequence_header_.color_config.subsampling_y == 1 && + film_grain_params.num_y_points == 0)) { + film_grain_params.num_u_points = 0; + film_grain_params.num_v_points = 0; + } else { + OBU_READ_LITERAL_OR_FAIL(4); + film_grain_params.num_u_points = scratch; + if (film_grain_params.num_u_points > 10) { + LIBGAV1_DLOG(ERROR, "Invalid value for num_u_points (%d).", + film_grain_params.num_u_points); + return false; + } + for (int i = 0; i < film_grain_params.num_u_points; ++i) { + OBU_READ_LITERAL_OR_FAIL(8); + film_grain_params.point_u_value[i] = scratch; + if (i != 0 && film_grain_params.point_u_value[i - 1] >= + film_grain_params.point_u_value[i]) { + LIBGAV1_DLOG(ERROR, "point_u_value[%d] (%d) >= point_u_value[%d] (%d).", + i - 1, film_grain_params.point_u_value[i - 1], i, + film_grain_params.point_u_value[i]); + return false; + } + OBU_READ_LITERAL_OR_FAIL(8); + film_grain_params.point_u_scaling[i] = scratch; + } + OBU_READ_LITERAL_OR_FAIL(4); + film_grain_params.num_v_points = scratch; + if (film_grain_params.num_v_points > 10) { + LIBGAV1_DLOG(ERROR, "Invalid value for num_v_points (%d).", + film_grain_params.num_v_points); + return false; + } + if (sequence_header_.color_config.subsampling_x == 1 && + sequence_header_.color_config.subsampling_y == 1 && + (film_grain_params.num_u_points == 0) != + (film_grain_params.num_v_points == 0)) { + LIBGAV1_DLOG(ERROR, + "Invalid values for num_u_points (%d) and num_v_points (%d) " + "for 4:2:0 chroma subsampling.", + film_grain_params.num_u_points, + film_grain_params.num_v_points); + return false; + } + for (int i = 0; i < film_grain_params.num_v_points; ++i) { + OBU_READ_LITERAL_OR_FAIL(8); + film_grain_params.point_v_value[i] = scratch; + if (i != 0 && film_grain_params.point_v_value[i - 1] >= + film_grain_params.point_v_value[i]) { + LIBGAV1_DLOG(ERROR, "point_v_value[%d] (%d) >= point_v_value[%d] (%d).", + i - 1, film_grain_params.point_v_value[i - 1], i, + film_grain_params.point_v_value[i]); + return false; + } + OBU_READ_LITERAL_OR_FAIL(8); + film_grain_params.point_v_scaling[i] = scratch; + } + } + OBU_READ_LITERAL_OR_FAIL(2); + film_grain_params.chroma_scaling = scratch + 8; + OBU_READ_LITERAL_OR_FAIL(2); + film_grain_params.auto_regression_coeff_lag = scratch; + + const int num_pos_y = + MultiplyBy2(film_grain_params.auto_regression_coeff_lag) * + (film_grain_params.auto_regression_coeff_lag + 1); + int num_pos_uv = num_pos_y; + if (film_grain_params.num_y_points > 0) { + ++num_pos_uv; + for (int i = 0; i < num_pos_y; ++i) { + OBU_READ_LITERAL_OR_FAIL(8); + film_grain_params.auto_regression_coeff_y[i] = + static_cast<int8_t>(scratch - 128); + } + } + if (film_grain_params.chroma_scaling_from_luma || + film_grain_params.num_u_points > 0) { + for (int i = 0; i < num_pos_uv; ++i) { + OBU_READ_LITERAL_OR_FAIL(8); + film_grain_params.auto_regression_coeff_u[i] = + static_cast<int8_t>(scratch - 128); + } + } + if (film_grain_params.chroma_scaling_from_luma || + film_grain_params.num_v_points > 0) { + for (int i = 0; i < num_pos_uv; ++i) { + OBU_READ_LITERAL_OR_FAIL(8); + film_grain_params.auto_regression_coeff_v[i] = + static_cast<int8_t>(scratch - 128); + } + } + OBU_READ_LITERAL_OR_FAIL(2); + film_grain_params.auto_regression_shift = static_cast<uint8_t>(scratch + 6); + OBU_READ_LITERAL_OR_FAIL(2); + film_grain_params.grain_scale_shift = static_cast<int>(scratch); + if (film_grain_params.num_u_points > 0) { + OBU_READ_LITERAL_OR_FAIL(8); + film_grain_params.u_multiplier = static_cast<int8_t>(scratch - 128); + OBU_READ_LITERAL_OR_FAIL(8); + film_grain_params.u_luma_multiplier = static_cast<int8_t>(scratch - 128); + OBU_READ_LITERAL_OR_FAIL(9); + film_grain_params.u_offset = static_cast<int16_t>(scratch - 256); + } + if (film_grain_params.num_v_points > 0) { + OBU_READ_LITERAL_OR_FAIL(8); + film_grain_params.v_multiplier = static_cast<int8_t>(scratch - 128); + OBU_READ_LITERAL_OR_FAIL(8); + film_grain_params.v_luma_multiplier = static_cast<int8_t>(scratch - 128); + OBU_READ_LITERAL_OR_FAIL(9); + film_grain_params.v_offset = static_cast<int16_t>(scratch - 256); + } + OBU_READ_BIT_OR_FAIL; + film_grain_params.overlap_flag = static_cast<bool>(scratch); + OBU_READ_BIT_OR_FAIL; + film_grain_params.clip_to_restricted_range = static_cast<bool>(scratch); + return true; +} + +bool ObuParser::ParseTileInfoSyntax() { + TileInfo* const tile_info = &frame_header_.tile_info; + const int sb_columns = sequence_header_.use_128x128_superblock + ? ((frame_header_.columns4x4 + 31) >> 5) + : ((frame_header_.columns4x4 + 15) >> 4); + const int sb_rows = sequence_header_.use_128x128_superblock + ? ((frame_header_.rows4x4 + 31) >> 5) + : ((frame_header_.rows4x4 + 15) >> 4); + tile_info->sb_columns = sb_columns; + tile_info->sb_rows = sb_rows; + const int sb_shift = sequence_header_.use_128x128_superblock ? 5 : 4; + const int sb_size = 2 + sb_shift; + const int sb_max_tile_width = kMaxTileWidth >> sb_size; + const int sb_max_tile_area = kMaxTileArea >> MultiplyBy2(sb_size); + const int minlog2_tile_columns = TileLog2(sb_max_tile_width, sb_columns); + const int maxlog2_tile_columns = + CeilLog2(std::min(sb_columns, static_cast<int>(kMaxTileColumns))); + const int maxlog2_tile_rows = + CeilLog2(std::min(sb_rows, static_cast<int>(kMaxTileRows))); + const int min_log2_tiles = std::max( + minlog2_tile_columns, TileLog2(sb_max_tile_area, sb_rows * sb_columns)); + int64_t scratch; + OBU_READ_BIT_OR_FAIL; + tile_info->uniform_spacing = static_cast<bool>(scratch); + if (tile_info->uniform_spacing) { + // Read tile columns. + tile_info->tile_columns_log2 = minlog2_tile_columns; + while (tile_info->tile_columns_log2 < maxlog2_tile_columns) { + OBU_READ_BIT_OR_FAIL; + if (scratch == 0) break; + ++tile_info->tile_columns_log2; + } + + // Compute tile column starts. + const int sb_tile_width = + (sb_columns + (1 << tile_info->tile_columns_log2) - 1) >> + tile_info->tile_columns_log2; + if (sb_tile_width <= 0) return false; + int i = 0; + for (int sb_start = 0; sb_start < sb_columns; sb_start += sb_tile_width) { + if (i >= kMaxTileColumns) { + LIBGAV1_DLOG(ERROR, + "tile_columns would be greater than kMaxTileColumns."); + return false; + } + tile_info->tile_column_start[i++] = sb_start << sb_shift; + } + tile_info->tile_column_start[i] = frame_header_.columns4x4; + tile_info->tile_columns = i; + + // Read tile rows. + const int minlog2_tile_rows = + std::max(min_log2_tiles - tile_info->tile_columns_log2, 0); + tile_info->tile_rows_log2 = minlog2_tile_rows; + while (tile_info->tile_rows_log2 < maxlog2_tile_rows) { + OBU_READ_BIT_OR_FAIL; + if (scratch == 0) break; + ++tile_info->tile_rows_log2; + } + + // Compute tile row starts. + const int sb_tile_height = + (sb_rows + (1 << tile_info->tile_rows_log2) - 1) >> + tile_info->tile_rows_log2; + if (sb_tile_height <= 0) return false; + i = 0; + for (int sb_start = 0; sb_start < sb_rows; sb_start += sb_tile_height) { + if (i >= kMaxTileRows) { + LIBGAV1_DLOG(ERROR, "tile_rows would be greater than kMaxTileRows."); + return false; + } + tile_info->tile_row_start[i++] = sb_start << sb_shift; + } + tile_info->tile_row_start[i] = frame_header_.rows4x4; + tile_info->tile_rows = i; + } else { + int widest_tile_sb = 1; + int i = 0; + for (int sb_start = 0; sb_start < sb_columns; ++i) { + if (i >= kMaxTileColumns) { + LIBGAV1_DLOG(ERROR, + "tile_columns would be greater than kMaxTileColumns."); + return false; + } + tile_info->tile_column_start[i] = sb_start << sb_shift; + const int max_width = + std::min(sb_columns - sb_start, static_cast<int>(sb_max_tile_width)); + if (!bit_reader_->DecodeUniform( + max_width, &tile_info->tile_column_width_in_superblocks[i])) { + LIBGAV1_DLOG(ERROR, "Not enough bits."); + return false; + } + ++tile_info->tile_column_width_in_superblocks[i]; + widest_tile_sb = std::max(tile_info->tile_column_width_in_superblocks[i], + widest_tile_sb); + sb_start += tile_info->tile_column_width_in_superblocks[i]; + } + tile_info->tile_column_start[i] = frame_header_.columns4x4; + tile_info->tile_columns = i; + tile_info->tile_columns_log2 = CeilLog2(tile_info->tile_columns); + + int max_tile_area_sb = sb_rows * sb_columns; + if (min_log2_tiles > 0) max_tile_area_sb >>= min_log2_tiles + 1; + const int max_tile_height_sb = + std::max(max_tile_area_sb / widest_tile_sb, 1); + + i = 0; + for (int sb_start = 0; sb_start < sb_rows; ++i) { + if (i >= kMaxTileRows) { + LIBGAV1_DLOG(ERROR, "tile_rows would be greater than kMaxTileRows."); + return false; + } + tile_info->tile_row_start[i] = sb_start << sb_shift; + const int max_height = std::min(sb_rows - sb_start, max_tile_height_sb); + if (!bit_reader_->DecodeUniform( + max_height, &tile_info->tile_row_height_in_superblocks[i])) { + LIBGAV1_DLOG(ERROR, "Not enough bits."); + return false; + } + ++tile_info->tile_row_height_in_superblocks[i]; + sb_start += tile_info->tile_row_height_in_superblocks[i]; + } + tile_info->tile_row_start[i] = frame_header_.rows4x4; + tile_info->tile_rows = i; + tile_info->tile_rows_log2 = CeilLog2(tile_info->tile_rows); + } + tile_info->tile_count = tile_info->tile_rows * tile_info->tile_columns; + if (!tile_buffers_.reserve(tile_info->tile_count)) { + LIBGAV1_DLOG(ERROR, "Unable to allocate memory for tile_buffers_."); + return false; + } + tile_info->context_update_id = 0; + const int tile_bits = + tile_info->tile_columns_log2 + tile_info->tile_rows_log2; + if (tile_bits != 0) { + OBU_READ_LITERAL_OR_FAIL(tile_bits); + tile_info->context_update_id = static_cast<int16_t>(scratch); + if (tile_info->context_update_id >= tile_info->tile_count) { + LIBGAV1_DLOG(ERROR, "Invalid context_update_tile_id (%d) >= %d.", + tile_info->context_update_id, tile_info->tile_count); + return false; + } + OBU_READ_LITERAL_OR_FAIL(2); + tile_info->tile_size_bytes = 1 + scratch; + } + return true; +} + +bool ObuParser::ReadAllowWarpedMotion() { + if (IsIntraFrame(frame_header_.frame_type) || + frame_header_.error_resilient_mode || + !sequence_header_.enable_warped_motion) { + return true; + } + int64_t scratch; + OBU_READ_BIT_OR_FAIL; + frame_header_.allow_warped_motion = static_cast<bool>(scratch); + return true; +} + +bool ObuParser::ParseFrameParameters() { + int64_t scratch; + if (sequence_header_.reduced_still_picture_header) { + frame_header_.show_frame = true; + current_frame_ = buffer_pool_->GetFreeBuffer(); + if (current_frame_ == nullptr) { + LIBGAV1_DLOG(ERROR, "Could not get current_frame from the buffer pool."); + return false; + } + } else { + OBU_READ_BIT_OR_FAIL; + frame_header_.show_existing_frame = static_cast<bool>(scratch); + if (frame_header_.show_existing_frame) { + OBU_READ_LITERAL_OR_FAIL(3); + frame_header_.frame_to_show = scratch; + if (sequence_header_.decoder_model_info_present_flag && + !sequence_header_.timing_info.equal_picture_interval) { + OBU_READ_LITERAL_OR_FAIL( + sequence_header_.decoder_model_info.frame_presentation_time_length); + frame_header_.frame_presentation_time = static_cast<uint32_t>(scratch); + } + if (sequence_header_.frame_id_numbers_present) { + OBU_READ_LITERAL_OR_FAIL(sequence_header_.frame_id_length_bits); + frame_header_.display_frame_id = static_cast<uint16_t>(scratch); + // Section 6.8.2: It is a requirement of bitstream conformance that + // whenever display_frame_id is read, the value matches + // RefFrameId[ frame_to_show_map_idx ] ..., and that + // RefValid[ frame_to_show_map_idx ] is equal to 1. + if (frame_header_.display_frame_id != + decoder_state_ + .reference_frame_id[frame_header_.frame_to_show] || + !decoder_state_.reference_valid[frame_header_.frame_to_show]) { + LIBGAV1_DLOG(ERROR, + "Reference buffer %d has a frame id number mismatch.", + frame_header_.frame_to_show); + return false; + } + } + // Section 7.18.2. Note: This is also needed for Section 7.21 if + // frame_type is kFrameKey. + current_frame_ = + decoder_state_.reference_frame[frame_header_.frame_to_show]; + if (current_frame_ == nullptr) { + LIBGAV1_DLOG(ERROR, "Buffer %d does not contain a decoded frame", + frame_header_.frame_to_show); + return false; + } + // Section 6.8.2: It is a requirement of bitstream conformance that + // when show_existing_frame is used to show a previous frame, that the + // value of showable_frame for the previous frame was equal to 1. + if (!current_frame_->showable_frame()) { + LIBGAV1_DLOG(ERROR, "Buffer %d does not contain a showable frame", + frame_header_.frame_to_show); + return false; + } + if (current_frame_->frame_type() == kFrameKey) { + frame_header_.refresh_frame_flags = 0xff; + // Section 6.8.2: It is a requirement of bitstream conformance that + // when show_existing_frame is used to show a previous frame with + // RefFrameType[ frame_to_show_map_idx ] equal to KEY_FRAME, that + // the frame is output via the show_existing_frame mechanism at most + // once. + current_frame_->set_showable_frame(false); + + // Section 7.21. Note: decoder_state_.current_frame_id must be set + // only when frame_type is kFrameKey per the spec. Among all the + // variables set in Section 7.21, current_frame_id is the only one + // whose value lives across frames. (PrevFrameID is set equal to the + // current_frame_id value for the previous frame.) + decoder_state_.current_frame_id = + decoder_state_.reference_frame_id[frame_header_.frame_to_show]; + decoder_state_.order_hint = + decoder_state_.reference_order_hint[frame_header_.frame_to_show]; + } + return true; + } + current_frame_ = buffer_pool_->GetFreeBuffer(); + if (current_frame_ == nullptr) { + LIBGAV1_DLOG(ERROR, "Could not get current_frame from the buffer pool."); + return false; + } + OBU_READ_LITERAL_OR_FAIL(2); + frame_header_.frame_type = static_cast<FrameType>(scratch); + current_frame_->set_frame_type(frame_header_.frame_type); + OBU_READ_BIT_OR_FAIL; + frame_header_.show_frame = static_cast<bool>(scratch); + if (frame_header_.show_frame && + sequence_header_.decoder_model_info_present_flag && + !sequence_header_.timing_info.equal_picture_interval) { + OBU_READ_LITERAL_OR_FAIL( + sequence_header_.decoder_model_info.frame_presentation_time_length); + frame_header_.frame_presentation_time = static_cast<uint32_t>(scratch); + } + if (frame_header_.show_frame) { + frame_header_.showable_frame = (frame_header_.frame_type != kFrameKey); + } else { + OBU_READ_BIT_OR_FAIL; + frame_header_.showable_frame = static_cast<bool>(scratch); + } + current_frame_->set_showable_frame(frame_header_.showable_frame); + if (frame_header_.frame_type == kFrameSwitch || + (frame_header_.frame_type == kFrameKey && frame_header_.show_frame)) { + frame_header_.error_resilient_mode = true; + } else { + OBU_READ_BIT_OR_FAIL; + frame_header_.error_resilient_mode = static_cast<bool>(scratch); + } + } + if (frame_header_.frame_type == kFrameKey && frame_header_.show_frame) { + decoder_state_.reference_valid.fill(false); + decoder_state_.reference_order_hint.fill(0); + decoder_state_.reference_frame.fill(nullptr); + } + OBU_READ_BIT_OR_FAIL; + frame_header_.enable_cdf_update = !static_cast<bool>(scratch); + if (sequence_header_.force_screen_content_tools == + kSelectScreenContentTools) { + OBU_READ_BIT_OR_FAIL; + frame_header_.allow_screen_content_tools = static_cast<bool>(scratch); + } else { + frame_header_.allow_screen_content_tools = + static_cast<bool>(sequence_header_.force_screen_content_tools); + } + if (frame_header_.allow_screen_content_tools) { + if (sequence_header_.force_integer_mv == kSelectIntegerMv) { + OBU_READ_BIT_OR_FAIL; + frame_header_.force_integer_mv = scratch; + } else { + frame_header_.force_integer_mv = sequence_header_.force_integer_mv; + } + } else { + frame_header_.force_integer_mv = 0; + } + if (IsIntraFrame(frame_header_.frame_type)) { + frame_header_.force_integer_mv = 1; + } + if (sequence_header_.frame_id_numbers_present) { + OBU_READ_LITERAL_OR_FAIL(sequence_header_.frame_id_length_bits); + frame_header_.current_frame_id = static_cast<uint16_t>(scratch); + const int previous_frame_id = decoder_state_.current_frame_id; + decoder_state_.current_frame_id = frame_header_.current_frame_id; + if (frame_header_.frame_type != kFrameKey || !frame_header_.show_frame) { + if (previous_frame_id >= 0) { + // Section 6.8.2: ..., it is a requirement of bitstream conformance + // that all of the following conditions are true: + // * current_frame_id is not equal to PrevFrameID, + // * DiffFrameID is less than 1 << ( idLen - 1 ) + int diff_frame_id = decoder_state_.current_frame_id - previous_frame_id; + const int id_length_max_value = + 1 << sequence_header_.frame_id_length_bits; + if (diff_frame_id <= 0) { + diff_frame_id += id_length_max_value; + } + if (diff_frame_id >= DivideBy2(id_length_max_value)) { + LIBGAV1_DLOG(ERROR, + "current_frame_id (%d) equals or differs too much from " + "previous_frame_id (%d).", + decoder_state_.current_frame_id, previous_frame_id); + return false; + } + } + MarkInvalidReferenceFrames(); + } + } else { + frame_header_.current_frame_id = 0; + decoder_state_.current_frame_id = frame_header_.current_frame_id; + } + if (frame_header_.frame_type == kFrameSwitch) { + frame_header_.frame_size_override_flag = true; + } else if (!sequence_header_.reduced_still_picture_header) { + OBU_READ_BIT_OR_FAIL; + frame_header_.frame_size_override_flag = static_cast<bool>(scratch); + } + if (sequence_header_.order_hint_bits > 0) { + OBU_READ_LITERAL_OR_FAIL(sequence_header_.order_hint_bits); + frame_header_.order_hint = scratch; + } + decoder_state_.order_hint = frame_header_.order_hint; + if (IsIntraFrame(frame_header_.frame_type) || + frame_header_.error_resilient_mode) { + frame_header_.primary_reference_frame = kPrimaryReferenceNone; + } else { + OBU_READ_LITERAL_OR_FAIL(3); + frame_header_.primary_reference_frame = scratch; + } + if (sequence_header_.decoder_model_info_present_flag) { + OBU_READ_BIT_OR_FAIL; + const auto buffer_removal_time_present = static_cast<bool>(scratch); + if (buffer_removal_time_present) { + for (int i = 0; i < sequence_header_.operating_points; ++i) { + if (!sequence_header_.decoder_model_present_for_operating_point[i]) { + continue; + } + const int index = sequence_header_.operating_point_idc[i]; + if (index == 0 || + (InTemporalLayer(index, obu_headers_.back().temporal_id) && + InSpatialLayer(index, obu_headers_.back().spatial_id))) { + OBU_READ_LITERAL_OR_FAIL( + sequence_header_.decoder_model_info.buffer_removal_time_length); + frame_header_.buffer_removal_time[i] = static_cast<uint32_t>(scratch); + } + } + } + } + if (frame_header_.frame_type == kFrameSwitch || + (frame_header_.frame_type == kFrameKey && frame_header_.show_frame)) { + frame_header_.refresh_frame_flags = 0xff; + } else { + OBU_READ_LITERAL_OR_FAIL(8); + frame_header_.refresh_frame_flags = scratch; + // Section 6.8.2: If frame_type is equal to INTRA_ONLY_FRAME, it is a + // requirement of bitstream conformance that refresh_frame_flags is not + // equal to 0xff. + if (frame_header_.frame_type == kFrameIntraOnly && + frame_header_.refresh_frame_flags == 0xff) { + LIBGAV1_DLOG(ERROR, "Intra only frames cannot have refresh flags 0xFF."); + return false; + } + } + if ((!IsIntraFrame(frame_header_.frame_type) || + frame_header_.refresh_frame_flags != 0xff) && + !ParseReferenceOrderHint()) { + return false; + } + if (IsIntraFrame(frame_header_.frame_type)) { + if (!ParseFrameSizeAndRenderSize()) return false; + if (frame_header_.allow_screen_content_tools && + frame_header_.width == frame_header_.upscaled_width) { + OBU_READ_BIT_OR_FAIL; + frame_header_.allow_intrabc = static_cast<bool>(scratch); + } + } else { + if (!sequence_header_.enable_order_hint) { + frame_header_.frame_refs_short_signaling = false; + } else { + OBU_READ_BIT_OR_FAIL; + frame_header_.frame_refs_short_signaling = static_cast<bool>(scratch); + if (frame_header_.frame_refs_short_signaling) { + OBU_READ_LITERAL_OR_FAIL(3); + const int8_t last_frame_idx = scratch; + OBU_READ_LITERAL_OR_FAIL(3); + const int8_t gold_frame_idx = scratch; + if (!SetFrameReferences(last_frame_idx, gold_frame_idx)) { + return false; + } + } + } + for (int i = 0; i < kNumInterReferenceFrameTypes; ++i) { + if (!frame_header_.frame_refs_short_signaling) { + OBU_READ_LITERAL_OR_FAIL(3); + frame_header_.reference_frame_index[i] = scratch; + } + const int reference_frame_index = frame_header_.reference_frame_index[i]; + assert(reference_frame_index >= 0); + // Section 6.8.2: It is a requirement of bitstream conformance that + // RefValid[ ref_frame_idx[ i ] ] is equal to 1 ... + // The remainder of the statement is handled by ParseSequenceHeader(). + // Note if support for Annex C: Error resilience behavior is added this + // check should be omitted per C.5 Decoder consequences of processable + // frames. + if (!decoder_state_.reference_valid[reference_frame_index]) { + LIBGAV1_DLOG(ERROR, "ref_frame_idx[%d] (%d) is not valid.", i, + reference_frame_index); + return false; + } + // Check if the inter frame requests a nonexistent reference, whether or + // not frame_refs_short_signaling is used. + if (decoder_state_.reference_frame[reference_frame_index] == nullptr) { + LIBGAV1_DLOG(ERROR, "ref_frame_idx[%d] (%d) is not a decoded frame.", i, + reference_frame_index); + return false; + } + if (sequence_header_.frame_id_numbers_present) { + OBU_READ_LITERAL_OR_FAIL(sequence_header_.delta_frame_id_length_bits); + const int delta_frame_id = static_cast<int>(1 + scratch); + const int id_length_max_value = + 1 << sequence_header_.frame_id_length_bits; + frame_header_.expected_frame_id[i] = + (frame_header_.current_frame_id + id_length_max_value - + delta_frame_id) % + id_length_max_value; + // Section 6.8.2: It is a requirement of bitstream conformance that + // whenever expectedFrameId[ i ] is calculated, the value matches + // RefFrameId[ ref_frame_idx[ i ] ] ... + // + // Section 6.8.2: It is a requirement of bitstream conformance that + // RefValid[ ref_frame_idx[ i ] ] is equal to 1, ... + if (frame_header_.expected_frame_id[i] != + decoder_state_.reference_frame_id[reference_frame_index] || + !decoder_state_.reference_valid[reference_frame_index]) { + LIBGAV1_DLOG(ERROR, + "Reference buffer %d has a frame id number mismatch.", + reference_frame_index); + return false; + } + } + } + if (frame_header_.frame_size_override_flag && + !frame_header_.error_resilient_mode) { + // Section 5.9.7. + for (int index : frame_header_.reference_frame_index) { + OBU_READ_BIT_OR_FAIL; + frame_header_.found_reference = static_cast<bool>(scratch); + if (frame_header_.found_reference) { + const RefCountedBuffer* reference_frame = + decoder_state_.reference_frame[index].get(); + // frame_header_.upscaled_width will be set in the + // ParseSuperResParametersAndComputeImageSize() call below. + frame_header_.width = reference_frame->upscaled_width(); + frame_header_.height = reference_frame->frame_height(); + frame_header_.render_width = reference_frame->render_width(); + frame_header_.render_height = reference_frame->render_height(); + if (!ParseSuperResParametersAndComputeImageSize()) return false; + break; + } + } + if (!frame_header_.found_reference && !ParseFrameSizeAndRenderSize()) { + return false; + } + } else { + if (!ParseFrameSizeAndRenderSize()) return false; + } + if (!ValidateInterFrameSize()) return false; + if (frame_header_.force_integer_mv != 0) { + frame_header_.allow_high_precision_mv = false; + } else { + OBU_READ_BIT_OR_FAIL; + frame_header_.allow_high_precision_mv = static_cast<bool>(scratch); + } + OBU_READ_BIT_OR_FAIL; + const auto is_filter_switchable = static_cast<bool>(scratch); + if (is_filter_switchable) { + frame_header_.interpolation_filter = kInterpolationFilterSwitchable; + } else { + OBU_READ_LITERAL_OR_FAIL(2); + frame_header_.interpolation_filter = + static_cast<InterpolationFilter>(scratch); + } + OBU_READ_BIT_OR_FAIL; + frame_header_.is_motion_mode_switchable = static_cast<bool>(scratch); + if (frame_header_.error_resilient_mode || + !sequence_header_.enable_ref_frame_mvs) { + frame_header_.use_ref_frame_mvs = false; + } else { + OBU_READ_BIT_OR_FAIL; + frame_header_.use_ref_frame_mvs = static_cast<bool>(scratch); + } + } + // At this point, we have parsed the frame and render sizes and computed + // the image size, whether it's an intra or inter frame. So we can save + // the sizes in the current frame now. + if (!current_frame_->SetFrameDimensions(frame_header_)) { + LIBGAV1_DLOG(ERROR, "Setting current frame dimensions failed."); + return false; + } + if (!IsIntraFrame(frame_header_.frame_type)) { + // Initialize the kReferenceFrameIntra type reference frame information to + // simplify the frame type validation in motion field projection. + // Set the kReferenceFrameIntra type |order_hint_| to + // |frame_header_.order_hint|. This guarantees that in SIMD implementations, + // the other reference frame information of the kReferenceFrameIntra type + // could be correctly initialized using the following loop with + // |frame_header_.order_hint| being the |hint|. + ReferenceInfo* const reference_info = current_frame_->reference_info(); + reference_info->order_hint[kReferenceFrameIntra] = frame_header_.order_hint; + reference_info->relative_distance_from[kReferenceFrameIntra] = 0; + reference_info->relative_distance_to[kReferenceFrameIntra] = 0; + reference_info->skip_references[kReferenceFrameIntra] = true; + reference_info->projection_divisions[kReferenceFrameIntra] = 0; + + for (int i = kReferenceFrameLast; i <= kNumInterReferenceFrameTypes; ++i) { + const auto reference_frame = static_cast<ReferenceFrameType>(i); + const uint8_t hint = + decoder_state_.reference_order_hint + [frame_header_.reference_frame_index[i - kReferenceFrameLast]]; + reference_info->order_hint[reference_frame] = hint; + const int relative_distance_from = + GetRelativeDistance(hint, frame_header_.order_hint, + sequence_header_.order_hint_shift_bits); + const int relative_distance_to = + GetRelativeDistance(frame_header_.order_hint, hint, + sequence_header_.order_hint_shift_bits); + reference_info->relative_distance_from[reference_frame] = + relative_distance_from; + reference_info->relative_distance_to[reference_frame] = + relative_distance_to; + reference_info->skip_references[reference_frame] = + relative_distance_to > kMaxFrameDistance || relative_distance_to <= 0; + reference_info->projection_divisions[reference_frame] = + reference_info->skip_references[reference_frame] + ? 0 + : kProjectionMvDivisionLookup[relative_distance_to]; + decoder_state_.reference_frame_sign_bias[reference_frame] = + relative_distance_from > 0; + } + } + if (frame_header_.enable_cdf_update && + !sequence_header_.reduced_still_picture_header) { + OBU_READ_BIT_OR_FAIL; + frame_header_.enable_frame_end_update_cdf = !static_cast<bool>(scratch); + } else { + frame_header_.enable_frame_end_update_cdf = false; + } + return true; +} + +bool ObuParser::ParseFrameHeader() { + // Section 6.8.1: It is a requirement of bitstream conformance that a + // sequence header OBU has been received before a frame header OBU. + if (!has_sequence_header_) return false; + if (!ParseFrameParameters()) return false; + if (frame_header_.show_existing_frame) return true; + assert(!obu_headers_.empty()); + current_frame_->set_spatial_id(obu_headers_.back().spatial_id); + current_frame_->set_temporal_id(obu_headers_.back().temporal_id); + bool status = ParseTileInfoSyntax() && ParseQuantizerParameters() && + ParseSegmentationParameters(); + if (!status) return false; + current_frame_->SetSegmentationParameters(frame_header_.segmentation); + status = + ParseQuantizerIndexDeltaParameters() && ParseLoopFilterDeltaParameters(); + if (!status) return false; + ComputeSegmentLosslessAndQIndex(); + // Section 6.8.2: It is a requirement of bitstream conformance that + // delta_q_present is equal to 0 when CodedLossless is equal to 1. + if (frame_header_.coded_lossless && frame_header_.delta_q.present) { + return false; + } + status = ParseLoopFilterParameters(); + if (!status) return false; + current_frame_->SetLoopFilterDeltas(frame_header_.loop_filter); + status = ParseCdefParameters() && ParseLoopRestorationParameters() && + ParseTxModeSyntax() && ParseFrameReferenceModeSyntax() && + ParseSkipModeParameters() && ReadAllowWarpedMotion(); + if (!status) return false; + int64_t scratch; + OBU_READ_BIT_OR_FAIL; + frame_header_.reduced_tx_set = static_cast<bool>(scratch); + status = ParseGlobalMotionParameters(); + if (!status) return false; + current_frame_->SetGlobalMotions(frame_header_.global_motion); + status = ParseFilmGrainParameters(); + if (!status) return false; + if (sequence_header_.film_grain_params_present) { + current_frame_->set_film_grain_params(frame_header_.film_grain_params); + } + return true; +} + +bool ObuParser::ParsePadding(const uint8_t* data, size_t size) { + // The spec allows a padding OBU to be header-only (i.e., |size| = 0). So + // check trailing bits only if |size| > 0. + if (size == 0) return true; + // The payload of a padding OBU is byte aligned. Therefore the first + // trailing byte should be 0x80. See https://crbug.com/aomedia/2393. + const int i = GetLastNonzeroByteIndex(data, size); + if (i < 0) { + LIBGAV1_DLOG(ERROR, "Trailing bit is missing."); + return false; + } + if (data[i] != 0x80) { + LIBGAV1_DLOG( + ERROR, + "The last nonzero byte of the payload data is 0x%x, should be 0x80.", + data[i]); + return false; + } + // Skip all bits before the trailing bit. + bit_reader_->SkipBytes(i); + return true; +} + +bool ObuParser::ParseMetadataScalability() { + int64_t scratch; + // scalability_mode_idc + OBU_READ_LITERAL_OR_FAIL(8); + const auto scalability_mode_idc = static_cast<int>(scratch); + if (scalability_mode_idc == kScalabilitySS) { + // Parse scalability_structure(). + // spatial_layers_cnt_minus_1 + OBU_READ_LITERAL_OR_FAIL(2); + const auto spatial_layers_count = static_cast<int>(scratch) + 1; + // spatial_layer_dimensions_present_flag + OBU_READ_BIT_OR_FAIL; + const auto spatial_layer_dimensions_present_flag = + static_cast<bool>(scratch); + // spatial_layer_description_present_flag + OBU_READ_BIT_OR_FAIL; + const auto spatial_layer_description_present_flag = + static_cast<bool>(scratch); + // temporal_group_description_present_flag + OBU_READ_BIT_OR_FAIL; + const auto temporal_group_description_present_flag = + static_cast<bool>(scratch); + // scalability_structure_reserved_3bits + OBU_READ_LITERAL_OR_FAIL(3); + if (scratch != 0) { + LIBGAV1_DLOG(WARNING, + "scalability_structure_reserved_3bits is not zero."); + } + if (spatial_layer_dimensions_present_flag) { + for (int i = 0; i < spatial_layers_count; ++i) { + // spatial_layer_max_width[i] + OBU_READ_LITERAL_OR_FAIL(16); + // spatial_layer_max_height[i] + OBU_READ_LITERAL_OR_FAIL(16); + } + } + if (spatial_layer_description_present_flag) { + for (int i = 0; i < spatial_layers_count; ++i) { + // spatial_layer_ref_id[i] + OBU_READ_LITERAL_OR_FAIL(8); + } + } + if (temporal_group_description_present_flag) { + // temporal_group_size + OBU_READ_LITERAL_OR_FAIL(8); + const auto temporal_group_size = static_cast<int>(scratch); + for (int i = 0; i < temporal_group_size; ++i) { + // temporal_group_temporal_id[i] + OBU_READ_LITERAL_OR_FAIL(3); + // temporal_group_temporal_switching_up_point_flag[i] + OBU_READ_BIT_OR_FAIL; + // temporal_group_spatial_switching_up_point_flag[i] + OBU_READ_BIT_OR_FAIL; + // temporal_group_ref_cnt[i] + OBU_READ_LITERAL_OR_FAIL(3); + const auto temporal_group_ref_count = static_cast<int>(scratch); + for (int j = 0; j < temporal_group_ref_count; ++j) { + // temporal_group_ref_pic_diff[i][j] + OBU_READ_LITERAL_OR_FAIL(8); + } + } + } + } + return true; +} + +bool ObuParser::ParseMetadataTimecode() { + int64_t scratch; + // counting_type: should be the same for all pictures in the coded video + // sequence. 7..31 are reserved. + OBU_READ_LITERAL_OR_FAIL(5); + // full_timestamp_flag + OBU_READ_BIT_OR_FAIL; + const auto full_timestamp_flag = static_cast<bool>(scratch); + // discontinuity_flag + OBU_READ_BIT_OR_FAIL; + // cnt_dropped_flag + OBU_READ_BIT_OR_FAIL; + // n_frames + OBU_READ_LITERAL_OR_FAIL(9); + if (full_timestamp_flag) { + // seconds_value + OBU_READ_LITERAL_OR_FAIL(6); + const auto seconds_value = static_cast<int>(scratch); + if (seconds_value > 59) { + LIBGAV1_DLOG(ERROR, "Invalid seconds_value %d.", seconds_value); + return false; + } + // minutes_value + OBU_READ_LITERAL_OR_FAIL(6); + const auto minutes_value = static_cast<int>(scratch); + if (minutes_value > 59) { + LIBGAV1_DLOG(ERROR, "Invalid minutes_value %d.", minutes_value); + return false; + } + // hours_value + OBU_READ_LITERAL_OR_FAIL(5); + const auto hours_value = static_cast<int>(scratch); + if (hours_value > 23) { + LIBGAV1_DLOG(ERROR, "Invalid hours_value %d.", hours_value); + return false; + } + } else { + // seconds_flag + OBU_READ_BIT_OR_FAIL; + const auto seconds_flag = static_cast<bool>(scratch); + if (seconds_flag) { + // seconds_value + OBU_READ_LITERAL_OR_FAIL(6); + const auto seconds_value = static_cast<int>(scratch); + if (seconds_value > 59) { + LIBGAV1_DLOG(ERROR, "Invalid seconds_value %d.", seconds_value); + return false; + } + // minutes_flag + OBU_READ_BIT_OR_FAIL; + const auto minutes_flag = static_cast<bool>(scratch); + if (minutes_flag) { + // minutes_value + OBU_READ_LITERAL_OR_FAIL(6); + const auto minutes_value = static_cast<int>(scratch); + if (minutes_value > 59) { + LIBGAV1_DLOG(ERROR, "Invalid minutes_value %d.", minutes_value); + return false; + } + // hours_flag + OBU_READ_BIT_OR_FAIL; + const auto hours_flag = static_cast<bool>(scratch); + if (hours_flag) { + // hours_value + OBU_READ_LITERAL_OR_FAIL(5); + const auto hours_value = static_cast<int>(scratch); + if (hours_value > 23) { + LIBGAV1_DLOG(ERROR, "Invalid hours_value %d.", hours_value); + return false; + } + } + } + } + } + // time_offset_length: should be the same for all pictures in the coded + // video sequence. + OBU_READ_LITERAL_OR_FAIL(5); + const auto time_offset_length = static_cast<int>(scratch); + if (time_offset_length > 0) { + // time_offset_value + OBU_READ_LITERAL_OR_FAIL(time_offset_length); + } + // Compute clockTimestamp. Section 6.7.7: + // When timing_info_present_flag is equal to 1 and discontinuity_flag is + // equal to 0, the value of clockTimestamp shall be greater than or equal + // to the value of clockTimestamp for the previous set of clock timestamp + // syntax elements in output order. + return true; +} + +bool ObuParser::ParseMetadata(const uint8_t* data, size_t size) { + const size_t start_offset = bit_reader_->byte_offset(); + size_t metadata_type; + if (!bit_reader_->ReadUnsignedLeb128(&metadata_type)) { + LIBGAV1_DLOG(ERROR, "Could not read metadata_type."); + return false; + } + const size_t metadata_type_size = bit_reader_->byte_offset() - start_offset; + if (size < metadata_type_size) { + LIBGAV1_DLOG( + ERROR, "metadata_type is longer than metadata OBU payload %zu vs %zu.", + metadata_type_size, size); + return false; + } + data += metadata_type_size; + size -= metadata_type_size; + int64_t scratch; + switch (metadata_type) { + case kMetadataTypeHdrContentLightLevel: + OBU_READ_LITERAL_OR_FAIL(16); + metadata_.max_cll = scratch; + OBU_READ_LITERAL_OR_FAIL(16); + metadata_.max_fall = scratch; + break; + case kMetadataTypeHdrMasteringDisplayColorVolume: + for (int i = 0; i < 3; ++i) { + OBU_READ_LITERAL_OR_FAIL(16); + metadata_.primary_chromaticity_x[i] = scratch; + OBU_READ_LITERAL_OR_FAIL(16); + metadata_.primary_chromaticity_y[i] = scratch; + } + OBU_READ_LITERAL_OR_FAIL(16); + metadata_.white_point_chromaticity_x = scratch; + OBU_READ_LITERAL_OR_FAIL(16); + metadata_.white_point_chromaticity_y = scratch; + OBU_READ_LITERAL_OR_FAIL(32); + metadata_.luminance_max = static_cast<uint32_t>(scratch); + OBU_READ_LITERAL_OR_FAIL(32); + metadata_.luminance_min = static_cast<uint32_t>(scratch); + break; + case kMetadataTypeScalability: + if (!ParseMetadataScalability()) return false; + break; + case kMetadataTypeItutT35: { + OBU_READ_LITERAL_OR_FAIL(8); + metadata_.itu_t_t35_country_code = static_cast<uint8_t>(scratch); + ++data; + --size; + if (metadata_.itu_t_t35_country_code == 0xFF) { + OBU_READ_LITERAL_OR_FAIL(8); + metadata_.itu_t_t35_country_code_extension_byte = + static_cast<uint8_t>(scratch); + ++data; + --size; + } + // Read itu_t_t35_payload_bytes. Section 6.7.2 of the spec says: + // itu_t_t35_payload_bytes shall be bytes containing data registered as + // specified in Recommendation ITU-T T.35. + // Therefore itu_t_t35_payload_bytes is byte aligned and the first + // trailing byte should be 0x80. Since the exact syntax of + // itu_t_t35_payload_bytes is not defined in the AV1 spec, identify the + // end of itu_t_t35_payload_bytes by searching for the trailing bit. + const int i = GetLastNonzeroByteIndex(data, size); + if (i < 0) { + LIBGAV1_DLOG(ERROR, "Trailing bit is missing."); + return false; + } + if (data[i] != 0x80) { + LIBGAV1_DLOG( + ERROR, + "itu_t_t35_payload_bytes is not byte aligned. The last nonzero " + "byte of the payload data is 0x%x, should be 0x80.", + data[i]); + return false; + } + if (i != 0) { + // data[0]..data[i - 1] are itu_t_t35_payload_bytes. + metadata_.itu_t_t35_payload_bytes.reset(new (std::nothrow) uint8_t[i]); + if (metadata_.itu_t_t35_payload_bytes == nullptr) { + LIBGAV1_DLOG(ERROR, "Allocation of itu_t_t35_payload_bytes failed."); + return false; + } + memcpy(metadata_.itu_t_t35_payload_bytes.get(), data, i); + metadata_.itu_t_t35_payload_size = i; + } + // Skip all bits before the trailing bit. + bit_reader_->SkipBytes(i); + break; + } + case kMetadataTypeTimecode: + if (!ParseMetadataTimecode()) return false; + break; + default: { + // metadata_type is equal to a value reserved for future use or a user + // private value. + // + // The Note in Section 5.8.1 says "Decoders should ignore the entire OBU + // if they do not understand the metadata_type." Find the trailing bit + // and skip all bits before the trailing bit. + const int i = GetLastNonzeroByteIndex(data, size); + if (i >= 0) { + // The last 1 bit in the last nonzero byte is the trailing bit. Skip + // all bits before the trailing bit. + const int n = CountTrailingZeros(data[i]); + bit_reader_->SkipBits(i * 8 + 7 - n); + } + break; + } + } + return true; +} + +bool ObuParser::AddTileBuffers(int start, int end, size_t total_size, + size_t tg_header_size, + size_t bytes_consumed_so_far) { + // Validate that the tile group start and end are within the allowed range. + if (start != next_tile_group_start_ || start > end || + end >= frame_header_.tile_info.tile_count) { + LIBGAV1_DLOG(ERROR, + "Invalid tile group start %d or end %d: expected tile group " + "start %d, tile_count %d.", + start, end, next_tile_group_start_, + frame_header_.tile_info.tile_count); + return false; + } + next_tile_group_start_ = end + 1; + + if (total_size < tg_header_size) { + LIBGAV1_DLOG(ERROR, "total_size (%zu) is less than tg_header_size (%zu).)", + total_size, tg_header_size); + return false; + } + size_t bytes_left = total_size - tg_header_size; + const uint8_t* data = data_ + bytes_consumed_so_far + tg_header_size; + for (int tile_number = start; tile_number <= end; ++tile_number) { + size_t tile_size = 0; + if (tile_number != end) { + RawBitReader bit_reader(data, bytes_left); + if (!bit_reader.ReadLittleEndian(frame_header_.tile_info.tile_size_bytes, + &tile_size)) { + LIBGAV1_DLOG(ERROR, "Could not read tile size for tile #%d", + tile_number); + return false; + } + ++tile_size; + data += frame_header_.tile_info.tile_size_bytes; + bytes_left -= frame_header_.tile_info.tile_size_bytes; + if (tile_size > bytes_left) { + LIBGAV1_DLOG(ERROR, "Invalid tile size %zu for tile #%d", tile_size, + tile_number); + return false; + } + } else { + tile_size = bytes_left; + if (tile_size == 0) { + LIBGAV1_DLOG(ERROR, "Invalid tile size %zu for tile #%d", tile_size, + tile_number); + return false; + } + } + // The memory for this has been allocated in ParseTileInfoSyntax(). So it is + // safe to use push_back_unchecked here. + tile_buffers_.push_back_unchecked({data, tile_size}); + data += tile_size; + bytes_left -= tile_size; + } + bit_reader_->SkipBytes(total_size - tg_header_size); + return true; +} + +bool ObuParser::ParseTileGroup(size_t size, size_t bytes_consumed_so_far) { + const TileInfo* const tile_info = &frame_header_.tile_info; + const size_t start_offset = bit_reader_->byte_offset(); + const int tile_bits = + tile_info->tile_columns_log2 + tile_info->tile_rows_log2; + if (tile_bits == 0) { + return AddTileBuffers(0, 0, size, 0, bytes_consumed_so_far); + } + int64_t scratch; + OBU_READ_BIT_OR_FAIL; + const auto tile_start_and_end_present_flag = static_cast<bool>(scratch); + if (!tile_start_and_end_present_flag) { + if (!bit_reader_->AlignToNextByte()) { + LIBGAV1_DLOG(ERROR, "Byte alignment has non zero bits."); + return false; + } + return AddTileBuffers(0, tile_info->tile_count - 1, size, 1, + bytes_consumed_so_far); + } + if (obu_headers_.back().type == kObuFrame) { + // 6.10.1: If obu_type is equal to OBU_FRAME, it is a requirement of + // bitstream conformance that the value of tile_start_and_end_present_flag + // is equal to 0. + LIBGAV1_DLOG(ERROR, + "tile_start_and_end_present_flag must be 0 in Frame OBU"); + return false; + } + OBU_READ_LITERAL_OR_FAIL(tile_bits); + const int start = static_cast<int>(scratch); + OBU_READ_LITERAL_OR_FAIL(tile_bits); + const int end = static_cast<int>(scratch); + if (!bit_reader_->AlignToNextByte()) { + LIBGAV1_DLOG(ERROR, "Byte alignment has non zero bits."); + return false; + } + const size_t tg_header_size = bit_reader_->byte_offset() - start_offset; + return AddTileBuffers(start, end, size, tg_header_size, + bytes_consumed_so_far); +} + +bool ObuParser::ParseHeader() { + ObuHeader obu_header; + int64_t scratch = bit_reader_->ReadBit(); + if (scratch != 0) { + LIBGAV1_DLOG(ERROR, "forbidden_bit is not zero."); + return false; + } + OBU_READ_LITERAL_OR_FAIL(4); + obu_header.type = static_cast<libgav1::ObuType>(scratch); + OBU_READ_BIT_OR_FAIL; + const auto extension_flag = static_cast<bool>(scratch); + OBU_READ_BIT_OR_FAIL; + obu_header.has_size_field = static_cast<bool>(scratch); + OBU_READ_BIT_OR_FAIL; // reserved. + if (scratch != 0) { + LIBGAV1_DLOG(WARNING, "obu_reserved_1bit is not zero."); + } + obu_header.has_extension = extension_flag; + if (extension_flag) { + if (extension_disallowed_) { + LIBGAV1_DLOG(ERROR, + "OperatingPointIdc is 0, but obu_extension_flag is 1."); + return false; + } + OBU_READ_LITERAL_OR_FAIL(3); + obu_header.temporal_id = scratch; + OBU_READ_LITERAL_OR_FAIL(2); + obu_header.spatial_id = scratch; + OBU_READ_LITERAL_OR_FAIL(3); // reserved. + if (scratch != 0) { + LIBGAV1_DLOG(WARNING, "extension_header_reserved_3bits is not zero."); + } + } else { + obu_header.temporal_id = 0; + obu_header.spatial_id = 0; + } + return obu_headers_.push_back(obu_header); +} + +#undef OBU_READ_UVLC_OR_FAIL +#undef OBU_READ_LITERAL_OR_FAIL +#undef OBU_READ_BIT_OR_FAIL +#undef OBU_PARSER_FAIL +#undef OBU_LOG_AND_RETURN_FALSE + +bool ObuParser::InitBitReader(const uint8_t* const data, size_t size) { + bit_reader_.reset(new (std::nothrow) RawBitReader(data, size)); + return bit_reader_ != nullptr; +} + +bool ObuParser::HasData() const { return size_ > 0; } + +StatusCode ObuParser::ParseOneFrame(RefCountedBufferPtr* const current_frame) { + if (data_ == nullptr || size_ == 0) return kStatusInvalidArgument; + + assert(current_frame_ == nullptr); + // This is used to release any references held in case of parsing failure. + RefCountedBufferPtrCleanup current_frame_cleanup(¤t_frame_); + + const uint8_t* data = data_; + size_t size = size_; + + // Clear everything except the sequence header. + obu_headers_.clear(); + frame_header_ = {}; + metadata_ = {}; + tile_buffers_.clear(); + next_tile_group_start_ = 0; + + bool parsed_one_full_frame = false; + bool seen_frame_header = false; + const uint8_t* frame_header = nullptr; + size_t frame_header_size_in_bits = 0; + while (size > 0 && !parsed_one_full_frame) { + if (!InitBitReader(data, size)) { + LIBGAV1_DLOG(ERROR, "Failed to initialize bit reader."); + return kStatusOutOfMemory; + } + if (!ParseHeader()) { + LIBGAV1_DLOG(ERROR, "Failed to parse OBU Header."); + return kStatusBitstreamError; + } + const ObuHeader& obu_header = obu_headers_.back(); + if (!obu_header.has_size_field) { + LIBGAV1_DLOG( + ERROR, + "has_size_field is zero. libgav1 does not support such streams."); + return kStatusUnimplemented; + } + const size_t obu_header_size = bit_reader_->byte_offset(); + size_t obu_size; + if (!bit_reader_->ReadUnsignedLeb128(&obu_size)) { + LIBGAV1_DLOG(ERROR, "Could not read OBU size."); + return kStatusBitstreamError; + } + const size_t obu_length_size = bit_reader_->byte_offset() - obu_header_size; + if (size - bit_reader_->byte_offset() < obu_size) { + LIBGAV1_DLOG(ERROR, "Not enough bits left to parse OBU %zu vs %zu.", + size - bit_reader_->bit_offset(), obu_size); + return kStatusBitstreamError; + } + + const ObuType obu_type = obu_header.type; + if (obu_type != kObuSequenceHeader && obu_type != kObuTemporalDelimiter && + has_sequence_header_ && + sequence_header_.operating_point_idc[operating_point_] != 0 && + obu_header.has_extension && + (!InTemporalLayer( + sequence_header_.operating_point_idc[operating_point_], + obu_header.temporal_id) || + !InSpatialLayer(sequence_header_.operating_point_idc[operating_point_], + obu_header.spatial_id))) { + obu_headers_.pop_back(); + bit_reader_->SkipBytes(obu_size); + data += bit_reader_->byte_offset(); + size -= bit_reader_->byte_offset(); + continue; + } + + const size_t obu_start_position = bit_reader_->bit_offset(); + // The bit_reader_ is byte aligned after reading obu_header and obu_size. + // Therefore the byte offset can be computed as obu_start_position >> 3 + // below. + assert((obu_start_position & 7) == 0); + bool obu_skipped = false; + switch (obu_type) { + case kObuTemporalDelimiter: + break; + case kObuSequenceHeader: + if (!ParseSequenceHeader(seen_frame_header)) { + LIBGAV1_DLOG(ERROR, "Failed to parse SequenceHeader OBU."); + return kStatusBitstreamError; + } + if (sequence_header_.color_config.bitdepth > LIBGAV1_MAX_BITDEPTH) { + LIBGAV1_DLOG( + ERROR, + "Bitdepth %d is not supported. The maximum bitdepth is %d.", + sequence_header_.color_config.bitdepth, LIBGAV1_MAX_BITDEPTH); + return kStatusUnimplemented; + } + break; + case kObuFrameHeader: + if (seen_frame_header) { + LIBGAV1_DLOG(ERROR, + "Frame header found but frame header was already seen."); + return kStatusBitstreamError; + } + if (!ParseFrameHeader()) { + LIBGAV1_DLOG(ERROR, "Failed to parse FrameHeader OBU."); + return kStatusBitstreamError; + } + frame_header = &data[obu_start_position >> 3]; + frame_header_size_in_bits = + bit_reader_->bit_offset() - obu_start_position; + seen_frame_header = true; + parsed_one_full_frame = frame_header_.show_existing_frame; + break; + case kObuRedundantFrameHeader: { + if (!seen_frame_header) { + LIBGAV1_DLOG(ERROR, + "Redundant frame header found but frame header was not " + "yet seen."); + return kStatusBitstreamError; + } + const size_t fh_size = (frame_header_size_in_bits + 7) >> 3; + if (obu_size < fh_size || + memcmp(frame_header, &data[obu_start_position >> 3], fh_size) != + 0) { + LIBGAV1_DLOG(ERROR, + "Redundant frame header differs from frame header."); + return kStatusBitstreamError; + } + bit_reader_->SkipBits(frame_header_size_in_bits); + break; + } + case kObuFrame: { + const size_t fh_start_offset = bit_reader_->byte_offset(); + if (seen_frame_header) { + LIBGAV1_DLOG(ERROR, + "Frame header found but frame header was already seen."); + return kStatusBitstreamError; + } + if (!ParseFrameHeader()) { + LIBGAV1_DLOG(ERROR, "Failed to parse FrameHeader in Frame OBU."); + return kStatusBitstreamError; + } + // Section 6.8.2: If obu_type is equal to OBU_FRAME, it is a + // requirement of bitstream conformance that show_existing_frame is + // equal to 0. + if (frame_header_.show_existing_frame) { + LIBGAV1_DLOG(ERROR, "Frame OBU cannot set show_existing_frame to 1."); + return kStatusBitstreamError; + } + if (!bit_reader_->AlignToNextByte()) { + LIBGAV1_DLOG(ERROR, "Byte alignment has non zero bits."); + return kStatusBitstreamError; + } + const size_t fh_size = bit_reader_->byte_offset() - fh_start_offset; + if (fh_size >= obu_size) { + LIBGAV1_DLOG(ERROR, "Frame header size (%zu) >= obu_size (%zu).", + fh_size, obu_size); + return kStatusBitstreamError; + } + if (!ParseTileGroup(obu_size - fh_size, + size_ - size + bit_reader_->byte_offset())) { + LIBGAV1_DLOG(ERROR, "Failed to parse TileGroup in Frame OBU."); + return kStatusBitstreamError; + } + parsed_one_full_frame = true; + break; + } + case kObuTileGroup: + if (!ParseTileGroup(obu_size, + size_ - size + bit_reader_->byte_offset())) { + LIBGAV1_DLOG(ERROR, "Failed to parse TileGroup OBU."); + return kStatusBitstreamError; + } + parsed_one_full_frame = + (next_tile_group_start_ == frame_header_.tile_info.tile_count); + break; + case kObuTileList: + LIBGAV1_DLOG(ERROR, "Decoding of tile list OBUs is not supported."); + return kStatusUnimplemented; + case kObuPadding: + if (!ParsePadding(&data[obu_start_position >> 3], obu_size)) { + LIBGAV1_DLOG(ERROR, "Failed to parse Padding OBU."); + return kStatusBitstreamError; + } + break; + case kObuMetadata: + if (!ParseMetadata(&data[obu_start_position >> 3], obu_size)) { + LIBGAV1_DLOG(ERROR, "Failed to parse Metadata OBU."); + return kStatusBitstreamError; + } + break; + default: + // Skip reserved OBUs. Section 6.2.2: Reserved units are for future use + // and shall be ignored by AV1 decoder. + bit_reader_->SkipBytes(obu_size); + obu_skipped = true; + break; + } + if (obu_size > 0 && !obu_skipped && obu_type != kObuFrame && + obu_type != kObuTileGroup) { + const size_t parsed_obu_size_in_bits = + bit_reader_->bit_offset() - obu_start_position; + if (obu_size * 8 < parsed_obu_size_in_bits) { + LIBGAV1_DLOG( + ERROR, + "Parsed OBU size (%zu bits) is greater than expected OBU size " + "(%zu bytes) obu_type: %d.", + parsed_obu_size_in_bits, obu_size, obu_type); + return kStatusBitstreamError; + } + if (!bit_reader_->VerifyAndSkipTrailingBits(obu_size * 8 - + parsed_obu_size_in_bits)) { + LIBGAV1_DLOG(ERROR, + "Error when verifying trailing bits for obu type: %d", + obu_type); + return kStatusBitstreamError; + } + } + const size_t bytes_consumed = bit_reader_->byte_offset(); + const size_t consumed_obu_size = + bytes_consumed - obu_length_size - obu_header_size; + if (consumed_obu_size != obu_size) { + LIBGAV1_DLOG(ERROR, + "OBU size (%zu) and consumed size (%zu) does not match for " + "obu_type: %d.", + obu_size, consumed_obu_size, obu_type); + return kStatusBitstreamError; + } + data += bytes_consumed; + size -= bytes_consumed; + } + if (!parsed_one_full_frame && seen_frame_header) { + LIBGAV1_DLOG(ERROR, "The last tile group in the frame was not received."); + return kStatusBitstreamError; + } + data_ = data; + size_ = size; + *current_frame = std::move(current_frame_); + return kStatusOk; +} + +} // namespace libgav1 |