Home | History | Annotate | Download | only in src
      1 #include <string>
      2 
      3 #include <sys/types.h>
      4 #include <sys/stat.h>
      5 #include <fcntl.h>
      6 #include <linux/videodev2.h>
      7 #include <sys/ioctl.h>
      8 #include <unistd.h>
      9 #include <system_error>
     10 
     11 #include <kms++/kms++.h>
     12 #include <kms++util/kms++util.h>
     13 #include <kms++util/videodevice.h>
     14 
     15 using namespace std;
     16 using namespace kms;
     17 
     18 /* V4L2 helper funcs */
     19 static vector<PixelFormat> v4l2_get_formats(int fd, uint32_t buf_type)
     20 {
     21 	vector<PixelFormat> v;
     22 
     23 	v4l2_fmtdesc desc { };
     24 	desc.type = buf_type;
     25 
     26 	while (ioctl(fd, VIDIOC_ENUM_FMT, &desc) == 0) {
     27 		v.push_back((PixelFormat)desc.pixelformat);
     28 		desc.index++;
     29 	}
     30 
     31 	return v;
     32 }
     33 
     34 static void v4l2_set_format(int fd, PixelFormat fmt, uint32_t width, uint32_t height, uint32_t buf_type)
     35 {
     36 	int r;
     37 
     38 	v4l2_format v4lfmt { };
     39 
     40 	v4lfmt.type = buf_type;
     41 	r = ioctl(fd, VIDIOC_G_FMT, &v4lfmt);
     42 	ASSERT(r == 0);
     43 
     44 	const PixelFormatInfo& pfi = get_pixel_format_info(fmt);
     45 
     46 	bool mplane = buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE || buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
     47 
     48 	if (mplane) {
     49 		v4l2_pix_format_mplane& mp = v4lfmt.fmt.pix_mp;
     50 
     51 		mp.pixelformat = (uint32_t)fmt;
     52 		mp.width = width;
     53 		mp.height = height;
     54 
     55 		mp.num_planes = pfi.num_planes;
     56 
     57 		for (unsigned i = 0; i < pfi.num_planes; ++i) {
     58 			const PixelFormatPlaneInfo& pfpi = pfi.planes[i];
     59 			v4l2_plane_pix_format& p = mp.plane_fmt[i];
     60 
     61 			p.bytesperline = width * pfpi.bitspp / 8;
     62 			p.sizeimage = p.bytesperline * height / pfpi.ysub;
     63 		}
     64 
     65 		r = ioctl(fd, VIDIOC_S_FMT, &v4lfmt);
     66 		ASSERT(r == 0);
     67 
     68 		ASSERT(mp.pixelformat == (uint32_t)fmt);
     69 		ASSERT(mp.width == width);
     70 		ASSERT(mp.height == height);
     71 
     72 		ASSERT(mp.num_planes == pfi.num_planes);
     73 
     74 		for (unsigned i = 0; i < pfi.num_planes; ++i) {
     75 			const PixelFormatPlaneInfo& pfpi = pfi.planes[i];
     76 			v4l2_plane_pix_format& p = mp.plane_fmt[i];
     77 
     78 			ASSERT(p.bytesperline == width * pfpi.bitspp / 8);
     79 			ASSERT(p.sizeimage == p.bytesperline * height / pfpi.ysub);
     80 		}
     81 	} else {
     82 		ASSERT(pfi.num_planes == 1);
     83 
     84 		v4lfmt.fmt.pix.pixelformat = (uint32_t)fmt;
     85 		v4lfmt.fmt.pix.width = width;
     86 		v4lfmt.fmt.pix.height = height;
     87 		v4lfmt.fmt.pix.bytesperline = width * pfi.planes[0].bitspp / 8;
     88 
     89 		r = ioctl(fd, VIDIOC_S_FMT, &v4lfmt);
     90 		ASSERT(r == 0);
     91 
     92 		ASSERT(v4lfmt.fmt.pix.pixelformat == (uint32_t)fmt);
     93 		ASSERT(v4lfmt.fmt.pix.width == width);
     94 		ASSERT(v4lfmt.fmt.pix.height == height);
     95 		ASSERT(v4lfmt.fmt.pix.bytesperline == width * pfi.planes[0].bitspp / 8);
     96 	}
     97 }
     98 
     99 static void v4l2_request_bufs(int fd, uint32_t queue_size, uint32_t buf_type)
    100 {
    101 	v4l2_requestbuffers v4lreqbuf { };
    102 	v4lreqbuf.type = buf_type;
    103 	v4lreqbuf.memory = V4L2_MEMORY_DMABUF;
    104 	v4lreqbuf.count = queue_size;
    105 	int r = ioctl(fd, VIDIOC_REQBUFS, &v4lreqbuf);
    106 	ASSERT(r == 0);
    107 	ASSERT(v4lreqbuf.count == queue_size);
    108 }
    109 
    110 static void v4l2_queue_dmabuf(int fd, uint32_t index, DumbFramebuffer* fb, uint32_t buf_type)
    111 {
    112 	v4l2_buffer buf { };
    113 	buf.type = buf_type;
    114 	buf.memory = V4L2_MEMORY_DMABUF;
    115 	buf.index = index;
    116 
    117 	const PixelFormatInfo& pfi = get_pixel_format_info(fb->format());
    118 
    119 	bool mplane = buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE || buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
    120 
    121 	if (mplane) {
    122 		buf.length = pfi.num_planes;
    123 
    124 		v4l2_plane planes[4] { };
    125 		buf.m.planes = planes;
    126 
    127 		for (unsigned i = 0; i < pfi.num_planes; ++i) {
    128 			planes[i].m.fd = fb->prime_fd(i);
    129 			planes[i].bytesused = fb->size(i);
    130 			planes[i].length = fb->size(i);
    131 		}
    132 
    133 		int r = ioctl(fd, VIDIOC_QBUF, &buf);
    134 		ASSERT(r == 0);
    135 	} else {
    136 		buf.m.fd = fb->prime_fd(0);
    137 
    138 		int r = ioctl(fd, VIDIOC_QBUF, &buf);
    139 		ASSERT(r == 0);
    140 	}
    141 }
    142 
    143 static uint32_t v4l2_dequeue(int fd, uint32_t buf_type)
    144 {
    145 	v4l2_buffer buf { };
    146 	buf.type = buf_type;
    147 	buf.memory = V4L2_MEMORY_DMABUF;
    148 
    149 	// V4L2 crashes if planes are not set
    150 	v4l2_plane planes[4] { };
    151 	buf.m.planes = planes;
    152 	buf.length = 4;
    153 
    154 	int r = ioctl(fd, VIDIOC_DQBUF, &buf);
    155 	if (r)
    156 		throw system_error(errno, generic_category());
    157 
    158 	return buf.index;
    159 }
    160 
    161 
    162 
    163 
    164 VideoDevice::VideoDevice(const string& dev)
    165 	:VideoDevice(::open(dev.c_str(), O_RDWR | O_NONBLOCK))
    166 {
    167 }
    168 
    169 VideoDevice::VideoDevice(int fd)
    170 	: m_fd(fd), m_has_capture(false), m_has_output(false), m_has_m2m(false), m_capture_streamer(0), m_output_streamer(0)
    171 {
    172 	FAIL_IF(fd < 0, "Bad fd");
    173 
    174 	struct v4l2_capability cap = { };
    175 	int r = ioctl(fd, VIDIOC_QUERYCAP, &cap);
    176 	ASSERT(r == 0);
    177 
    178 	if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE) {
    179 		m_has_capture = true;
    180 		m_has_mplane_capture = true;
    181 	} else if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) {
    182 		m_has_capture = true;
    183 		m_has_mplane_capture = false;
    184 	}
    185 
    186 	if (cap.capabilities & V4L2_CAP_VIDEO_OUTPUT_MPLANE) {
    187 		m_has_output = true;
    188 		m_has_mplane_output = true;
    189 	} else if (cap.capabilities & V4L2_CAP_VIDEO_OUTPUT) {
    190 		m_has_output = true;
    191 		m_has_mplane_output = false;
    192 	}
    193 
    194 	if (cap.capabilities & V4L2_CAP_VIDEO_M2M_MPLANE) {
    195 		m_has_m2m = true;
    196 		m_has_capture = true;
    197 		m_has_output = true;
    198 		m_has_mplane_m2m = true;
    199 		m_has_mplane_capture = true;
    200 		m_has_mplane_output = true;
    201 	} else if (cap.capabilities & V4L2_CAP_VIDEO_M2M) {
    202 		m_has_m2m = true;
    203 		m_has_capture = true;
    204 		m_has_output = true;
    205 		m_has_mplane_m2m = false;
    206 		m_has_mplane_capture = false;
    207 		m_has_mplane_output = false;
    208 	}
    209 }
    210 
    211 VideoDevice::~VideoDevice()
    212 {
    213 	::close(m_fd);
    214 }
    215 
    216 VideoStreamer* VideoDevice::get_capture_streamer()
    217 {
    218 	ASSERT(m_has_capture);
    219 
    220 	if (!m_capture_streamer) {
    221 		auto type = m_has_mplane_capture ? VideoStreamer::StreamerType::CaptureMulti : VideoStreamer::StreamerType::CaptureSingle;
    222 		m_capture_streamer = new VideoStreamer(m_fd, type);
    223 	}
    224 
    225 	return m_capture_streamer;
    226 }
    227 
    228 VideoStreamer* VideoDevice::get_output_streamer()
    229 {
    230 	ASSERT(m_has_output);
    231 
    232 	if (!m_output_streamer) {
    233 		auto type = m_has_mplane_output ? VideoStreamer::StreamerType::OutputMulti : VideoStreamer::StreamerType::OutputSingle;
    234 		m_output_streamer = new VideoStreamer(m_fd, type);
    235 	}
    236 
    237 	return m_output_streamer;
    238 }
    239 
    240 vector<tuple<uint32_t, uint32_t>> VideoDevice::get_discrete_frame_sizes(PixelFormat fmt)
    241 {
    242 	vector<tuple<uint32_t, uint32_t>> v;
    243 
    244 	v4l2_frmsizeenum v4lfrms { };
    245 	v4lfrms.pixel_format = (uint32_t)fmt;
    246 
    247 	int r = ioctl(m_fd, VIDIOC_ENUM_FRAMESIZES, &v4lfrms);
    248 	ASSERT(r);
    249 
    250 	FAIL_IF(v4lfrms.type != V4L2_FRMSIZE_TYPE_DISCRETE, "No discrete frame sizes");
    251 
    252 	while (ioctl(m_fd, VIDIOC_ENUM_FRAMESIZES, &v4lfrms) == 0) {
    253 		v.emplace_back(v4lfrms.discrete.width, v4lfrms.discrete.height);
    254 		v4lfrms.index++;
    255 	};
    256 
    257 	return v;
    258 }
    259 
    260 VideoDevice::VideoFrameSize VideoDevice::get_frame_sizes(PixelFormat fmt)
    261 {
    262 	v4l2_frmsizeenum v4lfrms { };
    263 	v4lfrms.pixel_format = (uint32_t)fmt;
    264 
    265 	int r = ioctl(m_fd, VIDIOC_ENUM_FRAMESIZES, &v4lfrms);
    266 	ASSERT(r);
    267 
    268 	FAIL_IF(v4lfrms.type == V4L2_FRMSIZE_TYPE_DISCRETE, "No continuous frame sizes");
    269 
    270 	VideoFrameSize s;
    271 
    272 	s.min_w = v4lfrms.stepwise.min_width;
    273 	s.max_w = v4lfrms.stepwise.max_width;
    274 	s.step_w = v4lfrms.stepwise.step_width;
    275 
    276 	s.min_h = v4lfrms.stepwise.min_height;
    277 	s.max_h = v4lfrms.stepwise.max_height;
    278 	s.step_h = v4lfrms.stepwise.step_height;
    279 
    280 	return s;
    281 }
    282 
    283 vector<string> VideoDevice::get_capture_devices()
    284 {
    285 	vector<string> v;
    286 
    287 	for (int i = 0; i < 20; ++i) {
    288 		string name = "/dev/video" + to_string(i);
    289 
    290 		struct stat buffer;
    291 		if (stat(name.c_str(), &buffer) != 0)
    292 			continue;
    293 
    294 		VideoDevice vid(name);
    295 
    296 		if (vid.has_capture() && !vid.has_m2m())
    297 			v.push_back(name);
    298 	}
    299 
    300 	return v;
    301 }
    302 
    303 vector<string> VideoDevice::get_m2m_devices()
    304 {
    305 	vector<string> v;
    306 
    307 	for (int i = 0; i < 20; ++i) {
    308 		string name = "/dev/video" + to_string(i);
    309 
    310 		struct stat buffer;
    311 		if (stat(name.c_str(), &buffer) != 0)
    312 			continue;
    313 
    314 		VideoDevice vid(name);
    315 
    316 		if (vid.has_m2m())
    317 			v.push_back(name);
    318 	}
    319 
    320 	return v;
    321 }
    322 
    323 
    324 VideoStreamer::VideoStreamer(int fd, StreamerType type)
    325 	: m_fd(fd), m_type(type)
    326 {
    327 
    328 }
    329 
    330 std::vector<string> VideoStreamer::get_ports()
    331 {
    332 	vector<string> v;
    333 
    334 	switch (m_type) {
    335 	case StreamerType::CaptureSingle:
    336 	case StreamerType::CaptureMulti:
    337 	{
    338 		struct v4l2_input input { };
    339 
    340 		while (ioctl(m_fd, VIDIOC_ENUMINPUT, &input) == 0) {
    341 			v.push_back(string((char*)&input.name));
    342 			input.index++;
    343 		}
    344 
    345 		break;
    346 	}
    347 
    348 	case StreamerType::OutputSingle:
    349 	case StreamerType::OutputMulti:
    350 	{
    351 		struct v4l2_output output { };
    352 
    353 		while (ioctl(m_fd, VIDIOC_ENUMOUTPUT, &output) == 0) {
    354 			v.push_back(string((char*)&output.name));
    355 			output.index++;
    356 		}
    357 
    358 		break;
    359 	}
    360 
    361 	default:
    362 		FAIL("Bad StreamerType");
    363 	}
    364 
    365 	return v;
    366 }
    367 
    368 void VideoStreamer::set_port(uint32_t index)
    369 {
    370 	unsigned long req;
    371 
    372 	switch (m_type) {
    373 	case StreamerType::CaptureSingle:
    374 	case StreamerType::CaptureMulti:
    375 		req = VIDIOC_S_INPUT;
    376 		break;
    377 
    378 	case StreamerType::OutputSingle:
    379 	case StreamerType::OutputMulti:
    380 		req = VIDIOC_S_OUTPUT;
    381 		break;
    382 
    383 	default:
    384 		FAIL("Bad StreamerType");
    385 	}
    386 
    387 	int r = ioctl(m_fd, req, &index);
    388 	ASSERT(r == 0);
    389 }
    390 
    391 static v4l2_buf_type get_buf_type(VideoStreamer::StreamerType type)
    392 {
    393 	switch (type) {
    394 	case VideoStreamer::StreamerType::CaptureSingle:
    395 		return V4L2_BUF_TYPE_VIDEO_CAPTURE;
    396 	case VideoStreamer::StreamerType::CaptureMulti:
    397 		return V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    398 	case VideoStreamer::StreamerType::OutputSingle:
    399 		return V4L2_BUF_TYPE_VIDEO_OUTPUT;
    400 	case VideoStreamer::StreamerType::OutputMulti:
    401 		return V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
    402 	default:
    403 		FAIL("Bad StreamerType");
    404 	}
    405 }
    406 
    407 std::vector<PixelFormat> VideoStreamer::get_formats()
    408 {
    409 	return v4l2_get_formats(m_fd, get_buf_type(m_type));
    410 }
    411 
    412 void VideoStreamer::set_format(PixelFormat fmt, uint32_t width, uint32_t height)
    413 {
    414 	v4l2_set_format(m_fd, fmt, width, height, get_buf_type(m_type));
    415 }
    416 
    417 void VideoStreamer::set_queue_size(uint32_t queue_size)
    418 {
    419 	v4l2_request_bufs(m_fd, queue_size, get_buf_type(m_type));
    420 	m_fbs.resize(queue_size);
    421 }
    422 
    423 void VideoStreamer::queue(DumbFramebuffer* fb)
    424 {
    425 	uint32_t idx;
    426 
    427 	for (idx = 0; idx < m_fbs.size(); ++idx) {
    428 		if (m_fbs[idx] == nullptr)
    429 			break;
    430 	}
    431 
    432 	FAIL_IF(idx == m_fbs.size(), "queue full");
    433 
    434 	m_fbs[idx] = fb;
    435 
    436 	v4l2_queue_dmabuf(m_fd, idx, fb, get_buf_type(m_type));
    437 }
    438 
    439 DumbFramebuffer* VideoStreamer::dequeue()
    440 {
    441 	uint32_t idx = v4l2_dequeue(m_fd, get_buf_type(m_type));
    442 
    443 	auto fb = m_fbs[idx];
    444 	m_fbs[idx] = nullptr;
    445 
    446 	return fb;
    447 }
    448 
    449 void VideoStreamer::stream_on()
    450 {
    451 	uint32_t buf_type = get_buf_type(m_type);
    452 	int r = ioctl(m_fd, VIDIOC_STREAMON, &buf_type);
    453 	FAIL_IF(r, "Failed to enable stream: %d", r);
    454 }
    455 
    456 void VideoStreamer::stream_off()
    457 {
    458 	uint32_t buf_type = get_buf_type(m_type);
    459 	int r = ioctl(m_fd, VIDIOC_STREAMOFF, &buf_type);
    460 	FAIL_IF(r, "Failed to disable stream: %d", r);
    461 }
    462