On Mon, Jul 13, 2020 at 07:39:41PM +0100, Laurence Tratt wrote: > video(1) allows users to adjust controls such as brightness, saturation > (etc.) depending on the input device in question. These values persist even > after video(1) has quit, allowing you to e.g. increase the brightness of a > webcam before connecting to a video call. However, the only way to adjust > values is to hold down keys in the GUI, which is slow, error prone, and > can't easily be scripted. > > This patch adds a "-c" option to video(1) which either takes the special > value "reset" or a "control=value" pair. For example: > > $ video -c reset > > resets all the controls to their default values. Assuming the input device > in question supports brightness one can set that as follows: > > $ video -c brightness=200 > > Note that the available controls, and their min/max values, will vary from > device to device. > > To keep the patch simple, only one "-c" option can be passed to video(1) at > a time. Note that passing this option causes video(1) to quit before > displaying video (in identical fashion to "-q") which makes it useful for > scripting purposes.
The attached patch reworks things a bit. First, it now works with white_balance_temperature, which (at least on my C920) requires mmap_init to be called first. Second, the previous patch sometimes set controls to surprising values because it called what is (in effect) a "change control by X" function. This patch now renames the "old" function to "dev_inc_ctrl" and introduces a new "dev_set_ctrl_abs". This then provides an obvious opportunity to simplify the reset function. With this patch I can do things like: $ video -c white_balance_temperature=6500 $ video -c brightness=200 $ video && video -c reset && video and see changes being made as appropriate. Laurie Index: video.1 =================================================================== RCS file: /cvs/xenocara/app/video/video.1,v retrieving revision 1.15 diff -u -r1.15 video.1 --- video.1 17 Jul 2020 07:51:23 -0000 1.15 +++ video.1 17 Jul 2020 21:44:49 -0000 @@ -27,6 +27,7 @@ .Bk -words .Op Fl \&gqRv .Op Fl a Ar adaptor +.Op Fl c Ar reset | control=value .Op Fl e Ar encoding .Op Fl f Ar file .Op Fl i Ar input @@ -81,6 +82,15 @@ adaptor to use. The default is 0, the first adaptor reported by .Xr X 7 . +.It Fl c Ar reset | control=value +Set control value (e.g. brightness) and exit. The special name +.Ql reset +resets all values to their default. The available controls can be found +with +.Fl q +and the default values with +.Fl c Ar reset +.Fl v . .It Fl e Ar encoding Lowercase FOURCC name of video encoding to use. Valid arguments are Index: video.c =================================================================== RCS file: /cvs/xenocara/app/video/video.c,v retrieving revision 1.31 diff -u -r1.31 video.c --- video.c 17 Jul 2020 07:51:23 -0000 1.31 +++ video.c 17 Jul 2020 21:44:49 -0000 @@ -192,7 +192,10 @@ #define M_IN_FILE 0x4 #define M_OUT_FILE 0x8 #define M_QUERY 0x10 +#define M_RESET 0x20 +#define M_SET_CTRL 0x40 int mode; + char *set_ctrl_str; int verbose; }; @@ -212,10 +215,12 @@ void dev_dump_info(struct video *); void dev_dump_query(struct video *); int dev_init(struct video *); -void dev_set_ctrl(struct video *, int, int); +void dev_inc_ctrl(struct video *, int, int); +void dev_set_ctrl_abs(struct video *vid, int, int); void dev_set_ctrl_auto_white_balance(struct video *, int); void dev_reset_ctrls(struct video *); +int parse_ctrl(struct video *, int *, int *); int parse_size(struct video *); int choose_size(struct video *); int choose_enc(struct video *); @@ -241,9 +246,9 @@ usage(void) { fprintf(stderr, "usage: %s [-gqRv] " - "[-a adaptor] [-e encoding] [-f file] [-i input] [-O output]\n" - " %*s [-o output] [-r rate] [-s size]\n", __progname, - (int)strlen(__progname), ""); + "[-a adaptor] [-c reset|control=value] [-e encoding] [-f file]\n" + " %*s [-i input] [-O output] [-o output] [-r rate] [-s size]\n", + __progname, (int)strlen(__progname), ""); } int @@ -657,46 +662,46 @@ switch (str) { case 'A': if (vid->mode & M_IN_DEV) - dev_set_ctrl(vid, CTRL_SHARPNESS, 1); + dev_inc_ctrl(vid, CTRL_SHARPNESS, 1); break; case 'a': if (vid->mode & M_IN_DEV) - dev_set_ctrl(vid, CTRL_SHARPNESS, -1); + dev_inc_ctrl(vid, CTRL_SHARPNESS, -1); break; case 'B': if (vid->mode & M_IN_DEV) - dev_set_ctrl(vid, CTRL_BRIGHTNESS, 1); + dev_inc_ctrl(vid, CTRL_BRIGHTNESS, 1); break; case 'b': if (vid->mode & M_IN_DEV) - dev_set_ctrl(vid, CTRL_BRIGHTNESS, -1); + dev_inc_ctrl(vid, CTRL_BRIGHTNESS, -1); break; case 'C': if (vid->mode & M_IN_DEV) - dev_set_ctrl(vid, CTRL_CONTRAST, 1); + dev_inc_ctrl(vid, CTRL_CONTRAST, 1); break; case 'c': if (vid->mode & M_IN_DEV) - dev_set_ctrl(vid, CTRL_CONTRAST, -1); + dev_inc_ctrl(vid, CTRL_CONTRAST, -1); break; case 'f': resize_window(vid, 1); break; case 'G': if (vid->mode & M_IN_DEV) - dev_set_ctrl(vid, CTRL_GAIN, 1); + dev_inc_ctrl(vid, CTRL_GAIN, 1); break; case 'g': if (vid->mode & M_IN_DEV) - dev_set_ctrl(vid, CTRL_GAIN, -1); + dev_inc_ctrl(vid, CTRL_GAIN, -1); break; case 'H': if (vid->mode & M_IN_DEV) - dev_set_ctrl(vid, CTRL_HUE, 1); + dev_inc_ctrl(vid, CTRL_HUE, 1); break; case 'h': if (vid->mode & M_IN_DEV) - dev_set_ctrl(vid, CTRL_HUE, -1); + dev_inc_ctrl(vid, CTRL_HUE, -1); break; case 'O': if (!wout && vid->verbose > 0) @@ -710,11 +715,11 @@ break; case 'M': if (vid->mode & M_IN_DEV) - dev_set_ctrl(vid, CTRL_GAMMA, 1); + dev_inc_ctrl(vid, CTRL_GAMMA, 1); break; case 'm': if (vid->mode & M_IN_DEV) - dev_set_ctrl(vid, CTRL_GAMMA, -1); + dev_inc_ctrl(vid, CTRL_GAMMA, -1); break; case 'p': hold = !hold; @@ -728,20 +733,20 @@ break; case 'S': if (vid->mode & M_IN_DEV) - dev_set_ctrl(vid, CTRL_SATURATION, 1); + dev_inc_ctrl(vid, CTRL_SATURATION, 1); break; case 's': if (vid->mode & M_IN_DEV) - dev_set_ctrl(vid, CTRL_SATURATION, -1); + dev_inc_ctrl(vid, CTRL_SATURATION, -1); break; case 'W': if (vid->mode & M_IN_DEV) - dev_set_ctrl(vid, + dev_inc_ctrl(vid, CTRL_WHITE_BALANCE_TEMPERATURE, 10); break; case 'w': if (vid->mode & M_IN_DEV) - dev_set_ctrl(vid, + dev_inc_ctrl(vid, CTRL_WHITE_BALANCE_TEMPERATURE, -10); break; default: @@ -1010,10 +1015,9 @@ } void -dev_set_ctrl(struct video *vid, int ctrl, int change) +dev_inc_ctrl(struct video *vid, int ctrl, int change) { struct dev *d = &vid->dev; - struct v4l2_control control; int val; if (ctrl < 0 || ctrl >= CTRL_LAST) { @@ -1025,6 +1029,25 @@ ctrls[ctrl].name, d->path); return; } + val = ctrls[ctrl].cur + ctrls[ctrl].step * change; + dev_set_ctrl_abs(vid, ctrl, val); +} + +void +dev_set_ctrl_abs(struct video *vid, int ctrl, int val) +{ + struct dev *d = &vid->dev; + struct v4l2_control control; + + if (ctrl < 0 || ctrl >= CTRL_LAST) { + warnx("invalid control"); + return; + } + if (!ctrls[ctrl].supported) { + warnx("control %s not supported by %s", + ctrls[ctrl].name, d->path); + return; + } if (ctrl == CTRL_WHITE_BALANCE_TEMPERATURE) { /* * The spec requires auto-white balance to be off before @@ -1032,7 +1055,6 @@ */ dev_set_ctrl_auto_white_balance(vid, 0); } - val = ctrls[ctrl].cur + ctrls[ctrl].step * change; if (val > ctrls[ctrl].max) val = ctrls[ctrl].max; else if (val < ctrls[ctrl].min) @@ -1081,25 +1103,7 @@ for (i = 0; i < CTRL_LAST; i++) { if (!ctrls[i].supported) continue; - if (i == CTRL_WHITE_BALANCE_TEMPERATURE) { - /* - * We might be asked to reset before the white balance - * temperature has been adjusted, so we need to make - * sure that auto-white balance really is off. - */ - dev_set_ctrl_auto_white_balance(vid, 0); - } - control.id = ctrls[i].id; - control.value = ctrls[i].def; - if (ioctl(d->fd, VIDIOC_S_CTRL, &control) != 0) - warn("VIDIOC_S_CTRL(%s)", ctrls[i].name); - control.id = ctrls[i].id; - if (ioctl(d->fd, VIDIOC_G_CTRL, &control) != 0) - warn("VIDIOC_G_CTRL(%s)", ctrls[i].name); - ctrls[i].cur = control.value; - if (vid->verbose > 0) - fprintf(stderr, "%s now %d\n", ctrls[i].name, - ctrls[i].cur); + dev_set_ctrl_abs(vid, i, ctrls[i].def); if (i == CTRL_WHITE_BALANCE_TEMPERATURE) { dev_set_ctrl_auto_white_balance(vid, 1); } @@ -1221,6 +1225,39 @@ return 1; } + +int +parse_ctrl(struct video *vid, int *ctrl_id, int *ctrl_val) +{ + char *valp; + const char *errstr; + + if (!vid->set_ctrl_str) { + return 0; + } + + valp = strsep(&vid->set_ctrl_str, "="); + if (*valp == '\0' || vid->set_ctrl_str == '\0') { + return 0; + } + for (*ctrl_id = 0; *ctrl_id < CTRL_LAST; (*ctrl_id)++) { + if (strcmp(valp, ctrls[*ctrl_id].name) == 0) { + break; + } + } + if (*ctrl_id == CTRL_LAST) { + warnx("Unknown control '%s'", valp); + return 0; + } + *ctrl_val = strtonum(vid->set_ctrl_str, ctrls[*ctrl_id].min, + ctrls[*ctrl_id].max, &errstr); + if (errstr != NULL) { + warnx("control value '%s' is %s", valp, errstr); + return 0; + } + return 1; +} + int parse_size(struct video *vid) { @@ -1481,6 +1518,8 @@ int setup(struct video *vid) { + int ctrl_id, ctrl_val; + if (vid->mode & M_IN_FILE) { if (!strcmp(vid->iofile, "-")) vid->iofile_fd = STDIN_FILENO; @@ -1520,6 +1559,7 @@ if (!parse_size(vid) || !choose_size(vid)) return 0; + vid->bpf = vid->width * vid->height * encs[vid->enc].bpp / NBBY; if (vid->verbose > 0) { @@ -1553,17 +1593,30 @@ if ((vid->mode & M_IN_DEV) && !dev_init(vid)) return 0; + if (vid->mmap_on) { + if (!mmap_init(vid)) + return 0; + } + + if (vid->mode & M_SET_CTRL) { + if (!parse_ctrl(vid, &ctrl_id, &ctrl_val)) { + return 0; + } + dev_set_ctrl_abs(vid, ctrl_id, ctrl_val); + return 1; + } + + if (vid->mode & M_RESET) { + dev_reset_ctrls(vid); + return 1; + } + if ((vid->mode & M_OUT_XV) && !xv_init(vid)) return 0; if (vid->sz_str && !strcmp(vid->sz_str, "full")) resize_window(vid, 1); - if (vid->mmap_on) { - if (!mmap_init(vid)) - return 0; - } - if (vid->mode & M_OUT_XV) net_wm_supported(vid); @@ -1949,7 +2002,7 @@ vid.mmap_on = 1; /* mmap method is default */ wout = 1; - while ((ch = getopt(argc, argv, "gqRva:e:f:i:O:o:r:s:")) != -1) { + while ((ch = getopt(argc, argv, "gqRva:c:e:f:i:O:o:r:s:")) != -1) { switch (ch) { case 'a': x->cur_adap = strtonum(optarg, 0, 4, &errstr); @@ -1958,6 +2011,17 @@ errs++; } break; + case 'c': + if ((vid.mode & M_RESET) || (vid.mode & M_SET_CTRL)) { + warnx("Only one '-c' option allowed."); + errs++; + } else if (strcmp(optarg, "reset") == 0) { + vid.mode |= M_RESET; + } else { + vid.mode |= M_SET_CTRL; + vid.set_ctrl_str = strdup(optarg); + } + break; case 'e': vid.enc = find_enc(optarg); if (vid.enc >= ENC_LAST) { @@ -2055,6 +2119,10 @@ if (!setup(&vid)) cleanup(&vid, 1); + + if ((vid.mode & M_RESET) || (vid.mode & M_SET_CTRL)) { + cleanup(&vid, 0); + } if (vid.mode & M_IN_FILE) { if (pledge("stdio rpath", NULL) == -1)