blob: e1d22b67ae87c15b6cfdeb772d01c65415671c7d [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() {
64 if (message_ != NULL) {
Brian Silvermana6d1b562013-09-01 14:39:39 -070065 queue_->FreeMessage(message_);
brians343bc112013-02-10 01:53:46 +000066 message_ = NULL;
67 }
68}
69const void *Buffers::GetNext(bool block,
70 uint32_t *bytesused, timeval *timestamp, uint32_t *sequence) {
71 Release();
72
73 // TODO(brians) make sure the camera reader process hasn't died
74 do {
75 if (block) {
Brian Silvermana6d1b562013-09-01 14:39:39 -070076 message_ = static_cast<const Message *>(queue_->ReadMessage(
Brian Silverman08661c72013-09-01 17:24:38 -070077 RawQueue::kPeek | RawQueue::kBlock));
brians343bc112013-02-10 01:53:46 +000078 } else {
79 static int index = 0;
Brian Silvermana6d1b562013-09-01 14:39:39 -070080 message_ = static_cast<const Message *>(queue_->ReadMessageIndex(
Brian Silverman08661c72013-09-01 17:24:38 -070081 RawQueue::kBlock, &index));
brians343bc112013-02-10 01:53:46 +000082 }
83 } while (block && message_ == NULL);
84 if (message_ != NULL) {
85 if (bytesused != NULL) memcpy(bytesused, &message_->bytesused, sizeof(*bytesused));
86 if (timestamp != NULL) memcpy(timestamp, &message_->timestamp, sizeof(*timestamp));
87 if (sequence != NULL) memcpy(sequence, &message_->sequence, sizeof(*sequence));
88 return buffers_[message_->index].start;
89 } else {
90 return NULL;
91 }
92}
93
94int Buffers::FetchFD() {
95 int myfds[Buffers::kNumFDs]; // where to retrieve the fds into
96 char buf[CMSG_SPACE(sizeof(myfds))]; // ancillary data buffer
97
98 iovec data;
99 memset(&data, 0, sizeof(data));
100 char dummy;
101 data.iov_base = &dummy;
102 data.iov_len = sizeof(dummy);
103 msghdr msg;
104 memset(&msg, 0, sizeof(msg));
105 msg.msg_iov = &data;
106 msg.msg_iovlen = 1;
107 msg.msg_control = buf;
108 msg.msg_controllen = sizeof(buf);
109
110 switch (recvmsg(server_, &msg, 0)) {
111 case 0: // "the peer has performed an orderly shutdown"
112 LOG(FATAL, "the fd server shut down (connected on %d)\n", server_);
113 case -1:
114 LOG(FATAL, "recvmsg(server_(=%d), %p, 0) failed with %d: %s\n",
115 server_, &msg, errno, strerror(errno));
116 }
117 const cmsghdr *const cmsg = CMSG_FIRSTHDR(&msg);
118 if (cmsg == NULL) {
119 LOG(FATAL, "no headers in message\n");
120 }
121 if (cmsg->cmsg_len != CMSG_LEN(sizeof(myfds))) {
122 LOG(FATAL, "got wrong size. got %d but expected %zd\n",
123 cmsg->cmsg_len, CMSG_LEN(sizeof(myfds)));
124 }
125 if (cmsg->cmsg_level != SOL_SOCKET) {
126 LOG(FATAL, "cmsg_level=%d. expected SOL_SOCKET(=%d)\n", cmsg->cmsg_level, SOL_SOCKET);
127 }
128 if (cmsg->cmsg_type != SCM_RIGHTS) {
129 LOG(FATAL, "cmsg_type=%d. expected SCM_RIGHTS(=%d)\n", cmsg->cmsg_type, SCM_RIGHTS);
130 }
131 memcpy(myfds, CMSG_DATA(cmsg), sizeof(myfds));
132
133 return myfds[0];
134}
135Buffers::Buffers() : server_(CreateSocket(connect)), fd_(FetchFD()), message_(NULL) {
136 MMap();
Brian Silverman08661c72013-09-01 17:24:38 -0700137 queue_ = RawQueue::Fetch(kQueueName.c_str(), sizeof(Message), 971, 1);
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