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

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: rebase 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/cdm_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()";
xhwang 2014/05/23 06:01:23 nit: usually we put this log in the first line of
jrummell 2014/05/29 00:54:40 Done.
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::NewSessionCdmPromise> promise(
107 new media::NewSessionCdmPromise(
108 base::Bind(&ProxyDecryptor::SetSessionId,
109 weak_ptr_factory_.GetWeakPtr(),
110 true), // Persistent session.
111 base::Bind(&ProxyDecryptor::OnSessionError,
112 weak_ptr_factory_.GetWeakPtr(),
113 std::string()))); // No session id until created.
116 media_keys_->LoadSession( 114 media_keys_->LoadSession(
117 session_id,
118 std::string(reinterpret_cast<const char*>( 115 std::string(reinterpret_cast<const char*>(
119 init_data + strlen(kPrefixedApiLoadSessionHeader)), 116 init_data + strlen(kPrefixedApiLoadSessionHeader)),
120 init_data_length - strlen(kPrefixedApiLoadSessionHeader))); 117 init_data_length - strlen(kPrefixedApiLoadSessionHeader)),
118 promise.Pass());
121 return true; 119 return true;
122 } 120 }
123 121
124 if (HasHeader( 122 bool persistent = HasHeader(
125 init_data, init_data_length, kPrefixedApiPersistentSessionHeader)) 123 init_data, init_data_length, kPrefixedApiPersistentSessionHeader);
126 persistent_sessions_.insert(session_id); 124 media::MediaKeys::SessionType session_type =
125 persistent ? media::MediaKeys::SESSION_TYPE_PERSISTENT
126 : media::MediaKeys::SESSION_TYPE_TEMPORARY;
127 127
128 return media_keys_->CreateSession( 128 scoped_ptr<media::NewSessionCdmPromise> promise(
129 session_id, content_type, init_data, init_data_length); 129 new media::NewSessionCdmPromise(
130 base::Bind(&ProxyDecryptor::SetSessionId,
131 weak_ptr_factory_.GetWeakPtr(),
132 persistent),
133 base::Bind(&ProxyDecryptor::OnSessionError,
134 weak_ptr_factory_.GetWeakPtr(),
135 std::string())));
xhwang 2014/05/23 06:01:23 Can we avoid creating this promise in two places?
jrummell 2014/05/29 00:54:40 Done.
136 media_keys_->CreateSession(
137 content_type, init_data, init_data_length, session_type, promise.Pass());
138 return true;
130 } 139 }
131 140
132 void ProxyDecryptor::AddKey(const uint8* key, 141 void ProxyDecryptor::AddKey(const uint8* key,
133 int key_length, 142 int key_length,
134 const uint8* init_data, 143 const uint8* init_data,
135 int init_data_length, 144 int init_data_length,
136 const std::string& web_session_id) { 145 const std::string& web_session_id) {
137 DVLOG(1) << "AddKey()"; 146 DVLOG(1) << "AddKey()";
138 147
139 // WebMediaPlayerImpl ensures GenerateKeyRequest() has been called. 148 // If |web_session_id| is null, then use the single reference id.
xhwang 2014/05/23 06:01:23 Can you elaborate more in which case this could ha
jrummell 2014/05/29 00:54:40 Done.
140 uint32 session_id = LookupSessionId(web_session_id); 149 std::string session_id(web_session_id);
141 if (session_id == kInvalidSessionId) { 150 if (session_id.empty()) {
142 // Session hasn't been referenced before, so it is an error. 151 if (active_sessions_.size() == 1) {
143 // Note that the specification says "If sessionId is not null and is 152 std::set<std::string>::iterator it = active_sessions_.begin();
144 // unrecognized, throw an INVALID_ACCESS_ERR." However, for backwards 153 session_id = *it;
145 // compatibility the error is not thrown, but rather reported as a 154 } else {
146 // KeyError. 155 OnSessionError(std::string(),
147 key_error_cb_.Run(std::string(), media::MediaKeys::kUnknownError, 0); 156 media::MediaKeys::EXCEPTION_NOT_SUPPORTED_ERROR,
148 return; 157 0,
158 "SessionId not specified.");
159 return;
160 }
149 } 161 }
150 162
163 scoped_ptr<media::SimpleCdmPromise> promise(
164 new media::SimpleCdmPromise(base::Bind(&ProxyDecryptor::OnSessionReady,
165 weak_ptr_factory_.GetWeakPtr(),
166 web_session_id),
167 base::Bind(&ProxyDecryptor::OnSessionError,
168 weak_ptr_factory_.GetWeakPtr(),
169 web_session_id)));
170
151 // EME WD spec only supports a single array passed to the CDM. For 171 // 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). 172 // 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, 173 // 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. 174 // convert the 2 arrays to a JWK and pass it as the single array.
155 if (is_clear_key_) { 175 if (is_clear_key_) {
156 // Decryptor doesn't support empty key ID (see http://crbug.com/123265). 176 // Decryptor doesn't support empty key ID (see http://crbug.com/123265).
157 // So ensure a non-empty value is passed. 177 // So ensure a non-empty value is passed.
158 if (!init_data) { 178 if (!init_data) {
159 static const uint8 kDummyInitData[1] = {0}; 179 static const uint8 kDummyInitData[1] = {0};
160 init_data = kDummyInitData; 180 init_data = kDummyInitData;
161 init_data_length = arraysize(kDummyInitData); 181 init_data_length = arraysize(kDummyInitData);
162 } 182 }
163 183
164 std::string jwk = 184 std::string jwk =
165 media::GenerateJWKSet(key, key_length, init_data, init_data_length); 185 media::GenerateJWKSet(key, key_length, init_data, init_data_length);
166 DCHECK(!jwk.empty()); 186 DCHECK(!jwk.empty());
167 media_keys_->UpdateSession( 187 media_keys_->UpdateSession(session_id,
168 session_id, reinterpret_cast<const uint8*>(jwk.data()), jwk.size()); 188 reinterpret_cast<const uint8*>(jwk.data()),
189 jwk.size(),
190 promise.Pass());
169 return; 191 return;
170 } 192 }
171 193
172 media_keys_->UpdateSession(session_id, key, key_length); 194 media_keys_->UpdateSession(session_id, key, key_length, promise.Pass());
173 } 195 }
174 196
175 void ProxyDecryptor::CancelKeyRequest(const std::string& session_id) { 197 void ProxyDecryptor::CancelKeyRequest(const std::string& web_session_id) {
176 DVLOG(1) << "CancelKeyRequest()"; 198 DVLOG(1) << "CancelKeyRequest()";
177 199
178 // WebMediaPlayerImpl ensures GenerateKeyRequest() has been called. 200 scoped_ptr<media::SimpleCdmPromise> promise(
179 uint32 session_reference_id = LookupSessionId(session_id); 201 new media::SimpleCdmPromise(base::Bind(&ProxyDecryptor::OnSessionClosed,
180 if (session_reference_id == kInvalidSessionId) { 202 weak_ptr_factory_.GetWeakPtr(),
181 // Session hasn't been created, so it is an error. 203 web_session_id),
182 key_error_cb_.Run( 204 base::Bind(&ProxyDecryptor::OnSessionError,
183 std::string(), media::MediaKeys::kUnknownError, 0); 205 weak_ptr_factory_.GetWeakPtr(),
184 } 206 web_session_id)));
185 else { 207 media_keys_->ReleaseSession(web_session_id, promise.Pass());
186 media_keys_->ReleaseSession(session_reference_id);
187 }
188 } 208 }
189 209
190 scoped_ptr<media::MediaKeys> ProxyDecryptor::CreateMediaKeys( 210 scoped_ptr<media::MediaKeys> ProxyDecryptor::CreateMediaKeys(
191 const std::string& key_system, 211 const std::string& key_system,
192 const GURL& security_origin) { 212 const GURL& security_origin) {
193 return ContentDecryptionModuleFactory::Create( 213 return ContentDecryptionModuleFactory::Create(
194 key_system, 214 key_system,
195 security_origin, 215 security_origin,
196 #if defined(ENABLE_PEPPER_CDMS) 216 #if defined(ENABLE_PEPPER_CDMS)
197 create_pepper_cdm_cb_, 217 create_pepper_cdm_cb_,
198 #elif defined(OS_ANDROID) 218 #elif defined(OS_ANDROID)
199 manager_, 219 manager_,
200 &cdm_id_, 220 &cdm_id_,
201 #endif // defined(ENABLE_PEPPER_CDMS) 221 #endif // defined(ENABLE_PEPPER_CDMS)
202 base::Bind(&ProxyDecryptor::OnSessionCreated,
203 weak_ptr_factory_.GetWeakPtr()),
204 base::Bind(&ProxyDecryptor::OnSessionMessage, 222 base::Bind(&ProxyDecryptor::OnSessionMessage,
205 weak_ptr_factory_.GetWeakPtr()), 223 weak_ptr_factory_.GetWeakPtr()),
206 base::Bind(&ProxyDecryptor::OnSessionReady, 224 base::Bind(&ProxyDecryptor::OnSessionReady,
207 weak_ptr_factory_.GetWeakPtr()), 225 weak_ptr_factory_.GetWeakPtr()),
208 base::Bind(&ProxyDecryptor::OnSessionClosed, 226 base::Bind(&ProxyDecryptor::OnSessionClosed,
209 weak_ptr_factory_.GetWeakPtr()), 227 weak_ptr_factory_.GetWeakPtr()),
210 base::Bind(&ProxyDecryptor::OnSessionError, 228 base::Bind(&ProxyDecryptor::OnSessionError,
211 weak_ptr_factory_.GetWeakPtr())); 229 weak_ptr_factory_.GetWeakPtr()));
212 } 230 }
213 231
214 void ProxyDecryptor::OnSessionCreated(uint32 session_id, 232 void ProxyDecryptor::OnSessionMessage(const std::string& web_session_id,
215 const std::string& web_session_id) { 233 const std::vector<uint8>& message,
216 // Due to heartbeat messages, OnSessionCreated() can get called multiple 234 const std::string& destination_url) {
217 // times. 235 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 } 236 }
223 237
224 void ProxyDecryptor::OnSessionMessage(uint32 session_id, 238 void ProxyDecryptor::OnSessionReady(const std::string& web_session_id) {
225 const std::vector<uint8>& message, 239 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 } 240 }
230 241
231 void ProxyDecryptor::OnSessionReady(uint32 session_id) { 242 void ProxyDecryptor::OnSessionClosed(const std::string& web_session_id) {
232 // Assumes that OnSessionCreated() has been called before this. 243 std::set<std::string>::iterator it =
233 key_added_cb_.Run(LookupWebSessionId(session_id)); 244 persistent_sessions_.find(web_session_id);
245 active_sessions_.erase(web_session_id);
246 if (it != persistent_sessions_.end()) {
247 persistent_sessions_.erase(it);
248 OnSessionError(web_session_id,
249 media::MediaKeys::EXCEPTION_NOT_SUPPORTED_ERROR,
250 kSessionClosedSystemCode,
251 "Do not close persistent sessions.");
252 }
234 } 253 }
235 254
236 void ProxyDecryptor::OnSessionClosed(uint32 session_id) { 255 void ProxyDecryptor::OnSessionError(const std::string& web_session_id,
237 std::set<uint32>::iterator it = persistent_sessions_.find(session_id); 256 media::MediaKeys::Exception exception_code,
238 if (it != persistent_sessions_.end()) { 257 uint32 system_code,
239 persistent_sessions_.erase(it); 258 const std::string& error_message) {
240 OnSessionError( 259 // Convert |error_name| back to MediaKeys::KeyError if possible. Prefixed
241 session_id, media::MediaKeys::kUnknownError, kSessionClosedSystemCode); 260 // EME has different error message, so all the specific error events will
261 // get lost.
262 media::MediaKeys::KeyError error_code;
263 switch (exception_code) {
264 case media::MediaKeys::EXCEPTION_CLIENT_ERROR:
265 error_code = media::MediaKeys::kClientError;
266 break;
267 case media::MediaKeys::EXCEPTION_OUTPUT_ERROR:
268 error_code = media::MediaKeys::kOutputError;
269 break;
270 default:
271 // This will include all other CDM4 errors and any error generated
272 // by CDM5 or later.
273 error_code = media::MediaKeys::kUnknownError;
274 break;
242 } 275 }
243 276 key_error_cb_.Run(web_session_id, error_code, system_code);
244 sessions_.erase(session_id);
245 } 277 }
246 278
247 void ProxyDecryptor::OnSessionError(uint32 session_id, 279 void ProxyDecryptor::SetSessionId(bool persistent,
248 media::MediaKeys::KeyError error_code, 280 const std::string& web_session_id) {
249 uint32 system_code) { 281 active_sessions_.insert(web_session_id);
250 // Assumes that OnSessionCreated() has been called before this. 282 if (persistent) {
251 key_error_cb_.Run(LookupWebSessionId(session_id), error_code, system_code); 283 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 } 284 }
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 } 285 }
276 286
277 } // namespace content 287 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698