kmscapture: cleanups
[android/external-libkmsxx.git] / tests / kmscapture.cpp
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>
10 #include <glob.h>
12 #include "kms++.h"
13 #include "test.h"
14 #include "opts.h"
16 #define CAMERA_BUF_QUEUE_SIZE   3
17 #define MAX_CAMERA              9
19 using namespace std;
20 using namespace kms;
22 enum class BufferProvider {
23         DRM,
24         V4L2,
25 };
27 class CameraPipeline
28 {
29 public:
30         CameraPipeline(int cam_fd, Card& card, Plane* plane, uint32_t x, uint32_t y,
31                        uint32_t iw, uint32_t ih, PixelFormat pixfmt,
32                        BufferProvider buffer_provider);
33         ~CameraPipeline();
35         CameraPipeline(const CameraPipeline& other) = delete;
36         CameraPipeline& operator=(const CameraPipeline& other) = delete;
38         void show_next_frame(Crtc* crtc);
39         int fd() const { return m_fd; }
40 private:
41         ExtFramebuffer* GetExtFrameBuffer(Card& card, uint32_t i, PixelFormat pixfmt);
42         int m_fd;       /* camera file descriptor */
43         Plane* m_plane;
44         BufferProvider m_buffer_provider;
45         vector<DumbFramebuffer*> m_fb; /* framebuffers for DRM buffers */
46         vector<ExtFramebuffer*> m_extfb; /* framebuffers for V4L2 buffers */
47         int m_prev_fb_index;
48         uint32_t m_in_width, m_in_height; /* camera capture resolution */
49         /* image properties for display */
50         uint32_t m_out_width, m_out_height;
51         uint32_t m_out_x, m_out_y;
52 };
54 static int buffer_export(int v4lfd, enum v4l2_buf_type bt, uint32_t index, int *dmafd)
55 {
56         struct v4l2_exportbuffer expbuf;
58         memset(&expbuf, 0, sizeof(expbuf));
59         expbuf.type = bt;
60         expbuf.index = index;
61         if (ioctl(v4lfd, VIDIOC_EXPBUF, &expbuf) == -1) {
62                 perror("VIDIOC_EXPBUF");
63                 return -1;
64         }
66         *dmafd = expbuf.fd;
68         return 0;
69 }
71 ExtFramebuffer* CameraPipeline::GetExtFrameBuffer(Card& card, uint32_t i, PixelFormat pixfmt)
72 {
73         int r, dmafd;
75         r = buffer_export(m_fd, V4L2_BUF_TYPE_VIDEO_CAPTURE, i, &dmafd);
76         ASSERT(r == 0);
78         uint32_t handle;
79         r = drmPrimeFDToHandle(card.fd(), dmafd, &handle);
80         ASSERT(r == 0);
82         const PixelFormatInfo& format_info = get_pixel_format_info(pixfmt);
83         ASSERT(format_info.num_planes == 1);
85         uint32_t handles[4] { handle };
86         uint32_t pitches[4] { m_in_width * (format_info.planes[0].bitspp / 8) };
87         uint32_t offsets[4] { };
89         return new ExtFramebuffer(card, m_in_width, m_in_height, pixfmt,
90                                   handles, pitches, offsets);
91 }
93 bool inline better_size(struct v4l2_frmsize_discrete* v4ldisc,
94                         uint32_t iw, uint32_t ih,
95                         uint32_t best_w, uint32_t best_h)
96 {
97         if (v4ldisc->width <= iw && v4ldisc->height <= ih &&
98             (v4ldisc->width >= best_w || v4ldisc->height >= best_h))
99                 return true;
101         return false;
104 CameraPipeline::CameraPipeline(int cam_fd, Card& card, Plane* plane, uint32_t x, uint32_t y,
105                                uint32_t iw, uint32_t ih, PixelFormat pixfmt,
106                                BufferProvider buffer_provider)
107         : m_fd(cam_fd), m_buffer_provider(buffer_provider)
109         int r;
110         uint32_t best_w = 320;
111         uint32_t best_h = 240;
113         struct v4l2_frmsizeenum v4lfrms = { };
114         v4lfrms.pixel_format = (uint32_t)pixfmt;
115         while (ioctl(m_fd, VIDIOC_ENUM_FRAMESIZES, &v4lfrms) == 0) {
116                 if (v4lfrms.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
117                         if (better_size(&v4lfrms.discrete, iw, ih,
118                                         best_w, best_h)) {
119                                 best_w = v4lfrms.discrete.width;
120                                 best_h = v4lfrms.discrete.height;
121                         }
122                 } else {
123                         break;
124                 }
125                 v4lfrms.index++;
126         };
128         m_out_width = m_in_width = best_w;
129         m_out_height = m_in_height = best_h;
130         /* Move it to the middle of the requested area */
131         m_out_x = x + iw / 2 - m_out_width / 2;
132         m_out_y = y + ih / 2 - m_out_height / 2;
134         struct v4l2_format v4lfmt = { };
135         v4lfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
136         r = ioctl(m_fd, VIDIOC_G_FMT, &v4lfmt);
137         ASSERT(r == 0);
139         v4lfmt.fmt.pix.pixelformat = (uint32_t)pixfmt;
140         v4lfmt.fmt.pix.width = m_in_width;
141         v4lfmt.fmt.pix.height = m_in_height;
143         r = ioctl(m_fd, VIDIOC_S_FMT, &v4lfmt);
144         ASSERT(r == 0);
146         uint32_t v4l_mem;
148         if (m_buffer_provider == BufferProvider::V4L2)
149                 v4l_mem = V4L2_MEMORY_MMAP;
150         else
151                 v4l_mem = V4L2_MEMORY_DMABUF;
153         struct v4l2_requestbuffers v4lreqbuf = { };
154         v4lreqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
155         v4lreqbuf.memory = v4l_mem;
156         v4lreqbuf.count = CAMERA_BUF_QUEUE_SIZE;
157         r = ioctl(m_fd, VIDIOC_REQBUFS, &v4lreqbuf);
158         ASSERT(r == 0);
159         ASSERT(v4lreqbuf.count == CAMERA_BUF_QUEUE_SIZE);
161         struct v4l2_buffer v4lbuf = { };
162         v4lbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
163         v4lbuf.memory = v4l_mem;
165         for (unsigned i = 0; i < CAMERA_BUF_QUEUE_SIZE; i++) {
166                 DumbFramebuffer *fb = NULL;
167                 ExtFramebuffer *extfb = NULL;
169                 if (m_buffer_provider == BufferProvider::V4L2)
170                         extfb = GetExtFrameBuffer(card, i, pixfmt);
171                 else
172                         fb = new DumbFramebuffer(card, m_in_width,
173                                                  m_in_height, pixfmt);
175                 v4lbuf.index = i;
176                 if (m_buffer_provider == BufferProvider::DRM)
177                         v4lbuf.m.fd = fb->prime_fd(0);
178                 r = ioctl(m_fd, VIDIOC_QBUF, &v4lbuf);
179                 ASSERT(r == 0);
181                 if (m_buffer_provider == BufferProvider::V4L2)
182                         m_extfb.push_back(extfb);
183                 else
184                         m_fb.push_back(fb);
185         }
186         m_prev_fb_index = -1;
187         enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
189         r = ioctl(m_fd, VIDIOC_STREAMON, &type);
190         ASSERT(r == 0);
192         m_plane = plane;
196 CameraPipeline::~CameraPipeline()
198         for (unsigned i = 0; i < m_fb.size(); i++)
199                 delete m_fb[i];
201         for (unsigned i = 0; i < m_extfb.size(); i++)
202                 delete m_extfb[i];
204         ::close(m_fd);
207 void CameraPipeline::show_next_frame(Crtc* crtc)
209         int r;
210         uint32_t v4l_mem;
212         if (m_buffer_provider == BufferProvider::V4L2)
213                 v4l_mem = V4L2_MEMORY_MMAP;
214         else
215                 v4l_mem = V4L2_MEMORY_DMABUF;
217         struct v4l2_buffer v4l2buf = { };
218         v4l2buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
219         v4l2buf.memory = v4l_mem;
220         r = ioctl(m_fd, VIDIOC_DQBUF, &v4l2buf);
221         if (r != 0) {
222                 printf("VIDIOC_DQBUF ioctl failed with %d\n", errno);
223                 return;
224         }
226         unsigned fb_index = v4l2buf.index;
227         if (m_buffer_provider == BufferProvider::V4L2)
228                 r = crtc->set_plane(m_plane, *m_extfb[fb_index],
229                                     m_out_x, m_out_y, m_out_width, m_out_height,
230                                     0, 0, m_in_width, m_in_height);
231         else
232                 r = crtc->set_plane(m_plane, *m_fb[fb_index],
233                                     m_out_x, m_out_y, m_out_width, m_out_height,
234                                     0, 0, m_in_width, m_in_height);
236         ASSERT(r == 0);
238         if (m_prev_fb_index >= 0) {
239                 memset(&v4l2buf, 0, sizeof(v4l2buf));
240                 v4l2buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
241                 v4l2buf.memory = v4l_mem;
242                 v4l2buf.index = m_prev_fb_index;
243                 if (m_buffer_provider == BufferProvider::DRM)
244                         v4l2buf.m.fd = m_fb[m_prev_fb_index]->prime_fd(0);
245                 r = ioctl(m_fd, VIDIOC_QBUF, &v4l2buf);
246                 ASSERT(r == 0);
248         }
249         m_prev_fb_index = fb_index;
252 static bool is_capture_dev(int fd)
254         struct v4l2_capability cap = { };
255         int r = ioctl(fd, VIDIOC_QUERYCAP, &cap);
256         ASSERT(r == 0);
257         return cap.capabilities & V4L2_CAP_VIDEO_CAPTURE;
260 std::vector<std::string> glob(const std::string& pat)
262         glob_t glob_result;
263         glob(pat.c_str(), 0, NULL, &glob_result);
264         vector<string> ret;
265         for(unsigned i = 0; i < glob_result.gl_pathc; ++i)
266                 ret.push_back(string(glob_result.gl_pathv[i]));
267         globfree(&glob_result);
268         return ret;
271 static const char* usage_str =
272                 "Usage: kmscapture [OPTIONS]\n\n"
273                 "Options:\n"
274                 "  -s, --single                Single camera mode. Open only /dev/video0\n"
275                 "      --buffer-type=<drm|v4l> Use DRM or V4L provided buffers. Default: DRM\n"
276                 "  -h, --help                  Print this help\n"
277                 ;
279 int main(int argc, char** argv)
281         BufferProvider buffer_provider = BufferProvider::DRM;
282         bool single_cam = false;
284         OptionSet optionset = {
285                 Option("s|single", [&]()
286                 {
287                         single_cam = true;
288                 }),
289                 Option("|buffer-type=", [&](string s)
290                 {
291                         if (s == "v4l")
292                                 buffer_provider = BufferProvider::V4L2;
293                         else if (s == "drm")
294                                 buffer_provider = BufferProvider::DRM;
295                         else
296                                 FAIL("Invalid buffer provider: %s", s.c_str());
297                 }),
298                 Option("h|help", [&]()
299                 {
300                         puts(usage_str);
301                         exit(-1);
302                 }),
303         };
305         optionset.parse(argc, argv);
307         if (optionset.params().size() > 0) {
308                 puts(usage_str);
309                 exit(-1);
310         }
312         auto pixfmt = PixelFormat::YUYV;
314         Card card;
316         auto conn = card.get_first_connected_connector();
317         auto crtc = conn->get_current_crtc();
318         printf("Display: %dx%d\n", crtc->width(), crtc->height());
319         printf("Buffer provider: %s\n", buffer_provider == BufferProvider::V4L2? "V4L" : "DRM");
321         vector<int> camera_fds;
323         for (string vidpath : glob("/dev/video*")) {
324                 int fd = ::open(vidpath.c_str(), O_RDWR | O_NONBLOCK);
326                 if (fd < 0)
327                         continue;
329                 if (!is_capture_dev(fd)) {
330                         close(fd);
331                         continue;
332                 }
334                 camera_fds.push_back(fd);
336                 if (single_cam)
337                         break;
338         }
340         FAIL_IF(camera_fds.size() == 0, "No cameras found");
342         vector<Plane*> available_planes;
343         for (Plane* p : crtc->get_possible_planes()) {
344                 if (p->plane_type() != PlaneType::Overlay)
345                         continue;
347                 if (!p->supports_format(pixfmt))
348                         continue;
350                 available_planes.push_back(p);
351         }
353         FAIL_IF(available_planes.size() < camera_fds.size(), "Not enough video planes for cameras");
355         uint32_t plane_w = crtc->width() / camera_fds.size();
356         vector<CameraPipeline*> cameras;
358         for (unsigned i = 0; i < camera_fds.size(); ++i) {
359                 int cam_fd = camera_fds[i];
360                 Plane* plane = available_planes[i];
362                 auto cam = new CameraPipeline(cam_fd, card, plane, i * plane_w, 0,
363                                               plane_w, crtc->height(), pixfmt, buffer_provider);
364                 cameras.push_back(cam);
365         }
367         unsigned nr_cameras = cameras.size();
369         vector<pollfd> fds(nr_cameras + 1);
371         for (unsigned i = 0; i < nr_cameras; i++) {
372                 fds[i].fd = cameras[i]->fd();
373                 fds[i].events =  POLLIN;
374         }
375         fds[nr_cameras].fd = 0;
376         fds[nr_cameras].events =  POLLIN;
378         while (true) {
379                 int r = poll(fds.data(), nr_cameras + 1, -1);
380                 ASSERT(r > 0);
382                 if (fds[nr_cameras].revents != 0)
383                         break;
385                 for (unsigned i = 0; i < nr_cameras; i++) {
386                         if (!fds[i].revents)
387                                 continue;
388                         cameras[i]->show_next_frame(crtc);
389                         fds[i].revents = 0;
390                 }
391         }
393         for (auto cam : cameras)
394                 delete cam;