Commit d344256e authored by joi@chromium.org's avatar joi@chromium.org

Output device enumeration for Alsa, plus unit test.

TBR=vrk@chromium.org
BUG=276894

Review URL: https://chromiumcodereview.appspot.com/23480030

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@221704 0039d316-1c4b-4281-b951-d872f2087c98
parent 42654b64
...@@ -317,7 +317,7 @@ void AudioManagerBase::GetAudioInputDeviceNames( ...@@ -317,7 +317,7 @@ void AudioManagerBase::GetAudioInputDeviceNames(
void AudioManagerBase::GetAudioOutputDeviceNames( void AudioManagerBase::GetAudioOutputDeviceNames(
AudioDeviceNames* device_names) { AudioDeviceNames* device_names) {
// TODO(joi): Remove this and keep pure virtual once implemented everywhere. // TODO(joi): Remove this and keep pure virtual once implemented everywhere.
NOTIMPLEMENTED(); NOTREACHED() << "Don't use this yet, it's not ready on all platforms!";
} }
void AudioManagerBase::ReleaseOutputStream(AudioOutputStream* stream) { void AudioManagerBase::ReleaseOutputStream(AudioOutputStream* stream) {
......
...@@ -7,21 +7,19 @@ ...@@ -7,21 +7,19 @@
#include "media/audio/audio_manager.h" #include "media/audio/audio_manager.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#if defined(OS_LINUX)
#include "media/audio/linux/audio_manager_linux.h"
#endif // defined(OS_LINUX)
#if defined(USE_PULSEAUDIO) #if defined(USE_PULSEAUDIO)
#include "media/audio/pulse/audio_manager_pulse.h" #include "media/audio/pulse/audio_manager_pulse.h"
#endif #endif // defined(USE_PULSEAUDIO)
namespace media { namespace media {
// TODO(joi): Remove guards once implemented for all platforms. void GetAudioOutputDeviceNamesImpl(AudioManager* audio_manager) {
TEST(AudioManagerTest, GetAudioOutputDeviceNames) {
#if defined(USE_PULSEAUDIO)
scoped_ptr<AudioManager> audio_manager_pulse(AudioManagerPulse::Create());
if (!audio_manager_pulse)
return;
AudioDeviceNames device_names; AudioDeviceNames device_names;
audio_manager_pulse->GetAudioOutputDeviceNames(&device_names); audio_manager->GetAudioOutputDeviceNames(&device_names);
VLOG(2) << "Got " << device_names.size() << " audio output devices."; VLOG(2) << "Got " << device_names.size() << " audio output devices.";
for (AudioDeviceNames::iterator it = device_names.begin(); for (AudioDeviceNames::iterator it = device_names.begin();
...@@ -31,7 +29,26 @@ TEST(AudioManagerTest, GetAudioOutputDeviceNames) { ...@@ -31,7 +29,26 @@ TEST(AudioManagerTest, GetAudioOutputDeviceNames) {
EXPECT_FALSE(it->device_name.empty()); EXPECT_FALSE(it->device_name.empty());
VLOG(2) << "Device ID(" << it->unique_id << "), label: " << it->device_name; VLOG(2) << "Device ID(" << it->unique_id << "), label: " << it->device_name;
} }
}
TEST(AudioManagerTest, GetAudioOutputDeviceNames) {
#if defined(USE_PULSEAUDIO)
{
VLOG(2) << "Testing AudioManagerPulse.";
scoped_ptr<AudioManager> pulse_audio_manager(AudioManagerPulse::Create());
if (pulse_audio_manager.get())
GetAudioOutputDeviceNamesImpl(pulse_audio_manager.get());
else
LOG(WARNING) << "No pulseaudio on this system.";
}
#endif // defined(USE_PULSEAUDIO) #endif // defined(USE_PULSEAUDIO)
#if defined(USE_ALSA)
{
VLOG(2) << "Testing AudioManagerLinux.";
scoped_ptr<AudioManager> alsa_audio_manager(new AudioManagerLinux());
GetAudioOutputDeviceNamesImpl(alsa_audio_manager.get());
}
#endif // defined(USE_ALSA)
} }
} // namespace media } // namespace media
...@@ -42,9 +42,9 @@ static const int kDefaultSampleRate = 48000; ...@@ -42,9 +42,9 @@ static const int kDefaultSampleRate = 48000;
// hence surround devices are not stored in the list. // hence surround devices are not stored in the list.
static const char* kInvalidAudioInputDevices[] = { static const char* kInvalidAudioInputDevices[] = {
"default", "default",
"dmix",
"null", "null",
"pulse", "pulse",
"dmix",
"surround", "surround",
}; };
...@@ -105,7 +105,13 @@ void AudioManagerLinux::ShowAudioInputSettings() { ...@@ -105,7 +105,13 @@ void AudioManagerLinux::ShowAudioInputSettings() {
void AudioManagerLinux::GetAudioInputDeviceNames( void AudioManagerLinux::GetAudioInputDeviceNames(
media::AudioDeviceNames* device_names) { media::AudioDeviceNames* device_names) {
DCHECK(device_names->empty()); DCHECK(device_names->empty());
GetAlsaAudioInputDevices(device_names); GetAlsaAudioDevices(kStreamCapture, device_names);
}
void AudioManagerLinux::GetAudioOutputDeviceNames(
media::AudioDeviceNames* device_names) {
DCHECK(device_names->empty());
GetAlsaAudioDevices(kStreamPlayback, device_names);
} }
AudioParameters AudioManagerLinux::GetInputStreamParameters( AudioParameters AudioManagerLinux::GetInputStreamParameters(
...@@ -117,7 +123,8 @@ AudioParameters AudioManagerLinux::GetInputStreamParameters( ...@@ -117,7 +123,8 @@ AudioParameters AudioManagerLinux::GetInputStreamParameters(
kDefaultSampleRate, 16, kDefaultInputBufferSize); kDefaultSampleRate, 16, kDefaultInputBufferSize);
} }
void AudioManagerLinux::GetAlsaAudioInputDevices( void AudioManagerLinux::GetAlsaAudioDevices(
StreamType type,
media::AudioDeviceNames* device_names) { media::AudioDeviceNames* device_names) {
// Constants specified by the ALSA API for device hints. // Constants specified by the ALSA API for device hints.
static const char kPcmInterfaceName[] = "pcm"; static const char kPcmInterfaceName[] = "pcm";
...@@ -128,37 +135,40 @@ void AudioManagerLinux::GetAlsaAudioInputDevices( ...@@ -128,37 +135,40 @@ void AudioManagerLinux::GetAlsaAudioInputDevices(
void** hints = NULL; void** hints = NULL;
int error = wrapper_->DeviceNameHint(card, kPcmInterfaceName, &hints); int error = wrapper_->DeviceNameHint(card, kPcmInterfaceName, &hints);
if (!error) { if (!error) {
GetAlsaDevicesInfo(hints, device_names); GetAlsaDevicesInfo(type, hints, device_names);
// Destroy the hints now that we're done with it. // Destroy the hints now that we're done with it.
wrapper_->DeviceNameFreeHint(hints); wrapper_->DeviceNameFreeHint(hints);
} else { } else {
DLOG(WARNING) << "GetAudioInputDevices: unable to get device hints: " DLOG(WARNING) << "GetAlsaAudioDevices: unable to get device hints: "
<< wrapper_->StrError(error); << wrapper_->StrError(error);
} }
} }
} }
void AudioManagerLinux::GetAlsaDevicesInfo( void AudioManagerLinux::GetAlsaDevicesInfo(
void** hints, media::AudioDeviceNames* device_names) { AudioManagerLinux::StreamType type,
void** hints,
media::AudioDeviceNames* device_names) {
static const char kIoHintName[] = "IOID"; static const char kIoHintName[] = "IOID";
static const char kNameHintName[] = "NAME"; static const char kNameHintName[] = "NAME";
static const char kDescriptionHintName[] = "DESC"; static const char kDescriptionHintName[] = "DESC";
static const char kOutputDevice[] = "Output";
const char* unwanted_device_type = UnwantedDeviceTypeWhenEnumerating(type);
for (void** hint_iter = hints; *hint_iter != NULL; hint_iter++) { for (void** hint_iter = hints; *hint_iter != NULL; hint_iter++) {
// Only examine devices that are input capable. Valid values are // Only examine devices of the right type. Valid values are
// "Input", "Output", and NULL which means both input and output. // "Input", "Output", and NULL which means both input and output.
scoped_ptr_malloc<char> io(wrapper_->DeviceNameGetHint(*hint_iter, scoped_ptr_malloc<char> io(wrapper_->DeviceNameGetHint(*hint_iter,
kIoHintName)); kIoHintName));
if (io != NULL && strcmp(kOutputDevice, io.get()) == 0) if (io != NULL && strcmp(unwanted_device_type, io.get()) == 0)
continue; continue;
// Found an input device, prepend the default device since we always want // Found a device, prepend the default device since we always want
// it to be on the top of the list for all platforms. And there is no // it to be on the top of the list for all platforms. And there is
// duplicate counting here since it is only done if the list is still empty. // no duplicate counting here since it is only done if the list is
// Note, pulse has exclusively opened the default device, so we must open // still empty. Note, pulse has exclusively opened the default
// the device via the "default" moniker. // device, so we must open the device via the "default" moniker.
if (device_names->empty()) { if (device_names->empty()) {
device_names->push_front(media::AudioDeviceName( device_names->push_front(media::AudioDeviceName(
AudioManagerBase::kDefaultDeviceName, AudioManagerBase::kDefaultDeviceName,
...@@ -170,7 +180,7 @@ void AudioManagerLinux::GetAlsaDevicesInfo( ...@@ -170,7 +180,7 @@ void AudioManagerLinux::GetAlsaDevicesInfo(
wrapper_->DeviceNameGetHint(*hint_iter, kNameHintName)); wrapper_->DeviceNameGetHint(*hint_iter, kNameHintName));
// Find out if the device is available. // Find out if the device is available.
if (IsAlsaDeviceAvailable(unique_device_name.get())) { if (IsAlsaDeviceAvailable(type, unique_device_name.get())) {
// Get the description for the device. // Get the description for the device.
scoped_ptr_malloc<char> desc(wrapper_->DeviceNameGetHint( scoped_ptr_malloc<char> desc(wrapper_->DeviceNameGetHint(
*hint_iter, kDescriptionHintName)); *hint_iter, kDescriptionHintName));
...@@ -196,25 +206,46 @@ void AudioManagerLinux::GetAlsaDevicesInfo( ...@@ -196,25 +206,46 @@ void AudioManagerLinux::GetAlsaDevicesInfo(
} }
} }
bool AudioManagerLinux::IsAlsaDeviceAvailable(const char* device_name) { // static
bool AudioManagerLinux::IsAlsaDeviceAvailable(
AudioManagerLinux::StreamType type,
const char* device_name) {
if (!device_name) if (!device_name)
return false; return false;
// Check if the device is in the list of invalid devices. // We do prefix matches on the device name to see whether to include
for (size_t i = 0; i < arraysize(kInvalidAudioInputDevices); ++i) { // it or not.
if (strncmp(kInvalidAudioInputDevices[i], device_name, if (type == kStreamCapture) {
strlen(kInvalidAudioInputDevices[i])) == 0) // Check if the device is in the list of invalid devices.
return false; for (size_t i = 0; i < arraysize(kInvalidAudioInputDevices); ++i) {
if (strncmp(kInvalidAudioInputDevices[i], device_name,
strlen(kInvalidAudioInputDevices[i])) == 0)
return false;
}
return true;
} else {
DCHECK_EQ(kStreamPlayback, type);
// We prefer the device type that maps straight to hardware but
// goes through software conversion if needed (e.g. incompatible
// sample rate).
// TODO(joi): Should we prefer "hw" instead?
static const char kDeviceTypeDesired[] = "plughw";
return strncmp(kDeviceTypeDesired,
device_name,
arraysize(kDeviceTypeDesired) - 1) == 0;
} }
}
return true; // static
const char* AudioManagerLinux::UnwantedDeviceTypeWhenEnumerating(
AudioManagerLinux::StreamType wanted_type) {
return wanted_type == kStreamPlayback ? "Input" : "Output";
} }
bool AudioManagerLinux::HasAnyAlsaAudioDevice(StreamType stream) { bool AudioManagerLinux::HasAnyAlsaAudioDevice(
AudioManagerLinux::StreamType stream) {
static const char kPcmInterfaceName[] = "pcm"; static const char kPcmInterfaceName[] = "pcm";
static const char kIoHintName[] = "IOID"; static const char kIoHintName[] = "IOID";
const char* kNotWantedDevice =
(stream == kStreamPlayback ? "Input" : "Output");
void** hints = NULL; void** hints = NULL;
bool has_device = false; bool has_device = false;
int card = -1; int card = -1;
...@@ -230,7 +261,8 @@ bool AudioManagerLinux::HasAnyAlsaAudioDevice(StreamType stream) { ...@@ -230,7 +261,8 @@ bool AudioManagerLinux::HasAnyAlsaAudioDevice(StreamType stream) {
// "Input", "Output", and NULL which means both input and output. // "Input", "Output", and NULL which means both input and output.
scoped_ptr_malloc<char> io(wrapper_->DeviceNameGetHint(*hint_iter, scoped_ptr_malloc<char> io(wrapper_->DeviceNameGetHint(*hint_iter,
kIoHintName)); kIoHintName));
if (io != NULL && strcmp(kNotWantedDevice, io.get()) == 0) const char* unwanted_type = UnwantedDeviceTypeWhenEnumerating(stream);
if (io != NULL && strcmp(unwanted_type, io.get()) == 0)
continue; // Wrong type, skip the device. continue; // Wrong type, skip the device.
// Found an input device. // Found an input device.
......
...@@ -27,6 +27,8 @@ class MEDIA_EXPORT AudioManagerLinux : public AudioManagerBase { ...@@ -27,6 +27,8 @@ class MEDIA_EXPORT AudioManagerLinux : public AudioManagerBase {
virtual void ShowAudioInputSettings() OVERRIDE; virtual void ShowAudioInputSettings() OVERRIDE;
virtual void GetAudioInputDeviceNames(media::AudioDeviceNames* device_names) virtual void GetAudioInputDeviceNames(media::AudioDeviceNames* device_names)
OVERRIDE; OVERRIDE;
virtual void GetAudioOutputDeviceNames(media::AudioDeviceNames* device_names)
OVERRIDE;
virtual AudioParameters GetInputStreamParameters( virtual AudioParameters GetInputStreamParameters(
const std::string& device_id) OVERRIDE; const std::string& device_id) OVERRIDE;
...@@ -55,14 +57,22 @@ class MEDIA_EXPORT AudioManagerLinux : public AudioManagerBase { ...@@ -55,14 +57,22 @@ class MEDIA_EXPORT AudioManagerLinux : public AudioManagerBase {
kStreamCapture, kStreamCapture,
}; };
// Gets a list of available ALSA input devices. // Gets a list of available ALSA devices.
void GetAlsaAudioInputDevices(media::AudioDeviceNames* device_names); void GetAlsaAudioDevices(StreamType type,
media::AudioDeviceNames* device_names);
// Gets the ALSA devices' names and ids. // Gets the ALSA devices' names and ids that support streams of the
void GetAlsaDevicesInfo(void** hint, media::AudioDeviceNames* device_names); // given type.
void GetAlsaDevicesInfo(StreamType type,
void** hint,
media::AudioDeviceNames* device_names);
// Checks if the specific ALSA device is available. // Checks if the specific ALSA device is available.
bool IsAlsaDeviceAvailable(const char* device_name); static bool IsAlsaDeviceAvailable(StreamType type,
const char* device_name);
static const char* UnwantedDeviceTypeWhenEnumerating(
StreamType wanted_type);
// Returns true if a device is present for the given stream type. // Returns true if a device is present for the given stream type.
bool HasAnyAlsaAudioDevice(StreamType stream); bool HasAnyAlsaAudioDevice(StreamType stream);
......
...@@ -1042,6 +1042,11 @@ ...@@ -1042,6 +1042,11 @@
'base/media_file_checker_unittest.cc', 'base/media_file_checker_unittest.cc',
], ],
}], }],
['use_alsa==1', {
'defines': [
'USE_ALSA',
],
}],
['use_pulseaudio==1', { ['use_pulseaudio==1', {
'defines': [ 'defines': [
'USE_PULSEAUDIO', 'USE_PULSEAUDIO',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment