xref: /aosp_15_r20/external/libkmsxx/kms++util/src/videodevice.cpp (revision f0687c8a10b3e371dbe09214db6664e37c283cca)
1*f0687c8aSRaman Tenneti #include <string>
2*f0687c8aSRaman Tenneti 
3*f0687c8aSRaman Tenneti #include <sys/types.h>
4*f0687c8aSRaman Tenneti #include <sys/stat.h>
5*f0687c8aSRaman Tenneti #include <fcntl.h>
6*f0687c8aSRaman Tenneti #include <linux/videodev2.h>
7*f0687c8aSRaman Tenneti #include <sys/ioctl.h>
8*f0687c8aSRaman Tenneti #include <unistd.h>
9*f0687c8aSRaman Tenneti #include <system_error>
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 using namespace std;
16*f0687c8aSRaman Tenneti using namespace kms;
17*f0687c8aSRaman Tenneti 
18*f0687c8aSRaman Tenneti /*
19*f0687c8aSRaman Tenneti  * V4L2 and DRM differ in their interpretation of YUV420::NV12
20*f0687c8aSRaman Tenneti  *
21*f0687c8aSRaman Tenneti  * V4L2 NV12 is a Y and UV co-located planes in a single plane buffer.
22*f0687c8aSRaman Tenneti  * DRM NV12 is a Y and UV planes presented as dual plane buffer,
23*f0687c8aSRaman Tenneti  * which is known as NM12 in V4L2.
24*f0687c8aSRaman Tenneti  *
25*f0687c8aSRaman Tenneti  * Since here we have hybrid DRM/V4L2 user space helper functions
26*f0687c8aSRaman Tenneti  * we need to translate DRM::NV12 to V4L2:NM12 pixel format back
27*f0687c8aSRaman Tenneti  * and forth to keep the data view consistent.
28*f0687c8aSRaman Tenneti  */
29*f0687c8aSRaman Tenneti 
30*f0687c8aSRaman Tenneti /* V4L2 helper funcs */
v4l2_get_formats(int fd,uint32_t buf_type)31*f0687c8aSRaman Tenneti static vector<PixelFormat> v4l2_get_formats(int fd, uint32_t buf_type)
32*f0687c8aSRaman Tenneti {
33*f0687c8aSRaman Tenneti 	vector<PixelFormat> v;
34*f0687c8aSRaman Tenneti 
35*f0687c8aSRaman Tenneti 	v4l2_fmtdesc desc{};
36*f0687c8aSRaman Tenneti 	desc.type = buf_type;
37*f0687c8aSRaman Tenneti 
38*f0687c8aSRaman Tenneti 	while (ioctl(fd, VIDIOC_ENUM_FMT, &desc) == 0) {
39*f0687c8aSRaman Tenneti 		if (desc.pixelformat == V4L2_PIX_FMT_NV12M)
40*f0687c8aSRaman Tenneti 			v.push_back(PixelFormat::NV12);
41*f0687c8aSRaman Tenneti 		else if (desc.pixelformat != V4L2_PIX_FMT_NV12)
42*f0687c8aSRaman Tenneti 			v.push_back((PixelFormat)desc.pixelformat);
43*f0687c8aSRaman Tenneti 
44*f0687c8aSRaman Tenneti 		desc.index++;
45*f0687c8aSRaman Tenneti 	}
46*f0687c8aSRaman Tenneti 
47*f0687c8aSRaman Tenneti 	return v;
48*f0687c8aSRaman Tenneti }
49*f0687c8aSRaman Tenneti 
v4l2_set_format(int fd,PixelFormat fmt,uint32_t width,uint32_t height,uint32_t buf_type)50*f0687c8aSRaman Tenneti static void v4l2_set_format(int fd, PixelFormat fmt, uint32_t width, uint32_t height, uint32_t buf_type)
51*f0687c8aSRaman Tenneti {
52*f0687c8aSRaman Tenneti 	int r;
53*f0687c8aSRaman Tenneti 
54*f0687c8aSRaman Tenneti 	v4l2_format v4lfmt{};
55*f0687c8aSRaman Tenneti 
56*f0687c8aSRaman Tenneti 	v4lfmt.type = buf_type;
57*f0687c8aSRaman Tenneti 	r = ioctl(fd, VIDIOC_G_FMT, &v4lfmt);
58*f0687c8aSRaman Tenneti 	ASSERT(r == 0);
59*f0687c8aSRaman Tenneti 
60*f0687c8aSRaman Tenneti 	const PixelFormatInfo& pfi = get_pixel_format_info(fmt);
61*f0687c8aSRaman Tenneti 
62*f0687c8aSRaman Tenneti 	bool mplane = buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE || buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
63*f0687c8aSRaman Tenneti 
64*f0687c8aSRaman Tenneti 	if (mplane) {
65*f0687c8aSRaman Tenneti 		v4l2_pix_format_mplane& mp = v4lfmt.fmt.pix_mp;
66*f0687c8aSRaman Tenneti 		uint32_t used_fmt;
67*f0687c8aSRaman Tenneti 
68*f0687c8aSRaman Tenneti 		if (fmt == PixelFormat::NV12)
69*f0687c8aSRaman Tenneti 			used_fmt = V4L2_PIX_FMT_NV12M;
70*f0687c8aSRaman Tenneti 		else
71*f0687c8aSRaman Tenneti 			used_fmt = (uint32_t)fmt;
72*f0687c8aSRaman Tenneti 
73*f0687c8aSRaman Tenneti 		mp.pixelformat = used_fmt;
74*f0687c8aSRaman Tenneti 		mp.width = width;
75*f0687c8aSRaman Tenneti 		mp.height = height;
76*f0687c8aSRaman Tenneti 
77*f0687c8aSRaman Tenneti 		mp.num_planes = pfi.num_planes;
78*f0687c8aSRaman Tenneti 
79*f0687c8aSRaman Tenneti 		for (unsigned i = 0; i < pfi.num_planes; ++i) {
80*f0687c8aSRaman Tenneti 			const PixelFormatPlaneInfo& pfpi = pfi.planes[i];
81*f0687c8aSRaman Tenneti 			v4l2_plane_pix_format& p = mp.plane_fmt[i];
82*f0687c8aSRaman Tenneti 
83*f0687c8aSRaman Tenneti 			p.bytesperline = width * pfpi.bitspp / 8;
84*f0687c8aSRaman Tenneti 			p.sizeimage = p.bytesperline * height / pfpi.ysub;
85*f0687c8aSRaman Tenneti 		}
86*f0687c8aSRaman Tenneti 
87*f0687c8aSRaman Tenneti 		r = ioctl(fd, VIDIOC_S_FMT, &v4lfmt);
88*f0687c8aSRaman Tenneti 		ASSERT(r == 0);
89*f0687c8aSRaman Tenneti 
90*f0687c8aSRaman Tenneti 		ASSERT(mp.pixelformat == used_fmt);
91*f0687c8aSRaman Tenneti 		ASSERT(mp.width == width);
92*f0687c8aSRaman Tenneti 		ASSERT(mp.height == height);
93*f0687c8aSRaman Tenneti 
94*f0687c8aSRaman Tenneti 		ASSERT(mp.num_planes == pfi.num_planes);
95*f0687c8aSRaman Tenneti 
96*f0687c8aSRaman Tenneti 		for (unsigned i = 0; i < pfi.num_planes; ++i) {
97*f0687c8aSRaman Tenneti 			const PixelFormatPlaneInfo& pfpi = pfi.planes[i];
98*f0687c8aSRaman Tenneti 			v4l2_plane_pix_format& p = mp.plane_fmt[i];
99*f0687c8aSRaman Tenneti 
100*f0687c8aSRaman Tenneti 			ASSERT(p.bytesperline == width * pfpi.bitspp / 8);
101*f0687c8aSRaman Tenneti 			ASSERT(p.sizeimage == p.bytesperline * height / pfpi.ysub);
102*f0687c8aSRaman Tenneti 		}
103*f0687c8aSRaman Tenneti 	} else {
104*f0687c8aSRaman Tenneti 		ASSERT(pfi.num_planes == 1);
105*f0687c8aSRaman Tenneti 
106*f0687c8aSRaman Tenneti 		v4lfmt.fmt.pix.pixelformat = (uint32_t)fmt;
107*f0687c8aSRaman Tenneti 		v4lfmt.fmt.pix.width = width;
108*f0687c8aSRaman Tenneti 		v4lfmt.fmt.pix.height = height;
109*f0687c8aSRaman Tenneti 		v4lfmt.fmt.pix.bytesperline = width * pfi.planes[0].bitspp / 8;
110*f0687c8aSRaman Tenneti 
111*f0687c8aSRaman Tenneti 		r = ioctl(fd, VIDIOC_S_FMT, &v4lfmt);
112*f0687c8aSRaman Tenneti 		ASSERT(r == 0);
113*f0687c8aSRaman Tenneti 
114*f0687c8aSRaman Tenneti 		ASSERT(v4lfmt.fmt.pix.pixelformat == (uint32_t)fmt);
115*f0687c8aSRaman Tenneti 		ASSERT(v4lfmt.fmt.pix.width == width);
116*f0687c8aSRaman Tenneti 		ASSERT(v4lfmt.fmt.pix.height == height);
117*f0687c8aSRaman Tenneti 		ASSERT(v4lfmt.fmt.pix.bytesperline == width * pfi.planes[0].bitspp / 8);
118*f0687c8aSRaman Tenneti 	}
119*f0687c8aSRaman Tenneti }
120*f0687c8aSRaman Tenneti 
v4l2_get_selection(int fd,uint32_t & left,uint32_t & top,uint32_t & width,uint32_t & height,uint32_t buf_type)121*f0687c8aSRaman Tenneti static void v4l2_get_selection(int fd, uint32_t& left, uint32_t& top, uint32_t& width, uint32_t& height, uint32_t buf_type)
122*f0687c8aSRaman Tenneti {
123*f0687c8aSRaman Tenneti 	int r;
124*f0687c8aSRaman Tenneti 	struct v4l2_selection selection;
125*f0687c8aSRaman Tenneti 
126*f0687c8aSRaman Tenneti 	if (buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT ||
127*f0687c8aSRaman Tenneti 	    buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
128*f0687c8aSRaman Tenneti 		selection.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
129*f0687c8aSRaman Tenneti 		selection.target = V4L2_SEL_TGT_CROP;
130*f0687c8aSRaman Tenneti 	} else if (buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE ||
131*f0687c8aSRaman Tenneti 		   buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
132*f0687c8aSRaman Tenneti 		selection.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
133*f0687c8aSRaman Tenneti 		selection.target = V4L2_SEL_TGT_COMPOSE;
134*f0687c8aSRaman Tenneti 	} else {
135*f0687c8aSRaman Tenneti 		FAIL("buf_type (%d) is not valid\n", buf_type);
136*f0687c8aSRaman Tenneti 	}
137*f0687c8aSRaman Tenneti 
138*f0687c8aSRaman Tenneti 	r = ioctl(fd, VIDIOC_G_SELECTION, &selection);
139*f0687c8aSRaman Tenneti 	ASSERT(r == 0);
140*f0687c8aSRaman Tenneti 
141*f0687c8aSRaman Tenneti 	left = selection.r.left;
142*f0687c8aSRaman Tenneti 	top = selection.r.top;
143*f0687c8aSRaman Tenneti 	width = selection.r.width;
144*f0687c8aSRaman Tenneti 	height = selection.r.height;
145*f0687c8aSRaman Tenneti }
146*f0687c8aSRaman Tenneti 
v4l2_set_selection(int fd,uint32_t & left,uint32_t & top,uint32_t & width,uint32_t & height,uint32_t buf_type)147*f0687c8aSRaman Tenneti static void v4l2_set_selection(int fd, uint32_t& left, uint32_t& top, uint32_t& width, uint32_t& height, uint32_t buf_type)
148*f0687c8aSRaman Tenneti {
149*f0687c8aSRaman Tenneti 	int r;
150*f0687c8aSRaman Tenneti 	struct v4l2_selection selection;
151*f0687c8aSRaman Tenneti 
152*f0687c8aSRaman Tenneti 	if (buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT ||
153*f0687c8aSRaman Tenneti 	    buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
154*f0687c8aSRaman Tenneti 		selection.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
155*f0687c8aSRaman Tenneti 		selection.target = V4L2_SEL_TGT_CROP;
156*f0687c8aSRaman Tenneti 	} else if (buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE ||
157*f0687c8aSRaman Tenneti 		   buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
158*f0687c8aSRaman Tenneti 		selection.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
159*f0687c8aSRaman Tenneti 		selection.target = V4L2_SEL_TGT_COMPOSE;
160*f0687c8aSRaman Tenneti 	} else {
161*f0687c8aSRaman Tenneti 		FAIL("buf_type (%d) is not valid\n", buf_type);
162*f0687c8aSRaman Tenneti 	}
163*f0687c8aSRaman Tenneti 
164*f0687c8aSRaman Tenneti 	selection.r.left = left;
165*f0687c8aSRaman Tenneti 	selection.r.top = top;
166*f0687c8aSRaman Tenneti 	selection.r.width = width;
167*f0687c8aSRaman Tenneti 	selection.r.height = height;
168*f0687c8aSRaman Tenneti 
169*f0687c8aSRaman Tenneti 	r = ioctl(fd, VIDIOC_S_SELECTION, &selection);
170*f0687c8aSRaman Tenneti 	ASSERT(r == 0);
171*f0687c8aSRaman Tenneti 
172*f0687c8aSRaman Tenneti 	left = selection.r.left;
173*f0687c8aSRaman Tenneti 	top = selection.r.top;
174*f0687c8aSRaman Tenneti 	width = selection.r.width;
175*f0687c8aSRaman Tenneti 	height = selection.r.height;
176*f0687c8aSRaman Tenneti }
177*f0687c8aSRaman Tenneti 
v4l2_request_bufs(int fd,uint32_t queue_size,uint32_t buf_type)178*f0687c8aSRaman Tenneti static void v4l2_request_bufs(int fd, uint32_t queue_size, uint32_t buf_type)
179*f0687c8aSRaman Tenneti {
180*f0687c8aSRaman Tenneti 	v4l2_requestbuffers v4lreqbuf{};
181*f0687c8aSRaman Tenneti 	v4lreqbuf.type = buf_type;
182*f0687c8aSRaman Tenneti 	v4lreqbuf.memory = V4L2_MEMORY_DMABUF;
183*f0687c8aSRaman Tenneti 	v4lreqbuf.count = queue_size;
184*f0687c8aSRaman Tenneti 	int r = ioctl(fd, VIDIOC_REQBUFS, &v4lreqbuf);
185*f0687c8aSRaman Tenneti 	ASSERT(r == 0);
186*f0687c8aSRaman Tenneti 	ASSERT(v4lreqbuf.count == queue_size);
187*f0687c8aSRaman Tenneti }
188*f0687c8aSRaman Tenneti 
v4l2_queue_dmabuf(int fd,uint32_t index,DumbFramebuffer * fb,uint32_t buf_type)189*f0687c8aSRaman Tenneti static void v4l2_queue_dmabuf(int fd, uint32_t index, DumbFramebuffer* fb, uint32_t buf_type)
190*f0687c8aSRaman Tenneti {
191*f0687c8aSRaman Tenneti 	v4l2_buffer buf{};
192*f0687c8aSRaman Tenneti 	buf.type = buf_type;
193*f0687c8aSRaman Tenneti 	buf.memory = V4L2_MEMORY_DMABUF;
194*f0687c8aSRaman Tenneti 	buf.index = index;
195*f0687c8aSRaman Tenneti 
196*f0687c8aSRaman Tenneti 	const PixelFormatInfo& pfi = get_pixel_format_info(fb->format());
197*f0687c8aSRaman Tenneti 
198*f0687c8aSRaman Tenneti 	bool mplane = buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE || buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
199*f0687c8aSRaman Tenneti 
200*f0687c8aSRaman Tenneti 	if (mplane) {
201*f0687c8aSRaman Tenneti 		buf.length = pfi.num_planes;
202*f0687c8aSRaman Tenneti 
203*f0687c8aSRaman Tenneti 		v4l2_plane planes[4]{};
204*f0687c8aSRaman Tenneti 		buf.m.planes = planes;
205*f0687c8aSRaman Tenneti 
206*f0687c8aSRaman Tenneti 		for (unsigned i = 0; i < pfi.num_planes; ++i) {
207*f0687c8aSRaman Tenneti 			planes[i].m.fd = fb->prime_fd(i);
208*f0687c8aSRaman Tenneti 			planes[i].bytesused = fb->size(i);
209*f0687c8aSRaman Tenneti 			planes[i].length = fb->size(i);
210*f0687c8aSRaman Tenneti 		}
211*f0687c8aSRaman Tenneti 
212*f0687c8aSRaman Tenneti 		int r = ioctl(fd, VIDIOC_QBUF, &buf);
213*f0687c8aSRaman Tenneti 		ASSERT(r == 0);
214*f0687c8aSRaman Tenneti 	} else {
215*f0687c8aSRaman Tenneti 		buf.m.fd = fb->prime_fd(0);
216*f0687c8aSRaman Tenneti 
217*f0687c8aSRaman Tenneti 		int r = ioctl(fd, VIDIOC_QBUF, &buf);
218*f0687c8aSRaman Tenneti 		ASSERT(r == 0);
219*f0687c8aSRaman Tenneti 	}
220*f0687c8aSRaman Tenneti }
221*f0687c8aSRaman Tenneti 
v4l2_dequeue(int fd,uint32_t buf_type)222*f0687c8aSRaman Tenneti static uint32_t v4l2_dequeue(int fd, uint32_t buf_type)
223*f0687c8aSRaman Tenneti {
224*f0687c8aSRaman Tenneti 	v4l2_buffer buf{};
225*f0687c8aSRaman Tenneti 	buf.type = buf_type;
226*f0687c8aSRaman Tenneti 	buf.memory = V4L2_MEMORY_DMABUF;
227*f0687c8aSRaman Tenneti 
228*f0687c8aSRaman Tenneti 	// V4L2 crashes if planes are not set
229*f0687c8aSRaman Tenneti 	v4l2_plane planes[4]{};
230*f0687c8aSRaman Tenneti 	buf.m.planes = planes;
231*f0687c8aSRaman Tenneti 	buf.length = 4;
232*f0687c8aSRaman Tenneti 
233*f0687c8aSRaman Tenneti 	int r = ioctl(fd, VIDIOC_DQBUF, &buf);
234*f0687c8aSRaman Tenneti 	if (r)
235*f0687c8aSRaman Tenneti 		throw system_error(errno, generic_category());
236*f0687c8aSRaman Tenneti 
237*f0687c8aSRaman Tenneti 	return buf.index;
238*f0687c8aSRaman Tenneti }
239*f0687c8aSRaman Tenneti 
VideoDevice(const string & dev)240*f0687c8aSRaman Tenneti VideoDevice::VideoDevice(const string& dev)
241*f0687c8aSRaman Tenneti 	: VideoDevice(::open(dev.c_str(), O_RDWR | O_NONBLOCK))
242*f0687c8aSRaman Tenneti {
243*f0687c8aSRaman Tenneti }
244*f0687c8aSRaman Tenneti 
VideoDevice(int fd)245*f0687c8aSRaman Tenneti VideoDevice::VideoDevice(int fd)
246*f0687c8aSRaman Tenneti 	: m_fd(fd), m_has_capture(false), m_has_output(false), m_has_m2m(false)
247*f0687c8aSRaman Tenneti {
248*f0687c8aSRaman Tenneti 	if (fd < 0)
249*f0687c8aSRaman Tenneti 		throw runtime_error("bad fd");
250*f0687c8aSRaman Tenneti 
251*f0687c8aSRaman Tenneti 	struct v4l2_capability cap = {};
252*f0687c8aSRaman Tenneti 	int r = ioctl(fd, VIDIOC_QUERYCAP, &cap);
253*f0687c8aSRaman Tenneti 	ASSERT(r == 0);
254*f0687c8aSRaman Tenneti 
255*f0687c8aSRaman Tenneti 	if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE) {
256*f0687c8aSRaman Tenneti 		m_has_capture = true;
257*f0687c8aSRaman Tenneti 		m_has_mplane_capture = true;
258*f0687c8aSRaman Tenneti 	} else if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) {
259*f0687c8aSRaman Tenneti 		m_has_capture = true;
260*f0687c8aSRaman Tenneti 		m_has_mplane_capture = false;
261*f0687c8aSRaman Tenneti 	}
262*f0687c8aSRaman Tenneti 
263*f0687c8aSRaman Tenneti 	if (cap.capabilities & V4L2_CAP_VIDEO_OUTPUT_MPLANE) {
264*f0687c8aSRaman Tenneti 		m_has_output = true;
265*f0687c8aSRaman Tenneti 		m_has_mplane_output = true;
266*f0687c8aSRaman Tenneti 	} else if (cap.capabilities & V4L2_CAP_VIDEO_OUTPUT) {
267*f0687c8aSRaman Tenneti 		m_has_output = true;
268*f0687c8aSRaman Tenneti 		m_has_mplane_output = false;
269*f0687c8aSRaman Tenneti 	}
270*f0687c8aSRaman Tenneti 
271*f0687c8aSRaman Tenneti 	if (cap.capabilities & V4L2_CAP_VIDEO_M2M_MPLANE) {
272*f0687c8aSRaman Tenneti 		m_has_m2m = true;
273*f0687c8aSRaman Tenneti 		m_has_capture = true;
274*f0687c8aSRaman Tenneti 		m_has_output = true;
275*f0687c8aSRaman Tenneti 		m_has_mplane_m2m = true;
276*f0687c8aSRaman Tenneti 		m_has_mplane_capture = true;
277*f0687c8aSRaman Tenneti 		m_has_mplane_output = true;
278*f0687c8aSRaman Tenneti 	} else if (cap.capabilities & V4L2_CAP_VIDEO_M2M) {
279*f0687c8aSRaman Tenneti 		m_has_m2m = true;
280*f0687c8aSRaman Tenneti 		m_has_capture = true;
281*f0687c8aSRaman Tenneti 		m_has_output = true;
282*f0687c8aSRaman Tenneti 		m_has_mplane_m2m = false;
283*f0687c8aSRaman Tenneti 		m_has_mplane_capture = false;
284*f0687c8aSRaman Tenneti 		m_has_mplane_output = false;
285*f0687c8aSRaman Tenneti 	}
286*f0687c8aSRaman Tenneti }
287*f0687c8aSRaman Tenneti 
~VideoDevice()288*f0687c8aSRaman Tenneti VideoDevice::~VideoDevice()
289*f0687c8aSRaman Tenneti {
290*f0687c8aSRaman Tenneti 	::close(m_fd);
291*f0687c8aSRaman Tenneti }
292*f0687c8aSRaman Tenneti 
get_capture_streamer()293*f0687c8aSRaman Tenneti VideoStreamer* VideoDevice::get_capture_streamer()
294*f0687c8aSRaman Tenneti {
295*f0687c8aSRaman Tenneti 	ASSERT(m_has_capture);
296*f0687c8aSRaman Tenneti 
297*f0687c8aSRaman Tenneti 	if (!m_capture_streamer) {
298*f0687c8aSRaman Tenneti 		auto type = m_has_mplane_capture ? VideoStreamer::StreamerType::CaptureMulti : VideoStreamer::StreamerType::CaptureSingle;
299*f0687c8aSRaman Tenneti 		m_capture_streamer = std::unique_ptr<VideoStreamer>(new VideoStreamer(m_fd, type));
300*f0687c8aSRaman Tenneti 	}
301*f0687c8aSRaman Tenneti 
302*f0687c8aSRaman Tenneti 	return m_capture_streamer.get();
303*f0687c8aSRaman Tenneti }
304*f0687c8aSRaman Tenneti 
get_output_streamer()305*f0687c8aSRaman Tenneti VideoStreamer* VideoDevice::get_output_streamer()
306*f0687c8aSRaman Tenneti {
307*f0687c8aSRaman Tenneti 	ASSERT(m_has_output);
308*f0687c8aSRaman Tenneti 
309*f0687c8aSRaman Tenneti 	if (!m_output_streamer) {
310*f0687c8aSRaman Tenneti 		auto type = m_has_mplane_output ? VideoStreamer::StreamerType::OutputMulti : VideoStreamer::StreamerType::OutputSingle;
311*f0687c8aSRaman Tenneti 		m_output_streamer = std::unique_ptr<VideoStreamer>(new VideoStreamer(m_fd, type));
312*f0687c8aSRaman Tenneti 	}
313*f0687c8aSRaman Tenneti 
314*f0687c8aSRaman Tenneti 	return m_output_streamer.get();
315*f0687c8aSRaman Tenneti }
316*f0687c8aSRaman Tenneti 
get_discrete_frame_sizes(PixelFormat fmt)317*f0687c8aSRaman Tenneti vector<tuple<uint32_t, uint32_t>> VideoDevice::get_discrete_frame_sizes(PixelFormat fmt)
318*f0687c8aSRaman Tenneti {
319*f0687c8aSRaman Tenneti 	vector<tuple<uint32_t, uint32_t>> v;
320*f0687c8aSRaman Tenneti 
321*f0687c8aSRaman Tenneti 	v4l2_frmsizeenum v4lfrms{};
322*f0687c8aSRaman Tenneti 	v4lfrms.pixel_format = (uint32_t)fmt;
323*f0687c8aSRaman Tenneti 
324*f0687c8aSRaman Tenneti 	int r = ioctl(m_fd, VIDIOC_ENUM_FRAMESIZES, &v4lfrms);
325*f0687c8aSRaman Tenneti 	ASSERT(r);
326*f0687c8aSRaman Tenneti 
327*f0687c8aSRaman Tenneti 	FAIL_IF(v4lfrms.type != V4L2_FRMSIZE_TYPE_DISCRETE, "No discrete frame sizes");
328*f0687c8aSRaman Tenneti 
329*f0687c8aSRaman Tenneti 	while (ioctl(m_fd, VIDIOC_ENUM_FRAMESIZES, &v4lfrms) == 0) {
330*f0687c8aSRaman Tenneti 		v.emplace_back(v4lfrms.discrete.width, v4lfrms.discrete.height);
331*f0687c8aSRaman Tenneti 		v4lfrms.index++;
332*f0687c8aSRaman Tenneti 	};
333*f0687c8aSRaman Tenneti 
334*f0687c8aSRaman Tenneti 	return v;
335*f0687c8aSRaman Tenneti }
336*f0687c8aSRaman Tenneti 
get_frame_sizes(PixelFormat fmt)337*f0687c8aSRaman Tenneti VideoDevice::VideoFrameSize VideoDevice::get_frame_sizes(PixelFormat fmt)
338*f0687c8aSRaman Tenneti {
339*f0687c8aSRaman Tenneti 	v4l2_frmsizeenum v4lfrms{};
340*f0687c8aSRaman Tenneti 	v4lfrms.pixel_format = (uint32_t)fmt;
341*f0687c8aSRaman Tenneti 
342*f0687c8aSRaman Tenneti 	int r = ioctl(m_fd, VIDIOC_ENUM_FRAMESIZES, &v4lfrms);
343*f0687c8aSRaman Tenneti 	ASSERT(r);
344*f0687c8aSRaman Tenneti 
345*f0687c8aSRaman Tenneti 	FAIL_IF(v4lfrms.type == V4L2_FRMSIZE_TYPE_DISCRETE, "No continuous frame sizes");
346*f0687c8aSRaman Tenneti 
347*f0687c8aSRaman Tenneti 	VideoFrameSize s;
348*f0687c8aSRaman Tenneti 
349*f0687c8aSRaman Tenneti 	s.min_w = v4lfrms.stepwise.min_width;
350*f0687c8aSRaman Tenneti 	s.max_w = v4lfrms.stepwise.max_width;
351*f0687c8aSRaman Tenneti 	s.step_w = v4lfrms.stepwise.step_width;
352*f0687c8aSRaman Tenneti 
353*f0687c8aSRaman Tenneti 	s.min_h = v4lfrms.stepwise.min_height;
354*f0687c8aSRaman Tenneti 	s.max_h = v4lfrms.stepwise.max_height;
355*f0687c8aSRaman Tenneti 	s.step_h = v4lfrms.stepwise.step_height;
356*f0687c8aSRaman Tenneti 
357*f0687c8aSRaman Tenneti 	return s;
358*f0687c8aSRaman Tenneti }
359*f0687c8aSRaman Tenneti 
get_capture_devices()360*f0687c8aSRaman Tenneti vector<string> VideoDevice::get_capture_devices()
361*f0687c8aSRaman Tenneti {
362*f0687c8aSRaman Tenneti 	vector<string> v;
363*f0687c8aSRaman Tenneti 
364*f0687c8aSRaman Tenneti 	for (int i = 0; i < 20; ++i) {
365*f0687c8aSRaman Tenneti 		string name = "/dev/video" + to_string(i);
366*f0687c8aSRaman Tenneti 
367*f0687c8aSRaman Tenneti 		struct stat buffer;
368*f0687c8aSRaman Tenneti 		if (stat(name.c_str(), &buffer) != 0)
369*f0687c8aSRaman Tenneti 			continue;
370*f0687c8aSRaman Tenneti 
371*f0687c8aSRaman Tenneti 		try {
372*f0687c8aSRaman Tenneti 			VideoDevice vid(name);
373*f0687c8aSRaman Tenneti 
374*f0687c8aSRaman Tenneti 			if (vid.has_capture() && !vid.has_m2m())
375*f0687c8aSRaman Tenneti 				v.push_back(name);
376*f0687c8aSRaman Tenneti 		} catch (...) {
377*f0687c8aSRaman Tenneti 		}
378*f0687c8aSRaman Tenneti 	}
379*f0687c8aSRaman Tenneti 
380*f0687c8aSRaman Tenneti 	return v;
381*f0687c8aSRaman Tenneti }
382*f0687c8aSRaman Tenneti 
get_m2m_devices()383*f0687c8aSRaman Tenneti vector<string> VideoDevice::get_m2m_devices()
384*f0687c8aSRaman Tenneti {
385*f0687c8aSRaman Tenneti 	vector<string> v;
386*f0687c8aSRaman Tenneti 
387*f0687c8aSRaman Tenneti 	for (int i = 0; i < 20; ++i) {
388*f0687c8aSRaman Tenneti 		string name = "/dev/video" + to_string(i);
389*f0687c8aSRaman Tenneti 
390*f0687c8aSRaman Tenneti 		struct stat buffer;
391*f0687c8aSRaman Tenneti 		if (stat(name.c_str(), &buffer) != 0)
392*f0687c8aSRaman Tenneti 			continue;
393*f0687c8aSRaman Tenneti 
394*f0687c8aSRaman Tenneti 		try {
395*f0687c8aSRaman Tenneti 			VideoDevice vid(name);
396*f0687c8aSRaman Tenneti 
397*f0687c8aSRaman Tenneti 			if (vid.has_m2m())
398*f0687c8aSRaman Tenneti 				v.push_back(name);
399*f0687c8aSRaman Tenneti 		} catch (...) {
400*f0687c8aSRaman Tenneti 		}
401*f0687c8aSRaman Tenneti 	}
402*f0687c8aSRaman Tenneti 
403*f0687c8aSRaman Tenneti 	return v;
404*f0687c8aSRaman Tenneti }
405*f0687c8aSRaman Tenneti 
VideoStreamer(int fd,StreamerType type)406*f0687c8aSRaman Tenneti VideoStreamer::VideoStreamer(int fd, StreamerType type)
407*f0687c8aSRaman Tenneti 	: m_fd(fd), m_type(type)
408*f0687c8aSRaman Tenneti {
409*f0687c8aSRaman Tenneti }
410*f0687c8aSRaman Tenneti 
get_ports()411*f0687c8aSRaman Tenneti std::vector<string> VideoStreamer::get_ports()
412*f0687c8aSRaman Tenneti {
413*f0687c8aSRaman Tenneti 	vector<string> v;
414*f0687c8aSRaman Tenneti 
415*f0687c8aSRaman Tenneti 	switch (m_type) {
416*f0687c8aSRaman Tenneti 	case StreamerType::CaptureSingle:
417*f0687c8aSRaman Tenneti 	case StreamerType::CaptureMulti: {
418*f0687c8aSRaman Tenneti 		struct v4l2_input input {
419*f0687c8aSRaman Tenneti 		};
420*f0687c8aSRaman Tenneti 
421*f0687c8aSRaman Tenneti 		while (ioctl(m_fd, VIDIOC_ENUMINPUT, &input) == 0) {
422*f0687c8aSRaman Tenneti 			v.push_back(string((char*)&input.name));
423*f0687c8aSRaman Tenneti 			input.index++;
424*f0687c8aSRaman Tenneti 		}
425*f0687c8aSRaman Tenneti 
426*f0687c8aSRaman Tenneti 		break;
427*f0687c8aSRaman Tenneti 	}
428*f0687c8aSRaman Tenneti 
429*f0687c8aSRaman Tenneti 	case StreamerType::OutputSingle:
430*f0687c8aSRaman Tenneti 	case StreamerType::OutputMulti: {
431*f0687c8aSRaman Tenneti 		struct v4l2_output output {
432*f0687c8aSRaman Tenneti 		};
433*f0687c8aSRaman Tenneti 
434*f0687c8aSRaman Tenneti 		while (ioctl(m_fd, VIDIOC_ENUMOUTPUT, &output) == 0) {
435*f0687c8aSRaman Tenneti 			v.push_back(string((char*)&output.name));
436*f0687c8aSRaman Tenneti 			output.index++;
437*f0687c8aSRaman Tenneti 		}
438*f0687c8aSRaman Tenneti 
439*f0687c8aSRaman Tenneti 		break;
440*f0687c8aSRaman Tenneti 	}
441*f0687c8aSRaman Tenneti 
442*f0687c8aSRaman Tenneti 	default:
443*f0687c8aSRaman Tenneti 		FAIL("Bad StreamerType");
444*f0687c8aSRaman Tenneti 	}
445*f0687c8aSRaman Tenneti 
446*f0687c8aSRaman Tenneti 	return v;
447*f0687c8aSRaman Tenneti }
448*f0687c8aSRaman Tenneti 
set_port(uint32_t index)449*f0687c8aSRaman Tenneti void VideoStreamer::set_port(uint32_t index)
450*f0687c8aSRaman Tenneti {
451*f0687c8aSRaman Tenneti 	unsigned long req;
452*f0687c8aSRaman Tenneti 
453*f0687c8aSRaman Tenneti 	switch (m_type) {
454*f0687c8aSRaman Tenneti 	case StreamerType::CaptureSingle:
455*f0687c8aSRaman Tenneti 	case StreamerType::CaptureMulti:
456*f0687c8aSRaman Tenneti 		req = VIDIOC_S_INPUT;
457*f0687c8aSRaman Tenneti 		break;
458*f0687c8aSRaman Tenneti 
459*f0687c8aSRaman Tenneti 	case StreamerType::OutputSingle:
460*f0687c8aSRaman Tenneti 	case StreamerType::OutputMulti:
461*f0687c8aSRaman Tenneti 		req = VIDIOC_S_OUTPUT;
462*f0687c8aSRaman Tenneti 		break;
463*f0687c8aSRaman Tenneti 
464*f0687c8aSRaman Tenneti 	default:
465*f0687c8aSRaman Tenneti 		FAIL("Bad StreamerType");
466*f0687c8aSRaman Tenneti 	}
467*f0687c8aSRaman Tenneti 
468*f0687c8aSRaman Tenneti 	int r = ioctl(m_fd, req, &index);
469*f0687c8aSRaman Tenneti 	ASSERT(r == 0);
470*f0687c8aSRaman Tenneti }
471*f0687c8aSRaman Tenneti 
get_buf_type(VideoStreamer::StreamerType type)472*f0687c8aSRaman Tenneti static v4l2_buf_type get_buf_type(VideoStreamer::StreamerType type)
473*f0687c8aSRaman Tenneti {
474*f0687c8aSRaman Tenneti 	switch (type) {
475*f0687c8aSRaman Tenneti 	case VideoStreamer::StreamerType::CaptureSingle:
476*f0687c8aSRaman Tenneti 		return V4L2_BUF_TYPE_VIDEO_CAPTURE;
477*f0687c8aSRaman Tenneti 	case VideoStreamer::StreamerType::CaptureMulti:
478*f0687c8aSRaman Tenneti 		return V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
479*f0687c8aSRaman Tenneti 	case VideoStreamer::StreamerType::OutputSingle:
480*f0687c8aSRaman Tenneti 		return V4L2_BUF_TYPE_VIDEO_OUTPUT;
481*f0687c8aSRaman Tenneti 	case VideoStreamer::StreamerType::OutputMulti:
482*f0687c8aSRaman Tenneti 		return V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
483*f0687c8aSRaman Tenneti 	default:
484*f0687c8aSRaman Tenneti 		FAIL("Bad StreamerType");
485*f0687c8aSRaman Tenneti 	}
486*f0687c8aSRaman Tenneti }
487*f0687c8aSRaman Tenneti 
get_formats()488*f0687c8aSRaman Tenneti std::vector<PixelFormat> VideoStreamer::get_formats()
489*f0687c8aSRaman Tenneti {
490*f0687c8aSRaman Tenneti 	return v4l2_get_formats(m_fd, get_buf_type(m_type));
491*f0687c8aSRaman Tenneti }
492*f0687c8aSRaman Tenneti 
set_format(PixelFormat fmt,uint32_t width,uint32_t height)493*f0687c8aSRaman Tenneti void VideoStreamer::set_format(PixelFormat fmt, uint32_t width, uint32_t height)
494*f0687c8aSRaman Tenneti {
495*f0687c8aSRaman Tenneti 	v4l2_set_format(m_fd, fmt, width, height, get_buf_type(m_type));
496*f0687c8aSRaman Tenneti }
497*f0687c8aSRaman Tenneti 
get_selection(uint32_t & left,uint32_t & top,uint32_t & width,uint32_t & height)498*f0687c8aSRaman Tenneti void VideoStreamer::get_selection(uint32_t& left, uint32_t& top, uint32_t& width, uint32_t& height)
499*f0687c8aSRaman Tenneti {
500*f0687c8aSRaman Tenneti 	v4l2_get_selection(m_fd, left, top, width, height, get_buf_type(m_type));
501*f0687c8aSRaman Tenneti }
502*f0687c8aSRaman Tenneti 
set_selection(uint32_t & left,uint32_t & top,uint32_t & width,uint32_t & height)503*f0687c8aSRaman Tenneti void VideoStreamer::set_selection(uint32_t& left, uint32_t& top, uint32_t& width, uint32_t& height)
504*f0687c8aSRaman Tenneti {
505*f0687c8aSRaman Tenneti 	v4l2_set_selection(m_fd, left, top, width, height, get_buf_type(m_type));
506*f0687c8aSRaman Tenneti }
507*f0687c8aSRaman Tenneti 
set_queue_size(uint32_t queue_size)508*f0687c8aSRaman Tenneti void VideoStreamer::set_queue_size(uint32_t queue_size)
509*f0687c8aSRaman Tenneti {
510*f0687c8aSRaman Tenneti 	v4l2_request_bufs(m_fd, queue_size, get_buf_type(m_type));
511*f0687c8aSRaman Tenneti 	m_fbs.resize(queue_size);
512*f0687c8aSRaman Tenneti }
513*f0687c8aSRaman Tenneti 
queue(DumbFramebuffer * fb)514*f0687c8aSRaman Tenneti void VideoStreamer::queue(DumbFramebuffer* fb)
515*f0687c8aSRaman Tenneti {
516*f0687c8aSRaman Tenneti 	uint32_t idx;
517*f0687c8aSRaman Tenneti 
518*f0687c8aSRaman Tenneti 	for (idx = 0; idx < m_fbs.size(); ++idx) {
519*f0687c8aSRaman Tenneti 		if (m_fbs[idx] == nullptr)
520*f0687c8aSRaman Tenneti 			break;
521*f0687c8aSRaman Tenneti 	}
522*f0687c8aSRaman Tenneti 
523*f0687c8aSRaman Tenneti 	FAIL_IF(idx == m_fbs.size(), "queue full");
524*f0687c8aSRaman Tenneti 
525*f0687c8aSRaman Tenneti 	m_fbs[idx] = fb;
526*f0687c8aSRaman Tenneti 
527*f0687c8aSRaman Tenneti 	v4l2_queue_dmabuf(m_fd, idx, fb, get_buf_type(m_type));
528*f0687c8aSRaman Tenneti }
529*f0687c8aSRaman Tenneti 
dequeue()530*f0687c8aSRaman Tenneti DumbFramebuffer* VideoStreamer::dequeue()
531*f0687c8aSRaman Tenneti {
532*f0687c8aSRaman Tenneti 	uint32_t idx = v4l2_dequeue(m_fd, get_buf_type(m_type));
533*f0687c8aSRaman Tenneti 
534*f0687c8aSRaman Tenneti 	auto fb = m_fbs[idx];
535*f0687c8aSRaman Tenneti 	m_fbs[idx] = nullptr;
536*f0687c8aSRaman Tenneti 
537*f0687c8aSRaman Tenneti 	return fb;
538*f0687c8aSRaman Tenneti }
539*f0687c8aSRaman Tenneti 
stream_on()540*f0687c8aSRaman Tenneti void VideoStreamer::stream_on()
541*f0687c8aSRaman Tenneti {
542*f0687c8aSRaman Tenneti 	uint32_t buf_type = get_buf_type(m_type);
543*f0687c8aSRaman Tenneti 	int r = ioctl(m_fd, VIDIOC_STREAMON, &buf_type);
544*f0687c8aSRaman Tenneti 	FAIL_IF(r, "Failed to enable stream: %d", r);
545*f0687c8aSRaman Tenneti }
546*f0687c8aSRaman Tenneti 
stream_off()547*f0687c8aSRaman Tenneti void VideoStreamer::stream_off()
548*f0687c8aSRaman Tenneti {
549*f0687c8aSRaman Tenneti 	uint32_t buf_type = get_buf_type(m_type);
550*f0687c8aSRaman Tenneti 	int r = ioctl(m_fd, VIDIOC_STREAMOFF, &buf_type);
551*f0687c8aSRaman Tenneti 	FAIL_IF(r, "Failed to disable stream: %d", r);
552*f0687c8aSRaman Tenneti }
553