OLD | NEW |
| (Empty) |
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 "content/common/media/audio_messages.h" | |
14 #include "content/renderer/media/audio_device.h" | |
15 #include "content/renderer/media/audio_message_filter.h" | |
16 #include "media/audio/audio_util.h" | |
17 #include "media/audio/sample_rates.h" | |
18 #include "testing/gmock/include/gmock/gmock.h" | |
19 #include "testing/gmock_mutant.h" | |
20 #include "testing/gtest/include/gtest/gtest.h" | |
21 | |
22 using base::CancelableSyncSocket; | |
23 using base::SharedMemory; | |
24 using base::SyncSocket; | |
25 using testing::_; | |
26 using testing::DoAll; | |
27 using testing::Invoke; | |
28 using testing::Return; | |
29 using testing::WithArgs; | |
30 | |
31 namespace { | |
32 | |
33 class MockRenderCallback : public media::AudioRendererSink::RenderCallback { | |
34 public: | |
35 MockRenderCallback() {} | |
36 virtual ~MockRenderCallback() {} | |
37 | |
38 MOCK_METHOD3(Render, int(const std::vector<float*>& audio_data, | |
39 int number_of_frames, | |
40 int audio_delay_milliseconds)); | |
41 MOCK_METHOD0(OnRenderError, void()); | |
42 }; | |
43 | |
44 class MockAudioMessageFilter : public AudioMessageFilter { | |
45 public: | |
46 MockAudioMessageFilter() {} | |
47 | |
48 MOCK_METHOD2(CreateStream, | |
49 void(int stream_id, const media::AudioParameters& params)); | |
50 MOCK_METHOD1(PlayStream, void(int stream_id)); | |
51 MOCK_METHOD1(CloseStream, void(int stream_id)); | |
52 MOCK_METHOD2(SetVolume, void(int stream_id, double volume)); | |
53 MOCK_METHOD1(PauseStream, void(int stream_id)); | |
54 MOCK_METHOD1(FlushStream, void(int stream_id)); | |
55 | |
56 protected: | |
57 virtual ~MockAudioMessageFilter() {} | |
58 }; | |
59 | |
60 // Creates a copy of a SyncSocket handle that we can give to AudioDevice. | |
61 // On Windows this means duplicating the pipe handle so that AudioDevice can | |
62 // call CloseHandle() (since ownership has been transferred), but on other | |
63 // platforms, we just copy the same socket handle since AudioDevice on those | |
64 // platforms won't actually own the socket (FileDescriptor.auto_close is false). | |
65 bool DuplicateSocketHandle(SyncSocket::Handle socket_handle, | |
66 SyncSocket::Handle* copy) { | |
67 #if defined(OS_WIN) | |
68 HANDLE process = GetCurrentProcess(); | |
69 ::DuplicateHandle(process, socket_handle, process, copy, | |
70 0, FALSE, DUPLICATE_SAME_ACCESS); | |
71 return *copy != NULL; | |
72 #else | |
73 *copy = socket_handle; | |
74 return *copy != -1; | |
75 #endif | |
76 } | |
77 | |
78 ACTION_P2(SendPendingBytes, socket, pending_bytes) { | |
79 socket->Send(&pending_bytes, sizeof(pending_bytes)); | |
80 } | |
81 | |
82 // Used to terminate a loop from a different thread than the loop belongs to. | |
83 // |loop| should be a MessageLoopProxy. | |
84 ACTION_P(QuitLoop, loop) { | |
85 loop->PostTask(FROM_HERE, MessageLoop::QuitClosure()); | |
86 } | |
87 | |
88 // Zeros out |number_of_frames| in all channel buffers pointed to by | |
89 // the |audio_data| vector. | |
90 void ZeroAudioData(int number_of_frames, | |
91 const std::vector<float*>& audio_data) { | |
92 std::vector<float*>::const_iterator it = audio_data.begin(); | |
93 for (; it != audio_data.end(); ++it) { | |
94 float* channel = *it; | |
95 for (int j = 0; j < number_of_frames; ++j) { | |
96 channel[j] = 0.0f; | |
97 } | |
98 } | |
99 } | |
100 | |
101 } // namespace. | |
102 | |
103 class AudioDeviceTest : public testing::Test { | |
104 public: | |
105 AudioDeviceTest() | |
106 : default_audio_parameters_(media::AudioParameters::AUDIO_PCM_LINEAR, | |
107 CHANNEL_LAYOUT_STEREO, | |
108 48000, 16, 1024), | |
109 stream_id_(-1) { | |
110 } | |
111 | |
112 ~AudioDeviceTest() {} | |
113 | |
114 virtual void SetUp() OVERRIDE { | |
115 // This sets a global audio_message_filter pointer. AudioDevice will pick | |
116 // up a pointer to this variable via the static AudioMessageFilter::Get() | |
117 // method. | |
118 audio_message_filter_ = new MockAudioMessageFilter(); | |
119 } | |
120 | |
121 AudioDevice* CreateAudioDevice() { | |
122 return new AudioDevice( | |
123 audio_message_filter_, io_loop_.message_loop_proxy()); | |
124 } | |
125 | |
126 void set_stream_id(int stream_id) { stream_id_ = stream_id; } | |
127 | |
128 protected: | |
129 // Used to clean up TLS pointers that the test(s) will initialize. | |
130 // Must remain the first member of this class. | |
131 base::ShadowingAtExitManager at_exit_manager_; | |
132 MessageLoopForIO io_loop_; | |
133 const media::AudioParameters default_audio_parameters_; | |
134 MockRenderCallback callback_; | |
135 scoped_refptr<MockAudioMessageFilter> audio_message_filter_; | |
136 int stream_id_; | |
137 }; | |
138 | |
139 // The simplest test for AudioDevice. Used to test construction of AudioDevice | |
140 // and that the runtime environment is set up correctly. | |
141 TEST_F(AudioDeviceTest, Initialize) { | |
142 scoped_refptr<AudioDevice> audio_device(CreateAudioDevice()); | |
143 audio_device->Initialize(default_audio_parameters_, &callback_); | |
144 io_loop_.RunAllPending(); | |
145 } | |
146 | |
147 // Calls Start() followed by an immediate Stop() and check for the basic message | |
148 // filter messages being sent in that case. | |
149 TEST_F(AudioDeviceTest, StartStop) { | |
150 scoped_refptr<AudioDevice> audio_device(CreateAudioDevice()); | |
151 audio_device->Initialize(default_audio_parameters_, &callback_); | |
152 | |
153 audio_device->Start(); | |
154 audio_device->Stop(); | |
155 | |
156 EXPECT_CALL(*audio_message_filter_, CreateStream(_, _)); | |
157 EXPECT_CALL(*audio_message_filter_, CloseStream(_)); | |
158 | |
159 io_loop_.RunAllPending(); | |
160 } | |
161 | |
162 // Starts an audio stream, creates a shared memory section + SyncSocket pair | |
163 // that AudioDevice must use for audio data. It then sends a request for | |
164 // a single audio packet and quits when the packet has been sent. | |
165 TEST_F(AudioDeviceTest, CreateStream) { | |
166 scoped_refptr<AudioDevice> audio_device(CreateAudioDevice()); | |
167 audio_device->Initialize(default_audio_parameters_, &callback_); | |
168 | |
169 audio_device->Start(); | |
170 | |
171 EXPECT_CALL(*audio_message_filter_, CreateStream(_, _)) | |
172 .WillOnce(WithArgs<0>(Invoke(this, &AudioDeviceTest::set_stream_id))); | |
173 | |
174 EXPECT_EQ(stream_id_, -1); | |
175 io_loop_.RunAllPending(); | |
176 | |
177 // OnCreateStream() must have been called and we should have a valid | |
178 // stream id. | |
179 ASSERT_NE(stream_id_, -1); | |
180 | |
181 // This is where it gets a bit hacky. The shared memory contract between | |
182 // AudioDevice and its browser side counter part includes a bit more than | |
183 // just the audio data, so we must call TotalSharedMemorySizeInBytes() to get | |
184 // the actual size needed to fit the audio data plus the extra data. | |
185 int memory_size = media::TotalSharedMemorySizeInBytes( | |
186 default_audio_parameters_.GetBytesPerBuffer()); | |
187 SharedMemory shared_memory; | |
188 ASSERT_TRUE(shared_memory.CreateAndMapAnonymous(memory_size)); | |
189 memset(shared_memory.memory(), 0xff, memory_size); | |
190 | |
191 CancelableSyncSocket browser_socket, renderer_socket; | |
192 ASSERT_TRUE(CancelableSyncSocket::CreatePair(&browser_socket, | |
193 &renderer_socket)); | |
194 | |
195 // Create duplicates of the handles we pass to AudioDevice since ownership | |
196 // will be transferred and AudioDevice is responsible for freeing. | |
197 SyncSocket::Handle audio_device_socket = SyncSocket::kInvalidHandle; | |
198 ASSERT_TRUE(DuplicateSocketHandle(renderer_socket.handle(), | |
199 &audio_device_socket)); | |
200 base::SharedMemoryHandle duplicated_memory_handle; | |
201 ASSERT_TRUE(shared_memory.ShareToProcess(base::GetCurrentProcessHandle(), | |
202 &duplicated_memory_handle)); | |
203 | |
204 // We should get a 'play' notification when we call OnStreamCreated(). | |
205 // Respond by asking for some audio data. This should ask our callback | |
206 // to provide some audio data that AudioDevice then writes into the shared | |
207 // memory section. | |
208 EXPECT_CALL(*audio_message_filter_, PlayStream(stream_id_)) | |
209 .WillOnce(SendPendingBytes(&browser_socket, memory_size)); | |
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 AudioDevice 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 | |
219 // A note on the call to ZeroAudioData(): | |
220 // Valgrind caught a bug in AudioDevice::AudioThreadCallback::Process() | |
221 // whereby we always interleaved all the frames in the buffer regardless | |
222 // of how many were actually rendered. So to keep the benefits of that | |
223 // test, we explicitly pass 0 in here as the number of frames to | |
224 // ZeroAudioData(). Other tests might want to pass the requested number | |
225 // by using WithArgs<1, 0>(Invoke(&ZeroAudioData)) and set the return | |
226 // value accordingly. | |
227 const int kNumberOfFramesToProcess = 0; | |
228 | |
229 EXPECT_CALL(callback_, Render(_, _, _)) | |
230 .WillOnce(DoAll( | |
231 WithArgs<0>(Invoke( | |
232 testing::CreateFunctor(&ZeroAudioData, | |
233 kNumberOfFramesToProcess))), | |
234 QuitLoop(io_loop_.message_loop_proxy()), | |
235 Return(kNumberOfFramesToProcess))); | |
236 | |
237 audio_device->OnStreamCreated(duplicated_memory_handle, audio_device_socket, | |
238 memory_size); | |
239 | |
240 io_loop_.PostDelayedTask(FROM_HERE, MessageLoop::QuitClosure(), | |
241 TestTimeouts::action_timeout()); | |
242 io_loop_.Run(); | |
243 | |
244 // Close the stream sequence. | |
245 EXPECT_CALL(*audio_message_filter_, CloseStream(stream_id_)); | |
246 | |
247 audio_device->Stop(); | |
248 io_loop_.RunAllPending(); | |
249 } | |
OLD | NEW |