目錄
一、V4L2簡介
二、V4L2操作流程
?1.打開攝像頭
2.查詢設備的屬性/能力/功能
3.獲取攝像頭支持的格式
4.設置攝像頭的采集通道
5.設置/獲取攝像頭的采集格式和參數(shù)
6.申請幀緩沖、內(nèi)存映射、入隊
(1)申請幀緩沖
(2)內(nèi)存映射
(3)入隊
7.開啟視頻采集
8.讀取數(shù)據(jù)、對數(shù)據(jù)進行處理
9.結(jié)束視頻采集
三、應用編程
一、V4L2簡介
V4L2(Video for linux two)是 Linux 內(nèi)核中視頻類設備的一套驅(qū)動框架,為視頻類設備驅(qū)動開發(fā)和應用層提供了一套統(tǒng)一的接口規(guī)范。使用 V4L2 設備驅(qū)動框架注冊的設備會在 Linux 系統(tǒng)/dev/目錄下生成對應的設備節(jié)點文件,設備節(jié)點的名稱通常為 videoX(X 標準一個數(shù)字編號:/dev/videox),每一個 videoX 設備文件就代表一個視頻類設備。應用程序通過對 videoX 設備文件進行 I/O 操作來配置、使用設備類設備。
?
二、V4L2操作流程
V4L2攝像頭驅(qū)動框架的訪問是通過系統(tǒng)IO的接口 ------ ioctl函數(shù),ioctl專用于硬件控制的系統(tǒng)IO的接口。
#include <sys/ioctl.h>? ? ?//包含頭文件
int ioctl(int fd, unsigned long request, ...);
fd: 文件描述符
request: 此參數(shù)與具體要操作的對象有關,?表示向文件描述符請求相應的操作
...: 可變參函數(shù), 第三個參數(shù)需要根據(jù) request 參數(shù)來決定,配合 request 來使用
返回值: 成功返回 0,失敗返回-1
ioctl()是一個文件 IO 操作的雜物箱,可以處理的事情非常雜、不統(tǒng)一,一般用于操作特殊文件或硬件外設,可以通過 ioctl 獲取外設相關信息。通過 ioctl()來完成,搭配不同的 V4L2 指令(request
參數(shù))請求不同的操作,這些指令定義在頭文件 linux/videodev2.h 中,常用的如下圖。
?1.打開攝像頭
視頻類設備對應的設備節(jié)點為/dev/videoX, X 為數(shù)字編號,通常從 0 開始,調(diào)用 open 打開,得到文件描述符 fd。
int fd = -1;
fd = open("/dev/video0", O_RDWR);
if (0 > fd)
{
perror( "open error");
exit(-1);
}
2.查詢設備的屬性/能力/功能 ?
打開設備后,需要查詢設備的屬性,確定該設備是否是一個視頻采集類設備。通過 ioctl()將獲取到一個 struct v4l2_capability 類型數(shù)據(jù), struct v4l2_capability 數(shù)據(jù)結(jié)構(gòu)描述了設備的一些屬性,結(jié)構(gòu)體定義如下:
struct v4l2_capability {
__u8 driver[16]; /* 驅(qū)動的名字 */
__u8 card[32]; /* 設備的名字 */
__u8 bus_info[32]; /* 總線的名字 */
__u32 version; /* 版本信息 */
__u32 capabilities; /* 設備擁有的能力 */
__u32 device_caps;
__u32 reserved[3]; /* 保留字段 */
};
這里關注capabilities 字段,該字段描述了設備擁有的能力,該字段的值如下
所以可以通過判斷 capabilities字段是否包含 V4L2_CAP_VIDEO_CAPTURE、 來確定它是否是一個攝像頭設備。
struct v4l2_capability cap = {};
ioctl(fd,VIDIOC_QUERYCAP,&cap);
if(cap.capabilities&V4L2_CAP_VIDEO_CAPTURE)
{
//是一個攝像頭
}
3.獲取攝像頭支持的格式
獲取支持的像素格式使用 VIDIOC_ENUM_FMT 指令
struct v4l2_fmtdesc {
__u32 index; /*格式編號*/
__u32 type; /*攝像頭的格式 V4L2_BUF_TYPE_VIDEO_CAPTURE*/
__u32 flags;
__u8 description[32]; /*描述信息:描述 pixelformat 像素格式。*/
__u32 pixelformat; /*類型格式 --- 4字節(jié):像素格式編號*/
__u32 reserved[4];
};
pixelfoemat像素格式:
//定義格式的宏
#define v4l2_fourcc(a, b, c, d)\
((__u32)(a) | ((__u32)(b) << 8) | ((__u32)(c) << 16) | ((__u32)(d) << 24))
#define V4L2_PIX_FMT_YUYV v4l2_fourcc('Y', 'U', 'Y', 'V') /* 16 YUV 4:2:2 */
#define V4L2_PIX_FMT_YVYU v4l2_fourcc('Y', 'V', 'Y', 'U') /* 16 YVU 4:2:2 */
#define V4L2_PIX_FMT_MJPEG v4l2_fourcc('M', 'J', 'P', 'G') /* Motion-JPEG */
#define V4L2_PIX_FMT_JPEG v4l2_fourcc('J', 'P', 'E', 'G') /* JFIF JPEG */
v4l2_fourcc是 宏定義,通過這個宏以及對應的參數(shù)合成的一個u32 類型數(shù)據(jù)。
type類型:
獲取設備的哪種功能對應的像素格式, 因為有些設備它可能即支持視頻采集功能、又支持視頻輸出等其它的功能;
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,
V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY = 8,
V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE = 9,
V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE = 10,
V4L2_BUF_TYPE_SDR_CAPTURE = 11,
V4L2_BUF_TYPE_SDR_OUTPUT = 12,
V4L2_BUF_TYPE_META_CAPTURE = 13,
/* Deprecated, do not use */
V4L2_BUF_TYPE_PRIVATE = 0x80,
};
type 字 段 需 要 在 調(diào) 用 ioctl() 之 前 設 置 它 的 值 , 對 于 攝 像 頭 , 需 要 將 type 字 段 設 置 為V4L2_BUF_TYPE_VIDEO_CAPTURE,指定將要獲取的是視頻采集的像素格式。
struct v4l2_fmtdesc fmt = {};
fmt.index = 0;//第一種格式
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//獲取攝像頭的格式
ioctl(fd,VIDIOC_ENUM_FMT,&fmt);
4.設置攝像頭的采集通道
int index = 0;//使用通道0
ioctl(fd,VIDIOC_S_INPUT,&index);
5.設置/獲取攝像頭的采集格式和參數(shù)
使用 VIDIOC_G_FMT 指令查看設備當期的格式和使用 VIDIOC_S_FMT 指令設置設備的格式;
int ioctl(int fd, VIDIOC_G_FMT, struct v4l2_format *fmt);
int ioctl(int fd, VIDIOC_S_FMT, struct v4l2_format *fmt);
ioctl()會將獲取到的數(shù)據(jù)寫入到 fmt 指針所指向的對象中或者會使用 fmt 所指對象的數(shù)據(jù)去設置設備的格式, struct v4l2_format 結(jié)構(gòu)體描述了格式相關的信息。
struct v4l2_format {
__u32 type;//V4L2_BUF_TYPE_VIDEO_CAPTURE
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;
};
struct v4l2_pix_format {
__u32 width;//像素寬度
__u32 height;//像素高度
__u32 pixelformat;//采集格式 V4L2_PIX_FMT_YVYU
__u32 field; /* V4L2_FIELD_NONE */
__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 */
};
type 字段依然與前面介紹的結(jié)構(gòu)體中的 type 字段意義相同,不管是獲取格式、還是設置格式都需要在調(diào)用 ioctl()函數(shù)之前設置它的值。接下來是一個 union 共用體,當 type 被設置為V4L2_BUF_TYPE_VIDEO_CAPTURE 時, pix 變量生效,它是一個 struct v4l2_pix_format 類型變量,記錄了視頻幀格式相關的信息。
獲取當前的格式、并設置格式:
struct v4l2_format format = {};
format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
format.fmt.pix.width = 640;
format.fmt.pix.height = 480;
format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
format.fmt.pix.field= V4L2_FIELD_NONE ;
ioctl(fd,VIDIOC_S_FMT,&format); //S:set
6.申請幀緩沖、內(nèi)存映射、入隊
(1)申請幀緩沖
讀取攝像頭數(shù)據(jù)的方式有兩種:一種是 read 方式,直接通過 read()系統(tǒng)調(diào)用讀取攝像頭采集到的數(shù)據(jù);另一種是 streaming 方式。使用 VIDIOC_QUERYCAP 指令查詢設備的屬性、得到一個 struct v4l2_capability 類型數(shù)據(jù), 其中 capabilities 字段記錄了設備擁有的能力,當該字段包含
V4L2_CAP_READWRITE 時,表示設備支持 read I/O 方式讀取數(shù)據(jù);當該字段包含V4L2_CAP_STREAMING時,表示設備支持 streaming I/O 方式;使用streaming I/O 方式,需要向設備申請幀緩沖,并將幀緩沖映射到應用程序進程地址空間中。
?使用 VIDIOC_REQBUFS 指令可申請幀緩沖:
ioctl(int fd, VIDIOC_REQBUFS, struct v4l2_requestbuffers *reqbuf);
調(diào)用 ioctl()需要傳入一個 struct v4l2_requestbuffers *指針, struct v4l2_requestbuffers 結(jié)構(gòu)體描述了申請幀緩沖的信息, ioctl()會根據(jù) reqbuf 所指對象填充的信息進行申請。
?
struct v4l2_requestbuffers {
__u32 count;//緩沖區(qū)塊數(shù) ----- 4
__u32 type; /* enum v4l2_buf_type */
__u32 memory; /* enum v4l2_memory */
__u32 reserved[2];
};
type 字段與前面所提及到的 type 字段意義相同,count 字段用于指定申請幀緩沖的數(shù)量。
memory 字段可取值如下:
enum v4l2_memory {
V4L2_MEMORY_MMAP = 1,
V4L2_MEMORY_USERPTR = 2,
V4L2_MEMORY_OVERLAY = 3,
V4L2_MEMORY_DMABUF = 4,
};
通常將 memory 設置為 V4L2_MEMORY_MMAP 。
申請緩存:
struct v4l2_requestbuffers req = {};
req.count = 4; //申請4個幀緩存
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
ioctl(fd,VIDIOC_REQBUFS,&req);
streaming I/O 方式會在內(nèi)核空間中維護一個幀緩沖隊列, 驅(qū)動程序會將從攝像頭讀取的一幀數(shù)據(jù)寫入到隊列中的一個幀緩沖,接著將下一幀數(shù)據(jù)寫入到隊列中的下一個幀緩沖;當應用程序需要讀取一幀數(shù)據(jù)時,需要從隊列中取出一個裝滿一幀數(shù)據(jù)的幀緩沖,這個取出過程就叫做出隊;當應用程序處理完這一幀數(shù)據(jù)后,需要再把這個幀緩沖加入到內(nèi)核的幀緩沖隊列中,這個過程叫做入隊。
使用 VIDIOC_REQBUFS 指令申請幀緩沖, 該緩沖區(qū)是由內(nèi)核所維護的,應用程序不能直接讀取該緩沖區(qū)的數(shù)據(jù),需要將其映射到用戶空間中,應用程序讀取映射區(qū)的數(shù)據(jù)實際上是讀取內(nèi)核維護的幀緩沖中的數(shù)據(jù)。
(2)內(nèi)存映射
在映射之前,需要查詢幀緩沖的信息:幀緩沖的長度、偏移量。使用VIDIOC_QUERYBUF指令查詢:
ioctl(int fd, VIDIOC_QUERYBUF, struct v4l2_buffer *buf);
調(diào)用 ioctl()需要傳入一個 struct v4l2_buffer *指針, struct v4l2_buffer 結(jié)構(gòu)體描述了幀緩沖的信息, ioctl()會將獲取到的數(shù)據(jù)寫入到 buf 指針所指的對象中。
struct v4l2_buffer {
__u32 index;//編號
__u32 type;//V4L2_BUF_TYPE_VIDEO_CAPTURE
__u32 bytesused;//使用的字節(jié)數(shù)
__u32 flags;
__u32 field;
struct timeval timestamp;
struct v4l2_timecode timecode;
__u32 sequence;
/* memory location */
__u32 memory;//V4L2_MEMORY_MMAP
union {
__u32 offset;//偏移
unsigned long userptr;
struct v4l2_plane *planes;
__s32 fd;
} m;
__u32 length;//長度
__u32 reserved2;
__u32 reserved;
};
index 字段表示一個編號, 申請的多個幀緩沖、 每一個幀緩沖都有一個編號,從 0 開始。一次 ioctl()調(diào)用只能獲取指定編號對應的幀緩沖的信息,所以要獲取多個幀緩沖的信息,需要重復調(diào)用多次,每調(diào)用一次ioctl()、 index 加 1,指向下一個幀緩沖。
type 字段和memory 字段與前面介紹的一樣。length 字段表示幀緩沖的長度,共同體中的 offset 表示幀緩沖的偏移量。因為應用程序通過 VIDIOC_REQBUFS 指令申請幀緩沖時,內(nèi)核會向操作系統(tǒng)申請一塊內(nèi)存空間作為幀緩沖區(qū),內(nèi)存空間的大小就等于申請的幀緩沖數(shù)量 * 每一個幀緩沖的大小,每一個幀緩沖對應到這一塊內(nèi)存空間的某一段,所以它們都有一個地址偏移量。
?
申請幀緩沖后、調(diào)用 mmap()將幀緩沖映射到用戶地址空間
?
struct v4l2_buffer buf = {};
buf.index = xxx;//0~3
but.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
ioctl(fd,VIDIOC_QUERYBUF,&buf);
//映射到用戶空間
mmap(NULL,buf.length,.......,fd,buf.m.offset);
(3)入隊
使用 VIDIOC_QBUF 指令將幀緩沖放入到內(nèi)核的幀緩沖隊列中,調(diào)用 ioctl()之前,需要設置 struct v4l2_buffer 類型對象的 memory、 type 字段
ioctl(fd,VIDIOC_QBUF,&buf);
7.開啟視頻采集
使用 VIDIOC_DQBUF 指令開啟視頻采集,
ioctl(int fd, VIDIOC_STREAMON, int *type); //開啟視頻采集
ioctl(int fd, VIDIOC_STREAMOFF, int *type); //停止視頻采集
enum v4l2_buf_type buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(fd,VIDIOC_STREAMON,&buf_type);
8.讀取數(shù)據(jù)、對數(shù)據(jù)進行處理
開啟視頻采集之后,便可以去讀取數(shù)據(jù)?,直接讀取每一個幀緩沖的在用戶空間的映射區(qū)即可讀取到攝像頭采集的每一幀圖像數(shù)據(jù)。在讀取數(shù)據(jù)之前,需要將幀緩沖從內(nèi)核的幀緩沖隊列中取出,這個操作叫做幀緩沖出隊。幀緩沖出隊,便可讀取數(shù)據(jù),對數(shù)據(jù)進行處理:將攝像頭采集的圖像顯示到 LCD屏上;數(shù)據(jù)處理完成之后,再將幀緩沖入隊,往復操作。
使用 VIDIOC_DQBUF 指令執(zhí)行出隊操作文章來源:http://www.zghlxwxcb.cn/news/detail-482779.html
ioctl(int fd, VIDIOC_DQBUF, struct v4l2_buffer *buf);
while(1){
//從采集隊列中取出一幀
ioctl(fd,VIDIOC_DQBUF,&buf);
//將該幀的數(shù)據(jù)拷貝走
memcpy(....);
//將取出的一幀放回隊列
ioctl(fd,VIDIOC_QBUF,&buf);
//顯示,存儲......
}
9.結(jié)束視頻采集
結(jié)束視頻采集,使用 VIDIOC_STREAMOFF 指令文章來源地址http://www.zghlxwxcb.cn/news/detail-482779.html
enum v4l2_buf_type buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(fd,VIDIOC_STREAMOFF,&buf_type);
//解除映射
//關閉設備文件
三、應用編程
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#include <sys/mman.h>
#include <string.h>
typedef struct{
char *start;
size_t length;
}buffer_t;
buffer_t buffer[4];
buffer_t current;//保存當前取出的一幀
int lcd_fd;
int *memp;
unsigned int sign3 = 0;
int yuyv2rgb(int y, int u, int v)
{
unsigned int pixel24 = 0;
unsigned char *pixel = (unsigned char *)&pixel24;
int r, g, b;
static int ruv, guv, buv;
if(sign3)
{
sign3 = 0;
ruv = 1159*(v-128);
guv = 380*(u-128) + 813*(v-128);
buv = 2018*(u-128);
}
r = (1164*(y-16) + ruv) / 1000;
g = (1164*(y-16) - guv) / 1000;
b = (1164*(y-16) + buv) / 1000;
if(r > 255) r = 255;
if(g > 255) g = 255;
if(b > 255) b = 255;
if(r < 0) r = 0;
if(g < 0) g = 0;
if(b < 0) b = 0;
pixel[0] = r;
pixel[1] = g;
pixel[2] = b;
return pixel24;
}
int yuyv2rgb0(unsigned char *yuv, unsigned char *rgb, unsigned int width, unsigned int height)
{
unsigned int in, out;
int y0, u, y1, v;
unsigned int pixel24;
unsigned char *pixel = (unsigned char *)&pixel24;
unsigned int size = width*height*2;
for(in = 0, out = 0; in < size; in += 4, out += 6)
{
y0 = yuv[in+0];
u = yuv[in+1];
y1 = yuv[in+2];
v = yuv[in+3];
sign3 = 1;
pixel24 = yuyv2rgb(y0, u, v);
rgb[out+0] = pixel[0];
rgb[out+1] = pixel[1];
rgb[out+2] = pixel[2];
pixel24 = yuyv2rgb(y1, u, v);
rgb[out+3] = pixel[0];
rgb[out+4] = pixel[1];
rgb[out+5] = pixel[2];
}
return 0;
}
//LCD初始化
void lcd_init()
{
lcd_fd = open("/dev/fb0",O_RDWR);
if(lcd_fd==-1){
perror("open");;
exit(-1);
}
//映射
memp = mmap(NULL,800*480*4,PROT_READ|PROT_WRITE,MAP_SHARED,lcd_fd,0);
if(memp==MAP_FAILED){
perror("mmap");;
exit(-1);
}
}
void lcd_uninit()
{
munmap(memp,800*480*4);
close(lcd_fd);
}
int main()
{
lcd_init();
//1.打開攝像頭
int fd = open("/dev/video7",O_RDWR);
if(fd==-1){
perror("open");
exit(-1);
}
//2.獲取功能參數(shù)
struct v4l2_capability cap = {};
int res = ioctl(fd,VIDIOC_QUERYCAP,&cap);
if(res==-1){
perror("ioctl cap");
exit(-1);
}
if(cap.capabilities&V4L2_CAP_VIDEO_CAPTURE){
//設備是一個攝像頭
printf("capture device!\n");
}
else{
printf("not a capture device!\n");
exit(-1);
}
//3.獲取攝像頭支持的格式
struct v4l2_fmtdesc fmt = {};
fmt.index = 0;//第一種格式
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//獲取攝像頭的格式
while((res=ioctl(fd,VIDIOC_ENUM_FMT,&fmt))==0){
printf("pixformat=%c%c%c%c,description=%s\n",fmt.pixelformat&0xff,(fmt.pixelformat>>8)&0xff,
(fmt.pixelformat>>16)&0xff,(fmt.pixelformat>>24)&0xff,fmt.description);
fmt.index++;
}
//4.設置采集通道
int index = 0;//使用通道0
res = ioctl(fd,VIDIOC_S_INPUT,&index);
if(res==-1){
perror("ioctl s_input");
exit(-1);
}
//5.設置攝像頭的采集格式
struct v4l2_format format = {};
format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
format.fmt.pix.width = 640;
format.fmt.pix.height = 480;
format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;//YUYV格式
format.fmt.pix.field= V4L2_FIELD_NONE;
res = ioctl(fd,VIDIOC_S_FMT,&format);
if(res==-1){
perror("ioctl s_fmt");
exit(-1);
}
//6.申請緩存空間
struct v4l2_requestbuffers req = {};
req.count = 4;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
res = ioctl(fd,VIDIOC_REQBUFS,&req);
if(res==-1){
perror("ioctl reqbufs");
exit(-1);
}
//7.分配,映射,入隊
size_t i,max_len = 0;
for(i=0;i<4;i++){
struct v4l2_buffer buf = {};
buf.index = i;//0~3
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
res = ioctl(fd,VIDIOC_QUERYBUF,&buf);
if(res==-1){
perror("ioctl querybuf");
exit(-1);
}
//記錄最大長度
if(buf.length>max_len)
max_len = buf.length;
//映射
buffer[i].length = buf.length;
buffer[i].start = mmap(NULL,buf.length,PROT_READ|PROT_WRITE,MAP_SHARED,fd,buf.m.offset);
if(buffer[i].start==MAP_FAILED){
perror("mmap");
exit(-1);
}
//入隊
res = ioctl(fd,VIDIOC_QBUF,&buf);
if(res==-1){
perror("ioctl qbuf");
exit(-1);
}
}
//申請臨時緩沖區(qū)
current.start = malloc(max_len);
if(current.start==NULL){
perror("malloc");
exit(-1);
}
//8.啟動攝像頭
enum v4l2_buf_type buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
res = ioctl(fd,VIDIOC_STREAMON,&buf_type);
if(res==-1){
perror("ioctl streamon");
exit(-1);
}
//延時
sleep(1);
//RGB緩沖區(qū)
char rgb[640*480*3];
//9.采集數(shù)據(jù)
while(1){
struct v4l2_buffer buf = {};
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
//出隊
res = ioctl(fd,VIDIOC_DQBUF,&buf);
if(res==-1){
perror("ioctl dqbuf");
}
//拷貝數(shù)據(jù)
memcpy(current.start,buffer[buf.index].start,buf.bytesused);
current.length = buf.bytesused;
//入隊
res = ioctl(fd,VIDIOC_QBUF,&buf);
if(res==-1){
perror("ioctl qbuf");
}
//顯示 保存 傳輸.....
//YUYV轉(zhuǎn)RGB
yuyv2rgb0(current.start,rgb,640,480);
//顯示到LCD
int x,y;
for(y=0;y<480;y++){
for(x=0;x<640;x++){
*(memp+y*800+x) = rgb[3*(y*640+x)]<<16 | rgb[3*(y*640+x)+1]<<8 | rgb[3*(y*640+x)+2];
}
}
}
//10.關閉攝像頭采集
buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
res = ioctl(fd,VIDIOC_STREAMOFF,&buf_type);
if(res==-1){
perror("ioctl streamoff");
exit(-1);
}
//解除映射
for(i=0;i<4;i++){
munmap(buffer[i].start,buffer[i].length);
}
free(current.start);
close(fd);
lcd_uninit();
return 0;
}
到了這里,關于Linux之V4L2驅(qū)動框架的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!