1 /*
2 * Copyright (c) 2015 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 "api/video_codecs/video_decoder_software_fallback_wrapper.h"
12
13 #include <stdint.h>
14
15 #include "absl/types/optional.h"
16 #include "api/video/encoded_image.h"
17 #include "api/video/video_frame.h"
18 #include "api/video_codecs/video_decoder.h"
19 #include "modules/video_coding/codecs/vp8/include/vp8.h"
20 #include "modules/video_coding/include/video_codec_interface.h"
21 #include "modules/video_coding/include/video_error_codes.h"
22 #include "rtc_base/checks.h"
23 #include "test/field_trial.h"
24 #include "test/gtest.h"
25
26 namespace webrtc {
27
28 class VideoDecoderSoftwareFallbackWrapperTest : public ::testing::Test {
29 protected:
VideoDecoderSoftwareFallbackWrapperTest()30 VideoDecoderSoftwareFallbackWrapperTest()
31 : VideoDecoderSoftwareFallbackWrapperTest("") {}
VideoDecoderSoftwareFallbackWrapperTest(const std::string & field_trials)32 explicit VideoDecoderSoftwareFallbackWrapperTest(
33 const std::string& field_trials)
34 : override_field_trials_(field_trials),
35 fake_decoder_(new CountingFakeDecoder()),
36 fallback_wrapper_(CreateVideoDecoderSoftwareFallbackWrapper(
37 std::unique_ptr<VideoDecoder>(VP8Decoder::Create()),
38 std::unique_ptr<VideoDecoder>(fake_decoder_))) {}
39
40 class CountingFakeDecoder : public VideoDecoder {
41 public:
Configure(const Settings & settings)42 bool Configure(const Settings& settings) override {
43 ++configure_count_;
44 return configure_return_value_;
45 }
46
Decode(const EncodedImage & input_image,bool missing_frames,int64_t render_time_ms)47 int32_t Decode(const EncodedImage& input_image,
48 bool missing_frames,
49 int64_t render_time_ms) override {
50 ++decode_count_;
51 return decode_return_code_;
52 }
53
RegisterDecodeCompleteCallback(DecodedImageCallback * callback)54 int32_t RegisterDecodeCompleteCallback(
55 DecodedImageCallback* callback) override {
56 decode_complete_callback_ = callback;
57 return WEBRTC_VIDEO_CODEC_OK;
58 }
59
Release()60 int32_t Release() override {
61 ++release_count_;
62 return WEBRTC_VIDEO_CODEC_OK;
63 }
64
ImplementationName() const65 const char* ImplementationName() const override { return "fake-decoder"; }
66
67 int configure_count_ = 0;
68 int decode_count_ = 0;
69 bool configure_return_value_ = true;
70 int32_t decode_return_code_ = WEBRTC_VIDEO_CODEC_OK;
71 DecodedImageCallback* decode_complete_callback_ = nullptr;
72 int release_count_ = 0;
73 int reset_count_ = 0;
74 };
75 test::ScopedFieldTrials override_field_trials_;
76 // `fake_decoder_` is owned and released by `fallback_wrapper_`.
77 CountingFakeDecoder* fake_decoder_;
78 std::unique_ptr<VideoDecoder> fallback_wrapper_;
79 };
80
TEST_F(VideoDecoderSoftwareFallbackWrapperTest,InitializesDecoder)81 TEST_F(VideoDecoderSoftwareFallbackWrapperTest, InitializesDecoder) {
82 fallback_wrapper_->Configure({});
83 EXPECT_EQ(1, fake_decoder_->configure_count_);
84
85 EncodedImage encoded_image;
86 encoded_image._frameType = VideoFrameType::kVideoFrameKey;
87 fallback_wrapper_->Decode(encoded_image, false, -1);
88 EXPECT_EQ(1, fake_decoder_->configure_count_)
89 << "Initialized decoder should not be reinitialized.";
90 EXPECT_EQ(1, fake_decoder_->decode_count_);
91 }
92
TEST_F(VideoDecoderSoftwareFallbackWrapperTest,UsesFallbackDecoderAfterAnyInitDecodeFailure)93 TEST_F(VideoDecoderSoftwareFallbackWrapperTest,
94 UsesFallbackDecoderAfterAnyInitDecodeFailure) {
95 fake_decoder_->configure_return_value_ = false;
96 fallback_wrapper_->Configure({});
97 EXPECT_EQ(1, fake_decoder_->configure_count_);
98
99 EncodedImage encoded_image;
100 encoded_image._frameType = VideoFrameType::kVideoFrameKey;
101 fallback_wrapper_->Decode(encoded_image, false, -1);
102 EXPECT_EQ(1, fake_decoder_->configure_count_)
103 << "Should not have attempted reinitializing the fallback decoder on "
104 "keyframe.";
105 // Unfortunately faking a VP8 frame is hard. Rely on no Decode -> using SW
106 // decoder.
107 EXPECT_EQ(0, fake_decoder_->decode_count_)
108 << "Decoder used even though no InitDecode had succeeded.";
109 }
110
TEST_F(VideoDecoderSoftwareFallbackWrapperTest,IsSoftwareFallbackSticky)111 TEST_F(VideoDecoderSoftwareFallbackWrapperTest, IsSoftwareFallbackSticky) {
112 fallback_wrapper_->Configure({});
113
114 fake_decoder_->decode_return_code_ = WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
115 EncodedImage encoded_image;
116 fallback_wrapper_->Decode(encoded_image, false, -1);
117 EXPECT_EQ(1, fake_decoder_->decode_count_);
118
119 // Software fallback should be sticky, fake_decoder_ shouldn't be used.
120 encoded_image._frameType = VideoFrameType::kVideoFrameKey;
121 fallback_wrapper_->Decode(encoded_image, false, -1);
122 EXPECT_EQ(1, fake_decoder_->decode_count_)
123 << "Decoder shouldn't be used after failure.";
124
125 // fake_decoder_ should have only been initialized once during the test.
126 EXPECT_EQ(1, fake_decoder_->configure_count_);
127 }
128
TEST_F(VideoDecoderSoftwareFallbackWrapperTest,DoesNotFallbackOnEveryError)129 TEST_F(VideoDecoderSoftwareFallbackWrapperTest, DoesNotFallbackOnEveryError) {
130 fallback_wrapper_->Configure({});
131 fake_decoder_->decode_return_code_ = WEBRTC_VIDEO_CODEC_ERROR;
132 EncodedImage encoded_image;
133 EXPECT_EQ(fake_decoder_->decode_return_code_,
134 fallback_wrapper_->Decode(encoded_image, false, -1));
135 EXPECT_EQ(1, fake_decoder_->decode_count_);
136
137 fallback_wrapper_->Decode(encoded_image, false, -1);
138 EXPECT_EQ(2, fake_decoder_->decode_count_)
139 << "Decoder should be active even though previous decode failed.";
140 }
141
TEST_F(VideoDecoderSoftwareFallbackWrapperTest,UsesHwDecoderAfterReinit)142 TEST_F(VideoDecoderSoftwareFallbackWrapperTest, UsesHwDecoderAfterReinit) {
143 fallback_wrapper_->Configure({});
144
145 fake_decoder_->decode_return_code_ = WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
146 EncodedImage encoded_image;
147 fallback_wrapper_->Decode(encoded_image, false, -1);
148 EXPECT_EQ(1, fake_decoder_->decode_count_);
149
150 fallback_wrapper_->Release();
151 fallback_wrapper_->Configure({});
152
153 fake_decoder_->decode_return_code_ = WEBRTC_VIDEO_CODEC_OK;
154 fallback_wrapper_->Decode(encoded_image, false, -1);
155 EXPECT_EQ(2, fake_decoder_->decode_count_)
156 << "Should not be using fallback after reinit.";
157 }
158
TEST_F(VideoDecoderSoftwareFallbackWrapperTest,ForwardsReleaseCall)159 TEST_F(VideoDecoderSoftwareFallbackWrapperTest, ForwardsReleaseCall) {
160 fallback_wrapper_->Configure({});
161 fallback_wrapper_->Release();
162 EXPECT_EQ(1, fake_decoder_->release_count_);
163
164 fallback_wrapper_->Configure({});
165 fake_decoder_->decode_return_code_ = WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
166 EncodedImage encoded_image;
167 fallback_wrapper_->Decode(encoded_image, false, -1);
168 EXPECT_EQ(2, fake_decoder_->release_count_)
169 << "Decoder should be released during fallback.";
170 fallback_wrapper_->Release();
171 EXPECT_EQ(2, fake_decoder_->release_count_);
172 }
173
174 // TODO(pbos): Fake a VP8 frame well enough to actually receive a callback from
175 // the software decoder.
TEST_F(VideoDecoderSoftwareFallbackWrapperTest,ForwardsRegisterDecodeCompleteCallback)176 TEST_F(VideoDecoderSoftwareFallbackWrapperTest,
177 ForwardsRegisterDecodeCompleteCallback) {
178 class FakeDecodedImageCallback : public DecodedImageCallback {
179 int32_t Decoded(VideoFrame& decodedImage) override { return 0; }
180 int32_t Decoded(webrtc::VideoFrame& decodedImage,
181 int64_t decode_time_ms) override {
182 RTC_DCHECK_NOTREACHED();
183 return -1;
184 }
185 void Decoded(webrtc::VideoFrame& decodedImage,
186 absl::optional<int32_t> decode_time_ms,
187 absl::optional<uint8_t> qp) override {
188 RTC_DCHECK_NOTREACHED();
189 }
190 } callback;
191
192 fallback_wrapper_->Configure({});
193 fallback_wrapper_->RegisterDecodeCompleteCallback(&callback);
194 EXPECT_EQ(&callback, fake_decoder_->decode_complete_callback_);
195 }
196
TEST_F(VideoDecoderSoftwareFallbackWrapperTest,ReportsFallbackImplementationName)197 TEST_F(VideoDecoderSoftwareFallbackWrapperTest,
198 ReportsFallbackImplementationName) {
199 fallback_wrapper_->Configure({});
200
201 fake_decoder_->decode_return_code_ = WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
202 EncodedImage encoded_image;
203 fallback_wrapper_->Decode(encoded_image, false, -1);
204 // Hard coded expected value since libvpx is the software implementation name
205 // for VP8. Change accordingly if the underlying implementation does.
206 EXPECT_STREQ("libvpx (fallback from: fake-decoder)",
207 fallback_wrapper_->ImplementationName());
208 fallback_wrapper_->Release();
209 }
210
TEST_F(VideoDecoderSoftwareFallbackWrapperTest,FallbacksOnTooManyErrors)211 TEST_F(VideoDecoderSoftwareFallbackWrapperTest, FallbacksOnTooManyErrors) {
212 fallback_wrapper_->Configure({});
213
214 fake_decoder_->decode_return_code_ = WEBRTC_VIDEO_CODEC_ERROR;
215 EncodedImage encoded_image;
216 encoded_image._frameType = VideoFrameType::kVideoFrameKey;
217 // Doesn't fallback from a single error.
218 fallback_wrapper_->Decode(encoded_image, false, -1);
219 EXPECT_STREQ("fake-decoder", fallback_wrapper_->ImplementationName());
220
221 // However, many frames with the same error, fallback should happen.
222 const int kNumFramesToEncode = 10;
223 for (int i = 0; i < kNumFramesToEncode; ++i) {
224 fallback_wrapper_->Decode(encoded_image, false, -1);
225 }
226 // Hard coded expected value since libvpx is the software implementation name
227 // for VP8. Change accordingly if the underlying implementation does.
228 EXPECT_STREQ("libvpx (fallback from: fake-decoder)",
229 fallback_wrapper_->ImplementationName());
230 fallback_wrapper_->Release();
231 }
232
TEST_F(VideoDecoderSoftwareFallbackWrapperTest,DoesNotFallbackOnDeltaFramesErrors)233 TEST_F(VideoDecoderSoftwareFallbackWrapperTest,
234 DoesNotFallbackOnDeltaFramesErrors) {
235 fallback_wrapper_->Configure({});
236
237 fake_decoder_->decode_return_code_ = WEBRTC_VIDEO_CODEC_ERROR;
238 EncodedImage encoded_image;
239 encoded_image._frameType = VideoFrameType::kVideoFrameDelta;
240
241 // Many decoded frames with the same error
242 const int kNumFramesToEncode = 10;
243 for (int i = 0; i < kNumFramesToEncode; ++i) {
244 fallback_wrapper_->Decode(encoded_image, false, -1);
245 }
246 EXPECT_STREQ("fake-decoder", fallback_wrapper_->ImplementationName());
247
248 fallback_wrapper_->Release();
249 }
250
TEST_F(VideoDecoderSoftwareFallbackWrapperTest,DoesNotFallbacksOnNonConsequtiveErrors)251 TEST_F(VideoDecoderSoftwareFallbackWrapperTest,
252 DoesNotFallbacksOnNonConsequtiveErrors) {
253 fallback_wrapper_->Configure({});
254
255 EncodedImage encoded_image;
256 encoded_image._frameType = VideoFrameType::kVideoFrameKey;
257
258 const int kNumFramesToEncode = 10;
259 for (int i = 0; i < kNumFramesToEncode; ++i) {
260 // Interleaved errors and successful decodes.
261 fake_decoder_->decode_return_code_ = WEBRTC_VIDEO_CODEC_ERROR;
262 fallback_wrapper_->Decode(encoded_image, false, -1);
263 fake_decoder_->decode_return_code_ = WEBRTC_VIDEO_CODEC_OK;
264 fallback_wrapper_->Decode(encoded_image, false, -1);
265 }
266 EXPECT_STREQ("fake-decoder", fallback_wrapper_->ImplementationName());
267 fallback_wrapper_->Release();
268 }
269
270 class ForcedSoftwareDecoderFallbackTest
271 : public VideoDecoderSoftwareFallbackWrapperTest {
272 public:
ForcedSoftwareDecoderFallbackTest()273 ForcedSoftwareDecoderFallbackTest()
274 : VideoDecoderSoftwareFallbackWrapperTest(
275 "WebRTC-Video-ForcedSwDecoderFallback/Enabled/") {
276 fake_decoder_ = new CountingFakeDecoder();
277 sw_fallback_decoder_ = new CountingFakeDecoder();
278 fallback_wrapper_ = CreateVideoDecoderSoftwareFallbackWrapper(
279 std::unique_ptr<VideoDecoder>(sw_fallback_decoder_),
280 std::unique_ptr<VideoDecoder>(fake_decoder_));
281 }
282
283 CountingFakeDecoder* sw_fallback_decoder_;
284 };
285
TEST_F(ForcedSoftwareDecoderFallbackTest,UsesForcedFallback)286 TEST_F(ForcedSoftwareDecoderFallbackTest, UsesForcedFallback) {
287 fallback_wrapper_->Configure({});
288 EXPECT_EQ(1, sw_fallback_decoder_->configure_count_);
289
290 EncodedImage encoded_image;
291 encoded_image._frameType = VideoFrameType::kVideoFrameKey;
292 fallback_wrapper_->Decode(encoded_image, false, -1);
293 EXPECT_EQ(1, sw_fallback_decoder_->configure_count_);
294 EXPECT_EQ(1, sw_fallback_decoder_->decode_count_);
295
296 fallback_wrapper_->Release();
297 EXPECT_EQ(1, sw_fallback_decoder_->release_count_);
298
299 // Only fallback decoder should have been used.
300 EXPECT_EQ(0, fake_decoder_->configure_count_);
301 EXPECT_EQ(0, fake_decoder_->decode_count_);
302 EXPECT_EQ(0, fake_decoder_->release_count_);
303 }
304
305 } // namespace webrtc
306