背景
本次需要用摄像头采集图片,但是不想用opencv,因为需要重新编译。搜了下相关文章,挺多的。但是实际上却远没有我想的那么简单。绕了很大的弯。
问题
其实说起来用v4l2的api操纵摄像头捕获视频步骤并不复杂:打开摄像头描述符、(可先查询)、设置摄像头、(可检查以下设置的参数是否正确)、获取图像、处理图像。其中最重要的函数ioctl。
我的问题发生在获取图像上。之前看一篇csdn的文章说可以用read来获取图像,不用先将图像存在内核空间然后用mmap映射导出图像。
代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <linux/videodev2.h>
#include <linux/v4l2-common.h>
const char uvc_name[] = "/dev/video1";
const int IMG_HEIGHT = 800;
const int IMG_WIDTH = 600;
const int IMG_PIX_SIZE = 3;
const int IMG_BUFF_SIZE = IMG_HEIGHT * IMG_WIDTH * IMG_PIX_SIZE;
uint8_t img_buff[IMG_BUFF_SIZE];
int main(void)
{
/*open camera*/
int uvcfd = open(uvc_name, O_RDWR, 0);
if (uvcfd < 0)
{
perror("Open Camera: ");
return -1;
}
/*ask the information of uvc*/
struct v4l2_capability cap;
if (ioctl(uvcfd, VIDIOC_QUERYCAP, &cap) < 0)
{
perror("ioctl: ");
}
printf("Driver Caps:\n"
" Driver: \"%s\"\n"
" Card: \"%s\"\n"
" Bus: \"%s\"\n"
" Version: %d\n"
" Capabilities: %x\n",
cap.driver,
cap.card,
cap.bus_info,
cap.version,
cap.capabilities);
if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == V4L2_CAP_VIDEO_CAPTURE)
{
printf("Camera device %s: support capture\n", uvc_name);
}
if ((cap.capabilities & V4L2_CAP_STREAMING) == V4L2_CAP_STREAMING)
{
printf("Camera device %s: support streaming.\n", uvc_name);
}
/*get supported video format*/
struct v4l2_fmtdesc fmtdesc;
fmtdesc.index = 0;
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
printf("Support format: \r\n");
while (ioctl(uvcfd, VIDIOC_ENUM_FMT, &fmtdesc) != -1)
{
printf("\t%d. %s\r\n", fmtdesc.index + 1, fmtdesc.description);
fmtdesc.index++;
}
/*setup camera*/
struct v4l2_format fmt;
memset(&fmt, 0, sizeof(fmt));
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = IMG_WIDTH;
fmt.fmt.pix.height = IMG_HEIGHT;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
//fmt.fmt.pix.field = V4L2_FIELD_ANY;
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
if (ioctl(uvcfd, VIDIOC_S_FMT, &fmt) < 0)
{
perror("file to set camera\r\n");
close(uvcfd);
return -1;
}
if (ioctl(uvcfd, VIDIOC_G_FMT, &fmt) < 0)
{
printf("Unable to get format\r\n");
return -1;
}
printf("fmt.type:\t%d\n", fmt.type);
printf("pix.pixelformat:\t%c%c%c%c\n", fmt.fmt.pix.pixelformat & 0xFF,
(fmt.fmt.pix.pixelformat >> 8) & 0xFF,
(fmt.fmt.pix.pixelformat >> 16) & 0xFF,
(fmt.fmt.pix.pixelformat >> 24) & 0xFF);
printf("pix.width:\t%d\n", fmt.fmt.pix.width);
printf("pix.height:\t%d\n", fmt.fmt.pix.height);
printf("pix.field:\t%d\n", fmt.fmt.pix.field);
int size = read(uvcfd, img_buff, IMG_BUFF_SIZE);
/*save image*/
if (size > 0)
{
int filefd = open("./img.jpg", O_RDWR | O_CREAT, 0777);
write(filefd, img_buff, size);
close(filefd);
}
else
{
perror("Read Camera:");
printf("The image size is %d\r\n", size);
printf("Can not capture image\r\n");
}
close(uvcfd);
return 0;
}
以上代码不工作
我的摄像头支持mjpeg和yuyv,但是检查了很多次。尽管有一些小问题,但是每次size = read(uvcfd, img_buff, IMG_BUFF_SIZE)获得的size是-1.这意味这不正确。
上述代码用的是read方法,因为我想自己的应用是按照要求在适当的时候拍一张照,不像别的用户需要用队列的方式来实现,所以没有用多数用户用的buffer的方式。
请问一下是不是我用错了?