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