背景

本次需要用摄像头采集图片,但是不想用opencv,因为需要重新编译。搜了下相关文章,挺多的。但是实际上却远没有我想的那么简单。绕了很大的弯。

问题

其实说起来用v4l2的api操纵摄像头捕获视频步骤并不复杂:打开摄像头描述符、(可先查询)、设置摄像头、(可检查以下设置的参数是否正确)、获取图像、处理图像。其中最重要的函数ioctl。
我的问题发生在获取图像上。之前看一篇csdn的文章说可以用read来获取图像,不用先将图像存在内核空间然后用mmap映射导出图像。
代码如下: [code] #include #include #include #include #include #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <sys/mman.h> #include #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;
}

[/code]

以上代码不工作
我的摄像头支持mjpeg和yuyv,但是检查了很多次。尽管有一些小问题,但是每次size = read(uvcfd, img_buff, IMG_BUFF_SIZE)获得的size是-1.这意味这不正确。
上述代码用的是read方法,因为我想自己的应用是按照要求在适当的时候拍一张照,不像别的用户需要用队列的方式来实现,所以没有用多数用户用的buffer的方式。
请问一下是不是我用错了?

解决方法

改成我之前不像用的复杂的方法就ok了。
这里不上代码了,别人比我写的好:
1、比较详细的
2、老外的
3、更多的