[{"id":23323,"web_url":"https://patchwork.libcamera.org/comment/23323/","msgid":"<20220605122413.2eywsgsplabnywfs@uno.localdomain>","date":"2022-06-05T12:24:13","subject":"Re: [libcamera-devel] [PATCH v4 13/16] py: examples: Add\n\tsimple-capture.py","submitter":{"id":3,"url":"https://patchwork.libcamera.org/api/people/3/","name":"Jacopo Mondi","email":"jacopo@jmondi.org"},"content":"Hi Tomi,\n\nOn Mon, May 30, 2022 at 05:27:19PM +0300, Tomi Valkeinen wrote:\n> Add an example to showcase the more-or-less minimal capture case.\n>\n> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>\n> ---\n>  src/py/examples/simple-capture.py | 162 ++++++++++++++++++++++++++++++\n>  1 file changed, 162 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..ff8cef4f\n> --- /dev/null\n> +++ b/src/py/examples/simple-capture.py\n> @@ -0,0 +1,162 @@\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 capture example showing:\n> +# - How to setup the camera\n> +# - Capture certain number of frames in a blocking manner\n> +# - How to stop the camera\n> +#\n> +# This simple example is, in many ways, too simple. The purpose of the example\n> +# is to introduce the concepts. A more realistic example is given in\n> +# simple-continuous-capture.py.\n> +\n> +import argparse\n> +import libcamera as libcam\n> +import sys\n> +\n> +# Number of frames to capture\n> +TOTAL_FRAMES = 30\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> +    cam_config = cam.generate_configuration([libcam.StreamRole.Viewfinder])\n> +\n> +    stream_config = cam_config.at(0)\n> +\n> +    print(f'Capturing {TOTAL_FRAMES} frames with {stream_config}')\n\nCould you move this after setting the sizes and formats ? Or maybe\neven after the camera has been configured..\n\nOtherwise it will print the default configuration\n\n> +\n> +    if args.format:\n> +        fmt = libcam.PixelFormat(args.format)\n> +        stream_config.pixel_format = fmt\n> +\n> +    if args.size:\n> +        w, h = [int(v) for v in args.size.split('x')]\n> +        stream_config.size = libcam.Size(w, h)\n> +\n> +    ret = cam.configure(cam_config)\n> +    assert ret == 0\n> +\n> +    stream = stream_config.stream\n> +\n> +    # Allocate the buffers for capture\n> +\n> +    allocator = libcam.FrameBufferAllocator(cam)\n> +    ret = allocator.allocate(stream)\n> +    assert ret > 0\n\nWe have a rather bad API when it comes to control how many buffer to\nallocate. Python can only reflect that, so I guess it's fine to just\ncheck for > 0 here\n\n> +\n> +    num_bufs = len(allocator.buffers(stream))\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> +    # frames_queued and frames_done track the number of frames queued and done\n> +\n> +    frames_queued = 0\n> +    frames_done = 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> +        frames_queued += 1\n> +\n> +    # The main loop. Wait for the queued Requests to complete, process them,\n> +    # and re-queue them again.\n> +\n> +    while frames_done < TOTAL_FRAMES:\n> +        # cm.get_ready_requests() blocks until there is an event and returns\n> +        # all the ready requests. Here we should almost always get a single\n> +        # Request, but in some cases there could be multiple or none.\n> +\n> +        reqs = cm.get_ready_requests()\n> +\n> +        for req in reqs:\n> +            frames_done += 1\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> +            # Here we could process the received buffer. In this example we only\n> +            # print a few details below.\n> +\n> +            meta = fb.metadata\n> +\n> +            print(\"seq {:3}, bytes {}, frames queued/done {:3}/{:<3}\"\n> +                  .format(meta.sequence,\n> +                          '/'.join([str(p.bytes_used) for p in meta.planes]),\n> +                          frames_queued, frames_done))\n\nI wonder if printing the number of queued requests is of any use..\n\nAll minors, the rest looks good to me\nReviewed-by: Jacopo Mondi <jacopo@jmondi.org>\n\nThanks\n   j\n\n> +\n> +            # If we want to capture more frames we need to queue more Requests.\n> +            # We could create a totally new Request, but it is more efficient\n> +            # to reuse the existing one that we just received.\n> +            if frames_queued < TOTAL_FRAMES:\n> +                req.reuse()\n> +                cam.queue_request(req)\n> +                frames_queued += 1\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())\n> --\n> 2.34.1\n>","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 26164BD160\n\tfor <parsemail@patchwork.libcamera.org>;\n\tSun,  5 Jun 2022 12:24:19 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 4071361FB5;\n\tSun,  5 Jun 2022 14:24:18 +0200 (CEST)","from relay4-d.mail.gandi.net (relay4-d.mail.gandi.net\n\t[IPv6:2001:4b98:dc4:8::224])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 8843C60104\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSun,  5 Jun 2022 14:24:16 +0200 (CEST)","(Authenticated sender: jacopo@jmondi.org)\n\tby mail.gandi.net (Postfix) with ESMTPSA id 4C2D7E0002;\n\tSun,  5 Jun 2022 12:24:15 +0000 (UTC)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1654431858;\n\tbh=OP6zXd5bTWy+NX2FUosahe3xUDGba9+2o0sSBbfgKJU=;\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=ELMriQbu2o1I+ImIW19YSWdQnru0MRYOr4x2GDTLoe/xkCNoS2cYLWoxU9hevockQ\n\tCjmr3eko8SxN6MLcNHd1szOxJTIPd1qLQNZ+FVAFDatYZCMO48UJLwdgeyQEIPbYNz\n\tM51SG03FP2D0vCkS7WRwZPLZDDZxU167Qx9xhpmm2HeBj5WEmcQ7O4bWanMG2YEngL\n\tRXTZtpa7ezq/xsHHNnP3qA4l7ABzsFrScscIBUKN3u5Oruhej8xpSLlyqznll/9uGb\n\t5/lquGgC9KkAnwSvJwtL3+DqrIgSDqOY83fis2NYQKopG+ZOA1I53GPHG8yhTIHfKp\n\t+2uQK4WurRjjQ==","Date":"Sun, 5 Jun 2022 14:24:13 +0200","To":"Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>","Message-ID":"<20220605122413.2eywsgsplabnywfs@uno.localdomain>","References":"<20220530142722.57618-1-tomi.valkeinen@ideasonboard.com>\n\t<20220530142722.57618-14-tomi.valkeinen@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<20220530142722.57618-14-tomi.valkeinen@ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH v4 13/16] 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":"Jacopo Mondi via libcamera-devel <libcamera-devel@lists.libcamera.org>","Reply-To":"Jacopo Mondi <jacopo@jmondi.org>","Cc":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":23331,"web_url":"https://patchwork.libcamera.org/comment/23331/","msgid":"<53bf1082-5549-ec0f-e752-4be627053608@ideasonboard.com>","date":"2022-06-06T08:37:56","subject":"Re: [libcamera-devel] [PATCH v4 13/16] 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 05/06/2022 15:24, Jacopo Mondi wrote:\n> Hi Tomi,\n> \n> On Mon, May 30, 2022 at 05:27:19PM +0300, Tomi Valkeinen wrote:\n>> Add an example to showcase the more-or-less minimal capture case.\n>>\n>> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>\n>> ---\n>>   src/py/examples/simple-capture.py | 162 ++++++++++++++++++++++++++++++\n>>   1 file changed, 162 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..ff8cef4f\n>> --- /dev/null\n>> +++ b/src/py/examples/simple-capture.py\n>> @@ -0,0 +1,162 @@\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 capture example showing:\n>> +# - How to setup the camera\n>> +# - Capture certain number of frames in a blocking manner\n>> +# - How to stop the camera\n>> +#\n>> +# This simple example is, in many ways, too simple. The purpose of the example\n>> +# is to introduce the concepts. A more realistic example is given in\n>> +# simple-continuous-capture.py.\n>> +\n>> +import argparse\n>> +import libcamera as libcam\n>> +import sys\n>> +\n>> +# Number of frames to capture\n>> +TOTAL_FRAMES = 30\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>> +    cam_config = cam.generate_configuration([libcam.StreamRole.Viewfinder])\n>> +\n>> +    stream_config = cam_config.at(0)\n>> +\n>> +    print(f'Capturing {TOTAL_FRAMES} frames with {stream_config}')\n> \n> Could you move this after setting the sizes and formats ? Or maybe\n> even after the camera has been configured..\n> \n> Otherwise it will print the default configuration\n\nThanks, you're right.\n\n>> +\n>> +    if args.format:\n>> +        fmt = libcam.PixelFormat(args.format)\n>> +        stream_config.pixel_format = fmt\n>> +\n>> +    if args.size:\n>> +        w, h = [int(v) for v in args.size.split('x')]\n>> +        stream_config.size = libcam.Size(w, h)\n>> +\n>> +    ret = cam.configure(cam_config)\n>> +    assert ret == 0\n>> +\n>> +    stream = stream_config.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> We have a rather bad API when it comes to control how many buffer to\n> allocate. Python can only reflect that, so I guess it's fine to just\n> check for > 0 here\n> \n>> +\n>> +    num_bufs = len(allocator.buffers(stream))\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>> +    # frames_queued and frames_done track the number of frames queued and done\n>> +\n>> +    frames_queued = 0\n>> +    frames_done = 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>> +        frames_queued += 1\n>> +\n>> +    # The main loop. Wait for the queued Requests to complete, process them,\n>> +    # and re-queue them again.\n>> +\n>> +    while frames_done < TOTAL_FRAMES:\n>> +        # cm.get_ready_requests() blocks until there is an event and returns\n>> +        # all the ready requests. Here we should almost always get a single\n>> +        # Request, but in some cases there could be multiple or none.\n>> +\n>> +        reqs = cm.get_ready_requests()\n>> +\n>> +        for req in reqs:\n>> +            frames_done += 1\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>> +            # Here we could process the received buffer. In this example we only\n>> +            # print a few details below.\n>> +\n>> +            meta = fb.metadata\n>> +\n>> +            print(\"seq {:3}, bytes {}, frames queued/done {:3}/{:<3}\"\n>> +                  .format(meta.sequence,\n>> +                          '/'.join([str(p.bytes_used) for p in meta.planes]),\n>> +                          frames_queued, frames_done))\n> \n> I wonder if printing the number of queued requests is of any use..\n\nI guess one could argue if the whole print is of any use =). But here I \nthought it'll show how at the end the queued number stops increasing \nwhile we still get new frames.\n\n> All minors, the rest looks good to me\n> Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>\n\nThanks!","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 71625BD160\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon,  6 Jun 2022 08:38:03 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 9936565633;\n\tMon,  6 Jun 2022 10:38:02 +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 B4B07633A4\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon,  6 Jun 2022 10:38:00 +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 B055230A;\n\tMon,  6 Jun 2022 10:37:59 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1654504682;\n\tbh=OW5nxCylgtjlLay964XsscjThXNRqaeuDw9S0klD2gM=;\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=UDsx8aeUIX1evmyPXxHUBhon5IvmdbJ5onI/OfejzLDVTPY2ySsJWcuGv7mYUr4Ix\n\tek1THta+VMbySracqSwwTFU3btr/NUqls0I9D8gPQdNIvTP10jDDrJ+79VcOxUXDy+\n\tzBXi0qaakWkKBlNhlvy/0wSprTBC/Svfr4TxNrOMqxZj0NfSWnqjbtGZTEnE2ISuQR\n\t1LgPpH0ptkTNHVPhPnSkON9h7rvhXQV368j3i3U6lKawt56+fgnmaz4wa+toiXhrKl\n\thKdq/wCJ7ejZPBPJ338Hn1bR0jzNLcTyUC0NgLuMPyNoRVtxW3ZXiJD7o7LcYdDCDz\n\tqkO+NLqpxnc7A==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1654504680;\n\tbh=OW5nxCylgtjlLay964XsscjThXNRqaeuDw9S0klD2gM=;\n\th=Date:Subject:To:Cc:References:From:In-Reply-To:From;\n\tb=WmSrqs7i2kn0LpKPB93XUMsXzI0wPDUqW7qeqiArhg8ktowpinjDlMXIxWjuwNobj\n\tcXKDyq8Yqtcw7F3XJybpP0tHMtfjA/I+iw1Lt8WbkU8z5+3RHrlzzCfCMJlLRQpXkZ\n\tZbmC3GFYSwWw566xTlEfbWMQ37zVvyx33BXhGR/k="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"WmSrqs7i\"; dkim-atps=neutral","Message-ID":"<53bf1082-5549-ec0f-e752-4be627053608@ideasonboard.com>","Date":"Mon, 6 Jun 2022 11:37:56 +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":"Jacopo Mondi <jacopo@jmondi.org>","References":"<20220530142722.57618-1-tomi.valkeinen@ideasonboard.com>\n\t<20220530142722.57618-14-tomi.valkeinen@ideasonboard.com>\n\t<20220605122413.2eywsgsplabnywfs@uno.localdomain>","In-Reply-To":"<20220605122413.2eywsgsplabnywfs@uno.localdomain>","Content-Type":"text/plain; charset=UTF-8; format=flowed","Content-Transfer-Encoding":"7bit","Subject":"Re: [libcamera-devel] [PATCH v4 13/16] 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>"}}]