Last active
April 17, 2018 04:17
-
-
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 file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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