更新一下摄像头采集程序


/* $Id: directshow.c,v 1.22 2005/11/12 14:12:39 fuhuizhong Exp $ */


#ifndef lint
static char vcid[] = "$Id: directshow.c,v 1.22 2005/11/12 14:12:39 fuhuizhong Exp $";
#endif /* lint */


/*
* 作者:傅惠忠
* /modified by xiaoshao_0_0
*/
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <linux/types.h>
#include <linux/videodev.h>
#include "jpeglib.h"
#include <setjmp.h>
#include <SDL/SDL.h>
#include <SDL/SDL_ttf.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
//#define USB_VIDEO "/dev/video0"
#define FONT "./bit.ttf"
char USB_VIDEO[20];//make it changeabel
SDL_Color fontcolor;
SDL_Surface *screen,*img,*text;
int cam_fd;
char mesg[256];
int width,height;
const int bpp = 24;
struct video_mmap cam_mm;
struct video_capability cam_cap;
struct video_picture cam_pic;
struct video_mbuf cam_mbuf;
struct video_window win;
char *cam_data = NULL;
int nframe;


TTF_Font *font=NULL;
void errexit(char *msg)
{
fputs(msg,stderr);
exit(-1);
}


void e_sig(int signum)
{
printf(" ERROR:signal %d ",signum);
exit(-1);
}


void setup_sighandler()
{
signal(SIGTERM,&e_sig);
signal(SIGINT,&e_sig);
signal(SIGSEGV,&e_sig);
signal(SIGILL,&e_sig);
signal(SIGFPE,&e_sig);
}
void free_font();
void init_font();
void init_video(int w,int h,int bpp); /* bpp == bits per pixel, 暂时无效*/
void init_screen(int w,int h,int bpp); /* like above*/
void read_video(int ,int);
void show_img(char *pixels);
void window_loop();
void exchange_r_b( char * f,long size);
void compress_to_jpeg_file( FILE *outfile, char * image_buffer,int w,int h, int quality);
void save_snapshot();
void init_font()
{
if(TTF_Init()==-1)
{
return ;
}
font=TTF_OpenFont(FONT,16);
if(font==NULL)
return ;

SDL_Color c={255,255,255};
fontcolor=c;
return ;
}
void free_font()
{
SDL_FreeSurface(text);
TTF_CloseFont(font);
TTF_Quit();
}
void free_dev()
{
printf("free device/n");
close(cam_fd);
}


int main(int argc,char *argv[])
{
int i;
float scale = 1.0;
if(argc<3||argc>5){
errexit("usage as /"cam -d /dev/video* -s 03~1.0/",please check again");
}

if( argc >= 3 ) {
if(!strcmp(argv[1],"-d"))//devices!!
{
strcpy(USB_VIDEO,argv[2]);
printf("using USB_VIDEO,%s",USB_VIDEO);
}
else
errexit("usage as /"cam -d /dev/video* -s 03~1.0/",please check again");
}
if(argc ==5){
if(!strcmp(argv[3],"-s"))//scale
{
scale = atof(argv[4]);
if(scale<0.3||scale>1.0)
errexit("scale out of range (0.3 ~ 1.0)/n");
}
}

width = (int)(640*scale);
height = (int)(480*scale);

atexit( &free_dev );


init_video(width,height,bpp);
init_screen(width,height,bpp);
init_font();
setup_sighandler();
SDL_WM_SetCaption("Captrue the webcam,by xiaoshao_0_0",NULL);

window_loop();


// getchar();
free_font();
SDL_Quit();
munmap(cam_data,cam_mbuf.size);

exit(0);
}


void config_vid_pic()
{
char *hp = getenv("HOME");
char cfpath[100];
FILE *cf;
int ret;
sprintf( cfpath,"%s/.dshow.conf",hp );
cf = fopen(cfpath,"r");
/* The struct video_picture consists of the following fields


brightness Picture brightness
hue Picture hue (colour only)
colour Picture colour (colour only)
contrast Picture contrast
whiteness The whiteness (greyscale only)
depth The capture depth (may need to match the frame buffer depth)
palette Reports the palette that should be used for this image


The following palettes are defined


VIDEO_PALETTE_GREY Linear intensity grey scale (255 is brightest).
VIDEO_PALETTE_HI240 The BT848 8bit colour cube.
VIDEO_PALETTE_RGB565 RGB565 packed into 16 bit words.
VIDEO_PALETTE_RGB555 RGV555 packed into 16 bit words, top bit undefined.
VIDEO_PALETTE_RGB24 RGB888 packed into 24bit words.
VIDEO_PALETTE_RGB32 RGB888 packed into the low 3 bytes of 32bit words. The top 8bits are undefined.
VIDEO_PALETTE_YUV422 Video style YUV422 - 8bits packed 4bits Y 2bits U 2bits V
VIDEO_PALETTE_YUYV Describe me
VIDEO_PALETTE_UYVY Describe me
VIDEO_PALETTE_YUV420 YUV420 capture
VIDEO_PALETTE_YUV411 YUV411 capture
VIDEO_PALETTE_RAW RAW capture (BT848)
VIDEO_PALETTE_YUV422P YUV 4:2:2 Planar
VIDEO_PALETTE_YUV411P YUV 4:1:1 Planar
*/
if (ioctl(cam_fd, VIDIOCGPICT, &cam_pic) < 0) {
errexit("ERROR:VIDIOCGPICT ");
}
//cam_pic.palette =VIDEO_PALETTE_RAW;
cam_pic.palette =VIDEO_PALETTE_RGB24;


if( cf==NULL ) {
cam_pic.brightness = 44464;
cam_pic.hue = 36000;
cam_pic.colour = 0;
cam_pic.contrast = 43312;
cam_pic.whiteness = 13312;
cam_pic.depth = 24;
ret = ioctl( cam_fd, VIDIOCSPICT,&cam_pic ); /*设置摄像头缓冲中voideo_picture信息*/
if( ret<0 ) {
close(cam_fd);
errexit("ERROR: VIDIOCSPICT,Can't set video_picture format ");
}
return;
}

fscanf(cf,"%d",&cam_pic.brightness);
fscanf(cf,"%d",&cam_pic.hue);
fscanf(cf,"%d",&cam_pic.colour);
fscanf(cf,"%d",&cam_pic.contrast);
fscanf(cf,"%d",&cam_pic.whiteness);
fclose( cf );
ret = ioctl( cam_fd, VIDIOCSPICT,&cam_pic ); /*设置摄像头缓冲中voideo_picture信息*/
if( ret<0 ) {
close(cam_fd);
errexit("ERROR: VIDIOCSPICT,Can't set video_picture format ");
}

}


void init_video(int w,int h,int bpp) /* bpp == bytes per pixel*/
{
int ret;


cam_fd = open( USB_VIDEO, O_RDWR );
if( cam_fd<0 )
errexit("Can't open video device ");


ret = ioctl( cam_fd,VIDIOCGCAP,&cam_cap );
/* 摄像头的基本信息
struct video_capability cam_cap;
name[32] Canonical name for this interface
type Type of interface
channels Number of radio/tv channels if appropriate
audios Number of audio devices if appropriate
maxwidth Maximum capture width in pixels
maxheight Maximum capture height in pixels
minwidth Minimum capture width in pixels
minheight Minimum capture height in pixels
*/
if( ret<0 ) {
errexit("Can't get device information: VIDIOCGCAP ");
}


print_device_info();


/* The struct video_window contains the following fields.


x The X co-ordinate specified in X windows format.
y The Y co-ordinate specified in X windows format.
width The width of the image capture.
height The height of the image capture.
chromakey A host order RGB32 value for the chroma key.
flags Additional capture flags.
clips A list of clipping rectangles. (Set only)
clipcount The number of clipping rectangles. (Set only)
*/
if( ioctl(cam_fd,VIDIOCGWIN,&win)<0 ) {
errexit("ERROR:VIDIOCGWIN ");
}
win.x = 0;
win.y = 0;
win.width=width;
win.height=height;
if (ioctl(cam_fd, VIDIOCSWIN, &win) < 0) {
errexit("ERROR:VIDIOCSWIN ");
}


config_vid_pic();

ret = ioctl(cam_fd,VIDIOCGMBUF,&cam_mbuf);
/*
struct video_mbuf
{
int size; Total memory to map
int frames; Frames
int offsets[VIDEO_MAX_FRAME];
};
*/


/*struct video_buffer.
视频缓存的信息读取结构,设定也是一样的结构。但是一般是X自己设定,你只要读取就好了。
void *base Base physical address of the buffer
int height Height of the frame buffer
int width Width of the frame buffer
int depth Depth of the frame buffer
int bytesperline Number of bytes of memory between the start of two adjacent lines*/


if( ret<0 ) {
errexit("ERROR:VIDIOCGMBUF,Can't get video_mbuf ");
}
printf("/nFrames:%d ",cam_mbuf.frames);
nframe = cam_mbuf.frames;
cam_data = (char*)mmap(0, cam_mbuf.size, PROT_READ|PROT_WRITE,MAP_SHARED,cam_fd,0);
if( cam_data == MAP_FAILED ) {
errexit("ERROR:mmap ");
}
printf("Buffer size:%d Offset:%d/n ",cam_mbuf.size,cam_mbuf.offsets[0]);
}


void print_device_info()
{
int type=cam_cap.type;
int i;
char type_info[14][100]=
{
"VID_TYPE_CAPTURE Can capture to memory",
"VID_TYPE_TUNER Has a tuner of some form",
"VID_TYPE_TELETEXT Has teletext capability",
"VID_TYPE_OVERLAY Can overlay its image onto the frame buffer",
"VID_TYPE_CHROMAKEY Overlay is Chromakeyed",
"VID_TYPE_CLIPPING Overlay clipping is supported",
"VID_TYPE_FRAMERAM Overlay overwrites frame buffer memory",
"VID_TYPE_SCALES The hardware supports image scaling",
"VID_TYPE_MONOCHROME Image capture is grey scale only",
"VID_TYPE_SUBCAPTURE Capture can be of only part of the image",
"VID_TYPE_MPEG_DECODER Can decode MPEG streams",
"VID_TYPE_MPEG_ENCODER Can encode MPEG streams",
"VID_TYPE_MJPEG_DECODER Can decode MJPEG streams",
"VID_TYPE_MJPEG_ENCODER Can encode MJPEG streams",
};
printf("/nDevice name:%s Width:%d ~ %d Height:%d ~ %d /n",
cam_cap.name,
cam_cap.maxwidth, cam_cap.minwidth,
cam_cap.maxheight, cam_cap.minheight);
for(i=0;i<14;i++)
{
if(type&(2^i))
{ printf(type_info[i]);
printf(" ");
}
else
{
printf("/n==not supported==%s",type_info[i]);
printf("/n");
}
}
}


void init_screen(int w,int h,int bpp) // like above
{
if ( SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO) < 0 ) {
fprintf(stderr, "无法初始化SDL: %s ", SDL_GetError());
exit(1);
}
screen = SDL_SetVideoMode(width, height, bpp, SDL_HWSURFACE);
if ( screen == NULL ) {
fprintf(stderr, "无法设置视频模式:%s ", SDL_GetError());
exit(1);
}
img = SDL_CreateRGBSurface(SDL_HWSURFACE, width, height, bpp, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000);
//这里改成SDL_HWSURFACE可能会快一点吧


atexit(SDL_Quit);
}


void read_video(int captrue_frame,int sync_frame)
{


/*
It is allowed to call VIDIOCMCAPTURE multiple times (with different frame numbers in video_mmap->frame of course) and thus have multiple outstanding capture requests. A simple way do to double-buffering using this feature looks like this:


* setup everything
VIDIOCMCAPTURE(0)
while (whatever) {
VIDIOCMCAPTURE(1)
VIDIOCSYNC(0)
// process frame 0 while the hardware captures frame 1
VIDIOCMCAPTURE(0)
VIDIOCSYNC(1)
// process frame 1 while the hardware captures frame 0
}
*/
int ret;
//int frame=0; /*这个似乎比较关键*/
//这个地方原作者弄错了。
cam_mm.frame=captrue_frame;
/* struct video_mmap
{
unsigned int frame; Frame (0 - n) for double buffer
int height,width;
unsigned int format; should be VIDEO_PALETTE_*
};
*/
ret = ioctl(cam_fd,VIDIOCMCAPTURE,&cam_mm);
if( ret<0 ) {
errexit("ERROR: VIDIOCMCAPTURE ");
}
//cam_mm.frame=sync_frame;
ret = ioctl(cam_fd,VIDIOCSYNC,&sync_frame);
if( ret<0 ) {
errexit("ERROR: VIDIOCSYNC ");
}
}


void show_img(char *pixels)
{
int row_stride = width*3;
char *pbuf = (char*)img->pixels;
int row;


/* for(row=0; row<height; row++) {
memcpy(pbuf, pixels, row_stride);
pbuf += img->pitch;
pixels += row_stride;
}*/
memcpy(pbuf,pixels,row_stride*height);
SDL_BlitSurface(img, NULL, screen, NULL);
text = TTF_RenderText_Solid(font, mesg, fontcolor);
SDL_BlitSurface(text,NULL,screen,NULL);
SDL_FreeSurface(text);
// SDL_UpdateRect(screen,0,0,width,height);
SDL_Flip(screen);
}


void window_loop()
{
int ret;
int frames=0;
int loop;
SDL_Event event;
int keystat = 0;
Uint32 ticks = 0;
SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,1000);
//////////////
cam_mm.height = height;
cam_mm.width = width;
cam_mm.frame=0;
cam_mm.format=VIDEO_PALETTE_RGB24;
ret = ioctl(cam_fd,VIDIOCMCAPTURE,&cam_mm);
//capture 0 first here!!
if( ret<0 ) {
errexit("ERROR: VIDIOCMCAPTURE ");
}
//Here just start caputer frame0,it not in the loop.
//////////////
while ( loop ) {
SDL_PollEvent(&event);
switch (event.type) {
case SDL_QUIT:
loop=0;
break;
// exit(0);
case SDL_KEYDOWN:
if( event.key.keysym.sym == SDLK_F8 && keystat == 0 )
save_snapshot();
if( event.key.keysym.sym == SDLK_F9 && keystat == 0 )
config_vid_pic();
if( event.key.keysym.sym == SDLK_UP && keystat == 0 )
increase_brightness();
if( event.key.keysym.sym == SDLK_DOWN && keystat == 0 )
decrease_birghtness();
if( event.key.keysym.sym == SDLK_LEFT && keystat== 0 )
increase_contrast();
if( event.key.keysym.sym == SDLK_RIGHT && keystat==0 )
decrease_contrast();
keystat = 1;
break;
case SDL_KEYUP:
keystat = 0;
break;
default:
break;
}
//usleep(1);
if( (SDL_GetTicks()-ticks)<1000/24)
continue;
read_video(1,0);//captrue=1,sync=0
show_img( cam_data+cam_mbuf.offsets[0]);
//handle frame 0 here and wait for 1
read_video(0,1);//captrue=0,sync=1
// usleep(1000/24);
show_img( cam_data+cam_mbuf.offsets[1]);
//handle frame 1 here and wait for 0
ticks = SDL_GetTicks();
frames+=2;
}
printf("/n ticks=%d,frames=%d",ticks,frames);
}
void increase_brightness()
{
int ret=0;
cam_pic.brightness+=1000;
sprintf(mesg,"brightness=:%d",cam_pic.brightness);
ret = ioctl( cam_fd, VIDIOCSPICT,&cam_pic ); /*设置摄像头缓冲中voideo_picture信息*/
if( ret<0 ) {
close(cam_fd);
errexit("ERROR: VIDIOCSPICT,Can't set video_picture format ");
}
}
void decrease_birghtness()
{
int ret=0;
cam_pic.brightness-=1000;
sprintf(mesg,"brightness=:%d",cam_pic.brightness);
ret = ioctl( cam_fd, VIDIOCSPICT,&cam_pic ); /*设置摄像头缓冲中voideo_picture信息*/
if( ret<0 ) {
close(cam_fd);
errexit("ERROR: VIDIOCSPICT,Can't set video_picture format ");
}
}
void increase_contrast()
{
int ret=0;
cam_pic.contrast+=1000;
sprintf(mesg,"contrast=:%d",cam_pic.contrast);
ret = ioctl( cam_fd, VIDIOCSPICT,&cam_pic ); /*设置摄像头缓冲中voideo_picture信息*/


if( ret<0 ) {
close(cam_fd);
errexit("ERROR: VIDIOCSPICT,Can't set video_picture format ");
}
}
void decrease_contrast()
{
int ret=0;
cam_pic.contrast-=1000;
sprintf(mesg,"contrast=:%d",cam_pic.contrast);
ret = ioctl( cam_fd, VIDIOCSPICT,&cam_pic ); /*设置摄像头缓冲中voideo_picture信息*/
if( ret<0 ) {
close(cam_fd);
errexit("ERROR: VIDIOCSPICT,Can't set video_picture format ");
}
}


void compress_to_jpeg_file( FILE *outfile, char * image_buffer,int w,int h, int quality)
{
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
int row_stride; /* physical row width in image buffer */
int image_width;
int image_height;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);
jpeg_stdio_dest(&cinfo, outfile);


image_width = w;
image_height = h;
cinfo.image_width = image_width; /* image width and height, in pixels */
cinfo.image_height = image_height;
cinfo.input_components = 3; /* # of color components per pixel */
cinfo.in_color_space = JCS_RGB; /* colorspace of input image */
jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
jpeg_start_compress(&cinfo, TRUE);
row_stride = image_width * 3; /* JSAMPLEs per row in image_buffer */
while (cinfo.next_scanline < cinfo.image_height) {
row_pointer[0] = (JSAMPROW)& image_buffer[cinfo.next_scanline * row_stride];
(void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
}


jpeg_finish_compress(&cinfo);


jpeg_destroy_compress(&cinfo);


/* And we're done! */
}
void exchange_r_b( char * f,long size)
{
char r,b;
long i;
for( i = 0; i < size ; i++)
{
r = *f;
b = *( f + 2);
*f = b;
*(f + 2) = r;
f = f +3;
}


}


void save_snapshot()
{
char *basepath = getenv("HOME");
char *basename = "/snapsot";
char *extname = ".jpg";
char filename[100];
int i = 0;
FILE *fo;
for(;;) {
sprintf(filename,"%s%s%d%s",basepath,basename,i,extname);
fo = fopen(filename,"rb");
if( fo==NULL )
break;
else
fclose(fo);
i += 1;
}
printf("snapshot:%s ",filename);
fo = fopen(filename,"wb");
exchange_r_b( cam_data, width*height );
compress_to_jpeg_file( fo, cam_data, width, height, 90 );
fclose(fo);
}

这次的更新,需要一个字体文件配合,英文和数字即可,名字我是叫做,bit.ttf.随便搞个字体就行。
采用了HWSURFACE,SDL_Flip提高了FPS大概到12左右。
提供了在屏幕上显示参数的功能。利用了SDL_ttf来显示屏幕上的信息。
另外,编译的命令请参考:
gcc old.c -o cam -lSDL -ljpeg -lSDL_ttf
不爱用makefile的人说。呵呵。


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/xiaoshao_0_0/archive/2008/11/09/3261119.aspx

你可能感兴趣的:(程序)