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.
5 #include "chrome/browser/media/media_capture_devices_dispatcher.h"
7 #include "base/command_line.h"
8 #include "base/logging.h"
9 #include "base/metrics/field_trial.h"
10 #include "base/prefs/pref_service.h"
11 #include "base/prefs/scoped_user_pref_update.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "chrome/browser/media/desktop_streams_registry.h"
16 #include "chrome/browser/media/media_access_handler.h"
17 #include "chrome/browser/media/media_stream_capture_indicator.h"
18 #include "chrome/browser/media/permission_bubble_media_access_handler.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/browser/ui/browser.h"
21 #include "chrome/browser/ui/browser_window.h"
22 #include "chrome/common/chrome_switches.h"
23 #include "chrome/common/pref_names.h"
24 #include "components/pref_registry/pref_registry_syncable.h"
25 #include "content/public/browser/browser_thread.h"
26 #include "content/public/browser/media_capture_devices.h"
27 #include "content/public/browser/notification_source.h"
28 #include "content/public/browser/render_frame_host.h"
29 #include "content/public/browser/render_process_host.h"
30 #include "content/public/browser/web_contents.h"
31 #include "content/public/common/media_stream_request.h"
32 #include "extensions/common/constants.h"
33 #include "media/base/media_switches.h"
34 #include "net/base/net_util.h"
36 #if defined(OS_CHROMEOS)
37 #include "ash/shell.h"
38 #endif // defined(OS_CHROMEOS)
40 #if defined(ENABLE_EXTENSIONS)
41 #include "chrome/browser/media/desktop_capture_access_handler.h"
42 #include "chrome/browser/media/extension_media_access_handler.h"
43 #include "chrome/browser/media/tab_capture_access_handler.h"
44 #include "extensions/browser/extension_registry.h"
45 #include "extensions/common/extension.h"
46 #include "extensions/common/permissions/permissions_data.h"
49 using content::BrowserThread
;
50 using content::MediaCaptureDevices
;
51 using content::MediaStreamDevices
;
55 // Finds a device in |devices| that has |device_id|, or NULL if not found.
56 const content::MediaStreamDevice
* FindDeviceWithId(
57 const content::MediaStreamDevices
& devices
,
58 const std::string
& device_id
) {
59 content::MediaStreamDevices::const_iterator iter
= devices
.begin();
60 for (; iter
!= devices
.end(); ++iter
) {
61 if (iter
->id
== device_id
) {
68 #if defined(ENABLE_EXTENSIONS)
69 inline DesktopCaptureAccessHandler
* ToDesktopCaptureAccessHandler(
70 MediaAccessHandler
* handler
) {
71 return static_cast<DesktopCaptureAccessHandler
*>(handler
);
76 MediaCaptureDevicesDispatcher
* MediaCaptureDevicesDispatcher::GetInstance() {
77 return Singleton
<MediaCaptureDevicesDispatcher
>::get();
80 MediaCaptureDevicesDispatcher::MediaCaptureDevicesDispatcher()
81 : is_device_enumeration_disabled_(false),
82 media_stream_capture_indicator_(new MediaStreamCaptureIndicator()) {
83 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
85 #if defined(OS_MACOSX)
86 // AVFoundation is used for video/audio device monitoring and video capture.
87 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
88 switches::kForceQTKit
)) {
89 base::CommandLine::ForCurrentProcess()->AppendSwitch(
90 switches::kEnableAVFoundation
);
94 #if defined(ENABLE_EXTENSIONS)
95 media_access_handlers_
.push_back(new ExtensionMediaAccessHandler());
96 media_access_handlers_
.push_back(new DesktopCaptureAccessHandler());
97 media_access_handlers_
.push_back(new TabCaptureAccessHandler());
99 media_access_handlers_
.push_back(new PermissionBubbleMediaAccessHandler());
102 MediaCaptureDevicesDispatcher::~MediaCaptureDevicesDispatcher() {}
104 void MediaCaptureDevicesDispatcher::RegisterProfilePrefs(
105 user_prefs::PrefRegistrySyncable
* registry
) {
106 registry
->RegisterStringPref(prefs::kDefaultAudioCaptureDevice
,
108 registry
->RegisterStringPref(prefs::kDefaultVideoCaptureDevice
,
112 bool MediaCaptureDevicesDispatcher::IsOriginForCasting(const GURL
& origin
) {
113 // Whitelisted tab casting extensions.
116 origin
.spec() == "chrome-extension://enhhojjnijigcajfphajepfemndkmdlo/" ||
118 origin
.spec() == "chrome-extension://hfaagokkkhdbgiakmmlclaapfelnkoah/" ||
120 origin
.spec() == "chrome-extension://fmfcbgogabcbclcofgocippekhfcmgfj/" ||
122 origin
.spec() == "chrome-extension://dliochdbjfkdbacpmhlcpmleaejidimm/" ||
123 // Google Cast Stable
124 origin
.spec() == "chrome-extension://boadgeojelhgndaghljhdicfkmllpafd/" ||
125 // http://crbug.com/457908
126 origin
.spec() == "chrome-extension://ekpaaapppgpmolpcldedioblbkmijaca/" ||
127 origin
.spec() == "chrome-extension://fjhoaacokmgbjemoflkofnenfaiekifl/";
130 void MediaCaptureDevicesDispatcher::AddObserver(Observer
* observer
) {
131 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
132 if (!observers_
.HasObserver(observer
))
133 observers_
.AddObserver(observer
);
136 void MediaCaptureDevicesDispatcher::RemoveObserver(Observer
* observer
) {
137 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
138 observers_
.RemoveObserver(observer
);
141 const MediaStreamDevices
&
142 MediaCaptureDevicesDispatcher::GetAudioCaptureDevices() {
143 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
144 if (is_device_enumeration_disabled_
|| !test_audio_devices_
.empty())
145 return test_audio_devices_
;
147 return MediaCaptureDevices::GetInstance()->GetAudioCaptureDevices();
150 const MediaStreamDevices
&
151 MediaCaptureDevicesDispatcher::GetVideoCaptureDevices() {
152 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
153 if (is_device_enumeration_disabled_
|| !test_video_devices_
.empty())
154 return test_video_devices_
;
156 return MediaCaptureDevices::GetInstance()->GetVideoCaptureDevices();
159 void MediaCaptureDevicesDispatcher::ProcessMediaAccessRequest(
160 content::WebContents
* web_contents
,
161 const content::MediaStreamRequest
& request
,
162 const content::MediaResponseCallback
& callback
,
163 const extensions::Extension
* extension
) {
164 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
166 for (MediaAccessHandler
* handler
: media_access_handlers_
) {
167 if (handler
->SupportsStreamType(request
.video_type
, extension
) ||
168 handler
->SupportsStreamType(request
.audio_type
, extension
)) {
169 handler
->HandleRequest(web_contents
, request
, callback
, extension
);
173 callback
.Run(content::MediaStreamDevices(),
174 content::MEDIA_DEVICE_NOT_SUPPORTED
, nullptr);
177 bool MediaCaptureDevicesDispatcher::CheckMediaAccessPermission(
178 content::WebContents
* web_contents
,
179 const GURL
& security_origin
,
180 content::MediaStreamType type
) {
181 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
182 return CheckMediaAccessPermission(web_contents
, security_origin
, type
,
186 bool MediaCaptureDevicesDispatcher::CheckMediaAccessPermission(
187 content::WebContents
* web_contents
,
188 const GURL
& security_origin
,
189 content::MediaStreamType type
,
190 const extensions::Extension
* extension
) {
191 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
192 for (MediaAccessHandler
* handler
: media_access_handlers_
) {
193 if (handler
->SupportsStreamType(type
, extension
)) {
194 return handler
->CheckMediaAccessPermission(web_contents
, security_origin
,
201 void MediaCaptureDevicesDispatcher::GetDefaultDevicesForProfile(
205 content::MediaStreamDevices
* devices
) {
206 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
207 DCHECK(audio
|| video
);
209 PrefService
* prefs
= profile
->GetPrefs();
210 std::string default_device
;
212 default_device
= prefs
->GetString(prefs::kDefaultAudioCaptureDevice
);
213 const content::MediaStreamDevice
* device
=
214 GetRequestedAudioDevice(default_device
);
216 device
= GetFirstAvailableAudioDevice();
218 devices
->push_back(*device
);
222 default_device
= prefs
->GetString(prefs::kDefaultVideoCaptureDevice
);
223 const content::MediaStreamDevice
* device
=
224 GetRequestedVideoDevice(default_device
);
226 device
= GetFirstAvailableVideoDevice();
228 devices
->push_back(*device
);
232 const content::MediaStreamDevice
*
233 MediaCaptureDevicesDispatcher::GetRequestedAudioDevice(
234 const std::string
& requested_audio_device_id
) {
235 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
236 const content::MediaStreamDevices
& audio_devices
= GetAudioCaptureDevices();
237 const content::MediaStreamDevice
* const device
=
238 FindDeviceWithId(audio_devices
, requested_audio_device_id
);
242 const content::MediaStreamDevice
*
243 MediaCaptureDevicesDispatcher::GetFirstAvailableAudioDevice() {
244 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
245 const content::MediaStreamDevices
& audio_devices
= GetAudioCaptureDevices();
246 if (audio_devices
.empty())
248 return &(*audio_devices
.begin());
251 const content::MediaStreamDevice
*
252 MediaCaptureDevicesDispatcher::GetRequestedVideoDevice(
253 const std::string
& requested_video_device_id
) {
254 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
255 const content::MediaStreamDevices
& video_devices
= GetVideoCaptureDevices();
256 const content::MediaStreamDevice
* const device
=
257 FindDeviceWithId(video_devices
, requested_video_device_id
);
261 const content::MediaStreamDevice
*
262 MediaCaptureDevicesDispatcher::GetFirstAvailableVideoDevice() {
263 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
264 const content::MediaStreamDevices
& video_devices
= GetVideoCaptureDevices();
265 if (video_devices
.empty())
267 return &(*video_devices
.begin());
270 void MediaCaptureDevicesDispatcher::DisableDeviceEnumerationForTesting() {
271 is_device_enumeration_disabled_
= true;
274 scoped_refptr
<MediaStreamCaptureIndicator
>
275 MediaCaptureDevicesDispatcher::GetMediaStreamCaptureIndicator() {
276 return media_stream_capture_indicator_
;
279 DesktopStreamsRegistry
*
280 MediaCaptureDevicesDispatcher::GetDesktopStreamsRegistry() {
281 if (!desktop_streams_registry_
)
282 desktop_streams_registry_
.reset(new DesktopStreamsRegistry());
283 return desktop_streams_registry_
.get();
286 void MediaCaptureDevicesDispatcher::OnAudioCaptureDevicesChanged() {
287 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
288 BrowserThread::PostTask(
289 BrowserThread::UI
, FROM_HERE
,
291 &MediaCaptureDevicesDispatcher::NotifyAudioDevicesChangedOnUIThread
,
292 base::Unretained(this)));
295 void MediaCaptureDevicesDispatcher::OnVideoCaptureDevicesChanged() {
296 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
297 BrowserThread::PostTask(
298 BrowserThread::UI
, FROM_HERE
,
300 &MediaCaptureDevicesDispatcher::NotifyVideoDevicesChangedOnUIThread
,
301 base::Unretained(this)));
304 void MediaCaptureDevicesDispatcher::OnMediaRequestStateChanged(
305 int render_process_id
,
308 const GURL
& security_origin
,
309 content::MediaStreamType stream_type
,
310 content::MediaRequestState state
) {
311 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
312 BrowserThread::PostTask(
313 BrowserThread::UI
, FROM_HERE
,
315 &MediaCaptureDevicesDispatcher::UpdateMediaRequestStateOnUIThread
,
316 base::Unretained(this), render_process_id
, render_frame_id
,
317 page_request_id
, security_origin
, stream_type
, state
));
320 void MediaCaptureDevicesDispatcher::OnCreatingAudioStream(
321 int render_process_id
,
322 int render_frame_id
) {
323 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
324 BrowserThread::PostTask(
325 BrowserThread::UI
, FROM_HERE
,
327 &MediaCaptureDevicesDispatcher::OnCreatingAudioStreamOnUIThread
,
328 base::Unretained(this), render_process_id
, render_frame_id
));
331 void MediaCaptureDevicesDispatcher::NotifyAudioDevicesChangedOnUIThread() {
332 MediaStreamDevices devices
= GetAudioCaptureDevices();
333 FOR_EACH_OBSERVER(Observer
, observers_
,
334 OnUpdateAudioDevices(devices
));
337 void MediaCaptureDevicesDispatcher::NotifyVideoDevicesChangedOnUIThread() {
338 MediaStreamDevices devices
= GetVideoCaptureDevices();
339 FOR_EACH_OBSERVER(Observer
, observers_
,
340 OnUpdateVideoDevices(devices
));
343 void MediaCaptureDevicesDispatcher::UpdateMediaRequestStateOnUIThread(
344 int render_process_id
,
347 const GURL
& security_origin
,
348 content::MediaStreamType stream_type
,
349 content::MediaRequestState state
) {
350 for (MediaAccessHandler
* handler
: media_access_handlers_
) {
351 if (handler
->SupportsStreamType(stream_type
, nullptr)) {
352 handler
->UpdateMediaRequestState(render_process_id
, render_frame_id
,
353 page_request_id
, stream_type
, state
);
358 #if defined(OS_CHROMEOS)
359 if (IsOriginForCasting(security_origin
) && IsVideoMediaType(stream_type
)) {
360 // Notify ash that casting state has changed.
361 if (state
== content::MEDIA_REQUEST_STATE_DONE
) {
362 ash::Shell::GetInstance()->OnCastingSessionStartedOrStopped(true);
363 } else if (state
== content::MEDIA_REQUEST_STATE_CLOSING
) {
364 ash::Shell::GetInstance()->OnCastingSessionStartedOrStopped(false);
369 FOR_EACH_OBSERVER(Observer
, observers_
,
370 OnRequestUpdate(render_process_id
,
376 void MediaCaptureDevicesDispatcher::OnCreatingAudioStreamOnUIThread(
377 int render_process_id
,
378 int render_frame_id
) {
379 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
380 FOR_EACH_OBSERVER(Observer
, observers_
,
381 OnCreatingAudioStream(render_process_id
, render_frame_id
));
384 bool MediaCaptureDevicesDispatcher::IsDesktopCaptureInProgress() {
385 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
386 #if defined(ENABLE_EXTENSIONS)
387 for (MediaAccessHandler
* handler
: media_access_handlers_
) {
388 if (handler
->SupportsStreamType(content::MEDIA_DESKTOP_VIDEO_CAPTURE
,
390 return ToDesktopCaptureAccessHandler(handler
)->IsCaptureInProgress();
397 void MediaCaptureDevicesDispatcher::SetTestAudioCaptureDevices(
398 const MediaStreamDevices
& devices
) {
399 test_audio_devices_
= devices
;
402 void MediaCaptureDevicesDispatcher::SetTestVideoCaptureDevices(
403 const MediaStreamDevices
& devices
) {
404 test_video_devices_
= devices
;