EMEUtils.cpp (9035B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "mozilla/EMEUtils.h" 8 9 #include "KeySystemConfig.h" 10 #include "MediaData.h" 11 #include "jsfriendapi.h" 12 #include "mozilla/StaticPrefs_media.h" 13 #include "mozilla/dom/BufferSourceBinding.h" 14 #include "mozilla/dom/Document.h" 15 #include "mozilla/dom/KeySystemNames.h" 16 #include "mozilla/dom/UnionTypes.h" 17 #include "nsContentUtils.h" 18 #include "nsIScriptObjectPrincipal.h" 19 20 #ifdef MOZ_WMF_CDM 21 # include "mozilla/PMFCDM.h" 22 #endif 23 24 namespace mozilla { 25 26 LogModule* GetEMELog() { 27 static LazyLogModule log("EME"); 28 return log; 29 } 30 31 LogModule* GetEMEVerboseLog() { 32 static LazyLogModule log("EMEV"); 33 return log; 34 } 35 36 void CopyArrayBufferViewOrArrayBufferData( 37 const dom::BufferSource& aBufferOrView, nsTArray<uint8_t>& aOutData) { 38 aOutData.Clear(); 39 (void)dom::AppendTypedArrayDataTo(aBufferOrView, aOutData); 40 } 41 42 bool IsClearkeyKeySystem(const nsAString& aKeySystem) { 43 if (StaticPrefs::media_clearkey_test_key_systems_enabled()) { 44 return aKeySystem.EqualsLiteral(kClearKeyKeySystemName) || 45 aKeySystem.EqualsLiteral(kClearKeyWithProtectionQueryKeySystemName); 46 } 47 return aKeySystem.EqualsLiteral(kClearKeyKeySystemName); 48 } 49 50 bool IsWidevineKeySystem(const nsAString& aKeySystem) { 51 return aKeySystem.EqualsLiteral(kWidevineKeySystemName); 52 } 53 54 #ifdef MOZ_WMF_CDM 55 bool IsMediaFoundationCDMPlaybackEnabled() { 56 // 1=enabled encrypted and clear, 2=enabled encrypted. 57 return StaticPrefs::media_wmf_media_engine_enabled() == 1 || 58 StaticPrefs::media_wmf_media_engine_enabled() == 2; 59 } 60 61 bool IsPlayReadyEnabled() { 62 return StaticPrefs::media_eme_playready_enabled() && 63 IsMediaFoundationCDMPlaybackEnabled(); 64 } 65 66 bool IsPlayReadyKeySystemAndSupported(const nsAString& aKeySystem) { 67 if (!IsPlayReadyEnabled()) { 68 return false; 69 } 70 return aKeySystem.EqualsLiteral(kPlayReadyKeySystemName) || 71 aKeySystem.EqualsLiteral(kPlayReadyKeySystemHardware) || 72 aKeySystem.EqualsLiteral(kPlayReadyHardwareClearLeadKeySystemName); 73 } 74 75 bool IsWidevineHardwareDecryptionEnabled() { 76 return StaticPrefs::media_eme_widevine_experiment_enabled() && 77 IsMediaFoundationCDMPlaybackEnabled(); 78 } 79 80 bool IsWidevineExperimentKeySystemAndSupported(const nsAString& aKeySystem) { 81 if (!IsWidevineHardwareDecryptionEnabled()) { 82 return false; 83 } 84 return aKeySystem.EqualsLiteral(kWidevineExperimentKeySystemName) || 85 aKeySystem.EqualsLiteral(kWidevineExperiment2KeySystemName); 86 } 87 88 bool IsWMFClearKeySystemAndSupported(const nsAString& aKeySystem) { 89 if (!StaticPrefs::media_eme_wmf_clearkey_enabled()) { 90 return false; 91 } 92 if (!IsMediaFoundationCDMPlaybackEnabled()) { 93 return false; 94 } 95 return aKeySystem.EqualsLiteral(kClearKeyKeySystemName); 96 } 97 #endif 98 99 nsString KeySystemToProxyName(const nsAString& aKeySystem) { 100 if (IsClearkeyKeySystem(aKeySystem)) { 101 #ifdef MOZ_WMF_CDM 102 if (StaticPrefs::media_eme_wmf_clearkey_enabled()) { 103 return u"mfcdm-clearkey"_ns; 104 } 105 #endif 106 return u"gmp-clearkey"_ns; 107 } 108 if (IsWidevineKeySystem(aKeySystem)) { 109 return u"gmp-widevinecdm"_ns; 110 } 111 #ifdef MOZ_WMF_CDM 112 if (IsPlayReadyKeySystemAndSupported(aKeySystem)) { 113 return u"mfcdm-playready"_ns; 114 } 115 if (IsWidevineExperimentKeySystemAndSupported(aKeySystem)) { 116 return u"mfcdm-widevine"_ns; 117 } 118 #endif 119 MOZ_ASSERT_UNREACHABLE("Not supported key system!"); 120 return u""_ns; 121 } 122 123 bool IsHardwareDecryptionSupported( 124 const dom::MediaKeySystemConfiguration& aConfig) { 125 for (const auto& capabilities : aConfig.mAudioCapabilities) { 126 if (capabilities.mRobustness.EqualsLiteral("HW_SECURE_ALL")) { 127 return true; 128 } 129 } 130 for (const auto& capabilities : aConfig.mVideoCapabilities) { 131 if (capabilities.mRobustness.EqualsLiteral("3000") || 132 capabilities.mRobustness.EqualsLiteral("HW_SECURE_ALL") || 133 capabilities.mRobustness.EqualsLiteral("HW_SECURE_DECODE")) { 134 return true; 135 } 136 } 137 return false; 138 } 139 140 bool IsHardwareDecryptionSupported(const KeySystemConfig& aConfig) { 141 for (const auto& robustness : aConfig.mAudioRobustness) { 142 if (robustness.EqualsLiteral("HW_SECURE_ALL")) { 143 return true; 144 } 145 } 146 for (const auto& robustness : aConfig.mVideoRobustness) { 147 if (robustness.EqualsLiteral("3000") || 148 robustness.EqualsLiteral("HW_SECURE_ALL") || 149 robustness.EqualsLiteral("HW_SECURE_DECODE")) { 150 return true; 151 } 152 } 153 return false; 154 } 155 156 #ifdef MOZ_WMF_CDM 157 void MFCDMCapabilitiesIPDLToKeySystemConfig( 158 const MFCDMCapabilitiesIPDL& aCDMConfig, 159 KeySystemConfig& aKeySystemConfig) { 160 aKeySystemConfig.mKeySystem = aCDMConfig.keySystem(); 161 162 for (const auto& type : aCDMConfig.initDataTypes()) { 163 aKeySystemConfig.mInitDataTypes.AppendElement(type); 164 } 165 166 for (const auto& type : aCDMConfig.sessionTypes()) { 167 aKeySystemConfig.mSessionTypes.AppendElement(type); 168 } 169 170 for (const auto& c : aCDMConfig.videoCapabilities()) { 171 if (!c.robustness().IsEmpty() && 172 !aKeySystemConfig.mVideoRobustness.Contains(c.robustness())) { 173 aKeySystemConfig.mVideoRobustness.AppendElement(c.robustness()); 174 } 175 CryptoSchemeSet schemes; 176 for (const auto& scheme : c.encryptionSchemes()) { 177 schemes += scheme; 178 } 179 aKeySystemConfig.mMP4.SetCanDecryptAndDecode( 180 NS_ConvertUTF16toUTF8(c.contentType()), Some(schemes)); 181 } 182 for (const auto& c : aCDMConfig.audioCapabilities()) { 183 if (!c.robustness().IsEmpty() && 184 !aKeySystemConfig.mAudioRobustness.Contains(c.robustness())) { 185 aKeySystemConfig.mAudioRobustness.AppendElement(c.robustness()); 186 } 187 CryptoSchemeSet schemes; 188 for (const auto& scheme : c.encryptionSchemes()) { 189 schemes += scheme; 190 } 191 aKeySystemConfig.mMP4.SetCanDecryptAndDecode( 192 NS_ConvertUTF16toUTF8(c.contentType()), Some(schemes)); 193 } 194 aKeySystemConfig.mPersistentState = aCDMConfig.persistentState(); 195 aKeySystemConfig.mDistinctiveIdentifier = aCDMConfig.distinctiveID(); 196 EME_LOG("New Capabilities=%s", 197 NS_ConvertUTF16toUTF8(aKeySystemConfig.GetDebugInfo()).get()); 198 } 199 #endif 200 201 bool DoesKeySystemSupportClearLead(const nsAString& aKeySystem) { 202 // I believe that Widevine L3 supports clear-lead, but I couldn't find any 203 // official documentation to prove that. The only one I can find is that Shaka 204 // player mentions the clear lead feature. So we expect L3 should have that as 205 // well. But for HWDRM, Widevine L1 and SL3000 needs to rely on special checks 206 // to know whether clearlead is supported. That will be implemented by 207 // querying for special key system names. 208 // https://shaka-project.github.io/shaka-packager/html/documentation.html 209 #ifdef MOZ_WMF_CDM 210 if (aKeySystem.EqualsLiteral(kWidevineExperiment2KeySystemName) || 211 aKeySystem.EqualsLiteral(kPlayReadyHardwareClearLeadKeySystemName)) { 212 return true; 213 } 214 #endif 215 return aKeySystem.EqualsLiteral(kWidevineKeySystemName); 216 } 217 218 bool CheckIfHarewareDRMConfigExists( 219 const nsTArray<dom::MediaKeySystemConfiguration>& aConfigs) { 220 bool foundHWDRMconfig = false; 221 for (const auto& config : aConfigs) { 222 if (IsHardwareDecryptionSupported(config)) { 223 foundHWDRMconfig = true; 224 break; 225 } 226 } 227 return foundHWDRMconfig; 228 } 229 230 bool DoesKeySystemSupportHardwareDecryption(const nsAString& aKeySystem) { 231 #ifdef MOZ_WMF_CDM 232 if (aKeySystem.EqualsLiteral(kPlayReadyKeySystemHardware) || 233 aKeySystem.EqualsLiteral(kPlayReadyHardwareClearLeadKeySystemName) || 234 aKeySystem.EqualsLiteral(kWidevineExperimentKeySystemName) || 235 aKeySystem.EqualsLiteral(kWidevineExperiment2KeySystemName)) { 236 return true; 237 } 238 #endif 239 return false; 240 } 241 242 void DeprecationWarningLog(const dom::Document* aDocument, 243 const char* aMsgName) { 244 if (!aDocument || !aMsgName) { 245 return; 246 } 247 EME_LOG("DeprecationWarning Logging deprecation warning '%s' to WebConsole.", 248 aMsgName); 249 nsTHashMap<nsCharPtrHashKey, bool> warnings; 250 warnings.InsertOrUpdate(aMsgName, true); 251 AutoTArray<nsString, 1> params; 252 nsString& uri = *params.AppendElement(); 253 (void)aDocument->GetDocumentURI(uri); 254 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "Media"_ns, 255 aDocument, nsContentUtils::eDOM_PROPERTIES, 256 aMsgName, params); 257 } 258 259 Maybe<nsCString> GetOrigin(const dom::Document* aDocument) { 260 if (!aDocument) { 261 return Nothing(); 262 } 263 nsCOMPtr<nsIScriptObjectPrincipal> sop = 264 do_QueryInterface(aDocument->GetInnerWindow()); 265 if (!sop) { 266 return Nothing(); 267 } 268 auto* principal = sop->GetPrincipal(); 269 nsAutoCString origin; 270 nsresult rv = principal->GetOrigin(origin); 271 if (NS_FAILED(rv)) { 272 return Nothing(); 273 } 274 return Some(origin); 275 } 276 277 } // namespace mozilla