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 "media/blink/encrypted_media_player_support.h"
10 #include "base/callback_helpers.h"
11 #include "base/metrics/histogram.h"
12 #include "base/numerics/safe_conversions.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "media/base/bind_to_current_loop.h"
17 #include "media/base/key_systems.h"
18 #include "media/blink/webcontentdecryptionmodule_impl.h"
19 #include "third_party/WebKit/public/platform/WebContentDecryptionModule.h"
20 #include "third_party/WebKit/public/platform/WebMediaPlayerClient.h"
21 #include "third_party/WebKit/public/web/WebDocument.h"
22 #include "third_party/WebKit/public/web/WebLocalFrame.h"
24 using blink::WebMediaPlayer
;
25 using blink::WebMediaPlayerClient
;
26 using blink::WebString
;
30 #define BIND_TO_RENDER_LOOP(function) \
31 (BindToCurrentLoop(base::Bind(function, AsWeakPtr())))
33 #define BIND_TO_RENDER_LOOP1(function, arg1) \
34 (BindToCurrentLoop(base::Bind(function, AsWeakPtr(), arg1)))
36 // Prefix for histograms related to Encrypted Media Extensions.
37 static const char* kMediaEme
= "Media.EME.";
39 // Convert a WebString to ASCII, falling back on an empty string in the case
40 // of a non-ASCII string.
41 static std::string
ToASCIIOrEmpty(const WebString
& string
) {
42 return base::IsStringASCII(string
) ? base::UTF16ToASCII(string
)
46 // Helper functions to report media EME related stats to UMA. They follow the
47 // convention of more commonly used macros UMA_HISTOGRAM_ENUMERATION and
48 // UMA_HISTOGRAM_COUNTS. The reason that we cannot use those macros directly is
49 // that UMA_* macros require the names to be constant throughout the process'
51 static void EmeUMAHistogramEnumeration(const std::string
& key_system
,
52 const std::string
& method
,
55 base::LinearHistogram::FactoryGet(
56 kMediaEme
+ GetKeySystemNameForUMA(key_system
) + "." + method
,
57 1, boundary_value
, boundary_value
+ 1,
58 base::Histogram::kUmaTargetedHistogramFlag
)->Add(sample
);
61 static void EmeUMAHistogramCounts(const std::string
& key_system
,
62 const std::string
& method
,
64 // Use the same parameters as UMA_HISTOGRAM_COUNTS.
65 base::Histogram::FactoryGet(
66 kMediaEme
+ GetKeySystemNameForUMA(key_system
) + "." + method
,
67 1, 1000000, 50, base::Histogram::kUmaTargetedHistogramFlag
)->Add(sample
);
70 // Helper enum for reporting generateKeyRequest/addKey histograms.
71 enum MediaKeyException
{
74 kKeySystemNotSupported
,
79 static MediaKeyException
MediaKeyExceptionForUMA(
80 WebMediaPlayer::MediaKeyException e
) {
82 case WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported
:
83 return kKeySystemNotSupported
;
84 case WebMediaPlayer::MediaKeyExceptionInvalidPlayerState
:
85 return kInvalidPlayerState
;
86 case WebMediaPlayer::MediaKeyExceptionNoError
:
89 return kUnknownResultId
;
93 // Helper for converting |key_system| name and exception |e| to a pair of enum
94 // values from above, for reporting to UMA.
95 static void ReportMediaKeyExceptionToUMA(const std::string
& method
,
96 const std::string
& key_system
,
97 WebMediaPlayer::MediaKeyException e
) {
98 MediaKeyException result_id
= MediaKeyExceptionForUMA(e
);
99 DCHECK_NE(result_id
, kUnknownResultId
) << e
;
100 EmeUMAHistogramEnumeration(
101 key_system
, method
, result_id
, kMaxMediaKeyException
);
104 // Guess the type of |init_data|. This is only used to handle some corner cases
105 // so we keep it as simple as possible without breaking major use cases.
106 static EmeInitDataType
GuessInitDataType(const unsigned char* init_data
,
107 unsigned init_data_length
) {
108 // Most WebM files use KeyId of 16 bytes. CENC init data is always >16 bytes.
109 if (init_data_length
== 16)
110 return EmeInitDataType::WEBM
;
112 return EmeInitDataType::CENC
;
115 EncryptedMediaPlayerSupport::EncryptedMediaPlayerSupport(
116 CdmFactory
* cdm_factory
,
117 blink::WebMediaPlayerClient
* client
,
118 MediaPermission
* media_permission
,
119 const SetCdmContextCB
& set_cdm_context_cb
)
120 : cdm_factory_(cdm_factory
),
122 media_permission_(media_permission
),
123 init_data_type_(EmeInitDataType::UNKNOWN
),
124 set_cdm_context_cb_(set_cdm_context_cb
) {
127 EncryptedMediaPlayerSupport::~EncryptedMediaPlayerSupport() {
130 WebMediaPlayer::MediaKeyException
131 EncryptedMediaPlayerSupport::GenerateKeyRequest(
132 blink::WebLocalFrame
* frame
,
133 const WebString
& key_system
,
134 const unsigned char* init_data
,
135 unsigned init_data_length
) {
136 DVLOG(1) << "generateKeyRequest: " << base::string16(key_system
) << ": "
137 << std::string(reinterpret_cast<const char*>(init_data
),
138 static_cast<size_t>(init_data_length
));
140 std::string ascii_key_system
=
141 GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system
));
143 WebMediaPlayer::MediaKeyException e
= GenerateKeyRequestInternal(
144 frame
, ascii_key_system
, init_data
, init_data_length
);
145 ReportMediaKeyExceptionToUMA("generateKeyRequest", ascii_key_system
, e
);
149 WebMediaPlayer::MediaKeyException
150 EncryptedMediaPlayerSupport::GenerateKeyRequestInternal(
151 blink::WebLocalFrame
* frame
,
152 const std::string
& key_system
,
153 const unsigned char* init_data
,
154 unsigned init_data_length
) {
155 if (!PrefixedIsSupportedConcreteKeySystem(key_system
))
156 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported
;
158 // We do not support run-time switching between key systems for now.
159 if (current_key_system_
.empty()) {
160 if (!proxy_decryptor_
) {
161 proxy_decryptor_
.reset(new ProxyDecryptor(
163 BIND_TO_RENDER_LOOP(&EncryptedMediaPlayerSupport::OnKeyAdded
),
164 BIND_TO_RENDER_LOOP(&EncryptedMediaPlayerSupport::OnKeyError
),
165 BIND_TO_RENDER_LOOP(&EncryptedMediaPlayerSupport::OnKeyMessage
)));
168 GURL
security_origin(frame
->document().securityOrigin().toString());
170 if (!proxy_decryptor_
->InitializeCDM(cdm_factory_
, key_system
,
172 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported
;
175 if (proxy_decryptor_
&& !set_cdm_context_cb_
.is_null()) {
176 base::ResetAndReturn(&set_cdm_context_cb_
)
177 .Run(proxy_decryptor_
->GetCdmContext(),
178 base::Bind(&IgnoreCdmAttached
));
181 current_key_system_
= key_system
;
182 } else if (key_system
!= current_key_system_
) {
183 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState
;
186 EmeInitDataType init_data_type
= init_data_type_
;
187 if (init_data_type
== EmeInitDataType::UNKNOWN
)
188 init_data_type
= GuessInitDataType(init_data
, init_data_length
);
190 if (!proxy_decryptor_
->GenerateKeyRequest(init_data_type
, init_data
,
192 current_key_system_
.clear();
193 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported
;
196 return WebMediaPlayer::MediaKeyExceptionNoError
;
199 WebMediaPlayer::MediaKeyException
EncryptedMediaPlayerSupport::AddKey(
200 const WebString
& key_system
,
201 const unsigned char* key
,
203 const unsigned char* init_data
,
204 unsigned init_data_length
,
205 const WebString
& session_id
) {
206 DVLOG(1) << "addKey: " << base::string16(key_system
) << ": "
207 << std::string(reinterpret_cast<const char*>(key
),
208 static_cast<size_t>(key_length
)) << ", "
209 << std::string(reinterpret_cast<const char*>(init_data
),
210 static_cast<size_t>(init_data_length
)) << " ["
211 << base::string16(session_id
) << "]";
213 std::string ascii_key_system
=
214 GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system
));
215 std::string ascii_session_id
= ToASCIIOrEmpty(session_id
);
217 WebMediaPlayer::MediaKeyException e
= AddKeyInternal(ascii_key_system
,
223 ReportMediaKeyExceptionToUMA("addKey", ascii_key_system
, e
);
227 WebMediaPlayer::MediaKeyException
228 EncryptedMediaPlayerSupport::AddKeyInternal(
229 const std::string
& key_system
,
230 const unsigned char* key
,
232 const unsigned char* init_data
,
233 unsigned init_data_length
,
234 const std::string
& session_id
) {
236 DCHECK_GT(key_length
, 0u);
238 if (!PrefixedIsSupportedConcreteKeySystem(key_system
))
239 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported
;
241 if (current_key_system_
.empty() || key_system
!= current_key_system_
)
242 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState
;
244 proxy_decryptor_
->AddKey(
245 key
, key_length
, init_data
, init_data_length
, session_id
);
246 return WebMediaPlayer::MediaKeyExceptionNoError
;
249 WebMediaPlayer::MediaKeyException
250 EncryptedMediaPlayerSupport::CancelKeyRequest(
251 const WebString
& key_system
,
252 const WebString
& session_id
) {
253 DVLOG(1) << "cancelKeyRequest: " << base::string16(key_system
) << ": "
254 << " [" << base::string16(session_id
) << "]";
256 std::string ascii_key_system
=
257 GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system
));
258 std::string ascii_session_id
= ToASCIIOrEmpty(session_id
);
260 WebMediaPlayer::MediaKeyException e
=
261 CancelKeyRequestInternal(ascii_key_system
, ascii_session_id
);
262 ReportMediaKeyExceptionToUMA("cancelKeyRequest", ascii_key_system
, e
);
266 WebMediaPlayer::MediaKeyException
267 EncryptedMediaPlayerSupport::CancelKeyRequestInternal(
268 const std::string
& key_system
,
269 const std::string
& session_id
) {
270 if (!PrefixedIsSupportedConcreteKeySystem(key_system
))
271 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported
;
273 if (current_key_system_
.empty() || key_system
!= current_key_system_
)
274 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState
;
276 proxy_decryptor_
->CancelKeyRequest(session_id
);
277 return WebMediaPlayer::MediaKeyExceptionNoError
;
280 void EncryptedMediaPlayerSupport::SetInitDataType(
281 EmeInitDataType init_data_type
) {
282 DCHECK(init_data_type
!= EmeInitDataType::UNKNOWN
);
283 DLOG_IF(WARNING
, init_data_type_
!= EmeInitDataType::UNKNOWN
&&
284 init_data_type
!= init_data_type_
)
285 << "Mixed init data type not supported. The new type is ignored.";
286 if (init_data_type_
== EmeInitDataType::UNKNOWN
)
287 init_data_type_
= init_data_type
;
290 void EncryptedMediaPlayerSupport::OnPipelineDecryptError() {
291 EmeUMAHistogramCounts(current_key_system_
, "DecryptError", 1);
294 void EncryptedMediaPlayerSupport::OnKeyAdded(const std::string
& session_id
) {
295 EmeUMAHistogramCounts(current_key_system_
, "KeyAdded", 1);
297 WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_
)),
298 WebString::fromUTF8(session_id
));
301 void EncryptedMediaPlayerSupport::OnKeyError(const std::string
& session_id
,
302 MediaKeys::KeyError error_code
,
303 uint32 system_code
) {
304 EmeUMAHistogramEnumeration(current_key_system_
, "KeyError",
305 error_code
, MediaKeys::kMaxKeyError
);
307 uint16 short_system_code
= 0;
308 if (system_code
> std::numeric_limits
<uint16
>::max()) {
309 LOG(WARNING
) << "system_code exceeds unsigned short limit.";
310 short_system_code
= std::numeric_limits
<uint16
>::max();
312 short_system_code
= static_cast<uint16
>(system_code
);
316 WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_
)),
317 WebString::fromUTF8(session_id
),
318 static_cast<WebMediaPlayerClient::MediaKeyErrorCode
>(error_code
),
322 void EncryptedMediaPlayerSupport::OnKeyMessage(
323 const std::string
& session_id
,
324 const std::vector
<uint8
>& message
,
325 const GURL
& destination_url
) {
326 DCHECK(destination_url
.is_empty() || destination_url
.is_valid());
329 WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_
)),
330 WebString::fromUTF8(session_id
),
331 message
.empty() ? NULL
: &message
[0],
332 base::saturated_cast
<unsigned int>(message
.size()),