This is an example application for testing mem-to-mem framework using mem2mem-testdev device. It is intended to be executed multiple times in parallel to test multi-instance operation and scheduling. Each process can be configured differently using command-line arguments. The application opens video test device and framebuffer, sets up params, queues src/dst buffers and displays processed results on the framebuffer. Configurable parameters: starting point on the framebuffer, width/height of buffers, transaction length (in buffers), transaction duration, total number of frames to be processed. Tested on a 800x480 framebuffer with the following script: #!/bin/bash for i in {0..3} do ((x=$i * 100)) ./process-vmalloc 0 $(($i + 1)) $((2000 - $i * 500)) $((($i+1) * 4)) \ $x $x 100 100 & done Signed-off-by: Pawel Osciak <p.osciak@xxxxxxxxxxx> Reviewed-by: Kyungmin Park <kyungmin.park@xxxxxxxxxxx> --- --- /dev/null 2009-11-17 07:51:25.574927259 +0100 +++ process-vmalloc.c 2009-11-26 11:00:26.000000000 +0100 @@ -0,0 +1,420 @@ +/** + * process-vmalloc.c + * Capture+output (process) V4L2 device tester. + * + * Pawel Osciak, p.osciak@xxxxxxxxxxx + * 2009, Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <time.h> +#include <errno.h> + +#include <fcntl.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <stdint.h> + +#include <linux/fb.h> +#include <linux/videodev2.h> + +#include <sys/mman.h> + +#define V4L2_CID_TRANS_TIME_MSEC (V4L2_CID_PRIVATE_BASE) +#define V4L2_CID_TRANS_NUM_BUFS (V4L2_CID_PRIVATE_BASE + 1) + +#define VIDEO_DEV_NAME "/dev/video0" +#define FB_DEV_NAME "/dev/fb0" +#define NUM_BUFS 4 +#define NUM_FRAMES 16 + +#define perror_exit(cond, func)\ + if (cond) {\ + fprintf(stderr, "%s:%d: ", __func__, __LINE__);\ + perror(func);\ + exit(EXIT_FAILURE);\ + } + +#define error_exit(cond, func)\ + if (cond) {\ + fprintf(stderr, "%s:%d: failed\n", func, __LINE__);\ + exit(EXIT_FAILURE);\ + } + +#define perror_ret(cond, func)\ + if (cond) {\ + fprintf(stderr, "%s:%d: ", __func__, __LINE__);\ + perror(func);\ + return ret;\ + } + +#define memzero(x)\ + memset(&(x), 0, sizeof (x)); + +#define PROCESS_DEBUG 1 +#ifdef PROCESS_DEBUG +#define debug(msg, ...)\ + fprintf(stderr, "%s: " msg, __func__, ##__VA_ARGS__); +#else +#define debug(msg, ...) +#endif + +static int vid_fd, fb_fd; +static void *fb_addr; +static char *p_src_buf[NUM_BUFS], *p_dst_buf[NUM_BUFS]; +static size_t src_buf_size[NUM_BUFS], dst_buf_size[NUM_BUFS]; +static uint32_t num_src_bufs = 0, num_dst_bufs = 0; + +/* Command-line params */ +int initial_delay = 0; +int fb_x, fb_y, width, height; +int translen = 1; +/* For displaying multi-buffer transaction simulations, indicates current + * buffer in an ongoing transaction */ +int curr_buf = 0; +int transtime = 1000; +int num_frames = 0; +off_t fb_off, fb_line_w, fb_buf_w; +struct fb_var_screeninfo fbinfo; + +static void init_video_dev(void) +{ + int ret; + struct v4l2_capability cap; + struct v4l2_format fmt; + struct v4l2_control ctrl; + + vid_fd = open(VIDEO_DEV_NAME, O_RDWR | O_NONBLOCK, 0); + perror_exit(vid_fd < 0, "open"); + + ctrl.id = V4L2_CID_TRANS_TIME_MSEC; + ctrl.value = transtime; + ret = ioctl(vid_fd, VIDIOC_S_CTRL, &ctrl); + perror_exit(ret != 0, "ioctl"); + + ctrl.id = V4L2_CID_TRANS_NUM_BUFS; + ctrl.value = translen; + ret = ioctl(vid_fd, VIDIOC_S_CTRL, &ctrl); + perror_exit(ret != 0, "ioctl"); + + ret = ioctl(vid_fd, VIDIOC_QUERYCAP, &cap); + perror_exit(ret != 0, "ioctl"); + + if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { + fprintf(stderr, "Device does not support capture\n"); + exit(EXIT_FAILURE); + } + if (!(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT)) { + fprintf(stderr, "Device does not support output\n"); + exit(EXIT_FAILURE); + } + + /* Set format for capture */ + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fmt.fmt.pix.width = width; + fmt.fmt.pix.height = height; + fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB565X; + fmt.fmt.pix.field = V4L2_FIELD_ANY; + + ret = ioctl(vid_fd, VIDIOC_S_FMT, &fmt); + perror_exit(ret != 0, "ioctl"); + + /* The same format for output */ + fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + fmt.fmt.pix.width = width; + fmt.fmt.pix.height = height; + fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB565X; + fmt.fmt.pix.field = V4L2_FIELD_ANY; + + ret = ioctl(vid_fd, VIDIOC_S_FMT, &fmt); + perror_exit(ret != 0, "ioctl"); +} + +static void gen_src_buf(void *p, size_t size) +{ + uint8_t val; + + val = rand() % 256; + memset(p, val, size); +} + +static void gen_dst_buf(void *p, size_t size) +{ + /* White */ + memset(p, 255, 0); +} + +static int read_frame(int last) +{ + struct v4l2_buffer buf; + int ret; + int j; + char * p_fb = fb_addr + fb_off; + + memzero(buf); + + buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + buf.memory = V4L2_MEMORY_MMAP; + + ret = ioctl(vid_fd, VIDIOC_DQBUF, &buf); + debug("Dequeued source buffer, index: %d\n", buf.index); + if (ret) { + switch (errno) { + case EAGAIN: + debug("Got EAGAIN\n"); + return 0; + + case EIO: + debug("Got EIO\n"); + return 0; + + default: + perror("ioctl"); + return 0; + } + } + + /* Verify we've got a correct buffer */ + assert(buf.index < num_src_bufs); + + /* Enqueue back the buffer (note that the index is preserved) */ + if (!last) { + gen_src_buf(p_src_buf[buf.index], src_buf_size[buf.index]); + buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + buf.memory = V4L2_MEMORY_MMAP; + ret = ioctl(vid_fd, VIDIOC_QBUF, &buf); + perror_ret(ret != 0, "ioctl"); + } + + + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + debug("Dequeuing destination buffer\n"); + ret = ioctl(vid_fd, VIDIOC_DQBUF, &buf); + if (ret) { + switch (errno) { + case EAGAIN: + debug("Got EAGAIN\n"); + return 0; + + case EIO: + debug("Got EIO\n"); + return 0; + + default: + perror("ioctl"); + return 1; + } + } + debug("Dequeued dst buffer, index: %d\n", buf.index); + /* Verify we've got a correct buffer */ + assert(buf.index < num_dst_bufs); + + debug("Current buffer in the transaction: %d\n", curr_buf); + p_fb += curr_buf * (height / translen) * fb_line_w; + ++curr_buf; + if (curr_buf >= translen) + curr_buf = 0; + + /* Display results */ + for (j = 0; j < height / translen; ++j) { + memcpy(p_fb, (void *)p_dst_buf[buf.index], fb_buf_w); + p_fb += fb_line_w; + } + + /* Enqueue back the buffer */ + if (!last) { + gen_dst_buf(p_dst_buf[buf.index], dst_buf_size[buf.index]); + ret = ioctl(vid_fd, VIDIOC_QBUF, &buf); + perror_ret(ret != 0, "ioctl"); + debug("Enqueued back dst buffer\n"); + } + + return 0; +} + +void init_usage(int argc, char *argv[]) +{ + if (argc != 9) { + printf("Usage: %s initial_delay bufs_per_transaction " + "trans_length_msec num_frames fb_offset_x fb_offset_y " + "width height\n", argv[0]); + exit(EXIT_FAILURE); + } + + initial_delay = atoi(argv[1]); + translen = atoi(argv[2]); + transtime = atoi(argv[3]); + num_frames = atoi(argv[4]); + fb_x = atoi(argv[5]); + fb_y = atoi(argv[6]); + width = atoi(argv[7]); + height = atoi(argv[8]); + debug("NEW PROCESS: fb_x: %d, fb_y: %d, width: %d, height: %d, " + "translen: %d, transtime: %d, num_frames: %d\n", + fb_x, fb_y, width, height, translen, transtime, num_frames); +} + +void init_fb(void) +{ + int ret; + size_t map_size; + + fb_fd = open(FB_DEV_NAME, O_RDWR, 0); + perror_exit(fb_fd < 0, "open"); + + ret = ioctl(fb_fd, FBIOGET_VSCREENINFO, &fbinfo); + perror_exit(ret != 0, "ioctl"); + debug("fbinfo: xres: %d, xres_virt: %d, yres: %d, yres_virt: %d\n", + fbinfo.xres, fbinfo.xres_virtual, + fbinfo.yres, fbinfo.yres_virtual); + + fb_line_w= fbinfo.xres_virtual * (fbinfo.bits_per_pixel >> 3); + fb_off = fb_y * fb_line_w + fb_x * (fbinfo.bits_per_pixel >> 3); + fb_buf_w = width * (fbinfo.bits_per_pixel >> 3); + map_size = fb_line_w * fbinfo.yres_virtual; + + fb_addr = mmap(0, map_size, PROT_WRITE | PROT_READ, + MAP_SHARED, fb_fd, 0); + perror_exit(fb_addr == MAP_FAILED, "mmap"); +} + +int main(int argc, char *argv[]) +{ + int ret = 0; + int i; + struct v4l2_buffer buf; + struct v4l2_requestbuffers reqbuf; + enum v4l2_buf_type type; + int last = 0; + + init_usage(argc, argv); + init_fb(); + + srand(time(NULL) ^ getpid()); + sleep(initial_delay); + + init_video_dev(); + + memzero(reqbuf); + reqbuf.count = NUM_BUFS; + reqbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + reqbuf.memory = V4L2_MEMORY_MMAP; + ret = ioctl(vid_fd, VIDIOC_REQBUFS, &reqbuf); + perror_exit(ret != 0, "ioctl"); + num_src_bufs = reqbuf.count; + debug("Got %d src buffers\n", num_src_bufs); + + reqbuf.count = NUM_BUFS; + reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ret = ioctl(vid_fd, VIDIOC_REQBUFS, &reqbuf); + perror_exit(ret != 0, "ioctl"); + num_dst_bufs = reqbuf.count; + debug("Got %d dst buffers\n", num_dst_bufs); + + for (i = 0; i < num_src_bufs; ++i) { + buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = i; + + ret = ioctl(vid_fd, VIDIOC_QUERYBUF, &buf); + perror_exit(ret != 0, "ioctl"); + debug("QUERYBUF returned offset: %x\n", buf.m.offset); + + src_buf_size[i] = buf.length; + p_src_buf[i] = mmap(NULL, buf.length, + PROT_READ | PROT_WRITE, MAP_SHARED, + vid_fd, buf.m.offset); + perror_exit(MAP_FAILED == p_src_buf[i], "mmap"); + } + + for (i = 0; i < num_dst_bufs; ++i) { + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = i; + + ret = ioctl(vid_fd, VIDIOC_QUERYBUF, &buf); + perror_exit(ret != 0, "ioctl"); + debug("QUERYBUF returned offset: %x\n", buf.m.offset); + + dst_buf_size[i] = buf.length; + p_dst_buf[i] = mmap(NULL, buf.length, + PROT_READ | PROT_WRITE, MAP_SHARED, + vid_fd, buf.m.offset); + perror_exit(MAP_FAILED == p_dst_buf[i], "mmap"); + } + + for (i = 0; i < num_src_bufs; ++i) { + + gen_src_buf(p_src_buf[i], src_buf_size[i]); + + memzero(buf); + buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = i; + + ret = ioctl(vid_fd, VIDIOC_QBUF, &buf); + perror_exit(ret != 0, "ioctl"); + } + + for (i = 0; i < num_dst_bufs; ++i) { + memzero(buf); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = i; + + ret = ioctl(vid_fd, VIDIOC_QBUF, &buf); + perror_exit(ret != 0, "ioctl"); + } + + ret = ioctl(vid_fd, VIDIOC_STREAMON, &type); + debug("STREAMON (%d): %d\n", VIDIOC_STREAMON, ret); + perror_exit(ret != 0, "ioctl"); + + while (num_frames) { + fd_set read_fds; + int r; + + FD_ZERO(&read_fds); + FD_SET(vid_fd, &read_fds); + + debug("Before select"); + r = select(vid_fd + 1, &read_fds, NULL, NULL, 0); + perror_exit(r < 0, "select"); + debug("After select"); + + if (num_frames == 1) + last = 1; + if (read_frame(last)) { + fprintf(stderr, "Read frame failed\n"); + break; + } + --num_frames; + printf("FRAMES LEFT: %d\n", num_frames); + } + + +done: + close(vid_fd); + close(fb_fd); + + for (i = 0; i < num_src_bufs; ++i) + munmap(p_src_buf[i], src_buf_size[i]); + + for (i = 0; i < num_dst_bufs; ++i) + munmap(p_dst_buf[i], dst_buf_size[i]); + + return ret; +} + -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html