| // Copyright 2016 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/media_devices_dispatcher_host.h" |
| |
| #include <stddef.h> |
| |
| #include <algorithm> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/callback_helpers.h" |
| #include "base/command_line.h" |
| #include "base/run_loop.h" |
| #include "base/task/bind_post_task.h" |
| #include "base/task/post_task.h" |
| #include "base/task/task_runner_util.h" |
| #include "build/build_config.h" |
| #include "content/browser/media/media_devices_permission_checker.h" |
| #include "content/browser/renderer_host/back_forward_cache_disable.h" |
| #include "content/browser/renderer_host/back_forward_cache_impl.h" |
| #include "content/browser/renderer_host/media/media_stream_manager.h" |
| #include "content/browser/renderer_host/media/video_capture_manager.h" |
| #include "content/browser/renderer_host/render_frame_host_delegate.h" |
| #include "content/browser/renderer_host/render_frame_host_impl.h" |
| #include "content/public/browser/browser_context.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/media_device_id.h" |
| #include "media/audio/audio_system.h" |
| #include "media/base/media_switches.h" |
| #include "media/base/video_facing.h" |
| #include "mojo/public/cpp/bindings/self_owned_receiver.h" |
| #include "services/service_manager/public/cpp/interface_provider.h" |
| #include "third_party/blink/public/common/mediastream/media_devices.h" |
| #include "third_party/blink/public/common/mediastream/media_stream_request.h" |
| #include "url/origin.h" |
| |
| #if !defined(OS_ANDROID) |
| #include "content/browser/media/capture/crop_id_web_contents_helper.h" |
| #endif |
| |
| using blink::mojom::MediaDeviceType; |
| |
| namespace content { |
| |
| namespace { |
| |
| std::vector<blink::mojom::AudioInputDeviceCapabilitiesPtr> |
| ToVectorAudioInputDeviceCapabilitiesPtr( |
| const std::vector<blink::mojom::AudioInputDeviceCapabilities>& |
| capabilities_vector, |
| const MediaDeviceSaltAndOrigin& salt_and_origin) { |
| std::vector<blink::mojom::AudioInputDeviceCapabilitiesPtr> result; |
| result.reserve(capabilities_vector.size()); |
| for (auto& capabilities : capabilities_vector) { |
| blink::mojom::AudioInputDeviceCapabilitiesPtr capabilities_ptr = |
| blink::mojom::AudioInputDeviceCapabilities::New(); |
| capabilities_ptr->device_id = |
| GetHMACForMediaDeviceID(salt_and_origin.device_id_salt, |
| salt_and_origin.origin, capabilities.device_id); |
| capabilities_ptr->group_id = |
| GetHMACForMediaDeviceID(salt_and_origin.group_id_salt, |
| salt_and_origin.origin, capabilities.group_id); |
| capabilities_ptr->parameters = capabilities.parameters; |
| result.push_back(std::move(capabilities_ptr)); |
| } |
| return result; |
| } |
| |
| } // namespace |
| |
| // static |
| void MediaDevicesDispatcherHost::Create( |
| int render_process_id, |
| int render_frame_id, |
| MediaStreamManager* media_stream_manager, |
| mojo::PendingReceiver<blink::mojom::MediaDevicesDispatcherHost> receiver) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| mojo::MakeSelfOwnedReceiver( |
| std::make_unique<MediaDevicesDispatcherHost>( |
| render_process_id, render_frame_id, media_stream_manager), |
| std::move(receiver)); |
| |
| GetUIThreadTaskRunner({})->PostTask( |
| FROM_HERE, base::BindOnce( |
| [](int render_process_id, int render_frame_id) { |
| RenderFrameHost* render_frame_host = |
| RenderFrameHost::FromID(render_process_id, |
| render_frame_id); |
| |
| if (!render_frame_host) |
| return; |
| |
| BackForwardCache::DisableForRenderFrameHost( |
| render_frame_host, |
| BackForwardCacheDisable::DisabledReason( |
| BackForwardCacheDisable::DisabledReasonId:: |
| kMediaDevicesDispatcherHost)); |
| }, |
| render_process_id, render_frame_id)); |
| } |
| |
| MediaDevicesDispatcherHost::MediaDevicesDispatcherHost( |
| int render_process_id, |
| int render_frame_id, |
| MediaStreamManager* media_stream_manager) |
| : render_process_id_(render_process_id), |
| render_frame_id_(render_frame_id), |
| media_stream_manager_(media_stream_manager), |
| num_pending_audio_input_parameters_(0) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| DCHECK(media_stream_manager_); |
| } |
| |
| MediaDevicesDispatcherHost::~MediaDevicesDispatcherHost() { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| // It may happen that media_devices_manager() is destroyed before MDDH on some |
| // shutdown scenarios. |
| if (!media_stream_manager_->media_devices_manager()) |
| return; |
| |
| for (auto subscription_id : subscription_ids_) { |
| media_stream_manager_->media_devices_manager() |
| ->UnsubscribeDeviceChangeNotifications(subscription_id); |
| } |
| } |
| |
| void MediaDevicesDispatcherHost::EnumerateDevices( |
| bool request_audio_input, |
| bool request_video_input, |
| bool request_audio_output, |
| bool request_video_input_capabilities, |
| bool request_audio_input_capabilities, |
| EnumerateDevicesCallback client_callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| |
| if ((!request_audio_input && !request_video_input && !request_audio_output) || |
| (request_video_input_capabilities && !request_video_input) || |
| (request_audio_input_capabilities && !request_audio_input)) { |
| ReceivedBadMessage(render_process_id_, |
| bad_message::MDDH_INVALID_DEVICE_TYPE_REQUEST); |
| return; |
| } |
| |
| MediaDevicesManager::BoolDeviceTypes devices_to_enumerate; |
| devices_to_enumerate[static_cast<size_t>( |
| MediaDeviceType::MEDIA_AUDIO_INPUT)] = request_audio_input; |
| devices_to_enumerate[static_cast<size_t>( |
| MediaDeviceType::MEDIA_VIDEO_INPUT)] = request_video_input; |
| devices_to_enumerate[static_cast<size_t>( |
| MediaDeviceType::MEDIA_AUDIO_OUTPUT)] = request_audio_output; |
| |
| media_stream_manager_->media_devices_manager()->EnumerateDevices( |
| render_process_id_, render_frame_id_, devices_to_enumerate, |
| request_video_input_capabilities, request_audio_input_capabilities, |
| std::move(client_callback)); |
| } |
| |
| void MediaDevicesDispatcherHost::GetVideoInputCapabilities( |
| GetVideoInputCapabilitiesCallback client_callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| base::PostTaskAndReplyWithResult( |
| GetUIThreadTaskRunner({}).get(), FROM_HERE, |
| base::BindOnce(media_stream_manager_->media_devices_manager() |
| ->salt_and_origin_callback(), |
| render_process_id_, render_frame_id_), |
| base::BindOnce(&MediaDevicesDispatcherHost::GetDefaultVideoInputDeviceID, |
| weak_factory_.GetWeakPtr(), std::move(client_callback))); |
| } |
| |
| void MediaDevicesDispatcherHost::GetAllVideoInputDeviceFormats( |
| const std::string& device_id, |
| GetAllVideoInputDeviceFormatsCallback client_callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| GetVideoInputDeviceFormats(device_id, false /* try_in_use_first */, |
| std::move(client_callback)); |
| } |
| |
| void MediaDevicesDispatcherHost::GetAvailableVideoInputDeviceFormats( |
| const std::string& device_id, |
| GetAvailableVideoInputDeviceFormatsCallback client_callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| GetVideoInputDeviceFormats(device_id, true /* try_in_use_first */, |
| std::move(client_callback)); |
| } |
| |
| void MediaDevicesDispatcherHost::GetAudioInputCapabilities( |
| GetAudioInputCapabilitiesCallback client_callback) { |
| base::PostTaskAndReplyWithResult( |
| GetUIThreadTaskRunner({}).get(), FROM_HERE, |
| base::BindOnce(media_stream_manager_->media_devices_manager() |
| ->salt_and_origin_callback(), |
| render_process_id_, render_frame_id_), |
| base::BindOnce(&MediaDevicesDispatcherHost::GetDefaultAudioInputDeviceID, |
| weak_factory_.GetWeakPtr(), std::move(client_callback))); |
| } |
| |
| void MediaDevicesDispatcherHost::AddMediaDevicesListener( |
| bool subscribe_audio_input, |
| bool subscribe_video_input, |
| bool subscribe_audio_output, |
| mojo::PendingRemote<blink::mojom::MediaDevicesListener> listener) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| |
| if (!subscribe_audio_input && !subscribe_video_input && |
| !subscribe_audio_output) { |
| ReceivedBadMessage(render_process_id_, |
| bad_message::MDDH_INVALID_DEVICE_TYPE_REQUEST); |
| return; |
| } |
| |
| MediaDevicesManager::BoolDeviceTypes devices_to_subscribe; |
| devices_to_subscribe[static_cast<size_t>( |
| MediaDeviceType::MEDIA_AUDIO_INPUT)] = subscribe_audio_input; |
| devices_to_subscribe[static_cast<size_t>( |
| MediaDeviceType::MEDIA_VIDEO_INPUT)] = subscribe_video_input; |
| devices_to_subscribe[static_cast<size_t>( |
| MediaDeviceType::MEDIA_AUDIO_OUTPUT)] = subscribe_audio_output; |
| |
| uint32_t subscription_id = media_stream_manager_->media_devices_manager() |
| ->SubscribeDeviceChangeNotifications( |
| render_process_id_, render_frame_id_, |
| devices_to_subscribe, std::move(listener)); |
| subscription_ids_.push_back(subscription_id); |
| } |
| |
| void MediaDevicesDispatcherHost::SetCaptureHandleConfig( |
| blink::mojom::CaptureHandleConfigPtr config) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| |
| if (!config) { |
| ReceivedBadMessage(render_process_id_, |
| bad_message::MDDH_NULL_CAPTURE_HANDLE_CONFIG); |
| return; |
| } |
| |
| static_assert(sizeof(decltype(config->capture_handle)::value_type) == 2, ""); |
| if (config->capture_handle.length() > 1024) { |
| ReceivedBadMessage(render_process_id_, |
| bad_message::MDDH_INVALID_CAPTURE_HANDLE); |
| return; |
| } |
| |
| if (config->all_origins_permitted && !config->permitted_origins.empty()) { |
| ReceivedBadMessage(render_process_id_, |
| bad_message::MDDH_INVALID_ALL_ORIGINS_PERMITTED); |
| return; |
| } |
| |
| for (const auto& origin : config->permitted_origins) { |
| if (origin.opaque()) { |
| ReceivedBadMessage(render_process_id_, |
| bad_message::MDDH_INVALID_PERMITTED_ORIGIN); |
| return; |
| } |
| } |
| |
| if (capture_handle_config_callback_for_testing_) { |
| capture_handle_config_callback_for_testing_.Run( |
| render_process_id_, render_frame_id_, config->Clone()); |
| } |
| |
| GetUIThreadTaskRunner({})->PostTask( |
| FROM_HERE, |
| base::BindOnce( |
| [](int render_process_id, int render_frame_id, |
| blink::mojom::CaptureHandleConfigPtr config) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| RenderFrameHostImpl* const rfhi = |
| RenderFrameHostImpl::FromID(render_process_id, render_frame_id); |
| if (!rfhi || !rfhi->IsActive()) { |
| return; |
| } |
| if (rfhi != rfhi->GetMainFrame()) { |
| // Would be overkill to add thread-hopping just to support a test, |
| // so we execute directly. |
| bad_message::ReceivedBadMessage(render_process_id, |
| bad_message::MDDH_NOT_TOP_LEVEL); |
| } |
| rfhi->delegate()->SetCaptureHandleConfig(std::move(config)); |
| }, |
| render_process_id_, render_frame_id_, std::move(config))); |
| } |
| |
| #if !defined(OS_ANDROID) |
| void MediaDevicesDispatcherHost::CloseFocusWindowOfOpportunity( |
| const std::string& label) { |
| media_stream_manager_->SetCapturedDisplaySurfaceFocus( |
| label, /*focus=*/true, |
| /*is_from_microtask=*/true, |
| /*is_from_timer=*/false); |
| } |
| |
| void MediaDevicesDispatcherHost::ProduceCropId(ProduceCropIdCallback callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| |
| GetUIThreadTaskRunner({})->PostTaskAndReplyWithResult( |
| FROM_HERE, |
| base::BindOnce( |
| [](int render_process_id, int render_frame_id) { |
| RenderFrameHostImpl* const rfh = |
| RenderFrameHostImpl::FromID(render_process_id, render_frame_id); |
| if (!rfh || !rfh->IsActive()) { |
| return std::string(); // Might have been asynchronously closed. |
| } |
| |
| WebContents* const web_contents = |
| WebContents::FromRenderFrameHost(rfh->GetMainFrame()); |
| DCHECK(web_contents); |
| |
| // No-op if already created. |
| CropIdWebContentsHelper::CreateForWebContents(web_contents); |
| |
| return CropIdWebContentsHelper::FromWebContents(web_contents) |
| ->ProduceCropId(); |
| }, |
| render_process_id_, render_frame_id_), |
| std::move(callback)); |
| } |
| #endif |
| |
| void MediaDevicesDispatcherHost::GetDefaultVideoInputDeviceID( |
| GetVideoInputCapabilitiesCallback client_callback, |
| MediaDeviceSaltAndOrigin salt_and_origin) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| GetDefaultMediaDeviceID( |
| MediaDeviceType::MEDIA_VIDEO_INPUT, render_process_id_, render_frame_id_, |
| base::BindOnce(&MediaDevicesDispatcherHost::GotDefaultVideoInputDeviceID, |
| weak_factory_.GetWeakPtr(), std::move(client_callback), |
| std::move(salt_and_origin))); |
| } |
| |
| void MediaDevicesDispatcherHost::GotDefaultVideoInputDeviceID( |
| GetVideoInputCapabilitiesCallback client_callback, |
| MediaDeviceSaltAndOrigin salt_and_origin, |
| const std::string& default_device_id) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| MediaDevicesManager::BoolDeviceTypes requested_types; |
| // Also request audio devices to make sure the heuristic to determine |
| // the video group ID works. |
| requested_types[static_cast<size_t>(MediaDeviceType::MEDIA_VIDEO_INPUT)] = |
| true; |
| media_stream_manager_->media_devices_manager()->EnumerateDevices( |
| requested_types, |
| base::BindOnce( |
| &MediaDevicesDispatcherHost::FinalizeGetVideoInputCapabilities, |
| weak_factory_.GetWeakPtr(), std::move(client_callback), |
| std::move(salt_and_origin), std::move(default_device_id))); |
| } |
| |
| void MediaDevicesDispatcherHost::FinalizeGetVideoInputCapabilities( |
| GetVideoInputCapabilitiesCallback client_callback, |
| const MediaDeviceSaltAndOrigin& salt_and_origin, |
| const std::string& default_device_id, |
| const MediaDeviceEnumeration& enumeration) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| std::vector<blink::mojom::VideoInputDeviceCapabilitiesPtr> |
| video_input_capabilities; |
| for (const auto& device_info : |
| enumeration[static_cast<size_t>(MediaDeviceType::MEDIA_VIDEO_INPUT)]) { |
| std::string hmac_device_id = |
| GetHMACForMediaDeviceID(salt_and_origin.device_id_salt, |
| salt_and_origin.origin, device_info.device_id); |
| std::string hmac_group_id = |
| GetHMACForMediaDeviceID(salt_and_origin.group_id_salt, |
| salt_and_origin.origin, device_info.group_id); |
| blink::mojom::VideoInputDeviceCapabilitiesPtr capabilities = |
| blink::mojom::VideoInputDeviceCapabilities::New(); |
| capabilities->device_id = std::move(hmac_device_id); |
| capabilities->group_id = std::move(hmac_group_id); |
| capabilities->control_support = device_info.video_control_support; |
| capabilities->formats = |
| media_stream_manager_->media_devices_manager()->GetVideoInputFormats( |
| device_info.device_id, true /* try_in_use_first */); |
| capabilities->facing_mode = device_info.video_facing; |
| if (device_info.device_id == default_device_id) { |
| video_input_capabilities.insert(video_input_capabilities.begin(), |
| std::move(capabilities)); |
| } else { |
| video_input_capabilities.push_back(std::move(capabilities)); |
| } |
| } |
| |
| std::move(client_callback).Run(std::move(video_input_capabilities)); |
| } |
| |
| void MediaDevicesDispatcherHost::GetVideoInputDeviceFormats( |
| const std::string& device_id, |
| bool try_in_use_first, |
| GetVideoInputDeviceFormatsCallback client_callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| base::PostTaskAndReplyWithResult( |
| GetUIThreadTaskRunner({}).get(), FROM_HERE, |
| base::BindOnce(media_stream_manager_->media_devices_manager() |
| ->salt_and_origin_callback(), |
| render_process_id_, render_frame_id_), |
| base::BindOnce( |
| &MediaDevicesDispatcherHost::EnumerateVideoDevicesForFormats, |
| weak_factory_.GetWeakPtr(), std::move(client_callback), device_id, |
| try_in_use_first)); |
| } |
| |
| void MediaDevicesDispatcherHost::EnumerateVideoDevicesForFormats( |
| GetVideoInputDeviceFormatsCallback client_callback, |
| const std::string& device_id, |
| bool try_in_use_first, |
| const MediaDeviceSaltAndOrigin& salt_and_origin) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| media_stream_manager_->video_capture_manager()->EnumerateDevices( |
| base::BindOnce( |
| &MediaDevicesDispatcherHost::FinalizeGetVideoInputDeviceFormats, |
| weak_factory_.GetWeakPtr(), std::move(client_callback), device_id, |
| try_in_use_first, salt_and_origin.device_id_salt, |
| salt_and_origin.origin)); |
| } |
| |
| void MediaDevicesDispatcherHost::FinalizeGetVideoInputDeviceFormats( |
| GetVideoInputDeviceFormatsCallback client_callback, |
| const std::string& device_id, |
| bool try_in_use_first, |
| const std::string& device_id_salt, |
| const url::Origin& security_origin, |
| const media::VideoCaptureDeviceDescriptors& device_descriptors) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| for (const auto& descriptor : device_descriptors) { |
| if (DoesMediaDeviceIDMatchHMAC(device_id_salt, security_origin, device_id, |
| descriptor.device_id)) { |
| std::move(client_callback) |
| .Run(media_stream_manager_->media_devices_manager() |
| ->GetVideoInputFormats(descriptor.device_id, |
| try_in_use_first)); |
| return; |
| } |
| } |
| std::move(client_callback).Run(media::VideoCaptureFormats()); |
| } |
| |
| struct MediaDevicesDispatcherHost::AudioInputCapabilitiesRequest { |
| MediaDeviceSaltAndOrigin salt_and_origin; |
| GetAudioInputCapabilitiesCallback client_callback; |
| }; |
| |
| void MediaDevicesDispatcherHost::GetDefaultAudioInputDeviceID( |
| GetAudioInputCapabilitiesCallback client_callback, |
| const MediaDeviceSaltAndOrigin& salt_and_origin) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| pending_audio_input_capabilities_requests_.push_back( |
| AudioInputCapabilitiesRequest{salt_and_origin, |
| std::move(client_callback)}); |
| if (pending_audio_input_capabilities_requests_.size() > 1U) |
| return; |
| |
| DCHECK(current_audio_input_capabilities_.empty()); |
| GetDefaultMediaDeviceID( |
| MediaDeviceType::MEDIA_AUDIO_INPUT, render_process_id_, render_frame_id_, |
| base::BindOnce(&MediaDevicesDispatcherHost::GotDefaultAudioInputDeviceID, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void MediaDevicesDispatcherHost::GotDefaultAudioInputDeviceID( |
| const std::string& default_device_id) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| DCHECK_GT(pending_audio_input_capabilities_requests_.size(), 0U); |
| DCHECK(current_audio_input_capabilities_.empty()); |
| MediaDevicesManager::BoolDeviceTypes devices_to_enumerate; |
| devices_to_enumerate[static_cast<size_t>( |
| MediaDeviceType::MEDIA_AUDIO_INPUT)] = true; |
| media_stream_manager_->media_devices_manager()->EnumerateDevices( |
| devices_to_enumerate, |
| base::BindOnce(&MediaDevicesDispatcherHost::GotAudioInputEnumeration, |
| weak_factory_.GetWeakPtr(), default_device_id)); |
| } |
| |
| void MediaDevicesDispatcherHost::GotAudioInputEnumeration( |
| const std::string& default_device_id, |
| const MediaDeviceEnumeration& enumeration) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| DCHECK_GT(pending_audio_input_capabilities_requests_.size(), 0U); |
| DCHECK(current_audio_input_capabilities_.empty()); |
| DCHECK_EQ(num_pending_audio_input_parameters_, 0U); |
| for (const auto& device_info : |
| enumeration[static_cast<size_t>(MediaDeviceType::MEDIA_AUDIO_INPUT)]) { |
| auto parameters = media::AudioParameters::UnavailableDeviceParams(); |
| blink::mojom::AudioInputDeviceCapabilities capabilities( |
| device_info.device_id, device_info.group_id, parameters, |
| parameters.IsValid(), parameters.channels(), parameters.sample_rate(), |
| parameters.GetBufferDuration()); |
| if (device_info.device_id == default_device_id) |
| current_audio_input_capabilities_.insert( |
| current_audio_input_capabilities_.begin(), std::move(capabilities)); |
| else |
| current_audio_input_capabilities_.push_back(std::move(capabilities)); |
| } |
| // No devices or fake devices, no need to read audio parameters. |
| if (current_audio_input_capabilities_.empty() || |
| base::CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kUseFakeDeviceForMediaStream)) { |
| FinalizeGetAudioInputCapabilities(); |
| return; |
| } |
| |
| num_pending_audio_input_parameters_ = |
| current_audio_input_capabilities_.size(); |
| for (size_t i = 0; i < num_pending_audio_input_parameters_; ++i) { |
| media_stream_manager_->audio_system()->GetInputStreamParameters( |
| current_audio_input_capabilities_[i].device_id, |
| base::BindOnce(&MediaDevicesDispatcherHost::GotAudioInputParameters, |
| weak_factory_.GetWeakPtr(), i)); |
| } |
| } |
| |
| void MediaDevicesDispatcherHost::GotAudioInputParameters( |
| size_t index, |
| const absl::optional<media::AudioParameters>& parameters) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| DCHECK_GT(pending_audio_input_capabilities_requests_.size(), 0U); |
| DCHECK_GT(current_audio_input_capabilities_.size(), index); |
| DCHECK_GT(num_pending_audio_input_parameters_, 0U); |
| |
| if (parameters) |
| current_audio_input_capabilities_[index].parameters = *parameters; |
| DCHECK(current_audio_input_capabilities_[index].parameters.IsValid()); |
| if (--num_pending_audio_input_parameters_ == 0U) |
| FinalizeGetAudioInputCapabilities(); |
| } |
| |
| void MediaDevicesDispatcherHost::FinalizeGetAudioInputCapabilities() { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| DCHECK_GT(pending_audio_input_capabilities_requests_.size(), 0U); |
| DCHECK_EQ(num_pending_audio_input_parameters_, 0U); |
| |
| for (auto& request : pending_audio_input_capabilities_requests_) { |
| std::move(request.client_callback) |
| .Run(ToVectorAudioInputDeviceCapabilitiesPtr( |
| current_audio_input_capabilities_, request.salt_and_origin)); |
| } |
| |
| current_audio_input_capabilities_.clear(); |
| pending_audio_input_capabilities_requests_.clear(); |
| } |
| |
| void MediaDevicesDispatcherHost::ReceivedBadMessage( |
| int render_process_id, |
| bad_message::BadMessageReason reason) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| |
| if (bad_message_callback_for_testing_) { |
| bad_message_callback_for_testing_.Run(render_process_id, reason); |
| } |
| |
| bad_message::ReceivedBadMessage(render_process_id, reason); |
| } |
| |
| void MediaDevicesDispatcherHost::SetBadMessageCallbackForTesting( |
| base::RepeatingCallback<void(int, bad_message::BadMessageReason)> |
| callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| DCHECK(!bad_message_callback_for_testing_); |
| bad_message_callback_for_testing_ = callback; |
| } |
| |
| void MediaDevicesDispatcherHost::SetCaptureHandleConfigCallbackForTesting( |
| base::RepeatingCallback< |
| void(int, int, blink::mojom::CaptureHandleConfigPtr)> callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| DCHECK(!capture_handle_config_callback_for_testing_); |
| capture_handle_config_callback_for_testing_ = callback; |
| } |
| |
| } // namespace content |