V4L2應用程式開發(1)

lethe1203發表於2024-03-26
參考資料:韋東山第三期
v4l2應用程式開發分為兩個部分,資料採集流程和控制流程兩個部分
資料採集流程:
分為空閒連結串列和完成連結串列
驅動程式週而復始地做如下事情:
  • 從硬體採集到資料
  • 把"空閒連結串列"取出buffer,把資料存入buffer
  • 把含有資料的buffer放入"完成連結串列"
APP也會週而復始地做如下事情:
  • 監測"完成連結串列",等待它含有buffer
  • 從"完成連結串列"中取出buffer
  • 處理資料
  • 把buffer放入"空閒連結串列"
連結串列操作示意圖:
0
攝像頭的應用操作流程,如下:
  • open:開啟裝置節點/dev/videoX
  • ioctl VIDIOC_QUERYCAP:Query Capbility,查詢能力,比如
    • 確認它是否是"捕獲裝置",因為有些節點是輸出裝置
    • 確認它是否支援mmap操作,還是僅支援read/write操作
  • ioctl VIDIOC_ENUM_FMT:列舉它支援的格式
  • ioctl VIDIOC_S_FMT:在上面列舉出來的格式裡,選擇一個來設定格式
  • ioctl VIDIOC_REQBUFS:申請buffer,APP可以申請很多個buffer,但是驅動程式不一定能申請到,requery申請
  • ioctl VIDIOC_QUERYBUF和mmap:查詢buffer資訊、對映
    • 如果申請到了N個buffer,這個ioctl就應該執行N次
    • 執行mmap後,APP就可以直接讀寫這些buffer
  • ioctl VIDIOC_QBUF:把buffer放入"空閒連結串列"
    • 如果申請到了N個buffer,這個ioctl就應該執行N次
  • ioctl VIDIOC_STREAMON:啟動攝像頭
  • 這裡是一個迴圈:使用poll/select監測buffer,然後從"完成連結串列"中取出buffer,處理後再放入"空閒連結串列"
    • poll/select
    • ioctl VIDIOC_DQBUF:從"完成連結串列"中取出buffer
    • 處理:前面使用mmap對映了每個buffer的地址,處理時就可以直接使用地址來訪問buffer
    • ioclt VIDIOC_QBUF:把buffer放入"空閒連結串列"
  • ioctl VIDIOC_STREAMOFF:停止攝像頭
控制流程:
應用程式介面

資料格式的列舉、獲得和設定:

1、資料格式

列舉格式的時候,只是返回支援的pixelformat和描述
struct v4l2_fmtdesc fmtdesc;
fmtdesc.index = 0;  // 比如從0開始
fmtdesc.type  = V4L2_BUF_TYPE_VIDEO_CAPTURE;  // 指定type為"捕獲"
ioctl(vd->fd, VIDIOC_ENUM_FMT, &fmtdesc);

// uapi/linux/videodev2.h
 struct v4l2_fmtdesc {
         __u32               index;             /* Format number      */
         __u32               type;              /* enum v4l2_buf_type */
         __u32               flags;
         __u8                description[32];   /* Description string */
         __u32               pixelformat;       /* Format fourcc      */
         __u32               reserved[4];
 };

 enum v4l2_buf_type {
         V4L2_BUF_TYPE_VIDEO_CAPTURE        = 1,            // 攝像頭一般就為捕獲裝置
         V4L2_BUF_TYPE_VIDEO_OUTPUT         = 2,
         V4L2_BUF_TYPE_VIDEO_OVERLAY        = 3,
         V4L2_BUF_TYPE_VBI_CAPTURE          = 4,
         V4L2_BUF_TYPE_VBI_OUTPUT           = 5,
         V4L2_BUF_TYPE_SLICED_VBI_CAPTURE   = 6,
         V4L2_BUF_TYPE_SLICED_VBI_OUTPUT    = 7,
 #if 1
         /* Experimental */
         V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY = 8,
 #endif
         V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE = 9,
         V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE  = 10,
         V4L2_BUF_TYPE_SDR_CAPTURE          = 11,
         /* Deprecated, do not use */
         V4L2_BUF_TYPE_PRIVATE              = 0x80,
 };

2、獲取當前攝像頭使用的格式

獲取當前格式更加詳細的資訊,就需要使用VIDIOC_G_FRT
struct v4l2_format currentFormat;
memset(&currentFormat, 0, sizeof(struct v4l2_format));
currentFormat.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(vd->fd, VIDIOC_G_FMT, &currentFormat);

#if 0
struct v4l2_format {
    __u32     type;            // 表示捕獲裝置
    union {
        struct v4l2_pix_format        pix;     /* V4L2_BUF_TYPE_VIDEO_CAPTURE */
        struct v4l2_pix_format_mplane    pix_mp;  /* V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE */
        struct v4l2_window        win;     /* V4L2_BUF_TYPE_VIDEO_OVERLAY */
        struct v4l2_vbi_format        vbi;     /* V4L2_BUF_TYPE_VBI_CAPTURE */
        struct v4l2_sliced_vbi_format    sliced;  /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */
        struct v4l2_sdr_format        sdr;     /* V4L2_BUF_TYPE_SDR_CAPTURE */
        __u8    raw_data[200];                   /* user-defined */
    } fmt;
};

/*
 *    V I D E O   I M A G E   F O R M A T
 */
struct v4l2_pix_format {
    __u32                 width;
    __u32            height;
    __u32            pixelformat;
    __u32            field;        /* enum v4l2_field */
    __u32                bytesperline;    /* for padding, zero if unused */
    __u32                  sizeimage;
    __u32            colorspace;    /* enum v4l2_colorspace */
    __u32            priv;        /* private data, depends on pixelformat */
    __u32            flags;        /* format flags (V4L2_PIX_FMT_FLAG_*) */
    __u32            ycbcr_enc;    /* enum v4l2_ycbcr_encoding */
    __u32            quantization;    /* enum v4l2_quantization */
    __u32            xfer_func;    /* enum v4l2_xfer_func */
};
#endif

3、設定當前格式

使用VIDIOC_S_FMT
struct v4l2_format fmt;
memset(&fmt, 0, sizeof(struct v4l2_format));
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = 1024;
fmt.fmt.pix.height = 768;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
fmt.fmt.pix.field = V4L2_FIELD_ANY;
int ret = ioctl(vd->fd, VIDIOC_S_FMT, &fmt);

選擇輸入源:

int value;
ioctl(h->fd,VIDIOC_G_INPUT,&value);  // 讀到的value從0開始, 0表示第1個input源

int value = 0;  // 0表示第1個input源
ioctl(h->fd,VIDIOC_S_INPUT,&value)

其他引數:

如果每一引數都提供一系列的ioctl cmd,那使用起來很不方便。
對於這些引數,APP使用對應ID來選中它,然後使用VIDIOC_QUERYCTRL、VIDIOC_G_CTRL、VIDIOC_S_CTRL來操作它。
不同引數的ID值不同。以亮度Brightness為例,有如下呼叫方法:

1、查詢

struct v4l2_queryctrl   qctrl;
memset(&qctrl, 0, sizeof(qctrl));
qctrl.id = V4L2_CID_BRIGHTNESS; // V4L2_CID_BASE+0;
ioctl(fd, VIDIOC_QUERYCTRL, &qctrl);

/*  Used in the VIDIOC_QUERYCTRL ioctl for querying controls */
struct v4l2_queryctrl {
    __u32             id;
    __u32             type;    /* enum v4l2_ctrl_type */
    __u8             name[32];    /* Whatever */
    __s32             minimum;    /* Note signedness */
    __s32             maximum;
    __s32             step;
    __s32             default_value;
    __u32                flags;
    __u32             reserved[2];
};

2、獲得當前值

struct v4l2_control c;
c.id = V4L2_CID_BRIGHTNESS; // V4L2_CID_BASE+0;
ioctl(h->fd, VIDIOC_G_CTRL, &c);


/*
 *    C O N T R O L S
 */
struct v4l2_control {
    __u32             id;
    __s32             value;
};

3、設定

struct v4l2_control c;
c.id = V4L2_CID_BRIGHTNESS; // V4L2_CID_BASE+0;
c.value = 99;
ioctl(h->fd, VIDIOC_S_CTRL, &c);

理解介面:

0
SU:select unit,選擇輸入源
PU:processiong unit,用於調整亮度、對比度、色度等
EU:encoding unit,對採集到的資料進行個性化處理的功能
0
ID會傳遞給PU
app->驅動->(id, val)->硬體

操作方法:

使用ioctl操作裝置節點/dev/video0時,不同的ioctl操作可能是video control介面,或者video streaming介面
跟影片流相關的操作,比如VIDIOC_ENUM_FMT,VIDIOC_G_FMT,VIDIOC_S_FMT,VIDIOC_STREAMON,VIDIOC_STREAMOFF,是操作video streaming的介面
其他的介面,大多是video control介面
從驅動和硬體角度來看,要操作video control介面,需要指明:
1、entity:要操作的是那個terminal或者unit,比如PU
2、control selector:要操作entity裡面的那個控制項,比如亮度
3、控制項裡面的哪些位:比如camera terminal裡的CT_PANTILT_RELATIVE_CONTROL控制項對應的32位資料,其中前16位對應PAN控制(左右轉動),後16位對應TILE控制(上下轉動)
但是應用程式不關心這些,使用一個ID來指定entity、control selector、哪些位:
struct v4l2_control c;
c.id = V4L2_CID_BRIGHTNESS;    // V4L2_CID_BASE+0;
c.value = 99;
ioctl(h->fd, VIDIOC_S_CTRL, &c);

/*
 *    C O N T R O L S
 */
struct v4l2_control {
    __u32             id;
    __s32             value;
};

支援V4L2_CAP_STREAMING就可以用mmap函式,不支援的話就只能用V4L2_CAP_READWRITE了

相關文章