Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(63)

Unified Diff: webrtc/modules/video_coding/utility/simulcast_state_unittest.cc

Issue 1913073002: Extract common simulcast logic from VP8 wrapper and simulcast adapter (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Address comments, added tests Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698