// 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 #include #include #include #include #include #include #include #include "src/buffer_pool.h" #include "src/dsp/constants.h" #include "src/motion_vector.h" #include "src/obu_parser.h" #include "src/prediction_mask.h" #include "src/symbol_decoder_context.h" #include "src/tile.h" #include "src/utils/array_2d.h" #include "src/utils/bit_mask_set.h" #include "src/utils/block_parameters_holder.h" #include "src/utils/common.h" #include "src/utils/constants.h" #include "src/utils/entropy_decoder.h" #include "src/utils/logging.h" #include "src/utils/segmentation.h" #include "src/utils/segmentation_map.h" #include "src/utils/types.h" namespace libgav1 { namespace { constexpr int kDeltaQSmall = 3; constexpr int kDeltaLfSmall = 3; constexpr uint8_t kIntraYModeContext[kIntraPredictionModesY] = { 0, 1, 2, 3, 4, 4, 4, 4, 3, 0, 1, 2, 0}; constexpr uint8_t kSizeGroup[kMaxBlockSizes] = { 0, 0, 0, 0, 1, 1, 1, 0, 1, 2, 2, 2, 1, 2, 3, 3, 2, 3, 3, 3, 3, 3}; constexpr int kCompoundModeNewMvContexts = 5; constexpr uint8_t kCompoundModeContextMap[3][kCompoundModeNewMvContexts] = { {0, 1, 1, 1, 1}, {1, 2, 3, 4, 4}, {4, 4, 5, 6, 7}}; enum CflSign : uint8_t { kCflSignZero = 0, kCflSignNegative = 1, kCflSignPositive = 2 }; // For each possible value of the combined signs (which is read from the // bitstream), this array stores the following: sign_u, sign_v, alpha_u_context, // alpha_v_context. Only positive entries are used. Entry at index i is computed // as follows: // sign_u = i / 3 // sign_v = i % 3 // alpha_u_context = i - 2 // alpha_v_context = (sign_v - 1) * 3 + sign_u constexpr int8_t kCflAlphaLookup[kCflAlphaSignsSymbolCount][4] = { {0, 1, -2, 0}, {0, 2, -1, 3}, {1, 0, 0, -2}, {1, 1, 1, 1}, {1, 2, 2, 4}, {2, 0, 3, -1}, {2, 1, 4, 2}, {2, 2, 5, 5}, }; constexpr BitMaskSet kPredictionModeHasNearMvMask(kPredictionModeNearMv, kPredictionModeNearNearMv, kPredictionModeNearNewMv, kPredictionModeNewNearMv); constexpr BitMaskSet kIsInterIntraModeAllowedMask(kBlock8x8, kBlock8x16, kBlock16x8, kBlock16x16, kBlock16x32, kBlock32x16, kBlock32x32); bool IsBackwardReference(ReferenceFrameType type) { return type >= kReferenceFrameBackward && type <= kReferenceFrameAlternate; } bool IsSameDirectionReferencePair(ReferenceFrameType type1, ReferenceFrameType type2) { return (type1 >= kReferenceFrameBackward) == (type2 >= kReferenceFrameBackward); } // This is called neg_deinterleave() in the spec. int DecodeSegmentId(int diff, int reference, int max) { if (reference == 0) return diff; if (reference >= max - 1) return max - diff - 1; const int value = ((diff & 1) != 0) ? reference + ((diff + 1) >> 1) : reference - (diff >> 1); const int reference2 = (reference << 1); if (reference2 < max) { return (diff <= reference2) ? value : diff; } return (diff <= ((max - reference - 1) << 1)) ? value : max - (diff + 1); } // This is called DrlCtxStack in section 7.10.2.14 of the spec. // In the spec, the weights of all the nearest mvs are incremented by a bonus // weight which is larger than any natural weight, and the weights of the mvs // are compared with this bonus weight to determine their contexts. We replace // this procedure by introducing |nearest_mv_count| in PredictionParameters, // which records the count of the nearest mvs. Since all the nearest mvs are in // the beginning of the mv stack, the |index| of a mv in the mv stack can be // compared with |nearest_mv_count| to get that mv's context. int GetRefMvIndexContext(int nearest_mv_count, int index) { if (index + 1 < nearest_mv_count) { return 0; } if (index + 1 == nearest_mv_count) { return 1; } return 2; } // Returns true if both the width and height of the block is less than 64. bool IsBlockDimensionLessThan64(BlockSize size) { return size <= kBlock32x32 && size != kBlock16x64; } int GetUseCompoundReferenceContext(const Tile::Block& block) { if (block.top_available[kPlaneY] && block.left_available[kPlaneY]) { if (block.IsTopSingle() && block.IsLeftSingle()) { return static_cast(IsBackwardReference(block.TopReference(0))) ^ static_cast(IsBackwardReference(block.LeftReference(0))); } if (block.IsTopSingle()) { return 2 + static_cast(IsBackwardReference(block.TopReference(0)) || block.IsTopIntra()); } if (block.IsLeftSingle()) { return 2 + static_cast(IsBackwardReference(block.LeftReference(0)) || block.IsLeftIntra()); } return 4; } if (block.top_available[kPlaneY]) { return block.IsTopSingle() ? static_cast(IsBackwardReference(block.TopReference(0))) : 3; } if (block.left_available[kPlaneY]) { return block.IsLeftSingle() ? static_cast(IsBackwardReference(block.LeftReference(0))) : 3; } return 1; } // Calculates count0 by calling block.CountReferences() on the frame types from // type0_start to type0_end, inclusive, and summing the results. // Calculates count1 by calling block.CountReferences() on the frame types from // type1_start to type1_end, inclusive, and summing the results. // Compares count0 with count1 and returns 0, 1 or 2. // // See count_refs and ref_count_ctx in 8.3.2. int GetReferenceContext(const Tile::Block& block, ReferenceFrameType type0_start, ReferenceFrameType type0_end, ReferenceFrameType type1_start, ReferenceFrameType type1_end) { int count0 = 0; int count1 = 0; for (int type = type0_start; type <= type0_end; ++type) { count0 += block.CountReferences(static_cast(type)); } for (int type = type1_start; type <= type1_end; ++type) { count1 += block.CountReferences(static_cast(type)); } return (count0 < count1) ? 0 : (count0 == count1 ? 1 : 2); } } // namespace bool Tile::ReadSegmentId(const Block& block) { int top_left = -1; if (block.top_available[kPlaneY] && block.left_available[kPlaneY]) { top_left = block_parameters_holder_.Find(block.row4x4 - 1, block.column4x4 - 1) ->segment_id; } int top = -1; if (block.top_available[kPlaneY]) { top = block.bp_top->segment_id; } int left = -1; if (block.left_available[kPlaneY]) { left = block.bp_left->segment_id; } int pred; if (top == -1) { pred = (left == -1) ? 0 : left; } else if (left == -1) { pred = top; } else { pred = (top_left == top) ? top : left; } BlockParameters& bp = *block.bp; if (bp.skip) { bp.segment_id = pred; return true; } int context = 0; if (top_left < 0) { context = 0; } else if (top_left == top && top_left == left) { context = 2; } else if (top_left == top || top_left == left || top == left) { context = 1; } uint16_t* const segment_id_cdf = symbol_decoder_context_.segment_id_cdf[context]; const int encoded_segment_id = reader_.ReadSymbol(segment_id_cdf); bp.segment_id = DecodeSegmentId(encoded_segment_id, pred, frame_header_.segmentation.last_active_segment_id + 1); // Check the bitstream conformance requirement in Section 6.10.8 of the spec. if (bp.segment_id < 0 || bp.segment_id > frame_header_.segmentation.last_active_segment_id) { LIBGAV1_DLOG( ERROR, "Corrupted segment_ids: encoded %d, last active %d, postprocessed %d", encoded_segment_id, frame_header_.segmentation.last_active_segment_id, bp.segment_id); return false; } return true; } bool Tile::ReadIntraSegmentId(const Block& block) { BlockParameters& bp = *block.bp; if (!frame_header_.segmentation.enabled) { bp.segment_id = 0; return true; } return ReadSegmentId(block); } void Tile::ReadSkip(const Block& block) { BlockParameters& bp = *block.bp; if (frame_header_.segmentation.segment_id_pre_skip && frame_header_.segmentation.FeatureActive(bp.segment_id, kSegmentFeatureSkip)) { bp.skip = true; return; } int context = 0; if (block.top_available[kPlaneY] && block.bp_top->skip) { ++context; } if (block.left_available[kPlaneY] && block.bp_left->skip) { ++context; } uint16_t* const skip_cdf = symbol_decoder_context_.skip_cdf[context]; bp.skip = reader_.ReadSymbol(skip_cdf); } void Tile::ReadSkipMode(const Block& block) { BlockParameters& bp = *block.bp; if (!frame_header_.skip_mode_present || frame_header_.segmentation.FeatureActive(bp.segment_id, kSegmentFeatureSkip) || frame_header_.segmentation.FeatureActive(bp.segment_id, kSegmentFeatureReferenceFrame) || frame_header_.segmentation.FeatureActive(bp.segment_id, kSegmentFeatureGlobalMv) || IsBlockDimension4(block.size)) { bp.skip_mode = false; return; } const int context = (block.left_available[kPlaneY] ? static_cast(block.bp_left->skip_mode) : 0) + (block.top_available[kPlaneY] ? static_cast(block.bp_top->skip_mode) : 0); bp.skip_mode = reader_.ReadSymbol(symbol_decoder_context_.skip_mode_cdf[context]); } void Tile::ReadCdef(const Block& block) { BlockParameters& bp = *block.bp; if (bp.skip || frame_header_.coded_lossless || !sequence_header_.enable_cdef || frame_header_.allow_intrabc) { return; } const int cdef_size4x4 = kNum4x4BlocksWide[kBlock64x64]; const int cdef_mask4x4 = ~(cdef_size4x4 - 1); const int row4x4 = block.row4x4 & cdef_mask4x4; const int column4x4 = block.column4x4 & cdef_mask4x4; const int row = DivideBy16(row4x4); const int column = DivideBy16(column4x4); if (cdef_index_[row][column] == -1) { cdef_index_[row][column] = frame_header_.cdef.bits > 0 ? static_cast(reader_.ReadLiteral(frame_header_.cdef.bits)) : 0; for (int i = row4x4; i < row4x4 + block.height4x4; i += cdef_size4x4) { for (int j = column4x4; j < column4x4 + block.width4x4; j += cdef_size4x4) { cdef_index_[DivideBy16(i)][DivideBy16(j)] = cdef_index_[row][column]; } } } } int Tile::ReadAndClipDelta(uint16_t* const cdf, int delta_small, int scale, int min_value, int max_value, int value) { int abs = reader_.ReadSymbol(cdf); if (abs == delta_small) { const int remaining_bit_count = static_cast(reader_.ReadLiteral(3)) + 1; const int abs_remaining_bits = static_cast(reader_.ReadLiteral(remaining_bit_count)); abs = abs_remaining_bits + (1 << remaining_bit_count) + 1; } if (abs != 0) { const bool sign = static_cast(reader_.ReadBit()); const int scaled_abs = abs << scale; const int reduced_delta = sign ? -scaled_abs : scaled_abs; value += reduced_delta; value = Clip3(value, min_value, max_value); } return value; } void Tile::ReadQuantizerIndexDelta(const Block& block) { assert(read_deltas_); BlockParameters& bp = *block.bp; if ((block.size == SuperBlockSize() && bp.skip)) { return; } current_quantizer_index_ = ReadAndClipDelta(symbol_decoder_context_.delta_q_cdf, kDeltaQSmall, frame_header_.delta_q.scale, kMinLossyQuantizer, kMaxQuantizer, current_quantizer_index_); } void Tile::ReadLoopFilterDelta(const Block& block) { assert(read_deltas_); BlockParameters& bp = *block.bp; if (!frame_header_.delta_lf.present || (block.size == SuperBlockSize() && bp.skip)) { return; } int frame_lf_count = 1; if (frame_header_.delta_lf.multi) { frame_lf_count = kFrameLfCount - (PlaneCount() > 1 ? 0 : 2); } bool recompute_deblock_filter_levels = false; for (int i = 0; i < frame_lf_count; ++i) { uint16_t* const delta_lf_abs_cdf = frame_header_.delta_lf.multi ? symbol_decoder_context_.delta_lf_multi_cdf[i] : symbol_decoder_context_.delta_lf_cdf; const int8_t old_delta_lf = delta_lf_[i]; delta_lf_[i] = ReadAndClipDelta( delta_lf_abs_cdf, kDeltaLfSmall, frame_header_.delta_lf.scale, -kMaxLoopFilterValue, kMaxLoopFilterValue, delta_lf_[i]); recompute_deblock_filter_levels = recompute_deblock_filter_levels || (old_delta_lf != delta_lf_[i]); } delta_lf_all_zero_ = (delta_lf_[0] | delta_lf_[1] | delta_lf_[2] | delta_lf_[3]) == 0; if (!delta_lf_all_zero_ && recompute_deblock_filter_levels) { post_filter_.ComputeDeblockFilterLevels(delta_lf_, deblock_filter_levels_); } } void Tile::ReadPredictionModeY(const Block& block, bool intra_y_mode) { uint16_t* cdf; if (intra_y_mode) { const PredictionMode top_mode = block.top_available[kPlaneY] ? block.bp_top->y_mode : kPredictionModeDc; const PredictionMode left_mode = block.left_available[kPlaneY] ? block.bp_left->y_mode : kPredictionModeDc; const int top_context = kIntraYModeContext[top_mode]; const int left_context = kIntraYModeContext[left_mode]; cdf = symbol_decoder_context_ .intra_frame_y_mode_cdf[top_context][left_context]; } else { cdf = symbol_decoder_context_.y_mode_cdf[kSizeGroup[block.size]]; } block.bp->y_mode = static_cast( reader_.ReadSymbol(cdf)); } void Tile::ReadIntraAngleInfo(const Block& block, PlaneType plane_type) { BlockParameters& bp = *block.bp; PredictionParameters& prediction_parameters = *block.bp->prediction_parameters; prediction_parameters.angle_delta[plane_type] = 0; const PredictionMode mode = (plane_type == kPlaneTypeY) ? bp.y_mode : bp.uv_mode; if (IsBlockSmallerThan8x8(block.size) || !IsDirectionalMode(mode)) return; uint16_t* const cdf = symbol_decoder_context_.angle_delta_cdf[mode - kPredictionModeVertical]; prediction_parameters.angle_delta[plane_type] = reader_.ReadSymbol(cdf); prediction_parameters.angle_delta[plane_type] -= kMaxAngleDelta; } void Tile::ReadCflAlpha(const Block& block) { const int signs = reader_.ReadSymbol( symbol_decoder_context_.cfl_alpha_signs_cdf); const int8_t* const cfl_lookup = kCflAlphaLookup[signs]; const auto sign_u = static_cast(cfl_lookup[0]); const auto sign_v = static_cast(cfl_lookup[1]); PredictionParameters& prediction_parameters = *block.bp->prediction_parameters; prediction_parameters.cfl_alpha_u = 0; if (sign_u != kCflSignZero) { assert(cfl_lookup[2] >= 0); prediction_parameters.cfl_alpha_u = reader_.ReadSymbol( symbol_decoder_context_.cfl_alpha_cdf[cfl_lookup[2]]) + 1; if (sign_u == kCflSignNegative) prediction_parameters.cfl_alpha_u *= -1; } prediction_parameters.cfl_alpha_v = 0; if (sign_v != kCflSignZero) { assert(cfl_lookup[3] >= 0); prediction_parameters.cfl_alpha_v = reader_.ReadSymbol( symbol_decoder_context_.cfl_alpha_cdf[cfl_lookup[3]]) + 1; if (sign_v == kCflSignNegative) prediction_parameters.cfl_alpha_v *= -1; } } void Tile::ReadPredictionModeUV(const Block& block) { BlockParameters& bp = *block.bp; bool chroma_from_luma_allowed; if (frame_header_.segmentation.lossless[bp.segment_id]) { chroma_from_luma_allowed = block.residual_size[kPlaneU] == kBlock4x4; } else { chroma_from_luma_allowed = IsBlockDimensionLessThan64(block.size); } uint16_t* const cdf = symbol_decoder_context_ .uv_mode_cdf[static_cast(chroma_from_luma_allowed)][bp.y_mode]; if (chroma_from_luma_allowed) { bp.uv_mode = static_cast( reader_.ReadSymbol(cdf)); } else { bp.uv_mode = static_cast( reader_.ReadSymbol(cdf)); } } int Tile::ReadMotionVectorComponent(const Block& block, const int component) { const int context = static_cast(block.bp->prediction_parameters->use_intra_block_copy); const bool sign = reader_.ReadSymbol( symbol_decoder_context_.mv_sign_cdf[component][context]); const int mv_class = reader_.ReadSymbol( symbol_decoder_context_.mv_class_cdf[component][context]); int magnitude = 1; int value; uint16_t* fraction_cdf; uint16_t* precision_cdf; if (mv_class == 0) { value = static_cast(reader_.ReadSymbol( symbol_decoder_context_.mv_class0_bit_cdf[component][context])); fraction_cdf = symbol_decoder_context_ .mv_class0_fraction_cdf[component][context][value]; precision_cdf = symbol_decoder_context_ .mv_class0_high_precision_cdf[component][context]; } else { assert(mv_class <= kMvBitSymbolCount); value = 0; for (int i = 0; i < mv_class; ++i) { const int bit = static_cast(reader_.ReadSymbol( symbol_decoder_context_.mv_bit_cdf[component][context][i])); value |= bit << i; } magnitude += 2 << (mv_class + 2); fraction_cdf = symbol_decoder_context_.mv_fraction_cdf[component][context]; precision_cdf = symbol_decoder_context_.mv_high_precision_cdf[component][context]; } const int fraction = (frame_header_.force_integer_mv == 0) ? reader_.ReadSymbol(fraction_cdf) : 3; const int precision = frame_header_.allow_high_precision_mv ? static_cast(reader_.ReadSymbol(precision_cdf)) : 1; magnitude += (value << 3) | (fraction << 1) | precision; return sign ? -magnitude : magnitude; } void Tile::ReadMotionVector(const Block& block, int index) { BlockParameters& bp = *block.bp; const int context = static_cast(block.bp->prediction_parameters->use_intra_block_copy); const auto mv_joint = static_cast(reader_.ReadSymbol( symbol_decoder_context_.mv_joint_cdf[context])); if (mv_joint == kMvJointTypeHorizontalZeroVerticalNonZero || mv_joint == kMvJointTypeNonZero) { bp.mv.mv[index].mv[0] = ReadMotionVectorComponent(block, 0); } if (mv_joint == kMvJointTypeHorizontalNonZeroVerticalZero || mv_joint == kMvJointTypeNonZero) { bp.mv.mv[index].mv[1] = ReadMotionVectorComponent(block, 1); } } void Tile::ReadFilterIntraModeInfo(const Block& block) { BlockParameters& bp = *block.bp; PredictionParameters& prediction_parameters = *block.bp->prediction_parameters; prediction_parameters.use_filter_intra = false; if (!sequence_header_.enable_filter_intra || bp.y_mode != kPredictionModeDc || bp.palette_mode_info.size[kPlaneTypeY] != 0 || !IsBlockDimensionLessThan64(block.size)) { return; } prediction_parameters.use_filter_intra = reader_.ReadSymbol( symbol_decoder_context_.use_filter_intra_cdf[block.size]); if (prediction_parameters.use_filter_intra) { prediction_parameters.filter_intra_mode = static_cast( reader_.ReadSymbol( symbol_decoder_context_.filter_intra_mode_cdf)); } } bool Tile::DecodeIntraModeInfo(const Block& block) { BlockParameters& bp = *block.bp; bp.skip = false; if (frame_header_.segmentation.segment_id_pre_skip && !ReadIntraSegmentId(block)) { return false; } bp.skip_mode = false; ReadSkip(block); if (!frame_header_.segmentation.segment_id_pre_skip && !ReadIntraSegmentId(block)) { return false; } ReadCdef(block); if (read_deltas_) { ReadQuantizerIndexDelta(block); ReadLoopFilterDelta(block); read_deltas_ = false; } PredictionParameters& prediction_parameters = *block.bp->prediction_parameters; prediction_parameters.use_intra_block_copy = false; if (frame_header_.allow_intrabc) { prediction_parameters.use_intra_block_copy = reader_.ReadSymbol(symbol_decoder_context_.intra_block_copy_cdf); } if (prediction_parameters.use_intra_block_copy) { bp.is_inter = true; bp.reference_frame[0] = kReferenceFrameIntra; bp.reference_frame[1] = kReferenceFrameNone; bp.y_mode = kPredictionModeDc; bp.uv_mode = kPredictionModeDc; prediction_parameters.motion_mode = kMotionModeSimple; prediction_parameters.compound_prediction_type = kCompoundPredictionTypeAverage; bp.palette_mode_info.size[kPlaneTypeY] = 0; bp.palette_mode_info.size[kPlaneTypeUV] = 0; bp.interpolation_filter[0] = kInterpolationFilterBilinear; bp.interpolation_filter[1] = kInterpolationFilterBilinear; MvContexts dummy_mode_contexts; FindMvStack(block, /*is_compound=*/false, &dummy_mode_contexts); return AssignIntraMv(block); } bp.is_inter = false; return ReadIntraBlockModeInfo(block, /*intra_y_mode=*/true); } int8_t Tile::ComputePredictedSegmentId(const Block& block) const { // If prev_segment_ids_ is null, treat it as if it pointed to a segmentation // map containing all 0s. if (prev_segment_ids_ == nullptr) return 0; const int x_limit = std::min(frame_header_.columns4x4 - block.column4x4, static_cast(block.width4x4)); const int y_limit = std::min(frame_header_.rows4x4 - block.row4x4, static_cast(block.height4x4)); int8_t id = 7; for (int y = 0; y < y_limit; ++y) { for (int x = 0; x < x_limit; ++x) { const int8_t prev_segment_id = prev_segment_ids_->segment_id(block.row4x4 + y, block.column4x4 + x); id = std::min(id, prev_segment_id); } } return id; } bool Tile::ReadInterSegmentId(const Block& block, bool pre_skip) { BlockParameters& bp = *block.bp; if (!frame_header_.segmentation.enabled) { bp.segment_id = 0; return true; } if (!frame_header_.segmentation.update_map) { bp.segment_id = ComputePredictedSegmentId(block); return true; } if (pre_skip) { if (!frame_header_.segmentation.segment_id_pre_skip) { bp.segment_id = 0; return true; } } else if (bp.skip) { bp.use_predicted_segment_id = false; return ReadSegmentId(block); } if (frame_header_.segmentation.temporal_update) { const int context = (block.left_available[kPlaneY] ? static_cast(block.bp_left->use_predicted_segment_id) : 0) + (block.top_available[kPlaneY] ? static_cast(block.bp_top->use_predicted_segment_id) : 0); bp.use_predicted_segment_id = reader_.ReadSymbol( symbol_decoder_context_.use_predicted_segment_id_cdf[context]); if (bp.use_predicted_segment_id) { bp.segment_id = ComputePredictedSegmentId(block); return true; } } return ReadSegmentId(block); } void Tile::ReadIsInter(const Block& block) { BlockParameters& bp = *block.bp; if (bp.skip_mode) { bp.is_inter = true; return; } if (frame_header_.segmentation.FeatureActive(bp.segment_id, kSegmentFeatureReferenceFrame)) { bp.is_inter = frame_header_.segmentation .feature_data[bp.segment_id][kSegmentFeatureReferenceFrame] != kReferenceFrameIntra; return; } if (frame_header_.segmentation.FeatureActive(bp.segment_id, kSegmentFeatureGlobalMv)) { bp.is_inter = true; return; } int context = 0; if (block.top_available[kPlaneY] && block.left_available[kPlaneY]) { context = (block.IsTopIntra() && block.IsLeftIntra()) ? 3 : static_cast(block.IsTopIntra() || block.IsLeftIntra()); } else if (block.top_available[kPlaneY] || block.left_available[kPlaneY]) { context = 2 * static_cast(block.top_available[kPlaneY] ? block.IsTopIntra() : block.IsLeftIntra()); } bp.is_inter = reader_.ReadSymbol(symbol_decoder_context_.is_inter_cdf[context]); } bool Tile::ReadIntraBlockModeInfo(const Block& block, bool intra_y_mode) { BlockParameters& bp = *block.bp; bp.reference_frame[0] = kReferenceFrameIntra; bp.reference_frame[1] = kReferenceFrameNone; ReadPredictionModeY(block, intra_y_mode); ReadIntraAngleInfo(block, kPlaneTypeY); if (block.HasChroma()) { ReadPredictionModeUV(block); if (bp.uv_mode == kPredictionModeChromaFromLuma) { ReadCflAlpha(block); } ReadIntraAngleInfo(block, kPlaneTypeUV); } ReadPaletteModeInfo(block); ReadFilterIntraModeInfo(block); return true; } CompoundReferenceType Tile::ReadCompoundReferenceType(const Block& block) { // compound and inter. const bool top_comp_inter = block.top_available[kPlaneY] && !block.IsTopIntra() && !block.IsTopSingle(); const bool left_comp_inter = block.left_available[kPlaneY] && !block.IsLeftIntra() && !block.IsLeftSingle(); // unidirectional compound. const bool top_uni_comp = top_comp_inter && IsSameDirectionReferencePair(block.TopReference(0), block.TopReference(1)); const bool left_uni_comp = left_comp_inter && IsSameDirectionReferencePair(block.LeftReference(0), block.LeftReference(1)); int context; if (block.top_available[kPlaneY] && !block.IsTopIntra() && block.left_available[kPlaneY] && !block.IsLeftIntra()) { const int same_direction = static_cast(IsSameDirectionReferencePair( block.TopReference(0), block.LeftReference(0))); if (!top_comp_inter && !left_comp_inter) { context = 1 + MultiplyBy2(same_direction); } else if (!top_comp_inter) { context = left_uni_comp ? 3 + same_direction : 1; } else if (!left_comp_inter) { context = top_uni_comp ? 3 + same_direction : 1; } else { if (!top_uni_comp && !left_uni_comp) { context = 0; } else if (!top_uni_comp || !left_uni_comp) { context = 2; } else { context = 3 + static_cast( (block.TopReference(0) == kReferenceFrameBackward) == (block.LeftReference(0) == kReferenceFrameBackward)); } } } else if (block.top_available[kPlaneY] && block.left_available[kPlaneY]) { if (top_comp_inter) { context = 1 + MultiplyBy2(static_cast(top_uni_comp)); } else if (left_comp_inter) { context = 1 + MultiplyBy2(static_cast(left_uni_comp)); } else { context = 2; } } else if (top_comp_inter) { context = MultiplyBy4(static_cast(top_uni_comp)); } else if (left_comp_inter) { context = MultiplyBy4(static_cast(left_uni_comp)); } else { context = 2; } return static_cast(reader_.ReadSymbol( symbol_decoder_context_.compound_reference_type_cdf[context])); } template uint16_t* Tile::GetReferenceCdf( const Block& block, CompoundReferenceType type /*= kNumCompoundReferenceTypes*/) { int context = 0; if ((type == kCompoundReferenceUnidirectional && index == 0) || (is_single && index == 1)) { // uni_comp_ref and single_ref_p1. context = GetReferenceContext(block, kReferenceFrameLast, kReferenceFrameGolden, kReferenceFrameBackward, kReferenceFrameAlternate); } else if (type == kCompoundReferenceUnidirectional && index == 1) { // uni_comp_ref_p1. context = GetReferenceContext(block, kReferenceFrameLast2, kReferenceFrameLast2, kReferenceFrameLast3, kReferenceFrameGolden); } else if ((type == kCompoundReferenceUnidirectional && index == 2) || (type == kCompoundReferenceBidirectional && index == 2) || (is_single && index == 5)) { // uni_comp_ref_p2, comp_ref_p2 and single_ref_p5. context = GetReferenceContext(block, kReferenceFrameLast3, kReferenceFrameLast3, kReferenceFrameGolden, kReferenceFrameGolden); } else if ((type == kCompoundReferenceBidirectional && index == 0) || (is_single && index == 3)) { // comp_ref and single_ref_p3. context = GetReferenceContext(block, kReferenceFrameLast, kReferenceFrameLast2, kReferenceFrameLast3, kReferenceFrameGolden); } else if ((type == kCompoundReferenceBidirectional && index == 1) || (is_single && index == 4)) { // comp_ref_p1 and single_ref_p4. context = GetReferenceContext(block, kReferenceFrameLast, kReferenceFrameLast, kReferenceFrameLast2, kReferenceFrameLast2); } else if ((is_single && index == 2) || (is_backward && index == 0)) { // single_ref_p2 and comp_bwdref. context = GetReferenceContext( block, kReferenceFrameBackward, kReferenceFrameAlternate2, kReferenceFrameAlternate, kReferenceFrameAlternate); } else if ((is_single && index == 6) || (is_backward && index == 1)) { // single_ref_p6 and comp_bwdref_p1. context = GetReferenceContext( block, kReferenceFrameBackward, kReferenceFrameBackward, kReferenceFrameAlternate2, kReferenceFrameAlternate2); } if (is_single) { // The index parameter for single references is offset by one since the spec // uses 1-based index for these elements. return symbol_decoder_context_.single_reference_cdf[context][index - 1]; } if (is_backward) { return symbol_decoder_context_ .compound_backward_reference_cdf[context][index]; } return symbol_decoder_context_.compound_reference_cdf[type][context][index]; } void Tile::ReadReferenceFrames(const Block& block) { BlockParameters& bp = *block.bp; if (bp.skip_mode) { bp.reference_frame[0] = frame_header_.skip_mode_frame[0]; bp.reference_frame[1] = frame_header_.skip_mode_frame[1]; return; } if (frame_header_.segmentation.FeatureActive(bp.segment_id, kSegmentFeatureReferenceFrame)) { bp.reference_frame[0] = static_cast( frame_header_.segmentation .feature_data[bp.segment_id][kSegmentFeatureReferenceFrame]); bp.reference_frame[1] = kReferenceFrameNone; return; } if (frame_header_.segmentation.FeatureActive(bp.segment_id, kSegmentFeatureSkip) || frame_header_.segmentation.FeatureActive(bp.segment_id, kSegmentFeatureGlobalMv)) { bp.reference_frame[0] = kReferenceFrameLast; bp.reference_frame[1] = kReferenceFrameNone; return; } const bool use_compound_reference = frame_header_.reference_mode_select && std::min(block.width4x4, block.height4x4) >= 2 && reader_.ReadSymbol(symbol_decoder_context_.use_compound_reference_cdf [GetUseCompoundReferenceContext(block)]); if (use_compound_reference) { CompoundReferenceType reference_type = ReadCompoundReferenceType(block); if (reference_type == kCompoundReferenceUnidirectional) { // uni_comp_ref. if (reader_.ReadSymbol( GetReferenceCdf(block, reference_type))) { bp.reference_frame[0] = kReferenceFrameBackward; bp.reference_frame[1] = kReferenceFrameAlternate; return; } // uni_comp_ref_p1. if (!reader_.ReadSymbol( GetReferenceCdf(block, reference_type))) { bp.reference_frame[0] = kReferenceFrameLast; bp.reference_frame[1] = kReferenceFrameLast2; return; } // uni_comp_ref_p2. if (reader_.ReadSymbol( GetReferenceCdf(block, reference_type))) { bp.reference_frame[0] = kReferenceFrameLast; bp.reference_frame[1] = kReferenceFrameGolden; return; } bp.reference_frame[0] = kReferenceFrameLast; bp.reference_frame[1] = kReferenceFrameLast3; return; } assert(reference_type == kCompoundReferenceBidirectional); // comp_ref. if (reader_.ReadSymbol( GetReferenceCdf(block, reference_type))) { // comp_ref_p2. bp.reference_frame[0] = reader_.ReadSymbol( GetReferenceCdf(block, reference_type)) ? kReferenceFrameGolden : kReferenceFrameLast3; } else { // comp_ref_p1. bp.reference_frame[0] = reader_.ReadSymbol( GetReferenceCdf(block, reference_type)) ? kReferenceFrameLast2 : kReferenceFrameLast; } // comp_bwdref. if (reader_.ReadSymbol(GetReferenceCdf(block))) { bp.reference_frame[1] = kReferenceFrameAlternate; } else { // comp_bwdref_p1. bp.reference_frame[1] = reader_.ReadSymbol(GetReferenceCdf(block)) ? kReferenceFrameAlternate2 : kReferenceFrameBackward; } return; } assert(!use_compound_reference); bp.reference_frame[1] = kReferenceFrameNone; // single_ref_p1. if (reader_.ReadSymbol(GetReferenceCdf(block))) { // single_ref_p2. if (reader_.ReadSymbol(GetReferenceCdf(block))) { bp.reference_frame[0] = kReferenceFrameAlternate; return; } // single_ref_p6. bp.reference_frame[0] = reader_.ReadSymbol(GetReferenceCdf(block)) ? kReferenceFrameAlternate2 : kReferenceFrameBackward; return; } // single_ref_p3. if (reader_.ReadSymbol(GetReferenceCdf(block))) { // single_ref_p5. bp.reference_frame[0] = reader_.ReadSymbol(GetReferenceCdf(block)) ? kReferenceFrameGolden : kReferenceFrameLast3; return; } // single_ref_p4. bp.reference_frame[0] = reader_.ReadSymbol(GetReferenceCdf(block)) ? kReferenceFrameLast2 : kReferenceFrameLast; } void Tile::ReadInterPredictionModeY(const Block& block, const MvContexts& mode_contexts) { BlockParameters& bp = *block.bp; if (bp.skip_mode) { bp.y_mode = kPredictionModeNearestNearestMv; return; } if (frame_header_.segmentation.FeatureActive(bp.segment_id, kSegmentFeatureSkip) || frame_header_.segmentation.FeatureActive(bp.segment_id, kSegmentFeatureGlobalMv)) { bp.y_mode = kPredictionModeGlobalMv; return; } if (bp.reference_frame[1] > kReferenceFrameIntra) { const int idx0 = mode_contexts.reference_mv >> 1; const int idx1 = std::min(mode_contexts.new_mv, kCompoundModeNewMvContexts - 1); const int context = kCompoundModeContextMap[idx0][idx1]; const int offset = reader_.ReadSymbol( symbol_decoder_context_.compound_prediction_mode_cdf[context]); bp.y_mode = static_cast(kPredictionModeNearestNearestMv + offset); return; } // new_mv. if (!reader_.ReadSymbol( symbol_decoder_context_.new_mv_cdf[mode_contexts.new_mv])) { bp.y_mode = kPredictionModeNewMv; return; } // zero_mv. if (!reader_.ReadSymbol( symbol_decoder_context_.zero_mv_cdf[mode_contexts.zero_mv])) { bp.y_mode = kPredictionModeGlobalMv; return; } // ref_mv. bp.y_mode = reader_.ReadSymbol( symbol_decoder_context_.reference_mv_cdf[mode_contexts.reference_mv]) ? kPredictionModeNearMv : kPredictionModeNearestMv; } void Tile::ReadRefMvIndex(const Block& block) { BlockParameters& bp = *block.bp; PredictionParameters& prediction_parameters = *block.bp->prediction_parameters; prediction_parameters.ref_mv_index = 0; if (bp.y_mode != kPredictionModeNewMv && bp.y_mode != kPredictionModeNewNewMv && !kPredictionModeHasNearMvMask.Contains(bp.y_mode)) { return; } const int start = static_cast(kPredictionModeHasNearMvMask.Contains(bp.y_mode)); prediction_parameters.ref_mv_index = start; for (int i = start; i < start + 2; ++i) { if (prediction_parameters.ref_mv_count <= i + 1) break; // drl_mode in the spec. const bool ref_mv_index_bit = reader_.ReadSymbol( symbol_decoder_context_.ref_mv_index_cdf[GetRefMvIndexContext( prediction_parameters.nearest_mv_count, i)]); prediction_parameters.ref_mv_index = i + static_cast(ref_mv_index_bit); if (!ref_mv_index_bit) return; } } void Tile::ReadInterIntraMode(const Block& block, bool is_compound) { BlockParameters& bp = *block.bp; PredictionParameters& prediction_parameters = *block.bp->prediction_parameters; prediction_parameters.inter_intra_mode = kNumInterIntraModes; prediction_parameters.is_wedge_inter_intra = false; if (bp.skip_mode || !sequence_header_.enable_interintra_compound || is_compound || !kIsInterIntraModeAllowedMask.Contains(block.size)) { return; } // kSizeGroup[block.size] is guaranteed to be non-zero because of the block // size constraint enforced in the above condition. assert(kSizeGroup[block.size] - 1 >= 0); if (!reader_.ReadSymbol( symbol_decoder_context_ .is_inter_intra_cdf[kSizeGroup[block.size] - 1])) { prediction_parameters.inter_intra_mode = kNumInterIntraModes; return; } prediction_parameters.inter_intra_mode = static_cast(reader_.ReadSymbol( symbol_decoder_context_ .inter_intra_mode_cdf[kSizeGroup[block.size] - 1])); bp.reference_frame[1] = kReferenceFrameIntra; prediction_parameters.angle_delta[kPlaneTypeY] = 0; prediction_parameters.angle_delta[kPlaneTypeUV] = 0; prediction_parameters.use_filter_intra = false; prediction_parameters.is_wedge_inter_intra = reader_.ReadSymbol( symbol_decoder_context_.is_wedge_inter_intra_cdf[block.size]); if (!prediction_parameters.is_wedge_inter_intra) return; prediction_parameters.wedge_index = reader_.ReadSymbol( symbol_decoder_context_.wedge_index_cdf[block.size]); prediction_parameters.wedge_sign = 0; } void Tile::ReadMotionMode(const Block& block, bool is_compound) { BlockParameters& bp = *block.bp; PredictionParameters& prediction_parameters = *block.bp->prediction_parameters; const auto global_motion_type = frame_header_.global_motion[bp.reference_frame[0]].type; if (bp.skip_mode || !frame_header_.is_motion_mode_switchable || IsBlockDimension4(block.size) || (frame_header_.force_integer_mv == 0 && (bp.y_mode == kPredictionModeGlobalMv || bp.y_mode == kPredictionModeGlobalGlobalMv) && global_motion_type > kGlobalMotionTransformationTypeTranslation) || is_compound || bp.reference_frame[1] == kReferenceFrameIntra || !block.HasOverlappableCandidates()) { prediction_parameters.motion_mode = kMotionModeSimple; return; } prediction_parameters.num_warp_samples = 0; int num_samples_scanned = 0; memset(prediction_parameters.warp_estimate_candidates, 0, sizeof(prediction_parameters.warp_estimate_candidates)); FindWarpSamples(block, &prediction_parameters.num_warp_samples, &num_samples_scanned, prediction_parameters.warp_estimate_candidates); if (frame_header_.force_integer_mv != 0 || prediction_parameters.num_warp_samples == 0 || !frame_header_.allow_warped_motion || IsScaled(bp.reference_frame[0])) { prediction_parameters.motion_mode = reader_.ReadSymbol(symbol_decoder_context_.use_obmc_cdf[block.size]) ? kMotionModeObmc : kMotionModeSimple; return; } prediction_parameters.motion_mode = static_cast(reader_.ReadSymbol( symbol_decoder_context_.motion_mode_cdf[block.size])); } uint16_t* Tile::GetIsExplicitCompoundTypeCdf(const Block& block) { int context = 0; if (block.top_available[kPlaneY]) { if (!block.IsTopSingle()) { context += static_cast(block.bp_top->is_explicit_compound_type); } else if (block.TopReference(0) == kReferenceFrameAlternate) { context += 3; } } if (block.left_available[kPlaneY]) { if (!block.IsLeftSingle()) { context += static_cast(block.bp_left->is_explicit_compound_type); } else if (block.LeftReference(0) == kReferenceFrameAlternate) { context += 3; } } return symbol_decoder_context_.is_explicit_compound_type_cdf[std::min( context, kIsExplicitCompoundTypeContexts - 1)]; } uint16_t* Tile::GetIsCompoundTypeAverageCdf(const Block& block) { const BlockParameters& bp = *block.bp; const ReferenceInfo& reference_info = *current_frame_.reference_info(); const int forward = std::abs(reference_info.relative_distance_from[bp.reference_frame[0]]); const int backward = std::abs(reference_info.relative_distance_from[bp.reference_frame[1]]); int context = (forward == backward) ? 3 : 0; if (block.top_available[kPlaneY]) { if (!block.IsTopSingle()) { context += static_cast(block.bp_top->is_compound_type_average); } else if (block.TopReference(0) == kReferenceFrameAlternate) { ++context; } } if (block.left_available[kPlaneY]) { if (!block.IsLeftSingle()) { context += static_cast(block.bp_left->is_compound_type_average); } else if (block.LeftReference(0) == kReferenceFrameAlternate) { ++context; } } return symbol_decoder_context_.is_compound_type_average_cdf[context]; } void Tile::ReadCompoundType(const Block& block, bool is_compound) { BlockParameters& bp = *block.bp; bp.is_explicit_compound_type = false; bp.is_compound_type_average = true; PredictionParameters& prediction_parameters = *block.bp->prediction_parameters; if (bp.skip_mode) { prediction_parameters.compound_prediction_type = kCompoundPredictionTypeAverage; return; } if (is_compound) { if (sequence_header_.enable_masked_compound) { bp.is_explicit_compound_type = reader_.ReadSymbol(GetIsExplicitCompoundTypeCdf(block)); } if (bp.is_explicit_compound_type) { if (kIsWedgeCompoundModeAllowed.Contains(block.size)) { // Only kCompoundPredictionTypeWedge and // kCompoundPredictionTypeDiffWeighted are signaled explicitly. prediction_parameters.compound_prediction_type = static_cast(reader_.ReadSymbol( symbol_decoder_context_.compound_type_cdf[block.size])); } else { prediction_parameters.compound_prediction_type = kCompoundPredictionTypeDiffWeighted; } } else { if (sequence_header_.enable_jnt_comp) { bp.is_compound_type_average = reader_.ReadSymbol(GetIsCompoundTypeAverageCdf(block)); prediction_parameters.compound_prediction_type = bp.is_compound_type_average ? kCompoundPredictionTypeAverage : kCompoundPredictionTypeDistance; } else { prediction_parameters.compound_prediction_type = kCompoundPredictionTypeAverage; return; } } if (prediction_parameters.compound_prediction_type == kCompoundPredictionTypeWedge) { prediction_parameters.wedge_index = reader_.ReadSymbol( symbol_decoder_context_.wedge_index_cdf[block.size]); prediction_parameters.wedge_sign = static_cast(reader_.ReadBit()); } else if (prediction_parameters.compound_prediction_type == kCompoundPredictionTypeDiffWeighted) { prediction_parameters.mask_is_inverse = static_cast(reader_.ReadBit()); } return; } if (prediction_parameters.inter_intra_mode != kNumInterIntraModes) { prediction_parameters.compound_prediction_type = prediction_parameters.is_wedge_inter_intra ? kCompoundPredictionTypeWedge : kCompoundPredictionTypeIntra; return; } prediction_parameters.compound_prediction_type = kCompoundPredictionTypeAverage; } uint16_t* Tile::GetInterpolationFilterCdf(const Block& block, int direction) { const BlockParameters& bp = *block.bp; int context = MultiplyBy8(direction) + MultiplyBy4(static_cast(bp.reference_frame[1] > kReferenceFrameIntra)); int top_type = kNumExplicitInterpolationFilters; if (block.top_available[kPlaneY]) { if (block.bp_top->reference_frame[0] == bp.reference_frame[0] || block.bp_top->reference_frame[1] == bp.reference_frame[0]) { top_type = block.bp_top->interpolation_filter[direction]; } } int left_type = kNumExplicitInterpolationFilters; if (block.left_available[kPlaneY]) { if (block.bp_left->reference_frame[0] == bp.reference_frame[0] || block.bp_left->reference_frame[1] == bp.reference_frame[0]) { left_type = block.bp_left->interpolation_filter[direction]; } } if (left_type == top_type) { context += left_type; } else if (left_type == kNumExplicitInterpolationFilters) { context += top_type; } else if (top_type == kNumExplicitInterpolationFilters) { context += left_type; } else { context += kNumExplicitInterpolationFilters; } return symbol_decoder_context_.interpolation_filter_cdf[context]; } void Tile::ReadInterpolationFilter(const Block& block) { BlockParameters& bp = *block.bp; if (frame_header_.interpolation_filter != kInterpolationFilterSwitchable) { static_assert( sizeof(bp.interpolation_filter) / sizeof(bp.interpolation_filter[0]) == 2, "Interpolation filter array size is not 2"); for (auto& interpolation_filter : bp.interpolation_filter) { interpolation_filter = frame_header_.interpolation_filter; } return; } bool interpolation_filter_present = true; if (bp.skip_mode || block.bp->prediction_parameters->motion_mode == kMotionModeLocalWarp) { interpolation_filter_present = false; } else if (!IsBlockDimension4(block.size) && bp.y_mode == kPredictionModeGlobalMv) { interpolation_filter_present = frame_header_.global_motion[bp.reference_frame[0]].type == kGlobalMotionTransformationTypeTranslation; } else if (!IsBlockDimension4(block.size) && bp.y_mode == kPredictionModeGlobalGlobalMv) { interpolation_filter_present = frame_header_.global_motion[bp.reference_frame[0]].type == kGlobalMotionTransformationTypeTranslation || frame_header_.global_motion[bp.reference_frame[1]].type == kGlobalMotionTransformationTypeTranslation; } for (int i = 0; i < (sequence_header_.enable_dual_filter ? 2 : 1); ++i) { bp.interpolation_filter[i] = interpolation_filter_present ? static_cast( reader_.ReadSymbol( GetInterpolationFilterCdf(block, i))) : kInterpolationFilterEightTap; } if (!sequence_header_.enable_dual_filter) { bp.interpolation_filter[1] = bp.interpolation_filter[0]; } } bool Tile::ReadInterBlockModeInfo(const Block& block) { BlockParameters& bp = *block.bp; bp.palette_mode_info.size[kPlaneTypeY] = 0; bp.palette_mode_info.size[kPlaneTypeUV] = 0; ReadReferenceFrames(block); const bool is_compound = bp.reference_frame[1] > kReferenceFrameIntra; MvContexts mode_contexts; FindMvStack(block, is_compound, &mode_contexts); ReadInterPredictionModeY(block, mode_contexts); ReadRefMvIndex(block); if (!AssignInterMv(block, is_compound)) return false; ReadInterIntraMode(block, is_compound); ReadMotionMode(block, is_compound); ReadCompoundType(block, is_compound); ReadInterpolationFilter(block); return true; } bool Tile::DecodeInterModeInfo(const Block& block) { BlockParameters& bp = *block.bp; block.bp->prediction_parameters->use_intra_block_copy = false; bp.skip = false; if (!ReadInterSegmentId(block, /*pre_skip=*/true)) return false; ReadSkipMode(block); if (bp.skip_mode) { bp.skip = true; } else { ReadSkip(block); } if (!frame_header_.segmentation.segment_id_pre_skip && !ReadInterSegmentId(block, /*pre_skip=*/false)) { return false; } ReadCdef(block); if (read_deltas_) { ReadQuantizerIndexDelta(block); ReadLoopFilterDelta(block); read_deltas_ = false; } ReadIsInter(block); return bp.is_inter ? ReadInterBlockModeInfo(block) : ReadIntraBlockModeInfo(block, /*intra_y_mode=*/false); } bool Tile::DecodeModeInfo(const Block& block) { return IsIntraFrame(frame_header_.frame_type) ? DecodeIntraModeInfo(block) : DecodeInterModeInfo(block); } } // namespace libgav1