JeVois  1.21
JeVois Smart Embedded Machine Vision Toolkit
Share this page:
Loading...
Searching...
No Matches
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
43namespace
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// ##############################################################################################################
76jevois::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// ##############################################################################################################
199void 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:
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// ##############################################################################################################
496void 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}
#define V4L2_COLORSPACE_DEFAULT
#define FDLERROR(msg)
#define FDLFATAL(msg)
#define FDLINFO(msg)
#define FDLDEBUG(msg)
#define V4L2_MODE_VIDEO
#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
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.
void get(RawImage &img)
Get the next captured buffer.
CameraDevice(std::string const &devname, unsigned int const nbufs, bool dummy)
Constructor opens the device, checks its capabilities, starts run thread.
int getFd() const
Get our fd; used by Camera to access controls.
void streamOff()
Stop streaming.
~CameraDevice()
Destructor frees all buffers and closes the device.
void done(RawImage &img)
Indicate that user processing is done with an image previously obtained via get()
void streamOn()
Start streaming.
void abortStream()
Abort streaming.
A raw image as coming from a V4L2 Camera and/or being sent out to a USB Gadget.
Definition RawImage.H:111
float fps
Programmed frames/s as given by current video mapping, may not be actual.
Definition RawImage.H:148
unsigned int fmt
Pixel format as a V4L2_PIX_FMT_XXX.
Definition RawImage.H:147
size_t bufindex
The index of the data buffer in the kernel driver.
Definition RawImage.H:150
void invalidate()
Invalidate the image by zero'ing out the pointer to pixel buffer and the dims and format.
Definition RawImage.C:42
unsigned int width
Image width in pixels.
Definition RawImage.H:145
unsigned int height
Image height in pixels.
Definition RawImage.H:146
std::shared_ptr< VideoBuf > buf
The pixel data buffer.
Definition RawImage.H:149
Collection of buffers for V4L2 video frames (Camera or Gadget) with hooks to the MMAP'd areas.
#define LFATAL(msg)
Convenience macro for users to print out console or syslog messages, FATAL level.
Definition Log.H:230
#define LDEBUG(msg)
Convenience macro for users to print out console or syslog messages, DEBUG level.
Definition Log.H:173
#define JEVOIS_TIMED_LOCK(mtx)
Helper macro to create a timed_lock_guard object.
Definition Log.H:328
std::string warnAndIgnoreException(std::string const &prefix="")
Convenience function to catch an exception, issue some LERROR (depending on type),...
Definition Log.C:236
#define PLFATAL(msg)
Like LDEBUG but appends errno and strerror(errno), to be used when some system call fails.
Definition Log.H:239
#define JEVOIS_TRACE(level)
Trace object.
Definition Log.H:296
#define LERROR(msg)
Convenience macro for users to print out console or syslog messages, ERROR level.
Definition Log.H:211
#define XIOCTL_QUIET(dev, req, mem)
Helper macro to execute an ioctl, ignore interruptions, and, if error throw quietly.
Definition Utils.H:216
std::string fccstr(unsigned int fcc)
Convert a V4L2 four-cc code (V4L2_PIX_FMT_...) to a 4-char string.
Definition Utils.C:45
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.
#define XIOCTL(dev, req, mem)
Helper macro to execute an ioctl, ignore interruptions, and, if error, issue a fatal message and thro...
Definition Utils.H:205
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
void convertBayerToYUYV(RawImage const &src, RawImage &dst)
Convert from Bayer to YUYV, only used internally by Camera class.
void convertGreyToYUYV(RawImage const &src, RawImage &dst)
Convert from Grey (monochrome) to YUYV, only used internally by Camera class.
static struct v4l2_fract fpsToV4l2(float fps)
Convert from fps to V4L2 interval.