From patchwork Wed Feb 9 07:19:09 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hanlin Chen X-Patchwork-Id: 15345 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 0A6FDC3257 for ; Wed, 9 Feb 2022 07:19:33 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id CA611610EA; Wed, 9 Feb 2022 08:19:30 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=chromium.org header.i=@chromium.org header.b="OrBOP5Sq"; dkim-atps=neutral Received: from mail-pl1-x62a.google.com (mail-pl1-x62a.google.com [IPv6:2607:f8b0:4864:20::62a]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 1406860E66 for ; Wed, 9 Feb 2022 08:19:28 +0100 (CET) Received: by mail-pl1-x62a.google.com with SMTP id 10so1489625plj.1 for ; Tue, 08 Feb 2022 23:19:28 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=WAjeWglpTXXvm2qM30sZOk2hyFk1CLMYUo2yD83YX/c=; b=OrBOP5SqAhVlAq4j9ezYn1F5bNOWjYqmAUH8QwFHOjqrgJOV3QfEe08xLLeCjAWJla C7CTQi5vRQiQWoKTig6/FdVg7ZFPZmWxMER6HY3ef/fMttW4Ef+Vjk5tZAQG9Eyhbfej KYLIN00ZQ0UhD19FkjBgtP55TZjuJoAuJ4Whw= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=WAjeWglpTXXvm2qM30sZOk2hyFk1CLMYUo2yD83YX/c=; b=uREzoUyWnJH1maZR71PxlLIr/2uc+k7PzK/ghN5TEhkhlfhUNCajqzy1/tBdpOsrNF nhOViCbxcm3+St/t8WRHdRzUakWqd+q+A1dMyqxZ/6Y0Rib6+44OkR0wG10D0Qt+0DUY AuTXnJ//CxMjmJdasgMKmahFML1q7y5wMbAhur1BEGsAOF7SF6lOl0rFp6BaV8a+v7eo a0XbtrSuSgCoMNMjsAZX3qpuH4M+M1XT0laQ/76ukWVrGjIpIKwmKH9BO2QMNtFJuNJ3 PoaMI3Sevj83b9bX1mLc+6mO5tCwdSMdGwzUZmWRgraaUiAr5Z+qH3Sssitsg+fIvKZL EutQ== X-Gm-Message-State: AOAM530Owy3Qo5EHOMtYGXHlXqV+5sAb5so5+caFYFdv3BQsc6KPwXpu OexHNiYNnCLJSfLpY4SchsWeomQJ3y9D/A== X-Google-Smtp-Source: ABdhPJw4ntJsUnKrbj1nAHG1nMO9enanzBKo5sV+qQCW02aN9txXeHOX1HmfFZ3gqFyQp0/X9ZBLOA== X-Received: by 2002:a17:90a:7e15:: with SMTP id i21mr1980775pjl.74.1644391166337; Tue, 08 Feb 2022 23:19:26 -0800 (PST) Received: from localhost ([2401:fa00:1:10:a5cf:9a43:1c6d:824]) by smtp.gmail.com with UTF8SMTPSA id a5sm1900138pfh.101.2022.02.08.23.19.25 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Tue, 08 Feb 2022 23:19:26 -0800 (PST) From: Han-Lin Chen To: libcamera-devel@lists.libcamera.org Date: Wed, 9 Feb 2022 15:19:09 +0800 Message-Id: <20220209071917.559993-2-hanlinchen@chromium.org> X-Mailer: git-send-email 2.35.0.263.gb82422642f-goog In-Reply-To: <20220209071917.559993-1-hanlinchen@chromium.org> References: <20220209071917.559993-1-hanlinchen@chromium.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 1/9] libcamera: Introduce option to customize behavior for a camera module X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Introduce option to customize behavior for a camera module, which might alter the camera characteristic and should be set before inspecting it. Re-use the Control generation infrastructure to generate options and define the first 'PipelineConfigFile' option, which allows user to specify preference for pipeline configuration. Signed-off-by: Han-Lin Chen --- include/libcamera/ipa/ipa_controls.h | 1 + include/libcamera/meson.build | 3 +- include/libcamera/option_ids.h.in | 36 +++++++++++++++++ src/libcamera/control_serializer.cpp | 12 ++++++ src/libcamera/option_ids.cpp.in | 58 ++++++++++++++++++++++++++++ src/libcamera/option_ids.yaml | 16 ++++++++ 6 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 include/libcamera/option_ids.h.in create mode 100644 src/libcamera/option_ids.cpp.in create mode 100644 src/libcamera/option_ids.yaml diff --git a/include/libcamera/ipa/ipa_controls.h b/include/libcamera/ipa/ipa_controls.h index da1a7596..8173a2e5 100644 --- a/include/libcamera/ipa/ipa_controls.h +++ b/include/libcamera/ipa/ipa_controls.h @@ -18,6 +18,7 @@ extern "C" { enum ipa_controls_id_map_type { IPA_CONTROL_ID_MAP_CONTROLS, IPA_CONTROL_ID_MAP_PROPERTIES, + IPA_CONTROL_ID_MAP_OPTIONS, IPA_CONTROL_ID_MAP_V4L2, }; diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build index 408b7acf..a9b71bc3 100644 --- a/include/libcamera/meson.build +++ b/include/libcamera/meson.build @@ -31,10 +31,11 @@ install_headers(libcamera_public_headers, libcamera_headers_install_dir = get_option('includedir') / libcamera_include_dir -# control_ids.h and property_ids.h +# control_ids.h, property_ids.h, and option_ids.h control_source_files = [ 'control_ids', 'property_ids', + 'option_ids', ] control_headers = [] diff --git a/include/libcamera/option_ids.h.in b/include/libcamera/option_ids.h.in new file mode 100644 index 00000000..53058a4b --- /dev/null +++ b/include/libcamera/option_ids.h.in @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2022, Google Inc. + * + * option_ids.h - Option ID list + * + * This file is auto-generated. Do not edit. + */ + +#pragma once + +#include + +#include + +namespace libcamera { + +namespace options { + +enum { +${ids} +}; + +${controls} + +namespace draft { + +${draft_controls} + +} /* namespace draft */ + +extern const ControlIdMap options; + +} /* namespace options */ + +} /* namespace libcamera */ diff --git a/src/libcamera/control_serializer.cpp b/src/libcamera/control_serializer.cpp index e87d2362..dc8d2906 100644 --- a/src/libcamera/control_serializer.cpp +++ b/src/libcamera/control_serializer.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -239,6 +240,8 @@ int ControlSerializer::serialize(const ControlInfoMap &infoMap, enum ipa_controls_id_map_type idMapType; if (idmap == &controls::controls) idMapType = IPA_CONTROL_ID_MAP_CONTROLS; + else if (idmap == &options::options) + idMapType = IPA_CONTROL_ID_MAP_OPTIONS; else if (idmap == &properties::properties) idMapType = IPA_CONTROL_ID_MAP_PROPERTIES; else @@ -333,6 +336,8 @@ int ControlSerializer::serialize(const ControlList &list, enum ipa_controls_id_map_type idMapType; if (idmap == &controls::controls) idMapType = IPA_CONTROL_ID_MAP_CONTROLS; + else if (idmap == &options::options) + idMapType = IPA_CONTROL_ID_MAP_OPTIONS; else if (idmap == &properties::properties) idMapType = IPA_CONTROL_ID_MAP_PROPERTIES; else @@ -458,6 +463,9 @@ ControlInfoMap ControlSerializer::deserialize(ByteStreamBuffer & case IPA_CONTROL_ID_MAP_CONTROLS: idMap = &controls::controls; break; + case IPA_CONTROL_ID_MAP_OPTIONS: + idMap = &options::options; + break; case IPA_CONTROL_ID_MAP_PROPERTIES: idMap = &properties::properties; break; @@ -589,6 +597,10 @@ ControlList ControlSerializer::deserialize(ByteStreamBuffer &buffer idMap = &controls::controls; break; + case IPA_CONTROL_ID_MAP_OPTIONS: + idMap = &options::options; + break; + case IPA_CONTROL_ID_MAP_PROPERTIES: idMap = &properties::properties; break; diff --git a/src/libcamera/option_ids.cpp.in b/src/libcamera/option_ids.cpp.in new file mode 100644 index 00000000..adfd0633 --- /dev/null +++ b/src/libcamera/option_ids.cpp.in @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2022, Google Inc. + * + * option_ids.cpp : Option ID list + * + * This file is auto-generated. Do not edit. + */ + +#include + +/** + * \file option_ids.h + * \brief Camera option identifiers + */ + +namespace libcamera { + +/** + * \brief Namespace for libcamera options + */ +namespace options { + +${controls_doc} + +/** + * \brief Namespace for libcamera draft options + */ +namespace draft { + +${draft_controls_doc} + +} /* namespace draft */ + +#ifndef __DOXYGEN__ +/* + * Keep the options definitions hidden from doxygen as it incorrectly parses + * them as functions. + */ +${controls_def} + +namespace draft { + +${draft_controls_def} + +} /* namespace draft */ +#endif + +/** + * \brief List of all supported libcamera options + */ +extern const ControlIdMap options { +${controls_map} +}; + +} /* namespace options */ + +} /* namespace libcamera */ diff --git a/src/libcamera/option_ids.yaml b/src/libcamera/option_ids.yaml new file mode 100644 index 00000000..3f9304be --- /dev/null +++ b/src/libcamera/option_ids.yaml @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# Copyright (C) 2022, Google Inc. +# +%YAML 1.2 +--- +controls: + - PipelineConfigFile: + type: string + description: | + Some platform supports customizing pipeline configuration preference + for a camera module. The structure and contents of the configuration + file is specific to each platform in YAML format. The PipelineConfigFile + option can be used to specify the path to load the configuration file. + +... From patchwork Wed Feb 9 07:19:10 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hanlin Chen X-Patchwork-Id: 15346 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id C8353C3262 for ; Wed, 9 Feb 2022 07:19:34 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 32390610EF; Wed, 9 Feb 2022 08:19:31 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=chromium.org header.i=@chromium.org header.b="RC/v7GzU"; dkim-atps=neutral Received: from mail-pl1-x62a.google.com (mail-pl1-x62a.google.com [IPv6:2607:f8b0:4864:20::62a]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id ED3DA60E66 for ; Wed, 9 Feb 2022 08:19:29 +0100 (CET) Received: by mail-pl1-x62a.google.com with SMTP id x3so1479000pll.3 for ; Tue, 08 Feb 2022 23:19:29 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=26FibggvqYPF43PLWR4Cxku84PjjPkOG3Xu8PZWi+7Q=; b=RC/v7GzUm4/FhiQhjjzwH7i+cc/xop0+t7cQkDYk4NxUJh/X2km/T3wjvcDstmPpiR pCrXLaGvPdIitVGn3VzZhuYl/09BCCamw/KVXsLxH7A971G8PyGIbaxL/XhYPoy7MirA yYZNcrLPtDDl4y+UsvoKKJSbWOxOe+BSTLiuI= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=26FibggvqYPF43PLWR4Cxku84PjjPkOG3Xu8PZWi+7Q=; b=sGKzCiQ2IPdcyHgxU/4MYrWQSvPZu0d46bE3Bn7sx2RzyHcpfz4yrQoluxQI1gR81b IfD/ePdnlRVYktXQ+wq3p1OtZLHKe8zM/97piirHkeW3ZwNzS1b04EfINcVQdKrTe313 TzS+Yhby49l8adEXeE9MOJPI7kczPNncH8fGZU4iQypkt/x1kW/e/UNQvSFd1FajtyVO gtEzgj+MH/0+wsKyHWVGEZHFd+43Wa8PcqnjWVTF9+SXfsk4C5elVO6y4vfwtGe1p9hY r5sdb6mEBwQn6KBLBxgyed4zCzs0v8tsLk9Ygf+Aw5QeTnAdnt96cnp2JFDB/bJhHNNK 9PJA== X-Gm-Message-State: AOAM532S1cY9W07C706/hFjhEWDmo1JHgGo5cYXL7KDGJSNOJpHG7rc9 UQ2aMW8XokkOC6RExnfpw31biRRZgCix4Q== X-Google-Smtp-Source: ABdhPJyyBucaJsgWM99vyP2VEd+Etg1CUNup3Hxfoixt6z3MAY0Ki4lViR5cxvD9atu+vS/uj+1nEA== X-Received: by 2002:a17:902:e80c:: with SMTP id u12mr1125550plg.159.1644391168337; Tue, 08 Feb 2022 23:19:28 -0800 (PST) Received: from localhost ([2401:fa00:1:10:a5cf:9a43:1c6d:824]) by smtp.gmail.com with UTF8SMTPSA id n22sm18873823pfu.77.2022.02.08.23.19.27 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Tue, 08 Feb 2022 23:19:28 -0800 (PST) From: Han-Lin Chen To: libcamera-devel@lists.libcamera.org Date: Wed, 9 Feb 2022 15:19:10 +0800 Message-Id: <20220209071917.559993-3-hanlinchen@chromium.org> X-Mailer: git-send-email 2.35.0.263.gb82422642f-goog In-Reply-To: <20220209071917.559993-1-hanlinchen@chromium.org> References: <20220209071917.559993-1-hanlinchen@chromium.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 2/9] libcamera: Add options() and setOptions() operations to Camera X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add options() and setOptions() operations to Camera. Both operations takes a camera instance and reads or writes the options to it. Because an option might change the supported characteristics of a camera module, it could only be set before configuring. Signed-off-by: Han-Lin Chen --- include/libcamera/camera.h | 3 + include/libcamera/internal/camera.h | 2 + include/libcamera/internal/pipeline_handler.h | 2 + src/libcamera/camera.cpp | 59 +++++++++++++++++++ src/libcamera/pipeline_handler.cpp | 26 ++++++++ 5 files changed, 92 insertions(+) diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h index 5bb06584..1bb80a6d 100644 --- a/include/libcamera/camera.h +++ b/include/libcamera/camera.h @@ -98,9 +98,12 @@ public: Signal requestCompleted; Signal<> disconnected; + int setOptions(const ControlList *options = nullptr); + int acquire(); int release(); + const ControlInfoMap &options() const; const ControlInfoMap &controls() const; const ControlList &properties() const; diff --git a/include/libcamera/internal/camera.h b/include/libcamera/internal/camera.h index 597426a6..c3cb1865 100644 --- a/include/libcamera/internal/camera.h +++ b/include/libcamera/internal/camera.h @@ -34,6 +34,8 @@ public: PipelineHandler *pipe() { return pipe_.get(); } std::list queuedRequests_; + + ControlInfoMap optionInfo_; ControlInfoMap controlInfo_; ControlList properties_; diff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h index c3e4c258..184e4b9a 100644 --- a/include/libcamera/internal/pipeline_handler.h +++ b/include/libcamera/internal/pipeline_handler.h @@ -45,6 +45,8 @@ public: MediaDevice *acquireMediaDevice(DeviceEnumerator *enumerator, const DeviceMatch &dm); + virtual int setOptions(Camera *camera, const ControlList *options); + bool lock(); void unlock(); diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp index bb856d60..9e95e6cc 100644 --- a/src/libcamera/camera.cpp +++ b/src/libcamera/camera.cpp @@ -480,6 +480,16 @@ Camera::Private::~Private() * well, when implementing official support for control info updates. */ +/** + * \var Camera::Private::optionInfo_ + * \brief The set of options supported by the camera + * + * The optionInfo_ information shall be initialised by the pipeline handler when + * creating the camera. + * + * This member was meant to stay constant after the camera is created. + */ + /** * \var Camera::Private::properties_ * \brief The list of properties supported by the camera @@ -802,6 +812,40 @@ int Camera::exportFrameBuffers(Stream *stream, buffers); } +/** + * \brief Set Options for a camera. + * \param[in] options Options to be applied before configuring a camera + * + * Prior to configuring a camera, it's optional to set options to a camera to + * change its characteristics or internal behaviors. The supported options are + * depended on the platform. The caller should iterate supported Options by + * options() before setting them. + * + * Options might change the internal behavior of configure() and capturing + * images, and thus the function can only be called before configure(). + * It's encouraged to re-check the results of controls(), properties() and + * generateConfiguration() after setting options to a camera. + * + * \context This function shall be synchronized by the caller with other + * functions that affect the camera state. + * + * \return 0 on success or a negative error code otherwise + * \retval -EACCES The camera is not in a state where it can set options + * \retval -EINVAL The options is not valid + */ +int Camera::setOptions(const ControlList *options) +{ + Private *const d = _d(); + + int ret = d->isAccessAllowed(Private::CameraAvailable, + Private::CameraAcquired); + if (ret < 0) + return -EACCES; + + return d->pipe_->invokeMethod(&PipelineHandler::setOptions, + ConnectionTypeBlocking, this, options); +} + /** * \brief Acquire the camera device for exclusive access * @@ -894,6 +938,21 @@ const ControlInfoMap &Camera::controls() const return _d()->controlInfo_; } +/** + * \brief Retrieve the list of options supported by the camera + * + * The list of options supported by the camera and their associated + * constraints remain constant through the lifetime of the Camera object. + * + * \context This function is \threadsafe. + * + * \return A ControlInfoMap listing the options supported by the camera + */ +const ControlInfoMap &Camera::options() const +{ + return _d()->optionInfo_; +} + /** * \brief Retrieve the list of properties of the camera * diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp index 7ebd76ad..1f2a6d7e 100644 --- a/src/libcamera/pipeline_handler.cpp +++ b/src/libcamera/pipeline_handler.cpp @@ -142,6 +142,32 @@ MediaDevice *PipelineHandler::acquireMediaDevice(DeviceEnumerator *enumerator, return media.get(); } +/** + * \fn PipelineHandler::setOptions() + * \brief Set options for camera + * \param[in] camera The camera to set options + * \param[in] options Options to be applied to the Camera + * + * Set options to the camera. The supported options are different for each + * pipeline handler. The intended caller of the this function could iterate + * options supported by the camera with Camera::options(). + * + * The intended caller of this function is the Camera class which will in turn + * be called from the application to indicate that a set of options should be + * applied. The user should check supported options with Camera::options() + * before setting them. + * + * \context This function is called from the CameraManager thread. + * + * \return 0 on success or a negative error code otherwise + */ +int PipelineHandler::setOptions([[maybe_unused]] Camera *camera, + [[maybe_unused]] const ControlList *options) +{ + /* The default implementation which supported no options. */ + return -EINVAL; +} + /** * \brief Lock all media devices acquired by the pipeline * From patchwork Wed Feb 9 07:19:11 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hanlin Chen X-Patchwork-Id: 15347 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id C69A3C3261 for ; Wed, 9 Feb 2022 07:19:34 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id E2C85610E7; Wed, 9 Feb 2022 08:19:33 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=chromium.org header.i=@chromium.org header.b="RhwLRR3g"; dkim-atps=neutral Received: from mail-pj1-x102d.google.com (mail-pj1-x102d.google.com [IPv6:2607:f8b0:4864:20::102d]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 846CD60E66 for ; Wed, 9 Feb 2022 08:19:32 +0100 (CET) Received: by mail-pj1-x102d.google.com with SMTP id c5-20020a17090a1d0500b001b904a7046dso2743072pjd.1 for ; Tue, 08 Feb 2022 23:19:32 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=vSxjdnVBCGYsMUphGRb24V2CxxmnLtecgOxg8GmfZZY=; b=RhwLRR3gn3AQ3bl4iyHSFE+qRzHUi8G80lOxF9H892aUe92uvNqPhoTjOTKjAapDQO 1bNZz1glpgAoN7bZhW2fSmxppJxZqFSmwMM8IL1VrZoeMkuBZ1EvkolhkihM/Bo6+AJt c16eKA7q8wsHgo7MFZg50IRYtlb01lpC9R1fk= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=vSxjdnVBCGYsMUphGRb24V2CxxmnLtecgOxg8GmfZZY=; b=HKgKSb88XytY0YjiFMVbrJHnWgzyN0N0biGzTzqunIji+nWOyZyJUhJGb7RepKZIy9 QiDMUdQ5Z+MCDDkg0DACYU2ciBUH2xHGOr39lHL4SizGQHjDFR+gsWaNairVKGj4ghLP htPGbu+c2gUOoJJKX1YDwwVuA7Ng/HBaZn4Pme7PEYv3GNY9pwxhHRmQAW7o7e16761N +Xrb7RFlZOlYUowkrePXZXz/oHVpqi7ndJ7n2gc4gehn9KMfsEd5OIOtPOHzxVLKZrqd uzDvptQa7fEG6+w6z3t91YfWSCg/8qE+FzqK1U8UaC7iTATbQXKw8hjtj4GDEvlVUbfl haUA== X-Gm-Message-State: AOAM5331ARp+LKqAVDPjKk38fTWMfExDVX5ja6ONQs+57u2Anl2Jp1QI xwsfVaBCyM47A++ySSI8BglaCqOxfsMk4Q== X-Google-Smtp-Source: ABdhPJy191/5Cj3Z98Hd1pUCFUGte7aVicEEUsQ9Ov2T3o7JfPcXCcQMMMnVdvEnlkfYO3h9rKPYrg== X-Received: by 2002:a17:902:a708:: with SMTP id w8mr838510plq.101.1644391170462; Tue, 08 Feb 2022 23:19:30 -0800 (PST) Received: from localhost ([2401:fa00:1:10:a5cf:9a43:1c6d:824]) by smtp.gmail.com with UTF8SMTPSA id ml19sm5266237pjb.52.2022.02.08.23.19.29 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Tue, 08 Feb 2022 23:19:30 -0800 (PST) From: Han-Lin Chen To: libcamera-devel@lists.libcamera.org Date: Wed, 9 Feb 2022 15:19:11 +0800 Message-Id: <20220209071917.559993-4-hanlinchen@chromium.org> X-Mailer: git-send-email 2.35.0.263.gb82422642f-goog In-Reply-To: <20220209071917.559993-1-hanlinchen@chromium.org> References: <20220209071917.559993-1-hanlinchen@chromium.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 3/9] libcamera: Introduce YamlParser as a helper to parse yaml files X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Introduce YamlParser as a helper to convert contents of a yaml file to a tree based structure for easier reading, and to avoid writing parser with raw yaml tokens. The class is based on libyaml, and only support reading but writing a yaml file. The interface is inspired by Json::Value class from jsoncpp: http://jsoncpp.sourceforge.net/class_json_1_1_value.html Signed-off-by: Han-Lin Chen --- README.rst | 2 +- include/libcamera/internal/meson.build | 1 + include/libcamera/internal/yaml_parser.h | 82 +++ src/libcamera/meson.build | 3 + src/libcamera/yaml_parser.cpp | 796 +++++++++++++++++++++++ 5 files changed, 883 insertions(+), 1 deletion(-) create mode 100644 include/libcamera/internal/yaml_parser.h create mode 100644 src/libcamera/yaml_parser.cpp diff --git a/README.rst b/README.rst index ca8a97cb..4a2a451a 100644 --- a/README.rst +++ b/README.rst @@ -60,7 +60,7 @@ Meson Build system: [required] pip3 install --user --upgrade meson for the libcamera core: [required] - python3-yaml python3-ply python3-jinja2 + libyaml-dev python3-yaml python3-ply python3-jinja2 for IPA module signing: [required] libgnutls28-dev openssl diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build index c9e055d4..7a780d48 100644 --- a/include/libcamera/internal/meson.build +++ b/include/libcamera/internal/meson.build @@ -42,4 +42,5 @@ libcamera_internal_headers = files([ 'v4l2_pixelformat.h', 'v4l2_subdevice.h', 'v4l2_videodevice.h', + 'yaml_parser.h', ]) diff --git a/include/libcamera/internal/yaml_parser.h b/include/libcamera/internal/yaml_parser.h new file mode 100644 index 00000000..7ba7c52c --- /dev/null +++ b/include/libcamera/internal/yaml_parser.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2022, Google Inc. + * + * yaml_parser.h - libcamera yaml parsing helper + */ + +#pragma once + +#include +#include +#include + +#include + +#include + +namespace libcamera { + +class YamlObject +{ +public: + YamlObject() = default; + ~YamlObject() = default; + + YamlObject(const YamlObject &) = delete; + YamlObject &operator=(const YamlObject &) = delete; + + bool isValue() const + { + return type_ == VALUE; + } + bool isList() const + { + return type_ == LIST; + } + bool isDictionary() const + { + return type_ == DICTIONARY; + } + + bool asBool(bool defaultValue = false, bool *ok = nullptr) const; + double asDouble(double defaultValue = 0.0, bool *ok = nullptr) const; + int32_t asInt32(int32_t defaultValue = 0, bool *ok = nullptr) const; + uint32_t asUint32(uint32_t defaultValue = 0, bool *ok = nullptr) const; + std::string asString(std::string defaultValue = "", bool *ok = nullptr) const; + Size asSize(Size defaultValue = Size(), bool *ok = nullptr) const; + + int length() const; + const YamlObject &operator[](int index) const; + + bool isMember(const std::string &key) const; + const YamlObject &get(const std::string &key) const; + std::vector getMemberNames() const; + +private: + friend class YamlParser; + + enum PropertyType { + DICTIONARY, + LIST, + VALUE, + } type_; + + std::string value_; + std::vector> list_; + std::map> dictionary_; +}; + +class YamlParser final : public Extensible +{ + LIBCAMERA_DECLARE_PRIVATE() + +public: + YamlParser(); + int ParseAsYamlObject(FILE *fh, YamlObject &yamlObject); + +private: + int ParseNextYamlObject(YamlObject &yamlObject); +}; + +} /* namespace libcamera */ diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index 26912ca1..f8e18e03 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -46,6 +46,7 @@ libcamera_sources = files([ 'v4l2_pixelformat.cpp', 'v4l2_subdevice.cpp', 'v4l2_videodevice.cpp', + 'yaml_parser.cpp', ]) libcamera_sources += libcamera_public_headers @@ -66,6 +67,7 @@ subdir('proxy') libdl = cc.find_library('dl') libgnutls = cc.find_library('gnutls', required : true) libudev = dependency('libudev', required : false) +libyaml = dependency('yaml-0.1', required : true) if libgnutls.found() config_h.set('HAVE_GNUTLS', 1) @@ -126,6 +128,7 @@ libcamera_deps = [ libgnutls, liblttng, libudev, + libyaml, ] # We add '/' to the build_rpath as a 'safe' path to act as a boolean flag. diff --git a/src/libcamera/yaml_parser.cpp b/src/libcamera/yaml_parser.cpp new file mode 100644 index 00000000..203d9cf4 --- /dev/null +++ b/src/libcamera/yaml_parser.cpp @@ -0,0 +1,796 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2022, Google Inc. + * + * yaml_parser.cpp - libcamera yaml parsing helper + */ + +#include "libcamera/internal/yaml_parser.h" + +#include + +#include + +#include + +/** + * \file libcamera/internal/yaml_parser.h + * \brief A yaml parser helper + */ + +namespace libcamera { + +LOG_DEFINE_CATEGORY(YamlParser) + +namespace { + +void setOk(bool *ok, bool result) +{ + if (ok) + *ok = result; +} + +} /* namespace */ + +/** + * \class YamlObject + * \brief A class representing the tree structure of the yaml content + * + * The YamlObject class represents the tree structure of a yaml content. A + * YamlObject can be a dictionary or list of YamlObjects or a value if a tree + * leaf. + */ + +/** + * \fn YamlObject::isValue() + * \brief Return whether the YamlObject is a value + * + * \return Whether the YamlObject is a value + */ + +/** + * \fn YamlObject::isList() + * \brief Return whether the YamlObject is a list + * + * \return Whether the YamlObject is a list + */ + +/** + * \fn YamlObject::isDictionary() + * \brief Return whether the YamlObject is a dictionary + * + * \return Whether the YamlObject is a dictionary + */ + +/** + * \fn YamlObject::asBool() + * \brief Helper function to parse the YamlObject as a bool value + * \param[in] defaultValue The default value when fail to parse + * \param[in] ok The result of whether the parse success + * + * Helper function to parse the YamlObject as a bool value. + * + * \return Value as a bool type + */ +bool YamlObject::asBool(bool defaultValue, bool *ok) const +{ + setOk(ok, false); + + if (type_ != VALUE) { + return defaultValue; + } + + if (value_ == "true") { + setOk(ok, true); + return true; + } else if (value_ == "false") { + setOk(ok, true); + return false; + } + + return defaultValue; +} + +/** + * \fn YamlObject::asInt32() + * \brief Helper function to parse the YamlObject as a int32_t value + * \param[in] defaultValue The default value when fail to parse + * \param[in] ok The result of whether the parse success + * + * Helper function to parse the YamlObject as a int32_t value. + * + * \return Value as a int32_t type + */ +int32_t YamlObject::asInt32(int32_t defaultValue, bool *ok) const +{ + setOk(ok, false); + + if (type_ != VALUE) + return defaultValue; + + int32_t value = std::strtol(value_.c_str(), nullptr, 10); + if (errno == ERANGE) + return defaultValue; + + setOk(ok, true); + return value; +} + +/** + * \fn YamlObject::asUint32() + * \brief Helper function to parse the YamlObject as a uint32_t value + * \param[in] defaultValue The default value when fail to parse + * \param[in] ok The result of whether the parse success + * + * Helper function to parse the YamlObject as a uint32_t value. + * + * \return Value as a uint32_t type + */ +uint32_t YamlObject::asUint32(uint32_t defaultValue, bool *ok) const +{ + setOk(ok, false); + + if (type_ != VALUE) { + return defaultValue; + } + + uint32_t value = std::strtoul(value_.c_str(), nullptr, 10); + if (errno == ERANGE) + return defaultValue; + + setOk(ok, true); + return value; +} + +/** + * \fn YamlObject::asDouble() + * \brief Helper function to parse the YamlObject as a double value + * \param[in] defaultValue The default value when fail to parse + * \param[in] ok The result of whether the parse success + * + * Helper function to parse the YamlObject as a double value. + * + * \return Value as a double type + */ +double YamlObject::asDouble(double defaultValue, bool *ok) const +{ + setOk(ok, false); + if (type_ != VALUE) + return defaultValue; + + double value = std::strtod(value_.c_str(), nullptr); + if (errno == ERANGE) + return defaultValue; + + setOk(ok, true); + return value; +} + +/** + * \fn YamlObject::asString() + * \brief Helper function to parse the YamlObject as a string value + * \param[in] defaultValue The default value when fail to parse + * \param[in] ok The result of whether the parse success + * + * Helper function to parse the YamlObject as a string value. + * + * \return Value as a string type + */ +std::string YamlObject::asString(std::string defaultValue, bool *ok) const +{ + if (type_ != VALUE) { + setOk(ok, false); + return defaultValue; + } + + return value_; +} + +/** + * \fn YamlObject::asSize() + * \brief Helper function to parse the YamlObject as a Size value + * \param[in] defaultValue The default value when fail to parse + * \param[in] ok The result of whether the parse success + * + * Helper function to parse the YamlObject as a Size value. The Size structure + * is represented as a list of two non-negative numbers. + * + * \return Value as a Size type + */ +Size YamlObject::asSize(Size defaultValue, bool *ok) const +{ + setOk(ok, false); + + if (type_ != LIST) + return defaultValue; + + if (list_.size() != 2) + return defaultValue; + + bool isInt = false; + int width = list_[0]->asUint32(0, &isInt); + if (!isInt) + return Size(0, 0); + + int height = list_[1]->asUint32(0, &isInt); + if (!isInt) + return Size(0, 0); + + setOk(ok, true); + return Size(width, height); +} + +/** + * \fn YamlObject::length() + * \brief Helper function to return length of the list + * + * Helper function to parse the YamlObject as a List and return the length of + * the List. + * + * \return The length as a list + */ +int YamlObject::length() const +{ + assert(type_ == LIST); + + return list_.size(); +} + +/** + * \fn YamlObject::operator[](int index) + * \brief Helper function to return an element of a list YamlObject by index + * + * Helper function to return an element of a list YamlObject by index. + * + * \return The YamlObject as an element of the list + */ +const YamlObject &YamlObject::operator[](int index) const +{ + assert(type_ == LIST); + + return *list_[index]; +} + +/** + * \fn YamlObject::isMember() + * \brief Helper function to check if an element of a dictionary exists + * + * Helper function to return an element of a list YamlObject by index. + * + * \return Whether an element exists + */ +bool YamlObject::isMember(const std::string &key) const +{ + assert(type_ == DICTIONARY); + + if (dictionary_.find(key) == dictionary_.end()) + return false; + + return true; +} + +/** + * \fn YamlObject::getMemberNames() + * \brief Helper function to get all member names of the dictionary + * + * Helper function to get all member names of the dictionary. + * + * \return A vector of string as the member names + */ +std::vector YamlObject::getMemberNames() const +{ + assert(type_ == DICTIONARY); + + std::vector memberNames; + for (auto &[key, _] : dictionary_) + memberNames.push_back(key); + + return memberNames; +} + +/** + * \fn YamlObject::get() + * \brief Helper function to get a member by name from the dictionary + * + * Helper function to get a member by name from the dictionary + * + * \return A YamlObject as a member + */ +const YamlObject &YamlObject::get(const std::string &key) const +{ + assert(type_ == DICTIONARY); + assert(isMember(key)); + + auto iter = dictionary_.find(key); + return *iter->second; +} + +class YamlParser::Private : public Extensible::Private +{ + LIBCAMERA_DECLARE_PUBLIC(YamlParser) + +public: + Private(); + ~Private(); + + using ItemParser = const std::function &; + using RecordParser = const std::function &; + + int initParser(FILE *fh); + void release(); + + int consumeDocumentStart(); + int consumeDocumentEnd(); + + int parseString(std::string &value); + int parseList(ItemParser parseItem); + int parseDictionary(RecordParser parseRecord); + +private: + int nextEvent(yaml_event_t &event); + int consume(yaml_event_type_t); + + int parseDictionaryOrList(bool isDictionary, + const std::function &parseItem); + + bool nextEventLoaded_; + yaml_event_t nextEvent_; + + bool parserValid_; + yaml_parser_t parser_; +}; + +/** + * \class YamlParser::Private + * \brief A Private class helps control event based parsing for yaml files. + * + * The YamlParser::Private class stores the internal yaml_parser_t and provides + * helper functions to do event based parsing for yaml files. + */ +YamlParser::Private::Private() + : nextEventLoaded_(false), parserValid_(false) +{ +} + +/** + * \class YamlParser::Private + * \brief Destructor of YamlParser::Private. + */ +YamlParser::Private::~Private() +{ + release(); +} + +/** + * \fn YamlParser::Private::initParser() + * \brief Initialize a parser with an opened file for parsing + * \param[in] fh The yaml file to parse + * + * Prior to parsing the yaml content, the YamlParser must be initialized with + * an opened FILE to create an internal parser. The FILE need to stay valid + * during the process. The release() should be called after use. + * + * \return 0 on success or a negative error code otherwise + * \retval -EINVAL The parser is failed to initialize + */ +int YamlParser::Private::initParser(FILE *fh) +{ + /* yaml_parser_initialize returns 1 when succeeded */ + if (!yaml_parser_initialize(&parser_)) { + LOG(YamlParser, Error) << "Failed to initialize yaml parser"; + return -EINVAL; + } + parserValid_ = true; + yaml_parser_set_input_file(&parser_, fh); + + return 0; +} + +/** + * \fn YamlParser::Private::release() + * \brief Release the internal parser + * + * After parsing the yaml content, The YamlParser::release() should be called + * to release the internal parser. + */ +void YamlParser::Private::release() +{ + if (nextEventLoaded_) { + yaml_event_delete(&nextEvent_); + nextEventLoaded_ = false; + } + + if (parserValid_) { + parserValid_ = false; + yaml_parser_delete(&parser_); + } +} + +/** + * \fn YamlParser::Private::consume() + * \brief Cosume the next event with an expected event type + * + * \param[in] type The expected event type to consume + * + * Consume next event and check whether next event has an expected type. + * + * \return 0 on success or a negative error code otherwise + * \retval -EINVAL The parser is failed to initialize + */ +int YamlParser::Private::consume(yaml_event_type_t type) +{ + yaml_event_t event; + int ret = nextEvent(event); + if (ret) + return ret; + + if (event.type != type) { + LOG(YamlParser, Error) + << "Expect event: " << type + << " but get " << event.type + << " at line: " << event.start_mark.line + << " column: " << event.start_mark.column; + return -EINVAL; + } + + yaml_event_delete(&event); + nextEventLoaded_ = false; + + return 0; +} + +/** + * \typedef YamlParser::Private::ItemParser + * \brief The functor to handle an item in a yaml list + */ + +/** + * \typedef YamlParser::Private::RecordParser + * \brief The functor to handle an item in a yaml dictionary + */ + +/** + * \fn YamlParser::Private::consumeDocumentStart() + * \brief Consume start of a yaml document + * + * Check yaml start of a yaml document. The function should be called and + * checked before parsing any content of the yaml file. + * + * \return 0 on success or a negative error code otherwise + * \retval -EINVAL The parser is failed to validate start of a yaml file + */ +int YamlParser::Private::consumeDocumentStart() +{ + if (consume(YAML_STREAM_START_EVENT)) + return -EINVAL; + + if (consume(YAML_DOCUMENT_START_EVENT)) + return -EINVAL; + + return 0; +} + +/** + * \fn YamlParser::Private::consumeDocumentEnd() + * \brief Consume end of a yaml document + * + * Check yaml end of a yaml document. The function should be called and + * checked after parsing any content of the yaml file. + * + * \return 0 on success or a negative error code otherwise + * \retval -EINVAL The parser is failed to validate end of a yaml file + */ +int YamlParser::Private::consumeDocumentEnd() +{ + if (consume(YAML_DOCUMENT_END_EVENT)) + return -EINVAL; + + if (consume(YAML_STREAM_END_EVENT)) + return -EINVAL; + + return 0; +} + +/** + * \fn YamlParser::Private::parseString() + * \brief Parse scalar and read its content as a string + * \param[in] value The string reference to fill value + * + * A helper function to peek the next event, check whether it's scalar type + * and read its content as a string. + * + * \return 0 on success or a negative error code otherwise + * \retval -EINVAL The parser is failed to initialize + */ +int YamlParser::Private::parseString(std::string &value) +{ + yaml_event_t event; + int ret = nextEvent(event); + if (ret) + return ret; + + if (event.type != YAML_SCALAR_EVENT) { + LOG(YamlParser, Error) << "Expect scalar at line: " + << event.start_mark.line + << " column: " + << event.start_mark.column; + return -EINVAL; + } + + value.assign(reinterpret_cast(event.data.scalar.value), + event.data.scalar.length); + + consume(YAML_SCALAR_EVENT); + + return 0; +} + +/** + * \fn YamlParser::Private::parseList() + * \brief Parse a list with an callback function to parse each item in the list + * \param[in] parseItem The functor to parse a single item in the list + * + * Start to parse a list from the current position and call back to parseItem + * for parsing each item in the list. The parseItem should return 0 on success + * or a negative error code otherwise. + * + * \return 0 on success or a negative error code otherwise + * \retval -EINVAL The parser is failed to parse the list + */ +int YamlParser::Private::parseList(ItemParser parseItem) +{ + return parseDictionaryOrList(false, parseItem); +} + +/** + * \fn YamlParser::Private::parseDictionary() + * \brief Parse a dictionary with an callback function to parse each item + * \param[in] parseRecord The functor to parse a single item in the dictionary + * + * Start to parse a dictionary from the current position and call back to + * parseRecord to parse value by a string argument as the key to the value. + * The parseRecord should return 0 on success or a negative error code + * otherwise. + * + * \return 0 on success or a negative error code otherwise + * \retval -EINVAL The parser is failed to parse the dictionary + */ +int YamlParser::Private::parseDictionary(RecordParser parseRecord) +{ + auto parseItem = [this, &parseRecord]() { + std::string key; + int ret = parseString(key); + if (ret) + return -EINVAL; + + return parseRecord(key); + }; + + return parseDictionaryOrList(true, parseItem); +} + +/** + * \fn YamlParser::Private::parseDictionaryOrList() + * \brief A helper function to abstract common part of parsing dictionary or list + * + * \param[in] isDictionary True for parsing a dictionary, and false for a list + * \param[in] parseItem The callback to handle an item + * + * A helper function to abstract parsing a item from a dictionary or a list. + * The differences of them in a yaml event stream are: + * + * 1. The start and end event type are different + * 2. There is a leading scalar string as key in the items of a dictionary + * + * The caller should handle the leading key string in its callback parseItem + * when it's a dictionary. + * + * \return 0 on success or a negative error code otherwise + * \retval -EINVAL The parser is failed to initialize + */ +int YamlParser::Private::parseDictionaryOrList(bool isDictionary, + const std::function &parseItem) +{ + yaml_event_type_t startEventType = YAML_SEQUENCE_START_EVENT; + yaml_event_type_t endEventType = YAML_SEQUENCE_END_EVENT; + + if (isDictionary) { + startEventType = YAML_MAPPING_START_EVENT; + endEventType = YAML_MAPPING_END_EVENT; + } + + if (consume(startEventType)) + return -EINVAL; + + /* + * Add a safety counter to make sure we don't loop indefinitely in case + * the configuration file is malformed. + */ + unsigned int sentinel = 1000; + yaml_event_t event; + do { + int ret = nextEvent(event); + if (ret) + return ret; + + if (event.type == endEventType) + return consume(endEventType); + + ret = parseItem(); + if (ret) + return ret; + + --sentinel; + } while (sentinel); + + if (!sentinel) + return -EINVAL; + + return 0; +} + +/** + * \fn YamlParser::Private::nextEvent() + * \brief Peek the next event + * + * \param[in] event The event reference to fill information + * + * Peek the next event in the current yaml event stream, and return -EINVAL when + * there is no more event. + * + * \return 0 on success or a negative error code otherwise + * \retval -EINVAL The parser is failed to initialize + */ +int YamlParser::Private::nextEvent(yaml_event_t &event) +{ + if (nextEventLoaded_) { + event = nextEvent_; + return 0; + } + + /* yaml_parser_parse returns 1 when succeeded */ + if (1 != yaml_parser_parse(&parser_, &nextEvent_)) { + return -EINVAL; + } + + nextEventLoaded_ = true; + event = nextEvent_; + + return 0; +} + +/** + * \class YamlParser + * \brief A helper class for parsing yaml files. + * + * The YamlParser class eases handling of parsing a yaml file by providing + * helper function to extract content yaml files into a tree base yaml object. + * + * Example usage 1: + * The following code illustrates how to parse the following yaml file: + * + * name: + * "John" + * numbers: + * - 1 + * - 2 + * + * @code + * + * YamlObject root; + * YamlParser yamlParser; + * if (yamlParser.ParseAsYamlObject(fh, root)); + * return; + * + * if (!root.isDictionary()) + * return; + * + * std::string name = root.get("name"); + * cout << name.asString(); + * + * const YamlObject &numbers = root.get("numbers"); + * if (!numbers.isList()) + * return; + * + * for (int i = 0; i < numbers.size; i++) + * cout << numbers[i].asInt(); + * + * @endcode + * + * Function ParseAsYamlObject(FILE *, YamlObject &) accept an opened FILE and + * initialize an internal parser. The FILE need to stay valid during function + * call. + */ + +/** + * \brief Construct a YamlParser + */ +YamlParser::YamlParser() + : Extensible(std::make_unique()) +{ +} + +/** + * \fn YamlParser::ParseAsYamlObject() + * \brief Parse a yaml file as a YamlObject + * \param[in] fh The yaml file to parse + * \param[in] yamlObject The result fo YamlObject + * + * Prior to parsing the yaml content, the function accepts an opened FILE to + * create an internal parser. The FILE need to stay valid during the function + * call. When fails, the contect of the YamlObject is undefined. + * + * \return 0 on success or a negative error code otherwise + * \retval -EINVAL Fail to parse the yaml file. + */ +int YamlParser::ParseAsYamlObject(FILE *fh, YamlObject &yamlObject) +{ + if (_d()->initParser(fh)) + return -EINVAL; + + if (_d()->consumeDocumentStart()) + goto error; + + if (ParseNextYamlObject(yamlObject)) + goto error; + + if (_d()->consumeDocumentEnd()) + goto error; + + _d()->release(); + return 0; + +error: + _d()->release(); + return -EINVAL; +} + +/** + * \fn YamlParser::ParseNextEvent() + * \brief Helper function to parse next yaml event and read it as a YamlObject + * \param[in] yamlObject The result of YamlObject + * + * A helper function to parse next yaml event by peeking next event and parse + * them separately as a value, list or dictionary. + * + * \return 0 on success or a negative error code otherwise + * \retval -EINVAL Fail to parse the yaml file. + */ +int YamlParser::ParseNextYamlObject(YamlObject &yamlObject) +{ + yaml_event_t event; + + if (_d()->nextEvent(event)) + return -EINVAL; + + if (event.type == YAML_SCALAR_EVENT) { + yamlObject.type_ = YamlObject::VALUE; + _d()->parseString(yamlObject.value_); + return 0; + } + + if (event.type == YAML_SEQUENCE_START_EVENT) { + yamlObject.type_ = YamlObject::LIST; + auto &list = yamlObject.list_; + auto handler = [this, &list]() { + list.emplace_back(new YamlObject()); + return ParseNextYamlObject(*list.back()); + }; + return _d()->parseList(handler); + } + + if (event.type == YAML_MAPPING_START_EVENT) { + yamlObject.type_ = YamlObject::DICTIONARY; + auto &dictionary = yamlObject.dictionary_; + auto handler = [this, &dictionary](const std::string &key) { + dictionary[key].reset(new YamlObject()); + return ParseNextYamlObject(*dictionary[key]); + }; + return _d()->parseDictionary(handler); + } + + LOG(YamlParser, Error) << "Invalid yaml file"; + return -EINVAL; +} + +} /* namespace libcamera */ From patchwork Wed Feb 9 07:19:12 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hanlin Chen X-Patchwork-Id: 15348 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 026A8BDCBF for ; Wed, 9 Feb 2022 07:19:36 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id AF5AF610E6; Wed, 9 Feb 2022 08:19:35 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=chromium.org header.i=@chromium.org header.b="FO8QJ+BI"; dkim-atps=neutral Received: from mail-pf1-x42a.google.com (mail-pf1-x42a.google.com [IPv6:2607:f8b0:4864:20::42a]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 48D37610F3 for ; Wed, 9 Feb 2022 08:19:34 +0100 (CET) Received: by mail-pf1-x42a.google.com with SMTP id e3so1385570pfd.12 for ; Tue, 08 Feb 2022 23:19:34 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=2UslFmxiS8DK1CfkxkA0O3/0Okkc9RQRB/j3HnwoUwA=; b=FO8QJ+BIU9YEuwtjz9BBB1/brvBxSve38+2IhBf2D8J+LDxkCvheG98AcmyRfXb+k1 +Ylnl/rcPjSKs7yDA1mc26KCFHjXRBWt1t+vlytdD2/8OdCJZcWLg5bONXw0uy2tA/BM CwF1MuxW2kOuUCS9ks5Nlw06bAr08JjpB0aIA= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=2UslFmxiS8DK1CfkxkA0O3/0Okkc9RQRB/j3HnwoUwA=; b=wYIhP0t1IMyuFWxGkssprue9EmxauSdK82s21n2WTR9EB/SaaEfmLfeMXKby/VackP eJCHURo6sqgeNxkfob8j2VL66jJDpuwEEo8r7icie9XyWY2xC3z5oXyl9D6jbDOWV43B HmksT6uetclHnRysDwgDSbdbSjzCjKrcA2+Flm5DYhOvo76XElFHAVZ6O6yPdSvyhVef DhZTQ8Z3uHT4/STQj4uBo3ZidaZvRxK46MZhDKeZAcmRdbQbpXC8K0RXv95yS2M81N1X 2F6/o1MyNCkf3PV+zg5qbESxR7PVZNer7yq+FhI941wak47ZZGBsbojym85GmA1BAoLd /pbA== X-Gm-Message-State: AOAM533SbzjpCh6ZnlpmSeLxliUSJ8Tq5xf3SFPtR4xXPOyCoZVzPCxb 8ZdUi821htkHPTSsOvq6am7t4UXY31EUFw== X-Google-Smtp-Source: ABdhPJyZKsBlbWQ+GrrkM1XtKrpeqMu1zFs/1hkzT+03/EtNe6q1OOKKRDtq2FOhPnlEcvC5QUThFA== X-Received: by 2002:a63:6a87:: with SMTP id f129mr897268pgc.0.1644391172503; Tue, 08 Feb 2022 23:19:32 -0800 (PST) Received: from localhost ([2401:fa00:1:10:a5cf:9a43:1c6d:824]) by smtp.gmail.com with UTF8SMTPSA id u12sm19294051pfk.220.2022.02.08.23.19.31 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Tue, 08 Feb 2022 23:19:32 -0800 (PST) From: Han-Lin Chen To: libcamera-devel@lists.libcamera.org Date: Wed, 9 Feb 2022 15:19:12 +0800 Message-Id: <20220209071917.559993-5-hanlinchen@chromium.org> X-Mailer: git-send-email 2.35.0.263.gb82422642f-goog In-Reply-To: <20220209071917.559993-1-hanlinchen@chromium.org> References: <20220209071917.559993-1-hanlinchen@chromium.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 4/9] android: camera_hal_config: Use YamlParser to parse android hal config X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Use YamlParser to parse android hal config files, instead of handling yaml tokens directly, as a preparation for the further parameter extension. Signed-off-by: Han-Lin Chen --- src/android/camera_hal_config.cpp | 335 +++++++----------------------- 1 file changed, 76 insertions(+), 259 deletions(-) diff --git a/src/android/camera_hal_config.cpp b/src/android/camera_hal_config.cpp index aa90dac7..54611956 100644 --- a/src/android/camera_hal_config.cpp +++ b/src/android/camera_hal_config.cpp @@ -14,15 +14,15 @@ namespace filesystem = std::experimental::filesystem; #else #include #endif -#include #include #include -#include #include #include +#include + using namespace libcamera; LOG_DEFINE_CATEGORY(HALConfig) @@ -37,307 +37,124 @@ public: int parseConfigFile(FILE *fh, std::map *cameras); private: - std::string parseValue(); - std::string parseKey(); - int parseValueBlock(); - int parseCameraLocation(CameraConfigData *cameraConfigData, - const std::string &location); - int parseCameraConfigData(const std::string &cameraId); - int parseCameras(); - int parseEntry(); - - yaml_parser_t parser_; + int parseCameraConfigData(const std::string &cameraId, const YamlObject &); + int parseLocation(const YamlObject &, CameraConfigData &cameraConfigData); + int parseRotation(const YamlObject &, CameraConfigData &cameraConfigData); + std::map *cameras_; + YamlParser yamlParser_; }; CameraHalConfig::Private::Private() { } -std::string CameraHalConfig::Private::parseValue() +int CameraHalConfig::Private::parseConfigFile(FILE *fh, + std::map *cameras) { - yaml_token_t token; - - /* Make sure the token type is a value and get its content. */ - yaml_parser_scan(&parser_, &token); - if (token.type != YAML_VALUE_TOKEN) { - yaml_token_delete(&token); - return ""; - } - yaml_token_delete(&token); - - yaml_parser_scan(&parser_, &token); - if (token.type != YAML_SCALAR_TOKEN) { - yaml_token_delete(&token); - return ""; - } - - std::string value(reinterpret_cast(token.data.scalar.value), - token.data.scalar.length); - yaml_token_delete(&token); - - return value; -} + /* + * Parse the HAL properties. + * + * Each camera properties block is a list of properties associated + * with the ID (as assembled by CameraSensor::generateId()) of the + * camera they refer to. + * + * cameras: + * "camera0 id": + * location: value + * rotation: value + * ... + * + * "camera1 id": + * location: value + * rotation: value + * ... + */ -std::string CameraHalConfig::Private::parseKey() -{ - yaml_token_t token; + cameras_ = cameras; - /* Make sure the token type is a key and get its value. */ - yaml_parser_scan(&parser_, &token); - if (token.type != YAML_SCALAR_TOKEN) { - yaml_token_delete(&token); - return ""; - } + YamlObject yamlObjectRoot; + if (yamlParser_.ParseAsYamlObject(fh, yamlObjectRoot)) + return -EINVAL; - std::string value(reinterpret_cast(token.data.scalar.value), - token.data.scalar.length); - yaml_token_delete(&token); + if (!yamlObjectRoot.isDictionary()) + return -EINVAL; - return value; -} + /* Parse property "cameras" */ + if (!yamlObjectRoot.isMember("cameras")) + return -EINVAL; -int CameraHalConfig::Private::parseValueBlock() -{ - yaml_token_t token; + const YamlObject &yamlObjectCameras = yamlObjectRoot.get("cameras"); - /* Make sure the next token are VALUE and BLOCK_MAPPING_START. */ - yaml_parser_scan(&parser_, &token); - if (token.type != YAML_VALUE_TOKEN) { - yaml_token_delete(&token); + if (!yamlObjectCameras.isDictionary()) return -EINVAL; - } - yaml_token_delete(&token); - yaml_parser_scan(&parser_, &token); - if (token.type != YAML_BLOCK_MAPPING_START_TOKEN) { - yaml_token_delete(&token); - return -EINVAL; + std::vector cameraIds = yamlObjectCameras.getMemberNames(); + for (const std::string &cameraId : cameraIds) { + if (parseCameraConfigData(cameraId, + yamlObjectCameras.get(cameraId))) + return -EINVAL; } - yaml_token_delete(&token); return 0; } -int CameraHalConfig::Private::parseCameraLocation(CameraConfigData *cameraConfigData, - const std::string &location) -{ - if (location == "front") - cameraConfigData->facing = CAMERA_FACING_FRONT; - else if (location == "back") - cameraConfigData->facing = CAMERA_FACING_BACK; - else - return -EINVAL; - - return 0; -} +int CameraHalConfig::Private::parseCameraConfigData(const std::string &cameraId, + const YamlObject &cameraObject) -int CameraHalConfig::Private::parseCameraConfigData(const std::string &cameraId) { - int ret = parseValueBlock(); - if (ret) - return ret; - - /* - * Parse the camera properties and store them in a cameraConfigData - * instance. - * - * Add a safety counter to make sure we don't loop indefinitely in case - * the configuration file is malformed. - */ CameraConfigData cameraConfigData; - unsigned int sentinel = 100; - bool blockEnd = false; - yaml_token_t token; - - do { - yaml_parser_scan(&parser_, &token); - switch (token.type) { - case YAML_KEY_TOKEN: { - yaml_token_delete(&token); - - /* - * Parse the camera property key and make sure it is - * valid. - */ - std::string key = parseKey(); - std::string value = parseValue(); - if (key.empty() || value.empty()) - return -EINVAL; - - if (key == "location") { - ret = parseCameraLocation(&cameraConfigData, value); - if (ret) { - LOG(HALConfig, Error) - << "Unknown location: " << value; - return -EINVAL; - } - } else if (key == "rotation") { - ret = std::stoi(value); - if (ret < 0 || ret >= 360) { - LOG(HALConfig, Error) - << "Unknown rotation: " << value; - return -EINVAL; - } - cameraConfigData.rotation = ret; - } else { - LOG(HALConfig, Error) - << "Unknown key: " << key; - return -EINVAL; - } - break; - } - - case YAML_BLOCK_END_TOKEN: - blockEnd = true; - [[fallthrough]]; - default: - yaml_token_delete(&token); - break; - } - - --sentinel; - } while (!blockEnd && sentinel); - if (!sentinel) - return -EINVAL; - (*cameras_)[cameraId] = cameraConfigData; + if (!cameraObject.isDictionary()) + return -EINVAL; - return 0; -} + /* Parse property "location" */ + if (parseLocation(cameraObject, cameraConfigData)) + return -EINVAL; -int CameraHalConfig::Private::parseCameras() -{ - int ret = parseValueBlock(); - if (ret) { - LOG(HALConfig, Error) << "Configuration file is not valid"; - return ret; - } + /* Parse property "rotation" */ + if (parseRotation(cameraObject, cameraConfigData)) + return -EINVAL; - /* - * Parse the camera properties. - * - * Each camera properties block is a list of properties associated - * with the ID (as assembled by CameraSensor::generateId()) of the - * camera they refer to. - * - * cameras: - * "camera0 id": - * key: value - * key: value - * ... - * - * "camera1 id": - * key: value - * key: value - * ... - */ - bool blockEnd = false; - yaml_token_t token; - do { - yaml_parser_scan(&parser_, &token); - switch (token.type) { - case YAML_KEY_TOKEN: { - yaml_token_delete(&token); - - /* Parse the camera ID as key of the property list. */ - std::string cameraId = parseKey(); - if (cameraId.empty()) - return -EINVAL; - - ret = parseCameraConfigData(cameraId); - if (ret) - return -EINVAL; - break; - } - case YAML_BLOCK_END_TOKEN: - blockEnd = true; - [[fallthrough]]; - default: - yaml_token_delete(&token); - break; - } - } while (!blockEnd); + (*cameras_)[cameraId] = cameraConfigData; return 0; } -int CameraHalConfig::Private::parseEntry() +int CameraHalConfig::Private::parseLocation(const YamlObject &cameraObject, + CameraConfigData &cameraConfigData) { - int ret = -EINVAL; - - /* - * Parse each key we find in the file. - * - * The 'cameras' keys maps to a list of (lists) of camera properties. - */ + if (!cameraObject.isMember("location")) + return -EINVAL; - std::string key = parseKey(); - if (key.empty()) - return ret; + std::string location = cameraObject.get("location").asString(); - if (key == "cameras") - ret = parseCameras(); + if (location == "front") + cameraConfigData.facing = CAMERA_FACING_FRONT; + else if (location == "back") + cameraConfigData.facing = CAMERA_FACING_BACK; else - LOG(HALConfig, Error) << "Unknown key: " << key; + return -EINVAL; - return ret; + return 0; } -int CameraHalConfig::Private::parseConfigFile(FILE *fh, - std::map *cameras) +int CameraHalConfig::Private::parseRotation(const YamlObject &cameraObject, + CameraConfigData &cameraConfigData) { - cameras_ = cameras; - - int ret = yaml_parser_initialize(&parser_); - if (!ret) { - LOG(HALConfig, Error) << "Failed to initialize yaml parser"; - return -EINVAL; - } - yaml_parser_set_input_file(&parser_, fh); - - yaml_token_t token; - yaml_parser_scan(&parser_, &token); - if (token.type != YAML_STREAM_START_TOKEN) { - LOG(HALConfig, Error) << "Configuration file is not valid"; - yaml_token_delete(&token); - yaml_parser_delete(&parser_); + if (!cameraObject.isMember("rotation")) return -EINVAL; - } - yaml_token_delete(&token); - yaml_parser_scan(&parser_, &token); - if (token.type != YAML_BLOCK_MAPPING_START_TOKEN) { - LOG(HALConfig, Error) << "Configuration file is not valid"; - yaml_token_delete(&token); - yaml_parser_delete(&parser_); + int32_t rotation = cameraObject.get("rotation").asInt32(); + + if (rotation < 0 || rotation >= 360) { + LOG(HALConfig, Error) + << "Unknown rotation: " << rotation; return -EINVAL; } - yaml_token_delete(&token); - - /* Parse the file and parse each single key one by one. */ - do { - yaml_parser_scan(&parser_, &token); - switch (token.type) { - case YAML_KEY_TOKEN: - yaml_token_delete(&token); - ret = parseEntry(); - break; - - case YAML_STREAM_END_TOKEN: - ret = -ENOENT; - [[fallthrough]]; - default: - yaml_token_delete(&token); - break; - } - } while (ret >= 0); - yaml_parser_delete(&parser_); - - if (ret && ret != -ENOENT) - LOG(HALConfig, Error) << "Configuration file is not valid"; - - return ret == -ENOENT ? 0 : ret; + + cameraConfigData.rotation = rotation; + return 0; } CameraHalConfig::CameraHalConfig() From patchwork Wed Feb 9 07:19:13 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hanlin Chen X-Patchwork-Id: 15349 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id D5700BDCBF for ; Wed, 9 Feb 2022 07:19:39 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 411EA60E6E; Wed, 9 Feb 2022 08:19:39 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=chromium.org header.i=@chromium.org header.b="JwyiDK/e"; dkim-atps=neutral Received: from mail-pj1-x102d.google.com (mail-pj1-x102d.google.com [IPv6:2607:f8b0:4864:20::102d]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id CAA0160E6E for ; Wed, 9 Feb 2022 08:19:36 +0100 (CET) Received: by mail-pj1-x102d.google.com with SMTP id a11-20020a17090a740b00b001b8b506c42fso4373330pjg.0 for ; Tue, 08 Feb 2022 23:19:36 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=1bKnChOEpUWF1o9epEwQFZqCyx+hUb7Ga4F1bJwcxaM=; b=JwyiDK/eLAevK2/MdESuDSdA5Ub7MZNVc4dTCF1DvZc1x27N1kXxYNGmLRvZbVxprT +NkFgUrKGY7ljAWKeMj3voDEJ0QawaeLirG1ihH1GbrtEPA6XZpiSR07bpvxwtGWg44c IkzHZt8Z1ovzktcQXbkKvJa8jXzoTGV4HXwnw= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=1bKnChOEpUWF1o9epEwQFZqCyx+hUb7Ga4F1bJwcxaM=; b=b3pB9Z7najmO6hO2HHtJB6GpgZZVDh61Yswl0Yslqt9emxlq9Brn9p+6uqVm7lryN/ kVJtd7DG7rU5QvkwlYnUarh3qEDl3t1ZDK14ywXesVY5INTArOTIBs6avUA789kowJW9 WbAK8IKpQQ8otNlp5NDnesCqdeymctySgp72svFWMJ0ty0pqXT4otUu6tc1WnjoFV8TD arYRSMvibCS9uZ9pOnoxA/3eGSeOcbOqSKbzxDjIcfNbgvJMDAKQZl2bJfhhwbdL7gM6 uCegilbVyOmr91om9xMUEb9PzJirTGl7Dt2/fBDgTOvEjCJUnk/nCVehZuJrBSHeDC4C 8JfQ== X-Gm-Message-State: AOAM530n8gbp0JmXSZ3OyKDowqQx8FAI9I06Bfba4n70C1rgZL+NsXU5 PwB+oxoiY55BHhvGmFc/EmFAwyAHoyIUMA== X-Google-Smtp-Source: ABdhPJyJ1l3id0a6yDiBxuxhMKLQ6TUqOcSlkGbQsBZu5EvMsukjlkYic5xsjz+A28+1tODMjZckBQ== X-Received: by 2002:a17:902:7b89:: with SMTP id w9mr830197pll.174.1644391174618; Tue, 08 Feb 2022 23:19:34 -0800 (PST) Received: from localhost ([2401:fa00:1:10:a5cf:9a43:1c6d:824]) by smtp.gmail.com with UTF8SMTPSA id o1sm19930081pfu.88.2022.02.08.23.19.33 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Tue, 08 Feb 2022 23:19:34 -0800 (PST) From: Han-Lin Chen To: libcamera-devel@lists.libcamera.org Date: Wed, 9 Feb 2022 15:19:13 +0800 Message-Id: <20220209071917.559993-6-hanlinchen@chromium.org> X-Mailer: git-send-email 2.35.0.263.gb82422642f-goog In-Reply-To: <20220209071917.559993-1-hanlinchen@chromium.org> References: <20220209071917.559993-1-hanlinchen@chromium.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 5/9] android: Add pipeline_config_file parameter for camera_hal.yaml X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add pipeline_config_file parameter for camera_hal.yaml for the ipu3 devices of ChromeOS, which specify a path to a pipeline configuration file for a camera, and could be empty if no configuration is provided. The configuration files for soraka [1] and nautilus [2] are added, which are copied and converted to yaml format from the ChromeOS repos. See: [1] https://chromium.googlesource.com/chromiumos/overlays/board-overlays/+/master/baseboard-poppy/media-libs/cros-camera-hal-configs-poppy/files/gcss [2] https://chromium.googlesource.com/chromiumos/overlays/board-overlays/+/refs/heads/main/overlay-nautilus/media-libs/cros-camera-hal-configs-nautilus/files/gcss/ Signed-off-by: Han-Lin Chen --- src/android/camera_hal_config.cpp | 17 ++ src/android/camera_hal_config.h | 1 + src/android/data/nautilus/camera_hal.yaml | 2 + src/android/data/nautilus/imx258.yaml | 248 ++++++++++++++++++++++ src/android/data/soraka/camera_hal.yaml | 2 + src/android/data/soraka/ov13858.yaml | 236 ++++++++++++++++++++ src/android/data/soraka/ov5670.yaml | 242 +++++++++++++++++++++ 7 files changed, 748 insertions(+) create mode 100644 src/android/data/nautilus/imx258.yaml create mode 100644 src/android/data/soraka/ov13858.yaml create mode 100644 src/android/data/soraka/ov5670.yaml diff --git a/src/android/camera_hal_config.cpp b/src/android/camera_hal_config.cpp index 54611956..f378df9d 100644 --- a/src/android/camera_hal_config.cpp +++ b/src/android/camera_hal_config.cpp @@ -40,6 +40,7 @@ private: int parseCameraConfigData(const std::string &cameraId, const YamlObject &); int parseLocation(const YamlObject &, CameraConfigData &cameraConfigData); int parseRotation(const YamlObject &, CameraConfigData &cameraConfigData); + int parsePipelineConf(const YamlObject &, CameraConfigData &cameraConfigData); std::map *cameras_; YamlParser yamlParser_; @@ -63,11 +64,13 @@ int CameraHalConfig::Private::parseConfigFile(FILE *fh, * "camera0 id": * location: value * rotation: value + * pipeline_config_file: path * ... * * "camera1 id": * location: value * rotation: value + * pipeline_config_file: path * ... */ @@ -116,6 +119,10 @@ int CameraHalConfig::Private::parseCameraConfigData(const std::string &cameraId, if (parseRotation(cameraObject, cameraConfigData)) return -EINVAL; + /* Parse property "pipeline_config_file" */ + if (parsePipelineConf(cameraObject, cameraConfigData)) + return -EINVAL; + (*cameras_)[cameraId] = cameraConfigData; return 0; @@ -157,6 +164,16 @@ int CameraHalConfig::Private::parseRotation(const YamlObject &cameraObject, return 0; } +int CameraHalConfig::Private::parsePipelineConf(const YamlObject &cameraObject, + CameraConfigData &cameraConfigData) +{ + if (!cameraObject.isMember("pipeline_config_file")) + return -EINVAL; + + cameraConfigData.pipelineConfigFile = cameraObject.get("pipeline_config_file").asString(); + return 0; +} + CameraHalConfig::CameraHalConfig() : Extensible(std::make_unique()), exists_(false), valid_(false) { diff --git a/src/android/camera_hal_config.h b/src/android/camera_hal_config.h index 9df554f9..3a905c92 100644 --- a/src/android/camera_hal_config.h +++ b/src/android/camera_hal_config.h @@ -15,6 +15,7 @@ struct CameraConfigData { int facing = -1; int rotation = -1; + std::string pipelineConfigFile; }; class CameraHalConfig final : public libcamera::Extensible diff --git a/src/android/data/nautilus/camera_hal.yaml b/src/android/data/nautilus/camera_hal.yaml index faddd29e..18a2b7a9 100644 --- a/src/android/data/nautilus/camera_hal.yaml +++ b/src/android/data/nautilus/camera_hal.yaml @@ -2,7 +2,9 @@ cameras: "\\_SB_.PCI0.I2C2.CAM0": location: back rotation: 0 + pipeline_config_file: "/etc/camera/libcamera/imx258.yaml" "\\_SB_.PCI0.XHCI.RHUB.HS09-9:1.0-04f2:b647": location: front rotation: 0 + pipeline_config_file: "" diff --git a/src/android/data/nautilus/imx258.yaml b/src/android/data/nautilus/imx258.yaml new file mode 100644 index 00000000..be52e752 --- /dev/null +++ b/src/android/data/nautilus/imx258.yaml @@ -0,0 +1,248 @@ +still_mode: +- bds: [4208, 3116] + cio2: [4208, 3118] + gdc: [4096, 3072] + iff: [4208, 3116] + main: [4096, 3072] + viewfinder: [0, 0] +- bds: [1968, 1184] + cio2: [4208, 3118] + gdc: [1920, 1080] + iff: [4182, 2516] + main: [1920, 1080] + viewfinder: [0, 0] +- bds: [1632, 1216] + cio2: [4208, 3118] + gdc: [1600, 1200] + iff: [4182, 3116] + main: [1600, 1200] + viewfinder: [0, 0] +- bds: [1632, 1216] + cio2: [4208, 3118] + gdc: [1600, 1200] + iff: [4182, 3116] + main: [1280, 960] + viewfinder: [0, 0] +- bds: [1968, 1184] + cio2: [4208, 3118] + gdc: [1920, 1080] + iff: [4182, 2516] + main: [1280, 720] + viewfinder: [0, 0] +- bds: [1632, 1216] + cio2: [4208, 3118] + gdc: [1600, 1200] + iff: [4182, 3116] + main: [640, 480] + viewfinder: [0, 0] +- bds: [1632, 1216] + cio2: [4208, 3118] + gdc: [1600, 1200] + iff: [4182, 3116] + main: [320, 240] + viewfinder: [0, 0] +video_mode: +- bds: [4208, 3116] + cio2: [4208, 3118] + gdc: [4096, 3072] + iff: [4208, 3116] + main: [4096, 3072] + viewfinder: [0, 0] +- bds: [1968, 1184] + cio2: [4208, 3118] + gdc: [1920, 1080] + iff: [4182, 2516] + main: [1920, 1080] + viewfinder: [0, 0] +- bds: [1632, 1216] + cio2: [4208, 3118] + gdc: [1600, 1200] + iff: [4182, 3116] + main: [1600, 1200] + viewfinder: [0, 0] +- bds: [1632, 1216] + cio2: [4208, 3118] + gdc: [1600, 1200] + iff: [4182, 3116] + main: [1280, 960] + viewfinder: [0, 0] +- bds: [1968, 1184] + cio2: [4208, 3118] + gdc: [1920, 1080] + iff: [4182, 2516] + main: [1280, 720] + viewfinder: [0, 0] +- bds: [1632, 1216] + cio2: [4208, 3118] + gdc: [1600, 1200] + iff: [4182, 3116] + main: [640, 480] + viewfinder: [0, 0] +- bds: [1632, 1216] + cio2: [4208, 3118] + gdc: [1600, 1200] + iff: [4182, 3116] + main: [320, 240] + viewfinder: [0, 0] +- bds: [4208, 3116] + cio2: [4208, 3118] + gdc: [4160, 3072] + iff: [4208, 3116] + main: [4096, 3072] + viewfinder: [4096, 3072] +- bds: [4208, 3116] + cio2: [4208, 3118] + gdc: [4160, 3072] + iff: [4208, 3116] + main: [4096, 3072] + viewfinder: [1920, 1080] +- bds: [4208, 3116] + cio2: [4208, 3118] + gdc: [4096, 3072] + iff: [4208, 3116] + main: [4096, 3072] + viewfinder: [1600, 1200] +- bds: [4208, 3116] + cio2: [4208, 3118] + gdc: [4096, 3072] + iff: [4208, 3116] + main: [4096, 3072] + viewfinder: [1280, 960] +- bds: [4208, 3116] + cio2: [4208, 3118] + gdc: [4160, 3072] + iff: [4208, 3116] + main: [4096, 3072] + viewfinder: [1280, 720] +- bds: [4208, 3116] + cio2: [4208, 3118] + gdc: [4096, 3072] + iff: [4208, 3116] + main: [4096, 3072] + viewfinder: [640, 480] +- bds: [4208, 3116] + cio2: [4208, 3118] + gdc: [4096, 3072] + iff: [4208, 3116] + main: [4096, 3072] + viewfinder: [320, 240] +- bds: [1968, 1184] + cio2: [4208, 3118] + gdc: [1920, 1080] + iff: [4182, 2516] + main: [1920, 1080] + viewfinder: [1920, 1080] +- bds: [1968, 1440] + cio2: [4208, 3118] + gdc: [1920, 1424] + iff: [4182, 3060] + main: [1920, 1080] + viewfinder: [1600, 1200] +- bds: [1968, 1440] + cio2: [4208, 3118] + gdc: [1920, 1424] + iff: [4182, 3060] + main: [1920, 1080] + viewfinder: [1280, 960] +- bds: [1968, 1184] + cio2: [4208, 3118] + gdc: [1920, 1080] + iff: [4182, 2516] + main: [1920, 1080] + viewfinder: [1280, 720] +- bds: [1968, 1440] + cio2: [4208, 3118] + gdc: [1920, 1424] + iff: [4182, 3060] + main: [1920, 1080] + viewfinder: [640, 480] +- bds: [1968, 1440] + cio2: [4208, 3118] + gdc: [1920, 1424] + iff: [4182, 3060] + main: [1920, 1080] + viewfinder: [320, 240] +- bds: [1632, 1216] + cio2: [4208, 3118] + gdc: [1600, 1200] + iff: [4182, 3116] + main: [1600, 1200] + viewfinder: [1600, 1200] +- bds: [1632, 1216] + cio2: [4208, 3118] + gdc: [1600, 1200] + iff: [4182, 3116] + main: [1600, 1200] + viewfinder: [1280, 960] +- bds: [1680, 1240] + cio2: [4208, 3118] + gdc: [1664, 1200] + iff: [4200, 3100] + main: [1600, 1200] + viewfinder: [1280, 720] +- bds: [1632, 1216] + cio2: [4208, 3118] + gdc: [1600, 1200] + iff: [4182, 3116] + main: [1600, 1200] + viewfinder: [640, 480] +- bds: [1632, 1216] + cio2: [4208, 3118] + gdc: [1600, 1200] + iff: [4182, 3116] + main: [1600, 1200] + viewfinder: [320, 240] +- bds: [1344, 992] + cio2: [4208, 3118] + gdc: [1280, 960] + iff: [4200, 3100] + main: [1280, 960] + viewfinder: [1280, 960] +- bds: [1400, 1036] + cio2: [4208, 3118] + gdc: [1344, 960] + iff: [4200, 3108] + main: [1280, 960] + viewfinder: [1280, 720] +- bds: [1344, 992] + cio2: [4208, 3118] + gdc: [1280, 960] + iff: [4200, 3100] + main: [1280, 960] + viewfinder: [640, 480] +- bds: [1344, 992] + cio2: [4208, 3118] + gdc: [1280, 960] + iff: [4200, 3100] + main: [1280, 960] + viewfinder: [320, 240] +- bds: [1288, 800] + cio2: [4208, 3118] + gdc: [1280, 720] + iff: [4186, 2600] + main: [1280, 720] + viewfinder: [1280, 720] +- bds: [1312, 960] + cio2: [4208, 3118] + gdc: [1280, 952] + iff: [4182, 3060] + main: [1280, 720] + viewfinder: [640, 480] +- bds: [1312, 960] + cio2: [4208, 3118] + gdc: [1280, 952] + iff: [4182, 3060] + main: [1280, 720] + viewfinder: [320, 240] +- bds: [700, 520] + cio2: [2104, 1560] + gdc: [640, 480] + iff: [2100, 1560] + main: [640, 480] + viewfinder: [320, 240] +- bds: [364, 276] + cio2: [4208, 3118] + gdc: [320, 240] + iff: [1456, 1104] + main: [320, 240] + viewfinder: [320, 240] diff --git a/src/android/data/soraka/camera_hal.yaml b/src/android/data/soraka/camera_hal.yaml index 2e996403..6f0062e9 100644 --- a/src/android/data/soraka/camera_hal.yaml +++ b/src/android/data/soraka/camera_hal.yaml @@ -2,7 +2,9 @@ cameras: "\\_SB_.PCI0.I2C4.CAM1": location: front rotation: 0 + pipeline_config_file: "/etc/camera/libcamera/ov5670.yaml" "\\_SB_.PCI0.I2C2.CAM0": location: back rotation: 0 + pipeline_config_file: "/etc/camera/libcamera/ov13858.yaml" diff --git a/src/android/data/soraka/ov13858.yaml b/src/android/data/soraka/ov13858.yaml new file mode 100644 index 00000000..c6f4d091 --- /dev/null +++ b/src/android/data/soraka/ov13858.yaml @@ -0,0 +1,236 @@ +still_mode: +- bds: [4224, 3136] + cio2: [4224, 3136] + gdc: [4096, 3072] + iff: [4224, 3136] + main: [640, 480] + viewfinder: [0, 0] +- bds: [4224, 3136] + cio2: [4224, 3136] + gdc: [4096, 3072] + iff: [4224, 3136] + main: [320, 240] + viewfinder: [0, 0] +- bds: [4216, 2386] + cio2: [4224, 3136] + gdc: [4128, 2322] + iff: [4216, 2386] + main: [1920, 1080] + viewfinder: [0, 0] +- bds: [4216, 2386] + cio2: [4224, 3136] + gdc: [4128, 2322] + iff: [4216, 2386] + main: [1280, 720] + viewfinder: [0, 0] +- bds: [4224, 3136] + cio2: [4224, 3136] + gdc: [4096, 3072] + iff: [4224, 3136] + main: [1280, 960] + viewfinder: [0, 0] +- bds: [4224, 3136] + cio2: [4224, 3136] + gdc: [4096, 3072] + iff: [4224, 3136] + main: [1600, 1200] + viewfinder: [0, 0] +- bds: [4224, 3136] + cio2: [4224, 3136] + gdc: [4096, 3072] + iff: [4224, 3136] + main: [4096, 3072] + viewfinder: [0, 0] +video_mode: +- bds: [4224, 3136] + cio2: [4224, 3136] + gdc: [4096, 3072] + iff: [4224, 3136] + main: [4096, 3072] + viewfinder: [1920, 1080] +- bds: [1984, 1184] + cio2: [4224, 3136] + gdc: [1920, 1080] + iff: [4216, 2516] + main: [1920, 1080] + viewfinder: [0, 0] +- bds: [4224, 3136] + cio2: [4224, 3136] + gdc: [4096, 3072] + iff: [4224, 3136] + main: [4096, 3072] + viewfinder: [1280, 720] +- bds: [1984, 1184] + cio2: [4224, 3136] + gdc: [1920, 1080] + iff: [4216, 2516] + main: [1280, 720] + viewfinder: [0, 0] +- bds: [4224, 3136] + cio2: [4224, 3136] + gdc: [4096, 3072] + iff: [4224, 3136] + main: [4096, 3072] + viewfinder: [640, 480] +- bds: [4224, 3136] + cio2: [4224, 3136] + gdc: [4096, 3072] + iff: [4224, 3136] + main: [4096, 3072] + viewfinder: [1280, 960] +- bds: [1632, 1216] + cio2: [4224, 3136] + gdc: [1600, 1200] + iff: [4182, 3116] + main: [1280, 960] + viewfinder: [0, 0] +- bds: [4224, 3136] + cio2: [4224, 3136] + gdc: [4096, 3072] + iff: [4224, 3136] + main: [4096, 3072] + viewfinder: [1600, 1200] +- bds: [4224, 3136] + cio2: [4224, 3136] + gdc: [4096, 3072] + iff: [4224, 3136] + main: [4096, 3072] + viewfinder: [320, 240] +- bds: [1296, 800] + cio2: [4224, 3136] + gdc: [1280, 720] + iff: [4212, 2600] + main: [1280, 720] + viewfinder: [1280, 720] +- bds: [1296, 960] + cio2: [4224, 3136] + gdc: [1280, 952] + iff: [4212, 3120] + main: [1280, 720] + viewfinder: [640, 480] +- bds: [4224, 3136] + cio2: [4224, 3136] + gdc: [4160, 3120] + iff: [4224, 3136] + main: [1920, 1080] + viewfinder: [1600, 1200] +- bds: [1984, 1472] + cio2: [4224, 3136] + gdc: [1920, 1432] + iff: [4216, 3128] + main: [1920, 1080] + viewfinder: [1600, 1200] +- bds: [1296, 960] + cio2: [4224, 3136] + gdc: [1280, 952] + iff: [4212, 3120] + main: [1280, 720] + viewfinder: [320, 240] +- bds: [1984, 1184] + cio2: [4224, 3136] + gdc: [1920, 1080] + iff: [4216, 2516] + main: [1920, 1080] + viewfinder: [1920, 1080] +- bds: [1984, 1184] + cio2: [4224, 3136] + gdc: [1920, 1080] + iff: [4216, 2516] + main: [1920, 1080] + viewfinder: [1280, 720] +- bds: [1984, 1472] + cio2: [4224, 3136] + gdc: [1920, 1432] + iff: [4216, 3128] + main: [1920, 1080] + viewfinder: [640, 480] +- bds: [1984, 1472] + cio2: [4224, 3136] + gdc: [1920, 1432] + iff: [4216, 3128] + main: [1920, 1080] + viewfinder: [1280, 960] +- bds: [1984, 1472] + cio2: [4224, 3136] + gdc: [1920, 1432] + iff: [4216, 3128] + main: [1920, 1080] + viewfinder: [320, 240] +- bds: [1688, 1248] + cio2: [4224, 3136] + gdc: [1664, 1200] + iff: [4220, 3120] + main: [1600, 1200] + viewfinder: [1280, 720] +- bds: [1632, 1216] + cio2: [4224, 3136] + gdc: [1600, 1200] + iff: [4182, 3116] + main: [1600, 1200] + viewfinder: [640, 480] +- bds: [1632, 1216] + cio2: [4224, 3136] + gdc: [1600, 1200] + iff: [4182, 3116] + main: [1600, 1200] + viewfinder: [1280, 960] +- bds: [1632, 1216] + cio2: [4224, 3136] + gdc: [1600, 1200] + iff: [4182, 3116] + main: [1600, 1200] + viewfinder: [1600, 1200] +- bds: [1632, 1216] + cio2: [4224, 3136] + gdc: [1600, 1200] + iff: [4182, 3116] + main: [1600, 1200] + viewfinder: [320, 240] +- bds: [1376, 1024] + cio2: [4224, 3136] + gdc: [1344, 960] + iff: [4214, 3136] + main: [1280, 960] + viewfinder: [1280, 720] +- bds: [1344, 992] + cio2: [4224, 3136] + gdc: [1280, 960] + iff: [4200, 3100] + main: [1280, 960] + viewfinder: [640, 480] +- bds: [1344, 992] + cio2: [4224, 3136] + gdc: [1280, 960] + iff: [4200, 3100] + main: [1280, 960] + viewfinder: [1280, 960] +- bds: [1344, 992] + cio2: [4224, 3136] + gdc: [1280, 960] + iff: [4200, 3100] + main: [1280, 960] + viewfinder: [320, 240] +- bds: [1632, 1216] + cio2: [4224, 3136] + gdc: [1600, 1200] + iff: [4182, 3116] + main: [640, 480] + viewfinder: [0, 0] +- bds: [1632, 1216] + cio2: [4224, 3136] + gdc: [1600, 1200] + iff: [4182, 3116] + main: [320, 240] + viewfinder: [0, 0] +- bds: [1632, 1216] + cio2: [4224, 3136] + gdc: [1600, 1200] + iff: [4182, 3116] + main: [1600, 1200] + viewfinder: [0, 0] +- bds: [4224, 3136] + cio2: [4224, 3136] + gdc: [4096, 3072] + iff: [4224, 3136] + main: [4096, 3072] + viewfinder: [0, 0] diff --git a/src/android/data/soraka/ov5670.yaml b/src/android/data/soraka/ov5670.yaml new file mode 100644 index 00000000..35ad8684 --- /dev/null +++ b/src/android/data/soraka/ov5670.yaml @@ -0,0 +1,242 @@ +still_mode: +- bds: [2592, 1944] + cio2: [2592, 1944] + gdc: [2560, 1920] + iff: [2592, 1944] + main: [1600, 1200] + viewfinder: [0, 0] +- bds: [2592, 1944] + cio2: [2592, 1944] + gdc: [2560, 1920] + iff: [2592, 1944] + main: [1280, 960] + viewfinder: [0, 0] +- bds: [2592, 1944] + cio2: [2592, 1944] + gdc: [2560, 1920] + iff: [2592, 1944] + main: [2560, 1920] + viewfinder: [0, 0] +- bds: [2592, 1944] + cio2: [2592, 1944] + gdc: [2560, 1920] + iff: [2592, 1944] + main: [1920, 1080] + viewfinder: [0, 0] +- bds: [2592, 1944] + cio2: [2592, 1944] + gdc: [2560, 1920] + iff: [2592, 1944] + main: [1280, 720] + viewfinder: [0, 0] +- bds: [2592, 1944] + cio2: [2592, 1944] + gdc: [2560, 1920] + iff: [2592, 1944] + main: [640, 480] + viewfinder: [0, 0] +- bds: [2592, 1944] + cio2: [2592, 1944] + gdc: [2560, 1920] + iff: [2592, 1944] + main: [320, 240] + viewfinder: [0, 0] +video_mode: +- bds: [1952, 1152] + cio2: [2592, 1944] + gdc: [1920, 1080] + iff: [2562, 1512] + main: [1920, 1080] + viewfinder: [1920, 1080] +- bds: [2592, 1944] + cio2: [2592, 1944] + gdc: [2560, 1920] + iff: [2592, 1944] + main: [2560, 1920] + viewfinder: [1920, 1080] +- bds: [1952, 1152] + cio2: [2592, 1944] + gdc: [1920, 1080] + iff: [2562, 1512] + main: [1920, 1080] + viewfinder: [0, 0] +- bds: [1952, 1152] + cio2: [2592, 1944] + gdc: [1920, 1080] + iff: [2562, 1512] + main: [1920, 1080] + viewfinder: [1280, 720] +- bds: [1296, 972] + cio2: [2592, 1944] + gdc: [1280, 960] + iff: [2592, 1944] + main: [1280, 960] + viewfinder: [1280, 720] +- bds: [1296, 804] + cio2: [2592, 1944] + gdc: [1280, 720] + iff: [2592, 1608] + main: [1280, 720] + viewfinder: [1280, 720] +- bds: [1632, 1216] + cio2: [2592, 1944] + gdc: [1600, 1200] + iff: [2550, 1900] + main: [1600, 1200] + viewfinder: [1280, 720] +- bds: [2592, 1944] + cio2: [2592, 1944] + gdc: [2560, 1920] + iff: [2592, 1944] + main: [2560, 1920] + viewfinder: [1280, 720] +- bds: [1952, 1152] + cio2: [2592, 1944] + gdc: [1920, 1080] + iff: [2562, 1512] + main: [1280, 720] + viewfinder: [0, 0] +- bds: [1952, 1472] + cio2: [2592, 1944] + gdc: [1920, 1440] + iff: [2562, 1932] + main: [1920, 1080] + viewfinder: [640, 480] +- bds: [1296, 972] + cio2: [2592, 1944] + gdc: [1280, 960] + iff: [2592, 1944] + main: [1280, 960] + viewfinder: [640, 480] +- bds: [1296, 972] + cio2: [2592, 1944] + gdc: [1280, 960] + iff: [2592, 1944] + main: [1280, 720] + viewfinder: [640, 480] +- bds: [1632, 1216] + cio2: [2592, 1944] + gdc: [1600, 1200] + iff: [2550, 1900] + main: [1600, 1200] + viewfinder: [640, 480] +- bds: [2592, 1944] + cio2: [2592, 1944] + gdc: [2560, 1920] + iff: [2592, 1944] + main: [2560, 1920] + viewfinder: [640, 480] +- bds: [1632, 1216] + cio2: [2592, 1944] + gdc: [1600, 1200] + iff: [2550, 1900] + main: [1600, 1200] + viewfinder: [1600, 1200] +- bds: [2592, 1944] + cio2: [2592, 1944] + gdc: [2560, 1920] + iff: [2592, 1944] + main: [2560, 1920] + viewfinder: [1600, 1200] +- bds: [2592, 1944] + cio2: [2592, 1944] + gdc: [2560, 1920] + iff: [2592, 1944] + main: [1600, 1200] + viewfinder: [0, 0] +- bds: [1952, 1472] + cio2: [2592, 1944] + gdc: [1920, 1440] + iff: [2562, 1932] + main: [1920, 1080] + viewfinder: [1280, 960] +- bds: [1296, 972] + cio2: [2592, 1944] + gdc: [1280, 960] + iff: [2592, 1944] + main: [1280, 960] + viewfinder: [1280, 960] +- bds: [1632, 1216] + cio2: [2592, 1944] + gdc: [1600, 1200] + iff: [2550, 1900] + main: [1600, 1200] + viewfinder: [1280, 960] +- bds: [2592, 1944] + cio2: [2592, 1944] + gdc: [2560, 1920] + iff: [2592, 1944] + main: [2560, 1920] + viewfinder: [1280, 960] +- bds: [2592, 1944] + cio2: [2592, 1944] + gdc: [2560, 1920] + iff: [2592, 1944] + main: [1280, 960] + viewfinder: [0, 0] +- bds: [1952, 1472] + cio2: [2592, 1944] + gdc: [1920, 1440] + iff: [2562, 1932] + main: [1920, 1080] + viewfinder: [320, 240] +- bds: [1296, 972] + cio2: [2592, 1944] + gdc: [1280, 960] + iff: [2592, 1944] + main: [1280, 960] + viewfinder: [320, 240] +- bds: [1296, 972] + cio2: [2592, 1944] + gdc: [1280, 960] + iff: [2592, 1944] + main: [1280, 720] + viewfinder: [320, 240] +- bds: [1632, 1216] + cio2: [2592, 1944] + gdc: [1600, 1200] + iff: [2550, 1900] + main: [1600, 1200] + viewfinder: [320, 240] +- bds: [2592, 1944] + cio2: [2592, 1944] + gdc: [2560, 1920] + iff: [2592, 1944] + main: [2560, 1920] + viewfinder: [2560, 1920] +- bds: [2592, 1944] + cio2: [2592, 1944] + gdc: [2560, 1920] + iff: [2592, 1944] + main: [2560, 1920] + viewfinder: [0, 0] +- bds: [1296, 972] + cio2: [2592, 1944] + gdc: [1280, 960] + iff: [2592, 1944] + main: [640, 480] + viewfinder: [0, 0] +- bds: [1296, 972] + cio2: [2592, 1944] + gdc: [1280, 960] + iff: [2592, 1944] + main: [320, 240] + viewfinder: [0, 0] +- bds: [976, 736] + cio2: [2576, 1936] + gdc: [960, 720] + iff: [2562, 1932] + main: [960, 720] + viewfinder: [320, 240] +- bds: [2572, 1936] + cio2: [2576, 1936] + gdc: [2560, 1920] + iff: [2572, 1936] + main: [2560, 1920] + viewfinder: [320, 240] +- bds: [2592, 1944] + cio2: [2592, 1944] + gdc: [2560, 1920] + iff: [2592, 1944] + main: [1920, 1080] + viewfinder: [1600, 1200] From patchwork Wed Feb 9 07:19:14 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hanlin Chen X-Patchwork-Id: 15350 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 8AB1AC3257 for ; Wed, 9 Feb 2022 07:19:42 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 3ED83610AD; Wed, 9 Feb 2022 08:19:42 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=chromium.org header.i=@chromium.org header.b="JH8e250O"; dkim-atps=neutral Received: from mail-pj1-x102e.google.com (mail-pj1-x102e.google.com [IPv6:2607:f8b0:4864:20::102e]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id C52DA610AD for ; Wed, 9 Feb 2022 08:19:38 +0100 (CET) Received: by mail-pj1-x102e.google.com with SMTP id t4-20020a17090a510400b001b8c4a6cd5dso1388767pjh.5 for ; Tue, 08 Feb 2022 23:19:38 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=fa3mkD3ACoS+DPsjqPN52SMehX2h2X5YPUKVCZ/aGyc=; b=JH8e250OnhXTvoWjgNbqSCcFIJ81FBcZTEuC7FuVixpB6HjUX/GK3HqhCrYFdN7+/F DYdSFivv5hC4Xb0s+2MhFiGMbaySXc+o4bU/QssiVm/oNhdFuLQLgyf+HHXOzllcNAV4 2/ksbk0HC01Dj9dZTZZj2eN4LI61bvlXcHtEY= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=fa3mkD3ACoS+DPsjqPN52SMehX2h2X5YPUKVCZ/aGyc=; b=AvQ8BzAd9q/HPoV/04ghyMCRBtaxc6UXCl28s/S//BjLgZVKECcmDtMRx90RNxHCby DYImdFALmYQiQH04hbX7jA0QazNNJ5+PC7GoEOH///6g7iKpdRZiUaU2xC9cXUZ05G4C l5Wd0zf9mlA37V/ybreDW2Aa6wszTfw5Y4ufbcQRFwitnNIEcyr8cAP2XqKV0qOpKZCW nq8QCGuhSy9mQbdw6tCkRIy86/V4SPht5XxzioDCvN8vDq45m/qCoBmu5HGq+3glsGr3 G+h4p/zWyMsqAkfPTYGRfPk2Kv0ERJOr7OXJxNUV+P9PFHkg4XBgZd9fVgegZZbH4Ght HE7Q== X-Gm-Message-State: AOAM531ZArc5Gcjdya1Pmt92GuVYpifKRWmcxzjS5WLfyI8q/ePS1Vpp tdNkVuhWSzQA/S3bUUEJ28cHCXi8wcdo0g== X-Google-Smtp-Source: ABdhPJwwmcDov8KaFQyWQKI2s6BBDXEbwrgpf25RfRin8CF+3LWkvMQmYfGZWB7UezH/aKIUUVdaHQ== X-Received: by 2002:a17:902:760e:: with SMTP id k14mr923625pll.11.1644391176663; Tue, 08 Feb 2022 23:19:36 -0800 (PST) Received: from localhost ([2401:fa00:1:10:a5cf:9a43:1c6d:824]) by smtp.gmail.com with UTF8SMTPSA id h18sm18038960pfh.51.2022.02.08.23.19.35 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Tue, 08 Feb 2022 23:19:36 -0800 (PST) From: Han-Lin Chen To: libcamera-devel@lists.libcamera.org Date: Wed, 9 Feb 2022 15:19:14 +0800 Message-Id: <20220209071917.559993-7-hanlinchen@chromium.org> X-Mailer: git-send-email 2.35.0.263.gb82422642f-goog In-Reply-To: <20220209071917.559993-1-hanlinchen@chromium.org> References: <20220209071917.559993-1-hanlinchen@chromium.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 6/9] android: Set PipelineConfigFile option if it's supported by the camera X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" If the libcamera::Camera supports the PipelineConfigFile option and the HAL configuration file provides the path, set the option before opening the camera. Signed-off-by: Han-Lin Chen --- src/android/camera_device.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp index 83825736..61883a54 100644 --- a/src/android/camera_device.cpp +++ b/src/android/camera_device.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -293,6 +294,24 @@ std::unique_ptr CameraDevice::create(unsigned int id, */ int CameraDevice::initialize(const CameraConfigData *cameraConfigData) { + /* + * Initialize pipeline configuration file for the camera. + * + * If the libcamera::Camera supports the PipelineConfigFile option and + * the HAL configuration file provides the path, set the option before + * opening the camera. + */ + const ControlInfoMap optionInfo = camera_->options(); + + if (cameraConfigData && + cameraConfigData->pipelineConfigFile != "" && + optionInfo.count(&options::PipelineConfigFile)) { + ControlList options(optionInfo); + options.set(options::PipelineConfigFile, + cameraConfigData->pipelineConfigFile); + camera_->setOptions(&options); + } + /* * Initialize orientation and facing side of the camera. * From patchwork Wed Feb 9 07:19:15 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hanlin Chen X-Patchwork-Id: 15351 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 21FCCC3261 for ; Wed, 9 Feb 2022 07:19:47 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id BBE52610DA; Wed, 9 Feb 2022 08:19:42 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=chromium.org header.i=@chromium.org header.b="X5Fz9p7o"; dkim-atps=neutral Received: from mail-pl1-x62f.google.com (mail-pl1-x62f.google.com [IPv6:2607:f8b0:4864:20::62f]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id E6C0060202 for ; Wed, 9 Feb 2022 08:19:40 +0100 (CET) Received: by mail-pl1-x62f.google.com with SMTP id k17so1516594plk.0 for ; Tue, 08 Feb 2022 23:19:40 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=BXvbw/qFuh4/mv/AibxotkM+baDpuUOcjWD8E0nJer0=; b=X5Fz9p7o2v10nwD/Yv/oZkL8s5HINCb4vsc4Cpp7IrS1NDB0jeIQP2LPdmTuvBNFOg MRocsgb//OiBX70J/UuhUWolk1Fin/PbMhFemGwvneYhA7JPQb9f1J38HGeOs0xG6eDw flQfYnpltzUZVH3dzuwQn8MHMRSkDaEEYCpjU= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=BXvbw/qFuh4/mv/AibxotkM+baDpuUOcjWD8E0nJer0=; b=s0BnVde0kq00TQKIV/kNcT4F6u1ry/HO0hg3QOEVlr7HA4kQ9TnqMKOKIX7FNEbT3e DBl/2DhlGljPvU6v8tfrXQVxXhASz1k5DL004D6SC7WtKLioi5V1UJl7l8OtYLHVTqyC xhh/hvKr1KlkgVPF+5T6na0bXVXq1Pn4xentCR3DtuuEnSM/sg3Lhv31RsrWbFPuOvtW FZkjSUKGzZIMTMHUlXvnPOMHpNGTN6RRh1EyCFa9+UJnpBNg4Kbz9uWBdgplNRnKt69d rEmUjZDoLQAalSXuZQn/dPMWIMX5vltqP0TgQVr7GvztiETzgwM+ZEKy1/G0Cx0muXkd ejqQ== X-Gm-Message-State: AOAM530Bb43XXFth1YOsxUi9F2OdgBv5QVDQKPDDP7tPnka9cR19n7Ms x3WvqnMuwHkVQmx0iTV1iNvzGk7nYOJfvg== X-Google-Smtp-Source: ABdhPJxC0Uty7ZeV42eb0fBswVHFjDiKbb5Ozap1sU/j0O9JsSTDSKNmo88XJ6/Xaco7nLh5EbtmHw== X-Received: by 2002:a17:902:ba98:: with SMTP id k24mr925032pls.44.1644391178659; Tue, 08 Feb 2022 23:19:38 -0800 (PST) Received: from localhost ([2401:fa00:1:10:a5cf:9a43:1c6d:824]) by smtp.gmail.com with UTF8SMTPSA id c7sm18094412pfp.164.2022.02.08.23.19.37 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Tue, 08 Feb 2022 23:19:38 -0800 (PST) From: Han-Lin Chen To: libcamera-devel@lists.libcamera.org Date: Wed, 9 Feb 2022 15:19:15 +0800 Message-Id: <20220209071917.559993-8-hanlinchen@chromium.org> X-Mailer: git-send-email 2.35.0.263.gb82422642f-goog In-Reply-To: <20220209071917.559993-1-hanlinchen@chromium.org> References: <20220209071917.559993-1-hanlinchen@chromium.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 7/9] libcamera: ipu3: Add helper class PipeConfigPreference X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add helper class PipeConfigPreference to load the caliberated ipu3 pipeline configuration files, and provides query interface for the pipeline handler. Signed-off-by: Han-Lin Chen --- include/libcamera/geometry.h | 4 + src/libcamera/geometry.cpp | 20 ++ src/libcamera/pipeline/ipu3/meson.build | 1 + .../pipeline/ipu3/pipe_config_pref.cpp | 285 ++++++++++++++++++ .../pipeline/ipu3/pipe_config_pref.h | 80 +++++ 5 files changed, 390 insertions(+) create mode 100644 src/libcamera/pipeline/ipu3/pipe_config_pref.cpp create mode 100644 src/libcamera/pipeline/ipu3/pipe_config_pref.h diff --git a/include/libcamera/geometry.h b/include/libcamera/geometry.h index 7838b679..ede54981 100644 --- a/include/libcamera/geometry.h +++ b/include/libcamera/geometry.h @@ -46,6 +46,8 @@ static inline bool operator!=(const Point &lhs, const Point &rhs) return !(lhs == rhs); } +std::ostream &operator<<(std::ostream &out, const Point &d); + class Size { public: @@ -192,6 +194,8 @@ static inline bool operator>=(const Size &lhs, const Size &rhs) return !(lhs < rhs); } +std::ostream &operator<<(std::ostream &out, const Size &s); + class SizeRange { public: diff --git a/src/libcamera/geometry.cpp b/src/libcamera/geometry.cpp index cb3c2de1..a65f9f2f 100644 --- a/src/libcamera/geometry.cpp +++ b/src/libcamera/geometry.cpp @@ -83,6 +83,16 @@ bool operator==(const Point &lhs, const Point &rhs) * \return True if the two points are not equal, false otherwise */ +/** + * \brief Insert operation for Point with ostream + * \return The input std::ostream + */ +std::ostream &operator<<(std::ostream &out, const Point &p) +{ + out << "(" << p.x << ", " << p.y << ")"; + return out; +} + /** * \struct Size * \brief Describe a two-dimensional size @@ -428,6 +438,16 @@ bool operator<(const Size &lhs, const Size &rhs) * \sa bool operator<(const Size &lhs, const Size &rhs) */ +/** + * \brief Insert operation for Size with ostream + * \return The input std::ostream + */ +std::ostream &operator<<(std::ostream &out, const Size &s) +{ + out << s.width << "x" << s.height; + return out; +} + /** * \struct SizeRange * \brief Describe a range of sizes diff --git a/src/libcamera/pipeline/ipu3/meson.build b/src/libcamera/pipeline/ipu3/meson.build index a1b0b31a..dcc450f0 100644 --- a/src/libcamera/pipeline/ipu3/meson.build +++ b/src/libcamera/pipeline/ipu3/meson.build @@ -5,4 +5,5 @@ libcamera_sources += files([ 'frames.cpp', 'imgu.cpp', 'ipu3.cpp', + 'pipe_config_pref.cpp', ]) diff --git a/src/libcamera/pipeline/ipu3/pipe_config_pref.cpp b/src/libcamera/pipeline/ipu3/pipe_config_pref.cpp new file mode 100644 index 00000000..5b4a17c9 --- /dev/null +++ b/src/libcamera/pipeline/ipu3/pipe_config_pref.cpp @@ -0,0 +1,285 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2022, Google Inc. + * + * pipe_config_pref.cpp - Helper class for IPU3 pipeline config preference + */ + +#include "pipe_config_pref.h" + +#include + +#include + +namespace libcamera { + +LOG_DECLARE_CATEGORY(IPU3) + +namespace { + +std::ostream &operator<<(std::ostream &out, + const PipeConfigPreference::PipeConfig &d) +{ + out << "cio2: " << d.cio2 << " bds: " << d.bds + << " gdc: " << d.gdc << " iff: " << d.iff + << " main: " << d.main << " viewfinder: " << d.viewfinder; + return out; +} + +int loadPipeConfig(const YamlObject &yamlObject, + PipeConfigPreference::PipeConfig &pipeConfig) +{ + if (!yamlObject.isMember("bds") || + !yamlObject.isMember("gdc") || + !yamlObject.isMember("iff") || + !yamlObject.isMember("cio2") || + !yamlObject.isMember("main") || + !yamlObject.isMember("viewfinder")) { + LOG(IPU3, Error) << "Missing mandatory attributes in a config"; + return -EINVAL; + } + + pipeConfig.bds = yamlObject.get("bds").asSize(); + pipeConfig.gdc = yamlObject.get("gdc").asSize(); + pipeConfig.iff = yamlObject.get("iff").asSize(); + pipeConfig.cio2 = yamlObject.get("cio2").asSize(); + pipeConfig.main = yamlObject.get("main").asSize(); + pipeConfig.viewfinder = yamlObject.get("viewfinder").asSize(); + + return 0; +} + +int loadPipeConfigs(const YamlObject &yamlPipeConfigs, + std::vector &pipeConfigs, + Size &maxResolution, Size &minResolution) +{ + for (int i = 0; i < yamlPipeConfigs.length(); i++) { + const YamlObject &yamlConfig = yamlPipeConfigs[i]; + pipeConfigs.emplace_back(); + if (loadPipeConfig(yamlConfig, pipeConfigs.back())) + return -EINVAL; + } + + if (pipeConfigs.size() == 0) + return -EINVAL; + + maxResolution = minResolution = pipeConfigs[0].main; + + for (const PipeConfigPreference::PipeConfig &config : pipeConfigs) { + maxResolution.expandTo(config.main); + minResolution.boundTo(config.main); + } + + /* Sort configs by the size of the cio2 */ + sort(pipeConfigs.begin(), pipeConfigs.end(), + [](const auto &a, const auto &b) -> bool { + return a.cio2 < b.cio2; + }); + + return 0; +} + +} /* namespace */ + +PipeConfigPreference::PipeConfigPreference() + : valid_(false) +{ +} + +/** + * \struct PipeConfig + * \brief Describe a valid ImgU configuration + * + * The ImgU unit processes images through several components, which have + * to be properly configured inspecting the input image size and the desired + * output sizes. This structure collects the ImgU configuration for IF, BDS + * and GDC, and the requested main output, viewfinder and the input (CIO2) + * resolutions. + */ + +/** + * \brief Parse the configuration file from a path + * \param[in] path The path to the configuration file + * + * Parse the configuration file from a path and set isValid() to true if + * success. + * + * \return 0 on success or a negative error code otherwise + */ +int PipeConfigPreference::parsePreferenceFile(const std::string &path) +{ + valid_ = false; + + FILE *fh = fopen(path.c_str(), "r"); + if (!fh) { + LOG(IPU3, Error) << "Fail to open file: " << path; + return -EINVAL; + } + + YamlParser yamlParser; + YamlObject yamlObjectPreference; + + int ret = yamlParser.ParseAsYamlObject(fh, yamlObjectPreference); + fclose(fh); + + if (ret) + return -EINVAL; + + ret = load(yamlObjectPreference); + + if (ret) + return -EINVAL; + + valid_ = true; + return 0; +} + +/** + * \brief Query the valid pipeline configuration for video and still pipe + * \param[in] videoMain The size of main output from video pipe + * \param[in] videoViewfinder The size of viewfinder output from video pipe + * \param[in] stillMain The size of main output from still pipe + * \param[in] stillViewfinder The size of viewfinder output from still pipe + * \param[out] videoResult The ImgU setting for video pipe + * \param[out] stillResult The ImgU setting for still pipe + * + * Helper function to query valid settings for ImgU with the desired + * output resolutions. The query interface is based on the assumption + * that both video and still ImgU might be used together. + * An output can be set disabled if not required. If both main and viewfinder + * are set disabled for a ImgU, video or still, the corresponding pipeConfig + * undefined. For example, a typical usage is to only one video output is + * required, the user may set: + * + * videoMain = [width, height] + * videoViewfinder = Disabled + * stillMain = Disabled + * stillViewfinder = Disabled + * + * In the case, only the videoResult would be valid, since still pipe is not + * used. When both video and still ImgU are used, their cio2 will have the + * same resolution, since they should use the same raw capture. + * + * \return 0 on success or a negative error code otherwise + */ +int PipeConfigPreference::queryPipeConfig( + const Size &videoMain, const Size &videoViewfinder, + const Size &stillMain, const Size &stillViewfinder, + PipeConfig &videoResult, PipeConfig &stillResult) const +{ + bool hasVideo = (videoMain != Disabled) && (videoMain >= videoViewfinder); + bool hasStill = (stillMain != Disabled) && (stillMain >= stillViewfinder); + + if (!hasStill && !hasVideo) + return -EINVAL; + + std::vector validVideoConfigs; + std::vector validStillConfigs; + + for (auto &config : videoPipeConfigs_) { + if (config.main == videoMain && config.viewfinder == videoViewfinder) + validVideoConfigs.emplace_back(config); + } + + for (auto &config : stillPipeConfigs_) { + if (config.main == stillMain && config.viewfinder == stillViewfinder) + validStillConfigs.emplace_back(config); + } + + /* + * Since the configurations are sorted by the size of CIO2, pick + * the first valid resolution for lower bandwith. + */ + if (hasVideo && !hasStill) { + if (validVideoConfigs.empty()) + return -EINVAL; + videoResult = validVideoConfigs[0]; + return 0; + } + + if (hasStill && !hasVideo) { + if (validStillConfigs.empty()) + return -EINVAL; + stillResult = validStillConfigs[0]; + return 0; + } + + /* (hasVideo && hasStill) */ + bool found = false; + for (const PipeConfig &videoConfig : validVideoConfigs) { + for (const PipeConfig &stillConfig : validVideoConfigs) { + if (videoConfig.cio2 == stillConfig.cio2) { + found = true; + videoResult = videoConfig; + stillResult = stillConfig; + break; + } + } + } + + return (found) ? 0 : -EINVAL; +} + +void PipeConfigPreference::dump() +{ + LOG(IPU3, Debug) << "Video Pipe configs: "; + for (auto &configs : videoPipeConfigs_) { + LOG(IPU3, Debug) << configs; + } + + LOG(IPU3, Debug) << "Still Pipe configs: "; + for (auto &configs : stillPipeConfigs_) { + LOG(IPU3, Debug) << configs; + } +} + +int PipeConfigPreference::load(const YamlObject &configs) +{ + /* + * Load the pipeline configure file properties. + * + * Each valid configuration is a list of properties associated + * with the corresponding IMGU settings and grouped into still + * and video modes. For each configuration, the main output should + * be valid, and the viewfinder is optional. If the viewfinder is + * disabled, its width and height should be set to [0, 0]; + * + * still_mode: + * - bds: [width, height] + * cio2: [width, height] + * gdc: [width, height] + * iff: [width, height] + * main: [width, height] + * viewfinder: [0, 0] + * ... + * + * video_mode: + * - bds: [width, height] + * cio2: [width, height] + * gdc: [width, height] + * iff: [width, height] + * main: [width, height] + * viewfinder: [0, 0] + * ... + */ + + if (!configs.isMember("video_mode") || !configs.isMember("still_mode")) + return -EINVAL; + + videoPipeConfigs_.clear(); + stillPipeConfigs_.clear(); + + int ret = loadPipeConfigs(configs.get("video_mode"), videoPipeConfigs_, + maxVideoResolution_, minVideoResolution_); + if (ret) + return -EINVAL; + + ret = loadPipeConfigs(configs.get("still_mode"), stillPipeConfigs_, + maxStillResolution_, minStillResolution_); + if (ret) + return -EINVAL; + + return 0; +} + +} /* namespace libcamera */ diff --git a/src/libcamera/pipeline/ipu3/pipe_config_pref.h b/src/libcamera/pipeline/ipu3/pipe_config_pref.h new file mode 100644 index 00000000..08626d4e --- /dev/null +++ b/src/libcamera/pipeline/ipu3/pipe_config_pref.h @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2022, Google Inc. + * + * pipe_config_pref.h - Helper class for IPU3 pipeline config preference + */ + +#pragma once + +#include + +#include + +namespace libcamera { + +class YamlObject; + +class PipeConfigPreference final +{ +public: + constexpr static Size Disabled = Size(0, 0); + + struct PipeConfig { + Size cio2; + Size bds; + Size gdc; + Size iff; + Size main; + Size viewfinder; + }; + + PipeConfigPreference(); + + int parsePreferenceFile(const std::string &path); + bool isValid() const + { + return valid_; + } + bool invalid() + { + return valid_ = false; + } + Size maxVideoResolution() + { + return maxVideoResolution_; + } + Size maxStillResolution() + { + return maxStillResolution_; + } + Size minVideoResolution() + { + return minVideoResolution_; + } + Size minStillResolution() + { + return minStillResolution_; + } + + int queryPipeConfig(const Size &videoMain, const Size &videoViewfinder, + const Size &stillMain, const Size &stillViewfinder, + PipeConfig &videoPipeConfig, + PipeConfig &stillPipeConfig) const; + void dump(); + +private: + int load(const YamlObject &object); + bool valid_; + + std::vector videoPipeConfigs_; + std::vector stillPipeConfigs_; + + Size maxVideoResolution_; + Size maxStillResolution_; + + Size minVideoResolution_; + Size minStillResolution_; +}; + +} /* namespace libcamera */ From patchwork Wed Feb 9 07:19:16 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hanlin Chen X-Patchwork-Id: 15353 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 2A128C3262 for ; Wed, 9 Feb 2022 07:19:57 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 79D3D610F9; Wed, 9 Feb 2022 08:19:45 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=chromium.org header.i=@chromium.org header.b="QppWawhO"; dkim-atps=neutral Received: from mail-pf1-x430.google.com (mail-pf1-x430.google.com [IPv6:2607:f8b0:4864:20::430]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id DC613610F9 for ; Wed, 9 Feb 2022 08:19:42 +0100 (CET) Received: by mail-pf1-x430.google.com with SMTP id e3so1386115pfd.12 for ; Tue, 08 Feb 2022 23:19:42 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=dw278bQHV6XJPfx2edmz6pUTSApQY/UOPFBqZmgNrek=; b=QppWawhO7zG3NhY2F8zdJbHSeQizU9jZYId9dSObubxdIuvh+xHnhH6QOnZ1GM9tke HVphkeeIUq/bG+n94OQ/lqK/GfdwBuCPT5zTsj320CZ+8ZSUOEKR0xMa6l0reLyjB3gv FFpQSe9IPDYAbstcLuCg8lJByZVxdOg/4cLd8= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=dw278bQHV6XJPfx2edmz6pUTSApQY/UOPFBqZmgNrek=; b=miof8n+0C5qGU5qUaHG2DujqFkcYoWtLUEKkl3zgZZLcGvJLojaAON+ogf/otHEuJP D2kgyIILvAqEDLd82m4Y3F5/OKWJ0edluMTvDep9NQutI3jNyYcYXYWsA/mIHr7OeEwk axGPf0E01pTI2cQjonkjC6SJItbkaHSPTutEBf8i7HzAtj2UFX/zxY9JN149AHD8uH7u tOrtUe5G1BoqoFcFQbmUr9/gsTUS7kf/0PHCbH2QejyTRztyvZp+Lj+IUiywzYVpGtUy /GyswAncKzPL+mk1PqwPV42voiiZIPA5G9IjDPP1bdJsBrk8C7cK0Mp4vYN30RfpUERP Gmbg== X-Gm-Message-State: AOAM531yY4xe3s+rcyvbcJtvr93mVa8+F0z94Yf4YmiMaR/dXU8SeV04 mfxqP132kxa/mSbcvbytYodnqFNKhcknsQ== X-Google-Smtp-Source: ABdhPJxKdJ+mk8jzyU4jNuQymWfZPpjj1JirZsazW+xhGfReubiUqPhhtIVGTycWuSCby8Tp/qFkrw== X-Received: by 2002:a62:b618:: with SMTP id j24mr998678pff.42.1644391180743; Tue, 08 Feb 2022 23:19:40 -0800 (PST) Received: from localhost ([2401:fa00:1:10:a5cf:9a43:1c6d:824]) by smtp.gmail.com with UTF8SMTPSA id my18sm4993033pjb.57.2022.02.08.23.19.39 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Tue, 08 Feb 2022 23:19:40 -0800 (PST) From: Han-Lin Chen To: libcamera-devel@lists.libcamera.org Date: Wed, 9 Feb 2022 15:19:16 +0800 Message-Id: <20220209071917.559993-9-hanlinchen@chromium.org> X-Mailer: git-send-email 2.35.0.263.gb82422642f-goog In-Reply-To: <20220209071917.559993-1-hanlinchen@chromium.org> References: <20220209071917.559993-1-hanlinchen@chromium.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 8/9] libcamera: ipu3: Support PipelineConfigFile option X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add PipelineConfigFile option for ipu3, which supports setting a yaml configuration file which contains preferred ImgU configurations when certain output resolutions are requested. This is a supplement of the current calculation designed by https://github.com/intel/intel-ipu3-pipecfg. This changes the result of IPU3CameraConfiguration::validate() and PipelineHandlerIPU3::generateConfiguration(), and only validates stream configurations defined in the file. The user can cancel the option by calling setOptions() again without the option. Signed-off-by: Han-Lin Chen --- src/libcamera/pipeline/ipu3/ipu3.cpp | 215 ++++++++++++++++++++++++++- 1 file changed, 214 insertions(+), 1 deletion(-) diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp index 6c5617cd..2d43b760 100644 --- a/src/libcamera/pipeline/ipu3/ipu3.cpp +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -35,11 +36,16 @@ #include "cio2.h" #include "frames.h" #include "imgu.h" +#include "pipe_config_pref.h" namespace libcamera { LOG_DEFINE_CATEGORY(IPU3) +static const ControlInfoMap::Map IPU3Options = { + { &options::PipelineConfigFile, ControlInfo(0, 512, std::string()) }, +}; + static const ControlInfoMap::Map IPU3Controls = { { &controls::draft::PipelineDepth, ControlInfo(2, 3) }, }; @@ -62,6 +68,11 @@ public: void cancelPendingRequests(); void frameStart(uint32_t sequence); + bool hasPreference() const + { + return pipeConfigPreference_.isValid(); + } + CIO2Device cio2_; ImgUDevice *imgu_; @@ -85,6 +96,8 @@ public: ControlInfoMap ipaControls_; + PipeConfigPreference pipeConfigPreference_; + private: void queueFrameAction(unsigned int id, const ipa::ipu3::IPU3Action &action); @@ -107,6 +120,10 @@ public: Transform combinedTransform_; private: + Status adjustTransform(); + Status validateByCalulation(); + Status validateByPipeConfigPreference(); + /* * The IPU3CameraData instance is guaranteed to be valid as long as the * corresponding Camera instance is valid. In order to borrow a @@ -131,6 +148,8 @@ public: PipelineHandlerIPU3(CameraManager *manager); + int setOptions(Camera *camera, const ControlList *options) override; + CameraConfiguration *generateConfiguration(Camera *camera, const StreamRoles &roles) override; int configure(Camera *camera, CameraConfiguration *config) override; @@ -151,6 +170,7 @@ private: return static_cast(camera->_d()); } + int initOptions(IPU3CameraData *data); int initControls(IPU3CameraData *data); int updateControls(IPU3CameraData *data); int registerCameras(); @@ -158,6 +178,11 @@ private: int allocateBuffers(Camera *camera); int freeBuffers(Camera *camera); + CameraConfiguration *generateConfigurationByCalculation(Camera *camera, + const StreamRoles &roles); + CameraConfiguration *generateConfigurationByPreference(Camera *camera, + const StreamRoles &roles); + ImgUDevice imgu0_; ImgUDevice imgu1_; MediaDevice *cio2MediaDev_; @@ -172,7 +197,7 @@ IPU3CameraConfiguration::IPU3CameraConfiguration(IPU3CameraData *data) data_ = data; } -CameraConfiguration::Status IPU3CameraConfiguration::validate() +CameraConfiguration::Status IPU3CameraConfiguration::adjustTransform() { Status status = Valid; @@ -221,6 +246,25 @@ CameraConfiguration::Status IPU3CameraConfiguration::validate() * apply to the sensor to save us working it out again. */ combinedTransform_ = combined; + return status; +} + +CameraConfiguration::Status IPU3CameraConfiguration::validate() +{ + if (data_->hasPreference()) + return validateByPipeConfigPreference(); + + return validateByCalulation(); +} + +CameraConfiguration::Status IPU3CameraConfiguration::validateByCalulation() +{ + Status status = Valid; + + if (config_.empty()) + return Invalid; + + status = adjustTransform(); /* Cap the number of entries to the available streams. */ if (config_.size() > kMaxStreams) { @@ -412,13 +456,118 @@ CameraConfiguration::Status IPU3CameraConfiguration::validate() return status; } +CameraConfiguration::Status IPU3CameraConfiguration::validateByPipeConfigPreference() +{ + if (config_.empty() || config_.size() > 2) + return Invalid; + + Status status = adjustTransform(); + + bool mainAvailable = false; + StreamConfiguration *mainStream = nullptr; + StreamConfiguration *viewfinderStream = nullptr; + + /* Assign the mainStream as the stream with the larger size. */ + for (StreamConfiguration &cfg : config_) { + const PixelFormatInfo &info = PixelFormatInfo::info(cfg.pixelFormat); + if (info.colourEncoding != PixelFormatInfo::ColourEncodingYUV) { + return Invalid; + } + if (!mainAvailable) { + mainStream = &cfg; + } else if (mainStream->size < cfg.size) { + viewfinderStream = mainStream; + mainStream = &cfg; + } + } + + /* + * \todo Query and use the configuration for the still pipe when the + * pipeline handler is refactored to use both ImgU device for a camera. + */ + PipeConfigPreference::PipeConfig videoPipe; + PipeConfigPreference::PipeConfig stillPipe; + + int ret = data_->pipeConfigPreference_.queryPipeConfig( + mainStream->size, + (viewfinderStream) ? viewfinderStream->size : PipeConfigPreference::Disabled, + PipeConfigPreference::Disabled, + PipeConfigPreference::Disabled, + videoPipe, stillPipe); + if (ret) { + LOG(IPU3, Error) + << "Fail to find a valid configuration: " + << " main: " << mainStream->size + << " viewfinder: " + << ((viewfinderStream) ? viewfinderStream->size : PipeConfigPreference::Disabled); + return Invalid; + } + + cio2Configuration_.size = videoPipe.cio2; + cio2Configuration_.pixelFormat = formats::SGRBG10_IPU3; + cio2Configuration_.bufferCount = kBufferCount; + + pipeConfig_.bds = videoPipe.bds; + pipeConfig_.iif = videoPipe.iff; + pipeConfig_.gdc = videoPipe.gdc; + + const PixelFormatInfo &info = PixelFormatInfo::info(formats::NV12); + + mainStream->pixelFormat = formats::NV12; + mainStream->bufferCount = kBufferCount; + mainStream->stride = info.stride(mainStream->size.width, 0, 1); + mainStream->frameSize = info.frameSize(mainStream->size, 1); + mainStream->setStream(const_cast(&data_->outStream_)); + + if (viewfinderStream) { + viewfinderStream->pixelFormat = formats::NV12; + viewfinderStream->bufferCount = kBufferCount; + viewfinderStream->stride = info.stride(viewfinderStream->size.width, 0, 1); + viewfinderStream->frameSize = info.frameSize(viewfinderStream->size, 1); + viewfinderStream->setStream(const_cast(&data_->vfStream_)); + } + + return status; +} + PipelineHandlerIPU3::PipelineHandlerIPU3(CameraManager *manager) : PipelineHandler(manager), cio2MediaDev_(nullptr), imguMediaDev_(nullptr) { } +int PipelineHandlerIPU3::setOptions(Camera *camera, + const ControlList *options) +{ + IPU3CameraData *data = cameraData(camera); + + PipeConfigPreference &preference = data->pipeConfigPreference_; + preference.invalid(); + + if (options && options->contains(options::PipelineConfigFile)) { + std::string path = options->get(options::PipelineConfigFile); + int ret = preference.parsePreferenceFile(path); + if (ret) { + LOG(IPU3, Error) << "Fail to load pipline config " + << "preference file: " << path; + return -EINVAL; + } + } + + return 0; +} + CameraConfiguration *PipelineHandlerIPU3::generateConfiguration(Camera *camera, const StreamRoles &roles) +{ + IPU3CameraData *data = cameraData(camera); + if (data->hasPreference()) + return generateConfigurationByPreference(camera, roles); + + return generateConfigurationByCalculation(camera, roles); +} + +CameraConfiguration *PipelineHandlerIPU3::generateConfigurationByCalculation( + Camera *camera, const StreamRoles &roles) { IPU3CameraData *data = cameraData(camera); IPU3CameraConfiguration *config = new IPU3CameraConfiguration(data); @@ -505,6 +654,57 @@ CameraConfiguration *PipelineHandlerIPU3::generateConfiguration(Camera *camera, return config; } +CameraConfiguration *PipelineHandlerIPU3::generateConfigurationByPreference( + Camera *camera, const StreamRoles &roles) +{ + IPU3CameraData *data = cameraData(camera); + IPU3CameraConfiguration *config = new IPU3CameraConfiguration(data); + PipeConfigPreference &preference = data->pipeConfigPreference_; + + if (roles.empty()) + return config; + + for (const StreamRole role : roles) { + std::map> streamFormats; + PixelFormat pixelFormat = formats::NV12; + Size size; + + switch (role) { + case StreamRole::StillCapture: + size = preference.maxStillResolution(); + streamFormats[pixelFormat] = { { preference.maxStillResolution(), + preference.maxStillResolution() } }; + break; + + case StreamRole::Viewfinder: + case StreamRole::VideoRecording: + size = preference.maxVideoResolution(); + streamFormats[pixelFormat] = { { preference.maxVideoResolution(), + preference.maxVideoResolution() } }; + break; + + case StreamRole::Raw: + default: + LOG(IPU3, Error) + << "Requested stream role not supported: " << role; + delete config; + return nullptr; + } + + StreamFormats formats(streamFormats); + StreamConfiguration cfg(formats); + cfg.size = size; + cfg.pixelFormat = pixelFormat; + cfg.bufferCount = IPU3CameraConfiguration::kBufferCount; + config->addConfiguration(cfg); + } + + if (config->validate() == CameraConfiguration::Invalid) + return {}; + + return config; +} + int PipelineHandlerIPU3::configure(Camera *camera, CameraConfiguration *c) { IPU3CameraConfiguration *config = @@ -971,6 +1171,15 @@ int PipelineHandlerIPU3::initControls(IPU3CameraData *data) return updateControls(data); } +int PipelineHandlerIPU3::initOptions(IPU3CameraData *data) +{ + /* Expose IPU3 options. */ + ControlInfoMap::Map options = IPU3Options; + data->optionInfo_ = ControlInfoMap(std::move(options), options::options); + + return 0; +} + /** * \brief Update the camera controls * \param[in] data The camera data @@ -1118,6 +1327,10 @@ int PipelineHandlerIPU3::registerCameras() /* Initialize the camera properties. */ data->properties_ = cio2->sensor()->properties(); + ret = initOptions(data.get()); + if (ret) + continue; + ret = initControls(data.get()); if (ret) continue; From patchwork Wed Feb 9 07:19:17 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hanlin Chen X-Patchwork-Id: 15352 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 2A5F8C3263 for ; Wed, 9 Feb 2022 07:19:57 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id E7E7E610FA; Wed, 9 Feb 2022 08:19:45 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=chromium.org header.i=@chromium.org header.b="NWnxySjH"; dkim-atps=neutral Received: from mail-pl1-x636.google.com (mail-pl1-x636.google.com [IPv6:2607:f8b0:4864:20::636]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 4C49D610EB for ; Wed, 9 Feb 2022 08:19:44 +0100 (CET) Received: by mail-pl1-x636.google.com with SMTP id x4so1478953plb.4 for ; Tue, 08 Feb 2022 23:19:44 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=Xt4lfOQkowpQXAH73t5mp6GGmsHlhDFYyEAO6kd44jw=; b=NWnxySjHAJucfSDM8cwryoE6s3y8rS8NfNIwN2UyE2r4vg+LkyQCXrP2izT741vtnG ZDnr6UA3WtZ/8RzqQdSSylVa+5WgDcRaBewYJIv0UToIEnELGps4aKmMkMiS6k3b2AtT WsJWne9cKpdPIDlnMR3OtQHtS4jjipmxFliP8= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=Xt4lfOQkowpQXAH73t5mp6GGmsHlhDFYyEAO6kd44jw=; b=AokOVHA1/Hf8eKY7gIYgR9ucQtfOBKOdLp0AyBMCoE+LCpxRvNXymoFAenWZzadKmO AdDgUIP6T5pHsPafTSyUX0F0euFwynKH7b/KDozMi7J03q2HbSIT3jxI3CSQdxeNkPEB VtI5XseOnTjqtb4apCZ0jBjGQdm8JXNXpw+AgwOLkC9nyXsnwi9t3shCR3RvaQZiQMlM 4jAGBRK/YmOJP0KdTMAXm+XzZlYYqSUjNgX3qSYWaUM4xAgd50Q5zRr7yIrDHRnJ2Hbk NpFK917107+V163Xm+9Ydm6qrvuYwaW727duy/7UHBEcIFjWyz4pFPfD2mb0x2uW38gf JmWg== X-Gm-Message-State: AOAM532YkO2d6eNiHyNPedIMJo/z18iaqrmtFXQJNwPXRE8d8T1LvmUs BcpKI1zGcFfhgWwejLjXe8paXuGl4+Nwlw== X-Google-Smtp-Source: ABdhPJz87QWNvko2IRU+AgRe2OkTsd6kMAambt90bk2CEQp6d/kyLP/PT/36vd6FU7tbumvFWzAF6g== X-Received: by 2002:a17:903:2d2:: with SMTP id s18mr1139085plk.56.1644391182728; Tue, 08 Feb 2022 23:19:42 -0800 (PST) Received: from localhost ([2401:fa00:1:10:a5cf:9a43:1c6d:824]) by smtp.gmail.com with UTF8SMTPSA id k16sm12562724pgh.45.2022.02.08.23.19.41 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Tue, 08 Feb 2022 23:19:42 -0800 (PST) From: Han-Lin Chen To: libcamera-devel@lists.libcamera.org Date: Wed, 9 Feb 2022 15:19:17 +0800 Message-Id: <20220209071917.559993-10-hanlinchen@chromium.org> X-Mailer: git-send-email 2.35.0.263.gb82422642f-goog In-Reply-To: <20220209071917.559993-1-hanlinchen@chromium.org> References: <20220209071917.559993-1-hanlinchen@chromium.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 9/9] android: Elevate min duration to 30 FPS if it's lower within 2% X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" It's notice that Chrome Camera App filters out the resolutions which cannot achieve 30 FPS. Elevate the minimum frame duration to 30 FPS if FPS is lower within 2%. Signed-off-by: Han-Lin Chen --- src/android/camera_capabilities.cpp | 31 +++++++++++++++++++---------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/android/camera_capabilities.cpp b/src/android/camera_capabilities.cpp index 55d651f3..e10ab036 100644 --- a/src/android/camera_capabilities.cpp +++ b/src/android/camera_capabilities.cpp @@ -655,7 +655,9 @@ int CameraCapabilities::initializeStreamConfigurations() int64_t maxFrameDuration = frameDurations->second.max().get() * 1000; /* - * Cap min frame duration to 30 FPS with 1% tolerance. + * Cap min frame duration to 30 FPS with 1% tolerance, + * and elevate min frame duration to 30 FPS with 2% + * tolerance. * * 30 frames per second has been validated as the most * opportune frame rate for quality tuning, and power @@ -675,17 +677,24 @@ int CameraCapabilities::initializeStreamConfigurations() * to the in-development configuration API rework. */ int64_t minFrameDurationCap = 1e9 / 30.0; - if (minFrameDuration < minFrameDurationCap) { - float tolerance = - (minFrameDurationCap - minFrameDuration) * 100.0 / minFrameDurationCap; + float tolerance = + (minFrameDurationCap - minFrameDuration) * 100.0 / minFrameDurationCap; - /* - * If the tolerance is less than 1%, do not cap - * the frame duration. - */ - if (tolerance > 1.0) - minFrameDuration = minFrameDurationCap; - } + /* + * If the tolerance is less than 1%, do not cap + * the frame duration. + */ + if (tolerance > 1.0) + minFrameDuration = minFrameDurationCap; + + /* + * Some applications, ex: Chrome Camera App filters out + * the resolutions which cannot achieve 30 FPS. Elevate + * the minimum frame duration to 30 FPS if FPS is lower + * within 2%. + */ + if (tolerance < 0 && tolerance > -2.0) + minFrameDuration = minFrameDurationCap; streamConfigurations_.push_back({ res, androidFormat, minFrameDuration, maxFrameDuration,