camera_driver.cpp
Go to the documentation of this file.
1 /*********************************************************************
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (C) 2012 Ken Tossell
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
17 * * Neither the name of the author nor other contributors may be
18 * used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 *********************************************************************/
35 
36 #include <ros/ros.h>
37 #include <sensor_msgs/Image.h>
38 #include <std_msgs/Header.h>
40 #include <dynamic_reconfigure/server.h>
41 #include <libuvc/libuvc.h>
42 #define libuvc_VERSION (libuvc_VERSION_MAJOR * 10000 \
43  + libuvc_VERSION_MINOR * 100 \
44  + libuvc_VERSION_PATCH)
45 
46 namespace libuvc_camera {
47 
49  : nh_(nh), priv_nh_(priv_nh),
50  state_(kInitial),
51  ctx_(NULL), dev_(NULL), devh_(NULL), rgb_frame_(NULL),
52  it_(nh_),
53  config_server_(mutex_, priv_nh_),
54  config_changed_(false),
55  cinfo_manager_(nh) {
56  cam_pub_ = it_.advertiseCamera("image_raw", 1, false);
57 }
58 
60  if (rgb_frame_)
61  uvc_free_frame(rgb_frame_);
62 
63  if (ctx_)
64  uvc_exit(ctx_); // Destroys dev_, devh_, etc.
65 }
66 
68  assert(state_ == kInitial);
69 
70  uvc_error_t err;
71 
72  err = uvc_init(&ctx_, NULL);
73 
74  if (err != UVC_SUCCESS) {
75  uvc_perror(err, "ERROR: uvc_init");
76  return false;
77  }
78 
79  state_ = kStopped;
80 
81  config_server_.setCallback(boost::bind(&CameraDriver::ReconfigureCallback, this, _1, _2));
82 
83  return state_ == kRunning;
84 }
85 
87  boost::recursive_mutex::scoped_lock lock(mutex_);
88 
89  assert(state_ != kInitial);
90 
91  if (state_ == kRunning)
92  CloseCamera();
93 
94  assert(state_ == kStopped);
95 
96  uvc_exit(ctx_);
97  ctx_ = NULL;
98 
99  state_ = kInitial;
100 }
101 
102 void CameraDriver::ReconfigureCallback(UVCCameraConfig &new_config, uint32_t level) {
103  boost::recursive_mutex::scoped_lock lock(mutex_);
104 
105  if ((level & kReconfigureClose) == kReconfigureClose) {
106  if (state_ == kRunning)
107  CloseCamera();
108  }
109 
110  if (state_ == kStopped) {
111  OpenCamera(new_config);
112  }
113 
114  if (new_config.camera_info_url != config_.camera_info_url)
115  cinfo_manager_.loadCameraInfo(new_config.camera_info_url);
116 
117  if (state_ == kRunning) {
118 #define PARAM_INT(name, fn, value) if (new_config.name != config_.name) { \
119  int val = (value); \
120  if (uvc_set_##fn(devh_, val)) { \
121  ROS_WARN("Unable to set " #name " to %d", val); \
122  new_config.name = config_.name; \
123  } \
124  }
125 
126  PARAM_INT(scanning_mode, scanning_mode, new_config.scanning_mode);
127  PARAM_INT(auto_exposure, ae_mode, 1 << new_config.auto_exposure);
128  PARAM_INT(auto_exposure_priority, ae_priority, new_config.auto_exposure_priority);
129  PARAM_INT(exposure_absolute, exposure_abs, new_config.exposure_absolute * 10000);
130  PARAM_INT(auto_focus, focus_auto, new_config.auto_focus ? 1 : 0);
131  PARAM_INT(focus_absolute, focus_abs, new_config.focus_absolute);
132 #if libuvc_VERSION > 00005 /* version > 0.0.5 */
133  PARAM_INT(gain, gain, new_config.gain);
134  PARAM_INT(iris_absolute, iris_abs, new_config.iris_absolute);
135  PARAM_INT(brightness, brightness, new_config.brightness);
136 #endif
137 
138 
139  if (new_config.pan_absolute != config_.pan_absolute || new_config.tilt_absolute != config_.tilt_absolute) {
140  if (uvc_set_pantilt_abs(devh_, new_config.pan_absolute, new_config.tilt_absolute)) {
141  ROS_WARN("Unable to set pantilt to %d, %d", new_config.pan_absolute, new_config.tilt_absolute);
142  new_config.pan_absolute = config_.pan_absolute;
143  new_config.tilt_absolute = config_.tilt_absolute;
144  }
145  }
146  // TODO: roll_absolute
147  // TODO: privacy
148  // TODO: backlight_compensation
149  // TODO: contrast
150  // TODO: power_line_frequency
151  // TODO: auto_hue
152  // TODO: saturation
153  // TODO: sharpness
154  // TODO: gamma
155  // TODO: auto_white_balance
156  // TODO: white_balance_temperature
157  // TODO: white_balance_BU
158  // TODO: white_balance_RV
159  }
160 
161  config_ = new_config;
162 }
163 
164 void CameraDriver::ImageCallback(uvc_frame_t *frame) {
165  ros::Time timestamp = ros::Time(frame->capture_time.tv_sec, frame->capture_time.tv_usec * 1000);
166  if ( timestamp == ros::Time(0) ) {
167  timestamp = ros::Time::now();
168  }
169 
170  boost::recursive_mutex::scoped_lock lock(mutex_);
171 
172  assert(state_ == kRunning);
173  assert(rgb_frame_);
174 
175  sensor_msgs::Image::Ptr image(new sensor_msgs::Image());
176  image->width = config_.width;
177  image->height = config_.height;
178  image->step = image->width * 3;
179  image->data.resize(image->step * image->height);
180 
181  if (frame->frame_format == UVC_FRAME_FORMAT_BGR){
182  image->encoding = "bgr8";
183  memcpy(&(image->data[0]), frame->data, frame->data_bytes);
184  } else if (frame->frame_format == UVC_FRAME_FORMAT_RGB){
185  image->encoding = "rgb8";
186  memcpy(&(image->data[0]), frame->data, frame->data_bytes);
187  } else if (frame->frame_format == UVC_FRAME_FORMAT_UYVY) {
188  uvc_error_t conv_ret = uvc_uyvy2bgr(frame, rgb_frame_);
189  if (conv_ret != UVC_SUCCESS) {
190  uvc_perror(conv_ret, "Couldn't convert frame to RGB");
191  return;
192  }
193  image->encoding = "bgr8";
194  memcpy(&(image->data[0]), rgb_frame_->data, rgb_frame_->data_bytes);
195  } else if (frame->frame_format == UVC_FRAME_FORMAT_GRAY8) {
196  image->encoding = "8UC1";
197  image->step = image->width;
198  image->data.resize(image->step * image->height);
199  memcpy(&(image->data[0]), frame->data, frame->data_bytes);
200  } else if (frame->frame_format == UVC_FRAME_FORMAT_GRAY16) {
201  image->encoding = "16UC1";
202  image->step = image->width*2;
203  image->data.resize(image->step * image->height);
204  memcpy(&(image->data[0]), frame->data, frame->data_bytes);
205  } else if (frame->frame_format == UVC_FRAME_FORMAT_YUYV) {
206  // FIXME: uvc_any2bgr does not work on "yuyv" format, so use uvc_yuyv2bgr directly.
207  uvc_error_t conv_ret = uvc_yuyv2bgr(frame, rgb_frame_);
208  if (conv_ret != UVC_SUCCESS) {
209  uvc_perror(conv_ret, "Couldn't convert frame to RGB");
210  return;
211  }
212  image->encoding = "bgr8";
213  memcpy(&(image->data[0]), rgb_frame_->data, rgb_frame_->data_bytes);
214 #if libuvc_VERSION > 00005 /* version > 0.0.5 */
215  } else if (frame->frame_format == UVC_FRAME_FORMAT_MJPEG) {
216  // Enable mjpeg support despite uvs_any2bgr shortcoming
217  // https://github.com/ros-drivers/libuvc_ros/commit/7508a09f
218  uvc_error_t conv_ret = uvc_mjpeg2rgb(frame, rgb_frame_);
219  if (conv_ret != UVC_SUCCESS) {
220  uvc_perror(conv_ret, "Couldn't convert frame to RGB");
221  return;
222  }
223  image->encoding = "rgb8";
224  memcpy(&(image->data[0]), rgb_frame_->data, rgb_frame_->data_bytes);
225 #endif
226  } else {
227  uvc_error_t conv_ret = uvc_any2bgr(frame, rgb_frame_);
228  if (conv_ret != UVC_SUCCESS) {
229  uvc_perror(conv_ret, "Couldn't convert frame to RGB");
230  return;
231  }
232  image->encoding = "bgr8";
233  memcpy(&(image->data[0]), rgb_frame_->data, rgb_frame_->data_bytes);
234  }
235 
236 
237  sensor_msgs::CameraInfo::Ptr cinfo(
238  new sensor_msgs::CameraInfo(cinfo_manager_.getCameraInfo()));
239 
240  image->header.frame_id = config_.frame_id;
241  image->header.stamp = timestamp;
242  cinfo->header.frame_id = config_.frame_id;
243  cinfo->header.stamp = timestamp;
244 
245  cam_pub_.publish(image, cinfo);
246 
247  if (config_changed_) {
248  config_server_.updateConfig(config_);
249  config_changed_ = false;
250  }
251 }
252 
253 /* static */ void CameraDriver::ImageCallbackAdapter(uvc_frame_t *frame, void *ptr) {
254  CameraDriver *driver = static_cast<CameraDriver*>(ptr);
255 
256  driver->ImageCallback(frame);
257 }
258 
260  enum uvc_status_class status_class,
261  int event,
262  int selector,
263  enum uvc_status_attribute status_attribute,
264  void *data, size_t data_len) {
265  boost::recursive_mutex::scoped_lock lock(mutex_);
266 
267  printf("Controls callback. class: %d, event: %d, selector: %d, attr: %d, data_len: %zu\n",
268  status_class, event, selector, status_attribute, data_len);
269 
270  if (status_attribute == UVC_STATUS_ATTRIBUTE_VALUE_CHANGE) {
271  switch (status_class) {
272  case UVC_STATUS_CLASS_CONTROL_CAMERA: {
273  switch (selector) {
274  case UVC_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL:
275  uint8_t *data_char = (uint8_t*) data;
276  uint32_t exposure_int = ((data_char[0]) | (data_char[1] << 8) |
277  (data_char[2] << 16) | (data_char[3] << 24));
278  config_.exposure_absolute = exposure_int * 0.0001;
279  config_changed_ = true;
280  break;
281  }
282  break;
283  }
284  case UVC_STATUS_CLASS_CONTROL_PROCESSING: {
285  switch (selector) {
286  case UVC_PU_WHITE_BALANCE_TEMPERATURE_CONTROL:
287  uint8_t *data_char = (uint8_t*) data;
288  config_.white_balance_temperature =
289  data_char[0] | (data_char[1] << 8);
290  config_changed_ = true;
291  break;
292  }
293  break;
294  }
295  }
296 
297  // config_server_.updateConfig(config_);
298  }
299 }
300 
302  enum uvc_status_class status_class,
303  int event,
304  int selector,
305  enum uvc_status_attribute status_attribute,
306  void *data, size_t data_len,
307  void *ptr) {
308  CameraDriver *driver = static_cast<CameraDriver*>(ptr);
309 
310  driver->AutoControlsCallback(status_class, event, selector,
311  status_attribute, data, data_len);
312 }
313 
314 enum uvc_frame_format CameraDriver::GetVideoMode(std::string vmode){
315  if(vmode == "uncompressed") {
316  return UVC_COLOR_FORMAT_UNCOMPRESSED;
317  } else if (vmode == "compressed") {
318  return UVC_COLOR_FORMAT_COMPRESSED;
319  } else if (vmode == "yuyv") {
320  return UVC_COLOR_FORMAT_YUYV;
321  } else if (vmode == "uyvy") {
322  return UVC_COLOR_FORMAT_UYVY;
323  } else if (vmode == "rgb") {
324  return UVC_COLOR_FORMAT_RGB;
325  } else if (vmode == "bgr") {
326  return UVC_COLOR_FORMAT_BGR;
327  } else if (vmode == "mjpeg") {
328  return UVC_COLOR_FORMAT_MJPEG;
329  } else if (vmode == "gray8") {
330  return UVC_COLOR_FORMAT_GRAY8;
331  } else if (vmode == "gray16") {
332  return UVC_COLOR_FORMAT_GRAY16;
333  } else {
334  ROS_ERROR_STREAM("Invalid Video Mode: " << vmode);
335  ROS_WARN_STREAM("Continue using video mode: uncompressed");
336  return UVC_COLOR_FORMAT_UNCOMPRESSED;
337  }
338 };
339 
340 void CameraDriver::OpenCamera(UVCCameraConfig &new_config) {
341  assert(state_ == kStopped);
342 
343  int vendor_id = strtol(new_config.vendor.c_str(), NULL, 0);
344  int product_id = strtol(new_config.product.c_str(), NULL, 0);
345 
346  ROS_INFO("Opening camera with vendor=0x%x, product=0x%x, serial=\"%s\", index=%d",
347  vendor_id, product_id, new_config.serial.c_str(), new_config.index);
348 
349  uvc_device_t **devs;
350 
351  // Implement missing index select behavior
352  // https://github.com/ros-drivers/libuvc_ros/commit/4f30e9a0
353 #if libuvc_VERSION > 00005 /* version > 0.0.5 */
354  uvc_error_t find_err = uvc_find_devices(
355  ctx_, &devs,
356  vendor_id,
357  product_id,
358  new_config.serial.empty() ? NULL : new_config.serial.c_str());
359 
360  if (find_err != UVC_SUCCESS) {
361  uvc_perror(find_err, "uvc_find_device");
362  return;
363  }
364 
365  // select device by index
366  dev_ = NULL;
367  int dev_idx = 0;
368  while (devs[dev_idx] != NULL) {
369  if(dev_idx == new_config.index) {
370  dev_ = devs[dev_idx];
371  }
372  else {
373  uvc_unref_device(devs[dev_idx]);
374  }
375 
376  dev_idx++;
377  }
378 
379  if(dev_ == NULL) {
380  ROS_ERROR("Unable to find device at index %d", new_config.index);
381  return;
382  }
383 #else
384  uvc_error_t find_err = uvc_find_device(
385  ctx_, &dev_,
386  vendor_id,
387  product_id,
388  new_config.serial.empty() ? NULL : new_config.serial.c_str());
389 
390  if (find_err != UVC_SUCCESS) {
391  uvc_perror(find_err, "uvc_find_device");
392  return;
393  }
394 
395 #endif
396  uvc_error_t open_err = uvc_open(dev_, &devh_);
397 
398  if (open_err != UVC_SUCCESS) {
399  switch (open_err) {
400  case UVC_ERROR_ACCESS:
401 #ifdef __linux__
402  ROS_ERROR("Permission denied opening /dev/bus/usb/%03d/%03d",
403  uvc_get_bus_number(dev_), uvc_get_device_address(dev_));
404 #else
405  ROS_ERROR("Permission denied opening device %d on bus %d",
406  uvc_get_device_address(dev_), uvc_get_bus_number(dev_));
407 #endif
408  break;
409  default:
410 #ifdef __linux__
411  ROS_ERROR("Can't open /dev/bus/usb/%03d/%03d: %s (%d)",
412  uvc_get_bus_number(dev_), uvc_get_device_address(dev_),
413  uvc_strerror(open_err), open_err);
414 #else
415  ROS_ERROR("Can't open device %d on bus %d: %s (%d)",
416  uvc_get_device_address(dev_), uvc_get_bus_number(dev_),
417  uvc_strerror(open_err), open_err);
418 #endif
419  break;
420  }
421 
422  uvc_unref_device(dev_);
423  return;
424  }
425 
426  uvc_set_status_callback(devh_, &CameraDriver::AutoControlsCallbackAdapter, this);
427 
428  uvc_stream_ctrl_t ctrl;
429  uvc_error_t mode_err = uvc_get_stream_ctrl_format_size(
430  devh_, &ctrl,
431  GetVideoMode(new_config.video_mode),
432  new_config.width, new_config.height,
433  new_config.frame_rate);
434 
435  if (mode_err != UVC_SUCCESS) {
436  uvc_perror(mode_err, "uvc_get_stream_ctrl_format_size");
437  uvc_close(devh_);
438  uvc_unref_device(dev_);
439  ROS_ERROR("check video_mode/width/height/frame_rate are available");
440  uvc_print_diag(devh_, NULL);
441  return;
442  }
443 
444  uvc_error_t stream_err = uvc_start_streaming(devh_, &ctrl, &CameraDriver::ImageCallbackAdapter, this, 0);
445 
446  if (stream_err != UVC_SUCCESS) {
447  uvc_perror(stream_err, "uvc_start_streaming");
448  uvc_close(devh_);
449  uvc_unref_device(dev_);
450  return;
451  }
452 
453  if (rgb_frame_)
454  uvc_free_frame(rgb_frame_);
455 
456  rgb_frame_ = uvc_allocate_frame(new_config.width * new_config.height * 3);
457  assert(rgb_frame_);
458 
459  state_ = kRunning;
460 }
461 
463  assert(state_ == kRunning);
464 
465  uvc_close(devh_);
466  devh_ = NULL;
467 
468  uvc_unref_device(dev_);
469  dev_ = NULL;
470 
471  state_ = kStopped;
472 }
473 
474 };
boost::recursive_mutex mutex_
Definition: camera_driver.h:61
static void ImageCallbackAdapter(uvc_frame_t *frame, void *ptr)
enum uvc_frame_format GetVideoMode(std::string vmode)
void publish(const sensor_msgs::Image &image, const sensor_msgs::CameraInfo &info) const
sensor_msgs::CameraInfo getCameraInfo(void)
bool loadCameraInfo(const std::string &url)
static void AutoControlsCallbackAdapter(enum uvc_status_class status_class, int event, int selector, enum uvc_status_attribute status_attribute, void *data, size_t data_len, void *ptr)
image_transport::ImageTransport it_
Definition: camera_driver.h:68
#define ROS_WARN(...)
dynamic_reconfigure::Server< UVCCameraConfig > config_server_
Definition: camera_driver.h:71
void ReconfigureCallback(UVCCameraConfig &config, uint32_t level)
CameraPublisher advertiseCamera(const std::string &base_topic, uint32_t queue_size, bool latch=false)
void ImageCallback(uvc_frame_t *frame)
#define ROS_INFO(...)
#define PARAM_INT(name, fn, value)
#define ROS_WARN_STREAM(args)
void OpenCamera(UVCCameraConfig &new_config)
image_transport::CameraPublisher cam_pub_
Definition: camera_driver.h:69
camera_info_manager::CameraInfoManager cinfo_manager_
Definition: camera_driver.h:75
void AutoControlsCallback(enum uvc_status_class status_class, int event, int selector, enum uvc_status_attribute status_attribute, void *data, size_t data_len)
static Time now()
static const int kReconfigureClose
Definition: camera_driver.h:32
CameraDriver(ros::NodeHandle nh, ros::NodeHandle priv_nh)
#define ROS_ERROR_STREAM(args)
#define ROS_ERROR(...)
uvc_device_handle_t * devh_
Definition: camera_driver.h:65


libuvc_camera
Author(s):
autogenerated on Mon Feb 22 2021 03:36:23