| // Copyright 2024 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "media/gpu/av1_builder.h" |
| |
| #include <iterator> |
| |
| #include "base/check_op.h" |
| #include "base/numerics/safe_conversions.h" |
| #include "third_party/libgav1/src/src/obu_parser.h" |
| |
| namespace media { |
| |
| namespace { |
| constexpr int kPrimaryReferenceNone = 7; |
| } // namespace |
| |
| AV1BitstreamBuilder::SequenceHeader::SequenceHeader() = default; |
| AV1BitstreamBuilder::SequenceHeader::SequenceHeader( |
| const AV1BitstreamBuilder::SequenceHeader&) = default; |
| AV1BitstreamBuilder::SequenceHeader& |
| AV1BitstreamBuilder::SequenceHeader::operator=( |
| AV1BitstreamBuilder::SequenceHeader&&) noexcept = default; |
| |
| AV1BitstreamBuilder::FrameHeader::FrameHeader() = default; |
| AV1BitstreamBuilder::FrameHeader::FrameHeader( |
| AV1BitstreamBuilder::FrameHeader&&) noexcept = default; |
| |
| AV1BitstreamBuilder::AV1BitstreamBuilder() = default; |
| AV1BitstreamBuilder::~AV1BitstreamBuilder() = default; |
| AV1BitstreamBuilder::AV1BitstreamBuilder(AV1BitstreamBuilder&&) = default; |
| |
| AV1BitstreamBuilder AV1BitstreamBuilder::BuildSequenceHeaderOBU( |
| const SequenceHeader& seq_hdr) { |
| AV1BitstreamBuilder ret; |
| ret.Write(seq_hdr.profile, 3); |
| ret.WriteBool(false); // Still picture default 0. |
| ret.WriteBool(false); // Disable reduced still picture. |
| ret.WriteBool(false); // No timing info present. |
| ret.WriteBool(false); // No initial display delay. |
| |
| CHECK_LT(seq_hdr.operating_points_cnt_minus_1, kMaxTemporalLayerNum); |
| ret.Write(seq_hdr.operating_points_cnt_minus_1, 5); |
| for (uint8_t i = 0; i <= seq_hdr.operating_points_cnt_minus_1; i++) { |
| if (seq_hdr.operating_points_cnt_minus_1 == 0) { |
| ret.Write(0, 12); // No scalability information. |
| } else { |
| ret.Write(1, 4); // Spatial layer 1 should be decoded. |
| ret.Write((1 << (seq_hdr.operating_points_cnt_minus_1 + 1 - i)) - 1, 8); |
| } |
| ret.Write(seq_hdr.level[i], 5); |
| if (seq_hdr.level[i] > 7) { |
| ret.WriteBool(seq_hdr.tier[i]); |
| } |
| } |
| |
| ret.Write(seq_hdr.frame_width_bits_minus_1, 4); |
| ret.Write(seq_hdr.frame_height_bits_minus_1, 4); |
| ret.Write(seq_hdr.width - 1, seq_hdr.frame_width_bits_minus_1 + 1); |
| ret.Write(seq_hdr.height - 1, seq_hdr.frame_height_bits_minus_1 + 1); |
| ret.WriteBool(false); // No frame id numbers present. |
| ret.WriteBool(seq_hdr.use_128x128_superblock); |
| ret.WriteBool(seq_hdr.enable_filter_intra); |
| ret.WriteBool(seq_hdr.enable_intra_edge_filter); |
| ret.WriteBool(seq_hdr.enable_interintra_compound); |
| ret.WriteBool(seq_hdr.enable_masked_compound); |
| ret.WriteBool(seq_hdr.enable_warped_motion); |
| ret.WriteBool(seq_hdr.enable_dual_filter); |
| ret.WriteBool(seq_hdr.enable_order_hint); |
| if (seq_hdr.enable_order_hint) { |
| ret.WriteBool(seq_hdr.enable_jnt_comp); |
| ret.WriteBool(seq_hdr.enable_ref_frame_mvs); |
| } |
| |
| ret.WriteBool(true); // Enable sequence choose screen content tools. |
| ret.WriteBool(false); // Disable sequence choose integer MV. |
| ret.WriteBool(false); // Disable sequence force integer MV. |
| if (seq_hdr.enable_order_hint) { |
| ret.Write(seq_hdr.order_hint_bits_minus_1, 3); |
| } |
| ret.WriteBool(seq_hdr.enable_superres); |
| ret.WriteBool(seq_hdr.enable_cdef); |
| ret.WriteBool(seq_hdr.enable_restoration); |
| |
| // AV1 spec section 5.5.2, color config syntax. |
| ret.WriteBool(false); // Disable high bitdepth. |
| if (seq_hdr.profile != libgav1::BitstreamProfile::kProfile1) { |
| ret.WriteBool(false); // Disable monochrome. |
| } |
| |
| if (seq_hdr.color_description_present_flag) { |
| ret.WriteBool(true); // Color description present. |
| ret.Write(seq_hdr.color_primaries, 8); |
| ret.Write(seq_hdr.transfer_characteristics, 8); |
| ret.Write(seq_hdr.matrix_coefficients, 8); |
| } else { |
| ret.WriteBool(false); // No color description present. |
| } |
| |
| // We won't skip color range syntax unless the color primariy is |
| // Rec.709, transfer is sRGB and at the same time the identity |
| // matrix is used. |
| ret.WriteBool(seq_hdr.color_range); |
| if (seq_hdr.profile != libgav1::BitstreamProfile::kProfile1) { |
| ret.Write(0, 2); // Chroma sample position = 0. |
| } |
| |
| ret.WriteBool(true); // Separate uv delta q. |
| ret.WriteBool(false); // No film grain parameters present. |
| |
| ret.PutTrailingBits(); |
| return ret; |
| } |
| |
| AV1BitstreamBuilder AV1BitstreamBuilder::BuildFrameHeaderOBU( |
| const SequenceHeader& seq_hdr, |
| const FrameHeader& pic_hdr) { |
| AV1BitstreamBuilder ret; |
| ret.WriteBool(false); // For a frame OBU, the show_existing_frame flag is |
| // always set to 0. |
| ret.Write(pic_hdr.frame_type, 2); |
| ret.WriteBool(true); // If this frame needs to be immediately output once |
| // decoded, show_frame flag should be true. |
| if (pic_hdr.frame_type != libgav1::FrameType::kFrameKey) { |
| ret.WriteBool(pic_hdr.error_resilient_mode); |
| } |
| ret.WriteBool(pic_hdr.disable_cdf_update); |
| ret.WriteBool(pic_hdr.allow_screen_content_tools); |
| ret.WriteBool(false); // Disable frame size override flag. |
| if (seq_hdr.enable_order_hint) { |
| ret.Write(pic_hdr.order_hint, seq_hdr.order_hint_bits_minus_1 + 1); |
| } |
| |
| if (pic_hdr.frame_type != libgav1::FrameType::kFrameKey) { |
| if (!pic_hdr.error_resilient_mode) { |
| ret.Write(pic_hdr.primary_ref_frame, 3); |
| } |
| ret.Write(pic_hdr.refresh_frame_flags, 8); |
| |
| if (pic_hdr.error_resilient_mode && seq_hdr.enable_order_hint) { |
| // Set order hint for each reference frame. |
| for (uint32_t order_hint : pic_hdr.ref_order_hint) { |
| ret.Write(order_hint, seq_hdr.order_hint_bits_minus_1 + 1); |
| } |
| } |
| if (seq_hdr.enable_order_hint) { |
| ret.WriteBool(false); // Disable frame reference short signaling. |
| } |
| for (uint8_t ref_idx : pic_hdr.ref_frame_idx) { |
| ret.Write(ref_idx, 3); |
| } |
| ret.WriteBool(false); // Render and frame size are the same. |
| ret.WriteBool(false); // No allow high precision MV. |
| bool is_switchable_interp = |
| pic_hdr.interpolation_filter == |
| libgav1::InterpolationFilter::kInterpolationFilterSwitchable; |
| ret.WriteBool(is_switchable_interp); |
| if (!is_switchable_interp) { |
| ret.Write(pic_hdr.interpolation_filter, 2); |
| } |
| ret.WriteBool(false); // Motion not switchable. |
| if (seq_hdr.enable_ref_frame_mvs) { |
| ret.WriteBool(false); // Do not use ref frame MVs. |
| } |
| } else { |
| ret.WriteBool(false); // Render and frame size are the same. |
| if (pic_hdr.allow_screen_content_tools) { |
| ret.WriteBool(pic_hdr.allow_intrabc); |
| } |
| } |
| if (!pic_hdr.disable_cdf_update) { |
| ret.WriteBool(pic_hdr.disable_frame_end_update_cdf); |
| } |
| // Pack tile info |
| ret.WriteBool(true); // Uniform tile spacing. |
| ret.WriteBool(false); // Don't increment log2 of tile cols. |
| ret.WriteBool(false); // Don't increment log2 of tile rows. |
| |
| // Pack quantization params. Refer to AV1 spec section 5.9.12. |
| ret.Write(pic_hdr.base_qindex, 8); |
| if (pic_hdr.delta_q_y_dc) { |
| ret.WriteBool(true); |
| ret.WriteSU(pic_hdr.delta_q_y_dc, 7); |
| } else { |
| ret.WriteBool(false); |
| } |
| if (pic_hdr.separate_uv_delta_q) { |
| bool diff_uv_delta = false; |
| if (pic_hdr.delta_q_u_dc != pic_hdr.delta_q_v_dc || |
| pic_hdr.delta_q_u_ac != pic_hdr.delta_q_v_ac) { |
| diff_uv_delta = true; |
| } |
| ret.WriteBool(diff_uv_delta); |
| for (const auto& delta_q : {pic_hdr.delta_q_u_dc, pic_hdr.delta_q_u_ac}) { |
| if (delta_q) { |
| ret.WriteBool(true); |
| ret.WriteSU(delta_q, 7); |
| } else { |
| ret.WriteBool(false); |
| } |
| } |
| if (diff_uv_delta) { |
| for (const auto& delta_q : {pic_hdr.delta_q_v_dc, pic_hdr.delta_q_v_ac}) { |
| if (delta_q) { |
| ret.WriteBool(true); |
| ret.WriteSU(delta_q, 7); |
| } else { |
| ret.WriteBool(false); |
| } |
| } |
| } |
| } |
| ret.WriteBool(pic_hdr.using_qmatrix); |
| if (pic_hdr.using_qmatrix) { |
| ret.Write(pic_hdr.qm_y, 4); |
| ret.Write(pic_hdr.qm_u, 4); |
| if (pic_hdr.separate_uv_delta_q) { |
| ret.Write(pic_hdr.qm_v, 4); |
| } |
| } |
| |
| // Pack segmentation params. Refer to AV1 spec section 5.9.14. |
| ret.WriteBool(pic_hdr.segmentation_enabled); |
| if (pic_hdr.segmentation_enabled) { |
| bool segmentation_update_data = true; |
| if (pic_hdr.primary_ref_frame != kPrimaryReferenceNone) { |
| const bool segmentation_update_map = pic_hdr.segmentation_update_map; |
| ret.WriteBool(segmentation_update_map); |
| if (segmentation_update_map) { |
| ret.WriteBool(pic_hdr.segmentation_temporal_update); |
| } |
| segmentation_update_data = pic_hdr.segmentation_update_data; |
| ret.WriteBool(segmentation_update_data); |
| } |
| if (segmentation_update_data) { |
| static constexpr std::array<uint8_t, libgav1::kSegmentFeatureMax> |
| kSegmentaionFeatureBits = {8, 6, 6, 6, 6, 3, 0, 0}; |
| static constexpr std::array<bool, libgav1::kSegmentFeatureMax> |
| kSegmentFeatureSigned = {true, true, true, true, |
| true, false, false, false}; |
| for (uint32_t i = 0; i < libgav1::kMaxSegments; i++) { |
| for (uint32_t j = 0; j < libgav1::kSegmentFeatureMax; j++) { |
| const bool feature_enabled = pic_hdr.feature_enabled[i][j]; |
| ret.WriteBool(feature_enabled); |
| if (feature_enabled) { |
| const size_t bits_to_write = kSegmentaionFeatureBits[j]; |
| const int16_t feature_data = pic_hdr.feature_data[i][j]; |
| if (kSegmentFeatureSigned[j]) { |
| ret.WriteSU(feature_data, bits_to_write + 1); |
| } else if (bits_to_write > 0) { |
| ret.Write(feature_data, bits_to_write); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| // Pack quantization index delta params. Refer to AV1 spec section 5.9.17. |
| if (pic_hdr.base_qindex > 0) { |
| ret.WriteBool(pic_hdr.delta_q_present); |
| if (pic_hdr.delta_q_present) { |
| ret.Write(pic_hdr.delta_q_res, 2); |
| } |
| } |
| |
| // Pack loop filter delta params. Refer to AV1 spec section 5.9.18. |
| if (pic_hdr.delta_q_present && !pic_hdr.allow_intrabc) { |
| ret.WriteBool(pic_hdr.delta_lf_present); |
| if (pic_hdr.delta_lf_present) { |
| ret.Write(pic_hdr.delta_lf_res, 2); |
| ret.WriteBool(pic_hdr.delta_lf_multi); |
| } |
| } |
| |
| if (!pic_hdr.allow_intrabc) { |
| // Pack loop filter parameters. Refer to AV1 spec section 5.9.11. |
| ret.Write(pic_hdr.filter_level[0], 6); |
| ret.Write(pic_hdr.filter_level[1], 6); |
| if (pic_hdr.filter_level[0] || pic_hdr.filter_level[1]) { |
| ret.Write(pic_hdr.filter_level_u, 6); |
| ret.Write(pic_hdr.filter_level_v, 6); |
| } |
| ret.Write(pic_hdr.sharpness_level, 3); |
| ret.WriteBool(pic_hdr.loop_filter_delta_enabled); |
| if (pic_hdr.loop_filter_delta_enabled) { |
| ret.WriteBool(pic_hdr.loop_filter_delta_update); |
| if (pic_hdr.loop_filter_delta_update) { |
| for (const auto& delta : pic_hdr.loop_filter_ref_deltas) { |
| if (delta) { |
| ret.WriteBool(true); |
| ret.WriteSU(delta, 7); |
| } else { |
| ret.WriteBool(false); |
| } |
| } |
| ret.WriteBool(pic_hdr.update_mode_delta); |
| for (const auto& delta : pic_hdr.loop_filter_mode_deltas) { |
| if (delta) { |
| ret.WriteBool(true); |
| ret.WriteSU(delta, 7); |
| } else { |
| ret.WriteBool(false); |
| } |
| } |
| } |
| } |
| |
| // Pack CDEF parameters. Refer to AV1 spec section 5.9.19. |
| if (seq_hdr.enable_cdef) { |
| ret.Write(pic_hdr.cdef_damping_minus_3, 2); |
| ret.Write(pic_hdr.cdef_bits, 2); |
| for (uint32_t i = 0; i < (1 << pic_hdr.cdef_bits); i++) { |
| ret.Write(pic_hdr.cdef_y_pri_strength[i], 4); |
| ret.Write(pic_hdr.cdef_y_sec_strength[i], 2); |
| ret.Write(pic_hdr.cdef_uv_pri_strength[i], 4); |
| ret.Write(pic_hdr.cdef_uv_sec_strength[i], 2); |
| } |
| } |
| |
| // Pack loop restoration filter parameters. Refer to AV1 spec |
| // section 5.9.20. |
| if (seq_hdr.enable_restoration) { |
| constexpr int kNumPlanes = 3; |
| bool use_lr = false; |
| bool use_chroma_lr = false; |
| for (int i = 0; i < kNumPlanes; i++) { |
| ret.Write(pic_hdr.restoration_type[i], 2); |
| if (pic_hdr.restoration_type[i] != |
| libgav1::LoopRestorationType::kLoopRestorationTypeNone) { |
| use_lr = true; |
| if (i > 0) { |
| use_chroma_lr = true; |
| } |
| } |
| } |
| if (use_lr) { |
| uint8_t lr_unit_shift = pic_hdr.lr_unit_shift; |
| if (seq_hdr.use_128x128_superblock) { |
| ret.WriteBool(lr_unit_shift > 0); |
| } else { |
| ret.WriteBool(lr_unit_shift > 0); |
| if (lr_unit_shift) { |
| ret.WriteBool(lr_unit_shift > 1); |
| } |
| } |
| |
| if (use_chroma_lr) { |
| ret.WriteBool(!!pic_hdr.lr_uv_shift); |
| } |
| } |
| } |
| } |
| |
| // TX mode syntax. Refer to AV1 spec section 5.9.21 |
| ret.WriteBool(pic_hdr.tx_mode == libgav1::TxMode::kTxModeSelect); |
| |
| // Skip mode parameters are not present, as the encoder will only enable |
| // single prediction. Refer to AV1 spec section 5.9.22. |
| |
| // Frame reference mode. Refer to AV1 spec section 5.9.23. |
| if (pic_hdr.frame_type != libgav1::FrameType::kFrameKey) { |
| ret.WriteBool(pic_hdr.reference_select); |
| } |
| ret.WriteBool(pic_hdr.reduced_tx_set); |
| |
| // Global motion parameters. Refer to AV1 spec section 5.9.24. |
| if (pic_hdr.frame_type != libgav1::FrameType::kFrameKey) { |
| for (int i = 1 /*LAST_FRAME*/; i <= 7 /*ALTREF_FRAME*/; i++) { |
| ret.WriteBool(false); // Set is_global to all zeros. |
| } |
| } |
| |
| ret.PutAlignBits(); |
| return ret; |
| } |
| |
| void AV1BitstreamBuilder::Write(uint64_t val, int num_bits) { |
| queued_writes_.emplace_back(val, num_bits); |
| total_outstanding_bits_ += num_bits; |
| } |
| |
| void AV1BitstreamBuilder::WriteBool(bool val) { |
| Write(val, 1); |
| } |
| |
| std::vector<uint8_t> AV1BitstreamBuilder::Flush() && { |
| std::vector<uint8_t> ret; |
| uint8_t curr_byte = 0; |
| int rem_bits_in_byte = 8; |
| for (auto queued_write : queued_writes_) { |
| uint64_t val = queued_write.first; |
| int outstanding_bits = queued_write.second; |
| while (outstanding_bits) { |
| if (rem_bits_in_byte >= outstanding_bits) { |
| curr_byte |= val << (rem_bits_in_byte - outstanding_bits); |
| rem_bits_in_byte -= outstanding_bits; |
| outstanding_bits = 0; |
| } else { |
| curr_byte |= (val >> (outstanding_bits - rem_bits_in_byte)) & |
| ((1 << rem_bits_in_byte) - 1); |
| outstanding_bits -= rem_bits_in_byte; |
| rem_bits_in_byte = 0; |
| } |
| if (!rem_bits_in_byte) { |
| ret.push_back(curr_byte); |
| curr_byte = 0; |
| rem_bits_in_byte = 8; |
| } |
| } |
| } |
| |
| if (rem_bits_in_byte != 8) { |
| ret.push_back(curr_byte); |
| } |
| |
| queued_writes_.clear(); |
| total_outstanding_bits_ = 0; |
| |
| return ret; |
| } |
| |
| void AV1BitstreamBuilder::PutAlignBits() { |
| int misalignment = total_outstanding_bits_ % 8; |
| if (misalignment != 0) { |
| int num_zero_bits = 8 - misalignment; |
| Write(0, num_zero_bits); |
| } |
| } |
| |
| void AV1BitstreamBuilder::PutTrailingBits() { |
| WriteBool(true); // trialing one bit. |
| PutAlignBits(); |
| } |
| |
| void AV1BitstreamBuilder::WriteOBUHeader(libgav1::ObuType type, |
| bool has_size, |
| bool extension_flag, |
| std::optional<uint8_t> temporal_id) { |
| DCHECK_LE(1, type); |
| DCHECK_LE(type, 8); |
| WriteBool(false); // forbidden bit must be set to 0. |
| Write(static_cast<uint64_t>(type), 4); |
| WriteBool(extension_flag); |
| WriteBool(has_size); |
| WriteBool(false); // reserved bit must be set to 0. |
| if (extension_flag) { |
| CHECK(temporal_id.has_value()); |
| Write(temporal_id.value(), 3); |
| Write(0, 2); // spatial layer must be zero. |
| Write(0, 3); // reserved bits must be set to 0. |
| } |
| } |
| |
| // Encode a variable length unsigned integer of up to 4 bytes. |
| // Most significant bit of each byte indicates if parsing should continue, and |
| // the 7 least significant bits hold the actual data. So the encoded length |
| // may be 5 bytes under some circumstances. |
| // This function also has a fixed size mode where we pass in a fixed size for |
| // the data and the function zero pads up to that size. |
| // See section 4.10.5 of the AV1 specification. |
| void AV1BitstreamBuilder::WriteValueInLeb128(uint32_t value, |
| std::optional<int> fixed_size) { |
| for (int i = 0; i < fixed_size.value_or(5); i++) { |
| uint8_t curr_byte = value & 0x7F; |
| value >>= 7; |
| if (value || fixed_size) { |
| curr_byte |= 0x80; |
| Write(curr_byte, 8); |
| } else { |
| Write(curr_byte, 8); |
| break; |
| } |
| } |
| } |
| |
| void AV1BitstreamBuilder::WriteSU(int16_t value, size_t num_bits) { |
| // Encode a signed integer in SU(num_bits) format. |
| // See section 4.10.6 of the AV1 specification. |
| Write(value & ((1 << num_bits) - 1), num_bits); |
| } |
| |
| void AV1BitstreamBuilder::AppendBitstreamBuffer(AV1BitstreamBuilder buffer) { |
| queued_writes_.insert(queued_writes_.end(), |
| std::make_move_iterator(buffer.queued_writes_.begin()), |
| std::make_move_iterator(buffer.queued_writes_.end())); |
| total_outstanding_bits_ += buffer.total_outstanding_bits_; |
| } |
| |
| } // namespace media |