Linux V4L2摄像头拍照

    参考链接:https://blog.csdn.net/small_po_kid/article/details/119913184
    步骤:
    1.打开设备
    2.获取支持格式
    3.配置摄像头
    4.申请内核缓冲区队列
    5.内核缓冲区队列映射到用户空间
    6.采集帧数据

    点击查看代码
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/ioctl.h>
    #include <linux/videodev2.h>
    #include <string.h>
    #include <sys/mman.h>
    int main(void) 
    {
        int fd = open("/dev/video0",O_RDWR);
        if (fd < 0)
        {
            perror("打开设备失败");
            return -1;
        }
    
        //获取摄像头支持格式 ioctl(文件描述符,命令,与命令对应的结构体)
        struct v4l2_format vfmt;
    
        vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //摄像头采集
        vfmt.fmt.pix.width = 640; //设置摄像头采集参数,不可以任意设置
        vfmt.fmt.pix.height = 480;
        vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG; //设置为mjpg格式,则我可以直接写入文件保存,YUYV格式保存后需要转换格式才能查看
        
        int ret = ioctl(fd,VIDIOC_S_FMT,&vfmt);
        if (ret < 0)
        {
            perror("设置格式失败1");
        }
    
        //申请内核空间
        struct v4l2_requestbuffers reqbuffer;
        reqbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        reqbuffer.count = 4; //申请4个缓冲区
        reqbuffer.memory = V4L2_MEMORY_MMAP;  //映射方式
    
        ret = ioctl(fd,VIDIOC_REQBUFS,&reqbuffer);
        if (ret < 0)
        {
            perror("申请空间失败");
        }
       
        //映射
        unsigned char *mptr[4];//保存映射后用户空间的首地址
        unsigned int size[4];
        struct v4l2_buffer mapbuffer;
        //初始化type和index
        mapbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    
        for(int i = 0; i <4;i++) {
            mapbuffer.index = i;
            ret = ioctl(fd,VIDIOC_QUERYBUF,&mapbuffer); //从内核空间中查询一个空间作映射
            if (ret < 0)
            {
                perror("查询内核空间失败");
            }
            //映射到用户空间
            mptr[i] = (unsigned char *)mmap(NULL,mapbuffer.length,PROT_READ|PROT_WRITE,MAP_SHARED,fd,mapbuffer.m.offset);
            size[i] = mapbuffer.length; //保存映射长度用于后期释放
            //查询后通知内核已经放回
            ret = ioctl(fd,VIDIOC_QBUF,&mapbuffer); 
            if (ret < 0)
            {
                perror("放回失败");
            }
        }
        //开始采集
        int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        ret = ioctl(fd,VIDIOC_STREAMON,&type); 
        if (ret < 0)
            {
                perror("开启失败");
            }
        //从队列中提取一帧数据
        struct v4l2_buffer readbuffer;
        readbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //每个结构体都需要设置type为这个参赛要记住
        ret = ioctl(fd,VIDIOC_DQBUF,&readbuffer); 
        if (ret < 0)
            {
                perror("读取数据失败");
            }
        
        FILE *file=fopen("my.jpg", "w+");  //打开一个文件
        fwrite( mptr[readbuffer.index],readbuffer.length,1,file);//写入文件
        fclose(file);    //写入完成,关闭文件
    
        //通知内核使用完毕
       ret = ioctl(fd, VIDIOC_QBUF, &readbuffer);
       if(ret < 0)
    	{
    		perror("放回队列失败");
    	}
    	
        //停止采集
        ret = ioctl(fd,VIDIOC_STREAMOFF,&type);
        
    	//释放映射
        for(int i=0; i<4; i)munmap(mptr[i], size[i]);
    
        close(fd); //关闭文件
        
        return 0;
    }
    
    
    posted @   相对维度  阅读(435)  评论(0编辑  收藏  举报
    相关博文:
    阅读排行:
    · 震惊!C++程序真的从main开始吗?99%的程序员都答错了
    · 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
    · 单元测试从入门到精通
    · 上周热点回顾(3.3-3.9)
    · Vue3状态管理终极指南:Pinia保姆级教程
    点击右上角即可分享
    微信分享提示