1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "webencryptedmediaclient_impl.h"
7 #include "base/logging.h"
8 #include "base/metrics/histogram.h"
9 #include "base/strings/string_util.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "media/base/key_systems.h"
12 #include "media/base/media_permission.h"
13 #include "net/base/mime_util.h"
14 #include "third_party/WebKit/public/platform/WebEncryptedMediaRequest.h"
15 #include "third_party/WebKit/public/platform/WebMediaKeySystemConfiguration.h"
16 #include "third_party/WebKit/public/platform/WebString.h"
17 #include "third_party/WebKit/public/platform/WebVector.h"
18 #include "webcontentdecryptionmodule_impl.h"
19 #include "webcontentdecryptionmoduleaccess_impl.h"
23 // These names are used by UMA.
24 const char kKeySystemSupportUMAPrefix
[] =
25 "Media.EME.RequestMediaKeySystemAccess.";
27 static bool IsSupportedContentType(
28 const std::string
& key_system
,
29 const std::string
& mime_type
,
30 const std::string
& codecs
) {
31 // Per RFC 6838, "Both top-level type and subtype names are case-insensitive."
32 // TODO(sandersd): Check that |container| matches the capability:
33 // - audioCapabilitys: audio/mp4 or audio/webm.
34 // - videoCapabilitys: video/mp4 or video/webm.
35 // http://crbug.com/457384.
36 std::string container
= base::StringToLowerASCII(mime_type
);
38 // Check that |codecs| are supported as specified (e.g. "mp4a.40.2").
39 std::vector
<std::string
> codec_vector
;
40 net::ParseCodecString(codecs
, &codec_vector
, false);
41 if (!net::AreSupportedMediaCodecs(codec_vector
))
44 // IsSupportedKeySystemWithMediaMimeType() only works with base codecs
45 // (e.g. "mp4a"), so reparse |codecs| to get the base only.
47 net::ParseCodecString(codecs
, &codec_vector
, true);
48 return IsSupportedKeySystemWithMediaMimeType(container
, codec_vector
,
52 static bool GetSupportedConfiguration(
53 const std::string
& key_system
,
54 const blink::WebMediaKeySystemConfiguration
& candidate
,
55 const blink::WebSecurityOrigin
& security_origin
,
56 blink::WebMediaKeySystemConfiguration
* accumulated_configuration
) {
57 if (!candidate
.initDataTypes
.isEmpty()) {
58 std::vector
<blink::WebString
> init_data_types
;
60 for (size_t i
= 0; i
< candidate
.initDataTypes
.size(); i
++) {
61 const blink::WebString
& init_data_type
= candidate
.initDataTypes
[i
];
62 if (init_data_type
.isEmpty())
64 if (base::IsStringASCII(init_data_type
) &&
65 IsSupportedKeySystemWithInitDataType(
66 key_system
, base::UTF16ToASCII(init_data_type
))) {
67 init_data_types
.push_back(init_data_type
);
71 if (init_data_types
.empty())
74 accumulated_configuration
->initDataTypes
= init_data_types
;
77 // TODO(sandersd): Implement distinctiveIdentifier and persistentState checks.
78 if (candidate
.distinctiveIdentifier
!=
79 blink::WebMediaKeySystemConfiguration::Requirement::Optional
||
80 candidate
.persistentState
!=
81 blink::WebMediaKeySystemConfiguration::Requirement::Optional
) {
85 if (!candidate
.audioCapabilities
.isEmpty()) {
86 std::vector
<blink::WebMediaKeySystemMediaCapability
> audio_capabilities
;
88 for (size_t i
= 0; i
< candidate
.audioCapabilities
.size(); i
++) {
89 const blink::WebMediaKeySystemMediaCapability
& capabilities
=
90 candidate
.audioCapabilities
[i
];
91 if (capabilities
.mimeType
.isEmpty())
93 if (!base::IsStringASCII(capabilities
.mimeType
) ||
94 !base::IsStringASCII(capabilities
.codecs
) ||
95 !IsSupportedContentType(
96 key_system
, base::UTF16ToASCII(capabilities
.mimeType
),
97 base::UTF16ToASCII(capabilities
.codecs
))) {
100 // TODO(sandersd): Support robustness.
101 if (!capabilities
.robustness
.isEmpty())
103 audio_capabilities
.push_back(capabilities
);
106 if (audio_capabilities
.empty())
109 accumulated_configuration
->audioCapabilities
= audio_capabilities
;
112 if (!candidate
.videoCapabilities
.isEmpty()) {
113 std::vector
<blink::WebMediaKeySystemMediaCapability
> video_capabilities
;
115 for (size_t i
= 0; i
< candidate
.videoCapabilities
.size(); i
++) {
116 const blink::WebMediaKeySystemMediaCapability
& capabilities
=
117 candidate
.videoCapabilities
[i
];
118 if (capabilities
.mimeType
.isEmpty())
120 if (!base::IsStringASCII(capabilities
.mimeType
) ||
121 !base::IsStringASCII(capabilities
.codecs
) ||
122 !IsSupportedContentType(
123 key_system
, base::UTF16ToASCII(capabilities
.mimeType
),
124 base::UTF16ToASCII(capabilities
.codecs
))) {
127 // TODO(sandersd): Support robustness.
128 if (!capabilities
.robustness
.isEmpty())
130 video_capabilities
.push_back(capabilities
);
133 if (video_capabilities
.empty())
136 accumulated_configuration
->videoCapabilities
= video_capabilities
;
139 // TODO(sandersd): Prompt for distinctive identifiers and/or persistent state
140 // if required. Make sure that future checks are silent.
141 // http://crbug.com/446263.
146 // Report usage of key system to UMA. There are 2 different counts logged:
147 // 1. The key system is requested.
148 // 2. The requested key system and options are supported.
149 // Each stat is only reported once per renderer frame per key system.
150 // Note that WebEncryptedMediaClientImpl is only created once by each
152 class WebEncryptedMediaClientImpl::Reporter
{
154 enum KeySystemSupportStatus
{
155 KEY_SYSTEM_REQUESTED
= 0,
156 KEY_SYSTEM_SUPPORTED
= 1,
157 KEY_SYSTEM_SUPPORT_STATUS_COUNT
160 explicit Reporter(const std::string
& key_system_for_uma
)
161 : uma_name_(kKeySystemSupportUMAPrefix
+ key_system_for_uma
),
162 is_request_reported_(false),
163 is_support_reported_(false) {}
166 void ReportRequested() {
167 if (is_request_reported_
)
169 Report(KEY_SYSTEM_REQUESTED
);
170 is_request_reported_
= true;
173 void ReportSupported() {
174 DCHECK(is_request_reported_
);
175 if (is_support_reported_
)
177 Report(KEY_SYSTEM_SUPPORTED
);
178 is_support_reported_
= true;
182 void Report(KeySystemSupportStatus status
) {
183 // Not using UMA_HISTOGRAM_ENUMERATION directly because UMA_* macros
184 // require the names to be constant throughout the process' lifetime.
185 base::LinearHistogram::FactoryGet(
186 uma_name_
, 1, KEY_SYSTEM_SUPPORT_STATUS_COUNT
,
187 KEY_SYSTEM_SUPPORT_STATUS_COUNT
+ 1,
188 base::Histogram::kUmaTargetedHistogramFlag
)->Add(status
);
191 const std::string uma_name_
;
192 bool is_request_reported_
;
193 bool is_support_reported_
;
196 WebEncryptedMediaClientImpl::WebEncryptedMediaClientImpl(
197 scoped_ptr
<CdmFactory
> cdm_factory
,
198 MediaPermission
* media_permission
)
199 : cdm_factory_(cdm_factory
.Pass()), weak_factory_(this) {
200 // TODO(sandersd): Use |media_permission| to check for media permissions in
202 DCHECK(media_permission
);
205 WebEncryptedMediaClientImpl::~WebEncryptedMediaClientImpl() {
208 void WebEncryptedMediaClientImpl::requestMediaKeySystemAccess(
209 blink::WebEncryptedMediaRequest request
) {
210 // TODO(jrummell): This should be asynchronous.
212 // Continued from requestMediaKeySystemAccess(), step 7, from
213 // https://w3c.github.io/encrypted-media/#requestmediakeysystemaccess
215 // 7.1 If keySystem is not one of the Key Systems supported by the user
216 // agent, reject promise with with a new DOMException whose name is
217 // NotSupportedError. String comparison is case-sensitive.
218 if (!base::IsStringASCII(request
.keySystem())) {
219 request
.requestNotSupported("Only ASCII keySystems are supported");
223 std::string key_system
= base::UTF16ToASCII(request
.keySystem());
225 // Report this request to the appropriate Reporter.
226 Reporter
* reporter
= GetReporter(key_system
);
227 reporter
->ReportRequested();
229 if (!IsConcreteSupportedKeySystem(key_system
)) {
230 request
.requestNotSupported("Unsupported keySystem");
234 // 7.2 Let implementation be the implementation of keySystem.
235 // 7.3 For each value in supportedConfigurations, run the GetSupported
236 // Configuration algorithm and if successful, resolve promise with access
237 // and abort these steps.
238 const blink::WebVector
<blink::WebMediaKeySystemConfiguration
>&
239 configurations
= request
.supportedConfigurations();
241 // TODO(sandersd): Remove once Blink requires the configurations parameter for
242 // requestMediaKeySystemAccess().
243 if (configurations
.isEmpty()) {
244 reporter
->ReportSupported();
245 request
.requestSucceeded(WebContentDecryptionModuleAccessImpl::Create(
246 request
.keySystem(), blink::WebMediaKeySystemConfiguration(),
247 request
.securityOrigin(), weak_factory_
.GetWeakPtr()));
251 for (size_t i
= 0; i
< configurations
.size(); i
++) {
252 const blink::WebMediaKeySystemConfiguration
& candidate
= configurations
[i
];
253 blink::WebMediaKeySystemConfiguration accumulated_configuration
;
254 if (GetSupportedConfiguration(key_system
, candidate
,
255 request
.securityOrigin(),
256 &accumulated_configuration
)) {
257 reporter
->ReportSupported();
258 request
.requestSucceeded(WebContentDecryptionModuleAccessImpl::Create(
259 request
.keySystem(), accumulated_configuration
,
260 request
.securityOrigin(), weak_factory_
.GetWeakPtr()));
265 // 7.4 Reject promise with a new DOMException whose name is NotSupportedError.
266 request
.requestNotSupported(
267 "None of the requested configurations were supported.");
270 void WebEncryptedMediaClientImpl::CreateCdm(
271 const blink::WebString
& key_system
,
272 const blink::WebSecurityOrigin
& security_origin
,
273 blink::WebContentDecryptionModuleResult result
) {
274 WebContentDecryptionModuleImpl::Create(cdm_factory_
.get(), security_origin
,
278 // Lazily create Reporters.
279 WebEncryptedMediaClientImpl::Reporter
* WebEncryptedMediaClientImpl::GetReporter(
280 const std::string
& key_system
) {
281 std::string uma_name
= GetKeySystemNameForUMA(key_system
);
282 Reporter
* reporter
= reporters_
.get(uma_name
);
283 if (reporter
!= nullptr)
286 // Reporter not found, so create one.
288 reporters_
.add(uma_name
, make_scoped_ptr(new Reporter(uma_name
)));
289 DCHECK(result
.second
);
290 return result
.first
->second
;