1 // Copyright 2013 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_proxy.h"
6
7 #include "base/command_line.h"
8 #include "content/browser/renderer_host/render_view_host_delegate.h"
9 #include "content/browser/renderer_host/render_view_host_impl.h"
10 #include "content/public/browser/browser_thread.h"
11 #include "content/public/common/content_switches.h"
12 #include "media/video/capture/fake_video_capture_device.h"
13
14 namespace content {
15
16 class MediaStreamUIProxy::Core {
17 public:
18 explicit Core(const base::WeakPtr<MediaStreamUIProxy>& proxy,
19 RenderViewHostDelegate* test_render_delegate);
20 ~Core();
21
22 void RequestAccess(const MediaStreamRequest& request);
23 void OnStarted(gfx::NativeViewId* window_id);
24
25 private:
26 void ProcessAccessRequestResponse(const MediaStreamDevices& devices,
27 content::MediaStreamRequestResult result,
28 scoped_ptr<MediaStreamUI> stream_ui);
29 void ProcessStopRequestFromUI();
30
31 base::WeakPtr<MediaStreamUIProxy> proxy_;
32 scoped_ptr<MediaStreamUI> ui_;
33
34 RenderViewHostDelegate* const test_render_delegate_;
35
36 // WeakPtr<> is used to RequestMediaAccessPermission() because there is no way
37 // cancel media requests.
38 base::WeakPtrFactory<Core> weak_factory_;
39
40 DISALLOW_COPY_AND_ASSIGN(Core);
41 };
42
Core(const base::WeakPtr<MediaStreamUIProxy> & proxy,RenderViewHostDelegate * test_render_delegate)43 MediaStreamUIProxy::Core::Core(const base::WeakPtr<MediaStreamUIProxy>& proxy,
44 RenderViewHostDelegate* test_render_delegate)
45 : proxy_(proxy),
46 test_render_delegate_(test_render_delegate),
47 weak_factory_(this) {
48 }
49
~Core()50 MediaStreamUIProxy::Core::~Core() {
51 DCHECK_CURRENTLY_ON(BrowserThread::UI);
52 }
53
RequestAccess(const MediaStreamRequest & request)54 void MediaStreamUIProxy::Core::RequestAccess(
55 const MediaStreamRequest& request) {
56 DCHECK_CURRENTLY_ON(BrowserThread::UI);
57
58 RenderViewHostDelegate* render_delegate;
59
60 if (test_render_delegate_) {
61 render_delegate = test_render_delegate_;
62 } else {
63 RenderViewHostImpl* host = RenderViewHostImpl::FromID(
64 request.render_process_id, request.render_view_id);
65
66 // Tab may have gone away.
67 if (!host || !host->GetDelegate()) {
68 ProcessAccessRequestResponse(
69 MediaStreamDevices(),
70 MEDIA_DEVICE_INVALID_STATE,
71 scoped_ptr<MediaStreamUI>());
72 return;
73 }
74
75 render_delegate = host->GetDelegate();
76 }
77
78 render_delegate->RequestMediaAccessPermission(
79 request, base::Bind(&Core::ProcessAccessRequestResponse,
80 weak_factory_.GetWeakPtr()));
81 }
82
OnStarted(gfx::NativeViewId * window_id)83 void MediaStreamUIProxy::Core::OnStarted(gfx::NativeViewId* window_id) {
84 DCHECK_CURRENTLY_ON(BrowserThread::UI);
85 if (ui_) {
86 *window_id = ui_->OnStarted(
87 base::Bind(&Core::ProcessStopRequestFromUI, base::Unretained(this)));
88 }
89 }
90
ProcessAccessRequestResponse(const MediaStreamDevices & devices,content::MediaStreamRequestResult result,scoped_ptr<MediaStreamUI> stream_ui)91 void MediaStreamUIProxy::Core::ProcessAccessRequestResponse(
92 const MediaStreamDevices& devices,
93 content::MediaStreamRequestResult result,
94 scoped_ptr<MediaStreamUI> stream_ui) {
95 DCHECK_CURRENTLY_ON(BrowserThread::UI);
96
97 ui_ = stream_ui.Pass();
98 BrowserThread::PostTask(
99 BrowserThread::IO, FROM_HERE,
100 base::Bind(&MediaStreamUIProxy::ProcessAccessRequestResponse,
101 proxy_, devices, result));
102 }
103
ProcessStopRequestFromUI()104 void MediaStreamUIProxy::Core::ProcessStopRequestFromUI() {
105 DCHECK_CURRENTLY_ON(BrowserThread::UI);
106
107 BrowserThread::PostTask(
108 BrowserThread::IO, FROM_HERE,
109 base::Bind(&MediaStreamUIProxy::ProcessStopRequestFromUI, proxy_));
110 }
111
112 // static
Create()113 scoped_ptr<MediaStreamUIProxy> MediaStreamUIProxy::Create() {
114 return scoped_ptr<MediaStreamUIProxy>(new MediaStreamUIProxy(NULL));
115 }
116
117 // static
CreateForTests(RenderViewHostDelegate * render_delegate)118 scoped_ptr<MediaStreamUIProxy> MediaStreamUIProxy::CreateForTests(
119 RenderViewHostDelegate* render_delegate) {
120 return scoped_ptr<MediaStreamUIProxy>(
121 new MediaStreamUIProxy(render_delegate));
122 }
123
MediaStreamUIProxy(RenderViewHostDelegate * test_render_delegate)124 MediaStreamUIProxy::MediaStreamUIProxy(
125 RenderViewHostDelegate* test_render_delegate)
126 : weak_factory_(this) {
127 DCHECK_CURRENTLY_ON(BrowserThread::IO);
128 core_.reset(new Core(weak_factory_.GetWeakPtr(), test_render_delegate));
129 }
130
~MediaStreamUIProxy()131 MediaStreamUIProxy::~MediaStreamUIProxy() {
132 DCHECK_CURRENTLY_ON(BrowserThread::IO);
133 BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, core_.release());
134 }
135
RequestAccess(const MediaStreamRequest & request,const ResponseCallback & response_callback)136 void MediaStreamUIProxy::RequestAccess(
137 const MediaStreamRequest& request,
138 const ResponseCallback& response_callback) {
139 DCHECK_CURRENTLY_ON(BrowserThread::IO);
140
141 response_callback_ = response_callback;
142 BrowserThread::PostTask(
143 BrowserThread::UI, FROM_HERE,
144 base::Bind(&Core::RequestAccess, base::Unretained(core_.get()), request));
145 }
146
OnStarted(const base::Closure & stop_callback,const WindowIdCallback & window_id_callback)147 void MediaStreamUIProxy::OnStarted(const base::Closure& stop_callback,
148 const WindowIdCallback& window_id_callback) {
149 DCHECK_CURRENTLY_ON(BrowserThread::IO);
150
151 stop_callback_ = stop_callback;
152
153 // Owned by the PostTaskAndReply callback.
154 gfx::NativeViewId* window_id = new gfx::NativeViewId(0);
155
156 BrowserThread::PostTaskAndReply(
157 BrowserThread::UI,
158 FROM_HERE,
159 base::Bind(&Core::OnStarted, base::Unretained(core_.get()), window_id),
160 base::Bind(&MediaStreamUIProxy::OnWindowId,
161 weak_factory_.GetWeakPtr(),
162 window_id_callback,
163 base::Owned(window_id)));
164 }
165
OnWindowId(const WindowIdCallback & window_id_callback,gfx::NativeViewId * window_id)166 void MediaStreamUIProxy::OnWindowId(const WindowIdCallback& window_id_callback,
167 gfx::NativeViewId* window_id) {
168 DCHECK_CURRENTLY_ON(BrowserThread::IO);
169 if (!window_id_callback.is_null())
170 window_id_callback.Run(*window_id);
171 }
172
ProcessAccessRequestResponse(const MediaStreamDevices & devices,content::MediaStreamRequestResult result)173 void MediaStreamUIProxy::ProcessAccessRequestResponse(
174 const MediaStreamDevices& devices,
175 content::MediaStreamRequestResult result) {
176 DCHECK_CURRENTLY_ON(BrowserThread::IO);
177 DCHECK(!response_callback_.is_null());
178
179 ResponseCallback cb = response_callback_;
180 response_callback_.Reset();
181 cb.Run(devices, result);
182 }
183
ProcessStopRequestFromUI()184 void MediaStreamUIProxy::ProcessStopRequestFromUI() {
185 DCHECK_CURRENTLY_ON(BrowserThread::IO);
186 DCHECK(!stop_callback_.is_null());
187
188 base::Closure cb = stop_callback_;
189 stop_callback_.Reset();
190 cb.Run();
191 }
192
FakeMediaStreamUIProxy()193 FakeMediaStreamUIProxy::FakeMediaStreamUIProxy()
194 : MediaStreamUIProxy(NULL) {
195 }
196
~FakeMediaStreamUIProxy()197 FakeMediaStreamUIProxy::~FakeMediaStreamUIProxy() {}
198
SetAvailableDevices(const MediaStreamDevices & devices)199 void FakeMediaStreamUIProxy::SetAvailableDevices(
200 const MediaStreamDevices& devices) {
201 devices_ = devices;
202 }
203
RequestAccess(const MediaStreamRequest & request,const ResponseCallback & response_callback)204 void FakeMediaStreamUIProxy::RequestAccess(
205 const MediaStreamRequest& request,
206 const ResponseCallback& response_callback) {
207 DCHECK_CURRENTLY_ON(BrowserThread::IO);
208
209 response_callback_ = response_callback;
210
211 if (CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
212 switches::kUseFakeUIForMediaStream) == "deny") {
213 // Immediately deny the request.
214 BrowserThread::PostTask(
215 BrowserThread::IO, FROM_HERE,
216 base::Bind(&MediaStreamUIProxy::ProcessAccessRequestResponse,
217 weak_factory_.GetWeakPtr(),
218 MediaStreamDevices(),
219 MEDIA_DEVICE_PERMISSION_DENIED));
220 return;
221 }
222
223 MediaStreamDevices devices_to_use;
224 bool accepted_audio = false;
225 bool accepted_video = false;
226
227 // Use the first capture device of the same media type in the list for the
228 // fake UI.
229 for (MediaStreamDevices::const_iterator it = devices_.begin();
230 it != devices_.end(); ++it) {
231 if (!accepted_audio &&
232 IsAudioInputMediaType(request.audio_type) &&
233 IsAudioInputMediaType(it->type) &&
234 (request.requested_audio_device_id.empty() ||
235 request.requested_audio_device_id == it->id)) {
236 devices_to_use.push_back(*it);
237 accepted_audio = true;
238 } else if (!accepted_video &&
239 IsVideoMediaType(request.video_type) &&
240 IsVideoMediaType(it->type) &&
241 (request.requested_video_device_id.empty() ||
242 request.requested_video_device_id == it->id)) {
243 devices_to_use.push_back(*it);
244 accepted_video = true;
245 }
246 }
247
248 // Fail the request if a device exist for the requested type.
249 if ((request.audio_type != MEDIA_NO_SERVICE && !accepted_audio) ||
250 (request.video_type != MEDIA_NO_SERVICE && !accepted_video)) {
251 devices_to_use.clear();
252 }
253
254 BrowserThread::PostTask(
255 BrowserThread::IO, FROM_HERE,
256 base::Bind(&MediaStreamUIProxy::ProcessAccessRequestResponse,
257 weak_factory_.GetWeakPtr(),
258 devices_to_use,
259 devices_to_use.empty() ?
260 MEDIA_DEVICE_NO_HARDWARE :
261 MEDIA_DEVICE_OK));
262 }
263
OnStarted(const base::Closure & stop_callback,const WindowIdCallback & window_id_callback)264 void FakeMediaStreamUIProxy::OnStarted(
265 const base::Closure& stop_callback,
266 const WindowIdCallback& window_id_callback) {}
267
268 } // namespace content
269