v4l2抓取一帧图像的程序

来源:互联网 发布:网络出版与电子出版 编辑:程序博客网 时间:2024/05/17 16:11
v4l2抓取一帧图像的程序 

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/time.h>
#include <errno.h>
#include <sys/mman.h>
#include <sys/select.h>


#include <linux/videodev2.h>

static char *dev_name = "/dev/video0";

struct buffer {
        void *start;
        size_t length;
};

struct buffer *buffers = NULL;
static unsigned int n_buffers = 0;

int main(int argc, char **argv)
{
        int fd;
        int ret;

        //设备的打开
        fd = open(dev_name, O_RDWR | O_NONBLOCK);
        if (fd < 0) {
                fprintf(stderr, "can not open /dev/video1\n");
                return -1;
        }
        printf("open %s success\n", dev_name);

        // 查询设备属性: VIDIOC_QUERYCAP
        struct v4l2_capability cap;
        memset(&cap, 0, sizeof(cap));
        ret = ioctl(fd, VIDIOC_QUERYCAP, &cap);
        if (ret < 0) {
                fprintf(stderr, "ioctl querycap error\n");
                return -1;
        } else {
                printf("Capability Informations:\n");
                printf("\t%-15s = %s\n", "cap.driver", cap.driver);
                printf("\t%-15s = %s\n", "cap.card", cap.card);
                printf("\t%-15s = %s\n", "cap.bus_info", cap.bus_info);
                printf("\t%-15s = 0x%x\n", "cap.version", cap.version);
        }

        /*===============select input device==================*/
        //struct v4l2_input input;
        //      memset(&input, 0, sizeof(input));
        //      while (ioctl(fd, VIDIOC_ENUMINPUT, &input) >= 0) {
        //              printf("\ncmd VIDIOC_ENUMINPUT:\n");
        //              printf(" %-15s = %u\n", "input.index", input.index);
        //              printf(" %-15s = %s\n", "input.name", input.name);
        //              printf(" %-15s = %u\n", "input.type", input.type);
        //              printf(" %-15s = %u\n", "input.audioset", input.audioset);
        //              printf(" %-15s = %u\n", "input.tuner", input.tuner);
        //              //printf("%-15s = %lu\n", "input.std", input.std);
        //              printf(" input.index = %d\n", input.index);
        //              input.index++;
        //      }

        //查询并显示所有支持的格式:VIDIOC_ENUM_FMT
        struct v4l2_fmtdesc fmtdesc;
        memset(&fmtdesc, 0, sizeof(fmtdesc));
        fmtdesc.index = 0;
        fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        printf("Support format:\n");

        while (ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) != -1) {
                printf("\t%d.%s\n", fmtdesc.index + 1, fmtdesc.description) ;
                fmtdesc.index++;
        }

        //查看或设置当前格式: VIDIOC_G_FMT, VIDIOC_S_FMT
        printf("Set Format\n");
        struct v4l2_format fmt;
        memset(&fmt, 0, sizeof(fmt));
        fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        fmt.fmt.pix.width = 640;
        fmt.fmt.pix.height = 480;
        fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
        //fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
        fmt.fmt.pix.field       = V4L2_FIELD_INTERLACED;
        ret = ioctl(fd, VIDIOC_S_FMT, &fmt);
        if (ret < 0) {
                fprintf(stderr, "ioctl VIDIOC_S_FMT error\n") ;
                return -1;
        }

        ret = ioctl(fd, VIDIOC_G_FMT, &fmt);
        if (ret < 0) {
                fprintf(stderr, "get format failed\n") ;
        }

        printf("Stream Format Informations:\n");
        printf("\t%-15s= %d\n", "type", fmt.type);
        printf("\t%-15s= %d\n", "width", fmt.fmt.pix.width);
        printf("\t%-15s= %d\n", "height", fmt.fmt.pix.height);
        char fmtstr[8];
        memset(fmtstr, 0, 8);
        memcpy(fmtstr, &fmt.fmt.pix.pixelformat, 4);
        printf("\t%-15s= %s\n", "pixelformat", fmtstr);
        printf("\t%-15s= %d\n", "field", fmt.fmt.pix.field);
        printf("\t%-15s= %d\n", "bytesperline", fmt.fmt.pix.bytesperline);
        printf("\t%-15s= %d\n", "sizeimage", fmt.fmt.pix.sizeimage);
        printf("\t%-15s= %d\n", "colorspace", fmt.fmt.pix.colorspace);
        printf("\t%-15s= %d\n", "priv", fmt.fmt.pix.priv);
        //printf(" raw_date: %s\n", fmt.fmt.raw_data);

        //申请和管理缓冲区
        //向设备申请缓冲区 VIDIOC_REQBUFS
        struct v4l2_requestbuffers req;
        memset(&req, 0, sizeof(req));
        req.count = 4;
        req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        req.memory = V4L2_MEMORY_MMAP;
        ret = ioctl(fd, VIDIOC_REQBUFS, &req);        //申请一个拥有四个缓冲帧的缓冲区
        if (ret < 0) {
                fprintf(stderr, "ioctl VIDIOC_REQBUFS error\n");
                return -1;
        }

        //将四个已申请到的缓冲帧映射到应用程序,用buffers 指针记录
        buffers = calloc(req.count, sizeof(*buffers));        //内存中建立对应空间
        if (!buffers) {
                fprintf(stderr, "Out of memory\n");
                return 0;
        }

        struct v4l2_buffer buf;
        for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {
                memset(&buf, 0, sizeof(buf));
                buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
                buf.memory = V4L2_MEMORY_MMAP;
                buf.index = n_buffers;
                //查询序号为n_buffers 的缓冲区,得到其起始物理地址和大小
                if (ioctl(fd, VIDIOC_QUERYBUF, &buf) < 0) {
                        printf("ioctl VIDIOC_QUERYBUF failed\n");
                        return -1;
                }
                printf("buf.length = %d\n", buf.length);
                buffers[n_buffers].length = buf.length;

                //映射内存
                buffers[n_buffers].start =
                    mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED,
                         fd, buf.m.offset);
                if (MAP_FAILED == buffers[n_buffers].start)
                        printf("map buffers failed\n");

//                //把四个缓冲帧放入队列
//                if (ioctl(fd, VIDIOC_QBUF, &buf) < 0)
//                        printf("ioctl VIDIOC_QBUF failed\n");
        }


        for (n_buffers = 0; n_buffers < req.count; n_buffers++) {
                memset(&buf, 0, sizeof(buf));
                buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
                buf.memory = V4L2_MEMORY_MMAP;
                buf.index = n_buffers;
                if (ioctl(fd, VIDIOC_QBUF, &buf) < 0)
                        printf("ioctl VIDIOC_QBUF failed\n");
        }

        //缓冲区处理好之后,就可以开始获取数据了
        //启动 或 停止数据流 VIDIOC_STREAMON, VIDIOC_STREAMOFF
        enum v4l2_buf_type type;
        type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        ret = ioctl(fd, VIDIOC_STREAMON, &type);
        if (ret < 0)
                printf("ioctl VIDIOC_STREAMON failed\n");

        fd_set fds;
        struct timeval tv;
        FD_ZERO(&fds);
        FD_SET(fd, &fds);
        tv.tv_sec = 2;
        tv.tv_usec = 0;
        ret = select(fd + 1, &fds, NULL, NULL, &tv);
        if (-1 == ret) {
                if (EINTR == errno)
                printf("fail to select\n");
        }
        if (0 == ret) {
                fprintf(stderr, "select Timeout\n");
        }

        memset(&buf, 0, sizeof(buf));
        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory = V4L2_MEMORY_MMAP;
        //取出一帧
        ret = ioctl(fd, VIDIOC_DQBUF, &buf);
        if (ret < 0)
                printf("ioctl VIDIOC_DQBUF\n");

        //处理一帧
        FILE *file_fd;
        file_fd = fopen("frame.jpg", "w");
        if (file_fd < 0)
                printf("create file failed\n");

        fwrite(buffers[buf.index].start, 1, buffers[buf.index].length, file_fd);
        printf("buffers[buf.index].length = %d\n", buffers[buf.index].length);
        usleep(500);
        fclose(file_fd);

        if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) {
                return -1;
        }

        type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        ret = ioctl(fd, VIDIOC_STREAMOFF, &type);
        if (ret < 0)
                printf("ioctl VIDIOC_STREAMOFF failed\n");

        for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {
                munmap(buffers[n_buffers].start, buffers[n_buffers].length);
        }
        free(buffers);

        close(fd);
        return 0;
}
0 0