| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 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_media_keys.h" | 5 #include "content/renderer/media/crypto/proxy_media_keys.h" |
| 6 | 6 |
| 7 #include <vector> | 7 #include <vector> |
| 8 | 8 |
| 9 #include "base/basictypes.h" | 9 #include "base/basictypes.h" |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| 11 #include "base/stl_util.h" | 11 #include "base/stl_util.h" |
| 12 #include "content/renderer/media/crypto/renderer_cdm_manager.h" | 12 #include "content/renderer/media/crypto/renderer_cdm_manager.h" |
| 13 #include "media/base/cdm_key_information.h" |
| 13 #include "media/base/cdm_promise.h" | 14 #include "media/base/cdm_promise.h" |
| 14 #include "media/base/key_systems.h" | 15 #include "media/base/key_systems.h" |
| 15 | 16 |
| 16 namespace content { | 17 namespace content { |
| 17 | 18 |
| 18 scoped_ptr<ProxyMediaKeys> ProxyMediaKeys::Create( | 19 scoped_ptr<ProxyMediaKeys> ProxyMediaKeys::Create( |
| 19 const std::string& key_system, | 20 const std::string& key_system, |
| 20 const GURL& security_origin, | 21 const GURL& security_origin, |
| 21 RendererCdmManager* manager, | 22 RendererCdmManager* manager, |
| 22 const media::SessionMessageCB& session_message_cb, | 23 const media::SessionMessageCB& session_message_cb, |
| 23 const media::SessionClosedCB& session_closed_cb, | 24 const media::SessionClosedCB& session_closed_cb, |
| 24 const media::SessionErrorCB& session_error_cb, | 25 const media::SessionErrorCB& session_error_cb, |
| 25 const media::SessionKeysChangeCB& session_keys_change_cb, | 26 const media::SessionKeysChangeCB& session_keys_change_cb, |
| 26 const media::SessionExpirationUpdateCB& session_expiration_update_cb) { | 27 const media::SessionExpirationUpdateCB& session_expiration_update_cb) { |
| 27 DCHECK(manager); | 28 DCHECK(manager); |
| 28 | 29 scoped_ptr<ProxyMediaKeys> proxy_media_keys(new ProxyMediaKeys( |
| 29 // TODO(jrummell): Add support for SessionKeysChangeCB and | 30 manager, session_message_cb, session_closed_cb, session_error_cb, |
| 30 // SessionExpirationUpdateCB. | 31 session_keys_change_cb, session_expiration_update_cb)); |
| 31 scoped_ptr<ProxyMediaKeys> proxy_media_keys( | |
| 32 new ProxyMediaKeys(manager, | |
| 33 session_message_cb, | |
| 34 session_closed_cb, | |
| 35 session_error_cb)); | |
| 36 proxy_media_keys->InitializeCdm(key_system, security_origin); | 32 proxy_media_keys->InitializeCdm(key_system, security_origin); |
| 37 return proxy_media_keys.Pass(); | 33 return proxy_media_keys.Pass(); |
| 38 } | 34 } |
| 39 | 35 |
| 40 ProxyMediaKeys::~ProxyMediaKeys() { | 36 ProxyMediaKeys::~ProxyMediaKeys() { |
| 41 manager_->DestroyCdm(cdm_id_); | 37 manager_->DestroyCdm(cdm_id_); |
| 42 manager_->UnregisterMediaKeys(cdm_id_); | 38 manager_->UnregisterMediaKeys(cdm_id_); |
| 43 | 39 cdm_promise_adapter_.Clear(); |
| 44 // Reject any outstanding promises. | |
| 45 for (PromiseMap::iterator it = session_id_to_promise_map_.begin(); | |
| 46 it != session_id_to_promise_map_.end(); | |
| 47 ++it) { | |
| 48 it->second->reject( | |
| 49 media::MediaKeys::NOT_SUPPORTED_ERROR, 0, "The operation was aborted."); | |
| 50 } | |
| 51 session_id_to_promise_map_.clear(); | |
| 52 } | 40 } |
| 53 | 41 |
| 54 void ProxyMediaKeys::SetServerCertificate( | 42 void ProxyMediaKeys::SetServerCertificate( |
| 55 const uint8* certificate_data, | 43 const uint8* certificate_data, |
| 56 int certificate_data_length, | 44 int certificate_data_length, |
| 57 scoped_ptr<media::SimpleCdmPromise> promise) { | 45 scoped_ptr<media::SimpleCdmPromise> promise) { |
| 58 promise->reject(NOT_SUPPORTED_ERROR, 0, "Not yet implemented."); | 46 uint32_t promise_id = cdm_promise_adapter_.SavePromise(promise.Pass()); |
| 47 manager_->SetServerCertificate( |
| 48 cdm_id_, promise_id, |
| 49 std::vector<uint8>(certificate_data, |
| 50 certificate_data + certificate_data_length)); |
| 59 } | 51 } |
| 60 | 52 |
| 61 void ProxyMediaKeys::CreateSessionAndGenerateRequest( | 53 void ProxyMediaKeys::CreateSessionAndGenerateRequest( |
| 62 SessionType session_type, | 54 SessionType session_type, |
| 63 const std::string& init_data_type, | 55 const std::string& init_data_type, |
| 64 const uint8* init_data, | 56 const uint8* init_data, |
| 65 int init_data_length, | 57 int init_data_length, |
| 66 scoped_ptr<media::NewSessionCdmPromise> promise) { | 58 scoped_ptr<media::NewSessionCdmPromise> promise) { |
| 59 if (session_type != media::MediaKeys::TEMPORARY_SESSION) { |
| 60 promise->reject(NOT_SUPPORTED_ERROR, 0, |
| 61 "Only the temporary session type is supported."); |
| 62 return; |
| 63 } |
| 64 |
| 67 // TODO(xhwang): Move these checks up to blink and DCHECK here. | 65 // TODO(xhwang): Move these checks up to blink and DCHECK here. |
| 68 // See http://crbug.com/342510 | 66 // See http://crbug.com/342510 |
| 69 CdmHostMsg_CreateSession_ContentType create_session_content_type; | 67 CdmHostMsg_CreateSession_InitDataType create_session_init_data_type; |
| 70 if (init_data_type == "cenc") { | 68 if (init_data_type == "cenc") { |
| 71 create_session_content_type = CREATE_SESSION_TYPE_MP4; | 69 create_session_init_data_type = CREATE_SESSION_TYPE_MP4; |
| 72 } else if (init_data_type == "webm") { | 70 } else if (init_data_type == "webm") { |
| 73 create_session_content_type = CREATE_SESSION_TYPE_WEBM; | 71 create_session_init_data_type = CREATE_SESSION_TYPE_WEBM; |
| 74 } else { | 72 } else { |
| 75 DLOG(ERROR) << "Unsupported EME CreateSession content type of " | 73 DLOG(ERROR) << "Unsupported EME CreateSession content type of " |
| 76 << init_data_type; | 74 << init_data_type; |
| 77 promise->reject( | 75 promise->reject( |
| 78 NOT_SUPPORTED_ERROR, | 76 NOT_SUPPORTED_ERROR, |
| 79 0, | 77 0, |
| 80 "Unsupported EME CreateSession init data type of " + init_data_type); | 78 "Unsupported EME CreateSession init data type of " + init_data_type); |
| 81 return; | 79 return; |
| 82 } | 80 } |
| 83 | 81 |
| 84 uint32 session_id = CreateSessionId(); | 82 uint32_t promise_id = cdm_promise_adapter_.SavePromise(promise.Pass()); |
| 85 SavePromise(session_id, promise.Pass()); | 83 manager_->CreateSessionAndGenerateRequest( |
| 86 manager_->CreateSession( | 84 cdm_id_, promise_id, create_session_init_data_type, |
| 87 cdm_id_, | |
| 88 session_id, | |
| 89 create_session_content_type, | |
| 90 std::vector<uint8>(init_data, init_data + init_data_length)); | 85 std::vector<uint8>(init_data, init_data + init_data_length)); |
| 91 } | 86 } |
| 92 | 87 |
| 93 void ProxyMediaKeys::LoadSession( | 88 void ProxyMediaKeys::LoadSession( |
| 94 SessionType session_type, | 89 SessionType session_type, |
| 95 const std::string& web_session_id, | 90 const std::string& session_id, |
| 96 scoped_ptr<media::NewSessionCdmPromise> promise) { | 91 scoped_ptr<media::NewSessionCdmPromise> promise) { |
| 97 // TODO(xhwang): Check key system and platform support for LoadSession in | 92 // TODO(xhwang): Check key system and platform support for LoadSession in |
| 98 // blink and add NOTREACHED() here. | 93 // blink and add NOTREACHED() here. See http://crbug.com/384152 |
| 99 DLOG(ERROR) << "ProxyMediaKeys doesn't support session loading."; | 94 DLOG(ERROR) << "ProxyMediaKeys doesn't support session loading."; |
| 100 promise->reject(NOT_SUPPORTED_ERROR, 0, "LoadSession() is not supported."); | 95 promise->reject(NOT_SUPPORTED_ERROR, 0, "LoadSession() is not supported."); |
| 101 } | 96 } |
| 102 | 97 |
| 103 void ProxyMediaKeys::UpdateSession( | 98 void ProxyMediaKeys::UpdateSession( |
| 104 const std::string& web_session_id, | 99 const std::string& session_id, |
| 105 const uint8* response, | 100 const uint8* response, |
| 106 int response_length, | 101 int response_length, |
| 107 scoped_ptr<media::SimpleCdmPromise> promise) { | 102 scoped_ptr<media::SimpleCdmPromise> promise) { |
| 108 uint32 session_id = LookupSessionId(web_session_id); | 103 uint32_t promise_id = cdm_promise_adapter_.SavePromise(promise.Pass()); |
| 109 if (!session_id) { | |
| 110 promise->reject(INVALID_ACCESS_ERROR, 0, "Session does not exist."); | |
| 111 return; | |
| 112 } | |
| 113 | |
| 114 SavePromise(session_id, promise.Pass()); | |
| 115 manager_->UpdateSession( | 104 manager_->UpdateSession( |
| 116 cdm_id_, | 105 cdm_id_, promise_id, session_id, |
| 117 session_id, | |
| 118 std::vector<uint8>(response, response + response_length)); | 106 std::vector<uint8>(response, response + response_length)); |
| 119 } | 107 } |
| 120 | 108 |
| 121 void ProxyMediaKeys::CloseSession(const std::string& web_session_id, | 109 void ProxyMediaKeys::CloseSession(const std::string& session_id, |
| 122 scoped_ptr<media::SimpleCdmPromise> promise) { | 110 scoped_ptr<media::SimpleCdmPromise> promise) { |
| 123 uint32 session_id = LookupSessionId(web_session_id); | 111 uint32_t promise_id = cdm_promise_adapter_.SavePromise(promise.Pass()); |
| 124 if (!session_id) { | 112 manager_->CloseSession(cdm_id_, promise_id, session_id); |
| 125 promise->reject(INVALID_ACCESS_ERROR, 0, "Session does not exist."); | |
| 126 return; | |
| 127 } | |
| 128 | |
| 129 SavePromise(session_id, promise.Pass()); | |
| 130 manager_->ReleaseSession(cdm_id_, session_id); | |
| 131 } | 113 } |
| 132 | 114 |
| 133 void ProxyMediaKeys::RemoveSession( | 115 void ProxyMediaKeys::RemoveSession( |
| 134 const std::string& web_session_id, | 116 const std::string& session_id, |
| 135 scoped_ptr<media::SimpleCdmPromise> promise) { | 117 scoped_ptr<media::SimpleCdmPromise> promise) { |
| 136 promise->reject(NOT_SUPPORTED_ERROR, 0, "Not yet implemented."); | 118 // TODO(xhwang): Check key system and platform support for LoadSession in |
| 119 // blink and add NOTREACHED() here. See http://crbug.com/384152 |
| 120 promise->reject(NOT_SUPPORTED_ERROR, 0, "RemoveSession() not supported."); |
| 137 } | 121 } |
| 138 | 122 |
| 139 media::CdmContext* ProxyMediaKeys::GetCdmContext() { | 123 media::CdmContext* ProxyMediaKeys::GetCdmContext() { |
| 140 return this; | 124 return this; |
| 141 } | 125 } |
| 142 | 126 |
| 143 media::Decryptor* ProxyMediaKeys::GetDecryptor() { | 127 media::Decryptor* ProxyMediaKeys::GetDecryptor() { |
| 144 return NULL; | 128 return NULL; |
| 145 } | 129 } |
| 146 | 130 |
| 147 int ProxyMediaKeys::GetCdmId() const { | 131 int ProxyMediaKeys::GetCdmId() const { |
| 148 return cdm_id_; | 132 return cdm_id_; |
| 149 } | 133 } |
| 150 | 134 |
| 151 void ProxyMediaKeys::OnSessionCreated(uint32 session_id, | 135 void ProxyMediaKeys::OnSessionMessage( |
| 152 const std::string& web_session_id) { | 136 const std::string& session_id, |
| 153 AssignWebSessionId(session_id, web_session_id); | 137 media::MediaKeys::MessageType message_type, |
| 154 scoped_ptr<media::CdmPromise> promise = TakePromise(session_id); | 138 const std::vector<uint8>& message, |
| 155 if (promise) { | 139 const GURL& legacy_destination_url) { |
| 156 media::NewSessionCdmPromise* session_promise( | 140 session_message_cb_.Run(session_id, message_type, message, |
| 157 static_cast<media::NewSessionCdmPromise*>(promise.get())); | |
| 158 session_promise->resolve(web_session_id); | |
| 159 } | |
| 160 } | |
| 161 | |
| 162 void ProxyMediaKeys::OnSessionMessage(uint32 session_id, | |
| 163 const std::vector<uint8>& message, | |
| 164 const GURL& legacy_destination_url) { | |
| 165 // TODO(jrummell): Once |message_type| is passed, use it rather than | |
| 166 // guessing from the URL. | |
| 167 media::MediaKeys::MessageType message_type = | |
| 168 legacy_destination_url.is_empty() ? media::MediaKeys::LICENSE_REQUEST | |
| 169 : media::MediaKeys::LICENSE_RENEWAL; | |
| 170 session_message_cb_.Run(LookupWebSessionId(session_id), message_type, message, | |
| 171 legacy_destination_url); | 141 legacy_destination_url); |
| 172 } | 142 } |
| 173 | 143 |
| 174 void ProxyMediaKeys::OnSessionReady(uint32 session_id) { | 144 void ProxyMediaKeys::OnSessionClosed(const std::string& session_id) { |
| 175 scoped_ptr<media::CdmPromise> promise = TakePromise(session_id); | 145 session_closed_cb_.Run(session_id); |
| 176 if (promise) { | |
| 177 media::SimpleCdmPromise* simple_promise( | |
| 178 static_cast<media::SimpleCdmPromise*>(promise.get())); | |
| 179 simple_promise->resolve(); | |
| 180 } | |
| 181 } | 146 } |
| 182 | 147 |
| 183 void ProxyMediaKeys::OnSessionClosed(uint32 session_id) { | 148 void ProxyMediaKeys::OnLegacySessionError(const std::string& session_id, |
| 184 const std::string web_session_id = LookupWebSessionId(session_id); | 149 media::MediaKeys::Exception exception, |
| 185 DropWebSessionId(web_session_id); | 150 uint32 system_code, |
| 186 scoped_ptr<media::CdmPromise> promise = TakePromise(session_id); | 151 const std::string& error_message) { |
| 187 if (promise) { | 152 session_error_cb_.Run(session_id, exception, system_code, error_message); |
| 188 media::SimpleCdmPromise* simple_promise( | |
| 189 static_cast<media::SimpleCdmPromise*>(promise.get())); | |
| 190 simple_promise->resolve(); | |
| 191 } else { | |
| 192 // It is possible for the CDM to close a session independent of a | |
| 193 // Release() request. | |
| 194 session_closed_cb_.Run(web_session_id); | |
| 195 } | |
| 196 } | 153 } |
| 197 | 154 |
| 198 void ProxyMediaKeys::OnSessionError(uint32 session_id, | 155 void ProxyMediaKeys::OnSessionKeysChange(const std::string& session_id, |
| 199 media::MediaKeys::KeyError error_code, | 156 bool has_additional_usable_key, |
| 200 uint32 system_code) { | 157 media::CdmKeysInfo keys_info) { |
| 201 const std::string web_session_id = LookupWebSessionId(session_id); | 158 session_keys_change_cb_.Run(session_id, has_additional_usable_key, |
| 202 media::MediaKeys::Exception exception_code; | 159 keys_info.Pass()); |
| 203 switch (error_code) { | 160 } |
| 204 case media::MediaKeys::kClientError: | |
| 205 exception_code = media::MediaKeys::CLIENT_ERROR; | |
| 206 break; | |
| 207 case media::MediaKeys::kOutputError: | |
| 208 exception_code = media::MediaKeys::OUTPUT_ERROR; | |
| 209 break; | |
| 210 case media::MediaKeys::kUnknownError: | |
| 211 default: | |
| 212 exception_code = media::MediaKeys::UNKNOWN_ERROR; | |
| 213 break; | |
| 214 } | |
| 215 | 161 |
| 216 scoped_ptr<media::CdmPromise> promise = TakePromise(session_id); | 162 void ProxyMediaKeys::OnSessionExpirationUpdate( |
| 217 if (promise) { | 163 const std::string& session_id, |
| 218 promise->reject(exception_code, system_code, std::string()); | 164 const base::Time& new_expiry_time) { |
| 219 return; | 165 session_expiration_update_cb_.Run(session_id, new_expiry_time); |
| 220 } | 166 } |
| 221 | 167 |
| 222 // Errors generally happen in response to a request, but it is possible | 168 void ProxyMediaKeys::OnPromiseResolved(uint32_t promise_id) { |
| 223 // for something bad to happen in the CDM and it needs to tell the client. | 169 cdm_promise_adapter_.ResolvePromise(promise_id); |
| 224 session_error_cb_.Run( | 170 } |
| 225 web_session_id, exception_code, system_code, std::string()); | 171 |
| 172 void ProxyMediaKeys::OnPromiseResolvedWithSession( |
| 173 uint32_t promise_id, |
| 174 const std::string& session_id) { |
| 175 cdm_promise_adapter_.ResolvePromise(promise_id, session_id); |
| 176 } |
| 177 |
| 178 void ProxyMediaKeys::OnPromiseRejected(uint32_t promise_id, |
| 179 media::MediaKeys::Exception exception, |
| 180 uint32_t system_code, |
| 181 const std::string& error_message) { |
| 182 cdm_promise_adapter_.RejectPromise(promise_id, exception, system_code, |
| 183 error_message); |
| 226 } | 184 } |
| 227 | 185 |
| 228 ProxyMediaKeys::ProxyMediaKeys( | 186 ProxyMediaKeys::ProxyMediaKeys( |
| 229 RendererCdmManager* manager, | 187 RendererCdmManager* manager, |
| 230 const media::SessionMessageCB& session_message_cb, | 188 const media::SessionMessageCB& session_message_cb, |
| 231 const media::SessionClosedCB& session_closed_cb, | 189 const media::SessionClosedCB& session_closed_cb, |
| 232 const media::SessionErrorCB& session_error_cb) | 190 const media::SessionErrorCB& session_error_cb, |
| 191 const media::SessionKeysChangeCB& session_keys_change_cb, |
| 192 const media::SessionExpirationUpdateCB& session_expiration_update_cb) |
| 233 : manager_(manager), | 193 : manager_(manager), |
| 234 session_message_cb_(session_message_cb), | 194 session_message_cb_(session_message_cb), |
| 235 session_closed_cb_(session_closed_cb), | 195 session_closed_cb_(session_closed_cb), |
| 236 session_error_cb_(session_error_cb), | 196 session_error_cb_(session_error_cb), |
| 237 next_session_id_(1) { | 197 session_keys_change_cb_(session_keys_change_cb), |
| 198 session_expiration_update_cb_(session_expiration_update_cb) { |
| 238 cdm_id_ = manager->RegisterMediaKeys(this); | 199 cdm_id_ = manager->RegisterMediaKeys(this); |
| 239 } | 200 } |
| 240 | 201 |
| 241 void ProxyMediaKeys::InitializeCdm(const std::string& key_system, | 202 void ProxyMediaKeys::InitializeCdm(const std::string& key_system, |
| 242 const GURL& security_origin) { | 203 const GURL& security_origin) { |
| 243 manager_->InitializeCdm(cdm_id_, this, key_system, security_origin); | 204 manager_->InitializeCdm(cdm_id_, this, key_system, security_origin); |
| 244 } | 205 } |
| 245 | 206 |
| 246 uint32_t ProxyMediaKeys::CreateSessionId() { | |
| 247 return next_session_id_++; | |
| 248 } | |
| 249 | |
| 250 void ProxyMediaKeys::AssignWebSessionId(uint32_t session_id, | |
| 251 const std::string& web_session_id) { | |
| 252 DCHECK(!ContainsKey(web_session_to_session_id_map_, web_session_id)); | |
| 253 DCHECK(session_id); | |
| 254 web_session_to_session_id_map_.insert( | |
| 255 std::make_pair(web_session_id, session_id)); | |
| 256 } | |
| 257 | |
| 258 uint32_t ProxyMediaKeys::LookupSessionId( | |
| 259 const std::string& web_session_id) const { | |
| 260 SessionIdMap::const_iterator it = | |
| 261 web_session_to_session_id_map_.find(web_session_id); | |
| 262 return (it != web_session_to_session_id_map_.end()) ? it->second : 0; | |
| 263 } | |
| 264 | |
| 265 std::string ProxyMediaKeys::LookupWebSessionId(uint32_t session_id) const { | |
| 266 for (SessionIdMap::const_iterator it = web_session_to_session_id_map_.begin(); | |
| 267 it != web_session_to_session_id_map_.end(); | |
| 268 ++it) { | |
| 269 if (it->second == session_id) | |
| 270 return it->first; | |
| 271 } | |
| 272 // Possible to get an error creating a session, so no |web_session_id| | |
| 273 // available. | |
| 274 return std::string(); | |
| 275 } | |
| 276 | |
| 277 void ProxyMediaKeys::DropWebSessionId(const std::string& web_session_id) { | |
| 278 web_session_to_session_id_map_.erase(web_session_id); | |
| 279 } | |
| 280 | |
| 281 void ProxyMediaKeys::SavePromise(uint32_t session_id, | |
| 282 scoped_ptr<media::CdmPromise> promise) { | |
| 283 // Should only be one promise outstanding for any |session_id|. | |
| 284 DCHECK(!ContainsKey(session_id_to_promise_map_, session_id)); | |
| 285 session_id_to_promise_map_.add(session_id, promise.Pass()); | |
| 286 } | |
| 287 | |
| 288 scoped_ptr<media::CdmPromise> ProxyMediaKeys::TakePromise(uint32_t session_id) { | |
| 289 PromiseMap::iterator it = session_id_to_promise_map_.find(session_id); | |
| 290 // May not be a promise associated with this session for asynchronous events. | |
| 291 if (it == session_id_to_promise_map_.end()) | |
| 292 return scoped_ptr<media::CdmPromise>(); | |
| 293 return session_id_to_promise_map_.take_and_erase(it); | |
| 294 } | |
| 295 | |
| 296 } // namespace content | 207 } // namespace content |
| OLD | NEW |