On Wed, 29 Jul 2020 07:45:52 +0200
Marcus Glocker <mar...@nazgul.ch> wrote:

> On Sat, 25 Jul 2020 18:27:45 +0100
> Laurence Tratt <lau...@tratt.net> wrote:
> 
> > On Sat, Jul 25, 2020 at 10:27:15AM -0600, Theo de Raadt wrote:
> > 
> > Hello Theo,
> >   
> > > My primary concern is about a user changing settings which then
> > > persist past close.
> > >
> > > Upon re-open, how do they know what mode they are in?
> > >
> > > I understand the default mode for a camera might not be nice.  But
> > > at least it is a default.  If the previous use of the camera put
> > > it into a nasty mode, does this mean all new users must first
> > > force default?
> > >
> > > Now you don't know what mode it is in.  As a result, you must
> > > *always* demand change into a specific mode.  Rather than making
> > > things simpler, doesn't use of a camera become potentially more
> > > complicated?    
> > 
> > From what I can tell, there are two ways to control a uvideo device:
> > 
> >   1) The "semi-persistent" state changes that video(1) can make that
> > affects subsequent apps which access the device. My patch simply
> > makes those state changes possible from the command-line instead of
> > forcing the user to open a video and hold down keys until they reach
> > the desired state. In otReset all supported controls to their
> > default value and quit.her words, it doesn't change how you control
> > the device: "-c reset" is equivalent to running video(1) and
> > pressing "r", for example.
> > 
> >   2) Control via a loopback device. For example, on Windows,
> > Logitech allow you to change controls in their app where you can
> > see video; they then expose a second internal device which other
> > apps can use; I think controls are reset when the Logitech app is
> > closed.
> > 
> > On Linux I believe v4l2-ctl works as video(1) does (semi-persistent
> > state changes) but Linux also has video loopback devices. Presumably
> > they could do something similar to the Logitech Windows app, but I
> > don't know if they do so or not.
> > 
> > Unless we develop a loopback facility (or, perhaps, some sort of
> > uvideo daemon roughly equivalent to sndiod), I don't think we have
> > much choice but to continue with the semi-persistent state changes
> > that video(1) has always been capable of. It is a bit icky, but it's
> > the only way, for example, to change a webcam's brightness before
> > taking a video call in a web browser.  
> 
> OK - Let me try to pick this thread up once more :-)
> 
> Lets assume we leave the current behaviour of video(1) as is, which
> doesn't reset back the default control values on device close.
> 
> This adapted patch adds two new options and the ability to set
> multiple control values on the CLI with a similar interface as
> sysctl(8) has:
> 
> * c: List all current supported control values and quit
>   (-q is already occupied).
> 
> * d: Reset all supported controls to their default value and quit
>   (-r is already occupied).
>   -> This does the same thing as when pressing 'r' in the GUI.  
> 
> Some examples:
> 
>       $ doas video -c
>       brightness=128
>       contrast=32
>       saturation=64
>       hue=0
>       gamma=120
>       sharpness=2
>       white_balance_temperature=4000
>       $
> 
>       $ doas video brightness=200 sharpness=4 
>       brightness: 128 -> 200
>       sharpness: 2 -> 4
>       $
> 
>       $ doas video brightness
>       brightness: 200
>       $
> 
>       $ doas video -c
>       brightness=200
>       contrast=32
>       saturation=64
>       hue=0
>       gamma=120
>       sharpness=4
>       white_balance_temperature=4000
>       $
> 
>       $ doas video -d
>       $
> 
>       $ doas video -c
>       brightness=128
>       contrast=32
>       saturation=64
>       hue=0
>       gamma=120
>       sharpness=2
>       white_balance_temperature=4000
>       $
> 
>       $ doas video -f /dev/video1 gain=1
>       gain: 0 -> 1
>       $
> 
>       $ doas video -f /dev/video1 -c    
>       brightness=128
>       contrast=128
>       saturation=128
>       gain=1
>       sharpness=128
>       white_balance_temperature=4000
>       $
> 
> What we could think about optionally is to reset to the default
> control values on device close when we did use the GUI mode, but
> leave the controls sticky when we set them through the CLI, and
> explicitly document that in the video(1) man page for the
> [control[=value]] arguments.
> 
> But entirely removing the sticky controls I think is odd since as
> already mentioned before, it will remove the ability to preset any
> controls before we enter an application where we can't change them,
> like in an browser.  And web conference calls are pretty common
> nowadays ...

Slightly adapted diff;  Negative numbers can happen on controls.


Index: video.1
===================================================================
RCS file: /cvs/xenocara/app/video/video.1,v
retrieving revision 1.15
diff -u -p -u -p -r1.15 video.1
--- video.1     17 Jul 2020 07:51:23 -0000      1.15
+++ video.1     29 Jul 2020 20:47:28 -0000
@@ -25,7 +25,7 @@
 .Sh SYNOPSIS
 .Nm
 .Bk -words
-.Op Fl \&gqRv
+.Op Fl \&cdgqRv
 .Op Fl a Ar adaptor
 .Op Fl e Ar encoding
 .Op Fl f Ar file
@@ -34,6 +34,7 @@
 .Op Fl o Ar output
 .Op Fl r Ar rate
 .Op Fl s Ar size
+.Op Ar control Ns Op = Ns Ar value
 .Ek
 .Sh DESCRIPTION
 .Nm
@@ -81,6 +82,10 @@ Index of
 adaptor to use.
 The default is 0, the first adaptor reported by
 .Xr X 7 .
+.It Fl c
+List all current supported control values and quit.
+.It Fl d
+Reset all supported controls to their default value and quit.
 .It Fl e Ar encoding
 Lowercase FOURCC name of video encoding to use.
 Valid arguments are
@@ -219,6 +224,14 @@ Verbose mode.
 Multiple instances of this option are allowed.
 Each instance increases the level of informational output printed to
 .Ar stderr .
+.It Ar control Ns Op = Ns Ar value
+Retrieve the specified
+.Ar control ,
+or attempt to set it to
+.Ar value .
+Multiple
+.Ar control Ns Op = Ns Ar value
+arguments may be given.
 .El
 .Pp
 .Nm
Index: video.c
===================================================================
RCS file: /cvs/xenocara/app/video/video.c,v
retrieving revision 1.31
diff -u -p -u -p -r1.31 video.c
--- video.c     17 Jul 2020 07:51:23 -0000      1.31
+++ video.c     29 Jul 2020 20:47:28 -0000
@@ -192,6 +192,8 @@ struct video {
 #define M_IN_FILE      0x4
 #define M_OUT_FILE     0x8
 #define M_QUERY                0x10
+#define M_QUERY_CTRLS  0x20
+#define M_RESET                0x40
        int              mode;
        int              verbose;
 };
@@ -211,11 +213,14 @@ int dev_get_rates(struct video *);
 int dev_get_ctrls(struct video *);
 void dev_dump_info(struct video *);
 void dev_dump_query(struct video *);
+void dev_dump_query_ctrls(struct video *);
 int dev_init(struct video *);
-void dev_set_ctrl(struct video *, int, int);
-void dev_set_ctrl_auto_white_balance(struct video *, int);
+int dev_set_ctrl_abs(struct video *vid, int, int);
+void dev_set_ctrl_rel(struct video *, int, int);
+void dev_set_ctrl_auto_white_balance(struct video *, int, int);
 void dev_reset_ctrls(struct video *);
 
+int parse_ctrl(struct video *, int, char **);
 int parse_size(struct video *);
 int choose_size(struct video *);
 int choose_enc(struct video *);
@@ -240,10 +245,10 @@ extern char *__progname;
 void
 usage(void)
 {
-       fprintf(stderr, "usage: %s [-gqRv] "
+       fprintf(stderr, "usage: %s [-cdgqRv] "
            "[-a adaptor] [-e encoding] [-f file] [-i input] [-O output]\n"
-           "       %*s [-o output] [-r rate] [-s size]\n", __progname,
-           (int)strlen(__progname), "");
+           "       %*s [-o output] [-r rate] [-s size] [control[=value]]\n",
+           __progname, (int)strlen(__progname), "");
 }
 
 int
@@ -657,46 +662,46 @@ display_event(struct video *vid)
                        switch (str) {
                        case 'A':
                                if (vid->mode & M_IN_DEV)
-                                       dev_set_ctrl(vid, CTRL_SHARPNESS, 1);
+                                       dev_set_ctrl_rel(vid, CTRL_SHARPNESS, 
1);
                                break;
                        case 'a':
                                if (vid->mode & M_IN_DEV)
-                                       dev_set_ctrl(vid, CTRL_SHARPNESS, -1);
+                                       dev_set_ctrl_rel(vid, CTRL_SHARPNESS, 
-1);
                                break;
                        case 'B':
                                if (vid->mode & M_IN_DEV)
-                                       dev_set_ctrl(vid, CTRL_BRIGHTNESS, 1);
+                                       dev_set_ctrl_rel(vid, CTRL_BRIGHTNESS, 
1);
                                break;
                        case 'b':
                                if (vid->mode & M_IN_DEV)
-                                       dev_set_ctrl(vid, CTRL_BRIGHTNESS, -1);
+                                       dev_set_ctrl_rel(vid, CTRL_BRIGHTNESS, 
-1);
                                break;
                        case 'C':
                                if (vid->mode & M_IN_DEV)
-                                       dev_set_ctrl(vid, CTRL_CONTRAST, 1);
+                                       dev_set_ctrl_rel(vid, CTRL_CONTRAST, 1);
                                break;
                        case 'c':
                                if (vid->mode & M_IN_DEV)
-                                       dev_set_ctrl(vid, CTRL_CONTRAST, -1);
+                                       dev_set_ctrl_rel(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_set_ctrl_rel(vid, CTRL_GAIN, 1);
                                break;
                        case 'g':
                                if (vid->mode & M_IN_DEV)
-                                       dev_set_ctrl(vid, CTRL_GAIN, -1);
+                                       dev_set_ctrl_rel(vid, CTRL_GAIN, -1);
                                break;
                        case 'H':
                                if (vid->mode & M_IN_DEV)
-                                       dev_set_ctrl(vid, CTRL_HUE, 1);
+                                       dev_set_ctrl_rel(vid, CTRL_HUE, 1);
                                break;
                        case 'h':
                                if (vid->mode & M_IN_DEV)
-                                       dev_set_ctrl(vid, CTRL_HUE, -1);
+                                       dev_set_ctrl_rel(vid, CTRL_HUE, -1);
                                break;
                        case 'O':
                                if (!wout && vid->verbose > 0)
@@ -710,11 +715,11 @@ display_event(struct video *vid)
                                break;
                        case 'M':
                                if (vid->mode & M_IN_DEV)
-                                       dev_set_ctrl(vid, CTRL_GAMMA, 1);
+                                       dev_set_ctrl_rel(vid, CTRL_GAMMA, 1);
                                break;
                        case 'm':
                                if (vid->mode & M_IN_DEV)
-                                       dev_set_ctrl(vid, CTRL_GAMMA, -1);
+                                       dev_set_ctrl_rel(vid, CTRL_GAMMA, -1);
                                break;
                        case 'p':
                                hold = !hold;
@@ -728,20 +733,20 @@ display_event(struct video *vid)
                                break;
                        case 'S':
                                if (vid->mode & M_IN_DEV)
-                                       dev_set_ctrl(vid, CTRL_SATURATION, 1);
+                                       dev_set_ctrl_rel(vid, CTRL_SATURATION, 
1);
                                break;
                        case 's':
                                if (vid->mode & M_IN_DEV)
-                                       dev_set_ctrl(vid, CTRL_SATURATION, -1);
+                                       dev_set_ctrl_rel(vid, CTRL_SATURATION, 
-1);
                                break;
                        case 'W':
                                if (vid->mode & M_IN_DEV)
-                                       dev_set_ctrl(vid,
+                                       dev_set_ctrl_rel(vid,
                                            CTRL_WHITE_BALANCE_TEMPERATURE, 10);
                                break;
                        case 'w':
                                if (vid->mode & M_IN_DEV)
-                                       dev_set_ctrl(vid,
+                                       dev_set_ctrl_rel(vid,
                                            CTRL_WHITE_BALANCE_TEMPERATURE, 
-10);
                                break;
                        default:
@@ -1009,30 +1014,28 @@ dev_get_ctrls(struct video *vid)
        return 1;
 }
 
-void
-dev_set_ctrl(struct video *vid, int ctrl, int change)
+int
+dev_set_ctrl_abs(struct video *vid, int ctrl, int val)
 {
        struct dev *d = &vid->dev;
        struct v4l2_control control;
-       int val;
 
        if (ctrl < 0 || ctrl >= CTRL_LAST) {
                warnx("invalid control");
-               return;
+               return -1;
        }
        if (!ctrls[ctrl].supported) {
                warnx("control %s not supported by %s",
                    ctrls[ctrl].name, d->path);
-               return;
+               return -1;
        }
        if (ctrl == CTRL_WHITE_BALANCE_TEMPERATURE) {
                /*
                 * The spec requires auto-white balance to be off before
                 * we can set the white balance temperature.
                 */
-               dev_set_ctrl_auto_white_balance(vid, 0);
+               dev_set_ctrl_auto_white_balance(vid, 0, 0);
        }
-       val = ctrls[ctrl].cur + ctrls[ctrl].step * change;
        if (val > ctrls[ctrl].max)
                val = ctrls[ctrl].max;
        else if (val < ctrls[ctrl].min)
@@ -1041,34 +1044,62 @@ dev_set_ctrl(struct video *vid, int ctrl
        control.value = val;
        if (ioctl(d->fd, VIDIOC_S_CTRL, &control) != 0) {
                warn("VIDIOC_S_CTRL");
-               return;
+               return -1;
        }
        control.id = ctrls[ctrl].id;
        if (ioctl(d->fd, VIDIOC_G_CTRL, &control) != 0) {
                warn("VIDIOC_G_CTRL");
-               return;
+               return -1;
        }
        ctrls[ctrl].cur = control.value;
        if (vid->verbose > 0)
                fprintf(stderr, "%s now %d\n", ctrls[ctrl].name,
                    ctrls[ctrl].cur);
+
+       return 0;
 }
 
 void
-dev_set_ctrl_auto_white_balance(struct video *vid, int toggle)
+dev_set_ctrl_rel(struct video *vid, int ctrl, int change)
+{
+       struct dev *d = &vid->dev;
+       int val;
+
+       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;
+       }
+       val = ctrls[ctrl].cur + ctrls[ctrl].step * change;
+       dev_set_ctrl_abs(vid, ctrl, val);
+}
+
+void
+dev_set_ctrl_auto_white_balance(struct video *vid, int value, int reset)
 {
        struct dev *d = &vid->dev;
        struct v4l2_control control;
 
        control.id = V4L2_CID_AUTO_WHITE_BALANCE;
-       if (ioctl(d->fd, VIDIOC_G_CTRL, &control) != 0)
+       if (ioctl(d->fd, VIDIOC_G_CTRL, &control) != 0) {
                warn("VIDIOC_G_CTRL");
-       if (control.value == toggle)
                return;
+       }
 
-       control.value = toggle;
-       if (ioctl(d->fd, VIDIOC_S_CTRL, &control) != 0)
-               warn("VIDIOC_S_CTRL");
+       if (reset) {
+               if (ioctl(d->fd, VIDIOC_S_CTRL, &control) != 0)
+                       warn("VIDIOC_S_CTRL");
+       } else {
+               if (control.value == value)
+                       return;
+               control.value = value;
+               if (ioctl(d->fd, VIDIOC_S_CTRL, &control) != 0)
+                       warn("VIDIOC_S_CTRL");
+       }
 }
 
 void
@@ -1081,27 +1112,9 @@ dev_reset_ctrls(struct video *vid)
        for (i = 0; i < CTRL_LAST; i++) {
                if (!ctrls[i].supported)
                        continue;
+               dev_set_ctrl_abs(vid, i, ctrls[i].def);
                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);
-               if (i == CTRL_WHITE_BALANCE_TEMPERATURE) {
-                       dev_set_ctrl_auto_white_balance(vid, 1);
+                       dev_set_ctrl_auto_white_balance(vid, 1, 0);
                }
        }
 }
@@ -1171,6 +1184,22 @@ dev_dump_query(struct video *vid)
        dev_dump_info(vid);
 }
 
+void
+dev_dump_query_ctrls(struct video *vid)
+{
+       int i;
+
+       if (!dev_check_caps(vid))
+               return;
+       if (!dev_get_ctrls(vid))
+               return;
+
+       for (i = 0; i < CTRL_LAST; i++) {
+               if (ctrls[i].supported)
+                       fprintf(stderr, "%s=%d\n", ctrls[i].name, ctrls[i].cur);
+       }
+}
+
 int
 dev_init(struct video *vid)
 {
@@ -1222,6 +1251,64 @@ dev_init(struct video *vid)
 }
 
 int
+parse_ctrl(struct video *vid, int argc, char **argv)
+{
+       int i, val_old, val_new;
+       char *p;
+       const char *errstr;
+
+       if (*argv == NULL)
+               return 1;       /* No control arguments found. */
+
+       if (!dev_check_caps(vid))
+               return 0;
+       if (!dev_get_ctrls(vid))
+               return 0;
+
+       for (; argc > 0; argc--, argv++) {
+               p = strchr(*argv, '=');
+
+               /* Display control value. */
+               if (p == NULL) {
+                       for (i = 0; i < CTRL_LAST; i++) {
+                               if (!strcmp(*argv, ctrls[i].name)) {
+                                       fprintf(stderr, "%s: %d\n",
+                                           ctrls[i].name, ctrls[i].cur);
+                                       break;
+                               }
+                       }
+                       if (i == CTRL_LAST)
+                               warnx("%s: unknown control", *argv);    
+                       continue;
+               }
+
+               /* Set control value. */
+               for (i = 0, *p++ = '\0'; i < CTRL_LAST; i++) {
+                       if (strcmp(*argv, ctrls[i].name) != 0)
+                               continue;
+                       if (*p == '\0') {
+                               warnx("%s: no value", *argv);
+                               break;
+                       }
+                       val_new = strtonum(p, -32768, 32768, &errstr);
+                       if (errstr != NULL) {
+                               warnx("%s: %s", *argv, errstr);
+                               return 0;
+                       }
+                       val_old = ctrls[i].cur;
+                       if (dev_set_ctrl_abs(vid, i, val_new) == 0)
+                               fprintf(stderr, "%s: %d -> %d\n",
+                                   ctrls[i].name, val_old, ctrls[i].cur);
+                       break;
+               }
+               if (i == CTRL_LAST)
+                       warnx("%s: unknown control", *argv);
+       }
+
+       return 0;
+}
+
+int
 parse_size(struct video *vid)
 {
        struct xdsp *x = &vid->xdsp;
@@ -1564,6 +1651,13 @@ setup(struct video *vid)
                        return 0;
        }
 
+       /*
+        * Reset the current White Balance Temperature Auto Control value
+        * after the video stream has been started since some cams only
+        * process this control while the video stream is on.
+        */
+       dev_set_ctrl_auto_white_balance(vid, 0, 1);
+
        if (vid->mode & M_OUT_XV)
                net_wm_supported(vid);
 
@@ -1949,7 +2043,7 @@ main(int argc, char *argv[])
        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, "cdgqRva:e:f:i:O:o:r:s:")) != -1) {
                switch (ch) {
                case 'a':
                        x->cur_adap = strtonum(optarg, 0, 4, &errstr);
@@ -1958,6 +2052,14 @@ main(int argc, char *argv[])
                                errs++;
                        }
                        break;
+               case 'c':
+                       vid.mode |= M_QUERY_CTRLS;
+                       vid.mode &= ~M_OUT_XV;
+                       break;
+               case 'd':
+                       vid.mode |= M_RESET;
+                       vid.mode &= ~M_OUT_XV;
+                       break;
                case 'e':
                        vid.enc = find_enc(optarg);
                        if (vid.enc >= ENC_LAST) {
@@ -2043,6 +2145,14 @@ main(int argc, char *argv[])
        argc -= optind;
        argv += optind;
 
+       if (!parse_ctrl(&vid, argc, argv))
+               cleanup(&vid, 0);
+
+       if (vid.mode & M_QUERY_CTRLS) {
+               dev_dump_query_ctrls(&vid);
+               cleanup(&vid, 0);
+       }
+
        if (vid.mode & M_QUERY) {
                if (pledge("stdio rpath wpath video", NULL) == -1)
                        err(1, "pledge");
@@ -2055,6 +2165,11 @@ main(int argc, char *argv[])
 
        if (!setup(&vid))
                cleanup(&vid, 1);
+
+       if (vid.mode & M_RESET) {
+               dev_reset_ctrls(&vid);
+               cleanup(&vid, 0);
+       }
 
        if (vid.mode & M_IN_FILE) {
                if (pledge("stdio rpath", NULL) == -1)

Reply via email to