Video4Linux2:
該應用使用的是USB攝像頭,在Linux中內核驅動框架多采Video4Linux2(V4L2),V4L2爲上層的訪問底層的視頻設備提供了統一的接口,提取出公共代碼避免底層硬件差異。 V4L2支持三類設備:視頻輸入輸出設備、VBI設備和radio設備及更多。下圖V4L2在Linux系統中的結構圖:
關於V4L2更加詳細的介紹見:
1.http://blog.chinaunix.net/uid-26851094-id-3356224.html
2.http://blog.csdn.net/rubyboss/article/details/14053523
USB攝像頭初始化:
1.打開設備文件:
fd =open("dev/video", O_RDWR);
2.查詢設備支持的能力:
ioctl(fd, VIDIOC_QUERYCAP, &tV4l2Cap);
3.獲取當前視頻設備支持的視頻格式:
攝像頭的輸出格式多種多樣,有可能是JPEG、RGB、YUYV等等,而LCD顯示屏需要輸入RGB格式數據。因此,此時需獲取當前攝像頭支持的視頻格式,以爲後續數據格式轉換作準備。
ioctl(fd, VIDIOC_ENUM_FMT, &tFmtDesc)
4.查看驅動程序是否支持當前LCD分辨率,若不支持,將調整參數,傳回應用層:
ioctl(iFd, VIDIOC_S_FMT, &tV4l2Fmt);
5.請求V4L2驅動分配視頻緩衝區,V4L2是視頻設備的驅動層,位於內核空間,所以通過VIDIOC_REQBUFS控制命令申請的內存位於內核空間,應用程序不能直接訪問,需要通過調用mmap內存映射函數把內核空間內存映射到用戶空間後,應用程序通過訪問用戶空間地址來訪問內核空間:
ioctl(iFd, VIDIOC_REQBUFS, &tV4l2ReqBuffs);
6. 使用控制命令VIDIOC_QUERYBUF獲得已經分配的V4L2的視頻緩衝區的使用狀態、在內核空間的偏移地址、緩衝區長度等相關信息。然後調用函數mmap把內核空間地址映射到用戶空間,以便應用程序訪問位於內核空間的視頻緩衝區:
ioctl(iFd, VIDIOC_QUERYBUF, &tV4l2Buf);
mmap(0,tV4l2Buf.length, PROT_READ, MAP_SHARED, fd,tV4l2Buf.m.offset);
7. 數據流IO:
當開始數據流IO時,數據(攝像頭獲取的單幀畫面數據)以v4l2_buffer的格式在應用和驅動之間傳輸。一個緩衝區可以有三種狀態:
a.在驅動的傳入隊列中,驅動程序將會對此隊列中的緩衝區進行處理,用戶空間通過VIDIOC_QBUF把緩衝區放入到隊列。對於一個視頻捕獲設備,傳入隊列中的緩衝區是空的,驅動會往其中填充數據;
b.在驅動的傳出隊列中,這些緩衝區已由驅動處理過,對於一個視頻捕獲設備,緩存區已經填充了視頻數據,正等用戶空間來認領;
c.用戶空間狀態的隊列,已經通過VIDIOC_DQBUF傳出到用戶空間的緩衝區,此時緩衝區由用戶空間擁有,驅動無法訪問。
此時應將申請並且mmap的多個視頻緩衝區v4l2_buffer依次放入隊列,等待驅動填充數據:
ioctl(iFd, VIDIOC_QBUF, &tV4l2Buf)
8.至此就完成了攝像頭的初始化
9.參考代碼
#define NB_BUFFER 4 //視頻緩衝區個數(自定義)
typedef struct videoDeviceParam
{
int iFd;
int iWidth;
int iHeight;
int iBpp;
int pixelformat;
int iVideoBufCnt;
int iVideoBufCurIndex;
int iVideoBufMaxLen;
unsigned char *pucVideBuf[NB_BUFFER];
}T_videoDeviceParam,*PT_videoDeviceParam;
int V4l2InitDevice(char * videoDevName , PT_videoDeviceParam pt_videoDeviceParam)
{
int i;
int iFd;
int iError;
struct v4l2_capability tV4l2Cap;
struct v4l2_fmtdesc tFmtDesc;
struct v4l2_format tV4l2Fmt;
struct v4l2_requestbuffers tV4l2ReqBuffs;
struct v4l2_buffer tV4l2Buf;
unsigned int iLcdWidth;
unsigned int iLcdHeigt;
unsigned int iLcdBpp;
iFd =open(videoDevName, O_RDWR); //打開設備文件
if(iFd < 0 )
printf("Video OPEN Failed.\n");
pt_videoDeviceParam->iFd = iFd;
memset(&tV4l2Cap, 0, sizeof(struct v4l2_capability));
iError = ioctl(iFd, VIDIOC_QUERYCAP, &tV4l2Cap);//查詢設備支持的能力
if (iError)
{
printf("unable to query device.\n");
goto err_exit;
}
/* 獲取當前視頻設備支持的視頻格式 */
memset(&tFmtDesc, 0, sizeof(tFmtDesc));
tFmtDesc.index = 0;
tFmtDesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
while ((iError = ioctl(iFd, VIDIOC_ENUM_FMT, &tFmtDesc)) == 0)
{
if (tFmtDesc.pixelformat == V4L2_PIX_FMT_YUYV)
{
pt_videoDeviceParam->pixelformat = tFmtDesc.pixelformat;
break;
}
tFmtDesc.index++;
}
if (!pt_videoDeviceParam->pixelformat)
{
printf("can not support the format of this device\n");
goto err_exit;
}
LcdGetResolution(&iLcdWidth, &iLcdHeigt, &iLcdBpp);
memset(&tV4l2Fmt, 0, sizeof(struct v4l2_format));
tV4l2Fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
tV4l2Fmt.fmt.pix.pixelformat = pt_videoDeviceParam->pixelformat;
tV4l2Fmt.fmt.pix.width = iLcdWidth;
tV4l2Fmt.fmt.pix.height = iLcdHeigt;
tV4l2Fmt.fmt.pix.field = V4L2_FIELD_ANY;
/* 如果驅動程序發現無法某些參數(比如分辨率),
* 它會調整這些參數, 並且返回給應用程序
*/
iError = ioctl(iFd, VIDIOC_S_FMT, &tV4l2Fmt);
if (iError)
{
printf("Unable to set format\n");
goto err_exit;
}
pt_videoDeviceParam->iWidth = tV4l2Fmt.fmt.pix.width;
pt_videoDeviceParam->iHeight = tV4l2Fmt.fmt.pix.height;
/* 申請視頻緩衝區 */
memset(&tV4l2ReqBuffs, 0, sizeof(struct v4l2_requestbuffers));
tV4l2ReqBuffs.count = NB_BUFFER;
tV4l2ReqBuffs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
tV4l2ReqBuffs.memory = V4L2_MEMORY_MMAP;
iError = ioctl(iFd, VIDIOC_REQBUFS, &tV4l2ReqBuffs);
if (iError)
{
printf("Unable to allocate buffers.\n");
goto err_exit;
}
pt_videoDeviceParam->iVideoBufCnt = tV4l2ReqBuffs.count;
if (tV4l2Cap.capabilities & V4L2_CAP_STREAMING)
{
/* 映射至應用程序空間 */
for (i = 0; i < pt_videoDeviceParam->iVideoBufCnt; i++)
{
memset(&tV4l2Buf, 0, sizeof(struct v4l2_buffer));
tV4l2Buf.index = i;
tV4l2Buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
tV4l2Buf.memory = V4L2_MEMORY_MMAP;
iError = ioctl(iFd, VIDIOC_QUERYBUF, &tV4l2Buf);
if (iError)
{
printf("Unable to query buffer.\n");
goto err_exit;
}
pt_videoDeviceParam->iVideoBufMaxLen = tV4l2Buf.length;
printf("tV4l2Buf.length = %d\n",tV4l2Buf.length );
pt_videoDeviceParam->pucVideBuf[i] = mmap(0,
tV4l2Buf.length, PROT_READ, MAP_SHARED, iFd,
tV4l2Buf.m.offset);
if (pt_videoDeviceParam->pucVideBuf[i] == MAP_FAILED)
{
printf("Unable to map buffer\n");
goto err_exit;
}
}
/* 將分配空間放入隊列*/
for (i = 0; i < pt_videoDeviceParam->iVideoBufCnt; i++)
{
memset(&tV4l2Buf, 0, sizeof(struct v4l2_buffer));
tV4l2Buf.index = i;
tV4l2Buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
tV4l2Buf.memory = V4L2_MEMORY_MMAP;
iError = ioctl(iFd, VIDIOC_QBUF, &tV4l2Buf);
if (iError)
{
printf("Unable to queue buffer.\n");
goto err_exit;
}
}
}
return 0;
err_exit:
printf("err_exit.\n");
close(iFd);
return -1;
}