audio_output_device_unittest.cc revision 868fa2fe829687343ffae624259930155e16dbd8
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_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 127static const int kStreamId = 123; 128 129int AudioOutputDeviceTest::CalculateMemorySize() { 130 // Calculate output and input memory size. 131 int output_memory_size = 132 AudioBus::CalculateMemorySize(default_audio_parameters_); 133 134 int frames = default_audio_parameters_.frames_per_buffer(); 135 int input_memory_size = 136 AudioBus::CalculateMemorySize(input_channels_, frames); 137 138 int io_buffer_size = output_memory_size + input_memory_size; 139 140 // This is where it gets a bit hacky. The shared memory contract between 141 // AudioOutputDevice and its browser side counter part includes a bit more 142 // than just the audio data, so we must call TotalSharedMemorySizeInBytes() 143 // to get the actual size needed to fit the audio data plus the extra data. 144 return TotalSharedMemorySizeInBytes(io_buffer_size); 145} 146 147AudioOutputDeviceTest::AudioOutputDeviceTest() 148 : synchronized_io_(GetParam()), 149 input_channels_(synchronized_io_ ? 2 : 0) { 150 default_audio_parameters_.Reset( 151 AudioParameters::AUDIO_PCM_LINEAR, 152 CHANNEL_LAYOUT_STEREO, 2, input_channels_, 153 48000, 16, 1024); 154 155 audio_output_ipc_ = new MockAudioOutputIPC(); 156 audio_device_ = new AudioOutputDevice( 157 scoped_ptr<AudioOutputIPC>(audio_output_ipc_), 158 io_loop_.message_loop_proxy()); 159 160 audio_device_->Initialize(default_audio_parameters_, 161 &callback_); 162 163 io_loop_.RunUntilIdle(); 164} 165 166AudioOutputDeviceTest::~AudioOutputDeviceTest() { 167 audio_device_ = NULL; 168} 169 170void AudioOutputDeviceTest::StartAudioDevice() { 171 audio_device_->Start(); 172 173 EXPECT_CALL(*audio_output_ipc_, CreateStream(audio_device_.get(), _, 0)); 174 175 io_loop_.RunUntilIdle(); 176} 177 178void AudioOutputDeviceTest::CreateStream() { 179 const int kMemorySize = CalculateMemorySize(); 180 181 ASSERT_TRUE(shared_memory_.CreateAndMapAnonymous(kMemorySize)); 182 memset(shared_memory_.memory(), 0xff, kMemorySize); 183 184 ASSERT_TRUE(CancelableSyncSocket::CreatePair(&browser_socket_, 185 &renderer_socket_)); 186 187 // Create duplicates of the handles we pass to AudioOutputDevice since 188 // ownership will be transferred and AudioOutputDevice is responsible for 189 // freeing. 190 SyncSocket::Handle audio_device_socket = SyncSocket::kInvalidHandle; 191 ASSERT_TRUE(DuplicateSocketHandle(renderer_socket_.handle(), 192 &audio_device_socket)); 193 base::SharedMemoryHandle duplicated_memory_handle; 194 ASSERT_TRUE(shared_memory_.ShareToProcess(base::GetCurrentProcessHandle(), 195 &duplicated_memory_handle)); 196 197 audio_device_->OnStreamCreated(duplicated_memory_handle, audio_device_socket, 198 PacketSizeInBytes(kMemorySize)); 199 io_loop_.RunUntilIdle(); 200} 201 202void AudioOutputDeviceTest::ExpectRenderCallback() { 203 // We should get a 'play' notification when we call OnStreamCreated(). 204 // Respond by asking for some audio data. This should ask our callback 205 // to provide some audio data that AudioOutputDevice then writes into the 206 // shared memory section. 207 const int kMemorySize = CalculateMemorySize(); 208 209 EXPECT_CALL(*audio_output_ipc_, PlayStream()) 210 .WillOnce(SendPendingBytes(&browser_socket_, kMemorySize)); 211 212 // We expect calls to our audio renderer callback, which returns the number 213 // of frames written to the memory section. 214 // Here's the second place where it gets hacky: There's no way for us to 215 // know (without using a sleep loop!) when the AudioOutputDevice has finished 216 // writing the interleaved audio data into the shared memory section. 217 // So, for the sake of this test, we consider the call to Render a sign 218 // of success and quit the loop. 219 if (synchronized_io_) { 220 // For synchronized I/O, we expect RenderIO(). 221 EXPECT_CALL(callback_, RenderIO(_, _, _)) 222 .WillOnce(QuitLoop(io_loop_.message_loop_proxy())); 223 } else { 224 // For output only we expect Render(). 225 const int kNumberOfFramesToProcess = 0; 226 EXPECT_CALL(callback_, Render(_, _)) 227 .WillOnce(DoAll( 228 QuitLoop(io_loop_.message_loop_proxy()), 229 Return(kNumberOfFramesToProcess))); 230 } 231} 232 233void AudioOutputDeviceTest::WaitUntilRenderCallback() { 234 // Don't hang the test if we never get the Render() callback. 235 io_loop_.PostDelayedTask(FROM_HERE, base::MessageLoop::QuitClosure(), 236 TestTimeouts::action_timeout()); 237 io_loop_.Run(); 238} 239 240void AudioOutputDeviceTest::StopAudioDevice() { 241 audio_device_->Stop(); 242 243 EXPECT_CALL(*audio_output_ipc_, CloseStream()); 244 245 io_loop_.RunUntilIdle(); 246} 247 248TEST_P(AudioOutputDeviceTest, Initialize) { 249 // Tests that the object can be constructed, initialized and destructed 250 // without having ever been started/stopped. 251} 252 253// Calls Start() followed by an immediate Stop() and check for the basic message 254// filter messages being sent in that case. 255TEST_P(AudioOutputDeviceTest, StartStop) { 256 StartAudioDevice(); 257 StopAudioDevice(); 258} 259 260// AudioOutputDevice supports multiple start/stop sequences. 261TEST_P(AudioOutputDeviceTest, StartStopStartStop) { 262 StartAudioDevice(); 263 StopAudioDevice(); 264 StartAudioDevice(); 265 StopAudioDevice(); 266} 267 268// Simulate receiving OnStreamCreated() prior to processing ShutDownOnIOThread() 269// on the IO loop. 270TEST_P(AudioOutputDeviceTest, StopBeforeRender) { 271 StartAudioDevice(); 272 273 // Call Stop() but don't run the IO loop yet. 274 audio_device_->Stop(); 275 276 // Expect us to shutdown IPC but not to render anything despite the stream 277 // getting created. 278 EXPECT_CALL(*audio_output_ipc_, CloseStream()); 279 CreateStream(); 280} 281 282// Full test with output only. 283TEST_P(AudioOutputDeviceTest, CreateStream) { 284 StartAudioDevice(); 285 ExpectRenderCallback(); 286 CreateStream(); 287 WaitUntilRenderCallback(); 288 StopAudioDevice(); 289} 290 291INSTANTIATE_TEST_CASE_P(Render, AudioOutputDeviceTest, Values(false)); 292INSTANTIATE_TEST_CASE_P(RenderIO, AudioOutputDeviceTest, Values(true)); 293 294} // namespace media. 295