blob: 5f30cfe4bcf6b74f9a7c6ea46849724437bd3505 [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) {
Brian Silverman8efe23e2013-07-07 23:31:37 -0700124 LOG(WARNING, "ioctl VIDIOC_QBUF(%d, %p) failed with %d: %s."
125 " losing buf #%" PRIu32 "\n",
brians343bc112013-02-10 01:53:46 +0000126 fd_, &buf, errno, strerror(errno), buf->index);
127 } else {
Brian Silverman8efe23e2013-07-07 23:31:37 -0700128 LOG(DEBUG, "put buf #%" PRIu32 " into driver's queue\n", buf->index);
brians343bc112013-02-10 01:53:46 +0000129 ++queued_;
130 }
131 }
132 void ReadFrame() {
133 v4l2_buffer buf;
134 CLEAR(buf);
135 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
136 buf.memory = V4L2_MEMORY_MMAP;
137
138 const Buffers::Message *read;
139 do {
140 read = static_cast<const Buffers::Message *>(
141 // we block waiting for one if we can't dequeue one without leaving
142 // the driver <= 2 (to be safe)
143 aos_queue_read_msg(recycle_queue_, (queued_ <= 2) ? BLOCK : NON_BLOCK));
144 if (read != NULL) {
145 buf.index = read->index;
146 aos_queue_free_msg(recycle_queue_, read);
147 QueueBuffer(&buf);
148 }
149 } while (read != NULL);
150
151 if (xioctl(fd_, VIDIOC_DQBUF, &buf) == -1) {
152 if (errno != EAGAIN) {
153 LOG(ERROR, "ioctl VIDIOC_DQBUF(%d, %p) failed with %d: %s\n",
154 fd_, &buf, errno, strerror(errno));
155 }
156 return;
157 }
158 --queued_;
159 if (buf.index >= Buffers::kNumBuffers) {
Brian Silverman8efe23e2013-07-07 23:31:37 -0700160 LOG(ERROR, "buf.index (%" PRIu32 ") is >= kNumBuffers (%u)\n",
brians343bc112013-02-10 01:53:46 +0000161 buf.index, Buffers::kNumBuffers);
162 return;
163 }
164
165 Buffers::Message *const msg = static_cast<Buffers::Message *>(
166 aos_queue_get_msg(queue_));
167 if (msg == NULL) {
Brian Silverman8efe23e2013-07-07 23:31:37 -0700168 LOG(WARNING,
169 "couldn't get a message to send buf #%" PRIu32 " from queue %p."
brians343bc112013-02-10 01:53:46 +0000170 " re-queueing now\n", buf.index, queue_);
171 QueueBuffer(&buf);
172 return;
173 }
174 msg->index = buf.index;
175 msg->bytesused = buf.bytesused;
176 memcpy(&msg->timestamp, &buf.timestamp, sizeof(msg->timestamp));
177 msg->sequence = buf.sequence;
178 if (aos_queue_write_msg_free(queue_, msg, OVERRIDE) == -1) {
Brian Silverman8efe23e2013-07-07 23:31:37 -0700179 LOG(WARNING,
180 "sending message %p with buf #%" PRIu32 " to queue %p failed."
brians343bc112013-02-10 01:53:46 +0000181 " re-queueing now\n", msg, buf.index, queue_);
182 QueueBuffer(&buf);
183 return;
184 } else {
Brian Silverman8efe23e2013-07-07 23:31:37 -0700185 LOG(DEBUG, "sent message off to queue %p with buffer #%" PRIu32 "\n",
brians343bc112013-02-10 01:53:46 +0000186 queue_, buf.index);
187 }
188 }
189
190 void init_mmap() {
191 v4l2_requestbuffers req;
192 CLEAR(req);
193 req.count = Buffers::kNumBuffers;
194 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
195 req.memory = V4L2_MEMORY_MMAP;
196 if (xioctl(fd_, VIDIOC_REQBUFS, &req) == -1) {
197 if (EINVAL == errno) {
198 LOG(FATAL, "%s does not support memory mapping\n", dev_name);
199 } else {
200 LOG(FATAL, "ioctl VIDIOC_REQBUFS(%d, %p) failed with %d: %s\n",
201 fd_, &req, errno, strerror(errno));
202 }
203 }
204 queued_ = Buffers::kNumBuffers;
205 if (req.count < Buffers::kNumBuffers) {
206 LOG(FATAL, "Insufficient buffer memory on %s\n", dev_name);
207 }
208 }
209
Austin Schuhb3fb8972013-04-04 05:46:09 +0000210 // Sets one of the camera's user-control values.
211 // Prints the old and new values.
212 // Just prints a message if the camera doesn't support this control or value.
Brian Silverman8efe23e2013-07-07 23:31:37 -0700213 bool SetCameraControl(uint32_t id, const char *name, int value) {
214 struct v4l2_control getArg = {id, 0U};
Austin Schuhb3fb8972013-04-04 05:46:09 +0000215 int r = xioctl(fd_, VIDIOC_G_CTRL, &getArg);
216 if (r == 0) {
217 if (getArg.value == value) {
218 printf("Camera control %s was already %d\n", name, getArg.value);
219 return true;
220 }
221 } else if (errno == EINVAL) {
222 printf("Camera control %s is invalid\n", name);
223 errno = 0;
224 return false;
225 }
226
227 struct v4l2_control setArg = {id, value};
228 r = xioctl(fd_, VIDIOC_S_CTRL, &setArg);
229 if (r == 0) {
230 printf("Set camera control %s from %d to %d\n", name, getArg.value, value);
231 return true;
232 }
233
234 printf("Couldn't set camera control %s to %d: %s (errno %d)\n",
235 name, value, strerror(errno), errno);
236 errno = 0;
237 return false;
238 }
239
brians343bc112013-02-10 01:53:46 +0000240 void Init() {
241 v4l2_capability cap;
242 if (xioctl(fd_, VIDIOC_QUERYCAP, &cap) == -1) {
243 if (EINVAL == errno) {
244 LOG(FATAL, "%s is no V4L2 device\n",
245 dev_name);
246 } else {
247 LOG(FATAL, "ioctl VIDIOC_QUERYCAP(%d, %p) failed with %d: %s\n",
248 fd_, &cap, errno, strerror(errno));
249 }
250 }
251 if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
252 LOG(FATAL, "%s is no video capture device\n",
253 dev_name);
254 }
255 if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
256 LOG(FATAL, "%s does not support streaming i/o\n",
257 dev_name);
258 }
259
260 /* Select video input, video standard and tune here. */
261
262 v4l2_cropcap cropcap;
263 CLEAR(cropcap);
264 cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
265 if (xioctl(fd_, VIDIOC_CROPCAP, &cropcap) == 0) {
266 v4l2_crop crop;
267 crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
268 crop.c = cropcap.defrect; /* reset to default */
269
270 if (xioctl(fd_, VIDIOC_S_CROP, &crop) == -1) {
271 switch (errno) {
272 case EINVAL:
273 /* Cropping not supported. */
274 break;
275 default:
276 /* Errors ignored. */
277 break;
278 }
279 }
280 } else {
281 /* Errors ignored. */
282 }
283
284 v4l2_format fmt;
285 CLEAR(fmt);
286 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
287 fmt.fmt.pix.width = Buffers::kWidth;
288 fmt.fmt.pix.height = Buffers::kHeight;
289 fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
290 fmt.fmt.pix.field = V4L2_FIELD_ANY;
291 //fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
292 //fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
293 if (xioctl(fd_, VIDIOC_S_FMT, &fmt) == -1) {
294 LOG(FATAL, "ioctl VIDIC_S_FMT(%d, %p) failed with %d: %s\n",
295 fd_, &fmt, errno, strerror(errno));
296 }
297 /* Note VIDIOC_S_FMT may change width and height. */
298
299 /* Buggy driver paranoia. */
300 unsigned int min = fmt.fmt.pix.width * 2;
301 if (fmt.fmt.pix.bytesperline < min)
302 fmt.fmt.pix.bytesperline = min;
303 min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
304 if (fmt.fmt.pix.sizeimage < min)
305 fmt.fmt.pix.sizeimage = min;
306
Austin Schuhb3fb8972013-04-04 05:46:09 +0000307 if (!SetCameraControl(V4L2_CID_EXPOSURE_AUTO,
308 "V4L2_CID_EXPOSURE_AUTO", V4L2_EXPOSURE_MANUAL)) {
309 LOG(FATAL, "Failed to set exposure\n");
310 }
311
312 if (!SetCameraControl(V4L2_CID_EXPOSURE_ABSOLUTE,
313 "V4L2_CID_EXPOSURE_ABSOLUTE", 65)) {
314 LOG(FATAL, "Failed to set exposure\n");
315 }
316
317 if (!SetCameraControl(V4L2_CID_BRIGHTNESS, "V4L2_CID_BRIGHTNESS", 128)) {
318 LOG(FATAL, "Failed to set up camera\n");
319 }
320
321 if (!SetCameraControl(V4L2_CID_GAIN, "V4L2_CID_GAIN", 0)) {
322 LOG(FATAL, "Failed to set up camera\n");
323 }
324
brians343bc112013-02-10 01:53:46 +0000325#if 0
326 // set framerate
327 struct v4l2_streamparm *setfps;
328 setfps = (struct v4l2_streamparm *) calloc(1, sizeof(struct v4l2_streamparm));
329 memset(setfps, 0, sizeof(struct v4l2_streamparm));
330 setfps->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
331 setfps->parm.capture.timeperframe.numerator = 1;
332 setfps->parm.capture.timeperframe.denominator = 20;
333 if (xioctl(fd_, VIDIOC_S_PARM, setfps) == -1) {
334 LOG(ERROR, "ioctl VIDIOC_S_PARM(%d, %p) failed with %d: %s\n",
335 fd_, setfps, errno, strerror(errno));
336 exit(EXIT_FAILURE);
337 }
338 LOG(INFO, "framerate ended up at %d/%d\n",
339 setfps->parm.capture.timeperframe.numerator,
340 setfps->parm.capture.timeperframe.denominator);
341#endif
342
343 init_mmap();
344 }
345
346 void Start() {
347 LOG(DEBUG, "queueing buffers for the first time\n");
348 v4l2_buffer buf;
349 for (unsigned int i = 0; i < Buffers::kNumBuffers; ++i) {
350 CLEAR(buf);
351 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
352 buf.memory = V4L2_MEMORY_MMAP;
353 buf.index = i;
354 QueueBuffer(&buf);
355 }
356 LOG(DEBUG, "done with first queue\n");
357
358 v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
359 if (xioctl(fd_, VIDIOC_STREAMON, &type) == -1) {
360 LOG(FATAL, "ioctl VIDIOC_STREAMON(%d, %p) failed with %d: %s\n",
361 fd_, &type, errno, strerror(errno));
362 }
363 }
364
365 public:
366 void Run() {
367 Start();
368
369 fd_set fds;
370 timeval tv;
371 while (true) {
372 // HAVE TO DO THIS EACH TIME THROUGH THE LOOP
373 tv.tv_sec = 2;
374 tv.tv_usec = 0;
375
376 FD_ZERO(&fds);
377 FD_SET(fd_, &fds);
378 FD_SET(server_fd_, &fds);
379 switch (select(std::max(fd_, server_fd_) + 1, &fds, NULL, NULL, &tv)) {
380 case -1:
381 if (errno != EINTR) {
382 LOG(ERROR, "select(%d, %p, NULL, NULL, %p) failed with %d: %s\n",
383 std::max(fd_, server_fd_) + 1, &fds, &tv, errno, strerror(errno));
384 }
385 continue;
386 case 0:
387 LOG(WARNING, "select timed out\n");
388 continue;
389 }
390
391 if (FD_ISSET(fd_, &fds)) {
Austin Schuhb3fb8972013-04-04 05:46:09 +0000392 LOG(INFO, "Got a frame\n");
brians343bc112013-02-10 01:53:46 +0000393 ReadFrame();
394 }
395 if (FD_ISSET(server_fd_, &fds)) {
396 const int sock = accept4(server_fd_, NULL, NULL, SOCK_NONBLOCK);
397 if (sock == -1) {
398 LOG(ERROR, "accept4(%d, NULL, NULL, SOCK_NONBLOCK(=%d) failed with %d: %s\n",
399 server_fd_, SOCK_NONBLOCK, errno, strerror(errno));
400 } else {
401 SendFD(sock);
402 }
403 }
404 }
405 }
406};
407const char *const Reader::dev_name = "/dev/video0";
408const aos_type_sig Reader::kRecycleSignature{
409 sizeof(Buffers::Message), 1, Buffers::kNumBuffers};
410
411} // namespace camera
412} // namespace aos
413
Brian Silverman598800f2013-05-09 17:08:42 -0700414int main() {
415 ::aos::InitNRT();
416 ::aos::camera::Reader reader;
417 reader.Run();
418 ::aos::Cleanup();
419}