From 4841189456475129fb6b50f0a95b62ea1b373876 Mon Sep 17 00:00:00 2001 From: Azsde Date: Fri, 10 Feb 2023 12:20:50 +0100 Subject: [PATCH] Add support for libcamera Autofocus parameters (#1417) * [DEV #1416] Add support for libcamera Autofocus parameters * [DEVMINOR] Fix Go formatting * [DEV] Support AF window parameter for rpiCamera * [DEV] Update default .yml file to add rpiCameraAfWindow parameter --- internal/conf/path.go | 5 ++ internal/core/source_static.go | 53 ++++++++++-------- internal/rpicamera/exe/Makefile | 6 +- internal/rpicamera/exe/camera.cpp | 65 ++++++++++++++++++++++ internal/rpicamera/exe/parameters.c | 17 +++++- internal/rpicamera/exe/parameters.h | 9 ++- internal/rpicamera/exe/{roi.c => window.c} | 6 +- internal/rpicamera/exe/{roi.h => window.h} | 8 +-- internal/rpicamera/params.go | 53 ++++++++++-------- internal/rpicamera/rpicamera.go | 5 ++ rtsp-simple-server.yml | 18 ++++++ 11 files changed, 185 insertions(+), 60 deletions(-) rename internal/rpicamera/exe/{roi.c => window.c} (81%) rename internal/rpicamera/exe/{roi.h => window.h} (50%) diff --git a/internal/conf/path.go b/internal/conf/path.go index a520bf5a..d2741f88 100644 --- a/internal/conf/path.go +++ b/internal/conf/path.go @@ -73,6 +73,11 @@ type PathConf struct { RPICameraBitrate int `json:"rpiCameraBitrate"` RPICameraProfile string `json:"rpiCameraProfile"` RPICameraLevel string `json:"rpiCameraLevel"` + RPICameraAfMode string `json:"rpiCameraAfMode"` + RPICameraAfRange string `json:"rpiCameraAfRange"` + RPICameraAfSpeed string `json:"rpiCameraAfSpeed"` + RPICameraLensPosition float64 `json:"rpiCameraLensPosition"` + RPICameraAfWindow string `json:"rpiCameraAfWindow"` // authentication PublishUser Credential `json:"publishUser"` diff --git a/internal/core/source_static.go b/internal/core/source_static.go index 6536d1bf..46fd93eb 100644 --- a/internal/core/source_static.go +++ b/internal/core/source_static.go @@ -86,30 +86,35 @@ func newSourceStatic( case conf.Source == "rpiCamera": s.impl = newRPICameraSource( rpicamera.Params{ - CameraID: conf.RPICameraCamID, - Width: conf.RPICameraWidth, - Height: conf.RPICameraHeight, - HFlip: conf.RPICameraHFlip, - VFlip: conf.RPICameraVFlip, - Brightness: conf.RPICameraBrightness, - Contrast: conf.RPICameraContrast, - Saturation: conf.RPICameraSaturation, - Sharpness: conf.RPICameraSharpness, - Exposure: conf.RPICameraExposure, - AWB: conf.RPICameraAWB, - Denoise: conf.RPICameraDenoise, - Shutter: conf.RPICameraShutter, - Metering: conf.RPICameraMetering, - Gain: conf.RPICameraGain, - EV: conf.RPICameraEV, - ROI: conf.RPICameraROI, - TuningFile: conf.RPICameraTuningFile, - Mode: conf.RPICameraMode, - FPS: conf.RPICameraFPS, - IDRPeriod: conf.RPICameraIDRPeriod, - Bitrate: conf.RPICameraBitrate, - Profile: conf.RPICameraProfile, - Level: conf.RPICameraLevel, + CameraID: conf.RPICameraCamID, + Width: conf.RPICameraWidth, + Height: conf.RPICameraHeight, + HFlip: conf.RPICameraHFlip, + VFlip: conf.RPICameraVFlip, + Brightness: conf.RPICameraBrightness, + Contrast: conf.RPICameraContrast, + Saturation: conf.RPICameraSaturation, + Sharpness: conf.RPICameraSharpness, + Exposure: conf.RPICameraExposure, + AWB: conf.RPICameraAWB, + Denoise: conf.RPICameraDenoise, + Shutter: conf.RPICameraShutter, + Metering: conf.RPICameraMetering, + Gain: conf.RPICameraGain, + EV: conf.RPICameraEV, + ROI: conf.RPICameraROI, + TuningFile: conf.RPICameraTuningFile, + Mode: conf.RPICameraMode, + FPS: conf.RPICameraFPS, + IDRPeriod: conf.RPICameraIDRPeriod, + Bitrate: conf.RPICameraBitrate, + Profile: conf.RPICameraProfile, + Level: conf.RPICameraLevel, + AfMode: conf.RPICameraAfMode, + AfRange: conf.RPICameraAfRange, + AfSpeed: conf.RPICameraAfSpeed, + LensPosition: conf.RPICameraLensPosition, + AfWindow: conf.RPICameraAfWindow, }, s) } diff --git a/internal/rpicamera/exe/Makefile b/internal/rpicamera/exe/Makefile index ac439a6a..455cc019 100644 --- a/internal/rpicamera/exe/Makefile +++ b/internal/rpicamera/exe/Makefile @@ -3,7 +3,8 @@ CFLAGS = \ -Werror \ -Wall \ -Wextra \ - -Wno-unused-parameter + -Wno-unused-parameter \ + -Wno-unused-result CXXFLAGS = \ -Ofast \ @@ -11,6 +12,7 @@ CXXFLAGS = \ -Wall \ -Wextra \ -Wno-unused-parameter \ + -Wno-unused-result \ -std=c++17 \ $$(pkg-config --cflags libcamera) @@ -24,7 +26,7 @@ OBJS = \ encoder.o \ main.o \ parameters.o \ - roi.o \ + window.o \ sensor_mode.o all: exe diff --git a/internal/rpicamera/exe/camera.cpp b/internal/rpicamera/exe/camera.cpp index 90aaa38b..792ffe2d 100644 --- a/internal/rpicamera/exe/camera.cpp +++ b/internal/rpicamera/exe/camera.cpp @@ -334,6 +334,71 @@ bool camera_start(camera_t *cam) { int64_t frame_time = 1000000 / camp->params->fps; ctrls.set(controls::FrameDurationLimits, Span({ frame_time, frame_time })); + int af_mode; + if (strcmp(camp->params->af_mode, "manual") == 0) { + af_mode = controls::AfModeManual; + } else if (strcmp(camp->params->af_mode, "auto") == 0) { + af_mode = controls::AfModeAuto; + } else if (strcmp(camp->params->af_mode, "continuous") == 0) { + af_mode = controls::AfModeContinuous; + } else { + af_mode = controls::AfModeManual; + } + ctrls.set(controls::AfMode, af_mode); + + int af_range; + if (strcmp(camp->params->af_range, "normal") == 0) { + af_range = controls::AfRangeNormal; + } else if (strcmp(camp->params->af_range, "macro") == 0) { + af_range = controls::AfRangeMacro; + } else if (strcmp(camp->params->af_range, "full") == 0) { + af_range = controls::AfRangeFull; + } else { + af_range = controls::AfRangeNormal; + } + ctrls.set(controls::AfRange, af_range); + + int af_speed; + if (strcmp(camp->params->af_range, "normal") == 0) { + af_speed = controls::AfSpeedNormal; + } else if (strcmp(camp->params->af_range, "fast") == 0) { + af_speed = controls::AfSpeedFast; + } else { + af_speed = controls::AfSpeedNormal; + } + ctrls.set(controls::AfSpeed, af_speed); + + + // Lens Position + ctrls.set(controls::LensPosition, camp->params->lens_position); + + // Af Window + if (camp->params->af_window != NULL) { + std::optional opt = camp->camera->properties().get(properties::ScalerCropMaximum); + Rectangle sensor_area; + try { + sensor_area = opt.value(); + } catch(const std::bad_optional_access& exc) { + set_error("get(ScalerCropMaximum) failed"); + return false; + } + + Rectangle afwindows_rectangle[1]; + + afwindows_rectangle[0] = Rectangle ( + camp->params->af_window->x * sensor_area.width, + camp->params->af_window->y * sensor_area.height, + camp->params->af_window->width * sensor_area.width, + camp->params->af_window->height * sensor_area.height); + + afwindows_rectangle[0].translateBy(sensor_area.topLeft()); + //activate the AfMeteringWindows + ctrls.set(controls::AfMetering, controls::AfMeteringWindows); + //set window + ctrls.set(controls::AfWindows, afwindows_rectangle); + } + + int res = camp->camera->start(&ctrls); if (res != 0) { set_error("Camera.start() failed"); diff --git a/internal/rpicamera/exe/parameters.c b/internal/rpicamera/exe/parameters.c index 95043348..98741236 100644 --- a/internal/rpicamera/exe/parameters.c +++ b/internal/rpicamera/exe/parameters.c @@ -39,7 +39,7 @@ bool parameters_load(parameters_t *params) { params->ev = atof(getenv("EV")); if (strlen(getenv("ROI")) != 0) { - bool ok = roi_load(getenv("ROI"), ¶ms->roi); + bool ok = window_load(getenv("ROI"), ¶ms->roi); if (!ok) { set_error("invalid ROI"); return false; @@ -85,5 +85,20 @@ bool parameters_load(parameters_t *params) { params->buffer_count = 6; params->capture_buffer_count = params->buffer_count * 2; + params->af_mode = getenv("AF_MODE"); + params->af_range = getenv("AF_RANGE"); + params->af_speed = getenv("AF_SPEED"); + params->lens_position = atof(getenv("LENS_POSITION")); + + if (strlen(getenv("AF_WINDOW")) != 0) { + bool ok = window_load(getenv("AF_WINDOW"), ¶ms->af_window); + if (!ok) { + set_error("invalid AF_WINDOW"); + return false; + } + } else { + params->af_window = NULL; + } + return true; } diff --git a/internal/rpicamera/exe/parameters.h b/internal/rpicamera/exe/parameters.h index f96fc9cd..7971c5d5 100644 --- a/internal/rpicamera/exe/parameters.h +++ b/internal/rpicamera/exe/parameters.h @@ -3,7 +3,7 @@ #include -#include "roi.h" +#include "window.h" #include "sensor_mode.h" typedef struct { @@ -23,7 +23,7 @@ typedef struct { const char *metering; float gain; float ev; - roi_t *roi; + window_t *roi; const char *tuning_file; sensor_mode_t *mode; unsigned int fps; @@ -31,6 +31,11 @@ typedef struct { unsigned int bitrate; unsigned int profile; unsigned int level; + const char *af_mode; + const char *af_range; + const char *af_speed; + float lens_position; + window_t *af_window; // private unsigned int buffer_count; diff --git a/internal/rpicamera/exe/roi.c b/internal/rpicamera/exe/window.c similarity index 81% rename from internal/rpicamera/exe/roi.c rename to internal/rpicamera/exe/window.c index f82c271f..7d75b2db 100644 --- a/internal/rpicamera/exe/roi.c +++ b/internal/rpicamera/exe/window.c @@ -1,9 +1,9 @@ #include #include -#include "roi.h" +#include "window.h" -bool roi_load(const char *encoded, roi_t **mode) { +bool window_load(const char *encoded, window_t **mode) { float vals[4]; int i = 0; char *token = strtok((char *)encoded, ","); @@ -21,7 +21,7 @@ bool roi_load(const char *encoded, roi_t **mode) { return false; } - *mode = malloc(sizeof(roi_t)); + *mode = malloc(sizeof(window_t)); (*mode)->x = vals[0]; (*mode)->y = vals[1]; (*mode)->width = vals[2]; diff --git a/internal/rpicamera/exe/roi.h b/internal/rpicamera/exe/window.h similarity index 50% rename from internal/rpicamera/exe/roi.h rename to internal/rpicamera/exe/window.h index 4634d925..3584d3ab 100644 --- a/internal/rpicamera/exe/roi.h +++ b/internal/rpicamera/exe/window.h @@ -1,5 +1,5 @@ -#ifndef __ROI_H__ -#define __ROI_H__ +#ifndef __WINDOW_H__ +#define __WINDOW_H__ #include @@ -8,8 +8,8 @@ typedef struct { float y; float width; float height; -} roi_t; +} window_t; -bool roi_load(const char *encoded, roi_t **mode); +bool window_load(const char *encoded, window_t **mode); #endif diff --git a/internal/rpicamera/params.go b/internal/rpicamera/params.go index 5e3761cb..07022a0e 100644 --- a/internal/rpicamera/params.go +++ b/internal/rpicamera/params.go @@ -2,28 +2,33 @@ package rpicamera // Params is a set of camera parameters. type Params struct { - CameraID int - Width int - Height int - HFlip bool - VFlip bool - Brightness float64 - Contrast float64 - Saturation float64 - Sharpness float64 - Exposure string - AWB string - Denoise string - Shutter int - Metering string - Gain float64 - EV float64 - ROI string - TuningFile string - Mode string - FPS int - IDRPeriod int - Bitrate int - Profile string - Level string + CameraID int + Width int + Height int + HFlip bool + VFlip bool + Brightness float64 + Contrast float64 + Saturation float64 + Sharpness float64 + Exposure string + AWB string + Denoise string + Shutter int + Metering string + Gain float64 + EV float64 + ROI string + TuningFile string + Mode string + FPS int + IDRPeriod int + Bitrate int + Profile string + Level string + AfMode string + AfRange string + AfSpeed string + LensPosition float64 + AfWindow string } diff --git a/internal/rpicamera/rpicamera.go b/internal/rpicamera/rpicamera.go index 53544a8a..36315448 100644 --- a/internal/rpicamera/rpicamera.go +++ b/internal/rpicamera/rpicamera.go @@ -145,6 +145,11 @@ func New( "BITRATE=" + strconv.FormatInt(int64(params.Bitrate), 10), "PROFILE=" + params.Profile, "LEVEL=" + params.Level, + "AF_MODE=" + params.AfMode, + "AF_RANGE=" + params.AfRange, + "AF_SPEED=" + params.AfSpeed, + "LENS_POSITION=" + strconv.FormatFloat(params.LensPosition, 'f', -1, 64), + "AF_WINDOW=" + params.AfWindow, } exe, err := newEmbeddedExe(exeContent, env) diff --git a/rtsp-simple-server.yml b/rtsp-simple-server.yml index efe02f4b..d39c6f76 100644 --- a/rtsp-simple-server.yml +++ b/rtsp-simple-server.yml @@ -327,6 +327,24 @@ paths: rpiCameraProfile: main # H264 level rpiCameraLevel: '4.1' + # Autofocus mode + # values: manual, auto, continuous + rpiCameraAfMode: continuous + # Autofocus range + # values: normal, macro, full + rpiCameraAfRange: normal + # Autofocus speed + # values: normal, fast + rpiCameraAfSpeed: normal + # Lens position (for manual autofocus only), will be set to focus to a specific distance + # calculated by the following formula: d = 1 / value + # Examples: 0 moves the lens to infinity. + # 0.5 moves the lens to focus on objects 2m away. + # 2 moves the lens to focus on objects 50cm away. + rpiCameraLensPosition: 0.0 + # Specifies the autofocus window, in the form x,y,width,height where the coordinates + # are given as a proportion of the entire image. + rpiCameraAfWindow: # Username required to publish. # SHA256-hashed values can be inserted with the "sha256:" prefix.