| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/renderer_host/media/old_render_frame_audio_input_stream_factory.h" |
| |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/feature_list.h" |
| #include "base/task/post_task.h" |
| #include "base/task_runner_util.h" |
| #include "content/browser/media/media_devices_permission_checker.h" |
| #include "content/browser/media/media_internals.h" |
| #include "content/browser/renderer_host/media/media_stream_manager.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/media_device_id.h" |
| #include "content/public/common/content_features.h" |
| #include "media/base/audio_parameters.h" |
| |
| namespace content { |
| namespace { |
| void CheckPermissionAndGetSaltAndOrigin( |
| const std::string& output_device_id, |
| int render_process_id, |
| int render_frame_id, |
| base::OnceCallback<void(const MediaDeviceSaltAndOrigin&)> cb) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| auto salt_and_origin = |
| GetMediaDeviceSaltAndOrigin(render_process_id, render_frame_id); |
| |
| // Check permissions for everything but the default device |
| if (!media::AudioDeviceDescription::IsDefaultDevice(output_device_id) && |
| !MediaDevicesPermissionChecker().CheckPermissionOnUIThread( |
| blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT, render_process_id, |
| render_frame_id)) { |
| // If we're not allowed to use the device, don't call |cb|. |
| return; |
| } |
| base::PostTaskWithTraits(FROM_HERE, {BrowserThread::IO}, |
| base::BindOnce(std::move(cb), salt_and_origin)); |
| } |
| |
| void OldEnumerateOutputDevices( |
| MediaDevicesManager* media_devices_manager, |
| base::RepeatingCallback< |
| void(const MediaDeviceSaltAndOrigin& salt_and_origin, |
| const MediaDeviceEnumeration& devices)> cb, |
| const MediaDeviceSaltAndOrigin& salt_and_origin) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| MediaDevicesManager::BoolDeviceTypes device_types; |
| device_types[blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] = true; |
| media_devices_manager->EnumerateDevices(device_types, |
| base::BindOnce(cb, salt_and_origin)); |
| } |
| |
| } // namespace |
| |
| // static |
| std::unique_ptr<RenderFrameAudioInputStreamFactoryHandle, |
| BrowserThread::DeleteOnIOThread> |
| RenderFrameAudioInputStreamFactoryHandle::CreateFactory( |
| OldRenderFrameAudioInputStreamFactory::CreateDelegateCallback |
| create_delegate_callback, |
| content::MediaStreamManager* media_stream_manager, |
| int render_process_id, |
| int render_frame_id, |
| mojom::RendererAudioInputStreamFactoryRequest request) { |
| std::unique_ptr<RenderFrameAudioInputStreamFactoryHandle, |
| BrowserThread::DeleteOnIOThread> |
| handle(new RenderFrameAudioInputStreamFactoryHandle( |
| std::move(create_delegate_callback), media_stream_manager, |
| render_process_id, render_frame_id)); |
| // Unretained is safe since |*handle| must be posted to the IO thread prior to |
| // deletion. |
| base::PostTaskWithTraits( |
| FROM_HERE, {BrowserThread::IO}, |
| base::BindOnce(&RenderFrameAudioInputStreamFactoryHandle::Init, |
| base::Unretained(handle.get()), std::move(request))); |
| return handle; |
| } |
| |
| RenderFrameAudioInputStreamFactoryHandle:: |
| ~RenderFrameAudioInputStreamFactoryHandle() { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| } |
| |
| RenderFrameAudioInputStreamFactoryHandle:: |
| RenderFrameAudioInputStreamFactoryHandle( |
| OldRenderFrameAudioInputStreamFactory::CreateDelegateCallback |
| create_delegate_callback, |
| MediaStreamManager* media_stream_manager, |
| int render_process_id, |
| int render_frame_id) |
| : impl_(std::move(create_delegate_callback), |
| media_stream_manager, |
| render_process_id, |
| render_frame_id), |
| binding_(&impl_) {} |
| |
| void RenderFrameAudioInputStreamFactoryHandle::Init( |
| mojom::RendererAudioInputStreamFactoryRequest request) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| binding_.Bind(std::move(request)); |
| } |
| |
| OldRenderFrameAudioInputStreamFactory::OldRenderFrameAudioInputStreamFactory( |
| CreateDelegateCallback create_delegate_callback, |
| MediaStreamManager* media_stream_manager, |
| int render_process_id, |
| int render_frame_id) |
| : create_delegate_callback_(std::move(create_delegate_callback)), |
| media_stream_manager_(media_stream_manager), |
| render_process_id_(render_process_id), |
| render_frame_id_(render_frame_id) { |
| DCHECK(create_delegate_callback_); |
| // No thread-hostile state has been initialized yet, so we don't have to bind |
| // to this specific thread. |
| } |
| |
| OldRenderFrameAudioInputStreamFactory:: |
| ~OldRenderFrameAudioInputStreamFactory() { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| } |
| |
| void OldRenderFrameAudioInputStreamFactory::CreateStream( |
| mojom::RendererAudioInputStreamFactoryClientPtr client, |
| const base::UnguessableToken& session_id, |
| const media::AudioParameters& audio_params, |
| bool automatic_gain_control, |
| uint32_t shared_memory_count, |
| audio::mojom::AudioProcessingConfigPtr processing_config) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| // |processing_config| gets dropped here. It's not supported outside of the |
| // audio service. As this class is slated for removal, it will not be updated |
| // to support audio processing. |
| #if defined(OS_CHROMEOS) |
| if (audio_params.channel_layout() == |
| media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC) { |
| media_stream_manager_->audio_input_device_manager() |
| ->RegisterKeyboardMicStream(base::BindOnce( |
| &OldRenderFrameAudioInputStreamFactory::DoCreateStream, |
| weak_ptr_factory_.GetWeakPtr(), std::move(client), session_id, |
| audio_params, automatic_gain_control, shared_memory_count)); |
| return; |
| } |
| #endif |
| DoCreateStream(std::move(client), session_id, audio_params, |
| automatic_gain_control, shared_memory_count, |
| AudioInputDeviceManager::KeyboardMicRegistration()); |
| } |
| |
| void OldRenderFrameAudioInputStreamFactory::DoCreateStream( |
| mojom::RendererAudioInputStreamFactoryClientPtr client, |
| const base::UnguessableToken& session_id, |
| const media::AudioParameters& audio_params, |
| bool automatic_gain_control, |
| uint32_t shared_memory_count, |
| AudioInputDeviceManager::KeyboardMicRegistration |
| keyboard_mic_registration) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| int stream_id = ++next_stream_id_; |
| |
| media::mojom::AudioLogPtr audio_log_ptr = |
| MediaInternals::GetInstance()->CreateMojoAudioLog( |
| media::AudioLogFactory::AUDIO_INPUT_CONTROLLER, stream_id, |
| render_process_id_, render_frame_id_); |
| |
| // Unretained is safe since |this| owns |streams_|. |
| streams_.insert(std::make_unique<AudioInputStreamHandle>( |
| std::move(client), |
| base::BindOnce( |
| create_delegate_callback_, |
| base::Unretained(media_stream_manager_->audio_input_device_manager()), |
| std::move(audio_log_ptr), std::move(keyboard_mic_registration), |
| shared_memory_count, stream_id, session_id, automatic_gain_control, |
| audio_params), |
| base::BindOnce(&OldRenderFrameAudioInputStreamFactory::RemoveStream, |
| weak_ptr_factory_.GetWeakPtr()))); |
| } |
| |
| void OldRenderFrameAudioInputStreamFactory::AssociateInputAndOutputForAec( |
| const base::UnguessableToken& input_stream_id, |
| const std::string& output_device_id) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| if (!IsValidDeviceId(output_device_id)) |
| return; |
| |
| if (media::AudioDeviceDescription::IsDefaultDevice(output_device_id)) { |
| for (const auto& stream : streams_) { |
| if (stream->id() == input_stream_id) { |
| stream->SetOutputDeviceForAec(output_device_id); |
| return; |
| } |
| } |
| } else { |
| base::PostTaskWithTraits( |
| FROM_HERE, {BrowserThread::UI}, |
| base::BindOnce( |
| CheckPermissionAndGetSaltAndOrigin, output_device_id, |
| render_process_id_, render_frame_id_, |
| base::BindOnce( |
| &OldEnumerateOutputDevices, |
| media_stream_manager_->media_devices_manager(), |
| base::BindRepeating(&OldRenderFrameAudioInputStreamFactory:: |
| TranslateAndSetOutputDeviceForAec, |
| weak_ptr_factory_.GetWeakPtr(), |
| input_stream_id, output_device_id)))); |
| } |
| } |
| |
| void OldRenderFrameAudioInputStreamFactory::TranslateAndSetOutputDeviceForAec( |
| const base::UnguessableToken& input_stream_id, |
| const std::string& output_device_id, |
| const MediaDeviceSaltAndOrigin& salt_and_origin, |
| const MediaDeviceEnumeration& device_array) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| std::string raw_output_device_id; |
| for (const auto& device_info : |
| device_array[blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT]) { |
| if (MediaStreamManager::DoesMediaDeviceIDMatchHMAC( |
| salt_and_origin.device_id_salt, salt_and_origin.origin, |
| output_device_id, device_info.device_id)) { |
| raw_output_device_id = device_info.device_id; |
| } |
| } |
| if (!raw_output_device_id.empty()) { |
| for (const auto& stream : streams_) { |
| if (stream->id() == input_stream_id) { |
| stream->SetOutputDeviceForAec(raw_output_device_id); |
| return; |
| } |
| } |
| } |
| } |
| |
| void OldRenderFrameAudioInputStreamFactory::RemoveStream( |
| AudioInputStreamHandle* stream) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| |
| streams_.erase(stream); |
| } |
| |
| } // namespace content |