blob: 7584369d7c1798ce826f20392b3449f6108d1508 [file] [log] [blame]
Parker Schuh24ee58d2017-03-11 16:13:23 -08001#include "aos/vision/image/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>
Parker Schuh44f86922017-01-03 23:59:50 -080014
John Park33858a32018-09-28 23:05:48 -070015#include "aos/logging/logging.h"
16#include "aos/time/time.h"
Parker Schuh44f86922017-01-03 23:59:50 -080017
18#define CLEAR(x) memset(&(x), 0, sizeof(x))
19
20namespace camera {
21
22struct Reader::Buffer {
23 void *start;
24 size_t length; // for munmap
25};
26
Parker Schuh24ee58d2017-03-11 16:13:23 -080027aos::vision::CameraParams MakeCameraParams(int32_t width, int32_t height,
28 int32_t exposure, int32_t brightness,
29 int32_t gain, int32_t fps) {
30 aos::vision::CameraParams cam;
31 cam.set_width(width);
32 cam.set_height(height);
33 cam.set_exposure(exposure);
34 cam.set_brightness(brightness);
35 cam.set_gain(gain);
36 cam.set_fps(fps);
37 return cam;
38}
39
Parker Schuh44f86922017-01-03 23:59:50 -080040Reader::Reader(const std::string &dev_name, ProcessCb process,
Parker Schuh24ee58d2017-03-11 16:13:23 -080041 aos::vision::CameraParams params)
Parker Schuh44f86922017-01-03 23:59:50 -080042 : dev_name_(dev_name), process_(std::move(process)), params_(params) {
43 struct stat st;
44 if (stat(dev_name.c_str(), &st) == -1) {
Austin Schuhf257f3c2019-10-27 21:00:43 -070045 AOS_PLOG(FATAL, "Cannot identify '%s'", dev_name.c_str());
Parker Schuh44f86922017-01-03 23:59:50 -080046 }
47 if (!S_ISCHR(st.st_mode)) {
Austin Schuhf257f3c2019-10-27 21:00:43 -070048 AOS_PLOG(FATAL, "%s is no device\n", dev_name.c_str());
Parker Schuh44f86922017-01-03 23:59:50 -080049 }
50
51 fd_ = open(dev_name.c_str(), O_RDWR /* required */ | O_NONBLOCK, 0);
52 if (fd_ == -1) {
Austin Schuhf257f3c2019-10-27 21:00:43 -070053 AOS_PLOG(FATAL, "Cannot open '%s'", dev_name.c_str());
Parker Schuh44f86922017-01-03 23:59:50 -080054 }
55
56 Init();
Parker Schuh309dd722017-02-25 11:31:18 -080057
58 InitMMap();
Tyler Chatowfdd7fbf2019-04-13 21:14:05 -070059
60 SetExposure(params.exposure());
Austin Schuhf257f3c2019-10-27 21:00:43 -070061 AOS_LOG(INFO, "Bat Vision Successfully Initialized.\n");
Parker Schuh44f86922017-01-03 23:59:50 -080062}
63
64void Reader::QueueBuffer(v4l2_buffer *buf) {
65 if (xioctl(fd_, VIDIOC_QBUF, buf) == -1) {
Austin Schuhf257f3c2019-10-27 21:00:43 -070066 AOS_PLOG(WARNING,
67 "ioctl VIDIOC_QBUF(%d, %p)."
68 " losing buf #%" PRIu32 "\n",
69 fd_, &buf, buf->index);
Parker Schuh44f86922017-01-03 23:59:50 -080070 } else {
Austin Schuhf257f3c2019-10-27 21:00:43 -070071 // AOS_LOG(DEBUG, "put buf #%" PRIu32 " into driver's queue\n",
72 // buf->index);
Parker Schuh44f86922017-01-03 23:59:50 -080073 ++queued_;
74 }
75}
76
77void Reader::HandleFrame() {
78 v4l2_buffer buf;
79 CLEAR(buf);
80 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
81 buf.memory = V4L2_MEMORY_MMAP;
82
83 if (xioctl(fd_, VIDIOC_DQBUF, &buf) == -1) {
84 if (errno != EAGAIN) {
Austin Schuhf257f3c2019-10-27 21:00:43 -070085 AOS_PLOG(ERROR, "ioctl VIDIOC_DQBUF(%d, %p)", fd_, &buf);
Parker Schuh44f86922017-01-03 23:59:50 -080086 }
87 return;
88 }
89 --queued_;
90
Parker Schuh309dd722017-02-25 11:31:18 -080091 ++tick_id_;
Parker Schuh44f86922017-01-03 23:59:50 -080092 // Get a timestamp now as proxy for when the image was taken
93 // TODO(ben): the image should come with a timestamp, parker
94 // will know how to get it.
95 auto time = aos::monotonic_clock::now();
96
97 process_(aos::vision::DataRef(
98 reinterpret_cast<const char *>(buffers_[buf.index].start),
99 buf.bytesused),
100 time);
Parker Schuh309dd722017-02-25 11:31:18 -0800101
Parker Schuh44f86922017-01-03 23:59:50 -0800102 QueueBuffer(&buf);
103}
104
105void Reader::MMapBuffers() {
106 buffers_ = new Buffer[kNumBuffers];
107 v4l2_buffer buf;
108 for (unsigned int n = 0; n < kNumBuffers; ++n) {
109 memset(&buf, 0, sizeof(buf));
110 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
111 buf.memory = V4L2_MEMORY_MMAP;
112 buf.index = n;
113 if (xioctl(fd_, VIDIOC_QUERYBUF, &buf) == -1) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700114 AOS_PLOG(FATAL, "ioctl VIDIOC_QUERYBUF(%d, %p)", fd_, &buf);
Parker Schuh44f86922017-01-03 23:59:50 -0800115 }
116 buffers_[n].length = buf.length;
117 buffers_[n].start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE,
118 MAP_SHARED, fd_, buf.m.offset);
119 if (buffers_[n].start == MAP_FAILED) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700120 AOS_PLOG(FATAL,
121 "mmap(NULL, %zd, PROT_READ | PROT_WRITE, MAP_SHARED, %d, %jd)",
122 (size_t)buf.length, fd_, static_cast<intmax_t>(buf.m.offset));
Parker Schuh44f86922017-01-03 23:59:50 -0800123 }
124 }
125}
126
127void Reader::InitMMap() {
128 v4l2_requestbuffers req;
129 CLEAR(req);
130 req.count = kNumBuffers;
131 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
132 req.memory = V4L2_MEMORY_MMAP;
133 if (xioctl(fd_, VIDIOC_REQBUFS, &req) == -1) {
134 if (EINVAL == errno) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700135 AOS_LOG(FATAL, "%s does not support memory mapping\n", dev_name_.c_str());
Parker Schuh44f86922017-01-03 23:59:50 -0800136 } else {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700137 AOS_PLOG(FATAL, "ioctl VIDIOC_REQBUFS(%d, %p)\n", fd_, &req);
Parker Schuh44f86922017-01-03 23:59:50 -0800138 }
139 }
140 queued_ = kNumBuffers;
Parker Schuh309dd722017-02-25 11:31:18 -0800141 if (req.count != kNumBuffers) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700142 AOS_LOG(FATAL, "Insufficient buffer memory on %s\n", dev_name_.c_str());
Parker Schuh44f86922017-01-03 23:59:50 -0800143 }
144}
145
146// Sets one of the camera's user-control values.
147// Prints the old and new values.
148// Just prints a message if the camera doesn't support this control or value.
149bool Reader::SetCameraControl(uint32_t id, const char *name, int value) {
150 struct v4l2_control getArg = {id, 0U};
151 int r = xioctl(fd_, VIDIOC_G_CTRL, &getArg);
152 if (r == 0) {
153 if (getArg.value == value) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700154 AOS_LOG(DEBUG, "Camera control %s was already %d\n", name, getArg.value);
Parker Schuh44f86922017-01-03 23:59:50 -0800155 return true;
156 }
157 } else if (errno == EINVAL) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700158 AOS_LOG(DEBUG, "Camera control %s is invalid\n", name);
Parker Schuh44f86922017-01-03 23:59:50 -0800159 errno = 0;
160 return false;
161 }
162
163 struct v4l2_control setArg = {id, value};
164 r = xioctl(fd_, VIDIOC_S_CTRL, &setArg);
165 if (r == 0) {
Parker Schuh44f86922017-01-03 23:59:50 -0800166 return true;
167 }
168
Austin Schuhf257f3c2019-10-27 21:00:43 -0700169 AOS_LOG(DEBUG, "Couldn't set camera control %s to %d", name, value);
Parker Schuh44f86922017-01-03 23:59:50 -0800170 errno = 0;
171 return false;
172}
173
Tyler Chatowfdd7fbf2019-04-13 21:14:05 -0700174bool Reader::SetExposure(int abs_exp) {
175 return SetCameraControl(V4L2_CID_EXPOSURE_ABSOLUTE,
176 "V4L2_CID_EXPOSURE_ABSOLUTE", abs_exp);
177}
178
Parker Schuh44f86922017-01-03 23:59:50 -0800179void Reader::Init() {
180 v4l2_capability cap;
181 if (xioctl(fd_, VIDIOC_QUERYCAP, &cap) == -1) {
182 if (EINVAL == errno) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700183 AOS_LOG(FATAL, "%s is no V4L2 device\n", dev_name_.c_str());
Parker Schuh44f86922017-01-03 23:59:50 -0800184 } else {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700185 AOS_PLOG(FATAL, "ioctl VIDIOC_QUERYCAP(%d, %p)", fd_, &cap);
Parker Schuh44f86922017-01-03 23:59:50 -0800186 }
187 }
188 if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700189 AOS_LOG(FATAL, "%s is no video capture device\n", dev_name_.c_str());
Parker Schuh44f86922017-01-03 23:59:50 -0800190 }
191 if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700192 AOS_LOG(FATAL, "%s does not support streaming i/o\n", dev_name_.c_str());
Parker Schuh44f86922017-01-03 23:59:50 -0800193 }
194
195 /* Select video input, video standard and tune here. */
196
197 v4l2_cropcap cropcap;
198 CLEAR(cropcap);
199 cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
200 if (xioctl(fd_, VIDIOC_CROPCAP, &cropcap) == 0) {
201 v4l2_crop crop;
202 crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
203 crop.c = cropcap.defrect; /* reset to default */
204
205 if (xioctl(fd_, VIDIOC_S_CROP, &crop) == -1) {
206 switch (errno) {
207 case EINVAL:
208 /* Cropping not supported. */
209 break;
210 default:
211 /* Errors ignored. */
Austin Schuhf257f3c2019-10-27 21:00:43 -0700212 AOS_PLOG(WARNING, "xioctl VIDIOC_S_CROP");
Parker Schuh44f86922017-01-03 23:59:50 -0800213 break;
214 }
215 }
216 } else {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700217 AOS_PLOG(WARNING, "xioctl VIDIOC_CROPCAP");
Parker Schuh44f86922017-01-03 23:59:50 -0800218 }
219
220 v4l2_format fmt;
221 CLEAR(fmt);
222 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
Parker Schuh24ee58d2017-03-11 16:13:23 -0800223 fmt.fmt.pix.width = params_.width();
224 fmt.fmt.pix.height = params_.height();
Parker Schuh44f86922017-01-03 23:59:50 -0800225 fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
226 fmt.fmt.pix.field = V4L2_FIELD_ANY;
227 // fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
228 // fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
229 if (xioctl(fd_, VIDIOC_S_FMT, &fmt) == -1) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700230 AOS_LOG(FATAL, "ioctl VIDIC_S_FMT(%d, %p) failed with %d: %s\n", fd_, &fmt,
231 errno, strerror(errno));
Parker Schuh44f86922017-01-03 23:59:50 -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 if (!SetCameraControl(V4L2_CID_EXPOSURE_AUTO, "V4L2_CID_EXPOSURE_AUTO",
242 V4L2_EXPOSURE_MANUAL)) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700243 AOS_LOG(FATAL, "Failed to set exposure\n");
Parker Schuh44f86922017-01-03 23:59:50 -0800244 }
245
246 if (!SetCameraControl(V4L2_CID_EXPOSURE_ABSOLUTE,
Parker Schuh24ee58d2017-03-11 16:13:23 -0800247 "V4L2_CID_EXPOSURE_ABSOLUTE", params_.exposure())) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700248 AOS_LOG(FATAL, "Failed to set exposure\n");
Parker Schuh44f86922017-01-03 23:59:50 -0800249 }
250
251 if (!SetCameraControl(V4L2_CID_BRIGHTNESS, "V4L2_CID_BRIGHTNESS",
Parker Schuh24ee58d2017-03-11 16:13:23 -0800252 params_.brightness())) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700253 AOS_LOG(FATAL, "Failed to set up camera\n");
Parker Schuh44f86922017-01-03 23:59:50 -0800254 }
255
Parker Schuh24ee58d2017-03-11 16:13:23 -0800256 if (!SetCameraControl(V4L2_CID_GAIN, "V4L2_CID_GAIN", params_.gain())) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700257 AOS_LOG(FATAL, "Failed to set up camera\n");
Parker Schuh44f86922017-01-03 23:59:50 -0800258 }
259
Parker Schuh44f86922017-01-03 23:59:50 -0800260 // set framerate
261 struct v4l2_streamparm *setfps;
262 setfps = (struct v4l2_streamparm *)calloc(1, sizeof(struct v4l2_streamparm));
263 memset(setfps, 0, sizeof(struct v4l2_streamparm));
264 setfps->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
265 setfps->parm.capture.timeperframe.numerator = 1;
Parker Schuh24ee58d2017-03-11 16:13:23 -0800266 setfps->parm.capture.timeperframe.denominator = params_.fps();
Parker Schuh44f86922017-01-03 23:59:50 -0800267 if (xioctl(fd_, VIDIOC_S_PARM, setfps) == -1) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700268 AOS_PLOG(FATAL, "ioctl VIDIOC_S_PARM(%d, %p)\n", fd_, setfps);
Parker Schuh44f86922017-01-03 23:59:50 -0800269 }
Austin Schuhf257f3c2019-10-27 21:00:43 -0700270 AOS_LOG(INFO, "framerate ended up at %d/%d\n",
271 setfps->parm.capture.timeperframe.numerator,
272 setfps->parm.capture.timeperframe.denominator);
Parker Schuh44f86922017-01-03 23:59:50 -0800273}
274
275aos::vision::ImageFormat Reader::get_format() {
276 struct v4l2_format fmt;
277 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
278 if (xioctl(fd_, VIDIOC_G_FMT, &fmt) == -1) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700279 AOS_PLOG(FATAL, "ioctl VIDIC_G_FMT(%d, %p)\n", fd_, &fmt);
Parker Schuh44f86922017-01-03 23:59:50 -0800280 }
281
282 return aos::vision::ImageFormat{(int)fmt.fmt.pix.width,
283 (int)fmt.fmt.pix.height};
284}
285
286void Reader::Start() {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700287 AOS_LOG(DEBUG, "queueing buffers for the first time\n");
Parker Schuh44f86922017-01-03 23:59:50 -0800288 v4l2_buffer buf;
289 for (unsigned int i = 0; i < kNumBuffers; ++i) {
290 CLEAR(buf);
291 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
292 buf.memory = V4L2_MEMORY_MMAP;
293 buf.index = i;
294 QueueBuffer(&buf);
295 }
Austin Schuhf257f3c2019-10-27 21:00:43 -0700296 AOS_LOG(DEBUG, "done with first queue\n");
Parker Schuh44f86922017-01-03 23:59:50 -0800297
298 v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
299 if (xioctl(fd_, VIDIOC_STREAMON, &type) == -1) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700300 AOS_PLOG(FATAL, "ioctl VIDIOC_STREAMON(%d, %p)\n", fd_, &type);
Parker Schuh44f86922017-01-03 23:59:50 -0800301 }
302}
303
304} // namespace camera