From patchwork Thu Jun 6 20:56:50 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 1373 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 4AC9F64092 for ; Thu, 6 Jun 2019 22:56:58 +0200 (CEST) Received: from localhost.localdomain (cpc89242-aztw30-2-0-cust488.18-1.cable.virginm.net [86.31.129.233]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id C47BB549; Thu, 6 Jun 2019 22:56:57 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1559854617; bh=6I1PCG8fdueE3foNeBpttwGWSJJaHxOnK9M2uP+NVxw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=QhAffW+1GRlGVN8ElvtMM4BBLMDzRb/lIjUSucl6IzNrqu5l3duks1Ut9/PazAe6y +o7LGkfwhMvD8OTGOI22D7/KKVdoyfo392GzITnWUB7+3IxbmxXJLaiJxVdcJNc/i/ lMOZNWyWDiw7pt1VIm7vAuGDkALGoIMZT5knOTtI= From: Kieran Bingham To: LibCamera Devel Date: Thu, 6 Jun 2019 21:56:50 +0100 Message-Id: <20190606205654.9311-2-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190606205654.9311-1-kieran.bingham@ideasonboard.com> References: <20190606205654.9311-1-kieran.bingham@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [RFC PATCH 1/5] libcamera: Add control handling X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 06 Jun 2019 20:56:58 -0000 --- include/libcamera/controls.h | 106 ++++++++++++ include/libcamera/meson.build | 1 + src/libcamera/controls.cpp | 310 ++++++++++++++++++++++++++++++++++ src/libcamera/meson.build | 1 + 4 files changed, 418 insertions(+) create mode 100644 include/libcamera/controls.h create mode 100644 src/libcamera/controls.cpp diff --git a/include/libcamera/controls.h b/include/libcamera/controls.h new file mode 100644 index 000000000000..0b751aa7a9d2 --- /dev/null +++ b/include/libcamera/controls.h @@ -0,0 +1,106 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * controls.h - Control handling + */ +#ifndef __LIBCAMERA_CONTROLS_H__ +#define __LIBCAMERA_CONTROLS_H__ + +#include +#include + +namespace libcamera { + +enum ControlId : uint32_t { + /* IPA Controls */ + IpaAwbEnable, + + /* Manual Controls */ + ManualBrightness, + ManualExposure, + ManualGain, +}; + +enum ControlType : uint32_t { + ControlBool, + ControlInteger, + ControlString, +}; + +enum ControlAction : uint8_t { + ControlWrite = 1, + ControlRead = 2, + ControlWriteRead = 3, +}; + +class ControlValue +{ +public: + ControlValue(enum ControlType type); + ControlValue(bool value); + ControlValue(int value); + ControlValue(const char *value); + ControlValue(const std::string &value); + + ControlType type() const { return type_; }; + + bool isRead() const { return action_ == ControlRead || + action_ == ControlWriteRead; }; + bool isWrite() const { return action_ == ControlWrite || + action_ == ControlWriteRead; }; + + void set(bool value); + void set(int value); + void set(unsigned int value); + + void set(const char *value); + void set(const std::string &value); + + bool getBool() const; + int getInt() const; + std::string getString() const; + + std::string toString() const; + +private: + ControlType type_; + ControlAction action_; + + union { + bool bool_; + int integer_; + }; + std::string string_; +}; + +std::ostream &operator<<(std::ostream &stream, const ControlValue &value); + +class Control +{ +public: + /* Create a Read control */ + Control(ControlId id); + + /* Create a Write control */ + Control(ControlId id, bool value); + Control(ControlId id, int value); + + ControlId id() const { return id_; }; + ControlValue &value() { return value_; }; + + std::string toString() const; + + /* Overloaded for std::set usage */ + bool operator<(const Control &rhs) const { return id_ < rhs.id_; }; + +private: + ControlId id_; + ControlValue value_; +}; + +std::ostream &operator<<(std::ostream &stream, const Control &control); + +} /* namespace libcamera */ + +#endif /* __LIBCAMERA_CONTROLS_H__ */ diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build index 1fcf6b509a1e..6a69db60b3c9 100644 --- a/include/libcamera/meson.build +++ b/include/libcamera/meson.build @@ -1,5 +1,6 @@ libcamera_api = files([ 'buffer.h', + 'controls.h', 'camera.h', 'camera_manager.h', 'event_dispatcher.h', diff --git a/src/libcamera/controls.cpp b/src/libcamera/controls.cpp new file mode 100644 index 000000000000..bef0ddcc89a3 --- /dev/null +++ b/src/libcamera/controls.cpp @@ -0,0 +1,310 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * controls.cpp - Control handling + */ + +#include +#include + +#include + +#include "log.h" + +/** + * \file controls.h + * \brief Describes all controls supported by a camera + */ + +namespace libcamera { + +LOG_DEFINE_CATEGORY(ControlId) + +/** + * \enum Controls + * Control Identifiers + * \var IpaAwbEnable + * bool: Enables or disables the AWB + * \var ManualExposure + * Manually control the exposure time in milli-seconds + * \var ManualGain + * Controls the value of the manual gain setting + */ + +/** + * \enum ControlType + * Determines the type of value represented by a \a ControlValue + * \var ControlBool + * Identifies controls storing a boolean value + * \var ControlInteger + * Identifies controls storing an integer value + * \var ControlString + * Identifies controls storing a string value. + */ + +/** + * \enum ControlAction + * Declare the intent of the ControlValue. + * \var ControlNoop + * The ControlValue has not been given an action to perform. It can exist as + * part of the request, but will not be acted upon by the device. + * \var ControlWrite + * The ControlValue is to be given and set at the camera + * \var ControlRead + * The ControlValue is to be read from the camera + * \var ControlWriteRead + * The ControlValue should be written to the camera and read before request + * completion + */ + +/* + * Two sets of tables are generated for the control definitions. + * + * An enum to declare the ID (in controls.h), and a type to establish its + * representation. + * + * Todo: Automate the generation of both tables from a single input table. + */ +struct ControlIdentifier { + ControlId id; + const char *name; + ControlType type; +} static const ControlTypes[] = { +#define CONTROL_TYPE(_id, _type) { _id, #_id, _type } + + CONTROL_TYPE(IpaAwbEnable, ControlBool), + CONTROL_TYPE(ManualBrightness, ControlInteger), + CONTROL_TYPE(ManualExposure, ControlInteger), + CONTROL_TYPE(ManualGain, ControlInteger), + +#undef CONTROL_TYPE +}; + +static struct ControlIdentifier FindControlType(ControlId id) +{ + for (auto ident : ControlTypes) { + if (ident.id == id) + return ident; + } + + LOG(ControlId, Fatal) << "Failed to find a ControlType."; + + /* Unreachable. */ + return {}; +} + +static ControlType GetControlType(ControlId id) +{ + return FindControlType(id).type; +} + +static std::string ControlIDName(ControlId id) +{ + return FindControlType(id).name; +} + +/** + * \brief Construct a ControlValue of a given type without initialising a value + * \param[in] type The type of ControlValue to be read + */ +ControlValue::ControlValue(ControlType type) + : type_(type), action_(ControlRead) +{ +} + +/** + * \brief Construct a Boolean Control Value + * \param[in] value Boolean value to store + */ +ControlValue::ControlValue(bool value) + : type_(ControlBool), action_(ControlWrite), bool_(value) +{ +} + +/** + * \brief Construct an integer Control Value + * \param[in] value Integer value to store + */ +ControlValue::ControlValue(int value) + : type_(ControlInteger), action_(ControlWrite), integer_(value) +{ +} + +/** + * \brief Construct a string Control Value + * \param[in] value String representation to store + */ +ControlValue::ControlValue(const char *value) + : type_(ControlString), action_(ControlWrite), string_(value) +{ +} + +/** + * \brief Construct a string Control Value + * \param[in] value String representation to store + */ +ControlValue::ControlValue(const std::string &value) + : type_(ControlString), action_(ControlWrite), string_(value) +{ +} + +/** + * \brief Set the Control value with a boolean + * \param[in] value Boolean value to store + */ +void ControlValue::set(bool value) +{ + ASSERT(type_ == ControlBool); + + bool_ = value; +} + +/** + * \brief Set the Control value with an integer + * \param[in] value Integer value to store + */ +void ControlValue::set(int value) +{ + ASSERT(type_ == ControlInteger); + + integer_ = value; +} + +/** + * \brief Set the Control value with a string representation + * \param[in] value String value to store + */ +void ControlValue::set(const char *value) +{ + ASSERT(type_ == ControlString); + + string_ = value; +} + +/** + * \brief Set the Control value with a string representation + * \param[in] value String value to store + */ +void ControlValue::set(const std::string &value) +{ + ASSERT(type_ == ControlString); + + string_ = value; +} + +/** + * \brief Get the Control value. + * + * The ControlValue type must be Boolean. + */ +bool ControlValue::getBool() const +{ + ASSERT(type_ == ControlBool); + + return bool_; +} + +/** + * \brief Get the Control value. + * + * The ControlValue type must be Integer. + */ +int ControlValue::getInt() const +{ + ASSERT(type_ == ControlInteger); + + return integer_; +} + +/** + * \brief Get the Control value. + * + * The ControlValue type must be String. + */ +std::string ControlValue::getString() const +{ + ASSERT(type_ == ControlString); + + return string_; +} + +/** + * \brief Prepare a string representation of the ControlValue + */ +std::string ControlValue::toString() const +{ + switch (type_) { + case ControlBool: + return bool_ ? "True" : "False"; + case ControlInteger: + return std::to_string(integer_); + case ControlString: + return string_; + } + + /* Unreachable */ + return ""; +} + +std::ostream &operator<<(std::ostream &stream, const ControlValue &value) +{ + return stream << value.toString(); +} + + +/** + * \brief Create a new empty control + * \param[in] id ID of the new control + * + * The control is constructed, and the type determined by the \a ControlId. + */ +Control::Control(ControlId id) + : id_(id), value_(GetControlType(id)) +{ +} + +/** + * \brief Create a new control + * \param[in] id ID of the new control + * \param[in] value Boolean value to apply to the new control + * + * The control is constructed, and the type determined by the \a ControlId. + */ +Control::Control(ControlId id, bool value) + : id_(id), value_(value) +{ + ASSERT(GetControlType(id) == ControlBool); +} + +/** + * \brief Create a new control + * \param[in] id ID of the new control + * \param[in] value Integer value to apply to the new control + * + * The control is constructed, and the type determined by the \a ControlId. + */ +Control::Control(ControlId id, int value) + : id_(id), value_(value) +{ + ASSERT(GetControlType(id) == ControlInteger); +} + +/** + * \brief Prepare a string representation of the Control + */ +std::string Control::toString() const +{ + std::stringstream ss; + + ss << "Control: " << ControlIDName(id_) << " : " << value_; + + return ss.str(); +} + +std::ostream &operator<<(std::ostream &stream, const Control &control) +{ + return stream << control.toString(); +} + +} /* namespace libcamera */ diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index fa1fbcb5faf5..7c67fdf3691a 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -3,6 +3,7 @@ libcamera_sources = files([ 'camera.cpp', 'camera_manager.cpp', 'camera_sensor.cpp', + 'controls.cpp', 'device_enumerator.cpp', 'device_enumerator_sysfs.cpp', 'event_dispatcher.cpp', From patchwork Thu Jun 6 20:56:51 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 1374 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 817C860BB7 for ; Thu, 6 Jun 2019 22:56:58 +0200 (CEST) Received: from localhost.localdomain (cpc89242-aztw30-2-0-cust488.18-1.cable.virginm.net [86.31.129.233]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 1BEBCD82; Thu, 6 Jun 2019 22:56:58 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1559854618; bh=EOPTi+o2XngfHcaFeZdHNGmoqgxaV6QGfoDCX9Y0Lkc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=FySkFZKi7Z4SL9NiryNeBSWqOscp7iUYZviOf/2PNDC9UazSIGnr8eu+Sn0v+I8BZ 3jVOJq+n14p79UD762/ck+MXg7NcqhSMRx6afD18F12+2eQpW0vXS1VrWZQLha6Yki ULjKZtsyq3iVxcxgeHpL5n6Iei0OX1mla/0xrU6Y= From: Kieran Bingham To: LibCamera Devel Date: Thu, 6 Jun 2019 21:56:51 +0100 Message-Id: <20190606205654.9311-3-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190606205654.9311-1-kieran.bingham@ideasonboard.com> References: <20190606205654.9311-1-kieran.bingham@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [RFC PATCH 2/5] libcamera: request: add a control set X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 06 Jun 2019 20:56:58 -0000 Provide a set to contain all controls applicable to the request. The set contains all controls whether they are write, read, or write-read controls. Signed-off-by: Kieran Bingham --- include/libcamera/request.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/libcamera/request.h b/include/libcamera/request.h index 58de6f00a554..5fae0d5fc838 100644 --- a/include/libcamera/request.h +++ b/include/libcamera/request.h @@ -10,6 +10,7 @@ #include #include +#include #include namespace libcamera { @@ -36,6 +37,8 @@ public: int setBuffers(const std::map &streamMap); Buffer *findBuffer(Stream *stream) const; + std::set &controls() { return controls_; }; + Status status() const { return status_; } bool hasPendingBuffers() const { return !pending_.empty(); } @@ -52,6 +55,7 @@ private: Camera *camera_; std::map bufferMap_; std::unordered_set pending_; + std::set controls_; Status status_; }; From patchwork Thu Jun 6 20:56:52 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 1375 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id C881860BB7 for ; Thu, 6 Jun 2019 22:56:58 +0200 (CEST) Received: from localhost.localdomain (cpc89242-aztw30-2-0-cust488.18-1.cable.virginm.net [86.31.129.233]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 6399ED87; Thu, 6 Jun 2019 22:56:58 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1559854618; bh=jB7fjuFM07QGwg1K2Vzb0MDgOJEvI0E6GIBudIfQw+A=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=WEc0BuIrxd/1vtV0qtD0zCZ2EYy9FDxqDggcx6CePwKt32GcNHpreuQoTK0Fg/vqb z6AOGFxocGjIuCSu4Cdd29749Tdk7UG1OpiqdfG2t2IedKwXPL7ccPuLkNyMBTR9u2 XBBoJMVTkQycVR7cHOD7U9iIGuuTya4UuA7gLdUQ= From: Kieran Bingham To: LibCamera Devel Date: Thu, 6 Jun 2019 21:56:52 +0100 Message-Id: <20190606205654.9311-4-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190606205654.9311-1-kieran.bingham@ideasonboard.com> References: <20190606205654.9311-1-kieran.bingham@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [RFC PATCH 3/5] libcamera: pipeline: Add readControls(), writeControl() interfaces X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 06 Jun 2019 20:56:59 -0000 Pipeline handlers must implement functions to handle controls as part of their interface. Extend the pipeline handler interface to declare this requirement and implement basic control functionality in the currently supported PipelineHandlers --- src/libcamera/include/pipeline_handler.h | 3 + src/libcamera/pipeline/ipu3/ipu3.cpp | 19 ++++ src/libcamera/pipeline/raspberrypi.cpp | 108 ++++++++++++++++++- src/libcamera/pipeline/rkisp1/rkisp1.cpp | 19 ++++ src/libcamera/pipeline/uvcvideo.cpp | 127 ++++++++++++++++++++++- src/libcamera/pipeline/vimc.cpp | 19 ++++ 6 files changed, 293 insertions(+), 2 deletions(-) diff --git a/src/libcamera/include/pipeline_handler.h b/src/libcamera/include/pipeline_handler.h index 7da6df1ab2a0..4e306d964bff 100644 --- a/src/libcamera/include/pipeline_handler.h +++ b/src/libcamera/include/pipeline_handler.h @@ -72,6 +72,9 @@ public: virtual int start(Camera *camera) = 0; virtual void stop(Camera *camera); + virtual int readControls(Camera *camera, Request *request) = 0; + virtual int writeControls(Camera *camera, Request *request) = 0; + virtual int queueRequest(Camera *camera, Request *request); bool completeBuffer(Camera *camera, Request *request, Buffer *buffer); diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp index 11e25e276c81..811a7c85bfc8 100644 --- a/src/libcamera/pipeline/ipu3/ipu3.cpp +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp @@ -213,6 +213,9 @@ public: int start(Camera *camera) override; void stop(Camera *camera) override; + int readControls(Camera *camera, Request *request) override; + int writeControls(Camera *camera, Request *request) override; + int queueRequest(Camera *camera, Request *request) override; bool match(DeviceEnumerator *enumerator) override; @@ -812,6 +815,22 @@ void PipelineHandlerIPU3::stop(Camera *camera) PipelineHandler::stop(camera); } +int PipelineHandlerIPU3::readControls(Camera *camera, Request *request) +{ + LOG(IPU3, Error) + << "NOT IMPLEMENTED"; + + return -EINVAL; +} + +int PipelineHandlerIPU3::writeControls(Camera *camera, Request *request) +{ + LOG(IPU3, Error) + << "NOT IMPLEMENTED"; + + return -EINVAL; +} + int PipelineHandlerIPU3::queueRequest(Camera *camera, Request *request) { int error = 0; diff --git a/src/libcamera/pipeline/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi.cpp index d6749eaae759..337554501c82 100644 --- a/src/libcamera/pipeline/raspberrypi.cpp +++ b/src/libcamera/pipeline/raspberrypi.cpp @@ -15,6 +15,7 @@ #include "media_device.h" #include "pipeline_handler.h" #include "utils.h" +#include "v4l2_controls.h" #include "v4l2_device.h" namespace libcamera { @@ -77,6 +78,9 @@ public: int start(Camera *camera) override; void stop(Camera *camera) override; + int writeControls(Camera *camera, Request *request); + int readControls(Camera *camera, Request *request); + int queueRequest(Camera *camera, Request *request) override; bool match(DeviceEnumerator *enumerator) override; @@ -293,6 +297,94 @@ void PipelineHandlerRPi::stop(Camera *camera) PipelineHandler::stop(camera); } +int PipelineHandlerRPi::writeControls(Camera *camera, Request *request) +{ + RPiCameraData *data = cameraData(camera); + + std::vector controls; + + for (Control c : request->controls()) { + if (c.value().isWrite()) { + switch (c.id()) { + case ManualGain: + break; + case ManualExposure: + break; + default: + LOG(RPI, Error) + << "Unhandled write control"; + break; + } + } + } + + /* Perhaps setControls could accept an empty vector/list as success? */ + if (!controls.empty()) + return data->unicam_->setControls(controls); + + return 0; +} + +/* This is becoming horrible. How can we improve this + * - Iterate reqeust controls to find controls to read + * - Add those to a new list + * - Query the device for that list to get controls + * - iterate returned list + * - For each control, search for corresponding control in request + * - Set control value to return value. + * - Return list. + * - Check for any values that were not updated? + */ +int PipelineHandlerRPi::readControls(Camera *camera, Request *request) +{ + RPiCameraData *data = cameraData(camera); + std::vector controlIDs; + std::vector controls; + + for (Control c : request->controls()) { + if (c.value().isRead()) { + LOG(RPI, Error) << "Read Control: " << c; + + switch (c.id()) { + case ManualGain: + controlIDs.push_back(V4L2_CID_ANALOGUE_GAIN); + break; + case ManualExposure: + controlIDs.push_back(V4L2_CID_EXPOSURE); + break; + default: + LOG(RPI, Error) + << "Unhandled write control"; + break; + } + } + } + + /* Perhaps setControls could accept an empty vector/list as success? */ + if (controlIDs.empty()) + return 0; + + controls = data->unicam_->getControls(controlIDs); + if (controls.empty()) + return -ENODATA; + + for (V4L2Control *ctrl : controls) { + switch(ctrl->id()) { + case V4L2_CID_ANALOGUE_GAIN: + //Control gain(ManualGain); //= request->controls().find(ManualGain); + auto it = request->controls().find(ManualGain); + Control gain = *it; + + //V4L2IntControl *c = static_cast(ctrl); + gain.value().set(0x88); + + break; + } + } + + return 0; +} + int PipelineHandlerRPi::queueRequest(Camera *camera, Request *request) { RPiCameraData *data = cameraData(camera); @@ -304,7 +396,15 @@ int PipelineHandlerRPi::queueRequest(Camera *camera, Request *request) return -ENOENT; } - int ret = data->isp_.capture->queueBuffer(buffer); + /* + * Handle 'set' controls first + * Todo: Validate ordering and timing. + */ + int ret = writeControls(camera, request); + if (ret < 0) + return ret; + + ret = data->isp_.capture->queueBuffer(buffer); if (ret < 0) return ret; @@ -394,6 +494,12 @@ void RPiCameraData::ispCaptureReady(Buffer *buffer) Request *request = queuedRequests_.front(); pipe_->completeBuffer(camera_, request, buffer); + + int ret = pipe_->readControls(camera_, request); + if (ret < 0) + LOG(RPI, Error) + << "Failed to read controls. No way to pass error"; + pipe_->completeRequest(camera_, request); } diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp index 9b3eea2f6dd3..d618d9c34a22 100644 --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp @@ -88,6 +88,9 @@ public: int start(Camera *camera) override; void stop(Camera *camera) override; + int readControls(Camera *camera, Request *request) override; + int writeControls(Camera *camera, Request *request) override; + int queueRequest(Camera *camera, Request *request) override; bool match(DeviceEnumerator *enumerator) override; @@ -359,6 +362,22 @@ void PipelineHandlerRkISP1::stop(Camera *camera) activeCamera_ = nullptr; } +int PipelineHandlerRkISP1::readControls(Camera *camera, Request *request) +{ + LOG(RkISP1, Error) + << "NOT IMPLEMENTED"; + + return -EINVAL; +} + +int PipelineHandlerRkISP1::writeControls(Camera *camera, Request *request) +{ + LOG(RkISP1, Error) + << "NOT IMPLEMENTED"; + + return -EINVAL; +} + int PipelineHandlerRkISP1::queueRequest(Camera *camera, Request *request) { RkISP1CameraData *data = cameraData(camera); diff --git a/src/libcamera/pipeline/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo.cpp index e2d02c0dafb8..216fbe237827 100644 --- a/src/libcamera/pipeline/uvcvideo.cpp +++ b/src/libcamera/pipeline/uvcvideo.cpp @@ -14,6 +14,7 @@ #include "media_device.h" #include "pipeline_handler.h" #include "utils.h" +#include "v4l2_controls.h" #include "v4l2_device.h" namespace libcamera { @@ -73,6 +74,9 @@ public: int start(Camera *camera) override; void stop(Camera *camera) override; + int readControls(Camera *camera, Request *request) override; + int writeControls(Camera *camera, Request *request) override; + int queueRequest(Camera *camera, Request *request) override; bool match(DeviceEnumerator *enumerator) override; @@ -252,6 +256,117 @@ void PipelineHandlerUVC::stop(Camera *camera) PipelineHandler::stop(camera); } +int PipelineHandlerUVC::writeControls(Camera *camera, Request *request) +{ + UVCCameraData *data = cameraData(camera); + + std::vector controls; + + for (Control c : request->controls()) { + if (c.value().isWrite()) { + switch (c.id()) { + case ManualBrightness: + controls.emplace_back(new V4L2IntControl(V4L2_CID_BRIGHTNESS, c.value().getInt())); + break; + + case IpaAwbEnable: + case ManualGain: + case ManualExposure: + break; + default: + LOG(UVC, Error) + << "Unhandled write control"; + break; + } + } + } + + /* Perhaps setControls could accept an empty vector/list as success? */ + if (!controls.empty()) { + int ret = data->video_->setControls(controls); + + /* It would be nice to avoid the need to manually delete + * the controls */ + for (V4L2Control *c : controls) + delete c; + + return ret; + } + + + return 0; +} + +int PipelineHandlerUVC::readControls(Camera *camera, Request *request) +{ + UVCCameraData *data = cameraData(camera); + std::vector controlIDs; + std::vector controls; + + for (Control c : request->controls()) { + if (c.value().isRead()) { + LOG(UVC, Error) << "Read Control: " << c; + + switch (c.id()) { + case ManualBrightness: + controlIDs.push_back(V4L2_CID_BRIGHTNESS); + break; + case ManualGain: + controlIDs.push_back(V4L2_CID_ANALOGUE_GAIN); + break; + case ManualExposure: + controlIDs.push_back(V4L2_CID_EXPOSURE); + break; + default: + LOG(UVC, Error) + << "Unhandled write control"; + break; + } + } + } + + /* Perhaps setControls could accept an empty vector/list as success? */ + if (controlIDs.empty()) + return 0; + + controls = data->video_->getControls(controlIDs); + if (controls.empty()) + return -ENODATA; + + for (V4L2Control *ctrl : controls) { + switch (ctrl->id()) { + case V4L2_CID_BRIGHTNESS: { + Control bright = *request->controls().find(ManualBrightness); + /* RFC: + * If the iterator doesn't find the control ^ + * then it causes a segfault, so this is really nasty... + * and where just a map might be better - (ornot?) as it + * will create the object at that key. + * Now , that *shouldn't* happen (we should only look + * for controls that were given to us in this + * function ... but + */ + V4L2IntControl *vc = static_cast(ctrl); + bright.value().set(vc->value()); + + LOG(UVC, Debug) << "Setting Brightness: " << bright; + + break; + } + case V4L2_CID_ANALOGUE_GAIN: { + Control gain = *request->controls().find(ManualGain); + V4L2IntControl *vc = static_cast(ctrl); + gain.value().set(vc->value()); + break; + } + default: + LOG(UVC, Warning) << "Unhandled V4L2 Control ID"; + } + } + + return 0; +} + int PipelineHandlerUVC::queueRequest(Camera *camera, Request *request) { UVCCameraData *data = cameraData(camera); @@ -263,7 +378,11 @@ int PipelineHandlerUVC::queueRequest(Camera *camera, Request *request) return -ENOENT; } - int ret = data->video_->queueBuffer(buffer); + int ret = writeControls(camera, request); + if (ret < 0) + return ret; + + ret = data->video_->queueBuffer(buffer); if (ret < 0) return ret; @@ -317,6 +436,12 @@ void UVCCameraData::bufferReady(Buffer *buffer) Request *request = queuedRequests_.front(); pipe_->completeBuffer(camera_, request, buffer); + + int ret = pipe_->readControls(camera_, request); + if (ret < 0) + LOG(UVC, Error) + << "Failed to read controls. No way to pass error"; + pipe_->completeRequest(camera_, request); } diff --git a/src/libcamera/pipeline/vimc.cpp b/src/libcamera/pipeline/vimc.cpp index 0e4eede351d8..9ecce48c0b12 100644 --- a/src/libcamera/pipeline/vimc.cpp +++ b/src/libcamera/pipeline/vimc.cpp @@ -67,6 +67,9 @@ public: int start(Camera *camera) override; void stop(Camera *camera) override; + int readControls(Camera *camera, Request *request) override; + int writeControls(Camera *camera, Request *request) override; + int queueRequest(Camera *camera, Request *request) override; bool match(DeviceEnumerator *enumerator) override; @@ -210,6 +213,22 @@ void PipelineHandlerVimc::stop(Camera *camera) PipelineHandler::stop(camera); } +int PipelineHandlerVimc::readControls(Camera *camera, Request *request) +{ + LOG(VIMC, Error) + << "NOT IMPLEMENTED"; + + return -EINVAL; +} + +int PipelineHandlerVimc::writeControls(Camera *camera, Request *request) +{ + LOG(VIMC, Error) + << "NOT IMPLEMENTED"; + + return -EINVAL; +} + int PipelineHandlerVimc::queueRequest(Camera *camera, Request *request) { VimcCameraData *data = cameraData(camera); From patchwork Thu Jun 6 20:56:53 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 1376 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 183D460BB7 for ; Thu, 6 Jun 2019 22:56:59 +0200 (CEST) Received: from localhost.localdomain (cpc89242-aztw30-2-0-cust488.18-1.cable.virginm.net [86.31.129.233]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id AD0A333B; Thu, 6 Jun 2019 22:56:58 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1559854618; bh=58XtCoTFn85cxKj5vTpGrKDu0gsk3nDFhQGsBwCQNgQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Z323XbTxejOnqd7vy2dLBvZLjIQFBoyTK/VEZiwnl4uYQqNWP+L9WwRpwBmhb22OQ ReWsHnzHKUR7ZrLw1/ZhOMr1F+6Uwe4+LNZqyJ+um71IXFa2mVyN0DWoGEWJyGA2em I1BxNI9m5T//W+oIvG0vVq2MD0TU9aOI2Df30HFI= From: Kieran Bingham To: LibCamera Devel Date: Thu, 6 Jun 2019 21:56:53 +0100 Message-Id: <20190606205654.9311-5-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190606205654.9311-1-kieran.bingham@ideasonboard.com> References: <20190606205654.9311-1-kieran.bingham@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [RFC PATCH 4/5] QCam: Set a read control on each request to get Gain value X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 06 Jun 2019 20:56:59 -0000 --- src/qcam/main_window.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp index c0ab865b46ff..8dbe597e7aa2 100644 --- a/src/qcam/main_window.cpp +++ b/src/qcam/main_window.cpp @@ -210,6 +210,10 @@ int MainWindow::startCapture() } for (Request *request : requests) { + + /* Add a read request for ManualGain */ + request->controls().emplace(ManualBrightness); + ret = camera_->queueRequest(request); if (ret < 0) { std::cerr << "Can't queue request" << std::endl; @@ -266,12 +270,18 @@ void MainWindow::requestComplete(Request *request, display(buffer); + /* Parse completed controls */ + for (Control c : request->controls()) + std::cout << c << std::endl; + + /* Create a new request */ request = camera_->createRequest(); if (!request) { std::cerr << "Can't create request" << std::endl; return; } + request->setBuffers(buffers); camera_->queueRequest(request); } From patchwork Thu Jun 6 20:56:54 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 1377 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 65AC560BB7 for ; Thu, 6 Jun 2019 22:56:59 +0200 (CEST) Received: from localhost.localdomain (cpc89242-aztw30-2-0-cust488.18-1.cable.virginm.net [86.31.129.233]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 04126549; Thu, 6 Jun 2019 22:56:58 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1559854619; bh=cqMsU/6hFBve0kvWuImzIiMDPL6s6Fy7ubS3FYzOpTM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=qjf4PR3pwEh219UGOVRYA7CsDAG4qD+Q7CWHf/VrUj9xDYdUA50bdpqqimf+b5Yhv fwPT35TM5jpNf7tt5ZIxQvhgolTD4lx9kxYbN6QxtqjyL81Kk3piTJdxmtA6W7RTLG r1eyGsqthF2KLCDsr8ChgguGftm9wSYkuV1p34Ig= From: Kieran Bingham To: LibCamera Devel Date: Thu, 6 Jun 2019 21:56:54 +0100 Message-Id: <20190606205654.9311-6-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190606205654.9311-1-kieran.bingham@ideasonboard.com> References: <20190606205654.9311-1-kieran.bingham@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [RFC PATCH 5/5] [PoC] QCam: Control demo: A SineWave Brightness X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 06 Jun 2019 20:56:59 -0000 As an example of how to add a control to each request, set the Brightness value with a changing value against the buffer time stamps. The Brightness will go up and down following a sine wave to visualise the control effect on the video stream. Signed-off-by: Kieran Bingham --- src/qcam/main_window.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp index 8dbe597e7aa2..d3fedce20a9c 100644 --- a/src/qcam/main_window.cpp +++ b/src/qcam/main_window.cpp @@ -5,6 +5,7 @@ * main_window.cpp - qcam - Main application window */ +#include #include #include #include @@ -247,6 +248,13 @@ void MainWindow::stopCapture() config_.reset(); } +int SineBrightness(Buffer *buffer) +{ + unsigned int millisec = buffer->timestamp() / 1000000; + constexpr float rads = 2 * M_PI / 180; + return sin(0.036 * rads * millisec) * 100; +} + void MainWindow::requestComplete(Request *request, const std::map &buffers) { @@ -281,6 +289,12 @@ void MainWindow::requestComplete(Request *request, return; } + /* + * A demonstration control, which has a distinct visual effect. + * Scale the brightness up and down with a sine wave. + */ + /* How do we deal with max/min/defaults etc? */ + request->controls().emplace(ManualBrightness, SineBrightness(buffer)); request->setBuffers(buffers); camera_->queueRequest(request);