linux v4l2 示例程序,Linux关于Camera使用V4L2

简介

Video for Linux two(Video4Linux2)简称V4L2,是V4L的改进版。V4L2是linux操作系统下用于采集图片、视频和音频数据的API接口。

工作流程

打开设备-> 初始化(检查和设置设备属性-> 设置帧格式-> 设置入输出方法)-> 处理帧数据(循环获取处理数据)-> 关闭设备。

应用程序与驱动进行信息交互通过ioctl函数实现。应用程序和设备有三种交换数据的方法,直接 read/write、内存映射(memory mapping)和用户指针。这里只讨论内存映射(memory mapping)。

设备信息初始化

初始化设备输入方式(内存映射)

设置摄像头输入格式YUV422

设置帧大小320*240

设置摄像头捕获方式预览(OPS_PREVIEW)

1. 打开设备

在V4L2中,视频设备被看做一个文件。使用open函数打开这个设备。用读写和非阻塞模式打开摄像头设备。

1

2

3

4

5#include

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

#include

int close(int fd);

例:

1

2static int fd= -1;

fd = open("/dev/video1", O_RDWR | O_NONBLOCK, 0);

2. 初始化设备

V4L2常用的命令标志符:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16VIDIOC_QUERYCAP /* 获取设备支持的操作 */

VIDIOC_G_FMT /* 读取当前视频捕获格式 */

VIDIOC_S_FMT /* 设置视频捕获格式 */

VIDIOC_REQBUFS /* 向驱动提出申请内存请求 */

VIDIOC_QUERYBUF /* 获取驱动申请到的内存信息 */

VIDIOC_QBUF /* 将空闲的内存加入可捕获视频的队列 */

VIDIOC_DQBUF /* 将已经捕获好视频的内存拉出已捕获视频的队列 */

VIDIOC_STREAMON /* 打开视频流 */

VIDIOC_STREAMOFF /* 关闭视频流 */

VIDIOC_QUERYCTRL /* 查询驱动是否支持该命令 */

VIDIOC_G_CTRL /* 获取当前命令值 */

VIDIOC_S_CTRL /* 设置新的命令值 */

VIDIOC_G_TUNER /* 获取调谐器信息 */

VIDIOC_S_TUNER /* 设置调谐器信息 */

VIDIOC_G_FREQUENCY /* 获取调谐器频率 */

VIDIOC_S_FREQUENCY /* 设置调谐器频率 */

V4L2常用数据结构:

1

2

3

4

5

6

7

8struct 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 //视频制式

2.1 获取设备支持的操作和相关信息(VIDIOC_QUERYCAP)

可以使用该命令来获取当前设备支持的操作相关信息,进一步判断该设备是否支持V4L2规范。

1

2

3

4

5

6

7

8

9struct v4l2_capability

{

u8 driver[16]; // 驱动名字

u8 card[32]; // 设备名字

u8 bus_info[32]; // 设备在系统中的位置

u32 version; // 驱动版本号

u32 capabilities; // 设备支持的操作

u32 reserved[4]; // 保留字段

};

1

2struct v4l2_capability cap;

ioctl(fd, VIDIOC_QUERYCAP, &cap);

2.2 获取缓冲帧的地址和长度(VIDIOC_CROPCAP)

根据获取到信息(帧地址和长度)映射到内存地址。

1

2

3

4

5

6

7struct v4l2_cropcap

{

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

struct v4l2_rect bounds; // 最大边界

struct v4l2_rect defrect; // 默认值

struct v4l2_fract pixelaspect;

};

1

2

3struct v4l2_cropcap cropcap

cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

ioctl(fd, VIDIOC_CROPCAP, &cropcap);

2.3 图像的缩放(VIDIOC_S_CROP)

1

2

3

4

5struct v4l2_crop

{

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

struct v4l2_rect c;

}

1

2

3

4struct v4l2_crop crop;

crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

crop.c = cropcap.defrect;/* reset to default */

ioctl(fd, VIDIOC_S_CROP, &crop);

3. 设置图像信息

3.1 选则当前输入一个设备节点对应多个视频源中的一个(VIDIOC_S_INPUT)

1

2int idx = 0;

ioctl(fd, VIDIOC_S_INPUT, &idx);

3.2 设置帧格式(VIDIOC_S_FMT)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24struct 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

{

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];

};

};

1

2

3

4

5

6

7

8

9struct v4l2_format fmt;

fmt.type= V4L2_BUF_TYPE_VIDEO_CAPTURE;

fmt.fmt.pix.width= camera_inf->param.width;

fmt.fmt.pix.height= camera_inf->param.height;

fmt.fmt.pix.pixelformat= camera_inf->fmt.fourcc;

fmt.fmt.pix.field= V4L2_FIELD_ANY;/*V4L2_FIELD_NONE;*/

ioctl(fd, VIDIOC_S_FMT, &fmt);

3.3 获取帧信息(VIDIOC_G_FMT)

1ioctl(fd, VIDIOC_G_FMT, &fmt);

4. 建立内存映射

4.1 向设备申请缓冲区(VIDIOC_REQBUFS)

1

2

3

4

5

6

7

8

9

10

11

12

13struct v4l2_requestbuffers req;

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};

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

1

2

3

4

5

6

7struct v4l2_requestbuffers req;

req.count= 2;

req.type= V4L2_BUF_TYPE_VIDEO_CAPTURE;

req.memory= V4L2_MEMORY_MMAP;

ioctl(fd, VIDIOC_REQBUFS, &req);

4.2 获取缓冲帧的地址和长度,并将申请到的缓冲帧映射到应用程序,buffers指针(VIDIOC_QUERYBUF)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24struct buffer *buffers;

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 有效

__u32 userptr;

};

__u32 length; // 缓冲帧长度

__u32 input;

__u32 reserved;

};

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22struct buffer

{

void *start;

size_t length;

};

struct buffer *buffers= NULL;

buffers = malloc(req.count * sizeof(*buffers));

for (n_buffers=0; 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;

ioctl(fd, VIDIOC_QUERYBUF, &buf);

buffers[n_buffers].length = buf.length;

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

}

5. 获帧图像数据

当我们把缓冲区设置好之后,就可以开始获取数据了。可以通过VIDIOC_STREAMON / VIDIOC_STREAMOFF启动或停止数据流。

5.1 把帧放入队列(VIDIOC_QBUF)

1

2

3

4

5

6

7

8

9

10

11

12

13enum v4l2_buf_type type;

for (i=0; 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;

ioctl(fd, VIDIOC_QBUF, &buf);

}

5.2 启动获取帧数据流(VIDIOC_STREAMON)

1

2type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

ioctl(fd, VIDIOC_STREAMON, &type);

5.3 读取帧数据并处理(VIDIOC_DQBUF)

首先读取帧队列并处理,处理后再次把帧缓存加入队列,待下一次读取帧队列数据。

1

2

3

4

5

6

7

8struct v4l2_buffer buf;

buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

buf.memory = V4L2_MEMORY_MMAP;

ioctl(fd, VIDIOC_DQBUF, &buf);

camera_inf->buf_vaddr = (void *)buffers[buf.index].start;

camera_ctl->ops.priview_picture(camera_inf);//调用回调函数处理帧数据

6. 关闭设备

6.1 关闭视频流(VIDIOC_STREAMOFF)

关闭视频流,munmap解除参数start所指向的内存起始地存起始地址。

1

2

3

4

5

6

7

8

9

10

11enum v4l2_buf_type type;

type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

ioctl(fd, VIDIOC_STREAMOFF, &type);

for (i=0; i

{

munmap(buffers[i].start, buffers[i].length);

}

close(fd);

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值