This sets up API to release OutputSurface from LTHClient.
[chromium-blink-merge.git] / media / blink / encrypted_media_player_support.cc
blob086c469bb6ec934b9dd16e83400df9e59de71dbc
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"
8 #include "base/bind.h"
9 #include "base/callback_helpers.h"
10 #include "base/metrics/histogram.h"
11 #include "base/numerics/safe_conversions.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "media/base/bind_to_current_loop.h"
16 #include "media/base/key_systems.h"
17 #include "media/blink/webcontentdecryptionmodule_impl.h"
18 #include "third_party/WebKit/public/platform/WebContentDecryptionModule.h"
19 #include "third_party/WebKit/public/platform/WebMediaPlayerEncryptedMediaClient.h"
20 #include "third_party/WebKit/public/web/WebDocument.h"
21 #include "third_party/WebKit/public/web/WebLocalFrame.h"
23 using blink::WebMediaPlayer;
24 using blink::WebMediaPlayerEncryptedMediaClient;
25 using blink::WebString;
27 namespace media {
29 #define BIND_TO_RENDER_LOOP(function) \
30 (BindToCurrentLoop(base::Bind(function, AsWeakPtr())))
32 #define BIND_TO_RENDER_LOOP1(function, arg1) \
33 (BindToCurrentLoop(base::Bind(function, AsWeakPtr(), arg1)))
35 // Prefix for histograms related to Encrypted Media Extensions.
36 static const char* kMediaEme = "Media.EME.";
38 // Convert a WebString to ASCII, falling back on an empty string in the case
39 // of a non-ASCII string.
40 static std::string ToASCIIOrEmpty(const WebString& string) {
41 return base::IsStringASCII(string)
42 ? base::UTF16ToASCII(base::StringPiece16(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 #if defined(USE_PROPRIETARY_CODECS)
109 // Most WebM files use KeyId of 16 bytes. CENC init data is always >16 bytes.
110 if (init_data_length > 16)
111 return EmeInitDataType::CENC;
112 #endif
114 return EmeInitDataType::WEBM;
117 EncryptedMediaPlayerSupport::EncryptedMediaPlayerSupport(
118 CdmFactory* cdm_factory,
119 WebMediaPlayerEncryptedMediaClient* client,
120 MediaPermission* media_permission,
121 const CdmContextReadyCB& cdm_context_ready_cb)
122 : cdm_factory_(cdm_factory),
123 client_(client),
124 media_permission_(media_permission),
125 init_data_type_(EmeInitDataType::UNKNOWN),
126 cdm_context_ready_cb_(cdm_context_ready_cb) {
129 EncryptedMediaPlayerSupport::~EncryptedMediaPlayerSupport() {
132 WebMediaPlayer::MediaKeyException
133 EncryptedMediaPlayerSupport::GenerateKeyRequest(
134 blink::WebLocalFrame* frame,
135 const WebString& key_system,
136 const unsigned char* init_data,
137 unsigned init_data_length) {
138 DVLOG(1) << "generateKeyRequest: " << base::string16(key_system) << ": "
139 << std::string(reinterpret_cast<const char*>(init_data),
140 static_cast<size_t>(init_data_length));
142 std::string ascii_key_system =
143 GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system));
145 WebMediaPlayer::MediaKeyException e = GenerateKeyRequestInternal(
146 frame, ascii_key_system, init_data, init_data_length);
147 ReportMediaKeyExceptionToUMA("generateKeyRequest", ascii_key_system, e);
148 return e;
151 WebMediaPlayer::MediaKeyException
152 EncryptedMediaPlayerSupport::GenerateKeyRequestInternal(
153 blink::WebLocalFrame* frame,
154 const std::string& key_system,
155 const unsigned char* init_data,
156 unsigned init_data_length) {
157 if (!PrefixedIsSupportedConcreteKeySystem(key_system))
158 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
160 // |use_hw_secure_codecs| is only supported on Android, and Android (WMPA)
161 // does not use EncryptedMediaPlayerSupport.
162 bool use_hw_secure_codecs = false;
164 if (!proxy_decryptor_) {
165 DCHECK(current_key_system_.empty());
166 DCHECK(!cdm_context_ready_cb_.is_null());
167 proxy_decryptor_.reset(new ProxyDecryptor(
168 media_permission_, use_hw_secure_codecs,
169 BIND_TO_RENDER_LOOP(&EncryptedMediaPlayerSupport::OnKeyAdded),
170 BIND_TO_RENDER_LOOP(&EncryptedMediaPlayerSupport::OnKeyError),
171 BIND_TO_RENDER_LOOP(&EncryptedMediaPlayerSupport::OnKeyMessage)));
173 GURL security_origin(frame->document().securityOrigin().toString());
174 proxy_decryptor_->CreateCdm(cdm_factory_, key_system, security_origin,
175 cdm_context_ready_cb_);
176 current_key_system_ = key_system;
179 // We do not support run-time switching between key systems for now.
180 DCHECK(!current_key_system_.empty());
181 if (key_system != current_key_system_)
182 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
184 EmeInitDataType init_data_type = init_data_type_;
185 if (init_data_type == EmeInitDataType::UNKNOWN)
186 init_data_type = GuessInitDataType(init_data, init_data_length);
188 proxy_decryptor_->GenerateKeyRequest(init_data_type, init_data,
189 init_data_length);
191 return WebMediaPlayer::MediaKeyExceptionNoError;
194 WebMediaPlayer::MediaKeyException EncryptedMediaPlayerSupport::AddKey(
195 const WebString& key_system,
196 const unsigned char* key,
197 unsigned key_length,
198 const unsigned char* init_data,
199 unsigned init_data_length,
200 const WebString& session_id) {
201 DVLOG(1) << "addKey: " << base::string16(key_system) << ": "
202 << std::string(reinterpret_cast<const char*>(key),
203 static_cast<size_t>(key_length)) << ", "
204 << std::string(reinterpret_cast<const char*>(init_data),
205 static_cast<size_t>(init_data_length)) << " ["
206 << base::string16(session_id) << "]";
208 std::string ascii_key_system =
209 GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system));
210 std::string ascii_session_id = ToASCIIOrEmpty(session_id);
212 WebMediaPlayer::MediaKeyException e = AddKeyInternal(ascii_key_system,
213 key,
214 key_length,
215 init_data,
216 init_data_length,
217 ascii_session_id);
218 ReportMediaKeyExceptionToUMA("addKey", ascii_key_system, e);
219 return e;
222 WebMediaPlayer::MediaKeyException
223 EncryptedMediaPlayerSupport::AddKeyInternal(
224 const std::string& key_system,
225 const unsigned char* key,
226 unsigned key_length,
227 const unsigned char* init_data,
228 unsigned init_data_length,
229 const std::string& session_id) {
230 DCHECK(key);
231 DCHECK_GT(key_length, 0u);
233 if (!PrefixedIsSupportedConcreteKeySystem(key_system))
234 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
236 if (current_key_system_.empty() || key_system != current_key_system_)
237 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
239 proxy_decryptor_->AddKey(
240 key, key_length, init_data, init_data_length, session_id);
241 return WebMediaPlayer::MediaKeyExceptionNoError;
244 WebMediaPlayer::MediaKeyException
245 EncryptedMediaPlayerSupport::CancelKeyRequest(
246 const WebString& key_system,
247 const WebString& session_id) {
248 DVLOG(1) << "cancelKeyRequest: " << base::string16(key_system) << ": "
249 << " [" << base::string16(session_id) << "]";
251 std::string ascii_key_system =
252 GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system));
253 std::string ascii_session_id = ToASCIIOrEmpty(session_id);
255 WebMediaPlayer::MediaKeyException e =
256 CancelKeyRequestInternal(ascii_key_system, ascii_session_id);
257 ReportMediaKeyExceptionToUMA("cancelKeyRequest", ascii_key_system, e);
258 return e;
261 WebMediaPlayer::MediaKeyException
262 EncryptedMediaPlayerSupport::CancelKeyRequestInternal(
263 const std::string& key_system,
264 const std::string& session_id) {
265 if (!PrefixedIsSupportedConcreteKeySystem(key_system))
266 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
268 if (current_key_system_.empty() || key_system != current_key_system_)
269 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
271 proxy_decryptor_->CancelKeyRequest(session_id);
272 return WebMediaPlayer::MediaKeyExceptionNoError;
275 void EncryptedMediaPlayerSupport::SetInitDataType(
276 EmeInitDataType init_data_type) {
277 DCHECK(init_data_type != EmeInitDataType::UNKNOWN);
278 DLOG_IF(WARNING, init_data_type_ != EmeInitDataType::UNKNOWN &&
279 init_data_type != init_data_type_)
280 << "Mixed init data type not supported. The new type is ignored.";
281 if (init_data_type_ == EmeInitDataType::UNKNOWN)
282 init_data_type_ = init_data_type;
285 void EncryptedMediaPlayerSupport::OnKeyAdded(const std::string& session_id) {
286 EmeUMAHistogramCounts(current_key_system_, "KeyAdded", 1);
287 client_->keyAdded(
288 WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)),
289 WebString::fromUTF8(session_id));
292 void EncryptedMediaPlayerSupport::OnKeyError(const std::string& session_id,
293 MediaKeys::KeyError error_code,
294 uint32 system_code) {
295 EmeUMAHistogramEnumeration(current_key_system_, "KeyError",
296 error_code, MediaKeys::kMaxKeyError);
298 uint16 short_system_code = 0;
299 if (system_code > std::numeric_limits<uint16>::max()) {
300 LOG(WARNING) << "system_code exceeds unsigned short limit.";
301 short_system_code = std::numeric_limits<uint16>::max();
302 } else {
303 short_system_code = static_cast<uint16>(system_code);
306 client_->keyError(
307 WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)),
308 WebString::fromUTF8(session_id),
309 static_cast<WebMediaPlayerEncryptedMediaClient::MediaKeyErrorCode>(
310 error_code),
311 short_system_code);
314 void EncryptedMediaPlayerSupport::OnKeyMessage(
315 const std::string& session_id,
316 const std::vector<uint8>& message,
317 const GURL& destination_url) {
318 DCHECK(destination_url.is_empty() || destination_url.is_valid());
320 client_->keyMessage(
321 WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)),
322 WebString::fromUTF8(session_id),
323 message.empty() ? NULL : &message[0],
324 base::saturated_cast<unsigned int>(message.size()),
325 destination_url);
328 } // namespace media