summaryrefslogtreecommitdiffstats
path: root/media/base/android/java
diff options
context:
space:
mode:
authortimav <timav@chromium.org>2015-12-17 18:47:35 -0800
committerCommit bot <commit-bot@chromium.org>2015-12-18 02:49:22 +0000
commit9c8a8a18cc38403bce21fbbd70ef2c6558cc697a (patch)
tree4674b675a6834680dfa74c386c4b4865ce68e31c /media/base/android/java
parent5e60cd892b772101ba6bfb34a6649bd7e4b688e8 (diff)
downloadchromium_src-9c8a8a18cc38403bce21fbbd70ef2c6558cc697a.zip
chromium_src-9c8a8a18cc38403bce21fbbd70ef2c6558cc697a.tar.gz
chromium_src-9c8a8a18cc38403bce21fbbd70ef2c6558cc697a.tar.bz2
Blacklist MediaCodec for VP8 on Samsung S4 Mini
Refactor MediaCodecBridge.create() and MediaCodecUtil.canDecode() to extract common part. This common part creates MediaCodec decoder, queries adaptive playback capabilities and returns the results. The common part is now uses the new method MediaUtil.isDecodedSupportedForDevice() that blacklists one device (Samsung S4 Mini) for one codec (VP8). As a result of this change the MediaCodecBridge creation will fail for VP8 for this device. BUG=567897 Review URL: https://codereview.chromium.org/1506013009 Cr-Commit-Position: refs/heads/master@{#365996}
Diffstat (limited to 'media/base/android/java')
-rw-r--r--media/base/android/java/src/org/chromium/media/MediaCodecBridge.java83
-rw-r--r--media/base/android/java/src/org/chromium/media/MediaCodecUtil.java113
2 files changed, 105 insertions, 91 deletions
diff --git a/media/base/android/java/src/org/chromium/media/MediaCodecBridge.java b/media/base/android/java/src/org/chromium/media/MediaCodecBridge.java
index 3a62582..e739c27 100644
--- a/media/base/android/java/src/org/chromium/media/MediaCodecBridge.java
+++ b/media/base/android/java/src/org/chromium/media/MediaCodecBridge.java
@@ -9,8 +9,6 @@ import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.media.MediaCodec;
-import android.media.MediaCodecInfo;
-import android.media.MediaCodecList;
import android.media.MediaCrypto;
import android.media.MediaFormat;
import android.os.Build;
@@ -142,26 +140,6 @@ class MediaCodecBridge {
}
}
- @SuppressWarnings("deprecation")
- private static String getDecoderNameForMime(String mime) {
- int count = MediaCodecList.getCodecCount();
- for (int i = 0; i < count; ++i) {
- MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
- if (info.isEncoder()) {
- continue;
- }
-
- String[] supportedTypes = info.getSupportedTypes();
- for (int j = 0; j < supportedTypes.length; ++j) {
- if (supportedTypes[j].equalsIgnoreCase(mime)) {
- return info.getName();
- }
- }
- }
-
- return null;
- }
-
private MediaCodecBridge(
MediaCodec mediaCodec, String mime, boolean adaptivePlaybackSupported) {
assert mediaCodec != null;
@@ -175,47 +153,23 @@ class MediaCodecBridge {
@CalledByNative
private static MediaCodecBridge create(String mime, boolean isSecure, int direction) {
- // Creation of ".secure" codecs sometimes crash instead of throwing exceptions
- // on pre-JBMR2 devices.
- if (isSecure && Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
- return null;
- }
- MediaCodec mediaCodec = null;
- boolean adaptivePlaybackSupported = false;
+ MediaCodecUtil.CodecCreationInfo info = new MediaCodecUtil.CodecCreationInfo();
try {
- // |isSecure| only applies to video decoders.
- if (mime.startsWith("video") && isSecure
- && direction == MediaCodecUtil.MEDIA_CODEC_DECODER) {
- String decoderName = MediaCodecUtil.getDecoderNameForMime(mime);
- if (decoderName == null) {
- return null;
- }
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
- // To work around an issue that we cannot get the codec info from the secure
- // decoder, create an insecure decoder first so that we can query its codec
- // info. http://b/15587335.
- MediaCodec insecureCodec = MediaCodec.createByCodecName(decoderName);
- adaptivePlaybackSupported = codecSupportsAdaptivePlayback(insecureCodec, mime);
- insecureCodec.release();
- }
- mediaCodec = MediaCodec.createByCodecName(decoderName + ".secure");
+ if (direction == MediaCodecUtil.MEDIA_CODEC_ENCODER) {
+ info.mediaCodec = MediaCodec.createEncoderByType(mime);
+ info.supportsAdaptivePlayback = false;
} else {
- if (direction == MediaCodecUtil.MEDIA_CODEC_ENCODER) {
- mediaCodec = MediaCodec.createEncoderByType(mime);
- } else {
- mediaCodec = MediaCodec.createDecoderByType(mime);
- adaptivePlaybackSupported = codecSupportsAdaptivePlayback(mediaCodec, mime);
- }
+ // |isSecure| only applies to video decoders.
+ info = MediaCodecUtil.createDecoder(mime, isSecure);
}
} catch (Exception e) {
Log.e(TAG, "Failed to create MediaCodec: %s, isSecure: %s, direction: %d",
mime, isSecure, direction, e);
}
- if (mediaCodec == null) {
- return null;
- }
- return new MediaCodecBridge(mediaCodec, mime, adaptivePlaybackSupported);
+ if (info.mediaCodec == null) return null;
+
+ return new MediaCodecBridge(info.mediaCodec, mime, info.supportsAdaptivePlayback);
}
@CalledByNative
@@ -509,25 +463,6 @@ class MediaCodecBridge {
return width <= MAX_ADAPTIVE_PLAYBACK_WIDTH && height <= MAX_ADAPTIVE_PLAYBACK_HEIGHT;
}
- @TargetApi(Build.VERSION_CODES.KITKAT)
- private static boolean codecSupportsAdaptivePlayback(MediaCodec mediaCodec, String mime) {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT || mediaCodec == null) {
- return false;
- }
- try {
- MediaCodecInfo info = mediaCodec.getCodecInfo();
- if (info.isEncoder()) {
- return false;
- }
- MediaCodecInfo.CodecCapabilities capabilities = info.getCapabilitiesForType(mime);
- return (capabilities != null) && capabilities.isFeatureSupported(
- MediaCodecInfo.CodecCapabilities.FEATURE_AdaptivePlayback);
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Cannot retrieve codec information", e);
- }
- return false;
- }
-
@CalledByNative
private static void setCodecSpecificData(MediaFormat format, int index, byte[] bytes) {
// Codec Specific Data is set in the MediaFormat as ByteBuffer entries with keys csd-0,
diff --git a/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java b/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java
index b498b29..1bd40fd 100644
--- a/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java
+++ b/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java
@@ -16,6 +16,7 @@ import org.chromium.base.annotations.JNINamespace;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.Locale;
import java.util.Map;
/**
@@ -60,6 +61,14 @@ class MediaCodecUtil {
}
/**
+ * Class to pass parameters from createDecoder()
+ */
+ public static class CodecCreationInfo {
+ public MediaCodec mediaCodec = null;
+ public boolean supportsAdaptivePlayback = false;
+ }
+
+ /**
* @return a list of supported android codec information.
*/
@SuppressWarnings("deprecation")
@@ -168,7 +177,7 @@ class MediaCodecUtil {
* @return name of the decoder.
*/
@SuppressWarnings("deprecation")
- protected static String getDecoderNameForMime(String mime) {
+ static String getDecoderNameForMime(String mime) {
int count = MediaCodecList.getCodecCount();
for (int i = 0; i < count; ++i) {
MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
@@ -187,45 +196,115 @@ class MediaCodecUtil {
}
/**
- * Check if a given MIME type can be decoded.
+ * Check if a given MIME type can be decoded.
+ * @param mime MIME type of the media.
+ * @param secure Whether secure decoder is required.
+ * @return true if system is able to decode, or false otherwise.
+ */
+ @CalledByNative
+ private static boolean canDecode(String mime, boolean isSecure) {
+ CodecCreationInfo info = createDecoder(mime, isSecure);
+ if (info.mediaCodec == null) return false;
+
+ try {
+ info.mediaCodec.release();
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "Cannot release media codec", e);
+ }
+ return true;
+ }
+
+ /**
+ * Creates MediaCodec decoder.
* @param mime MIME type of the media.
* @param secure Whether secure decoder is required.
- * @return true if system is able to decode, or false otherwise.
+ * @return CodecCreationInfo object
*/
- @CalledByNative
- private static boolean canDecode(String mime, boolean isSecure) {
+ static CodecCreationInfo createDecoder(String mime, boolean isSecure) {
+ // Always return a valid CodecCreationInfo, its |mediaCodec| field will be null
+ // if we cannot create the codec.
+ CodecCreationInfo result = new CodecCreationInfo();
+
+ assert result.mediaCodec == null;
+
// Creation of ".secure" codecs sometimes crash instead of throwing exceptions
// on pre-JBMR2 devices.
- if (isSecure && Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
- return false;
- }
- MediaCodec mediaCodec = null;
+ if (isSecure && Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) return result;
+
+ // Do not create codec for blacklisted devices.
+ if (!isDecoderSupportedForDevice(mime)) return result;
+
try {
// |isSecure| only applies to video decoders.
if (mime.startsWith("video") && isSecure) {
String decoderName = getDecoderNameForMime(mime);
- if (decoderName == null) return false;
+ if (decoderName == null) return null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// To work around an issue that we cannot get the codec info from the secure
// decoder, create an insecure decoder first so that we can query its codec
// info. http://b/15587335.
+ // Futhermore, it is impossible to create an insecure decoder if the secure
+ // one is already created.
MediaCodec insecureCodec = MediaCodec.createByCodecName(decoderName);
+ result.supportsAdaptivePlayback =
+ codecSupportsAdaptivePlayback(insecureCodec, mime);
insecureCodec.release();
}
- mediaCodec = MediaCodec.createByCodecName(decoderName + ".secure");
+ result.mediaCodec = MediaCodec.createByCodecName(decoderName + ".secure");
} else {
- mediaCodec = MediaCodec.createDecoderByType(mime);
+ result.mediaCodec = MediaCodec.createDecoderByType(mime);
+ result.supportsAdaptivePlayback =
+ codecSupportsAdaptivePlayback(result.mediaCodec, mime);
}
} catch (Exception e) {
Log.e(TAG, "Failed to create MediaCodec: %s, isSecure: %s", mime, isSecure, e);
+ result.mediaCodec = null;
}
+ return result;
+ }
- if (mediaCodec == null) return false;
- try {
- mediaCodec.release();
- } catch (IllegalStateException e) {
- Log.e(TAG, "Cannot release media codec", e);
+ /**
+ * This is a way to blacklist misbehaving devices.
+ * Some devices cannot decode certain codecs, while other codecs work fine.
+ * @param mime MIME type as passed to mediaCodec.createDecoderByType(mime).
+ * @return true if this codec is supported for decoder on this device.
+ */
+ private static boolean isDecoderSupportedForDevice(String mime) {
+ if (mime.equals("video/x-vnd.on2.vp8")) {
+ // Samsung Galaxy S4 Mini cannot render the frames decoded with VP8
+ if (Build.MANUFACTURER.toLowerCase(Locale.getDefault()).equals("samsung")
+ && Build.MODEL.equals("GT-I9190")) {
+ Log.e(TAG, "VP8 video decoder is not supported on this device");
+ return false;
+ }
}
+
return true;
}
+
+ /**
+ * Returns true if the given codec supports adaptive playback (dynamic resolution change).
+ * @param mediaCodec the codec.
+ * @param mime MIME type that corresponds to the codec creation.
+ * @return true if this codec and mime type combination supports adaptive playback.
+ */
+ @TargetApi(Build.VERSION_CODES.KITKAT)
+ private static boolean codecSupportsAdaptivePlayback(MediaCodec mediaCodec, String mime) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT || mediaCodec == null) {
+ return false;
+ }
+ try {
+ MediaCodecInfo info = mediaCodec.getCodecInfo();
+ if (info.isEncoder()) {
+ return false;
+ }
+ MediaCodecInfo.CodecCapabilities capabilities = info.getCapabilitiesForType(mime);
+ return (capabilities != null)
+ && capabilities.isFeatureSupported(
+ MediaCodecInfo.CodecCapabilities.FEATURE_AdaptivePlayback);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Cannot retrieve codec information", e);
+ }
+ return false;
+ }
}