modedb_dmt: update table
[android/external-libkmsxx.git] / utils / wbcap.cpp
1 #include <cstdio>
2 #include <poll.h>
3 #include <unistd.h>
4 #include <algorithm>
6 #include <kms++/kms++.h>
7 #include <kms++util/kms++util.h>
8 #include <kms++util/videodevice.h>
10 #define CAMERA_BUF_QUEUE_SIZE 5
12 using namespace std;
13 using namespace kms;
15 static vector<DumbFramebuffer*> s_fbs;
16 static vector<DumbFramebuffer*> s_free_fbs;
17 static vector<DumbFramebuffer*> s_wb_fbs;
18 static vector<DumbFramebuffer*> s_ready_fbs;
20 class WBStreamer
21 {
22 public:
23         WBStreamer(VideoStreamer* streamer, Crtc* crtc, uint32_t width, uint32_t height, PixelFormat pixfmt)
24                 : m_capdev(*streamer)
25         {
26                 m_capdev.set_port(crtc->idx());
27                 m_capdev.set_format(pixfmt, width, height);
28                 m_capdev.set_queue_size(s_fbs.size());
30                 for (auto fb : s_free_fbs) {
31                         m_capdev.queue(fb);
32                         s_wb_fbs.push_back(fb);
33                 }
35                 s_free_fbs.clear();
36         }
38         ~WBStreamer()
39         {
40         }
42         WBStreamer(const WBStreamer& other) = delete;
43         WBStreamer& operator=(const WBStreamer& other) = delete;
45         int fd() const { return m_capdev.fd(); }
47         void start_streaming()
48         {
49                 m_capdev.stream_on();
50         }
52         void stop_streaming()
53         {
54                 m_capdev.stream_off();
55         }
57         void Dequeue()
58         {
59                 auto fb = m_capdev.dequeue();
61                 auto iter = find(s_wb_fbs.begin(), s_wb_fbs.end(), fb);
62                 s_wb_fbs.erase(iter);
64                 s_ready_fbs.insert(s_ready_fbs.begin(), fb);
65         }
67         void Queue()
68         {
69                 if (s_free_fbs.size() == 0)
70                         return;
72                 auto fb = s_free_fbs.back();
73                 s_free_fbs.pop_back();
75                 m_capdev.queue(fb);
77                 s_wb_fbs.insert(s_wb_fbs.begin(), fb);
78         }
80 private:
81         VideoStreamer& m_capdev;
82 };
84 class WBFlipState : private PageFlipHandlerBase
85 {
86 public:
87         WBFlipState(Card& card, Crtc* crtc, Plane* plane)
88                 : m_card(card), m_crtc(crtc), m_plane(plane)
89         {
90         }
92         void setup(uint32_t x, uint32_t y, uint32_t width, uint32_t height)
93         {
94                 auto fb = s_ready_fbs.back();
95                 s_ready_fbs.pop_back();
97                 AtomicReq req(m_card);
99                 req.add(m_plane, "CRTC_ID", m_crtc->id());
100                 req.add(m_plane, "FB_ID", fb->id());
102                 req.add(m_plane, "CRTC_X", x);
103                 req.add(m_plane, "CRTC_Y", y);
104                 req.add(m_plane, "CRTC_W", width);
105                 req.add(m_plane, "CRTC_H", height);
107                 req.add(m_plane, "SRC_X", 0);
108                 req.add(m_plane, "SRC_Y", 0);
109                 req.add(m_plane, "SRC_W", fb->width() << 16);
110                 req.add(m_plane, "SRC_H", fb->height() << 16);
112                 int r = req.commit_sync();
113                 FAIL_IF(r, "initial plane setup failed");
115                 m_current_fb = fb;
116         }
118         void queue_next()
119         {
120                 if (m_queued_fb)
121                         return;
123                 if (s_ready_fbs.size() == 0)
124                         return;
126                 auto fb = s_ready_fbs.back();
127                 s_ready_fbs.pop_back();
129                 AtomicReq req(m_card);
130                 req.add(m_plane, "FB_ID", fb->id());
132                 int r = req.commit(this);
133                 if (r)
134                         EXIT("Flip commit failed: %d\n", r);
136                 m_queued_fb = fb;
137         }
139 private:
140         void handle_page_flip(uint32_t frame, double time)
141         {
142                 if (m_queued_fb) {
143                         if (m_current_fb)
144                                 s_free_fbs.insert(s_free_fbs.begin(), m_current_fb);
146                         m_current_fb = m_queued_fb;
147                         m_queued_fb = nullptr;
148                 }
150                 queue_next();
151         }
153         Card& m_card;
154         Crtc* m_crtc;
155         Plane* m_plane;
157         DumbFramebuffer* m_current_fb = nullptr;
158         DumbFramebuffer* m_queued_fb = nullptr;
159 };
161 class BarFlipState : private PageFlipHandlerBase
163 public:
164         BarFlipState(Card& card, Crtc* crtc)
165                 : m_card(card), m_crtc(crtc)
166         {
167                 m_plane = m_crtc->get_primary_plane();
169                 uint32_t w = m_crtc->mode().hdisplay;
170                 uint32_t h = m_crtc->mode().vdisplay;
172                 for (unsigned i = 0; i < s_num_buffers; ++i)
173                         m_fbs[i] = new DumbFramebuffer(card, w, h, PixelFormat::XRGB8888);
174         }
176         ~BarFlipState()
177         {
178                 for (unsigned i = 0; i < s_num_buffers; ++i)
179                         delete m_fbs[i];
180         }
182         void start_flipping()
183         {
184                 m_frame_num = 0;
185                 queue_next();
186         }
188 private:
189         void handle_page_flip(uint32_t frame, double time)
190         {
191                 m_frame_num++;
192                 queue_next();
193         }
195         static unsigned get_bar_pos(DumbFramebuffer* fb, unsigned frame_num)
196         {
197                 return (frame_num * bar_speed) % (fb->width() - bar_width + 1);
198         }
200         void draw_bar(DumbFramebuffer* fb, unsigned frame_num)
201         {
202                 int old_xpos = frame_num < s_num_buffers ? -1 : get_bar_pos(fb, frame_num - s_num_buffers);
203                 int new_xpos = get_bar_pos(fb, frame_num);
205                 draw_color_bar(*fb, old_xpos, new_xpos, bar_width);
206                 draw_text(*fb, fb->width() / 2, 0, to_string(frame_num), RGB(255, 255, 255));
207         }
209         void queue_next()
210         {
211                 AtomicReq req(m_card);
213                 unsigned cur = m_frame_num % s_num_buffers;
215                 auto fb = m_fbs[cur];
217                 draw_bar(fb, m_frame_num);
219                 req.add(m_plane, {
220                                 { "FB_ID", fb->id() },
221                         });
223                 int r = req.commit(this);
224                 if (r)
225                         EXIT("Flip commit failed: %d\n", r);
226         }
228         static const unsigned s_num_buffers = 3;
230         DumbFramebuffer* m_fbs[s_num_buffers];
232         Card& m_card;
233         Crtc* m_crtc;
234         Plane* m_plane;
236         unsigned m_frame_num;
238         static const unsigned bar_width = 20;
239         static const unsigned bar_speed = 8;
240 };
242 static const char* usage_str =
243                 "Usage: wbcap [OPTIONS]\n\n"
244                 "Options:\n"
245                 "  -s, --src=CONN            Source connector\n"
246                 "  -d, --dst=CONN            Destination connector\n"
247                 "  -f, --format=4CC          Format"
248                 "  -h, --help                Print this help\n"
249                 ;
251 int main(int argc, char** argv)
253         string src_conn_name = "unknown";
254         string dst_conn_name = "hdmi";
255         PixelFormat pixfmt = PixelFormat::XRGB8888;
257         OptionSet optionset = {
258                 Option("s|src=", [&](string s)
259                 {
260                         src_conn_name = s;
261                 }),
262                 Option("d|dst=", [&](string s)
263                 {
264                         dst_conn_name = s;
265                 }),
266                 Option("f|format=", [&](string s)
267                 {
268                         pixfmt = FourCCToPixelFormat(s);
269                 }),
270                 Option("h|help", [&]()
271                 {
272                         puts(usage_str);
273                         exit(-1);
274                 }),
275         };
277         optionset.parse(argc, argv);
279         if (optionset.params().size() > 0) {
280                 puts(usage_str);
281                 exit(-1);
282         }
284         VideoDevice vid("/dev/video11");
286         Card card;
287         ResourceManager resman(card);
289         auto src_conn = resman.reserve_connector(src_conn_name);
290         auto src_crtc = resman.reserve_crtc(src_conn);
292         uint32_t src_width = src_crtc->mode().hdisplay;
293         uint32_t src_height = src_crtc->mode().vdisplay;
295         printf("src %s, crtc %ux%u\n", src_conn->fullname().c_str(), src_width, src_height);
297         auto dst_conn = resman.reserve_connector(dst_conn_name);
298         auto dst_crtc = resman.reserve_crtc(dst_conn);
299         auto dst_plane = resman.reserve_overlay_plane(dst_crtc, pixfmt);
300         FAIL_IF(!dst_plane, "Plane not found");
302         uint32_t dst_width = min((uint32_t)dst_crtc->mode().hdisplay, src_width);
303         uint32_t dst_height = min((uint32_t)dst_crtc->mode().vdisplay, src_height);
305         printf("dst %s, crtc %ux%u, plane %ux%u\n", dst_conn->fullname().c_str(),
306                dst_crtc->mode().hdisplay, dst_crtc->mode().vdisplay,
307                dst_width, dst_height);
309         for (int i = 0; i < CAMERA_BUF_QUEUE_SIZE; ++i) {
310                 auto fb = new DumbFramebuffer(card, src_width, src_height, pixfmt);
311                 s_fbs.push_back(fb);
312                 s_free_fbs.push_back(fb);
313         }
315         // get one fb for initial setup
316         s_ready_fbs.push_back(s_free_fbs.back());
317         s_free_fbs.pop_back();
319         // This draws a moving bar to SRC display
320         BarFlipState barflipper(card, src_crtc);
321         barflipper.start_flipping();
323         // This shows the captures SRC frames on DST display
324         WBFlipState wbflipper(card, dst_crtc, dst_plane);
325         wbflipper.setup(0, 0, dst_width, dst_height);
327         WBStreamer wb(vid.get_capture_streamer(), src_crtc, src_width, src_height, pixfmt);
328         wb.start_streaming();
330         vector<pollfd> fds(3);
332         fds[0].fd = 0;
333         fds[0].events =  POLLIN;
334         fds[1].fd = wb.fd();
335         fds[1].events =  POLLIN;
336         fds[2].fd = card.fd();
337         fds[2].events =  POLLIN;
339         while (true) {
340                 int r = poll(fds.data(), fds.size(), -1);
341                 ASSERT(r > 0);
343                 if (fds[0].revents != 0)
344                         break;
346                 if (fds[1].revents) {
347                         fds[1].revents = 0;
349                         wb.Dequeue();
350                         wbflipper.queue_next();
351                 }
353                 if (fds[2].revents) {
354                         fds[2].revents = 0;
356                         card.call_page_flip_handlers();
357                         wb.Queue();
358                 }
359         }
361         printf("exiting...\n");