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/check_op.h"
14 #include "base/location.h"
15 #include "base/memory/weak_ptr.h"
16 #include "base/trace_event/trace_event.h"
17 #include "base/unguessable_token.h"
18 #include "content/browser/media/audio_stream_broker.h"
19 #include "content/browser/media/capture/desktop_capture_device_uma_types.h"
20 #include "content/browser/media/forwarding_audio_stream_factory.h"
21 #include "content/browser/media/media_devices_permission_checker.h"
22 #include "content/browser/media/media_devices_util.h"
23 #include "content/browser/renderer_host/media/audio_input_device_manager.h"
24 #include "content/browser/renderer_host/media/media_devices_manager.h"
25 #include "content/browser/renderer_host/media/media_stream_manager.h"
26 #include "content/public/browser/browser_task_traits.h"
27 #include "content/public/browser/browser_thread.h"
28 #include "content/public/browser/media_device_id.h"
29 #include "content/public/browser/render_frame_host.h"
30 #include "content/public/browser/render_process_host.h"
31 #include "content/public/browser/web_contents_media_capture_id.h"
32 #include "media/audio/audio_device_description.h"
33 #include "media/base/audio_parameters.h"
34 #include "mojo/public/cpp/bindings/pending_remote.h"
35 #include "mojo/public/cpp/bindings/receiver.h"
36 #include "third_party/blink/public/common/mediastream/media_stream_request.h"
37 #include "url/origin.h"
39 using blink::mojom::MediaDeviceType;
45 AudioStreamBroker::LoopbackSource* GetLoopbackSourceOnUIThread(
46 int render_process_id,
47 int render_frame_id) {
48 DCHECK_CURRENTLY_ON(BrowserThread::UI);
49 auto* source = ForwardingAudioStreamFactory::CoreForFrame(
50 (RenderFrameHost::FromID(render_process_id, render_frame_id)));
52 // The source of the capture has already been destroyed, so fail early.
55 // Note: this pointer is sent over to the IO thread. This is safe since the
56 // destruction of |source| is posted to the IO thread and it hasn't been
61 void EnumerateOutputDevices(MediaStreamManager* media_stream_manager,
62 MediaDevicesManager::EnumerationCallback cb) {
63 DCHECK_CURRENTLY_ON(BrowserThread::IO);
64 MediaDevicesManager::BoolDeviceTypes device_types;
65 device_types[static_cast<size_t>(MediaDeviceType::MEDIA_AUDIO_OUTPUT)] = true;
66 media_stream_manager->media_devices_manager()->EnumerateDevices(
67 device_types, std::move(cb));
70 void TranslateDeviceId(const std::string& device_id,
71 const MediaDeviceSaltAndOrigin& salt_and_origin,
72 base::RepeatingCallback<void(const std::string&)> cb,
73 const MediaDeviceEnumeration& device_array) {
74 DCHECK_CURRENTLY_ON(BrowserThread::IO);
75 for (const auto& device_info :
76 device_array[static_cast<size_t>(MediaDeviceType::MEDIA_AUDIO_OUTPUT)]) {
77 if (MediaStreamManager::DoesMediaDeviceIDMatchHMAC(
78 salt_and_origin.device_id_salt, salt_and_origin.origin, device_id,
79 device_info.device_id)) {
80 cb.Run(device_info.device_id);
84 // If we're unable to translate the device id, |cb| will not be run.
87 void GetSaltOriginAndPermissionsOnUIThread(
90 base::OnceCallback<void(MediaDeviceSaltAndOrigin salt_and_origin,
91 bool has_access)> cb) {
92 auto salt_and_origin = GetMediaDeviceSaltAndOrigin(process_id, frame_id);
93 bool access = MediaDevicesPermissionChecker().CheckPermissionOnUIThread(
94 MediaDeviceType::MEDIA_AUDIO_OUTPUT, process_id, frame_id);
95 GetIOThreadTaskRunner({})->PostTask(
97 base::BindOnce(std::move(cb), std::move(salt_and_origin), access));
102 class RenderFrameAudioInputStreamFactory::Core final
103 : public blink::mojom::RendererAudioInputStreamFactory {
105 Core(mojo::PendingReceiver<blink::mojom::RendererAudioInputStreamFactory>
107 MediaStreamManager* media_stream_manager,
108 RenderFrameHost* render_frame_host);
112 void Init(mojo::PendingReceiver<blink::mojom::RendererAudioInputStreamFactory>
115 // mojom::RendererAudioInputStreamFactory implementation.
117 mojo::PendingRemote<blink::mojom::RendererAudioInputStreamFactoryClient>
119 const base::UnguessableToken& session_id,
120 const media::AudioParameters& audio_params,
121 bool automatic_gain_control,
122 uint32_t shared_memory_count) final;
124 void AssociateInputAndOutputForAec(
125 const base::UnguessableToken& input_stream_id,
126 const std::string& output_device_id) final;
128 void CreateLoopbackStream(
129 mojo::PendingRemote<blink::mojom::RendererAudioInputStreamFactoryClient>
131 const media::AudioParameters& audio_params,
132 uint32_t shared_memory_count,
133 bool disable_local_echo,
134 AudioStreamBroker::LoopbackSource* loopback_source);
136 void AssociateInputAndOutputForAecAfterCheckingAccess(
137 const base::UnguessableToken& input_stream_id,
138 const std::string& output_device_id,
139 MediaDeviceSaltAndOrigin salt_and_origin,
140 bool access_granted);
142 void AssociateTranslatedOutputDeviceForAec(
143 const base::UnguessableToken& input_stream_id,
144 const std::string& raw_output_device_id);
146 MediaStreamManager* const media_stream_manager_;
147 const int process_id_;
149 const url::Origin origin_;
151 mojo::Receiver<RendererAudioInputStreamFactory> receiver_{this};
152 // Always null-check this weak pointer before dereferencing it.
153 base::WeakPtr<ForwardingAudioStreamFactory::Core> forwarding_factory_;
155 base::WeakPtrFactory<Core> weak_ptr_factory_{this};
157 DISALLOW_COPY_AND_ASSIGN(Core);
160 RenderFrameAudioInputStreamFactory::RenderFrameAudioInputStreamFactory(
161 mojo::PendingReceiver<blink::mojom::RendererAudioInputStreamFactory>
163 MediaStreamManager* media_stream_manager,
164 RenderFrameHost* render_frame_host)
165 : core_(new Core(std::move(receiver),
166 media_stream_manager,
167 render_frame_host)) {
168 DCHECK_CURRENTLY_ON(BrowserThread::UI);
171 RenderFrameAudioInputStreamFactory::~RenderFrameAudioInputStreamFactory() {
172 DCHECK_CURRENTLY_ON(BrowserThread::UI);
173 // Ensure |core_| is deleted on the right thread. DeleteOnIOThread isn't used
174 // as it doesn't post in case it is already executed on the right thread. That
175 // causes issues in unit tests where the UI thread and the IO thread are the
177 GetIOThreadTaskRunner({})->PostTask(
179 base::BindOnce([](std::unique_ptr<Core>) {}, std::move(core_)));
182 RenderFrameAudioInputStreamFactory::Core::Core(
183 mojo::PendingReceiver<blink::mojom::RendererAudioInputStreamFactory>
185 MediaStreamManager* media_stream_manager,
186 RenderFrameHost* render_frame_host)
187 : media_stream_manager_(media_stream_manager),
188 process_id_(render_frame_host->GetProcess()->GetID()),
189 frame_id_(render_frame_host->GetRoutingID()),
190 origin_(render_frame_host->GetLastCommittedOrigin()) {
191 DCHECK_CURRENTLY_ON(BrowserThread::UI);
193 ForwardingAudioStreamFactory::Core* tmp_factory =
194 ForwardingAudioStreamFactory::CoreForFrame(render_frame_host);
197 // The only case when we not have a forwarding factory at this point is when
198 // the frame belongs to an interstitial. Interstitials don't need audio, so
199 // it's fine to drop the receiver.
203 forwarding_factory_ = tmp_factory->AsWeakPtr();
205 // Unretained is safe since the destruction of |this| is posted to the IO
207 GetIOThreadTaskRunner({})->PostTask(
209 base::BindOnce(&Core::Init, base::Unretained(this), std::move(receiver)));
212 RenderFrameAudioInputStreamFactory::Core::~Core() {
213 DCHECK_CURRENTLY_ON(BrowserThread::IO);
216 void RenderFrameAudioInputStreamFactory::Core::Init(
217 mojo::PendingReceiver<blink::mojom::RendererAudioInputStreamFactory>
219 DCHECK_CURRENTLY_ON(BrowserThread::IO);
220 receiver_.Bind(std::move(receiver));
223 void RenderFrameAudioInputStreamFactory::Core::CreateStream(
224 mojo::PendingRemote<blink::mojom::RendererAudioInputStreamFactoryClient>
226 const base::UnguessableToken& session_id,
227 const media::AudioParameters& audio_params,
228 bool automatic_gain_control,
229 uint32_t shared_memory_count) {
230 DCHECK_CURRENTLY_ON(BrowserThread::IO);
231 TRACE_EVENT1("audio", "RenderFrameAudioInputStreamFactory::CreateStream",
232 "session id", session_id.ToString());
234 if (!forwarding_factory_)
237 const blink::MediaStreamDevice* device =
238 media_stream_manager_->audio_input_device_manager()->GetOpenedDeviceById(
242 TRACE_EVENT_INSTANT0("audio", "device not found", TRACE_EVENT_SCOPE_THREAD);
246 WebContentsMediaCaptureId capture_id;
247 if (WebContentsMediaCaptureId::Parse(device->id, &capture_id)) {
248 // For MEDIA_GUM_DESKTOP_AUDIO_CAPTURE, the source is selected from
249 // picker window, we do not mute the source audio. For
250 // MEDIA_GUM_TAB_AUDIO_CAPTURE, the probable use case is Cast, we mute
252 // TODO(qiangchen): Analyze audio constraints to make a duplicating or
253 // diverting decision. It would give web developer more flexibility.
255 GetUIThreadTaskRunner({})->PostTaskAndReplyWithResult(
257 base::BindOnce(&GetLoopbackSourceOnUIThread,
258 capture_id.render_process_id,
259 capture_id.main_render_frame_id),
261 &RenderFrameAudioInputStreamFactory::Core::CreateLoopbackStream,
262 weak_ptr_factory_.GetWeakPtr(), std::move(client), audio_params,
263 shared_memory_count, capture_id.disable_local_echo));
266 blink::mojom::MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE)
267 IncrementDesktopCaptureCounter(SYSTEM_LOOPBACK_AUDIO_CAPTURER_CREATED);
270 forwarding_factory_->CreateInputStream(
271 process_id_, frame_id_, device->id, audio_params, shared_memory_count,
272 automatic_gain_control, std::move(client));
274 // Only count for captures from desktop media picker dialog and system loop
277 blink::mojom::MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE &&
278 (media::AudioDeviceDescription::IsLoopbackDevice(device->id))) {
279 IncrementDesktopCaptureCounter(SYSTEM_LOOPBACK_AUDIO_CAPTURER_CREATED);
284 void RenderFrameAudioInputStreamFactory::Core::CreateLoopbackStream(
285 mojo::PendingRemote<blink::mojom::RendererAudioInputStreamFactoryClient>
287 const media::AudioParameters& audio_params,
288 uint32_t shared_memory_count,
289 bool disable_local_echo,
290 AudioStreamBroker::LoopbackSource* loopback_source) {
291 if (!loopback_source || !forwarding_factory_)
294 forwarding_factory_->CreateLoopbackStream(
295 process_id_, frame_id_, loopback_source, audio_params,
296 shared_memory_count, disable_local_echo, std::move(client));
299 void RenderFrameAudioInputStreamFactory::Core::AssociateInputAndOutputForAec(
300 const base::UnguessableToken& input_stream_id,
301 const std::string& output_device_id) {
302 DCHECK_CURRENTLY_ON(BrowserThread::IO);
303 if (!IsValidDeviceId(output_device_id))
306 GetUIThreadTaskRunner({})->PostTask(
309 &GetSaltOriginAndPermissionsOnUIThread, process_id_, frame_id_,
311 &Core::AssociateInputAndOutputForAecAfterCheckingAccess,
312 weak_ptr_factory_.GetWeakPtr(), input_stream_id,
316 void RenderFrameAudioInputStreamFactory::Core::
317 AssociateInputAndOutputForAecAfterCheckingAccess(
318 const base::UnguessableToken& input_stream_id,
319 const std::string& output_device_id,
320 MediaDeviceSaltAndOrigin salt_and_origin,
321 bool access_granted) {
322 DCHECK_CURRENTLY_ON(BrowserThread::IO);
324 if (!forwarding_factory_ || !access_granted)
327 if (media::AudioDeviceDescription::IsDefaultDevice(output_device_id) ||
328 media::AudioDeviceDescription::IsCommunicationsDevice(output_device_id)) {
329 forwarding_factory_->AssociateInputAndOutputForAec(input_stream_id,
332 EnumerateOutputDevices(
333 media_stream_manager_,
334 base::BindOnce(&TranslateDeviceId, output_device_id, salt_and_origin,
336 &RenderFrameAudioInputStreamFactory::Core::
337 AssociateTranslatedOutputDeviceForAec,
338 weak_ptr_factory_.GetWeakPtr(), input_stream_id)));
342 void RenderFrameAudioInputStreamFactory::Core::
343 AssociateTranslatedOutputDeviceForAec(
344 const base::UnguessableToken& input_stream_id,
345 const std::string& raw_output_device_id) {
346 DCHECK_CURRENTLY_ON(BrowserThread::IO);
347 if (!forwarding_factory_)
349 forwarding_factory_->AssociateInputAndOutputForAec(input_stream_id,
350 raw_output_device_id);
353 } // namespace content