blob: b9dc36dccf9c3509bbd097cbf3ab4e4a545c658a [file] [log] [blame]
brians343bc112013-02-10 01:53:46 +00001#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4#include <fcntl.h>
5#include <unistd.h>
6#include <errno.h>
7#include <malloc.h>
8#include <sys/stat.h>
9#include <sys/types.h>
10#include <sys/time.h>
11#include <sys/mman.h>
12
13#include <string>
14#include <inttypes.h>
15
Brian Silverman598800f2013-05-09 17:08:42 -070016#include "aos/atom_code/init.h"
brians343bc112013-02-10 01:53:46 +000017#include "aos/atom_code/camera/V4L2.h"
18#include "aos/atom_code/camera/Buffers.h"
Brian Silverman598800f2013-05-09 17:08:42 -070019#include "aos/common/logging/logging.h"
brians343bc112013-02-10 01:53:46 +000020
21#define CLEAR(x) memset(&(x), 0, sizeof(x))
22
23namespace aos {
24namespace camera {
25
26class Reader {
27 static const char *const dev_name;
28
29 // of the camera
30 int fd_;
31 // the bound socket listening for fd requests
32 int server_fd_;
33
34 static const aos_type_sig kRecycleSignature;
35 aos_queue *queue_, *recycle_queue_;
36 // the number of buffers currently queued in v4l2
37 uint32_t queued_;
38 public:
39 Reader() {
40 struct stat st;
41 if (stat(dev_name, &st) == -1) {
42 LOG(FATAL, "Cannot identify '%s' because of %d: %s\n",
43 dev_name, errno, strerror(errno));
44 }
45 if (!S_ISCHR(st.st_mode)) {
46 LOG(FATAL, "%s is no device\n", dev_name);
47 }
48
49 fd_ = open(dev_name, O_RDWR /* required */ | O_NONBLOCK, 0);
50 if (fd_ == -1) {
51 LOG(FATAL, "Cannot open '%s' because of %d: %s\n",
52 dev_name, errno, strerror(errno));
53 }
54
55 queue_ = aos_fetch_queue_recycle(Buffers::kQueueName.c_str(), &Buffers::kSignature,
56 &kRecycleSignature, &recycle_queue_);
57 // read off any existing recycled messages
58 while (aos_queue_read_msg(recycle_queue_, NON_BLOCK) != NULL);
59 queued_ = 0;
60
61 InitServer();
62 Init();
63 }
64 private:
65 void InitServer() {
66 if (unlink(Buffers::kFDServerName.c_str()) == -1 && errno != ENOENT) {
67 LOG(WARNING, "unlink(kFDServerName(='%s')) failed with %d: %s\n",
68 Buffers::kFDServerName.c_str(), errno, strerror(errno));
69 }
70 if ((server_fd_ = Buffers::CreateSocket(bind)) == -1) {
71 LOG(FATAL, "creating the IPC socket failed\n");
72 }
73 if (listen(server_fd_, 10) == -1) {
74 LOG(FATAL, "listen(%d, 10) failed with %d: %s\n",
75 server_fd_, errno, strerror(errno));
76 }
77 }
78 void SendFD(const int sock) {
79 int myfds[Buffers::kNumFDs]; /* Contains the file descriptors to pass. */
80 myfds[0] = fd_;
81 char buf[CMSG_SPACE(sizeof(myfds))]; /* ancillary data buffer */
82
83 iovec data;
84 memset(&data, 0, sizeof(data));
85 char dummy = 'A';
86 data.iov_base = &dummy;
87 data.iov_len = sizeof(dummy);
88 msghdr msg;
89 memset(&msg, 0, sizeof(msg));
90 msg.msg_iov = &data;
91 msg.msg_iovlen = 1;
92 msg.msg_control = buf;
93 msg.msg_controllen = sizeof(buf);
94 cmsghdr *const cmsg = CMSG_FIRSTHDR(&msg);
95 cmsg->cmsg_level = SOL_SOCKET;
96 cmsg->cmsg_type = SCM_RIGHTS;
97 cmsg->cmsg_len = CMSG_LEN(sizeof(myfds));
98 /* Initialize the payload: */
99 memcpy(CMSG_DATA(cmsg), myfds, sizeof(myfds));
100 if (sendmsg(sock, &msg, 0) == -1) {
101 LOG(ERROR, "sendmsg(%d, %p, 0) failed with %d: %s\n",
102 sock, &msg, errno, strerror(errno));
103 }
104 // leave it open so that the other end can tell if this process dies
105 }
106
107#if 0
108 // if we ever do want to do any of these things, this is how
109 void Stop() {
110 const v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
111 if (xioctl(fd_, VIDIOC_STREAMOFF, &type) == -1) {
112 errno_exit("VIDIOC_STREAMOFF");
113 }
114 }
115 void Close() {
116 if (close(fd_) == -1)
117 errno_exit("close");
118 fd_ = -1;
119 }
120#endif
121
122 void QueueBuffer(v4l2_buffer *buf) {
123 if (xioctl(fd_, VIDIOC_QBUF, buf) == -1) {
124 LOG(WARNING, "ioctl VIDIOC_QBUF(%d, %p) failed with %d: %s. losing buf #%"PRIu32"\n",
125 fd_, &buf, errno, strerror(errno), buf->index);
126 } else {
127 LOG(DEBUG, "put buf #%"PRIu32" into driver's queue\n", buf->index);
128 ++queued_;
129 }
130 }
131 void ReadFrame() {
132 v4l2_buffer buf;
133 CLEAR(buf);
134 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
135 buf.memory = V4L2_MEMORY_MMAP;
136
137 const Buffers::Message *read;
138 do {
139 read = static_cast<const Buffers::Message *>(
140 // we block waiting for one if we can't dequeue one without leaving
141 // the driver <= 2 (to be safe)
142 aos_queue_read_msg(recycle_queue_, (queued_ <= 2) ? BLOCK : NON_BLOCK));
143 if (read != NULL) {
144 buf.index = read->index;
145 aos_queue_free_msg(recycle_queue_, read);
146 QueueBuffer(&buf);
147 }
148 } while (read != NULL);
149
150 if (xioctl(fd_, VIDIOC_DQBUF, &buf) == -1) {
151 if (errno != EAGAIN) {
152 LOG(ERROR, "ioctl VIDIOC_DQBUF(%d, %p) failed with %d: %s\n",
153 fd_, &buf, errno, strerror(errno));
154 }
155 return;
156 }
157 --queued_;
158 if (buf.index >= Buffers::kNumBuffers) {
159 LOG(ERROR, "buf.index (%"PRIu32") is >= kNumBuffers (%u)\n",
160 buf.index, Buffers::kNumBuffers);
161 return;
162 }
163
164 Buffers::Message *const msg = static_cast<Buffers::Message *>(
165 aos_queue_get_msg(queue_));
166 if (msg == NULL) {
167 LOG(WARNING, "couldn't get a message to send buf #%"PRIu32" from queue %p."
168 " re-queueing now\n", buf.index, queue_);
169 QueueBuffer(&buf);
170 return;
171 }
172 msg->index = buf.index;
173 msg->bytesused = buf.bytesused;
174 memcpy(&msg->timestamp, &buf.timestamp, sizeof(msg->timestamp));
175 msg->sequence = buf.sequence;
176 if (aos_queue_write_msg_free(queue_, msg, OVERRIDE) == -1) {
177 LOG(WARNING, "sending message %p with buf #%"PRIu32" to queue %p failed."
178 " re-queueing now\n", msg, buf.index, queue_);
179 QueueBuffer(&buf);
180 return;
181 } else {
182 LOG(DEBUG, "sent message off to queue %p with buffer #%"PRIu32"\n",
183 queue_, buf.index);
184 }
185 }
186
187 void init_mmap() {
188 v4l2_requestbuffers req;
189 CLEAR(req);
190 req.count = Buffers::kNumBuffers;
191 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
192 req.memory = V4L2_MEMORY_MMAP;
193 if (xioctl(fd_, VIDIOC_REQBUFS, &req) == -1) {
194 if (EINVAL == errno) {
195 LOG(FATAL, "%s does not support memory mapping\n", dev_name);
196 } else {
197 LOG(FATAL, "ioctl VIDIOC_REQBUFS(%d, %p) failed with %d: %s\n",
198 fd_, &req, errno, strerror(errno));
199 }
200 }
201 queued_ = Buffers::kNumBuffers;
202 if (req.count < Buffers::kNumBuffers) {
203 LOG(FATAL, "Insufficient buffer memory on %s\n", dev_name);
204 }
205 }
206
Austin Schuhb3fb8972013-04-04 05:46:09 +0000207 // Sets one of the camera's user-control values.
208 // Prints the old and new values.
209 // Just prints a message if the camera doesn't support this control or value.
210 bool SetCameraControl(int id, const char *name, int value) {
211 struct v4l2_control getArg = {id, 0};
212 int r = xioctl(fd_, VIDIOC_G_CTRL, &getArg);
213 if (r == 0) {
214 if (getArg.value == value) {
215 printf("Camera control %s was already %d\n", name, getArg.value);
216 return true;
217 }
218 } else if (errno == EINVAL) {
219 printf("Camera control %s is invalid\n", name);
220 errno = 0;
221 return false;
222 }
223
224 struct v4l2_control setArg = {id, value};
225 r = xioctl(fd_, VIDIOC_S_CTRL, &setArg);
226 if (r == 0) {
227 printf("Set camera control %s from %d to %d\n", name, getArg.value, value);
228 return true;
229 }
230
231 printf("Couldn't set camera control %s to %d: %s (errno %d)\n",
232 name, value, strerror(errno), errno);
233 errno = 0;
234 return false;
235 }
236
brians343bc112013-02-10 01:53:46 +0000237 void Init() {
238 v4l2_capability cap;
239 if (xioctl(fd_, VIDIOC_QUERYCAP, &cap) == -1) {
240 if (EINVAL == errno) {
241 LOG(FATAL, "%s is no V4L2 device\n",
242 dev_name);
243 } else {
244 LOG(FATAL, "ioctl VIDIOC_QUERYCAP(%d, %p) failed with %d: %s\n",
245 fd_, &cap, errno, strerror(errno));
246 }
247 }
248 if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
249 LOG(FATAL, "%s is no video capture device\n",
250 dev_name);
251 }
252 if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
253 LOG(FATAL, "%s does not support streaming i/o\n",
254 dev_name);
255 }
256
257 /* Select video input, video standard and tune here. */
258
259 v4l2_cropcap cropcap;
260 CLEAR(cropcap);
261 cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
262 if (xioctl(fd_, VIDIOC_CROPCAP, &cropcap) == 0) {
263 v4l2_crop crop;
264 crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
265 crop.c = cropcap.defrect; /* reset to default */
266
267 if (xioctl(fd_, VIDIOC_S_CROP, &crop) == -1) {
268 switch (errno) {
269 case EINVAL:
270 /* Cropping not supported. */
271 break;
272 default:
273 /* Errors ignored. */
274 break;
275 }
276 }
277 } else {
278 /* Errors ignored. */
279 }
280
281 v4l2_format fmt;
282 CLEAR(fmt);
283 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
284 fmt.fmt.pix.width = Buffers::kWidth;
285 fmt.fmt.pix.height = Buffers::kHeight;
286 fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
287 fmt.fmt.pix.field = V4L2_FIELD_ANY;
288 //fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
289 //fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
290 if (xioctl(fd_, VIDIOC_S_FMT, &fmt) == -1) {
291 LOG(FATAL, "ioctl VIDIC_S_FMT(%d, %p) failed with %d: %s\n",
292 fd_, &fmt, errno, strerror(errno));
293 }
294 /* Note VIDIOC_S_FMT may change width and height. */
295
296 /* Buggy driver paranoia. */
297 unsigned int min = fmt.fmt.pix.width * 2;
298 if (fmt.fmt.pix.bytesperline < min)
299 fmt.fmt.pix.bytesperline = min;
300 min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
301 if (fmt.fmt.pix.sizeimage < min)
302 fmt.fmt.pix.sizeimage = min;
303
Austin Schuhb3fb8972013-04-04 05:46:09 +0000304 if (!SetCameraControl(V4L2_CID_EXPOSURE_AUTO,
305 "V4L2_CID_EXPOSURE_AUTO", V4L2_EXPOSURE_MANUAL)) {
306 LOG(FATAL, "Failed to set exposure\n");
307 }
308
309 if (!SetCameraControl(V4L2_CID_EXPOSURE_ABSOLUTE,
310 "V4L2_CID_EXPOSURE_ABSOLUTE", 65)) {
311 LOG(FATAL, "Failed to set exposure\n");
312 }
313
314 if (!SetCameraControl(V4L2_CID_BRIGHTNESS, "V4L2_CID_BRIGHTNESS", 128)) {
315 LOG(FATAL, "Failed to set up camera\n");
316 }
317
318 if (!SetCameraControl(V4L2_CID_GAIN, "V4L2_CID_GAIN", 0)) {
319 LOG(FATAL, "Failed to set up camera\n");
320 }
321
brians343bc112013-02-10 01:53:46 +0000322#if 0
323 // set framerate
324 struct v4l2_streamparm *setfps;
325 setfps = (struct v4l2_streamparm *) calloc(1, sizeof(struct v4l2_streamparm));
326 memset(setfps, 0, sizeof(struct v4l2_streamparm));
327 setfps->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
328 setfps->parm.capture.timeperframe.numerator = 1;
329 setfps->parm.capture.timeperframe.denominator = 20;
330 if (xioctl(fd_, VIDIOC_S_PARM, setfps) == -1) {
331 LOG(ERROR, "ioctl VIDIOC_S_PARM(%d, %p) failed with %d: %s\n",
332 fd_, setfps, errno, strerror(errno));
333 exit(EXIT_FAILURE);
334 }
335 LOG(INFO, "framerate ended up at %d/%d\n",
336 setfps->parm.capture.timeperframe.numerator,
337 setfps->parm.capture.timeperframe.denominator);
338#endif
339
340 init_mmap();
341 }
342
343 void Start() {
344 LOG(DEBUG, "queueing buffers for the first time\n");
345 v4l2_buffer buf;
346 for (unsigned int i = 0; i < Buffers::kNumBuffers; ++i) {
347 CLEAR(buf);
348 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
349 buf.memory = V4L2_MEMORY_MMAP;
350 buf.index = i;
351 QueueBuffer(&buf);
352 }
353 LOG(DEBUG, "done with first queue\n");
354
355 v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
356 if (xioctl(fd_, VIDIOC_STREAMON, &type) == -1) {
357 LOG(FATAL, "ioctl VIDIOC_STREAMON(%d, %p) failed with %d: %s\n",
358 fd_, &type, errno, strerror(errno));
359 }
360 }
361
362 public:
363 void Run() {
364 Start();
365
366 fd_set fds;
367 timeval tv;
368 while (true) {
369 // HAVE TO DO THIS EACH TIME THROUGH THE LOOP
370 tv.tv_sec = 2;
371 tv.tv_usec = 0;
372
373 FD_ZERO(&fds);
374 FD_SET(fd_, &fds);
375 FD_SET(server_fd_, &fds);
376 switch (select(std::max(fd_, server_fd_) + 1, &fds, NULL, NULL, &tv)) {
377 case -1:
378 if (errno != EINTR) {
379 LOG(ERROR, "select(%d, %p, NULL, NULL, %p) failed with %d: %s\n",
380 std::max(fd_, server_fd_) + 1, &fds, &tv, errno, strerror(errno));
381 }
382 continue;
383 case 0:
384 LOG(WARNING, "select timed out\n");
385 continue;
386 }
387
388 if (FD_ISSET(fd_, &fds)) {
Austin Schuhb3fb8972013-04-04 05:46:09 +0000389 LOG(INFO, "Got a frame\n");
brians343bc112013-02-10 01:53:46 +0000390 ReadFrame();
391 }
392 if (FD_ISSET(server_fd_, &fds)) {
393 const int sock = accept4(server_fd_, NULL, NULL, SOCK_NONBLOCK);
394 if (sock == -1) {
395 LOG(ERROR, "accept4(%d, NULL, NULL, SOCK_NONBLOCK(=%d) failed with %d: %s\n",
396 server_fd_, SOCK_NONBLOCK, errno, strerror(errno));
397 } else {
398 SendFD(sock);
399 }
400 }
401 }
402 }
403};
404const char *const Reader::dev_name = "/dev/video0";
405const aos_type_sig Reader::kRecycleSignature{
406 sizeof(Buffers::Message), 1, Buffers::kNumBuffers};
407
408} // namespace camera
409} // namespace aos
410
Brian Silverman598800f2013-05-09 17:08:42 -0700411int main() {
412 ::aos::InitNRT();
413 ::aos::camera::Reader reader;
414 reader.Run();
415 ::aos::Cleanup();
416}