From patchwork Mon Dec 23 07:26:15 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 2444 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id C3F30605D0 for ; Mon, 23 Dec 2019 08:26:47 +0100 (CET) Received: from neptunite.amanokami.net (173-16-160-11.client.mchsi.com [173.16.160.11]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 3D51097A; Mon, 23 Dec 2019 08:26:46 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1577086007; bh=ADhUIuHGA6FLWE/WdJXHK0Z76gzbqUuxcTwm8eRHAFo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=poQ55OJeDJB9iJ1ZaOnZ4K/ancuvvy/uH3tLv1Q9nT/7SFxOMOFqOfruY/7VpxVmj fwu/y8w/SQp+AgDA61CiVZgQW8ynk45pwryp8yUcOiBoqLCe+07NCFxTbL7/ZujGSp tOuL8lLPVSi6HtBfy6cDAzSkEjAi4DzLn1goTLko= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Mon, 23 Dec 2019 01:26:15 -0600 Message-Id: <20191223072620.13022-2-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.23.0 In-Reply-To: <20191223072620.13022-1-paul.elder@ideasonboard.com> References: <20191223072620.13022-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 1/6] libcamera: v4l2_device, v4l2_videodevice: call open system call directly 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: Mon, 23 Dec 2019 07:26:48 -0000 We are preparing to integrate the V4L2 adaptation layer, which will intercept open() calls (among others) via LD_PRELOAD. To prevent libcamera's own open() calls from being intercepted, replace them with a direct syscall. Signed-off-by: Paul Elder Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart --- Changes in v3: - change SYS_open to SYS_openat No change in v2 --- src/libcamera/v4l2_device.cpp | 3 ++- src/libcamera/v4l2_videodevice.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp index 0452f801..c13eddc8 100644 --- a/src/libcamera/v4l2_device.cpp +++ b/src/libcamera/v4l2_device.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include "log.h" @@ -75,7 +76,7 @@ int V4L2Device::open(unsigned int flags) return -EBUSY; } - int ret = ::open(deviceNode_.c_str(), flags); + int ret = syscall(SYS_openat, AT_FDCWD, deviceNode_.c_str(), flags); if (ret < 0) { ret = -errno; LOG(V4L2, Error) << "Failed to open V4L2 device: " diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp index 13e02323..50e3bc11 100644 --- a/src/libcamera/v4l2_videodevice.cpp +++ b/src/libcamera/v4l2_videodevice.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -1426,7 +1427,7 @@ int V4L2M2MDevice::open() * as the V4L2VideoDevice::open() retains a handle by duplicating the * fd passed in. */ - fd = ::open(deviceNode_.c_str(), O_RDWR | O_NONBLOCK); + fd = syscall(SYS_openat, AT_FDCWD, deviceNode_.c_str(), O_RDWR | O_NONBLOCK); if (fd < 0) { ret = -errno; LOG(V4L2, Error) From patchwork Mon Dec 23 07:26:16 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 2445 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 11D2B605D6 for ; Mon, 23 Dec 2019 08:26:49 +0100 (CET) Received: from neptunite.amanokami.net (173-16-160-11.client.mchsi.com [173.16.160.11]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id D98C5330; Mon, 23 Dec 2019 08:26:47 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1577086008; bh=s/apEtM2B5nIXIB1dh4v42bvpo2N+ga36ZE6MG2S6xM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=QE/feDWFTWVDyXyInJsGLQyi5oc9ig1fwxhZr0NCOOW91OKzutELvJyKnvp1d0Fv/ A9BVp/00q9piWWpGX1HADPoVHY3kg//EzTipxjx16WkVBC9agyMyFpaeExFGjk8yel G12JbrTmyrvpCFUAJGYH8n7OZ5kF+UdTf/PaNKSQ= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Mon, 23 Dec 2019 01:26:16 -0600 Message-Id: <20191223072620.13022-3-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.23.0 In-Reply-To: <20191223072620.13022-1-paul.elder@ideasonboard.com> References: <20191223072620.13022-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 2/6] libcamera: pipeline_handler: Add map from devnum to cameras 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: Mon, 23 Dec 2019 07:26:49 -0000 The V4L2 compatibility layer will need a way to map device numbers to libcamera Cameras. As a first step, we add a map from devnum to Cameras to PipelineHandler, as well as an overloaded registerCamera() method to add to this map. Signed-off-by: Paul Elder Reviewed-by: Jacopo Mondi --- New in v3 --- src/libcamera/include/pipeline_handler.h | 6 +++++ src/libcamera/pipeline_handler.cpp | 30 ++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/src/libcamera/include/pipeline_handler.h b/src/libcamera/include/pipeline_handler.h index f3622631..563de72a 100644 --- a/src/libcamera/include/pipeline_handler.h +++ b/src/libcamera/include/pipeline_handler.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -84,9 +85,13 @@ public: const char *name() const { return name_; } + const std::map> &camerasByDevnum() const { return camerasByDevnum_; } + protected: void registerCamera(std::shared_ptr camera, std::unique_ptr data); + void registerCamera(std::shared_ptr camera, + std::unique_ptr data, dev_t devnum); void hotplugMediaDevice(MediaDevice *media); virtual int queueRequestDevice(Camera *camera, Request *request) = 0; @@ -102,6 +107,7 @@ private: std::vector> mediaDevices_; std::vector> cameras_; std::map> cameraData_; + std::map> camerasByDevnum_; const char *name_; diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp index 5badf31c..90211f12 100644 --- a/src/libcamera/pipeline_handler.cpp +++ b/src/libcamera/pipeline_handler.cpp @@ -7,6 +7,8 @@ #include "pipeline_handler.h" +#include + #include #include #include @@ -453,6 +455,28 @@ void PipelineHandler::registerCamera(std::shared_ptr camera, manager_->addCamera(std::move(camera)); } +/** + * \brief Register a camera to the camera manager and pipeline handler + * \param[in] camera The camera to be added + * \param[in] data Pipeline-specific data for the camera + * \param[in] devnum Device number of the camera + * + * This method is called by pipeline handlers to register the cameras they + * handle with the camera manager. It associates the pipeline-specific \a data + * with the camera, for later retrieval with cameraData(). Ownership of \a data + * is transferred to the PipelineHandler. + * + * \a devnum is the device number (as returned by makedev) that the \a camera + * is to be associated with. + */ +void PipelineHandler::registerCamera(std::shared_ptr camera, + std::unique_ptr data, + dev_t devnum) +{ + registerCamera(camera, std::move(data)); + camerasByDevnum_[devnum] = camera; +} + /** * \brief Enable hotplug handling for a media device * \param[in] media The media device @@ -538,6 +562,12 @@ CameraData *PipelineHandler::cameraData(const Camera *camera) * \return The pipeline handler name */ +/** + * \fn PipelineHandler::camerasByDevnum() + * \brief Retrieve the map of devnums to cameras + * \return The map of devnums to cameras + */ + /** * \class PipelineHandlerFactory * \brief Registration of PipelineHandler classes and creation of instances From patchwork Mon Dec 23 07:26:17 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 2446 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 45F57605D0 for ; Mon, 23 Dec 2019 08:26:50 +0100 (CET) Received: from neptunite.amanokami.net (173-16-160-11.client.mchsi.com [173.16.160.11]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 35E8E330; Mon, 23 Dec 2019 08:26:49 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1577086010; bh=XjPedv3ULegMUBy6xhhY2mZExTaVM1tfjZZagdkETd8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=eHLoH71bpLxokfRZ7RvZ9BjdAwamgMJ9UHpQN1i5qxX+vpB6EfHqg2Tq8jakDa6C7 i0Ay7bHcEQRGoambVdRu7zYCv+fjRfXgfmGhzaAQFuUS2RG15/eWZVgqOtPbBDUqYS mj6eMyh/94Quxky4mGJicA/jE9G8MfCsN+6+YCwk= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Mon, 23 Dec 2019 01:26:17 -0600 Message-Id: <20191223072620.13022-4-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.23.0 In-Reply-To: <20191223072620.13022-1-paul.elder@ideasonboard.com> References: <20191223072620.13022-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 3/6] libcamera: pipeline_handler: uvcvideo: register all Cameras along with a devnum 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: Mon, 23 Dec 2019 07:26:50 -0000 Register all UVC Cameras along with its device number, to eventually allow the V4L2 compatibility layer to match against it. Signed-off-by: Paul Elder Reviewed-by: Laurent Pinchart --- New in v3 --- src/libcamera/pipeline/uvcvideo.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/libcamera/pipeline/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo.cpp index 3043366b..096c1c09 100644 --- a/src/libcamera/pipeline/uvcvideo.cpp +++ b/src/libcamera/pipeline/uvcvideo.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -294,17 +295,19 @@ bool PipelineHandlerUVC::match(DeviceEnumerator *enumerator) return false; std::unique_ptr data = utils::make_unique(this); + dev_t devnum = 0; /* Locate and initialise the camera data with the default video node. */ for (MediaEntity *entity : media->entities()) { if (entity->flags() & MEDIA_ENT_FL_DEFAULT) { if (data->init(entity)) return false; + devnum = makedev(entity->deviceMajor(), entity->deviceMinor()); break; } } - if (!data) { + if (!data || !devnum) { LOG(UVC, Error) << "Could not find a default video device"; return false; } @@ -312,7 +315,7 @@ bool PipelineHandlerUVC::match(DeviceEnumerator *enumerator) /* Create and register the camera. */ std::set streams{ &data->stream_ }; std::shared_ptr camera = Camera::create(this, media->model(), streams); - registerCamera(std::move(camera), std::move(data)); + registerCamera(std::move(camera), std::move(data), devnum); /* Enable hot-unplug notifications. */ hotplugMediaDevice(media); From patchwork Mon Dec 23 07:26:18 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 2447 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 4D2F36046D for ; Mon, 23 Dec 2019 08:26:51 +0100 (CET) Received: from neptunite.amanokami.net (173-16-160-11.client.mchsi.com [173.16.160.11]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 6E187330; Mon, 23 Dec 2019 08:26:50 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1577086011; bh=4IEUMXY74vjJC71TPRtV2Nlq+V2SOF9HiN37jWgnzk4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=OvosLaZqbCMklTBBxELeY0WF+bc93HeDN2aBDCRPg3kohvJI+cxPAbrFH48KRkLhm /xNIFvQJvC5R3kYz9aEbh1CyeHHfgW/ov8Mx4nx73lj5Tix6LjpYDBcgcF549fAveC cbrHSC34cMSrvff1B+uP28xCvytK3MAhoftsG7Qo= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Mon, 23 Dec 2019 01:26:18 -0600 Message-Id: <20191223072620.13022-5-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.23.0 In-Reply-To: <20191223072620.13022-1-paul.elder@ideasonboard.com> References: <20191223072620.13022-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 4/6] libcamera: camera_manager: allow retrieving cameras by devnum 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: Mon, 23 Dec 2019 07:26:51 -0000 Expose a method to retrieve Cameras by devnum. The map of devnum to Camera is gathered from all PipelineHandlers. This method is mainly for the V4L2 compatibility layer, in order to match V4L2 devices to libcamera Cameras. Signed-off-by: Paul Elder Reviewed-by: Jacopo Mondi --- New in v3 --- include/libcamera/camera_manager.h | 3 +++ src/libcamera/camera_manager.cpp | 24 ++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/include/libcamera/camera_manager.h b/include/libcamera/camera_manager.h index 8331898c..6ce63912 100644 --- a/include/libcamera/camera_manager.h +++ b/include/libcamera/camera_manager.h @@ -7,6 +7,7 @@ #ifndef __LIBCAMERA_CAMERA_MANAGER_H__ #define __LIBCAMERA_CAMERA_MANAGER_H__ +#include #include #include #include @@ -33,6 +34,7 @@ public: const std::vector> &cameras() const { return cameras_; } std::shared_ptr get(const std::string &name); + std::shared_ptr get(dev_t devnum); void addCamera(std::shared_ptr camera); void removeCamera(Camera *camera); @@ -46,6 +48,7 @@ private: std::unique_ptr enumerator_; std::vector> pipes_; std::vector> cameras_; + std::map> camerasByDevnum_; static const std::string version_; static CameraManager *self_; diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp index 7c6f72bb..1354df4c 100644 --- a/src/libcamera/camera_manager.cpp +++ b/src/libcamera/camera_manager.cpp @@ -121,6 +121,11 @@ int CameraManager::start() } } + for (std::shared_ptr pipe : pipes_) { + const std::map> devMap = pipe->camerasByDevnum(); + camerasByDevnum_.insert(devMap.begin(), devMap.end()); + } + /* TODO: register hot-plug callback here */ return 0; @@ -180,6 +185,25 @@ std::shared_ptr CameraManager::get(const std::string &name) return nullptr; } +/** + * \brief Get a camera based on devnum + * \param[in] devnum Device number of camera to get + * + * Before calling this function the caller is responsible for ensuring that + * the camera manager is running. + * + * \return Shared pointer to Camera object or nullptr if camera not found + */ +std::shared_ptr CameraManager::get(dev_t devnum) +{ + auto iter = camerasByDevnum_.find(devnum); + + if (iter == camerasByDevnum_.end()) + return nullptr; + + return iter->second; +} + /** * \brief Add a camera to the camera manager * \param[in] camera The camera to be added From patchwork Mon Dec 23 07:26:19 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 2448 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id E9B37605D0 for ; Mon, 23 Dec 2019 08:26:52 +0100 (CET) Received: from neptunite.amanokami.net (173-16-160-11.client.mchsi.com [173.16.160.11]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 8D5A5330; Mon, 23 Dec 2019 08:26:51 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1577086012; bh=yaZaNfhlugxX5pGH7T2hPBIW8eTMTwPJEdSMKuof/ag=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=IA54BDrMRUYntqwunGnRNJ4bEwXpqt08hsPz9MsolI0Q0RqovLrUT+3mPoY+tZZz1 TZPu9IMny0wv35iJSLF5Jh2EX36gFXdlBQutpxdVqKd+yKJCoL9w/GfZWWxP7bh7Jh V1//21mWs38cRzjKc6nKcjdGUfwCGmQPL2WBsmME= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Mon, 23 Dec 2019 01:26:19 -0600 Message-Id: <20191223072620.13022-6-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.23.0 In-Reply-To: <20191223072620.13022-1-paul.elder@ideasonboard.com> References: <20191223072620.13022-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 5/6] v4l2: v4l2_compat: Add V4L2 compatibility layer 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: Mon, 23 Dec 2019 07:26:53 -0000 Add libcamera V4L2 compatibility layer. This initial implementation supports the minimal set of V4L2 operations, which allows getting, setting, and enumerating formats, and streaming frames from a video device. Some data about the wrapped V4L2 video device are hardcoded. Add a build option named 'v4l2' and adjust the build system to selectively compile the V4L2 compatibility layer. For now we match the V4L2 device node to a libcamera camera based on a devnum that a device enumerator may optionally map to a libcamera camera. Signed-off-by: Paul Elder --- Major changes in v3: - V4L2CompatManager::openat() verify video node and match to camera via devnum instead of path - add FrameMetadata class to encapsulate portions of the libcamera Buffer that needs to be extracted to v4l2_buffer, and to prepare for the Buffer rework - V4L2CameraProxy refcount for tracking dups and closes (and open) - add V4L2CompatManager::initialized_ to deal with race of waiting on init cv after the cv is notified Less major changes in v3: - change the list of pending Reqeusts (v4l2 buffers queued before streamon) to unique pointers - V4L2Camera::updateSizeImage() -> V4L2CameraProxy::calculateSizeImage() - change V4L2CompatManager list of V4L2CameraProxy to unique_ptr, with maps of fd/mmap addr to V4L2CameraProxy * - removed cross-thread validation methods from V4L2CameraProxy (validate[Stream|Memory]Type) - removed hasPixelFormat() and hasSize() from V4L2CameraProxy - moved mmap logic out of V4L2Camera and into V4L2CameraProxy - moved nonblock logic out of V4L2Camera and into V4L2CameraProxy - add todos - oter cosmetic changes Changes in v2: - move all errno acrobatics to V4L2CameraProxy - remove all mentions of dmabuf - make V4L2CompatManager::getCamera return pointer rather than shared_ptr - match V4L2 device nodes to libcamera cameras using Camera::name() compared to /sys/dev/char/maj:min/name (only works for UVC cameras) - in V4L2CompatManager::getCameraIndex() - add -fvisibility=hidden to v4l2 compat - cache the results of V4L2CompatManager::imageSize() within V4L2Camera (where V4L2Camera is interested) - remove V4L2CompatManager::valid[fd|mmap], and where those methods were used, check V4L2CompatManager::getCamera() != nullptr instead - fixed V4L2CompatManager::drmToV4L2() mappings for DRM_FORMAT_BGR888 and DRM_FORMAT_RGB888 - other cosmetic changes --- meson_options.txt | 5 + src/meson.build | 4 + src/v4l2/meson.build | 31 ++ src/v4l2/v4l2_camera.cpp | 248 ++++++++++++++++ src/v4l2/v4l2_camera.h | 84 ++++++ src/v4l2/v4l2_camera_proxy.cpp | 480 +++++++++++++++++++++++++++++++ src/v4l2/v4l2_camera_proxy.h | 70 +++++ src/v4l2/v4l2_compat.cpp | 78 +++++ src/v4l2/v4l2_compat_manager.cpp | 379 ++++++++++++++++++++++++ src/v4l2/v4l2_compat_manager.h | 86 ++++++ 10 files changed, 1465 insertions(+) create mode 100644 src/v4l2/meson.build create mode 100644 src/v4l2/v4l2_camera.cpp create mode 100644 src/v4l2/v4l2_camera.h create mode 100644 src/v4l2/v4l2_camera_proxy.cpp create mode 100644 src/v4l2/v4l2_camera_proxy.h create mode 100644 src/v4l2/v4l2_compat.cpp create mode 100644 src/v4l2/v4l2_compat_manager.cpp create mode 100644 src/v4l2/v4l2_compat_manager.h diff --git a/meson_options.txt b/meson_options.txt index 1a328045..b06dd494 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -10,3 +10,8 @@ option('documentation', option('test', type : 'boolean', description: 'Compile and include the tests') + +option('v4l2', + type : 'boolean', + value : false, + description : 'Compile libcamera with V4L2 compatibility layer') diff --git a/src/meson.build b/src/meson.build index 67ad20aa..5adcd61f 100644 --- a/src/meson.build +++ b/src/meson.build @@ -6,3 +6,7 @@ subdir('libcamera') subdir('ipa') subdir('cam') subdir('qcam') + +if get_option('v4l2') + subdir('v4l2') +endif diff --git a/src/v4l2/meson.build b/src/v4l2/meson.build new file mode 100644 index 00000000..14ee3594 --- /dev/null +++ b/src/v4l2/meson.build @@ -0,0 +1,31 @@ +v4l2_compat_sources = files([ + 'v4l2_camera.cpp', + 'v4l2_camera_proxy.cpp', + 'v4l2_compat.cpp', + 'v4l2_compat_manager.cpp', +]) + +v4l2_compat_includes = [ + libcamera_includes, + libcamera_internal_includes, +] + +v4l2_compat_cpp_args = [ + # Meson enables large file support unconditionally, which redirect file + # operations to 64-bit versions. This results in some symbols being + # renamed, for instance open() being renamed to open64(). As the V4L2 + # adaptation wrapper needs to provide both 32-bit and 64-bit versions of + # file operations, disable transparent large file support. + '-U_FILE_OFFSET_BITS', + '-D_FILE_OFFSET_BITS=32', + '-fvisibility=hidden', +] + +v4l2_compat = shared_library('v4l2-compat', + v4l2_compat_sources, + name_prefix : '', + install : true, + link_with : libcamera, + include_directories : v4l2_compat_includes, + dependencies : libcamera_deps, + cpp_args : v4l2_compat_cpp_args) diff --git a/src/v4l2/v4l2_camera.cpp b/src/v4l2/v4l2_camera.cpp new file mode 100644 index 00000000..2d33be9f --- /dev/null +++ b/src/v4l2/v4l2_camera.cpp @@ -0,0 +1,248 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * v4l2_camera.cpp - V4L2 compatibility camera + */ + +#include "v4l2_camera.h" + +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "utils.h" +#include "v4l2_compat_manager.h" + +using namespace libcamera; + +LOG_DECLARE_CATEGORY(V4L2Compat); + +FrameMetadata::FrameMetadata(Buffer *buffer) + : index_(buffer->index()), + bytesused_(buffer->bytesused()), + timestamp_(buffer->timestamp()), + sequence_(buffer->sequence()), + status_(buffer->status()) +{ +} + +V4L2Camera::V4L2Camera(std::shared_ptr camera) + : camera_(camera), bufferCount_(0), isRunning_(false) +{ + camera_->requestCompleted.connect(this, &V4L2Camera::requestComplete); +}; + +V4L2Camera::~V4L2Camera() +{ + camera_->release(); +} + +void V4L2Camera::open(int *ret) +{ + if (camera_->acquire() < 0) { + LOG(V4L2Compat, Error) << "Failed to acquire camera"; + *ret = -EINVAL; + return; + } + + config_ = camera_->generateConfiguration({ StreamRole::Viewfinder }); + if (!config_) { + camera_->release(); + *ret = -EINVAL; + return; + } + + *ret = 0; +} + +void V4L2Camera::close(int *ret) +{ + *ret = camera_->release(); +} + +void V4L2Camera::getStreamConfig(StreamConfiguration *streamConfig) +{ + *streamConfig = config_->at(0); +} + +void V4L2Camera::requestComplete(Request *request) +{ + if (request->status() == Request::RequestCancelled) + return; + + /* We only have one stream at the moment. */ + bufferLock_.lock(); + Buffer *buffer = request->buffers().begin()->second; + std::unique_ptr fmd = + utils::make_unique(buffer); + completedBuffers_.push(std::move(fmd)); + bufferLock_.unlock(); + + bufferSema_.release(); +} + +void V4L2Camera::configure(int *ret, StreamConfiguration *streamConfigOut, + Size *size, PixelFormat pixelformat, + unsigned int bufferCount) +{ + StreamConfiguration &streamConfig = config_->at(0); + streamConfig.size.width = size->width; + streamConfig.size.height = size->height; + streamConfig.pixelFormat = pixelformat; + bufferCount_ = bufferCount; + streamConfig.bufferCount = bufferCount_; + /* \todo memoryType (interval vs external) */ + + CameraConfiguration::Status validation = config_->validate(); + if (validation == CameraConfiguration::Invalid) { + LOG(V4L2Compat, Error) << "Configuration invalid"; + *ret = -EINVAL; + return; + } + if (validation == CameraConfiguration::Adjusted) + LOG(V4L2Compat, Info) << "Configuration adjusted"; + + LOG(V4L2Compat, Info) << "Validated configuration is: " + << streamConfig.toString(); + + *ret = camera_->configure(config_.get()); + if (*ret < 0) + return; + + bufferCount_ = streamConfig.bufferCount; + + *streamConfigOut = config_->at(0); +} + +void V4L2Camera::mmap(void **ret, unsigned int index) +{ + LOG(V4L2Compat, Debug) << "Servicing MMAP"; + + Stream *stream = *camera_->streams().begin(); + *ret = stream->buffers()[index].planes()[0].mem(); +} + +void V4L2Camera::allocBuffers(int *ret, unsigned int count) +{ + LOG(V4L2Compat, Debug) << "Allocating libcamera bufs"; + + *ret = camera_->allocateBuffers(); + if (*ret == -EACCES) + *ret = -EBUSY; +} + +void V4L2Camera::freeBuffers() +{ + camera_->freeBuffers(); + bufferCount_ = 0; +} + +void V4L2Camera::streamOn(int *ret) +{ + *ret = 0; + + if (isRunning_) + return; + + *ret = camera_->start(); + if (*ret < 0) { + if (*ret == -EACCES) + *ret = -EBUSY; + return; + } + isRunning_ = true; + + for (std::unique_ptr &req : pendingRequests_) { + /* \todo What should we do if this returns -EINVAL? */ + *ret = camera_->queueRequest(req.release()); + if (*ret < 0) { + *ret = (*ret == -EACCES ? -EAGAIN : *ret); + return; + } + } + + pendingRequests_.clear(); +} + +void V4L2Camera::streamOff(int *ret) +{ + *ret = 0; + + /* \todo restore buffers to reqbufs state? */ + if (!isRunning_) + return; + + *ret = camera_->stop(); + if (*ret < 0) { + if (*ret == -EACCES) + *ret = -EBUSY; + return; + } + isRunning_ = false; +} + +void V4L2Camera::qbuf(int *ret, unsigned int index) +{ + Stream *stream = config_->at(0).stream(); + std::unique_ptr buffer = stream->createBuffer(index); + if (!buffer) { + LOG(V4L2Compat, Error) << "Can't create buffer"; + *ret = -ENOMEM; + return; + } + + std::unique_ptr request = + std::unique_ptr(camera_->createRequest()); + if (!request) { + LOG(V4L2Compat, Error) << "Can't create request"; + *ret = -ENOMEM; + return; + } + + *ret = request->addBuffer(std::move(buffer)); + if (*ret < 0) { + LOG(V4L2Compat, Error) << "Can't set buffer for request"; + *ret = -ENOMEM; + return; + } + + if (!isRunning_) { + pendingRequests_.push_back(std::move(request)); + } else { + *ret = camera_->queueRequest(request.release()); + if (*ret < 0) { + LOG(V4L2Compat, Error) << "Can't queue request"; + if (*ret == -EACCES) + *ret = -EBUSY; + return; + } + } + + *ret = 0; +} + +int V4L2Camera::dqbuf(struct v4l2_buffer *arg, bool nonblock) +{ + if (nonblock && !bufferSema_.tryAcquire()) + return -EAGAIN; + else + bufferSema_.acquire(); + + bufferLock_.lock(); + FrameMetadata *fmd = completedBuffers_.front().get(); + completedBuffers_.pop(); + bufferLock_.unlock(); + + arg->bytesused = fmd->bytesused(); + arg->field = V4L2_FIELD_NONE; + arg->timestamp.tv_sec = fmd->timestamp() / 1000000000; + arg->timestamp.tv_usec = fmd->timestamp() % 1000000; + arg->sequence = fmd->sequence(); + + return 0; +} diff --git a/src/v4l2/v4l2_camera.h b/src/v4l2/v4l2_camera.h new file mode 100644 index 00000000..13418b6b --- /dev/null +++ b/src/v4l2/v4l2_camera.h @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * v4l2_camera.h - V4L2 compatibility camera + */ + +#ifndef __V4L2_CAMERA_H__ +#define __V4L2_CAMERA_H__ + +#include +#include +#include +#include + +#include +#include +#include "semaphore.h" + +using namespace libcamera; + +class FrameMetadata +{ +public: + FrameMetadata(Buffer *buffer); + + int index() const { return index_; } + + unsigned int bytesused() const { return bytesused_; } + uint64_t timestamp() const { return timestamp_; } + unsigned int sequence() const { return sequence_; } + + Buffer::Status status() const { return status_; } + +private: + int index_; + + unsigned int bytesused_; + uint64_t timestamp_; + unsigned int sequence_; + + Buffer::Status status_; +}; + +class V4L2Camera : public Object +{ +public: + V4L2Camera(std::shared_ptr camera); + ~V4L2Camera(); + + void open(int *ret); + void close(int *ret); + void getStreamConfig(StreamConfiguration *streamConfig); + + void mmap(void **ret, unsigned int index); + + void configure(int *ret, StreamConfiguration *streamConfigOut, Size *size, + PixelFormat pixelformat, unsigned int bufferCount); + + void allocBuffers(int *ret, unsigned int count); + void freeBuffers(); + void streamOn(int *ret); + void streamOff(int *ret); + + void qbuf(int *ret, unsigned int index); + int dqbuf(struct v4l2_buffer *arg, bool nonblock); + +private: + void requestComplete(Request *request); + + std::shared_ptr camera_; + std::unique_ptr config_; + + unsigned int bufferCount_; + bool isRunning_; + + Semaphore bufferSema_; + std::mutex bufferLock_; + + std::deque> pendingRequests_; + std::queue> completedBuffers_; +}; + +#endif /* __V4L2_CAMERA_H__ */ diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp new file mode 100644 index 00000000..b0acd477 --- /dev/null +++ b/src/v4l2/v4l2_camera_proxy.cpp @@ -0,0 +1,480 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * v4l2_camera_proxy.cpp - Proxy to V4L2 compatibility camera + */ + +#include "v4l2_camera_proxy.h" + +#include +#include +#include +#include + +#include +#include + +#include "log.h" +#include "utils.h" +#include "v4l2_camera.h" +#include "v4l2_compat_manager.h" + +#define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c)) + +using namespace libcamera; + +LOG_DECLARE_CATEGORY(V4L2Compat); + +V4L2CameraProxy::V4L2CameraProxy(unsigned int index, + std::shared_ptr camera) + : index_(index), bufferCount_(0), currentBuf_(0), + vcam_(utils::make_unique(camera)) +{ + querycap(camera); +} + +int V4L2CameraProxy::open(bool nonBlocking) +{ + LOG(V4L2Compat, Debug) << "Servicing open"; + + int ret; + vcam_->invokeMethod(&V4L2Camera::open, ConnectionTypeBlocking, + &ret); + if (ret < 0) { + errno = -ret; + return -1; + } + + nonBlocking_ = nonBlocking; + + vcam_->invokeMethod(&V4L2Camera::getStreamConfig, + ConnectionTypeBlocking, &streamConfig_); + setFmtFromConfig(streamConfig_); + sizeimage_ = calculateSizeImage(streamConfig_); + + refcount_++; + + return 0; +} + +void V4L2CameraProxy::dup() +{ + refcount_++; +} + +int V4L2CameraProxy::close() +{ + LOG(V4L2Compat, Debug) << "Servicing close"; + + if (refcount_ > 1) { + refcount_--; + return 0; + } + + int ret; + vcam_->invokeMethod(&V4L2Camera::close, ConnectionTypeBlocking, &ret); + if (ret < 0) { + errno = EIO; + return -1; + } + + return ret; +} + +void *V4L2CameraProxy::mmap(void *addr, size_t length, int prot, int flags, + off_t offset) +{ + LOG(V4L2Compat, Debug) << "Servicing mmap"; + + if (prot != (PROT_READ | PROT_WRITE)) { + errno = ENOTSUP; + return MAP_FAILED; + } + + unsigned int index = offset / sizeimage_; + if (index * sizeimage_ != offset || length != sizeimage_) { + errno = EINVAL; + return MAP_FAILED; + } + + void *val; + vcam_->invokeMethod(&V4L2Camera::mmap, ConnectionTypeBlocking, + &val, index); + return val; +} + +int V4L2CameraProxy::munmap(void *addr, size_t length) +{ + LOG(V4L2Compat, Debug) << "Servicing munmap"; + + if (length != sizeimage_) { + errno = EINVAL; + return -1; + } + + return 0; +} + +/* \todo getDeviceCaps? getMemoryCaps? */ + +bool V4L2CameraProxy::validateStreamType(uint32_t type) +{ + return (type == V4L2_BUF_TYPE_VIDEO_CAPTURE); +} + +bool V4L2CameraProxy::validateMemoryType(uint32_t memory) +{ + return (memory == V4L2_MEMORY_MMAP); +} + +void V4L2CameraProxy::setFmtFromConfig(StreamConfiguration &streamConfig) +{ + curV4L2Format_.fmt.pix.width = streamConfig.size.width; + curV4L2Format_.fmt.pix.height = streamConfig.size.height; + curV4L2Format_.fmt.pix.pixelformat = + V4L2CompatManager::drmToV4L2(streamConfig.pixelFormat); + curV4L2Format_.fmt.pix.field = V4L2_FIELD_NONE; + curV4L2Format_.fmt.pix.bytesperline = + V4L2CompatManager::bplMultiplier( + curV4L2Format_.fmt.pix.pixelformat) * + curV4L2Format_.fmt.pix.width; + curV4L2Format_.fmt.pix.sizeimage = + V4L2CompatManager::imageSize(curV4L2Format_.fmt.pix.pixelformat, + curV4L2Format_.fmt.pix.width, + curV4L2Format_.fmt.pix.height); + curV4L2Format_.fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; +} + +unsigned int V4L2CameraProxy::calculateSizeImage(StreamConfiguration &streamConfig) +{ + return V4L2CompatManager::imageSize( + V4L2CompatManager::drmToV4L2(streamConfig.pixelFormat), + streamConfig.size.width, + streamConfig.size.height); +} + +void V4L2CameraProxy::querycap(std::shared_ptr camera) +{ + std::string driver = "libcamera"; + std::string bus_info = driver + ":" + std::to_string(index_); + + memcpy(capabilities_.driver, driver.c_str(), + sizeof(capabilities_.driver)); + memcpy(capabilities_.card, camera->name().c_str(), + sizeof(capabilities_.card)); + memcpy(capabilities_.bus_info, bus_info.c_str(), + sizeof(capabilities_.bus_info)); + /* \todo Put this is header/config somewhere. */ + capabilities_.version = KERNEL_VERSION(5, 2, 0); + capabilities_.device_caps = V4L2_CAP_VIDEO_CAPTURE; + capabilities_.capabilities = + capabilities_.device_caps | V4L2_CAP_DEVICE_CAPS; + memset(capabilities_.reserved, 0, sizeof(capabilities_.reserved)); +} + +int V4L2CameraProxy::vidioc_querycap(struct v4l2_capability *arg) +{ + LOG(V4L2Compat, Debug) << "Servicing vidioc_querycap"; + + memcpy(arg, &capabilities_, sizeof(*arg)); + + return 0; +} + +int V4L2CameraProxy::vidioc_enum_fmt(struct v4l2_fmtdesc *arg) +{ + LOG(V4L2Compat, Debug) << "Servicing vidioc_enum_fmt"; + + if (!validateStreamType(arg->type) || + arg->index > streamConfig_.formats().pixelformats().size()) + return -EINVAL; + + /* \todo Add map from format to description. */ + memcpy(arg->description, "asdf", 5); + arg->pixelformat = V4L2CompatManager::drmToV4L2( + streamConfig_.formats().pixelformats()[arg->index]); + + return 0; +} + +int V4L2CameraProxy::vidioc_g_fmt(struct v4l2_format *arg) +{ + LOG(V4L2Compat, Debug) << "Servicing vidioc_g_fmt"; + + if (!validateStreamType(arg->type)) + return -EINVAL; + + arg->fmt.pix.width = curV4L2Format_.fmt.pix.width; + arg->fmt.pix.height = curV4L2Format_.fmt.pix.height; + arg->fmt.pix.pixelformat = curV4L2Format_.fmt.pix.pixelformat; + arg->fmt.pix.field = curV4L2Format_.fmt.pix.field; + arg->fmt.pix.bytesperline = curV4L2Format_.fmt.pix.bytesperline; + arg->fmt.pix.sizeimage = curV4L2Format_.fmt.pix.sizeimage; + arg->fmt.pix.colorspace = curV4L2Format_.fmt.pix.colorspace; + + return 0; +} + +int V4L2CameraProxy::vidioc_s_fmt(struct v4l2_format *arg) +{ + LOG(V4L2Compat, Debug) << "Servicing vidioc_s_fmt"; + + int ret = vidioc_try_fmt(arg); + if (ret < 0) + return ret; + + vcam_->invokeMethod(&V4L2Camera::configure, ConnectionTypeBlocking, &ret, + &streamConfig_, + new Size(arg->fmt.pix.width, arg->fmt.pix.height), + V4L2CompatManager::v4l2ToDrm(arg->fmt.pix.pixelformat), + bufferCount_); + if (ret < 0) + return -EINVAL; + + sizeimage_ = calculateSizeImage(streamConfig_); + if (sizeimage_ == 0) + return -EINVAL; + + setFmtFromConfig(streamConfig_); + + return 0; +} + +int V4L2CameraProxy::vidioc_try_fmt(struct v4l2_format *arg) +{ + LOG(V4L2Compat, Debug) << "Servicing vidioc_try_fmt"; + if (!validateStreamType(arg->type)) + return -EINVAL; + + unsigned int format = arg->fmt.pix.pixelformat; + const std::vector &formats = + streamConfig_.formats().pixelformats(); + if (std::find(formats.begin(), formats.end(), format) == formats.end()) + format = streamConfig_.formats().pixelformats()[0]; + + Size size(arg->fmt.pix.width, arg->fmt.pix.height); + const std::vector &sizes = streamConfig_.formats().sizes(format); + if (std::find(sizes.begin(), sizes.end(), size) == sizes.end()) + size = streamConfig_.formats().sizes(format)[0]; + + arg->fmt.pix.width = size.width; + arg->fmt.pix.height = size.height; + arg->fmt.pix.pixelformat = format; + arg->fmt.pix.field = V4L2_FIELD_NONE; + arg->fmt.pix.bytesperline = + V4L2CompatManager::bplMultiplier( + V4L2CompatManager::drmToV4L2(format)) * + arg->fmt.pix.width; + arg->fmt.pix.sizeimage = + V4L2CompatManager::imageSize( + V4L2CompatManager::drmToV4L2(format), + arg->fmt.pix.width, arg->fmt.pix.height); + arg->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; + + return 0; +} + +int V4L2CameraProxy::vidioc_reqbufs(struct v4l2_requestbuffers *arg) +{ + int ret; + + LOG(V4L2Compat, Debug) << "Servicing vidioc_reqbufs"; + if (!validateStreamType(arg->type) || + !validateMemoryType(arg->memory)) + return -EINVAL; + + LOG(V4L2Compat, Debug) << arg->count << " bufs requested "; + + arg->capabilities = V4L2_BUF_CAP_SUPPORTS_MMAP; + + if (arg->count == 0) { + LOG(V4L2Compat, Debug) << "Freeing libcamera bufs"; + vcam_->invokeMethod(&V4L2Camera::streamOff, + ConnectionTypeBlocking, &ret); + if (ret < 0) { + LOG(V4L2Compat, Error) << "Failed to stop stream"; + return ret; + } + vcam_->invokeMethod(&V4L2Camera::freeBuffers, + ConnectionTypeBlocking); + bufferCount_ = 0; + return 0; + } + + vcam_->invokeMethod(&V4L2Camera::configure, ConnectionTypeBlocking, &ret, + &streamConfig_, + new Size(curV4L2Format_.fmt.pix.width, + curV4L2Format_.fmt.pix.height), + V4L2CompatManager::v4l2ToDrm(curV4L2Format_.fmt.pix.pixelformat), + arg->count); + if (ret < 0) + return -EINVAL; + + sizeimage_ = calculateSizeImage(streamConfig_); + if (sizeimage_ == 0) + return -EINVAL; + + setFmtFromConfig(streamConfig_); + + arg->count = streamConfig_.bufferCount; + bufferCount_ = arg->count; + + if (arg->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + vcam_->invokeMethod(&V4L2Camera::allocBuffers, + ConnectionTypeBlocking, &ret, arg->count); + if (ret < 0) { + arg->count = 0; + return ret == -EACCES ? -EBUSY : ret; + } + + LOG(V4L2Compat, Debug) << "Allocated " << arg->count << " buffers"; + + return 0; +} + +int V4L2CameraProxy::vidioc_querybuf(struct v4l2_buffer *arg) +{ + LOG(V4L2Compat, Debug) << "Servicing vidioc_querybuf"; + Stream *stream = streamConfig_.stream(); + + if (!validateStreamType(arg->type) || + arg->index >= stream->buffers().size()) + return -EINVAL; + + unsigned int index = arg->index; + memset(arg, 0, sizeof(*arg)); + arg->index = index; + arg->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + arg->length = curV4L2Format_.fmt.pix.sizeimage; + arg->memory = V4L2_MEMORY_MMAP; + arg->m.offset = arg->index * curV4L2Format_.fmt.pix.sizeimage; + + return 0; +} + +int V4L2CameraProxy::vidioc_qbuf(struct v4l2_buffer *arg) +{ + LOG(V4L2Compat, Debug) << "Servicing vidioc_qbuf, index = " + << arg->index; + + Stream *stream = streamConfig_.stream(); + + if (!validateStreamType(arg->type) || + !validateMemoryType(arg->memory) || + arg->index >= stream->buffers().size()) + return -EINVAL; + + int ret; + vcam_->invokeMethod(&V4L2Camera::qbuf, ConnectionTypeBlocking, + &ret, arg->index); + if (ret < 0) + return ret; + + arg->flags |= V4L2_BUF_FLAG_QUEUED; + arg->flags |= V4L2_BUF_FLAG_MAPPED; + arg->flags &= ~V4L2_BUF_FLAG_DONE; + + return ret; +} + +int V4L2CameraProxy::vidioc_dqbuf(struct v4l2_buffer *arg) +{ + LOG(V4L2Compat, Debug) << "Servicing vidioc_dqbuf"; + + if (!validateStreamType(arg->type) || + !validateMemoryType(arg->memory)) + return -EINVAL; + + arg->index = currentBuf_; + currentBuf_ = (currentBuf_ + 1) % bufferCount_; + + int ret = vcam_->dqbuf(arg, nonBlocking_); + if (ret < 0) + return ret; + + arg->flags &= ~V4L2_BUF_FLAG_QUEUED; + arg->flags |= V4L2_BUF_FLAG_DONE; + + arg->length = sizeimage_; + + return ret; +} + +int V4L2CameraProxy::vidioc_streamon(int *arg) +{ + LOG(V4L2Compat, Debug) << "Servicing vidioc_streamon"; + + if (!validateStreamType(*arg)) + return -EINVAL; + + int ret; + vcam_->invokeMethod(&V4L2Camera::streamOn, + ConnectionTypeBlocking, &ret); + return ret; +} + +int V4L2CameraProxy::vidioc_streamoff(int *arg) +{ + LOG(V4L2Compat, Debug) << "Servicing vidioc_streamoff"; + + if (!validateStreamType(*arg)) + return -EINVAL; + + int ret; + vcam_->invokeMethod(&V4L2Camera::streamOff, + ConnectionTypeBlocking, &ret); + return ret; +} + +int V4L2CameraProxy::ioctl(unsigned long request, void *arg) +{ + int ret; + switch (request) { + case VIDIOC_QUERYCAP: + ret = vidioc_querycap(static_cast(arg)); + break; + case VIDIOC_ENUM_FMT: + ret = vidioc_enum_fmt(static_cast(arg)); + break; + case VIDIOC_G_FMT: + ret = vidioc_g_fmt(static_cast(arg)); + break; + case VIDIOC_S_FMT: + ret = vidioc_s_fmt(static_cast(arg)); + break; + case VIDIOC_TRY_FMT: + ret = vidioc_try_fmt(static_cast(arg)); + break; + case VIDIOC_REQBUFS: + ret = vidioc_reqbufs(static_cast(arg)); + break; + case VIDIOC_QUERYBUF: + ret = vidioc_querybuf(static_cast(arg)); + break; + case VIDIOC_QBUF: + ret = vidioc_qbuf(static_cast(arg)); + break; + case VIDIOC_DQBUF: + ret = vidioc_dqbuf(static_cast(arg)); + break; + case VIDIOC_STREAMON: + ret = vidioc_streamon(static_cast(arg)); + break; + case VIDIOC_STREAMOFF: + ret = vidioc_streamoff(static_cast(arg)); + break; + default: + ret = -ENOTTY; + break; + } + + if (ret < 0) { + errno = -ret; + return -1; + } + + return ret; +} diff --git a/src/v4l2/v4l2_camera_proxy.h b/src/v4l2/v4l2_camera_proxy.h new file mode 100644 index 00000000..51fdbe19 --- /dev/null +++ b/src/v4l2/v4l2_camera_proxy.h @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * v4l2_camera_proxy.h - Proxy to V4L2 compatibility camera + */ + +#ifndef __V4L2_CAMERA_PROXY_H__ +#define __V4L2_CAMERA_PROXY_H__ + +#include +#include +#include + +#include + +#include "v4l2_camera.h" + +using namespace libcamera; + +class V4L2CameraProxy +{ +public: + V4L2CameraProxy(unsigned int index, std::shared_ptr camera); + + int open(bool nonBlocking); + void dup(); + int close(); + void *mmap(void *addr, size_t length, int prot, int flags, + off_t offset); + int munmap(void *addr, size_t length); + + int ioctl(unsigned long request, void *arg); + + unsigned int refcount() { return refcount_; } + +private: + bool validateStreamType(uint32_t type); + bool validateMemoryType(uint32_t memory); + void setFmtFromConfig(StreamConfiguration &streamConfig); + unsigned int calculateSizeImage(StreamConfiguration &streamConfig); + void querycap(std::shared_ptr camera); + + int vidioc_querycap(struct v4l2_capability *arg); + int vidioc_enum_fmt(struct v4l2_fmtdesc *arg); + int vidioc_g_fmt(struct v4l2_format *arg); + int vidioc_s_fmt(struct v4l2_format *arg); + int vidioc_try_fmt(struct v4l2_format *arg); + int vidioc_reqbufs(struct v4l2_requestbuffers *arg); + int vidioc_querybuf(struct v4l2_buffer *arg); + int vidioc_qbuf(struct v4l2_buffer *arg); + int vidioc_dqbuf(struct v4l2_buffer *arg); + int vidioc_streamon(int *arg); + int vidioc_streamoff(int *arg); + + unsigned int refcount_; + unsigned int index_; + bool nonBlocking_; + + struct v4l2_format curV4L2Format_; + StreamConfiguration streamConfig_; + struct v4l2_capability capabilities_; + unsigned int bufferCount_; + unsigned int currentBuf_; + unsigned int sizeimage_; + + std::unique_ptr vcam_; +}; + +#endif /* __V4L2_CAMERA_PROXY_H__ */ diff --git a/src/v4l2/v4l2_compat.cpp b/src/v4l2/v4l2_compat.cpp new file mode 100644 index 00000000..a6a7aed2 --- /dev/null +++ b/src/v4l2/v4l2_compat.cpp @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * v4l2_compat.cpp - V4L2 compatibility layer + */ + +#include "v4l2_compat_manager.h" + +#include +#include +#include +#include +#include +#include + +#define LIBCAMERA_PUBLIC __attribute__((visibility("default"))) + +using namespace libcamera; + +#define extract_va_arg(type, arg, last) \ +{ \ + va_list ap; \ + va_start(ap, last); \ + arg = va_arg(ap, type); \ + va_end(ap); \ +} + +extern "C" { + +LIBCAMERA_PUBLIC int open(const char *path, int oflag, ...) +{ + mode_t mode = 0; + if (oflag & O_CREAT || oflag & O_TMPFILE) + extract_va_arg(mode_t, mode, oflag); + + return V4L2CompatManager::instance()->openat(AT_FDCWD, path, oflag, mode); +} + +LIBCAMERA_PUBLIC int openat(int dirfd, const char *path, int oflag, ...) +{ + mode_t mode = 0; + if (oflag & O_CREAT || oflag & O_TMPFILE) + extract_va_arg(mode_t, mode, oflag); + + return V4L2CompatManager::instance()->openat(dirfd, path, oflag, mode); +} + +LIBCAMERA_PUBLIC int dup(int oldfd) +{ + return V4L2CompatManager::instance()->dup(oldfd); +} + +LIBCAMERA_PUBLIC int close(int fd) +{ + return V4L2CompatManager::instance()->close(fd); +} + +LIBCAMERA_PUBLIC void *mmap(void *addr, size_t length, int prot, int flags, + int fd, off_t offset) +{ + return V4L2CompatManager::instance()->mmap(addr, length, prot, flags, fd, offset); +} + +LIBCAMERA_PUBLIC int munmap(void *addr, size_t length) +{ + return V4L2CompatManager::instance()->munmap(addr, length); +} + +LIBCAMERA_PUBLIC int ioctl(int fd, unsigned long request, ...) +{ + void *arg; + extract_va_arg(void *, arg, request); + + return V4L2CompatManager::instance()->ioctl(fd, request, arg); +} + +} diff --git a/src/v4l2/v4l2_compat_manager.cpp b/src/v4l2/v4l2_compat_manager.cpp new file mode 100644 index 00000000..bfe76e82 --- /dev/null +++ b/src/v4l2/v4l2_compat_manager.cpp @@ -0,0 +1,379 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * v4l2_compat_manager.cpp - V4L2 compatibility manager + */ + +#include "v4l2_compat_manager.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "log.h" + +using namespace libcamera; + +LOG_DEFINE_CATEGORY(V4L2Compat) + +V4L2CompatManager::V4L2CompatManager() + : cm_(nullptr), initialized_(false) +{ + openat_func_ = (openat_func_t)dlsym(RTLD_NEXT, "openat"); + dup_func_ = (dup_func_t )dlsym(RTLD_NEXT, "dup"); + close_func_ = (close_func_t )dlsym(RTLD_NEXT, "close"); + ioctl_func_ = (ioctl_func_t )dlsym(RTLD_NEXT, "ioctl"); + mmap_func_ = (mmap_func_t )dlsym(RTLD_NEXT, "mmap"); + munmap_func_ = (munmap_func_t)dlsym(RTLD_NEXT, "munmap"); +} + +V4L2CompatManager::~V4L2CompatManager() +{ + devices_.clear(); + mmaps_.clear(); + proxies_.clear(); + + if (isRunning()) { + exit(0); + /* \todo Wait with a timeout, just in case. */ + wait(); + } +} + +int V4L2CompatManager::init() +{ + start(); + + MutexLocker locker(mutex_); + cv_.wait(locker, []{ return V4L2CompatManager::instance()->initialized_; }); + + return 0; +} + +void V4L2CompatManager::run() +{ + cm_ = new CameraManager(); + + int ret = cm_->start(); + if (ret) { + LOG(V4L2Compat, Error) << "Failed to start camera manager: " + << strerror(-ret); + return; + } + + LOG(V4L2Compat, Debug) << "Started camera manager"; + + /* + * For each Camera registered in the system, a V4L2CameraProxy gets + * created here to wrap a camera device. + */ + unsigned int index = 0; + for (auto &camera : cm_->cameras()) { + V4L2CameraProxy *proxy = new V4L2CameraProxy(index, camera); + proxies_.emplace_back(proxy); + ++index; + } + + /* + * libcamera has been initialized. Unlock the init() caller as we're + * now ready to handle calls from the framework. + */ + mutex_.lock(); + initialized_ = true; + mutex_.unlock(); + cv_.notify_one(); + + /* Now start processing events and messages. */ + exec(); + + cm_->stop(); + delete cm_; + cm_ = nullptr; +} + +V4L2CompatManager *V4L2CompatManager::instance() +{ + static V4L2CompatManager instance; + return &instance; +} + +V4L2CameraProxy *V4L2CompatManager::getCamera(int fd) +{ + auto device = devices_.find(fd); + if (device == devices_.end()) + return nullptr; + + return device->second; +} + +int V4L2CompatManager::getCameraIndex(int fd) +{ + struct stat statbuf; + fstat(fd, &statbuf); + unsigned int devMajor = major(statbuf.st_rdev); + unsigned int devMinor = minor(statbuf.st_rdev); + + std::shared_ptr target = cm_->get(makedev(devMajor, devMinor)); + + unsigned int index = 0; + for (auto &camera : cm_->cameras()) { + if (camera == target) + break; + ++index; + } + + if (index >= cm_->cameras().size()) + return -1; + + return index; +} + +int V4L2CompatManager::openat(int dirfd, const char *path, int oflag, mode_t mode) +{ + int fd = openat_func_(dirfd, path, oflag, mode); + if (fd < 0) + return fd; + + struct stat statbuf; + fstat(fd, &statbuf); + if (major(statbuf.st_rdev) != 81) + return fd; + + if (!isRunning()) + init(); + + int ret = getCameraIndex(fd); + if (ret < 0) { + LOG(V4L2Compat, Info) << "No camera found for " << path; + return fd; + } + + close_func_(fd); + + unsigned int camera_index = static_cast(ret); + + V4L2CameraProxy *proxy = proxies_[camera_index].get(); + ret = proxy->open(oflag & O_NONBLOCK); + if (ret < 0) + return ret; + + int efd = eventfd(0, (oflag & O_CLOEXEC) | (oflag & O_NONBLOCK)); + if (efd < 0) + return efd; + + devices_.emplace(efd, proxy); + + return efd; +} + +int V4L2CompatManager::dup(int oldfd) +{ + int newfd = dup_func_(oldfd); + if (getCamera(oldfd)) { + devices_[oldfd]->dup(); + devices_[newfd] = devices_[oldfd]; + } + + return newfd; +} + +int V4L2CompatManager::close(int fd) +{ + V4L2CameraProxy *proxy = getCamera(fd); + if (proxy) { + int ret = proxy->close(); + devices_.erase(fd); + return ret; + } + + return close_func_(fd); +} + +void *V4L2CompatManager::mmap(void *addr, size_t length, int prot, int flags, + int fd, off_t offset) +{ + V4L2CameraProxy *proxy = getCamera(fd); + if (!proxy) + return mmap_func_(addr, length, prot, flags, fd, offset); + + void *map = proxy->mmap(addr, length, prot, flags, offset); + if (map == MAP_FAILED) + return map; + + mmaps_[map] = proxy; + return map; +} + +int V4L2CompatManager::munmap(void *addr, size_t length) +{ + auto device = mmaps_.find(addr); + if (device == mmaps_.end()) + return munmap_func_(addr, length); + + V4L2CameraProxy *proxy = device->second; + + int ret = proxy->munmap(addr, length); + if (ret < 0) + return ret; + + mmaps_.erase(device); + + return 0; +} + +int V4L2CompatManager::ioctl(int fd, unsigned long request, void *arg) +{ + V4L2CameraProxy *proxy = getCamera(fd); + if (!proxy) + return ioctl_func_(fd, request, arg); + + return proxy->ioctl(request, arg); +} + +/* \todo make libcamera export these */ +int V4L2CompatManager::bplMultiplier(unsigned int format) +{ + switch (format) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + case V4L2_PIX_FMT_NV24: + case V4L2_PIX_FMT_NV42: + return 1; + case V4L2_PIX_FMT_BGR24: + case V4L2_PIX_FMT_RGB24: + return 3; + case V4L2_PIX_FMT_ARGB32: + return 4; + case V4L2_PIX_FMT_VYUY: + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_YUYV: + return 2; + default: + return 0; + }; +} + +int V4L2CompatManager::imageSize(unsigned int format, + unsigned int width, unsigned int height) +{ + switch (format) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + return width * height + width * height / 2; + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + return width * height * 2; + case V4L2_PIX_FMT_NV24: + case V4L2_PIX_FMT_NV42: + return width * height * 3; + case V4L2_PIX_FMT_BGR24: + case V4L2_PIX_FMT_RGB24: + return width * height * 3; + case V4L2_PIX_FMT_ARGB32: + return width * height * 4; + case V4L2_PIX_FMT_VYUY: + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_YUYV: + return width * height * 2; + default: + return 0; + }; +} + +unsigned int V4L2CompatManager::v4l2ToDrm(unsigned int pixelformat) +{ + switch (pixelformat) { + /* RGB formats. */ + case V4L2_PIX_FMT_RGB24: + return DRM_FORMAT_BGR888; + case V4L2_PIX_FMT_BGR24: + return DRM_FORMAT_RGB888; + case V4L2_PIX_FMT_ARGB32: + return DRM_FORMAT_BGRA8888; + + /* YUV packed formats. */ + case V4L2_PIX_FMT_YUYV: + return DRM_FORMAT_YUYV; + case V4L2_PIX_FMT_YVYU: + return DRM_FORMAT_YVYU; + case V4L2_PIX_FMT_UYVY: + return DRM_FORMAT_UYVY; + case V4L2_PIX_FMT_VYUY: + return DRM_FORMAT_VYUY; + + /* YUY planar formats. */ + case V4L2_PIX_FMT_NV16: + return DRM_FORMAT_NV16; + case V4L2_PIX_FMT_NV61: + return DRM_FORMAT_NV61; + case V4L2_PIX_FMT_NV12: + return DRM_FORMAT_NV12; + case V4L2_PIX_FMT_NV21: + return DRM_FORMAT_NV21; + case V4L2_PIX_FMT_NV24: + return DRM_FORMAT_NV24; + case V4L2_PIX_FMT_NV42: + return DRM_FORMAT_NV42; + default: + return pixelformat; + }; +} + +unsigned int V4L2CompatManager::drmToV4L2(unsigned int pixelformat) +{ + switch (pixelformat) { + /* RGB formats. */ + case DRM_FORMAT_BGR888: + return V4L2_PIX_FMT_RGB24; + case DRM_FORMAT_RGB888: + return V4L2_PIX_FMT_BGR24; + case DRM_FORMAT_BGRA8888: + return V4L2_PIX_FMT_ARGB32; + + /* YUV packed formats. */ + case DRM_FORMAT_YUYV: + return V4L2_PIX_FMT_YUYV; + case DRM_FORMAT_YVYU: + return V4L2_PIX_FMT_YVYU; + case DRM_FORMAT_UYVY: + return V4L2_PIX_FMT_UYVY; + case DRM_FORMAT_VYUY: + return V4L2_PIX_FMT_VYUY; + + /* YUY planar formats. */ + case DRM_FORMAT_NV16: + return V4L2_PIX_FMT_NV16; + case DRM_FORMAT_NV61: + return V4L2_PIX_FMT_NV61; + case DRM_FORMAT_NV12: + return V4L2_PIX_FMT_NV12; + case DRM_FORMAT_NV21: + return V4L2_PIX_FMT_NV21; + case DRM_FORMAT_NV24: + return V4L2_PIX_FMT_NV24; + case DRM_FORMAT_NV42: + return V4L2_PIX_FMT_NV42; + default: + return pixelformat; + } +} diff --git a/src/v4l2/v4l2_compat_manager.h b/src/v4l2/v4l2_compat_manager.h new file mode 100644 index 00000000..b8ae6efe --- /dev/null +++ b/src/v4l2/v4l2_compat_manager.h @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * v4l2_compat_manager.h - V4L2 compatibility manager + */ + +#ifndef __V4L2_COMPAT_MANAGER_H__ +#define __V4L2_COMPAT_MANAGER_H__ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "thread.h" +#include "v4l2_camera_proxy.h" + +using namespace libcamera; + +class V4L2CompatManager : public Thread +{ +public: + static V4L2CompatManager *instance(); + + int init(); + + V4L2CameraProxy *getCamera(int fd); + V4L2CameraProxy *getCamera(void *addr); + + int openat(int dirfd, const char *path, int oflag, mode_t mode); + + int dup(int oldfd); + int close(int fd); + void *mmap(void *addr, size_t length, int prot, int flags, + int fd, off_t offset); + int munmap(void *addr, size_t length); + int ioctl(int fd, unsigned long request, void *arg); + + static int bplMultiplier(unsigned int format); + static int imageSize(unsigned int format, unsigned int width, + unsigned int height); + + static unsigned int v4l2ToDrm(unsigned int pixelformat); + static unsigned int drmToV4L2(unsigned int pixelformat); + +private: + V4L2CompatManager(); + ~V4L2CompatManager(); + + void run() override; + int getCameraIndex(int fd); + + typedef int (*openat_func_t)(int dirfd, const char *path, int oflag, ...); + typedef int (*dup_func_t)(int oldfd); + typedef int (*close_func_t)(int fd); + typedef int (*ioctl_func_t)(int fd, unsigned long request, ...); + typedef void *(*mmap_func_t)(void *addr, size_t length, int prot, + int flags, int fd, off_t offset); + typedef int (*munmap_func_t)(void *addr, size_t length); + + openat_func_t openat_func_; + dup_func_t dup_func_; + close_func_t close_func_; + ioctl_func_t ioctl_func_; + mmap_func_t mmap_func_; + munmap_func_t munmap_func_; + + CameraManager *cm_; + + std::mutex mutex_; + std::condition_variable cv_; + bool initialized_; + + std::vector> proxies_; + std::map devices_; + std::map mmaps_; +}; + +#endif /* __V4L2_COMPAT_MANAGER_H__ */ From patchwork Mon Dec 23 07:26:20 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 2449 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 2C03F605D0 for ; Mon, 23 Dec 2019 08:26:54 +0100 (CET) Received: from neptunite.amanokami.net (173-16-160-11.client.mchsi.com [173.16.160.11]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 1C0DE330; Mon, 23 Dec 2019 08:26:52 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1577086013; bh=IXYGWPXGfQwEnLjlRtnoBfAV1KHfvNGI+ukb5UFk0EU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=s8Ul/Kv62iEDGNjpd58l2yckf60TLARF4VnsKRYSqDxN6hsrSXNZZnDqUurT/F28i cw14NUkZ9REv4YsjWLiy95GSeynhX7ER3PbXBYoPVYISCvb8FwPDoLSwkEflli14pL NgulvUjey2qT9Fib4Sn0jdN0i+VEBEYW1Tr3cjmI= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Mon, 23 Dec 2019 01:26:20 -0600 Message-Id: <20191223072620.13022-7-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.23.0 In-Reply-To: <20191223072620.13022-1-paul.elder@ideasonboard.com> References: <20191223072620.13022-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 6/6] v4l2: v4l2-compat: add buffer state tracking to V4L2CameraProxy 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: Mon, 23 Dec 2019 07:26:54 -0000 Add a way for V4L2CameraProxy to cache the state of all the completed buffers as v4l2_buffers. This reduces the number of cross-thread calls, since the newly added V4L2CameraProxy::updateBuffers(), which goes through V4L2Camera::completedBuffers(), does not need to be called across the thread boundary. Also move the v4l2_buffer flag-setting logic to V4L2CameraProxy. Signed-off-by: Paul Elder --- New in v3 --- src/v4l2/v4l2_camera.cpp | 36 ++++++--------- src/v4l2/v4l2_camera.h | 7 ++- src/v4l2/v4l2_camera_proxy.cpp | 80 +++++++++++++++++++++++++++------- src/v4l2/v4l2_camera_proxy.h | 3 ++ 4 files changed, 84 insertions(+), 42 deletions(-) diff --git a/src/v4l2/v4l2_camera.cpp b/src/v4l2/v4l2_camera.cpp index 2d33be9f..403e24f6 100644 --- a/src/v4l2/v4l2_camera.cpp +++ b/src/v4l2/v4l2_camera.cpp @@ -70,6 +70,19 @@ void V4L2Camera::getStreamConfig(StreamConfiguration *streamConfig) *streamConfig = config_->at(0); } +std::vector V4L2Camera::completedBuffers() +{ + std::vector v; + + bufferLock_.lock(); + for (std::unique_ptr &fmd : completedBuffers_) + v.push_back(*fmd.get()); + completedBuffers_.clear(); + bufferLock_.unlock(); + + return v; +} + void V4L2Camera::requestComplete(Request *request) { if (request->status() == Request::RequestCancelled) @@ -80,7 +93,7 @@ void V4L2Camera::requestComplete(Request *request) Buffer *buffer = request->buffers().begin()->second; std::unique_ptr fmd = utils::make_unique(buffer); - completedBuffers_.push(std::move(fmd)); + completedBuffers_.push_back(std::move(fmd)); bufferLock_.unlock(); bufferSema_.release(); @@ -225,24 +238,3 @@ void V4L2Camera::qbuf(int *ret, unsigned int index) *ret = 0; } - -int V4L2Camera::dqbuf(struct v4l2_buffer *arg, bool nonblock) -{ - if (nonblock && !bufferSema_.tryAcquire()) - return -EAGAIN; - else - bufferSema_.acquire(); - - bufferLock_.lock(); - FrameMetadata *fmd = completedBuffers_.front().get(); - completedBuffers_.pop(); - bufferLock_.unlock(); - - arg->bytesused = fmd->bytesused(); - arg->field = V4L2_FIELD_NONE; - arg->timestamp.tv_sec = fmd->timestamp() / 1000000000; - arg->timestamp.tv_usec = fmd->timestamp() % 1000000; - arg->sequence = fmd->sequence(); - - return 0; -} diff --git a/src/v4l2/v4l2_camera.h b/src/v4l2/v4l2_camera.h index 13418b6b..43ab8d02 100644 --- a/src/v4l2/v4l2_camera.h +++ b/src/v4l2/v4l2_camera.h @@ -11,7 +11,6 @@ #include #include #include -#include #include #include @@ -51,6 +50,7 @@ public: void open(int *ret); void close(int *ret); void getStreamConfig(StreamConfiguration *streamConfig); + std::vector completedBuffers(); void mmap(void **ret, unsigned int index); @@ -63,8 +63,8 @@ public: void streamOff(int *ret); void qbuf(int *ret, unsigned int index); - int dqbuf(struct v4l2_buffer *arg, bool nonblock); + Semaphore bufferSema_; private: void requestComplete(Request *request); @@ -74,11 +74,10 @@ private: unsigned int bufferCount_; bool isRunning_; - Semaphore bufferSema_; std::mutex bufferLock_; std::deque> pendingRequests_; - std::queue> completedBuffers_; + std::deque> completedBuffers_; }; #endif /* __V4L2_CAMERA_H__ */ diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp index b0acd477..4e303500 100644 --- a/src/v4l2/v4l2_camera_proxy.cpp +++ b/src/v4l2/v4l2_camera_proxy.cpp @@ -101,6 +101,9 @@ void *V4L2CameraProxy::mmap(void *addr, size_t length, int prot, int flags, void *val; vcam_->invokeMethod(&V4L2Camera::mmap, ConnectionTypeBlocking, &val, index); + + buffers_[index].flags |= V4L2_BUF_FLAG_MAPPED; + return val; } @@ -173,6 +176,35 @@ void V4L2CameraProxy::querycap(std::shared_ptr camera) memset(capabilities_.reserved, 0, sizeof(capabilities_.reserved)); } +void V4L2CameraProxy::updateBuffers() +{ + std::vector completedBuffers = vcam_->completedBuffers(); + for (FrameMetadata &fmd : completedBuffers) { + /* \todo is this index valid if the buffer status != success? */ + struct v4l2_buffer &buf = buffers_[fmd.index()]; + + switch (fmd.status()) { + case Buffer::Status::BufferSuccess: + buf.index = fmd.index(); + buf.bytesused = fmd.bytesused(); + buf.field = V4L2_FIELD_NONE; + buf.timestamp.tv_sec = fmd.timestamp() / 1000000000; + buf.timestamp.tv_usec = fmd.timestamp() % 1000000; + buf.sequence = fmd.sequence(); + + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.length = curV4L2Format_.fmt.pix.sizeimage; + buf.memory = V4L2_MEMORY_MMAP; + buf.m.offset = buf.index * curV4L2Format_.fmt.pix.sizeimage; + break; + case Buffer::Status::BufferError: + buf.flags |= V4L2_BUF_FLAG_ERROR; + default: + break; + } + } +} + int V4L2CameraProxy::vidioc_querycap(struct v4l2_capability *arg) { LOG(V4L2Compat, Debug) << "Servicing vidioc_querycap"; @@ -344,13 +376,21 @@ int V4L2CameraProxy::vidioc_querybuf(struct v4l2_buffer *arg) arg->index >= stream->buffers().size()) return -EINVAL; - unsigned int index = arg->index; - memset(arg, 0, sizeof(*arg)); - arg->index = index; - arg->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - arg->length = curV4L2Format_.fmt.pix.sizeimage; - arg->memory = V4L2_MEMORY_MMAP; - arg->m.offset = arg->index * curV4L2Format_.fmt.pix.sizeimage; + /* \todo make updateBuffers() get only one buffer? */ + updateBuffers(); + + if (buffers_.size() <= arg->index) { + struct v4l2_buffer buf; + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.length = curV4L2Format_.fmt.pix.sizeimage; + buf.memory = V4L2_MEMORY_MMAP; + buf.m.offset = arg->index * curV4L2Format_.fmt.pix.sizeimage; + + buffers_.resize(arg->index + 1); + buffers_[arg->index] = buf; + } + + memcpy(arg, &buffers_[arg->index], sizeof(*arg)); return 0; } @@ -388,19 +428,23 @@ int V4L2CameraProxy::vidioc_dqbuf(struct v4l2_buffer *arg) !validateMemoryType(arg->memory)) return -EINVAL; - arg->index = currentBuf_; - currentBuf_ = (currentBuf_ + 1) % bufferCount_; + if (nonBlocking_ && !vcam_->bufferSema_.tryAcquire()) + return -EAGAIN; + else + vcam_->bufferSema_.acquire(); - int ret = vcam_->dqbuf(arg, nonBlocking_); - if (ret < 0) - return ret; + updateBuffers(); - arg->flags &= ~V4L2_BUF_FLAG_QUEUED; - arg->flags |= V4L2_BUF_FLAG_DONE; + memcpy(arg, &buffers_[arg->index], sizeof(*arg)); - arg->length = sizeimage_; + struct v4l2_buffer &buf = buffers_[arg->index]; + arg->index = currentBuf_; + currentBuf_ = (currentBuf_ + 1) % bufferCount_; + buf.flags &= ~V4L2_BUF_FLAG_QUEUED; + buf.flags |= V4L2_BUF_FLAG_DONE; + buf.length = sizeimage_; - return ret; + return 0; } int V4L2CameraProxy::vidioc_streamon(int *arg) @@ -426,6 +470,10 @@ int V4L2CameraProxy::vidioc_streamoff(int *arg) int ret; vcam_->invokeMethod(&V4L2Camera::streamOff, ConnectionTypeBlocking, &ret); + + for (struct v4l2_buffer &buf : buffers_) + buf.flags &= ~V4L2_BUF_FLAG_QUEUED; + return ret; } diff --git a/src/v4l2/v4l2_camera_proxy.h b/src/v4l2/v4l2_camera_proxy.h index 51fdbe19..19688717 100644 --- a/src/v4l2/v4l2_camera_proxy.h +++ b/src/v4l2/v4l2_camera_proxy.h @@ -40,6 +40,7 @@ private: void setFmtFromConfig(StreamConfiguration &streamConfig); unsigned int calculateSizeImage(StreamConfiguration &streamConfig); void querycap(std::shared_ptr camera); + void updateBuffers(); int vidioc_querycap(struct v4l2_capability *arg); int vidioc_enum_fmt(struct v4l2_fmtdesc *arg); @@ -64,6 +65,8 @@ private: unsigned int currentBuf_; unsigned int sizeimage_; + std::vector buffers_; + std::unique_ptr vcam_; };