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/renderer/pepper/pepper_platform_audio_input.h"
6 
7 #include "base/bind.h"
8 #include "base/check_op.h"
9 #include "base/location.h"
10 #include "base/single_thread_task_runner.h"
11 #include "build/build_config.h"
12 #include "content/child/child_process.h"
13 #include "content/renderer/pepper/pepper_audio_input_host.h"
14 #include "content/renderer/pepper/pepper_media_device_manager.h"
15 #include "content/renderer/render_frame_impl.h"
16 #include "content/renderer/render_thread_impl.h"
17 #include "content/renderer/render_view_impl.h"
18 #include "media/audio/audio_device_description.h"
19 #include "media/audio/audio_source_parameters.h"
20 #include "ppapi/shared_impl/ppb_audio_config_shared.h"
21 #include "third_party/blink/public/platform/task_type.h"
22 #include "third_party/blink/public/web/modules/media/audio/web_audio_input_ipc_factory.h"
23 #include "third_party/blink/public/web/web_local_frame.h"
24 
25 namespace content {
26 
27 // static
Create(int render_frame_id,const std::string & device_id,int sample_rate,int frames_per_buffer,PepperAudioInputHost * client)28 PepperPlatformAudioInput* PepperPlatformAudioInput::Create(
29     int render_frame_id,
30     const std::string& device_id,
31     int sample_rate,
32     int frames_per_buffer,
33     PepperAudioInputHost* client) {
34   scoped_refptr<PepperPlatformAudioInput> audio_input(
35       new PepperPlatformAudioInput());
36   if (audio_input->Initialize(render_frame_id,
37                               device_id,
38                               sample_rate,
39                               frames_per_buffer,
40                               client)) {
41     // Balanced by Release invoked in
42     // PepperPlatformAudioInput::ShutDownOnIOThread().
43     audio_input->AddRef();
44     return audio_input.get();
45   }
46   return nullptr;
47 }
48 
StartCapture()49 void PepperPlatformAudioInput::StartCapture() {
50   DCHECK(main_task_runner_->BelongsToCurrentThread());
51 
52   io_task_runner_->PostTask(
53       FROM_HERE,
54       base::BindOnce(&PepperPlatformAudioInput::StartCaptureOnIOThread, this));
55 }
56 
StopCapture()57 void PepperPlatformAudioInput::StopCapture() {
58   DCHECK(main_task_runner_->BelongsToCurrentThread());
59 
60   io_task_runner_->PostTask(
61       FROM_HERE,
62       base::BindOnce(&PepperPlatformAudioInput::StopCaptureOnIOThread, this));
63 }
64 
ShutDown()65 void PepperPlatformAudioInput::ShutDown() {
66   DCHECK(main_task_runner_->BelongsToCurrentThread());
67 
68   // Make sure we don't call shutdown more than once.
69   if (!client_)
70     return;
71 
72   // Called on the main thread to stop all audio callbacks. We must only change
73   // the client on the main thread, and the delegates from the I/O thread.
74   client_ = nullptr;
75   io_task_runner_->PostTask(
76       FROM_HERE,
77       base::BindOnce(&PepperPlatformAudioInput::ShutDownOnIOThread, this));
78 }
79 
OnStreamCreated(base::ReadOnlySharedMemoryRegion shared_memory_region,base::SyncSocket::ScopedHandle socket_handle,bool initially_muted)80 void PepperPlatformAudioInput::OnStreamCreated(
81     base::ReadOnlySharedMemoryRegion shared_memory_region,
82     base::SyncSocket::ScopedHandle socket_handle,
83     bool initially_muted) {
84   DCHECK(shared_memory_region.IsValid());
85 #if defined(OS_WIN)
86   DCHECK(socket_handle.IsValid());
87 #else
88   DCHECK(socket_handle.is_valid());
89 #endif
90   DCHECK_GT(shared_memory_region.GetSize(), 0u);
91 
92   // If we're not on the main thread then bounce over to it. Don't use
93   // base::ThreadTaskRunnerHandle::Get() as |main_task_runner_| will never
94   // match that. See crbug.com/1150822.
95   if (!main_task_runner_->BelongsToCurrentThread()) {
96     // If shutdown has occurred, |client_| will be NULL and the handles will be
97     // cleaned up on the main thread.
98     main_task_runner_->PostTask(
99         FROM_HERE, base::BindOnce(&PepperPlatformAudioInput::OnStreamCreated,
100                                   this, std::move(shared_memory_region),
101                                   std::move(socket_handle), initially_muted));
102   } else {
103     // Must dereference the client only on the main thread. Shutdown may have
104     // occurred while the request was in-flight, so we need to NULL check.
105     if (client_) {
106       client_->StreamCreated(std::move(shared_memory_region),
107                              std::move(socket_handle));
108     }
109   }
110 }
111 
OnError()112 void PepperPlatformAudioInput::OnError() {}
113 
OnMuted(bool is_muted)114 void PepperPlatformAudioInput::OnMuted(bool is_muted) {}
115 
OnIPCClosed()116 void PepperPlatformAudioInput::OnIPCClosed() { ipc_.reset(); }
117 
~PepperPlatformAudioInput()118 PepperPlatformAudioInput::~PepperPlatformAudioInput() {
119   // Make sure we have been shut down. Warning: this may happen on the I/O
120   // thread!
121   // Although these members should be accessed on a specific thread (either the
122   // main thread or the I/O thread), it should be fine to examine their value
123   // here.
124   DCHECK(!ipc_);
125   DCHECK(!client_);
126   DCHECK(label_.empty());
127   DCHECK(!pending_open_device_);
128 }
129 
PepperPlatformAudioInput()130 PepperPlatformAudioInput::PepperPlatformAudioInput()
131     : io_task_runner_(ChildProcess::current()->io_task_runner()) {}
132 
Initialize(int render_frame_id,const std::string & device_id,int sample_rate,int frames_per_buffer,PepperAudioInputHost * client)133 bool PepperPlatformAudioInput::Initialize(
134     int render_frame_id,
135     const std::string& device_id,
136     int sample_rate,
137     int frames_per_buffer,
138     PepperAudioInputHost* client) {
139   RenderFrameImpl* const render_frame =
140       RenderFrameImpl::FromRoutingID(render_frame_id);
141   if (!render_frame || !client)
142     return false;
143 
144   main_task_runner_ =
145       render_frame->GetTaskRunner(blink::TaskType::kInternalMediaRealTime);
146 
147   render_frame_id_ = render_frame_id;
148   render_frame_token_ = render_frame->GetWebFrame()->GetLocalFrameToken();
149   client_ = client;
150 
151   if (!GetMediaDeviceManager())
152     return false;
153 
154 
155   params_.Reset(media::AudioParameters::AUDIO_PCM_LINEAR,
156                 media::CHANNEL_LAYOUT_MONO,
157                 sample_rate,
158                 frames_per_buffer);
159 
160   // We need to open the device and obtain the label and session ID before
161   // initializing.
162   pending_open_device_id_ = GetMediaDeviceManager()->OpenDevice(
163       PP_DEVICETYPE_DEV_AUDIOCAPTURE,
164       device_id.empty() ? media::AudioDeviceDescription::kDefaultDeviceId
165                         : device_id,
166       client->pp_instance(),
167       base::BindOnce(&PepperPlatformAudioInput::OnDeviceOpened, this));
168   pending_open_device_ = true;
169 
170   return true;
171 }
172 
InitializeOnIOThread(const base::UnguessableToken & session_id)173 void PepperPlatformAudioInput::InitializeOnIOThread(
174     const base::UnguessableToken& session_id) {
175   DCHECK(io_task_runner_->BelongsToCurrentThread());
176 
177   if (ipc_startup_state_ != kStopped)
178     ipc_ = blink::WebAudioInputIPCFactory::GetInstance().CreateAudioInputIPC(
179         render_frame_token_, media::AudioSourceParameters(session_id));
180   if (!ipc_)
181     return;
182 
183   // We will be notified by OnStreamCreated().
184   create_stream_sent_ = true;
185   ipc_->CreateStream(this, params_, false, 1);
186 
187   if (ipc_startup_state_ == kStarted)
188     ipc_->RecordStream();
189 }
190 
StartCaptureOnIOThread()191 void PepperPlatformAudioInput::StartCaptureOnIOThread() {
192   DCHECK(io_task_runner_->BelongsToCurrentThread());
193 
194   if (!ipc_) {
195     ipc_startup_state_ = kStarted;
196     return;
197   }
198 
199   ipc_->RecordStream();
200 }
201 
StopCaptureOnIOThread()202 void PepperPlatformAudioInput::StopCaptureOnIOThread() {
203   DCHECK(io_task_runner_->BelongsToCurrentThread());
204 
205   if (!ipc_) {
206     ipc_startup_state_ = kStopped;
207     return;
208   }
209 
210   // TODO(yzshen): We cannot re-start capturing if the stream is closed.
211   if (ipc_ && create_stream_sent_) {
212     ipc_->CloseStream();
213   }
214   ipc_.reset();
215 }
216 
ShutDownOnIOThread()217 void PepperPlatformAudioInput::ShutDownOnIOThread() {
218   DCHECK(io_task_runner_->BelongsToCurrentThread());
219 
220   StopCaptureOnIOThread();
221 
222   main_task_runner_->PostTask(
223       FROM_HERE, base::BindOnce(&PepperPlatformAudioInput::CloseDevice, this));
224 
225   Release();  // Release for the delegate, balances out the reference taken in
226               // PepperPlatformAudioInput::Create.
227 }
228 
OnDeviceOpened(int request_id,bool succeeded,const std::string & label)229 void PepperPlatformAudioInput::OnDeviceOpened(int request_id,
230                                               bool succeeded,
231                                               const std::string& label) {
232   DCHECK(main_task_runner_->BelongsToCurrentThread());
233 
234   pending_open_device_ = false;
235   pending_open_device_id_ = -1;
236 
237   PepperMediaDeviceManager* const device_manager = GetMediaDeviceManager();
238   if (succeeded && device_manager) {
239     DCHECK(!label.empty());
240     label_ = label;
241 
242     if (client_) {
243       base::UnguessableToken session_id =
244           device_manager->GetSessionID(PP_DEVICETYPE_DEV_AUDIOCAPTURE, label);
245       io_task_runner_->PostTask(
246           FROM_HERE,
247           base::BindOnce(&PepperPlatformAudioInput::InitializeOnIOThread, this,
248                          session_id));
249     } else {
250       // Shutdown has occurred.
251       CloseDevice();
252     }
253   } else {
254     NotifyStreamCreationFailed();
255   }
256 }
257 
CloseDevice()258 void PepperPlatformAudioInput::CloseDevice() {
259   DCHECK(main_task_runner_->BelongsToCurrentThread());
260 
261   if (!label_.empty()) {
262     PepperMediaDeviceManager* const device_manager = GetMediaDeviceManager();
263     if (device_manager)
264       device_manager->CloseDevice(label_);
265     label_.clear();
266   }
267   if (pending_open_device_) {
268     PepperMediaDeviceManager* const device_manager = GetMediaDeviceManager();
269     if (device_manager)
270       device_manager->CancelOpenDevice(pending_open_device_id_);
271     pending_open_device_ = false;
272     pending_open_device_id_ = -1;
273   }
274 }
275 
NotifyStreamCreationFailed()276 void PepperPlatformAudioInput::NotifyStreamCreationFailed() {
277   DCHECK(main_task_runner_->BelongsToCurrentThread());
278 
279   if (client_)
280     client_->StreamCreationFailed();
281 }
282 
GetMediaDeviceManager()283 PepperMediaDeviceManager* PepperPlatformAudioInput::GetMediaDeviceManager() {
284   DCHECK(main_task_runner_->BelongsToCurrentThread());
285 
286   RenderFrameImpl* const render_frame =
287       RenderFrameImpl::FromRoutingID(render_frame_id_);
288   return render_frame
289              ? PepperMediaDeviceManager::GetForRenderFrame(render_frame).get()
290              : nullptr;
291 }
292 
293 }  // namespace content
294