我的第一個部落格_超超超級詳細的V4L2程式設計
阿新 • • 發佈:2019-02-16
參考:https://wenku.baidu.com/view/acaef8d8f01dc281e43af004.html
第一次寫部落格,有點小雞凍,小二,先來盤花生,半斤牛肉,一斤白酒,容我壓壓驚。。。
心得:
1.多看優秀的程式碼,然後一步步的去調。再看懂別人的每一行程式碼和v4l2的每一個結構體,然後去消化它。
2.細心一點,跟蹤問題出在哪裡,然後順藤摸瓜,一定是可以調出來的。
3.一定要相信,你是可以調出來的。
4.規範命名,規範註釋,規範寫程式碼。
嘿嘿,溜了溜了,還得做個ppt,給別人講解,分享一下經驗。
#include"photo.h"
structbuffer{
void*start;
unsignedintlength;
}*buffers;
#defineDEV_PATH"/dev/video0"//0/9
#defineCAPTURE_FILE"./pic/0.jpeg"
intphoto()
{
//1.opendevice.開啟攝像頭裝置
intfd=open(DEV_PATH,O_RDWR,0);//弄了好久以阻塞模式開啟攝像頭|O_NONBLOCK非堵塞
if(fd<0){
printf("opendevicefailed.\n");
}
printf("opendevicesuccess.->fd=%d\n",fd);
/*****************************
*arm:/include/linux/videodev2.h
*linux:/usr/include/linux/videodev2.h
structv4l2_capability{
u8driver[16];//驅動名字
u8card[32];//裝置名字
u8bus_info[32];//裝置在系統中的位置
u32version;//驅動版本號
u32capabilities;//裝置支援的操作
u32reserved[4];//保留欄位};
};
***************************/
//2.searchdeviceproperty.查詢裝置屬性
structv4l2_capabilitycap;
if(ioctl(fd,VIDIOC_QUERYCAP,&cap)==-1){
printf("VIDIOC_QUERYCAPfailed.\n");
}
printf("VIDIOC_QUERYCAPsuccess.->DriverName:%sCardName:%sBusInfo:%s\n",\
cap.driver,cap.card,cap.bus_info);//deviceinfo.裝置資訊
/***********************
structv4l2_fmtdesc{
u32index;//要查詢的格式序號,應用程式設定
enumv4l2_buf_typetype;//幀型別,資料流型別,必須永遠是V4L2_BUF_TYPE_VIDEO_CAPTURE
u32flags;//是否為壓縮格式
u8description[32];//格式名稱
u32pixelformat;//格式
u32reserved[4];//保留
};
***********************/
//3.showallsupportedformat.顯示所有支援的格式
structv4l2_fmtdescfmtdesc;
fmtdesc.index=0;//formnumber
fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;//frametype
while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)!=-1){
//if(fmtdesc.pixelformat&&fmt.fmt.pix.pixelformat){
printf("VIDIOC_ENUM_FMTsuccess.->fmt.fmt.pix.pixelformat:%s\n",fmtdesc.description);
//}
fmtdesc.index++;
}
/*************************
structv4l2_format{
enumv4l2_buf_typetype;//幀型別,應用程式設定,
union{
structv4l2_pix_formatpix;//視訊裝置使用
structv4l2_windowwin;
structv4l2_vbi_formatvbi;
structv4l2_sliced_vbi_formatsliced;
u8raw_data[200];
}fmt;
};
structv4l2_pix_format{
__u32width;//幀寬,必須是16的倍數
__u32height;//幀高,必須是16的倍數
__u32pixelformat;//視訊資料儲存型別,例如是YUV4:2:2還是RGB
enumv4l2_fieldfield;
__u32bytesperline;
__u32sizeimage;
enumv4l2_colorspacecolorspace;
__u32priv;
};
*************************/
//4.setorgaincurrentframe.設定或檢視當前格式
structv4l2_formatfmt;
memset(&fmt,0,sizeof(fmt));
fmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
//V4L2_PIX_FMT_RGB32V4L2_PIX_FMT_YUYVV4L2_STD_CAMERA_VGAV4L2_PIX_FMT_JPEG
fmt.fmt.pix.pixelformat=V4L2_PIX_FMT_JPEG;
if(ioctl(fd,VIDIOC_S_FMT,&fmt)==-1){
printf("VIDIOC_S_FMTfailed.\n");
return-1;
}
printf("VIDIOC_S_FMTsucess.\n");
if(ioctl(fd,VIDIOC_G_FMT,&fmt)==-1){
printf("VIDIOC_G_FMTfailed.\n");
return-1;
}
printf("VIDIOC_G_FMTsucess.->fmt.fmt.widthis%ld\nfmt.fmt.pix.heightis%ld\n\
fmt.fmt.pix.colorspaceis%ld\n",fmt.fmt.pix.width,fmt.fmt.pix.height,fmt.fmt.pix.colorspace);
//5.checktheformat.檢查是否支援某種幀格式
memset(&fmt,0,sizeof(fmt));
fmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.pixelformat=V4L2_PIX_FMT_JPEG;
//if(ioctl(fd,VIDIOC_TRY_FMT,&fmt)==-1)if(errno==EINVAL)
if(ioctl(fd,VIDIOC_TRY_FMT,&fmt)==-1){
printf("VIDIOC_TRY_FMTfailed.\n");
return-1;
}
printf("VIDIOC_TRY_FMTsucess.->fmt.fmt.widthis%ld\nfmt.fmt.pix.heightis%ld\n\
fmt.fmt.pix.colorspaceis%ld\n",fmt.fmt.pix.width,fmt.fmt.pix.height,fmt.fmt.pix.colorspace);
/***************
v4l2_requestbuffers結構如下:
structv4l2_requestbuffers{
u32count;//快取數量,也就是說在快取佇列裡保持多少張照片,一般不超過5個。
enumv4l2_buf_typetype;//資料流型別,必須永遠是V4L2_BUF_TYPE_VIDEO_CAPTURE
enumv4l2_memorymemory;//V4L2_MEMORY_MMAP或V4L2_MEMORY_USERPTR
u32reserved[2];
};
************************/
//6.1requestbuffers.申請緩衝區
structv4l2_requestbuffersreq;
req.count=2;//framecount.幀的個數
req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory=V4L2_MEMORY_MMAP;//automationoruserdefine.自動分配還是自定義
if(ioctl(fd,VIDIOC_REQBUFS,&req)==-1){
printf("VIDIOC_REQBUFSmapfailed.\n");
close(fd);
exit(-1);
}
printf("VIDIOC_REQBUFSmapsuccess.\n");
//6.2managebuffers.管理快取區
//應用程式和裝置3種交換方式:read/write,mmap,使用者指標.這裡用memorymap.記憶體對映
unsignedintn_buffers=0;
//buffers=(structbuffer*)calloc(req.count,sizeof(*buffers));
buffers=calloc(req.count,sizeof(*buffers));
/*****************************
v4l2_buffer結構如下:
structv4l2_buffer{
__u32index;//序號
enumv4l2_buf_typetype;//型別
__u32bytesused;//buffer中已使用的位元組數
__u32flags;//區分是MMAP還是USERPTR
enumv4l2_fieldfield;
structtimevaltimestamp;//獲取第一個位元組
structv4l2_timecodetimecode;
__u32sequence;//佇列中的序號
enumv4l2_memorymemory;//memorylocation
union{
__u32offset;//快取幀地址,只對MMAP有效
unsignedlonguserptr;
}m;
__u32length;//緩衝幀長度
__u32input;
__u32reserved;
};
****************************/
//structv4l2_bufferbuf;
for(n_buffers=0;n_buffers<req.count;++n_buffers)
{
structv4l2_bufferbuf;
memset(&buf,0,sizeof(buf));
buf.index=n_buffers;
buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory=V4L2_MEMORY_MMAP;
//查詢序號為n_buffers的緩衝區,得到其起始實體地址和大小
if(ioctl(fd,VIDIOC_QUERYBUF,&buf)==-1)
{
printf("VIDIOC_QUERYBUFfailed.\n");
close(fd);
exit(-1);
}
printf("VIDIOC_QUERYBUFsuccess.\n");
/******************
函式:void*mmap(void*start,size_tlength,intprot,intflags,intfd,off_toffsize);
引數start:指向欲對映的記憶體起始地址,通常設為NULL,代表讓系統自動選定地址,對映成功後返回該地址。
引數length:代表將檔案中多大的部分對映到記憶體。
引數prot:對映區域的保護方式。可以為以下幾種方式的組合:
PROT_EXEC對映區域可被執行
PROT_READ對映區域可被讀取
PROT_WRITE對映區域可被寫入
PROT_NONE對映區域不能存取
引數flags:影響對映區域的各種特性。在呼叫mmap()時必須要指定MAP_SHARED或MAP_PRIVATE。
MAP_FIXED如果引數start所指的地址無法成功建立對映時,則放棄對映,不對地址做修正。通常不鼓勵用此旗標。
MAP_SHARED對對映區域的寫入資料會複製迴文件內,而且允許其他對映該檔案的程序共享。
MAP_PRIVATE對對映區域的寫入操作會產生一個對映檔案的複製,即私人的“寫入時複製”(copyonwrite)對此區域作的任何修改都不會寫回原來的檔案內容。
MAP_ANONYMOUS建立匿名對映。此時會忽略引數fd,不涉及檔案,而且對映區域無法和其他程序共享。
MAP_DENYWRITE只允許對對映區域的寫入操作,其他對檔案直接寫入的操作將會被拒絕。
MAP_LOCKED將對映區域鎖定住,這表示該區域不會被置換(swap)。
引數fd:要對映到記憶體中的檔案描述符。如果使用匿名記憶體對映時,即flags中設定了MAP_ANONYMOUS,fd設為-1。
有些系統不支援匿名記憶體對映,則可以使用fopen開啟/dev/zero檔案,然後對該檔案進行對映,可以同樣達到匿名記憶體對映的效果。
引數offset:檔案對映的偏移量,通常設定為0,代表從檔案最前方開始對應,offset必須是分頁大小的整數倍。
返回值:若對映成功則返回對映區的記憶體起始地址,否則返回MAP_FAILED(-1),錯誤原因存於errno中。
*****************/
//memorymap
buffers[n_buffers].length=buf.length;
buffers[n_buffers].start=mmap(NULL,buf.length,PROT_READ|PROT_WRITE,MAP_SHARED,fd,buf.m.offset);
if(MAP_FAILED==buffers[n_buffers].start){
printf("memorymapfailed.\n");
close(fd);
exit(-1);
}
printf("memorymapsuccess.\n");
//Queenbuffer.將緩衝幀放入佇列
if(ioctl(fd,VIDIOC_QBUF,&buf)==-1){
printf("VIDIOC_QBUFfailed.->n_buffers=%d\n",n_buffers);
return-1;
}
printf("VIDIOC_QBUF.->Framebuffer%d:address=0x%x,length=%d\n",\
n_buffers,(unsignedint)buffers[n_buffers].start,buffers[n_buffers].length);
}
//將緩衝幀放入佇列
/*unsignedintii;
for(ii=0;ii<2;ii++){
structv4l2_bufferbuf;
buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory=V4L2_MEMORY_MMAP;
buf.index=ii;
if(ioctl(fd,VIDIOC_QBUF,&buf)==-1){
printf("VIDIOC_QBUFfailed.\n");
}
printf("VIDIOC_QBUFsuccess.->buf=%d:address=0x%x,length=%d\n",\
ii,(unsignedint)buffers[ii].start,buffers[ii].length);
}*/
//7.使能視訊裝置輸出視訊流
enumv4l2_buf_typetype;
type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
if(ioctl(fd,VIDIOC_STREAMON,&type)==-1){
printf("VIDIOC_STREAMONfailed.\n");
return-1;
}
printf("VIDIOC_STREAMONsuccess.\n");
//8.DQBUF.取出一幀
structv4l2_bufferbuf;
memset(&buf,0,sizeof(buf));
buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory=V4L2_MEMORY_MMAP;
if(ioctl(fd,VIDIOC_DQBUF,&buf)==-1){
printf("VIDIOC_DQBUFfailed.->fd=%d\n",fd);
return-1;
}
printf("VIDIOC_DQBUFsuccess.\n");
//9.Processtheframe.處理這一幀
FILE*fp=fopen(CAPTURE_FILE,"wb");
if(fp<0){
printf("openframe-data-filefailed.\n");
return-1;
}
printf("openframe-data-filesuccess.\n");
fwrite(buffers[buf.index].start,1,buf.length,fp);
fclose(fp);
printf("saveoneframesuccess.\n");//%s,CAPTURE_FILE
//10.QBUF.把一幀放回去
if(ioctl(fd,VIDIOC_QBUF,&buf)<0){
printf("VIDIOC_QBUFfailed.->fd=%d\n",fd);
return-1;
}
printf("VIDIOC_QBUFsuccess.\n");
/*********************
函式:intmunmap(void*start,size_tlength)
start:要取消對映的記憶體區域的起始地址。
length:要取消對映的記憶體區域的大小。
返回說明:成功執行時munmap()返回0。失敗時munmap返回-1。
**********************/
//11.Releasetheresource.釋放資源
inti;
for(i=0;i<2;i++){
munmap(buffers[i].start,buffers[i].length);
printf("munmapsuccess.\n");
}
close(fd);
printf("CameratestDone.\n");
return0;
}