| /* | |
| * Copyright (c) 2024 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 "video/quality_convergence_controller.h" | |
| #include <algorithm> | |
| #include <memory> | |
| #include <optional> | |
| #include "api/field_trials_view.h" | |
| #include "api/video/video_codec_type.h" | |
| #include "rtc_base/checks.h" | |
| #include "rtc_base/experiments/struct_parameters_parser.h" | |
| #include "rtc_base/logging.h" | |
| #include "video/quality_convergence_monitor.h" | |
| namespace webrtc { | |
| namespace { | |
| // TODO(https://crbug.com/328598314): Remove default values once HW encoders | |
| // correctly report the minimum QP value. These thresholds correspond to the | |
| // default configurations used for the software encoders. | |
| constexpr int kVp8DefaultStaticQpThreshold = 15; | |
| constexpr int kVp9DefaultStaticQpThreshold = 32; | |
| constexpr int kAv1DefaultStaticQpThreshold = 60; | |
| struct StaticDetectionConfig { | |
| // Overrides the static QP threshold if set to a higher value than what is | |
| // reported by the encoder. | |
| std::optional<int> static_qp_threshold_override; | |
| std::unique_ptr<StructParametersParser> Parser(); | |
| }; | |
| std::unique_ptr<StructParametersParser> StaticDetectionConfig::Parser() { | |
| // The empty comments ensures that each pair is on a separate line. | |
| return StructParametersParser::Create("static_qp_threshold", | |
| &static_qp_threshold_override); | |
| } | |
| int GetDefaultStaticQpThreshold(VideoCodecType codec, | |
| const FieldTrialsView& trials) { | |
| StaticDetectionConfig static_config; | |
| int default_static_qp_threhsold = 0; | |
| switch (codec) { | |
| case kVideoCodecVP8: | |
| default_static_qp_threhsold = kVp8DefaultStaticQpThreshold; | |
| static_config.Parser()->Parse(trials.Lookup("WebRTC-QCM-Static-VP8")); | |
| break; | |
| case kVideoCodecVP9: | |
| default_static_qp_threhsold = kVp9DefaultStaticQpThreshold; | |
| static_config.Parser()->Parse(trials.Lookup("WebRTC-QCM-Static-VP9")); | |
| break; | |
| case kVideoCodecAV1: | |
| default_static_qp_threhsold = kAv1DefaultStaticQpThreshold; | |
| static_config.Parser()->Parse(trials.Lookup("WebRTC-QCM-Static-AV1")); | |
| break; | |
| case kVideoCodecGeneric: | |
| case kVideoCodecH264: | |
| case kVideoCodecH265: | |
| // -1 will effectively disable the static QP threshold since QP values are | |
| // always >= 0. | |
| return -1; | |
| } | |
| if (static_config.static_qp_threshold_override.has_value()) { | |
| RTC_LOG(LS_INFO) << "static_qp_threshold_override: " | |
| << *static_config.static_qp_threshold_override; | |
| return *static_config.static_qp_threshold_override; | |
| } | |
| return default_static_qp_threhsold; | |
| } | |
| } // namespace | |
| void QualityConvergenceController::Initialize(int number_of_layers, | |
| std::optional<int> encoder_min_qp, | |
| VideoCodecType codec, | |
| const FieldTrialsView& trials) { | |
| RTC_DCHECK(sequence_checker_.IsCurrent()); | |
| RTC_CHECK(number_of_layers > 0); | |
| number_of_layers_ = number_of_layers; | |
| convergence_monitors_.clear(); | |
| int qp_threshold = GetDefaultStaticQpThreshold(codec, trials); | |
| if (encoder_min_qp.has_value()) { | |
| qp_threshold = std::max(qp_threshold, *encoder_min_qp); | |
| } | |
| for (int i = 0; i < number_of_layers_; ++i) { | |
| convergence_monitors_.push_back( | |
| QualityConvergenceMonitor::Create(qp_threshold, codec, trials)); | |
| } | |
| initialized_ = true; | |
| } | |
| bool QualityConvergenceController::AddSampleAndCheckTargetQuality( | |
| int layer_index, | |
| int qp, | |
| bool is_refresh_frame) { | |
| RTC_DCHECK(sequence_checker_.IsCurrent()); | |
| RTC_CHECK(initialized_); | |
| if (layer_index < 0 || layer_index >= number_of_layers_) { | |
| return false; | |
| } | |
| // TODO(kron): Remove temporary check that verifies that the initialization is | |
| // working as expected. See https://crbug.com/359410061. | |
| RTC_DCHECK(number_of_layers_ == | |
| static_cast<int>(convergence_monitors_.size())); | |
| if (number_of_layers_ != static_cast<int>(convergence_monitors_.size())) { | |
| return false; | |
| } | |
| convergence_monitors_[layer_index]->AddSample(qp, is_refresh_frame); | |
| return convergence_monitors_[layer_index]->AtTargetQuality(); | |
| } | |
| } // namespace webrtc |