On Sun, 2008-03-09 at 16:28 -0400, Andy Walls wrote:
> Hans Verkuil wrote:
> > On Sunday 09 March 2008 05:37:04 Andy Walls wrote:
> > > Hans Verkuil wrote:
> > > > > Here's my observation:  There was no good reason for q_full to
> > > > > stay empty for so long (> 5 seconds), just because data was
> > > > > sitting queued in q_io.  It was as if data stopped being moved
> > > > > from the encoder to buffers and into q_full.  What is both
> > > > > fortunate and unfortunate is that draining q_full and q_io,
> > seems
> > > > > to restart the transfers from the encoder.


> > > Argh.  I'm giving up on hunting this down for now.  Here are my
> > > observations and speculations with the return value of
> > > cx18_v4l2_enc_poll() does not depend on q_io.length:
> > >
> > > a) The most buffers I've ever seen in use in the driver are 1 in
> > q_io
> > > and 10 in q_full.  That was when then encoder decided to give the
> > > driver 10 buffers all at once.


> > > b) I can always get the transfer from the encoder to stall.
> > >
> > > c) stopping the capture, reloading all the MDLs and restarting the
> > > capture doesn't make transfers from the encoder work again.
> > >
> > > d) a common sequence of failure looked like the following, with the
> > > encoder sending a small (2048 bytes) buffer, the driver returning 2
> > > MDLs very close together, and then the encoder sending only one more
> > > buffer:
> > 
> > ...
> > 
> > >
> > > I changed the cx18_read() function to exit the loop and return once
> > > it had returned 1 MDL to the encoder, but the problem still
> > > persisted.  In this case the short buffer in at least one trial was
> > > 4096 bytes.
> > >
> > >
> > 
> > Is it possible to reproduce it by writing a small capture program
> > that 
> > acts similar to MythTV? If I had a program like this, then I could 
> > investigate.
> 


> Yes, if I have time, I can write a program to emulate the critical
> aspects of MythTV's operation.  You will have to back out the fix to
> cx18_v4l2_enc_poll() to purposely induce the stall.  Also tuning to a
> weak, snowy channel (but not all snow) and changing channels will induce
> the problem more rapidly.  I assume the encoder can't compress as well
> under these circumstances, so it sends more data to the host.

Hans,

Attached is the test program for which you asked.  With it, you should
be able to reproduce select() timeouts with cx18_v4l2_enc_poll()
modified to not watch q_io.  So you can at least get the encoder
transfers to stall for 5 seconds.

I have not been able to get the test program to stall the encoder
transfers long term yet.  I'll have to make it more MythTV-like, I
guess, by calling some of the ioctls that MythTV does.

-Andy


/*
 * pollcx18 - exercise the cx18 driver in a manner similar to MythTV
 * Copyright (C) 2008 Andy Walls
 *
 * 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.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Author: Andy Walls <[EMAIL PROTECTED]>
 *
 * Compile: gcc -Wall -O2 -o pollcx18 pollcx18.c
 *
 * Invoke:  ./pollcx18 -d /dev/video1 -o foo.mpg
 *
 * To see the cx23418 encoder stall out for a while and a select() timeout,
 * cx18_v4l2_enc_poll() should be modified not to check q_io, and you should
 * tune to and away from a weak, snowy TV station (but not one that's all snow)
 */

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/select.h>
#include <errno.h>

struct parsed_args
{
	char *devpath;
	char *outfile;
};

static int quit = 0;

void sig_handler (int signum, siginfo_t *info, void *ucontext)
{
	quit = 1;
}

int parse_args (int argc, char **argv, struct parsed_args *args)
{
	int c, retval;

	args->devpath = "/dev/video";
	args->outfile = NULL;
	retval = 0;
	while ((c = getopt(argc, argv, "d:o:")) != -1)
	{
		switch (c)
		{
			case 'd':
				args->devpath = optarg;
				break;
			case 'o':
				args->outfile = optarg;
				break;
			default:
				fprintf (stderr, "Usage:\npollcx18 [-d videodev] [-o outfile]\n");
				retval = -1;
				break;
		}
	}
	return retval;
}

void mainloop (int *readfd, int outfd, int *quit, char *devpath)
{
	const int bufsize = 4 * 1024; /* 4 kB is what MythTV uses */
	char buffer[bufsize];
	int bytesused, n;

	int nfds;
	struct timeval timeout;
	fd_set rfds;

	bytesused = 0;
	while (!(*quit))
	{
		/* 
 		 * Attempt to reopen the capture device like MythTV, since
 		 * we may close it later in this loop on timeout, like MythTV
 		 *
 		 * We reopen it without O_NONBLOCK, as does MythTV,
 		 * but that's really a MythTV bug.  We should really reopen with
 		 * O_NONBLOCK set.
 		 *
 		 */
		if (*readfd < 0 &&
		    (*readfd = open(devpath, O_RDWR /*|O_NONBLOCK*/)) < 0)
		{
			perror("open");
			fprintf(stderr,
			        "Unable to re-open video capture device: %s\n",
		                devpath);
			*quit = 1;
			break;
		}

		FD_ZERO(&rfds);
		FD_SET(*readfd, &rfds);
		timeout.tv_sec  = 5;  /* MythTV uses 5 seconds */
		timeout.tv_usec = 0;
		nfds = *readfd + 1;

		nfds = select (nfds, &rfds, NULL, NULL, &timeout);

		switch (nfds)
		{
			case -1:
				if (errno == EINTR)
					continue;

				perror("select");
				fprintf(stderr, "select failed on video "
				        "capture device: %s\n",
		                        devpath);
				*quit = 1;
				continue;

			case 0:
				fprintf(stderr, "select timeout on video "
				        "capture device: %s\n",
		                        devpath);
				/*
 				 * MythTV tries to fix things by closing and
 				 * reopening the readfd
 				 */
				close(*readfd);
				*readfd = -1;
				continue;

			default:
				break;
		}

		n = read(*readfd, &(buffer[bytesused]), bufsize-bytesused);
		if (n == -1 && errno != EAGAIN && errno != EINTR)
		{
			perror("read");
			fprintf(stderr,
			        "read failed on video capture device: %s\n",
		                devpath);
			*quit = 1;
		}
		if (n > 0)
			bytesused += n;
		if (bytesused >= bufsize)
		{
			if (outfd > -1)
				write(outfd, buffer, bufsize);
			bytesused = 0;
		}
	}

	/* write out the final partially filled buffer */
	if (bytesused > 0 && outfd > -1)
		write(outfd, buffer, bytesused);

	return;
}

int main (int argc, char **argv)
{
	int chanfd, readfd, outfd;
	struct parsed_args args;
	struct sigaction sigact;

	if (parse_args(argc, argv, &args) < 0)
		exit(1);
	
	outfd = -1;
	if (args.outfile != NULL && 
	    (outfd = open(args.outfile, O_WRONLY|O_CREAT|O_TRUNC, 0660)) < 0)
	{
		perror("open");
		fprintf(stderr, "Unable to open output file: %s\n",
		        args.outfile);
	}

	/* MythTV would use this fd for controlling the capture device */
	if ((chanfd = open(args.devpath, O_RDWR)) < 0)
	{
		perror("open");
		fprintf(stderr, "Unable to open video control device: %s\n",
		        args.devpath);
	}

	/* MythTV would use this fd for video capture */
	if ((readfd = open(args.devpath, O_RDWR|O_NONBLOCK)) < 0)
	{
		perror("open");
		fprintf(stderr, "Unable to open video capture device: %s\n",
		        args.devpath);
		exit(2);
	}

	sigact.sa_flags = SA_SIGINFO;
	sigact.sa_sigaction = sig_handler;
	sigemptyset(&(sigact.sa_mask));
	sigaction(SIGINT, &sigact, NULL);  /* handle ^C to do a graceful exit */

	quit = 0;
	mainloop(&readfd, outfd, &quit, args.devpath);

	close(readfd);
	close(chanfd);
	close(outfd);

	exit(0);
}
_______________________________________________
ivtv-devel mailing list
[email protected]
http://ivtvdriver.org/mailman/listinfo/ivtv-devel

Reply via email to