请选择 进入手机版 | 继续访问电脑版
搜索
查看: 13563|回复: 6

【RSun+手记11】Linux下基于V4L2 USB Camera操作

[复制链接]

16

主题

36

帖子

276

积分

中级会员

Rank: 3Rank: 3

积分
276
发表于 2014-5-23 13:28 | 显示全部楼层 |阅读模式
       一直在做的视频流硬件加速工作基本结束, 最后验证的时候发现一个问题,为了计算视频流加速的加速比打算采用1920x1080的分辨率,买的USB camera是1080P的,在window下也正常工作; 但是我用的opencv的VideoCap类打开摄像头的, 发现其不能设置分辨率(HIGHGUI ERROR: V4L/V4L2: VIDIOC_S_CROP),导致摄像头的分辨率一直是640x480的。因为Opencv摄像头才做也是基于UVC驱动,利用V4L2 API数据采集的, 所以自己根据V4L2重写了摄像头操作相关的API, 发现可以调节分辨率和帧率等信息的, Opencv的驱动应该没写好。
 
      LInux UVC是视频设备驱动,使用前应确保将其加入到linux kernel中(可参考 [RSun +手记6]解决OpenCV不能在目标板中打开摄像头问题),配置完成后,目标板上电后插入USB摄像头, 如果有/dev/video0出现,说明UVC驱动应该没问题。V4L2是linux下根据UVC驱动开发的API,核心是通过ioctl函数实现的。 ioctl是设备驱动程序中对IO通道进行管理的函数,主要是对设备控制/数据寄存器等的读写控制。现在USB摄像头一般都支持iotcl管理的。参考博客:Linux2.6.33下ZC301USB摄像头使用教程
    我将摄像头的操作封装成v4l2cap类(见附件源码)的,具体的操作和注意事项如下:
一:打开摄像头
  linux下驱动设备与文件设备类似,也是可以通告open函数打开的:
vd_info->camfd = open(device, O_RDWR | O_NONBLOCK, 0);
这里需要注意的是打开方式, 加 O_NONBLOCK表示非阻塞方式打开,否则为阻塞方式打开。这一点对摄像头数据采集速度非常重要。非阻塞方式在不主动询问时,会自动采集数据,但是阻塞方式只有在询问后才开始采集。当用定时器进行数据采集时,如果使用阻塞方式,将会额外消耗(800 x 600) 30ms时间(实验测量),非阻塞方式就基本不用等待时间了。所以建议大家使用非阻塞方式。
二:获取摄像头属性
    memset(&vd_info->cap, 0, sizeof(struct v4l2_capability));
    if ( -1 == ioctl(vd_info->camfd, VIDIOC_QUERYCAP,&(vd_info->cap)))
  摄像头属性在v4l2_capability中定义(videodev2.h),包含了driver, bus_info, version等信息。
三:摄像头视频格式
   摄像头视频格式定义在v4l2_format中,在videodev2.h中可以看到几十种视频格式,包括RGB, MJPG,YUYV等,但是常见的摄像头一般只支持V4L2_PIX_FMT_MJPEG和V4L2_PIX_FMT_YUYV格式,为了得到RGB格式图像,MJPG需要解码, YUYV需要YUV到RGB的转换。获取视频格式操作:
   memset(&vd_info->fmt,0,sizeof(struct v4l2_format));
    vd_info->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if (-1 == ioctl(vd_info->camfd, VIDIOC_G_FMT, &(vd_info->fmt)))

设置摄像头格式类似, 只需将VIDiO_G_FMT改为VIDIOC_S_FMT(G=>Get, S=>Set)
四:设置帧率
    struct v4l2_streamparm *parm = new struct v4l2_streamparm;
     memset(parm,0,sizeof(struct v4l2_streamparm));
     parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
//     parm->parm.capture.capturemode = V4L2_MODE_HIGHQUALITY;
     parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
     parm->parm.capture.timeperframe.denominator = 40 ;
     parm->parm.capture.timeperframe.numerator = 1;
     if(-1 == ioctl(vd_info->camfd,VIDIOC_S_PARM,parm))
帧率信息定义在v4l2_streamparm结构体中,parm.capture.timeperframe.denominator就是帧率(frame/s),每帧的时间是1000ms/parm.capture.timeperframe.denominator。帧率的设置不是随意的,各个分辨率下硬件支持的分辨率各不相同,硬件会选择最接近的帧率。比如640*480帧率33,800*600只有15,1920*1080只有5了。
五:抓取帧数据
 摄像头正常打开后,需要设置存储b帧数据的buffer等信息,之后就可以抓取帧数据:
    memset(&vd_info->buf, 0, sizeof(struct v4l2_buffer));
    vd_info->buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    vd_info->buf.memory = V4L2_MEMORY_MMAP;

    if (-1 = ioctl(vd_info->camfd, VIDIOC_DQBUF, &vd_info->buf))
 当采用非阻塞方式打开摄像头时, 不是每次抓取帧数据都会成功, 因为时间不够或者冲突等,但是多抓几次总能抓的到的。
 
除了上面讲的基本操作,还有许多相关的配置, 我将其封装在附件的v4l2cap类中,希望对大家有所帮助。

v4l2cap.zip

3.66 KB, 下载次数: 601

回复

使用道具 举报

0

主题

40

帖子

321

积分

中级会员

Rank: 3Rank: 3

积分
321
发表于 2014-6-6 08:32 | 显示全部楼层

RE:【RSun+手记11】Linux下基于V4L2 USB Camera操作

看看,正需要摄像头方面的
回复 支持 反对

使用道具 举报

901

主题

2703

帖子

8万

积分

论坛元老

Rank: 8Rank: 8

积分
82017
发表于 2014-6-6 09:11 | 显示全部楼层

RE:【RSun+手记11】Linux下基于V4L2 USB Camera操作

灰常赞!
回复 支持 反对

使用道具 举报

0

主题

13

帖子

98

积分

初级会员

Rank: 2

积分
98
发表于 2014-7-19 20:21 | 显示全部楼层

RE:【RSun+手记11】Linux下基于V4L2 USB Camera操作

谢谢分享,我的摄像头在linux用opencv就只能是哦嗯640480的像素,郁闷死我了
回复 支持 反对

使用道具 举报

0

主题

1

帖子

4

积分

新手上路

Rank: 1

积分
4
发表于 2014-8-2 19:18 | 显示全部楼层

RE:【RSun+手记11】Linux下基于V4L2 USB Camera操作

顶起啦!!!!!!!!!!!!!!!!!!!!!!
回复 支持 反对

使用道具 举报

0

主题

1

帖子

0

积分

新手上路

Rank: 1

积分
0
发表于 2014-8-5 09:14 | 显示全部楼层

回复:【RSun+手记11】Linux下基于V4L2 USB Camera操作

 谢谢,正需要呢
回复 支持 反对

使用道具 举报

0

主题

2

帖子

5

积分

新手上路

Rank: 1

积分
5
发表于 2016-4-13 10:14 | 显示全部楼层
写得很简洁,感谢分享
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

站长推荐上一条 /2 下一条

QQ|小黑屋|手机版|Archiver|OpenHW技术社区

GMT+8, 2018-12-10 20:45 , Processed in 0.058255 second(s), 10 queries , MemCache On.

Powered by Discuz! X3.4

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表