Skip to content

Instantly share code, notes, and snippets.

@Gnurou
Last active January 31, 2018 10:20
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/dbc3776ed97ea7d4ce6041ea15eb0438 to your computer and use it in GitHub Desktop.
Save Gnurou/dbc3776ed97ea7d4ce6041ea15eb0438 to your computer and use it in GitHub Desktop.
Request API simple test program
/*
* 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
*
* It performs the following:
* * open the vim2m and /dev/media0 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 <signal.h>
#include <poll.h>
#include <linux/videodev2.h>
#include <linux/v4l2-controls.h>
#include <linux/media.h>
static void open_devices(const char *vim2m, int *fd, int *media_fd)
{
printf("Opening /dev/media0\n");
*media_fd = open("/dev/media0", O_RDWR, 0);
if (*media_fd < 0) {
perror("cannot open media file");
exit(EXIT_FAILURE);
}
printf("Opening %s\n", vim2m);
*fd = open(vim2m, O_RDWR, 0);
if (*fd < 0) {
perror("cannot open media 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_cmd cmd;
int ret;
memset(&cmd, 0, sizeof(cmd));
cmd.cmd = MEDIA_REQ_CMD_ALLOC;
ret = ioctl(fd, MEDIA_IOC_REQUEST_CMD, &cmd);
if (ret < 0) {
perror("error while allocating request");
exit(EXIT_FAILURE);
}
*req = cmd.fd;
}
static void queue_request(int fd, int req)
{
struct media_request_cmd cmd;
int ret;
memset(&cmd, 0, sizeof(cmd));
cmd.cmd = MEDIA_REQ_CMD_SUBMIT;
cmd.fd = req;
ret = ioctl(fd, MEDIA_IOC_REQUEST_CMD, &cmd);
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 = V4L2_CTRL_WHICH_CUR_VAL;
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 = V4L2_CTRL_WHICH_CUR_VAL;
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 ret;
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, m_fd;
int i;
struct pollfd pfds[2] = {
{
.events = POLLIN,
},
{
.events = POLLIN,
},
};
if (argc != 2) {
printf("Usage: %s /dev/video_device\n", argv[0]);
return 0;
}
open_devices(argv[1], &fd, &m_fd);
/* 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(m_fd, &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\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\n",
trans_time, hflp, vflp);
get_vim2m_ctrls(fd, 0, &trans_time, &hflp, &vflp);
printf("Pre-submit HW transaction time: %d, hflip: %d, vflip: %d\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 */
output_buf[0].request_fd = request[0];
qbuf(fd, &output_buf[0]);
qbuf(fd, &capture_buf[0]);
/* Queue the request. This will apply the controls previously set and
* actually start streaming */
queue_request(m_fd, request[0]);
output_buf[1].request_fd = request[1];
qbuf(fd, &output_buf[1]);
/* The request is queued before the capture buf - this is valid, the
* driver will just wait for the correct number of buffers to start
* processing */
queue_request(m_fd, request[1]);
qbuf(fd, &capture_buf[1]);
/* Poll on the requests */
pfds[0].fd = request[1];
pfds[1].fd = request[0];
printf("Poll on first request\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);
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\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\n",
trans_time, hflp, vflp);
get_vim2m_ctrls(fd, 0, &trans_time, &hflp, &vflp);
printf("Post-dequeue HW transaction time: %d, hflip: %d, vflip: %d\n",
trans_time, hflp, vflp);
/* Close all requests */
for (i = nb_bufs; i > 0; i--)
close(request[i - 1]);
request_buffers(fd, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0);
request_buffers(fd, V4L2_BUF_TYPE_VIDEO_OUTPUT, 0);
close(m_fd);
close(fd);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment