Skip to content

Commit

Permalink
Android: enable/disable WebRTC HW H264 with a flag.
Browse files Browse the repository at this point in the history
This cl is to enable/disable HW H264 codec on Android by
expanding an existing flag.
Qcom and Exynos codecs (with given minimum SDKs) are  white-listed at this stage.
Also there is a black list to excludes known models with
poor performance.
This cl also includes some basic performance tuning to make
sure video works normally under most common scenarios.
More study on it will be done in the following working.

BUG = 615108
CQ_INCLUDE_TRYBOTS=master.tryserver.chromium.linux:linux_optional_gpu_tests_rel;master.tryserver.chromium.mac:mac_optional_gpu_tests_rel;master.tryserver.chromium.win:win_optional_gpu_tests_rel

Review-Url: https://codereview.chromium.org/2358683002
Cr-Commit-Position: refs/heads/master@{#425819}
  • Loading branch information
braveyao authored and Commit bot committed Oct 17, 2016
1 parent eac5469 commit 899bc7e
Show file tree
Hide file tree
Showing 14 changed files with 257 additions and 50 deletions.
12 changes: 12 additions & 0 deletions chrome/app/generated_resources.grd
Original file line number Diff line number Diff line change
Expand Up @@ -5661,6 +5661,18 @@ Keep your key file in a safe place. You will need it to create new versions of y
<message name="IDS_FLAGS_WEBRTC_HW_ENCODING_DESCRIPTION" desc="Description of chrome:flags option to turn off WebRTC hardware video encoding support.">
Support in WebRTC for encoding video streams using platform hardware.
</message>
<message name="IDS_FLAGS_WEBRTC_HW_ENCODING_ALL">
Disable all hw encoding
</message>
<message name="IDS_FLAGS_WEBRTC_HW_ENCODING_VPX">
Disable vpx hw encoding
</message>
<message name="IDS_FLAGS_WEBRTC_HW_ENCODING_H264">
Disable h264 hw encoding
</message>
<message name="IDS_FLAGS_WEBRTC_HW_ENCODING_NONE">
Disable none hw encoding
</message>
<message name="IDS_FLAGS_WEBRTC_STUN_ORIGIN_NAME" desc="Name of chrome:flags option to turn on Origin header for WebRTC STUN messages">
WebRTC Stun origin header
</message>
Expand Down
15 changes: 14 additions & 1 deletion chrome/browser/about_flags.cc
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,19 @@ const FeatureEntry::Choice kSecurityChipAnimationChoices[] = {
switches::kSecurityChipAnimationAll},
};

#if defined(ENABLE_WEBRTC)
const FeatureEntry::Choice kDisableWebRtcHWEncodingChoices[] = {
{IDS_GENERIC_EXPERIMENT_CHOICE_DEFAULT, "", ""},
{IDS_FLAGS_WEBRTC_HW_ENCODING_ALL, switches::kDisableWebRtcHWEncoding, ""},
{IDS_FLAGS_WEBRTC_HW_ENCODING_VPX, switches::kDisableWebRtcHWEncoding,
switches::kDisableWebRtcHWEncodingVPx},
{IDS_FLAGS_WEBRTC_HW_ENCODING_H264, switches::kDisableWebRtcHWEncoding,
switches::kDisableWebRtcHWEncodingH264},
{IDS_FLAGS_WEBRTC_HW_ENCODING_NONE, switches::kDisableWebRtcHWEncoding,
switches::kDisableWebRtcHWEncodingNone},
};
#endif

// RECORDING USER METRICS FOR FLAGS:
// -----------------------------------------------------------------------------
// The first line of the entry is the internal name.
Expand Down Expand Up @@ -656,7 +669,7 @@ const FeatureEntry kFeatureEntries[] = {
SINGLE_DISABLE_VALUE_TYPE(switches::kDisableWebRtcHWDecoding)},
{"disable-webrtc-hw-encoding", IDS_FLAGS_WEBRTC_HW_ENCODING_NAME,
IDS_FLAGS_WEBRTC_HW_ENCODING_DESCRIPTION, kOsAndroid | kOsCrOS,
SINGLE_DISABLE_VALUE_TYPE(switches::kDisableWebRtcHWEncoding)},
MULTI_VALUE_TYPE(kDisableWebRtcHWEncodingChoices)},
{"enable-webrtc-stun-origin", IDS_FLAGS_WEBRTC_STUN_ORIGIN_NAME,
IDS_FLAGS_WEBRTC_STUN_ORIGIN_DESCRIPTION, kOsAll,
SINGLE_VALUE_TYPE(switches::kEnableWebRtcStunOrigin)},
Expand Down
4 changes: 3 additions & 1 deletion content/public/browser/gpu_utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ const gpu::GpuPreferences GetGpuPreferencesFromCommandLine() {
#endif
#if defined(ENABLE_WEBRTC)
gpu_preferences.disable_web_rtc_hw_encoding =
command_line->HasSwitch(switches::kDisableWebRtcHWEncoding);
command_line->HasSwitch(switches::kDisableWebRtcHWEncoding) &&
command_line->GetSwitchValueASCII(switches::kDisableWebRtcHWEncoding)
.empty();
#endif
#if defined(OS_WIN)
uint32_t enable_accelerated_vpx_decode_val =
Expand Down
3 changes: 3 additions & 0 deletions content/public/common/content_switches.cc
Original file line number Diff line number Diff line change
Expand Up @@ -909,6 +909,9 @@ const char kDisableWebRtcEncryption[] = "disable-webrtc-encryption";

// Disables HW encode acceleration for WebRTC.
const char kDisableWebRtcHWEncoding[] = "disable-webrtc-hw-encoding";
const char kDisableWebRtcHWEncodingVPx[] = "vpx";
const char kDisableWebRtcHWEncodingH264[] = "h264";
const char kDisableWebRtcHWEncodingNone[] = "none";

// Enables H264 HW encode acceleration for WebRTC.
const char kEnableWebRtcHWH264Encoding[] = "enable-webrtc-hw-h264-encoding";
Expand Down
3 changes: 3 additions & 0 deletions content/public/common/content_switches.h
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,9 @@ CONTENT_EXPORT extern const char kZygoteProcess[];
CONTENT_EXPORT extern const char kDisableWebRtcHWDecoding[];
CONTENT_EXPORT extern const char kDisableWebRtcEncryption[];
CONTENT_EXPORT extern const char kDisableWebRtcHWEncoding[];
CONTENT_EXPORT extern const char kDisableWebRtcHWEncodingVPx[];
CONTENT_EXPORT extern const char kDisableWebRtcHWEncodingH264[];
CONTENT_EXPORT extern const char kDisableWebRtcHWEncodingNone[];
CONTENT_EXPORT extern const char kEnableWebRtcHWH264Encoding[];
CONTENT_EXPORT extern const char kEnableWebRtcStunOrigin[];
CONTENT_EXPORT extern const char kEnforceWebRtcIPPermissionCheck[];
Expand Down
20 changes: 17 additions & 3 deletions content/renderer/media/gpu/rtc_video_encoder_factory.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@
namespace content {

namespace {
bool IsCodecDisabledByCommandLine(const base::CommandLine* cmd_line,
const std::string codec_name) {
if (!cmd_line->HasSwitch(switches::kDisableWebRtcHWEncoding))
return false;

const std::string codec_filter =
cmd_line->GetSwitchValueASCII(switches::kDisableWebRtcHWEncoding);
return codec_filter.empty() || codec_filter == codec_name;
}

// Translate from media::VideoEncodeAccelerator::SupportedProfile to
// one or more instances of cricket::WebRtcVideoEncoderFactory::VideoCodec
Expand All @@ -29,8 +38,11 @@ void VEAToWebRTCCodecs(
const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
if (profile.profile >= media::VP8PROFILE_MIN &&
profile.profile <= media::VP8PROFILE_MAX) {
codecs->push_back(cricket::WebRtcVideoEncoderFactory::VideoCodec(
webrtc::kVideoCodecVP8, "VP8", width, height, fps));
if (!IsCodecDisabledByCommandLine(cmd_line,
switches::kDisableWebRtcHWEncodingVPx)) {
codecs->push_back(cricket::WebRtcVideoEncoderFactory::VideoCodec(
webrtc::kVideoCodecVP8, "VP8", width, height, fps));
}
} else if (profile.profile >= media::H264PROFILE_MIN &&
profile.profile <= media::H264PROFILE_MAX) {
// Enable H264 HW encode for WebRTC when SW fallback is available, which is
Expand All @@ -44,7 +56,9 @@ void VEAToWebRTCCodecs(
base::FeatureList::IsEnabled(kWebRtcH264WithOpenH264FFmpeg);
#endif // BUILDFLAG(RTC_USE_H264) && !defined(MEDIA_DISABLE_FFMPEG)
if (cmd_line->HasSwitch(switches::kEnableWebRtcHWH264Encoding) ||
webrtc_h264_sw_enabled) {
webrtc_h264_sw_enabled ||
!IsCodecDisabledByCommandLine(cmd_line,
switches::kDisableWebRtcHWEncodingH264)) {
codecs->push_back(cricket::WebRtcVideoEncoderFactory::VideoCodec(
webrtc::kVideoCodecH264, "H264", width, height, fps));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,8 +238,11 @@ void PeerConnectionDependencyFactory::InitializeSignalingThread(
if (!cmd_line->HasSwitch(switches::kDisableWebRtcHWDecoding))
decoder_factory.reset(new RTCVideoDecoderFactory(gpu_factories));

if (!cmd_line->HasSwitch(switches::kDisableWebRtcHWEncoding))
if (!cmd_line->HasSwitch(switches::kDisableWebRtcHWEncoding) ||
!cmd_line->GetSwitchValueASCII(switches::kDisableWebRtcHWEncoding)
.empty()) {
encoder_factory.reset(new RTCVideoEncoderFactory(gpu_factories));
}
}

#if defined(OS_ANDROID)
Expand Down
13 changes: 12 additions & 1 deletion gpu/config/software_rendering_list_json.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const char kSoftwareRenderingListJson[] = LONG_STRING_CONST(
{
"name": "software rendering list",
// Please update the version number whenever you change this file.
"version": "11.16",
"version": "11.17",
"entries": [
{
"id": 1,
Expand Down Expand Up @@ -690,6 +690,17 @@ LONG_STRING_CONST(
"all"
]
},
{
"id": 82,
"description": "MediaCodec is still too buggy to use for encoding (b/11536167)",
"cr_bugs": [615108],
"os": {
"type": "android"
},
"features": [
"accelerated_video_encode"
]
},
{
"id": 86,
"description": "Intel Graphics Media Accelerator 3150 causes the GPU process to hang running WebGL",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
*/
@JNINamespace("media")
class MediaCodecBridge {
private static final String TAG = "cr_media";
private static final String TAG = "cr_MediaCodecBridge";

// Error code for MediaCodecBridge. Keep this value in sync with
// MediaCodecStatus in media_codec_bridge.h.
Expand Down Expand Up @@ -202,8 +202,7 @@ private static MediaCodecBridge create(
MediaCodecUtil.CodecCreationInfo info = new MediaCodecUtil.CodecCreationInfo();
try {
if (direction == MediaCodecUtil.MEDIA_CODEC_ENCODER) {
info.mediaCodec = MediaCodec.createEncoderByType(mime);
info.supportsAdaptivePlayback = false;
info = MediaCodecUtil.createEncoder(mime);
} else {
// |isSecure| only applies to video decoders.
info = MediaCodecUtil.createDecoder(mime, isSecure, requireSoftwareCodec);
Expand Down Expand Up @@ -358,6 +357,7 @@ private void setVideoBitrate(int bps) {
Bundle b = new Bundle();
b.putInt(MediaCodec.PARAMETER_KEY_VIDEO_BITRATE, bps);
mMediaCodec.setParameters(b);
Log.v(TAG, "setVideoBitrate " + bps);
}

@TargetApi(Build.VERSION_CODES.KITKAT)
Expand Down Expand Up @@ -545,6 +545,7 @@ private static MediaFormat createVideoEncoderFormat(String mime, int width, int
format.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, iFrameInterval);
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat);
Log.d(TAG, "video encoder format: " + format);
return format;
}

Expand Down
123 changes: 122 additions & 1 deletion media/base/android/java/src/org/chromium/media/MediaCodecUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,16 @@
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.MainDex;

import java.util.Arrays;
import java.util.List;
import java.util.Locale;

/**
* A collection of MediaCodec utility functions.
*/
@JNINamespace("media")
class MediaCodecUtil {
private static final String TAG = "MediaCodecUtil";
private static final String TAG = "cr_MediaCodecUtil";

// Codec direction. Keep this in sync with media_codec_direction.h.
static final int MEDIA_CODEC_DECODER = 0;
Expand Down Expand Up @@ -356,4 +358,123 @@ private static boolean codecSupportsAdaptivePlayback(MediaCodec mediaCodec, Stri
}
return false;
}

// List of supported HW encoders.
private static enum HWEncoderProperties {
QcomVp8(MimeTypes.VIDEO_VP8, "OMX.qcom.", Build.VERSION_CODES.KITKAT),
QcomH264(MimeTypes.VIDEO_H264, "OMX.qcom.", Build.VERSION_CODES.KITKAT),
ExynosVp8(MimeTypes.VIDEO_VP8, "OMX.Exynos.", Build.VERSION_CODES.M),
ExynosH264(MimeTypes.VIDEO_H264, "OMX.Exynos.", Build.VERSION_CODES.LOLLIPOP);

private final String mMime;
private final String mPrefix;
private final int mMinSDK;

private HWEncoderProperties(String mime, String prefix, int minSDK) {
this.mMime = mime;
this.mPrefix = prefix;
this.mMinSDK = minSDK;
}

public String getMime() {
return mMime;
}

public String getPrefix() {
return mPrefix;
}

public int getMinSDK() {
return mMinSDK;
}
}

// List of devices with poor H.264 encoder quality.
private static final String[] H264_ENCODER_MODEL_BLACKLIST = new String[] {
// HW H.264 encoder on below devices has poor bitrate control - actual bitrates deviates
// a lot from the target value.
"SAMSUNG-SGH-I337", "Nexus 7", "Nexus 4"};

/**
* Creates MediaCodec encoder.
* @param mime MIME type of the media.
* @return CodecCreationInfo object
*/
static CodecCreationInfo createEncoder(String mime) {
// Always return a valid CodecCreationInfo, its |mediaCodec| field will be null
// if we cannot create the codec.
CodecCreationInfo result = new CodecCreationInfo();

if (!isEncoderSupportedByDevice(mime)) return result;

try {
result.mediaCodec = MediaCodec.createEncoderByType(mime);
result.supportsAdaptivePlayback = false;
} catch (Exception e) {
Log.e(TAG, "Failed to create MediaCodec: %s", mime, e);
}
return result;
}

/**
* This is a way to blacklist misbehaving devices.
* @param mime MIME type as passed to mediaCodec.createEncoderByType(mime).
* @return true if this codec is supported for encoder on this device.
*/
@CalledByNative
static boolean isEncoderSupportedByDevice(String mime) {
// MediaCodec.setParameters is missing for JB and below, so bitrate
// can not be adjusted dynamically.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
return false;
}

// Check if this is supported HW encoder.
if (mime.equals(MimeTypes.VIDEO_H264)) {
// Check if device is in H.264 exception list.
List<String> exceptionModels = Arrays.asList(H264_ENCODER_MODEL_BLACKLIST);
if (exceptionModels.contains(Build.MODEL)) {
Log.w(TAG, "Model: " + Build.MODEL + " has blacklisted H.264 encoder.");
return false;
}
}

MediaCodecListHelper codecListHelper = new MediaCodecListHelper();
int codecCount = codecListHelper.getCodecCount();
for (int i = 0; i < codecCount; ++i) {
MediaCodecInfo info = codecListHelper.getCodecInfoAt(i);

if (!info.isEncoder() || isSoftwareCodec(info.getName())) continue;

String encoderName = null;
for (String mimeType : info.getSupportedTypes()) {
if (mimeType.equalsIgnoreCase(mime)) {
encoderName = info.getName();
break;
}
}

if (encoderName == null) {
continue; // No HW support in this codec; try the next one.
}

// Check if this is supported HW encoder.
for (HWEncoderProperties codecProperties : HWEncoderProperties.values()) {
if (!mime.equalsIgnoreCase(codecProperties.getMime())) continue;

if (encoderName.startsWith(codecProperties.getPrefix())) {
if (Build.VERSION.SDK_INT < codecProperties.getMinSDK()) {
Log.w(TAG, "Codec " + encoderName + " is disabled due to SDK version "
+ Build.VERSION.SDK_INT);
continue;
}
Log.d(TAG, "Found target encoder for mime " + mime + " : " + encoderName);
return true;
}
}
}

Log.w(TAG, "HW encoder for " + mime + " is not available on this device.");
return false;
}
}
13 changes: 13 additions & 0 deletions media/base/android/media_codec_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,14 @@ static bool IsDecoderSupportedByDevice(const std::string& android_mime_type) {
return Java_MediaCodecUtil_isDecoderSupportedForDevice(env, j_mime);
}

static bool IsEncoderSupportedByDevice(const std::string& android_mime_type) {
DCHECK(MediaCodecUtil::IsMediaCodecAvailable());
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jstring> j_mime =
ConvertUTF8ToJavaString(env, android_mime_type);
return Java_MediaCodecUtil_isEncoderSupportedByDevice(env, j_mime);
}

// static
bool MediaCodecUtil::IsMediaCodecAvailable() {
// Blacklist some devices on Jellybean as MediaCodec is buggy.
Expand Down Expand Up @@ -211,6 +219,11 @@ bool MediaCodecUtil::IsVp9DecoderAvailable() {
return IsMediaCodecAvailable() && IsDecoderSupportedByDevice(kVp9MimeType);
}

// static
bool MediaCodecUtil::IsH264EncoderAvailable() {
return IsMediaCodecAvailable() && IsEncoderSupportedByDevice(kAvcMimeType);
}

// static
bool MediaCodecUtil::IsSurfaceViewOutputSupported() {
// Disable SurfaceView output for the Samsung Galaxy S3; it does not work
Expand Down
3 changes: 3 additions & 0 deletions media/base/android/media_codec_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ class MEDIA_EXPORT MediaCodecUtil {
// Indicates if the vp9 decoder is available on this device.
static bool IsVp9DecoderAvailable();

// Indicates if the h264 encoder is available on this device.
static bool IsH264EncoderAvailable();

// Indicates if SurfaceView and MediaCodec work well together on this device.
static bool IsSurfaceViewOutputSupported();

Expand Down
Loading

0 comments on commit 899bc7e

Please sign in to comment.