OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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/browser/renderer_host/media/media_stream_device_settings.h" | 5 #include "content/browser/renderer_host/media/media_stream_device_settings.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/callback.h" | 10 #include "base/callback.h" |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
79 }; | 79 }; |
80 | 80 |
81 } // namespace | 81 } // namespace |
82 | 82 |
83 namespace media_stream { | 83 namespace media_stream { |
84 | 84 |
85 typedef std::map<MediaStreamType, StreamDeviceInfoArray> DeviceMap; | 85 typedef std::map<MediaStreamType, StreamDeviceInfoArray> DeviceMap; |
86 | 86 |
87 // Device request contains all data needed to keep track of requests between the | 87 // Device request contains all data needed to keep track of requests between the |
88 // different calls. | 88 // different calls. |
89 class MediaStreamDeviceSettingsRequest : public MediaStreamRequest { | 89 struct MediaStreamDeviceSettingsRequest : public MediaStreamRequest { |
90 public: | 90 public: |
91 MediaStreamDeviceSettingsRequest( | 91 MediaStreamDeviceSettingsRequest( |
92 int render_pid, | 92 int render_pid, |
93 int render_vid, | 93 int render_vid, |
94 const GURL& origin, | 94 const GURL& origin, |
95 const StreamOptions& request_options) | 95 const StreamOptions& request_options) |
96 : MediaStreamRequest(render_pid, render_vid, origin), | 96 : MediaStreamRequest(render_pid, render_vid, origin), |
97 options(request_options), | 97 options(request_options), |
98 posted_task(false) {} | 98 posted_task(false) {} |
99 | 99 |
100 ~MediaStreamDeviceSettingsRequest() {} | 100 ~MediaStreamDeviceSettingsRequest() {} |
101 | 101 |
102 // Request options. | 102 // Request options. |
103 StreamOptions options; | 103 StreamOptions options; |
104 // Map containing available devices for the requested capture types. | 104 // Map containing available devices for the requested capture types. |
105 // Note, never call devices_full[stream_type].empty() before making sure | |
106 // that type of device has existed on the map, otherwise it will create an | |
107 // empty device entry on the map. | |
105 DeviceMap devices_full; | 108 DeviceMap devices_full; |
106 // Whether or not a task was posted to make the call to | 109 // Whether or not a task was posted to make the call to |
107 // RequestMediaAccessPermission, to make sure that we never post twice to it. | 110 // RequestMediaAccessPermission, to make sure that we never post twice to it. |
108 bool posted_task; | 111 bool posted_task; |
109 }; | 112 }; |
110 | 113 |
111 namespace { | 114 namespace { |
112 | 115 |
113 // Sends the request to the appropriate WebContents. | 116 // Sends the request to the appropriate WebContents. |
114 void DoDeviceRequest( | 117 void DoDeviceRequest(const MediaStreamDeviceSettingsRequest& request, |
115 const MediaStreamDeviceSettingsRequest& request, | 118 const content::MediaResponseCallback& callback) { |
116 const content::MediaResponseCallback& callback) { | |
117 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 119 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
118 | 120 |
119 // Send the permission request to the web contents. | 121 // Send the permission request to the web contents. |
120 content::RenderViewHostImpl* host = | 122 content::RenderViewHostImpl* host = |
121 content::RenderViewHostImpl::FromID(request.render_process_id, | 123 content::RenderViewHostImpl::FromID(request.render_process_id, |
122 request.render_view_id); | 124 request.render_view_id); |
123 | 125 |
124 // Tab may have gone away. | 126 // Tab may have gone away. |
125 if (!host || !host->GetDelegate()) { | 127 if (!host || !host->GetDelegate()) { |
126 callback.Run(content::MediaStreamDevices()); | 128 callback.Run(content::MediaStreamDevices()); |
127 return; | 129 return; |
128 } | 130 } |
129 | 131 |
130 host->GetDelegate()->RequestMediaAccessPermission(&request, callback); | 132 host->GetDelegate()->RequestMediaAccessPermission(&request, callback); |
131 } | 133 } |
132 | 134 |
135 bool IsRequestReadyForView( | |
136 media_stream::MediaStreamDeviceSettingsRequest* request) { | |
137 if ((request->options.audio && request->devices_full.count( | |
138 content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE) == 0) || | |
139 (request->options.video && request->devices_full.count( | |
140 content::MEDIA_STREAM_DEVICE_TYPE_VIDEO_CAPTURE) == 0)) | |
141 return false; | |
scherkus (not reviewing)
2012/08/13 19:51:22
please use {} for if-statements spanning >1 lines
no longer working on chromium
2012/08/14 11:17:04
Done.
| |
142 | |
143 // We have got all the requested devices, it is ready if it has not | |
144 // been posted for UI yet. | |
145 return !request->posted_task; | |
146 } | |
147 | |
133 } // namespace | 148 } // namespace |
134 | 149 |
135 MediaStreamDeviceSettings::MediaStreamDeviceSettings( | 150 MediaStreamDeviceSettings::MediaStreamDeviceSettings( |
136 SettingsRequester* requester) | 151 SettingsRequester* requester) |
137 : requester_(requester), | 152 : requester_(requester), |
138 use_fake_ui_(false), | 153 use_fake_ui_(false), |
139 weak_ptr_factory_(this) { | 154 weak_ptr_factory_(this) { |
140 DCHECK(requester_); | 155 DCHECK(requester_); |
141 } | 156 } |
142 | 157 |
143 MediaStreamDeviceSettings::~MediaStreamDeviceSettings() { | 158 MediaStreamDeviceSettings::~MediaStreamDeviceSettings() { |
144 STLDeleteValues(&requests_); | 159 STLDeleteValues(&requests_); |
145 } | 160 } |
146 | 161 |
147 void MediaStreamDeviceSettings::RequestCaptureDeviceUsage( | 162 void MediaStreamDeviceSettings::RequestCaptureDeviceUsage( |
148 const std::string& label, int render_process_id, int render_view_id, | 163 const std::string& label, int render_process_id, int render_view_id, |
149 const StreamOptions& request_options, const GURL& security_origin) { | 164 const StreamOptions& request_options, const GURL& security_origin) { |
150 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 165 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
151 | 166 DCHECK(requests_.find(label) == requests_.end()); |
152 if (requests_.find(label) != requests_.end()) { | |
153 // Request with this id already exists. | |
154 requester_->SettingsError(label); | |
155 return; | |
156 } | |
157 | 167 |
158 // Create a new request. | 168 // Create a new request. |
159 requests_.insert(std::make_pair(label, new MediaStreamDeviceSettingsRequest( | 169 requests_.insert(std::make_pair(label, new MediaStreamDeviceSettingsRequest( |
160 render_process_id, render_view_id, security_origin, request_options))); | 170 render_process_id, render_view_id, security_origin, request_options))); |
161 } | 171 } |
162 | 172 |
163 void MediaStreamDeviceSettings::RemovePendingCaptureRequest( | 173 void MediaStreamDeviceSettings::RemovePendingCaptureRequest( |
164 const std::string& label) { | 174 const std::string& label) { |
165 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 175 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
166 | |
167 SettingsRequests::iterator request_it = requests_.find(label); | 176 SettingsRequests::iterator request_it = requests_.find(label); |
168 if (request_it != requests_.end()) { | 177 if (request_it != requests_.end()) { |
169 // Proceed the next pending request for the same page. | 178 // Proceed the next pending request for the same page. |
170 MediaStreamDeviceSettingsRequest* request = request_it->second; | 179 MediaStreamDeviceSettingsRequest* request = request_it->second; |
171 std::string new_label = FindReadyRequestForView(request->render_view_id, | 180 int render_view_id = request->render_view_id; |
172 request->render_process_id); | 181 int render_process_id = request->render_process_id; |
173 if (!new_label.empty()) { | 182 bool was_posted = request->posted_task; |
174 PostRequestToUi(new_label); | |
175 } | |
176 | 183 |
177 // TODO(xians): Post a cancel request on UI thread to dismiss the infobar | 184 // TODO(xians): Post a cancel request on UI thread to dismiss the infobar |
178 // if request has been sent to the UI. | 185 // if request has been sent to the UI. |
179 // Remove the request from the queue. | 186 // Remove the request from the queue. |
180 requests_.erase(request_it); | 187 requests_.erase(request_it); |
181 delete request; | 188 delete request; |
189 | |
190 // Simply return if the canceled request has not been brought to UI. | |
191 if (!was_posted) | |
192 return; | |
193 | |
194 // Process the next pending request to replace the old infobar on the same | |
195 // page. | |
196 ProcessNextRequestForView(render_view_id, render_process_id); | |
182 } | 197 } |
183 } | 198 } |
184 | 199 |
185 void MediaStreamDeviceSettings::AvailableDevices( | 200 void MediaStreamDeviceSettings::AvailableDevices( |
186 const std::string& label, | 201 const std::string& label, |
187 MediaStreamType stream_type, | 202 MediaStreamType stream_type, |
188 const StreamDeviceInfoArray& devices) { | 203 const StreamDeviceInfoArray& devices) { |
189 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 204 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
190 | 205 |
191 SettingsRequests::iterator request_it = requests_.find(label); | 206 SettingsRequests::iterator request_it = requests_.find(label); |
192 DCHECK(request_it != requests_.end()); | 207 DCHECK(request_it != requests_.end()); |
193 | |
194 // Add the answer for the request. | 208 // Add the answer for the request. |
195 MediaStreamDeviceSettingsRequest* request = request_it->second; | 209 MediaStreamDeviceSettingsRequest* request = request_it->second; |
196 DCHECK_EQ(request->devices_full.count(stream_type), static_cast<size_t>(0)) << | 210 DCHECK_EQ(request->devices_full.count(stream_type), static_cast<size_t>(0)) |
197 "This request already has a list of devices for this stream type."; | 211 << "This request already has a list of devices for this stream type."; |
198 request->devices_full[stream_type] = devices; | 212 request->devices_full[stream_type] = devices; |
199 | 213 |
200 // Check if we're done. | 214 if (IsRequestReadyForView(request)) { |
scherkus (not reviewing)
2012/08/13 19:51:22
nit: prefer if-and-early-return instead of deeply
no longer working on chromium
2012/08/14 11:17:04
Done.
| |
201 size_t num_media_requests = 0; | 215 if (use_fake_ui_) { |
202 if (request->options.audio) { | 216 PostRequestToFakeUI(label); |
203 num_media_requests++; | 217 } else if (!IsUiBusy(request->render_view_id, request->render_process_id)) { |
204 } | 218 // The UI can handle only one request at the time, post the request |
205 if (request->options.video) { | 219 // to the view only if the UI is not handling any other request. |
206 num_media_requests++; | |
207 } | |
208 | |
209 if (request->devices_full.size() == num_media_requests) { | |
210 // We have all answers needed. | |
211 if (!use_fake_ui_) { | |
212 // Abort if the task was already posted: wait for it to PostResponse. | |
213 if (request->posted_task) { | |
214 return; | |
215 } | |
216 // Since the UI can only handle one request at the time, verify there | |
217 // is no unanswered request posted for this view. If there is, this | |
218 // new request will be handled once we get a response for the first one. | |
219 if (IsUiBusy(request->render_view_id, request->render_process_id)) { | |
220 return; | |
221 } | |
222 PostRequestToUi(label); | 220 PostRequestToUi(label); |
223 } else { | |
224 // Used to fake UI, which is needed for server based testing. | |
225 // Choose first non-opened device for each media type. | |
226 StreamDeviceInfoArray devices_to_use; | |
227 for (DeviceMap::iterator it = request->devices_full.begin(); | |
228 it != request->devices_full.end(); ++it) { | |
229 for (StreamDeviceInfoArray::iterator device_it = it->second.begin(); | |
230 device_it != it->second.end(); ++device_it) { | |
231 if (!device_it->in_use) { | |
232 devices_to_use.push_back(*device_it); | |
233 break; | |
234 } | |
235 } | |
236 } | |
237 | |
238 if (!request->devices_full[ | |
239 content::MEDIA_STREAM_DEVICE_TYPE_VIDEO_CAPTURE].empty() && | |
240 num_media_requests != devices_to_use.size()) { | |
241 // Not all requested device types were opened. This happens if all | |
242 // video capture devices are already opened, |in_use| isn't set for | |
243 // audio devices. Allow the first video capture device in the list to be | |
244 // opened for this user too. | |
245 StreamDeviceInfoArray device_array = | |
246 request->devices_full[ | |
247 content::MEDIA_STREAM_DEVICE_TYPE_VIDEO_CAPTURE]; | |
248 devices_to_use.push_back(*(device_array.begin())); | |
249 } | |
250 | |
251 // Post result and delete request. | |
252 requester_->DevicesAccepted(label, devices_to_use); | |
253 requests_.erase(request_it); | |
254 delete request; | |
255 } | 221 } |
256 } | 222 } |
257 } | 223 } |
258 | 224 |
259 void MediaStreamDeviceSettings::PostResponse( | 225 void MediaStreamDeviceSettings::PostResponse( |
260 const std::string& label, | 226 const std::string& label, |
261 const content::MediaStreamDevices& devices) { | 227 const content::MediaStreamDevices& devices) { |
262 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 228 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
263 | |
264 SettingsRequests::iterator req = requests_.find(label); | 229 SettingsRequests::iterator req = requests_.find(label); |
265 // Return if the request has been removed. | 230 // Return if the request has been removed. |
266 if (req == requests_.end()) | 231 if (req == requests_.end()) |
267 return; | 232 return; |
268 | 233 |
269 DCHECK(requester_); | 234 DCHECK(requester_); |
270 MediaStreamDeviceSettingsRequest* request = req->second; | 235 scoped_ptr<MediaStreamDeviceSettingsRequest> request(req->second); |
271 requests_.erase(req); | 236 requests_.erase(req); |
272 | 237 |
273 // Look for queued requests for the same view. If there is a pending request, | 238 // Look for queued requests for the same view. If there is a pending request, |
274 // post it for user approval. | 239 // post it for user approval. |
275 std::string new_label = FindReadyRequestForView(request->render_view_id, | 240 ProcessNextRequestForView(request->render_view_id, |
276 request->render_process_id); | 241 request->render_process_id); |
277 if (!new_label.empty()) { | |
278 PostRequestToUi(new_label); | |
279 } | |
280 | 242 |
281 if (devices.size() > 0) { | 243 if (devices.size() > 0) { |
282 // Build a list of "full" device objects for the accepted devices. | 244 // Build a list of "full" device objects for the accepted devices. |
283 StreamDeviceInfoArray deviceList; | 245 StreamDeviceInfoArray deviceList; |
284 for (content::MediaStreamDevices::const_iterator dev = devices.begin(); | 246 for (content::MediaStreamDevices::const_iterator dev = devices.begin(); |
285 dev != devices.end(); ++dev) { | 247 dev != devices.end(); ++dev) { |
286 DeviceMap::iterator subList = request->devices_full.find(dev->type); | 248 DeviceMap::iterator subList = request->devices_full.find(dev->type); |
287 DCHECK(subList != request->devices_full.end()); | 249 DCHECK(subList != request->devices_full.end()); |
288 | 250 |
289 deviceList.push_back(*std::find_if(subList->second.begin(), | 251 deviceList.push_back(*std::find_if(subList->second.begin(), |
290 subList->second.end(), DeviceIdEquals(dev->device_id))); | 252 subList->second.end(), DeviceIdEquals(dev->device_id))); |
291 } | 253 } |
292 requester_->DevicesAccepted(label, deviceList); | 254 requester_->DevicesAccepted(label, deviceList); |
293 } else { | 255 } else { |
294 requester_->SettingsError(label); | 256 requester_->SettingsError(label); |
295 } | 257 } |
296 delete request; | |
297 } | 258 } |
298 | 259 |
299 void MediaStreamDeviceSettings::UseFakeUI() { | 260 void MediaStreamDeviceSettings::UseFakeUI() { |
300 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 261 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
301 use_fake_ui_ = true; | 262 use_fake_ui_ = true; |
302 } | 263 } |
303 | 264 |
304 bool MediaStreamDeviceSettings::IsUiBusy(int render_view_id, | 265 bool MediaStreamDeviceSettings::IsUiBusy(int render_view_id, |
305 int render_process_id) { | 266 int render_process_id) { |
306 for (SettingsRequests::iterator it = requests_.begin(); | 267 for (SettingsRequests::iterator it = requests_.begin(); |
307 it != requests_.end(); ++it) { | 268 it != requests_.end(); ++it) { |
308 if (it->second->render_process_id == render_process_id && | 269 if (it->second->render_process_id == render_process_id && |
309 it->second->render_view_id == render_view_id && | 270 it->second->render_view_id == render_view_id && |
310 it->second->posted_task) { | 271 it->second->posted_task) { |
311 return true; | 272 return true; |
312 } | 273 } |
313 } | 274 } |
314 return false; | 275 return false; |
315 } | 276 } |
316 | 277 |
317 std::string MediaStreamDeviceSettings::FindReadyRequestForView( | 278 void MediaStreamDeviceSettings::ProcessNextRequestForView( |
318 int render_view_id, int render_process_id) { | 279 int render_view_id, int render_process_id) { |
280 std::string new_label; | |
319 for (SettingsRequests::iterator it = requests_.begin(); it != requests_.end(); | 281 for (SettingsRequests::iterator it = requests_.begin(); it != requests_.end(); |
320 ++it) { | 282 ++it) { |
321 if (it->second->render_process_id == render_process_id && | 283 if (it->second->render_process_id == render_process_id && |
322 it->second->render_view_id == render_view_id) { | 284 it->second->render_view_id == render_view_id) { |
323 // This request belongs to the given render view. | 285 // This request belongs to the given render view. |
324 MediaStreamDeviceSettingsRequest* request = it->second; | 286 MediaStreamDeviceSettingsRequest* request = it->second; |
325 if (request->options.audio && | 287 if (IsRequestReadyForView(request)) { |
326 request->devices_full[ | 288 new_label = it->first; |
327 content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE].empty()) { | 289 break; |
328 // Audio requested, but no devices enumerated yet. Continue to next | |
329 // request. | |
330 continue; | |
331 } | 290 } |
332 if (request->options.video && | |
333 request->devices_full[ | |
334 content::MEDIA_STREAM_DEVICE_TYPE_VIDEO_CAPTURE].empty()) { | |
335 // Video requested, but no devices enumerated yet. Continue to next | |
336 // request. | |
337 continue; | |
338 } | |
339 // This request belongs to the same view as the treated request and is | |
340 // ready to be requested. Return its label. | |
341 return it->first; | |
342 } | 291 } |
343 } | 292 } |
344 return std::string(); | 293 |
294 if (!new_label.empty()) { | |
scherkus (not reviewing)
2012/08/13 19:51:22
ditto for early return
no longer working on chromium
2012/08/14 11:17:04
Done.
| |
295 if (use_fake_ui_) | |
296 PostRequestToFakeUI(new_label); | |
297 else | |
298 PostRequestToUi(new_label); | |
299 } | |
345 } | 300 } |
346 | 301 |
347 void MediaStreamDeviceSettings::PostRequestToUi(const std::string& label) { | 302 void MediaStreamDeviceSettings::PostRequestToUi(const std::string& label) { |
348 MediaStreamDeviceSettingsRequest* request = requests_[label]; | 303 MediaStreamDeviceSettingsRequest* request = requests_[label]; |
349 DCHECK(request != NULL); | 304 DCHECK(request != NULL); |
350 | 305 |
351 request->posted_task = true; | 306 request->posted_task = true; |
352 | 307 |
353 // Create the simplified list of devices. | 308 // Create the simplified list of devices. |
354 for (DeviceMap::iterator it = request->devices_full.begin(); | 309 for (DeviceMap::iterator it = request->devices_full.begin(); |
(...skipping 10 matching lines...) Expand all Loading... | |
365 new ResponseCallbackHelper(weak_ptr_factory_.GetWeakPtr()); | 320 new ResponseCallbackHelper(weak_ptr_factory_.GetWeakPtr()); |
366 content::MediaResponseCallback callback = | 321 content::MediaResponseCallback callback = |
367 base::Bind(&ResponseCallbackHelper::PostResponse, | 322 base::Bind(&ResponseCallbackHelper::PostResponse, |
368 helper.get(), label); | 323 helper.get(), label); |
369 | 324 |
370 BrowserThread::PostTask( | 325 BrowserThread::PostTask( |
371 BrowserThread::UI, FROM_HERE, | 326 BrowserThread::UI, FROM_HERE, |
372 base::Bind(&DoDeviceRequest, *request, callback)); | 327 base::Bind(&DoDeviceRequest, *request, callback)); |
373 } | 328 } |
374 | 329 |
330 void MediaStreamDeviceSettings::PostRequestToFakeUI(const std::string& label) { | |
331 SettingsRequests::iterator request_it = requests_.find(label); | |
332 DCHECK(request_it != requests_.end()); | |
333 MediaStreamDeviceSettingsRequest* request = request_it->second; | |
334 // Used to fake UI, which is needed for server based testing. | |
335 // Choose first non-opened device for each media type. | |
336 content::MediaStreamDevices devices_to_use; | |
337 for (DeviceMap::iterator it = request->devices_full.begin(); | |
338 it != request->devices_full.end(); ++it) { | |
339 StreamDeviceInfoArray::iterator device_it = it->second.begin(); | |
340 for (; device_it != it->second.end(); ++device_it) { | |
341 if (!device_it->in_use) { | |
342 devices_to_use.push_back(content::MediaStreamDevice( | |
343 device_it->stream_type, device_it->device_id, device_it->name)); | |
344 break; | |
345 } | |
346 } | |
347 | |
348 if (it->second.size() != 0 && device_it == it->second.end()) { | |
349 // Use the first capture device in the list if all the devices are | |
350 // being used. | |
351 devices_to_use.push_back( | |
352 content::MediaStreamDevice(it->second.begin()->stream_type, | |
353 it->second.begin()->device_id, | |
354 it->second.begin()->name)); | |
355 } | |
356 } | |
357 | |
358 BrowserThread::PostTask( | |
359 BrowserThread::IO, FROM_HERE, | |
360 base::Bind(&MediaStreamDeviceSettings::PostResponse, | |
361 weak_ptr_factory_.GetWeakPtr(), label, devices_to_use)); | |
362 } | |
363 | |
375 } // namespace media_stream | 364 } // namespace media_stream |
OLD | NEW |