diff --git a/src/libs/capture.c b/src/libs/capture.c index 052d347..dd48d52 100644 --- a/src/libs/capture.c +++ b/src/libs/capture.c @@ -108,23 +108,6 @@ static int _capture_open_queue_buffers(us_capture_s *cap); static int _capture_open_export_to_dma(us_capture_s *cap); static int _capture_apply_resolution(us_capture_s *cap, uint width, uint height, float hz); -static void _capture_apply_controls(const us_capture_s *cap); - -static int _capture_query_control( - const us_capture_s *cap, - struct v4l2_queryctrl *query, - const char *name, - uint cid, - bool quiet); - -static void _capture_set_control( - const us_capture_s *cap, - const struct v4l2_queryctrl *query, - const char *name, - uint cid, - int value, - bool quiet); - static const char *_format_to_string_nullable(uint format); static const char *_format_to_string_supported(uint format); static const char *_standard_to_string(v4l2_std_id standard); @@ -160,11 +143,13 @@ us_capture_s *us_capture_init(void) { cap->n_bufs = us_get_cores_available() + 1; cap->min_frame_size = 128; cap->timeout = 1; + cap->ctl = us_controls_init(); cap->run = run; return cap; } void us_capture_destroy(us_capture_s *cap) { + us_controls_destroy(cap->ctl); free(cap->run); free(cap); } @@ -283,7 +268,7 @@ int us_capture_open(us_capture_s *cap) { goto error; } } - _capture_apply_controls(cap); + us_controls_apply(cap->ctl, cap->run->fd); enum v4l2_buf_type type = run->capture_type; if (us_xioctl(run->fd, VIDIOC_STREAMON, &type) < 0) { @@ -1361,111 +1346,6 @@ static int _capture_apply_resolution(us_capture_s *cap, uint width, uint height, return 0; } -static void _capture_apply_controls(const us_capture_s *cap) { -# define SET_CID_VALUE(x_cid, x_field, x_value, x_quiet) { \ - struct v4l2_queryctrl m_query; \ - if (_capture_query_control(cap, &m_query, #x_field, x_cid, x_quiet) == 0) { \ - _capture_set_control(cap, &m_query, #x_field, x_cid, x_value, x_quiet); \ - } \ - } - -# define SET_CID_DEFAULT(x_cid, x_field, x_quiet) { \ - struct v4l2_queryctrl m_query; \ - if (_capture_query_control(cap, &m_query, #x_field, x_cid, x_quiet) == 0) { \ - _capture_set_control(cap, &m_query, #x_field, x_cid, m_query.default_value, x_quiet); \ - } \ - } - -# define CONTROL_MANUAL_CID(x_cid, x_field) { \ - if (cap->ctl.x_field.mode == CTL_MODE_VALUE) { \ - SET_CID_VALUE(x_cid, x_field, cap->ctl.x_field.value, false); \ - } else if (cap->ctl.x_field.mode == CTL_MODE_DEFAULT) { \ - SET_CID_DEFAULT(x_cid, x_field, false); \ - } \ - } - -# define CONTROL_AUTO_CID(x_cid_auto, x_cid_manual, x_field) { \ - if (cap->ctl.x_field.mode == CTL_MODE_VALUE) { \ - SET_CID_VALUE(x_cid_auto, x_field##_auto, 0, true); \ - SET_CID_VALUE(x_cid_manual, x_field, cap->ctl.x_field.value, false); \ - } else if (cap->ctl.x_field.mode == CTL_MODE_AUTO) { \ - SET_CID_VALUE(x_cid_auto, x_field##_auto, 1, false); \ - } else if (cap->ctl.x_field.mode == CTL_MODE_DEFAULT) { \ - SET_CID_VALUE(x_cid_auto, x_field##_auto, 0, true); /* Reset inactive flag */ \ - SET_CID_DEFAULT(x_cid_manual, x_field, false); \ - SET_CID_DEFAULT(x_cid_auto, x_field##_auto, false); \ - } \ - } - - CONTROL_AUTO_CID (V4L2_CID_AUTOBRIGHTNESS, V4L2_CID_BRIGHTNESS, brightness); - CONTROL_MANUAL_CID ( V4L2_CID_CONTRAST, contrast); - CONTROL_MANUAL_CID ( V4L2_CID_SATURATION, saturation); - CONTROL_AUTO_CID (V4L2_CID_HUE_AUTO, V4L2_CID_HUE, hue); - CONTROL_MANUAL_CID ( V4L2_CID_GAMMA, gamma); - CONTROL_MANUAL_CID ( V4L2_CID_SHARPNESS, sharpness); - CONTROL_MANUAL_CID ( V4L2_CID_BACKLIGHT_COMPENSATION, backlight_compensation); - CONTROL_AUTO_CID (V4L2_CID_AUTO_WHITE_BALANCE, V4L2_CID_WHITE_BALANCE_TEMPERATURE, white_balance); - CONTROL_AUTO_CID (V4L2_CID_AUTOGAIN, V4L2_CID_GAIN, gain); - CONTROL_MANUAL_CID ( V4L2_CID_COLORFX, color_effect); - CONTROL_MANUAL_CID ( V4L2_CID_ROTATE, rotate); - CONTROL_MANUAL_CID ( V4L2_CID_VFLIP, flip_vertical); - CONTROL_MANUAL_CID ( V4L2_CID_HFLIP, flip_horizontal); - -# undef CONTROL_AUTO_CID -# undef CONTROL_MANUAL_CID -# undef SET_CID_DEFAULT -# undef SET_CID_VALUE -} - -static int _capture_query_control( - const us_capture_s *cap, - struct v4l2_queryctrl *query, - const char *name, - uint cid, - bool quiet -) { - // cppcheck-suppress redundantPointerOp - US_MEMSET_ZERO(*query); - query->id = cid; - - if (us_xioctl(cap->run->fd, VIDIOC_QUERYCTRL, query) < 0 || query->flags & V4L2_CTRL_FLAG_DISABLED) { - if (!quiet) { - _LOG_ERROR("Changing control %s is unsupported", name); - } - return -1; - } - return 0; -} - -static void _capture_set_control( - const us_capture_s *cap, - const struct v4l2_queryctrl *query, - const char *name, - uint cid, - int value, - bool quiet -) { - if (value < query->minimum || value > query->maximum || value % query->step != 0) { - if (!quiet) { - _LOG_ERROR("Invalid value %d of control %s: min=%d, max=%d, default=%d, step=%u", - value, name, query->minimum, query->maximum, query->default_value, query->step); - } - return; - } - - struct v4l2_control ctl = { - .id = cid, - .value = value, - }; - if (us_xioctl(cap->run->fd, VIDIOC_S_CTRL, &ctl) < 0) { - if (!quiet) { - _LOG_PERROR("Can't set control %s", name); - } - } else if (!quiet) { - _LOG_INFO("Applying control %s: %d", name, ctl.value); - } -} - static const char *_format_to_string_nullable(uint format) { US_ARRAY_ITERATE(_FORMATS, 0, item, { if (item->format == format) { diff --git a/src/libs/capture.h b/src/libs/capture.h index 3b63f19..bfed3a2 100644 --- a/src/libs/capture.h +++ b/src/libs/capture.h @@ -28,6 +28,8 @@ #include "types.h" #include "frame.h" +#include "media.h" +#include "controls.h" #define US_VIDEO_MIN_WIDTH ((uint)160) @@ -51,11 +53,6 @@ typedef struct { atomic_int refs; } us_capture_hwbuf_s; -typedef struct { - int fd; - uint pad; -} us_media_pad_s; - typedef struct { int fd; int dv_timings_fd; @@ -76,34 +73,6 @@ typedef struct { int open_error_once; } us_capture_runtime_s; -typedef enum { - CTL_MODE_NONE = 0, - CTL_MODE_VALUE, - CTL_MODE_AUTO, - CTL_MODE_DEFAULT, -} us_control_mode_e; - -typedef struct { - us_control_mode_e mode; - int value; -} us_control_s; - -typedef struct { - us_control_s brightness; - us_control_s contrast; - us_control_s saturation; - us_control_s hue; - us_control_s gamma; - us_control_s sharpness; - us_control_s backlight_compensation; - us_control_s white_balance; - us_control_s gain; - us_control_s color_effect; - us_control_s rotate; - us_control_s flip_vertical; - us_control_s flip_horizontal; -} us_controls_s; - typedef struct { char *path; uint input; @@ -125,7 +94,7 @@ typedef struct { bool allow_truncated_frames; bool persistent; uint timeout; - us_controls_s ctl; + us_controls_s *ctl; us_capture_runtime_s *run; } us_capture_s; diff --git a/src/libs/controls.c b/src/libs/controls.c new file mode 100644 index 0000000..f93bfbd --- /dev/null +++ b/src/libs/controls.c @@ -0,0 +1,173 @@ +/***************************************************************************** +# # +# uStreamer - Lightweight and fast MJPEG-HTTP streamer. # +# # +# Copyright (C) 2018-2024 Maxim Devaev # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +*****************************************************************************/ + + +#include "controls.h" + +#include +#include +#include +#include + +#include +#include + +#include "types.h" +#include "tools.h" +#include "logging.h" +#include "xioctl.h" + + +static int _query_control( + int fd, + struct v4l2_queryctrl *query, + const char *name, + uint cid, + bool quiet); + +static void _set_control( + int fd, + const struct v4l2_queryctrl *query, + const char *name, + uint cid, + int value, + bool quiet); + + +#define _LOG_ERROR(x_msg, ...) US_LOG_ERROR("CAP: " x_msg, ##__VA_ARGS__) +#define _LOG_PERROR(x_msg, ...) US_LOG_PERROR("CAP: " x_msg, ##__VA_ARGS__) +#define _LOG_INFO(x_msg, ...) US_LOG_INFO("CAP: " x_msg, ##__VA_ARGS__) + + +us_controls_s *us_controls_init(void) { + us_controls_s *ctl; + US_CALLOC(ctl, 1); + return ctl; +} + +void us_controls_destroy(us_controls_s *ctl) { + free(ctl); +} + +void us_controls_apply(const us_controls_s *ctl, int fd) { +# define SET_CID_VALUE(x_cid, x_field, x_value, x_quiet) { \ + struct v4l2_queryctrl m_query; \ + if (_query_control(fd, &m_query, #x_field, x_cid, x_quiet) == 0) { \ + _set_control(fd, &m_query, #x_field, x_cid, x_value, x_quiet); \ + } \ + } + +# define SET_CID_DEFAULT(x_cid, x_field, x_quiet) { \ + struct v4l2_queryctrl m_query; \ + if (_query_control(fd, &m_query, #x_field, x_cid, x_quiet) == 0) { \ + _set_control(fd, &m_query, #x_field, x_cid, m_query.default_value, x_quiet); \ + } \ + } + +# define CONTROL_MANUAL_CID(x_cid, x_field) { \ + if (ctl->x_field.mode == US_CTL_MODE_VALUE) { \ + SET_CID_VALUE(x_cid, x_field, ctl->x_field.value, false); \ + } else if (ctl->x_field.mode == US_CTL_MODE_DEFAULT) { \ + SET_CID_DEFAULT(x_cid, x_field, false); \ + } \ + } + +# define CONTROL_AUTO_CID(x_cid_auto, x_cid_manual, x_field) { \ + if (ctl->x_field.mode == US_CTL_MODE_VALUE) { \ + SET_CID_VALUE(x_cid_auto, x_field##_auto, 0, true); \ + SET_CID_VALUE(x_cid_manual, x_field, ctl->x_field.value, false); \ + } else if (ctl->x_field.mode == US_CTL_MODE_AUTO) { \ + SET_CID_VALUE(x_cid_auto, x_field##_auto, 1, false); \ + } else if (ctl->x_field.mode == US_CTL_MODE_DEFAULT) { \ + SET_CID_VALUE(x_cid_auto, x_field##_auto, 0, true); /* Reset inactive flag */ \ + SET_CID_DEFAULT(x_cid_manual, x_field, false); \ + SET_CID_DEFAULT(x_cid_auto, x_field##_auto, false); \ + } \ + } + + CONTROL_AUTO_CID (V4L2_CID_AUTOBRIGHTNESS, V4L2_CID_BRIGHTNESS, brightness); + CONTROL_MANUAL_CID ( V4L2_CID_CONTRAST, contrast); + CONTROL_MANUAL_CID ( V4L2_CID_SATURATION, saturation); + CONTROL_AUTO_CID (V4L2_CID_HUE_AUTO, V4L2_CID_HUE, hue); + CONTROL_MANUAL_CID ( V4L2_CID_GAMMA, gamma); + CONTROL_MANUAL_CID ( V4L2_CID_SHARPNESS, sharpness); + CONTROL_MANUAL_CID ( V4L2_CID_BACKLIGHT_COMPENSATION, backlight_compensation); + CONTROL_AUTO_CID (V4L2_CID_AUTO_WHITE_BALANCE, V4L2_CID_WHITE_BALANCE_TEMPERATURE, white_balance); + CONTROL_AUTO_CID (V4L2_CID_AUTOGAIN, V4L2_CID_GAIN, gain); + CONTROL_MANUAL_CID ( V4L2_CID_COLORFX, color_effect); + CONTROL_MANUAL_CID ( V4L2_CID_ROTATE, rotate); + CONTROL_MANUAL_CID ( V4L2_CID_VFLIP, flip_vertical); + CONTROL_MANUAL_CID ( V4L2_CID_HFLIP, flip_horizontal); + +# undef CONTROL_AUTO_CID +# undef CONTROL_MANUAL_CID +# undef SET_CID_DEFAULT +# undef SET_CID_VALUE +} + +static int _query_control( + int fd, + struct v4l2_queryctrl *query, + const char *name, + uint cid, + bool quiet +) { + // cppcheck-suppress redundantPointerOp + US_MEMSET_ZERO(*query); + query->id = cid; + + if (us_xioctl(fd, VIDIOC_QUERYCTRL, query) < 0 || query->flags & V4L2_CTRL_FLAG_DISABLED) { + if (!quiet) { + _LOG_ERROR("Changing control %s is unsupported", name); + } + return -1; + } + return 0; +} + +static void _set_control( + int fd, + const struct v4l2_queryctrl *query, + const char *name, + uint cid, + int value, + bool quiet +) { + if (value < query->minimum || value > query->maximum || value % query->step != 0) { + if (!quiet) { + _LOG_ERROR("Invalid value %d of control %s: min=%d, max=%d, default=%d, step=%u", + value, name, query->minimum, query->maximum, query->default_value, query->step); + } + return; + } + + struct v4l2_control ctl = { + .id = cid, + .value = value, + }; + if (us_xioctl(fd, VIDIOC_S_CTRL, &ctl) < 0) { + if (!quiet) { + _LOG_PERROR("Can't set control %s", name); + } + } else if (!quiet) { + _LOG_INFO("Applying control %s: %d", name, ctl.value); + } +} diff --git a/src/libs/controls.h b/src/libs/controls.h new file mode 100644 index 0000000..878edd9 --- /dev/null +++ b/src/libs/controls.h @@ -0,0 +1,60 @@ +/***************************************************************************** +# # +# uStreamer - Lightweight and fast MJPEG-HTTP streamer. # +# # +# Copyright (C) 2018-2024 Maxim Devaev # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +*****************************************************************************/ + + +#pragma once + +#include "types.h" + + +typedef enum { + US_CTL_MODE_NONE = 0, + US_CTL_MODE_VALUE, + US_CTL_MODE_AUTO, + US_CTL_MODE_DEFAULT, +} us_control_mode_e; + +typedef struct { + us_control_mode_e mode; + int value; +} us_control_s; + +typedef struct { + us_control_s brightness; + us_control_s contrast; + us_control_s saturation; + us_control_s hue; + us_control_s gamma; + us_control_s sharpness; + us_control_s backlight_compensation; + us_control_s white_balance; + us_control_s gain; + us_control_s color_effect; + us_control_s rotate; + us_control_s flip_vertical; + us_control_s flip_horizontal; +} us_controls_s; + + +us_controls_s *us_controls_init(void); +void us_controls_destroy(us_controls_s *ctl); + +void us_controls_apply(const us_controls_s *ctl, int fd); diff --git a/src/libs/media.h b/src/libs/media.h index e94295d..b75de42 100644 --- a/src/libs/media.h +++ b/src/libs/media.h @@ -29,6 +29,12 @@ #include "types.h" +typedef struct { + int fd; + uint pad; +} us_media_pad_s; + + int us_media_find_link( struct media_v2_topology *topology, u32 link_type, diff --git a/src/ustreamer/options.c b/src/ustreamer/options.c index 519556f..d4b00e5 100644 --- a/src/ustreamer/options.c +++ b/src/ustreamer/options.c @@ -379,15 +379,15 @@ int us_options_parse( } # define OPT_CTL_DEFAULT_NOBREAK(x_dest) { \ - cap->ctl.x_dest.mode = CTL_MODE_DEFAULT; \ + cap->ctl->x_dest.mode = US_CTL_MODE_DEFAULT; \ } # define OPT_CTL_MANUAL(x_dest) { \ if (!strcasecmp(optarg, "default")) { \ OPT_CTL_DEFAULT_NOBREAK(x_dest); \ } else { \ - cap->ctl.x_dest.mode = CTL_MODE_VALUE; \ - OPT_NUMBER("--"#x_dest, cap->ctl.x_dest.value, INT_MIN, INT_MAX, 0); \ + cap->ctl->x_dest.mode = US_CTL_MODE_VALUE; \ + OPT_NUMBER("--"#x_dest, cap->ctl->x_dest.value, INT_MIN, INT_MAX, 0); \ } \ break; \ } @@ -396,10 +396,10 @@ int us_options_parse( if (!strcasecmp(optarg, "default")) { \ OPT_CTL_DEFAULT_NOBREAK(x_dest); \ } else if (!strcasecmp(optarg, "auto")) { \ - cap->ctl.x_dest.mode = CTL_MODE_AUTO; \ + cap->ctl->x_dest.mode = US_CTL_MODE_AUTO; \ } else { \ - cap->ctl.x_dest.mode = CTL_MODE_VALUE; \ - OPT_NUMBER("--"#x_dest, cap->ctl.x_dest.value, INT_MIN, INT_MAX, 0); \ + cap->ctl->x_dest.mode = US_CTL_MODE_VALUE; \ + OPT_NUMBER("--"#x_dest, cap->ctl->x_dest.value, INT_MIN, INT_MAX, 0); \ } \ break; \ }