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