diff --git a/src/device.c b/src/device.c index f016216..2e4c157 100644 --- a/src/device.c +++ b/src/device.c @@ -69,7 +69,9 @@ static int _device_open_mmap(struct device_t *dev); static int _device_open_queue_buffers(struct device_t *dev); static void _device_open_alloc_picbufs(struct device_t *dev); static int _device_apply_resolution(struct device_t *dev, unsigned width, unsigned height); -static void _device_apply_image_settings(struct device_t *dev); +static void _device_apply_controls(struct device_t *dev); +static int _device_check_control(struct device_t *dev, const char *name, unsigned cid, int value, bool quiet); +static void _device_set_control(struct device_t *dev, const char *name, unsigned cid, int value, bool quiet); static const char *_format_to_string_fourcc(char *buf, size_t size, unsigned format); static const char *_format_to_string_nullable(unsigned format); @@ -78,11 +80,11 @@ static const char *_standard_to_string(v4l2_std_id standard); struct device_t *device_init() { - struct image_settings_t *img; + struct controls_t *ctl; struct device_runtime_t *run; struct device_t *dev; - A_CALLOC(img, 1); + A_CALLOC(ctl, 1); A_CALLOC(run, 1); run->fd = -1; @@ -97,14 +99,14 @@ struct device_t *device_init() { dev->n_workers = dev->n_buffers; dev->timeout = 1; dev->error_delay = 1; - dev->img = img; + dev->ctl = ctl; dev->run = run; return dev; } void device_destroy(struct device_t *dev) { free(dev->run); - free(dev->img); + free(dev->ctl); free(dev); } @@ -149,7 +151,7 @@ int device_open(struct device_t *dev) { goto error; } _device_open_alloc_picbufs(dev); - _device_apply_image_settings(dev); + _device_apply_controls(dev); LOG_DEBUG("Device fd=%d initialized", dev->run->fd); return 0; @@ -460,38 +462,23 @@ static int _device_apply_resolution(struct device_t *dev, unsigned width, unsign return 0; } -static void _device_apply_image_settings(struct device_t *dev) { - struct v4l2_queryctrl query; - struct v4l2_control ctl; - -# define SET_CID(_cid, _dest) { \ - MEMSET_ZERO(query); query.id = _cid; \ - if (xioctl(dev->run->fd, VIDIOC_QUERYCTRL, &query) < 0 || query.flags & V4L2_CTRL_FLAG_DISABLED) { \ - LOG_INFO("Changing image " #_dest " is unsupported"); \ - } else { \ - MEMSET_ZERO(ctl); ctl.id = _cid; ctl.value = (int)dev->img->_dest; \ - if (ctl.value < query.minimum || ctl.value > query.maximum || ctl.value % query.step != 0) { \ - LOG_ERROR("Invalid value %d for image " #_dest ": min=%d, max=%d, default=%d, step=%u", \ - ctl.value, query.minimum, query.maximum, query.default_value, query.step); \ - } else { \ - if (xioctl(dev->run->fd, VIDIOC_S_CTRL, &ctl) < 0) { \ - LOG_PERROR("Can't set image " #_dest); \ - } else { \ - LOG_INFO("Using image " #_dest ": %d (min=%d, max=%d, default=%d, step=%u)", \ - ctl.value, query.minimum, query.maximum, query.default_value, query.step); \ - } \ - } \ +static void _device_apply_controls(struct device_t *dev) { +# define SET_CID(_cid, _dest, _value, _quiet) { \ + if (_device_check_control(dev, #_dest, _cid, _value, _quiet) == 0) { \ + _device_set_control(dev, #_dest, _cid, _value, _quiet); \ } \ } # define SET_CID_MANUAL(_cid, _dest) { \ - if (dev->img->_dest##_set) { SET_CID(_cid, _dest); } \ + if (dev->ctl->_dest.value_set) { \ + SET_CID(_cid, _dest, dev->ctl->_dest.value, false); \ + } \ } # define SET_CID_AUTO(_cid_auto, _cid_manual, _dest) { \ - if (dev->img->_dest##_set) { \ - SET_CID(_cid_auto, _dest##_auto); \ - if (!dev->img->_dest##_auto) { SET_CID(_cid_manual, _dest); } \ + if (dev->ctl->_dest.value_set || dev->ctl->_dest.auto_set) { \ + SET_CID(_cid_auto, _dest##_auto, dev->ctl->_dest.auto_set, dev->ctl->_dest.value_set); \ + SET_CID_MANUAL(_cid_manual, _dest); \ } \ } @@ -510,6 +497,44 @@ static void _device_apply_image_settings(struct device_t *dev) { # undef SET_CID } +static int _device_check_control(struct device_t *dev, const char *name, unsigned cid, int value, bool quiet) { + struct v4l2_queryctrl query; + + MEMSET_ZERO(query); + query.id = cid; + + if (xioctl(dev->run->fd, VIDIOC_QUERYCTRL, &query) < 0 || query.flags & V4L2_CTRL_FLAG_DISABLED) { + if (!quiet) { + LOG_ERROR("Changing control %s is unsupported", name); + } + return -1; + } + 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 -2; + } + return 0; +} + +static void _device_set_control(struct device_t *dev, const char *name, unsigned cid, int value, bool quiet) { + struct v4l2_control ctl; + + MEMSET_ZERO(ctl); + ctl.id = cid; + ctl.value = value; + + if (xioctl(dev->run->fd, VIDIOC_S_CTRL, &ctl) < 0) { + if (!quiet) { + LOG_PERROR("Can't set control %s", name); + } + } else if (!quiet) { + LOG_INFO("Using control %s: %d", name, ctl.value); + } +} + static const char *_format_to_string_fourcc(char *buf, size_t size, unsigned format) { assert(size >= 8); buf[0] = format & 0x7F; diff --git a/src/device.h b/src/device.h index bcf5081..3af7150 100644 --- a/src/device.h +++ b/src/device.h @@ -69,23 +69,23 @@ struct device_runtime_t { bool capturing; }; -#define S_MANUAL(_dest) int _dest; bool _dest##_set; -#define S_AUTO(_dest) int _dest; bool _dest##_set; bool _dest##_auto; - -struct image_settings_t { - S_AUTO (brightness); - S_MANUAL (contrast); - S_MANUAL (saturation); - S_AUTO (hue); - S_MANUAL (gamma); - S_MANUAL (sharpness); - S_MANUAL (backlight_compensation); - S_AUTO (white_balance); - S_AUTO (gain); +struct control_t { + int value; + bool value_set; + bool auto_set; }; -#undef S_AUTO -#undef S_MANUAL +struct controls_t { + struct control_t brightness; + struct control_t contrast; + struct control_t saturation; + struct control_t hue; + struct control_t gamma; + struct control_t sharpness; + struct control_t backlight_compensation; + struct control_t white_balance; + struct control_t gain; +}; struct device_t { char *path; @@ -103,7 +103,7 @@ struct device_t { unsigned timeout; unsigned error_delay; - struct image_settings_t *img; + struct controls_t *ctl; struct device_runtime_t *run; sig_atomic_t volatile stop; diff --git a/src/main.c b/src/main.c index 5ab027b..b867b52 100644 --- a/src/main.c +++ b/src/main.c @@ -43,53 +43,53 @@ static const char _short_opts[] = "d:i:x:y:m:a:f:z:tb:w:q:c:s:p:u:ro:e:h"; static const struct option _long_opts[] = { - {"device", required_argument, NULL, 'd'}, - {"input", required_argument, NULL, 'i'}, - {"width", required_argument, NULL, 'x'}, - {"height", required_argument, NULL, 'y'}, - {"format", required_argument, NULL, 'm'}, - {"tv-standard", required_argument, NULL, 'a'}, - {"desired-fps", required_argument, NULL, 'f'}, - {"min-frame-size", required_argument, NULL, 'z'}, - {"dv-timings", no_argument, NULL, 't'}, - {"buffers", required_argument, NULL, 'b'}, - {"workers", required_argument, NULL, 'w'}, - {"quality", required_argument, NULL, 'q'}, - {"encoder", required_argument, NULL, 'c'}, - {"device-timeout", required_argument, NULL, 1000}, - {"device-persistent", no_argument, NULL, 1001}, - {"device-error-delay", required_argument, NULL, 1002}, + {"device", required_argument, NULL, 'd'}, + {"input", required_argument, NULL, 'i'}, + {"width", required_argument, NULL, 'x'}, + {"height", required_argument, NULL, 'y'}, + {"format", required_argument, NULL, 'm'}, + {"tv-standard", required_argument, NULL, 'a'}, + {"desired-fps", required_argument, NULL, 'f'}, + {"min-frame-size", required_argument, NULL, 'z'}, + {"dv-timings", no_argument, NULL, 't'}, + {"buffers", required_argument, NULL, 'b'}, + {"workers", required_argument, NULL, 'w'}, + {"quality", required_argument, NULL, 'q'}, + {"encoder", required_argument, NULL, 'c'}, + {"device-timeout", required_argument, NULL, 1000}, + {"device-persistent", no_argument, NULL, 1001}, + {"device-error-delay", required_argument, NULL, 1002}, - {"image-brightness", required_argument, NULL, 2000}, - {"image-brightness-auto", no_argument, NULL, 2001}, - {"image-contrast", required_argument, NULL, 2002}, - {"image-saturation", required_argument, NULL, 2003}, - {"image-hue", required_argument, NULL, 2004}, - {"image-hue-auto", no_argument, NULL, 2005}, - {"image-gamma", required_argument, NULL, 2006}, - {"image-sharpness", required_argument, NULL, 2007}, - {"image-backlight-compensation", required_argument, NULL, 2008}, - {"image-white-balance", required_argument, NULL, 2009}, - {"image-white-balance-auto", no_argument, NULL, 2010}, - {"image-gain", required_argument, NULL, 2011}, - {"image-gain-auto", no_argument, NULL, 2012}, + {"brightness", required_argument, NULL, 2000}, + {"brightness-auto", no_argument, NULL, 2001}, + {"contrast", required_argument, NULL, 2002}, + {"saturation", required_argument, NULL, 2003}, + {"hue", required_argument, NULL, 2004}, + {"hue-auto", no_argument, NULL, 2005}, + {"gamma", required_argument, NULL, 2006}, + {"sharpness", required_argument, NULL, 2007}, + {"backlight-compensation", required_argument, NULL, 2008}, + {"white-balance", required_argument, NULL, 2009}, + {"white-balance-auto", no_argument, NULL, 2010}, + {"gain", required_argument, NULL, 2011}, + {"gain-auto", no_argument, NULL, 2012}, - {"host", required_argument, NULL, 's'}, - {"port", required_argument, NULL, 'p'}, - {"unix", required_argument, NULL, 'u'}, - {"unix-rm", no_argument, NULL, 'r'}, - {"unix-mode", required_argument, NULL, 'o'}, - {"drop-same-frames", required_argument, NULL, 'e'}, - {"fake-width", required_argument, NULL, 3001}, - {"fake-height", required_argument, NULL, 3002}, - {"server-timeout", required_argument, NULL, 3003}, + {"host", required_argument, NULL, 's'}, + {"port", required_argument, NULL, 'p'}, + {"unix", required_argument, NULL, 'u'}, + {"unix-rm", no_argument, NULL, 'r'}, + {"unix-mode", required_argument, NULL, 'o'}, + {"drop-same-frames", required_argument, NULL, 'e'}, + {"fake-width", required_argument, NULL, 3001}, + {"fake-height", required_argument, NULL, 3002}, + {"server-timeout", required_argument, NULL, 3003}, - {"perf", no_argument, NULL, 5000}, - {"verbose", no_argument, NULL, 5001}, - {"debug", no_argument, NULL, 5002}, - {"log-level", required_argument, NULL, 5010}, - {"help", no_argument, NULL, 'h'}, - {"version", no_argument, NULL, 6000}, + {"perf", no_argument, NULL, 5000}, + {"verbose", no_argument, NULL, 5001}, + {"debug", no_argument, NULL, 5002}, + {"log-level", required_argument, NULL, 5010}, + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 6000}, {NULL, 0, NULL, 0}, }; @@ -136,21 +136,21 @@ static void _help(struct device_t *dev, struct encoder_t *encoder, struct http_s printf(" --device-persistent -- Don't re-initialize device on timeout. Default: disabled.\n\n"); printf(" --device-error-delay -- Delay before trying to connect to the device again\n"); printf(" after a timeout. Default: %u\n\n", dev->error_delay); - printf("Image options:\n"); + printf("Image control options:\n"); printf("---------------\n"); - printf(" --image-brightness -- Set brightness. Default: no change.\n\n"); - printf(" --image-brightness-auto -- Enable automatic brightness control. Default: no change.\n\n"); - printf(" --image-contrast -- Set contrast. Default: no change.\n\n"); - printf(" --image-saturation -- Set saturation. Default: no change.\n\n"); - printf(" --image-hue -- Set hue. Default: no change.\n\n"); - printf(" --image-hue-auto -- Enable automatic hue control. Default: no change.\n\n"); - printf(" --image-gamma -- Set gamma. Default: no change.\n\n"); - printf(" --image-sharpness -- Set sharpness. Default: no change.\n\n"); - printf(" --image-backlight-compensation -- Set backlight compensation. Default: no change.\n\n"); - printf(" --image-white-balance -- Set white balance. Default: no change.\n\n"); - printf(" --image-white-balance-auto -- Enable automatic white balance control. Default: no change.\n\n"); - printf(" --image-gain -- Set gain. Default: no change.\n\n"); - printf(" --image-gain-auto -- Enable automatic gain control. Default: no change.\n\n"); + printf(" --brightness -- Set brightness. Default: no change.\n\n"); + printf(" --brightness-auto -- Enable automatic brightness control. Default: no change.\n\n"); + printf(" --contrast -- Set contrast. Default: no change.\n\n"); + printf(" --saturation -- Set saturation. Default: no change.\n\n"); + printf(" --hue -- Set hue. Default: no change.\n\n"); + printf(" --hue-auto -- Enable automatic hue control. Default: no change.\n\n"); + printf(" --gamma -- Set gamma. Default: no change.\n\n"); + printf(" --sharpness -- Set sharpness. Default: no change.\n\n"); + printf(" --backlight-compensation -- Set backlight compensation. Default: no change.\n\n"); + printf(" --white-balance -- Set white balance. Default: no change.\n\n"); + printf(" --white-balance-auto -- Enable automatic white balance control. Default: no change.\n\n"); + printf(" --gain -- Set gain. Default: no change.\n\n"); + printf(" --gain-auto -- Enable automatic gain control. Default: no change.\n\n"); printf("HTTP server options:\n"); printf("--------------------\n"); printf(" -s|--host
-- Listen on Hostname or IP. Default: %s\n\n", server->host); @@ -209,13 +209,21 @@ static int _parse_options(int argc, char *argv[], struct device_t *dev, struct e break; \ } -# define OPT_CHMOD(_dest, _name) OPT_INT(_dest, _name, 8) +# define OPT_CHMOD(_dest, _name) \ + OPT_INT(_dest, _name, 8) -# define OPT_IMG(_dest) \ - { dev->img->_dest##_set = true; OPT_INT(dev->img->_dest, "--image-"#_dest, 10); break; } +# define OPT_CTL(_dest) { \ + dev->ctl->_dest.value_set = true; \ + dev->ctl->_dest.auto_set = false; \ + OPT_INT(dev->ctl->_dest.value, "--"#_dest, 10); \ + break; \ + } -# define OPT_IMG_AUTO(_dest) \ - { dev->img->_dest##_set = true; dev->img->_dest##_auto = true; break; } +# define OPT_CTL_AUTO(_dest) { \ + dev->ctl->_dest.value_set = false; \ + dev->ctl->_dest.auto_set = true; \ + break; \ + } int index; int ch; @@ -243,19 +251,19 @@ static int _parse_options(int argc, char *argv[], struct device_t *dev, struct e case 1001: OPT_SET(dev->persistent, true); case 1002: OPT_UNSIGNED(dev->error_delay, "--device-error-delay", 1, 60); - case 2000: OPT_IMG(brightness); - case 2001: OPT_IMG_AUTO(brightness); - case 2002: OPT_IMG(contrast); - case 2003: OPT_IMG(saturation); - case 2004: OPT_IMG(hue); - case 2005: OPT_IMG_AUTO(hue); - case 2006: OPT_IMG(gamma); - case 2007: OPT_IMG(sharpness); - case 2008: OPT_IMG(backlight_compensation); - case 2009: OPT_IMG(white_balance); - case 2010: OPT_IMG_AUTO(white_balance); - case 2011: OPT_IMG(gain); - case 2012: OPT_IMG_AUTO(gain); + case 2000: OPT_CTL(brightness); + case 2001: OPT_CTL_AUTO(brightness); + case 2002: OPT_CTL(contrast); + case 2003: OPT_CTL(saturation); + case 2004: OPT_CTL(hue); + case 2005: OPT_CTL_AUTO(hue); + case 2006: OPT_CTL(gamma); + case 2007: OPT_CTL(sharpness); + case 2008: OPT_CTL(backlight_compensation); + case 2009: OPT_CTL(white_balance); + case 2010: OPT_CTL_AUTO(white_balance); + case 2011: OPT_CTL(gain); + case 2012: OPT_CTL_AUTO(gain); case 's': OPT_SET(server->host, optarg); case 'p': OPT_UNSIGNED(server->port, "--port", 1, 65535); @@ -278,8 +286,8 @@ static int _parse_options(int argc, char *argv[], struct device_t *dev, struct e } } -# undef OPT_IMG_AUTO -# undef OPT_IMG +# undef OPT_CTL_AUTO +# undef OPT_CTL # undef OPT_CHMOD # undef OPT_INT # undef OPT_PARSE