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