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
3 // found in the LICENSE file.
5 #include "content/renderer/media/crypto/proxy_media_keys.h"
9 #include "base/basictypes.h"
10 #include "base/logging.h"
11 #include "base/stl_util.h"
12 #include "content/renderer/media/crypto/key_systems.h"
13 #include "content/renderer/media/crypto/renderer_cdm_manager.h"
14 #include "media/base/cdm_promise.h"
18 scoped_ptr<ProxyMediaKeys> ProxyMediaKeys::Create(
19 const std::string& key_system,
20 const GURL& security_origin,
21 RendererCdmManager* manager,
22 const media::SessionMessageCB& session_message_cb,
23 const media::SessionReadyCB& session_ready_cb,
24 const media::SessionClosedCB& session_closed_cb,
25 const media::SessionErrorCB& session_error_cb) {
27 scoped_ptr<ProxyMediaKeys> proxy_media_keys(
28 new ProxyMediaKeys(manager,
33 proxy_media_keys->InitializeCdm(key_system, security_origin);
34 return proxy_media_keys.Pass();
37 ProxyMediaKeys::~ProxyMediaKeys() {
38 manager_->DestroyCdm(cdm_id_);
39 manager_->UnregisterMediaKeys(cdm_id_);
41 // Reject any outstanding promises.
42 for (PromiseMap::iterator it = session_id_to_promise_map_.begin();
43 it != session_id_to_promise_map_.end();
46 media::MediaKeys::NOT_SUPPORTED_ERROR, 0, "The operation was aborted.");
48 session_id_to_promise_map_.clear();
51 void ProxyMediaKeys::CreateSession(
52 const std::string& init_data_type,
53 const uint8* init_data,
55 SessionType session_type,
56 scoped_ptr<media::NewSessionCdmPromise> promise) {
57 // TODO(xhwang): Move these checks up to blink and DCHECK here.
58 // See http://crbug.com/342510
59 CdmHostMsg_CreateSession_ContentType create_session_content_type;
60 if (init_data_type == "cenc") {
61 create_session_content_type = CREATE_SESSION_TYPE_MP4;
62 } else if (init_data_type == "webm") {
63 create_session_content_type = CREATE_SESSION_TYPE_WEBM;
65 DLOG(ERROR) << "Unsupported EME CreateSession content type of "
70 "Unsupported EME CreateSession init data type of " + init_data_type);
74 uint32 session_id = CreateSessionId();
75 SavePromise(session_id, promise.PassAs<media::CdmPromise>());
76 manager_->CreateSession(
79 create_session_content_type,
80 std::vector<uint8>(init_data, init_data + init_data_length));
83 void ProxyMediaKeys::LoadSession(
84 const std::string& web_session_id,
85 scoped_ptr<media::NewSessionCdmPromise> promise) {
86 // TODO(xhwang): Check key system and platform support for LoadSession in
87 // blink and add NOTREACHED() here.
88 DLOG(ERROR) << "ProxyMediaKeys doesn't support session loading.";
89 promise->reject(NOT_SUPPORTED_ERROR, 0, "LoadSession() is not supported.");
92 void ProxyMediaKeys::UpdateSession(
93 const std::string& web_session_id,
94 const uint8* response,
96 scoped_ptr<media::SimpleCdmPromise> promise) {
97 uint32 session_id = LookupSessionId(web_session_id);
99 promise->reject(INVALID_ACCESS_ERROR, 0, "Session does not exist.");
103 SavePromise(session_id, promise.PassAs<media::CdmPromise>());
104 manager_->UpdateSession(
107 std::vector<uint8>(response, response + response_length));
110 void ProxyMediaKeys::ReleaseSession(
111 const std::string& web_session_id,
112 scoped_ptr<media::SimpleCdmPromise> promise) {
113 uint32 session_id = LookupSessionId(web_session_id);
115 promise->reject(INVALID_ACCESS_ERROR, 0, "Session does not exist.");
119 SavePromise(session_id, promise.PassAs<media::CdmPromise>());
120 manager_->ReleaseSession(cdm_id_, session_id);
123 void ProxyMediaKeys::OnSessionCreated(uint32 session_id,
124 const std::string& web_session_id) {
125 AssignWebSessionId(session_id, web_session_id);
126 scoped_ptr<media::CdmPromise> promise = TakePromise(session_id);
128 media::NewSessionCdmPromise* session_promise(
129 static_cast<media::NewSessionCdmPromise*>(promise.get()));
130 session_promise->resolve(web_session_id);
134 void ProxyMediaKeys::OnSessionMessage(uint32 session_id,
135 const std::vector<uint8>& message,
136 const GURL& destination_url) {
137 session_message_cb_.Run(
138 LookupWebSessionId(session_id), message, destination_url);
141 void ProxyMediaKeys::OnSessionReady(uint32 session_id) {
142 scoped_ptr<media::CdmPromise> promise = TakePromise(session_id);
144 media::SimpleCdmPromise* simple_promise(
145 static_cast<media::SimpleCdmPromise*>(promise.get()));
146 simple_promise->resolve();
148 // Still needed for keyadded.
149 const std::string web_session_id = LookupWebSessionId(session_id);
150 session_ready_cb_.Run(web_session_id);
154 void ProxyMediaKeys::OnSessionClosed(uint32 session_id) {
155 const std::string web_session_id = LookupWebSessionId(session_id);
156 DropWebSessionId(web_session_id);
157 scoped_ptr<media::CdmPromise> promise = TakePromise(session_id);
159 media::SimpleCdmPromise* simple_promise(
160 static_cast<media::SimpleCdmPromise*>(promise.get()));
161 simple_promise->resolve();
163 // It is possible for the CDM to close a session independent of a
164 // Release() request.
165 session_closed_cb_.Run(web_session_id);
169 void ProxyMediaKeys::OnSessionError(uint32 session_id,
170 media::MediaKeys::KeyError error_code,
171 uint32 system_code) {
172 const std::string web_session_id = LookupWebSessionId(session_id);
173 media::MediaKeys::Exception exception_code;
174 switch (error_code) {
175 case media::MediaKeys::kClientError:
176 exception_code = media::MediaKeys::CLIENT_ERROR;
178 case media::MediaKeys::kOutputError:
179 exception_code = media::MediaKeys::OUTPUT_ERROR;
181 case media::MediaKeys::kUnknownError:
183 exception_code = media::MediaKeys::UNKNOWN_ERROR;
187 scoped_ptr<media::CdmPromise> promise = TakePromise(session_id);
189 promise->reject(exception_code, system_code, std::string());
193 // Errors generally happen in response to a request, but it is possible
194 // for something bad to happen in the CDM and it needs to tell the client.
195 session_error_cb_.Run(
196 web_session_id, exception_code, system_code, std::string());
199 int ProxyMediaKeys::GetCdmId() const {
203 ProxyMediaKeys::ProxyMediaKeys(
204 RendererCdmManager* manager,
205 const media::SessionMessageCB& session_message_cb,
206 const media::SessionReadyCB& session_ready_cb,
207 const media::SessionClosedCB& session_closed_cb,
208 const media::SessionErrorCB& session_error_cb)
210 session_message_cb_(session_message_cb),
211 session_ready_cb_(session_ready_cb),
212 session_closed_cb_(session_closed_cb),
213 session_error_cb_(session_error_cb),
214 next_session_id_(1) {
215 cdm_id_ = manager->RegisterMediaKeys(this);
218 void ProxyMediaKeys::InitializeCdm(const std::string& key_system,
219 const GURL& security_origin) {
220 manager_->InitializeCdm(cdm_id_, this, key_system, security_origin);
223 uint32_t ProxyMediaKeys::CreateSessionId() {
224 return next_session_id_++;
227 void ProxyMediaKeys::AssignWebSessionId(uint32_t session_id,
228 const std::string& web_session_id) {
229 DCHECK(!ContainsKey(web_session_to_session_id_map_, web_session_id));
231 web_session_to_session_id_map_.insert(
232 std::make_pair(web_session_id, session_id));
235 uint32_t ProxyMediaKeys::LookupSessionId(
236 const std::string& web_session_id) const {
237 SessionIdMap::const_iterator it =
238 web_session_to_session_id_map_.find(web_session_id);
239 return (it != web_session_to_session_id_map_.end()) ? it->second : 0;
242 std::string ProxyMediaKeys::LookupWebSessionId(uint32_t session_id) const {
243 for (SessionIdMap::const_iterator it = web_session_to_session_id_map_.begin();
244 it != web_session_to_session_id_map_.end();
246 if (it->second == session_id)
249 // Possible to get an error creating a session, so no |web_session_id|
251 return std::string();
254 void ProxyMediaKeys::DropWebSessionId(const std::string& web_session_id) {
255 web_session_to_session_id_map_.erase(web_session_id);
258 void ProxyMediaKeys::SavePromise(uint32_t session_id,
259 scoped_ptr<media::CdmPromise> promise) {
260 // Should only be one promise outstanding for any |session_id|.
261 DCHECK(!ContainsKey(session_id_to_promise_map_, session_id));
262 session_id_to_promise_map_.add(session_id, promise.Pass());
265 scoped_ptr<media::CdmPromise> ProxyMediaKeys::TakePromise(uint32_t session_id) {
266 PromiseMap::iterator it = session_id_to_promise_map_.find(session_id);
267 // May not be a promise associated with this session for asynchronous events.
268 if (it == session_id_to_promise_map_.end())
269 return scoped_ptr<media::CdmPromise>();
270 return session_id_to_promise_map_.take_and_erase(it);
273 } // namespace content