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 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 50 MediaStreamUIProxy::Core::~Core() { 51 DCHECK_CURRENTLY_ON(BrowserThread::UI); 52 } 53 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 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 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 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 113 scoped_ptr<MediaStreamUIProxy> MediaStreamUIProxy::Create() { 114 return scoped_ptr<MediaStreamUIProxy>(new MediaStreamUIProxy(NULL)); 115 } 116 117 // static 118 scoped_ptr<MediaStreamUIProxy> MediaStreamUIProxy::CreateForTests( 119 RenderViewHostDelegate* render_delegate) { 120 return scoped_ptr<MediaStreamUIProxy>( 121 new MediaStreamUIProxy(render_delegate)); 122 } 123 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 131 MediaStreamUIProxy::~MediaStreamUIProxy() { 132 DCHECK_CURRENTLY_ON(BrowserThread::IO); 133 BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, core_.release()); 134 } 135 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 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 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 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 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 193 FakeMediaStreamUIProxy::FakeMediaStreamUIProxy() 194 : MediaStreamUIProxy(NULL) { 195 } 196 197 FakeMediaStreamUIProxy::~FakeMediaStreamUIProxy() {} 198 199 void FakeMediaStreamUIProxy::SetAvailableDevices( 200 const MediaStreamDevices& devices) { 201 devices_ = devices; 202 } 203 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 264 void FakeMediaStreamUIProxy::OnStarted( 265 const base::Closure& stop_callback, 266 const WindowIdCallback& window_id_callback) {} 267 268 } // namespace content 269