|  | /* | 
|  | *  Copyright (c) 2016 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/audio_coding/audio_network_adaptor/controller_manager.h" | 
|  |  | 
|  | #include <cmath> | 
|  | #include <memory> | 
|  | #include <string> | 
|  | #include <utility> | 
|  |  | 
|  | #include "modules/audio_coding/audio_network_adaptor/bitrate_controller.h" | 
|  | #include "modules/audio_coding/audio_network_adaptor/channel_controller.h" | 
|  | #include "modules/audio_coding/audio_network_adaptor/debug_dump_writer.h" | 
|  | #include "modules/audio_coding/audio_network_adaptor/dtx_controller.h" | 
|  | #include "modules/audio_coding/audio_network_adaptor/fec_controller_plr_based.h" | 
|  | #include "modules/audio_coding/audio_network_adaptor/frame_length_controller.h" | 
|  | #include "modules/audio_coding/audio_network_adaptor/frame_length_controller_v2.h" | 
|  | #include "modules/audio_coding/audio_network_adaptor/util/threshold_curve.h" | 
|  | #include "rtc_base/ignore_wundef.h" | 
|  | #include "rtc_base/logging.h" | 
|  | #include "rtc_base/time_utils.h" | 
|  |  | 
|  | #if WEBRTC_ENABLE_PROTOBUF | 
|  | RTC_PUSH_IGNORING_WUNDEF() | 
|  | #ifdef WEBRTC_ANDROID_PLATFORM_BUILD | 
|  | #include "external/webrtc/webrtc/modules/audio_coding/audio_network_adaptor/config.pb.h" | 
|  | #else | 
|  | #include "modules/audio_coding/audio_network_adaptor/config.pb.h" | 
|  | #endif | 
|  | RTC_POP_IGNORING_WUNDEF() | 
|  | #endif | 
|  |  | 
|  | namespace webrtc { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | #if WEBRTC_ENABLE_PROTOBUF | 
|  |  | 
|  | std::unique_ptr<FecControllerPlrBased> CreateFecControllerPlrBased( | 
|  | const audio_network_adaptor::config::FecController& config, | 
|  | bool initial_fec_enabled) { | 
|  | RTC_CHECK(config.has_fec_enabling_threshold()); | 
|  | RTC_CHECK(config.has_fec_disabling_threshold()); | 
|  | RTC_CHECK(config.has_time_constant_ms()); | 
|  |  | 
|  | auto& fec_enabling_threshold = config.fec_enabling_threshold(); | 
|  | RTC_CHECK(fec_enabling_threshold.has_low_bandwidth_bps()); | 
|  | RTC_CHECK(fec_enabling_threshold.has_low_bandwidth_packet_loss()); | 
|  | RTC_CHECK(fec_enabling_threshold.has_high_bandwidth_bps()); | 
|  | RTC_CHECK(fec_enabling_threshold.has_high_bandwidth_packet_loss()); | 
|  |  | 
|  | auto& fec_disabling_threshold = config.fec_disabling_threshold(); | 
|  | RTC_CHECK(fec_disabling_threshold.has_low_bandwidth_bps()); | 
|  | RTC_CHECK(fec_disabling_threshold.has_low_bandwidth_packet_loss()); | 
|  | RTC_CHECK(fec_disabling_threshold.has_high_bandwidth_bps()); | 
|  | RTC_CHECK(fec_disabling_threshold.has_high_bandwidth_packet_loss()); | 
|  |  | 
|  | return std::unique_ptr<FecControllerPlrBased>( | 
|  | new FecControllerPlrBased(FecControllerPlrBased::Config( | 
|  | initial_fec_enabled, | 
|  | ThresholdCurve(fec_enabling_threshold.low_bandwidth_bps(), | 
|  | fec_enabling_threshold.low_bandwidth_packet_loss(), | 
|  | fec_enabling_threshold.high_bandwidth_bps(), | 
|  | fec_enabling_threshold.high_bandwidth_packet_loss()), | 
|  | ThresholdCurve(fec_disabling_threshold.low_bandwidth_bps(), | 
|  | fec_disabling_threshold.low_bandwidth_packet_loss(), | 
|  | fec_disabling_threshold.high_bandwidth_bps(), | 
|  | fec_disabling_threshold.high_bandwidth_packet_loss()), | 
|  | config.time_constant_ms()))); | 
|  | } | 
|  |  | 
|  | std::unique_ptr<FrameLengthController> CreateFrameLengthController( | 
|  | const audio_network_adaptor::config::FrameLengthController& config, | 
|  | rtc::ArrayView<const int> encoder_frame_lengths_ms, | 
|  | int initial_frame_length_ms, | 
|  | int min_encoder_bitrate_bps) { | 
|  | RTC_CHECK(config.has_fl_increasing_packet_loss_fraction()); | 
|  | RTC_CHECK(config.has_fl_decreasing_packet_loss_fraction()); | 
|  |  | 
|  | std::map<FrameLengthController::Config::FrameLengthChange, int> | 
|  | fl_changing_bandwidths_bps; | 
|  |  | 
|  | if (config.has_fl_20ms_to_60ms_bandwidth_bps()) { | 
|  | fl_changing_bandwidths_bps.insert( | 
|  | std::make_pair(FrameLengthController::Config::FrameLengthChange(20, 60), | 
|  | config.fl_20ms_to_60ms_bandwidth_bps())); | 
|  | } | 
|  |  | 
|  | if (config.has_fl_60ms_to_20ms_bandwidth_bps()) { | 
|  | fl_changing_bandwidths_bps.insert( | 
|  | std::make_pair(FrameLengthController::Config::FrameLengthChange(60, 20), | 
|  | config.fl_60ms_to_20ms_bandwidth_bps())); | 
|  | } | 
|  |  | 
|  | if (config.has_fl_20ms_to_40ms_bandwidth_bps()) { | 
|  | fl_changing_bandwidths_bps.insert( | 
|  | std::make_pair(FrameLengthController::Config::FrameLengthChange(20, 40), | 
|  | config.fl_20ms_to_40ms_bandwidth_bps())); | 
|  | } | 
|  |  | 
|  | if (config.has_fl_40ms_to_20ms_bandwidth_bps()) { | 
|  | fl_changing_bandwidths_bps.insert( | 
|  | std::make_pair(FrameLengthController::Config::FrameLengthChange(40, 20), | 
|  | config.fl_40ms_to_20ms_bandwidth_bps())); | 
|  | } | 
|  |  | 
|  | if (config.has_fl_40ms_to_60ms_bandwidth_bps()) { | 
|  | fl_changing_bandwidths_bps.insert( | 
|  | std::make_pair(FrameLengthController::Config::FrameLengthChange(40, 60), | 
|  | config.fl_40ms_to_60ms_bandwidth_bps())); | 
|  | } | 
|  |  | 
|  | if (config.has_fl_60ms_to_40ms_bandwidth_bps()) { | 
|  | fl_changing_bandwidths_bps.insert( | 
|  | std::make_pair(FrameLengthController::Config::FrameLengthChange(60, 40), | 
|  | config.fl_60ms_to_40ms_bandwidth_bps())); | 
|  | } | 
|  |  | 
|  | if (config.has_fl_60ms_to_120ms_bandwidth_bps()) { | 
|  | fl_changing_bandwidths_bps.insert(std::make_pair( | 
|  | FrameLengthController::Config::FrameLengthChange(60, 120), | 
|  | config.fl_60ms_to_120ms_bandwidth_bps())); | 
|  | } | 
|  |  | 
|  | if (config.has_fl_120ms_to_60ms_bandwidth_bps()) { | 
|  | fl_changing_bandwidths_bps.insert(std::make_pair( | 
|  | FrameLengthController::Config::FrameLengthChange(120, 60), | 
|  | config.fl_120ms_to_60ms_bandwidth_bps())); | 
|  | } | 
|  |  | 
|  | int fl_increase_overhead_offset = 0; | 
|  | if (config.has_fl_increase_overhead_offset()) { | 
|  | fl_increase_overhead_offset = config.fl_increase_overhead_offset(); | 
|  | } | 
|  | int fl_decrease_overhead_offset = 0; | 
|  | if (config.has_fl_decrease_overhead_offset()) { | 
|  | fl_decrease_overhead_offset = config.fl_decrease_overhead_offset(); | 
|  | } | 
|  |  | 
|  | FrameLengthController::Config ctor_config( | 
|  | std::set<int>(), initial_frame_length_ms, min_encoder_bitrate_bps, | 
|  | config.fl_increasing_packet_loss_fraction(), | 
|  | config.fl_decreasing_packet_loss_fraction(), fl_increase_overhead_offset, | 
|  | fl_decrease_overhead_offset, std::move(fl_changing_bandwidths_bps)); | 
|  |  | 
|  | for (auto frame_length : encoder_frame_lengths_ms) | 
|  | ctor_config.encoder_frame_lengths_ms.insert(frame_length); | 
|  |  | 
|  | return std::unique_ptr<FrameLengthController>( | 
|  | new FrameLengthController(ctor_config)); | 
|  | } | 
|  |  | 
|  | std::unique_ptr<ChannelController> CreateChannelController( | 
|  | const audio_network_adaptor::config::ChannelController& config, | 
|  | size_t num_encoder_channels, | 
|  | size_t intial_channels_to_encode) { | 
|  | RTC_CHECK(config.has_channel_1_to_2_bandwidth_bps()); | 
|  | RTC_CHECK(config.has_channel_2_to_1_bandwidth_bps()); | 
|  |  | 
|  | return std::unique_ptr<ChannelController>(new ChannelController( | 
|  | ChannelController::Config(num_encoder_channels, intial_channels_to_encode, | 
|  | config.channel_1_to_2_bandwidth_bps(), | 
|  | config.channel_2_to_1_bandwidth_bps()))); | 
|  | } | 
|  |  | 
|  | std::unique_ptr<DtxController> CreateDtxController( | 
|  | const audio_network_adaptor::config::DtxController& dtx_config, | 
|  | bool initial_dtx_enabled) { | 
|  | RTC_CHECK(dtx_config.has_dtx_enabling_bandwidth_bps()); | 
|  | RTC_CHECK(dtx_config.has_dtx_disabling_bandwidth_bps()); | 
|  |  | 
|  | return std::unique_ptr<DtxController>(new DtxController(DtxController::Config( | 
|  | initial_dtx_enabled, dtx_config.dtx_enabling_bandwidth_bps(), | 
|  | dtx_config.dtx_disabling_bandwidth_bps()))); | 
|  | } | 
|  |  | 
|  | using audio_network_adaptor::BitrateController; | 
|  | std::unique_ptr<BitrateController> CreateBitrateController( | 
|  | const audio_network_adaptor::config::BitrateController& bitrate_config, | 
|  | int initial_bitrate_bps, | 
|  | int initial_frame_length_ms) { | 
|  | int fl_increase_overhead_offset = 0; | 
|  | if (bitrate_config.has_fl_increase_overhead_offset()) { | 
|  | fl_increase_overhead_offset = bitrate_config.fl_increase_overhead_offset(); | 
|  | } | 
|  | int fl_decrease_overhead_offset = 0; | 
|  | if (bitrate_config.has_fl_decrease_overhead_offset()) { | 
|  | fl_decrease_overhead_offset = bitrate_config.fl_decrease_overhead_offset(); | 
|  | } | 
|  | return std::unique_ptr<BitrateController>( | 
|  | new BitrateController(BitrateController::Config( | 
|  | initial_bitrate_bps, initial_frame_length_ms, | 
|  | fl_increase_overhead_offset, fl_decrease_overhead_offset))); | 
|  | } | 
|  |  | 
|  | std::unique_ptr<FrameLengthControllerV2> CreateFrameLengthControllerV2( | 
|  | const audio_network_adaptor::config::FrameLengthControllerV2& config, | 
|  | rtc::ArrayView<const int> encoder_frame_lengths_ms) { | 
|  | return std::make_unique<FrameLengthControllerV2>( | 
|  | encoder_frame_lengths_ms, config.min_payload_bitrate_bps(), | 
|  | config.use_slow_adaptation()); | 
|  | } | 
|  | #endif  // WEBRTC_ENABLE_PROTOBUF | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | ControllerManagerImpl::Config::Config(int min_reordering_time_ms, | 
|  | float min_reordering_squared_distance) | 
|  | : min_reordering_time_ms(min_reordering_time_ms), | 
|  | min_reordering_squared_distance(min_reordering_squared_distance) {} | 
|  |  | 
|  | ControllerManagerImpl::Config::~Config() = default; | 
|  |  | 
|  | std::unique_ptr<ControllerManager> ControllerManagerImpl::Create( | 
|  | const std::string& config_string, | 
|  | size_t num_encoder_channels, | 
|  | rtc::ArrayView<const int> encoder_frame_lengths_ms, | 
|  | int min_encoder_bitrate_bps, | 
|  | size_t intial_channels_to_encode, | 
|  | int initial_frame_length_ms, | 
|  | int initial_bitrate_bps, | 
|  | bool initial_fec_enabled, | 
|  | bool initial_dtx_enabled) { | 
|  | return Create(config_string, num_encoder_channels, encoder_frame_lengths_ms, | 
|  | min_encoder_bitrate_bps, intial_channels_to_encode, | 
|  | initial_frame_length_ms, initial_bitrate_bps, | 
|  | initial_fec_enabled, initial_dtx_enabled, nullptr); | 
|  | } | 
|  |  | 
|  | std::unique_ptr<ControllerManager> ControllerManagerImpl::Create( | 
|  | const std::string& config_string, | 
|  | size_t num_encoder_channels, | 
|  | rtc::ArrayView<const int> encoder_frame_lengths_ms, | 
|  | int min_encoder_bitrate_bps, | 
|  | size_t intial_channels_to_encode, | 
|  | int initial_frame_length_ms, | 
|  | int initial_bitrate_bps, | 
|  | bool initial_fec_enabled, | 
|  | bool initial_dtx_enabled, | 
|  | DebugDumpWriter* debug_dump_writer) { | 
|  | #if WEBRTC_ENABLE_PROTOBUF | 
|  | audio_network_adaptor::config::ControllerManager controller_manager_config; | 
|  | RTC_CHECK(controller_manager_config.ParseFromString(config_string)); | 
|  | if (debug_dump_writer) | 
|  | debug_dump_writer->DumpControllerManagerConfig(controller_manager_config, | 
|  | rtc::TimeMillis()); | 
|  |  | 
|  | std::vector<std::unique_ptr<Controller>> controllers; | 
|  | std::map<const Controller*, std::pair<int, float>> scoring_points; | 
|  |  | 
|  | for (int i = 0; i < controller_manager_config.controllers_size(); ++i) { | 
|  | auto& controller_config = controller_manager_config.controllers(i); | 
|  | std::unique_ptr<Controller> controller; | 
|  | switch (controller_config.controller_case()) { | 
|  | case audio_network_adaptor::config::Controller::kFecController: | 
|  | controller = CreateFecControllerPlrBased( | 
|  | controller_config.fec_controller(), initial_fec_enabled); | 
|  | break; | 
|  | case audio_network_adaptor::config::Controller::kFecControllerRplrBased: | 
|  | // FecControllerRplrBased has been removed and can't be used anymore. | 
|  | RTC_NOTREACHED(); | 
|  | continue; | 
|  | case audio_network_adaptor::config::Controller::kFrameLengthController: | 
|  | controller = CreateFrameLengthController( | 
|  | controller_config.frame_length_controller(), | 
|  | encoder_frame_lengths_ms, initial_frame_length_ms, | 
|  | min_encoder_bitrate_bps); | 
|  | break; | 
|  | case audio_network_adaptor::config::Controller::kChannelController: | 
|  | controller = CreateChannelController( | 
|  | controller_config.channel_controller(), num_encoder_channels, | 
|  | intial_channels_to_encode); | 
|  | break; | 
|  | case audio_network_adaptor::config::Controller::kDtxController: | 
|  | controller = CreateDtxController(controller_config.dtx_controller(), | 
|  | initial_dtx_enabled); | 
|  | break; | 
|  | case audio_network_adaptor::config::Controller::kBitrateController: | 
|  | controller = CreateBitrateController( | 
|  | controller_config.bitrate_controller(), initial_bitrate_bps, | 
|  | initial_frame_length_ms); | 
|  | break; | 
|  | case audio_network_adaptor::config::Controller::kFrameLengthControllerV2: | 
|  | controller = CreateFrameLengthControllerV2( | 
|  | controller_config.frame_length_controller_v2(), | 
|  | encoder_frame_lengths_ms); | 
|  | break; | 
|  | default: | 
|  | RTC_NOTREACHED(); | 
|  | } | 
|  | if (controller_config.has_scoring_point()) { | 
|  | auto& scoring_point = controller_config.scoring_point(); | 
|  | RTC_CHECK(scoring_point.has_uplink_bandwidth_bps()); | 
|  | RTC_CHECK(scoring_point.has_uplink_packet_loss_fraction()); | 
|  | scoring_points[controller.get()] = std::make_pair<int, float>( | 
|  | scoring_point.uplink_bandwidth_bps(), | 
|  | scoring_point.uplink_packet_loss_fraction()); | 
|  | } | 
|  | controllers.push_back(std::move(controller)); | 
|  | } | 
|  |  | 
|  | if (scoring_points.size() == 0) { | 
|  | return std::unique_ptr<ControllerManagerImpl>( | 
|  | new ControllerManagerImpl(ControllerManagerImpl::Config(0, 0), | 
|  | std::move(controllers), scoring_points)); | 
|  | } else { | 
|  | RTC_CHECK(controller_manager_config.has_min_reordering_time_ms()); | 
|  | RTC_CHECK(controller_manager_config.has_min_reordering_squared_distance()); | 
|  | return std::unique_ptr<ControllerManagerImpl>(new ControllerManagerImpl( | 
|  | ControllerManagerImpl::Config( | 
|  | controller_manager_config.min_reordering_time_ms(), | 
|  | controller_manager_config.min_reordering_squared_distance()), | 
|  | std::move(controllers), scoring_points)); | 
|  | } | 
|  |  | 
|  | #else | 
|  | RTC_NOTREACHED(); | 
|  | return nullptr; | 
|  | #endif  // WEBRTC_ENABLE_PROTOBUF | 
|  | } | 
|  |  | 
|  | ControllerManagerImpl::ControllerManagerImpl(const Config& config) | 
|  | : ControllerManagerImpl( | 
|  | config, | 
|  | std::vector<std::unique_ptr<Controller>>(), | 
|  | std::map<const Controller*, std::pair<int, float>>()) {} | 
|  |  | 
|  | ControllerManagerImpl::ControllerManagerImpl( | 
|  | const Config& config, | 
|  | std::vector<std::unique_ptr<Controller>> controllers, | 
|  | const std::map<const Controller*, std::pair<int, float>>& scoring_points) | 
|  | : config_(config), | 
|  | controllers_(std::move(controllers)), | 
|  | last_reordering_time_ms_(absl::nullopt), | 
|  | last_scoring_point_(0, 0.0) { | 
|  | for (auto& controller : controllers_) | 
|  | default_sorted_controllers_.push_back(controller.get()); | 
|  | sorted_controllers_ = default_sorted_controllers_; | 
|  | for (auto& controller_point : scoring_points) { | 
|  | controller_scoring_points_.insert(std::make_pair( | 
|  | controller_point.first, ScoringPoint(controller_point.second.first, | 
|  | controller_point.second.second))); | 
|  | } | 
|  | } | 
|  |  | 
|  | ControllerManagerImpl::~ControllerManagerImpl() = default; | 
|  |  | 
|  | std::vector<Controller*> ControllerManagerImpl::GetSortedControllers( | 
|  | const Controller::NetworkMetrics& metrics) { | 
|  | if (controller_scoring_points_.size() == 0) | 
|  | return default_sorted_controllers_; | 
|  |  | 
|  | if (!metrics.uplink_bandwidth_bps || !metrics.uplink_packet_loss_fraction) | 
|  | return sorted_controllers_; | 
|  |  | 
|  | const int64_t now_ms = rtc::TimeMillis(); | 
|  | if (last_reordering_time_ms_ && | 
|  | now_ms - *last_reordering_time_ms_ < config_.min_reordering_time_ms) | 
|  | return sorted_controllers_; | 
|  |  | 
|  | ScoringPoint scoring_point(*metrics.uplink_bandwidth_bps, | 
|  | *metrics.uplink_packet_loss_fraction); | 
|  |  | 
|  | if (last_reordering_time_ms_ && | 
|  | last_scoring_point_.SquaredDistanceTo(scoring_point) < | 
|  | config_.min_reordering_squared_distance) | 
|  | return sorted_controllers_; | 
|  |  | 
|  | // Sort controllers according to the distances of |scoring_point| to the | 
|  | // scoring points of controllers. | 
|  | // | 
|  | // A controller that does not associate with any scoring point | 
|  | // are treated as if | 
|  | // 1) they are less important than any controller that has a scoring point, | 
|  | // 2) they are equally important to any controller that has no scoring point, | 
|  | //    and their relative order will follow |default_sorted_controllers_|. | 
|  | std::vector<Controller*> sorted_controllers(default_sorted_controllers_); | 
|  | std::stable_sort( | 
|  | sorted_controllers.begin(), sorted_controllers.end(), | 
|  | [this, &scoring_point](const Controller* lhs, const Controller* rhs) { | 
|  | auto lhs_scoring_point = controller_scoring_points_.find(lhs); | 
|  | auto rhs_scoring_point = controller_scoring_points_.find(rhs); | 
|  |  | 
|  | if (lhs_scoring_point == controller_scoring_points_.end()) | 
|  | return false; | 
|  |  | 
|  | if (rhs_scoring_point == controller_scoring_points_.end()) | 
|  | return true; | 
|  |  | 
|  | return lhs_scoring_point->second.SquaredDistanceTo(scoring_point) < | 
|  | rhs_scoring_point->second.SquaredDistanceTo(scoring_point); | 
|  | }); | 
|  |  | 
|  | if (sorted_controllers_ != sorted_controllers) { | 
|  | sorted_controllers_ = sorted_controllers; | 
|  | last_reordering_time_ms_ = now_ms; | 
|  | last_scoring_point_ = scoring_point; | 
|  | } | 
|  | return sorted_controllers_; | 
|  | } | 
|  |  | 
|  | std::vector<Controller*> ControllerManagerImpl::GetControllers() const { | 
|  | return default_sorted_controllers_; | 
|  | } | 
|  |  | 
|  | ControllerManagerImpl::ScoringPoint::ScoringPoint( | 
|  | int uplink_bandwidth_bps, | 
|  | float uplink_packet_loss_fraction) | 
|  | : uplink_bandwidth_bps(uplink_bandwidth_bps), | 
|  | uplink_packet_loss_fraction(uplink_packet_loss_fraction) {} | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | constexpr int kMinUplinkBandwidthBps = 0; | 
|  | constexpr int kMaxUplinkBandwidthBps = 120000; | 
|  |  | 
|  | float NormalizeUplinkBandwidth(int uplink_bandwidth_bps) { | 
|  | uplink_bandwidth_bps = | 
|  | std::min(kMaxUplinkBandwidthBps, | 
|  | std::max(kMinUplinkBandwidthBps, uplink_bandwidth_bps)); | 
|  | return static_cast<float>(uplink_bandwidth_bps - kMinUplinkBandwidthBps) / | 
|  | (kMaxUplinkBandwidthBps - kMinUplinkBandwidthBps); | 
|  | } | 
|  |  | 
|  | float NormalizePacketLossFraction(float uplink_packet_loss_fraction) { | 
|  | // |uplink_packet_loss_fraction| is seldom larger than 0.3, so we scale it up | 
|  | // by 3.3333f. | 
|  | return std::min(uplink_packet_loss_fraction * 3.3333f, 1.0f); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | float ControllerManagerImpl::ScoringPoint::SquaredDistanceTo( | 
|  | const ScoringPoint& scoring_point) const { | 
|  | float diff_normalized_bitrate_bps = | 
|  | NormalizeUplinkBandwidth(scoring_point.uplink_bandwidth_bps) - | 
|  | NormalizeUplinkBandwidth(uplink_bandwidth_bps); | 
|  | float diff_normalized_packet_loss = | 
|  | NormalizePacketLossFraction(scoring_point.uplink_packet_loss_fraction) - | 
|  | NormalizePacketLossFraction(uplink_packet_loss_fraction); | 
|  | return std::pow(diff_normalized_bitrate_bps, 2) + | 
|  | std::pow(diff_normalized_packet_loss, 2); | 
|  | } | 
|  |  | 
|  | }  // namespace webrtc |