summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/modules/video_coding/utility/simulcast_rate_allocator_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/libwebrtc/modules/video_coding/utility/simulcast_rate_allocator_unittest.cc')
-rw-r--r--third_party/libwebrtc/modules/video_coding/utility/simulcast_rate_allocator_unittest.cc824
1 files changed, 824 insertions, 0 deletions
diff --git a/third_party/libwebrtc/modules/video_coding/utility/simulcast_rate_allocator_unittest.cc b/third_party/libwebrtc/modules/video_coding/utility/simulcast_rate_allocator_unittest.cc
new file mode 100644
index 0000000000..24d7c58bcd
--- /dev/null
+++ b/third_party/libwebrtc/modules/video_coding/utility/simulcast_rate_allocator_unittest.cc
@@ -0,0 +1,824 @@
+/*
+ * 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/video_coding/utility/simulcast_rate_allocator.h"
+
+#include <limits>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "api/video_codecs/vp8_frame_buffer_controller.h"
+#include "api/video_codecs/vp8_frame_config.h"
+#include "api/video_codecs/vp8_temporal_layers.h"
+#include "rtc_base/checks.h"
+#include "test/field_trial.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace {
+using ::testing::_;
+
+constexpr uint32_t kFramerateFps = 5;
+constexpr uint32_t kMinBitrateKbps = 50;
+// These correspond to kLegacyScreenshareTl(0|1)BitrateKbps in cc.
+constexpr uint32_t kLegacyScreenshareTargetBitrateKbps = 200;
+constexpr uint32_t kLegacyScreenshareMaxBitrateKbps = 1000;
+// Bitrates for upper simulcast screenshare layer.
+constexpr uint32_t kSimulcastScreenshareMinBitrateKbps = 600;
+constexpr uint32_t kSimulcastScreenshareMaxBitrateKbps = 1250;
+// Default video hysteresis factor: allocatable bitrate for next layer must
+// exceed 20% of min setting in order to be initially turned on.
+const double kDefaultHysteresis = 1.2;
+
+class MockTemporalLayers : public Vp8FrameBufferController {
+ public:
+ MOCK_METHOD(Vp8FrameConfig, NextFrameConfig, (size_t, uint32_t), (override));
+ MOCK_METHOD(void,
+ OnRatesUpdated,
+ (size_t, const std::vector<uint32_t>&, int),
+ (override));
+ MOCK_METHOD(Vp8EncoderConfig, UpdateConfiguration, (size_t), (override));
+ MOCK_METHOD(void,
+ OnEncodeDone,
+ (size_t, uint32_t, size_t, bool, int, CodecSpecificInfo*),
+ (override));
+};
+} // namespace
+
+class SimulcastRateAllocatorTest : public ::testing::TestWithParam<bool> {
+ public:
+ SimulcastRateAllocatorTest() {
+ codec_.codecType = kVideoCodecVP8;
+ codec_.minBitrate = kMinBitrateKbps;
+ codec_.maxBitrate = kLegacyScreenshareMaxBitrateKbps;
+ codec_.active = true;
+ CreateAllocator();
+ }
+ virtual ~SimulcastRateAllocatorTest() {}
+
+ template <size_t S>
+ void ExpectEqual(uint32_t (&expected)[S],
+ const std::vector<uint32_t>& actual) {
+ EXPECT_EQ(S, actual.size());
+ for (size_t i = 0; i < S; ++i)
+ EXPECT_EQ(expected[i], actual[i]) << "Mismatch at index " << i;
+ }
+
+ template <size_t S>
+ void ExpectEqual(uint32_t (&expected)[S],
+ const VideoBitrateAllocation& actual) {
+ // EXPECT_EQ(S, actual.size());
+ uint32_t sum = 0;
+ for (size_t i = 0; i < S; ++i) {
+ uint32_t layer_bitrate = actual.GetSpatialLayerSum(i);
+ if (layer_bitrate == 0) {
+ EXPECT_FALSE(actual.IsSpatialLayerUsed(i));
+ }
+ EXPECT_EQ(expected[i] * 1000U, layer_bitrate)
+ << "Mismatch at index " << i;
+ sum += layer_bitrate;
+ }
+ EXPECT_EQ(sum, actual.get_sum_bps());
+ }
+
+ void CreateAllocator(bool legacy_conference_mode = false) {
+ allocator_.reset(new SimulcastRateAllocator(codec_));
+ allocator_->SetLegacyConferenceMode(legacy_conference_mode);
+ }
+
+ void SetupCodec3SL3TL(const std::vector<bool>& active_streams) {
+ const size_t num_simulcast_layers = 3;
+ RTC_DCHECK_GE(active_streams.size(), num_simulcast_layers);
+ SetupCodec2SL3TL(active_streams);
+ codec_.numberOfSimulcastStreams = num_simulcast_layers;
+ codec_.simulcastStream[2].numberOfTemporalLayers = 3;
+ codec_.simulcastStream[2].maxBitrate = 4000;
+ codec_.simulcastStream[2].targetBitrate = 3000;
+ codec_.simulcastStream[2].minBitrate = 2000;
+ codec_.simulcastStream[2].active = active_streams[2];
+ }
+
+ void SetupCodec2SL3TL(const std::vector<bool>& active_streams) {
+ const size_t num_simulcast_layers = 2;
+ RTC_DCHECK_GE(active_streams.size(), num_simulcast_layers);
+ SetupCodec1SL3TL(active_streams);
+ codec_.numberOfSimulcastStreams = num_simulcast_layers;
+ codec_.simulcastStream[1].numberOfTemporalLayers = 3;
+ codec_.simulcastStream[1].maxBitrate = 1000;
+ codec_.simulcastStream[1].targetBitrate = 500;
+ codec_.simulcastStream[1].minBitrate = 50;
+ codec_.simulcastStream[1].active = active_streams[1];
+ }
+
+ void SetupCodec1SL3TL(const std::vector<bool>& active_streams) {
+ const size_t num_simulcast_layers = 2;
+ RTC_DCHECK_GE(active_streams.size(), num_simulcast_layers);
+ SetupCodec3TL();
+ codec_.numberOfSimulcastStreams = num_simulcast_layers;
+ codec_.simulcastStream[0].numberOfTemporalLayers = 3;
+ codec_.simulcastStream[0].maxBitrate = 500;
+ codec_.simulcastStream[0].targetBitrate = 100;
+ codec_.simulcastStream[0].minBitrate = 10;
+ codec_.simulcastStream[0].active = active_streams[0];
+ }
+
+ void SetupCodec3TL() {
+ codec_.maxBitrate = 0;
+ codec_.VP8()->numberOfTemporalLayers = 3;
+ }
+
+ VideoBitrateAllocation GetAllocation(uint32_t target_bitrate) {
+ return allocator_->Allocate(VideoBitrateAllocationParameters(
+ DataRate::KilobitsPerSec(target_bitrate), kDefaultFrameRate));
+ }
+
+ VideoBitrateAllocation GetAllocation(DataRate target_rate,
+ DataRate stable_rate) {
+ return allocator_->Allocate(VideoBitrateAllocationParameters(
+ target_rate, stable_rate, kDefaultFrameRate));
+ }
+
+ DataRate MinRate(size_t layer_index) const {
+ return DataRate::KilobitsPerSec(
+ codec_.simulcastStream[layer_index].minBitrate);
+ }
+
+ DataRate TargetRate(size_t layer_index) const {
+ return DataRate::KilobitsPerSec(
+ codec_.simulcastStream[layer_index].targetBitrate);
+ }
+
+ DataRate MaxRate(size_t layer_index) const {
+ return DataRate::KilobitsPerSec(
+ codec_.simulcastStream[layer_index].maxBitrate);
+ }
+
+ protected:
+ static const int kDefaultFrameRate = 30;
+ VideoCodec codec_;
+ std::unique_ptr<SimulcastRateAllocator> allocator_;
+};
+
+TEST_F(SimulcastRateAllocatorTest, NoSimulcastBelowMin) {
+ uint32_t expected[] = {codec_.minBitrate};
+ codec_.active = true;
+ ExpectEqual(expected, GetAllocation(codec_.minBitrate - 1));
+ ExpectEqual(expected, GetAllocation(1));
+ ExpectEqual(expected, GetAllocation(0));
+}
+
+TEST_F(SimulcastRateAllocatorTest, NoSimulcastAboveMax) {
+ uint32_t expected[] = {codec_.maxBitrate};
+ codec_.active = true;
+ ExpectEqual(expected, GetAllocation(codec_.maxBitrate + 1));
+ ExpectEqual(expected, GetAllocation(std::numeric_limits<uint32_t>::max()));
+}
+
+TEST_F(SimulcastRateAllocatorTest, NoSimulcastNoMax) {
+ const uint32_t kMax = VideoBitrateAllocation::kMaxBitrateBps / 1000;
+ codec_.active = true;
+ codec_.maxBitrate = 0;
+ CreateAllocator();
+
+ uint32_t expected[] = {kMax};
+ ExpectEqual(expected, GetAllocation(kMax));
+}
+
+TEST_F(SimulcastRateAllocatorTest, NoSimulcastWithinLimits) {
+ codec_.active = true;
+ for (uint32_t bitrate = codec_.minBitrate; bitrate <= codec_.maxBitrate;
+ ++bitrate) {
+ uint32_t expected[] = {bitrate};
+ ExpectEqual(expected, GetAllocation(bitrate));
+ }
+}
+
+// Tests that when we aren't using simulcast and the codec is marked inactive no
+// bitrate will be allocated.
+TEST_F(SimulcastRateAllocatorTest, NoSimulcastInactive) {
+ codec_.active = false;
+ uint32_t expected[] = {0};
+ CreateAllocator();
+
+ ExpectEqual(expected, GetAllocation(kMinBitrateKbps - 10));
+ ExpectEqual(expected, GetAllocation(kLegacyScreenshareTargetBitrateKbps));
+ ExpectEqual(expected, GetAllocation(kLegacyScreenshareMaxBitrateKbps + 10));
+}
+
+TEST_F(SimulcastRateAllocatorTest, SingleSimulcastBelowMin) {
+ // With simulcast, use the min bitrate from the ss spec instead of the global.
+ codec_.numberOfSimulcastStreams = 1;
+ const uint32_t kMin = codec_.minBitrate - 10;
+ codec_.simulcastStream[0].minBitrate = kMin;
+ codec_.simulcastStream[0].targetBitrate = kLegacyScreenshareTargetBitrateKbps;
+ codec_.simulcastStream[0].active = true;
+ CreateAllocator();
+
+ uint32_t expected[] = {kMin};
+ ExpectEqual(expected, GetAllocation(kMin - 1));
+ ExpectEqual(expected, GetAllocation(1));
+ ExpectEqual(expected, GetAllocation(0));
+}
+
+TEST_F(SimulcastRateAllocatorTest, SignalsBwLimited) {
+ // Enough to enable all layers.
+ const int kVeryBigBitrate = 100000;
+
+ // With simulcast, use the min bitrate from the ss spec instead of the global.
+ SetupCodec3SL3TL({true, true, true});
+ CreateAllocator();
+
+ EXPECT_TRUE(
+ GetAllocation(codec_.simulcastStream[0].minBitrate - 10).is_bw_limited());
+ EXPECT_TRUE(
+ GetAllocation(codec_.simulcastStream[0].targetBitrate).is_bw_limited());
+ EXPECT_TRUE(GetAllocation(codec_.simulcastStream[0].targetBitrate +
+ codec_.simulcastStream[1].minBitrate)
+ .is_bw_limited());
+ EXPECT_FALSE(
+ GetAllocation(
+ codec_.simulcastStream[0].targetBitrate +
+ codec_.simulcastStream[1].targetBitrate +
+ static_cast<uint32_t>(
+ codec_.simulcastStream[2].minBitrate * kDefaultHysteresis + 0.5))
+ .is_bw_limited());
+ EXPECT_FALSE(GetAllocation(kVeryBigBitrate).is_bw_limited());
+}
+
+TEST_F(SimulcastRateAllocatorTest, SingleSimulcastAboveMax) {
+ codec_.numberOfSimulcastStreams = 1;
+ codec_.simulcastStream[0].minBitrate = kMinBitrateKbps;
+ const uint32_t kMax = codec_.simulcastStream[0].maxBitrate + 1000;
+ codec_.simulcastStream[0].maxBitrate = kMax;
+ codec_.simulcastStream[0].active = true;
+ CreateAllocator();
+
+ uint32_t expected[] = {kMax};
+ ExpectEqual(expected, GetAllocation(kMax));
+ ExpectEqual(expected, GetAllocation(kMax + 1));
+ ExpectEqual(expected, GetAllocation(std::numeric_limits<uint32_t>::max()));
+}
+
+TEST_F(SimulcastRateAllocatorTest, SingleSimulcastWithinLimits) {
+ codec_.numberOfSimulcastStreams = 1;
+ codec_.simulcastStream[0].minBitrate = kMinBitrateKbps;
+ codec_.simulcastStream[0].targetBitrate = kLegacyScreenshareTargetBitrateKbps;
+ codec_.simulcastStream[0].maxBitrate = kLegacyScreenshareMaxBitrateKbps;
+ codec_.simulcastStream[0].active = true;
+ CreateAllocator();
+
+ for (uint32_t bitrate = kMinBitrateKbps;
+ bitrate <= kLegacyScreenshareMaxBitrateKbps; ++bitrate) {
+ uint32_t expected[] = {bitrate};
+ ExpectEqual(expected, GetAllocation(bitrate));
+ }
+}
+
+TEST_F(SimulcastRateAllocatorTest, Regular3TLTemporalRateAllocation) {
+ SetupCodec3SL3TL({true, true, true});
+ CreateAllocator();
+
+ const VideoBitrateAllocation alloc = GetAllocation(kMinBitrateKbps);
+ // 40/20/40.
+ EXPECT_EQ(static_cast<uint32_t>(0.4 * kMinBitrateKbps),
+ alloc.GetBitrate(0, 0) / 1000);
+ EXPECT_EQ(static_cast<uint32_t>(0.2 * kMinBitrateKbps),
+ alloc.GetBitrate(0, 1) / 1000);
+ EXPECT_EQ(static_cast<uint32_t>(0.4 * kMinBitrateKbps),
+ alloc.GetBitrate(0, 2) / 1000);
+}
+
+TEST_F(SimulcastRateAllocatorTest, BaseHeavy3TLTemporalRateAllocation) {
+ test::ScopedFieldTrials field_trials(
+ "WebRTC-UseBaseHeavyVP8TL3RateAllocation/Enabled/");
+
+ SetupCodec3SL3TL({true, true, true});
+ CreateAllocator();
+
+ const VideoBitrateAllocation alloc = GetAllocation(kMinBitrateKbps);
+ // 60/20/20.
+ EXPECT_EQ(static_cast<uint32_t>(0.6 * kMinBitrateKbps),
+ alloc.GetBitrate(0, 0) / 1000);
+ EXPECT_EQ(static_cast<uint32_t>(0.2 * kMinBitrateKbps),
+ alloc.GetBitrate(0, 1) / 1000);
+ EXPECT_EQ(static_cast<uint32_t>(0.2 * kMinBitrateKbps),
+ alloc.GetBitrate(0, 2) / 1000);
+}
+
+TEST_F(SimulcastRateAllocatorTest, SingleSimulcastInactive) {
+ codec_.numberOfSimulcastStreams = 1;
+ codec_.simulcastStream[0].minBitrate = kMinBitrateKbps;
+ codec_.simulcastStream[0].targetBitrate = kLegacyScreenshareTargetBitrateKbps;
+ codec_.simulcastStream[0].maxBitrate = kLegacyScreenshareMaxBitrateKbps;
+ codec_.simulcastStream[0].active = false;
+ CreateAllocator();
+
+ uint32_t expected[] = {0};
+ ExpectEqual(expected, GetAllocation(kMinBitrateKbps - 10));
+ ExpectEqual(expected, GetAllocation(kLegacyScreenshareTargetBitrateKbps));
+ ExpectEqual(expected, GetAllocation(kLegacyScreenshareMaxBitrateKbps + 10));
+}
+
+TEST_F(SimulcastRateAllocatorTest, OneToThreeStreams) {
+ SetupCodec3SL3TL({true, true, true});
+ CreateAllocator();
+
+ {
+ // Single stream, min bitrate.
+ const uint32_t bitrate = codec_.simulcastStream[0].minBitrate;
+ uint32_t expected[] = {bitrate, 0, 0};
+ ExpectEqual(expected, GetAllocation(bitrate));
+ }
+
+ {
+ // Single stream at target bitrate.
+ const uint32_t bitrate = codec_.simulcastStream[0].targetBitrate;
+ uint32_t expected[] = {bitrate, 0, 0};
+ ExpectEqual(expected, GetAllocation(bitrate));
+ }
+
+ uint32_t kMinInitialRateTwoLayers =
+ codec_.simulcastStream[0].targetBitrate +
+ static_cast<uint32_t>(codec_.simulcastStream[1].minBitrate *
+ kDefaultHysteresis);
+ {
+ // Bitrate above target for first stream, but below min for the next one.
+ const uint32_t bitrate = kMinInitialRateTwoLayers - 1;
+ uint32_t expected[] = {bitrate, 0, 0};
+ ExpectEqual(expected, GetAllocation(bitrate));
+ }
+
+ {
+ // Just enough for two streams.
+ const uint32_t bitrate = kMinInitialRateTwoLayers;
+ uint32_t expected[] = {
+ codec_.simulcastStream[0].targetBitrate,
+ kMinInitialRateTwoLayers - codec_.simulcastStream[0].targetBitrate, 0};
+ ExpectEqual(expected, GetAllocation(bitrate));
+ }
+
+ {
+ // Second stream maxed out, but not enough for third.
+ const uint32_t bitrate = codec_.simulcastStream[0].targetBitrate +
+ codec_.simulcastStream[1].maxBitrate;
+ uint32_t expected[] = {codec_.simulcastStream[0].targetBitrate,
+ codec_.simulcastStream[1].maxBitrate, 0};
+ ExpectEqual(expected, GetAllocation(bitrate));
+ }
+
+ uint32_t kMinInitialRateThreeLayers =
+ codec_.simulcastStream[0].targetBitrate +
+ codec_.simulcastStream[1].targetBitrate +
+ static_cast<uint32_t>(codec_.simulcastStream[2].minBitrate *
+ kDefaultHysteresis);
+ {
+ // First two streams maxed out, but not enough for third. Nowhere to put
+ // remaining bits.
+ const uint32_t bitrate = kMinInitialRateThreeLayers - 1;
+ uint32_t expected[] = {codec_.simulcastStream[0].targetBitrate,
+ codec_.simulcastStream[1].maxBitrate, 0};
+ ExpectEqual(expected, GetAllocation(bitrate));
+ }
+
+ {
+ // Just enough for all three streams.
+ const uint32_t bitrate = kMinInitialRateThreeLayers;
+ uint32_t expected[] = {
+ codec_.simulcastStream[0].targetBitrate,
+ codec_.simulcastStream[1].targetBitrate,
+ static_cast<uint32_t>(codec_.simulcastStream[2].minBitrate *
+ kDefaultHysteresis)};
+ ExpectEqual(expected, GetAllocation(bitrate));
+ }
+
+ {
+ // Third maxed out.
+ const uint32_t bitrate = codec_.simulcastStream[0].targetBitrate +
+ codec_.simulcastStream[1].targetBitrate +
+ codec_.simulcastStream[2].maxBitrate;
+ uint32_t expected[] = {codec_.simulcastStream[0].targetBitrate,
+ codec_.simulcastStream[1].targetBitrate,
+ codec_.simulcastStream[2].maxBitrate};
+ ExpectEqual(expected, GetAllocation(bitrate));
+ }
+
+ {
+ // Enough to max out all streams which will allocate the target amount to
+ // the lower streams.
+ const uint32_t bitrate = codec_.simulcastStream[0].maxBitrate +
+ codec_.simulcastStream[1].maxBitrate +
+ codec_.simulcastStream[2].maxBitrate;
+ uint32_t expected[] = {codec_.simulcastStream[0].targetBitrate,
+ codec_.simulcastStream[1].targetBitrate,
+ codec_.simulcastStream[2].maxBitrate};
+ ExpectEqual(expected, GetAllocation(bitrate));
+ }
+}
+
+// If three simulcast streams that are all inactive, none of them should be
+// allocated bitrate.
+TEST_F(SimulcastRateAllocatorTest, ThreeStreamsInactive) {
+ SetupCodec3SL3TL({false, false, false});
+ CreateAllocator();
+
+ // Just enough to allocate the min.
+ const uint32_t min_bitrate = codec_.simulcastStream[0].minBitrate +
+ codec_.simulcastStream[1].minBitrate +
+ codec_.simulcastStream[2].minBitrate;
+ // Enough bitrate to allocate target to all streams.
+ const uint32_t target_bitrate = codec_.simulcastStream[0].targetBitrate +
+ codec_.simulcastStream[1].targetBitrate +
+ codec_.simulcastStream[2].targetBitrate;
+ // Enough bitrate to allocate max to all streams.
+ const uint32_t max_bitrate = codec_.simulcastStream[0].maxBitrate +
+ codec_.simulcastStream[1].maxBitrate +
+ codec_.simulcastStream[2].maxBitrate;
+ uint32_t expected[] = {0, 0, 0};
+ ExpectEqual(expected, GetAllocation(0));
+ ExpectEqual(expected, GetAllocation(min_bitrate));
+ ExpectEqual(expected, GetAllocation(target_bitrate));
+ ExpectEqual(expected, GetAllocation(max_bitrate));
+}
+
+// If there are two simulcast streams, we expect the high active stream to be
+// allocated as if it is a single active stream.
+TEST_F(SimulcastRateAllocatorTest, TwoStreamsLowInactive) {
+ SetupCodec2SL3TL({false, true});
+ CreateAllocator();
+
+ const uint32_t kActiveStreamMinBitrate = codec_.simulcastStream[1].minBitrate;
+ const uint32_t kActiveStreamTargetBitrate =
+ codec_.simulcastStream[1].targetBitrate;
+ const uint32_t kActiveStreamMaxBitrate = codec_.simulcastStream[1].maxBitrate;
+ {
+ // Expect that the stream is always allocated its min bitrate.
+ uint32_t expected[] = {0, kActiveStreamMinBitrate};
+ ExpectEqual(expected, GetAllocation(0));
+ ExpectEqual(expected, GetAllocation(kActiveStreamMinBitrate - 10));
+ ExpectEqual(expected, GetAllocation(kActiveStreamMinBitrate));
+ }
+
+ {
+ // The stream should be allocated its target bitrate.
+ uint32_t expected[] = {0, kActiveStreamTargetBitrate};
+ ExpectEqual(expected, GetAllocation(kActiveStreamTargetBitrate));
+ }
+
+ {
+ // The stream should be allocated its max if the target input is sufficient.
+ uint32_t expected[] = {0, kActiveStreamMaxBitrate};
+ ExpectEqual(expected, GetAllocation(kActiveStreamMaxBitrate));
+ ExpectEqual(expected, GetAllocation(std::numeric_limits<uint32_t>::max()));
+ }
+}
+
+// If there are two simulcast streams, we expect the low active stream to be
+// allocated as if it is a single active stream.
+TEST_F(SimulcastRateAllocatorTest, TwoStreamsHighInactive) {
+ SetupCodec2SL3TL({true, false});
+ CreateAllocator();
+
+ const uint32_t kActiveStreamMinBitrate = codec_.simulcastStream[0].minBitrate;
+ const uint32_t kActiveStreamTargetBitrate =
+ codec_.simulcastStream[0].targetBitrate;
+ const uint32_t kActiveStreamMaxBitrate = codec_.simulcastStream[0].maxBitrate;
+ {
+ // Expect that the stream is always allocated its min bitrate.
+ uint32_t expected[] = {kActiveStreamMinBitrate, 0};
+ ExpectEqual(expected, GetAllocation(0));
+ ExpectEqual(expected, GetAllocation(kActiveStreamMinBitrate - 10));
+ ExpectEqual(expected, GetAllocation(kActiveStreamMinBitrate));
+ }
+
+ {
+ // The stream should be allocated its target bitrate.
+ uint32_t expected[] = {kActiveStreamTargetBitrate, 0};
+ ExpectEqual(expected, GetAllocation(kActiveStreamTargetBitrate));
+ }
+
+ {
+ // The stream should be allocated its max if the target input is sufficent.
+ uint32_t expected[] = {kActiveStreamMaxBitrate, 0};
+ ExpectEqual(expected, GetAllocation(kActiveStreamMaxBitrate));
+ ExpectEqual(expected, GetAllocation(std::numeric_limits<uint32_t>::max()));
+ }
+}
+
+// If there are three simulcast streams and the middle stream is inactive, the
+// other two streams should be allocated bitrate the same as if they are two
+// active simulcast streams.
+TEST_F(SimulcastRateAllocatorTest, ThreeStreamsMiddleInactive) {
+ SetupCodec3SL3TL({true, false, true});
+ CreateAllocator();
+
+ {
+ const uint32_t kLowStreamMinBitrate = codec_.simulcastStream[0].minBitrate;
+ // The lowest stream should always be allocated its minimum bitrate.
+ uint32_t expected[] = {kLowStreamMinBitrate, 0, 0};
+ ExpectEqual(expected, GetAllocation(0));
+ ExpectEqual(expected, GetAllocation(kLowStreamMinBitrate - 10));
+ ExpectEqual(expected, GetAllocation(kLowStreamMinBitrate));
+ }
+
+ {
+ // The lowest stream gets its target bitrate.
+ uint32_t expected[] = {codec_.simulcastStream[0].targetBitrate, 0, 0};
+ ExpectEqual(expected,
+ GetAllocation(codec_.simulcastStream[0].targetBitrate));
+ }
+
+ {
+ // The lowest stream gets its max bitrate, but not enough for the high
+ // stream.
+ const uint32_t bitrate = codec_.simulcastStream[0].targetBitrate +
+ codec_.simulcastStream[2].minBitrate - 1;
+ uint32_t expected[] = {codec_.simulcastStream[0].maxBitrate, 0, 0};
+ ExpectEqual(expected, GetAllocation(bitrate));
+ }
+
+ {
+ // Both active streams get allocated target bitrate.
+ const uint32_t bitrate = codec_.simulcastStream[0].targetBitrate +
+ codec_.simulcastStream[2].targetBitrate;
+ uint32_t expected[] = {codec_.simulcastStream[0].targetBitrate, 0,
+ codec_.simulcastStream[2].targetBitrate};
+ ExpectEqual(expected, GetAllocation(bitrate));
+ }
+
+ {
+ // Lowest stream gets its target bitrate, high stream gets its max bitrate.
+ uint32_t bitrate = codec_.simulcastStream[0].targetBitrate +
+ codec_.simulcastStream[2].maxBitrate;
+ uint32_t expected[] = {codec_.simulcastStream[0].targetBitrate, 0,
+ codec_.simulcastStream[2].maxBitrate};
+ ExpectEqual(expected, GetAllocation(bitrate));
+ ExpectEqual(expected, GetAllocation(bitrate + 10));
+ ExpectEqual(expected, GetAllocation(std::numeric_limits<uint32_t>::max()));
+ }
+}
+
+TEST_F(SimulcastRateAllocatorTest, NonConferenceModeScreenshare) {
+ codec_.mode = VideoCodecMode::kScreensharing;
+ SetupCodec3SL3TL({true, true, true});
+ CreateAllocator();
+
+ // Make sure we have enough bitrate for all 3 simulcast layers
+ const uint32_t bitrate = codec_.simulcastStream[0].maxBitrate +
+ codec_.simulcastStream[1].maxBitrate +
+ codec_.simulcastStream[2].maxBitrate;
+ const VideoBitrateAllocation alloc = GetAllocation(bitrate);
+
+ EXPECT_EQ(alloc.GetTemporalLayerAllocation(0).size(), 3u);
+ EXPECT_EQ(alloc.GetTemporalLayerAllocation(1).size(), 3u);
+ EXPECT_EQ(alloc.GetTemporalLayerAllocation(2).size(), 3u);
+}
+
+TEST_F(SimulcastRateAllocatorTest, StableRate) {
+ webrtc::test::ScopedFieldTrials field_trials(
+ "WebRTC-StableTargetRate/"
+ "enabled:true,"
+ "video_hysteresis_factor:1.1/");
+
+ SetupCodec3SL3TL({true, true, true});
+ CreateAllocator();
+
+ // Let the volatile rate always be be enough for all streams, in this test we
+ // are only interested in how the stable rate affects enablement.
+ const DataRate volatile_rate =
+ (TargetRate(0) + TargetRate(1) + MinRate(2)) * 1.1;
+
+ {
+ // On the first call to a new SimulcastRateAllocator instance, hysteresis
+ // is disabled, but stable rate still caps layers.
+ uint32_t expected[] = {TargetRate(0).kbps<uint32_t>(),
+ MaxRate(1).kbps<uint32_t>()};
+ ExpectEqual(expected,
+ GetAllocation(volatile_rate, TargetRate(0) + MinRate(1)));
+ }
+
+ {
+ // Let stable rate go to a bitrate below what is needed for two streams.
+ uint32_t expected[] = {MaxRate(0).kbps<uint32_t>(), 0};
+ ExpectEqual(expected,
+ GetAllocation(volatile_rate, TargetRate(0) + MinRate(1) -
+ DataRate::BitsPerSec(1)));
+ }
+
+ {
+ // Don't enable stream as we need to get up above hysteresis threshold.
+ uint32_t expected[] = {MaxRate(0).kbps<uint32_t>(), 0};
+ ExpectEqual(expected,
+ GetAllocation(volatile_rate, TargetRate(0) + MinRate(1)));
+ }
+
+ {
+ // Above threshold with hysteresis, enable second stream.
+ uint32_t expected[] = {TargetRate(0).kbps<uint32_t>(),
+ MaxRate(1).kbps<uint32_t>()};
+ ExpectEqual(expected, GetAllocation(volatile_rate,
+ (TargetRate(0) + MinRate(1)) * 1.1));
+ }
+
+ {
+ // Enough to enable all thee layers.
+ uint32_t expected[] = {
+ TargetRate(0).kbps<uint32_t>(), TargetRate(1).kbps<uint32_t>(),
+ (volatile_rate - TargetRate(0) - TargetRate(1)).kbps<uint32_t>()};
+ ExpectEqual(expected, GetAllocation(volatile_rate, volatile_rate));
+ }
+
+ {
+ // Drop hysteresis, all three still on.
+ uint32_t expected[] = {
+ TargetRate(0).kbps<uint32_t>(), TargetRate(1).kbps<uint32_t>(),
+ (volatile_rate - TargetRate(0) - TargetRate(1)).kbps<uint32_t>()};
+ ExpectEqual(expected,
+ GetAllocation(volatile_rate,
+ TargetRate(0) + TargetRate(1) + MinRate(2)));
+ }
+}
+
+class ScreenshareRateAllocationTest : public SimulcastRateAllocatorTest {
+ public:
+ void SetupConferenceScreenshare(bool use_simulcast, bool active = true) {
+ codec_.mode = VideoCodecMode::kScreensharing;
+ codec_.minBitrate = kMinBitrateKbps;
+ codec_.maxBitrate =
+ kLegacyScreenshareMaxBitrateKbps + kSimulcastScreenshareMaxBitrateKbps;
+ if (use_simulcast) {
+ codec_.numberOfSimulcastStreams = 2;
+ codec_.simulcastStream[0].minBitrate = kMinBitrateKbps;
+ codec_.simulcastStream[0].targetBitrate =
+ kLegacyScreenshareTargetBitrateKbps;
+ codec_.simulcastStream[0].maxBitrate = kLegacyScreenshareMaxBitrateKbps;
+ codec_.simulcastStream[0].numberOfTemporalLayers = 2;
+ codec_.simulcastStream[0].active = active;
+
+ codec_.simulcastStream[1].minBitrate =
+ kSimulcastScreenshareMinBitrateKbps;
+ codec_.simulcastStream[1].targetBitrate =
+ kSimulcastScreenshareMaxBitrateKbps;
+ codec_.simulcastStream[1].maxBitrate =
+ kSimulcastScreenshareMaxBitrateKbps;
+ codec_.simulcastStream[1].numberOfTemporalLayers = 2;
+ codec_.simulcastStream[1].active = active;
+ } else {
+ codec_.numberOfSimulcastStreams = 0;
+ codec_.VP8()->numberOfTemporalLayers = 2;
+ codec_.active = active;
+ }
+ }
+};
+
+INSTANTIATE_TEST_SUITE_P(ScreenshareTest,
+ ScreenshareRateAllocationTest,
+ ::testing::Bool());
+
+TEST_P(ScreenshareRateAllocationTest, ConferenceBitrateBelowTl0) {
+ SetupConferenceScreenshare(GetParam());
+ CreateAllocator(true);
+
+ VideoBitrateAllocation allocation =
+ allocator_->Allocate(VideoBitrateAllocationParameters(
+ kLegacyScreenshareTargetBitrateKbps * 1000, kFramerateFps));
+
+ // All allocation should go in TL0.
+ EXPECT_EQ(kLegacyScreenshareTargetBitrateKbps, allocation.get_sum_kbps());
+ EXPECT_EQ(kLegacyScreenshareTargetBitrateKbps,
+ allocation.GetBitrate(0, 0) / 1000);
+ EXPECT_EQ(allocation.is_bw_limited(), GetParam());
+}
+
+TEST_P(ScreenshareRateAllocationTest, ConferenceBitrateAboveTl0) {
+ SetupConferenceScreenshare(GetParam());
+ CreateAllocator(true);
+
+ uint32_t target_bitrate_kbps =
+ (kLegacyScreenshareTargetBitrateKbps + kLegacyScreenshareMaxBitrateKbps) /
+ 2;
+ VideoBitrateAllocation allocation =
+ allocator_->Allocate(VideoBitrateAllocationParameters(
+ target_bitrate_kbps * 1000, kFramerateFps));
+
+ // Fill TL0, then put the rest in TL1.
+ EXPECT_EQ(target_bitrate_kbps, allocation.get_sum_kbps());
+ EXPECT_EQ(kLegacyScreenshareTargetBitrateKbps,
+ allocation.GetBitrate(0, 0) / 1000);
+ EXPECT_EQ(target_bitrate_kbps - kLegacyScreenshareTargetBitrateKbps,
+ allocation.GetBitrate(0, 1) / 1000);
+ EXPECT_EQ(allocation.is_bw_limited(), GetParam());
+}
+
+TEST_F(ScreenshareRateAllocationTest, ConferenceBitrateAboveTl1) {
+ // This test is only for the non-simulcast case.
+ SetupConferenceScreenshare(false);
+ CreateAllocator(true);
+
+ VideoBitrateAllocation allocation =
+ allocator_->Allocate(VideoBitrateAllocationParameters(
+ kLegacyScreenshareMaxBitrateKbps * 2000, kFramerateFps));
+
+ // Fill both TL0 and TL1, but no more.
+ EXPECT_EQ(kLegacyScreenshareMaxBitrateKbps, allocation.get_sum_kbps());
+ EXPECT_EQ(kLegacyScreenshareTargetBitrateKbps,
+ allocation.GetBitrate(0, 0) / 1000);
+ EXPECT_EQ(
+ kLegacyScreenshareMaxBitrateKbps - kLegacyScreenshareTargetBitrateKbps,
+ allocation.GetBitrate(0, 1) / 1000);
+ EXPECT_FALSE(allocation.is_bw_limited());
+}
+
+// This tests when the screenshare is inactive it should be allocated 0 bitrate
+// for all layers.
+TEST_P(ScreenshareRateAllocationTest, InactiveScreenshare) {
+ SetupConferenceScreenshare(GetParam(), false);
+ CreateAllocator();
+
+ // Enough bitrate for TL0 and TL1.
+ uint32_t target_bitrate_kbps =
+ (kLegacyScreenshareTargetBitrateKbps + kLegacyScreenshareMaxBitrateKbps) /
+ 2;
+ VideoBitrateAllocation allocation =
+ allocator_->Allocate(VideoBitrateAllocationParameters(
+ target_bitrate_kbps * 1000, kFramerateFps));
+
+ EXPECT_EQ(0U, allocation.get_sum_kbps());
+}
+
+TEST_F(ScreenshareRateAllocationTest, Hysteresis) {
+ // This test is only for the simulcast case.
+ SetupConferenceScreenshare(true);
+ CreateAllocator();
+
+ // The bitrate at which we would normally enable the upper simulcast stream.
+ const uint32_t default_enable_rate_bps =
+ codec_.simulcastStream[0].targetBitrate +
+ codec_.simulcastStream[1].minBitrate;
+ const uint32_t enable_rate_with_hysteresis_bps =
+ (default_enable_rate_bps * 135) / 100;
+
+ {
+ // On the first call to a new SimulcastRateAllocator instance, hysteresis
+ // is disabled.
+ const uint32_t bitrate = default_enable_rate_bps;
+ uint32_t expected[] = {codec_.simulcastStream[0].targetBitrate,
+ codec_.simulcastStream[1].minBitrate};
+ ExpectEqual(expected, GetAllocation(bitrate));
+ }
+
+ {
+ // Go down to a bitrate below what is needed for two streams.
+ const uint32_t bitrate = default_enable_rate_bps - 1;
+ uint32_t expected[] = {bitrate, 0};
+ ExpectEqual(expected, GetAllocation(bitrate));
+ }
+
+ {
+ // Don't enable stream as we need to get up above hysteresis threshold.
+ const uint32_t bitrate = default_enable_rate_bps;
+ uint32_t expected[] = {bitrate, 0};
+ ExpectEqual(expected, GetAllocation(bitrate));
+ }
+
+ {
+ // Above threshold, enable second stream.
+ const uint32_t bitrate = enable_rate_with_hysteresis_bps;
+ uint32_t expected[] = {codec_.simulcastStream[0].targetBitrate,
+ enable_rate_with_hysteresis_bps -
+ codec_.simulcastStream[0].targetBitrate};
+ ExpectEqual(expected, GetAllocation(bitrate));
+ }
+
+ {
+ // Go down again, still keep the second stream alive.
+ const uint32_t bitrate = default_enable_rate_bps;
+ uint32_t expected[] = {codec_.simulcastStream[0].targetBitrate,
+ codec_.simulcastStream[1].minBitrate};
+ ExpectEqual(expected, GetAllocation(bitrate));
+ }
+
+ {
+ // Go down below default enable, second stream is shut down again.
+ const uint32_t bitrate = default_enable_rate_bps - 1;
+ uint32_t expected[] = {bitrate, 0};
+ ExpectEqual(expected, GetAllocation(bitrate));
+ }
+
+ {
+ // Go up, hysteresis is blocking us again.
+ const uint32_t bitrate = default_enable_rate_bps;
+ uint32_t expected[] = {bitrate, 0};
+ ExpectEqual(expected, GetAllocation(bitrate));
+ }
+}
+
+} // namespace webrtc