blob: 723ed67a12b1b58ae3018676d23273e64dceb668 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/media_effects/media_device_info.h"
#include "base/auto_reset.h"
#include "base/no_destructor.h"
#include "content/public/browser/audio_service.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/video_capture_service.h"
#include "services/video_capture/public/mojom/video_capture_service.mojom.h"
namespace {
media_effects::MediaDeviceInfo* g_instance_override = nullptr;
}
namespace media_effects {
std::optional<std::string> GetRealDefaultDeviceId(
const std::vector<media::AudioDeviceDescription>& infos) {
for (const auto& info : infos) {
if (info.is_system_default &&
!media::AudioDeviceDescription::IsDefaultDevice(info.unique_id)) {
return info.unique_id;
}
}
return std::nullopt;
}
std::optional<std::string> GetRealCommunicationsDeviceId(
const std::vector<media::AudioDeviceDescription>& infos) {
for (const auto& info : infos) {
if (info.is_communications_device &&
!media::AudioDeviceDescription::IsCommunicationsDevice(
info.unique_id)) {
return info.unique_id;
}
}
return std::nullopt;
}
std::vector<std::string> GetRealAudioDeviceNames(
const std::vector<media::AudioDeviceDescription>& infos) {
std::vector<std::string> real_names;
for (const auto& info : infos) {
if (!media::AudioDeviceDescription::IsDefaultDevice(info.unique_id) &&
!media::AudioDeviceDescription::IsCommunicationsDevice(
info.unique_id)) {
real_names.push_back(info.device_name);
}
}
return real_names;
}
std::vector<std::string> GetRealVideoDeviceNames(
const std::vector<media::VideoCaptureDeviceInfo>& infos) {
std::vector<std::string> names;
for (const auto& info : infos) {
names.push_back(info.descriptor.GetNameAndModel());
}
return names;
}
MediaDeviceInfo::MediaDeviceInfo() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (auto* monitor = base::SystemMonitor::Get(); monitor) {
monitor->AddDevicesChangedObserver(this);
}
content::GetVideoCaptureService().ConnectToVideoSourceProvider(
video_source_provider_.BindNewPipeAndPassReceiver());
content::GetAudioService().BindSystemInfo(
audio_system_info_.BindNewPipeAndPassReceiver());
audio_system_info_.reset_on_disconnect();
video_source_provider_.reset_on_disconnect();
// Initialize the device lists. `base::SystemMonitor` only calls observers for
// changes that happen after they start observing.
OnDevicesChanged(base::SystemMonitor::DeviceType::DEVTYPE_AUDIO);
OnDevicesChanged(base::SystemMonitor::DeviceType::DEVTYPE_VIDEO_CAPTURE);
}
MediaDeviceInfo::~MediaDeviceInfo() {
if (auto* monitor = base::SystemMonitor::Get(); monitor) {
monitor->RemoveDevicesChangedObserver(this);
}
}
MediaDeviceInfo* MediaDeviceInfo::GetInstance() {
if (g_instance_override) {
return g_instance_override;
}
static base::NoDestructor<MediaDeviceInfo> instance;
return instance.get();
}
std::pair<std::unique_ptr<MediaDeviceInfo>, base::AutoReset<MediaDeviceInfo*>>
MediaDeviceInfo::OverrideInstanceForTesting() {
auto instance = base::WrapUnique(new MediaDeviceInfo());
base::AutoReset auto_reset{
&g_instance_override,
// Can't use std::make_optional because the constructor is private.
instance.get()};
return std::make_pair(std::move(instance), std::move(auto_reset));
}
std::optional<media::AudioDeviceDescription>
MediaDeviceInfo::GetAudioDeviceInfoForId(const std::string& device_id) const {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!audio_device_infos_) {
return std::nullopt;
}
auto device_iter =
std::ranges::find(audio_device_infos_.value(), device_id,
&media::AudioDeviceDescription::unique_id);
if (device_iter != audio_device_infos_->end()) {
return *device_iter;
}
return std::nullopt;
}
std::optional<media::VideoCaptureDeviceInfo>
MediaDeviceInfo::GetVideoDeviceInfoForId(const std::string& device_id) const {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!video_device_infos_) {
return std::nullopt;
}
auto device_iter =
std::ranges::find(video_device_infos_.value(), device_id,
[](const media::VideoCaptureDeviceInfo& device) {
return device.descriptor.device_id;
});
if (device_iter != video_device_infos_->end()) {
return *device_iter;
}
return std::nullopt;
}
const std::optional<std::vector<media::AudioDeviceDescription>>&
MediaDeviceInfo::GetAudioDeviceInfos() const {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
return audio_device_infos_;
}
const std::optional<std::vector<media::VideoCaptureDeviceInfo>>&
MediaDeviceInfo::GetVideoDeviceInfos() const {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
return video_device_infos_;
}
void MediaDeviceInfo::GetAudioInputStreamParameters(
const std::string& device_id,
audio::mojom::SystemInfo::GetInputStreamParametersCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (audio_system_info_) {
audio_system_info_->GetInputStreamParameters(device_id,
std::move(callback));
}
}
void MediaDeviceInfo::AddObserver(Observer* observer) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!observers_.HasObserver(observer)) {
observers_.AddObserver(observer);
}
}
void MediaDeviceInfo::RemoveObserver(Observer* observer) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
observers_.RemoveObserver(observer);
}
void MediaDeviceInfo::OnDevicesChanged(
base::SystemMonitor::DeviceType device_type) {
if (device_type == base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE &&
video_source_provider_) {
// Unretained is safe here because the `video_source_provider_` remote will
// be destroyed before `this`.
video_source_provider_->GetSourceInfos(base::BindOnce(
[](MediaDeviceInfo* mdi,
video_capture::mojom::VideoSourceProvider::GetSourceInfosResult,
const std::vector<media::VideoCaptureDeviceInfo>& device_infos) {
mdi->OnVideoDeviceInfosReceived(device_infos);
},
base::Unretained(this)));
} else if (device_type == base::SystemMonitor::DEVTYPE_AUDIO &&
audio_system_info_) {
// Unretained is safe here because the `audio_system_info_` remote will
// be destroyed before `this`.
audio_system_info_->GetInputDeviceDescriptions(
base::BindOnce(&MediaDeviceInfo::OnAudioDeviceDescriptionsReceived,
base::Unretained(this)));
}
}
void MediaDeviceInfo::OnAudioDeviceDescriptionsReceived(
const std::vector<media::AudioDeviceDescription> device_infos) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
audio_device_infos_ = device_infos;
NotifyAudioDevicesChanged();
}
void MediaDeviceInfo::OnVideoDeviceInfosReceived(
const std::vector<media::VideoCaptureDeviceInfo>& device_infos) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
video_device_infos_ = device_infos;
NotifyVideoDevicesChanged();
}
void MediaDeviceInfo::NotifyAudioDevicesChanged() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
for (auto& observer : observers_) {
observer.OnAudioDevicesChanged(audio_device_infos_);
}
}
void MediaDeviceInfo::NotifyVideoDevicesChanged() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
for (auto& observer : observers_) {
observer.OnVideoDevicesChanged(video_device_infos_);
}
}
} // namespace media_effects