JeVois  1.6
JeVois Smart Embedded Machine Vision Toolkit
Share this page:
Gadget.C
Go to the documentation of this file.
1 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2 //
3 // JeVois Smart Embedded Machine Vision Toolkit - Copyright (C) 2016 by Laurent Itti, the University of Southern
4 // California (USC), and iLab at USC. See http://iLab.usc.edu and http://jevois.org for information about this project.
5 //
6 // This file is part of the JeVois Smart Embedded Machine Vision Toolkit. This program is free software; you can
7 // redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software
8 // Foundation, version 2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9 // without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
10 // License for more details. You should have received a copy of the GNU General Public License along with this program;
11 // if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
12 //
13 // Contact information: Laurent Itti - 3641 Watt Way, HNB-07A - Los Angeles, CA 90089-2520 - USA.
14 // Tel: +1 213 740 3527 - itti@pollux.usc.edu - http://iLab.usc.edu - http://jevois.org
15 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
16 /*! \file */
17 
18 #include <jevois/Core/Gadget.H>
19 #include <jevois/Debug/Log.H>
20 #include <jevois/Core/VideoInput.H>
21 #include <jevois/Util/Utils.H>
23 #include <jevois/Core/Engine.H>
24 
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <fcntl.h>
28 #include <unistd.h>
29 #include <sys/time.h> // for gettimeofday()
30 
31 namespace
32 {
33  inline void debugCtrlReq(struct usb_ctrlrequest const & ctrl)
34  {
35  (void)ctrl; // avoid compiler warning about unused param if LDEBUG is turned off
36 
37  LDEBUG(std::showbase << std::hex << "bRequestType " << ctrl.bRequestType << " bRequest " << ctrl.bRequest
38  << " wValue " << ctrl.wValue << " wIndex " << ctrl.wIndex << " wLength " << ctrl.wLength);
39  }
40 
41  inline void debugStreamingCtrl(std::string const & msg, struct uvc_streaming_control const & ctrl)
42  {
43  (void)ctrl; // avoid compiler warning about unused param if LDEBUG is turned off
44  (void)msg; // avoid compiler warning about unused param if LDEBUG is turned off
45  LDEBUG(msg << ": " << std::showbase << std::hex << "bmHint=" << ctrl.bmHint << ", bFormatIndex=" <<
46  ctrl.bFormatIndex << ", bFrameIndex=" << ctrl.bFrameIndex << ", dwFrameInterval=" << ctrl.dwFrameInterval <<
47  ", wKeyFrameRate=" << ctrl.wKeyFrameRate << ", wPFrameRate=" << ctrl.wPFrameRate <<
48  ", wCompQuality=" << ctrl.wCompQuality << ", wCompWindowSize=" << ctrl.wCompWindowSize <<
49  ", wDelay=" << ctrl.wDelay << ", dwMaxVideoFrameSize=" << ctrl.dwMaxVideoFrameSize <<
50  ", dwMaxPayloadTransferSize=" << ctrl.dwMaxPayloadTransferSize << ", dwClockFrequency=" <<
51  ctrl.dwClockFrequency << ", bmFramingInfo=" << ctrl.bmFramingInfo << ", bPreferedVersion=" <<
52  ctrl.bPreferedVersion << ", bMinVersion=" << ctrl.bMinVersion << ", bMaxVersion=" << ctrl.bMaxVersion);
53  }
54 
55  unsigned int uvcToV4Lcontrol(unsigned int entity, unsigned int cs)
56  {
57  switch (entity)
58  {
59  case 1: // Our camera unit
60  // -- #define UVC_CT_SCANNING_MODE_CONTROL 0x01
61  // OK #define UVC_CT_AE_MODE_CONTROL 0x02
62  // OK #define UVC_CT_AE_PRIORITY_CONTROL 0x03
63  // OK #define UVC_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL 0x04
64  // -- #define UVC_CT_EXPOSURE_TIME_RELATIVE_CONTROL 0x05
65  // -- #define UVC_CT_FOCUS_ABSOLUTE_CONTROL 0x06
66  // -- #define UVC_CT_FOCUS_RELATIVE_CONTROL 0x07
67  // -- #define UVC_CT_FOCUS_AUTO_CONTROL 0x08
68  // -- #define UVC_CT_IRIS_ABSOLUTE_CONTROL 0x09
69  // -- #define UVC_CT_IRIS_RELATIVE_CONTROL 0x0a
70  // -- #define UVC_CT_ZOOM_ABSOLUTE_CONTROL 0x0b
71  // -- #define UVC_CT_ZOOM_RELATIVE_CONTROL 0x0c
72  // -- #define UVC_CT_PANTILT_ABSOLUTE_CONTROL 0x0d
73  // -- #define UVC_CT_PANTILT_RELATIVE_CONTROL 0x0e
74  // -- #define UVC_CT_ROLL_ABSOLUTE_CONTROL 0x0f
75  // -- #define UVC_CT_ROLL_RELATIVE_CONTROL 0x10
76  // -- #define UVC_CT_PRIVACY_CONTROL 0x11
77  // note: UVC 1.5 has a few more...
78  //
79  // Windows 10 insists on doing a GET_DEF on this 10-byte control even though we never said we support it:
80  // -- #define UVC_CT_REGION_OF_INTEREST_CONTROL 0x14
81  // This is now handled by sending a default blank reply to all unsupported controls.
82  switch (cs)
83  {
84  case UVC_CT_AE_MODE_CONTROL: return V4L2_CID_EXPOSURE_AUTO;
85  case UVC_CT_AE_PRIORITY_CONTROL: return V4L2_CID_EXPOSURE_AUTO_PRIORITY;
86  case UVC_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL: return V4L2_CID_EXPOSURE_ABSOLUTE;
87  }
88  break;
89 
90  case 2: // Our processing unit
91  // From uvcvideo.h and the UVC specs, here are the available processing unit controls:
92  // A.9.5. Processing Unit Control Selectors
93 
94  // Note one trick here: UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL contains both the V4L2_CID_RED_BALANCE and
95  // V4L2_CID_BLUE_BALANCE; we handle that in the ioctl processing section, here we just return V4L2_CID_RED_BALANCE
96 
97  // OK #define UVC_PU_BACKLIGHT_COMPENSATION_CONTROL 0x01
98  // OK #define UVC_PU_BRIGHTNESS_CONTROL 0x02
99  // OK #define UVC_PU_CONTRAST_CONTROL 0x03
100  // OK #define UVC_PU_GAIN_CONTROL 0x04
101  // OK #define UVC_PU_POWER_LINE_FREQUENCY_CONTROL 0x05
102  // OK #define UVC_PU_HUE_CONTROL 0x06
103  // OK #define UVC_PU_SATURATION_CONTROL 0x07
104  // OK #define UVC_PU_SHARPNESS_CONTROL 0x08
105  // -- #define UVC_PU_GAMMA_CONTROL 0x09
106  // -- #define UVC_PU_WHITE_BALANCE_TEMPERATURE_CONTROL 0x0a
107  // -- #define UVC_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL 0x0b
108  // OK #define UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL 0x0c
109  // OK #define UVC_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL 0x0d
110  // TODO #define UVC_PU_DIGITAL_MULTIPLIER_CONTROL 0x0e
111  // TODO #define UVC_PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL 0x0f
112  // TODO #define UVC_PU_HUE_AUTO_CONTROL 0x10
113  // TODO #define UVC_PU_ANALOG_VIDEO_STANDARD_CONTROL 0x11
114  // TODO #define UVC_PU_ANALOG_LOCK_STATUS_CONTROL 0x12
115  switch (cs)
116  {
117  case UVC_PU_BACKLIGHT_COMPENSATION_CONTROL: return V4L2_CID_BACKLIGHT_COMPENSATION;
118  case UVC_PU_BRIGHTNESS_CONTROL: return V4L2_CID_BRIGHTNESS;
119  case UVC_PU_CONTRAST_CONTROL: return V4L2_CID_CONTRAST;
120  case UVC_PU_GAIN_CONTROL: return V4L2_CID_GAIN;
121  case UVC_PU_POWER_LINE_FREQUENCY_CONTROL: return V4L2_CID_POWER_LINE_FREQUENCY;
122  case UVC_PU_HUE_CONTROL: return V4L2_CID_HUE;
123  case UVC_PU_SATURATION_CONTROL: return V4L2_CID_SATURATION;
124  case UVC_PU_SHARPNESS_CONTROL: return V4L2_CID_SHARPNESS;
125  case UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL: return V4L2_CID_RED_BALANCE;
126  case UVC_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL: return V4L2_CID_AUTO_WHITE_BALANCE;
127  //case UVC_PU_GAMMA_CONTROL: return V4L2_CID_GAMMA;
128  }
129  break;
130  }
131 
132  LFATAL("Request to access unsupported control " << cs << " on entity " << entity);
133  }
134 
135 } // namespace
136 
137 // ##############################################################################################################
138 jevois::Gadget::Gadget(std::string const & devname, jevois::VideoInput * camera, jevois::Engine * engine,
139  size_t const nbufs) :
140  itsFd(-1), itsNbufs(nbufs), itsBuffers(nullptr), itsCamera(camera), itsEngine(engine), itsRunning(false),
141  itsFormat(), itsFps(0.0F), itsStreaming(false), itsErrorCode(0), itsControl(0), itsEntity(0)
142 {
143  JEVOIS_TRACE(1);
144 
145  if (itsCamera == nullptr) LFATAL("Gadget requires a valid camera to work");
146 
147  jevois::VideoMapping const & m = itsEngine->getDefaultVideoMapping();
148  fillStreamingControl(&itsProbe, m);
149  fillStreamingControl(&itsCommit, m);
150 
151  // Get our run() thread going and wait until it is cranking, it will flip itsRunning to true as it starts:
152  itsRunFuture = std::async(std::launch::async, &jevois::Gadget::run, this);
153  while (itsRunning.load() == false) std::this_thread::sleep_for(std::chrono::milliseconds(5));
154 
155  // Open the device:
156  itsFd = open(devname.c_str(), O_RDWR | O_NONBLOCK);
157  if (itsFd == -1) PLFATAL("Gadget device open failed for " << devname);
158 
159  // Get ready to handle UVC events:
160  struct v4l2_event_subscription sub = { };
161 
162  sub.type = UVC_EVENT_SETUP;
163  XIOCTL(itsFd, VIDIOC_SUBSCRIBE_EVENT, &sub);
164 
165  sub.type = UVC_EVENT_DATA;
166  XIOCTL(itsFd, VIDIOC_SUBSCRIBE_EVENT, &sub);
167 
168  sub.type = UVC_EVENT_STREAMON;
169  XIOCTL(itsFd, VIDIOC_SUBSCRIBE_EVENT, &sub);
170 
171  sub.type = UVC_EVENT_STREAMOFF;
172  XIOCTL(itsFd, VIDIOC_SUBSCRIBE_EVENT, &sub);
173 
174  // Find out what the driver can do:
175  struct v4l2_capability cap = { };
176  XIOCTL(itsFd, VIDIOC_QUERYCAP, &cap);
177 
178  LINFO('[' << itsFd << "] UVC gadget " << devname << " card " << cap.card << " bus " << cap.bus_info);
179  if ((cap.capabilities & V4L2_CAP_VIDEO_OUTPUT) == 0) LFATAL(devname << " is not a video output device");
180  if ((cap.capabilities & V4L2_CAP_STREAMING) == 0) LFATAL(devname << " does not support streaming");
181 }
182 
183 // ##############################################################################################################
185 {
186  JEVOIS_TRACE(1);
187 
188  streamOff();
189 
190  // Tell run() thread to finish up:
191  itsRunning.store(false);
192 
193  // Will block until the run() thread completes:
194  if (itsRunFuture.valid()) try { itsRunFuture.get(); } catch (...) { jevois::warnAndIgnoreException(); }
195 
196  if (close(itsFd) == -1) PLERROR("Error closing UVC gadget -- IGNORED");
197 }
198 
199 // ##############################################################################################################
201 {
202  JEVOIS_TRACE(2);
203 
204  JEVOIS_TIMED_LOCK(itsMtx);
205 
206  // Set the format:
207  memset(&itsFormat, 0, sizeof(struct v4l2_format));
208 
209  itsFormat.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
210  itsFormat.fmt.pix.width = m.ow;
211  itsFormat.fmt.pix.height = m.oh;
212  itsFormat.fmt.pix.pixelformat = m.ofmt;
213  itsFormat.fmt.pix.field = V4L2_FIELD_NONE;
214  itsFormat.fmt.pix.sizeimage = m.osize();
215  itsFps = m.ofps;
216 
217  // First try to set our own format, will throw if phony:
218  XIOCTL(itsFd, VIDIOC_S_FMT, &itsFormat);
219 
220  // Note that the format does not include fps, this is done with VIDIOC_S_PARM:
221  try
222  {
223  // The gadget driver may not support this ioctl...
224  struct v4l2_streamparm sparm = { };
225  sparm.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
226  sparm.parm.output.outputmode = 2; // V4L2_MODE_VIDEO not defined in our headers? its value is 2.
227  sparm.parm.output.timeperframe = jevois::VideoMapping::fpsToV4l2(m.ofps);
228  XIOCTL_QUIET(itsFd, VIDIOC_S_PARM, &sparm);
229  } catch (...) { }
230 }
231 
232 // ##############################################################################################################
233 void jevois::Gadget::run()
234 {
235  JEVOIS_TRACE(1);
236 
237  fd_set wfds; // For UVC video streaming
238  fd_set efds; // For UVC events
239  struct timeval tv;
240 
241  // Switch to running state:
242  itsRunning.store(true);
243 
244  // We may have to wait until the device is opened:
245  while (itsFd == -1) std::this_thread::sleep_for(std::chrono::milliseconds(1));
246 
247  // Wait for event from the gadget kernel driver and process them:
248  while (itsRunning.load())
249  {
250  // Wait until we either receive an event or we are ready to send the next buffer over:
251  FD_ZERO(&wfds); FD_ZERO(&efds); FD_SET(itsFd, &wfds); FD_SET(itsFd, &efds);
252  tv.tv_sec = 0; tv.tv_usec = 10000;
253 
254  int ret = select(itsFd + 1, nullptr, &wfds, &efds, &tv);
255 
256  if (ret == -1) { PLERROR("Select error"); if (errno == EINTR) continue; else break; }
257  else if (ret > 0) // We have some events, handle them right away:
258  {
259  // Note: we may have more than one event, so here we try processEvents() several times to be sure:
260  if (FD_ISSET(itsFd, &efds))
261  {
262  // First event, we will report error if any:
263  try { processEvents(); } catch (...) { jevois::warnAndIgnoreException(); }
264 
265  // Let's try to dequeue one more, in most cases it should throw:
266  while (true) try { processEvents(); } catch (...) { break; }
267  }
268 
269  if (FD_ISSET(itsFd, &wfds)) try { processVideo(); } catch (...) { jevois::warnAndIgnoreException(); }
270  }
271 
272  // We timed out
273 
274  // Sometimes we miss events in the main loop, likely because more events come while we are unlocked in the USB UDC
275  // driver and processing here. So let's try to dequeue one more, in most cases it should throw:
276  while (true) try { processEvents(); } catch (...) { break; }
277 
278  // While the driver is not busy in select(), queue at most one buffer that is ready to send off:
279  try
280  {
281  JEVOIS_TIMED_LOCK(itsMtx);
282  if (itsDoneImgs.size())
283  {
284  LDEBUG("Queuing image " << itsDoneImgs.front() << " for sending over USB");
285 
286  // We need to prepare a legit v4l2_buffer, including bytesused:
287  struct v4l2_buffer buf = { };
288 
289  buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
290  buf.memory = V4L2_MEMORY_MMAP;
291  buf.index = itsDoneImgs.front();
292  buf.length = itsBuffers->get(buf.index)->length();
293 
294  if (itsFormat.fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG)
295  buf.bytesused = itsBuffers->get(buf.index)->bytesUsed();
296  else
297  buf.bytesused = buf.length;
298 
299  buf.field = V4L2_FIELD_NONE;
300  buf.flags = 0;
301  gettimeofday(&buf.timestamp, nullptr);
302 
303  // Queue it up so it can be sent to the host:
304  itsBuffers->qbuf(buf);
305 
306  // This one is done:
307  itsDoneImgs.pop_front();
308  }
309  } catch (...) { jevois::warnAndIgnoreException(); std::this_thread::sleep_for(std::chrono::milliseconds(10)); }
310  }
311 
312  // Switch out of running state in case we did interrupt the loop here by a break statement:
313  itsRunning.store(false);
314 }
315 
316 // ##############################################################################################################
317 void jevois::Gadget::processEvents()
318 {
319  JEVOIS_TRACE(3);
320 
321  // Get the event from the driver:
322  struct v4l2_event v4l2ev = { };
323  XIOCTL_QUIET(itsFd, VIDIOC_DQEVENT, &v4l2ev);
324  struct uvc_event * uvcev = reinterpret_cast<struct uvc_event *>(&v4l2ev.u.data);
325 
326  // Prepare our response, if any will be sent:
327  struct uvc_request_data resp = { };
328  resp.length = -EL2HLT;
329 
330  // Act according to the event type:
331  try
332  {
333  switch (v4l2ev.type)
334  {
335  case UVC_EVENT_CONNECT: return;
336  case UVC_EVENT_DISCONNECT: LDEBUG("EVENT DISCONNECT"); itsEngine->streamOff(); return;
337  case UVC_EVENT_SETUP: LDEBUG("EVENT SETUP"); processEventSetup(uvcev->req, resp); return;
338  case UVC_EVENT_DATA: LDEBUG("EVENT DATA"); processEventData(uvcev->data); return;
339  case UVC_EVENT_STREAMON: LDEBUG("EVENT STREAMON"); itsEngine->streamOn(); return;
340  case UVC_EVENT_STREAMOFF: LDEBUG("EVENT STREAMOFF"); itsEngine->streamOff(); return;
341  }
342  } catch (...) { }
343 }
344 
345 // ##############################################################################################################
346 void jevois::Gadget::processVideo()
347 {
348  JEVOIS_TRACE(3);
349 
350  jevois::RawImage img;
351  JEVOIS_TIMED_LOCK(itsMtx);
352 
353  // If we are not streaming anymore, abort:
354  if (itsStreaming.load() == false) LFATAL("Aborted while not streaming");
355 
356  // Dequeue a buffer from the gadget driver, this is an image that has been sent to the host and hence the buffer is
357  // now available to be filled up with image data and later queued again to the gadget driver:
358  struct v4l2_buffer buf;
359  itsBuffers->dqbuf(buf);
360 
361  // Create a RawImage from that buffer:
362  img.width = itsFormat.fmt.pix.width;
363  img.height = itsFormat.fmt.pix.height;
364  img.fmt = itsFormat.fmt.pix.pixelformat;
365  img.fps = itsFps;
366  img.buf = itsBuffers->get(buf.index);
367  img.bufindex = buf.index;
368 
369  // Push the RawImage to outside consumers:
370  itsImageQueue.push_back(img);
371  LDEBUG("Empty image " << img.bufindex << " ready for filling in by application code");
372 }
373 
374 // ##############################################################################################################
375 void jevois::Gadget::processEventSetup(struct usb_ctrlrequest const & ctrl, struct uvc_request_data & resp)
376 {
377  JEVOIS_TRACE(3);
378 
379  itsControl = 0; itsEntity = 0;
380 
381  debugCtrlReq(ctrl);
382 
383  switch (ctrl.bRequestType & USB_TYPE_MASK)
384  {
385  case USB_TYPE_STANDARD: processEventStandard(ctrl, resp); break;
386  case USB_TYPE_CLASS: processEventClass(ctrl, resp); break;
387  default: LERROR("Unsupported setup event type " << std::showbase << std::hex <<
388  (ctrl.bRequestType & USB_TYPE_MASK) << " -- IGNORED");
389  }
390  if (ctrl.bRequestType != 0x21) XIOCTL(itsFd, UVCIOC_SEND_RESPONSE, &resp);
391 }
392 
393 // ##############################################################################################################
394 void jevois::Gadget::processEventStandard(struct usb_ctrlrequest const & ctrl,
395  struct uvc_request_data & JEVOIS_UNUSED_PARAM(resp))
396 {
397  JEVOIS_TRACE(3);
398 
399  LDEBUG("UVC standard setup event ignored:");
400  debugCtrlReq(ctrl);
401 }
402 
403 // ##############################################################################################################
404 void jevois::Gadget::processEventClass(struct usb_ctrlrequest const & ctrl, struct uvc_request_data & resp)
405 {
406  JEVOIS_TRACE(3);
407 
408  if ((ctrl.bRequestType & USB_RECIP_MASK) != USB_RECIP_INTERFACE) return;
409 
410  switch (ctrl.wIndex & 0xff)
411  {
412  case UVC_INTF_CONTROL:
413  processEventControl(ctrl.bRequest, ctrl.wValue >> 8, ctrl.wIndex >> 8, ctrl.wLength, resp);
414  break;
415 
416  case UVC_INTF_STREAMING:
417  processEventStreaming(ctrl.bRequest, ctrl.wValue >> 8, resp);
418  break;
419 
420  default:
421  LERROR("Unsupported setup event class " << std::showbase << std::hex << (ctrl.wIndex & 0xff) << " -- IGNORED");
422  }
423 }
424 
425 // ##############################################################################################################
426 void jevois::Gadget::processEventControl(uint8_t req, uint8_t cs, uint8_t entity_id, uint8_t len,
427  struct uvc_request_data & resp)
428 {
429  JEVOIS_TRACE(3);
430 
431  // Local function we run on successful processing of an event: we just reset our internal error code
432 #define success() { itsErrorCode = 0; }
433 
434  // Local function we run on failed processing of an event: stall the request, set our internal error code
435 #define failure(code) { resp.length = -EL2HLT; itsErrorCode = code; }
436 
437  // Shortcurt to successfully send a 1-byte response:
438 #define byteresponse(val) { resp.data[0] = val; resp.length = 1; itsErrorCode = 0; }
439 
440  // Shortcurt to successfully send a 2-byte response:
441 #define wordresponse(val) { resp.data[0] = val & 0xff; resp.data[1] = (val >> 8) & 0xff; \
442  resp.length = 2; itsErrorCode = 0; }
443 
444  // Shortcurt to successfully send a 4-byte response:
445 #define intresponse(val) { resp.data[0] = val & 0xff; resp.data[1] = (val >> 8) & 0xff; \
446  resp.data[2] = (val >> 16) & 0xff; resp.data[3] = (val >> 24) & 0xff; \
447  resp.length = 4; itsErrorCode = 0; }
448 
449  // Shortcurt to successfully send a blank N-byte response:
450 #define arrayblankresponse(len) { memset(resp.data, 0, len); resp.length = len; itsErrorCode = 0; }
451 
452  // If anything throws here, we will return failure:
453  try
454  {
455  // First handle any request that is directed to entity 0:
456  if (entity_id == 0)
457  {
458  switch (cs)
459  {
460  case UVC_VC_REQUEST_ERROR_CODE_CONTROL: byteresponse(itsErrorCode); return; // Send error code last prepared
461  default: failure(0x06); return;
462  }
463  }
464 
465  // Process according to the entity that this event is directed to and the control that is requested:
466  if (req == UVC_SET_CUR)
467  {
468  // We need to wait for the data phase, so for now just remember the control and return success:
469  itsEntity = entity_id; itsControl = cs;
470  resp.data[0] = 0x0; resp.length = len; success();
471  LDEBUG("SET_CUR ent " << itsEntity <<" ctrl "<< itsControl <<" len "<< len);
472  }
473  else if (req == UVC_GET_INFO)
474  {
475  // FIXME: controls could also be disabled, autoupdate, or asynchronous:
476  byteresponse(UVC_CONTROL_CAP_GET | UVC_CONTROL_CAP_SET);
477  }
478  else if (req == UVC_GET_CUR)
479  {
480  // Fetch the current value from the camera. Note: both Windows and Android insist on querying some controls, like
481  // IRIS and GAMMA, which we did not declare as supported in the kernel driver. We need to handle those requests
482  // and to send some phony data:
483  struct v4l2_control ctrl = { };
484  try { ctrl.id = uvcToV4Lcontrol(entity_id, cs); itsCamera->getControl(ctrl); } catch (...) { ctrl.id = 0; }
485 
486  // We need a special handling of white balance here:
487  if (ctrl.id == V4L2_CID_RED_BALANCE)
488  {
489  unsigned int redval = (ctrl.value & 0xffff) << 16; // red is at offset 2 in PU_WHITE_BALANCE_COMPONENT_CONTROL
490 
491  // Also get the blue balance value:
492  ctrl.id = V4L2_CID_BLUE_BALANCE;
493  itsCamera->getControl(ctrl);
494 
495  // Combine both red and blue values:
496  ctrl.value = (ctrl.value & 0xffff) | redval;
497  }
498  // We also need to remap auto exposure values:
499  else if (ctrl.id == V4L2_CID_EXPOSURE_AUTO)
500  {
501  if (ctrl.value == V4L2_EXPOSURE_MANUAL) ctrl.value = 0x01; // manual mode, set UVC bit D0
502  else if (ctrl.value == V4L2_EXPOSURE_AUTO) ctrl.value = 0x02; // auto mode, set UVC bit D1
503  else ctrl.value = 0x03;
504  // Note, there are 2 more bits under CT_AE_MODE_CONTROL
505  }
506  // Handle the unknown controls:
507  else if (ctrl.id == 0) ctrl.value = 0;
508 
509  switch (len)
510  {
511  case 1: byteresponse(ctrl.value); break;
512  case 2: wordresponse(ctrl.value); break;
513  case 4: intresponse(ctrl.value); break;
514  default: LERROR("Unsupported control with length " << len << " -- SENDING BLANK RESPONSE");
515  arrayblankresponse(len); break;
516  }
517  }
518  else
519  {
520  // It's a GET_DEF/RES/MIN/MAX let's first get the data from the camera: Note: both Windows and Android insist on
521  // querying some controls, like IRIS and GAMMA, which we did not declare as supported in the kernel driver. We
522  // need to handle those requests and to send some phony data:
523  struct v4l2_queryctrl qc = { };
524  try { qc.id = uvcToV4Lcontrol(entity_id, cs); itsCamera->queryControl(qc); } catch (...) { qc.id = 0; }
525 
526  // We need a special handling of white balance here:
527  if (qc.id == V4L2_CID_RED_BALANCE)
528  {
529  // Also get the blue balance values:
530  struct v4l2_queryctrl qc2 = { };
531  qc2.id = V4L2_CID_BLUE_BALANCE;
532  itsCamera->queryControl(qc2);
533 
534  // Combine red and blue values into qc:
535  qc.default_value = (qc.default_value << 16) | qc2.default_value;
536  qc.step = (qc.step << 16) | qc2.step;
537  qc.minimum = (qc.minimum << 16) | qc2.minimum;
538  qc.maximum = (qc.maximum << 16) | qc2.maximum;
539  }
540  // We also need to remap auto exposure values:
541  else if (qc.id == V4L2_CID_EXPOSURE_AUTO)
542  {
543  // Tricky: in the 'step' field, we are supposed to provide a bitmap of the modes that are supported, see UVC
544  // specs. D0=manual, D1=auto, D2=shutter priority, D3=aperture priority. Min and max are ignored for this
545  // control, default is handled.
546  qc.minimum = 0; qc.step = 3; qc.maximum = 3; qc.default_value = 1;
547  }
548  // Also handle the unknown controls here:
549  else if (qc.id == 0)
550  { qc.minimum = 0; qc.step = 1; qc.maximum = 1; qc.default_value = 0; }
551 
552  int val = 0;
553  switch (req)
554  {
555  case UVC_GET_DEF: val = qc.default_value; break;
556  case UVC_GET_RES: val = qc.step; break;
557  case UVC_GET_MIN: val = qc.minimum; break;
558  case UVC_GET_MAX: val = qc.maximum; break;
559  default: failure(0x07); return;
560  }
561 
562  switch (len)
563  {
564  case 1: byteresponse(val); break;
565  case 2: wordresponse(val); break;
566  case 4: intresponse(val); break;
567  default: LERROR("Unsupported control with length " << len << " -- SENDING BLANK RESPONSE");
568  arrayblankresponse(len); break;
569  }
570  }
571  }
572  catch (...)
573  {
574  LERROR("FAILED entity " << entity_id << " cs " << cs << " len " << len);
575  failure(0x06);
576  }
577 }
578 
579 // ##############################################################################################################
580 void jevois::Gadget::fillStreamingControl(struct uvc_streaming_control * ctrl, jevois::VideoMapping const & m)
581 {
582  JEVOIS_TRACE(3);
583 
584  memset(ctrl, 0, sizeof(struct uvc_streaming_control));
585 
586  ctrl->bFormatIndex = m.uvcformat;
587  ctrl->bFrameIndex = m.uvcframe;
588  ctrl->dwFrameInterval = jevois::VideoMapping::fpsToUvc(m.ofps);
589  ctrl->dwMaxVideoFrameSize = m.osize();
590  ctrl->dwMaxPayloadTransferSize = 3072;
591  ctrl->bmFramingInfo = 3;
592  ctrl->bPreferedVersion = 1;
593  ctrl->bMaxVersion = 1;
594 }
595 
596 // ##############################################################################################################
597 void jevois::Gadget::processEventStreaming(uint8_t req, uint8_t cs, struct uvc_request_data & resp)
598 {
599  JEVOIS_TRACE(3);
600 
601  int const datalen = 26; // uvc 1.0 as reported by our kernel driver
602  if (cs != UVC_VS_PROBE_CONTROL && cs != UVC_VS_COMMIT_CONTROL) return;
603 
604  struct uvc_streaming_control * ctrl = reinterpret_cast<struct uvc_streaming_control *>(&resp.data);
605  struct uvc_streaming_control * target = (cs == UVC_VS_PROBE_CONTROL) ? &itsProbe : &itsCommit;
606  resp.length = datalen;
607 
608  switch (req)
609  {
610  case UVC_SET_CUR: itsControl = cs; resp.length = datalen; break; // will finish up in data stage
611 
612  case UVC_GET_CUR:
613  case UVC_GET_MIN: // we have nothing to negotiate
614  case UVC_GET_MAX: // we have nothing to negotiate
615  memcpy(ctrl, target, datalen);
616  break;
617 
618  case UVC_GET_DEF:
619  {
620  // If requested format index, frame index, or interval is bogus (including zero), initialize to our default mapping,
621  // otherwise pass down the selected mapping:
622  size_t idx = itsEngine->getDefaultVideoMappingIdx();
623  try { idx = itsEngine->getVideoMappingIdx(ctrl->bFormatIndex, ctrl->bFrameIndex, ctrl->dwFrameInterval); }
624  catch (...) { }
625  fillStreamingControl(target, itsEngine->getVideoMapping(idx));
626  memcpy(ctrl, target, datalen);
627  }
628  break;
629 
630  case UVC_GET_RES: memset(ctrl, 0, datalen); break;
631 
632  case UVC_GET_LEN: resp.data[0] = 0x00; resp.data[1] = datalen; resp.length = 2; break;
633 
634  case UVC_GET_INFO: resp.data[0] = 0x03; resp.length = 1; break;
635  }
636 }
637 
638 // ##############################################################################################################
639 void jevois::Gadget::processEventData(struct uvc_request_data & data)
640 {
641  JEVOIS_TRACE(3);
642 
643  struct uvc_streaming_control * target;
644 
645  // If entity is 1 or 2, this is to set a control:
646  if (itsEntity == 2 || itsEntity == 1) { processEventControlData(data); return; }
647 
648  switch (itsControl)
649  {
650  case UVC_VS_PROBE_CONTROL: target = &itsProbe; break;
651  case UVC_VS_COMMIT_CONTROL: target = &itsCommit; break;
652  default: processEventControlData(data); return;
653  }
654 
655  // Find the selected format and frame info and fill-in the control data:
656  struct uvc_streaming_control * ctrl = reinterpret_cast<struct uvc_streaming_control *>(&data.data);
657 
658  size_t idx = itsEngine->getVideoMappingIdx(ctrl->bFormatIndex, ctrl->bFrameIndex, ctrl->dwFrameInterval);
659 
660  fillStreamingControl(target, itsEngine->getVideoMapping(idx));
661 
662  LDEBUG("Host requested " << ctrl->bFormatIndex << '/' << ctrl->bFrameIndex << '/' << ctrl->dwFrameInterval <<
663  ", " << ((itsControl == UVC_VS_COMMIT_CONTROL) ? "setting " : "returning ") <<
664  itsEngine->getVideoMapping(idx).str());
665 
666  // Set the format if we are doing a commit control:
667  if (itsControl == UVC_VS_COMMIT_CONTROL) itsEngine->setFormat(idx);
668 }
669 
670 // ##############################################################################################################
671 void jevois::Gadget::processEventControlData(struct uvc_request_data & data)
672 {
673  JEVOIS_TRACE(3);
674 
675  struct v4l2_control ctrl;
676 
677  // Get the control ID for V4L or throw if unsupported:
678  ctrl.id = uvcToV4Lcontrol(itsEntity, itsControl);
679 
680  // Copy the data we received into the control's value:
681  switch (data.length)
682  {
683  case 1: ctrl.value = static_cast<int>(data.data[0]); break;
684  case 2: ctrl.value = static_cast<int>(__s16(data.data[0] | (static_cast<short>(data.data[1]) << 8))); break;
685  case 4: ctrl.value = data.data[0] | (data.data[1] << 8) | (data.data[2] << 16) | (data.data[3] << 24); break;
686  default: LFATAL("Ooops data len is " << data.length);
687  }
688 
689  // Tell the camera to set the control. We do not have enough time here to do it as otherwise our USB transaction will
690  // time out while we transfer a bunch of bytes to the camera over the 400kHz serial control link, so we just push it
691  // to a queue and our run() thread will do the work. First, handle special cases:
692  switch (ctrl.id)
693  {
694  case V4L2_CID_RED_BALANCE:
695  {
696  // We need to set both the red and the blue:
697  int blue = ctrl.value & 0xffff;
698  ctrl.value >>= 16; itsCamera->setControl(ctrl);
699  ctrl.id = V4L2_CID_BLUE_BALANCE; ctrl.value = blue; itsCamera->setControl(ctrl);
700  }
701  break;
702 
703  case V4L2_CID_EXPOSURE_AUTO:
704  if (ctrl.value & 0x01) ctrl.value = V4L2_EXPOSURE_MANUAL; // UVC bit D0 set for manual mode
705  else if (ctrl.value & 0x02) ctrl.value = V4L2_EXPOSURE_AUTO; // auto mode
706  // Note, there are 2 more bits under CT_AE_MODE_CONTROL
707  itsCamera->setControl(ctrl);
708  break;
709 
710  default: itsCamera->setControl(ctrl);
711  }
712 }
713 
714 // ##############################################################################################################
716 {
717  JEVOIS_TRACE(2);
718 
719  LDEBUG("Turning on UVC stream");
720 
721  JEVOIS_TIMED_LOCK(itsMtx);
722 
723  if (itsStreaming.load() || itsBuffers) { LERROR("Stream is already on -- IGNORED"); return; }
724 
725  // If number of buffers is zero, adjust it depending on frame size:
726  unsigned int nbuf = itsNbufs;
727  if (nbuf == 0)
728  {
729  unsigned int framesize = jevois::v4l2ImageSize(itsFormat.fmt.pix.pixelformat, itsFormat.fmt.pix.width,
730  itsFormat.fmt.pix.height);
731 
732  // Aim for about 4 mbyte when using small images:
733  nbuf = (4U * 1024U * 1024U) / framesize;
734  }
735 
736  // Force number of buffers to a sane value:
737  if (nbuf < 3) nbuf = 3; else if (nbuf > 16) nbuf = 16;
738 
739  // Allocate our buffers for the currently selected resolution, format, etc:
740  itsBuffers = new jevois::VideoBuffers("gadget", itsFd, V4L2_BUF_TYPE_VIDEO_OUTPUT, nbuf);
741  LINFO(itsBuffers->size() << " buffers of " << itsBuffers->get(0)->length() << " bytes allocated");
742 
743  // Fill itsImageQueue with blank frames that can be given off to application code:
744  for (size_t i = 0; i < nbuf; ++i)
745  {
746  jevois::RawImage img;
747  img.width = itsFormat.fmt.pix.width;
748  img.height = itsFormat.fmt.pix.height;
749  img.fmt = itsFormat.fmt.pix.pixelformat;
750  img.fps = itsFps;
751  img.buf = itsBuffers->get(i);
752  img.bufindex = i;
753 
754  // Push the RawImage to outside consumers:
755  itsImageQueue.push_back(img);
756  LDEBUG("Empty image " << img.bufindex << " ready for filling in by application code");
757  }
758 
759  // Start streaming over the USB link:
760  int type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
761  XIOCTL(itsFd, VIDIOC_STREAMON, &type);
762  LDEBUG("Device stream on");
763 
764  itsStreaming.store(true);
765  LDEBUG("Stream is on");
766 }
767 
768 // ##############################################################################################################
770 {
771  JEVOIS_TRACE(2);
772 
773  itsStreaming.store(false);
774 }
775 
776 // ##############################################################################################################
778 {
779  JEVOIS_TRACE(2);
780 
781  // Note: we allow for several streamOff() without complaining, this happens, e.g., when destroying a Gadget that is
782  // not currently streaming.
783 
784  LDEBUG("Turning off gadget stream");
785 
786  // Abort stream in case it was not already done, which will introduce some sleeping in our run() thread, thereby
787  // helping us acquire our needed double lock:
788  abortStream();
789 
790  JEVOIS_TIMED_LOCK(itsMtx);
791 
792  // Stop streaming over the USB link:
793  int type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
794  try { XIOCTL_QUIET(itsFd, VIDIOC_STREAMOFF, &type); } catch (...) { }
795 
796  // Nuke all our buffers:
797  if (itsBuffers) { delete itsBuffers; itsBuffers = nullptr; }
798  itsImageQueue.clear();
799  itsDoneImgs.clear();
800 
801  LDEBUG("Gadget stream is off");
802 }
803 
804 // ##############################################################################################################
806 {
807  JEVOIS_TRACE(4);
808  int retry = 2000;
809 
810  while (--retry >= 0)
811  {
812  if (itsStreaming.load() == false)
813  { LDEBUG("Not streaming"); throw std::runtime_error("Gadget get() rejected while not streaming"); }
814 
815  if (itsMtx.try_lock_for(std::chrono::milliseconds(100)))
816  {
817  if (itsStreaming.load() == false)
818  {
819  LDEBUG("Not streaming");
820  itsMtx.unlock();
821  throw std::runtime_error("Gadget get() rejected while not streaming");
822  }
823 
824  if (itsImageQueue.size())
825  {
826  img = itsImageQueue.front();
827  itsImageQueue.pop_front();
828  itsMtx.unlock();
829  LDEBUG("Empty image " << img.bufindex << " handed over to application code for filling");
830  return;
831  }
832 
833  // No image in the queue, unlock and wait for one:
834  itsMtx.unlock();
835  LDEBUG("Waiting for blank UVC image...");
836  std::this_thread::sleep_for(std::chrono::milliseconds(5));
837  }
838  else
839  {
840  LDEBUG("Waiting for lock");
841  std::this_thread::sleep_for(std::chrono::milliseconds(5));
842  }
843  }
844  LFATAL("Giving up waiting for blank UVC image");
845 }
846 
847 // ##############################################################################################################
849 {
850  JEVOIS_TRACE(4);
851  int retry = 2000;
852 
853  while (--retry >= 0)
854  {
855  if (itsStreaming.load() == false)
856  { LDEBUG("Not streaming"); throw std::runtime_error("Gadget send() rejected while not streaming"); }
857 
858  if (itsMtx.try_lock_for(std::chrono::milliseconds(100)))
859  {
860  if (itsStreaming.load() == false)
861  {
862  LDEBUG("Not streaming");
863  itsMtx.unlock();
864  throw std::runtime_error("Gadget send() rejected while not streaming");
865  }
866 
867  // Check that the format matches, this may not be the case if we changed format while the buffer was out for
868  // processing. IF so, we just drop this image since it cannot be sent to the host anymore:
869  if (img.width != itsFormat.fmt.pix.width ||
870  img.height != itsFormat.fmt.pix.height ||
871  img.fmt != itsFormat.fmt.pix.pixelformat)
872  {
873  LDEBUG("Dropping image to send out as format just changed");
874  itsMtx.unlock();
875  return;
876  }
877 
878  // We cannot just qbuf() here as our run() thread is likely in select() and the driver will bomb the qbuf as
879  // resource unavailable. So we just enqueue the buffer index and the run() thread will handle the qbuf later:
880  itsDoneImgs.push_back(img.bufindex);
881  itsMtx.unlock();
882  LDEBUG("Filled image " << img.bufindex << " received from application code");
883  return;
884  }
885  else
886  {
887  LDEBUG("Waiting for lock");
888  std::this_thread::sleep_for(std::chrono::milliseconds(5));
889  }
890  }
891  LFATAL("Giving up waiting for lock");
892 }
893 
#define LDEBUG(msg)
Convenience macro for users to print out console or syslog messages, DEBUG level. ...
Definition: Log.H:155
#define UVC_EVENT_DISCONNECT
Definition: uvc.h:27
std::string warnAndIgnoreException()
Convenience function to catch an exception, issue some LERROR (depending on type), and ignore it.
Definition: Log.C:211
void abortStream() override
Abort streaming.
Definition: Gadget.C:769
float ofps
output frame rate in frames/sec
Definition: VideoMapping.H:46
#define UVC_INTF_CONTROL
Definition: uvc.h:53
size_t getVideoMappingIdx(unsigned int iformat, unsigned int iframe, unsigned int interval) const
Get the video mapping index for a given UVC iformat, iframe and interval.
Definition: Engine.C:876
unsigned int uvcformat
USB-UVC format number (1-based)
Definition: VideoMapping.H:53
#define UVC_EVENT_STREAMOFF
Definition: uvc.h:29
void dqbuf(struct v4l2_buffer &buf)
Dequeue the next captured/displayed buffer, blocks until one is available.
Definition: VideoBuffers.C:133
unsigned int height
Image height in pixels.
Definition: RawImage.H:146
static struct v4l2_fract fpsToV4l2(float fps)
Convert from fps to V4L2 interval.
Definition: VideoMapping.C:65
__u8 data[UVC_MAX_REQUEST_SIZE - sizeof(__s32)]
Definition: uvc.h:39
void streamOff()
Stop streaming on video from camera, processing, and USB.
Definition: Engine.C:546
virtual void setControl(struct v4l2_control const &ctrl)=0
Set a control, throw if the hardware rejects the value.
void send(RawImage const &img) override
Send an image out over USB to the host computer.
Definition: Gadget.C:848
virtual ~Gadget()
Close the device and free all resources.
Definition: Gadget.C:184
void streamOn() override
Start streaming.
Definition: Gadget.C:715
void streamOn()
Start streaming on video from camera, processing, and USB.
Definition: Engine.C:535
unsigned int ofmt
output pixel format, or 0 for no output over USB
Definition: VideoMapping.H:43
unsigned int fmt
Pixel format as a V4L2_PIX_FMT_XXX.
Definition: RawImage.H:147
#define UVC_EVENT_DATA
Definition: uvc.h:31
#define XIOCTL_QUIET(dev, req, mem)
Helper macro to execute an ioctl, ignore interruptions, and, if error throw quietly.
std::shared_ptr< VideoBuf > buf
The pixel data buffer.
Definition: RawImage.H:149
#define JEVOIS_TIMED_LOCK(mtx)
Helper macro to create a timed_lock_guard object.
Definition: Log.H:299
#define success()
Gadget(std::string const &devname, VideoInput *camera, Engine *engine, size_t const nbufs=0)
Construct and open the device.
Definition: Gadget.C:138
#define wordresponse(val)
struct usb_ctrlrequest req
Definition: uvc.h:46
#define JEVOIS_TRACE(level)
Trace object.
Definition: Log.H:267
#define PLFATAL(msg)
Like LDEBUG but appends errno and strerror(errno), to be used when some system call fails...
Definition: Log.H:221
#define UVCIOC_SEND_RESPONSE
Definition: uvc.h:51
void get(RawImage &img) override
Get a pre-allocated image so that we can fill the pixel data and later send out over USB using send()...
Definition: Gadget.C:805
std::string str() const
Convenience function to print out the whole mapping in a human-friendly way.
Definition: VideoMapping.C:87
#define LERROR(msg)
Convenience macro for users to print out console or syslog messages, ERROR level. ...
Definition: Log.H:193
A raw image as coming from a V4L2 Camera and/or being sent out to a USB Gadget.
Definition: RawImage.H:110
struct uvc_request_data data
Definition: uvc.h:47
Collection of buffers for V4L2 video frames (Camera or Gadget) with hooks to the MMAP&#39;d areas...
Definition: VideoBuffers.H:41
VideoMapping const & getVideoMapping(size_t idx) const
Allow access to our video mappings which are parsed from file at construction.
Definition: Engine.C:867
size_t size() const
Get the number of buffers allocated.
Definition: VideoBuffers.C:80
Simple struct to hold video mapping definitions for the processing Engine.
Definition: VideoMapping.H:41
#define UVC_INTF_STREAMING
Definition: uvc.h:54
#define XIOCTL(dev, req, mem)
Helper macro to execute an ioctl, ignore interruptions, and, if error, issue a fatal message and thro...
std::shared_ptr< VideoBuf > get(size_t const index) const
Get one buffer, by index [0 .. size()[.
Definition: VideoBuffers.C:92
__s32 length
Definition: uvc.h:38
unsigned int uvcframe
USB UVC frame number (1-based)
Definition: VideoMapping.H:54
unsigned int oh
output height
Definition: VideoMapping.H:45
virtual void queryControl(struct v4l2_queryctrl &qc) const =0
Get information about a control, throw if unsupported by hardware.
Base class for video input, which will get derived into Camera and MovieInput.
Definition: VideoInput.H:31
#define byteresponse(val)
#define LFATAL(msg)
Convenience macro for users to print out console or syslog messages, FATAL level. ...
Definition: Log.H:212
size_t getDefaultVideoMappingIdx() const
Allow access to the default video mapping index.
Definition: Engine.C:911
unsigned int v4l2ImageSize(unsigned int fcc, unsigned int width, unsigned int height)
Return the image size in bytes for a given V4L2_PIX_FMT_..., width, height.
Definition: Utils.C:79
void qbuf(size_t const index)
Queue one buffer to V4L2, by index [0 .. size()[.
Definition: VideoBuffers.C:100
#define failure(code)
JeVois processing engine - gets images from camera sensor, processes them, and sends results over USB...
Definition: Engine.H:229
unsigned int osize() const
Return the size in bytes of an output image.
Definition: VideoMapping.C:37
VideoMapping const & getDefaultVideoMapping() const
Allow access to the default video mapping.
Definition: Engine.C:907
Definition: uvc.h:42
float fps
Programmed frames/s as given by current video mapping, may not be actual.
Definition: RawImage.H:148
#define arrayblankresponse(len)
static unsigned int fpsToUvc(float fps)
Convert from fps to USB/UVC interval.
Definition: VideoMapping.C:52
#define PLERROR(msg)
Like LERROR but appends errno and strerror(errno), to be used when some system call fails...
Definition: Log.H:201
void setFormat(size_t idx)
Callback for when the user selects a new output video format.
Definition: Engine.C:567
void setFormat(jevois::VideoMapping const &m) override
Set the video format and frame rate.
Definition: Gadget.C:200
#define LINFO(msg)
Convenience macro for users to print out console or syslog messages, INFO level.
Definition: Log.H:176
size_t bufindex
The index of the data buffer in the kernel driver.
Definition: RawImage.H:150
unsigned int ow
output width
Definition: VideoMapping.H:44
unsigned int width
Image width in pixels.
Definition: RawImage.H:145
void streamOff() override
Stop streaming.
Definition: Gadget.C:777
#define intresponse(val)
#define UVC_EVENT_STREAMON
Definition: uvc.h:28
#define UVC_EVENT_CONNECT
Definition: uvc.h:26
virtual void getControl(struct v4l2_control &ctrl) const =0
Get a control&#39;s current value, throw if unsupported by hardware.
#define UVC_EVENT_SETUP
Definition: uvc.h:30