1*f0687c8aSRaman Tenneti #include <linux/videodev2.h>
2*f0687c8aSRaman Tenneti #include <cstdio>
3*f0687c8aSRaman Tenneti #include <string.h>
4*f0687c8aSRaman Tenneti #include <poll.h>
5*f0687c8aSRaman Tenneti #include <fcntl.h>
6*f0687c8aSRaman Tenneti #include <unistd.h>
7*f0687c8aSRaman Tenneti #include <fstream>
8*f0687c8aSRaman Tenneti #include <sys/ioctl.h>
9*f0687c8aSRaman Tenneti #include <glob.h>
10*f0687c8aSRaman Tenneti
11*f0687c8aSRaman Tenneti #include <kms++/kms++.h>
12*f0687c8aSRaman Tenneti #include <kms++util/kms++util.h>
13*f0687c8aSRaman Tenneti
14*f0687c8aSRaman Tenneti #define CAMERA_BUF_QUEUE_SIZE 3
15*f0687c8aSRaman Tenneti #define MAX_CAMERA 9
16*f0687c8aSRaman Tenneti
17*f0687c8aSRaman Tenneti using namespace std;
18*f0687c8aSRaman Tenneti using namespace kms;
19*f0687c8aSRaman Tenneti
20*f0687c8aSRaman Tenneti enum class BufferProvider {
21*f0687c8aSRaman Tenneti DRM,
22*f0687c8aSRaman Tenneti V4L2,
23*f0687c8aSRaman Tenneti };
24*f0687c8aSRaman Tenneti
25*f0687c8aSRaman Tenneti class CameraPipeline
26*f0687c8aSRaman Tenneti {
27*f0687c8aSRaman Tenneti public:
28*f0687c8aSRaman Tenneti CameraPipeline(int cam_fd, Card& card, Crtc* crtc, Plane* plane, uint32_t x, uint32_t y,
29*f0687c8aSRaman Tenneti uint32_t iw, uint32_t ih, PixelFormat pixfmt,
30*f0687c8aSRaman Tenneti BufferProvider buffer_provider);
31*f0687c8aSRaman Tenneti ~CameraPipeline();
32*f0687c8aSRaman Tenneti
33*f0687c8aSRaman Tenneti CameraPipeline(const CameraPipeline& other) = delete;
34*f0687c8aSRaman Tenneti CameraPipeline& operator=(const CameraPipeline& other) = delete;
35*f0687c8aSRaman Tenneti
36*f0687c8aSRaman Tenneti void show_next_frame(AtomicReq& req);
fd() const37*f0687c8aSRaman Tenneti int fd() const { return m_fd; }
38*f0687c8aSRaman Tenneti void start_streaming();
39*f0687c8aSRaman Tenneti
40*f0687c8aSRaman Tenneti private:
41*f0687c8aSRaman Tenneti DmabufFramebuffer* GetDmabufFrameBuffer(Card& card, uint32_t i, PixelFormat pixfmt);
42*f0687c8aSRaman Tenneti int m_fd; /* camera file descriptor */
43*f0687c8aSRaman Tenneti Crtc* m_crtc;
44*f0687c8aSRaman Tenneti Plane* m_plane;
45*f0687c8aSRaman Tenneti BufferProvider m_buffer_provider;
46*f0687c8aSRaman Tenneti vector<Framebuffer*> m_fb;
47*f0687c8aSRaman Tenneti int m_prev_fb_index;
48*f0687c8aSRaman Tenneti uint32_t m_in_width, m_in_height; /* camera capture resolution */
49*f0687c8aSRaman Tenneti /* image properties for display */
50*f0687c8aSRaman Tenneti uint32_t m_out_width, m_out_height;
51*f0687c8aSRaman Tenneti uint32_t m_out_x, m_out_y;
52*f0687c8aSRaman Tenneti };
53*f0687c8aSRaman Tenneti
buffer_export(int v4lfd,enum v4l2_buf_type bt,uint32_t index,int * dmafd)54*f0687c8aSRaman Tenneti static int buffer_export(int v4lfd, enum v4l2_buf_type bt, uint32_t index, int* dmafd)
55*f0687c8aSRaman Tenneti {
56*f0687c8aSRaman Tenneti struct v4l2_exportbuffer expbuf;
57*f0687c8aSRaman Tenneti
58*f0687c8aSRaman Tenneti memset(&expbuf, 0, sizeof(expbuf));
59*f0687c8aSRaman Tenneti expbuf.type = bt;
60*f0687c8aSRaman Tenneti expbuf.index = index;
61*f0687c8aSRaman Tenneti if (ioctl(v4lfd, VIDIOC_EXPBUF, &expbuf) == -1) {
62*f0687c8aSRaman Tenneti perror("VIDIOC_EXPBUF");
63*f0687c8aSRaman Tenneti return -1;
64*f0687c8aSRaman Tenneti }
65*f0687c8aSRaman Tenneti
66*f0687c8aSRaman Tenneti *dmafd = expbuf.fd;
67*f0687c8aSRaman Tenneti
68*f0687c8aSRaman Tenneti return 0;
69*f0687c8aSRaman Tenneti }
70*f0687c8aSRaman Tenneti
GetDmabufFrameBuffer(Card & card,uint32_t i,PixelFormat pixfmt)71*f0687c8aSRaman Tenneti DmabufFramebuffer* CameraPipeline::GetDmabufFrameBuffer(Card& card, uint32_t i, PixelFormat pixfmt)
72*f0687c8aSRaman Tenneti {
73*f0687c8aSRaman Tenneti int r, dmafd;
74*f0687c8aSRaman Tenneti
75*f0687c8aSRaman Tenneti r = buffer_export(m_fd, V4L2_BUF_TYPE_VIDEO_CAPTURE, i, &dmafd);
76*f0687c8aSRaman Tenneti ASSERT(r == 0);
77*f0687c8aSRaman Tenneti
78*f0687c8aSRaman Tenneti const PixelFormatInfo& format_info = get_pixel_format_info(pixfmt);
79*f0687c8aSRaman Tenneti ASSERT(format_info.num_planes == 1);
80*f0687c8aSRaman Tenneti
81*f0687c8aSRaman Tenneti vector<int> fds{ dmafd };
82*f0687c8aSRaman Tenneti vector<uint32_t> pitches{ m_in_width * (format_info.planes[0].bitspp / 8) };
83*f0687c8aSRaman Tenneti vector<uint32_t> offsets{ 0 };
84*f0687c8aSRaman Tenneti
85*f0687c8aSRaman Tenneti return new DmabufFramebuffer(card, m_in_width, m_in_height, pixfmt,
86*f0687c8aSRaman Tenneti fds, pitches, offsets);
87*f0687c8aSRaman Tenneti }
88*f0687c8aSRaman Tenneti
better_size(struct v4l2_frmsize_discrete * v4ldisc,uint32_t iw,uint32_t ih,uint32_t best_w,uint32_t best_h)89*f0687c8aSRaman Tenneti bool inline better_size(struct v4l2_frmsize_discrete* v4ldisc,
90*f0687c8aSRaman Tenneti uint32_t iw, uint32_t ih,
91*f0687c8aSRaman Tenneti uint32_t best_w, uint32_t best_h)
92*f0687c8aSRaman Tenneti {
93*f0687c8aSRaman Tenneti if (v4ldisc->width <= iw && v4ldisc->height <= ih &&
94*f0687c8aSRaman Tenneti (v4ldisc->width >= best_w || v4ldisc->height >= best_h))
95*f0687c8aSRaman Tenneti return true;
96*f0687c8aSRaman Tenneti
97*f0687c8aSRaman Tenneti return false;
98*f0687c8aSRaman Tenneti }
99*f0687c8aSRaman Tenneti
CameraPipeline(int cam_fd,Card & card,Crtc * crtc,Plane * plane,uint32_t x,uint32_t y,uint32_t iw,uint32_t ih,PixelFormat pixfmt,BufferProvider buffer_provider)100*f0687c8aSRaman Tenneti CameraPipeline::CameraPipeline(int cam_fd, Card& card, Crtc* crtc, Plane* plane, uint32_t x, uint32_t y,
101*f0687c8aSRaman Tenneti uint32_t iw, uint32_t ih, PixelFormat pixfmt,
102*f0687c8aSRaman Tenneti BufferProvider buffer_provider)
103*f0687c8aSRaman Tenneti : m_fd(cam_fd), m_crtc(crtc), m_buffer_provider(buffer_provider), m_prev_fb_index(-1)
104*f0687c8aSRaman Tenneti {
105*f0687c8aSRaman Tenneti int r;
106*f0687c8aSRaman Tenneti uint32_t best_w = 320;
107*f0687c8aSRaman Tenneti uint32_t best_h = 240;
108*f0687c8aSRaman Tenneti
109*f0687c8aSRaman Tenneti struct v4l2_frmsizeenum v4lfrms = {};
110*f0687c8aSRaman Tenneti v4lfrms.pixel_format = (uint32_t)pixfmt;
111*f0687c8aSRaman Tenneti while (ioctl(m_fd, VIDIOC_ENUM_FRAMESIZES, &v4lfrms) == 0) {
112*f0687c8aSRaman Tenneti if (v4lfrms.type != V4L2_FRMSIZE_TYPE_DISCRETE) {
113*f0687c8aSRaman Tenneti v4lfrms.index++;
114*f0687c8aSRaman Tenneti continue;
115*f0687c8aSRaman Tenneti }
116*f0687c8aSRaman Tenneti
117*f0687c8aSRaman Tenneti if (v4lfrms.discrete.width > iw || v4lfrms.discrete.height > ih) {
118*f0687c8aSRaman Tenneti //skip
119*f0687c8aSRaman Tenneti } else if (v4lfrms.discrete.width == iw && v4lfrms.discrete.height == ih) {
120*f0687c8aSRaman Tenneti // Exact match
121*f0687c8aSRaman Tenneti best_w = v4lfrms.discrete.width;
122*f0687c8aSRaman Tenneti best_h = v4lfrms.discrete.height;
123*f0687c8aSRaman Tenneti break;
124*f0687c8aSRaman Tenneti } else if (v4lfrms.discrete.width >= best_w || v4lfrms.discrete.height >= ih) {
125*f0687c8aSRaman Tenneti best_w = v4lfrms.discrete.width;
126*f0687c8aSRaman Tenneti best_h = v4lfrms.discrete.height;
127*f0687c8aSRaman Tenneti }
128*f0687c8aSRaman Tenneti
129*f0687c8aSRaman Tenneti v4lfrms.index++;
130*f0687c8aSRaman Tenneti };
131*f0687c8aSRaman Tenneti
132*f0687c8aSRaman Tenneti m_out_width = m_in_width = best_w;
133*f0687c8aSRaman Tenneti m_out_height = m_in_height = best_h;
134*f0687c8aSRaman Tenneti /* Move it to the middle of the requested area */
135*f0687c8aSRaman Tenneti m_out_x = x + iw / 2 - m_out_width / 2;
136*f0687c8aSRaman Tenneti m_out_y = y + ih / 2 - m_out_height / 2;
137*f0687c8aSRaman Tenneti
138*f0687c8aSRaman Tenneti printf("Capture: %ux%u\n", best_w, best_h);
139*f0687c8aSRaman Tenneti
140*f0687c8aSRaman Tenneti struct v4l2_format v4lfmt = {};
141*f0687c8aSRaman Tenneti v4lfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
142*f0687c8aSRaman Tenneti r = ioctl(m_fd, VIDIOC_G_FMT, &v4lfmt);
143*f0687c8aSRaman Tenneti ASSERT(r == 0);
144*f0687c8aSRaman Tenneti
145*f0687c8aSRaman Tenneti v4lfmt.fmt.pix.pixelformat = (uint32_t)pixfmt;
146*f0687c8aSRaman Tenneti v4lfmt.fmt.pix.width = m_in_width;
147*f0687c8aSRaman Tenneti v4lfmt.fmt.pix.height = m_in_height;
148*f0687c8aSRaman Tenneti
149*f0687c8aSRaman Tenneti r = ioctl(m_fd, VIDIOC_S_FMT, &v4lfmt);
150*f0687c8aSRaman Tenneti ASSERT(r == 0);
151*f0687c8aSRaman Tenneti
152*f0687c8aSRaman Tenneti uint32_t v4l_mem;
153*f0687c8aSRaman Tenneti
154*f0687c8aSRaman Tenneti if (m_buffer_provider == BufferProvider::V4L2)
155*f0687c8aSRaman Tenneti v4l_mem = V4L2_MEMORY_MMAP;
156*f0687c8aSRaman Tenneti else
157*f0687c8aSRaman Tenneti v4l_mem = V4L2_MEMORY_DMABUF;
158*f0687c8aSRaman Tenneti
159*f0687c8aSRaman Tenneti struct v4l2_requestbuffers v4lreqbuf = {};
160*f0687c8aSRaman Tenneti v4lreqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
161*f0687c8aSRaman Tenneti v4lreqbuf.memory = v4l_mem;
162*f0687c8aSRaman Tenneti v4lreqbuf.count = CAMERA_BUF_QUEUE_SIZE;
163*f0687c8aSRaman Tenneti r = ioctl(m_fd, VIDIOC_REQBUFS, &v4lreqbuf);
164*f0687c8aSRaman Tenneti ASSERT(r == 0);
165*f0687c8aSRaman Tenneti ASSERT(v4lreqbuf.count == CAMERA_BUF_QUEUE_SIZE);
166*f0687c8aSRaman Tenneti
167*f0687c8aSRaman Tenneti struct v4l2_buffer v4lbuf = {};
168*f0687c8aSRaman Tenneti v4lbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
169*f0687c8aSRaman Tenneti v4lbuf.memory = v4l_mem;
170*f0687c8aSRaman Tenneti
171*f0687c8aSRaman Tenneti for (unsigned i = 0; i < CAMERA_BUF_QUEUE_SIZE; i++) {
172*f0687c8aSRaman Tenneti Framebuffer* fb;
173*f0687c8aSRaman Tenneti
174*f0687c8aSRaman Tenneti if (m_buffer_provider == BufferProvider::V4L2)
175*f0687c8aSRaman Tenneti fb = GetDmabufFrameBuffer(card, i, pixfmt);
176*f0687c8aSRaman Tenneti else
177*f0687c8aSRaman Tenneti fb = new DumbFramebuffer(card, m_in_width,
178*f0687c8aSRaman Tenneti m_in_height, pixfmt);
179*f0687c8aSRaman Tenneti
180*f0687c8aSRaman Tenneti v4lbuf.index = i;
181*f0687c8aSRaman Tenneti if (m_buffer_provider == BufferProvider::DRM)
182*f0687c8aSRaman Tenneti v4lbuf.m.fd = fb->prime_fd(0);
183*f0687c8aSRaman Tenneti r = ioctl(m_fd, VIDIOC_QBUF, &v4lbuf);
184*f0687c8aSRaman Tenneti ASSERT(r == 0);
185*f0687c8aSRaman Tenneti
186*f0687c8aSRaman Tenneti m_fb.push_back(fb);
187*f0687c8aSRaman Tenneti }
188*f0687c8aSRaman Tenneti
189*f0687c8aSRaman Tenneti m_plane = plane;
190*f0687c8aSRaman Tenneti
191*f0687c8aSRaman Tenneti // Do initial plane setup with first fb, so that we only need to
192*f0687c8aSRaman Tenneti // set the FB when page flipping
193*f0687c8aSRaman Tenneti AtomicReq req(card);
194*f0687c8aSRaman Tenneti
195*f0687c8aSRaman Tenneti Framebuffer* fb = m_fb[0];
196*f0687c8aSRaman Tenneti
197*f0687c8aSRaman Tenneti req.add(m_plane, "CRTC_ID", m_crtc->id());
198*f0687c8aSRaman Tenneti req.add(m_plane, "FB_ID", fb->id());
199*f0687c8aSRaman Tenneti
200*f0687c8aSRaman Tenneti req.add(m_plane, "CRTC_X", m_out_x);
201*f0687c8aSRaman Tenneti req.add(m_plane, "CRTC_Y", m_out_y);
202*f0687c8aSRaman Tenneti req.add(m_plane, "CRTC_W", m_out_width);
203*f0687c8aSRaman Tenneti req.add(m_plane, "CRTC_H", m_out_height);
204*f0687c8aSRaman Tenneti
205*f0687c8aSRaman Tenneti req.add(m_plane, "SRC_X", 0);
206*f0687c8aSRaman Tenneti req.add(m_plane, "SRC_Y", 0);
207*f0687c8aSRaman Tenneti req.add(m_plane, "SRC_W", m_in_width << 16);
208*f0687c8aSRaman Tenneti req.add(m_plane, "SRC_H", m_in_height << 16);
209*f0687c8aSRaman Tenneti
210*f0687c8aSRaman Tenneti r = req.commit_sync();
211*f0687c8aSRaman Tenneti FAIL_IF(r, "initial plane setup failed");
212*f0687c8aSRaman Tenneti }
213*f0687c8aSRaman Tenneti
~CameraPipeline()214*f0687c8aSRaman Tenneti CameraPipeline::~CameraPipeline()
215*f0687c8aSRaman Tenneti {
216*f0687c8aSRaman Tenneti for (unsigned i = 0; i < m_fb.size(); i++)
217*f0687c8aSRaman Tenneti delete m_fb[i];
218*f0687c8aSRaman Tenneti
219*f0687c8aSRaman Tenneti ::close(m_fd);
220*f0687c8aSRaman Tenneti }
221*f0687c8aSRaman Tenneti
start_streaming()222*f0687c8aSRaman Tenneti void CameraPipeline::start_streaming()
223*f0687c8aSRaman Tenneti {
224*f0687c8aSRaman Tenneti enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
225*f0687c8aSRaman Tenneti
226*f0687c8aSRaman Tenneti int r = ioctl(m_fd, VIDIOC_STREAMON, &type);
227*f0687c8aSRaman Tenneti FAIL_IF(r, "Failed to enable camera stream: %d", r);
228*f0687c8aSRaman Tenneti }
229*f0687c8aSRaman Tenneti
show_next_frame(AtomicReq & req)230*f0687c8aSRaman Tenneti void CameraPipeline::show_next_frame(AtomicReq& req)
231*f0687c8aSRaman Tenneti {
232*f0687c8aSRaman Tenneti int r;
233*f0687c8aSRaman Tenneti uint32_t v4l_mem;
234*f0687c8aSRaman Tenneti
235*f0687c8aSRaman Tenneti if (m_buffer_provider == BufferProvider::V4L2)
236*f0687c8aSRaman Tenneti v4l_mem = V4L2_MEMORY_MMAP;
237*f0687c8aSRaman Tenneti else
238*f0687c8aSRaman Tenneti v4l_mem = V4L2_MEMORY_DMABUF;
239*f0687c8aSRaman Tenneti
240*f0687c8aSRaman Tenneti struct v4l2_buffer v4l2buf = {};
241*f0687c8aSRaman Tenneti v4l2buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
242*f0687c8aSRaman Tenneti v4l2buf.memory = v4l_mem;
243*f0687c8aSRaman Tenneti r = ioctl(m_fd, VIDIOC_DQBUF, &v4l2buf);
244*f0687c8aSRaman Tenneti if (r != 0) {
245*f0687c8aSRaman Tenneti printf("VIDIOC_DQBUF ioctl failed with %d\n", errno);
246*f0687c8aSRaman Tenneti return;
247*f0687c8aSRaman Tenneti }
248*f0687c8aSRaman Tenneti
249*f0687c8aSRaman Tenneti unsigned fb_index = v4l2buf.index;
250*f0687c8aSRaman Tenneti
251*f0687c8aSRaman Tenneti Framebuffer* fb = m_fb[fb_index];
252*f0687c8aSRaman Tenneti
253*f0687c8aSRaman Tenneti req.add(m_plane, "FB_ID", fb->id());
254*f0687c8aSRaman Tenneti
255*f0687c8aSRaman Tenneti if (m_prev_fb_index >= 0) {
256*f0687c8aSRaman Tenneti memset(&v4l2buf, 0, sizeof(v4l2buf));
257*f0687c8aSRaman Tenneti v4l2buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
258*f0687c8aSRaman Tenneti v4l2buf.memory = v4l_mem;
259*f0687c8aSRaman Tenneti v4l2buf.index = m_prev_fb_index;
260*f0687c8aSRaman Tenneti if (m_buffer_provider == BufferProvider::DRM)
261*f0687c8aSRaman Tenneti v4l2buf.m.fd = m_fb[m_prev_fb_index]->prime_fd(0);
262*f0687c8aSRaman Tenneti r = ioctl(m_fd, VIDIOC_QBUF, &v4l2buf);
263*f0687c8aSRaman Tenneti ASSERT(r == 0);
264*f0687c8aSRaman Tenneti }
265*f0687c8aSRaman Tenneti
266*f0687c8aSRaman Tenneti m_prev_fb_index = fb_index;
267*f0687c8aSRaman Tenneti }
268*f0687c8aSRaman Tenneti
is_capture_dev(int fd)269*f0687c8aSRaman Tenneti static bool is_capture_dev(int fd)
270*f0687c8aSRaman Tenneti {
271*f0687c8aSRaman Tenneti struct v4l2_capability cap = {};
272*f0687c8aSRaman Tenneti int r = ioctl(fd, VIDIOC_QUERYCAP, &cap);
273*f0687c8aSRaman Tenneti ASSERT(r == 0);
274*f0687c8aSRaman Tenneti return cap.capabilities & V4L2_CAP_VIDEO_CAPTURE;
275*f0687c8aSRaman Tenneti }
276*f0687c8aSRaman Tenneti
glob(const std::string & pat)277*f0687c8aSRaman Tenneti std::vector<std::string> glob(const std::string& pat)
278*f0687c8aSRaman Tenneti {
279*f0687c8aSRaman Tenneti glob_t glob_result;
280*f0687c8aSRaman Tenneti glob(pat.c_str(), 0, NULL, &glob_result);
281*f0687c8aSRaman Tenneti vector<string> ret;
282*f0687c8aSRaman Tenneti for (unsigned i = 0; i < glob_result.gl_pathc; ++i)
283*f0687c8aSRaman Tenneti ret.push_back(string(glob_result.gl_pathv[i]));
284*f0687c8aSRaman Tenneti globfree(&glob_result);
285*f0687c8aSRaman Tenneti return ret;
286*f0687c8aSRaman Tenneti }
287*f0687c8aSRaman Tenneti
288*f0687c8aSRaman Tenneti static const char* usage_str =
289*f0687c8aSRaman Tenneti "Usage: kmscapture [OPTIONS]\n\n"
290*f0687c8aSRaman Tenneti "Options:\n"
291*f0687c8aSRaman Tenneti " -s, --single Single camera mode. Open only /dev/video0\n"
292*f0687c8aSRaman Tenneti " --buffer-type=<drm|v4l> Use DRM or V4L provided buffers. Default: DRM\n"
293*f0687c8aSRaman Tenneti " -h, --help Print this help\n";
294*f0687c8aSRaman Tenneti
main(int argc,char ** argv)295*f0687c8aSRaman Tenneti int main(int argc, char** argv)
296*f0687c8aSRaman Tenneti {
297*f0687c8aSRaman Tenneti BufferProvider buffer_provider = BufferProvider::DRM;
298*f0687c8aSRaman Tenneti bool single_cam = false;
299*f0687c8aSRaman Tenneti
300*f0687c8aSRaman Tenneti OptionSet optionset = {
301*f0687c8aSRaman Tenneti Option("s|single", [&]() {
302*f0687c8aSRaman Tenneti single_cam = true;
303*f0687c8aSRaman Tenneti }),
304*f0687c8aSRaman Tenneti Option("|buffer-type=", [&](string s) {
305*f0687c8aSRaman Tenneti if (s == "v4l")
306*f0687c8aSRaman Tenneti buffer_provider = BufferProvider::V4L2;
307*f0687c8aSRaman Tenneti else if (s == "drm")
308*f0687c8aSRaman Tenneti buffer_provider = BufferProvider::DRM;
309*f0687c8aSRaman Tenneti else
310*f0687c8aSRaman Tenneti FAIL("Invalid buffer provider: %s", s.c_str());
311*f0687c8aSRaman Tenneti }),
312*f0687c8aSRaman Tenneti Option("h|help", [&]() {
313*f0687c8aSRaman Tenneti puts(usage_str);
314*f0687c8aSRaman Tenneti exit(-1);
315*f0687c8aSRaman Tenneti }),
316*f0687c8aSRaman Tenneti };
317*f0687c8aSRaman Tenneti
318*f0687c8aSRaman Tenneti optionset.parse(argc, argv);
319*f0687c8aSRaman Tenneti
320*f0687c8aSRaman Tenneti if (optionset.params().size() > 0) {
321*f0687c8aSRaman Tenneti puts(usage_str);
322*f0687c8aSRaman Tenneti exit(-1);
323*f0687c8aSRaman Tenneti }
324*f0687c8aSRaman Tenneti
325*f0687c8aSRaman Tenneti auto pixfmt = PixelFormat::YUYV;
326*f0687c8aSRaman Tenneti
327*f0687c8aSRaman Tenneti Card card;
328*f0687c8aSRaman Tenneti
329*f0687c8aSRaman Tenneti auto conn = card.get_first_connected_connector();
330*f0687c8aSRaman Tenneti auto crtc = conn->get_current_crtc();
331*f0687c8aSRaman Tenneti printf("Display: %dx%d\n", crtc->width(), crtc->height());
332*f0687c8aSRaman Tenneti printf("Buffer provider: %s\n", buffer_provider == BufferProvider::V4L2 ? "V4L" : "DRM");
333*f0687c8aSRaman Tenneti
334*f0687c8aSRaman Tenneti vector<int> camera_fds;
335*f0687c8aSRaman Tenneti
336*f0687c8aSRaman Tenneti for (string vidpath : glob("/dev/video*")) {
337*f0687c8aSRaman Tenneti int fd = ::open(vidpath.c_str(), O_RDWR | O_NONBLOCK);
338*f0687c8aSRaman Tenneti
339*f0687c8aSRaman Tenneti if (fd < 0)
340*f0687c8aSRaman Tenneti continue;
341*f0687c8aSRaman Tenneti
342*f0687c8aSRaman Tenneti if (!is_capture_dev(fd)) {
343*f0687c8aSRaman Tenneti close(fd);
344*f0687c8aSRaman Tenneti continue;
345*f0687c8aSRaman Tenneti }
346*f0687c8aSRaman Tenneti
347*f0687c8aSRaman Tenneti camera_fds.push_back(fd);
348*f0687c8aSRaman Tenneti printf("Using %s\n", vidpath.c_str());
349*f0687c8aSRaman Tenneti
350*f0687c8aSRaman Tenneti if (single_cam)
351*f0687c8aSRaman Tenneti break;
352*f0687c8aSRaman Tenneti }
353*f0687c8aSRaman Tenneti
354*f0687c8aSRaman Tenneti FAIL_IF(camera_fds.size() == 0, "No cameras found");
355*f0687c8aSRaman Tenneti
356*f0687c8aSRaman Tenneti vector<Plane*> available_planes;
357*f0687c8aSRaman Tenneti for (Plane* p : crtc->get_possible_planes()) {
358*f0687c8aSRaman Tenneti if (p->plane_type() != PlaneType::Overlay)
359*f0687c8aSRaman Tenneti continue;
360*f0687c8aSRaman Tenneti
361*f0687c8aSRaman Tenneti if (!p->supports_format(pixfmt))
362*f0687c8aSRaman Tenneti continue;
363*f0687c8aSRaman Tenneti
364*f0687c8aSRaman Tenneti available_planes.push_back(p);
365*f0687c8aSRaman Tenneti }
366*f0687c8aSRaman Tenneti
367*f0687c8aSRaman Tenneti FAIL_IF(available_planes.size() < camera_fds.size(), "Not enough video planes for cameras");
368*f0687c8aSRaman Tenneti
369*f0687c8aSRaman Tenneti uint32_t plane_w = crtc->width() / camera_fds.size();
370*f0687c8aSRaman Tenneti vector<CameraPipeline*> cameras;
371*f0687c8aSRaman Tenneti
372*f0687c8aSRaman Tenneti for (unsigned i = 0; i < camera_fds.size(); ++i) {
373*f0687c8aSRaman Tenneti int cam_fd = camera_fds[i];
374*f0687c8aSRaman Tenneti Plane* plane = available_planes[i];
375*f0687c8aSRaman Tenneti
376*f0687c8aSRaman Tenneti auto cam = new CameraPipeline(cam_fd, card, crtc, plane, i * plane_w, 0,
377*f0687c8aSRaman Tenneti plane_w, crtc->height(), pixfmt, buffer_provider);
378*f0687c8aSRaman Tenneti cameras.push_back(cam);
379*f0687c8aSRaman Tenneti }
380*f0687c8aSRaman Tenneti
381*f0687c8aSRaman Tenneti unsigned nr_cameras = cameras.size();
382*f0687c8aSRaman Tenneti
383*f0687c8aSRaman Tenneti vector<pollfd> fds(nr_cameras + 1);
384*f0687c8aSRaman Tenneti
385*f0687c8aSRaman Tenneti for (unsigned i = 0; i < nr_cameras; i++) {
386*f0687c8aSRaman Tenneti fds[i].fd = cameras[i]->fd();
387*f0687c8aSRaman Tenneti fds[i].events = POLLIN;
388*f0687c8aSRaman Tenneti }
389*f0687c8aSRaman Tenneti fds[nr_cameras].fd = 0;
390*f0687c8aSRaman Tenneti fds[nr_cameras].events = POLLIN;
391*f0687c8aSRaman Tenneti
392*f0687c8aSRaman Tenneti for (auto cam : cameras)
393*f0687c8aSRaman Tenneti cam->start_streaming();
394*f0687c8aSRaman Tenneti
395*f0687c8aSRaman Tenneti while (true) {
396*f0687c8aSRaman Tenneti int r = poll(fds.data(), nr_cameras + 1, -1);
397*f0687c8aSRaman Tenneti ASSERT(r > 0);
398*f0687c8aSRaman Tenneti
399*f0687c8aSRaman Tenneti if (fds[nr_cameras].revents != 0)
400*f0687c8aSRaman Tenneti break;
401*f0687c8aSRaman Tenneti
402*f0687c8aSRaman Tenneti AtomicReq req(card);
403*f0687c8aSRaman Tenneti
404*f0687c8aSRaman Tenneti for (unsigned i = 0; i < nr_cameras; i++) {
405*f0687c8aSRaman Tenneti if (!fds[i].revents)
406*f0687c8aSRaman Tenneti continue;
407*f0687c8aSRaman Tenneti cameras[i]->show_next_frame(req);
408*f0687c8aSRaman Tenneti fds[i].revents = 0;
409*f0687c8aSRaman Tenneti }
410*f0687c8aSRaman Tenneti
411*f0687c8aSRaman Tenneti r = req.test();
412*f0687c8aSRaman Tenneti FAIL_IF(r, "Atomic commit failed: %d", r);
413*f0687c8aSRaman Tenneti
414*f0687c8aSRaman Tenneti req.commit_sync();
415*f0687c8aSRaman Tenneti }
416*f0687c8aSRaman Tenneti
417*f0687c8aSRaman Tenneti for (auto cam : cameras)
418*f0687c8aSRaman Tenneti delete cam;
419*f0687c8aSRaman Tenneti }
420