| /* |
| * Copyright 2017 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 "common/camera_algorithm_bridge_impl.h" |
| |
| #include <sys/socket.h> |
| #include <sys/types.h> |
| #include <sys/un.h> |
| #include <unistd.h> |
| |
| #include <algorithm> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include <base/bind.h> |
| #include <base/files/file_path.h> |
| #include <base/files/file_util.h> |
| #include <base/logging.h> |
| #include <mojo/public/cpp/system/platform_handle.h> |
| |
| #include "cros-camera/common.h" |
| #include "cros-camera/constants.h" |
| |
| namespace cros { |
| |
| // static |
| std::unique_ptr<CameraAlgorithmBridge> CameraAlgorithmBridge::CreateInstance( |
| CameraAlgorithmBackend backend) { |
| return CameraAlgorithmBridge::CreateInstance( |
| backend, CameraMojoChannelManager::GetInstance()); |
| } |
| |
| // static |
| std::unique_ptr<CameraAlgorithmBridge> CameraAlgorithmBridge::CreateInstance( |
| CameraAlgorithmBackend backend, CameraMojoChannelManager* mojo_manager) { |
| VLOGF_ENTER(); |
| return std::make_unique<CameraAlgorithmBridgeImpl>(backend, mojo_manager); |
| } |
| |
| CameraAlgorithmBridgeImpl::CameraAlgorithmBridgeImpl( |
| CameraAlgorithmBackend backend, CameraMojoChannelManager* mojo_manager) |
| : mojo_manager_(mojo_manager), |
| ipc_bridge_(new IPCBridge(backend, mojo_manager)) {} |
| |
| CameraAlgorithmBridgeImpl::~CameraAlgorithmBridgeImpl() { |
| VLOGF_ENTER(); |
| |
| bool result = mojo_manager_->GetIpcTaskRunner()->DeleteSoon( |
| FROM_HERE, std::move(ipc_bridge_)); |
| DCHECK(result); |
| |
| VLOGF_EXIT(); |
| } |
| |
| int32_t CameraAlgorithmBridgeImpl::Initialize( |
| const camera_algorithm_callback_ops_t* callback_ops) { |
| VLOGF_ENTER(); |
| |
| const int32_t kInitializationRetryTimeoutMs = 3000; |
| const int32_t kInitializationWaitConnectionMs = 500; |
| const int32_t kInitializationRetrySleepUs = 100000; |
| auto get_elapsed_ms = [](struct timespec& start) { |
| struct timespec stop = {}; |
| if (clock_gettime(CLOCK_MONOTONIC, &stop)) { |
| LOG(ERROR) << "Failed to get clock time"; |
| return 0L; |
| } |
| return (stop.tv_sec - start.tv_sec) * 1000 + |
| (stop.tv_nsec - start.tv_nsec) / 1000000; |
| }; |
| struct timespec start_ts = {}; |
| if (clock_gettime(CLOCK_MONOTONIC, &start_ts)) { |
| LOG(ERROR) << "Failed to get clock time"; |
| } |
| int ret = 0; |
| do { |
| int32_t elapsed_ms = get_elapsed_ms(start_ts); |
| if (elapsed_ms >= kInitializationRetryTimeoutMs) { |
| ret = -ETIMEDOUT; |
| break; |
| } |
| auto future = cros::Future<int32_t>::Create(&relay_); |
| |
| mojo_manager_->GetIpcTaskRunner()->PostTask( |
| FROM_HERE, base::Bind(&CameraAlgorithmBridgeImpl::IPCBridge::Initialize, |
| ipc_bridge_->GetWeakPtr(), callback_ops, |
| cros::GetFutureCallback(future))); |
| |
| if (future->Wait(std::min(kInitializationWaitConnectionMs, |
| kInitializationRetryTimeoutMs - elapsed_ms))) { |
| ret = future->Get(); |
| if (ret == 0 || ret == -EINVAL) { |
| break; |
| } |
| } |
| usleep(kInitializationRetrySleepUs); |
| } while (1); |
| VLOGF_EXIT(); |
| return ret; |
| } |
| |
| int32_t CameraAlgorithmBridgeImpl::RegisterBuffer(int buffer_fd) { |
| VLOGF_ENTER(); |
| auto future = cros::Future<int32_t>::Create(&relay_); |
| |
| mojo_manager_->GetIpcTaskRunner()->PostTask( |
| FROM_HERE, |
| base::Bind(&CameraAlgorithmBridgeImpl::IPCBridge::RegisterBuffer, |
| ipc_bridge_->GetWeakPtr(), buffer_fd, |
| cros::GetFutureCallback(future))); |
| |
| future->Wait(); |
| VLOGF_EXIT(); |
| return future->Get(); |
| } |
| |
| void CameraAlgorithmBridgeImpl::Request(uint32_t req_id, |
| const std::vector<uint8_t>& req_header, |
| int32_t buffer_handle) { |
| VLOGF_ENTER(); |
| |
| mojo_manager_->GetIpcTaskRunner()->PostTask( |
| FROM_HERE, |
| base::Bind(&CameraAlgorithmBridgeImpl::IPCBridge::Request, |
| ipc_bridge_->GetWeakPtr(), req_id, req_header, buffer_handle)); |
| |
| VLOGF_EXIT(); |
| } |
| |
| void CameraAlgorithmBridgeImpl::DeregisterBuffers( |
| const std::vector<int32_t>& buffer_handles) { |
| VLOGF_ENTER(); |
| |
| mojo_manager_->GetIpcTaskRunner()->PostTask( |
| FROM_HERE, |
| base::Bind(&CameraAlgorithmBridgeImpl::IPCBridge::DeregisterBuffers, |
| ipc_bridge_->GetWeakPtr(), buffer_handles)); |
| |
| VLOGF_EXIT(); |
| } |
| |
| CameraAlgorithmBridgeImpl::IPCBridge::IPCBridge( |
| CameraAlgorithmBackend backend, CameraMojoChannelManager* mojo_manager) |
| : algo_backend_(backend), |
| callback_ops_(nullptr), |
| mojo_manager_(mojo_manager), |
| ipc_task_runner_(mojo_manager_->GetIpcTaskRunner()) {} |
| |
| CameraAlgorithmBridgeImpl::IPCBridge::~IPCBridge() { |
| DCHECK(ipc_task_runner_->BelongsToCurrentThread()); |
| VLOGF_ENTER(); |
| |
| Destroy(); |
| } |
| |
| void CameraAlgorithmBridgeImpl::IPCBridge::Initialize( |
| const camera_algorithm_callback_ops_t* callback_ops, |
| base::Callback<void(int32_t)> cb) { |
| DCHECK(ipc_task_runner_->BelongsToCurrentThread()); |
| VLOGF_ENTER(); |
| if (!callback_ops || !callback_ops->return_callback) { |
| cb.Run(-EINVAL); |
| return; |
| } |
| if (cb_impl_) { |
| LOGF(WARNING) |
| << "Camera algorithm bridge is already initialized. Reinitializing..."; |
| Destroy(); |
| } |
| |
| constexpr char kGpuAlgoJobFilePath[] = "/etc/init/cros-camera-gpu-algo.conf"; |
| switch (algo_backend_) { |
| case CameraAlgorithmBackend::kVendorCpu: |
| interface_ptr_ = mojo_manager_->CreateCameraAlgorithmOpsPtr( |
| cros::constants::kCrosCameraAlgoSocketPathString, "vendor_cpu"); |
| break; |
| case CameraAlgorithmBackend::kGoogleGpu: |
| if (!base::PathExists(base::FilePath(kGpuAlgoJobFilePath))) { |
| cb.Run(-EINVAL); |
| return; |
| } |
| interface_ptr_ = mojo_manager_->CreateCameraAlgorithmOpsPtr( |
| cros::constants::kCrosCameraGPUAlgoSocketPathString, "google_gpu"); |
| break; |
| case CameraAlgorithmBackend::kTest: |
| interface_ptr_ = mojo_manager_->CreateCameraAlgorithmOpsPtr( |
| cros::constants::kCrosCameraAlgoSocketPathString, "test"); |
| break; |
| } |
| if (!interface_ptr_) { |
| LOGF(ERROR) << "Failed to connect to the server"; |
| cb.Run(-EAGAIN); |
| return; |
| } |
| interface_ptr_.set_connection_error_handler(base::Bind( |
| &CameraAlgorithmBridgeImpl::IPCBridge::OnConnectionError, GetWeakPtr())); |
| cb_impl_.reset( |
| new CameraAlgorithmCallbackOpsImpl(ipc_task_runner_, callback_ops)); |
| interface_ptr_->Initialize(cb_impl_->CreateInterfacePtr(), cb); |
| callback_ops_ = callback_ops; |
| VLOGF_EXIT(); |
| } |
| |
| void CameraAlgorithmBridgeImpl::IPCBridge::RegisterBuffer( |
| int buffer_fd, base::Callback<void(int32_t)> cb) { |
| DCHECK(ipc_task_runner_->BelongsToCurrentThread()); |
| VLOGF_ENTER(); |
| if (!interface_ptr_.is_bound()) { |
| LOGF(ERROR) << "Interface is not bound probably because IPC is broken"; |
| cb.Run(-ECONNRESET); |
| return; |
| } |
| int dup_fd = dup(buffer_fd); |
| if (dup_fd < 0) { |
| PLOGF(ERROR) << "Failed to dup fd: "; |
| cb.Run(-errno); |
| return; |
| } |
| interface_ptr_->RegisterBuffer(mojo::WrapPlatformFile(dup_fd), cb); |
| } |
| |
| void CameraAlgorithmBridgeImpl::IPCBridge::Request( |
| uint32_t req_id, std::vector<uint8_t> req_header, int32_t buffer_handle) { |
| DCHECK(ipc_task_runner_->BelongsToCurrentThread()); |
| VLOGF_ENTER(); |
| if (!interface_ptr_.is_bound()) { |
| LOGF(ERROR) << "Interface is not bound probably because IPC is broken"; |
| return; |
| } |
| interface_ptr_->Request(req_id, std::move(req_header), buffer_handle); |
| VLOGF_EXIT(); |
| } |
| |
| void CameraAlgorithmBridgeImpl::IPCBridge::DeregisterBuffers( |
| std::vector<int32_t> buffer_handles) { |
| DCHECK(ipc_task_runner_->BelongsToCurrentThread()); |
| VLOGF_ENTER(); |
| if (!interface_ptr_.is_bound()) { |
| LOGF(ERROR) << "Interface is not bound probably because IPC is broken"; |
| return; |
| } |
| interface_ptr_->DeregisterBuffers(std::move(buffer_handles)); |
| } |
| |
| void CameraAlgorithmBridgeImpl::IPCBridge::OnConnectionError() { |
| DCHECK(ipc_task_runner_->BelongsToCurrentThread()); |
| DCHECK(callback_ops_); |
| VLOGF_ENTER(); |
| Destroy(); |
| if (callback_ops_->notify) { |
| callback_ops_->notify(callback_ops_, CAMERA_ALGORITHM_MSG_IPC_ERROR); |
| } |
| VLOGF_EXIT(); |
| } |
| |
| void CameraAlgorithmBridgeImpl::IPCBridge::Destroy() { |
| DCHECK(ipc_task_runner_->BelongsToCurrentThread()); |
| VLOGF_ENTER(); |
| if (interface_ptr_.is_bound()) { |
| cb_impl_.reset(); |
| interface_ptr_.reset(); |
| } |
| VLOGF_EXIT(); |
| } |
| |
| base::WeakPtr<CameraAlgorithmBridgeImpl::IPCBridge> |
| CameraAlgorithmBridgeImpl::IPCBridge::GetWeakPtr() { |
| return weak_ptr_factory_.GetWeakPtr(); |
| } |
| |
| } // namespace cros |