1 #include <cstdio>
2 #include <poll.h>
3 #include <unistd.h>
4 #include <algorithm>
5 #include <regex>
6 #include <fstream>
7 #include <map>
8 #include <system_error>
9 #include <fmt/format.h>
10
11 #include <kms++/kms++.h>
12 #include <kms++util/kms++util.h>
13 #include <kms++util/videodevice.h>
14
15 const uint32_t NUM_SRC_BUFS = 2;
16 const uint32_t NUM_DST_BUFS = 2;
17
18 using namespace std;
19 using namespace kms;
20
21 static const char* usage_str =
22 "Usage: wbm2m [OPTIONS]\n\n"
23 "Options:\n"
24 " -f, --format=4CC Output format\n"
25 " -c, --crop=CROP CROP is <x>,<y>-<w>x<h>\n"
26 " -h, --help Print this help\n";
27
28 const int bar_speed = 4;
29 const int bar_width = 10;
30
get_bar_pos(DumbFramebuffer * fb,unsigned frame_num)31 static unsigned get_bar_pos(DumbFramebuffer* fb, unsigned frame_num)
32 {
33 return (frame_num * bar_speed) % (fb->width() - bar_width + 1);
34 }
35
read_frame(DumbFramebuffer * fb,unsigned frame_num)36 static void read_frame(DumbFramebuffer* fb, unsigned frame_num)
37 {
38 static map<DumbFramebuffer*, int> s_bar_pos_map;
39
40 int old_pos = -1;
41 if (s_bar_pos_map.find(fb) != s_bar_pos_map.end())
42 old_pos = s_bar_pos_map[fb];
43
44 int pos = get_bar_pos(fb, frame_num);
45 draw_color_bar(*fb, old_pos, pos, bar_width);
46 draw_text(*fb, fb->width() / 2, 0, to_string(frame_num), RGB(255, 255, 255));
47 s_bar_pos_map[fb] = pos;
48 }
49
parse_crop(const string & crop_str,uint32_t & c_left,uint32_t & c_top,uint32_t & c_width,uint32_t & c_height)50 static void parse_crop(const string& crop_str, uint32_t& c_left, uint32_t& c_top,
51 uint32_t& c_width, uint32_t& c_height)
52 {
53 const regex crop_re("(\\d+),(\\d+)-(\\d+)x(\\d+)"); // 400,400-400x400
54
55 smatch sm;
56 if (!regex_match(crop_str, sm, crop_re))
57 EXIT("Failed to parse crop option '%s'", crop_str.c_str());
58
59 c_left = stoul(sm[1]);
60 c_top = stoul(sm[2]);
61 c_width = stoul(sm[3]);
62 c_height = stoul(sm[4]);
63 }
64
main(int argc,char ** argv)65 int main(int argc, char** argv)
66 {
67 // XXX get from args
68 const uint32_t src_width = 800;
69 const uint32_t src_height = 480;
70 const auto src_fmt = PixelFormat::XRGB8888;
71 const uint32_t num_src_frames = 10;
72
73 const uint32_t dst_width = 800;
74 const uint32_t dst_height = 480;
75 uint32_t c_top, c_left, c_width, c_height;
76
77 auto dst_fmt = PixelFormat::XRGB8888;
78 bool use_selection = false;
79
80 OptionSet optionset = {
81 Option("f|format=", [&](string s) {
82 dst_fmt = FourCCToPixelFormat(s);
83 }),
84 Option("c|crop=", [&](string s) {
85 parse_crop(s, c_left, c_top, c_width, c_height);
86 use_selection = true;
87 }),
88 Option("h|help", [&]() {
89 puts(usage_str);
90 exit(-1);
91 }),
92 };
93
94 optionset.parse(argc, argv);
95
96 if (optionset.params().size() > 0) {
97 puts(usage_str);
98 exit(-1);
99 }
100
101 printf("%ux%u-%s -> %ux%u-%s\n", src_width, src_height, PixelFormatToFourCC(src_fmt).c_str(),
102 dst_width, dst_height, PixelFormatToFourCC(dst_fmt).c_str());
103
104 const string filename = fmt::format("wb-out-{}x{}-{}.raw", dst_width, dst_height,
105 PixelFormatToFourCC(dst_fmt));
106
107 printf("writing to %s\n", filename.c_str());
108
109 VideoDevice vid("/dev/video10");
110
111 Card card;
112
113 uint32_t src_frame_num = 0;
114 uint32_t dst_frame_num = 0;
115
116 VideoStreamer* out = vid.get_output_streamer();
117 VideoStreamer* in = vid.get_capture_streamer();
118
119 out->set_format(src_fmt, src_width, src_height);
120 in->set_format(dst_fmt, dst_width, dst_height);
121
122 if (use_selection) {
123 out->set_selection(c_left, c_top, c_width, c_height);
124 printf("crop -> %u,%u-%ux%u\n", c_left, c_top, c_width, c_height);
125 }
126
127 out->set_queue_size(NUM_SRC_BUFS);
128 in->set_queue_size(NUM_DST_BUFS);
129
130 for (unsigned i = 0; i < min(NUM_SRC_BUFS, num_src_frames); ++i) {
131 auto fb = new DumbFramebuffer(card, src_width, src_height, src_fmt);
132
133 read_frame(fb, src_frame_num++);
134
135 out->queue(fb);
136 }
137
138 for (unsigned i = 0; i < min(NUM_DST_BUFS, num_src_frames); ++i) {
139 auto fb = new DumbFramebuffer(card, dst_width, dst_height, dst_fmt);
140 in->queue(fb);
141 }
142
143 vector<pollfd> fds(3);
144
145 fds[0].fd = 0;
146 fds[0].events = POLLIN;
147 fds[1].fd = vid.fd();
148 fds[1].events = POLLIN;
149 fds[2].fd = card.fd();
150 fds[2].events = POLLIN;
151
152 ofstream os(filename, ofstream::binary);
153
154 out->stream_on();
155 in->stream_on();
156
157 while (true) {
158 int r = poll(fds.data(), fds.size(), -1);
159 ASSERT(r > 0);
160
161 if (fds[0].revents != 0)
162 break;
163
164 if (fds[1].revents) {
165 fds[1].revents = 0;
166
167 try {
168 DumbFramebuffer* dst_fb = in->dequeue();
169 printf("Writing frame %u\n", dst_frame_num);
170 for (unsigned i = 0; i < dst_fb->num_planes(); ++i)
171 os.write((char*)dst_fb->map(i), dst_fb->size(i));
172 in->queue(dst_fb);
173
174 dst_frame_num++;
175
176 if (dst_frame_num >= num_src_frames)
177 break;
178
179 } catch (system_error& se) {
180 if (se.code() != errc::resource_unavailable_try_again)
181 FAIL("dequeue failed: %s", se.what());
182
183 break;
184 }
185
186 DumbFramebuffer* src_fb = out->dequeue();
187
188 if (src_frame_num < num_src_frames) {
189 read_frame(src_fb, src_frame_num++);
190 out->queue(src_fb);
191 }
192 }
193
194 if (fds[2].revents) {
195 fds[2].revents = 0;
196 }
197 }
198
199 printf("exiting...\n");
200 }
201