[{"id":23166,"web_url":"https://patchwork.libcamera.org/comment/23166/","msgid":"<Yo+nYk+aTVU16NhC@pendragon.ideasonboard.com>","date":"2022-05-26T16:14:26","subject":"Re: [libcamera-devel] [PATCH v2 11/19] py: examples: Add\n\tsimple-continuous-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 Tue, May 24, 2022 at 02:46:02PM +0300, Tomi Valkeinen wrote:\n> Add a slightly more complex, and I think a more realistic, example,\n> where the script reacts to events and re-queues the buffers.\n\nAh, I should have read this before reviewing 10/19 and asking you to\nimplement the features of this application in simple-capture.py :-)\n\nI think simple-capture.py is a bit too simple, I'm tempted to keep this\none only and rename it simple-capture.py, or maybe better simple-cam.py\nto match the C++ application. Having the exact same features in both the\nC++ and Python examples would be very nice I think.\n\n> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>\n> ---\n>  src/py/examples/simple-continuous-capture.py | 179 +++++++++++++++++++\n>  1 file changed, 179 insertions(+)\n>  create mode 100755 src/py/examples/simple-continuous-capture.py\n> \n> diff --git a/src/py/examples/simple-continuous-capture.py b/src/py/examples/simple-continuous-capture.py\n> new file mode 100755\n> index 00000000..05fa2ada\n> --- /dev/null\n> +++ b/src/py/examples/simple-continuous-capture.py\n> @@ -0,0 +1,179 @@\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 using events\n> +# - How to requeue requests\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 selectors\n> +import sys\n> +\n> +\n> +def handle_camera_event(cm, cam):\n> +    # cm.read_event() will not block here, as we know there is an event to read.\n> +    # We have to read the event to clear it.\n> +\n> +    cm.read_event()\n> +\n> +    reqs = cm.get_ready_requests()\n> +\n> +    # Process the captured frames\n> +\n> +    for req in reqs:\n> +        buffers = req.buffers\n> +\n> +        assert len(buffers) == 1\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> +        stream, fb = next(iter(buffers.items()))\n\nCould this be made simpler, for instance\n\n        stream, fb = buffers[0]\n\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\nSame comment as in 10/19, buffers should be pre-mapped.\n\n> +\n> +        meta = fb.metadata\n> +\n> +        print('buf {}, seq {}, bytes {}, CRCs {}'\n> +              .format(req.cookie, meta.sequence, meta.bytesused, crcs))\n> +\n> +        # We want to re-queue the buffer we just handled. Instead of creating\n> +        # a new Request, we re-use the old one. We need to call req.reuse()\n> +        # to re-initialize the Request before queuing.\n> +\n> +        req.reuse()\n> +        cam.queue_request(req)\n> +\n> +    return True\n> +\n> +\n> +def handle_key_envet(fileob):\n\ns/envet/event/\n\n> +    sys.stdin.readline()\n> +    print(\"Exiting...\")\n\ns/\"/'/g\n\nMuscle memory ? :-)\n\n> +    return False\n> +\n> +\n> +def capture(cm, cam, reqs):\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> +    # Use Selector to wait for events from the camera and from the keyboard\n> +\n> +    sel = selectors.DefaultSelector()\n> +    sel.register(cm.efd, selectors.EVENT_READ, lambda fd: handle_camera_event(cm, cam))\n> +    sel.register(sys.stdin, selectors.EVENT_READ, handle_key_envet)\n> +\n> +    reqs = []\n> +    running = True\n> +\n> +    while running:\n> +        events = sel.select()\n> +        for key, mask in events:\n> +            running = key.data(key.fileobj)\n\nIf we happen to have two event, first a key press and then a frame\ncapture, processed in the same iteration of the while loop, then running\nwill be overridden. You can make running a global variable and set it in\nhandle_key_event, or possibly make handle_key_event a closure that\ncaptures a running variable local to this function.\n\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> +    streamconfig = camconfig.at(0)\n\nSame comments as for patch 10/19.\n\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 {streamconfig} with {num_bufs} buffers from {cam.id}')\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> +    capture(cm, cam, reqs)\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 E5DE9BD160\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 26 May 2022 16:14:35 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 2D8E9611BF;\n\tThu, 26 May 2022 18:14:35 +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 EC08D60DB0\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 26 May 2022 18:14:33 +0200 (CEST)","from pendragon.ideasonboard.com (unknown [37.120.212.14])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 19BEC32A;\n\tThu, 26 May 2022 18:14:33 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1653581675;\n\tbh=vECgTRqh14sDHzK2FBj+oTU0WOfIJSFTqgUoYtntTYo=;\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=FmN+BxX/HR/K5vcqu+bC7q0IDTF/lI7ve9tsuaDe1DW9quSA38H1YO91Hv7Co52GD\n\tJu3RmQ3daGe7ly4fkgBQ2PtfORtIcD4DmPaIXMUJPKVqNxpeqC9IAzPKaWxStS7TTB\n\twzIIZBUQ4XeZ9ALjjEwG6ECCu2iZ/Zw+BE6oxXlefRIob0VsIxihPep5yldneoTrWe\n\tsPHNLFovvLlfikJtnDe2tPd/y/EkvRKJmSdONI10REJjwR+nCT8aLS2nLlD29nj6bp\n\tnfyHClG35kcMZAMK9zTCfVRiZJjmSE788E1UrKGz4rRWu146GScoLha+oJsxfQZCL9\n\t/eb2EgwhimzmQ==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1653581673;\n\tbh=vECgTRqh14sDHzK2FBj+oTU0WOfIJSFTqgUoYtntTYo=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=dz4MaHTm01LLotRNvC8fp/EEdjRlRxUgaPFF248A5CshAVH7TNEo/ltrKFdoVhUG/\n\tC3LrLDU8vlJBIIYWoq7Jek9+hqd/VN2txIzQ4pYSWFO6wLJHcfilaUmeikML1EI/Vm\n\tYQu2MrshU/IonJkdzS963Eef6YqBTHr7CJHhEWCg="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"dz4MaHTm\"; dkim-atps=neutral","Date":"Thu, 26 May 2022 19:14:26 +0300","To":"Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>","Message-ID":"<Yo+nYk+aTVU16NhC@pendragon.ideasonboard.com>","References":"<20220524114610.41848-1-tomi.valkeinen@ideasonboard.com>\n\t<20220524114610.41848-12-tomi.valkeinen@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<20220524114610.41848-12-tomi.valkeinen@ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH v2 11/19] py: examples: Add\n\tsimple-continuous-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":23178,"web_url":"https://patchwork.libcamera.org/comment/23178/","msgid":"<024f8de3-d968-9b47-9155-4b953c52cd34@ideasonboard.com>","date":"2022-05-27T06:40:14","subject":"Re: [libcamera-devel] [PATCH v2 11/19] py: examples: Add\n\tsimple-continuous-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 19:14, Laurent Pinchart wrote:\n> Hi Tomi,\n> \n> On Tue, May 24, 2022 at 02:46:02PM +0300, Tomi Valkeinen wrote:\n>> Add a slightly more complex, and I think a more realistic, example,\n>> where the script reacts to events and re-queues the buffers.\n> \n> Ah, I should have read this before reviewing 10/19 and asking you to\n> implement the features of this application in simple-capture.py :-)\n> \n> I think simple-capture.py is a bit too simple, I'm tempted to keep this\n> one only and rename it simple-capture.py, or maybe better simple-cam.py\n> to match the C++ application. Having the exact same features in both the\n> C++ and Python examples would be very nice I think.\n\nI'll have a look at the C++ simple-cam.\n\nI had a few things in mind when writing the examples:\n\n- An example should be fully contained in a single file\n\n- It should only do \"extra\" things that take a few lines. E.g. I added \nthe -s and -f options as it only took a few lines to implement them.\n\n- I had an idea that I'd have multiple (3-4 maybe) examples, with \nincreasing complexity. I remember looking at some KMS examples long time \nback that piece by piece added more functionality, going from a simple \nsingle buffer to triple buffering.\n\n- The examples would have overlapping code, but the first ones could \nhave more descriptions about the basic code, and the latter ones could \nskip those parts.\n\n- With that said, the examples should be simple, as this could easily \nexpand to cam.py, which is, kind of, doing only basic things, but has an \nextra feature here and there.\n\n>> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>\n>> ---\n>>   src/py/examples/simple-continuous-capture.py | 179 +++++++++++++++++++\n>>   1 file changed, 179 insertions(+)\n>>   create mode 100755 src/py/examples/simple-continuous-capture.py\n>>\n>> diff --git a/src/py/examples/simple-continuous-capture.py b/src/py/examples/simple-continuous-capture.py\n>> new file mode 100755\n>> index 00000000..05fa2ada\n>> --- /dev/null\n>> +++ b/src/py/examples/simple-continuous-capture.py\n>> @@ -0,0 +1,179 @@\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 using events\n>> +# - How to requeue requests\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 selectors\n>> +import sys\n>> +\n>> +\n>> +def handle_camera_event(cm, cam):\n>> +    # cm.read_event() will not block here, as we know there is an event to read.\n>> +    # We have to read the event to clear it.\n>> +\n>> +    cm.read_event()\n>> +\n>> +    reqs = cm.get_ready_requests()\n>> +\n>> +    # Process the captured frames\n>> +\n>> +    for req in reqs:\n>> +        buffers = req.buffers\n>> +\n>> +        assert len(buffers) == 1\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>> +        stream, fb = next(iter(buffers.items()))\n> \n> Could this be made simpler, for instance\n> \n>          stream, fb = buffers[0]\n\nNo, buffers is a dict, so it's a bit cumbersome to access. That's how it \nis in the C++ API (BufferMap). I don't know why that is, I would have \nused a vector.\n\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> Same comment as in 10/19, buffers should be pre-mapped.\n\nYep.\n\n>> +\n>> +        meta = fb.metadata\n>> +\n>> +        print('buf {}, seq {}, bytes {}, CRCs {}'\n>> +              .format(req.cookie, meta.sequence, meta.bytesused, crcs))\n>> +\n>> +        # We want to re-queue the buffer we just handled. Instead of creating\n>> +        # a new Request, we re-use the old one. We need to call req.reuse()\n>> +        # to re-initialize the Request before queuing.\n>> +\n>> +        req.reuse()\n>> +        cam.queue_request(req)\n>> +\n>> +    return True\n>> +\n>> +\n>> +def handle_key_envet(fileob):\n> \n> s/envet/event/\n> \n>> +    sys.stdin.readline()\n>> +    print(\"Exiting...\")\n> \n> s/\"/'/g\n> \n> Muscle memory ? :-)\n\nAnd I've already used ' characters in C code when using a string. This \nis a nightmare... ;)\n\n>> +    return False\n>> +\n>> +\n>> +def capture(cm, cam, reqs):\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>> +    # Use Selector to wait for events from the camera and from the keyboard\n>> +\n>> +    sel = selectors.DefaultSelector()\n>> +    sel.register(cm.efd, selectors.EVENT_READ, lambda fd: handle_camera_event(cm, cam))\n>> +    sel.register(sys.stdin, selectors.EVENT_READ, handle_key_envet)\n>> +\n>> +    reqs = []\n>> +    running = True\n>> +\n>> +    while running:\n>> +        events = sel.select()\n>> +        for key, mask in events:\n>> +            running = key.data(key.fileobj)\n> \n> If we happen to have two event, first a key press and then a frame\n> capture, processed in the same iteration of the while loop, then running\n> will be overridden. You can make running a global variable and set it in\n> handle_key_event, or possibly make handle_key_event a closure that\n> captures a running variable local to this function.\n\nGood point. I'll just change it so that the code can only set running to \nFalse, never to True.\n\nIn this particular case the libcam handler never returns False. Perhaps \nit should exit after, say, 50 frames, just to showcase that too.\n\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>> +    streamconfig = camconfig.at(0)\n> \n> Same comments as for patch 10/19.\n\nYep.\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 7962EBD161\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 27 May 2022 06:40:20 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id C00A0633A1;\n\tFri, 27 May 2022 08:40:19 +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 AF04361FB6\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 27 May 2022 08:40:17 +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 C4D9F31A;\n\tFri, 27 May 2022 08:40:16 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1653633619;\n\tbh=ah63ERbenVHNWOYA9XmAf23bmLtLOiD0VF2nO2J6G24=;\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=ON+JRmAhTaJC3URJ4BEzJBuwDuksD7B7GL1Q1NYPJkYSvi4QfTLtLPcnWvLJEPTmd\n\toeL9Q8OWXtIyhOvACSK7eCqGgKoF7jFUggzEKS6n7qQJOz4wgxn8X8EsK4IVK6TeoX\n\t387DBEwyLG39kap6WFHQmpyd5eMD7XvRGr/98NZHjlgvNS3G06eembO/pEivrHH7i/\n\tT1NKZLRzIYkvgRUbWlI5lrb7W04/iYQvdNBEBdNOvAnCcu7KDq7Nz//rjkZTwaePKP\n\tgvcWIEUF48k+IXp1h69UdNFFm+V5QQBc7rvmkf9OoMHNClb16eLLi9JKs3WzaM04x9\n\t3cwjtx4q3FZUQ==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1653633617;\n\tbh=ah63ERbenVHNWOYA9XmAf23bmLtLOiD0VF2nO2J6G24=;\n\th=Date:Subject:To:Cc:References:From:In-Reply-To:From;\n\tb=Vf5ost/SY9qsbhWcRDIy9wO17iL2UFGZf1OFJ4pWoC1rrapPIQSl2g5AKuxr7csUA\n\tYNSjDhv6eKPm091AxDoZBq4gqXDdTKhkrGGLih1cRksmNNqh53zKZ+kBONg3qc800g\n\tskiWjD+U0qxqn/n1GWZsZpsX/XU1lAx8FlM9JXLg="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"Vf5ost/S\"; dkim-atps=neutral","Message-ID":"<024f8de3-d968-9b47-9155-4b953c52cd34@ideasonboard.com>","Date":"Fri, 27 May 2022 09:40:14 +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-12-tomi.valkeinen@ideasonboard.com>\n\t<Yo+nYk+aTVU16NhC@pendragon.ideasonboard.com>","In-Reply-To":"<Yo+nYk+aTVU16NhC@pendragon.ideasonboard.com>","Content-Type":"text/plain; charset=UTF-8; format=flowed","Content-Transfer-Encoding":"7bit","Subject":"Re: [libcamera-devel] [PATCH v2 11/19] py: examples: Add\n\tsimple-continuous-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>"}}]