From 3a0ffe21e68996cee31c42201831e4895b66ca45 Mon Sep 17 00:00:00 2001 From: David Turner Date: Mon, 9 Jun 2025 13:22:35 +0100 Subject: [PATCH] libcamera: Default to auto-focus & auto-exposure libcamera says that cameras should default to manual focus mode. This means that unless pipewire clients specifically change this control, users with an autofocus-capable camera are left with an out-of-focus image. This patch sets the autofocus mode to continuous and enables auto-exposure (as the default for this is unspecified). Testing with an imx708 on Raspberry Pi OS on a Raspberry Pi 4, before this patch the image was generally out of focus in Firefox/webrtc, after this patch autofocus works correctly. --- spa/plugins/libcamera/libcamera-source.cpp | 1 + spa/plugins/libcamera/libcamera-utils.cpp | 29 +++++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/spa/plugins/libcamera/libcamera-source.cpp b/spa/plugins/libcamera/libcamera-source.cpp index 0dffbc92b..5523f7c8b 100644 --- a/spa/plugins/libcamera/libcamera-source.cpp +++ b/spa/plugins/libcamera/libcamera-source.cpp @@ -163,6 +163,7 @@ struct impl { struct spa_source source = {}; ControlList ctrls; + ControlList initial_controls; bool active = false; bool acquired = false; diff --git a/spa/plugins/libcamera/libcamera-utils.cpp b/spa/plugins/libcamera/libcamera-utils.cpp index d116a4094..97634a5d8 100644 --- a/spa/plugins/libcamera/libcamera-utils.cpp +++ b/spa/plugins/libcamera/libcamera-utils.cpp @@ -16,6 +16,30 @@ #include #include +static void setup_initial_controls(const ControlInfoMap& ctrl_infos, ControlList& ctrls) +{ + /* Libcamera recommends cameras default to manual focus mode, but we don't + * expose any focus controls. So, specifically enable autofocus on + * cameras which support it. */ + auto af_it = ctrl_infos.find(libcamera::controls::AF_MODE); + if (af_it != ctrl_infos.end()) { + const ControlInfo &ctrl_info = af_it->second; + auto is_af_continuous = [](const ControlValue &value) { + return value.get() == libcamera::controls::AfModeContinuous; + }; + if (std::any_of(ctrl_info.values().begin(), + ctrl_info.values().end(), is_af_continuous)) { + ctrls.set(libcamera::controls::AF_MODE, + libcamera::controls::AfModeContinuous); + } + } + + auto ae_it = ctrl_infos.find(libcamera::controls::AE_ENABLE); + if (ae_it != ctrl_infos.end()) { + ctrls.set(libcamera::controls::AE_ENABLE, true); + } +} + int spa_libcamera_open(struct impl *impl) { if (impl->acquired) @@ -26,6 +50,9 @@ int spa_libcamera_open(struct impl *impl) impl->allocator = new FrameBufferAllocator(impl->camera); + const ControlInfoMap &controls = impl->camera->controls(); + setup_initial_controls(controls, impl->initial_controls); + impl->acquired = true; return 0; } @@ -990,7 +1017,7 @@ static int spa_libcamera_stream_on(struct impl *impl) impl->camera->requestCompleted.connect(impl, &impl::requestComplete); spa_log_info(impl->log, "starting camera %s", impl->device_id.c_str()); - if ((res = impl->camera->start()) < 0) + if ((res = impl->camera->start(&impl->initial_controls)) < 0) goto error; for (Request *req : impl->pendingRequests) {