[Telemetry] Reenable unused-import lint check for telemetry.
[chromium-blink-merge.git] / media / blink / encrypted_media_player_support.cc
blobcc11a2e50413d8b6cc5c1427da0ba36a88b0677d
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"
7 #include <string>
9 #include "base/bind.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;
28 namespace media {
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)
43 : std::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'
50 // lifetime.
51 static void EmeUMAHistogramEnumeration(const std::string& key_system,
52 const std::string& method,
53 int sample,
54 int boundary_value) {
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,
63 int sample) {
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 {
72 kUnknownResultId,
73 kSuccess,
74 kKeySystemNotSupported,
75 kInvalidPlayerState,
76 kMaxMediaKeyException
79 static MediaKeyException MediaKeyExceptionForUMA(
80 WebMediaPlayer::MediaKeyException e) {
81 switch (e) {
82 case WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported:
83 return kKeySystemNotSupported;
84 case WebMediaPlayer::MediaKeyExceptionInvalidPlayerState:
85 return kInvalidPlayerState;
86 case WebMediaPlayer::MediaKeyExceptionNoError:
87 return kSuccess;
88 default:
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 CdmContextReadyCB& cdm_context_ready_cb)
120 : cdm_factory_(cdm_factory),
121 client_(client),
122 media_permission_(media_permission),
123 init_data_type_(EmeInitDataType::UNKNOWN),
124 cdm_context_ready_cb_(cdm_context_ready_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);
146 return 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 if (!proxy_decryptor_) {
159 DCHECK(current_key_system_.empty());
160 DCHECK(!cdm_context_ready_cb_.is_null());
161 proxy_decryptor_.reset(new ProxyDecryptor(
162 media_permission_,
163 BIND_TO_RENDER_LOOP(&EncryptedMediaPlayerSupport::OnKeyAdded),
164 BIND_TO_RENDER_LOOP(&EncryptedMediaPlayerSupport::OnKeyError),
165 BIND_TO_RENDER_LOOP(&EncryptedMediaPlayerSupport::OnKeyMessage)));
167 GURL security_origin(frame->document().securityOrigin().toString());
168 proxy_decryptor_->CreateCdm(cdm_factory_, key_system, security_origin,
169 cdm_context_ready_cb_);
170 current_key_system_ = key_system;
173 // We do not support run-time switching between key systems for now.
174 DCHECK(!current_key_system_.empty());
175 if (key_system != current_key_system_)
176 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
178 EmeInitDataType init_data_type = init_data_type_;
179 if (init_data_type == EmeInitDataType::UNKNOWN)
180 init_data_type = GuessInitDataType(init_data, init_data_length);
182 proxy_decryptor_->GenerateKeyRequest(init_data_type, init_data,
183 init_data_length);
185 return WebMediaPlayer::MediaKeyExceptionNoError;
188 WebMediaPlayer::MediaKeyException EncryptedMediaPlayerSupport::AddKey(
189 const WebString& key_system,
190 const unsigned char* key,
191 unsigned key_length,
192 const unsigned char* init_data,
193 unsigned init_data_length,
194 const WebString& session_id) {
195 DVLOG(1) << "addKey: " << base::string16(key_system) << ": "
196 << std::string(reinterpret_cast<const char*>(key),
197 static_cast<size_t>(key_length)) << ", "
198 << std::string(reinterpret_cast<const char*>(init_data),
199 static_cast<size_t>(init_data_length)) << " ["
200 << base::string16(session_id) << "]";
202 std::string ascii_key_system =
203 GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system));
204 std::string ascii_session_id = ToASCIIOrEmpty(session_id);
206 WebMediaPlayer::MediaKeyException e = AddKeyInternal(ascii_key_system,
207 key,
208 key_length,
209 init_data,
210 init_data_length,
211 ascii_session_id);
212 ReportMediaKeyExceptionToUMA("addKey", ascii_key_system, e);
213 return e;
216 WebMediaPlayer::MediaKeyException
217 EncryptedMediaPlayerSupport::AddKeyInternal(
218 const std::string& key_system,
219 const unsigned char* key,
220 unsigned key_length,
221 const unsigned char* init_data,
222 unsigned init_data_length,
223 const std::string& session_id) {
224 DCHECK(key);
225 DCHECK_GT(key_length, 0u);
227 if (!PrefixedIsSupportedConcreteKeySystem(key_system))
228 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
230 if (current_key_system_.empty() || key_system != current_key_system_)
231 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
233 proxy_decryptor_->AddKey(
234 key, key_length, init_data, init_data_length, session_id);
235 return WebMediaPlayer::MediaKeyExceptionNoError;
238 WebMediaPlayer::MediaKeyException
239 EncryptedMediaPlayerSupport::CancelKeyRequest(
240 const WebString& key_system,
241 const WebString& session_id) {
242 DVLOG(1) << "cancelKeyRequest: " << base::string16(key_system) << ": "
243 << " [" << base::string16(session_id) << "]";
245 std::string ascii_key_system =
246 GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system));
247 std::string ascii_session_id = ToASCIIOrEmpty(session_id);
249 WebMediaPlayer::MediaKeyException e =
250 CancelKeyRequestInternal(ascii_key_system, ascii_session_id);
251 ReportMediaKeyExceptionToUMA("cancelKeyRequest", ascii_key_system, e);
252 return e;
255 WebMediaPlayer::MediaKeyException
256 EncryptedMediaPlayerSupport::CancelKeyRequestInternal(
257 const std::string& key_system,
258 const std::string& session_id) {
259 if (!PrefixedIsSupportedConcreteKeySystem(key_system))
260 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
262 if (current_key_system_.empty() || key_system != current_key_system_)
263 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
265 proxy_decryptor_->CancelKeyRequest(session_id);
266 return WebMediaPlayer::MediaKeyExceptionNoError;
269 void EncryptedMediaPlayerSupport::SetInitDataType(
270 EmeInitDataType init_data_type) {
271 DCHECK(init_data_type != EmeInitDataType::UNKNOWN);
272 DLOG_IF(WARNING, init_data_type_ != EmeInitDataType::UNKNOWN &&
273 init_data_type != init_data_type_)
274 << "Mixed init data type not supported. The new type is ignored.";
275 if (init_data_type_ == EmeInitDataType::UNKNOWN)
276 init_data_type_ = init_data_type;
279 void EncryptedMediaPlayerSupport::OnPipelineDecryptError() {
280 EmeUMAHistogramCounts(current_key_system_, "DecryptError", 1);
283 void EncryptedMediaPlayerSupport::OnKeyAdded(const std::string& session_id) {
284 EmeUMAHistogramCounts(current_key_system_, "KeyAdded", 1);
285 client_->keyAdded(
286 WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)),
287 WebString::fromUTF8(session_id));
290 void EncryptedMediaPlayerSupport::OnKeyError(const std::string& session_id,
291 MediaKeys::KeyError error_code,
292 uint32 system_code) {
293 EmeUMAHistogramEnumeration(current_key_system_, "KeyError",
294 error_code, MediaKeys::kMaxKeyError);
296 uint16 short_system_code = 0;
297 if (system_code > std::numeric_limits<uint16>::max()) {
298 LOG(WARNING) << "system_code exceeds unsigned short limit.";
299 short_system_code = std::numeric_limits<uint16>::max();
300 } else {
301 short_system_code = static_cast<uint16>(system_code);
304 client_->keyError(
305 WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)),
306 WebString::fromUTF8(session_id),
307 static_cast<WebMediaPlayerClient::MediaKeyErrorCode>(error_code),
308 short_system_code);
311 void EncryptedMediaPlayerSupport::OnKeyMessage(
312 const std::string& session_id,
313 const std::vector<uint8>& message,
314 const GURL& destination_url) {
315 DCHECK(destination_url.is_empty() || destination_url.is_valid());
317 client_->keyMessage(
318 WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)),
319 WebString::fromUTF8(session_id),
320 message.empty() ? NULL : &message[0],
321 base::saturated_cast<unsigned int>(message.size()),
322 destination_url);
325 } // namespace media