diff --git a/call/call_perf_tests.cc b/call/call_perf_tests.cc index a6fe0b27ad..88ac2e8d3f 100644 --- a/call/call_perf_tests.cc +++ b/call/call_perf_tests.cc @@ -20,6 +20,7 @@ #include "modules/audio_coding/include/audio_coding_module.h" #include "modules/audio_mixer/audio_mixer_impl.h" #include "modules/rtp_rtcp/include/rtp_header_parser.h" +#include "rtc_base/bitrateallocationstrategy.h" #include "rtc_base/checks.h" #include "rtc_base/ptr_util.h" #include "rtc_base/thread_annotations.h" @@ -66,6 +67,13 @@ class CallPerfTest : public test::CallTest { int threshold_ms, int start_time_ms, int run_time_ms); + void TestMinAudioVideoBitrate(bool use_bitrate_allocation_strategy, + int test_bitrate_from, + int test_bitrate_to, + int test_bitrate_step, + int min_bwe, + int start_bwe, + int max_bwe); }; class VideoRtcpAndSyncObserver : public test::RtpRtcpObserver, @@ -790,4 +798,166 @@ TEST_F(CallPerfTest, KeepsHighBitrateWhenReconfiguringSender) { RunBaseTest(&test); } +// Discovers the minimal supported audio+video bitrate. The test bitrate is +// considered supported if Rtt does not go above 400ms with the network +// contrained to the test bitrate. +// +// |use_bitrate_allocation_strategy| use AudioPriorityBitrateAllocationStrategy +// |test_bitrate_from test_bitrate_to| bitrate constraint range +// |test_bitrate_step| bitrate constraint update step during the test +// |min_bwe max_bwe| BWE range +// |start_bwe| initial BWE +void CallPerfTest::TestMinAudioVideoBitrate( + bool use_bitrate_allocation_strategy, + int test_bitrate_from, + int test_bitrate_to, + int test_bitrate_step, + int min_bwe, + int start_bwe, + int max_bwe) { + static const std::string kAudioTrackId = "audio_track_0"; + static constexpr uint32_t kSufficientAudioBitrateBps = 16000; + static constexpr int kOpusMinBitrateBps = 6000; + static constexpr int kOpusBitrateFbBps = 32000; + static constexpr int kBitrateStabilizationMs = 10000; + static constexpr int kBitrateMeasurements = 10; + static constexpr int kBitrateMeasurementMs = 1000; + static constexpr int kMinGoodRttMs = 400; + + class MinVideoAndAudioBitrateTester : public test::EndToEndTest { + public: + MinVideoAndAudioBitrateTester(bool use_bitrate_allocation_strategy, + int test_bitrate_from, + int test_bitrate_to, + int test_bitrate_step, + int min_bwe, + int start_bwe, + int max_bwe) + : EndToEndTest(), + allocation_strategy_(new rtc::AudioPriorityBitrateAllocationStrategy( + kAudioTrackId, + kSufficientAudioBitrateBps)), + use_bitrate_allocation_strategy_(use_bitrate_allocation_strategy), + test_bitrate_from_(test_bitrate_from), + test_bitrate_to_(test_bitrate_to), + test_bitrate_step_(test_bitrate_step), + min_bwe_(min_bwe), + start_bwe_(start_bwe), + max_bwe_(max_bwe) {} + + protected: + FakeNetworkPipe::Config GetFakeNetworkPipeConfig() { + FakeNetworkPipe::Config pipe_config; + pipe_config.link_capacity_kbps = test_bitrate_from_; + return pipe_config; + } + + test::PacketTransport* CreateSendTransport( + test::SingleThreadedTaskQueueForTesting* task_queue, + Call* sender_call) override { + return send_transport_ = new test::PacketTransport( + task_queue, sender_call, this, test::PacketTransport::kSender, + test::CallTest::payload_type_map_, GetFakeNetworkPipeConfig()); + } + + test::PacketTransport* CreateReceiveTransport( + test::SingleThreadedTaskQueueForTesting* task_queue) override { + return receive_transport_ = new test::PacketTransport( + task_queue, nullptr, this, test::PacketTransport::kReceiver, + test::CallTest::payload_type_map_, GetFakeNetworkPipeConfig()); + } + + void PerformTest() override { + int last_passed_test_bitrate = -1; + for (int test_bitrate = test_bitrate_from_; + test_bitrate_from_ < test_bitrate_to_ + ? test_bitrate <= test_bitrate_to_ + : test_bitrate >= test_bitrate_to_; + test_bitrate += test_bitrate_step_) { + FakeNetworkPipe::Config pipe_config; + pipe_config.link_capacity_kbps = test_bitrate; + send_transport_->SetConfig(pipe_config); + receive_transport_->SetConfig(pipe_config); + + rtc::ThreadManager::Instance()->CurrentThread()->SleepMs( + kBitrateStabilizationMs); + + int64_t avg_rtt = 0; + for (int i = 0; i < kBitrateMeasurements; i++) { + Call::Stats call_stats = sender_call_->GetStats(); + avg_rtt += call_stats.rtt_ms; + rtc::ThreadManager::Instance()->CurrentThread()->SleepMs( + kBitrateMeasurementMs); + } + avg_rtt = avg_rtt / kBitrateMeasurements; + if (avg_rtt > kMinGoodRttMs) { + break; + } else { + last_passed_test_bitrate = test_bitrate; + } + } + EXPECT_GT(last_passed_test_bitrate, -1) + << "Minimum supported bitrate out of the test scope"; + webrtc::test::PrintResult("min_test_bitrate_", + use_bitrate_allocation_strategy_ + ? "with_allocation_strategy" + : "no_allocation_strategy", + "", last_passed_test_bitrate, "kbps", false); + } + + void OnCallsCreated(Call* sender_call, Call* receiver_call) override { + sender_call_ = sender_call; + Call::Config::BitrateConfig bitrate_config; + bitrate_config.min_bitrate_bps = min_bwe_; + bitrate_config.start_bitrate_bps = start_bwe_; + bitrate_config.max_bitrate_bps = max_bwe_; + sender_call->SetBitrateConfig(bitrate_config); + if (use_bitrate_allocation_strategy_) { + sender_call->SetBitrateAllocationStrategy( + std::move(allocation_strategy_)); + } + } + + size_t GetNumVideoStreams() const override { return 1; } + + size_t GetNumAudioStreams() const override { return 1; } + + void ModifyAudioConfigs( + AudioSendStream::Config* send_config, + std::vector* receive_configs) override { + if (use_bitrate_allocation_strategy_) { + send_config->track_id = kAudioTrackId; + send_config->min_bitrate_bps = kOpusMinBitrateBps; + send_config->max_bitrate_bps = kOpusBitrateFbBps; + } else { + send_config->send_codec_spec->target_bitrate_bps = + rtc::Optional(kOpusBitrateFbBps); + } + } + + private: + std::unique_ptr allocation_strategy_; + const bool use_bitrate_allocation_strategy_; + const int test_bitrate_from_; + const int test_bitrate_to_; + const int test_bitrate_step_; + const int min_bwe_; + const int start_bwe_; + const int max_bwe_; + test::PacketTransport* send_transport_; + test::PacketTransport* receive_transport_; + Call* sender_call_; + } test(use_bitrate_allocation_strategy, test_bitrate_from, test_bitrate_to, + test_bitrate_step, min_bwe, start_bwe, max_bwe); + + RunBaseTest(&test); +} + +TEST_F(CallPerfTest, MinVideoAndAudioBitrate) { + TestMinAudioVideoBitrate(false, 110, 40, -10, 10000, 70000, 200000); +} +TEST_F(CallPerfTest, MinVideoAndAudioBitrateWStrategy) { + TestMinAudioVideoBitrate(true, 110, 40, -10, 10000, 70000, 200000); +} + } // namespace webrtc diff --git a/rtc_base/BUILD.gn b/rtc_base/BUILD.gn index 873a3eb00b..ae4101c29a 100644 --- a/rtc_base/BUILD.gn +++ b/rtc_base/BUILD.gn @@ -860,6 +860,7 @@ if (rtc_include_tests) { "basictypes_unittest.cc", "bind_unittest.cc", "bitbuffer_unittest.cc", + "bitrateallocationstrategy_unittest.cc", "buffer_unittest.cc", "bufferqueue_unittest.cc", "bytebuffer_unittest.cc", diff --git a/rtc_base/bitrateallocationstrategy_unittest.cc b/rtc_base/bitrateallocationstrategy_unittest.cc new file mode 100644 index 0000000000..bfc41f56e8 --- /dev/null +++ b/rtc_base/bitrateallocationstrategy_unittest.cc @@ -0,0 +1,244 @@ +/* + * Copyright 2017 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 "rtc_base/bitrateallocationstrategy.h" +#include "rtc_base/gunit.h" + +namespace rtc { + +std::vector +MakeTrackConfigPtrsVector( + const std::vector& track_configs) { + std::vector + track_config_ptrs(track_configs.size()); + int i = 0; + for (const auto& c : track_configs) { + track_config_ptrs[i++] = &c; + } + return track_config_ptrs; +} + +TEST(BitrateAllocationStrategyTest, SetAllBitratesToMinimum) { + const std::string audio_track_id = "audio_track"; + constexpr uint32_t min_audio_bitrate = 6000; + constexpr uint32_t max_audio_bitrate = 64000; + const std::string video_track_id = "video_track"; + constexpr uint32_t min_video_bitrate = 30000; + constexpr uint32_t max_video_bitrate = 300000; + constexpr uint32_t min_other_bitrate = 3000; + constexpr uint32_t max_other_bitrate = 30000; + + std::vector track_configs = { + BitrateAllocationStrategy::TrackConfig( + min_audio_bitrate, max_audio_bitrate, false, audio_track_id), + BitrateAllocationStrategy::TrackConfig( + min_video_bitrate, max_video_bitrate, false, video_track_id), + BitrateAllocationStrategy::TrackConfig(min_other_bitrate, + max_other_bitrate, false, "")}; + + std::vector + track_config_ptrs = MakeTrackConfigPtrsVector(track_configs); + + std::vector allocations = + BitrateAllocationStrategy::SetAllBitratesToMinimum(track_config_ptrs); + EXPECT_EQ(min_audio_bitrate, allocations[0]); + EXPECT_EQ(min_video_bitrate, allocations[1]); + EXPECT_EQ(min_other_bitrate, allocations[2]); +} + +TEST(BitrateAllocationStrategyTest, DistributeBitratesEvenly) { + const std::string audio_track_id = "audio_track"; + constexpr uint32_t min_audio_bitrate = 16000; + constexpr uint32_t max_audio_bitrate = 64000; + const std::string video_track_id = "video_track"; + constexpr uint32_t min_video_bitrate = 30000; + constexpr uint32_t max_video_bitrate = 300000; + constexpr uint32_t min_other_bitrate = 3000; + constexpr uint32_t max_other_bitrate = 30000; + constexpr uint32_t available_bitrate = 52000; + constexpr uint32_t even_bitrate_increase = + (available_bitrate - min_audio_bitrate - min_video_bitrate - + min_other_bitrate) / + 3; + + std::vector track_configs = { + BitrateAllocationStrategy::TrackConfig( + min_audio_bitrate, max_audio_bitrate, false, audio_track_id), + BitrateAllocationStrategy::TrackConfig( + min_video_bitrate, max_video_bitrate, false, video_track_id), + BitrateAllocationStrategy::TrackConfig(min_other_bitrate, + max_other_bitrate, false, "")}; + + std::vector + track_config_ptrs = MakeTrackConfigPtrsVector(track_configs); + + std::vector allocations = + BitrateAllocationStrategy::DistributeBitratesEvenly(track_config_ptrs, + available_bitrate); + EXPECT_EQ(min_audio_bitrate + even_bitrate_increase, allocations[0]); + EXPECT_EQ(min_video_bitrate + even_bitrate_increase, allocations[1]); + EXPECT_EQ(min_other_bitrate + even_bitrate_increase, allocations[2]); +} + +std::vector RunAudioPriorityAllocation( + uint32_t sufficient_audio_bitrate, + std::string audio_track_id, + uint32_t min_audio_bitrate, + uint32_t max_audio_bitrate, + std::string video_track_id, + uint32_t min_video_bitrate, + uint32_t max_video_bitrate, + uint32_t min_other_bitrate, + uint32_t max_other_bitrate, + uint32_t available_bitrate) { + AudioPriorityBitrateAllocationStrategy allocation_strategy( + audio_track_id, sufficient_audio_bitrate); + std::vector track_configs = { + BitrateAllocationStrategy::TrackConfig( + min_audio_bitrate, max_audio_bitrate, false, audio_track_id), + BitrateAllocationStrategy::TrackConfig( + min_video_bitrate, max_video_bitrate, false, video_track_id), + BitrateAllocationStrategy::TrackConfig(min_other_bitrate, + max_other_bitrate, false, "")}; + + std::vector + track_config_ptrs = MakeTrackConfigPtrsVector(track_configs); + + return allocation_strategy.AllocateBitrates(available_bitrate, + track_config_ptrs); +} + +// Test that when the available bitrate is less than the sum of the minimum +// bitrates, the minimum bitrate is allocated for each track. +TEST(AudioPriorityBitrateAllocationStrategyTest, MinAllocateBitrate) { + constexpr uint32_t sufficient_audio_bitrate = 16000; + const std::string audio_track_id = "audio_track"; + constexpr uint32_t min_audio_bitrate = 6000; + constexpr uint32_t max_audio_bitrate = 64000; + const std::string video_track_id = "video_track"; + constexpr uint32_t min_video_bitrate = 30000; + constexpr uint32_t max_video_bitrate = 300000; + constexpr uint32_t min_other_bitrate = 3000; + constexpr uint32_t max_other_bitrate = 30000; + constexpr uint32_t available_bitrate = 10000; + + std::vector allocations = RunAudioPriorityAllocation( + sufficient_audio_bitrate, audio_track_id, min_audio_bitrate, + max_audio_bitrate, video_track_id, min_video_bitrate, max_video_bitrate, + min_other_bitrate, max_other_bitrate, available_bitrate); + EXPECT_EQ(min_audio_bitrate, allocations[0]); + EXPECT_EQ(min_video_bitrate, allocations[1]); + EXPECT_EQ(min_other_bitrate, allocations[2]); +} + +// Test that when the available bitrate is more than the sum of the max +// bitrates, the max bitrate is allocated for each track. +TEST(AudioPriorityBitrateAllocationStrategyTest, MaxAllocateBitrate) { + constexpr uint32_t sufficient_audio_bitrate = 16000; + const std::string audio_track_id = "audio_track"; + constexpr uint32_t min_audio_bitrate = 6000; + constexpr uint32_t max_audio_bitrate = 64000; + const std::string video_track_id = "video_track"; + constexpr uint32_t min_video_bitrate = 30000; + constexpr uint32_t max_video_bitrate = 300000; + constexpr uint32_t min_other_bitrate = 3000; + constexpr uint32_t max_other_bitrate = 30000; + constexpr uint32_t available_bitrate = 400000; + + std::vector allocations = RunAudioPriorityAllocation( + sufficient_audio_bitrate, audio_track_id, min_audio_bitrate, + max_audio_bitrate, video_track_id, min_video_bitrate, max_video_bitrate, + min_other_bitrate, max_other_bitrate, available_bitrate); + + // TODO(bugs.webrtc.org/8541): Until the bug is fixed not audio streams will + // get up to kTransmissionMaxBitrateMultiplier*max_bitrate + constexpr uint32_t video_bitrate = + (available_bitrate - max_audio_bitrate - max_other_bitrate * 2); + EXPECT_EQ(max_audio_bitrate, allocations[0]); + EXPECT_EQ(video_bitrate, allocations[1]); + EXPECT_EQ(max_other_bitrate * 2, allocations[2]); +} + +// Test that audio track will get up to sufficient bitrate before video and +// other bitrate will be allocated. +TEST(AudioPriorityBitrateAllocationStrategyTest, AudioPriorityAllocateBitrate) { + constexpr uint32_t sufficient_audio_bitrate = 16000; + const std::string audio_track_id = "audio_track"; + constexpr uint32_t min_audio_bitrate = 6000; + constexpr uint32_t max_audio_bitrate = 64000; + const std::string video_track_id = "video_track"; + constexpr uint32_t min_video_bitrate = 30000; + constexpr uint32_t max_video_bitrate = 300000; + constexpr uint32_t min_other_bitrate = 3000; + constexpr uint32_t max_other_bitrate = 30000; + constexpr uint32_t available_bitrate = 49000; + + std::vector allocations = RunAudioPriorityAllocation( + sufficient_audio_bitrate, audio_track_id, min_audio_bitrate, + max_audio_bitrate, video_track_id, min_video_bitrate, max_video_bitrate, + min_other_bitrate, max_other_bitrate, available_bitrate); + EXPECT_EQ(sufficient_audio_bitrate, allocations[0]); + EXPECT_EQ(min_video_bitrate, allocations[1]); + EXPECT_EQ(min_other_bitrate, allocations[2]); +} + +// Test that bitrate will be allocated evenly after sufficient audio bitrate is +// allocated. +TEST(AudioPriorityBitrateAllocationStrategyTest, EvenAllocateBitrate) { + constexpr uint32_t sufficient_audio_bitrate = 16000; + const std::string audio_track_id = "audio_track"; + constexpr uint32_t min_audio_bitrate = 6000; + constexpr uint32_t max_audio_bitrate = 64000; + const std::string video_track_id = "video_track"; + constexpr uint32_t min_video_bitrate = 30000; + constexpr uint32_t max_video_bitrate = 300000; + constexpr uint32_t min_other_bitrate = 3000; + constexpr uint32_t max_other_bitrate = 30000; + constexpr uint32_t available_bitrate = 52000; + constexpr uint32_t even_bitrate_increase = + (available_bitrate - sufficient_audio_bitrate - min_video_bitrate - + min_other_bitrate) / + 3; + + std::vector allocations = RunAudioPriorityAllocation( + sufficient_audio_bitrate, audio_track_id, min_audio_bitrate, + max_audio_bitrate, video_track_id, min_video_bitrate, max_video_bitrate, + min_other_bitrate, max_other_bitrate, available_bitrate); + EXPECT_EQ(sufficient_audio_bitrate + even_bitrate_increase, allocations[0]); + EXPECT_EQ(min_video_bitrate + even_bitrate_increase, allocations[1]); + EXPECT_EQ(min_other_bitrate + even_bitrate_increase, allocations[2]); +} + +// Test that bitrate will be allocated to video after audio and other max +// allocation. +TEST(AudioPriorityBitrateAllocationStrategyTest, VideoAllocateBitrate) { + constexpr uint32_t sufficient_audio_bitrate = 16000; + const std::string audio_track_id = "audio_track"; + constexpr uint32_t min_audio_bitrate = 6000; + constexpr uint32_t max_audio_bitrate = 64000; + const std::string video_track_id = "video_track"; + constexpr uint32_t min_video_bitrate = 30000; + constexpr uint32_t max_video_bitrate = 300000; + constexpr uint32_t min_other_bitrate = 3000; + constexpr uint32_t max_other_bitrate = 30000; + constexpr uint32_t available_bitrate = 200000; + constexpr uint32_t video_bitrate = + available_bitrate - max_audio_bitrate - max_other_bitrate; + + std::vector allocations = RunAudioPriorityAllocation( + sufficient_audio_bitrate, audio_track_id, min_audio_bitrate, + max_audio_bitrate, video_track_id, min_video_bitrate, max_video_bitrate, + min_other_bitrate, max_other_bitrate, available_bitrate); + EXPECT_EQ(max_audio_bitrate, allocations[0]); + EXPECT_EQ(video_bitrate, allocations[1]); + EXPECT_EQ(max_other_bitrate, allocations[2]); +} + +} // namespace rtc