On 06/09/2014 03:51 PM, Thiago Santos wrote:
> Adds options to allow the buffer dqbuf to happen on one thread while
> the qbuf happens on another. This is useful to test concurrency access to
> the v4l2 features. To enable this, 3 new options were added:
>
> t: enable threaded mode (off by default and will use the loop)
> b: enable blocking io mode (off by default
> s: how much the consumer thread will sleep after reading a buffer, this is to
> simulate the time that it takes to process a buffer in a real application
> (in ms)
>
> For example, you can simulate an application that takes 1s to process a buffer
> with:
>
> v4l2grab -t -b -s 1000
Is there a reason why you want to use v4l2grab instead of v4l2-ctl? My guess is
that is was just easier to add it there. I'm not so keen about this, I'd much
rather see this being added to v4l2-ctl, ideally for both capture, output and
m2m devices.
I'll commit the second patch since HdG Acked it.
Regards,
Hans
>
> Signed-off-by: Thiago Santos <[email protected]>
> ---
> contrib/test/Makefile.am | 2 +-
> contrib/test/v4l2grab.c | 261
> +++++++++++++++++++++++++++++++++++++++--------
> 2 files changed, 219 insertions(+), 44 deletions(-)
>
> diff --git a/contrib/test/Makefile.am b/contrib/test/Makefile.am
> index 80c7665..c2e3860 100644
> --- a/contrib/test/Makefile.am
> +++ b/contrib/test/Makefile.am
> @@ -25,7 +25,7 @@ pixfmt_test_CFLAGS = $(X11_CFLAGS)
> pixfmt_test_LDFLAGS = $(X11_LIBS)
>
> v4l2grab_SOURCES = v4l2grab.c
> -v4l2grab_LDADD = ../../lib/libv4l2/libv4l2.la
> ../../lib/libv4lconvert/libv4lconvert.la
> +v4l2grab_LDADD = ../../lib/libv4l2/libv4l2.la
> ../../lib/libv4lconvert/libv4lconvert.la -lpthread
>
> v4l2gl_SOURCES = v4l2gl.c
> v4l2gl_LDFLAGS = $(X11_LIBS) $(GL_LIBS) $(GLU_LIBS)
> diff --git a/contrib/test/v4l2grab.c b/contrib/test/v4l2grab.c
> index 674cbe7..3e1be3d 100644
> --- a/contrib/test/v4l2grab.c
> +++ b/contrib/test/v4l2grab.c
> @@ -24,8 +24,10 @@
> #include <linux/videodev2.h>
> #include "../../lib/include/libv4l2.h"
> #include <argp.h>
> +#include <pthread.h>
>
> -#define CLEAR(x) memset(&(x), 0, sizeof(x))
> +#define CLEAR_P(x,s) memset((x), 0, s)
> +#define CLEAR(x) CLEAR_P(&(x), sizeof(x))
>
> struct buffer {
> void *start;
> @@ -46,22 +48,206 @@ static void xioctl(int fh, unsigned long int request,
> void *arg)
> }
> }
>
> +/* Used by the multi thread capture version */
> +struct buffer_queue {
> + struct v4l2_buffer *buffers;
> + int buffers_size;
> +
> + int read_pos;
> + int write_pos;
> + int n_frames;
> +
> + int fd;
> +
> + pthread_mutex_t mutex;
> + pthread_cond_t buffer_cond;
> +};
> +
> +/* Gets a buffer and puts it in the buffers list at write position, then
> + * notifies the consumer that a new buffer is ready to be used */
> +static void *produce_buffer (void * p)
> +{
> + struct buffer_queue *bq;
> + fd_set fds;
> + struct timeval tv;
> + int i;
> + struct v4l2_buffer *buf;
> + int r;
> +
> + bq = p;
> +
> + for (i = 0; i < bq->n_frames; i++) {
> + printf ("Prod: %d\n", i);
> + buf = &bq->buffers[bq->write_pos % bq->buffers_size];
> + do {
> + FD_ZERO(&fds);
> + FD_SET(bq->fd, &fds);
> +
> + /* Timeout. */
> + tv.tv_sec = 2;
> + tv.tv_usec = 0;
> +
> + r = select(bq->fd + 1, &fds, NULL, NULL, &tv);
> + } while ((r == -1 && (errno == EINTR)));
> + if (r == -1) {
> + perror("select");
> + pthread_exit (NULL);
> + return NULL;
> + }
> +
> + CLEAR_P(buf, sizeof(struct v4l2_buffer));
> + buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> + buf->memory = V4L2_MEMORY_MMAP;
> + xioctl(bq->fd, VIDIOC_DQBUF, buf);
> +
> + pthread_mutex_lock (&bq->mutex);
> + bq->write_pos++;
> + printf ("Prod: %d (done)\n", i);
> + pthread_cond_signal (&bq->buffer_cond);
> + pthread_mutex_unlock (&bq->mutex);
> +
> + }
> +
> + pthread_exit (NULL);
> +}
> +
> +/* will create a separate thread that will produce the buffers and put
> + * into a circular array while this same thread will get the buffers from
> + * this array and 'render' them */
> +static int capture_threads (int fd, struct buffer *buffers, int bufpool_size,
> + struct v4l2_format fmt, int n_frames,
> + char *out_dir, int sleep_ms)
> +{
> + struct v4l2_buffer buf;
> + unsigned int i;
> + struct buffer_queue buf_queue;
> + pthread_t producer;
> + char out_name[25 + strlen(out_dir)];
> + FILE *fout;
> + struct timespec sleeptime;
> +
> + if (sleep_ms) {
> + sleeptime.tv_sec = sleep_ms / 1000;
> + sleeptime.tv_nsec = (sleep_ms % 1000) * 1000000;
> + }
> +
> + buf_queue.buffers_size = bufpool_size * 2;
> + buf_queue.buffers = calloc(buf_queue.buffers_size,
> + sizeof(struct v4l2_buffer));
> + buf_queue.fd = fd;
> + buf_queue.read_pos = 0;
> + buf_queue.write_pos = 0;
> + buf_queue.n_frames = n_frames;
> + pthread_mutex_init (&buf_queue.mutex, NULL);
> + pthread_cond_init (&buf_queue.buffer_cond, NULL);
> +
> + pthread_create (&producer, NULL, produce_buffer, &buf_queue);
> +
> + for (i = 0; i < n_frames; i++) {
> + printf ("Read: %d\n", i);
> +
> + /* wait for a buffer to be available in the queue */
> + pthread_mutex_lock (&buf_queue.mutex);
> + while (buf_queue.read_pos == buf_queue.write_pos) {
> + pthread_cond_wait (&buf_queue.buffer_cond,
> + &buf_queue.mutex);
> + }
> + pthread_mutex_unlock (&buf_queue.mutex);
> +
> + if (sleep_ms)
> + nanosleep (&sleeptime, NULL);
> +
> + sprintf(out_name, "%s/out%03d.ppm", out_dir, i);
> + fout = fopen(out_name, "w");
> + if (!fout) {
> + perror("Cannot open image");
> + exit(EXIT_FAILURE);
> + }
> + fprintf(fout, "P6\n%d %d 255\n",
> + fmt.fmt.pix.width, fmt.fmt.pix.height);
> + buf = buf_queue.buffers[buf_queue.read_pos %
> + buf_queue.buffers_size];
> + fwrite(buffers[buf.index].start, buf.bytesused, 1, fout);
> + fclose(fout);
> +
> + xioctl(fd, VIDIOC_QBUF, &buf);
> +
> + pthread_mutex_lock (&buf_queue.mutex);
> + buf_queue.read_pos++;
> + printf ("Read: %d (done)\n", i);
> + pthread_cond_signal (&buf_queue.buffer_cond);
> + pthread_mutex_unlock (&buf_queue.mutex);
> + }
> +
> + pthread_mutex_destroy (&buf_queue.mutex);
> + pthread_cond_destroy (&buf_queue.buffer_cond);
> + free (buf_queue.buffers);
> + return 0;
> +}
> +
> +static int capture_loop (int fd, struct buffer *buffers, struct v4l2_format
> fmt,
> + int n_frames, char *out_dir)
> +{
> + struct v4l2_buffer buf;
> + unsigned int i;
> + struct timeval tv;
> + int r;
> + fd_set fds;
> + FILE *fout;
> + char out_name[25 + strlen(out_dir)];
> +
> + for (i = 0; i < n_frames; i++) {
> + do {
> + FD_ZERO(&fds);
> + FD_SET(fd, &fds);
> +
> + /* Timeout. */
> + tv.tv_sec = 2;
> + tv.tv_usec = 0;
> +
> + r = select(fd + 1, &fds, NULL, NULL, &tv);
> + } while ((r == -1 && (errno == EINTR)));
> + if (r == -1) {
> + perror("select");
> + return errno;
> + }
> +
> + CLEAR(buf);
> + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> + buf.memory = V4L2_MEMORY_MMAP;
> + xioctl(fd, VIDIOC_DQBUF, &buf);
> +
> + sprintf(out_name, "%s/out%03d.ppm", out_dir, i);
> + fout = fopen(out_name, "w");
> + if (!fout) {
> + perror("Cannot open image");
> + exit(EXIT_FAILURE);
> + }
> + fprintf(fout, "P6\n%d %d 255\n",
> + fmt.fmt.pix.width, fmt.fmt.pix.height);
> + fwrite(buffers[buf.index].start, buf.bytesused, 1, fout);
> + fclose(fout);
> +
> + xioctl(fd, VIDIOC_QBUF, &buf);
> + }
> + return 0;
> +}
> +
> static int capture(char *dev_name, int x_res, int y_res, int n_frames,
> - char *out_dir)
> + char *out_dir, int block, int threads, int sleep_ms)
> {
> struct v4l2_format fmt;
> struct v4l2_buffer buf;
> struct v4l2_requestbuffers req;
> enum v4l2_buf_type type;
> - fd_set fds;
> - struct timeval tv;
> - int r, fd = -1;
> + int fd = -1;
> unsigned int i, n_buffers;
> - char out_name[25 + strlen(out_dir)];
> - FILE *fout;
> struct buffer *buffers;
>
> - fd = v4l2_open(dev_name, O_RDWR | O_NONBLOCK, 0);
> + if (block)
> + fd = v4l2_open(dev_name, O_RDWR, 0);
> + else
> + fd = v4l2_open(dev_name, O_RDWR | O_NONBLOCK, 0);
> if (fd < 0) {
> perror("Cannot open device");
> exit(EXIT_FAILURE);
> @@ -119,40 +305,11 @@ static int capture(char *dev_name, int x_res, int
> y_res, int n_frames,
> type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>
> xioctl(fd, VIDIOC_STREAMON, &type);
> - for (i = 0; i < n_frames; i++) {
> - do {
> - FD_ZERO(&fds);
> - FD_SET(fd, &fds);
> -
> - /* Timeout. */
> - tv.tv_sec = 2;
> - tv.tv_usec = 0;
> -
> - r = select(fd + 1, &fds, NULL, NULL, &tv);
> - } while ((r == -1 && (errno == EINTR)));
> - if (r == -1) {
> - perror("select");
> - return errno;
> - }
> -
> - CLEAR(buf);
> - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> - buf.memory = V4L2_MEMORY_MMAP;
> - xioctl(fd, VIDIOC_DQBUF, &buf);
> -
> - sprintf(out_name, "%s/out%03d.ppm", out_dir, i);
> - fout = fopen(out_name, "w");
> - if (!fout) {
> - perror("Cannot open image");
> - exit(EXIT_FAILURE);
> - }
> - fprintf(fout, "P6\n%d %d 255\n",
> - fmt.fmt.pix.width, fmt.fmt.pix.height);
> - fwrite(buffers[buf.index].start, buf.bytesused, 1, fout);
> - fclose(fout);
> -
> - xioctl(fd, VIDIOC_QBUF, &buf);
> - }
> + if (threads)
> + capture_threads(fd, buffers, 2, fmt, n_frames, out_dir,
> + sleep_ms);
> + else
> + capture_loop(fd, buffers, fmt, n_frames, out_dir);
>
> type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> xioctl(fd, VIDIOC_STREAMOFF, &type);
> @@ -179,6 +336,9 @@ static const struct argp_option options[] = {
> {"xres", 'x', "XRES", 0, "horizontal
> resolution", 0},
> {"yres", 'y', "YRES", 0, "vertical resolution",
> 0},
> {"n-frames", 'n', "NFRAMES", 0, "number of frames to
> capture", 0},
> + {"thread-enable", 't', "THREADS", 0, "if different threads
> should capture and save", 0},
> + {"blockmode-enable", 'b', "BLOCKMODE", 0, "if blocking mode
> should be used", 0},
> + {"sleep-time", 's', "SLEEP", 0, "how long should the
> consumer thread sleep to simulate the processing of a buffer (in ms)"},
> { 0, 0, 0, 0, 0, 0 }
> };
>
> @@ -188,6 +348,9 @@ static char *out_dir = ".";
> static int x_res = 640;
> static int y_res = 480;
> static int n_frames = 20;
> +static int threads = 0;
> +static int block = 0;
> +static int sleep_ms = 0;
>
> static error_t parse_opt(int k, char *arg, struct argp_state *state)
> {
> @@ -215,6 +378,17 @@ static error_t parse_opt(int k, char *arg, struct
> argp_state *state)
> if (val)
> n_frames = val;
> break;
> + case 't':
> + threads = 1;
> + break;
> + case 'b':
> + block = 1;
> + break;
> + case 's':
> + val = atoi(arg);
> + if (val)
> + sleep_ms = val;
> + break;
> default:
> return ARGP_ERR_UNKNOWN;
> }
> @@ -232,5 +406,6 @@ int main(int argc, char **argv)
> {
> argp_parse(&argp, argc, argv, 0, 0, 0);
>
> - return capture(dev_name, x_res, y_res, n_frames, out_dir);
> + return capture(dev_name, x_res, y_res, n_frames, out_dir, block,
> + threads, sleep_ms);
> }
>
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html