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