OLD | NEW |
---|---|
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 Loading... | |
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::CdmNewSessionPromise> promise( |
107 new media::CdmNewSessionPromise( | |
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::CdmNewSessionPromise> promise( |
129 session_id, content_type, init_data, init_data_length); | 129 new media::CdmNewSessionPromise( |
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()))); | |
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. |
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::MEDIA_KEYS_EXCEPTION_NOT_SUPPORTED_ERROR, |
148 return; | 157 0, |
158 "SessionId not specified."); | |
159 return; | |
160 } | |
149 } | 161 } |
150 | 162 |
163 scoped_ptr<media::CdmChangeSessionPromise> promise( | |
164 new media::CdmChangeSessionPromise( | |
165 base::Bind(&ProxyDecryptor::OnSessionReady, | |
166 weak_ptr_factory_.GetWeakPtr(), | |
167 web_session_id), | |
168 base::Bind(&ProxyDecryptor::OnSessionError, | |
169 weak_ptr_factory_.GetWeakPtr(), | |
170 web_session_id))); | |
171 | |
151 // EME WD spec only supports a single array passed to the CDM. For | 172 // 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). | 173 // 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, | 174 // 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. | 175 // convert the 2 arrays to a JWK and pass it as the single array. |
155 if (is_clear_key_) { | 176 if (is_clear_key_) { |
156 // Decryptor doesn't support empty key ID (see http://crbug.com/123265). | 177 // Decryptor doesn't support empty key ID (see http://crbug.com/123265). |
157 // So ensure a non-empty value is passed. | 178 // So ensure a non-empty value is passed. |
158 if (!init_data) { | 179 if (!init_data) { |
159 static const uint8 kDummyInitData[1] = {0}; | 180 static const uint8 kDummyInitData[1] = {0}; |
160 init_data = kDummyInitData; | 181 init_data = kDummyInitData; |
161 init_data_length = arraysize(kDummyInitData); | 182 init_data_length = arraysize(kDummyInitData); |
162 } | 183 } |
163 | 184 |
164 std::string jwk = | 185 std::string jwk = |
165 media::GenerateJWKSet(key, key_length, init_data, init_data_length); | 186 media::GenerateJWKSet(key, key_length, init_data, init_data_length); |
166 DCHECK(!jwk.empty()); | 187 DCHECK(!jwk.empty()); |
167 media_keys_->UpdateSession( | 188 media_keys_->UpdateSession(session_id, |
168 session_id, reinterpret_cast<const uint8*>(jwk.data()), jwk.size()); | 189 reinterpret_cast<const uint8*>(jwk.data()), |
190 jwk.size(), | |
191 promise.Pass()); | |
169 return; | 192 return; |
170 } | 193 } |
171 | 194 |
172 media_keys_->UpdateSession(session_id, key, key_length); | 195 media_keys_->UpdateSession(session_id, key, key_length, promise.Pass()); |
173 } | 196 } |
174 | 197 |
175 void ProxyDecryptor::CancelKeyRequest(const std::string& session_id) { | 198 void ProxyDecryptor::CancelKeyRequest(const std::string& web_session_id) { |
176 DVLOG(1) << "CancelKeyRequest()"; | 199 DVLOG(1) << "CancelKeyRequest()"; |
177 | 200 |
178 // WebMediaPlayerImpl ensures GenerateKeyRequest() has been called. | 201 scoped_ptr<media::CdmChangeSessionPromise> promise( |
179 uint32 session_reference_id = LookupSessionId(session_id); | 202 new media::CdmChangeSessionPromise( |
180 if (session_reference_id == kInvalidSessionId) { | 203 base::Bind(&ProxyDecryptor::OnSessionClosed, |
181 // Session hasn't been created, so it is an error. | 204 weak_ptr_factory_.GetWeakPtr(), |
182 key_error_cb_.Run( | 205 web_session_id), |
183 std::string(), media::MediaKeys::kUnknownError, 0); | 206 base::Bind(&ProxyDecryptor::OnSessionError, |
184 } | 207 weak_ptr_factory_.GetWeakPtr(), |
185 else { | 208 web_session_id))); |
186 media_keys_->ReleaseSession(session_reference_id); | 209 media_keys_->ReleaseSession(web_session_id, promise.Pass()); |
187 } | |
188 } | 210 } |
189 | 211 |
190 scoped_ptr<media::MediaKeys> ProxyDecryptor::CreateMediaKeys( | 212 scoped_ptr<media::MediaKeys> ProxyDecryptor::CreateMediaKeys( |
191 const std::string& key_system, | 213 const std::string& key_system, |
192 const GURL& security_origin) { | 214 const GURL& security_origin) { |
193 return ContentDecryptionModuleFactory::Create( | 215 return ContentDecryptionModuleFactory::Create( |
194 key_system, | 216 key_system, |
195 security_origin, | 217 security_origin, |
196 #if defined(ENABLE_PEPPER_CDMS) | 218 #if defined(ENABLE_PEPPER_CDMS) |
197 create_pepper_cdm_cb_, | 219 create_pepper_cdm_cb_, |
198 #elif defined(OS_ANDROID) | 220 #elif defined(OS_ANDROID) |
199 manager_, | 221 manager_, |
200 &cdm_id_, | 222 &cdm_id_, |
201 #endif // defined(ENABLE_PEPPER_CDMS) | 223 #endif // defined(ENABLE_PEPPER_CDMS) |
202 base::Bind(&ProxyDecryptor::OnSessionCreated, | |
203 weak_ptr_factory_.GetWeakPtr()), | |
204 base::Bind(&ProxyDecryptor::OnSessionMessage, | 224 base::Bind(&ProxyDecryptor::OnSessionMessage, |
205 weak_ptr_factory_.GetWeakPtr()), | 225 weak_ptr_factory_.GetWeakPtr()), |
206 base::Bind(&ProxyDecryptor::OnSessionReady, | 226 base::Bind(&ProxyDecryptor::OnSessionReady, |
207 weak_ptr_factory_.GetWeakPtr()), | 227 weak_ptr_factory_.GetWeakPtr()), |
208 base::Bind(&ProxyDecryptor::OnSessionClosed, | 228 base::Bind(&ProxyDecryptor::OnSessionClosed, |
209 weak_ptr_factory_.GetWeakPtr()), | 229 weak_ptr_factory_.GetWeakPtr()), |
210 base::Bind(&ProxyDecryptor::OnSessionError, | 230 base::Bind(&ProxyDecryptor::OnSessionError, |
211 weak_ptr_factory_.GetWeakPtr())); | 231 weak_ptr_factory_.GetWeakPtr())); |
212 } | 232 } |
213 | 233 |
214 void ProxyDecryptor::OnSessionCreated(uint32 session_id, | 234 void ProxyDecryptor::OnSessionMessage(const std::string& web_session_id, |
215 const std::string& web_session_id) { | 235 const std::vector<uint8>& message, |
216 // Due to heartbeat messages, OnSessionCreated() can get called multiple | 236 const std::string& destination_url) { |
217 // times. | 237 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 } | 238 } |
223 | 239 |
224 void ProxyDecryptor::OnSessionMessage(uint32 session_id, | 240 void ProxyDecryptor::OnSessionReady(const std::string& web_session_id) { |
225 const std::vector<uint8>& message, | 241 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 } | 242 } |
230 | 243 |
231 void ProxyDecryptor::OnSessionReady(uint32 session_id) { | 244 void ProxyDecryptor::OnSessionClosed(const std::string& web_session_id) { |
232 // Assumes that OnSessionCreated() has been called before this. | 245 std::set<std::string>::iterator it = |
233 key_added_cb_.Run(LookupWebSessionId(session_id)); | 246 persistent_sessions_.find(web_session_id); |
247 active_sessions_.erase(web_session_id); | |
248 if (it != persistent_sessions_.end()) { | |
249 persistent_sessions_.erase(it); | |
250 OnSessionError(web_session_id, | |
251 media::MediaKeys::MEDIA_KEYS_EXCEPTION_NOT_SUPPORTED_ERROR, | |
ddorwin
2014/05/13 22:44:02
I see a mix of enum naming styles. (This is the Ch
jrummell
2014/05/15 22:38:09
Looks like ENUM_NAME is the preferred style. Remov
| |
252 kSessionClosedSystemCode, | |
253 "Do not close persistent sessions."); | |
254 } | |
234 } | 255 } |
235 | 256 |
236 void ProxyDecryptor::OnSessionClosed(uint32 session_id) { | 257 void ProxyDecryptor::OnSessionError( |
237 std::set<uint32>::iterator it = persistent_sessions_.find(session_id); | 258 const std::string& web_session_id, |
238 if (it != persistent_sessions_.end()) { | 259 media::MediaKeys::MediaKeysException exception_code, |
239 persistent_sessions_.erase(it); | 260 uint32 system_code, |
240 OnSessionError( | 261 const std::string& error_message) { |
241 session_id, media::MediaKeys::kUnknownError, kSessionClosedSystemCode); | 262 // Convert |error_name| back to MediaKeys::KeyError if possible. Prefixed |
263 // EME has different error message, so all the specific error events will | |
264 // get lost. | |
265 switch (exception_code) { | |
ddorwin
2014/05/13 22:44:02
The switch should convert the exception then there
jrummell
2014/05/15 22:38:09
Done.
| |
266 case media::MediaKeys::MEDIA_KEYS_EXCEPTION_CLIENT_ERROR: | |
267 key_error_cb_.Run( | |
268 web_session_id, media::MediaKeys::kClientError, system_code); | |
269 break; | |
270 case media::MediaKeys::MEDIA_KEYS_EXCEPTION_OUTPUT_ERROR: | |
271 key_error_cb_.Run( | |
272 web_session_id, media::MediaKeys::kOutputError, system_code); | |
273 break; | |
274 default: | |
275 // This will include all other CDM4 errors and any error generated | |
276 // by CDM5 or later. | |
277 key_error_cb_.Run( | |
278 web_session_id, media::MediaKeys::kUnknownError, system_code); | |
279 break; | |
242 } | 280 } |
243 | |
244 sessions_.erase(session_id); | |
245 } | 281 } |
246 | 282 |
247 void ProxyDecryptor::OnSessionError(uint32 session_id, | 283 void ProxyDecryptor::SetSessionId(bool persistent, |
248 media::MediaKeys::KeyError error_code, | 284 const std::string& web_session_id) { |
249 uint32 system_code) { | 285 active_sessions_.insert(web_session_id); |
250 // Assumes that OnSessionCreated() has been called before this. | 286 if (persistent) { |
251 key_error_cb_.Run(LookupWebSessionId(session_id), error_code, system_code); | 287 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 } | 288 } |
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 } | 289 } |
276 | 290 |
277 } // namespace content | 291 } // namespace content |
OLD | NEW |