diff --git a/webrtc/modules/pacing/alr_detector.cc b/webrtc/modules/pacing/alr_detector.cc index 9eeca26012..5066e6ff79 100644 --- a/webrtc/modules/pacing/alr_detector.cc +++ b/webrtc/modules/pacing/alr_detector.cc @@ -10,28 +10,36 @@ #include "webrtc/modules/pacing/alr_detector.h" +#include + #include "webrtc/base/checks.h" +#include "webrtc/base/format_macros.h" #include "webrtc/base/logging.h" +#include "webrtc/system_wrappers/include/field_trial.h" namespace { - // Time period over which outgoing traffic is measured. constexpr int kMeasurementPeriodMs = 500; -// Sent traffic percentage as a function of network capacity used to determine -// application-limited region. ALR region start when bandwidth usage drops below -// kAlrStartUsagePercent and ends when it raises above kAlrEndUsagePercent. -// NOTE: This is intentionally conservative at the moment until BW adjustments -// of application limited region is fine tuned. -constexpr int kAlrStartUsagePercent = 60; -constexpr int kAlrEndUsagePercent = 70; - } // namespace namespace webrtc { +const char* AlrDetector::kScreenshareProbingBweExperimentName = + "WebRTC-ProbingScreenshareBwe"; + AlrDetector::AlrDetector() - : rate_(kMeasurementPeriodMs, RateStatistics::kBpsScale) {} + : alr_start_usage_percent_(kDefaultAlrStartUsagePercent), + alr_end_usage_percent_(kDefaultAlrEndUsagePercent), + rate_(kMeasurementPeriodMs, RateStatistics::kBpsScale), + estimated_bitrate_bps_(0) { + rtc::Optional experiment_settings = + ParseAlrSettingsFromFieldTrial(); + if (experiment_settings) { + alr_start_usage_percent_ = experiment_settings->alr_start_usage_percent; + alr_end_usage_percent_ = experiment_settings->alr_end_usage_percent; + } +} AlrDetector::~AlrDetector() {} @@ -44,9 +52,9 @@ void AlrDetector::OnBytesSent(size_t bytes_sent, int64_t now_ms) { return; int percentage = static_cast(*rate) * 100 / estimated_bitrate_bps_; - if (percentage < kAlrStartUsagePercent && !alr_started_time_ms_) { + if (percentage < alr_start_usage_percent_ && !alr_started_time_ms_) { alr_started_time_ms_ = rtc::Optional(now_ms); - } else if (percentage > kAlrEndUsagePercent && alr_started_time_ms_) { + } else if (percentage > alr_end_usage_percent_ && alr_started_time_ms_) { alr_started_time_ms_ = rtc::Optional(); } } @@ -61,4 +69,38 @@ rtc::Optional AlrDetector::GetApplicationLimitedRegionStartTime() return alr_started_time_ms_; } +rtc::Optional +AlrDetector::ParseAlrSettingsFromFieldTrial() { + rtc::Optional ret; + std::string group_name = + field_trial::FindFullName(kScreenshareProbingBweExperimentName); + + const std::string kIgnoredSuffix = "_Dogfood"; + if (group_name.rfind(kIgnoredSuffix) == + group_name.length() - kIgnoredSuffix.length()) { + group_name.resize(group_name.length() - kIgnoredSuffix.length()); + } + + if (group_name.empty()) + return ret; + + AlrExperimentSettings settings; + if (sscanf(group_name.c_str(), "%f-%" PRId64 "-%d-%d", + &settings.pacing_factor, &settings.max_paced_queue_time, + &settings.alr_start_usage_percent, + &settings.alr_end_usage_percent) == 4) { + ret.emplace(settings); + LOG(LS_INFO) << "Using screenshare ALR experiment settings: " + "pacing factor: " + << settings.pacing_factor << ", max pacer queue length: " + << settings.max_paced_queue_time + << ", ALR start usage percent: " + << settings.alr_start_usage_percent + << ", ALR end usage percent: " + << settings.alr_end_usage_percent; + } + + return ret; +} + } // namespace webrtc diff --git a/webrtc/modules/pacing/alr_detector.h b/webrtc/modules/pacing/alr_detector.h index d8870ff04b..087e444da1 100644 --- a/webrtc/modules/pacing/alr_detector.h +++ b/webrtc/modules/pacing/alr_detector.h @@ -11,6 +11,7 @@ #ifndef WEBRTC_MODULES_PACING_ALR_DETECTOR_H_ #define WEBRTC_MODULES_PACING_ALR_DETECTOR_H_ +#include "webrtc/base/optional.h" #include "webrtc/base/rate_statistics.h" #include "webrtc/common_types.h" #include "webrtc/modules/pacing/paced_sender.h" @@ -25,6 +26,7 @@ namespace webrtc { // AlrDetector provides a signal that can be utilized to adjust // estimate bandwidth. // Note: This class is not thread-safe. + class AlrDetector { public: AlrDetector(); @@ -39,9 +41,28 @@ class AlrDetector { // started or empty result if the sender is currently not application-limited. rtc::Optional GetApplicationLimitedRegionStartTime() const; + struct AlrExperimentSettings { + float pacing_factor = PacedSender::kDefaultPaceMultiplier; + int64_t max_paced_queue_time = PacedSender::kMaxQueueLengthMs; + int alr_start_usage_percent = kDefaultAlrStartUsagePercent; + int alr_end_usage_percent = kDefaultAlrEndUsagePercent; + }; + static rtc::Optional ParseAlrSettingsFromFieldTrial(); + + // Sent traffic percentage as a function of network capacity used to determine + // application-limited region. ALR region start when bandwidth usage drops + // below kAlrStartUsagePercent and ends when it raises above + // kAlrEndUsagePercent. NOTE: This is intentionally conservative at the moment + // until BW adjustments of application limited region is fine tuned. + static constexpr int kDefaultAlrStartUsagePercent = 60; + static constexpr int kDefaultAlrEndUsagePercent = 70; + static const char* kScreenshareProbingBweExperimentName; + private: + int alr_start_usage_percent_; + int alr_end_usage_percent_; RateStatistics rate_; - int estimated_bitrate_bps_ = 0; + int estimated_bitrate_bps_; // Non-empty in ALR state. rtc::Optional alr_started_time_ms_; diff --git a/webrtc/modules/pacing/paced_sender.cc b/webrtc/modules/pacing/paced_sender.cc index 7c24301b95..913dbfc274 100644 --- a/webrtc/modules/pacing/paced_sender.cc +++ b/webrtc/modules/pacing/paced_sender.cc @@ -264,7 +264,9 @@ PacedSender::PacedSender(const Clock* clock, time_last_update_us_(clock->TimeInMicroseconds()), first_sent_packet_ms_(-1), packets_(new paced_sender::PacketQueue(clock)), - packet_counter_(0) { + packet_counter_(0), + pacing_factor_(kDefaultPaceMultiplier), + queue_time_limit(kMaxQueueLengthMs) { UpdateBudgetWithElapsedTime(kMinPacketLimitMs); } @@ -314,7 +316,7 @@ void PacedSender::SetEstimatedBitrate(uint32_t bitrate_bps) { std::min(estimated_bitrate_bps_ / 1000, max_padding_bitrate_kbps_)); pacing_bitrate_kbps_ = std::max(min_send_bitrate_kbps_, estimated_bitrate_bps_ / 1000) * - kDefaultPaceMultiplier; + pacing_factor_; alr_detector_->SetEstimatedBitrate(bitrate_bps); } @@ -324,7 +326,7 @@ void PacedSender::SetSendBitrateLimits(int min_send_bitrate_bps, min_send_bitrate_kbps_ = min_send_bitrate_bps / 1000; pacing_bitrate_kbps_ = std::max(min_send_bitrate_kbps_, estimated_bitrate_bps_ / 1000) * - kDefaultPaceMultiplier; + pacing_factor_; max_padding_bitrate_kbps_ = padding_bitrate / 1000; padding_budget_->set_target_rate_kbps( std::min(estimated_bitrate_bps_ / 1000, max_padding_bitrate_kbps_)); @@ -419,7 +421,7 @@ void PacedSender::Process() { // time constraint shall be met. Determine bitrate needed for that. packets_->UpdateQueueTime(clock_->TimeInMilliseconds()); int64_t avg_time_left_ms = std::max( - 1, kMaxQueueLengthMs - packets_->AverageQueueTimeMs()); + 1, queue_time_limit - packets_->AverageQueueTimeMs()); int min_bitrate_needed_kbps = static_cast(queue_size_bytes * 8 / avg_time_left_ms); if (min_bitrate_needed_kbps > target_bitrate_kbps) @@ -535,4 +537,15 @@ void PacedSender::UpdateBudgetWithBytesSent(size_t bytes_sent) { media_budget_->UseBudget(bytes_sent); padding_budget_->UseBudget(bytes_sent); } + +void PacedSender::SetPacingFactor(float pacing_factor) { + rtc::CritScope cs(&critsect_); + pacing_factor_ = pacing_factor; +} + +void PacedSender::SetQueueTimeLimit(int limit_ms) { + rtc::CritScope cs(&critsect_); + queue_time_limit = limit_ms; +} + } // namespace webrtc diff --git a/webrtc/modules/pacing/paced_sender.h b/webrtc/modules/pacing/paced_sender.h index e04fafdbb4..be1318d42b 100644 --- a/webrtc/modules/pacing/paced_sender.h +++ b/webrtc/modules/pacing/paced_sender.h @@ -149,6 +149,9 @@ class PacedSender : public Module, public RtpPacketSender { // Called when the prober is associated with a process thread. void ProcessThreadAttached(ProcessThread* process_thread) override; + void SetPacingFactor(float pacing_factor); + void SetQueueTimeLimit(int limit_ms); + private: // Updates the number of bytes that can be sent for the next time interval. void UpdateBudgetWithElapsedTime(int64_t delta_time_in_ms) @@ -193,6 +196,9 @@ class PacedSender : public Module, public RtpPacketSender { std::unique_ptr packets_ GUARDED_BY(critsect_); uint64_t packet_counter_; ProcessThread* process_thread_ = nullptr; + + float pacing_factor_ GUARDED_BY(critsect_); + int64_t queue_time_limit GUARDED_BY(critsect_); }; } // namespace webrtc #endif // WEBRTC_MODULES_PACING_PACED_SENDER_H_ diff --git a/webrtc/video/BUILD.gn b/webrtc/video/BUILD.gn index 2204c74a93..5faf28048e 100644 --- a/webrtc/video/BUILD.gn +++ b/webrtc/video/BUILD.gn @@ -133,6 +133,7 @@ if (rtc_include_tests) { ] deps = [ ":video_quality_test", + "../modules/pacing:pacing", "../test:field_trial", "../test:test_support", "//testing/gtest", diff --git a/webrtc/video/full_stack_tests.cc b/webrtc/video/full_stack_tests.cc index 8299771379..4a7334fe1e 100644 --- a/webrtc/video/full_stack_tests.cc +++ b/webrtc/video/full_stack_tests.cc @@ -9,19 +9,29 @@ */ #include +#include "webrtc/modules/pacing/alr_detector.h" #include "webrtc/test/field_trial.h" #include "webrtc/test/gtest.h" #include "webrtc/video/video_quality_test.h" namespace webrtc { +namespace { static const int kFullStackTestDurationSecs = 45; +} // namespace class FullStackTest : public VideoQualityTest { public: void RunTest(const VideoQualityTest::Params ¶ms) { RunWithAnalyzer(params); } + + protected: + const std::string kScreenshareSimulcastExperiment = + "WebRTC-SimulcastScreenshare/Enabled/"; + const std::string kAlrProbingExperiment = + std::string(AlrDetector::kScreenshareProbingBweExperimentName) + + "/1.1-2875-80-90/"; }; // VideoQualityTest::Params params = { @@ -310,7 +320,7 @@ TEST_F(FullStackTest, ScreenshareSlidesVP8_2TL) { } TEST_F(FullStackTest, ScreenshareSlidesVP8_3TL_Simulcast) { - test::ScopedFieldTrials field_trial("WebRTC-SimulcastScreenshare/Enabled/"); + test::ScopedFieldTrials field_trial(kScreenshareSimulcastExperiment); VideoQualityTest::Params screenshare; screenshare.call.send_side_bwe = true; screenshare.screenshare = {true, 10}; @@ -389,6 +399,93 @@ TEST_F(FullStackTest, ScreenshareSlidesVP8_2TL_LossyNetRestrictedQueue) { RunTest(screenshare); } +TEST_F(FullStackTest, ScreenshareSlidesVP8_2TL_ModeratelyRestricted) { + VideoQualityTest::Params screenshare; + screenshare.call.send_side_bwe = true; + screenshare.video = {true, 1850, 1110, 5, 50000, 200000, 2000000, false, + "VP8", 2, 1, 400000, false, false, "", ""}; + screenshare.screenshare = {true, 10}; + screenshare.analyzer = {"screenshare_slides_moderately_restricted", 0.0, 0.0, + kFullStackTestDurationSecs}; + screenshare.pipe.loss_percent = 1; + screenshare.pipe.link_capacity_kbps = 1200; + screenshare.pipe.queue_length_packets = 30; + + RunTest(screenshare); +} + +// TODO(sprang): Retire these tests once experiment is removed. +TEST_F(FullStackTest, ScreenshareSlidesVP8_2TL_LossyNetRestrictedQueue_ALR) { + test::ScopedFieldTrials field_trial(kAlrProbingExperiment); + VideoQualityTest::Params screenshare; + screenshare.call.send_side_bwe = true; + screenshare.video = {true, 1850, 1110, 5, 50000, 200000, 2000000, false, + "VP8", 2, 1, 400000, false, false, "", ""}; + screenshare.screenshare = {true, 10}; + screenshare.analyzer = {"screenshare_slides_lossy_limited_ALR", 0.0, 0.0, + kFullStackTestDurationSecs}; + screenshare.pipe.loss_percent = 5; + screenshare.pipe.link_capacity_kbps = 200; + screenshare.pipe.queue_length_packets = 30; + + RunTest(screenshare); +} + +TEST_F(FullStackTest, ScreenshareSlidesVP8_2TL_ALR) { + test::ScopedFieldTrials field_trial(kAlrProbingExperiment); + VideoQualityTest::Params screenshare; + screenshare.call.send_side_bwe = true; + screenshare.video = {true, 1850, 1110, 5, 50000, 200000, 2000000, false, + "VP8", 2, 1, 400000, false, false, "", ""}; + screenshare.screenshare = {true, 10}; + screenshare.analyzer = {"screenshare_slides_ALR", 0.0, 0.0, + kFullStackTestDurationSecs}; + RunTest(screenshare); +} + +TEST_F(FullStackTest, ScreenshareSlidesVP8_2TL_ModeratelyRestricted_ALR) { + test::ScopedFieldTrials field_trial(kAlrProbingExperiment); + VideoQualityTest::Params screenshare; + screenshare.call.send_side_bwe = true; + screenshare.video = {true, 1850, 1110, 5, 50000, 200000, 2000000, false, + "VP8", 2, 1, 400000, false, false, "", ""}; + screenshare.screenshare = {true, 10}; + screenshare.analyzer = {"screenshare_slides_moderately_restricted_ALR", 0.0, + 0.0, kFullStackTestDurationSecs}; + screenshare.pipe.loss_percent = 1; + screenshare.pipe.link_capacity_kbps = 1200; + screenshare.pipe.queue_length_packets = 30; + + RunTest(screenshare); +} + +TEST_F(FullStackTest, ScreenshareSlidesVP8_3TL_Simulcast_ALR) { + test::ScopedFieldTrials field_trial(kScreenshareSimulcastExperiment + + kAlrProbingExperiment); + VideoQualityTest::Params screenshare; + screenshare.call.send_side_bwe = true; + screenshare.screenshare = {true, 10}; + screenshare.video = {true, 1850, 1110, 5, 800000, 2500000, + 2500000, false, "VP8", 3, 2, 400000, + false, false, "", ""}; + screenshare.analyzer = {"screenshare_slides_simulcast_alr", 0.0, 0.0, + kFullStackTestDurationSecs}; + VideoQualityTest::Params screenshare_params_high; + screenshare_params_high.video = {true, 1850, 1110, 5, 800000, 2500000, + 2500000, false, "VP8", 3, 0, 400000, + false, false, "", ""}; + VideoQualityTest::Params screenshare_params_low; + screenshare_params_low.video = {true, 1850, 1110, 5, 50000, 200000, + 2000000, false, "VP8", 2, 0, 400000, + false, false, "", ""}; + + std::vector streams = { + DefaultVideoStream(screenshare_params_low), + DefaultVideoStream(screenshare_params_high)}; + screenshare.ss = {streams, 1, 1, 0, std::vector(), false}; + RunTest(screenshare); +} + const VideoQualityTest::Params::Video kSvcVp9Video = { true, 1280, 720, 30, 800000, 2500000, 2500000, false, diff --git a/webrtc/video/screenshare_loopback.cc b/webrtc/video/screenshare_loopback.cc index c0638c9b1a..4212f35a67 100644 --- a/webrtc/video/screenshare_loopback.cc +++ b/webrtc/video/screenshare_loopback.cc @@ -312,6 +312,7 @@ int main(int argc, char* argv[]) { // with a scope that outlives the test. std::string field_trials = webrtc::flags::FLAG_force_fieldtrials; webrtc::test::InitFieldTrialsFromString(field_trials); + webrtc::test::RunTest(webrtc::Loopback); return 0; } diff --git a/webrtc/video/video_loopback.cc b/webrtc/video/video_loopback.cc index 66bafbb6f0..dc8de56ade 100644 --- a/webrtc/video/video_loopback.cc +++ b/webrtc/video/video_loopback.cc @@ -318,6 +318,7 @@ int main(int argc, char* argv[]) { // with a scope that outlives the test. std::string field_trials = webrtc::flags::FLAG_force_fieldtrials; webrtc::test::InitFieldTrialsFromString(field_trials); + webrtc::test::RunTest(webrtc::Loopback); return 0; } diff --git a/webrtc/video/video_send_stream.cc b/webrtc/video/video_send_stream.cc index 285b7ef033..f4c82f49c1 100644 --- a/webrtc/video/video_send_stream.cc +++ b/webrtc/video/video_send_stream.cc @@ -27,6 +27,7 @@ #include "webrtc/common_video/include/video_bitrate_allocator.h" #include "webrtc/modules/bitrate_controller/include/bitrate_controller.h" #include "webrtc/modules/congestion_controller/include/send_side_congestion_controller.h" +#include "webrtc/modules/pacing/alr_detector.h" #include "webrtc/modules/pacing/packet_router.h" #include "webrtc/modules/rtp_rtcp/include/rtp_rtcp.h" #include "webrtc/modules/rtp_rtcp/source/rtp_sender.h" @@ -345,7 +346,8 @@ class VideoSendStreamImpl : public webrtc::BitrateAllocatorObserver, RtcEventLog* event_log, const VideoSendStream::Config* config, int initial_encoder_max_bitrate, - std::map suspended_ssrcs); + std::map suspended_ssrcs, + VideoEncoderConfig::ContentType content_type); ~VideoSendStreamImpl() override; // RegisterProcessThread register |module_process_thread| with those objects @@ -475,7 +477,8 @@ class VideoSendStream::ConstructionTask : public rtc::QueuedTask { RtcEventLog* event_log, const VideoSendStream::Config* config, int initial_encoder_max_bitrate, - const std::map& suspended_ssrcs) + const std::map& suspended_ssrcs, + VideoEncoderConfig::ContentType content_type) : send_stream_(send_stream), done_event_(done_event), stats_proxy_(stats_proxy), @@ -487,7 +490,8 @@ class VideoSendStream::ConstructionTask : public rtc::QueuedTask { event_log_(event_log), config_(config), initial_encoder_max_bitrate_(initial_encoder_max_bitrate), - suspended_ssrcs_(suspended_ssrcs) {} + suspended_ssrcs_(suspended_ssrcs), + content_type_(content_type) {} ~ConstructionTask() override { done_event_->Set(); } @@ -496,7 +500,8 @@ class VideoSendStream::ConstructionTask : public rtc::QueuedTask { send_stream_->reset(new VideoSendStreamImpl( stats_proxy_, rtc::TaskQueue::Current(), call_stats_, transport_, bitrate_allocator_, send_delay_stats_, vie_encoder_, event_log_, - config_, initial_encoder_max_bitrate_, std::move(suspended_ssrcs_))); + config_, initial_encoder_max_bitrate_, std::move(suspended_ssrcs_), + content_type_)); return true; } @@ -512,6 +517,7 @@ class VideoSendStream::ConstructionTask : public rtc::QueuedTask { const VideoSendStream::Config* config_; int initial_encoder_max_bitrate_; std::map suspended_ssrcs_; + const VideoEncoderConfig::ContentType content_type_; }; class VideoSendStream::DestructAndGetRtpStateTask : public rtc::QueuedTask { @@ -639,8 +645,8 @@ VideoSendStream::VideoSendStream( worker_queue_->PostTask(std::unique_ptr(new ConstructionTask( &send_stream_, &thread_sync_event_, &stats_proxy_, vie_encoder_.get(), module_process_thread, call_stats, transport, bitrate_allocator, - send_delay_stats, event_log, &config_, - encoder_config.max_bitrate_bps, suspended_ssrcs))); + send_delay_stats, event_log, &config_, encoder_config.max_bitrate_bps, + suspended_ssrcs, encoder_config.content_type))); // Wait for ConstructionTask to complete so that |send_stream_| can be used. // |module_process_thread| must be registered and deregistered on the thread @@ -758,7 +764,8 @@ VideoSendStreamImpl::VideoSendStreamImpl( RtcEventLog* event_log, const VideoSendStream::Config* config, int initial_encoder_max_bitrate, - std::map suspended_ssrcs) + std::map suspended_ssrcs, + VideoEncoderConfig::ContentType content_type) : send_side_bwe_with_overhead_( webrtc::field_trial::IsEnabled("WebRTC-SendSideBwe-WithOverhead")), stats_proxy_(stats_proxy), @@ -811,8 +818,21 @@ VideoSendStreamImpl::VideoSendStreamImpl( RTC_DCHECK(transport_); RTC_DCHECK(transport_->send_side_cc()); - transport->send_side_cc()->EnablePeriodicAlrProbing( - config_->periodic_alr_bandwidth_probing); + if (content_type == VideoEncoderConfig::ContentType::kScreen) { + rtc::Optional alr_settings = + AlrDetector::ParseAlrSettingsFromFieldTrial(); + if (alr_settings) { + transport->send_side_cc()->EnablePeriodicAlrProbing(true); + transport->send_side_cc()->pacer()->SetPacingFactor( + alr_settings->pacing_factor); + transport->send_side_cc()->pacer()->SetQueueTimeLimit( + alr_settings->max_paced_queue_time); + } + } + + if (config_->periodic_alr_bandwidth_probing) { + transport->send_side_cc()->EnablePeriodicAlrProbing(true); + } // RTP/RTCP initialization.