diff options
author | Torne (Richard Coles) <torne@google.com> | 2013-09-12 12:10:22 +0100 |
---|---|---|
committer | Torne (Richard Coles) <torne@google.com> | 2013-09-12 12:10:22 +0100 |
commit | 58537e28ecd584eab876aee8be7156509866d23a (patch) | |
tree | 8988984e52090aaadf33cff139d7dd212cd13656 /media/audio | |
parent | 0a1b11dee8e5cb2520121c300858fea6138e3c54 (diff) | |
download | chromium_org-58537e28ecd584eab876aee8be7156509866d23a.tar.gz |
Merge from Chromium at DEPS revision 222756
This commit was generated by merge_to_master.py.
Change-Id: I40d7f32f195f328f005f230ea80d07092d48040e
Diffstat (limited to 'media/audio')
55 files changed, 1033 insertions, 600 deletions
diff --git a/media/audio/android/audio_manager_android.cc b/media/audio/android/audio_manager_android.cc index 164344aba0..e6eed7fea4 100644 --- a/media/audio/android/audio_manager_android.cc +++ b/media/audio/android/audio_manager_android.cc @@ -74,9 +74,12 @@ AudioParameters AudioManagerAndroid::GetInputStreamParameters( } AudioOutputStream* AudioManagerAndroid::MakeAudioOutputStream( - const AudioParameters& params, const std::string& input_device_id) { + const AudioParameters& params, + const std::string& device_id, + const std::string& input_device_id) { AudioOutputStream* stream = - AudioManagerBase::MakeAudioOutputStream(params, std::string()); + AudioManagerBase::MakeAudioOutputStream(params, std::string(), + std::string()); if (stream && output_stream_count() == 1) { SetAudioMode(kAudioModeInCommunication); RegisterHeadsetReceiver(); @@ -104,13 +107,16 @@ void AudioManagerAndroid::ReleaseInputStream(AudioInputStream* stream) { } AudioOutputStream* AudioManagerAndroid::MakeLinearOutputStream( - const AudioParameters& params) { + const AudioParameters& params) { DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format()); return new OpenSLESOutputStream(this, params); } AudioOutputStream* AudioManagerAndroid::MakeLowLatencyOutputStream( - const AudioParameters& params, const std::string& input_device_id) { + const AudioParameters& params, + const std::string& device_id, + const std::string& input_device_id) { + DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!"; DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format()); return new OpenSLESOutputStream(this, params); } @@ -140,7 +146,10 @@ int AudioManagerAndroid::GetOptimalOutputFrameSize(int sample_rate, } AudioParameters AudioManagerAndroid::GetPreferredOutputStreamParameters( + const std::string& output_device_id, const AudioParameters& input_params) { + // TODO(tommi): Support |output_device_id|. + DLOG_IF(ERROR, !output_device_id.empty()) << "Not implemented!"; ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO; int sample_rate = GetNativeOutputSampleRate(); int buffer_size = GetOptimalOutputFrameSize(sample_rate, 2); diff --git a/media/audio/android/audio_manager_android.h b/media/audio/android/audio_manager_android.h index fa1c3736a3..ba5bed61e3 100644 --- a/media/audio/android/audio_manager_android.h +++ b/media/audio/android/audio_manager_android.h @@ -25,6 +25,7 @@ class MEDIA_EXPORT AudioManagerAndroid : public AudioManagerBase { virtual AudioOutputStream* MakeAudioOutputStream( const AudioParameters& params, + const std::string& device_id, const std::string& input_device_id) OVERRIDE; virtual AudioInputStream* MakeAudioInputStream( const AudioParameters& params, const std::string& device_id) OVERRIDE; @@ -36,6 +37,7 @@ class MEDIA_EXPORT AudioManagerAndroid : public AudioManagerBase { const AudioParameters& params) OVERRIDE; virtual AudioOutputStream* MakeLowLatencyOutputStream( const AudioParameters& params, + const std::string& device_id, const std::string& input_device_id) OVERRIDE; virtual AudioInputStream* MakeLinearInputStream( const AudioParameters& params, const std::string& device_id) OVERRIDE; @@ -48,6 +50,7 @@ class MEDIA_EXPORT AudioManagerAndroid : public AudioManagerBase { virtual ~AudioManagerAndroid(); virtual AudioParameters GetPreferredOutputStreamParameters( + const std::string& output_device_id, const AudioParameters& input_params) OVERRIDE; private: diff --git a/media/audio/audio_input_controller.cc b/media/audio/audio_input_controller.cc index d701337b51..ef94d1274d 100644 --- a/media/audio/audio_input_controller.cc +++ b/media/audio/audio_input_controller.cc @@ -19,16 +19,10 @@ const int kMaxInputChannels = 2; // breakage (very hard to repro bugs!) on other platforms: See // http://crbug.com/226327 and http://crbug.com/230972. const int kTimerResetIntervalSeconds = 1; -#if defined(OS_IOS) -// The first callback on iOS is received after the current background -// audio has faded away. -const int kTimerInitialIntervalSeconds = 4; -#else // We have received reports that the timer can be too trigger happy on some // Mac devices and the initial timer interval has therefore been increased // from 1 second to 5 seconds. const int kTimerInitialIntervalSeconds = 5; -#endif // defined(OS_IOS) } namespace media { @@ -179,7 +173,7 @@ void AudioInputController::DoCreate(AudioManager* audio_manager, SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioInputController.CreateTime"); // TODO(miu): See TODO at top of file. Until that's resolved, assume all // platform audio input requires the |no_data_timer_| be used to auto-detect - // errors. In reality, probably only Windows and IOS need to be treated as + // errors. In reality, probably only Windows needs to be treated as // unreliable here. DoCreateForStream(audio_manager->MakeAudioInputStream(params, device_id), true); diff --git a/media/audio/audio_input_device.cc b/media/audio/audio_input_device.cc index 5477be6e63..d7685840ec 100644 --- a/media/audio/audio_input_device.cc +++ b/media/audio/audio_input_device.cc @@ -291,7 +291,9 @@ void AudioInputDevice::AudioThreadCallback::Process(int pending_data) { uint8* ptr = static_cast<uint8*>(shared_memory_.memory()); ptr += current_segment_id_ * segment_length_; AudioInputBuffer* buffer = reinterpret_cast<AudioInputBuffer*>(ptr); - DCHECK_EQ(buffer->params.size, + // Usually this will be equal but in the case of low sample rate (e.g. 8kHz, + // the buffer may be bigger (on mac at least)). + DCHECK_GE(buffer->params.size, segment_length_ - sizeof(AudioInputBufferParameters)); double volume = buffer->params.volume; bool key_pressed = buffer->params.key_pressed; diff --git a/media/audio/audio_input_device_unittest.cc b/media/audio/audio_input_device_unittest.cc index dc211a48a9..61a97832f6 100644 --- a/media/audio/audio_input_device_unittest.cc +++ b/media/audio/audio_input_device_unittest.cc @@ -164,7 +164,7 @@ TEST_F(AudioInputDeviceTest, WinXPDeviceIdUnchanged) { } } -TEST_F(AudioInputDeviceTest, ConvertToWinXPDeviceId) { +TEST_F(AudioInputDeviceTest, ConvertToWinXPInputDeviceId) { if (!CanRunAudioTest()) return; diff --git a/media/audio/audio_low_latency_input_output_unittest.cc b/media/audio/audio_low_latency_input_output_unittest.cc index 33729c45a0..a616761294 100644 --- a/media/audio/audio_low_latency_input_output_unittest.cc +++ b/media/audio/audio_low_latency_input_output_unittest.cc @@ -308,7 +308,8 @@ class AudioOutputStreamTraits { static StreamType* CreateStream(AudioManager* audio_manager, const AudioParameters& params) { - return audio_manager->MakeAudioOutputStream(params, std::string()); + return audio_manager->MakeAudioOutputStream(params, std::string(), + std::string()); } }; diff --git a/media/audio/audio_manager.h b/media/audio/audio_manager.h index cc5b95c819..891d2a2658 100644 --- a/media/audio/audio_manager.h +++ b/media/audio/audio_manager.h @@ -58,11 +58,16 @@ class MEDIA_EXPORT AudioManager { // threads to avoid blocking the rest of the application. virtual void ShowAudioInputSettings() = 0; - // Appends a list of available input devices. It is not guaranteed that - // all the devices in the list support all formats and sample rates for + // Appends a list of available input devices to |device_names|, + // which must initially be empty. It is not guaranteed that all the + // devices in the list support all formats and sample rates for // recording. virtual void GetAudioInputDeviceNames(AudioDeviceNames* device_names) = 0; + // Appends a list of available output devices to |device_names|, + // which must initially be empty. + virtual void GetAudioOutputDeviceNames(AudioDeviceNames* device_names) = 0; + // Factory for all the supported stream formats. |params| defines parameters // of the audio stream to be created. // @@ -71,6 +76,14 @@ class MEDIA_EXPORT AudioManager { // or three buffers are created, one will be locked for playback and one will // be ready to be filled in the call to AudioSourceCallback::OnMoreData(). // + // To create a stream for the default output device, pass an empty string + // for |device_id|, otherwise the specified audio device will be opened. + // + // The |input_device_id| is used for low-latency unified streams + // (input+output) only and then only if the audio parameters specify a >0 + // input channel count. In other cases this id is ignored and should be + // empty. + // // Returns NULL if the combination of the parameters is not supported, or if // we have reached some other platform specific limit. // @@ -82,14 +95,18 @@ class MEDIA_EXPORT AudioManager { // // Do not free the returned AudioOutputStream. It is owned by AudioManager. virtual AudioOutputStream* MakeAudioOutputStream( - const AudioParameters& params, const std::string& input_device_id) = 0; + const AudioParameters& params, + const std::string& device_id, + const std::string& input_device_id) = 0; // Creates new audio output proxy. A proxy implements // AudioOutputStream interface, but unlike regular output stream // created with MakeAudioOutputStream() it opens device only when a // sound is actually playing. virtual AudioOutputStream* MakeAudioOutputStreamProxy( - const AudioParameters& params, const std::string& input_device_id) = 0; + const AudioParameters& params, + const std::string& device_id, + const std::string& input_device_id) = 0; // Factory to create audio recording streams. // |channels| can be 1 or 2. @@ -130,14 +147,28 @@ class MEDIA_EXPORT AudioManager { // streams. It is a convenience interface to // AudioManagerBase::GetPreferredOutputStreamParameters and each AudioManager // does not need their own implementation to this interface. + // TODO(tommi): Remove this method and use GetOutputStreamParameteres instead. virtual AudioParameters GetDefaultOutputStreamParameters() = 0; + // Returns the output hardware audio parameters for a specific output device. + virtual AudioParameters GetOutputStreamParameters( + const std::string& device_id) = 0; + // Returns the input hardware audio parameters of the specific device // for opening input streams. Each AudioManager needs to implement their own // version of this interface. virtual AudioParameters GetInputStreamParameters( const std::string& device_id) = 0; + // Returns the device id of an output device that belongs to the same hardware + // as the specified input device. + // If the hardware has only an input device (e.g. a webcam), the return value + // will be empty (which the caller can then interpret to be the default output + // device). Implementations that don't yet support this feature, must return + // an empty string. + virtual std::string GetAssociatedOutputDeviceID( + const std::string& input_device_id) = 0; + protected: AudioManager(); diff --git a/media/audio/audio_manager_base.cc b/media/audio/audio_manager_base.cc index db77f004e3..1477ce3c5a 100644 --- a/media/audio/audio_manager_base.cc +++ b/media/audio/audio_manager_base.cc @@ -38,15 +38,18 @@ const char AudioManagerBase::kDefaultDeviceId[] = "default"; struct AudioManagerBase::DispatcherParams { DispatcherParams(const AudioParameters& input, const AudioParameters& output, - const std::string& device_id) + const std::string& output_device_id, + const std::string& input_device_id) : input_params(input), output_params(output), - input_device_id(device_id) {} + input_device_id(input_device_id), + output_device_id(output_device_id) {} ~DispatcherParams() {} const AudioParameters input_params; const AudioParameters output_params; const std::string input_device_id; + const std::string output_device_id; scoped_refptr<AudioOutputDispatcher> dispatcher; private: @@ -65,6 +68,7 @@ class AudioManagerBase::CompareByParams { // of the existing dispatcher are the same as the request dispatcher. return (dispatcher_->input_params == dispatcher_in->input_params && dispatcher_->output_params == dispatcher_in->output_params && + dispatcher_->output_device_id == dispatcher_in->output_device_id && (!dispatcher_->input_params.input_channels() || dispatcher_->input_device_id == dispatcher_in->input_device_id)); } @@ -134,6 +138,7 @@ scoped_refptr<base::MessageLoopProxy> AudioManagerBase::GetWorkerLoop() { AudioOutputStream* AudioManagerBase::MakeAudioOutputStream( const AudioParameters& params, + const std::string& device_id, const std::string& input_device_id) { // TODO(miu): Fix ~50 call points across several unit test modules to call // this method on the audio thread, then uncomment the following: @@ -159,10 +164,12 @@ AudioOutputStream* AudioManagerBase::MakeAudioOutputStream( AudioOutputStream* stream; switch (params.format()) { case AudioParameters::AUDIO_PCM_LINEAR: + DCHECK(device_id.empty()) + << "AUDIO_PCM_LINEAR supports only the default device."; stream = MakeLinearOutputStream(params); break; case AudioParameters::AUDIO_PCM_LOW_LATENCY: - stream = MakeLowLatencyOutputStream(params, input_device_id); + stream = MakeLowLatencyOutputStream(params, device_id, input_device_id); break; case AudioParameters::AUDIO_FAKE: stream = FakeAudioOutputStream::MakeFakeStream(this, params); @@ -180,7 +187,8 @@ AudioOutputStream* AudioManagerBase::MakeAudioOutputStream( } AudioInputStream* AudioManagerBase::MakeAudioInputStream( - const AudioParameters& params, const std::string& device_id) { + const AudioParameters& params, + const std::string& device_id) { // TODO(miu): Fix ~20 call points across several unit test modules to call // this method on the audio thread, then uncomment the following: // DCHECK(message_loop_->BelongsToCurrentThread()); @@ -222,19 +230,26 @@ AudioInputStream* AudioManagerBase::MakeAudioInputStream( } AudioOutputStream* AudioManagerBase::MakeAudioOutputStreamProxy( - const AudioParameters& params, const std::string& input_device_id) { -#if defined(OS_IOS) - // IOS implements audio input only. - NOTIMPLEMENTED(); - return NULL; -#else + const AudioParameters& params, + const std::string& device_id, + const std::string& input_device_id) { DCHECK(message_loop_->BelongsToCurrentThread()); + // If the caller supplied an empty device id to select the default device, + // we fetch the actual device id of the default device so that the lookup + // will find the correct device regardless of whether it was opened as + // "default" or via the specific id. + // NOTE: Implementations that don't yet support opening non-default output + // devices may return an empty string from GetDefaultOutputDeviceID(). + std::string output_device_id = device_id.empty() ? + GetDefaultOutputDeviceID() : device_id; + // If we're not using AudioOutputResampler our output parameters are the same // as our input parameters. AudioParameters output_params = params; if (params.format() == AudioParameters::AUDIO_PCM_LOW_LATENCY) { - output_params = GetPreferredOutputStreamParameters(params); + output_params = + GetPreferredOutputStreamParameters(output_device_id, params); // Ensure we only pass on valid output parameters. if (!output_params.IsValid()) { @@ -257,7 +272,8 @@ AudioOutputStream* AudioManagerBase::MakeAudioOutputStreamProxy( } DispatcherParams* dispatcher_params = - new DispatcherParams(params, output_params, input_device_id); + new DispatcherParams(params, output_params, output_device_id, + input_device_id); AudioOutputDispatchers::iterator it = std::find_if(output_dispatchers_.begin(), output_dispatchers_.end(), @@ -272,23 +288,30 @@ AudioOutputStream* AudioManagerBase::MakeAudioOutputStreamProxy( scoped_refptr<AudioOutputDispatcher> dispatcher; if (output_params.format() != AudioParameters::AUDIO_FAKE) { dispatcher = new AudioOutputResampler(this, params, output_params, - input_device_id, kCloseDelay); + output_device_id, input_device_id, + kCloseDelay); } else { dispatcher = new AudioOutputDispatcherImpl(this, output_params, + output_device_id, input_device_id, kCloseDelay); } dispatcher_params->dispatcher = dispatcher; output_dispatchers_.push_back(dispatcher_params); return new AudioOutputProxy(dispatcher.get()); -#endif // defined(OS_IOS) } void AudioManagerBase::ShowAudioInputSettings() { } void AudioManagerBase::GetAudioInputDeviceNames( - media::AudioDeviceNames* device_names) { + AudioDeviceNames* device_names) { +} + +void AudioManagerBase::GetAudioOutputDeviceNames( + AudioDeviceNames* device_names) { + // TODO(joi): Remove this and keep pure virtual once implemented everywhere. + NOTREACHED() << "Don't use this yet, it's not ready on all platforms!"; } void AudioManagerBase::ReleaseOutputStream(AudioOutputStream* stream) { @@ -333,10 +356,6 @@ void AudioManagerBase::Shutdown() { } void AudioManagerBase::ShutdownOnAudioThread() { -// IOS implements audio input only. -#if defined(OS_IOS) - return; -#else // This should always be running on the audio thread, but since we've cleared // the audio_thread_ member pointer when we get here, we can't verify exactly // what thread we're running on. The method is not public though and only @@ -357,7 +376,6 @@ void AudioManagerBase::ShutdownOnAudioThread() { } output_dispatchers_.clear(); -#endif // defined(OS_IOS) } void AudioManagerBase::AddOutputDeviceChangeListener( @@ -379,7 +397,14 @@ void AudioManagerBase::NotifyAllOutputDeviceChangeListeners() { } AudioParameters AudioManagerBase::GetDefaultOutputStreamParameters() { - return GetPreferredOutputStreamParameters(AudioParameters()); + return GetPreferredOutputStreamParameters(GetDefaultOutputDeviceID(), + AudioParameters()); +} + +AudioParameters AudioManagerBase::GetOutputStreamParameters( + const std::string& device_id) { + return GetPreferredOutputStreamParameters(device_id, + AudioParameters()); } AudioParameters AudioManagerBase::GetInputStreamParameters( @@ -388,4 +413,15 @@ AudioParameters AudioManagerBase::GetInputStreamParameters( return AudioParameters(); } +std::string AudioManagerBase::GetAssociatedOutputDeviceID( + const std::string& input_device_id) { + NOTIMPLEMENTED(); + return ""; +} + +std::string AudioManagerBase::GetDefaultOutputDeviceID() { + NOTIMPLEMENTED(); + return ""; +} + } // namespace media diff --git a/media/audio/audio_manager_base.h b/media/audio/audio_manager_base.h index 8b34d9fcf9..15c1b24066 100644 --- a/media/audio/audio_manager_base.h +++ b/media/audio/audio_manager_base.h @@ -47,10 +47,14 @@ class MEDIA_EXPORT AudioManagerBase : public AudioManager { virtual void ShowAudioInputSettings() OVERRIDE; virtual void GetAudioInputDeviceNames( - media::AudioDeviceNames* device_names) OVERRIDE; + AudioDeviceNames* device_names) OVERRIDE; + + virtual void GetAudioOutputDeviceNames( + AudioDeviceNames* device_names) OVERRIDE; virtual AudioOutputStream* MakeAudioOutputStream( const AudioParameters& params, + const std::string& device_id, const std::string& input_device_id) OVERRIDE; virtual AudioInputStream* MakeAudioInputStream( @@ -58,6 +62,7 @@ class MEDIA_EXPORT AudioManagerBase : public AudioManager { virtual AudioOutputStream* MakeAudioOutputStreamProxy( const AudioParameters& params, + const std::string& device_id, const std::string& input_device_id) OVERRIDE; // Called internally by the audio stream when it has been closed. @@ -72,7 +77,9 @@ class MEDIA_EXPORT AudioManagerBase : public AudioManager { // Creates the output stream for the |AUDIO_PCM_LOW_LATENCY| format. // |input_device_id| is used by unified IO to open the correct input device. virtual AudioOutputStream* MakeLowLatencyOutputStream( - const AudioParameters& params, const std::string& input_device_id) = 0; + const AudioParameters& params, + const std::string& device_id, + const std::string& input_device_id) = 0; // Creates the input stream for the |AUDIO_PCM_LINEAR| format. The legacy // name is also from |AUDIO_PCM_LINEAR|. @@ -90,9 +97,15 @@ class MEDIA_EXPORT AudioManagerBase : public AudioManager { AudioDeviceListener* listener) OVERRIDE; virtual AudioParameters GetDefaultOutputStreamParameters() OVERRIDE; + virtual AudioParameters GetOutputStreamParameters( + const std::string& device_id) OVERRIDE; + virtual AudioParameters GetInputStreamParameters( const std::string& device_id) OVERRIDE; + virtual std::string GetAssociatedOutputDeviceID( + const std::string& input_device_id) OVERRIDE; + protected: AudioManagerBase(); @@ -115,9 +128,16 @@ class MEDIA_EXPORT AudioManagerBase : public AudioManager { // will decide if they should return the values from |input_params| or the // default hardware values. If the |input_params| is invalid, it will return // the default hardware audio parameters. + // If |output_device_id| is empty, the implementation must treat that as + // a request for the default output device. virtual AudioParameters GetPreferredOutputStreamParameters( + const std::string& output_device_id, const AudioParameters& input_params) = 0; + // Returns the ID of the default audio output device. + // Implementations that don't yet support this should return an empty string. + virtual std::string GetDefaultOutputDeviceID(); + // Get number of input or output streams. int input_stream_count() { return num_input_streams_; } int output_stream_count() { return num_output_streams_; } diff --git a/media/audio/audio_manager_unittest.cc b/media/audio/audio_manager_unittest.cc new file mode 100644 index 0000000000..96300c9a83 --- /dev/null +++ b/media/audio/audio_manager_unittest.cc @@ -0,0 +1,145 @@ +// Copyright 2013 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 "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "media/audio/audio_manager.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(OS_WIN) +#include "media/audio/win/audio_manager_win.h" +#endif // defined(OS_WIN) + +#if defined(USE_PULSEAUDIO) +#include "media/audio/pulse/audio_manager_pulse.h" +#endif // defined(USE_PULSEAUDIO) + +namespace media { + +void GetAudioOutputDeviceNamesImpl(AudioManager* audio_manager) { + AudioDeviceNames device_names; + audio_manager->GetAudioOutputDeviceNames(&device_names); + + VLOG(2) << "Got " << device_names.size() << " audio output devices."; + for (AudioDeviceNames::iterator it = device_names.begin(); + it != device_names.end(); + ++it) { + EXPECT_FALSE(it->unique_id.empty()); + EXPECT_FALSE(it->device_name.empty()); + VLOG(2) << "Device ID(" << it->unique_id << "), label: " << it->device_name; + } +} + +// So that tests herein can be friends of AudioManagerWin. +// +// TODO(joi): Make this go away by unifying audio_manager_unittest.cc +// and audio_input_device_unittest.cc +class AudioManagerTest : public ::testing::Test { + public: + bool SetupForSecondTest(AudioManager* amw) { +#if defined(OS_WIN) + AudioManagerWin* audio_manager_win = static_cast<AudioManagerWin*>(amw); + if (audio_manager_win->enumeration_type() == + AudioManagerWin::kWaveEnumeration) { + // This will be true only if running on Windows XP. + VLOG(2) << "AudioManagerWin on WinXP; nothing more to test."; + } else { + VLOG(2) << "Testing AudioManagerWin in fallback WinXP mode."; + audio_manager_win->SetEnumerationType(AudioManagerWin::kWaveEnumeration); + return true; + } +#endif // defined(OS_WIN) + return false; + } +}; + +TEST_F(AudioManagerTest, GetAudioOutputDeviceNames) { + // On Linux, we may be able to test both the Alsa and Pulseaudio + // versions of the audio manager. +#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) +#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) + +#if defined(OS_MACOSX) + VLOG(2) << "Testing platform-default AudioManager."; + scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); + GetAudioOutputDeviceNamesImpl(audio_manager.get()); +#endif // defined(OS_MACOSX) + +#if defined(OS_WIN) + { + // TODO(joi): Unify the tests in audio_input_device_unittest.cc + // with the tests in this file, and reuse the Windows-specific + // bits from that file. + VLOG(2) << "Testing AudioManagerWin in its default mode."; + scoped_ptr<AudioManager> audio_manager_win(AudioManager::Create()); + GetAudioOutputDeviceNamesImpl(audio_manager_win.get()); + + if (SetupForSecondTest(audio_manager_win.get())) { + GetAudioOutputDeviceNamesImpl(audio_manager_win.get()); + } + } +#endif // defined(OS_WIN) +} + +TEST_F(AudioManagerTest, GetDefaultOutputStreamParameters) { +#if defined(OS_WIN) || defined(OS_MACOSX) + scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); + ASSERT_TRUE(audio_manager); + if (!audio_manager->HasAudioOutputDevices()) + return; + + AudioParameters params = audio_manager->GetDefaultOutputStreamParameters(); + EXPECT_TRUE(params.IsValid()); +#endif // defined(OS_WIN) || defined(OS_MACOSX) +} + +TEST_F(AudioManagerTest, GetAssociatedOutputDeviceID) { +#if defined(OS_WIN) || defined(OS_MACOSX) + scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); + ASSERT_TRUE(audio_manager); + if (!audio_manager->HasAudioOutputDevices() || + !audio_manager->HasAudioInputDevices()) { + return; + } + + AudioDeviceNames device_names; + audio_manager->GetAudioInputDeviceNames(&device_names); + bool found_an_associated_device = false; + for (AudioDeviceNames::iterator it = device_names.begin(); + it != device_names.end(); + ++it) { + EXPECT_FALSE(it->unique_id.empty()); + EXPECT_FALSE(it->device_name.empty()); + std::string output_device_id( + audio_manager->GetAssociatedOutputDeviceID(it->unique_id)); + if (!output_device_id.empty()) { + VLOG(2) << it->unique_id << " matches with " << output_device_id; + found_an_associated_device = true; + } + } + + EXPECT_TRUE(found_an_associated_device); +#endif // defined(OS_WIN) || defined(OS_MACOSX) +} + +} // namespace media diff --git a/media/audio/audio_output_controller.cc b/media/audio/audio_output_controller.cc index f7f4cf8240..1784c9b69a 100644 --- a/media/audio/audio_output_controller.cc +++ b/media/audio/audio_output_controller.cc @@ -32,14 +32,17 @@ static const int kPowerMeasurementsPerSecond = 30; const int AudioOutputController::kPollNumAttempts = 3; const int AudioOutputController::kPollPauseInMilliseconds = 3; -AudioOutputController::AudioOutputController(AudioManager* audio_manager, - EventHandler* handler, - const AudioParameters& params, - const std::string& input_device_id, - SyncReader* sync_reader) +AudioOutputController::AudioOutputController( + AudioManager* audio_manager, + EventHandler* handler, + const AudioParameters& params, + const std::string& output_device_id, + const std::string& input_device_id, + SyncReader* sync_reader) : audio_manager_(audio_manager), params_(params), handler_(handler), + output_device_id_(output_device_id), input_device_id_(input_device_id), stream_(NULL), diverting_to_stream_(NULL), @@ -67,6 +70,7 @@ scoped_refptr<AudioOutputController> AudioOutputController::Create( AudioManager* audio_manager, EventHandler* event_handler, const AudioParameters& params, + const std::string& output_device_id, const std::string& input_device_id, SyncReader* sync_reader) { DCHECK(audio_manager); @@ -76,7 +80,8 @@ scoped_refptr<AudioOutputController> AudioOutputController::Create( return NULL; scoped_refptr<AudioOutputController> controller(new AudioOutputController( - audio_manager, event_handler, params, input_device_id, sync_reader)); + audio_manager, event_handler, params, output_device_id, input_device_id, + sync_reader)); controller->message_loop_->PostTask(FROM_HERE, base::Bind( &AudioOutputController::DoCreate, controller, false)); return controller; @@ -114,8 +119,10 @@ void AudioOutputController::DoCreate(bool is_for_device_change) { DoStopCloseAndClearStream(); // Calls RemoveOutputDeviceChangeListener(). DCHECK_EQ(kEmpty, state_); - stream_ = diverting_to_stream_ ? diverting_to_stream_ : - audio_manager_->MakeAudioOutputStreamProxy(params_, input_device_id_); + stream_ = diverting_to_stream_ ? + diverting_to_stream_ : + audio_manager_->MakeAudioOutputStreamProxy(params_, output_device_id_, + input_device_id_); if (!stream_) { state_ = kError; handler_->OnError(); diff --git a/media/audio/audio_output_controller.h b/media/audio/audio_output_controller.h index 38a2c03f59..387214e1d0 100644 --- a/media/audio/audio_output_controller.h +++ b/media/audio/audio_output_controller.h @@ -101,10 +101,14 @@ class MEDIA_EXPORT AudioOutputController // thread, and if this is successful, the |event_handler| will receive an // OnCreated() call from the same audio manager thread. |audio_manager| must // outlive AudioOutputController. + // The |output_device_id| can be either empty (default device) or specify a + // specific hardware device for audio output. The |input_device_id| is + // used only for unified audio when opening up input and output at the same + // time (controlled by |params.input_channel_count()|). static scoped_refptr<AudioOutputController> Create( AudioManager* audio_manager, EventHandler* event_handler, - const AudioParameters& params, const std::string& input_device_id, - SyncReader* sync_reader); + const AudioParameters& params, const std::string& output_device_id, + const std::string& input_device_id, SyncReader* sync_reader); // Methods to control playback of the stream. @@ -166,6 +170,7 @@ class MEDIA_EXPORT AudioOutputController AudioOutputController(AudioManager* audio_manager, EventHandler* handler, const AudioParameters& params, + const std::string& output_device_id, const std::string& input_device_id, SyncReader* sync_reader); @@ -198,8 +203,12 @@ class MEDIA_EXPORT AudioOutputController const AudioParameters params_; EventHandler* const handler_; + // Specifies the device id of the output device to open or empty for the + // default output device. + const std::string output_device_id_; + // Used by the unified IO to open the correct input device. - std::string input_device_id_; + const std::string input_device_id_; AudioOutputStream* stream_; diff --git a/media/audio/audio_output_controller_unittest.cc b/media/audio/audio_output_controller_unittest.cc index 128cc07716..37305c3978 100644 --- a/media/audio/audio_output_controller_unittest.cc +++ b/media/audio/audio_output_controller_unittest.cc @@ -29,8 +29,6 @@ static const int kSampleRate = AudioParameters::kAudioCDSampleRate; static const int kBitsPerSample = 16; static const ChannelLayout kChannelLayout = CHANNEL_LAYOUT_STEREO; static const int kSamplesPerPacket = kSampleRate / 100; -static const int kHardwareBufferSize = kSamplesPerPacket * - ChannelLayoutToChannelCount(kChannelLayout) * kBitsPerSample / 8; static const double kTestVolume = 0.25; class MockAudioOutputControllerEventHandler @@ -122,7 +120,7 @@ class AudioOutputControllerTest : public testing::Test { controller_ = AudioOutputController::Create( audio_manager_.get(), &mock_event_handler_, params_, std::string(), - &mock_sync_reader_); + std::string(), &mock_sync_reader_); if (controller_.get()) controller_->SetVolume(kTestVolume); diff --git a/media/audio/audio_output_dispatcher.cc b/media/audio/audio_output_dispatcher.cc index 06206d7be7..a151c449f0 100644 --- a/media/audio/audio_output_dispatcher.cc +++ b/media/audio/audio_output_dispatcher.cc @@ -11,10 +11,12 @@ namespace media { AudioOutputDispatcher::AudioOutputDispatcher( AudioManager* audio_manager, const AudioParameters& params, + const std::string& output_device_id, const std::string& input_device_id) : audio_manager_(audio_manager), message_loop_(base::MessageLoop::current()), params_(params), + output_device_id_(output_device_id), input_device_id_(input_device_id) { // We expect to be instantiated on the audio thread. Otherwise the // message_loop_ member will point to the wrong message loop! diff --git a/media/audio/audio_output_dispatcher.h b/media/audio/audio_output_dispatcher.h index a79fd94477..30266ed6a9 100644 --- a/media/audio/audio_output_dispatcher.h +++ b/media/audio/audio_output_dispatcher.h @@ -38,6 +38,7 @@ class MEDIA_EXPORT AudioOutputDispatcher public: AudioOutputDispatcher(AudioManager* audio_manager, const AudioParameters& params, + const std::string& output_device_id, const std::string& input_device_id); // Called by AudioOutputProxy to open the stream. @@ -79,6 +80,7 @@ class MEDIA_EXPORT AudioOutputDispatcher AudioManager* audio_manager_; base::MessageLoop* message_loop_; AudioParameters params_; + const std::string output_device_id_; const std::string input_device_id_; private: diff --git a/media/audio/audio_output_dispatcher_impl.cc b/media/audio/audio_output_dispatcher_impl.cc index 1df8e7ddd5..bcdcd65146 100644 --- a/media/audio/audio_output_dispatcher_impl.cc +++ b/media/audio/audio_output_dispatcher_impl.cc @@ -19,9 +19,11 @@ namespace media { AudioOutputDispatcherImpl::AudioOutputDispatcherImpl( AudioManager* audio_manager, const AudioParameters& params, + const std::string& output_device_id, const std::string& input_device_id, const base::TimeDelta& close_delay) - : AudioOutputDispatcher(audio_manager, params, input_device_id), + : AudioOutputDispatcher(audio_manager, params, output_device_id, + input_device_id), pause_delay_(base::TimeDelta::FromMicroseconds( 2 * params.frames_per_buffer() * base::Time::kMicrosecondsPerSecond / static_cast<float>(params.sample_rate()))), @@ -168,7 +170,7 @@ void AudioOutputDispatcherImpl::Shutdown() { bool AudioOutputDispatcherImpl::CreateAndOpenStream() { DCHECK_EQ(base::MessageLoop::current(), message_loop_); AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream( - params_, input_device_id_); + params_, output_device_id_, input_device_id_); if (!stream) return false; diff --git a/media/audio/audio_output_dispatcher_impl.h b/media/audio/audio_output_dispatcher_impl.h index 06fe3ebeaf..b59f835f9b 100644 --- a/media/audio/audio_output_dispatcher_impl.h +++ b/media/audio/audio_output_dispatcher_impl.h @@ -35,6 +35,7 @@ class MEDIA_EXPORT AudioOutputDispatcherImpl : public AudioOutputDispatcher { // the audio device is closed. AudioOutputDispatcherImpl(AudioManager* audio_manager, const AudioParameters& params, + const std::string& output_device_id, const std::string& input_device_id, const base::TimeDelta& close_delay); diff --git a/media/audio/audio_output_proxy_unittest.cc b/media/audio/audio_output_proxy_unittest.cc index de95b0661e..1806ce6613 100644 --- a/media/audio/audio_output_proxy_unittest.cc +++ b/media/audio/audio_output_proxy_unittest.cc @@ -95,10 +95,14 @@ class MockAudioManager : public AudioManagerBase { MOCK_METHOD0(HasAudioOutputDevices, bool()); MOCK_METHOD0(HasAudioInputDevices, bool()); MOCK_METHOD0(GetAudioInputDeviceModel, string16()); - MOCK_METHOD2(MakeAudioOutputStream, AudioOutputStream*( - const AudioParameters& params, const std::string& input_device_id)); - MOCK_METHOD2(MakeAudioOutputStreamProxy, AudioOutputStream*( - const AudioParameters& params, const std::string& input_device_id)); + MOCK_METHOD3(MakeAudioOutputStream, AudioOutputStream*( + const AudioParameters& params, + const std::string& device_id, + const std::string& input_device_id)); + MOCK_METHOD3(MakeAudioOutputStreamProxy, AudioOutputStream*( + const AudioParameters& params, + const std::string& device_id, + const std::string& input_device_id)); MOCK_METHOD2(MakeAudioInputStream, AudioInputStream*( const AudioParameters& params, const std::string& device_id)); MOCK_METHOD0(ShowAudioInputSettings, void()); @@ -108,14 +112,15 @@ class MockAudioManager : public AudioManagerBase { MOCK_METHOD1(MakeLinearOutputStream, AudioOutputStream*( const AudioParameters& params)); - MOCK_METHOD2(MakeLowLatencyOutputStream, AudioOutputStream*( - const AudioParameters& params, const std::string& input_device_id)); + MOCK_METHOD3(MakeLowLatencyOutputStream, AudioOutputStream*( + const AudioParameters& params, const std::string& device_id, + const std::string& input_device_id)); MOCK_METHOD2(MakeLinearInputStream, AudioInputStream*( const AudioParameters& params, const std::string& device_id)); MOCK_METHOD2(MakeLowLatencyInputStream, AudioInputStream*( const AudioParameters& params, const std::string& device_id)); - MOCK_METHOD1(GetPreferredOutputStreamParameters, AudioParameters( - const AudioParameters& params)); + MOCK_METHOD2(GetPreferredOutputStreamParameters, AudioParameters( + const std::string& device_id, const AudioParameters& params)); }; class MockAudioSourceCallback : public AudioOutputStream::AudioSourceCallback { @@ -161,6 +166,7 @@ class AudioOutputProxyTest : public testing::Test { dispatcher_impl_ = new AudioOutputDispatcherImpl(&manager(), params_, std::string(), + std::string(), close_delay); // Necessary to know how long the dispatcher will wait before posting @@ -186,7 +192,7 @@ class AudioOutputProxyTest : public testing::Test { void OpenAndClose(AudioOutputDispatcher* dispatcher) { MockAudioOutputStream stream(&manager_, params_); - EXPECT_CALL(manager(), MakeAudioOutputStream(_, _)) + EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _)) .WillOnce(Return(&stream)); EXPECT_CALL(stream, Open()) .WillOnce(Return(true)); @@ -203,7 +209,7 @@ class AudioOutputProxyTest : public testing::Test { void StartAndStop(AudioOutputDispatcher* dispatcher) { MockAudioOutputStream stream(&manager_, params_); - EXPECT_CALL(manager(), MakeAudioOutputStream(_, _)) + EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _)) .WillOnce(Return(&stream)); EXPECT_CALL(stream, Open()) .WillOnce(Return(true)); @@ -229,7 +235,7 @@ class AudioOutputProxyTest : public testing::Test { void CloseAfterStop(AudioOutputDispatcher* dispatcher) { MockAudioOutputStream stream(&manager_, params_); - EXPECT_CALL(manager(), MakeAudioOutputStream(_, _)) + EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _)) .WillOnce(Return(&stream)); EXPECT_CALL(stream, Open()) .WillOnce(Return(true)); @@ -261,7 +267,7 @@ class AudioOutputProxyTest : public testing::Test { void TwoStreams(AudioOutputDispatcher* dispatcher) { MockAudioOutputStream stream(&manager_, params_); - EXPECT_CALL(manager(), MakeAudioOutputStream(_, _)) + EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _)) .WillOnce(Return(&stream)); EXPECT_CALL(stream, Open()) .WillOnce(Return(true)); @@ -283,7 +289,7 @@ class AudioOutputProxyTest : public testing::Test { void OpenFailed(AudioOutputDispatcher* dispatcher) { MockAudioOutputStream stream(&manager_, params_); - EXPECT_CALL(manager(), MakeAudioOutputStream(_, _)) + EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _)) .WillOnce(Return(&stream)); EXPECT_CALL(stream, Open()) .WillOnce(Return(false)); @@ -301,7 +307,7 @@ class AudioOutputProxyTest : public testing::Test { void CreateAndWait(AudioOutputDispatcher* dispatcher) { MockAudioOutputStream stream(&manager_, params_); - EXPECT_CALL(manager(), MakeAudioOutputStream(_, _)) + EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _)) .WillOnce(Return(&stream)); EXPECT_CALL(stream, Open()) .WillOnce(Return(true)); @@ -328,7 +334,7 @@ class AudioOutputProxyTest : public testing::Test { MockAudioOutputStream stream1(&manager_, params_); MockAudioOutputStream stream2(&manager_, params_); - EXPECT_CALL(manager(), MakeAudioOutputStream(_, _)) + EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _)) .WillOnce(Return(&stream1)) .WillOnce(Return(&stream2)); @@ -366,7 +372,7 @@ class AudioOutputProxyTest : public testing::Test { MockAudioOutputStream stream1(&manager_, params_); MockAudioOutputStream stream2(&manager_, params_); - EXPECT_CALL(manager(), MakeAudioOutputStream(_, _)) + EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _)) .WillOnce(Return(&stream1)) .WillOnce(Return(&stream2)); @@ -406,7 +412,7 @@ class AudioOutputProxyTest : public testing::Test { void StartFailed(AudioOutputDispatcher* dispatcher) { MockAudioOutputStream stream(&manager_, params_); - EXPECT_CALL(manager(), MakeAudioOutputStream(_, _)) + EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _)) .WillOnce(Return(&stream)); EXPECT_CALL(stream, Open()) .WillOnce(Return(true)); @@ -425,7 +431,7 @@ class AudioOutputProxyTest : public testing::Test { Mock::VerifyAndClear(&stream); // |stream| is closed at this point. Start() should reopen it again. - EXPECT_CALL(manager(), MakeAudioOutputStream(_, _)) + EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _)) .Times(2) .WillRepeatedly(Return(reinterpret_cast<AudioOutputStream*>(NULL))); @@ -467,7 +473,8 @@ class AudioOutputResamplerTest : public AudioOutputProxyTest { AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO, 16000, 16, 1024); resampler_ = new AudioOutputResampler( - &manager(), params_, resampler_params_, std::string(), close_delay); + &manager(), params_, resampler_params_, std::string(), std::string(), + close_delay); } virtual void OnStart() OVERRIDE { @@ -568,7 +575,7 @@ TEST_F(AudioOutputResamplerTest, StartFailed) { StartFailed(resampler_.get()); } // ensure AudioOutputResampler falls back to the high latency path. TEST_F(AudioOutputResamplerTest, LowLatencyCreateFailedFallback) { MockAudioOutputStream stream(&manager_, params_); - EXPECT_CALL(manager(), MakeAudioOutputStream(_, _)) + EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _)) .Times(2) .WillOnce(Return(static_cast<AudioOutputStream*>(NULL))) .WillRepeatedly(Return(&stream)); @@ -588,7 +595,7 @@ TEST_F(AudioOutputResamplerTest, LowLatencyCreateFailedFallback) { TEST_F(AudioOutputResamplerTest, LowLatencyOpenFailedFallback) { MockAudioOutputStream failed_stream(&manager_, params_); MockAudioOutputStream okay_stream(&manager_, params_); - EXPECT_CALL(manager(), MakeAudioOutputStream(_, _)) + EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _)) .Times(2) .WillOnce(Return(&failed_stream)) .WillRepeatedly(Return(&okay_stream)); @@ -619,7 +626,7 @@ TEST_F(AudioOutputResamplerTest, HighLatencyFallbackFailed) { #else static const int kFallbackCount = 1; #endif - EXPECT_CALL(manager(), MakeAudioOutputStream(_, _)) + EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _)) .Times(kFallbackCount) .WillRepeatedly(Return(static_cast<AudioOutputStream*>(NULL))); @@ -630,7 +637,7 @@ TEST_F(AudioOutputResamplerTest, HighLatencyFallbackFailed) { testing::Property(&AudioParameters::sample_rate, params_.sample_rate()), testing::Property( &AudioParameters::frames_per_buffer, params_.frames_per_buffer())), - _)) + _, _)) .Times(1) .WillOnce(Return(&okay_stream)); EXPECT_CALL(okay_stream, Open()) @@ -655,7 +662,7 @@ TEST_F(AudioOutputResamplerTest, AllFallbackFailed) { #else static const int kFallbackCount = 2; #endif - EXPECT_CALL(manager(), MakeAudioOutputStream(_, _)) + EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _)) .Times(kFallbackCount) .WillRepeatedly(Return(static_cast<AudioOutputStream*>(NULL))); @@ -673,7 +680,7 @@ TEST_F(AudioOutputResamplerTest, LowLatencyOpenEventuallyFails) { MockAudioOutputStream stream3(&manager_, params_); // Setup the mock such that all three streams are successfully created. - EXPECT_CALL(manager(), MakeAudioOutputStream(_, _)) + EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _)) .WillOnce(Return(&stream1)) .WillOnce(Return(&stream2)) .WillOnce(Return(&stream3)) diff --git a/media/audio/audio_output_resampler.cc b/media/audio/audio_output_resampler.cc index 6db0e2fb2f..da424ec124 100644 --- a/media/audio/audio_output_resampler.cc +++ b/media/audio/audio_output_resampler.cc @@ -147,12 +147,13 @@ static AudioParameters SetupFallbackParams( AudioOutputResampler::AudioOutputResampler(AudioManager* audio_manager, const AudioParameters& input_params, const AudioParameters& output_params, + const std::string& output_device_id, const std::string& input_device_id, const base::TimeDelta& close_delay) - : AudioOutputDispatcher(audio_manager, input_params, input_device_id), + : AudioOutputDispatcher(audio_manager, input_params, output_device_id, + input_device_id), close_delay_(close_delay), output_params_(output_params), - input_device_id_(input_device_id), streams_opened_(false) { DCHECK(input_params.IsValid()); DCHECK(output_params.IsValid()); @@ -172,7 +173,8 @@ void AudioOutputResampler::Initialize() { DCHECK(!streams_opened_); DCHECK(callbacks_.empty()); dispatcher_ = new AudioOutputDispatcherImpl( - audio_manager_, output_params_, input_device_id_, close_delay_); + audio_manager_, output_params_, output_device_id_, input_device_id_, + close_delay_); } bool AudioOutputResampler::OpenStream() { diff --git a/media/audio/audio_output_resampler.h b/media/audio/audio_output_resampler.h index df9e4320b5..f9a75ac38f 100644 --- a/media/audio/audio_output_resampler.h +++ b/media/audio/audio_output_resampler.h @@ -40,6 +40,7 @@ class MEDIA_EXPORT AudioOutputResampler : public AudioOutputDispatcher { AudioOutputResampler(AudioManager* audio_manager, const AudioParameters& input_params, const AudioParameters& output_params, + const std::string& output_device_id, const std::string& input_device_id, const base::TimeDelta& close_delay); @@ -74,9 +75,6 @@ class MEDIA_EXPORT AudioOutputResampler : public AudioOutputDispatcher { // AudioParameters used to setup the output stream. AudioParameters output_params_; - // Device ID to be used by the unified IO to open the correct input device. - const std::string input_device_id_; - // Whether any streams have been opened through |dispatcher_|, if so we can't // fallback on future OpenStream() failures. bool streams_opened_; diff --git a/media/audio/cras/audio_manager_cras.cc b/media/audio/cras/audio_manager_cras.cc index 165d642922..276487557e 100644 --- a/media/audio/cras/audio_manager_cras.cc +++ b/media/audio/cras/audio_manager_cras.cc @@ -76,7 +76,10 @@ AudioOutputStream* AudioManagerCras::MakeLinearOutputStream( } AudioOutputStream* AudioManagerCras::MakeLowLatencyOutputStream( - const AudioParameters& params, const std::string& input_device_id) { + const AudioParameters& params, + const std::string& device_id, + const std::string& input_device_id) { + DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!"; DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format()); // TODO(dgreid): Open the correct input device for unified IO. return MakeOutputStream(params); @@ -95,7 +98,10 @@ AudioInputStream* AudioManagerCras::MakeLowLatencyInputStream( } AudioParameters AudioManagerCras::GetPreferredOutputStreamParameters( + const std::string& output_device_id, const AudioParameters& input_params) { + // TODO(tommi): Support |output_device_id|. + DLOG_IF(ERROR, !output_device_id.empty()) << "Not implemented!"; static const int kDefaultOutputBufferSize = 512; ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO; diff --git a/media/audio/cras/audio_manager_cras.h b/media/audio/cras/audio_manager_cras.h index fdc5b02688..41e1876ac8 100644 --- a/media/audio/cras/audio_manager_cras.h +++ b/media/audio/cras/audio_manager_cras.h @@ -35,6 +35,7 @@ class MEDIA_EXPORT AudioManagerCras : public AudioManagerBase { const AudioParameters& params) OVERRIDE; virtual AudioOutputStream* MakeLowLatencyOutputStream( const AudioParameters& params, + const std::string& device_id, const std::string& input_device_id) OVERRIDE; virtual AudioInputStream* MakeLinearInputStream( const AudioParameters& params, const std::string& device_id) OVERRIDE; @@ -45,6 +46,7 @@ class MEDIA_EXPORT AudioManagerCras : public AudioManagerBase { virtual ~AudioManagerCras(); virtual AudioParameters GetPreferredOutputStreamParameters( + const std::string& output_device_id, const AudioParameters& input_params) OVERRIDE; private: diff --git a/media/audio/cras/cras_input.cc b/media/audio/cras/cras_input.cc index a82fe283f7..363dc68227 100644 --- a/media/audio/cras/cras_input.cc +++ b/media/audio/cras/cras_input.cc @@ -114,7 +114,6 @@ void CrasInputStream::Start(AudioInputCallback* callback) { StartAgc(); callback_ = callback; - LOG(ERROR) << "Input Start"; // Prepare |audio_format| and |stream_params| for the stream we // will create. diff --git a/media/audio/ios/audio_manager_ios.h b/media/audio/ios/audio_manager_ios.h deleted file mode 100644 index 19751502fd..0000000000 --- a/media/audio/ios/audio_manager_ios.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 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 MEDIA_AUDIO_IOS_AUDIO_MANAGER_IOS_H_ -#define MEDIA_AUDIO_IOS_AUDIO_MANAGER_IOS_H_ - -#include "base/basictypes.h" -#include "media/audio/audio_manager_base.h" - -namespace media { - -class PCMQueueInAudioInputStream; - -// iOS implementation of the AudioManager singleton. Supports only audio input. -class MEDIA_EXPORT AudioManagerIOS : public AudioManagerBase { - public: - AudioManagerIOS(); - - // Implementation of AudioManager. - virtual bool HasAudioOutputDevices() OVERRIDE; - virtual bool HasAudioInputDevices() OVERRIDE; - virtual AudioOutputStream* MakeAudioOutputStream( - const AudioParameters& params, - const std::string& input_device_id) OVERRIDE; - virtual AudioInputStream* MakeAudioInputStream( - const AudioParameters& params, const std::string& device_id) OVERRIDE; - virtual AudioParameters GetInputStreamParameters( - const std::string& device_id) OVERRIDE; - - // Implementation of AudioManagerBase. - virtual AudioOutputStream* MakeLinearOutputStream( - const AudioParameters& params) OVERRIDE; - virtual AudioOutputStream* MakeLowLatencyOutputStream( - const AudioParameters& params, - const std::string& input_device_id) OVERRIDE; - virtual AudioInputStream* MakeLinearInputStream( - const AudioParameters& params, const std::string& device_id) OVERRIDE; - virtual AudioInputStream* MakeLowLatencyInputStream( - const AudioParameters& params, const std::string& device_id) OVERRIDE; - virtual void ReleaseOutputStream(AudioOutputStream* stream) OVERRIDE; - virtual void ReleaseInputStream(AudioInputStream* stream) OVERRIDE; - - protected: - virtual ~AudioManagerIOS(); - - virtual AudioParameters GetPreferredOutputStreamParameters( - const AudioParameters& input_params) OVERRIDE; - - private: - DISALLOW_COPY_AND_ASSIGN(AudioManagerIOS); -}; - -} // namespace media - -#endif // MEDIA_AUDIO_IOS_AUDIO_MANAGER_IOS_H_ diff --git a/media/audio/ios/audio_manager_ios.mm b/media/audio/ios/audio_manager_ios.mm deleted file mode 100644 index 49479302ef..0000000000 --- a/media/audio/ios/audio_manager_ios.mm +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 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 "media/audio/ios/audio_manager_ios.h" - -#import <AudioToolbox/AudioToolbox.h> -#import <AVFoundation/AVFoundation.h> - -#include "base/sys_info.h" -#include "media/audio/audio_parameters.h" -#include "media/audio/fake_audio_input_stream.h" -#include "media/audio/ios/audio_session_util_ios.h" -#include "media/audio/mac/audio_input_mac.h" -#include "media/base/channel_layout.h" -#include "media/base/limits.h" - -namespace media { - -enum { kMaxInputChannels = 2 }; - -AudioManagerIOS::AudioManagerIOS() { -} - -AudioManagerIOS::~AudioManagerIOS() { - Shutdown(); -} - -bool AudioManagerIOS::HasAudioOutputDevices() { - return false; -} - -bool AudioManagerIOS::HasAudioInputDevices() { - if (!InitAudioSessionIOS()) - return false; - // Note that the |kAudioSessionProperty_AudioInputAvailable| property is a - // 32-bit integer, not a boolean. - UInt32 property_size; - OSStatus error = - AudioSessionGetPropertySize(kAudioSessionProperty_AudioInputAvailable, - &property_size); - if (error != kAudioSessionNoError) - return false; - UInt32 audio_input_is_available = false; - DCHECK(property_size == sizeof(audio_input_is_available)); - error = AudioSessionGetProperty(kAudioSessionProperty_AudioInputAvailable, - &property_size, - &audio_input_is_available); - return error == kAudioSessionNoError ? audio_input_is_available : false; -} - -AudioParameters AudioManagerIOS::GetInputStreamParameters( - const std::string& device_id) { - // TODO(xians): figure out the right input sample rate and buffer size to - // achieve the best audio performance for iOS devices. - // TODO(xians): query the native channel layout for the specific device. - static const int kDefaultSampleRate = 48000; - static const int kDefaultBufferSize = 2048; - return AudioParameters( - AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO, - kDefaultSampleRate, 16, kDefaultBufferSize); -} - -AudioOutputStream* AudioManagerIOS::MakeAudioOutputStream( - const AudioParameters& params, const std::string& input_device_id) { - NOTIMPLEMENTED(); // Only input is supported on iOS. - return NULL; -} - -AudioInputStream* AudioManagerIOS::MakeAudioInputStream( - const AudioParameters& params, const std::string& device_id) { - // Current line of iOS devices has only one audio input. - // Ignore the device_id (unittest uses a test value in it). - if (!params.IsValid() || (params.channels() > kMaxInputChannels)) - return NULL; - - if (params.format() == AudioParameters::AUDIO_FAKE) - return FakeAudioInputStream::MakeFakeStream(this, params); - else if (params.format() == AudioParameters::AUDIO_PCM_LINEAR) - return new PCMQueueInAudioInputStream(this, params); - return NULL; -} - -AudioOutputStream* AudioManagerIOS::MakeLinearOutputStream( - const AudioParameters& params) { - NOTIMPLEMENTED(); // Only input is supported on iOS. - return NULL; -} - -AudioOutputStream* AudioManagerIOS::MakeLowLatencyOutputStream( - const AudioParameters& params, const std::string& input_device_id) { - NOTIMPLEMENTED(); // Only input is supported on iOS. - return NULL; -} - -AudioInputStream* AudioManagerIOS::MakeLinearInputStream( - const AudioParameters& params, const std::string& device_id) { - return MakeAudioInputStream(params, device_id); -} - -AudioInputStream* AudioManagerIOS::MakeLowLatencyInputStream( - const AudioParameters& params, const std::string& device_id) { - NOTIMPLEMENTED(); // Only linear audio input is supported on iOS. - return MakeAudioInputStream(params, device_id); -} - - -AudioParameters AudioManagerIOS::GetPreferredOutputStreamParameters( - const AudioParameters& input_params) { - // TODO(xians): handle the case when input_params is valid. - // TODO(xians): figure out the right output sample rate and sample rate to - // achieve the best audio performance for iOS devices. - // TODO(xians): add support to --audio-buffer-size flag. - static const int kDefaultSampleRate = 48000; - static const int kDefaultBufferSize = 2048; - if (input_params.IsValid()) { - NOTREACHED(); - } - - return AudioParameters( - AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO, - kDefaultSampleRate, 16, kDefaultBufferSize); -} - -// Called by the stream when it has been released by calling Close(). -void AudioManagerIOS::ReleaseOutputStream(AudioOutputStream* stream) { - NOTIMPLEMENTED(); // Only input is supported on iOS. -} - -// Called by the stream when it has been released by calling Close(). -void AudioManagerIOS::ReleaseInputStream(AudioInputStream* stream) { - delete stream; -} - -// static -AudioManager* CreateAudioManager() { - return new AudioManagerIOS(); -} - -} // namespace media diff --git a/media/audio/ios/audio_manager_ios_unittest.cc b/media/audio/ios/audio_manager_ios_unittest.cc deleted file mode 100644 index 30ebc04f20..0000000000 --- a/media/audio/ios/audio_manager_ios_unittest.cc +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 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 "base/basictypes.h" -#include "base/memory/scoped_ptr.h" -#include "media/audio/audio_io.h" -#include "media/audio/audio_manager.h" -#include "testing/gtest/include/gtest/gtest.h" - -using namespace media; - -// Test that input is supported and output is not. -TEST(IOSAudioTest, AudioSupport) { - scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); - ASSERT_TRUE(NULL != audio_manager.get()); - ASSERT_FALSE(audio_manager->HasAudioOutputDevices()); - ASSERT_TRUE(audio_manager->HasAudioInputDevices()); -} - -// Test that input stream can be opened and closed. -TEST(IOSAudioTest, InputStreamOpenAndClose) { - scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); - ASSERT_TRUE(NULL != audio_manager.get()); - if (!audio_manager->HasAudioInputDevices()) - return; - AudioInputStream* ias = audio_manager->MakeAudioInputStream( - AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_STEREO, - 8000, 16, 1024), - std::string("test_device")); - ASSERT_TRUE(NULL != ias); - EXPECT_TRUE(ias->Open()); - ias->Close(); -} diff --git a/media/audio/ios/audio_session_util_ios.h b/media/audio/ios/audio_session_util_ios.h deleted file mode 100644 index 175db91fae..0000000000 --- a/media/audio/ios/audio_session_util_ios.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 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 MEDIA_AUDIO_IOS_AUDIO_SESSION_UTIL_IOS_H_ -#define MEDIA_AUDIO_IOS_AUDIO_SESSION_UTIL_IOS_H_ - -namespace media { - -// Initializes and configures the audio session, returning a bool indicating -// whether initialization was successful. Can be called multiple times. -// Safe to call from any thread. -bool InitAudioSessionIOS(); - -} // namespace media - -#endif // MEDIA_AUDIO_IOS_AUDIO_SESSION_UTIL_IOS_H_ diff --git a/media/audio/ios/audio_session_util_ios.mm b/media/audio/ios/audio_session_util_ios.mm deleted file mode 100644 index a4071a04cc..0000000000 --- a/media/audio/ios/audio_session_util_ios.mm +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 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 "media/audio/ios/audio_session_util_ios.h" - -#include <AVFoundation/AVFoundation.h> - -#include "base/logging.h" - -namespace media { - -bool InitAudioSessionIOS() { - static bool kSessionInitialized = false; - static dispatch_once_t once = 0; - dispatch_once(&once, ^{ - OSStatus error = AudioSessionInitialize(NULL, NULL, NULL, NULL); - if (error != kAudioSessionNoError) - DLOG(ERROR) << "AudioSessionInitialize OSStatus error: " << error; - BOOL result = [[AVAudioSession sharedInstance] - setCategory:AVAudioSessionCategoryPlayAndRecord - error:nil]; - if (!result) - DLOG(ERROR) << "AVAudioSession setCategory failed"; - UInt32 allowMixing = true; - AudioSessionSetProperty( - kAudioSessionProperty_OverrideCategoryMixWithOthers, - sizeof(allowMixing), &allowMixing); - UInt32 defaultToSpeaker = true; - AudioSessionSetProperty( - kAudioSessionProperty_OverrideCategoryDefaultToSpeaker, - sizeof(defaultToSpeaker), - &defaultToSpeaker); - // Speech input cannot be used if either of these two conditions fail. - kSessionInitialized = (error == kAudioSessionNoError) && result; - }); - return kSessionInitialized; -} - -} // namespace media diff --git a/media/audio/linux/alsa_output_unittest.cc b/media/audio/linux/alsa_output_unittest.cc index 32456360f4..82fbab94c1 100644 --- a/media/audio/linux/alsa_output_unittest.cc +++ b/media/audio/linux/alsa_output_unittest.cc @@ -83,8 +83,10 @@ class MockAudioManagerLinux : public AudioManagerLinux { MOCK_METHOD0(HasAudioInputDevices, bool()); MOCK_METHOD1(MakeLinearOutputStream, AudioOutputStream*( const AudioParameters& params)); - MOCK_METHOD2(MakeLowLatencyOutputStream, AudioOutputStream*( - const AudioParameters& params, const std::string& input_device_id)); + MOCK_METHOD3(MakeLowLatencyOutputStream, AudioOutputStream*( + const AudioParameters& params, + const std::string& device_id, + const std::string& input_device_id)); MOCK_METHOD2(MakeLowLatencyInputStream, AudioInputStream*( const AudioParameters& params, const std::string& device_id)); diff --git a/media/audio/linux/audio_manager_linux.cc b/media/audio/linux/audio_manager_linux.cc index 38253e2e65..7596c2fe25 100644 --- a/media/audio/linux/audio_manager_linux.cc +++ b/media/audio/linux/audio_manager_linux.cc @@ -42,9 +42,9 @@ static const int kDefaultSampleRate = 48000; // hence surround devices are not stored in the list. static const char* kInvalidAudioInputDevices[] = { "default", + "dmix", "null", "pulse", - "dmix", "surround", }; @@ -105,7 +105,13 @@ void AudioManagerLinux::ShowAudioInputSettings() { void AudioManagerLinux::GetAudioInputDeviceNames( media::AudioDeviceNames* device_names) { 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( @@ -117,7 +123,8 @@ AudioParameters AudioManagerLinux::GetInputStreamParameters( kDefaultSampleRate, 16, kDefaultInputBufferSize); } -void AudioManagerLinux::GetAlsaAudioInputDevices( +void AudioManagerLinux::GetAlsaAudioDevices( + StreamType type, media::AudioDeviceNames* device_names) { // Constants specified by the ALSA API for device hints. static const char kPcmInterfaceName[] = "pcm"; @@ -128,37 +135,40 @@ void AudioManagerLinux::GetAlsaAudioInputDevices( void** hints = NULL; int error = wrapper_->DeviceNameHint(card, kPcmInterfaceName, &hints); if (!error) { - GetAlsaDevicesInfo(hints, device_names); + GetAlsaDevicesInfo(type, hints, device_names); // Destroy the hints now that we're done with it. wrapper_->DeviceNameFreeHint(hints); } else { - DLOG(WARNING) << "GetAudioInputDevices: unable to get device hints: " + DLOG(WARNING) << "GetAlsaAudioDevices: unable to get device hints: " << wrapper_->StrError(error); } } } 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 kNameHintName[] = "NAME"; 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++) { - // 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. scoped_ptr_malloc<char> io(wrapper_->DeviceNameGetHint(*hint_iter, kIoHintName)); - if (io != NULL && strcmp(kOutputDevice, io.get()) == 0) + if (io != NULL && strcmp(unwanted_device_type, io.get()) == 0) continue; - // Found an input 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 - // duplicate counting here since it is only done if the list is still empty. - // Note, pulse has exclusively opened the default device, so we must open - // the device via the "default" moniker. + // 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 duplicate counting here since it is only done if the list is + // still empty. Note, pulse has exclusively opened the default + // device, so we must open the device via the "default" moniker. if (device_names->empty()) { device_names->push_front(media::AudioDeviceName( AudioManagerBase::kDefaultDeviceName, @@ -170,7 +180,7 @@ void AudioManagerLinux::GetAlsaDevicesInfo( wrapper_->DeviceNameGetHint(*hint_iter, kNameHintName)); // 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. scoped_ptr_malloc<char> desc(wrapper_->DeviceNameGetHint( *hint_iter, kDescriptionHintName)); @@ -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) return false; - // Check if the device is in the list of invalid devices. - for (size_t i = 0; i < arraysize(kInvalidAudioInputDevices); ++i) { - if (strncmp(kInvalidAudioInputDevices[i], device_name, - strlen(kInvalidAudioInputDevices[i])) == 0) - return false; + // We do prefix matches on the device name to see whether to include + // it or not. + if (type == kStreamCapture) { + // Check if the device is in the list of invalid devices. + 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 kIoHintName[] = "IOID"; - const char* kNotWantedDevice = - (stream == kStreamPlayback ? "Input" : "Output"); void** hints = NULL; bool has_device = false; int card = -1; @@ -230,7 +261,8 @@ bool AudioManagerLinux::HasAnyAlsaAudioDevice(StreamType stream) { // "Input", "Output", and NULL which means both input and output. scoped_ptr_malloc<char> io(wrapper_->DeviceNameGetHint(*hint_iter, 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. // Found an input device. @@ -258,7 +290,9 @@ AudioOutputStream* AudioManagerLinux::MakeLinearOutputStream( AudioOutputStream* AudioManagerLinux::MakeLowLatencyOutputStream( const AudioParameters& params, + const std::string& device_id, const std::string& input_device_id) { + DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!"; DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format()); // TODO(xians): Use input_device_id for unified IO. return MakeOutputStream(params); @@ -277,7 +311,10 @@ AudioInputStream* AudioManagerLinux::MakeLowLatencyInputStream( } AudioParameters AudioManagerLinux::GetPreferredOutputStreamParameters( + const std::string& output_device_id, const AudioParameters& input_params) { + // TODO(tommi): Support |output_device_id|. + DLOG_IF(ERROR, !output_device_id.empty()) << "Not implemented!"; static const int kDefaultOutputBufferSize = 2048; ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO; int sample_rate = kDefaultSampleRate; diff --git a/media/audio/linux/audio_manager_linux.h b/media/audio/linux/audio_manager_linux.h index 28abaa116e..2258e81eb9 100644 --- a/media/audio/linux/audio_manager_linux.h +++ b/media/audio/linux/audio_manager_linux.h @@ -27,6 +27,8 @@ class MEDIA_EXPORT AudioManagerLinux : public AudioManagerBase { virtual void ShowAudioInputSettings() OVERRIDE; virtual void GetAudioInputDeviceNames(media::AudioDeviceNames* device_names) OVERRIDE; + virtual void GetAudioOutputDeviceNames(media::AudioDeviceNames* device_names) + OVERRIDE; virtual AudioParameters GetInputStreamParameters( const std::string& device_id) OVERRIDE; @@ -35,6 +37,7 @@ class MEDIA_EXPORT AudioManagerLinux : public AudioManagerBase { const AudioParameters& params) OVERRIDE; virtual AudioOutputStream* MakeLowLatencyOutputStream( const AudioParameters& params, + const std::string& device_id, const std::string& input_device_id) OVERRIDE; virtual AudioInputStream* MakeLinearInputStream( const AudioParameters& params, const std::string& device_id) OVERRIDE; @@ -45,6 +48,7 @@ class MEDIA_EXPORT AudioManagerLinux : public AudioManagerBase { virtual ~AudioManagerLinux(); virtual AudioParameters GetPreferredOutputStreamParameters( + const std::string& output_device_id, const AudioParameters& input_params) OVERRIDE; private: @@ -53,14 +57,22 @@ class MEDIA_EXPORT AudioManagerLinux : public AudioManagerBase { kStreamCapture, }; - // Gets a list of available ALSA input devices. - void GetAlsaAudioInputDevices(media::AudioDeviceNames* device_names); + // Gets a list of available ALSA devices. + void GetAlsaAudioDevices(StreamType type, + media::AudioDeviceNames* device_names); - // Gets the ALSA devices' names and ids. - void GetAlsaDevicesInfo(void** hint, media::AudioDeviceNames* device_names); + // Gets the ALSA devices' names and ids that support streams of the + // given type. + void GetAlsaDevicesInfo(StreamType type, + void** hint, + media::AudioDeviceNames* device_names); // 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. bool HasAnyAlsaAudioDevice(StreamType stream); diff --git a/media/audio/mac/audio_auhal_mac_unittest.cc b/media/audio/mac/audio_auhal_mac_unittest.cc index b4cf8c64cc..9b699ff10f 100644 --- a/media/audio/mac/audio_auhal_mac_unittest.cc +++ b/media/audio/mac/audio_auhal_mac_unittest.cc @@ -101,7 +101,7 @@ class AudioOutputStreamWrapper { samples_per_packet_); AudioOutputStream* aos = audio_man_->MakeAudioOutputStream(params, - std::string()); + std::string(), std::string()); EXPECT_TRUE(aos); return aos; } diff --git a/media/audio/mac/audio_input_mac.cc b/media/audio/mac/audio_input_mac.cc index 06af6d11c1..7930567fd9 100644 --- a/media/audio/mac/audio_input_mac.cc +++ b/media/audio/mac/audio_input_mac.cc @@ -4,15 +4,14 @@ #include "media/audio/mac/audio_input_mac.h" +#include <CoreServices/CoreServices.h> + #include "base/basictypes.h" #include "base/logging.h" #include "base/mac/mac_logging.h" #include "media/audio/audio_manager_base.h" #include "media/audio/audio_util.h" -#if !defined(OS_IOS) -#include <CoreServices/CoreServices.h> -#endif namespace media { diff --git a/media/audio/mac/audio_low_latency_input_mac.cc b/media/audio/mac/audio_low_latency_input_mac.cc index 17a87b0a7d..d97f453ca9 100644 --- a/media/audio/mac/audio_low_latency_input_mac.cc +++ b/media/audio/mac/audio_low_latency_input_mac.cc @@ -35,7 +35,9 @@ static std::ostream& operator<<(std::ostream& os, // for more details and background regarding this implementation. AUAudioInputStream::AUAudioInputStream( - AudioManagerMac* manager, const AudioParameters& params, + AudioManagerMac* manager, + const AudioParameters& input_params, + const AudioParameters& output_params, AudioDeviceID audio_device_id) : manager_(manager), sink_(NULL), @@ -48,15 +50,15 @@ AUAudioInputStream::AUAudioInputStream( DCHECK(manager_); // Set up the desired (output) format specified by the client. - format_.mSampleRate = params.sample_rate(); + format_.mSampleRate = input_params.sample_rate(); format_.mFormatID = kAudioFormatLinearPCM; format_.mFormatFlags = kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsSignedInteger; - format_.mBitsPerChannel = params.bits_per_sample(); - format_.mChannelsPerFrame = params.channels(); + format_.mBitsPerChannel = input_params.bits_per_sample(); + format_.mChannelsPerFrame = input_params.channels(); format_.mFramesPerPacket = 1; // uncompressed audio format_.mBytesPerPacket = (format_.mBitsPerChannel * - params.channels()) / 8; + input_params.channels()) / 8; format_.mBytesPerFrame = format_.mBytesPerPacket; format_.mReserved = 0; @@ -68,10 +70,7 @@ AUAudioInputStream::AUAudioInputStream( // Note that we use the same native buffer size as for the output side here // since the AUHAL implementation requires that both capture and render side // use the same buffer size. See http://crbug.com/154352 for more details. - // TODO(xians): Get the audio parameters from the right device. - const AudioParameters parameters = - manager_->GetInputStreamParameters(AudioManagerBase::kDefaultDeviceId); - number_of_frames_ = parameters.frames_per_buffer(); + number_of_frames_ = output_params.frames_per_buffer(); DVLOG(1) << "Size of data buffer in frames : " << number_of_frames_; // Derive size (in bytes) of the buffers that we will render to. @@ -85,7 +84,7 @@ AUAudioInputStream::AUAudioInputStream( audio_buffer_list_.mNumberBuffers = 1; AudioBuffer* audio_buffer = audio_buffer_list_.mBuffers; - audio_buffer->mNumberChannels = params.channels(); + audio_buffer->mNumberChannels = input_params.channels(); audio_buffer->mDataByteSize = data_byte_size; audio_buffer->mData = audio_data_buffer_.get(); @@ -93,9 +92,16 @@ AUAudioInputStream::AUAudioInputStream( // until a requested size is ready to be sent to the client. // It is not possible to ask for less than |kAudioFramesPerCallback| number of // audio frames. - const size_t requested_size_frames = - params.GetBytesPerBuffer() / format_.mBytesPerPacket; - DCHECK_GE(requested_size_frames, number_of_frames_); + size_t requested_size_frames = + input_params.GetBytesPerBuffer() / format_.mBytesPerPacket; + if (requested_size_frames < number_of_frames_) { + // For devices that only support a low sample rate like 8kHz, we adjust the + // buffer size to match number_of_frames_. The value of number_of_frames_ + // in this case has not been calculated based on hardware settings but + // rather our hardcoded defaults (see ChooseBufferSize). + requested_size_frames = number_of_frames_; + } + requested_size_bytes_ = requested_size_frames * format_.mBytesPerFrame; DVLOG(1) << "Requested buffer size in bytes : " << requested_size_bytes_; DLOG_IF(INFO, requested_size_frames > number_of_frames_) << "FIFO is used"; diff --git a/media/audio/mac/audio_low_latency_input_mac.h b/media/audio/mac/audio_low_latency_input_mac.h index 736bf082f5..04592d2cec 100644 --- a/media/audio/mac/audio_low_latency_input_mac.h +++ b/media/audio/mac/audio_low_latency_input_mac.h @@ -57,7 +57,8 @@ class AUAudioInputStream : public AgcAudioStream<AudioInputStream> { // The ctor takes all the usual parameters, plus |manager| which is the // the audio manager who is creating this object. AUAudioInputStream(AudioManagerMac* manager, - const AudioParameters& params, + const AudioParameters& input_params, + const AudioParameters& output_params, AudioDeviceID audio_device_id); // The dtor is typically called by the AudioManager only and it is usually // triggered by calling AudioInputStream::Close(). diff --git a/media/audio/mac/audio_manager_mac.cc b/media/audio/mac/audio_manager_mac.cc index c0c18ee2cc..8e4b969854 100644 --- a/media/audio/mac/audio_manager_mac.cc +++ b/media/audio/mac/audio_manager_mac.cc @@ -81,11 +81,10 @@ bool AudioManagerMac::HasUnifiedDefaultIO() { return input_id == output_id; } +// Retrieves information on audio devices, and prepends the default +// device to the list if the list is non-empty. static void GetAudioDeviceInfo(bool is_input, media::AudioDeviceNames* device_names) { - DCHECK(device_names); - device_names->clear(); - // Query the number of total devices. AudioObjectPropertyAddress property_address = { kAudioHardwarePropertyDevices, @@ -176,6 +175,16 @@ static void GetAudioDeviceInfo(bool is_input, if (name) CFRelease(name); } + + if (!device_names->empty()) { + // Prepend the default device to the list since we always want it to be + // on the top of the list for all platforms. There is no duplicate + // counting here since the default device has been abstracted out before. + media::AudioDeviceName name; + name.device_name = AudioManagerBase::kDefaultDeviceName; + name.unique_id = AudioManagerBase::kDefaultDeviceId; + device_names->push_front(name); + } } static AudioDeviceID GetAudioDeviceIdByUId(bool is_input, @@ -189,7 +198,7 @@ static AudioDeviceID GetAudioDeviceIdByUId(bool is_input, UInt32 device_size = sizeof(audio_device_id); OSStatus result = -1; - if (device_id == AudioManagerBase::kDefaultDeviceId) { + if (device_id == AudioManagerBase::kDefaultDeviceId || device_id.empty()) { // Default Device. property_address.mSelector = is_input ? kAudioHardwarePropertyDefaultInputDevice : @@ -263,7 +272,7 @@ bool AudioManagerMac::HasAudioInputDevices() { return HasAudioHardware(kAudioHardwarePropertyDefaultInputDevice); } -// TODO(crogers): There are several places on the OSX specific code which +// TODO(xians): There are several places on the OSX specific code which // could benefit from these helper functions. bool AudioManagerMac::GetDefaultInputDevice( AudioDeviceID* device) { @@ -397,16 +406,14 @@ int AudioManagerMac::HardwareSampleRate() { void AudioManagerMac::GetAudioInputDeviceNames( media::AudioDeviceNames* device_names) { + DCHECK(device_names->empty()); GetAudioDeviceInfo(true, device_names); - if (!device_names->empty()) { - // Prepend the default device to the list since we always want it to be - // on the top of the list for all platforms. There is no duplicate - // counting here since the default device has been abstracted out before. - media::AudioDeviceName name; - name.device_name = AudioManagerBase::kDefaultDeviceName; - name.unique_id = AudioManagerBase::kDefaultDeviceId; - device_names->push_front(name); - } +} + +void AudioManagerMac::GetAudioOutputDeviceNames( + media::AudioDeviceNames* device_names) { + DCHECK(device_names->empty()); + GetAudioDeviceInfo(false, device_names); } AudioParameters AudioManagerMac::GetInputStreamParameters( @@ -443,21 +450,86 @@ AudioParameters AudioManagerMac::GetInputStreamParameters( sample_rate, 16, buffer_size); } +std::string AudioManagerMac::GetAssociatedOutputDeviceID( + const std::string& input_device_id) { + AudioDeviceID device = GetAudioDeviceIdByUId(true, input_device_id); + if (device == kAudioObjectUnknown) + return std::string(); + + UInt32 size = 0; + AudioObjectPropertyAddress pa = { + kAudioDevicePropertyRelatedDevices, + kAudioDevicePropertyScopeOutput, + kAudioObjectPropertyElementMaster + }; + OSStatus result = AudioObjectGetPropertyDataSize(device, &pa, 0, 0, &size); + if (result || !size) + return std::string(); + + int device_count = size / sizeof(AudioDeviceID); + scoped_ptr_malloc<AudioDeviceID> + devices(reinterpret_cast<AudioDeviceID*>(malloc(size))); + result = AudioObjectGetPropertyData( + device, &pa, 0, NULL, &size, devices.get()); + if (result) + return std::string(); + + for (int i = 0; i < device_count; ++i) { + // Get the number of output channels of the device. + pa.mSelector = kAudioDevicePropertyStreams; + size = 0; + result = AudioObjectGetPropertyDataSize(devices.get()[i], + &pa, + 0, + NULL, + &size); + if (result || !size) + continue; // Skip if there aren't any output channels. + + // Get device UID. + CFStringRef uid = NULL; + size = sizeof(uid); + pa.mSelector = kAudioDevicePropertyDeviceUID; + result = AudioObjectGetPropertyData(devices.get()[i], + &pa, + 0, + NULL, + &size, + &uid); + if (result || !uid) + continue; + + std::string ret(base::SysCFStringRefToUTF8(uid)); + CFRelease(uid); + return ret; + } + + // No matching device found. + return std::string(); +} + AudioOutputStream* AudioManagerMac::MakeLinearOutputStream( const AudioParameters& params) { - return MakeLowLatencyOutputStream(params, std::string()); + return MakeLowLatencyOutputStream(params, std::string(), std::string()); } AudioOutputStream* AudioManagerMac::MakeLowLatencyOutputStream( - const AudioParameters& params, const std::string& input_device_id) { + const AudioParameters& params, + const std::string& device_id, + const std::string& input_device_id) { // Handle basic output with no input channels. if (params.input_channels() == 0) { - AudioDeviceID device = kAudioObjectUnknown; - GetDefaultOutputDevice(&device); + AudioDeviceID device = GetAudioDeviceIdByUId(false, device_id); + if (device == kAudioObjectUnknown) { + DLOG(ERROR) << "Failed to open output device: " << device_id; + return NULL; + } return new AUHALStream(this, params, device); } - // TODO(crogers): support more than stereo input. + DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!"; + + // TODO(xians): support more than stereo input. if (params.input_channels() != 2) { // WebAudio is currently hard-coded to 2 channels so we should not // see this case. @@ -494,7 +566,7 @@ AudioOutputStream* AudioManagerMac::MakeLowLatencyOutputStream( // different and arbitrary combinations of input and output devices // even running at different sample-rates. // kAudioDeviceUnknown translates to "use default" here. - // TODO(crogers): consider tracking UMA stats on AUHALStream + // TODO(xians): consider tracking UMA stats on AUHALStream // versus AudioSynchronizedStream. AudioDeviceID audio_device_id = GetAudioDeviceIdByUId(true, input_device_id); if (audio_device_id == kAudioObjectUnknown) @@ -506,6 +578,33 @@ AudioOutputStream* AudioManagerMac::MakeLowLatencyOutputStream( kAudioDeviceUnknown); } +std::string AudioManagerMac::GetDefaultOutputDeviceID() { + AudioDeviceID device_id = kAudioObjectUnknown; + if (!GetDefaultOutputDevice(&device_id)) + return std::string(); + + const AudioObjectPropertyAddress property_address = { + kAudioDevicePropertyDeviceUID, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster + }; + CFStringRef device_uid = NULL; + UInt32 size = sizeof(device_uid); + OSStatus status = AudioObjectGetPropertyData(device_id, + &property_address, + 0, + NULL, + &size, + &device_uid); + if (status != kAudioHardwareNoError || !device_uid) + return std::string(); + + std::string ret(base::SysCFStringRefToUTF8(device_uid)); + CFRelease(device_uid); + + return ret; +} + AudioInputStream* AudioManagerMac::MakeLinearInputStream( const AudioParameters& params, const std::string& device_id) { DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format()); @@ -515,27 +614,47 @@ AudioInputStream* AudioManagerMac::MakeLinearInputStream( AudioInputStream* AudioManagerMac::MakeLowLatencyInputStream( const AudioParameters& params, const std::string& device_id) { DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format()); - // Gets the AudioDeviceID that refers to the AudioOutputDevice with the device + // Gets the AudioDeviceID that refers to the AudioInputDevice with the device // unique id. This AudioDeviceID is used to set the device for Audio Unit. AudioDeviceID audio_device_id = GetAudioDeviceIdByUId(true, device_id); AudioInputStream* stream = NULL; - if (audio_device_id != kAudioObjectUnknown) - stream = new AUAudioInputStream(this, params, audio_device_id); + if (audio_device_id != kAudioObjectUnknown) { + // AUAudioInputStream needs to be fed the preferred audio output parameters + // of the matching device so that the buffer size of both input and output + // can be matched. See constructor of AUAudioInputStream for more. + const std::string associated_output_device( + GetAssociatedOutputDeviceID(device_id)); + const AudioParameters output_params = + GetPreferredOutputStreamParameters( + associated_output_device.empty() ? + AudioManagerBase::kDefaultDeviceId : associated_output_device, + params); + stream = new AUAudioInputStream(this, params, output_params, + audio_device_id); + } return stream; } AudioParameters AudioManagerMac::GetPreferredOutputStreamParameters( + const std::string& output_device_id, const AudioParameters& input_params) { + AudioDeviceID device = GetAudioDeviceIdByUId(false, output_device_id); + if (device == kAudioObjectUnknown) { + DLOG(ERROR) << "Invalid output device " << output_device_id; + return AudioParameters(); + } + int hardware_channels = 2; - if (!GetDefaultOutputChannels(&hardware_channels)) { + if (!GetDeviceChannels(device, kAudioDevicePropertyScopeOutput, + &hardware_channels)) { // Fallback to stereo. hardware_channels = 2; } ChannelLayout channel_layout = GuessChannelLayout(hardware_channels); - const int hardware_sample_rate = AUAudioOutputStream::HardwareSampleRate(); + const int hardware_sample_rate = HardwareSampleRateForDevice(device); const int buffer_size = ChooseBufferSize(hardware_sample_rate); int input_channels = 0; @@ -543,7 +662,7 @@ AudioParameters AudioManagerMac::GetPreferredOutputStreamParameters( input_channels = input_params.input_channels(); if (input_channels > 0) { - // TODO(crogers): given the limitations of the AudioOutputStream + // TODO(xians): given the limitations of the AudioOutputStream // back-ends used with synchronized I/O, we hard-code to stereo. // Specifically, this is a limitation of AudioSynchronizedStream which // can be removed as part of the work to consolidate these back-ends. diff --git a/media/audio/mac/audio_manager_mac.h b/media/audio/mac/audio_manager_mac.h index cd3cc2e94b..9757315920 100644 --- a/media/audio/mac/audio_manager_mac.h +++ b/media/audio/mac/audio_manager_mac.h @@ -29,19 +29,25 @@ class MEDIA_EXPORT AudioManagerMac : public AudioManagerBase { virtual bool HasAudioInputDevices() OVERRIDE; virtual void GetAudioInputDeviceNames(media::AudioDeviceNames* device_names) OVERRIDE; + virtual void GetAudioOutputDeviceNames(media::AudioDeviceNames* device_names) + OVERRIDE; virtual AudioParameters GetInputStreamParameters( const std::string& device_id) OVERRIDE; + virtual std::string GetAssociatedOutputDeviceID( + const std::string& input_device_id) OVERRIDE; // Implementation of AudioManagerBase. virtual AudioOutputStream* MakeLinearOutputStream( const AudioParameters& params) OVERRIDE; virtual AudioOutputStream* MakeLowLatencyOutputStream( const AudioParameters& params, + const std::string& device_id, const std::string& input_device_id) OVERRIDE; virtual AudioInputStream* MakeLinearInputStream( const AudioParameters& params, const std::string& device_id) OVERRIDE; virtual AudioInputStream* MakeLowLatencyInputStream( const AudioParameters& params, const std::string& device_id) OVERRIDE; + virtual std::string GetDefaultOutputDeviceID() OVERRIDE; static bool GetDefaultInputDevice(AudioDeviceID* device); static bool GetDefaultOutputDevice(AudioDeviceID* device); @@ -64,6 +70,7 @@ class MEDIA_EXPORT AudioManagerMac : public AudioManagerBase { virtual ~AudioManagerMac(); virtual AudioParameters GetPreferredOutputStreamParameters( + const std::string& output_device_id, const AudioParameters& input_params) OVERRIDE; private: diff --git a/media/audio/mock_audio_manager.cc b/media/audio/mock_audio_manager.cc index 60898bd61b..2ab2b708da 100644 --- a/media/audio/mock_audio_manager.cc +++ b/media/audio/mock_audio_manager.cc @@ -36,15 +36,21 @@ void MockAudioManager::GetAudioInputDeviceNames( media::AudioDeviceNames* device_names) { } +void MockAudioManager::GetAudioOutputDeviceNames( + media::AudioDeviceNames* device_names) { +} + media::AudioOutputStream* MockAudioManager::MakeAudioOutputStream( - const media::AudioParameters& params, - const std::string& input_device_id) { + const media::AudioParameters& params, + const std::string& device_id, + const std::string& input_device_id) { NOTREACHED(); return NULL; } media::AudioOutputStream* MockAudioManager::MakeAudioOutputStreamProxy( const media::AudioParameters& params, + const std::string& device_id, const std::string& input_device_id) { NOTREACHED(); return NULL; @@ -77,9 +83,19 @@ AudioParameters MockAudioManager::GetDefaultOutputStreamParameters() { return AudioParameters(); } +AudioParameters MockAudioManager::GetOutputStreamParameters( + const std::string& device_id) { + return AudioParameters(); +} + AudioParameters MockAudioManager::GetInputStreamParameters( const std::string& device_id) { return AudioParameters(); } +std::string MockAudioManager::GetAssociatedOutputDeviceID( + const std::string& input_device_id) { + return std::string(); +} + } // namespace media. diff --git a/media/audio/mock_audio_manager.h b/media/audio/mock_audio_manager.h index eee84b1643..7bc30f578e 100644 --- a/media/audio/mock_audio_manager.h +++ b/media/audio/mock_audio_manager.h @@ -34,12 +34,17 @@ class MockAudioManager : public media::AudioManager { virtual void GetAudioInputDeviceNames( media::AudioDeviceNames* device_names) OVERRIDE; + virtual void GetAudioOutputDeviceNames( + media::AudioDeviceNames* device_names) OVERRIDE; + virtual media::AudioOutputStream* MakeAudioOutputStream( const media::AudioParameters& params, + const std::string& device_id, const std::string& input_device_id) OVERRIDE; virtual media::AudioOutputStream* MakeAudioOutputStreamProxy( const media::AudioParameters& params, + const std::string& device_id, const std::string& input_device_id) OVERRIDE; virtual media::AudioInputStream* MakeAudioInputStream( @@ -55,8 +60,12 @@ class MockAudioManager : public media::AudioManager { AudioDeviceListener* listener) OVERRIDE; virtual AudioParameters GetDefaultOutputStreamParameters() OVERRIDE; + virtual AudioParameters GetOutputStreamParameters( + const std::string& device_id) OVERRIDE; virtual AudioParameters GetInputStreamParameters( const std::string& device_id) OVERRIDE; + virtual std::string GetAssociatedOutputDeviceID( + const std::string& input_device_id) OVERRIDE; private: virtual ~MockAudioManager(); diff --git a/media/audio/openbsd/audio_manager_openbsd.cc b/media/audio/openbsd/audio_manager_openbsd.cc index 4005aeb98f..a97ea8f625 100644 --- a/media/audio/openbsd/audio_manager_openbsd.cc +++ b/media/audio/openbsd/audio_manager_openbsd.cc @@ -92,7 +92,9 @@ AudioOutputStream* AudioManagerOpenBSD::MakeLinearOutputStream( AudioOutputStream* AudioManagerOpenBSD::MakeLowLatencyOutputStream( const AudioParameters& params, + const std::string& device_id, const std::string& input_device_id) { + DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!"; DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format); return MakeOutputStream(params); } @@ -112,7 +114,10 @@ AudioInputStream* AudioManagerOpenBSD::MakeLowLatencyInputStream( } AudioParameters AudioManagerOpenBSD::GetPreferredOutputStreamParameters( + const std::string& output_device_id, const AudioParameters& input_params) { + // TODO(tommi): Support |output_device_id|. + DLOG_IF(ERROR, !output_device_id.empty()) << "Not implemented!"; static const int kDefaultOutputBufferSize = 512; ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO; diff --git a/media/audio/openbsd/audio_manager_openbsd.h b/media/audio/openbsd/audio_manager_openbsd.h index a1adcb6c86..e4bb3948d2 100644 --- a/media/audio/openbsd/audio_manager_openbsd.h +++ b/media/audio/openbsd/audio_manager_openbsd.h @@ -27,6 +27,7 @@ class MEDIA_EXPORT AudioManagerOpenBSD : public AudioManagerBase { const AudioParameters& params) OVERRIDE; virtual AudioOutputStream* MakeLowLatencyOutputStream( const AudioParameters& params, + const std::string& device_id, const std::string& input_device_id) OVERRIDE; virtual AudioInputStream* MakeLinearInputStream( const AudioParameters& params, const std::string& device_id) OVERRIDE; @@ -37,6 +38,7 @@ class MEDIA_EXPORT AudioManagerOpenBSD : public AudioManagerBase { virtual ~AudioManagerOpenBSD(); virtual AudioParameters GetPreferredOutputStreamParameters( + const std::string& output_device_id, const AudioParameters& input_params) OVERRIDE; private: diff --git a/media/audio/pulse/audio_manager_pulse.cc b/media/audio/pulse/audio_manager_pulse.cc index dcdd328222..5c09f14905 100644 --- a/media/audio/pulse/audio_manager_pulse.cc +++ b/media/audio/pulse/audio_manager_pulse.cc @@ -66,19 +66,13 @@ AudioManagerPulse::~AudioManagerPulse() { // Implementation of AudioManager. bool AudioManagerPulse::HasAudioOutputDevices() { - DCHECK(input_mainloop_); - DCHECK(input_context_); - media::AudioDeviceNames devices; - AutoPulseLock auto_lock(input_mainloop_); - devices_ = &devices; - pa_operation* operation = pa_context_get_sink_info_list( - input_context_, OutputDevicesInfoCallback, this); - WaitForOperationCompletion(input_mainloop_, operation); + AudioDeviceNames devices; + GetAudioOutputDeviceNames(&devices); return !devices.empty(); } bool AudioManagerPulse::HasAudioInputDevices() { - media::AudioDeviceNames devices; + AudioDeviceNames devices; GetAudioInputDeviceNames(&devices); return !devices.empty(); } @@ -87,18 +81,24 @@ void AudioManagerPulse::ShowAudioInputSettings() { AudioManagerLinux::ShowLinuxAudioInputSettings(); } -void AudioManagerPulse::GetAudioInputDeviceNames( - media::AudioDeviceNames* device_names) { +void AudioManagerPulse::GetAudioDeviceNames( + bool input, media::AudioDeviceNames* device_names) { DCHECK(device_names->empty()); DCHECK(input_mainloop_); DCHECK(input_context_); AutoPulseLock auto_lock(input_mainloop_); devices_ = device_names; - pa_operation* operation = pa_context_get_source_info_list( + pa_operation* operation = NULL; + if (input) { + operation = pa_context_get_source_info_list( input_context_, InputDevicesInfoCallback, this); + } else { + operation = pa_context_get_sink_info_list( + input_context_, OutputDevicesInfoCallback, this); + } WaitForOperationCompletion(input_mainloop_, operation); - // Append the default device on the top of the list if the list is not empty. + // Prepend the default device if the list is not empty. if (!device_names->empty()) { device_names->push_front( AudioDeviceName(AudioManagerBase::kDefaultDeviceName, @@ -106,6 +106,16 @@ void AudioManagerPulse::GetAudioInputDeviceNames( } } +void AudioManagerPulse::GetAudioInputDeviceNames( + AudioDeviceNames* device_names) { + GetAudioDeviceNames(true, device_names); +} + +void AudioManagerPulse::GetAudioOutputDeviceNames( + AudioDeviceNames* device_names) { + GetAudioDeviceNames(false, device_names); +} + AudioParameters AudioManagerPulse::GetInputStreamParameters( const std::string& device_id) { static const int kDefaultInputBufferSize = 1024; @@ -123,7 +133,10 @@ AudioOutputStream* AudioManagerPulse::MakeLinearOutputStream( } AudioOutputStream* AudioManagerPulse::MakeLowLatencyOutputStream( - const AudioParameters& params, const std::string& input_device_id) { + const AudioParameters& params, + const std::string& device_id, + const std::string& input_device_id) { + DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!"; DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format()); return MakeOutputStream(params, input_device_id); } @@ -141,7 +154,10 @@ AudioInputStream* AudioManagerPulse::MakeLowLatencyInputStream( } AudioParameters AudioManagerPulse::GetPreferredOutputStreamParameters( + const std::string& output_device_id, const AudioParameters& input_params) { + // TODO(tommi): Support |output_device_id|. + DLOG_IF(ERROR, !output_device_id.empty()) << "Not implemented!"; static const int kDefaultOutputBufferSize = 512; ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO; @@ -286,8 +302,8 @@ void AudioManagerPulse::InputDevicesInfoCallback(pa_context* context, // Exclude the output devices. if (info->monitor_of_sink == PA_INVALID_INDEX) { - manager->devices_->push_back(media::AudioDeviceName(info->description, - info->name)); + manager->devices_->push_back(AudioDeviceName(info->description, + info->name)); } } @@ -302,8 +318,8 @@ void AudioManagerPulse::OutputDevicesInfoCallback(pa_context* context, return; } - manager->devices_->push_back(media::AudioDeviceName(info->description, - info->name)); + manager->devices_->push_back(AudioDeviceName(info->description, + info->name)); } void AudioManagerPulse::SampleRateInfoCallback(pa_context* context, diff --git a/media/audio/pulse/audio_manager_pulse.h b/media/audio/pulse/audio_manager_pulse.h index 6dfebaeff3..8fc4310cba 100644 --- a/media/audio/pulse/audio_manager_pulse.h +++ b/media/audio/pulse/audio_manager_pulse.h @@ -27,6 +27,8 @@ class MEDIA_EXPORT AudioManagerPulse : public AudioManagerBase { virtual void ShowAudioInputSettings() OVERRIDE; virtual void GetAudioInputDeviceNames(media::AudioDeviceNames* device_names) OVERRIDE; + virtual void GetAudioOutputDeviceNames(media::AudioDeviceNames* device_names) + OVERRIDE; virtual AudioParameters GetInputStreamParameters( const std::string& device_id) OVERRIDE; @@ -35,6 +37,7 @@ class MEDIA_EXPORT AudioManagerPulse : public AudioManagerBase { const AudioParameters& params) OVERRIDE; virtual AudioOutputStream* MakeLowLatencyOutputStream( const AudioParameters& params, + const std::string& device_id, const std::string& input_device_id) OVERRIDE; virtual AudioInputStream* MakeLinearInputStream( const AudioParameters& params, const std::string& device_id) OVERRIDE; @@ -43,12 +46,15 @@ class MEDIA_EXPORT AudioManagerPulse : public AudioManagerBase { protected: virtual AudioParameters GetPreferredOutputStreamParameters( + const std::string& output_device_id, const AudioParameters& input_params) OVERRIDE; private: bool Init(); void DestroyPulse(); + void GetAudioDeviceNames(bool input, media::AudioDeviceNames* device_names); + // Callback to get the devices' info like names, used by GetInputDevices(). static void InputDevicesInfoCallback(pa_context* context, const pa_source_info* info, diff --git a/media/audio/win/audio_low_latency_output_win.cc b/media/audio/win/audio_low_latency_output_win.cc index b2098b0209..c889c03ef2 100644 --- a/media/audio/win/audio_low_latency_output_win.cc +++ b/media/audio/win/audio_low_latency_output_win.cc @@ -111,14 +111,26 @@ ChannelLayout WASAPIAudioOutputStream::HardwareChannelLayout() { } // static -int WASAPIAudioOutputStream::HardwareSampleRate() { +int WASAPIAudioOutputStream::HardwareSampleRate(const std::string& device_id) { WAVEFORMATPCMEX format; - return SUCCEEDED(CoreAudioUtil::GetDefaultSharedModeMixFormat( - eRender, eConsole, &format)) ? - static_cast<int>(format.Format.nSamplesPerSec) : 0; + ScopedComPtr<IAudioClient> client; + if (device_id.empty()) { + client = CoreAudioUtil::CreateDefaultClient(eRender, eConsole); + } else { + ScopedComPtr<IMMDevice> device(CoreAudioUtil::CreateDevice(device_id)); + if (!device) + return 0; + client = CoreAudioUtil::CreateClient(device); + } + + if (!client || FAILED(CoreAudioUtil::GetSharedModeMixFormat(client, &format))) + return 0; + + return static_cast<int>(format.Format.nSamplesPerSec); } WASAPIAudioOutputStream::WASAPIAudioOutputStream(AudioManagerWin* manager, + const std::string& device_id, const AudioParameters& params, ERole device_role) : creating_thread_id_(base::PlatformThread::CurrentId()), @@ -127,6 +139,7 @@ WASAPIAudioOutputStream::WASAPIAudioOutputStream(AudioManagerWin* manager, audio_parameters_are_valid_(false), volume_(1.0), endpoint_buffer_size_frames_(0), + device_id_(device_id), device_role_(device_role), share_mode_(GetShareMode()), num_written_frames_(0), @@ -142,12 +155,16 @@ WASAPIAudioOutputStream::WASAPIAudioOutputStream(AudioManagerWin* manager, // channel count are excluded) to the preferred (native) audio parameters. // Open() will fail if this is not the case. AudioParameters preferred_params; - HRESULT hr = CoreAudioUtil::GetPreferredAudioParameters( - eRender, device_role, &preferred_params); + HRESULT hr = device_id_.empty() ? + CoreAudioUtil::GetPreferredAudioParameters(eRender, device_role, + &preferred_params) : + CoreAudioUtil::GetPreferredAudioParameters(device_id_, + &preferred_params); audio_parameters_are_valid_ = SUCCEEDED(hr) && CompareAudioParametersNoBitDepthOrChannels(params, preferred_params); LOG_IF(WARNING, !audio_parameters_are_valid_) - << "Input and preferred parameters are not identical."; + << "Input and preferred parameters are not identical. " + << "Device id: " << device_id_; } // Load the Avrt DLL if not already loaded. Required to support MMCSS. @@ -203,7 +220,6 @@ bool WASAPIAudioOutputStream::Open() { if (opened_) return true; - // Audio parameters must be identical to the preferred set of parameters // if shared mode (default) is utilized. if (share_mode_ == AUDCLNT_SHAREMODE_SHARED) { @@ -214,8 +230,16 @@ bool WASAPIAudioOutputStream::Open() { } // Create an IAudioClient interface for the default rendering IMMDevice. - ScopedComPtr<IAudioClient> audio_client = - CoreAudioUtil::CreateDefaultClient(eRender, device_role_); + ScopedComPtr<IAudioClient> audio_client; + if (device_id_.empty()) { + audio_client = CoreAudioUtil::CreateDefaultClient(eRender, device_role_); + } else { + ScopedComPtr<IMMDevice> device(CoreAudioUtil::CreateDevice(device_id_)); + DLOG_IF(ERROR, !device) << "Failed to open device: " << device_id_; + if (device) + audio_client = CoreAudioUtil::CreateClient(device); + } + if (!audio_client) return false; diff --git a/media/audio/win/audio_low_latency_output_win.h b/media/audio/win/audio_low_latency_output_win.h index b0e990bb1a..7884d8840f 100644 --- a/media/audio/win/audio_low_latency_output_win.h +++ b/media/audio/win/audio_low_latency_output_win.h @@ -122,6 +122,7 @@ class MEDIA_EXPORT WASAPIAudioOutputStream : // The ctor takes all the usual parameters, plus |manager| which is the // the audio manager who is creating this object. WASAPIAudioOutputStream(AudioManagerWin* manager, + const std::string& device_id, const AudioParameters& params, ERole device_role); @@ -149,8 +150,9 @@ class MEDIA_EXPORT WASAPIAudioOutputStream : static ChannelLayout HardwareChannelLayout(); // Retrieves the sample rate the audio engine uses for its internal - // processing/mixing of shared-mode streams for the default endpoint device. - static int HardwareSampleRate(); + // processing/mixing of shared-mode streams. To fetch the settings for the + // default device, pass an empty string as the |device_id|. + static int HardwareSampleRate(const std::string& device_id); // Returns AUDCLNT_SHAREMODE_EXCLUSIVE if --enable-exclusive-mode is used // as command-line flag and AUDCLNT_SHAREMODE_SHARED otherwise (default). @@ -219,6 +221,9 @@ class MEDIA_EXPORT WASAPIAudioOutputStream : // Length of the audio endpoint buffer. uint32 endpoint_buffer_size_frames_; + // The target device id or an empty string for the default device. + const std::string device_id_; + // Defines the role that the system has assigned to an audio endpoint device. ERole device_role_; diff --git a/media/audio/win/audio_low_latency_output_win_unittest.cc b/media/audio/win/audio_low_latency_output_win_unittest.cc index 8c3e366c0c..1f78facf91 100644 --- a/media/audio/win/audio_low_latency_output_win_unittest.cc +++ b/media/audio/win/audio_low_latency_output_win_unittest.cc @@ -234,7 +234,7 @@ class AudioOutputStreamWrapper { AudioOutputStream* aos = audio_man_->MakeAudioOutputStream( AudioParameters(format_, channel_layout_, sample_rate_, bits_per_sample_, samples_per_packet_), - std::string()); + std::string(), std::string()); EXPECT_TRUE(aos); return aos; } @@ -268,7 +268,7 @@ TEST(WASAPIAudioOutputStreamTest, HardwareSampleRate) { // Default device intended for games, system notification sounds, // and voice commands. int fs = static_cast<int>( - WASAPIAudioOutputStream::HardwareSampleRate()); + WASAPIAudioOutputStream::HardwareSampleRate(std::string())); EXPECT_GE(fs, 0); } diff --git a/media/audio/win/audio_manager_win.cc b/media/audio/win/audio_manager_win.cc index a753e554cb..e3a95883b2 100644 --- a/media/audio/win/audio_manager_win.cc +++ b/media/audio/win/audio_manager_win.cc @@ -240,27 +240,44 @@ void AudioManagerWin::ShowAudioInputSettings() { base::LaunchProcess(command_line, base::LaunchOptions(), NULL); } -void AudioManagerWin::GetAudioInputDeviceNames( - media::AudioDeviceNames* device_names) { +void AudioManagerWin::GetAudioDeviceNamesImpl( + bool input, + AudioDeviceNames* device_names) { + DCHECK(device_names->empty()); DCHECK(enumeration_type() != kUninitializedEnumeration); // Enumerate all active audio-endpoint capture devices. if (enumeration_type() == kWaveEnumeration) { // Utilize the Wave API for Windows XP. - media::GetInputDeviceNamesWinXP(device_names); + if (input) + GetInputDeviceNamesWinXP(device_names); + else + GetOutputDeviceNamesWinXP(device_names); } else { // Utilize the MMDevice API (part of Core Audio) for Vista and higher. - media::GetInputDeviceNamesWin(device_names); + if (input) + GetInputDeviceNamesWin(device_names); + else + GetOutputDeviceNamesWin(device_names); } // Always add default device parameters as first element. if (!device_names->empty()) { - media::AudioDeviceName name; + AudioDeviceName name; name.device_name = AudioManagerBase::kDefaultDeviceName; name.unique_id = AudioManagerBase::kDefaultDeviceId; device_names->push_front(name); } } +void AudioManagerWin::GetAudioInputDeviceNames(AudioDeviceNames* device_names) { + GetAudioDeviceNamesImpl(true, device_names); +} + +void AudioManagerWin::GetAudioOutputDeviceNames( + AudioDeviceNames* device_names) { + GetAudioDeviceNamesImpl(false, device_names); +} + AudioParameters AudioManagerWin::GetInputStreamParameters( const std::string& device_id) { int sample_rate = 48000; @@ -280,6 +297,11 @@ AudioParameters AudioManagerWin::GetInputStreamParameters( sample_rate, 16, kFallbackBufferSize); } +std::string AudioManagerWin::GetAssociatedOutputDeviceID( + const std::string& input_device_id) { + return CoreAudioUtil::GetMatchingOutputDeviceID(input_device_id); +} + // Factory for the implementations of AudioOutputStream for AUDIO_PCM_LINEAR // mode. // - PCMWaveOutAudioOutputStream: Based on the waveOut API. @@ -291,7 +313,7 @@ AudioOutputStream* AudioManagerWin::MakeLinearOutputStream( return new PCMWaveOutAudioOutputStream(this, params, - media::NumberOfWaveOutBuffers(), + NumberOfWaveOutBuffers(), WAVE_MAPPER); } @@ -301,25 +323,31 @@ AudioOutputStream* AudioManagerWin::MakeLinearOutputStream( // - PCMWaveOutAudioOutputStream: Based on the waveOut API. // - WASAPIAudioOutputStream: Based on Core Audio (WASAPI) API. AudioOutputStream* AudioManagerWin::MakeLowLatencyOutputStream( - const AudioParameters& params, const std::string& input_device_id) { + const AudioParameters& params, + const std::string& device_id, + const std::string& input_device_id) { DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format()); if (params.channels() > kWinMaxChannels) return NULL; if (!CoreAudioUtil::IsSupported()) { // Fall back to Windows Wave implementation on Windows XP or lower. + DLOG_IF(ERROR, !device_id.empty()) + << "Opening by device id not supported by PCMWaveOutAudioOutputStream"; DVLOG(1) << "Using WaveOut since WASAPI requires at least Vista."; return new PCMWaveOutAudioOutputStream( - this, params, media::NumberOfWaveOutBuffers(), WAVE_MAPPER); + this, params, NumberOfWaveOutBuffers(), WAVE_MAPPER); } - // TODO(crogers): support more than stereo input. + // TODO(rtoy): support more than stereo input. if (params.input_channels() > 0) { DVLOG(1) << "WASAPIUnifiedStream is created."; + DLOG_IF(ERROR, !device_id.empty()) + << "Opening by device id not supported by WASAPIUnifiedStream"; return new WASAPIUnifiedStream(this, params, input_device_id); } - return new WASAPIAudioOutputStream(this, params, eConsole); + return new WASAPIAudioOutputStream(this, device_id, params, eConsole); } // Factory for the implementations of AudioInputStream for AUDIO_PCM_LINEAR @@ -347,55 +375,68 @@ AudioInputStream* AudioManagerWin::MakeLowLatencyInputStream( return stream; } +std::string AudioManagerWin::GetDefaultOutputDeviceID() { + if (!CoreAudioUtil::IsSupported()) + return std::string(); + return CoreAudioUtil::GetDefaultOutputDeviceID(); +} + AudioParameters AudioManagerWin::GetPreferredOutputStreamParameters( + const std::string& output_device_id, const AudioParameters& input_params) { + const bool core_audio_supported = CoreAudioUtil::IsSupported(); + DLOG_IF(ERROR, !core_audio_supported && !output_device_id.empty()) + << "CoreAudio is required to open non-default devices."; + const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO; int sample_rate = 48000; int buffer_size = kFallbackBufferSize; int bits_per_sample = 16; int input_channels = 0; - bool use_input_params = !CoreAudioUtil::IsSupported(); - if (cmd_line->HasSwitch(switches::kEnableExclusiveAudio)) { - // TODO(crogers): tune these values for best possible WebAudio performance. - // WebRTC works well at 48kHz and a buffer size of 480 samples will be used - // for this case. Note that exclusive mode is experimental. - // This sample rate will be combined with a buffer size of 256 samples, - // which corresponds to an output delay of ~5.33ms. - sample_rate = 48000; - buffer_size = 256; - if (input_params.IsValid()) - channel_layout = input_params.channel_layout(); - } else if (!use_input_params) { - // Hardware sample-rate on Windows can be configured, so we must query. - // TODO(henrika): improve possibility to specify an audio endpoint. - // Use the default device (same as for Wave) for now to be compatible. - int hw_sample_rate = WASAPIAudioOutputStream::HardwareSampleRate(); - - AudioParameters params; - HRESULT hr = CoreAudioUtil::GetPreferredAudioParameters(eRender, eConsole, - ¶ms); - int hw_buffer_size = - FAILED(hr) ? kFallbackBufferSize : params.frames_per_buffer(); - channel_layout = WASAPIAudioOutputStream::HardwareChannelLayout(); - - // TODO(henrika): Figure out the right thing to do here. - if (hw_sample_rate && hw_buffer_size) { - sample_rate = hw_sample_rate; - buffer_size = hw_buffer_size; + bool use_input_params = !core_audio_supported; + if (core_audio_supported) { + if (cmd_line->HasSwitch(switches::kEnableExclusiveAudio)) { + // TODO(rtoy): tune these values for best possible WebAudio + // performance. WebRTC works well at 48kHz and a buffer size of 480 + // samples will be used for this case. Note that exclusive mode is + // experimental. This sample rate will be combined with a buffer size of + // 256 samples, which corresponds to an output delay of ~5.33ms. + sample_rate = 48000; + buffer_size = 256; + if (input_params.IsValid()) + channel_layout = input_params.channel_layout(); } else { - use_input_params = true; + AudioParameters params; + HRESULT hr = CoreAudioUtil::GetPreferredAudioParameters( + output_device_id.empty() ? + GetDefaultOutputDeviceID() : output_device_id, + ¶ms); + if (SUCCEEDED(hr)) { + bits_per_sample = params.bits_per_sample(); + buffer_size = params.frames_per_buffer(); + channel_layout = params.channel_layout(); + sample_rate = params.sample_rate(); + } else { + use_input_params = true; + } } } if (input_params.IsValid()) { - if (cmd_line->HasSwitch(switches::kTrySupportedChannelLayouts) && - CoreAudioUtil::IsSupported()) { + if (core_audio_supported && + cmd_line->HasSwitch(switches::kTrySupportedChannelLayouts)) { // Check if it is possible to open up at the specified input channel // layout but avoid checking if the specified layout is the same as the // hardware (preferred) layout. We do this extra check to avoid the // CoreAudioUtil::IsChannelLayoutSupported() overhead in most cases. if (input_params.channel_layout() != channel_layout) { + // TODO(henrika): Use |output_device_id| here. + // Internally, IsChannelLayoutSupported does many of the operations + // that have already been done such as opening up a client and fetching + // the WAVEFORMATPCMEX format. Ideally we should only do that once and + // do it for the requested device. Then here, we can check the layout + // from the data we already hold. if (CoreAudioUtil::IsChannelLayoutSupported( eRender, eConsole, input_params.channel_layout())) { // Open up using the same channel layout as the source if it is @@ -413,10 +454,10 @@ AudioParameters AudioManagerWin::GetPreferredOutputStreamParameters( // equal to the input values, AudioOutputResampler will skip resampling // and bit per sample differences (since the input parameters will match // the output parameters). - sample_rate = input_params.sample_rate(); bits_per_sample = input_params.bits_per_sample(); - channel_layout = input_params.channel_layout(); buffer_size = input_params.frames_per_buffer(); + channel_layout = input_params.channel_layout(); + sample_rate = input_params.sample_rate(); } } @@ -435,7 +476,7 @@ AudioInputStream* AudioManagerWin::CreatePCMWaveInAudioInputStream( std::string xp_device_id = device_id; if (device_id != AudioManagerBase::kDefaultDeviceId && enumeration_type_ == kMMDeviceEnumeration) { - xp_device_id = media::ConvertToWinXPDeviceId(device_id); + xp_device_id = ConvertToWinXPInputDeviceId(device_id); if (xp_device_id.empty()) { DLOG(ERROR) << "Cannot find a waveIn device which matches the device ID " << device_id; diff --git a/media/audio/win/audio_manager_win.h b/media/audio/win/audio_manager_win.h index 65cc73bbd6..b3e8de9286 100644 --- a/media/audio/win/audio_manager_win.h +++ b/media/audio/win/audio_manager_win.h @@ -25,26 +25,33 @@ class MEDIA_EXPORT AudioManagerWin : public AudioManagerBase { virtual bool HasAudioInputDevices() OVERRIDE; virtual string16 GetAudioInputDeviceModel() OVERRIDE; virtual void ShowAudioInputSettings() OVERRIDE; - virtual void GetAudioInputDeviceNames(media::AudioDeviceNames* device_names) + virtual void GetAudioInputDeviceNames(AudioDeviceNames* device_names) + OVERRIDE; + virtual void GetAudioOutputDeviceNames(AudioDeviceNames* device_names) OVERRIDE; virtual AudioParameters GetInputStreamParameters( const std::string& device_id) OVERRIDE; + virtual std::string GetAssociatedOutputDeviceID( + const std::string& input_device_id) OVERRIDE; // Implementation of AudioManagerBase. virtual AudioOutputStream* MakeLinearOutputStream( const AudioParameters& params) OVERRIDE; virtual AudioOutputStream* MakeLowLatencyOutputStream( const AudioParameters& params, + const std::string& device_id, const std::string& input_device_id) OVERRIDE; virtual AudioInputStream* MakeLinearInputStream( const AudioParameters& params, const std::string& device_id) OVERRIDE; virtual AudioInputStream* MakeLowLatencyInputStream( const AudioParameters& params, const std::string& device_id) OVERRIDE; + virtual std::string GetDefaultOutputDeviceID() OVERRIDE; protected: virtual ~AudioManagerWin(); virtual AudioParameters GetPreferredOutputStreamParameters( + const std::string& output_device_id, const AudioParameters& input_params) OVERRIDE; private: @@ -55,6 +62,8 @@ class MEDIA_EXPORT AudioManagerWin : public AudioManagerBase { }; // Allow unit test to modify the utilized enumeration API. + // TODO(joi): Collapse these tests into one. + friend class AudioManagerTest; friend class AudioInputDeviceTest; EnumerationType enumeration_type_; @@ -76,6 +85,8 @@ class MEDIA_EXPORT AudioManagerWin : public AudioManagerBase { void CreateDeviceListener(); void DestroyDeviceListener(); + void GetAudioDeviceNamesImpl(bool input, AudioDeviceNames* device_names); + // Listen for output device changes. scoped_ptr<AudioDeviceListenerWin> output_device_listener_; diff --git a/media/audio/win/audio_output_win_unittest.cc b/media/audio/win/audio_output_win_unittest.cc index 4e13d84f3d..7ce146b0ab 100644 --- a/media/audio/win/audio_output_win_unittest.cc +++ b/media/audio/win/audio_output_win_unittest.cc @@ -185,7 +185,7 @@ TEST(WinAudioTest, PCMWaveStreamGetAndClose) { AudioOutputStream* oas = audio_man->MakeAudioOutputStream( AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_STEREO, 8000, 16, 256), - std::string()); + std::string(), std::string()); ASSERT_TRUE(NULL != oas); oas->Close(); } @@ -201,29 +201,29 @@ TEST(WinAudioTest, SanityOnMakeParams) { AudioParameters::Format fmt = AudioParameters::AUDIO_PCM_LINEAR; EXPECT_TRUE(NULL == audio_man->MakeAudioOutputStream( AudioParameters(fmt, CHANNEL_LAYOUT_UNSUPPORTED, 8000, 16, 256), - std::string())); + std::string(), std::string())); EXPECT_TRUE(NULL == audio_man->MakeAudioOutputStream( AudioParameters(fmt, CHANNEL_LAYOUT_MONO, 1024 * 1024, 16, 256), - std::string())); + std::string(), std::string())); EXPECT_TRUE(NULL == audio_man->MakeAudioOutputStream( AudioParameters(fmt, CHANNEL_LAYOUT_STEREO, 8000, 80, 256), - std::string())); + std::string(), std::string())); EXPECT_TRUE(NULL == audio_man->MakeAudioOutputStream( AudioParameters(fmt, CHANNEL_LAYOUT_UNSUPPORTED, 8000, 16, 256), - std::string())); + std::string(), std::string())); EXPECT_TRUE(NULL == audio_man->MakeAudioOutputStream( AudioParameters(fmt, CHANNEL_LAYOUT_STEREO, -8000, 16, 256), - std::string())); + std::string(), std::string())); EXPECT_TRUE(NULL == audio_man->MakeAudioOutputStream( AudioParameters(fmt, CHANNEL_LAYOUT_MONO, 8000, 16, -100), - std::string())); + std::string(), std::string())); EXPECT_TRUE(NULL == audio_man->MakeAudioOutputStream( AudioParameters(fmt, CHANNEL_LAYOUT_MONO, 8000, 16, 0), - std::string())); + std::string(), std::string())); EXPECT_TRUE(NULL == audio_man->MakeAudioOutputStream( AudioParameters(fmt, CHANNEL_LAYOUT_MONO, 8000, 16, media::limits::kMaxSamplesPerPacket + 1), - std::string())); + std::string(), std::string())); } // Test that it can be opened and closed. @@ -237,7 +237,7 @@ TEST(WinAudioTest, PCMWaveStreamOpenAndClose) { AudioOutputStream* oas = audio_man->MakeAudioOutputStream( AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_STEREO, 8000, 16, 256), - std::string()); + std::string(), std::string()); ASSERT_TRUE(NULL != oas); EXPECT_TRUE(oas->Open()); oas->Close(); @@ -254,7 +254,7 @@ TEST(WinAudioTest, PCMWaveStreamOpenLimit) { AudioOutputStream* oas = audio_man->MakeAudioOutputStream( AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_STEREO, 8000, 16, 1024 * 1024 * 1024), - std::string()); + std::string(), std::string()); EXPECT_TRUE(NULL == oas); if (oas) oas->Close(); @@ -273,7 +273,7 @@ TEST(WinAudioTest, PCMWaveSlowSource) { AudioOutputStream* oas = audio_man->MakeAudioOutputStream( AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_MONO, 16000, 16, 256), - std::string()); + std::string(), std::string()); ASSERT_TRUE(NULL != oas); TestSourceLaggy test_laggy(2, 90); EXPECT_TRUE(oas->Open()); @@ -302,7 +302,7 @@ TEST(WinAudioTest, PCMWaveStreamPlaySlowLoop) { AudioOutputStream* oas = audio_man->MakeAudioOutputStream( AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_MONO, AudioParameters::kAudioCDSampleRate, 16, samples_100_ms), - std::string()); + std::string(), std::string()); ASSERT_TRUE(NULL != oas); SineWaveAudioSource source(1, 200.0, AudioParameters::kAudioCDSampleRate); @@ -333,7 +333,7 @@ TEST(WinAudioTest, PCMWaveStreamPlay200HzTone44Kss) { AudioOutputStream* oas = audio_man->MakeAudioOutputStream( AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_MONO, AudioParameters::kAudioCDSampleRate, 16, samples_100_ms), - std::string()); + std::string(), std::string()); ASSERT_TRUE(NULL != oas); SineWaveAudioSource source(1, 200.0, AudioParameters::kAudioCDSampleRate); @@ -362,7 +362,7 @@ TEST(WinAudioTest, PCMWaveStreamPlay200HzTone22Kss) { AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_MONO, AudioParameters::kAudioCDSampleRate / 2, 16, samples_100_ms), - std::string()); + std::string(), std::string()); ASSERT_TRUE(NULL != oas); SineWaveAudioSource source(1, 200.0, AudioParameters::kAudioCDSampleRate/2); @@ -402,7 +402,7 @@ TEST(WinAudioTest, PushSourceFile16KHz) { AudioOutputStream* oas = audio_man->MakeAudioOutputStream( AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_MONO, kSampleRate, 16, kSamples100ms), - std::string()); + std::string(), std::string()); ASSERT_TRUE(NULL != oas); EXPECT_TRUE(oas->Open()); @@ -439,7 +439,7 @@ TEST(WinAudioTest, PCMWaveStreamPlayTwice200HzTone44Kss) { AudioOutputStream* oas = audio_man->MakeAudioOutputStream( AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_MONO, AudioParameters::kAudioCDSampleRate, 16, samples_100_ms), - std::string()); + std::string(), std::string()); ASSERT_TRUE(NULL != oas); SineWaveAudioSource source(1, 200.0, AudioParameters::kAudioCDSampleRate); @@ -486,7 +486,7 @@ TEST(WinAudioTest, PCMWaveStreamPlay200HzToneLowLatency) { AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_MONO, sample_rate, 16, n * samples_10_ms), - std::string()); + std::string(), std::string()); ASSERT_TRUE(NULL != oas); SineWaveAudioSource source(1, 200, sample_rate); @@ -520,7 +520,7 @@ TEST(WinAudioTest, PCMWaveStreamPendingBytes) { AudioOutputStream* oas = audio_man->MakeAudioOutputStream( AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_MONO, AudioParameters::kAudioCDSampleRate, 16, samples_100_ms), - std::string()); + std::string(), std::string()); ASSERT_TRUE(NULL != oas); NiceMock<MockAudioSource> source; @@ -680,7 +680,7 @@ TEST(WinAudioTest, SyncSocketBasic) { AudioOutputStream* oas = audio_man->MakeAudioOutputStream(params, - std::string()); + std::string(), std::string()); ASSERT_TRUE(NULL != oas); ASSERT_TRUE(oas->Open()); diff --git a/media/audio/win/audio_unified_win_unittest.cc b/media/audio/win/audio_unified_win_unittest.cc index cfd17aea14..011c36348b 100644 --- a/media/audio/win/audio_unified_win_unittest.cc +++ b/media/audio/win/audio_unified_win_unittest.cc @@ -196,13 +196,13 @@ class AudioUnifiedStreamWrapper { // Creates an AudioOutputStream object using default parameters. WASAPIUnifiedStream* Create() { - return static_cast<WASAPIUnifiedStream*> (CreateOutputStream()); + return static_cast<WASAPIUnifiedStream*>(CreateOutputStream()); } // Creates an AudioOutputStream object using default parameters but a // specified input device. WASAPIUnifiedStream* Create(const std::string device_id) { - return static_cast<WASAPIUnifiedStream*> (CreateOutputStream(device_id)); + return static_cast<WASAPIUnifiedStream*>(CreateOutputStream(device_id)); } AudioParameters::Format format() const { return params_.format(); } @@ -223,20 +223,21 @@ class AudioUnifiedStreamWrapper { CoreAudioUtil::CreateDefaultDevice(eCapture, eConsole); AudioDeviceName name; EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetDeviceName(audio_device, &name))); - const std::string& device_id = name.unique_id; - EXPECT_TRUE(CoreAudioUtil::DeviceIsDefault(eCapture, eConsole, device_id)); + const std::string& input_device_id = name.unique_id; + EXPECT_TRUE(CoreAudioUtil::DeviceIsDefault(eCapture, eConsole, + input_device_id)); // Create the unified audio I/O stream using the default input device. AudioOutputStream* aos = audio_man_->MakeAudioOutputStream(params_, - device_id); + "", input_device_id); EXPECT_TRUE(aos); return aos; } - AudioOutputStream* CreateOutputStream(const std::string& device_id) { + AudioOutputStream* CreateOutputStream(const std::string& input_device_id) { // Create the unified audio I/O stream using the specified input device. AudioOutputStream* aos = audio_man_->MakeAudioOutputStream(params_, - device_id); + "", input_device_id); EXPECT_TRUE(aos); return aos; } diff --git a/media/audio/win/core_audio_util_win.cc b/media/audio/win/core_audio_util_win.cc index 73d7b11171..4adfdda090 100644 --- a/media/audio/win/core_audio_util_win.cc +++ b/media/audio/win/core_audio_util_win.cc @@ -123,7 +123,7 @@ static std::ostream& operator<<(std::ostream& os, return os; } -bool LoadAudiosesDll() { +static bool LoadAudiosesDll() { static const wchar_t* const kAudiosesDLL = L"%WINDIR%\\system32\\audioses.dll"; @@ -132,7 +132,7 @@ bool LoadAudiosesDll() { return (LoadLibraryExW(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH) != NULL); } -bool CanCreateDeviceEnumerator() { +static bool CanCreateDeviceEnumerator() { ScopedComPtr<IMMDeviceEnumerator> device_enumerator; HRESULT hr = device_enumerator.CreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER); @@ -144,6 +144,14 @@ bool CanCreateDeviceEnumerator() { return SUCCEEDED(hr); } +static std::string GetDeviceID(IMMDevice* device) { + ScopedCoMem<WCHAR> device_id_com; + std::string device_id; + if (SUCCEEDED(device->GetId(&device_id_com))) + WideToUTF8(device_id_com, wcslen(device_id_com), &device_id); + return device_id; +} + bool CoreAudioUtil::IsSupported() { // It is possible to force usage of WaveXxx APIs by using a command line flag. const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); @@ -263,6 +271,12 @@ ScopedComPtr<IMMDevice> CoreAudioUtil::CreateDefaultDevice(EDataFlow data_flow, return endpoint_device; } +std::string CoreAudioUtil::GetDefaultOutputDeviceID() { + DCHECK(IsSupported()); + ScopedComPtr<IMMDevice> device(CreateDefaultDevice(eRender, eConsole)); + return device ? GetDeviceID(device) : std::string(); +} + ScopedComPtr<IMMDevice> CoreAudioUtil::CreateDevice( const std::string& device_id) { DCHECK(IsSupported()); @@ -289,17 +303,14 @@ HRESULT CoreAudioUtil::GetDeviceName(IMMDevice* device, AudioDeviceName* name) { // Retrieve unique name of endpoint device. // Example: "{0.0.1.00000000}.{8db6020f-18e3-4f25-b6f5-7726c9122574}". AudioDeviceName device_name; - ScopedCoMem<WCHAR> endpoint_device_id; - HRESULT hr = device->GetId(&endpoint_device_id); - if (FAILED(hr)) - return hr; - WideToUTF8(endpoint_device_id, wcslen(endpoint_device_id), - &device_name.unique_id); + device_name.unique_id = GetDeviceID(device); + if (device_name.unique_id.empty()) + return E_FAIL; // Retrieve user-friendly name of endpoint device. // Example: "Microphone (Realtek High Definition Audio)". ScopedComPtr<IPropertyStore> properties; - hr = device->OpenPropertyStore(STGM_READ, properties.Receive()); + HRESULT hr = device->OpenPropertyStore(STGM_READ, properties.Receive()); if (FAILED(hr)) return hr; base::win::ScopedPropVariant friendly_name; @@ -365,6 +376,41 @@ std::string CoreAudioUtil::GetAudioControllerID(IMMDevice* device, return controller_id; } +std::string CoreAudioUtil::GetMatchingOutputDeviceID( + const std::string& input_device_id) { + ScopedComPtr<IMMDevice> input_device(CreateDevice(input_device_id)); + if (!input_device) + return std::string(); + + // See if we can get id of the associated controller. + ScopedComPtr<IMMDeviceEnumerator> enumerator(CreateDeviceEnumerator()); + std::string controller_id(GetAudioControllerID(input_device, enumerator)); + if (controller_id.empty()) + return std::string(); + + // Now enumerate the available (and active) output devices and see if any of + // them is associated with the same controller. + ScopedComPtr<IMMDeviceCollection> collection; + enumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, + collection.Receive()); + if (!collection) + return std::string(); + + UINT count = 0; + collection->GetCount(&count); + ScopedComPtr<IMMDevice> output_device; + for (UINT i = 0; i < count; ++i) { + collection->Item(i, output_device.Receive()); + std::string output_controller_id(CoreAudioUtil::GetAudioControllerID( + output_device, enumerator)); + if (output_controller_id == controller_id) + break; + output_device = NULL; + } + + return output_device ? GetDeviceID(output_device) : std::string(); +} + std::string CoreAudioUtil::GetFriendlyName(const std::string& device_id) { DCHECK(IsSupported()); ScopedComPtr<IMMDevice> audio_device = CreateDevice(device_id); @@ -387,16 +433,8 @@ bool CoreAudioUtil::DeviceIsDefault(EDataFlow flow, if (!device) return false; - ScopedCoMem<WCHAR> default_device_id; - HRESULT hr = device->GetId(&default_device_id); - if (FAILED(hr)) - return false; - - std::string str_default; - WideToUTF8(default_device_id, wcslen(default_device_id), &str_default); - if (device_id.compare(str_default) != 0) - return false; - return true; + std::string str_default(GetDeviceID(device)); + return device_id.compare(str_default) == 0; } EDataFlow CoreAudioUtil::GetDataFlow(IMMDevice* device) { diff --git a/media/audio/win/core_audio_util_win.h b/media/audio/win/core_audio_util_win.h index 154a33a8a4..cdf6dfb11d 100644 --- a/media/audio/win/core_audio_util_win.h +++ b/media/audio/win/core_audio_util_win.h @@ -59,6 +59,10 @@ class MEDIA_EXPORT CoreAudioUtil { static ScopedComPtr<IMMDevice> CreateDefaultDevice( EDataFlow data_flow, ERole role); + // Returns the device id of the default output device or an empty string + // if no such device exists or if the default device has been disabled. + static std::string GetDefaultOutputDeviceID(); + // Creates an endpoint device that is specified by a unique endpoint device- // identification string. static ScopedComPtr<IMMDevice> CreateDevice(const std::string& device_id); @@ -80,6 +84,12 @@ class MEDIA_EXPORT CoreAudioUtil { static std::string GetAudioControllerID(IMMDevice* device, IMMDeviceEnumerator* enumerator); + // Accepts an id of an input device and finds a matching output device id. + // If the associated hardware does not have an audio output device (e.g. + // a webcam with a mic), an empty string is returned. + static std::string GetMatchingOutputDeviceID( + const std::string& input_device_id); + // Gets the user-friendly name of the endpoint device which is represented // by a unique id in |device_id|. static std::string GetFriendlyName(const std::string& device_id); diff --git a/media/audio/win/core_audio_util_win_unittest.cc b/media/audio/win/core_audio_util_win_unittest.cc index f18c6118e1..abef868202 100644 --- a/media/audio/win/core_audio_util_win_unittest.cc +++ b/media/audio/win/core_audio_util_win_unittest.cc @@ -3,7 +3,9 @@ // found in the LICENSE file. #include "base/memory/scoped_ptr.h" +#include "base/strings/utf_string_conversions.h" #include "base/synchronization/waitable_event.h" +#include "base/win/scoped_co_mem.h" #include "base/win/scoped_com_initializer.h" #include "base/win/scoped_handle.h" #include "media/audio/win/core_audio_util_win.h" @@ -475,6 +477,46 @@ TEST_F(CoreAudioUtilWinTest, FillRenderEndpointBufferWithSilence) { EXPECT_EQ(num_queued_frames, endpoint_buffer_size); } -// +// This test can only succeed on a machine that has audio hardware +// that has both input and output devices. Currently this is the case +// with our test bots and the CanRunAudioTest() method should make sure +// that the test won't run in unsupported environments, but be warned. +TEST_F(CoreAudioUtilWinTest, GetMatchingOutputDeviceID) { + if (!CanRunAudioTest()) + return; + + bool found_a_pair = false; + + ScopedComPtr<IMMDeviceEnumerator> enumerator( + CoreAudioUtil::CreateDeviceEnumerator()); + ASSERT_TRUE(enumerator); + + // Enumerate all active input and output devices and fetch the ID of + // the associated device. + ScopedComPtr<IMMDeviceCollection> collection; + ASSERT_TRUE(SUCCEEDED(enumerator->EnumAudioEndpoints(eCapture, + DEVICE_STATE_ACTIVE, collection.Receive()))); + UINT count = 0; + collection->GetCount(&count); + for (UINT i = 0; i < count && !found_a_pair; ++i) { + ScopedComPtr<IMMDevice> device; + collection->Item(i, device.Receive()); + base::win::ScopedCoMem<WCHAR> wide_id; + device->GetId(&wide_id); + std::string id; + WideToUTF8(wide_id, wcslen(wide_id), &id); + found_a_pair = !CoreAudioUtil::GetMatchingOutputDeviceID(id).empty(); + } + + EXPECT_TRUE(found_a_pair); +} + +TEST_F(CoreAudioUtilWinTest, GetDefaultOutputDeviceID) { + if (!CanRunAudioTest()) + return; + + std::string default_device_id(CoreAudioUtil::GetDefaultOutputDeviceID()); + EXPECT_FALSE(default_device_id.empty()); +} } // namespace media diff --git a/media/audio/win/device_enumeration_win.cc b/media/audio/win/device_enumeration_win.cc index 36ed2913ff..50d0b7aa0e 100644 --- a/media/audio/win/device_enumeration_win.cc +++ b/media/audio/win/device_enumeration_win.cc @@ -8,13 +8,13 @@ #include "media/audio/win/audio_manager_win.h" +#include "base/basictypes.h" #include "base/logging.h" #include "base/strings/utf_string_conversions.h" #include "base/win/scoped_co_mem.h" #include "base/win/scoped_comptr.h" #include "base/win/scoped_propvariant.h" -using media::AudioDeviceNames; using base::win::ScopedComPtr; using base::win::ScopedCoMem; @@ -25,7 +25,10 @@ using base::win::ScopedCoMem; namespace media { -bool GetInputDeviceNamesWin(AudioDeviceNames* device_names) { +namespace { + +bool GetDeviceNamesWinImpl(EDataFlow data_flow, + 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; @@ -37,24 +40,24 @@ bool GetInputDeviceNamesWin(AudioDeviceNames* device_names) { return false; } - // Generate a collection of active audio capture endpoint devices. + // Generate a collection of active audio endpoint devices. // This method will succeed even if all devices are disabled. ScopedComPtr<IMMDeviceCollection> collection; - hr = enumerator->EnumAudioEndpoints(eCapture, + hr = enumerator->EnumAudioEndpoints(data_flow, DEVICE_STATE_ACTIVE, collection.Receive()); if (FAILED(hr)) return false; - // Retrieve the number of active capture devices. + // Retrieve the number of active devices. UINT number_of_active_devices = 0; collection->GetCount(&number_of_active_devices); if (number_of_active_devices == 0) return true; - media::AudioDeviceName device; + AudioDeviceName device; - // Loop over all active capture devices and add friendly name and + // Loop over all active 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. @@ -92,14 +95,22 @@ bool GetInputDeviceNamesWin(AudioDeviceNames* device_names) { return true; } -bool GetInputDeviceNamesWinXP(AudioDeviceNames* device_names) { +// The waveform API is weird in that it has completely separate but +// almost identical functions and structs for input devices vs. output +// devices. We deal with this by implementing the logic as a templated +// function that takes the functions and struct type to use as +// template parameters. +template <UINT (__stdcall *NumDevsFunc)(), + typename CAPSSTRUCT, + MMRESULT (__stdcall *DevCapsFunc)(UINT_PTR, CAPSSTRUCT*, UINT)> +bool GetDeviceNamesWinXPImpl(AudioDeviceNames* device_names) { // Retrieve the number of active waveform input devices. - UINT number_of_active_devices = waveInGetNumDevs(); + UINT number_of_active_devices = NumDevsFunc(); if (number_of_active_devices == 0) return true; - media::AudioDeviceName device; - WAVEINCAPS capabilities; + AudioDeviceName device; + CAPSSTRUCT capabilities; MMRESULT err = MMSYSERR_NOERROR; // Loop over all active capture devices and add friendly name and @@ -108,7 +119,7 @@ bool GetInputDeviceNamesWinXP(AudioDeviceNames* device_names) { // 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)); + err = DevCapsFunc(i, &capabilities, sizeof(capabilities)); if (err != MMSYSERR_NOERROR) continue; @@ -118,7 +129,7 @@ bool GetInputDeviceNamesWinXP(AudioDeviceNames* device_names) { 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); + device.unique_id = device.device_name; // Add combination of user-friendly and unique name to the output list. device_names->push_back(device); @@ -127,7 +138,27 @@ bool GetInputDeviceNamesWinXP(AudioDeviceNames* device_names) { return true; } -std::string ConvertToWinXPDeviceId(const std::string& device_id) { +} // namespace + +bool GetInputDeviceNamesWin(AudioDeviceNames* device_names) { + return GetDeviceNamesWinImpl(eCapture, device_names); +} + +bool GetOutputDeviceNamesWin(AudioDeviceNames* device_names) { + return GetDeviceNamesWinImpl(eRender, device_names); +} + +bool GetInputDeviceNamesWinXP(AudioDeviceNames* device_names) { + return GetDeviceNamesWinXPImpl< + waveInGetNumDevs, WAVEINCAPSW, waveInGetDevCapsW>(device_names); +} + +bool GetOutputDeviceNamesWinXP(AudioDeviceNames* device_names) { + return GetDeviceNamesWinXPImpl< + waveOutGetNumDevs, WAVEOUTCAPSW, waveOutGetDevCapsW>(device_names); +} + +std::string ConvertToWinXPInputDeviceId(const std::string& device_id) { UINT number_of_active_devices = waveInGetNumDevs(); MMRESULT result = MMSYSERR_NOERROR; diff --git a/media/audio/win/device_enumeration_win.h b/media/audio/win/device_enumeration_win.h index 3d44670a6d..e61a331842 100644 --- a/media/audio/win/device_enumeration_win.h +++ b/media/audio/win/device_enumeration_win.h @@ -11,28 +11,32 @@ namespace media { -// 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. +// Returns a list of audio input or output 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); +bool GetOutputDeviceNamesWin(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. +// Returns a list of audio input or output 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); +bool GetOutputDeviceNamesWinXP(media::AudioDeviceNames* device_names); -// Converts a device ID generated by |GetInputDeviceNamesWin()| to the +// Converts an input device ID generated by |GetInputDeviceNamesWin()| to the // corresponding ID by |GetInputDeviceNamesWinXP()|. Returns an empty string on // failure. // Example input and output: // - input ID: "{0.0.1.00000000}.{8db6020f-18e3-4f25-b6f5-7726c9122574}" // - output ID: "Microphone (Realtek High Defini" -std::string ConvertToWinXPDeviceId(const std::string& device_id); +std::string ConvertToWinXPInputDeviceId(const std::string& device_id); } // namespace media |