643|2

53

帖子

0

TA的资源

一粒金砂(中级)

楼主
 

【玄铁杯第三届RISC-V应用创新大赛】LicheePi 4A 4、使用v4l2调用usb摄像头拍照 c语言 [复制链接]

 

我买的官方的MIPI CSI摄像头,但现在没有提供支持系统下载了,还好有备选usb摄像头,只不过是100万像素的usb摄像头,拍的照片一点也不清楚,但用于学习还是够了的。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>//O_RDWR宏
#include <unistd.h>//open close函数
#include <linux/videodev2.h>//v4l2头文件
#include <sys/mman.h>//mmap函数
#include <sys/ioctl.h>//ioctl函数

#define		video_width		640
#define 	video_height	480

char *userbuff[4];        /* 存放摄像头设备内核缓冲区映射后的用户内存地址 */
int userbuff_length[4];   /* 保存映射后的数据长度,释放缓存时要用 */

int video_fd;

//打开 & 设置相机
int v4l2_open()
{
    int ret;

    /* 1.打开摄像头设备 /dev/video0 */
    video_fd = open("/dev/video0", O_RDWR);
    if(video_fd < 0){
        perror("打开摄像头设备失败");
        return -1;
    }
    /* 3.获取摄像头的能力 (VIDIOC_QUERYCAP:是否支持视频采集、内存映射等) */
    struct v4l2_capability capability;
    if(0 == ioctl(video_fd, VIDIOC_QUERYCAP, &capability)){
        if((capability.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0){
            perror("该摄像头设备不支持视频采集!");
            close(video_fd);
            return -2;
        }
        if((capability.capabilities & V4L2_MEMORY_MMAP) == 0){
            perror("该摄像头设备不支持mmap内存映射!");
            close(video_fd);
            return -3;
        }
    }

    /* 4.枚举摄像头支持的格式           (VIDIOC_ENUM_FMT:MJPG、YUYV等)
      列举出每种格式下支持的分辨率      (VIDIOC_ENUM_FRAMESIZES) */
    struct v4l2_fmtdesc fmtdesc;
    memset(&fmtdesc, 0, sizeof(fmtdesc));
    fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  //设置视频采集设备类型
    int i = 0;
    while(1){
        fmtdesc.index = i++;
        // 获取支持格式
        if(0 == ioctl(video_fd, VIDIOC_ENUM_FMT, &fmtdesc)){
            printf("支持格式:%s, %c%c%c%c\n", fmtdesc.description,
                                            fmtdesc.pixelformat & 0xff,
                                            fmtdesc.pixelformat >> 8 & 0xff,
                                            fmtdesc.pixelformat >> 16 & 0xff,
                                            fmtdesc.pixelformat >> 24 & 0xff);
            // 列出该格式下支持的分辨率             VIDIOC_ENUM_FRAMESIZES & 默认帧率 VIDIOC_G_PARM
            // 1.默认帧率
            struct v4l2_streamparm streamparm;
            streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
            if(0 == ioctl(video_fd, VIDIOC_G_PARM, &streamparm))
                printf("该格式默认帧率 %d fps\n", streamparm.parm.capture.timeperframe.denominator);
            // 2.循环列出支持的分辨率
            struct v4l2_frmsizeenum frmsizeenum;
            frmsizeenum.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
            frmsizeenum.pixel_format = fmtdesc.pixelformat;   //设置成对应的格式
            int j = 0;
            printf("支持的分辨率有:\n");
            while(1){
                frmsizeenum.index = j++;
                if(0 == ioctl(video_fd, VIDIOC_ENUM_FRAMESIZES, &frmsizeenum))
                    printf("%d x %d\n", frmsizeenum.discrete.width, frmsizeenum.discrete.height);
                else break;
            }
            printf("\n");
        }else break;
    }

    /* 5.设置摄像头类型为捕获、设置分辨率、视频采集格式 (VIDIOC_S_FMT) */
    struct v4l2_format format;
    format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;   /* 视频采集 */
    format.fmt.pix.width = video_width;          /* 宽 */
    format.fmt.pix.height = video_height;    	 /* 高 */
    format.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;   /* 设置输出类型:MJPG */
    if(0 > ioctl(video_fd, VIDIOC_S_FMT, &format)){
        perror("设置摄像头参数失败!");
        close(video_fd);
        return -4;
    }

    /* 6.向内核申请内存 (VIDIOC_REQBUFS:个数、映射方式为mmap)
         将申请到的缓存加入内核队列 (VIDIOC_QBUF)
         将内核内存映射到用户空间 (mmap) */
    struct v4l2_requestbuffers requestbuffers;
    requestbuffers.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    requestbuffers.count = 4;    //申请缓存个数
    requestbuffers.memory = V4L2_MEMORY_MMAP;     //申请为物理连续的内存空间
    if(0 == ioctl(video_fd, VIDIOC_REQBUFS, &requestbuffers)){
        /* 申请到内存后 */
        for(int i = 0; i < requestbuffers.count; i++){
            /* 将申请到的缓存加入内核队列 (VIDIOC_QBUF)              */
            struct v4l2_buffer buffer;
            buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
            buffer.index = i;
            buffer.memory = V4L2_MEMORY_MMAP;
            ret = ioctl(video_fd, VIDIOC_QBUF, &buffer);
            if(ret < 0){
                perror("查询内核空间失败");
                close(video_fd);
                return -6;
            }
            else if(0 == ret){
                /* 加入内核队列成功后,将内存映射到用户空间 (mmap) */
                userbuff[i] = (char *)mmap(NULL, buffer.length, PROT_READ | PROT_WRITE, MAP_SHARED, video_fd, buffer.m.offset);
                userbuff_length[i] = buffer.length;//保存映射长度用于后期释放
            }
        }
    }
    else{
        perror("申请内存失败!");
        close(video_fd);
        return -5;
    }

    int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if(0 > ioctl(video_fd, VIDIOC_STREAMON, &type)){
        perror("打开视频流失败!");
        return -7;
    }
    return 0;
}

/* 拍照 */
void v4l2_take()
{
    char str[] = "./photo.jpg";
    int ret;

    /* 采集图片数据 */
    //定义结构体变量,用于获取内核队列数据
    struct v4l2_buffer buffer;
    buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    /* 从内核中捕获好的输出队列中取出一个 */
    ret = ioctl(video_fd, VIDIOC_DQBUF, &buffer);
    if(ret < 0){
        perror("捕获图片失败!");
    }
    else if(0 == ret){
        //保存到本地
        int fd = open(str, O_RDWR | O_CREAT, 0777);   //打开并创建一个新文件
        write(fd, userbuff[buffer.index], buffer.bytesused);
        printf("文件地址:%s\n", str);
        close(fd);
    }
    /* 将使用后的缓冲区放回到内核的输入队列中 (VIDIOC_QBUF) */
    ret = ioctl(video_fd, VIDIOC_QBUF, &buffer);
    if(ret < 0){
        perror("返回队列失败!");
    }
}

//关闭
int v4l2_close()
{
    /* 8.停止采集,关闭视频流 (VIDIOC_STREAMOFF)
      关闭摄像头设备 & 关闭LCD设备 */
    int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if(0 == ioctl(video_fd, VIDIOC_STREAMOFF, &type)){
        /* 9.释放映射 */
        for(int i = 0; i < 4; i++)
            munmap(userbuff[i], userbuff_length[i]);
        close(video_fd);
        printf("关闭相机成功!\n");
        return 0;
    }
    return -1;
}

int main(int argc, char* argv[]){
    v4l2_open();
    v4l2_take();
    v4l2_close();
    return 0;
}

 

执行结果:

 

 

打开photo.jpg

 

最新回复

这个代码写的很详细,有很大的参考价值,有很大的启发作用   详情 回复 发表于 2023-11-7 02:26
点赞 关注
 
 

回复
举报

731

帖子

4

TA的资源

纯净的硅(高级)

沙发
 

这个代码写的很详细,有很大的参考价值,有很大的启发作用

点评

这就是开源linux的好处,代码都是相互抄。。。  详情 回复 发表于 2023-11-7 08:42
 
 
 

回复

53

帖子

0

TA的资源

一粒金砂(中级)

板凳
 
chejm 发表于 2023-11-7 02:26 这个代码写的很详细,有很大的参考价值,有很大的启发作用

这就是开源linux的好处,代码都是相互抄。。。

 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表