usb_cam_hardware.hpp
Go to the documentation of this file.
1 #ifndef USB_CAM_HARDWARE_USB_CAM_HARDWARE
2 #define USB_CAM_HARDWARE_USB_CAM_HARDWARE
3 
4 #include <cstring> // for std::memset()
5 #include <string>
6 #include <vector>
7 
9 #include <ros/console.h>
10 #include <ros/duration.h>
11 #include <ros/node_handle.h>
12 #include <ros/time.h>
14 
15 #include <fcntl.h>
16 #include <sys/ioctl.h>
17 #include <sys/mman.h>
18 #include <sys/stat.h>
19 #include <sys/types.h>
20 
21 #include <linux/videodev2.h>
22 
23 namespace usb_cam_hardware {
24 
26 public:
27  USBCamHardware() : fd_(-1) {}
28 
29  virtual ~USBCamHardware() { uninit(); }
30 
31  // init the camera.
32  // returns the time-per-frame which the camera is operated at on success, or negative value on
33  // failure.
35  // register the packet buffer to the interface so that controllers can see the packet
36  {
37  packet_.stamp = ros::Time(0);
38  packet_.start = NULL;
39  packet_.length = 0;
40  packet_.buffer_index = -1;
42  "packet", &packet_.stamp, &packet_.start, &packet_.length));
44  }
45 
46  // open the device
47  {
48  const std::string video_device(param_nh.param< std::string >("video_device", "/dev/video0"));
49  fd_ = open(video_device.c_str(), O_RDWR | O_NONBLOCK);
50  if (fd_ < 0) {
51  ROS_ERROR_STREAM("Cannot open \"" << video_device << "\"");
52  return ros::Duration(-1.);
53  }
54  }
55 
56  // TODO: check device capability
57 
58  // cancel device-side cropping and scaling
59  {
60  v4l2_cropcap cropcap;
61  std::memset(&cropcap, 0, sizeof(cropcap));
62  // get the cropping capability
63  if (xioctl(fd_, VIDIOC_CROPCAP, &cropcap) == 0) {
64  // set the default (no) cropping
65  v4l2_crop crop;
66  crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
67  crop.c = cropcap.defrect;
68  xioctl(fd_, VIDIOC_S_CROP, &crop);
69  }
70  }
71 
72  // set image format
73  {
74  v4l2_format format;
75  std::memset(&format, 0, sizeof(format));
76  format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
77  format.fmt.pix.width = param_nh.param("image_width", 640);
78  format.fmt.pix.height = param_nh.param("image_height", 480);
79  const std::string pixel_format(param_nh.param< std::string >("pixel_format", "mjpeg"));
80  if (pixel_format == "grey") {
81  format.fmt.pix.pixelformat = V4L2_PIX_FMT_GREY;
82  } else if (pixel_format == "h264") {
83  format.fmt.pix.pixelformat = V4L2_PIX_FMT_H264;
84  } else if (pixel_format == "mjpeg") {
85  format.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
86  } else if (pixel_format == "rgb24") {
87  format.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24;
88  } else if (pixel_format == "uyvy") {
89  format.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
90  } else if (pixel_format == "yuyv") {
91  format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
92  } else {
93  ROS_ERROR_STREAM("Pixel format \"" << pixel_format << "\" is not supported");
94  return ros::Duration(-1.);
95  }
96  format.fmt.pix.field = V4L2_FIELD_INTERLACED;
97  if (xioctl(fd_, VIDIOC_S_FMT, &format) < 0) {
98  ROS_ERROR("Cannot set format");
99  return ros::Duration(-1.);
100  }
101  }
102 
103  // set framerate
104  ros::Duration time_per_frame;
105  {
106  v4l2_streamparm streamparm;
107  std::memset(&streamparm, 0, sizeof(streamparm));
108  streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
109  if (xioctl(fd_, VIDIOC_G_PARM, &streamparm) < 0) {
110  ROS_ERROR("Cannot get streaming parameters");
111  return ros::Duration(-1.);
112  }
113  v4l2_fract &timeperframe(streamparm.parm.capture.timeperframe);
114  timeperframe.numerator = 1;
115  timeperframe.denominator = param_nh.param("framerate", 30);
116  if (xioctl(fd_, VIDIOC_S_PARM, &streamparm) < 0) {
117  ROS_ERROR("Cannot set framerate");
118  return ros::Duration(-1.);
119  }
120  time_per_frame =
121  ros::Duration(static_cast< double >(timeperframe.numerator) / timeperframe.denominator);
122  }
123 
124  // allocate buffers
125  // TODO: support not only mmap, but also userp and read
126  {
127  v4l2_requestbuffers reqbufs;
128  std::memset(&reqbufs, 0, sizeof(reqbufs));
129  reqbufs.count = 4;
130  reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
131  reqbufs.memory = V4L2_MEMORY_MMAP;
132  if (xioctl(fd_, VIDIOC_REQBUFS, &reqbufs) < 0) {
133  ROS_ERROR("Cannot request buffers");
134  return ros::Duration(-1.);
135  }
136  if (reqbufs.count < 2) {
137  ROS_ERROR("Insufficient buffer memory on the device");
138  return ros::Duration(-1.);
139  }
140 
141  for (std::size_t i = 0; i < reqbufs.count; ++i) {
142  v4l2_buffer buffer;
143  std::memset(&buffer, 0, sizeof(buffer));
144  buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
145  buffer.memory = V4L2_MEMORY_MMAP;
146  buffer.index = i;
147  if (xioctl(fd_, VIDIOC_QUERYBUF, &buffer) < 0) {
148  ROS_ERROR("Cannot query buffer");
149  return ros::Duration(-1.);
150  }
151 
152  Buffer mapped_buffer;
153  mapped_buffer.start = mmap(NULL /* start anywhere */, buffer.length, PROT_READ | PROT_WRITE,
154  MAP_SHARED, fd_, buffer.m.offset);
155  mapped_buffer.length = buffer.length;
156  if (mapped_buffer.start == MAP_FAILED) {
157  ROS_ERROR("Cannot map memory");
158  return ros::Duration(-1.);
159  }
160 
161  buffers_.push_back(mapped_buffer);
162  }
163  }
164 
165  // start streaming
166  {
167  for (std::size_t i = 0; i < buffers_.size(); ++i) {
168  v4l2_buffer buffer;
169  std::memset(&buffer, 0, sizeof(buffer));
170  buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
171  buffer.memory = V4L2_MEMORY_MMAP;
172  buffer.index = i;
173  if (xioctl(fd_, VIDIOC_QBUF, &buffer) < 0) {
174  ROS_ERROR("Cannot enqueue buffer");
175  return ros::Duration(-1.);
176  }
177  }
178 
179  v4l2_buf_type buf_type;
180  buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
181  if (xioctl(fd_, VIDIOC_STREAMON, &buf_type) < 0) {
182  ROS_ERROR("Cannot start streaming");
183  return ros::Duration(-1.);
184  }
185  }
186 
187  return time_per_frame;
188  }
189 
190  virtual void read(const ros::Time &time, const ros::Duration &period) {
191  //
192  if (packet_.buffer_index >= 0) {
193  ROS_ERROR("last packet is not cleared. call write() first.");
194  return;
195  }
196 
197  // pop buffer
198  v4l2_buffer buffer;
199  std::memset(&buffer, 0, sizeof(buffer));
200  buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
201  buffer.memory = V4L2_MEMORY_MMAP;
202  if (xioctl(fd_, VIDIOC_DQBUF, &buffer) < 0) {
203  switch (errno) {
204  case EAGAIN:
205  /* */
206  return;
207  case EIO:
208  // TODO: code here is just copied from the original usb_cam.
209  // understand why falling through is ok.
210  break;
211  default:
212  ROS_ERROR("Cannot dequeue buffer");
213  return;
214  }
215  }
216 
217  // fill packet info with the poped buffer
219  packet_.start = buffers_[buffer.index].start;
220  packet_.length = buffer.bytesused;
221  packet_.buffer_index = buffer.index;
222  }
223 
224  virtual void write(const ros::Time &time, const ros::Duration &period) {
225  // push buffer
226  if (packet_.buffer_index >= 0) {
227  v4l2_buffer buffer;
228  std::memset(&buffer, 0, sizeof(buffer));
229  buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
230  buffer.memory = V4L2_MEMORY_MMAP;
231  buffer.index = packet_.buffer_index;
232  if (xioctl(fd_, VIDIOC_QBUF, &buffer) == 0) {
233  packet_.stamp = ros::Time(0);
234  packet_.start = NULL;
235  packet_.length = 0;
236  packet_.buffer_index = -1;
237  } else {
238  ROS_ERROR("Cannot enqueue buffer");
239  }
240  }
241  }
242 
243 private:
244  bool uninit() {
245  // stop streaming
246  {
247  v4l2_buf_type buf_type;
248  buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
249  if (xioctl(fd_, VIDIOC_STREAMOFF, &buf_type) < 0) {
250  ROS_ERROR("Cannot stop streaming");
251  return false;
252  }
253  }
254 
255  // unmap memory
256  for (Buffer &buffer : buffers_) {
257  if (munmap(buffer.start, buffer.length) < 0) {
258  ROS_ERROR("Cannot unmap memory");
259  return false;
260  }
261  }
262  buffers_.clear();
263 
264  // close device
265  if (close(fd_) < 0) {
266  ROS_ERROR("Cannot close the device");
267  return false;
268  }
269 
270  return true;
271  }
272 
273  static int xioctl(int fd, int request, void *arg) {
274  int result;
275  do {
276  result = ioctl(fd, request, arg);
277  // retry if failed because of temporary signal interruption
278  } while (result < 0 && errno == EINTR);
279  return result;
280  }
281 
282 private:
283  struct Packet {
285  const void *start;
286  std::size_t length;
288  };
289  struct Buffer {
290  void *start;
291  std::size_t length;
292  };
293 
294  int fd_;
295 
298 
299  std::vector< Buffer > buffers_;
300 };
301 
302 } // namespace usb_cam_hardware
303 
304 #endif
virtual void read(const ros::Time &time, const ros::Duration &period)
ros::Duration init(ros::NodeHandle param_nh)
bool param(const std::string &param_name, T &param_val, const T &default_val) const
virtual void write(const ros::Time &time, const ros::Duration &period)
static Time now()
static int xioctl(int fd, int request, void *arg)
#define ROS_ERROR_STREAM(args)
#define ROS_ERROR(...)
usb_cam_hardware_interface::PacketInterface packet_interface_


usb_cam_hardware
Author(s):
autogenerated on Tue Jul 14 2020 03:12:10