blob: 259e650c1431914f978616c1019c3b1d0add270e [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
Parker Schuh58b39e82019-02-22 22:32:46 -080016#include "aos/time/time.h"
Brian Silverman58899fd2019-03-24 11:03:11 -070017#include "glog/logging.h"
Parker Schuh58b39e82019-02-22 22:32:46 -080018
19#define CLEAR(x) memset(&(x), 0, sizeof(x))
20
21namespace y2019 {
22namespace camera {
23
24using ::camera::xioctl;
25
26struct Reader::Buffer {
27 void *start;
28 size_t length; // for munmap
29};
30
31aos::vision::CameraParams MakeCameraParams(int32_t width, int32_t height,
32 int32_t exposure, int32_t brightness,
33 int32_t gain, int32_t fps) {
34 aos::vision::CameraParams cam;
35 cam.set_width(width);
36 cam.set_height(height);
37 cam.set_exposure(exposure);
38 cam.set_brightness(brightness);
39 cam.set_gain(gain);
40 cam.set_fps(fps);
41 return cam;
42}
43
44Reader::Reader(const std::string &dev_name, ProcessCb process,
45 aos::vision::CameraParams params)
46 : dev_name_(dev_name), process_(std::move(process)), params_(params) {
47 struct stat st;
48 if (stat(dev_name.c_str(), &st) == -1) {
Brian Silverman58899fd2019-03-24 11:03:11 -070049 PLOG(FATAL) << "Cannot identify '" << dev_name << "'";
Parker Schuh58b39e82019-02-22 22:32:46 -080050 }
51 if (!S_ISCHR(st.st_mode)) {
Brian Silverman58899fd2019-03-24 11:03:11 -070052 PLOG(FATAL) << dev_name << " is no device";
Parker Schuh58b39e82019-02-22 22:32:46 -080053 }
54
55 fd_ = open(dev_name.c_str(), O_RDWR /* required */ | O_NONBLOCK, 0);
56 if (fd_ == -1) {
Brian Silverman58899fd2019-03-24 11:03:11 -070057 PLOG(FATAL) << "Cannot open '" << dev_name << "'";
Parker Schuh58b39e82019-02-22 22:32:46 -080058 }
59
60 Init();
61
62 InitMMap();
Brian Silverman58899fd2019-03-24 11:03:11 -070063 LOG(INFO) << "Bat Vision Successfully Initialized.";
Parker Schuh58b39e82019-02-22 22:32:46 -080064}
65
66void Reader::QueueBuffer(v4l2_buffer *buf) {
67 if (xioctl(fd_, VIDIOC_QBUF, buf) == -1) {
Brian Silverman58899fd2019-03-24 11:03:11 -070068 PLOG(WARNING) << "ioctl VIDIOC_QBUF(" << fd_ << ", " << &buf
69 << "). losing buf #" << buf->index;
Parker Schuh58b39e82019-02-22 22:32:46 -080070 } else {
71 ++queued_;
72 }
73}
74
75void Reader::HandleFrame() {
76 v4l2_buffer buf;
77 CLEAR(buf);
78 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
79 buf.memory = V4L2_MEMORY_MMAP;
80
81 if (xioctl(fd_, VIDIOC_DQBUF, &buf) == -1) {
82 if (errno != EAGAIN) {
Brian Silverman58899fd2019-03-24 11:03:11 -070083 PLOG(ERROR) << "ioctl VIDIOC_DQBUF(" << fd_ << ", " << &buf << ")";
Parker Schuh58b39e82019-02-22 22:32:46 -080084 }
85 return;
86 }
87 --queued_;
88
89 ++tick_id_;
90 // Get a timestamp now as proxy for when the image was taken
91 // TODO(ben): the image should come with a timestamp, parker
92 // will know how to get it.
93 auto time = aos::monotonic_clock::now();
94
95 process_(aos::vision::DataRef(
96 reinterpret_cast<const char *>(buffers_[buf.index].start),
97 buf.bytesused),
98 time);
99
100 QueueBuffer(&buf);
101}
102
103void Reader::MMapBuffers() {
104 buffers_ = new Buffer[kNumBuffers];
105 v4l2_buffer buf;
106 for (unsigned int n = 0; n < kNumBuffers; ++n) {
107 memset(&buf, 0, sizeof(buf));
108 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
109 buf.memory = V4L2_MEMORY_MMAP;
110 buf.index = n;
111 if (xioctl(fd_, VIDIOC_QUERYBUF, &buf) == -1) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700112 PLOG(FATAL) << "ioctl VIDIOC_QUERYBUF(" << fd_ << ", " << &buf << ")";
Parker Schuh58b39e82019-02-22 22:32:46 -0800113 }
114 buffers_[n].length = buf.length;
115 buffers_[n].start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE,
116 MAP_SHARED, fd_, buf.m.offset);
117 if (buffers_[n].start == MAP_FAILED) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700118 PLOG(FATAL) << "mmap(NULL, " << buf.length
119 << ", PROT_READ | PROT_WRITE, MAP_SHARED, " << fd_ << ", "
120 << buf.m.offset << ")";
Parker Schuh58b39e82019-02-22 22:32:46 -0800121 }
122 }
123}
124
125void Reader::InitMMap() {
126 v4l2_requestbuffers req;
127 CLEAR(req);
128 req.count = kNumBuffers;
129 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
130 req.memory = V4L2_MEMORY_MMAP;
131 if (xioctl(fd_, VIDIOC_REQBUFS, &req) == -1) {
132 if (EINVAL == errno) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700133 LOG(FATAL) << dev_name_ << " does not support memory mapping\n";
Parker Schuh58b39e82019-02-22 22:32:46 -0800134 } else {
Brian Silverman58899fd2019-03-24 11:03:11 -0700135 LOG(FATAL) << "ioctl VIDIOC_REQBUFS(" << fd_ << ", " << &req << ")";
Parker Schuh58b39e82019-02-22 22:32:46 -0800136 }
137 }
138 queued_ = kNumBuffers;
139 if (req.count != kNumBuffers) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700140 LOG(FATAL) << "Insufficient buffer memory on " << dev_name_;
Parker Schuh58b39e82019-02-22 22:32:46 -0800141 }
142}
143
144// Sets one of the camera's user-control values.
145// Prints the old and new values.
146// Just prints a message if the camera doesn't support this control or value.
147bool Reader::SetCameraControl(uint32_t id, const char *name, int value) {
148 struct v4l2_control getArg = {id, 0U};
149 int r;
150 // Used to be: r = xioctl(fd_, VIDIOC_S_CTRL, &getArg);
151 // Jevois wants this incorrect number below:.
152 r = xioctl(fd_, 0xc00c561b, &getArg);
153 if (r == 0) {
154 if (getArg.value == value) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700155 VLOG(1) << "Camera control " << name << " was already " << getArg.value;
Parker Schuh58b39e82019-02-22 22:32:46 -0800156 return true;
157 }
158 } else if (errno == EINVAL) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700159 VLOG(1) << "Camera control " << name << " is invalid";
Parker Schuh58b39e82019-02-22 22:32:46 -0800160 errno = 0;
161 return false;
162 }
163
164 struct v4l2_control setArg = {id, value};
165 // Should be: r = xioctl(fd_, VIDIOC_S_CTRL, &setArg);
166 // Jevois wants this incorrect number below:.
167 r = xioctl(fd_, 0xc00c561c, &setArg);
168 if (r == 0) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700169 VLOG(1) << "Set camera control " << name << " from " << getArg.value
170 << " to " << value;
Parker Schuh58b39e82019-02-22 22:32:46 -0800171 return true;
172 }
173
Brian Silverman58899fd2019-03-24 11:03:11 -0700174 VLOG(1) << "Couldn't set camera control " << name << " to " << value;
Parker Schuh58b39e82019-02-22 22:32:46 -0800175 errno = 0;
176 return false;
177}
178
179void Reader::Init() {
180 v4l2_capability cap;
181 if (xioctl(fd_, VIDIOC_QUERYCAP, &cap) == -1) {
182 if (EINVAL == errno) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700183 LOG(FATAL) << dev_name_ << " is no V4L2 device";
Parker Schuh58b39e82019-02-22 22:32:46 -0800184 } else {
Brian Silverman58899fd2019-03-24 11:03:11 -0700185 PLOG(FATAL) << "ioctl VIDIOC_QUERYCAP(" << fd_ << ", " << &cap << ")";
Parker Schuh58b39e82019-02-22 22:32:46 -0800186 }
187 }
188 if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700189 LOG(FATAL) << dev_name_ << " is no video capture device";
Parker Schuh58b39e82019-02-22 22:32:46 -0800190 }
191 if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700192 LOG(FATAL) << dev_name_ << " does not support streaming i/o";
Parker Schuh58b39e82019-02-22 22:32:46 -0800193 }
194
195 int camidx = -1;
196 struct v4l2_input inp = {};
197 while (true) {
198 if (xioctl(fd_, VIDIOC_ENUMINPUT, &inp) == -1) {
199 break;
200 }
201 if (inp.type == V4L2_INPUT_TYPE_CAMERA) {
202 if (camidx == -1) camidx = inp.index;
203 printf("Input %d [ %s ] is a camera sensor\n", inp.index, inp.name);
204 } else
205 printf("Input %d [ %s ] is not a camera sensor\n", inp.index, inp.name);
206 ++inp.index;
207 }
208
209 if (xioctl(fd_, VIDIOC_S_INPUT, &camidx) == -1) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700210 PLOG(FATAL) << "ioctl VIDIOC_S_INPUT(" << fd_ << ") failed";
Parker Schuh58b39e82019-02-22 22:32:46 -0800211 }
212 printf("camera idx: %d\n", camidx);
213
214 /* Select video input, video standard and tune here. */
215
216 v4l2_format fmt;
217 CLEAR(fmt);
218
219 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
220 if (xioctl(fd_, VIDIOC_G_FMT, &fmt) == -1) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700221 PLOG(FATAL) << "ioctl VIDIC_G_FMT(" << fd_ << ", " << &fmt << ") failed";
Parker Schuh58b39e82019-02-22 22:32:46 -0800222 }
223
224 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
225 fmt.fmt.pix.width = params_.width();
226 fmt.fmt.pix.height = params_.height();
227 printf("setting format: %d, %d\n", params_.width(), params_.height());
228 fmt.fmt.pix.field = V4L2_FIELD_NONE;
229 fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
230 if (xioctl(fd_, VIDIOC_S_FMT, &fmt) == -1) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700231 PLOG(FATAL) << "ioctl VIDIC_S_FMT(" << fd_ << ", " << &fmt << ") failed";
Parker Schuh58b39e82019-02-22 22:32:46 -0800232 }
233 /* Note VIDIOC_S_FMT may change width and height. */
234
235 /* Buggy driver paranoia. */
236 unsigned int min = fmt.fmt.pix.width * 2;
237 if (fmt.fmt.pix.bytesperline < min) fmt.fmt.pix.bytesperline = min;
238 min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
239 if (fmt.fmt.pix.sizeimage < min) fmt.fmt.pix.sizeimage = min;
240
241 // set framerate
242 struct v4l2_streamparm *setfps;
243 setfps = (struct v4l2_streamparm *)calloc(1, sizeof(struct v4l2_streamparm));
244 memset(setfps, 0, sizeof(struct v4l2_streamparm));
245 setfps->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
246 setfps->parm.capture.timeperframe.numerator = 1;
247 setfps->parm.capture.timeperframe.denominator = params_.fps();
248 if (xioctl(fd_, VIDIOC_S_PARM, setfps) == -1) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700249 PLOG(FATAL) << "ioctl VIDIOC_S_PARM(" << fd_ << ", " << setfps << ")";
Parker Schuh58b39e82019-02-22 22:32:46 -0800250 }
Brian Silverman58899fd2019-03-24 11:03:11 -0700251 LOG(INFO) << "framerate ended up at "
252 << setfps->parm.capture.timeperframe.numerator << "/"
253 << setfps->parm.capture.timeperframe.denominator;
Parker Schuh58b39e82019-02-22 22:32:46 -0800254
255 for (int j = 0; j < 2; ++j) {
256 if (!SetCameraControl(V4L2_CID_EXPOSURE_AUTO, "V4L2_CID_EXPOSURE_AUTO",
257 V4L2_EXPOSURE_MANUAL)) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700258 LOG(FATAL) << "Failed to set exposure";
Parker Schuh58b39e82019-02-22 22:32:46 -0800259 }
260
261 if (!SetCameraControl(V4L2_CID_EXPOSURE_ABSOLUTE,
262 "V4L2_CID_EXPOSURE_ABSOLUTE", params_.exposure())) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700263 LOG(FATAL) << "Failed to set exposure";
Parker Schuh58b39e82019-02-22 22:32:46 -0800264 }
265 sleep(1);
266 }
267}
268
269aos::vision::ImageFormat Reader::get_format() {
270 struct v4l2_format fmt;
271 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
272 if (xioctl(fd_, VIDIOC_G_FMT, &fmt) == -1) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700273 PLOG(FATAL) << "ioctl VIDIC_G_FMT(" << fd_ << ", " << &fmt << ")";
Parker Schuh58b39e82019-02-22 22:32:46 -0800274 }
275
276 return aos::vision::ImageFormat{(int)fmt.fmt.pix.width,
277 (int)fmt.fmt.pix.height};
278}
279
280void Reader::Start() {
Brian Silverman58899fd2019-03-24 11:03:11 -0700281 VLOG(1) << "queueing buffers for the first time";
Parker Schuh58b39e82019-02-22 22:32:46 -0800282 v4l2_buffer buf;
283 for (unsigned int i = 0; i < kNumBuffers; ++i) {
284 CLEAR(buf);
285 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
286 buf.memory = V4L2_MEMORY_MMAP;
287 buf.index = i;
288 QueueBuffer(&buf);
289 }
Brian Silverman58899fd2019-03-24 11:03:11 -0700290 VLOG(1) << "done with first queue";
Parker Schuh58b39e82019-02-22 22:32:46 -0800291
292 v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
293 if (xioctl(fd_, VIDIOC_STREAMON, &type) == -1) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700294 PLOG(FATAL) << "ioctl VIDIOC_STREAMON(" << fd_ << ", " << &type << ")";
Parker Schuh58b39e82019-02-22 22:32:46 -0800295 }
296}
297
298} // namespace camera
299} // namespace y2019