Index: webrtc/modules/video_coding/utility/simulcast_state_unittest.cc |
diff --git a/webrtc/modules/video_coding/utility/simulcast_state_unittest.cc b/webrtc/modules/video_coding/utility/simulcast_state_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..aecdd74341e3b58e42a6c06ccb47ba150c6e53e6 |
--- /dev/null |
+++ b/webrtc/modules/video_coding/utility/simulcast_state_unittest.cc |
@@ -0,0 +1,304 @@ |
+/* |
+ * 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 "webrtc/modules/video_coding/utility/simulcast_state.h" |
+ |
+#include <algorithm> |
+#include <memory> |
+#include <vector> |
+ |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+namespace webrtc { |
+namespace { |
+const int kMaxStreams = kMaxSimulcastStreams; |
+} // namespace |
+ |
+class SimulcastStateTest : public ::testing::Test { |
+ public: |
+ SimulcastStateTest() {} |
+ virtual ~SimulcastStateTest() {} |
+ |
+ void SetUp() override { |
+ memset(&codec_, 0, sizeof(VideoCodec)); |
+ codec_.codecType = kVideoCodecVP8; |
+ codec_.numberOfSimulcastStreams = 0; |
+ codec_.minBitrate = 60; |
+ codec_.targetBitrate = 600; |
+ codec_.maxBitrate = 2400; |
+ codec_.startBitrate = 300; |
+ } |
+ |
+ void ExpectState( |
+ const std::vector<SimulcastState::Stream>& expected_streams) { |
+ const std::vector<SimulcastState::Stream>& actual_streams = |
+ simulcast_state_->Streams(); |
+ ASSERT_EQ(expected_streams.size(), actual_streams.size()); |
+ const int num_streams = expected_streams.size(); |
+ ASSERT_LE(num_streams, kMaxStreams); |
+ for (int i = 0; i < num_streams; ++i) { |
+ const SimulcastState::Stream& expected = expected_streams[i]; |
+ const SimulcastState::Stream& actual = actual_streams[i]; |
+ EXPECT_EQ(i, expected.idx); |
+ EXPECT_EQ(i, actual.idx); |
+ EXPECT_EQ(expected.allocated_rate_bps, actual.allocated_rate_bps); |
+ EXPECT_EQ(expected.sending, actual.sending); |
+ EXPECT_EQ(expected.keyframe_request, actual.keyframe_request); |
+ } |
+ } |
+ |
+ void TestRateAllocation() { |
+ int spillover_bps = 0; |
+ int margin_bps = 0; |
+ std::vector<SimulcastState::Stream> expected_streams; |
+ for (int i = 0; i < codec_.numberOfSimulcastStreams; ++i) { |
+ SimulcastState::Stream stream(i, &codec_, 0); |
+ stream.sending = i == 0; |
+ stream.keyframe_request = stream.sending; |
+ stream.allocated_rate_bps = |
+ stream.sending ? stream.config.minBitrate * 1000 : 0; |
+ expected_streams.push_back(stream); |
+ } |
+ |
+ simulcast_state_.reset(new SimulcastState(codec_)); |
+ simulcast_state_->AllocateBitrate(0); |
+ EXPECT_TRUE(simulcast_state_->GetAndResetKeyFrameRequest(0)); |
+ expected_streams[0].keyframe_request = false; |
+ |
+ int top_layer = 0; |
+ for (uint32_t i = 1; i < codec_.maxBitrate; ++i) { |
+ simulcast_state_->AllocateBitrate(i * 1000); |
+ ExpectState(expected_streams); |
+ |
+ // Always allocate enough bitrate for the lowest stream. |
+ if (top_layer == 0 && i < codec_.simulcastStream[0].minBitrate) { |
+ expected_streams[0].allocated_rate_bps = |
+ codec_.simulcastStream[0].minBitrate * 1000; |
+ continue; |
+ } |
+ |
+ const int min_bitrate = |
+ expected_streams[top_layer].config.minBitrate * 1000; |
+ const int target_bitrate = |
+ expected_streams[top_layer].config.targetBitrate * 1000; |
+ const int max_bitrate = |
+ expected_streams[top_layer].config.maxBitrate * 1000; |
+ |
+ if (top_layer > 0 && |
+ expected_streams[top_layer].allocated_rate_bps == min_bitrate) { |
+ // Update and reset key frame status on first updater after enabling |
+ // layer. |
+ EXPECT_TRUE(simulcast_state_->GetAndResetKeyFrameRequest(top_layer)); |
+ expected_streams[top_layer].keyframe_request = false; |
+ } |
+ |
+ // First fill up the top layer until target_bitrate. |
+ if (expected_streams[top_layer].allocated_rate_bps < target_bitrate) { |
+ expected_streams[top_layer].allocated_rate_bps += 1000; |
+ continue; |
+ } |
+ |
+ // We have allocated up to the target bitrate. Anything above this may |
+ // be reallocated to a higher layer once it has enough bitrate to reach |
+ // its min_bitrate. |
+ margin_bps += 1000; |
+ if (expected_streams[top_layer].allocated_rate_bps < max_bitrate) { |
+ expected_streams[top_layer].allocated_rate_bps += 1000; |
+ } else { |
+ // TODO(sprang): When we enable allocating extra bits to layers other |
+ // than the top one, traverse down and do that here. |
+ spillover_bps += 1000; |
+ } |
+ |
+ // Check if there is enough bitrate to enable the next layer. |
+ if (top_layer + 1 < codec_.numberOfSimulcastStreams && |
+ margin_bps >= static_cast<int>( |
+ expected_streams[top_layer + 1].config.minBitrate) * |
+ 1000) { |
+ ++top_layer; |
+ expected_streams[top_layer].sending = true; |
+ expected_streams[top_layer].keyframe_request = true; |
+ |
+ // Steal margin bits from the lower layers. |
+ // TODO(sprang): Loop over j when we enable allocating extra bits to |
+ // layers other than the top one. |
+ int j = top_layer - 1; |
+ int available_margin = |
+ expected_streams[j].allocated_rate_bps - |
+ (expected_streams[j].config.targetBitrate * 1000); |
+ int consumed_margin = std::min(available_margin, margin_bps); |
+ expected_streams[j].allocated_rate_bps -= consumed_margin; |
+ margin_bps -= consumed_margin; |
+ |
+ if (margin_bps > 0 && spillover_bps > 0) { |
+ // The rest of the needed margin is in allocated spillover. |
+ EXPECT_EQ(margin_bps, spillover_bps); |
+ spillover_bps = 0; |
+ margin_bps = 0; |
+ } else { |
+ EXPECT_EQ(0, margin_bps); |
+ } |
+ |
+ expected_streams[top_layer].allocated_rate_bps = |
+ expected_streams[top_layer].config.minBitrate * 1000; |
+ } |
+ } |
+ } |
+ |
+ VideoCodec codec_; |
+ std::unique_ptr<SimulcastState> simulcast_state_; |
+}; |
+ |
+TEST_F(SimulcastStateTest, NoSimulcast) { |
+ simulcast_state_.reset(new SimulcastState(codec_)); |
+ |
+ SimulcastState::Stream expected_stream(0, &codec_, |
+ codec_.startBitrate * 1000); |
+ std::vector<SimulcastState::Stream> expected_streams; |
+ expected_streams.push_back(expected_stream); |
+ |
+ ExpectState(expected_streams); |
+ |
+ for (uint32_t i = 0; i < codec_.maxBitrate + 1; ++i) { |
+ simulcast_state_->AllocateBitrate(i * 1000); |
+ if (i < codec_.minBitrate) { |
+ expected_streams[0].allocated_rate_bps = codec_.minBitrate * 1000; |
+ } else if (i > codec_.maxBitrate) { |
+ expected_streams[0].allocated_rate_bps = codec_.maxBitrate * 1000; |
+ } else { |
+ expected_streams[0].allocated_rate_bps = i * 1000; |
+ } |
+ ExpectState(expected_streams); |
+ } |
+} |
+ |
+TEST_F(SimulcastStateTest, MultipleStreamsRates) { |
+ for (int num_streams = 1; num_streams < kMaxStreams; ++num_streams) { |
+ codec_.numberOfSimulcastStreams = num_streams; |
+ codec_.startBitrate = 0; |
+ |
+ for (int i = 0; i < num_streams; ++i) { |
+ codec_.simulcastStream[i].minBitrate = |
+ ((i + 1) * codec_.minBitrate) / kMaxStreams; |
+ codec_.simulcastStream[i].targetBitrate = |
+ ((i + 1) * codec_.targetBitrate) / kMaxStreams; |
+ codec_.simulcastStream[i].maxBitrate = |
+ ((i + 1) * codec_.maxBitrate) / kMaxStreams; |
+ } |
+ |
+ TestRateAllocation(); |
+ } |
+} |
+ |
+TEST_F(SimulcastStateTest, NextMinLargerThanMax) { |
+ codec_.startBitrate = 0; |
+ codec_.numberOfSimulcastStreams = 2; |
+ codec_.simulcastStream[0].minBitrate = 10; |
+ codec_.simulcastStream[0].targetBitrate = 100; |
+ codec_.simulcastStream[0].maxBitrate = 200; |
+ codec_.simulcastStream[1].minBitrate = 300; |
+ codec_.simulcastStream[1].targetBitrate = 400; |
+ codec_.simulcastStream[1].maxBitrate = 500; |
+ |
+ TestRateAllocation(); |
+} |
+ |
+TEST_F(SimulcastStateTest, LargerMinButLowerMax) { |
+ codec_.startBitrate = 0; |
+ codec_.numberOfSimulcastStreams = 3; |
+ codec_.simulcastStream[0].minBitrate = 100; |
+ codec_.simulcastStream[0].targetBitrate = 200; |
+ codec_.simulcastStream[0].maxBitrate = 600; |
+ codec_.simulcastStream[1].minBitrate = 200; |
+ codec_.simulcastStream[1].targetBitrate = 300; |
+ codec_.simulcastStream[1].maxBitrate = 400; |
+ codec_.simulcastStream[2].minBitrate = 300; |
+ codec_.simulcastStream[2].targetBitrate = 600; |
+ codec_.simulcastStream[2].maxBitrate = 800; |
+ |
+ TestRateAllocation(); |
+} |
+ |
+TEST_F(SimulcastStateTest, SetsSendingAndKeyframeStates) { |
+ codec_.startBitrate = 0; |
+ codec_.numberOfSimulcastStreams = 3; |
+ |
+ codec_.simulcastStream[0].minBitrate = 100; |
+ codec_.simulcastStream[0].targetBitrate = 200; |
+ codec_.simulcastStream[0].maxBitrate = 600; |
+ const int kFirstCutoff = codec_.simulcastStream[0].minBitrate * 1000; |
+ |
+ codec_.simulcastStream[1].minBitrate = 300; |
+ codec_.simulcastStream[1].targetBitrate = 600; |
+ codec_.simulcastStream[1].maxBitrate = 1200; |
+ const int kSecondCutoff = (codec_.simulcastStream[0].targetBitrate + |
+ codec_.simulcastStream[1].minBitrate) * |
+ 1000; |
+ |
+ codec_.simulcastStream[2].minBitrate = 500; |
+ codec_.simulcastStream[2].targetBitrate = 800; |
+ codec_.simulcastStream[2].maxBitrate = 1500; |
+ const int kThirdCutoff = (codec_.simulcastStream[0].targetBitrate + |
+ codec_.simulcastStream[1].targetBitrate + |
+ codec_.simulcastStream[2].minBitrate) * |
+ 1000; |
+ |
+ simulcast_state_.reset(new SimulcastState(codec_)); |
+ |
+ simulcast_state_->AllocateBitrate(kFirstCutoff); |
+ EXPECT_TRUE(simulcast_state_->GetAndResetKeyFrameRequest(0)); |
+ EXPECT_TRUE(simulcast_state_->IsSending(0)); |
+ EXPECT_FALSE(simulcast_state_->GetAndResetKeyFrameRequest(1)); |
+ EXPECT_FALSE(simulcast_state_->IsSending(1)); |
+ EXPECT_FALSE(simulcast_state_->GetAndResetKeyFrameRequest(2)); |
+ EXPECT_FALSE(simulcast_state_->IsSending(2)); |
+ |
+ simulcast_state_->AllocateBitrate(kSecondCutoff); |
+ EXPECT_FALSE(simulcast_state_->GetAndResetKeyFrameRequest(0)); |
+ EXPECT_TRUE(simulcast_state_->IsSending(0)); |
+ EXPECT_TRUE(simulcast_state_->GetAndResetKeyFrameRequest(1)); |
+ EXPECT_TRUE(simulcast_state_->IsSending(1)); |
+ EXPECT_FALSE(simulcast_state_->GetAndResetKeyFrameRequest(2)); |
+ EXPECT_FALSE(simulcast_state_->IsSending(2)); |
+ |
+ simulcast_state_->AllocateBitrate(kThirdCutoff); |
+ EXPECT_FALSE(simulcast_state_->GetAndResetKeyFrameRequest(0)); |
+ EXPECT_TRUE(simulcast_state_->IsSending(0)); |
+ EXPECT_FALSE(simulcast_state_->GetAndResetKeyFrameRequest(1)); |
+ EXPECT_TRUE(simulcast_state_->IsSending(1)); |
+ EXPECT_TRUE(simulcast_state_->GetAndResetKeyFrameRequest(2)); |
+ EXPECT_TRUE(simulcast_state_->IsSending(2)); |
+ |
+ simulcast_state_->AllocateBitrate(kSecondCutoff); |
+ EXPECT_FALSE(simulcast_state_->GetAndResetKeyFrameRequest(0)); |
+ EXPECT_TRUE(simulcast_state_->IsSending(0)); |
+ EXPECT_FALSE(simulcast_state_->GetAndResetKeyFrameRequest(1)); |
+ EXPECT_TRUE(simulcast_state_->IsSending(1)); |
+ EXPECT_FALSE(simulcast_state_->GetAndResetKeyFrameRequest(2)); |
+ EXPECT_FALSE(simulcast_state_->IsSending(2)); |
+ |
+ simulcast_state_->AllocateBitrate(kFirstCutoff); |
+ EXPECT_FALSE(simulcast_state_->GetAndResetKeyFrameRequest(0)); |
+ EXPECT_TRUE(simulcast_state_->IsSending(0)); |
+ EXPECT_FALSE(simulcast_state_->GetAndResetKeyFrameRequest(1)); |
+ EXPECT_FALSE(simulcast_state_->IsSending(1)); |
+ EXPECT_FALSE(simulcast_state_->GetAndResetKeyFrameRequest(2)); |
+ EXPECT_FALSE(simulcast_state_->IsSending(2)); |
+ |
+ simulcast_state_->AllocateBitrate(kThirdCutoff); |
+ EXPECT_FALSE(simulcast_state_->GetAndResetKeyFrameRequest(0)); |
+ EXPECT_TRUE(simulcast_state_->IsSending(0)); |
+ EXPECT_TRUE(simulcast_state_->GetAndResetKeyFrameRequest(1)); |
+ EXPECT_TRUE(simulcast_state_->IsSending(1)); |
+ EXPECT_TRUE(simulcast_state_->GetAndResetKeyFrameRequest(2)); |
+ EXPECT_TRUE(simulcast_state_->IsSending(2)); |
+} |
+ |
+} // namespace webrtc |