blob: 181c25551b3d70986773dc0bdb3d76c98c9ecadd [file] [log] [blame]
// 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 "base/bind_helpers.h"
#include "base/command_line.h"
#include "base/task/post_task.h"
#include "base/task_runner_util.h"
#include "content/browser/bad_message.h"
#include "content/browser/media/media_devices_permission_checker.h"
#include "content/browser/renderer_host/media/media_stream_manager.h"
#include "content/browser/renderer_host/media/video_capture_manager.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 "content/public/browser/render_frame_host.h"
#include "media/audio/audio_system.h"
#include "media/base/media_switches.h"
#include "media/base/video_facing.h"
#include "mojo/public/cpp/bindings/strong_binding.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"
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,
blink::mojom::MediaDevicesDispatcherHostRequest request) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
mojo::MakeStrongBinding(
std::make_unique<MediaDevicesDispatcherHost>(
render_process_id, render_frame_id, media_stream_manager),
std::move(request));
}
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),
weak_factory_(this) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
}
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,
EnumerateDevicesCallback client_callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!request_audio_input && !request_video_input && !request_audio_output) {
bad_message::ReceivedBadMessage(
render_process_id_, bad_message::MDDH_INVALID_DEVICE_TYPE_REQUEST);
return;
}
MediaDevicesManager::BoolDeviceTypes devices_to_enumerate;
devices_to_enumerate[blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT] =
request_audio_input;
devices_to_enumerate[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT] =
request_video_input;
devices_to_enumerate[blink::MEDIA_DEVICE_TYPE_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, std::move(client_callback));
}
void MediaDevicesDispatcherHost::GetVideoInputCapabilities(
GetVideoInputCapabilitiesCallback client_callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
base::PostTaskAndReplyWithResult(
base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::UI}).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(
base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::UI}).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,
blink::mojom::MediaDevicesListenerPtr listener) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!subscribe_audio_input && !subscribe_video_input &&
!subscribe_audio_output) {
bad_message::ReceivedBadMessage(
render_process_id_, bad_message::MDDH_INVALID_DEVICE_TYPE_REQUEST);
return;
}
MediaDevicesManager::BoolDeviceTypes devices_to_subscribe;
devices_to_subscribe[blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT] =
subscribe_audio_input;
devices_to_subscribe[blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT] =
subscribe_video_input;
devices_to_subscribe[blink::MEDIA_DEVICE_TYPE_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::GetDefaultVideoInputDeviceID(
GetVideoInputCapabilitiesCallback client_callback,
MediaDeviceSaltAndOrigin salt_and_origin) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
GetDefaultMediaDeviceID(
blink::MEDIA_DEVICE_TYPE_VIDEO_INPUT, render_process_id_,
render_frame_id_,
base::Bind(&MediaDevicesDispatcherHost::GotDefaultVideoInputDeviceID,
weak_factory_.GetWeakPtr(), base::Passed(&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[blink::MEDIA_DEVICE_TYPE_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[blink::MEDIA_DEVICE_TYPE_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->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(
base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::UI}).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(
blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT, render_process_id_,
render_frame_id_,
base::Bind(&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[blink::MEDIA_DEVICE_TYPE_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[blink::MEDIA_DEVICE_TYPE_AUDIO_INPUT]) {
blink::mojom::AudioInputDeviceCapabilities capabilities(
device_info.device_id, device_info.group_id,
media::AudioParameters::UnavailableDeviceParams());
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 base::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();
}
} // namespace content