blob: 23b58c281c231f8eb77a52019f35785cb2c55c29 [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) {
45 PLOG(FATAL, "Cannot identify '%s'", dev_name.c_str());
46 }
47 if (!S_ISCHR(st.st_mode)) {
48 PLOG(FATAL, "%s is no device\n", dev_name.c_str());
49 }
50
51 fd_ = open(dev_name.c_str(), O_RDWR /* required */ | O_NONBLOCK, 0);
52 if (fd_ == -1) {
53 PLOG(FATAL, "Cannot open '%s'", dev_name.c_str());
54 }
55
56 Init();
Parker Schuh309dd722017-02-25 11:31:18 -080057
58 InitMMap();
59 LOG(INFO, "Bat Vision Successfully Initialized.\n");
Parker Schuh44f86922017-01-03 23:59:50 -080060}
61
62void Reader::QueueBuffer(v4l2_buffer *buf) {
63 if (xioctl(fd_, VIDIOC_QBUF, buf) == -1) {
64 PLOG(WARNING,
65 "ioctl VIDIOC_QBUF(%d, %p)."
66 " losing buf #%" PRIu32 "\n",
67 fd_, &buf, buf->index);
68 } else {
69 // LOG(DEBUG, "put buf #%" PRIu32 " into driver's queue\n", buf->index);
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) {
82 PLOG(ERROR, "ioctl VIDIOC_DQBUF(%d, %p)", fd_, &buf);
83 }
84 return;
85 }
86 --queued_;
87
Parker Schuh309dd722017-02-25 11:31:18 -080088 ++tick_id_;
Parker Schuh44f86922017-01-03 23:59:50 -080089 // 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);
Parker Schuh309dd722017-02-25 11:31:18 -080098
Parker Schuh44f86922017-01-03 23:59:50 -080099 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) {
111 PLOG(FATAL, "ioctl VIDIOC_QUERYBUF(%d, %p)", fd_, &buf);
112 }
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) {
117 PLOG(FATAL,
118 "mmap(NULL, %zd, PROT_READ | PROT_WRITE, MAP_SHARED, %d, %jd)",
119 (size_t)buf.length, fd_, static_cast<intmax_t>(buf.m.offset));
120 }
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) {
132 LOG(FATAL, "%s does not support memory mapping\n", dev_name_.c_str());
133 } else {
134 PLOG(FATAL, "ioctl VIDIOC_REQBUFS(%d, %p)\n", fd_, &req);
135 }
136 }
137 queued_ = kNumBuffers;
Parker Schuh309dd722017-02-25 11:31:18 -0800138 if (req.count != kNumBuffers) {
Parker Schuh44f86922017-01-03 23:59:50 -0800139 LOG(FATAL, "Insufficient buffer memory on %s\n", dev_name_.c_str());
140 }
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 = xioctl(fd_, VIDIOC_G_CTRL, &getArg);
149 if (r == 0) {
150 if (getArg.value == value) {
151 LOG(DEBUG, "Camera control %s was already %d\n", name, getArg.value);
152 return true;
153 }
154 } else if (errno == EINVAL) {
155 LOG(DEBUG, "Camera control %s is invalid\n", name);
156 errno = 0;
157 return false;
158 }
159
160 struct v4l2_control setArg = {id, value};
161 r = xioctl(fd_, VIDIOC_S_CTRL, &setArg);
162 if (r == 0) {
Parker Schuh44f86922017-01-03 23:59:50 -0800163 return true;
164 }
165
166 LOG(DEBUG, "Couldn't set camera control %s to %d", name, value);
167 errno = 0;
168 return false;
169}
170
171void Reader::Init() {
172 v4l2_capability cap;
173 if (xioctl(fd_, VIDIOC_QUERYCAP, &cap) == -1) {
174 if (EINVAL == errno) {
175 LOG(FATAL, "%s is no V4L2 device\n", dev_name_.c_str());
176 } else {
177 PLOG(FATAL, "ioctl VIDIOC_QUERYCAP(%d, %p)", fd_, &cap);
178 }
179 }
180 if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
181 LOG(FATAL, "%s is no video capture device\n", dev_name_.c_str());
182 }
183 if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
184 LOG(FATAL, "%s does not support streaming i/o\n", dev_name_.c_str());
185 }
186
187 /* Select video input, video standard and tune here. */
188
189 v4l2_cropcap cropcap;
190 CLEAR(cropcap);
191 cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
192 if (xioctl(fd_, VIDIOC_CROPCAP, &cropcap) == 0) {
193 v4l2_crop crop;
194 crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
195 crop.c = cropcap.defrect; /* reset to default */
196
197 if (xioctl(fd_, VIDIOC_S_CROP, &crop) == -1) {
198 switch (errno) {
199 case EINVAL:
200 /* Cropping not supported. */
201 break;
202 default:
203 /* Errors ignored. */
204 PLOG(WARNING, "xioctl VIDIOC_S_CROP");
205 break;
206 }
207 }
208 } else {
209 PLOG(WARNING, "xioctl VIDIOC_CROPCAP");
210 }
211
212 v4l2_format fmt;
213 CLEAR(fmt);
214 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
Parker Schuh24ee58d2017-03-11 16:13:23 -0800215 fmt.fmt.pix.width = params_.width();
216 fmt.fmt.pix.height = params_.height();
Parker Schuh44f86922017-01-03 23:59:50 -0800217 fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
218 fmt.fmt.pix.field = V4L2_FIELD_ANY;
219 // fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
220 // fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
221 if (xioctl(fd_, VIDIOC_S_FMT, &fmt) == -1) {
222 LOG(FATAL, "ioctl VIDIC_S_FMT(%d, %p) failed with %d: %s\n", fd_, &fmt,
223 errno, strerror(errno));
224 }
225 /* Note VIDIOC_S_FMT may change width and height. */
226
227 /* Buggy driver paranoia. */
228 unsigned int min = fmt.fmt.pix.width * 2;
229 if (fmt.fmt.pix.bytesperline < min) fmt.fmt.pix.bytesperline = min;
230 min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
231 if (fmt.fmt.pix.sizeimage < min) fmt.fmt.pix.sizeimage = min;
232
233 if (!SetCameraControl(V4L2_CID_EXPOSURE_AUTO, "V4L2_CID_EXPOSURE_AUTO",
234 V4L2_EXPOSURE_MANUAL)) {
235 LOG(FATAL, "Failed to set exposure\n");
236 }
237
238 if (!SetCameraControl(V4L2_CID_EXPOSURE_ABSOLUTE,
Parker Schuh24ee58d2017-03-11 16:13:23 -0800239 "V4L2_CID_EXPOSURE_ABSOLUTE", params_.exposure())) {
Parker Schuh44f86922017-01-03 23:59:50 -0800240 LOG(FATAL, "Failed to set exposure\n");
241 }
242
243 if (!SetCameraControl(V4L2_CID_BRIGHTNESS, "V4L2_CID_BRIGHTNESS",
Parker Schuh24ee58d2017-03-11 16:13:23 -0800244 params_.brightness())) {
Parker Schuh44f86922017-01-03 23:59:50 -0800245 LOG(FATAL, "Failed to set up camera\n");
246 }
247
Parker Schuh24ee58d2017-03-11 16:13:23 -0800248 if (!SetCameraControl(V4L2_CID_GAIN, "V4L2_CID_GAIN", params_.gain())) {
Parker Schuh44f86922017-01-03 23:59:50 -0800249 LOG(FATAL, "Failed to set up camera\n");
250 }
251
Parker Schuh44f86922017-01-03 23:59:50 -0800252 // set framerate
253 struct v4l2_streamparm *setfps;
254 setfps = (struct v4l2_streamparm *)calloc(1, sizeof(struct v4l2_streamparm));
255 memset(setfps, 0, sizeof(struct v4l2_streamparm));
256 setfps->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
257 setfps->parm.capture.timeperframe.numerator = 1;
Parker Schuh24ee58d2017-03-11 16:13:23 -0800258 setfps->parm.capture.timeperframe.denominator = params_.fps();
Parker Schuh44f86922017-01-03 23:59:50 -0800259 if (xioctl(fd_, VIDIOC_S_PARM, setfps) == -1) {
260 PLOG(FATAL, "ioctl VIDIOC_S_PARM(%d, %p)\n", fd_, setfps);
261 }
262 LOG(INFO, "framerate ended up at %d/%d\n",
263 setfps->parm.capture.timeperframe.numerator,
264 setfps->parm.capture.timeperframe.denominator);
Parker Schuh44f86922017-01-03 23:59:50 -0800265}
266
267aos::vision::ImageFormat Reader::get_format() {
268 struct v4l2_format fmt;
269 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
270 if (xioctl(fd_, VIDIOC_G_FMT, &fmt) == -1) {
271 PLOG(FATAL, "ioctl VIDIC_G_FMT(%d, %p)\n", fd_, &fmt);
272 }
273
274 return aos::vision::ImageFormat{(int)fmt.fmt.pix.width,
275 (int)fmt.fmt.pix.height};
276}
277
278void Reader::Start() {
279 LOG(DEBUG, "queueing buffers for the first time\n");
280 v4l2_buffer buf;
281 for (unsigned int i = 0; i < kNumBuffers; ++i) {
282 CLEAR(buf);
283 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
284 buf.memory = V4L2_MEMORY_MMAP;
285 buf.index = i;
286 QueueBuffer(&buf);
287 }
288 LOG(DEBUG, "done with first queue\n");
289
290 v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
291 if (xioctl(fd_, VIDIOC_STREAMON, &type) == -1) {
292 PLOG(FATAL, "ioctl VIDIOC_STREAMON(%d, %p)\n", fd_, &type);
293 }
294}
295
296} // namespace camera