Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2262620
  • 博文数量: 668
  • 博客积分: 10016
  • 博客等级: 上将
  • 技术积分: 8588
  • 用 户 组: 普通用户
  • 注册时间: 2008-05-29 19:22
文章分类

全部博文(668)

文章存档

2011年(1)

2010年(2)

2009年(273)

2008年(392)

分类:

2009-08-04 15:00:41

用一系列的ioctl发命令控制设备。v4l支持的ioctl命令大概有二十几个,为了尽快的编出一个
简单的图象捕捉程序,让我们先来看看几个主要的命令:

1. ioctl(fd,VIDIOCGCAP,&cap);
   该命令主要是为了获取电视卡的功能信息。例如电视卡的名称,类型,channel等。参数cap是一个结构,当ioctl命令返回时,结构的各成员就被赋值了,结构体的定义为:
struct video_capability
{
char name[32];
int type;
int channels; /* Num channels */
int audios; /* Num audio devices */
int maxwidth; /* Supported width */
int maxheight; /* And height */
int minwidth; /* Supported width */
int minheight; /* And height */
};
channel 指的是有几个信号输入源,例如television,composite,s-video等。

2.ioctl(fd,VIDIOCGCHAN,&vc)
3.ioctl(fd,VIDIOCSCHAN.&vc)
这两个命令用来取得和设置电视卡的channel信息,例如使用那个输入源,制式等。
vc 是一个video_channel结构,其定义为:
struct video_capability
{
char name[32];
int type;
int channels; /* Num channels */
int audios; /* Num audio devices */
int maxwidth; /* Supported width */
int maxheight; /* And height */
int minwidth; /* Supported width */
int minheight; /* And height */
};


struct video_channel
{
int channel;
char name[32];
int tuners;//number of tuners for this input
__u32   flags;
__u16   type; 
__u16 norm; 
};
成员channel代表输入源,通常,0: television 1:composite1 2:s-video
name 表示该输入源的名称。
norm 表示制式,通常,0:pal 1:ntsc 2:secam 3:auto

4. ioctl(fd,VIDIOCGMBUF,*mbuf)
获得电视卡缓存的信息,参数mbuf是video_mbuf结构。其定义如下:
struct video_mbuf
{
int size; /* Total memory to map */
int frames; /* Frames */
int offsets[VIDEO_MAX_FRAME];
};
size是缓存的大小,frames表明该电视卡的缓存可以容纳的帧数,数组offsets则表明
对应一帧的起始位置,0帧对应offsets[0],1帧对应offsets[1]....
执行完该命令后,就可以用mmap函数将缓存映射到内存中了。大致用法可以参考以下的代

struct video_mbuf mbuf;
unsigned char *buf1,*buf2;

if(ioctl(fd,VIDIOCGMBUF,&mbuf)<0)
{
perror("VIDIOCGMBUF");
return -1;
}

printf("the frame number is %d\n",mbuf.frames);

buf1 = (unsigned char*)mmap(0,mbuf.size,PROT_READ|PROT_WRITE,MAP_SHARED,fd.0);

buf1 = buf1 + mbuf.offset[0];
buf2 = buf1 + mbuf.offset[1];//当然,如果mbuf.frames=1,就不需要下面的了。
......

5. ioctl(fd.VIDIOCMCAPTURE,&mm)
   启动硬件去捕捉图象,mm 是video_mmap 结构,设置捕捉图象需要设置的信息。结构体
如下定义:

struct video_mmap
{
unsigned int frame; /* Frame (0 - n) for double buffer */
int height,width;
unsigned int format; /* should be VIDEO_PALETTE_* */
};
frame :设置当前是第几帧
height,width:设置图象的高和宽。
format :颜色模式

要注意的是,该命令是非阻塞的,也就是说,它仅仅设置了硬件,而不负责是否捕捉到图象。
要确定是否捕捉到图象,要用到下一个命令。

6. ioctl(fd,VIDIOCSYNC,&frame)
等待捕捉到这一帧图象。frame 是要等待的图象,它的值应和上一个命令中设置的frame相对应。

好了,说了这么多,读者大概也对视频捕捉有了一个了解,是不是想亲自动手试一下,那就让我们
开始实际程序的编写吧。
下面我们会编一个程序,将捕捉到的图象存为jpeg文件。为此,还要向大家介绍一个函数,

int write_jpeg(char *filename,unsigned char *buf,int quality,int width, int height, int gray)
{
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
FILE *fp;
int i;
unsigned char *line;
int line_length;

if (NULL == (fp = fopen(filename,"w"))) 
{
fprintf(stderr,"grab: can't open %s: %s\n",filename,strerror(errno));
return -1;
}
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);
jpeg_stdio_dest(&cinfo, fp);
cinfo.image_width   = width;
cinfo.image_height = height;
cinfo.input_components = gray ? 1: 3;
cinfo.in_color_space = gray ? JCS_GRAYSCALE: JCS_RGB;
jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, quality, TRUE);
jpeg_start_compress(&cinfo, TRUE);

line_length = gray ? width : width * 3;
for (i = 0, line = buf; i < height; i++, line += line_length)
jpeg_write_scanlines(&cinfo, &line, 1);

jpeg_finish_compress(&(cinfo));
jpeg_destroy_compress(&(cinfo));
fclose(fp);

return 0;
}

这个函数很通用,它的作用是把buf中的数据压缩成jpeg格式。

/*       下面是一个完整的程序 test.c
*           gcc test.c -o test -ljpeg
*/

#include
#include
#include
#include
#include
#include
#include
#include
#include
   
#include

#define WIDTH   320
#define HEIGHT 240
#define V4L_DEVICE "/dev/video0"

main()
{

unsigned char* buf;
int i,j;
int fd;
int re;

struct video_capability vcap;
struct video_channel vc;
struct video_mbuf    mbuf;
struct video_mmap    mm;

fd = open(V4L_DEVICE, O_RDWR);
if(fd<=0)
{
   perror("open");
     exit(1);
}

if(ioctl(fd, VIDIOCGCAP, &vcap)<0)
{
     perror("VIDIOCGCAP");
     exit(1);
}

fprintf(stderr,"Video Capture Device Name : %s\n",vcap.name);

for(i=0;i{
     vc.channel = i;
     if(ioctl(fd, VIDIOCGCHAN, &vc)<0)
     {
   perror("VIDIOCGCHAN");
   exit(1);
     }

     fprintf(stderr,"Video Source (%d) Name : %s\n",i, vc.name);
}

vc.channel =1;
vc.norm=1;

if(ioctl(fd, VIDIOCSCHAN, &vc) < 0)
{
     perror("VIDIOCSCHAN");
     exit(1);
}

   if(ioctl(fd, VIDIOCGMBUF, &mbuf) < 0)
   {
perror("VIDIOCGMBUF");
exit(1);
   }
fprintf(stderr,"the frames number is %d\n",mbuf.frames); 

   buf = (unsigned char*)mmap(0, mbuf.size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if((int)buf < 0)
{
     perror("mmap");
     exit(1);
}
mm.frame   = 0;
mm.height = HEIGHT;
mm.width   = WIDTH;
mm.format = VIDEO_PALETTE_RGB24;

if(ioctl(fd, VIDIOCMCAPTURE, &mm)<0)
{
     perror("VIDIOCMCAPTURE");
exit(1);
}

if(ioctl(fd, VIDIOCSYNC, &mm.frame)<0)
{
     perror("VIDIOCSYNC");
     exit(1);
}

if(-1 == (write_jpeg("./pic001.jpeg",buf,75,WIDTH,HEIGHT,0)))
{
printf("write_jpeg error\n");
exit(1);
}

munmap(buf,mbuf.size);
close(fd);
}

阅读(594) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~