OLD | NEW |
---|---|
(Empty) | |
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. | |
4 | |
5 #include "content/browser/media/cdm/browser_cdm_manager.h" | |
6 | |
7 #include "base/command_line.h" | |
8 #include "base/stl_util.h" | |
9 #include "content/common/media/cdm_messages.h" | |
10 #include "content/public/browser/browser_context.h" | |
11 #include "content/public/browser/render_frame_host.h" | |
12 #include "content/public/browser/render_process_host.h" | |
13 #include "content/public/browser/render_view_host.h" | |
14 #include "content/public/browser/web_contents.h" | |
15 #include "content/public/common/content_client.h" | |
16 #include "content/public/common/content_switches.h" | |
17 #include "media/base/browser_cdm.h" | |
18 #include "media/base/browser_cdm_factory.h" | |
19 #include "media/base/media_switches.h" | |
20 | |
21 namespace content { | |
22 | |
23 using media::MediaKeys; | |
ddorwin
2014/06/04 01:02:30
order
xhwang
2014/06/04 18:54:07
Done.
| |
24 using media::BrowserCdm; | |
25 | |
26 // Maximum lengths for various EME API parameters. These are checks to | |
27 // prevent unnecessarily large parameters from being passed around, and the | |
28 // lengths are somewhat arbitrary as the EME spec doesn't specify any limits. | |
29 const size_t kMaxInitDataLength = 64 * 1024; // 64 KB | |
30 const size_t kMaxSessionResponseLength = 64 * 1024; // 64 KB | |
31 const size_t kMaxKeySystemLength = 256; | |
32 | |
33 // static | |
34 BrowserCdmManager* BrowserCdmManager::Create(RenderFrameHost* rfh) { | |
35 return new BrowserCdmManager(rfh); | |
36 } | |
37 | |
38 BrowserCdmManager::BrowserCdmManager(RenderFrameHost* render_frame_host) | |
39 : render_frame_host_(render_frame_host), | |
40 web_contents_(WebContents::FromRenderFrameHost(render_frame_host)), | |
41 weak_ptr_factory_(this) { | |
42 } | |
43 | |
44 BrowserCdmManager::~BrowserCdmManager() { | |
xhwang
2014/06/03 22:43:08
There could be some destruction order issues here.
| |
45 for (CdmMap::iterator it = cdm_map_.begin(); it != cdm_map_.end(); ++it) | |
ddorwin
2014/06/04 01:02:30
Does stl_utils handle this?
xhwang
2014/06/04 18:54:07
Good point. Done.
| |
46 delete it->second; | |
47 } | |
48 | |
49 BrowserCdm* BrowserCdmManager::GetCdm(int cdm_id) { | |
50 CdmMap::const_iterator iter = cdm_map_.find(cdm_id); | |
51 return (iter == cdm_map_.end()) ? NULL : iter->second; | |
52 } | |
53 | |
54 void BrowserCdmManager::OnSessionCreated( | |
55 int cdm_id, | |
56 uint32 session_id, | |
57 const std::string& web_session_id) { | |
58 Send(new CdmMsg_SessionCreated( | |
59 RoutingID(), cdm_id, session_id, web_session_id)); | |
60 } | |
61 | |
62 void BrowserCdmManager::OnSessionMessage( | |
63 int cdm_id, | |
64 uint32 session_id, | |
65 const std::vector<uint8>& message, | |
66 const GURL& destination_url) { | |
67 GURL verified_gurl = destination_url; | |
68 if (!verified_gurl.is_valid() && !verified_gurl.is_empty()) { | |
69 DLOG(WARNING) << "SessionMessage destination_url is invalid : " | |
70 << destination_url.possibly_invalid_spec(); | |
71 verified_gurl = GURL::EmptyGURL(); // Replace invalid destination_url. | |
72 } | |
73 | |
74 Send(new CdmMsg_SessionMessage( | |
75 RoutingID(), cdm_id, session_id, message, verified_gurl)); | |
76 } | |
77 | |
78 void BrowserCdmManager::OnSessionReady(int cdm_id, uint32 session_id) { | |
79 Send(new CdmMsg_SessionReady(RoutingID(), cdm_id, session_id)); | |
80 } | |
81 | |
82 void BrowserCdmManager::OnSessionClosed(int cdm_id, uint32 session_id) { | |
83 Send(new CdmMsg_SessionClosed(RoutingID(), cdm_id, session_id)); | |
84 } | |
85 | |
86 void BrowserCdmManager::OnSessionError(int cdm_id, | |
87 uint32 session_id, | |
88 MediaKeys::KeyError error_code, | |
89 uint32 system_code) { | |
90 Send(new CdmMsg_SessionError( | |
91 RoutingID(), cdm_id, session_id, error_code, system_code)); | |
92 } | |
93 | |
94 void BrowserCdmManager::OnInitializeCdm(int cdm_id, | |
95 const std::string& key_system, | |
96 const GURL& security_origin) { | |
97 if (key_system.size() > kMaxKeySystemLength) { | |
98 // This failure will be discovered and reported by OnCreateSession() | |
99 // as GetCdm() will return null. | |
100 NOTREACHED() << "Invalid key system: " << key_system; | |
101 return; | |
102 } | |
103 | |
104 AddCdm(cdm_id, key_system, security_origin); | |
105 } | |
106 | |
107 void BrowserCdmManager::OnCreateSession( | |
108 int cdm_id, | |
109 uint32 session_id, | |
110 CdmHostMsg_CreateSession_ContentType content_type, | |
111 const std::vector<uint8>& init_data) { | |
112 if (init_data.size() > kMaxInitDataLength) { | |
113 LOG(WARNING) << "InitData for ID: " << cdm_id | |
114 << " too long: " << init_data.size(); | |
115 OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0); | |
116 return; | |
117 } | |
118 | |
119 // Convert the session content type into a MIME type. "audio" and "video" | |
120 // don't matter, so using "video" for the MIME type. | |
121 // Ref: | |
122 // https://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/encrypte d-media.html#dom-createsession | |
123 std::string mime_type; | |
124 switch (content_type) { | |
125 case CREATE_SESSION_TYPE_WEBM: | |
126 mime_type = "video/webm"; | |
127 break; | |
128 case CREATE_SESSION_TYPE_MP4: | |
129 mime_type = "video/mp4"; | |
130 break; | |
131 default: | |
132 NOTREACHED(); | |
133 return; | |
134 } | |
135 | |
136 if (CommandLine::ForCurrentProcess() | |
137 ->HasSwitch(switches::kDisableInfobarForProtectedMediaIdentifier)) { | |
138 CreateSessionIfPermitted(cdm_id, session_id, mime_type, init_data, true); | |
139 return; | |
140 } | |
141 | |
142 BrowserCdm* cdm = GetCdm(cdm_id); | |
143 if (!cdm) { | |
144 DLOG(WARNING) << "No CDM for ID " << cdm_id << " found"; | |
145 OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0); | |
146 return; | |
147 } | |
148 | |
149 BrowserContext* context = | |
150 web_contents_->GetRenderProcessHost()->GetBrowserContext(); | |
151 | |
152 std::map<int, GURL>::const_iterator iter = | |
153 cdm_security_origin_map_.find(cdm_id); | |
154 if (iter == cdm_security_origin_map_.end()) { | |
155 NOTREACHED(); | |
156 OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0); | |
157 return; | |
158 } | |
159 | |
160 context->RequestProtectedMediaIdentifierPermission( | |
161 web_contents_->GetRenderProcessHost()->GetID(), | |
162 web_contents_->GetRenderViewHost()->GetRoutingID(), | |
163 iter->second, | |
164 base::Bind(&BrowserCdmManager::CreateSessionIfPermitted, | |
165 weak_ptr_factory_.GetWeakPtr(), | |
166 cdm_id, | |
167 session_id, | |
168 mime_type, | |
169 init_data)); | |
170 } | |
171 | |
172 void BrowserCdmManager::OnUpdateSession( | |
173 int cdm_id, | |
174 uint32 session_id, | |
175 const std::vector<uint8>& response) { | |
176 BrowserCdm* cdm = GetCdm(cdm_id); | |
177 if (!cdm) { | |
178 DLOG(WARNING) << "No CDM for ID " << cdm_id << " found"; | |
179 OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0); | |
180 return; | |
181 } | |
182 | |
183 if (response.size() > kMaxSessionResponseLength) { | |
184 LOG(WARNING) << "Response for ID " << cdm_id | |
185 << " is too long: " << response.size(); | |
186 OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0); | |
187 return; | |
188 } | |
189 | |
190 cdm->UpdateSession(session_id, &response[0], response.size()); | |
191 } | |
192 | |
193 void BrowserCdmManager::OnReleaseSession(int cdm_id, uint32 session_id) { | |
194 BrowserCdm* cdm = GetCdm(cdm_id); | |
195 if (!cdm) { | |
196 DLOG(WARNING) << "No CDM for ID " << cdm_id << " found"; | |
197 OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0); | |
198 return; | |
199 } | |
200 | |
201 cdm->ReleaseSession(session_id); | |
202 } | |
203 | |
204 void BrowserCdmManager::OnDestroyCdm(int cdm_id) { | |
205 BrowserCdm* cdm = GetCdm(cdm_id); | |
206 if (!cdm) | |
207 return; | |
208 | |
209 CancelAllPendingSessionCreations(cdm_id); | |
210 RemoveCdm(cdm_id); | |
211 } | |
212 | |
213 void BrowserCdmManager::CancelAllPendingSessionCreations(int cdm_id) { | |
214 BrowserContext* context = | |
215 web_contents_->GetRenderProcessHost()->GetBrowserContext(); | |
216 std::map<int, GURL>::const_iterator iter = | |
217 cdm_security_origin_map_.find(cdm_id); | |
218 if (iter == cdm_security_origin_map_.end()) | |
219 return; | |
220 context->CancelProtectedMediaIdentifierPermissionRequests( | |
221 web_contents_->GetRenderProcessHost()->GetID(), | |
222 web_contents_->GetRenderViewHost()->GetRoutingID(), | |
223 iter->second); | |
224 } | |
225 | |
226 void BrowserCdmManager::AddCdm(int cdm_id, | |
227 const std::string& key_system, | |
228 const GURL& security_origin) { | |
229 DCHECK(!GetCdm(cdm_id)); | |
230 base::WeakPtr<BrowserCdmManager> weak_this = weak_ptr_factory_.GetWeakPtr(); | |
231 scoped_ptr<BrowserCdm> cdm(media::CreateBrowserCdm( | |
232 key_system, | |
233 base::Bind(&BrowserCdmManager::OnSessionCreated, weak_this, cdm_id), | |
234 base::Bind(&BrowserCdmManager::OnSessionMessage, weak_this, cdm_id), | |
235 base::Bind(&BrowserCdmManager::OnSessionReady, weak_this, cdm_id), | |
236 base::Bind(&BrowserCdmManager::OnSessionClosed, weak_this, cdm_id), | |
237 base::Bind(&BrowserCdmManager::OnSessionError, weak_this, cdm_id))); | |
238 | |
239 if (!cdm) { | |
240 // This failure will be discovered and reported by OnCreateSession() | |
241 // as GetCdm() will return null. | |
242 DVLOG(1) << "failed to create CDM."; | |
243 return; | |
244 } | |
245 | |
246 cdm_map_[cdm_id] = cdm.release(); | |
247 cdm_security_origin_map_[cdm_id] = security_origin; | |
248 } | |
249 | |
250 void BrowserCdmManager::RemoveCdm(int cdm_id) { | |
251 // TODO(xhwang): Detach CDM from the player it's set to. In prefixed | |
252 // EME implementation the current code is fine because we always destroy the | |
253 // player before we destroy the DrmBridge. This will not always be the case | |
254 // in unprefixed EME implementation. | |
255 CdmMap::iterator iter = cdm_map_.find(cdm_id); | |
256 if (iter != cdm_map_.end()) { | |
257 delete iter->second; | |
258 cdm_map_.erase(iter); | |
259 } | |
260 cdm_security_origin_map_.erase(cdm_id); | |
261 } | |
262 | |
263 int BrowserCdmManager::RoutingID() { | |
264 return render_frame_host_->GetRoutingID(); | |
265 } | |
266 | |
267 bool BrowserCdmManager::Send(IPC::Message* msg) { | |
268 return render_frame_host_->Send(msg); | |
269 } | |
270 | |
271 void BrowserCdmManager::CreateSessionIfPermitted( | |
272 int cdm_id, | |
273 uint32 session_id, | |
274 const std::string& content_type, | |
275 const std::vector<uint8>& init_data, | |
276 bool permitted) { | |
277 if (!permitted) { | |
278 OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0); | |
279 return; | |
280 } | |
281 | |
282 BrowserCdm* cdm = GetCdm(cdm_id); | |
283 if (!cdm) { | |
284 DLOG(WARNING) << "No CDM for ID: " << cdm_id << " found"; | |
285 OnSessionError(cdm_id, session_id, MediaKeys::kUnknownError, 0); | |
286 return; | |
287 } | |
288 | |
289 // This could fail, in which case a SessionError will be fired. | |
290 cdm->CreateSession(session_id, content_type, &init_data[0], init_data.size()); | |
291 } | |
292 | |
293 } // namespace content | |
OLD | NEW |