diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /dom/media/eme/KeySystemConfig.cpp | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/media/eme/KeySystemConfig.cpp')
-rw-r--r-- | dom/media/eme/KeySystemConfig.cpp | 360 |
1 files changed, 360 insertions, 0 deletions
diff --git a/dom/media/eme/KeySystemConfig.cpp b/dom/media/eme/KeySystemConfig.cpp new file mode 100644 index 0000000000..b1b1f9ff63 --- /dev/null +++ b/dom/media/eme/KeySystemConfig.cpp @@ -0,0 +1,360 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* 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/. */ + +#include "KeySystemConfig.h" + +#include "EMEUtils.h" +#include "GMPUtils.h" +#include "KeySystemNames.h" +#include "mozilla/dom/Promise.h" +#include "mozilla/StaticPrefs_media.h" +#include "nsPrintfCString.h" + +#ifdef XP_WIN +# include "WMFDecoderModule.h" +#endif +#ifdef MOZ_WIDGET_ANDROID +# include "AndroidDecoderModule.h" +# include "mozilla/java/MediaDrmProxyWrappers.h" +# include "nsMimeTypes.h" +#endif + +#ifdef MOZ_WMF_CDM +# include "mediafoundation/WMFCDMImpl.h" +#endif + +namespace mozilla { + +/* static */ +bool KeySystemConfig::Supports(const nsAString& aKeySystem) { +#ifdef MOZ_WIDGET_ANDROID + // No GMP on Android, check if we can use MediaDrm for this keysystem. + if (mozilla::java::MediaDrmProxy::IsSchemeSupported( + NS_ConvertUTF16toUTF8(aKeySystem))) { + return true; + } +#else +# ifdef MOZ_WMF_CDM + // Test only, pretend we have already installed CDMs. + if (StaticPrefs::media_eme_wmf_use_mock_cdm_for_external_cdms()) { + return true; + } +# endif + // Check if Widevine L3 or Clearkey has been downloaded via GMP downloader. + if (IsWidevineKeySystem(aKeySystem) || IsClearkeyKeySystem(aKeySystem)) { + return HaveGMPFor(nsCString(CHROMIUM_CDM_API), + {NS_ConvertUTF16toUTF8(aKeySystem)}); + } +#endif + +#if MOZ_WMF_CDM + // Check if Widevine L1 has been downloaded via GMP downloader. + if (IsWidevineExperimentKeySystemAndSupported(aKeySystem)) { + return HaveGMPFor(nsCString(kWidevineExperimentAPIName), + {nsCString(kWidevineExperimentKeySystemName)}); + } + + if ((IsPlayReadyKeySystemAndSupported(aKeySystem) || + IsWMFClearKeySystemAndSupported(aKeySystem)) && + WMFCDMImpl::Supports(aKeySystem)) { + return true; + } +#endif + + return false; +} + +/* static */ +bool KeySystemConfig::CreateKeySystemConfigs( + const nsAString& aKeySystem, nsTArray<KeySystemConfig>& aOutConfigs) { + if (!Supports(aKeySystem)) { + return false; + } + + 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 + 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->mWebM.SetCanDecrypt(EME_CODEC_VORBIS); + config->mWebM.SetCanDecrypt(EME_CODEC_OPUS); + config->mWebM.SetCanDecrypt(EME_CODEC_VP8); + config->mWebM.SetCanDecrypt(EME_CODEC_VP9); + + 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 (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); +#ifdef MOZ_WIDGET_ANDROID + 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); + +#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; + + DataForValidation validationList[] = { + {nsCString(VIDEO_MP4), EME_CODEC_H264, java::MediaDrmProxy::AVC, + &config->mMP4}, + {nsCString(VIDEO_MP4), EME_CODEC_VP9, java::MediaDrmProxy::AVC, + &config->mMP4}, + {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_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); + } + } + } +#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 + 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->mWebM.SetCanDecrypt(EME_CODEC_VORBIS); + config->mWebM.SetCanDecrypt(EME_CODEC_OPUS); + config->mWebM.SetCanDecryptAndDecode(EME_CODEC_VP8); + config->mWebM.SetCanDecryptAndDecode(EME_CODEC_VP9); +#endif + return true; + } +#ifdef MOZ_WMF_CDM + if (IsPlayReadyKeySystemAndSupported(aKeySystem) || + IsWidevineExperimentKeySystemAndSupported(aKeySystem)) { + RefPtr<WMFCDMImpl> cdm = MakeRefPtr<WMFCDMImpl>(aKeySystem); + return cdm->GetCapabilities(aOutConfigs); + } +#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); + } +#endif + return mKeySystem.Equals(aKeySystem); +} + +/* static */ +void KeySystemConfig::GetGMPKeySystemConfigs(dom::Promise* aPromise) { + MOZ_ASSERT(aPromise); + nsTArray<KeySystemConfig> keySystemConfigs; + const nsTArray<nsString> keySystemNames{ + NS_ConvertUTF8toUTF16(kClearKeyKeySystemName), + NS_ConvertUTF8toUTF16(kWidevineKeySystemName), + }; + FallibleTArray<dom::CDMInformation> cdmInfo; + for (const auto& name : keySystemNames) { +#ifdef MOZ_WMF_CDM + if (IsWMFClearKeySystemAndSupported(name)) { + // Using wmf clearkey, not gmp clearkey. + continue; + } +#endif + if (KeySystemConfig::CreateKeySystemConfigs(name, 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; + } + } + aPromise->MaybeResolve(cdmInfo); +} + +nsString KeySystemConfig::GetDebugInfo() const { + nsString debugInfo; + debugInfo.AppendLiteral(" key-system="); + debugInfo.Append(mKeySystem); + debugInfo.AppendLiteral(" init-data-type=["); + for (size_t idx = 0; idx < mInitDataTypes.Length(); idx++) { + debugInfo.Append(mInitDataTypes[idx]); + if (idx + 1 < mInitDataTypes.Length()) { + debugInfo.AppendLiteral(","); + } + } + debugInfo.AppendLiteral("]"); + debugInfo.AppendASCII( + nsPrintfCString(" persistent=%s", RequirementToStr(mPersistentState)) + .get()); + debugInfo.AppendASCII( + nsPrintfCString(" distinctive=%s", + RequirementToStr(mDistinctiveIdentifier)) + .get()); + debugInfo.AppendLiteral(" sessionType=["); + for (size_t idx = 0; idx < mSessionTypes.Length(); idx++) { + debugInfo.AppendASCII( + nsPrintfCString("%s", SessionTypeToStr(mSessionTypes[idx])).get()); + if (idx + 1 < mSessionTypes.Length()) { + debugInfo.AppendLiteral(","); + } + } + debugInfo.AppendLiteral("]"); + debugInfo.AppendLiteral(" video-robustness="); + for (size_t idx = 0; idx < mVideoRobustness.Length(); idx++) { + debugInfo.Append(mVideoRobustness[idx]); + if (idx + 1 < mVideoRobustness.Length()) { + debugInfo.AppendLiteral(","); + } + } + debugInfo.AppendLiteral(" audio-robustness="); + for (size_t idx = 0; idx < mAudioRobustness.Length(); idx++) { + debugInfo.Append(mAudioRobustness[idx]); + if (idx + 1 < mAudioRobustness.Length()) { + debugInfo.AppendLiteral(","); + } + } + debugInfo.AppendLiteral(" scheme=["); + for (size_t idx = 0; idx < mEncryptionSchemes.Length(); idx++) { + debugInfo.Append(mEncryptionSchemes[idx]); + if (idx + 1 < mEncryptionSchemes.Length()) { + debugInfo.AppendLiteral(","); + } + } + debugInfo.AppendLiteral("]"); + debugInfo.AppendLiteral(" MP4={"); + debugInfo.Append(NS_ConvertUTF8toUTF16(mMP4.GetDebugInfo())); + debugInfo.AppendLiteral("}"); + debugInfo.AppendLiteral(" WEBM={"); + debugInfo.Append(NS_ConvertUTF8toUTF16(mWebM.GetDebugInfo())); + debugInfo.AppendLiteral("}"); + debugInfo.AppendASCII( + nsPrintfCString(" isHDCP22Compatible=%d", mIsHDCP22Compatible)); + return debugInfo; +} + +KeySystemConfig::SessionType ConvertToKeySystemConfigSessionType( + dom::MediaKeySessionType aType) { + switch (aType) { + case dom::MediaKeySessionType::Temporary: + return KeySystemConfig::SessionType::Temporary; + case dom::MediaKeySessionType::Persistent_license: + return KeySystemConfig::SessionType::PersistentLicense; + default: + MOZ_ASSERT_UNREACHABLE("Invalid session type"); + return KeySystemConfig::SessionType::Temporary; + } +} + +const char* SessionTypeToStr(KeySystemConfig::SessionType aType) { + switch (aType) { + case KeySystemConfig::SessionType::Temporary: + return "Temporary"; + case KeySystemConfig::SessionType::PersistentLicense: + return "PersistentLicense"; + default: + MOZ_ASSERT_UNREACHABLE("Invalid session type"); + return "Invalid"; + } +} + +const char* RequirementToStr(KeySystemConfig::Requirement aRequirement) { + switch (aRequirement) { + case KeySystemConfig::Requirement::Required: + return "required"; + case KeySystemConfig::Requirement::Optional: + return "optional"; + default: + return "not-allowed"; + } +} + +} // namespace mozilla |