JeVois  1.16
JeVois Smart Embedded Machine Vision Toolkit
Share this page:
CameraDevice.C
Go to the documentation of this file.
1 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2 //
3 // JeVois Smart Embedded Machine Vision Toolkit - Copyright (C) 2020 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 
19 #include <jevois/Debug/Log.H>
20 #include <jevois/Util/Utils.H>
21 #include <jevois/Util/Async.H>
25 
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <fcntl.h>
29 #include <unistd.h>
30 #include <sys/select.h>
31 
32 #define FDLDEBUG(msg) LDEBUG('[' << itsDevName << ':' << itsFd << "] " << msg)
33 #define FDLINFO(msg) LINFO('[' << itsDevName << ':' << itsFd << "] " << msg)
34 #define FDLERROR(msg) LERROR('[' << itsDevName << ':' << itsFd << "] " << msg)
35 #define FDLFATAL(msg) LFATAL('[' << itsDevName << ':' << itsFd << "] " << msg)
36 
37 // V4L2_MODE_VIDEO not defined in our kernel? Needed by capturemode of VIDIOC_S_PARM
38 #ifndef V4L2_MODE_VIDEO
39 #define V4L2_MODE_VIDEO 2
40 #endif
41 
42 #ifdef JEVOIS_PLATFORM_A33
43 namespace
44 {
45  //! Temporary fix for bug in sunxi-vfe kernel camera driver which returns MBUS format instead or V4L2
46  unsigned int v4l2sunxiFix(unsigned int fcc)
47  {
48  switch (fcc)
49  {
50  case 0x2008: // Handle bug in our sunxi camera driver
51  case V4L2_PIX_FMT_YUYV: return V4L2_PIX_FMT_YUYV;
52 
53  case 0x2001: // Handle bug in our sunxi camera driver
54  case V4L2_PIX_FMT_GREY: return V4L2_PIX_FMT_GREY;
55 
56  case 0x3001: // Handle bug in our sunxi camera driver
57  case V4L2_PIX_FMT_SRGGB8: return V4L2_PIX_FMT_SRGGB8;
58 
59  case 0x1008: // Handle bug in our sunxi camera driver
60  case V4L2_PIX_FMT_RGB565: return V4L2_PIX_FMT_RGB565;
61 
62  case V4L2_PIX_FMT_MJPEG: return V4L2_PIX_FMT_MJPEG;
63 
64  case V4L2_PIX_FMT_BGR24: return V4L2_PIX_FMT_BGR24;
65 
66  default: LFATAL("Unsupported pixel format " << jevois::fccstr(fcc));
67  }
68  }
69 }
70 
71 // Define a few things which are absent from our A33 platform kernel 3.4:
72 #define V4L2_COLORSPACE_DEFAULT v4l2_colorspace(0)
73 #endif
74 
75 // ##############################################################################################################
76 jevois::CameraDevice::CameraDevice(std::string const & devname, unsigned int const nbufs, bool dummy) :
77  itsDevName(devname), itsNbufs(nbufs), itsBuffers(nullptr), itsStreaming(false), itsFormatOk(false),
78  itsRunning(false)
79 {
80  JEVOIS_TRACE(1);
81 
82  // Open the device:
83  itsFd = open(devname.c_str(), O_RDWR | O_NONBLOCK, 0);
84  if (itsFd == -1) LFATAL("Camera device open failed on " << devname);
85 
86  // See what kinds of inputs we have and select the first one that is a camera:
87  int camidx = -1;
88  struct v4l2_input inp = { };
89  while (true)
90  {
91  try { XIOCTL_QUIET(itsFd, VIDIOC_ENUMINPUT, &inp); } catch (...) { break; }
92  if (inp.type == V4L2_INPUT_TYPE_CAMERA)
93  {
94  if (camidx == -1) camidx = inp.index;
95  FDLDEBUG(devname << ": Input " << inp.index << " [" << inp.name << "] is a camera sensor");
96  } else FDLDEBUG(devname << ": Input " << inp.index << " [" << inp.name << "] is a NOT camera sensor");
97  ++inp.index;
98  }
99 
100  if (camidx == -1) FDLFATAL("No valid camera input");
101 
102  // Select the camera input, this seems to be required by sunxi-VFE on JeVois-A33 for the camera to power on:
103  XIOCTL(itsFd, VIDIOC_S_INPUT, &camidx);
104 
105  // Find out what camera can do:
106  struct v4l2_capability cap = { };
107  XIOCTL(itsFd, VIDIOC_QUERYCAP, &cap);
108 
109  FDLINFO("V4L2 camera " << devname << " card " << cap.card << " bus " << cap.bus_info);
110 
111  // Amlogic ISP V4L2 does not report a video capture capability, but it has video capture mplane
112  if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE) itsMplane = true;
113 
114  if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0 && itsMplane == false)
115  FDLFATAL(devname << " is not a video capture device");
116 
117  if ((cap.capabilities & V4L2_CAP_STREAMING) == 0)
118  FDLFATAL(devname << " does not support streaming");
119 
120  // List the supported formats, only once:
121  static bool showfmts = true;
122  if (dummy == false && showfmts)
123  {
124  struct v4l2_fmtdesc fmtdesc = { };
125  if (itsMplane) fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; else fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
126  while (true)
127  {
128  try { XIOCTL_QUIET(itsFd, VIDIOC_ENUM_FMT, &fmtdesc); } catch (...) { break; }
129  FDLINFO("Supported format " << fmtdesc.index << " is [" << fmtdesc.description << "] fcc " << std::showbase <<
130  std::hex << fmtdesc.pixelformat << " [" << jevois::fccstr(fmtdesc.pixelformat) << ']');
131  ++fmtdesc.index;
132  }
133  showfmts = false;
134  }
135 
136  // Get our run() thread going and wait until it is cranking, it will flip itsRunning to true as it starts:
137  if (dummy == false)
138  {
139  itsRunFuture = jevois::async_little(std::bind(&jevois::CameraDevice::run, this));
140  while (itsRunning.load() == false) std::this_thread::sleep_for(std::chrono::milliseconds(1));
141  }
142 }
143 
144 // ##############################################################################################################
146 {
147  JEVOIS_TRACE(1);
148 
149  // Turn off streaming if it was on:
150  try { streamOff(); } catch (...) { jevois::warnAndIgnoreException(); }
151 
152  // Block until the run() thread completes:
153  itsRunning.store(false);
154  JEVOIS_WAIT_GET_FUTURE(itsRunFuture);
155 
156  while (true)
157  {
158  std::unique_lock lck(itsMtx, std::chrono::seconds(5));
159  if (lck.owns_lock() == false) { FDLERROR("Timeout trying to acquire camera lock"); continue; }
160 
161  if (itsBuffers) delete itsBuffers;
162  if (itsFd != -1) close(itsFd);
163  break;
164  }
165 }
166 
167 // ##############################################################################################################
169 { return itsFd; }
170 
171 // ##############################################################################################################
172 void jevois::CameraDevice::run()
173 {
174  JEVOIS_TRACE(1);
175 
176  fd_set rfds; // For new images captured
177  fd_set efds; // For errors
178  struct timeval tv;
179 
180  // Switch to running state:
181  itsRunning.store(true);
182  LDEBUG("run() thread ready");
183 
184  // NOTE: The flow is a little complex here, the goal is to minimize latency between a frame being captured and us
185  // dequeueing it from the driver and making it available to get(). To achieve low latency, we thus need to be polling
186  // the driver most of the time, and we need to prevent other threads from doing various ioctls while we are polling,
187  // as the SUNXI-VFE driver does not like that. Thus, there is high contention on itsMtx which we lock most of the
188  // time. For this reason we do a bit of sleeping with itsMtx unlocked at places where we know it will not increase our
189  // captured image delivery latency.
190  std::vector<size_t> doneidx;
191 
192  // Wait for events from the kernel driver and process them:
193  while (itsRunning.load())
194  try
195  {
196  // Requeue any done buffer. To avoid having to use a double lock on itsOutputMtx (for itsDoneIdx) and itsMtx (for
197  // itsBuffers->qbuf()), we just swap itsDoneIdx into a local variable here, and invalidate it, with itsOutputMtx
198  // locked, then we will do the qbuf() later, if needed, while itsMtx is locked:
199  {
200  JEVOIS_TIMED_LOCK(itsOutputMtx);
201  if (itsDoneIdx.empty() == false) itsDoneIdx.swap(doneidx);
202  }
203 
204  std::unique_lock lck(itsMtx, std::chrono::seconds(5));
205  if (lck.owns_lock() == false) FDLFATAL("Timeout trying to acquire camera lock");
206 
207  // Do the actual qbuf of any done buffer, ignoring any exception:
208  if (itsBuffers) { for (size_t idx : doneidx) try { itsBuffers->qbuf(idx); } catch (...) { } }
209  doneidx.clear();
210 
211  // SUNXI-VFE does not like to be polled when not streaming; if indeed we are not streaming, unlock and then sleep
212  // a bit to avoid too much contention on itsMtx:
213  if (itsStreaming.load() == false)
214  {
215  lck.unlock();
216  std::this_thread::sleep_for(std::chrono::milliseconds(5));
217  continue;
218  }
219 
220  // Check whether user code cannot keep up with the frame rate, and if so requeue all dequeued buffers except maybe
221  // the one currently associated with itsOutputImage:
222  if (itsBuffers && itsBuffers->nqueued() < 4)
223  {
224  LERROR("Running out of camera buffers - your process() function is too slow - DROPPING FRAMES");
225  size_t keep = 12345678;
226 
227  lck.unlock();
228  {
229  JEVOIS_TIMED_LOCK(itsOutputMtx);
230  if (itsOutputImage.valid()) keep = itsOutputImage.bufindex;
231  }
232  lck.lock();
233 
234  itsBuffers->qbufallbutone(keep);
235  }
236 
237  // Poll the device to wait for any new captured video frame:
238  FD_ZERO(&rfds); FD_ZERO(&efds); FD_SET(itsFd, &rfds); FD_SET(itsFd, &efds);
239  tv.tv_sec = 0; tv.tv_usec = 5000;
240 
241  int ret = select(itsFd + 1, &rfds, nullptr, &efds, &tv);
242  if (ret == -1) { FDLERROR("Select error"); if (errno == EINTR) continue; else PLFATAL("Error polling camera"); }
243  else if (ret > 0) // NOTE: ret == 0 would mean timeout
244  {
245  if (FD_ISSET(itsFd, &efds)) FDLFATAL("Camera device error");
246 
247  if (FD_ISSET(itsFd, &rfds))
248  {
249  // A new frame has been captured. Dequeue a buffer from the camera driver:
250  struct v4l2_buffer buf;
251  itsBuffers->dqbuf(buf);
252 
253  // Create a RawImage from that buffer:
254  jevois::RawImage img;
255  img.width = itsFormat.fmt.pix.width;
256  img.height = itsFormat.fmt.pix.height;
257  img.fmt = itsFormat.fmt.pix.pixelformat;
258  img.fps = itsFps;
259  img.buf = itsBuffers->get(buf.index);
260  img.bufindex = buf.index;
261 
262  // Unlock itsMtx:
263  lck.unlock();
264 
265  // We want to never block waiting for people to consume our grabbed frames here, hence we just overwrite our
266  // output image here, it just always contains the latest grabbed image:
267  {
268  JEVOIS_TIMED_LOCK(itsOutputMtx);
269 
270  // If user never called get()/done() on an image we already have, drop it and requeue the buffer:
271  if (itsOutputImage.valid()) itsDoneIdx.push_back(itsOutputImage.bufindex);
272 
273  // Set our new output image:
274  itsOutputImage = img;
275  }
276  LDEBUG("Captured image " << img.bufindex << " ready for processing");
277 
278  // Let anyone trying to get() our image know it's here:
279  itsOutputCondVar.notify_all();
280 
281  // This is also a good time to sleep a bit since it will take a while for the next frame to arrive, this
282  // should allow people who had been trying to get a lock on itsMtx to get it now:
283  std::this_thread::sleep_for(std::chrono::milliseconds(5));
284  }
285  }
286  } catch (...) { jevois::warnAndIgnoreException(); }
287 
288  // Switch out of running state in case we did interrupt the loop here by a break statement:
289  itsRunning.store(false);
290 }
291 
292 // ##############################################################################################################
294 {
295  JEVOIS_TRACE(2);
296 
297  LDEBUG("Turning on camera stream");
298 
299  JEVOIS_TIMED_LOCK(itsMtx);
300 
301  if (itsFormatOk == false) FDLFATAL("No valid capture format was set -- ABORT");
302 
303  if (itsStreaming.load() || itsBuffers) { FDLERROR("Stream is already on -- IGNORED"); return; }
304 
305  itsStreaming.store(false); // just in case user forgot to call abortStream()
306  /*
307  // If number of buffers is zero, adjust it depending on frame size:
308  unsigned int nbuf = itsNbufs;
309  if (nbuf == 0)
310  {
311  unsigned int framesize = jevois::v4l2ImageSize(itsFormat.fmt.pix.pixelformat, itsFormat.fmt.pix.width,
312  itsFormat.fmt.pix.height);
313 
314  // Aim for about 4 mbyte when using small images, and no more than 5 buffers in any case:
315  nbuf = (4U * 1024U * 1024U) / framesize;
316  if (nbuf > 5) nbuf = 5;
317  }
318 
319  // Force number of buffers to a sane value:
320  if (nbuf < 3) nbuf = 3; else if (nbuf > 63) nbuf = 63;
321  */
322  unsigned int nbuf = 10;
323 
324  // Allocate the buffers for our current video format:
325  v4l2_buf_type btype = itsMplane ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE : V4L2_BUF_TYPE_VIDEO_CAPTURE;
326  itsBuffers = new jevois::VideoBuffers("camera", itsFd, btype, nbuf);
327  FDLINFO(itsBuffers->size() << " buffers of " << itsBuffers->get(0)->length() << " bytes allocated");
328 
329  // Enqueue all our buffers:
330  itsBuffers->qbufall();
331  FDLDEBUG("All buffers queued to camera driver");
332 
333  // Start streaming at the device level:
334  XIOCTL(itsFd, VIDIOC_STREAMON, &btype);
335  FDLDEBUG("Device stream on");
336 
337  itsStreaming.store(true);
338  FDLDEBUG("Streaming is on");
339 }
340 
341 // ##############################################################################################################
343 {
344  JEVOIS_TRACE(2);
345 
346  // Set its Streaming to false here while unlocked, which will introduce some sleeping in our run() thread, thereby
347  // helping us acquire our needed double lock:
348  itsStreaming.store(false);
349 
350  // Unblock any get() that is waiting on itsOutputCondVar, it will then throw now that streaming is off:
351  for (int i = 0; i < 20; ++i) itsOutputCondVar.notify_all();
352 }
353 
354 // ##############################################################################################################
356 {
357  JEVOIS_TRACE(2);
358 
359  // Note: we allow for several streamOff() without complaining, this happens, e.g., when destroying a Camera that is
360  // not currently streaming.
361 
362  FDLDEBUG("Turning off camera stream");
363 
364  // Abort stream in case it was not already done, which will introduce some sleeping in our run() thread, thereby
365  // helping us acquire our needed double lock:
366  abortStream();
367 
368  // We need a double lock here so that we can both turn off the stream and nuke our output image and done idx:
369  std::unique_lock<std::timed_mutex> lk1(itsMtx, std::defer_lock);
370  std::unique_lock<std::timed_mutex> lk2(itsOutputMtx, std::defer_lock);
371  LDEBUG("Ready to double-lock...");
372  std::lock(lk1, lk2);
373  LDEBUG("Double-lock success.");
374 
375  // Invalidate our output image:
376  itsOutputImage.invalidate();
377 
378  // User may have called done() but our run() thread has not yet gotten to requeueing this image, if so requeue it here
379  // as it seems to keep the driver happier:
380  if (itsBuffers)
381  for (size_t idx : itsDoneIdx) try { itsBuffers->qbuf(idx); } catch (...) { jevois::warnAndIgnoreException(); }
382  itsDoneIdx.clear();
383 
384  // Stop streaming at the device level:
385  int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
386  try { XIOCTL_QUIET(itsFd, VIDIOC_STREAMOFF, &type); } catch (...) { }
387 
388  // Nuke all the buffers:
389  if (itsBuffers) { delete itsBuffers; itsBuffers = nullptr; }
390 
391  // Unblock any get() that is waiting on itsOutputCondVar, it will then throw now that streaming is off:
392  lk2.unlock();
393  for (int i = 0; i < 20; ++i) itsOutputCondVar.notify_all();
394 
395  FDLDEBUG("Camera stream is off");
396 }
397 
398 // ##############################################################################################################
400 {
401  JEVOIS_TRACE(4);
402 
403  if (itsConvertedOutputImage.valid())
404  {
405  // We need to convert from Bayer/Mono to YUYV:
406  std::unique_lock ulck(itsOutputMtx, std::chrono::seconds(5));
407  if (ulck.owns_lock() == false) FDLFATAL("Timeout trying to acquire output lock");
408 
409  if (itsOutputImage.valid() == false)
410  {
411  if (itsOutputCondVar.wait_for(ulck, std::chrono::milliseconds(2500),
412  [&]() { return itsOutputImage.valid() || itsStreaming.load() == false; }) == false)
413  throw std::runtime_error("Timeout waiting for camera frame or camera not streaming");
414  }
415 
416  if (itsStreaming.load() == false) throw std::runtime_error("Camera not streaming");
417 
418  switch (itsFormat.fmt.pix.pixelformat) // FIXME may need to protect this?
419  {
420  case V4L2_PIX_FMT_SRGGB8: jevois::rawimage::convertBayerToYUYV(itsOutputImage, itsConvertedOutputImage); break;
421  case V4L2_PIX_FMT_GREY: jevois::rawimage::convertGreyToYUYV(itsOutputImage, itsConvertedOutputImage); break;
422  default: FDLFATAL("Oops, cannot convert captured image");
423  }
424 
425  img = itsConvertedOutputImage;
426  img.bufindex = itsOutputImage.bufindex;
427  itsOutputImage.invalidate();
428  }
429  else
430  {
431  // Regular get() with no conversion:
432  std::unique_lock ulck(itsOutputMtx, std::chrono::seconds(5));
433  if (ulck.owns_lock() == false) FDLFATAL("Timeout trying to acquire output lock");
434 
435  if (itsOutputImage.valid() == false)
436  {
437  if (itsOutputCondVar.wait_for(ulck, std::chrono::milliseconds(2500),
438  [&]() { return itsOutputImage.valid() || itsStreaming.load() == false; }) == false)
439  throw std::runtime_error("Timeout waiting for camera frame or camera not streaming");
440  }
441 
442  if (itsStreaming.load() == false) throw std::runtime_error("Camera not streaming");
443 
444  img = itsOutputImage;
445  itsOutputImage.invalidate();
446  }
447 
448  LDEBUG("Camera image " << img.bufindex << " handed over to processing");
449 }
450 
451 // ##############################################################################################################
453 {
454  JEVOIS_TRACE(4);
455 
456  if (itsStreaming.load() == false) throw std::runtime_error("Camera done() rejected while not streaming");
457 
458  // To avoid blocking for a long time here, we do not try to lock itsMtx and to qbuf() the buffer right now, instead we
459  // just make a note that this buffer is available and it will be requeued by our run() thread:
460  JEVOIS_TIMED_LOCK(itsOutputMtx);
461  itsDoneIdx.push_back(img.bufindex);
462 
463  LDEBUG("Image " << img.bufindex << " freed by processing");
464 }
465 
466 // ##############################################################################################################
467 void jevois::CameraDevice::setFormat(unsigned int const fmt, unsigned int const capw, unsigned int const caph,
468  float const fps, unsigned int const cropw, unsigned int const croph)
469 {
470  JEVOIS_TRACE(2);
471 
472  // We may be streaming, eg, if we were running a mapping with no USB out and then the user starts a video grabber. So
473  // make sure we stream off first:
474  if (itsStreaming.load()) streamOff();
475 
476  JEVOIS_TIMED_LOCK(itsMtx);
477 
478  // Assume format not set in case we exit on exception:
479  itsFormatOk = false;
480 
481  // Set desired format:
482  if (itsMplane)
483  {
484  // Get current format:
485  itsFormat.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
486  XIOCTL(itsFd, VIDIOC_G_FMT, &itsFormat);
487 
488  // Set desired format:
489  // see https://chromium.googlesource.com/chromiumos/platform/cros-yavta/+/refs/heads/upstream/master/yavta.c
490  itsFormat.fmt.pix_mp.width = capw;
491  itsFormat.fmt.pix_mp.height = caph;
492  itsFormat.fmt.pix_mp.pixelformat = fmt;
493  itsFormat.fmt.pix_mp.num_planes = 1; // FIXME force to 1 plane, likely will break NV12 support
494  itsFormat.fmt.pix_mp.colorspace = V4L2_COLORSPACE_DEFAULT;
495  itsFormat.fmt.pix_mp.field = V4L2_FIELD_NONE;
496  itsFps = fps;
497  LDEBUG("Requesting multiplane video format " << itsFormat.fmt.pix.width << 'x' << itsFormat.fmt.pix.height << ' ' <<
498  jevois::fccstr(itsFormat.fmt.pix.pixelformat));
499 
500  // Amlogic kernel bugfix: still set the regular fields:
501  itsFormat.fmt.pix.width = capw;
502  itsFormat.fmt.pix.height = caph;
503  itsFormat.fmt.pix.pixelformat = fmt;
504  itsFormat.fmt.pix.colorspace = V4L2_COLORSPACE_DEFAULT;
505  itsFormat.fmt.pix.field = V4L2_FIELD_NONE;
506  }
507  else
508  {
509  // Get current format:
510  itsFormat.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
511  XIOCTL(itsFd, VIDIOC_G_FMT, &itsFormat);
512 
513  // Set desired format:
514  itsFormat.fmt.pix.width = capw;
515  itsFormat.fmt.pix.height = caph;
516  itsFormat.fmt.pix.pixelformat = fmt;
517  itsFormat.fmt.pix.colorspace = V4L2_COLORSPACE_DEFAULT;
518  itsFormat.fmt.pix.field = V4L2_FIELD_NONE;
519  itsFps = fps;
520  LDEBUG("Requesting video format " << itsFormat.fmt.pix.width << 'x' << itsFormat.fmt.pix.height << ' ' <<
521  jevois::fccstr(itsFormat.fmt.pix.pixelformat));
522  }
523 
524  // Try to set the format. If it fails, try to see whether we can use BAYER or MONO instead, and we will convert:
525  try
526  {
527  XIOCTL_QUIET(itsFd, VIDIOC_S_FMT, &itsFormat);
528  }
529  catch (...)
530  {
531  if (itsMplane)
532  FDLFATAL("Could not set camera format to " << capw << 'x' << caph << ' ' << jevois::fccstr(fmt) <<
533  ". Maybe the sensor does not support requested pixel type or resolution.");
534  else
535  {
536  try
537  {
538  // Oops, maybe this sensor only supports raw Bayer:
539  itsFormat.fmt.pix.pixelformat = V4L2_PIX_FMT_SRGGB8;
540  XIOCTL_QUIET(itsFd, VIDIOC_S_FMT, &itsFormat);
541  }
542  catch (...)
543  {
544  try
545  {
546  // Oops, maybe this sensor only supports monochrome:
547  itsFormat.fmt.pix.pixelformat = V4L2_PIX_FMT_GREY;
548  XIOCTL_QUIET(itsFd, VIDIOC_S_FMT, &itsFormat);
549  }
550  catch (...)
551  {
552  FDLFATAL("Could not set camera format to " << capw << 'x' << caph << ' ' << jevois::fccstr(fmt) <<
553  ". Maybe the sensor does not support requested pixel type or resolution.");
554  }
555  }
556  }
557  }
558 
559  // Get the format back as the driver may have adjusted some sizes, etc:
560  XIOCTL(itsFd, VIDIOC_G_FMT, &itsFormat);
561 
562  // The sunxi driver on JeVois-A33 returns a different format code, may be the mbus code instead of the v4l2 fcc...
563 #ifdef JEVOIS_PLATFORM_A33
564  itsFormat.fmt.pix.pixelformat = v4l2sunxiFix(itsFormat.fmt.pix.pixelformat);
565 #endif
566 
567  FDLINFO("Camera set video format to " << itsFormat.fmt.pix.width << 'x' << itsFormat.fmt.pix.height << ' ' <<
568  jevois::fccstr(itsFormat.fmt.pix.pixelformat));
569 
570  // Because modules may rely on the exact format that they request, throw if the camera modified it:
571  if (itsMplane)
572  {
573  if (itsFormat.fmt.pix_mp.width != capw ||
574  itsFormat.fmt.pix_mp.height != caph ||
575  itsFormat.fmt.pix_mp.pixelformat != fmt)
576  FDLFATAL("Camera did not accept the requested video format as specified");
577  }
578  else
579  {
580  if (itsFormat.fmt.pix.width != capw ||
581  itsFormat.fmt.pix.height != caph ||
582  (itsFormat.fmt.pix.pixelformat != fmt &&
583  (fmt != V4L2_PIX_FMT_YUYV ||
584  (itsFormat.fmt.pix.pixelformat != V4L2_PIX_FMT_SRGGB8 &&
585  itsFormat.fmt.pix.pixelformat != V4L2_PIX_FMT_GREY))))
586  FDLFATAL("Camera did not accept the requested video format as specified");
587  }
588 
589  // Reset cropping parameters. NOTE: just open()'ing the device does not reset it, according to the unix toolchain
590  // philosophy. Hence, although here we do not provide support for cropping, we still need to ensure that it is
591  // properly reset. Note that some cameras do not support this so here we swallow that exception:
592  if (fps > 0.0F)
593  try
594  {
595  struct v4l2_cropcap cropcap = { };
596  cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; // Note: kernel docs say do not use the MPLANE type here.
597  XIOCTL_QUIET(itsFd, VIDIOC_CROPCAP, &cropcap);
598 
599  LDEBUG("Cropcap bounds " << cropcap.bounds.width << 'x' << cropcap.bounds.height <<
600  " @ (" << cropcap.bounds.left << ", " << cropcap.bounds.top << ')');
601  LDEBUG("Cropcap defrect " << cropcap.defrect.width << 'x' << cropcap.defrect.height <<
602  " @ (" << cropcap.defrect.left << ", " << cropcap.defrect.top << ')');
603 
604  struct v4l2_crop crop = { };
605  crop.type = itsFormat.type;
606  if (capw == cropw && caph == croph)
607  crop.c = cropcap.defrect;
608  else
609  {
610  crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; // Note: kernel docs say do not use the MPLANE type here.
611  crop.c.top = ((caph - croph) >> 1) & 0xfffc; // force multiple of 4
612  crop.c.left = ((capw - cropw) >> 1) & 0xfffc;
613  crop.c.width = cropw; crop.c.height = croph;
614 
615  // From now on, as far as we are concerned, these are the capture width and height:
616  itsFormat.fmt.pix.width = cropw;
617  itsFormat.fmt.pix.height = croph;
618  }
619 
620  XIOCTL_QUIET(itsFd, VIDIOC_S_CROP, &crop);
621 
622  LINFO("Set cropping rectangle to " << crop.c.width << 'x' << crop.c.height <<
623  " @ (" << crop.c.left << ", " << crop.c.top << ')');
624  }
625  catch (...) { LERROR("Querying/setting crop rectangle not supported"); }
626 
627  // From now on, as far as we are concerned, these are the capture width and height:
628  itsFormat.fmt.pix.width = cropw;
629  itsFormat.fmt.pix.height = croph;
630 
631  // Allocate a RawImage for conversion from Bayer or Monochrome to YUYV if needed:
632  itsConvertedOutputImage.invalidate();
633  if (itsMplane == false && fmt == V4L2_PIX_FMT_YUYV &&
634  (itsFormat.fmt.pix.pixelformat == V4L2_PIX_FMT_SRGGB8 || itsFormat.fmt.pix.pixelformat == V4L2_PIX_FMT_GREY))
635  {
636  // We will grab raw Bayer/Mono and store that into itsOutputImage, finally converting to YUYV in get():
637  itsConvertedOutputImage.width = itsFormat.fmt.pix.width;
638  itsConvertedOutputImage.height = itsFormat.fmt.pix.height;
639  itsConvertedOutputImage.fmt = V4L2_PIX_FMT_YUYV;
640  itsConvertedOutputImage.fps = itsFps;
641  itsConvertedOutputImage.buf = std::make_shared<jevois::VideoBuf>(-1, itsConvertedOutputImage.bytesize(), 0, -1);
642  }
643 
644  // Set frame rate:
645  if (fps > 0.0F)
646  try
647  {
648  struct v4l2_streamparm parms = { };
649  parms.type = itsFormat.type;
650  parms.parm.capture.timeperframe = jevois::VideoMapping::fpsToV4l2(fps);
651  parms.parm.capture.capturemode = V4L2_MODE_VIDEO;
652  XIOCTL(itsFd, VIDIOC_S_PARM, &parms);
653 
654  LDEBUG("Set framerate to " << fps << " fps");
655  }
656  catch (...) { LERROR("Setting frame rate to " << fps << " fps failed -- IGNORED"); }
657 
658  // All good, note that we succeeded:
659  itsFormatOk = true;
660 }
JEVOIS_WAIT_GET_FUTURE
#define JEVOIS_WAIT_GET_FUTURE(f)
Wait for a future to become ready for 5 seconds, get(), warn and ignore exception,...
Definition: Log.H:312
jevois::rawimage::convertBayerToYUYV
void convertBayerToYUYV(RawImage const &src, RawImage &dst)
Convert from Bayer to YUYV, only used internally by Camera class.
Definition: RawImageOps.C:1593
FDLINFO
#define FDLINFO(msg)
Definition: CameraDevice.C:33
jevois::CameraDevice::CameraDevice
CameraDevice(std::string const &devname, unsigned int const nbufs, bool dummy)
Constructor opens the device, checks its capabilities, starts run thread.
Definition: CameraDevice.C:76
jevois::CameraDevice::done
void done(RawImage &img)
Indicate that user processing is done with an image previously obtained via get()
Definition: CameraDevice.C:452
Async.H
LDEBUG
#define LDEBUG(msg)
Convenience macro for users to print out console or syslog messages, DEBUG level.
Definition: Log.H:160
RawImageOps.H
JEVOIS_TIMED_LOCK
#define JEVOIS_TIMED_LOCK(mtx)
Helper macro to create a timed_lock_guard object.
Definition: Log.H:304
FDLFATAL
#define FDLFATAL(msg)
Definition: CameraDevice.C:35
V4L2_MODE_VIDEO
#define V4L2_MODE_VIDEO
Definition: CameraDevice.C:39
FDLDEBUG
#define FDLDEBUG(msg)
Definition: CameraDevice.C:32
jevois::CameraDevice::abortStream
void abortStream()
Abort streaming.
Definition: CameraDevice.C:342
jevois::RawImage
A raw image as coming from a V4L2 Camera and/or being sent out to a USB Gadget.
Definition: RawImage.H:110
jevois::RawImage::bufindex
size_t bufindex
The index of the data buffer in the kernel driver.
Definition: RawImage.H:150
jevois::RawImage::fps
float fps
Programmed frames/s as given by current video mapping, may not be actual.
Definition: RawImage.H:148
jevois::CameraDevice::get
void get(RawImage &img)
Get the next captured buffer.
Definition: CameraDevice.C:399
JEVOIS_TRACE
#define JEVOIS_TRACE(level)
Trace object.
Definition: Log.H:272
jevois::CameraDevice::getFd
int getFd() const
Get our fd; used by Camera to access controls.
Definition: CameraDevice.C:168
CameraDevice.H
LERROR
#define LERROR(msg)
Convenience macro for users to print out console or syslog messages, ERROR level.
Definition: Log.H:198
jevois::RawImage::invalidate
void invalidate()
Invalidate the image by zero'ing out the pointer to pixel buffer and the dims and format.
Definition: RawImage.C:42
jevois::async_little
std::future< std::invoke_result_t< std::decay_t< Function >, std::decay_t< Args >... > > async_little(Function &&f, Args &&... args)
Async execution using a thread pool.
jevois::RawImage::width
unsigned int width
Image width in pixels.
Definition: RawImage.H:145
PLFATAL
#define PLFATAL(msg)
Like LDEBUG but appends errno and strerror(errno), to be used when some system call fails.
Definition: Log.H:226
F
float F
Definition: GUIhelper.C:2150
jevois::CameraDevice::streamOff
void streamOff()
Stop streaming.
Definition: CameraDevice.C:355
Log.H
ICM20948_regs.H
jevois::CameraDevice::~CameraDevice
~CameraDevice()
Destructor frees all buffers and closes the device.
Definition: CameraDevice.C:145
jevois::fccstr
std::string fccstr(unsigned int fcc)
Convert a V4L2 four-cc code (V4L2_PIX_FMT_...) to a 4-char string.
Definition: Utils.C:44
jevois::warnAndIgnoreException
std::string warnAndIgnoreException(std::string const &prefix="")
Convenience function to catch an exception, issue some LERROR (depending on type),...
Definition: Log.C:224
LFATAL
#define LFATAL(msg)
Convenience macro for users to print out console or syslog messages, FATAL level.
Definition: Log.H:217
VideoMapping.H
jevois::RawImage::height
unsigned int height
Image height in pixels.
Definition: RawImage.H:146
jevois::CameraDevice::streamOn
void streamOn()
Start streaming.
Definition: CameraDevice.C:293
jevois::RawImage::fmt
unsigned int fmt
Pixel format as a V4L2_PIX_FMT_XXX.
Definition: RawImage.H:147
Utils.H
jevois::RawImage::buf
std::shared_ptr< VideoBuf > buf
The pixel data buffer.
Definition: RawImage.H:149
jevois::CameraDevice::setFormat
void setFormat(unsigned int const fmt, unsigned int const capw, unsigned int const caph, float const fps, unsigned int const cropw, unsigned int const croph)
Set the video format and frame rate.
Definition: CameraDevice.C:467
LINFO
#define LINFO(msg)
Convenience macro for users to print out console or syslog messages, INFO level.
Definition: Log.H:181
jevois::VideoBuffers
Collection of buffers for V4L2 video frames (Camera or Gadget) with hooks to the MMAP'd areas.
Definition: VideoBuffers.H:42
jevois::rawimage::convertGreyToYUYV
void convertGreyToYUYV(RawImage const &src, RawImage &dst)
Convert from Grey (monochrome) to YUYV, only used internally by Camera class.
Definition: RawImageOps.C:1613
FDLERROR
#define FDLERROR(msg)
Definition: CameraDevice.C:34