26 #include <sys/types.h>
30 #include <sys/select.h>
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)
38 #ifndef V4L2_MODE_VIDEO
39 #define V4L2_MODE_VIDEO 2
42 #ifdef JEVOIS_PLATFORM_A33
46 unsigned int v4l2sunxiFix(
unsigned int fcc)
51 case V4L2_PIX_FMT_YUYV:
return V4L2_PIX_FMT_YUYV;
54 case V4L2_PIX_FMT_GREY:
return V4L2_PIX_FMT_GREY;
57 case V4L2_PIX_FMT_SRGGB8:
return V4L2_PIX_FMT_SRGGB8;
60 case V4L2_PIX_FMT_RGB565:
return V4L2_PIX_FMT_RGB565;
62 case V4L2_PIX_FMT_MJPEG:
return V4L2_PIX_FMT_MJPEG;
64 case V4L2_PIX_FMT_BGR24:
return V4L2_PIX_FMT_BGR24;
72 #define V4L2_COLORSPACE_DEFAULT v4l2_colorspace(0)
77 itsDevName(devname), itsNbufs(nbufs), itsBuffers(nullptr), itsStreaming(false), itsFormatOk(false),
83 itsFd = open(devname.c_str(), O_RDWR | O_NONBLOCK, 0);
84 if (itsFd == -1)
LFATAL(
"Camera device open failed on " << devname);
88 struct v4l2_input inp = { };
91 try { XIOCTL_QUIET(itsFd, VIDIOC_ENUMINPUT, &inp); }
catch (...) {
break; }
92 if (inp.type == V4L2_INPUT_TYPE_CAMERA)
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");
100 if (camidx == -1)
FDLFATAL(
"No valid camera input");
103 XIOCTL(itsFd, VIDIOC_S_INPUT, &camidx);
106 struct v4l2_capability cap = { };
107 XIOCTL(itsFd, VIDIOC_QUERYCAP, &cap);
109 FDLINFO(
"V4L2 camera " << devname <<
" card " << cap.card <<
" bus " << cap.bus_info);
112 if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE) itsMplane =
true;
114 if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0 && itsMplane ==
false)
115 FDLFATAL(devname <<
" is not a video capture device");
117 if ((cap.capabilities & V4L2_CAP_STREAMING) == 0)
118 FDLFATAL(devname <<
" does not support streaming");
121 static bool showfmts =
true;
122 if (dummy ==
false && showfmts)
124 struct v4l2_fmtdesc fmtdesc { };
125 if (itsMplane) fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
else fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
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) <<
']');
132 std::string res =
" - Supports";
133 struct v4l2_frmsizeenum frsiz { };
134 frsiz.pixel_format = fmtdesc.pixelformat;
135 bool keepgoing =
true;
138 try { XIOCTL_QUIET(itsFd, VIDIOC_ENUM_FRAMESIZES, &frsiz); }
catch (...) {
break; }
142 case V4L2_FRMSIZE_TYPE_DISCRETE:
145 case V4L2_FRMSIZE_TYPE_STEPWISE:
146 res +=
" stepwize frame sizes";
149 case V4L2_FRMSIZE_TYPE_CONTINUOUS:
150 res +=
" continuous frame sizes";
167 while (itsRunning.load() ==
false) std::this_thread::sleep_for(std::chrono::milliseconds(1));
180 itsRunning.store(
false);
185 std::unique_lock lck(itsMtx, std::chrono::seconds(5));
186 if (lck.owns_lock() ==
false) {
FDLERROR(
"Timeout trying to acquire camera lock");
continue; }
188 if (itsBuffers)
delete itsBuffers;
189 if (itsFd != -1) close(itsFd);
199 void jevois::CameraDevice::run()
208 itsRunning.store(
true);
209 LDEBUG(
"run() thread ready");
217 std::vector<size_t> doneidx;
220 while (itsRunning.load())
228 if (itsDoneIdx.empty() ==
false) itsDoneIdx.swap(doneidx);
231 std::unique_lock lck(itsMtx, std::chrono::seconds(5));
232 if (lck.owns_lock() ==
false)
FDLFATAL(
"Timeout trying to acquire camera lock");
235 if (itsBuffers) {
for (
size_t idx : doneidx)
try { itsBuffers->qbuf(idx); }
catch (...) { } }
240 if (itsStreaming.load() ==
false)
243 std::this_thread::sleep_for(std::chrono::milliseconds(5));
249 if (itsBuffers && itsBuffers->nqueued() < 2)
251 LERROR(
"Running out of camera buffers - your process() function is too slow - DROPPING FRAMES");
252 size_t keep = 12345678;
257 if (itsOutputImage.valid()) keep = itsOutputImage.bufindex;
261 itsBuffers->qbufallbutone(keep);
265 FD_ZERO(&rfds); FD_ZERO(&efds); FD_SET(itsFd, &rfds); FD_SET(itsFd, &efds);
266 tv.tv_sec = 0; tv.tv_usec = 5000;
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"); }
272 if (FD_ISSET(itsFd, &efds))
FDLFATAL(
"Camera device error");
274 if (FD_ISSET(itsFd, &rfds))
277 struct v4l2_buffer buf;
278 itsBuffers->dqbuf(buf);
282 img.
width = itsFormat.fmt.pix.width;
283 img.
height = itsFormat.fmt.pix.height;
284 img.
fmt = itsFormat.fmt.pix.pixelformat;
286 img.
buf = itsBuffers->get(buf.index);
298 if (itsOutputImage.valid()) itsDoneIdx.push_back(itsOutputImage.bufindex);
301 itsOutputImage = img;
303 LDEBUG(
"Captured image " << img.
bufindex <<
" ready for processing");
306 itsOutputCondVar.notify_all();
310 std::this_thread::sleep_for(std::chrono::milliseconds(5));
316 itsRunning.store(
false);
324 LDEBUG(
"Turning on camera stream");
328 if (itsFormatOk ==
false)
FDLFATAL(
"No valid capture format was set -- ABORT");
330 if (itsStreaming.load() || itsBuffers) {
FDLERROR(
"Stream is already on -- IGNORED");
return; }
332 itsStreaming.store(
false);
335 unsigned int nbuf = itsNbufs;
338 unsigned int framesize =
jevois::v4l2ImageSize(itsFormat.fmt.pix.pixelformat, itsFormat.fmt.pix.width,
339 itsFormat.fmt.pix.height);
343 nbuf = (256U * 1024U * 1024U) / framesize;
346 nbuf = (4U * 1024U * 1024U) / framesize;
351 if (nbuf < 5) nbuf = 5;
else if (nbuf > 8) nbuf = 8;
354 v4l2_buf_type btype = itsMplane ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE : V4L2_BUF_TYPE_VIDEO_CAPTURE;
356 FDLINFO(itsBuffers->size() <<
" buffers of " << itsBuffers->get(0)->length() <<
" bytes allocated");
359 itsBuffers->qbufall();
360 FDLDEBUG(
"All buffers queued to camera driver");
363 XIOCTL(itsFd, VIDIOC_STREAMON, &btype);
366 itsStreaming.store(
true);
377 itsStreaming.store(
false);
380 for (
int i = 0; i < 20; ++i) itsOutputCondVar.notify_all();
391 FDLDEBUG(
"Turning off camera stream");
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...");
402 LDEBUG(
"Double-lock success.");
405 itsOutputImage.invalidate();
414 int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
415 try { XIOCTL_QUIET(itsFd, VIDIOC_STREAMOFF, &type); }
catch (...) { }
418 if (itsBuffers) {
delete itsBuffers; itsBuffers =
nullptr; }
422 for (
int i = 0; i < 20; ++i) itsOutputCondVar.notify_all();
432 if (itsConvertedOutputImage.valid())
435 std::unique_lock ulck(itsOutputMtx, std::chrono::seconds(5));
436 if (ulck.owns_lock() ==
false)
FDLFATAL(
"Timeout trying to acquire output lock");
438 if (itsOutputImage.valid() ==
false)
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");
445 if (itsStreaming.load() ==
false)
throw std::runtime_error(
"Camera not streaming");
447 switch (itsFormat.fmt.pix.pixelformat)
451 default:
FDLFATAL(
"Oops, cannot convert captured image");
454 img = itsConvertedOutputImage;
455 img.
bufindex = itsOutputImage.bufindex;
456 itsOutputImage.invalidate();
461 std::unique_lock ulck(itsOutputMtx, std::chrono::seconds(5));
462 if (ulck.owns_lock() ==
false)
FDLFATAL(
"Timeout trying to acquire output lock");
464 if (itsOutputImage.valid() ==
false)
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");
471 if (itsStreaming.load() ==
false)
throw std::runtime_error(
"Camera not streaming");
473 img = itsOutputImage;
477 LDEBUG(
"Camera image " << img.
bufindex <<
" handed over to processing");
485 if (itsStreaming.load() ==
false)
throw std::runtime_error(
"Camera done() rejected while not streaming");
497 float const fps,
unsigned int const cropw,
unsigned int const croph,
504 if (itsStreaming.load()) streamOff();
515 itsFormat.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
516 XIOCTL(itsFd, VIDIOC_G_FMT, &itsFormat);
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;
525 itsFormat.fmt.pix_mp.field = V4L2_FIELD_NONE;
527 FDLDEBUG(
"Requesting multiplane video format " << itsFormat.fmt.pix.width <<
'x' << itsFormat.fmt.pix.height
531 itsFormat.fmt.pix.width = capw;
532 itsFormat.fmt.pix.height = caph;
533 itsFormat.fmt.pix.pixelformat = fmt;
535 itsFormat.fmt.pix.field = V4L2_FIELD_NONE;
540 itsFormat.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
541 XIOCTL(itsFd, VIDIOC_G_FMT, &itsFormat);
544 itsFormat.fmt.pix.width = capw;
545 itsFormat.fmt.pix.height = caph;
546 itsFormat.fmt.pix.pixelformat = fmt;
548 itsFormat.fmt.pix.field = V4L2_FIELD_NONE;
550 FDLDEBUG(
"Requesting video format " << itsFormat.fmt.pix.width <<
'x' << itsFormat.fmt.pix.height <<
' ' <<
557 XIOCTL_QUIET(itsFd, VIDIOC_S_FMT, &itsFormat);
563 ". Maybe the sensor does not support requested pixel type or resolution.");
569 itsFormat.fmt.pix.pixelformat = V4L2_PIX_FMT_SRGGB8;
570 XIOCTL_QUIET(itsFd, VIDIOC_S_FMT, &itsFormat);
577 itsFormat.fmt.pix.pixelformat = V4L2_PIX_FMT_GREY;
578 XIOCTL_QUIET(itsFd, VIDIOC_S_FMT, &itsFormat);
583 ". Maybe the sensor does not support requested pixel type or resolution.");
590 XIOCTL(itsFd, VIDIOC_G_FMT, &itsFormat);
593 #ifdef JEVOIS_PLATFORM_A33
594 itsFormat.fmt.pix.pixelformat = v4l2sunxiFix(itsFormat.fmt.pix.pixelformat);
597 FDLINFO(
"Camera set video format to " << itsFormat.fmt.pix.width <<
'x' << itsFormat.fmt.pix.height <<
' ' <<
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");
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");
625 struct v4l2_cropcap cropcap = { };
626 cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
627 XIOCTL_QUIET(itsFd, VIDIOC_CROPCAP, &cropcap);
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 <<
')');
634 struct v4l2_crop crop = { };
635 crop.type = itsFormat.type;
636 if (capw == cropw && caph == croph)
637 crop.c = cropcap.defrect;
640 crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
641 crop.c.top = ((caph - croph) >> 1) & 0xfffc;
642 crop.c.left = ((capw - cropw) >> 1) & 0xfffc;
643 crop.c.width = cropw; crop.c.height = croph;
646 itsFormat.fmt.pix.width = cropw;
647 itsFormat.fmt.pix.height = croph;
650 XIOCTL_QUIET(itsFd, VIDIOC_S_CROP, &crop);
652 FDLINFO(
"Set cropping rectangle to " << crop.c.width <<
'x' << crop.c.height <<
653 " @ (" << crop.c.left <<
", " << crop.c.top <<
')');
655 catch (...) {
FDLERROR(
"Querying/setting crop rectangle not supported"); }
658 itsFormat.fmt.pix.width = cropw;
659 itsFormat.fmt.pix.height = croph;
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))
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);
679 struct v4l2_streamparm parms = { };
680 parms.type = itsFormat.type;
681 parms.parm.capture.timeperframe = jevois::VideoMapping::fpsToV4l2(fps);
683 XIOCTL(itsFd, VIDIOC_S_PARM, &parms);
685 FDLDEBUG(
"Set framerate to " << fps <<
" fps");
687 catch (...) {
FDLERROR(
"Setting frame rate to " << fps <<
" fps failed -- IGNORED"); }
693 FDLINFO(
"Loading sensor preset " << preset);
696 if (preset == 0) {
struct v4l2_control ctrl { 0xf0f003, 1 }; XIOCTL(itsFd, VIDIOC_S_CTRL, &ctrl); }
698 struct v4l2_control ctrl { 0xf0f003, preset };
699 XIOCTL(itsFd, VIDIOC_S_CTRL, &ctrl);