|  | /* | 
|  | *  Copyright (c) 2018 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 <string> | 
|  |  | 
|  | #include "media/engine/internalencoderfactory.h" | 
|  | #include "modules/video_coding/codecs/h264/include/h264.h" | 
|  | #include "modules/video_coding/codecs/vp8/include/vp8.h" | 
|  | #include "modules/video_coding/codecs/vp9/include/vp9.h" | 
|  | #include "test/call_test.h" | 
|  | #include "test/field_trial.h" | 
|  | #include "test/frame_generator_capturer.h" | 
|  | #include "test/function_video_encoder_factory.h" | 
|  |  | 
|  | namespace webrtc { | 
|  | namespace { | 
|  | constexpr int kWidth = 1280; | 
|  | constexpr int kHeight = 720; | 
|  | constexpr int kLowStartBps = 100000; | 
|  | constexpr int kHighStartBps = 600000; | 
|  | constexpr size_t kTimeoutMs = 10000;  // Some tests are expected to time out. | 
|  |  | 
|  | void SetEncoderSpecific(VideoEncoderConfig* encoder_config, | 
|  | VideoCodecType type, | 
|  | bool automatic_resize, | 
|  | bool frame_dropping) { | 
|  | if (type == kVideoCodecVP8) { | 
|  | VideoCodecVP8 vp8 = VideoEncoder::GetDefaultVp8Settings(); | 
|  | vp8.automaticResizeOn = automatic_resize; | 
|  | vp8.frameDroppingOn = frame_dropping; | 
|  | encoder_config->encoder_specific_settings = new rtc::RefCountedObject< | 
|  | VideoEncoderConfig::Vp8EncoderSpecificSettings>(vp8); | 
|  | } else if (type == kVideoCodecVP9) { | 
|  | VideoCodecVP9 vp9 = VideoEncoder::GetDefaultVp9Settings(); | 
|  | vp9.automaticResizeOn = automatic_resize; | 
|  | vp9.frameDroppingOn = frame_dropping; | 
|  | encoder_config->encoder_specific_settings = new rtc::RefCountedObject< | 
|  | VideoEncoderConfig::Vp9EncoderSpecificSettings>(vp9); | 
|  | } else if (type == kVideoCodecH264) { | 
|  | VideoCodecH264 h264 = VideoEncoder::GetDefaultH264Settings(); | 
|  | h264.frameDroppingOn = frame_dropping; | 
|  | encoder_config->encoder_specific_settings = new rtc::RefCountedObject< | 
|  | VideoEncoderConfig::H264EncoderSpecificSettings>(h264); | 
|  | } | 
|  | } | 
|  | }  // namespace | 
|  |  | 
|  | class QualityScalingTest : public test::CallTest { | 
|  | protected: | 
|  | void RunTest(VideoEncoderFactory* encoder_factory, | 
|  | const std::string& payload_name, | 
|  | int start_bps, | 
|  | bool automatic_resize, | 
|  | bool frame_dropping, | 
|  | bool expect_adaptation); | 
|  |  | 
|  | const std::string kPrefix = "WebRTC-Video-QualityScaling/Enabled-"; | 
|  | const std::string kEnd = ",0,0,0.9995,0.9999,1/"; | 
|  | }; | 
|  |  | 
|  | void QualityScalingTest::RunTest(VideoEncoderFactory* encoder_factory, | 
|  | const std::string& payload_name, | 
|  | int start_bps, | 
|  | bool automatic_resize, | 
|  | bool frame_dropping, | 
|  | bool expect_adaptation) { | 
|  | class ScalingObserver | 
|  | : public test::SendTest, | 
|  | public test::FrameGeneratorCapturer::SinkWantsObserver { | 
|  | public: | 
|  | ScalingObserver(VideoEncoderFactory* encoder_factory, | 
|  | const std::string& payload_name, | 
|  | int start_bps, | 
|  | bool automatic_resize, | 
|  | bool frame_dropping, | 
|  | bool expect_adaptation) | 
|  | : SendTest(expect_adaptation ? kDefaultTimeoutMs : kTimeoutMs), | 
|  | encoder_factory_(encoder_factory), | 
|  | payload_name_(payload_name), | 
|  | start_bps_(start_bps), | 
|  | automatic_resize_(automatic_resize), | 
|  | frame_dropping_(frame_dropping), | 
|  | expect_adaptation_(expect_adaptation) {} | 
|  |  | 
|  | private: | 
|  | void OnFrameGeneratorCapturerCreated( | 
|  | test::FrameGeneratorCapturer* frame_generator_capturer) override { | 
|  | frame_generator_capturer->SetSinkWantsObserver(this); | 
|  | // Set initial resolution. | 
|  | frame_generator_capturer->ChangeResolution(kWidth, kHeight); | 
|  | } | 
|  |  | 
|  | // Called when FrameGeneratorCapturer::AddOrUpdateSink is called. | 
|  | void OnSinkWantsChanged(rtc::VideoSinkInterface<VideoFrame>* sink, | 
|  | const rtc::VideoSinkWants& wants) override { | 
|  | EXPECT_LT(wants.max_pixel_count, kWidth * kHeight) << "Not a downscale."; | 
|  | observation_complete_.Set(); | 
|  | } | 
|  |  | 
|  | Call::Config GetSenderCallConfig() override { | 
|  | Call::Config config(event_log_.get()); | 
|  | config.bitrate_config.start_bitrate_bps = start_bps_; | 
|  | return config; | 
|  | } | 
|  |  | 
|  | 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 = kVideoSendPayloadType; | 
|  | const VideoCodecType codec_type = PayloadStringToCodecType(payload_name_); | 
|  | encoder_config->codec_type = codec_type; | 
|  | encoder_config->max_bitrate_bps = start_bps_; | 
|  | SetEncoderSpecific(encoder_config, codec_type, automatic_resize_, | 
|  | frame_dropping_); | 
|  | } | 
|  |  | 
|  | void PerformTest() override { | 
|  | EXPECT_EQ(expect_adaptation_, Wait()) | 
|  | << "Timed out while waiting for a scale down."; | 
|  | } | 
|  |  | 
|  | VideoEncoderFactory* const encoder_factory_; | 
|  | const std::string payload_name_; | 
|  | const int start_bps_; | 
|  | const bool automatic_resize_; | 
|  | const bool frame_dropping_; | 
|  | const bool expect_adaptation_; | 
|  | } test(encoder_factory, payload_name, start_bps, automatic_resize, | 
|  | frame_dropping, expect_adaptation); | 
|  |  | 
|  | RunBaseTest(&test); | 
|  | } | 
|  |  | 
|  | TEST_F(QualityScalingTest, AdaptsDownForHighQp_Vp8) { | 
|  | // VP8 QP thresholds, low:1, high:1 -> high QP. | 
|  | test::ScopedFieldTrials field_trials(kPrefix + "1,1,0,0,0,0" + kEnd); | 
|  |  | 
|  | // QualityScaler enabled. | 
|  | const bool kAutomaticResize = true; | 
|  | const bool kFrameDropping = true; | 
|  | const bool kExpectAdapt = true; | 
|  |  | 
|  | test::FunctionVideoEncoderFactory encoder_factory( | 
|  | []() { return VP8Encoder::Create(); }); | 
|  | RunTest(&encoder_factory, "VP8", kHighStartBps, kAutomaticResize, | 
|  | kFrameDropping, kExpectAdapt); | 
|  | } | 
|  |  | 
|  | TEST_F(QualityScalingTest, NoAdaptDownForHighQpWithResizeOff_Vp8) { | 
|  | // VP8 QP thresholds, low:1, high:1 -> high QP. | 
|  | test::ScopedFieldTrials field_trials(kPrefix + "1,1,0,0,0,0" + kEnd); | 
|  |  | 
|  | // QualityScaler disabled. | 
|  | const bool kAutomaticResize = false; | 
|  | const bool kFrameDropping = true; | 
|  | const bool kExpectAdapt = false; | 
|  |  | 
|  | test::FunctionVideoEncoderFactory encoder_factory( | 
|  | []() { return VP8Encoder::Create(); }); | 
|  | RunTest(&encoder_factory, "VP8", kHighStartBps, kAutomaticResize, | 
|  | kFrameDropping, kExpectAdapt); | 
|  | } | 
|  |  | 
|  | TEST_F(QualityScalingTest, NoAdaptDownForHighQpWithFrameDroppingOff_Vp8) { | 
|  | // VP8 QP thresholds, low:1, high:1 -> high QP. | 
|  | test::ScopedFieldTrials field_trials(kPrefix + "1,1,0,0,0,0" + kEnd); | 
|  |  | 
|  | // QualityScaler disabled. | 
|  | const bool kAutomaticResize = true; | 
|  | const bool kFrameDropping = false; | 
|  | const bool kExpectAdapt = false; | 
|  |  | 
|  | test::FunctionVideoEncoderFactory encoder_factory( | 
|  | []() { return VP8Encoder::Create(); }); | 
|  | RunTest(&encoder_factory, "VP8", kHighStartBps, kAutomaticResize, | 
|  | kFrameDropping, kExpectAdapt); | 
|  | } | 
|  |  | 
|  | TEST_F(QualityScalingTest, NoAdaptDownForNormalQp_Vp8) { | 
|  | // VP8 QP thresholds, low:1, high:127 -> normal QP. | 
|  | test::ScopedFieldTrials field_trials(kPrefix + "1,127,0,0,0,0" + kEnd); | 
|  |  | 
|  | // QualityScaler enabled. | 
|  | const bool kAutomaticResize = true; | 
|  | const bool kFrameDropping = true; | 
|  | const bool kExpectAdapt = false; | 
|  |  | 
|  | test::FunctionVideoEncoderFactory encoder_factory( | 
|  | []() { return VP8Encoder::Create(); }); | 
|  | RunTest(&encoder_factory, "VP8", kHighStartBps, kAutomaticResize, | 
|  | kFrameDropping, kExpectAdapt); | 
|  | } | 
|  |  | 
|  | TEST_F(QualityScalingTest, AdaptsDownForLowStartBitrate) { | 
|  | // VP8 QP thresholds, low:1, high:127 -> normal QP. | 
|  | test::ScopedFieldTrials field_trials(kPrefix + "1,127,0,0,0,0" + kEnd); | 
|  |  | 
|  | // QualityScaler enabled. | 
|  | const bool kAutomaticResize = true; | 
|  | const bool kFrameDropping = true; | 
|  | const bool kExpectAdapt = true; | 
|  |  | 
|  | test::FunctionVideoEncoderFactory encoder_factory( | 
|  | []() { return VP8Encoder::Create(); }); | 
|  | RunTest(&encoder_factory, "VP8", kLowStartBps, kAutomaticResize, | 
|  | kFrameDropping, kExpectAdapt); | 
|  | } | 
|  |  | 
|  | TEST_F(QualityScalingTest, NoAdaptDownForLowStartBitrateWithScalingOff) { | 
|  | // VP8 QP thresholds, low:1, high:127 -> normal QP. | 
|  | test::ScopedFieldTrials field_trials(kPrefix + "1,127,0,0,0,0" + kEnd); | 
|  |  | 
|  | // QualityScaler disabled. | 
|  | const bool kAutomaticResize = false; | 
|  | const bool kFrameDropping = true; | 
|  | const bool kExpectAdapt = false; | 
|  |  | 
|  | test::FunctionVideoEncoderFactory encoder_factory( | 
|  | []() { return VP8Encoder::Create(); }); | 
|  | RunTest(&encoder_factory, "VP8", kLowStartBps, kAutomaticResize, | 
|  | kFrameDropping, kExpectAdapt); | 
|  | } | 
|  |  | 
|  | TEST_F(QualityScalingTest, NoAdaptDownForHighQp_Vp9) { | 
|  | // VP9 QP thresholds, low:1, high:1 -> high QP. | 
|  | test::ScopedFieldTrials field_trials(kPrefix + "0,0,1,1,0,0" + kEnd); | 
|  |  | 
|  | // QualityScaler always disabled. | 
|  | const bool kAutomaticResize = true; | 
|  | const bool kFrameDropping = true; | 
|  | const bool kExpectAdapt = false; | 
|  |  | 
|  | test::FunctionVideoEncoderFactory encoder_factory( | 
|  | []() { return VP9Encoder::Create(); }); | 
|  | RunTest(&encoder_factory, "VP9", kHighStartBps, kAutomaticResize, | 
|  | kFrameDropping, kExpectAdapt); | 
|  | } | 
|  |  | 
|  | #if defined(WEBRTC_USE_H264) | 
|  | TEST_F(QualityScalingTest, AdaptsDownForHighQp_H264) { | 
|  | // H264 QP thresholds, low:1, high:1 -> high QP. | 
|  | test::ScopedFieldTrials field_trials(kPrefix + "0,0,0,0,1,1" + kEnd); | 
|  |  | 
|  | // QualityScaler always enabled. | 
|  | const bool kAutomaticResize = false; | 
|  | const bool kFrameDropping = false; | 
|  | const bool kExpectAdapt = true; | 
|  |  | 
|  | test::FunctionVideoEncoderFactory encoder_factory( | 
|  | []() { return H264Encoder::Create(cricket::VideoCodec("H264")); }); | 
|  | RunTest(&encoder_factory, "H264", kHighStartBps, kAutomaticResize, | 
|  | kFrameDropping, kExpectAdapt); | 
|  | } | 
|  | #endif  // defined(WEBRTC_USE_H264) | 
|  |  | 
|  | }  // namespace webrtc |