1 // Copyright 2018 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.
5 #include "content/browser/renderer_host/media/render_frame_audio_input_stream_factory.h"
11 #include "base/bind.h"
12 #include "base/callback.h"
13 #include "base/location.h"
14 #include "base/logging.h"
15 #include "base/memory/weak_ptr.h"
16 #include "base/task/post_task.h"
17 #include "base/trace_event/trace_event.h"
18 #include "base/unguessable_token.h"
19 #include "content/browser/media/audio_stream_broker.h"
20 #include "content/browser/media/capture/desktop_capture_device_uma_types.h"
21 #include "content/browser/media/forwarding_audio_stream_factory.h"
22 #include "content/browser/media/media_devices_permission_checker.h"
23 #include "content/browser/media/media_devices_util.h"
24 #include "content/browser/renderer_host/media/audio_input_device_manager.h"
25 #include "content/browser/renderer_host/media/media_devices_manager.h"
26 #include "content/browser/renderer_host/media/media_stream_manager.h"
27 #include "content/public/browser/browser_task_traits.h"
28 #include "content/public/browser/browser_thread.h"
29 #include "content/public/browser/media_device_id.h"
30 #include "content/public/browser/render_frame_host.h"
31 #include "content/public/browser/render_process_host.h"
32 #include "content/public/browser/web_contents_media_capture_id.h"
33 #include "media/audio/audio_device_description.h"
34 #include "media/base/audio_parameters.h"
35 #include "mojo/public/cpp/bindings/binding.h"
36 #include "services/audio/public/mojom/audio_processing.mojom.h"
37 #include "third_party/blink/public/common/mediastream/media_stream_request.h"
38 #include "url/origin.h"
44 AudioStreamBroker::LoopbackSource* GetLoopbackSourceOnUIThread(
45 int render_process_id,
46 int render_frame_id) {
47 DCHECK_CURRENTLY_ON(BrowserThread::UI);
48 auto* source = ForwardingAudioStreamFactory::CoreForFrame(
49 (RenderFrameHost::FromID(render_process_id, render_frame_id)));
51 // The source of the capture has already been destroyed, so fail early.
54 // Note: this pointer is sent over to the IO thread. This is safe since the
55 // destruction of |source| is posted to the IO thread and it hasn't been
60 void EnumerateOutputDevices(MediaStreamManager* media_stream_manager,
61 MediaDevicesManager::EnumerationCallback cb) {
62 DCHECK_CURRENTLY_ON(BrowserThread::IO);
63 MediaDevicesManager::BoolDeviceTypes device_types;
64 device_types[blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] = true;
65 media_stream_manager->media_devices_manager()->EnumerateDevices(
66 device_types, std::move(cb));
69 void TranslateDeviceId(const std::string& device_id,
70 const MediaDeviceSaltAndOrigin& salt_and_origin,
71 base::RepeatingCallback<void(const std::string&)> cb,
72 const MediaDeviceEnumeration& device_array) {
73 DCHECK_CURRENTLY_ON(BrowserThread::IO);
74 for (const auto& device_info :
75 device_array[blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT]) {
76 if (MediaStreamManager::DoesMediaDeviceIDMatchHMAC(
77 salt_and_origin.device_id_salt, salt_and_origin.origin, device_id,
78 device_info.device_id)) {
79 cb.Run(device_info.device_id);
83 // If we're unable to translate the device id, |cb| will not be run.
86 void GetSaltOriginAndPermissionsOnUIThread(
89 base::OnceCallback<void(MediaDeviceSaltAndOrigin salt_and_origin,
90 bool has_access)> cb) {
91 auto salt_and_origin = GetMediaDeviceSaltAndOrigin(process_id, frame_id);
92 bool access = MediaDevicesPermissionChecker().CheckPermissionOnUIThread(
93 blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT, process_id, frame_id);
94 base::PostTaskWithTraits(
95 FROM_HERE, {BrowserThread::IO},
96 base::BindOnce(std::move(cb), std::move(salt_and_origin), access));
101 class RenderFrameAudioInputStreamFactory::Core final
102 : public mojom::RendererAudioInputStreamFactory {
104 Core(mojom::RendererAudioInputStreamFactoryRequest request,
105 MediaStreamManager* media_stream_manager,
106 RenderFrameHost* render_frame_host);
110 void Init(mojom::RendererAudioInputStreamFactoryRequest request);
112 // mojom::RendererAudioInputStreamFactory implementation.
114 mojom::RendererAudioInputStreamFactoryClientPtr client,
116 const media::AudioParameters& audio_params,
117 bool automatic_gain_control,
118 uint32_t shared_memory_count,
119 audio::mojom::AudioProcessingConfigPtr processing_config) final;
121 void AssociateInputAndOutputForAec(
122 const base::UnguessableToken& input_stream_id,
123 const std::string& output_device_id) final;
125 void CreateLoopbackStream(
126 mojom::RendererAudioInputStreamFactoryClientPtr client,
127 const media::AudioParameters& audio_params,
128 uint32_t shared_memory_count,
129 bool disable_local_echo,
130 AudioStreamBroker::LoopbackSource* loopback_source);
132 void AssociateInputAndOutputForAecAfterCheckingAccess(
133 const base::UnguessableToken& input_stream_id,
134 const std::string& output_device_id,
135 MediaDeviceSaltAndOrigin salt_and_origin,
136 bool access_granted);
138 void AssociateTranslatedOutputDeviceForAec(
139 const base::UnguessableToken& input_stream_id,
140 const std::string& raw_output_device_id);
142 MediaStreamManager* const media_stream_manager_;
143 const int process_id_;
145 const url::Origin origin_;
147 mojo::Binding<RendererAudioInputStreamFactory> binding_;
148 // Always null-check this weak pointer before dereferencing it.
149 base::WeakPtr<ForwardingAudioStreamFactory::Core> forwarding_factory_;
151 base::WeakPtrFactory<Core> weak_ptr_factory_;
153 DISALLOW_COPY_AND_ASSIGN(Core);
156 RenderFrameAudioInputStreamFactory::RenderFrameAudioInputStreamFactory(
157 mojom::RendererAudioInputStreamFactoryRequest request,
158 MediaStreamManager* media_stream_manager,
159 RenderFrameHost* render_frame_host)
160 : core_(new Core(std::move(request),
161 media_stream_manager,
162 render_frame_host)) {
163 DCHECK_CURRENTLY_ON(BrowserThread::UI);
166 RenderFrameAudioInputStreamFactory::~RenderFrameAudioInputStreamFactory() {
167 DCHECK_CURRENTLY_ON(BrowserThread::UI);
168 // Ensure |core_| is deleted on the right thread. DeleteOnIOThread isn't used
169 // as it doesn't post in case it is already executed on the right thread. That
170 // causes issues in unit tests where the UI thread and the IO thread are the
172 base::PostTaskWithTraits(
173 FROM_HERE, {BrowserThread::IO},
174 base::BindOnce([](std::unique_ptr<Core>) {}, std::move(core_)));
177 RenderFrameAudioInputStreamFactory::Core::Core(
178 mojom::RendererAudioInputStreamFactoryRequest request,
179 MediaStreamManager* media_stream_manager,
180 RenderFrameHost* render_frame_host)
181 : media_stream_manager_(media_stream_manager),
182 process_id_(render_frame_host->GetProcess()->GetID()),
183 frame_id_(render_frame_host->GetRoutingID()),
184 origin_(render_frame_host->GetLastCommittedOrigin()),
186 weak_ptr_factory_(this) {
187 DCHECK_CURRENTLY_ON(BrowserThread::UI);
189 ForwardingAudioStreamFactory::Core* tmp_factory =
190 ForwardingAudioStreamFactory::CoreForFrame(render_frame_host);
193 // The only case when we not have a forwarding factory at this point is when
194 // the frame belongs to an interstitial. Interstitials don't need audio, so
195 // it's fine to drop the request.
199 forwarding_factory_ = tmp_factory->AsWeakPtr();
201 // Unretained is safe since the destruction of |this| is posted to the IO
203 base::PostTaskWithTraits(
204 FROM_HERE, {BrowserThread::IO},
205 base::BindOnce(&Core::Init, base::Unretained(this), std::move(request)));
208 RenderFrameAudioInputStreamFactory::Core::~Core() {
209 DCHECK_CURRENTLY_ON(BrowserThread::IO);
212 void RenderFrameAudioInputStreamFactory::Core::Init(
213 mojom::RendererAudioInputStreamFactoryRequest request) {
214 DCHECK_CURRENTLY_ON(BrowserThread::IO);
215 binding_.Bind(std::move(request));
218 void RenderFrameAudioInputStreamFactory::Core::CreateStream(
219 mojom::RendererAudioInputStreamFactoryClientPtr client,
221 const media::AudioParameters& audio_params,
222 bool automatic_gain_control,
223 uint32_t shared_memory_count,
224 audio::mojom::AudioProcessingConfigPtr processing_config) {
225 DCHECK_CURRENTLY_ON(BrowserThread::IO);
226 TRACE_EVENT1("audio", "RenderFrameAudioInputStreamFactory::CreateStream",
227 "session id", session_id);
229 if (!forwarding_factory_)
232 const blink::MediaStreamDevice* device =
233 media_stream_manager_->audio_input_device_manager()->GetOpenedDeviceById(
237 TRACE_EVENT_INSTANT0("audio", "device not found", TRACE_EVENT_SCOPE_THREAD);
241 WebContentsMediaCaptureId capture_id;
242 if (WebContentsMediaCaptureId::Parse(device->id, &capture_id)) {
243 // For MEDIA_GUM_DESKTOP_AUDIO_CAPTURE, the source is selected from
244 // picker window, we do not mute the source audio. For
245 // MEDIA_GUM_TAB_AUDIO_CAPTURE, the probable use case is Cast, we mute
247 // TODO(qiangchen): Analyze audio constraints to make a duplicating or
248 // diverting decision. It would give web developer more flexibility.
250 base::PostTaskWithTraitsAndReplyWithResult(
251 FROM_HERE, {BrowserThread::UI},
252 base::BindOnce(&GetLoopbackSourceOnUIThread,
253 capture_id.render_process_id,
254 capture_id.main_render_frame_id),
256 &RenderFrameAudioInputStreamFactory::Core::CreateLoopbackStream,
257 weak_ptr_factory_.GetWeakPtr(), std::move(client), audio_params,
258 shared_memory_count, capture_id.disable_local_echo));
260 if (device->type == blink::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE)
261 IncrementDesktopCaptureCounter(SYSTEM_LOOPBACK_AUDIO_CAPTURER_CREATED);
264 forwarding_factory_->CreateInputStream(
265 process_id_, frame_id_, device->id, audio_params, shared_memory_count,
266 automatic_gain_control, std::move(processing_config),
269 // Only count for captures from desktop media picker dialog and system loop
271 if (device->type == blink::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE &&
272 (media::AudioDeviceDescription::IsLoopbackDevice(device->id))) {
273 IncrementDesktopCaptureCounter(SYSTEM_LOOPBACK_AUDIO_CAPTURER_CREATED);
278 void RenderFrameAudioInputStreamFactory::Core::CreateLoopbackStream(
279 mojom::RendererAudioInputStreamFactoryClientPtr client,
280 const media::AudioParameters& audio_params,
281 uint32_t shared_memory_count,
282 bool disable_local_echo,
283 AudioStreamBroker::LoopbackSource* loopback_source) {
284 if (!loopback_source || !forwarding_factory_)
287 forwarding_factory_->CreateLoopbackStream(
288 process_id_, frame_id_, loopback_source, audio_params,
289 shared_memory_count, disable_local_echo, std::move(client));
292 void RenderFrameAudioInputStreamFactory::Core::AssociateInputAndOutputForAec(
293 const base::UnguessableToken& input_stream_id,
294 const std::string& output_device_id) {
295 DCHECK_CURRENTLY_ON(BrowserThread::IO);
296 if (!IsValidDeviceId(output_device_id))
299 base::PostTaskWithTraits(
300 FROM_HERE, {BrowserThread::UI},
302 &GetSaltOriginAndPermissionsOnUIThread, process_id_, frame_id_,
304 &Core::AssociateInputAndOutputForAecAfterCheckingAccess,
305 weak_ptr_factory_.GetWeakPtr(), input_stream_id,
309 void RenderFrameAudioInputStreamFactory::Core::
310 AssociateInputAndOutputForAecAfterCheckingAccess(
311 const base::UnguessableToken& input_stream_id,
312 const std::string& output_device_id,
313 MediaDeviceSaltAndOrigin salt_and_origin,
314 bool access_granted) {
315 DCHECK_CURRENTLY_ON(BrowserThread::IO);
317 if (!forwarding_factory_ || !access_granted)
320 if (media::AudioDeviceDescription::IsDefaultDevice(output_device_id) ||
321 media::AudioDeviceDescription::IsCommunicationsDevice(output_device_id)) {
322 forwarding_factory_->AssociateInputAndOutputForAec(input_stream_id,
325 EnumerateOutputDevices(
326 media_stream_manager_,
328 &TranslateDeviceId, output_device_id, salt_and_origin,
329 base::BindRepeating(&RenderFrameAudioInputStreamFactory::Core::
330 AssociateTranslatedOutputDeviceForAec,
331 weak_ptr_factory_.GetWeakPtr(),
336 void RenderFrameAudioInputStreamFactory::Core::
337 AssociateTranslatedOutputDeviceForAec(
338 const base::UnguessableToken& input_stream_id,
339 const std::string& raw_output_device_id) {
340 DCHECK_CURRENTLY_ON(BrowserThread::IO);
341 if (!forwarding_factory_)
343 forwarding_factory_->AssociateInputAndOutputForAec(input_stream_id,
344 raw_output_device_id);
347 } // namespace content