Chromium Code Reviews| 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/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 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::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 |
| OLD | NEW |