/* * Copyright 2025 The WebRTC project authors. All rights reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "modules/video_coding/utility/av1_qp_parser.h" #include #include #include #include #include #include "third_party/libgav1/src/src/buffer_pool.h" #include "third_party/libgav1/src/src/decoder_state.h" #include "third_party/libgav1/src/src/gav1/decoder.h" #include "third_party/libgav1/src/src/gav1/status_code.h" #include "third_party/libgav1/src/src/obu_parser.h" namespace webrtc { namespace { class Av1QpHeaderParser : public Av1QpParser { public: Av1QpHeaderParser() : buffer_pool_(/*on_frame_buffer_size_changed=*/nullptr, /*get_frame_buffer=*/nullptr, /*release_frame_buffer=*/nullptr, /*callback_private_data=*/nullptr) {} std::optional Parse(std::span frame_data) override { return Parse(frame_data, 0); } std::optional Parse(std::span frame_data, int operating_point) override { libgav1::RefCountedBufferPtr curr_frame; libgav1::ObuParser parser(frame_data.data(), frame_data.size(), operating_point, &buffer_pool_, &decoder_state_); std::optional highest_acceptable_spatial_layers_qp; // Since the temporal unit can have more than 1 frame in scalable coding, we // go through all the frame's. while (parser.HasData()) { // If the frame is not a keyframe, the `parser` must know the information // from `sequence_header` to parse the OBU properly. if (sequence_header_) { parser.set_sequence_header(*sequence_header_); } if (parser.ParseOneFrame(&curr_frame) != libgav1::kStatusOk) { return std::nullopt; } // Get QP from frame header. const auto& frame_header = parser.frame_header(); // frame_header.quantizer.base_index returns 0 if based on // `operating_point` we are not interested in higher spatial layer's QP // values. if (frame_header.quantizer.base_index != 0) { highest_acceptable_spatial_layers_qp = frame_header.quantizer.base_index; } // Update the state for the next frame. if (parser.sequence_header_changed()) { sequence_header_ = parser.sequence_header(); } decoder_state_.UpdateReferenceFrames(curr_frame, frame_header.refresh_frame_flags); } return highest_acceptable_spatial_layers_qp; } private: libgav1::BufferPool buffer_pool_; libgav1::DecoderState decoder_state_; std::optional sequence_header_ = std::nullopt; }; class Av1QpAverageParser : public Av1QpParser { public: Av1QpAverageParser() = default; std::optional Parse(std::span frame_data) override { return Parse(frame_data, 0); } std::optional Parse(std::span frame_data, int operating_point) override { if (!decoder_ || operating_point_ != operating_point) { libgav1::DecoderSettings settings; settings.parse_only = true; settings.operating_point = operating_point; decoder_ = std::make_unique(); if (decoder_->Init(&settings) != libgav1::kStatusOk) { decoder_.reset(); return std::nullopt; } operating_point_ = operating_point; } if (decoder_->EnqueueFrame(frame_data.data(), frame_data.size(), 0, nullptr) != libgav1::kStatusOk) { return std::nullopt; } const libgav1::DecoderBuffer* buffer = nullptr; if (decoder_->DequeueFrame(&buffer) != libgav1::kStatusOk) { return std::nullopt; } std::vector qps = decoder_->GetFramesMeanQpInTemporalUnit(); if (qps.empty()) { return std::nullopt; } return qps.back(); } private: std::unique_ptr decoder_; int operating_point_ = -1; }; } // namespace std::unique_ptr Av1QpParser::Create(Settings settings) { if (settings.use_average_qp) { return std::make_unique(); } return std::make_unique(); } } // namespace webrtc