Added user-defined predicate to filter video codec implementations.

Ability to provide user defined predicate to disable particular
codec in particular circumstances was added. This could help
addressing mysterious crashes on specific Android devices.

Bug: webrtc:10029
Change-Id: I7ad81f4b1351aa68f036c0ee3b6d32fbf0f697ed
Reviewed-on: https://webrtc-review.googlesource.com/c/111781
Reviewed-by: Sami Kalliomäki <sakal@webrtc.org>
Commit-Queue: Sami Kalliomäki <sakal@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#25820}
This commit is contained in:
Yura Yaroshevich
2018-11-28 15:41:17 +03:00
committed by Commit Bot
parent 7f7e973362
commit 68478b8287
6 changed files with 179 additions and 30 deletions

View File

@ -189,6 +189,7 @@ if (is_android) {
rtc_android_library("base_java") {
java_files = [
"api/org/webrtc/RefCounted.java",
"api/org/webrtc/Predicate.java",
"src/java/org/webrtc/CalledByNative.java",
"src/java/org/webrtc/CalledByNativeUnchecked.java",
"src/java/org/webrtc/Histogram.java",

View File

@ -10,10 +10,29 @@
package org.webrtc;
import android.media.MediaCodecInfo;
import java.util.Arrays;
import javax.annotation.Nullable;
/** Factory for Android hardware VideoDecoders. */
public class HardwareVideoDecoderFactory extends MediaCodecVideoDecoderFactory {
private final static Predicate<MediaCodecInfo> defaultAllowedPredicate =
new Predicate<MediaCodecInfo>() {
private String[] prefixBlacklist =
Arrays.copyOf(MediaCodecUtils.SOFTWARE_IMPLEMENTATION_PREFIXES,
MediaCodecUtils.SOFTWARE_IMPLEMENTATION_PREFIXES.length);
@Override
public boolean test(MediaCodecInfo arg) {
final String name = arg.getName();
for (String prefix : prefixBlacklist) {
if (name.startsWith(prefix)) {
return false;
}
}
return true;
}
};
/** Creates a HardwareVideoDecoderFactory that does not use surface textures. */
@Deprecated // Not removed yet to avoid breaking callers.
public HardwareVideoDecoderFactory() {
@ -27,7 +46,21 @@ public class HardwareVideoDecoderFactory extends MediaCodecVideoDecoderFactory {
* this disables texture support.
*/
public HardwareVideoDecoderFactory(@Nullable EglBase.Context sharedContext) {
super(sharedContext, /* prefixWhitelist= */ new String[] {""},
/* prefixBlacklist= */ MediaCodecUtils.SOFTWARE_IMPLEMENTATION_PREFIXES);
this(sharedContext, /* codecAllowedPredicate= */ null);
}
/**
* Creates a HardwareVideoDecoderFactory that supports surface texture rendering.
*
* @param sharedContext The textures generated will be accessible from this context. May be null,
* this disables texture support.
* @param codecAllowedPredicate predicate to filter codecs. It is combined with the default
* predicate that only allows hardware codecs.
*/
public HardwareVideoDecoderFactory(@Nullable EglBase.Context sharedContext,
@Nullable Predicate<MediaCodecInfo> codecAllowedPredicate) {
super(sharedContext,
(codecAllowedPredicate == null ? defaultAllowedPredicate
: codecAllowedPredicate.and(defaultAllowedPredicate)));
}
}

View File

@ -42,9 +42,34 @@ public class HardwareVideoEncoderFactory implements VideoEncoderFactory {
@Nullable private final EglBase14.Context sharedContext;
private final boolean enableIntelVp8Encoder;
private final boolean enableH264HighProfile;
@Nullable private final Predicate<MediaCodecInfo> codecAllowedPredicate;
/**
* Creates a HardwareVideoEncoderFactory that supports surface texture encoding.
*
* @param sharedContext The textures generated will be accessible from this context. May be null,
* this disables texture support.
* @param enableIntelVp8Encoder true if Intel's VP8 encoder enabled.
* @param enableH264HighProfile true if H264 High Profile enabled.
*/
public HardwareVideoEncoderFactory(
EglBase.Context sharedContext, boolean enableIntelVp8Encoder, boolean enableH264HighProfile) {
this(sharedContext, enableIntelVp8Encoder, enableH264HighProfile,
/* codecAllowedPredicate= */ null);
}
/**
* Creates a HardwareVideoEncoderFactory that supports surface texture encoding.
*
* @param sharedContext The textures generated will be accessible from this context. May be null,
* this disables texture support.
* @param enableIntelVp8Encoder true if Intel's VP8 encoder enabled.
* @param enableH264HighProfile true if H264 High Profile enabled.
* @param codecAllowedPredicate optional predicate to filter codecs. All codecs are allowed
* when predicate is not provided.
*/
public HardwareVideoEncoderFactory(EglBase.Context sharedContext, boolean enableIntelVp8Encoder,
boolean enableH264HighProfile, @Nullable Predicate<MediaCodecInfo> codecAllowedPredicate) {
// Texture mode requires EglBase14.
if (sharedContext instanceof EglBase14.Context) {
this.sharedContext = (EglBase14.Context) sharedContext;
@ -54,6 +79,7 @@ public class HardwareVideoEncoderFactory implements VideoEncoderFactory {
}
this.enableIntelVp8Encoder = enableIntelVp8Encoder;
this.enableH264HighProfile = enableH264HighProfile;
this.codecAllowedPredicate = codecAllowedPredicate;
}
@Deprecated
@ -164,7 +190,7 @@ public class HardwareVideoEncoderFactory implements VideoEncoderFactory {
== null) {
return false;
}
return isHardwareSupportedInCurrentSdk(info, type);
return isHardwareSupportedInCurrentSdk(info, type) && isMediaCodecAllowed(info);
}
// Returns true if the given MediaCodecInfo indicates a hardware module that is supported on the
@ -212,6 +238,13 @@ public class HardwareVideoEncoderFactory implements VideoEncoderFactory {
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP);
}
private boolean isMediaCodecAllowed(MediaCodecInfo info) {
if (codecAllowedPredicate == null) {
return true;
}
return codecAllowedPredicate.test(info);
}
private int getKeyFrameIntervalSec(VideoCodecType type) {
switch (type) {
case VP8: // Fallthrough intended.

View File

@ -10,10 +10,33 @@
package org.webrtc;
import android.media.MediaCodecInfo;
import java.util.Arrays;
import javax.annotation.Nullable;
/** Factory for Android platform software VideoDecoders. */
public class PlatformSoftwareVideoDecoderFactory extends MediaCodecVideoDecoderFactory {
/**
* Default allowed predicate.
*/
private static final Predicate<MediaCodecInfo> defaultAllowedPredicate =
new Predicate<MediaCodecInfo>() {
private String[] prefixWhitelist =
Arrays.copyOf(MediaCodecUtils.SOFTWARE_IMPLEMENTATION_PREFIXES,
MediaCodecUtils.SOFTWARE_IMPLEMENTATION_PREFIXES.length);
@Override
public boolean test(MediaCodecInfo arg) {
final String name = arg.getName();
for (String prefix : prefixWhitelist) {
if (name.startsWith(prefix)) {
return true;
}
}
return false;
}
};
/**
* Creates a PlatformSoftwareVideoDecoderFactory that supports surface texture rendering.
*
@ -21,7 +44,6 @@ public class PlatformSoftwareVideoDecoderFactory extends MediaCodecVideoDecoderF
* this disables texture support.
*/
public PlatformSoftwareVideoDecoderFactory(@Nullable EglBase.Context sharedContext) {
super(sharedContext, /* prefixWhitelist= */ MediaCodecUtils.SOFTWARE_IMPLEMENTATION_PREFIXES,
/* prefixBlacklist= */ new String[] {});
super(sharedContext, defaultAllowedPredicate);
}
}

View File

@ -0,0 +1,73 @@
/*
* Copyright 2018 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.
*/
package org.webrtc;
/**
* Represents a predicate (boolean-valued function) of one argument.
*/
public interface Predicate<T> {
/**
* Evaluates this predicate on the given argument.
*
* @param arg the input argument
* @return true if the input argument matches the predicate, otherwise false
*/
boolean test(T arg);
/**
* Returns a composed predicate that represents a short-circuiting logical OR of this predicate
* and another. When evaluating the composed predicate, if this predicate is true, then the other
* predicate is not evaluated.
*
* @param other a predicate that will be logically-ORed with this predicate
* @return a composed predicate that represents the short-circuiting logical OR of this predicate
* and the other predicate
*/
default Predicate<T> or(Predicate<? super T> other) {
return new Predicate<T>() {
@Override
public boolean test(T arg) {
return Predicate.this.test(arg) || other.test(arg);
}
};
}
/**
* Returns a composed predicate that represents a short-circuiting logical AND of this predicate
* and another.
*
* @param other a predicate that will be logically-ANDed with this predicate
* @return a composed predicate that represents the short-circuiting logical AND of this predicate
* and the other predicate
*/
default Predicate<T> and(Predicate<? super T> other) {
return new Predicate<T>() {
@Override
public boolean test(T arg) {
return Predicate.this.test(arg) && other.test(arg);
}
};
}
/**
* Returns a predicate that represents the logical negation of this predicate.
*
* @return a predicate that represents the logical negation of this predicate
*/
default Predicate<T> negate() {
return new Predicate<T>() {
@Override
public boolean test(T arg) {
return !Predicate.this.test(arg);
}
};
}
}

View File

@ -28,22 +28,20 @@ class MediaCodecVideoDecoderFactory implements VideoDecoderFactory {
private static final String TAG = "MediaCodecVideoDecoderFactory";
private final @Nullable EglBase.Context sharedContext;
private final String[] prefixWhitelist;
private final String[] prefixBlacklist;
private final @Nullable Predicate<MediaCodecInfo> codecAllowedPredicate;
/**
* MediaCodecVideoDecoderFactory will support codecs whitelisted excluding those blacklisted.
* MediaCodecVideoDecoderFactory with support of codecs filtering.
*
* @param sharedContext The textures generated will be accessible from this context. May be null,
* this disables texture support.
* @param prefixWhitelist List of codec prefixes to be whitelisted.
* @param prefixBlacklist List of codec prefixes to be blacklisted.
* @param codecAllowedPredicate optional predicate to test if codec allowed. All codecs are
* allowed when predicate is not provided.
*/
public MediaCodecVideoDecoderFactory(
@Nullable EglBase.Context sharedContext, String[] prefixWhitelist, String[] prefixBlacklist) {
public MediaCodecVideoDecoderFactory(@Nullable EglBase.Context sharedContext,
@Nullable Predicate<MediaCodecInfo> codecAllowedPredicate) {
this.sharedContext = sharedContext;
this.prefixWhitelist = Arrays.copyOf(prefixWhitelist, prefixWhitelist.length);
this.prefixBlacklist = Arrays.copyOf(prefixBlacklist, prefixBlacklist.length);
this.codecAllowedPredicate = codecAllowedPredicate;
}
@Nullable
@ -123,25 +121,14 @@ class MediaCodecVideoDecoderFactory implements VideoDecoderFactory {
== null) {
return false;
}
return isWhitelisted(name) && !isBlacklisted(name);
return isCodecAllowed(info);
}
private boolean isWhitelisted(String name) {
for (String prefix : prefixWhitelist) {
if (name.startsWith(prefix)) {
return true;
}
private boolean isCodecAllowed(MediaCodecInfo info) {
if (codecAllowedPredicate == null) {
return true;
}
return false;
}
private boolean isBlacklisted(String name) {
for (String prefix : prefixBlacklist) {
if (name.startsWith(prefix)) {
return true;
}
}
return false;
return codecAllowedPredicate.test(info);
}
private boolean isH264HighProfileSupported(MediaCodecInfo info) {