| /* |
| * 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 = |
| rtc::make_ref_counted<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 = |
| rtc::make_ref_counted<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 |