This is an automatic generated email to let you know that the following patch were queued at the http://git.linuxtv.org/v4l-utils.git tree:
Subject: v4l2-ctl: add output streaming. Author: Hans Verkuil <[email protected]> Date: Wed Aug 8 14:03:53 2012 +0200 Signed-off-by: Hans Verkuil <[email protected]> (cherry picked from commit 21a1dd5e758d115d455145a9175583d09b7788be) Signed-off-by: Gregor Jasny <[email protected]> utils/v4l2-ctl/v4l2-ctl-streaming.cpp | 480 ++++++++++++++++++++++++++++++++- utils/v4l2-ctl/v4l2-ctl.cpp | 4 + utils/v4l2-ctl/v4l2-ctl.h | 4 + 3 files changed, 487 insertions(+), 1 deletions(-) --- http://git.linuxtv.org/v4l-utils.git?a=commitdiff;h=051c8b81025ac4d8bdcac48f363d406f9ffefca4 diff --git a/utils/v4l2-ctl/v4l2-ctl-streaming.cpp b/utils/v4l2-ctl/v4l2-ctl-streaming.cpp index 6612143..9dd868f 100644 --- a/utils/v4l2-ctl/v4l2-ctl-streaming.cpp +++ b/utils/v4l2-ctl/v4l2-ctl-streaming.cpp @@ -23,9 +23,12 @@ static unsigned stream_count; static unsigned stream_skip; +static unsigned stream_pat; static unsigned reqbufs_count = 3; static char *file; +#define NUM_PATTERNS (4) + void streaming_usage(void) { printf("\nVideo Streaming options:\n" @@ -45,6 +48,16 @@ void streaming_usage(void) " --stream-user=<count>\n" " capture video using user pointers [VIDIOC_(D)QBUF]\n" " count: the number of buffers to allocate. The default is 3.\n" + " --stream-from=<file> stream from this file. The default is to generate a pattern.\n" + " If <file> is '-', then the data is read from stdin.\n" + " --stream-pattern=<count>\n" + " choose output pattern. The default is 0.\n" + " --stream-out-mmap=<count>\n" + " output video using mmap() [VIDIOC_(D)QBUF]\n" + " count: the number of buffers to allocate. The default is 3.\n" + " --stream-out-user=<count>\n" + " output video using user pointers [VIDIOC_(D)QBUF]\n" + " count: the number of buffers to allocate. The default is 3.\n" " --list-buffers list all video buffers [VIDIOC_QUERYBUF]\n" " --list-buffers-mplane\n" " list all multi-planar video buffers [VIDIOC_QUERYBUF]\n" @@ -157,13 +170,22 @@ void streaming_cmd(int ch, char *optarg) case OptStreamSkip: stream_skip = strtoul(optarg, 0L, 0); break; + case OptStreamPattern: + stream_pat = strtoul(optarg, 0L, 0); + stream_pat %= NUM_PATTERNS; + break; case OptStreamTo: file = optarg; if (strcmp(file, "-")) options[OptSilent] = true; break; + case OptStreamFrom: + file = optarg; + break; case OptStreamMmap: case OptStreamUser: + case OptStreamOutMmap: + case OptStreamOutUser: if (optarg) { reqbufs_count = strtoul(optarg, 0L, 0); if (reqbufs_count == 0) @@ -173,6 +195,291 @@ void streaming_cmd(int ch, char *optarg) } } +/* Note: the code to create the test patterns is derived from the vivi.c kernel + driver. */ + +/* Bars and Colors should match positions */ + +enum colors { + WHITE, + AMBER, + CYAN, + GREEN, + MAGENTA, + RED, + BLUE, + BLACK, + TEXT_BLACK, +}; + +/* R G B */ +#define COLOR_WHITE {204, 204, 204} +#define COLOR_AMBER {208, 208, 0} +#define COLOR_CYAN { 0, 206, 206} +#define COLOR_GREEN { 0, 239, 0} +#define COLOR_MAGENTA {239, 0, 239} +#define COLOR_RED {205, 0, 0} +#define COLOR_BLUE { 0, 0, 205} +#define COLOR_BLACK { 0, 0, 0} + +struct bar_std { + __u8 bar[9][3]; +}; + +static struct bar_std bars[NUM_PATTERNS] = { + { /* Standard ITU-R color bar sequence */ + { COLOR_WHITE, COLOR_AMBER, COLOR_CYAN, COLOR_GREEN, + COLOR_MAGENTA, COLOR_RED, COLOR_BLUE, COLOR_BLACK, COLOR_BLACK } + }, { + { COLOR_WHITE, COLOR_AMBER, COLOR_BLACK, COLOR_WHITE, + COLOR_AMBER, COLOR_BLACK, COLOR_WHITE, COLOR_AMBER, COLOR_BLACK } + }, { + { COLOR_WHITE, COLOR_CYAN, COLOR_BLACK, COLOR_WHITE, + COLOR_CYAN, COLOR_BLACK, COLOR_WHITE, COLOR_CYAN, COLOR_BLACK } + }, { + { COLOR_WHITE, COLOR_GREEN, COLOR_BLACK, COLOR_WHITE, + COLOR_GREEN, COLOR_BLACK, COLOR_WHITE, COLOR_GREEN, COLOR_BLACK } + }, +}; + +#define TO_Y(r, g, b) \ + (((16829 * r + 33039 * g + 6416 * b + 32768) >> 16) + 16) +/* RGB to V(Cr) Color transform */ +#define TO_V(r, g, b) \ + (((28784 * r - 24103 * g - 4681 * b + 32768) >> 16) + 128) +/* RGB to U(Cb) Color transform */ +#define TO_U(r, g, b) \ + (((-9714 * r - 19070 * g + 28784 * b + 32768) >> 16) + 128) + +static __u8 calc_bars[9][3]; +static int pixel_size; + +/* precalculate color bar values to speed up rendering */ +static bool precalculate_bars(__u32 pixfmt, int pattern) +{ + __u8 r, g, b; + int k, is_yuv; + + pixel_size = 2; + for (k = 0; k < 9; k++) { + r = bars[pattern].bar[k][0]; + g = bars[pattern].bar[k][1]; + b = bars[pattern].bar[k][2]; + is_yuv = 0; + + switch (pixfmt) { + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_VYUY: + is_yuv = 1; + break; + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB565X: + r >>= 3; + g >>= 2; + b >>= 3; + break; + case V4L2_PIX_FMT_RGB555: + case V4L2_PIX_FMT_RGB555X: + r >>= 3; + g >>= 3; + b >>= 3; + break; + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + pixel_size = 3; + break; + case V4L2_PIX_FMT_RGB32: + case V4L2_PIX_FMT_BGR32: + pixel_size = 4; + break; + default: + return false; + } + + if (is_yuv) { + calc_bars[k][0] = TO_Y(r, g, b); /* Luma */ + calc_bars[k][1] = TO_U(r, g, b); /* Cb */ + calc_bars[k][2] = TO_V(r, g, b); /* Cr */ + } else { + calc_bars[k][0] = r; + calc_bars[k][1] = g; + calc_bars[k][2] = b; + } + } + return true; +} + +/* 'odd' is true for pixels 1, 3, 5, etc. and false for pixels 0, 2, 4, etc. */ +static void gen_twopix(__u8 *buf, __u32 pixfmt, int colorpos, bool odd) +{ + __u8 r_y, g_u, b_v; + int color; + + r_y = calc_bars[colorpos][0]; /* R or precalculated Y */ + g_u = calc_bars[colorpos][1]; /* G or precalculated U */ + b_v = calc_bars[colorpos][2]; /* B or precalculated V */ + + for (color = 0; color < pixel_size; color++) { + __u8 *p = buf + color; + + switch (pixfmt) { + case V4L2_PIX_FMT_YUYV: + switch (color) { + case 0: + *p = r_y; + break; + case 1: + *p = odd ? b_v : g_u; + break; + } + break; + case V4L2_PIX_FMT_UYVY: + switch (color) { + case 0: + *p = odd ? b_v : g_u; + break; + case 1: + *p = r_y; + break; + } + break; + case V4L2_PIX_FMT_YVYU: + switch (color) { + case 0: + *p = r_y; + break; + case 1: + *p = odd ? g_u : b_v; + break; + } + break; + case V4L2_PIX_FMT_VYUY: + switch (color) { + case 0: + *p = odd ? g_u : b_v; + break; + case 1: + *p = r_y; + break; + } + break; + case V4L2_PIX_FMT_RGB565: + switch (color) { + case 0: + *p = (g_u << 5) | b_v; + break; + case 1: + *p = (r_y << 3) | (g_u >> 3); + break; + } + break; + case V4L2_PIX_FMT_RGB565X: + switch (color) { + case 0: + *p = (r_y << 3) | (g_u >> 3); + break; + case 1: + *p = (g_u << 5) | b_v; + break; + } + break; + case V4L2_PIX_FMT_RGB555: + switch (color) { + case 0: + *p = (g_u << 5) | b_v; + break; + case 1: + *p = (r_y << 2) | (g_u >> 3); + break; + } + break; + case V4L2_PIX_FMT_RGB555X: + switch (color) { + case 0: + *p = (r_y << 2) | (g_u >> 3); + break; + case 1: + *p = (g_u << 5) | b_v; + break; + } + break; + case V4L2_PIX_FMT_RGB24: + switch (color) { + case 0: + *p = r_y; + break; + case 1: + *p = g_u; + break; + case 2: + *p = b_v; + break; + } + break; + case V4L2_PIX_FMT_BGR24: + switch (color) { + case 0: + *p = b_v; + break; + case 1: + *p = g_u; + break; + case 2: + *p = r_y; + break; + } + break; + case V4L2_PIX_FMT_RGB32: + switch (color) { + case 0: + *p = 0; + break; + case 1: + *p = r_y; + break; + case 2: + *p = g_u; + break; + case 3: + *p = b_v; + break; + } + break; + case V4L2_PIX_FMT_BGR32: + switch (color) { + case 0: + *p = b_v; + break; + case 1: + *p = g_u; + break; + case 2: + *p = r_y; + break; + case 3: + *p = 0; + break; + } + break; + } + } +} + +static void fill_buffer(void *buffer, struct v4l2_pix_format *pix) +{ + for (unsigned y = 0; y < pix->height; y++) { + __u8 *ptr = (__u8 *)buffer + y * pix->bytesperline; + + for (unsigned x = 0; x < pix->width; x++) { + int colorpos = x / (pix->width / 8) % 8; + + gen_twopix(ptr + x * pixel_size, pix->pixelformat, colorpos, x & 1); + } + } +} + void streaming_set(int fd) { if (options[OptStreamMmap] || options[OptStreamUser]) { @@ -331,9 +638,180 @@ void streaming_set(int fd) else free(buffers[i]); } - if (fout) + if (fout && fout != stdout) fclose(fout); } + + if (options[OptStreamOutMmap] || options[OptStreamOutUser]) { + struct v4l2_format fmt; + struct v4l2_requestbuffers reqbufs; + int fd_flags = fcntl(fd, F_GETFL); + bool is_mmap = options[OptStreamOutMmap]; + bool use_poll = options[OptStreamPoll]; + FILE *fin = NULL; + + memset(&fmt, 0, sizeof(fmt)); + fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + doioctl(fd, VIDIOC_G_FMT, &fmt); + + if (!precalculate_bars(fmt.fmt.pix.pixelformat, stream_pat % NUM_PATTERNS)) { + fprintf(stderr, "unsupported pixelformat\n"); + return; + } + + memset(&reqbufs, 0, sizeof(reqbufs)); + reqbufs.count = reqbufs_count; + reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + reqbufs.memory = is_mmap ? V4L2_MEMORY_MMAP : V4L2_MEMORY_USERPTR; + + if (file) { + if (!strcmp(file, "-")) + fin = stdin; + else + fin = fopen(file, "r"); + } + + if (doioctl(fd, VIDIOC_REQBUFS, &reqbufs)) + return; + + void *buffers[reqbufs.count]; + + for (unsigned i = 0; i < reqbufs.count; i++) { + struct v4l2_buffer buf; + + memset(&buf, 0, sizeof(buf)); + buf.type = reqbufs.type; + buf.memory = reqbufs.memory; + buf.index = i; + if (doioctl(fd, VIDIOC_QUERYBUF, &buf)) + return; + if (is_mmap) { + buffers[i] = mmap(NULL, buf.length, + PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset); + + if (buffers[i] == MAP_FAILED) { + fprintf(stderr, "mmap failed\n"); + return; + } + } else { + buffers[i] = calloc(1, buf.length); + buf.m.userptr = (unsigned long)buffers[i]; + } + fill_buffer(buffers[i], &fmt.fmt.pix); + if (doioctl(fd, VIDIOC_QBUF, &buf)) + return; + } + + int type = reqbufs.type; + if (doioctl(fd, VIDIOC_STREAMON, &type)) + return; + + if (use_poll) + fcntl(fd, F_SETFL, fd_flags | O_NONBLOCK); + + unsigned count = 0, last = 0; + struct timeval tv_last; + + for (;;) { + struct v4l2_buffer buf; + int ret; + + if (use_poll) { + fd_set fds; + struct timeval tv; + int r; + + FD_ZERO(&fds); + FD_SET(fd, &fds); + + /* Timeout. */ + tv.tv_sec = 2; + tv.tv_usec = 0; + + r = select(fd + 1, NULL, &fds, NULL, &tv); + + if (r == -1) { + if (EINTR == errno) + continue; + fprintf(stderr, "select error: %s\n", + strerror(errno)); + return; + } + + if (r == 0) { + fprintf(stderr, "select timeout\n"); + return; + } + } + + memset(&buf, 0, sizeof(buf)); + buf.type = reqbufs.type; + buf.memory = reqbufs.memory; + + ret = test_ioctl(fd, VIDIOC_DQBUF, &buf); + if (ret < 0 && errno == EAGAIN) + continue; + if (ret < 0) { + fprintf(stderr, "%s: failed: %s\n", "VIDIOC_DQBUF", strerror(errno)); + return; + } + if (fin && !stream_skip) { + unsigned sz = fread(buffers[buf.index], 1, buf.length, fin); + if (sz != buf.length) + fprintf(stderr, "%u != %u\n", sz, buf.length); + } + if (doioctl(fd, VIDIOC_QBUF, &buf)) + return; + + fprintf(stderr, "."); + fflush(stderr); + + if (count == 0) { + gettimeofday(&tv_last, NULL); + } else { + struct timeval tv_cur, res; + + gettimeofday(&tv_cur, NULL); + timersub(&tv_cur, &tv_last, &res); + if (res.tv_sec) { + unsigned fps = (100 * (count - last)) / + (res.tv_sec * 100 + res.tv_usec / 10000); + last = count; + tv_last = tv_cur; + fprintf(stderr, " %d fps\n", fps); + } + } + count++; + if (stream_skip) { + stream_skip--; + continue; + } + if (stream_count == 0) + continue; + if (--stream_count == 0) + break; + } + doioctl(fd, VIDIOC_STREAMOFF, &type); + fcntl(fd, F_SETFL, fd_flags); + fprintf(stderr, "\n"); + + for (unsigned i = 0; i < reqbufs.count; i++) { + struct v4l2_buffer buf; + + memset(&buf, 0, sizeof(buf)); + buf.type = reqbufs.type; + buf.memory = reqbufs.memory; + buf.index = i; + if (doioctl(fd, VIDIOC_QUERYBUF, &buf)) + return; + if (is_mmap) + munmap(buffers[i], buf.length); + else + free(buffers[i]); + } + if (fin && fin != stdin) + fclose(fin); + } } void streaming_list(int fd) diff --git a/utils/v4l2-ctl/v4l2-ctl.cpp b/utils/v4l2-ctl/v4l2-ctl.cpp index 0a5826f..a1c7b61 100644 --- a/utils/v4l2-ctl/v4l2-ctl.cpp +++ b/utils/v4l2-ctl/v4l2-ctl.cpp @@ -200,6 +200,10 @@ static struct option long_options[] = { {"stream-to", required_argument, 0, OptStreamTo}, {"stream-mmap", optional_argument, 0, OptStreamMmap}, {"stream-user", optional_argument, 0, OptStreamUser}, + {"stream-from", required_argument, 0, OptStreamFrom}, + {"stream-pattern", required_argument, 0, OptStreamPattern}, + {"stream-out-mmap", optional_argument, 0, OptStreamOutMmap}, + {"stream-out-user", optional_argument, 0, OptStreamOutUser}, {0, 0, 0, 0} }; diff --git a/utils/v4l2-ctl/v4l2-ctl.h b/utils/v4l2-ctl/v4l2-ctl.h index c6363bd..013f65f 100644 --- a/utils/v4l2-ctl/v4l2-ctl.h +++ b/utils/v4l2-ctl/v4l2-ctl.h @@ -146,6 +146,10 @@ enum Option { OptStreamTo, OptStreamMmap, OptStreamUser, + OptStreamFrom, + OptStreamPattern, + OptStreamOutMmap, + OptStreamOutUser, OptHelpTuner, OptHelpIO, OptHelpStds, _______________________________________________ linuxtv-commits mailing list [email protected] http://www.linuxtv.org/cgi-bin/mailman/listinfo/linuxtv-commits
