Loading services/camera/virtualcamera/Android.bp 0 → 100644 +99 −0 Original line number Diff line number Diff line package { // See: http://go/android-license-faq default_applicable_licenses: ["Android-Apache-2.0"], } cc_defaults { name: "libvirtualcamera_defaults", shared_libs: [ "android.hardware.common-V2-ndk", "android.hardware.common.fmq-V1-ndk", "libbinder", "libbinder_ndk", "libcamera_metadata", "liblog", "libfmq", "libgui", "libjpeg", "libnativewindow", "libbase", "libcutils", "libui", "libutils", "libEGL", "libGLESv2", "libGLESv3", ], static_libs: [ "android.hardware.camera.common@1.0-helper", "android.hardware.camera.common-V1-ndk", "android.hardware.camera.device-V2-ndk", "android.hardware.camera.metadata-V2-ndk", "android.hardware.camera.provider-V2-ndk", "libaidlcommonsupport", "virtual_camera_service_aidl-ndk", ], cflags: [ "-Wall", "-Werror", "-Wformat", "-Wthread-safety", "-DLOG_NDEBUG=0" ], } cc_library_static { name: "libvirtualcamera_utils", srcs: [ "util/JpegUtil.cc", "util/MetadataBuilder.cc", "util/Util.cc", "util/TestPatternHelper.cc", "util/EglDisplayContext.cc", "util/EglFramebuffer.cc", "util/EglProgram.cc", "util/EglSurfaceTexture.cc", "util/EglUtil.cc", ], defaults: [ "libvirtualcamera_defaults", ], } cc_library_static { name: "libvirtualcamera", srcs: [ "VirtualCameraProvider.cc", "VirtualCameraDevice.cc", "VirtualCameraSession.cc", "VirtualCameraStream.cc", "VirtualCameraService.cc", "VirtualCameraSessionContext.cc", "VirtualCameraRenderThread.cc", ], defaults: [ "libvirtualcamera_defaults", ], static_libs: [ "libvirtualcamera_utils", ], export_include_dirs: ["."], min_sdk_version: "current", } cc_binary { name: "virtual_camera", srcs: ["main.cc"], defaults: [ "libvirtualcamera_defaults", ], static_libs: [ "libvirtualcamera", "libvirtualcamera_utils", ], // Remove before flight. // We don't want the service to be started and discovered // yet - remove comments on the lines below in order to // test locally. // init_rc: ["virtual_camera.hal.rc"], } services/camera/virtualcamera/TEST_MAPPING 0 → 100644 +7 −0 Original line number Diff line number Diff line { "postsubmit": [ { "name": "virtual_camera_tests" } ] } services/camera/virtualcamera/VirtualCameraDevice.cc 0 → 100644 +248 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // #define LOG_NDEBUG 0 #define LOG_TAG "VirtualCameraDevice" #include "VirtualCameraDevice.h" #include <chrono> #include <cstdint> #include <string> #include "VirtualCameraSession.h" #include "aidl/android/hardware/camera/common/Status.h" #include "aidl/android/hardware/camera/device/CameraMetadata.h" #include "android/binder_auto_utils.h" #include "android/binder_status.h" #include "log/log.h" #include "system/camera_metadata.h" #include "util/MetadataBuilder.h" #include "util/Util.h" namespace android { namespace companion { namespace virtualcamera { using ::aidl::android::companion::virtualcamera::IVirtualCameraCallback; using ::aidl::android::hardware::camera::common::CameraResourceCost; using ::aidl::android::hardware::camera::common::Status; using ::aidl::android::hardware::camera::device::CameraMetadata; using ::aidl::android::hardware::camera::device::ICameraDeviceCallback; using ::aidl::android::hardware::camera::device::ICameraDeviceSession; using ::aidl::android::hardware::camera::device::ICameraInjectionSession; using ::aidl::android::hardware::camera::device::StreamConfiguration; using ::aidl::android::hardware::camera::device::StreamRotation; using ::aidl::android::hardware::camera::device::StreamType; using ::aidl::android::hardware::graphics::common::PixelFormat; namespace { using namespace std::chrono_literals; // Prefix of camera name - "device@1.1/virtual/{numerical_id}" const char* kDevicePathPrefix = "device@1.1/virtual/"; constexpr int32_t kVgaWidth = 640; constexpr int32_t kVgaHeight = 480; constexpr std::chrono::nanoseconds kMinFrameDuration30Fps = 1s / 30; constexpr int32_t kMaxJpegSize = 3 * 1024 * 1024 /*3MiB*/; constexpr MetadataBuilder::ControlRegion kDefaultEmptyControlRegion{}; // TODO(b/301023410) - Populate camera characteristics according to camera configuration. CameraMetadata initCameraCharacteristics() { auto metadata = MetadataBuilder() .setSupportedHardwareLevel( ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL) .setFlashAvailable(false) .setLensFacing(ANDROID_LENS_FACING_EXTERNAL) .setSensorOrientation(0) .setAvailableFaceDetectModes({ANDROID_STATISTICS_FACE_DETECT_MODE_OFF}) .setControlAfAvailableModes({ANDROID_CONTROL_AF_MODE_OFF}) .setAvailableOutputStreamConfigurations( {MetadataBuilder::StreamConfiguration{ .width = kVgaWidth, .height = kVgaHeight, .format = ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED, .minFrameDuration = kMinFrameDuration30Fps, .minStallDuration = 0s}, MetadataBuilder::StreamConfiguration{ .width = kVgaWidth, .height = kVgaHeight, .format = ANDROID_SCALER_AVAILABLE_FORMATS_YCbCr_420_888, .minFrameDuration = kMinFrameDuration30Fps, .minStallDuration = 0s}, {MetadataBuilder::StreamConfiguration{ .width = kVgaWidth, .height = kVgaHeight, .format = ANDROID_SCALER_AVAILABLE_FORMATS_BLOB, .minFrameDuration = kMinFrameDuration30Fps, .minStallDuration = 0s}}}) .setControlAeAvailableFpsRange(10, 30) .setControlMaxRegions(0, 0, 0) .setSensorActiveArraySize(0, 0, kVgaWidth, kVgaHeight) .setControlAfRegions({kDefaultEmptyControlRegion}) .setControlAeRegions({kDefaultEmptyControlRegion}) .setControlAwbRegions({kDefaultEmptyControlRegion}) .setControlAeCompensationRange(0, 1) .setControlAeCompensationStep(camera_metadata_rational_t{0, 1}) .setMaxJpegSize(kMaxJpegSize) .setAvailableRequestKeys({ANDROID_CONTROL_AF_MODE}) .setAvailableResultKeys({ANDROID_CONTROL_AF_MODE}) .setAvailableCapabilities( {ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE}) .setAvailableCharacteristicKeys() .build(); if (metadata == nullptr) { ALOGE("Failed to build metadata!"); return CameraMetadata(); } return std::move(*metadata); } } // namespace VirtualCameraDevice::VirtualCameraDevice( const uint32_t cameraId, std::shared_ptr<IVirtualCameraCallback> virtualCameraClientCallback) : mCameraId(cameraId), mVirtualCameraClientCallback(virtualCameraClientCallback) { mCameraCharacteristics = initCameraCharacteristics(); } ndk::ScopedAStatus VirtualCameraDevice::getCameraCharacteristics( CameraMetadata* _aidl_return) { ALOGV("%s", __func__); if (_aidl_return == nullptr) { return cameraStatus(Status::ILLEGAL_ARGUMENT); } *_aidl_return = mCameraCharacteristics; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus VirtualCameraDevice::getPhysicalCameraCharacteristics( const std::string& in_physicalCameraId, CameraMetadata* _aidl_return) { ALOGV("%s: physicalCameraId %s", __func__, in_physicalCameraId.c_str()); (void)_aidl_return; // VTS tests expect this call to fail with illegal argument status for // all publicly advertised camera ids. // Because we don't support physical camera ids, we just always // fail with illegal argument (there's no valid argument to provide). return cameraStatus(Status::ILLEGAL_ARGUMENT); } ndk::ScopedAStatus VirtualCameraDevice::getResourceCost( CameraResourceCost* _aidl_return) { ALOGV("%s", __func__); if (_aidl_return == nullptr) { return cameraStatus(Status::ILLEGAL_ARGUMENT); } _aidl_return->resourceCost = 100; // ¯\_(ツ)_/¯ return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus VirtualCameraDevice::isStreamCombinationSupported( const StreamConfiguration& in_streams, bool* _aidl_return) { ALOGV("%s", __func__); if (_aidl_return == nullptr) { return cameraStatus(Status::ILLEGAL_ARGUMENT); } for (const auto& stream : in_streams.streams) { ALOGV("%s: Configuration queried: %s", __func__, stream.toString().c_str()); if (stream.streamType == StreamType::INPUT) { ALOGW("%s: Input stream type is not supported", __func__); *_aidl_return = false; return ndk::ScopedAStatus::ok(); } // TODO(b/301023410) remove hardcoded format checks, verify against configuration. if (stream.width != 640 || stream.height != 480 || stream.rotation != StreamRotation::ROTATION_0 || (stream.format != PixelFormat::IMPLEMENTATION_DEFINED && stream.format != PixelFormat::YCBCR_420_888 && stream.format != PixelFormat::BLOB)) { *_aidl_return = false; return ndk::ScopedAStatus::ok(); } } *_aidl_return = true; return ndk::ScopedAStatus::ok(); }; ndk::ScopedAStatus VirtualCameraDevice::open( const std::shared_ptr<ICameraDeviceCallback>& in_callback, std::shared_ptr<ICameraDeviceSession>* _aidl_return) { ALOGV("%s", __func__); *_aidl_return = ndk::SharedRefBase::make<VirtualCameraSession>( std::to_string(mCameraId), in_callback, mVirtualCameraClientCallback); return ndk::ScopedAStatus::ok(); }; ndk::ScopedAStatus VirtualCameraDevice::openInjectionSession( const std::shared_ptr<ICameraDeviceCallback>& in_callback, std::shared_ptr<ICameraInjectionSession>* _aidl_return) { ALOGV("%s", __func__); (void)in_callback; (void)_aidl_return; return cameraStatus(Status::OPERATION_NOT_SUPPORTED); } ndk::ScopedAStatus VirtualCameraDevice::setTorchMode(bool in_on) { ALOGV("%s: on = %s", __func__, in_on ? "on" : "off"); return cameraStatus(Status::OPERATION_NOT_SUPPORTED); } ndk::ScopedAStatus VirtualCameraDevice::turnOnTorchWithStrengthLevel( int32_t in_torchStrength) { ALOGV("%s: torchStrength = %d", __func__, in_torchStrength); return cameraStatus(Status::OPERATION_NOT_SUPPORTED); } ndk::ScopedAStatus VirtualCameraDevice::getTorchStrengthLevel( int32_t* _aidl_return) { (void)_aidl_return; return cameraStatus(Status::OPERATION_NOT_SUPPORTED); } binder_status_t VirtualCameraDevice::dump(int fd, const char** args, uint32_t numArgs) { // TODO(b/301023410) Implement. (void)fd; (void)args; (void)numArgs; return STATUS_OK; } std::string VirtualCameraDevice::getCameraName() const { return std::string(kDevicePathPrefix) + std::to_string(mCameraId); } } // namespace virtualcamera } // namespace companion } // namespace android services/camera/virtualcamera/VirtualCameraDevice.h 0 → 100644 +103 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_COMPANION_VIRTUALCAMERA_VIRTUALCAMERADEVICE_H #define ANDROID_COMPANION_VIRTUALCAMERA_VIRTUALCAMERADEVICE_H #include <cstdint> #include <memory> #include "aidl/android/companion/virtualcamera/IVirtualCameraCallback.h" #include "aidl/android/hardware/camera/device/BnCameraDevice.h" namespace android { namespace companion { namespace virtualcamera { // Representation of single virtual camera device, implements // ICameraDevice AIDL to expose camera to camera framework. class VirtualCameraDevice : public ::aidl::android::hardware::camera::device::BnCameraDevice { public: explicit VirtualCameraDevice( uint32_t cameraId, std::shared_ptr< ::aidl::android::companion::virtualcamera::IVirtualCameraCallback> virtualCameraClientCallback = nullptr); virtual ~VirtualCameraDevice() override = default; ndk::ScopedAStatus getCameraCharacteristics( ::aidl::android::hardware::camera::device::CameraMetadata* _aidl_return) override; ndk::ScopedAStatus getPhysicalCameraCharacteristics( const std::string& in_physicalCameraId, ::aidl::android::hardware::camera::device::CameraMetadata* _aidl_return) override; ndk::ScopedAStatus getResourceCost( ::aidl::android::hardware::camera::common::CameraResourceCost* _aidl_return) override; ndk::ScopedAStatus isStreamCombinationSupported( const ::aidl::android::hardware::camera::device::StreamConfiguration& in_streams, bool* _aidl_return) override; ndk::ScopedAStatus open( const std::shared_ptr< ::aidl::android::hardware::camera::device::ICameraDeviceCallback>& in_callback, std::shared_ptr< ::aidl::android::hardware::camera::device::ICameraDeviceSession>* _aidl_return) override; ndk::ScopedAStatus openInjectionSession( const std::shared_ptr< ::aidl::android::hardware::camera::device::ICameraDeviceCallback>& in_callback, std::shared_ptr< ::aidl::android::hardware::camera::device::ICameraInjectionSession>* _aidl_return) override; ndk::ScopedAStatus setTorchMode(bool in_on) override; ndk::ScopedAStatus turnOnTorchWithStrengthLevel( int32_t in_torchStrength) override; ndk::ScopedAStatus getTorchStrengthLevel(int32_t* _aidl_return) override; binder_status_t dump(int fd, const char** args, uint32_t numArgs) override; // Returns unique virtual camera name in form // "device@{major}.{minor}/virtual/{numerical_id}" std::string getCameraName() const; private: const uint32_t mCameraId; const std::shared_ptr< ::aidl::android::companion::virtualcamera::IVirtualCameraCallback> mVirtualCameraClientCallback; ::aidl::android::hardware::camera::device::CameraMetadata mCameraCharacteristics; }; } // namespace virtualcamera } // namespace companion } // namespace android #endif // ANDROID_COMPANION_VIRTUALCAMERA_VIRTUALCAMERADEVICE_H services/camera/virtualcamera/VirtualCameraProvider.cc 0 → 100644 +219 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // #define LOG_NDEBUG 0 #define LOG_TAG "VirtualCameraProvider" #include "VirtualCameraProvider.h" #include <atomic> #include <memory> #include <mutex> #include <tuple> #include <utility> #include "VirtualCameraDevice.h" #include "aidl/android/hardware/camera/common/Status.h" #include "log/log.h" #include "util/Util.h" namespace android { namespace companion { namespace virtualcamera { using ::aidl::android::companion::virtualcamera::IVirtualCameraCallback; using ::aidl::android::hardware::camera::common::CameraDeviceStatus; using ::aidl::android::hardware::camera::common::Status; using ::aidl::android::hardware::camera::common::VendorTagSection; using ::aidl::android::hardware::camera::device::ICameraDevice; using ::aidl::android::hardware::camera::provider::CameraIdAndStreamCombination; using ::aidl::android::hardware::camera::provider::ConcurrentCameraIdCombination; using ::aidl::android::hardware::camera::provider::ICameraProviderCallback; // TODO(b/301023410) Make camera id range configurable / dynamic // based on already registered devices. std::atomic_int VirtualCameraProvider::sNextId{42}; ndk::ScopedAStatus VirtualCameraProvider::setCallback( const std::shared_ptr<ICameraProviderCallback>& in_callback) { ALOGV("%s", __func__); if (in_callback == nullptr) { return cameraStatus(Status::ILLEGAL_ARGUMENT); } { const std::lock_guard<std::mutex> lock(mLock); mCameraProviderCallback = in_callback; for (const auto& [cameraName, _] : mCameras) { auto ret = mCameraProviderCallback->cameraDeviceStatusChange( cameraName, CameraDeviceStatus::PRESENT); if (!ret.isOk()) { ALOGE("Failed to announce camera status change: %s", ret.getDescription().c_str()); } } } return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus VirtualCameraProvider::getVendorTags( std::vector<VendorTagSection>* _aidl_return) { ALOGV("%s", __func__); if (_aidl_return == nullptr) { return cameraStatus(Status::ILLEGAL_ARGUMENT); } // No vendor tags supported. _aidl_return->clear(); return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus VirtualCameraProvider::getCameraIdList( std::vector<std::string>* _aidl_return) { ALOGV("%s", __func__); if (_aidl_return == nullptr) { return cameraStatus(Status::ILLEGAL_ARGUMENT); } { const std::lock_guard<std::mutex> lock(mLock); _aidl_return->clear(); _aidl_return->reserve(mCameras.size()); for (const auto& [cameraName, _] : mCameras) { _aidl_return->emplace_back(cameraName); } } return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus VirtualCameraProvider::getCameraDeviceInterface( const std::string& in_cameraDeviceName, std::shared_ptr<ICameraDevice>* _aidl_return) { ALOGV("%s cameraDeviceName %s", __func__, in_cameraDeviceName.c_str()); if (_aidl_return == nullptr) { return cameraStatus(Status::ILLEGAL_ARGUMENT); } { const std::lock_guard<std::mutex> lock(mLock); const auto it = mCameras.find(in_cameraDeviceName); *_aidl_return = (it == mCameras.end()) ? nullptr : it->second; } return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus VirtualCameraProvider::notifyDeviceStateChange( int64_t in_deviceState) { ALOGV("%s", __func__); (void)in_deviceState; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus VirtualCameraProvider::getConcurrentCameraIds( std::vector<ConcurrentCameraIdCombination>* _aidl_return) { ALOGV("%s", __func__); if (_aidl_return == nullptr) { return cameraStatus(Status::ILLEGAL_ARGUMENT); } // No support for any concurrent combination. _aidl_return->clear(); return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus VirtualCameraProvider::isConcurrentStreamCombinationSupported( const std::vector<CameraIdAndStreamCombination>& in_configs, bool* _aidl_return) { ALOGV("%s", __func__); (void)in_configs; if (_aidl_return == nullptr) { return cameraStatus(Status::ILLEGAL_ARGUMENT); } // No support for any stream combination at the moment. *_aidl_return = false; return ndk::ScopedAStatus::ok(); } std::shared_ptr<VirtualCameraDevice> VirtualCameraProvider::createCamera( std::shared_ptr<IVirtualCameraCallback> virtualCameraClientCallback) { auto camera = ndk::SharedRefBase::make<VirtualCameraDevice>( sNextId++, virtualCameraClientCallback); std::shared_ptr<ICameraProviderCallback> callback; { const std::lock_guard<std::mutex> lock(mLock); if (mCameras.find(camera->getCameraName()) != mCameras.end()) { ALOGE("Camera with identical name already exists."); return nullptr; } mCameras.emplace(std::piecewise_construct, std::forward_as_tuple(camera->getCameraName()), std::forward_as_tuple(camera)); callback = mCameraProviderCallback; } if (callback != nullptr) { auto ret = callback->cameraDeviceStatusChange(camera->getCameraName(), CameraDeviceStatus::PRESENT); if (!ret.isOk()) { ALOGE("Failed to announce camera %s status change (PRESENT): %s", camera->getCameraName().c_str(), ret.getDescription().c_str()); } } return camera; } std::shared_ptr<VirtualCameraDevice> VirtualCameraProvider::getCamera( const std::string& cameraName) { const std::lock_guard<std::mutex> lock(mLock); auto it = mCameras.find(cameraName); return it == mCameras.end() ? nullptr : it->second; } bool VirtualCameraProvider::removeCamera(const std::string& name) { std::shared_ptr<ICameraProviderCallback> callback; { const std::lock_guard<std::mutex> lock(mLock); auto it = mCameras.find(name); if (it == mCameras.end()) { ALOGE("Cannot remove camera %s: no such camera", name.c_str()); return false; } // TODO(b/301023410) Gracefully shut down camera. mCameras.erase(it); callback = mCameraProviderCallback; } if (callback != nullptr) { auto ret = callback->cameraDeviceStatusChange( name, CameraDeviceStatus::NOT_PRESENT); if (!ret.isOk()) { ALOGE("Failed to announce camera %s status change (NOT_PRESENT): %s", name.c_str(), ret.getDescription().c_str()); } } return true; } } // namespace virtualcamera } // namespace companion } // namespace android Loading
services/camera/virtualcamera/Android.bp 0 → 100644 +99 −0 Original line number Diff line number Diff line package { // See: http://go/android-license-faq default_applicable_licenses: ["Android-Apache-2.0"], } cc_defaults { name: "libvirtualcamera_defaults", shared_libs: [ "android.hardware.common-V2-ndk", "android.hardware.common.fmq-V1-ndk", "libbinder", "libbinder_ndk", "libcamera_metadata", "liblog", "libfmq", "libgui", "libjpeg", "libnativewindow", "libbase", "libcutils", "libui", "libutils", "libEGL", "libGLESv2", "libGLESv3", ], static_libs: [ "android.hardware.camera.common@1.0-helper", "android.hardware.camera.common-V1-ndk", "android.hardware.camera.device-V2-ndk", "android.hardware.camera.metadata-V2-ndk", "android.hardware.camera.provider-V2-ndk", "libaidlcommonsupport", "virtual_camera_service_aidl-ndk", ], cflags: [ "-Wall", "-Werror", "-Wformat", "-Wthread-safety", "-DLOG_NDEBUG=0" ], } cc_library_static { name: "libvirtualcamera_utils", srcs: [ "util/JpegUtil.cc", "util/MetadataBuilder.cc", "util/Util.cc", "util/TestPatternHelper.cc", "util/EglDisplayContext.cc", "util/EglFramebuffer.cc", "util/EglProgram.cc", "util/EglSurfaceTexture.cc", "util/EglUtil.cc", ], defaults: [ "libvirtualcamera_defaults", ], } cc_library_static { name: "libvirtualcamera", srcs: [ "VirtualCameraProvider.cc", "VirtualCameraDevice.cc", "VirtualCameraSession.cc", "VirtualCameraStream.cc", "VirtualCameraService.cc", "VirtualCameraSessionContext.cc", "VirtualCameraRenderThread.cc", ], defaults: [ "libvirtualcamera_defaults", ], static_libs: [ "libvirtualcamera_utils", ], export_include_dirs: ["."], min_sdk_version: "current", } cc_binary { name: "virtual_camera", srcs: ["main.cc"], defaults: [ "libvirtualcamera_defaults", ], static_libs: [ "libvirtualcamera", "libvirtualcamera_utils", ], // Remove before flight. // We don't want the service to be started and discovered // yet - remove comments on the lines below in order to // test locally. // init_rc: ["virtual_camera.hal.rc"], }
services/camera/virtualcamera/TEST_MAPPING 0 → 100644 +7 −0 Original line number Diff line number Diff line { "postsubmit": [ { "name": "virtual_camera_tests" } ] }
services/camera/virtualcamera/VirtualCameraDevice.cc 0 → 100644 +248 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // #define LOG_NDEBUG 0 #define LOG_TAG "VirtualCameraDevice" #include "VirtualCameraDevice.h" #include <chrono> #include <cstdint> #include <string> #include "VirtualCameraSession.h" #include "aidl/android/hardware/camera/common/Status.h" #include "aidl/android/hardware/camera/device/CameraMetadata.h" #include "android/binder_auto_utils.h" #include "android/binder_status.h" #include "log/log.h" #include "system/camera_metadata.h" #include "util/MetadataBuilder.h" #include "util/Util.h" namespace android { namespace companion { namespace virtualcamera { using ::aidl::android::companion::virtualcamera::IVirtualCameraCallback; using ::aidl::android::hardware::camera::common::CameraResourceCost; using ::aidl::android::hardware::camera::common::Status; using ::aidl::android::hardware::camera::device::CameraMetadata; using ::aidl::android::hardware::camera::device::ICameraDeviceCallback; using ::aidl::android::hardware::camera::device::ICameraDeviceSession; using ::aidl::android::hardware::camera::device::ICameraInjectionSession; using ::aidl::android::hardware::camera::device::StreamConfiguration; using ::aidl::android::hardware::camera::device::StreamRotation; using ::aidl::android::hardware::camera::device::StreamType; using ::aidl::android::hardware::graphics::common::PixelFormat; namespace { using namespace std::chrono_literals; // Prefix of camera name - "device@1.1/virtual/{numerical_id}" const char* kDevicePathPrefix = "device@1.1/virtual/"; constexpr int32_t kVgaWidth = 640; constexpr int32_t kVgaHeight = 480; constexpr std::chrono::nanoseconds kMinFrameDuration30Fps = 1s / 30; constexpr int32_t kMaxJpegSize = 3 * 1024 * 1024 /*3MiB*/; constexpr MetadataBuilder::ControlRegion kDefaultEmptyControlRegion{}; // TODO(b/301023410) - Populate camera characteristics according to camera configuration. CameraMetadata initCameraCharacteristics() { auto metadata = MetadataBuilder() .setSupportedHardwareLevel( ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL) .setFlashAvailable(false) .setLensFacing(ANDROID_LENS_FACING_EXTERNAL) .setSensorOrientation(0) .setAvailableFaceDetectModes({ANDROID_STATISTICS_FACE_DETECT_MODE_OFF}) .setControlAfAvailableModes({ANDROID_CONTROL_AF_MODE_OFF}) .setAvailableOutputStreamConfigurations( {MetadataBuilder::StreamConfiguration{ .width = kVgaWidth, .height = kVgaHeight, .format = ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED, .minFrameDuration = kMinFrameDuration30Fps, .minStallDuration = 0s}, MetadataBuilder::StreamConfiguration{ .width = kVgaWidth, .height = kVgaHeight, .format = ANDROID_SCALER_AVAILABLE_FORMATS_YCbCr_420_888, .minFrameDuration = kMinFrameDuration30Fps, .minStallDuration = 0s}, {MetadataBuilder::StreamConfiguration{ .width = kVgaWidth, .height = kVgaHeight, .format = ANDROID_SCALER_AVAILABLE_FORMATS_BLOB, .minFrameDuration = kMinFrameDuration30Fps, .minStallDuration = 0s}}}) .setControlAeAvailableFpsRange(10, 30) .setControlMaxRegions(0, 0, 0) .setSensorActiveArraySize(0, 0, kVgaWidth, kVgaHeight) .setControlAfRegions({kDefaultEmptyControlRegion}) .setControlAeRegions({kDefaultEmptyControlRegion}) .setControlAwbRegions({kDefaultEmptyControlRegion}) .setControlAeCompensationRange(0, 1) .setControlAeCompensationStep(camera_metadata_rational_t{0, 1}) .setMaxJpegSize(kMaxJpegSize) .setAvailableRequestKeys({ANDROID_CONTROL_AF_MODE}) .setAvailableResultKeys({ANDROID_CONTROL_AF_MODE}) .setAvailableCapabilities( {ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE}) .setAvailableCharacteristicKeys() .build(); if (metadata == nullptr) { ALOGE("Failed to build metadata!"); return CameraMetadata(); } return std::move(*metadata); } } // namespace VirtualCameraDevice::VirtualCameraDevice( const uint32_t cameraId, std::shared_ptr<IVirtualCameraCallback> virtualCameraClientCallback) : mCameraId(cameraId), mVirtualCameraClientCallback(virtualCameraClientCallback) { mCameraCharacteristics = initCameraCharacteristics(); } ndk::ScopedAStatus VirtualCameraDevice::getCameraCharacteristics( CameraMetadata* _aidl_return) { ALOGV("%s", __func__); if (_aidl_return == nullptr) { return cameraStatus(Status::ILLEGAL_ARGUMENT); } *_aidl_return = mCameraCharacteristics; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus VirtualCameraDevice::getPhysicalCameraCharacteristics( const std::string& in_physicalCameraId, CameraMetadata* _aidl_return) { ALOGV("%s: physicalCameraId %s", __func__, in_physicalCameraId.c_str()); (void)_aidl_return; // VTS tests expect this call to fail with illegal argument status for // all publicly advertised camera ids. // Because we don't support physical camera ids, we just always // fail with illegal argument (there's no valid argument to provide). return cameraStatus(Status::ILLEGAL_ARGUMENT); } ndk::ScopedAStatus VirtualCameraDevice::getResourceCost( CameraResourceCost* _aidl_return) { ALOGV("%s", __func__); if (_aidl_return == nullptr) { return cameraStatus(Status::ILLEGAL_ARGUMENT); } _aidl_return->resourceCost = 100; // ¯\_(ツ)_/¯ return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus VirtualCameraDevice::isStreamCombinationSupported( const StreamConfiguration& in_streams, bool* _aidl_return) { ALOGV("%s", __func__); if (_aidl_return == nullptr) { return cameraStatus(Status::ILLEGAL_ARGUMENT); } for (const auto& stream : in_streams.streams) { ALOGV("%s: Configuration queried: %s", __func__, stream.toString().c_str()); if (stream.streamType == StreamType::INPUT) { ALOGW("%s: Input stream type is not supported", __func__); *_aidl_return = false; return ndk::ScopedAStatus::ok(); } // TODO(b/301023410) remove hardcoded format checks, verify against configuration. if (stream.width != 640 || stream.height != 480 || stream.rotation != StreamRotation::ROTATION_0 || (stream.format != PixelFormat::IMPLEMENTATION_DEFINED && stream.format != PixelFormat::YCBCR_420_888 && stream.format != PixelFormat::BLOB)) { *_aidl_return = false; return ndk::ScopedAStatus::ok(); } } *_aidl_return = true; return ndk::ScopedAStatus::ok(); }; ndk::ScopedAStatus VirtualCameraDevice::open( const std::shared_ptr<ICameraDeviceCallback>& in_callback, std::shared_ptr<ICameraDeviceSession>* _aidl_return) { ALOGV("%s", __func__); *_aidl_return = ndk::SharedRefBase::make<VirtualCameraSession>( std::to_string(mCameraId), in_callback, mVirtualCameraClientCallback); return ndk::ScopedAStatus::ok(); }; ndk::ScopedAStatus VirtualCameraDevice::openInjectionSession( const std::shared_ptr<ICameraDeviceCallback>& in_callback, std::shared_ptr<ICameraInjectionSession>* _aidl_return) { ALOGV("%s", __func__); (void)in_callback; (void)_aidl_return; return cameraStatus(Status::OPERATION_NOT_SUPPORTED); } ndk::ScopedAStatus VirtualCameraDevice::setTorchMode(bool in_on) { ALOGV("%s: on = %s", __func__, in_on ? "on" : "off"); return cameraStatus(Status::OPERATION_NOT_SUPPORTED); } ndk::ScopedAStatus VirtualCameraDevice::turnOnTorchWithStrengthLevel( int32_t in_torchStrength) { ALOGV("%s: torchStrength = %d", __func__, in_torchStrength); return cameraStatus(Status::OPERATION_NOT_SUPPORTED); } ndk::ScopedAStatus VirtualCameraDevice::getTorchStrengthLevel( int32_t* _aidl_return) { (void)_aidl_return; return cameraStatus(Status::OPERATION_NOT_SUPPORTED); } binder_status_t VirtualCameraDevice::dump(int fd, const char** args, uint32_t numArgs) { // TODO(b/301023410) Implement. (void)fd; (void)args; (void)numArgs; return STATUS_OK; } std::string VirtualCameraDevice::getCameraName() const { return std::string(kDevicePathPrefix) + std::to_string(mCameraId); } } // namespace virtualcamera } // namespace companion } // namespace android
services/camera/virtualcamera/VirtualCameraDevice.h 0 → 100644 +103 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_COMPANION_VIRTUALCAMERA_VIRTUALCAMERADEVICE_H #define ANDROID_COMPANION_VIRTUALCAMERA_VIRTUALCAMERADEVICE_H #include <cstdint> #include <memory> #include "aidl/android/companion/virtualcamera/IVirtualCameraCallback.h" #include "aidl/android/hardware/camera/device/BnCameraDevice.h" namespace android { namespace companion { namespace virtualcamera { // Representation of single virtual camera device, implements // ICameraDevice AIDL to expose camera to camera framework. class VirtualCameraDevice : public ::aidl::android::hardware::camera::device::BnCameraDevice { public: explicit VirtualCameraDevice( uint32_t cameraId, std::shared_ptr< ::aidl::android::companion::virtualcamera::IVirtualCameraCallback> virtualCameraClientCallback = nullptr); virtual ~VirtualCameraDevice() override = default; ndk::ScopedAStatus getCameraCharacteristics( ::aidl::android::hardware::camera::device::CameraMetadata* _aidl_return) override; ndk::ScopedAStatus getPhysicalCameraCharacteristics( const std::string& in_physicalCameraId, ::aidl::android::hardware::camera::device::CameraMetadata* _aidl_return) override; ndk::ScopedAStatus getResourceCost( ::aidl::android::hardware::camera::common::CameraResourceCost* _aidl_return) override; ndk::ScopedAStatus isStreamCombinationSupported( const ::aidl::android::hardware::camera::device::StreamConfiguration& in_streams, bool* _aidl_return) override; ndk::ScopedAStatus open( const std::shared_ptr< ::aidl::android::hardware::camera::device::ICameraDeviceCallback>& in_callback, std::shared_ptr< ::aidl::android::hardware::camera::device::ICameraDeviceSession>* _aidl_return) override; ndk::ScopedAStatus openInjectionSession( const std::shared_ptr< ::aidl::android::hardware::camera::device::ICameraDeviceCallback>& in_callback, std::shared_ptr< ::aidl::android::hardware::camera::device::ICameraInjectionSession>* _aidl_return) override; ndk::ScopedAStatus setTorchMode(bool in_on) override; ndk::ScopedAStatus turnOnTorchWithStrengthLevel( int32_t in_torchStrength) override; ndk::ScopedAStatus getTorchStrengthLevel(int32_t* _aidl_return) override; binder_status_t dump(int fd, const char** args, uint32_t numArgs) override; // Returns unique virtual camera name in form // "device@{major}.{minor}/virtual/{numerical_id}" std::string getCameraName() const; private: const uint32_t mCameraId; const std::shared_ptr< ::aidl::android::companion::virtualcamera::IVirtualCameraCallback> mVirtualCameraClientCallback; ::aidl::android::hardware::camera::device::CameraMetadata mCameraCharacteristics; }; } // namespace virtualcamera } // namespace companion } // namespace android #endif // ANDROID_COMPANION_VIRTUALCAMERA_VIRTUALCAMERADEVICE_H
services/camera/virtualcamera/VirtualCameraProvider.cc 0 → 100644 +219 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // #define LOG_NDEBUG 0 #define LOG_TAG "VirtualCameraProvider" #include "VirtualCameraProvider.h" #include <atomic> #include <memory> #include <mutex> #include <tuple> #include <utility> #include "VirtualCameraDevice.h" #include "aidl/android/hardware/camera/common/Status.h" #include "log/log.h" #include "util/Util.h" namespace android { namespace companion { namespace virtualcamera { using ::aidl::android::companion::virtualcamera::IVirtualCameraCallback; using ::aidl::android::hardware::camera::common::CameraDeviceStatus; using ::aidl::android::hardware::camera::common::Status; using ::aidl::android::hardware::camera::common::VendorTagSection; using ::aidl::android::hardware::camera::device::ICameraDevice; using ::aidl::android::hardware::camera::provider::CameraIdAndStreamCombination; using ::aidl::android::hardware::camera::provider::ConcurrentCameraIdCombination; using ::aidl::android::hardware::camera::provider::ICameraProviderCallback; // TODO(b/301023410) Make camera id range configurable / dynamic // based on already registered devices. std::atomic_int VirtualCameraProvider::sNextId{42}; ndk::ScopedAStatus VirtualCameraProvider::setCallback( const std::shared_ptr<ICameraProviderCallback>& in_callback) { ALOGV("%s", __func__); if (in_callback == nullptr) { return cameraStatus(Status::ILLEGAL_ARGUMENT); } { const std::lock_guard<std::mutex> lock(mLock); mCameraProviderCallback = in_callback; for (const auto& [cameraName, _] : mCameras) { auto ret = mCameraProviderCallback->cameraDeviceStatusChange( cameraName, CameraDeviceStatus::PRESENT); if (!ret.isOk()) { ALOGE("Failed to announce camera status change: %s", ret.getDescription().c_str()); } } } return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus VirtualCameraProvider::getVendorTags( std::vector<VendorTagSection>* _aidl_return) { ALOGV("%s", __func__); if (_aidl_return == nullptr) { return cameraStatus(Status::ILLEGAL_ARGUMENT); } // No vendor tags supported. _aidl_return->clear(); return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus VirtualCameraProvider::getCameraIdList( std::vector<std::string>* _aidl_return) { ALOGV("%s", __func__); if (_aidl_return == nullptr) { return cameraStatus(Status::ILLEGAL_ARGUMENT); } { const std::lock_guard<std::mutex> lock(mLock); _aidl_return->clear(); _aidl_return->reserve(mCameras.size()); for (const auto& [cameraName, _] : mCameras) { _aidl_return->emplace_back(cameraName); } } return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus VirtualCameraProvider::getCameraDeviceInterface( const std::string& in_cameraDeviceName, std::shared_ptr<ICameraDevice>* _aidl_return) { ALOGV("%s cameraDeviceName %s", __func__, in_cameraDeviceName.c_str()); if (_aidl_return == nullptr) { return cameraStatus(Status::ILLEGAL_ARGUMENT); } { const std::lock_guard<std::mutex> lock(mLock); const auto it = mCameras.find(in_cameraDeviceName); *_aidl_return = (it == mCameras.end()) ? nullptr : it->second; } return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus VirtualCameraProvider::notifyDeviceStateChange( int64_t in_deviceState) { ALOGV("%s", __func__); (void)in_deviceState; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus VirtualCameraProvider::getConcurrentCameraIds( std::vector<ConcurrentCameraIdCombination>* _aidl_return) { ALOGV("%s", __func__); if (_aidl_return == nullptr) { return cameraStatus(Status::ILLEGAL_ARGUMENT); } // No support for any concurrent combination. _aidl_return->clear(); return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus VirtualCameraProvider::isConcurrentStreamCombinationSupported( const std::vector<CameraIdAndStreamCombination>& in_configs, bool* _aidl_return) { ALOGV("%s", __func__); (void)in_configs; if (_aidl_return == nullptr) { return cameraStatus(Status::ILLEGAL_ARGUMENT); } // No support for any stream combination at the moment. *_aidl_return = false; return ndk::ScopedAStatus::ok(); } std::shared_ptr<VirtualCameraDevice> VirtualCameraProvider::createCamera( std::shared_ptr<IVirtualCameraCallback> virtualCameraClientCallback) { auto camera = ndk::SharedRefBase::make<VirtualCameraDevice>( sNextId++, virtualCameraClientCallback); std::shared_ptr<ICameraProviderCallback> callback; { const std::lock_guard<std::mutex> lock(mLock); if (mCameras.find(camera->getCameraName()) != mCameras.end()) { ALOGE("Camera with identical name already exists."); return nullptr; } mCameras.emplace(std::piecewise_construct, std::forward_as_tuple(camera->getCameraName()), std::forward_as_tuple(camera)); callback = mCameraProviderCallback; } if (callback != nullptr) { auto ret = callback->cameraDeviceStatusChange(camera->getCameraName(), CameraDeviceStatus::PRESENT); if (!ret.isOk()) { ALOGE("Failed to announce camera %s status change (PRESENT): %s", camera->getCameraName().c_str(), ret.getDescription().c_str()); } } return camera; } std::shared_ptr<VirtualCameraDevice> VirtualCameraProvider::getCamera( const std::string& cameraName) { const std::lock_guard<std::mutex> lock(mLock); auto it = mCameras.find(cameraName); return it == mCameras.end() ? nullptr : it->second; } bool VirtualCameraProvider::removeCamera(const std::string& name) { std::shared_ptr<ICameraProviderCallback> callback; { const std::lock_guard<std::mutex> lock(mLock); auto it = mCameras.find(name); if (it == mCameras.end()) { ALOGE("Cannot remove camera %s: no such camera", name.c_str()); return false; } // TODO(b/301023410) Gracefully shut down camera. mCameras.erase(it); callback = mCameraProviderCallback; } if (callback != nullptr) { auto ret = callback->cameraDeviceStatusChange( name, CameraDeviceStatus::NOT_PRESENT); if (!ret.isOk()) { ALOGE("Failed to announce camera %s status change (NOT_PRESENT): %s", name.c_str(), ret.getDescription().c_str()); } } return true; } } // namespace virtualcamera } // namespace companion } // namespace android