[libcamera-devel] [RFC PATCH v2 3/9] libcamera: controls: Introduce Control structures

Laurent Pinchart laurent.pinchart at ideasonboard.com
Sun Jun 23 23:52:40 CEST 2019


Hi Kieran,

Thank you for the patch.

On Fri, Jun 21, 2019 at 05:13:55PM +0100, Kieran Bingham wrote:
> ControlIdentifiers declare the types of a control, and map their names,
> and the ControlInfo class allows runtime state to be represented.
> 
> Signed-off-by: Kieran Bingham <kieran.bingham at ideasonboard.com>
> ---
>  include/libcamera/controls.h  |  58 ++++++++++++
>  include/libcamera/meson.build |   1 +
>  src/libcamera/controls.cpp    | 160 ++++++++++++++++++++++++++++++++++
>  src/libcamera/meson.build     |   1 +
>  4 files changed, 220 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..95198d41c4cf
> --- /dev/null
> +++ b/include/libcamera/controls.h
> @@ -0,0 +1,58 @@
> +/* 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 <libcamera/value.h>
> +
> +namespace libcamera {
> +
> +enum ControlId : uint32_t {
> +	/* IPA Controls */
> +	IpaAwbEnable,
> +
> +	/* Manual Controls */
> +	ManualBrightness,
> +	ManualExposure,
> +	ManualGain,

There will be lots of nitpicking about the names, we'll revisit this
later. For the time being I would still remove the IPA prefix and avoid
mentioning IPA at all, as I think it should remain an internal concept.

> +};
> +
> +struct ControlIdentifier {
> +	ControlId id;
> +	const char *name;
> +	ValueType type;
> +};
> +
> +class ControlInfo
> +{
> +public:
> +	ControlInfo(ControlId id, Value min = 0, Value max = 0);

Should you define the constructor as explicit as it's callable with a
single argument ? Or does the compiler refuse that as there's more than
one argument, even if all but the first have default values ?

> +
> +	ControlId id() const { return ident_->id; }
> +	const char *name() const { return ident_->name; }
> +	ValueType type() const { return ident_->type; }
> +
> +	const Value &min() { return min_; }
> +	const Value &max() { return min_; }
> +
> +	std::string toString() const;
> +
> +	bool operator==(const ControlInfo &rhs) const { return id() == rhs.id(); }
> +	bool operator==(const ControlId rhs) const { return id() == rhs; }

I wonder if these methods should be defined as non-members, as a bool
operator==(const ControlId lhs, const ControlInfo &rhs) should also be
defined for symmetry. Or maybe we should drop the second method, and
always write info.id() == id explicitly ? It's not much longer, and
should be more explicit.

You should also define operator!= as !(lhs == rhs).

> +
> +private:
> +	struct ControlIdentifier const *ident_;
> +	Value min_;
> +	Value max_;
> +
> +};
> +
> +std::ostream &operator<<(std::ostream &stream, const ControlInfo &value);

Don't you need to #include <ostream> ?

> +
> +} /* namespace libcamera */
> +
> +#endif /* __LIBCAMERA_CONTROLS_H__ */
> diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build
> index eb2211ae1fc3..06e3feebd23d 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..b1be46ddb55e
> --- /dev/null
> +++ b/src/libcamera/controls.cpp
> @@ -0,0 +1,160 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2019, Google Inc.
> + *
> + * controls.cpp - Control handling
> + */
> +
> +#include <limits.h>
> +#include <sstream>
> +#include <string>
> +
> +#include <libcamera/controls.h>

Please include this header first, for the same reason as in patch 1/9.

> +
> +#include "log.h"
> +#include "utils.h"
> +
> +/**
> + * \file controls.h
> + * \brief Describes control framework and controls supported by a camera
> + */
> +
> +namespace libcamera {
> +
> +LOG_DEFINE_CATEGORY(Controls)
> +
> +/**
> + * \enum ControlId
> + * 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
> + */

This is an area where I want libcamera to have amazing documentation.
Not only will each control need to be documented in details, but I also
want interactions between controls to be documented. In the meantime,
could you already try to format the documentation block appropriately ?
One block per control would be ideal.

> +
> +/**
> + * \struct ControlIdentifier
> + * Defines a Control with a unique ID, a name, and a type.

\brief ?

I think a more elaborate description would be useful, as so far I'm not
sure why you have split control information in two parts (although I
suspect the reason, but an explicit description would be better :-)).

> + * \var ControlIdentifier::id

Is the ControlIdentifier:: prefix needed ?

> + * The unique ID for a control
> + * \var ControlIdentifier::name
> + * The string representation of the control
> + * \var ControlIdentifier::type
> + * The ValueType required to represent the control value
> + */
> +
> +/*
> + * Two sets of tables are generated for the static control definitions.
> + *

s/.\n \*\n \* An/:\r * an/

Is that readable ? :-)

> + * An enum to declare the ID (in controls.h), and a type to establish its
> + * representation.

The enum is actually not a table, it doesn't generate data as such.

> + *
> + * Todo: Automate the generation of both tables from a single input table.

s/Todo: /\\todo /

Would it be possible to generate them both from a documentation file ?

> + * Todo: Consider if this should be a static std::map created at init instead.

Any reason why it shouldn't be ? :-) Lookup would be more efficient.

> + */
> +static const struct ControlIdentifier ControlTypes[] = {
> +#define CONTROL_TYPE(_id, _type) { _id, #_id, _type }
> +
> +	CONTROL_TYPE(IpaAwbEnable,		ValueBool),
> +	CONTROL_TYPE(ManualBrightness,		ValueInteger),
> +	CONTROL_TYPE(ManualExposure,		ValueInteger),
> +	CONTROL_TYPE(ManualGain,		ValueInteger),
> +
> +#undef CONTROL_TYPE
> +};
> +
> +static struct ControlIdentifier const *FindControlType(ControlId id)

This is a function and should thus start with a lowercase letter.

> +{
> +	struct ControlIdentifier const *ident;
> +
> +	for (ident = ControlTypes;
> +	     ident != &ControlTypes[ARRAY_SIZE(ControlTypes)];
> +	     ++ident) {
> +		if (ident->id == id)
> +			return ident;
> +	}
> +
> +	LOG(Controls, Fatal) << "Failed to find a ControlType.";
> +
> +	/* Unreachable. */
> +	return nullptr;
> +}
> +
> +/**
> + * \class ControlInfo
> + * \brief Describes the information and capabilities of a Control

s/Describes/Describe/

Again a bit more documentation would help.

> + */
> +
> +/**
> + * \brief Construct a ControlInfo with minimum and maximum range parameters.
> + */
> +ControlInfo::ControlInfo(ControlId id, Value min, Value max)
> +	: ident_(FindControlType(id)), min_(min), max_(max)
> +{
> +}
> +
> +/**
> + * \fn ControlInfo::id()
> + * \brief Return the ID of the control information descriptor

s/Return/Retrieve/

This is a bit confusing as nothing documents what a "control information
descriptor" is. I think the documentation will need to be revisited.

> + * \return the ControlId

The control ID

> + */
> +
> +/**
> + * \fn ControlInfo::name()
> + * \brief Return the string name of the control information descriptor

s/Return/Retrieve/

and below too.

> + * \return A string name for the Control
> + */
> +
> +/**
> + * \fn ControlInfo::type()
> + * \brief Return the ValueType of the control information descriptor
> + * \return the ControlId

The control type

> + */
> +
> +/**
> + * \fn ControlInfo::min()
> + * \brief Reports the minimum value of the control
> + * \return a Value with the minimum setting for the control

s/a/A/

How does that apply to non-integer controls ?

> + */
> +
> +/**
> + * \fn ControlInfo::max()
> + * \brief Reports the maximum value of the control
> + * \return a Value with the maximum setting for the control
> + */
> +
> +/**
> + * \brief Provide a string representation of the ControlInfo
> + */
> +std::string ControlInfo::toString() const
> +{
> +	std::stringstream ss;
> +
> +	ss << "Control: " << name()
> +	   << " : Min(" << min_ << ") Max(" << max_ << ")";

I still need to look at how you use this, but in general it's best to
shorten the representations as much as possible. I may remove the
"Control:" prefix, maybe with

	ss << name() << "[" << min_ << "," << max_ << "]";

This would print

	ManualExposure[1,1000]

We could replace the comma with ..

	ManualExposure[1..1000]

> +
> +	return ss.str();
> +}
> +
> +/**
> + * \fn ControlInfo::operator==(const ControlInfo &rhs) const
> + * \brief Establish equivalence of ControlInfo. Only the IDs are considered.
> + */
> +
> +/**
> + * \fn ControlInfo::operator==(const ControlId rhs) const
> + * \brief Establish equivalence of ControlInfo, against a ControlID.
> + */
> +
> +/**
> + * \brief Provide a string stream representation of the ControlInfo \a info to
> + * the \a stream.
> + */
> +std::ostream &operator<<(std::ostream &stream, const ControlInfo &info)
> +{
> +	return stream << info.toString();
> +}
> +
> +} /* namespace libcamera */
> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
> index 8e68373118df..e2c07d79bfb5 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',

-- 
Regards,

Laurent Pinchart


More information about the libcamera-devel mailing list