summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorxians@chromium.org <xians@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-16 17:03:12 +0000
committerxians@chromium.org <xians@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-16 17:03:12 +0000
commit0b6e4e0fe970154171fa633102704dbfa0ee4684 (patch)
tree219611af8eb7f1a10fbc835b5b050015406413bf
parentb40a1272637611b8a091b11c7ec84611b5e01d66 (diff)
downloadchromium_src-0b6e4e0fe970154171fa633102704dbfa0ee4684.zip
chromium_src-0b6e4e0fe970154171fa633102704dbfa0ee4684.tar.gz
chromium_src-0b6e4e0fe970154171fa633102704dbfa0ee4684.tar.bz2
This patch implements a device selection UI to chrome content setting, and the UI will be similar to that device UI in gmail-chat settings.
In order to achieve it, this patch: # added a MediaDevicesSelectionHandler in UI options to handle the device selection. # added a MediaCaptureDevicesDispatcher to chrome media, which will receive device changed notification from MediaStreamManager when a capture device is plugged in or unplugged, then it will cached the device lists and send the new lists to its observers. # MediaDevicesSelectionHandler will register itself as the observer to MediaCaptureDevicesDispatcher when it is initialized, so that it will always get up-to-date device lists. And it will remove itself on destruction. BUG=159398 TEST= go to content setting media section, select devices via the menus. Review URL: https://codereview.chromium.org/11364048 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@168227 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/app/generated_resources.grd6
-rw-r--r--chrome/browser/media/media_capture_devices_dispatcher.cc90
-rw-r--r--chrome/browser/media/media_capture_devices_dispatcher.h72
-rw-r--r--chrome/browser/media/media_internals.cc21
-rw-r--r--chrome/browser/media/media_internals.h8
-rw-r--r--chrome/browser/prefs/browser_prefs.cc2
-rw-r--r--chrome/browser/resources/options/content_settings.css14
-rw-r--r--chrome/browser/resources/options/content_settings.html8
-rw-r--r--chrome/browser/resources/options/content_settings.js58
-rw-r--r--chrome/browser/ui/webui/options/media_devices_selection_handler.cc150
-rw-r--r--chrome/browser/ui/webui/options/media_devices_selection_handler.h55
-rw-r--r--chrome/browser/ui/webui/options/options_ui.cc3
-rw-r--r--chrome/chrome_browser.gypi2
-rw-r--r--chrome/chrome_browser_ui.gypi2
-rw-r--r--chrome/common/pref_names.cc6
-rw-r--r--chrome/common/pref_names.h3
-rw-r--r--content/browser/browser_main_loop.cc4
-rw-r--r--content/browser/renderer_host/media/media_stream_manager.cc105
-rw-r--r--content/browser/renderer_host/media/media_stream_manager.h15
-rw-r--r--content/browser/renderer_host/media/mock_media_observer.h4
-rw-r--r--content/public/browser/media_observer.h8
-rw-r--r--content/test/webrtc_audio_device_test.cc8
22 files changed, 584 insertions, 60 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index f8a840d5..f41cd28 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -7898,6 +7898,12 @@ The following plug-in is unresponsive: <ph name="PLUGIN_NAME">$1
<message name="IDS_MEDIA_STREAM_ASK_RADIO" desc="A radio button in Content Settings dialog to allow site to query the permision to access capture devices.">
Ask me when a site requires access to my camera and microphone (recommended)
</message>
+ <message name="IDS_MEDIA_SELECTED_MIC_LABEL" desc="The label of the 'microphone device' select menu">
+ Microphone:
+ </message>
+ <message name="IDS_MEDIA_SELECTED_CAMERA_LABEL" desc="The label of the 'camera device' select menu">
+ Camera:
+ </message>
<message name="IDS_MEDIA_GALLERY_SECTION_LABEL" desc="The label for the section of content settings that relates to media galleries.">
Media galleries
</message>
diff --git a/chrome/browser/media/media_capture_devices_dispatcher.cc b/chrome/browser/media/media_capture_devices_dispatcher.cc
new file mode 100644
index 0000000..d246059
--- /dev/null
+++ b/chrome/browser/media/media_capture_devices_dispatcher.cc
@@ -0,0 +1,90 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/media/media_capture_devices_dispatcher.h"
+
+#include "chrome/browser/prefs/scoped_user_pref_update.h"
+#include "chrome/common/pref_names.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/common/media_stream_request.h"
+
+using content::BrowserThread;
+using content::MediaStreamDevices;
+
+MediaCaptureDevicesDispatcher::MediaCaptureDevicesDispatcher() {}
+
+MediaCaptureDevicesDispatcher::~MediaCaptureDevicesDispatcher() {}
+
+void MediaCaptureDevicesDispatcher::RegisterUserPrefs(PrefService* user_prefs) {
+ if (!user_prefs->FindPreference(prefs::kDefaultAudioCaptureDevice)) {
+ user_prefs->RegisterStringPref(prefs::kDefaultAudioCaptureDevice,
+ std::string(),
+ PrefService::UNSYNCABLE_PREF);
+ }
+ if (!user_prefs->FindPreference(prefs::kDefaultVideoCaptureDevice)) {
+ user_prefs->RegisterStringPref(prefs::kDefaultVideoCaptureDevice,
+ std::string(),
+ PrefService::UNSYNCABLE_PREF);
+ }
+}
+
+void MediaCaptureDevicesDispatcher::AudioCaptureDevicesChanged(
+ const MediaStreamDevices& devices) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&MediaCaptureDevicesDispatcher::UpdateAudioDevicesOnUIThread,
+ this, devices));
+}
+
+void MediaCaptureDevicesDispatcher::VideoCaptureDevicesChanged(
+ const MediaStreamDevices& devices) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&MediaCaptureDevicesDispatcher::UpdateVideoDevicesOnUIThread,
+ this, devices));
+}
+
+void MediaCaptureDevicesDispatcher::AddObserver(Observer* observer) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (!observers_.HasObserver(observer))
+ observers_.AddObserver(observer);
+}
+
+void MediaCaptureDevicesDispatcher::RemoveObserver(Observer* observer) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ observers_.RemoveObserver(observer);
+}
+
+const MediaStreamDevices&
+MediaCaptureDevicesDispatcher::GetAudioCaptureDevices() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ return audio_devices_;
+}
+
+const MediaStreamDevices&
+MediaCaptureDevicesDispatcher::GetVideoCaptureDevices() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ return video_devices_;
+}
+
+void MediaCaptureDevicesDispatcher::UpdateAudioDevicesOnUIThread(
+ const content::MediaStreamDevices& devices) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ audio_devices_ = devices;
+ FOR_EACH_OBSERVER(Observer, observers_,
+ OnUpdateAudioDevices(audio_devices_));
+}
+
+void MediaCaptureDevicesDispatcher::UpdateVideoDevicesOnUIThread(
+ const content::MediaStreamDevices& devices){
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ video_devices_ = devices;
+ FOR_EACH_OBSERVER(Observer, observers_,
+ OnUpdateVideoDevices(video_devices_));
+}
+
diff --git a/chrome/browser/media/media_capture_devices_dispatcher.h b/chrome/browser/media/media_capture_devices_dispatcher.h
new file mode 100644
index 0000000..955c4af
--- /dev/null
+++ b/chrome/browser/media/media_capture_devices_dispatcher.h
@@ -0,0 +1,72 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_MEDIA_MEDIA_CAPTURE_DEVICES_DISPATCHER_H_
+#define CHROME_BROWSER_MEDIA_MEDIA_CAPTURE_DEVICES_DISPATCHER_H_
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/observer_list.h"
+#include "content/public/common/media_stream_request.h"
+
+class PrefService;
+
+// This observer is owned by MediaInternals and deleted when MediaInternals
+// is deleted.
+class MediaCaptureDevicesDispatcher
+ : public base::RefCountedThreadSafe<MediaCaptureDevicesDispatcher> {
+ public:
+ class Observer {
+ public:
+ // Handle an information update consisting of a up-to-date audio capture
+ // device lists. This happens when a microphone is plugged in or unplugged.
+ virtual void OnUpdateAudioDevices(
+ const content::MediaStreamDevices& devices) {}
+
+ // Handle an information update consisting of a up-to-date video capture
+ // device lists. This happens when a camera is plugged in or unplugged.
+ virtual void OnUpdateVideoDevices(
+ const content::MediaStreamDevices& devices) {}
+
+ virtual ~Observer() {}
+ };
+
+ MediaCaptureDevicesDispatcher();
+
+ // Registers the preferences related to Media Stream default devices.
+ static void RegisterUserPrefs(PrefService* user_prefs);
+
+ // Called on IO thread when one audio device is plugged in or unplugged.
+ void AudioCaptureDevicesChanged(const content::MediaStreamDevices& devices);
+
+ // Called on IO thread when one video device is plugged in or unplugged.
+ void VideoCaptureDevicesChanged(const content::MediaStreamDevices& devices);
+
+ // Methods for observers. Called on UI thread.
+ // Observers should add themselves on construction and remove themselves
+ // on destruction.
+ void AddObserver(Observer* observer);
+ void RemoveObserver(Observer* observer);
+ const content::MediaStreamDevices& GetAudioCaptureDevices();
+ const content::MediaStreamDevices& GetVideoCaptureDevices();
+
+ private:
+ friend class base::RefCountedThreadSafe<MediaCaptureDevicesDispatcher>;
+ virtual ~MediaCaptureDevicesDispatcher();
+
+ // Called by the public functions, executed on UI thread.
+ void UpdateAudioDevicesOnUIThread(const content::MediaStreamDevices& devices);
+ void UpdateVideoDevicesOnUIThread(const content::MediaStreamDevices& devices);
+
+ // A list of cached audio capture devices.
+ content::MediaStreamDevices audio_devices_;
+
+ // A list of cached video capture devices.
+ content::MediaStreamDevices video_devices_;
+
+ // A list of observers for the device update notifications.
+ ObserverList<Observer> observers_;
+};
+
+#endif // CHROME_BROWSER_MEDIA_MEDIA_CAPTURE_DEVICES_DISPATCHER_H_
diff --git a/chrome/browser/media/media_internals.cc b/chrome/browser/media/media_internals.cc
index 8584ae4..c57fb48 100644
--- a/chrome/browser/media/media_internals.cc
+++ b/chrome/browser/media/media_internals.cc
@@ -7,6 +7,7 @@
#include "base/memory/scoped_ptr.h"
#include "base/string16.h"
#include "base/stringprintf.h"
+#include "chrome/browser/media/media_capture_devices_dispatcher.h"
#include "chrome/browser/media/media_internals_observer.h"
#include "chrome/browser/media/media_stream_capture_indicator.h"
#include "content/public/browser/browser_thread.h"
@@ -84,6 +85,18 @@ void MediaInternals::OnCaptureDevicesClosed(
devices);
}
+void MediaInternals::OnAudioCaptureDevicesChanged(
+ const content::MediaStreamDevices& devices) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ media_devices_dispatcher_->AudioCaptureDevicesChanged(devices);
+}
+
+void MediaInternals::OnVideoCaptureDevicesChanged(
+ const content::MediaStreamDevices& devices) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ media_devices_dispatcher_->VideoCaptureDevicesChanged(devices);
+}
+
void MediaInternals::OnMediaRequestStateChanged(
int render_process_id,
int render_view_id,
@@ -111,13 +124,19 @@ void MediaInternals::SendEverything() {
SendUpdate("media.onReceiveEverything", &data_);
}
+scoped_refptr<MediaCaptureDevicesDispatcher>
+MediaInternals::GetMediaCaptureDevicesDispatcher() {
+ return media_devices_dispatcher_;
+}
+
scoped_refptr<MediaStreamCaptureIndicator>
MediaInternals::GetMediaStreamCaptureIndicator() {
return media_stream_capture_indicator_.get();
}
MediaInternals::MediaInternals()
- : media_stream_capture_indicator_(new MediaStreamCaptureIndicator()) {
+ : media_stream_capture_indicator_(new MediaStreamCaptureIndicator()),
+ media_devices_dispatcher_(new MediaCaptureDevicesDispatcher()) {
}
void MediaInternals::UpdateAudioStream(
diff --git a/chrome/browser/media/media_internals.h b/chrome/browser/media/media_internals.h
index 9f15c5c..62bf9b5 100644
--- a/chrome/browser/media/media_internals.h
+++ b/chrome/browser/media/media_internals.h
@@ -12,6 +12,7 @@
#include "content/public/browser/media_observer.h"
#include "content/public/common/media_stream_request.h"
+class MediaCaptureDevicesDispatcher;
class MediaInternalsObserver;
class MediaStreamCaptureIndicator;
@@ -49,6 +50,10 @@ class MediaInternals : public content::MediaObserver {
int render_process_id,
int render_view_id,
const content::MediaStreamDevices& devices) OVERRIDE;
+ virtual void OnAudioCaptureDevicesChanged(
+ const content::MediaStreamDevices& devices) OVERRIDE;
+ virtual void OnVideoCaptureDevicesChanged(
+ const content::MediaStreamDevices& devices) OVERRIDE;
virtual void OnMediaRequestStateChanged(
int render_process_id,
int render_view_id,
@@ -63,6 +68,8 @@ class MediaInternals : public content::MediaObserver {
void SendEverything();
scoped_refptr<MediaStreamCaptureIndicator> GetMediaStreamCaptureIndicator();
+ scoped_refptr<MediaCaptureDevicesDispatcher>
+ GetMediaCaptureDevicesDispatcher();
private:
friend class MediaInternalsTest;
@@ -90,6 +97,7 @@ class MediaInternals : public content::MediaObserver {
DictionaryValue data_;
ObserverList<MediaInternalsObserver> observers_;
scoped_refptr<MediaStreamCaptureIndicator> media_stream_capture_indicator_;
+ scoped_refptr<MediaCaptureDevicesDispatcher> media_devices_dispatcher_;
DISALLOW_COPY_AND_ASSIGN(MediaInternals);
};
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index b2145c7..8ec5f38 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -30,6 +30,7 @@
#include "chrome/browser/intents/web_intents_util.h"
#include "chrome/browser/intranet_redirect_detector.h"
#include "chrome/browser/managed_mode/managed_mode.h"
+#include "chrome/browser/media/media_capture_devices_dispatcher.h"
#include "chrome/browser/metrics/metrics_log.h"
#include "chrome/browser/metrics/metrics_service.h"
#include "chrome/browser/metrics/variations/variations_service.h"
@@ -234,6 +235,7 @@ void RegisterUserPrefs(PrefService* user_prefs) {
HostContentSettingsMap::RegisterUserPrefs(user_prefs);
IncognitoModePrefs::RegisterUserPrefs(user_prefs);
InstantController::RegisterUserPrefs(user_prefs);
+ MediaCaptureDevicesDispatcher::RegisterUserPrefs(user_prefs);
NetPrefObserver::RegisterPrefs(user_prefs);
NewTabUI::RegisterUserPrefs(user_prefs);
PasswordManager::RegisterUserPrefs(user_prefs);
diff --git a/chrome/browser/resources/options/content_settings.css b/chrome/browser/resources/options/content_settings.css
index fa2768e..e17464f 100644
--- a/chrome/browser/resources/options/content_settings.css
+++ b/chrome/browser/resources/options/content_settings.css
@@ -74,3 +74,17 @@ div[role='listitem'][controlled-by] {
.sublabel {
-webkit-margin-start: 2em;
}
+
+.media-device-control {
+ display: table-row;
+}
+
+.media-device-control > span {
+ display: table-cell;
+ min-width: 145px;
+}
+
+.media-device-control > select {
+ display: table-cell;
+ width: 12em;
+} \ No newline at end of file
diff --git a/chrome/browser/resources/options/content_settings.html b/chrome/browser/resources/options/content_settings.html
index f44f17d..a0655c4 100644
--- a/chrome/browser/resources/options/content_settings.html
+++ b/chrome/browser/resources/options/content_settings.html
@@ -434,6 +434,14 @@
<section>
<h3 i18n-content="mediaStreamTabLabel"></h3>
<div>
+ <div class="media-device-control">
+ <span i18n-content="mediaSelectMicLabel"></span>
+ <select id="media-select-mic" class="weakrtl"></select>
+ </div>
+ <div class="media-device-control">
+ <span i18n-content="mediaSelectCameraLabel"></span>
+ <select id="media-select-camera" class="weakrtl"></select>
+ </div>
<div class="radio">
<span class="controlled-setting-with-label">
<input id="media-stream-ask" type="radio" name="media-stream"
diff --git a/chrome/browser/resources/options/content_settings.js b/chrome/browser/resources/options/content_settings.js
index 5b88c15..3958741 100644
--- a/chrome/browser/resources/options/content_settings.js
+++ b/chrome/browser/resources/options/content_settings.js
@@ -84,6 +84,11 @@ cr.define('options', function() {
$('pepper-flash-cameramic-section').style.display = 'none';
$('pepper-flash-cameramic-exceptions-div').style.display = 'none';
+
+ $('media-select-mic').addEventListener('change',
+ ContentSettings.setDefaultMicrophone_);
+ $('media-select-camera').addEventListener('change',
+ ContentSettings.setDefaultCamera_);
},
};
@@ -182,7 +187,58 @@ cr.define('options', function() {
ContentSettings.enablePepperFlashCameraMicSettings = function() {
$('pepper-flash-cameramic-section').style.display = '';
$('pepper-flash-cameramic-exceptions-div').style.display = '';
- }
+ };
+
+ /**
+ * Updates the microphone/camera devices menu with the given entries.
+ * @param {string} type The device type.
+ * @param {Array} devices List of available devices.
+ * @param {string} defaultdevice The unique id of the current default device.
+ */
+ ContentSettings.updateDevicesMenu = function(type, devices, defaultdevice) {
+ var deviceSelect = '';
+ if (type == 'mic') {
+ deviceSelect = $('media-select-mic');
+ } else if (type == 'camera') {
+ deviceSelect = $('media-select-camera');
+ } else {
+ console.error('Unknown device type for <device select> UI element: ' +
+ type);
+ return;
+ }
+
+ deviceSelect.textContent = '';
+
+ var deviceCount = devices.length;
+ var defaultIndex = -1;
+ for (var i = 0; i < deviceCount; i++) {
+ var device = devices[i];
+ var option = new Option(device.name, device.id);
+ if (option.value == defaultdevice)
+ defaultIndex = i;
+ deviceSelect.appendChild(option);
+ }
+ if (defaultIndex >= 0)
+ deviceSelect.selectedIndex = defaultIndex;
+ };
+
+ /**
+ * Set the default microphone device based on the popup selection.
+ * @private
+ */
+ ContentSettings.setDefaultMicrophone_ = function() {
+ var deviceSelect = $('media-select-mic');
+ chrome.send('setDefaultCaptureDevice', ['mic', deviceSelect.value]);
+ };
+
+ /**
+ * Set the default camera device based on the popup selection.
+ * @private
+ */
+ ContentSettings.setDefaultCamera_ = function() {
+ var deviceSelect = $('media-select-camera');
+ chrome.send('setDefaultCaptureDevice', ['camera', deviceSelect.value]);
+ };
// Export
return {
diff --git a/chrome/browser/ui/webui/options/media_devices_selection_handler.cc b/chrome/browser/ui/webui/options/media_devices_selection_handler.cc
new file mode 100644
index 0000000..25277a2
--- /dev/null
+++ b/chrome/browser/ui/webui/options/media_devices_selection_handler.cc
@@ -0,0 +1,150 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/options/media_devices_selection_handler.h"
+
+#include "base/bind.h"
+#include "chrome/browser/media/media_internals.h"
+#include "chrome/browser/prefs/pref_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/pref_names.h"
+
+namespace {
+
+const char kAudio[] = "mic";
+const char kVideo[] = "camera";
+
+} // namespace
+
+namespace options {
+
+MediaDevicesSelectionHandler::MediaDevicesSelectionHandler() {}
+
+MediaDevicesSelectionHandler::~MediaDevicesSelectionHandler() {
+ // Register to the device observer list to get up-to-date device lists.
+ MediaCaptureDevicesDispatcher* dispatcher =
+ MediaInternals::GetInstance()->GetMediaCaptureDevicesDispatcher();
+ dispatcher->RemoveObserver(this);
+}
+
+void MediaDevicesSelectionHandler::GetLocalizedValues(DictionaryValue* values) {
+ DCHECK(values);
+
+ static OptionsStringResource resources[] = {
+ { "mediaSelectMicLabel", IDS_MEDIA_SELECTED_MIC_LABEL },
+ { "mediaSelectCameraLabel", IDS_MEDIA_SELECTED_CAMERA_LABEL },
+ };
+
+ RegisterStrings(values, resources, arraysize(resources));
+}
+
+void MediaDevicesSelectionHandler::InitializePage() {
+ // Register to the device observer list to get up-to-date device lists.
+ MediaCaptureDevicesDispatcher* dispatcher =
+ MediaInternals::GetInstance()->GetMediaCaptureDevicesDispatcher();
+ dispatcher->AddObserver(this);
+
+ // Update the device selection menus.
+ UpdateDevicesMenuForType(AUDIO);
+ UpdateDevicesMenuForType(VIDEO);
+}
+
+void MediaDevicesSelectionHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback("setDefaultCaptureDevice",
+ base::Bind(&MediaDevicesSelectionHandler::SetDefaultCaptureDevice,
+ base::Unretained(this)));
+}
+
+void MediaDevicesSelectionHandler::OnUpdateAudioDevices(
+ const content::MediaStreamDevices& devices) {
+ UpdateDevicesMenu(AUDIO, devices);
+}
+
+void MediaDevicesSelectionHandler::OnUpdateVideoDevices(
+ const content::MediaStreamDevices& devices) {
+ UpdateDevicesMenu(VIDEO, devices);
+}
+
+void MediaDevicesSelectionHandler::SetDefaultCaptureDevice(
+ const ListValue* args) {
+ DCHECK_EQ(2U, args->GetSize());
+ std::string type, device;
+ if (!(args->GetString(0, &type) && args->GetString(1, &device))) {
+ NOTREACHED();
+ return;
+ }
+
+ DCHECK(!type.empty());
+ DCHECK(!device.empty());
+
+ Profile* profile = Profile::FromWebUI(web_ui());
+ PrefService* prefs = profile->GetPrefs();
+ if (type == kAudio)
+ prefs->SetString(prefs::kDefaultAudioCaptureDevice, device);
+ else if (type == kVideo)
+ prefs->SetString(prefs::kDefaultVideoCaptureDevice, device);
+ else
+ NOTREACHED();
+}
+
+void MediaDevicesSelectionHandler::UpdateDevicesMenu(
+ DeviceType type, const content::MediaStreamDevices& devices) {
+ // Get the default device unique id from prefs.
+ Profile* profile = Profile::FromWebUI(web_ui());
+ PrefService* prefs = profile->GetPrefs();
+ std::string default_device;
+ std::string device_type;
+ switch (type) {
+ case AUDIO:
+ default_device = prefs->GetString(prefs::kDefaultAudioCaptureDevice);
+ device_type = kAudio;
+ break;
+ case VIDEO:
+ default_device = prefs->GetString(prefs::kDefaultVideoCaptureDevice);
+ device_type = kVideo;
+ break;
+ }
+
+ // Build the list of devices to send to JS.
+ std::string default_id;
+ ListValue device_list;
+ for (size_t i = 0; i < devices.size(); ++i) {
+ DictionaryValue* entry = new DictionaryValue();
+ entry->SetString("name", devices[i].name);
+ entry->SetString("id", devices[i].device_id);
+ device_list.Append(entry);
+ if (devices[i].device_id == default_device)
+ default_id = default_device;
+ }
+
+ // Use the first device as the default device if the preferred default device
+ // does not exist in the OS.
+ if (!devices.empty() && default_id.empty())
+ default_id = devices[0].device_id;
+
+ StringValue default_value(default_id);
+ StringValue type_value(device_type);
+ web_ui()->CallJavascriptFunction("ContentSettings.updateDevicesMenu",
+ type_value,
+ device_list,
+ default_value);
+}
+
+void MediaDevicesSelectionHandler::UpdateDevicesMenuForType(DeviceType type) {
+ scoped_refptr<MediaCaptureDevicesDispatcher> dispatcher =
+ MediaInternals::GetInstance()->GetMediaCaptureDevicesDispatcher();
+ content::MediaStreamDevices devices;
+ switch (type) {
+ case AUDIO:
+ devices = dispatcher->GetAudioCaptureDevices();
+ break;
+ case VIDEO:
+ devices = dispatcher->GetVideoCaptureDevices();
+ break;
+ }
+
+ UpdateDevicesMenu(type, devices);
+}
+
+} // namespace options
diff --git a/chrome/browser/ui/webui/options/media_devices_selection_handler.h b/chrome/browser/ui/webui/options/media_devices_selection_handler.h
new file mode 100644
index 0000000..e060872
--- /dev/null
+++ b/chrome/browser/ui/webui/options/media_devices_selection_handler.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_MEDIA_DEVICES_SELECTION_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_OPTIONS_MEDIA_DEVICES_SELECTION_HANDLER_H_
+
+#include "chrome/browser/media/media_capture_devices_dispatcher.h"
+#include "chrome/browser/ui/webui/options/options_ui.h"
+#include "content/public/browser/web_contents.h"
+#include "grit/generated_resources.h"
+
+namespace options {
+
+// Handler for media devices selection in content settings.
+class MediaDevicesSelectionHandler
+ : public MediaCaptureDevicesDispatcher::Observer,
+ public OptionsPageUIHandler {
+ public:
+ MediaDevicesSelectionHandler();
+ virtual ~MediaDevicesSelectionHandler();
+
+ // OptionsPageUIHandler implementation.
+ virtual void GetLocalizedValues(base::DictionaryValue* values) OVERRIDE;
+ virtual void InitializePage() OVERRIDE;
+ virtual void RegisterMessages() OVERRIDE;
+
+ // MediaCaptureDevicesDispatcher::Observer implementation.
+ virtual void OnUpdateAudioDevices(
+ const content::MediaStreamDevices& devices) OVERRIDE;
+ virtual void OnUpdateVideoDevices(
+ const content::MediaStreamDevices& devices) OVERRIDE;
+
+ private:
+ enum DeviceType {
+ AUDIO,
+ VIDEO,
+ };
+
+ // Sets the default audio/video capture device for media. |args| includes the
+ // media type (kAuudio/kVideo) and the unique id of the new default device
+ // that the user has chosen.
+ void SetDefaultCaptureDevice(const base::ListValue* args);
+
+ // Helpers methods to update the device menus.
+ void UpdateDevicesMenuForType(DeviceType type);
+ void UpdateDevicesMenu(DeviceType type,
+ const content::MediaStreamDevices& devices);
+
+ DISALLOW_COPY_AND_ASSIGN(MediaDevicesSelectionHandler);
+};
+
+} // namespace options
+
+#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_MEDIA_DEVICES_SELECTION_HANDLER_H_
diff --git a/chrome/browser/ui/webui/options/options_ui.cc b/chrome/browser/ui/webui/options/options_ui.cc
index 3d0b6be..a719353 100644
--- a/chrome/browser/ui/webui/options/options_ui.cc
+++ b/chrome/browser/ui/webui/options/options_ui.cc
@@ -35,6 +35,7 @@
#include "chrome/browser/ui/webui/options/import_data_handler.h"
#include "chrome/browser/ui/webui/options/language_options_handler.h"
#include "chrome/browser/ui/webui/options/manage_profile_handler.h"
+#include "chrome/browser/ui/webui/options/media_devices_selection_handler.h"
#include "chrome/browser/ui/webui/options/media_galleries_handler.h"
#include "chrome/browser/ui/webui/options/options_sync_setup_handler.h"
#include "chrome/browser/ui/webui/options/password_manager_handler.h"
@@ -245,6 +246,8 @@ OptionsUI::OptionsUI(content::WebUI* web_ui)
AddOptionsPageUIHandler(localized_strings, new CookiesViewHandler());
AddOptionsPageUIHandler(localized_strings, new FontSettingsHandler());
AddOptionsPageUIHandler(localized_strings, new HomePageOverlayHandler());
+ AddOptionsPageUIHandler(localized_strings,
+ new MediaDevicesSelectionHandler());
AddOptionsPageUIHandler(localized_strings, new MediaGalleriesHandler());
AddOptionsPageUIHandler(localized_strings, new WebIntentsSettingsHandler());
#if defined(OS_CHROMEOS)
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 7a4b2b3..f25281e 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -981,6 +981,8 @@
'browser/managed_mode/managed_mode.h',
'browser/managed_mode/managed_mode_url_filter.cc',
'browser/managed_mode/managed_mode_url_filter.h',
+ 'browser/media/media_capture_devices_dispatcher.cc',
+ 'browser/media/media_capture_devices_dispatcher.h',
'browser/media/media_internals.cc',
'browser/media/media_internals.h',
'browser/media/media_stream_capture_indicator.cc',
diff --git a/chrome/chrome_browser_ui.gypi b/chrome/chrome_browser_ui.gypi
index 46eeb7f0..6b08ef8 100644
--- a/chrome/chrome_browser_ui.gypi
+++ b/chrome/chrome_browser_ui.gypi
@@ -1992,6 +1992,8 @@
'browser/ui/webui/options/language_options_handler_common.h',
'browser/ui/webui/options/manage_profile_handler.cc',
'browser/ui/webui/options/manage_profile_handler.h',
+ 'browser/ui/webui/options/media_devices_selection_handler.cc',
+ 'browser/ui/webui/options/media_devices_selection_handler.h',
'browser/ui/webui/options/media_galleries_handler.cc',
'browser/ui/webui/options/media_galleries_handler.h',
'browser/ui/webui/options/options_sync_setup_handler.cc',
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index 07100c6..5e5f6ce 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -1056,6 +1056,12 @@ const char kGeolocationEnabled[] = "geolocation.enabled";
// Dictionary that maps [frame, toplevel] to their Geolocation content setting.
const char kGeolocationContentSettings[] = "geolocation.content_settings";
+// The default audio capture device used by the Media content setting.
+const char kDefaultAudioCaptureDevice[] = "media.default_audio_capture_device";
+
+// The default video capture device used by the Media content setting.
+const char kDefaultVideoCaptureDevice[] = "media.default_video_capture_Device";
+
// Preference to disable 3D APIs (WebGL, Pepper 3D).
const char kDisable3DAPIs[] = "disable_3d_apis";
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 3af7178..a3e9496 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -622,6 +622,9 @@ extern const char kGeolocationContentSettings[];
extern const char kGeolocationEnabled[];
#endif
+extern const char kDefaultAudioCaptureDevice[];
+extern const char kDefaultVideoCaptureDevice[];
+
extern const char kRemoteAccessHostFirewallTraversal[];
extern const char kRemoteAccessHostRequireTwoFactor[];
extern const char kRemoteAccessHostDomain[];
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index 777b31a..01dd2e7 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -348,7 +348,6 @@ void BrowserMainLoop::MainMessageLoopStart() {
}
online_state_observer_.reset(new BrowserOnlineStateObserver);
- media_stream_manager_.reset(new MediaStreamManager(audio_manager_.get()));
// Prior to any processing happening on the io thread, we create the
// plugin service as it is predominantly used from the io thread,
@@ -669,6 +668,9 @@ void BrowserMainLoop::BrowserThreadsStarted() {
// RDH needs the IO thread to be created.
resource_dispatcher_host_.reset(new ResourceDispatcherHostImpl());
+ // MediaStreamManager needs the IO thread to be created.
+ media_stream_manager_.reset(new MediaStreamManager(audio_manager_.get()));
+
// Initialize the GpuDataManager before we set up the MessageLoops because
// otherwise we'll trigger the assertion about doing IO on the UI thread.
GpuDataManagerImpl::GetInstance()->Initialize();
diff --git a/content/browser/renderer_host/media/media_stream_manager.cc b/content/browser/renderer_host/media/media_stream_manager.cc
index ca625e4..d040f0a 100644
--- a/content/browser/renderer_host/media/media_stream_manager.cc
+++ b/content/browser/renderer_host/media/media_stream_manager.cc
@@ -153,6 +153,17 @@ MediaStreamManager::MediaStreamManager(media::AudioManager* audio_manager)
DCHECK(audio_manager_);
memset(active_enumeration_ref_count_, 0,
sizeof(active_enumeration_ref_count_));
+
+ // Some unit tests create the MSM in the IO thread and assumes the
+ // initialization is done synchronously.
+ if (BrowserThread::CurrentlyOn(BrowserThread::IO)) {
+ InitializeDeviceManagersOnIOThread();
+ } else {
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&MediaStreamManager::InitializeDeviceManagersOnIOThread,
+ base::Unretained(this)));
+ }
}
MediaStreamManager::~MediaStreamManager() {
@@ -163,14 +174,12 @@ MediaStreamManager::~MediaStreamManager() {
VideoCaptureManager* MediaStreamManager::video_capture_manager() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- EnsureDeviceManagersStarted();
DCHECK(video_capture_manager_);
return video_capture_manager_;
}
AudioInputDeviceManager* MediaStreamManager::audio_input_device_manager() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- EnsureDeviceManagersStarted();
DCHECK(audio_input_device_manager_);
return audio_input_device_manager_;
}
@@ -392,7 +401,6 @@ void MediaStreamManager::EnumerateDevices(
base::Unretained(this), cache, *label));
} else {
StartEnumeration(&new_request, label);
- StartMonitoring();
}
}
@@ -403,9 +411,6 @@ void MediaStreamManager::StopEnumerateDevices(const std::string& label) {
if (it != requests_.end()) {
DCHECK_EQ(it->second.type, DeviceRequest::ENUMERATE_DEVICES);
requests_.erase(it);
- if (!HasEnumerationRequest()) {
- StopMonitoring();
- }
}
}
@@ -476,15 +481,24 @@ void MediaStreamManager::SendCachedDeviceList(
void MediaStreamManager::StartMonitoring() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ if (!base::SystemMonitor::Get())
+ return;
+
if (!monitoring_started_) {
monitoring_started_ = true;
base::SystemMonitor::Get()->AddDevicesChangedObserver(this);
+
+ // Enumerate the devices now to post the device lists to media observer.
+ ++active_enumeration_ref_count_[MEDIA_DEVICE_AUDIO_CAPTURE];
+ audio_input_device_manager_->EnumerateDevices();
+ ++active_enumeration_ref_count_[MEDIA_DEVICE_VIDEO_CAPTURE];
+ video_capture_manager_->EnumerateDevices();
}
}
void MediaStreamManager::StopMonitoring() {
DCHECK_EQ(MessageLoop::current(), io_loop_);
- if (monitoring_started_ && !HasEnumerationRequest()) {
+ if (monitoring_started_) {
base::SystemMonitor::Get()->RemoveDevicesChangedObserver(this);
monitoring_started_ = false;
ClearEnumerationCache(&audio_enumeration_cache_);
@@ -546,7 +560,7 @@ void MediaStreamManager::PostRequestToUI(const std::string& label) {
request.security_origin);
}
-void MediaStreamManager::EnsureDeviceManagersStarted() {
+void MediaStreamManager::InitializeDeviceManagersOnIOThread() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (device_thread_.get())
return;
@@ -568,6 +582,10 @@ void MediaStreamManager::EnsureDeviceManagersStarted() {
// and the device managers.
io_loop_ = MessageLoop::current();
io_loop_->AddDestructionObserver(this);
+
+ // Start the devices monitoring since the media observer needs up-to-date
+ // device lists.
+ StartMonitoring();
}
void MediaStreamManager::Opened(MediaStreamType stream_type,
@@ -657,21 +675,22 @@ void MediaStreamManager::DevicesEnumerated(
MediaStreamType stream_type, const StreamDeviceInfoArray& devices) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- // Only cache the device list when there is an EnumerateDevices request, since
- // other requests don't turn on device monitoring.
+ // Only cache the device list when the device list has been changed.
bool need_update_clients = false;
EnumerationCache* cache = (stream_type == MEDIA_DEVICE_AUDIO_CAPTURE ?
&audio_enumeration_cache_ : &video_enumeration_cache_);
- if (HasEnumerationRequest(stream_type) &&
- (!cache->valid ||
+ if (!cache->valid ||
devices.size() != cache->devices.size() ||
!std::equal(devices.begin(), devices.end(), cache->devices.begin(),
- StreamDeviceInfo::IsEqual))) {
+ StreamDeviceInfo::IsEqual)) {
cache->valid = true;
cache->devices = devices;
need_update_clients = true;
}
+ if (need_update_clients && monitoring_started_)
+ NotifyDevicesChanged(stream_type, devices);
+
// Publish the result for all requests waiting for device list(s).
// Find the requests waiting for this device list, store their labels and
// release the iterator before calling device settings. We might get a call
@@ -913,6 +932,32 @@ void MediaStreamManager::DevicesFromRequest(
}
}
+void MediaStreamManager::NotifyDevicesChanged(
+ MediaStreamType stream_type,
+ const StreamDeviceInfoArray& devices) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ MediaObserver* media_observer =
+ GetContentClient()->browser()->GetMediaObserver();
+ if (media_observer == NULL)
+ return;
+
+ // Map the devices to MediaStreamDevices.
+ MediaStreamDevices new_devices;
+ for (StreamDeviceInfoArray::const_iterator it = devices.begin();
+ it != devices.end(); ++it) {
+ new_devices.push_back(MediaStreamDevice(
+ it->stream_type, it->device_id, it->name));
+ }
+
+ if (IsAudioMediaType(stream_type)) {
+ media_observer->OnAudioCaptureDevicesChanged(new_devices);
+ } else if (IsVideoMediaType(stream_type)) {
+ media_observer->OnVideoCaptureDevicesChanged(new_devices);
+ } else {
+ NOTREACHED();
+ }
+}
+
bool MediaStreamManager::RequestDone(const DeviceRequest& request) const {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
@@ -968,24 +1013,14 @@ void MediaStreamManager::OnDevicesChanged(
// changes (from the operating system).
MediaStreamType stream_type;
- EnumerationCache* cache;
if (device_type == base::SystemMonitor::DEVTYPE_AUDIO_CAPTURE) {
stream_type = MEDIA_DEVICE_AUDIO_CAPTURE;
- cache = &audio_enumeration_cache_;
} else if (device_type == base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE) {
stream_type = MEDIA_DEVICE_VIDEO_CAPTURE;
- cache = &video_enumeration_cache_;
} else {
return; // Uninteresting device change.
}
- if (!HasEnumerationRequest(stream_type)) {
- // There is no request for that type, No need to enumerate devices.
- // Therefore, invalidate the cache of that type.
- ClearEnumerationCache(cache);
- return;
- }
-
// Always do enumeration even though some enumeration is in progress,
// because those enumeration commands could be sent before these devices
// change.
@@ -993,28 +1028,4 @@ void MediaStreamManager::OnDevicesChanged(
GetDeviceManager(stream_type)->EnumerateDevices();
}
-bool MediaStreamManager::HasEnumerationRequest() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- for (DeviceRequests::iterator it = requests_.begin();
- it != requests_.end(); ++it) {
- if (it->second.type == DeviceRequest::ENUMERATE_DEVICES) {
- return true;
- }
- }
- return false;
-}
-
-bool MediaStreamManager::HasEnumerationRequest(
- MediaStreamType stream_type) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- for (DeviceRequests::iterator it = requests_.begin();
- it != requests_.end(); ++it) {
- if (it->second.type == DeviceRequest::ENUMERATE_DEVICES &&
- Requested(it->second.options, stream_type)) {
- return true;
- }
- }
- return false;
-}
-
} // namespace content
diff --git a/content/browser/renderer_host/media/media_stream_manager.h b/content/browser/renderer_host/media/media_stream_manager.h
index 439e30d..3364520 100644
--- a/content/browser/renderer_host/media/media_stream_manager.h
+++ b/content/browser/renderer_host/media/media_stream_manager.h
@@ -186,6 +186,10 @@ class CONTENT_EXPORT MediaStreamManager
StreamDeviceInfoArray devices;
};
+ // Initializes the device managers on IO thread. Auto-starts the device
+ // thread and registers this as a listener with the device managers.
+ void InitializeDeviceManagersOnIOThread();
+
// Helpers for signaling the media observer that new capture devices are
// opened/closed.
void NotifyDevicesOpened(const DeviceRequest& request);
@@ -193,21 +197,20 @@ class CONTENT_EXPORT MediaStreamManager
void DevicesFromRequest(const DeviceRequest& request,
MediaStreamDevices* devices);
+ // Helper for sending up-to-date device lists to media observer when a
+ // capture device is plugged in or unplugged.
+ void NotifyDevicesChanged(MediaStreamType stream_type,
+ const StreamDeviceInfoArray& devices);
+
// Helpers.
bool RequestDone(const MediaStreamManager::DeviceRequest& request) const;
MediaStreamProvider* GetDeviceManager(MediaStreamType stream_type);
void StartEnumeration(DeviceRequest* new_request,
std::string* label);
void AddRequest(const DeviceRequest& new_request, std::string* label);
- bool HasEnumerationRequest(MediaStreamType type);
- bool HasEnumerationRequest();
void ClearEnumerationCache(EnumerationCache* cache);
void PostRequestToUI(const std::string& label);
- // Helper to create the device managers, if needed. Auto-starts the device
- // thread and registers this as a listener with the device managers.
- void EnsureDeviceManagersStarted();
-
// Sends cached device list to a client corresponding to the request
// identified by |label|.
void SendCachedDeviceList(EnumerationCache* cache, const std::string& label);
diff --git a/content/browser/renderer_host/media/mock_media_observer.h b/content/browser/renderer_host/media/mock_media_observer.h
index 458a396..afcc70f 100644
--- a/content/browser/renderer_host/media/mock_media_observer.h
+++ b/content/browser/renderer_host/media/mock_media_observer.h
@@ -35,6 +35,10 @@ class MockMediaObserver : public MediaObserver {
MOCK_METHOD3(OnCaptureDevicesClosed,
void(int render_process_id, int render_view_id,
const MediaStreamDevices& devices));
+ MOCK_METHOD1(OnAudioCaptureDevicesChanged,
+ void(const MediaStreamDevices& devices));
+ MOCK_METHOD1(OnVideoCaptureDevicesChanged,
+ void(const MediaStreamDevices& devices));
MOCK_METHOD4(OnMediaRequestStateChanged,
void(int render_process_id, int render_view_id,
const MediaStreamDevice& device,
diff --git a/content/public/browser/media_observer.h b/content/public/browser/media_observer.h
index 7e33d89..cfb0667 100644
--- a/content/public/browser/media_observer.h
+++ b/content/public/browser/media_observer.h
@@ -50,6 +50,14 @@ class MediaObserver {
int render_view_id,
const MediaStreamDevices& devices) = 0;
+ // Called when a audio capture device is plugged in or unplugged.
+ virtual void OnAudioCaptureDevicesChanged(
+ const MediaStreamDevices& devices) = 0;
+
+ // Called when a video capture device is plugged in or unplugged.
+ virtual void OnVideoCaptureDevicesChanged(
+ const MediaStreamDevices& devices) = 0;
+
// Called when a media request changes state.
virtual void OnMediaRequestStateChanged(
int render_process_id,
diff --git a/content/test/webrtc_audio_device_test.cc b/content/test/webrtc_audio_device_test.cc
index 0fc3e2d..78013ad 100644
--- a/content/test/webrtc_audio_device_test.cc
+++ b/content/test/webrtc_audio_device_test.cc
@@ -132,10 +132,6 @@ void WebRTCAudioDeviceTest::SetUp() {
ui_thread_.reset(new TestBrowserThread(BrowserThread::UI,
MessageLoop::current()));
- // Create our own AudioManager and MediaStreamManager.
- audio_manager_.reset(media::AudioManager::Create());
- media_stream_manager_.reset(new MediaStreamManager(audio_manager_.get()));
-
// Construct the resource context on the UI thread.
resource_context_.reset(new MockRTCResourceContext);
@@ -211,6 +207,10 @@ void WebRTCAudioDeviceTest::InitializeIOThread(const char* thread_name) {
resource_context->set_request_context(test_request_context_.get());
media_observer_.reset(new MockMediaObserver());
+ // Create our own AudioManager and MediaStreamManager.
+ audio_manager_.reset(media::AudioManager::Create());
+ media_stream_manager_.reset(new MediaStreamManager(audio_manager_.get()));
+
has_input_devices_ = audio_manager_->HasAudioInputDevices();
has_output_devices_ = audio_manager_->HasAudioOutputDevices();