From patchwork Thu Jun 11 17:16:02 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Umang Jain X-Patchwork-Id: 4024 Return-Path: Received: from o1.f.az.sendgrid.net (o1.f.az.sendgrid.net [208.117.55.132]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id C2B3461027 for ; Thu, 11 Jun 2020 19:16:03 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=uajain.com header.i=@uajain.com header.b="gSwJ5D0U"; dkim-atps=neutral DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=uajain.com; h=from:subject:in-reply-to:references:mime-version:to:cc:content-type: content-transfer-encoding; s=s1; bh=AOsWfebRFA6ErG2RAs6ttl4LB0gt5ThztqU8dHnZVRk=; b=gSwJ5D0Uoh/cygkrqlucIgdDsjVzK12aVAozrqCBo4s0Dqjro0eP4PgW8MPd5oJdihp1 C40GuaBdA4QFKqYRSdBr0Q2E7TviT1r19gvTGC+dWa3grr2Eg5lSn147hy2Wk2zd3Y2eA7 tql0gGedS6luUU7+sI2SNFTRUx3HXM8NY= Received: by filterdrecv-p3iad2-784dbb6bd8-pfkxc with SMTP id filterdrecv-p3iad2-784dbb6bd8-pfkxc-19-5EE266D2-19 2020-06-11 17:16:02.265012104 +0000 UTC m=+680947.585168719 Received: from mail.uajain.com (unknown) by ismtpd0002p1hnd1.sendgrid.net (SG) with ESMTP id JkbzlI51T2uT2U_ULET41w Thu, 11 Jun 2020 17:16:01.887 +0000 (UTC) From: Umang Jain Date: Thu, 11 Jun 2020 17:16:02 +0000 (UTC) Message-Id: <20200611171528.9381-2-email@uajain.com> In-Reply-To: <20200611171528.9381-1-email@uajain.com> References: <20200611171528.9381-1-email@uajain.com> Mime-Version: 1.0 X-SG-EID: 1Q40EQ7YGir8a9gjSIAdTjhngY657NMk9ckeo4dbHZDiOpywc/L3L9rFqlwE4KPcGsFXrTC3nkaVUz7y/MXE3XUcgQ86CGdnwAGSBYcas7hRdz37+jERDWJQITOSZgEsMGL5UDkVbNyzlEQe1Z++UJRuaht3LAS/e8h9ZMxTTVJtxofhExRCGxZnNAiJVfnQ6xSKMwE7IJKjufmj1APjKi5pcLOSKU6VwbQEZ66FxttuKaO8BK0zhcoMTElJtENRdqX2msWGW5Uy0UXhoOQ5lA== To: laurent.pinchart@ideasonboard.com, libcamera-devel@lists.libcamera.org Subject: [libcamera-devel] [PATCH v4 1/6] libcamera: CameraManager: Drop the vector of created PipelineHandlers X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 11 Jun 2020 17:16:04 -0000 This placeholder for the pipeline-handlers created by CameraManager, was responsible to hold a reference to pipeline-handlers which were not getting dropped anywhere in the code-path. Instead of introducing a fix of decrementing the reference, it is decided to eliminate this vector entirely from the CameraManager. This is due to the fact that the vector does not seem to have much use in CameraManager and thus reducing futile book-keeping. Since the vector is eliminated, there are no unchecked reference of the pipeline-handlers. This fixes the initial issue of dangling driver directories (for e.g. for UVC camera - in /sys/bus/usb/drivers/uvcvideo) on 'unbind' → 'bind' operation while the CameraManager is running. The directories were still kept around even after 'unbind' because of the pipeline-handler's unchecked reference holding onto them. Signed-off-by: Umang Jain --- src/libcamera/camera_manager.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp index 576856a..b8128df 100644 --- a/src/libcamera/camera_manager.cpp +++ b/src/libcamera/camera_manager.cpp @@ -63,7 +63,6 @@ private: bool initialized_; int status_; - std::vector> pipes_; std::unique_ptr enumerator_; IPAManager ipaManager_; @@ -144,7 +143,6 @@ int CameraManager::Private::init() LOG(Camera, Debug) << "Pipeline handler \"" << factory->name() << "\" matched"; - pipes_.push_back(std::move(pipe)); } } @@ -162,7 +160,6 @@ void CameraManager::Private::cleanup() * they all get destroyed before the device enumerator deletes the * media devices. */ - pipes_.clear(); cameras_.clear(); enumerator_.reset(nullptr); From patchwork Thu Jun 11 17:16:04 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Umang Jain X-Patchwork-Id: 4026 Return-Path: Received: from o1.f.az.sendgrid.net (o1.f.az.sendgrid.net [208.117.55.132]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 8612561DFC for ; Thu, 11 Jun 2020 19:16:05 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=uajain.com header.i=@uajain.com header.b="gV02pM6i"; dkim-atps=neutral DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=uajain.com; h=from:subject:in-reply-to:references:mime-version:to:cc: content-transfer-encoding:content-type; s=s1; bh=dziieE3rjqgnky53UeH/783O3RqTH7OtjKspSaaGZvM=; b=gV02pM6iLLtRHyaazn3qkoCIlpLqiludPZ3jQsZIQO2k/d9f9ivFOod8UI1ERSiIiFOP WELeS5SbXcdjX1YfiVN4mxOLHcgiOn701sumFHkvdeNcWfvQEFc9mCP6sQ6y/m+qkR5+Qk s1cCZX32gW3xEnWg0x//oCU79juUPMJiM= Received: by filterdrecv-p3iad2-784dbb6bd8-p7j56 with SMTP id filterdrecv-p3iad2-784dbb6bd8-p7j56-17-5EE266D4-7 2020-06-11 17:16:04.216290268 +0000 UTC m=+680945.016001562 Received: from mail.uajain.com (unknown) by ismtpd0008p1hnd1.sendgrid.net (SG) with ESMTP id rpAEfMy_SMqsn1TiB_m4Dg Thu, 11 Jun 2020 17:16:03.800 +0000 (UTC) From: Umang Jain Date: Thu, 11 Jun 2020 17:16:04 +0000 (UTC) Message-Id: <20200611171528.9381-3-email@uajain.com> In-Reply-To: <20200611171528.9381-1-email@uajain.com> References: <20200611171528.9381-1-email@uajain.com> Mime-Version: 1.0 X-SG-EID: 1Q40EQ7YGir8a9gjSIAdTjhngY657NMk9ckeo4dbHZDiOpywc/L3L9rFqlwE4KPcIgOyDPOiX995eQdp4XFd3BFl5pNeueaPJmHuUAql2udzi84Ctd4sLAv7MxzkKZ0Pkz4DRH5DBvV1uGTjK34SzpoM0WHHfx2TCp0BYAQ9yp3B4e9uxqk4HBrJFKrzBIoS1YsGQJtiHI0G3E5wLLfgDUxWVSbbQyR2h+vLyhjUnrLi7tcwiSUetHlnp0NQgHEmLiRkb1cB6tA07NV3Hh0Lsw== To: laurent.pinchart@ideasonboard.com, libcamera-devel@lists.libcamera.org Subject: [libcamera-devel] [PATCH v4 2/6] libcamera: camera_manager: Refactor pipelines creation into separate function X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 11 Jun 2020 17:16:06 -0000 This commit introduces no functional changes. Split pipelines creation code into a separate function, so that the function can be re-used for upcoming hotplug functionality in subsequent commits. Also, fixup correct tag for \todo. Signed-off-by: Umang Jain Reviewed-by: Laurent Pinchart --- src/libcamera/camera_manager.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp index b8128df..aba5a0c 100644 --- a/src/libcamera/camera_manager.cpp +++ b/src/libcamera/camera_manager.cpp @@ -56,6 +56,7 @@ protected: private: int init(); void cleanup(); + void createPipelineHandlers(); CameraManager *cm_; @@ -123,12 +124,20 @@ int CameraManager::Private::init() if (!enumerator_ || enumerator_->enumerate()) return -ENODEV; + createPipelineHandlers(); + + return 0; +} + +void CameraManager::Private::createPipelineHandlers() +{ /* - * TODO: Try to read handlers and order from configuration + * \todo Try to read handlers and order from configuration * file and only fallback on all handlers if there is no * configuration file. */ - std::vector &factories = PipelineHandlerFactory::factories(); + std::vector &factories = + PipelineHandlerFactory::factories(); for (PipelineHandlerFactory *factory : factories) { /* @@ -146,14 +155,12 @@ int CameraManager::Private::init() } } - /* TODO: register hot-plug callback here */ - - return 0; + /* \todo Register hot-plug callback here */ } void CameraManager::Private::cleanup() { - /* TODO: unregister hot-plug callback here */ + /* \todo Unregister hot-plug callback here */ /* * Release all references to cameras and pipeline handlers to ensure From patchwork Thu Jun 11 17:16:05 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Umang Jain X-Patchwork-Id: 4027 Return-Path: Received: from o1.f.az.sendgrid.net (o1.f.az.sendgrid.net [208.117.55.132]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 952D961027 for ; Thu, 11 Jun 2020 19:16:06 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=uajain.com header.i=@uajain.com header.b="n+xRptwD"; dkim-atps=neutral DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=uajain.com; h=from:subject:in-reply-to:references:mime-version:to:cc: content-transfer-encoding:content-type; s=s1; bh=hy9kPHlRcNDPV9xcmzuBcY0diH6AD2OeqkMd1R7orls=; b=n+xRptwDuTQTuFp1LzmiK1A+id2reV4JR1AsZFdwiq0D6omMgytXLjR76gtnz03joT84 gR/E+8ZVv9IEB5kL+sVCLiOcbPAmoxIEEr8dCE2GfDqr5xl00JYauPtbjHA2+nTS/t4gnu fqRALGGuBGYrVyxgzlIdtI+LU833MS/Nc= Received: by filterdrecv-p3iad2-784dbb6bd8-jftzh with SMTP id filterdrecv-p3iad2-784dbb6bd8-jftzh-17-5EE266D5-23 2020-06-11 17:16:05.310937936 +0000 UTC m=+680954.236033796 Received: from mail.uajain.com (unknown) by ismtpd0001p1hnd1.sendgrid.net (SG) with ESMTP id BQmhn86PQSKaruCQddJE9A Thu, 11 Jun 2020 17:16:04.923 +0000 (UTC) From: Umang Jain Date: Thu, 11 Jun 2020 17:16:05 +0000 (UTC) Message-Id: <20200611171528.9381-4-email@uajain.com> In-Reply-To: <20200611171528.9381-1-email@uajain.com> References: <20200611171528.9381-1-email@uajain.com> Mime-Version: 1.0 X-SG-EID: 1Q40EQ7YGir8a9gjSIAdTjhngY657NMk9ckeo4dbHZDiOpywc/L3L9rFqlwE4KPcofpXQ1KwJPJayd5tGsU1eP6ax03JDjPaDSEaSM2mPEVoxIECu4EqLbxhCzPjDh7ubXCNxFCfLoNsveqR5JoGf3wpL7FfNtcTyZfgF33X8BDEhnu1AYYqNBgD7T5GUmG0ZuArQebzDv0ZCAunIpmQrVE612Ua9baS7/uKDeFuqjGQbNxZPoPLF1z5fDP/g/P2JDC0J+AhF+wzTK/+5XHImQ== To: laurent.pinchart@ideasonboard.com, libcamera-devel@lists.libcamera.org Subject: [libcamera-devel] [PATCH v4 3/6] libcamera: device_enumerator: Emit a signal when a new devices are added X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 11 Jun 2020 17:16:07 -0000 Emit a signal whenever a new MediaDevices are added to the DeviceEnumerator. This will allow CameraManager to be notified about the new devices and it can re-emumerate all the devices currently present on the system. Device enumeration by the CameraManger is a expensive operation hence, we want one signal emission per 'x' milliseconds to notify multiple devices additions as a single batch, by the DeviceEnumerator. Add a \todo to investigate the support for that. Signed-off-by: Umang Jain Reviewed-by: Laurent Pinchart Reviewed-by: Kieran Bingham --- include/libcamera/internal/device_enumerator.h | 4 ++++ src/libcamera/camera_manager.cpp | 4 ++-- src/libcamera/device_enumerator.cpp | 13 +++++++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/include/libcamera/internal/device_enumerator.h b/include/libcamera/internal/device_enumerator.h index 25a3630..a985040 100644 --- a/include/libcamera/internal/device_enumerator.h +++ b/include/libcamera/internal/device_enumerator.h @@ -13,6 +13,8 @@ #include +#include + namespace libcamera { class MediaDevice; @@ -43,6 +45,8 @@ public: std::shared_ptr search(const DeviceMatch &dm); + Signal<> devicesAdded; + protected: std::unique_ptr createDevice(const std::string &deviceNode); void addDevice(std::unique_ptr &&media); diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp index aba5a0c..17a4afb 100644 --- a/src/libcamera/camera_manager.cpp +++ b/src/libcamera/camera_manager.cpp @@ -155,12 +155,12 @@ void CameraManager::Private::createPipelineHandlers() } } - /* \todo Register hot-plug callback here */ + enumerator_->devicesAdded.connect(this, &Private::createPipelineHandlers); } void CameraManager::Private::cleanup() { - /* \todo Unregister hot-plug callback here */ + enumerator_->devicesAdded.disconnect(this, &Private::createPipelineHandlers); /* * Release all references to cameras and pipeline handlers to ensure diff --git a/src/libcamera/device_enumerator.cpp b/src/libcamera/device_enumerator.cpp index e21a2a7..e3264a5 100644 --- a/src/libcamera/device_enumerator.cpp +++ b/src/libcamera/device_enumerator.cpp @@ -227,6 +227,16 @@ std::unique_ptr DeviceEnumerator::createDevice(const std::string &d return media; } +/** +* \var DeviceEnumerator::devicesAdded +* \brief Notify of new media devices being found +* +* This signal is emitted when the device enumerator finds new media devices in +* the system. It may be emitted for every newly detected device, or once for +* multiple of devices, at the discretion of the device enumerator. Not all +* device enumerator types may support dynamic detection of new devices. +*/ + /** * \brief Add a media device to the enumerator * \param[in] media media device instance to add @@ -242,6 +252,9 @@ void DeviceEnumerator::addDevice(std::unique_ptr &&media) << "Added device " << media->deviceNode() << ": " << media->driver(); devices_.push_back(std::move(media)); + + /* \todo To batch multiple additions, emit with a small delay here. */ + devicesAdded.emit(); } /** From patchwork Thu Jun 11 17:16:07 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Umang Jain X-Patchwork-Id: 4029 Return-Path: Received: from o1.f.az.sendgrid.net (o1.f.az.sendgrid.net [208.117.55.132]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 28ABD61DD5 for ; Thu, 11 Jun 2020 19:16:09 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=uajain.com header.i=@uajain.com header.b="QxgbcBH4"; dkim-atps=neutral DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=uajain.com; h=from:subject:in-reply-to:references:mime-version:to:cc: content-transfer-encoding:content-type; s=s1; bh=uCl/J67FgCo4M0iTjSjYFrrXNUOL4Xij4N/9uBG8uWg=; b=QxgbcBH4+NExzcmrIKlSF31HSYbqDxvW3SB6SI5oI0Vd6iEqnfQzwqSKzjH8tIJyuG2n 0sYGgX3ZWz6Uvwy5ybXdt08JryNFVvCBlgJ+/LfdoLvG2pAp4ZXvdIvnGZCeN6/Buud1qy jGIC7LFh2ODlm28cHgwzIQ3On3t5z/33Y= Received: by filter0090p3las1.sendgrid.net with SMTP id filter0090p3las1-14468-5EE266D6-EC 2020-06-11 17:16:07.203653683 +0000 UTC m=+255693.521769057 Received: from mail.uajain.com (unknown) by ismtpd0006p1maa1.sendgrid.net (SG) with ESMTP id lvWJsPnVQbmps2Y_IfyTlQ Thu, 11 Jun 2020 17:16:06.142 +0000 (UTC) From: Umang Jain Date: Thu, 11 Jun 2020 17:16:07 +0000 (UTC) Message-Id: <20200611171528.9381-5-email@uajain.com> In-Reply-To: <20200611171528.9381-1-email@uajain.com> References: <20200611171528.9381-1-email@uajain.com> Mime-Version: 1.0 X-SG-EID: 1Q40EQ7YGir8a9gjSIAdTjhngY657NMk9ckeo4dbHZDiOpywc/L3L9rFqlwE4KPc1jYMYoB2JcJvIPCLN/wEG610TgdcqfhdUUmOwh3OVHUvLG4JLQNOjA55evh7qLtED5wbsJ1FUDfsK1LNzyi2h/VYO26X5F1UGd0nXCLPVdgFI3SBgr7ie1Esq4seniqlzP/BBNUTzW5L0sDtVwsN3yuuEbeZMUq2vWqksJfEvXQ4LX/ABn9dtvfwcWh0R++e To: laurent.pinchart@ideasonboard.com, libcamera-devel@lists.libcamera.org Subject: [libcamera-devel] [PATCH v4 4/6] libcamera: camera_manager: Introduce signals when a camera is added/removed X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 11 Jun 2020 17:16:09 -0000 Emit 'cameraAdded' and 'cameraRemoved' from CameraManager to enable hotplug and hot-unplug support in application like QCam. To avoid use-after-free race between the CameraManager and the application, emit the 'cameraRemoved' with the shared_ptr version of . This requires to change the function signature of CameraManager::removeCamera() API. Also, until now, CameraManager::Private::addCamera() transfers the entire ownership of camera shared_ptr to CameraManager using std::move(). This patch changes the signature of Private::addCamera to accept pass-by-value camera parameter. It is done to make it clear from the caller point of view that the pointer within the caller will still be valid after this function returns. With this change in, we can emit the camera pointer via 'cameraAdded' signal without hitting a segfault. Signed-off-by: Umang Jain Reviewed-by: Laurent Pinchart --- include/libcamera/camera_manager.h | 6 ++++- src/libcamera/camera_manager.cpp | 38 ++++++++++++++++++++++++++---- src/libcamera/pipeline_handler.cpp | 2 +- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/include/libcamera/camera_manager.h b/include/libcamera/camera_manager.h index 95dc636..9eb2b6f 100644 --- a/include/libcamera/camera_manager.h +++ b/include/libcamera/camera_manager.h @@ -13,6 +13,7 @@ #include #include +#include namespace libcamera { @@ -36,13 +37,16 @@ public: void addCamera(std::shared_ptr camera, const std::vector &devnums); - void removeCamera(Camera *camera); + void removeCamera(std::shared_ptr camera); static const std::string &version() { return version_; } void setEventDispatcher(std::unique_ptr dispatcher); EventDispatcher *eventDispatcher(); + Signal> cameraAdded; + Signal> cameraRemoved; + private: static const std::string version_; static CameraManager *self_; diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp index 17a4afb..7f846c0 100644 --- a/src/libcamera/camera_manager.cpp +++ b/src/libcamera/camera_manager.cpp @@ -36,7 +36,7 @@ public: Private(CameraManager *cm); int start(); - void addCamera(std::shared_ptr &camera, + void addCamera(std::shared_ptr camera, const std::vector &devnums); void removeCamera(Camera *camera); @@ -172,7 +172,7 @@ void CameraManager::Private::cleanup() enumerator_.reset(nullptr); } -void CameraManager::Private::addCamera(std::shared_ptr &camera, +void CameraManager::Private::addCamera(std::shared_ptr camera, const std::vector &devnums) { MutexLocker locker(mutex_); @@ -375,6 +375,34 @@ std::shared_ptr CameraManager::get(dev_t devnum) return iter->second.lock(); } +/** + * \var CameraManager::cameraAdded + * \brief Notify of a new camera added to the system + * + * This signal is emitted when a new camera is detected and successfully handled + * by the camera manager. The notification occurs alike for cameras detected + * when the manager is started with start() or when new cameras are later + * connected to the system. When the signal is emitted the new camera is already + * available from the list of cameras(). + * + * The signal is emitted from the CameraManager thread. Applications shall + * minimize the time spent in the signal handler and shall in particular not + * perform any blocking operation. + */ + +/** + * \var CameraManager::cameraRemoved + * \brief Notify of a new camera removed from the system + * + * This signal is emitted when a camera is removed from the system. When the + * signal is emitted the camera is not available from the list of cameras() + * anymore. + * + * The signal is emitted from the CameraManager thread. Applications shall + * minimize the time spent in the signal handler and shall in particular not + * perform any blocking operation. + */ + /** * \brief Add a camera to the camera manager * \param[in] camera The camera to be added @@ -395,6 +423,7 @@ void CameraManager::addCamera(std::shared_ptr camera, ASSERT(Thread::current() == p_.get()); p_->addCamera(camera, devnums); + cameraAdded.emit(camera); } /** @@ -407,11 +436,12 @@ void CameraManager::addCamera(std::shared_ptr camera, * * \context This function shall be called from the CameraManager thread. */ -void CameraManager::removeCamera(Camera *camera) +void CameraManager::removeCamera(std::shared_ptr camera) { ASSERT(Thread::current() == p_.get()); - p_->removeCamera(camera); + p_->removeCamera(camera.get()); + cameraRemoved.emit(camera); } /** diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp index a0f6b0f..bad79dc 100644 --- a/src/libcamera/pipeline_handler.cpp +++ b/src/libcamera/pipeline_handler.cpp @@ -565,7 +565,7 @@ void PipelineHandler::disconnect() continue; camera->disconnect(); - manager_->removeCamera(camera.get()); + manager_->removeCamera(camera); } cameras_.clear(); From patchwork Thu Jun 11 17:16:07 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Umang Jain X-Patchwork-Id: 4028 Return-Path: Received: from o1.f.az.sendgrid.net (o1.f.az.sendgrid.net [208.117.55.132]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 0EEF161DFC for ; Thu, 11 Jun 2020 19:16:07 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=uajain.com header.i=@uajain.com header.b="oCXxs9BS"; dkim-atps=neutral DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=uajain.com; h=from:subject:in-reply-to:references:mime-version:to:cc: content-transfer-encoding:content-type; s=s1; bh=HBjEdPx/lhdtvyjmpXvqfUD2vSMUlvrIpG19HuvHFEg=; b=oCXxs9BSoIy2AC94FyfS+4kffIZ0LDwpGH0uGn0S2I2fH8qIAKXxy/2z1LepDSNEmrEK XJH+DQhll21xOtMEz/WVtiyeQnocFBq9DjjpPQMXVqdjLZyWo1dHcTEic8ZUFTGTeaoxQE h/dfvJIgb59z+io7tIwoGs331eSdFCLeQ= Received: by filterdrecv-p3iad2-784dbb6bd8-6snds with SMTP id filterdrecv-p3iad2-784dbb6bd8-6snds-19-5EE266D6-83 2020-06-11 17:16:06.927259659 +0000 UTC m=+680958.357246709 Received: from mail.uajain.com (unknown) by ismtpd0008p1maa1.sendgrid.net (SG) with ESMTP id qY0nuwV4QSyqTiQxjTeDYA Thu, 11 Jun 2020 17:16:06.451 +0000 (UTC) From: Umang Jain Date: Thu, 11 Jun 2020 17:16:07 +0000 (UTC) Message-Id: <20200611171528.9381-6-email@uajain.com> In-Reply-To: <20200611171528.9381-1-email@uajain.com> References: <20200611171528.9381-1-email@uajain.com> Mime-Version: 1.0 X-SG-EID: 1Q40EQ7YGir8a9gjSIAdTjhngY657NMk9ckeo4dbHZDiOpywc/L3L9rFqlwE4KPc4om/Oij3lMEQBxKPi/30Oz/DwSQ6C97TnwZl55Mgdu1vHj4ACGu6c9ssDkHq3vZAUZCBbkOtIYFPbf8pH/IG+2A9W51daH+NkONGw4JYwKJhIFg4YkXPbTHuKhXcaU+pGBAROzEFTCuYYZYoglk/PE0BR9t3dztqSe+s1+fx6GAjxgzKFB68PpH7gE9RtLOb0w1keQxQObdony+EdeBgiA== To: laurent.pinchart@ideasonboard.com, libcamera-devel@lists.libcamera.org Subject: [libcamera-devel] [PATCH v4 5/6] qcam: main_window: Introduce initial hotplug support X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 11 Jun 2020 17:16:08 -0000 Hook up various QCam UI bits with hotplug support introduced in previous commits. This looks good-enough as first steps to see how the hotplugging functionality is turning out to be from application point-of-view. One can still think of few edge case nuances not yet covered under this implementation hence, those are intentionally kept out of scope for now. It might require some thinking and/or additional time on hand. Signed-off-by: Umang Jain Reviewed-by: Laurent Pinchart Reviewed-by: Kieran Bingham --- src/qcam/main_window.cpp | 76 ++++++++++++++++++++++++++++++++++++++++ src/qcam/main_window.h | 6 ++++ 2 files changed, 82 insertions(+) diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp index 2960259..7bc1360 100644 --- a/src/qcam/main_window.cpp +++ b/src/qcam/main_window.cpp @@ -59,6 +59,36 @@ public: } }; +/** + * \brief Custom QEvent to signal hotplug or unplug + */ +class HotplugEvent : public QEvent +{ +public: + enum PlugEvent { + HotPlug, + HotUnplug + }; + + HotplugEvent(std::shared_ptr camera, PlugEvent event) + : QEvent(type()), camera_(std::move(camera)), plugEvent_(event) + { + } + + static Type type() + { + static int type = QEvent::registerEventType(); + return static_cast(type); + } + + PlugEvent hotplugEvent() const { return plugEvent_; } + Camera *camera() const { return camera_.get(); } + +private: + std::shared_ptr camera_; + PlugEvent plugEvent_; +}; + MainWindow::MainWindow(CameraManager *cm, const OptionsParser::Options &options) : saveRaw_(nullptr), options_(options), cm_(cm), allocator_(nullptr), isCapturing_(false), captureRaw_(false) @@ -81,6 +111,10 @@ MainWindow::MainWindow(CameraManager *cm, const OptionsParser::Options &options) setCentralWidget(viewfinder_); adjustSize(); + /* Hotplug/unplug support */ + cm_->cameraAdded.connect(this, &MainWindow::addCamera); + cm_->cameraRemoved.connect(this, &MainWindow::removeCamera); + /* Open the camera and start capture. */ ret = openCamera(); if (ret < 0) { @@ -105,6 +139,9 @@ bool MainWindow::event(QEvent *e) if (e->type() == CaptureEvent::type()) { processCapture(); return true; + } else if (e->type() == HotplugEvent::type()) { + processHotplug(static_cast(e)); + return true; } return QMainWindow::event(e); @@ -535,6 +572,45 @@ void MainWindow::stopCapture() setWindowTitle(title_); } +/* ----------------------------------------------------------------------------- + * Camera hotplugging support + */ + +void MainWindow::processHotplug(HotplugEvent *e) +{ + Camera *camera = e->camera(); + HotplugEvent::PlugEvent event = e->hotplugEvent(); + + if (event == HotplugEvent::HotPlug) { + cameraCombo_->addItem(QString::fromStdString(camera->name())); + } else if (event == HotplugEvent::HotUnplug) { + /* Check if the currently-streaming camera is removed. */ + if (camera == camera_.get()) { + toggleCapture(false); + cameraCombo_->setCurrentIndex(0); + } + + int camIndex = cameraCombo_->findText(QString::fromStdString(camera->name())); + cameraCombo_->removeItem(camIndex); + } +} + +void MainWindow::addCamera(std::shared_ptr camera) +{ + qInfo() << "Adding new camera:" << camera->name().c_str(); + QCoreApplication::postEvent(this, + new HotplugEvent(std::move(camera), + HotplugEvent::HotPlug)); +} + +void MainWindow::removeCamera(std::shared_ptr camera) +{ + qInfo() << "Removing camera:" << camera->name().c_str(); + QCoreApplication::postEvent(this, + new HotplugEvent(std::move(camera), + HotplugEvent::HotUnplug)); +} + /* ----------------------------------------------------------------------------- * Image Save */ diff --git a/src/qcam/main_window.h b/src/qcam/main_window.h index 59fa2d9..4606fe4 100644 --- a/src/qcam/main_window.h +++ b/src/qcam/main_window.h @@ -32,6 +32,8 @@ using namespace libcamera; class QAction; class QComboBox; +class HotplugEvent; + enum { OptCamera = 'c', OptHelp = 'h', @@ -87,8 +89,12 @@ private: int startCapture(); void stopCapture(); + void addCamera(std::shared_ptr camera); + void removeCamera(std::shared_ptr camera); + void requestComplete(Request *request); void processCapture(); + void processHotplug(HotplugEvent *e); void processViewfinder(FrameBuffer *buffer); /* UI elements */ From patchwork Thu Jun 11 17:16:08 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Umang Jain X-Patchwork-Id: 4030 Return-Path: Received: from o1.f.az.sendgrid.net (o1.f.az.sendgrid.net [208.117.55.132]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 51BAC61167 for ; Thu, 11 Jun 2020 19:16:09 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=uajain.com header.i=@uajain.com header.b="MLvjpl9k"; dkim-atps=neutral DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=uajain.com; h=from:subject:in-reply-to:references:mime-version:to:cc: content-transfer-encoding:content-type; s=s1; bh=BemP8mCiSuDiWTKrP7yL6afMkUnpjFUGlYIFEVEdddU=; b=MLvjpl9kjlza2Iy4nM/mQQ483uKT6+qClWBfu+oVNc50PH3WB9zKTVTnAucw5Xr9NBDL D6tqouVnW5bQATFqowVniHR6nCEytdnX3lmj79v/px3JVVBmpMma9efJr6i3YN8rVA5thl Zqb3tLiVTmUgVcSuuoZconf7Wkw8YVWwk= Received: by filterdrecv-p3iad2-784dbb6bd8-hcng7 with SMTP id filterdrecv-p3iad2-784dbb6bd8-hcng7-18-5EE266D7-DD 2020-06-11 17:16:08.22047431 +0000 UTC m=+680961.308968930 Received: from mail.uajain.com (unknown) by ismtpd0008p1maa1.sendgrid.net (SG) with ESMTP id Pfb7fKVHRxi6I5sRD3YkMQ Thu, 11 Jun 2020 17:16:07.475 +0000 (UTC) From: Umang Jain Date: Thu, 11 Jun 2020 17:16:08 +0000 (UTC) Message-Id: <20200611171528.9381-7-email@uajain.com> In-Reply-To: <20200611171528.9381-1-email@uajain.com> References: <20200611171528.9381-1-email@uajain.com> Mime-Version: 1.0 X-SG-EID: 1Q40EQ7YGir8a9gjSIAdTjhngY657NMk9ckeo4dbHZDiOpywc/L3L9rFqlwE4KPcYDswUgqLHty1lirORaEntR95UyLLITGvBSHaUC4UjCvx2dCMfy3hTqgrHZ5ipiSUrCjpIrCCy7Qh//02WzlWEIfZvUgptFn09BhgLNaxQw8M8wHM6Zp0mHT8kaWM5v4ceJLBZ4db5ezZkpdmTwO67bGgf7EsIAJ9theFlLxjT8ZwRScRPCXYC2IVBPZPL4CF0yO7OkTG0mj+2f+XG5on3A== To: laurent.pinchart@ideasonboard.com, libcamera-devel@lists.libcamera.org Subject: [libcamera-devel] [PATCH v4 6/6] tests: Introduce hotplug hot-unplug unit test X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 11 Jun 2020 17:16:09 -0000 This test checks the code-paths for camera's hotplugged and unplugged support. It is based on bind/unbind of a UVC device from sysfs. Hence, this test requires root permissions to run and should have at least one already bound UVC device present in the system. Signed-off-by: Umang Jain Reviewed-by: Laurent Pinchart --- test/hotplug-cameras.cpp | 124 +++++++++++++++++++++++++++++++++++++++ test/meson.build | 1 + 2 files changed, 125 insertions(+) create mode 100644 test/hotplug-cameras.cpp diff --git a/test/hotplug-cameras.cpp b/test/hotplug-cameras.cpp new file mode 100644 index 0000000..07500fc --- /dev/null +++ b/test/hotplug-cameras.cpp @@ -0,0 +1,124 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020, Umang Jain + * + * hotplug-cameras.cpp - Test cameraAdded/cameraRemoved signals in CameraManager + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "libcamera/internal/file.h" +#include "libcamera/internal/thread.h" + +#include "test.h" + +using namespace libcamera; + +class HotplugTest : public Test +{ +protected: + void cameraAddedHandler(std::shared_ptr cam) + { + cameraAdded_ = true; + } + + void cameraRemovedHandler(std::shared_ptr cam) + { + cameraRemoved_ = true; + } + + int init() + { + if (!File::exists("/sys/module/uvcvideo")) { + std::cout << "uvcvideo driver is not loaded, skipping" << std::endl; + return TestSkip; + } + + if (geteuid() != 0) { + std::cout << "This test requires root permissions, skipping" << std::endl; + return TestSkip; + } + + cm_ = new CameraManager(); + if (cm_->start()) { + std::cout << "Failed to start camera manager" << std::endl; + return TestFail; + } + + cameraAdded_ = false; + cameraRemoved_ = false; + + cm_->cameraAdded.connect(this, &HotplugTest::cameraAddedHandler); + cm_->cameraRemoved.connect(this, &HotplugTest::cameraRemovedHandler); + + return 0; + } + + int run() + { + DIR *dir; + struct dirent *dirent; + std::string uvcDeviceDir; + + dir = opendir(uvcDriverDir_.c_str()); + /* Find a UVC device directory, which we can bind/unbind. */ + while ((dirent = readdir(dir)) != nullptr) { + if (!File::exists(uvcDriverDir_ + dirent->d_name + "/video4linux")) + continue; + + uvcDeviceDir = dirent->d_name; + break; + } + closedir(dir); + + /* If no UVC device found, skip the test. */ + if (uvcDeviceDir.empty()) + return TestSkip; + + /* Unbind a camera and process events. */ + std::ofstream(uvcDriverDir_ + "unbind", std::ios::binary) + << uvcDeviceDir; + Timer timer; + timer.start(1000); + while (timer.isRunning() && !cameraRemoved_) + Thread::current()->eventDispatcher()->processEvents(); + if (!cameraRemoved_) + return TestFail; + + /* Bind the camera again and process events. */ + std::ofstream(uvcDriverDir_ + "bind", std::ios::binary) + << uvcDeviceDir; + timer.start(1000); + while (timer.isRunning() && !cameraAdded_) + Thread::current()->eventDispatcher()->processEvents(); + if (!cameraAdded_) + return TestFail; + + return TestPass; + } + + void cleanup() + { + cm_->stop(); + delete cm_; + } + +private: + CameraManager *cm_; + static const std::string uvcDriverDir_; + bool cameraRemoved_; + bool cameraAdded_; +}; + +const std::string HotplugTest::uvcDriverDir_ = "/sys/bus/usb/drivers/uvcvideo/"; + +TEST_REGISTER(HotplugTest) diff --git a/test/meson.build b/test/meson.build index bd7da14..a868813 100644 --- a/test/meson.build +++ b/test/meson.build @@ -30,6 +30,7 @@ internal_tests = [ ['event-thread', 'event-thread.cpp'], ['file', 'file.cpp'], ['file-descriptor', 'file-descriptor.cpp'], + ['hotplug-cameras', 'hotplug-cameras.cpp'], ['message', 'message.cpp'], ['object', 'object.cpp'], ['object-invoke', 'object-invoke.cpp'],