From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../video/end_to_end_tests/bandwidth_tests.cc | 402 +++++++++++++++++++++ 1 file changed, 402 insertions(+) create mode 100644 third_party/libwebrtc/video/end_to_end_tests/bandwidth_tests.cc (limited to 'third_party/libwebrtc/video/end_to_end_tests/bandwidth_tests.cc') diff --git a/third_party/libwebrtc/video/end_to_end_tests/bandwidth_tests.cc b/third_party/libwebrtc/video/end_to_end_tests/bandwidth_tests.cc new file mode 100644 index 0000000000..d6610a8ec2 --- /dev/null +++ b/third_party/libwebrtc/video/end_to_end_tests/bandwidth_tests.cc @@ -0,0 +1,402 @@ +/* + * Copyright 2018 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 + +#include "api/task_queue/task_queue_base.h" +#include "api/test/simulated_network.h" +#include "api/units/time_delta.h" +#include "api/video/builtin_video_bitrate_allocator_factory.h" +#include "api/video/video_bitrate_allocation.h" +#include "call/fake_network_pipe.h" +#include "call/simulated_network.h" +#include "modules/rtp_rtcp/source/rtp_rtcp_impl2.h" +#include "rtc_base/rate_limiter.h" +#include "rtc_base/synchronization/mutex.h" +#include "rtc_base/task_queue_for_test.h" +#include "system_wrappers/include/sleep.h" +#include "test/call_test.h" +#include "test/fake_encoder.h" +#include "test/field_trial.h" +#include "test/gtest.h" +#include "test/rtcp_packet_parser.h" +#include "test/rtp_rtcp_observer.h" +#include "test/video_encoder_proxy_factory.h" + +namespace webrtc { +namespace { +enum : int { // The first valid value is 1. + kAbsSendTimeExtensionId = 1, + kTransportSequenceNumberId, +}; +} // namespace + +class BandwidthEndToEndTest : public test::CallTest { + public: + BandwidthEndToEndTest() = default; +}; + +TEST_F(BandwidthEndToEndTest, ReceiveStreamSendsRemb) { + class RembObserver : public test::EndToEndTest { + public: + RembObserver() : EndToEndTest(kDefaultTimeout) {} + + void ModifyVideoConfigs( + VideoSendStream::Config* send_config, + std::vector* receive_configs, + VideoEncoderConfig* encoder_config) override { + send_config->rtp.extensions.clear(); + send_config->rtp.extensions.push_back( + RtpExtension(RtpExtension::kAbsSendTimeUri, kAbsSendTimeExtensionId)); + } + + Action OnReceiveRtcp(const uint8_t* packet, size_t length) override { + test::RtcpPacketParser parser; + EXPECT_TRUE(parser.Parse(packet, length)); + + if (parser.remb()->num_packets() > 0) { + EXPECT_EQ(kReceiverLocalVideoSsrc, parser.remb()->sender_ssrc()); + EXPECT_LT(0U, parser.remb()->bitrate_bps()); + EXPECT_EQ(1U, parser.remb()->ssrcs().size()); + EXPECT_EQ(kVideoSendSsrcs[0], parser.remb()->ssrcs()[0]); + observation_complete_.Set(); + } + + return SEND_PACKET; + } + void PerformTest() override { + EXPECT_TRUE(Wait()) << "Timed out while waiting for a " + "receiver RTCP REMB packet to be " + "sent."; + } + } test; + + RunBaseTest(&test); +} + +class BandwidthStatsTest : public test::EndToEndTest { + public: + BandwidthStatsTest(bool send_side_bwe, TaskQueueBase* task_queue) + : EndToEndTest(test::CallTest::kDefaultTimeout), + sender_call_(nullptr), + receiver_call_(nullptr), + has_seen_pacer_delay_(false), + send_side_bwe_(send_side_bwe), + task_queue_(task_queue) {} + + ~BandwidthStatsTest() override { + // Block until all already posted tasks run to avoid races when such task + // accesses `this`. + SendTask(task_queue_, [] {}); + } + + void ModifyVideoConfigs( + VideoSendStream::Config* send_config, + std::vector* receive_configs, + VideoEncoderConfig* encoder_config) override { + send_config->rtp.extensions.clear(); + if (!send_side_bwe_) { + send_config->rtp.extensions.push_back( + RtpExtension(RtpExtension::kAbsSendTimeUri, kAbsSendTimeExtensionId)); + } else { + send_config->rtp.extensions.push_back( + RtpExtension(RtpExtension::kTransportSequenceNumberUri, + kTransportSequenceNumberId)); + } + + // Force a too high encoder bitrate to make sure we get pacer delay. + encoder_config->number_of_streams = 1; + encoder_config->max_bitrate_bps = kMaxBitrateBps * 2; + encoder_config->simulcast_layers[0].min_bitrate_bps = kMaxBitrateBps * 2; + encoder_config->simulcast_layers[0].target_bitrate_bps = kMaxBitrateBps * 2; + encoder_config->simulcast_layers[0].max_bitrate_bps = kMaxBitrateBps * 2; + } + + void ModifySenderBitrateConfig(BitrateConstraints* bitrate_config) override { + bitrate_config->max_bitrate_bps = kMaxBitrateBps; + } + + // Called on the pacer thread. + Action OnSendRtp(const uint8_t* packet, size_t length) override { + // Stats need to be fetched on the thread where the caller objects were + // constructed. + task_queue_->PostTask([this]() { + if (!sender_call_ || !receiver_call_) { + return; + } + + Call::Stats sender_stats = sender_call_->GetStats(); + if (!has_seen_pacer_delay_) { + has_seen_pacer_delay_ = sender_stats.pacer_delay_ms > 0; + } + + if (sender_stats.send_bandwidth_bps > 0 && has_seen_pacer_delay_) { + Call::Stats receiver_stats = receiver_call_->GetStats(); + if (send_side_bwe_ || receiver_stats.recv_bandwidth_bps > 0) { + observation_complete_.Set(); + } + } + }); + + return SEND_PACKET; + } + + void OnCallsCreated(Call* sender_call, Call* receiver_call) override { + sender_call_ = sender_call; + receiver_call_ = receiver_call; + } + + void OnStreamsStopped() override { + sender_call_ = nullptr; + receiver_call_ = nullptr; + } + + void PerformTest() override { + EXPECT_TRUE(Wait()) << "Timed out while waiting for " + "non-zero bandwidth stats."; + } + + private: + static const int kMaxBitrateBps = 3000000; + Call* sender_call_; + Call* receiver_call_; + bool has_seen_pacer_delay_; + const bool send_side_bwe_; + TaskQueueBase* const task_queue_; +}; + +TEST_F(BandwidthEndToEndTest, VerifySendSideBweStats) { + BandwidthStatsTest test(true, task_queue()); + RunBaseTest(&test); +} + +TEST_F(BandwidthEndToEndTest, VerifyRecvSideBweStats) { + BandwidthStatsTest test(false, task_queue()); + RunBaseTest(&test); +} + +// Verifies that it's possible to limit the send BWE by sending a REMB. +// This is verified by allowing the send BWE to ramp-up to >1000 kbps, +// then have the test generate a REMB of 500 kbps and verify that the send BWE +// is reduced to exactly 500 kbps. Then a REMB of 1000 kbps is generated and the +// test verifies that the send BWE ramps back up to exactly 1000 kbps. +TEST_F(BandwidthEndToEndTest, RembWithSendSideBwe) { + class BweObserver : public test::EndToEndTest { + public: + explicit BweObserver(TaskQueueBase* task_queue) + : EndToEndTest(kDefaultTimeout), + sender_call_(nullptr), + clock_(Clock::GetRealTimeClock()), + sender_ssrc_(0), + remb_bitrate_bps_(1000000), + state_(kWaitForFirstRampUp), + retransmission_rate_limiter_(clock_, 1000), + task_queue_(task_queue) {} + + void OnStreamsStopped() override { rtp_rtcp_ = nullptr; } + + void ModifySenderBitrateConfig( + BitrateConstraints* bitrate_config) override { + // Set a high start bitrate to reduce the test completion time. + bitrate_config->start_bitrate_bps = remb_bitrate_bps_; + } + + void ModifyVideoConfigs( + VideoSendStream::Config* send_config, + std::vector* receive_configs, + VideoEncoderConfig* encoder_config) override { + ASSERT_EQ(1u, send_config->rtp.ssrcs.size()); + sender_ssrc_ = send_config->rtp.ssrcs[0]; + + encoder_config->max_bitrate_bps = 2000000; + + ASSERT_EQ(1u, receive_configs->size()); + remb_sender_local_ssrc_ = (*receive_configs)[0].rtp.local_ssrc; + remb_sender_remote_ssrc_ = (*receive_configs)[0].rtp.remote_ssrc; + } + + void OnCallsCreated(Call* sender_call, Call* receiver_call) override { + RTC_DCHECK(sender_call); + sender_call_ = sender_call; + task_queue_->PostTask([this]() { PollStats(); }); + } + + void OnTransportCreated( + test::PacketTransport* /*to_receiver*/, + SimulatedNetworkInterface* /*sender_network*/, + test::PacketTransport* to_sender, + SimulatedNetworkInterface* /*receiver_network*/) override { + RtpRtcpInterface::Configuration config; + config.receiver_only = true; + config.clock = clock_; + config.outgoing_transport = to_sender; + config.retransmission_rate_limiter = &retransmission_rate_limiter_; + config.local_media_ssrc = remb_sender_local_ssrc_; + + rtp_rtcp_ = ModuleRtpRtcpImpl2::Create(config); + rtp_rtcp_->SetRemoteSSRC(remb_sender_remote_ssrc_); + rtp_rtcp_->SetRTCPStatus(RtcpMode::kReducedSize); + } + + void PollStats() { + Call::Stats stats = sender_call_->GetStats(); + switch (state_) { + case kWaitForFirstRampUp: + if (stats.send_bandwidth_bps >= remb_bitrate_bps_) { + state_ = kWaitForRemb; + remb_bitrate_bps_ /= 2; + rtp_rtcp_->SetRemb( + remb_bitrate_bps_, + std::vector(&sender_ssrc_, &sender_ssrc_ + 1)); + rtp_rtcp_->SendRTCP(kRtcpRr); + } + break; + + case kWaitForRemb: + if (stats.send_bandwidth_bps == remb_bitrate_bps_) { + state_ = kWaitForSecondRampUp; + remb_bitrate_bps_ *= 2; + rtp_rtcp_->SetRemb( + remb_bitrate_bps_, + std::vector(&sender_ssrc_, &sender_ssrc_ + 1)); + rtp_rtcp_->SendRTCP(kRtcpRr); + } + break; + + case kWaitForSecondRampUp: + if (stats.send_bandwidth_bps == remb_bitrate_bps_) { + observation_complete_.Set(); + return; + } + break; + } + + task_queue_->PostDelayedTask([this] { PollStats(); }, + TimeDelta::Seconds(1)); + } + + void PerformTest() override { + EXPECT_TRUE(Wait()) + << "Timed out while waiting for bitrate to change according to REMB."; + } + + private: + enum TestState { kWaitForFirstRampUp, kWaitForRemb, kWaitForSecondRampUp }; + + Call* sender_call_; + Clock* const clock_; + uint32_t sender_ssrc_; + uint32_t remb_sender_local_ssrc_ = 0; + uint32_t remb_sender_remote_ssrc_ = 0; + int remb_bitrate_bps_; + std::unique_ptr rtp_rtcp_; + TestState state_; + RateLimiter retransmission_rate_limiter_; + TaskQueueBase* const task_queue_; + } test(task_queue()); + + RunBaseTest(&test); +} + +TEST_F(BandwidthEndToEndTest, ReportsSetEncoderRates) { + // If these fields trial are on, we get lower bitrates than expected by this + // test, due to the packetization overhead and encoder pushback. + webrtc::test::ScopedFieldTrials field_trials( + std::string(field_trial::GetFieldTrialString()) + + "WebRTC-VideoRateControl/bitrate_adjuster:false/"); + class EncoderRateStatsTest : public test::EndToEndTest, + public test::FakeEncoder { + public: + explicit EncoderRateStatsTest(TaskQueueBase* task_queue) + : EndToEndTest(kDefaultTimeout), + FakeEncoder(Clock::GetRealTimeClock()), + task_queue_(task_queue), + send_stream_(nullptr), + encoder_factory_(this), + bitrate_allocator_factory_( + CreateBuiltinVideoBitrateAllocatorFactory()), + bitrate_kbps_(0) {} + + void OnVideoStreamsCreated(VideoSendStream* send_stream, + const std::vector& + receive_streams) override { + send_stream_ = send_stream; + } + + void ModifyVideoConfigs( + VideoSendStream::Config* send_config, + std::vector* receive_configs, + VideoEncoderConfig* encoder_config) override { + send_config->encoder_settings.encoder_factory = &encoder_factory_; + send_config->encoder_settings.bitrate_allocator_factory = + bitrate_allocator_factory_.get(); + RTC_DCHECK_EQ(1, encoder_config->number_of_streams); + } + + void SetRates(const RateControlParameters& parameters) override { + // Make sure not to trigger on any default zero bitrates. + if (parameters.bitrate.get_sum_bps() == 0) + return; + MutexLock lock(&mutex_); + bitrate_kbps_ = parameters.bitrate.get_sum_kbps(); + observation_complete_.Set(); + } + + void PerformTest() override { + ASSERT_TRUE(Wait()) + << "Timed out while waiting for encoder SetRates() call."; + + SendTask(task_queue_, [this]() { + WaitForEncoderTargetBitrateMatchStats(); + send_stream_->Stop(); + WaitForStatsReportZeroTargetBitrate(); + send_stream_->Start(); + WaitForEncoderTargetBitrateMatchStats(); + }); + } + + void WaitForEncoderTargetBitrateMatchStats() { + for (int i = 0; i < kDefaultTimeout.ms(); ++i) { + VideoSendStream::Stats stats = send_stream_->GetStats(); + { + MutexLock lock(&mutex_); + if ((stats.target_media_bitrate_bps + 500) / 1000 == + static_cast(bitrate_kbps_)) { + return; + } + } + SleepMs(1); + } + FAIL() + << "Timed out waiting for stats reporting the currently set bitrate."; + } + + void WaitForStatsReportZeroTargetBitrate() { + for (int i = 0; i < kDefaultTimeout.ms(); ++i) { + if (send_stream_->GetStats().target_media_bitrate_bps == 0) { + return; + } + SleepMs(1); + } + FAIL() << "Timed out waiting for stats reporting zero bitrate."; + } + + private: + TaskQueueBase* const task_queue_; + Mutex mutex_; + VideoSendStream* send_stream_; + test::VideoEncoderProxyFactory encoder_factory_; + std::unique_ptr bitrate_allocator_factory_; + uint32_t bitrate_kbps_ RTC_GUARDED_BY(mutex_); + } test(task_queue()); + + RunBaseTest(&test); +} +} // namespace webrtc -- cgit v1.2.3