Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(59)

Side by Side Diff: content/renderer/media/crypto/proxy_decryptor.cc

Issue 265993002: Add Promises for EME (Chromium side) (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/renderer/media/crypto/proxy_decryptor.h" 5 #include "content/renderer/media/crypto/proxy_decryptor.h"
6 6
7 #include <cstring> 7 #include <cstring>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/callback_helpers.h" 10 #include "base/callback_helpers.h"
11 #include "base/logging.h" 11 #include "base/logging.h"
12 #include "base/strings/string_util.h" 12 #include "base/strings/string_util.h"
13 #include "content/renderer/media/crypto/content_decryption_module_factory.h" 13 #include "content/renderer/media/crypto/content_decryption_module_factory.h"
14 #include "media/base/media_keys_session_promise.h"
14 #include "media/cdm/json_web_key.h" 15 #include "media/cdm/json_web_key.h"
15 #include "media/cdm/key_system_names.h" 16 #include "media/cdm/key_system_names.h"
16 17
17 #if defined(ENABLE_PEPPER_CDMS) 18 #if defined(ENABLE_PEPPER_CDMS)
18 #include "content/renderer/media/crypto/pepper_cdm_wrapper.h" 19 #include "content/renderer/media/crypto/pepper_cdm_wrapper.h"
19 #endif // defined(ENABLE_PEPPER_CDMS) 20 #endif // defined(ENABLE_PEPPER_CDMS)
20 21
21 #if defined(OS_ANDROID) 22 #if defined(OS_ANDROID)
22 #include "content/renderer/media/android/renderer_media_player_manager.h" 23 #include "content/renderer/media/android/renderer_media_player_manager.h"
23 #endif // defined(OS_ANDROID) 24 #endif // defined(OS_ANDROID)
24 25
25 namespace content { 26 namespace content {
26 27
27 // Since these reference IDs may conflict with the ones generated in
28 // WebContentDecryptionModuleSessionImpl for the short time both paths are
29 // active, start with 100000 and generate the IDs from there.
30 // TODO(jrummell): Only allow one path http://crbug.com/306680.
31 uint32 ProxyDecryptor::next_session_id_ = 100000;
32
33 const uint32 kInvalidSessionId = 0;
34
35 // Special system code to signal a closed persistent session in a SessionError() 28 // Special system code to signal a closed persistent session in a SessionError()
36 // call. This is needed because there is no SessionClosed() call in the prefixed 29 // call. This is needed because there is no SessionClosed() call in the prefixed
37 // EME API. 30 // EME API.
38 const int kSessionClosedSystemCode = 29127; 31 const int kSessionClosedSystemCode = 29127;
39 32
40 ProxyDecryptor::ProxyDecryptor( 33 ProxyDecryptor::ProxyDecryptor(
41 #if defined(ENABLE_PEPPER_CDMS) 34 #if defined(ENABLE_PEPPER_CDMS)
42 const CreatePepperCdmCB& create_pepper_cdm_cb, 35 const CreatePepperCdmCB& create_pepper_cdm_cb,
43 #elif defined(OS_ANDROID) 36 #elif defined(OS_ANDROID)
44 RendererMediaPlayerManager* manager, 37 RendererMediaPlayerManager* manager,
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
98 // Returns true if |data| is prefixed with |header| and has data after the 91 // Returns true if |data| is prefixed with |header| and has data after the
99 // |header|. 92 // |header|.
100 bool HasHeader(const uint8* data, int data_length, const std::string& header) { 93 bool HasHeader(const uint8* data, int data_length, const std::string& header) {
101 return static_cast<size_t>(data_length) > header.size() && 94 return static_cast<size_t>(data_length) > header.size() &&
102 std::equal(data, data + header.size(), header.begin()); 95 std::equal(data, data + header.size(), header.begin());
103 } 96 }
104 97
105 bool ProxyDecryptor::GenerateKeyRequest(const std::string& content_type, 98 bool ProxyDecryptor::GenerateKeyRequest(const std::string& content_type,
106 const uint8* init_data, 99 const uint8* init_data,
107 int init_data_length) { 100 int init_data_length) {
108 // Use a unique reference id for this request.
109 uint32 session_id = next_session_id_++;
110
111 const char kPrefixedApiPersistentSessionHeader[] = "PERSISTENT|"; 101 const char kPrefixedApiPersistentSessionHeader[] = "PERSISTENT|";
112 const char kPrefixedApiLoadSessionHeader[] = "LOAD_SESSION|"; 102 const char kPrefixedApiLoadSessionHeader[] = "LOAD_SESSION|";
103 DVLOG(1) << "GenerateKeyRequest()";
113 104
114 if (HasHeader(init_data, init_data_length, kPrefixedApiLoadSessionHeader)) { 105 if (HasHeader(init_data, init_data_length, kPrefixedApiLoadSessionHeader)) {
115 persistent_sessions_.insert(session_id); 106 scoped_ptr<media::MediaKeysSessionPromise> promise(
107 new media::MediaKeysSessionPromise(
108 media::PromiseResolvedCB(),
109 base::Bind(&ProxyDecryptor::SetSessionId,
110 weak_ptr_factory_.GetWeakPtr(),
111 true), // Persistent session.
112 base::Bind(&ProxyDecryptor::OnSessionError,
113 weak_ptr_factory_.GetWeakPtr(),
114 std::string()))); // No session id until created.
116 media_keys_->LoadSession( 115 media_keys_->LoadSession(
117 session_id,
118 std::string(reinterpret_cast<const char*>( 116 std::string(reinterpret_cast<const char*>(
119 init_data + strlen(kPrefixedApiLoadSessionHeader)), 117 init_data + strlen(kPrefixedApiLoadSessionHeader)),
120 init_data_length - strlen(kPrefixedApiLoadSessionHeader))); 118 init_data_length - strlen(kPrefixedApiLoadSessionHeader)),
119 promise.Pass());
121 return true; 120 return true;
122 } 121 }
123 122
124 if (HasHeader( 123 bool persistent = HasHeader(
125 init_data, init_data_length, kPrefixedApiPersistentSessionHeader)) 124 init_data, init_data_length, kPrefixedApiPersistentSessionHeader);
126 persistent_sessions_.insert(session_id); 125 media::MediaKeys::SessionType session_type =
126 persistent ? media::MediaKeys::SessionType::kPersistent
xhwang 2014/05/05 20:46:42 The "SessionType::" part isn't needed.
jrummell 2014/05/08 23:37:45 Done.
127 : media::MediaKeys::SessionType::kTemporary;
127 128
128 return media_keys_->CreateSession( 129 scoped_ptr<media::MediaKeysSessionPromise> promise(
129 session_id, content_type, init_data, init_data_length); 130 new media::MediaKeysSessionPromise(
131 media::PromiseResolvedCB(),
132 base::Bind(&ProxyDecryptor::SetSessionId,
133 weak_ptr_factory_.GetWeakPtr(),
134 persistent),
135 base::Bind(&ProxyDecryptor::OnSessionError,
136 weak_ptr_factory_.GetWeakPtr(),
137 std::string())));
138 media_keys_->CreateSession(
139 content_type, init_data, init_data_length, session_type, promise.Pass());
140 return true;
130 } 141 }
131 142
132 void ProxyDecryptor::AddKey(const uint8* key, 143 void ProxyDecryptor::AddKey(const uint8* key,
133 int key_length, 144 int key_length,
134 const uint8* init_data, 145 const uint8* init_data,
135 int init_data_length, 146 int init_data_length,
136 const std::string& web_session_id) { 147 const std::string& web_session_id) {
137 DVLOG(1) << "AddKey()"; 148 DVLOG(1) << "AddKey()";
138 149
139 // WebMediaPlayerImpl ensures GenerateKeyRequest() has been called. 150 // If |web_session_id| is null, then use the single reference id.
140 uint32 session_id = LookupSessionId(web_session_id); 151 std::string session_id(web_session_id);
141 if (session_id == kInvalidSessionId) { 152 if (session_id.empty()) {
142 // Session hasn't been referenced before, so it is an error. 153 if (active_sessions_.size() == 1) {
143 // Note that the specification says "If sessionId is not null and is 154 std::set<std::string>::iterator it = active_sessions_.begin();
144 // unrecognized, throw an INVALID_ACCESS_ERR." However, for backwards 155 session_id = *it;
145 // compatibility the error is not thrown, but rather reported as a 156 } else {
146 // KeyError. 157 OnSessionError(
147 key_error_cb_.Run(std::string(), media::MediaKeys::kUnknownError, 0); 158 std::string(), "NotSupportedError", 0, "SessionId not specified.");
148 return; 159 return;
160 }
149 } 161 }
150 162
163 scoped_ptr<media::MediaKeysSessionPromise> promise(
164 new media::MediaKeysSessionPromise(
165 base::Bind(&ProxyDecryptor::OnSessionReady,
166 weak_ptr_factory_.GetWeakPtr(),
167 web_session_id),
168 media::PromiseResolvedWithSessionCB(),
169 base::Bind(&ProxyDecryptor::OnSessionError,
170 weak_ptr_factory_.GetWeakPtr(),
171 web_session_id)));
172
151 // EME WD spec only supports a single array passed to the CDM. For 173 // EME WD spec only supports a single array passed to the CDM. For
152 // Clear Key using v0.1b, both arrays are used (|init_data| is key_id). 174 // Clear Key using v0.1b, both arrays are used (|init_data| is key_id).
153 // Since the EME WD spec supports the key as a JSON Web Key, 175 // Since the EME WD spec supports the key as a JSON Web Key,
154 // convert the 2 arrays to a JWK and pass it as the single array. 176 // convert the 2 arrays to a JWK and pass it as the single array.
155 if (is_clear_key_) { 177 if (is_clear_key_) {
156 // Decryptor doesn't support empty key ID (see http://crbug.com/123265). 178 // Decryptor doesn't support empty key ID (see http://crbug.com/123265).
157 // So ensure a non-empty value is passed. 179 // So ensure a non-empty value is passed.
158 if (!init_data) { 180 if (!init_data) {
159 static const uint8 kDummyInitData[1] = {0}; 181 static const uint8 kDummyInitData[1] = {0};
160 init_data = kDummyInitData; 182 init_data = kDummyInitData;
161 init_data_length = arraysize(kDummyInitData); 183 init_data_length = arraysize(kDummyInitData);
162 } 184 }
163 185
164 std::string jwk = 186 std::string jwk =
165 media::GenerateJWKSet(key, key_length, init_data, init_data_length); 187 media::GenerateJWKSet(key, key_length, init_data, init_data_length);
166 DCHECK(!jwk.empty()); 188 DCHECK(!jwk.empty());
167 media_keys_->UpdateSession( 189 media_keys_->UpdateSession(session_id,
168 session_id, reinterpret_cast<const uint8*>(jwk.data()), jwk.size()); 190 reinterpret_cast<const uint8*>(jwk.data()),
191 jwk.size(),
192 promise.Pass());
169 return; 193 return;
170 } 194 }
171 195
172 media_keys_->UpdateSession(session_id, key, key_length); 196 media_keys_->UpdateSession(session_id, key, key_length, promise.Pass());
173 } 197 }
174 198
175 void ProxyDecryptor::CancelKeyRequest(const std::string& session_id) { 199 void ProxyDecryptor::CancelKeyRequest(const std::string& web_session_id) {
176 DVLOG(1) << "CancelKeyRequest()"; 200 DVLOG(1) << "CancelKeyRequest()";
177 201
178 // WebMediaPlayerImpl ensures GenerateKeyRequest() has been called. 202 scoped_ptr<media::MediaKeysSessionPromise> promise(
179 uint32 session_reference_id = LookupSessionId(session_id); 203 new media::MediaKeysSessionPromise(
180 if (session_reference_id == kInvalidSessionId) { 204 base::Bind(&ProxyDecryptor::OnSessionClosed,
181 // Session hasn't been created, so it is an error. 205 weak_ptr_factory_.GetWeakPtr(),
182 key_error_cb_.Run( 206 web_session_id),
183 std::string(), media::MediaKeys::kUnknownError, 0); 207 media::PromiseResolvedWithSessionCB(),
184 } 208 base::Bind(&ProxyDecryptor::OnSessionError,
185 else { 209 weak_ptr_factory_.GetWeakPtr(),
186 media_keys_->ReleaseSession(session_reference_id); 210 web_session_id)));
187 } 211 media_keys_->ReleaseSession(web_session_id, promise.Pass());
188 } 212 }
189 213
190 scoped_ptr<media::MediaKeys> ProxyDecryptor::CreateMediaKeys( 214 scoped_ptr<media::MediaKeys> ProxyDecryptor::CreateMediaKeys(
191 const std::string& key_system, 215 const std::string& key_system,
192 const GURL& security_origin) { 216 const GURL& security_origin) {
193 return ContentDecryptionModuleFactory::Create( 217 return ContentDecryptionModuleFactory::Create(
194 key_system, 218 key_system,
195 security_origin, 219 security_origin,
196 #if defined(ENABLE_PEPPER_CDMS) 220 #if defined(ENABLE_PEPPER_CDMS)
197 create_pepper_cdm_cb_, 221 create_pepper_cdm_cb_,
198 #elif defined(OS_ANDROID) 222 #elif defined(OS_ANDROID)
199 manager_, 223 manager_,
200 &cdm_id_, 224 &cdm_id_,
201 #endif // defined(ENABLE_PEPPER_CDMS) 225 #endif // defined(ENABLE_PEPPER_CDMS)
202 base::Bind(&ProxyDecryptor::OnSessionCreated,
203 weak_ptr_factory_.GetWeakPtr()),
204 base::Bind(&ProxyDecryptor::OnSessionMessage, 226 base::Bind(&ProxyDecryptor::OnSessionMessage,
205 weak_ptr_factory_.GetWeakPtr()), 227 weak_ptr_factory_.GetWeakPtr()),
206 base::Bind(&ProxyDecryptor::OnSessionReady, 228 base::Bind(&ProxyDecryptor::OnSessionReady,
207 weak_ptr_factory_.GetWeakPtr()), 229 weak_ptr_factory_.GetWeakPtr()),
208 base::Bind(&ProxyDecryptor::OnSessionClosed, 230 base::Bind(&ProxyDecryptor::OnSessionClosed,
209 weak_ptr_factory_.GetWeakPtr()), 231 weak_ptr_factory_.GetWeakPtr()),
210 base::Bind(&ProxyDecryptor::OnSessionError, 232 base::Bind(&ProxyDecryptor::OnSessionError,
211 weak_ptr_factory_.GetWeakPtr())); 233 weak_ptr_factory_.GetWeakPtr()));
212 } 234 }
213 235
214 void ProxyDecryptor::OnSessionCreated(uint32 session_id, 236 void ProxyDecryptor::OnSessionMessage(const std::string& web_session_id,
215 const std::string& web_session_id) { 237 const std::vector<uint8>& message,
216 // Due to heartbeat messages, OnSessionCreated() can get called multiple 238 const std::string& destination_url) {
217 // times. 239 key_message_cb_.Run(web_session_id, message, destination_url);
218 SessionIdMap::iterator it = sessions_.find(session_id);
219 DCHECK(it == sessions_.end() || it->second == web_session_id);
220 if (it == sessions_.end())
221 sessions_[session_id] = web_session_id;
222 } 240 }
223 241
224 void ProxyDecryptor::OnSessionMessage(uint32 session_id, 242 void ProxyDecryptor::OnSessionReady(const std::string& web_session_id) {
225 const std::vector<uint8>& message, 243 key_added_cb_.Run(web_session_id);
226 const std::string& destination_url) {
227 // Assumes that OnSessionCreated() has been called before this.
228 key_message_cb_.Run(LookupWebSessionId(session_id), message, destination_url);
229 } 244 }
230 245
231 void ProxyDecryptor::OnSessionReady(uint32 session_id) { 246 void ProxyDecryptor::OnSessionClosed(const std::string& web_session_id) {
232 // Assumes that OnSessionCreated() has been called before this. 247 std::set<std::string>::iterator it =
233 key_added_cb_.Run(LookupWebSessionId(session_id)); 248 persistent_sessions_.find(web_session_id);
249 active_sessions_.erase(web_session_id);
250 if (it != persistent_sessions_.end()) {
251 persistent_sessions_.erase(it);
252 OnSessionError(web_session_id,
253 "NotSupportedError",
254 kSessionClosedSystemCode,
255 "Do not close persistent sessions.");
256 }
234 } 257 }
235 258
236 void ProxyDecryptor::OnSessionClosed(uint32 session_id) { 259 void ProxyDecryptor::OnSessionError(const std::string& web_session_id,
237 std::set<uint32>::iterator it = persistent_sessions_.find(session_id); 260 const std::string& error_name,
238 if (it != persistent_sessions_.end()) { 261 uint32 system_code,
239 persistent_sessions_.erase(it); 262 const std::string& error_message) {
240 OnSessionError( 263 // Convert |error_name| back to MediaKeys::KeyError if possible. Prefixed
241 session_id, media::MediaKeys::kUnknownError, kSessionClosedSystemCode); 264 // EME has different error message, so all the specific error events will
265 // get lost.
266 if (error_name == "CDM4ClientError") {
267 key_error_cb_.Run(
268 web_session_id, media::MediaKeys::KeyError::kClientError, system_code);
269 } else if (error_name == "CDM4OutputError") {
270 key_error_cb_.Run(
271 web_session_id, media::MediaKeys::KeyError::kOutputError, system_code);
272 } else {
273 // This will include all other CDM4 errors and any error generated
274 // by CDM5 or later.
275 key_error_cb_.Run(
276 web_session_id, media::MediaKeys::KeyError::kUnknownError, system_code);
242 } 277 }
243
244 sessions_.erase(session_id);
245 } 278 }
246 279
247 void ProxyDecryptor::OnSessionError(uint32 session_id, 280 void ProxyDecryptor::SetSessionId(bool persistent,
248 media::MediaKeys::KeyError error_code, 281 const std::string& web_session_id) {
249 uint32 system_code) { 282 active_sessions_.insert(web_session_id);
250 // Assumes that OnSessionCreated() has been called before this. 283 if (persistent) {
251 key_error_cb_.Run(LookupWebSessionId(session_id), error_code, system_code); 284 persistent_sessions_.insert(web_session_id);
252 }
253
254 uint32 ProxyDecryptor::LookupSessionId(const std::string& session_id) const {
255 for (SessionIdMap::const_iterator it = sessions_.begin();
256 it != sessions_.end();
257 ++it) {
258 if (it->second == session_id)
259 return it->first;
260 } 285 }
261
262 // If |session_id| is null, then use the single reference id.
263 if (session_id.empty() && sessions_.size() == 1)
264 return sessions_.begin()->first;
265
266 return kInvalidSessionId;
267 }
268
269 const std::string& ProxyDecryptor::LookupWebSessionId(uint32 session_id) const {
270 DCHECK_NE(session_id, kInvalidSessionId);
271
272 // Session may not exist if error happens during GenerateKeyRequest().
273 SessionIdMap::const_iterator it = sessions_.find(session_id);
274 return (it != sessions_.end()) ? it->second : base::EmptyString();
275 } 286 }
276 287
277 } // namespace content 288 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698