// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/capture/video/chromeos/camera_app_device_bridge_impl.h"
#include <string>
#include "base/command_line.h"
#include "base/functional/callback_helpers.h"
#include "base/task/bind_post_task.h"
#include "base/task/single_thread_task_runner.h"
#include "components/device_event_log/device_event_log.h"
#include "media/base/media_switches.h"
#include "media/capture/video/chromeos/public/cros_features.h"
#include "media/capture/video/chromeos/video_capture_device_chromeos_halv3.h"
namespace media {
CameraAppDeviceBridgeImpl::CameraAppDeviceBridgeImpl()
: ipc_thread_("CameraAppDeviceBridgeThread") {
const base::CommandLine* command_line =
base::CommandLine::ForCurrentProcess();
bool use_fake_camera =
command_line->HasSwitch(switches::kUseFakeDeviceForMediaStream);
bool use_file_camera =
command_line->HasSwitch(switches::kUseFileForFakeVideoCapture);
is_supported_ =
ShouldUseCrosCameraService() && !use_fake_camera && !use_file_camera;
receivers_.set_disconnect_with_reason_handler(
base::BindRepeating([](uint32_t reason, const std::string& description) {
CAMERA_LOG(EVENT) << "Receiver disconnected, reason " << reason << " ("
<< description << ")";
}));
CHECK(ipc_thread_.Start())
<< "Can't bootstrap the CameraAppDeviceBridge thread.";
ipc_task_runner_ = ipc_thread_.task_runner();
}
CameraAppDeviceBridgeImpl::~CameraAppDeviceBridgeImpl() {
ipc_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&CameraAppDeviceBridgeImpl::StopOnIPCThread,
base::Unretained(this)));
ipc_thread_.Stop();
}
// static
CameraAppDeviceBridgeImpl* CameraAppDeviceBridgeImpl::GetInstance() {
return base::Singleton<CameraAppDeviceBridgeImpl>::get();
}
void CameraAppDeviceBridgeImpl::BindReceiver(
mojo::PendingReceiver<cros::mojom::CameraAppDeviceBridge> receiver) {
ipc_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&CameraAppDeviceBridgeImpl::BindReceiverOnIPCThread,
base::Unretained(this), std::move(receiver)));
}
void CameraAppDeviceBridgeImpl::BindReceiverOnIPCThread(
mojo::PendingReceiver<cros::mojom::CameraAppDeviceBridge> receiver) {
CHECK(ipc_task_runner_->BelongsToCurrentThread());
receivers_.Add(this, std::move(receiver));
}
void CameraAppDeviceBridgeImpl::OnVideoCaptureDeviceCreated(
const std::string& device_id,
scoped_refptr<base::SingleThreadTaskRunner> vcd_task_runner) {
{
base::AutoLock lock(task_runner_map_lock_);
DCHECK_EQ(vcd_task_runners_.count(device_id), 0u);
vcd_task_runners_.emplace(device_id, vcd_task_runner);
}
// Update the cached camera info while VCD is connected as well so that when
// the camera service is restarted the camera info can be updated properly.
UpdateCameraInfo(device_id);
}
void CameraAppDeviceBridgeImpl::OnVideoCaptureDeviceClosing(
const std::string& device_id) {
auto remove_vcd_task_runner =
base::BindOnce(&CameraAppDeviceBridgeImpl::RemoveVCDTaskRunner,
base::Unretained(this), device_id);
base::AutoLock lock(task_runner_map_lock_);
DCHECK_EQ(vcd_task_runners_.count(device_id), 1u);
// Since the IPC thread is owned by VCD and the CameraAppBridgeImpl is a
// singleton which has longer lifetime than VCD, it is safe to use
// base::Unretained(this) here.
vcd_task_runners_[device_id]->PostTask(
FROM_HERE,
base::BindOnce(
&CameraAppDeviceBridgeImpl::InvalidateDevicePtrsOnDeviceIpcThread,
base::Unretained(this), device_id,
/* should_disable_new_ptrs */ false,
std::move(remove_vcd_task_runner)));
}
void CameraAppDeviceBridgeImpl::OnDeviceMojoDisconnected(
const std::string& device_id) {
auto remove_device = base::BindPostTaskToCurrentDefault(
base::BindOnce(&CameraAppDeviceBridgeImpl::RemoveCameraAppDevice,
base::Unretained(this), device_id));
{
base::AutoLock lock(task_runner_map_lock_);
auto it = vcd_task_runners_.find(device_id);
if (it != vcd_task_runners_.end()) {
// Since the IPC thread is owned by VCD and the CameraAppBridgeImpl is a
// singleton which has longer lifetime than VCD, it is safe to use
// base::Unretained(this) here.
it->second->PostTask(
FROM_HERE,
base::BindOnce(
&CameraAppDeviceBridgeImpl::InvalidateDevicePtrsOnDeviceIpcThread,
base::Unretained(this), device_id,
/* should_disable_new_ptrs */ true, std::move(remove_device)));
return;
}
}
std::move(remove_device).Run();
}
void CameraAppDeviceBridgeImpl::UpdateCameraInfo(const std::string& device_id) {
cros::mojom::CameraInfoPtr camera_info;
{
base::AutoLock lock(camera_info_getter_lock_);
DCHECK(camera_info_getter_);
camera_info = camera_info_getter_.Run(device_id);
}
{
base::AutoLock lock(device_map_lock_);
auto it = camera_app_devices_.find(device_id);
if (it != camera_app_devices_.end()) {
const auto& device = it->second;
device->OnCameraInfoUpdated(std::move(camera_info));
}
}
}
void CameraAppDeviceBridgeImpl::InvalidateDevicePtrsOnDeviceIpcThread(
const std::string& device_id,
bool should_disable_new_ptrs,
base::OnceClosure callback) {
auto device = GetWeakCameraAppDevice(device_id);
if (device) {
device->ResetOnDeviceIpcThread(std::move(callback),
should_disable_new_ptrs);
} else {
std::move(callback).Run();
}
}
void CameraAppDeviceBridgeImpl::SetCameraInfoGetter(
CameraInfoGetter camera_info_getter) {
base::AutoLock lock(camera_info_getter_lock_);
camera_info_getter_ = std::move(camera_info_getter);
}
void CameraAppDeviceBridgeImpl::UnsetCameraInfoGetter() {
base::AutoLock lock(camera_info_getter_lock_);
camera_info_getter_ = base::NullCallback();
}
void CameraAppDeviceBridgeImpl::SetVirtualDeviceController(
VirtualDeviceController virtual_device_controller) {
base::AutoLock lock(virtual_device_controller_lock_);
virtual_device_controller_ = std::move(virtual_device_controller);
}
void CameraAppDeviceBridgeImpl::UnsetVirtualDeviceController() {
base::AutoLock lock(virtual_device_controller_lock_);
virtual_device_controller_ = base::NullCallback();
}
base::WeakPtr<CameraAppDeviceImpl>
CameraAppDeviceBridgeImpl::GetWeakCameraAppDevice(
const std::string& device_id) {
base::AutoLock lock(device_map_lock_);
auto it = camera_app_devices_.find(device_id);
if (it == camera_app_devices_.end()) {
return nullptr;
}
return it->second->GetWeakPtr();
}
void CameraAppDeviceBridgeImpl::RemoveCameraAppDevice(
const std::string& device_id) {
base::AutoLock lock(device_map_lock_);
auto it = camera_app_devices_.find(device_id);
if (it == camera_app_devices_.end()) {
return;
}
camera_app_devices_.erase(it);
}
void CameraAppDeviceBridgeImpl::RemoveVCDTaskRunner(
const std::string& device_id) {
base::AutoLock lock(task_runner_map_lock_);
vcd_task_runners_.erase(device_id);
}
void CameraAppDeviceBridgeImpl::GetCameraAppDevice(
const std::string& device_id,
GetCameraAppDeviceCallback callback) {
CHECK(is_supported_);
CHECK(ipc_task_runner_->BelongsToCurrentThread());
CHECK(ui_task_runner_);
mojo::PendingRemote<cros::mojom::CameraAppDevice> device_remote;
{
base::AutoLock lock(device_map_lock_);
CameraAppDeviceImpl* device;
auto it = camera_app_devices_.find(device_id);
if (it != camera_app_devices_.end()) {
device = it->second.get();
} else {
auto device_impl = std::make_unique<media::CameraAppDeviceImpl>(
device_id, ui_task_runner_);
const auto& iterator =
camera_app_devices_.emplace(device_id, std::move(device_impl)).first;
device = iterator->second.get();
}
device->BindReceiver(device_remote.InitWithNewPipeAndPassReceiver());
}
UpdateCameraInfo(device_id);
std::move(callback).Run(cros::mojom::GetCameraAppDeviceStatus::kSuccess,
std::move(device_remote));
}
void CameraAppDeviceBridgeImpl::IsSupported(IsSupportedCallback callback) {
CHECK(ipc_task_runner_->BelongsToCurrentThread());
std::move(callback).Run(is_supported_);
}
void CameraAppDeviceBridgeImpl::SetVirtualDeviceEnabled(
const std::string& device_id,
bool enabled,
SetVirtualDeviceEnabledCallback callback) {
CHECK(ipc_task_runner_->BelongsToCurrentThread());
base::AutoLock lock(virtual_device_controller_lock_);
if (!virtual_device_controller_) {
std::move(callback).Run(false);
return;
}
virtual_device_controller_.Run(device_id, enabled);
std::move(callback).Run(true);
}
void CameraAppDeviceBridgeImpl::SetDeviceInUse(const std::string& device_id,
bool in_use) {
base::AutoLock lock(devices_in_use_lock_);
if (in_use) {
devices_in_use_.insert(device_id);
} else {
devices_in_use_.erase(device_id);
}
}
void CameraAppDeviceBridgeImpl::IsDeviceInUse(const std::string& device_id,
IsDeviceInUseCallback callback) {
bool in_use;
{
base::AutoLock lock(devices_in_use_lock_);
in_use = devices_in_use_.contains(device_id);
}
std::move(callback).Run(in_use);
}
void CameraAppDeviceBridgeImpl::StopOnIPCThread() {
CHECK(ipc_task_runner_->BelongsToCurrentThread());
base::AutoLock lock(device_map_lock_);
camera_app_devices_.clear();
receivers_.Clear();
}
void CameraAppDeviceBridgeImpl::SetUITaskRunner(
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
ui_task_runner_ = ui_task_runner;
}
} // namespace media