V4L2学习笔记【转】
本文转载自:http://www.cnblogs.com/silence-hust/p/4464291.html
v4l2,一开始听到这个名词的时候,以为又是一个很难很难的模块,涉及到视频的处理,后来在网上各种找资料后,才发现其实v4l2已经分装好了驱动程序,只要我们根据需要调用相应的接口和函数,从而实现视频的获取和处理。只要认真的看几篇文章就对v4l2有一定的了解了,由于是第一次接触,网上的资料良莠不齐,难得可以找到几篇自己感觉很不错的。记录下来:(没必要看太多,很多都是一样的意思)
http://www.embedu.org/Column/Column320.htm 这篇是不错的介绍,很讨厌有弹窗
http://www.cnblogs.com/emouse/archive/2013/03/04/2943243.html 这个可以作为第一篇来看,博主整理的不错
http://blog.chinaunix.net/uid-11765716-id-2855735.html 这篇也比较详细
http://blog.csdn.net/ddddwant/article/details/8475211 这篇提到的问题和我遇到的一样,花屏了,内存没有读取好
http://my.oschina.net/u/1024767/blog/210801#OSC_h2_14 对capture.c文件的解读
http://blog.csdn.net/g_salamander/article/details/8107692 对各个结构体有比较好的说明
一、Video for Linux two
v4l2为linux下视频设备程序提供了一套接口规范。包括一套数据结构和底层V4L2驱动接口。只能在linux下使用。它使程序有发现设备和操作设备的能力。它主要是用一系列的回调函数来实现这些功能。像设置摄像头的频率、帧频、视频压缩格式和图像参数等等。当然也可以用于其他多媒体的开发,如音频等。
在Linux下,所有外设都被看成一种特殊的文件,成为“设备文件”,可以象访问普通文件一样对其进行读写。一般来说,采用V4L2驱动的摄像头设备文是/dev/v4l/video0。为了通用,可以建立一个到/dev/video0的链接。V4L2支持两种方式来采集图像:内存映射方式(mmap)和直接读取方式(read)。V4L2在include/linux/videodev.h文件中定义了一些重要的数据结构,在采集图像的过程中,就是通过对这些数据的操作来获得最终的图像数据。Linux系统V4L2的能力可在Linux内核编译阶段配置,默认情况下都有此开发接口。V4L2从Linux 2.5.x版本的内核中开始出现。
V4L2规范中不仅定义了通用API元素(Common API Elements),图像的格式(Image Formats),输入/输出方法(Input/Output),还定义了Linux内核驱动处理视频信息的一系列接口(Interfaces),这些接口主要有:
视频采集接口——Video Capture Interface;
视频输出接口—— Video Output Interface;
视频覆盖/预览接口——Video Overlay Interface;
视频输出覆盖接口——Video Output Overlay Interface;
编解码接口——Codec Interface。
二、v4l2结构体介绍
1、常用的结构体在内核目录include/linux/videodev2.h中定义
struct v4l2_requestbuffers //申请帧缓冲,对应命令VIDIOC_REQBUFS
struct v4l2_capability //视频设备的功能,对应命令VIDIOC_QUERYCAP
struct v4l2_input //视频输入信息,对应命令VIDIOC_ENUMINPUT
struct v4l2_standard //视频的制式,比如PAL,NTSC,对应命令VIDIOC_ENUMSTD
struct v4l2_format //帧的格式,对应命令VIDIOC_G_FMT、VIDIOC_S_FMT等
struct v4l2_buffer //驱动中的一帧图像缓存,对应命令VIDIOC_QUERYBUF
struct v4l2_crop //视频信号矩形边框
v4l2_std_id //视频制式
常用结构体的内容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
struct v4l2_capability { u8 driver[16]; // 驱动名字 u8 card[32]; // 设备名字 u8 bus_info[32]; // 设备在系统中的位置 u32 version; // 驱动版本号 u32 capabilities; // 设备支持的操作 u32 reserved[4]; // 保留字段 }; |
其中域 capabilities 代表设备支持的操作模式,常见的值有 V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING 表示是一个视频捕捉设备并且具有数据流控制模式;另外 driver 域需要和 struct video_device 中的 name 匹配。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
struct v4l2_format { enum v4l2_buf_type type; 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; }; 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_PRIVATE = 0x80, }; struct v4l2_pix_format { __u32 width; __u32 height; __u32 pixelformat; enum v4l2_field field; __u32 bytesperline; /* for padding, zero if unused */ __u32 sizeimage; enum v4l2_colorspace colorspace; __u32 priv; /* private data, depends on pixelformat */ }; |
常见的捕获模式为 V4L2_BUF_TYPE_VIDEO_CAPTURE 即视频捕捉模式,在此模式下 fmt 联合体采用域 v4l2_pix_format:其中 width 为视频的宽、height 为视频的高、pixelformat 为视频数据格式(常见的值有 V4L2_PIX_FMT_YUV422P | V4L2_PIX_FMT_RGB565)、bytesperline 为一行图像占用的字节数、sizeimage 则为图像占用的总字节数、colorspace 指定设备的颜色空间。
struct v4l2_requestbuffers 与 VIDIOC_REQBUFS ,VIDIOC_REQBUFS 命令通过结构 v4l2_requestbuffers 请求驱动申请一片连续的内存用于缓存视频信息:
1
2
3
4
5
6
7
8
9
10
11
|
struct v4l2_requestbuffers { __u32 count; enum v4l2_buf_type type; enum v4l2_memory memory; __u32 reserved[2]; }; enum v4l2_memory { V4L2_MEMORY_MMAP = 1, V4L2_MEMORY_USERPTR = 2, V4L2_MEMORY_OVERLAY = 3, }; |
count 指定根据图像占用空间大小申请的缓存区个数,type 为视频捕获模式,memory 为内存区的使用方式。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
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; }; |
index 为缓存编号
type 为视频捕获模式
bytesused 为缓存已使用空间大小
flags 为缓存当前状态(常见值有 V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE,分别代表当前缓存已经映射、缓存可以采集数据、缓存可以提取数据)
timestamp 为时间戳
sequence为缓存序号
memory 为缓存使用方式
offset 为当前缓存与内存区起始地址的偏移
length 为缓存大小
reserved 一般用于传递物理地址值。
另外 VIDIOC_QBUF 和 VIDIOC_DQBUF 命令都采用结构 v4l2_buffer 与驱动通信:VIDIOC_QBUF 命令向驱动传递应用程序已经处理完的缓存,即将缓存加入空闲可捕获视频的队列,传递的主要参数为 index;VIDIOC_DQBUF 命令向驱动获取已经存放有视频数据的缓存,v4l2_buffer 的各个域几乎都会被更新,但主要的参数也是 index,应用程序会根据 index 确定可用数据的起始地址和范围。
2、常用的IOCTL接口命令也在include/linux/videodev2.h中定义
VIDIOC_REQBUFS //分配内存
VIDIOC_QUERYBUF //把VIDIOC_REQBUFS中分配的数据缓存转换成物理地址
VIDIOC_QUERYCAP //查询驱动功能
VIDIOC_ENUM_FMT //获取当前驱动支持的视频格式
VIDIOC_S_FMT //设置当前驱动的频捕获格式
VIDIOC_G_FMT //读取当前驱动的频捕获格式
VIDIOC_TRY_FMT //验证当前驱动的显示格式
VIDIOC_CROPCAP //查询驱动的修剪能力
VIDIOC_S_CROP //设置视频信号的矩形边框
VIDIOC_G_CROP //读取视频信号的矩形边框
VIDIOC_QBUF //把数据从缓存中读取出来
VIDIOC_DQBUF //把数据放回缓存队列
VIDIOC_STREAMON //开始视频显示函数
VIDIOC_STREAMOFF //结束视频显示函数
VIDIOC_QUERYSTD //检查当前视频设备支持的标准,例如PAL或NTSC。
三、调用v4l2的工作流程
打开设备-> 检查和设置设备属性-> 设置帧格式-> 设置一种输入输出方法(缓冲 区管理)-> 循环获取数据-> 关闭设备。
(1)打开设备文件
打开视频设备非常简单,在V4L2中,视频设备被看做一个文件。使用open函数打开这个设备:
1. 用非阻塞模式打开摄像头设备
int cameraFd;
cameraFd = open("/dev/video0", O_RDWR | O_NONBLOCK);
2. 如果用阻塞模式打开摄像头设备,上述代码变为:
cameraFd = open("/dev/video0", O_RDWR);
关于阻塞模式和非阻塞模式
应用程序能够使用阻塞模式或非阻塞模式打开视频设备,如果使用非阻塞模式调用视频设备,即使尚未捕获到信息,驱动依旧会把缓存(DQBUFF)里的东西返回给应用程序。
(2)取得设备的capability
struct v4l2_capability capability;
int ret = ioctl(fd, VIDIOC_QUERYCAP, &capability);
看看设备具有什么功能,比如是否具有视频输入特性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
struct v4l2_capability cap; memset (&cap, 0, sizeof (cap)); /* 获取设备支持的操作 */ if (ioctl(dev->fd, VIDIOC_QUERYCAP, &cap) < 0){ if (EINVAL == errno ){ /*EINVAL为返回的错误值*/ printf (stderr, "%s is no V4L2 device\n" , dev->dev); return TFAIL; } else { printf (stderr, "%s is not V4L2 device,unknow error\n" , dev->dev); return TFAIL; } } //获取成功,检查是否有视频捕获功能 if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)){ printf (stderr, "%s is no video capture device\n" ,dev->dev); return TFAIL; } /* streaming I/O ioctls */ if (!(cap.capabilities & V4L2_CAP_STREAMING)){ printf (stderr, "%s does not support streaming i/o\n" ,dev->dev); return TFAIL; } |
(3)选择视频输入
struct v4l2_input input;
……初始化input
int ret = ioctl(fd, VIDIOC_QUERYCAP, &input);
一个视频设备可以有多个视频输入。如果只有一路输入,这个功能可以没有。
VIDIOC_G_INPUT 和 VIDIOC_S_INPUT 用来查询和选则当前的 input,一个 video 设备节点可能对应多个视频源,比如 saf7113 可以最多支持四路 cvbs 输入,如果上层想在四个cvbs视频输入间切换,那么就要调用 ioctl(fd, VIDIOC_S_INPUT, &input) 来切换。VIDIOC_G_INPUT and VIDIOC_G_OUTPUT 返回当前的 video input和output的index.
1
2
3
4
5
6
7
8
9
10
|
struct v4l2_input { __u32 index; /* Which input * /__u8 name[32]; /* Label */ __u32 type; /* Type of input */ __u32 audioset; /* Associated audios (bitfield) */ __u32 tuner; /* Associated tuner */ v4l2_std_id std; __u32 status; __u32 reserved[4]; }; |
(4)检测视频支持的制式
v4l2_std_id std;
do {
ret = ioctl(fd, VIDIOC_QUERYSTD, &std);
} while (ret == -1 && errno == EAGAIN);
switch (std) {
case V4L2_STD_NTSC:
//……
case V4L2_STD_PAL:
//……
}
(5)设置视频捕获格式
v4l2_format 结构体用来设置摄像头的视频制式、帧格式等,在设置这个参数时应先填 好 v4l2_format 的各个域,如 type(传输流类型),fmt.pix.width(宽),fmt.pix.heigth(高),fmt.pix.field(采样区域,如隔行采样),fmt.pix.pixelformat(采样类型,如 YUV4:2:2),然后通过 VIDIO_S_FMT 操作命令设置视频捕捉格式。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
struct v4l2_format fmt; memset (&fmt, 0, sizeof (fmt)); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.fmt.pix.width = g_display_width; fmt.fmt.pix.height = g_display_height; fmt.fmt.pix.pixelformat = g_fmt; fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; /* 设置设备捕获视频的格式 */ if (ioctl(dev->fd, VIDIOC_S_FMT, &fmt) < 0) { printf (stderr, "%s iformat not supported \n" ,dev->dev); close(dev->fd); return TFAIL; } |
注意:如果该视频设备驱动不支持你所设定的图像格式,视频驱动会重新修改struct v4l2_format结构体变量的值为该视频设备所支持的图像格式,所以在程序设计中,设定完所有的视频格式后,要获取实际的视频格式,要重新读取struct v4l2_format结构体变量。
(6)向驱动申请帧缓存
一般不超过5个,CAP_BUF_NUM = 4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
struct v4l2_requestbuffers req; /* 申请设备的缓存区 */ memset (&req, 0, sizeof (req)); req.count = CAP_BUF_NUM; //申请一个拥有四个缓冲帧的缓冲区 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP; if (ioctl(dev->fd, VIDIOC_REQBUFS, &req) < 0) { if (EINVAL == errno ) { printf (stderr, "%s does not support " "memory mapping\n" , dev->dev); return TFAIL; } else { printf (stderr, "%s does not support " "memory mapping, unknow error\n" , dev->dev); return TFAIL; } } if (req.count < 2) { printf (stderr, "Insufficient buffer memory on %s\n" , dev->dev); return TFAIL; } |
v4l2_requestbuffers结构中定义了缓存的数量,驱动会据此申请对应数量的视频缓存。多个缓存可以用于建立FIFO,来提高视频采集的效率。控制命令VIDIOC_REQBUFS
功能: 请求V4L2驱动分配视频缓冲区(申请V4L2视频驱动分配内存),V4L2是视频设备的驱动层,位于内核空间,所以通过VIDIOC_REQBUFS控制命令字申请的内存位于内核空间,应用程序不能直接访问,需要通过调用mmap内存映射函数把内核空间内存映射到用户空间后,应用程序通过访问用户空间地址来访问内核空间。
参数说明:参数类型为V4L2的申请缓冲区数据结构体类型struct v4l2_requestbuffers ;
返回值说明: 执行成功时,函数返回值为 0;V4L2驱动层分配好了视频缓冲区;
(7)获取每个缓存的信息,并mmap到用户空间
应用程序和设备有三种交换数据的方法,直接 read/write、内存映射(memory mapping)和用户指针。这里只讨论内存映射(memory mapping)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
typedef struct VideoBuffer { //定义一个结构体来映射每个缓冲帧 void *start; size_t length; } VideoBuffer; VideoBuffer* buffers = calloc ( req.count, sizeof (*buffers) ); struct v4l2_buffer buf; for (numBufs = 0; numBufs < req.count; numBufs++) { //映射所有的缓存 memset ( &buf, 0, sizeof (buf) ); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = numBufs; if (ioctl(fd, VIDIOC_QUERYBUF, &buf) == -1) { //获取到对应index的缓存信息,此处主要利用length信息及offset信息来完成后面的mmap操作。 return -1; } buffers[numBufs].length = buf.length; // 转换成相对地址 buffers[numBufs].start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset); if (buffers[numBufs].start == MAP_FAILED) { return -1; } //addr 映射起始地址,一般为NULL ,让内核自动选择 //length 被映射内存块的长度 //prot 标志映射后能否被读写,其值为PROT_EXEC,PROT_READ,PROT_WRITE, PROT_NONE //flags 确定此内存映射能否被其他进程共享,MAP_SHARED,MAP_PRIVATE //fd,offset, 确定被映射的内存地址 返回成功映射后的地址,不成功返回MAP_FAILED ((void*)-1) int munmap( void *addr, size_t length); // 断开映射 //addr 为映射后的地址,length 为映射后的内存长度 |
(8)开始采集视频 (在缓冲区处理好之后就可以获得视频了 )
在开始之前,还应当把缓冲帧放入缓冲队列,应用程序和设备有三种交换数据的方法,直接 read/write、内存映射(memory mapping)和用户指针。这里只讨论内存映射(memory mapping)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
//把四个缓冲帧放入队列 for (i = 0; i < CAP_BUF_NUM; i++) { memset (&buf, 0, sizeof (buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = i; buf.m.offset = dev->buffer[i].offset; /* 将空闲的内存加入可捕获视频的队列 */ if (ioctl(dev->fd, VIDIOC_QBUF, &buf) < 0) { printf ( "ERROR: VIDIOC_QBUF[%s], FUNC[%s], LINE[%d]\n" , dev->dev, __FUNCTION__, __LINE__); return TFAIL; } } type = V4L2_BUF_TYPE_VIDEO_CAPTURE; /* 打开设备视频流 */ if (ioctl(dev->fd, VIDIOC_STREAMON, &type) < 0) { printf ( "ERROR: VIDIOC_STREAMON[%s], FUNC[%s], LINE[%d]\n" , dev->dev, __FUNCTION__, __LINE__); return TFAIL; } |
前期初始化完成后,只是解决了一帧视频数据的格式和大小问题,而连续视频帧数据的采集需要用帧缓冲区队列的方式来解决,即要通过驱动程序在内存中申请几个帧缓冲区来存放视频数据。
应用程序通过API接口提供的方法(VIDIOC_REQBUFS)申请若干个视频数据的帧缓冲区,申请帧缓冲区数量一般不低于3个,每个帧缓冲区存放一帧视频数据,这些帧缓冲区在内核空间。
应用程序通过API接口提供的查询方法(VIDIOC_QUERYBUF)查询到帧缓冲区在内核空间的长度和偏移量地址。
应用程序再通过内存映射方法(mmap),将申请到的内核空间帧缓冲区的地址映射到用户空间地址,这样就可以直接处理帧缓冲区的数据。
(1)将帧缓冲区在视频输入队列排队,并启动视频采集
在驱动程序处理视频的过程中,定义了两个队列:视频采集输入队列(incoming queues)和视频采集输出队列(outgoing queues),前者是等待驱动存放视频数据的队列,后者是驱动程序已经放入了视频数据的队列。如图2所示。
应用程序需要将上述帧缓冲区在视频采集输入队列排队(VIDIOC_QBUF),然后可启动视频采集。
(2)循环往复,采集连续的视频数据
启动视频采集后,驱动程序开始采集一帧数据,把采集的数据放入视频采集输入队列的第一个帧缓冲区,一帧数据采集完成,也就是第一个帧缓冲区存满一帧数据后,驱动程序将该帧缓冲区移至视频采集输出队列,等待应用程序从输出队列取出。驱动程序接下来采集下一帧数据,放入第二个帧缓冲区,同样帧缓冲区存满下一帧数据后,被放入视频采集输出队列。
应用程序从视频采集输出队列中取出含有视频数据的帧缓冲区,处理帧缓冲区中的视频数据,如存储或压缩。
最后,应用程序将处理完数据的帧缓冲区重新放入视频采集输入队列,这样可以循环采集,如图1所示。
(9)取出FIFO缓存中已经采样的帧缓存
1
2
3
4
5
6
7
8
9
10
11
12
13
|
struct v4l2_buffer capture_buf; memset (&capture_buf, 0, sizeof (capture_buf)); capture_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; capture_buf.memory = V4L2_MEMORY_MMAP; /* 将已经捕获好视频的内存拉出已捕获视频的队列 */ if (ioctl(dev.fd, VIDIOC_DQBUF, &capture_buf) < 0) { printf ( "ERROR: VIDIOC_DQBUF[%s], FUNC[%s], LINE[%d]\n" , dev, __FUNCTION__, __LINE__); return TFAIL; } } image_data_handle(buffer[capture_buf.index].start, capture_buf.bytesused); |
(10)将刚刚处理完的缓冲重新入队列尾,这样可以循环采集
if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) {
return -1;
}
(11)停止视频的采集,解除映射
int ret = ioctl(fd, VIDIOC_STREAMOFF, &buf_type);
munmap(buffer[j].start, buffer[j].length);
(12)关闭视频设备
close(fd);
最后这个是一般mmap形式的使用流程,还有使用read/write方式的内存读写流程,具体的可以参考官方的capture.c这个文档,程序的流程很清楚,也有相关的博文有写到。
文字描述版流程:
(1)打开视频设备文件。int fd=open("/dev/video0",O_RDWR);
(2)查询视频设备的能力,比如是否具有视频输入,或者音频输入输出等。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)向驱动申请视频流数据的帧缓冲区
请求/申请若干个帧缓冲区,一般为不少于3个,使用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)。
以上的程序流程,包含了视频设备采集连续的视频数据的逻辑关系。而在实际运用中,往往还要加入对视频数据进行处理(如压缩编码)的工作,否则,视频流数据量相当大,需要很大的存储空间和传输带宽。
V4L2学习笔记【转】的更多相关文章
- (一)V4L2学习流程
title: V4L2学习流程 date: 2019/4/23 18:00:00 toc: true --- V4L2学习流程 参考资料 关键资料,插图让人一下子就理解了 Linux摄像头驱动1--v ...
- js学习笔记:webpack基础入门(一)
之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...
- PHP-自定义模板-学习笔记
1. 开始 这几天,看了李炎恢老师的<PHP第二季度视频>中的“章节7:创建TPL自定义模板”,做一个学习笔记,通过绘制架构图.UML类图和思维导图,来对加深理解. 2. 整体架构图 ...
- PHP-会员登录与注册例子解析-学习笔记
1.开始 最近开始学习李炎恢老师的<PHP第二季度视频>中的“章节5:使用OOP注册会员”,做一个学习笔记,通过绘制基本页面流程和UML类图,来对加深理解. 2.基本页面流程 3.通过UM ...
- 2014年暑假c#学习笔记目录
2014年暑假c#学习笔记 一.C#编程基础 1. c#编程基础之枚举 2. c#编程基础之函数可变参数 3. c#编程基础之字符串基础 4. c#编程基础之字符串函数 5.c#编程基础之ref.ou ...
- JAVA GUI编程学习笔记目录
2014年暑假JAVA GUI编程学习笔记目录 1.JAVA之GUI编程概述 2.JAVA之GUI编程布局 3.JAVA之GUI编程Frame窗口 4.JAVA之GUI编程事件监听机制 5.JAVA之 ...
- seaJs学习笔记2 – seaJs组建库的使用
原文地址:seaJs学习笔记2 – seaJs组建库的使用 我觉得学习新东西并不是会使用它就够了的,会使用仅仅代表你看懂了,理解了,二不代表你深入了,彻悟了它的精髓. 所以不断的学习将是源源不断. 最 ...
- CSS学习笔记
CSS学习笔记 2016年12月15日整理 CSS基础 Chapter1 在console输入escape("宋体") ENTER 就会出现unicode编码 显示"%u ...
- HTML学习笔记
HTML学习笔记 2016年12月15日整理 Chapter1 URL(scheme://host.domain:port/path/filename) scheme: 定义因特网服务的类型,常见的为 ...
随机推荐
- JQuery:通过noConflict()方法同时使用jQuery 和其他框架
jQuery - noConflict()方法 一.如何在页面上同时使用 jQuery 和其他框架?jQuery 和其他 JavaScript 框架正如您已经了解到的,jQuery 使用 $ 符号作为 ...
- Volume rendering
Volume rendering Reconstruction filter UCDAVIS
- 私有IP地址范围
私有IP地址范围 有一部分的地址专门是用于内网的地址,包括: A类中 10.0.0.0/8 B类中 172.16.0.0/12 ~ 172.31.0.0/12 C类中 192.168.0.0/16 这 ...
- iptables常用操作
1.iptables服务重启 service iptables restart 2.保存iptables规则 iptables-save > ~/iptables.save 3.恢复iptabl ...
- js 自动下载函数
function downloadFile(fileName, content){ var aLink = document.createElement('a'); var blob = new Bl ...
- 九个uname命令获取Linux系统详情的实例
当你在控制台模式下,无法通过“鼠标右键 > 关于”获取操作系统的信息.这时,在Linux下,你可以使用uname命令,帮助你完成这些工作. Uname是unix name的缩写.在控制台中实际使 ...
- python center, ljust, rjust
例子 >>> s = "jihite" >>> s.center(, "*") '**jihite**' >>& ...
- linux系统服务名称
服务列表(按字母顺序排列) 服务名 必需(是/否) 用途描述 acon 否 语言支持 acpi 否 电源管理 acpid 否 监听精灵进程 adsl 否 内部ADSL开关控制 alsa 否 高级Lin ...
- 使用Adreno Profiler分析android游戏
有时候我们需要对自己的游戏或者别人的游戏进行分析,比如我们需要了解一个引擎的大体渲染的流程,这个时候我们可以借助一些工具,在PC上我们可以使用Microsoft PIX.Intel GPA.Nvidi ...
- 续【C# 以管理员方式启动Winform,进而使用管理员控制Windows Service】
前提:在我们的域环境下,给分配了管理员级别两个账号(user0,user1). 需求:只允许一个账户运行进程"WindowsFormsApplication1": 1,)当已经运行 ...