(一)视频定义(什么是视频)
(二)图像
1.像素:图像由像素组成(如下图图片中的一个个小格子)。
对于每个像素,还有位深的概念:用多少个位来表示位深。类似于音频中的采样大小
RGB888:对于R、G、B中每个元素占8位
RGBA:同上,多了一个A(透明度)
2.RGB:每个像素是由RGB三元素组成;如下图格子表示一个像素,内部由RGB(三个发光二极管)组成。
通过RGB(三个发光二极管不同亮度组成)可以组合成多种色彩:
3.分辨率:用来表示图像在X、Y轴上各有多少个像素点。
(三)屏幕显示器
1.显示器和图像都具有像素!!!
对于屏幕而言,每一个像素如下所示:
根据图像的数据,来控制屏幕中每一个像素点的发光二极管的强度。从而实现不同颜色点,拼接为最终图像!!
2.RGB的色彩问题(部分情况使用的BGR顺序,使得与显示器的驱动程序处理顺序不一致,导致图像渲染出错)
3.屏幕指标
PPI:每英寸下的像素数
DPI:每英寸下的点数
基本上两者相等,极少情况下DPI每个点中存在多个像素
PPI > 300 :视网膜级别(人眼无法区别是由像素组成,认为是一整块图像)
(四)码流的计算
1.分辨率(清晰度)
常见的视频宽高比是16:9,部分老式显示器可能是4:3;如果一个视频的宽高比不是这两种,则需要进行转换。 对于360P,16:9中是指高360P,宽640P
2.帧率(平滑、流畅度)
对于直播系统中,为了减少所占码流的大小,通常采用15帧/S;
录课:30帧/S
视频播放60帧/S
3.未编码视频的RGB码流
其中3Byte表示一个像素RGB所占大小
(五)图像的显示(图像和显示器分辨率不一致情况)
二:YUV了解
各种格式解析:https://www.fourcc.org/rgb.php
(一)什么是YUV(有了RGB图像之后,为什么还需要YUV?)
由电视系统发展而来,逐步从黑白--->彩色;YUV可以兼容这两种模式。当获取Y信号之后就可以正常的进行播放,此时颜色为黑白(黑白电视机可以播放)。UV用于描述影像的色彩和饱和度(彩色电视机播放需要)。最标准YUV格式为YUV4:2:0
在DVD、摄像机、数字电视等消费类视频产品中,常用的色彩编码方案是YCbCr,其中Y是指亮度分量,Cb指蓝色色度分量,而Cr指红色色度分量。
人的肉眼对视频的Y分量更敏感,因此在通过对色度分量进行子采样来减少色度分量后,肉眼将察觉不到的图像质量的变化。
1.YUV图像各个分量
YUV原始图像:
YUV图像的Y分量(只有明亮度、没有色彩和饱和度):
YUV图像的U分量(只有Cb分量,图片蓝色分量):
YUV图像的V分量(只有Cr分量,图片红色分量):
只有在电视中使用了YUV格式,在手机、屏幕、显示器中使用RGB格式,所以YUV显示在屏幕需要进行转换!
2.RGB与YUV的关系
RGB转YUV:
YUV转RGB:
3.YUV常见格式
YUV4:4:4 一个Y元素对应一个U元素对应一个V元素,对于1280×720图像,数据量为:1280×720×3---其中3同RGB888一致,表示3个字节(24位) YUV4:2:2 对于每一个横行,隔一列有一个UV,数据量为1280×720×1+2×1280×720/2 = 1280×720×2--- ×1表示Y占1字节,/2是对于UV来说是每隔一列才有一个UV YUV4:2:0 最标准,最广泛; 隔行分U或者V分量,隔列分是否有UV分量;数据量为1280×720×1+2×1280×720/4 = 1280×720×1.5
各个格式详细见下面图片!!!
YUV4:2:0
对于YUV4:2:0而言满足下面的计算:
YUV4:2:0优点:
1.YUV兼容以前的图像
2.相比较RGB而言,数据量减少1半,存储更加具有优势!
补充:对齐知识!!!!
对于已经对齐的分辨率如:1280*720,640*480这样的分辨率它的YUV420P数据格式是完全对齐(16位对齐) 而像176*144这样的size它的YUV420P不是16位对齐,需要补空白位(0补齐),使得176*144这样的size能16位对齐。
YUV4:2:2
此格式的打包格式为,YUYV YUYV YUYV ……(YUYV422格式);此格式的存储格式为:首先全是Y,然后全是U,最后全是V。
YUV4:4:4
更多格式见:https://blog.csdn.net/cgwang_1580/article/details/79595958
(二)YUV4:2:0存储格式
YUV数据是分层存储的,所以可以更加方便的与黑白电视机相兼容,只需要读取前面的Y数据即可,后面的UV数据舍弃即可。对于彩色电视机,将后面的UV数据一块读取。
4:2:0,是指4个Y对应纵向1个U,1个V;如下图(色彩对应)
不同系统下,存储格式可能不同:比如IOS使用YV12,android使用NV21
YUV4:2:0 码流计算:
(三)YUV命令行采集数据
原始mp4参数:
1.根据参数进行命令行采集YUV数据
ffmpeg -i gfxm.mp4 -an -c:v rawvideo -pix_fmt yuv420p out.yuv
ffplay -pix_fmt yuv420p -s 864*486 out.yuv #-s是说明播放时的分辨率 -pix_fmt指定格式 如果两个参数不一致,会导致播放出现花屏等问题
播放YUV图像的Y分量(其他分量也可以播放):
ffplay -pix_fmt yuv420p -s 864*486 -vf extractplanes='y' out.yuv #-vf 表示滤波器,vf属于简单滤波
提取单独分量:
ffmpeg -i gfxm.mp4 -filter_complex 'extractplanes=y+u+v[y][u][v]' -map '[y]' y.yuv -map '[u]' u.yuv -map '[v]' v.yuv
#其中[]表示别名,map可以引用别名
播放出现问题:
ffplay -s 864*486 y.yuv
需要指定格式pix_fmt为单色,而不是按默认YUV去读取
ffplay y.yuv -pix_fmt gray -s 864*486
注意:对于U、V分量,由于是YUV4:2:0,其中U、V分量只占1/2(即X,Y轴分辨率各自减少一半)
ffplay u.yuv -pix_fmt gray -s 432*243
(四)FFmpeg编程采集
x11grab见:https://www.cnblogs.com/ssyfj/p/14576359.html
#include <stdio.h> #include <libavutil/log.h> #include <libavcodec/avcodec.h> #include <libavdevice/avdevice.h> #include <libavformat/avformat.h> #include <libswresample/swresample.h> void rec_video(){ char* devicename = ":0.0"; //:前面为空,表示录制本地;:后面,如果NumA为0, 则表示连接到6000端口; 使用unix socket方式连接时则表示连接的unix socket的路径, 如果为0, 则表示连接到/tmp/.X11-unix/X0 . NumB则几乎总是0. char errors[1024]; int ret,count=0,len; FILE* fp = NULL; AVFormatContext* fmt_ctx=NULL; //格式上下文获取-----av_read_frame获取packet AVDictionary* options=NULL; AVInputFormat *iformat=NULL; AVPacket packet; //包结构 //获取输入(采集)格式 iformat = av_find_input_format("x11grab"); //驱动,用来录制桌面 //设置参数 av_dict_set(&options,"video_size","1024*768",0); av_dict_set(&options,"framerate","15",0); //打开输入设备 ret = avformat_open_input(&fmt_ctx,devicename,iformat,&options); //----打开输入设备,初始化格式上下文和选项 if(ret<0){ av_strerror(ret,errors,1024); av_log(NULL,AV_LOG_ERROR,"Failed to open video device,[%d]%s\n",ret,errors); } av_log(NULL,AV_LOG_INFO,"Success to open video device\n"); //打开文件 fp = fopen("./video.yuv","wb"); if(fp==NULL){ av_log(NULL,AV_LOG_ERROR,"Failed to open out file\n"); goto fail; } //开始从设备中读取数据 while((ret=av_read_frame(fmt_ctx,&packet))==0&&count++<500){ av_log(NULL,AV_LOG_INFO,"Packet size:%d(%p),cout:%d\n",packet.size,packet.data,count); fwrite(packet.data,1,packet.size,fp); fflush(fp); //释放空间 av_packet_unref(&packet); } fail: if(fp) fclose(fp); //关闭设备、释放上下文空间 avformat_close_input(&fmt_ctx); return ; } int main(int argc,char* argv) { av_register_all(); av_log_set_level(AV_LOG_DEBUG); //注册所有的设备,包括我们需要的音频设备 avdevice_register_all(); rec_video(); return 0; }