blob: 22b733700d3035d0ba86b124527ce62f9b0c2cf9 [file] [log] [blame]
brians343bc112013-02-10 01:53:46 +00001#include "Buffers.h"
2#include "V4L2.h"
3
4#include <sys/mman.h>
5
6namespace aos {
7namespace camera {
8
9// Represents an actual v4l2 buffer.
10struct Buffers::Buffer {
11 void *start;
12 size_t length; // for munmap
13};
14const std::string Buffers::kFDServerName("/tmp/aos_fd_server");
15const std::string Buffers::kQueueName("CameraBufferQueue");
16const aos_type_sig Buffers::kSignature{sizeof(Message), 971, 1};
17
18int Buffers::CreateSocket(int (*bind_connect)(int, const sockaddr *, socklen_t)) {
19 union af_unix_sockaddr {
20 sockaddr_un un;
21 sockaddr addr;
22 } addr;
23 const int r = socket(AF_UNIX, SOCK_STREAM, 0);
24 if (r == -1) {
25 LOG(ERROR, "socket(AF_UNIX, SOCK_STREAM, 0) failed with %d: %s\n",
26 errno, strerror(errno));
27 return -1;
28 }
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) {
33 LOG(ERROR, "bind_connect(=%p)(%d, %p, %zd) failed with %d: %s\n",
34 bind_connect, r, &addr.addr, sizeof(addr.un), errno, strerror(errno));
35 close(r); // what are we going to do about an error?
36 return -1;
37 }
38 return r;
39}
40
41void Buffers::MMap() {
42 buffers_ = new Buffer[kNumBuffers];
43 v4l2_buffer buf;
44 for (unsigned int n = 0; n < kNumBuffers; ++n) {
45 memset(&buf, 0, sizeof(buf));
46 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
47 buf.memory = V4L2_MEMORY_MMAP;
48 buf.index = n;
49 if (xioctl(fd_, VIDIOC_QUERYBUF, &buf) == -1) {
50 LOG(FATAL, "ioctl VIDIOC_QUERYBUF(%d, %p) failed with %d: %s\n",
51 fd_, &buf, errno, strerror(errno));
52 }
53 buffers_[n].length = buf.length;
54 buffers_[n].start = mmap(NULL, buf.length,
55 PROT_READ | PROT_WRITE, MAP_SHARED,
56 fd_, buf.m.offset);
57 if (buffers_[n].start == MAP_FAILED) {
58 LOG(FATAL, "mmap(NULL, %zd, PROT_READ | PROT_WRITE, MAP_SHARED, %d, %jd)"
59 " failed with %d: %s\n", buf.length, fd_, static_cast<intmax_t>(buf.m.offset),
60 errno, strerror(errno));
61 }
62 }
63}
64
65void Buffers::Release() {
66 if (message_ != NULL) {
67 aos_queue_free_msg(queue_, message_);
68 message_ = NULL;
69 }
70}
71const void *Buffers::GetNext(bool block,
72 uint32_t *bytesused, timeval *timestamp, uint32_t *sequence) {
73 Release();
74
75 // TODO(brians) make sure the camera reader process hasn't died
76 do {
77 if (block) {
78 message_ = static_cast<const Message *>(aos_queue_read_msg(queue_, PEEK | BLOCK));
79 } else {
80 static int index = 0;
81 message_ = static_cast<const Message *>(aos_queue_read_msg_index(queue_, BLOCK,
82 &index));
83 }
84 } while (block && message_ == NULL);
85 if (message_ != NULL) {
86 if (bytesused != NULL) memcpy(bytesused, &message_->bytesused, sizeof(*bytesused));
87 if (timestamp != NULL) memcpy(timestamp, &message_->timestamp, sizeof(*timestamp));
88 if (sequence != NULL) memcpy(sequence, &message_->sequence, sizeof(*sequence));
89 return buffers_[message_->index].start;
90 } else {
91 return NULL;
92 }
93}
94
95int Buffers::FetchFD() {
96 int myfds[Buffers::kNumFDs]; // where to retrieve the fds into
97 char buf[CMSG_SPACE(sizeof(myfds))]; // ancillary data buffer
98
99 iovec data;
100 memset(&data, 0, sizeof(data));
101 char dummy;
102 data.iov_base = &dummy;
103 data.iov_len = sizeof(dummy);
104 msghdr msg;
105 memset(&msg, 0, sizeof(msg));
106 msg.msg_iov = &data;
107 msg.msg_iovlen = 1;
108 msg.msg_control = buf;
109 msg.msg_controllen = sizeof(buf);
110
111 switch (recvmsg(server_, &msg, 0)) {
112 case 0: // "the peer has performed an orderly shutdown"
113 LOG(FATAL, "the fd server shut down (connected on %d)\n", server_);
114 case -1:
115 LOG(FATAL, "recvmsg(server_(=%d), %p, 0) failed with %d: %s\n",
116 server_, &msg, errno, strerror(errno));
117 }
118 const cmsghdr *const cmsg = CMSG_FIRSTHDR(&msg);
119 if (cmsg == NULL) {
120 LOG(FATAL, "no headers in message\n");
121 }
122 if (cmsg->cmsg_len != CMSG_LEN(sizeof(myfds))) {
123 LOG(FATAL, "got wrong size. got %d but expected %zd\n",
124 cmsg->cmsg_len, CMSG_LEN(sizeof(myfds)));
125 }
126 if (cmsg->cmsg_level != SOL_SOCKET) {
127 LOG(FATAL, "cmsg_level=%d. expected SOL_SOCKET(=%d)\n", cmsg->cmsg_level, SOL_SOCKET);
128 }
129 if (cmsg->cmsg_type != SCM_RIGHTS) {
130 LOG(FATAL, "cmsg_type=%d. expected SCM_RIGHTS(=%d)\n", cmsg->cmsg_type, SCM_RIGHTS);
131 }
132 memcpy(myfds, CMSG_DATA(cmsg), sizeof(myfds));
133
134 return myfds[0];
135}
136Buffers::Buffers() : server_(CreateSocket(connect)), fd_(FetchFD()), message_(NULL) {
137 MMap();
138 queue_ = aos_fetch_queue(kQueueName.c_str(), &kSignature);
139}
140
141Buffers::~Buffers() {
142 Release();
143
144 for (unsigned i = 0; i < kNumBuffers; ++i) {
145 if (munmap(buffers_[i].start, buffers_[i].length) == -1) {
146 LOG(WARNING, "munmap(%p, %zd) for destruction failed with %d: %s\n",
147 buffers_[i].start, buffers_[i].length, errno, strerror(errno));
148 }
149 }
150 delete[] buffers_;
151
152 if (close(fd_) == -1) {
153 LOG(WARNING, "close(%d) for destruction failed with %d: %s\n",
154 fd_, errno, strerror(errno));
155 }
156}
157
158} // namespace camera
159} // namespace aos
160