From patchwork Thu May 21 13:54:23 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Umang Jain X-Patchwork-Id: 3831 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 1231F603D9 for ; Thu, 21 May 2020 15:54:24 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=uajain.com header.i=@uajain.com header.b="LNO1b5uf"; 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=wn+gdJqcuE9rglJzbT/jTFN06B0QGnuQfW53WggZ38o=; b=LNO1b5ufanmHUtu0RI3Gb85ns6yVeTG+r+hzIniZqqtEGJM0UvNLSV09Bo0fA4MbyUKd dOes8Dy+CkaJv4V/7gnYC9sx12DxEg1fY5jAQ4QS27pTFCQ3Wxc8ihyl6WQBWViNRvYkq2 inOdae1I7vFTQkEZInug1bhrvwkfQRDcs= Received: by filterdrecv-p3iad2-8ddf98858-z54vx with SMTP id filterdrecv-p3iad2-8ddf98858-z54vx-21-5EC6880F-2C 2020-05-21 13:54:23.393741462 +0000 UTC m=+4884413.876381335 Received: from mail.uajain.com (unknown) by ismtpd0007p1hnd1.sendgrid.net (SG) with ESMTP id DeCBve0bRmiF6NygHMSz7A for ; Thu, 21 May 2020 13:54:23.027 +0000 (UTC) From: Umang Jain Date: Thu, 21 May 2020 13:54:23 +0000 (UTC) Message-Id: <20200521135416.13685-2-email@uajain.com> In-Reply-To: <20200521135416.13685-1-email@uajain.com> References: <20200521135416.13685-1-email@uajain.com> Mime-Version: 1.0 X-SG-EID: 1Q40EQ7YGir8a9gjSIAdTjhngY657NMk9ckeo4dbHZDiOpywc/L3L9rFqlwE4KPcu4OVMbZ0DPo4pBBcZhwsTNJljPL69hOm6sh0zLvK/f8YcU6EYwcaDRtEIrRA1LqGi7+lhb+/Q81m6Tapah73poiLqsoJ4Qk0QyAEO35p9RnhyRGzKAZ+xwV4TFFEFVIZdwRNZA7ANIvy5+ZaVtmPutVZE9dIdENxy+63Yu/o2XQ4gw8AjCwKSvTVTW9tHii5myKb8ZwrHOJdWA03RkbRsA== To: libcamera-devel@lists.libcamera.org Subject: [libcamera-devel] [PATCH v3 1/5] libcamera: camera_manager: Refactor device enumeration 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, 21 May 2020 13:54:27 -0000 This commit introduces no functional changes. Split device enumeration 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 849377a..8d9cb02 100644 --- a/src/libcamera/camera_manager.cpp +++ b/src/libcamera/camera_manager.cpp @@ -53,6 +53,7 @@ protected: private: int init(); + void enumerateDevices(); void cleanup(); CameraManager *cm_; @@ -120,12 +121,20 @@ int CameraManager::Private::init() if (!enumerator_ || enumerator_->enumerate()) return -ENODEV; + enumerateDevices(); + + return 0; +} + +void CameraManager::Private::enumerateDevices() +{ /* - * 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) { /* @@ -144,14 +153,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 May 21 13:54:25 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Umang Jain X-Patchwork-Id: 3834 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 0BC5E60E3A for ; Thu, 21 May 2020 15:54:26 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=uajain.com header.i=@uajain.com header.b="RHmLBNsm"; 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=947S7DVuvlTm+8i8IFP//rdO5Jthn/wz8+oYu/zUtq0=; b=RHmLBNsmekG7tTDKP40BgpvXkLDACRuTqsBoQ5t1PgFehYwbeQPTz2i91N/k5LRoMEWG uZMKU0TbisVHFZbsSWtV7PFAIRYjLEu/FoRgHyUrVhTdWmXvtoBs8WWkSwbOTdsyHq9OKJ pDvj53Y8Kal0Ud2CbcZIx6SQXffyEfQZg= Received: by filterdrecv-p3iad2-8ddf98858-xxtk7 with SMTP id filterdrecv-p3iad2-8ddf98858-xxtk7-19-5EC68811-F 2020-05-21 13:54:25.454086813 +0000 UTC m=+4884415.326188984 Received: from mail.uajain.com (unknown) by ismtpd0002p1maa1.sendgrid.net (SG) with ESMTP id APaP3d8zSB6JcgguhOnXrA for ; Thu, 21 May 2020 13:54:24.468 +0000 (UTC) From: Umang Jain Date: Thu, 21 May 2020 13:54:25 +0000 (UTC) Message-Id: <20200521135416.13685-3-email@uajain.com> In-Reply-To: <20200521135416.13685-1-email@uajain.com> References: <20200521135416.13685-1-email@uajain.com> Mime-Version: 1.0 X-SG-EID: 1Q40EQ7YGir8a9gjSIAdTjhngY657NMk9ckeo4dbHZDiOpywc/L3L9rFqlwE4KPcREc2X1LpIgLW8oEasGbeg2Bl5j4DWfGLn/larXYgYhgN9fr5OnjLUzMofQ4XGnoUUbc9ciKFxnN1DhxKKazKGtf/TmgJnZzfgVlMD1QhZE3850RZLqVqnxHn5SF1+1v6wCGacd8oB+r12bRC+Tnrw3xe3L6Yw7O3mmLaKPBaDZkI9bcrA18JwMyWjACttepfoHkBFS2Me2QVY0yyzky03Q== To: libcamera-devel@lists.libcamera.org Subject: [libcamera-devel] [PATCH v3 2/5] 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, 21 May 2020 13:54:27 -0000 Emit a signal whenever a new MediaDevice(s) 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 --- 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 433e357..aa6a6fe 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<> deviceAdded; + 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 8d9cb02..27064d2 100644 --- a/src/libcamera/camera_manager.cpp +++ b/src/libcamera/camera_manager.cpp @@ -153,12 +153,12 @@ void CameraManager::Private::enumerateDevices() } } - /* \todo register hot-plug callback here */ + enumerator_->deviceAdded.connect(this, &Private::enumerateDevices); } void CameraManager::Private::cleanup() { - /* \todo unregister hot-plug callback here */ + enumerator_->deviceAdded.disconnect(this, &Private::enumerateDevices); /* * 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..6c5ebb7 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::deviceAdded +* \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. */ + deviceAdded.emit(); } /** From patchwork Thu May 21 13:54:25 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Umang Jain X-Patchwork-Id: 3833 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 BA68960E08 for ; Thu, 21 May 2020 15:54:26 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=uajain.com header.i=@uajain.com header.b="oMHhjdfs"; 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=Df4ZvtW1VJ32k7ot9799b04IQDgezPZJIKxu8lNrNt0=; b=oMHhjdfszSDQJOBJ+v6OOqo2Onn0nupvBm3g2EO2eZads9pKnZRC60JhYm5SirM9MiAZ hWenV9Mm+QOo1eNByvng+rYXBjsdPXC+x90WqxVvFjIqxInCaqYQwpILGNtboCGTuh+Ti7 84mQub6dELx9qcVooHkW6PZ9vFvorskZQ= Received: by filterdrecv-p3iad2-8ddf98858-xm5rk with SMTP id filterdrecv-p3iad2-8ddf98858-xm5rk-18-5EC68811-11 2020-05-21 13:54:25.414650661 +0000 UTC m=+4807854.394384064 Received: from mail.uajain.com (unknown) by ismtpd0008p1maa1.sendgrid.net (SG) with ESMTP id NWP7hY6pRuS3tH6hTbiaGQ for ; Thu, 21 May 2020 13:54:24.614 +0000 (UTC) From: Umang Jain Date: Thu, 21 May 2020 13:54:25 +0000 (UTC) Message-Id: <20200521135416.13685-4-email@uajain.com> In-Reply-To: <20200521135416.13685-1-email@uajain.com> References: <20200521135416.13685-1-email@uajain.com> Mime-Version: 1.0 X-SG-EID: 1Q40EQ7YGir8a9gjSIAdTjhngY657NMk9ckeo4dbHZDiOpywc/L3L9rFqlwE4KPcHIuSzkf5/M5SksOT1pntG7QXTTZK2cE1S7jwIaCKrb/mbbypVgSEEUHuO3QBMfxxVazHlquUhRPeV6ihAIwFbVMpYvTr2j+FAYbzl8NFIA3BUZ+j7RkJRsq7H8GO6fLsCVipybfAfgfgqpzG3Kiwlzd7FWFVmconZFxj/RWrP6G9NNqfy9lwXwqj7bAUj65Mp4aYuCEUz96n6qk5xb/nHA== To: libcamera-devel@lists.libcamera.org Subject: [libcamera-devel] [PATCH v3 3/5] 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, 21 May 2020 13:54:27 -0000 Emit 'newCameraAdded' and 'cameraRemoved' from CameraManager to enable hotplug and hot-unplug support in appplication 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. Signed-off-by: Umang Jain Reviewed-by: Laurent Pinchart --- include/libcamera/camera_manager.h | 6 ++++- src/libcamera/camera_manager.cpp | 36 +++++++++++++++++++++++++++--- src/libcamera/pipeline_handler.cpp | 2 +- 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/include/libcamera/camera_manager.h b/include/libcamera/camera_manager.h index 079f848..365e286 100644 --- a/include/libcamera/camera_manager.h +++ b/include/libcamera/camera_manager.h @@ -13,6 +13,7 @@ #include #include +#include namespace libcamera { @@ -35,13 +36,16 @@ public: std::shared_ptr get(dev_t devnum); void addCamera(std::shared_ptr camera, dev_t devnum); - 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> newCameraAdded; + 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 27064d2..bdd78f5 100644 --- a/src/libcamera/camera_manager.cpp +++ b/src/libcamera/camera_manager.cpp @@ -185,7 +185,7 @@ void CameraManager::Private::addCamera(std::shared_ptr &camera, } } - cameras_.push_back(std::move(camera)); + cameras_.push_back(camera); if (devnum) { unsigned int index = cameras_.size() - 1; @@ -375,6 +375,34 @@ std::shared_ptr CameraManager::get(dev_t devnum) return iter->second.lock(); } +/** + * \var CameraManager::newCameraAdded + * \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 @@ -394,6 +422,7 @@ void CameraManager::addCamera(std::shared_ptr camera, dev_t devnum) ASSERT(Thread::current() == p_.get()); p_->addCamera(camera, devnum); + newCameraAdded.emit(camera); } /** @@ -406,11 +435,12 @@ void CameraManager::addCamera(std::shared_ptr camera, dev_t devnum) * * \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 53aeebd..9a1e6ff 100644 --- a/src/libcamera/pipeline_handler.cpp +++ b/src/libcamera/pipeline_handler.cpp @@ -555,7 +555,7 @@ void PipelineHandler::disconnect() continue; camera->disconnect(); - manager_->removeCamera(camera.get()); + manager_->removeCamera(camera); } cameras_.clear(); From patchwork Thu May 21 13:54:26 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Umang Jain X-Patchwork-Id: 3835 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 66C55603D9 for ; Thu, 21 May 2020 15:54:27 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=uajain.com header.i=@uajain.com header.b="j/P+5ob1"; 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=YYVNSzVIGlkduleifeiQvrWjcbhYk9UxIf31QLYEb04=; b=j/P+5ob1hT3tJH3R907kkFMju1aknQOHepIqZCXXFEIo7GpfFJvdCgU4llVXN7kIkVEa 2QmEX9mQ4Oxcjcbo8ztBkkk4N34Hn+52mTdGAsjJ7UmKQpr41boUj5WUl5DiJwf346NbSu eLIWoijMtSweOUDVgYy28IjolkxO8Tmxc= Received: by filterdrecv-p3iad2-8ddf98858-cq957 with SMTP id filterdrecv-p3iad2-8ddf98858-cq957-19-5EC68811-81 2020-05-21 13:54:26.061975347 +0000 UTC m=+4884416.209782124 Received: from mail.uajain.com (unknown) by ismtpd0007p1maa1.sendgrid.net (SG) with ESMTP id 5VvgiGk5SgWFKPJ3int_xg for ; Thu, 21 May 2020 13:54:25.633 +0000 (UTC) From: Umang Jain Date: Thu, 21 May 2020 13:54:26 +0000 (UTC) Message-Id: <20200521135416.13685-5-email@uajain.com> In-Reply-To: <20200521135416.13685-1-email@uajain.com> References: <20200521135416.13685-1-email@uajain.com> Mime-Version: 1.0 X-SG-EID: 1Q40EQ7YGir8a9gjSIAdTjhngY657NMk9ckeo4dbHZDiOpywc/L3L9rFqlwE4KPcmGfnt8UlNcpPI7X/q8AWBmiCvDjcK63MedTb+DWhuglfIl0VxLKPKJ8e1utqNA6dzdMLM+G7vGPLRcZvCzrTV/R7kb3JKs5f5FGch+UpqGksv20rQzQG3iohTs+2dFc5LZTxrF8cgx4FbKV9gg2FwxLYn8ikNX7aHyNOqezsuvxM1Ofish+XnwDo2G/3E6AcUVmwUAiaN1IEbJhilnzzmQ== To: libcamera-devel@lists.libcamera.org Subject: [libcamera-devel] [PATCH v3 4/5] 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, 21 May 2020 13:54:27 -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 --- src/qcam/main_window.cpp | 83 ++++++++++++++++++++++++++++++++++++++++ src/qcam/main_window.h | 6 +++ 2 files changed, 89 insertions(+) diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp index 7de0895..ba96c27 100644 --- a/src/qcam/main_window.cpp +++ b/src/qcam/main_window.cpp @@ -49,6 +49,43 @@ public: } }; +/** + * \brief Custom QEvent to signal hotplug or unplug + */ +class HotplugEvent : public QEvent +{ +public: + enum PLUGEVENT { + HOTPLUG, + UNPLUG + }; + + HotplugEvent(std::shared_ptr camera, PLUGEVENT event) + : QEvent(type()) + { + camera_ = camera; + plugEvent_ = event; + } + + ~HotplugEvent() + { + camera_.reset(); + } + + static Type type() + { + static int type = QEvent::registerEventType(); + return static_cast(type); + } + + PLUGEVENT getHotplugEvent() { return plugEvent_; } + Camera *getCamera() { 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) @@ -71,6 +108,10 @@ MainWindow::MainWindow(CameraManager *cm, const OptionsParser::Options &options) setCentralWidget(viewfinder_); adjustSize(); + /* Hotplug/unplug support */ + cm_->newCameraAdded.connect(this, &MainWindow::addNewCamera); + cm_->cameraRemoved.connect(this, &MainWindow::removeCamera); + /* Open the camera and start capture. */ ret = openCamera(); if (ret < 0) { @@ -95,6 +136,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); @@ -525,6 +569,45 @@ void MainWindow::stopCapture() setWindowTitle(title_); } +/* ----------------------------------------------------------------------------- + * Camera hotplugging support + */ + +void MainWindow::processHotplug(HotplugEvent *e) +{ + Camera *camera = e->getCamera(); + HotplugEvent::PLUGEVENT event = e->getHotplugEvent(); + + if (event == HotplugEvent::PLUGEVENT::HOTPLUG) { + cameraCombo_->addItem(QString::fromStdString(camera->name())); + } else if (event == HotplugEvent::PLUGEVENT::UNPLUG) { + int camIndex = cameraCombo_->findText(QString::fromStdString(camera->name())); + + /* Check if the currently-streaming camera is removed. */ + if (camera == camera_.get()) { + toggleCapture(false); + cameraCombo_->setCurrentIndex(0); + } + cameraCombo_->removeItem(camIndex); + } +} + +void MainWindow::addNewCamera(std::shared_ptr camera) +{ + qInfo() << "Adding new camera:" << camera->name().c_str(); + QCoreApplication::postEvent(this, + new HotplugEvent(std::move(camera), + HotplugEvent::PLUGEVENT::HOTPLUG)); +} + +void MainWindow::removeCamera(std::shared_ptr camera) +{ + qInfo() << "Removing camera:" << camera->name().c_str(); + QCoreApplication::postEvent(this, + new HotplugEvent(std::move(camera), + HotplugEvent::PLUGEVENT::UNPLUG)); +} + /* ----------------------------------------------------------------------------- * Image Save */ diff --git a/src/qcam/main_window.h b/src/qcam/main_window.h index 59fa2d9..9108780 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 addNewCamera(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 May 21 13:54:27 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Umang Jain X-Patchwork-Id: 3836 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 A6950603D9 for ; Thu, 21 May 2020 15:54:28 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=uajain.com header.i=@uajain.com header.b="IGpZuLTC"; 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=eAp+C9bfhjTo63Nn7YEXdnItNWT1wnBN473amMBJM7Y=; b=IGpZuLTCjHNnmXkUgBQ0gUE4hokBNvwrztNJnki4QYIWPSdXJvpagMAFWYmfDDiLhKVI naHJ44QeTx+kkRD4K/HkENQsrJtz7gW950mKhrpNyaPqKkpgYXOLvMO+FHtf4mEzU/io/r /CuNLhdpIyBeQsYTZ5znOzlE2Q0REyKWw= Received: by filterdrecv-p3iad2-8ddf98858-4fqk8 with SMTP id filterdrecv-p3iad2-8ddf98858-4fqk8-19-5EC68813-26 2020-05-21 13:54:27.596622901 +0000 UTC m=+4884413.031055259 Received: from mail.uajain.com (unknown) by ismtpd0007p1maa1.sendgrid.net (SG) with ESMTP id 7RRIfSyHQZOK4rWW4y9ucg for ; Thu, 21 May 2020 13:54:27.117 +0000 (UTC) From: Umang Jain Date: Thu, 21 May 2020 13:54:27 +0000 (UTC) Message-Id: <20200521135416.13685-6-email@uajain.com> In-Reply-To: <20200521135416.13685-1-email@uajain.com> References: <20200521135416.13685-1-email@uajain.com> Mime-Version: 1.0 X-SG-EID: 1Q40EQ7YGir8a9gjSIAdTjhngY657NMk9ckeo4dbHZDiOpywc/L3L9rFqlwE4KPcDltxpLqwX5mosojZSfQDSt90SyLMoXpNcGo9OiQynrD+lJlSt65IbYy8vmLB7GCUkaLWxXssjT8bzIZQ8GazNkLMqyNmHHfWM3VAsw052SSAf/2sw5Qk0Qdn0W7ZoeVvtdnStJjuCLzG4FH5WZwXwZue1FzX6g0x4Rjrkv0+79ic0vDDoMp69U5IiwF51DTXErAGp/koldEmuEaYz1L//g== To: libcamera-devel@lists.libcamera.org Subject: [libcamera-devel] [PATCH v3 5/5] 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, 21 May 2020 13:54:30 -0000 This test checks the code-paths for camera's hotplugged and unplugged support. It is based off bind/unbind of a UVC device from sysfs. Hence, this tests required root permissions to run and should have atleast one already bound UVC device present on the system. Signed-off-by: Umang Jain --- test/hotplug-cameras.cpp | 153 +++++++++++++++++++++++++++++++++++++++ test/meson.build | 1 + 2 files changed, 154 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..72c370f --- /dev/null +++ b/test/hotplug-cameras.cpp @@ -0,0 +1,153 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020, Umang Jain + * + * hotplug-cameras.cpp - Emulate cameraAdded/cameraRemoved signals in CameraManager + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "libcamera/internal/file.h" +#include "libcamera/internal/thread.h" + +#include "test.h" + +using namespace std; +using namespace libcamera; + +class HotplugTest : public Test +{ +protected: + void cameraAddedHandler(std::shared_ptr cam) + { + cameraAddedPass_ = true; + } + + void cameraRemovedHandler(std::shared_ptr cam) + { + cameraRemovedPass_ = 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; + } + + cameraAddedPass_ = false; + cameraRemovedPass_ = false; + + cm_->newCameraAdded.connect(this, &HotplugTest::cameraAddedHandler); + cm_->cameraRemoved.connect(this, &HotplugTest::cameraRemovedHandler); + + uvc_toplevel_ = "/sys/module/uvcvideo/drivers/usb:uvcvideo/"; + + return 0; + } + + int run() + { + DIR *dir; + struct dirent *dirent, *dirent2; + std::string uvc_driver_dir; + bool uvc_driver_found = false; + + dir = opendir(uvc_toplevel_.c_str()); + /* Find a UVC device driver symlink, which we can bind/unbind */ + while ((dirent = readdir(dir)) != nullptr) { + if (dirent->d_type != DT_LNK) + continue; + + std::string child_dir = uvc_toplevel_ + dirent->d_name; + DIR *device_driver = opendir(child_dir.c_str()); + while ((dirent2 = readdir(device_driver)) != nullptr) { + if (strncmp(dirent2->d_name, "video4linux", 11) == 0) { + uvc_driver_dir = dirent->d_name; + uvc_driver_found = true; + break; + } + } + closedir(device_driver); + + if (uvc_driver_found) + break; + } + closedir(dir); + + /* If no UVC driver found, skip */ + if (!uvc_driver_found) + return TestSkip; + + /* Unbind a camera, process events */ + int fd1 = open("/sys/module/uvcvideo/drivers/usb:uvcvideo/unbind", O_WRONLY); + write(fd1, uvc_driver_dir.c_str(), uvc_driver_dir.size()); + close(fd1); + Timer timer; + timer.start(1000); + while (timer.isRunning()) + Thread::current()->eventDispatcher()->processEvents(); + + /* \todo: Fix this workaround of stopping and starting the camera-manager. + * We need to do this, so that cm_ release all references to the uvc media symlinks. + */ + cm_->stop(); + if (cm_->start()) { + std::cout << "Failed to restart camera-manager" << std::endl; + return TestFail; + } + + /* Bind the camera again, process events */ + int fd2 = open("/sys/module/uvcvideo/drivers/usb:uvcvideo/bind", O_WRONLY); + write(fd2, uvc_driver_dir.c_str(), uvc_driver_dir.size()); + close(fd2); + + timer.start(1000); + while (timer.isRunning()) + Thread::current()->eventDispatcher()->processEvents(); + + if (cameraAddedPass_ && cameraRemovedPass_) + return TestPass; + else + return TestFail; + } + + void cleanup() + { + cm_->stop(); + delete cm_; + } + +private: + CameraManager *cm_; + std::string uvc_toplevel_; + bool cameraRemovedPass_; + bool cameraAddedPass_; +}; + +TEST_REGISTER(HotplugTest) + diff --git a/test/meson.build b/test/meson.build index bd7da14..f7e27d7 100644 --- a/test/meson.build +++ b/test/meson.build @@ -31,6 +31,7 @@ internal_tests = [ ['file', 'file.cpp'], ['file-descriptor', 'file-descriptor.cpp'], ['message', 'message.cpp'], + ['hotplug-cameras', 'hotplug-cameras.cpp'], ['object', 'object.cpp'], ['object-invoke', 'object-invoke.cpp'], ['signal-threads', 'signal-threads.cpp'],