Following on from the previous blog entry where we got started with v4l2 & usbvc we'll now expand the functionality so we can query the device & driver to obtain the video controls (brightness, contrast, hue, etc).

The man page for usbvc(7D) states the following for the ioctl we're interested in

     VIDIOC_QUERYCTRL

         Query the device and driver  for  supported  video  con-
         trols.  Currently, the usbvc driver supports the bright-
         ness, contrast, saturation, hue, and  gamma  video  con-
         trols.

Unfortunately it doesn't elaborate as to what we expect happen when we send this ioctl.  For that we must go and read the documentation:

"To query the attributes of a control applications set the id field of a struct v4l2_queryctrl and call the VIDIOC_QUERYCTRL ioctl with a pointer to this structure. The driver fills the rest of the structure or returns an EINVAL error code when the id is invalid.

It is possible to enumerate controls by calling VIDIOC_QUERYCTRL with successive id values starting from V4L2_CID_BASE up to and exclusive V4L2_CID_BASE_LASTP1. Drivers may return EINVAL if a control in this range is not supported. Further applications can enumerate private controls, which are not defined in this specification, by starting at V4L2_CID_PRIVATE_BASE and incrementing id until the driver returns EINVAL.

In both cases, when the driver sets the V4L2_CTRL_FLAG_DISABLED flag in the flags field this control is permanently disabled and should be ignored by the application.[1]

When the application ORs id with V4L2_CTRL_FLAG_NEXT_CTRL the driver returns the next supported control, or EINVAL if there is none. Drivers which do not support this flag yet always return EINVAL."

Note that in Solaris, we do not implement the V4L2_CID_BASE_LASTP1, instead we have V4L2_CID_LASTP1.  Another subtle difference between the Linux & Solaris implementations.
 

The documentation asks us to iterate over the following id's which will auto-populate the v4l2_queryctrl structure.  I will only implement the first method outlined in the documentation.  For completeness however here are all the options defined within <sys/videodev2.h>:

    871 /*  Query flag, to be ORed with the control ID */
    872 #define    V4L2_CTRL_FLAG_NEXT_CTRL    0x80000000
    873
    874 /*  User-class control IDs defined by V4L2 */
    875 #define    V4L2_CID_BASE            (V4L2_CTRL_CLASS_USER | 0x900)
    876 #define    V4L2_CID_USER_BASE        V4L2_CID_BASE
    877 /*  IDs reserved for driver specific controls */
    878 #define    V4L2_CID_PRIVATE_BASE        0x08000000
    879
    880 #define    V4L2_CID_USER_CLASS        (V4L2_CTRL_CLASS_USER | 1)
    881 #define    V4L2_CID_BRIGHTNESS        (V4L2_CID_BASE+0)
    882 #define    V4L2_CID_CONTRAST        (V4L2_CID_BASE+1)
    883 #define    V4L2_CID_SATURATION        (V4L2_CID_BASE+2)
    884 #define    V4L2_CID_HUE            (V4L2_CID_BASE+3)
    885 #define    V4L2_CID_AUDIO_VOLUME        (V4L2_CID_BASE+5)
    886 #define    V4L2_CID_AUDIO_BALANCE        (V4L2_CID_BASE+6)
    887 #define    V4L2_CID_AUDIO_BASS        (V4L2_CID_BASE+7)
    888 #define    V4L2_CID_AUDIO_TREBLE        (V4L2_CID_BASE+8)
    889 #define    V4L2_CID_AUDIO_MUTE        (V4L2_CID_BASE+9)
    890 #define    V4L2_CID_AUDIO_LOUDNESS        (V4L2_CID_BASE+10)
    891 #define    V4L2_CID_BLACK_LEVEL        (V4L2_CID_BASE+11)
    892 #define    V4L2_CID_AUTO_WHITE_BALANCE    (V4L2_CID_BASE+12)
    893 #define    V4L2_CID_DO_WHITE_BALANCE    (V4L2_CID_BASE+13)
    894 #define    V4L2_CID_RED_BALANCE        (V4L2_CID_BASE+14)
    895 #define    V4L2_CID_BLUE_BALANCE        (V4L2_CID_BASE+15)
    896 #define    V4L2_CID_GAMMA            (V4L2_CID_BASE+16)
    897 #define    V4L2_CID_WHITENESS        (V4L2_CID_GAMMA) /* ? Not sure */
    898 #define    V4L2_CID_EXPOSURE        (V4L2_CID_BASE+17)
    899 #define    V4L2_CID_AUTOGAIN        (V4L2_CID_BASE+18)
    900 #define    V4L2_CID_GAIN            (V4L2_CID_BASE+19)
    901 #define    V4L2_CID_HFLIP            (V4L2_CID_BASE+20)
    902 #define    V4L2_CID_VFLIP            (V4L2_CID_BASE+21)
    903 #define    V4L2_CID_HCENTER        (V4L2_CID_BASE+22)
    904 #define    V4L2_CID_VCENTER        (V4L2_CID_BASE+23)
    905 #define    V4L2_CID_LASTP1            (V4L2_CID_BASE+24) /* last CID + 1 */

Here's the v4l2_queryctrl structure which will get populated.  Again, it is defined in <sys/videodev2.h>:

    840 /*  Used in the VIDIOC_QUERYCTRL ioctl for querying controls */
    841 struct v4l2_queryctrl
    842 {
    843     uint32_t        id;
    844     enum            v4l2_ctrl_type type;
    845     char            name[32];     /* Whatever */
    846     int32_t         minimum; /* Note signedness */
    847     int32_t         maximum;
    848     int32_t         step;
    849     int32_t         default_value;
    850     uint32_t        flags;
    851     uint32_t        reserved[2];
    852 };

The enumerated v4l2_ctrl_type structure is defined as:

    134 enum v4l2_ctrl_type {
    135     V4L2_CTRL_TYPE_INTEGER        = 1,
    136     V4L2_CTRL_TYPE_BOOLEAN        = 2,
    137     V4L2_CTRL_TYPE_MENU         = 3,
    138     V4L2_CTRL_TYPE_BUTTON         = 4,
    139     V4L2_CTRL_TYPE_INTEGER64     = 5,
    140     V4L2_CTRL_TYPE_CTRL_CLASS     = 6
    141 };
    
The documentation goes on to say:   

"Additional information is required for menu controls, the name of menu items. To query them applications set the id and index fields of struct v4l2_querymenu and call the VIDIOC_QUERYMENU ioctl with a pointer to this structure. The driver fills the rest of the structure or returns an EINVAL error code when the id or index is invalid. Menu items are enumerated by calling VIDIOC_QUERYMENU with successive index values from struct v4l2_queryctrl minimum (0) to maximum, inclusive. " 
   
In other words if the v4l2_queryctrl.type is of type V4L2_CTRL_TYPE_MENU then we need to do some more processing by issuing VIDIOC_QUERYMENU ioctl which will populate the v4l2_querymenu structure, defined as:

    854 /*  Used in the VIDIOC_QUERYMENU ioctl for querying menu items */
    855 struct v4l2_querymenu
    856 {
    857     uint32_t        id;
    858     uint32_t        index;
    859     uint8_t        name[32];    /* Whatever */
    860     uint32_t        reserved;
    861 };
   
With a better understanding of what we need to do, we can implement the function to enumerate the controls.  Within webcam.c the enum_video_ctrls_and_menus(camera_t * cam) function has been implemented.  Note that all variables are local, ie. there's nothing added to our camera_t structure.  If we decide later to keep these values then we could update the structure, but for now this suits the purpose.

/*
* enum_video_ctrls_and_menus
*
*   @cam - Pointer to a camera object
*
*   Enumerate controls and menu control items
 *     VIDIOC_QUERYCTRL & VIDIOC_QUERYMENU ioctls. 
 */
void enum_video_ctrls_and_menus(camera_t * cam){
/*
  * To query the attributes of a control applications set the id field of a struct
  * v4l2_queryctrl and call the VIDIOC_QUERYCTRL ioctl with a pointer to this
  * structure. The driver fills the rest of the structure or returns an EINVAL
  * error code when the id is invalid.
  *
  * To enumerate controls call VIDIOC_QUERYCTRL with successive
  * id values starting from V4L2_CID_BASE up to and exclusive V4L2_CID_LASTP1,
  * or starting from V4L2_CID_PRIVATE_BASE until the driver returns EINVAL.
  */ 
  struct v4l2_queryctrl queryctrl;    /* Query Control structure as defined in <sys/videodev2.h> */

struct v4l2_querymenu querymenu;    /* Query Menu Structure as defined in <sys/videodev2.h> */

  fprintf(stdout,"Discovering controls:\n");
 

  memset (&queryctrl, 0, sizeof (queryctrl));  
  for (queryctrl.id = V4L2_CID_BASE; queryctrl.id < V4L2_CID_LASTP1; queryctrl.id++) {
      if ( ioctl (cam->dev_fd, VIDIOC_QUERYCTRL, &queryctrl) == 0 ) {
          /* Check to see if this control is permanently disabled and should be ignored by the application */
          if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
              continue;
          /* We got a video control back */
          fprintf(stdout,"VIDIOC_QUERYCTRL(V4L2_CID_BASE+%d)\n", queryctrl.id-V4L2_CID_BASE);
          fprintf(stdout,"   id: %d\n", queryctrl.id);
          switch (queryctrl.type){
              case V4L2_CTRL_TYPE_INTEGER:
                  fprintf(stdout, "   type: INTEGER\n");
                  break;
              case V4L2_CTRL_TYPE_BOOLEAN:
                  fprintf(stdout, "   type: BOOLEAN\n");
                  break;
              case V4L2_CTRL_TYPE_MENU:
                  fprintf(stdout, "   type: MENU\n");
                  /* Additional information is required for menu controls, the name of menu items.
                   * To query them applications set the id and index fields of struct v4l2_querymenu
                   *   and call the VIDIOC_QUERYMENU ioctl with a pointer to this structure. The driver
                   *   fills the rest of the structure or returns an EINVAL error code when the id or
                   *   index is invalid. Menu items are enumerated by calling VIDIOC_QUERYMENU with
                   *   successive index values from struct v4l2_queryctrl minimum (0) to maximum, inclusive.
                   */
                  querymenu.id = queryctrl.id;
                  for (querymenu.index = queryctrl.minimum; querymenu.index < queryctrl.maximum; querymenu.index++){
                      fprintf(stdout, "      menu id: %d\n", querymenu.index);
                      fprintf(stdout, "      menu name: %s\n", querymenu.name);
                  }
                  break;
              case V4L2_CTRL_TYPE_BUTTON:
                  fprintf(stdout, "   type: BUTTON\n");
                  break;
          }
          fprintf(stdout,"   name: %s\n", queryctrl.name);
          fprintf(stdout,"   minimum: %d\n", queryctrl.minimum);
          fprintf(stdout,"   maximum: %d\n", queryctrl.maximum);
          fprintf(stdout,"   step: %d\n", queryctrl.step);
          fprintf(stdout,"   default_value: %d\n", queryctrl.default_value);
          fprintf(stdout,"   flags: %d\n", queryctrl.flags);

      } else {

             if (errno == EINVAL)
                 continue;

             perror("VIDIOC_QUERYCTRL");
             break;
      }

   }     

} /* End of enum_video_ctrls_and_menus() */

Using my Toshiba Tecra M5 & Logitech Quickcam Pro 5000 camera I get the following output when running the enum_video_ctrls_and_menus() function:

Discovering controls:
VIDIOC_QUERYCTRL(V4L2_CID_BASE+0)
   id: 9963776
   type: INTEGER
   name: Brightness
   minimum: 0
   maximum: 255
   step: 1
   default_value: 127
   flags: 0
VIDIOC_QUERYCTRL(V4L2_CID_BASE+1)
   id: 9963777
   type: INTEGER
   name: Contrast
   minimum: 0
   maximum: 255
   step: 1
   default_value: 32
   flags: 0
VIDIOC_QUERYCTRL(V4L2_CID_BASE+2)
   id: 9963778
   type: INTEGER
   name: Saturation
   minimum: 0
   maximum: 255
   step: 1
   default_value: 32
   flags: 0
If you want the Sun Studio 12 Project, which includes all the source code, you can download it here.


 
Comments:

Post a Comment:
  • HTML Syntax: NOT allowed

This blog copyright 2009 by Steve Scargall