Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(136)

Side by Side Diff: content/browser/renderer_host/media/renderer_audio_output_stream_factory_context_impl_unittest.cc

Issue 2788173002: Revert of Add mojo interface+impl creation of audio streams. (Closed)
Patch Set: Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 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 "content/browser/renderer_host/media/renderer_audio_output_stream_facto ry_context_impl.h"
6
7 #include <utility>
8
9 #include "base/bind.h"
10 #include "base/memory/shared_memory.h"
11 #include "base/memory/shared_memory_handle.h"
12 #include "base/run_loop.h"
13 #include "base/sync_socket.h"
14 #include "cc/base/math_util.h"
15 #include "content/browser/audio_manager_thread.h"
16 #include "content/browser/renderer_host/media/media_stream_manager.h"
17 #include "content/common/media/renderer_audio_output_stream_factory.mojom.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/test/mock_render_process_host.h"
20 #include "content/public/test/test_browser_context.h"
21 #include "content/public/test/test_browser_thread_bundle.h"
22 #include "media/audio/audio_manager_base.h"
23 #include "media/audio/audio_output_controller.h"
24 #include "media/audio/audio_system_impl.h"
25 #include "media/audio/fake_audio_log_factory.h"
26 #include "media/audio/simple_sources.h"
27 #include "media/base/audio_parameters.h"
28 #include "media/base/media_switches.h"
29 #include "mojo/public/cpp/bindings/binding.h"
30 #include "mojo/public/cpp/system/platform_handle.h"
31 #include "testing/gmock/include/gmock/gmock.h"
32 #include "testing/gtest/include/gtest/gtest.h"
33
34 namespace content {
35
36 namespace {
37
38 using testing::_;
39 using testing::StrictMock;
40 using testing::Return;
41 using testing::Test;
42 using AudioOutputStreamFactory = mojom::RendererAudioOutputStreamFactory;
43 using AudioOutputStreamFactoryPtr =
44 mojo::InterfacePtr<AudioOutputStreamFactory>;
45 using AudioOutputStreamFactoryRequest =
46 mojo::InterfaceRequest<AudioOutputStreamFactory>;
47 using AudioOutputStream = media::mojom::AudioOutputStream;
48 using AudioOutputStreamPtr = mojo::InterfacePtr<AudioOutputStream>;
49 using AudioOutputStreamRequest = mojo::InterfaceRequest<AudioOutputStream>;
50 using AudioOutputStreamProvider = media::mojom::AudioOutputStreamProvider;
51 using AudioOutputStreamProviderPtr =
52 mojo::InterfacePtr<AudioOutputStreamProvider>;
53 using AudioOutputStreamProviderRequest =
54 mojo::InterfaceRequest<AudioOutputStreamProvider>;
55
56 const int kRenderProcessId = 42;
57 const int kRenderFrameId = 24;
58 const int kNoSessionId = 0;
59 const float kWaveFrequency = 440.f;
60 const int kChannels = 1;
61 const int kBuffers = 1000;
62 const int kSampleFrequency = 44100;
63 const int kBitsPerSample = 16;
64 const int kSamplesPerBuffer = kSampleFrequency / 100;
65 const char kSalt[] = "salt";
66
67 std::unique_ptr<media::AudioOutputStream::AudioSourceCallback>
68 GetTestAudioSource() {
69 return base::MakeUnique<media::SineWaveAudioSource>(kChannels, kWaveFrequency,
70 kSampleFrequency);
71 }
72
73 media::AudioParameters GetTestAudioParameters() {
74 return media::AudioParameters(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
75 media::CHANNEL_LAYOUT_MONO, kSampleFrequency,
76 kBitsPerSample, kSamplesPerBuffer);
77 }
78
79 void SyncWith(scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
80 CHECK(task_runner);
81 CHECK(!task_runner->BelongsToCurrentThread());
82 base::WaitableEvent e = {base::WaitableEvent::ResetPolicy::MANUAL,
83 base::WaitableEvent::InitialState::NOT_SIGNALED};
84 task_runner->PostTask(FROM_HERE, base::Bind(&base::WaitableEvent::Signal,
85 base::Unretained(&e)));
86 e.Wait();
87 }
88
89 void SyncWithAllThreads() {
90 DCHECK_CURRENTLY_ON(BrowserThread::UI);
91 // New tasks might be posted while we are syncing, but in every iteration at
92 // least one task will be run. 20 iterations should be enough for our code.
93 for (int i = 0; i < 20; ++i) {
94 {
95 base::MessageLoop::ScopedNestableTaskAllower allower(
96 base::MessageLoop::current());
97 base::RunLoop().RunUntilIdle();
98 }
99 SyncWith(BrowserThread::GetTaskRunnerForThread(BrowserThread::IO));
100 SyncWith(media::AudioManager::Get()->GetWorkerTaskRunner());
101 }
102 }
103
104 class MockAudioManager : public media::AudioManagerBase {
105 public:
106 MockAudioManager(
107 scoped_refptr<base::SingleThreadTaskRunner> task_runner,
108 scoped_refptr<base::SingleThreadTaskRunner> worker_task_runner,
109 media::AudioLogFactory* audio_log_factory)
110 : media::AudioManagerBase(task_runner,
111 worker_task_runner,
112 audio_log_factory) {
113 ON_CALL(*this, HasAudioOutputDevices()).WillByDefault(Return(true));
114 }
115
116 ~MockAudioManager() override { Shutdown(); }
117
118 MOCK_METHOD0(HasAudioOutputDevices, bool());
119 MOCK_METHOD0(HasAudioInputDevices, bool());
120 MOCK_METHOD0(GetName, const char*());
121
122 MOCK_METHOD2(MakeLinearOutputStream,
123 media::AudioOutputStream*(const media::AudioParameters& params,
124 const LogCallback& log_callback));
125 MOCK_METHOD3(MakeLowLatencyOutputStream,
126 media::AudioOutputStream*(const media::AudioParameters& params,
127 const std::string& device_id,
128 const LogCallback& log_callback));
129 MOCK_METHOD3(MakeLinearInputStream,
130 media::AudioInputStream*(const media::AudioParameters& params,
131 const std::string& device_id,
132 const LogCallback& log_callback));
133 MOCK_METHOD3(MakeLowLatencyInputStream,
134 media::AudioInputStream*(const media::AudioParameters& params,
135 const std::string& device_id,
136 const LogCallback& log_callback));
137 MOCK_METHOD2(GetPreferredOutputStreamParameters,
138 media::AudioParameters(const std::string& device_id,
139 const media::AudioParameters& params));
140 };
141
142 class MockAudioOutputStream : public media::AudioOutputStream,
143 public base::PlatformThread::Delegate {
144 public:
145 explicit MockAudioOutputStream(MockAudioManager* audio_manager)
146 : done_(base::WaitableEvent::ResetPolicy::MANUAL,
147 base::WaitableEvent::InitialState::NOT_SIGNALED),
148 audio_manager_(audio_manager) {}
149
150 ~MockAudioOutputStream() override {
151 base::PlatformThread::Join(thread_handle_);
152 }
153
154 void Start(AudioSourceCallback* callback) override {
155 callback_ = callback;
156 EXPECT_TRUE(base::PlatformThread::CreateWithPriority(
157 0, this, &thread_handle_, base::ThreadPriority::REALTIME_AUDIO));
158 }
159
160 void Stop() override {
161 done_.Wait();
162 callback_ = nullptr;
163 }
164
165 bool Open() override { return true; }
166 void SetVolume(double volume) override {}
167 void GetVolume(double* volume) override { *volume = 1; }
168 void Close() override {
169 Stop();
170 audio_manager_->ReleaseOutputStream(this);
171 }
172
173 void ThreadMain() override {
174 std::unique_ptr<media::AudioOutputStream::AudioSourceCallback>
175 expected_audio = GetTestAudioSource();
176 media::AudioParameters params = GetTestAudioParameters();
177 std::unique_ptr<media::AudioBus> dest = media::AudioBus::Create(params);
178 std::unique_ptr<media::AudioBus> expected_buffer =
179 media::AudioBus::Create(params);
180 for (int i = 0; i < kBuffers; ++i) {
181 expected_audio->OnMoreData(base::TimeDelta(), base::TimeTicks::Now(), 0,
182 expected_buffer.get());
183 callback_->OnMoreData(base::TimeDelta(), base::TimeTicks::Now(), 0,
184 dest.get());
185 for (int frame = 0; frame < params.frames_per_buffer(); ++frame) {
186 // Using EXPECT here causes massive log spam in case of a broken test,
187 // and ASSERT causes it to hang, so we use CHECK.
188 CHECK(cc::MathUtil::IsNearlyTheSameForTesting(
189 expected_buffer->channel(0)[frame], dest->channel(0)[frame]))
190 << "Got " << dest->channel(0)[frame] << ", expected "
191 << expected_buffer->channel(0)[frame];
192 }
193 }
194 done_.Signal();
195 }
196
197 private:
198 base::OnceClosure sync_closure_;
199 base::PlatformThreadHandle thread_handle_;
200 base::WaitableEvent done_;
201 MockAudioManager* audio_manager_;
202 AudioSourceCallback* callback_;
203 };
204
205 void AuthCallback(base::OnceClosure sync_closure,
206 media::OutputDeviceStatus* status_out,
207 media::AudioParameters* params_out,
208 std::string* id_out,
209 media::OutputDeviceStatus status,
210 const media::AudioParameters& params,
211 const std::string& id) {
212 *status_out = status;
213 *params_out = params;
214 *id_out = id;
215 std::move(sync_closure).Run();
216 }
217
218 // "Renderer-side" audio client. Provides the signal given by
219 // GetTestAudioSource() from a dedicated thread when given sync socket and
220 // shared memory.
221 // TODO(maxmorin): Replace with an instance of the real client, when it exists.
222 class TestIPCClient : public base::PlatformThread::Delegate {
223 public:
224 TestIPCClient() {}
225
226 ~TestIPCClient() override { base::PlatformThread::Join(thread_handle_); }
227
228 // Starts thread, sets up IPC primitives and sends signal on thread.
229 void Start(mojo::ScopedSharedBufferHandle shared_buffer,
230 mojo::ScopedHandle socket_handle) {
231 EXPECT_TRUE(socket_handle.is_valid());
232 // Set up socket.
233 base::PlatformFile fd;
234 mojo::UnwrapPlatformFile(std::move(socket_handle), &fd);
235 socket_ = base::MakeUnique<base::CancelableSyncSocket>(fd);
236 EXPECT_NE(socket_->handle(), base::CancelableSyncSocket::kInvalidHandle);
237
238 // Set up memory.
239 EXPECT_TRUE(shared_buffer.is_valid());
240 size_t memory_length;
241 base::SharedMemoryHandle shmem_handle;
242 bool read_only;
243 EXPECT_EQ(
244 mojo::UnwrapSharedMemoryHandle(std::move(shared_buffer), &shmem_handle,
245 &memory_length, &read_only),
246 MOJO_RESULT_OK);
247 EXPECT_EQ(memory_length, sizeof(media::AudioOutputBufferParameters) +
248 media::AudioBus::CalculateMemorySize(
249 GetTestAudioParameters()));
250 EXPECT_EQ(read_only, false);
251 memory_ = base::MakeUnique<base::SharedMemory>(shmem_handle, read_only);
252 EXPECT_TRUE(memory_->Map(memory_length));
253
254 EXPECT_TRUE(base::PlatformThread::CreateWithPriority(
255 0, this, &thread_handle_, base::ThreadPriority::REALTIME_AUDIO));
256 }
257
258 void ThreadMain() override {
259 std::unique_ptr<media::AudioOutputStream::AudioSourceCallback>
260 audio_source = GetTestAudioSource();
261
262 media::AudioOutputBuffer* buffer =
263 reinterpret_cast<media::AudioOutputBuffer*>(memory_->memory());
264 std::unique_ptr<media::AudioBus> output_bus =
265 media::AudioBus::WrapMemory(GetTestAudioParameters(), buffer->audio);
266
267 // Send s.
268 for (uint32_t i = 0; i < kBuffers;) {
269 uint32_t pending_data = 0;
270 size_t bytes_read = socket_->Receive(&pending_data, sizeof(pending_data));
271 // Use check here, since there's a risk of hangs in case of a bug.
272 PCHECK(sizeof(pending_data) == bytes_read)
273 << "Tried to read " << sizeof(pending_data) << " bytes but only read "
274 << bytes_read << " bytes";
275 CHECK_EQ(0u, pending_data);
276
277 ++i;
278 audio_source->OnMoreData(base::TimeDelta(), base::TimeTicks(), 0,
279 output_bus.get());
280
281 size_t bytes_written = socket_->Send(&i, sizeof(i));
282 PCHECK(sizeof(pending_data) == bytes_written)
283 << "Tried to write " << sizeof(pending_data)
284 << " bytes but only wrote " << bytes_written << " bytes";
285 }
286 }
287
288 private:
289 base::PlatformThreadHandle thread_handle_;
290 std::unique_ptr<base::CancelableSyncSocket> socket_;
291 std::unique_ptr<base::SharedMemory> memory_;
292 };
293
294 } // namespace
295
296 // TODO(maxmorin): Add test for play, pause and set volume.
297 class RendererAudioOutputStreamFactoryIntegrationTest : public Test {
298 public:
299 RendererAudioOutputStreamFactoryIntegrationTest()
300 : media_stream_manager_(),
301 thread_bundle_(TestBrowserThreadBundle::Options::REAL_IO_THREAD),
302 audio_thread_(),
303 log_factory_(),
304 audio_manager_(new MockAudioManager(audio_thread_.task_runner(),
305 audio_thread_.worker_task_runner(),
306 &log_factory_)),
307 audio_system_(media::AudioSystemImpl::Create(audio_manager_.get())) {
308 media_stream_manager_ =
309 base::MakeUnique<MediaStreamManager>(audio_system_.get());
310 }
311
312 void CreateAndBindFactory(AudioOutputStreamFactoryRequest request) {
313 factory_context_.reset(new RendererAudioOutputStreamFactoryContextImpl(
314 kRenderProcessId, audio_system_.get(), audio_manager_.get(),
315 media_stream_manager_.get(), kSalt));
316 factory_context_->CreateFactory(kRenderFrameId, std::move(request));
317 }
318
319 std::unique_ptr<MediaStreamManager> media_stream_manager_;
320 TestBrowserThreadBundle thread_bundle_;
321 AudioManagerThread audio_thread_;
322 media::FakeAudioLogFactory log_factory_;
323 media::ScopedAudioManagerPtr audio_manager_;
324 std::unique_ptr<media::AudioSystem> audio_system_;
325 std::unique_ptr<RendererAudioOutputStreamFactoryContextImpl,
326 BrowserThread::DeleteOnIOThread>
327 factory_context_;
328 };
329
330 TEST_F(RendererAudioOutputStreamFactoryIntegrationTest, StreamIntegrationTest) {
331 // Sets up the factory on the IO thread and runs client code on the UI thread.
332 // Send a sine wave from the client and makes sure it's received by the output
333 // stream.
334 MockAudioOutputStream* stream = new MockAudioOutputStream(
335 static_cast<MockAudioManager*>(audio_manager_.get()));
336
337 // Make sure the mock audio manager uses our mock stream.
338 EXPECT_CALL(*static_cast<MockAudioManager*>(audio_manager_.get()),
339 MakeLowLatencyOutputStream(_, "", _))
340 .WillOnce(Return(stream));
341 EXPECT_CALL(*static_cast<MockAudioManager*>(audio_manager_.get()),
342 GetPreferredOutputStreamParameters(_, _))
343 .WillRepeatedly(Return(GetTestAudioParameters()));
344
345 AudioOutputStreamFactoryPtr factory_ptr;
346 BrowserThread::PostTask(
347 BrowserThread::IO, FROM_HERE,
348 base::Bind(&RendererAudioOutputStreamFactoryIntegrationTest::
349 CreateAndBindFactory,
350 base::Unretained(this),
351 base::Passed(mojo::MakeRequest(&factory_ptr))));
352
353 AudioOutputStreamProviderPtr provider_ptr;
354 base::RunLoop loop;
355 media::OutputDeviceStatus status;
356 media::AudioParameters params;
357 std::string id;
358 factory_ptr->RequestDeviceAuthorization(
359 mojo::MakeRequest(&provider_ptr), kNoSessionId, "default",
360 base::Bind(&AuthCallback, base::Passed(loop.QuitWhenIdleClosure()),
361 base::Unretained(&status), base::Unretained(&params),
362 base::Unretained(&id)));
363 loop.Run();
364 ASSERT_EQ(status, media::OUTPUT_DEVICE_STATUS_OK);
365 ASSERT_EQ(GetTestAudioParameters().AsHumanReadableString(),
366 params.AsHumanReadableString());
367 ASSERT_TRUE(id.empty());
368
369 AudioOutputStreamPtr stream_ptr;
370 {
371 TestIPCClient client;
372 provider_ptr->Acquire(
373 mojo::MakeRequest(&stream_ptr), params,
374 base::Bind(&TestIPCClient::Start, base::Unretained(&client)));
375 SyncWithAllThreads();
376 stream_ptr->Play();
377 SyncWithAllThreads();
378 } // Joining client thread.
379 stream_ptr.reset();
380 SyncWithAllThreads();
381 }
382
383 } // namespace content
OLDNEW
« no previous file with comments | « content/browser/renderer_host/media/renderer_audio_output_stream_factory_context_impl.cc ('k') | content/common/BUILD.gn » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698