Upload upstream chromium 71.0.3578.0
[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/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 "content/public/common/media_stream_request.h"
34 #include "media/audio/audio_device_description.h"
35 #include "media/base/audio_parameters.h"
36 #include "mojo/public/cpp/bindings/binding.h"
37 #include "services/audio/public/mojom/audio_processing.mojom.h"
38 #include "url/origin.h"
39
40 namespace content {
41
42 namespace {
43
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)));
50   if (!source) {
51     // The source of the capture has already been destroyed, so fail early.
52     return nullptr;
53   }
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
56   // posted yet.
57   return source;
58 }
59
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[MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] = true;
65   media_stream_manager->media_devices_manager()->EnumerateDevices(
66       device_types, std::move(cb));
67 }
68
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 : device_array[MEDIA_DEVICE_TYPE_AUDIO_OUTPUT]) {
75     if (MediaStreamManager::DoesMediaDeviceIDMatchHMAC(
76             salt_and_origin.device_id_salt, salt_and_origin.origin, device_id,
77             device_info.device_id)) {
78       cb.Run(device_info.device_id);
79       break;
80     }
81   }
82   // If we're unable to translate the device id, |cb| will not be run.
83 }
84
85 void GetSaltOriginAndPermissionsOnUIThread(
86     int process_id,
87     int frame_id,
88     base::OnceCallback<void(MediaDeviceSaltAndOrigin salt_and_origin,
89                             bool has_access)> cb) {
90   auto salt_and_origin = GetMediaDeviceSaltAndOrigin(process_id, frame_id);
91   bool access = MediaDevicesPermissionChecker().CheckPermissionOnUIThread(
92       MEDIA_DEVICE_TYPE_AUDIO_OUTPUT, process_id, frame_id);
93   base::PostTaskWithTraits(
94       FROM_HERE, {BrowserThread::IO},
95       base::BindOnce(std::move(cb), std::move(salt_and_origin), access));
96 }
97
98 }  // namespace
99
100 class RenderFrameAudioInputStreamFactory::Core final
101     : public mojom::RendererAudioInputStreamFactory {
102  public:
103   Core(mojom::RendererAudioInputStreamFactoryRequest request,
104        MediaStreamManager* media_stream_manager,
105        RenderFrameHost* render_frame_host);
106
107   ~Core() final;
108
109   void Init(mojom::RendererAudioInputStreamFactoryRequest request);
110
111   // mojom::RendererAudioInputStreamFactory implementation.
112   void CreateStream(
113       mojom::RendererAudioInputStreamFactoryClientPtr client,
114       int32_t session_id,
115       const media::AudioParameters& audio_params,
116       bool automatic_gain_control,
117       uint32_t shared_memory_count,
118       audio::mojom::AudioProcessingConfigPtr processing_config) final;
119
120   void AssociateInputAndOutputForAec(
121       const base::UnguessableToken& input_stream_id,
122       const std::string& output_device_id) final;
123
124   void CreateLoopbackStream(
125       mojom::RendererAudioInputStreamFactoryClientPtr client,
126       const media::AudioParameters& audio_params,
127       uint32_t shared_memory_count,
128       bool disable_local_echo,
129       AudioStreamBroker::LoopbackSource* loopback_source);
130
131   void AssociateInputAndOutputForAecAfterCheckingAccess(
132       const base::UnguessableToken& input_stream_id,
133       const std::string& output_device_id,
134       MediaDeviceSaltAndOrigin salt_and_origin,
135       bool access_granted);
136
137   void AssociateTranslatedOutputDeviceForAec(
138       const base::UnguessableToken& input_stream_id,
139       const std::string& raw_output_device_id);
140
141   MediaStreamManager* const media_stream_manager_;
142   const int process_id_;
143   const int frame_id_;
144   const url::Origin origin_;
145
146   mojo::Binding<RendererAudioInputStreamFactory> binding_;
147   ForwardingAudioStreamFactory::Core* forwarding_factory_;
148
149   base::WeakPtrFactory<Core> weak_ptr_factory_;
150
151   DISALLOW_COPY_AND_ASSIGN(Core);
152 };
153
154 RenderFrameAudioInputStreamFactory::RenderFrameAudioInputStreamFactory(
155     mojom::RendererAudioInputStreamFactoryRequest request,
156     MediaStreamManager* media_stream_manager,
157     RenderFrameHost* render_frame_host)
158     : core_(new Core(std::move(request),
159                      media_stream_manager,
160                      render_frame_host)) {
161   DCHECK_CURRENTLY_ON(BrowserThread::UI);
162 }
163
164 RenderFrameAudioInputStreamFactory::~RenderFrameAudioInputStreamFactory() {
165   DCHECK_CURRENTLY_ON(BrowserThread::UI);
166   // Ensure |core_| is deleted on the right thread. DeleteOnIOThread isn't used
167   // as it doesn't post in case it is already executed on the right thread. That
168   // causes issues in unit tests where the UI thread and the IO thread are the
169   // same.
170   base::PostTaskWithTraits(
171       FROM_HERE, {BrowserThread::IO},
172       base::BindOnce([](std::unique_ptr<Core>) {}, std::move(core_)));
173 }
174
175 RenderFrameAudioInputStreamFactory::Core::Core(
176     mojom::RendererAudioInputStreamFactoryRequest request,
177     MediaStreamManager* media_stream_manager,
178     RenderFrameHost* render_frame_host)
179     : media_stream_manager_(media_stream_manager),
180       process_id_(render_frame_host->GetProcess()->GetID()),
181       frame_id_(render_frame_host->GetRoutingID()),
182       origin_(render_frame_host->GetLastCommittedOrigin()),
183       binding_(this),
184       forwarding_factory_(
185           ForwardingAudioStreamFactory::CoreForFrame(render_frame_host)),
186       weak_ptr_factory_(this) {
187   DCHECK_CURRENTLY_ON(BrowserThread::UI);
188
189   if (!forwarding_factory_) {
190     // The only case when we not have a forwarding factory is when the
191     // frame belongs to an interstitial. Interstitials don't need audio, so it's
192     // fine to drop the request.
193     return;
194   }
195
196   // Unretained is safe since the destruction of |this| is posted to the IO
197   // thread.
198   base::PostTaskWithTraits(
199       FROM_HERE, {BrowserThread::IO},
200       base::BindOnce(&Core::Init, base::Unretained(this), std::move(request)));
201 }
202
203 RenderFrameAudioInputStreamFactory::Core::~Core() {
204   DCHECK_CURRENTLY_ON(BrowserThread::IO);
205 }
206
207 void RenderFrameAudioInputStreamFactory::Core::Init(
208     mojom::RendererAudioInputStreamFactoryRequest request) {
209   DCHECK_CURRENTLY_ON(BrowserThread::IO);
210   binding_.Bind(std::move(request));
211 }
212
213 void RenderFrameAudioInputStreamFactory::Core::CreateStream(
214     mojom::RendererAudioInputStreamFactoryClientPtr client,
215     int32_t session_id,
216     const media::AudioParameters& audio_params,
217     bool automatic_gain_control,
218     uint32_t shared_memory_count,
219     audio::mojom::AudioProcessingConfigPtr processing_config) {
220   DCHECK_CURRENTLY_ON(BrowserThread::IO);
221   DCHECK(forwarding_factory_);
222   TRACE_EVENT1("audio", "RenderFrameAudioInputStreamFactory::CreateStream",
223                "session id", session_id);
224
225   const MediaStreamDevice* device =
226       media_stream_manager_->audio_input_device_manager()->GetOpenedDeviceById(
227           session_id);
228
229   if (!device) {
230     TRACE_EVENT_INSTANT0("audio", "device not found", TRACE_EVENT_SCOPE_THREAD);
231     return;
232   }
233
234   WebContentsMediaCaptureId capture_id;
235   if (WebContentsMediaCaptureId::Parse(device->id, &capture_id)) {
236     // For MEDIA_GUM_DESKTOP_AUDIO_CAPTURE, the source is selected from
237     // picker window, we do not mute the source audio. For
238     // MEDIA_GUM_TAB_AUDIO_CAPTURE, the probable use case is Cast, we mute
239     // the source audio.
240     // TODO(qiangchen): Analyze audio constraints to make a duplicating or
241     // diverting decision. It would give web developer more flexibility.
242
243     base::PostTaskWithTraitsAndReplyWithResult(
244         FROM_HERE, {BrowserThread::UI},
245         base::BindOnce(&GetLoopbackSourceOnUIThread,
246                        capture_id.render_process_id,
247                        capture_id.main_render_frame_id),
248         base::BindOnce(
249             &RenderFrameAudioInputStreamFactory::Core::CreateLoopbackStream,
250             weak_ptr_factory_.GetWeakPtr(), std::move(client), audio_params,
251             shared_memory_count, capture_id.disable_local_echo));
252
253     if (device->type == MEDIA_GUM_DESKTOP_AUDIO_CAPTURE)
254       IncrementDesktopCaptureCounter(SYSTEM_LOOPBACK_AUDIO_CAPTURER_CREATED);
255     return;
256   } else {
257     forwarding_factory_->CreateInputStream(
258         process_id_, frame_id_, device->id, audio_params, shared_memory_count,
259         automatic_gain_control, std::move(processing_config),
260         std::move(client));
261
262     // Only count for captures from desktop media picker dialog and system loop
263     // back audio.
264     if (device->type == MEDIA_GUM_DESKTOP_AUDIO_CAPTURE &&
265         (media::AudioDeviceDescription::IsLoopbackDevice(device->id))) {
266       IncrementDesktopCaptureCounter(SYSTEM_LOOPBACK_AUDIO_CAPTURER_CREATED);
267     }
268   }
269 }
270
271 void RenderFrameAudioInputStreamFactory::Core::CreateLoopbackStream(
272     mojom::RendererAudioInputStreamFactoryClientPtr client,
273     const media::AudioParameters& audio_params,
274     uint32_t shared_memory_count,
275     bool disable_local_echo,
276     AudioStreamBroker::LoopbackSource* loopback_source) {
277   if (!loopback_source)
278     return;
279
280   forwarding_factory_->CreateLoopbackStream(
281       process_id_, frame_id_, loopback_source, audio_params,
282       shared_memory_count, disable_local_echo, std::move(client));
283 }
284
285 void RenderFrameAudioInputStreamFactory::Core::AssociateInputAndOutputForAec(
286     const base::UnguessableToken& input_stream_id,
287     const std::string& output_device_id) {
288   DCHECK_CURRENTLY_ON(BrowserThread::IO);
289   DCHECK(forwarding_factory_);
290   if (!IsValidDeviceId(output_device_id))
291     return;
292
293   base::PostTaskWithTraits(
294       FROM_HERE, {BrowserThread::UI},
295       base::BindOnce(
296           &GetSaltOriginAndPermissionsOnUIThread, process_id_, frame_id_,
297           base::BindOnce(
298               &Core::AssociateInputAndOutputForAecAfterCheckingAccess,
299               weak_ptr_factory_.GetWeakPtr(), input_stream_id,
300               output_device_id)));
301 }
302
303 void RenderFrameAudioInputStreamFactory::Core::
304     AssociateInputAndOutputForAecAfterCheckingAccess(
305         const base::UnguessableToken& input_stream_id,
306         const std::string& output_device_id,
307         MediaDeviceSaltAndOrigin salt_and_origin,
308         bool access_granted) {
309   DCHECK_CURRENTLY_ON(BrowserThread::IO);
310   DCHECK(forwarding_factory_);
311
312   if (!access_granted)
313     return;
314
315   if (media::AudioDeviceDescription::IsDefaultDevice(output_device_id) ||
316       media::AudioDeviceDescription::IsCommunicationsDevice(output_device_id)) {
317     forwarding_factory_->AssociateInputAndOutputForAec(input_stream_id,
318                                                        output_device_id);
319   } else {
320     EnumerateOutputDevices(
321         media_stream_manager_,
322         base::BindRepeating(
323             &TranslateDeviceId, output_device_id, salt_and_origin,
324             base::BindRepeating(&RenderFrameAudioInputStreamFactory::Core::
325                                     AssociateTranslatedOutputDeviceForAec,
326                                 weak_ptr_factory_.GetWeakPtr(),
327                                 input_stream_id)));
328   }
329 }
330
331 void RenderFrameAudioInputStreamFactory::Core::
332     AssociateTranslatedOutputDeviceForAec(
333         const base::UnguessableToken& input_stream_id,
334         const std::string& raw_output_device_id) {
335   DCHECK_CURRENTLY_ON(BrowserThread::IO);
336   DCHECK(forwarding_factory_);
337   forwarding_factory_->AssociateInputAndOutputForAec(input_stream_id,
338                                                      raw_output_device_id);
339 }
340
341 }  // namespace content