JeVois  1.20
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 and frame sizes, 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("Video format " << fmtdesc.index << " is [" << fmtdesc.description << "] fcc " << std::showbase <<
130  std::hex << fmtdesc.pixelformat << " [" << jevois::fccstr(fmtdesc.pixelformat) << ']');
131 
132  std::string res = " - Supports";
133  struct v4l2_frmsizeenum frsiz { };
134  frsiz.pixel_format = fmtdesc.pixelformat;
135  bool keepgoing = true;
136  while (keepgoing)
137  {
138  try { XIOCTL_QUIET(itsFd, VIDIOC_ENUM_FRAMESIZES, &frsiz); } catch (...) { break; }
139 
140  switch (frsiz.type)
141  {
142  case V4L2_FRMSIZE_TYPE_DISCRETE:
143  res += ' ' + std::to_string(frsiz.discrete.width) + 'x' + std::to_string(frsiz.discrete.height);
144  break;
145  case V4L2_FRMSIZE_TYPE_STEPWISE:
146  res += " stepwize frame sizes";
147  keepgoing = false;
148  break;
149  case V4L2_FRMSIZE_TYPE_CONTINUOUS:
150  res += " continuous frame sizes";
151  keepgoing = false;
152  break;
153  default: break;
154  }
155  ++frsiz.index;
156  }
157  FDLINFO(res);
158  ++fmtdesc.index;
159  }
160  showfmts = false;
161  }
162 
163  // Get our run() thread going and wait until it is cranking, it will flip itsRunning to true as it starts:
164  if (dummy == false)
165  {
166  itsRunFuture = jevois::async_little(std::bind(&jevois::CameraDevice::run, this));
167  while (itsRunning.load() == false) std::this_thread::sleep_for(std::chrono::milliseconds(1));
168  }
169 }
170 
171 // ##############################################################################################################
173 {
174  JEVOIS_TRACE(1);
175 
176  // Turn off streaming if it was on:
177  try { streamOff(); } catch (...) { jevois::warnAndIgnoreException(); }
178 
179  // Block until the run() thread completes:
180  itsRunning.store(false);
181  JEVOIS_WAIT_GET_FUTURE(itsRunFuture);
182 
183  while (true)
184  {
185  std::unique_lock lck(itsMtx, std::chrono::seconds(5));
186  if (lck.owns_lock() == false) { FDLERROR("Timeout trying to acquire camera lock"); continue; }
187 
188  if (itsBuffers) delete itsBuffers;
189  if (itsFd != -1) close(itsFd);
190  break;
191  }
192 }
193 
194 // ##############################################################################################################
196 { return itsFd; }
197 
198 // ##############################################################################################################
199 void jevois::CameraDevice::run()
200 {
201  JEVOIS_TRACE(1);
202 
203  fd_set rfds; // For new images captured
204  fd_set efds; // For errors
205  struct timeval tv;
206 
207  // Switch to running state:
208  itsRunning.store(true);
209  LDEBUG("run() thread ready");
210 
211  // NOTE: The flow is a little complex here, the goal is to minimize latency between a frame being captured and us
212  // dequeueing it from the driver and making it available to get(). To achieve low latency, we thus need to be polling
213  // the driver most of the time, and we need to prevent other threads from doing various ioctls while we are polling,
214  // as the SUNXI-VFE driver does not like that. Thus, there is high contention on itsMtx which we lock most of the
215  // time. For this reason we do a bit of sleeping with itsMtx unlocked at places where we know it will not increase our
216  // captured image delivery latency.
217  std::vector<size_t> doneidx;
218 
219  // Wait for events from the kernel driver and process them:
220  while (itsRunning.load())
221  try
222  {
223  // Requeue any done buffer. To avoid having to use a double lock on itsOutputMtx (for itsDoneIdx) and itsMtx (for
224  // itsBuffers->qbuf()), we just swap itsDoneIdx into a local variable here, and invalidate it, with itsOutputMtx
225  // locked, then we will do the qbuf() later, if needed, while itsMtx is locked:
226  {
227  JEVOIS_TIMED_LOCK(itsOutputMtx);
228  if (itsDoneIdx.empty() == false) itsDoneIdx.swap(doneidx);
229  }
230 
231  std::unique_lock lck(itsMtx, std::chrono::seconds(5));
232  if (lck.owns_lock() == false) FDLFATAL("Timeout trying to acquire camera lock");
233 
234  // Do the actual qbuf of any done buffer, ignoring any exception:
235  if (itsBuffers) { for (size_t idx : doneidx) try { itsBuffers->qbuf(idx); } catch (...) { } }
236  doneidx.clear();
237 
238  // SUNXI-VFE does not like to be polled when not streaming; if indeed we are not streaming, unlock and then sleep
239  // a bit to avoid too much contention on itsMtx:
240  if (itsStreaming.load() == false)
241  {
242  lck.unlock();
243  std::this_thread::sleep_for(std::chrono::milliseconds(5));
244  continue;
245  }
246 
247  // Check whether user code cannot keep up with the frame rate, and if so requeue all dequeued buffers except maybe
248  // the one currently associated with itsOutputImage:
249  if (itsBuffers && itsBuffers->nqueued() < 2)
250  {
251  LERROR("Running out of camera buffers - your process() function is too slow - DROPPING FRAMES");
252  size_t keep = 12345678;
253 
254  lck.unlock();
255  {
256  JEVOIS_TIMED_LOCK(itsOutputMtx);
257  if (itsOutputImage.valid()) keep = itsOutputImage.bufindex;
258  }
259  lck.lock();
260 
261  itsBuffers->qbufallbutone(keep);
262  }
263 
264  // Poll the device to wait for any new captured video frame:
265  FD_ZERO(&rfds); FD_ZERO(&efds); FD_SET(itsFd, &rfds); FD_SET(itsFd, &efds);
266  tv.tv_sec = 0; tv.tv_usec = 5000;
267 
268  int ret = select(itsFd + 1, &rfds, nullptr, &efds, &tv);
269  if (ret == -1) { FDLERROR("Select error"); if (errno == EINTR) continue; else PLFATAL("Error polling camera"); }
270  else if (ret > 0) // NOTE: ret == 0 would mean timeout
271  {
272  if (FD_ISSET(itsFd, &efds)) FDLFATAL("Camera device error");
273 
274  if (FD_ISSET(itsFd, &rfds))
275  {
276  // A new frame has been captured. Dequeue a buffer from the camera driver:
277  struct v4l2_buffer buf;
278  itsBuffers->dqbuf(buf);
279 
280  // Create a RawImage from that buffer:
281  jevois::RawImage img;
282  img.width = itsFormat.fmt.pix.width;
283  img.height = itsFormat.fmt.pix.height;
284  img.fmt = itsFormat.fmt.pix.pixelformat;
285  img.fps = itsFps;
286  img.buf = itsBuffers->get(buf.index);
287  img.bufindex = buf.index;
288 
289  // Unlock itsMtx:
290  lck.unlock();
291 
292  // We want to never block waiting for people to consume our grabbed frames here, hence we just overwrite our
293  // output image here, it just always contains the latest grabbed image:
294  {
295  JEVOIS_TIMED_LOCK(itsOutputMtx);
296 
297  // If user never called get()/done() on an image we already have, drop it and requeue the buffer:
298  if (itsOutputImage.valid()) itsDoneIdx.push_back(itsOutputImage.bufindex);
299 
300  // Set our new output image:
301  itsOutputImage = img;
302  }
303  LDEBUG("Captured image " << img.bufindex << " ready for processing");
304 
305  // Let anyone trying to get() our image know it's here:
306  itsOutputCondVar.notify_all();
307 
308  // This is also a good time to sleep a bit since it will take a while for the next frame to arrive, this
309  // should allow people who had been trying to get a lock on itsMtx to get it now:
310  std::this_thread::sleep_for(std::chrono::milliseconds(5));
311  }
312  }
313  } catch (...) { jevois::warnAndIgnoreException(); }
314 
315  // Switch out of running state in case we did interrupt the loop here by a break statement:
316  itsRunning.store(false);
317 }
318 
319 // ##############################################################################################################
321 {
322  JEVOIS_TRACE(2);
323 
324  LDEBUG("Turning on camera stream");
325 
326  JEVOIS_TIMED_LOCK(itsMtx);
327 
328  if (itsFormatOk == false) FDLFATAL("No valid capture format was set -- ABORT");
329 
330  if (itsStreaming.load() || itsBuffers) { FDLERROR("Stream is already on -- IGNORED"); return; }
331 
332  itsStreaming.store(false); // just in case user forgot to call abortStream()
333 
334  // If number of buffers is zero, adjust it depending on frame size:
335  unsigned int nbuf = itsNbufs;
336  if (nbuf == 0)
337  {
338  unsigned int framesize = jevois::v4l2ImageSize(itsFormat.fmt.pix.pixelformat, itsFormat.fmt.pix.width,
339  itsFormat.fmt.pix.height);
340 
341 #ifdef JEVOIS_PRO
342  // Aim for about 256 mbyte when using small images, and no more than 5 buffers in any case:
343  nbuf = (256U * 1024U * 1024U) / framesize;
344 #else
345  // Aim for about 4 mbyte when using small images, and no more than 5 buffers in any case:
346  nbuf = (4U * 1024U * 1024U) / framesize;
347 #endif
348  }
349 
350  // Force number of buffers to a sane value:
351  if (nbuf < 5) nbuf = 5; else if (nbuf > 8) nbuf = 8;
352 
353  // Allocate the buffers for our current video format:
354  v4l2_buf_type btype = itsMplane ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE : V4L2_BUF_TYPE_VIDEO_CAPTURE;
355  itsBuffers = new jevois::VideoBuffers("camera", itsFd, btype, nbuf);
356  FDLINFO(itsBuffers->size() << " buffers of " << itsBuffers->get(0)->length() << " bytes allocated");
357 
358  // Enqueue all our buffers:
359  itsBuffers->qbufall();
360  FDLDEBUG("All buffers queued to camera driver");
361 
362  // Start streaming at the device level:
363  XIOCTL(itsFd, VIDIOC_STREAMON, &btype);
364  FDLDEBUG("Device stream on");
365 
366  itsStreaming.store(true);
367  FDLDEBUG("Streaming is on");
368 }
369 
370 // ##############################################################################################################
372 {
373  JEVOIS_TRACE(2);
374 
375  // Set its Streaming to false here while unlocked, which will introduce some sleeping in our run() thread, thereby
376  // helping us acquire our needed double lock:
377  itsStreaming.store(false);
378 
379  // Unblock any get() that is waiting on itsOutputCondVar, it will then throw now that streaming is off:
380  for (int i = 0; i < 20; ++i) itsOutputCondVar.notify_all();
381 }
382 
383 // ##############################################################################################################
385 {
386  JEVOIS_TRACE(2);
387 
388  // Note: we allow for several streamOff() without complaining, this happens, e.g., when destroying a Camera that is
389  // not currently streaming.
390 
391  FDLDEBUG("Turning off camera stream");
392 
393  // Abort stream in case it was not already done, which will introduce some sleeping in our run() thread, thereby
394  // helping us acquire our needed double lock:
395  abortStream();
396 
397  // We need a double lock here so that we can both turn off the stream and nuke our output image and done idx:
398  std::unique_lock<std::timed_mutex> lk1(itsMtx, std::defer_lock);
399  std::unique_lock<std::timed_mutex> lk2(itsOutputMtx, std::defer_lock);
400  LDEBUG("Ready to double-lock...");
401  std::lock(lk1, lk2);
402  LDEBUG("Double-lock success.");
403 
404  // Invalidate our output image:
405  itsOutputImage.invalidate();
406 
407  // User may have called done() but our run() thread has not yet gotten to requeueing this image, if so requeue it here
408  // as it seems to keep the driver happier:
409  if (itsBuffers)
410  for (size_t idx : itsDoneIdx) try { itsBuffers->qbuf(idx); } catch (...) { jevois::warnAndIgnoreException(); }
411  itsDoneIdx.clear();
412 
413  // Stop streaming at the device level:
414  int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
415  try { XIOCTL_QUIET(itsFd, VIDIOC_STREAMOFF, &type); } catch (...) { }
416 
417  // Nuke all the buffers:
418  if (itsBuffers) { delete itsBuffers; itsBuffers = nullptr; }
419 
420  // Unblock any get() that is waiting on itsOutputCondVar, it will then throw now that streaming is off:
421  lk2.unlock();
422  for (int i = 0; i < 20; ++i) itsOutputCondVar.notify_all();
423 
424  FDLDEBUG("Camera stream is off");
425 }
426 
427 // ##############################################################################################################
429 {
430  JEVOIS_TRACE(4);
431 
432  if (itsConvertedOutputImage.valid())
433  {
434  // We need to convert from Bayer/Mono to YUYV:
435  std::unique_lock ulck(itsOutputMtx, std::chrono::seconds(5));
436  if (ulck.owns_lock() == false) FDLFATAL("Timeout trying to acquire output lock");
437 
438  if (itsOutputImage.valid() == false)
439  {
440  if (itsOutputCondVar.wait_for(ulck, std::chrono::milliseconds(2500),
441  [&]() { return itsOutputImage.valid() || itsStreaming.load() == false; }) == false)
442  throw std::runtime_error("Timeout waiting for camera frame or camera not streaming");
443  }
444 
445  if (itsStreaming.load() == false) throw std::runtime_error("Camera not streaming");
446 
447  switch (itsFormat.fmt.pix.pixelformat) // FIXME may need to protect this?
448  {
449  case V4L2_PIX_FMT_SRGGB8: jevois::rawimage::convertBayerToYUYV(itsOutputImage, itsConvertedOutputImage); break;
450  case V4L2_PIX_FMT_GREY: jevois::rawimage::convertGreyToYUYV(itsOutputImage, itsConvertedOutputImage); break;
451  default: FDLFATAL("Oops, cannot convert captured image");
452  }
453 
454  img = itsConvertedOutputImage;
455  img.bufindex = itsOutputImage.bufindex;
456  itsOutputImage.invalidate();
457  }
458  else
459  {
460  // Regular get() with no conversion:
461  std::unique_lock ulck(itsOutputMtx, std::chrono::seconds(5));
462  if (ulck.owns_lock() == false) FDLFATAL("Timeout trying to acquire output lock");
463 
464  if (itsOutputImage.valid() == false)
465  {
466  if (itsOutputCondVar.wait_for(ulck, std::chrono::milliseconds(2500),
467  [&]() { return itsOutputImage.valid() || itsStreaming.load() == false; }) == false)
468  throw std::runtime_error("Timeout waiting for camera frame or camera not streaming");
469  }
470 
471  if (itsStreaming.load() == false) throw std::runtime_error("Camera not streaming");
472 
473  img = itsOutputImage;
474  itsOutputImage.invalidate();
475  }
476 
477  LDEBUG("Camera image " << img.bufindex << " handed over to processing");
478 }
479 
480 // ##############################################################################################################
482 {
483  JEVOIS_TRACE(4);
484 
485  if (itsStreaming.load() == false) throw std::runtime_error("Camera done() rejected while not streaming");
486 
487  // To avoid blocking for a long time here, we do not try to lock itsMtx and to qbuf() the buffer right now, instead we
488  // just make a note that this buffer is available and it will be requeued by our run() thread:
489  JEVOIS_TIMED_LOCK(itsOutputMtx);
490  itsDoneIdx.push_back(img.bufindex);
491 
492  LDEBUG("Image " << img.bufindex << " freed by processing");
493 }
494 
495 // ##############################################################################################################
496 void jevois::CameraDevice::setFormat(unsigned int const fmt, unsigned int const capw, unsigned int const caph,
497  float const fps, unsigned int const cropw, unsigned int const croph,
498  int preset)
499 {
500  JEVOIS_TRACE(2);
501 
502  // We may be streaming, eg, if we were running a mapping with no USB out and then the user starts a video grabber. So
503  // make sure we stream off first:
504  if (itsStreaming.load()) streamOff();
505 
506  JEVOIS_TIMED_LOCK(itsMtx);
507 
508  // Assume format not set in case we exit on exception:
509  itsFormatOk = false;
510 
511  // Set desired format:
512  if (itsMplane)
513  {
514  // Get current format:
515  itsFormat.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
516  XIOCTL(itsFd, VIDIOC_G_FMT, &itsFormat);
517 
518  // Set desired format:
519  // see https://chromium.googlesource.com/chromiumos/platform/cros-yavta/+/refs/heads/upstream/master/yavta.c
520  itsFormat.fmt.pix_mp.width = capw;
521  itsFormat.fmt.pix_mp.height = caph;
522  itsFormat.fmt.pix_mp.pixelformat = fmt;
523  itsFormat.fmt.pix_mp.num_planes = 1; // FIXME force to 1 plane, likely will break NV12 support
524  itsFormat.fmt.pix_mp.colorspace = V4L2_COLORSPACE_DEFAULT;
525  itsFormat.fmt.pix_mp.field = V4L2_FIELD_NONE;
526  itsFps = fps;
527  FDLDEBUG("Requesting multiplane video format " << itsFormat.fmt.pix.width << 'x' << itsFormat.fmt.pix.height
528  << ' ' << jevois::fccstr(itsFormat.fmt.pix.pixelformat));
529 
530  // Amlogic kernel bugfix: still set the regular fields:
531  itsFormat.fmt.pix.width = capw;
532  itsFormat.fmt.pix.height = caph;
533  itsFormat.fmt.pix.pixelformat = fmt;
534  itsFormat.fmt.pix.colorspace = V4L2_COLORSPACE_DEFAULT;
535  itsFormat.fmt.pix.field = V4L2_FIELD_NONE;
536  }
537  else
538  {
539  // Get current format:
540  itsFormat.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
541  XIOCTL(itsFd, VIDIOC_G_FMT, &itsFormat);
542 
543  // Set desired format:
544  itsFormat.fmt.pix.width = capw;
545  itsFormat.fmt.pix.height = caph;
546  itsFormat.fmt.pix.pixelformat = fmt;
547  itsFormat.fmt.pix.colorspace = V4L2_COLORSPACE_DEFAULT;
548  itsFormat.fmt.pix.field = V4L2_FIELD_NONE;
549  itsFps = fps;
550  FDLDEBUG("Requesting video format " << itsFormat.fmt.pix.width << 'x' << itsFormat.fmt.pix.height << ' ' <<
551  jevois::fccstr(itsFormat.fmt.pix.pixelformat));
552  }
553 
554  // Try to set the format. If it fails, try to see whether we can use BAYER or MONO instead, and we will convert:
555  try
556  {
557  XIOCTL_QUIET(itsFd, VIDIOC_S_FMT, &itsFormat);
558  }
559  catch (...)
560  {
561  if (itsMplane)
562  FDLFATAL("Could not set camera format to " << capw << 'x' << caph << ' ' << jevois::fccstr(fmt) <<
563  ". Maybe the sensor does not support requested pixel type or resolution.");
564  else
565  {
566  try
567  {
568  // Oops, maybe this sensor only supports raw Bayer:
569  itsFormat.fmt.pix.pixelformat = V4L2_PIX_FMT_SRGGB8;
570  XIOCTL_QUIET(itsFd, VIDIOC_S_FMT, &itsFormat);
571  }
572  catch (...)
573  {
574  try
575  {
576  // Oops, maybe this sensor only supports monochrome:
577  itsFormat.fmt.pix.pixelformat = V4L2_PIX_FMT_GREY;
578  XIOCTL_QUIET(itsFd, VIDIOC_S_FMT, &itsFormat);
579  }
580  catch (...)
581  {
582  FDLFATAL("Could not set camera format to " << capw << 'x' << caph << ' ' << jevois::fccstr(fmt) <<
583  ". Maybe the sensor does not support requested pixel type or resolution.");
584  }
585  }
586  }
587  }
588 
589  // Get the format back as the driver may have adjusted some sizes, etc:
590  XIOCTL(itsFd, VIDIOC_G_FMT, &itsFormat);
591 
592  // The sunxi driver on JeVois-A33 returns a different format code, may be the mbus code instead of the v4l2 fcc...
593 #ifdef JEVOIS_PLATFORM_A33
594  itsFormat.fmt.pix.pixelformat = v4l2sunxiFix(itsFormat.fmt.pix.pixelformat);
595 #endif
596 
597  FDLINFO("Camera set video format to " << itsFormat.fmt.pix.width << 'x' << itsFormat.fmt.pix.height << ' ' <<
598  jevois::fccstr(itsFormat.fmt.pix.pixelformat));
599 
600  // Because modules may rely on the exact format that they request, throw if the camera modified it:
601  if (itsMplane)
602  {
603  if (itsFormat.fmt.pix_mp.width != capw ||
604  itsFormat.fmt.pix_mp.height != caph ||
605  itsFormat.fmt.pix_mp.pixelformat != fmt)
606  FDLFATAL("Camera did not accept the requested video format as specified");
607  }
608  else
609  {
610  if (itsFormat.fmt.pix.width != capw ||
611  itsFormat.fmt.pix.height != caph ||
612  (itsFormat.fmt.pix.pixelformat != fmt &&
613  (fmt != V4L2_PIX_FMT_YUYV ||
614  (itsFormat.fmt.pix.pixelformat != V4L2_PIX_FMT_SRGGB8 &&
615  itsFormat.fmt.pix.pixelformat != V4L2_PIX_FMT_GREY))))
616  FDLFATAL("Camera did not accept the requested video format as specified");
617  }
618 
619  // Reset cropping parameters. NOTE: just open()'ing the device does not reset it, according to the unix toolchain
620  // philosophy. Hence, although here we do not provide support for cropping, we still need to ensure that it is
621  // properly reset. Note that some cameras do not support this so here we swallow that exception:
622  if (fps > 0.0F)
623  try
624  {
625  struct v4l2_cropcap cropcap = { };
626  cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; // Note: kernel docs say do not use the MPLANE type here.
627  XIOCTL_QUIET(itsFd, VIDIOC_CROPCAP, &cropcap);
628 
629  FDLDEBUG("Cropcap bounds " << cropcap.bounds.width << 'x' << cropcap.bounds.height <<
630  " @ (" << cropcap.bounds.left << ", " << cropcap.bounds.top << ')');
631  FDLDEBUG("Cropcap defrect " << cropcap.defrect.width << 'x' << cropcap.defrect.height <<
632  " @ (" << cropcap.defrect.left << ", " << cropcap.defrect.top << ')');
633 
634  struct v4l2_crop crop = { };
635  crop.type = itsFormat.type;
636  if (capw == cropw && caph == croph)
637  crop.c = cropcap.defrect;
638  else
639  {
640  crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; // Note: kernel docs say do not use the MPLANE type here.
641  crop.c.top = ((caph - croph) >> 1) & 0xfffc; // force multiple of 4
642  crop.c.left = ((capw - cropw) >> 1) & 0xfffc;
643  crop.c.width = cropw; crop.c.height = croph;
644 
645  // From now on, as far as we are concerned, these are the capture width and height:
646  itsFormat.fmt.pix.width = cropw;
647  itsFormat.fmt.pix.height = croph;
648  }
649 
650  XIOCTL_QUIET(itsFd, VIDIOC_S_CROP, &crop);
651 
652  FDLINFO("Set cropping rectangle to " << crop.c.width << 'x' << crop.c.height <<
653  " @ (" << crop.c.left << ", " << crop.c.top << ')');
654  }
655  catch (...) { FDLERROR("Querying/setting crop rectangle not supported"); }
656 
657  // From now on, as far as we are concerned, these are the capture width and height:
658  itsFormat.fmt.pix.width = cropw;
659  itsFormat.fmt.pix.height = croph;
660 
661  // Allocate a RawImage for conversion from Bayer or Monochrome to YUYV if needed:
662  itsConvertedOutputImage.invalidate();
663  if (itsMplane == false && fmt == V4L2_PIX_FMT_YUYV &&
664  (itsFormat.fmt.pix.pixelformat == V4L2_PIX_FMT_SRGGB8 || itsFormat.fmt.pix.pixelformat == V4L2_PIX_FMT_GREY))
665  {
666  // We will grab raw Bayer/Mono and store that into itsOutputImage, finally converting to YUYV in get():
667  itsConvertedOutputImage.width = itsFormat.fmt.pix.width;
668  itsConvertedOutputImage.height = itsFormat.fmt.pix.height;
669  itsConvertedOutputImage.fmt = V4L2_PIX_FMT_YUYV;
670  itsConvertedOutputImage.fps = itsFps;
671  itsConvertedOutputImage.buf = std::make_shared<jevois::VideoBuf>(-1, itsConvertedOutputImage.bytesize(), 0, -1);
672  }
673 
674 #ifndef JEVOIS_PRO
675  // Set frame rate:
676  if (fps > 0.0F)
677  try
678  {
679  struct v4l2_streamparm parms = { };
680  parms.type = itsFormat.type;
681  parms.parm.capture.timeperframe = jevois::VideoMapping::fpsToV4l2(fps);
682  parms.parm.capture.capturemode = V4L2_MODE_VIDEO;
683  XIOCTL(itsFd, VIDIOC_S_PARM, &parms);
684 
685  FDLDEBUG("Set framerate to " << fps << " fps");
686  }
687  catch (...) { FDLERROR("Setting frame rate to " << fps << " fps failed -- IGNORED"); }
688 #endif
689 
690  // Load any low-level camera sensor preset register sequence:
691  if (preset != -1)
692  {
693  FDLINFO("Loading sensor preset " << preset);
694 
695  // Bugfix for when loading preset 0: the kernel driver will ignore the request unless we set non-zero preset first:
696  if (preset == 0) { struct v4l2_control ctrl { 0xf0f003, 1 }; XIOCTL(itsFd, VIDIOC_S_CTRL, &ctrl); }
697 
698  struct v4l2_control ctrl { 0xf0f003, preset }; // 0xf0f003 = ispsensorpreset
699  XIOCTL(itsFd, VIDIOC_S_CTRL, &ctrl);
700  }
701 
702  // All good, note that we succeeded:
703  itsFormatOk = true;
704 }
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:336
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:481
Async.H
LDEBUG
#define LDEBUG(msg)
Convenience macro for users to print out console or syslog messages, DEBUG level.
Definition: Log.H:173
RawImageOps.H
JEVOIS_TIMED_LOCK
#define JEVOIS_TIMED_LOCK(mtx)
Helper macro to create a timed_lock_guard object.
Definition: Log.H:328
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::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, int preset=-1)
Set the video format and frame rate.
Definition: CameraDevice.C:496
jevois::CameraDevice::abortStream
void abortStream()
Abort streaming.
Definition: CameraDevice.C:371
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:428
JEVOIS_TRACE
#define JEVOIS_TRACE(level)
Trace object.
Definition: Log.H:296
jevois::CameraDevice::getFd
int getFd() const
Get our fd; used by Camera to access controls.
Definition: CameraDevice.C:195
CameraDevice.H
LERROR
#define LERROR(msg)
Convenience macro for users to print out console or syslog messages, ERROR level.
Definition: Log.H:211
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::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:239
F
float F
Definition: GUIhelper.C:2373
jevois::CameraDevice::streamOff
void streamOff()
Stop streaming.
Definition: CameraDevice.C:384
Log.H
ICM20948_regs.H
jevois::CameraDevice::~CameraDevice
~CameraDevice()
Destructor frees all buffers and closes the device.
Definition: CameraDevice.C:172
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:45
jevois::warnAndIgnoreException
std::string warnAndIgnoreException(std::string const &prefix="")
Convenience function to catch an exception, issue some LERROR (depending on type),...
Definition: Log.C:236
LFATAL
#define LFATAL(msg)
Convenience macro for users to print out console or syslog messages, FATAL level.
VideoMapping.H
jevois::RawImage::height
unsigned int height
Image height in pixels.
Definition: RawImage.H:146
jevois::to_string
std::string to_string(T const &val)
Convert from type to string.
V4L2_COLORSPACE_DEFAULT
#define V4L2_COLORSPACE_DEFAULT
Definition: CameraDevice.C:72
jevois::CameraDevice::streamOn
void streamOn()
Start streaming.
Definition: CameraDevice.C:320
jevois::RawImage::fmt
unsigned int fmt
Pixel format as a V4L2_PIX_FMT_XXX.
Definition: RawImage.H:147
Utils.H
jevois::v4l2ImageSize
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:168
jevois::RawImage::buf
std::shared_ptr< VideoBuf > buf
The pixel data buffer.
Definition: RawImage.H:149
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::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