Show a patch.

GET /api/1.1/patches/520/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 520,
    "url": "https://patchwork.libcamera.org/api/1.1/patches/520/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/520/",
    "project": {
        "id": 1,
        "url": "https://patchwork.libcamera.org/api/1.1/projects/1/?format=api",
        "name": "libcamera",
        "link_name": "libcamera",
        "list_id": "libcamera_core",
        "list_email": "libcamera-devel@lists.libcamera.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": ""
    },
    "msgid": "<20190206060818.13907-6-laurent.pinchart@ideasonboard.com>",
    "date": "2019-02-06T06:07:56",
    "name": "[libcamera-devel,05/27] libcamera: v4l2_device: Implement queue/dequeue operations",
    "commit_ref": null,
    "pull_url": null,
    "state": "accepted",
    "archived": false,
    "hash": "5ea1d1fa432788ab2d2e0264133dccdcc37bb92d",
    "submitter": {
        "id": 2,
        "url": "https://patchwork.libcamera.org/api/1.1/people/2/?format=api",
        "name": "Laurent Pinchart",
        "email": "laurent.pinchart@ideasonboard.com"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/520/mbox/",
    "series": [
        {
            "id": 170,
            "url": "https://patchwork.libcamera.org/api/1.1/series/170/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=170",
            "date": "2019-02-06T06:07:51",
            "name": "Capture frames throught requests",
            "version": 1,
            "mbox": "https://patchwork.libcamera.org/series/170/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/520/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/520/checks/",
    "tags": {},
    "headers": {
        "Return-Path": "<laurent.pinchart@ideasonboard.com>",
        "Received": [
            "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 8429E61022\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed,  6 Feb 2019 07:08:24 +0100 (CET)",
            "from pendragon.ideasonboard.com (d51A4137F.access.telenet.be\n\t[81.164.19.127])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 32E2341\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed,  6 Feb 2019 07:08:24 +0100 (CET)"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1549433304;\n\tbh=d8wcCrzGR1tGUHS6G4gxu7udEN+yJlkjmCc3PwBAbV0=;\n\th=From:To:Subject:Date:In-Reply-To:References:From;\n\tb=HPPi8oDeUiAxkO5qiOvmsRUrq6JaSu3rdtLjeRGtItXebxPrml3DEmPk+7Af2TXc6\n\tCNh21yRA3k2yqXBPzj7dhWWcIxGWgDnWor9cNEZJmSvoYY9HX64ic0EmGZGO+3odHb\n\thC78DQmxHQLckvPwNlrshwO3gaeqSZbHybEui9FI=",
        "From": "Laurent Pinchart <laurent.pinchart@ideasonboard.com>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Date": "Wed,  6 Feb 2019 08:07:56 +0200",
        "Message-Id": "<20190206060818.13907-6-laurent.pinchart@ideasonboard.com>",
        "X-Mailer": "git-send-email 2.19.2",
        "In-Reply-To": "<20190206060818.13907-1-laurent.pinchart@ideasonboard.com>",
        "References": "<20190206060818.13907-1-laurent.pinchart@ideasonboard.com>",
        "MIME-Version": "1.0",
        "Content-Type": "text/plain; charset=UTF-8",
        "Content-Transfer-Encoding": "8bit",
        "Subject": "[libcamera-devel] [PATCH 05/27] libcamera: v4l2_device: Implement\n\tqueue/dequeue operations",
        "X-BeenThere": "libcamera-devel@lists.libcamera.org",
        "X-Mailman-Version": "2.1.23",
        "Precedence": "list",
        "List-Id": "<libcamera-devel.lists.libcamera.org>",
        "List-Unsubscribe": "<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>",
        "List-Archive": "<https://lists.libcamera.org/pipermail/libcamera-devel/>",
        "List-Post": "<mailto:libcamera-devel@lists.libcamera.org>",
        "List-Help": "<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>",
        "List-Subscribe": "<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>",
        "X-List-Received-Date": "Wed, 06 Feb 2019 06:08:24 -0000"
    },
    "content": "From: Kieran Bingham <kieran.bingham@ideasonboard.com>\n\nProvide queueBuffer() and dequeueBuffer() methods to interact with the\nV4L2Device.\n\nBuffers will be directly referenced from the bufferPool of the\nV4L2Device based on the index in the pool.\n\nThe V4L2Device is now opened in non-blocking mode in order to avoid\nblocking the dequeueBuffer() method. A signal is emitted when a buffer\nis ready and has been dequeued.\n\nSigned-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\nSigned-off-by: Jacopo Mondi <jacopo@jmondi.org>\nSigned-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\nSigned-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>\n---\n src/libcamera/include/v4l2_device.h |  13 +++\n src/libcamera/v4l2_device.cpp       | 129 +++++++++++++++++++++++++++-\n 2 files changed, 140 insertions(+), 2 deletions(-)",
    "diff": "diff --git a/src/libcamera/include/v4l2_device.h b/src/libcamera/include/v4l2_device.h\nindex 510b74f12d05..30a8f77d9772 100644\n--- a/src/libcamera/include/v4l2_device.h\n+++ b/src/libcamera/include/v4l2_device.h\n@@ -7,15 +7,19 @@\n #ifndef __LIBCAMERA_V4L2_DEVICE_H__\n #define __LIBCAMERA_V4L2_DEVICE_H__\n \n+#include <atomic>\n #include <string>\n #include <vector>\n \n #include <linux/videodev2.h>\n \n+#include <libcamera/signal.h>\n+\n namespace libcamera {\n \n class Buffer;\n class BufferPool;\n+class EventNotifier;\n class MediaEntity;\n \n struct V4L2Capability final : v4l2_capability {\n@@ -96,6 +100,9 @@ public:\n \tint exportBuffers(unsigned int count, BufferPool *pool);\n \tint releaseBuffers();\n \n+\tint queueBuffer(Buffer *buffer);\n+\tSignal<Buffer *> bufferReady;\n+\n private:\n \tint getFormatSingleplane(V4L2DeviceFormat *format);\n \tint setFormatSingleplane(V4L2DeviceFormat *format);\n@@ -107,6 +114,9 @@ private:\n \tint createPlane(Buffer *buffer, unsigned int plane,\n \t\t\tunsigned int length);\n \n+\tBuffer *dequeueBuffer();\n+\tvoid bufferAvailable(EventNotifier *notifier);\n+\n \tstd::string deviceNode_;\n \tint fd_;\n \tV4L2Capability caps_;\n@@ -115,6 +125,9 @@ private:\n \tenum v4l2_memory memoryType_;\n \n \tBufferPool *bufferPool_;\n+\tstd::atomic<unsigned int> queuedBuffersCount_;\n+\n+\tEventNotifier *fdEvent_;\n };\n \n } /* namespace libcamera */\ndiff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp\nindex 2d0a1cb6abbe..134a468c4236 100644\n--- a/src/libcamera/v4l2_device.cpp\n+++ b/src/libcamera/v4l2_device.cpp\n@@ -13,6 +13,7 @@\n #include <vector>\n \n #include <libcamera/buffer.h>\n+#include <libcamera/event_notifier.h>\n \n #include \"log.h\"\n #include \"media_object.h\"\n@@ -203,6 +204,10 @@ LOG_DEFINE_CATEGORY(V4L2)\n  * No API call other than open(), isOpen() and close() shall be called on an\n  * unopened device instance.\n  *\n+ * The V4L2Device class tracks queued buffers and handles buffer events. It\n+ * automatically dequeues completed buffers and emits the \\ref bufferReady\n+ * signal.\n+ *\n  * Upon destruction any device left open will be closed, and any resources\n  * released.\n  */\n@@ -212,7 +217,8 @@ LOG_DEFINE_CATEGORY(V4L2)\n  * \\param deviceNode The file-system path to the video device node\n  */\n V4L2Device::V4L2Device(const std::string &deviceNode)\n-\t: deviceNode_(deviceNode), fd_(-1), bufferPool_(nullptr)\n+\t: deviceNode_(deviceNode), fd_(-1), bufferPool_(nullptr),\n+\t  queuedBuffersCount_(0), fdEvent_(nullptr)\n {\n \t/*\n \t * We default to an MMAP based CAPTURE device, however this will be\n@@ -251,7 +257,7 @@ int V4L2Device::open()\n \t\treturn -EBUSY;\n \t}\n \n-\tret = ::open(deviceNode_.c_str(), O_RDWR);\n+\tret = ::open(deviceNode_.c_str(), O_RDWR | O_NONBLOCK);\n \tif (ret < 0) {\n \t\tret = -errno;\n \t\tLOG(V4L2, Error)\n@@ -294,6 +300,10 @@ int V4L2Device::open()\n \t\t\t    ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE\n \t\t\t    : V4L2_BUF_TYPE_VIDEO_OUTPUT;\n \n+\tfdEvent_ = new EventNotifier(fd_, EventNotifier::Read);\n+\tfdEvent_->activated.connect(this, &V4L2Device::bufferAvailable);\n+\tfdEvent_->setEnabled(false);\n+\n \treturn 0;\n }\n \n@@ -315,6 +325,7 @@ void V4L2Device::close()\n \t\treturn;\n \n \treleaseBuffers();\n+\tdelete fdEvent_;\n \n \t::close(fd_);\n \tfd_ = -1;\n@@ -631,4 +642,118 @@ int V4L2Device::releaseBuffers()\n \treturn 0;\n }\n \n+/**\n+ * \\brief Queue a buffer into the device\n+ * \\param[in] buffer The buffer to be queued\n+ *\n+ * For capture devices the \\a buffer will be filled with data by the device.\n+ * For output devices the \\a buffer shall contain valid data and will be\n+ * processed by the device. Once the device has finished processing the buffer,\n+ * it will be available for dequeue.\n+ *\n+ * \\todo Support output devices (bytesused, ...)\n+ * \\todo Support imported buffers (dmabuf fd)\n+ *\n+ * \\return 0 on success or a negative error number otherwise\n+ */\n+int V4L2Device::queueBuffer(Buffer *buffer)\n+{\n+\tstruct v4l2_buffer buf = {};\n+\tstruct v4l2_plane planes[VIDEO_MAX_PLANES] = {};\n+\tint ret;\n+\n+\tbuf.index = buffer->index();\n+\tbuf.type = bufferType_;\n+\tbuf.memory = memoryType_;\n+\n+\tif (V4L2_TYPE_IS_MULTIPLANAR(buf.type)) {\n+\t\tbuf.length = buffer->planes().size();\n+\t\tbuf.m.planes = planes;\n+\t}\n+\n+\tLOG(V4L2, Debug) << \"Queueing buffer \" << buf.index;\n+\n+\tret = ioctl(fd_, VIDIOC_QBUF, &buf);\n+\tif (ret < 0) {\n+\t\tret = -errno;\n+\t\tLOG(V4L2, Error)\n+\t\t\t<< \"Failed to queue buffer \" << buf.index << \": \"\n+\t\t\t<< strerror(-ret);\n+\t\treturn ret;\n+\t}\n+\n+\tif (queuedBuffersCount_++ == 0)\n+\t\tfdEvent_->setEnabled(true);\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * \\brief Dequeue the next available buffer from the device\n+ *\n+ * This method dequeues the next available buffer from the device. If no buffer\n+ * is available to be dequeued it will return nullptr immediately.\n+ *\n+ * \\return A pointer to the dequeued buffer on succcess, or nullptr otherwise\n+ */\n+Buffer *V4L2Device::dequeueBuffer()\n+{\n+\tstruct v4l2_buffer buf = {};\n+\tstruct v4l2_plane planes[VIDEO_MAX_PLANES] = {};\n+\tint ret;\n+\n+\tbuf.type = bufferType_;\n+\tbuf.memory = memoryType_;\n+\n+\tif (V4L2_TYPE_IS_MULTIPLANAR(buf.type)) {\n+\t\tbuf.length = VIDEO_MAX_PLANES;\n+\t\tbuf.m.planes = planes;\n+\t}\n+\n+\tret = ioctl(fd_, VIDIOC_DQBUF, &buf);\n+\tif (ret < 0) {\n+\t\tret = -errno;\n+\t\tLOG(V4L2, Error)\n+\t\t\t<< \"Failed to dequeue buffer: \" << strerror(-ret);\n+\t\treturn nullptr;\n+\t}\n+\n+\tASSERT(buf.index < bufferPool_->count());\n+\n+\tif (--queuedBuffersCount_ == 0)\n+\t\tfdEvent_->setEnabled(false);\n+\n+\treturn &bufferPool_->buffers()[buf.index];\n+}\n+\n+/**\n+ * \\brief Slot to handle completed buffer events from the V4L2 device\n+ * \\param[in] notifier The event notifier\n+ *\n+ * When this slot is called, a Buffer has become available from the device, and\n+ * will be emitted through the bufferReady Signal.\n+ *\n+ * For Capture devices the Buffer will contain valid data.\n+ * For Output devices the Buffer can be considered empty.\n+ */\n+void V4L2Device::bufferAvailable(EventNotifier *notifier)\n+{\n+\tBuffer *buffer = dequeueBuffer();\n+\tif (!buffer)\n+\t\treturn;\n+\n+\tLOG(V4L2, Debug) << \"Buffer \" << buffer->index() << \" is available\";\n+\n+\t/* Notify anyone listening to the device. */\n+\tbufferReady.emit(buffer);\n+\n+\t/* Notify anyone listening to the buffer specifically. */\n+\tbuffer->completed.emit(buffer);\n+}\n+\n+/**\n+ * \\var V4L2Device::bufferReady\n+ * \\brief A Signal emitted when a buffer completes\n+ */\n+\n } /* namespace libcamera */\n",
    "prefixes": [
        "libcamera-devel",
        "05/27"
    ]
}