blob: 03ca4c5449bac69db85e9a63f2e65d82fb7df767 [file] [log] [blame]
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001// 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 "chrome/browser/media/media_capture_devices_dispatcher.h"
6
7#include "base/command_line.h"
8#include "base/logging.h"
9#include "base/prefs/pref_service.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010010#include "base/sha1.h"
11#include "base/strings/string_number_conversions.h"
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +010012#include "base/strings/string_util.h"
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010013#include "base/strings/utf_string_conversions.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010014#include "chrome/browser/extensions/api/tab_capture/tab_capture_registry.h"
15#include "chrome/browser/extensions/api/tab_capture/tab_capture_registry_factory.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000016#include "chrome/browser/media/audio_stream_indicator.h"
17#include "chrome/browser/media/media_stream_capture_indicator.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010018#include "chrome/browser/media/media_stream_infobar_delegate.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000019#include "chrome/browser/prefs/scoped_user_pref_update.h"
20#include "chrome/browser/profiles/profile.h"
Ben Murdoch32409262013-08-07 11:04:47 +010021#include "chrome/browser/ui/screen_capture_notification_ui.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000022#include "chrome/browser/ui/simple_message_box.h"
23#include "chrome/common/chrome_switches.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010024#include "chrome/common/extensions/extension.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000025#include "chrome/common/pref_names.h"
26#include "components/user_prefs/pref_registry_syncable.h"
27#include "content/public/browser/browser_thread.h"
28#include "content/public/browser/media_devices_monitor.h"
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010029#include "content/public/browser/notification_service.h"
30#include "content/public/browser/notification_source.h"
31#include "content/public/browser/notification_types.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010032#include "content/public/browser/web_contents.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000033#include "content/public/common/media_stream_request.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010034#include "extensions/common/constants.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000035#include "grit/generated_resources.h"
36#include "ui/base/l10n/l10n_util.h"
37
Ben Murdochbb1529c2013-08-08 10:24:53 +010038#if defined(USE_CRAS)
39#include "media/audio/cras/audio_manager_cras.h"
40#endif
41
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000042using content::BrowserThread;
43using content::MediaStreamDevices;
44
45namespace {
46
Ben Murdoch7dbb3d52013-07-17 14:55:54 +010047// Finds a device in |devices| that has |device_id|, or NULL if not found.
48const content::MediaStreamDevice* FindDeviceWithId(
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000049 const content::MediaStreamDevices& devices,
50 const std::string& device_id) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000051 content::MediaStreamDevices::const_iterator iter = devices.begin();
52 for (; iter != devices.end(); ++iter) {
53 if (iter->id == device_id) {
54 return &(*iter);
55 }
56 }
Ben Murdoch7dbb3d52013-07-17 14:55:54 +010057 return NULL;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000058};
59
60// This is a short-term solution to allow testing of the the Screen Capture API
61// with Google Hangouts in M27.
62// TODO(sergeyu): Remove this whitelist as soon as possible.
63bool IsOriginWhitelistedForScreenCapture(const GURL& origin) {
64#if defined(OFFICIAL_BUILD)
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010065 if (// Google Hangouts.
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +010066 (origin.SchemeIs("https") &&
67 EndsWith(origin.spec(), ".talkgadget.google.com/", true)) ||
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010068 origin.spec() == "https://plus.google.com/" ||
69 origin.spec() == "chrome-extension://pkedcjkdefgpdelpbcmbmeomcjbeemfm/" ||
70 origin.spec() == "chrome-extension://fmfcbgogabcbclcofgocippekhfcmgfj/" ||
Ben Murdochca12bfa2013-07-23 11:17:05 +010071 origin.spec() == "chrome-extension://hfaagokkkhdbgiakmmlclaapfelnkoah/" ||
72 origin.spec() == "chrome-extension://gfdkimpbcpahaombhbimeihdjnejgicl/") {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010073 return true;
74 }
75 // Check against hashed origins.
76 const std::string origin_hash = base::SHA1HashString(origin.spec());
77 DCHECK_EQ(origin_hash.length(), base::kSHA1Length);
78 const std::string hexencoded_origin_hash =
79 base::HexEncode(origin_hash.data(), origin_hash.length());
80 return
81 hexencoded_origin_hash == "3C2705BC432E7C51CA8553FDC5BEE873FF2468EE" ||
82 hexencoded_origin_hash == "50F02B8A668CAB274527D58356F07C2143080FCC";
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000083#else
84 return false;
85#endif
86}
87
88} // namespace
89
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010090MediaCaptureDevicesDispatcher::PendingAccessRequest::PendingAccessRequest(
91 const content::MediaStreamRequest& request,
92 const content::MediaResponseCallback& callback)
93 : request(request),
94 callback(callback) {
95}
96
97MediaCaptureDevicesDispatcher::PendingAccessRequest::~PendingAccessRequest() {}
98
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000099MediaCaptureDevicesDispatcher* MediaCaptureDevicesDispatcher::GetInstance() {
100 return Singleton<MediaCaptureDevicesDispatcher>::get();
101}
102
103MediaCaptureDevicesDispatcher::MediaCaptureDevicesDispatcher()
104 : devices_enumerated_(false),
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100105 is_device_enumeration_disabled_(false),
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000106 media_stream_capture_indicator_(new MediaStreamCaptureIndicator()),
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100107 audio_stream_indicator_(new AudioStreamIndicator()) {
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100108 // MediaCaptureDevicesDispatcher is a singleton. It should be created on
109 // UI thread. Otherwise, it will not receive
110 // content::NOTIFICATION_WEB_CONTENTS_DESTROYED, and that will result in
111 // possible use after free.
112 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100113 notifications_registrar_.Add(
114 this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
115 content::NotificationService::AllSources());
116}
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000117
118MediaCaptureDevicesDispatcher::~MediaCaptureDevicesDispatcher() {}
119
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100120void MediaCaptureDevicesDispatcher::RegisterProfilePrefs(
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100121 user_prefs::PrefRegistrySyncable* registry) {
122 registry->RegisterStringPref(
123 prefs::kDefaultAudioCaptureDevice,
124 std::string(),
125 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
126 registry->RegisterStringPref(
127 prefs::kDefaultVideoCaptureDevice,
128 std::string(),
129 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000130}
131
132void MediaCaptureDevicesDispatcher::AddObserver(Observer* observer) {
133 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
134 if (!observers_.HasObserver(observer))
135 observers_.AddObserver(observer);
136}
137
138void MediaCaptureDevicesDispatcher::RemoveObserver(Observer* observer) {
139 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
140 observers_.RemoveObserver(observer);
141}
142
143const MediaStreamDevices&
144MediaCaptureDevicesDispatcher::GetAudioCaptureDevices() {
145 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100146 if (!is_device_enumeration_disabled_ && !devices_enumerated_) {
Ben Murdocheb525c52013-07-10 11:40:50 +0100147 content::EnsureMonitorCaptureDevices();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000148 devices_enumerated_ = true;
149 }
150 return audio_devices_;
151}
152
153const MediaStreamDevices&
154MediaCaptureDevicesDispatcher::GetVideoCaptureDevices() {
155 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100156 if (!is_device_enumeration_disabled_ && !devices_enumerated_) {
Ben Murdocheb525c52013-07-10 11:40:50 +0100157 content::EnsureMonitorCaptureDevices();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000158 devices_enumerated_ = true;
159 }
160 return video_devices_;
161}
162
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100163void MediaCaptureDevicesDispatcher::Observe(
164 int type,
165 const content::NotificationSource& source,
166 const content::NotificationDetails& details) {
167 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
168 if (type == content::NOTIFICATION_WEB_CONTENTS_DESTROYED) {
169 content::WebContents* web_contents =
170 content::Source<content::WebContents>(source).ptr();
171 pending_requests_.erase(web_contents);
172 }
173}
174
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100175void MediaCaptureDevicesDispatcher::ProcessMediaAccessRequest(
176 content::WebContents* web_contents,
177 const content::MediaStreamRequest& request,
178 const content::MediaResponseCallback& callback,
179 const extensions::Extension* extension) {
180 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
181
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100182 if (request.video_type == content::MEDIA_SCREEN_VIDEO_CAPTURE ||
183 request.audio_type == content::MEDIA_SYSTEM_AUDIO_CAPTURE) {
Ben Murdochca12bfa2013-07-23 11:17:05 +0100184 ProcessScreenCaptureAccessRequest(
Ben Murdoch32409262013-08-07 11:04:47 +0100185 web_contents, request, callback, extension);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100186 } else if (extension) {
187 // For extensions access is approved based on extension permissions.
188 ProcessMediaAccessRequestFromExtension(
189 web_contents, request, callback, extension);
190 } else {
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100191 ProcessRegularMediaAccessRequest(web_contents, request, callback);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100192 }
193}
194
195void MediaCaptureDevicesDispatcher::ProcessScreenCaptureAccessRequest(
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000196 content::WebContents* web_contents,
197 const content::MediaStreamRequest& request,
Ben Murdochca12bfa2013-07-23 11:17:05 +0100198 const content::MediaResponseCallback& callback,
Ben Murdoch32409262013-08-07 11:04:47 +0100199 const extensions::Extension* extension) {
Ben Murdochbb1529c2013-08-08 10:24:53 +0100200 const bool component_extension =
Ben Murdoch32409262013-08-07 11:04:47 +0100201 extension && extension->location() == extensions::Manifest::COMPONENT;
202
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000203 content::MediaStreamDevices devices;
204
Ben Murdochbb1529c2013-08-08 10:24:53 +0100205 const bool screen_capture_enabled =
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000206 CommandLine::ForCurrentProcess()->HasSwitch(
207 switches::kEnableUserMediaScreenCapturing) ||
208 IsOriginWhitelistedForScreenCapture(request.security_origin);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100209
Ben Murdochbb1529c2013-08-08 10:24:53 +0100210 const bool origin_is_secure =
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100211 request.security_origin.SchemeIsSecure() ||
212 request.security_origin.SchemeIs(extensions::kExtensionScheme) ||
213 CommandLine::ForCurrentProcess()->HasSwitch(
214 switches::kAllowHttpScreenCapture);
215
Ben Murdochbb1529c2013-08-08 10:24:53 +0100216 const bool screen_video_capture_requested =
217 request.video_type == content::MEDIA_SCREEN_VIDEO_CAPTURE;
218
219 const bool system_audio_capture_requested =
220 request.audio_type == content::MEDIA_SYSTEM_AUDIO_CAPTURE;
221
222#if defined(USE_CRAS)
223 const bool system_audio_capture_supported = true;
224#else
225 const bool system_audio_capture_supported = false;
226#endif
227
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100228 // Approve request only when the following conditions are met:
229 // 1. Screen capturing is enabled via command line switch or white-listed for
230 // the given origin.
231 // 2. Request comes from a page with a secure origin or from an extension.
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100232 // 3. Video capture is requested for screen video.
233 // 4. Audio capture is either not requested, or requested for system audio.
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100234 if (screen_capture_enabled && origin_is_secure &&
Ben Murdochbb1529c2013-08-08 10:24:53 +0100235 screen_video_capture_requested &&
236 (!system_audio_capture_requested || system_audio_capture_supported)) {
Ben Murdochca12bfa2013-07-23 11:17:05 +0100237 // For component extensions, bypass message box.
238 bool user_approved = false;
239 if (!component_extension) {
240 string16 application_name = UTF8ToUTF16(request.security_origin.spec());
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100241 string16 confirmation_text = l10n_util::GetStringFUTF16(
242 request.audio_type == content::MEDIA_NO_SERVICE ?
243 IDS_MEDIA_SCREEN_CAPTURE_CONFIRMATION_TEXT :
244 IDS_MEDIA_SCREEN_AND_AUDIO_CAPTURE_CONFIRMATION_TEXT,
245 application_name);
Ben Murdochca12bfa2013-07-23 11:17:05 +0100246 chrome::MessageBoxResult result = chrome::ShowMessageBox(
247 NULL,
248 l10n_util::GetStringFUTF16(
249 IDS_MEDIA_SCREEN_CAPTURE_CONFIRMATION_TITLE, application_name),
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100250 confirmation_text,
Ben Murdochca12bfa2013-07-23 11:17:05 +0100251 chrome::MESSAGE_BOX_TYPE_QUESTION);
252 user_approved = (result == chrome::MESSAGE_BOX_RESULT_YES);
253 }
254
255 if (user_approved || component_extension) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000256 devices.push_back(content::MediaStreamDevice(
257 content::MEDIA_SCREEN_VIDEO_CAPTURE, std::string(), "Screen"));
Ben Murdochbb1529c2013-08-08 10:24:53 +0100258 if (system_audio_capture_requested) {
259#if defined(USE_CRAS)
260 // Use the special loopback device ID for system audio capture.
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100261 devices.push_back(content::MediaStreamDevice(
Ben Murdochbb1529c2013-08-08 10:24:53 +0100262 content::MEDIA_SYSTEM_AUDIO_CAPTURE,
263 media::AudioManagerCras::kLoopbackDeviceId,
264 "System Audio"));
265#endif
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100266 }
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000267 }
268 }
269
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100270 scoped_ptr<content::MediaStreamUI> ui;
Ben Murdochca12bfa2013-07-23 11:17:05 +0100271 // Unless we're being invoked from a component extension, register to display
272 // the notification for stream capture.
273 if (!devices.empty() && !component_extension) {
Ben Murdoch32409262013-08-07 11:04:47 +0100274 // Use extension name as title for extensions and origin for drive-by web.
275 std::string title;
276 if (extension) {
277 title = extension->name();
278 } else {
279 title = web_contents->GetURL().GetOrigin().spec();
280 }
281
282 ui = ScreenCaptureNotificationUI::Create(l10n_util::GetStringFUTF16(
283 IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_TEXT, UTF8ToUTF16(title)));
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100284 }
285 callback.Run(devices, ui.Pass());
286}
287
288void MediaCaptureDevicesDispatcher::ProcessMediaAccessRequestFromExtension(
289 content::WebContents* web_contents,
290 const content::MediaStreamRequest& request,
291 const content::MediaResponseCallback& callback,
292 const extensions::Extension* extension) {
293 content::MediaStreamDevices devices;
294 Profile* profile =
295 Profile::FromBrowserContext(web_contents->GetBrowserContext());
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100296
297#if defined(OS_ANDROID)
298 // Tab capture is not supported on Android.
299 bool tab_capture_allowed = false;
300#else
301 extensions::TabCaptureRegistry* tab_capture_registry =
302 extensions::TabCaptureRegistryFactory::GetForProfile(profile);
303 bool tab_capture_allowed =
304 tab_capture_registry->VerifyRequest(request.render_process_id,
305 request.render_view_id);
306#endif
307
308 if (request.audio_type == content::MEDIA_TAB_AUDIO_CAPTURE &&
309 tab_capture_allowed &&
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100310 extension->HasAPIPermission(extensions::APIPermission::kTabCapture)) {
311 devices.push_back(content::MediaStreamDevice(
312 content::MEDIA_TAB_AUDIO_CAPTURE, std::string(), std::string()));
313 } else if (request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE &&
314 extension->HasAPIPermission(
315 extensions::APIPermission::kAudioCapture)) {
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100316 GetDefaultDevicesForProfile(profile, true, false, &devices);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100317 }
318
319 if (request.video_type == content::MEDIA_TAB_VIDEO_CAPTURE &&
320 tab_capture_allowed &&
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100321 extension->HasAPIPermission(extensions::APIPermission::kTabCapture)) {
322 devices.push_back(content::MediaStreamDevice(
323 content::MEDIA_TAB_VIDEO_CAPTURE, std::string(), std::string()));
324 } else if (request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE &&
325 extension->HasAPIPermission(extensions::APIPermission::kVideoCapture)) {
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100326 GetDefaultDevicesForProfile(profile, false, true, &devices);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100327 }
328
329 scoped_ptr<content::MediaStreamUI> ui;
330 if (!devices.empty()) {
331 ui = media_stream_capture_indicator_->RegisterMediaStream(
332 web_contents, devices);
333 }
334 callback.Run(devices, ui.Pass());
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000335}
336
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100337void MediaCaptureDevicesDispatcher::ProcessRegularMediaAccessRequest(
338 content::WebContents* web_contents,
339 const content::MediaStreamRequest& request,
340 const content::MediaResponseCallback& callback) {
341 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
342
343 RequestsQueue& queue = pending_requests_[web_contents];
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100344 queue.push_back(PendingAccessRequest(request, callback));
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100345
346 // If this is the only request then show the infobar.
347 if (queue.size() == 1)
348 ProcessQueuedAccessRequest(web_contents);
349}
350
351void MediaCaptureDevicesDispatcher::ProcessQueuedAccessRequest(
352 content::WebContents* web_contents) {
353 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
354
355 std::map<content::WebContents*, RequestsQueue>::iterator it =
356 pending_requests_.find(web_contents);
357
358 if (it == pending_requests_.end() || it->second.empty()) {
359 // Don't do anything if the tab was was closed.
360 return;
361 }
362
363 DCHECK(!it->second.empty());
364
365 MediaStreamInfoBarDelegate::Create(
366 web_contents, it->second.front().request,
367 base::Bind(&MediaCaptureDevicesDispatcher::OnAccessRequestResponse,
368 base::Unretained(this), web_contents));
369}
370
371void MediaCaptureDevicesDispatcher::OnAccessRequestResponse(
372 content::WebContents* web_contents,
373 const content::MediaStreamDevices& devices,
374 scoped_ptr<content::MediaStreamUI> ui) {
375 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
376
377 std::map<content::WebContents*, RequestsQueue>::iterator it =
378 pending_requests_.find(web_contents);
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100379 if (it == pending_requests_.end()) {
380 // WebContents has been destroyed. Don't need to do anything.
381 return;
382 }
383
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100384 RequestsQueue& queue(it->second);
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100385 if (queue.empty())
386 return;
387
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100388 content::MediaResponseCallback callback = queue.front().callback;
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100389 queue.pop_front();
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100390
391 if (!queue.empty()) {
392 // Post a task to process next queued request. It has to be done
393 // asynchronously to make sure that calling infobar is not destroyed until
394 // after this function returns.
395 BrowserThread::PostTask(
396 BrowserThread::UI, FROM_HERE,
397 base::Bind(&MediaCaptureDevicesDispatcher::ProcessQueuedAccessRequest,
398 base::Unretained(this), web_contents));
399 }
400
401 callback.Run(devices, ui.Pass());
402}
403
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000404void MediaCaptureDevicesDispatcher::GetDefaultDevicesForProfile(
405 Profile* profile,
406 bool audio,
407 bool video,
408 content::MediaStreamDevices* devices) {
409 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
410 DCHECK(audio || video);
411
412 PrefService* prefs = profile->GetPrefs();
413 std::string default_device;
414 if (audio) {
415 default_device = prefs->GetString(prefs::kDefaultAudioCaptureDevice);
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100416 const content::MediaStreamDevice* device =
417 GetRequestedAudioDevice(default_device);
418 if (!device)
419 device = GetFirstAvailableAudioDevice();
420 if (device)
421 devices->push_back(*device);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000422 }
423
424 if (video) {
425 default_device = prefs->GetString(prefs::kDefaultVideoCaptureDevice);
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100426 const content::MediaStreamDevice* device =
427 GetRequestedVideoDevice(default_device);
428 if (!device)
429 device = GetFirstAvailableVideoDevice();
430 if (device)
431 devices->push_back(*device);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000432 }
433}
434
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100435const content::MediaStreamDevice*
436MediaCaptureDevicesDispatcher::GetRequestedAudioDevice(
437 const std::string& requested_audio_device_id) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000438 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100439 const content::MediaStreamDevices& audio_devices = GetAudioCaptureDevices();
440 const content::MediaStreamDevice* const device =
441 FindDeviceWithId(audio_devices, requested_audio_device_id);
442 return device;
443}
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000444
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100445const content::MediaStreamDevice*
446MediaCaptureDevicesDispatcher::GetFirstAvailableAudioDevice() {
447 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
448 const content::MediaStreamDevices& audio_devices = GetAudioCaptureDevices();
449 if (audio_devices.empty())
450 return NULL;
451 return &(*audio_devices.begin());
452}
453
454const content::MediaStreamDevice*
455MediaCaptureDevicesDispatcher::GetRequestedVideoDevice(
456 const std::string& requested_video_device_id) {
457 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
458 const content::MediaStreamDevices& video_devices = GetVideoCaptureDevices();
459 const content::MediaStreamDevice* const device =
460 FindDeviceWithId(video_devices, requested_video_device_id);
461 return device;
462}
463
464const content::MediaStreamDevice*
465MediaCaptureDevicesDispatcher::GetFirstAvailableVideoDevice() {
466 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
467 const content::MediaStreamDevices& video_devices = GetVideoCaptureDevices();
468 if (video_devices.empty())
469 return NULL;
470 return &(*video_devices.begin());
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000471}
472
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100473void MediaCaptureDevicesDispatcher::DisableDeviceEnumerationForTesting() {
474 is_device_enumeration_disabled_ = true;
475}
476
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000477scoped_refptr<MediaStreamCaptureIndicator>
478 MediaCaptureDevicesDispatcher::GetMediaStreamCaptureIndicator() {
479 return media_stream_capture_indicator_;
480}
481
482scoped_refptr<AudioStreamIndicator>
483MediaCaptureDevicesDispatcher::GetAudioStreamIndicator() {
484 return audio_stream_indicator_;
485}
486
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000487void MediaCaptureDevicesDispatcher::OnAudioCaptureDevicesChanged(
488 const content::MediaStreamDevices& devices) {
489 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
490 BrowserThread::PostTask(
491 BrowserThread::UI, FROM_HERE,
492 base::Bind(&MediaCaptureDevicesDispatcher::UpdateAudioDevicesOnUIThread,
493 base::Unretained(this), devices));
494}
495
496void MediaCaptureDevicesDispatcher::OnVideoCaptureDevicesChanged(
497 const content::MediaStreamDevices& devices) {
498 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
499 BrowserThread::PostTask(
500 BrowserThread::UI, FROM_HERE,
501 base::Bind(&MediaCaptureDevicesDispatcher::UpdateVideoDevicesOnUIThread,
502 base::Unretained(this), devices));
503}
504
505void MediaCaptureDevicesDispatcher::OnMediaRequestStateChanged(
506 int render_process_id,
507 int render_view_id,
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100508 int page_request_id,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000509 const content::MediaStreamDevice& device,
510 content::MediaRequestState state) {
511 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
512 BrowserThread::PostTask(
513 BrowserThread::UI, FROM_HERE,
514 base::Bind(
515 &MediaCaptureDevicesDispatcher::UpdateMediaRequestStateOnUIThread,
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100516 base::Unretained(this), render_process_id, render_view_id,
517 page_request_id, device, state));
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000518
519}
520
521void MediaCaptureDevicesDispatcher::OnAudioStreamPlayingChanged(
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100522 int render_process_id, int render_view_id, int stream_id,
Ben Murdochca12bfa2013-07-23 11:17:05 +0100523 bool is_playing, float power_dbfs, bool clipped) {
524 audio_stream_indicator_->UpdateWebContentsStatus(
525 render_process_id, render_view_id, stream_id,
526 is_playing, power_dbfs, clipped);
527}
528
529void MediaCaptureDevicesDispatcher::OnCreatingAudioStream(
530 int render_process_id,
531 int render_view_id) {
532 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
533 BrowserThread::PostTask(
534 BrowserThread::UI, FROM_HERE,
535 base::Bind(
536 &MediaCaptureDevicesDispatcher::OnCreatingAudioStreamOnUIThread,
537 base::Unretained(this), render_process_id, render_view_id));
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000538}
539
540void MediaCaptureDevicesDispatcher::UpdateAudioDevicesOnUIThread(
541 const content::MediaStreamDevices& devices) {
542 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
543 devices_enumerated_ = true;
544 audio_devices_ = devices;
545 FOR_EACH_OBSERVER(Observer, observers_,
546 OnUpdateAudioDevices(audio_devices_));
547}
548
549void MediaCaptureDevicesDispatcher::UpdateVideoDevicesOnUIThread(
550 const content::MediaStreamDevices& devices){
551 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
552 devices_enumerated_ = true;
553 video_devices_ = devices;
554 FOR_EACH_OBSERVER(Observer, observers_,
555 OnUpdateVideoDevices(video_devices_));
556}
557
558void MediaCaptureDevicesDispatcher::UpdateMediaRequestStateOnUIThread(
559 int render_process_id,
560 int render_view_id,
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100561 int page_request_id,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000562 const content::MediaStreamDevice& device,
563 content::MediaRequestState state) {
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100564 // Cancel the request.
565 if (state == content::MEDIA_REQUEST_STATE_CLOSING) {
566 bool found = false;
567 for (RequestsQueues::iterator rqs_it = pending_requests_.begin();
568 rqs_it != pending_requests_.end(); ++rqs_it) {
569 RequestsQueue& queue = rqs_it->second;
570 for (RequestsQueue::iterator it = queue.begin();
571 it != queue.end(); ++it) {
572 if (it->request.render_process_id == render_process_id &&
573 it->request.render_view_id == render_view_id &&
574 it->request.page_request_id == page_request_id) {
575 queue.erase(it);
576 found = true;
577 break;
578 }
579 }
580 if (found)
581 break;
582 }
583 }
584
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000585 FOR_EACH_OBSERVER(Observer, observers_,
586 OnRequestUpdate(render_process_id,
587 render_view_id,
588 device,
589 state));
590}
Ben Murdochca12bfa2013-07-23 11:17:05 +0100591
592void MediaCaptureDevicesDispatcher::OnCreatingAudioStreamOnUIThread(
593 int render_process_id,
594 int render_view_id) {
595 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
596 FOR_EACH_OBSERVER(Observer, observers_,
597 OnCreatingAudioStream(render_process_id, render_view_id));
598}