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 "content/test/webrtc_audio_device_test.h"
6
7#include "base/bind.h"
8#include "base/bind_helpers.h"
9#include "base/compiler_specific.h"
10#include "base/file_util.h"
11#include "base/message_loop/message_loop.h"
12#include "base/run_loop.h"
13#include "base/synchronization/waitable_event.h"
14#include "base/test/test_timeouts.h"
15#include "content/browser/media/media_internals.h"
16#include "content/browser/renderer_host/media/audio_input_renderer_host.h"
17#include "content/browser/renderer_host/media/audio_mirroring_manager.h"
18#include "content/browser/renderer_host/media/audio_renderer_host.h"
19#include "content/browser/renderer_host/media/media_stream_manager.h"
20#include "content/browser/renderer_host/media/mock_media_observer.h"
21#include "content/common/media/media_param_traits.h"
22#include "content/common/view_messages.h"
23#include "content/public/browser/browser_thread.h"
24#include "content/public/browser/resource_context.h"
25#include "content/public/common/content_paths.h"
26#include "content/public/test/test_browser_thread.h"
27#include "content/renderer/media/audio_input_message_filter.h"
28#include "content/renderer/media/audio_message_filter.h"
29#include "content/renderer/media/webrtc_audio_device_impl.h"
30#include "content/renderer/render_process.h"
31#include "content/renderer/render_thread_impl.h"
32#include "content/renderer/renderer_webkitplatformsupport_impl.h"
33#include "media/audio/audio_parameters.h"
34#include "media/base/audio_hardware_config.h"
35#include "net/url_request/url_request_test_util.h"
36#include "testing/gmock/include/gmock/gmock.h"
37#include "testing/gtest/include/gtest/gtest.h"
38#include "third_party/webrtc/voice_engine/include/voe_audio_processing.h"
39#include "third_party/webrtc/voice_engine/include/voe_base.h"
40#include "third_party/webrtc/voice_engine/include/voe_file.h"
41#include "third_party/webrtc/voice_engine/include/voe_network.h"
42
43#if defined(OS_WIN)
44#include "base/win/scoped_com_initializer.h"
45#endif
46
47using media::AudioParameters;
48using media::ChannelLayout;
49using testing::_;
50using testing::InvokeWithoutArgs;
51using testing::Return;
52using testing::StrEq;
53
54namespace content {
55
56// This class is a mock of the child process singleton which is needed
57// to be able to create a RenderThread object.
58class WebRTCMockRenderProcess : public RenderProcess {
59 public:
60  WebRTCMockRenderProcess() {}
61  virtual ~WebRTCMockRenderProcess() {}
62
63  // RenderProcess implementation.
64  virtual skia::PlatformCanvas* GetDrawingCanvas(
65      TransportDIB** memory, const gfx::Rect& rect) OVERRIDE {
66    return NULL;
67  }
68  virtual void ReleaseTransportDIB(TransportDIB* memory) OVERRIDE {}
69  virtual bool UseInProcessPlugins() const OVERRIDE { return false; }
70  virtual void AddBindings(int bindings) OVERRIDE {}
71  virtual int GetEnabledBindings() const OVERRIDE { return 0; }
72  virtual TransportDIB* CreateTransportDIB(size_t size) OVERRIDE {
73    return NULL;
74  }
75  virtual void FreeTransportDIB(TransportDIB*) OVERRIDE {}
76
77 private:
78  DISALLOW_COPY_AND_ASSIGN(WebRTCMockRenderProcess);
79};
80
81class TestAudioRendererHost : public AudioRendererHost {
82 public:
83  TestAudioRendererHost(
84      int render_process_id,
85      media::AudioManager* audio_manager,
86      AudioMirroringManager* mirroring_manager,
87      MediaInternals* media_internals,
88      MediaStreamManager* media_stream_manager,
89      IPC::Channel* channel)
90      : AudioRendererHost(render_process_id, audio_manager, mirroring_manager,
91                          media_internals, media_stream_manager),
92        channel_(channel) {}
93  virtual bool Send(IPC::Message* message) OVERRIDE {
94    if (channel_)
95      return channel_->Send(message);
96    return false;
97  }
98  void ResetChannel() {
99    channel_ = NULL;
100  }
101
102 protected:
103  virtual ~TestAudioRendererHost() {}
104
105 private:
106  IPC::Channel* channel_;
107};
108
109class TestAudioInputRendererHost : public AudioInputRendererHost {
110 public:
111  TestAudioInputRendererHost(
112      media::AudioManager* audio_manager,
113      MediaStreamManager* media_stream_manager,
114      AudioMirroringManager* audio_mirroring_manager,
115      media::UserInputMonitor* user_input_monitor,
116      IPC::Channel* channel)
117      : AudioInputRendererHost(audio_manager, media_stream_manager,
118                               audio_mirroring_manager, user_input_monitor),
119        channel_(channel) {}
120  virtual bool Send(IPC::Message* message) OVERRIDE {
121    if (channel_)
122      return channel_->Send(message);
123    return false;
124  }
125  void ResetChannel() {
126    channel_ = NULL;
127  }
128
129 protected:
130  virtual ~TestAudioInputRendererHost() {}
131
132 private:
133  IPC::Channel* channel_;
134};
135
136// Utility scoped class to replace the global content client's renderer for the
137// duration of the test.
138class ReplaceContentClientRenderer {
139 public:
140  explicit ReplaceContentClientRenderer(ContentRendererClient* new_renderer) {
141    saved_renderer_ = SetRendererClientForTesting(new_renderer);
142  }
143  ~ReplaceContentClientRenderer() {
144    // Restore the original renderer.
145    SetRendererClientForTesting(saved_renderer_);
146  }
147 private:
148  ContentRendererClient* saved_renderer_;
149  DISALLOW_COPY_AND_ASSIGN(ReplaceContentClientRenderer);
150};
151
152class MockRTCResourceContext : public ResourceContext {
153 public:
154  MockRTCResourceContext() : test_request_context_(NULL) {}
155  virtual ~MockRTCResourceContext() {}
156
157  void set_request_context(net::URLRequestContext* request_context) {
158    test_request_context_ = request_context;
159  }
160
161  // ResourceContext implementation:
162  virtual net::HostResolver* GetHostResolver() OVERRIDE {
163    return NULL;
164  }
165  virtual net::URLRequestContext* GetRequestContext() OVERRIDE {
166    return test_request_context_;
167  }
168
169  virtual bool AllowMicAccess(const GURL& origin) OVERRIDE {
170    return false;
171  }
172
173  virtual bool AllowCameraAccess(const GURL& origin) OVERRIDE {
174    return false;
175  }
176
177 private:
178  net::URLRequestContext* test_request_context_;
179
180  DISALLOW_COPY_AND_ASSIGN(MockRTCResourceContext);
181};
182
183ACTION_P(QuitMessageLoop, loop_or_proxy) {
184  loop_or_proxy->PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
185}
186
187MAYBE_WebRTCAudioDeviceTest::MAYBE_WebRTCAudioDeviceTest()
188    : render_thread_(NULL), audio_hardware_config_(NULL),
189      has_input_devices_(false), has_output_devices_(false) {
190}
191
192MAYBE_WebRTCAudioDeviceTest::~MAYBE_WebRTCAudioDeviceTest() {}
193
194void MAYBE_WebRTCAudioDeviceTest::SetUp() {
195  // This part sets up a RenderThread environment to ensure that
196  // RenderThread::current() (<=> TLS pointer) is valid.
197  // Main parts are inspired by the RenderViewFakeResourcesTest.
198  // Note that, the IPC part is not utilized in this test.
199  saved_content_renderer_.reset(
200      new ReplaceContentClientRenderer(&content_renderer_client_));
201  mock_process_.reset(new WebRTCMockRenderProcess());
202  ui_thread_.reset(
203      new TestBrowserThread(BrowserThread::UI, base::MessageLoop::current()));
204
205  // Construct the resource context on the UI thread.
206  resource_context_.reset(new MockRTCResourceContext);
207
208  static const char kThreadName[] = "RenderThread";
209  ChildProcess::current()->io_message_loop()->PostTask(FROM_HERE,
210      base::Bind(&MAYBE_WebRTCAudioDeviceTest::InitializeIOThread,
211                 base::Unretained(this), kThreadName));
212  WaitForIOThreadCompletion();
213
214  sandbox_was_enabled_ =
215      RendererWebKitPlatformSupportImpl::SetSandboxEnabledForTesting(false);
216  render_thread_ = new RenderThreadImpl(kThreadName);
217}
218
219void MAYBE_WebRTCAudioDeviceTest::TearDown() {
220  SetAudioHardwareConfig(NULL);
221
222  // Run any pending cleanup tasks that may have been posted to the main thread.
223  base::RunLoop().RunUntilIdle();
224
225  // Kick of the cleanup process by closing the channel. This queues up
226  // OnStreamClosed calls to be executed on the audio thread.
227  ChildProcess::current()->io_message_loop()->PostTask(FROM_HERE,
228      base::Bind(&MAYBE_WebRTCAudioDeviceTest::DestroyChannel,
229                 base::Unretained(this)));
230  WaitForIOThreadCompletion();
231
232  // When audio [input] render hosts are notified that the channel has
233  // been closed, they post tasks to the audio thread to close the
234  // AudioOutputController and once that's completed, a task is posted back to
235  // the IO thread to actually delete the AudioEntry for the audio stream. Only
236  // then is the reference to the audio manager released, so we wait for the
237  // whole thing to be torn down before we finally uninitialize the io thread.
238  WaitForAudioManagerCompletion();
239
240  ChildProcess::current()->io_message_loop()->PostTask(FROM_HERE,
241      base::Bind(&MAYBE_WebRTCAudioDeviceTest::UninitializeIOThread,
242                 base::Unretained((this))));
243  WaitForIOThreadCompletion();
244  mock_process_.reset();
245  media_stream_manager_.reset();
246  mirroring_manager_.reset();
247  RendererWebKitPlatformSupportImpl::SetSandboxEnabledForTesting(
248      sandbox_was_enabled_);
249}
250
251bool MAYBE_WebRTCAudioDeviceTest::Send(IPC::Message* message) {
252  return channel_->Send(message);
253}
254
255void MAYBE_WebRTCAudioDeviceTest::SetAudioHardwareConfig(
256    media::AudioHardwareConfig* hardware_config) {
257  audio_hardware_config_ = hardware_config;
258}
259
260scoped_refptr<WebRtcAudioRenderer>
261MAYBE_WebRTCAudioDeviceTest::CreateDefaultWebRtcAudioRenderer(
262    int render_view_id) {
263  media::AudioHardwareConfig* hardware_config =
264      RenderThreadImpl::current()->GetAudioHardwareConfig();
265  int sample_rate = hardware_config->GetOutputSampleRate();
266  int frames_per_buffer = hardware_config->GetOutputBufferSize();
267
268  return new WebRtcAudioRenderer(render_view_id, 0, sample_rate,
269                                 frames_per_buffer);
270}
271
272void MAYBE_WebRTCAudioDeviceTest::InitializeIOThread(const char* thread_name) {
273#if defined(OS_WIN)
274  // We initialize COM (STA) on our IO thread as is done in Chrome.
275  // See BrowserProcessSubThread::Init.
276  initialize_com_.reset(new base::win::ScopedCOMInitializer());
277#endif
278
279  // Set the current thread as the IO thread.
280  io_thread_.reset(
281      new TestBrowserThread(BrowserThread::IO, base::MessageLoop::current()));
282
283  // Populate our resource context.
284  test_request_context_.reset(new net::TestURLRequestContext());
285  MockRTCResourceContext* resource_context =
286      static_cast<MockRTCResourceContext*>(resource_context_.get());
287  resource_context->set_request_context(test_request_context_.get());
288
289  // Create our own AudioManager, AudioMirroringManager and MediaStreamManager.
290  audio_manager_.reset(media::AudioManager::CreateForTesting());
291  mirroring_manager_.reset(new AudioMirroringManager());
292  media_stream_manager_.reset(new MediaStreamManager(audio_manager_.get()));
293
294  has_input_devices_ = audio_manager_->HasAudioInputDevices();
295  has_output_devices_ = audio_manager_->HasAudioOutputDevices();
296
297  // Create an IPC channel that handles incoming messages on the IO thread.
298  CreateChannel(thread_name);
299}
300
301void MAYBE_WebRTCAudioDeviceTest::UninitializeIOThread() {
302  resource_context_.reset();
303
304  test_request_context_.reset();
305
306#if defined(OS_WIN)
307  initialize_com_.reset();
308#endif
309
310  audio_manager_.reset();
311}
312
313void MAYBE_WebRTCAudioDeviceTest::CreateChannel(const char* name) {
314  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
315
316  channel_.reset(new IPC::Channel(name, IPC::Channel::MODE_SERVER, this));
317  ASSERT_TRUE(channel_->Connect());
318
319  static const int kRenderProcessId = 1;
320  audio_render_host_ = new TestAudioRendererHost(kRenderProcessId,
321                                                 audio_manager_.get(),
322                                                 mirroring_manager_.get(),
323                                                 MediaInternals::GetInstance(),
324                                                 media_stream_manager_.get(),
325                                                 channel_.get());
326  audio_render_host_->set_peer_pid_for_testing(base::GetCurrentProcId());
327
328  audio_input_renderer_host_ =
329      new TestAudioInputRendererHost(audio_manager_.get(),
330                                     media_stream_manager_.get(),
331                                     mirroring_manager_.get(),
332                                     NULL,
333                                     channel_.get());
334  audio_input_renderer_host_->set_peer_pid_for_testing(
335      base::GetCurrentProcId());
336}
337
338void MAYBE_WebRTCAudioDeviceTest::DestroyChannel() {
339  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
340  audio_render_host_->OnChannelClosing();
341  audio_render_host_->OnFilterRemoved();
342  audio_input_renderer_host_->OnChannelClosing();
343  audio_input_renderer_host_->OnFilterRemoved();
344  audio_render_host_->ResetChannel();
345  audio_input_renderer_host_->ResetChannel();
346  channel_.reset();
347  audio_render_host_ = NULL;
348  audio_input_renderer_host_ = NULL;
349}
350
351void MAYBE_WebRTCAudioDeviceTest::OnGetAudioHardwareConfig(
352    AudioParameters* input_params, AudioParameters* output_params) {
353  ASSERT_TRUE(audio_hardware_config_);
354  *input_params = audio_hardware_config_->GetInputConfig();
355  *output_params = audio_hardware_config_->GetOutputConfig();
356}
357
358// IPC::Listener implementation.
359bool MAYBE_WebRTCAudioDeviceTest::OnMessageReceived(
360    const IPC::Message& message) {
361  if (render_thread_) {
362    IPC::ChannelProxy::MessageFilter* filter =
363        render_thread_->audio_input_message_filter();
364    if (filter->OnMessageReceived(message))
365      return true;
366
367    filter = render_thread_->audio_message_filter();
368    if (filter->OnMessageReceived(message))
369      return true;
370  }
371
372  if (audio_render_host_.get()) {
373    bool message_was_ok = false;
374    if (audio_render_host_->OnMessageReceived(message, &message_was_ok))
375      return true;
376  }
377
378  if (audio_input_renderer_host_.get()) {
379    bool message_was_ok = false;
380    if (audio_input_renderer_host_->OnMessageReceived(message, &message_was_ok))
381      return true;
382  }
383
384  bool handled ALLOW_UNUSED = true;
385  bool message_is_ok = true;
386  IPC_BEGIN_MESSAGE_MAP_EX(MAYBE_WebRTCAudioDeviceTest, message, message_is_ok)
387    IPC_MESSAGE_HANDLER(ViewHostMsg_GetAudioHardwareConfig,
388                        OnGetAudioHardwareConfig)
389    IPC_MESSAGE_UNHANDLED(handled = false)
390  IPC_END_MESSAGE_MAP_EX()
391
392  EXPECT_TRUE(message_is_ok);
393
394  return true;
395}
396
397// Posts a final task to the IO message loop and waits for completion.
398void MAYBE_WebRTCAudioDeviceTest::WaitForIOThreadCompletion() {
399  WaitForMessageLoopCompletion(
400      ChildProcess::current()->io_message_loop()->message_loop_proxy().get());
401}
402
403void MAYBE_WebRTCAudioDeviceTest::WaitForAudioManagerCompletion() {
404  if (audio_manager_)
405    WaitForMessageLoopCompletion(audio_manager_->GetMessageLoop().get());
406}
407
408void MAYBE_WebRTCAudioDeviceTest::WaitForMessageLoopCompletion(
409    base::MessageLoopProxy* loop) {
410  base::WaitableEvent* event = new base::WaitableEvent(false, false);
411  loop->PostTask(FROM_HERE, base::Bind(&base::WaitableEvent::Signal,
412                 base::Unretained(event)));
413  if (event->TimedWait(TestTimeouts::action_max_timeout())) {
414    delete event;
415  } else {
416    // Don't delete the event object in case the message ever gets processed.
417    // If we do, we will crash the test process.
418    ADD_FAILURE() << "Failed to wait for message loop";
419  }
420}
421
422std::string MAYBE_WebRTCAudioDeviceTest::GetTestDataPath(
423    const base::FilePath::StringType& file_name) {
424  base::FilePath path;
425  EXPECT_TRUE(PathService::Get(DIR_TEST_DATA, &path));
426  path = path.Append(file_name);
427  EXPECT_TRUE(base::PathExists(path));
428#if defined(OS_WIN)
429  return WideToUTF8(path.value());
430#else
431  return path.value();
432#endif
433}
434
435WebRTCTransportImpl::WebRTCTransportImpl(webrtc::VoENetwork* network)
436    : network_(network) {
437}
438
439WebRTCTransportImpl::~WebRTCTransportImpl() {}
440
441int WebRTCTransportImpl::SendPacket(int channel, const void* data, int len) {
442  return network_->ReceivedRTPPacket(channel, data, len);
443}
444
445int WebRTCTransportImpl::SendRTCPPacket(int channel, const void* data,
446                                        int len) {
447  return network_->ReceivedRTCPPacket(channel, data, len);
448}
449
450}  // namespace content
451