From 9d8feab426ce2921b3da0b8133882ee5406baf4c Mon Sep 17 00:00:00 2001 From: Qiang Lu Date: Fri, 24 Jul 2015 12:45:55 -0700 Subject: [PATCH] Bug 1178069 - Check devices capability before enable use of vp8 hardware acceleration using android.media.MediaCodecList and android.media.MediaCodecInfo r=jrmuizel --- .../peerconnection/MediaPipelineFactory.cpp | 5 +- mobile/android/base/GeckoAppShell.java | 12 ++ mobile/android/base/moz.build | 1 + .../util/HardwareCodecCapabilityUtils.java | 143 ++++++++++++++++++ .../xpcshell/data/test_gfxBlacklist_AllOS.xml | 26 ++++ .../xpcshell/test_gfxBlacklist_Version.js | 6 + widget/GfxInfoBase.cpp | 12 ++ widget/android/AndroidBridge.cpp | 21 +++ widget/android/AndroidBridge.h | 3 + widget/android/GeneratedJNIWrappers.cpp | 16 ++ widget/android/GeneratedJNIWrappers.h | 34 +++++ widget/android/GfxInfo.cpp | 21 +-- widget/nsIGfxInfo.idl | 6 +- 13 files changed, 289 insertions(+), 17 deletions(-) create mode 100644 mobile/android/base/util/HardwareCodecCapabilityUtils.java diff --git a/media/webrtc/signaling/src/peerconnection/MediaPipelineFactory.cpp b/media/webrtc/signaling/src/peerconnection/MediaPipelineFactory.cpp index df063637fb9..db5bde2d461 100644 --- a/media/webrtc/signaling/src/peerconnection/MediaPipelineFactory.cpp +++ b/media/webrtc/signaling/src/peerconnection/MediaPipelineFactory.cpp @@ -886,7 +886,7 @@ MediaPipelineFactory::EnsureExternalCodec(VideoSessionConduit& aConduit, nsCOMPtr gfxInfo = do_GetService("@mozilla.org/gfx/info;1"); if (gfxInfo) { int32_t status; - if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION, &status))) { + if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_ENCODE, &status))) { if (status != nsIGfxInfo::FEATURE_STATUS_OK) { NS_WARNING("VP8 encoder hardware is not whitelisted: disabling.\n"); } else { @@ -911,11 +911,10 @@ MediaPipelineFactory::EnsureExternalCodec(VideoSessionConduit& aConduit, nsCOMPtr gfxInfo = do_GetService("@mozilla.org/gfx/info;1"); if (gfxInfo) { int32_t status; - if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION, &status))) { + if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_DECODE, &status))) { if (status != nsIGfxInfo::FEATURE_STATUS_OK) { NS_WARNING("VP8 decoder hardware is not whitelisted: disabling.\n"); } else { - VideoDecoder* decoder; decoder = MediaCodecVideoCodec::CreateDecoder(MediaCodecVideoCodec::CodecType::CODEC_VP8); if (decoder) { diff --git a/mobile/android/base/GeckoAppShell.java b/mobile/android/base/GeckoAppShell.java index e86dda18267..25e0eadedbf 100644 --- a/mobile/android/base/GeckoAppShell.java +++ b/mobile/android/base/GeckoAppShell.java @@ -47,6 +47,7 @@ import org.mozilla.gecko.overlays.ui.ShareDialog; import org.mozilla.gecko.prompts.PromptService; import org.mozilla.gecko.util.EventCallback; import org.mozilla.gecko.util.GeckoRequest; +import org.mozilla.gecko.util.HardwareCodecCapabilityUtils; import org.mozilla.gecko.util.HardwareUtils; import org.mozilla.gecko.util.NativeEventListener; import org.mozilla.gecko.util.NativeJSContainer; @@ -92,6 +93,7 @@ import android.location.LocationManager; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.os.Handler; @@ -961,6 +963,16 @@ public class GeckoAppShell return getHandlersForIntent(intent); } + @WrapElementForJNI(stubName = "GetHWEncoderCapability") + static boolean getHWEncoderCapability() { + return HardwareCodecCapabilityUtils.getHWEncoderCapability(); + } + + @WrapElementForJNI(stubName = "GetHWDecoderCapability") + static boolean getHWDecoderCapability() { + return HardwareCodecCapabilityUtils.getHWDecoderCapability(); + } + static List queryIntentActivities(Intent intent) { final PackageManager pm = getContext().getPackageManager(); diff --git a/mobile/android/base/moz.build b/mobile/android/base/moz.build index cbe0f6bd87b..0ff04df185b 100644 --- a/mobile/android/base/moz.build +++ b/mobile/android/base/moz.build @@ -83,6 +83,7 @@ gujar.sources += [ 'util/GeckoEventListener.java', 'util/GeckoJarReader.java', 'util/GeckoRequest.java', + 'util/HardwareCodecCapabilityUtils.java', 'util/HardwareUtils.java', 'util/INIParser.java', 'util/INISection.java', diff --git a/mobile/android/base/util/HardwareCodecCapabilityUtils.java b/mobile/android/base/util/HardwareCodecCapabilityUtils.java new file mode 100644 index 00000000000..ce4649b7d06 --- /dev/null +++ b/mobile/android/base/util/HardwareCodecCapabilityUtils.java @@ -0,0 +1,143 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- + * * This Source Code Form is subject to the terms of the Mozilla Public + * * License, v. 2.0. If a copy of the MPL was not distributed with this + * * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +package org.mozilla.gecko.util; + +import org.mozilla.gecko.AppConstants.Versions; + +import android.media.MediaCodecInfo; +import android.media.MediaCodecInfo.CodecCapabilities; +import android.media.MediaCodecList; +import android.util.Log; + +public final class HardwareCodecCapabilityUtils { + private static final String LOGTAG = "GeckoHardwareCodecCapabilityUtils"; + + // List of supported HW VP8 encoders. + private static final String[] supportedVp8HwEncCodecPrefixes = + {"OMX.qcom.", "OMX.Intel." }; + // List of supported HW VP8 decoders. + private static final String[] supportedVp8HwDecCodecPrefixes = + {"OMX.qcom.", "OMX.Nvidia.", "OMX.Exynos.", "OMX.Intel." }; + private static final String VP8_MIME_TYPE = "video/x-vnd.on2.vp8"; + // NV12 color format supported by QCOM codec, but not declared in MediaCodec - + // see /hardware/qcom/media/mm-core/inc/OMX_QCOMExtns.h + private static final int + COLOR_QCOM_FORMATYUV420PackedSemiPlanar32m = 0x7FA30C04; + // Allowable color formats supported by codec - in order of preference. + private static final int[] supportedColorList = { + CodecCapabilities.COLOR_FormatYUV420Planar, + CodecCapabilities.COLOR_FormatYUV420SemiPlanar, + CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar, + COLOR_QCOM_FORMATYUV420PackedSemiPlanar32m + }; + + + public static boolean getHWEncoderCapability() { + if (Versions.feature20Plus) { + for (int i = 0; i < MediaCodecList.getCodecCount(); ++i) { + MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i); + if (!info.isEncoder()) { + continue; + } + String name = null; + for (String mimeType : info.getSupportedTypes()) { + if (mimeType.equals(VP8_MIME_TYPE)) { + name = info.getName(); + break; + } + } + if (name == null) { + continue; // No HW support in this codec; try the next one. + } + Log.e(LOGTAG, "Found candidate encoder " + name); + + // Check if this is supported encoder. + boolean supportedCodec = false; + for (String codecPrefix : supportedVp8HwEncCodecPrefixes) { + if (name.startsWith(codecPrefix)) { + supportedCodec = true; + break; + } + } + if (!supportedCodec) { + continue; + } + + // Check if codec supports either yuv420 or nv12. + CodecCapabilities capabilities = + info.getCapabilitiesForType(VP8_MIME_TYPE); + for (int colorFormat : capabilities.colorFormats) { + Log.v(LOGTAG, " Color: 0x" + Integer.toHexString(colorFormat)); + } + for (int supportedColorFormat : supportedColorList) { + for (int codecColorFormat : capabilities.colorFormats) { + if (codecColorFormat == supportedColorFormat) { + // Found supported HW Encoder. + Log.e(LOGTAG, "Found target encoder " + name + + ". Color: 0x" + Integer.toHexString(codecColorFormat)); + return true; + } + } + } + } + } + // No HW encoder. + return false; + } + + public static boolean getHWDecoderCapability() { + if (Versions.feature20Plus) { + for (int i = 0; i < MediaCodecList.getCodecCount(); ++i) { + MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i); + if (info.isEncoder()) { + continue; + } + String name = null; + for (String mimeType : info.getSupportedTypes()) { + if (mimeType.equals(VP8_MIME_TYPE)) { + name = info.getName(); + break; + } + } + if (name == null) { + continue; // No HW support in this codec; try the next one. + } + Log.e(LOGTAG, "Found candidate decoder " + name); + + // Check if this is supported decoder. + boolean supportedCodec = false; + for (String codecPrefix : supportedVp8HwDecCodecPrefixes) { + if (name.startsWith(codecPrefix)) { + supportedCodec = true; + break; + } + } + if (!supportedCodec) { + continue; + } + + // Check if codec supports either yuv420 or nv12. + CodecCapabilities capabilities = + info.getCapabilitiesForType(VP8_MIME_TYPE); + for (int colorFormat : capabilities.colorFormats) { + Log.v(LOGTAG, " Color: 0x" + Integer.toHexString(colorFormat)); + } + for (int supportedColorFormat : supportedColorList) { + for (int codecColorFormat : capabilities.colorFormats) { + if (codecColorFormat == supportedColorFormat) { + // Found supported HW decoder. + Log.e(LOGTAG, "Found target decoder " + name + + ". Color: 0x" + Integer.toHexString(codecColorFormat)); + return true; + } + } + } + } + } + return false; // No HW decoder. + } +} diff --git a/toolkit/mozapps/extensions/test/xpcshell/data/test_gfxBlacklist_AllOS.xml b/toolkit/mozapps/extensions/test/xpcshell/data/test_gfxBlacklist_AllOS.xml index b3719a6e7ab..7494591b260 100755 --- a/toolkit/mozapps/extensions/test/xpcshell/data/test_gfxBlacklist_AllOS.xml +++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_gfxBlacklist_AllOS.xml @@ -132,6 +132,32 @@ BLOCKED_DRIVER_VERSION + + All + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + WEBRTC_HW_ACCELERATION_ENCODE + BLOCKED_DRIVER_VERSION + + + + All + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + WEBRTC_HW_ACCELERATION_DECODE + BLOCKED_DRIVER_VERSION + + All 0xabcd diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_Version.js b/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_Version.js index 2f749e8a999..1a6c4457542 100755 --- a/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_Version.js +++ b/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_Version.js @@ -107,6 +107,12 @@ function run_test() { status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_WEBRTC_HW_ACCELERATION); do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK); + status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_WEBRTC_HW_ACCELERATION_ENCODE); + do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK); + + status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_WEBRTC_HW_ACCELERATION_DECODE); + do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK); + status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT3D_11_LAYERS); do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK); diff --git a/widget/GfxInfoBase.cpp b/widget/GfxInfoBase.cpp index b9d3022444e..3bbb10de5e8 100644 --- a/widget/GfxInfoBase.cpp +++ b/widget/GfxInfoBase.cpp @@ -151,6 +151,12 @@ GetPrefNameForFeature(int32_t aFeature) case nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION: name = BLACKLIST_PREF_BRANCH "webrtc.hw.acceleration"; break; + case nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_ENCODE: + name = BLACKLIST_PREF_BRANCH "webrtc.hw.acceleration.encode"; + break; + case nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_DECODE: + name = BLACKLIST_PREF_BRANCH "webrtc.hw.acceleration.decode"; + break; default: break; }; @@ -340,6 +346,10 @@ BlacklistFeatureToGfxFeature(const nsAString& aFeature) return nsIGfxInfo::FEATURE_WEBGL_MSAA; else if (aFeature.EqualsLiteral("STAGEFRIGHT")) return nsIGfxInfo::FEATURE_STAGEFRIGHT; + else if (aFeature.EqualsLiteral("WEBRTC_HW_ACCELERATION_ENCODE")) + return nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_ENCODE; + else if (aFeature.EqualsLiteral("WEBRTC_HW_ACCELERATION_DECODE")) + return nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_DECODE; else if (aFeature.EqualsLiteral("WEBRTC_HW_ACCELERATION")) return nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION; @@ -972,6 +982,8 @@ GfxInfoBase::EvaluateDownloadedBlacklist(nsTArray& aDriverInfo) nsIGfxInfo::FEATURE_OPENGL_LAYERS, nsIGfxInfo::FEATURE_WEBGL_OPENGL, nsIGfxInfo::FEATURE_WEBGL_ANGLE, + nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_ENCODE, + nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_DECODE, nsIGfxInfo::FEATURE_WEBGL_MSAA, nsIGfxInfo::FEATURE_STAGEFRIGHT, nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION, diff --git a/widget/android/AndroidBridge.cpp b/widget/android/AndroidBridge.cpp index b141b16b75d..807410e3a22 100644 --- a/widget/android/AndroidBridge.cpp +++ b/widget/android/AndroidBridge.cpp @@ -471,6 +471,27 @@ AndroidBridge::GetHandlersForMimeType(const nsAString& aMimeType, return true; } +bool +AndroidBridge::GetHWEncoderCapability() +{ + ALOG_BRIDGE("AndroidBridge::GetHWEncoderCapability"); + + bool value = GeckoAppShell::GetHWEncoderCapability(); + + return value; +} + + +bool +AndroidBridge::GetHWDecoderCapability() +{ + ALOG_BRIDGE("AndroidBridge::GetHWDecoderCapability"); + + bool value = GeckoAppShell::GetHWDecoderCapability(); + + return value; +} + bool AndroidBridge::GetHandlersForURL(const nsAString& aURL, nsIMutableArray* aHandlersArray, diff --git a/widget/android/AndroidBridge.h b/widget/android/AndroidBridge.h index ec89f075d3f..8d97b7fc02c 100644 --- a/widget/android/AndroidBridge.h +++ b/widget/android/AndroidBridge.h @@ -207,6 +207,9 @@ public: nsIHandlerApp **aDefaultApp = nullptr, const nsAString& aAction = EmptyString()); + bool GetHWEncoderCapability(); + bool GetHWDecoderCapability(); + void GetMimeTypeFromExtensions(const nsACString& aFileExt, nsCString& aMimeType); void GetExtensionFromMimeType(const nsACString& aMimeType, nsACString& aFileExt); diff --git a/widget/android/GeneratedJNIWrappers.cpp b/widget/android/GeneratedJNIWrappers.cpp index fddf93ef1dd..e8681c87135 100644 --- a/widget/android/GeneratedJNIWrappers.cpp +++ b/widget/android/GeneratedJNIWrappers.cpp @@ -278,6 +278,22 @@ mozilla::jni::String::LocalRef GeckoAppShell::GetExternalPublicDirectory(mozilla return mozilla::jni::Method::Call(nullptr, nullptr, a0); } +constexpr char GeckoAppShell::GetHWDecoderCapability_t::name[]; +constexpr char GeckoAppShell::GetHWDecoderCapability_t::signature[]; + +bool GeckoAppShell::GetHWDecoderCapability() +{ + return mozilla::jni::Method::Call(nullptr, nullptr); +} + +constexpr char GeckoAppShell::GetHWEncoderCapability_t::name[]; +constexpr char GeckoAppShell::GetHWEncoderCapability_t::signature[]; + +bool GeckoAppShell::GetHWEncoderCapability() +{ + return mozilla::jni::Method::Call(nullptr, nullptr); +} + constexpr char GeckoAppShell::GetHandlersForMimeTypeWrapper_t::name[]; constexpr char GeckoAppShell::GetHandlersForMimeTypeWrapper_t::signature[]; diff --git a/widget/android/GeneratedJNIWrappers.h b/widget/android/GeneratedJNIWrappers.h index ff2d6964e07..c0ee1a020ee 100644 --- a/widget/android/GeneratedJNIWrappers.h +++ b/widget/android/GeneratedJNIWrappers.h @@ -644,6 +644,40 @@ public: static mozilla::jni::String::LocalRef GetExternalPublicDirectory(mozilla::jni::String::Param); +public: + struct GetHWDecoderCapability_t { + typedef GeckoAppShell Owner; + typedef bool ReturnType; + typedef bool SetterType; + typedef mozilla::jni::Args<> Args; + static constexpr char name[] = "getHWDecoderCapability"; + static constexpr char signature[] = + "()Z"; + static const bool isStatic = true; + static const bool isMultithreaded = false; + static const mozilla::jni::ExceptionMode exceptionMode = + mozilla::jni::ExceptionMode::ABORT; + }; + + static bool GetHWDecoderCapability(); + +public: + struct GetHWEncoderCapability_t { + typedef GeckoAppShell Owner; + typedef bool ReturnType; + typedef bool SetterType; + typedef mozilla::jni::Args<> Args; + static constexpr char name[] = "getHWEncoderCapability"; + static constexpr char signature[] = + "()Z"; + static const bool isStatic = true; + static const bool isMultithreaded = false; + static const mozilla::jni::ExceptionMode exceptionMode = + mozilla::jni::ExceptionMode::ABORT; + }; + + static bool GetHWEncoderCapability(); + public: struct GetHandlersForMimeTypeWrapper_t { typedef GeckoAppShell Owner; diff --git a/widget/android/GfxInfo.cpp b/widget/android/GfxInfo.cpp index 5fdf1fa99b0..2555b08c1cb 100644 --- a/widget/android/GfxInfo.cpp +++ b/widget/android/GfxInfo.cpp @@ -587,20 +587,15 @@ GfxInfo::GetFeatureStatusImpl(int32_t aFeature, } } - if (aFeature == FEATURE_WEBRTC_HW_ACCELERATION) { - NS_LossyConvertUTF16toASCII cManufacturer(mManufacturer); - NS_LossyConvertUTF16toASCII cModel(mModel); - NS_LossyConvertUTF16toASCII cHardware(mHardware); - - if (cHardware.EqualsLiteral("hammerhead") && - CompareVersions(mOSVersion.get(), "4.4.2") >= 0 && - cManufacturer.Equals("lge", nsCaseInsensitiveCStringComparator()) && - cModel.Equals("nexus 5", nsCaseInsensitiveCStringComparator())) { - *aStatus = nsIGfxInfo::FEATURE_STATUS_OK; + if (aFeature == FEATURE_WEBRTC_HW_ACCELERATION_ENCODE) { + if (mozilla::AndroidBridge::Bridge()) { + *aStatus = mozilla::AndroidBridge::Bridge()->GetHWEncoderCapability() ? nsIGfxInfo::FEATURE_STATUS_OK : nsIGfxInfo::FEATURE_BLOCKED_DEVICE; return NS_OK; - } else { - // Blocklist all other devices except Nexus 5 which VP8 hardware acceleration is supported - *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE; + } + } + if (aFeature == FEATURE_WEBRTC_HW_ACCELERATION_DECODE) { + if (mozilla::AndroidBridge::Bridge()) { + *aStatus = mozilla::AndroidBridge::Bridge()->GetHWDecoderCapability() ? nsIGfxInfo::FEATURE_STATUS_OK : nsIGfxInfo::FEATURE_BLOCKED_DEVICE; return NS_OK; } } diff --git a/widget/nsIGfxInfo.idl b/widget/nsIGfxInfo.idl index 1e76c22c897..5e6492b0bd8 100644 --- a/widget/nsIGfxInfo.idl +++ b/widget/nsIGfxInfo.idl @@ -8,7 +8,7 @@ /* NOTE: this interface is completely undesigned, not stable and likely to change */ -[scriptable, uuid(98690931-c9a5-4675-9ab4-90932ec32bf2)] +[scriptable, uuid(4b5ea59e-af89-44f7-8c1c-2dea47a170d1)] interface nsIGfxInfo : nsISupports { /* @@ -102,6 +102,10 @@ interface nsIGfxInfo : nsISupports const long FEATURE_HARDWARE_VIDEO_DECODING = 12; /* Whether Direct3D 11 is supported for ANGLE, starting in 38. */ const long FEATURE_DIRECT3D_11_ANGLE = 13; + /* Whether Webrtc Hardware acceleration is supported, starting in 42. */ + const long FEATURE_WEBRTC_HW_ACCELERATION_ENCODE = 14; + /* Whether Webrtc Hardware acceleration is supported, starting in 42. */ + const long FEATURE_WEBRTC_HW_ACCELERATION_DECODE = 15; /* * A set of return values from GetFeatureStatus