4 #ifdef RS_USE_V4L2_BACKEND 32 #include <sys/ioctl.h> 33 #include <sys/sysmacros.h> 34 #include <linux/usb/video.h> 35 #include <linux/uvcvideo.h> 36 #include <linux/videodev2.h> 38 #pragma GCC diagnostic ignored "-Wpedantic" 40 #pragma GCC diagnostic pop 42 #pragma GCC diagnostic ignored "-Woverflow" 44 #ifndef V4L2_CAP_META_CAPTURE 45 #define V4L2_CAP_META_CAPTURE 0x00800000 46 #endif // V4L2_CAP_META_CAPTURE 52 static void throw_error(
const char *
s)
54 std::ostringstream ss;
55 ss << s <<
" error " << errno <<
", " << strerror(errno);
56 throw std::runtime_error(ss.str());
59 static void warn_error(
const char * s)
61 LOG_ERROR(s <<
" error " << errno <<
", " << strerror(errno));
64 static int xioctl(
int fh,
int request,
void *arg)
68 r = ioctl(fh, request, arg);
69 }
while (r < 0 && errno == EINTR);
77 libusb_context * usb_context;
81 int status = libusb_init(&usb_context);
82 if(status < 0)
throw std::runtime_error(to_string() <<
"libusb_init(...) returned " << libusb_error_name(status));
86 libusb_exit(usb_context);
93 int busnum, devnum, parent_devnum;
104 subdevice(
const std::string &
name) : dev_name(
"/dev/" + name), vid(), pid(), fd(), width(), height(), format(), callback(nullptr), channel_data_callback(nullptr), is_capturing(), is_metastream()
107 if(stat(dev_name.c_str(), &st) < 0)
109 std::ostringstream ss; ss <<
"Cannot identify '" << dev_name <<
"': " << errno <<
", " << strerror(errno);
110 throw std::runtime_error(ss.str());
112 if(!S_ISCHR(st.st_mode))
throw std::runtime_error(dev_name +
" is no device");
115 std::ostringstream ss; ss <<
"/sys/dev/char/" <<
major(st.st_rdev) <<
":" <<
minor(st.st_rdev) <<
"/device/";
116 auto path = ss.str();
118 for(
int i=0; i<=3; ++i)
120 if(std::ifstream(
path +
"busnum") >> busnum)
122 if(std::ifstream(
path +
"devnum") >> devnum)
124 if(std::ifstream(
path +
"../devnum") >> parent_devnum)
133 if(!good)
throw std::runtime_error(
"Failed to read busnum/devnum");
136 if(!(std::ifstream(
"/sys/class/video4linux/" + name +
"/device/modalias") >> modalias))
137 throw std::runtime_error(
"Failed to read modalias");
138 if(modalias.size() < 14 || modalias.substr(0,5) !=
"usb:v" || modalias[9] !=
'p')
139 throw std::runtime_error(
"Not a usb format modalias");
140 if(!(std::istringstream(modalias.substr(5,4)) >> std::hex >> vid))
141 throw std::runtime_error(
"Failed to read vendor ID");
142 if(!(std::istringstream(modalias.substr(10,4)) >> std::hex >> pid))
143 throw std::runtime_error(
"Failed to read product ID");
144 if(!(std::ifstream(
"/sys/class/video4linux/" + name +
"/device/bInterfaceNumber") >> std::hex >> mi))
145 throw std::runtime_error(
"Failed to read interface number");
147 fd = open(dev_name.c_str(), O_RDWR | O_NONBLOCK, 0);
150 std::ostringstream ss; ss <<
"Cannot open '" << dev_name <<
"': " << errno <<
", " << strerror(errno);
151 throw std::runtime_error(ss.str());
154 v4l2_capability
cap = {};
155 if(xioctl(fd, VIDIOC_QUERYCAP, &cap) < 0)
157 if(errno == EINVAL)
throw std::runtime_error(dev_name +
" is no V4L2 device");
158 else throw_error(
"VIDIOC_QUERYCAP");
160 if(!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
throw std::runtime_error(dev_name +
" is no video capture device");
161 if(!(cap.capabilities & V4L2_CAP_STREAMING))
throw std::runtime_error(dev_name +
" does not support streaming I/O");
162 if((cap.device_caps & V4L2_CAP_META_CAPTURE)){
167 v4l2_cropcap cropcap = {};
168 cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
169 if(xioctl(fd, VIDIOC_CROPCAP, &cropcap) == 0)
172 crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
173 crop.c = cropcap.defrect;
174 if(xioctl(fd, VIDIOC_S_CROP, &crop) < 0)
188 if(close(fd) < 0) warn_error(
"close");
191 int get_vid()
const {
return vid; }
192 int get_pid()
const {
return pid; }
193 int get_mi()
const {
return mi; }
197 uvc_xu_control_query
q = {
static_cast<uint8_t
>(xu.unit), control,
UVC_GET_CUR, static_cast<uint16_t>(size),
reinterpret_cast<uint8_t *
>(
data)};
198 if(xioctl(fd, UVCIOC_CTRL_QUERY, &q) < 0) throw_error(
"UVCIOC_CTRL_QUERY:UVC_GET_CUR");
201 void set_control(
const extension_unit & xu, uint8_t control,
void * data,
size_t size)
203 uvc_xu_control_query q = {
static_cast<uint8_t
>(xu.unit), control,
UVC_SET_CUR, static_cast<uint16_t>(size),
reinterpret_cast<uint8_t *
>(
data)};
204 if(xioctl(fd, UVCIOC_CTRL_QUERY, &q) < 0) throw_error(
"UVCIOC_CTRL_QUERY:UVC_SET_CUR");
211 this->format = fourcc;
213 this->callback = callback;
218 this->channel_data_callback = callback;
225 v4l2_format fmt = {};
226 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
227 fmt.fmt.pix.width =
width;
228 fmt.fmt.pix.height =
height;
229 fmt.fmt.pix.pixelformat =
format;
230 fmt.fmt.pix.field = V4L2_FIELD_NONE;
231 if(xioctl(fd, VIDIOC_S_FMT, &fmt) < 0) throw_error(
"VIDIOC_S_FMT");
233 v4l2_streamparm parm = {};
234 parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
235 if(xioctl(fd, VIDIOC_G_PARM, &parm) < 0) throw_error(
"VIDIOC_G_PARM");
236 parm.parm.capture.timeperframe.numerator = 1;
237 parm.parm.capture.timeperframe.denominator = fps;
238 if(xioctl(fd, VIDIOC_S_PARM, &parm) < 0) throw_error(
"VIDIOC_S_PARM");
241 v4l2_requestbuffers req = {};
243 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
244 req.memory = V4L2_MEMORY_MMAP;
245 if(xioctl(fd, VIDIOC_REQBUFS, &req) < 0)
247 if(errno == EINVAL)
throw std::runtime_error(dev_name +
" does not support memory mapping");
248 else throw_error(
"VIDIOC_REQBUFS");
252 throw std::runtime_error(
"Insufficient buffer memory on " + dev_name);
255 buffers.resize(req.count);
256 for(
size_t i = 0; i < buffers.size(); ++i)
258 v4l2_buffer
buf = {};
259 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
260 buf.memory = V4L2_MEMORY_MMAP;
262 if(xioctl(fd, VIDIOC_QUERYBUF, &buf) < 0) throw_error(
"VIDIOC_QUERYBUF");
264 buffers[i].length = buf.length;
265 buffers[i].start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset);
266 if(buffers[i].
start == MAP_FAILED) throw_error(
"mmap");
270 for(
size_t i = 0; i < buffers.size(); ++i)
272 v4l2_buffer buf = {};
273 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
274 buf.memory = V4L2_MEMORY_MMAP;
276 if(xioctl(fd, VIDIOC_QBUF, &buf) < 0) throw_error(
"VIDIOC_QBUF");
279 v4l2_buf_type
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
280 for(
int i=0; i<10; ++i)
282 if(xioctl(fd, VIDIOC_STREAMON, &type) < 0)
284 std::this_thread::sleep_for(std::chrono::milliseconds(100));
287 if(xioctl(fd, VIDIOC_STREAMON, &type) < 0) throw_error(
"VIDIOC_STREAMON");
298 v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
299 if(xioctl(fd, VIDIOC_STREAMOFF, &type) < 0) warn_error(
"VIDIOC_STREAMOFF");
301 for(
size_t i = 0; i < buffers.size(); i++)
303 if(munmap(buffers[i].
start, buffers[i].
length) < 0) warn_error(
"munmap");
307 struct v4l2_requestbuffers req = {};
309 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
310 req.memory = V4L2_MEMORY_MMAP;
311 if(xioctl(fd, VIDIOC_REQBUFS, &req) < 0)
313 if(errno == EINVAL)
LOG_ERROR(dev_name +
" does not support memory mapping");
314 else warn_error(
"VIDIOC_REQBUFS");
318 is_capturing =
false;
322 static void poll(
const std::vector<subdevice *> &
subdevices)
327 for(
auto * sub : subdevices)
329 FD_SET(sub->fd, &fds);
330 max_fd = std::max(max_fd, sub->fd);
333 struct timeval tv = {0,10000};
334 if(select(max_fd + 1, &fds, NULL, NULL, &tv) < 0)
336 if (errno == EINTR)
return;
337 throw_error(
"select");
340 for(
auto * sub : subdevices)
342 if(FD_ISSET(sub->fd, &fds))
344 v4l2_buffer buf = {};
345 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
346 buf.memory = V4L2_MEMORY_MMAP;
347 if(xioctl(sub->fd, VIDIOC_DQBUF, &buf) < 0)
349 if(errno == EAGAIN)
return;
350 throw_error(
"VIDIOC_DQBUF");
353 sub->callback(sub->buffers[buf.index].start,
354 [sub, buf]()
mutable {
355 if(xioctl(sub->fd, VIDIOC_QBUF, &buf) < 0) throw_error(
"VIDIOC_QBUF");
361 static void poll_interrupts(libusb_device_handle *handle,
const std::vector<subdevice *> & subdevices, uint16_t
timeout)
363 static const unsigned short interrupt_buf_size = 0x400;
364 uint8_t
buffer[interrupt_buf_size];
368 int res = libusb_interrupt_transfer(handle, 0x84, buffer, interrupt_buf_size, &num_bytes, timeout);
372 for(
auto & sub : subdevices)
373 if (sub->channel_data_callback)
374 sub->channel_data_callback(buffer, num_bytes);
380 case LIBUSB_ERROR_TIMEOUT :
384 throw std::runtime_error(to_string() <<
"USB Interrupt end-point error " << libusb_strerror((libusb_error)res));
393 const std::shared_ptr<context> parent;
394 std::vector<std::unique_ptr<subdevice>>
subdevices;
396 std::thread data_channel_thread;
398 volatile bool data_stop;
400 libusb_device * usb_device;
401 libusb_device_handle * usb_handle;
402 std::vector<int> claimed_interfaces;
404 device(std::shared_ptr<context> parent) : parent(parent), stop(), data_stop(), usb_device(), usb_handle() {}
409 for(
auto interface_number : claimed_interfaces)
411 int status = libusb_release_interface(usb_handle, interface_number);
412 if(status < 0)
LOG_ERROR(
"libusb_release_interface(...) returned " << libusb_error_name(status));
415 if(usb_handle) libusb_close(usb_handle);
416 if(usb_device) libusb_unref_device(usb_device);
419 bool has_mi(
int mi)
const 421 for(
auto & sub : subdevices)
423 if(sub->get_mi() == mi)
return true;
430 std::vector<subdevice *> subs;
432 for(
auto & sub : subdevices)
436 sub->start_capture();
437 subs.push_back(sub.get());
441 thread = std::thread([
this, subs]()
443 while(!stop) subdevice::poll(subs);
449 if(thread.joinable())
455 for(
auto & sub : subdevices) sub->stop_capture();
461 std::vector<subdevice *> data_channel_subs;
462 for (
auto & sub : subdevices)
464 if (sub->channel_data_callback)
466 data_channel_subs.push_back(sub.get());
471 if (claimed_interfaces.size())
473 data_channel_thread = std::thread([
this, data_channel_subs]()
478 subdevice::poll_interrupts(this->usb_handle, data_channel_subs, 100);
486 if (data_channel_thread.joinable())
489 data_channel_thread.join();
499 int get_vendor_id(
const device & device) {
return device.subdevices[0]->get_vid(); }
500 int get_product_id(
const device & device) {
return device.subdevices[0]->get_pid(); }
504 std::string usb_bus = std::to_string(libusb_get_bus_number(device.usb_device));
507 const int max_usb_depth = 8;
508 uint8_t usb_ports[max_usb_depth];
509 std::stringstream port_path;
510 int port_count = libusb_get_port_numbers(device.usb_device, usb_ports, max_usb_depth);
512 for (
size_t i = 0; i < port_count; ++i)
514 port_path <<
"-" << std::to_string(usb_ports[i]);
517 return usb_bus + port_path.str();
520 void get_control(
const device & device,
const extension_unit & xu, uint8_t ctrl,
void * data,
int len)
522 device.subdevices[xu.subdevice]->get_control(xu, ctrl, data, len);
524 void set_control(device & device,
const extension_unit & xu, uint8_t ctrl,
void * data,
int len)
526 device.subdevices[xu.subdevice]->set_control(xu, ctrl, data, len);
529 void claim_interface(device & device,
const guid & ,
int interface_number)
531 if(!device.usb_handle)
533 int status = libusb_open(device.usb_device, &device.usb_handle);
534 if(status < 0)
throw std::runtime_error(to_string() <<
"libusb_open(...) returned " << libusb_error_name(status));
537 int status = libusb_claim_interface(device.usb_handle, interface_number);
538 if(status < 0)
throw std::runtime_error(to_string() <<
"libusb_claim_interface(...) returned " << libusb_error_name(status));
539 device.claimed_interfaces.push_back(interface_number);
541 void claim_aux_interface(device & device,
const guid & interface_guid,
int interface_number)
546 void bulk_transfer(device & device,
unsigned char endpoint,
void * data,
int length,
int *actual_length,
unsigned int timeout)
548 if(!device.usb_handle)
throw std::logic_error(
"called uvc::bulk_transfer before uvc::claim_interface");
549 int status = libusb_bulk_transfer(device.usb_handle, endpoint, (
unsigned char *)data, length, actual_length, timeout);
550 if(status < 0)
throw std::runtime_error(to_string() <<
"libusb_bulk_transfer(...) returned " << libusb_error_name(status));
553 void interrupt_transfer(device & device,
unsigned char endpoint,
void * data,
int length,
int *actual_length,
unsigned int timeout)
555 if(!device.usb_handle)
throw std::logic_error(
"called uvc::interrupt_transfer before uvc::claim_interface");
556 int status = libusb_interrupt_transfer(device.usb_handle, endpoint, (
unsigned char *)data, length, actual_length, timeout);
557 if(status < 0)
throw std::runtime_error(to_string() <<
"libusb_interrupt_transfer(...) returned " << libusb_error_name(status));
562 device.subdevices[subdevice_index]->set_format(width, height, (
const big_endian<int> &)fourcc, fps, callback);
567 device.subdevices[subdevice_index]->set_data_channel_cfg(callback);
572 device.start_streaming();
577 device.stop_streaming();
582 device.start_data_acquisition();
587 device.stop_data_acquisition();
607 default:
throw std::runtime_error(to_string() <<
"no v4l2 cid for option " << option);
613 struct v4l2_control control = {get_cid(option), value};
615 if (xioctl(device.subdevices[subdevice]->fd, VIDIOC_S_CTRL, &control) < 0) throw_error(
"VIDIOC_S_CTRL");
620 struct v4l2_control control = {get_cid(option), 0};
621 if (xioctl(device.subdevices[subdevice]->fd, VIDIOC_G_CTRL, &control) < 0) throw_error(
"VIDIOC_G_CTRL");
623 return control.value;
638 struct v4l2_queryctrl
query = {};
639 query.id = get_cid(option);
640 if (xioctl(device.subdevices[subdevice]->fd, VIDIOC_QUERYCTRL, &query) < 0)
645 query.minimum = query.maximum = 0;
647 if(min) *min = query.minimum;
648 if(max) *max = query.maximum;
649 if(step) *step = query.step;
650 if(def) *def = query.default_value;
653 void get_extension_control_range(
const device & device,
const extension_unit & xu,
char control,
int * min,
int * max,
int * step,
int * def)
659 __u8 * data = (__u8 *)&value;
660 struct uvc_xu_control_query xquery;
661 memset(&xquery, 0,
sizeof(xquery));
667 xquery.unit = xu.unit;
668 xquery.data = (__u8 *)&size;
670 if(-1 == ioctl(device.subdevices[xu.subdevice]->fd,UVCIOC_CTRL_QUERY,&xquery)){
671 throw std::runtime_error(to_string() <<
" ioctl failed on UVC_GET_LEN");
677 xquery.unit = xu.unit;
679 if(-1 == ioctl(device.subdevices[xu.subdevice]->fd,UVCIOC_CTRL_QUERY,&xquery)){
680 throw std::runtime_error(to_string() <<
" ioctl failed on UVC_GET_MIN");
687 xquery.unit = xu.unit;
689 if(-1 == ioctl(device.subdevices[xu.subdevice]->fd,UVCIOC_CTRL_QUERY,&xquery)){
690 throw std::runtime_error(to_string() <<
" ioctl failed on UVC_GET_MAX");
697 xquery.unit = xu.unit;
699 if(-1 == ioctl(device.subdevices[xu.subdevice]->fd,UVCIOC_CTRL_QUERY,&xquery)){
700 throw std::runtime_error(to_string() <<
" ioctl failed on UVC_GET_DEF");
707 xquery.unit = xu.unit;
709 if(-1 == ioctl(device.subdevices[xu.subdevice]->fd,UVCIOC_CTRL_QUERY,&xquery)){
710 throw std::runtime_error(to_string() <<
" ioctl failed on UVC_GET_CUR");
721 return std::make_shared<context>();
726 for (
auto& sub : device.subdevices)
728 if (sub->vid == vid && sub->pid == pid)
735 std::vector<std::shared_ptr<device>>
query_devices(std::shared_ptr<context> context)
738 std::vector<std::unique_ptr<subdevice>>
subdevices;
739 DIR * dir = opendir(
"/sys/class/video4linux");
740 if(!dir)
throw std::runtime_error(
"Cannot access /sys/class/video4linux");
741 while (dirent * entry = readdir(dir))
744 if(name ==
"." || name ==
"..")
continue;
749 ssize_t len = ::readlink(path.c_str(), buff,
sizeof(buff)-1);
754 if (real_path.find(
"virtual") != std::string::npos)
760 std::unique_ptr<subdevice> sub(
new subdevice(name));
761 subdevices.push_back(move(sub));
763 catch(
const std::exception &
e)
765 LOG_INFO(
"Not a USB video device: " << e.what());
774 std::vector<std::shared_ptr<device>> devices;
775 for(
auto & sub : subdevices)
777 bool is_new_device =
true;
778 for(
auto &
dev : devices)
780 if(sub->busnum ==
dev->subdevices[0]->busnum && sub->devnum ==
dev->subdevices[0]->devnum)
782 if (sub->is_metastream)
784 dev->subdevices.push_back(move(sub));
785 is_new_device =
false;
793 if (sub->is_metastream)
795 devices.push_back(std::make_shared<device>(context));
796 devices.back()->subdevices.push_back(move(sub));
801 for(
auto &
dev : devices)
803 std::sort(begin(
dev->subdevices),
end(
dev->subdevices), [](
const std::unique_ptr<subdevice> &
a,
const std::unique_ptr<subdevice> &
b)
805 return a->mi < b->mi;
811 for(
auto & sub : subdevices)
816 for(
auto &
dev : devices)
818 if (sub->is_metastream)
823 dev->subdevices.push_back(move(sub));
831 libusb_device ** list;
832 int status = libusb_get_device_list(context->usb_context, &list);
833 if(status < 0)
throw std::runtime_error(to_string() <<
"libusb_get_device_list(...) returned " << libusb_error_name(status));
834 for(
int i=0; list[i]; ++i)
836 libusb_device * usb_device = list[i];
837 int busnum = libusb_get_bus_number(usb_device);
838 int devnum = libusb_get_device_address(usb_device);
841 for(
auto &
dev : devices)
843 if (
dev->subdevices.size() >=4)
845 auto parent_device = libusb_get_parent(usb_device);
848 int parent_devnum = libusb_get_device_address(libusb_get_parent(usb_device));
851 bool bFishEyeDevice = ((busnum ==
dev->subdevices[3]->busnum) && (parent_devnum ==
dev->subdevices[3]->parent_devnum));
852 if(bFishEyeDevice && !
dev->usb_device)
854 dev->usb_device = usb_device;
855 libusb_ref_device(usb_device);
861 if(busnum ==
dev->subdevices[0]->busnum && devnum ==
dev->subdevices[0]->devnum)
863 if (!
dev->usb_device)
865 dev->usb_device = usb_device;
866 libusb_ref_device(usb_device);
872 libusb_free_device_list(list, 1);
std::shared_ptr< context > create_context()
void get_pu_control_range(const device &device, int subdevice, rs_option option, int *min, int *max, int *step, int *def)
void claim_aux_interface(device &device, const guid &interface_guid, int interface_number)
void set_subdevice_mode(device &device, int subdevice_index, int width, int height, uint32_t fourcc, int fps, video_channel_callback callback)
GLint GLint GLsizei GLsizei height
bool is_device_connected(device &device, int vid, int pid)
GLsizei const GLchar *const * path
const uint16_t VID_INTEL_CAMERA
GLsizei const GLchar *const * string
GLenum GLuint GLenum GLsizei const GLchar * buf
rs_option
Defines general configuration controls.
GLbitfield GLuint64 timeout
std::string get_usb_port_id(const device &device)
const uint16_t ZR300_FISHEYE_PID
std::function< void(const unsigned char *data, const int size)> data_channel_callback
void set_subdevice_data_channel_handler(device &device, int subdevice_index, data_channel_callback callback)
void start_streaming(device &device, int num_transfer_bufs)
void start_data_acquisition(device &device)
void get_extension_control_range(const device &device, const extension_unit &xu, char control, int *min, int *max, int *step, int *def)
format
Formats: defines how each stream can be encoded. rs_format specifies how a frame is represented in me...
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const void * data
void stop_streaming(device &device)
GLboolean GLboolean GLboolean GLboolean a
GLsizei const GLfloat * value
const uint16_t ZR300_CX3_PID
GLboolean GLboolean GLboolean b
void claim_interface(device &device, const guid &interface_guid, int interface_number)
int get_pu_control(const device &device, int subdevice, rs_option option)
int get_product_id(const device &device)
void bulk_transfer(device &device, unsigned char endpoint, void *data, int length, int *actual_length, unsigned int timeout)
GLint GLint GLsizei width
GLuint const GLchar * name
void set_control(device &device, const extension_unit &xu, uint8_t ctrl, void *data, int len)
std::vector< std::shared_ptr< device > > query_devices(std::shared_ptr< context > context)
GLuint GLsizei GLsizei * length
void set_pu_control(device &device, int subdevice, rs_option option, int value)
GLdouble GLdouble GLdouble GLdouble q
void get_control(const device &device, const extension_unit &xu, uint8_t ctrl, void *data, int len)
void stop_data_acquisition(device &device)
GLdouble GLdouble GLdouble r
std::function< void(const void *frame, std::function< void()> continuation)> video_channel_callback
int get_vendor_id(const device &device)
GLuint GLuint GLsizei GLenum type