| /* |
| * 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 "absl/strings/string_view.h" |
| #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/logging.h" |
| #include "rtc_base/time_utils.h" |
| |
| #if WEBRTC_ENABLE_PROTOBUF |
| #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 |
| |
| #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( |
| absl::string_view 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( |
| absl::string_view 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(std::string(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_DCHECK_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_DCHECK_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_DCHECK_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 |