From d8bbc7858622b6d9c278469aab701ca0b609cddf Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 15 May 2024 05:35:49 +0200 Subject: Merging upstream version 126.0. Signed-off-by: Daniel Baumann --- dom/media/eme/EMEUtils.cpp | 79 +++--- dom/media/eme/EMEUtils.h | 12 +- dom/media/eme/KeySystemConfig.cpp | 363 ++++++++++++++------------ dom/media/eme/KeySystemConfig.h | 36 ++- dom/media/eme/MediaKeySession.cpp | 67 +++-- dom/media/eme/MediaKeySession.h | 6 + dom/media/eme/MediaKeySystemAccess.cpp | 196 +++++++------- dom/media/eme/MediaKeySystemAccess.h | 16 +- dom/media/eme/MediaKeySystemAccessManager.cpp | 67 ++--- dom/media/eme/MediaKeySystemAccessManager.h | 2 + dom/media/eme/mediafoundation/WMFCDMImpl.cpp | 155 ++++++----- dom/media/eme/mediafoundation/WMFCDMImpl.h | 24 +- dom/media/eme/mediafoundation/WMFCDMProxy.cpp | 2 +- dom/media/eme/metrics.yaml | 42 +++ 14 files changed, 615 insertions(+), 452 deletions(-) create mode 100644 dom/media/eme/metrics.yaml (limited to 'dom/media/eme') 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 warnings; + warnings.InsertOrUpdate(aMsgName, true); + AutoTArray 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& 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& 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::CreateKeySystemConfigs( + const nsTArray& aRequests) { + // Create available configs for all supported key systems in the request, but + // some of them might not be created immediately. + + nsTArray outConfigs; + nsTArray 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 cdm = MakeRefPtr(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 promise = + new SupportedConfigsPromise::Private(__func__); + RefPtr 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 keySystemConfigs; + + // Generate config requests const nsTArray keySystemNames{ NS_ConvertUTF8toUTF16(kClearKeyKeySystemName), NS_ConvertUTF8toUTF16(kWidevineKeySystemName), }; - FallibleTArray cdmInfo; - for (const auto& name : keySystemNames) { + nsTArray 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{aPromise}]( + const SupportedConfigsPromise::ResolveOrRejectValue& aResult) { + if (aResult.IsResolve()) { + // Generate CDMInformation from configs + FallibleTArray 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, bool /* aIgnored */, + /* IsExclusive = */ true>; + using KeySystemConfigPromise = + MozPromise; + // 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& aOutConfigs); + static RefPtr CreateKeySystemConfigs( + const nsTArray& 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 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& aOutConfigs); + static void CreateWivineL3KeySystemConfigs( + const KeySystemConfigRequest& aRequest, + nsTArray& 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 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{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& 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 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 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 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& aData, + DetailedPromise* aPromise); + RefPtr mClosed; RefPtr 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 GetSupportedKeySystems( - const nsAString& aKeySystem, bool aIsHardwareDecryption) { +static RefPtr +GetSupportedKeySystemConfigs(const nsAString& aKeySystem, + bool aIsHardwareDecryption) { using DecryptionInfo = KeySystemConfig::DecryptionInfo; - nsTArray keySystemConfigs; + nsTArray 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& 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 MediaKeySystemAccess::KeySystemSupportsInitDataType( const nsAString& aKeySystem, const nsAString& aInitDataType, bool aIsHardwareDecryption) { - nsTArray implementations; - GetKeySystemConfigs(aKeySystem, aIsHardwareDecryption, implementations); - bool containInitType = false; - for (const auto& config : implementations) { - if (config.mInitDataTypes.Contains(aInitDataType)) { - containInitType = true; - break; - } - } - return containInitType; + RefPtr 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 GetSupportedCapabilities( const nsTArray& aRequestedCapabilities, const MediaKeySystemConfiguration& aPartialConfig, const KeySystemConfig& aKeySystem, DecoderDoctorDiagnostics* aDiagnostics, - const std::function& 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 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 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& 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 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 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& aConfigs, - MediaKeySystemConfiguration& aOutConfig, - DecoderDoctorDiagnostics* aDiagnostics, bool aIsPrivateBrowsing, - const std::function& aDeprecationLogFn) { +RefPtr +MediaKeySystemAccess::GetSupportedConfig(MediaKeySystemAccessRequest* aRequest, + bool aIsPrivateBrowsing, + const Document* aDocument) { nsTArray 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 promise = + new KeySystemConfig::KeySystemConfigPromise::Private(__func__); + GetSupportedKeySystemConfigs(aRequest->mKeySystem, + isHardwareDecryptionRequest) + ->Then(GetMainThreadSerialEventTarget(), __func__, + [promise, aRequest, aIsPrivateBrowsing, + document = RefPtr{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& aConfigs, - MediaKeySystemConfiguration& aOutConfig, - DecoderDoctorDiagnostics* aDiagnostics, bool aIsPrivateBrowsing, - const std::function& aDeprecationLogFn); + static RefPtr GetSupportedConfig( + MediaKeySystemAccessRequest* aRequest, bool aIsPrivateBrowsing, + const Document* aDocument); - static bool KeySystemSupportsInitDataType(const nsAString& aKeySystem, - const nsAString& aInitDataType, - bool aIsHardwareDecryption); + static RefPtr KeySystemSupportsInitDataType( + const nsAString& aKeySystem, const nsAString& aInitDataType, + bool aIsHardwareDecryption); static nsCString ToCString( const Sequence& 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 doc = mWindow->GetExtantDoc(); - nsTHashMap warnings; - std::function deprecationWarningLogFn = - [&](const char* aMsgName) { - EME_LOG( - "MediaKeySystemAccessManager::DeprecationWarningLambda Logging " - "deprecation warning '%s' to WebConsole.", - aMsgName); - warnings.InsertOrUpdate(aMsgName, true); - AutoTArray 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{this}, this, + request = UniquePtr{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 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& aOutConfigs) { - MOZ_ASSERT(NS_IsMainThread()); - if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) { - return false; - } - - static std::unordered_map> - 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 backgroundTaskQueue; - NS_CreateBackgroundTaskQueue(__func__, getter_AddRefs(backgroundTaskQueue)); - if (!mCDM) { - mCDM = MakeRefPtr(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::Init( const WMFCDMImpl::InitParams& aParams) { if (!mCDM) { @@ -111,4 +42,88 @@ RefPtr WMFCDMImpl::Init( return mInitPromiseHolder.Ensure(__func__); } +RefPtr +WMFCDMCapabilites::GetCapabilities( + const nsTArray& 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> promises; + for (const auto& request : aRequests) { + RefPtr 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(this), this]( + CapabilitiesPromise::AllSettledPromiseType::ResolveOrRejectValue&& + aResult) { + mCapabilitiesPromisesRequest.Complete(); + + // Reset cdm + auto exit = MakeScopeExit([&] { + for (auto& cdm : mCDMs) { + cdm->Shutdown(); + } + mCDMs.Clear(); + }); + + nsTArray 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& aOutConfigs); - using InitPromise = GenericPromise; struct InitParams { nsString mOrigin; @@ -119,6 +115,26 @@ class WMFCDMImpl final { MozPromiseHolder 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 GetCapabilities( + const nsTArray& aRequests); + + private: + ~WMFCDMCapabilites(); + + nsTArray> mCDMs; + MozPromiseHolder 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 -- cgit v1.2.3