Patch Detail
Show a patch.
GET /api/1.1/patches/15796/?format=api
{ "id": 15796, "url": "https://patchwork.libcamera.org/api/1.1/patches/15796/?format=api", "web_url": "https://patchwork.libcamera.org/patch/15796/", "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": "<20220505104104.70841-14-tomi.valkeinen@ideasonboard.com>", "date": "2022-05-05T10:41:04", "name": "[libcamera-devel,v7,13/13] py: implement MappedFrameBuffer", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": false, "hash": "c58e2834beb7a5ec187330bb7b61159cca820a7d", "submitter": { "id": 109, "url": "https://patchwork.libcamera.org/api/1.1/people/109/?format=api", "name": "Tomi Valkeinen", "email": "tomi.valkeinen@ideasonboard.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/15796/mbox/", "series": [ { "id": 3093, "url": "https://patchwork.libcamera.org/api/1.1/series/3093/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=3093", "date": "2022-05-05T10:40:51", "name": "Python bindings", "version": 7, "mbox": "https://patchwork.libcamera.org/series/3093/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/15796/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/15796/checks/", "tags": {}, "headers": { "Return-Path": "<libcamera-devel-bounces@lists.libcamera.org>", "X-Original-To": "parsemail@patchwork.libcamera.org", "Delivered-To": "parsemail@patchwork.libcamera.org", "Received": [ "from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id D089CC3272\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 5 May 2022 10:41:39 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 73A4E6564A;\n\tThu, 5 May 2022 12:41:39 +0200 (CEST)", "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 9A2F865664\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 5 May 2022 12:41:32 +0200 (CEST)", "from deskari.lan (91-156-85-209.elisa-laajakaista.fi\n\t[91.156.85.209])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 0A80D4A8;\n\tThu, 5 May 2022 12:41:31 +0200 (CEST)" ], "DKIM-Signature": [ "v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1651747299;\n\tbh=pmogevaeNcSjdHQ7Hgy5H641piE912B+uxhkbOw+v34=;\n\th=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:\n\tFrom;\n\tb=YnTP/T/aXaDhBGNbZWPFBUAgwsr0z5x23AsQEkRYXYnhGPdGNHTz169d43PDcl2vj\n\t1gaCrMZChuwBJMYY7d0AX/S13aTqRlIMChACoG8G1pxWciHxoga+g/Nspbydl+88ec\n\tX94UiuFgeSQl9L5LGXFkTMICGLagmIENDTRRnKjzMa7uVc2jcJjMENd8nPzVaZpYOr\n\t+WGxGd56cuRkP7hjBTTuEsUFf7F/a6794v1s2y5k0/XTIqIldArzec7UJw+g955zes\n\tckzteEx0WVI67iEM4Q5wFWEEHAvW2xabybG0jDMOXkPmc6TD0w1q9rhmpKfZiLXAw4\n\t5b+FMNuNsmo+A==", "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1651747292;\n\tbh=pmogevaeNcSjdHQ7Hgy5H641piE912B+uxhkbOw+v34=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=R47LJtFqKkaxe4CnB8vC67c8c6HTABikZ2J9EKEP+cqF/YAeBb1FrjHl740TgH4Sj\n\tgdmxtRtNxDMaMnE7Be6I62Qx4UpRWy2v5KGLPCs5dCv56ZLYMDl+DOqGZqjrLGBE8/\n\t1NQxJRm4a4lAJNwRXOqWfgKSUsBtMVoCP9xa0BTE=" ], "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"R47LJtFq\"; dkim-atps=neutral", "To": "libcamera-devel@lists.libcamera.org,\n\tDavid Plowman <david.plowman@raspberrypi.com>,\n\tKieran Bingham <kieran.bingham@ideasonboard.com>,\n\tLaurent Pinchart <laurent.pinchart@ideasonboard.com>,\n\tJacopo Mondi <jacopo@jmondi.org>", "Date": "Thu, 5 May 2022 13:41:04 +0300", "Message-Id": "<20220505104104.70841-14-tomi.valkeinen@ideasonboard.com>", "X-Mailer": "git-send-email 2.34.1", "In-Reply-To": "<20220505104104.70841-1-tomi.valkeinen@ideasonboard.com>", "References": "<20220505104104.70841-1-tomi.valkeinen@ideasonboard.com>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "Subject": "[libcamera-devel] [PATCH v7 13/13] py: implement MappedFrameBuffer", "X-BeenThere": "libcamera-devel@lists.libcamera.org", "X-Mailman-Version": "2.1.29", "Precedence": "list", "List-Id": "<libcamera-devel.lists.libcamera.org>", "List-Unsubscribe": "<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>", "List-Archive": "<https://lists.libcamera.org/pipermail/libcamera-devel/>", "List-Post": "<mailto:libcamera-devel@lists.libcamera.org>", "List-Help": "<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>", "List-Subscribe": "<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>", "From": "Tomi Valkeinen via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>", "Reply-To": "Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>", "Errors-To": "libcamera-devel-bounces@lists.libcamera.org", "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>" }, "content": "Instead of just exposing plain mmap via fb.mmap(planenum), implement a\nMappedFrameBuffer class, similar to C++'s MappedFrameBuffer.\nMappedFrameBuffer mmaps the underlying filedescriptors and provides\nPython memoryviews for each plane.\n\nAs an example, to save a Framebuffer to a file:\n\nwith fb.mmap() as mfb:\n\twith open(filename, \"wb\") as f:\n\t\tfor p in mfb.planes:\n\t\t\tf.write(p)\n\nThe objects in mfb.planes are memoryviews that cover only the plane in\nquestion.\n\nSigned-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>\n---\n src/py/cam/cam.py | 11 +++---\n src/py/cam/cam_qt.py | 6 +--\n src/py/libcamera/__init__.py | 72 +++++++++++++++++++++++++++++++++++-\n src/py/libcamera/pymain.cpp | 7 ++++\n 4 files changed, 86 insertions(+), 10 deletions(-)", "diff": "diff --git a/src/py/cam/cam.py b/src/py/cam/cam.py\nindex 4efa6459..c0ebb186 100755\n--- a/src/py/cam/cam.py\n+++ b/src/py/cam/cam.py\n@@ -329,9 +329,9 @@ def request_handler(state, ctx, req):\n \n crcs = []\n if ctx[\"opt-crc\"]:\n- with fb.mmap(0) as b:\n- crc = binascii.crc32(b)\n- crcs.append(crc)\n+ with fb.mmap() as mfb:\n+ plane_crcs = [binascii.crc32(p) for p in mfb.planes]\n+ crcs.append(plane_crcs)\n \n meta = fb.metadata\n \n@@ -347,10 +347,11 @@ def request_handler(state, ctx, req):\n print(f\"\\t{ctrl} = {val}\")\n \n if ctx[\"opt-save-frames\"]:\n- with fb.mmap(0) as b:\n+ with fb.mmap() as mfb:\n filename = \"frame-{}-{}-{}.data\".format(ctx[\"id\"], stream_name, ctx[\"reqs-completed\"])\n with open(filename, \"wb\") as f:\n- f.write(b)\n+ for p in mfb.planes:\n+ f.write(p)\n \n state[\"renderer\"].request_handler(ctx, req)\n \ndiff --git a/src/py/cam/cam_qt.py b/src/py/cam/cam_qt.py\nindex 30fb7a1d..d394987b 100644\n--- a/src/py/cam/cam_qt.py\n+++ b/src/py/cam/cam_qt.py\n@@ -324,17 +324,17 @@ class MainWindow(QtWidgets.QWidget):\n controlsLayout.addStretch()\n \n def buf_to_qpixmap(self, stream, fb):\n- with fb.mmap(0) as b:\n+ with fb.mmap() as mfb:\n cfg = stream.configuration\n w, h = cfg.size\n pitch = cfg.stride\n \n if cfg.pixelFormat == \"MJPEG\":\n- img = Image.open(BytesIO(b))\n+ img = Image.open(BytesIO(mfb.planes[0]))\n qim = ImageQt(img).copy()\n pix = QtGui.QPixmap.fromImage(qim)\n else:\n- data = np.array(b, dtype=np.uint8)\n+ data = np.array(mfb.planes[0], dtype=np.uint8)\n rgb = to_rgb(cfg.pixelFormat, cfg.size, data)\n \n if rgb is None:\ndiff --git a/src/py/libcamera/__init__.py b/src/py/libcamera/__init__.py\nindex cd7512a2..caf06af7 100644\n--- a/src/py/libcamera/__init__.py\n+++ b/src/py/libcamera/__init__.py\n@@ -1,12 +1,80 @@\n # SPDX-License-Identifier: LGPL-2.1-or-later\n # Copyright (C) 2021, Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>\n \n+from os import lseek, SEEK_END\n from ._libcamera import *\n import mmap\n \n \n-def __FrameBuffer__mmap(self, plane):\n- return mmap.mmap(self.fd(plane), self.length(plane), mmap.MAP_SHARED, mmap.PROT_READ)\n+def __FrameBuffer__mmap(self):\n+ return MappedFrameBuffer(self)\n \n \n FrameBuffer.mmap = __FrameBuffer__mmap\n+\n+\n+class MappedFrameBuffer:\n+ def __init__(self, fb):\n+ self.fb = fb\n+\n+ # Collect information about the buffers\n+\n+ bufinfos = {}\n+\n+ for i in range(fb.num_planes):\n+ fd = fb.fd(i)\n+\n+ if fd not in bufinfos:\n+ buflen = lseek(fd, 0, SEEK_END)\n+ bufinfos[fd] = {\"maplen\": 0, \"buflen\": buflen}\n+ else:\n+ buflen = bufinfos[fd][\"buflen\"]\n+\n+ if fb.offset(i) > buflen or fb.offset(i) + fb.length(i) > buflen:\n+ raise RuntimeError(f\"plane is out of buffer: buffer length={buflen}, \"\n+ f\"plane offset={fb.offset(i)}, plane length={fb.length(i)}\")\n+\n+ bufinfos[fd][\"maplen\"] = max(bufinfos[fd][\"maplen\"], fb.offset(i) + fb.length(i))\n+\n+ # mmap the buffers\n+\n+ maps = []\n+\n+ for fd, info in bufinfos.items():\n+ map = mmap.mmap(fd, info[\"maplen\"], mmap.MAP_SHARED, mmap.PROT_READ | mmap.PROT_WRITE)\n+ info[\"map\"] = map\n+ maps.append(map)\n+\n+ self.maps = tuple(maps)\n+\n+ # Create memoryviews for the planes\n+\n+ planes = []\n+\n+ for i in range(fb.num_planes):\n+ fd = fb.fd(i)\n+ info = bufinfos[fd]\n+\n+ mv = memoryview(info[\"map\"])\n+\n+ start = fb.offset(i)\n+ end = fb.offset(i) + fb.length(i)\n+\n+ mv = mv[start:end]\n+\n+ planes.append(mv)\n+\n+ self.planes = tuple(planes)\n+\n+ def __enter__(self):\n+ return self\n+\n+ def __exit__(self, exc_type, exc_value, exc_traceback):\n+ for p in self.planes:\n+ p.release()\n+\n+ for mm in self.maps:\n+ mm.close()\n+\n+ def planes(self):\n+ return self.planes\ndiff --git a/src/py/libcamera/pymain.cpp b/src/py/libcamera/pymain.cpp\nindex b9b52f6b..73d29479 100644\n--- a/src/py/libcamera/pymain.cpp\n+++ b/src/py/libcamera/pymain.cpp\n@@ -439,6 +439,9 @@ PYBIND11_MODULE(_libcamera, m)\n \t\t\treturn new FrameBuffer(v, cookie);\n \t\t}))\n \t\t.def_property_readonly(\"metadata\", &FrameBuffer::metadata, py::return_value_policy::reference_internal)\n+\t\t.def_property_readonly(\"num_planes\", [](const FrameBuffer &self) {\n+\t\t\treturn self.planes().size();\n+\t\t})\n \t\t.def(\"length\", [](FrameBuffer &self, uint32_t idx) {\n \t\t\tconst FrameBuffer::Plane &plane = self.planes()[idx];\n \t\t\treturn plane.length;\n@@ -447,6 +450,10 @@ PYBIND11_MODULE(_libcamera, m)\n \t\t\tconst FrameBuffer::Plane &plane = self.planes()[idx];\n \t\t\treturn plane.fd.get();\n \t\t})\n+\t\t.def(\"offset\", [](FrameBuffer &self, uint32_t idx) {\n+\t\t\tconst FrameBuffer::Plane &plane = self.planes()[idx];\n+\t\t\treturn plane.offset;\n+\t\t})\n \t\t.def_property(\"cookie\", &FrameBuffer::cookie, &FrameBuffer::setCookie);\n \n \tpyStream\n", "prefixes": [ "libcamera-devel", "v7", "13/13" ] }