summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorhenrika@chromium.org <henrika@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-11-24 07:18:19 +0000
committerhenrika@chromium.org <henrika@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-11-24 07:18:19 +0000
commitd5577f7266e34ed412fd3b9b9aed2450094b5995 (patch)
tree806c82daafc17fc3700ea9800217dbbbf4fa9ea3 /media
parenta3c8aa0b48eefddfe8718febb58bd7a685339e08 (diff)
downloadchromium_src-d5577f7266e34ed412fd3b9b9aed2450094b5995.zip
chromium_src-d5577f7266e34ed412fd3b9b9aed2450094b5995.tar.gz
chromium_src-d5577f7266e34ed412fd3b9b9aed2450094b5995.tar.bz2
Adds support for capture device enumeration on Windows.
Example output (two devices found on Windows 7 machine): device_name: Microphone (6- SB Arena Headset) unique_id: {0.0.1.00000000}.{24515814-26fd-4382-b4bc-0a8d847ed853} device_name: Microphone (Realtek High Definition Audio) unique_id: {0.0.1.00000000}.{8db6020f-18e3-4f25-b6f5-7726c9122574} BUG=None TEST=audio_input_device_unittest.cc Review URL: http://codereview.chromium.org/8606006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@111500 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r--media/audio/audio_input_device_unittest.cc139
-rw-r--r--media/audio/win/audio_manager_win.cc29
-rw-r--r--media/audio/win/audio_manager_win.h19
-rw-r--r--media/audio/win/device_enumeration_win.cc125
-rw-r--r--media/audio/win/device_enumeration_win.h26
-rw-r--r--media/media.gyp2
6 files changed, 319 insertions, 21 deletions
diff --git a/media/audio/audio_input_device_unittest.cc b/media/audio/audio_input_device_unittest.cc
index e709936..2d50f4b 100644
--- a/media/audio/audio_input_device_unittest.cc
+++ b/media/audio/audio_input_device_unittest.cc
@@ -2,32 +2,145 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/environment.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/win/scoped_com_initializer.h"
+#include "media/audio/audio_manager.h"
#include "media/audio/audio_manager_base.h"
+#if defined(OS_WIN)
+#include "media/audio/win/audio_manager_win.h"
+#endif
#include "testing/gtest/include/gtest/gtest.h"
-namespace media {
+using base::win::ScopedCOMInitializer;
+using media::AudioDeviceNames;
-// Test that devices can be enumerated.
-TEST(AudioInputDeviceTest, EnumerateDevices) {
- AudioDeviceNames device_names;
- ASSERT_TRUE(AudioManager::GetAudioManager() != NULL);
- AudioManager::GetAudioManager()->GetAudioInputDeviceNames(
- &device_names);
+// Test fixture which allows us to override the default enumeration API on
+// Windows.
+class AudioInputDeviceTest
+ : public ::testing::Test {
+ protected:
+#if defined(OS_WIN)
+ // Store current device-enumeration type to ensure that it can be restored
+ // after last test in this test suite.
+ static void SetUpTestCase() {
+ enumeration_type_ = static_cast<AudioManagerWin*>(
+ AudioManager::GetAudioManager())->enumeration_type();
+ }
+
+ // Restore pre-test state of device-enumeration type.
+ static void TearDownTestCase() {
+ static_cast<AudioManagerWin*>(
+ AudioManager::GetAudioManager())->SetEnumerationType(enumeration_type_);
+ }
+
+ bool SetMMDeviceEnumeration(AudioManager* audio_manager) {
+ AudioManagerWin* amw = static_cast<AudioManagerWin*>(audio_manager);
+ // Windows Wave is used as default if Windows XP was detected =>
+ // return false since MMDevice is not supported on XP.
+ if (amw->enumeration_type() == AudioManagerWin::kWaveEnumeration)
+ return false;
+
+ amw->SetEnumerationType(AudioManagerWin::kMMDeviceEnumeration);
+ return true;
+ }
+
+ void SetWaveEnumeration(AudioManager* audio_manager) {
+ AudioManagerWin* amw = static_cast<AudioManagerWin*>(audio_manager);
+ amw->SetEnumerationType(AudioManagerWin::kWaveEnumeration);
+ }
+
+ // Stores the pre-test state representing the device-enumeration type.
+ static AudioManagerWin::EnumerationType enumeration_type_;
+#endif
+};
+
+#if defined(OS_WIN)
+AudioManagerWin::EnumerationType AudioInputDeviceTest::enumeration_type_ =
+ AudioManagerWin::kUninitializedEnumeration;
+#endif
+
+// Convenience method which ensures that we are not running on the build
+// bots which lacks audio device support.
+static bool CanRunAudioTests() {
+ scoped_ptr<base::Environment> env(base::Environment::Create());
+ if (env->HasVar("CHROME_HEADLESS"))
+ return false;
+ return true;
+}
+
+// Helper method which verifies that the device list starts with a valid
+// default record followed by non-default device names.
+static void CheckDeviceNames(const AudioDeviceNames& device_names) {
if (!device_names.empty()) {
AudioDeviceNames::const_iterator it = device_names.begin();
- // The first device in the list is the prepended default device.
+
+ // The first device in the list should always be the default device.
EXPECT_EQ(std::string(AudioManagerBase::kDefaultDeviceName),
- it->device_name);
+ it->device_name);
EXPECT_EQ(std::string(AudioManagerBase::kDefaultDeviceId), it->unique_id);
++it;
- // Other devices should have non-empty name and id.
+ // Other devices should have non-empty name and id and should not contain
+ // default name or id.
while (it != device_names.end()) {
- EXPECT_NE("", it->device_name);
- EXPECT_NE("", it->unique_id);
+ EXPECT_FALSE(it->device_name.empty());
+ EXPECT_FALSE(it->unique_id.empty());
+ EXPECT_NE(std::string(AudioManagerBase::kDefaultDeviceName),
+ it->device_name);
+ EXPECT_NE(std::string(AudioManagerBase::kDefaultDeviceId),
+ it->unique_id);
++it;
}
}
}
-} // namespace media
+// Test that devices can be enumerated.
+TEST_F(AudioInputDeviceTest, EnumerateDevices) {
+ if (!CanRunAudioTests())
+ return;
+ // The MMDevice API requires a correct COM environment.
+ ScopedCOMInitializer com_init(ScopedCOMInitializer::kMTA);
+ AudioDeviceNames device_names;
+ AudioManager* audio_man = AudioManager::GetAudioManager();
+ EXPECT_TRUE(audio_man != NULL);
+ audio_man->GetAudioInputDeviceNames(&device_names);
+ CheckDeviceNames(device_names);
+}
+
+// Run additional tests for Windows since enumeration can be done using
+// two different APIs. MMDevice is default for Vista and higher and Wave
+// is default for XP and lower.
+#if defined(OS_WIN)
+
+// Override default enumeration API and force usage of Windows MMDevice.
+// This test will only run on Windows Vista and higher.
+TEST_F(AudioInputDeviceTest, EnumerateDevicesWinMMDevice) {
+ if (!CanRunAudioTests())
+ return;
+ // The MMDevice API requires a correct COM environment.
+ ScopedCOMInitializer com_init(ScopedCOMInitializer::kMTA);
+ AudioDeviceNames device_names;
+ AudioManager* audio_man = AudioManager::GetAudioManager();
+ if (!SetMMDeviceEnumeration(audio_man)) {
+ // Usage of MMDevice will fail on XP and lower.
+ return;
+ }
+ audio_man->GetAudioInputDeviceNames(&device_names);
+ CheckDeviceNames(device_names);
+}
+
+// Override default enumeration API and force usage of Windows Wave.
+// This test will run on Windows XP, Windows Vista and Windows 7.
+TEST_F(AudioInputDeviceTest, EnumerateDevicesWinWave) {
+ if (!CanRunAudioTests())
+ return;
+ AudioDeviceNames device_names;
+ AudioManager* audio_man = AudioManager::GetAudioManager();
+ EXPECT_TRUE(audio_man != NULL);
+ SetWaveEnumeration(audio_man);
+ audio_man->GetAudioInputDeviceNames(&device_names);
+ CheckDeviceNames(device_names);
+}
+
+#endif
diff --git a/media/audio/win/audio_manager_win.cc b/media/audio/win/audio_manager_win.cc
index c999f61..92f59d8 100644
--- a/media/audio/win/audio_manager_win.cc
+++ b/media/audio/win/audio_manager_win.cc
@@ -24,6 +24,7 @@
#include "media/audio/win/audio_low_latency_input_win.h"
#include "media/audio/win/audio_low_latency_output_win.h"
#include "media/audio/win/audio_manager_win.h"
+#include "media/audio/win/device_enumeration_win.h"
#include "media/audio/win/wavein_input_win.h"
#include "media/audio/win/waveout_output_win.h"
#include "media/base/limits.h"
@@ -99,6 +100,13 @@ static string16 GetDeviceAndDriverInfo(HDEVINFO device_info,
AudioManagerWin::AudioManagerWin()
: num_output_streams_(0) {
+ if (base::win::GetVersion() <= base::win::VERSION_XP) {
+ // Use the Wave API for device enumeration if XP or lower.
+ enumeration_type_ = kWaveEnumeration;
+ } else {
+ // Use the MMDevice API for device enumeration if Vista or higher.
+ enumeration_type_ = kMMDeviceEnumeration;
+ }
}
AudioManagerWin::~AudioManagerWin() {
@@ -280,19 +288,26 @@ void AudioManagerWin::ShowAudioInputSettings() {
void AudioManagerWin::GetAudioInputDeviceNames(
media::AudioDeviceNames* device_names) {
- // TODO(xians): query a full list of valid devices.
- if (HasAudioInputDevices()) {
- // Add the default device to the list.
- // We use index 0 to make up the unique_id to identify the
- // default devices.
+ DCHECK(enumeration_type() != kUninitializedEnumeration);
+ // Enumerate all active audio-endpoint capture devices.
+ if (enumeration_type() == kWaveEnumeration) {
+ // Utilize the Wave API for Windows XP.
+ GetInputDeviceNamesWinXP(device_names);
+ } else {
+ // Utilize the MMDevice API (part of Core Audio) for Vista and higher.
+ GetInputDeviceNamesWin(device_names);
+ }
+
+ // Always add default device parameters as first element.
+ if (!device_names->empty()) {
media::AudioDeviceName name;
name.device_name = AudioManagerBase::kDefaultDeviceName;
name.unique_id = AudioManagerBase::kDefaultDeviceId;
- device_names->push_back(name);
+ device_names->push_front(name);
}
}
-// static
+/// static
AudioManager* AudioManager::CreateAudioManager() {
return new AudioManagerWin();
}
diff --git a/media/audio/win/audio_manager_win.h b/media/audio/win/audio_manager_win.h
index f1d73d8..6c519ee 100644
--- a/media/audio/win/audio_manager_win.h
+++ b/media/audio/win/audio_manager_win.h
@@ -6,9 +6,11 @@
#define MEDIA_AUDIO_WIN_AUDIO_MANAGER_WIN_H_
#include <windows.h>
+#include <string>
#include "base/basictypes.h"
#include "base/compiler_specific.h"
+#include "base/gtest_prod_util.h"
#include "media/audio/audio_manager_base.h"
class PCMWaveOutAudioOutputStream;
@@ -16,7 +18,7 @@ class PCMWaveOutAudioOutputStream;
// Windows implementation of the AudioManager singleton. This class is internal
// to the audio output and only internal users can call methods not exposed by
// the AudioManager class.
-class AudioManagerWin : public AudioManagerBase {
+class MEDIA_EXPORT AudioManagerWin : public AudioManagerBase {
public:
AudioManagerWin();
// Implementation of AudioManager.
@@ -42,8 +44,23 @@ class AudioManagerWin : public AudioManagerBase {
void ReleaseInputStream(AudioInputStream* stream);
private:
+ enum EnumerationType {
+ kUninitializedEnumeration = 0,
+ kMMDeviceEnumeration,
+ kWaveEnumeration,
+ };
+
virtual ~AudioManagerWin();
+ // Allow unit test to modify the utilized enumeration API.
+ friend class AudioInputDeviceTest;
+
+ EnumerationType enumeration_type_;
+ EnumerationType enumeration_type() { return enumeration_type_; }
+ void SetEnumerationType(EnumerationType type) {
+ enumeration_type_ = type;
+ }
+
// Number of currently open output streams.
int num_output_streams_;
diff --git a/media/audio/win/device_enumeration_win.cc b/media/audio/win/device_enumeration_win.cc
new file mode 100644
index 0000000..0087a89
--- /dev/null
+++ b/media/audio/win/device_enumeration_win.cc
@@ -0,0 +1,125 @@
+// Copyright (c) 2011 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 <MMDeviceAPI.h>
+#include <mmsystem.h>
+#include <Functiondiscoverykeys_devpkey.h> // MMDeviceAPI.h must come first
+
+#include "media/audio/win/audio_manager_win.h"
+
+#include "base/logging.h"
+#include "base/utf_string_conversions.h"
+#include "base/win/scoped_co_mem.h"
+#include "base/win/scoped_comptr.h"
+
+using media::AudioDeviceNames;
+using base::win::ScopedComPtr;
+using base::win::ScopedCoMem;
+
+bool GetInputDeviceNamesWin(AudioDeviceNames* device_names) {
+ // It is assumed that this method is called from a COM thread, i.e.,
+ // CoInitializeEx() is not called here again to avoid STA/MTA conflicts.
+ ScopedComPtr<IMMDeviceEnumerator> enumerator;
+ HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
+ NULL,
+ CLSCTX_INPROC_SERVER,
+ __uuidof(IMMDeviceEnumerator),
+ enumerator.ReceiveVoid());
+ if (FAILED(hr)) {
+ LOG(WARNING) << "Failed to create IMMDeviceEnumerator: " << std::hex << hr;
+ return false;
+ }
+
+ // Generate a collection of active audio capture endpoint devices.
+ // This method will succeed even if all devices are disabled.
+ ScopedComPtr<IMMDeviceCollection> collection;
+ hr = enumerator->EnumAudioEndpoints(eCapture,
+ DEVICE_STATE_ACTIVE,
+ collection.Receive());
+ if (FAILED(hr))
+ return false;
+
+ // Retrieve the number of active capture devices.
+ UINT number_of_active_devices = 0;
+ collection->GetCount(&number_of_active_devices);
+ if (number_of_active_devices == 0)
+ return true;
+
+ media::AudioDeviceName device;
+
+ // Loop over all active capture devices and add friendly name and
+ // unique ID to the |device_names| list.
+ for (UINT i = 0; i < number_of_active_devices; ++i) {
+ // Retrieve unique name of endpoint device.
+ // Example: "{0.0.1.00000000}.{8db6020f-18e3-4f25-b6f5-7726c9122574}".
+ ScopedComPtr<IMMDevice> audio_device;
+ ScopedCoMem<WCHAR> endpoint_device_id;
+ hr = collection->Item(i, audio_device.Receive());
+ if (FAILED(hr))
+ continue;
+ audio_device->GetId(&endpoint_device_id);
+
+ // Store the unique name.
+ device.unique_id = WideToUTF8(static_cast<WCHAR*>(endpoint_device_id));
+
+ // Retrieve user-friendly name of endpoint device.
+ // Example: "Microphone (Realtek High Definition Audio)".
+ ScopedComPtr<IPropertyStore> properties;
+ hr = audio_device->OpenPropertyStore(STGM_READ, properties.Receive());
+ if (SUCCEEDED(hr)) {
+ PROPVARIANT friendly_name;
+ PropVariantInit(&friendly_name);
+ hr = properties->GetValue(PKEY_Device_FriendlyName, &friendly_name);
+
+ // Store the user-friendly name.
+ if (SUCCEEDED(hr) &&
+ friendly_name.vt == VT_LPWSTR && friendly_name.pwszVal) {
+ device.device_name = WideToUTF8(friendly_name.pwszVal);
+ }
+ PropVariantClear(&friendly_name);
+ }
+
+ // Add combination of user-friendly and unique name to the output list.
+ device_names->push_back(device);
+ }
+
+ return true;
+}
+
+bool GetInputDeviceNamesWinXP(AudioDeviceNames* device_names) {
+ // Retrieve the number of active waveform input devices.
+ UINT number_of_active_devices = waveInGetNumDevs();
+ if (number_of_active_devices == 0)
+ return true;
+
+ media::AudioDeviceName device;
+ WAVEINCAPS capabilities;
+ MMRESULT err = MMSYSERR_NOERROR;
+
+ // Loop over all active capture devices and add friendly name and
+ // unique ID to the |device_names| list. Note that, for Wave on XP,
+ // the "unique" name will simply be a copy of the friendly name since
+ // there is no safe method to retrieve a unique device name on XP.
+ for (UINT i = 0; i < number_of_active_devices; ++i) {
+ // Retrieve the capabilities of the specified waveform-audio input device.
+ err = waveInGetDevCaps(i, &capabilities, sizeof(capabilities));
+ if (err != MMSYSERR_NOERROR)
+ continue;
+
+ if (capabilities.szPname != NULL) {
+ // Store the user-friendly name. Max length is MAXPNAMELEN(=32)
+ // characters and the name cane be truncated on XP.
+ // Example: "Microphone (Realtek High Defini".
+ device.device_name = WideToUTF8(capabilities.szPname);
+
+ // Store the "unique" name (we use same as friendly name on Windows XP).
+ device.unique_id = WideToUTF8(capabilities.szPname);
+
+ // Add combination of user-friendly and unique name to the output list.
+ device_names->push_back(device);
+ }
+ }
+
+ return true;
+}
diff --git a/media/audio/win/device_enumeration_win.h b/media/audio/win/device_enumeration_win.h
new file mode 100644
index 0000000..a797eec
--- /dev/null
+++ b/media/audio/win/device_enumeration_win.h
@@ -0,0 +1,26 @@
+// Copyright (c) 2011 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 MEDIA_AUDIO_WIN_DEVICE_ENUMERATION_WIN_H_
+#define MEDIA_AUDIO_WIN_DEVICE_ENUMERATION_WIN_H_
+
+#include "media/audio/audio_device_name.h"
+
+// Returns a list of audio input device structures (name and unique device ID)
+// using the MMDevice API which is supported on Windows Vista and higher.
+// Example record in the output list:
+// - device_name: "Microphone (Realtek High Definition Audio)".
+// - unique_id: "{0.0.1.00000000}.{8db6020f-18e3-4f25-b6f5-7726c9122574}"
+// This method must be called from a COM thread using MTA.
+bool GetInputDeviceNamesWin(media::AudioDeviceNames* device_names);
+
+// Returns a list of audio input device structures (name and unique device ID)
+// using the WaveIn API which is supported on Windows XP and higher.
+// Example record in the output list:
+// - device_name: "Microphone (Realtek High Defini".
+// - unique_id: "Microphone (Realtek High Defini" (same as friendly name).
+bool GetInputDeviceNamesWinXP(media::AudioDeviceNames* device_names);
+
+#endif // MEDIA_AUDIO_WIN_DEVICE_ENUMERATION_WIN_H_
+
diff --git a/media/media.gyp b/media/media.gyp
index b47be2c..44a7bcc 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -87,6 +87,8 @@
'audio/win/audio_manager_win.h',
'audio/win/avrt_wrapper_win.cc',
'audio/win/avrt_wrapper_win.h',
+ 'audio/win/device_enumeration_win.cc',
+ 'audio/win/device_enumeration_win.h',
'audio/win/wavein_input_win.cc',
'audio/win/wavein_input_win.h',
'audio/win/waveout_output_win.cc',