1 /*
2  *  Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #include <string>
12 
13 #include "api/test/video/function_video_encoder_factory.h"
14 #include "media/engine/internal_encoder_factory.h"
15 #include "modules/video_coding/codecs/h264/include/h264.h"
16 #include "modules/video_coding/codecs/vp8/include/vp8.h"
17 #include "modules/video_coding/codecs/vp9/include/vp9.h"
18 #include "test/call_test.h"
19 #include "test/field_trial.h"
20 #include "test/frame_generator_capturer.h"
21 
22 namespace webrtc {
23 namespace {
24 constexpr int kWidth = 1280;
25 constexpr int kHeight = 720;
26 constexpr int kLowStartBps = 100000;
27 constexpr int kHighStartBps = 600000;
28 constexpr size_t kTimeoutMs = 10000;  // Some tests are expected to time out.
29 
SetEncoderSpecific(VideoEncoderConfig * encoder_config,VideoCodecType type,bool automatic_resize,bool frame_dropping)30 void SetEncoderSpecific(VideoEncoderConfig* encoder_config,
31                         VideoCodecType type,
32                         bool automatic_resize,
33                         bool frame_dropping) {
34   if (type == kVideoCodecVP8) {
35     VideoCodecVP8 vp8 = VideoEncoder::GetDefaultVp8Settings();
36     vp8.automaticResizeOn = automatic_resize;
37     vp8.frameDroppingOn = frame_dropping;
38     encoder_config->encoder_specific_settings = new rtc::RefCountedObject<
39         VideoEncoderConfig::Vp8EncoderSpecificSettings>(vp8);
40   } else if (type == kVideoCodecVP9) {
41     VideoCodecVP9 vp9 = VideoEncoder::GetDefaultVp9Settings();
42     vp9.automaticResizeOn = automatic_resize;
43     vp9.frameDroppingOn = frame_dropping;
44     encoder_config->encoder_specific_settings = new rtc::RefCountedObject<
45         VideoEncoderConfig::Vp9EncoderSpecificSettings>(vp9);
46   } else if (type == kVideoCodecH264) {
47     VideoCodecH264 h264 = VideoEncoder::GetDefaultH264Settings();
48     h264.frameDroppingOn = frame_dropping;
49     encoder_config->encoder_specific_settings = new rtc::RefCountedObject<
50         VideoEncoderConfig::H264EncoderSpecificSettings>(h264);
51   }
52 }
53 }  // namespace
54 
55 class QualityScalingTest : public test::CallTest {
56  protected:
57   void RunTest(VideoEncoderFactory* encoder_factory,
58                const std::string& payload_name,
59                int start_bps,
60                bool automatic_resize,
61                bool frame_dropping,
62                bool expect_adaptation);
63 
64   const std::string kPrefix = "WebRTC-Video-QualityScaling/Enabled-";
65   const std::string kEnd = ",0,0,0.9995,0.9999,1/";
66 };
67 
RunTest(VideoEncoderFactory * encoder_factory,const std::string & payload_name,int start_bps,bool automatic_resize,bool frame_dropping,bool expect_adaptation)68 void QualityScalingTest::RunTest(VideoEncoderFactory* encoder_factory,
69                                  const std::string& payload_name,
70                                  int start_bps,
71                                  bool automatic_resize,
72                                  bool frame_dropping,
73                                  bool expect_adaptation) {
74   class ScalingObserver
75       : public test::SendTest,
76         public test::FrameGeneratorCapturer::SinkWantsObserver {
77    public:
78     ScalingObserver(VideoEncoderFactory* encoder_factory,
79                     const std::string& payload_name,
80                     int start_bps,
81                     bool automatic_resize,
82                     bool frame_dropping,
83                     bool expect_adaptation)
84         : SendTest(expect_adaptation ? kDefaultTimeoutMs : kTimeoutMs),
85           encoder_factory_(encoder_factory),
86           payload_name_(payload_name),
87           start_bps_(start_bps),
88           automatic_resize_(automatic_resize),
89           frame_dropping_(frame_dropping),
90           expect_adaptation_(expect_adaptation) {}
91 
92    private:
93     void OnFrameGeneratorCapturerCreated(
94         test::FrameGeneratorCapturer* frame_generator_capturer) override {
95       frame_generator_capturer->SetSinkWantsObserver(this);
96       // Set initial resolution.
97       frame_generator_capturer->ChangeResolution(kWidth, kHeight);
98     }
99 
100     // Called when FrameGeneratorCapturer::AddOrUpdateSink is called.
101     void OnSinkWantsChanged(rtc::VideoSinkInterface<VideoFrame>* sink,
102                             const rtc::VideoSinkWants& wants) override {
103       if (wants.max_pixel_count < kWidth * kHeight)
104         observation_complete_.Set();
105     }
106     void ModifySenderBitrateConfig(
107         BitrateConstraints* bitrate_config) override {
108       bitrate_config->start_bitrate_bps = start_bps_;
109     }
110 
111     void ModifyVideoConfigs(
112         VideoSendStream::Config* send_config,
113         std::vector<VideoReceiveStream::Config>* receive_configs,
114         VideoEncoderConfig* encoder_config) override {
115       send_config->encoder_settings.encoder_factory = encoder_factory_;
116       send_config->rtp.payload_name = payload_name_;
117       send_config->rtp.payload_type = kVideoSendPayloadType;
118       const VideoCodecType codec_type = PayloadStringToCodecType(payload_name_);
119       encoder_config->codec_type = codec_type;
120       encoder_config->max_bitrate_bps = start_bps_;
121       SetEncoderSpecific(encoder_config, codec_type, automatic_resize_,
122                          frame_dropping_);
123     }
124 
125     void PerformTest() override {
126       EXPECT_EQ(expect_adaptation_, Wait())
127           << "Timed out while waiting for a scale down.";
128     }
129 
130     VideoEncoderFactory* const encoder_factory_;
131     const std::string payload_name_;
132     const int start_bps_;
133     const bool automatic_resize_;
134     const bool frame_dropping_;
135     const bool expect_adaptation_;
136   } test(encoder_factory, payload_name, start_bps, automatic_resize,
137          frame_dropping, expect_adaptation);
138 
139   RunBaseTest(&test);
140 }
141 
TEST_F(QualityScalingTest,AdaptsDownForHighQp_Vp8)142 TEST_F(QualityScalingTest, AdaptsDownForHighQp_Vp8) {
143   // VP8 QP thresholds, low:1, high:1 -> high QP.
144   test::ScopedFieldTrials field_trials(kPrefix + "1,1,0,0,0,0" + kEnd);
145 
146   // QualityScaler enabled.
147   const bool kAutomaticResize = true;
148   const bool kFrameDropping = true;
149   const bool kExpectAdapt = true;
150 
151   test::FunctionVideoEncoderFactory encoder_factory(
152       []() { return VP8Encoder::Create(); });
153   RunTest(&encoder_factory, "VP8", kHighStartBps, kAutomaticResize,
154           kFrameDropping, kExpectAdapt);
155 }
156 
TEST_F(QualityScalingTest,NoAdaptDownForHighQpWithResizeOff_Vp8)157 TEST_F(QualityScalingTest, NoAdaptDownForHighQpWithResizeOff_Vp8) {
158   // VP8 QP thresholds, low:1, high:1 -> high QP.
159   test::ScopedFieldTrials field_trials(kPrefix + "1,1,0,0,0,0" + kEnd);
160 
161   // QualityScaler disabled.
162   const bool kAutomaticResize = false;
163   const bool kFrameDropping = true;
164   const bool kExpectAdapt = false;
165 
166   test::FunctionVideoEncoderFactory encoder_factory(
167       []() { return VP8Encoder::Create(); });
168   RunTest(&encoder_factory, "VP8", kHighStartBps, kAutomaticResize,
169           kFrameDropping, kExpectAdapt);
170 }
171 
172 // TODO(bugs.webrtc.org/10388): Fix and re-enable.
TEST_F(QualityScalingTest,DISABLED_NoAdaptDownForHighQpWithFrameDroppingOff_Vp8)173 TEST_F(QualityScalingTest,
174        DISABLED_NoAdaptDownForHighQpWithFrameDroppingOff_Vp8) {
175   // VP8 QP thresholds, low:1, high:1 -> high QP.
176   test::ScopedFieldTrials field_trials(kPrefix + "1,1,0,0,0,0" + kEnd);
177 
178   // QualityScaler disabled.
179   const bool kAutomaticResize = true;
180   const bool kFrameDropping = false;
181   const bool kExpectAdapt = false;
182 
183   test::FunctionVideoEncoderFactory encoder_factory(
184       []() { return VP8Encoder::Create(); });
185   RunTest(&encoder_factory, "VP8", kHighStartBps, kAutomaticResize,
186           kFrameDropping, kExpectAdapt);
187 }
188 
TEST_F(QualityScalingTest,NoAdaptDownForNormalQp_Vp8)189 TEST_F(QualityScalingTest, NoAdaptDownForNormalQp_Vp8) {
190   // VP8 QP thresholds, low:1, high:127 -> normal QP.
191   test::ScopedFieldTrials field_trials(kPrefix + "1,127,0,0,0,0" + kEnd);
192 
193   // QualityScaler enabled.
194   const bool kAutomaticResize = true;
195   const bool kFrameDropping = true;
196   const bool kExpectAdapt = false;
197 
198   test::FunctionVideoEncoderFactory encoder_factory(
199       []() { return VP8Encoder::Create(); });
200   RunTest(&encoder_factory, "VP8", kHighStartBps, kAutomaticResize,
201           kFrameDropping, kExpectAdapt);
202 }
203 
TEST_F(QualityScalingTest,AdaptsDownForLowStartBitrate)204 TEST_F(QualityScalingTest, AdaptsDownForLowStartBitrate) {
205   // VP8 QP thresholds, low:1, high:127 -> normal QP.
206   test::ScopedFieldTrials field_trials(kPrefix + "1,127,0,0,0,0" + kEnd);
207 
208   // QualityScaler enabled.
209   const bool kAutomaticResize = true;
210   const bool kFrameDropping = true;
211   const bool kExpectAdapt = true;
212 
213   test::FunctionVideoEncoderFactory encoder_factory(
214       []() { return VP8Encoder::Create(); });
215   RunTest(&encoder_factory, "VP8", kLowStartBps, kAutomaticResize,
216           kFrameDropping, kExpectAdapt);
217 }
218 
TEST_F(QualityScalingTest,NoAdaptDownForLowStartBitrateWithScalingOff)219 TEST_F(QualityScalingTest, NoAdaptDownForLowStartBitrateWithScalingOff) {
220   // VP8 QP thresholds, low:1, high:127 -> normal QP.
221   test::ScopedFieldTrials field_trials(kPrefix + "1,127,0,0,0,0" + kEnd);
222 
223   // QualityScaler disabled.
224   const bool kAutomaticResize = false;
225   const bool kFrameDropping = true;
226   const bool kExpectAdapt = false;
227 
228   test::FunctionVideoEncoderFactory encoder_factory(
229       []() { return VP8Encoder::Create(); });
230   RunTest(&encoder_factory, "VP8", kLowStartBps, kAutomaticResize,
231           kFrameDropping, kExpectAdapt);
232 }
233 
TEST_F(QualityScalingTest,NoAdaptDownForHighQp_Vp9)234 TEST_F(QualityScalingTest, NoAdaptDownForHighQp_Vp9) {
235   // VP9 QP thresholds, low:1, high:1 -> high QP.
236   test::ScopedFieldTrials field_trials(kPrefix + "0,0,1,1,0,0" + kEnd +
237                                        "WebRTC-VP9QualityScaler/Disabled/");
238 
239   // QualityScaler always disabled.
240   const bool kAutomaticResize = true;
241   const bool kFrameDropping = true;
242   const bool kExpectAdapt = false;
243 
244   test::FunctionVideoEncoderFactory encoder_factory(
245       []() { return VP9Encoder::Create(); });
246   RunTest(&encoder_factory, "VP9", kHighStartBps, kAutomaticResize,
247           kFrameDropping, kExpectAdapt);
248 }
249 
250 #if defined(WEBRTC_USE_H264)
TEST_F(QualityScalingTest,AdaptsDownForHighQp_H264)251 TEST_F(QualityScalingTest, AdaptsDownForHighQp_H264) {
252   // H264 QP thresholds, low:1, high:1 -> high QP.
253   test::ScopedFieldTrials field_trials(kPrefix + "0,0,0,0,1,1" + kEnd);
254 
255   // QualityScaler always enabled.
256   const bool kAutomaticResize = false;
257   const bool kFrameDropping = false;
258   const bool kExpectAdapt = true;
259 
260   test::FunctionVideoEncoderFactory encoder_factory(
261       []() { return H264Encoder::Create(cricket::VideoCodec("H264")); });
262   RunTest(&encoder_factory, "H264", kHighStartBps, kAutomaticResize,
263           kFrameDropping, kExpectAdapt);
264 }
265 #endif  // defined(WEBRTC_USE_H264)
266 
267 }  // namespace webrtc
268