By Date: <-- -->
By Thread: <-- -->

Capture image from frame grabber in C



Just as you know, I'm not proud of this code :-)
It's a little messy, but it should be useful. I just compiled it with not
problem, both with gui and without.

I needed it to test a Win-TV USB card, so some options are actually not
needed with simple webcams.

Salvo

On 2/2/07, Salvatore Benedetto <emitrax (at) gmail.com> wrote:

I remember it worked ok for me. I'll have a look and post the code later.

On 2/2/07, Jean-Claude Repetto < jean-claude.repetto (at) worldonline.fr>
wrote:
>
> Salvatore Benedetto a écrit :
> > If you look in the mailing list you should find a frame grabber I
> wrote
> > couple of weeks ago.
> >
>
> Hi Salvatore,
>
> Have you updated your program since the version you posted on Jan. 2 ?
> Your program doesn't work for me (several problems), perhaps you have
> fixed them now.
>
> Jean-Claude
>
> --
> video4linux-list mailing list
> Unsubscribe mailto:video4linux-list-request (at) redhat.com
> ?subject=unsubscribe
> https://www.redhat.com/mailman/listinfo/video4linux-list
>



--
Salvatore Benedetto (a.k.a. emitrax)
Student of Computer Engineering and Telecommunications
University of Messina (Italy)
www.messinalug.org

No to global warming!
http://www.climatecrisis.net/
http://www.stopglobalwarming.org/

Siti di vera informazione Italiana
www.beppegrillo.it
www.marcotravaglio.it
www.danieleluttazzi.it
www.antoniodipietro.it

Please do not send me any word, excel or power point file
http://www.gnu.org/philosophy/no-word-attachments.html




-- Salvatore Benedetto (a.k.a. emitrax) Student of Computer Engineering and Telecommunications University of Messina (Italy) www.messinalug.org

No to global warming!
http://www.climatecrisis.net/
http://www.stopglobalwarming.org/

Siti di vera informazione Italiana
www.beppegrillo.it
www.marcotravaglio.it
www.danieleluttazzi.it
www.antoniodipietro.it

Please do not send me any word, excel or power point file
http://www.gnu.org/philosophy/no-word-attachments.html
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/ioctl.h>
#include<fcntl.h>
#include<linux/videodev.h>
#include<linux/videodev2.h>
#include<sys/mman.h>
#include<assert.h>

#ifdef GUI
	#include<gtk/gtk.h>
#endif

#include<jpeglib.h>

struct v4l2_capability	vcap;
struct v4l2_requestbuffers reqbuf;

struct 
{
	void *start;
	size_t length;
} *buffers;

#define OPTIONS	"hx:y:D:p:j:c:n:v:d:q"

static int		width = 320;
static int		height = 240;
static int		depth = 3;
static int		palette = V4L2_PIX_FMT_BGR24;
static v4l2_std_id	video_standard = V4L2_STD_PAL; 
static char		*video_std = "PAL";
static int		jpeg_quality = 80;
static int		channel = 0;
static int		n_frames = 3;
static char 		*filename;

void usage( char **argv);

int query_device( int video_fd) ;
int get_input_channel( int video_fd );
int set_input_channel( int video_fd );
int get_supported_video_standard( int video_fd ); // Same as above
int get_current_video_standard( int video_fd ); // I should pass the index of the input channel
int set_video_standard( int video_fd );
int set_capture_buffer(int video_fd);
int set_picture(int video_fd);
void process_image( void *frame );
void main_loop(int video_fd);
int read_frame(int fd);

#ifdef GUI
GtkWidget *window;
GtkWidget *darea;

void init_gui(void);
void on_darea_expose(GtkWidget *);
void display_frame( void *frame);
#endif


int create_jpeg( char *fileout, unsigned char *img, int lx, int ly, int lw);

int main( int argc, char **argv )
{
	int video_fd,i;
	int opt;
	int qflag = 0;
	char *file_device = "/dev/video";
	
	v4l2_std_id std_id ;

	while( ( opt = getopt( argc, argv, OPTIONS )) != EOF )
	{
		switch(opt)
		{
			case 'x':
				width = atoi(optarg);
				break;
			case 'y':
				height = atoi(optarg);
				break;
			case 'D':
				depth = atoi(optarg);
				break;
			case 'p':
				palette = atoi(optarg);
				break;
			case 'j':
				jpeg_quality = atoi(optarg);
				break;
			case 'c':
				channel = atoi(optarg);
				break;
			case 'n':
				n_frames = atoi(optarg);
				break;
			case 'd':
				file_device = optarg;
				break;
			case 'v':
				if( !strcmp(optarg, "PAL"))
					video_standard = V4L2_STD_PAL;
				else if( !strcmp( optarg, "NTSC"))
					video_standard = V4L2_STD_NTSC;
				else if( !strcmp( optarg, "SECAM"))
					video_standard = V4L2_STD_SECAM;
				else 
					fprintf(stderr,"%s is not supported. Using default video standard\n",optarg);
				break;
			case 'q':
				qflag = 1;
				break;
			case '?':
				fprintf (stderr, "Unknown option `-%c'.\n", optopt);
				return 1;
			case 'h':
			default : 
				usage( argv );
		}
	}
	
	if( (video_fd = open( file_device, O_RDWR ) ) < 1 )
	{
		perror( file_device );
		exit(EXIT_FAILURE);
	}

	if(qflag)
	{
		query_device( video_fd );
		return 0;
	}

#ifdef GUI
	g_thread_init(NULL);
	gtk_init( &argc, &argv );
	gdk_init( &argc, &argv );
#endif

	set_input_channel( video_fd );
	set_video_standard( video_fd );
	
	set_picture(video_fd);
	set_capture_buffer(video_fd);
	
	/*
	get_input_channel( video_fd) ;
	get_current_video_standard( video_fd );
	*/

#ifdef GUI
	init_gui();
#endif
	
	main_loop(video_fd);

#ifdef GUI
	gtk_main_quit();
#endif

	/* Cleanup. */
	for( i = 0; i < reqbuf.count ; i++ )
		munmap (buffers[i].start, buffers[i].length);

	free(filename);

	return 0;
}

void main_loop( int fd )
{
	int buf_index = 0;
	int stop = 1;

	while( stop )
	{
		fd_set fds;
		struct timeval tv;
		int r;

		FD_ZERO (&fds);
		FD_SET (fd, &fds);

		/* Timeout. */
		tv.tv_sec = 3;
		tv.tv_usec = 0;

		r = select (fd + 1, &fds, NULL, NULL, &tv);

		if (-1 == r) {
			if (EINTR == errno)
				continue;

			perror("select");
		}

		if (0 == r) {
			fprintf (stderr, "select timeout\n");
			exit (EXIT_FAILURE);
		}

		if (read_frame(fd))
			break;

	}
}

int read_frame(int fd)
{
        struct v4l2_buffer buf;
	unsigned int i;

	memset( &buf, 0, sizeof(buf));
	buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	buf.memory = V4L2_MEMORY_MMAP;
	buf.index = 0;

	if (-1 == ioctl (fd, VIDIOC_DQBUF, &buf)) {
		switch (errno) {
		case EAGAIN:
			return 0;

		default:
			perror("VIDIOC_DQBUF");
		}
	}

	process_image(buffers[buf.index].start);

	if (-1 == ioctl (fd, VIDIOC_QBUF, &buf))
		perror("VIDIOC_QBUF");

	return 0;
}

int set_picture(video_fd)
{
	struct v4l2_format fmt;

	memset( &fmt, 0, sizeof(fmt) );

	fmt.type		= V4L2_BUF_TYPE_VIDEO_CAPTURE;
	fmt.fmt.pix.width	= width;
	fmt.fmt.pix.height	= height;
	fmt.fmt.pix.pixelformat = palette;

	if( ( ioctl( video_fd, VIDIOC_S_FMT, &fmt )) == 1 )
	{
		perror("VIDIOC_S_MT");
		exit(1);
	}

	return 0;
}

int set_video_standard( int video_fd )
{
	struct v4l2_input input;
	
	memset (&input, 0, sizeof (input));
	
	if (-1 == ioctl (video_fd, VIDIOC_G_INPUT, &input.index)) {
		perror ("VIDIOC_G_INPUT");
		exit (EXIT_FAILURE);
	}
	
	if (-1 == ioctl (video_fd, VIDIOC_ENUMINPUT, &input)) {
		perror ("VIDIOC_ENUM_INPUT");
		exit (EXIT_FAILURE);
	}
	
	/*
	if (0 == (input.std & video_standard)) {
		fprintf (stderr, "Oops. This video standard is not supported.\n");
		exit (EXIT_FAILURE);
	}
	*/

	if (-1 == ioctl (video_fd, VIDIOC_S_STD, &video_standard)) {
		perror ("VIDIOC_S_STD");
		exit (EXIT_FAILURE);
	}
}

int set_input_channel( int video_fd )
{
	if( ( ioctl( video_fd, VIDIOC_S_INPUT, &channel )) != 0 )
	{
		perror( "VIDIOC_S_INPUT" );
		exit(EXIT_FAILURE);
	}

	return 0;
}

int get_current_video_standard( int video_fd )
{
	v4l2_std_id std_id;
	struct v4l2_standard standard;
	
	if (  (ioctl (video_fd, VIDIOC_G_STD, &std_id)) != 0 )
	{
		/* Note when VIDIOC_ENUMSTD always returns EINVAL this
		 * is no video device or it falls under the USB exception,
		 * and VIDIOC_G_STD returning EINVAL is no error. */
		perror ("Warning: VIDIOC_G_STD");
	}

	memset (&standard, 0, sizeof (standard));
	standard.index = 0;
	
	while ( (ioctl (video_fd, VIDIOC_ENUMSTD, &standard)) == 0 )
	{
		if (standard.id & std_id) 
		{
			printf ("\nCurrent video standard: %s\n", standard.name);
			return 0;
		}
		standard.index++;
	}
	
	/* EINVAL indicates the end of the enumeration, which cannot be
	 * empty unless this device falls under the USB exception. */

	if (errno == EINVAL || standard.index == 0) 
	{
		perror("VIDIOC_ENUMSTD");
		exit(EXIT_FAILURE);
	}

}
	

int query_device( int video_fd )
{
	if( ( ioctl( video_fd, VIDIOC_QUERYCAP, &vcap )) != 0 )
	{
		perror( "VIDIOC_QUERYCAP" );
		exit(EXIT_FAILURE);
	}

	printf( "\nDriver name : %s" \
		"\nCard name   : %s" \
		"\nBus Info    : %s" \
		"\nCapabilities:"
		, vcap.driver, vcap.card, vcap.bus_info );
	
	if( vcap.capabilities & V4L2_CAP_VIDEO_CAPTURE ) 
		printf(" V4L2_CAP_VIDEO_CAPTURE ");

	if( vcap.capabilities & V4L2_CAP_VIDEO_OUTPUT )
		printf(" V4L2_CAP_VIDEO_OUTPUT ");

	if( vcap.capabilities & V4L2_CAP_VIDEO_OVERLAY )
		printf(" V4L2_CAP_VIDEO_OVERLAY ");

	if( vcap.capabilities & V4L2_CAP_VBI_CAPTURE )
		printf(" V4L2_CAP_VBI_CAPTURE ");

	if( vcap.capabilities & V4L2_CAP_VBI_OUTPUT )
		printf(" V4L2_CAP_VBI_OUTPUT ");

	if( vcap.capabilities & V4L2_CAP_SLICED_VBI_CAPTURE )
		printf(" V4L2_CAP_SLICED_VBI_CAPTURE ");

	if( vcap.capabilities & V4L2_CAP_SLICED_VBI_OUTPUT )
		printf(" V4L2_CAP_SLICED_VBI_OUTPUT ");
	
	if( vcap.capabilities & V4L2_CAP_RDS_CAPTURE )
		printf(" V4L2_CAP_RDS_CAPTURE " );
	
	if( vcap.capabilities & V4L2_CAP_TUNER )
		printf(" V4L2_CAP_TUNER ");

	if( vcap.capabilities & V4L2_CAP_AUDIO )
		printf(" V4L2_CAP_AUDIO ");
	
	if( vcap.capabilities & V4L2_CAP_RADIO )
		printf(" V4L2_CAP_RADIO ");

	if( vcap.capabilities & V4L2_CAP_READWRITE )
		printf(" V4L2_CAP_READWRITE ");

	if( vcap.capabilities & V4L2_CAP_ASYNCIO )
		printf(" V4L2_CAP_ASYNCIO ");

	if( vcap.capabilities & V4L2_CAP_STREAMING )
		printf(" V4L2_CAP_STREAMING ");

	printf("\n\n");

	get_input_channel( video_fd );
	get_supported_video_standard( video_fd );
	
	return 0;
}

int get_input_channel( int video_fd )
{
	struct v4l2_input	vinput;

	if (-1 == ioctl (video_fd, VIDIOC_G_INPUT, &channel)) 
	{
		perror ("VIDIOC_G_INPUT");
		exit (EXIT_FAILURE);
	}

	memset (&vinput, 0, sizeof (vinput));
	vinput.index = channel;

	if (-1 == ioctl (video_fd, VIDIOC_ENUMINPUT, &vinput)) 
	{
		perror ("VIDIOC_ENUMINPUT");
		exit (EXIT_FAILURE);
	}

	printf ("Current input name:   %s\n" \
		"Current input number: %d\n" \
		, vinput.name, vinput.index);

	return 0;
}


/************************************************
 * 	Display all supported video standard	*
 * 	for the current input video channel	*
 * *********************************************/
int get_supported_video_standard( int video_fd )
{
	struct v4l2_standard standard;
	struct v4l2_input input;
	
	memset (&input, 0, sizeof (input));
	
	/* Get current input channel */
	if( (ioctl (video_fd, VIDIOC_G_INPUT, &input.index)) != 0 )
	{
		perror("VIDIOC_G_INPUT");
		exit(EXIT_FAILURE);
	}

	if( (ioctl (video_fd, VIDIOC_ENUMINPUT, &input)) != 0 )
	{
		perror("VIDIOC_ENUM_INPUT");
		exit(EXIT_FAILURE);
	}

	printf("Supported standard: ");
	
	memset (&standard, 0, sizeof (standard));
	standard.index = 0;
	
	while ( (ioctl (video_fd, VIDIOC_ENUMSTD, &standard)) == 0)
	{
		if (standard.id & input.std)
			printf (" %s ", standard.name);
		standard.index++;
	}

	putc('\n', stdout);
	
	/* EINVAL indicates the end of the enumeration, which cannot be
	 * empty unless this device falls under the USB exception. */
	if (errno != EINVAL || standard.index == 0) 
	{
		perror ("VIDIOC_ENUMSTD");
		exit (EXIT_FAILURE);
	}
}


int set_capture_buffer( int fd )
{
	unsigned int i = 0;
	enum v4l2_buf_type type;
	int requested = 0;

	memset (&reqbuf, 0, sizeof (reqbuf));
	reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	reqbuf.memory = V4L2_MEMORY_MMAP;
	reqbuf.count = 3;

	if (-1 == ioctl (fd, VIDIOC_REQBUFS, &reqbuf)) {
		if (errno == EINVAL)
			printf ("Video capturing or mmap-streaming is not supported\n");
		else
			perror ("VIDIOC_REQBUFS");
		exit (EXIT_FAILURE);
	}
	
	/* We want at least five buffers. */
/*	if (reqbuf.count < 3) {
		printf ("Not enough buffer memory\n");
		exit (EXIT_FAILURE);
	}
*/
	if( !(buffers = calloc (reqbuf.count, sizeof (*buffers)) ))
	{
		perror("Allocating buffer ");
		exit(1);
	}

	/* Can't map more than one buffer on the fox */
	for( i = 0; i < reqbuf.count ; i++ )
	{
		struct v4l2_buffer buffer;
		memset (&buffer, 0, sizeof (buffer));
		buffer.type = reqbuf.type;
		buffer.memory = V4L2_MEMORY_MMAP;
		buffer.index = i;

		if (-1 == ioctl (fd, VIDIOC_QUERYBUF, &buffer))
		{
			perror ("VIDIOC_QUERYBUF");
			exit (EXIT_FAILURE);
		}
		
		buffers[i].length = buffer.length; /* remember for munmap() */
		//printf("Buffer %d lenght : %d\n", i, buffer.length);
		
		buffers[i].start = mmap (NULL, buffer.length,
				PROT_READ | PROT_WRITE, /* required */
				MAP_SHARED, /* recommended */
				fd, buffer.m.offset);


		if (buffers[i].start == MAP_FAILED) 
		{	
			if( requested == 0 )
			{
				perror ("mmap");
				exit (EXIT_FAILURE);
			}
			else
			{
				printf("Could allocate only %d buffers\n",requested);
				break;	//get out of the loop
			}
		}
		else
		{
			printf("Buffer %d mapped\n",i);
			++requested;

			if( (ioctl(fd, VIDIOC_QBUF, &buffer)) != 0 )
			{
				perror("VIDIOC_QBUF");
				exit(EXIT_FAILURE);
			}
			else
			{	
				printf("Buffer %d queued\n", i);
			}
		}
	}

	type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

	ioctl(fd, VIDIOC_STREAMON, &type);

	return 0;
}

void process_image( void *frame )
{
	static int n = 0; //To substitute with time()
	
#ifdef GUI	
	display_frame( frame );
#endif

	//TO FIX:Useful fuction in order to redirect the frame to a file
	//fwrite( (unsigned char *) frame , buffers[0].length, 1, stdout );
	
	filename = (char *)malloc( sizeof(char) * 20 );
	sprintf( filename, "frame%d.jpg", n++ );
	create_jpeg( filename,  (unsigned char *) frame, 320, 240, 3 );
	printf("%s saved\n",filename);
	fflush(stdout);
	free(filename);

	// Not the best way :-)
	if( n >= n_frames )
		exit(0);

}

/************************************************
 * 	Jpeg function from videodog-0.7.	*
 * 	Sorry I'm too lazy to read the libjpeg  *
 * 	documentation. :-)			*
 * *********************************************/
int create_jpeg (char *fileout, unsigned char *img, int lx, int ly, int lw) {  // records a file w/ compressed jpeg
	
	FILE *fp;
	unsigned char *line;  // pointer to line
	unsigned int linesize = lx * lw, i;
	struct jpeg_compress_struct cinfo;
        struct jpeg_error_mgr jerr;

	
	if ( (fp=fopen(fileout , "w+")) == NULL) {
        	perror ("fopen");
		return -1;
	}
	
	cinfo.err = jpeg_std_error(&jerr);
	jpeg_create_compress(&cinfo);
        jpeg_stdio_dest(&cinfo, fp);
        cinfo.image_width = lx;
        cinfo.image_height = ly;

	cinfo.input_components = lw;
	if (lw == 1)  cinfo.in_color_space = JCS_GRAYSCALE;
	else cinfo.in_color_space = JCS_RGB;

	jpeg_set_defaults(&cinfo);
	jpeg_set_quality(&cinfo, jpeg_quality, TRUE);
	jpeg_start_compress(&cinfo, TRUE);
	
	line=img;

	for (i = 1; i <= ly; i++) {
		jpeg_write_scanlines(&cinfo, &line, 1);
		line=img + (linesize * i);
		}
	
	jpeg_finish_compress(&(cinfo));
	jpeg_destroy_compress(&(cinfo));

	fclose (fp);
	
			
}

/********************************
 *	GTK+ functions		*
 *******************************/
#ifdef GUI
void display_frame( void *frame)
{
	static int first = 1;
	
	if( first )
	{
		gtk_signal_connect (GTK_OBJECT (darea), "expose-event",	
			GTK_SIGNAL_FUNC (on_darea_expose), (gpointer) darea);
		
		first = 0;
	}

	gtk_drawing_area_size (GTK_DRAWING_AREA (darea), 320 , 240);
	
	gtk_widget_show_all( window );	
	
	gdk_threads_enter();
	gtk_main();
	gdk_threads_leave();
}

void init_gui( )
{
	/* Setting up main window */
	window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
	gtk_window_set_title( GTK_WINDOW(window), "v4l application" );
	g_signal_connect( G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
	gtk_container_set_border_width (GTK_CONTAINER (window), 10);

	/* Drawing area */
	darea = gtk_drawing_area_new();
	gtk_container_add( GTK_CONTAINER(window), darea);
}

void on_darea_expose( GtkWidget *widget )
{
	gdk_draw_rgb_image (widget->window, widget->style->fg_gc[GTK_STATE_NORMAL],
			0, 0, 320, 240,
			GDK_RGB_DITHER_MAX, buffers[0].start, 320*3);
	
	gdk_threads_enter();
	gtk_main_quit();
	gdk_threads_leave();

}
#endif


/********************************
 * 	Last but not least	*
 *******************************/
void usage( char **argv)
{
	printf(	"\n"						\
		"Usage: %s [options ...] /dev/videoX \n\n"	\
		"Options:\n"					\
		"-h		print this help\n"		\
		"-d		file device [/dev/video]\n"	\
		"-x		width [320]\n"			\
		"-y		height [240]\n"			\
		"-D		depth [Not implemented Yet]\n"	\
		"-p		Palette [RGB24]\n"		\
		"-v		Video Standard [PAL]\n"		\
		"-j		jpeg quality [75]\n"		\
		"-c		set channel [0]\n"		\
		"-n		number of frames [1]\n"		\
		"-q		query device and exit\n\n\n"	\
		"Example:\n"					\
		"	%s -q -d /dev/video0\n"			\
		"	%s -x 320 -y 240 -v SECAM -d /dev/video\n"\
		"	%s -n 3 -f frame -j 90 -d /dev/video\n\n"\
		, argv[0], argv[0], argv[0], argv[0] );
		
	exit(1); 
} 

Attachment: Makefile
Description: Binary data

--
video4linux-list mailing list
Unsubscribe mailto:video4linux-list-request (at) redhat.com?subject=unsubscribe
https://www.redhat.com/mailman/listinfo/video4linux-list