blob: 95f6128a7d02fe4e51e72c9f2a61ccd109613e0b [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"
Brian Silverman08661c72013-09-01 17:24:38 -070020#include "aos/atom_code/ipc_lib/queue.h"
brians343bc112013-02-10 01:53:46 +000021
22#define CLEAR(x) memset(&(x), 0, sizeof(x))
23
24namespace aos {
25namespace camera {
26
27class Reader {
28 static const char *const dev_name;
29
30 // of the camera
31 int fd_;
32 // the bound socket listening for fd requests
33 int server_fd_;
34
Brian Silverman08661c72013-09-01 17:24:38 -070035 RawQueue *queue_, *recycle_queue_;
brians343bc112013-02-10 01:53:46 +000036 // 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
Brian Silverman08661c72013-09-01 17:24:38 -070055 queue_ = RawQueue::Fetch(Buffers::kQueueName.c_str(),
Brian Silvermana6d1b562013-09-01 14:39:39 -070056 sizeof(Buffers::Message), 971, 1,
57 1, Buffers::kNumBuffers, &recycle_queue_);
brians343bc112013-02-10 01:53:46 +000058 // read off any existing recycled messages
Brian Silverman08661c72013-09-01 17:24:38 -070059 while (recycle_queue_->ReadMessage(RawQueue::kNonBlock) != NULL);
brians343bc112013-02-10 01:53:46 +000060 queued_ = 0;
61
62 InitServer();
63 Init();
64 }
65 private:
66 void InitServer() {
67 if (unlink(Buffers::kFDServerName.c_str()) == -1 && errno != ENOENT) {
68 LOG(WARNING, "unlink(kFDServerName(='%s')) failed with %d: %s\n",
69 Buffers::kFDServerName.c_str(), errno, strerror(errno));
70 }
71 if ((server_fd_ = Buffers::CreateSocket(bind)) == -1) {
72 LOG(FATAL, "creating the IPC socket failed\n");
73 }
74 if (listen(server_fd_, 10) == -1) {
75 LOG(FATAL, "listen(%d, 10) failed with %d: %s\n",
76 server_fd_, errno, strerror(errno));
77 }
78 }
79 void SendFD(const int sock) {
80 int myfds[Buffers::kNumFDs]; /* Contains the file descriptors to pass. */
81 myfds[0] = fd_;
82 char buf[CMSG_SPACE(sizeof(myfds))]; /* ancillary data buffer */
83
84 iovec data;
85 memset(&data, 0, sizeof(data));
86 char dummy = 'A';
87 data.iov_base = &dummy;
88 data.iov_len = sizeof(dummy);
89 msghdr msg;
90 memset(&msg, 0, sizeof(msg));
91 msg.msg_iov = &data;
92 msg.msg_iovlen = 1;
93 msg.msg_control = buf;
94 msg.msg_controllen = sizeof(buf);
95 cmsghdr *const cmsg = CMSG_FIRSTHDR(&msg);
96 cmsg->cmsg_level = SOL_SOCKET;
97 cmsg->cmsg_type = SCM_RIGHTS;
98 cmsg->cmsg_len = CMSG_LEN(sizeof(myfds));
99 /* Initialize the payload: */
100 memcpy(CMSG_DATA(cmsg), myfds, sizeof(myfds));
101 if (sendmsg(sock, &msg, 0) == -1) {
102 LOG(ERROR, "sendmsg(%d, %p, 0) failed with %d: %s\n",
103 sock, &msg, errno, strerror(errno));
104 }
105 // leave it open so that the other end can tell if this process dies
106 }
107
108#if 0
109 // if we ever do want to do any of these things, this is how
110 void Stop() {
111 const v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
112 if (xioctl(fd_, VIDIOC_STREAMOFF, &type) == -1) {
113 errno_exit("VIDIOC_STREAMOFF");
114 }
115 }
116 void Close() {
117 if (close(fd_) == -1)
118 errno_exit("close");
119 fd_ = -1;
120 }
121#endif
122
123 void QueueBuffer(v4l2_buffer *buf) {
124 if (xioctl(fd_, VIDIOC_QBUF, buf) == -1) {
Brian Silverman8efe23e2013-07-07 23:31:37 -0700125 LOG(WARNING, "ioctl VIDIOC_QBUF(%d, %p) failed with %d: %s."
126 " losing buf #%" PRIu32 "\n",
brians343bc112013-02-10 01:53:46 +0000127 fd_, &buf, errno, strerror(errno), buf->index);
128 } else {
Brian Silverman8efe23e2013-07-07 23:31:37 -0700129 LOG(DEBUG, "put buf #%" PRIu32 " into driver's queue\n", buf->index);
brians343bc112013-02-10 01:53:46 +0000130 ++queued_;
131 }
132 }
133 void ReadFrame() {
134 v4l2_buffer buf;
135 CLEAR(buf);
136 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
137 buf.memory = V4L2_MEMORY_MMAP;
138
139 const Buffers::Message *read;
140 do {
141 read = static_cast<const Buffers::Message *>(
142 // we block waiting for one if we can't dequeue one without leaving
143 // the driver <= 2 (to be safe)
Brian Silvermana6d1b562013-09-01 14:39:39 -0700144 recycle_queue_->ReadMessage((queued_ <= 2) ?
Brian Silverman08661c72013-09-01 17:24:38 -0700145 RawQueue::kBlock : RawQueue::kNonBlock));
brians343bc112013-02-10 01:53:46 +0000146 if (read != NULL) {
147 buf.index = read->index;
Brian Silvermana6d1b562013-09-01 14:39:39 -0700148 recycle_queue_->FreeMessage(read);
brians343bc112013-02-10 01:53:46 +0000149 QueueBuffer(&buf);
150 }
151 } while (read != NULL);
152
153 if (xioctl(fd_, VIDIOC_DQBUF, &buf) == -1) {
154 if (errno != EAGAIN) {
155 LOG(ERROR, "ioctl VIDIOC_DQBUF(%d, %p) failed with %d: %s\n",
156 fd_, &buf, errno, strerror(errno));
157 }
158 return;
159 }
160 --queued_;
161 if (buf.index >= Buffers::kNumBuffers) {
Brian Silverman8efe23e2013-07-07 23:31:37 -0700162 LOG(ERROR, "buf.index (%" PRIu32 ") is >= kNumBuffers (%u)\n",
brians343bc112013-02-10 01:53:46 +0000163 buf.index, Buffers::kNumBuffers);
164 return;
165 }
166
167 Buffers::Message *const msg = static_cast<Buffers::Message *>(
Brian Silvermana6d1b562013-09-01 14:39:39 -0700168 queue_->GetMessage());
brians343bc112013-02-10 01:53:46 +0000169 if (msg == NULL) {
Brian Silverman8efe23e2013-07-07 23:31:37 -0700170 LOG(WARNING,
171 "couldn't get a message to send buf #%" PRIu32 " from queue %p."
brians343bc112013-02-10 01:53:46 +0000172 " re-queueing now\n", buf.index, queue_);
173 QueueBuffer(&buf);
174 return;
175 }
176 msg->index = buf.index;
177 msg->bytesused = buf.bytesused;
178 memcpy(&msg->timestamp, &buf.timestamp, sizeof(msg->timestamp));
179 msg->sequence = buf.sequence;
Brian Silverman08661c72013-09-01 17:24:38 -0700180 if (!queue_->WriteMessage(msg, RawQueue::kOverride)) {
Brian Silverman8efe23e2013-07-07 23:31:37 -0700181 LOG(WARNING,
182 "sending message %p with buf #%" PRIu32 " to queue %p failed."
brians343bc112013-02-10 01:53:46 +0000183 " re-queueing now\n", msg, buf.index, queue_);
184 QueueBuffer(&buf);
185 return;
186 } else {
Brian Silverman8efe23e2013-07-07 23:31:37 -0700187 LOG(DEBUG, "sent message off to queue %p with buffer #%" PRIu32 "\n",
brians343bc112013-02-10 01:53:46 +0000188 queue_, buf.index);
189 }
190 }
191
192 void init_mmap() {
193 v4l2_requestbuffers req;
194 CLEAR(req);
195 req.count = Buffers::kNumBuffers;
196 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
197 req.memory = V4L2_MEMORY_MMAP;
198 if (xioctl(fd_, VIDIOC_REQBUFS, &req) == -1) {
199 if (EINVAL == errno) {
200 LOG(FATAL, "%s does not support memory mapping\n", dev_name);
201 } else {
202 LOG(FATAL, "ioctl VIDIOC_REQBUFS(%d, %p) failed with %d: %s\n",
203 fd_, &req, errno, strerror(errno));
204 }
205 }
206 queued_ = Buffers::kNumBuffers;
207 if (req.count < Buffers::kNumBuffers) {
208 LOG(FATAL, "Insufficient buffer memory on %s\n", dev_name);
209 }
210 }
211
Austin Schuhb3fb8972013-04-04 05:46:09 +0000212 // Sets one of the camera's user-control values.
213 // Prints the old and new values.
214 // Just prints a message if the camera doesn't support this control or value.
Brian Silverman8efe23e2013-07-07 23:31:37 -0700215 bool SetCameraControl(uint32_t id, const char *name, int value) {
216 struct v4l2_control getArg = {id, 0U};
Austin Schuhb3fb8972013-04-04 05:46:09 +0000217 int r = xioctl(fd_, VIDIOC_G_CTRL, &getArg);
218 if (r == 0) {
219 if (getArg.value == value) {
220 printf("Camera control %s was already %d\n", name, getArg.value);
221 return true;
222 }
223 } else if (errno == EINVAL) {
224 printf("Camera control %s is invalid\n", name);
225 errno = 0;
226 return false;
227 }
228
229 struct v4l2_control setArg = {id, value};
230 r = xioctl(fd_, VIDIOC_S_CTRL, &setArg);
231 if (r == 0) {
232 printf("Set camera control %s from %d to %d\n", name, getArg.value, value);
233 return true;
234 }
235
236 printf("Couldn't set camera control %s to %d: %s (errno %d)\n",
237 name, value, strerror(errno), errno);
238 errno = 0;
239 return false;
240 }
241
brians343bc112013-02-10 01:53:46 +0000242 void Init() {
243 v4l2_capability cap;
244 if (xioctl(fd_, VIDIOC_QUERYCAP, &cap) == -1) {
245 if (EINVAL == errno) {
246 LOG(FATAL, "%s is no V4L2 device\n",
247 dev_name);
248 } else {
249 LOG(FATAL, "ioctl VIDIOC_QUERYCAP(%d, %p) failed with %d: %s\n",
250 fd_, &cap, errno, strerror(errno));
251 }
252 }
253 if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
254 LOG(FATAL, "%s is no video capture device\n",
255 dev_name);
256 }
257 if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
258 LOG(FATAL, "%s does not support streaming i/o\n",
259 dev_name);
260 }
261
262 /* Select video input, video standard and tune here. */
263
264 v4l2_cropcap cropcap;
265 CLEAR(cropcap);
266 cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
267 if (xioctl(fd_, VIDIOC_CROPCAP, &cropcap) == 0) {
268 v4l2_crop crop;
269 crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
270 crop.c = cropcap.defrect; /* reset to default */
271
272 if (xioctl(fd_, VIDIOC_S_CROP, &crop) == -1) {
273 switch (errno) {
274 case EINVAL:
275 /* Cropping not supported. */
276 break;
277 default:
278 /* Errors ignored. */
279 break;
280 }
281 }
282 } else {
283 /* Errors ignored. */
284 }
285
286 v4l2_format fmt;
287 CLEAR(fmt);
288 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
289 fmt.fmt.pix.width = Buffers::kWidth;
290 fmt.fmt.pix.height = Buffers::kHeight;
291 fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
292 fmt.fmt.pix.field = V4L2_FIELD_ANY;
293 //fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
294 //fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
295 if (xioctl(fd_, VIDIOC_S_FMT, &fmt) == -1) {
296 LOG(FATAL, "ioctl VIDIC_S_FMT(%d, %p) failed with %d: %s\n",
297 fd_, &fmt, errno, strerror(errno));
298 }
299 /* Note VIDIOC_S_FMT may change width and height. */
300
301 /* Buggy driver paranoia. */
302 unsigned int min = fmt.fmt.pix.width * 2;
303 if (fmt.fmt.pix.bytesperline < min)
304 fmt.fmt.pix.bytesperline = min;
305 min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
306 if (fmt.fmt.pix.sizeimage < min)
307 fmt.fmt.pix.sizeimage = min;
308
Austin Schuhb3fb8972013-04-04 05:46:09 +0000309 if (!SetCameraControl(V4L2_CID_EXPOSURE_AUTO,
310 "V4L2_CID_EXPOSURE_AUTO", V4L2_EXPOSURE_MANUAL)) {
311 LOG(FATAL, "Failed to set exposure\n");
312 }
313
314 if (!SetCameraControl(V4L2_CID_EXPOSURE_ABSOLUTE,
315 "V4L2_CID_EXPOSURE_ABSOLUTE", 65)) {
316 LOG(FATAL, "Failed to set exposure\n");
317 }
318
319 if (!SetCameraControl(V4L2_CID_BRIGHTNESS, "V4L2_CID_BRIGHTNESS", 128)) {
320 LOG(FATAL, "Failed to set up camera\n");
321 }
322
323 if (!SetCameraControl(V4L2_CID_GAIN, "V4L2_CID_GAIN", 0)) {
324 LOG(FATAL, "Failed to set up camera\n");
325 }
326
brians343bc112013-02-10 01:53:46 +0000327#if 0
328 // set framerate
329 struct v4l2_streamparm *setfps;
330 setfps = (struct v4l2_streamparm *) calloc(1, sizeof(struct v4l2_streamparm));
331 memset(setfps, 0, sizeof(struct v4l2_streamparm));
332 setfps->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
333 setfps->parm.capture.timeperframe.numerator = 1;
334 setfps->parm.capture.timeperframe.denominator = 20;
335 if (xioctl(fd_, VIDIOC_S_PARM, setfps) == -1) {
336 LOG(ERROR, "ioctl VIDIOC_S_PARM(%d, %p) failed with %d: %s\n",
337 fd_, setfps, errno, strerror(errno));
338 exit(EXIT_FAILURE);
339 }
340 LOG(INFO, "framerate ended up at %d/%d\n",
341 setfps->parm.capture.timeperframe.numerator,
342 setfps->parm.capture.timeperframe.denominator);
343#endif
344
345 init_mmap();
346 }
347
348 void Start() {
349 LOG(DEBUG, "queueing buffers for the first time\n");
350 v4l2_buffer buf;
351 for (unsigned int i = 0; i < Buffers::kNumBuffers; ++i) {
352 CLEAR(buf);
353 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
354 buf.memory = V4L2_MEMORY_MMAP;
355 buf.index = i;
356 QueueBuffer(&buf);
357 }
358 LOG(DEBUG, "done with first queue\n");
359
360 v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
361 if (xioctl(fd_, VIDIOC_STREAMON, &type) == -1) {
362 LOG(FATAL, "ioctl VIDIOC_STREAMON(%d, %p) failed with %d: %s\n",
363 fd_, &type, errno, strerror(errno));
364 }
365 }
366
367 public:
368 void Run() {
369 Start();
370
371 fd_set fds;
372 timeval tv;
373 while (true) {
374 // HAVE TO DO THIS EACH TIME THROUGH THE LOOP
375 tv.tv_sec = 2;
376 tv.tv_usec = 0;
377
378 FD_ZERO(&fds);
379 FD_SET(fd_, &fds);
380 FD_SET(server_fd_, &fds);
381 switch (select(std::max(fd_, server_fd_) + 1, &fds, NULL, NULL, &tv)) {
382 case -1:
383 if (errno != EINTR) {
384 LOG(ERROR, "select(%d, %p, NULL, NULL, %p) failed with %d: %s\n",
385 std::max(fd_, server_fd_) + 1, &fds, &tv, errno, strerror(errno));
386 }
387 continue;
388 case 0:
389 LOG(WARNING, "select timed out\n");
390 continue;
391 }
392
393 if (FD_ISSET(fd_, &fds)) {
Austin Schuhb3fb8972013-04-04 05:46:09 +0000394 LOG(INFO, "Got a frame\n");
brians343bc112013-02-10 01:53:46 +0000395 ReadFrame();
396 }
397 if (FD_ISSET(server_fd_, &fds)) {
398 const int sock = accept4(server_fd_, NULL, NULL, SOCK_NONBLOCK);
399 if (sock == -1) {
400 LOG(ERROR, "accept4(%d, NULL, NULL, SOCK_NONBLOCK(=%d) failed with %d: %s\n",
401 server_fd_, SOCK_NONBLOCK, errno, strerror(errno));
402 } else {
403 SendFD(sock);
404 }
405 }
406 }
407 }
408};
409const char *const Reader::dev_name = "/dev/video0";
brians343bc112013-02-10 01:53:46 +0000410
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}