uvc_cam.cpp
Go to the documentation of this file.
1 /* This file is from the uvc_cam package by Morgan Quigley */
2 #include <cstring>
3 #include <string>
4 #include <cstdio>
5 #include <stdexcept>
6 #include <dirent.h>
7 #include <sys/stat.h>
8 #include <sys/ioctl.h>
9 #include <sys/mman.h>
10 #include <fcntl.h>
11 #include <errno.h>
12 #include <unistd.h>
13 #include <ros/console.h>
14 #include <err.h>
15 
16 #include "uvc_cam/uvc_cam.h"
17 
18 using std::string;
19 using namespace uvc_cam;
20 
21 Cam::Cam(const char *_device, mode_t _mode, int _width, int _height, int _fps)
22 : mode(_mode), device(_device),
23  motion_threshold_luminance(100), motion_threshold_count(-1),
24  width(_width), height(_height), fps(_fps), rgb_frame(NULL)
25 {
26  printf("opening %s\n", _device);
27  if ((fd = open(_device, O_RDWR)) == -1)
28  throw std::runtime_error("couldn't open " + device);
29  memset(&fmt, 0, sizeof(v4l2_format));
30  memset(&cap, 0, sizeof(v4l2_capability));
31  if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0)
32  throw std::runtime_error("couldn't query " + device);
33  if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
34  throw std::runtime_error(device + " does not support capture");
35  if (!(cap.capabilities & V4L2_CAP_STREAMING))
36  throw std::runtime_error(device + " does not support streaming");
37  // enumerate formats
38  v4l2_fmtdesc f;
39  memset(&f, 0, sizeof(f));
40  f.index = 0;
41  f.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
42  int ret;
43  while ((ret = ioctl(fd, VIDIOC_ENUM_FMT, &f)) == 0)
44  {
45  printf("pixfmt %d = '%4s' desc = '%s'\n",
46  f.index++, (char *)&f.pixelformat, f.description);
47  // enumerate frame sizes
48  v4l2_frmsizeenum fsize;
49  fsize.index = 0;
50  fsize.pixel_format = f.pixelformat;
51  while ((ret = ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &fsize)) == 0)
52  {
53  fsize.index++;
54  if (fsize.type == V4L2_FRMSIZE_TYPE_DISCRETE)
55  {
56  printf(" discrete: %ux%u: ",
57  fsize.discrete.width, fsize.discrete.height);
58  // enumerate frame rates
59  v4l2_frmivalenum fival;
60  fival.index = 0;
61  fival.pixel_format = f.pixelformat;
62  fival.width = fsize.discrete.width;
63  fival.height = fsize.discrete.height;
64  while ((ret = ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &fival)) == 0)
65  {
66  fival.index++;
67  if (fival.type == V4L2_FRMIVAL_TYPE_DISCRETE)
68  {
69  printf("%u/%u ",
70  fival.discrete.numerator, fival.discrete.denominator);
71  }
72  else
73  printf("I only handle discrete frame intervals...\n");
74  }
75  printf("\n");
76  }
77  else if (fsize.type == V4L2_FRMSIZE_TYPE_CONTINUOUS)
78  {
79  printf(" continuous: %ux%u to %ux%u\n",
80  fsize.stepwise.min_width, fsize.stepwise.min_height,
81  fsize.stepwise.max_width, fsize.stepwise.max_height);
82  }
83  else if (fsize.type == V4L2_FRMSIZE_TYPE_STEPWISE)
84  {
85  printf(" stepwise: %ux%u to %ux%u step %ux%u\n",
86  fsize.stepwise.min_width, fsize.stepwise.min_height,
87  fsize.stepwise.max_width, fsize.stepwise.max_height,
88  fsize.stepwise.step_width, fsize.stepwise.step_height);
89  }
90  else
91  {
92  printf(" fsize.type not supported: %d\n", fsize.type);
93  }
94  }
95  }
96  if (errno != EINVAL)
97  throw std::runtime_error("error enumerating frame formats");
98  fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
99  fmt.fmt.pix.width = width;
100  fmt.fmt.pix.height = height;
101  if (mode == MODE_RGB || mode == MODE_YUYV) // we'll convert later
102  fmt.fmt.pix.pixelformat = 'Y' | ('U' << 8) | ('Y' << 16) | ('V' << 24);
103  else
104  fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
105  fmt.fmt.pix.field = V4L2_FIELD_ANY;
106  if ((ret = ioctl(fd, VIDIOC_S_FMT, &fmt)) < 0)
107  throw std::runtime_error("couldn't set format");
108  if (fmt.fmt.pix.width != width || fmt.fmt.pix.height != height)
109  throw std::runtime_error("pixel format unavailable");
110  streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
111  streamparm.parm.capture.timeperframe.numerator = 1;
112  streamparm.parm.capture.timeperframe.denominator = fps;
113  ret = ioctl(fd, VIDIOC_S_PARM, &streamparm);
114  if (ret < 0)
115  if (errno == ENOTTY) {
116  ROS_WARN("VIDIOC_S_PARM not spported on this v4l2 device, framerate not set");
117  } else {
118  throw std::runtime_error("unable to set framerate");
119  }
120  v4l2_queryctrl queryctrl;
121  memset(&queryctrl, 0, sizeof(queryctrl));
122  uint32_t i = V4L2_CID_BASE;
123  while (i != V4L2_CID_LAST_EXTCTR)
124  {
125  queryctrl.id = i;
126  if ((ret = ioctl(fd, VIDIOC_QUERYCTRL, &queryctrl)) == 0 &&
127  !(queryctrl.flags & V4L2_CTRL_FLAG_DISABLED))
128  {
129  const char *ctrl_type = NULL;
130  if (queryctrl.type == V4L2_CTRL_TYPE_INTEGER)
131  ctrl_type = "int";
132  else if (queryctrl.type == V4L2_CTRL_TYPE_BOOLEAN)
133  ctrl_type = "bool";
134  else if (queryctrl.type == V4L2_CTRL_TYPE_BUTTON)
135  ctrl_type = "button";
136  else if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
137  ctrl_type = "menu";
138  printf(" %s (%s, %d, id = %x): %d to %d (%d)\n",
139  ctrl_type,
140  queryctrl.name, queryctrl.flags, queryctrl.id,
141  queryctrl.minimum, queryctrl.maximum, queryctrl.step);
142  if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
143  {
144  v4l2_querymenu querymenu;
145  memset(&querymenu, 0, sizeof(querymenu));
146  querymenu.id = queryctrl.id;
147  querymenu.index = 0;
148  while (ioctl(fd, VIDIOC_QUERYMENU, &querymenu) == 0)
149  {
150  printf(" %d: %s\n", querymenu.index, querymenu.name);
151  querymenu.index++;
152  }
153  }
154 
155  }
156  // else if (errno != EINVAL)
157  // throw std::runtime_error("couldn't query control");
158  i++;
159  if (i == V4L2_CID_LAST_NEW)
161  else if (i == V4L2_CID_CAMERA_CLASS_LAST)
163  else if (i == V4L2_CID_PRIVATE_LAST)
165  }
166 
167 /*
168  v4l2_jpegcompression v4l2_jpeg;
169  if (ioctl(fd, VIDIOC_G_JPEGCOMP, &v4l2_jpeg) < 0)
170  throw std::runtime_error("no jpeg compression iface exposed");
171  printf("jpeg quality: %d\n", v4l2_jpeg.quality);
172 */
173 
174  memset(&rb, 0, sizeof(rb));
175  rb.count = NUM_BUFFER;
176  rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
177  rb.memory = V4L2_MEMORY_MMAP;
178  if (ioctl(fd, VIDIOC_REQBUFS, &rb) < 0)
179  throw std::runtime_error("unable to allocate buffers");
180  if (NUM_BUFFER != rb.count)
181  ROS_WARN("asked for %d buffers, got %d\r\n", NUM_BUFFER, rb.count);
182  for (unsigned i = 0; i < NUM_BUFFER; i++)
183  {
184  memset(&buf, 0, sizeof(buf));
185  buf.index = i;
186  buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
187  buf.flags = V4L2_BUF_FLAG_TIMECODE;
188  buf.timecode = timecode;
189  buf.timestamp.tv_sec = 0;
190  buf.timestamp.tv_usec = 0;
191  buf.memory = V4L2_MEMORY_MMAP;
192  if (ioctl(fd, VIDIOC_QUERYBUF, &buf) < 0)
193  throw std::runtime_error("unable to query buffer");
194  if (buf.length <= 0)
195  throw std::runtime_error("buffer length is bogus");
196  mem[i] = mmap(0, buf.length, PROT_READ|PROT_WRITE, MAP_SHARED, fd, buf.m.offset);
197  //printf("buf length = %d at %x\n", buf.length, mem[i]);
198  if (mem[i] == MAP_FAILED)
199  throw std::runtime_error("couldn't map buffer");
200  }
201  buf_length = buf.length;
202  for (unsigned i = 0; i < NUM_BUFFER; i++)
203  {
204  memset(&buf, 0, sizeof(buf));
205  buf.index = i;
206  buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
207  buf.flags = V4L2_BUF_FLAG_TIMECODE;
208  buf.timecode = timecode;
209  buf.timestamp.tv_sec = 0;
210  buf.timestamp.tv_usec = 0;
211  buf.memory = V4L2_MEMORY_MMAP;
212  if (ioctl(fd, VIDIOC_QBUF, &buf) < 0)
213  throw std::runtime_error("unable to queue buffer");
214  }
215  int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
216  if (ioctl(fd, VIDIOC_STREAMON, &type) < 0)
217  throw std::runtime_error("unable to start capture");
218  rgb_frame = new unsigned char[width * height * 3];
219  last_yuv_frame = new unsigned char[width * height * 2];
220 }
221 
223 {
224  // stop stream
225  int type = V4L2_BUF_TYPE_VIDEO_CAPTURE, ret;
226  if ((ret = ioctl(fd, VIDIOC_STREAMOFF, &type)) < 0)
227  perror("VIDIOC_STREAMOFF");
228  for (unsigned i = 0; i < NUM_BUFFER; i++)
229  if (munmap(mem[i], buf_length) < 0)
230  perror("failed to unmap buffer");
231  close(fd);
232  if (rgb_frame)
233  {
234  delete[] rgb_frame;
235  delete[] last_yuv_frame;
236  }
237  last_yuv_frame = rgb_frame = NULL;
238 }
239 
241 {
242  string v4l_path = "/sys/class/video4linux";
243  DIR *d = opendir(v4l_path.c_str());
244  if (!d)
245  throw std::runtime_error("couldn't open " + v4l_path);
246  struct dirent *ent, *ent2, *ent3;
247  int fd, ret;
248  struct v4l2_capability v4l2_cap;
249  while ((ent = readdir(d)) != NULL)
250  {
251  if (strncmp(ent->d_name, "video", 5))
252  continue; // ignore anything not starting with "video"
253  string dev_name = string("/dev/") + string(ent->d_name);
254  printf("enumerating %s ...\n", dev_name.c_str());
255  if ((fd = open(dev_name.c_str(), O_RDWR)) == -1)
256  throw std::runtime_error("couldn't open " + dev_name + " perhaps the " +
257  "permissions are not set correctly?");
258  if ((ret = ioctl(fd, VIDIOC_QUERYCAP, &v4l2_cap)) < 0)
259  throw std::runtime_error("couldn't query " + dev_name);
260  printf("name = [%s]\n", v4l2_cap.card);
261  printf("driver = [%s]\n", v4l2_cap.driver);
262  printf("location = [%s]\n", v4l2_cap.bus_info);
263  close(fd);
264  string v4l_dev_path = v4l_path + string("/") + string(ent->d_name) +
265  string("/device");
266  // my kernel is using /sys/class/video4linux/videoN/device/inputX/id
267  DIR *d2 = opendir(v4l_dev_path.c_str());
268  if (!d2)
269  throw std::runtime_error("couldn't open " + v4l_dev_path);
270  string input_dir;
271  while ((ent2 = readdir(d2)) != NULL)
272  {
273  if (strncmp(ent2->d_name, "input", 5))
274  continue; // ignore anything not beginning with "input"
275 
276  DIR *input = opendir((v4l_dev_path + string("/") + string(ent2->d_name)).c_str());
277  bool output_set = false;
278  while ((ent3 = readdir(input)) != NULL)
279  {
280  if (!strncmp(ent3->d_name, "input", 5))
281  {
282  input_dir = (string("input/") + string(ent3->d_name )).c_str();
283  output_set = true;
284  break;
285  }
286  }
287  if (!output_set)
288  input_dir = ent2->d_name;
289  break;
290  }
291  closedir(d2);
292  if (!input_dir.length())
293  throw std::runtime_error("couldn't find input dir in " + v4l_dev_path);
294  string vid_fname = v4l_dev_path + string("/") + input_dir +
295  string("/id/vendor");
296  string pid_fname = v4l_dev_path + string("/") + input_dir +
297  string("/id/product");
298  string ver_fname = v4l_dev_path + string("/") + input_dir +
299  string("/id/version");
300  char vid[5], pid[5], ver[5];
301  FILE *vid_fp = fopen(vid_fname.c_str(), "r");
302  if (!vid_fp)
303  throw std::runtime_error("couldn't open " + vid_fname);
304  if (!fgets(vid, sizeof(vid), vid_fp))
305  throw std::runtime_error("couldn't read VID from " + vid_fname);
306  fclose(vid_fp);
307  vid[4] = 0;
308  printf("vid = [%s]\n", vid);
309  FILE *pid_fp = fopen(pid_fname.c_str(), "r");
310  if (!pid_fp)
311  throw std::runtime_error("couldn't open " + pid_fname);
312  if (!fgets(pid, sizeof(pid), pid_fp))
313  throw std::runtime_error("couldn't read PID from " + pid_fname);
314  fclose(pid_fp);
315  printf("pid = [%s]\n", pid);
316  FILE *ver_fp = fopen(ver_fname.c_str(), "r");
317  if (!ver_fp)
318  throw std::runtime_error("couldn't open " + ver_fname);
319  if (!fgets(ver, sizeof(ver), ver_fp))
320  throw std::runtime_error("couldn't read version from " + ver_fname);
321  fclose(ver_fp);
322  printf("ver = [%s]\n", ver);
323  }
324  closedir(d);
325 }
326 
327 // saturate input into [0, 255]
328 inline unsigned char sat(float f)
329 {
330  return (unsigned char)( f >= 255 ? 255 : (f < 0 ? 0 : f));
331 }
332 
333 int Cam::grab(unsigned char **frame, uint32_t &bytes_used)
334 {
335  *frame = NULL;
336  int ret = 0;
337  fd_set rdset;
338  timeval timeout;
339  FD_ZERO(&rdset);
340  FD_SET(fd, &rdset);
341  timeout.tv_sec = 1;
342  timeout.tv_usec = 0;
343  bytes_used = 0;
344  ret = select(fd + 1, &rdset, NULL, NULL, &timeout);
345  if (ret == 0)
346  {
347  printf("select timeout in grab\n");
348  return -1;
349  }
350  else if (ret < 0)
351  {
352  perror("couldn't grab image");
353  return -1;
354  }
355  if (!FD_ISSET(fd, &rdset))
356  return -1;
357  memset(&buf, 0, sizeof(buf));
358  buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
359  buf.memory = V4L2_MEMORY_MMAP;
360  if (ioctl(fd, VIDIOC_DQBUF, &buf) < 0)
361  throw std::runtime_error("couldn't dequeue buffer");
362  bytes_used = buf.bytesused;
363  if (mode == MODE_RGB)
364  {
365  int num_pixels_different = 0; // just look at the Y channel
366  unsigned char *pyuv = (unsigned char *)mem[buf.index];
367  // yuyv is 2 bytes per pixel. step through every pixel pair.
368  unsigned char *prgb = rgb_frame;
369  unsigned char *pyuv_last = last_yuv_frame;
370  for (unsigned i = 0; i < width * height * 2; i += 4)
371  {
372  *prgb++ = sat(pyuv[i]+1.402f *(pyuv[i+3]-128));
373  *prgb++ = sat(pyuv[i]-0.34414f*(pyuv[i+1]-128)-0.71414f*(pyuv[i+3]-128));
374  *prgb++ = sat(pyuv[i]+1.772f *(pyuv[i+1]-128));
375  *prgb++ = sat(pyuv[i+2]+1.402f*(pyuv[i+3]-128));
376  *prgb++ = sat(pyuv[i+2]-0.34414f*(pyuv[i+1]-128)-0.71414f*(pyuv[i+3]-128));
377  *prgb++ = sat(pyuv[i+2]+1.772f*(pyuv[i+1]-128));
378  if ((int)pyuv[i] - (int)pyuv_last[i] > motion_threshold_luminance ||
379  (int)pyuv_last[i] - (int)pyuv[i] > motion_threshold_luminance)
380  num_pixels_different++;
381  if ((int)pyuv[i+2] - (int)pyuv_last[i+2] > motion_threshold_luminance ||
382  (int)pyuv_last[i+2] - (int)pyuv[i+2] > motion_threshold_luminance)
383  num_pixels_different++;
384 
385  // this gives bgr images...
386  /*
387  *prgb++ = sat(pyuv[i]+1.772f *(pyuv[i+1]-128));
388  *prgb++ = sat(pyuv[i]-0.34414f*(pyuv[i+1]-128)-0.71414f*(pyuv[i+3]-128));
389  *prgb++ = sat(pyuv[i]+1.402f *(pyuv[i+3]-128));
390  *prgb++ = sat(pyuv[i+2]+1.772f*(pyuv[i+1]-128));
391  *prgb++ = sat(pyuv[i+2]-0.34414f*(pyuv[i+1]-128)-0.71414f*(pyuv[i+3]-128));
392  *prgb++ = sat(pyuv[i+2]+1.402f*(pyuv[i+3]-128));
393  */
394  }
395  memcpy(last_yuv_frame, pyuv, width * height * 2);
396  if (num_pixels_different > motion_threshold_count) // default: always true
397  *frame = rgb_frame;
398  else
399  {
400  *frame = NULL; // not enough luminance change
401  release(buf.index); // let go of this image
402  }
403  }
404  else if (mode == MODE_YUYV)
405  {
406  *frame = (uint8_t *)mem[buf.index];
407  }
408  else // mode == MODE_JPEG
409  {
410  //if (bytes_used > 100)
411  *frame = (unsigned char *)mem[buf.index];
412  }
413  return buf.index;
414 }
415 
416 void Cam::release(unsigned buf_idx)
417 {
418  if (buf_idx < NUM_BUFFER)
419  if (ioctl(fd, VIDIOC_QBUF, &buf) < 0)
420  throw std::runtime_error("couldn't requeue buffer");
421 }
422 
423 bool
424 Cam::v4l2_query(int id, const std::string& name)
425 {
426  if (fd < 0) {
427  printf("Capture file not open: Can't %s\n", name.c_str());
428  return false;
429  }
430 
431  struct v4l2_queryctrl queryctrl;
432  memset(&queryctrl, 0, sizeof(queryctrl));
433  queryctrl.id = id;
434  if (v4l2_ioctl(fd, VIDIOC_QUERYCTRL, &queryctrl) < 0) {
435  if (errno == EINVAL) {
436  //error(FLF("Setting %s is not supported\n"), name.c_str());
437  } else {
438  ROS_WARN("Failed query %s", name.c_str());
439  }
440  return false;
441  }
442 
443  return true;
444 }
445 
446 bool
447 Cam::set_v4l2_control(int id, int value, const std::string& name)
448 {
449  if (fd < 0) {
450  printf("Capture file not open: Can't %s\n", name.c_str());
451  return false;
452  }
453 
454  if (!v4l2_query(id, name)) {
455  printf("Setting %s is not supported\n", name.c_str());
456  return false;
457  }
458 
459  struct v4l2_control control;
460  memset(&control, 0, sizeof(control));
461  control.id = id;
462  control.value = value;
463  if (v4l2_ioctl(fd, VIDIOC_S_CTRL, &control) < 0) {
464  ROS_WARN("Failed to change %s to %d", name.c_str(), control.value);
465  return false;
466  }
467 
468  return true;
469 }
470 
471 void Cam::set_control(uint32_t id, int val)
472 {
473  v4l2_control c;
474  c.id = id;
475 
476  if (ioctl(fd, VIDIOC_G_CTRL, &c) == 0)
477  {
478  printf("current value of %d is %d\n", id, c.value);
479  /*
480  perror("unable to get control");
481  throw std::runtime_error("unable to get control");
482  */
483  }
484  c.value = val;
485  if (ioctl(fd, VIDIOC_S_CTRL, &c) < 0)
486  {
487  perror("unable to set control");
488  throw std::runtime_error("unable to set control");
489  }
490 }
491 
492 void Cam::set_motion_thresholds(int lum, int count)
493 {
495  motion_threshold_count = count;
496 }
497 
unsigned char * rgb_frame
Definition: uvc_cam.h:83
unsigned char sat(float f)
Definition: uvc_cam.cpp:328
CSU32 V4L2_CID_LAST_EXTCTR
Definition: uvc_cam.h:145
void release(unsigned buf_idx)
Definition: uvc_cam.cpp:416
std::string device
Definition: uvc_cam.h:71
static void enumerate()
Definition: uvc_cam.cpp:240
f
CSU32 V4L2_CID_PRIVATE_LAST
Definition: uvc_cam.h:127
bool set_v4l2_control(int id, int value, const std::string &name)
Definition: uvc_cam.cpp:447
v4l2_buffer buf
Definition: uvc_cam.h:78
CSU32 V4L2_CID_LAST_NEW
Definition: uvc_cam.h:93
unsigned fps
Definition: uvc_cam.h:73
unsigned width
Definition: uvc_cam.h:73
int motion_threshold_count
Definition: uvc_cam.h:72
void set_control(uint32_t id, int val)
Definition: uvc_cam.cpp:471
unsigned height
Definition: uvc_cam.h:73
v4l2_requestbuffers rb
Definition: uvc_cam.h:77
#define ROS_WARN(...)
CSU32 V4L2_CID_CAMERA_CLASS_BASE_NEW
Definition: uvc_cam.h:96
CSU32 V4L2_CID_CAMERA_CLASS_LAST
Definition: uvc_cam.h:109
void * mem[NUM_BUFFER]
Definition: uvc_cam.h:81
v4l2_timecode timecode
Definition: uvc_cam.h:79
unsigned buf_length
Definition: uvc_cam.h:82
enum uvc_cam::Cam::mode_t mode
v4l2_format fmt
Definition: uvc_cam.h:74
void set_motion_thresholds(int lum, int count)
Definition: uvc_cam.cpp:492
Cam(const char *device, mode_t _mode=MODE_RGB, int _width=640, int _height=480, int _fps=30)
Definition: uvc_cam.cpp:21
unsigned char * last_yuv_frame
Definition: uvc_cam.h:83
int motion_threshold_luminance
Definition: uvc_cam.h:72
CSU32 V4L2_CID_PRIVATE_BASE_OLD
Definition: uvc_cam.h:112
bool v4l2_query(int id, const std::string &name)
Definition: uvc_cam.cpp:424
v4l2_capability cap
Definition: uvc_cam.h:75
v4l2_streamparm streamparm
Definition: uvc_cam.h:76
CSU32 V4L2_CID_BASE_EXTCTR
Definition: uvc_cam.h:135
static const unsigned NUM_BUFFER
Definition: uvc_cam.h:80
int grab(unsigned char **frame, uint32_t &bytes_used)
Definition: uvc_cam.cpp:333


uvc_camera
Author(s): Ken Tossell
autogenerated on Sat Jun 8 2019 17:54:12