嵌入式-v4l2攝像頭的工作流程及ioctl功能詳解
一.攝像頭的工作流程
1.開啟裝置檔案
int fd=open("/dev/video0",O_RDWR);
2.取得裝置的capability,看看裝置具體支援哪些功能,比如是否具有視訊的輸入或者音訊的輸入等等ioctl(fd_v4l, VIDIOC_QUERYCAP, &cap)
3. 設定視訊採集的引數:
設定視訊的制式,制式包括PAL/NTSC,使用 ioctl(fd_v4l, VIDIOC_S_STD, &std_id)
設定視訊影象的採集視窗的大小,使用 ioctl(fd_v4l, VIDIOC_S_CROP, &crop)
設定視訊幀格式,包括幀的點陣格式,寬度和高度等,使用 ioctl(fd_v4l, VIDIOC_S_FMT, &fmt)
設定視訊的幀率,使用 ioctl(fd_v4l, VIDIOC_S_PARM, &parm)
設定視訊的旋轉方式,使用 ioctl(fd_v4l, VIDIOC_S_CTRL, &ctrl)
4.向驅動申請視訊的幀緩衝區,一般不會超過5個ioctl(fd_v4l, VIDIOC_REQBUFS, &req)
查詢幀緩衝區在核心空間中的長度和偏移量 ioctl(fd_v4l, VIDIOC_QUERYBUF, &buf)
5.實體記憶體對映
將幀緩衝區的地址對映到使用者空間,這樣就可以直接操作採集到的幀了,而不必去複製。
buffers[i].start = mmap (NULL, buffers[i].length, PROT_READ | PROT_WRITE, MAP_SHARED, fd_v4l, buffers[i].offset);
6. 將申請到的幀緩衝全部放入視訊採集輸出佇列,以便存放採集的資料。ioctl (fd_v4l, VIDIOC_QBUF, &buf)
7. 開始視訊流資料的採集。 ioctl (fd_v4l, VIDIOC_STREAMON, &type)
8. 驅動將採集到的一幀視訊資料存入輸入佇列第一個幀緩衝區,存完後將該幀緩衝區移至視訊採集輸出佇列。
9. 應用程式從視訊採集輸出佇列中取出已含有采集資料的幀緩衝區。ioctl (fd_v4l, VIDIOC_DQBUF, &buf) ,應用程式處理該幀緩衝區的原始視訊資料。
10. 處理完後,應用程式的將該幀緩衝區重新排入輸入佇列,這樣便可以迴圈採集資料。ioctl (fd_v4l, VIDIOC_QBUF, &buf)重複上述步驟8到10,直到停止採集資料。
11. 停止視訊的採集。ioctl (fd_v4l, VIDIOC_STREAMOFF, &type)
12. 釋放申請的視訊幀緩衝區 unmap,關閉視訊裝置檔案 close(fd_v4l)。
二.攝像頭ioctl功能詳解
1.ioctl(fd_v4l, VIDIOC_S_PARM, &parm)
功能:獲取或設定資料流的引數,一般設定幀率等引數(幀率(Frame rate)是用於測量顯示幀數的量度。所謂的測量單位為每秒顯示幀數(Frames per Second,簡稱:FPS)或”赫茲”(Hz)。)
引數:
(1)fd_v4l:開啟裝置檔案
(2)VIDIOC_S_PARM:cmd傳參
(3)&parm:struct v4l2_streamparm currentparm;
struct v4l2_streamparm {
__u32 type;/*type 涉及的操作的型別,對於視訊捕獲裝置,應該為V4L2_BUF_TYPE_VIDEO_CAPTURE;對於輸出裝置應該為 V4L2_BUF_TYPE_VIDEO_OUTPUT。 它的值也可以是V4L2_BUF_TYPE_PRIVATE,在這種情況下,raw_data字 段用來傳遞一些私有的,不可移植的,甚至是不鼓勵的資料給核心 。*/
union {
struct v4l2_captureparm capture;/*對於捕獲裝置而言,parm.capture欄位是要關注的內容*/
struct v4l2_outputparm output;
__u8 raw_data[200]; /* user-defined */
} parm;
};
struct v4l2_captureparm {
__u32 capability; /* capability 一組功能標籤,目前為止已經定義的只有一個V4L2_CAP_TIMEPERFRAME,它代表可以改變幀頻率 */
__u32 capturemode; /* capturemode也是一個只定義了一個標籤的欄位:V4L2_MODE_HIGHQUALITY,這個標籤意在使硬體在高清模式下工作,實現單幀的捕獲。
這個模式可以做出任何的犧牲 (包括支援的格式,曝光時間等),以達到裝置可以處理的最佳圖片質量。 */
struct v4l2_fract timeperframe; /* timeperframe欄位用於指定想要使用的幀頻率 */
__u32 extendedmode; /* extendedmode,它在API中沒有明確的意義。 */
__u32 readbuffers; /* readbuffers欄位是read()操作被呼叫時核心應為輸入的幀準備的緩衝區數量。 */
__u32 reserved[4];
};
struct v4l2_fract {
__u32 numerator;
__u32 denominator;
};
/*numerator和denominator所描述的係數給出的是成功的幀之間的時間間隔。*/
struct v4l2_outputparm {
__u32 capability; /* capability 一組功能標籤,目前為止已經定義的只有一個V4L2_CAP_TIMEPERFRAME,它代表可以改變幀頻率 */
__u32 outputmode; /* capturemode也是一個只定義了一個標籤的欄位:V4L2_MODE_HIGHQUALITY,這個標籤意在使硬體在高清模式下工作,實現單幀的捕獲。
這個模式可以做出任何的犧牲 (包括支援的格式,曝光時間等),以達到裝置可以處理的最佳圖片質量。 */
struct v4l2_fract timeperframe; /* timeperframe欄位用於指定想要使用的幀頻率 */
__u32 extendedmode; /* extendedmode,它在API中沒有明確的意義。 */
__u32 writebuffers; /* readbuffers欄位是read()操作被呼叫時核心應為輸入的幀準備的緩衝區數量。 */
__u32 reserved[4];
};
註釋:幀率資訊定義在v4l2_streamparm結構體中,parm.capture.timeperframe.denominator就是幀率(frame/s),
每幀的時間是1000ms/parm.capture.timeperframe.denominator。幀率的設定不是隨意的,各個解析度下硬體支援
的解析度各不相同,硬體會選擇最接近的幀率。比如640*480幀率33,800*600只有15,1920*1080只有5了。
當應用想要查詢現在的引數時,它會發出一個VIDIOC_G_PARM呼叫,因而呼叫驅動的vidioc_g_parm()方法。驅動應該提供現在的設定,不用的話確保將extendedmode設為0,並且把reserved欄位永遠設為0.
設定引數將呼叫vidioc_s_parm()。在這種情況下,驅動將引數設為與應用所提供的引數儘可能近的值,並調整v4l2_streamparm結構 體以反應現行使用的值 。例如,應用可以會請求一個比硬體所能提供的更高的幀頻率,在這種情況下,幀頻率會設為最高,並把imeperframe設為這個最高的幀頻率。
如果應用提供timeperframe為0,the driver should program the nominal frame rate associated with the current video norm.(這句不懂)。如果readbuffers 或writebuffers是0,驅動應返回現行設定而不是刪除緩衝區。
到現在為止,我們已經可以寫一個支援read()和write()方式幀傳輸的簡單的驅動了。
2.ioctl(fd_v4l, VIDIOC_S_INPUT, &g_input)
功能:選擇cmos影象傳到攝像頭介面之後經過的處理通道
引數:(1)fd_v4l:開啟裝置檔案
(2)VIDIOC_S_INPUT:cmd傳參
(3)&g_input:int型別
註釋:一個video 裝置節點可能對應多個視訊源,比如saf7113 可以最多支援四路cvbs 輸入,如果上層想在四個cvbs
視訊輸入間切換,那麼就要呼叫ioctl(fd, VIDIOC_S_INPUT, &input) 來切換。
3.ioctl(fd_v4l, VIDIOC_S_CROP, &crop)
功能:設定矩形區域–修剪功能,這個區域是capture或者overlap模式下的取景範圍
引數:(1)fd_v4l:開啟裝置檔案
(2)VIDIOC_S_CROP:cmd傳參
(3)&crop:struct v4l2_crop
struct v4l2_crop {
__u32 type; /* type 涉及的操作的型別 */
struct v4l2_rect c;/*設定矩形長寬高*/
};
struct v4l2_rect {
__s32 left;
__s32 top;
__s32 width;
__s32 height;
};
4.ioctl(fd_v4l, VIDIOC_G_CROP, &crop)
功能:獲取矩形區域–修剪功能
引數:(1)fd_v4l:開啟裝置檔案
(2)VIDIOC_G_CROP:cmd傳參
(3)&crop:struct v4l2_crop
struct v4l2_crop {
__u32 type; /* type 涉及的操作的型別 */
struct v4l2_rect c;/*獲取矩形長寬高*/
};
5.ioctl(fd_v4l, VIDIOC_S_FMT, &fmt)
功能:設定當前驅動的是視訊捕獲格式
引數:(1)fd_v4l:開啟裝置檔案
(2)VIDIOC_S_FMT:cmd傳參
(3)&fmt:struct v4l2_format
struct v4l2_format {//結構體用來設定攝像頭的視訊制式、幀格式等
__u32 type;/*緩衝型別*/
union {
struct v4l2_pix_format pix; // 視訊裝置使用
struct v4l2_pix_format_mplane pix_mp; /* V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE */
struct v4l2_window win; /*這是video overlay使用的結構體 */
struct v4l2_vbi_format vbi; /* V4L2_BUF_TYPE_VBI_CAPTURE */
struct v4l2_sliced_vbi_format sliced; /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */
__u8 raw_data[200]; /* user-defined */
} fmt;
};
struct v4l2_pix_format{
__u32 width; //寬
__u32 height; //高
__u32 pixelformat;//圖素格式, rgb565 rgb888 yuv422 yuv420等等
enum v4l2_field field; //取樣區域,如隔行取樣,是否逐行掃描,是否隔行掃描. Sam通常採用V4L2_FIELD_NONE,逐行放置資料
__u32 bytesperline; //表明緩衝區中有多少位元組用於表示影象中一行畫素的所有畫素值。
//由於一個畫素可能有多個位元組表示,所以 bytesPerLine 可能是欄位 width 值的若干倍
__u32 sizeimage;//影象大小
enum v4l2_colorspace colorspace;//色彩空間 SMPTE170M等 http://linuxtv.org/downloads/v4l-dvb-apis/colorspaces.html介紹了轉換方法
//http://vektor.theorem.ca/graphics/ycbcr/介紹
__u32 priv; /* private data, depends on pixelformat */
};
struct v4l2_pix_format_mplane {
__u32 width;//寬
__u32 height;//高
__u32 pixelformat;//畫素格式
__u32 field; //見struct v4l2_pix_format
__u32 colorspace; //struct v4l2_pix_format
struct v4l2_plane_pix_format plane_fmt[VIDEO_MAX_PLANES];//每平面資訊
__u8 num_planes;//此格式的平面數
__u8 reserved[11];
} __attribute__ ((packed));
struct v4l2_window {
struct v4l2_rect w;//視窗位置
__u32 field; //如下field詳說
__u32 chromakey;//色度:色調和飽和度
struct v4l2_clip __user *clips;//剪下
__u32 clipcount;//剪下計數
void __user *bitmap;//每個位對應覆蓋影象的一個畫素,此位置一時對應畫素才顯示。
__u8 global_alpha;//全域性alpha值
};
struct v4l2_vbi_format {
__u32 sampling_rate;
__u32 offset;
__u32 samples_per_line;
__u32 sample_format;
__s32 start[2];
__u32 count[2];
__u32 flags;
__u32 reserved[2];
};
struct v4l2_sliced_vbi_format {
__u16 service_set;
/* service_lines[0][...] specifies lines 0-23 (1-23 used) of the first field
service_lines[1][...] specifies lines 0-23 (1-23 used) of the second field
(equals frame lines 313-336 for 625 line video
standards, 263-286 for 525 line standards) */
__u16 service_lines[2][24];
__u32 io_size;
__u32 reserved[2]; /* must be zero */
};
6.ioctl(fd_v4l, VIDIOC_S_CTRL, &ctrl)
功能:設定指定control的值設定視訊的旋轉方式
引數:(1)fd_v4l:開啟裝置檔案
(2)VIDIOC_S_CTRL:cmd傳參
(3)&ctrl:struct v4l2_control
struct v4l2_control {
__u32 id;
__s32 value;
};
註釋:
曝光模式:
struct v4l2_control ctrl;
ctrl.id = V4L2_CID_EXPOSURE_AUTO;
ret = ioctl(Handle, VIDIOC_G_CTRL, &ctrl);
ctrl.value 則由Driver填寫。告知當前曝光模式。
有以下幾個選擇:
enum v4l2_exposure_auto_type {
V4L2_EXPOSURE_AUTO = 0,
V4L2_EXPOSURE_MANUAL = 1,
V4L2_EXPOSURE_SHUTTER_PRIORITY = 2,
V4L2_EXPOSURE_APERTURE_PRIORITY = 3
};
曝光:
struct v4l2_control ctrl;
ctrl.id = V4L2_CID_EXPOSURE_ABSOLUTE;
ret = ioctl(Handle, VIDIOC_G_CTRL, &ctrl);
同樣,driver填寫ctrl.value. 內容為曝光值。
增益:
struct v4l2_control ctrl;
ctrl.id = V4L2_CID_GAIN;
ret = ioctl(Handle, VIDIOC_G_CTRL, &ctrl);
同樣,driver填寫ctrl.value. 內容為增益。
7.ioctl(fd_v4l, VIDIOC_REQBUFS, &req)
功能:請求V4L2驅動分配視訊緩衝區(申請V4L2視訊驅動分配記憶體),V4L2是視訊裝置的驅動層位於
核心空間,所以通過VIDIOC_REQBUFS控制命令字申請的記憶體位於核心空間,應用程式不能直接訪問
需要通過呼叫mmap記憶體對映函式把核心空間記憶體對映到使用者空間後,應用程式通過訪問使用者空間地址來
訪問核心空間。
引數:(1)fd_v4l:開啟裝置檔案
(2)VIDIOC_REQBUFS:cmd傳參
(3)&req:struct v4l2_requestbuffers
struct v4l2_requestbuffers {
__u32 count;//當memory=V4L2_MEMORY_MMAP時,此處才有效。表明要申請的buffer個數。
__u32 type; //Stream 或者Buffer的型別。此處肯定為V4L2_BUF_TYPE_VIDEO_CAPTURE
__u32 memory; //既然是Memory Mapping模式,則此處設定為:V4L2_MEMORY_MMAP
__u32 reserved[2];
};
註釋:有兩種方式的I/O。 Memory Mapping 和User Pointer
Memory Mapping的Buffer由Driver申請為物理連續的記憶體空間(Kernel空間)。
在此ioctl呼叫時被分配,需要早於mmap()動作將他們對映到使用者空間。
8.ioctl(fd_v4l, VIDIOC_QUERYBUF, &buf)
功能:查詢已經分配的V4L2的視訊緩衝區的相關資訊,包括視訊緩衝區的使用狀態、在核心空間的偏移地址、緩衝區長度等。
在應用程式設計中通過調VIDIOC_QUERYBUF來獲取核心空間的視訊緩衝區資訊,然後呼叫函式mmap把核心空間地址對映到使用者空間,
這樣應用程式才能夠訪問位於核心空間的視訊緩衝區。
引數:(1)fd_v4l:開啟裝置檔案
(2)VIDIOC_QUERYBUF:cmd傳參
(3)&buf:struct v4l2_buffer
struct v4l2_buffer {
__u32 index; //buffer 序號
__u32 type;//buffer 型別
__u32 bytesused;//buffer 中已使用的位元組數
__u32 flags;// 區分是MMAP 還是USERPTR
__u32 field;
struct timeval timestamp;// 獲取第一個位元組時的系統時間
struct v4l2_timecode timecode;
__u32 sequence;// 佇列中的序號
/* memory location */
__u32 memory; //IO 方式,被應用程式設定
union {
__u32 offset;// 緩衝幀地址,只對MMAP 有效
unsigned long userptr;
struct v4l2_plane *planes;
__s32 fd;
} m;
__u32 length;// 緩衝幀長度
__u32 reserved2;
__u32 reserved;
};
9.ioctl (fd_v4l, VIDIOC_STREAMON, &type)
功能:啟動視訊採集命令,應用程式呼叫VIDIOC_STREAMON啟動視訊採集命令後,視訊裝置驅動程
序開始採集視訊資料,並把採集到的視訊資料儲存到視訊驅動的視訊緩衝區中。
引數:(1)fd_v4l:開啟裝置檔案
(2)VIDIOC_STREAMON:cmd傳參
(3)&type:int型別
11.ioctl (fd_v4l, VIDIOC_QBUF, &buf)
功能:投放一個空的視訊緩衝區到視訊緩衝區輸入佇列中,指令(指定)的視訊緩衝區進入視訊輸入佇列,在啟動視訊裝置拍攝影象時,相應的視訊
資料被儲存到視訊輸入佇列相應的視訊緩衝區中。
引數:(1)fd_v4l:開啟的裝置檔案
(2)VIDIOC_QBUF:cmd傳參
(3)&buf:struct v4l2_buffer
struct v4l2_buffer {
__u32 index; //快取編號
__u32 type; //視訊捕獲模式
__u32 bytesused; //快取已使用空間大小
__u32 flags; //快取當前狀態常見值有 V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE,分別代表當前快取已經 對映、快取可以採集資料、快取可以提取資料)
__u32 field; //影象存在緩衝區的那一個區域
struct timeval timestamp; //輸入裝置來說,代表幀捕獲的時間.對輸出裝置來說,在沒有到達時間戳所代表的時間前,驅動不可以把幀傳送出去;
struct v4l2_timecode timecode; //存放時間編碼
__u32 sequence; //存入現行序號
/* memory location */
__u32 memory; //緩衝是記憶體對映的還是使用者空間的
union {
__u32 offset; //緩衝區的位置
unsigned long userptr; //緩衝區的使用者空間地址。
struct v4l2_plane *planes;
__s32 fd;
} m;
__u32 length;
__u32 reserved2;
__u32 reserved;
};
12.ioctl (fd_v4l, VIDIOC_DQBUF, &buf)
功能:從視訊緩衝區的輸出佇列中取得一個已經儲存有一幀視訊資料的視訊緩衝區;相應的核心視訊緩衝區中儲存有當前拍攝
到的視訊資料,應用程式可以通過訪問使用者空間來讀取該視訊資料。
引數:(1)fd_v4l:開啟的裝置檔案
(2)VIDIOC_DQBUF:cmd傳參
(3)&buf:struct v4l2_buffer
struct v4l2_buffer {
__u32 index; //快取編號
__u32 type; //視訊捕獲模式
__u32 bytesused; //快取已使用空間大小
__u32 flags; //快取當前狀態常見值有 V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE,分別代表當前快取已經 對映、快取可以採集資料、快取可以提取資料)
__u32 field; //影象存在緩衝區的那一個區域
struct timeval timestamp; //輸入裝置來說,代表幀捕獲的時間.對輸出裝置來說,在沒有到達時間戳所代表的時間前,驅動不可以把幀傳送出去;
struct v4l2_timecode timecode; //存放時間編碼
__u32 sequence; //存入現行序號
/* memory location */
__u32 memory; //緩衝是記憶體對映的還是使用者空間的
union {
__u32 offset; //緩衝區的位置
unsigned long userptr; //緩衝區的使用者空間地址。
struct v4l2_plane *planes;
__s32 fd;
} m;
__u32 length;
__u32 reserved2;
__u32 reserved;
};
- ioctl (fd_v4l, VIDIOC_STREAMOFF, &type)
功能:停止視訊採集命令,應用程式呼叫VIDIOC_ STREAMOFF停止視訊採集命令後,視訊裝置驅動程
序不在採集視訊資料。
引數:(1)fd_v4l:開啟裝置檔案
(2)VIDIOC_STREAMOFF:cmd傳參
(3)&type:int型別