Skip to content

Commit

Permalink
[Merge-M115] WebRTC HW encoders: Do SW fallback if resolution is less…
Browse files Browse the repository at this point in the history
… than 360p.

Manual HW vs SW quality testing has revealed that, to little surprise,
the lowest resolutions provide inferior quality, especially if bitrate
constrained. The HW encoders also don't like some resolutions due to
divisibility, but today we already avoid e.g. 270p due to not being
divisible by 4 so this already triggers SW for some HW encoders like
MediaFoundationVideoEncodeAccelerator. But 180p and other resolutions
suffer from bad quality. The breakeven point appears to be around 360p.

This CL puts the "avoid < 360p HW" check in RTCVideoEncoder rather than
on individual encoder implementations for two reasons:
1. This limits the logic to WebRTC meaning we don't break WebCodecs.
2. The quality issue appears to be similar on all implementations tested
   so far, power efficiency is less of a problem for lower resolutions
   anyway, and we don't have to duplicate this logic in multiple places.

TESTED=See repro steps in https://crbug.com/1448816 as well as quality
screenshots of before and after the fix: https://crbug.com/1448816#c3

(cherry picked from commit 629d58f)

Bug: 1448816
Change-Id: Ibf41b815c788c475000206a4cfd6e13ffc419e45
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4562238
Reviewed-by: Ilya Nikolaevskiy <ilnik@chromium.org>
Commit-Queue: Henrik Boström <hbos@chromium.org>
Cr-Original-Commit-Position: refs/heads/main@{#1149102}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4568339
Auto-Submit: Henrik Boström <hbos@chromium.org>
Commit-Queue: Ilya Nikolaevskiy <ilnik@chromium.org>
Cr-Commit-Position: refs/branch-heads/5790@{#109}
Cr-Branched-From: 1d71a33-refs/heads/main@{#1148114}
  • Loading branch information
henbos authored and Chromium LUCI CQ committed May 29, 2023
1 parent 2fa5fdf commit b0fbc3e
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 5 deletions.
Expand Up @@ -10,6 +10,7 @@

#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/feature_list.h"
#include "base/functional/callback_helpers.h"
#include "base/location.h"
#include "base/logging.h"
Expand Down Expand Up @@ -61,6 +62,11 @@

namespace {

// Enabled-by-default: only used as a kill-switch.
BASE_FEATURE(kForceSoftwareForLowResolutions,
"ForceSoftwareForLowResolutions",
base::FEATURE_ENABLED_BY_DEFAULT);

#if BUILDFLAG(IS_CHROMEOS_ASH) && defined(ARCH_CPU_ARM_FAMILY)
bool IsRK3399Board() {
const std::string board = base::SysInfo::GetLsbReleaseBoard();
Expand Down Expand Up @@ -1677,6 +1683,23 @@ int32_t RTCVideoEncoder::InitEncode(
<< ", height=" << codec_settings->height
<< ", startBitrate=" << codec_settings->startBitrate;

if (impl_) {
Release();
}

// Several HW encoders are known to yield worse quality compared to SW
// encoders for smaller resolutions such as 180p. (270p should also be a
// problem but some HW encoders already fallback for resolutions not divisible
// by 4.) At 360p, manual testing suggests HW and SW are roughly on par in
// terms of quality.
if (base::FeatureList::IsEnabled(kForceSoftwareForLowResolutions) &&
codec_settings->height < 360) {
LOG(WARNING)
<< "Fallback to SW due to low resolution being less than 360p ("
<< codec_settings->width << "x" << codec_settings->height << ")";
return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
}

if (profile_ >= media::H264PROFILE_MIN &&
profile_ <= media::H264PROFILE_MAX &&
(codec_settings->width % 2 != 0 || codec_settings->height % 2 != 0)) {
Expand All @@ -1687,9 +1710,6 @@ int32_t RTCVideoEncoder::InitEncode(
return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
}

if (impl_)
Release();

has_error_ = false;

uint32_t bitrate_bps = 0;
Expand Down
Expand Up @@ -56,9 +56,13 @@ namespace {
const int kInputFrameFillY = 12;
const int kInputFrameFillU = 23;
const int kInputFrameFillV = 34;
const uint16_t kInputFrameHeight = 234;
const uint16_t kInputFrameWidth = 456;
// 360p is a valid HW resolution.
const uint16_t kInputFrameWidth = 480;
const uint16_t kInputFrameHeight = 360;
const uint16_t kStartBitrate = 100;
// Less than 360p should result in SW fallback.
const uint16_t kSoftwareFallbackInputFrameWidth = 479;
const uint16_t kSoftwareFallbackInputFrameHeight = 359;

const webrtc::VideoEncoder::Capabilities kVideoEncoderCapabilities(
/* loss_notification= */ false);
Expand Down Expand Up @@ -593,6 +597,17 @@ TEST_P(RTCVideoEncoderInitTest, RepeatedInitSucceeds) {
rtc_encoder_->InitEncode(&codec, kVideoEncoderSettings));
}

TEST_P(RTCVideoEncoderInitTest, SoftwareFallbackForLowResolution) {
const webrtc::VideoCodecType codec_type = GetParam().codec_type;
CreateEncoder(codec_type);
webrtc::VideoCodec codec = GetDefaultCodec();
codec.width = kSoftwareFallbackInputFrameWidth;
codec.height = kSoftwareFallbackInputFrameHeight;
codec.codecType = codec_type;
EXPECT_EQ(WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE,
rtc_encoder_->InitEncode(&codec, kVideoEncoderSettings));
}

TEST_P(RTCVideoEncoderInitTest, CreateAndInitSucceedsForTemporalLayer) {
const webrtc::VideoCodecType codec_type = GetParam().codec_type;
if (codec_type == webrtc::kVideoCodecVP8)
Expand Down

0 comments on commit b0fbc3e

Please sign in to comment.