基於S3C2440的USB攝像頭應用簡單實現之攝像頭初始化(三)

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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章