blob: c87d1739635259dfe113fd7870d132299610722e [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
Brian Silvermana6d1b562013-09-01 14:39:39 -070034 Queue *queue_, *recycle_queue_;
brians343bc112013-02-10 01:53:46 +000035 // the number of buffers currently queued in v4l2
36 uint32_t queued_;
37 public:
38 Reader() {
39 struct stat st;
40 if (stat(dev_name, &st) == -1) {
41 LOG(FATAL, "Cannot identify '%s' because of %d: %s\n",
42 dev_name, errno, strerror(errno));
43 }
44 if (!S_ISCHR(st.st_mode)) {
45 LOG(FATAL, "%s is no device\n", dev_name);
46 }
47
48 fd_ = open(dev_name, O_RDWR /* required */ | O_NONBLOCK, 0);
49 if (fd_ == -1) {
50 LOG(FATAL, "Cannot open '%s' because of %d: %s\n",
51 dev_name, errno, strerror(errno));
52 }
53
Brian Silvermana6d1b562013-09-01 14:39:39 -070054 queue_ = Queue::Fetch(Buffers::kQueueName.c_str(),
55 sizeof(Buffers::Message), 971, 1,
56 1, Buffers::kNumBuffers, &recycle_queue_);
brians343bc112013-02-10 01:53:46 +000057 // read off any existing recycled messages
Brian Silvermana6d1b562013-09-01 14:39:39 -070058 while (recycle_queue_->ReadMessage(Queue::kNonBlock) != NULL);
brians343bc112013-02-10 01:53:46 +000059 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)
Brian Silvermana6d1b562013-09-01 14:39:39 -0700143 recycle_queue_->ReadMessage((queued_ <= 2) ?
144 Queue::kBlock : Queue::kNonBlock));
brians343bc112013-02-10 01:53:46 +0000145 if (read != NULL) {
146 buf.index = read->index;
Brian Silvermana6d1b562013-09-01 14:39:39 -0700147 recycle_queue_->FreeMessage(read);
brians343bc112013-02-10 01:53:46 +0000148 QueueBuffer(&buf);
149 }
150 } while (read != NULL);
151
152 if (xioctl(fd_, VIDIOC_DQBUF, &buf) == -1) {
153 if (errno != EAGAIN) {
154 LOG(ERROR, "ioctl VIDIOC_DQBUF(%d, %p) failed with %d: %s\n",
155 fd_, &buf, errno, strerror(errno));
156 }
157 return;
158 }
159 --queued_;
160 if (buf.index >= Buffers::kNumBuffers) {
Brian Silverman8efe23e2013-07-07 23:31:37 -0700161 LOG(ERROR, "buf.index (%" PRIu32 ") is >= kNumBuffers (%u)\n",
brians343bc112013-02-10 01:53:46 +0000162 buf.index, Buffers::kNumBuffers);
163 return;
164 }
165
166 Buffers::Message *const msg = static_cast<Buffers::Message *>(
Brian Silvermana6d1b562013-09-01 14:39:39 -0700167 queue_->GetMessage());
brians343bc112013-02-10 01:53:46 +0000168 if (msg == NULL) {
Brian Silverman8efe23e2013-07-07 23:31:37 -0700169 LOG(WARNING,
170 "couldn't get a message to send buf #%" PRIu32 " from queue %p."
brians343bc112013-02-10 01:53:46 +0000171 " re-queueing now\n", buf.index, queue_);
172 QueueBuffer(&buf);
173 return;
174 }
175 msg->index = buf.index;
176 msg->bytesused = buf.bytesused;
177 memcpy(&msg->timestamp, &buf.timestamp, sizeof(msg->timestamp));
178 msg->sequence = buf.sequence;
Brian Silvermana6d1b562013-09-01 14:39:39 -0700179 if (!queue->WriteMessage(msg, Queue::kOverride)) {
Brian Silverman8efe23e2013-07-07 23:31:37 -0700180 LOG(WARNING,
181 "sending message %p with buf #%" PRIu32 " to queue %p failed."
brians343bc112013-02-10 01:53:46 +0000182 " re-queueing now\n", msg, buf.index, queue_);
183 QueueBuffer(&buf);
184 return;
185 } else {
Brian Silverman8efe23e2013-07-07 23:31:37 -0700186 LOG(DEBUG, "sent message off to queue %p with buffer #%" PRIu32 "\n",
brians343bc112013-02-10 01:53:46 +0000187 queue_, buf.index);
188 }
189 }
190
191 void init_mmap() {
192 v4l2_requestbuffers req;
193 CLEAR(req);
194 req.count = Buffers::kNumBuffers;
195 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
196 req.memory = V4L2_MEMORY_MMAP;
197 if (xioctl(fd_, VIDIOC_REQBUFS, &req) == -1) {
198 if (EINVAL == errno) {
199 LOG(FATAL, "%s does not support memory mapping\n", dev_name);
200 } else {
201 LOG(FATAL, "ioctl VIDIOC_REQBUFS(%d, %p) failed with %d: %s\n",
202 fd_, &req, errno, strerror(errno));
203 }
204 }
205 queued_ = Buffers::kNumBuffers;
206 if (req.count < Buffers::kNumBuffers) {
207 LOG(FATAL, "Insufficient buffer memory on %s\n", dev_name);
208 }
209 }
210
Austin Schuhb3fb8972013-04-04 05:46:09 +0000211 // Sets one of the camera's user-control values.
212 // Prints the old and new values.
213 // Just prints a message if the camera doesn't support this control or value.
Brian Silverman8efe23e2013-07-07 23:31:37 -0700214 bool SetCameraControl(uint32_t id, const char *name, int value) {
215 struct v4l2_control getArg = {id, 0U};
Austin Schuhb3fb8972013-04-04 05:46:09 +0000216 int r = xioctl(fd_, VIDIOC_G_CTRL, &getArg);
217 if (r == 0) {
218 if (getArg.value == value) {
219 printf("Camera control %s was already %d\n", name, getArg.value);
220 return true;
221 }
222 } else if (errno == EINVAL) {
223 printf("Camera control %s is invalid\n", name);
224 errno = 0;
225 return false;
226 }
227
228 struct v4l2_control setArg = {id, value};
229 r = xioctl(fd_, VIDIOC_S_CTRL, &setArg);
230 if (r == 0) {
231 printf("Set camera control %s from %d to %d\n", name, getArg.value, value);
232 return true;
233 }
234
235 printf("Couldn't set camera control %s to %d: %s (errno %d)\n",
236 name, value, strerror(errno), errno);
237 errno = 0;
238 return false;
239 }
240
brians343bc112013-02-10 01:53:46 +0000241 void Init() {
242 v4l2_capability cap;
243 if (xioctl(fd_, VIDIOC_QUERYCAP, &cap) == -1) {
244 if (EINVAL == errno) {
245 LOG(FATAL, "%s is no V4L2 device\n",
246 dev_name);
247 } else {
248 LOG(FATAL, "ioctl VIDIOC_QUERYCAP(%d, %p) failed with %d: %s\n",
249 fd_, &cap, errno, strerror(errno));
250 }
251 }
252 if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
253 LOG(FATAL, "%s is no video capture device\n",
254 dev_name);
255 }
256 if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
257 LOG(FATAL, "%s does not support streaming i/o\n",
258 dev_name);
259 }
260
261 /* Select video input, video standard and tune here. */
262
263 v4l2_cropcap cropcap;
264 CLEAR(cropcap);
265 cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
266 if (xioctl(fd_, VIDIOC_CROPCAP, &cropcap) == 0) {
267 v4l2_crop crop;
268 crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
269 crop.c = cropcap.defrect; /* reset to default */
270
271 if (xioctl(fd_, VIDIOC_S_CROP, &crop) == -1) {
272 switch (errno) {
273 case EINVAL:
274 /* Cropping not supported. */
275 break;
276 default:
277 /* Errors ignored. */
278 break;
279 }
280 }
281 } else {
282 /* Errors ignored. */
283 }
284
285 v4l2_format fmt;
286 CLEAR(fmt);
287 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
288 fmt.fmt.pix.width = Buffers::kWidth;
289 fmt.fmt.pix.height = Buffers::kHeight;
290 fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
291 fmt.fmt.pix.field = V4L2_FIELD_ANY;
292 //fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
293 //fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
294 if (xioctl(fd_, VIDIOC_S_FMT, &fmt) == -1) {
295 LOG(FATAL, "ioctl VIDIC_S_FMT(%d, %p) failed with %d: %s\n",
296 fd_, &fmt, errno, strerror(errno));
297 }
298 /* Note VIDIOC_S_FMT may change width and height. */
299
300 /* Buggy driver paranoia. */
301 unsigned int min = fmt.fmt.pix.width * 2;
302 if (fmt.fmt.pix.bytesperline < min)
303 fmt.fmt.pix.bytesperline = min;
304 min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
305 if (fmt.fmt.pix.sizeimage < min)
306 fmt.fmt.pix.sizeimage = min;
307
Austin Schuhb3fb8972013-04-04 05:46:09 +0000308 if (!SetCameraControl(V4L2_CID_EXPOSURE_AUTO,
309 "V4L2_CID_EXPOSURE_AUTO", V4L2_EXPOSURE_MANUAL)) {
310 LOG(FATAL, "Failed to set exposure\n");
311 }
312
313 if (!SetCameraControl(V4L2_CID_EXPOSURE_ABSOLUTE,
314 "V4L2_CID_EXPOSURE_ABSOLUTE", 65)) {
315 LOG(FATAL, "Failed to set exposure\n");
316 }
317
318 if (!SetCameraControl(V4L2_CID_BRIGHTNESS, "V4L2_CID_BRIGHTNESS", 128)) {
319 LOG(FATAL, "Failed to set up camera\n");
320 }
321
322 if (!SetCameraControl(V4L2_CID_GAIN, "V4L2_CID_GAIN", 0)) {
323 LOG(FATAL, "Failed to set up camera\n");
324 }
325
brians343bc112013-02-10 01:53:46 +0000326#if 0
327 // set framerate
328 struct v4l2_streamparm *setfps;
329 setfps = (struct v4l2_streamparm *) calloc(1, sizeof(struct v4l2_streamparm));
330 memset(setfps, 0, sizeof(struct v4l2_streamparm));
331 setfps->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
332 setfps->parm.capture.timeperframe.numerator = 1;
333 setfps->parm.capture.timeperframe.denominator = 20;
334 if (xioctl(fd_, VIDIOC_S_PARM, setfps) == -1) {
335 LOG(ERROR, "ioctl VIDIOC_S_PARM(%d, %p) failed with %d: %s\n",
336 fd_, setfps, errno, strerror(errno));
337 exit(EXIT_FAILURE);
338 }
339 LOG(INFO, "framerate ended up at %d/%d\n",
340 setfps->parm.capture.timeperframe.numerator,
341 setfps->parm.capture.timeperframe.denominator);
342#endif
343
344 init_mmap();
345 }
346
347 void Start() {
348 LOG(DEBUG, "queueing buffers for the first time\n");
349 v4l2_buffer buf;
350 for (unsigned int i = 0; i < Buffers::kNumBuffers; ++i) {
351 CLEAR(buf);
352 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
353 buf.memory = V4L2_MEMORY_MMAP;
354 buf.index = i;
355 QueueBuffer(&buf);
356 }
357 LOG(DEBUG, "done with first queue\n");
358
359 v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
360 if (xioctl(fd_, VIDIOC_STREAMON, &type) == -1) {
361 LOG(FATAL, "ioctl VIDIOC_STREAMON(%d, %p) failed with %d: %s\n",
362 fd_, &type, errno, strerror(errno));
363 }
364 }
365
366 public:
367 void Run() {
368 Start();
369
370 fd_set fds;
371 timeval tv;
372 while (true) {
373 // HAVE TO DO THIS EACH TIME THROUGH THE LOOP
374 tv.tv_sec = 2;
375 tv.tv_usec = 0;
376
377 FD_ZERO(&fds);
378 FD_SET(fd_, &fds);
379 FD_SET(server_fd_, &fds);
380 switch (select(std::max(fd_, server_fd_) + 1, &fds, NULL, NULL, &tv)) {
381 case -1:
382 if (errno != EINTR) {
383 LOG(ERROR, "select(%d, %p, NULL, NULL, %p) failed with %d: %s\n",
384 std::max(fd_, server_fd_) + 1, &fds, &tv, errno, strerror(errno));
385 }
386 continue;
387 case 0:
388 LOG(WARNING, "select timed out\n");
389 continue;
390 }
391
392 if (FD_ISSET(fd_, &fds)) {
Austin Schuhb3fb8972013-04-04 05:46:09 +0000393 LOG(INFO, "Got a frame\n");
brians343bc112013-02-10 01:53:46 +0000394 ReadFrame();
395 }
396 if (FD_ISSET(server_fd_, &fds)) {
397 const int sock = accept4(server_fd_, NULL, NULL, SOCK_NONBLOCK);
398 if (sock == -1) {
399 LOG(ERROR, "accept4(%d, NULL, NULL, SOCK_NONBLOCK(=%d) failed with %d: %s\n",
400 server_fd_, SOCK_NONBLOCK, errno, strerror(errno));
401 } else {
402 SendFD(sock);
403 }
404 }
405 }
406 }
407};
408const char *const Reader::dev_name = "/dev/video0";
brians343bc112013-02-10 01:53:46 +0000409
410} // namespace camera
411} // namespace aos
412
Brian Silverman598800f2013-05-09 17:08:42 -0700413int main() {
414 ::aos::InitNRT();
415 ::aos::camera::Reader reader;
416 reader.Run();
417 ::aos::Cleanup();
418}