summaryrefslogtreecommitdiffstats
path: root/dom/media/eme
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dom/media/eme/EMEUtils.cpp79
-rw-r--r--dom/media/eme/EMEUtils.h12
-rw-r--r--dom/media/eme/KeySystemConfig.cpp363
-rw-r--r--dom/media/eme/KeySystemConfig.h36
-rw-r--r--dom/media/eme/MediaKeySession.cpp67
-rw-r--r--dom/media/eme/MediaKeySession.h6
-rw-r--r--dom/media/eme/MediaKeySystemAccess.cpp196
-rw-r--r--dom/media/eme/MediaKeySystemAccess.h16
-rw-r--r--dom/media/eme/MediaKeySystemAccessManager.cpp67
-rw-r--r--dom/media/eme/MediaKeySystemAccessManager.h2
-rw-r--r--dom/media/eme/mediafoundation/WMFCDMImpl.cpp155
-rw-r--r--dom/media/eme/mediafoundation/WMFCDMImpl.h24
-rw-r--r--dom/media/eme/mediafoundation/WMFCDMProxy.cpp2
-rw-r--r--dom/media/eme/metrics.yaml42
14 files changed, 615 insertions, 452 deletions
diff --git a/dom/media/eme/EMEUtils.cpp b/dom/media/eme/EMEUtils.cpp
index 5a6b645df2..19639388da 100644
--- a/dom/media/eme/EMEUtils.cpp
+++ b/dom/media/eme/EMEUtils.cpp
@@ -10,8 +10,10 @@
#include "MediaData.h"
#include "KeySystemConfig.h"
#include "mozilla/StaticPrefs_media.h"
+#include "mozilla/dom/Document.h"
#include "mozilla/dom/KeySystemNames.h"
#include "mozilla/dom/UnionTypes.h"
+#include "nsContentUtils.h"
#ifdef MOZ_WMF_CDM
# include "mozilla/PMFCDM.h"
@@ -49,37 +51,33 @@ bool IsWidevineKeySystem(const nsAString& aKeySystem) {
}
#ifdef MOZ_WMF_CDM
-bool IsPlayReadyKeySystemAndSupported(const nsAString& aKeySystem) {
- if (!StaticPrefs::media_eme_playready_enabled()) {
- return false;
- }
+bool IsPlayReadyEnabled() {
// 1=enabled encrypted and clear, 2=enabled encrytped.
- if (StaticPrefs::media_wmf_media_engine_enabled() != 1 &&
- StaticPrefs::media_wmf_media_engine_enabled() != 2) {
- return false;
- }
- return IsPlayReadyKeySystem(aKeySystem);
+ return StaticPrefs::media_eme_playready_enabled() &&
+ (StaticPrefs::media_wmf_media_engine_enabled() == 1 ||
+ StaticPrefs::media_wmf_media_engine_enabled() == 2);
}
-bool IsPlayReadyKeySystem(const nsAString& aKeySystem) {
+bool IsPlayReadyKeySystemAndSupported(const nsAString& aKeySystem) {
+ if (!IsPlayReadyEnabled()) {
+ return false;
+ }
return aKeySystem.EqualsLiteral(kPlayReadyKeySystemName) ||
aKeySystem.EqualsLiteral(kPlayReadyKeySystemHardware) ||
aKeySystem.EqualsLiteral(kPlayReadyHardwareClearLeadKeySystemName);
}
-bool IsWidevineExperimentKeySystemAndSupported(const nsAString& aKeySystem) {
- if (!StaticPrefs::media_eme_widevine_experiment_enabled()) {
- return false;
- }
+bool IsWidevineHardwareDecryptionEnabled() {
// 1=enabled encrypted and clear, 2=enabled encrytped.
- if (StaticPrefs::media_wmf_media_engine_enabled() != 1 &&
- StaticPrefs::media_wmf_media_engine_enabled() != 2) {
- return false;
- }
- return IsWidevineExperimentKeySystem(aKeySystem);
+ return StaticPrefs::media_eme_widevine_experiment_enabled() &&
+ (StaticPrefs::media_wmf_media_engine_enabled() == 1 ||
+ StaticPrefs::media_wmf_media_engine_enabled() == 2);
}
-bool IsWidevineExperimentKeySystem(const nsAString& aKeySystem) {
+bool IsWidevineExperimentKeySystemAndSupported(const nsAString& aKeySystem) {
+ if (!IsWidevineHardwareDecryptionEnabled()) {
+ return false;
+ }
return aKeySystem.EqualsLiteral(kWidevineExperimentKeySystemName) ||
aKeySystem.EqualsLiteral(kWidevineExperiment2KeySystemName);
}
@@ -121,26 +119,6 @@ nsString KeySystemToProxyName(const nsAString& aKeySystem) {
return u""_ns;
}
-#define ENUM_TO_STR(enumVal) \
- case enumVal: \
- return #enumVal
-
-const char* ToMediaKeyStatusStr(dom::MediaKeyStatus aStatus) {
- switch (aStatus) {
- ENUM_TO_STR(dom::MediaKeyStatus::Usable);
- ENUM_TO_STR(dom::MediaKeyStatus::Expired);
- ENUM_TO_STR(dom::MediaKeyStatus::Released);
- ENUM_TO_STR(dom::MediaKeyStatus::Output_restricted);
- ENUM_TO_STR(dom::MediaKeyStatus::Output_downscaled);
- ENUM_TO_STR(dom::MediaKeyStatus::Status_pending);
- ENUM_TO_STR(dom::MediaKeyStatus::Internal_error);
- default:
- return "Undefined MediaKeyStatus!";
- }
-}
-
-#undef ENUM_TO_STR
-
bool IsHardwareDecryptionSupported(
const dom::MediaKeySystemConfiguration& aConfig) {
for (const auto& capabilities : aConfig.mAudioCapabilities) {
@@ -223,7 +201,9 @@ void MFCDMCapabilitiesIPDLToKeySystemConfig(
aKeySystemConfig.mEncryptionSchemes.AppendElement(
NS_ConvertUTF8toUTF16(EncryptionSchemeStr(scheme)));
}
- aKeySystemConfig.mIsHDCP22Compatible = aCDMConfig.isHDCP22Compatible();
+ aKeySystemConfig.mIsHDCP22Compatible = aCDMConfig.isHDCP22Compatible()
+ ? *aCDMConfig.isHDCP22Compatible()
+ : false;
EME_LOG("New Capabilities=%s",
NS_ConvertUTF16toUTF8(aKeySystemConfig.GetDebugInfo()).get());
}
@@ -270,4 +250,21 @@ bool DoesKeySystemSupportHardwareDecryption(const nsAString& aKeySystem) {
return false;
}
+void DeprecationWarningLog(const dom::Document* aDocument,
+ const char* aMsgName) {
+ if (!aDocument || !aMsgName) {
+ return;
+ }
+ EME_LOG("DeprecationWarning Logging deprecation warning '%s' to WebConsole.",
+ aMsgName);
+ nsTHashMap<nsCharPtrHashKey, bool> warnings;
+ warnings.InsertOrUpdate(aMsgName, true);
+ AutoTArray<nsString, 1> params;
+ nsString& uri = *params.AppendElement();
+ Unused << aDocument->GetDocumentURI(uri);
+ nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "Media"_ns,
+ aDocument, nsContentUtils::eDOM_PROPERTIES,
+ aMsgName, params);
+}
+
} // namespace mozilla
diff --git a/dom/media/eme/EMEUtils.h b/dom/media/eme/EMEUtils.h
index 3fbf22f359..424346645c 100644
--- a/dom/media/eme/EMEUtils.h
+++ b/dom/media/eme/EMEUtils.h
@@ -23,7 +23,8 @@ struct KeySystemConfig;
namespace dom {
class ArrayBufferViewOrArrayBuffer;
-}
+class Document;
+} // namespace dom
#ifndef EME_LOG
LogModule* GetEMELog();
@@ -61,14 +62,14 @@ bool IsClearkeyKeySystem(const nsAString& aKeySystem);
bool IsWidevineKeySystem(const nsAString& aKeySystem);
#ifdef MOZ_WMF_CDM
+bool IsPlayReadyEnabled();
+
bool IsPlayReadyKeySystemAndSupported(const nsAString& aKeySystem);
-bool IsPlayReadyKeySystem(const nsAString& aKeySystem);
+bool IsWidevineHardwareDecryptionEnabled();
bool IsWidevineExperimentKeySystemAndSupported(const nsAString& aKeySystem);
-bool IsWidevineExperimentKeySystem(const nsAString& aKeySystem);
-
bool IsWMFClearKeySystemAndSupported(const nsAString& aKeySystem);
#endif
@@ -107,6 +108,9 @@ bool CheckIfHarewareDRMConfigExists(
bool DoesKeySystemSupportHardwareDecryption(const nsAString& aKeySystem);
+void DeprecationWarningLog(const dom::Document* aDocument,
+ const char* aMsgName);
+
} // namespace mozilla
#endif // EME_LOG_H_
diff --git a/dom/media/eme/KeySystemConfig.cpp b/dom/media/eme/KeySystemConfig.cpp
index 0cb5da1a56..8f3227ecf6 100644
--- a/dom/media/eme/KeySystemConfig.cpp
+++ b/dom/media/eme/KeySystemConfig.cpp
@@ -68,221 +68,262 @@ bool KeySystemConfig::Supports(const nsAString& aKeySystem) {
return false;
}
-/* static */
-bool KeySystemConfig::CreateKeySystemConfigs(
- const nsAString& aKeySystem, const DecryptionInfo aDecryption,
+/* static */ void KeySystemConfig::CreateClearKeyKeySystemConfigs(
+ const KeySystemConfigRequest& aRequest,
nsTArray<KeySystemConfig>& aOutConfigs) {
- if (!Supports(aKeySystem)) {
- return false;
+ KeySystemConfig* config = aOutConfigs.AppendElement();
+ config->mKeySystem = aRequest.mKeySystem;
+ config->mInitDataTypes.AppendElement(u"cenc"_ns);
+ config->mInitDataTypes.AppendElement(u"keyids"_ns);
+ config->mInitDataTypes.AppendElement(u"webm"_ns);
+ config->mPersistentState = Requirement::Optional;
+ config->mDistinctiveIdentifier = Requirement::NotAllowed;
+ config->mSessionTypes.AppendElement(SessionType::Temporary);
+ config->mEncryptionSchemes.AppendElement(u"cenc"_ns);
+ config->mEncryptionSchemes.AppendElement(u"cbcs"_ns);
+ config->mEncryptionSchemes.AppendElement(u"cbcs-1-9"_ns);
+ if (StaticPrefs::media_clearkey_persistent_license_enabled()) {
+ config->mSessionTypes.AppendElement(SessionType::PersistentLicense);
}
-
- if (IsClearkeyKeySystem(aKeySystem)) {
- KeySystemConfig* config = aOutConfigs.AppendElement();
- config->mKeySystem = aKeySystem;
- config->mInitDataTypes.AppendElement(u"cenc"_ns);
- config->mInitDataTypes.AppendElement(u"keyids"_ns);
- config->mInitDataTypes.AppendElement(u"webm"_ns);
- config->mPersistentState = Requirement::Optional;
- config->mDistinctiveIdentifier = Requirement::NotAllowed;
- config->mSessionTypes.AppendElement(SessionType::Temporary);
- config->mEncryptionSchemes.AppendElement(u"cenc"_ns);
- config->mEncryptionSchemes.AppendElement(u"cbcs"_ns);
- config->mEncryptionSchemes.AppendElement(u"cbcs-1-9"_ns);
- if (StaticPrefs::media_clearkey_persistent_license_enabled()) {
- config->mSessionTypes.AppendElement(SessionType::PersistentLicense);
- }
#if defined(XP_WIN)
- // Clearkey CDM uses WMF's H.264 decoder on Windows.
- if (WMFDecoderModule::CanCreateMFTDecoder(WMFStreamType::H264)) {
- config->mMP4.SetCanDecryptAndDecode(EME_CODEC_H264);
- } else {
- config->mMP4.SetCanDecrypt(EME_CODEC_H264);
- }
-#else
+ // Clearkey CDM uses WMF's H.264 decoder on Windows.
+ if (WMFDecoderModule::CanCreateMFTDecoder(WMFStreamType::H264)) {
+ config->mMP4.SetCanDecryptAndDecode(EME_CODEC_H264);
+ } else {
config->mMP4.SetCanDecrypt(EME_CODEC_H264);
+ }
+#else
+ config->mMP4.SetCanDecrypt(EME_CODEC_H264);
#endif
- config->mMP4.SetCanDecrypt(EME_CODEC_AAC);
- config->mMP4.SetCanDecrypt(EME_CODEC_FLAC);
- config->mMP4.SetCanDecrypt(EME_CODEC_OPUS);
- config->mMP4.SetCanDecrypt(EME_CODEC_VP9);
+ config->mMP4.SetCanDecrypt(EME_CODEC_AAC);
+ config->mMP4.SetCanDecrypt(EME_CODEC_FLAC);
+ config->mMP4.SetCanDecrypt(EME_CODEC_OPUS);
+ config->mMP4.SetCanDecrypt(EME_CODEC_VP9);
#ifdef MOZ_AV1
- config->mMP4.SetCanDecrypt(EME_CODEC_AV1);
+ config->mMP4.SetCanDecrypt(EME_CODEC_AV1);
#endif
- config->mWebM.SetCanDecrypt(EME_CODEC_VORBIS);
- config->mWebM.SetCanDecrypt(EME_CODEC_OPUS);
- config->mWebM.SetCanDecrypt(EME_CODEC_VP8);
- config->mWebM.SetCanDecrypt(EME_CODEC_VP9);
+ config->mWebM.SetCanDecrypt(EME_CODEC_VORBIS);
+ config->mWebM.SetCanDecrypt(EME_CODEC_OPUS);
+ config->mWebM.SetCanDecrypt(EME_CODEC_VP8);
+ config->mWebM.SetCanDecrypt(EME_CODEC_VP9);
#ifdef MOZ_AV1
- config->mWebM.SetCanDecrypt(EME_CODEC_AV1);
+ config->mWebM.SetCanDecrypt(EME_CODEC_AV1);
#endif
- if (StaticPrefs::media_clearkey_test_key_systems_enabled()) {
- // Add testing key systems. These offer the same capabilities as the
- // base clearkey system, so just clone clearkey and change the name.
- KeySystemConfig clearkeyWithProtectionQuery{*config};
- clearkeyWithProtectionQuery.mKeySystem.AssignLiteral(
- kClearKeyWithProtectionQueryKeySystemName);
- aOutConfigs.AppendElement(std::move(clearkeyWithProtectionQuery));
- }
- return true;
+ if (StaticPrefs::media_clearkey_test_key_systems_enabled()) {
+ // Add testing key systems. These offer the same capabilities as the
+ // base clearkey system, so just clone clearkey and change the name.
+ KeySystemConfig clearkeyWithProtectionQuery{*config};
+ clearkeyWithProtectionQuery.mKeySystem.AssignLiteral(
+ kClearKeyWithProtectionQueryKeySystemName);
+ aOutConfigs.AppendElement(std::move(clearkeyWithProtectionQuery));
}
+}
- if (IsWidevineKeySystem(aKeySystem)) {
- KeySystemConfig* config = aOutConfigs.AppendElement();
- config->mKeySystem = aKeySystem;
- config->mInitDataTypes.AppendElement(u"cenc"_ns);
- config->mInitDataTypes.AppendElement(u"keyids"_ns);
- config->mInitDataTypes.AppendElement(u"webm"_ns);
- config->mPersistentState = Requirement::Optional;
- config->mDistinctiveIdentifier = Requirement::NotAllowed;
- config->mSessionTypes.AppendElement(SessionType::Temporary);
+/* static */ void KeySystemConfig::CreateWivineL3KeySystemConfigs(
+ const KeySystemConfigRequest& aRequest,
+ nsTArray<KeySystemConfig>& aOutConfigs) {
+ KeySystemConfig* config = aOutConfigs.AppendElement();
+ config->mKeySystem = aRequest.mKeySystem;
+ config->mInitDataTypes.AppendElement(u"cenc"_ns);
+ config->mInitDataTypes.AppendElement(u"keyids"_ns);
+ config->mInitDataTypes.AppendElement(u"webm"_ns);
+ config->mPersistentState = Requirement::Optional;
+ config->mDistinctiveIdentifier = Requirement::NotAllowed;
+ config->mSessionTypes.AppendElement(SessionType::Temporary);
#ifdef MOZ_WIDGET_ANDROID
- config->mSessionTypes.AppendElement(SessionType::PersistentLicense);
+ config->mSessionTypes.AppendElement(SessionType::PersistentLicense);
#endif
- config->mAudioRobustness.AppendElement(u"SW_SECURE_CRYPTO"_ns);
- config->mVideoRobustness.AppendElement(u"SW_SECURE_CRYPTO"_ns);
- config->mVideoRobustness.AppendElement(u"SW_SECURE_DECODE"_ns);
- config->mEncryptionSchemes.AppendElement(u"cenc"_ns);
- config->mEncryptionSchemes.AppendElement(u"cbcs"_ns);
- config->mEncryptionSchemes.AppendElement(u"cbcs-1-9"_ns);
+ config->mAudioRobustness.AppendElement(u"SW_SECURE_CRYPTO"_ns);
+ config->mVideoRobustness.AppendElement(u"SW_SECURE_CRYPTO"_ns);
+ config->mVideoRobustness.AppendElement(u"SW_SECURE_DECODE"_ns);
+ config->mEncryptionSchemes.AppendElement(u"cenc"_ns);
+ config->mEncryptionSchemes.AppendElement(u"cbcs"_ns);
+ config->mEncryptionSchemes.AppendElement(u"cbcs-1-9"_ns);
#if defined(MOZ_WIDGET_ANDROID)
- // MediaDrm.isCryptoSchemeSupported only allows passing
- // "video/mp4" or "video/webm" for mimetype string.
- // See
- // https://developer.android.com/reference/android/media/MediaDrm.html#isCryptoSchemeSupported(java.util.UUID,
- // java.lang.String) for more detail.
- typedef struct {
- const nsCString& mMimeType;
- const nsCString& mEMECodecType;
- const char16_t* mCodecType;
- KeySystemConfig::ContainerSupport* mSupportType;
- } DataForValidation;
+ // MediaDrm.isCryptoSchemeSupported only allows passing
+ // "video/mp4" or "video/webm" for mimetype string.
+ // See
+ // https://developer.android.com/reference/android/media/MediaDrm.html#isCryptoSchemeSupported(java.util.UUID,
+ // java.lang.String) for more detail.
+ typedef struct {
+ const nsCString& mMimeType;
+ const nsCString& mEMECodecType;
+ const char16_t* mCodecType;
+ KeySystemConfig::ContainerSupport* mSupportType;
+ } DataForValidation;
- DataForValidation validationList[] = {
- {nsCString(VIDEO_MP4), EME_CODEC_H264, java::MediaDrmProxy::AVC,
- &config->mMP4},
- {nsCString(VIDEO_MP4), EME_CODEC_VP9, java::MediaDrmProxy::AVC,
- &config->mMP4},
+ DataForValidation validationList[] = {
+ {nsCString(VIDEO_MP4), EME_CODEC_H264, java::MediaDrmProxy::AVC,
+ &config->mMP4},
+ {nsCString(VIDEO_MP4), EME_CODEC_VP9, java::MediaDrmProxy::AVC,
+ &config->mMP4},
# ifdef MOZ_AV1
- {nsCString(VIDEO_MP4), EME_CODEC_AV1, java::MediaDrmProxy::AV1,
- &config->mMP4},
+ {nsCString(VIDEO_MP4), EME_CODEC_AV1, java::MediaDrmProxy::AV1,
+ &config->mMP4},
# endif
- {nsCString(AUDIO_MP4), EME_CODEC_AAC, java::MediaDrmProxy::AAC,
- &config->mMP4},
- {nsCString(AUDIO_MP4), EME_CODEC_FLAC, java::MediaDrmProxy::FLAC,
- &config->mMP4},
- {nsCString(AUDIO_MP4), EME_CODEC_OPUS, java::MediaDrmProxy::OPUS,
- &config->mMP4},
- {nsCString(VIDEO_WEBM), EME_CODEC_VP8, java::MediaDrmProxy::VP8,
- &config->mWebM},
- {nsCString(VIDEO_WEBM), EME_CODEC_VP9, java::MediaDrmProxy::VP9,
- &config->mWebM},
+ {nsCString(AUDIO_MP4), EME_CODEC_AAC, java::MediaDrmProxy::AAC,
+ &config->mMP4},
+ {nsCString(AUDIO_MP4), EME_CODEC_FLAC, java::MediaDrmProxy::FLAC,
+ &config->mMP4},
+ {nsCString(AUDIO_MP4), EME_CODEC_OPUS, java::MediaDrmProxy::OPUS,
+ &config->mMP4},
+ {nsCString(VIDEO_WEBM), EME_CODEC_VP8, java::MediaDrmProxy::VP8,
+ &config->mWebM},
+ {nsCString(VIDEO_WEBM), EME_CODEC_VP9, java::MediaDrmProxy::VP9,
+ &config->mWebM},
# ifdef MOZ_AV1
- {nsCString(VIDEO_WEBM), EME_CODEC_AV1, java::MediaDrmProxy::AV1,
- &config->mWebM},
+ {nsCString(VIDEO_WEBM), EME_CODEC_AV1, java::MediaDrmProxy::AV1,
+ &config->mWebM},
# endif
- {nsCString(AUDIO_WEBM), EME_CODEC_VORBIS, java::MediaDrmProxy::VORBIS,
- &config->mWebM},
- {nsCString(AUDIO_WEBM), EME_CODEC_OPUS, java::MediaDrmProxy::OPUS,
- &config->mWebM},
- };
+ {nsCString(AUDIO_WEBM), EME_CODEC_VORBIS, java::MediaDrmProxy::VORBIS,
+ &config->mWebM},
+ {nsCString(AUDIO_WEBM), EME_CODEC_OPUS, java::MediaDrmProxy::OPUS,
+ &config->mWebM},
+ };
- for (const auto& data : validationList) {
- if (java::MediaDrmProxy::IsCryptoSchemeSupported(kWidevineKeySystemName,
- data.mMimeType)) {
- if (!AndroidDecoderModule::SupportsMimeType(data.mMimeType).isEmpty()) {
- data.mSupportType->SetCanDecryptAndDecode(data.mEMECodecType);
- } else {
- data.mSupportType->SetCanDecrypt(data.mEMECodecType);
- }
+ for (const auto& data : validationList) {
+ if (java::MediaDrmProxy::IsCryptoSchemeSupported(kWidevineKeySystemName,
+ data.mMimeType)) {
+ if (!AndroidDecoderModule::SupportsMimeType(data.mMimeType).isEmpty()) {
+ data.mSupportType->SetCanDecryptAndDecode(data.mEMECodecType);
+ } else {
+ data.mSupportType->SetCanDecrypt(data.mEMECodecType);
}
}
+ }
#else
# if defined(XP_WIN)
- // Widevine CDM doesn't include an AAC decoder. So if WMF can't
- // decode AAC, and a codec wasn't specified, be conservative
- // and reject the MediaKeys request, since we assume Widevine
- // will be used with AAC.
- if (WMFDecoderModule::CanCreateMFTDecoder(WMFStreamType::AAC)) {
- config->mMP4.SetCanDecrypt(EME_CODEC_AAC);
- }
-# else
+ // Widevine CDM doesn't include an AAC decoder. So if WMF can't
+ // decode AAC, and a codec wasn't specified, be conservative
+ // and reject the MediaKeys request, since we assume Widevine
+ // will be used with AAC.
+ if (WMFDecoderModule::CanCreateMFTDecoder(WMFStreamType::AAC)) {
config->mMP4.SetCanDecrypt(EME_CODEC_AAC);
+ }
+# else
+ config->mMP4.SetCanDecrypt(EME_CODEC_AAC);
# endif
- config->mMP4.SetCanDecrypt(EME_CODEC_FLAC);
- config->mMP4.SetCanDecrypt(EME_CODEC_OPUS);
- config->mMP4.SetCanDecryptAndDecode(EME_CODEC_H264);
- config->mMP4.SetCanDecryptAndDecode(EME_CODEC_VP9);
+ config->mMP4.SetCanDecrypt(EME_CODEC_FLAC);
+ config->mMP4.SetCanDecrypt(EME_CODEC_OPUS);
+ config->mMP4.SetCanDecryptAndDecode(EME_CODEC_H264);
+ config->mMP4.SetCanDecryptAndDecode(EME_CODEC_VP9);
# ifdef MOZ_AV1
- config->mMP4.SetCanDecryptAndDecode(EME_CODEC_AV1);
+ config->mMP4.SetCanDecryptAndDecode(EME_CODEC_AV1);
# endif
- config->mWebM.SetCanDecrypt(EME_CODEC_VORBIS);
- config->mWebM.SetCanDecrypt(EME_CODEC_OPUS);
- config->mWebM.SetCanDecryptAndDecode(EME_CODEC_VP8);
- config->mWebM.SetCanDecryptAndDecode(EME_CODEC_VP9);
+ config->mWebM.SetCanDecrypt(EME_CODEC_VORBIS);
+ config->mWebM.SetCanDecrypt(EME_CODEC_OPUS);
+ config->mWebM.SetCanDecryptAndDecode(EME_CODEC_VP8);
+ config->mWebM.SetCanDecryptAndDecode(EME_CODEC_VP9);
# ifdef MOZ_AV1
- config->mWebM.SetCanDecryptAndDecode(EME_CODEC_AV1);
+ config->mWebM.SetCanDecryptAndDecode(EME_CODEC_AV1);
# endif
#endif
- return true;
- }
+}
+
+/* static */
+RefPtr<KeySystemConfig::SupportedConfigsPromise>
+KeySystemConfig::CreateKeySystemConfigs(
+ const nsTArray<KeySystemConfigRequest>& aRequests) {
+ // Create available configs for all supported key systems in the request, but
+ // some of them might not be created immediately.
+
+ nsTArray<KeySystemConfig> outConfigs;
+ nsTArray<KeySystemConfigRequest> asyncRequests;
+
+ for (const auto& request : aRequests) {
+ const nsAString& keySystem = request.mKeySystem;
+ if (!Supports(keySystem)) {
+ continue;
+ }
+
+ if (IsClearkeyKeySystem(keySystem)) {
+ CreateClearKeyKeySystemConfigs(request, outConfigs);
+ } else if (IsWidevineKeySystem(keySystem)) {
+ CreateWivineL3KeySystemConfigs(request, outConfigs);
+ }
#ifdef MOZ_WMF_CDM
- if (IsPlayReadyKeySystemAndSupported(aKeySystem) ||
- IsWidevineExperimentKeySystemAndSupported(aKeySystem)) {
- RefPtr<WMFCDMImpl> cdm = MakeRefPtr<WMFCDMImpl>(aKeySystem);
- return cdm->GetCapabilities(aDecryption == DecryptionInfo::Hardware,
- aOutConfigs);
- }
+ else if (IsPlayReadyKeySystemAndSupported(keySystem) ||
+ IsWidevineExperimentKeySystemAndSupported(keySystem)) {
+ asyncRequests.AppendElement(request);
+ }
#endif
- return false;
-}
+ }
-bool KeySystemConfig::IsSameKeySystem(const nsAString& aKeySystem) const {
#ifdef MOZ_WMF_CDM
- // We want to map Widevine experiment key system to normal Widevine key system
- // as well.
- if (IsWidevineExperimentKeySystemAndSupported(mKeySystem)) {
- return mKeySystem.Equals(aKeySystem) ||
- aKeySystem.EqualsLiteral(kWidevineKeySystemName);
+ if (!asyncRequests.IsEmpty()) {
+ RefPtr<SupportedConfigsPromise::Private> promise =
+ new SupportedConfigsPromise::Private(__func__);
+ RefPtr<WMFCDMCapabilites> cdm = new WMFCDMCapabilites();
+ cdm->GetCapabilities(asyncRequests)
+ ->Then(GetMainThreadSerialEventTarget(), __func__,
+ [syncConfigs = std::move(outConfigs),
+ promise](SupportedConfigsPromise::ResolveOrRejectValue&&
+ aResult) mutable {
+ // Return the capabilities we already know
+ if (aResult.IsReject()) {
+ promise->Resolve(std::move(syncConfigs), __func__);
+ return;
+ }
+ // Merge sync results with async results
+ auto& asyncConfigs = aResult.ResolveValue();
+ asyncConfigs.AppendElements(std::move(syncConfigs));
+ promise->Resolve(std::move(asyncConfigs), __func__);
+ });
+ return promise;
}
#endif
- return mKeySystem.Equals(aKeySystem);
+ return SupportedConfigsPromise::CreateAndResolve(std::move(outConfigs),
+ __func__);
}
/* static */
void KeySystemConfig::GetGMPKeySystemConfigs(dom::Promise* aPromise) {
MOZ_ASSERT(aPromise);
- nsTArray<KeySystemConfig> keySystemConfigs;
+
+ // Generate config requests
const nsTArray<nsString> keySystemNames{
NS_ConvertUTF8toUTF16(kClearKeyKeySystemName),
NS_ConvertUTF8toUTF16(kWidevineKeySystemName),
};
- FallibleTArray<dom::CDMInformation> cdmInfo;
- for (const auto& name : keySystemNames) {
+ nsTArray<KeySystemConfigRequest> requests;
+ for (const auto& keySystem : keySystemNames) {
#ifdef MOZ_WMF_CDM
- if (IsWMFClearKeySystemAndSupported(name)) {
+ if (IsWMFClearKeySystemAndSupported(keySystem)) {
// Using wmf clearkey, not gmp clearkey.
continue;
}
#endif
- if (KeySystemConfig::CreateKeySystemConfigs(
- name, KeySystemConfig::DecryptionInfo::Software,
- keySystemConfigs)) {
- auto* info = cdmInfo.AppendElement(fallible);
- if (!info) {
- aPromise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
- return;
- }
- MOZ_ASSERT(keySystemConfigs.Length() == cdmInfo.Length());
- info->mKeySystemName = name;
- info->mCapabilities = keySystemConfigs.LastElement().GetDebugInfo();
- info->mClearlead = DoesKeySystemSupportClearLead(name);
- // TODO : ask real CDM
- info->mIsHDCP22Compatible = false;
- }
+ requests.AppendElement(
+ KeySystemConfigRequest{keySystem, DecryptionInfo::Software});
}
- aPromise->MaybeResolve(cdmInfo);
+
+ // Get supported configs
+ KeySystemConfig::CreateKeySystemConfigs(requests)->Then(
+ GetMainThreadSerialEventTarget(), __func__,
+ [promise = RefPtr<dom::Promise>{aPromise}](
+ const SupportedConfigsPromise::ResolveOrRejectValue& aResult) {
+ if (aResult.IsResolve()) {
+ // Generate CDMInformation from configs
+ FallibleTArray<dom::CDMInformation> cdmInfo;
+ for (const auto& config : aResult.ResolveValue()) {
+ auto* info = cdmInfo.AppendElement(fallible);
+ if (!info) {
+ promise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
+ return;
+ }
+ info->mKeySystemName = config.mKeySystem;
+ info->mCapabilities = config.GetDebugInfo();
+ info->mClearlead = DoesKeySystemSupportClearLead(config.mKeySystem);
+ // TODO : ask real CDM
+ info->mIsHDCP22Compatible = false;
+ }
+ promise->MaybeResolve(cdmInfo);
+ } else {
+ promise->MaybeReject(NS_ERROR_DOM_MEDIA_CDM_ERR);
+ }
+ });
}
nsString KeySystemConfig::GetDebugInfo() const {
diff --git a/dom/media/eme/KeySystemConfig.h b/dom/media/eme/KeySystemConfig.h
index cc35ba76de..39027d4401 100644
--- a/dom/media/eme/KeySystemConfig.h
+++ b/dom/media/eme/KeySystemConfig.h
@@ -9,12 +9,23 @@
#include "nsString.h"
#include "nsTArray.h"
+#include "mozilla/MozPromise.h"
#include "mozilla/dom/MediaKeysBinding.h"
+#include "mozilla/dom/MediaKeySystemAccessBinding.h"
namespace mozilla {
+struct KeySystemConfigRequest;
+
struct KeySystemConfig {
public:
+ using SupportedConfigsPromise =
+ MozPromise<nsTArray<KeySystemConfig>, bool /* aIgnored */,
+ /* IsExclusive = */ true>;
+ using KeySystemConfigPromise =
+ MozPromise<dom::MediaKeySystemConfiguration, bool /* aIgnored */,
+ /* IsExclusive = */ true>;
+
// EME MediaKeysRequirement:
// https://www.w3.org/TR/encrypted-media/#dom-mediakeysrequirement
enum class Requirement {
@@ -129,9 +140,8 @@ struct KeySystemConfig {
Software,
Hardware,
};
- static bool CreateKeySystemConfigs(const nsAString& aKeySystem,
- const DecryptionInfo aDecryption,
- nsTArray<KeySystemConfig>& aOutConfigs);
+ static RefPtr<SupportedConfigsPromise> CreateKeySystemConfigs(
+ const nsTArray<KeySystemConfigRequest>& aRequests);
static void GetGMPKeySystemConfigs(dom::Promise* aPromise);
KeySystemConfig() = default;
@@ -169,10 +179,6 @@ struct KeySystemConfig {
nsString GetDebugInfo() const;
- // Return true if the given key system is equal to `mKeySystem`, or it can be
- // mapped to the same key system
- bool IsSameKeySystem(const nsAString& aKeySystem) const;
-
nsString mKeySystem;
nsTArray<nsString> mInitDataTypes;
Requirement mPersistentState = Requirement::NotAllowed;
@@ -184,6 +190,22 @@ struct KeySystemConfig {
ContainerSupport mMP4;
ContainerSupport mWebM;
bool mIsHDCP22Compatible = false;
+
+ private:
+ static void CreateClearKeyKeySystemConfigs(
+ const KeySystemConfigRequest& aRequest,
+ nsTArray<KeySystemConfig>& aOutConfigs);
+ static void CreateWivineL3KeySystemConfigs(
+ const KeySystemConfigRequest& aRequest,
+ nsTArray<KeySystemConfig>& aOutConfigs);
+};
+
+struct KeySystemConfigRequest final {
+ KeySystemConfigRequest(const nsAString& aKeySystem,
+ KeySystemConfig::DecryptionInfo aDecryption)
+ : mKeySystem(aKeySystem), mDecryption(aDecryption) {}
+ const nsString mKeySystem;
+ const KeySystemConfig::DecryptionInfo mDecryption;
};
KeySystemConfig::SessionType ConvertToKeySystemConfigSessionType(
diff --git a/dom/media/eme/MediaKeySession.cpp b/dom/media/eme/MediaKeySession.cpp
index 8a3a01dd5c..66ee77a2f5 100644
--- a/dom/media/eme/MediaKeySession.cpp
+++ b/dom/media/eme/MediaKeySession.cpp
@@ -250,17 +250,33 @@ already_AddRefed<Promise> MediaKeySession::GenerateRequest(
// cdm implementation value does not support initDataType as an
// Initialization Data Type, return a promise rejected with a
// NotSupportedError. String comparison is case-sensitive.
- if (!MediaKeySystemAccess::KeySystemSupportsInitDataType(
- mKeySystem, aInitDataType, mHardwareDecryption)) {
- promise->MaybeRejectWithNotSupportedError(
- "Unsupported initDataType passed to MediaKeySession.generateRequest()");
- EME_LOG(
- "MediaKeySession[%p,'%s'] GenerateRequest() failed, unsupported "
- "initDataType",
- this, NS_ConvertUTF16toUTF8(mSessionId).get());
- return promise.forget();
- }
+ MediaKeySystemAccess::KeySystemSupportsInitDataType(mKeySystem, aInitDataType,
+ mHardwareDecryption)
+ ->Then(GetMainThreadSerialEventTarget(), __func__,
+ [self = RefPtr<MediaKeySession>{this}, this,
+ initDataType = nsString{aInitDataType},
+ initData = std::move(data), promise](
+ const GenericPromise::ResolveOrRejectValue& aResult) mutable {
+ if (aResult.IsReject()) {
+ promise->MaybeRejectWithNotSupportedError(
+ "Unsupported initDataType passed to "
+ "MediaKeySession.generateRequest()");
+ EME_LOG(
+ "MediaKeySession[%p,'%s'] GenerateRequest() failed, "
+ "unsupported "
+ "initDataType",
+ this, NS_ConvertUTF16toUTF8(mSessionId).get());
+ return;
+ }
+ // Run rest of steps in the spec, starting from 6.6.2.7
+ CompleteGenerateRequest(initDataType, initData, promise);
+ });
+ return promise.forget();
+}
+void MediaKeySession::CompleteGenerateRequest(const nsString& aInitDataType,
+ nsTArray<uint8_t>& aData,
+ DetailedPromise* aPromise) {
// Let init data be a copy of the contents of the initData parameter.
// Note: Handled by the CopyArrayBufferViewOrArrayBufferData call above.
@@ -270,42 +286,41 @@ already_AddRefed<Promise> MediaKeySession::GenerateRequest(
// Run the following steps in parallel:
- // If the init data is not valid for initDataType, reject promise with
- // a newly created TypeError.
- if (!ValidateInitData(data, aInitDataType)) {
+ // If the init data is not valid for initDataType, reject promise with a newly
+ // created TypeError.
+ if (!ValidateInitData(aData, aInitDataType)) {
// If the preceding step failed, reject promise with a newly created
// TypeError.
- promise->MaybeRejectWithTypeError(
- "initData sanitization failed in MediaKeySession.generateRequest()");
+ aPromise->MaybeRejectWithTypeError(
+ "initData sanitization failed in "
+ "MediaKeySession.generateRequest()");
EME_LOG(
- "MediaKeySession[%p,'%s'] GenerateRequest() initData sanitization "
+ "MediaKeySession[%p,'%s'] GenerateRequest() initData "
+ "sanitization "
"failed",
this, NS_ConvertUTF16toUTF8(mSessionId).get());
- return promise.forget();
+ return;
}
// Let sanitized init data be a validated and sanitized version of init data.
// If sanitized init data is empty, reject promise with a NotSupportedError.
- // Note: Remaining steps of generateRequest method continue in CDM.
+ // Note: Remaining steps of generateRequest method continue in CDM.
// Convert initData to hex for easier logging.
- // Note: CreateSession() std::move()s the data out of the array, so we have
- // to copy it here.
- nsAutoCString hexInitData(ToHexString(data));
- PromiseId pid = mKeys->StorePromise(promise);
+ // Note: CreateSession() std::move()s the data out of the array, so we have to
+ // copy it here.
+ nsAutoCString hexInitData(ToHexString(aData));
+ PromiseId pid = mKeys->StorePromise(aPromise);
mKeys->ConnectPendingPromiseIdWithToken(pid, Token());
mKeys->GetCDMProxy()->CreateSession(Token(), mSessionType, pid, aInitDataType,
- data);
-
+ aData);
EME_LOG(
"MediaKeySession[%p,'%s'] GenerateRequest() sent, "
"promiseId=%d initData='%s' initDataType='%s'",
this, NS_ConvertUTF16toUTF8(mSessionId).get(), pid, hexInitData.get(),
NS_ConvertUTF16toUTF8(aInitDataType).get());
-
- return promise.forget();
}
already_AddRefed<Promise> MediaKeySession::Load(const nsAString& aSessionId,
diff --git a/dom/media/eme/MediaKeySession.h b/dom/media/eme/MediaKeySession.h
index 7204f99eef..b0edb16cf1 100644
--- a/dom/media/eme/MediaKeySession.h
+++ b/dom/media/eme/MediaKeySession.h
@@ -120,6 +120,12 @@ class MediaKeySession final : public DOMEventTargetHelper,
already_AddRefed<DetailedPromise> MakePromise(ErrorResult& aRv,
const nsACString& aName);
+ // EME spec, starting from 6.6.2.7
+ // https://w3c.github.io/encrypted-media/
+ void CompleteGenerateRequest(const nsString& aInitDataType,
+ nsTArray<uint8_t>& aData,
+ DetailedPromise* aPromise);
+
RefPtr<DetailedPromise> mClosed;
RefPtr<MediaKeyError> mMediaKeyError;
diff --git a/dom/media/eme/MediaKeySystemAccess.cpp b/dom/media/eme/MediaKeySystemAccess.cpp
index d498c2a773..af9038d309 100644
--- a/dom/media/eme/MediaKeySystemAccess.cpp
+++ b/dom/media/eme/MediaKeySystemAccess.cpp
@@ -10,7 +10,6 @@
#include "DecoderDoctorDiagnostics.h"
#include "DecoderTraits.h"
-#include "KeySystemConfig.h"
#include "MP4Decoder.h"
#include "MediaContainerType.h"
#include "WebMDecoder.h"
@@ -19,6 +18,7 @@
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPrefs_media.h"
+#include "mozilla/dom/Document.h"
#include "mozilla/dom/KeySystemNames.h"
#include "mozilla/dom/MediaKeySession.h"
#include "mozilla/dom/MediaKeySystemAccessBinding.h"
@@ -231,75 +231,83 @@ static KeySystemConfig::EMECodecString ToEMEAPICodecString(
return ""_ns;
}
-static nsTArray<KeySystemConfig> GetSupportedKeySystems(
- const nsAString& aKeySystem, bool aIsHardwareDecryption) {
+static RefPtr<KeySystemConfig::SupportedConfigsPromise>
+GetSupportedKeySystemConfigs(const nsAString& aKeySystem,
+ bool aIsHardwareDecryption) {
using DecryptionInfo = KeySystemConfig::DecryptionInfo;
- nsTArray<KeySystemConfig> keySystemConfigs;
+ nsTArray<KeySystemConfigRequest> requests;
+
+ // Software Widevine and Clearkey
if (IsWidevineKeySystem(aKeySystem) || IsClearkeyKeySystem(aKeySystem)) {
- Unused << KeySystemConfig::CreateKeySystemConfigs(
- aKeySystem, DecryptionInfo::Software, keySystemConfigs);
+ requests.AppendElement(
+ KeySystemConfigRequest{aKeySystem, DecryptionInfo::Software});
}
#ifdef MOZ_WMF_CDM
- if (IsPlayReadyKeySystem(aKeySystem)) {
- Unused << KeySystemConfig::CreateKeySystemConfigs(
- NS_ConvertUTF8toUTF16(kPlayReadyKeySystemName),
- DecryptionInfo::Software, keySystemConfigs);
- if (aIsHardwareDecryption) {
- Unused << KeySystemConfig::CreateKeySystemConfigs(
- NS_ConvertUTF8toUTF16(kPlayReadyKeySystemName),
- DecryptionInfo::Hardware, keySystemConfigs);
- Unused << KeySystemConfig::CreateKeySystemConfigs(
- NS_ConvertUTF8toUTF16(kPlayReadyKeySystemHardware),
- DecryptionInfo::Hardware, keySystemConfigs);
- Unused << KeySystemConfig::CreateKeySystemConfigs(
+ if (IsPlayReadyEnabled()) {
+ // PlayReady software and hardware
+ if (aKeySystem.EqualsLiteral(kPlayReadyKeySystemName) ||
+ aKeySystem.EqualsLiteral(kPlayReadyKeySystemHardware)) {
+ requests.AppendElement(
+ KeySystemConfigRequest{NS_ConvertUTF8toUTF16(kPlayReadyKeySystemName),
+ DecryptionInfo::Software});
+ if (aIsHardwareDecryption) {
+ requests.AppendElement(KeySystemConfigRequest{
+ NS_ConvertUTF8toUTF16(kPlayReadyKeySystemName),
+ DecryptionInfo::Hardware});
+ requests.AppendElement(KeySystemConfigRequest{
+ NS_ConvertUTF8toUTF16(kPlayReadyKeySystemHardware),
+ DecryptionInfo::Hardware});
+ }
+ }
+ // PlayReady clearlead
+ if (aKeySystem.EqualsLiteral(kPlayReadyHardwareClearLeadKeySystemName)) {
+ requests.AppendElement(KeySystemConfigRequest{
NS_ConvertUTF8toUTF16(kPlayReadyHardwareClearLeadKeySystemName),
- DecryptionInfo::Hardware, keySystemConfigs);
+ DecryptionInfo::Hardware});
}
}
- // If key system is kWidevineKeySystemName but with hardware decryption
- // requirement, then we need to check those experiement key systems which are
- // used for hardware decryption.
- if (IsWidevineExperimentKeySystem(aKeySystem) ||
- (IsWidevineKeySystem(aKeySystem) && aIsHardwareDecryption)) {
- Unused << KeySystemConfig::CreateKeySystemConfigs(
- NS_ConvertUTF8toUTF16(kWidevineExperimentKeySystemName),
- DecryptionInfo::Hardware, keySystemConfigs);
- Unused << KeySystemConfig::CreateKeySystemConfigs(
- NS_ConvertUTF8toUTF16(kWidevineExperiment2KeySystemName),
- DecryptionInfo::Hardware, keySystemConfigs);
- }
-#endif
- return keySystemConfigs;
-}
-static bool GetKeySystemConfigs(
- const nsAString& aKeySystem, bool aIsHardwareDecryption,
- nsTArray<KeySystemConfig>& aOutKeySystemConfig) {
- bool foundConfigs = false;
- for (auto& config :
- GetSupportedKeySystems(aKeySystem, aIsHardwareDecryption)) {
- if (config.IsSameKeySystem(aKeySystem)) {
- aOutKeySystemConfig.AppendElement(std::move(config));
- foundConfigs = true;
+ if (IsWidevineHardwareDecryptionEnabled()) {
+ // Widevine hardware
+ if (aKeySystem.EqualsLiteral(kWidevineExperimentKeySystemName) ||
+ (IsWidevineKeySystem(aKeySystem) && aIsHardwareDecryption)) {
+ requests.AppendElement(KeySystemConfigRequest{
+ NS_ConvertUTF8toUTF16(kWidevineExperimentKeySystemName),
+ DecryptionInfo::Hardware});
+ }
+ // Widevine clearlead
+ if (aKeySystem.EqualsLiteral(kWidevineExperiment2KeySystemName)) {
+ requests.AppendElement(KeySystemConfigRequest{
+ NS_ConvertUTF8toUTF16(kWidevineExperiment2KeySystemName),
+ DecryptionInfo::Hardware});
}
}
- return foundConfigs;
+#endif
+ return KeySystemConfig::CreateKeySystemConfigs(requests);
}
/* static */
-bool MediaKeySystemAccess::KeySystemSupportsInitDataType(
+RefPtr<GenericPromise> MediaKeySystemAccess::KeySystemSupportsInitDataType(
const nsAString& aKeySystem, const nsAString& aInitDataType,
bool aIsHardwareDecryption) {
- nsTArray<KeySystemConfig> implementations;
- GetKeySystemConfigs(aKeySystem, aIsHardwareDecryption, implementations);
- bool containInitType = false;
- for (const auto& config : implementations) {
- if (config.mInitDataTypes.Contains(aInitDataType)) {
- containInitType = true;
- break;
- }
- }
- return containInitType;
+ RefPtr<GenericPromise::Private> promise =
+ new GenericPromise::Private(__func__);
+ GetSupportedKeySystemConfigs(aKeySystem, aIsHardwareDecryption)
+ ->Then(GetMainThreadSerialEventTarget(), __func__,
+ [promise, initDataType = nsString{std::move(aInitDataType)}](
+ const KeySystemConfig::SupportedConfigsPromise::
+ ResolveOrRejectValue& aResult) {
+ if (aResult.IsResolve()) {
+ for (const auto& config : aResult.ResolveValue()) {
+ if (config.mInitDataTypes.Contains(initDataType)) {
+ promise->Resolve(true, __func__);
+ return;
+ }
+ }
+ }
+ promise->Reject(NS_ERROR_DOM_MEDIA_CDM_ERR, __func__);
+ });
+ return promise.forget();
}
enum CodecType { Audio, Video, Invalid };
@@ -474,7 +482,7 @@ static Sequence<MediaKeySystemMediaCapability> GetSupportedCapabilities(
const nsTArray<MediaKeySystemMediaCapability>& aRequestedCapabilities,
const MediaKeySystemConfiguration& aPartialConfig,
const KeySystemConfig& aKeySystem, DecoderDoctorDiagnostics* aDiagnostics,
- const std::function<void(const char*)>& aDeprecationLogFn) {
+ const Document* aDocument) {
// Let local accumulated configuration be a local copy of partial
// configuration. (Note: It's not necessary for us to maintain a local copy,
// as we don't need to test whether capabilites from previous calls to this
@@ -609,7 +617,7 @@ static Sequence<MediaKeySystemMediaCapability> GetSupportedCapabilities(
// If media types is empty:
if (codecs.IsEmpty()) {
// Log deprecation warning to encourage authors to not do this!
- aDeprecationLogFn("MediaEMENoCodecsDeprecatedWarning");
+ DeprecationWarningLog(aDocument, "MediaEMENoCodecsDeprecatedWarning");
// TODO: Remove this once we're sure it doesn't break the web.
// If container normatively implies a specific set of codecs and codec
// constraints: Let parameters be that set.
@@ -808,12 +816,12 @@ static Sequence<nsString> UnboxSessionTypes(
}
// 3.1.1.2 Get Supported Configuration and Consent
-static bool GetSupportedConfig(
- const KeySystemConfig& aKeySystem,
- const MediaKeySystemConfiguration& aCandidate,
- MediaKeySystemConfiguration& aOutConfig,
- DecoderDoctorDiagnostics* aDiagnostics, bool aInPrivateBrowsing,
- const std::function<void(const char*)>& aDeprecationLogFn) {
+static bool GetSupportedConfig(const KeySystemConfig& aKeySystem,
+ const MediaKeySystemConfiguration& aCandidate,
+ MediaKeySystemConfiguration& aOutConfig,
+ DecoderDoctorDiagnostics* aDiagnostics,
+ bool aInPrivateBrowsing,
+ const Document* aDocument) {
EME_LOG("Compare implementation '%s'\n with request '%s'",
NS_ConvertUTF16toUTF8(aKeySystem.GetDebugInfo()).get(),
ToCString(aCandidate).get());
@@ -941,7 +949,7 @@ static bool GetSupportedConfig(
// TODO: Most sites using EME still don't pass capabilities, so we
// can't reject on it yet without breaking them. So add this later.
// Log deprecation warning to encourage authors to not do this!
- aDeprecationLogFn("MediaEMENoCapabilitiesDeprecatedWarning");
+ DeprecationWarningLog(aDocument, "MediaEMENoCapabilitiesDeprecatedWarning");
}
// If the videoCapabilities member in candidate configuration is non-empty:
@@ -952,7 +960,7 @@ static bool GetSupportedConfig(
// and restrictions.
Sequence<MediaKeySystemMediaCapability> caps =
GetSupportedCapabilities(Video, aCandidate.mVideoCapabilities, config,
- aKeySystem, aDiagnostics, aDeprecationLogFn);
+ aKeySystem, aDiagnostics, aDocument);
// If video capabilities is null, return NotSupported.
if (caps.IsEmpty()) {
EME_LOG(
@@ -978,7 +986,7 @@ static bool GetSupportedConfig(
// restrictions.
Sequence<MediaKeySystemMediaCapability> caps =
GetSupportedCapabilities(Audio, aCandidate.mAudioCapabilities, config,
- aKeySystem, aDiagnostics, aDeprecationLogFn);
+ aKeySystem, aDiagnostics, aDocument);
// If audio capabilities is null, return NotSupported.
if (caps.IsEmpty()) {
EME_LOG(
@@ -1058,30 +1066,42 @@ static bool GetSupportedConfig(
}
/* static */
-bool MediaKeySystemAccess::GetSupportedConfig(
- const nsAString& aKeySystem,
- const Sequence<MediaKeySystemConfiguration>& aConfigs,
- MediaKeySystemConfiguration& aOutConfig,
- DecoderDoctorDiagnostics* aDiagnostics, bool aIsPrivateBrowsing,
- const std::function<void(const char*)>& aDeprecationLogFn) {
+RefPtr<KeySystemConfig::KeySystemConfigPromise>
+MediaKeySystemAccess::GetSupportedConfig(MediaKeySystemAccessRequest* aRequest,
+ bool aIsPrivateBrowsing,
+ const Document* aDocument) {
nsTArray<KeySystemConfig> implementations;
const bool isHardwareDecryptionRequest =
- CheckIfHarewareDRMConfigExists(aConfigs) ||
- DoesKeySystemSupportHardwareDecryption(aKeySystem);
- if (!GetKeySystemConfigs(aKeySystem, isHardwareDecryptionRequest,
- implementations)) {
- return false;
- }
- for (const auto& implementation : implementations) {
- for (const MediaKeySystemConfiguration& candidate : aConfigs) {
- if (mozilla::dom::GetSupportedConfig(
- implementation, candidate, aOutConfig, aDiagnostics,
- aIsPrivateBrowsing, aDeprecationLogFn)) {
- return true;
- }
- }
- }
- return false;
+ CheckIfHarewareDRMConfigExists(aRequest->mConfigs) ||
+ DoesKeySystemSupportHardwareDecryption(aRequest->mKeySystem);
+
+ RefPtr<KeySystemConfig::KeySystemConfigPromise::Private> promise =
+ new KeySystemConfig::KeySystemConfigPromise::Private(__func__);
+ GetSupportedKeySystemConfigs(aRequest->mKeySystem,
+ isHardwareDecryptionRequest)
+ ->Then(GetMainThreadSerialEventTarget(), __func__,
+ [promise, aRequest, aIsPrivateBrowsing,
+ document = RefPtr<const Document>{aDocument}](
+ const KeySystemConfig::SupportedConfigsPromise::
+ ResolveOrRejectValue& aResult) {
+ if (aResult.IsResolve()) {
+ MediaKeySystemConfiguration outConfig;
+ for (const auto& implementation : aResult.ResolveValue()) {
+ for (const MediaKeySystemConfiguration& candidate :
+ aRequest->mConfigs) {
+ if (mozilla::dom::GetSupportedConfig(
+ implementation, candidate, outConfig,
+ &aRequest->mDiagnostics, aIsPrivateBrowsing,
+ document)) {
+ promise->Resolve(std::move(outConfig), __func__);
+ return;
+ }
+ }
+ }
+ }
+ promise->Reject(false, __func__);
+ });
+ return promise.forget();
}
/* static */
diff --git a/dom/media/eme/MediaKeySystemAccess.h b/dom/media/eme/MediaKeySystemAccess.h
index 18eec47008..5f3309766d 100644
--- a/dom/media/eme/MediaKeySystemAccess.h
+++ b/dom/media/eme/MediaKeySystemAccess.h
@@ -14,6 +14,7 @@
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/MediaKeySystemAccessBinding.h"
#include "mozilla/dom/MediaKeysRequestStatusBinding.h"
+#include "mozilla/KeySystemConfig.h"
#include "js/TypeDecls.h"
@@ -59,16 +60,13 @@ class MediaKeySystemAccess final : public nsISupports, public nsWrapperCache {
const nsAString& aKeySystem,
MediaKeySystemStatus aStatus);
- static bool GetSupportedConfig(
- const nsAString& aKeySystem,
- const Sequence<MediaKeySystemConfiguration>& aConfigs,
- MediaKeySystemConfiguration& aOutConfig,
- DecoderDoctorDiagnostics* aDiagnostics, bool aIsPrivateBrowsing,
- const std::function<void(const char*)>& aDeprecationLogFn);
+ static RefPtr<KeySystemConfig::KeySystemConfigPromise> GetSupportedConfig(
+ MediaKeySystemAccessRequest* aRequest, bool aIsPrivateBrowsing,
+ const Document* aDocument);
- static bool KeySystemSupportsInitDataType(const nsAString& aKeySystem,
- const nsAString& aInitDataType,
- bool aIsHardwareDecryption);
+ static RefPtr<GenericPromise> KeySystemSupportsInitDataType(
+ const nsAString& aKeySystem, const nsAString& aInitDataType,
+ bool aIsHardwareDecryption);
static nsCString ToCString(
const Sequence<MediaKeySystemConfiguration>& aConfig);
diff --git a/dom/media/eme/MediaKeySystemAccessManager.cpp b/dom/media/eme/MediaKeySystemAccessManager.cpp
index 8ebe7ceee7..84389d1db0 100644
--- a/dom/media/eme/MediaKeySystemAccessManager.cpp
+++ b/dom/media/eme/MediaKeySystemAccessManager.cpp
@@ -368,8 +368,6 @@ void MediaKeySystemAccessManager::RequestMediaKeySystemAccess(
// 5. Let promise be a new promise.
// 6. Run the following steps in parallel:
- DecoderDoctorDiagnostics diagnostics;
-
// 1. If keySystem is not one of the Key Systems supported by the user
// agent, reject promise with a NotSupportedError. String comparison is
// case-sensitive.
@@ -383,7 +381,7 @@ void MediaKeySystemAccessManager::RequestMediaKeySystemAccess(
// supported.
aRequest->RejectPromiseWithNotSupportedError(
"Key system is unsupported"_ns);
- diagnostics.StoreMediaKeySystemAccess(
+ aRequest->mDiagnostics.StoreMediaKeySystemAccess(
mWindow->GetExtantDoc(), aRequest->mKeySystem, false, __func__);
return;
}
@@ -399,7 +397,7 @@ void MediaKeySystemAccessManager::RequestMediaKeySystemAccess(
MediaKeySystemStatus::Api_disabled);
}
aRequest->RejectPromiseWithNotSupportedError("EME has been preffed off"_ns);
- diagnostics.StoreMediaKeySystemAccess(
+ aRequest->mDiagnostics.StoreMediaKeySystemAccess(
mWindow->GetExtantDoc(), aRequest->mKeySystem, false, __func__);
return;
}
@@ -439,7 +437,7 @@ void MediaKeySystemAccessManager::RequestMediaKeySystemAccess(
// "I can't play, updating" notification.
aRequest->RejectPromiseWithNotSupportedError(
"Timed out while waiting for a CDM update"_ns);
- diagnostics.StoreMediaKeySystemAccess(
+ aRequest->mDiagnostics.StoreMediaKeySystemAccess(
mWindow->GetExtantDoc(), aRequest->mKeySystem, false, __func__);
return;
}
@@ -453,6 +451,7 @@ void MediaKeySystemAccessManager::RequestMediaKeySystemAccess(
keySystem = NS_ConvertUTF8toUTF16(kWidevineExperimentKeySystemName);
}
#endif
+ auto& diagnostics = aRequest->mDiagnostics;
if (AwaitInstall(std::move(aRequest))) {
// Notify chrome that we're going to wait for the CDM to download/update.
EME_LOG("Await %s for installation",
@@ -480,25 +479,6 @@ void MediaKeySystemAccessManager::RequestMediaKeySystemAccess(
return;
}
- nsCOMPtr<Document> doc = mWindow->GetExtantDoc();
- nsTHashMap<nsCharPtrHashKey, bool> warnings;
- std::function<void(const char*)> deprecationWarningLogFn =
- [&](const char* aMsgName) {
- EME_LOG(
- "MediaKeySystemAccessManager::DeprecationWarningLambda Logging "
- "deprecation warning '%s' to WebConsole.",
- aMsgName);
- warnings.InsertOrUpdate(aMsgName, true);
- AutoTArray<nsString, 1> params;
- nsString& uri = *params.AppendElement();
- if (doc) {
- Unused << doc->GetDocumentURI(uri);
- }
- nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "Media"_ns,
- doc, nsContentUtils::eDOM_PROPERTIES,
- aMsgName, params);
- };
-
bool isPrivateBrowsing =
mWindow->GetExtantDoc() &&
mWindow->GetExtantDoc()->NodePrincipal()->GetPrivateBrowsingId() > 0;
@@ -517,23 +497,28 @@ void MediaKeySystemAccessManager::RequestMediaKeySystemAccess(
// 3. Let the cdm implementation value be implementation.
// 2. Resolve promise with access and abort the parallel steps of this
// algorithm.
- MediaKeySystemConfiguration config;
- if (MediaKeySystemAccess::GetSupportedConfig(
- aRequest->mKeySystem, aRequest->mConfigs, config, &diagnostics,
- isPrivateBrowsing, deprecationWarningLogFn)) {
- aRequest->mSupportedConfig = Some(config);
- // The app gets the final say on if we provide access or not.
- CheckDoesAppAllowProtectedMedia(std::move(aRequest));
- return;
- }
- // 4. Reject promise with a NotSupportedError.
-
- // Not to inform user, because nothing to do if the corresponding keySystem
- // configuration is not supported.
- aRequest->RejectPromiseWithNotSupportedError(
- "Key system configuration is not supported"_ns);
- diagnostics.StoreMediaKeySystemAccess(mWindow->GetExtantDoc(),
- aRequest->mKeySystem, false, __func__);
+ MediaKeySystemAccess::GetSupportedConfig(aRequest.get(), isPrivateBrowsing,
+ mWindow->GetExtantDoc())
+ ->Then(GetMainThreadSerialEventTarget(), __func__,
+ [self = RefPtr<MediaKeySystemAccessManager>{this}, this,
+ request = UniquePtr<PendingRequest>{std::move(aRequest)}](
+ const KeySystemConfig::KeySystemConfigPromise::
+ ResolveOrRejectValue& aResult) mutable {
+ if (aResult.IsResolve()) {
+ request->mSupportedConfig = Some(aResult.ResolveValue());
+ // The app gets the final say on if we provide access or not.
+ CheckDoesAppAllowProtectedMedia(std::move(request));
+ } else {
+ // 4. Reject promise with a NotSupportedError.
+ // Not to inform user, because nothing to do if the
+ // corresponding keySystem configuration is not supported.
+ request->RejectPromiseWithNotSupportedError(
+ "Key system configuration is not supported"_ns);
+ request->mDiagnostics.StoreMediaKeySystemAccess(
+ mWindow->GetExtantDoc(), request->mKeySystem, false,
+ __func__);
+ }
+ });
}
void MediaKeySystemAccessManager::ProvideAccess(
diff --git a/dom/media/eme/MediaKeySystemAccessManager.h b/dom/media/eme/MediaKeySystemAccessManager.h
index 77feded701..9ea621df84 100644
--- a/dom/media/eme/MediaKeySystemAccessManager.h
+++ b/dom/media/eme/MediaKeySystemAccessManager.h
@@ -5,6 +5,7 @@
#ifndef DOM_MEDIA_MEDIAKEYSYSTEMACCESSMANAGER_H_
#define DOM_MEDIA_MEDIAKEYSYSTEMACCESSMANAGER_H_
+#include "DecoderDoctorDiagnostics.h"
#include "mozilla/dom/MediaKeySystemAccess.h"
#include "mozilla/MozPromise.h"
#include "nsCycleCollectionParticipant.h"
@@ -95,6 +96,7 @@ struct MediaKeySystemAccessRequest {
const nsString mKeySystem;
// The config(s) passed for this request.
const Sequence<MediaKeySystemConfiguration> mConfigs;
+ DecoderDoctorDiagnostics mDiagnostics;
};
class MediaKeySystemAccessManager final : public nsIObserver, public nsINamed {
diff --git a/dom/media/eme/mediafoundation/WMFCDMImpl.cpp b/dom/media/eme/mediafoundation/WMFCDMImpl.cpp
index add978f755..983a7c00f2 100644
--- a/dom/media/eme/mediafoundation/WMFCDMImpl.cpp
+++ b/dom/media/eme/mediafoundation/WMFCDMImpl.cpp
@@ -10,81 +10,12 @@
#include "mozilla/AppShutdown.h"
#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/ScopeExit.h"
#include "mozilla/dom/MediaKeySession.h"
#include "mozilla/dom/KeySystemNames.h"
namespace mozilla {
-bool WMFCDMImpl::GetCapabilities(bool aIsHardwareDecryption,
- nsTArray<KeySystemConfig>& aOutConfigs) {
- MOZ_ASSERT(NS_IsMainThread());
- if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
- return false;
- }
-
- static std::unordered_map<std::string, nsTArray<KeySystemConfig>>
- sKeySystemConfigs{};
- static bool sSetRunOnShutdown = false;
- if (!sSetRunOnShutdown) {
- GetMainThreadSerialEventTarget()->Dispatch(
- NS_NewRunnableFunction("WMFCDMImpl::GetCapabilities", [&] {
- RunOnShutdown([&] { sKeySystemConfigs.clear(); },
- ShutdownPhase::XPCOMShutdown);
- }));
- sSetRunOnShutdown = true;
- }
-
- // Retrieve result from our cached key system
- auto keySystem = std::string{NS_ConvertUTF16toUTF8(mKeySystem).get()};
- if (auto rv = sKeySystemConfigs.find(keySystem);
- rv != sKeySystemConfigs.end()) {
- for (const auto& config : rv->second) {
- if (IsHardwareDecryptionSupported(config) == aIsHardwareDecryption) {
- EME_LOG("Return cached capabilities for %s (%s)", keySystem.c_str(),
- NS_ConvertUTF16toUTF8(config.GetDebugInfo()).get());
- aOutConfigs.AppendElement(config);
- return true;
- }
- }
- }
-
- // Not cached result, ask the remote process.
- nsCOMPtr<nsISerialEventTarget> backgroundTaskQueue;
- NS_CreateBackgroundTaskQueue(__func__, getter_AddRefs(backgroundTaskQueue));
- if (!mCDM) {
- mCDM = MakeRefPtr<MFCDMChild>(mKeySystem);
- }
- bool ok = false;
- media::Await(
- do_AddRef(backgroundTaskQueue),
- mCDM->GetCapabilities(aIsHardwareDecryption),
- [&ok, &aOutConfigs, keySystem,
- aIsHardwareDecryption](const MFCDMCapabilitiesIPDL& capabilities) {
- EME_LOG("capabilities: keySystem=%s (hw-secure=%d)", keySystem.c_str(),
- aIsHardwareDecryption);
- for (const auto& v : capabilities.videoCapabilities()) {
- EME_LOG("capabilities: video=%s",
- NS_ConvertUTF16toUTF8(v.contentType()).get());
- }
- for (const auto& a : capabilities.audioCapabilities()) {
- EME_LOG("capabilities: audio=%s",
- NS_ConvertUTF16toUTF8(a.contentType()).get());
- }
- for (const auto& v : capabilities.encryptionSchemes()) {
- EME_LOG("capabilities: encryptionScheme=%s", EncryptionSchemeStr(v));
- }
- KeySystemConfig* config = aOutConfigs.AppendElement();
- MFCDMCapabilitiesIPDLToKeySystemConfig(capabilities, *config);
- sKeySystemConfigs[keySystem].AppendElement(*config);
- ok = true;
- },
- [](nsresult rv) {
- EME_LOG("Fail to get key system capabilities. rv=%x", uint32_t(rv));
- });
-
- return ok;
-}
-
RefPtr<WMFCDMImpl::InitPromise> WMFCDMImpl::Init(
const WMFCDMImpl::InitParams& aParams) {
if (!mCDM) {
@@ -111,4 +42,88 @@ RefPtr<WMFCDMImpl::InitPromise> WMFCDMImpl::Init(
return mInitPromiseHolder.Ensure(__func__);
}
+RefPtr<KeySystemConfig::SupportedConfigsPromise>
+WMFCDMCapabilites::GetCapabilities(
+ const nsTArray<KeySystemConfigRequest>& aRequests) {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
+ return SupportedConfigsPromise::CreateAndReject(false, __func__);
+ }
+
+ if (!mCapabilitiesPromiseHolder.IsEmpty()) {
+ return mCapabilitiesPromiseHolder.Ensure(__func__);
+ }
+
+ using CapabilitiesPromise = MFCDMChild::CapabilitiesPromise;
+ nsTArray<RefPtr<CapabilitiesPromise>> promises;
+ for (const auto& request : aRequests) {
+ RefPtr<MFCDMChild> cdm = new MFCDMChild(request.mKeySystem);
+ promises.AppendElement(cdm->GetCapabilities(MFCDMCapabilitiesRequest{
+ nsString{request.mKeySystem},
+ request.mDecryption == KeySystemConfig::DecryptionInfo::Hardware}));
+ mCDMs.AppendElement(std::move(cdm));
+ }
+
+ CapabilitiesPromise::AllSettled(GetCurrentSerialEventTarget(), promises)
+ ->Then(
+ GetMainThreadSerialEventTarget(), __func__,
+ [self = RefPtr<WMFCDMCapabilites>(this), this](
+ CapabilitiesPromise::AllSettledPromiseType::ResolveOrRejectValue&&
+ aResult) {
+ mCapabilitiesPromisesRequest.Complete();
+
+ // Reset cdm
+ auto exit = MakeScopeExit([&] {
+ for (auto& cdm : mCDMs) {
+ cdm->Shutdown();
+ }
+ mCDMs.Clear();
+ });
+
+ nsTArray<KeySystemConfig> outConfigs;
+ for (const auto& promiseRv : aResult.ResolveValue()) {
+ if (promiseRv.IsReject()) {
+ continue;
+ }
+ const MFCDMCapabilitiesIPDL& capabilities =
+ promiseRv.ResolveValue();
+ EME_LOG("capabilities: keySystem=%s (hw-secure=%d)",
+ NS_ConvertUTF16toUTF8(capabilities.keySystem()).get(),
+ capabilities.isHardwareDecryption());
+ for (const auto& v : capabilities.videoCapabilities()) {
+ EME_LOG("capabilities: video=%s",
+ NS_ConvertUTF16toUTF8(v.contentType()).get());
+ }
+ for (const auto& a : capabilities.audioCapabilities()) {
+ EME_LOG("capabilities: audio=%s",
+ NS_ConvertUTF16toUTF8(a.contentType()).get());
+ }
+ for (const auto& v : capabilities.encryptionSchemes()) {
+ EME_LOG("capabilities: encryptionScheme=%s",
+ EncryptionSchemeStr(v));
+ }
+ KeySystemConfig* config = outConfigs.AppendElement();
+ MFCDMCapabilitiesIPDLToKeySystemConfig(capabilities, *config);
+ }
+ if (outConfigs.IsEmpty()) {
+ EME_LOG(
+ "Failed on getting capabilities from all settled promise");
+ mCapabilitiesPromiseHolder.Reject(false, __func__);
+ return;
+ }
+ mCapabilitiesPromiseHolder.Resolve(std::move(outConfigs), __func__);
+ })
+ ->Track(mCapabilitiesPromisesRequest);
+
+ return mCapabilitiesPromiseHolder.Ensure(__func__);
+}
+
+WMFCDMCapabilites::~WMFCDMCapabilites() {
+ mCapabilitiesPromisesRequest.DisconnectIfExists();
+ mCapabilitiesPromiseHolder.RejectIfExists(false, __func__);
+ for (auto& cdm : mCDMs) {
+ cdm->Shutdown();
+ }
+}
+
} // namespace mozilla
diff --git a/dom/media/eme/mediafoundation/WMFCDMImpl.h b/dom/media/eme/mediafoundation/WMFCDMImpl.h
index b7e6308848..c5bf8234af 100644
--- a/dom/media/eme/mediafoundation/WMFCDMImpl.h
+++ b/dom/media/eme/mediafoundation/WMFCDMImpl.h
@@ -34,10 +34,6 @@ class WMFCDMImpl final {
explicit WMFCDMImpl(const nsAString& aKeySystem) : mKeySystem(aKeySystem) {}
- // TODO: make this async?
- bool GetCapabilities(bool aIsHardwareDecryption,
- nsTArray<KeySystemConfig>& aOutConfigs);
-
using InitPromise = GenericPromise;
struct InitParams {
nsString mOrigin;
@@ -119,6 +115,26 @@ class WMFCDMImpl final {
MozPromiseHolder<InitPromise> mInitPromiseHolder;
};
+// A helper class to get multiple capabilities from different key systems.
+class WMFCDMCapabilites final {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WMFCDMCapabilites);
+ WMFCDMCapabilites() = default;
+
+ using SupportedConfigsPromise = KeySystemConfig::SupportedConfigsPromise;
+ RefPtr<SupportedConfigsPromise> GetCapabilities(
+ const nsTArray<KeySystemConfigRequest>& aRequests);
+
+ private:
+ ~WMFCDMCapabilites();
+
+ nsTArray<RefPtr<MFCDMChild>> mCDMs;
+ MozPromiseHolder<SupportedConfigsPromise> mCapabilitiesPromiseHolder;
+ MozPromiseRequestHolder<
+ MFCDMChild::CapabilitiesPromise::AllSettledPromiseType>
+ mCapabilitiesPromisesRequest;
+};
+
} // namespace mozilla
#endif // DOM_MEDIA_EME_MEDIAFOUNDATION_WMFCDMIMPL_H_
diff --git a/dom/media/eme/mediafoundation/WMFCDMProxy.cpp b/dom/media/eme/mediafoundation/WMFCDMProxy.cpp
index f7e05dfb6a..5fd73c2dcf 100644
--- a/dom/media/eme/mediafoundation/WMFCDMProxy.cpp
+++ b/dom/media/eme/mediafoundation/WMFCDMProxy.cpp
@@ -158,7 +158,7 @@ void WMFCDMProxy::ResolvePromiseWithKeyStatus(
RETURN_IF_SHUTDOWN();
EME_LOG("WMFCDMProxy::ResolvePromiseWithKeyStatus(this=%p, pid=%" PRIu32
", status=%s)",
- this, aId, ToMediaKeyStatusStr(aStatus));
+ this, aId, dom::GetEnumString(aStatus).get());
if (!mKeys.IsNull()) {
mKeys->ResolvePromiseWithKeyStatus(aId, aStatus);
} else {
diff --git a/dom/media/eme/metrics.yaml b/dom/media/eme/metrics.yaml
new file mode 100644
index 0000000000..9a8cb0783a
--- /dev/null
+++ b/dom/media/eme/metrics.yaml
@@ -0,0 +1,42 @@
+# 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/.
+
+# Adding a new metric? We have docs for that!
+# https://firefox-source-docs.mozilla.org/toolkit/components/glean/user/new_definitions_file.html
+
+---
+$schema: moz://mozilla.org/schemas/glean/metrics/2-0-0
+$tags:
+ - 'Core :: Audio/Video'
+
+mediadrm:
+ eme_playback:
+ type: event
+ description: >
+ Record the EME play time with the video codec and resolutions.
+ metadata:
+ tags:
+ - 'Core :: Audio/Video: Playback'
+ bugs:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1882567
+ data_reviews:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1882567#3
+ data_sensitivity:
+ - technical
+ notification_emails:
+ - media-alerts@mozilla.com
+ extra_keys:
+ key_system:
+ description: The key system used for the EME playback
+ type: string
+ played_time:
+ description: How many second the EME content has been played since last record
+ type: quantity
+ resolution:
+ description: The video resolution used for EME playback
+ type: string
+ video_codec:
+ description: The video codec used for EME playback
+ type: string
+ expires: never