#ifdef USE_LIBCAMERA #include "libcamera.hh" #include "third_party/magic_enum/include/magic_enum.hpp" static std::string libcamera_device_option_normalize(std::string key) { key.resize(device_option_normalize_name(key.data(), key.data())); return key; } template static std::map control_enum_values(const char *prefix) { std::map ret; for (auto e : magic_enum::enum_entries()) { auto value = std::string(e.second); if (prefix && value.find(prefix) == 0) { value = value.substr(strlen(prefix)); } ret[e.first] = value; } return ret; } struct libcamera_control_id_t { const libcamera::ControlId *control_id; std::map enum_values; }; #define LIBCAMERA_CONTROL_RAW(Name, Strip) \ { Name.id(), { .control_id = &Name, .enum_values = control_enum_values(Strip), } } #define LIBCAMERA_CONTROL(Name, Strip) \ LIBCAMERA_CONTROL_RAW(libcamera::controls::Name, Strip ? Strip : #Name) #define LIBCAMERA_DRAFT_CONTROL(Name) \ LIBCAMERA_CONTROL_RAW(libcamera::controls::draft::Name, #Name) static std::map libcamera_control_ids = { LIBCAMERA_CONTROL(AeMeteringMode, "Metering"), LIBCAMERA_CONTROL(AeConstraintMode, "Constraint"), LIBCAMERA_CONTROL(AeExposureMode, "Exposure"), LIBCAMERA_CONTROL(AwbMode, "Awb"), LIBCAMERA_CONTROL(AfMode, "AfMode"), LIBCAMERA_CONTROL(AfRange, "AfRange"), LIBCAMERA_CONTROL(AfSpeed, "AfSpeed"), LIBCAMERA_CONTROL(AfTrigger, "AfTrigger"), LIBCAMERA_CONTROL(AfState, "AfState"), LIBCAMERA_DRAFT_CONTROL(AePrecaptureTrigger), LIBCAMERA_DRAFT_CONTROL(NoiseReductionMode), LIBCAMERA_DRAFT_CONTROL(ColorCorrectionAberrationMode), LIBCAMERA_DRAFT_CONTROL(AeState), LIBCAMERA_DRAFT_CONTROL(AwbState), LIBCAMERA_DRAFT_CONTROL(LensShadingMapMode), #if LIBCAMERA_VERSION_MAJOR == 0 && LIBCAMERA_VERSION_MINOR < 1 // Support RasPI bullseye LIBCAMERA_DRAFT_CONTROL(SceneFlicker), #endif LIBCAMERA_DRAFT_CONTROL(TestPatternMode) }; static std::set ignored_controls = { &libcamera::controls::FrameDurationLimits }; static auto control_type_values = control_enum_values("ControlType"); static const std::map *libcamera_find_control_ids(unsigned control_id) { auto iter = libcamera_control_ids.find(control_id); if (iter == libcamera_control_ids.end()) return NULL; return &iter->second.enum_values; } static long long libcamera_find_control_id_named_value(unsigned control_id, const char *name) { 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; } } return strtoll(name, NULL, 10); } static libcamera::ControlInfoMap::Map libcamera_control_list(device_t *dev) { libcamera::ControlInfoMap::Map controls_map; for (auto const &control : dev->libcamera->camera->controls()) { controls_map[control.first] = control.second; } return controls_map; } void libcamera_device_dump_options(device_t *dev, FILE *stream) { auto &properties = dev->libcamera->camera->properties(); auto idMap = properties.idMap(); fprintf(stream, "%s Properties:\n", dev->name); for (auto const &control : properties) { if (!control.first) continue; auto control_id = control.first; auto control_value = control.second; std::string control_id_name = ""; if (auto control_id_info = idMap ? idMap->at(control_id) : NULL) { control_id_name = control_id_info->name(); } fprintf(stream, "- property: %s (%08x, type=%s) = %s\n", control_id_name.c_str(), control_id, control_type_values[control_value.type()].c_str(), control_value.toString().c_str()); } fprintf(stream, "\n"); fprintf(stream, "%s Options:\n", dev->name); for (auto const &control : libcamera_control_list(dev)) { if (!control.first) continue; auto control_id = control.first; 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", control_id->name().c_str(), control_id->id(), control_type_values[control_id->type()].c_str(), control_info.toString().c_str()); auto named_values = libcamera_find_control_ids(control_id->id()); if (named_values != NULL) { for (const auto & named_value : *named_values) { fprintf(stream, "\t\t%d: %s\n", named_value.first, named_value.second.c_str()); } } else { for (size_t i = 0; i < control_info.values().size(); i++) { fprintf(stream, "\t\t%s\n", control_info.values()[i].toString().c_str()); } } } fprintf(stream, "\n"); } static int libcamera_device_dump_control_option(device_option_fn fn, void *opaque, const libcamera::ControlId &control_id, const libcamera::ControlInfo *control_info, const libcamera::ControlValue *control_value, bool read_only) { if (ignored_controls.find(&control_id) != ignored_controls.end()) { return 0; } device_option_t opt = { .control_id = control_id.id() }; opt.flags.read_only = read_only; strcpy(opt.name, control_id.name().c_str()); if (control_info) { strcpy(opt.description, control_info->toString().c_str()); } if (control_value) { if (!control_value->isNone()) { strcpy(opt.value, control_value->toString().c_str()); } else { control_value = NULL; } } switch (control_id.type()) { case libcamera::ControlTypeNone: break; case libcamera::ControlTypeBool: opt.type = device_option_type_bool; break; case libcamera::ControlTypeByte: opt.type = device_option_type_u8; break; case libcamera::ControlTypeInteger32: opt.type = device_option_type_integer; break; case libcamera::ControlTypeInteger64: opt.type = device_option_type_integer64; break; case libcamera::ControlTypeFloat: opt.type = device_option_type_float; break; case libcamera::ControlTypeRectangle: opt.type = device_option_type_float; opt.elems = 4; break; case libcamera::ControlTypeSize: opt.type = device_option_type_float; opt.elems = 2; break; case libcamera::ControlTypeString: opt.type = device_option_type_string; break; } auto named_values = libcamera_find_control_ids(control_id.id()); if (named_values != NULL) { for (const auto & named_value : *named_values) { if (opt.menu_items >= MAX_DEVICE_OPTION_MENU) { opt.flags.invalid = true; break; } device_option_menu_t *opt_menu = &opt.menu[opt.menu_items++]; opt_menu->id = named_value.first; strcpy(opt_menu->name, named_value.second.c_str()); if (control_value && atoi(control_value->toString().c_str()) == opt_menu->id) { strcpy(opt.value, opt_menu->name); } } } else if (control_info) { for (size_t i = 0; i < control_info->values().size(); i++) { if (opt.menu_items >= MAX_DEVICE_OPTION_MENU) { opt.flags.invalid = true; break; } device_option_menu_t *opt_menu = &opt.menu[opt.menu_items++]; opt_menu->id = atoi(control_info->values()[i].toString().c_str()); strcpy(opt_menu->name, control_info->values()[i].toString().c_str()); if (control_value && atoi(control_value->toString().c_str()) == opt_menu->id) { strcpy(opt.value, opt_menu->name); } } } else if (control_value && control_value->numElements() > 0) { opt.elems = control_value->numElements(); } if (opt.type) { int ret = fn(&opt, opaque); if (ret < 0) return ret; } return 0; } int libcamera_device_dump_options2(device_t *dev, device_option_fn fn, void *opaque) { auto &properties = dev->libcamera->camera->properties(); auto idMap = properties.idMap(); int n = 0; for (auto const &control : properties) { if (!control.first) continue; auto control_id = control.first; auto control_value = control.second; std::string control_id_name = ""; if (auto control_id_info = idMap ? idMap->at(control_id) : NULL) { int ret = libcamera_device_dump_control_option(fn, opaque, *control_id_info, NULL, &control_value, true); if (ret < 0) return ret; n++; } } for (auto const &control : libcamera_control_list(dev)) { if (!control.first) continue; auto control_id = control.first; auto control_info = control.second; auto control_value = dev->libcamera->applied_controls.contains(control_id->id()) ? &dev->libcamera->applied_controls.get(control_id->id()) : nullptr; int ret = libcamera_device_dump_control_option(fn, opaque, *control_id, &control_info, control_value, false); if (ret < 0) return ret; n++; } return n; } static libcamera::Rectangle libcamera_parse_rectangle(const char *value) { static const char *RECTANGLE_PATTERNS[] = { "(%d,%d)/%ux%u", "%d,%d,%u,%u", NULL }; for (int i = 0; RECTANGLE_PATTERNS[i]; i++) { libcamera::Rectangle rectangle; if (4 == sscanf(value, RECTANGLE_PATTERNS[i], &rectangle.x, &rectangle.y, &rectangle.width, &rectangle.height)) { return rectangle; } } return libcamera::Rectangle(); } static libcamera::Size libcamera_parse_size(const char *value) { static const char *SIZE_PATTERNS[] = { "%ux%u", "%u,%u", NULL }; for (int i = 0; SIZE_PATTERNS[i]; i++) { libcamera::Size size; if (2 == sscanf(value, SIZE_PATTERNS[i], &size.width, &size.height)) { return size; } } return libcamera::Size(); } template static bool libcamera_parse_control_value(libcamera::ControlValue &control_value, const char *value, const F &fn) { std::vector parsed; while (value && *value) { std::string current_value; 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 (parsed.empty()) { return false; } if (parsed.size() > 1) { control_value.set >(parsed); } else { control_value.set(parsed[0]); } return true; } int libcamera_device_set_option(device_t *dev, const char *keyp, const char *value) { for (auto const &control : libcamera_control_list(dev)) { if (!control.first) continue; auto control_id = control.first; auto control_key = control_id->name(); if (!device_option_is_equal(keyp, control_key.c_str())) continue; libcamera::ControlValue control_value; switch (control_id->type()) { case libcamera::ControlTypeNone: break; case libcamera::ControlTypeBool: control_value.set(atoi(value)); break; case libcamera::ControlTypeByte: libcamera_parse_control_value(control_value, 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, [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, [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); break; case libcamera::ControlTypeRectangle: libcamera_parse_control_value( control_value, value, libcamera_parse_rectangle); break; case libcamera::ControlTypeSize: libcamera_parse_control_value( control_value, value, libcamera_parse_size); break; case libcamera::ControlTypeString: break; } if (control_value.isNone()) { LOG_ERROR(dev, "The `%s` type `%d` is not supported.", control_key.c_str(), control_id->type()); } LOG_INFO(dev, "Configuring option '%s' (%08x, type=%d) = %s", control_key.c_str(), control_id->id(), control_id->type(), control_value.toString().c_str()); dev->libcamera->controls.set(control_id->id(), control_value); return 1; } return 0; error: return -1; } #endif // USE_LIBCAMERA