Upload upstream chromium 94.0.4606.31
[platform/framework/web/chromium-efl.git] / content / browser / renderer_host / media / render_frame_audio_input_stream_factory.cc
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.
4
5 #include "content/browser/renderer_host/media/render_frame_audio_input_stream_factory.h"
6
7 #include <cstdint>
8 #include <string>
9 #include <utility>
10
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"
38
39 using blink::mojom::MediaDeviceType;
40
41 namespace content {
42
43 namespace {
44
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)));
51   if (!source) {
52     // The source of the capture has already been destroyed, so fail early.
53     return nullptr;
54   }
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
57   // posted yet.
58   return source;
59 }
60
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));
68 }
69
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);
81       break;
82     }
83   }
84   // If we're unable to translate the device id, |cb| will not be run.
85 }
86
87 void GetSaltOriginAndPermissionsOnUIThread(
88     int process_id,
89     int frame_id,
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(
96       FROM_HERE,
97       base::BindOnce(std::move(cb), std::move(salt_and_origin), access));
98 }
99
100 }  // namespace
101
102 class RenderFrameAudioInputStreamFactory::Core final
103     : public blink::mojom::RendererAudioInputStreamFactory {
104  public:
105   Core(mojo::PendingReceiver<blink::mojom::RendererAudioInputStreamFactory>
106            receiver,
107        MediaStreamManager* media_stream_manager,
108        RenderFrameHost* render_frame_host);
109
110   ~Core() final;
111
112   void Init(mojo::PendingReceiver<blink::mojom::RendererAudioInputStreamFactory>
113                 receiver);
114
115   // mojom::RendererAudioInputStreamFactory implementation.
116   void CreateStream(
117       mojo::PendingRemote<blink::mojom::RendererAudioInputStreamFactoryClient>
118           client,
119       const base::UnguessableToken& session_id,
120       const media::AudioParameters& audio_params,
121       bool automatic_gain_control,
122       uint32_t shared_memory_count) final;
123
124   void AssociateInputAndOutputForAec(
125       const base::UnguessableToken& input_stream_id,
126       const std::string& output_device_id) final;
127
128   void CreateLoopbackStream(
129       mojo::PendingRemote<blink::mojom::RendererAudioInputStreamFactoryClient>
130           client,
131       const media::AudioParameters& audio_params,
132       uint32_t shared_memory_count,
133       bool disable_local_echo,
134       AudioStreamBroker::LoopbackSource* loopback_source);
135
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);
141
142   void AssociateTranslatedOutputDeviceForAec(
143       const base::UnguessableToken& input_stream_id,
144       const std::string& raw_output_device_id);
145
146   MediaStreamManager* const media_stream_manager_;
147   const int process_id_;
148   const int frame_id_;
149   const url::Origin origin_;
150
151   mojo::Receiver<RendererAudioInputStreamFactory> receiver_{this};
152   // Always null-check this weak pointer before dereferencing it.
153   base::WeakPtr<ForwardingAudioStreamFactory::Core> forwarding_factory_;
154
155   base::WeakPtrFactory<Core> weak_ptr_factory_{this};
156
157   DISALLOW_COPY_AND_ASSIGN(Core);
158 };
159
160 RenderFrameAudioInputStreamFactory::RenderFrameAudioInputStreamFactory(
161     mojo::PendingReceiver<blink::mojom::RendererAudioInputStreamFactory>
162         receiver,
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);
169 }
170
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
176   // same.
177   GetIOThreadTaskRunner({})->PostTask(
178       FROM_HERE,
179       base::BindOnce([](std::unique_ptr<Core>) {}, std::move(core_)));
180 }
181
182 RenderFrameAudioInputStreamFactory::Core::Core(
183     mojo::PendingReceiver<blink::mojom::RendererAudioInputStreamFactory>
184         receiver,
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);
192
193   ForwardingAudioStreamFactory::Core* tmp_factory =
194       ForwardingAudioStreamFactory::CoreForFrame(render_frame_host);
195
196   if (!tmp_factory) {
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.
200     return;
201   }
202
203   forwarding_factory_ = tmp_factory->AsWeakPtr();
204
205   // Unretained is safe since the destruction of |this| is posted to the IO
206   // thread.
207   GetIOThreadTaskRunner({})->PostTask(
208       FROM_HERE,
209       base::BindOnce(&Core::Init, base::Unretained(this), std::move(receiver)));
210 }
211
212 RenderFrameAudioInputStreamFactory::Core::~Core() {
213   DCHECK_CURRENTLY_ON(BrowserThread::IO);
214 }
215
216 void RenderFrameAudioInputStreamFactory::Core::Init(
217     mojo::PendingReceiver<blink::mojom::RendererAudioInputStreamFactory>
218         receiver) {
219   DCHECK_CURRENTLY_ON(BrowserThread::IO);
220   receiver_.Bind(std::move(receiver));
221 }
222
223 void RenderFrameAudioInputStreamFactory::Core::CreateStream(
224     mojo::PendingRemote<blink::mojom::RendererAudioInputStreamFactoryClient>
225         client,
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());
233
234   if (!forwarding_factory_)
235     return;
236
237   const blink::MediaStreamDevice* device =
238       media_stream_manager_->audio_input_device_manager()->GetOpenedDeviceById(
239           session_id);
240
241   if (!device) {
242     TRACE_EVENT_INSTANT0("audio", "device not found", TRACE_EVENT_SCOPE_THREAD);
243     return;
244   }
245
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
251     // the source audio.
252     // TODO(qiangchen): Analyze audio constraints to make a duplicating or
253     // diverting decision. It would give web developer more flexibility.
254
255     GetUIThreadTaskRunner({})->PostTaskAndReplyWithResult(
256         FROM_HERE,
257         base::BindOnce(&GetLoopbackSourceOnUIThread,
258                        capture_id.render_process_id,
259                        capture_id.main_render_frame_id),
260         base::BindOnce(
261             &RenderFrameAudioInputStreamFactory::Core::CreateLoopbackStream,
262             weak_ptr_factory_.GetWeakPtr(), std::move(client), audio_params,
263             shared_memory_count, capture_id.disable_local_echo));
264
265     if (device->type ==
266         blink::mojom::MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE)
267       IncrementDesktopCaptureCounter(SYSTEM_LOOPBACK_AUDIO_CAPTURER_CREATED);
268     return;
269   } else {
270     forwarding_factory_->CreateInputStream(
271         process_id_, frame_id_, device->id, audio_params, shared_memory_count,
272         automatic_gain_control, std::move(client));
273
274     // Only count for captures from desktop media picker dialog and system loop
275     // back audio.
276     if (device->type ==
277             blink::mojom::MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE &&
278         (media::AudioDeviceDescription::IsLoopbackDevice(device->id))) {
279       IncrementDesktopCaptureCounter(SYSTEM_LOOPBACK_AUDIO_CAPTURER_CREATED);
280     }
281   }
282 }
283
284 void RenderFrameAudioInputStreamFactory::Core::CreateLoopbackStream(
285     mojo::PendingRemote<blink::mojom::RendererAudioInputStreamFactoryClient>
286         client,
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_)
292     return;
293
294   forwarding_factory_->CreateLoopbackStream(
295       process_id_, frame_id_, loopback_source, audio_params,
296       shared_memory_count, disable_local_echo, std::move(client));
297 }
298
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))
304     return;
305
306   GetUIThreadTaskRunner({})->PostTask(
307       FROM_HERE,
308       base::BindOnce(
309           &GetSaltOriginAndPermissionsOnUIThread, process_id_, frame_id_,
310           base::BindOnce(
311               &Core::AssociateInputAndOutputForAecAfterCheckingAccess,
312               weak_ptr_factory_.GetWeakPtr(), input_stream_id,
313               output_device_id)));
314 }
315
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);
323
324   if (!forwarding_factory_ || !access_granted)
325     return;
326
327   if (media::AudioDeviceDescription::IsDefaultDevice(output_device_id) ||
328       media::AudioDeviceDescription::IsCommunicationsDevice(output_device_id)) {
329     forwarding_factory_->AssociateInputAndOutputForAec(input_stream_id,
330                                                        output_device_id);
331   } else {
332     EnumerateOutputDevices(
333         media_stream_manager_,
334         base::BindOnce(&TranslateDeviceId, output_device_id, salt_and_origin,
335                        base::BindRepeating(
336                            &RenderFrameAudioInputStreamFactory::Core::
337                                AssociateTranslatedOutputDeviceForAec,
338                            weak_ptr_factory_.GetWeakPtr(), input_stream_id)));
339   }
340 }
341
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_)
348     return;
349   forwarding_factory_->AssociateInputAndOutputForAec(input_stream_id,
350                                                      raw_output_device_id);
351 }
352
353 }  // namespace content