libcamera: controls: Add support for string controls

String controls are stored internally as an array of char, but the
ControlValue constructor, get() and set() functions operate on an
std::string for convenience. Array of strings are thus not supported.

Unlike for other control types, the ControlInfo range reports the
minimum and maximum allowed lengths of the string (the minimum will
usually be 0), not the minimum and maximum value of each element.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>
This commit is contained in:
Laurent Pinchart 2020-03-01 17:51:42 +02:00
parent e5a9e6e9cd
commit 4e3f835126
4 changed files with 87 additions and 16 deletions

View file

@ -26,6 +26,7 @@ enum ControlType {
ControlTypeInteger32, ControlTypeInteger32,
ControlTypeInteger64, ControlTypeInteger64,
ControlTypeFloat, ControlTypeFloat,
ControlTypeString,
}; };
namespace details { namespace details {
@ -64,6 +65,11 @@ struct control_type<float> {
static constexpr ControlType value = ControlTypeFloat; static constexpr ControlType value = ControlTypeFloat;
}; };
template<>
struct control_type<std::string> {
static constexpr ControlType value = ControlTypeString;
};
template<typename T, std::size_t N> template<typename T, std::size_t N>
struct control_type<Span<T, N>> : public control_type<std::remove_cv_t<T>> { struct control_type<Span<T, N>> : public control_type<std::remove_cv_t<T>> {
}; };
@ -76,7 +82,9 @@ public:
ControlValue(); ControlValue();
#ifndef __DOXYGEN__ #ifndef __DOXYGEN__
template<typename T, typename std::enable_if_t<!details::is_span<T>::value, std::nullptr_t> = nullptr> template<typename T, typename std::enable_if_t<!details::is_span<T>::value &&
!std::is_same<std::string, std::remove_cv_t<T>>::value,
std::nullptr_t> = nullptr>
ControlValue(const T &value) ControlValue(const T &value)
: type_(ControlTypeNone), numElements_(0) : type_(ControlTypeNone), numElements_(0)
{ {
@ -84,7 +92,9 @@ public:
&value, 1, sizeof(T)); &value, 1, sizeof(T));
} }
template<typename T, typename std::enable_if_t<details::is_span<T>::value, std::nullptr_t> = nullptr> template<typename T, typename std::enable_if_t<details::is_span<T>::value ||
std::is_same<std::string, std::remove_cv_t<T>>::value,
std::nullptr_t> = nullptr>
#else #else
template<typename T> template<typename T>
#endif #endif
@ -115,7 +125,9 @@ public:
} }
#ifndef __DOXYGEN__ #ifndef __DOXYGEN__
template<typename T, typename std::enable_if_t<!details::is_span<T>::value, std::nullptr_t> = nullptr> template<typename T, typename std::enable_if_t<!details::is_span<T>::value &&
!std::is_same<std::string, std::remove_cv_t<T>>::value,
std::nullptr_t> = nullptr>
T get() const T get() const
{ {
assert(type_ == details::control_type<std::remove_cv_t<T>>::value); assert(type_ == details::control_type<std::remove_cv_t<T>>::value);
@ -124,7 +136,9 @@ public:
return *reinterpret_cast<const T *>(data().data()); return *reinterpret_cast<const T *>(data().data());
} }
template<typename T, typename std::enable_if_t<details::is_span<T>::value, std::nullptr_t> = nullptr> template<typename T, typename std::enable_if_t<details::is_span<T>::value ||
std::is_same<std::string, std::remove_cv_t<T>>::value,
std::nullptr_t> = nullptr>
#else #else
template<typename T> template<typename T>
#endif #endif
@ -139,14 +153,18 @@ public:
} }
#ifndef __DOXYGEN__ #ifndef __DOXYGEN__
template<typename T, typename std::enable_if_t<!details::is_span<T>::value, std::nullptr_t> = nullptr> template<typename T, typename std::enable_if_t<!details::is_span<T>::value &&
!std::is_same<std::string, std::remove_cv_t<T>>::value,
std::nullptr_t> = nullptr>
void set(const T &value) void set(const T &value)
{ {
set(details::control_type<std::remove_cv_t<T>>::value, false, set(details::control_type<std::remove_cv_t<T>>::value, false,
reinterpret_cast<const void *>(&value), 1, sizeof(T)); reinterpret_cast<const void *>(&value), 1, sizeof(T));
} }
template<typename T, typename std::enable_if_t<details::is_span<T>::value, std::nullptr_t> = nullptr> template<typename T, typename std::enable_if_t<details::is_span<T>::value ||
std::is_same<std::string, std::remove_cv_t<T>>::value,
std::nullptr_t> = nullptr>
#else #else
template<typename T> template<typename T>
#endif #endif

View file

@ -314,6 +314,22 @@ ControlValue ControlSerializer::loadControlValue(ByteStreamBuffer &buffer,
return value; return value;
} }
template<>
ControlValue ControlSerializer::loadControlValue<std::string>(ByteStreamBuffer &buffer,
bool isArray,
unsigned int count)
{
ControlValue value;
const char *data = buffer.read<char>(count);
if (!data)
return value;
value.set(std::string{ data, count });
return value;
}
ControlValue ControlSerializer::loadControlValue(ControlType type, ControlValue ControlSerializer::loadControlValue(ControlType type,
ByteStreamBuffer &buffer, ByteStreamBuffer &buffer,
bool isArray, bool isArray,
@ -335,6 +351,9 @@ ControlValue ControlSerializer::loadControlValue(ControlType type,
case ControlTypeFloat: case ControlTypeFloat:
return loadControlValue<float>(buffer, isArray, count); return loadControlValue<float>(buffer, isArray, count);
case ControlTypeString:
return loadControlValue<std::string>(buffer, isArray, count);
case ControlTypeNone: case ControlTypeNone:
return ControlValue(); return ControlValue();
} }
@ -345,6 +364,9 @@ ControlValue ControlSerializer::loadControlValue(ControlType type,
ControlInfo ControlSerializer::loadControlInfo(ControlType type, ControlInfo ControlSerializer::loadControlInfo(ControlType type,
ByteStreamBuffer &b) ByteStreamBuffer &b)
{ {
if (type == ControlTypeString)
type = ControlTypeInteger32;
ControlValue min = loadControlValue(type, b); ControlValue min = loadControlValue(type, b);
ControlValue max = loadControlValue(type, b); ControlValue max = loadControlValue(type, b);

View file

@ -57,6 +57,7 @@ static constexpr size_t ControlValueSize[] = {
[ControlTypeInteger32] = sizeof(int32_t), [ControlTypeInteger32] = sizeof(int32_t),
[ControlTypeInteger64] = sizeof(int64_t), [ControlTypeInteger64] = sizeof(int64_t),
[ControlTypeFloat] = sizeof(float), [ControlTypeFloat] = sizeof(float),
[ControlTypeString] = sizeof(char),
}; };
} /* namespace */ } /* namespace */
@ -76,6 +77,8 @@ static constexpr size_t ControlValueSize[] = {
* The control stores a 64-bit integer value * The control stores a 64-bit integer value
* \var ControlTypeFloat * \var ControlTypeFloat
* The control stores a 32-bit floating point value * The control stores a 32-bit floating point value
* \var ControlTypeString
* The control stores a string value as an array of char
*/ */
/** /**
@ -164,7 +167,8 @@ ControlValue &ControlValue::operator=(const ControlValue &other)
* \brief Retrieve the number of elements stored in the ControlValue * \brief Retrieve the number of elements stored in the ControlValue
* *
* For instances storing an array, this function returns the number of elements * For instances storing an array, this function returns the number of elements
* in the array. Otherwise, it returns 1. * in the array. For instances storing a string, it returns the length of the
* string, not counting the terminating '\0'. Otherwise, it returns 1.
* *
* \return The number of elements stored in the ControlValue * \return The number of elements stored in the ControlValue
*/ */
@ -192,6 +196,11 @@ std::string ControlValue::toString() const
return "<ValueType Error>"; return "<ValueType Error>";
const uint8_t *data = ControlValue::data().data(); const uint8_t *data = ControlValue::data().data();
if (type_ == ControlTypeString)
return std::string(reinterpret_cast<const char *>(data),
numElements_);
std::string str(isArray_ ? "[ " : ""); std::string str(isArray_ ? "[ " : "");
for (unsigned int i = 0; i < numElements_; ++i) { for (unsigned int i = 0; i < numElements_; ++i) {
@ -222,6 +231,7 @@ std::string ControlValue::toString() const
break; break;
} }
case ControlTypeNone: case ControlTypeNone:
case ControlTypeString:
break; break;
} }
@ -439,12 +449,22 @@ ControlInfo::ControlInfo(const ControlValue &min,
/** /**
* \fn ControlInfo::min() * \fn ControlInfo::min()
* \brief Retrieve the minimum value of the control * \brief Retrieve the minimum value of the control
*
* For string controls, this is the minimum length of the string, not counting
* the terminating '\0'. For all other control types, this is the minimum value
* of each element.
*
* \return A ControlValue with the minimum value for the control * \return A ControlValue with the minimum value for the control
*/ */
/** /**
* \fn ControlInfo::max() * \fn ControlInfo::max()
* \brief Retrieve the maximum value of the control * \brief Retrieve the maximum value of the control
*
* For string controls, this is the maximum length of the string, not counting
* the terminating '\0'. For all other control types, this is the maximum value
* of each element.
*
* \return A ControlValue with the maximum value for the control * \return A ControlValue with the maximum value for the control
*/ */
@ -653,7 +673,16 @@ void ControlInfoMap::generateIdmap()
idmap_.clear(); idmap_.clear();
for (const auto &ctrl : *this) { for (const auto &ctrl : *this) {
if (ctrl.first->type() != ctrl.second.min().type()) { /*
* For string controls, min and max define the valid
* range for the string size, not for the individual
* values.
*/
ControlType rangeType = ctrl.first->type() == ControlTypeString
? ControlTypeInteger32 : ctrl.first->type();
const ControlInfo &info = ctrl.second;
if (info.min().type() != rangeType) {
LOG(Controls, Error) LOG(Controls, Error)
<< "Control " << utils::hex(ctrl.first->id()) << "Control " << utils::hex(ctrl.first->id())
<< " type and info type mismatch"; << " type and info type mismatch";

View file

@ -42,10 +42,11 @@ ${description}
name, ctrl = ctrl.popitem() name, ctrl = ctrl.popitem()
id_name = snake_case(name).upper() id_name = snake_case(name).upper()
if ctrl.get('size'): ctrl_type = ctrl['type']
ctrl_type = 'Span<const %s>' % ctrl['type'] if ctrl_type == 'string':
else: ctrl_type = 'std::string'
ctrl_type = ctrl['type'] elif ctrl.get('size'):
ctrl_type = 'Span<const %s>' % ctrl_type
info = { info = {
'name': name, 'name': name,
@ -97,10 +98,11 @@ def generate_h(controls):
ids.append('\t' + id_name + ' = ' + str(id_value) + ',') ids.append('\t' + id_name + ' = ' + str(id_value) + ',')
if ctrl.get('size'): ctrl_type = ctrl['type']
ctrl_type = 'Span<const %s>' % ctrl['type'] if ctrl_type == 'string':
else: ctrl_type = 'std::string'
ctrl_type = ctrl['type'] elif ctrl.get('size'):
ctrl_type = 'Span<const %s>' % ctrl_type
info = { info = {
'name': name, 'name': name,