| /* | 
 |  *  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 |