blob: 3a4b3493b6443bf930648584bbc714666f5bfc00 [file] [log] [blame]
Parker Schuh44f86922017-01-03 23:59:50 -08001#include "aos/common/time.h"
2
3#include "aos/common/logging/logging.h"
4#include "aos/vision/image/reader.h"
5
6#define CLEAR(x) memset(&(x), 0, sizeof(x))
7
8namespace camera {
9
10struct Reader::Buffer {
11 void *start;
12 size_t length; // for munmap
13};
14
15Reader::Reader(const std::string &dev_name, ProcessCb process,
16 CameraParams params)
17 : dev_name_(dev_name), process_(std::move(process)), params_(params) {
18 struct stat st;
19 if (stat(dev_name.c_str(), &st) == -1) {
20 PLOG(FATAL, "Cannot identify '%s'", dev_name.c_str());
21 }
22 if (!S_ISCHR(st.st_mode)) {
23 PLOG(FATAL, "%s is no device\n", dev_name.c_str());
24 }
25
26 fd_ = open(dev_name.c_str(), O_RDWR /* required */ | O_NONBLOCK, 0);
27 if (fd_ == -1) {
28 PLOG(FATAL, "Cannot open '%s'", dev_name.c_str());
29 }
30
31 Init();
Parker Schuh309dd722017-02-25 11:31:18 -080032
33 InitMMap();
34 LOG(INFO, "Bat Vision Successfully Initialized.\n");
Parker Schuh44f86922017-01-03 23:59:50 -080035}
36
37void Reader::QueueBuffer(v4l2_buffer *buf) {
38 if (xioctl(fd_, VIDIOC_QBUF, buf) == -1) {
39 PLOG(WARNING,
40 "ioctl VIDIOC_QBUF(%d, %p)."
41 " losing buf #%" PRIu32 "\n",
42 fd_, &buf, buf->index);
43 } else {
44 // LOG(DEBUG, "put buf #%" PRIu32 " into driver's queue\n", buf->index);
45 ++queued_;
46 }
47}
48
49void Reader::HandleFrame() {
50 v4l2_buffer buf;
51 CLEAR(buf);
52 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
53 buf.memory = V4L2_MEMORY_MMAP;
54
55 if (xioctl(fd_, VIDIOC_DQBUF, &buf) == -1) {
56 if (errno != EAGAIN) {
57 PLOG(ERROR, "ioctl VIDIOC_DQBUF(%d, %p)", fd_, &buf);
58 }
59 return;
60 }
61 --queued_;
62
Parker Schuh309dd722017-02-25 11:31:18 -080063 if (tick_id_ % 10 == 0) {
64 if (!SetCameraControl(V4L2_CID_EXPOSURE_AUTO, "V4L2_CID_EXPOSURE_AUTO",
65 V4L2_EXPOSURE_MANUAL)) {
66 LOG(FATAL, "Failed to set exposure\n");
67 }
68
69 if (!SetCameraControl(V4L2_CID_EXPOSURE_ABSOLUTE,
70 "V4L2_CID_EXPOSURE_ABSOLUTE", params_.exposure)) {
71 LOG(FATAL, "Failed to set exposure\n");
72 }
73 }
74 ++tick_id_;
Parker Schuh44f86922017-01-03 23:59:50 -080075 // Get a timestamp now as proxy for when the image was taken
76 // TODO(ben): the image should come with a timestamp, parker
77 // will know how to get it.
78 auto time = aos::monotonic_clock::now();
79
80 process_(aos::vision::DataRef(
81 reinterpret_cast<const char *>(buffers_[buf.index].start),
82 buf.bytesused),
83 time);
Parker Schuh309dd722017-02-25 11:31:18 -080084
Parker Schuh44f86922017-01-03 23:59:50 -080085 QueueBuffer(&buf);
86}
87
88void Reader::MMapBuffers() {
89 buffers_ = new Buffer[kNumBuffers];
90 v4l2_buffer buf;
91 for (unsigned int n = 0; n < kNumBuffers; ++n) {
92 memset(&buf, 0, sizeof(buf));
93 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
94 buf.memory = V4L2_MEMORY_MMAP;
95 buf.index = n;
96 if (xioctl(fd_, VIDIOC_QUERYBUF, &buf) == -1) {
97 PLOG(FATAL, "ioctl VIDIOC_QUERYBUF(%d, %p)", fd_, &buf);
98 }
99 buffers_[n].length = buf.length;
100 buffers_[n].start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE,
101 MAP_SHARED, fd_, buf.m.offset);
102 if (buffers_[n].start == MAP_FAILED) {
103 PLOG(FATAL,
104 "mmap(NULL, %zd, PROT_READ | PROT_WRITE, MAP_SHARED, %d, %jd)",
105 (size_t)buf.length, fd_, static_cast<intmax_t>(buf.m.offset));
106 }
107 }
108}
109
110void Reader::InitMMap() {
111 v4l2_requestbuffers req;
112 CLEAR(req);
113 req.count = kNumBuffers;
114 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
115 req.memory = V4L2_MEMORY_MMAP;
116 if (xioctl(fd_, VIDIOC_REQBUFS, &req) == -1) {
117 if (EINVAL == errno) {
118 LOG(FATAL, "%s does not support memory mapping\n", dev_name_.c_str());
119 } else {
120 PLOG(FATAL, "ioctl VIDIOC_REQBUFS(%d, %p)\n", fd_, &req);
121 }
122 }
123 queued_ = kNumBuffers;
Parker Schuh309dd722017-02-25 11:31:18 -0800124 if (req.count != kNumBuffers) {
Parker Schuh44f86922017-01-03 23:59:50 -0800125 LOG(FATAL, "Insufficient buffer memory on %s\n", dev_name_.c_str());
126 }
127}
128
129// Sets one of the camera's user-control values.
130// Prints the old and new values.
131// Just prints a message if the camera doesn't support this control or value.
132bool Reader::SetCameraControl(uint32_t id, const char *name, int value) {
133 struct v4l2_control getArg = {id, 0U};
134 int r = xioctl(fd_, VIDIOC_G_CTRL, &getArg);
135 if (r == 0) {
136 if (getArg.value == value) {
137 LOG(DEBUG, "Camera control %s was already %d\n", name, getArg.value);
138 return true;
139 }
140 } else if (errno == EINVAL) {
141 LOG(DEBUG, "Camera control %s is invalid\n", name);
142 errno = 0;
143 return false;
144 }
145
146 struct v4l2_control setArg = {id, value};
147 r = xioctl(fd_, VIDIOC_S_CTRL, &setArg);
148 if (r == 0) {
149 LOG(DEBUG, "Set camera control %s from %d to %d\n", name, getArg.value,
150 value);
151 return true;
152 }
153
154 LOG(DEBUG, "Couldn't set camera control %s to %d", name, value);
155 errno = 0;
156 return false;
157}
158
159void Reader::Init() {
160 v4l2_capability cap;
161 if (xioctl(fd_, VIDIOC_QUERYCAP, &cap) == -1) {
162 if (EINVAL == errno) {
163 LOG(FATAL, "%s is no V4L2 device\n", dev_name_.c_str());
164 } else {
165 PLOG(FATAL, "ioctl VIDIOC_QUERYCAP(%d, %p)", fd_, &cap);
166 }
167 }
168 if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
169 LOG(FATAL, "%s is no video capture device\n", dev_name_.c_str());
170 }
171 if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
172 LOG(FATAL, "%s does not support streaming i/o\n", dev_name_.c_str());
173 }
174
175 /* Select video input, video standard and tune here. */
176
177 v4l2_cropcap cropcap;
178 CLEAR(cropcap);
179 cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
180 if (xioctl(fd_, VIDIOC_CROPCAP, &cropcap) == 0) {
181 v4l2_crop crop;
182 crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
183 crop.c = cropcap.defrect; /* reset to default */
184
185 if (xioctl(fd_, VIDIOC_S_CROP, &crop) == -1) {
186 switch (errno) {
187 case EINVAL:
188 /* Cropping not supported. */
189 break;
190 default:
191 /* Errors ignored. */
192 PLOG(WARNING, "xioctl VIDIOC_S_CROP");
193 break;
194 }
195 }
196 } else {
197 PLOG(WARNING, "xioctl VIDIOC_CROPCAP");
198 }
199
200 v4l2_format fmt;
201 CLEAR(fmt);
202 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
203 fmt.fmt.pix.width = params_.width;
204 fmt.fmt.pix.height = params_.height;
205 fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
206 fmt.fmt.pix.field = V4L2_FIELD_ANY;
207 // fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
208 // fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
209 if (xioctl(fd_, VIDIOC_S_FMT, &fmt) == -1) {
210 LOG(FATAL, "ioctl VIDIC_S_FMT(%d, %p) failed with %d: %s\n", fd_, &fmt,
211 errno, strerror(errno));
212 }
213 /* Note VIDIOC_S_FMT may change width and height. */
214
215 /* Buggy driver paranoia. */
216 unsigned int min = fmt.fmt.pix.width * 2;
217 if (fmt.fmt.pix.bytesperline < min) fmt.fmt.pix.bytesperline = min;
218 min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
219 if (fmt.fmt.pix.sizeimage < min) fmt.fmt.pix.sizeimage = min;
220
221 if (!SetCameraControl(V4L2_CID_EXPOSURE_AUTO, "V4L2_CID_EXPOSURE_AUTO",
222 V4L2_EXPOSURE_MANUAL)) {
223 LOG(FATAL, "Failed to set exposure\n");
224 }
225
226 if (!SetCameraControl(V4L2_CID_EXPOSURE_ABSOLUTE,
227 "V4L2_CID_EXPOSURE_ABSOLUTE", params_.exposure)) {
228 LOG(FATAL, "Failed to set exposure\n");
229 }
230
231 if (!SetCameraControl(V4L2_CID_BRIGHTNESS, "V4L2_CID_BRIGHTNESS",
232 params_.brightness)) {
233 LOG(FATAL, "Failed to set up camera\n");
234 }
235
236 if (!SetCameraControl(V4L2_CID_GAIN, "V4L2_CID_GAIN", params_.gain)) {
237 LOG(FATAL, "Failed to set up camera\n");
238 }
239
240 // #if 0
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) {
249 PLOG(FATAL, "ioctl VIDIOC_S_PARM(%d, %p)\n", fd_, setfps);
250 }
251 LOG(INFO, "framerate ended up at %d/%d\n",
252 setfps->parm.capture.timeperframe.numerator,
253 setfps->parm.capture.timeperframe.denominator);
254 // #endif
Parker Schuh44f86922017-01-03 23:59:50 -0800255}
256
257aos::vision::ImageFormat Reader::get_format() {
258 struct v4l2_format fmt;
259 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
260 if (xioctl(fd_, VIDIOC_G_FMT, &fmt) == -1) {
261 PLOG(FATAL, "ioctl VIDIC_G_FMT(%d, %p)\n", fd_, &fmt);
262 }
263
264 return aos::vision::ImageFormat{(int)fmt.fmt.pix.width,
265 (int)fmt.fmt.pix.height};
266}
267
268void Reader::Start() {
269 LOG(DEBUG, "queueing buffers for the first time\n");
270 v4l2_buffer buf;
271 for (unsigned int i = 0; i < kNumBuffers; ++i) {
272 CLEAR(buf);
273 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
274 buf.memory = V4L2_MEMORY_MMAP;
275 buf.index = i;
276 QueueBuffer(&buf);
277 }
278 LOG(DEBUG, "done with first queue\n");
279
280 v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
281 if (xioctl(fd_, VIDIOC_STREAMON, &type) == -1) {
282 PLOG(FATAL, "ioctl VIDIOC_STREAMON(%d, %p)\n", fd_, &type);
283 }
284}
285
286} // namespace camera