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