| // Copyright 2017 The Chromium 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/capture/video/chromeos/camera_hal_delegate.h" |
| |
| #include <fcntl.h> |
| #include <sys/uio.h> |
| |
| #include <algorithm> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/callback_helpers.h" |
| #include "base/containers/flat_set.h" |
| #include "base/posix/safe_strerror.h" |
| #include "base/process/launch.h" |
| #include "base/strings/strcat.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_piece.h" |
| #include "base/strings/string_split.h" |
| #include "base/strings/string_util.h" |
| #include "base/system/system_monitor.h" |
| #include "base/unguessable_token.h" |
| #include "components/device_event_log/device_event_log.h" |
| #include "media/capture/video/chromeos/camera_buffer_factory.h" |
| #include "media/capture/video/chromeos/camera_hal_dispatcher_impl.h" |
| #include "media/capture/video/chromeos/camera_metadata_utils.h" |
| #include "media/capture/video/chromeos/video_capture_device_chromeos_delegate.h" |
| #include "media/capture/video/chromeos/video_capture_device_chromeos_halv3.h" |
| |
| namespace media { |
| |
| namespace { |
| |
| constexpr int32_t kDefaultFps = 30; |
| constexpr char kVirtualPrefix[] = "VIRTUAL_"; |
| |
| constexpr base::TimeDelta kEventWaitTimeoutSecs = base::Seconds(1); |
| |
| class LocalCameraClientObserver : public CameraClientObserver { |
| public: |
| explicit LocalCameraClientObserver( |
| scoped_refptr<CameraHalDelegate> camera_hal_delegate, |
| cros::mojom::CameraClientType type, |
| base::UnguessableToken auth_token) |
| : CameraClientObserver(type, std::move(auth_token)), |
| camera_hal_delegate_(std::move(camera_hal_delegate)) {} |
| |
| void OnChannelCreated( |
| mojo::PendingRemote<cros::mojom::CameraModule> camera_module) override { |
| camera_hal_delegate_->SetCameraModule(std::move(camera_module)); |
| } |
| |
| private: |
| scoped_refptr<CameraHalDelegate> camera_hal_delegate_; |
| DISALLOW_IMPLICIT_CONSTRUCTORS(LocalCameraClientObserver); |
| }; |
| |
| // chromeos::system::StatisticsProvider::IsRunningOnVM() is not available in |
| // unittest. |
| bool IsRunningOnVM() { |
| static bool is_vm = []() { |
| std::string output; |
| if (!base::GetAppOutput({"crossystem", "inside_vm"}, &output)) { |
| return false; |
| } |
| return output == "1"; |
| }(); |
| return is_vm; |
| } |
| |
| bool IsVividLoaded() { |
| std::string output; |
| if (!base::GetAppOutput({"lsmod"}, &output)) { |
| return false; |
| } |
| |
| std::vector<base::StringPiece> lines = base::SplitStringPieceUsingSubstr( |
| output, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY); |
| |
| return std::any_of(lines.begin(), lines.end(), [](const auto& line) { |
| return base::StartsWith(line, "vivid", base::CompareCase::SENSITIVE); |
| }); |
| } |
| |
| void NotifyVideoCaptureDevicesChanged() { |
| base::SystemMonitor* monitor = base::SystemMonitor::Get(); |
| // |monitor| might be nullptr in unittest. |
| if (monitor) { |
| monitor->ProcessDevicesChanged( |
| base::SystemMonitor::DeviceType::DEVTYPE_VIDEO_CAPTURE); |
| } |
| } |
| |
| base::flat_set<int32_t> GetAvailableFramerates( |
| const cros::mojom::CameraInfoPtr& camera_info) { |
| base::flat_set<int32_t> candidates; |
| auto available_fps_ranges = GetMetadataEntryAsSpan<int32_t>( |
| camera_info->static_camera_characteristics, |
| cros::mojom::CameraMetadataTag:: |
| ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES); |
| if (available_fps_ranges.empty()) { |
| // If there is no available target fps ranges listed in metadata, we set a |
| // default fps as candidate. |
| LOG(WARNING) << "No available fps ranges in metadata. Set default fps as " |
| "candidate."; |
| candidates.insert(kDefaultFps); |
| return candidates; |
| } |
| |
| // The available target fps ranges are stored as pairs int32s: (min, max) x n. |
| const size_t kRangeMaxOffset = 1; |
| const size_t kRangeSize = 2; |
| |
| for (size_t i = 0; i < available_fps_ranges.size(); i += kRangeSize) { |
| candidates.insert(available_fps_ranges[i + kRangeMaxOffset]); |
| } |
| return candidates; |
| } |
| |
| } // namespace |
| |
| CameraHalDelegate::CameraHalDelegate( |
| scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner) |
| : authenticated_(false), |
| camera_module_has_been_set_( |
| base::WaitableEvent::ResetPolicy::MANUAL, |
| base::WaitableEvent::InitialState::NOT_SIGNALED), |
| builtin_camera_info_updated_( |
| base::WaitableEvent::ResetPolicy::MANUAL, |
| base::WaitableEvent::InitialState::NOT_SIGNALED), |
| external_camera_info_updated_( |
| base::WaitableEvent::ResetPolicy::MANUAL, |
| base::WaitableEvent::InitialState::SIGNALED), |
| has_camera_connected_(base::WaitableEvent::ResetPolicy::MANUAL, |
| base::WaitableEvent::InitialState::NOT_SIGNALED), |
| num_builtin_cameras_(0), |
| camera_buffer_factory_(new CameraBufferFactory()), |
| ipc_task_runner_(std::move(ipc_task_runner)), |
| camera_module_callbacks_(this), |
| vendor_tag_ops_delegate_(ipc_task_runner_) { |
| DETACH_FROM_SEQUENCE(sequence_checker_); |
| } |
| |
| CameraHalDelegate::~CameraHalDelegate() = default; |
| |
| bool CameraHalDelegate::RegisterCameraClient() { |
| auto* dispatcher = CameraHalDispatcherImpl::GetInstance(); |
| auto type = cros::mojom::CameraClientType::CHROME; |
| dispatcher->AddClientObserver( |
| std::make_unique<LocalCameraClientObserver>( |
| this, type, dispatcher->GetTokenForTrustedClient(type)), |
| base::BindOnce(&CameraHalDelegate::OnRegisteredCameraHalClient, |
| base::Unretained(this))); |
| camera_hal_client_registered_.Wait(); |
| return authenticated_; |
| } |
| |
| void CameraHalDelegate::OnRegisteredCameraHalClient(int32_t result) { |
| if (result != 0) { |
| LOG(ERROR) << "Failed to register camera HAL client"; |
| camera_hal_client_registered_.Signal(); |
| return; |
| } |
| CAMERA_LOG(EVENT) << "Registered camera HAL client"; |
| authenticated_ = true; |
| camera_hal_client_registered_.Signal(); |
| } |
| |
| void CameraHalDelegate::SetCameraModule( |
| mojo::PendingRemote<cros::mojom::CameraModule> camera_module) { |
| ipc_task_runner_->PostTask( |
| FROM_HERE, base::BindOnce(&CameraHalDelegate::SetCameraModuleOnIpcThread, |
| this, std::move(camera_module))); |
| } |
| |
| void CameraHalDelegate::Reset() { |
| ipc_task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&CameraHalDelegate::ResetMojoInterfaceOnIpcThread, this)); |
| } |
| |
| std::unique_ptr<VideoCaptureDevice> CameraHalDelegate::CreateDevice( |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner_for_screen_observer, |
| const VideoCaptureDeviceDescriptor& device_descriptor) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (!UpdateBuiltInCameraInfo()) { |
| return nullptr; |
| } |
| int camera_id = GetCameraIdFromDeviceId(device_descriptor.device_id); |
| if (camera_id == -1) { |
| LOG(ERROR) << "Invalid camera device: " << device_descriptor.device_id; |
| return nullptr; |
| } |
| |
| auto* delegate = |
| GetVCDDelegate(task_runner_for_screen_observer, device_descriptor); |
| return std::make_unique<VideoCaptureDeviceChromeOSHalv3>(delegate, |
| device_descriptor); |
| } |
| |
| void CameraHalDelegate::GetSupportedFormats( |
| const cros::mojom::CameraInfoPtr& camera_info, |
| VideoCaptureFormats* supported_formats) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| base::flat_set<int32_t> candidate_fps_set = |
| GetAvailableFramerates(camera_info); |
| |
| const cros::mojom::CameraMetadataEntryPtr* min_frame_durations = |
| GetMetadataEntry(camera_info->static_camera_characteristics, |
| cros::mojom::CameraMetadataTag:: |
| ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS); |
| if (!min_frame_durations) { |
| LOG(ERROR) |
| << "Failed to get available min frame durations from camera info"; |
| return; |
| } |
| // The min frame durations are stored as tuples of four int64s: |
| // (hal_pixel_format, width, height, ns) x n |
| const size_t kStreamFormatOffset = 0; |
| const size_t kStreamWidthOffset = 1; |
| const size_t kStreamHeightOffset = 2; |
| const size_t kStreamDurationOffset = 3; |
| const size_t kStreamDurationSize = 4; |
| int64_t* iter = |
| reinterpret_cast<int64_t*>((*min_frame_durations)->data.data()); |
| for (size_t i = 0; i < (*min_frame_durations)->count; |
| i += kStreamDurationSize) { |
| auto hal_format = |
| static_cast<cros::mojom::HalPixelFormat>(iter[kStreamFormatOffset]); |
| int32_t width = base::checked_cast<int32_t>(iter[kStreamWidthOffset]); |
| int32_t height = base::checked_cast<int32_t>(iter[kStreamHeightOffset]); |
| int64_t duration = iter[kStreamDurationOffset]; |
| iter += kStreamDurationSize; |
| |
| if (hal_format == cros::mojom::HalPixelFormat::HAL_PIXEL_FORMAT_BLOB) { |
| // Skip BLOB formats and use it only for TakePicture() since it's |
| // inefficient to stream JPEG frames for CrOS camera HAL. |
| continue; |
| } |
| |
| if (duration <= 0) { |
| LOG(ERROR) << "Ignoring invalid frame duration: " << duration; |
| continue; |
| } |
| float max_fps = 1.0 * 1000000000LL / duration; |
| |
| // There's no consumer information here to determine the buffer usage, so |
| // hard-code the usage that all the clients should be using. |
| constexpr gfx::BufferUsage kClientBufferUsage = |
| gfx::BufferUsage::VEA_READ_CAMERA_AND_CPU_READ_WRITE; |
| const ChromiumPixelFormat cr_format = |
| camera_buffer_factory_->ResolveStreamBufferFormat(hal_format, |
| kClientBufferUsage); |
| if (cr_format.video_format == PIXEL_FORMAT_UNKNOWN) { |
| continue; |
| } |
| |
| for (auto fps : candidate_fps_set) { |
| if (fps > max_fps) { |
| continue; |
| } |
| |
| CAMERA_LOG(DEBUG) << "Supported format: " << width << "x" << height |
| << " fps=" << fps |
| << " format=" << cr_format.video_format; |
| supported_formats->emplace_back(gfx::Size(width, height), fps, |
| cr_format.video_format); |
| } |
| } |
| } |
| |
| void CameraHalDelegate::GetDevicesInfo( |
| VideoCaptureDeviceFactory::GetDevicesInfoCallback callback) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| if (!UpdateBuiltInCameraInfo()) { |
| std::move(callback).Run({}); |
| return; |
| } |
| |
| if (!external_camera_info_updated_.TimedWait(kEventWaitTimeoutSecs)) { |
| LOG(ERROR) << "Failed to get camera info from all external cameras"; |
| } |
| |
| if (IsRunningOnVM() && IsVividLoaded()) { |
| has_camera_connected_.TimedWait(kEventWaitTimeoutSecs); |
| } |
| |
| std::vector<VideoCaptureDeviceInfo> devices_info; |
| |
| { |
| base::AutoLock info_lock(camera_info_lock_); |
| base::AutoLock id_map_lock(device_id_to_camera_id_lock_); |
| base::AutoLock virtual_lock(enable_virtual_device_lock_); |
| for (const auto& it : camera_info_) { |
| int camera_id = it.first; |
| const cros::mojom::CameraInfoPtr& camera_info = it.second; |
| if (!camera_info) { |
| continue; |
| } |
| |
| auto get_vendor_string = [&](const std::string& key) -> const char* { |
| const VendorTagInfo* info = vendor_tag_ops_delegate_.GetInfoByName(key); |
| if (info == nullptr) { |
| return nullptr; |
| } |
| auto val = GetMetadataEntryAsSpan<char>( |
| camera_info->static_camera_characteristics, info->tag); |
| return val.empty() ? nullptr : val.data(); |
| }; |
| |
| VideoCaptureDeviceDescriptor desc; |
| desc.capture_api = VideoCaptureApi::ANDROID_API2_LIMITED; |
| desc.transport_type = VideoCaptureTransportType::OTHER_TRANSPORT; |
| switch (camera_info->facing) { |
| case cros::mojom::CameraFacing::CAMERA_FACING_BACK: |
| desc.facing = VideoFacingMode::MEDIA_VIDEO_FACING_ENVIRONMENT; |
| desc.device_id = base::NumberToString(camera_id); |
| desc.set_display_name("Back Camera"); |
| break; |
| case cros::mojom::CameraFacing::CAMERA_FACING_FRONT: |
| desc.facing = VideoFacingMode::MEDIA_VIDEO_FACING_USER; |
| desc.device_id = base::NumberToString(camera_id); |
| desc.set_display_name("Front Camera"); |
| break; |
| case cros::mojom::CameraFacing::CAMERA_FACING_EXTERNAL: { |
| desc.facing = VideoFacingMode::MEDIA_VIDEO_FACING_NONE; |
| |
| // The webcam_private api expects that |device_id| to be set as the |
| // corresponding device path for external cameras used in GVC system. |
| auto* path = get_vendor_string("com.google.usb.devicePath"); |
| desc.device_id = |
| path != nullptr ? path : base::NumberToString(camera_id); |
| |
| auto* name = get_vendor_string("com.google.usb.modelName"); |
| desc.set_display_name(name != nullptr ? name : "External Camera"); |
| |
| break; |
| // Mojo validates the input parameters for us so we don't need to |
| // worry about malformed values. |
| } |
| case cros::mojom::CameraFacing::CAMERA_FACING_VIRTUAL_BACK: |
| case cros::mojom::CameraFacing::CAMERA_FACING_VIRTUAL_FRONT: |
| case cros::mojom::CameraFacing::CAMERA_FACING_VIRTUAL_EXTERNAL: |
| // |camera_info_| should not have these facing types. |
| LOG(ERROR) << "Invalid facing type: " << camera_info->facing; |
| break; |
| } |
| auto* vid = get_vendor_string("com.google.usb.vendorId"); |
| auto* pid = get_vendor_string("com.google.usb.productId"); |
| if (vid != nullptr && pid != nullptr) { |
| desc.model_id = base::StrCat({vid, ":", pid}); |
| } |
| desc.set_control_support(GetControlSupport(camera_info)); |
| device_id_to_camera_id_[desc.device_id] = camera_id; |
| devices_info.emplace_back(desc); |
| GetSupportedFormats(camera_info_[camera_id], |
| &devices_info.back().supported_formats); |
| |
| // Create a virtual device when multiple streams are enabled. |
| if (enable_virtual_device_[camera_id]) { |
| desc.facing = VideoFacingMode::MEDIA_VIDEO_FACING_NONE; |
| desc.device_id = |
| std::string(kVirtualPrefix) + base::NumberToString(camera_id); |
| desc.set_display_name("Virtual Camera"); |
| device_id_to_camera_id_[desc.device_id] = camera_id; |
| devices_info.emplace_back(desc); |
| GetSupportedFormats(camera_info_[camera_id], |
| &devices_info.back().supported_formats); |
| } |
| } |
| } |
| // TODO(shik): Report external camera first when lid is closed. |
| // TODO(jcliang): Remove this after JS API supports query camera facing |
| // (http://crbug.com/543997). |
| std::sort( |
| devices_info.begin(), devices_info.end(), |
| [](const VideoCaptureDeviceInfo& a, const VideoCaptureDeviceInfo& b) { |
| return a.descriptor < b.descriptor; |
| }); |
| DVLOG(1) << "Number of devices: " << devices_info.size(); |
| |
| std::move(callback).Run(std::move(devices_info)); |
| } |
| |
| VideoCaptureControlSupport CameraHalDelegate::GetControlSupport( |
| const cros::mojom::CameraInfoPtr& camera_info) { |
| VideoCaptureControlSupport control_support; |
| |
| auto is_vendor_range_valid = [&](const std::string& key) -> bool { |
| const VendorTagInfo* info = vendor_tag_ops_delegate_.GetInfoByName(key); |
| if (info == nullptr) |
| return false; |
| auto range = GetMetadataEntryAsSpan<int32_t>( |
| camera_info->static_camera_characteristics, info->tag); |
| return range.size() == 3 && range[0] < range[1]; |
| }; |
| |
| if (is_vendor_range_valid("com.google.control.panRange")) |
| control_support.pan = true; |
| |
| if (is_vendor_range_valid("com.google.control.tiltRange")) |
| control_support.tilt = true; |
| |
| if (is_vendor_range_valid("com.google.control.zoomRange")) |
| control_support.zoom = true; |
| |
| auto max_digital_zoom = GetMetadataEntryAsSpan<float>( |
| camera_info->static_camera_characteristics, |
| cros::mojom::CameraMetadataTag:: |
| ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM); |
| if (max_digital_zoom.size() == 1 && max_digital_zoom[0] > 1) { |
| control_support.zoom = true; |
| } |
| |
| return control_support; |
| } |
| |
| cros::mojom::CameraInfoPtr CameraHalDelegate::GetCameraInfoFromDeviceId( |
| const std::string& device_id) { |
| base::AutoLock lock(camera_info_lock_); |
| int camera_id = GetCameraIdFromDeviceId(device_id); |
| if (camera_id == -1) { |
| return {}; |
| } |
| auto it = camera_info_.find(camera_id); |
| if (it == camera_info_.end()) { |
| return {}; |
| } |
| auto info = it->second.Clone(); |
| if (base::StartsWith(device_id, std::string(kVirtualPrefix))) { |
| switch (it->second->facing) { |
| case cros::mojom::CameraFacing::CAMERA_FACING_BACK: |
| info->facing = cros::mojom::CameraFacing::CAMERA_FACING_VIRTUAL_BACK; |
| break; |
| case cros::mojom::CameraFacing::CAMERA_FACING_FRONT: |
| info->facing = cros::mojom::CameraFacing::CAMERA_FACING_VIRTUAL_FRONT; |
| break; |
| case cros::mojom::CameraFacing::CAMERA_FACING_EXTERNAL: |
| info->facing = |
| cros::mojom::CameraFacing::CAMERA_FACING_VIRTUAL_EXTERNAL; |
| break; |
| default: |
| break; |
| } |
| } |
| return info; |
| } |
| |
| void CameraHalDelegate::EnableVirtualDevice(const std::string& device_id, |
| bool enable) { |
| if (base::StartsWith(device_id, std::string(kVirtualPrefix))) { |
| return; |
| } |
| auto camera_id = GetCameraIdFromDeviceId(device_id); |
| if (camera_id != -1) { |
| base::AutoLock lock(enable_virtual_device_lock_); |
| enable_virtual_device_[camera_id] = enable; |
| } |
| } |
| |
| void CameraHalDelegate::DisableAllVirtualDevices() { |
| base::AutoLock lock(enable_virtual_device_lock_); |
| for (auto& it : enable_virtual_device_) { |
| it.second = false; |
| } |
| } |
| |
| const VendorTagInfo* CameraHalDelegate::GetVendorTagInfoByName( |
| const std::string& full_name) { |
| return vendor_tag_ops_delegate_.GetInfoByName(full_name); |
| } |
| |
| void CameraHalDelegate::OpenDevice( |
| int32_t camera_id, |
| mojo::PendingReceiver<cros::mojom::Camera3DeviceOps> device_ops_receiver, |
| OpenDeviceCallback callback) { |
| DCHECK(!ipc_task_runner_->BelongsToCurrentThread()); |
| // This method may be called on any thread except |ipc_task_runner_|. |
| // Currently this method is used by CameraDeviceDelegate to open a camera |
| // device. |
| camera_module_has_been_set_.Wait(); |
| ipc_task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&CameraHalDelegate::OpenDeviceOnIpcThread, this, camera_id, |
| std::move(device_ops_receiver), std::move(callback))); |
| } |
| |
| int CameraHalDelegate::GetCameraIdFromDeviceId(const std::string& device_id) { |
| base::AutoLock lock(device_id_to_camera_id_lock_); |
| auto it = device_id_to_camera_id_.find(device_id); |
| if (it == device_id_to_camera_id_.end()) { |
| return -1; |
| } |
| return it->second; |
| } |
| |
| VideoCaptureDeviceChromeOSDelegate* CameraHalDelegate::GetVCDDelegate( |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner_for_screen_observer, |
| const VideoCaptureDeviceDescriptor& device_descriptor) { |
| auto camera_id = GetCameraIdFromDeviceId(device_descriptor.device_id); |
| auto it = vcd_delegate_map_.find(camera_id); |
| if (it == vcd_delegate_map_.end() || it->second->HasDeviceClient() == 0) { |
| auto cleanup_callback = base::BindOnce( |
| [](int camera_id, |
| base::flat_map<int, |
| std::unique_ptr<VideoCaptureDeviceChromeOSDelegate>>* |
| vcd_delegate_map) { vcd_delegate_map->erase(camera_id); }, |
| camera_id, &vcd_delegate_map_); |
| auto delegate = std::make_unique<VideoCaptureDeviceChromeOSDelegate>( |
| std::move(task_runner_for_screen_observer), device_descriptor, this, |
| std::move(cleanup_callback)); |
| vcd_delegate_map_[camera_id] = std::move(delegate); |
| return vcd_delegate_map_[camera_id].get(); |
| } |
| return it->second.get(); |
| } |
| |
| void CameraHalDelegate::SetCameraModuleOnIpcThread( |
| mojo::PendingRemote<cros::mojom::CameraModule> camera_module) { |
| DCHECK(ipc_task_runner_->BelongsToCurrentThread()); |
| if (camera_module_.is_bound()) { |
| LOG(ERROR) << "CameraModule is already bound"; |
| return; |
| } |
| if (camera_module.is_valid()) { |
| camera_module_.Bind(std::move(camera_module)); |
| camera_module_.set_disconnect_handler(base::BindOnce( |
| &CameraHalDelegate::ResetMojoInterfaceOnIpcThread, this)); |
| } |
| camera_module_has_been_set_.Signal(); |
| } |
| |
| void CameraHalDelegate::ResetMojoInterfaceOnIpcThread() { |
| DCHECK(ipc_task_runner_->BelongsToCurrentThread()); |
| camera_module_.reset(); |
| camera_module_callbacks_.reset(); |
| vendor_tag_ops_delegate_.Reset(); |
| builtin_camera_info_updated_.Reset(); |
| camera_module_has_been_set_.Reset(); |
| has_camera_connected_.Reset(); |
| external_camera_info_updated_.Signal(); |
| |
| // Clear all cached camera info, especially external cameras. |
| base::AutoLock lock(camera_info_lock_); |
| camera_info_.clear(); |
| pending_external_camera_info_.clear(); |
| } |
| |
| bool CameraHalDelegate::UpdateBuiltInCameraInfo() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK(!ipc_task_runner_->BelongsToCurrentThread()); |
| |
| camera_module_has_been_set_.Wait(); |
| if (builtin_camera_info_updated_.IsSignaled()) { |
| return true; |
| } |
| // The built-in camera are static per specification of the Android camera HAL |
| // v3 specification. We only update the built-in camera info once. |
| ipc_task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&CameraHalDelegate::UpdateBuiltInCameraInfoOnIpcThread, |
| this)); |
| if (!builtin_camera_info_updated_.TimedWait(kEventWaitTimeoutSecs)) { |
| LOG(ERROR) << "Timed out getting camera info"; |
| return false; |
| } |
| return true; |
| } |
| |
| void CameraHalDelegate::UpdateBuiltInCameraInfoOnIpcThread() { |
| DCHECK(ipc_task_runner_->BelongsToCurrentThread()); |
| camera_module_->GetNumberOfCameras(base::BindOnce( |
| &CameraHalDelegate::OnGotNumberOfCamerasOnIpcThread, this)); |
| } |
| |
| void CameraHalDelegate::OnGotNumberOfCamerasOnIpcThread(int32_t num_cameras) { |
| DCHECK(ipc_task_runner_->BelongsToCurrentThread()); |
| |
| base::AutoLock lock(camera_info_lock_); |
| if (num_cameras < 0) { |
| builtin_camera_info_updated_.Signal(); |
| LOG(ERROR) << "Failed to get number of cameras: " << num_cameras; |
| return; |
| } |
| CAMERA_LOG(EVENT) << "Number of built-in cameras: " << num_cameras; |
| num_builtin_cameras_ = num_cameras; |
| // Per camera HAL v3 specification SetCallbacks() should be called after the |
| // first time GetNumberOfCameras() is called, and before other CameraModule |
| // functions are called. |
| camera_module_->SetCallbacksAssociated( |
| camera_module_callbacks_.BindNewEndpointAndPassRemote(), |
| base::BindOnce(&CameraHalDelegate::OnSetCallbacksOnIpcThread, this)); |
| |
| camera_module_->GetVendorTagOps( |
| vendor_tag_ops_delegate_.MakeReceiver(), |
| base::BindOnce(&CameraHalDelegate::OnGotVendorTagOpsOnIpcThread, this)); |
| } |
| |
| void CameraHalDelegate::OnSetCallbacksOnIpcThread(int32_t result) { |
| DCHECK(ipc_task_runner_->BelongsToCurrentThread()); |
| |
| base::AutoLock lock(camera_info_lock_); |
| if (result) { |
| num_builtin_cameras_ = 0; |
| builtin_camera_info_updated_.Signal(); |
| LOG(ERROR) << "Failed to set camera module callbacks: " |
| << base::safe_strerror(-result); |
| return; |
| } |
| |
| if (num_builtin_cameras_ == 0) { |
| builtin_camera_info_updated_.Signal(); |
| return; |
| } |
| |
| for (size_t camera_id = 0; camera_id < num_builtin_cameras_; ++camera_id) { |
| GetCameraInfoOnIpcThread( |
| camera_id, |
| base::BindOnce(&CameraHalDelegate::OnGotCameraInfoOnIpcThread, this, |
| camera_id)); |
| } |
| } |
| |
| void CameraHalDelegate::OnGotVendorTagOpsOnIpcThread() { |
| DCHECK(ipc_task_runner_->BelongsToCurrentThread()); |
| vendor_tag_ops_delegate_.Initialize(); |
| } |
| |
| void CameraHalDelegate::GetCameraInfoOnIpcThread( |
| int32_t camera_id, |
| GetCameraInfoCallback callback) { |
| DCHECK(ipc_task_runner_->BelongsToCurrentThread()); |
| camera_module_->GetCameraInfo(camera_id, std::move(callback)); |
| } |
| |
| void CameraHalDelegate::OnGotCameraInfoOnIpcThread( |
| int32_t camera_id, |
| int32_t result, |
| cros::mojom::CameraInfoPtr camera_info) { |
| DCHECK(ipc_task_runner_->BelongsToCurrentThread()); |
| DVLOG(1) << "Got camera info of camera " << camera_id; |
| if (result) { |
| LOG(ERROR) << "Failed to get camera info. Camera id: " << camera_id; |
| return; |
| } |
| SortCameraMetadata(&camera_info->static_camera_characteristics); |
| |
| base::AutoLock lock(camera_info_lock_); |
| camera_info_[camera_id] = std::move(camera_info); |
| |
| if (camera_id < base::checked_cast<int32_t>(num_builtin_cameras_)) { |
| // |camera_info_| might contain some entries for external cameras as well, |
| // we should check all built-in cameras explicitly. |
| bool all_updated = [&]() { |
| camera_info_lock_.AssertAcquired(); |
| for (size_t i = 0; i < num_builtin_cameras_; i++) { |
| if (camera_info_.find(i) == camera_info_.end()) { |
| return false; |
| } |
| } |
| return true; |
| }(); |
| |
| if (all_updated) { |
| builtin_camera_info_updated_.Signal(); |
| } |
| } else { |
| // It's an external camera. |
| pending_external_camera_info_.erase(camera_id); |
| if (pending_external_camera_info_.empty()) { |
| external_camera_info_updated_.Signal(); |
| } |
| NotifyVideoCaptureDevicesChanged(); |
| } |
| |
| if (camera_info_.size() == 1) { |
| has_camera_connected_.Signal(); |
| } |
| } |
| |
| void CameraHalDelegate::OpenDeviceOnIpcThread( |
| int32_t camera_id, |
| mojo::PendingReceiver<cros::mojom::Camera3DeviceOps> device_ops_receiver, |
| OpenDeviceCallback callback) { |
| DCHECK(ipc_task_runner_->BelongsToCurrentThread()); |
| camera_module_->OpenDevice(camera_id, std::move(device_ops_receiver), |
| std::move(callback)); |
| } |
| |
| // CameraModuleCallbacks implementations. |
| void CameraHalDelegate::CameraDeviceStatusChange( |
| int32_t camera_id, |
| cros::mojom::CameraDeviceStatus new_status) { |
| DCHECK(ipc_task_runner_->BelongsToCurrentThread()); |
| CAMERA_LOG(EVENT) << "camera_id = " << camera_id |
| << ", new_status = " << new_status; |
| base::AutoLock lock(camera_info_lock_); |
| auto it = camera_info_.find(camera_id); |
| switch (new_status) { |
| case cros::mojom::CameraDeviceStatus::CAMERA_DEVICE_STATUS_PRESENT: |
| if (it == camera_info_.end()) { |
| // Get info for the newly connected external camera. |
| // |has_camera_connected_| might be signaled in |
| // OnGotCameraInfoOnIpcThread(). |
| pending_external_camera_info_.insert(camera_id); |
| if (pending_external_camera_info_.size() == 1) { |
| external_camera_info_updated_.Reset(); |
| } |
| GetCameraInfoOnIpcThread( |
| camera_id, |
| base::BindOnce(&CameraHalDelegate::OnGotCameraInfoOnIpcThread, this, |
| camera_id)); |
| } else { |
| LOG(WARNING) << "Ignore duplicated camera_id = " << camera_id; |
| } |
| break; |
| case cros::mojom::CameraDeviceStatus::CAMERA_DEVICE_STATUS_NOT_PRESENT: |
| if (it != camera_info_.end()) { |
| camera_info_.erase(it); |
| if (camera_info_.empty()) { |
| has_camera_connected_.Reset(); |
| } |
| NotifyVideoCaptureDevicesChanged(); |
| } else { |
| LOG(WARNING) << "Ignore nonexistent camera_id = " << camera_id; |
| } |
| break; |
| default: |
| NOTREACHED() << "Unexpected new status " << new_status; |
| } |
| } |
| |
| void CameraHalDelegate::TorchModeStatusChange( |
| int32_t camera_id, |
| cros::mojom::TorchModeStatus new_status) { |
| DCHECK(ipc_task_runner_->BelongsToCurrentThread()); |
| // Do nothing here as we don't care about torch mode status. |
| } |
| |
| } // namespace media |