blob: 16eee8cb990eb54e22b92fc04400a6b88403238b [file] [log] [blame]
/*
* Copyright 2021 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 <algorithm>
#include "media/engine/webrtc_video_engine.h"
#include "rtc_base/experiments/encoder_info_settings.h"
#include "test/call_test.h"
#include "test/fake_encoder.h"
#include "test/field_trial.h"
#include "test/gtest.h"
#include "test/video_encoder_proxy_factory.h"
namespace webrtc {
namespace test {
namespace {
void SetEncoderSpecific(VideoEncoderConfig* encoder_config,
VideoCodecType type,
size_t num_spatial_layers) {
if (type == kVideoCodecVP9) {
VideoCodecVP9 vp9 = VideoEncoder::GetDefaultVp9Settings();
vp9.numberOfSpatialLayers = num_spatial_layers;
encoder_config->encoder_specific_settings = new rtc::RefCountedObject<
VideoEncoderConfig::Vp9EncoderSpecificSettings>(vp9);
}
}
SpatialLayer GetLayer(int pixels, const VideoCodec& codec) {
if (codec.codecType == VideoCodecType::kVideoCodecVP9) {
for (size_t i = 0; i < codec.VP9().numberOfSpatialLayers; ++i) {
if (codec.spatialLayers[i].width * codec.spatialLayers[i].height ==
pixels) {
return codec.spatialLayers[i];
}
}
} else {
for (int i = 0; i < codec.numberOfSimulcastStreams; ++i) {
if (codec.simulcastStream[i].width * codec.simulcastStream[i].height ==
pixels) {
return codec.simulcastStream[i];
}
}
}
ADD_FAILURE();
return SpatialLayer();
}
} // namespace
class ResolutionBitrateLimitsTest
: public test::CallTest,
public ::testing::WithParamInterface<std::string> {
public:
ResolutionBitrateLimitsTest() : payload_name_(GetParam()) {}
const std::string payload_name_;
};
INSTANTIATE_TEST_SUITE_P(PayloadName,
ResolutionBitrateLimitsTest,
::testing::Values("VP8", "VP9"));
class InitEncodeTest : public test::EndToEndTest,
public test::FrameGeneratorCapturer::SinkWantsObserver,
public test::FakeEncoder {
public:
struct Bitrate {
const absl::optional<uint32_t> min;
const absl::optional<uint32_t> max;
};
struct TestConfig {
const bool active;
const Bitrate bitrate_bps;
};
struct Expectation {
const uint32_t pixels = 0;
const Bitrate eq_bitrate_bps;
const Bitrate ne_bitrate_bps;
};
InitEncodeTest(const std::string& payload_name,
const std::vector<TestConfig>& configs,
const std::vector<Expectation>& expectations)
: EndToEndTest(test::CallTest::kDefaultTimeoutMs),
FakeEncoder(Clock::GetRealTimeClock()),
encoder_factory_(this),
payload_name_(payload_name),
configs_(configs),
expectations_(expectations) {}
void OnFrameGeneratorCapturerCreated(
test::FrameGeneratorCapturer* frame_generator_capturer) override {
frame_generator_capturer->SetSinkWantsObserver(this);
// Set initial resolution.
frame_generator_capturer->ChangeResolution(1280, 720);
}
void OnSinkWantsChanged(rtc::VideoSinkInterface<VideoFrame>* sink,
const rtc::VideoSinkWants& wants) override {}
size_t GetNumVideoStreams() const override {
return (payload_name_ == "VP9") ? 1 : configs_.size();
}
void ModifyVideoConfigs(
VideoSendStream::Config* send_config,
std::vector<VideoReceiveStream::Config>* receive_configs,
VideoEncoderConfig* encoder_config) override {
send_config->encoder_settings.encoder_factory = &encoder_factory_;
send_config->rtp.payload_name = payload_name_;
send_config->rtp.payload_type = test::CallTest::kVideoSendPayloadType;
const VideoCodecType codec_type = PayloadStringToCodecType(payload_name_);
encoder_config->codec_type = codec_type;
encoder_config->video_stream_factory =
new rtc::RefCountedObject<cricket::EncoderStreamFactory>(
payload_name_, /*max qp*/ 0, /*screencast*/ false,
/*screenshare enabled*/ false);
encoder_config->max_bitrate_bps = -1;
if (configs_.size() == 1 && configs_[0].bitrate_bps.max)
encoder_config->max_bitrate_bps = *configs_[0].bitrate_bps.max;
if (payload_name_ == "VP9") {
// Simulcast layers indicates which spatial layers are active.
encoder_config->simulcast_layers.resize(configs_.size());
}
double scale_factor = 1.0;
for (int i = configs_.size() - 1; i >= 0; --i) {
VideoStream& stream = encoder_config->simulcast_layers[i];
stream.active = configs_[i].active;
if (configs_[i].bitrate_bps.min)
stream.min_bitrate_bps = *configs_[i].bitrate_bps.min;
if (configs_[i].bitrate_bps.max)
stream.max_bitrate_bps = *configs_[i].bitrate_bps.max;
stream.scale_resolution_down_by = scale_factor;
scale_factor *= (payload_name_ == "VP9") ? 1.0 : 2.0;
}
SetEncoderSpecific(encoder_config, codec_type, configs_.size());
}
int32_t InitEncode(const VideoCodec* codec,
const Settings& settings) override {
for (const auto& expected : expectations_) {
SpatialLayer layer = GetLayer(expected.pixels, *codec);
if (expected.eq_bitrate_bps.min)
EXPECT_EQ(*expected.eq_bitrate_bps.min, layer.minBitrate * 1000);
if (expected.eq_bitrate_bps.max)
EXPECT_EQ(*expected.eq_bitrate_bps.max, layer.maxBitrate * 1000);
EXPECT_NE(expected.ne_bitrate_bps.min, layer.minBitrate * 1000);
EXPECT_NE(expected.ne_bitrate_bps.max, layer.maxBitrate * 1000);
}
observation_complete_.Set();
return 0;
}
VideoEncoder::EncoderInfo GetEncoderInfo() const override {
EncoderInfo info = FakeEncoder::GetEncoderInfo();
if (!encoder_info_override_.resolution_bitrate_limits().empty()) {
info.resolution_bitrate_limits =
encoder_info_override_.resolution_bitrate_limits();
}
return info;
}
void PerformTest() override {
ASSERT_TRUE(Wait()) << "Timed out while waiting for InitEncode() call.";
}
private:
test::VideoEncoderProxyFactory encoder_factory_;
const std::string payload_name_;
const std::vector<TestConfig> configs_;
const std::vector<Expectation> expectations_;
const LibvpxVp8EncoderInfoSettings encoder_info_override_;
};
TEST_P(ResolutionBitrateLimitsTest, LimitsApplied) {
webrtc::test::ScopedFieldTrials field_trials(
"WebRTC-GetEncoderInfoOverride/"
"frame_size_pixels:921600,"
"min_start_bitrate_bps:0,"
"min_bitrate_bps:32000,"
"max_bitrate_bps:3333000/");
InitEncodeTest test(
payload_name_,
{{/*active=*/true, /*bitrate_bps=*/{absl::nullopt, absl::nullopt}}},
// Expectations:
{{1280 * 720,
/*eq_bitrate_bps=*/{32000, 3333000},
/*ne_bitrate_bps=*/{absl::nullopt, absl::nullopt}}});
RunBaseTest(&test);
}
TEST_P(ResolutionBitrateLimitsTest, EncodingsApplied) {
InitEncodeTest test(payload_name_,
{{/*active=*/true, /*bitrate_bps=*/{22000, 3555000}}},
// Expectations:
{{1280 * 720,
/*eq_bitrate_bps=*/{22000, 3555000},
/*ne_bitrate_bps=*/{absl::nullopt, absl::nullopt}}});
RunBaseTest(&test);
}
TEST_P(ResolutionBitrateLimitsTest, IntersectionApplied) {
webrtc::test::ScopedFieldTrials field_trials(
"WebRTC-GetEncoderInfoOverride/"
"frame_size_pixels:921600,"
"min_start_bitrate_bps:0,"
"min_bitrate_bps:32000,"
"max_bitrate_bps:3333000/");
InitEncodeTest test(payload_name_,
{{/*active=*/true, /*bitrate_bps=*/{22000, 1555000}}},
// Expectations:
{{1280 * 720,
/*eq_bitrate_bps=*/{32000, 1555000},
/*ne_bitrate_bps=*/{absl::nullopt, absl::nullopt}}});
RunBaseTest(&test);
}
TEST_P(ResolutionBitrateLimitsTest, LimitsAppliedMiddleActive) {
webrtc::test::ScopedFieldTrials field_trials(
"WebRTC-GetEncoderInfoOverride/"
"frame_size_pixels:230400|921600,"
"min_start_bitrate_bps:0|0,"
"min_bitrate_bps:21000|32000,"
"max_bitrate_bps:2222000|3333000/");
InitEncodeTest test(
payload_name_,
{{/*active=*/false, /*bitrate_bps=*/{absl::nullopt, absl::nullopt}},
{/*active=*/true, /*bitrate_bps=*/{absl::nullopt, absl::nullopt}},
{/*active=*/false, /*bitrate_bps=*/{absl::nullopt, absl::nullopt}}},
// Expectations:
{{640 * 360,
/*eq_bitrate_bps=*/{21000, 2222000},
/*ne_bitrate_bps=*/{absl::nullopt, absl::nullopt}}});
RunBaseTest(&test);
}
TEST_P(ResolutionBitrateLimitsTest, IntersectionAppliedMiddleActive) {
webrtc::test::ScopedFieldTrials field_trials(
"WebRTC-GetEncoderInfoOverride/"
"frame_size_pixels:230400|921600,"
"min_start_bitrate_bps:0|0,"
"min_bitrate_bps:31000|32000,"
"max_bitrate_bps:2222000|3333000/");
InitEncodeTest test(
payload_name_,
{{/*active=*/false, /*bitrate_bps=*/{absl::nullopt, absl::nullopt}},
{/*active=*/true, /*bitrate_bps=*/{30000, 1555000}},
{/*active=*/false, /*bitrate_bps=*/{absl::nullopt, absl::nullopt}}},
// Expectations:
{{640 * 360,
/*eq_bitrate_bps=*/{31000, 1555000},
/*ne_bitrate_bps=*/{absl::nullopt, absl::nullopt}}});
RunBaseTest(&test);
}
TEST_P(ResolutionBitrateLimitsTest, DefaultLimitsAppliedMiddleActive) {
const absl::optional<VideoEncoder::ResolutionBitrateLimits>
kDefaultSinglecastLimits360p =
EncoderInfoSettings::GetDefaultSinglecastBitrateLimitsForResolution(
PayloadStringToCodecType(payload_name_), 640 * 360);
InitEncodeTest test(
payload_name_,
{{/*active=*/false, /*bitrate_bps=*/{absl::nullopt, absl::nullopt}},
{/*active=*/true, /*bitrate_bps=*/{absl::nullopt, absl::nullopt}},
{/*active=*/false, /*bitrate_bps=*/{absl::nullopt, absl::nullopt}}},
// Expectations:
{{640 * 360,
/*eq_bitrate_bps=*/
{kDefaultSinglecastLimits360p->min_bitrate_bps,
kDefaultSinglecastLimits360p->max_bitrate_bps},
/*ne_bitrate_bps=*/{absl::nullopt, absl::nullopt}}});
RunBaseTest(&test);
}
TEST_P(ResolutionBitrateLimitsTest, LimitsAppliedHighestActive) {
webrtc::test::ScopedFieldTrials field_trials(
"WebRTC-GetEncoderInfoOverride/"
"frame_size_pixels:230400|921600,"
"min_start_bitrate_bps:0|0,"
"min_bitrate_bps:31000|32000,"
"max_bitrate_bps:2222000|3333000/");
InitEncodeTest test(
payload_name_,
{{/*active=*/false, /*bitrate_bps=*/{absl::nullopt, absl::nullopt}},
{/*active=*/false, /*bitrate_bps=*/{absl::nullopt, absl::nullopt}},
{/*active=*/true, /*bitrate_bps=*/{absl::nullopt, absl::nullopt}}},
// Expectations:
{{1280 * 720,
/*eq_bitrate_bps=*/{32000, 3333000},
/*ne_bitrate_bps=*/{absl::nullopt, absl::nullopt}}});
RunBaseTest(&test);
}
TEST_P(ResolutionBitrateLimitsTest, IntersectionAppliedHighestActive) {
webrtc::test::ScopedFieldTrials field_trials(
"WebRTC-GetEncoderInfoOverride/"
"frame_size_pixels:230400|921600,"
"min_start_bitrate_bps:0|0,"
"min_bitrate_bps:31000|32000,"
"max_bitrate_bps:2222000|3333000/");
InitEncodeTest test(
payload_name_,
{{/*active=*/false, /*bitrate_bps=*/{absl::nullopt, absl::nullopt}},
{/*active=*/false, /*bitrate_bps=*/{absl::nullopt, absl::nullopt}},
{/*active=*/true, /*bitrate_bps=*/{30000, 1555000}}},
// Expectations:
{{1280 * 720,
/*eq_bitrate_bps=*/{32000, 1555000},
/*ne_bitrate_bps=*/{absl::nullopt, absl::nullopt}}});
RunBaseTest(&test);
}
TEST_P(ResolutionBitrateLimitsTest, LimitsNotAppliedLowestActive) {
webrtc::test::ScopedFieldTrials field_trials(
"WebRTC-GetEncoderInfoOverride/"
"frame_size_pixels:230400|921600,"
"min_start_bitrate_bps:0|0,"
"min_bitrate_bps:31000|32000,"
"max_bitrate_bps:2222000|3333000/");
InitEncodeTest test(
payload_name_,
{{/*active=*/true, /*bitrate_bps=*/{absl::nullopt, absl::nullopt}},
{/*active=*/false, /*bitrate_bps=*/{absl::nullopt, absl::nullopt}}},
// Expectations:
{{640 * 360,
/*eq_bitrate_bps=*/{absl::nullopt, absl::nullopt},
/*ne_bitrate_bps=*/{31000, 2222000}},
{1280 * 720,
/*eq_bitrate_bps=*/{absl::nullopt, absl::nullopt},
/*ne_bitrate_bps=*/{32000, 3333000}}});
RunBaseTest(&test);
}
TEST_P(ResolutionBitrateLimitsTest, LimitsNotAppliedSimulcast) {
webrtc::test::ScopedFieldTrials field_trials(
"WebRTC-GetEncoderInfoOverride/"
"frame_size_pixels:230400|921600,"
"min_start_bitrate_bps:0|0,"
"min_bitrate_bps:31000|32000,"
"max_bitrate_bps:2222000|3333000/");
InitEncodeTest test(
payload_name_,
{{/*active=*/true, /*bitrate_bps=*/{absl::nullopt, absl::nullopt}},
{/*active=*/true, /*bitrate_bps=*/{absl::nullopt, absl::nullopt}}},
// Expectations:
{{640 * 360,
/*eq_bitrate_bps=*/{absl::nullopt, absl::nullopt},
/*ne_bitrate_bps=*/{31000, 2222000}},
{1280 * 720,
/*eq_bitrate_bps=*/{absl::nullopt, absl::nullopt},
/*ne_bitrate_bps=*/{32000, 3333000}}});
RunBaseTest(&test);
}
} // namespace test
} // namespace webrtc