一. linux下V4L2一般操作流程(视频设备):
1. 打开设备文件。 int fd=open(”/dev/video0″,O_RDWR);
2. 取得设备的capability,看看设备具有什么功能,比如是否具有视频输入,或者音频输入输出等。
VIDIOC_QUERYCAP,struct v4l2_capability
3. 选择视频输入,一个视频设备可以有多个视频输入。VIDIOC_S_INPUT,struct v4l2_input
4. 设置视频的制式和帧格式,制式包括PAL,NTSC,帧的格式个包括宽度和高度等。
VIDIOC_S_STD,VIDIOC_S_FMT,struct v4l2_std_id,struct v4l2_format
5. 向驱动申请帧缓冲,一般不超过5个。struct v4l2_requestbuffers
6. 将申请到的帧缓冲映射到用户空间,这样就可以直接操作采集到的帧了,而不必去复制。mmap
7. 将申请到的帧缓冲全部入队列,以便存放采集到的数据.VIDIOC_QBUF,struct v4l2_buffer
8. 开始视频的采集。VIDIOC_STREAMON
9. 出队列以取得已采集数据的帧缓冲,取得原始采集数据。VIDIOC_DQBUF
10. 将缓冲重新入队列尾,这样可以循环采集。VIDIOC_QBUF
11. 停止视频的采集。VIDIOC_STREAMOFF
12. 关闭视频设备。close(fd);
#define VIDEO_FORMAT V4L2_PIX_FMT_YUYV
#define BUFFER_COUNT 4
typedef struct VideoBuffer {
void *start; //视频缓冲区的起始地址
size_t length;//缓冲区的长度
} VideoBuffer;
什么是位图文件
VideoBuffer framebuf[BUFFER_COUNT];
v4l2编程步骤:
1,// 获取驱动信息
ret = ioctl(fd, VIDIOC_QUERYCAP, &cap);
控制命令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];
};
cap 定义为v4l2_capability结构体
通过 ret = ioctl(fd, VIDIOC_QUERYCAP, &cap);便可得到驱动信息
可使用printf(" driver: %s\n", cap.driver);打印出相关信息;
2,//获取当前视频设备支持的视频格式
truct 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];
};
struct v4l2_fmtdesc fmtdesc;
ret=ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc);
//如果fmtdesc的index适合该设备,则ret=0;
3,//设置视频设备的视频数据格式
控制命令 :VIDIOC_S_FMT
struct v4l2_format fmt;
参数说明:参数类型为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;
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; //V4L2_FIELD_ANY
4,请求V4L2驱动分配视频缓冲区(申请V4L2视频驱动分配内存)
重要的结构体
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;
};
该步骤比较复杂:
ret = ioctl(fd , VIDIOC_QUERYBUF, &buf); //获得内存缓冲区的信息
//然后mmap buffer
例如:
framebuf[i].length = buf.length;//framebuf是程序最前面定义的一个结构体类型的数据
framebuf[i].start = 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;
}
再然后:
memset(&buf, 0, sizeof(struct buf));
buf.index = i;
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
ret = ioctl(fd , VIDIOC_QBUF, &buf);
if (ret < 0) {
printf("VIDIOC_QBUF (%d) failed (%d)\n", i, ret);
return;
}
映射到framebuf的4个地址;
最后在改完video2lcd_opencv2(椭圆检测)成功,在修改过程中本来使用fread()函数将buf里边的像素数据读出了
,但是读出来的.jpg文件用UE总打开总是全零,最后采用fwrite()函数将buf里边的数据写给一个.jpg文件,才成功得
到一个完整的.jpg文件,具体原因在我调试fwrite是猜想可能是我使用fread里边读的mode不对,没有使用rb试试,
1. 打开设备文件。 int fd=open(”/dev/video0″,O_RDWR);
2. 取得设备的capability,看看设备具有什么功能,比如是否具有视频输入,或者音频输入输出等。
VIDIOC_QUERYCAP,struct v4l2_capability
3. 选择视频输入,一个视频设备可以有多个视频输入。VIDIOC_S_INPUT,struct v4l2_input
4. 设置视频的制式和帧格式,制式包括PAL,NTSC,帧的格式个包括宽度和高度等。
VIDIOC_S_STD,VIDIOC_S_FMT,struct v4l2_std_id,struct v4l2_format
5. 向驱动申请帧缓冲,一般不超过5个。struct v4l2_requestbuffers
6. 将申请到的帧缓冲映射到用户空间,这样就可以直接操作采集到的帧了,而不必去复制。mmap
7. 将申请到的帧缓冲全部入队列,以便存放采集到的数据.VIDIOC_QBUF,struct v4l2_buffer
8. 开始视频的采集。VIDIOC_STREAMON
9. 出队列以取得已采集数据的帧缓冲,取得原始采集数据。VIDIOC_DQBUF
10. 将缓冲重新入队列尾,这样可以循环采集。VIDIOC_QBUF
11. 停止视频的采集。VIDIOC_STREAMOFF
12. 关闭视频设备。close(fd);
#define VIDEO_FORMAT V4L2_PIX_FMT_YUYV
#define BUFFER_COUNT 4
typedef struct VideoBuffer {
void *start; //视频缓冲区的起始地址
size_t length;//缓冲区的长度
} VideoBuffer;
什么是位图文件
VideoBuffer framebuf[BUFFER_COUNT];
v4l2编程步骤:
1,// 获取驱动信息
ret = ioctl(fd, VIDIOC_QUERYCAP, &cap);
控制命令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];
};
cap 定义为v4l2_capability结构体
通过 ret = ioctl(fd, VIDIOC_QUERYCAP, &cap);便可得到驱动信息
可使用printf(" driver: %s\n", cap.driver);打印出相关信息;
2,//获取当前视频设备支持的视频格式
truct 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];
};
struct v4l2_fmtdesc fmtdesc;
ret=ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc);
//如果fmtdesc的index适合该设备,则ret=0;
3,//设置视频设备的视频数据格式
控制命令 :VIDIOC_S_FMT
struct v4l2_format fmt;
参数说明:参数类型为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;
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; //V4L2_FIELD_ANY
4,请求V4L2驱动分配视频缓冲区(申请V4L2视频驱动分配内存)
重要的结构体
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;
};
该步骤比较复杂:
ret = ioctl(fd , VIDIOC_QUERYBUF, &buf); //获得内存缓冲区的信息
//然后mmap buffer
例如:
framebuf[i].length = buf.length;//framebuf是程序最前面定义的一个结构体类型的数据
framebuf[i].start = 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;
}
再然后:
memset(&buf, 0, sizeof(struct buf));
buf.index = i;
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
ret = ioctl(fd , VIDIOC_QBUF, &buf);
if (ret < 0) {
printf("VIDIOC_QBUF (%d) failed (%d)\n", i, ret);
return;
}
映射到framebuf的4个地址;
最后在改完video2lcd_opencv2(椭圆检测)成功,在修改过程中本来使用fread()函数将buf里边的像素数据读出了
,但是读出来的.jpg文件用UE总打开总是全零,最后采用fwrite()函数将buf里边的数据写给一个.jpg文件,才成功得
到一个完整的.jpg文件,具体原因在我调试fwrite是猜想可能是我使用fread里边读的mode不对,没有使用rb试试,
也有可能是内核空间mmap到用户空间,不能fread(有点扯)