blob: 19c1d45c25c4dd439ad9118a32aa31acfa16bc52 [file] [log] [blame]
Brian Silverman14fd0fb2014-01-14 21:42:01 -08001#include "aos/linux_code/camera/Buffers.h"
brians343bc112013-02-10 01:53:46 +00002
3#include <sys/mman.h>
4
Brian Silverman14fd0fb2014-01-14 21:42:01 -08005#include "aos/linux_code/camera/V4L2.h"
Brian Silvermanf665d692013-02-17 22:11:39 -08006#include "aos/common/logging/logging.h"
7
brians343bc112013-02-10 01:53:46 +00008namespace aos {
9namespace camera {
10
11// Represents an actual v4l2 buffer.
12struct Buffers::Buffer {
13 void *start;
14 size_t length; // for munmap
15};
16const std::string Buffers::kFDServerName("/tmp/aos_fd_server");
17const std::string Buffers::kQueueName("CameraBufferQueue");
brians343bc112013-02-10 01:53:46 +000018
19int Buffers::CreateSocket(int (*bind_connect)(int, const sockaddr *, socklen_t)) {
20 union af_unix_sockaddr {
21 sockaddr_un un;
22 sockaddr addr;
23 } addr;
24 const int r = socket(AF_UNIX, SOCK_STREAM, 0);
25 if (r == -1) {
Brian Silverman6742a442013-11-03 12:58:42 -080026 LOG(FATAL, "socket(AF_UNIX, SOCK_STREAM, 0) failed with %d: %s\n",
brians343bc112013-02-10 01:53:46 +000027 errno, strerror(errno));
brians343bc112013-02-10 01:53:46 +000028 }
29 addr.un.sun_family = AF_UNIX;
30 memset(addr.un.sun_path, 0, sizeof(addr.un.sun_path));
31 strcpy(addr.un.sun_path, kFDServerName.c_str());
32 if (bind_connect(r, &addr.addr, sizeof(addr.un)) == -1) {
Brian Silverman6742a442013-11-03 12:58:42 -080033 LOG(FATAL, "bind_connect(=%p)(%d, %p, %zd) failed with %d: %s\n",
brians343bc112013-02-10 01:53:46 +000034 bind_connect, r, &addr.addr, sizeof(addr.un), errno, strerror(errno));
brians343bc112013-02-10 01:53:46 +000035 }
36 return r;
37}
38
39void Buffers::MMap() {
40 buffers_ = new Buffer[kNumBuffers];
41 v4l2_buffer buf;
42 for (unsigned int n = 0; n < kNumBuffers; ++n) {
43 memset(&buf, 0, sizeof(buf));
44 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
45 buf.memory = V4L2_MEMORY_MMAP;
46 buf.index = n;
47 if (xioctl(fd_, VIDIOC_QUERYBUF, &buf) == -1) {
48 LOG(FATAL, "ioctl VIDIOC_QUERYBUF(%d, %p) failed with %d: %s\n",
49 fd_, &buf, errno, strerror(errno));
50 }
51 buffers_[n].length = buf.length;
52 buffers_[n].start = mmap(NULL, buf.length,
53 PROT_READ | PROT_WRITE, MAP_SHARED,
54 fd_, buf.m.offset);
55 if (buffers_[n].start == MAP_FAILED) {
56 LOG(FATAL, "mmap(NULL, %zd, PROT_READ | PROT_WRITE, MAP_SHARED, %d, %jd)"
57 " failed with %d: %s\n", buf.length, fd_, static_cast<intmax_t>(buf.m.offset),
58 errno, strerror(errno));
59 }
60 }
61}
62
63void Buffers::Release() {
Brian Silverman5f8c4922014-02-11 21:22:38 -080064 message_.reset();
brians343bc112013-02-10 01:53:46 +000065}
Brian Silverman5f8c4922014-02-11 21:22:38 -080066const void *Buffers::GetNext(bool block, uint32_t *bytesused,
67 timeval *timestamp, uint32_t *sequence) {
brians343bc112013-02-10 01:53:46 +000068 Release();
69
70 // TODO(brians) make sure the camera reader process hasn't died
71 do {
72 if (block) {
Brian Silverman5f8c4922014-02-11 21:22:38 -080073 message_.reset(static_cast<const Message *>(
74 queue_->ReadMessage(RawQueue::kPeek | RawQueue::kBlock)));
brians343bc112013-02-10 01:53:46 +000075 } else {
76 static int index = 0;
Brian Silverman5f8c4922014-02-11 21:22:38 -080077 message_.reset(static_cast<const Message *>(
78 queue_->ReadMessageIndex(RawQueue::kBlock, &index)));
brians343bc112013-02-10 01:53:46 +000079 }
80 } while (block && message_ == NULL);
81 if (message_ != NULL) {
82 if (bytesused != NULL) memcpy(bytesused, &message_->bytesused, sizeof(*bytesused));
83 if (timestamp != NULL) memcpy(timestamp, &message_->timestamp, sizeof(*timestamp));
84 if (sequence != NULL) memcpy(sequence, &message_->sequence, sizeof(*sequence));
85 return buffers_[message_->index].start;
86 } else {
87 return NULL;
88 }
89}
90
91int Buffers::FetchFD() {
92 int myfds[Buffers::kNumFDs]; // where to retrieve the fds into
93 char buf[CMSG_SPACE(sizeof(myfds))]; // ancillary data buffer
94
95 iovec data;
96 memset(&data, 0, sizeof(data));
97 char dummy;
98 data.iov_base = &dummy;
99 data.iov_len = sizeof(dummy);
100 msghdr msg;
101 memset(&msg, 0, sizeof(msg));
102 msg.msg_iov = &data;
103 msg.msg_iovlen = 1;
104 msg.msg_control = buf;
105 msg.msg_controllen = sizeof(buf);
106
107 switch (recvmsg(server_, &msg, 0)) {
108 case 0: // "the peer has performed an orderly shutdown"
109 LOG(FATAL, "the fd server shut down (connected on %d)\n", server_);
110 case -1:
111 LOG(FATAL, "recvmsg(server_(=%d), %p, 0) failed with %d: %s\n",
112 server_, &msg, errno, strerror(errno));
113 }
114 const cmsghdr *const cmsg = CMSG_FIRSTHDR(&msg);
115 if (cmsg == NULL) {
116 LOG(FATAL, "no headers in message\n");
117 }
118 if (cmsg->cmsg_len != CMSG_LEN(sizeof(myfds))) {
119 LOG(FATAL, "got wrong size. got %d but expected %zd\n",
120 cmsg->cmsg_len, CMSG_LEN(sizeof(myfds)));
121 }
122 if (cmsg->cmsg_level != SOL_SOCKET) {
123 LOG(FATAL, "cmsg_level=%d. expected SOL_SOCKET(=%d)\n", cmsg->cmsg_level, SOL_SOCKET);
124 }
125 if (cmsg->cmsg_type != SCM_RIGHTS) {
126 LOG(FATAL, "cmsg_type=%d. expected SCM_RIGHTS(=%d)\n", cmsg->cmsg_type, SCM_RIGHTS);
127 }
128 memcpy(myfds, CMSG_DATA(cmsg), sizeof(myfds));
129
130 return myfds[0];
131}
Brian Silverman5f8c4922014-02-11 21:22:38 -0800132Buffers::Buffers()
133 : server_(CreateSocket(connect)),
134 fd_(FetchFD()),
135 queue_(RawQueue::Fetch(kQueueName.c_str(), sizeof(Message), 971, 1)),
136 message_(queue_) {
brians343bc112013-02-10 01:53:46 +0000137 MMap();
brians343bc112013-02-10 01:53:46 +0000138}
139
140Buffers::~Buffers() {
141 Release();
142
143 for (unsigned i = 0; i < kNumBuffers; ++i) {
144 if (munmap(buffers_[i].start, buffers_[i].length) == -1) {
145 LOG(WARNING, "munmap(%p, %zd) for destruction failed with %d: %s\n",
146 buffers_[i].start, buffers_[i].length, errno, strerror(errno));
147 }
148 }
149 delete[] buffers_;
150
151 if (close(fd_) == -1) {
152 LOG(WARNING, "close(%d) for destruction failed with %d: %s\n",
153 fd_, errno, strerror(errno));
154 }
155}
156
Brian Silvermana6d1b562013-09-01 14:39:39 -0700157} // namespace camera
158} // namespace aos