diff --git a/device/libcamera/options.cc b/device/libcamera/options.cc index d31060a..dd9520b 100644 --- a/device/libcamera/options.cc +++ b/device/libcamera/options.cc @@ -73,6 +73,13 @@ static std::set ignored_controls = static auto control_type_values = control_enum_values("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 *libcamera_find_control_ids(unsigned control_id) { auto iter = libcamera_control_ids.find(control_id); @@ -82,17 +89,20 @@ static const std::map *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 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 libcamera_parse_float(const char *value) +{ + char *endptr = NULL; + auto res = strtof(value, &endptr); + return std::make_pair(res, endptr); +} + +static std::pair 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_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 -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 parsed; + std::vector 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 (current_value.empty()) - continue; - - parsed.push_back(fn(current_value.c_str())); + if (!*next) + break; // reached end of elements + if (*next != ',') + return false; // expected comma to split items + value = ++next; } - if (parsed.empty()) { + if (items.empty()) { return false; - } - - if (parsed.size() > 1) { - control_value.set >(parsed); + } else if (control_array) { + control_value.set >(items); } else { - control_value.set(parsed[0]); + control_value.set(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(atoi(value)); break; case libcamera::ControlTypeByte: - libcamera_parse_control_value(control_value, value, + libcamera_parse_control_value(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(control_value, value, + libcamera_parse_control_value(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(control_value, value, + libcamera_parse_control_value(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(control_value, value, atof); + libcamera_parse_control_value(control_value, control_array, value, libcamera_parse_float); break; case libcamera::ControlTypeRectangle: libcamera_parse_control_value( - control_value, value, libcamera_parse_rectangle); + control_value, control_array, value, libcamera_parse_rectangle); break; case libcamera::ControlTypeSize: libcamera_parse_control_value( - 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( - control_value, value, libcamera_parse_point); + control_value, control_array, value, libcamera_parse_point); break; #endif