Introduction
The Video4Linux2 and usbvc are kernel drivers to allow the interaction of applications with webcams. Before diving in I strongly recommend you read the man page for usbvc(7D) and study the associated header files:
- /usr/include/sys/usb/clients/video/usbvc/usbvc.h
- /usr/include/sys/videodev2.h
If you're unfamiliar with this type of low-level API one of the first things that strikes you is a distinct lack of user functions. That's because we're extremely close to kernel land and the way to manage userland <-> kernel interaction is via ioctl(2m) or read/write commands if the device/driver supports it. It's up to you to write the necessary wrapper functions for your application. There are some very good wrappers if you're on Linux or Windows including:
- Intel Open Computer Vision (OpenCV)
- Logitech's libwebcam
We could port these to Solaris but that's no fun, although I'll be porting them at a later date when I get time. You learn much more by writing your own which is why I'm going about it this way. I'll be using the aforementioned libraries as a guide because there's no need to reinvent the wheel.
Getting Started
You can use whatever editor & compiler you feel most comfortable with but I'll be using Sun Studio 12 only because I've never used it before and thought this would be a good time to learn plus it's got some great features and I expect this app/API/Library to grow across multiple files so what better way to manage the project.
If you've read the man page for usbvc(7D) you'll note that one of the first ioctl commands it talks about is VIDIOC_QUERYCAP.
Devices usbvc(7D)
VIDIOC_QUERYCAP
Query the device capabilities. Besides device capabili-
ties, the usbvc driver returns structure v4l2_capability
which includes information on the driver, data bus and
OS kernel. Please note that the "Version" structure
member has no meaning in Solaris and is always set to 1.
Sounds like a good place to start.
If you want my Sun Studio12 project directory you can grab it from here. Download and extract it to your Sun Studio12 project directory then import it using File -> Open Project. You'll need to do a clean build (Shift+F11) to ensure it all works before doing any editing. If you just want the webcam.c & webcam.h files you can download webcam_cap_filesonly.tar.gz but you'll have to build your own Makefile or use the correct compiler options.
Defining the camera_t structure
If you read the videodev2.h and usbvc.h files you'll see that there's lots of features available so the best way to manage all those features is to declare a structure which we can then use throughout our code. This way we only need to declare a single pointer to the structure rather than having multiple variables. It also makes things easier when we add support for multiple capture devices. Within webcam.h I've declared the following structure:
typedef struct camera {
int dev; /* Camera Device Number 0..N */
char *video_dev; /* Video device path, i.e. /dev/videoN - where N is the device number */
int dev_fd; /* File descriptor for /dev/videoN */
int contrast; /* Contrast value */
int brightness; /* Brightness value */
int colour; /* Colour value */
int hue; /* Hue value */
int wb; /* White Balance value */
int frame_number; /* Frame Number 0..N */
struct v4l2_capability vid_cap; /* Video capability bit field as defined in <sys/videodev2.h>*/
struct v4l2_buffer vid_buf; /* Video buffer structure as defined in <sys/videodev2.h> */
} camera_t;
Not all of the above is required for this simple example but eventually this structure will grow as we add more features so I've stuck a few things in here while I remember. The purpose here is to populate the "v4l2_capability vid_cap" structure so we know what we can and can't do with our webcam. It's also a good test to ensure it's plugged in and that the usbvc can read/write to it.
webcam.c/main()
If you look at the main() function within webcam.c it's a fairly straight forward piece of code. All we're doing here is declaring the object camera_t object as a usable pointer (*cam), set a few defaults for our object, verify we can open the webcam device read/write, and if successful we then call our get_camera_cap() function to find out what capabilities our device has.
141 int main(int argc, char** argv) {
142 camera_t cam_object, *cam; /* Declare a pointer to a camera object. See webcam.h */
143 cam = &cam_object;
144
145 /* Set some defaults for the camera device */
146 cam->frame_number = 0;
147 cam->video_dev = "/dev/video0"; /* We're only going to support one device for now.
148 * In future we'll get the device or device number
149 * from the command line arguments. */
150 fprintf(stdout, "Video Device: %s\n", cam->video_dev);
151
152 /* Attempt to open the camera device read/write */
153 if ((cam->dev_fd = open(cam->video_dev, O_RDWR)) == -1)
154 {
155 perror("Cannot open /dev/video0");
156 exit(1);
157 } else {
158 fprintf(stdout, "Succesfully opened /dev/video0 and got file descriptor: %d \n", cam->dev_fd);
159 /* Get and display the camera capabilities */
160 fprintf(stdout, "Ascertaining camera capabilities...\n");
161 get_camera_cap(cam);
162 };
163
164 /* Clean up before exiting */
165 close(cam->dev_fd);
166
167 return (EXIT_SUCCESS);
168 }
Once we've obtained the webcam's capabilities we then close the device and exit.
webcam.c/get_camera_cap()
The man page states that is we send the VIDIOC_QUERYCAP ioctl, and it's successful, we should expect to get a v4l2_capability structure returned. This v4l2_capability structure has been declared within our camera_t structure as vid_cap so we'll access it via cam->vid_cap.
If we look at /usr/include/sys/videodev2.h that structure is defined as:
207 struct v4l2_capability
208 {
209 uint8_t driver[16]; /* i.e. "bttv" */
210 uint8_t card[32]; /* i.e. "Hauppauge WinTV" */
211 uint8_t bus_info[32]; /* "PCI:" + pci_name(pci_dev) */
212 uint32_t version; /* should use KERNEL_VERSION() */
213 uint32_t capabilities; /* Device capabilities */
214 uint32_t reserved[4];
215 };
216
217 /* Values for 'capabilities' field */
218
219 /* Is a video capture device */
220 #define V4L2_CAP_VIDEO_CAPTURE 0x00000001
221
222 /* Is a video output device */
223 #define V4L2_CAP_VIDEO_OUTPUT 0x00000002
224 #define V4L2_CAP_VIDEO_OVERLAY 0x00000004 /* Can do video overlay */
225
226 /* Is a raw VBI capture device */
227 #define V4L2_CAP_VBI_CAPTURE 0x00000010
228
229 /* Is a raw VBI output device */
230 #define V4L2_CAP_VBI_OUTPUT 0x00000020
231 #if 1
232
233 /* Is a sliced VBI capture device */
234 #define V4L2_CAP_SLICED_VBI_CAPTURE 0x00000040
235
236 /* Is a sliced VBI output device */
237 #define V4L2_CAP_SLICED_VBI_OUTPUT 0x00000080
238 #endif
239 #define V4L2_CAP_RDS_CAPTURE 0x00000100 /* RDS data capture */
240
241 #define V4L2_CAP_TUNER 0x00010000 /* has a tuner */
242 #define V4L2_CAP_AUDIO 0x00020000 /* has audio support */
243 #define V4L2_CAP_RADIO 0x00040000 /* is a radio device */
244
245 #define V4L2_CAP_READWRITE 0x01000000 /* read/write systemcalls */
246 #define V4L2_CAP_ASYNCIO 0x02000000 /* async I/O */
247 #define V4L2_CAP_STREAMING 0x04000000 /* streaming I/O ioctls */
The v4l2_capability structure is simple but we do have a bit field to contend with for the device capabilities. Remember, on Solaris 'version' isn't supported so we only expect to get "1" returned. Essentially all the get_camera_cap() function does at this stage is issue the ioctl and if successful print the resulting v4l2_capability structure to stdout. Most of the code within this function handles the capabilities bitfield to determine what flags are set and which are not. Line 38 is where we send the ioctl:
26 /*
27 * get_camera_cap()
28 *
29 * @cam - Pointer to a camera object
30 */
31 void get_camera_cap(camera_t * cam){
32 char *msg;
33
34 /* Send the VIDIOCGCAP ioctl to the camera and see what it returns.
35 * If successful, the ioctl will return a populated v4l2_capability
36 * structure which we deposit within our cam object for later reference.
37 */
38 if(ioctl(cam->dev_fd, VIDIOC_QUERYCAP, &cam->vid_cap) == -1) {
39 fprintf(stdout, "Could not connect to video device (%s).\nPlease check connection.", cam->video_dev);
40 exit(0);
41 } else {
42 fprintf(stdout, " Driver: %s\n", cam->vid_cap.driver);
43 fprintf(stdout, " Card: %s\n", cam->vid_cap.card);
44 fprintf(stdout, " Bus Info: %s\n", cam->vid_cap.bus_info);
45 fprintf(stdout, " Version: %2d\n", cam->vid_cap.version);
46 fprintf(stdout, " Capabilities: \n");
47
48 /*
49 * Test each flag to see what is and is not set
50 * Refer to "D R I V E R C A P A B I L I T I E S" section of videodev2.h (lines 204-247)
51 */
52
53 /* Is this a video capture device? (Yes/No) */
54 if ( cam->vid_cap.capabilities & V4L2_CAP_VIDEO_CAPTURE ){
55 fprintf(stdout, " Video capture support: Yes\n");
56 } else {
57 fprintf(stdout, " Video capture support: No\n");
58 }
59 /* Does the device support video output? (Yes/No) */
60 if ( cam->vid_cap.capabilities & V4L2_CAP_VIDEO_OUTPUT ){
61 fprintf(stdout, " Video output support: Yes\n");
62 } else {
63 fprintf(stdout, " Video output support: No\n");
64 }
65 /* Does the device support video overlay? (Yes/No) */
66 if ( cam->vid_cap.capabilities & V4L2_CAP_VIDEO_OVERLAY ){
67 fprintf(stdout, " Video overlay support: Yes\n");
68 } else {
69 fprintf(stdout, " Video overlay support: No\n");
70 }
71 /* Supports raw VBI capture (Yes/No) */
72 if ( cam->vid_cap.capabilities & V4L2_CAP_VBI_CAPTURE ){
73 fprintf(stdout, " RAW VBI capture support: Yes\n");
74 } else {
75 fprintf(stdout, " RAW VBI capture support: No\n");
76 }
77 /* Supports raw VBI output (Yes/No) */
78 if ( cam->vid_cap.capabilities & V4L2_CAP_VBI_OUTPUT ){
79 fprintf(stdout, " RAW VBI output support: Yes\n");
80 } else {
81 fprintf(stdout, " RAW VBI output support: No\n");
82 }
83 /* Supports sliced VBI capture (Yes/No) */
84 if ( cam->vid_cap.capabilities & V4L2_CAP_SLICED_VBI_CAPTURE ){
85 fprintf(stdout, " Sliced VBI capture support: Yes\n");
86 } else {
87 fprintf(stdout, " Sliced VBI capture support: No\n");
88 }
89 /* Supports slices VBI output (Yes/No) */
90 if ( cam->vid_cap.capabilities & V4L2_CAP_SLICED_VBI_OUTPUT ){
91 fprintf(stdout, " Sliced VBI capture support: Yes\n");
92 } else {
93 fprintf(stdout, " Sliced VBI capture support: No\n");
94 }
95 /* Supports RDS Capture (Yes/No) */
96 if ( cam->vid_cap.capabilities & V4L2_CAP_RDS_CAPTURE ){
97 fprintf(stdout, " RDS capture support: Yes\n");
98 } else {
99 fprintf(stdout, " RDS capture support: No\n");
100 }
101 /* Is this a tuner device? (Yes/No) */
102 if ( cam->vid_cap.capabilities & V4L2_CAP_TUNER ){
103 fprintf(stdout, " Device has a tuner: Yes\n");
104 } else {
105 fprintf(stdout, " Device has a tuner: No\n");
106 }
107 /* Device has audio support (Yes/No) */
108 if ( cam->vid_cap.capabilities & V4L2_CAP_AUDIO ){
109 fprintf(stdout, " Audio support: Yes\n");
110 } else {
111 fprintf(stdout, " Audio support: No\n");
112 }
113 /* Is a radio device (Yes/No) */
114 if ( cam->vid_cap.capabilities & V4L2_CAP_RADIO ){
115 fprintf(stdout, " Device has radio support: Yes\n");
116 } else {
117 fprintf(stdout, " Device has radio support: No\n");
118 }
119 /* Supports read/write system calls (Yes/No) */
120 if ( cam->vid_cap.capabilities & V4L2_CAP_READWRITE ){
121 fprintf(stdout, " Supports read/write system calls: Yes\n");
122 } else {
123 fprintf(stdout, " Supports read/write system calls: No\n");
124 }
125 /* Supports asynchronous I/O (Yes/No) */
126 if ( cam->vid_cap.capabilities & V4L2_CAP_ASYNCIO ){
127 fprintf(stdout, " Supports async i/o: Yes\n");
128 } else {
129 fprintf(stdout, " Supports async i/o: No\n");
130 }
131 /* Supports streaming */
132 if ( cam->vid_cap.capabilities & V4L2_CAP_STREAMING ){
133 fprintf(stdout, " Supports streaming: Yes\n");
134 } else {
135 fprintf(stdout, " Supports streaming: No\n");
136 }
137
138 }
139 } /* End of get_camera_cap() function */
When I run this on my Toshiba Tecra M5 with a USB Logitech Quickcam Pro 5000 I get:
Video Device: /dev/video0
Succesfully opened /dev/video0 and got file descriptor: 3
Ascertaining camera capabilities...
Driver: usbvc
Card: Generic USB videoclass device
Bus Info: usb
Version: 1
Capabilities:
Video capture support: Yes
Video output support: No
Video overlay support: No
RAW VBI capture support: No
RAW VBI output support: No
Sliced VBI capture support: No
Sliced VBI capture support: No
RDS capture support: No
Device has a tuner: No
Audio support: No
Device has radio support: No
Supports read/write system calls: Yes
Supports async i/o: No
Supports streaming: Yes
This is what you'd expect to see from just a webcam. Now that we know what the driver will let us do with the device we can extend this code to do something useful, like grab frames. I'll cover this in the next installment.