{"id":9276,"url":"https://patchwork.libcamera.org/api/1.1/patches/9276/?format=json","web_url":"https://patchwork.libcamera.org/patch/9276/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/1.1/projects/1/?format=json","name":"libcamera","link_name":"libcamera","list_id":"libcamera_core","list_email":"libcamera-devel@lists.libcamera.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20200810120406.52654-4-email@uajain.com>","date":"2020-08-10T12:04:14","name":"[libcamera-devel,v2,3/4] android: camera_hal_manager: Support camera hotplug","commit_ref":null,"pull_url":null,"state":"accepted","archived":false,"hash":"c7d12749ed5b70ce94a12e59b14dd6ee3fef537b","submitter":{"id":1,"url":"https://patchwork.libcamera.org/api/1.1/people/1/?format=json","name":"Umang Jain","email":"email@uajain.com"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/9276/mbox/","series":[{"id":1210,"url":"https://patchwork.libcamera.org/api/1.1/series/1210/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=1210","date":"2020-08-10T12:04:11","name":"android: Camera hotplug support","version":2,"mbox":"https://patchwork.libcamera.org/series/1210/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/9276/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/9276/checks/","tags":{},"headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id A5599BD87E\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 10 Aug 2020 12:04:19 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 6186A61054;\n\tMon, 10 Aug 2020 14:04:19 +0200 (CEST)","from o1.f.az.sendgrid.net (o1.f.az.sendgrid.net [208.117.55.132])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 3228560F1D\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 10 Aug 2020 14:04:15 +0200 (CEST)","by filterdrecv-p3las1-559bd7b968-2g29q with SMTP id\n\tfilterdrecv-p3las1-559bd7b968-2g29q-19-5F3137BD-13A\n\t2020-08-10 12:04:14.047155862 +0000 UTC m=+1014490.091964234","from mail.uajain.com (unknown)\n\tby ismtpd0002p1maa1.sendgrid.net (SG) with ESMTP id\n\tqT9G6iTAThiZIB59QpMWWA for <libcamera-devel@lists.libcamera.org>;\n\tMon, 10 Aug 2020 12:04:13.632 +0000 (UTC)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=uajain.com header.i=@uajain.com\n\theader.b=\"WyNfPAqW\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=uajain.com;\n\th=from:subject:in-reply-to:references:mime-version:to:cc:\n\tcontent-transfer-encoding:content-type;\n\ts=s1; bh=M8aQgNhnE+DYpjlzQU2EkiFf6G69fJp/ilD7RDwFstU=;\n\tb=WyNfPAqW25U8xH0y5haUb2u6v9QZPzBMWxKtskQtruzfC9FQs3R0p8Pcl4Frq6R0Jqbc\n\t02+uobvBULk15k01CjQt+OINxYy/3mc4foTq/X5n64hCRqUwHSax6w2bx/R/ZXuCh5QD0a\n\tF8VVb7CVTmcAvo0ueKDB0avxwvYs3Dthw=","From":"Umang Jain <email@uajain.com>","Date":"Mon, 10 Aug 2020 12:04:14 +0000 (UTC)","Message-Id":"<20200810120406.52654-4-email@uajain.com>","In-Reply-To":"<20200810120406.52654-1-email@uajain.com>","References":"<20200810120406.52654-1-email@uajain.com>","Mime-Version":"1.0","X-SG-EID":"1Q40EQ7YGir8a9gjSIAdTjhngY657NMk9ckeo4dbHZDiOpywc/L3L9rFqlwE4KPc4om/Oij3lMEQBxKPi/30O1C7bNFrpbmMJqYozJm+2XE3DPizDXOrMbEn6An6vJDVO5OjqO0feTUot5T1cQ5l2cmgjQ0aHh508gnnWwa6Rb/kEIdNJSPjzsY7po1iTf0G552YCD00hYY5QPkxeK4qC30kJ0HDB+Dp6fM5PED+/dVU4/fEjTBEBcV7Oa37s+czIbzX14nXesKnn0t724GH/Q==","To":"libcamera-devel@lists.libcamera.org","Subject":"[libcamera-devel] [PATCH v2 3/4] android: camera_hal_manager:\n\tSupport camera hotplug","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Content-Type":"text/plain; charset=\"us-ascii\"","Content-Transfer-Encoding":"7bit","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"},"content":"Extend the support for camera hotplug from libcamera's CameraManager\nto CameraHalManager. Use camera module callbacks to let the framework\nknow about the hotplug events and change the status of cameras being\nbeing hotplugged or unplugged via camera_device_status_change().\n\nIntroduce a map camerasMap_ which book-keeps all cameras seen in the\npast by the CameraHalManager. If the camera is seen for the first time,\na new id is assigned to it. If the camera has been seen before by the\nmanager, it's old id is reused. IDs for internal cameras start with\n'0' and for external cameras, they start with '1000'. Note, for the\ncurrent implementation, we assume all UVC cameras are external cameras.\n\nCameraDevice is now a shared object and cameras_ vector stores shared\npointers to CameraDevice. This is done in order to introduce reference\ncounting for CameraDevice objects - especially to handle hot-unplug\nevents. Both camerasMap_ and cameras_ are protected by a mutex.\n\nSigned-off-by: Umang Jain <email@uajain.com>\n---\n src/android/camera_device.cpp      |  15 +++\n src/android/camera_device.h        |   8 +-\n src/android/camera_hal_manager.cpp | 153 ++++++++++++++++++++++++-----\n src/android/camera_hal_manager.h   |  15 ++-\n 4 files changed, 166 insertions(+), 25 deletions(-)","diff":"diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp\nindex d918350..a79bb69 100644\n--- a/src/android/camera_device.cpp\n+++ b/src/android/camera_device.cpp\n@@ -233,6 +233,21 @@ CameraDevice::~CameraDevice()\n \t\tdelete it.second;\n }\n \n+std::shared_ptr<CameraDevice> CameraDevice::create(unsigned int id,\n+\t\t\t\t\t\t    const std::shared_ptr<Camera> &cam)\n+{\n+\tstruct Deleter : std::default_delete<CameraDevice> {\n+\t\tvoid operator()(CameraDevice *camera)\n+\t\t{\n+\t\t\tdelete camera;\n+\t\t}\n+\t};\n+\n+\tCameraDevice *camera = new CameraDevice(id, cam);\n+\n+\treturn std::shared_ptr<CameraDevice>(camera, Deleter());\n+}\n+\n /*\n  * Initialize the camera static information.\n  * This method is called before the camera device is opened.\ndiff --git a/src/android/camera_device.h b/src/android/camera_device.h\nindex 7be9e11..7f9e010 100644\n--- a/src/android/camera_device.h\n+++ b/src/android/camera_device.h\n@@ -47,8 +47,8 @@ struct CameraStream {\n class CameraDevice : protected libcamera::Loggable\n {\n public:\n-\tCameraDevice(unsigned int id, const std::shared_ptr<libcamera::Camera> &camera);\n-\t~CameraDevice();\n+\tstatic std::shared_ptr<CameraDevice> create(unsigned int id,\n+\t\t\t\t\t\t    const std::shared_ptr<libcamera::Camera> &cam);\n \n \tint initialize();\n \n@@ -57,6 +57,7 @@ public:\n \n \tunsigned int id() const { return id_; }\n \tcamera3_device_t *camera3Device() { return &camera3Device_; }\n+\tconst libcamera::Camera *getCamera() { return camera_.get(); };\n \n \tint facing() const { return facing_; }\n \tint orientation() const { return orientation_; }\n@@ -72,6 +73,9 @@ protected:\n \tstd::string logPrefix() const override;\n \n private:\n+\tCameraDevice(unsigned int id, const std::shared_ptr<libcamera::Camera> &camera);\n+\t~CameraDevice();\n+\n \tstruct Camera3RequestDescriptor {\n \t\tCamera3RequestDescriptor(unsigned int frameNumber,\n \t\t\t\t\t unsigned int numBuffers);\ndiff --git a/src/android/camera_hal_manager.cpp b/src/android/camera_hal_manager.cpp\nindex 3d6d2b4..fdde2c0 100644\n--- a/src/android/camera_hal_manager.cpp\n+++ b/src/android/camera_hal_manager.cpp\n@@ -8,6 +8,7 @@\n #include \"camera_hal_manager.h\"\n \n #include <libcamera/camera.h>\n+#include <libcamera/property_ids.h>\n \n #include \"libcamera/internal/log.h\"\n \n@@ -35,6 +36,7 @@ CameraHalManager::CameraHalManager()\n CameraHalManager::~CameraHalManager()\n {\n \tcameras_.clear();\n+\tcamerasMap_.clear();\n \n \tif (cameraManager_) {\n \t\tcameraManager_->stop();\n@@ -47,6 +49,13 @@ int CameraHalManager::init()\n {\n \tcameraManager_ = new CameraManager();\n \n+\t/* Support camera hotplug. */\n+\tcameraManager_->cameraAdded.connect(this, &CameraHalManager::cameraAdded);\n+\tcameraManager_->cameraRemoved.connect(this, &CameraHalManager::cameraRemoved);\n+\n+\tcameraCounter_ = 0;\n+\texternalCameraCounter_ = 1000;\n+\n \tint ret = cameraManager_->start();\n \tif (ret) {\n \t\tLOG(HAL, Error) << \"Failed to start camera manager: \"\n@@ -56,35 +65,25 @@ int CameraHalManager::init()\n \t\treturn ret;\n \t}\n \n-\t/*\n-\t * For each Camera registered in the system, a CameraDevice\n-\t * gets created here to wraps a libcamera Camera instance.\n-\t *\n-\t * \\todo Support camera hotplug.\n-\t */\n-\tunsigned int index = 0;\n-\tfor (auto &cam : cameraManager_->cameras()) {\n-\t\tCameraDevice *camera = new CameraDevice(index, cam);\n-\t\tret = camera->initialize();\n-\t\tif (ret)\n-\t\t\tcontinue;\n-\n-\t\tcameras_.emplace_back(camera);\n-\t\t++index;\n-\t}\n-\n \treturn 0;\n }\n \n CameraDevice *CameraHalManager::open(unsigned int id,\n \t\t\t\t     const hw_module_t *hardwareModule)\n {\n-\tif (id >= numCameras()) {\n+\tMutexLocker locker(mutex_);\n+\n+\tauto iter = std::find_if(cameras_.begin(), cameras_.end(),\n+\t\t\t\t [id](std::shared_ptr<CameraDevice> &cam) {\n+\t\t\t\t\treturn cam->id() == id;\n+\t\t\t\t});\n+\tif (iter == cameras_.end()) {\n \t\tLOG(HAL, Error) << \"Invalid camera id '\" << id << \"'\";\n \t\treturn nullptr;\n \t}\n \n-\tCameraDevice *camera = cameras_[id].get();\n+\tCameraDevice *camera = iter->get();\n+\n \tif (camera->open(hardwareModule))\n \t\treturn nullptr;\n \n@@ -93,9 +92,91 @@ CameraDevice *CameraHalManager::open(unsigned int id,\n \treturn camera;\n }\n \n+void CameraHalManager::cameraAdded(std::shared_ptr<Camera> cam)\n+{\n+\tunsigned int id;\n+\tbool isCameraExternal = false;\n+\tbool isCameraNew = false;\n+\n+\tMutexLocker locker(mutex_);\n+\n+\t/* Each camera is assigned a unique integer id when it is seen for the\n+\t * first time. If the camera has been seen before, the id is reused and\n+\t * the camera is marked as CAMERA_DEVICE_STATUS_PRESENT subsequently.\n+\t *\n+\t * ID starts from '0' for internal cameras and '1000' for external cameras.\n+\t */\n+\tauto iter = camerasMap_.find(cam->id());\n+\tif (iter != camerasMap_.end()) {\n+\t\tid = iter->second;\n+\t} else {\n+\t\tisCameraNew = true;\n+\n+\t\t/*\n+\t\t *  Now check if this is an external camera and assign\n+\t\t *  its id accordingly.\n+\t\t */\n+\t\tconst ControlList &properties = cam->properties();\n+\t\tif (properties.contains(properties::Location) &&\n+\t\t    properties.get(properties::Location) &\n+\t\t    properties::CameraLocationExternal) {\n+\t\t\tisCameraExternal = true;\n+\t\t\tid = externalCameraCounter_;\n+\t\t} else {\n+\t\t\tid = cameraCounter_;\n+\t\t}\n+\t}\n+\n+\t/*\n+\t * For each Camera registered in the system, a CameraDevice\n+\t * gets created here to wraps a libcamera Camera instance.\n+\t */\n+\tstd::shared_ptr<CameraDevice> camera = CameraDevice::create(id, std::move(cam));\n+\tint ret = camera->initialize();\n+\tif (ret) {\n+\t\tLOG(HAL, Error) << \"Failed to initialize camera: \" << cam->id();\n+\t\treturn;\n+\t}\n+\n+\tif (isCameraNew) {\n+\t\tcamerasMap_.emplace(cam->id(), id);\n+\n+\t\tif (isCameraExternal)\n+\t\t\texternalCameraCounter_++;\n+\t\telse\n+\t\t\tcameraCounter_++;\n+\t}\n+\n+\tcameras_.emplace_back(std::move(camera));\n+\n+\tif (callbacks_)\n+\t\tcallbacks_->camera_device_status_change(callbacks_, id,\n+\t\t\t\t\t\t\tCAMERA_DEVICE_STATUS_PRESENT);\n+\tLOG(HAL, Debug) << \"Camera ID: \" << id << \" added successfully.\";\n+}\n+\n+void CameraHalManager::cameraRemoved(std::shared_ptr<Camera> cam)\n+{\n+\tMutexLocker locker(mutex_);\n+\n+\tauto iter = std::find_if(cameras_.begin(), cameras_.end(),\n+\t\t\t\t [cam](std::shared_ptr<CameraDevice> &camera) {\n+\t\t\t\t\treturn cam.get() == camera->getCamera();\n+\t\t\t\t});\n+\tif (iter == cameras_.end())\n+\t\treturn;\n+\n+\tunsigned int id = (*iter)->id();\n+\tcallbacks_->camera_device_status_change(callbacks_, id,\n+\t\t\t\t\t\tCAMERA_DEVICE_STATUS_NOT_PRESENT);\n+\tcameras_.erase(iter);\n+\n+\tLOG(HAL, Debug) << \"Camera ID: \" << id << \" removed successfully.\";\n+}\n+\n unsigned int CameraHalManager::numCameras() const\n {\n-\treturn cameraManager_->cameras().size();\n+\treturn cameraCounter_;\n }\n \n int CameraHalManager::getCameraInfo(unsigned int id, struct camera_info *info)\n@@ -103,12 +184,18 @@ int CameraHalManager::getCameraInfo(unsigned int id, struct camera_info *info)\n \tif (!info)\n \t\treturn -EINVAL;\n \n-\tif (id >= numCameras()) {\n+\tMutexLocker locker(mutex_);\n+\n+\tauto iter = std::find_if(cameras_.begin(), cameras_.end(),\n+\t\t\t\t [id](std::shared_ptr<CameraDevice> &cam) {\n+\t\t\t\t\treturn cam->id() == id;\n+\t\t\t\t});\n+\tif (iter == cameras_.end()) {\n \t\tLOG(HAL, Error) << \"Invalid camera id '\" << id << \"'\";\n \t\treturn -EINVAL;\n \t}\n \n-\tCameraDevice *camera = cameras_[id].get();\n+\tCameraDevice *camera = iter->get();\n \n \tinfo->facing = camera->facing();\n \tinfo->orientation = camera->orientation();\n@@ -124,4 +211,26 @@ int CameraHalManager::getCameraInfo(unsigned int id, struct camera_info *info)\n void CameraHalManager::setCallbacks(const camera_module_callbacks_t *callbacks)\n {\n \tcallbacks_ = callbacks;\n+\n+\tMutexLocker locker(mutex_);\n+\t/*\n+\t * Few cameras might have been hotplugged before setting callbacks_ here.\n+\t * We need to mark CAMERA_DEVICE_STATUS_PRESENT for them explicitly.\n+\t * This hold only for external cameras, as internal cameras are assumed to\n+\t * be present at module load time, by the framework.\n+\t */\n+\tfor (auto &cam : cameraManager_->cameras()) {\n+\t\tauto iter = camerasMap_.find(cam->id());\n+\t\tif (iter == camerasMap_.end())\n+\t\t\tcontinue;\n+\n+\t\tunsigned int id = iter->second;\n+\t\tconst ControlList &properties = cam->properties();\n+\t\tif (properties.contains(properties::Location) &&\n+\t\t    properties.get(properties::Location) &\n+\t\t    properties::CameraLocationExternal) {\n+\t\t\tcallbacks_->camera_device_status_change(callbacks_, id,\n+\t\t\t\t\t\t\t\tCAMERA_DEVICE_STATUS_PRESENT);\n+\t\t}\n+\t}\n }\ndiff --git a/src/android/camera_hal_manager.h b/src/android/camera_hal_manager.h\nindex a582f04..7c481d4 100644\n--- a/src/android/camera_hal_manager.h\n+++ b/src/android/camera_hal_manager.h\n@@ -7,6 +7,8 @@\n #ifndef __ANDROID_CAMERA_MANAGER_H__\n #define __ANDROID_CAMERA_MANAGER_H__\n \n+#include <map>\n+#include <mutex>\n #include <stddef.h>\n #include <vector>\n \n@@ -18,6 +20,9 @@\n \n class CameraDevice;\n \n+using Mutex = std::mutex;\n+using MutexLocker = std::unique_lock<std::mutex>;\n+\n class CameraHalManager\n {\n public:\n@@ -33,10 +38,18 @@ public:\n \tvoid setCallbacks(const camera_module_callbacks_t *callbacks);\n \n private:\n+\tvoid cameraAdded(std::shared_ptr<libcamera::Camera> cam);\n+\tvoid cameraRemoved(std::shared_ptr<libcamera::Camera> cam);\n+\n \tlibcamera::CameraManager *cameraManager_;\n \n \tconst camera_module_callbacks_t *callbacks_;\n-\tstd::vector<std::unique_ptr<CameraDevice>> cameras_;\n+\tstd::vector<std::shared_ptr<CameraDevice>> cameras_;\n+\tstd::map<std::string, unsigned int> camerasMap_;\n+\tMutex mutex_;\n+\n+\tunsigned int externalCameraCounter_;\n+\tunsigned int cameraCounter_;\n };\n \n #endif /* __ANDROID_CAMERA_MANAGER_H__ */\n","prefixes":["libcamera-devel","v2","3/4"]}