blob: 06b56e836d7c9e24fe5c9207446cd0bef038ea48 [file] [log] [blame]
// Copyright 2018 The Chromium OS 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 "media_perception/mojo_connector.h"
#include <map>
#include <utility>
#include <vector>
namespace mri {
namespace {
DeviceAccessResultCode GetDeviceAccessResultCode(
video_capture::mojom::DeviceAccessResultCode code) {
switch (code) {
case video_capture::mojom::DeviceAccessResultCode::NOT_INITIALIZED:
return DeviceAccessResultCode::NOT_INITIALIZED;
case video_capture::mojom::DeviceAccessResultCode::SUCCESS:
return DeviceAccessResultCode::SUCCESS;
case video_capture::mojom::DeviceAccessResultCode::ERROR_DEVICE_NOT_FOUND:
return DeviceAccessResultCode::ERROR_DEVICE_NOT_FOUND;
}
return DeviceAccessResultCode::RESULT_UNKNOWN;
}
PixelFormat GetPixelFormatFromVideoCapturePixelFormat(
media::mojom::VideoCapturePixelFormat format) {
switch (format) {
case media::mojom::VideoCapturePixelFormat::I420:
return PixelFormat::I420;
case media::mojom::VideoCapturePixelFormat::MJPEG:
return PixelFormat::MJPEG;
default:
return PixelFormat::FORMAT_UNKNOWN;
}
return PixelFormat::FORMAT_UNKNOWN;
}
media::mojom::VideoCapturePixelFormat GetVideoCapturePixelFormatFromPixelFormat(
PixelFormat pixel_format) {
switch (pixel_format) {
case PixelFormat::I420:
return media::mojom::VideoCapturePixelFormat::I420;
case PixelFormat::MJPEG:
return media::mojom::VideoCapturePixelFormat::MJPEG;
default:
return media::mojom::VideoCapturePixelFormat::UNKNOWN;
}
return media::mojom::VideoCapturePixelFormat::UNKNOWN;
}
constexpr char kConnectorPipe[] = "mpp-connector-pipe";
} // namespace
MojoConnector::MojoConnector(): ipc_thread_("IpcThread") {
mojo::edk::Init();
LOG(INFO) << "Starting IPC thread.";
if (!ipc_thread_.StartWithOptions(
base::Thread::Options(base::MessageLoop::TYPE_IO, 0))) {
LOG(ERROR) << "Failed to start IPC Thread";
}
mojo::edk::InitIPCSupport(ipc_thread_.task_runner());
unique_device_counter_ = 1;
}
void MojoConnector::SetVideoCaptureServiceClient(
std::shared_ptr<VideoCaptureServiceClient> video_capture_service_client) {
video_capture_service_client_ = video_capture_service_client;
}
void MojoConnector::SetRtanalytics(
std::shared_ptr<Rtanalytics> rtanalytics) {
rtanalytics_ = rtanalytics;
}
void MojoConnector::ReceiveMojoInvitationFileDescriptor(int fd_int) {
base::ScopedFD fd(fd_int);
if (!fd.is_valid()) {
LOG(ERROR) << "FD is not valid.";
return;
}
ipc_thread_.task_runner()->PostTask(
FROM_HERE, base::Bind(&MojoConnector::AcceptConnectionOnIpcThread,
base::Unretained(this), base::Passed(&fd)));
}
void MojoConnector::OnConnectionErrorOrClosed() {
LOG(ERROR) << "Connection error/closed received";
}
void MojoConnector::OnDeviceFactoryConnectionErrorOrClosed() {
std::lock_guard<std::mutex> lock(vcs_connection_state_mutex_);
is_connected_to_vcs_ = false;
}
void MojoConnector::AcceptConnectionOnIpcThread(base::ScopedFD fd) {
CHECK(ipc_thread_.task_runner()->BelongsToCurrentThread());
mojo::edk::SetParentPipeHandle(
mojo::edk::ScopedPlatformHandle(mojo::edk::PlatformHandle(fd.release())));
mojo::ScopedMessagePipeHandle child_pipe =
mojo::edk::CreateChildMessagePipe(kConnectorPipe);
if (!child_pipe.is_valid()) {
LOG(ERROR) << "child_pipe is not valid";
}
media_perception_service_impl_ = std::make_unique<MediaPerceptionServiceImpl>(
std::move(child_pipe),
base::Bind(&MojoConnector::OnConnectionErrorOrClosed,
base::Unretained(this)),
video_capture_service_client_,
rtanalytics_);
}
void MojoConnector::ConnectToVideoCaptureService() {
std::lock_guard<std::mutex> lock(vcs_connection_state_mutex_);
if (!is_connected_to_vcs_) {
ipc_thread_.task_runner()->PostTask(
FROM_HERE,
base::Bind(&MojoConnector::ConnectToVideoCaptureServiceOnIpcThread,
base::Unretained(this)));
is_connected_to_vcs_ = true;
}
}
void MojoConnector::ConnectToVideoCaptureServiceOnIpcThread() {
unique_device_counter_ = 1;
media_perception_service_impl_->ConnectToVideoCaptureService(
mojo::MakeRequest(&device_factory_));
device_factory_.set_connection_error_handler(
base::Bind(&MojoConnector::OnDeviceFactoryConnectionErrorOrClosed,
base::Unretained(this)));
}
bool MojoConnector::IsConnectedToVideoCaptureService() {
std::lock_guard<std::mutex> lock(vcs_connection_state_mutex_);
return is_connected_to_vcs_;
}
void MojoConnector::GetDevices(
const VideoCaptureServiceClient::GetDevicesCallback& callback) {
ipc_thread_.task_runner()->PostTask(
FROM_HERE, base::Bind(&MojoConnector::GetDevicesOnIpcThread,
base::Unretained(this), callback));
}
void MojoConnector::GetDevicesOnIpcThread(
const VideoCaptureServiceClient::GetDevicesCallback& callback) {
device_factory_->GetDeviceInfos(base::Bind(
&MojoConnector::OnDeviceInfosReceived, base::Unretained(this), callback));
}
std::string MojoConnector::GetObfuscatedDeviceId(
const std::string& device_id,
const std::string& display_name) {
const std::string unique_id = device_id + display_name;
std::map<std::string, std::string>::iterator it =
unique_id_map_.find(unique_id);
if (it == unique_id_map_.end()) {
std::string obfuscated_id = std::to_string(unique_device_counter_);
unique_id_map_.insert(
std::make_pair(unique_id, obfuscated_id));
// Increment the counter so the next obfuscated id is different.
unique_device_counter_++;
return obfuscated_id;
}
// Obfuscated id already created for this device.
return it->second;
}
void MojoConnector::OnDeviceInfosReceived(
const VideoCaptureServiceClient::GetDevicesCallback& callback,
std::vector<media::mojom::VideoCaptureDeviceInfoPtr> infos) {
LOG(INFO) << "Got callback for device infos.";
std::vector<SerializedVideoDevice> devices;
for (const auto& capture_device : infos) {
VideoDevice device;
const std::string device_id = capture_device->descriptor->device_id;
const std::string obfuscated_device_id = GetObfuscatedDeviceId(
device_id, capture_device->descriptor->display_name);
obfuscated_device_id_map_.insert(
std::make_pair(obfuscated_device_id, device_id));
device.set_id(obfuscated_device_id);
device.set_display_name(capture_device->descriptor->display_name);
device.set_model_id(capture_device->descriptor->model_id);
LOG(INFO) << "Device: " << device.display_name();
for (const auto& capture_format : capture_device->supported_formats) {
VideoStreamParams supported_format;
supported_format.set_width_in_pixels(capture_format->frame_size->width);
supported_format.set_height_in_pixels(capture_format->frame_size->height);
supported_format.set_frame_rate_in_frames_per_second(
capture_format->frame_rate);
supported_format.set_pixel_format(
GetPixelFormatFromVideoCapturePixelFormat(
capture_format->pixel_format));
*device.add_supported_configurations() = supported_format;
}
const int size = device.ByteSize();
std::vector<uint8_t> bytes(size, 0);
CHECK(device.SerializeToArray(bytes.data(), size))
<< "Failed to serialize mri::VideoDevice proto.";
devices.push_back(bytes);
}
callback(devices);
}
void MojoConnector::OpenDevice(
const std::string& device_id,
const VideoCaptureServiceClient::OpenDeviceCallback& callback) {
ipc_thread_.task_runner()->PostTask(
FROM_HERE, base::Bind(&MojoConnector::OpenDeviceOnIpcThread,
base::Unretained(this), device_id, callback));
}
void MojoConnector::OpenDeviceOnIpcThread(
const std::string& device_id,
const VideoCaptureServiceClient::OpenDeviceCallback& callback) {
std::map<std::string, std::string>::iterator it =
obfuscated_device_id_map_.find(device_id);
if (it == obfuscated_device_id_map_.end()) {
LOG(ERROR) << "Device id not found in obfuscated_device_id map.";
return;
}
std::map<std::string, video_capture::mojom::DevicePtr>::iterator
device_it = device_id_to_active_device_map_.find(device_id);
// Check to see if the device is already successfully opened.
if (device_it != device_id_to_active_device_map_.end()) {
callback(DeviceAccessResultCode::SUCCESS);
return;
}
video_capture::mojom::DevicePtr new_device;
device_factory_->CreateDevice(
it->second, mojo::MakeRequest(&new_device),
base::Bind(&MojoConnector::OnOpenDeviceCallback,
base::Unretained(this), device_id, callback));
device_id_to_active_device_map_.insert(
std::make_pair(device_id, std::move(new_device)));
}
void MojoConnector::OnOpenDeviceCallback(
const std::string& device_id,
const VideoCaptureServiceClient::OpenDeviceCallback& callback,
video_capture::mojom::DeviceAccessResultCode code) {
// Delete the device from the active device map if we were not successful in
// opening it.
if (code != video_capture::mojom::DeviceAccessResultCode::SUCCESS) {
device_id_to_active_device_map_.erase(device_id);
}
callback(GetDeviceAccessResultCode(code));
}
void MojoConnector::StartVideoCapture(
const std::string& device_id,
std::shared_ptr<ReceiverImpl> receiver_impl,
const VideoStreamParams& capture_format) {
ipc_thread_.task_runner()->PostTask(
FROM_HERE, base::Bind(&MojoConnector::StartVideoCaptureOnIpcThread,
base::Unretained(this),
device_id,
receiver_impl,
capture_format));
}
void MojoConnector::StartVideoCaptureOnIpcThread(
const std::string& device_id,
std::shared_ptr<ReceiverImpl> receiver_impl,
const VideoStreamParams& capture_format) {
LOG(INFO) << "Starting video capture on ipc thread.";
std::map<std::string, video_capture::mojom::DevicePtr>::iterator
it = device_id_to_active_device_map_.find(device_id);
if (it == device_id_to_active_device_map_.end()) {
LOG(ERROR) << "Device id not found in active device map.";
return;
}
auto requested_settings = media::mojom::VideoCaptureParams::New();
requested_settings->requested_format =
media::mojom::VideoCaptureFormat::New();
requested_settings->requested_format->frame_rate =
capture_format.frame_rate_in_frames_per_second();
requested_settings->requested_format->pixel_format =
GetVideoCapturePixelFormatFromPixelFormat(capture_format.pixel_format());
requested_settings->requested_format->frame_size = gfx::mojom::Size::New();
requested_settings->requested_format->frame_size->width =
capture_format.width_in_pixels();
requested_settings->requested_format->frame_size->height =
capture_format.height_in_pixels();
requested_settings->buffer_type =
media::mojom::VideoCaptureBufferType::kSharedMemoryViaRawFileDescriptor;
it->second->Start(std::move(requested_settings),
receiver_impl->CreateInterfacePtr());
}
void MojoConnector::StopVideoCapture(const std::string& device_id) {
ipc_thread_.task_runner()->PostTask(
FROM_HERE, base::Bind(&MojoConnector::StopVideoCaptureOnIpcThread,
base::Unretained(this), device_id));
}
void MojoConnector::StopVideoCaptureOnIpcThread(const std::string& device_id) {
device_id_to_active_device_map_.erase(device_id);
}
void MojoConnector::CreateVirtualDevice(
const VideoDevice& video_device,
std::shared_ptr<ProducerImpl> producer_impl,
const VideoCaptureServiceClient::VirtualDeviceCallback& callback) {
ipc_thread_.task_runner()->PostTask(
FROM_HERE, base::Bind(&MojoConnector::CreateVirtualDeviceOnIpcThread,
base::Unretained(this), video_device, producer_impl,
callback));
}
void MojoConnector::CreateVirtualDeviceOnIpcThread(
const VideoDevice& video_device,
std::shared_ptr<ProducerImpl> producer_impl,
const VideoCaptureServiceClient::VirtualDeviceCallback& callback) {
media::mojom::VideoCaptureDeviceInfoPtr info =
media::mojom::VideoCaptureDeviceInfo::New();
// TODO(b/3743548): After libchrome uprev, assigning to std::vector<> is
// just redundant, so should be removed.
info->supported_formats = std::vector<media::mojom::VideoCaptureFormatPtr>();
info->descriptor = media::mojom::VideoCaptureDeviceDescriptor::New();
info->descriptor->model_id = video_device.model_id();
info->descriptor->device_id = video_device.id();
info->descriptor->display_name = video_device.display_name();
info->descriptor->capture_api = media::mojom::VideoCaptureApi::VIRTUAL_DEVICE;
producer_impl->RegisterVirtualDeviceAtFactory(&device_factory_,
std::move(info));
const int size = video_device.ByteSize();
std::vector<uint8_t> bytes(size /*count*/, 0 /*value*/);
CHECK(video_device.SerializeToArray(bytes.data(), size))
<< "Failed to serialize mri::VideoDevice proto.";
callback(bytes);
}
void MojoConnector::PushFrameToVirtualDevice(
std::shared_ptr<ProducerImpl> producer_impl, base::TimeDelta timestamp,
std::unique_ptr<const uint8_t[]> data, int data_size,
PixelFormat pixel_format, int frame_width, int frame_height) {
ipc_thread_.task_runner()->PostTask(
FROM_HERE, base::Bind(&MojoConnector::PushFrameToVirtualDeviceOnIpcThread,
base::Unretained(this), producer_impl, timestamp,
base::Passed(&data), data_size, pixel_format,
frame_width, frame_height));
}
void MojoConnector::PushFrameToVirtualDeviceOnIpcThread(
std::shared_ptr<ProducerImpl> producer_impl, base::TimeDelta timestamp,
std::unique_ptr<const uint8_t[]> data, int data_size,
PixelFormat pixel_format, int frame_width, int frame_height) {
producer_impl->PushNextFrame(
producer_impl, timestamp, std::move(data), data_size,
GetVideoCapturePixelFormatFromPixelFormat(pixel_format), frame_width,
frame_height);
}
} // namespace mri