blob: 84bf7fe67489ad5be666e3ab47c28e5a4dfc3209 [file] [log] [blame]
Parker Schuh58b39e82019-02-22 22:32:46 -08001#include "y2019/jevois/camera/reader.h"
2
Parker Schuh58b39e82019-02-22 22:32:46 -08003#include <fcntl.h>
4#include <malloc.h>
Parker Schuh58b39e82019-02-22 22:32:46 -08005#include <sys/mman.h>
6#include <sys/stat.h>
7#include <sys/time.h>
8#include <sys/types.h>
9#include <unistd.h>
10
Tyler Chatowbf0609c2021-07-31 16:13:27 -070011#include <cerrno>
12#include <cstdio>
13#include <cstdlib>
14#include <cstring>
15
Brian Silverman58899fd2019-03-24 11:03:11 -070016#include "glog/logging.h"
Parker Schuh58b39e82019-02-22 22:32:46 -080017
Philipp Schrader790cb542023-07-05 21:06:52 -070018#include "aos/time/time.h"
19
Parker Schuh58b39e82019-02-22 22:32:46 -080020#define CLEAR(x) memset(&(x), 0, sizeof(x))
21
22namespace y2019 {
23namespace camera {
24
25using ::camera::xioctl;
26
27struct Reader::Buffer {
28 void *start;
29 size_t length; // for munmap
30};
31
32aos::vision::CameraParams MakeCameraParams(int32_t width, int32_t height,
33 int32_t exposure, int32_t brightness,
34 int32_t gain, int32_t fps) {
35 aos::vision::CameraParams cam;
36 cam.set_width(width);
37 cam.set_height(height);
38 cam.set_exposure(exposure);
39 cam.set_brightness(brightness);
40 cam.set_gain(gain);
41 cam.set_fps(fps);
42 return cam;
43}
44
45Reader::Reader(const std::string &dev_name, ProcessCb process,
46 aos::vision::CameraParams params)
47 : dev_name_(dev_name), process_(std::move(process)), params_(params) {
48 struct stat st;
49 if (stat(dev_name.c_str(), &st) == -1) {
Brian Silverman58899fd2019-03-24 11:03:11 -070050 PLOG(FATAL) << "Cannot identify '" << dev_name << "'";
Parker Schuh58b39e82019-02-22 22:32:46 -080051 }
52 if (!S_ISCHR(st.st_mode)) {
Brian Silverman58899fd2019-03-24 11:03:11 -070053 PLOG(FATAL) << dev_name << " is no device";
Parker Schuh58b39e82019-02-22 22:32:46 -080054 }
55
56 fd_ = open(dev_name.c_str(), O_RDWR /* required */ | O_NONBLOCK, 0);
57 if (fd_ == -1) {
Brian Silverman58899fd2019-03-24 11:03:11 -070058 PLOG(FATAL) << "Cannot open '" << dev_name << "'";
Parker Schuh58b39e82019-02-22 22:32:46 -080059 }
60
61 Init();
62
63 InitMMap();
Brian Silverman58899fd2019-03-24 11:03:11 -070064 LOG(INFO) << "Bat Vision Successfully Initialized.";
Parker Schuh58b39e82019-02-22 22:32:46 -080065}
66
67void Reader::QueueBuffer(v4l2_buffer *buf) {
68 if (xioctl(fd_, VIDIOC_QBUF, buf) == -1) {
Brian Silverman58899fd2019-03-24 11:03:11 -070069 PLOG(WARNING) << "ioctl VIDIOC_QBUF(" << fd_ << ", " << &buf
70 << "). losing buf #" << buf->index;
Parker Schuh58b39e82019-02-22 22:32:46 -080071 } else {
72 ++queued_;
73 }
74}
75
76void Reader::HandleFrame() {
77 v4l2_buffer buf;
78 CLEAR(buf);
79 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
80 buf.memory = V4L2_MEMORY_MMAP;
81
82 if (xioctl(fd_, VIDIOC_DQBUF, &buf) == -1) {
83 if (errno != EAGAIN) {
Brian Silverman58899fd2019-03-24 11:03:11 -070084 PLOG(ERROR) << "ioctl VIDIOC_DQBUF(" << fd_ << ", " << &buf << ")";
Parker Schuh58b39e82019-02-22 22:32:46 -080085 }
86 return;
87 }
88 --queued_;
89
90 ++tick_id_;
91 // Get a timestamp now as proxy for when the image was taken
92 // TODO(ben): the image should come with a timestamp, parker
93 // will know how to get it.
94 auto time = aos::monotonic_clock::now();
95
96 process_(aos::vision::DataRef(
97 reinterpret_cast<const char *>(buffers_[buf.index].start),
98 buf.bytesused),
99 time);
100
101 QueueBuffer(&buf);
102}
103
104void Reader::MMapBuffers() {
105 buffers_ = new Buffer[kNumBuffers];
106 v4l2_buffer buf;
107 for (unsigned int n = 0; n < kNumBuffers; ++n) {
108 memset(&buf, 0, sizeof(buf));
109 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
110 buf.memory = V4L2_MEMORY_MMAP;
111 buf.index = n;
112 if (xioctl(fd_, VIDIOC_QUERYBUF, &buf) == -1) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700113 PLOG(FATAL) << "ioctl VIDIOC_QUERYBUF(" << fd_ << ", " << &buf << ")";
Parker Schuh58b39e82019-02-22 22:32:46 -0800114 }
115 buffers_[n].length = buf.length;
116 buffers_[n].start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE,
117 MAP_SHARED, fd_, buf.m.offset);
118 if (buffers_[n].start == MAP_FAILED) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700119 PLOG(FATAL) << "mmap(NULL, " << buf.length
120 << ", PROT_READ | PROT_WRITE, MAP_SHARED, " << fd_ << ", "
121 << buf.m.offset << ")";
Parker Schuh58b39e82019-02-22 22:32:46 -0800122 }
123 }
124}
125
126void Reader::InitMMap() {
127 v4l2_requestbuffers req;
128 CLEAR(req);
129 req.count = kNumBuffers;
130 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
131 req.memory = V4L2_MEMORY_MMAP;
132 if (xioctl(fd_, VIDIOC_REQBUFS, &req) == -1) {
133 if (EINVAL == errno) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700134 LOG(FATAL) << dev_name_ << " does not support memory mapping\n";
Parker Schuh58b39e82019-02-22 22:32:46 -0800135 } else {
Brian Silverman58899fd2019-03-24 11:03:11 -0700136 LOG(FATAL) << "ioctl VIDIOC_REQBUFS(" << fd_ << ", " << &req << ")";
Parker Schuh58b39e82019-02-22 22:32:46 -0800137 }
138 }
139 queued_ = kNumBuffers;
140 if (req.count != kNumBuffers) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700141 LOG(FATAL) << "Insufficient buffer memory on " << dev_name_;
Parker Schuh58b39e82019-02-22 22:32:46 -0800142 }
143}
144
145// Sets one of the camera's user-control values.
146// Prints the old and new values.
147// Just prints a message if the camera doesn't support this control or value.
148bool Reader::SetCameraControl(uint32_t id, const char *name, int value) {
149 struct v4l2_control getArg = {id, 0U};
150 int r;
151 // Used to be: r = xioctl(fd_, VIDIOC_S_CTRL, &getArg);
152 // Jevois wants this incorrect number below:.
153 r = xioctl(fd_, 0xc00c561b, &getArg);
154 if (r == 0) {
155 if (getArg.value == value) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700156 VLOG(1) << "Camera control " << name << " was already " << getArg.value;
Parker Schuh58b39e82019-02-22 22:32:46 -0800157 return true;
158 }
159 } else if (errno == EINVAL) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700160 VLOG(1) << "Camera control " << name << " is invalid";
Parker Schuh58b39e82019-02-22 22:32:46 -0800161 errno = 0;
162 return false;
163 }
164
165 struct v4l2_control setArg = {id, value};
166 // Should be: r = xioctl(fd_, VIDIOC_S_CTRL, &setArg);
167 // Jevois wants this incorrect number below:.
168 r = xioctl(fd_, 0xc00c561c, &setArg);
169 if (r == 0) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700170 VLOG(1) << "Set camera control " << name << " from " << getArg.value
171 << " to " << value;
Parker Schuh58b39e82019-02-22 22:32:46 -0800172 return true;
173 }
174
Brian Silverman58899fd2019-03-24 11:03:11 -0700175 VLOG(1) << "Couldn't set camera control " << name << " to " << value;
Parker Schuh58b39e82019-02-22 22:32:46 -0800176 errno = 0;
177 return false;
178}
179
180void Reader::Init() {
181 v4l2_capability cap;
182 if (xioctl(fd_, VIDIOC_QUERYCAP, &cap) == -1) {
183 if (EINVAL == errno) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700184 LOG(FATAL) << dev_name_ << " is no V4L2 device";
Parker Schuh58b39e82019-02-22 22:32:46 -0800185 } else {
Brian Silverman58899fd2019-03-24 11:03:11 -0700186 PLOG(FATAL) << "ioctl VIDIOC_QUERYCAP(" << fd_ << ", " << &cap << ")";
Parker Schuh58b39e82019-02-22 22:32:46 -0800187 }
188 }
189 if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700190 LOG(FATAL) << dev_name_ << " is no video capture device";
Parker Schuh58b39e82019-02-22 22:32:46 -0800191 }
192 if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700193 LOG(FATAL) << dev_name_ << " does not support streaming i/o";
Parker Schuh58b39e82019-02-22 22:32:46 -0800194 }
195
196 int camidx = -1;
197 struct v4l2_input inp = {};
198 while (true) {
199 if (xioctl(fd_, VIDIOC_ENUMINPUT, &inp) == -1) {
200 break;
201 }
202 if (inp.type == V4L2_INPUT_TYPE_CAMERA) {
203 if (camidx == -1) camidx = inp.index;
204 printf("Input %d [ %s ] is a camera sensor\n", inp.index, inp.name);
205 } else
206 printf("Input %d [ %s ] is not a camera sensor\n", inp.index, inp.name);
207 ++inp.index;
208 }
209
210 if (xioctl(fd_, VIDIOC_S_INPUT, &camidx) == -1) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700211 PLOG(FATAL) << "ioctl VIDIOC_S_INPUT(" << fd_ << ") failed";
Parker Schuh58b39e82019-02-22 22:32:46 -0800212 }
213 printf("camera idx: %d\n", camidx);
214
215 /* Select video input, video standard and tune here. */
216
217 v4l2_format fmt;
218 CLEAR(fmt);
219
220 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
221 if (xioctl(fd_, VIDIOC_G_FMT, &fmt) == -1) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700222 PLOG(FATAL) << "ioctl VIDIC_G_FMT(" << fd_ << ", " << &fmt << ") failed";
Parker Schuh58b39e82019-02-22 22:32:46 -0800223 }
224
225 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
226 fmt.fmt.pix.width = params_.width();
227 fmt.fmt.pix.height = params_.height();
228 printf("setting format: %d, %d\n", params_.width(), params_.height());
229 fmt.fmt.pix.field = V4L2_FIELD_NONE;
230 fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
231 if (xioctl(fd_, VIDIOC_S_FMT, &fmt) == -1) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700232 PLOG(FATAL) << "ioctl VIDIC_S_FMT(" << fd_ << ", " << &fmt << ") failed";
Parker Schuh58b39e82019-02-22 22:32:46 -0800233 }
234 /* Note VIDIOC_S_FMT may change width and height. */
235
236 /* Buggy driver paranoia. */
237 unsigned int min = fmt.fmt.pix.width * 2;
238 if (fmt.fmt.pix.bytesperline < min) fmt.fmt.pix.bytesperline = min;
239 min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
240 if (fmt.fmt.pix.sizeimage < min) fmt.fmt.pix.sizeimage = min;
241
242 // set framerate
243 struct v4l2_streamparm *setfps;
244 setfps = (struct v4l2_streamparm *)calloc(1, sizeof(struct v4l2_streamparm));
245 memset(setfps, 0, sizeof(struct v4l2_streamparm));
246 setfps->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
247 setfps->parm.capture.timeperframe.numerator = 1;
248 setfps->parm.capture.timeperframe.denominator = params_.fps();
249 if (xioctl(fd_, VIDIOC_S_PARM, setfps) == -1) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700250 PLOG(FATAL) << "ioctl VIDIOC_S_PARM(" << fd_ << ", " << setfps << ")";
Parker Schuh58b39e82019-02-22 22:32:46 -0800251 }
Brian Silverman58899fd2019-03-24 11:03:11 -0700252 LOG(INFO) << "framerate ended up at "
253 << setfps->parm.capture.timeperframe.numerator << "/"
254 << setfps->parm.capture.timeperframe.denominator;
Parker Schuh58b39e82019-02-22 22:32:46 -0800255
256 for (int j = 0; j < 2; ++j) {
257 if (!SetCameraControl(V4L2_CID_EXPOSURE_AUTO, "V4L2_CID_EXPOSURE_AUTO",
258 V4L2_EXPOSURE_MANUAL)) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700259 LOG(FATAL) << "Failed to set exposure";
Parker Schuh58b39e82019-02-22 22:32:46 -0800260 }
261
262 if (!SetCameraControl(V4L2_CID_EXPOSURE_ABSOLUTE,
263 "V4L2_CID_EXPOSURE_ABSOLUTE", params_.exposure())) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700264 LOG(FATAL) << "Failed to set exposure";
Parker Schuh58b39e82019-02-22 22:32:46 -0800265 }
266 sleep(1);
267 }
268}
269
270aos::vision::ImageFormat Reader::get_format() {
271 struct v4l2_format fmt;
272 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
273 if (xioctl(fd_, VIDIOC_G_FMT, &fmt) == -1) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700274 PLOG(FATAL) << "ioctl VIDIC_G_FMT(" << fd_ << ", " << &fmt << ")";
Parker Schuh58b39e82019-02-22 22:32:46 -0800275 }
276
277 return aos::vision::ImageFormat{(int)fmt.fmt.pix.width,
278 (int)fmt.fmt.pix.height};
279}
280
281void Reader::Start() {
Brian Silverman58899fd2019-03-24 11:03:11 -0700282 VLOG(1) << "queueing buffers for the first time";
Parker Schuh58b39e82019-02-22 22:32:46 -0800283 v4l2_buffer buf;
284 for (unsigned int i = 0; i < kNumBuffers; ++i) {
285 CLEAR(buf);
286 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
287 buf.memory = V4L2_MEMORY_MMAP;
288 buf.index = i;
289 QueueBuffer(&buf);
290 }
Brian Silverman58899fd2019-03-24 11:03:11 -0700291 VLOG(1) << "done with first queue";
Parker Schuh58b39e82019-02-22 22:32:46 -0800292
293 v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
294 if (xioctl(fd_, VIDIOC_STREAMON, &type) == -1) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700295 PLOG(FATAL) << "ioctl VIDIOC_STREAMON(" << fd_ << ", " << &type << ")";
Parker Schuh58b39e82019-02-22 22:32:46 -0800296 }
297}
298
299} // namespace camera
300} // namespace y2019