Re: [PATCH/RFC v2 1/2] v4l2grab: Add threaded producer/consumer option

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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 <ts.santos@xxxxxxxxxxxxxxxx>
> ---
>  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 majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux