Linux视频设备驱动常用操控指令运用阐明
设置视频设备特点经过ioctl来进行设置,ioctl有三个参数,分别是fd, cmd,和parameter,表明设备描绘符,操控指令和操控指令参数。
1. 操控指令VIDIOC_QUERYCAP
功用: 查询设备驱动的功用 ;
参数阐明:参数类型为V4L2的才能描绘类型struct v4l2_capability;
struct v4l2_capability {
__u8 driver[16]; /* i.e. “bttv” */ //驱动称号,
__u8 card[32]; /* i.e. “Hauppauge WinTV” */ //
__u8 bus_info[32]; /* “PCI:” + pci_name(pci_dev) */ //PCI总线信息
__u32 version; /* should use KERNEL_VERSION() */
__u32 capabilities; /* Device capabiliTIes */ //设备才能
__u32 reserved[4];
};
回来值阐明: 履行成功时,函数回来值为 0;
函数履行成功后,struct v4l2_capability 结构体变量中的回来当时视频设备所支撑的功用;
例如支撑视频捕获功用V4L2_CAP_VIDEO_CAPTURE、 V4L2_CAP_STREAMING等。
运用举例:
——————————————————————————————————-
struct v4l2_capability cap;
iret = ioctl(fd_usbcam, VIDIOC_QUERYCAP, &cap);
if(iret 《 0){
printf(“get vidieo capability error,error code: %d \n”, errno);
return ;
}
——————————————————————————————————
履行完VIDIOC_QUERYCAP指令后,cap变量中包含了该视频设备的才能信息,程序中经过查看cap中的设备才能信息来判别设备是否支撑某项功用。
2. 操控指令 VIDIOC_ENUM_FMT
功用: 获取当时视频设备支撑的视频格局 。
参数阐明:参数类型为V4L2的视频格局描绘符类型 struct v4l2_fmtdesc
struct v4l2_fmtdesc {
__u32 index; /* Format number */
enum v4l2_buf_type type; /* buffer type */
__u32 flags;
__u8 descripTIon[32]; /* DescripTIon string */
__u32 pixelformat; /* Format fourcc */
__u32 reserved[4];
};
回来值阐明: 履行成功时,函数回来值为 0;
struct v4l2_fmtdesc 结构体中的 .pixelformat和 .descripTIon 成员回来当时视频设备所支撑的视频格局;
运用举例:
————————————————————————————————-
struct v4l2_fmtdesc fmt;
memset(&fmt, 0, sizeof(fmt));
fmt.index = 0;
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
while ((ret = ioctl(dev, VIDIOC_ENUM_FMT, &fmt)) == 0)
{
fmt.index++;
printf(“{ pixelformat = ‘’%c%c%c%c‘’, description = ‘’%s‘’ }\n”,
fmt.pixelformat & 0xFF, (fmt.pixelformat 》》 8) & 0xFF, (fmt.pixelformat 》》 16) & 0xFF,
(fmt.pixelformat 》》 24) & 0xFF, fmt.description);
}
——————————————————————————————————-
3. 操控指令VIDIOC_S_FMT
功用: 设置视频设备的视频数据格局,例如设置视频图画数据的长、宽,图画格局(JPEG、YUYV格局);
参数阐明:参数类型为V4L2的视频数据格局类型 struct v4l2_format;
struct v4l2_format {
enum v4l2_buf_type type; //数据流类型,有必要永远是V4L2_BUF_TYPE_VIDEO_CAPTURE
union {
struct v4l2_pix_format pix; /* V4L2_BUF_TYPE_VIDEO_CAPTURE */
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 */
__u8 raw_data[200]; /* user-defined */
} fmt;
};
struct v4l2_pix_format {
__u32 width; // 宽,有必要是16的倍数
__u32 height; // 高,有必要是16的倍数
__u32 pixelformat; // 视频数据存储类型,例如是YUV4:2:2仍是RGB
enum v4l2_field field;
__u32 bytesperline;
__u32 sizeimage;
enum v4l2_colorspace colorspace;
__u32 priv;
};
回来值阐明: 履行成功时,函数回来值为 0;
运用举例:
———————————————————————————————————-
struct v4l2_format tv4l2_format;
tv4l2_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
tv4l2_format.fmt.pix.width = img_width;
tv4l2_format.fmt.pix.height = img_height;
tv4l2_format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
tv4l2_format.fmt.pix.field = V4L2_FIELD_INTERLACED;
iret = ioctl(fd_usbcam, VIDIOC_S_FMT, &tv4l2_format);
———————————————————————————————————–
留意:假如该视频设备驱动不支撑你所设定的图画格局,视频驱动会从头修正struct v4l2_format结构体变量的值为该视频设备所支撑的图画格局,所以在程序设计中,设定完所
有的视频格局后,要获取实践的视频格局,要从头读取 struct v4l2_format结构体变量。
运用VIDIOC_G_FMT设置视频设备的视频数据格局,VIDIOC_TRY_FMT验证视频设备的视频数据格局。
4. 操控指令VIDIOC_REQBUFS
功用: 恳求V4L2驱动分配视频缓冲区(恳求V4L2视频驱动分配内存),V4L2是视频设备的驱动层,坐落内核空间,所以经过VIDIOC_REQBUFS操控指令字恳求的内存坐落内核空间,应
用程序不能直接拜访,需求经过调用mmap内存映射函数把内核空间内存映射到用户空间后,运用程序经过拜访用户空间地址来拜访内核空间。
参数阐明:参数类型为V4L2的恳求缓冲区数据结构体类型struct v4l2_requestbuffers;
struct v4l2_requestbuffers {
u32 count; //缓存数量,也便是说在缓存行列里坚持多少张相片
enum v4l2_buf_type type; //数据流类型,有必要永远是V4L2_BUF_TYPE_VIDEO_CAPTURE
enum v4l2_memory memory; //V4L2_MEMORY_MMAP或V4L2_MEMORY_USERPTR
u32 reserved[2];
};
回来值阐明: 履行成功时,函数回来值为 0,V4L2驱动层分配好了视频缓冲区;
运用举例:
—————————————————————————————————–
struct v4l2_requestbuffers tV4L2_reqbuf;
memset(&tV4L2_reqbuf, 0, sizeof(struct v4l2_requestbuffers ));
tV4L2_reqbuf.count = 1; //恳求缓冲区的个数
tV4L2_reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
tV4L2_reqbuf.memory = V4L2_MEMORY_MMAP; //mmap方法
iret = ioctl(fd_usbcam, VIDIOC_REQBUFS, &tV4L2_reqbuf);
—————————————————————————————————–
留意:VIDIOC_REQBUFS会修正tV4L2_reqbuf的count值,tV4L2_reqbuf的count值回来实践恳求成功的视频缓冲区数目;
5. 操控指令VIDIOC_QUERYBUF
功用: 查询现已分配的V4L2的视频缓冲区的相关信息,包含视频缓冲区的运用状况、在内核空间的偏移地址、缓冲区长度等。在运用程序设计中经过调 VIDIOC_QUERYBUF来获取内
核空间的视频缓冲区信息,然后调用函数mmap把内核空间地址映射到用户空间,这样运用程序才能够拜访坐落内核空间的视频缓冲区。
参数阐明:参数类型为V4L2缓冲区数据结构类型 struct v4l2_buffer;
struct v4l2_buffer {
__u32 index;
enum v4l2_buf_type type;
__u32 bytesused;
__u32 flags;
enum v4l2_field field;
struct timeval timestamp;
struct v4l2_timecode timecode;
__u32 sequence;
/* memory location */
enum v4l2_memory memory;
union {
__u32 offset;
unsigned long userptr;
} m;
__u32 length;
__u32 input;
__u32 reserved;
};
回来值阐明: 履行成功时,函数回来值为 0;
struct v4l2_buffer结构体变量中保存了指令的缓冲区的相关信息;一般情况下,运用程序中调用VIDIOC_QUERYBUF获得了内核缓冲区信息后,紧接着调用mmap函数把内核空间地址
映射到用户空间,便利用户空间运用程序的拜访。
运用举例:
——————————————————————————————————-
struct v4l2_buffer tV4L2buf;
memset(&tV4L2buf, 0, sizeof(struct v4l2_buffer));
tV4L2buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
tV4L2buf.memory = V4L2_MEMORY_MMAP;
tV4L2buf.index = i; // 要获取内核视频缓冲区的信息编号
iret = ioctl(fd_usbcam, VIDIOC_QUERYBUF, &tV4L2buf);
// 把内核空间缓冲区映射到用户空间缓冲区
AppBufLength = tV4L2buf.length;
AppBufStartAddr = mmap( NULL, /* start anywhere */
tV4L2buf.length,
PROT_READ | PROT_WRITE, /* access privilege */
MAP_SHARED, /* recommended */
fd_usbcam,
tV4L2buf.m.offset);
——————————————————————————————————-
上述代码在经过调用VIDIOC_QUERYBUF获得内核空间的缓冲区信息后,接着调用mmap函数把内核空间缓冲区映射到用户空间;
6. 操控指令VIDIOC_QBUF
功用: 投进一个空的视频缓冲区到视频缓冲区输入行列中 ;
参数阐明:参数类型为V4L2缓冲区数据结构类型 struct v4l2_buffer;
回来值阐明: 履行成功时,函数回来值为 0;
函数履行成功后,指令(指定)的视频缓冲区进入视频输入行列,在发动视频设备拍照图画时,相应的视频数据被保存到视频输入行列相应的视频缓冲区中。
运用举例:
——————————————————————————————————-
struct v4l2_buffer tV4L2buf;
memset(&tV4L2buf, 0, sizeof(struct v4l2_buffer));
tV4L2buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
tV4L2buf.memory = V4L2_MEMORY_MMAP;
tV4L2buf.index = i; //指令(指定)要投进到视频输入行列中的内核空间视频缓冲区的编号;
iret = ioctl(fd_usbcam, VIDIOC_QBUF, &tV4L2buf);
——————————————————————————————————-
7. 操控指令VIDIOC_DQBUF
功用: 从视频缓冲区的输出行列中获得一个现已保存有一帧视频数据的视频缓冲区;
参数阐明:参数类型为V4L2缓冲区数据结构类型 struct v4l2_buffer;
回来值阐明: 履行成功时,函数回来值为 0;函数履行成功后,相应的内核视频缓冲区中保存有当时拍照到的视频数据,运用程序能够经过拜访用户空间来读取该视频数据。(前面
现现已过调用函数 mmap做了用户空间和内核空间的内存映射)。
运用举例:
———————————————————————————————————-
struct v4l2_buffer tV4L2buf;
memset(&tV4L2buf, 0, sizeof(struct v4l2_buffer));
tV4L2buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
tV4L2buf.memory = V4L2_MEMORY_MMAP;
iret = ioctl(fd_usbcam, VIDIOC_DQBUF, &tV4L2buf);
————————————————————————————————-
阐明: VIDIOC_DQBUF指令成果, 使从行列删去的缓冲帧信息传给了此tVL2buf。
V4L2_buffer结构体的效果就相当于恳求的缓冲帧的署理,找缓冲帧的都要先问问它,经过它来联络缓冲帧,起了中心桥梁的效果。
8. 操控指令VIDIOC_STREAMON
功用: 发动视频收集指令,运用程序调用VIDIOC_STREAMON发动视频收集指令后,视频设备驱动程序开端收集视频数据,并把收集到的视频数据保存到视频驱动的视频缓冲区中。
参数阐明:参数类型为V4L2的视频缓冲区类型 enum v4l2_buf_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,
#if 1
/* Experimental */
V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY = 8,
#endif
V4L2_BUF_TYPE_PRIVATE = 0x80,
};
回来值阐明: 履行成功时,函数回来值为 0;函数履行成功后,视频设备驱动程序开端收集视频数据,此刻运用程序一般经过调用select函数来判别一帧视频数据是否收集完结,
当视频设备驱动完结一帧视频数据收集并保存到视频缓冲区中时,select函数回来,运用程序接着能够读取视频数据;不然select函数堵塞直到视频数据收集完结。 Select函数的
运用请读者参阅相关材料。
运用举例:
———————————————————————————————————-
enum v4l2_buf_type v4l2type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fd_set fds ;
struct timeval tv;
iret = ioctl(fd_usbcam, VIDIOC_STREAMON, &v4l2type);
FD_ZERO(&fds);
FD_SET(fd_usbcam, &fds);
tv.tv_sec = 2; /* Timeout. */
tv.tv_usec = 0;
iret = select(fd_usbcam+ 1, &fds, NULL, NULL, &tv);
———————————————————————————————————-
9. 操控指令VIDIOC_STREAMOFF
功用: 中止视频收集指令,运用程序调用VIDIOC_ STREAMOFF中止视频收集指令后,视频设备驱动程序不在收集视频数据。
参数阐明:参数类型为V4L2的视频缓冲区类型 enum v4l2_buf_type;
回来值阐明: 履行成功时,函数回来值为 0;函数履行成功后,视频设备中止收集视频数据。
运用举例:
———————————————————————————————————-
enum v4l2_buf_type v4l2type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
iret = ioctl(fd_usbcam, VIDIOC_STREAMOFF, &v4l2type);
———————————————————————————————————–
10. 操控指令VIDIOC_QUERYSTD
功用:在亚洲,一般运用PAL(720X576)制式的摄像头,而欧洲一般运用NTSC(720X480),运用VIDIOC_QUERYSTD来检测:
参数阐明:
回来值阐明:
typedef __u64 v4l2_std_id;
运用举例:
———————————————————————————————————–
v4l2_std_id std;
do{
ret= ioctl(fd, VIDIOC_QUERYSTD, &std);
} while(ret== -1 && errno== EAGAIN);
switch(std) {
caseV4L2_STD_NTSC:
//……
caseV4L2_STD_PAL:
//……
}
———————————————————————————————————–
以上便是Linux 视频设备驱动V4L2最常用的操控指令运用阐明,经过运用以上操控指令,能够完结一幅视频数据的收集进程。
V4L2更多的操控指令运用阐明请参阅:http://v4l2spec.bytesex.org/spec/book1.htm
下面是damo程序(经过实践验证,修正了网上的例程的过错)
———————————————————————————————————–
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define CAMERA_DEVICE “/dev/video0”
#define CAPTURE_FILE “frame.jpg”
#define VIDEO_WIDTH 640
#define VIDEO_HEIGHT 480
#define VIDEO_FORMAT V4L2_PIX_FMT_YUYV
#define BUFFER_COUNT 4
typedef struct VideoBuffer {
void *start;
size_t length;
} VideoBuffer;
VideoBuffer framebuf[BUFFER_COUNT]; //修正了过错,2012-5.21
int main()
{
int i, ret;
// 翻开设备
int fd;
fd = open(CAMERA_DEVICE, O_RDWR, 0);
if (fd 《 0) {
printf(“Open %s failed\n”, CAMERA_DEVICE);
return -1;
}
// 获取驱动信息
struct v4l2_capability cap;
ret = ioctl(fd, VIDIOC_QUERYCAP, &cap);
if (ret 《 0) {
printf(“VIDIOC_QUERYCAP failed (%d)\n”, ret);
return ret;
}
// Print capability infomations
printf(“Capability Informations:\n”);
printf(“ driver: %s\n”, cap.driver);
printf(“ card: %s\n”, cap.card);
printf(“ bus_info: %s\n”, cap.bus_info);
printf(“ version: %08X\n”, cap.version);
printf(“ capabilities: %08X\n”, cap.capabilities);
// 设置视频格局
struct v4l2_format fmt;
memset(&fmt, 0, sizeof(fmt));
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = VIDEO_WIDTH;
fmt.fmt.pix.height = VIDEO_HEIGHT;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
ret = ioctl(fd, VIDIOC_S_FMT, &fmt);
if (ret 《 0) {
printf(“VIDIOC_S_FMT failed (%d)\n”, ret);
return ret;
}
// 获取视频格局
ret = ioctl(fd, VIDIOC_G_FMT, &fmt);
if (ret 《 0) {
printf(“VIDIOC_G_FMT failed (%d)\n”, ret);
return ret;
}
// Print Stream Format
printf(“Stream Format Informations:\n”);
printf(“ type: %d\n”, fmt.type);
printf(“ width: %d\n”, fmt.fmt.pix.width);
printf(“ height: %d\n”, fmt.fmt.pix.height);
char fmtstr[8];
memset(fmtstr, 0, 8);
memcpy(fmtstr, &fmt.fmt.pix.pixelformat, 4);
printf(“ pixelformat: %s\n”, fmtstr);
printf(“ field: %d\n”, fmt.fmt.pix.field);
printf(“ bytesperline: %d\n”, fmt.fmt.pix.bytesperline);
printf(“ sizeimage: %d\n”, fmt.fmt.pix.sizeimage);
printf(“ colorspace: %d\n”, fmt.fmt.pix.colorspace);
printf(“ priv: %d\n”, fmt.fmt.pix.priv);
printf(“ raw_date: %s\n”, fmt.fmt.raw_data);
// 恳求分配内存
struct v4l2_requestbuffers reqbuf;
reqbuf.count = BUFFER_COUNT;
reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
reqbuf.memory = V4L2_MEMORY_MMAP;
ret = ioctl(fd , VIDIOC_REQBUFS, &reqbuf);
if(ret 《 0) {
printf(“VIDIOC_REQBUFS failed (%d)\n”, ret);
return ret;
}
// 获取空间
VideoBuffer* buffers = calloc( reqbuf.count, sizeof(*buffers) );
struct v4l2_buffer buf;
for (i = 0; i 《 reqbuf.count; i++)
{
buf.index = i;
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
ret = ioctl(fd , VIDIOC_QUERYBUF, &buf);
if(ret 《 0) {
printf(“VIDIOC_QUERYBUF (%d) failed (%d)\n”, i, ret);
return ret;
}
// mmap buffer
framebuf[i].length = buf.length;
framebuf[i].start = (char *) mmap(0, buf.length, PROT_READ|PROT_WRITE, MAP_SHARED, fd, buf.m.offset);
if (framebuf[i].start == MAP_FAILED) {
printf(“mmap (%d) failed: %s\n”, i, strerror(errno));
return -1;
}
// Queen buffer
ret = ioctl(fd , VIDIOC_QBUF, &buf);
if (ret 《 0) {
printf(“VIDIOC_QBUF (%d) failed (%d)\n”, i, ret);
return -1;
}
printf(“Frame buffer %d: address=0x%x, length=%d\n”, i, (unsigned int)framebuf[i].start, framebuf[i].length);
}
// 开端录制
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = ioctl(fd, VIDIOC_STREAMON, &type);
if (ret 《 0) {
printf(“VIDIOC_STREAMON failed (%d)\n”, ret);
return ret;
}
// Get frame
ret = ioctl(fd, VIDIOC_DQBUF, &buf);
if (ret 《 0) {
printf(“VIDIOC_DQBUF failed (%d)\n”, ret);
return ret;
}
// Process the frame
FILE *fp = fopen(CAPTURE_FILE, “wb”);
if (fp 《 0) {
printf(“open frame data file failed\n”);
return -1;
}
fwrite(framebuf[buf.index].start, 1, buf.length, fp);
fclose(fp);
printf(“Capture one frame saved in %s\n”, CAPTURE_FILE);
// Re-queen buffer
ret = ioctl(fd, VIDIOC_QBUF, &buf);
if (ret 《 0) {
printf(“VIDIOC_QBUF failed (%d)\n”, ret);
return ret;
}
// Release the resource
for (i=0; i《 4; i++)
{
munmap(framebuf[i].start, framebuf[i].length);
}
close(fd);
printf(“Camera test Done.\n”);
return 0;
}
———————————————————————————————————–
附件:
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
int munmap(void *start, size_t length);
参数阐明:
——start:映射区的开端地址。
——length:映射区的长度。
——prot:希望的内存保护标志,不能与文件的翻开形式抵触。是以下的某个值,能够经过or运算合理地组合在一同
—PROT_EXEC //页内容能够被履行
—PROT_READ //页内容能够被读取
—PROT_WRITE //页能够被写入
—PROT_NONE //页不行拜访
——flags:指定映射目标的类型,映射选项和映射页是否能够同享。它的值能够是一个或许多个以下位的组合体
—MAP_FIXED //运用指定的映射开始地址,假如由start和len参数指定的内存区堆叠于现存的映射空间,堆叠部分将会被丢掉。假如指定的开始地址不行用,操作将会失
败。而且开始地址有必要落在页的鸿沟上。
—MAP_SHARED //与其它所有映射这个目标的进程同享映射空间。对同享区的写入,相当于输出到文件。直到msync()或许munmap()被调用,文件实践上不会被更新。
—MAP_PRIVATE //树立一个写入时复制的私有映射。内存区域的写入不会影响到原文件。这个标志和以上标志是互斥的,只能运用其间一个。
—MAP_DENYWRITE //这个标志被疏忽。
—MAP_EXECUTABLE //同上
—MAP_NORESERVE //不要为这个映射保存交流空间。当交流空间被保存,对映射区修正的可能会得到确保。当交流空间不被保存,一同内存不足,对映射区的修正会引起
段违例信号。
—MAP_LOCKED //锁定映射区的页面,然后避免页面被交流出内存。
—MAP_GROWSDOWN //用于仓库,告知内核VM体系,映射区能够向下扩展。
—MAP_ANONYMOUS //匿名映射,映射区不与任何文件相关。
—MAP_ANON //MAP_ANONYMOUS的别称,不再被运用。
—MAP_FILE //兼容标志,被疏忽。
—MAP_32BIT //将映射区放在进程地址空间的低2GB,MAP_FIXED指定时会被疏忽。当时这个标志只在x86-64渠道上得到支撑。
—MAP_POPULATE //为文件映射经过预读的方法准备好页表。随后对映射区的拜访不会被页违例堵塞。
—MAP_NONBLOCK //仅和MAP_POPULATE一同运用时才有含义。不履行预读,只为已存在于内存中的页面树立页表进口。
——fd:有用的文件描绘词。假如MAP_ANONYMOUS被设定,为了兼容问题,其值应为-1。
——offset:被映射目标内容的起点。
回来值:
成功履行时,mmap()回来被映射区的指针,munmap()回来0。
失利时,mmap()回来MAP_FAILED[其值为(void *)-1],munmap回来-1。errno被设为以下的某个值。