libcamera: parse arrays for options (#180)

This does that by trying to parse value,
and then advancing pointer. It checks if the
string ends or is split by comma.

With collaboration from @arekm. Take a look at:

- https://github.com/ayufan/camera-streamer/pull/170
- https://github.com/ayufan/camera-streamer/pull/172
- https://github.com/ayufan/camera-streamer/pull/176
This commit is contained in:
Kamil Trzciński 2025-07-02 21:43:47 +02:00 committed by GitHub
parent 5e689449d4
commit 9379ffde78
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -73,6 +73,13 @@ static std::set<const libcamera::ControlId *> ignored_controls =
static auto control_type_values = control_enum_values<libcamera::ControlType>("ControlType");
#if LIBCAMERA_VERSION_MAJOR == 0 && LIBCAMERA_VERSION_MINOR < 4
// Not supported before 0.4.0
#define CONTROL_ID_IS_ARRAY(ctrl) (false)
#else
#define CONTROL_ID_IS_ARRAY(ctrl) (ctrl)->isArray()
#endif
static const std::map<unsigned, std::string> *libcamera_find_control_ids(unsigned control_id)
{
auto iter = libcamera_control_ids.find(control_id);
@ -82,17 +89,20 @@ static const std::map<unsigned, std::string> *libcamera_find_control_ids(unsigne
return &iter->second.enum_values;
}
static long long libcamera_find_control_id_named_value(unsigned control_id, const char *name)
static std::pair<long long, const char*> libcamera_find_control_id_named_value(unsigned control_id, const char *value)
{
auto named_values = libcamera_find_control_ids(control_id);
if (named_values) {
for (const auto & named_value : *named_values) {
if (device_option_is_equal(named_value.second.c_str(), name))
return named_value.first;
if (device_option_is_equal(named_value.second.c_str(), value)) {
return std::make_pair(named_value.first, value + named_value.second.length());
}
}
}
return strtoll(name, NULL, 10);
char *endptr = NULL;
auto res = strtoll(value, &endptr, 10);
return std::make_pair(res, endptr);
}
static libcamera::ControlInfoMap::Map libcamera_control_list(device_t *dev)
@ -140,9 +150,10 @@ void libcamera_device_dump_options(device_t *dev, FILE *stream)
auto control_key = libcamera_device_option_normalize(control_id->name());
auto control_info = control.second;
fprintf(stream, "- available option: %s (%08x, type=%s): %s\n",
fprintf(stream, "- available option: %s (%08x, type=%s%s): %s\n",
control_id->name().c_str(), control_id->id(),
control_type_values[control_id->type()].c_str(),
CONTROL_ID_IS_ARRAY(control_id) ? " Array" : "",
control_info.toString().c_str());
auto named_values = libcamera_find_control_ids(control_id->id());
@ -321,46 +332,55 @@ int libcamera_device_dump_options2(device_t *dev, device_option_fn fn, void *opa
return n;
}
static libcamera::Rectangle libcamera_parse_rectangle(const char *value)
static std::pair<float, const char*> libcamera_parse_float(const char *value)
{
char *endptr = NULL;
auto res = strtof(value, &endptr);
return std::make_pair(res, endptr);
}
static std::pair<libcamera::Rectangle, const char*> libcamera_parse_rectangle(const char *value)
{
static const char *RECTANGLE_PATTERNS[] =
{
"(%d,%d)/%ux%u",
"%d,%d,%u,%u",
"(%d,%d)/%ux%u%n",
"%d,%d,%u,%u%n",
NULL
};
for (int i = 0; RECTANGLE_PATTERNS[i]; i++) {
libcamera::Rectangle rectangle;
int read = 0;
if (4 == sscanf(value, RECTANGLE_PATTERNS[i],
&rectangle.x, &rectangle.y,
&rectangle.width, &rectangle.height)) {
return rectangle;
&rectangle.width, &rectangle.height, &read)) {
return std::make_pair(rectangle, value + read);
}
}
return libcamera::Rectangle();
return std::make_pair(libcamera::Rectangle(), (const char*)NULL);
}
static libcamera::Size libcamera_parse_size(const char *value)
static std::pair<libcamera::Size, const char*> libcamera_parse_size(const char *value)
{
static const char *SIZE_PATTERNS[] =
{
"%ux%u",
"%u,%u",
"%ux%u%n",
"%u,%u%n",
NULL
};
for (int i = 0; SIZE_PATTERNS[i]; i++) {
libcamera::Size size;
int read = 0;
if (2 == sscanf(value, SIZE_PATTERNS[i], &size.width, &size.height)) {
return size;
if (2 == sscanf(value, SIZE_PATTERNS[i], &size.width, &size.height, &read)) {
return std::make_pair(size, value + read);
}
}
return libcamera::Size();
return std::make_pair(libcamera::Size(), (const char*)NULL);
}
#if LIBCAMERA_VERSION_MAJOR == 0 && LIBCAMERA_VERSION_MINOR > 3 && LIBCAMERA_VERSION_PATCH >= 2 // Support for older libcamera versions
@ -368,54 +388,49 @@ static libcamera::Point libcamera_parse_point(const char *value)
{
static const char *POINT_PATTERNS[] =
{
"(%d,%d)",
"%d,%d",
"(%d,%d)%n",
"%d,%d%n",
NULL
};
for (int i = 0; POINT_PATTERNS[i]; i++) {
libcamera::Point point;
int read = 0;
if (2 == sscanf(value, POINT_PATTERNS[i],
&point.x, &point.y)) {
return point;
&point.x, &point.y, &read)) {
return std::make_pair(point, value + read);
}
}
return libcamera::Point();
return std::make_pair(libcamera::Point(), NULL);
}
#endif
template<typename T, typename F>
static bool libcamera_parse_control_value(libcamera::ControlValue &control_value, const char *value, const F &fn)
static bool libcamera_parse_control_value(libcamera::ControlValue &control_value, bool control_array, const char *value, const F& fn)
{
std::vector<T> parsed;
std::vector<T> items;
while (value && *value) {
std::string current_value;
auto [item, next] = fn(value);
if (!next)
return false; // failed to parse
items.push_back(item);
if (const char *next = strchr(value, ',')) {
current_value.assign(value, next);
value = &next[1];
} else {
current_value.assign(value);
value = NULL;
if (!*next)
break; // reached end of elements
if (*next != ',')
return false; // expected comma to split items
value = ++next;
}
if (current_value.empty())
continue;
parsed.push_back(fn(current_value.c_str()));
}
if (parsed.empty()) {
if (items.empty()) {
return false;
}
if (parsed.size() > 1) {
control_value.set<libcamera::Span<T> >(parsed);
} else if (control_array) {
control_value.set<libcamera::Span<T> >(items);
} else {
control_value.set<T>(parsed[0]);
control_value.set<T>(items[0]);
}
return true;
@ -433,42 +448,46 @@ int libcamera_device_set_option(device_t *dev, const char *keyp, const char *val
continue;
libcamera::ControlValue control_value;
bool control_array = CONTROL_ID_IS_ARRAY(control_id);
switch (control_id->type()) {
case libcamera::ControlTypeNone:
break;
case libcamera::ControlTypeBool:
if (control_array) {
throw std::runtime_error("Array ControlType for boolean is not supported");
}
control_value.set<bool>(atoi(value));
break;
case libcamera::ControlTypeByte:
libcamera_parse_control_value<unsigned char>(control_value, value,
libcamera_parse_control_value<unsigned char>(control_value, control_array, value,
[control_id](const char *value) { return libcamera_find_control_id_named_value(control_id->id(), value); });
break;
case libcamera::ControlTypeInteger32:
libcamera_parse_control_value<int32_t>(control_value, value,
libcamera_parse_control_value<int32_t>(control_value, control_array, value,
[control_id](const char *value) { return libcamera_find_control_id_named_value(control_id->id(), value); });
break;
case libcamera::ControlTypeInteger64:
libcamera_parse_control_value<int64_t>(control_value, value,
libcamera_parse_control_value<int64_t>(control_value, control_array, value,
[control_id](const char *value) { return libcamera_find_control_id_named_value(control_id->id(), value); });
break;
case libcamera::ControlTypeFloat:
libcamera_parse_control_value<float>(control_value, value, atof);
libcamera_parse_control_value<float>(control_value, control_array, value, libcamera_parse_float);
break;
case libcamera::ControlTypeRectangle:
libcamera_parse_control_value<libcamera::Rectangle>(
control_value, value, libcamera_parse_rectangle);
control_value, control_array, value, libcamera_parse_rectangle);
break;
case libcamera::ControlTypeSize:
libcamera_parse_control_value<libcamera::Size>(
control_value, value, libcamera_parse_size);
control_value, control_array, value, libcamera_parse_size);
break;
case libcamera::ControlTypeString:
@ -477,7 +496,7 @@ int libcamera_device_set_option(device_t *dev, const char *keyp, const char *val
#if LIBCAMERA_VERSION_MAJOR == 0 && LIBCAMERA_VERSION_MINOR > 3 && LIBCAMERA_VERSION_PATCH >= 2 // Support for older libcamera versions
case libcamera::ControlTypePoint:
libcamera_parse_control_value<libcamera::Point>(
control_value, value, libcamera_parse_point);
control_value, control_array, value, libcamera_parse_point);
break;
#endif