本文介绍在Linux 下怎么操作camera,camera在linux的设备文件一般都是/dev/videoX,如果只有一个摄像头一般都是/dev/video0。
需要包含v4l2的内核头文件:#include <linux/videodev2.h>
1.打开设备文件:
/*open video node*/
video2lcd->videofd = open(“/dev/video0”, O_RDWR);
if(video2lcd->videofd < 0){
printf("open %s fail!\r\n", VIDEODEV);
return -1;
}
2.查询v4l2设备的能力:
通过VIDIOC_QUERYCAP 获取V4L2设备的能力保存到 tV4l2Cap中,V4L2_CAP_VIDEO_CAPTURE 代表是视频设备,V4L2_CAP_STREAMING代表是通过流的方式获取帧输出,这个比使用read来读取数据相对更快,比较适合获取视频数据。
//struct v4l2_capability tV4l2Cap;
//quer video capabilities: VIDIOC_QUERYCAP
memset(&video2lcd->tV4l2Cap, 0, sizeof(struct v4l2_capability));
ret = ioctl(video2lcd->videofd, VIDIOC_QUERYCAP, &video2lcd->tV4l2Cap);
if (ret) {
printf("Error: unable to query device.\n");
close(video2lcd->videofd);
}
if (!(video2lcd->tV4l2Cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
{
printf("%s is not a video capture device\n", VIDEODEV);
close(video2lcd->videofd);
return -1;
}
if (video2lcd->tV4l2Cap.capabilities & V4L2_CAP_STREAMING)
printf("%s supports streaming i/o\n", VIDEODEV);
3.VIDIOC_ENUM_FMT获取该设备支持的格式
常见格式如下:
V4L2_PIX_FMT_UYVY,
V4L2_PIX_FMT_JPEG,
V4L2_PIX_FMT_RGB565,
/*get video fmt: VIDIOC_ENUM_FMT*/
memset(&video2lcd->tFmtDesc, 0, sizeof(video2lcd->tFmtDesc));
video2lcd->tFmtDesc.index = 0;
video2lcd->tFmtDesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
while ((ret = ioctl(video2lcd->videofd, VIDIOC_ENUM_FMT, &video2lcd->tFmtDesc)) == 0) {
if (isSupportThisFormat(video2lcd->tFmtDesc.pixelformat))
{
video2lcd->iPixelFormat = video2lcd->tFmtDesc.pixelformat;
break;
}
printf("tFmtDesc.index:%d iPixelFormat:%x\n", video2lcd->tFmtDesc.index, video2lcd->tFmtDesc.pixelformat);
video2lcd->tFmtDesc.index++;
}
4.VIDIOC_S_FMT 设置上一步中支持的一种格式,同时设置采样的窗口大小(x y)。
/*set video fmt: VIDIOC_S_FMT*/
memset(&video2lcd->tV4l2Fmt, 0, sizeof(struct v4l2_format));
video2lcd->tV4l2Fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
video2lcd->tV4l2Fmt.fmt.pix.pixelformat = video2lcd->iPixelFormat;
video2lcd->tV4l2Fmt.fmt.pix.width = x;
video2lcd->tV4l2Fmt.fmt.pix.height = y;
video2lcd->tV4l2Fmt.fmt.pix.field = V4L2_FIELD_ANY;
ret = ioctl(video2lcd->videofd , VIDIOC_S_FMT, &video2lcd->tV4l2Fmt);
if (ret)
printf("Unable to set format\n");
printf("set format success\n");
5.设置了视频格式以后需要申请内存,这里通过VIDIOC_REQBUFS申请了4个帧缓冲内存,然后VIDIOC_QUERYBUF将内核的帧缓冲内存拿到应用层并且保存mmap以后的地址。在通过VIDIOC_QBUF把这四个buff放到内核的队列中,等待使用。
/* request buffers */
memset(&video2lcd->tV4l2ReqBuffs, 0, sizeof(struct v4l2_requestbuffers));
video2lcd->tV4l2ReqBuffs.count = 4;
video2lcd->tV4l2ReqBuffs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
video2lcd->tV4l2ReqBuffs.memory = V4L2_MEMORY_MMAP;
ret = ioctl(video2lcd->videofd, VIDIOC_REQBUFS, &video2lcd->tV4l2ReqBuffs);
if (ret)
printf("Unable to allocate buffers.\n");
printf("request buffers success\n");
if (video2lcd->tV4l2Cap.capabilities & V4L2_CAP_STREAMING)
{
/* map the buffers */
for (i = 0; i < video2lcd->tV4l2ReqBuffs.count; i++)
{
memset(&video2lcd->tV4l2Buf, 0, sizeof(struct v4l2_buffer));
video2lcd->tV4l2Buf.index = i;
video2lcd->tV4l2Buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
video2lcd->tV4l2Buf.memory = V4L2_MEMORY_MMAP;
ret = ioctl(video2lcd->videofd, VIDIOC_QUERYBUF, &video2lcd->tV4l2Buf);
if (ret)
printf("Unable to query buffer.\n");
video2lcd->iVideoBufMaxLen = video2lcd->tV4l2Buf.length;
video2lcd->pucVideBuf[i] = mmap(0 /* start anywhere */ ,
video2lcd->tV4l2Buf.length, PROT_READ, MAP_SHARED, video2lcd->videofd,
video2lcd->tV4l2Buf.m.offset);
if (video2lcd->pucVideBuf[i] == MAP_FAILED)
printf("Unable to map buffer\n");
printf(" buffer[%d] length:%d, addr:%x \n", i, video2lcd->iVideoBufMaxLen, video2lcd->pucVideBuf[i]);
}
/* Queue the buffers. */
for (i = 0; i < video2lcd->tV4l2ReqBuffs.count; i++)
{
memset(&video2lcd->tV4l2Buf, 0, sizeof(struct v4l2_buffer));
video2lcd->tV4l2Buf.index = i;
video2lcd->tV4l2Buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
video2lcd->tV4l2Buf.memory = V4L2_MEMORY_MMAP;
ret = ioctl(video2lcd->videofd, VIDIOC_QBUF, &video2lcd->tV4l2Buf);
if (ret)
printf("Unable to queue buffer.\n");
}
}
6.一切准备就绪,下面只需要打开stream
while(1){
tFds[0].fd = video2lcd.videofd;
tFds[0].events = POLLIN;
ret = poll(tFds, 1, -1);
if (ret <= 0){
printf("poll error!\n");
return -1;
}
/* VIDIOC_DQBUF */
memset(&tV4l2Buf, 0, sizeof(struct v4l2_buffer));
tV4l2Buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
tV4l2Buf.memory = V4L2_MEMORY_MMAP;
ret = ioctl(video2lcd.videofd, VIDIOC_DQBUF, &tV4l2Buf);
if (ret < 0) {
printf("Unable to dequeue buffer.\n");
return -1;
}
video2lcd.iVideoBufCurIndex = tV4l2Buf.index;
printf("VIDIOC_DQBUF VideoBuf:%d\n",video2lcd.iVideoBufCurIndex);
/* VIDIOC_QBUF */
memset(&tV4l2Buf, 0, sizeof(struct v4l2_buffer));
tV4l2Buf.index = video2lcd.iVideoBufCurIndex;
tV4l2Buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
tV4l2Buf.memory = V4L2_MEMORY_MMAP;
ret = ioctl(video2lcd.videofd, VIDIOC_QBUF, &tV4l2Buf);
if (ret) {
printf("Unable to queue buffer.\n");
return -1;
}
printf("VIDIOC_QBUF Ok\n");
}