V4L2编程初体验

来源:互联网 发布:国服魔兽世界mac版 编辑:程序博客网 时间:2024/05/16 15:55

内容摘要:

      Video for Linux two(Video4Linux2)简称V4L2,是V4L的改进版。V4L2是linux操作系统下用于采集图片、视频和音频数据的API接口,配合适当的视频采集设备和相应的驱动程序,可以实现图片、视频、音频等的采集。在远程会议、可视电话、视频监控系统和嵌入式多媒体终端中都有广泛的应用。在Linux中,视频设备是设备文件,可以像访问普通文件一样对其进行读写,摄像头在/dev/video2下。

     最近想做智能机器人,想加上视频采集这个模块,于是对linux下的视频方面的编程产生了兴趣,首先从入门开始吧!

一、Video for Linux Tow

      在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的介绍网上很多,这里简单说下我们经常常用相关结构体:

struct v4l2_requestbuffers reqbufs;//向驱动申请帧缓冲的请求,里面包含申请的个数
struct v4l2_capability cap;//这个设备的功能,比如是否是视频输入设备
struct v4l2_standard std;//视频的制式,比如PAL,NTSC
struct v4l2_format fmt;//帧的格式,比如宽度,高度等

struct v4l2_buffer buf;//代表驱动中的一帧
v4l2_std_id stdid;//视频制式,例如:V4L2_STD_PAL_B
struct v4l2_queryctrl query;//查询的控制
struct v4l2_control control;//具体控制的值

    下面就介绍相关的操作流程:

1.打开设备文件。 int fd=open(”/dev/video2″,O_RDWR);
2.取得设备的capability,看看设备具有什么功能,比如是否具有视频输入,或者音频输入输出等。VIDIOC_QUERYCAP,struct v4l2_capability
3.设置视频的制式和帧格式,制式包括PAL,NTSC,帧的格式个包括宽度和高度等。
VIDIOC_S_STD,VIDIOC_S_FMT,struct v4l2_std_id,struct v4l2_format
4.向驱动申请帧缓冲,一般不超过5个。struct v4l2_requestbuffers
5.将申请到的帧缓冲映射到用户空间,这样就可以直接操作采集到的帧了,而不必去复制。mmap
6.将申请到的帧缓冲全部入队列,以便存放采集到的数据.VIDIOC_QBUF,struct v4l2_buffer
7.开始视频的采集。VIDIOC_STREAMON
8.出队列以取得已采集数据的帧缓冲,取得原始采集数据。VIDIOC_DQBUF
9.将缓冲重新入队列尾,这样可以循环采集。VIDIOC_QBUF
10.停止视频的采集。VIDIOC_STREAMOFF
11.关闭视频设备。close(fd);

 

以下详细介绍操作流程(相信对新手有用):

1. 定义

V4L2(Video For Linux Two) 是内核提供给应用程序访问音、视频驱动的统一接口。

2. 工作流程:

打开设备-> 检查和设置设备属性-> 设置帧格式-> 设置一种输入输出方法(缓冲区管理)-> 循环获取数据-> 关闭设备。

3. 设备的打开和关闭:

#include <fcntl.h>

int open(const char *device_name, int flags);

#include <unistd.h>

int close(int fd);

例:

int fd=open(“/dev/video2”,O_RDWR);// 打开设备

close(fd);// 关闭设备

注意:V4L2 的相关定义包含在头文件<linux/videodev2.h> 中.

4. 查询设备属性: VIDIOC_QUERYCAP

相关函数:

int ioctl(int fd, int request, struct v4l2_capability *argp);

相关结构体:

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 // 是否支持图像获取

例:显示设备信息

struct v4l2_capability cap;

ioctl(fd,VIDIOC_QUERYCAP,&cap);

printf(“Driver Name:%s/nCard Name:%s/nBus info:%s/nDriver Version:%u.%u.%u/n”,cap.driver,cap.card,cap.bus_info,(cap.version>>16)&0XFF, (cap.version>>8)&0XFF,cap.version&OXFF);

5. 帧格式:

VIDIOC_ENUM_FMT // 显示所有支持的格式

int ioctl(int fd, int request, struct v4l2_fmtdesc *argp);

struct v4l2_fmtdesc

{

__u32 index; // 要查询的格式序号,应用程序设置

enum v4l2_buf_type type; // 帧类型,应用程序设置

__u32 flags; // 是否为压缩格式

__u8 description[32]; // 格式名称

__u32 pixelformat; // 格式

__u32 reserved[4]; // 保留

};

例:显示所有支持的格式

struct v4l2_fmtdesc fmtdesc;

fmtdesc.index=0;

fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;

printf("Support format:/n");

while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)!=-1)

{

printf("/t%d.%s/n",fmtdesc.index+1,fmtdesc.description);

fmtdesc.index++;

}

// 查看或设置当前格式

VIDIOC_G_FMT, VIDIOC_S_FMT

// 检查是否支持某种格式

VIDIOC_TRY_FMT

int ioctl(int fd, int request, struct v4l2_format *argp);

struct v4l2_format

{

enum v4l2_buf_type type;// 帧类型,应用程序设置

union fmt

{

struct v4l2_pix_format pix;// 视频设备使用

struct v4l2_window win;

struct v4l2_vbi_format vbi;

struct v4l2_sliced_vbi_format sliced;

__u8 raw_data[200];

};

};

struct v4l2_pix_format

{

__u32 width; // 帧宽,单位像素

__u32 height; // 帧高,单位像素

__u32 pixelformat; // 帧格式

enum v4l2_field field;

__u32 bytesperline;

__u32 sizeimage;

enum v4l2_colorspace colorspace;

__u32 priv;

};

例:显示当前帧的相关信息

struct v4l2_format fmt;

fmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;

ioctl(fd,VIDIOC_G_FMT,&fmt);

printf(“Current data format information:/n/twidth:%d/n/theight:%d/n”,fmt.fmt.width,fmt.fmt.height);

struct v4l2_fmtdesc fmtdesc;

fmtdesc.index=0;

fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;

while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)!=-1)

{

if(fmtdesc.pixelformat & fmt.fmt.pixelformat)

{

printf(“/tformat:%s/n”,fmtdesc.description);

break;

}

fmtdesc.index++;

}

例:检查是否支持某种帧格式

struct v4l2_format fmt;

fmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;

fmt.fmt.pix.pixelformat=V4L2_PIX_FMT_RGB32;

if(ioctl(fd,VIDIOC_TRY_FMT,&fmt)==-1)

if(errno==EINVAL)

printf(“not support format RGB32!/n”);

6. 图像的缩放

VIDIOC_CROPCAP

int ioctl(int fd, int request, struct v4l2_cropcap *argp);

struct v4l2_cropcap

{

enum v4l2_buf_type type;// 应用程序设置

struct v4l2_rect bounds;// 最大边界

struct v4l2_rect defrect;// 默认值

struct v4l2_fract pixelaspect;

};

// 设置缩放

VIDIOC_G_CROP,VIDIOC_S_CROP

int ioctl(int fd, int request, struct v4l2_crop *argp);

int ioctl(int fd, int request, const struct v4l2_crop *argp);

struct v4l2_crop

{

enum v4l2_buf_type type;// 应用程序设置

struct v4l2_rect c;

}

7. 申请和管理缓冲区,应用程序和设备有三种交换数据的方法,直接 read/write ,内存映射(memory mapping) ,用户指针。这里只讨论 memory mapping.

// 向设备申请缓冲区

VIDIOC_REQBUFS

int ioctl(int fd, int request, struct v4l2_requestbuffers *argp);

struct v4l2_requestbuffers

{

__u32 count; // 缓冲区内缓冲帧的数目

enum v4l2_buf_type type; // 缓冲帧数据格式

enum v4l2_memory memory; // 区别是内存映射还是用户指针方式

__u32 reserved[2];

};

enum v4l2_memoy {V4L2_MEMORY_MMAP,V4L2_MEMORY_USERPTR};

//count,type,memory 都要应用程序设置

例:申请一个拥有四个缓冲帧的缓冲区

struct v4l2_requestbuffers req;

req.count=4;

req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;

req.memory=V4L2_MEMORY_MMAP;

ioctl(fd,VIDIOC_REQBUFS,&req);

获取缓冲帧的地址,长度:

VIDIOC_QUERYBUF

int ioctl(int fd, int request, struct v4l2_buffer *argp);

struct v4l2_buffer

{

__u32 index; //buffer 序号

enum v4l2_buf_type type; //buffer 类型

__u32 byteused; //buffer 中已使用的字节数

__u32 flags; // 区分是MMAP 还是USERPTR

enum v4l2_field field;

struct timeval timestamp;// 获取第一个字节时的系统时间

struct v4l2_timecode timecode;

__u32 sequence; // 队列中的序号

enum v4l2_memory memory;//IO 方式,被应用程序设置

union m

{

__u32 offset;// 缓冲帧地址,只对MMAP 有效

unsigned long userptr;

};

__u32 length;// 缓冲帧长度

__u32 input;

__u32 reserved;

};

MMAP ,定义一个结构体来映射每个缓冲帧。

Struct buffer

{

void* start;

unsigned int length;

}*buffers;

#include <sys/mman.h>

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

//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 为映射后的内存长度

例:将四个已申请到的缓冲帧映射到应用程序,用buffers 指针记录。

buffers = (buffer*)calloc (req.count, sizeof (*buffers));

if (!buffers) {

fprintf (stderr, "Out of memory/n");

exit (EXIT_FAILURE);

}

// 映射

for (unsigned int n_buffers = 0; n_buffers < req.count; ++n_buffers) {

struct v4l2_buffer buf;

memset(&buf,0,sizeof(buf));

buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

buf.memory = V4L2_MEMORY_MMAP;

buf.index = n_buffers;

// 查询序号为n_buffers 的缓冲区,得到其起始物理地址和大小

if (-1 == ioctl (fd, VIDIOC_QUERYBUF, &buf))

exit(-1);

buffers[n_buffers].length = buf.length;

// 映射内存

buffers[n_buffers].start =mmap (NULL,buf.length,PROT_READ | PROT_WRITE ,MAP_SHARED,fd, buf.m.offset);

if (MAP_FAILED == buffers[n_buffers].start)

exit(-1);

}

8. 缓冲区处理好之后,就可以开始获取数据了

// 启动/ 停止数据流

VIDIOC_STREAMON,VIDIOC_STREAMOFF

int ioctl(int fd, int request, const int *argp);

//argp 为流类型指针,如V4L2_BUF_TYPE_VIDEO_CAPTURE.

在开始之前,还应当把缓冲帧放入缓冲队列:

VIDIOC_QBUF// 把帧放入队列

VIDIOC_DQBUF// 从队列中取出帧

int ioctl(int fd, int request, struct v4l2_buffer *argp);

例:把四个缓冲帧放入队列,并启动数据流

unsigned int i;

enum v4l2_buf_type type;

// 将缓冲帧放入队列

for (i = 0; i < 4; ++i)

{

struct v4l2_buffer buf;

buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

buf.memory = V4L2_MEMORY_MMAP;

buf.index = i;

ioctl (fd, VIDIOC_QBUF, &buf);

}

type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

ioctl (fd, VIDIOC_STREAMON, &type);

// 这有个问题,这些buf 看起来和前面申请的buf 没什么关系,为什么呢?

例:获取一帧并处理

struct v4l2_buffer buf;

CLEAR (buf);

buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

buf.memory = V4L2_MEMORY_MMAP;

// 从缓冲区取出一个缓冲帧

ioctl (fd, VIDIOC_DQBUF, &buf);

// 图像处理

process_image (buffers[buf.index].start);

// 将取出的缓冲帧放回缓冲区

ioctl (fd, VIDIOC_QBUF, &buf);

 

关于视频采集方式

操作系统一般把系统使用的内存划分成用户空间和内核空间,分别由应用程序管理和操作系统管理。应用程序可以直接访问内存的地址,而内核空间存放的是供内核访问的代码和数据,用户不能直接访问。v4l2捕获的数据,最初是存放在内核空间的,这意味着用户不能直接访问该段内存,必须通过某些手段来转换地址。

一共有三种视频采集方式:使用read、write方式;内存映射方式和用户指针模式。

read、write方式:在用户空间和内核空间不断拷贝数据,占用了大量用户内存空间,效率不高。

内存映射方式:把设备里的内存映射到应用程序中的内存控件,直接处理设备内存,这是一种有效的方式。上面的mmap函数就是使用这种方式。

用户指针模式:内存片段由应用程序自己分配。这点需要在v4l2_requestbuffers里将memory字段设置成V4L2_MEMORY_USERPTR。

处理采集数据

V4L2有一个数据缓存,存放req.count数量的缓存数据。数据缓存采用FIFO的方式,当应用程序调用缓存数据时,缓存队列将最先采集到的 视频数据缓存送出,并重新采集一张视频数据。这个过程需要用到两个ioctl命令,VIDIOC_DQBUF和VIDIOC_QBUF:

structv4l2_buffer buf;

memset(&buf,0,sizeof(buf));

buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;

buf.memory=V4L2_MEMORY_MMAP;

buf.index=0;

//读取缓存

if(ioctl(cameraFd, VIDIOC_DQBUF, &buf) == -1)

{

return-1;

}

//…………视频处理算法

//重新放入缓存队列

if(ioctl(cameraFd, VIDIOC_QBUF, &buf) == -1) {

return-1;

}

关闭视频设备

使用close函数关闭一个视频设备

close(cameraFd)

还需要使用munmap方法。

 

下面是我自己参照网上一步步写的,成功采集.

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
/*=============================================================================
#     FileName: v4l2.c
#         Desc: this program aim to get image from USB camera,
#               used the V4L2 interface.
#       Author: LiXiaoming
#        Email: lixiaoming5700@gmail.com
#     HomePage: http://www.cnblogs.com/lixiaoming90
#      Version: 0.0.1
#   LastChange: 2012-08-22 15:52:37
#      History:
=============================================================================*/
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <linux/types.h>
#include <linux/videodev2.h>
#include <malloc.h>
#include <math.h>
#include <string.h>
#include <sys/mman.h>
#include <errno.h>
#include <assert.h>
 
#define FILE_VIDEO  "/dev/video2"
#define JPG "/lxm/picture/image%d.jpg"
 
typedef struct{
    void *start;
    int length;
}BUFTYPE;
BUFTYPE *usr_buf;
static unsignedint n_buffer = 0;
 
//set video capture ways(mmap)
int init_mmap(int fd)
{
    //to request frame cache, contain requested counts
    struct v4l2_requestbuffers reqbufs;
    //request V4L2 driver allocation video cache
    //this cache is locate in kernel and need mmap mapping
    memset(&reqbufs, 0, sizeof(reqbufs));
    reqbufs.count = 4;
    reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    reqbufs.memory = V4L2_MEMORY_MMAP;
 
    if(-1 == ioctl(fd,VIDIOC_REQBUFS,&reqbufs)){
        perror("Fail to ioctl 'VIDIOC_REQBUFS'");
        exit(EXIT_FAILURE);
    }
 
    n_buffer = reqbufs.count;
    printf("n_buffer = %d\n", n_buffer);
    usr_buf = calloc(reqbufs.count,sizeof(usr_buf));
    if(usr_buf == NULL){
        printf("Out of memory\n");
        exit(-1);
    }
 
    //map kernel cache to user process
    for(n_buffer = 0; n_buffer < reqbufs.count; ++n_buffer){
        //stand for a frame
        struct v4l2_buffer buf;
        memset(&buf, 0, sizeof(buf));
        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory = V4L2_MEMORY_MMAP;
        buf.index = n_buffer;
         
        //check the information of the kernel cache requested
        if(-1 == ioctl(fd,VIDIOC_QUERYBUF,&buf))
        {
            perror("Fail to ioctl : VIDIOC_QUERYBUF");
            exit(EXIT_FAILURE);
        }
 
        usr_buf[n_buffer].length = buf.length;
        usr_buf[n_buffer].start =
            (char *)mmap(
                    NULL,
                    buf.length,
                    PROT_READ | PROT_WRITE,
                    MAP_PRIVATE,
                    fd,
                    buf.m.offset
                );
        if(MAP_FAILED == usr_buf[n_buffer].start)
        {
            perror("Fail to mmap");
            exit(EXIT_FAILURE);
        }
    }
    return 0;
}
 
//initial camera device
int init_camera_device(int fd)
{
    //decive fuction, such as video input
    struct v4l2_capability cap;
    //video standard,such as PAL,NTSC
    struct v4l2_standard std;
    //frame format
    struct v4l2_format tv_fmt;
    //check control
    struct v4l2_queryctrl query;
    //detail control value
    struct v4l2_fmtdesc fmt;
    int ret;
    //get the format of video supply
    memset(&fmt, 0, sizeof(fmt));
    fmt.index = 0;
    //supply to image capture
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    // show all format of supply
    printf("Support format:\n");
    while(ioctl(fd, VIDIOC_ENUM_FMT, &fmt) == 0){
        fmt.index++;
        printf("pixelformat = ''%c%c%c%c''\ndescription = ''%s''\n",fmt.pixelformat & 0xFF, (fmt.pixelformat >> 8) & 0xFF,(fmt.pixelformat >> 16) & 0xFF, (fmt.pixelformat >> 24) & 0xFF,fmt.description);
    }
    //check video decive driver capability
    ret = ioctl(fd, VIDIOC_QUERYCAP, &cap);
    if(ret < 0){
        perror("Fail to ioctl VIDEO_QUERYCAP");
        exit(EXIT_FAILURE);
    }
 
    //judge wherher or not to be a video-get device
    if(!(cap.capabilities & V4L2_BUF_TYPE_VIDEO_CAPTURE))
    {
        printf("The Current device is not a video capture device\n");
        exit(-1);
    }
 
    //judge whether or not to supply the form of video stream
    if(!(cap.capabilities & V4L2_CAP_STREAMING))
    {
        printf("The Current device does not support streaming i/o\n");
        exit(EXIT_FAILURE);
    }
 
    //set the form of camera capture data
    tv_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    tv_fmt.fmt.pix.width = 680;
    tv_fmt.fmt.pix.height = 480;
    tv_fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
    tv_fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
    if (ioctl(fd, VIDIOC_S_FMT, &tv_fmt)< 0) {
        printf("VIDIOC_S_FMT\n");
        exit(-1);
        close(fd);
    }
    //initial video capture way(mmap)
    init_mmap(fd);
    return 0;
}
 
int open_camera_device()
{
    int fd;
    //open video device with block
    fd = open(FILE_VIDEO, O_RDONLY);
    if(fd < 0){
        perror(FILE_VIDEO);
        exit(EXIT_FAILURE);
    }
    return fd;
}
 
int start_capture(int fd)
{
    unsignedint i;
    enum v4l2_buf_type type;
    //place the kernel cache to a queue
    for(i = 0; i < n_buffer; i++){
        struct v4l2_buffer buf;
        memset(&buf, 0, sizeof(buf));
        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory = V4L2_MEMORY_MMAP;
        buf.index = i;
 
        if(-1 == ioctl(fd, VIDIOC_QBUF, &buf)){
            perror("Fail to ioctl 'VIDIOC_QBUF'");
            exit(EXIT_FAILURE);
        }
    }
 
    //start capture data
    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if(-1 == ioctl(fd, VIDIOC_STREAMON, &type)){
        printf("i=%d.\n", i);
        perror("VIDIOC_STREAMON");
        close(fd);
        exit(EXIT_FAILURE);
    }
    return 0;
}
 
int process_image(void *addr,int length)
{
    FILE *fp;
    static int num = 0;
    char image_name[20];
 
    sprintf(image_name, JPG, num++);
    if((fp = fopen(image_name,"w")) == NULL){
        perror("Fail to fopen");
        exit(EXIT_FAILURE);
    }
    fwrite(addr, length, 1, fp);
    usleep(500);
    fclose(fp);
    return 0;
}
 
int read_frame(int fd)
{
    struct v4l2_buffer buf;
    unsignedint i;
    memset(&buf, 0, sizeof(buf));
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;
    //put cache from queue
    if(-1 == ioctl(fd, VIDIOC_DQBUF,&buf)){
        perror("Fail to ioctl 'VIDIOC_DQBUF'");
        exit(EXIT_FAILURE);
    }
 
    assert(buf.index < n_buffer);
    //read process space's data to a file
    process_image(usr_buf[buf.index].start, usr_buf[buf.index].length);
    if(-1 == ioctl(fd, VIDIOC_QBUF,&buf)){
        perror("Fail to ioctl 'VIDIOC_QBUF'");
        exit(EXIT_FAILURE);
    }
    return 1;
}
 
int mainloop(int fd)
{
    int count = 10;
 
    while(count-- > 0)
    {
        for(;;)
        {
            fd_set fds;
            struct timeval tv;
            int r;
 
            FD_ZERO(&fds);
            FD_SET(fd,&fds);
 
            /*Timeout*/
            tv.tv_sec = 2;
            tv.tv_usec = 0;
            r = select(fd + 1,&fds,NULL,NULL,&tv);
 
            if(-1 == r)
            {
                if(EINTR == errno)
                    continue;
                perror("Fail to select");
                exit(EXIT_FAILURE);
            }
 
            if(0 == r)
            {
                fprintf(stderr,"select Timeout\n");
                exit(-1);
            }
 
            if(read_frame(fd))
                break;
        }
    }
    return 0;
}
 
void stop_capture(int fd)
{
    enum v4l2_buf_type type;
    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if(-1 == ioctl(fd,VIDIOC_STREAMOFF,&type))
    {
        perror("Fail to ioctl 'VIDIOC_STREAMOFF'");
        exit(EXIT_FAILURE);
    }
    return;
}
 
void close_camera_device(int fd)
{
    unsignedint i;
    for(i = 0;i < n_buffer; i++)
    {
        if(-1 == munmap(usr_buf[i].start,usr_buf[i].length)){
            exit(-1);
        }
    }
    free(usr_buf);
 
    if(-1 == close(fd))
    {
        perror("Fail to close fd");
        exit(EXIT_FAILURE);
    }
    return;
}
 
int main()
{
    int fd;
    fd = open_camera_device();
    init_camera_device(fd);
    start_capture(fd);
    mainloop(fd);
    stop_capture(fd);
    close_camera_device(fd);
    return 0;
}

 

   终于可以读取USB摄像头的图像了, 感谢前人的文章。后面需要编写的就是图像处理,例如H.264编码和通过UDP数据传输到客户端等等。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 媳妇要离婚我想要孩子该怎么办 媳妇带了避孕环我想要孩子怎么办 新开的文具店一点生意都没有怎么办 孩子在学校被坏孩子欺负了该怎么办 老师像个傻叉我妈还喷我我怎么办啊 承台上预埋桥墩连接钢筋错了怎么办 冲床油缸螺栓拆不下来怎么办 汇款到账银行写错了怎么办 搜狗输入法数字序号超过20怎么办 苹果手机保存的图片变模糊怎么办 微信视频保存到手机变模糊怎么办 自己的位置被别人取代了怎么办 给工厂做半成品老板跑了怎么办 微信变成英文再恢复汉字怎么办 cad中标注尺寸数字太小怎么办 扣扣的钱包手势密码忘记了怎么办 台式电脑带符号的数字打不出怎么办 情侣之间出现看见对方就烦怎么办 电信卡号和联通卡号怎么办情侣号 电脑能登qq但打不开网页怎么办 想跟朋友聊天但对方不理怎么办 刚进婆家门被婆婆欺负怎么办 支付宝的聊天记录被删了怎么办 彩票站买彩票把钱付了没出票怎么办 与异性朋友聊天没话题了怎么办 快递写错地址但已经发货了怎么办 快递写错电话但已经发货了怎么办 微信添加好友功能被限制怎么办 qq号被冻结了限制解封怎么办 被别人强制拉入qq群怎么办 qq群里的图片过期了怎么办 q附近人不能关注不能发信息怎么办 qq畅聊之火掉了怎么办 打印机打印时上面空白留太多怎么办 发短信一直空格里面写0怎么办 网贷获取我新手机号通讯录怎么办 系统音频驱动异常或未安装怎么办 附近功能已屏蔽你的qq好友怎么办 新申请的qq号忘了怎么办 刚申请的qq号忘了怎么办 以前申请的qq号忘了怎么办