Make rtc_software_fallback_wrappers target visible.

Need to depend on them from Chromium.

Bug: webrtc:7925
Change-Id: Iea1bb3b937c602920bfd87f885c87c790ac7bc17
Reviewed-on: https://webrtc-review.googlesource.com/82061
Reviewed-by: Magnus Jedvert <magjed@webrtc.org>
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Commit-Queue: Anders Carlsson <andersc@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#23580}
This commit is contained in:
Anders Carlsson
2018-06-12 11:15:56 +02:00
committed by Commit Bot
parent cf15eb57ff
commit dd3e0ab2bf
18 changed files with 340 additions and 304 deletions

View File

@ -76,3 +76,31 @@ rtc_static_library("builtin_video_encoder_factory") {
"../../rtc_base:ptr_util",
]
}
rtc_static_library("rtc_software_fallback_wrappers") {
visibility = [ "*" ]
sources = [
"video_decoder_software_fallback_wrapper.cc",
"video_decoder_software_fallback_wrapper.h",
"video_encoder_software_fallback_wrapper.cc",
"video_encoder_software_fallback_wrapper.h",
]
if (!build_with_chromium && is_clang) {
# Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
}
deps = [
":video_codecs_api",
"../../media:rtc_h264_profile_id",
"../../media:rtc_media_base",
"../../modules/video_coding:video_codec_interface",
"../../rtc_base:checks",
"../../rtc_base:rtc_base_approved",
"../../rtc_base/system:fallthrough",
"../../system_wrappers:field_trial_api",
"../video:video_bitrate_allocation",
]
}

View File

@ -9,18 +9,28 @@
import("../../../webrtc.gni")
if (rtc_include_tests) {
rtc_source_set("builtin_video_codec_factory_unittests") {
rtc_source_set("video_codecs_api_unittests") {
testonly = true
sources = [
"builtin_video_encoder_factory_unittest.cc",
"video_decoder_software_fallback_wrapper_unittest.cc",
"video_encoder_software_fallback_wrapper_unittest.cc",
]
deps = [
"..:builtin_video_encoder_factory",
"..:rtc_software_fallback_wrappers",
"..:video_codecs_api",
"../../../modules/video_coding:video_codec_interface",
"../../../modules/video_coding:webrtc_vp8",
"../../../modules/video_coding:webrtc_vp8_helpers",
"../../../rtc_base:checks",
"../../../rtc_base:rtc_base_tests_utils",
"../../../system_wrappers:metrics_default",
"../../../test:field_trial",
"../../../test:test_support",
"../../video:video_bitrate_allocation",
"../../video:video_frame_i420",
"//testing/gtest",
]
}

View File

@ -0,0 +1,208 @@
/*
* 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 "api/video_codecs/video_decoder.h"
#include "modules/video_coding/codecs/vp8/include/vp8.h"
#include "modules/video_coding/include/video_error_codes.h"
#include "rtc_base/checks.h"
#include "test/gtest.h"
namespace webrtc {
class VideoDecoderSoftwareFallbackWrapperTest : public ::testing::Test {
protected:
VideoDecoderSoftwareFallbackWrapperTest()
: fake_decoder_(new CountingFakeDecoder()),
fallback_wrapper_(CreateVideoDecoderSoftwareFallbackWrapper(
std::unique_ptr<VideoDecoder>(VP8Decoder::Create()),
std::unique_ptr<VideoDecoder>(fake_decoder_))) {}
class CountingFakeDecoder : public VideoDecoder {
public:
int32_t InitDecode(const VideoCodec* codec_settings,
int32_t number_of_cores) override {
++init_decode_count_;
return init_decode_return_code_;
}
int32_t Decode(const EncodedImage& input_image,
bool missing_frames,
const CodecSpecificInfo* codec_specific_info,
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 init_decode_count_ = 0;
int decode_count_ = 0;
int32_t init_decode_return_code_ = WEBRTC_VIDEO_CODEC_OK;
int32_t decode_return_code_ = WEBRTC_VIDEO_CODEC_OK;
DecodedImageCallback* decode_complete_callback_ = nullptr;
int release_count_ = 0;
int reset_count_ = 0;
};
// |fake_decoder_| is owned and released by |fallback_wrapper_|.
CountingFakeDecoder* fake_decoder_;
std::unique_ptr<VideoDecoder> fallback_wrapper_;
};
TEST_F(VideoDecoderSoftwareFallbackWrapperTest, InitializesDecoder) {
VideoCodec codec = {};
fallback_wrapper_->InitDecode(&codec, 2);
EXPECT_EQ(1, fake_decoder_->init_decode_count_);
EncodedImage encoded_image;
encoded_image._frameType = kVideoFrameKey;
fallback_wrapper_->Decode(encoded_image, false, nullptr, -1);
EXPECT_EQ(1, fake_decoder_->init_decode_count_)
<< "Initialized decoder should not be reinitialized.";
EXPECT_EQ(1, fake_decoder_->decode_count_);
}
TEST_F(VideoDecoderSoftwareFallbackWrapperTest,
UsesFallbackDecoderAfterAnyInitDecodeFailure) {
VideoCodec codec = {};
fake_decoder_->init_decode_return_code_ = WEBRTC_VIDEO_CODEC_UNINITIALIZED;
fallback_wrapper_->InitDecode(&codec, 2);
EXPECT_EQ(1, fake_decoder_->init_decode_count_);
EncodedImage encoded_image;
encoded_image._frameType = kVideoFrameKey;
fallback_wrapper_->Decode(encoded_image, false, nullptr, -1);
EXPECT_EQ(1, fake_decoder_->init_decode_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) {
VideoCodec codec = {};
fallback_wrapper_->InitDecode(&codec, 2);
fake_decoder_->decode_return_code_ = WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
EncodedImage encoded_image;
fallback_wrapper_->Decode(encoded_image, false, nullptr, -1);
EXPECT_EQ(1, fake_decoder_->decode_count_);
// Software fallback should be sticky, fake_decoder_ shouldn't be used.
encoded_image._frameType = kVideoFrameKey;
fallback_wrapper_->Decode(encoded_image, false, nullptr, -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_->init_decode_count_);
}
TEST_F(VideoDecoderSoftwareFallbackWrapperTest, DoesNotFallbackOnEveryError) {
VideoCodec codec = {};
fallback_wrapper_->InitDecode(&codec, 2);
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, nullptr, -1));
EXPECT_EQ(1, fake_decoder_->decode_count_);
fallback_wrapper_->Decode(encoded_image, false, nullptr, -1);
EXPECT_EQ(2, fake_decoder_->decode_count_)
<< "Decoder should be active even though previous decode failed.";
}
TEST_F(VideoDecoderSoftwareFallbackWrapperTest, UsesHwDecoderAfterReinit) {
VideoCodec codec = {};
fallback_wrapper_->InitDecode(&codec, 2);
fake_decoder_->decode_return_code_ = WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
EncodedImage encoded_image;
fallback_wrapper_->Decode(encoded_image, false, nullptr, -1);
EXPECT_EQ(1, fake_decoder_->decode_count_);
fallback_wrapper_->Release();
fallback_wrapper_->InitDecode(&codec, 2);
fake_decoder_->decode_return_code_ = WEBRTC_VIDEO_CODEC_OK;
fallback_wrapper_->Decode(encoded_image, false, nullptr, -1);
EXPECT_EQ(2, fake_decoder_->decode_count_)
<< "Should not be using fallback after reinit.";
}
TEST_F(VideoDecoderSoftwareFallbackWrapperTest, ForwardsReleaseCall) {
VideoCodec codec = {};
fallback_wrapper_->InitDecode(&codec, 2);
fallback_wrapper_->Release();
EXPECT_EQ(1, fake_decoder_->release_count_);
fallback_wrapper_->InitDecode(&codec, 2);
fake_decoder_->decode_return_code_ = WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
EncodedImage encoded_image;
fallback_wrapper_->Decode(encoded_image, false, nullptr, -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_NOTREACHED();
return -1;
}
void Decoded(webrtc::VideoFrame& decodedImage,
rtc::Optional<int32_t> decode_time_ms,
rtc::Optional<uint8_t> qp) override {
RTC_NOTREACHED();
}
} callback;
VideoCodec codec = {};
fallback_wrapper_->InitDecode(&codec, 2);
fallback_wrapper_->RegisterDecodeCompleteCallback(&callback);
EXPECT_EQ(&callback, fake_decoder_->decode_complete_callback_);
}
TEST_F(VideoDecoderSoftwareFallbackWrapperTest,
ReportsFallbackImplementationName) {
VideoCodec codec = {};
fallback_wrapper_->InitDecode(&codec, 2);
fake_decoder_->decode_return_code_ = WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
EncodedImage encoded_image;
fallback_wrapper_->Decode(encoded_image, false, nullptr, -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();
}
} // namespace webrtc

View File

@ -0,0 +1,532 @@
/*
* 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_encoder_software_fallback_wrapper.h"
#include <utility>
#include "api/video/i420_buffer.h"
#include "api/video/video_bitrate_allocation.h"
#include "modules/video_coding/codecs/vp8/include/vp8.h"
#include "modules/video_coding/codecs/vp8/simulcast_rate_allocator.h"
#include "modules/video_coding/codecs/vp8/temporal_layers.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 "rtc_base/fakeclock.h"
#include "test/field_trial.h"
#include "test/gtest.h"
namespace webrtc {
namespace {
const int kWidth = 320;
const int kHeight = 240;
const int kNumCores = 2;
const uint32_t kFramerate = 30;
const size_t kMaxPayloadSize = 800;
const int kDefaultMinPixelsPerFrame = 320 * 180;
const int kLowThreshold = 10;
const int kHighThreshold = 20;
} // namespace
class VideoEncoderSoftwareFallbackWrapperTest : public ::testing::Test {
protected:
VideoEncoderSoftwareFallbackWrapperTest()
: VideoEncoderSoftwareFallbackWrapperTest("") {}
explicit VideoEncoderSoftwareFallbackWrapperTest(
const std::string& field_trials)
: override_field_trials_(field_trials),
fake_encoder_(new CountingFakeEncoder()),
fallback_wrapper_(CreateVideoEncoderSoftwareFallbackWrapper(
std::unique_ptr<VideoEncoder>(VP8Encoder::Create()),
std::unique_ptr<VideoEncoder>(fake_encoder_))) {}
class CountingFakeEncoder : public VideoEncoder {
public:
int32_t InitEncode(const VideoCodec* codec_settings,
int32_t number_of_cores,
size_t max_payload_size) override {
++init_encode_count_;
return init_encode_return_code_;
}
int32_t Encode(const VideoFrame& frame,
const CodecSpecificInfo* codec_specific_info,
const std::vector<FrameType>* frame_types) override {
++encode_count_;
if (encode_complete_callback_ &&
encode_return_code_ == WEBRTC_VIDEO_CODEC_OK) {
CodecSpecificInfo info;
info.codec_name = ImplementationName();
encode_complete_callback_->OnEncodedImage(EncodedImage(), &info,
nullptr);
}
return encode_return_code_;
}
int32_t RegisterEncodeCompleteCallback(
EncodedImageCallback* callback) override {
encode_complete_callback_ = callback;
return WEBRTC_VIDEO_CODEC_OK;
}
int32_t Release() override {
++release_count_;
return WEBRTC_VIDEO_CODEC_OK;
}
int32_t SetChannelParameters(uint32_t packet_loss, int64_t rtt) override {
++set_channel_parameters_count_;
return WEBRTC_VIDEO_CODEC_OK;
}
int32_t SetRateAllocation(const VideoBitrateAllocation& bitrate_allocation,
uint32_t framerate) override {
++set_rates_count_;
return WEBRTC_VIDEO_CODEC_OK;
}
bool SupportsNativeHandle() const override {
++supports_native_handle_count_;
return supports_native_handle_;
}
const char* ImplementationName() const override { return "fake-encoder"; }
VideoEncoder::ScalingSettings GetScalingSettings() const override {
return VideoEncoder::ScalingSettings(kLowThreshold, kHighThreshold);
}
int init_encode_count_ = 0;
int32_t init_encode_return_code_ = WEBRTC_VIDEO_CODEC_OK;
int32_t encode_return_code_ = WEBRTC_VIDEO_CODEC_OK;
int encode_count_ = 0;
EncodedImageCallback* encode_complete_callback_ = nullptr;
int release_count_ = 0;
int set_channel_parameters_count_ = 0;
int set_rates_count_ = 0;
mutable int supports_native_handle_count_ = 0;
bool supports_native_handle_ = false;
};
class FakeEncodedImageCallback : public EncodedImageCallback {
public:
Result OnEncodedImage(
const EncodedImage& encoded_image,
const CodecSpecificInfo* codec_specific_info,
const RTPFragmentationHeader* fragmentation) override {
++callback_count_;
last_codec_name_ = codec_specific_info->codec_name;
return Result(Result::OK, callback_count_);
}
int callback_count_ = 0;
std::string last_codec_name_;
};
void UtilizeFallbackEncoder();
void FallbackFromEncodeRequest();
void EncodeFrame();
void EncodeFrame(int expected_ret);
void CheckLastEncoderName(const char* expected_name) {
EXPECT_STREQ(expected_name, callback_.last_codec_name_.c_str());
}
test::ScopedFieldTrials override_field_trials_;
FakeEncodedImageCallback callback_;
// |fake_encoder_| is owned and released by |fallback_wrapper_|.
CountingFakeEncoder* fake_encoder_;
std::unique_ptr<VideoEncoder> fallback_wrapper_;
VideoCodec codec_ = {};
std::unique_ptr<VideoFrame> frame_;
std::unique_ptr<SimulcastRateAllocator> rate_allocator_;
};
void VideoEncoderSoftwareFallbackWrapperTest::EncodeFrame() {
EncodeFrame(WEBRTC_VIDEO_CODEC_OK);
}
void VideoEncoderSoftwareFallbackWrapperTest::EncodeFrame(int expected_ret) {
rtc::scoped_refptr<I420Buffer> buffer =
I420Buffer::Create(codec_.width, codec_.height);
I420Buffer::SetBlack(buffer);
std::vector<FrameType> types(1, kVideoFrameKey);
frame_.reset(
new VideoFrame(buffer, webrtc::kVideoRotation_0, 0 /* timestamp_us */));
EXPECT_EQ(expected_ret, fallback_wrapper_->Encode(*frame_, nullptr, &types));
}
void VideoEncoderSoftwareFallbackWrapperTest::UtilizeFallbackEncoder() {
fallback_wrapper_->RegisterEncodeCompleteCallback(&callback_);
EXPECT_EQ(&callback_, fake_encoder_->encode_complete_callback_);
// Register with failing fake encoder. Should succeed with VP8 fallback.
codec_.codecType = kVideoCodecVP8;
codec_.maxFramerate = kFramerate;
codec_.width = kWidth;
codec_.height = kHeight;
codec_.VP8()->numberOfTemporalLayers = 1;
rate_allocator_.reset(new SimulcastRateAllocator(codec_));
fake_encoder_->init_encode_return_code_ = WEBRTC_VIDEO_CODEC_ERROR;
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
fallback_wrapper_->InitEncode(&codec_, kNumCores, kMaxPayloadSize));
EXPECT_EQ(
WEBRTC_VIDEO_CODEC_OK,
fallback_wrapper_->SetRateAllocation(
rate_allocator_->GetAllocation(300000, kFramerate), kFramerate));
int callback_count = callback_.callback_count_;
int encode_count = fake_encoder_->encode_count_;
EncodeFrame();
EXPECT_EQ(encode_count, fake_encoder_->encode_count_);
EXPECT_EQ(callback_count + 1, callback_.callback_count_);
}
void VideoEncoderSoftwareFallbackWrapperTest::FallbackFromEncodeRequest() {
fallback_wrapper_->RegisterEncodeCompleteCallback(&callback_);
codec_.codecType = kVideoCodecVP8;
codec_.maxFramerate = kFramerate;
codec_.width = kWidth;
codec_.height = kHeight;
codec_.VP8()->numberOfTemporalLayers = 1;
rate_allocator_.reset(new SimulcastRateAllocator(codec_));
fallback_wrapper_->InitEncode(&codec_, 2, kMaxPayloadSize);
EXPECT_EQ(
WEBRTC_VIDEO_CODEC_OK,
fallback_wrapper_->SetRateAllocation(
rate_allocator_->GetAllocation(300000, kFramerate), kFramerate));
EXPECT_EQ(1, fake_encoder_->init_encode_count_);
// Have the non-fallback encoder request a software fallback.
fake_encoder_->encode_return_code_ = WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
int callback_count = callback_.callback_count_;
int encode_count = fake_encoder_->encode_count_;
EncodeFrame();
// Single encode request, which returned failure.
EXPECT_EQ(encode_count + 1, fake_encoder_->encode_count_);
EXPECT_EQ(callback_count + 1, callback_.callback_count_);
}
TEST_F(VideoEncoderSoftwareFallbackWrapperTest, InitializesEncoder) {
VideoCodec codec = {};
fallback_wrapper_->InitEncode(&codec, 2, kMaxPayloadSize);
EXPECT_EQ(1, fake_encoder_->init_encode_count_);
}
TEST_F(VideoEncoderSoftwareFallbackWrapperTest, EncodeRequestsFallback) {
FallbackFromEncodeRequest();
// After fallback, further encodes shouldn't hit the fake encoder.
int encode_count = fake_encoder_->encode_count_;
EncodeFrame();
EXPECT_EQ(encode_count, fake_encoder_->encode_count_);
}
TEST_F(VideoEncoderSoftwareFallbackWrapperTest, CanUtilizeFallbackEncoder) {
UtilizeFallbackEncoder();
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, fallback_wrapper_->Release());
}
TEST_F(VideoEncoderSoftwareFallbackWrapperTest,
InternalEncoderReleasedDuringFallback) {
EXPECT_EQ(0, fake_encoder_->release_count_);
UtilizeFallbackEncoder();
EXPECT_EQ(1, fake_encoder_->release_count_);
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, fallback_wrapper_->Release());
// No extra release when the fallback is released.
EXPECT_EQ(1, fake_encoder_->release_count_);
}
TEST_F(VideoEncoderSoftwareFallbackWrapperTest,
InternalEncoderNotEncodingDuringFallback) {
UtilizeFallbackEncoder();
int encode_count = fake_encoder_->encode_count_;
EncodeFrame();
EXPECT_EQ(encode_count, fake_encoder_->encode_count_);
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, fallback_wrapper_->Release());
}
TEST_F(VideoEncoderSoftwareFallbackWrapperTest,
CanRegisterCallbackWhileUsingFallbackEncoder) {
UtilizeFallbackEncoder();
// Registering an encode-complete callback should still work when fallback
// encoder is being used.
FakeEncodedImageCallback callback2;
fallback_wrapper_->RegisterEncodeCompleteCallback(&callback2);
EXPECT_EQ(&callback2, fake_encoder_->encode_complete_callback_);
// Encoding a frame using the fallback should arrive at the new callback.
std::vector<FrameType> types(1, kVideoFrameKey);
frame_->set_timestamp(frame_->timestamp() + 1000);
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
fallback_wrapper_->Encode(*frame_, nullptr, &types));
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, fallback_wrapper_->Release());
}
TEST_F(VideoEncoderSoftwareFallbackWrapperTest,
SetChannelParametersForwardedDuringFallback) {
UtilizeFallbackEncoder();
EXPECT_EQ(0, fake_encoder_->set_channel_parameters_count_);
fallback_wrapper_->SetChannelParameters(1, 1);
EXPECT_EQ(1, fake_encoder_->set_channel_parameters_count_);
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, fallback_wrapper_->Release());
}
TEST_F(VideoEncoderSoftwareFallbackWrapperTest,
SetRatesForwardedDuringFallback) {
UtilizeFallbackEncoder();
EXPECT_EQ(1, fake_encoder_->set_rates_count_);
fallback_wrapper_->SetRateAllocation(VideoBitrateAllocation(), 1);
EXPECT_EQ(2, fake_encoder_->set_rates_count_);
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, fallback_wrapper_->Release());
}
TEST_F(VideoEncoderSoftwareFallbackWrapperTest,
SupportsNativeHandleForwardedWithoutFallback) {
fallback_wrapper_->SupportsNativeHandle();
EXPECT_EQ(1, fake_encoder_->supports_native_handle_count_);
}
TEST_F(VideoEncoderSoftwareFallbackWrapperTest,
SupportsNativeHandleNotForwardedDuringFallback) {
UtilizeFallbackEncoder();
fallback_wrapper_->SupportsNativeHandle();
EXPECT_EQ(0, fake_encoder_->supports_native_handle_count_);
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, fallback_wrapper_->Release());
}
TEST_F(VideoEncoderSoftwareFallbackWrapperTest, ReportsImplementationName) {
codec_.width = kWidth;
codec_.height = kHeight;
fallback_wrapper_->RegisterEncodeCompleteCallback(&callback_);
fallback_wrapper_->InitEncode(&codec_, kNumCores, kMaxPayloadSize);
EncodeFrame();
CheckLastEncoderName("fake-encoder");
}
TEST_F(VideoEncoderSoftwareFallbackWrapperTest,
ReportsFallbackImplementationName) {
UtilizeFallbackEncoder();
// Hard coded expected value since libvpx is the software implementation name
// for VP8. Change accordingly if the underlying implementation does.
CheckLastEncoderName("libvpx");
}
namespace {
const int kBitrateKbps = 200;
const int kMinPixelsPerFrame = 1;
const char kFieldTrial[] = "WebRTC-VP8-Forced-Fallback-Encoder-v2";
} // namespace
class ForcedFallbackTest : public VideoEncoderSoftwareFallbackWrapperTest {
public:
explicit ForcedFallbackTest(const std::string& field_trials)
: VideoEncoderSoftwareFallbackWrapperTest(field_trials) {}
~ForcedFallbackTest() override {}
protected:
void SetUp() override {
clock_.SetTimeMicros(1234);
ConfigureVp8Codec();
}
void TearDown() override {
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, fallback_wrapper_->Release());
}
void ConfigureVp8Codec() {
fallback_wrapper_->RegisterEncodeCompleteCallback(&callback_);
codec_.codecType = kVideoCodecVP8;
codec_.maxFramerate = kFramerate;
codec_.width = kWidth;
codec_.height = kHeight;
codec_.VP8()->numberOfTemporalLayers = 1;
codec_.VP8()->automaticResizeOn = true;
codec_.VP8()->frameDroppingOn = true;
rate_allocator_.reset(new SimulcastRateAllocator(codec_));
}
void InitEncode(int width, int height) {
codec_.width = width;
codec_.height = height;
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, fallback_wrapper_->InitEncode(
&codec_, kNumCores, kMaxPayloadSize));
SetRateAllocation(kBitrateKbps);
}
void SetRateAllocation(uint32_t bitrate_kbps) {
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, fallback_wrapper_->SetRateAllocation(
rate_allocator_->GetAllocation(
bitrate_kbps * 1000, kFramerate),
kFramerate));
}
void EncodeFrameAndVerifyLastName(const char* expected_name) {
EncodeFrameAndVerifyLastName(expected_name, WEBRTC_VIDEO_CODEC_OK);
}
void EncodeFrameAndVerifyLastName(const char* expected_name,
int expected_ret) {
EncodeFrame(expected_ret);
CheckLastEncoderName(expected_name);
}
rtc::ScopedFakeClock clock_;
};
class ForcedFallbackTestEnabled : public ForcedFallbackTest {
public:
ForcedFallbackTestEnabled()
: ForcedFallbackTest(std::string(kFieldTrial) + "/Enabled-" +
std::to_string(kMinPixelsPerFrame) + "," +
std::to_string(kWidth * kHeight) + ",30000/") {}
};
class ForcedFallbackTestDisabled : public ForcedFallbackTest {
public:
ForcedFallbackTestDisabled()
: ForcedFallbackTest(std::string(kFieldTrial) + "/Disabled/") {}
};
TEST_F(ForcedFallbackTestDisabled, NoFallbackWithoutFieldTrial) {
// Resolution above max threshold.
InitEncode(kWidth + 1, kHeight);
EXPECT_EQ(1, fake_encoder_->init_encode_count_);
EncodeFrameAndVerifyLastName("fake-encoder");
// Resolution at max threshold.
InitEncode(kWidth, kHeight);
EncodeFrameAndVerifyLastName("fake-encoder");
}
TEST_F(ForcedFallbackTestEnabled, FallbackIfAtMaxResolutionLimit) {
// Resolution above max threshold.
InitEncode(kWidth + 1, kHeight);
EXPECT_EQ(1, fake_encoder_->init_encode_count_);
EncodeFrameAndVerifyLastName("fake-encoder");
// Resolution at max threshold.
InitEncode(kWidth, kHeight);
EncodeFrameAndVerifyLastName("libvpx");
}
TEST_F(ForcedFallbackTestEnabled, FallbackIsKeptWhenInitEncodeIsCalled) {
// Resolution above max threshold.
InitEncode(kWidth + 1, kHeight);
EXPECT_EQ(1, fake_encoder_->init_encode_count_);
EncodeFrameAndVerifyLastName("fake-encoder");
// Resolution at max threshold.
InitEncode(kWidth, kHeight);
EncodeFrameAndVerifyLastName("libvpx");
// Re-initialize encoder, still expect fallback.
InitEncode(kWidth / 2, kHeight / 2);
EXPECT_EQ(1, fake_encoder_->init_encode_count_); // No change.
EncodeFrameAndVerifyLastName("libvpx");
}
TEST_F(ForcedFallbackTestEnabled, FallbackIsEndedWhenResolutionIsTooLarge) {
// Resolution above max threshold.
InitEncode(kWidth + 1, kHeight);
EXPECT_EQ(1, fake_encoder_->init_encode_count_);
EncodeFrameAndVerifyLastName("fake-encoder");
// Resolution at max threshold.
InitEncode(kWidth, kHeight);
EncodeFrameAndVerifyLastName("libvpx");
// Re-initialize encoder with a larger resolution, expect no fallback.
InitEncode(kWidth + 1, kHeight);
EXPECT_EQ(2, fake_encoder_->init_encode_count_);
EncodeFrameAndVerifyLastName("fake-encoder");
}
TEST_F(ForcedFallbackTestEnabled, FallbackIsEndedForNonValidSettings) {
// Resolution at max threshold.
InitEncode(kWidth, kHeight);
EncodeFrameAndVerifyLastName("libvpx");
// Re-initialize encoder with invalid setting, expect no fallback.
codec_.VP8()->numberOfTemporalLayers = 2;
InitEncode(kWidth, kHeight);
EXPECT_EQ(1, fake_encoder_->init_encode_count_);
EncodeFrameAndVerifyLastName("fake-encoder");
// Re-initialize encoder with valid setting but fallback disabled from now on.
codec_.VP8()->numberOfTemporalLayers = 1;
InitEncode(kWidth, kHeight);
EXPECT_EQ(2, fake_encoder_->init_encode_count_);
EncodeFrameAndVerifyLastName("fake-encoder");
}
TEST_F(ForcedFallbackTestEnabled, MultipleStartEndFallback) {
const int kNumRuns = 5;
for (int i = 1; i <= kNumRuns; ++i) {
// Resolution at max threshold.
InitEncode(kWidth, kHeight);
EncodeFrameAndVerifyLastName("libvpx");
// Resolution above max threshold.
InitEncode(kWidth + 1, kHeight);
EXPECT_EQ(i, fake_encoder_->init_encode_count_);
EncodeFrameAndVerifyLastName("fake-encoder");
}
}
TEST_F(ForcedFallbackTestDisabled, GetScaleSettings) {
// Resolution above max threshold.
InitEncode(kWidth + 1, kHeight);
EXPECT_EQ(1, fake_encoder_->init_encode_count_);
EncodeFrameAndVerifyLastName("fake-encoder");
// Default min pixels per frame should be used.
const auto settings = fallback_wrapper_->GetScalingSettings();
EXPECT_TRUE(settings.thresholds.has_value());
EXPECT_EQ(kDefaultMinPixelsPerFrame, settings.min_pixels_per_frame);
}
TEST_F(ForcedFallbackTestEnabled, GetScaleSettingsWithNoFallback) {
// Resolution above max threshold.
InitEncode(kWidth + 1, kHeight);
EncodeFrameAndVerifyLastName("fake-encoder");
// Configured min pixels per frame should be used.
const auto settings = fallback_wrapper_->GetScalingSettings();
EXPECT_EQ(kMinPixelsPerFrame, settings.min_pixels_per_frame);
ASSERT_TRUE(settings.thresholds);
EXPECT_EQ(kLowThreshold, settings.thresholds->low);
EXPECT_EQ(kHighThreshold, settings.thresholds->high);
}
TEST_F(ForcedFallbackTestEnabled, GetScaleSettingsWithFallback) {
// Resolution at max threshold.
InitEncode(kWidth, kHeight);
EncodeFrameAndVerifyLastName("libvpx");
// Configured min pixels per frame should be used.
const auto settings = fallback_wrapper_->GetScalingSettings();
EXPECT_TRUE(settings.thresholds.has_value());
EXPECT_EQ(kMinPixelsPerFrame, settings.min_pixels_per_frame);
}
TEST_F(ForcedFallbackTestEnabled, ScalingDisabledIfResizeOff) {
// Resolution at max threshold.
codec_.VP8()->automaticResizeOn = false;
InitEncode(kWidth, kHeight);
EncodeFrameAndVerifyLastName("libvpx");
// Should be disabled for automatic resize off.
const auto settings = fallback_wrapper_->GetScalingSettings();
EXPECT_FALSE(settings.thresholds.has_value());
}
} // namespace webrtc

View File

@ -0,0 +1,224 @@
/*
* Copyright (c) 2016 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 <string>
#include <utility>
#include "modules/video_coding/include/video_error_codes.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "rtc_base/ptr_util.h"
#include "rtc_base/system/fallthrough.h"
#include "rtc_base/trace_event.h"
namespace webrtc {
namespace {
class VideoDecoderSoftwareFallbackWrapper final : public VideoDecoder {
public:
VideoDecoderSoftwareFallbackWrapper(
std::unique_ptr<VideoDecoder> sw_fallback_decoder,
std::unique_ptr<VideoDecoder> hw_decoder);
~VideoDecoderSoftwareFallbackWrapper() override;
int32_t InitDecode(const VideoCodec* codec_settings,
int32_t number_of_cores) override;
int32_t Decode(const EncodedImage& input_image,
bool missing_frames,
const CodecSpecificInfo* codec_specific_info,
int64_t render_time_ms) override;
int32_t RegisterDecodeCompleteCallback(
DecodedImageCallback* callback) override;
int32_t Release() override;
bool PrefersLateDecoding() const override;
const char* ImplementationName() const override;
private:
bool InitFallbackDecoder();
int32_t InitHwDecoder();
VideoDecoder& active_decoder() const;
// Determines if we are trying to use the HW or SW decoder.
enum class DecoderType {
kNone,
kHardware,
kFallback,
} decoder_type_;
std::unique_ptr<VideoDecoder> hw_decoder_;
VideoCodec codec_settings_;
int32_t number_of_cores_;
const std::unique_ptr<VideoDecoder> fallback_decoder_;
const std::string fallback_implementation_name_;
DecodedImageCallback* callback_;
};
VideoDecoderSoftwareFallbackWrapper::VideoDecoderSoftwareFallbackWrapper(
std::unique_ptr<VideoDecoder> sw_fallback_decoder,
std::unique_ptr<VideoDecoder> hw_decoder)
: decoder_type_(DecoderType::kNone),
hw_decoder_(std::move(hw_decoder)),
fallback_decoder_(std::move(sw_fallback_decoder)),
fallback_implementation_name_(
std::string(fallback_decoder_->ImplementationName()) +
" (fallback from: " + hw_decoder_->ImplementationName() + ")"),
callback_(nullptr) {}
VideoDecoderSoftwareFallbackWrapper::~VideoDecoderSoftwareFallbackWrapper() =
default;
int32_t VideoDecoderSoftwareFallbackWrapper::InitDecode(
const VideoCodec* codec_settings,
int32_t number_of_cores) {
codec_settings_ = *codec_settings;
number_of_cores_ = number_of_cores;
int32_t status = InitHwDecoder();
if (status == WEBRTC_VIDEO_CODEC_OK) {
return WEBRTC_VIDEO_CODEC_OK;
}
RTC_DCHECK(decoder_type_ == DecoderType::kNone);
if (InitFallbackDecoder()) {
return WEBRTC_VIDEO_CODEC_OK;
}
return status;
}
int32_t VideoDecoderSoftwareFallbackWrapper::InitHwDecoder() {
RTC_DCHECK(decoder_type_ == DecoderType::kNone);
int32_t status = hw_decoder_->InitDecode(&codec_settings_, number_of_cores_);
if (status != WEBRTC_VIDEO_CODEC_OK) {
return status;
}
decoder_type_ = DecoderType::kHardware;
if (callback_)
hw_decoder_->RegisterDecodeCompleteCallback(callback_);
return status;
}
bool VideoDecoderSoftwareFallbackWrapper::InitFallbackDecoder() {
RTC_DCHECK(decoder_type_ == DecoderType::kNone ||
decoder_type_ == DecoderType::kHardware);
RTC_LOG(LS_WARNING) << "Decoder falling back to software decoding.";
int32_t status =
fallback_decoder_->InitDecode(&codec_settings_, number_of_cores_);
if (status != WEBRTC_VIDEO_CODEC_OK) {
RTC_LOG(LS_ERROR) << "Failed to initialize software-decoder fallback.";
return false;
}
if (decoder_type_ == DecoderType::kHardware) {
hw_decoder_->Release();
}
decoder_type_ = DecoderType::kFallback;
if (callback_)
fallback_decoder_->RegisterDecodeCompleteCallback(callback_);
return true;
}
int32_t VideoDecoderSoftwareFallbackWrapper::Decode(
const EncodedImage& input_image,
bool missing_frames,
const CodecSpecificInfo* codec_specific_info,
int64_t render_time_ms) {
TRACE_EVENT0("webrtc", "VideoDecoderSoftwareFallbackWrapper::Decode");
switch (decoder_type_) {
case DecoderType::kNone:
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
case DecoderType::kHardware: {
int32_t ret = WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
ret = hw_decoder_->Decode(input_image, missing_frames,
codec_specific_info, render_time_ms);
if (ret != WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE) {
return ret;
}
// HW decoder returned WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE or
// initialization failed, fallback to software.
if (!InitFallbackDecoder()) {
return ret;
}
// Fallback decoder initialized, fall-through.
RTC_FALLTHROUGH();
}
case DecoderType::kFallback:
return fallback_decoder_->Decode(input_image, missing_frames,
codec_specific_info, render_time_ms);
default:
RTC_NOTREACHED();
return WEBRTC_VIDEO_CODEC_ERROR;
}
}
int32_t VideoDecoderSoftwareFallbackWrapper::RegisterDecodeCompleteCallback(
DecodedImageCallback* callback) {
callback_ = callback;
return active_decoder().RegisterDecodeCompleteCallback(callback);
}
int32_t VideoDecoderSoftwareFallbackWrapper::Release() {
int32_t status;
switch (decoder_type_) {
case DecoderType::kHardware:
status = hw_decoder_->Release();
break;
case DecoderType::kFallback:
RTC_LOG(LS_INFO) << "Releasing software fallback decoder.";
status = fallback_decoder_->Release();
break;
case DecoderType::kNone:
status = WEBRTC_VIDEO_CODEC_OK;
break;
default:
RTC_NOTREACHED();
status = WEBRTC_VIDEO_CODEC_ERROR;
}
decoder_type_ = DecoderType::kNone;
return status;
}
bool VideoDecoderSoftwareFallbackWrapper::PrefersLateDecoding() const {
return active_decoder().PrefersLateDecoding();
}
const char* VideoDecoderSoftwareFallbackWrapper::ImplementationName() const {
return decoder_type_ == DecoderType::kFallback
? fallback_implementation_name_.c_str()
: hw_decoder_->ImplementationName();
}
VideoDecoder& VideoDecoderSoftwareFallbackWrapper::active_decoder() const {
return decoder_type_ == DecoderType::kFallback ? *fallback_decoder_
: *hw_decoder_;
}
} // namespace
std::unique_ptr<VideoDecoder> CreateVideoDecoderSoftwareFallbackWrapper(
std::unique_ptr<VideoDecoder> sw_fallback_decoder,
std::unique_ptr<VideoDecoder> hw_decoder) {
return rtc::MakeUnique<VideoDecoderSoftwareFallbackWrapper>(
std::move(sw_fallback_decoder), std::move(hw_decoder));
}
} // namespace webrtc

View File

@ -0,0 +1,29 @@
/*
* Copyright (c) 2016 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.
*/
#ifndef API_VIDEO_CODECS_VIDEO_DECODER_SOFTWARE_FALLBACK_WRAPPER_H_
#define API_VIDEO_CODECS_VIDEO_DECODER_SOFTWARE_FALLBACK_WRAPPER_H_
#include <memory>
#include "api/video_codecs/video_decoder.h"
namespace webrtc {
// Used to wrap external VideoDecoders to provide a fallback option on
// software decoding when a hardware decoder fails to decode a stream due to
// hardware restrictions, such as max resolution.
std::unique_ptr<VideoDecoder> CreateVideoDecoderSoftwareFallbackWrapper(
std::unique_ptr<VideoDecoder> sw_fallback_decoder,
std::unique_ptr<VideoDecoder> hw_decoder);
} // namespace webrtc
#endif // API_VIDEO_CODECS_VIDEO_DECODER_SOFTWARE_FALLBACK_WRAPPER_H_

View File

@ -0,0 +1,387 @@
/*
* Copyright (c) 2016 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_encoder_software_fallback_wrapper.h"
#include <cstdio>
#include <string>
#include <utility>
#include <vector>
#include "media/base/codec.h"
#include "media/base/h264_profile_level_id.h"
#include "modules/video_coding/include/video_error_codes.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "rtc_base/ptr_util.h"
#include "rtc_base/timeutils.h"
#include "system_wrappers/include/field_trial.h"
namespace webrtc {
namespace {
const char kVp8ForceFallbackEncoderFieldTrial[] =
"WebRTC-VP8-Forced-Fallback-Encoder-v2";
bool EnableForcedFallback() {
return field_trial::IsEnabled(kVp8ForceFallbackEncoderFieldTrial);
}
bool IsForcedFallbackPossible(const VideoCodec& codec_settings) {
return codec_settings.codecType == kVideoCodecVP8 &&
codec_settings.numberOfSimulcastStreams <= 1 &&
codec_settings.VP8().numberOfTemporalLayers == 1;
}
void GetForcedFallbackParamsFromFieldTrialGroup(int* param_min_pixels,
int* param_max_pixels,
int minimum_max_pixels) {
RTC_DCHECK(param_min_pixels);
RTC_DCHECK(param_max_pixels);
std::string group =
webrtc::field_trial::FindFullName(kVp8ForceFallbackEncoderFieldTrial);
if (group.empty())
return;
int min_pixels;
int max_pixels;
int min_bps;
if (sscanf(group.c_str(), "Enabled-%d,%d,%d", &min_pixels, &max_pixels,
&min_bps) != 3) {
RTC_LOG(LS_WARNING)
<< "Invalid number of forced fallback parameters provided.";
return;
}
if (min_pixels <= 0 || max_pixels < minimum_max_pixels ||
max_pixels < min_pixels || min_bps <= 0) {
RTC_LOG(LS_WARNING) << "Invalid forced fallback parameter value provided.";
return;
}
*param_min_pixels = min_pixels;
*param_max_pixels = max_pixels;
}
class VideoEncoderSoftwareFallbackWrapper final : public VideoEncoder {
public:
VideoEncoderSoftwareFallbackWrapper(
std::unique_ptr<webrtc::VideoEncoder> sw_encoder,
std::unique_ptr<webrtc::VideoEncoder> hw_encoder);
~VideoEncoderSoftwareFallbackWrapper() override;
int32_t InitEncode(const VideoCodec* codec_settings,
int32_t number_of_cores,
size_t max_payload_size) override;
int32_t RegisterEncodeCompleteCallback(
EncodedImageCallback* callback) override;
int32_t Release() override;
int32_t Encode(const VideoFrame& frame,
const CodecSpecificInfo* codec_specific_info,
const std::vector<FrameType>* frame_types) override;
int32_t SetChannelParameters(uint32_t packet_loss, int64_t rtt) override;
int32_t SetRateAllocation(const VideoBitrateAllocation& bitrate_allocation,
uint32_t framerate) override;
bool SupportsNativeHandle() const override;
ScalingSettings GetScalingSettings() const override;
const char* ImplementationName() const override;
private:
bool InitFallbackEncoder();
// If |forced_fallback_possible_| is true:
// The forced fallback is requested if the resolution is less than or equal to
// |max_pixels_|. The resolution is allowed to be scaled down to
// |min_pixels_|.
class ForcedFallbackParams {
public:
bool IsValid(const VideoCodec& codec) const {
return codec.width * codec.height <= max_pixels_;
}
bool active_ = false;
int min_pixels_ = 320 * 180;
int max_pixels_ = 320 * 240;
};
bool TryInitForcedFallbackEncoder();
bool TryReInitForcedFallbackEncoder();
void ValidateSettingsForForcedFallback();
bool IsForcedFallbackActive() const;
void MaybeModifyCodecForFallback();
// Settings used in the last InitEncode call and used if a dynamic fallback to
// software is required.
VideoCodec codec_settings_;
int32_t number_of_cores_;
size_t max_payload_size_;
// The last bitrate/framerate set, and a flag for noting they are set.
bool rates_set_;
VideoBitrateAllocation bitrate_allocation_;
uint32_t framerate_;
// The last channel parameters set, and a flag for noting they are set.
bool channel_parameters_set_;
uint32_t packet_loss_;
int64_t rtt_;
bool use_fallback_encoder_;
const std::unique_ptr<webrtc::VideoEncoder> encoder_;
const std::unique_ptr<webrtc::VideoEncoder> fallback_encoder_;
EncodedImageCallback* callback_;
bool forced_fallback_possible_;
ForcedFallbackParams forced_fallback_;
};
VideoEncoderSoftwareFallbackWrapper::VideoEncoderSoftwareFallbackWrapper(
std::unique_ptr<webrtc::VideoEncoder> sw_encoder,
std::unique_ptr<webrtc::VideoEncoder> hw_encoder)
: number_of_cores_(0),
max_payload_size_(0),
rates_set_(false),
framerate_(0),
channel_parameters_set_(false),
packet_loss_(0),
rtt_(0),
use_fallback_encoder_(false),
encoder_(std::move(hw_encoder)),
fallback_encoder_(std::move(sw_encoder)),
callback_(nullptr),
forced_fallback_possible_(EnableForcedFallback()) {
if (forced_fallback_possible_) {
GetForcedFallbackParamsFromFieldTrialGroup(
&forced_fallback_.min_pixels_, &forced_fallback_.max_pixels_,
encoder_->GetScalingSettings().min_pixels_per_frame -
1); // No HW below.
}
}
VideoEncoderSoftwareFallbackWrapper::~VideoEncoderSoftwareFallbackWrapper() =
default;
bool VideoEncoderSoftwareFallbackWrapper::InitFallbackEncoder() {
RTC_LOG(LS_WARNING) << "Encoder falling back to software encoding.";
const int ret = fallback_encoder_->InitEncode(
&codec_settings_, number_of_cores_, max_payload_size_);
use_fallback_encoder_ = (ret == WEBRTC_VIDEO_CODEC_OK);
if (!use_fallback_encoder_) {
RTC_LOG(LS_ERROR) << "Failed to initialize software-encoder fallback.";
fallback_encoder_->Release();
return false;
}
// Replay callback, rates, and channel parameters.
if (callback_)
fallback_encoder_->RegisterEncodeCompleteCallback(callback_);
if (rates_set_)
fallback_encoder_->SetRateAllocation(bitrate_allocation_, framerate_);
if (channel_parameters_set_)
fallback_encoder_->SetChannelParameters(packet_loss_, rtt_);
// Since we're switching to the fallback encoder, Release the real encoder. It
// may be re-initialized via InitEncode later, and it will continue to get
// Set calls for rates and channel parameters in the meantime.
encoder_->Release();
return true;
}
int32_t VideoEncoderSoftwareFallbackWrapper::InitEncode(
const VideoCodec* codec_settings,
int32_t number_of_cores,
size_t max_payload_size) {
// Store settings, in case we need to dynamically switch to the fallback
// encoder after a failed Encode call.
codec_settings_ = *codec_settings;
number_of_cores_ = number_of_cores;
max_payload_size_ = max_payload_size;
// Clear stored rate/channel parameters.
rates_set_ = false;
channel_parameters_set_ = false;
ValidateSettingsForForcedFallback();
// Try to reinit forced software codec if it is in use.
if (TryReInitForcedFallbackEncoder()) {
return WEBRTC_VIDEO_CODEC_OK;
}
// Try to init forced software codec if it should be used.
if (TryInitForcedFallbackEncoder()) {
return WEBRTC_VIDEO_CODEC_OK;
}
forced_fallback_.active_ = false;
int32_t ret =
encoder_->InitEncode(codec_settings, number_of_cores, max_payload_size);
if (ret == WEBRTC_VIDEO_CODEC_OK) {
if (use_fallback_encoder_) {
RTC_LOG(LS_WARNING)
<< "InitEncode OK, no longer using the software fallback encoder.";
fallback_encoder_->Release();
use_fallback_encoder_ = false;
}
if (callback_)
encoder_->RegisterEncodeCompleteCallback(callback_);
return ret;
}
// Try to instantiate software codec.
if (InitFallbackEncoder()) {
return WEBRTC_VIDEO_CODEC_OK;
}
// Software encoder failed, use original return code.
return ret;
}
int32_t VideoEncoderSoftwareFallbackWrapper::RegisterEncodeCompleteCallback(
EncodedImageCallback* callback) {
callback_ = callback;
int32_t ret = encoder_->RegisterEncodeCompleteCallback(callback);
if (use_fallback_encoder_)
return fallback_encoder_->RegisterEncodeCompleteCallback(callback);
return ret;
}
int32_t VideoEncoderSoftwareFallbackWrapper::Release() {
return use_fallback_encoder_ ? fallback_encoder_->Release()
: encoder_->Release();
}
int32_t VideoEncoderSoftwareFallbackWrapper::Encode(
const VideoFrame& frame,
const CodecSpecificInfo* codec_specific_info,
const std::vector<FrameType>* frame_types) {
if (use_fallback_encoder_)
return fallback_encoder_->Encode(frame, codec_specific_info, frame_types);
int32_t ret = encoder_->Encode(frame, codec_specific_info, frame_types);
// If requested, try a software fallback.
bool fallback_requested = (ret == WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE);
if (fallback_requested && InitFallbackEncoder()) {
// Start using the fallback with this frame.
return fallback_encoder_->Encode(frame, codec_specific_info, frame_types);
}
return ret;
}
int32_t VideoEncoderSoftwareFallbackWrapper::SetChannelParameters(
uint32_t packet_loss,
int64_t rtt) {
channel_parameters_set_ = true;
packet_loss_ = packet_loss;
rtt_ = rtt;
int32_t ret = encoder_->SetChannelParameters(packet_loss, rtt);
if (use_fallback_encoder_)
return fallback_encoder_->SetChannelParameters(packet_loss, rtt);
return ret;
}
int32_t VideoEncoderSoftwareFallbackWrapper::SetRateAllocation(
const VideoBitrateAllocation& bitrate_allocation,
uint32_t framerate) {
rates_set_ = true;
bitrate_allocation_ = bitrate_allocation;
framerate_ = framerate;
int32_t ret = encoder_->SetRateAllocation(bitrate_allocation_, framerate);
if (use_fallback_encoder_)
return fallback_encoder_->SetRateAllocation(bitrate_allocation_, framerate);
return ret;
}
bool VideoEncoderSoftwareFallbackWrapper::SupportsNativeHandle() const {
return use_fallback_encoder_ ? fallback_encoder_->SupportsNativeHandle()
: encoder_->SupportsNativeHandle();
}
VideoEncoder::ScalingSettings
VideoEncoderSoftwareFallbackWrapper::GetScalingSettings() const {
if (forced_fallback_possible_) {
const auto settings = forced_fallback_.active_
? fallback_encoder_->GetScalingSettings()
: encoder_->GetScalingSettings();
return settings.thresholds
? VideoEncoder::ScalingSettings(settings.thresholds->low,
settings.thresholds->high,
forced_fallback_.min_pixels_)
: VideoEncoder::ScalingSettings::kOff;
}
return encoder_->GetScalingSettings();
}
const char* VideoEncoderSoftwareFallbackWrapper::ImplementationName() const {
return use_fallback_encoder_ ? fallback_encoder_->ImplementationName()
: encoder_->ImplementationName();
}
bool VideoEncoderSoftwareFallbackWrapper::IsForcedFallbackActive() const {
return (forced_fallback_possible_ && use_fallback_encoder_ &&
forced_fallback_.active_);
}
bool VideoEncoderSoftwareFallbackWrapper::TryInitForcedFallbackEncoder() {
if (!forced_fallback_possible_ || use_fallback_encoder_) {
return false;
}
// Fallback not active.
if (!forced_fallback_.IsValid(codec_settings_)) {
return false;
}
// Settings valid, try to instantiate software codec.
RTC_LOG(LS_INFO) << "Request forced SW encoder fallback: "
<< codec_settings_.width << "x" << codec_settings_.height;
if (!InitFallbackEncoder()) {
return false;
}
forced_fallback_.active_ = true;
return true;
}
bool VideoEncoderSoftwareFallbackWrapper::TryReInitForcedFallbackEncoder() {
if (!IsForcedFallbackActive()) {
return false;
}
// Forced fallback active.
if (!forced_fallback_.IsValid(codec_settings_)) {
RTC_LOG(LS_INFO) << "Stop forced SW encoder fallback, max pixels exceeded.";
return false;
}
// Settings valid, reinitialize the forced fallback encoder.
if (fallback_encoder_->InitEncode(&codec_settings_, number_of_cores_,
max_payload_size_) !=
WEBRTC_VIDEO_CODEC_OK) {
RTC_LOG(LS_ERROR) << "Failed to init forced SW encoder fallback.";
return false;
}
return true;
}
void VideoEncoderSoftwareFallbackWrapper::ValidateSettingsForForcedFallback() {
if (!forced_fallback_possible_)
return;
if (!IsForcedFallbackPossible(codec_settings_)) {
if (IsForcedFallbackActive()) {
fallback_encoder_->Release();
use_fallback_encoder_ = false;
}
RTC_LOG(LS_INFO) << "Disable forced_fallback_possible_ due to settings.";
forced_fallback_possible_ = false;
}
}
} // namespace
std::unique_ptr<VideoEncoder> CreateVideoEncoderSoftwareFallbackWrapper(
std::unique_ptr<VideoEncoder> sw_fallback_encoder,
std::unique_ptr<VideoEncoder> hw_encoder) {
return rtc::MakeUnique<VideoEncoderSoftwareFallbackWrapper>(
std::move(sw_fallback_encoder), std::move(hw_encoder));
}
} // namespace webrtc

View File

@ -0,0 +1,29 @@
/*
* Copyright (c) 2016 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.
*/
#ifndef API_VIDEO_CODECS_VIDEO_ENCODER_SOFTWARE_FALLBACK_WRAPPER_H_
#define API_VIDEO_CODECS_VIDEO_ENCODER_SOFTWARE_FALLBACK_WRAPPER_H_
#include <memory>
#include "api/video_codecs/video_encoder.h"
namespace webrtc {
// Used to wrap external VideoEncoders to provide a fallback option on
// software encoding when a hardware encoder fails to encode a stream due to
// hardware restrictions, such as max resolution.
std::unique_ptr<VideoEncoder> CreateVideoEncoderSoftwareFallbackWrapper(
std::unique_ptr<VideoEncoder> sw_fallback_encoder,
std::unique_ptr<VideoEncoder> hw_encoder);
} // namespace webrtc
#endif // API_VIDEO_CODECS_VIDEO_ENCODER_SOFTWARE_FALLBACK_WRAPPER_H_