c 实用化的文本终端实时显示摄像头视频

因为采用yuv格式,帧率都很低。图像会拖影。把图像尺寸尽量缩小,能大大改善。现在最麻烦的是图像上有黑色的闪影,不知是为啥?如是framebuffer引起的就无解了。终于找到问题了,是在显示前加了一条用黑色清屏造成的,删除此句图像马上干净完美了。

对比茄子软件拍的视频,肉眼可见的超越。

还有,每个摄像头的录像尺寸是固定的,不能乱设,否则不能显示图像,可以用前面写的查看v4l2摄像头软件查一下参数。

在这个基础上,再加上把数据写入文件,就具有录像功能了。但因为数据太大。不能长时间录像,意义不大。准备学一下mjpg格式再写。

此方法.可以编写视频回放慢放功能软件,可惜yuv格式拍摄的视频帧率都不高,一般都不会超过10帧/秒。

正准备采用多进程,管道,内存共享等方式来提高帧率,原理就是减少拍摄采样循环中非采样占用的时间。如显示,如yuv转换,一句话就是采样循环中尽量少塞入其他功能代码。

现在采用framebuffer 来显示视频不是通用方法,最完美是在gui图形界面中显示,但gui显示方法涉及到QT编程太复杂。

还有,在显示环节填充黑色字节,可以把视频图像移动到整个屏幕中任何地方。我在想,华为鸿蒙的开机动画是不是也是采用的这个显示方法。

复制代码
#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>  //v4l2 头文件
#include <string.h>
#include <sys/mman.h>
#include <linux/fb.h>


#define  pic_width   640      //1280*720  640*360  960*540   320*240   424*240  848*480 640*480     
#define  pic_heigth  480

#define WT  0    //如果图片是斜的,微调此值

static int color1=0;
static int color2=0;
static	int sp[3000*2000];      //摄像头图片最大尺寸
static struct fb_var_screeninfo var;

#define  HCQ  10              //v4l2 缓冲区个数


int main(void)
{
	
	int fd = open("/dev/video0", O_RDWR);
	if (fd < 0) {
		perror("打开设备失败");
		return -1;
	}
	struct v4l2_format vfmt;
	vfmt.type = 1;
	vfmt.fmt.pix.width = pic_width;
	vfmt.fmt.pix.height =pic_heigth;
	
	vfmt.fmt.pix.pixelformat=v4l2_fourcc('Y','U','Y','V');
	
	int ret = ioctl(fd, VIDIOC_S_FMT, &vfmt);
	if (ret < 0) {
		perror("设置格式失败");
	}
	
	struct v4l2_requestbuffers reqbuffer;
	reqbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	
	reqbuffer.count = HCQ;
	reqbuffer.memory = V4L2_MEMORY_MMAP ;
	ret  = ioctl(fd, VIDIOC_REQBUFS, &reqbuffer);
	if (ret < 0) {
		perror("申请队列空间失败");
	}
	//我理解以下部分
	struct v4l2_buffer mapbuffer; //相当于在为mapbuffer 设置读写规则,还没有开始数据传输,怎样���摄像头buf传输数据保存在多少个mmap中
	mapbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	unsigned char *mptr[HCQ];
	
	
	for (int t = 0; t < HCQ; t++) {
		
		mapbuffer.index = t;
		ret = ioctl(fd, VIDIOC_QUERYBUF, &mapbuffer);//读摄像头缓冲区数据,为映射mmap准备
		if(ret < 0)
		{
			perror("查询内核空间队列失败");
		}
		
		mptr[t] = mmap(NULL, mapbuffer.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, mapbuffer.m.offset); //摄像头缓冲区映射到内存
		//	size[t] = mapbuffer.length;
		
		ret  = ioctl(fd, VIDIOC_QBUF, &mapbuffer);    //释放一个buf缓冲区空间,摄像头可以为此buf写入数据
		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;  //安装mapbuffer设置的规矩来读,数据从mmap中提取
//---------------------------------------------------------------------------
	while(1){
		
		
		ret = ioctl(fd, VIDIOC_DQBUF, &readbuffer); //读当前队列缓冲区的数据  index循环+1,VIDIOC_DQBUF执行一次,index加1
		if (ret < 0) {
			perror("提取数据失败");
		}
		int i=readbuffer.index;
		int len = readbuffer.length;
		
		
		char (*p)[4]=(char (*)[4])mptr[i];    //   p[][0]=Y   p[][1]=U  p[][2]=Y  p[][3]=V
		//R = Y + 1.4075 *(V-128)
		//G = Y -- 0.3455 *(U --128) -- 0.7169 *(V --128)
		//	B = Y + 1.779 *(U -- 128)
		
		
		int t=0;
		int u=0;
		while(1){
			if(4*t>=len){
				break;
			}
			int Y1=(unsigned char)p[t][0];
			if((4*t+1)>=len){
				break;
			}
			int U=(unsigned char)p[t][1];
			if((4*t+2)>=len){
				break;
			}
			int Y2=(unsigned char)p[t][2];
			if((4*t+3)>=len){
				break;
			}
			int V=(unsigned char)p[t][3];
			//-------------------------------------------------
			int B0=Y1+1.779*(U-128);
			if(B0>255)  B0=255;
			if(B0<0)    B0=0;
			
			int G0=Y1-0.3455*(U-128)-0.7169*(V-128);
			if(G0>255)  G0=255;
			if(G0<0)    G0=0;
			
			int R0=Y1+1.4075*(V-128);
			if(R0>255)  R0=255;
			if(R0<0)    R0=0;
			
			int B1=Y2+1.779*(U-128);
			if(B1>255)  B1=255;
			if(B1<0)    B1=0;
			
			int G1=Y2-0.3455*(U-128)-0.7169*(V-128);
			if(G1>255)  G1=255;
			if(G1<0)    G1=0;
			
			int R1=Y2+1.4075*(V-128);
			if(R1>255)  R1=255;
			if(R1<0)    R1=0;
			
			color1=(R0<<16)|(G0<<8)|B0;
			color2=(R1<<16)|(G1<<8)|B1;
			
			sp[u]=color1;
			sp[u+1]=color2;
			
			t++;
			u=u+2;
			
		}
		
		
		
		//--------------------------------
		int fd_fb = open("/dev/fb0", O_RDWR);
		if (fd_fb < 0)
		{
			puts("/dev/fb0 error");
			return -1;
		}
		if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var))
		{
			puts("ioctl error");
			return -1;
		}
		
//	unsigned int  pixel_width = var.bits_per_pixel / 8;                  //deepin=32
		int screen_size = var.xres * var.yres * var.bits_per_pixel / 8;     //刚好等于图片的长乘宽
		
		unsigned char *fb_base = mmap(NULL, screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);
		if (fb_base == NULL)
		{
			puts("mmap error");
			return -1;
		}
//	memset(fb_base,0, screen_size);     //设底色为黑色  必须删除这句,否则要造成黑带屏闪
		
		int zz=0;
		var.xres=var.xres+WT;            //如果图片是斜的,微调WT的值
		for(int a=0;a<var.yres;a++){
			for(int b=0;b<var.xres;b++){
				
				
				unsigned int (*p)[var.xres]=(unsigned int (*)[var.xres])fb_base;  //此处不能用图片的尺寸,因为有不同尺���大小
				if(b<pic_width){               //如果图片宽度小于framebuffer 的宽度,小与部分填充0(黑色)
					p[a][b]=sp[zz];   //sp[zz]
					zz++;
				}else{
					p[a][b]=0;                 //填充黑色
				}
				
			}
		}
		
		ret  = ioctl(fd, VIDIOC_QBUF, &readbuffer);    //把缓冲区数据放入读队列中
		if (ret < 0) {
			perror("放回失败");
		}
		
		munmap(fb_base, screen_size);
		close(fd_fb);
		
	}
	puts("over");
	
	return 0;
}
相关推荐
Scott9999HH7 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
Yana.nice7 小时前
Linux 只保留 30 天内日志(find命令删除日志文件)
linux·运维·chrome
码智社8 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海8 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖9 小时前
JDK 26 新特性详解
java·开发语言
马优晨9 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
DFT计算杂谈11 小时前
无 Root 权限在 Tesla K80 零门槛部署 DeepSeek 大模型
linux·服务器·网络·数据库·机器学习
人邮异步社区11 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大11 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai12 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang