Video4Linux2:
该应用使用的是USB摄像头,在Linux中内核驱动框架多采Video4Linux2(V4L2),V4L2为上层的访问底层的视频设备提供了统一的接口,提取出公共代码避免底层硬件差异。 V4L2支持三类设备:视频输入输出设备、VBI设备和radio设备及更多。下图V4L2在Linux系统中的结构图:
USB摄像头初始化:
1.打开设备文件:
fd =open("dev/video", O_RDWR);
2.查询设备支持的能力:
ioctl(fd, VIDIOC_QUERYCAP, &tV4l2Cap);
3.获取当前视频设备支持的视频格式:
摄像头的输出格式多种多样,有可能是JPEG、RGB、YUYV等等,而LCD显示屏需要输入RGB格式数据。因此,此时需获取当前摄像头支持的视频格式,以为后续数据格式转换作准备。
ioctl(fd, VIDIOC_ENUM_FMT, &tFmtDesc)
4.查看驱动程序是否支持当前LCD分辨率,若不支持,将调整参数,传回应用层:
ioctl(iFd, VIDIOC_S_FMT, &tV4l2Fmt);
5.请求V4L2驱动分配视频缓冲区,V4L2是视频设备的驱动层,位于内核空间,所以通过VIDIOC_REQBUFS控制命令申请的内存位于内核空间,应用程序不能直接访问,需要通过调用mmap内存映射函数把内核空间内存映射到用户空间后,应用程序通过访问用户空间地址来访问内核空间:
ioctl(iFd, VIDIOC_REQBUFS, &tV4l2ReqBuffs);
6. 使用控制命令VIDIOC_QUERYBUF获得已经分配的V4L2的视频缓冲区的使用状态、在内核空间的偏移地址、缓冲区长度等相关信息。然后调用函数mmap把内核空间地址映射到用户空间,以便应用程序访问位于内核空间的视频缓冲区:
ioctl(iFd, VIDIOC_QUERYBUF, &tV4l2Buf);
mmap(0,tV4l2Buf.length, PROT_READ, MAP_SHARED, fd,tV4l2Buf.m.offset);
7. 数据流IO:
当开始数据流IO时,数据(摄像头获取的单帧画面数据)以v4l2_buffer的格式在应用和驱动之间传输。一个缓冲区可以有三种状态:
a.在驱动的传入队列中,驱动程序将会对此队列中的缓冲区进行处理,用户空间通过VIDIOC_QBUF把缓冲区放入到队列。对于一个视频捕获设备,传入队列中的缓冲区是空的,驱动会往其中填充数据;
b.在驱动的传出队列中,这些缓冲区已由驱动处理过,对于一个视频捕获设备,缓存区已经填充了视频数据,正等用户空间来认领;
c.用户空间状态的队列,已经通过VIDIOC_DQBUF传出到用户空间的缓冲区,此时缓冲区由用户空间拥有,驱动无法访问。
此时应将申请并且mmap的多个视频缓冲区v4l2_buffer依次放入队列,等待驱动填充数据:
ioctl(iFd, VIDIOC_QBUF, &tV4l2Buf)
8.至此就完成了摄像头的初始化
9.参考代码
#define NB_BUFFER 4 //视频缓冲区个数(自定义)
typedef struct videoDeviceParam
{
int iFd;
int iWidth;
int iHeight;
int iBpp;
int pixelformat;
int iVideoBufCnt;
int iVideoBufCurIndex;
int iVideoBufMaxLen;
unsigned char *pucVideBuf[NB_BUFFER];
}T_videoDeviceParam,*PT_videoDeviceParam;
int V4l2InitDevice(char * videoDevName , PT_videoDeviceParam pt_videoDeviceParam)
{
int i;
int iFd;
int iError;
struct v4l2_capability tV4l2Cap;
struct v4l2_fmtdesc tFmtDesc;
struct v4l2_format tV4l2Fmt;
struct v4l2_requestbuffers tV4l2ReqBuffs;
struct v4l2_buffer tV4l2Buf;
unsigned int iLcdWidth;
unsigned int iLcdHeigt;
unsigned int iLcdBpp;
iFd =open(videoDevName, O_RDWR); //打开设备文件
if(iFd < 0 )
printf("Video OPEN Failed.\n");
pt_videoDeviceParam->iFd = iFd;
memset(&tV4l2Cap, 0, sizeof(struct v4l2_capability));
iError = ioctl(iFd, VIDIOC_QUERYCAP, &tV4l2Cap);//查询设备支持的能力
if (iError)
{
printf("unable to query device.\n");
goto err_exit;
}
/* 获取当前视频设备支持的视频格式 */
memset(&tFmtDesc, 0, sizeof(tFmtDesc));
tFmtDesc.index = 0;
tFmtDesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
while ((iError = ioctl(iFd, VIDIOC_ENUM_FMT, &tFmtDesc)) == 0)
{
if (tFmtDesc.pixelformat == V4L2_PIX_FMT_YUYV)
{
pt_videoDeviceParam->pixelformat = tFmtDesc.pixelformat;
break;
}
tFmtDesc.index++;
}
if (!pt_videoDeviceParam->pixelformat)
{
printf("can not support the format of this device\n");
goto err_exit;
}
LcdGetResolution(&iLcdWidth, &iLcdHeigt, &iLcdBpp);
memset(&tV4l2Fmt, 0, sizeof(struct v4l2_format));
tV4l2Fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
tV4l2Fmt.fmt.pix.pixelformat = pt_videoDeviceParam->pixelformat;
tV4l2Fmt.fmt.pix.width = iLcdWidth;
tV4l2Fmt.fmt.pix.height = iLcdHeigt;
tV4l2Fmt.fmt.pix.field = V4L2_FIELD_ANY;
/* 如果驱动程序发现无法某些参数(比如分辨率),
* 它会调整这些参数, 并且返回给应用程序
*/
iError = ioctl(iFd, VIDIOC_S_FMT, &tV4l2Fmt);
if (iError)
{
printf("Unable to set format\n");
goto err_exit;
}
pt_videoDeviceParam->iWidth = tV4l2Fmt.fmt.pix.width;
pt_videoDeviceParam->iHeight = tV4l2Fmt.fmt.pix.height;
/* 申请视频缓冲区 */
memset(&tV4l2ReqBuffs, 0, sizeof(struct v4l2_requestbuffers));
tV4l2ReqBuffs.count = NB_BUFFER;
tV4l2ReqBuffs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
tV4l2ReqBuffs.memory = V4L2_MEMORY_MMAP;
iError = ioctl(iFd, VIDIOC_REQBUFS, &tV4l2ReqBuffs);
if (iError)
{
printf("Unable to allocate buffers.\n");
goto err_exit;
}
pt_videoDeviceParam->iVideoBufCnt = tV4l2ReqBuffs.count;
if (tV4l2Cap.capabilities & V4L2_CAP_STREAMING)
{
/* 映射至应用程序空间 */
for (i = 0; i < pt_videoDeviceParam->iVideoBufCnt; i++)
{
memset(&tV4l2Buf, 0, sizeof(struct v4l2_buffer));
tV4l2Buf.index = i;
tV4l2Buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
tV4l2Buf.memory = V4L2_MEMORY_MMAP;
iError = ioctl(iFd, VIDIOC_QUERYBUF, &tV4l2Buf);
if (iError)
{
printf("Unable to query buffer.\n");
goto err_exit;
}
pt_videoDeviceParam->iVideoBufMaxLen = tV4l2Buf.length;
printf("tV4l2Buf.length = %d\n",tV4l2Buf.length );
pt_videoDeviceParam->pucVideBuf[i] = mmap(0,
tV4l2Buf.length, PROT_READ, MAP_SHARED, iFd,
tV4l2Buf.m.offset);
if (pt_videoDeviceParam->pucVideBuf[i] == MAP_FAILED)
{
printf("Unable to map buffer\n");
goto err_exit;
}
}
/* 将分配空间放入队列*/
for (i = 0; i < pt_videoDeviceParam->iVideoBufCnt; i++)
{
memset(&tV4l2Buf, 0, sizeof(struct v4l2_buffer));
tV4l2Buf.index = i;
tV4l2Buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
tV4l2Buf.memory = V4L2_MEMORY_MMAP;
iError = ioctl(iFd, VIDIOC_QBUF, &tV4l2Buf);
if (iError)
{
printf("Unable to queue buffer.\n");
goto err_exit;
}
}
}
return 0;
err_exit:
printf("err_exit.\n");
close(iFd);
return -1;
}