Skip to content

Instantly share code, notes, and snippets.

@Gnurou
Last active April 17, 2018 04:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Gnurou/34c35f1f8e278dad454b51578d239a42 to your computer and use it in GitHub Desktop.
Save Gnurou/34c35f1f8e278dad454b51578d239a42 to your computer and use it in GitHub Desktop.
Test program demonstrating the V4L2 request API on a codec device (vim2m)
/*
* This program is a very simple demonstration of the proposed V4L2 request API.
* It must be invoked with the path to the vim2m device as parameter, e.g.
*
* $ ./m2mtest /dev/videoX /dev/mediaX
*
* It performs the following:
* * open the vim2m and media devices, allocate buffers and requests
* * submit a first request with a processing time of 600 ms
* * submit a second request with a processing time of 2000 ms
* * wait for requests to complete
* * queries the requests' controls to verify that the value are as expected
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <time.h>
#include <signal.h>
#include <poll.h>
#include <linux/videodev2.h>
#include <linux/v4l2-controls.h>
#include <linux/media.h>
static void open_device(const char *vim2m, const char *media, int *fd, int *mfd)
{
printf("Opening %s\n", media);
*mfd = open(media, O_RDWR, 0);
if (*mfd < 0) {
perror("cannot open media device file");
exit(EXIT_FAILURE);
}
printf("Opening %s\n", vim2m);
*fd = open(vim2m, O_RDWR, 0);
if (*fd < 0) {
perror("cannot open device file");
exit(EXIT_FAILURE);
}
}
static void set_format(int fd, int buf_type)
{
struct v4l2_format format;
int ret;
memset(&format, 0, sizeof(format));
format.type = buf_type;
format.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24;
format.fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
format.fmt.pix.width = 1280;
format.fmt.pix.height = 720;
ret = ioctl(fd, VIDIOC_S_FMT, &format);
if (ret < 0) {
perror("error while setting format");
exit(EXIT_FAILURE);
}
}
static void request_buffers(int fd, int buf_type, unsigned int count)
{
struct v4l2_requestbuffers requestbuffers;
int ret;
memset(&requestbuffers, 0, sizeof(requestbuffers));
requestbuffers.type = buf_type;
requestbuffers.memory = V4L2_MEMORY_MMAP;
requestbuffers.count = count;
ret = ioctl(fd, VIDIOC_REQBUFS, &requestbuffers);
if (ret < 0) {
perror("error while requesting buffers");
exit(EXIT_FAILURE);
}
if (count > 0)
printf("%d %s buffers allocated\n", requestbuffers.count,
buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE ?
"capture" : "output");
else
printf("All %s buffers freed\n",
buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE ?
"capture" : "output");
}
static void query_buf(int fd, int buf_type, int index, struct v4l2_buffer *buf)
{
int ret;
memset(buf, 0, sizeof(*buf));
buf->memory = V4L2_MEMORY_MMAP;
buf->type = buf_type;
buf->index = index;
ret = ioctl(fd, VIDIOC_QUERYBUF, buf);
if (ret < 0) {
perror("error while querying buffer");
exit(EXIT_FAILURE);
}
}
static void alloc_request(int fd, int *req)
{
struct media_request_alloc new;
int ret;
memset(&new, 0, sizeof(new));
ret = ioctl(fd, MEDIA_IOC_REQUEST_ALLOC, &new);
if (ret < 0) {
perror("error while allocating request");
exit(EXIT_FAILURE);
}
*req = new.fd;
}
static void submit_request(int req)
{
int ret;
ret = ioctl(req, MEDIA_REQUEST_IOC_QUEUE, NULL);
if (ret < 0) {
perror("error while queueing request");
exit(EXIT_FAILURE);
}
}
static void set_stream(int fd, int buf_type, int on)
{
int ret;
ret = ioctl(fd, on ? VIDIOC_STREAMON : VIDIOC_STREAMOFF, &buf_type);
if (ret < 0) {
perror("error while setting streaming");
exit(EXIT_FAILURE);
}
printf("%s stream %s\n", buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE ?
"Capture" : "Output", on ? "activated" : "deactivated");
}
static void set_vim2m_ctrls(int fd, int req, unsigned int trans_time,
int hflip, int vflip)
{
struct v4l2_ext_control ctrl[3];
struct v4l2_ext_controls ctrls;
int ret;
memset(&ctrl, 0, sizeof(ctrl));
ctrl[0].id = (V4L2_CID_USER_BASE + 0x1000); // Transaction time
ctrl[0].value = trans_time;
ctrl[1].id = V4L2_CID_HFLIP;
ctrl[1].value = hflip;
ctrl[2].id = V4L2_CID_VFLIP;
ctrl[2].value = vflip;
memset(&ctrls, 0, sizeof(ctrls));
ctrls.request_fd = req;
ctrls.which = req < 0 ? V4L2_CTRL_WHICH_CUR_VAL : V4L2_CTRL_WHICH_REQUEST;
ctrls.count = 3;
ctrls.controls = ctrl;
ret = ioctl(fd, VIDIOC_S_EXT_CTRLS, &ctrls);
if (ret < 0) {
perror("error while setting control value");
exit(EXIT_FAILURE);
}
}
static void get_vim2m_ctrls(int fd, int req, int *trans_time,
int *hflip, int *vflip)
{
struct v4l2_ext_control ctrl[3];
struct v4l2_ext_controls ctrls;
int ret;
memset(&ctrl, 0, sizeof(ctrl));
ctrl[0].id = (V4L2_CID_USER_BASE + 0x1000); // Transaction time
ctrl[1].id = V4L2_CID_HFLIP;
ctrl[2].id = V4L2_CID_VFLIP;
memset(&ctrls, 0, sizeof(ctrls));
ctrls.request_fd = req;
ctrls.which = req < 0 ? V4L2_CTRL_WHICH_CUR_VAL : V4L2_CTRL_WHICH_REQUEST;
ctrls.count = 3;
ctrls.controls = ctrl;
ret = ioctl(fd, VIDIOC_G_EXT_CTRLS, &ctrls);
if (ret < 0) {
perror("error while getting control value");
exit(EXIT_FAILURE);
}
if (trans_time)
*trans_time = ctrl[0].value;
if (hflip)
*hflip = ctrl[1].value;
if (vflip)
*vflip = ctrl[2].value;
}
static void qbuf(int fd, struct v4l2_buffer *buf, int req)
{
int ret;
if (req > 0) {
buf->request_fd = req;
buf->flags |= V4L2_BUF_FLAG_REQUEST_FD;
} else {
buf->request_fd = 0;
buf->flags &= ~V4L2_BUF_FLAG_REQUEST_FD;
}
ret = ioctl(fd, VIDIOC_QBUF, buf);
if (ret < 0) {
perror("error while queuing buffer");
exit(EXIT_FAILURE);
}
printf("%s buffer %d queued, request FD %d\n",
buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE ? "Capture" : "Output",
buf->index, buf->request_fd);
}
static int dqbuf(int fd, int buf_type)
{
struct v4l2_buffer bufferinfo;
int ret;
memset(&bufferinfo, 0, sizeof bufferinfo);
bufferinfo.memory = V4L2_MEMORY_MMAP;
bufferinfo.type = buf_type;
ret = ioctl(fd, VIDIOC_DQBUF, &bufferinfo);
if (ret < 0) {
perror("error while dequeuing buffer");
exit(EXIT_FAILURE);
}
printf("%s buffer %d dequeued, request FD %d\n",
buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE ? "Capture" : "Output",
bufferinfo.index, bufferinfo.request_fd);
return bufferinfo.index;
}
int main(int argc, char *argv[])
{
static const unsigned int nb_bufs = 2;
struct v4l2_buffer output_buf[nb_bufs];
struct v4l2_buffer capture_buf[nb_bufs];
int request[nb_bufs];
int trans_time, hflp, vflp;
int fd, mfd;
int i;
struct pollfd pfds[2] = {
{
.events = POLLPRI,
},
{
.events = POLLPRI,
},
};
if (argc != 3) {
printf("Usage: %s /dev/video_device /dev/media_device\n", argv[0]);
return 0;
}
open_device(argv[1], argv[2], &fd, &mfd);
/* Set format on capture and output queues */
set_format(fd, V4L2_BUF_TYPE_VIDEO_CAPTURE);
set_format(fd, V4L2_BUF_TYPE_VIDEO_OUTPUT);
request_buffers(fd, V4L2_BUF_TYPE_VIDEO_CAPTURE, nb_bufs);
request_buffers(fd, V4L2_BUF_TYPE_VIDEO_OUTPUT, nb_bufs);
for (i = 0; i < nb_bufs; i++) {
query_buf(fd, V4L2_BUF_TYPE_VIDEO_OUTPUT, i, &output_buf[i]);
output_buf[i].bytesused = output_buf[i].length;
query_buf(fd, V4L2_BUF_TYPE_VIDEO_CAPTURE, i, &capture_buf[i]);
}
for (i = 0; i < nb_bufs; i++) {
alloc_request(mfd, &request[i]);
printf("Allocated request %d\n", request[i]);
}
set_stream(fd, V4L2_BUF_TYPE_VIDEO_OUTPUT, 1);
set_stream(fd, V4L2_BUF_TYPE_VIDEO_CAPTURE, 1);
set_vim2m_ctrls(fd, request[0], 600, 1, 0);
set_vim2m_ctrls(fd, request[1], 2000, 0, 1);
/* Query the controls on the requests and the hardware */
get_vim2m_ctrls(fd, request[0], &trans_time, &hflp, &vflp);
printf("Pre-submit first request transaction time: %d, hflip: %d, vflip: %d (should be transaction time: 600, hflip: 1, vflip: 0)\n",
trans_time, hflp, vflp);
get_vim2m_ctrls(fd, request[1], &trans_time, &hflp, &vflp);
printf("Pre-submit second request transaction time: %d, hflip: %d, vflip: %d (should be transaction time: 2000, hflip: 0, vflip: 1)\n",
trans_time, hflp, vflp);
get_vim2m_ctrls(fd, -1, &trans_time, &hflp, &vflp);
printf("Pre-submit HW transaction time: %d, hflip: %d, vflip: %d (should be transaction time: 40, hflip: 0, vflip: 0)\n",
trans_time, hflp, vflp);
/* Queue buffers for our first request. Even though the stream is active
* on both queues, streaming will not start until the request is queued */
qbuf(fd, &output_buf[0], request[0]);
// TODO request should not be needed!
qbuf(fd, &capture_buf[0], request[0]);
/* Queue the request. This will apply the controls previously set and
* actually start streaming */
submit_request(request[0]);
qbuf(fd, &output_buf[1], request[1]);
// TODO request should not be needed!
qbuf(fd, &capture_buf[1], request[1]);
submit_request(request[1]);
/* Poll on the requests */
pfds[0].fd = request[0];
pfds[1].fd = request[1];
printf("Poll on both requests\n");
poll(pfds, 2, -1);
printf("%08x %08x\n", pfds[0].revents, pfds[1].revents);
printf("Poll on second request\n");
poll(&pfds[1], 1, -1);
printf("%08x\n", pfds[0].revents);
/* Dequeue all buffers. */
for (i = 0; i < nb_bufs; i++) {
dqbuf(fd, V4L2_BUF_TYPE_VIDEO_OUTPUT);
dqbuf(fd, V4L2_BUF_TYPE_VIDEO_CAPTURE);
}
set_stream(fd, V4L2_BUF_TYPE_VIDEO_OUTPUT, 0);
set_stream(fd, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0);
/* Query the controls on the completed requests and the hardware */
get_vim2m_ctrls(fd, request[0], &trans_time, &hflp, &vflp);
printf("Post-dequeue first request transaction time: %d, hflip: %d, vflip: %d (should be transaction time: 600, hflip: 1, vflip: 0)\n",
trans_time, hflp, vflp);
get_vim2m_ctrls(fd, request[1], &trans_time, &hflp, &vflp);
printf("Post-dequeue second request transaction time: %d, hflip: %d, vflip: %d (should be transaction time: 2000, hflip: 0, vflip: 1)\n",
trans_time, hflp, vflp);
get_vim2m_ctrls(fd, -1, &trans_time, &hflp, &vflp);
printf("Post-dequeue HW transaction time: %d, hflip: %d, vflip: %d (should be transaction time: 2000, hflip: 0, vflip: 1)\n",
trans_time, hflp, vflp);
/* free buffers */
request_buffers(fd, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0);
request_buffers(fd, V4L2_BUF_TYPE_VIDEO_OUTPUT, 0);
/* Close all requests */
for (i = nb_bufs; i > 0; i--)
close(request[i - 1]);
close(fd);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment