blob: 14faa2406e8f485bc676268f03a0de5576d26111 [file] [log] [blame]
Parker Schuh58b39e82019-02-22 22:32:46 -08001#include "y2019/jevois/camera/reader.h"
2
3#include <errno.h>
4#include <fcntl.h>
5#include <malloc.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9#include <sys/mman.h>
10#include <sys/stat.h>
11#include <sys/time.h>
12#include <sys/types.h>
13#include <unistd.h>
14
Parker Schuh58b39e82019-02-22 22:32:46 -080015#include "aos/time/time.h"
Brian Silverman58899fd2019-03-24 11:03:11 -070016#include "glog/logging.h"
Parker Schuh58b39e82019-02-22 22:32:46 -080017
18#define CLEAR(x) memset(&(x), 0, sizeof(x))
19
20namespace y2019 {
21namespace camera {
22
23using ::camera::xioctl;
24
25struct Reader::Buffer {
26 void *start;
27 size_t length; // for munmap
28};
29
30aos::vision::CameraParams MakeCameraParams(int32_t width, int32_t height,
31 int32_t exposure, int32_t brightness,
32 int32_t gain, int32_t fps) {
33 aos::vision::CameraParams cam;
34 cam.set_width(width);
35 cam.set_height(height);
36 cam.set_exposure(exposure);
37 cam.set_brightness(brightness);
38 cam.set_gain(gain);
39 cam.set_fps(fps);
40 return cam;
41}
42
43Reader::Reader(const std::string &dev_name, ProcessCb process,
44 aos::vision::CameraParams params)
45 : dev_name_(dev_name), process_(std::move(process)), params_(params) {
46 struct stat st;
47 if (stat(dev_name.c_str(), &st) == -1) {
Brian Silverman58899fd2019-03-24 11:03:11 -070048 PLOG(FATAL) << "Cannot identify '" << dev_name << "'";
Parker Schuh58b39e82019-02-22 22:32:46 -080049 }
50 if (!S_ISCHR(st.st_mode)) {
Brian Silverman58899fd2019-03-24 11:03:11 -070051 PLOG(FATAL) << dev_name << " is no device";
Parker Schuh58b39e82019-02-22 22:32:46 -080052 }
53
54 fd_ = open(dev_name.c_str(), O_RDWR /* required */ | O_NONBLOCK, 0);
55 if (fd_ == -1) {
Brian Silverman58899fd2019-03-24 11:03:11 -070056 PLOG(FATAL) << "Cannot open '" << dev_name << "'";
Parker Schuh58b39e82019-02-22 22:32:46 -080057 }
58
59 Init();
60
61 InitMMap();
Brian Silverman58899fd2019-03-24 11:03:11 -070062 LOG(INFO) << "Bat Vision Successfully Initialized.";
Parker Schuh58b39e82019-02-22 22:32:46 -080063}
64
65void Reader::QueueBuffer(v4l2_buffer *buf) {
66 if (xioctl(fd_, VIDIOC_QBUF, buf) == -1) {
Brian Silverman58899fd2019-03-24 11:03:11 -070067 PLOG(WARNING) << "ioctl VIDIOC_QBUF(" << fd_ << ", " << &buf
68 << "). losing buf #" << buf->index;
Parker Schuh58b39e82019-02-22 22:32:46 -080069 } else {
70 ++queued_;
71 }
72}
73
74void Reader::HandleFrame() {
75 v4l2_buffer buf;
76 CLEAR(buf);
77 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
78 buf.memory = V4L2_MEMORY_MMAP;
79
80 if (xioctl(fd_, VIDIOC_DQBUF, &buf) == -1) {
81 if (errno != EAGAIN) {
Brian Silverman58899fd2019-03-24 11:03:11 -070082 PLOG(ERROR) << "ioctl VIDIOC_DQBUF(" << fd_ << ", " << &buf << ")";
Parker Schuh58b39e82019-02-22 22:32:46 -080083 }
84 return;
85 }
86 --queued_;
87
88 ++tick_id_;
89 // Get a timestamp now as proxy for when the image was taken
90 // TODO(ben): the image should come with a timestamp, parker
91 // will know how to get it.
92 auto time = aos::monotonic_clock::now();
93
94 process_(aos::vision::DataRef(
95 reinterpret_cast<const char *>(buffers_[buf.index].start),
96 buf.bytesused),
97 time);
98
99 QueueBuffer(&buf);
100}
101
102void Reader::MMapBuffers() {
103 buffers_ = new Buffer[kNumBuffers];
104 v4l2_buffer buf;
105 for (unsigned int n = 0; n < kNumBuffers; ++n) {
106 memset(&buf, 0, sizeof(buf));
107 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
108 buf.memory = V4L2_MEMORY_MMAP;
109 buf.index = n;
110 if (xioctl(fd_, VIDIOC_QUERYBUF, &buf) == -1) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700111 PLOG(FATAL) << "ioctl VIDIOC_QUERYBUF(" << fd_ << ", " << &buf << ")";
Parker Schuh58b39e82019-02-22 22:32:46 -0800112 }
113 buffers_[n].length = buf.length;
114 buffers_[n].start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE,
115 MAP_SHARED, fd_, buf.m.offset);
116 if (buffers_[n].start == MAP_FAILED) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700117 PLOG(FATAL) << "mmap(NULL, " << buf.length
118 << ", PROT_READ | PROT_WRITE, MAP_SHARED, " << fd_ << ", "
119 << buf.m.offset << ")";
Parker Schuh58b39e82019-02-22 22:32:46 -0800120 }
121 }
122}
123
124void Reader::InitMMap() {
125 v4l2_requestbuffers req;
126 CLEAR(req);
127 req.count = kNumBuffers;
128 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
129 req.memory = V4L2_MEMORY_MMAP;
130 if (xioctl(fd_, VIDIOC_REQBUFS, &req) == -1) {
131 if (EINVAL == errno) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700132 LOG(FATAL) << dev_name_ << " does not support memory mapping\n";
Parker Schuh58b39e82019-02-22 22:32:46 -0800133 } else {
Brian Silverman58899fd2019-03-24 11:03:11 -0700134 LOG(FATAL) << "ioctl VIDIOC_REQBUFS(" << fd_ << ", " << &req << ")";
Parker Schuh58b39e82019-02-22 22:32:46 -0800135 }
136 }
137 queued_ = kNumBuffers;
138 if (req.count != kNumBuffers) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700139 LOG(FATAL) << "Insufficient buffer memory on " << dev_name_;
Parker Schuh58b39e82019-02-22 22:32:46 -0800140 }
141}
142
143// Sets one of the camera's user-control values.
144// Prints the old and new values.
145// Just prints a message if the camera doesn't support this control or value.
146bool Reader::SetCameraControl(uint32_t id, const char *name, int value) {
147 struct v4l2_control getArg = {id, 0U};
148 int r;
149 // Used to be: r = xioctl(fd_, VIDIOC_S_CTRL, &getArg);
150 // Jevois wants this incorrect number below:.
151 r = xioctl(fd_, 0xc00c561b, &getArg);
152 if (r == 0) {
153 if (getArg.value == value) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700154 VLOG(1) << "Camera control " << name << " was already " << getArg.value;
Parker Schuh58b39e82019-02-22 22:32:46 -0800155 return true;
156 }
157 } else if (errno == EINVAL) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700158 VLOG(1) << "Camera control " << name << " is invalid";
Parker Schuh58b39e82019-02-22 22:32:46 -0800159 errno = 0;
160 return false;
161 }
162
163 struct v4l2_control setArg = {id, value};
164 // Should be: r = xioctl(fd_, VIDIOC_S_CTRL, &setArg);
165 // Jevois wants this incorrect number below:.
166 r = xioctl(fd_, 0xc00c561c, &setArg);
167 if (r == 0) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700168 VLOG(1) << "Set camera control " << name << " from " << getArg.value
169 << " to " << value;
Parker Schuh58b39e82019-02-22 22:32:46 -0800170 return true;
171 }
172
Brian Silverman58899fd2019-03-24 11:03:11 -0700173 VLOG(1) << "Couldn't set camera control " << name << " to " << value;
Parker Schuh58b39e82019-02-22 22:32:46 -0800174 errno = 0;
175 return false;
176}
177
178void Reader::Init() {
179 v4l2_capability cap;
180 if (xioctl(fd_, VIDIOC_QUERYCAP, &cap) == -1) {
181 if (EINVAL == errno) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700182 LOG(FATAL) << dev_name_ << " is no V4L2 device";
Parker Schuh58b39e82019-02-22 22:32:46 -0800183 } else {
Brian Silverman58899fd2019-03-24 11:03:11 -0700184 PLOG(FATAL) << "ioctl VIDIOC_QUERYCAP(" << fd_ << ", " << &cap << ")";
Parker Schuh58b39e82019-02-22 22:32:46 -0800185 }
186 }
187 if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700188 LOG(FATAL) << dev_name_ << " is no video capture device";
Parker Schuh58b39e82019-02-22 22:32:46 -0800189 }
190 if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700191 LOG(FATAL) << dev_name_ << " does not support streaming i/o";
Parker Schuh58b39e82019-02-22 22:32:46 -0800192 }
193
194 int camidx = -1;
195 struct v4l2_input inp = {};
196 while (true) {
197 if (xioctl(fd_, VIDIOC_ENUMINPUT, &inp) == -1) {
198 break;
199 }
200 if (inp.type == V4L2_INPUT_TYPE_CAMERA) {
201 if (camidx == -1) camidx = inp.index;
202 printf("Input %d [ %s ] is a camera sensor\n", inp.index, inp.name);
203 } else
204 printf("Input %d [ %s ] is not a camera sensor\n", inp.index, inp.name);
205 ++inp.index;
206 }
207
208 if (xioctl(fd_, VIDIOC_S_INPUT, &camidx) == -1) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700209 PLOG(FATAL) << "ioctl VIDIOC_S_INPUT(" << fd_ << ") failed";
Parker Schuh58b39e82019-02-22 22:32:46 -0800210 }
211 printf("camera idx: %d\n", camidx);
212
213 /* Select video input, video standard and tune here. */
214
215 v4l2_format fmt;
216 CLEAR(fmt);
217
218 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
219 if (xioctl(fd_, VIDIOC_G_FMT, &fmt) == -1) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700220 PLOG(FATAL) << "ioctl VIDIC_G_FMT(" << fd_ << ", " << &fmt << ") failed";
Parker Schuh58b39e82019-02-22 22:32:46 -0800221 }
222
223 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
224 fmt.fmt.pix.width = params_.width();
225 fmt.fmt.pix.height = params_.height();
226 printf("setting format: %d, %d\n", params_.width(), params_.height());
227 fmt.fmt.pix.field = V4L2_FIELD_NONE;
228 fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
229 if (xioctl(fd_, VIDIOC_S_FMT, &fmt) == -1) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700230 PLOG(FATAL) << "ioctl VIDIC_S_FMT(" << fd_ << ", " << &fmt << ") failed";
Parker Schuh58b39e82019-02-22 22:32:46 -0800231 }
232 /* Note VIDIOC_S_FMT may change width and height. */
233
234 /* Buggy driver paranoia. */
235 unsigned int min = fmt.fmt.pix.width * 2;
236 if (fmt.fmt.pix.bytesperline < min) fmt.fmt.pix.bytesperline = min;
237 min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
238 if (fmt.fmt.pix.sizeimage < min) fmt.fmt.pix.sizeimage = min;
239
240 // set framerate
241 struct v4l2_streamparm *setfps;
242 setfps = (struct v4l2_streamparm *)calloc(1, sizeof(struct v4l2_streamparm));
243 memset(setfps, 0, sizeof(struct v4l2_streamparm));
244 setfps->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
245 setfps->parm.capture.timeperframe.numerator = 1;
246 setfps->parm.capture.timeperframe.denominator = params_.fps();
247 if (xioctl(fd_, VIDIOC_S_PARM, setfps) == -1) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700248 PLOG(FATAL) << "ioctl VIDIOC_S_PARM(" << fd_ << ", " << setfps << ")";
Parker Schuh58b39e82019-02-22 22:32:46 -0800249 }
Brian Silverman58899fd2019-03-24 11:03:11 -0700250 LOG(INFO) << "framerate ended up at "
251 << setfps->parm.capture.timeperframe.numerator << "/"
252 << setfps->parm.capture.timeperframe.denominator;
Parker Schuh58b39e82019-02-22 22:32:46 -0800253
254 for (int j = 0; j < 2; ++j) {
255 if (!SetCameraControl(V4L2_CID_EXPOSURE_AUTO, "V4L2_CID_EXPOSURE_AUTO",
256 V4L2_EXPOSURE_MANUAL)) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700257 LOG(FATAL) << "Failed to set exposure";
Parker Schuh58b39e82019-02-22 22:32:46 -0800258 }
259
260 if (!SetCameraControl(V4L2_CID_EXPOSURE_ABSOLUTE,
261 "V4L2_CID_EXPOSURE_ABSOLUTE", params_.exposure())) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700262 LOG(FATAL) << "Failed to set exposure";
Parker Schuh58b39e82019-02-22 22:32:46 -0800263 }
264 sleep(1);
265 }
266}
267
268aos::vision::ImageFormat Reader::get_format() {
269 struct v4l2_format fmt;
270 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
271 if (xioctl(fd_, VIDIOC_G_FMT, &fmt) == -1) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700272 PLOG(FATAL) << "ioctl VIDIC_G_FMT(" << fd_ << ", " << &fmt << ")";
Parker Schuh58b39e82019-02-22 22:32:46 -0800273 }
274
275 return aos::vision::ImageFormat{(int)fmt.fmt.pix.width,
276 (int)fmt.fmt.pix.height};
277}
278
279void Reader::Start() {
Brian Silverman58899fd2019-03-24 11:03:11 -0700280 VLOG(1) << "queueing buffers for the first time";
Parker Schuh58b39e82019-02-22 22:32:46 -0800281 v4l2_buffer buf;
282 for (unsigned int i = 0; i < kNumBuffers; ++i) {
283 CLEAR(buf);
284 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
285 buf.memory = V4L2_MEMORY_MMAP;
286 buf.index = i;
287 QueueBuffer(&buf);
288 }
Brian Silverman58899fd2019-03-24 11:03:11 -0700289 VLOG(1) << "done with first queue";
Parker Schuh58b39e82019-02-22 22:32:46 -0800290
291 v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
292 if (xioctl(fd_, VIDIOC_STREAMON, &type) == -1) {
Brian Silverman58899fd2019-03-24 11:03:11 -0700293 PLOG(FATAL) << "ioctl VIDIOC_STREAMON(" << fd_ << ", " << &type << ")";
Parker Schuh58b39e82019-02-22 22:32:46 -0800294 }
295}
296
297} // namespace camera
298} // namespace y2019