三、V4L2的demo
capture.c是官方示例程序。
capture.c 程序中的 process_image 函数:
capture.c 程序主要是用来演示怎样使用 v4l2 接口,并没有对采集到的视频帧数据做任何实际的处理,仅仅用 process_image 函数表示了处理图像的代码位置。
process_image 函数只有一个参数,就是存储视频帧的内存的地址指针,但是在真正的应用中,通常还需要知道该指针指向的数据的大小。
因此可以修改函数,改成 void process_image ( const void * p, int len ) ,但是每次调用 process_image 的时候,第 2 个参数该传递什么值?
考虑到程序中对 buffer 的定义
struct buffer {
void * start;
size_t length};
如果将 buffer.length 作为第 2 个参数传递到修改后的 process_image 函数中,这样做是不正确的。 process_image 需要的第二个参数应该是每帧图像的大小,仔细阅读代码后会发现, buffer.length 并不一定就等于图像帧的大小。 (buffer 的大小,还需要考虑其他的一些因素,比如内存对齐等 )。
capture.c只是一个示例程序,仅仅是演示怎样使用v4l2中最基本的接口。尤其是在main函数中的那几个函数调用,表明了在使用v4l2时的最基本的一个流程,包括 open_device,init_device,start_capturing,mainloop,stop_capturing,uninit_device,close_device。在写程序的时候,可以充分的利用这几个基本模块,把他们分散在不同的代码位置上,灵活的调用,有兴趣的可以看一下gstreamer中v4l2src的源代码或者其他的大型程序的相关部分。
总之一句话,capture.c仅仅是一个演示程序,不要局限于它的代码结构,要灵活的使用。
下面是capture.c的源代码:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
#include<getopt.h>/*getopt_long()*/
#include<fcntl.h>/*low-leveli/o*/
#include<unistd.h>
#include<errno.h>
#include<malloc.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<sys/time.h>
#include<sys/mman.h>
#include<sys/ioctl.h>
#include<asm/types.h>/*forvideodev2.h*/
#include<linux/videodev2.h>
#defineCLEAR(x)memset(&(x),0,sizeof(x))
typedefenum{
IO_METHOD_READ,IO_METHOD_MMAP,IO_METHOD_USERPTR,
}io_method;
structbuffer{
void*start;
size_tlength;//buffer'slengthisdifferentfromcap_image_size
};
staticchar*dev_name=NULL;
staticio_methodio=IO_METHOD_MMAP;//IO_METHOD_READ;//IO_METHOD_MMAP;
staticintfd=-1;
structbuffer*buffers=NULL;
staticunsignedintn_buffers=0;
staticFILE*outf=0;
staticunsignedintcap_image_size=0;//tokeeptherealimagesize!!
//////////////////////////////////////////
staticvoiderrno_exit(constchar*s){
fprintf(stderr,"%serror%d,%s/n",s,errno,strerror(errno));
exit(EXIT_FAILURE);
}
staticintxioctl(intfd,intrequest,void*arg){
intr;
do
r=ioctl(fd,request,arg);
while(-1==r&&EINTR==errno);
returnr;
}
staticvoidprocess_image(constvoid*p,intlen){
//staticchar[115200]Outbuff;
fputc('.',stdout);
if(len>0){
fputc('.',stdout);
fwrite(p,1,len,outf);
}
fflush(stdout);
}
staticintread_frame(void){
structv4l2_bufferbuf;
unsignedinti;
switch(io){
caseIO_METHOD_READ:
if(-1==read(fd,buffers[0].start,buffers[0].length)){
switch(errno){
caseEAGAIN:
return0;
caseEIO:
/*CouldignoreEIO,seespec.*/
/*fallthrough*/
default:
errno_exit("read");
}
}
//printf("length=%d/r",buffers[0].length);
//process_image(buffers[0].start,buffers[0].length);
printf("image_size=%d,/tIO_METHOD_READbuffer.length=%d/r",
cap_image_size,buffers[0].length);
process_image(buffers[0].start,cap_image_size);
break;
caseIO_METHOD_MMAP:
CLEAR(buf);
buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory=V4L2_MEMORY_MMAP;
if(-1==xioctl(fd,VIDIOC_DQBUF,&buf)){
switch(errno){
caseEAGAIN:
return0;
caseEIO:
/*CouldignoreEIO,seespec.*/
/*fallthrough*/
default:
errno_exit("VIDIOC_DQBUF");
}
}
assert(buf.index<n_buffers);
//printf("length=%d/r",buffers[buf.index].length);
//process_image(buffers[buf.index].start,buffers[buf.index].length);
printf("image_size=%d,/tIO_METHOD_MMAPbuffer.length=%d/r",
cap_image_size,buffers[0].length);
process_image(buffers[0].start,cap_image_size);
if(-1==xioctl(fd,VIDIOC_QBUF,&buf))
errno_exit("VIDIOC_QBUF");
break;
caseIO_METHOD_USERPTR:
CLEAR(buf);
buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory=V4L2_MEMORY_USERPTR;
if(-1==xioctl(fd,VIDIOC_DQBUF,&buf)){
switch(errno){
caseEAGAIN:
return0;
caseEIO:
/*CouldignoreEIO,seespec.*/
/*fallthrough*/
default:
errno_exit("VIDIOC_DQBUF");
}
}
for(i=0;i<n_buffers;++i)
if(buf.m.userptr==(unsignedlong)buffers[i].start&&buf.length
==buffers[i].length)
break;
assert(i<n_buffers);
//printf("length=%d/r",buffers[i].length);
//process_image((void*)buf.m.userptr,buffers[i].length);
printf("image_size=%d,/tIO_METHOD_USERPTRbuffer.length=%d/r",
cap_image_size,buffers[0].length);
process_image(buffers[0].start,cap_image_size);
if(-1==xioctl(fd,VIDIOC_QBUF,&buf))
errno_exit("VIDIOC_QBUF");
break;
}
return1;
}
staticvoidmainloop(void){
unsignedintcount;
count=100;
while(count-->0){
for(;;){
fd_setfds;
structtimevaltv;
intr;
FD_ZERO(&fds);
FD_SET(fd,&fds);
/*Timeout.*/
tv.tv_sec=2;
tv.tv_usec=0;
r=select(fd+1,&fds,NULL,NULL,&tv);
if(-1==r){
if(EINTR==errno)
continue;
errno_exit("select");
}
if(0==r){
fprintf(stderr,"selecttimeout/n");
exit(EXIT_FAILURE);
}
if(read_frame())
break;
/*EAGAIN-continueselectloop.*/
}
}
}
staticvoidstop_capturing(void){
enumv4l2_buf_typetype;
switch(io){
caseIO_METHOD_READ:
/*Nothingtodo.*/
break;
caseIO_METHOD_MMAP:
caseIO_METHOD_USERPTR:
type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
if(-1==xioctl(fd,VIDIOC_STREAMOFF,&type))
errno_exit("VIDIOC_STREAMOFF");
break;
}
}
staticvoidstart_capturing(void){
unsignedinti;
enumv4l2_buf_typetype;
switch(io){
caseIO_METHOD_READ:
/*Nothingtodo.*/
break;
caseIO_METHOD_MMAP:
for(i=0;i<n_buffers;++i){
structv4l2_bufferbuf;
CLEAR(buf);
buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory=V4L2_MEMORY_MMAP;
buf.index=i;
if(-1==xioctl(fd,VIDIOC_QBUF,&buf))
errno_exit("VIDIOC_QBUF");
}
type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
if(-1==xioctl(fd,VIDIOC_STREAMON,&type))
errno_exit("VIDIOC_STREAMON");
break;
caseIO_METHOD_USERPTR:
for(i=0;i<n_buffers;++i){
structv4l2_bufferbuf;
CLEAR(buf);
buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory=V4L2_MEMORY_USERPTR;
buf.index=i;
buf.m.userptr=(unsignedlong)buffers[i].start;
buf.length=buffers[i].length;
if(-1==xioctl(fd,VIDIOC_QBUF,&buf))
errno_exit("VIDIOC_QBUF");
}
type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
if(-1==xioctl(fd,VIDIOC_STREAMON,&type))
errno_exit("VIDIOC_STREAMON");
break;
}
}
staticvoiduninit_device(void){
unsignedinti;
switch(io){
caseIO_METHOD_READ:
free(buffers[0].start);
break;
caseIO_METHOD_MMAP:
for(i=0;i<n_buffers;++i)
if(-1==munmap(buffers[i].start,buffers[i].length))
errno_exit("munmap");
break;
caseIO_METHOD_USERPTR:
for(i=0;i<n_buffers;++i)
free(buffers[i].start);
break;
}
free(buffers);
}
staticvoidinit_read(unsignedintbuffer_size){
buffers=calloc(1,sizeof(*buffers));
if(!buffers){
fprintf(stderr,"Outofmemory/n");
exit(EXIT_FAILURE);
}
buffers[0].length=buffer_size;
buffers[0].start=malloc(buffer_size);
if(!buffers[0].start){
fprintf(stderr,"Outofmemory/n");
exit(EXIT_FAILURE);
}
}
staticvoidinit_mmap(void){
structv4l2_requestbuffersreq;
CLEAR(req);
req.count=4;
req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory=V4L2_MEMORY_MMAP;
if(-1==xioctl(fd,VIDIOC_REQBUFS,&req)){
if(EINVAL==errno){
fprintf(stderr,"%sdoesnotsupport"
"memorymapping/n",dev_name);
exit(EXIT_FAILURE);
}else{
errno_exit("VIDIOC_REQBUFS");
}
}
if(req.count<2){
fprintf(stderr,"Insufficientbuffermemoryon%s/n",dev_name);
exit(EXIT_FAILURE);
}
buffers=calloc(req.count,sizeof(*buffers));
if(!buffers){
fprintf(stderr,"Outofmemory/n");
exit(EXIT_FAILURE);
}
for(n_buffers=0;n_buffers<req.count;++n_buffers){
structv4l2_bufferbuf;
CLEAR(buf);
buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory=V4L2_MEMORY_MMAP;
buf.index=n_buffers;
if(-1==xioctl(fd,VIDIOC_QUERYBUF,&buf))
errno_exit("VIDIOC_QUERYBUF");
buffers[n_buffers].length=buf.length;
buffers[n_buffers].start=mmap(NULL/*startanywhere*/,buf.length,
PROT_READ|PROT_WRITE/*required*/,
MAP_SHARED/*recommended*/,fd,buf.m.offset);
if(MAP_FAILED==buffers[n_buffers].start)
errno_exit("mmap");
}
}
staticvoidinit_userp(unsignedintbuffer_size){
structv4l2_requestbuffersreq;
unsignedintpage_size;
page_size=getpagesize();
buffer_size=(buffer_size+page_size-1)&~(page_size-1);
CLEAR(req);
req.count=4;
req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory=V4L2_MEMORY_USERPTR;
if(-1==xioctl(fd,VIDIOC_REQBUFS,&req)){
if(EINVAL==errno){
fprintf(stderr,"%sdoesnotsupport"
"userpointeri/o/n",dev_name);
exit(EXIT_FAILURE);
}else{
errno_exit("VIDIOC_REQBUFS");
}
}
buffers=calloc(4,sizeof(*buffers));
if(!buffers){
fprintf(stderr,"Outofmemory/n");
exit(EXIT_FAILURE);
}
for(n_buffers=0;n_buffers<4;++n_buffers){
buffers[n_buffers].length=buffer_size;
buffers[n_buffers].start=memalign(/*boundary*/page_size,
buffer_size);
if(!buffers[n_buffers].start){
fprintf(stderr,"Outofmemory/n");
exit(EXIT_FAILURE);
}
}
}
staticvoidinit_device(void){
structv4l2_capabilitycap;
structv4l2_cropcapcropcap;
structv4l2_cropcrop;
structv4l2_formatfmt;
unsignedintmin;
if(-1==xioctl(fd,VIDIOC_QUERYCAP,&cap)){
if(EINVAL==errno){
fprintf(stderr,"%sisnoV4L2device/n",dev_name);
exit(EXIT_FAILURE);
}else{
errno_exit("VIDIOC_QUERYCAP");
}
}
if(!(cap.capabilities&V4L2_CAP_VIDEO_CAPTURE)){
fprintf(stderr,"%sisnovideocapturedevice/n",dev_name);
exit(EXIT_FAILURE);
}
switch(io){
caseIO_METHOD_READ:
if(!(cap.capabilities&V4L2_CAP_READWRITE)){
fprintf(stderr,"%sdoesnotsupportreadi/o/n",dev_name);
exit(EXIT_FAILURE);
}
break;
caseIO_METHOD_MMAP:
caseIO_METHOD_USERPTR:
if(!(cap.capabilities&V4L2_CAP_STREAMING)){
fprintf(stderr,"%sdoesnotsupportstreamingi/o/n",dev_name);
exit(EXIT_FAILURE);
}
break;
}
//////notallcapturesupportcrop!!!!!!!
/*Selectvideoinput,videostandardandtunehere.*/
printf("-#-#-#-#-#-#-#-#-#-#-#-#-#-/n");
CLEAR(cropcap);
cropcap.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
if(0==xioctl(fd,VIDIOC_CROPCAP,&cropcap)){
crop.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
#ifndefCROP_BY_JACK
crop.c=cropcap.defrect;/*resettodefault*/
#else
crop.c.left=cropcap.defrect.left;
crop.c.top=cropcap.defrect.top;
crop.c.width=352;
crop.c.height=288;
#endif
printf("----->hasabilitytocrop!!/n");
printf("cropcap.defrect=(%d,%d,%d,%d)/n",cropcap.defrect.left,
cropcap.defrect.top,cropcap.defrect.width,
cropcap.defrect.height);
if(-1==xioctl(fd,VIDIOC_S_CROP,&crop)){
switch(errno){
caseEINVAL:
/*Croppingnotsupported.*/
break;
default:
/*Errorsignored.*/
break;
}
printf("-----!!butcropto(%d,%d,%d,%d)Failed!!/n",
crop.c.left,crop.c.top,crop.c.width,crop.c.height);
}else{
printf("----->sussesscropto(%d,%d,%d,%d)/n",crop.c.left,
crop.c.top,crop.c.width,crop.c.height);
}
}else{
/*Errorsignored.*/
printf("!!hasnoabilitytocrop!!/n");
}
printf("-#-#-#-#-#-#-#-#-#-#-#-#-#-/n");
printf("/n");
////////////cropfinished!
//////////settheformat
CLEAR(fmt);
fmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width=640;
fmt.fmt.pix.height=480;
//V4L2_PIX_FMT_YVU420,V4L2_PIX_FMT_YUV420—Planarformatswith1/2horizontalandverticalchromaresolution,alsoknownasYUV4:2:0
//V4L2_PIX_FMT_YUYV—Packedformatwith1/2horizontalchromaresolution,alsoknownasYUV4:2:2
fmt.fmt.pix.pixelformat=V4L2_PIX_FMT_YUYV;//V4L2_PIX_FMT_YUV420;//V4L2_PIX_FMT_YUYV;
fmt.fmt.pix.field=V4L2_FIELD_INTERLACED;
{
printf("-#-#-#-#-#-#-#-#-#-#-#-#-#-/n");
printf("=====willsetfmtto(%d,%d)--",fmt.fmt.pix.width,
fmt.fmt.pix.height);
if(fmt.fmt.pix.pixelformat==V4L2_PIX_FMT_YUYV){
printf("V4L2_PIX_FMT_YUYV/n");
}elseif(fmt.fmt.pix.pixelformat==V4L2_PIX_FMT_YUV420){
printf("V4L2_PIX_FMT_YUV420/n");
}elseif(fmt.fmt.pix.pixelformat==V4L2_PIX_FMT_NV12){
printf("V4L2_PIX_FMT_NV12/n");
}
}
if(-1==xioctl(fd,VIDIOC_S_FMT,&fmt))
errno_exit("VIDIOC_S_FMT");
{
printf("=====aftersetfmt/n");
printf("fmt.fmt.pix.width=%d/n",fmt.fmt.pix.width);
printf("fmt.fmt.pix.height=%d/n",fmt.fmt.pix.height);
printf("fmt.fmt.pix.sizeimage=%d/n",fmt.fmt.pix.sizeimage);
cap_image_size=fmt.fmt.pix.sizeimage;
printf("fmt.fmt.pix.bytesperline=%d/n",fmt.fmt.pix.bytesperline);
printf("-#-#-#-#-#-#-#-#-#-#-#-#-#-/n");
printf("/n");
}
cap_image_size=fmt.fmt.pix.sizeimage;
/*NoteVIDIOC_S_FMTmaychangewidthandheight.*/
printf("-#-#-#-#-#-#-#-#-#-#-#-#-#-/n");
/*Buggydriverparanoia.*/
min=fmt.fmt.pix.width*2;
if(fmt.fmt.pix.bytesperline<min)
fmt.fmt.pix.bytesperline=min;
min=fmt.fmt.pix.bytesperline*fmt.fmt.pix.height;
if(fmt.fmt.pix.sizeimage<min)
fmt.fmt.pix.sizeimage=min;
printf("AfterBuggydriverparanoia/n");
printf(">>fmt.fmt.pix.sizeimage=%d/n",fmt.fmt.pix.sizeimage);
printf(">>fmt.fmt.pix.bytesperline=%d/n",fmt.fmt.pix.bytesperline);
printf("-#-#-#-#-#-#-#-#-#-#-#-#-#-/n");
printf("/n");
switch(io){
caseIO_METHOD_READ:
init_read(fmt.fmt.pix.sizeimage);
break;
caseIO_METHOD_MMAP:
init_mmap();
break;
caseIO_METHOD_USERPTR:
init_userp(fmt.fmt.pix.sizeimage);
break;
}
}
staticvoidclose_device(void){
if(-1==close(fd))
errno_exit("close");
fd=-1;
}
staticvoidopen_device(void){
structstatst;
if(-1==stat(dev_name,&st)){
fprintf(stderr,"Cannotidentify'%s':%d,%s/n",dev_name,errno,
strerror(errno));
exit(EXIT_FAILURE);
}
if(!S_ISCHR(st.st_mode)){
fprintf(stderr,"%sisnodevice/n",dev_name);
exit(EXIT_FAILURE);
}
fd=open(dev_name,O_RDWR/*required*/|O_NONBLOCK,0);
if(-1==fd){
fprintf(stderr,"Cannotopen'%s':%d,%s/n",dev_name,errno,
strerror(errno));
exit(EXIT_FAILURE);
}
}
staticvoidusage(FILE*fp,intargc,char**argv){
fprintf(fp,"Usage:%s[options]/n/n"
"Options:/n"
"-d|--devicenameVideodevicename[/dev/video0]/n"
"-h|--helpPrintthismessage/n"
"-m|--mmapUsememorymappedbuffers/n"
"-r|--readUseread()calls/n"
"-u|--userpUseapplicationallocatedbuffers/n"
"",argv[0]);
}
staticconstcharshort_options[]="d:hmru";
staticconststructoptionlong_options[]={{"device",required_argument,
NULL,'d'},{"help",no_argument,NULL,'h'},{"mmap",no_argument,
NULL,'m'},{"read",no_argument,NULL,'r'},{"userp",
no_argument,NULL,'u'},{0,0,0,0}};
intmain(intargc,char**argv){
dev_name="/dev/video0";
outf=fopen("out.yuv","wb");
for(;;){
intindex;
intc;
c=getopt_long(argc,argv,short_options,long_options,&index);
if(-1==c)
break;
switch(c){
case0:/*getopt_long()flag*/
break;
case'd':
dev_name=optarg;
break;
case'h':
usage(stdout,argc,argv);
exit(EXIT_SUCCESS);
case'm':
io=IO_METHOD_MMAP;
break;
case'r':
io=IO_METHOD_READ;
break;
case'u':
io=IO_METHOD_USERPTR;
break;
default:
usage(stderr,argc,argv);
exit(EXIT_FAILURE);
}
}
open_device();
init_device();
start_capturing();
mainloop();
printf("/n");
stop_capturing();
fclose(outf);
uninit_device();
close_device();
exit(EXIT_SUCCESS);
return0;
}