1 #include <linux/videodev2.h>
2 #include <cstdio>
3 #include <string.h>
4 #include <poll.h>
5 #include <fcntl.h>
6 #include <unistd.h>
7 #include <fstream>
8 #include <sys/ioctl.h>
9 #include <xf86drm.h>
11 #include "kms++.h"
12 #include "test.h"
13 #include "opts.h"
15 #define CAMERA_BUF_QUEUE_SIZE 3
16 #define MAX_CAMERA 9
18 using namespace std;
19 using namespace kms;
21 enum class BufferProvider {
22 DRM,
23 V4L2,
24 };
26 class Camera
27 {
28 public:
29 Camera(int camera_id, Card& card, Plane* plane, uint32_t x, uint32_t y,
30 uint32_t iw, uint32_t ih, PixelFormat pixfmt,
31 BufferProvider buffer_type);
32 ~Camera();
34 Camera(const Camera& other) = delete;
35 Camera& operator=(const Camera& other) = delete;
37 void show_next_frame(Crtc* crtc);
38 int fd() const { return m_fd; }
39 private:
40 ExtFramebuffer* GetExtFrameBuffer(Card& card, int i, PixelFormat pixfmt);
41 int m_fd; /* camera file descriptor */
42 Plane* m_plane;
43 BufferProvider m_buffer_type;
44 vector<DumbFramebuffer*> m_fb; /* framebuffers for DRM buffers */
45 vector<ExtFramebuffer*> m_extfb; /* framebuffers for V4L2 buffers */
46 int m_prev_fb_index;
47 uint32_t m_in_width, m_in_height; /* camera capture resolution */
48 /* image properties for display */
49 uint32_t m_out_width, m_out_height;
50 uint32_t m_out_x, m_out_y;
51 };
53 static int buffer_export(int v4lfd, enum v4l2_buf_type bt, int index, int *dmafd)
54 {
55 struct v4l2_exportbuffer expbuf;
57 memset(&expbuf, 0, sizeof(expbuf));
58 expbuf.type = bt;
59 expbuf.index = index;
60 if (ioctl(v4lfd, VIDIOC_EXPBUF, &expbuf) == -1) {
61 perror("VIDIOC_EXPBUF");
62 return -1;
63 }
65 *dmafd = expbuf.fd;
67 return 0;
68 }
70 ExtFramebuffer* Camera::GetExtFrameBuffer(Card& card, int i, PixelFormat pixfmt)
71 {
72 int r, dmafd;
74 r = buffer_export(m_fd, V4L2_BUF_TYPE_VIDEO_CAPTURE, i, &dmafd);
75 ASSERT(r == 0);
77 uint32_t handle;
78 r = drmPrimeFDToHandle(card.fd(), dmafd, &handle);
79 ASSERT(r == 0);
81 const PixelFormatInfo& format_info = get_pixel_format_info(pixfmt);
82 ASSERT(format_info.num_planes == 1);
84 uint32_t handles[4] { handle };
85 uint32_t pitches[4] { m_in_width * (format_info.planes[0].bitspp / 8) };
86 uint32_t offsets[4] { };
88 return new ExtFramebuffer(card, m_in_width, m_in_height, pixfmt,
89 handles, pitches, offsets);
90 }
92 bool inline better_size(struct v4l2_frmsize_discrete* v4ldisc,
93 uint32_t iw, uint32_t ih,
94 uint32_t best_w, uint32_t best_h)
95 {
96 if (v4ldisc->width <= iw && v4ldisc->height <= ih &&
97 (v4ldisc->width >= best_w || v4ldisc->height >= best_h))
98 return true;
100 return false;
101 }
103 Camera::Camera(int camera_id, Card& card, Plane* plane, uint32_t x, uint32_t y,
104 uint32_t iw, uint32_t ih, PixelFormat pixfmt,
105 BufferProvider buffer_type)
106 {
107 char dev_name[20];
108 int r, i;
109 uint32_t best_w = 320;
110 uint32_t best_h = 240;
111 uint32_t v4l_mem;
113 m_buffer_type = buffer_type;
114 if (m_buffer_type == BufferProvider::V4L2)
115 v4l_mem = V4L2_MEMORY_MMAP;
116 else
117 v4l_mem = V4L2_MEMORY_DMABUF;
119 sprintf(dev_name, "/dev/video%d", camera_id);
120 m_fd = ::open(dev_name, O_RDWR | O_NONBLOCK);
122 ASSERT(m_fd >= 0);
124 struct v4l2_frmsizeenum v4lfrms = { };
125 v4lfrms.pixel_format = (uint32_t) pixfmt;
126 while (ioctl(m_fd, VIDIOC_ENUM_FRAMESIZES, &v4lfrms) == 0) {
127 if (v4lfrms.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
128 if (better_size(&v4lfrms.discrete, iw, ih,
129 best_w, best_h)) {
130 best_w = v4lfrms.discrete.width;
131 best_h = v4lfrms.discrete.height;
132 }
133 } else {
134 break;
135 }
136 v4lfrms.index++;
137 };
139 m_out_width = m_in_width = best_w;
140 m_out_height = m_in_height = best_h;
141 /* Move it to the middle of the requested area */
142 m_out_x = x + iw / 2 - m_out_width / 2;
143 m_out_y = y + ih / 2 - m_out_height / 2;
145 struct v4l2_format v4lfmt = { };
146 v4lfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
147 r = ioctl(m_fd, VIDIOC_G_FMT, &v4lfmt);
148 ASSERT(r == 0);
150 v4lfmt.fmt.pix.pixelformat = (uint32_t) pixfmt;
151 v4lfmt.fmt.pix.width = m_in_width;
152 v4lfmt.fmt.pix.height = m_in_height;
154 r = ioctl(m_fd, VIDIOC_S_FMT, &v4lfmt);
155 ASSERT(r == 0);
157 struct v4l2_requestbuffers v4lreqbuf = { };
158 v4lreqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
159 v4lreqbuf.memory = v4l_mem;
160 v4lreqbuf.count = CAMERA_BUF_QUEUE_SIZE;
161 r = ioctl(m_fd, VIDIOC_REQBUFS, &v4lreqbuf);
162 ASSERT(r == 0);
163 ASSERT(v4lreqbuf.count == CAMERA_BUF_QUEUE_SIZE);
165 struct v4l2_buffer v4lbuf = { };
166 v4lbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
167 v4lbuf.memory = v4l_mem;
169 for (i = 0; i < CAMERA_BUF_QUEUE_SIZE; i++) {
170 DumbFramebuffer *fb = NULL;
171 ExtFramebuffer *extfb = NULL;
173 if (m_buffer_type == BufferProvider::V4L2)
174 extfb = GetExtFrameBuffer(card, i, pixfmt);
175 else
176 fb = new DumbFramebuffer(card, m_in_width,
177 m_in_height, pixfmt);
179 v4lbuf.index = i;
180 if (m_buffer_type == BufferProvider::DRM)
181 v4lbuf.m.fd = fb->prime_fd(0);
182 r = ioctl(m_fd, VIDIOC_QBUF, &v4lbuf);
183 ASSERT(r == 0);
185 if (m_buffer_type == BufferProvider::V4L2)
186 m_extfb.push_back(extfb);
187 else
188 m_fb.push_back(fb);
189 }
190 m_prev_fb_index = -1;
191 enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
193 r = ioctl(m_fd, VIDIOC_STREAMON, &type);
194 ASSERT(r == 0);
196 m_plane = plane;
197 }
200 Camera::~Camera()
201 {
202 for (unsigned i = 0; i < m_fb.size(); i++)
203 delete m_fb[i];
205 for (unsigned i = 0; i < m_extfb.size(); i++)
206 delete m_extfb[i];
208 ::close(m_fd);
209 }
211 void Camera::show_next_frame(Crtc* crtc)
212 {
213 int r;
214 int fb_index;
215 uint32_t v4l_mem;
217 if (m_buffer_type == BufferProvider::V4L2)
218 v4l_mem = V4L2_MEMORY_MMAP;
219 else
220 v4l_mem = V4L2_MEMORY_DMABUF;
222 struct v4l2_buffer v4l2buf = { };
223 v4l2buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
224 v4l2buf.memory = v4l_mem;
225 r = ioctl(m_fd, VIDIOC_DQBUF, &v4l2buf);
226 if (r != 0) {
227 printf("VIDIOC_DQBUF ioctl failed with %d\n", errno);
228 return;
229 }
231 fb_index = v4l2buf.index;
232 if (m_buffer_type == BufferProvider::V4L2)
233 r = crtc->set_plane(m_plane, *m_extfb[fb_index],
234 m_out_x, m_out_y, m_out_width, m_out_height,
235 0, 0, m_in_width, m_in_height);
236 else
237 r = crtc->set_plane(m_plane, *m_fb[fb_index],
238 m_out_x, m_out_y, m_out_width, m_out_height,
239 0, 0, m_in_width, m_in_height);
241 ASSERT(r == 0);
243 if (m_prev_fb_index >= 0) {
244 memset(&v4l2buf, 0, sizeof(v4l2buf));
245 v4l2buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
246 v4l2buf.memory = v4l_mem;
247 v4l2buf.index = m_prev_fb_index;
248 if (m_buffer_type == BufferProvider::DRM)
249 v4l2buf.m.fd = m_fb[m_prev_fb_index]->prime_fd(0);
250 r = ioctl(m_fd, VIDIOC_QBUF, &v4l2buf);
251 ASSERT(r == 0);
253 }
254 m_prev_fb_index = fb_index;
255 }
258 static bool is_capture_dev(int fd)
259 {
260 struct v4l2_capability cap = { };
261 int r;
263 r = ioctl(fd, VIDIOC_QUERYCAP, &cap);
264 ASSERT(r == 0);
265 if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
266 return false;
268 return true;
269 }
271 static vector<int> count_cameras()
272 {
273 int i, fd;
274 vector<int> camera_idx;
275 char dev_name[20];
277 for (i = 0; i < MAX_CAMERA; i++) {
278 sprintf(dev_name, "/dev/video%d", i);
279 fd = ::open(dev_name, O_RDWR | O_NONBLOCK);
280 if (fd >= 0) {
281 if (is_capture_dev(fd))
282 camera_idx.push_back(i);
283 close(fd);
284 }
285 }
287 return camera_idx;
288 }
290 static const char* usage_str =
291 "Usage: kmscapture [OPTIONS]\n\n"
292 "Options:\n"
293 " -s, --single Single camera mode. Open only /dev/video0\n"
294 " --buffer-type=<drm|v4l> Use DRM or V4L provided buffers. Default: DRM\n"
295 " -h, --help Print this help\n"
296 ;
298 int main(int argc, char** argv)
299 {
300 uint32_t w;
301 BufferProvider buffer_type = BufferProvider::DRM;
302 int i;
304 auto camera_idx = count_cameras();
305 int nr_cameras = camera_idx.size();
307 FAIL_IF(!nr_cameras, "Not a single camera has been found.");
309 OptionSet optionset = {
310 Option("s|single", [&](string s)
311 {
312 nr_cameras = 1;
313 }),
314 Option("|buffer-type=", [&](string s)
315 {
316 if (!s.compare("v4l"))
317 buffer_type = BufferProvider::V4L2;
318 else if (s.compare("drm"))
319 printf("invalid buffer-type: %s\n", s.c_str());
320 }),
321 Option("h|help", [&]()
322 {
323 puts(usage_str);
324 exit(-1);
325 }),
326 };
328 optionset.parse(argc, argv);
330 if (optionset.params().size() > 0) {
331 puts(usage_str);
332 exit(-1);
333 }
335 auto pixfmt = FourCCToPixelFormat("YUYV");
337 Card card;
339 auto conn = card.get_first_connected_connector();
340 auto crtc = conn->get_current_crtc();
341 printf("Display: %dx%d\n", crtc->width(), crtc->height());
342 printf("Buffer provider: %s\n",
343 buffer_type == BufferProvider::V4L2? "V4L" : "DRM");
345 w = crtc->width() / nr_cameras;
346 vector<Camera*> cameras;
347 i = 0;
348 for (Plane* p : crtc->get_possible_planes()) {
349 if (p->plane_type() != PlaneType::Overlay)
350 continue;
352 if (!p->supports_format(pixfmt))
353 continue;
355 auto cam = new Camera(camera_idx[i], card, p, i * w, 0,
356 w, crtc->height(), pixfmt, buffer_type);
357 cameras.push_back(cam);
358 if (++i == nr_cameras)
359 break;
360 }
362 FAIL_IF(i < nr_cameras, "available plane not found");
364 vector<pollfd> fds(nr_cameras + 1);
366 for (i = 0; i < nr_cameras; i++) {
367 fds[i].fd = cameras[i]->fd();
368 fds[i].events = POLLIN;
369 }
370 fds[nr_cameras].fd = 0;
371 fds[nr_cameras].events = POLLIN;
373 while (true) {
374 int r = poll(fds.data(), nr_cameras + 1, -1);
375 ASSERT(r > 0);
377 if (fds[nr_cameras].revents != 0)
378 break;
380 for (i = 0; i < nr_cameras; i++) {
381 if (!fds[i].revents)
382 continue;
383 cameras[i]->show_next_frame(crtc);
384 fds[i].revents = 0;
385 }
386 }
388 for (auto cam : cameras)
389 delete cam;
390 }