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