678 lines
16 KiB
C
678 lines
16 KiB
C
|
/* video grab for linux ... uses the original v4l
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
|
||
|
This file is part of VIPS.
|
||
|
|
||
|
VIPS is free software; you can redistribute it and/or modify
|
||
|
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU Lesser 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
|
||
|
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
|
||
|
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||
|
|
||
|
*/
|
||
|
|
||
|
#ifdef HAVE_CONFIG_H
|
||
|
#include <config.h>
|
||
|
#endif /*HAVE_CONFIG_H*/
|
||
|
#include <vips/intl.h>
|
||
|
|
||
|
#ifdef HAVE_VIDEODEV
|
||
|
|
||
|
/* Lots of debugging output.
|
||
|
#define DEBUG
|
||
|
*/
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#ifdef HAVE_UNISTD_H
|
||
|
#include <unistd.h>
|
||
|
#endif /*HAVE_UNISTD_H*/
|
||
|
#include <errno.h>
|
||
|
#include <assert.h>
|
||
|
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/stat.h>
|
||
|
#include <sys/ioctl.h>
|
||
|
#include <sys/mman.h>
|
||
|
|
||
|
#include <fcntl.h>
|
||
|
#include <linux/types.h>
|
||
|
#include <linux/videodev.h>
|
||
|
|
||
|
#include <vips/vips.h>
|
||
|
|
||
|
#ifdef WITH_DMALLOC
|
||
|
#include <dmalloc.h>
|
||
|
#endif /*WITH_DMALLOC*/
|
||
|
|
||
|
/* Zero freed mem to help catch stray pointers.
|
||
|
*/
|
||
|
#ifdef NDEBUG
|
||
|
#define FREE( S ) { if( S ) { (void) im_free( (char *) (S) ); \
|
||
|
(char *) (S) = NULL; } }
|
||
|
#else
|
||
|
#define FREE( S ) { if( S ) { memset( (char *)(S), 0, sizeof( *(S) ) ); \
|
||
|
(void) im_free( (char *) (S) ); (S) = NULL; } }
|
||
|
#endif /*NDEBUG*/
|
||
|
|
||
|
#define FREEF( F, S ) { if( S ) { (void) F( S ); (S) = NULL; } }
|
||
|
#define FREEFI( F, S ) { if( S ) { (void) F( S ); (S) = 0; } }
|
||
|
#define SETSTR( S, V ) \
|
||
|
{ const char *sst = (V); FREE( S ); (S) = im_strdup( NULL, sst ); }
|
||
|
|
||
|
/* Max channels on a device.
|
||
|
*/
|
||
|
#define IM_MAXCHANNELS (10)
|
||
|
|
||
|
/* Video input sources, e.g. tuner, svideo, composite.
|
||
|
*/
|
||
|
#define TUNER (0)
|
||
|
#define COMPOSITE (1)
|
||
|
#define SVIDEO (2)
|
||
|
|
||
|
typedef struct lgrab {
|
||
|
/* Mmap here, plus file descriptor.
|
||
|
*/
|
||
|
char *device;
|
||
|
char *capture_buffer;
|
||
|
int capture_size;
|
||
|
int fd;
|
||
|
|
||
|
/* Current settings.
|
||
|
*/
|
||
|
int c_channel;
|
||
|
int c_width;
|
||
|
int c_height;
|
||
|
int c_ngrabs;
|
||
|
|
||
|
/* Extract capabilities here.
|
||
|
*/
|
||
|
struct video_capability capability;
|
||
|
struct video_channel channel[IM_MAXCHANNELS];
|
||
|
struct video_window window;
|
||
|
struct video_picture picture;
|
||
|
struct video_mbuf mbuf;
|
||
|
struct video_mmap mmap;
|
||
|
} LGrab;
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
/* Decode various things ... capability bits etc.
|
||
|
*/
|
||
|
typedef struct {
|
||
|
int value;
|
||
|
const char *name;
|
||
|
const char *description;
|
||
|
} Decode;
|
||
|
|
||
|
static const Decode decode_palette[] = {
|
||
|
{ VIDEO_PALETTE_GREY,
|
||
|
"VIDEO_PALETTE_GREY", "Linear greyscale" },
|
||
|
{ VIDEO_PALETTE_HI240,
|
||
|
"VIDEO_PALETTE_HI240", "High 240 cube (BT848)" },
|
||
|
{ VIDEO_PALETTE_RGB565,
|
||
|
"VIDEO_PALETTE_RGB565", "565 16 bit RGB" },
|
||
|
{ VIDEO_PALETTE_RGB24,
|
||
|
"VIDEO_PALETTE_RGB24", "24bit RGB" },
|
||
|
{ VIDEO_PALETTE_RGB32,
|
||
|
"VIDEO_PALETTE_RGB32", "32bit RGB" },
|
||
|
{ VIDEO_PALETTE_RGB555,
|
||
|
"VIDEO_PALETTE_RGB555", "555 15bit RGB" },
|
||
|
{ VIDEO_PALETTE_YUV422,
|
||
|
"VIDEO_PALETTE_YUV422", "YUV422 capture" },
|
||
|
{ VIDEO_PALETTE_YUYV,
|
||
|
"VIDEO_PALETTE_YUYV", "" },
|
||
|
{ VIDEO_PALETTE_UYVY,
|
||
|
"VIDEO_PALETTE_UYVY", "" },
|
||
|
{ VIDEO_PALETTE_YUV420,
|
||
|
"VIDEO_PALETTE_YUV420", "" },
|
||
|
{ VIDEO_PALETTE_YUV411,
|
||
|
"VIDEO_PALETTE_YUV411", "YUV411 capture" },
|
||
|
{ VIDEO_PALETTE_RAW,
|
||
|
"VIDEO_PALETTE_RAW", "RAW capture (BT848)" },
|
||
|
{ VIDEO_PALETTE_YUV422P,
|
||
|
"VIDEO_PALETTE_YUV422P", "YUV 4:2:2 Planar" },
|
||
|
{ VIDEO_PALETTE_YUV411P,
|
||
|
"VIDEO_PALETTE_YUV411P", "YUV 4:1:1 Planar" },
|
||
|
{ VIDEO_PALETTE_YUV420P,
|
||
|
"VIDEO_PALETTE_YUV420P", "YUV 4:2:0 Planar" },
|
||
|
{ VIDEO_PALETTE_YUV410P,
|
||
|
"VIDEO_PALETTE_YUV410P", "YUV 4:1:0 Planar" }
|
||
|
};
|
||
|
|
||
|
static const Decode decode_type[] = {
|
||
|
{ VIDEO_TYPE_TV,
|
||
|
"VIDEO_TYPE_TV", "TV" },
|
||
|
{ VIDEO_TYPE_CAMERA,
|
||
|
"VIDEO_TYPE_CAMERA", "Camera" },
|
||
|
};
|
||
|
|
||
|
static const Decode decode_vtype[] = {
|
||
|
{ VID_TYPE_CAPTURE,
|
||
|
"VID_TYPE_CAPTURE", "Can capture to memory" },
|
||
|
{ VID_TYPE_TUNER,
|
||
|
"VID_TYPE_TUNER", "Has Tuner" },
|
||
|
{ VID_TYPE_TELETEXT,
|
||
|
"VID_TYPE_TELETEXT", "Has Teletext" },
|
||
|
{ VID_TYPE_OVERLAY,
|
||
|
"VID_TYPE_OVERLAY", "Chromakeyed overlay" },
|
||
|
{ VID_TYPE_CLIPPING,
|
||
|
"VID_TYPE_CLIPPING", "Overlay clipping" },
|
||
|
{ VID_TYPE_FRAMERAM,
|
||
|
"VID_TYPE_FRAMERAM", "Overlay overwrites frame buffer memory" },
|
||
|
{ VID_TYPE_SCALES,
|
||
|
"VID_TYPE_SCALES", "Hardware supports image scaling" },
|
||
|
{ VID_TYPE_MONOCHROME,
|
||
|
"VID_TYPE_MONOCHROME", "Image capture is grey scale only" },
|
||
|
{ VID_TYPE_SUBCAPTURE,
|
||
|
"VID_TYPE_SUBCAPTURE", "Capture sub-image" },
|
||
|
{ VID_TYPE_MPEG_DECODER,
|
||
|
"VID_TYPE_MPEG_DECODER", "Can decode MPEG streams" },
|
||
|
{ VID_TYPE_MPEG_ENCODER,
|
||
|
"VID_TYPE_MPEG_ENCODER", "Can encode MPEG streams" },
|
||
|
{ VID_TYPE_MJPEG_DECODER,
|
||
|
"VID_TYPE_MJPEG_DECODER", "Can decode MJPEG streams" },
|
||
|
{ VID_TYPE_MJPEG_ENCODER,
|
||
|
"VID_TYPE_MJPEG_ENCODER", "Can encode MJPEG streams" }
|
||
|
};
|
||
|
|
||
|
static const Decode decode_ctype[] = {
|
||
|
{ VIDEO_VC_TUNER,
|
||
|
"VIDEO_VC_TUNER", "Has tuner" },
|
||
|
{ VIDEO_VC_AUDIO,
|
||
|
"VIDEO_VC_AUDIO", "Has audio" }
|
||
|
};
|
||
|
|
||
|
/* Prettyprint a value.
|
||
|
*/
|
||
|
static void
|
||
|
decode_print( const Decode *decode, int ndecode, int value )
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
for( i = 0; i < ndecode; i++ )
|
||
|
if( decode[i].value == value ) {
|
||
|
printf( "%s, %s",
|
||
|
decode[i].name, decode[i].description );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
printf( "unknown (%p)", value );
|
||
|
}
|
||
|
|
||
|
/* Prettyprint a set of flags.
|
||
|
*/
|
||
|
static void
|
||
|
decode_print_flags( const Decode *decode, int ndecode, int flags )
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
printf( "0x%x ", (unsigned int) flags );
|
||
|
|
||
|
for( i = 0; i < ndecode; i++ )
|
||
|
if( decode[i].value & flags ) {
|
||
|
printf( "[" );
|
||
|
decode_print( decode, ndecode,
|
||
|
decode[i].value & flags );
|
||
|
printf( "] " );
|
||
|
flags &= -1 ^ decode[i].value;
|
||
|
}
|
||
|
|
||
|
if( flags )
|
||
|
printf( "[unknown extra flags 0x%x]", (unsigned int) flags );
|
||
|
}
|
||
|
#endif /*DEBUG*/
|
||
|
|
||
|
static int
|
||
|
lgrab_ioctl( LGrab *lg, int request, void *argp )
|
||
|
{
|
||
|
if( !lg->fd ) {
|
||
|
im_error( "lgrab_ioctl", _( "no file descriptor" ) );
|
||
|
return( -1 );
|
||
|
}
|
||
|
|
||
|
if( ioctl( lg->fd, request, argp ) < 0 ) {
|
||
|
im_error( "lgrab_ioctl", _( "ioctl(0x%x) failed: %s" ),
|
||
|
(unsigned int) request, strerror( errno ) );
|
||
|
return( -1 );
|
||
|
}
|
||
|
|
||
|
return( 0 );
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
lgrab_destroy( LGrab *lg )
|
||
|
{
|
||
|
if( lg->fd != -1 ) {
|
||
|
int zero = 0;
|
||
|
|
||
|
(void) lgrab_ioctl( lg, VIDIOCCAPTURE, &zero );
|
||
|
close( lg->fd );
|
||
|
lg->fd = -1;
|
||
|
}
|
||
|
|
||
|
if( lg->capture_buffer ) {
|
||
|
munmap( lg->capture_buffer, lg->capture_size );
|
||
|
lg->capture_buffer = NULL;
|
||
|
}
|
||
|
|
||
|
FREE( lg->device );
|
||
|
FREE( lg );
|
||
|
}
|
||
|
|
||
|
static LGrab *
|
||
|
lgrab_new( const char *device )
|
||
|
{
|
||
|
LGrab *lg = IM_NEW( NULL, LGrab );
|
||
|
int i;
|
||
|
|
||
|
if( !lg )
|
||
|
return( NULL );
|
||
|
|
||
|
lg->device = NULL;
|
||
|
lg->capture_buffer = NULL;
|
||
|
lg->capture_size = 0;
|
||
|
lg->fd = -1;
|
||
|
|
||
|
lg->c_channel = -1;
|
||
|
lg->c_width = -1;
|
||
|
lg->c_height = -1;
|
||
|
lg->c_ngrabs = 1;
|
||
|
|
||
|
SETSTR( lg->device, device );
|
||
|
if( !lg->device || (lg->fd = open( lg->device, O_RDWR )) == -1 ) {
|
||
|
im_error( "lgrab_new", _( "cannot open video device \"%s\"" ),
|
||
|
lg->device );
|
||
|
lgrab_destroy( lg );
|
||
|
return( NULL );
|
||
|
}
|
||
|
|
||
|
if( lgrab_ioctl( lg, VIDIOCGCAP, &lg->capability ) ) {
|
||
|
im_error( "lgrab_new", _( "cannot get video capability" ) );
|
||
|
lgrab_destroy( lg );
|
||
|
return( NULL );
|
||
|
}
|
||
|
|
||
|
/* Check that it can capture to memory.
|
||
|
*/
|
||
|
if( !(lg->capability.type & VID_TYPE_CAPTURE) ) {
|
||
|
im_error( "lgrab_new", _( "card cannot capture to memory" ) );
|
||
|
lgrab_destroy( lg );
|
||
|
return( NULL );
|
||
|
}
|
||
|
|
||
|
/* Read channel info.
|
||
|
*/
|
||
|
for( i = 0; i < IM_MIN( lg->capability.channels, IM_MAXCHANNELS );
|
||
|
i++ ) {
|
||
|
lg->channel[i].channel = i;
|
||
|
if( lgrab_ioctl( lg, VIDIOCGCHAN, &lg->channel[i] ) ) {
|
||
|
lgrab_destroy( lg );
|
||
|
return( NULL );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Get other props.
|
||
|
*/
|
||
|
if( lgrab_ioctl( lg, VIDIOCGWIN, &lg->window) ||
|
||
|
lgrab_ioctl( lg, VIDIOCGPICT, &lg->picture) ) {
|
||
|
lgrab_destroy( lg );
|
||
|
return( NULL );
|
||
|
}
|
||
|
|
||
|
/* Set 24 bit mode.
|
||
|
*/
|
||
|
lg->picture.depth = 24;
|
||
|
lg->picture.palette = VIDEO_PALETTE_RGB24;
|
||
|
if( lgrab_ioctl( lg, VIDIOCSPICT, &lg->picture ) ) {
|
||
|
lgrab_destroy( lg );
|
||
|
return( NULL );
|
||
|
}
|
||
|
|
||
|
return( lg );
|
||
|
}
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
static void
|
||
|
lgrab_dump_capability( struct video_capability *capability )
|
||
|
{
|
||
|
printf( "capability->name = \"%s\"\n", capability->name );
|
||
|
printf( "capability->channels = %d\n", capability->channels );
|
||
|
printf( "capability->audios = %d\n", capability->audios );
|
||
|
printf( "capability->maxwidth = %d\n", capability->maxwidth );
|
||
|
printf( "capability->maxheight = %d\n", capability->maxheight );
|
||
|
printf( "capability->minwidth = %d\n", capability->maxwidth );
|
||
|
printf( "capability->minheight = %d\n", capability->maxheight );
|
||
|
printf( "capability->type = " );
|
||
|
decode_print_flags( decode_vtype, IM_NUMBER( decode_vtype ),
|
||
|
capability->type );
|
||
|
printf( "\n" );
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
lgrab_dump_channel( struct video_channel *channel )
|
||
|
{
|
||
|
printf( "channel->channel = %d\n", channel->channel );
|
||
|
printf( "channel->name = \"%s\"\n", channel->name );
|
||
|
printf( "channel->tuners = %d\n", channel->tuners );
|
||
|
|
||
|
printf( "channel->flags = " );
|
||
|
decode_print_flags( decode_ctype, IM_NUMBER( decode_ctype ),
|
||
|
channel->flags );
|
||
|
printf( "\n" );
|
||
|
printf( "channel->type = " );
|
||
|
decode_print( decode_type, IM_NUMBER( decode_type ),
|
||
|
channel->type );
|
||
|
printf( "\n" );
|
||
|
printf( "channel->norm = %d\n", channel->norm );
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
lgrab_dump_picture( struct video_picture *picture )
|
||
|
{
|
||
|
printf( "picture->brightness = %d\n", picture->brightness );
|
||
|
printf( "picture->hue = %d\n", picture->hue );
|
||
|
printf( "picture->colour = %d\n", picture->colour );
|
||
|
printf( "picture->contrast = %d\n", picture->contrast );
|
||
|
printf( "picture->whiteness = %d\n", picture->whiteness );
|
||
|
printf( "picture->depth = %d\n", picture->depth );
|
||
|
printf( "picture->palette = " );
|
||
|
decode_print( decode_palette, IM_NUMBER( decode_palette ),
|
||
|
picture->palette );
|
||
|
printf( "\n" );
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
lgrab_dump( LGrab *lg )
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
printf( "lg->device = \"%s\"\n", lg->device );
|
||
|
printf( "lg->capture_buffer = %p\n",
|
||
|
lg->capture_buffer );
|
||
|
printf( "lg->capture_size = 0x%x\n",
|
||
|
(unsigned int) lg->capture_size );
|
||
|
printf( "lg->fd = %d\n", lg->fd );
|
||
|
|
||
|
printf( "lg->c_channel = %d\n", lg->c_channel );
|
||
|
printf( "lg->c_width = %d\n", lg->c_width );
|
||
|
printf( "lg->c_height = %d\n", lg->c_height );
|
||
|
printf( "lg->c_ngrabs = %d\n", lg->c_ngrabs );
|
||
|
|
||
|
lgrab_dump_capability( &lg->capability );
|
||
|
for( i = 0; i < lg->capability.channels; i++ )
|
||
|
lgrab_dump_channel( &lg->channel[i] );
|
||
|
lgrab_dump_picture( &lg->picture );
|
||
|
|
||
|
printf( "mbuf->size = 0x%x\n", (unsigned int) lg->mbuf.size );
|
||
|
printf( "mbuf->frames = %d\n", lg->mbuf.frames );
|
||
|
printf( "mbuf->offsets = " );
|
||
|
for( i = 0; i < lg->mbuf.frames; i++ )
|
||
|
printf( "0x%x ", (unsigned int) lg->mbuf.offsets[i] );
|
||
|
printf( "\n" );
|
||
|
}
|
||
|
#endif /*DEBUG*/
|
||
|
|
||
|
static int
|
||
|
lgrab_set_capture_size( LGrab *lg, int width, int height )
|
||
|
{
|
||
|
lg->c_width = width;
|
||
|
lg->c_height = height;
|
||
|
|
||
|
lg->window.clipcount = 0;
|
||
|
lg->window.flags = 0;
|
||
|
lg->window.x = 0;
|
||
|
lg->window.y = 0;
|
||
|
lg->window.width = width;
|
||
|
lg->window.height = height;
|
||
|
if( lgrab_ioctl( lg, VIDIOCSWIN, &lg->window ) )
|
||
|
return( -1 );
|
||
|
|
||
|
/* Make sure the correct amount of memory is mapped.
|
||
|
*/
|
||
|
if( lgrab_ioctl( lg, VIDIOCGMBUF, &lg->mbuf ) )
|
||
|
return( -1 );
|
||
|
|
||
|
if( lg->capture_buffer ) {
|
||
|
munmap( lg->capture_buffer, lg->capture_size );
|
||
|
lg->capture_buffer = NULL;
|
||
|
}
|
||
|
|
||
|
lg->capture_size = lg->mbuf.size;
|
||
|
if( !(lg->capture_buffer = mmap( 0, lg->capture_size,
|
||
|
PROT_READ | PROT_WRITE, MAP_SHARED, lg->fd, 0 )) ) {
|
||
|
im_error( "lgrab_set_capture_size",
|
||
|
_( "unable to map memory" ) );
|
||
|
return( -1 );
|
||
|
}
|
||
|
|
||
|
return( 0 );
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
lgrab_set_channel( LGrab *lg, int channel )
|
||
|
{
|
||
|
if( channel < 0 || channel >= lg->capability.channels ) {
|
||
|
im_error( "lgrab_set_channel",
|
||
|
_( "channel not between 0 and %d" ),
|
||
|
lg->capability.channels - 1 );
|
||
|
return( -1 );
|
||
|
}
|
||
|
|
||
|
if( lgrab_ioctl( lg, VIDIOCSCHAN, &lg->channel[channel] ) )
|
||
|
return( -1 );
|
||
|
lg->c_channel = channel;
|
||
|
|
||
|
return( 0 );
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
lgrab_set_brightness( LGrab *lg, int brightness )
|
||
|
{
|
||
|
lg->picture.brightness = IM_CLIP( 0, brightness, 65535 );
|
||
|
if( lgrab_ioctl( lg, VIDIOCSPICT, &lg->picture ) )
|
||
|
return( -1 );
|
||
|
|
||
|
return( 0 );
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
lgrab_set_colour( LGrab *lg, int colour )
|
||
|
{
|
||
|
lg->picture.colour = IM_CLIP( 0, colour, 65535 );
|
||
|
if( lgrab_ioctl( lg, VIDIOCSPICT, &lg->picture ) )
|
||
|
return( -1 );
|
||
|
|
||
|
return( 0 );
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
lgrab_set_contrast( LGrab *lg, int contrast )
|
||
|
{
|
||
|
lg->picture.contrast = IM_CLIP( 0, contrast, 65535 );
|
||
|
if( lgrab_ioctl( lg, VIDIOCSPICT, &lg->picture ) )
|
||
|
return( -1 );
|
||
|
|
||
|
return( 0 );
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
lgrab_set_hue( LGrab *lg, int hue )
|
||
|
{
|
||
|
lg->picture.hue = IM_CLIP( 0, hue, 65535 );
|
||
|
if( lgrab_ioctl( lg, VIDIOCSPICT, &lg->picture ) )
|
||
|
return( -1 );
|
||
|
|
||
|
return( 0 );
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
lgrab_set_ngrabs( LGrab *lg, int ngrabs )
|
||
|
{
|
||
|
lg->c_ngrabs = IM_CLIP( 1, ngrabs, 1000 );
|
||
|
|
||
|
return( 0 );
|
||
|
}
|
||
|
|
||
|
/* Grab a single frame.
|
||
|
*/
|
||
|
static int
|
||
|
lgrab_capture1( LGrab *lg )
|
||
|
{
|
||
|
lg->mmap.format = lg->picture.palette;
|
||
|
lg->mmap.frame = 0;
|
||
|
lg->mmap.width = lg->c_width;
|
||
|
lg->mmap.height = lg->c_height;
|
||
|
if( lgrab_ioctl( lg, VIDIOCMCAPTURE, &lg->mmap ) ||
|
||
|
lgrab_ioctl( lg, VIDIOCSYNC, &lg->mmap.frame ) )
|
||
|
return( -1 );
|
||
|
|
||
|
return( 0 );
|
||
|
}
|
||
|
|
||
|
/* Grab and average many frames.
|
||
|
*/
|
||
|
static int
|
||
|
lgrab_capturen( LGrab *lg )
|
||
|
{
|
||
|
if( lg->c_ngrabs == 1 ) {
|
||
|
if( lgrab_capture1( lg ) )
|
||
|
return( -1 );
|
||
|
}
|
||
|
else {
|
||
|
int i, j;
|
||
|
int npx = lg->c_width * lg->c_height * 3;
|
||
|
unsigned int *acc;
|
||
|
|
||
|
if( !(acc = IM_ARRAY( NULL, npx, unsigned int )) )
|
||
|
return( -1 );
|
||
|
memset( acc, 0, npx * sizeof( unsigned int ) );
|
||
|
|
||
|
for( i = 0; i < lg->c_ngrabs; i++ ) {
|
||
|
if( lgrab_capture1( lg ) ) {
|
||
|
FREE( acc );
|
||
|
return( -1 );
|
||
|
}
|
||
|
|
||
|
for( j = 0; j < npx; j++ )
|
||
|
acc[j] += (unsigned char) lg->capture_buffer[j];
|
||
|
}
|
||
|
|
||
|
for( j = 0; j < npx; j++ ) {
|
||
|
int avg = (acc[j] + lg->c_ngrabs / 2) / lg->c_ngrabs;
|
||
|
|
||
|
lg->capture_buffer[j] = IM_CLIP( 0, avg, 255 );
|
||
|
}
|
||
|
|
||
|
FREE( acc );
|
||
|
}
|
||
|
|
||
|
return( 0 );
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
lgrab_capture( LGrab *lg, IMAGE *im )
|
||
|
{
|
||
|
int x, y;
|
||
|
unsigned char *line;
|
||
|
|
||
|
if( lgrab_capturen( lg ) )
|
||
|
return( -1 );
|
||
|
|
||
|
if( im_outcheck( im ) )
|
||
|
return( -1 );
|
||
|
im_initdesc( im, lg->c_width, lg->c_height, 3,
|
||
|
IM_BBITS_BYTE, IM_BANDFMT_UCHAR,
|
||
|
IM_CODING_NONE, IM_TYPE_MULTIBAND, 1.0, 1.0, 0, 0 );
|
||
|
if( im_setupout( im ) )
|
||
|
return( -1 );
|
||
|
if( !(line = IM_ARRAY( im,
|
||
|
IM_IMAGE_SIZEOF_LINE( im ), unsigned char )) )
|
||
|
return( -1 );
|
||
|
|
||
|
for( y = 0; y < lg->c_height; y++ ) {
|
||
|
unsigned char *p = (unsigned char *) lg->capture_buffer +
|
||
|
y * IM_IMAGE_SIZEOF_LINE( im );
|
||
|
unsigned char *q = line;
|
||
|
|
||
|
for( x = 0; x < lg->c_width; x++ ) {
|
||
|
q[0] = p[2];
|
||
|
q[1] = p[1];
|
||
|
q[2] = p[0];
|
||
|
|
||
|
p += 3;
|
||
|
q += 3;
|
||
|
}
|
||
|
|
||
|
if( im_writeline( y, im, line ) )
|
||
|
return( -1 );
|
||
|
}
|
||
|
|
||
|
return( 0 );
|
||
|
}
|
||
|
|
||
|
int
|
||
|
im_video_v4l1( IMAGE *im, const char *device,
|
||
|
int channel, int brightness, int colour, int contrast, int hue,
|
||
|
int ngrabs )
|
||
|
{
|
||
|
LGrab *lg;
|
||
|
|
||
|
if( !(lg = lgrab_new( device )) )
|
||
|
return( -1 );
|
||
|
|
||
|
if( lgrab_set_capture_size( lg,
|
||
|
lg->capability.maxwidth, lg->capability.maxheight ) ||
|
||
|
lgrab_set_channel( lg, channel ) ||
|
||
|
lgrab_set_brightness( lg, brightness ) ||
|
||
|
lgrab_set_colour( lg, colour ) ||
|
||
|
lgrab_set_contrast( lg, contrast ) ||
|
||
|
lgrab_set_hue( lg, hue ) ||
|
||
|
lgrab_set_ngrabs( lg, ngrabs ) ||
|
||
|
lgrab_capture( lg, im ) ) {
|
||
|
lgrab_destroy( lg );
|
||
|
return( -1 );
|
||
|
}
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
printf( "Successful capture with:\n" );
|
||
|
lgrab_dump( lg );
|
||
|
#endif /*DEBUG*/
|
||
|
|
||
|
lgrab_destroy( lg );
|
||
|
|
||
|
return( 0 );
|
||
|
}
|
||
|
|
||
|
#else /*!HAVE_VIDEODEV*/
|
||
|
|
||
|
#include <vips/vips.h>
|
||
|
|
||
|
int
|
||
|
im_video_v4l1( IMAGE *im, const char *device,
|
||
|
int channel, int brightness, int colour, int contrast, int hue,
|
||
|
int ngrabs )
|
||
|
{
|
||
|
im_error( "im_video_v4l1",
|
||
|
_( "compiled without im_video_v4l1 support" ) );
|
||
|
return( -1 );
|
||
|
}
|
||
|
|
||
|
#endif /*HAVE_VIDEODEV*/
|