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, |
ddorwin
2015/01/14 23:09:20
This param is ignored. Should be checked for suppo
xhwang
2015/01/15 08:13:13
Checked. Not passing down due to IPC limitations.
| |
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) { |
67 // TODO(xhwang): Move these checks up to blink and DCHECK here. | 59 // TODO(xhwang): Move these checks up to blink and DCHECK here. |
68 // See http://crbug.com/342510 | 60 // See http://crbug.com/342510 |
69 CdmHostMsg_CreateSession_ContentType create_session_content_type; | 61 CdmHostMsg_CreateSession_InitDataType create_session_init_data_type; |
70 if (init_data_type == "cenc") { | 62 if (init_data_type == "cenc") { |
71 create_session_content_type = CREATE_SESSION_TYPE_MP4; | 63 create_session_init_data_type = CREATE_SESSION_TYPE_MP4; |
ddorwin
2015/01/14 23:09:20
We need to fix the "...SESSION_TYPE.." naming at s
xhwang
2015/01/15 08:13:13
Agreed.
| |
72 } else if (init_data_type == "webm") { | 64 } else if (init_data_type == "webm") { |
73 create_session_content_type = CREATE_SESSION_TYPE_WEBM; | 65 create_session_init_data_type = CREATE_SESSION_TYPE_WEBM; |
74 } else { | 66 } else { |
75 DLOG(ERROR) << "Unsupported EME CreateSession content type of " | 67 DLOG(ERROR) << "Unsupported EME CreateSession content type of " |
76 << init_data_type; | 68 << init_data_type; |
77 promise->reject( | 69 promise->reject( |
78 NOT_SUPPORTED_ERROR, | 70 NOT_SUPPORTED_ERROR, |
79 0, | 71 0, |
80 "Unsupported EME CreateSession init data type of " + init_data_type); | 72 "Unsupported EME CreateSession init data type of " + init_data_type); |
81 return; | 73 return; |
82 } | 74 } |
83 | 75 |
84 uint32 session_id = CreateSessionId(); | 76 uint32_t promise_id = cdm_promise_adapter_.SavePromise(promise.Pass()); |
85 SavePromise(session_id, promise.Pass()); | 77 manager_->CreateSessionAndGenerateRequest( |
86 manager_->CreateSession( | 78 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)); | 79 std::vector<uint8>(init_data, init_data + init_data_length)); |
91 } | 80 } |
92 | 81 |
93 void ProxyMediaKeys::LoadSession( | 82 void ProxyMediaKeys::LoadSession( |
94 SessionType session_type, | 83 SessionType session_type, |
95 const std::string& web_session_id, | 84 const std::string& session_id, |
96 scoped_ptr<media::NewSessionCdmPromise> promise) { | 85 scoped_ptr<media::NewSessionCdmPromise> promise) { |
97 // TODO(xhwang): Check key system and platform support for LoadSession in | 86 // TODO(xhwang): Check key system and platform support for LoadSession in |
98 // blink and add NOTREACHED() here. | 87 // blink and add NOTREACHED() here. |
ddorwin
2015/01/14 23:09:20
This at least depends on 384152.
xhwang
2015/01/15 08:13:13
Done.
| |
99 DLOG(ERROR) << "ProxyMediaKeys doesn't support session loading."; | 88 DLOG(ERROR) << "ProxyMediaKeys doesn't support session loading."; |
100 promise->reject(NOT_SUPPORTED_ERROR, 0, "LoadSession() is not supported."); | 89 promise->reject(NOT_SUPPORTED_ERROR, 0, "LoadSession() is not supported."); |
101 } | 90 } |
102 | 91 |
103 void ProxyMediaKeys::UpdateSession( | 92 void ProxyMediaKeys::UpdateSession( |
104 const std::string& web_session_id, | 93 const std::string& session_id, |
105 const uint8* response, | 94 const uint8* response, |
106 int response_length, | 95 int response_length, |
107 scoped_ptr<media::SimpleCdmPromise> promise) { | 96 scoped_ptr<media::SimpleCdmPromise> promise) { |
108 uint32 session_id = LookupSessionId(web_session_id); | 97 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( | 98 manager_->UpdateSession( |
116 cdm_id_, | 99 cdm_id_, promise_id, session_id, |
117 session_id, | |
118 std::vector<uint8>(response, response + response_length)); | 100 std::vector<uint8>(response, response + response_length)); |
119 } | 101 } |
120 | 102 |
121 void ProxyMediaKeys::CloseSession(const std::string& web_session_id, | 103 void ProxyMediaKeys::CloseSession(const std::string& session_id, |
122 scoped_ptr<media::SimpleCdmPromise> promise) { | 104 scoped_ptr<media::SimpleCdmPromise> promise) { |
123 uint32 session_id = LookupSessionId(web_session_id); | 105 uint32_t promise_id = cdm_promise_adapter_.SavePromise(promise.Pass()); |
124 if (!session_id) { | 106 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 } | 107 } |
132 | 108 |
133 void ProxyMediaKeys::RemoveSession( | 109 void ProxyMediaKeys::RemoveSession( |
134 const std::string& web_session_id, | 110 const std::string& session_id, |
135 scoped_ptr<media::SimpleCdmPromise> promise) { | 111 scoped_ptr<media::SimpleCdmPromise> promise) { |
136 promise->reject(NOT_SUPPORTED_ERROR, 0, "Not yet implemented."); | 112 promise->reject(NOT_SUPPORTED_ERROR, 0, "RemoveSession() not supported."); |
ddorwin
2015/01/14 23:09:20
Similar TODO here?
xhwang
2015/01/15 08:13:13
Done.
| |
137 } | 113 } |
138 | 114 |
139 media::CdmContext* ProxyMediaKeys::GetCdmContext() { | 115 media::CdmContext* ProxyMediaKeys::GetCdmContext() { |
140 return this; | 116 return this; |
141 } | 117 } |
142 | 118 |
143 media::Decryptor* ProxyMediaKeys::GetDecryptor() { | 119 media::Decryptor* ProxyMediaKeys::GetDecryptor() { |
144 return NULL; | 120 return NULL; |
145 } | 121 } |
146 | 122 |
147 int ProxyMediaKeys::GetCdmId() const { | 123 int ProxyMediaKeys::GetCdmId() const { |
148 return cdm_id_; | 124 return cdm_id_; |
149 } | 125 } |
150 | 126 |
151 void ProxyMediaKeys::OnSessionCreated(uint32 session_id, | 127 void ProxyMediaKeys::OnSessionMessage(const std::string& session_id, |
152 const std::string& web_session_id) { | 128 const std::vector<uint8>& message, |
153 AssignWebSessionId(session_id, web_session_id); | 129 const GURL& destination_url) { |
154 scoped_ptr<media::CdmPromise> promise = TakePromise(session_id); | 130 session_message_cb_.Run(session_id, message, destination_url); |
155 if (promise) { | |
156 media::NewSessionCdmPromise* session_promise( | |
157 static_cast<media::NewSessionCdmPromise*>(promise.get())); | |
158 session_promise->resolve(web_session_id); | |
159 } | |
160 } | 131 } |
161 | 132 |
162 void ProxyMediaKeys::OnSessionMessage(uint32 session_id, | 133 void ProxyMediaKeys::OnSessionClosed(const std::string& session_id) { |
163 const std::vector<uint8>& message, | 134 session_closed_cb_.Run(session_id); |
164 const GURL& destination_url) { | |
165 session_message_cb_.Run( | |
166 LookupWebSessionId(session_id), message, destination_url); | |
167 } | 135 } |
168 | 136 |
169 void ProxyMediaKeys::OnSessionReady(uint32 session_id) { | 137 void ProxyMediaKeys::OnSessionError(const std::string& session_id, |
170 scoped_ptr<media::CdmPromise> promise = TakePromise(session_id); | 138 media::MediaKeys::Exception exception, |
171 if (promise) { | 139 uint32 system_code, |
172 media::SimpleCdmPromise* simple_promise( | 140 const std::string& error_message) { |
173 static_cast<media::SimpleCdmPromise*>(promise.get())); | 141 session_error_cb_.Run(session_id, exception, system_code, error_message); |
174 simple_promise->resolve(); | |
175 } | |
176 } | 142 } |
177 | 143 |
178 void ProxyMediaKeys::OnSessionClosed(uint32 session_id) { | 144 void ProxyMediaKeys::OnSessionKeysChange(const std::string& session_id, |
179 const std::string web_session_id = LookupWebSessionId(session_id); | 145 bool has_additional_usable_key, |
180 DropWebSessionId(web_session_id); | 146 media::CdmKeysInfo keys_info) { |
181 scoped_ptr<media::CdmPromise> promise = TakePromise(session_id); | 147 session_keys_change_cb_.Run(session_id, has_additional_usable_key, |
182 if (promise) { | 148 keys_info.Pass()); |
183 media::SimpleCdmPromise* simple_promise( | |
184 static_cast<media::SimpleCdmPromise*>(promise.get())); | |
185 simple_promise->resolve(); | |
186 } else { | |
187 // It is possible for the CDM to close a session independent of a | |
188 // Release() request. | |
189 session_closed_cb_.Run(web_session_id); | |
190 } | |
191 } | 149 } |
192 | 150 |
193 void ProxyMediaKeys::OnSessionError(uint32 session_id, | 151 void ProxyMediaKeys::OnSessionExpirationUpdate( |
194 media::MediaKeys::KeyError error_code, | 152 const std::string& session_id, |
195 uint32 system_code) { | 153 const base::Time& new_expiry_time) { |
196 const std::string web_session_id = LookupWebSessionId(session_id); | 154 session_expiration_update_cb_.Run(session_id, new_expiry_time); |
197 media::MediaKeys::Exception exception_code; | 155 } |
198 switch (error_code) { | |
199 case media::MediaKeys::kClientError: | |
200 exception_code = media::MediaKeys::CLIENT_ERROR; | |
201 break; | |
202 case media::MediaKeys::kOutputError: | |
203 exception_code = media::MediaKeys::OUTPUT_ERROR; | |
204 break; | |
205 case media::MediaKeys::kUnknownError: | |
206 default: | |
207 exception_code = media::MediaKeys::UNKNOWN_ERROR; | |
208 break; | |
209 } | |
210 | 156 |
211 scoped_ptr<media::CdmPromise> promise = TakePromise(session_id); | 157 void ProxyMediaKeys::OnPromiseResolved(uint32_t promise_id) { |
212 if (promise) { | 158 cdm_promise_adapter_.ResolvePromise(promise_id); |
213 promise->reject(exception_code, system_code, std::string()); | 159 } |
214 return; | |
215 } | |
216 | 160 |
217 // Errors generally happen in response to a request, but it is possible | 161 void ProxyMediaKeys::OnPromiseResolvedWithSession( |
218 // for something bad to happen in the CDM and it needs to tell the client. | 162 uint32_t promise_id, |
219 session_error_cb_.Run( | 163 const std::string& session_id) { |
220 web_session_id, exception_code, system_code, std::string()); | 164 cdm_promise_adapter_.ResolvePromise(promise_id, session_id); |
165 } | |
166 | |
167 void ProxyMediaKeys::OnPromiseRejected(uint32_t promise_id, | |
168 media::MediaKeys::Exception exception, | |
169 uint32_t system_code, | |
170 const std::string& error_message) { | |
171 cdm_promise_adapter_.RejectPromise(promise_id, exception, system_code, | |
172 error_message); | |
221 } | 173 } |
222 | 174 |
223 ProxyMediaKeys::ProxyMediaKeys( | 175 ProxyMediaKeys::ProxyMediaKeys( |
224 RendererCdmManager* manager, | 176 RendererCdmManager* manager, |
225 const media::SessionMessageCB& session_message_cb, | 177 const media::SessionMessageCB& session_message_cb, |
226 const media::SessionClosedCB& session_closed_cb, | 178 const media::SessionClosedCB& session_closed_cb, |
227 const media::SessionErrorCB& session_error_cb) | 179 const media::SessionErrorCB& session_error_cb, |
180 const media::SessionKeysChangeCB& session_keys_change_cb, | |
181 const media::SessionExpirationUpdateCB& session_expiration_update_cb) | |
228 : manager_(manager), | 182 : manager_(manager), |
229 session_message_cb_(session_message_cb), | 183 session_message_cb_(session_message_cb), |
230 session_closed_cb_(session_closed_cb), | 184 session_closed_cb_(session_closed_cb), |
231 session_error_cb_(session_error_cb), | 185 session_error_cb_(session_error_cb), |
232 next_session_id_(1) { | 186 session_keys_change_cb_(session_keys_change_cb), |
187 session_expiration_update_cb_(session_expiration_update_cb) { | |
233 cdm_id_ = manager->RegisterMediaKeys(this); | 188 cdm_id_ = manager->RegisterMediaKeys(this); |
234 } | 189 } |
235 | 190 |
236 void ProxyMediaKeys::InitializeCdm(const std::string& key_system, | 191 void ProxyMediaKeys::InitializeCdm(const std::string& key_system, |
237 const GURL& security_origin) { | 192 const GURL& security_origin) { |
238 manager_->InitializeCdm(cdm_id_, this, key_system, security_origin); | 193 manager_->InitializeCdm(cdm_id_, this, key_system, security_origin); |
239 } | 194 } |
240 | 195 |
241 uint32_t ProxyMediaKeys::CreateSessionId() { | |
242 return next_session_id_++; | |
243 } | |
244 | |
245 void ProxyMediaKeys::AssignWebSessionId(uint32_t session_id, | |
246 const std::string& web_session_id) { | |
247 DCHECK(!ContainsKey(web_session_to_session_id_map_, web_session_id)); | |
248 DCHECK(session_id); | |
249 web_session_to_session_id_map_.insert( | |
250 std::make_pair(web_session_id, session_id)); | |
251 } | |
252 | |
253 uint32_t ProxyMediaKeys::LookupSessionId( | |
254 const std::string& web_session_id) const { | |
255 SessionIdMap::const_iterator it = | |
256 web_session_to_session_id_map_.find(web_session_id); | |
257 return (it != web_session_to_session_id_map_.end()) ? it->second : 0; | |
258 } | |
259 | |
260 std::string ProxyMediaKeys::LookupWebSessionId(uint32_t session_id) const { | |
261 for (SessionIdMap::const_iterator it = web_session_to_session_id_map_.begin(); | |
262 it != web_session_to_session_id_map_.end(); | |
263 ++it) { | |
264 if (it->second == session_id) | |
265 return it->first; | |
266 } | |
267 // Possible to get an error creating a session, so no |web_session_id| | |
268 // available. | |
269 return std::string(); | |
270 } | |
271 | |
272 void ProxyMediaKeys::DropWebSessionId(const std::string& web_session_id) { | |
273 web_session_to_session_id_map_.erase(web_session_id); | |
274 } | |
275 | |
276 void ProxyMediaKeys::SavePromise(uint32_t session_id, | |
277 scoped_ptr<media::CdmPromise> promise) { | |
278 // Should only be one promise outstanding for any |session_id|. | |
279 DCHECK(!ContainsKey(session_id_to_promise_map_, session_id)); | |
280 session_id_to_promise_map_.add(session_id, promise.Pass()); | |
281 } | |
282 | |
283 scoped_ptr<media::CdmPromise> ProxyMediaKeys::TakePromise(uint32_t session_id) { | |
284 PromiseMap::iterator it = session_id_to_promise_map_.find(session_id); | |
285 // May not be a promise associated with this session for asynchronous events. | |
286 if (it == session_id_to_promise_map_.end()) | |
287 return scoped_ptr<media::CdmPromise>(); | |
288 return session_id_to_promise_map_.take_and_erase(it); | |
289 } | |
290 | |
291 } // namespace content | 196 } // namespace content |
OLD | NEW |