diff options
Diffstat (limited to 'third_party/libwebrtc/api/video_codecs/test/video_decoder_software_fallback_wrapper_unittest.cc')
-rw-r--r-- | third_party/libwebrtc/api/video_codecs/test/video_decoder_software_fallback_wrapper_unittest.cc | 305 |
1 files changed, 305 insertions, 0 deletions
diff --git a/third_party/libwebrtc/api/video_codecs/test/video_decoder_software_fallback_wrapper_unittest.cc b/third_party/libwebrtc/api/video_codecs/test/video_decoder_software_fallback_wrapper_unittest.cc new file mode 100644 index 0000000000..73dedc8395 --- /dev/null +++ b/third_party/libwebrtc/api/video_codecs/test/video_decoder_software_fallback_wrapper_unittest.cc @@ -0,0 +1,305 @@ +/* + * Copyright (c) 2015 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 "api/video_codecs/video_decoder_software_fallback_wrapper.h" + +#include <stdint.h> + +#include "absl/types/optional.h" +#include "api/video/encoded_image.h" +#include "api/video/video_frame.h" +#include "api/video_codecs/video_decoder.h" +#include "modules/video_coding/codecs/vp8/include/vp8.h" +#include "modules/video_coding/include/video_codec_interface.h" +#include "modules/video_coding/include/video_error_codes.h" +#include "rtc_base/checks.h" +#include "test/field_trial.h" +#include "test/gtest.h" + +namespace webrtc { + +class VideoDecoderSoftwareFallbackWrapperTest : public ::testing::Test { + protected: + VideoDecoderSoftwareFallbackWrapperTest() + : VideoDecoderSoftwareFallbackWrapperTest("") {} + explicit VideoDecoderSoftwareFallbackWrapperTest( + const std::string& field_trials) + : override_field_trials_(field_trials), + fake_decoder_(new CountingFakeDecoder()), + fallback_wrapper_(CreateVideoDecoderSoftwareFallbackWrapper( + std::unique_ptr<VideoDecoder>(VP8Decoder::Create()), + std::unique_ptr<VideoDecoder>(fake_decoder_))) {} + + class CountingFakeDecoder : public VideoDecoder { + public: + bool Configure(const Settings& settings) override { + ++configure_count_; + return configure_return_value_; + } + + int32_t Decode(const EncodedImage& input_image, + bool missing_frames, + int64_t render_time_ms) override { + ++decode_count_; + return decode_return_code_; + } + + int32_t RegisterDecodeCompleteCallback( + DecodedImageCallback* callback) override { + decode_complete_callback_ = callback; + return WEBRTC_VIDEO_CODEC_OK; + } + + int32_t Release() override { + ++release_count_; + return WEBRTC_VIDEO_CODEC_OK; + } + + const char* ImplementationName() const override { return "fake-decoder"; } + + int configure_count_ = 0; + int decode_count_ = 0; + bool configure_return_value_ = true; + int32_t decode_return_code_ = WEBRTC_VIDEO_CODEC_OK; + DecodedImageCallback* decode_complete_callback_ = nullptr; + int release_count_ = 0; + int reset_count_ = 0; + }; + test::ScopedFieldTrials override_field_trials_; + // `fake_decoder_` is owned and released by `fallback_wrapper_`. + CountingFakeDecoder* fake_decoder_; + std::unique_ptr<VideoDecoder> fallback_wrapper_; +}; + +TEST_F(VideoDecoderSoftwareFallbackWrapperTest, InitializesDecoder) { + fallback_wrapper_->Configure({}); + EXPECT_EQ(1, fake_decoder_->configure_count_); + + EncodedImage encoded_image; + encoded_image._frameType = VideoFrameType::kVideoFrameKey; + fallback_wrapper_->Decode(encoded_image, false, -1); + EXPECT_EQ(1, fake_decoder_->configure_count_) + << "Initialized decoder should not be reinitialized."; + EXPECT_EQ(1, fake_decoder_->decode_count_); +} + +TEST_F(VideoDecoderSoftwareFallbackWrapperTest, + UsesFallbackDecoderAfterAnyInitDecodeFailure) { + fake_decoder_->configure_return_value_ = false; + fallback_wrapper_->Configure({}); + EXPECT_EQ(1, fake_decoder_->configure_count_); + + EncodedImage encoded_image; + encoded_image._frameType = VideoFrameType::kVideoFrameKey; + fallback_wrapper_->Decode(encoded_image, false, -1); + EXPECT_EQ(1, fake_decoder_->configure_count_) + << "Should not have attempted reinitializing the fallback decoder on " + "keyframe."; + // Unfortunately faking a VP8 frame is hard. Rely on no Decode -> using SW + // decoder. + EXPECT_EQ(0, fake_decoder_->decode_count_) + << "Decoder used even though no InitDecode had succeeded."; +} + +TEST_F(VideoDecoderSoftwareFallbackWrapperTest, IsSoftwareFallbackSticky) { + fallback_wrapper_->Configure({}); + + fake_decoder_->decode_return_code_ = WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE; + EncodedImage encoded_image; + fallback_wrapper_->Decode(encoded_image, false, -1); + EXPECT_EQ(1, fake_decoder_->decode_count_); + + // Software fallback should be sticky, fake_decoder_ shouldn't be used. + encoded_image._frameType = VideoFrameType::kVideoFrameKey; + fallback_wrapper_->Decode(encoded_image, false, -1); + EXPECT_EQ(1, fake_decoder_->decode_count_) + << "Decoder shouldn't be used after failure."; + + // fake_decoder_ should have only been initialized once during the test. + EXPECT_EQ(1, fake_decoder_->configure_count_); +} + +TEST_F(VideoDecoderSoftwareFallbackWrapperTest, DoesNotFallbackOnEveryError) { + fallback_wrapper_->Configure({}); + fake_decoder_->decode_return_code_ = WEBRTC_VIDEO_CODEC_ERROR; + EncodedImage encoded_image; + EXPECT_EQ(fake_decoder_->decode_return_code_, + fallback_wrapper_->Decode(encoded_image, false, -1)); + EXPECT_EQ(1, fake_decoder_->decode_count_); + + fallback_wrapper_->Decode(encoded_image, false, -1); + EXPECT_EQ(2, fake_decoder_->decode_count_) + << "Decoder should be active even though previous decode failed."; +} + +TEST_F(VideoDecoderSoftwareFallbackWrapperTest, UsesHwDecoderAfterReinit) { + fallback_wrapper_->Configure({}); + + fake_decoder_->decode_return_code_ = WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE; + EncodedImage encoded_image; + fallback_wrapper_->Decode(encoded_image, false, -1); + EXPECT_EQ(1, fake_decoder_->decode_count_); + + fallback_wrapper_->Release(); + fallback_wrapper_->Configure({}); + + fake_decoder_->decode_return_code_ = WEBRTC_VIDEO_CODEC_OK; + fallback_wrapper_->Decode(encoded_image, false, -1); + EXPECT_EQ(2, fake_decoder_->decode_count_) + << "Should not be using fallback after reinit."; +} + +TEST_F(VideoDecoderSoftwareFallbackWrapperTest, ForwardsReleaseCall) { + fallback_wrapper_->Configure({}); + fallback_wrapper_->Release(); + EXPECT_EQ(1, fake_decoder_->release_count_); + + fallback_wrapper_->Configure({}); + fake_decoder_->decode_return_code_ = WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE; + EncodedImage encoded_image; + fallback_wrapper_->Decode(encoded_image, false, -1); + EXPECT_EQ(2, fake_decoder_->release_count_) + << "Decoder should be released during fallback."; + fallback_wrapper_->Release(); + EXPECT_EQ(2, fake_decoder_->release_count_); +} + +// TODO(pbos): Fake a VP8 frame well enough to actually receive a callback from +// the software decoder. +TEST_F(VideoDecoderSoftwareFallbackWrapperTest, + ForwardsRegisterDecodeCompleteCallback) { + class FakeDecodedImageCallback : public DecodedImageCallback { + int32_t Decoded(VideoFrame& decodedImage) override { return 0; } + int32_t Decoded(webrtc::VideoFrame& decodedImage, + int64_t decode_time_ms) override { + RTC_DCHECK_NOTREACHED(); + return -1; + } + void Decoded(webrtc::VideoFrame& decodedImage, + absl::optional<int32_t> decode_time_ms, + absl::optional<uint8_t> qp) override { + RTC_DCHECK_NOTREACHED(); + } + } callback; + + fallback_wrapper_->Configure({}); + fallback_wrapper_->RegisterDecodeCompleteCallback(&callback); + EXPECT_EQ(&callback, fake_decoder_->decode_complete_callback_); +} + +TEST_F(VideoDecoderSoftwareFallbackWrapperTest, + ReportsFallbackImplementationName) { + fallback_wrapper_->Configure({}); + + fake_decoder_->decode_return_code_ = WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE; + EncodedImage encoded_image; + 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, FallbacksOnTooManyErrors) { + fallback_wrapper_->Configure({}); + + 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) { + fallback_wrapper_->Configure({}); + + 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) { + fallback_wrapper_->Configure({}); + + 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: + ForcedSoftwareDecoderFallbackTest() + : VideoDecoderSoftwareFallbackWrapperTest( + "WebRTC-Video-ForcedSwDecoderFallback/Enabled/") { + fake_decoder_ = new CountingFakeDecoder(); + sw_fallback_decoder_ = new CountingFakeDecoder(); + fallback_wrapper_ = CreateVideoDecoderSoftwareFallbackWrapper( + std::unique_ptr<VideoDecoder>(sw_fallback_decoder_), + std::unique_ptr<VideoDecoder>(fake_decoder_)); + } + + CountingFakeDecoder* sw_fallback_decoder_; +}; + +TEST_F(ForcedSoftwareDecoderFallbackTest, UsesForcedFallback) { + fallback_wrapper_->Configure({}); + EXPECT_EQ(1, sw_fallback_decoder_->configure_count_); + + EncodedImage encoded_image; + encoded_image._frameType = VideoFrameType::kVideoFrameKey; + fallback_wrapper_->Decode(encoded_image, false, -1); + EXPECT_EQ(1, sw_fallback_decoder_->configure_count_); + EXPECT_EQ(1, sw_fallback_decoder_->decode_count_); + + fallback_wrapper_->Release(); + EXPECT_EQ(1, sw_fallback_decoder_->release_count_); + + // Only fallback decoder should have been used. + EXPECT_EQ(0, fake_decoder_->configure_count_); + EXPECT_EQ(0, fake_decoder_->decode_count_); + EXPECT_EQ(0, fake_decoder_->release_count_); +} + +} // namespace webrtc |