1// Copyright 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "base/environment.h" 6#include "base/logging.h" 7#include "base/memory/scoped_ptr.h" 8#include "base/synchronization/waitable_event.h" 9#include "media/audio/audio_manager.h" 10#include "media/audio/audio_manager_base.h" 11#include "media/audio/fake_audio_log_factory.h" 12#include "testing/gtest/include/gtest/gtest.h" 13 14#if defined(USE_ALSA) 15#include "media/audio/alsa/audio_manager_alsa.h" 16#endif // defined(USE_ALSA) 17 18#if defined(OS_WIN) 19#include "base/win/scoped_com_initializer.h" 20#include "media/audio/win/audio_manager_win.h" 21#include "media/audio/win/wavein_input_win.h" 22#endif 23 24#if defined(USE_PULSEAUDIO) 25#include "media/audio/pulse/audio_manager_pulse.h" 26#endif // defined(USE_PULSEAUDIO) 27 28namespace media { 29 30// Test fixture which allows us to override the default enumeration API on 31// Windows. 32class AudioManagerTest : public ::testing::Test { 33 protected: 34 AudioManagerTest() 35 : audio_manager_(AudioManager::CreateForTesting()) 36#if defined(OS_WIN) 37 , com_init_(base::win::ScopedCOMInitializer::kMTA) 38#endif 39 { 40 // Wait for audio thread initialization to complete. Otherwise the 41 // enumeration type may not have been set yet. 42 base::WaitableEvent event(false, false); 43 audio_manager_->GetTaskRunner()->PostTask(FROM_HERE, base::Bind( 44 &base::WaitableEvent::Signal, base::Unretained(&event))); 45 event.Wait(); 46 } 47 48 AudioManager* audio_manager() { return audio_manager_.get(); }; 49 50#if defined(OS_WIN) 51 bool SetMMDeviceEnumeration() { 52 AudioManagerWin* amw = static_cast<AudioManagerWin*>(audio_manager_.get()); 53 // Windows Wave is used as default if Windows XP was detected => 54 // return false since MMDevice is not supported on XP. 55 if (amw->enumeration_type() == AudioManagerWin::kWaveEnumeration) 56 return false; 57 58 amw->SetEnumerationType(AudioManagerWin::kMMDeviceEnumeration); 59 return true; 60 } 61 62 void SetWaveEnumeration() { 63 AudioManagerWin* amw = static_cast<AudioManagerWin*>(audio_manager_.get()); 64 amw->SetEnumerationType(AudioManagerWin::kWaveEnumeration); 65 } 66 67 std::string GetDeviceIdFromPCMWaveInAudioInputStream( 68 const std::string& device_id) { 69 AudioManagerWin* amw = static_cast<AudioManagerWin*>(audio_manager_.get()); 70 AudioParameters parameters( 71 AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_STEREO, 72 AudioParameters::kAudioCDSampleRate, 16, 73 1024); 74 scoped_ptr<PCMWaveInAudioInputStream> stream( 75 static_cast<PCMWaveInAudioInputStream*>( 76 amw->CreatePCMWaveInAudioInputStream(parameters, device_id))); 77 return stream.get() ? stream->device_id_ : std::string(); 78 } 79#endif 80 81 // Helper method which verifies that the device list starts with a valid 82 // default record followed by non-default device names. 83 static void CheckDeviceNames(const AudioDeviceNames& device_names) { 84 VLOG(2) << "Got " << device_names.size() << " audio devices."; 85 if (!device_names.empty()) { 86 AudioDeviceNames::const_iterator it = device_names.begin(); 87 88 // The first device in the list should always be the default device. 89 EXPECT_EQ(std::string(AudioManagerBase::kDefaultDeviceName), 90 it->device_name); 91 EXPECT_EQ(std::string(AudioManagerBase::kDefaultDeviceId), it->unique_id); 92 ++it; 93 94 // Other devices should have non-empty name and id and should not contain 95 // default name or id. 96 while (it != device_names.end()) { 97 EXPECT_FALSE(it->device_name.empty()); 98 EXPECT_FALSE(it->unique_id.empty()); 99 VLOG(2) << "Device ID(" << it->unique_id 100 << "), label: " << it->device_name; 101 EXPECT_NE(std::string(AudioManagerBase::kDefaultDeviceName), 102 it->device_name); 103 EXPECT_NE(std::string(AudioManagerBase::kDefaultDeviceId), 104 it->unique_id); 105 ++it; 106 } 107 } else { 108 // Log a warning so we can see the status on the build bots. No need to 109 // break the test though since this does successfully test the code and 110 // some failure cases. 111 LOG(WARNING) << "No input devices detected"; 112 } 113 } 114 115 bool CanRunInputTest() { 116 return audio_manager_->HasAudioInputDevices(); 117 } 118 119 bool CanRunOutputTest() { 120 return audio_manager_->HasAudioOutputDevices(); 121 } 122 123#if defined(USE_ALSA) || defined(USE_PULSEAUDIO) 124 template <class T> 125 void CreateAudioManagerForTesting() { 126 // Only one AudioManager may exist at a time, so destroy the one we're 127 // currently holding before creating a new one. 128 audio_manager_.reset(); 129 audio_manager_.reset(T::Create(&fake_audio_log_factory_)); 130 } 131#endif 132 133 // Synchronously runs the provided callback/closure on the audio thread. 134 void RunOnAudioThread(const base::Closure& closure) { 135 if (!audio_manager()->GetTaskRunner()->BelongsToCurrentThread()) { 136 base::WaitableEvent event(false, false); 137 audio_manager_->GetTaskRunner()->PostTask( 138 FROM_HERE, 139 base::Bind(&AudioManagerTest::RunOnAudioThreadImpl, 140 base::Unretained(this), 141 closure, 142 &event)); 143 event.Wait(); 144 } else { 145 closure.Run(); 146 } 147 } 148 149 void RunOnAudioThreadImpl(const base::Closure& closure, 150 base::WaitableEvent* event) { 151 DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread()); 152 closure.Run(); 153 event->Signal(); 154 } 155 156 FakeAudioLogFactory fake_audio_log_factory_; 157 scoped_ptr<AudioManager> audio_manager_; 158 159#if defined(OS_WIN) 160 // The MMDevice API requires COM to be initialized on the current thread. 161 base::win::ScopedCOMInitializer com_init_; 162#endif 163}; 164 165// Test that devices can be enumerated. 166TEST_F(AudioManagerTest, EnumerateInputDevices) { 167 if (!CanRunInputTest()) 168 return; 169 170 AudioDeviceNames device_names; 171 RunOnAudioThread( 172 base::Bind(&AudioManager::GetAudioInputDeviceNames, 173 base::Unretained(audio_manager()), 174 &device_names)); 175 CheckDeviceNames(device_names); 176} 177 178// Test that devices can be enumerated. 179TEST_F(AudioManagerTest, EnumerateOutputDevices) { 180 if (!CanRunOutputTest()) 181 return; 182 183 AudioDeviceNames device_names; 184 RunOnAudioThread( 185 base::Bind(&AudioManager::GetAudioOutputDeviceNames, 186 base::Unretained(audio_manager()), 187 &device_names)); 188 CheckDeviceNames(device_names); 189} 190 191// Run additional tests for Windows since enumeration can be done using 192// two different APIs. MMDevice is default for Vista and higher and Wave 193// is default for XP and lower. 194#if defined(OS_WIN) 195 196// Override default enumeration API and force usage of Windows MMDevice. 197// This test will only run on Windows Vista and higher. 198TEST_F(AudioManagerTest, EnumerateInputDevicesWinMMDevice) { 199 if (!CanRunInputTest()) 200 return; 201 202 AudioDeviceNames device_names; 203 if (!SetMMDeviceEnumeration()) { 204 // Usage of MMDevice will fail on XP and lower. 205 LOG(WARNING) << "MM device enumeration is not supported."; 206 return; 207 } 208 audio_manager_->GetAudioInputDeviceNames(&device_names); 209 CheckDeviceNames(device_names); 210} 211 212TEST_F(AudioManagerTest, EnumerateOutputDevicesWinMMDevice) { 213 if (!CanRunOutputTest()) 214 return; 215 216 AudioDeviceNames device_names; 217 if (!SetMMDeviceEnumeration()) { 218 // Usage of MMDevice will fail on XP and lower. 219 LOG(WARNING) << "MM device enumeration is not supported."; 220 return; 221 } 222 audio_manager_->GetAudioOutputDeviceNames(&device_names); 223 CheckDeviceNames(device_names); 224} 225 226// Override default enumeration API and force usage of Windows Wave. 227// This test will run on Windows XP, Windows Vista and Windows 7. 228TEST_F(AudioManagerTest, EnumerateInputDevicesWinWave) { 229 if (!CanRunInputTest()) 230 return; 231 232 AudioDeviceNames device_names; 233 SetWaveEnumeration(); 234 audio_manager_->GetAudioInputDeviceNames(&device_names); 235 CheckDeviceNames(device_names); 236} 237 238TEST_F(AudioManagerTest, EnumerateOutputDevicesWinWave) { 239 if (!CanRunOutputTest()) 240 return; 241 242 AudioDeviceNames device_names; 243 SetWaveEnumeration(); 244 audio_manager_->GetAudioOutputDeviceNames(&device_names); 245 CheckDeviceNames(device_names); 246} 247 248TEST_F(AudioManagerTest, WinXPDeviceIdUnchanged) { 249 if (!CanRunInputTest()) 250 return; 251 252 AudioDeviceNames xp_device_names; 253 SetWaveEnumeration(); 254 audio_manager_->GetAudioInputDeviceNames(&xp_device_names); 255 CheckDeviceNames(xp_device_names); 256 257 // Device ID should remain unchanged, including the default device ID. 258 for (AudioDeviceNames::iterator i = xp_device_names.begin(); 259 i != xp_device_names.end(); ++i) { 260 EXPECT_EQ(i->unique_id, 261 GetDeviceIdFromPCMWaveInAudioInputStream(i->unique_id)); 262 } 263} 264 265TEST_F(AudioManagerTest, ConvertToWinXPInputDeviceId) { 266 if (!CanRunInputTest()) 267 return; 268 269 if (!SetMMDeviceEnumeration()) { 270 // Usage of MMDevice will fail on XP and lower. 271 LOG(WARNING) << "MM device enumeration is not supported."; 272 return; 273 } 274 275 AudioDeviceNames device_names; 276 audio_manager_->GetAudioInputDeviceNames(&device_names); 277 CheckDeviceNames(device_names); 278 279 for (AudioDeviceNames::iterator i = device_names.begin(); 280 i != device_names.end(); ++i) { 281 std::string converted_id = 282 GetDeviceIdFromPCMWaveInAudioInputStream(i->unique_id); 283 if (i == device_names.begin()) { 284 // The first in the list is the default device ID, which should not be 285 // changed when passed to PCMWaveInAudioInputStream. 286 EXPECT_EQ(i->unique_id, converted_id); 287 } else { 288 // MMDevice-style device IDs should be converted to WaveIn-style device 289 // IDs. 290 EXPECT_NE(i->unique_id, converted_id); 291 } 292 } 293} 294 295#endif // defined(OS_WIN) 296 297#if defined(USE_PULSEAUDIO) 298// On Linux, there are two implementations available and both can 299// sometimes be tested on a single system. These tests specifically 300// test Pulseaudio. 301 302TEST_F(AudioManagerTest, EnumerateInputDevicesPulseaudio) { 303 if (!CanRunInputTest()) 304 return; 305 306 CreateAudioManagerForTesting<AudioManagerPulse>(); 307 if (audio_manager_.get()) { 308 AudioDeviceNames device_names; 309 audio_manager_->GetAudioInputDeviceNames(&device_names); 310 CheckDeviceNames(device_names); 311 } else { 312 LOG(WARNING) << "No pulseaudio on this system."; 313 } 314} 315 316TEST_F(AudioManagerTest, EnumerateOutputDevicesPulseaudio) { 317 if (!CanRunOutputTest()) 318 return; 319 320 CreateAudioManagerForTesting<AudioManagerPulse>(); 321 if (audio_manager_.get()) { 322 AudioDeviceNames device_names; 323 audio_manager_->GetAudioOutputDeviceNames(&device_names); 324 CheckDeviceNames(device_names); 325 } else { 326 LOG(WARNING) << "No pulseaudio on this system."; 327 } 328} 329#endif // defined(USE_PULSEAUDIO) 330 331#if defined(USE_ALSA) 332// On Linux, there are two implementations available and both can 333// sometimes be tested on a single system. These tests specifically 334// test Alsa. 335 336TEST_F(AudioManagerTest, EnumerateInputDevicesAlsa) { 337 if (!CanRunInputTest()) 338 return; 339 340 VLOG(2) << "Testing AudioManagerAlsa."; 341 CreateAudioManagerForTesting<AudioManagerAlsa>(); 342 AudioDeviceNames device_names; 343 audio_manager_->GetAudioInputDeviceNames(&device_names); 344 CheckDeviceNames(device_names); 345} 346 347TEST_F(AudioManagerTest, EnumerateOutputDevicesAlsa) { 348 if (!CanRunOutputTest()) 349 return; 350 351 VLOG(2) << "Testing AudioManagerAlsa."; 352 CreateAudioManagerForTesting<AudioManagerAlsa>(); 353 AudioDeviceNames device_names; 354 audio_manager_->GetAudioOutputDeviceNames(&device_names); 355 CheckDeviceNames(device_names); 356} 357#endif // defined(USE_ALSA) 358 359TEST_F(AudioManagerTest, GetDefaultOutputStreamParameters) { 360#if defined(OS_WIN) || defined(OS_MACOSX) 361 if (!CanRunInputTest()) 362 return; 363 364 AudioParameters params = audio_manager_->GetDefaultOutputStreamParameters(); 365 EXPECT_TRUE(params.IsValid()); 366#endif // defined(OS_WIN) || defined(OS_MACOSX) 367} 368 369TEST_F(AudioManagerTest, GetAssociatedOutputDeviceID) { 370#if defined(OS_WIN) || defined(OS_MACOSX) 371 if (!CanRunInputTest() || !CanRunOutputTest()) 372 return; 373 374 AudioDeviceNames device_names; 375 audio_manager_->GetAudioInputDeviceNames(&device_names); 376 bool found_an_associated_device = false; 377 for (AudioDeviceNames::iterator it = device_names.begin(); 378 it != device_names.end(); 379 ++it) { 380 EXPECT_FALSE(it->unique_id.empty()); 381 EXPECT_FALSE(it->device_name.empty()); 382 std::string output_device_id( 383 audio_manager_->GetAssociatedOutputDeviceID(it->unique_id)); 384 if (!output_device_id.empty()) { 385 VLOG(2) << it->unique_id << " matches with " << output_device_id; 386 found_an_associated_device = true; 387 } 388 } 389 390 EXPECT_TRUE(found_an_associated_device); 391#endif // defined(OS_WIN) || defined(OS_MACOSX) 392} 393 394} // namespace media 395