| By Date: | <-- --> |
| By Thread: | <-- --> |
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
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