Fallback to software decoders on consequtive decode errors on key-frames
Bug: webrtc:11575
Change-Id: I09be17ab5155e9f610c8f7c451ca52d7d65e24d1
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/175222
Commit-Queue: Ilya Nikolaevskiy <ilnik@webrtc.org>
Reviewed-by: Rasmus Brandt <brandtr@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#31295}
diff --git a/api/video_codecs/test/video_decoder_software_fallback_wrapper_unittest.cc b/api/video_codecs/test/video_decoder_software_fallback_wrapper_unittest.cc
index ee61893..30d5287 100644
--- a/api/video_codecs/test/video_decoder_software_fallback_wrapper_unittest.cc
+++ b/api/video_codecs/test/video_decoder_software_fallback_wrapper_unittest.cc
@@ -218,6 +218,68 @@
fallback_wrapper_->Release();
}
+TEST_F(VideoDecoderSoftwareFallbackWrapperTest, FallbacksOnTooManyErrors) {
+ VideoCodec codec = {};
+ fallback_wrapper_->InitDecode(&codec, 2);
+
+ fake_decoder_->decode_return_code_ = WEBRTC_VIDEO_CODEC_ERROR;
+ EncodedImage encoded_image;
+ encoded_image._frameType = VideoFrameType::kVideoFrameKey;
+ // Doesn't fallback from a single error.
+ fallback_wrapper_->Decode(encoded_image, false, -1);
+ EXPECT_STREQ("fake-decoder", fallback_wrapper_->ImplementationName());
+
+ // However, many frames with the same error, fallback should happen.
+ const int kNumFramesToEncode = 10;
+ for (int i = 0; i < kNumFramesToEncode; ++i) {
+ fallback_wrapper_->Decode(encoded_image, false, -1);
+ }
+ // Hard coded expected value since libvpx is the software implementation name
+ // for VP8. Change accordingly if the underlying implementation does.
+ EXPECT_STREQ("libvpx (fallback from: fake-decoder)",
+ fallback_wrapper_->ImplementationName());
+ fallback_wrapper_->Release();
+}
+
+TEST_F(VideoDecoderSoftwareFallbackWrapperTest,
+ DoesNotFallbackOnDeltaFramesErrors) {
+ VideoCodec codec = {};
+ fallback_wrapper_->InitDecode(&codec, 2);
+
+ fake_decoder_->decode_return_code_ = WEBRTC_VIDEO_CODEC_ERROR;
+ EncodedImage encoded_image;
+ encoded_image._frameType = VideoFrameType::kVideoFrameDelta;
+
+ // Many decoded frames with the same error
+ const int kNumFramesToEncode = 10;
+ for (int i = 0; i < kNumFramesToEncode; ++i) {
+ fallback_wrapper_->Decode(encoded_image, false, -1);
+ }
+ EXPECT_STREQ("fake-decoder", fallback_wrapper_->ImplementationName());
+
+ fallback_wrapper_->Release();
+}
+
+TEST_F(VideoDecoderSoftwareFallbackWrapperTest,
+ DoesNotFallbacksOnNonConsequtiveErrors) {
+ VideoCodec codec = {};
+ fallback_wrapper_->InitDecode(&codec, 2);
+
+ EncodedImage encoded_image;
+ encoded_image._frameType = VideoFrameType::kVideoFrameKey;
+
+ const int kNumFramesToEncode = 10;
+ for (int i = 0; i < kNumFramesToEncode; ++i) {
+ // Interleaved errors and successful decodes.
+ fake_decoder_->decode_return_code_ = WEBRTC_VIDEO_CODEC_ERROR;
+ fallback_wrapper_->Decode(encoded_image, false, -1);
+ fake_decoder_->decode_return_code_ = WEBRTC_VIDEO_CODEC_OK;
+ fallback_wrapper_->Decode(encoded_image, false, -1);
+ }
+ EXPECT_STREQ("fake-decoder", fallback_wrapper_->ImplementationName());
+ fallback_wrapper_->Release();
+}
+
class ForcedSoftwareDecoderFallbackTest
: public VideoDecoderSoftwareFallbackWrapperTest {
public:
diff --git a/api/video_codecs/video_decoder_software_fallback_wrapper.cc b/api/video_codecs/video_decoder_software_fallback_wrapper.cc
index f78d9b8..128087f 100644
--- a/api/video_codecs/video_decoder_software_fallback_wrapper.cc
+++ b/api/video_codecs/video_decoder_software_fallback_wrapper.cc
@@ -30,6 +30,8 @@
namespace {
+constexpr size_t kMaxConsequtiveHwErrors = 4;
+
class VideoDecoderSoftwareFallbackWrapper final : public VideoDecoder {
public:
VideoDecoderSoftwareFallbackWrapper(
@@ -74,6 +76,7 @@
const std::string fallback_implementation_name_;
DecodedImageCallback* callback_;
int32_t hw_decoded_frames_since_last_fallback_;
+ size_t hw_consequtive_generic_errors_;
};
VideoDecoderSoftwareFallbackWrapper::VideoDecoderSoftwareFallbackWrapper(
@@ -86,7 +89,8 @@
std::string(fallback_decoder_->ImplementationName()) +
" (fallback from: " + hw_decoder_->ImplementationName() + ")"),
callback_(nullptr),
- hw_decoded_frames_since_last_fallback_(0) {}
+ hw_decoded_frames_since_last_fallback_(0),
+ hw_consequtive_generic_errors_(0) {}
VideoDecoderSoftwareFallbackWrapper::~VideoDecoderSoftwareFallbackWrapper() =
default;
@@ -196,14 +200,24 @@
int32_t ret = WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
ret = hw_decoder_->Decode(input_image, missing_frames, render_time_ms);
if (ret != WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE) {
- if (ret == WEBRTC_VIDEO_CODEC_OK) {
+ if (ret != WEBRTC_VIDEO_CODEC_ERROR) {
++hw_decoded_frames_since_last_fallback_;
+ hw_consequtive_generic_errors_ = 0;
+ return ret;
}
- return ret;
+ if (input_image._frameType == VideoFrameType::kVideoFrameKey) {
+ // Only count errors on key-frames, since generic errors can happen
+ // with hw decoder due to many arbitrary reasons.
+ // However, requesting a key-frame is supposed to fix the issue.
+ ++hw_consequtive_generic_errors_;
+ }
+ if (hw_consequtive_generic_errors_ < kMaxConsequtiveHwErrors) {
+ return ret;
+ }
}
// HW decoder returned WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE or
- // initialization failed, fallback to software.
+ // too many generic errors on key-frames encountered.
if (!InitFallbackDecoder()) {
return ret;
}