[{"id":23165,"web_url":"https://patchwork.libcamera.org/comment/23165/","msgid":"<Yo+jArIL1TobIxcf@pendragon.ideasonboard.com>","date":"2022-05-26T15:55:46","subject":"Re: [libcamera-devel] [PATCH v2 10/19] py: examples: Add\n\tsimple-capture.py","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Tomi,\n\nThank you for the patch.\n\nOn Tue, May 24, 2022 at 02:46:01PM +0300, Tomi Valkeinen wrote:\n> Add an example to showcase the more-or-less minimal capture case.\n\nNiiiice :-) This is a really good start. I'd like more comments, but\nthat can be done on top.\n\n> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>\n> ---\n>  src/py/examples/simple-capture.py | 153 ++++++++++++++++++++++++++++++\n>  1 file changed, 153 insertions(+)\n>  create mode 100755 src/py/examples/simple-capture.py\n> \n> diff --git a/src/py/examples/simple-capture.py b/src/py/examples/simple-capture.py\n> new file mode 100755\n> index 00000000..45df9e7a\n> --- /dev/null\n> +++ b/src/py/examples/simple-capture.py\n> @@ -0,0 +1,153 @@\n> +#!/usr/bin/env python3\n> +\n> +# SPDX-License-Identifier: BSD-3-Clause\n> +# Copyright (C) 2022, Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>\n> +\n> +# A simple minimal capture example showing:\n> +# - How to setup the camera\n> +# - Capture frames in a blocking manner\n> +# - Memory map the frames\n> +# - How to stop the camera\n> +\n> +import argparse\n> +import binascii\n> +import libcamera as libcam\n> +import libcamera.utils\n> +import sys\n> +\n> +\n> +def main():\n> +    parser = argparse.ArgumentParser()\n> +    parser.add_argument('-c', '--camera', type=str, default='1',\n> +                        help='Camera index number (starting from 1) or part of the name')\n> +    parser.add_argument('-f', '--format', type=str, help='Pixel format')\n> +    parser.add_argument('-s', '--size', type=str, help='Size (\"WxH\")')\n> +    args = parser.parse_args()\n> +\n> +    cm = libcam.CameraManager.singleton()\n> +\n> +    try:\n> +        if args.camera.isnumeric():\n> +            cam_idx = int(args.camera)\n> +            cam = next((cam for i, cam in enumerate(cm.cameras) if i + 1 == cam_idx))\n> +        else:\n> +            cam = next((cam for cam in cm.cameras if args.camera in cam.id))\n> +    except Exception:\n> +        print(f'Failed to find camera \"{args.camera}\"')\n> +        return -1\n> +\n> +    # Acquire the camera for our use\n> +\n> +    ret = cam.acquire()\n> +    assert ret == 0\n> +\n> +    # Configure the camera\n> +\n> +    camconfig = cam.generate_configuration([libcam.StreamRole.StillCapture])\n\nI'm going to bikeshed a bit as this will be used as a starting point by\nmany people, sorry.\n\nAs the Python coding style uses snake_case, should this be cam_config ?\nThe next variable would be stream_config.\n\nI'd also use the ViewFinder role to match the C++ simple-cam.\n\n> +\n> +    streamconfig = camconfig.at(0)\n> +\n> +    if args.format:\n> +        fmt = libcam.PixelFormat(args.format)\n> +        streamconfig.pixel_format = fmt\n> +\n> +    if args.size:\n> +        w, h = [int(v) for v in args.size.split('x')]\n> +        streamconfig.size = libcam.Size(w, h)\n> +\n> +    ret = cam.configure(camconfig)\n> +    assert ret == 0\n> +\n> +    stream = streamconfig.stream\n> +\n> +    # Allocate the buffers for capture\n> +\n> +    allocator = libcam.FrameBufferAllocator(cam)\n> +    ret = allocator.allocate(stream)\n> +    assert ret > 0\n> +\n> +    num_bufs = len(allocator.buffers(stream))\n> +\n> +    print(f'Capturing {num_bufs} frames with {streamconfig}')\n\nThis can be done on top, but I'd like to capture more frames (100 for\ninstance), to show how to requeue requests.\n\n> +\n> +    # Create the requests and assign a buffer for each request\n> +\n> +    reqs = []\n> +    for i in range(num_bufs):\n> +        # Use the buffer index as the cookie\n> +        req = cam.create_request(i)\n> +\n> +        buffer = allocator.buffers(stream)[i]\n> +        ret = req.add_buffer(stream, buffer)\n> +        assert ret == 0\n> +\n> +        reqs.append(req)\n> +\n> +    # Start the camera\n> +\n> +    ret = cam.start()\n> +    assert ret == 0\n> +\n> +    # Queue the requests to the camera\n> +\n> +    for req in reqs:\n> +        ret = cam.queue_request(req)\n> +        assert ret == 0\n> +\n> +    # Wait until the requests are finished\n> +\n> +    reqs = []\n> +\n> +    while True:\n> +        # cm.read_event() blocks until there is an event\n> +        cm.read_event()\n> +\n> +        # Get all the ready requests\n> +        ready_reqs = cm.get_ready_requests()\n\nIntegrating a simple event loop (maybe based on the Python asyncio\nmodule ?) would be good here.\n\n> +\n> +        reqs += ready_reqs\n> +\n> +        if len(reqs) >= num_bufs:\n> +            break\n> +\n> +    # Process the captured frames\n> +\n> +    for i, req in enumerate(reqs):\n> +        assert i == req.cookie\n> +\n> +        buffers = req.buffers\n> +\n> +        # A ready Request could contain multiple buffers if multiple streams\n> +        # were being used. Here we know we only have a single stream,\n> +        # and we use next(iter()) to get the first and only buffer.\n> +\n> +        assert len(buffers) == 1\n> +\n> +        stream, fb = next(iter(buffers.items()))\n> +\n> +        # Use MappedFrameBuffer to access the pixel data with CPU. We calculate\n> +        # the crc for each plane.\n> +\n> +        with libcamera.utils.MappedFrameBuffer(fb) as mfb:\n> +            crcs = [binascii.crc32(p) for p in mfb.planes]\n\nMapping buffers is expensive, this should be done before starting the\ncamera.\n\n> +\n> +        meta = fb.metadata\n> +\n> +        print('seq {}, bytes {}, CRCs {}'\n> +              .format(meta.sequence, meta.bytesused, crcs))\n> +\n> +    # Stop the camera\n> +\n> +    ret = cam.stop()\n> +    assert ret == 0\n> +\n> +    # Release the camera\n> +\n> +    ret = cam.release()\n> +    assert ret == 0\n> +\n> +    return 0\n> +\n> +\n> +if __name__ == '__main__':\n> +    sys.exit(main())","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 0764DBD161\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 26 May 2022 15:55:56 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 07944611BF;\n\tThu, 26 May 2022 17:55:55 +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 9D0EF60DB0\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 26 May 2022 17:55:53 +0200 (CEST)","from pendragon.ideasonboard.com (unknown [37.120.212.14])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 9B83032A;\n\tThu, 26 May 2022 17:55:52 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1653580555;\n\tbh=Wtd+w8cwbNZfrGSnHTn7Y1GgmJgVYZD7vujkQ9Ogero=;\n\th=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=F1AP3Nc9aV/pCSciK31d6yuqQ57IKuKyNv9H18OUicszf4qeCdWOD45pChzXmf88j\n\tbu8rwiHsWW4lJrQgcNIEZVGCUAO+uNoXgvPK1d7J7w4vqydJe1x6CkmutlJstRZ9Vr\n\tonK3Rg14p45UDDOGVGzJQwYzn2IopI52Ya62gtiAOHLSh/tG0lPEva5ECaBsBChrq8\n\tCz50jzq+0vkyQFG8C2y4ZXkoBmO0B5v7J+lJ+9NGXEg/US2GSkSLXqIW1k4oMJUEPK\n\tJi84OmxqryyKk13bYefbJeNDOd12CK0v6Xr4SnCP9APi7mKYuT/eV40rkuKj6QJTPr\n\tDykSWdFYPYeWw==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1653580552;\n\tbh=Wtd+w8cwbNZfrGSnHTn7Y1GgmJgVYZD7vujkQ9Ogero=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=Q4tcKivPZOBq+U8YcAUyQ9z5YvT9wSKnF4THlUgqD2XBAILOeiXYm24pDzcDrit0P\n\t84sf530trkwM7Vho6TQ4q4q9eSt5fw2bnMCMbfdPesUhpwDRaNPpCP8YxBkxs2nkeO\n\ttY5s1kj6kmBnr2UUv8GZJd1ePXunnezjtgW1zP2s="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"Q4tcKivP\"; dkim-atps=neutral","Date":"Thu, 26 May 2022 18:55:46 +0300","To":"Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>","Message-ID":"<Yo+jArIL1TobIxcf@pendragon.ideasonboard.com>","References":"<20220524114610.41848-1-tomi.valkeinen@ideasonboard.com>\n\t<20220524114610.41848-11-tomi.valkeinen@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<20220524114610.41848-11-tomi.valkeinen@ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH v2 10/19] py: examples: Add\n\tsimple-capture.py","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":"Laurent Pinchart via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":23174,"web_url":"https://patchwork.libcamera.org/comment/23174/","msgid":"<d937aee9-24cf-b599-f23f-7fe2500559c3@ideasonboard.com>","date":"2022-05-27T06:08:37","subject":"Re: [libcamera-devel] [PATCH v2 10/19] py: examples: Add\n\tsimple-capture.py","submitter":{"id":109,"url":"https://patchwork.libcamera.org/api/people/109/","name":"Tomi Valkeinen","email":"tomi.valkeinen@ideasonboard.com"},"content":"On 26/05/2022 18:55, Laurent Pinchart wrote:\n> Hi Tomi,\n> \n> Thank you for the patch.\n> \n> On Tue, May 24, 2022 at 02:46:01PM +0300, Tomi Valkeinen wrote:\n>> Add an example to showcase the more-or-less minimal capture case.\n> \n> Niiiice :-) This is a really good start. I'd like more comments, but\n> that can be done on top.\n> \n>> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>\n>> ---\n>>   src/py/examples/simple-capture.py | 153 ++++++++++++++++++++++++++++++\n>>   1 file changed, 153 insertions(+)\n>>   create mode 100755 src/py/examples/simple-capture.py\n>>\n>> diff --git a/src/py/examples/simple-capture.py b/src/py/examples/simple-capture.py\n>> new file mode 100755\n>> index 00000000..45df9e7a\n>> --- /dev/null\n>> +++ b/src/py/examples/simple-capture.py\n>> @@ -0,0 +1,153 @@\n>> +#!/usr/bin/env python3\n>> +\n>> +# SPDX-License-Identifier: BSD-3-Clause\n>> +# Copyright (C) 2022, Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>\n>> +\n>> +# A simple minimal capture example showing:\n>> +# - How to setup the camera\n>> +# - Capture frames in a blocking manner\n>> +# - Memory map the frames\n>> +# - How to stop the camera\n>> +\n>> +import argparse\n>> +import binascii\n>> +import libcamera as libcam\n>> +import libcamera.utils\n>> +import sys\n>> +\n>> +\n>> +def main():\n>> +    parser = argparse.ArgumentParser()\n>> +    parser.add_argument('-c', '--camera', type=str, default='1',\n>> +                        help='Camera index number (starting from 1) or part of the name')\n>> +    parser.add_argument('-f', '--format', type=str, help='Pixel format')\n>> +    parser.add_argument('-s', '--size', type=str, help='Size (\"WxH\")')\n>> +    args = parser.parse_args()\n>> +\n>> +    cm = libcam.CameraManager.singleton()\n>> +\n>> +    try:\n>> +        if args.camera.isnumeric():\n>> +            cam_idx = int(args.camera)\n>> +            cam = next((cam for i, cam in enumerate(cm.cameras) if i + 1 == cam_idx))\n>> +        else:\n>> +            cam = next((cam for cam in cm.cameras if args.camera in cam.id))\n>> +    except Exception:\n>> +        print(f'Failed to find camera \"{args.camera}\"')\n>> +        return -1\n>> +\n>> +    # Acquire the camera for our use\n>> +\n>> +    ret = cam.acquire()\n>> +    assert ret == 0\n>> +\n>> +    # Configure the camera\n>> +\n>> +    camconfig = cam.generate_configuration([libcam.StreamRole.StillCapture])\n> \n> I'm going to bikeshed a bit as this will be used as a starting point by\n> many people, sorry.\n> \n> As the Python coding style uses snake_case, should this be cam_config ?\n> The next variable would be stream_config.\n> \n> I'd also use the ViewFinder role to match the C++ simple-cam.\n\nSounds good to me.\n\n>> +\n>> +    streamconfig = camconfig.at(0)\n>> +\n>> +    if args.format:\n>> +        fmt = libcam.PixelFormat(args.format)\n>> +        streamconfig.pixel_format = fmt\n>> +\n>> +    if args.size:\n>> +        w, h = [int(v) for v in args.size.split('x')]\n>> +        streamconfig.size = libcam.Size(w, h)\n>> +\n>> +    ret = cam.configure(camconfig)\n>> +    assert ret == 0\n>> +\n>> +    stream = streamconfig.stream\n>> +\n>> +    # Allocate the buffers for capture\n>> +\n>> +    allocator = libcam.FrameBufferAllocator(cam)\n>> +    ret = allocator.allocate(stream)\n>> +    assert ret > 0\n>> +\n>> +    num_bufs = len(allocator.buffers(stream))\n>> +\n>> +    print(f'Capturing {num_bufs} frames with {streamconfig}')\n> \n> This can be done on top, but I'd like to capture more frames (100 for\n> instance), to show how to requeue requests.\n> \n>> +\n>> +    # Create the requests and assign a buffer for each request\n>> +\n>> +    reqs = []\n>> +    for i in range(num_bufs):\n>> +        # Use the buffer index as the cookie\n>> +        req = cam.create_request(i)\n>> +\n>> +        buffer = allocator.buffers(stream)[i]\n>> +        ret = req.add_buffer(stream, buffer)\n>> +        assert ret == 0\n>> +\n>> +        reqs.append(req)\n>> +\n>> +    # Start the camera\n>> +\n>> +    ret = cam.start()\n>> +    assert ret == 0\n>> +\n>> +    # Queue the requests to the camera\n>> +\n>> +    for req in reqs:\n>> +        ret = cam.queue_request(req)\n>> +        assert ret == 0\n>> +\n>> +    # Wait until the requests are finished\n>> +\n>> +    reqs = []\n>> +\n>> +    while True:\n>> +        # cm.read_event() blocks until there is an event\n>> +        cm.read_event()\n>> +\n>> +        # Get all the ready requests\n>> +        ready_reqs = cm.get_ready_requests()\n> \n> Integrating a simple event loop (maybe based on the Python asyncio\n> module ?) would be good here.\n\nLet's discuss these in the thread for the following patch.\n\n>> +\n>> +        reqs += ready_reqs\n>> +\n>> +        if len(reqs) >= num_bufs:\n>> +            break\n>> +\n>> +    # Process the captured frames\n>> +\n>> +    for i, req in enumerate(reqs):\n>> +        assert i == req.cookie\n>> +\n>> +        buffers = req.buffers\n>> +\n>> +        # A ready Request could contain multiple buffers if multiple streams\n>> +        # were being used. Here we know we only have a single stream,\n>> +        # and we use next(iter()) to get the first and only buffer.\n>> +\n>> +        assert len(buffers) == 1\n>> +\n>> +        stream, fb = next(iter(buffers.items()))\n>> +\n>> +        # Use MappedFrameBuffer to access the pixel data with CPU. We calculate\n>> +        # the crc for each plane.\n>> +\n>> +        with libcamera.utils.MappedFrameBuffer(fb) as mfb:\n>> +            crcs = [binascii.crc32(p) for p in mfb.planes]\n> \n> Mapping buffers is expensive, this should be done before starting the\n> camera.\n\nHmm... How do we invalidate the caches if we keep the buffers mapped?\n\n  Tomi","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 D15ECBD160\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 27 May 2022 06:08:43 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 26C31633A0;\n\tFri, 27 May 2022 08:08:43 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 945C461FB6\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 27 May 2022 08:08:41 +0200 (CEST)","from [192.168.1.111] (91-156-85-209.elisa-laajakaista.fi\n\t[91.156.85.209])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id AF56A31A;\n\tFri, 27 May 2022 08:08:40 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1653631723;\n\tbh=6A8h1kCs5GpJTic1MWO0iW54Y+IvKSLGJxi/KbQgbUE=;\n\th=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=uPepWkmXGhSBduzhd+J6uM4QJnQ2w62GfMzxDPExY6j7Sr3ikQDhx7mOkJ/ZOUUPA\n\tOfW0LOxTiK/cp5/EOkZKeKth+b7BdzDQonM8gBeSr/z/rH6LXRcfVPUIxQ95l6JrhK\n\tmYHaK1PgRa1WhaNJpIE7n0T+svWJjp12KmcrAZJtanB41sFFYdgssKDqAABN3ovnBS\n\t1suvZhcy/zQGqTLLkNe8o7pxm14hxNEOfzPgDDscujTLS8eOs+7zfrjX1S9PlYIvSA\n\t8ma7QwSVu7+xywveXxyN+wjndIlW7BYdRkKu4/+9HOrdkF33XgRFlgWoRSX9c3n9AT\n\tDn7zL1wQqCTtA==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1653631721;\n\tbh=6A8h1kCs5GpJTic1MWO0iW54Y+IvKSLGJxi/KbQgbUE=;\n\th=Date:Subject:To:Cc:References:From:In-Reply-To:From;\n\tb=awkd6xpsXmFcCFA38IB5vQmBodJR9x0VdRk7gwlIdrcuvT9/rF5WVaf5tgk2GhpLF\n\tTVqm4dT5Oyuh9UQSnOmJpS02PxLLmjVhgcXwRDoxtz0yepNo6g39cbfLC91lCm9DW+\n\tDfPjj4fXqOu9BqNLufk32GzAzaHo6U1cpKUE9pkk="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"awkd6xps\"; dkim-atps=neutral","Message-ID":"<d937aee9-24cf-b599-f23f-7fe2500559c3@ideasonboard.com>","Date":"Fri, 27 May 2022 09:08:37 +0300","MIME-Version":"1.0","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101\n\tThunderbird/91.9.1","Content-Language":"en-US","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","References":"<20220524114610.41848-1-tomi.valkeinen@ideasonboard.com>\n\t<20220524114610.41848-11-tomi.valkeinen@ideasonboard.com>\n\t<Yo+jArIL1TobIxcf@pendragon.ideasonboard.com>","In-Reply-To":"<Yo+jArIL1TobIxcf@pendragon.ideasonboard.com>","Content-Type":"text/plain; charset=UTF-8; format=flowed","Content-Transfer-Encoding":"7bit","Subject":"Re: [libcamera-devel] [PATCH v2 10/19] py: examples: Add\n\tsimple-capture.py","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>","Cc":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":23175,"web_url":"https://patchwork.libcamera.org/comment/23175/","msgid":"<YpBsQ40TPqil7r8P@pendragon.ideasonboard.com>","date":"2022-05-27T06:14:27","subject":"Re: [libcamera-devel] [PATCH v2 10/19] py: examples: Add\n\tsimple-capture.py","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Tomi,\n\nOn Fri, May 27, 2022 at 09:08:37AM +0300, Tomi Valkeinen wrote:\n> On 26/05/2022 18:55, Laurent Pinchart wrote:\n> > On Tue, May 24, 2022 at 02:46:01PM +0300, Tomi Valkeinen wrote:\n> >> Add an example to showcase the more-or-less minimal capture case.\n> > \n> > Niiiice :-) This is a really good start. I'd like more comments, but\n> > that can be done on top.\n> > \n> >> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>\n> >> ---\n> >>   src/py/examples/simple-capture.py | 153 ++++++++++++++++++++++++++++++\n> >>   1 file changed, 153 insertions(+)\n> >>   create mode 100755 src/py/examples/simple-capture.py\n> >>\n> >> diff --git a/src/py/examples/simple-capture.py b/src/py/examples/simple-capture.py\n> >> new file mode 100755\n> >> index 00000000..45df9e7a\n> >> --- /dev/null\n> >> +++ b/src/py/examples/simple-capture.py\n> >> @@ -0,0 +1,153 @@\n> >> +#!/usr/bin/env python3\n> >> +\n> >> +# SPDX-License-Identifier: BSD-3-Clause\n> >> +# Copyright (C) 2022, Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>\n> >> +\n> >> +# A simple minimal capture example showing:\n> >> +# - How to setup the camera\n> >> +# - Capture frames in a blocking manner\n> >> +# - Memory map the frames\n> >> +# - How to stop the camera\n> >> +\n> >> +import argparse\n> >> +import binascii\n> >> +import libcamera as libcam\n> >> +import libcamera.utils\n> >> +import sys\n> >> +\n> >> +\n> >> +def main():\n> >> +    parser = argparse.ArgumentParser()\n> >> +    parser.add_argument('-c', '--camera', type=str, default='1',\n> >> +                        help='Camera index number (starting from 1) or part of the name')\n> >> +    parser.add_argument('-f', '--format', type=str, help='Pixel format')\n> >> +    parser.add_argument('-s', '--size', type=str, help='Size (\"WxH\")')\n> >> +    args = parser.parse_args()\n> >> +\n> >> +    cm = libcam.CameraManager.singleton()\n> >> +\n> >> +    try:\n> >> +        if args.camera.isnumeric():\n> >> +            cam_idx = int(args.camera)\n> >> +            cam = next((cam for i, cam in enumerate(cm.cameras) if i + 1 == cam_idx))\n> >> +        else:\n> >> +            cam = next((cam for cam in cm.cameras if args.camera in cam.id))\n> >> +    except Exception:\n> >> +        print(f'Failed to find camera \"{args.camera}\"')\n> >> +        return -1\n> >> +\n> >> +    # Acquire the camera for our use\n> >> +\n> >> +    ret = cam.acquire()\n> >> +    assert ret == 0\n> >> +\n> >> +    # Configure the camera\n> >> +\n> >> +    camconfig = cam.generate_configuration([libcam.StreamRole.StillCapture])\n> > \n> > I'm going to bikeshed a bit as this will be used as a starting point by\n> > many people, sorry.\n> > \n> > As the Python coding style uses snake_case, should this be cam_config ?\n> > The next variable would be stream_config.\n> > \n> > I'd also use the ViewFinder role to match the C++ simple-cam.\n> \n> Sounds good to me.\n> \n> >> +\n> >> +    streamconfig = camconfig.at(0)\n> >> +\n> >> +    if args.format:\n> >> +        fmt = libcam.PixelFormat(args.format)\n> >> +        streamconfig.pixel_format = fmt\n> >> +\n> >> +    if args.size:\n> >> +        w, h = [int(v) for v in args.size.split('x')]\n> >> +        streamconfig.size = libcam.Size(w, h)\n> >> +\n> >> +    ret = cam.configure(camconfig)\n> >> +    assert ret == 0\n> >> +\n> >> +    stream = streamconfig.stream\n> >> +\n> >> +    # Allocate the buffers for capture\n> >> +\n> >> +    allocator = libcam.FrameBufferAllocator(cam)\n> >> +    ret = allocator.allocate(stream)\n> >> +    assert ret > 0\n> >> +\n> >> +    num_bufs = len(allocator.buffers(stream))\n> >> +\n> >> +    print(f'Capturing {num_bufs} frames with {streamconfig}')\n> > \n> > This can be done on top, but I'd like to capture more frames (100 for\n> > instance), to show how to requeue requests.\n> > \n> >> +\n> >> +    # Create the requests and assign a buffer for each request\n> >> +\n> >> +    reqs = []\n> >> +    for i in range(num_bufs):\n> >> +        # Use the buffer index as the cookie\n> >> +        req = cam.create_request(i)\n> >> +\n> >> +        buffer = allocator.buffers(stream)[i]\n> >> +        ret = req.add_buffer(stream, buffer)\n> >> +        assert ret == 0\n> >> +\n> >> +        reqs.append(req)\n> >> +\n> >> +    # Start the camera\n> >> +\n> >> +    ret = cam.start()\n> >> +    assert ret == 0\n> >> +\n> >> +    # Queue the requests to the camera\n> >> +\n> >> +    for req in reqs:\n> >> +        ret = cam.queue_request(req)\n> >> +        assert ret == 0\n> >> +\n> >> +    # Wait until the requests are finished\n> >> +\n> >> +    reqs = []\n> >> +\n> >> +    while True:\n> >> +        # cm.read_event() blocks until there is an event\n> >> +        cm.read_event()\n> >> +\n> >> +        # Get all the ready requests\n> >> +        ready_reqs = cm.get_ready_requests()\n> > \n> > Integrating a simple event loop (maybe based on the Python asyncio\n> > module ?) would be good here.\n> \n> Let's discuss these in the thread for the following patch.\n> \n> >> +\n> >> +        reqs += ready_reqs\n> >> +\n> >> +        if len(reqs) >= num_bufs:\n> >> +            break\n> >> +\n> >> +    # Process the captured frames\n> >> +\n> >> +    for i, req in enumerate(reqs):\n> >> +        assert i == req.cookie\n> >> +\n> >> +        buffers = req.buffers\n> >> +\n> >> +        # A ready Request could contain multiple buffers if multiple streams\n> >> +        # were being used. Here we know we only have a single stream,\n> >> +        # and we use next(iter()) to get the first and only buffer.\n> >> +\n> >> +        assert len(buffers) == 1\n> >> +\n> >> +        stream, fb = next(iter(buffers.items()))\n> >> +\n> >> +        # Use MappedFrameBuffer to access the pixel data with CPU. We calculate\n> >> +        # the crc for each plane.\n> >> +\n> >> +        with libcamera.utils.MappedFrameBuffer(fb) as mfb:\n> >> +            crcs = [binascii.crc32(p) for p in mfb.planes]\n> > \n> > Mapping buffers is expensive, this should be done before starting the\n> > camera.\n> \n> Hmm... How do we invalidate the caches if we keep the buffers mapped?\n\nQBUF/DQBUF are supposed to handle that.","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 14982BD161\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 27 May 2022 06:14:35 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 6FCA8633A1;\n\tFri, 27 May 2022 08:14:34 +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 79E7D61FB6\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 27 May 2022 08:14:32 +0200 (CEST)","from pendragon.ideasonboard.com (ip-109-40-242-63.web.vodafone.de\n\t[109.40.242.63])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id A9D4531A;\n\tFri, 27 May 2022 08:14:31 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1653632074;\n\tbh=mxMNF7R5xcceWAj/IfoOaBjgZYgetIOYlRqdk95rE9A=;\n\th=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=4dWMGogZWEN3R0Aoij6/qa6LuEKGZ9jxx7QFTulODgNRVaEzKByLzkI2NNn/FrsXq\n\tpTQRwXO9LfjNCy2GChEoFSPAbDoED2OxNZXoeTLU8aMrBxuaaYi5hPuux4wdxdmc8h\n\t7A3VAlaF6rsXxrzRCxvred0xKl3JgJ80XFpwrMJO/BXLXWPCC3dgWAN0wi6JNn8EWs\n\tNFuQgCJ2MoOoqREOYsbNnQEBv2tH7SDTMqOyl17ZInwj+LcMhOYqobd8gvNA/XMUlq\n\tI7okjggSj9tWrlVwxzhQBVqx1E/3TqWgEHQUK/1QdCjJQUjTXFG3HBip88xuIZIOSa\n\tU+LDaoBQNKc7g==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1653632071;\n\tbh=mxMNF7R5xcceWAj/IfoOaBjgZYgetIOYlRqdk95rE9A=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=CnBLpbZPrcv6ckrx34sC+PvevpfQ68IF2DNiVYsH7zos/a/TXTkzw20NZ/Y/vDc+0\n\tbtx+phZ3+aUYJj+M6WNuA8R39GJo8Kl5Aele0+rf6inu1YRWADiZ42MyEob3Dj4uQk\n\tEFwUOYZRjhUb0bMeIib14d0EscZPzmI/lKuunmK8="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"CnBLpbZP\"; dkim-atps=neutral","Date":"Fri, 27 May 2022 09:14:27 +0300","To":"Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>","Message-ID":"<YpBsQ40TPqil7r8P@pendragon.ideasonboard.com>","References":"<20220524114610.41848-1-tomi.valkeinen@ideasonboard.com>\n\t<20220524114610.41848-11-tomi.valkeinen@ideasonboard.com>\n\t<Yo+jArIL1TobIxcf@pendragon.ideasonboard.com>\n\t<d937aee9-24cf-b599-f23f-7fe2500559c3@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<d937aee9-24cf-b599-f23f-7fe2500559c3@ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH v2 10/19] py: examples: Add\n\tsimple-capture.py","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":"Laurent Pinchart via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":23176,"web_url":"https://patchwork.libcamera.org/comment/23176/","msgid":"<46c06cde-7cfe-0f8b-d2b0-32ff92fbcd34@ideasonboard.com>","date":"2022-05-27T06:16:01","subject":"Re: [libcamera-devel] [PATCH v2 10/19] py: examples: Add\n\tsimple-capture.py","submitter":{"id":109,"url":"https://patchwork.libcamera.org/api/people/109/","name":"Tomi Valkeinen","email":"tomi.valkeinen@ideasonboard.com"},"content":"On 27/05/2022 09:14, Laurent Pinchart wrote:\n\n>>>> +        with libcamera.utils.MappedFrameBuffer(fb) as mfb:\n>>>> +            crcs = [binascii.crc32(p) for p in mfb.planes]\n>>>\n>>> Mapping buffers is expensive, this should be done before starting the\n>>> camera.\n>>\n>> Hmm... How do we invalidate the caches if we keep the buffers mapped?\n> \n> QBUF/DQBUF are supposed to handle that.\n\nAh, ok. Isn't that horribly inefficient if you never actually access the \nbuffer with CPU?\n\n  Tomi","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 ADD75BD161\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 27 May 2022 06:16:05 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 5F3CF633A1;\n\tFri, 27 May 2022 08:16:05 +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 6608561FB6\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 27 May 2022 08:16:04 +0200 (CEST)","from [192.168.1.111] (91-156-85-209.elisa-laajakaista.fi\n\t[91.156.85.209])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id DE16531A;\n\tFri, 27 May 2022 08:16:03 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1653632165;\n\tbh=aq9G0h3VcD4g6GEyhJ82MB6AWP2HiWv/VHxwrQy+WI4=;\n\th=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=ewTycUmFPGjLAmMBgUOE8krKRgszJj4fO1kLG3VD8fAvUqBMBEHLVcwcTMTgnydpG\n\tha1BfyunwbsQeyHQgjO2hVOcfj6xJ2bztK/qDMBrzYV6U5NGi328OrU19urj+oMll0\n\tU21JLuk3+20ONRtX2Vi9YZaZtTS6tAGxvP/3snhOITrp7o216Kxd5meo6hGyh4kEbB\n\tj2ii8Zasipgb82cWNv+mpA9PU8Dyi7uvM73W57fCcOulgty4FkKEJzJJGU7ZHZcPP5\n\tKhPpbQDYlzIS0JwBrN201wllnM6v0WYaxr/BB1MD2T3RNU/8SGAL8vxzQKXtnYw0oc\n\tdMCejVqVu9Efw==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1653632164;\n\tbh=aq9G0h3VcD4g6GEyhJ82MB6AWP2HiWv/VHxwrQy+WI4=;\n\th=Date:Subject:To:Cc:References:From:In-Reply-To:From;\n\tb=R4s9etFrM5PC1jNvcbCHwFzFhJI6mF+xj1qOz276AYszufyDsRg4EGZ4V7jQcVP8Q\n\tW0iNzNIT4U6mt7IqH/fp0w9BcKe3/N6GijRIEOqekWf+YIiAfYyD4Tb8gXilbm4rDw\n\tAc70aTJzMxpwo3lcf8lnrEBKAKUDyQ0qcjGWofus="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"R4s9etFr\"; dkim-atps=neutral","Message-ID":"<46c06cde-7cfe-0f8b-d2b0-32ff92fbcd34@ideasonboard.com>","Date":"Fri, 27 May 2022 09:16:01 +0300","MIME-Version":"1.0","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101\n\tThunderbird/91.9.1","Content-Language":"en-US","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","References":"<20220524114610.41848-1-tomi.valkeinen@ideasonboard.com>\n\t<20220524114610.41848-11-tomi.valkeinen@ideasonboard.com>\n\t<Yo+jArIL1TobIxcf@pendragon.ideasonboard.com>\n\t<d937aee9-24cf-b599-f23f-7fe2500559c3@ideasonboard.com>\n\t<YpBsQ40TPqil7r8P@pendragon.ideasonboard.com>","In-Reply-To":"<YpBsQ40TPqil7r8P@pendragon.ideasonboard.com>","Content-Type":"text/plain; charset=UTF-8; format=flowed","Content-Transfer-Encoding":"7bit","Subject":"Re: [libcamera-devel] [PATCH v2 10/19] py: examples: Add\n\tsimple-capture.py","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>","Cc":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":23177,"web_url":"https://patchwork.libcamera.org/comment/23177/","msgid":"<YpBu2KhGyThHcWE5@pendragon.ideasonboard.com>","date":"2022-05-27T06:25:28","subject":"Re: [libcamera-devel] [PATCH v2 10/19] py: examples: Add\n\tsimple-capture.py","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"On Fri, May 27, 2022 at 09:16:01AM +0300, Tomi Valkeinen wrote:\n> On 27/05/2022 09:14, Laurent Pinchart wrote:\n> \n> >>>> +        with libcamera.utils.MappedFrameBuffer(fb) as mfb:\n> >>>> +            crcs = [binascii.crc32(p) for p in mfb.planes]\n> >>>\n> >>> Mapping buffers is expensive, this should be done before starting the\n> >>> camera.\n> >>\n> >> Hmm... How do we invalidate the caches if we keep the buffers mapped?\n> > \n> > QBUF/DQBUF are supposed to handle that.\n> \n> Ah, ok. Isn't that horribly inefficient if you never actually access the \n> buffer with CPU?\n\nThere are hints that can be passed to the ioctls to have cache\noperations when not touching the buffers with the CPU.","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 876DBBD160\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 27 May 2022 06:25:35 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id CD927633A1;\n\tFri, 27 May 2022 08:25:34 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 0A8A161FB6\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 27 May 2022 08:25:34 +0200 (CEST)","from pendragon.ideasonboard.com (ip-109-40-242-63.web.vodafone.de\n\t[109.40.242.63])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 2D7F831A;\n\tFri, 27 May 2022 08:25:33 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1653632734;\n\tbh=qyjK7Uw4Ru16pN93JsJ1W4wEgwEvd8mUia33vOB7R1o=;\n\th=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=fnd4NJ+RJhvoe9GORCueukzSaoVJnY5QKPQWcBWx/AytYcskx3ZXYDykBtk/H5ZC7\n\t/2HsR81vr0FxjUqZxnlJmGE2qqJ86OI2Qck7NSDA6tmdXu+ihiLnwMe0MW7U/lhegO\n\tUT4Lh+fYh5zIZTbC5A5bnrq4LkTRL8WKo/y02HSWpsUZucQlmNOER9CnCALfvDV79i\n\tOMVnkzMLFRohnJoYtBvjY28HBGEb2hxxZWg5UDMRhJX3Q7YoJsMM70RZYLdUD9a3Rz\n\t9Eclva9xqMr0oU8Ht0e0yjHq1CNmeRHT0zcuzz6QtPx8jcDUrgL5AyuA5jvPG+31mp\n\t5Ijd8ixsRJUBw==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1653632733;\n\tbh=qyjK7Uw4Ru16pN93JsJ1W4wEgwEvd8mUia33vOB7R1o=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=IkFrbXC35Eh4MYr/004tm7OhT4gL4PLXVhBtf9yhCdX8ZioDXEgXg2+Dzd7t+nmII\n\tNFaAUQFfabEDX89eiYNOmleQ70PyJNukhx7sDAtlbNrgjq45KfA2i49pMW0vNMmZCI\n\tf+xVu1p+lmqXqN4S4tWiNyVbQ7/wdvqa2DEb7wu4="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"IkFrbXC3\"; dkim-atps=neutral","Date":"Fri, 27 May 2022 09:25:28 +0300","To":"Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>","Message-ID":"<YpBu2KhGyThHcWE5@pendragon.ideasonboard.com>","References":"<20220524114610.41848-1-tomi.valkeinen@ideasonboard.com>\n\t<20220524114610.41848-11-tomi.valkeinen@ideasonboard.com>\n\t<Yo+jArIL1TobIxcf@pendragon.ideasonboard.com>\n\t<d937aee9-24cf-b599-f23f-7fe2500559c3@ideasonboard.com>\n\t<YpBsQ40TPqil7r8P@pendragon.ideasonboard.com>\n\t<46c06cde-7cfe-0f8b-d2b0-32ff92fbcd34@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<46c06cde-7cfe-0f8b-d2b0-32ff92fbcd34@ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH v2 10/19] py: examples: Add\n\tsimple-capture.py","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":"Laurent Pinchart via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]