[{"id":28102,"web_url":"https://patchwork.libcamera.org/comment/28102/","msgid":"<20231120041846.GH524@pendragon.ideasonboard.com>","date":"2023-11-20T04:18:46","subject":"Re: [libcamera-devel] [PATCH v1 4/5] libcamera: controls: Use\n\tvendor tags for draft controls and properties","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Naush,\n\nThank you for the patch.\n\nOn Fri, Nov 10, 2023 at 11:00:01AM +0000, Naushir Patuck via libcamera-devel wrote:\n> Label draft controls and properties through the \"draft\" vendor tag\n> and deprecate the existing \"draft: true\" mechanism. This uses the new\n> vendor tags mechanism to place draft controls in the same\n> libcamera::controls::draft namespace and provide a defined control id\n> range for these controls.\n> \n> One breaking change in this commit is that draft control ids also move\n> to the libcamera::controls::draft namespace from the existing\n> libcamera::controls namespace. This is desirable to avoid API breakages\n> when adding new libcamera controls. So, for example, the use of\n> controls::NOISE_REDUCTION_MODE will need to be replaced with\n> controls::draft::NOISE_REDUCTION_MODE.\n\nThe idea looks good to me, but I'll hold off reviewing the changes in\ndetails as discussions on previous poatches may result in a different\nimplementation here.\n\n> \n> Signed-off-by: Naushir Patuck <naush@raspberrypi.com>\n> ---\n>  include/libcamera/control_ids.h.in            |  6 ---\n>  include/libcamera/property_ids.h.in           |  6 ---\n>  src/ipa/rpi/common/ipa_base.cpp               |  2 +-\n>  src/ipa/rpi/vc4/vc4.cpp                       |  2 +-\n>  src/libcamera/control_ids.cpp.in              | 16 +-------\n>  src/libcamera/control_ids.yaml                | 20 +++++-----\n>  src/libcamera/property_ids.cpp.in             | 16 +-------\n>  src/libcamera/property_ids.yaml               |  2 +-\n>  src/py/libcamera/gen-py-controls.py           |  5 +--\n>  src/py/libcamera/py_controls_generated.cpp.in |  5 ---\n>  .../libcamera/py_properties_generated.cpp.in  |  1 -\n>  utils/gen-controls.py                         | 39 +++++--------------\n>  12 files changed, 27 insertions(+), 93 deletions(-)\n> \n> diff --git a/include/libcamera/control_ids.h.in b/include/libcamera/control_ids.h.in\n> index c97b09a82450..d53b1cf7beb2 100644\n> --- a/include/libcamera/control_ids.h.in\n> +++ b/include/libcamera/control_ids.h.in\n> @@ -26,12 +26,6 @@ ${controls}\n>  \n>  extern const ControlIdMap controls;\n>  \n> -namespace draft {\n> -\n> -${draft_controls}\n> -\n> -} /* namespace draft */\n> -\n>  ${vendor_controls}\n>  \n>  } /* namespace controls */\n> diff --git a/include/libcamera/property_ids.h.in b/include/libcamera/property_ids.h.in\n> index 47c5d6bf2e28..43372c718fc9 100644\n> --- a/include/libcamera/property_ids.h.in\n> +++ b/include/libcamera/property_ids.h.in\n> @@ -23,12 +23,6 @@ ${ids}\n>  \n>  ${controls}\n>  \n> -namespace draft {\n> -\n> -${draft_controls}\n> -\n> -} /* namespace draft */\n> -\n>  extern const ControlIdMap properties;\n>  \n>  ${vendor_controls}\n> diff --git a/src/ipa/rpi/common/ipa_base.cpp b/src/ipa/rpi/common/ipa_base.cpp\n> index a1fec3aa3dd1..6ac9d5de2f88 100644\n> --- a/src/ipa/rpi/common/ipa_base.cpp\n> +++ b/src/ipa/rpi/common/ipa_base.cpp\n> @@ -1024,7 +1024,7 @@ void IpaBase::applyControls(const ControlList &controls)\n>  \t\t\tbreak;\n>  \t\t}\n>  \n> -\t\tcase controls::NOISE_REDUCTION_MODE:\n> +\t\tcase controls::draft::NOISE_REDUCTION_MODE:\n>  \t\t\t/* Handled below in handleControls() */\n>  \t\t\tlibcameraMetadata_.set(controls::draft::NoiseReductionMode,\n>  \t\t\t\t\t       ctrl.second.get<int32_t>());\n> diff --git a/src/ipa/rpi/vc4/vc4.cpp b/src/ipa/rpi/vc4/vc4.cpp\n> index c4baf04fb1e7..c165a5b8b0b6 100644\n> --- a/src/ipa/rpi/vc4/vc4.cpp\n> +++ b/src/ipa/rpi/vc4/vc4.cpp\n> @@ -260,7 +260,7 @@ void IpaVc4::handleControls(const ControlList &controls)\n>  \n>  \tfor (auto const &ctrl : controls) {\n>  \t\tswitch (ctrl.first) {\n> -\t\tcase controls::NOISE_REDUCTION_MODE: {\n> +\t\tcase controls::draft::NOISE_REDUCTION_MODE: {\n>  \t\t\tRPiController::DenoiseAlgorithm *sdn = dynamic_cast<RPiController::DenoiseAlgorithm *>(\n>  \t\t\t\tcontroller_.getAlgorithm(\"SDN\"));\n>  \t\t\t/* Some platforms may have a combined \"denoise\" algorithm instead. */\n> diff --git a/src/libcamera/control_ids.cpp.in b/src/libcamera/control_ids.cpp.in\n> index d26eb930b30c..284589567cfb 100644\n> --- a/src/libcamera/control_ids.cpp.in\n> +++ b/src/libcamera/control_ids.cpp.in\n> @@ -24,15 +24,6 @@ namespace controls {\n>  \n>  ${controls_doc}\n>  \n> -/**\n> - * \\brief Namespace for libcamera draft controls\n> - */\n> -namespace draft {\n> -\n> -${draft_controls_doc}\n> -\n> -} /* namespace draft */\n> -\n>  ${vendor_controls_doc}\n>  \n>  #ifndef __DOXYGEN__\n> @@ -42,13 +33,8 @@ ${vendor_controls_doc}\n>   */\n>  ${controls_def}\n>  \n> -namespace draft {\n> -\n> -${draft_controls_def}\n> -\n> -} /* namespace draft */\n> -\n>  ${vendor_controls_def}\n> +\n>  #endif\n>  \n>  /**\n> diff --git a/src/libcamera/control_ids.yaml b/src/libcamera/control_ids.yaml\n> index 5827d7ecef49..ce65522e1b71 100644\n> --- a/src/libcamera/control_ids.yaml\n> +++ b/src/libcamera/control_ids.yaml\n> @@ -869,7 +869,7 @@ controls:\n>  \n>    - AePrecaptureTrigger:\n>        type: int32_t\n> -      draft: true\n> +      vendor: draft\n>        description: |\n>          Control for AE metering trigger. Currently identical to\n>          ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER.\n> @@ -891,7 +891,7 @@ controls:\n>  \n>    - NoiseReductionMode:\n>        type: int32_t\n> -      draft: true\n> +      vendor: draft\n>        description: |\n>         Control to select the noise reduction algorithm mode. Currently\n>         identical to ANDROID_NOISE_REDUCTION_MODE.\n> @@ -920,7 +920,7 @@ controls:\n>  \n>    - ColorCorrectionAberrationMode:\n>        type: int32_t\n> -      draft: true\n> +      vendor: draft\n>        description: |\n>         Control to select the color correction aberration mode. Currently\n>         identical to ANDROID_COLOR_CORRECTION_ABERRATION_MODE.\n> @@ -941,7 +941,7 @@ controls:\n>  \n>    - AeState:\n>        type: int32_t\n> -      draft: true\n> +      vendor: draft\n>        description: |\n>         Control to report the current AE algorithm state. Currently identical to\n>         ANDROID_CONTROL_AE_STATE.\n> @@ -971,7 +971,7 @@ controls:\n>  \n>    - AwbState:\n>        type: int32_t\n> -      draft: true\n> +      vendor: draft\n>        description: |\n>         Control to report the current AWB algorithm state. Currently identical\n>         to ANDROID_CONTROL_AWB_STATE.\n> @@ -993,7 +993,7 @@ controls:\n>  \n>    - SensorRollingShutterSkew:\n>        type: int64_t\n> -      draft: true\n> +      vendor: draft\n>        description: |\n>         Control to report the time between the start of exposure of the first\n>         row and the start of exposure of the last row. Currently identical to\n> @@ -1001,7 +1001,7 @@ controls:\n>  \n>    - LensShadingMapMode:\n>        type: int32_t\n> -      draft: true\n> +      vendor: draft\n>        description: |\n>         Control to report if the lens shading map is available. Currently\n>         identical to ANDROID_STATISTICS_LENS_SHADING_MAP_MODE.\n> @@ -1015,7 +1015,7 @@ controls:\n>  \n>    - PipelineDepth:\n>        type: int32_t\n> -      draft: true\n> +      vendor: draft\n>        description: |\n>          Specifies the number of pipeline stages the frame went through from when\n>          it was exposed to when the final completed result was available to the\n> @@ -1030,7 +1030,7 @@ controls:\n>  \n>    - MaxLatency:\n>        type: int32_t\n> -      draft: true\n> +      vendor: draft\n>        description: |\n>          The maximum number of frames that can occur after a request (different\n>          than the previous) has been submitted, and before the result's state\n> @@ -1040,7 +1040,7 @@ controls:\n>  \n>    - TestPatternMode:\n>        type: int32_t\n> -      draft: true\n> +      vendor: draft\n>        description: |\n>          Control to select the test pattern mode. Currently identical to\n>          ANDROID_SENSOR_TEST_PATTERN_MODE.\n> diff --git a/src/libcamera/property_ids.cpp.in b/src/libcamera/property_ids.cpp.in\n> index ddbe714c3f00..b72e12e4cc70 100644\n> --- a/src/libcamera/property_ids.cpp.in\n> +++ b/src/libcamera/property_ids.cpp.in\n> @@ -23,14 +23,7 @@ namespace properties {\n>  \n>  ${controls_doc}\n>  \n> -/**\n> - * \\brief Namespace for libcamera draft properties\n> - */\n> -namespace draft {\n> -\n> -${draft_controls_doc}\n> -\n> -} /* namespace draft */\n> +${vendor_controls_doc}\n>  \n>  ${vendor_controls_doc}\n>  \n> @@ -41,13 +34,8 @@ ${vendor_controls_doc}\n>   */\n>  ${controls_def}\n>  \n> -namespace draft {\n> -\n> -${draft_controls_def}\n> -\n> -} /* namespace draft */\n> -\n>  ${vendor_controls_def}\n> +\n>  #endif\n>  \n>  /**\n> diff --git a/src/libcamera/property_ids.yaml b/src/libcamera/property_ids.yaml\n> index f35563842a5a..622d0a8cf26f 100644\n> --- a/src/libcamera/property_ids.yaml\n> +++ b/src/libcamera/property_ids.yaml\n> @@ -705,7 +705,7 @@ controls:\n>  \n>    - ColorFilterArrangement:\n>        type: int32_t\n> -      draft: true\n> +      vendor: draft\n>        description: |\n>          The arrangement of color filters on sensor; represents the colors in the\n>          top-left 2x2 section of the sensor, in reading order. Currently\n> diff --git a/src/py/libcamera/gen-py-controls.py b/src/py/libcamera/gen-py-controls.py\n> index a32ef09c2f48..9416be6e611d 100755\n> --- a/src/py/libcamera/gen-py-controls.py\n> +++ b/src/py/libcamera/gen-py-controls.py\n> @@ -36,10 +36,7 @@ def generate_py(controls, mode):\n>              vendor_defs.append('\\tauto {} = py::class_<Py{}Controls>(controls, \\\"{}\\\");'.format(vendor, vendor, vendor))\n>              vendors.append(vendor)\n>  \n> -        if ctrl.get('draft'):\n> -            ns = 'libcamera::{}::draft::'.format(mode)\n> -            container = 'draft'\n> -        elif vendor:\n> +        if vendor:\n>              ns = 'libcamera::{}::{}::'.format(mode, vendor)\n>              container = vendor\n>          else:\n> diff --git a/src/py/libcamera/py_controls_generated.cpp.in b/src/py/libcamera/py_controls_generated.cpp.in\n> index ec4b55ef2011..8d282ce51856 100644\n> --- a/src/py/libcamera/py_controls_generated.cpp.in\n> +++ b/src/py/libcamera/py_controls_generated.cpp.in\n> @@ -17,16 +17,11 @@ class PyControls\n>  {\n>  };\n>  \n> -class PyDraftControls\n> -{\n> -};\n> -\n>  ${vendors_class_def}\n>  \n>  void init_py_controls_generated(py::module& m)\n>  {\n>  \tauto controls = py::class_<PyControls>(m, \"controls\");\n> -\tauto draft = py::class_<PyDraftControls>(controls, \"draft\");\n>  ${vendors_defs}\n>  \n>  ${controls}\n> diff --git a/src/py/libcamera/py_properties_generated.cpp.in b/src/py/libcamera/py_properties_generated.cpp.in\n> index 87bc5e5d937e..1fa1967a9672 100644\n> --- a/src/py/libcamera/py_properties_generated.cpp.in\n> +++ b/src/py/libcamera/py_properties_generated.cpp.in\n> @@ -26,7 +26,6 @@ ${vendors_class_def}\n>  void init_py_properties_generated(py::module& m)\n>  {\n>  \tauto controls = py::class_<PyProperties>(m, \"properties\");\n> -\tauto draft = py::class_<PyDraftProperties>(controls, \"draft\");\n>  ${vendors_defs}\t\n>  \n>  ${controls}\n> diff --git a/utils/gen-controls.py b/utils/gen-controls.py\n> index a10b9fdc19ee..3ae48ca3e0bc 100755\n> --- a/utils/gen-controls.py\n> +++ b/utils/gen-controls.py\n> @@ -84,11 +84,6 @@ class Control(object):\n>          \"\"\"Is the control an enumeration\"\"\"\n>          return self.__enum_values is not None\n>  \n> -    @property\n> -    def is_draft(self):\n> -        \"\"\"Is the control a draft control\"\"\"\n> -        return self.__data.get('draft') is not None\n> -\n>      @property\n>      def vendor(self):\n>          \"\"\"The vendor string, or None\"\"\"\n> @@ -99,12 +94,6 @@ class Control(object):\n>          \"\"\"The control name (CamelCase)\"\"\"\n>          return self.__name\n>  \n> -    @property\n> -    def q_name(self):\n> -        \"\"\"The control name, qualified with a namespace\"\"\"\n> -        ns = 'draft::' if self.is_draft else ''\n> -        return ns + self.__name\n> -\n>      @property\n>      def type(self):\n>          typ = self.__data.get('type')\n> @@ -159,7 +148,7 @@ ${description}\n>  \n>          vendor = ctrl.vendor\n>          if vendor is None:\n> -            vendor = 'draft' if ctrl.is_draft else 'libcamera'\n> +            vendor = 'libcamera'\n>  \n>          if vendor not in ctrls_doc:\n>              ctrls_doc[vendor] = []\n> @@ -211,7 +200,7 @@ ${description}\n>          target_doc.append(doc_template.substitute(info))\n>          target_def.append(def_template.substitute(info))\n>  \n> -        target_ctrls_map.append('\\t{ ' + id_name + ', &' + ctrl.q_name + ' },')\n> +        target_ctrls_map.append('\\t{ ' + id_name + ', &' + ctrl.name + ' },')\n>  \n>      vendor_ctrl_doc_sub = []\n>      vendor_ctrl_template = string.Template('''\n> @@ -221,11 +210,11 @@ ${vendor_controls_str}\n>  \n>  } /* namespace ${vendor} */''')\n>  \n> -    for vendor in [v for v in ctrls_map.keys() if v not in ['libcamera', 'draft']]:\n> +    for vendor in [v for v in ctrls_map.keys() if v != 'libcamera']:\n>          vendor_ctrl_doc_sub.append(vendor_ctrl_template.substitute({'vendor': vendor, 'vendor_controls_str': '\\n\\n'.join(ctrls_doc[vendor])}))\n>  \n>      vendor_ctrl_def_sub = []\n> -    for vendor in [v for v in ctrls_map.keys() if v not in ['libcamera', 'draft']]:\n> +    for vendor in [v for v in ctrls_map.keys() if v != 'libcamera']:\n>          vendor_ctrl_def_sub.append(vendor_ctrl_template.substitute({'vendor': vendor, 'vendor_controls_str': '\\n'.join(ctrls_def[vendor])}))\n>  \n>      vendor_ctrl_map_sub = []\n> @@ -243,16 +232,14 @@ ${vendor_controls_map}\n>  } /* namespace ${vendor} */\n>  ''')\n>  \n> -    for vendor in [v for v in ctrls_map.keys() if v not in ['libcamera', 'draft']]:\n> +    for vendor in [v for v in ctrls_map.keys() if v != 'libcamera']:\n>          vendor_ctrl_map_sub.append(vendor_ctrl_template.substitute({'vendor': vendor,\n>                                                                      'vendor_controls_map': '\\n'.join(ctrls_map[vendor])}))\n>  \n>      return {\n>          'controls_doc': '\\n\\n'.join(ctrls_doc['libcamera']),\n>          'controls_def': '\\n'.join(ctrls_def['libcamera']),\n> -        'draft_controls_doc': '\\n\\n'.join(ctrls_doc['draft']),\n> -        'draft_controls_def': '\\n\\n'.join(ctrls_def['draft']),\n> -        'controls_map': '\\n'.join(ctrls_map['libcamera'] + ctrls_map['draft']),\n> +        'controls_map': '\\n'.join(ctrls_map['libcamera']),\n>          'vendor_controls_doc': '\\n'.join(vendor_ctrl_doc_sub),\n>          'vendor_controls_def': '\\n'.join(vendor_ctrl_def_sub),\n>          'vendor_controls_map': '\\n'.join(vendor_ctrl_map_sub),\n> @@ -274,7 +261,7 @@ def generate_h(controls, mode, ranges):\n>  \n>          vendor = ctrl.vendor\n>          if vendor is None:\n> -            vendor = 'draft' if ctrl.is_draft else 'libcamera'\n> +            vendor = 'libcamera'\n>  \n>          if vendor not in ctrls:\n>              if vendor not in ranges.keys():\n> @@ -283,8 +270,7 @@ def generate_h(controls, mode, ranges):\n>              ids[vendor] = []\n>              ctrls[vendor] = []\n>  \n> -        # Core and draft controls use the same ID value\n> -        target_ids = ids['libcamera'] if vendor in ['libcamera', 'draft'] else ids[vendor]\n> +        target_ids = ids[vendor]\n>          target_ids.append('\\t' + id_name + ' = ' + str(id_value[vendor]) + ',')\n>  \n>          info = {\n> @@ -292,11 +278,7 @@ def generate_h(controls, mode, ranges):\n>              'type': ctrl.type,\n>          }\n>  \n> -        target_ctrls = ctrls['libcamera']\n> -        if ctrl.is_draft:\n> -            target_ctrls = ctrls['draft']\n> -        elif vendor != 'libcamera':\n> -            target_ctrls = ctrls[vendor]\n> +        target_ctrls = ctrls[vendor]\n>  \n>          if ctrl.is_enum:\n>              target_ctrls.append(enum_template_start.substitute(info))\n> @@ -337,7 +319,7 @@ ${vendor_controls}\n>  ''')\n>  \n>      vendor_sub = []\n> -    for vendor in [v for v in ctrls.keys() if v not in ['libcamera', 'draft']]:\n> +    for vendor in [v for v in ctrls.keys() if v != 'libcamera']:\n>          vendor_sub.append(vendor_template.substitute({'mode': mode.upper(),\n>                                                        'vendor': vendor,\n>                                                        'vendor_def': vendor.upper(),\n> @@ -347,7 +329,6 @@ ${vendor_controls}\n>      return {\n>          'ids': '\\n'.join(ids['libcamera']),\n>          'controls': '\\n'.join(ctrls['libcamera']),\n> -        'draft_controls': '\\n'.join(ctrls['draft']),\n>          'vendor_controls': '\\n'.join(vendor_sub)\n>      }\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 7456FC3200\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 20 Nov 2023 04:18:41 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id D0381629BD;\n\tMon, 20 Nov 2023 05:18:40 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id BABD1629B6\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 20 Nov 2023 05:18:39 +0100 (CET)","from pendragon.ideasonboard.com (213-243-189-158.bb.dnainternet.fi\n\t[213.243.189.158])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 2396574A;\n\tMon, 20 Nov 2023 05:18:10 +0100 (CET)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1700453920;\n\tbh=lR7KBk6MOwqXv6V6E5nOAudv8cHeLfCzwbe5+dYE2+8=;\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=mcil64Q6EUh7OpE+5B/gPov3XRBm+fMiwQHcxRXmuNBfb0Z98kkOyQUKlpp3Fkxwq\n\tUsmGk5QmGtQ8f9C3aOFqlBz3b5dHe2Ma+46CJZanEofrjEKT12NfDofQ2aQhpAN0Ls\n\tGT8i42cmdj8KH0Zblak8PdXxnON76oEO4kX/gg3vnmunFsUgrlYIsDEf+b7Pp4542u\n\tY9oVZXJqLaAImtB/MT05gvqgKpRWG8yWQTGeud2sGj1En/38etzaXbn6XZ7b0mYo7h\n\tZr7SMVRLeWGBvSRN0T6rs24S5omA4VloSrldWhV9Q9KBXRxdskPi4bxn3BjLYNljuL\n\tOs95gX7KWnRUw==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1700453890;\n\tbh=lR7KBk6MOwqXv6V6E5nOAudv8cHeLfCzwbe5+dYE2+8=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=ob0GCnvXEk264fT4HA9JvpRVNDsWli9D26ajcXFcRW4SONFuuN7VxQxf5IIJpohbR\n\tFS614jnNfQexcm/+UGwW0c9XuGGow5Z4BHQv2vchTenO4hxZGQSbtzgtE6dbrk5iKN\n\tBdtvH/MLAjzSq/XUxjON4JHs32dX2+TVhPLmKGpE="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"ob0GCnvX\"; dkim-atps=neutral","Date":"Mon, 20 Nov 2023 06:18:46 +0200","To":"Naushir Patuck <naush@raspberrypi.com>","Message-ID":"<20231120041846.GH524@pendragon.ideasonboard.com>","References":"<20231110110002.21381-1-naush@raspberrypi.com>\n\t<20231110110002.21381-5-naush@raspberrypi.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<20231110110002.21381-5-naush@raspberrypi.com>","Subject":"Re: [libcamera-devel] [PATCH v1 4/5] libcamera: controls: Use\n\tvendor tags for draft controls and properties","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>"}}]