blob: 9cd1a7df5f870e67604efdc7f83f17679411e417 [file] [log] [blame]
Brian Silvermanf665d692013-02-17 22:11:39 -08001#include "aos/atom_code/camera/Buffers.h"
brians343bc112013-02-10 01:53:46 +00002
3#include <sys/mman.h>
4
Brian Silvermanf665d692013-02-17 22:11:39 -08005#include "aos/atom_code/camera/V4L2.h"
6#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) {
26 LOG(ERROR, "socket(AF_UNIX, SOCK_STREAM, 0) failed with %d: %s\n",
27 errno, strerror(errno));
28 return -1;
29 }
30 addr.un.sun_family = AF_UNIX;
31 memset(addr.un.sun_path, 0, sizeof(addr.un.sun_path));
32 strcpy(addr.un.sun_path, kFDServerName.c_str());
33 if (bind_connect(r, &addr.addr, sizeof(addr.un)) == -1) {
34 LOG(ERROR, "bind_connect(=%p)(%d, %p, %zd) failed with %d: %s\n",
35 bind_connect, r, &addr.addr, sizeof(addr.un), errno, strerror(errno));
36 close(r); // what are we going to do about an error?
37 return -1;
38 }
39 return r;
40}
41
42void Buffers::MMap() {
43 buffers_ = new Buffer[kNumBuffers];
44 v4l2_buffer buf;
45 for (unsigned int n = 0; n < kNumBuffers; ++n) {
46 memset(&buf, 0, sizeof(buf));
47 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
48 buf.memory = V4L2_MEMORY_MMAP;
49 buf.index = n;
50 if (xioctl(fd_, VIDIOC_QUERYBUF, &buf) == -1) {
51 LOG(FATAL, "ioctl VIDIOC_QUERYBUF(%d, %p) failed with %d: %s\n",
52 fd_, &buf, errno, strerror(errno));
53 }
54 buffers_[n].length = buf.length;
55 buffers_[n].start = mmap(NULL, buf.length,
56 PROT_READ | PROT_WRITE, MAP_SHARED,
57 fd_, buf.m.offset);
58 if (buffers_[n].start == MAP_FAILED) {
59 LOG(FATAL, "mmap(NULL, %zd, PROT_READ | PROT_WRITE, MAP_SHARED, %d, %jd)"
60 " failed with %d: %s\n", buf.length, fd_, static_cast<intmax_t>(buf.m.offset),
61 errno, strerror(errno));
62 }
63 }
64}
65
66void Buffers::Release() {
67 if (message_ != NULL) {
Brian Silvermana6d1b562013-09-01 14:39:39 -070068 queue_->FreeMessage(message_);
brians343bc112013-02-10 01:53:46 +000069 message_ = NULL;
70 }
71}
72const void *Buffers::GetNext(bool block,
73 uint32_t *bytesused, timeval *timestamp, uint32_t *sequence) {
74 Release();
75
76 // TODO(brians) make sure the camera reader process hasn't died
77 do {
78 if (block) {
Brian Silvermana6d1b562013-09-01 14:39:39 -070079 message_ = static_cast<const Message *>(queue_->ReadMessage(
Brian Silverman08661c72013-09-01 17:24:38 -070080 RawQueue::kPeek | RawQueue::kBlock));
brians343bc112013-02-10 01:53:46 +000081 } else {
82 static int index = 0;
Brian Silvermana6d1b562013-09-01 14:39:39 -070083 message_ = static_cast<const Message *>(queue_->ReadMessageIndex(
Brian Silverman08661c72013-09-01 17:24:38 -070084 RawQueue::kBlock, &index));
brians343bc112013-02-10 01:53:46 +000085 }
86 } while (block && message_ == NULL);
87 if (message_ != NULL) {
88 if (bytesused != NULL) memcpy(bytesused, &message_->bytesused, sizeof(*bytesused));
89 if (timestamp != NULL) memcpy(timestamp, &message_->timestamp, sizeof(*timestamp));
90 if (sequence != NULL) memcpy(sequence, &message_->sequence, sizeof(*sequence));
91 return buffers_[message_->index].start;
92 } else {
93 return NULL;
94 }
95}
96
97int Buffers::FetchFD() {
98 int myfds[Buffers::kNumFDs]; // where to retrieve the fds into
99 char buf[CMSG_SPACE(sizeof(myfds))]; // ancillary data buffer
100
101 iovec data;
102 memset(&data, 0, sizeof(data));
103 char dummy;
104 data.iov_base = &dummy;
105 data.iov_len = sizeof(dummy);
106 msghdr msg;
107 memset(&msg, 0, sizeof(msg));
108 msg.msg_iov = &data;
109 msg.msg_iovlen = 1;
110 msg.msg_control = buf;
111 msg.msg_controllen = sizeof(buf);
112
113 switch (recvmsg(server_, &msg, 0)) {
114 case 0: // "the peer has performed an orderly shutdown"
115 LOG(FATAL, "the fd server shut down (connected on %d)\n", server_);
116 case -1:
117 LOG(FATAL, "recvmsg(server_(=%d), %p, 0) failed with %d: %s\n",
118 server_, &msg, errno, strerror(errno));
119 }
120 const cmsghdr *const cmsg = CMSG_FIRSTHDR(&msg);
121 if (cmsg == NULL) {
122 LOG(FATAL, "no headers in message\n");
123 }
124 if (cmsg->cmsg_len != CMSG_LEN(sizeof(myfds))) {
125 LOG(FATAL, "got wrong size. got %d but expected %zd\n",
126 cmsg->cmsg_len, CMSG_LEN(sizeof(myfds)));
127 }
128 if (cmsg->cmsg_level != SOL_SOCKET) {
129 LOG(FATAL, "cmsg_level=%d. expected SOL_SOCKET(=%d)\n", cmsg->cmsg_level, SOL_SOCKET);
130 }
131 if (cmsg->cmsg_type != SCM_RIGHTS) {
132 LOG(FATAL, "cmsg_type=%d. expected SCM_RIGHTS(=%d)\n", cmsg->cmsg_type, SCM_RIGHTS);
133 }
134 memcpy(myfds, CMSG_DATA(cmsg), sizeof(myfds));
135
136 return myfds[0];
137}
138Buffers::Buffers() : server_(CreateSocket(connect)), fd_(FetchFD()), message_(NULL) {
139 MMap();
Brian Silverman08661c72013-09-01 17:24:38 -0700140 queue_ = RawQueue::Fetch(kQueueName.c_str(), sizeof(Message), 971, 1);
brians343bc112013-02-10 01:53:46 +0000141}
142
143Buffers::~Buffers() {
144 Release();
145
146 for (unsigned i = 0; i < kNumBuffers; ++i) {
147 if (munmap(buffers_[i].start, buffers_[i].length) == -1) {
148 LOG(WARNING, "munmap(%p, %zd) for destruction failed with %d: %s\n",
149 buffers_[i].start, buffers_[i].length, errno, strerror(errno));
150 }
151 }
152 delete[] buffers_;
153
154 if (close(fd_) == -1) {
155 LOG(WARNING, "close(%d) for destruction failed with %d: %s\n",
156 fd_, errno, strerror(errno));
157 }
158}
159
Brian Silvermana6d1b562013-09-01 14:39:39 -0700160} // namespace camera
161} // namespace aos