Change forced software encoder fallback for VP8 to be only based on resolution and not bitrate.
Switches from VP8 HW to VP8 SW for resolutions <= max_pixels. |<- min_pixels VP8 SW max_pixels ->| VP8 HW | Bug: webrtc:6634 Change-Id: Ib324df2b8418659c29d999259c0ed47448310696 Reviewed-on: https://webrtc-review.googlesource.com/7362 Commit-Queue: Åsa Persson <asapersson@webrtc.org> Reviewed-by: Rasmus Brandt <brandtr@webrtc.org> Reviewed-by: Magnus Jedvert <magjed@webrtc.org> Cr-Commit-Position: refs/heads/master@{#20646}
This commit is contained in:
parent
b577d5e4c3
commit
45bbc8ac19
@ -18,7 +18,7 @@ extern const int kVideoRtpBufferSize;
|
||||
|
||||
extern const char kH264CodecName[];
|
||||
|
||||
extern const int kMinVideoBitrateKbps;
|
||||
extern const int kMinVideoBitrateBps;
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
|
||||
@ -209,7 +209,7 @@ std::vector<webrtc::VideoStream> GetSimulcastConfig(size_t max_streams,
|
||||
streams[0].height = height;
|
||||
streams[0].max_qp = max_qp;
|
||||
streams[0].max_framerate = 5;
|
||||
streams[0].min_bitrate_bps = kMinVideoBitrateKbps * 1000;
|
||||
streams[0].min_bitrate_bps = kMinVideoBitrateBps;
|
||||
streams[0].target_bitrate_bps = config.tl0_bitrate_kbps * 1000;
|
||||
streams[0].max_bitrate_bps = config.tl1_bitrate_kbps * 1000;
|
||||
streams[0].temporal_layer_thresholds_bps.clear();
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
const char kVp8ForceFallbackEncoderFieldTrial[] =
|
||||
"WebRTC-VP8-Forced-Fallback-Encoder";
|
||||
"WebRTC-VP8-Forced-Fallback-Encoder-v2";
|
||||
|
||||
bool EnableForcedFallback(const cricket::VideoCodec& codec) {
|
||||
if (!webrtc::field_trial::IsEnabled(kVp8ForceFallbackEncoderFieldTrial))
|
||||
@ -38,35 +38,32 @@ bool IsForcedFallbackPossible(const VideoCodec& codec_settings) {
|
||||
codec_settings.VP8().numberOfTemporalLayers == 1;
|
||||
}
|
||||
|
||||
void GetForcedFallbackParamsFromFieldTrialGroup(uint32_t* param_low_kbps,
|
||||
uint32_t* param_high_kbps,
|
||||
int64_t* param_min_low_ms) {
|
||||
RTC_DCHECK(param_low_kbps);
|
||||
RTC_DCHECK(param_high_kbps);
|
||||
RTC_DCHECK(param_min_low_ms);
|
||||
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 low_kbps;
|
||||
int high_kbps;
|
||||
int min_low_ms;
|
||||
int min_pixels;
|
||||
if (sscanf(group.c_str(), "Enabled-%d,%d,%d,%d", &low_kbps, &high_kbps,
|
||||
&min_low_ms, &min_pixels) != 4) {
|
||||
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_low_ms <= 0 || min_pixels <= 0 || low_kbps <= 0 ||
|
||||
high_kbps <= low_kbps) {
|
||||
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_low_kbps = low_kbps;
|
||||
*param_high_kbps = high_kbps;
|
||||
*param_min_low_ms = min_low_ms;
|
||||
*param_min_pixels = min_pixels;
|
||||
*param_max_pixels = max_pixels;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@ -85,9 +82,10 @@ VideoEncoderSoftwareFallbackWrapper::VideoEncoderSoftwareFallbackWrapper(
|
||||
callback_(nullptr),
|
||||
forced_fallback_possible_(EnableForcedFallback(codec)) {
|
||||
if (forced_fallback_possible_) {
|
||||
GetForcedFallbackParamsFromFieldTrialGroup(&forced_fallback_.low_kbps,
|
||||
&forced_fallback_.high_kbps,
|
||||
&forced_fallback_.min_low_ms);
|
||||
GetForcedFallbackParamsFromFieldTrialGroup(
|
||||
&forced_fallback_.min_pixels_, &forced_fallback_.max_pixels_,
|
||||
encoder_->GetScalingSettings().min_pixels_per_frame -
|
||||
1); // No HW below.
|
||||
}
|
||||
}
|
||||
|
||||
@ -117,9 +115,6 @@ bool VideoEncoderSoftwareFallbackWrapper::InitFallbackEncoder() {
|
||||
if (channel_parameters_set_)
|
||||
fallback_encoder_->SetChannelParameters(packet_loss_, rtt_);
|
||||
|
||||
fallback_implementation_name_ =
|
||||
std::string(fallback_encoder_->ImplementationName()) +
|
||||
" (fallback from: " + encoder_->ImplementationName() + ")";
|
||||
// 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.
|
||||
@ -145,7 +140,11 @@ int32_t VideoEncoderSoftwareFallbackWrapper::InitEncode(
|
||||
if (TryReInitForcedFallbackEncoder()) {
|
||||
return WEBRTC_VIDEO_CODEC_OK;
|
||||
}
|
||||
forced_fallback_.Reset();
|
||||
// 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);
|
||||
@ -190,27 +189,12 @@ int32_t VideoEncoderSoftwareFallbackWrapper::Encode(
|
||||
const VideoFrame& frame,
|
||||
const CodecSpecificInfo* codec_specific_info,
|
||||
const std::vector<FrameType>* frame_types) {
|
||||
if (TryReleaseForcedFallbackEncoder()) {
|
||||
// Frame may have been converted from kNative to kI420 during fallback.
|
||||
if (encoder_->SupportsNativeHandle() &&
|
||||
frame.video_frame_buffer()->type() != VideoFrameBuffer::Type::kNative) {
|
||||
RTC_LOG(LS_WARNING)
|
||||
<< "Encoder supports native frames, dropping one frame "
|
||||
<< "to avoid possible reconfig due to format change.";
|
||||
return WEBRTC_VIDEO_CODEC_ERROR;
|
||||
}
|
||||
}
|
||||
if (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) ||
|
||||
(ret == WEBRTC_VIDEO_CODEC_OK && RequestForcedFallback());
|
||||
bool fallback_requested = (ret == WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE);
|
||||
if (fallback_requested && InitFallbackEncoder()) {
|
||||
// Fallback was successful.
|
||||
if (ret == WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE)
|
||||
forced_fallback_.Reset(); // Not a forced fallback.
|
||||
if (frame.video_frame_buffer()->type() == VideoFrameBuffer::Type::kNative &&
|
||||
!fallback_encoder_->SupportsNativeHandle()) {
|
||||
RTC_LOG(LS_WARNING) << "Fallback encoder doesn't support native frames, "
|
||||
@ -256,8 +240,21 @@ bool VideoEncoderSoftwareFallbackWrapper::SupportsNativeHandle() const {
|
||||
|
||||
VideoEncoder::ScalingSettings
|
||||
VideoEncoderSoftwareFallbackWrapper::GetScalingSettings() const {
|
||||
if (forced_fallback_possible_ && fallback_encoder_)
|
||||
return fallback_encoder_->GetScalingSettings();
|
||||
if (forced_fallback_possible_) {
|
||||
if (forced_fallback_.active_) {
|
||||
return VideoEncoder::ScalingSettings(
|
||||
codec_settings_.VP8().automaticResizeOn,
|
||||
forced_fallback_.min_pixels_);
|
||||
}
|
||||
const auto settings = encoder_->GetScalingSettings();
|
||||
if (settings.thresholds) {
|
||||
return VideoEncoder::ScalingSettings(
|
||||
settings.enabled, settings.thresholds->low, settings.thresholds->high,
|
||||
forced_fallback_.min_pixels_);
|
||||
}
|
||||
return VideoEncoder::ScalingSettings(settings.enabled,
|
||||
forced_fallback_.min_pixels_);
|
||||
}
|
||||
return encoder_->GetScalingSettings();
|
||||
}
|
||||
|
||||
@ -269,45 +266,32 @@ const char *VideoEncoderSoftwareFallbackWrapper::ImplementationName() const {
|
||||
|
||||
bool VideoEncoderSoftwareFallbackWrapper::IsForcedFallbackActive() const {
|
||||
return (forced_fallback_possible_ && fallback_encoder_ &&
|
||||
forced_fallback_.start_ms);
|
||||
forced_fallback_.active_);
|
||||
}
|
||||
|
||||
bool VideoEncoderSoftwareFallbackWrapper::RequestForcedFallback() {
|
||||
if (!forced_fallback_possible_ || fallback_encoder_ || !rates_set_)
|
||||
return false;
|
||||
|
||||
// No fallback encoder.
|
||||
return forced_fallback_.ShouldStart(bitrate_allocation_.get_sum_kbps(),
|
||||
codec_settings_);
|
||||
}
|
||||
|
||||
bool VideoEncoderSoftwareFallbackWrapper::TryReleaseForcedFallbackEncoder() {
|
||||
if (!IsForcedFallbackActive())
|
||||
return false;
|
||||
|
||||
if (!forced_fallback_.ShouldStop(bitrate_allocation_.get_sum_kbps(),
|
||||
codec_settings_)) {
|
||||
bool VideoEncoderSoftwareFallbackWrapper::TryInitForcedFallbackEncoder() {
|
||||
if (!forced_fallback_possible_ || fallback_encoder_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Release the forced fallback encoder.
|
||||
if (encoder_->InitEncode(&codec_settings_, number_of_cores_,
|
||||
max_payload_size_) == WEBRTC_VIDEO_CODEC_OK) {
|
||||
RTC_LOG(LS_INFO)
|
||||
<< "Stop forced SW encoder fallback, max bitrate exceeded.";
|
||||
fallback_encoder_->Release();
|
||||
fallback_encoder_.reset();
|
||||
forced_fallback_.Reset();
|
||||
return true;
|
||||
// Fallback not active.
|
||||
if (!forced_fallback_.IsValid(codec_settings_)) {
|
||||
return false;
|
||||
}
|
||||
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())
|
||||
if (!IsForcedFallbackActive()) {
|
||||
return false;
|
||||
|
||||
// Encoder reconfigured.
|
||||
}
|
||||
// Forced fallback active.
|
||||
if (!forced_fallback_.IsValid(codec_settings_)) {
|
||||
RTC_LOG(LS_INFO) << "Stop forced SW encoder fallback, max pixels exceeded.";
|
||||
return false;
|
||||
@ -336,35 +320,6 @@ void VideoEncoderSoftwareFallbackWrapper::ValidateSettingsForForcedFallback() {
|
||||
}
|
||||
}
|
||||
|
||||
bool VideoEncoderSoftwareFallbackWrapper::ForcedFallbackParams::ShouldStart(
|
||||
uint32_t bitrate_kbps,
|
||||
const VideoCodec& codec) {
|
||||
if (bitrate_kbps > low_kbps || !IsValid(codec)) {
|
||||
start_ms.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Has bitrate been below |low_kbps| for long enough duration.
|
||||
int64_t now_ms = rtc::TimeMillis();
|
||||
if (!start_ms)
|
||||
start_ms.emplace(now_ms);
|
||||
|
||||
if ((now_ms - *start_ms) >= min_low_ms) {
|
||||
RTC_LOG(LS_INFO) << "Request forced SW encoder fallback.";
|
||||
// In case the request fails, update time to avoid too frequent requests.
|
||||
start_ms.emplace(now_ms);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VideoEncoderSoftwareFallbackWrapper::ForcedFallbackParams::ShouldStop(
|
||||
uint32_t bitrate_kbps,
|
||||
const VideoCodec& codec) const {
|
||||
return bitrate_kbps >= high_kbps &&
|
||||
(codec.width * codec.height >= kMinPixelsStop);
|
||||
}
|
||||
|
||||
void VideoEncoderSoftwareFallbackWrapper::MaybeModifyCodecForFallback() {
|
||||
// We have a specific case for H264 ConstrainedBaseline because that is the
|
||||
// only supported profile in Sw fallback.
|
||||
|
||||
@ -51,30 +51,21 @@ class VideoEncoderSoftwareFallbackWrapper : public VideoEncoder {
|
||||
bool InitFallbackEncoder();
|
||||
|
||||
// If |forced_fallback_possible_| is true:
|
||||
// The forced fallback is requested if the target bitrate is below |low_kbps|
|
||||
// for more than |min_low_ms| and the input video resolution is not larger
|
||||
// than |kMaxPixelsStart|.
|
||||
// If the bitrate is above |high_kbps| and the resolution is not smaller than
|
||||
// |kMinPixelsStop|, the forced fallback is requested to immediately be
|
||||
// stopped.
|
||||
// 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 ShouldStart(uint32_t bitrate_kbps, const VideoCodec& codec);
|
||||
bool ShouldStop(uint32_t bitrate_kbps, const VideoCodec& codec) const;
|
||||
void Reset() { start_ms.reset(); }
|
||||
bool IsValid(const VideoCodec& codec) const {
|
||||
return codec.width * codec.height <= kMaxPixelsStart;
|
||||
return codec.width * codec.height <= max_pixels_;
|
||||
}
|
||||
rtc::Optional<int64_t> start_ms; // Set when bitrate is below |low_kbps|.
|
||||
uint32_t low_kbps = 100;
|
||||
uint32_t high_kbps = 150;
|
||||
int64_t min_low_ms = 10000;
|
||||
const int kMaxPixelsStart = 320 * 240;
|
||||
const int kMinPixelsStop = 320 * 180;
|
||||
|
||||
bool active_ = false;
|
||||
int min_pixels_ = 320 * 180;
|
||||
int max_pixels_ = 320 * 240;
|
||||
};
|
||||
|
||||
bool RequestForcedFallback();
|
||||
bool TryReleaseForcedFallbackEncoder();
|
||||
bool TryInitForcedFallbackEncoder();
|
||||
bool TryReInitForcedFallbackEncoder();
|
||||
void ValidateSettingsForForcedFallback();
|
||||
bool IsForcedFallbackActive() const;
|
||||
@ -100,7 +91,6 @@ class VideoEncoderSoftwareFallbackWrapper : public VideoEncoder {
|
||||
std::unique_ptr<webrtc::VideoEncoder> encoder_;
|
||||
|
||||
std::unique_ptr<webrtc::VideoEncoder> fallback_encoder_;
|
||||
std::string fallback_implementation_name_;
|
||||
EncodedImageCallback* callback_;
|
||||
|
||||
bool forced_fallback_possible_;
|
||||
|
||||
@ -30,6 +30,8 @@ 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 {
|
||||
@ -97,7 +99,7 @@ class VideoEncoderSoftwareFallbackWrapperTest : public ::testing::Test {
|
||||
}
|
||||
|
||||
VideoEncoder::ScalingSettings GetScalingSettings() const override {
|
||||
return VideoEncoder::ScalingSettings(true);
|
||||
return VideoEncoder::ScalingSettings(true, kLowThreshold, kHighThreshold);
|
||||
}
|
||||
|
||||
int init_encode_count_ = 0;
|
||||
@ -325,12 +327,9 @@ TEST_F(VideoEncoderSoftwareFallbackWrapperTest,
|
||||
}
|
||||
|
||||
namespace {
|
||||
const int kLowKbps = 220;
|
||||
const int kHighKbps = 300;
|
||||
const int kMinLowDurationMs = 4000;
|
||||
const int kBitrateKbps = 200;
|
||||
const int kMinPixelsPerFrame = 1;
|
||||
const int kMinPixelsStop = 320 * 180;
|
||||
const char kFieldTrial[] = "WebRTC-VP8-Forced-Fallback-Encoder";
|
||||
const char kFieldTrial[] = "WebRTC-VP8-Forced-Fallback-Encoder-v2";
|
||||
} // namespace
|
||||
|
||||
class ForcedFallbackTest : public VideoEncoderSoftwareFallbackWrapperTest {
|
||||
@ -344,9 +343,6 @@ class ForcedFallbackTest : public VideoEncoderSoftwareFallbackWrapperTest {
|
||||
void SetUp() override {
|
||||
clock_.SetTimeMicros(1234);
|
||||
ConfigureVp8Codec();
|
||||
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, fallback_wrapper_.InitEncode(
|
||||
&codec_, kNumCores, kMaxPayloadSize));
|
||||
EXPECT_EQ(1, fake_encoder_->init_encode_count_);
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
@ -369,6 +365,14 @@ class ForcedFallbackTest : public VideoEncoderSoftwareFallbackWrapperTest {
|
||||
new SimulcastRateAllocator(codec_, std::move(tl_factory)));
|
||||
}
|
||||
|
||||
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(
|
||||
@ -393,10 +397,8 @@ class ForcedFallbackTestEnabled : public ForcedFallbackTest {
|
||||
public:
|
||||
ForcedFallbackTestEnabled()
|
||||
: ForcedFallbackTest(std::string(kFieldTrial) + "/Enabled-" +
|
||||
std::to_string(kLowKbps) + "," +
|
||||
std::to_string(kHighKbps) + "," +
|
||||
std::to_string(kMinLowDurationMs) + "," +
|
||||
std::to_string(kMinPixelsPerFrame) + "/") {}
|
||||
std::to_string(kMinPixelsPerFrame) + "," +
|
||||
std::to_string(kWidth * kHeight) + ",30000/") {}
|
||||
};
|
||||
|
||||
class ForcedFallbackTestDisabled : public ForcedFallbackTest {
|
||||
@ -406,242 +408,136 @@ class ForcedFallbackTestDisabled : public ForcedFallbackTest {
|
||||
};
|
||||
|
||||
TEST_F(ForcedFallbackTestDisabled, NoFallbackWithoutFieldTrial) {
|
||||
// Bitrate at low threshold.
|
||||
SetRateAllocation(kLowKbps);
|
||||
// Resolution above max threshold.
|
||||
InitEncode(kWidth + 1, kHeight);
|
||||
EXPECT_EQ(1, fake_encoder_->init_encode_count_);
|
||||
EncodeFrameAndVerifyLastName("fake-encoder");
|
||||
// Duration passed, expect no fallback.
|
||||
clock_.AdvanceTime(rtc::TimeDelta::FromMilliseconds(kMinLowDurationMs));
|
||||
|
||||
// Resolution at max threshold.
|
||||
InitEncode(kWidth, kHeight);
|
||||
EncodeFrameAndVerifyLastName("fake-encoder");
|
||||
}
|
||||
|
||||
TEST_F(ForcedFallbackTestEnabled, FallbackIfAtLowLimit) {
|
||||
// Bitrate at low threshold.
|
||||
SetRateAllocation(kLowKbps);
|
||||
TEST_F(ForcedFallbackTestEnabled, FallbackIfAtMaxResolutionLimit) {
|
||||
// Resolution above max threshold.
|
||||
InitEncode(kWidth + 1, kHeight);
|
||||
EXPECT_EQ(1, fake_encoder_->init_encode_count_);
|
||||
EncodeFrameAndVerifyLastName("fake-encoder");
|
||||
// Duration passed, expect fallback.
|
||||
clock_.AdvanceTime(rtc::TimeDelta::FromMilliseconds(kMinLowDurationMs));
|
||||
|
||||
// Resolution at max threshold.
|
||||
InitEncode(kWidth, kHeight);
|
||||
EncodeFrameAndVerifyLastName("libvpx");
|
||||
}
|
||||
|
||||
TEST_F(ForcedFallbackTestEnabled, NoFallbackIfNotAtLowLimit) {
|
||||
// Bitrate above low threshold.
|
||||
SetRateAllocation(kLowKbps + 1);
|
||||
TEST_F(ForcedFallbackTestEnabled, FallbackIsKeptWhenInitEncodeIsCalled) {
|
||||
// Resolution above max threshold.
|
||||
InitEncode(kWidth + 1, kHeight);
|
||||
EXPECT_EQ(1, fake_encoder_->init_encode_count_);
|
||||
EncodeFrameAndVerifyLastName("fake-encoder");
|
||||
// Duration passed, expect no fallback.
|
||||
clock_.AdvanceTime(rtc::TimeDelta::FromMilliseconds(kMinLowDurationMs));
|
||||
EncodeFrameAndVerifyLastName("fake-encoder");
|
||||
}
|
||||
|
||||
TEST_F(ForcedFallbackTestEnabled, NoFallbackIfResolutionIsTooLarge) {
|
||||
// Resolution above max pixels.
|
||||
codec_.width += 1;
|
||||
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
|
||||
fallback_wrapper_.InitEncode(&codec_, kNumCores, kMaxPayloadSize));
|
||||
// Bitrate at low threshold.
|
||||
SetRateAllocation(kLowKbps);
|
||||
EncodeFrameAndVerifyLastName("fake-encoder");
|
||||
// Duration passed, expect no fallback.
|
||||
clock_.AdvanceTime(rtc::TimeDelta::FromMilliseconds(kMinLowDurationMs));
|
||||
EncodeFrameAndVerifyLastName("fake-encoder");
|
||||
}
|
||||
// Resolution at max threshold.
|
||||
InitEncode(kWidth, kHeight);
|
||||
EncodeFrameAndVerifyLastName("libvpx");
|
||||
|
||||
TEST_F(ForcedFallbackTestEnabled, FallbackIfMinDurationPassed) {
|
||||
// Bitrate at low threshold.
|
||||
SetRateAllocation(kLowKbps);
|
||||
EncodeFrameAndVerifyLastName("fake-encoder");
|
||||
// Duration not passed, expect no fallback.
|
||||
clock_.AdvanceTime(rtc::TimeDelta::FromMilliseconds(kMinLowDurationMs - 1));
|
||||
EncodeFrameAndVerifyLastName("fake-encoder");
|
||||
// Duration passed, expect fallback.
|
||||
clock_.AdvanceTime(rtc::TimeDelta::FromMilliseconds(1));
|
||||
// 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, FallbackStartTimeResetIfAboveLowLimit) {
|
||||
// Bitrate at low threshold, start time set.
|
||||
SetRateAllocation(kLowKbps);
|
||||
EncodeFrameAndVerifyLastName("fake-encoder");
|
||||
// Duration not passed, expect no fallback.
|
||||
clock_.AdvanceTime(rtc::TimeDelta::FromMilliseconds(kMinLowDurationMs - 1));
|
||||
TEST_F(ForcedFallbackTestEnabled, FallbackIsEndedWhenResolutionIsTooLarge) {
|
||||
// Resolution above max threshold.
|
||||
InitEncode(kWidth + 1, kHeight);
|
||||
EXPECT_EQ(1, fake_encoder_->init_encode_count_);
|
||||
EncodeFrameAndVerifyLastName("fake-encoder");
|
||||
|
||||
// Bitrate above low threshold, start time reset.
|
||||
SetRateAllocation(kLowKbps + 1);
|
||||
clock_.AdvanceTime(rtc::TimeDelta::FromMilliseconds(1));
|
||||
EncodeFrameAndVerifyLastName("fake-encoder");
|
||||
|
||||
// Bitrate at low threshold, start time set.
|
||||
SetRateAllocation(kLowKbps);
|
||||
EncodeFrameAndVerifyLastName("fake-encoder");
|
||||
// Duration not passed, expect no fallback.
|
||||
clock_.AdvanceTime(rtc::TimeDelta::FromMilliseconds(kMinLowDurationMs - 1));
|
||||
EncodeFrameAndVerifyLastName("fake-encoder");
|
||||
// Duration passed, expect fallback.
|
||||
clock_.AdvanceTime(rtc::TimeDelta::FromMilliseconds(1));
|
||||
// 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, FallbackEndsIfAtHighLimit) {
|
||||
// Bitrate at low threshold.
|
||||
SetRateAllocation(kLowKbps);
|
||||
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");
|
||||
// Duration passed, expect fallback.
|
||||
clock_.AdvanceTime(rtc::TimeDelta::FromMilliseconds(kMinLowDurationMs));
|
||||
EncodeFrameAndVerifyLastName("libvpx");
|
||||
// Bitrate below high threshold, expect fallback.
|
||||
SetRateAllocation(kHighKbps - 1);
|
||||
EncodeFrameAndVerifyLastName("libvpx");
|
||||
// Bitrate at high threshold, expect fallback ended.
|
||||
SetRateAllocation(kHighKbps);
|
||||
|
||||
// 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 = 0; i < kNumRuns; ++i) {
|
||||
// Bitrate at low threshold.
|
||||
SetRateAllocation(kLowKbps);
|
||||
EncodeFrameAndVerifyLastName("fake-encoder");
|
||||
// Duration passed, expect fallback.
|
||||
clock_.AdvanceTime(rtc::TimeDelta::FromMilliseconds(kMinLowDurationMs));
|
||||
for (int i = 1; i <= kNumRuns; ++i) {
|
||||
// Resolution at max threshold.
|
||||
InitEncode(kWidth, kHeight);
|
||||
EncodeFrameAndVerifyLastName("libvpx");
|
||||
// Bitrate at high threshold, expect fallback ended.
|
||||
SetRateAllocation(kHighKbps);
|
||||
// Resolution above max threshold.
|
||||
InitEncode(kWidth + 1, kHeight);
|
||||
EXPECT_EQ(i, fake_encoder_->init_encode_count_);
|
||||
EncodeFrameAndVerifyLastName("fake-encoder");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ForcedFallbackTestEnabled, DropsFirstNonNativeFrameAfterFallbackEnds) {
|
||||
fake_encoder_->supports_native_handle_ = true;
|
||||
|
||||
// Bitrate at low threshold.
|
||||
SetRateAllocation(kLowKbps);
|
||||
EncodeFrameAndVerifyLastName("fake-encoder");
|
||||
// Duration passed, expect fallback.
|
||||
clock_.AdvanceTime(rtc::TimeDelta::FromMilliseconds(kMinLowDurationMs));
|
||||
EncodeFrameAndVerifyLastName("libvpx");
|
||||
// Bitrate at high threshold, fallback should be ended but first non-native
|
||||
// frame dropped (i.e. frame not encoded).
|
||||
SetRateAllocation(kHighKbps);
|
||||
EncodeFrameAndVerifyLastName("libvpx", WEBRTC_VIDEO_CODEC_ERROR);
|
||||
// Next frame should be encoded.
|
||||
EncodeFrameAndVerifyLastName("fake-encoder");
|
||||
}
|
||||
|
||||
TEST_F(ForcedFallbackTestEnabled, FallbackIsKeptWhenInitEncodeIsCalled) {
|
||||
// Bitrate below low threshold.
|
||||
SetRateAllocation(kLowKbps - 1);
|
||||
EncodeFrameAndVerifyLastName("fake-encoder");
|
||||
// Duration passed, expect fallback.
|
||||
clock_.AdvanceTime(rtc::TimeDelta::FromMilliseconds(kMinLowDurationMs));
|
||||
EncodeFrameAndVerifyLastName("libvpx");
|
||||
|
||||
// Re-initialize encoder, still expect fallback.
|
||||
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
|
||||
fallback_wrapper_.InitEncode(&codec_, kNumCores, kMaxPayloadSize));
|
||||
EXPECT_EQ(1, fake_encoder_->init_encode_count_); // No change.
|
||||
SetRateAllocation(kLowKbps);
|
||||
EncodeFrameAndVerifyLastName("libvpx");
|
||||
}
|
||||
|
||||
TEST_F(ForcedFallbackTestEnabled, FallbackIsEndedWhenResolutionIsTooLarge) {
|
||||
// Bitrate below low threshold.
|
||||
SetRateAllocation(kLowKbps - 1);
|
||||
EncodeFrameAndVerifyLastName("fake-encoder");
|
||||
// Duration passed, expect fallback.
|
||||
clock_.AdvanceTime(rtc::TimeDelta::FromMilliseconds(kMinLowDurationMs));
|
||||
EncodeFrameAndVerifyLastName("libvpx");
|
||||
|
||||
// Re-initialize encoder with a larger resolution, expect no fallback.
|
||||
codec_.width += 1;
|
||||
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
|
||||
fallback_wrapper_.InitEncode(&codec_, kNumCores, kMaxPayloadSize));
|
||||
EXPECT_EQ(2, fake_encoder_->init_encode_count_);
|
||||
SetRateAllocation(kLowKbps);
|
||||
EncodeFrameAndVerifyLastName("fake-encoder");
|
||||
}
|
||||
|
||||
TEST_F(ForcedFallbackTestEnabled, FallbackIsEndedForNonValidSettings) {
|
||||
// Bitrate below low threshold.
|
||||
SetRateAllocation(kLowKbps - 1);
|
||||
EncodeFrameAndVerifyLastName("fake-encoder");
|
||||
// Duration passed, expect fallback.
|
||||
clock_.AdvanceTime(rtc::TimeDelta::FromMilliseconds(kMinLowDurationMs));
|
||||
EncodeFrameAndVerifyLastName("libvpx");
|
||||
|
||||
// Re-initialize encoder with invalid setting, expect no fallback.
|
||||
codec_.VP8()->numberOfTemporalLayers = 2;
|
||||
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
|
||||
fallback_wrapper_.InitEncode(&codec_, kNumCores, kMaxPayloadSize));
|
||||
EXPECT_EQ(2, fake_encoder_->init_encode_count_);
|
||||
SetRateAllocation(kLowKbps);
|
||||
TEST_F(ForcedFallbackTestDisabled, GetScaleSettings) {
|
||||
// Resolution above max threshold.
|
||||
InitEncode(kWidth + 1, 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;
|
||||
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
|
||||
fallback_wrapper_.InitEncode(&codec_, kNumCores, kMaxPayloadSize));
|
||||
EXPECT_EQ(3, fake_encoder_->init_encode_count_);
|
||||
// Bitrate at low threshold.
|
||||
SetRateAllocation(kLowKbps);
|
||||
EncodeFrameAndVerifyLastName("fake-encoder");
|
||||
// Duration passed, expect no fallback.
|
||||
clock_.AdvanceTime(rtc::TimeDelta::FromMilliseconds(kMinLowDurationMs));
|
||||
EncodeFrameAndVerifyLastName("fake-encoder");
|
||||
}
|
||||
|
||||
TEST_F(ForcedFallbackTestEnabled, GetScaleSettingsWithoutFallback) {
|
||||
// Bitrate at low threshold.
|
||||
SetRateAllocation(kLowKbps);
|
||||
EncodeFrameAndVerifyLastName("fake-encoder");
|
||||
// Default min pixels per frame should be used.
|
||||
const auto settings = fallback_wrapper_.GetScalingSettings();
|
||||
EXPECT_TRUE(settings.enabled);
|
||||
EXPECT_EQ(kDefaultMinPixelsPerFrame, settings.min_pixels_per_frame);
|
||||
}
|
||||
|
||||
TEST_F(ForcedFallbackTestEnabled, GetScaleSettingsWithFallback) {
|
||||
// Bitrate at low threshold.
|
||||
SetRateAllocation(kLowKbps);
|
||||
TEST_F(ForcedFallbackTestEnabled, GetScaleSettingsWithNoFallback) {
|
||||
// Resolution above max threshold.
|
||||
InitEncode(kWidth + 1, kHeight);
|
||||
EncodeFrameAndVerifyLastName("fake-encoder");
|
||||
// Duration passed, expect fallback.
|
||||
clock_.AdvanceTime(rtc::TimeDelta::FromMilliseconds(kMinLowDurationMs));
|
||||
|
||||
// Configured min pixels per frame should be used.
|
||||
const auto settings = fallback_wrapper_.GetScalingSettings();
|
||||
EXPECT_TRUE(settings.enabled);
|
||||
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.enabled);
|
||||
EXPECT_EQ(kMinPixelsPerFrame, settings.min_pixels_per_frame);
|
||||
}
|
||||
|
||||
TEST_F(ForcedFallbackTestEnabled, FallbackIsKeptIfResolutionIsTooSmall) {
|
||||
// Bitrate at low threshold.
|
||||
SetRateAllocation(kLowKbps);
|
||||
EncodeFrameAndVerifyLastName("fake-encoder");
|
||||
// Duration passed, expect fallback.
|
||||
clock_.AdvanceTime(rtc::TimeDelta::FromMilliseconds(kMinLowDurationMs));
|
||||
TEST_F(ForcedFallbackTestEnabled, ScalingDisabledIfResizeOff) {
|
||||
// Resolution at max threshold.
|
||||
codec_.VP8()->automaticResizeOn = false;
|
||||
InitEncode(kWidth, kHeight);
|
||||
EncodeFrameAndVerifyLastName("libvpx");
|
||||
|
||||
// Re-initialize encoder with a resolution less than |kMinPixelsStop|.
|
||||
codec_.height = kMinPixelsStop / codec_.width - 1;
|
||||
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
|
||||
fallback_wrapper_.InitEncode(&codec_, kNumCores, kMaxPayloadSize));
|
||||
EXPECT_EQ(1, fake_encoder_->init_encode_count_); // No change
|
||||
SetRateAllocation(kHighKbps - 1);
|
||||
EncodeFrameAndVerifyLastName("libvpx");
|
||||
// Bitrate at high threshold but resolution too small for fallback to end.
|
||||
SetRateAllocation(kHighKbps);
|
||||
EncodeFrameAndVerifyLastName("libvpx");
|
||||
|
||||
// Re-initialize encoder with a resolution equal to |kMinPixelsStop|.
|
||||
codec_.height++;
|
||||
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
|
||||
fallback_wrapper_.InitEncode(&codec_, kNumCores, kMaxPayloadSize));
|
||||
EXPECT_EQ(1, fake_encoder_->init_encode_count_); // No change
|
||||
SetRateAllocation(kHighKbps - 1);
|
||||
EncodeFrameAndVerifyLastName("libvpx");
|
||||
// Bitrate at high threshold and resolution large enough for fallback to end.
|
||||
SetRateAllocation(kHighKbps);
|
||||
EncodeFrameAndVerifyLastName("fake-encoder");
|
||||
// Should be disabled for automatic resize off.
|
||||
const auto settings = fallback_wrapper_.GetScalingSettings();
|
||||
EXPECT_FALSE(settings.enabled);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
@ -317,11 +317,41 @@ int GetDefaultVp9TemporalLayers() {
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char kForcedFallbackFieldTrial[] =
|
||||
"WebRTC-VP8-Forced-Fallback-Encoder-v2";
|
||||
|
||||
rtc::Optional<int> GetFallbackMinBpsFromFieldTrial() {
|
||||
if (!webrtc::field_trial::IsEnabled(kForcedFallbackFieldTrial))
|
||||
return rtc::Optional<int>();
|
||||
|
||||
std::string group =
|
||||
webrtc::field_trial::FindFullName(kForcedFallbackFieldTrial);
|
||||
if (group.empty())
|
||||
return rtc::Optional<int>();
|
||||
|
||||
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) {
|
||||
return rtc::Optional<int>();
|
||||
}
|
||||
|
||||
if (min_bps <= 0)
|
||||
return rtc::Optional<int>();
|
||||
|
||||
return rtc::Optional<int>(min_bps);
|
||||
}
|
||||
|
||||
int GetMinVideoBitrateBps() {
|
||||
return GetFallbackMinBpsFromFieldTrial().value_or(kMinVideoBitrateBps);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// Constants defined in webrtc/media/engine/constants.h
|
||||
// TODO(pbos): Move these to a separate constants.cc file.
|
||||
const int kMinVideoBitrateKbps = 30;
|
||||
const int kMinVideoBitrateBps = 30000;
|
||||
|
||||
const int kVideoMtu = 1200;
|
||||
const int kVideoRtpBufferSize = 65536;
|
||||
@ -2569,7 +2599,7 @@ std::vector<webrtc::VideoStream> EncoderStreamFactory::CreateEncoderStreams(
|
||||
stream.width = width;
|
||||
stream.height = height;
|
||||
stream.max_framerate = max_framerate_;
|
||||
stream.min_bitrate_bps = kMinVideoBitrateKbps * 1000;
|
||||
stream.min_bitrate_bps = GetMinVideoBitrateBps();
|
||||
stream.target_bitrate_bps = stream.max_bitrate_bps = max_bitrate_bps;
|
||||
stream.max_qp = max_qp_;
|
||||
|
||||
|
||||
@ -2216,6 +2216,20 @@ TEST_F(Vp9SettingsTestWith2SL3TLFlag, VerifySettings) {
|
||||
VerifySettings(kNumSpatialLayers, kNumTemporalLayers);
|
||||
}
|
||||
|
||||
TEST_F(WebRtcVideoChannelTest, VerifyMinBitrate) {
|
||||
std::vector<webrtc::VideoStream> streams = AddSendStream()->GetVideoStreams();
|
||||
ASSERT_EQ(1u, streams.size());
|
||||
EXPECT_EQ(cricket::kMinVideoBitrateBps, streams[0].min_bitrate_bps);
|
||||
}
|
||||
|
||||
TEST_F(WebRtcVideoChannelTest, VerifyMinBitrateWithForcedFallbackFieldTrial) {
|
||||
webrtc::test::ScopedFieldTrials override_field_trials_(
|
||||
"WebRTC-VP8-Forced-Fallback-Encoder-v2/Enabled-1,2,34567/");
|
||||
std::vector<webrtc::VideoStream> streams = AddSendStream()->GetVideoStreams();
|
||||
ASSERT_EQ(1u, streams.size());
|
||||
EXPECT_EQ(34567, streams[0].min_bitrate_bps);
|
||||
}
|
||||
|
||||
TEST_F(WebRtcVideoChannelTest,
|
||||
BalancedDegradationPreferenceNotSupportedWithoutFieldtrial) {
|
||||
webrtc::test::ScopedFieldTrials override_field_trials_(
|
||||
@ -4679,7 +4693,7 @@ class WebRtcVideoChannelSimulcastTest : public testing::Test {
|
||||
stream.width = capture_width;
|
||||
stream.height = capture_height;
|
||||
stream.max_framerate = kDefaultVideoMaxFramerate;
|
||||
stream.min_bitrate_bps = cricket::kMinVideoBitrateKbps * 1000;
|
||||
stream.min_bitrate_bps = cricket::kMinVideoBitrateBps;
|
||||
int max_bitrate_kbps;
|
||||
if (capture_width * capture_height <= 320 * 240) {
|
||||
max_bitrate_kbps = 600;
|
||||
|
||||
@ -104,36 +104,6 @@ TEST_F(VideoProcessorIntegrationTestMediaCodec,
|
||||
kNoVisualizationParams);
|
||||
}
|
||||
|
||||
TEST_F(VideoProcessorIntegrationTestMediaCodec,
|
||||
Foreman240p100kbpsVp8WithForcedSwFallback) {
|
||||
ScopedFieldTrials override_field_trials(
|
||||
"WebRTC-VP8-Forced-Fallback-Encoder/Enabled-150,175,10000,1/");
|
||||
|
||||
config_.filename = "foreman_320x240";
|
||||
config_.input_filename = ResourcePath(config_.filename, "yuv");
|
||||
config_.sw_fallback_encoder = true;
|
||||
config_.SetCodecSettings(kVideoCodecVP8, 1, false, false, false, false, false,
|
||||
320, 240);
|
||||
|
||||
std::vector<RateProfile> rate_profiles = {
|
||||
{100, 10, 80}, // Start below |low_kbps|.
|
||||
{100, 10, 200}, // Fallback in this bucket.
|
||||
{200, 10, kForemanNumFrames + 1}}; // Switch back here.
|
||||
|
||||
// The thresholds below may have to be tweaked to let even poor MediaCodec
|
||||
// implementations pass. If this test fails on the bots, disable it and
|
||||
// ping brandtr@.
|
||||
std::vector<RateControlThresholds> rc_thresholds = {{0, 50, 75, 70, 10, 0, 1},
|
||||
{0, 50, 25, 12, 60, 0, 1},
|
||||
{0, 65, 15, 5, 5, 0, 1}};
|
||||
|
||||
QualityThresholds quality_thresholds(33.0, 30.0, 0.90, 0.85);
|
||||
|
||||
ProcessFramesAndMaybeVerify(rate_profiles, &rc_thresholds,
|
||||
&quality_thresholds, nullptr,
|
||||
kNoVisualizationParams);
|
||||
}
|
||||
|
||||
#endif // defined(WEBRTC_ANDROID)
|
||||
|
||||
} // namespace test
|
||||
|
||||
@ -295,7 +295,8 @@ std::vector<TemporalLayers::FrameConfig> GetTemporalPattern(size_t num_layers) {
|
||||
// deregistred when deleted by SW codec (tl factory might not exist, owned by
|
||||
// SimulcastRateAllocator).
|
||||
bool ExcludeOnTemporalLayersCreated(int num_temporal_layers) {
|
||||
return webrtc::field_trial::IsEnabled("WebRTC-VP8-Forced-Fallback-Encoder") &&
|
||||
return webrtc::field_trial::IsEnabled(
|
||||
"WebRTC-VP8-Forced-Fallback-Encoder-v2") &&
|
||||
num_temporal_layers == 1;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@ -35,7 +35,6 @@ constexpr int64_t kInitialTimestampMs = 789;
|
||||
constexpr uint32_t kTimestampIncrement = 3000;
|
||||
constexpr int kNumCores = 1;
|
||||
constexpr size_t kMaxPayloadSize = 1440;
|
||||
constexpr int kMinPixelsPerFrame = 12345;
|
||||
constexpr int kDefaultMinPixelsPerFrame = 320 * 180;
|
||||
constexpr int kWidth = 172;
|
||||
constexpr int kHeight = 144;
|
||||
@ -504,22 +503,4 @@ TEST_F(TestVp8Impl, ScalingEnabledIfAutomaticResizeOn) {
|
||||
EXPECT_EQ(kDefaultMinPixelsPerFrame, settings.min_pixels_per_frame);
|
||||
}
|
||||
|
||||
class TestVp8ImplWithForcedFallbackEnabled : public TestVp8Impl {
|
||||
public:
|
||||
TestVp8ImplWithForcedFallbackEnabled()
|
||||
: TestVp8Impl("WebRTC-VP8-Forced-Fallback-Encoder/Enabled-1,2,3," +
|
||||
std::to_string(kMinPixelsPerFrame) + "/") {}
|
||||
};
|
||||
|
||||
TEST_F(TestVp8ImplWithForcedFallbackEnabled, MinPixelsPerFrameConfigured) {
|
||||
codec_settings_.VP8()->frameDroppingOn = true;
|
||||
codec_settings_.VP8()->automaticResizeOn = true;
|
||||
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
|
||||
encoder_->InitEncode(&codec_settings_, kNumCores, kMaxPayloadSize));
|
||||
|
||||
VideoEncoder::ScalingSettings settings = encoder_->GetScalingSettings();
|
||||
EXPECT_TRUE(settings.enabled);
|
||||
EXPECT_EQ(kMinPixelsPerFrame, settings.min_pixels_per_frame);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
@ -42,8 +42,6 @@ namespace {
|
||||
|
||||
const char kVp8PostProcArmFieldTrial[] = "WebRTC-VP8-Postproc-Config-Arm";
|
||||
const char kVp8GfBoostFieldTrial[] = "WebRTC-VP8-GfBoost";
|
||||
const char kVp8ForceFallbackEncoderFieldTrial[] =
|
||||
"WebRTC-VP8-Forced-Fallback-Encoder";
|
||||
|
||||
const int kTokenPartitions = VP8_ONE_TOKENPARTITION;
|
||||
enum { kVp8ErrorPropagationTh = 30 };
|
||||
@ -127,31 +125,6 @@ int NumStreamsDisabled(const std::vector<bool>& streams) {
|
||||
return num_disabled;
|
||||
}
|
||||
|
||||
rtc::Optional<int> GetForcedFallbackMinPixelsFromFieldTrialGroup() {
|
||||
if (!webrtc::field_trial::IsEnabled(kVp8ForceFallbackEncoderFieldTrial))
|
||||
return rtc::Optional<int>();
|
||||
|
||||
std::string group =
|
||||
webrtc::field_trial::FindFullName(kVp8ForceFallbackEncoderFieldTrial);
|
||||
if (group.empty())
|
||||
return rtc::Optional<int>();
|
||||
|
||||
int low_kbps;
|
||||
int high_kbps;
|
||||
int min_low_ms;
|
||||
int min_pixels;
|
||||
if (sscanf(group.c_str(), "Enabled-%d,%d,%d,%d", &low_kbps, &high_kbps,
|
||||
&min_low_ms, &min_pixels) != 4) {
|
||||
return rtc::Optional<int>();
|
||||
}
|
||||
|
||||
if (min_low_ms <= 0 || min_pixels <= 0 || low_kbps <= 0 ||
|
||||
high_kbps <= low_kbps) {
|
||||
return rtc::Optional<int>();
|
||||
}
|
||||
return rtc::Optional<int>(min_pixels);
|
||||
}
|
||||
|
||||
bool GetGfBoostPercentageFromFieldTrialGroup(int* boost_percentage) {
|
||||
std::string group = webrtc::field_trial::FindFullName(kVp8GfBoostFieldTrial);
|
||||
if (group.empty())
|
||||
@ -223,7 +196,6 @@ vpx_enc_frame_flags_t VP8EncoderImpl::EncodeFlags(
|
||||
|
||||
VP8EncoderImpl::VP8EncoderImpl()
|
||||
: use_gf_boost_(webrtc::field_trial::IsEnabled(kVp8GfBoostFieldTrial)),
|
||||
min_pixels_per_frame_(GetForcedFallbackMinPixelsFromFieldTrialGroup()),
|
||||
encoded_complete_callback_(nullptr),
|
||||
inited_(false),
|
||||
timestamp_(0),
|
||||
@ -999,10 +971,6 @@ VideoEncoder::ScalingSettings VP8EncoderImpl::GetScalingSettings() const {
|
||||
const bool enable_scaling = encoders_.size() == 1 &&
|
||||
configurations_[0].rc_dropframe_thresh > 0 &&
|
||||
codec_.VP8().automaticResizeOn;
|
||||
if (enable_scaling && min_pixels_per_frame_) {
|
||||
return VideoEncoder::ScalingSettings(enable_scaling,
|
||||
*min_pixels_per_frame_);
|
||||
}
|
||||
return VideoEncoder::ScalingSettings(enable_scaling);
|
||||
}
|
||||
|
||||
|
||||
@ -94,7 +94,6 @@ class VP8EncoderImpl : public VP8Encoder {
|
||||
uint32_t MaxIntraTarget(uint32_t optimal_buffer_size);
|
||||
|
||||
const bool use_gf_boost_;
|
||||
const rtc::Optional<int> min_pixels_per_frame_;
|
||||
|
||||
EncodedImageCallback* encoded_complete_callback_;
|
||||
VideoCodec codec_;
|
||||
|
||||
@ -130,7 +130,7 @@ PayloadRouter::PayloadRouter(const std::vector<RtpRtcp*>& rtp_modules,
|
||||
rtp_modules_(rtp_modules),
|
||||
payload_type_(payload_type),
|
||||
forced_fallback_enabled_((webrtc::field_trial::IsEnabled(
|
||||
"WebRTC-VP8-Forced-Fallback-Encoder"))) {
|
||||
"WebRTC-VP8-Forced-Fallback-Encoder-v2"))) {
|
||||
RTC_DCHECK_EQ(ssrcs.size(), rtp_modules.size());
|
||||
// SSRCs are assumed to be sorted in the same order as |rtp_modules|.
|
||||
for (uint32_t ssrc : ssrcs) {
|
||||
|
||||
@ -347,14 +347,14 @@ class PayloadRouterTest : public ::testing::Test {
|
||||
class TestWithForcedFallbackDisabled : public PayloadRouterTest {
|
||||
public:
|
||||
TestWithForcedFallbackDisabled()
|
||||
: PayloadRouterTest("WebRTC-VP8-Forced-Fallback-Encoder/Disabled/") {}
|
||||
: PayloadRouterTest("WebRTC-VP8-Forced-Fallback-Encoder-v2/Disabled/") {}
|
||||
};
|
||||
|
||||
class TestWithForcedFallbackEnabled : public PayloadRouterTest {
|
||||
public:
|
||||
TestWithForcedFallbackEnabled()
|
||||
: PayloadRouterTest(
|
||||
"WebRTC-VP8-Forced-Fallback-Encoder/Enabled-1,2,3,4/") {}
|
||||
"WebRTC-VP8-Forced-Fallback-Encoder-v2/Enabled-1,2,3/") {}
|
||||
};
|
||||
|
||||
TEST_F(TestWithForcedFallbackDisabled, PictureIdIsNotChangedForVp8) {
|
||||
|
||||
@ -26,7 +26,7 @@ const int kEncoderBitrateBps = 100000;
|
||||
const uint32_t kPictureIdWraparound = (1 << 15);
|
||||
|
||||
const char kVp8ForcedFallbackEncoderEnabled[] =
|
||||
"WebRTC-VP8-Forced-Fallback-Encoder/Enabled/";
|
||||
"WebRTC-VP8-Forced-Fallback-Encoder-v2/Enabled/";
|
||||
} // namespace
|
||||
|
||||
class PictureIdObserver : public test::RtpRtcpObserver {
|
||||
|
||||
@ -31,7 +31,7 @@ const int64_t kBucketSizeMs = 100;
|
||||
const size_t kBucketCount = 10;
|
||||
|
||||
const char kVp8ForcedFallbackEncoderFieldTrial[] =
|
||||
"WebRTC-VP8-Forced-Fallback-Encoder";
|
||||
"WebRTC-VP8-Forced-Fallback-Encoder-v2";
|
||||
const char kVp8SwCodecName[] = "libvpx";
|
||||
|
||||
// Used by histograms. Values of entries should not be changed.
|
||||
@ -85,7 +85,7 @@ bool IsForcedFallbackPossible(const CodecSpecificInfo* codec_info) {
|
||||
codec_info->codecSpecific.VP8.temporalIdx == kNoTemporalIdx);
|
||||
}
|
||||
|
||||
rtc::Optional<int> GetFallbackIntervalFromFieldTrial() {
|
||||
rtc::Optional<int> GetFallbackMaxPixelsFromFieldTrial() {
|
||||
if (!webrtc::field_trial::IsEnabled(kVp8ForcedFallbackEncoderFieldTrial))
|
||||
return rtc::Optional<int>();
|
||||
|
||||
@ -94,20 +94,19 @@ rtc::Optional<int> GetFallbackIntervalFromFieldTrial() {
|
||||
if (group.empty())
|
||||
return rtc::Optional<int>();
|
||||
|
||||
int low_kbps;
|
||||
int high_kbps;
|
||||
int min_low_ms;
|
||||
int min_pixels;
|
||||
if (sscanf(group.c_str(), "Enabled-%d,%d,%d,%d", &low_kbps, &high_kbps,
|
||||
&min_low_ms, &min_pixels) != 4) {
|
||||
int max_pixels;
|
||||
int min_bps;
|
||||
if (sscanf(group.c_str(), "Enabled-%d,%d,%d", &min_pixels, &max_pixels,
|
||||
&min_bps) != 3) {
|
||||
return rtc::Optional<int>();
|
||||
}
|
||||
|
||||
if (min_low_ms <= 0 || min_pixels <= 0 || low_kbps <= 0 ||
|
||||
high_kbps <= low_kbps) {
|
||||
return rtc::Optional<int>();
|
||||
if (min_pixels <= 0 || max_pixels <= 0 || max_pixels < min_pixels ||
|
||||
min_bps <= 0) {
|
||||
return rtc::Optional<int>(); // Do not log stats.
|
||||
}
|
||||
return rtc::Optional<int>(min_low_ms);
|
||||
return rtc::Optional<int>(max_pixels);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@ -121,7 +120,7 @@ SendStatisticsProxy::SendStatisticsProxy(
|
||||
: clock_(clock),
|
||||
payload_name_(config.encoder_settings.payload_name),
|
||||
rtp_config_(config.rtp),
|
||||
min_first_fallback_interval_ms_(GetFallbackIntervalFromFieldTrial()),
|
||||
fallback_max_pixels_(GetFallbackMaxPixelsFromFieldTrial()),
|
||||
content_type_(content_type),
|
||||
start_ms_(clock->TimeInMilliseconds()),
|
||||
encode_time_(kEncodeTimeWeigthFactor),
|
||||
@ -728,9 +727,9 @@ void SendStatisticsProxy::OnSetEncoderTargetRate(uint32_t bitrate_bps) {
|
||||
}
|
||||
|
||||
void SendStatisticsProxy::UpdateEncoderFallbackStats(
|
||||
const CodecSpecificInfo* codec_info) {
|
||||
if (!min_first_fallback_interval_ms_ ||
|
||||
!uma_container_->fallback_info_.is_possible) {
|
||||
const CodecSpecificInfo* codec_info,
|
||||
int pixels) {
|
||||
if (!fallback_max_pixels_ || !uma_container_->fallback_info_.is_possible) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -750,16 +749,11 @@ void SendStatisticsProxy::UpdateEncoderFallbackStats(
|
||||
// First or not a VP8 SW change, update stats on next call.
|
||||
return;
|
||||
}
|
||||
if (is_active && fallback_info->on_off_events == 0) {
|
||||
// The minimum set time should have passed for the first fallback (else
|
||||
// skip to avoid fallback due to failure).
|
||||
int64_t elapsed_ms = fallback_info->elapsed_ms;
|
||||
if (fallback_info->last_update_ms)
|
||||
elapsed_ms += now_ms - *(fallback_info->last_update_ms);
|
||||
if (elapsed_ms < *min_first_fallback_interval_ms_) {
|
||||
fallback_info->is_possible = false;
|
||||
return;
|
||||
}
|
||||
if (is_active && (pixels > *fallback_max_pixels_)) {
|
||||
// Pixels should not be above |fallback_max_pixels_|. If above skip to
|
||||
// avoid fallbacks due to failure.
|
||||
fallback_info->is_possible = false;
|
||||
return;
|
||||
}
|
||||
++fallback_info->on_off_events;
|
||||
}
|
||||
@ -792,7 +786,8 @@ void SendStatisticsProxy::OnSendEncodedImage(
|
||||
simulcast_idx = codec_info->codecSpecific.generic.simulcast_idx;
|
||||
}
|
||||
if (codec_info->codec_name) {
|
||||
UpdateEncoderFallbackStats(codec_info);
|
||||
UpdateEncoderFallbackStats(codec_info, encoded_image._encodedWidth *
|
||||
encoded_image._encodedHeight);
|
||||
stats_.encoder_implementation_name = codec_info->codec_name;
|
||||
}
|
||||
}
|
||||
|
||||
@ -205,13 +205,14 @@ class SendStatisticsProxy : public CpuOveruseMetricsObserver,
|
||||
const VideoStreamEncoder::AdaptCounts& quality_counts)
|
||||
RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_);
|
||||
|
||||
void UpdateEncoderFallbackStats(const CodecSpecificInfo* codec_info)
|
||||
void UpdateEncoderFallbackStats(const CodecSpecificInfo* codec_info,
|
||||
int pixels)
|
||||
RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_);
|
||||
|
||||
Clock* const clock_;
|
||||
const std::string payload_name_;
|
||||
const VideoSendStream::Config::Rtp rtp_config_;
|
||||
const rtc::Optional<int> min_first_fallback_interval_ms_;
|
||||
const rtc::Optional<int> fallback_max_pixels_;
|
||||
rtc::CriticalSection crit_;
|
||||
VideoEncoderConfig::ContentType content_type_ RTC_GUARDED_BY(crit_);
|
||||
const int64_t start_ms_;
|
||||
|
||||
@ -32,7 +32,6 @@ const int kWidth = 640;
|
||||
const int kHeight = 480;
|
||||
const int kQpIdx0 = 21;
|
||||
const int kQpIdx1 = 39;
|
||||
const int kMinFirstFallbackIntervalMs = 1500;
|
||||
const CodecSpecificInfo kDefaultCodecInfo = []() {
|
||||
CodecSpecificInfo codec_info;
|
||||
codec_info.codecType = kVideoCodecVP8;
|
||||
@ -1838,6 +1837,8 @@ class ForcedFallbackTest : public SendStatisticsProxyTest {
|
||||
codec_info_.codecSpecific.VP8.simulcastIdx = 0;
|
||||
codec_info_.codecSpecific.VP8.temporalIdx = 0;
|
||||
codec_info_.codec_name = "fake_codec";
|
||||
encoded_image_._encodedWidth = kWidth;
|
||||
encoded_image_._encodedHeight = kHeight;
|
||||
}
|
||||
|
||||
~ForcedFallbackTest() override {}
|
||||
@ -1866,15 +1867,14 @@ class ForcedFallbackTest : public SendStatisticsProxyTest {
|
||||
class ForcedFallbackDisabled : public ForcedFallbackTest {
|
||||
public:
|
||||
ForcedFallbackDisabled()
|
||||
: ForcedFallbackTest("WebRTC-VP8-Forced-Fallback-Encoder/Disabled/") {}
|
||||
: ForcedFallbackTest("WebRTC-VP8-Forced-Fallback-Encoder-v2/Disabled/") {}
|
||||
};
|
||||
|
||||
class ForcedFallbackEnabled : public ForcedFallbackTest {
|
||||
public:
|
||||
ForcedFallbackEnabled()
|
||||
: ForcedFallbackTest("WebRTC-VP8-Forced-Fallback-Encoder/Enabled-1,2," +
|
||||
std::to_string(kMinFirstFallbackIntervalMs) +
|
||||
",4/") {}
|
||||
: ForcedFallbackTest("WebRTC-VP8-Forced-Fallback-Encoder-v2/Enabled-1," +
|
||||
std::to_string(kWidth * kHeight) + ",3/") {}
|
||||
};
|
||||
|
||||
TEST_F(ForcedFallbackEnabled, StatsNotUpdatedIfMinRunTimeHasNotPassed) {
|
||||
@ -1961,8 +1961,8 @@ TEST_F(ForcedFallbackEnabled, ThreeFallbackEvents) {
|
||||
EXPECT_EQ(1, metrics::NumEvents(kPrefix + "FallbackChangesPerMinute.Vp8", 3));
|
||||
}
|
||||
|
||||
TEST_F(ForcedFallbackEnabled, NoFallbackIfMinIntervalHasNotPassed) {
|
||||
InsertEncodedFrames(1, kMinFirstFallbackIntervalMs - 1);
|
||||
TEST_F(ForcedFallbackEnabled, NoFallbackIfAboveMaxPixels) {
|
||||
encoded_image_._encodedWidth = kWidth + 1;
|
||||
codec_info_.codec_name = "libvpx";
|
||||
InsertEncodedFrames(kMinFrames, kFrameIntervalMs);
|
||||
|
||||
@ -1971,8 +1971,8 @@ TEST_F(ForcedFallbackEnabled, NoFallbackIfMinIntervalHasNotPassed) {
|
||||
EXPECT_EQ(0, metrics::NumSamples(kPrefix + "FallbackChangesPerMinute.Vp8"));
|
||||
}
|
||||
|
||||
TEST_F(ForcedFallbackEnabled, FallbackIfMinIntervalPassed) {
|
||||
InsertEncodedFrames(1, kMinFirstFallbackIntervalMs);
|
||||
TEST_F(ForcedFallbackEnabled, FallbackIfAtMaxPixels) {
|
||||
encoded_image_._encodedWidth = kWidth;
|
||||
codec_info_.codec_name = "libvpx";
|
||||
InsertEncodedFrames(kMinFrames, kFrameIntervalMs);
|
||||
|
||||
|
||||
@ -153,6 +153,38 @@ bool TransportSeqNumExtensionConfigured(const VideoSendStream::Config& config) {
|
||||
}) != extensions.end();
|
||||
}
|
||||
|
||||
const char kForcedFallbackFieldTrial[] =
|
||||
"WebRTC-VP8-Forced-Fallback-Encoder-v2";
|
||||
|
||||
rtc::Optional<int> GetFallbackMinBpsFromFieldTrial() {
|
||||
if (!webrtc::field_trial::IsEnabled(kForcedFallbackFieldTrial))
|
||||
return rtc::Optional<int>();
|
||||
|
||||
std::string group =
|
||||
webrtc::field_trial::FindFullName(kForcedFallbackFieldTrial);
|
||||
if (group.empty())
|
||||
return rtc::Optional<int>();
|
||||
|
||||
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) {
|
||||
return rtc::Optional<int>();
|
||||
}
|
||||
|
||||
if (min_bps <= 0)
|
||||
return rtc::Optional<int>();
|
||||
|
||||
return rtc::Optional<int>(min_bps);
|
||||
}
|
||||
|
||||
int GetEncoderMinBitrateBps() {
|
||||
const int kDefaultEncoderMinBitrateBps = 30000;
|
||||
return GetFallbackMinBpsFromFieldTrial().value_or(
|
||||
kDefaultEncoderMinBitrateBps);
|
||||
}
|
||||
|
||||
bool PayloadTypeSupportsSkippingFecPackets(const std::string& payload_name) {
|
||||
const VideoCodecType codecType = PayloadStringToCodecType(payload_name);
|
||||
if (codecType == kVideoCodecVP8 || codecType == kVideoCodecVP9) {
|
||||
@ -922,9 +954,8 @@ void VideoSendStreamImpl::OnEncoderConfigurationChanged(
|
||||
RTC_DCHECK_GE(config_->rtp.ssrcs.size(), streams.size());
|
||||
RTC_DCHECK_RUN_ON(worker_queue_);
|
||||
|
||||
const int kEncoderMinBitrateBps = 30000;
|
||||
encoder_min_bitrate_bps_ =
|
||||
std::max(streams[0].min_bitrate_bps, kEncoderMinBitrateBps);
|
||||
std::max(streams[0].min_bitrate_bps, GetEncoderMinBitrateBps());
|
||||
encoder_max_bitrate_bps_ = 0;
|
||||
for (const auto& stream : streams)
|
||||
encoder_max_bitrate_bps_ += stream.max_bitrate_bps;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user