38a8cc045c5e510753b8c75e8587962e6615f6bc
[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, PixelFormat pixfmt)
24                 : m_capdev(*streamer)
25         {
26                 Videomode m = crtc->mode();
28                 m_capdev.set_port(crtc->idx());
29                 m_capdev.set_format(pixfmt, m.hdisplay, m.vdisplay / (m.interlace() ? 2 : 1));
30                 m_capdev.set_queue_size(s_fbs.size());
32                 for (auto fb : s_free_fbs) {
33                         m_capdev.queue(fb);
34                         s_wb_fbs.push_back(fb);
35                 }
37                 s_free_fbs.clear();
38         }
40         ~WBStreamer()
41         {
42         }
44         WBStreamer(const WBStreamer& other) = delete;
45         WBStreamer& operator=(const WBStreamer& other) = delete;
47         int fd() const { return m_capdev.fd(); }
49         void start_streaming()
50         {
51                 m_capdev.stream_on();
52         }
54         void stop_streaming()
55         {
56                 m_capdev.stream_off();
57         }
59         void Dequeue()
60         {
61                 auto fb = m_capdev.dequeue();
63                 auto iter = find(s_wb_fbs.begin(), s_wb_fbs.end(), fb);
64                 s_wb_fbs.erase(iter);
66                 s_ready_fbs.insert(s_ready_fbs.begin(), fb);
67         }
69         void Queue()
70         {
71                 if (s_free_fbs.size() == 0)
72                         return;
74                 auto fb = s_free_fbs.back();
75                 s_free_fbs.pop_back();
77                 m_capdev.queue(fb);
79                 s_wb_fbs.insert(s_wb_fbs.begin(), fb);
80         }
82 private:
83         VideoStreamer& m_capdev;
84 };
86 class WBFlipState : private PageFlipHandlerBase
87 {
88 public:
89         WBFlipState(Card& card, Crtc* crtc, Plane* plane)
90                 : m_card(card), m_crtc(crtc), m_plane(plane)
91         {
92                 auto fb = s_ready_fbs.back();
93                 s_ready_fbs.pop_back();
95                 AtomicReq req(m_card);
97                 req.add(m_plane, "CRTC_ID", m_crtc->id());
98                 req.add(m_plane, "FB_ID", fb->id());
100                 req.add(m_plane, "CRTC_X", 0);
101                 req.add(m_plane, "CRTC_Y", 0);
102                 req.add(m_plane, "CRTC_W", min((uint32_t)m_crtc->mode().hdisplay, fb->width()));
103                 req.add(m_plane, "CRTC_H", min((uint32_t)m_crtc->mode().vdisplay, fb->height()));
105                 req.add(m_plane, "SRC_X", 0);
106                 req.add(m_plane, "SRC_Y", 0);
107                 req.add(m_plane, "SRC_W", fb->width() << 16);
108                 req.add(m_plane, "SRC_H", fb->height() << 16);
110                 int r = req.commit_sync();
111                 FAIL_IF(r, "initial plane setup failed");
113                 m_current_fb = fb;
114         }
116         void queue_next()
117         {
118                 if (m_queued_fb)
119                         return;
121                 if (s_ready_fbs.size() == 0)
122                         return;
124                 auto fb = s_ready_fbs.back();
125                 s_ready_fbs.pop_back();
127                 AtomicReq req(m_card);
128                 req.add(m_plane, "FB_ID", fb->id());
130                 int r = req.commit(this);
131                 if (r)
132                         EXIT("Flip commit failed: %d\n", r);
134                 m_queued_fb = fb;
135         }
137 private:
138         void handle_page_flip(uint32_t frame, double time)
139         {
140                 if (m_queued_fb) {
141                         if (m_current_fb)
142                                 s_free_fbs.insert(s_free_fbs.begin(), m_current_fb);
144                         m_current_fb = m_queued_fb;
145                         m_queued_fb = nullptr;
146                 }
148                 queue_next();
149         }
151         Card& m_card;
152         Crtc* m_crtc;
153         Plane* m_plane;
155         DumbFramebuffer* m_current_fb = nullptr;
156         DumbFramebuffer* m_queued_fb = nullptr;
157 };
159 class BarFlipState : private PageFlipHandlerBase
161 public:
162         BarFlipState(Card& card, Crtc* crtc, Plane* plane, uint32_t width, uint32_t height)
163                 : m_card(card), m_crtc(crtc), m_plane(plane)
164         {
165                 for (unsigned i = 0; i < s_num_buffers; ++i)
166                         m_fbs[i] = new DumbFramebuffer(card, width, height, PixelFormat::XRGB8888);
167         }
169         ~BarFlipState()
170         {
171                 for (unsigned i = 0; i < s_num_buffers; ++i)
172                         delete m_fbs[i];
173         }
175         void start_flipping()
176         {
177                 m_frame_num = 0;
178                 queue_next();
179         }
181 private:
182         void handle_page_flip(uint32_t frame, double time)
183         {
184                 m_frame_num++;
185                 queue_next();
186         }
188         static unsigned get_bar_pos(DumbFramebuffer* fb, unsigned frame_num)
189         {
190                 return (frame_num * bar_speed) % (fb->width() - bar_width + 1);
191         }
193         void draw_bar(DumbFramebuffer* fb, unsigned frame_num)
194         {
195                 int old_xpos = frame_num < s_num_buffers ? -1 : get_bar_pos(fb, frame_num - s_num_buffers);
196                 int new_xpos = get_bar_pos(fb, frame_num);
198                 draw_color_bar(*fb, old_xpos, new_xpos, bar_width);
199                 draw_text(*fb, fb->width() / 2, 0, to_string(frame_num), RGB(255, 255, 255));
200         }
202         void queue_next()
203         {
204                 AtomicReq req(m_card);
206                 unsigned cur = m_frame_num % s_num_buffers;
208                 auto fb = m_fbs[cur];
210                 draw_bar(fb, m_frame_num);
212                 req.add(m_plane, {
213                                 { "CRTC_ID", m_crtc->id() },
214                                 { "FB_ID", fb->id() },
216                                 { "CRTC_X", 0 },
217                                 { "CRTC_Y", 0 },
218                                 { "CRTC_W", min((uint32_t)m_crtc->mode().hdisplay, fb->width()) },
219                                 { "CRTC_H", min((uint32_t)m_crtc->mode().vdisplay, fb->height()) },
221                                 { "SRC_X", 0 },
222                                 { "SRC_Y", 0 },
223                                 { "SRC_W", fb->width() << 16 },
224                                 { "SRC_H", fb->height() << 16 },
225                         });
227                 int r = req.commit(this);
228                 if (r)
229                         EXIT("Flip commit failed: %d\n", r);
230         }
232         static const unsigned s_num_buffers = 3;
234         DumbFramebuffer* m_fbs[s_num_buffers];
236         Card& m_card;
237         Crtc* m_crtc;
238         Plane* m_plane;
240         unsigned m_frame_num;
242         static const unsigned bar_width = 20;
243         static const unsigned bar_speed = 8;
244 };
246 static const char* usage_str =
247                 "Usage: wbcap [OPTIONS]\n\n"
248                 "Options:\n"
249                 "  -s, --src=CONN            Source connector\n"
250                 "  -d, --dst=CONN            Destination connector\n"
251                 "  -f, --format=4CC          Format"
252                 "  -h, --help                Print this help\n"
253                 ;
255 int main(int argc, char** argv)
257         string src_conn_name;
258         string src_mode_name;
259         string dst_conn_name;
260         string dst_mode_name;
261         PixelFormat pixfmt = PixelFormat::XRGB8888;
263         OptionSet optionset = {
264                 Option("s|src=", [&](string s)
265                 {
266                         src_conn_name = s;
267                 }),
268                 Option("m|smode=", [&](string s)
269                 {
270                         src_mode_name = s;
271                 }),
272                 Option("d|dst=", [&](string s)
273                 {
274                         dst_conn_name = s;
275                 }),
276                 Option("M|dmode=", [&](string s)
277                 {
278                         dst_mode_name = s;
279                 }),
280                 Option("f|format=", [&](string s)
281                 {
282                         pixfmt = FourCCToPixelFormat(s);
283                 }),
284                 Option("h|help", [&]()
285                 {
286                         puts(usage_str);
287                         exit(-1);
288                 }),
289         };
291         optionset.parse(argc, argv);
293         if (optionset.params().size() > 0) {
294                 puts(usage_str);
295                 exit(-1);
296         }
298         if (src_conn_name.empty())
299                 EXIT("No source connector defined");
301         if (dst_conn_name.empty())
302                 EXIT("No destination connector defined");
304         VideoDevice vid("/dev/video11");
306         Card card;
307         ResourceManager resman(card);
309         card.disable_all();
311         auto src_conn = resman.reserve_connector(src_conn_name);
312         auto src_crtc = resman.reserve_crtc(src_conn);
313         auto src_plane = resman.reserve_generic_plane(src_crtc, pixfmt);
314         FAIL_IF(!src_plane, "Plane not found");
315         Videomode src_mode = src_mode_name.empty() ? src_conn->get_default_mode() : src_conn->get_mode(src_mode_name);
316         src_crtc->set_mode(src_conn, src_mode);
319         auto dst_conn = resman.reserve_connector(dst_conn_name);
320         auto dst_crtc = resman.reserve_crtc(dst_conn);
321         auto dst_plane = resman.reserve_overlay_plane(dst_crtc, pixfmt);
322         FAIL_IF(!dst_plane, "Plane not found");
323         Videomode dst_mode = dst_mode_name.empty() ? dst_conn->get_default_mode() : dst_conn->get_mode(dst_mode_name);
324         dst_crtc->set_mode(dst_conn, dst_mode);
326         uint32_t src_width = src_mode.hdisplay;
327         uint32_t src_height = src_mode.vdisplay;
329         uint32_t dst_width = src_mode.hdisplay;
330         uint32_t dst_height = src_mode.vdisplay;
331         if (src_mode.interlace())
332                 dst_height /= 2;
334         printf("src %s, crtc %s\n", src_conn->fullname().c_str(), src_mode.to_string().c_str());
336         printf("dst %s, crtc %s\n", dst_conn->fullname().c_str(), dst_mode.to_string().c_str());
338         printf("src_fb %ux%u, dst_fb %ux%u\n", src_width, src_height, dst_width, dst_height);
340         for (int i = 0; i < CAMERA_BUF_QUEUE_SIZE; ++i) {
341                 auto fb = new DumbFramebuffer(card, dst_width, dst_height, pixfmt);
342                 s_fbs.push_back(fb);
343                 s_free_fbs.push_back(fb);
344         }
346         // get one fb for initial setup
347         s_ready_fbs.push_back(s_free_fbs.back());
348         s_free_fbs.pop_back();
350         // This draws a moving bar to SRC display
351         BarFlipState barflipper(card, src_crtc, src_plane, src_width, src_height);
352         barflipper.start_flipping();
354         // This shows the captured SRC frames on DST display
355         WBFlipState wbflipper(card, dst_crtc, dst_plane);
357         WBStreamer wb(vid.get_capture_streamer(), src_crtc, pixfmt);
358         wb.start_streaming();
360         vector<pollfd> fds(3);
362         fds[0].fd = 0;
363         fds[0].events =  POLLIN;
364         fds[1].fd = wb.fd();
365         fds[1].events =  POLLIN;
366         fds[2].fd = card.fd();
367         fds[2].events =  POLLIN;
369         while (true) {
370                 int r = poll(fds.data(), fds.size(), -1);
371                 ASSERT(r > 0);
373                 if (fds[0].revents != 0)
374                         break;
376                 if (fds[1].revents) {
377                         fds[1].revents = 0;
379                         wb.Dequeue();
380                         wbflipper.queue_next();
381                 }
383                 if (fds[2].revents) {
384                         fds[2].revents = 0;
386                         card.call_page_flip_handlers();
387                         wb.Queue();
388                 }
389         }
391         printf("exiting...\n");