v4l2在帧缓冲区预览摄像头

2023-11-19

在应用层通过v4l2 api将采集的摄像头数据yuv转为rgb后写到帧缓冲区达到预览摄像头的目的,程序运行后切换到tty下就可以看到。我的屏幕是bgra格式的,这点要注意,不同屏幕格式不同,不同r,g,b的偏移通过修改(0x00<<24) | (R0<<16) | (G0<<8) | (B0<<0);中的顺序即可

#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <asm/types.h>
#include <linux/videodev2.h>
#include <sys/mman.h>
#include <string.h>
#include <malloc.h>
#include <linux/fb.h>
/**
 ** v4l2在帧缓存区预览摄像头: gcc -std=c99 -o main main.c
 **/
static int width;//屏幕宽度
static int height;//屏幕高度
static __u32 *pfb;//屏幕缓冲区指针
static int fb;
static struct fb_fix_screeninfo finfo;
static struct fb_var_screeninfo vinfo;
void fb_fillimg(const __u32* img);
void initFb()
{
    int ret = -1;
    //打开帧缓冲区设备
    fb = open("/dev/fb0", O_RDWR);
    if (fb < 0) {  perror("open fb0"); return;  }
    printf("open /dev/fb0 success \n");
    //得到固定屏幕信息
    ret = ioctl(fb,FBIOGET_FSCREENINFO,&finfo);
    //得到可变屏幕信息
    ret = ioctl(fb, FBIOGET_VSCREENINFO, &vinfo);
    if (ret < 0) { perror("get var info"); return; }
    width = vinfo.xres_virtual;
    height = vinfo.yres_virtual;
    printf("width=%d,height=%d\n",width,height);
    //映射帧缓冲区基址
    pfb = (__u32*)mmap(NULL, finfo.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fb, 0);
    if (NULL == pfb) { perror("fb0 mmap"); return; }
}
void closeFb()
{
    munmap(pfb,finfo.smem_len);
    close(fb);
}
/**
 ** yuv422转bgra
 **/
int max(int a,int b){ return a<b? b:a; }
int min(int a,int b){ return a<b? a:b; }
__u32 yuv2rgb(int Y0,int U0,int Y1,int V1,int index)
{
    int Y = index? Y1:Y0;
    int R0 = max(0,min(255,1.164*(Y-16) + 1.596*(V1-128)));
    int G0 = max(0,min(255,1.164*(Y-16) - 0.391*(U0-128) - 0.813*(V1-128)));
    int B0 = max(0,min(255,1.164*(Y-16) + 2.018*(U0-128)));
    return (0x00<<24) | (R0<<16) | (G0<<8) | (B0<<0);//转为bgra
}
int main()
{
    int fd;
    struct v4l2_format fmt;
    struct v4l2_requestbuffers req;
    struct v4l2_buffer buf;
    __u8* bufStart;//图形缓冲区基址
    enum v4l2_buf_type type;
    //打开摄像头设备
    fd = open("/dev/video0", O_RDWR);
    if (fd<0) { perror("open video0"); return -1;}
    /**
     ** 设置摄像头格式,我的UVC摄像头有两个格式:YUV422(YUYV)和MJPEG
     ** 我的摄像头宽高是640x480
     **/
	memset( &fmt, 0, sizeof(fmt));
	fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;//
	fmt.fmt.pix.width = 640;
	fmt.fmt.pix.height = 480;
	if (ioctl(fd, VIDIOC_S_FMT, &fmt) < 0) { perror("set format failed\n");return -1; }
    //得到实际的格式
	if (ioctl(fd, VIDIOC_G_FMT, &fmt) < 0) { perror("get format failed\n");return -1; }
	printf("Picture:Width = %d   Height = %d\n", fmt.fmt.pix.width, fmt.fmt.pix.height);
    //申请缓冲区
	memset(&req, 0, sizeof (req));
	req.count = 1; //缓冲区数量为1
	req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	req.memory = V4L2_MEMORY_MMAP;//mmap方式
	if (ioctl(fd, VIDIOC_REQBUFS, &req) == -1) { perror("request buffer error \n"); return 0-1; }
    //缓冲区类型设置
    memset( &buf, 0, sizeof(buf));
	buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	buf.memory = V4L2_MEMORY_MMAP;
	buf.index = 0;
	if (ioctl(fd, VIDIOC_QUERYBUF, &buf) < 0) { perror("VIDIOC_QUERYBUF error\n"); return -1;}

    printf("buffer len=%u\n",buf.length);//长度614400=640x480x2
    /** 映射得到缓冲区的地址 **/
    bufStart = mmap(NULL, buf.length, PROT_READ | PROT_WRITE,MAP_SHARED, fd, buf.m.offset);
	if (bufStart == MAP_FAILED)
	{
		perror("buffers mmap\n");
		return -1;
	}
    /** 将缓冲区放入到循环队列**/
	if (ioctl(fd, VIDIOC_QBUF, &buf) < 0) { perror("VIDIOC_QBUF error\n"); return -1; }
	/** 打开流:stream on,打开流后摄像头驱动就开始往缓冲区缓冲区队列中的缓冲区填入数据了 **/
	type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	if (ioctl(fd, VIDIOC_STREAMON, &type) < 0) { perror("VIDIOC_STREAMON error\n"); return -1;}

    int i=0;
    //定义转换后的bgra区域
        #define OUT_SIZE 640*480*4
        __u32 *out = (__u32*)malloc(OUT_SIZE);//bgra
        __u32 *saveout;
        if(out==NULL)  { perror("out"); return -1;}
        __u8 *saveBufStart;
        initFb();//初始化帧缓冲区
    while(i++<500){  //循环读取500帧
        //从循环队列中取出缓冲区
        if (ioctl(fd, VIDIOC_DQBUF, &buf) < 0) { perror("VIDIOC_DQBUF failed.\n"); return -1; }

         //将yuv422转为bgra
            saveBufStart = bufStart;
            saveout=out;
            for(int t = 0;t<OUT_SIZE/2;t+=4){
                *out++ = yuv2rgb(bufStart[0],bufStart[1],bufStart[2],bufStart[3],0);
                *out++ = yuv2rgb(bufStart[0],bufStart[1],bufStart[2],bufStart[3],1);
                bufStart += 4;
            }
            out = saveout;
            bufStart = saveBufStart;
         //将bgra数据显示到帧缓冲区
            fb_fillimg(out);

        /** 将缓冲区重新放入循环队列**/
        if (ioctl(fd, VIDIOC_QBUF, &buf) < 0) { perror("VIDIOC_QBUF error\n"); return -1;}
    }

    closeFb();
    munmap(bufStart,buf.length);
	close(fd);
    return 0;
}
/**
 ** 在帧缓冲区居中显示图片
 **/
#define imgW 640
#define imgH 480
void fb_fillimg(const __u32* img)
{
    unsigned int x, y,cX,cY;
    cX = (width-imgW)/2;
    cY = (height-imgH)/2;
    for (y = cY; y < cY+imgH; y++)
    {
        for (x = cX; x < cX+imgW; x++)
        {
            *(pfb + y * width + x) = *(img + (y-cY)*imgW+(x-cX));
        }
    }

}

预览效果:



本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

v4l2在帧缓冲区预览摄像头 的相关文章

随机推荐

  • 使用adb工具打开TCL电视的第三方应用安装权限

    使用adb工具打开TCL电视的第三方应用安装权限 前言 安装adb工具 打开电视的adb调试开关 abd工具打开电视权限 前言 新买的TCL电视往往默认是无法安装第三方应用的 即使用U盘安装了第三方应用 应用也没有升级权限 另外 也无法通过
  • Android 经验分享

    搞Android已经两年了 之前一直在eoe上面写文章 竟然没有写一篇CSDN的文章 真的很惭愧 从今天希望自己可以坚持下去 把每天的收获都可以保存起来 同时也分享给大家 希望大家有用 不说废话了 我先写几条我自己工作中的一些经验吧 1 推
  • Python 一年中的第几天

    给你一个字符串 date 按 YYYY MM DD 格式表示一个 现行公元纪年法 日期 返回该日期是当年的第几天 LeetCode 1154 一年中的第几天 class Solution def dayOfYear self date st
  • oracle突然变慢 awr,案例:Oracle awr 数据严重不一致 awr部分表损坏等情况 需要重建awr...

    天萃荷净 Oracle数据库服务器突然断电 导致AWR部分表出现问题 记录重建awr的步骤过程 由于某种原因 比如数据异常断电 导致awr数据严重不一致 awr部分表损坏等情况 需要重建awr 可以参考如下步骤进行重建 本文主要针对目前主流
  • RabbitMQ(四):RabbitMQ高级特性

    消息队列在使用过程中 面临着很多实际问题需要思考 消息可靠性问题 如何确保发送的消息至少被消费 次 延迟消息问题 如何实现消息的延迟投递 消息堆积问题 如何解决数百万消息堆积 无法及时消费的问题 高可用问题 如何避免单点的MQ故障而导致的不
  • MATLAB ARMA时间序列分析引导——理解与应用

    MATLAB ARMA时间序列分析引导 理解与应用 引言 时间序列分析是一种重要的数据分析方法 广泛应用于金融 经济 自然科学等领域 ARMA模型是时间序列分析中常用的模型之一 它可以帮助我们预测未来的数值趋势和特征 以便做出相应的决策 本
  • 百度Apollo7.0轨迹规划模块

    百度Apollo 7 0 轨迹规划模块是一种用于自动驾驶汽车的软件工具 其中包含了一组算法和工具 用于在道路上规划车辆的路线和轨迹 这个模块能够考虑车辆的动态特性 如转弯半径 最大转弯角度和最大加速度等 并能够在实时环境中规划车辆的路线 此
  • 记一次关于uni的公共样式使用遇到的坑

    今天在使用uniapp开发小程序时遇到一个问题 在app vue中引入公共样式 在其他界面中使用 直接运行到小程序模拟器上时 是可以正常使用的 但是如果分包放到小程序上公共样式就会失效 在网上找了半天也没找到问题所在 后边瞎写的的时候偶然解
  • OpenStack rdo一键allinone部署

    目录 1 环境准备 2 配置阿里yum源 3 安装openstack 4 安装packstack软件包 5 执行一键部署命令 6 遇到一些问题 7 登录OpenStack 1 环境准备 CentOS7 最小化安装 设置静态IP 编辑 vi
  • 关于求职及面试的一些小技巧

    关于面试的一些小窍门 内容仅代表我个人观点 欢迎批评指正 之前已经分享过怎么样做一份看起来还算不错的简历了 老司机的分享 写简历的过程中 都有哪些坑 点开即可查看 1 关于面试时机 对相当一部分的部门需求者而言 如果求职者不是绝对的让部门需
  • 常用的十种算法--马踏棋盘算法

    1 马踏棋盘算法介绍 马踏棋盘算法也被称为骑士周游问题 将马随机放在国际象棋的 8 8 棋盘 Board 0 7 0 7 的某个方格中 马按走棋规则 马走日字 进行移动 要求每个方格只进入一次 走遍棋盘上全部 64 个方格 2 马踏棋盘算法
  • “大三在读生”都四面成功拿到字节跳动Offer了,你还有什么理由去摸鱼?

    博主大三在读 投的是字节 Data 的后端开发实习生 base 杭州 时间线 4 12 投递 4 13 安排简历筛选 4 14 安排面试 4 19 16 00 一面 4 22 16 00 二面 4 23 8 00 三面 4 23 16 00
  • 在WINDOW 系统下如何用批处理命令生成代码

    如图要实现一个每次编译都会自动重新生成的代码 一般是 软件版本相关的代码最适合这种自动生成 上图中需在用到编译时间和日期 来直接上代码 BEGIN COLOR 07 cls echo off ECHO ECHO 自动生成软件版本号 ECHO
  • Discuz 如何设置SSL类型的SMTP邮箱

    由于国际与国内均对垃圾邮件进行严格管控 我国 互联网信息服务管理办法 中国互联网协会反垃圾邮件规范 均对垃圾邮件进行说明与管理规范 鉴于服务器25端口被大量垃圾邮件充斥 严重影响广大用户正常使用 很多服务器不再提供25端口邮件服务 包括进站
  • QNX系统的ftp调试,连接FTP调试-QNX的PC104调试ftp

    如果你认为本系列文章对你有所帮助 请大家有钱的捧个钱场 点击此处赞助 赞助额1元起步 多少随意 锋影 e mail 174176320 qq com 板子上QNX系统ftp的调试 双网卡 IP分别为 192 192 1 10 192 192
  • Qt编程学习笔记

    新建项目 gt 其他项目 gt Empty qmake Project 添加新文件 项目 gt run gt Run in terminal 发现cin gt gt a无法向a输入数据 检查 pro文件发现缺少参数CONFIG consol
  • chrome黑暗模式设置

    1 前言 使用Chrome有很久了 但一直没发现有令人满意的黑暗模式 期间试过通过在谷歌商店中安装黑暗主题 但感觉不好 主要有以下 除了chrome的界面是黑暗模式外 打开其它的页面还是亮模式 我希望黑暗模式支持以下特性 chrome的标签
  • Android ViewPager实现导航页效果

    效果 源码地址 文章翻译自 https www cnblogs com aademeng articles 6238933 html 代码 MainActivity public class MainActivity extends App
  • mysql APPARENT DEADLOCK!!! Complete Status:Managed Threads: 3 (c3p0,druid)

    问题场景 由于在生产环境出现问题 应用挂掉 作为菜鸟运维 解决问题有点忙手忙脚 线上bug修复 重启tomcat 启动报错 错误截图在下面 根据日志分析像是死锁 使用C3P0连接池 tomcat启动完之后 还能正常运行 解决问题经过 网上有
  • v4l2在帧缓冲区预览摄像头

    在应用层通过v4l2 api将采集的摄像头数据yuv转为rgb后写到帧缓冲区达到预览摄像头的目的 程序运行后切换到tty下就可以看到 我的屏幕是bgra格式的 这点要注意 不同屏幕格式不同 不同r g b的偏移通过修改 0x00 lt lt