| // 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> |
| |
| #include "media_perception/serialized_proto.h" |
| |
| 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::SetChromeAudioServiceClient( |
| std::shared_ptr<ChromeAudioServiceClient> chrome_audio_service_client) { |
| chrome_audio_service_client_ = chrome_audio_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_, |
| chrome_audio_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; |
| } |
| devices.push_back(Serialized<VideoDevice>(device).GetBytes()); |
| } |
| 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)); |
| |
| callback(Serialized<VideoDevice>(video_device).GetBytes()); |
| } |
| |
| 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 |
| |