summaryrefslogtreecommitdiffstats
path: root/dom/media/ipc/MFCDMParent.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/ipc/MFCDMParent.cpp')
-rw-r--r--dom/media/ipc/MFCDMParent.cpp229
1 files changed, 145 insertions, 84 deletions
diff --git a/dom/media/ipc/MFCDMParent.cpp b/dom/media/ipc/MFCDMParent.cpp
index 2e91048b88..4570fbe838 100644
--- a/dom/media/ipc/MFCDMParent.cpp
+++ b/dom/media/ipc/MFCDMParent.cpp
@@ -5,6 +5,7 @@
#include "MFCDMParent.h"
#include <mfmediaengine.h>
+#include <unknwnbase.h>
#include <wtypes.h>
#define INITGUID // Enable DEFINE_PROPERTYKEY()
#include <propkeydef.h> // For DEFINE_PROPERTYKEY() definition
@@ -92,6 +93,8 @@ StaticMutex sFactoryMutex;
static nsTHashMap<nsStringHashKey, ComPtr<IMFContentDecryptionModuleFactory>>
sFactoryMap;
static CopyableTArray<MFCDMCapabilitiesIPDL> sCapabilities;
+StaticMutex sCapabilitesMutex;
+static ComPtr<IUnknown> sMediaEngineClassFactory;
// RAIIized PROPVARIANT. See
// third_party/libwebrtc/modules/audio_device/win/core_audio_utility_win.h
@@ -166,6 +169,11 @@ static nsString GetHdcpPolicy(const dom::HDCPVersion& aMinHdcpVersion) {
return nsString(u"hdcp=1");
}
+static bool RequireClearLead(const nsString& aKeySystem) {
+ return aKeySystem.EqualsLiteral(kWidevineExperiment2KeySystemName) ||
+ aKeySystem.EqualsLiteral(kPlayReadyHardwareClearLeadKeySystemName);
+}
+
static void BuildCapabilitiesArray(
const nsTArray<MFCDMMediaCapability>& aCapabilities,
AutoPropVar& capabilitiesPropOut) {
@@ -464,8 +472,10 @@ LPCWSTR MFCDMParent::GetCDMLibraryName(const nsString& aKeySystem) {
/* static */
void MFCDMParent::Shutdown() {
+ StaticMutexAutoLock lock(sCapabilitesMutex);
sFactoryMap.Clear();
sCapabilities.Clear();
+ sMediaEngineClassFactory.Reset();
}
/* static */
@@ -500,10 +510,13 @@ HRESULT MFCDMParent::LoadFactory(
NS_ConvertUTF16toUTF8(aKeySystem).get());
ComPtr<IMFContentDecryptionModuleFactory> cdmFactory;
if (loadFromPlatform) {
+ if (!sMediaEngineClassFactory) {
+ MFCDM_RETURN_IF_FAILED(CoCreateInstance(
+ CLSID_MFMediaEngineClassFactory, nullptr, CLSCTX_INPROC_SERVER,
+ IID_PPV_ARGS(&sMediaEngineClassFactory)));
+ }
ComPtr<IMFMediaEngineClassFactory4> clsFactory;
- MFCDM_RETURN_IF_FAILED(CoCreateInstance(CLSID_MFMediaEngineClassFactory,
- nullptr, CLSCTX_INPROC_SERVER,
- IID_PPV_ARGS(&clsFactory)));
+ MFCDM_RETURN_IF_FAILED(sMediaEngineClassFactory.As(&clsFactory));
MFCDM_RETURN_IF_FAILED(clsFactory->CreateContentDecryptionModuleFactory(
MapKeySystem(aKeySystem).get(), IID_PPV_ARGS(&cdmFactory)));
aFactoryOut.Swap(cdmFactory);
@@ -617,12 +630,8 @@ static bool FactorySupports(ComPtr<IMFContentDecryptionModuleFactory>& aFactory,
// use another way to check the capabilities.
if (IsPlayReadyKeySystemAndSupported(aKeySystem) &&
StaticPrefs::media_eme_playready_istypesupportedex()) {
- ComPtr<IMFMediaEngineClassFactory> spFactory;
ComPtr<IMFExtendedDRMTypeSupport> spDrmTypeSupport;
- MFCDM_RETURN_BOOL_IF_FAILED(
- CoCreateInstance(CLSID_MFMediaEngineClassFactory, NULL,
- CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&spFactory)));
- MFCDM_RETURN_BOOL_IF_FAILED(spFactory.As(&spDrmTypeSupport));
+ MFCDM_RETURN_BOOL_IF_FAILED(sMediaEngineClassFactory.As(&spDrmTypeSupport));
BSTR keySystem = aIsHWSecure
? CreateBSTRFromConstChar(kPlayReadyKeySystemHardware)
: CreateBSTRFromConstChar(kPlayReadyKeySystemName);
@@ -699,46 +708,55 @@ MFCDMParent::GetAllKeySystemsCapabilities() {
new CapabilitiesPromise::Private(__func__);
Unused << backgroundTaskQueue->Dispatch(NS_NewRunnableFunction(__func__, [p] {
MFCDM_PARENT_SLOG("GetAllKeySystemsCapabilities");
- if (sCapabilities.IsEmpty()) {
- enum SecureLevel : bool {
- Software = false,
- Hardware = true,
- };
- const nsTArray<std::pair<nsString, SecureLevel>> kKeySystems{
- std::pair<nsString, SecureLevel>(
- NS_ConvertUTF8toUTF16(kPlayReadyKeySystemName),
- SecureLevel::Software),
- std::pair<nsString, SecureLevel>(
- NS_ConvertUTF8toUTF16(kPlayReadyKeySystemHardware),
- SecureLevel::Hardware),
- std::pair<nsString, SecureLevel>(
- NS_ConvertUTF8toUTF16(kPlayReadyHardwareClearLeadKeySystemName),
- SecureLevel::Hardware),
- std::pair<nsString, SecureLevel>(
- NS_ConvertUTF8toUTF16(kWidevineExperimentKeySystemName),
- SecureLevel::Hardware),
- std::pair<nsString, SecureLevel>(
- NS_ConvertUTF8toUTF16(kWidevineExperiment2KeySystemName),
- SecureLevel::Hardware),
- };
- for (const auto& keySystem : kKeySystems) {
- // Only check the capabilites if the relative prefs for the key system
- // are ON.
- if (IsPlayReadyKeySystemAndSupported(keySystem.first) ||
- IsWidevineExperimentKeySystemAndSupported(keySystem.first)) {
- MFCDMCapabilitiesIPDL* c = sCapabilities.AppendElement();
- GetCapabilities(keySystem.first, keySystem.second, nullptr, *c);
+ enum SecureLevel : bool {
+ Software = false,
+ Hardware = true,
+ };
+ const nsTArray<std::pair<nsString, SecureLevel>> kKeySystems{
+ std::pair<nsString, SecureLevel>(
+ NS_ConvertUTF8toUTF16(kPlayReadyKeySystemName),
+ SecureLevel::Software),
+ std::pair<nsString, SecureLevel>(
+ NS_ConvertUTF8toUTF16(kPlayReadyKeySystemHardware),
+ SecureLevel::Hardware),
+ std::pair<nsString, SecureLevel>(
+ NS_ConvertUTF8toUTF16(kPlayReadyHardwareClearLeadKeySystemName),
+ SecureLevel::Hardware),
+ std::pair<nsString, SecureLevel>(
+ NS_ConvertUTF8toUTF16(kWidevineExperimentKeySystemName),
+ SecureLevel::Hardware),
+ std::pair<nsString, SecureLevel>(
+ NS_ConvertUTF8toUTF16(kWidevineExperiment2KeySystemName),
+ SecureLevel::Hardware),
+ };
+
+ CopyableTArray<MFCDMCapabilitiesIPDL> capabilitiesArr;
+ for (const auto& keySystem : kKeySystems) {
+ // Only check the capabilites if the relative prefs for the key system
+ // are ON.
+ if (IsPlayReadyKeySystemAndSupported(keySystem.first) ||
+ IsWidevineExperimentKeySystemAndSupported(keySystem.first)) {
+ MFCDMCapabilitiesIPDL* c = capabilitiesArr.AppendElement();
+ CapabilitesFlagSet flags;
+ if (keySystem.second == SecureLevel::Hardware) {
+ flags += CapabilitesFlag::HarewareDecryption;
+ }
+ flags += CapabilitesFlag::NeedHDCPCheck;
+ if (RequireClearLead(keySystem.first)) {
+ flags += CapabilitesFlag::NeedClearLeadCheck;
}
+ GetCapabilities(keySystem.first, flags, nullptr, *c);
}
}
- p->Resolve(sCapabilities, __func__);
+
+ p->Resolve(std::move(capabilitiesArr), __func__);
}));
return p;
}
/* static */
void MFCDMParent::GetCapabilities(const nsString& aKeySystem,
- const bool aIsHWSecure,
+ const CapabilitesFlagSet& aFlags,
IMFContentDecryptionModuleFactory* aFactory,
MFCDMCapabilitiesIPDL& aCapabilitiesOut) {
aCapabilitiesOut.keySystem() = aKeySystem;
@@ -747,9 +765,12 @@ void MFCDMParent::GetCapabilities(const nsString& aKeySystem,
aCapabilitiesOut.persistentState() = KeySystemConfig::Requirement::Required;
aCapabilitiesOut.distinctiveID() = KeySystemConfig::Requirement::Required;
+ const bool isHardwareDecryption =
+ aFlags.contains(CapabilitesFlag::HarewareDecryption);
+ aCapabilitiesOut.isHardwareDecryption() = isHardwareDecryption;
// Return empty capabilites for SWDRM on Windows 10 because it has the process
// leaking problem.
- if (!IsWin11OrLater() && !aIsHWSecure) {
+ if (!IsWin11OrLater() && !isHardwareDecryption) {
return;
}
@@ -758,6 +779,30 @@ void MFCDMParent::GetCapabilities(const nsString& aKeySystem,
RETURN_VOID_IF_FAILED(GetOrCreateFactory(aKeySystem, factory));
}
+ StaticMutexAutoLock lock(sCapabilitesMutex);
+ for (auto& capabilities : sCapabilities) {
+ if (capabilities.keySystem().Equals(aKeySystem) &&
+ capabilities.isHardwareDecryption() == isHardwareDecryption) {
+ MFCDM_PARENT_SLOG(
+ "Return cached capabilities for %s (hardwareDecryption=%d)",
+ NS_ConvertUTF16toUTF8(aKeySystem).get(), isHardwareDecryption);
+ if (capabilities.isHDCP22Compatible().isNothing() &&
+ aFlags.contains(CapabilitesFlag::NeedHDCPCheck)) {
+ const bool rv = IsHDCPVersionSupported(factory, aKeySystem,
+ dom::HDCPVersion::_2_2) == NS_OK;
+ MFCDM_PARENT_SLOG(
+ "Check HDCP 2.2 compatible (%d) for the cached capabilites", rv);
+ capabilities.isHDCP22Compatible() = Some(rv);
+ }
+ aCapabilitiesOut = capabilities;
+ return;
+ }
+ }
+
+ MFCDM_PARENT_SLOG(
+ "Query capabilities for %s from the factory (hardwareDecryption=%d)",
+ NS_ConvertUTF16toUTF8(aKeySystem).get(), isHardwareDecryption);
+
// Widevine requires codec type to be four CC, PlayReady is fine with both.
static auto convertCodecToFourCC =
[](const KeySystemConfig::EMECodecString& aCodec) {
@@ -809,12 +854,12 @@ void MFCDMParent::GetCapabilities(const nsString& aKeySystem,
}
if (FactorySupports(factory, aKeySystem, convertCodecToFourCC(codec),
KeySystemConfig::EMECodecString(""), nsString(u""),
- aIsHWSecure)) {
+ isHardwareDecryption)) {
MFCDMMediaCapability* c =
aCapabilitiesOut.videoCapabilities().AppendElement();
c->contentType() = NS_ConvertUTF8toUTF16(codec);
c->robustness() =
- GetRobustnessStringForKeySystem(aKeySystem, aIsHWSecure);
+ GetRobustnessStringForKeySystem(aKeySystem, isHardwareDecryption);
MFCDM_PARENT_SLOG("%s: +video:%s", __func__, codec.get());
supportedVideoCodecs.AppendElement(codec);
}
@@ -831,52 +876,51 @@ void MFCDMParent::GetCapabilities(const nsString& aKeySystem,
KeySystemConfig::EME_CODEC_VORBIS,
});
for (const auto& codec : kAudioCodecs) {
- if (FactorySupports(
- factory, aKeySystem, convertCodecToFourCC(supportedVideoCodecs[0]),
- convertCodecToFourCC(codec), nsString(u""), aIsHWSecure)) {
+ // Hardware decryption is usually only used for video, so we can just check
+ // the software capabilities for audio in order to save some time. As the
+ // media foundation would create a new D3D device everytime when we check
+ // hardware decryption, which takes way longer time.
+ if (FactorySupports(factory, aKeySystem,
+ convertCodecToFourCC(supportedVideoCodecs[0]),
+ convertCodecToFourCC(codec), nsString(u""),
+ false /* aIsHWSecure */)) {
MFCDMMediaCapability* c =
aCapabilitiesOut.audioCapabilities().AppendElement();
c->contentType() = NS_ConvertUTF8toUTF16(codec);
- c->robustness() = GetRobustnessStringForKeySystem(aKeySystem, aIsHWSecure,
- false /* isVideo */);
+ c->robustness() = GetRobustnessStringForKeySystem(
+ aKeySystem, false /* aIsHWSecure */, false /* isVideo */);
MFCDM_PARENT_SLOG("%s: +audio:%s", __func__, codec.get());
}
}
- // Collect schemes supported by all video codecs.
- static nsTArray<std::pair<CryptoScheme, nsDependentString>> kSchemes = {
- std::pair<CryptoScheme, nsDependentString>(
- CryptoScheme::Cenc, u"encryption-type=cenc,encryption-iv-size=8,"),
- std::pair<CryptoScheme, nsDependentString>(
- CryptoScheme::Cbcs, u"encryption-type=cbcs,encryption-iv-size=16,")};
- for (auto& scheme : kSchemes) {
- bool ok = true;
- for (auto& codec : supportedVideoCodecs) {
- ok &= FactorySupports(
- factory, aKeySystem, convertCodecToFourCC(codec), nsCString(""),
- scheme.second /* additional feature */, aIsHWSecure);
- if (!ok) {
- break;
- }
- }
- if (ok) {
- aCapabilitiesOut.encryptionSchemes().AppendElement(scheme.first);
- MFCDM_PARENT_SLOG("%s: +scheme:%s", __func__,
- scheme.first == CryptoScheme::Cenc ? "cenc" : "cbcs");
- }
+ // 'If value is unspecified, default value of "cenc" is used.' See
+ // https://learn.microsoft.com/en-us/windows/win32/api/mfmediaengine/nf-mfmediaengine-imfextendeddrmtypesupport-istypesupportedex
+ if (!supportedVideoCodecs.IsEmpty()) {
+ aCapabilitiesOut.encryptionSchemes().AppendElement(CryptoScheme::Cenc);
+ MFCDM_PARENT_SLOG("%s: +scheme:cenc", __func__);
}
- static auto RequireClearLead = [](const nsString& aKeySystem) {
- if (aKeySystem.EqualsLiteral(kWidevineExperiment2KeySystemName) ||
- aKeySystem.EqualsLiteral(kPlayReadyHardwareClearLeadKeySystemName)) {
- return true;
+ // Check another scheme "cbcs"
+ static std::pair<CryptoScheme, nsDependentString> kCbcs =
+ std::pair<CryptoScheme, nsDependentString>(
+ CryptoScheme::Cbcs, u"encryption-type=cbcs,encryption-iv-size=16,");
+ bool ok = true;
+ for (const auto& codec : supportedVideoCodecs) {
+ ok &= FactorySupports(factory, aKeySystem, convertCodecToFourCC(codec),
+ nsCString(""), kCbcs.second /* additional feature */,
+ isHardwareDecryption);
+ if (!ok) {
+ break;
}
- return false;
- };
+ }
+ if (ok) {
+ aCapabilitiesOut.encryptionSchemes().AppendElement(kCbcs.first);
+ MFCDM_PARENT_SLOG("%s: +scheme:cbcs", __func__);
+ }
// For key system requires clearlead, every codec needs to have clear support.
// If not, then we will remove the codec from supported codec.
- if (RequireClearLead(aKeySystem)) {
+ if (aFlags.contains(CapabilitesFlag::NeedClearLeadCheck)) {
for (const auto& scheme : aCapabilitiesOut.encryptionSchemes()) {
nsTArray<KeySystemConfig::EMECodecString> noClearLeadCodecs;
for (const auto& codec : supportedVideoCodecs) {
@@ -894,9 +938,9 @@ void MFCDMParent::GetCapabilities(const nsString& aKeySystem,
} else {
additionalFeature.AppendLiteral(u"cbcs-clearlead,");
}
- bool rv =
- FactorySupports(factory, aKeySystem, convertCodecToFourCC(codec),
- nsCString(""), additionalFeature, aIsHWSecure);
+ bool rv = FactorySupports(factory, aKeySystem,
+ convertCodecToFourCC(codec), nsCString(""),
+ additionalFeature, isHardwareDecryption);
MFCDM_PARENT_SLOG("clearlead %s IV 8 bytes %s %s",
CryptoSchemeToString(scheme), codec.get(),
rv ? "supported" : "not supported");
@@ -906,7 +950,8 @@ void MFCDMParent::GetCapabilities(const nsString& aKeySystem,
// Try 16 bytes IV.
additionalFeature.AppendLiteral(u"encryption-iv-size=16,");
rv = FactorySupports(factory, aKeySystem, convertCodecToFourCC(codec),
- nsCString(""), additionalFeature, aIsHWSecure);
+ nsCString(""), additionalFeature,
+ isHardwareDecryption);
MFCDM_PARENT_SLOG("clearlead %s IV 16 bytes %s %s",
CryptoSchemeToString(scheme), codec.get(),
rv ? "supported" : "not supported");
@@ -926,9 +971,14 @@ void MFCDMParent::GetCapabilities(const nsString& aKeySystem,
}
}
- if (IsHDCPVersionSupported(factory, aKeySystem, dom::HDCPVersion::_2_2) ==
- NS_OK) {
- aCapabilitiesOut.isHDCP22Compatible() = true;
+ // Only perform HDCP if necessary, "The hdcp query (item 4) has a
+ // computationally expensive first invocation cost". See
+ // https://learn.microsoft.com/en-us/windows/win32/api/mfmediaengine/nf-mfmediaengine-imfextendeddrmtypesupport-istypesupportedex
+ if (aFlags.contains(CapabilitesFlag::NeedHDCPCheck) &&
+ IsHDCPVersionSupported(factory, aKeySystem, dom::HDCPVersion::_2_2) ==
+ NS_OK) {
+ MFCDM_PARENT_SLOG("Capabilites is compatible with HDCP 2.2");
+ aCapabilitiesOut.isHDCP22Compatible() = Some(true);
}
// TODO: don't hardcode
@@ -938,13 +988,24 @@ void MFCDMParent::GetCapabilities(const nsString& aKeySystem,
KeySystemConfig::SessionType::Temporary);
aCapabilitiesOut.sessionTypes().AppendElement(
KeySystemConfig::SessionType::PersistentLicense);
+
+ // Cache capabilities for reuse.
+ sCapabilities.AppendElement(aCapabilitiesOut);
}
mozilla::ipc::IPCResult MFCDMParent::RecvGetCapabilities(
- const bool aIsHWSecure, GetCapabilitiesResolver&& aResolver) {
+ const MFCDMCapabilitiesRequest& aRequest,
+ GetCapabilitiesResolver&& aResolver) {
MFCDM_REJECT_IF(!mFactory, NS_ERROR_DOM_NOT_SUPPORTED_ERR);
MFCDMCapabilitiesIPDL capabilities;
- GetCapabilities(mKeySystem, aIsHWSecure, mFactory.Get(), capabilities);
+ CapabilitesFlagSet flags;
+ if (aRequest.isHardwareDecryption()) {
+ flags += CapabilitesFlag::HarewareDecryption;
+ }
+ if (RequireClearLead(aRequest.keySystem())) {
+ flags += CapabilitesFlag::NeedClearLeadCheck;
+ }
+ GetCapabilities(aRequest.keySystem(), flags, mFactory.Get(), capabilities);
aResolver(std::move(capabilities));
return IPC_OK();
}