audio_output_device_unittest.cc revision 68043e1e95eeb07d5cae7aca370b26518b0867d6
1// Copyright (c) 2012 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 <vector> 6 7#include "base/at_exit.h" 8#include "base/memory/shared_memory.h" 9#include "base/message_loop/message_loop.h" 10#include "base/process/process_handle.h" 11#include "base/sync_socket.h" 12#include "base/test/test_timeouts.h" 13#include "media/audio/audio_output_device.h" 14#include "media/audio/sample_rates.h" 15#include "media/audio/shared_memory_util.h" 16#include "testing/gmock/include/gmock/gmock.h" 17#include "testing/gmock_mutant.h" 18#include "testing/gtest/include/gtest/gtest.h" 19 20using base::CancelableSyncSocket; 21using base::SharedMemory; 22using base::SyncSocket; 23using testing::_; 24using testing::DoAll; 25using testing::Invoke; 26using testing::Return; 27using testing::WithArgs; 28using testing::StrictMock; 29using testing::Values; 30 31namespace media { 32 33namespace { 34 35class MockRenderCallback : public AudioRendererSink::RenderCallback { 36 public: 37 MockRenderCallback() {} 38 virtual ~MockRenderCallback() {} 39 40 MOCK_METHOD2(Render, int(AudioBus* dest, int audio_delay_milliseconds)); 41 MOCK_METHOD3(RenderIO, void(AudioBus* source, 42 AudioBus* dest, 43 int audio_delay_milliseconds)); 44 MOCK_METHOD0(OnRenderError, void()); 45}; 46 47class MockAudioOutputIPC : public AudioOutputIPC { 48 public: 49 MockAudioOutputIPC() {} 50 virtual ~MockAudioOutputIPC() {} 51 52 MOCK_METHOD3(CreateStream, void(AudioOutputIPCDelegate* delegate, 53 const AudioParameters& params, 54 int session_id)); 55 MOCK_METHOD0(PlayStream, void()); 56 MOCK_METHOD0(PauseStream, void()); 57 MOCK_METHOD0(CloseStream, void()); 58 MOCK_METHOD1(SetVolume, void(double volume)); 59}; 60 61// Creates a copy of a SyncSocket handle that we can give to AudioOutputDevice. 62// On Windows this means duplicating the pipe handle so that AudioOutputDevice 63// can call CloseHandle() (since ownership has been transferred), but on other 64// platforms, we just copy the same socket handle since AudioOutputDevice on 65// those platforms won't actually own the socket (FileDescriptor.auto_close is 66// false). 67bool DuplicateSocketHandle(SyncSocket::Handle socket_handle, 68 SyncSocket::Handle* copy) { 69#if defined(OS_WIN) 70 HANDLE process = GetCurrentProcess(); 71 ::DuplicateHandle(process, socket_handle, process, copy, 72 0, FALSE, DUPLICATE_SAME_ACCESS); 73 return *copy != NULL; 74#else 75 *copy = socket_handle; 76 return *copy != -1; 77#endif 78} 79 80ACTION_P2(SendPendingBytes, socket, pending_bytes) { 81 socket->Send(&pending_bytes, sizeof(pending_bytes)); 82} 83 84// Used to terminate a loop from a different thread than the loop belongs to. 85// |loop| should be a MessageLoopProxy. 86ACTION_P(QuitLoop, loop) { 87 loop->PostTask(FROM_HERE, base::MessageLoop::QuitClosure()); 88} 89 90} // namespace. 91 92class AudioOutputDeviceTest 93 : public testing::Test, 94 public testing::WithParamInterface<bool> { 95 public: 96 AudioOutputDeviceTest(); 97 ~AudioOutputDeviceTest(); 98 99 void StartAudioDevice(); 100 void CreateStream(); 101 void ExpectRenderCallback(); 102 void WaitUntilRenderCallback(); 103 void StopAudioDevice(); 104 105 protected: 106 // Used to clean up TLS pointers that the test(s) will initialize. 107 // Must remain the first member of this class. 108 base::ShadowingAtExitManager at_exit_manager_; 109 base::MessageLoopForIO io_loop_; 110 AudioParameters default_audio_parameters_; 111 StrictMock<MockRenderCallback> callback_; 112 MockAudioOutputIPC* audio_output_ipc_; // owned by audio_device_ 113 scoped_refptr<AudioOutputDevice> audio_device_; 114 115 private: 116 int CalculateMemorySize(); 117 118 const bool synchronized_io_; 119 const int input_channels_; 120 SharedMemory shared_memory_; 121 CancelableSyncSocket browser_socket_; 122 CancelableSyncSocket renderer_socket_; 123 124 DISALLOW_COPY_AND_ASSIGN(AudioOutputDeviceTest); 125}; 126 127int AudioOutputDeviceTest::CalculateMemorySize() { 128 // Calculate output and input memory size. 129 int output_memory_size = 130 AudioBus::CalculateMemorySize(default_audio_parameters_); 131 132 int frames = default_audio_parameters_.frames_per_buffer(); 133 int input_memory_size = 134 AudioBus::CalculateMemorySize(input_channels_, frames); 135 136 int io_buffer_size = output_memory_size + input_memory_size; 137 138 // This is where it gets a bit hacky. The shared memory contract between 139 // AudioOutputDevice and its browser side counter part includes a bit more 140 // than just the audio data, so we must call TotalSharedMemorySizeInBytes() 141 // to get the actual size needed to fit the audio data plus the extra data. 142 return TotalSharedMemorySizeInBytes(io_buffer_size); 143} 144 145AudioOutputDeviceTest::AudioOutputDeviceTest() 146 : synchronized_io_(GetParam()), 147 input_channels_(synchronized_io_ ? 2 : 0) { 148 default_audio_parameters_.Reset( 149 AudioParameters::AUDIO_PCM_LINEAR, 150 CHANNEL_LAYOUT_STEREO, 2, input_channels_, 151 48000, 16, 1024); 152 153 audio_output_ipc_ = new MockAudioOutputIPC(); 154 audio_device_ = new AudioOutputDevice( 155 scoped_ptr<AudioOutputIPC>(audio_output_ipc_), 156 io_loop_.message_loop_proxy()); 157 158 audio_device_->Initialize(default_audio_parameters_, 159 &callback_); 160 161 io_loop_.RunUntilIdle(); 162} 163 164AudioOutputDeviceTest::~AudioOutputDeviceTest() { 165 audio_device_ = NULL; 166} 167 168void AudioOutputDeviceTest::StartAudioDevice() { 169 audio_device_->Start(); 170 171 EXPECT_CALL(*audio_output_ipc_, CreateStream(audio_device_.get(), _, 0)); 172 173 io_loop_.RunUntilIdle(); 174} 175 176void AudioOutputDeviceTest::CreateStream() { 177 const int kMemorySize = CalculateMemorySize(); 178 179 ASSERT_TRUE(shared_memory_.CreateAndMapAnonymous(kMemorySize)); 180 memset(shared_memory_.memory(), 0xff, kMemorySize); 181 182 ASSERT_TRUE(CancelableSyncSocket::CreatePair(&browser_socket_, 183 &renderer_socket_)); 184 185 // Create duplicates of the handles we pass to AudioOutputDevice since 186 // ownership will be transferred and AudioOutputDevice is responsible for 187 // freeing. 188 SyncSocket::Handle audio_device_socket = SyncSocket::kInvalidHandle; 189 ASSERT_TRUE(DuplicateSocketHandle(renderer_socket_.handle(), 190 &audio_device_socket)); 191 base::SharedMemoryHandle duplicated_memory_handle; 192 ASSERT_TRUE(shared_memory_.ShareToProcess(base::GetCurrentProcessHandle(), 193 &duplicated_memory_handle)); 194 195 audio_device_->OnStreamCreated(duplicated_memory_handle, audio_device_socket, 196 PacketSizeInBytes(kMemorySize)); 197 io_loop_.RunUntilIdle(); 198} 199 200void AudioOutputDeviceTest::ExpectRenderCallback() { 201 // We should get a 'play' notification when we call OnStreamCreated(). 202 // Respond by asking for some audio data. This should ask our callback 203 // to provide some audio data that AudioOutputDevice then writes into the 204 // shared memory section. 205 const int kMemorySize = CalculateMemorySize(); 206 207 EXPECT_CALL(*audio_output_ipc_, PlayStream()) 208 .WillOnce(SendPendingBytes(&browser_socket_, kMemorySize)); 209 210 // We expect calls to our audio renderer callback, which returns the number 211 // of frames written to the memory section. 212 // Here's the second place where it gets hacky: There's no way for us to 213 // know (without using a sleep loop!) when the AudioOutputDevice has finished 214 // writing the interleaved audio data into the shared memory section. 215 // So, for the sake of this test, we consider the call to Render a sign 216 // of success and quit the loop. 217 if (synchronized_io_) { 218 // For synchronized I/O, we expect RenderIO(). 219 EXPECT_CALL(callback_, RenderIO(_, _, _)) 220 .WillOnce(QuitLoop(io_loop_.message_loop_proxy())); 221 } else { 222 // For output only we expect Render(). 223 const int kNumberOfFramesToProcess = 0; 224 EXPECT_CALL(callback_, Render(_, _)) 225 .WillOnce(DoAll( 226 QuitLoop(io_loop_.message_loop_proxy()), 227 Return(kNumberOfFramesToProcess))); 228 } 229} 230 231void AudioOutputDeviceTest::WaitUntilRenderCallback() { 232 // Don't hang the test if we never get the Render() callback. 233 io_loop_.PostDelayedTask(FROM_HERE, base::MessageLoop::QuitClosure(), 234 TestTimeouts::action_timeout()); 235 io_loop_.Run(); 236} 237 238void AudioOutputDeviceTest::StopAudioDevice() { 239 audio_device_->Stop(); 240 241 EXPECT_CALL(*audio_output_ipc_, CloseStream()); 242 243 io_loop_.RunUntilIdle(); 244} 245 246TEST_P(AudioOutputDeviceTest, Initialize) { 247 // Tests that the object can be constructed, initialized and destructed 248 // without having ever been started/stopped. 249} 250 251// Calls Start() followed by an immediate Stop() and check for the basic message 252// filter messages being sent in that case. 253TEST_P(AudioOutputDeviceTest, StartStop) { 254 StartAudioDevice(); 255 StopAudioDevice(); 256} 257 258// AudioOutputDevice supports multiple start/stop sequences. 259TEST_P(AudioOutputDeviceTest, StartStopStartStop) { 260 StartAudioDevice(); 261 StopAudioDevice(); 262 StartAudioDevice(); 263 StopAudioDevice(); 264} 265 266// Simulate receiving OnStreamCreated() prior to processing ShutDownOnIOThread() 267// on the IO loop. 268TEST_P(AudioOutputDeviceTest, StopBeforeRender) { 269 StartAudioDevice(); 270 271 // Call Stop() but don't run the IO loop yet. 272 audio_device_->Stop(); 273 274 // Expect us to shutdown IPC but not to render anything despite the stream 275 // getting created. 276 EXPECT_CALL(*audio_output_ipc_, CloseStream()); 277 CreateStream(); 278} 279 280// Full test with output only. 281TEST_P(AudioOutputDeviceTest, CreateStream) { 282 StartAudioDevice(); 283 ExpectRenderCallback(); 284 CreateStream(); 285 WaitUntilRenderCallback(); 286 StopAudioDevice(); 287} 288 289INSTANTIATE_TEST_CASE_P(Render, AudioOutputDeviceTest, Values(false)); 290INSTANTIATE_TEST_CASE_P(RenderIO, AudioOutputDeviceTest, Values(true)); 291 292} // namespace media. 293