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