Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(188)

Side by Side Diff: content/browser/renderer_host/media/media_stream_ui_controller.cc

Issue 13989003: Replace MediaStreamUIController with MediaStreamUIProxy. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "content/browser/renderer_host/media/media_stream_ui_controller.h"
6
7 #include <algorithm>
8
9 #include "base/bind.h"
10 #include "base/callback.h"
11 #include "base/logging.h"
12 #include "base/message_loop_proxy.h"
13 #include "base/stl_util.h"
14 #include "content/browser/renderer_host/media/media_stream_settings_requester.h"
15 #include "content/browser/renderer_host/render_view_host_delegate.h"
16 #include "content/browser/renderer_host/render_view_host_impl.h"
17 #include "content/common/media/media_stream_options.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/browser/content_browser_client.h"
20 #include "content/public/browser/media_observer.h"
21 #include "content/public/common/media_stream_request.h"
22 #include "googleurl/src/gurl.h"
23 #include "media/base/bind_to_loop.h"
24
25 namespace content {
26
27 // UI request contains all data needed to keep track of requests between the
28 // different calls.
29 class MediaStreamRequestForUI : public MediaStreamRequest {
30 public:
31 MediaStreamRequestForUI(int render_pid,
32 int render_vid,
33 const GURL& origin,
34 const StreamOptions& options,
35 MediaStreamRequestType request_type,
36 const std::string& requested_device_id)
37 : MediaStreamRequest(render_pid, render_vid, origin,
38 request_type, requested_device_id,
39 options.audio_type, options.video_type),
40 posted_task(false) {
41 DCHECK(IsAudioMediaType(options.audio_type) ||
42 IsVideoMediaType(options.video_type));
43 }
44
45 ~MediaStreamRequestForUI() {}
46
47 // Whether or not a task was posted to make the call to
48 // RequestMediaAccessPermission, to make sure that we never post twice to it.
49 bool posted_task;
50 };
51
52 namespace {
53
54 // Sends the request to the appropriate WebContents.
55 void ProceedMediaAccessPermission(const MediaStreamRequestForUI& request,
56 const MediaResponseCallback& callback) {
57 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
58
59 // Send the permission request to the web contents.
60 RenderViewHostImpl* host = RenderViewHostImpl::FromID(
61 request.render_process_id, request.render_view_id);
62
63 // Tab may have gone away.
64 if (!host || !host->GetDelegate()) {
65 callback.Run(MediaStreamDevices(), scoped_ptr<MediaStreamUI>());
66 return;
67 }
68
69 host->GetDelegate()->RequestMediaAccessPermission(request, callback);
70 }
71
72 } // namespace
73
74 MediaStreamUIController::MediaStreamUIController(SettingsRequester* requester)
75 : requester_(requester),
76 use_fake_ui_(false) {
77 DCHECK(requester_);
78 }
79
80 MediaStreamUIController::~MediaStreamUIController() {
81 DCHECK(requests_.empty());
82 DCHECK(stream_indicators_.empty());
83 }
84
85 void MediaStreamUIController::MakeUIRequest(
86 const std::string& label,
87 int render_process_id,
88 int render_view_id,
89 const StreamOptions& request_options,
90 const GURL& security_origin, MediaStreamRequestType request_type,
91 const std::string& requested_device_id) {
92 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
93
94 // Create a new request.
95 if (!requests_.insert(
96 std::make_pair(label, new MediaStreamRequestForUI(
97 render_process_id, render_view_id, security_origin,
98 request_options, request_type, requested_device_id))).second) {
99 NOTREACHED();
100 }
101
102 if (use_fake_ui_) {
103 PostRequestToFakeUI(label);
104 return;
105 }
106
107 // The UI can handle only one request at the time, do not post the
108 // request to the view if the UI is handling any other request.
109 if (IsUIBusy(render_process_id, render_view_id))
110 return;
111
112 PostRequestToUI(label);
113 }
114
115 void MediaStreamUIController::CancelUIRequest(const std::string& label) {
116 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
117 UIRequests::iterator request_iter = requests_.find(label);
118 if (request_iter != requests_.end()) {
119 // Proceed the next pending request for the same page.
120 scoped_ptr<MediaStreamRequestForUI> request(request_iter->second);
121 int render_view_id = request->render_view_id;
122 int render_process_id = request->render_process_id;
123 bool was_posted = request->posted_task;
124
125 // TODO(xians): Post a cancel request on UI thread to dismiss the infobar
126 // if request has been sent to the UI.
127 // Remove the request from the queue.
128 requests_.erase(request_iter);
129
130 // Simply return if the canceled request has not been brought to UI.
131 if (!was_posted)
132 return;
133
134 // Process the next pending request to replace the old infobar on the same
135 // page.
136 ProcessNextRequestForView(render_process_id, render_view_id);
137 }
138
139 NotifyUIIndicatorDevicesClosed(label);
140 }
141
142 void MediaStreamUIController::ProcessAccessRequestResponse(
143 const std::string& label,
144 const MediaStreamDevices& devices,
145 scoped_ptr<MediaStreamUI> stream_ui) {
146 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
147
148 UIRequests::iterator request_iter = requests_.find(label);
149 // Return if the request has been removed.
150 if (request_iter == requests_.end()) {
151 if (stream_ui) {
152 BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE,
153 stream_ui.release());
154 }
155 return;
156 }
157
158 DCHECK(requester_);
159 scoped_ptr<MediaStreamRequestForUI> request(request_iter->second);
160 requests_.erase(request_iter);
161
162 // Look for queued requests for the same view. If there is a pending request,
163 // post it for user approval.
164 ProcessNextRequestForView(request->render_process_id,
165 request->render_view_id);
166
167 if (!devices.empty()) {
168 if (stream_ui) {
169 DCHECK(stream_indicators_.find(label) == stream_indicators_.end());
170 stream_indicators_[label] = stream_ui.release();
171 }
172
173 // Build a list of "full" device objects for the accepted devices.
174 StreamDeviceInfoArray device_list;
175 // TODO(xians): figure out if it is all right to hard code in_use to false,
176 // though DevicesAccepted seems to do so.
177 for (MediaStreamDevices::const_iterator dev = devices.begin();
178 dev != devices.end(); ++dev) {
179 device_list.push_back(StreamDeviceInfo(
180 dev->type, dev->name, dev->id,
181 dev->sample_rate, dev->channel_layout, false));
182 }
183
184 requester_->DevicesAccepted(label, device_list);
185 } else {
186 DCHECK(!stream_ui);
187 requester_->SettingsError(label);
188 }
189 }
190
191 void MediaStreamUIController::NotifyUIIndicatorDevicesOpened(
192 const std::string& label) {
193 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
194
195 IndicatorsMap::iterator it = stream_indicators_.find(label);
196 if (it != stream_indicators_.end()) {
197 base::Closure stop_callback = media::BindToLoop(
198 base::MessageLoopProxy::current(),
199 base::Bind(&MediaStreamUIController::OnStopStreamFromUI,
200 base::Unretained(this), label));
201
202 // base::Unretained is safe here because the target can be deleted only on
203 // UI thread when posted from IO thread (see
204 // NotifyUIIndicatorDevicesClosed()).
205 BrowserThread::PostTask(
206 BrowserThread::UI, FROM_HERE,
207 base::Bind(&MediaStreamUI::OnStarted,
208 base::Unretained(it->second), stop_callback));
209 }
210 }
211
212 void MediaStreamUIController::NotifyUIIndicatorDevicesClosed(
213 const std::string& label) {
214 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
215
216 IndicatorsMap::iterator indicator = stream_indicators_.find(label);
217 if (indicator != stream_indicators_.end()) {
218 BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, indicator->second);
219 stream_indicators_.erase(indicator);
220 }
221 }
222
223 void MediaStreamUIController::UseFakeUI(scoped_ptr<MediaStreamUI> fake_ui) {
224 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
225 use_fake_ui_ = true;
226 fake_ui_ = fake_ui.Pass();
227 }
228
229 bool MediaStreamUIController::IsUIBusy(int render_process_id,
230 int render_view_id) {
231 for (UIRequests::iterator it = requests_.begin();
232 it != requests_.end(); ++it) {
233 if (it->second->render_process_id == render_process_id &&
234 it->second->render_view_id == render_view_id &&
235 it->second->posted_task) {
236 return true;
237 }
238 }
239 return false;
240 }
241
242 void MediaStreamUIController::ProcessNextRequestForView(
243 int render_process_id,
244 int render_view_id) {
245 std::string next_request_label;
246 for (UIRequests::iterator it = requests_.begin(); it != requests_.end();
247 ++it) {
248 if (it->second->render_process_id == render_process_id &&
249 it->second->render_view_id == render_view_id) {
250 // This request belongs to the given render view.
251 if (!it->second->posted_task) {
252 next_request_label = it->first;
253 break;
254 }
255 }
256 }
257
258 if (next_request_label.empty())
259 return;
260
261 if (fake_ui_) {
262 PostRequestToFakeUI(next_request_label);
263 } else {
264 PostRequestToUI(next_request_label);
265 }
266 }
267
268 void MediaStreamUIController::PostRequestToUI(const std::string& label) {
269 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
270 UIRequests::iterator request_iter = requests_.find(label);
271
272 if (request_iter == requests_.end()) {
273 NOTREACHED();
274 return;
275 }
276 MediaStreamRequestForUI* request = request_iter->second;
277 DCHECK(request != NULL);
278
279 request->posted_task = true;
280
281 BrowserThread::PostTask(
282 BrowserThread::UI, FROM_HERE, base::Bind(
283 &ProceedMediaAccessPermission, *request, media::BindToLoop(
284 base::MessageLoopProxy::current(), base::Bind(
285 &MediaStreamUIController::ProcessAccessRequestResponse,
286 base::Unretained(this), label))));
287 }
288
289 void MediaStreamUIController::PostRequestToFakeUI(const std::string& label) {
290 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
291 DCHECK(requester_);
292 UIRequests::iterator request_iter = requests_.find(label);
293 DCHECK(request_iter != requests_.end());
294 MediaStreamRequestForUI* request = request_iter->second;
295
296 MediaStreamDevices devices;
297 requester_->GetAvailableDevices(&devices);
298 MediaStreamDevices devices_to_use;
299 bool accepted_audio = false;
300 bool accepted_video = false;
301 // Use the first capture device of the same media type in the list for the
302 // fake UI.
303 for (MediaStreamDevices::const_iterator it = devices.begin();
304 it != devices.end(); ++it) {
305 if (!accepted_audio &&
306 IsAudioMediaType(request->audio_type) &&
307 IsAudioMediaType(it->type)) {
308 devices_to_use.push_back(*it);
309 accepted_audio = true;
310 } else if (!accepted_video &&
311 IsVideoMediaType(request->video_type) &&
312 IsVideoMediaType(it->type)) {
313 devices_to_use.push_back(*it);
314 accepted_video = true;
315 }
316 }
317
318 BrowserThread::PostTask(
319 BrowserThread::IO, FROM_HERE,
320 base::Bind(&MediaStreamUIController::ProcessAccessRequestResponse,
321 base::Unretained(this), label, devices_to_use,
322 base::Passed(&fake_ui_)));
323 }
324
325 void MediaStreamUIController::OnStopStreamFromUI(const std::string& label) {
326 // It's safe to base::Unretained() here because |requester_| references
327 // MediaStreamManager which always outlives IO thread.
328 //
329 // TODO(sergeyu): Refactor this code to not rely on what |requester_| is.
330 BrowserThread::PostTask(
331 BrowserThread::IO, FROM_HERE,
332 base::Bind(&SettingsRequester::StopStreamFromUI,
333 base::Unretained(requester_), label));
334 }
335
336 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698