一、前语
本系列文章是对音视频技能入门常识的整理和复习,为进一步深化系统研究音视频技能稳固根底。文章列表:
- 01-音视频技能中心常识|了解音频技能【移动通讯技能的开展、声响的实质、深化了解音频】
- 02-音视频技能中心常识|建立开发环境【FFmpeg与Qt、Windows开发环境建立、Mac开发环境建立、Qt开发根底】
- 03-音视频技能中心常识|Qt开发根底【
.pro
文件的装备、Qt控件根底、信号与槽】 - 04-音视频技能中心常识|音频录制【指令行、C++编程】
- 05-音视频技能中心常识|音频播映【播映PCM、WAV、PCM转WAV、PCM转WAV、播映WAV】
- 06-音视频技能中心常识|音频重采样【音频重采样简介、用指令行进行重采样、经过编程重采样】
- 07-音视频技能中心常识|AAC编码【AAC编码器解码器、编译FFmpeg、AAC编码实战、AAC解码实战】
- 08-音视频技能中心常识|成像技能【重识图片、详解YUV、视频录制、显现BMP图片、显现YUV图片】
- 09-音视频技能中心常识|视频编码解码【了解H.264编码、H.264编码、H.264编码解码】
- 10-音视频技能中心常识|RTMP服务器建立【流媒体、服务器环境】
二、重识图片
要想学好音视频,首要得先好好研究一下图片。
1. 像素
下图的分辩率是60×50。
用Photoshop放大图片上百倍后,能够明晰地看到图片由若干个方形的色块组成,每一个方形的色块被称为:像素(Pixel)。这张图片的每一行都有60个像素,共50行,一共60*50=3000个像素。
总结一下:
- 每张图片都是由N个像素组成的(N≥1)
- 假如一张图片的分辩率是WxH,那么:
- 每一行都有W个像素,共H行,一共W*H个像素
- 宽度是W像素,高度是H像素
每个像素都有自己独立的色彩,若干个像素就组成了一张色彩缤纷的完好图片。
2. RGB色彩模型
1666年,伟大的科学家牛顿进行了闻名的色散试验:用一块三棱镜分化太阳光。
试验发现:太阳光经过三棱镜折射后,会被折射涣散成红、橙、黄、绿、蓝、靛、紫7种单色光。其间的红、绿、蓝被称为是色光三原色。
接下来,再看一个很重要的概念:RGB色彩模型(RGB color model),又称为三原色光方式。
- 将红(Red)、绿(Green)、蓝(Blue)三原色的色光以不同的含量相叠加,能够组成发生各种色彩光
每个像素的色彩,能够经过赤色、绿色、蓝色以不同的含量混合而成。比方:
- 红色(Red)、绿色(Green)能够组成:黄色(Yellow)
- 红色(Red)、蓝色(Blue)能够组成:洋赤色(Magenta)
- 绿色(Green)、蓝色(Blue)能够组成:青色(Cyan)
- 红色(Red)、绿色(Green)、蓝色(Blue)能够组成:白色(White)
3. 位深度
每一个像素的色彩信息是怎么存储的呢?
-
取决于图片的位深度(Bit Depth),也称为:色彩深度(Color Depth,简称:色深)
-
假如一张图片的位深度为n,那么它的每一个像素都会运用n个二进制位来存储色彩信息
3.1 24bit位深度的意义
上图的位深度是24,它的详细意义是:
- 每一个像素都会运用24个二进制位来存储色彩信息
- 每一个像素的色彩都是由红(Red)、绿(Green)、蓝(Blue)3个色彩通道组成的
- 每个色彩通道都用8bit来表明其“含量”(值),取值规模是:
- 二进制:00000000~11111111
- 十进制:0~255
- 十六进制:00~FF
- 举例:01000000 11100000 11010000(共24bit)表明绿宝石色(Turquoise)
- 赤色的值:二进制01000000,十进制64,十六进制40
- 绿色的值:二进制11100000,十进制224,十六进制E0
- 蓝色的值:二进制11010000,十进制208,十六进制D0
- 64的赤色 + 224的绿色 + 208的蓝色 = 绿宝石色
3.2 24bit色彩的表明方式
咱们常用2种方式来表明24bit色彩,比方刚才提到的绿宝石色
- 十进制:rgb(64, 224, 208)
- 十六进制:#40E0D0
常见的24bit色彩:
- 赤色:rgb(255, 0, 0),#FF0000
- 绿色:rgb(0, 255, 0),#00FF00
- 蓝色:rgb(0, 0, 255),#0000FF
- 黄色:rgb(255, 255, 0),#FFFF00
- 洋赤色:rgb(255, 0, 255),#FF00FF
- 青色:rgb(0, 255, 255),#00FFFF
- 白色:rgb(255, 255, 255),#FFFFFF
- 黑色:rgb(0, 0, 0),#000000
- 当红绿蓝全为0时,便是黑色
- 这个其实很简略了解:没有任何光,自然是一片乌黑
- 所以说:黑色是世界上最纯真的色彩,由于它啥也没有,(づ。◕ᴗᴗ◕。)づ
- 相反,白色是世界上最不纯真的色彩,由于它啥都有,并且都是满色(全是255)
- 更多色彩,能够参考色彩对照表,红绿蓝的份额不同,组成的色彩也就不同
3.3 色彩数量
假如位深度为n,那么每一个像素能显现2n种色彩。
-
所以,位深度为24时,每一个像素能显现224种色彩,也便是16777216种色彩(约1678万)
-
24bit色彩,也被称为是:真五颜六色(True Color),也便是常说的24位真彩
3.4 其他位深度
除了24bit,常见的位深度还有:
- 1bit:2种色彩,黑白两色
- 3bit:8种色彩,用于大部分前期的电脑显现器,红绿蓝各占1位
- 8bit:256种色彩,用于最前期的五颜六色Unix工作站,赤色占3位、绿色占3位、蓝色占2位
- 16bit:赤色占5位、蓝色占5位、绿色占6位
- 32bit:根据24位,添加8个位的透明通道
- 能够表明带有透明度的色彩
- 比方CSS中的rgba(255, 0, 0, 0.5)表明50%透明度的赤色
3.5 不同位深度的比照
位深度越大,能表明的色彩数量就越多,图片也就越鲜艳,色彩过渡就会越平滑。下面的图片来源自Tech-ease。
-
24bit(能表明约1678万种色彩)
-
8bit(能表明256种色彩)
-
7bit(能表明128种色彩)
-
6bit(能表明64种色彩)
-
5bit(能表明32种色彩)
-
4bit(能表明16种色彩)
-
3bit(能表明8种色彩)
-
2bit(能表明4种色彩)
-
1bit(能表明2种色彩)
4. 格局
一说到图片,咱们应该马上能想到拓展名为jpg、png、gif的图片文件。
每张图片都有自己的巨细,你是否思考过:一张图片的巨细是怎么计算出来的?为什么dragon01.jpg的巨细是288KB?
-
要想知道一张图片的巨细是多少?首要得知道每个像素的巨细是多少。
-
假如位深度是n,那么每个像素的巨细便是n个二进制位
下图的分辩率是60×50,位深度是24,所以:
- 每个像素的巨细是:24bit(3字节,1字节=8bit)
- 图片的理论巨细是:(60*50)*(24/8)=9000B≈8.79KB
但实际上你会发现:在相同分辩率、相同位深度的前提下,把这张图片存成2种不同的格局(jpg、png),它们的巨细是不同的,并且都小于理论上的8.79KB。
其实jpg、png都是经过紧缩后的图片(详细的紧缩算法和原理,就不在此评论了,咱们能够到网上自行搜索),所以它们的巨细会比理论值偏小。
图片的紧缩类型能够分为2种:
-
无损紧缩
- 不丢失图片质量
- 紧缩比小,体积大
- 解压(显现)后能够还原出完好的原始图片数据,不会丢失任何图片信息
-
有损紧缩
- 会丢失图片质量
- 紧缩比大,体积小
- 解压(显现)后无法还原出完好的原始图片数据,会丢失掉一些图片信息
- 紧缩比 = 未紧缩巨细 / 紧缩后巨细
紧缩类型 | 位深度 | |
---|---|---|
JPG(JPEG) | 有损紧缩 | 24bit |
PNG | 无损紧缩 | 8bit、24bit、32bit |
GIF | 无损紧缩 | 8bit |
5. GIF
众所周知,gif是一种支撑动画的图片,所以一般也叫作gif动态图,微信的动态表情包便是根据gif动态图。
gif动画的完成原理相似手翻书。
gif的动画原理是:
- gif内部存储了很多帧(张)静态图片
- 在短时间内,接连按次序地呈现每一帧静态图片,就构成了动画的作用
像上面那张《悟空vs克林》的gif动态图,它内部存储了44帧静态图,只要按次序从01.jpg播映到44.jpg,就能呈现出连接的动画作用。
不管是gif动态图,仍是手翻书,它们的动画原理其实都根据:视觉暂留(Persistence of vision)现象。
- 当人眼所看到的形象消失后,人眼仍能持续保留其形象约0.1~0.4秒左右,这种现象被称为视觉暂留现象
- 人眼观看物体时,成像于视网膜上,并由视神经输入人脑,感觉到物体的像,但当物体移去时,视神经对物体的形象不会当即消失,而要连续0.1~0.4秒的时间,人眼的这种性质被称为“眼睛的视觉暂留”
- 咱们日常运用的日光灯每秒大约平息100余次,但咱们根本感觉不到日光灯的闪烁,这都是由于视觉暂留的作用
- 在一帧图片消失在大脑中之前呈现下一帧图片,反复如此,就能够构成连接的动画作用
- 电影的帧率是24fps
- fps:每秒的帧数,Frames Per Second
三、详解YUV
本文的主角是多媒体领域非常重要的一个概念:YUV。
1. 简介
YUV,是一种色彩编码办法,跟RGB是同一个等级的概念,广泛应用于多媒体领域中。
也便是说,图画中每1个像素的色彩信息,除了能够用RGB的办法表明,也能够用YUV的办法表明。
2. YUV vs RGB
比照RGB,YUV有哪些不同和优势呢?
2.1 体积更小
- 假如运用RGB
- 比方RGB888(R、G、B每个重量都是8bit)
- 1个像素占用24bit(3字节)
- 假如运用YUV
- 1个像素能够减小至均匀只占用12bit(1.5字节)
- 体积为RGB888的一半
2.2 组成
RGB数据由R、G、B三个重量组成。
YUV数据由Y、U、V三个重量组成,现在一般说的YUV指的是YCbCr。
- Y:表明亮度(Luminance、Luma),占8bit(1字节)
-
Cb、Cr:表明色度(Chrominance、Chroma)
- Cb(U):蓝色色度重量,占8bit(1字节)
- Cr(V):赤色色度重量,占8bit(1字节)
2.3 兼容性
根据上面的图片,不难看出:
- Y重量对呈现出明晰的图画有着很大的贡献
- Cb、Cr重量的内容不太简略辨认清楚
此外,你是否感觉:Y重量的内容看着有点眼熟?其实以前黑白电视的画面便是长这姿态的。
YUV的创造处在五颜六色电视与黑白电视的过渡时期。
- YUV将亮度信息(Y)与色度信息(UV)别离,没有UV信息相同能够显现完好的图画,只不过是黑白的
- 这样的设计很好地解决了五颜六色电视与黑白电视的兼容性问题,使黑白电视也能够接纳五颜六色电视信号,只不过它只显现了Y重量
- 五颜六色电视有Y、U、V重量,假如去掉UV重量,剩下的Y重量和黑白电视相同
3. 转化
3.1 公式1
Y = 0.257R + 0.504G + 0.098B + 16
U = -0.148R - 0.291G + 0.439B + 128
V = 0.439R - 0.368G - 0.071B + 128
R = 1.164(Y - 16) + 2.018(U - 128)
G = 1.164(Y - 16) - 0.813(V - 128) - 0.391(U - 128)
B = 1.164(Y - 16) + 1.596(V - 128)
- RGB的取值规模是[0,255]
- Y的取值规模是[16,235]
- UV的取值规模是[16,239]
3.2 公式2
Y = 0.299R + 0.587G + 0.114B
U = 0.564(B - Y) = -0.169R - 0.331G + 0.500B
V = 0.713(R - Y) = 0.500R - 0.419G - 0.081B
R = Y + 1.403V
G = Y - 0.344U - 0.714V
B = Y + 1.770U
- RGB的取值规模是[0, 1]
- Y的取值规模是[0, 1]
- UV的取值规模是[-0.5, 0.5]
3.3 公式3
Y = 0.299R + 0.587G + 0.114B
U = -0.169R - 0.331G + 0.500B + 128
V = 0.500R - 0.419G - 0.081B + 128
R = Y + 1.403(V - 128)
G = Y - 0.343(U - 128) - 0.714(V - 128)
B = Y + 1.770(U - 128)
- RGB的取值规模是[0, 255]
- YUV的取值规模是[0, 255]
4. 色度二次采样
4.1 原理
人眼的视网膜上,散布着两种感光细胞:视杆细胞和视锥细胞。
-
视杆细胞
- 感知光线的强弱
- 没有色彩辨认功能
- 担任夜间非五颜六色视觉
-
视锥细胞
- 感知色彩
- 担任白天五颜六色视觉
- 假如你的视锥细胞发育不正常,数量太少,那感知色彩就会受阻,可能会导致你色弱
人眼中有上亿个感光细胞,其间视杆细胞占了95%,而视锥细胞仅占5%。
因而,人眼对亮度的灵敏程度要高于对色度的灵敏程度,人眼对于亮度的分辩要比对色彩的分辩精密一些。
假如把图画的色度重量减少一些,人眼也一点点感觉不到改变和差异。
4.2 概念
假如在色度重量上进行(相对亮度重量)较低分辩率的采样,也便是存储较多的亮度细节、较少的色度细节,这样就能够在不明显下降画面质量的同时减小图画的体积。上述进程称为:色度二次采样(Chroma Subsampling)。
4.3 采样格局
采样格局一般用A:B:C的方式来表明,比方4:4:4、4:2:2、4:2:0等,其间咱们最需要关注的是4:2:0。
- A:一块A*2个像素的概念区域,一般都是4
- B:第1行的色度采样数目
-
C:第2行的色度采样数目
- C的值一般要么等于B,要么等于0
上图中,不管是哪种采样格局,Y重量都是全水平、全垂直分辩率采样的,每一个像素都有自己独立的Y重量。
4.3.1 4:4:4
- 第1行收集4组CbCr重量,第2行收集4组CbCr重量
- 每1个像素都有自己独立的1组CbCr重量
- Y重量与CbCr重量的水平方向份额是1:1(每1列都有1组CbCr重量)
- Y重量与CbCr重量的垂直方向份额是1:1(每1行都有1组CbCr重量)
- Y重量与CbCr重量的总份额是1:1
- 1个像素占用24bit(3字节),跟RGB888的体积相同
- 24bpp(bits per pixel)
- 这种格局是没有进行色度二次采样的
叉叉代表:亮度。
圆圈代表:色度。
4.3.2 4:2:2
- 第1行收集2组CbCr重量,第2行收集2组CbCr重量
- 水平方向相邻的2个像素(1行2列)共用1组CbCr重量
- Y重量与CbCr重量的水平方向份额是2:1(每2列就有1组CbCr重量)
- Y重量与CbCr重量的垂直方向份额是1:1(每1行都有1组CbCr重量)
- Y重量与CbCr重量的总份额是2:1
- 1个像素均匀占用16bit(2字节)
- 16bpp
- 由于2个像素共占用32bit(4字节 = 2个Y重量 + 1个Cb重量 + 1个Cr重量)
4.3.3 4:2:0
- 第1行收集2组CbCr重量,第2行同享第1行的CbCr重量
- 相邻的4个像素(2行2列)共用1组CbCr重量
- Y重量与CbCr重量的水平方向份额是2:1(每2列就有1组CbCr重量)
- Y重量与CbCr重量的垂直方向份额是2:1(每2行就有1组CbCr重量)
- Y重量与CbCr重量的总份额是4:1
- 1个像素均匀占用12bit(1.5字节)
- 12bpp
- 由于4个像素共占用48bit(6字节 = 4个Y重量 + 1个Cb重量 + 1个Cr重量)
5. 存储格局
存储格局,决议了YUV数据是怎么摆放和存储的。本文只介绍一些常见的存储格局。
5.1 分类
YUV的存储格局能够分为3大类:
-
Planar(平面)
- Y、U、V重量分开单独存储
- 称号一般以字母p结束
-
Semi-Planar(半平面)
- Y重量单独存储,U、V重量交织存储
- 称号一般以字母sp结束
-
Packed(紧凑)
- 或许叫Interleaved (交织)
- Y、U、V重量交织存储
5.2 4:4:4
5.2.1 Planar
- I444
Y Y Y Y
Y Y Y Y
U U U U
U U U U
V V V V
V V V V
- YV24
Y Y Y Y
Y Y Y Y
V V V V
V V V V
U U U U
U U U U
5.2.2 Semi-Planar
- NV24
Y Y Y Y
Y Y Y Y
U V U V U V U V
U V U V U V U V
- NV42
Y Y Y Y
Y Y Y Y
V U V U V U V U
V U V U V U V U
5.3 4:2:2
5.3.1 Planar
- I422
Y Y Y Y
Y Y Y Y
U U
U U
V V
V V
- YV16
Y Y Y Y
Y Y Y Y
V V
V V
U U
U U
5.3.2 Semi-Planar
- NV16
Y Y Y Y
Y Y Y Y
U V U V
U V U V
- NV61
Y Y Y Y
Y Y Y Y
V U V U
V U V U
5.3.3 Packed
- UYVY
U Y V Y U Y V Y
U Y V Y U Y V Y
- YUYV
Y U Y V Y U Y V
Y U Y V Y U Y V
- YVYU
Y V Y U Y V Y U
Y V Y U Y V Y U
5.4 4:2:0
5.4.1 Planar
- I420
- 大多数视频解码器以I420格局输出原始图片
Y Y Y Y
Y Y Y Y
U U
V V
- YV12
Y Y Y Y
Y Y Y Y
V V
U U
5.4.2 Semi-Planar
- NV12
Y Y Y Y
Y Y Y Y
U V U V
- NV21
Y Y Y Y
Y Y Y Y
V U V U
6. 格局转化
6.1 其他图片格局转YUV
ffmpeg -i in.png -s 512x512 -pix_fmt yuv420p out.yuv
上述指令生成的yuv文件巨细是:393216字节 = 512 * 512 * 1.5字节。
-
-s
- 设置图片的尺度
- 能够用一些固定字符串表明尺度,比方hd720表明1280×720
- 假如不设置此选项,默许会跟从输入图片的尺度
-
-pix_fmt
- 设置像素格局
- 能够经过ffmpeg -pix_fmts检查FFmpeg支撑的像素格局
- 假如不设置此选项,默许会跟从输入图片的像素格局
- 比方可能是rgb24、rgba8、pal8等
- 能够经过ffprobe检查某图片的像素格局,比方ffprobe in.png
6.2 YUV转其他图片格局
ffmpeg -s 512x512 -pix_fmt yuv420p -i in.yuv out.jpg
- 这里必须得设置YUV的尺度(-s)、像素格局(-pix_fmt)
- 这就相似于:对pcm进行编码时,必须得设置采样率(-ar)、声道数(-ac)、采样格局(-f)
7. 显现YUV
7.1 完好的YUV
能够经过ffplay显现YUV数据。
-
YUV中直接存储的是一切像素的色彩信息(能够了解为是图画的一种原始数据)
-
必须得设置YUV的尺度(-s)、像素格局(-pix_fmt)才干正常显现
-
这就相似于:播映pcm时,必须得设置采样率(-ar)、声道数(-ac)、采样格局(-f)
ffplay -s 512x512 -pix_fmt yuv420p in.yuv
# 在ffplay中
# -s已经过期,主张改为:-video_size
# -pix_fmt已经过期,主张改为:-pixel_format
ffplay -video_size 512x512 -pixel_format yuv420p in.yuv
7.2 单个重量
能够运用过滤器(filter)显现其间的单个重量(r、g、b、y、u、v)。
# 只显现r重量
ffplay -vf extractplanes=r in.png
# 只显现g重量
ffplay -vf extractplanes=g in.png
# 只显现b重量
ffplay -vf extractplanes=b in.png
# 只显现y重量
ffplay -video_size 512x512 -pixel_format yuv420p -vf extractplanes=y in.yuv
# 只显现u重量
ffplay -video_size 512x512 -pixel_format yuv420p -vf extractplanes=u in.yuv
# 只显现v重量
ffplay -video_size 512x512 -pixel_format yuv420p -vf extractplanes=v in.yuv
-
-vf
- 设置视频过滤器
- 等价写法:-filter:v
-
extractplanes
- 抽取单个重量的内容到灰度视频流中
四、视频录制01_指令行
本文的主要内容:演示怎么利用指令行收集摄像头的视频数据。 阅览本文之前,主张先阅览《音频录制01_指令行》对常用指令作一个根本知道。
1. Windows
1.1 dshow支撑的设备
ffmpeg -f dshow -list_devices true -i dummy
输出成果大致如下所示。
DirectShow video devices (some may be both video and audio devices)
"Integrated Camera"
DirectShow audio devices
"麦克风阵列 (Realtek(R) Audio)"
Integrated Camera是我笔记本上自带的摄像头,等会咱们便是经过这个摄像头收集视频数据。
1.2 dshow支撑的参数
ffmpeg -h demuxer=dshow
输出成果大致如下所示。
dshow indev AVOptions:
-video_size <image_size> set video size given a string such as 640x480 or hd720.
-pixel_format <pix_fmt> set video pixel format (default none)
-framerate <string> set video frame rate
-list_devices <boolean> list available devices (default false)
-list_options <boolean> list available options for specified device (default false)
- -video_size:分辩率
- -pixel_format:像素格局
- -framerate:帧率(每秒收集多少帧画面)
- -list_devices:true表明列出dshow支撑的一切设备
- -list_options:true表明列出特定设备支撑的一切参数
1.3 摄像头支撑的参数
ffmpeg -f dshow -list_options true -i video="Integrated Camera"
输出成果大致如下所示。
DirectShow video device options (from video devices)
Pin "捕获" (alternative pin name "捕获")
vcodec=mjpeg min s=1280x720 fps=30 max s=1280x720 fps=30
vcodec=mjpeg min s=320x180 fps=30 max s=320x180 fps=30
vcodec=mjpeg min s=320x240 fps=30 max s=320x240 fps=30
vcodec=mjpeg min s=352x288 fps=30 max s=352x288 fps=30
vcodec=mjpeg min s=424x240 fps=30 max s=424x240 fps=30
vcodec=mjpeg min s=640x360 fps=30 max s=640x360 fps=30
vcodec=mjpeg min s=640x480 fps=30 max s=640x480 fps=30
vcodec=mjpeg min s=848x480 fps=30 max s=848x480 fps=30
vcodec=mjpeg min s=960x540 fps=30 max s=960x540 fps=30
pixel_format=yuyv422 min s=1280x720 fps=10 max s=1280x720 fps=10
pixel_format=bgr24 min s=1280x720 fps=10 max s=1280x720 fps=10
pixel_format=yuyv422 min s=320x180 fps=30 max s=320x180 fps=30
pixel_format=bgr24 min s=320x180 fps=30 max s=320x180 fps=30
pixel_format=yuyv422 min s=320x240 fps=30 max s=320x240 fps=30
pixel_format=bgr24 min s=320x240 fps=30 max s=320x240 fps=30
pixel_format=yuyv422 min s=352x288 fps=30 max s=352x288 fps=30
pixel_format=bgr24 min s=352x288 fps=30 max s=352x288 fps=30
pixel_format=yuyv422 min s=424x240 fps=30 max s=424x240 fps=30
pixel_format=bgr24 min s=424x240 fps=30 max s=424x240 fps=30
pixel_format=yuyv422 min s=640x360 fps=30 max s=640x360 fps=30
pixel_format=bgr24 min s=640x360 fps=30 max s=640x360 fps=30
pixel_format=yuyv422 min s=640x480 fps=30 max s=640x480 fps=30
pixel_format=bgr24 min s=640x480 fps=30 max s=640x480 fps=30
pixel_format=yuyv422 min s=848x480 fps=20 max s=848x480 fps=20
pixel_format=bgr24 min s=848x480 fps=20 max s=848x480 fps=20
pixel_format=yuyv422 min s=960x540 fps=15 max s=960x540 fps=15
pixel_format=bgr24 min s=960x540 fps=15 max s=960x540 fps=15
能够看到摄像头支撑的分辩率、像素格局、帧率等参数。
1.4 录制
ffmpeg -f dshow -i video="Integrated Camera" out.yuv
输出成果大致如下所示。
Input #0, dshow, from 'video=Integrated Camera':
Stream #0:0: Video: mjpeg, yuvj422p, 1280x720, 30 fps
Output #0, rawvideo, to 'out.yuv':
Stream #0:0: Video: rawvideo, yuvj422p, 1280x720, 30 fps
根据输出成果,不难看出:
-
从摄像头收集的数据,终究存成了YUV格局
-
摄像头的默许参数
- 分辩率:1280×720
- 像素格局:yuvj422p
- 帧率:30fps
所以,播映YUV时的指令如下所示。
- 需要留意的是:YUV文件中只存储了图画信息,并没有声响信息
- 因而,播映YUV时是听不到任何声响的
- ffplay的framerate默许值是25
ffplay -video_size 1280x720 -pixel_format yuvj422p -framerate 30 out.yuv
能够自界说参数进行录制。
ffmpeg -f dshow -video_size 640x480 -pixel_format yuyv422 -framerate 30 -i video="Integrated Camera" out.yuv
播映录制好的YUV。
ffplay -video_size 640x480 -pixel_format yuyv422 -framerate 30 out.yuv
2. Mac
2.1 avfoundation支撑的设备
ffmpeg -f avfoundation -list_devices true -i ''
输出成果大致如下所示。
AVFoundation video devices:
[0] FaceTime高清摄像头(内建)
[1] Capture screen 0
AVFoundation audio devices:
[0] Soundflower (64ch)
[1] Edu Audio Device
[2] MacBook Pro麦克风
[3] Soundflower (2ch)
0号设备便是Mac自带的摄像头。
2.2 avfoundation支撑的参数
ffmpeg -h demuxer=avfoundation
输出成果大致如下所示。
AVFoundation indev AVOptions:
-list_devices <boolean> .D........ list available devices (default false)
-pixel_format <pix_fmt> .D........ set pixel format (default yuv420p)
-framerate <video_rate> .D........ set frame rate (default "ntsc")
-video_size <image_size> .D........ set video size
- -video_size:分辩率
-
-pixel_format:像素格局
- 默许是yuv420p
-
-framerate:帧率(每秒收集多少帧画面)
- 默许是ntsc,也便是30000/1001,约等于29.970030
- -list_devices:true表明列出avfoundation支撑的一切设备
2.3 录制
# 运用0号视频设备
ffmpeg -f avfoundation -i 0 out.yuv
然后你可能会遇到一个过错:这个设备(摄像头)不支撑29.970030的帧率。
Selected framerate (29.970030) is not supported by the device
从头设置个30的帧率试试。
ffmpeg -f avfoundation -framerate 30 -i 0 out.yuv
然后你会看到以下提示信息。
- 这个设备(摄像头)不支撑yuv420p
- 只支撑uyvy422、yuyv422、nv12、0rgb、bgr0
- 并且自动挑选运用uyvy422来替代yuv420p
Selected pixel format (yuv420p) is not supported by the input device.
Supported pixel formats:
uyvy422
yuyv422
nv12
0rgb
bgr0
Overriding selected pixel format to use uyvy422 instead.
与此同时,也成功开端收集摄像头的视频数据了。
- 像素格局:uyvy422
- 分辩率:1280×720
- 帧率:30
Input #0, avfoundation, from '0':
Stream #0:0: Video: rawvideo, uyvy422, 1280x720
Output #0, rawvideo, to 'out.yuv':
Stream #0:0: Video: rawvideo, uyvy422, 1280x720, 30 fps
播映录制好的YUV。
ffplay -video_size 1280x720 -pixel_format uyvy422 -framerate 30 out.yuv
五、视频录制02_编程
本文的主要内容:演示怎么经过编程收集摄像头的视频数据。 全体的流程跟《音频录制02_编程》相似。
1. 依靠库
需要依靠4个库。
extern "C" {
#include <libavdevice/avdevice.h>
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
#include <libavutil/imgutils.h>
#include <libavcodec/avcodec.h>
}
2. 宏界说
#ifdef Q_OS_WIN
// 格局称号
#define FMT_NAME "dshow"
// 设备称号
#define DEVICE_NAME "video=Integrated Camera"
// YUV文件名
#define FILENAME "F:/out.yuv"
#else
#define FMT_NAME "avfoundation"
#define DEVICE_NAME "0"
#define FILENAME "/Users/mj/Desktop/out.yuv"
#endif
#define ERROR_BUF(ret) \
char errbuf[1024]; \
av_strerror(ret, errbuf, sizeof (errbuf));
3. 权限申请
在Mac渠道,有2个留意点:
- 需要在Info.plist中添加摄像头的运用阐明,申请摄像头的运用权限
- 运用Debug方式运转程序
- 否则会出现闪退的状况
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSCameraUsageDescription</key>
<string>运用摄像头收集您的靓照</string>
</dict>
</plist>
4. 注册设备
在整个程序的运转进程中,只需要履行1次注册设备的代码。
// 初始化libavdevice并注册一切输入和输出设备
avdevice_register_all();
5. 获取输入格局目标
// 获取输入格局目标
AVInputFormat *fmt = av_find_input_format(FMT_NAME);
if (!fmt) {
qDebug() << "av_find_input_format error" << FMT_NAME;
return;
}
6. 翻开输入设备
// 格局上下文
AVFormatContext *ctx = nullptr;
// 传递给输入设备的参数
AVDictionary *options = nullptr;
av_dict_set(&options, "video_size", "640x480", 0);
av_dict_set(&options, "pixel_format", "yuyv422", 0);
av_dict_set(&options, "framerate", "30", 0);
// 翻开输入设备
int ret = avformat_open_input(&ctx, DEVICE_NAME, fmt, &options);
if (ret < 0) {
ERROR_BUF(ret);
qDebug() << "avformat_open_input error" << errbuf;
return;
}
7. 翻开输出文件
// 翻开文件
QFile file(FILENAME);
if (!file.open(QFile::WriteOnly)) {
qDebug() << "file open error" << FILENAME;
// 封闭输入设备
avformat_close_input(&ctx);
return;
}
8. 收集视频数据
// 计算每一帧的巨细
AVCodecParameters *params = ctx->streams[0]->codecpar;
int imageSize = av_image_get_buffer_size(
(AVPixelFormat) params->format,
params->width, params->height,
1);
// 数据包
AVPacket *pkt = av_packet_alloc();
while (!isInterruptionRequested()) {
// 不断收集数据
ret = av_read_frame(ctx, pkt);
if (ret == 0) { // 读取成功
// 将数据写入文件
file.write((const char *) pkt->data, imageSize);
/*
这里要运用imageSize,而不是pkt->size。
pkt->size有可能比imageSize大(比方在Mac渠道),
运用pkt->size会导致写入一些多余数据到YUV文件中,
进而导致YUV内容无法正常播映
*/
// 开释资源
av_packet_unref(pkt);
} else if (ret == AVERROR(EAGAIN)) { // 资源暂时不可用
continue;
} else { // 其他过错
ERROR_BUF(ret);
qDebug() << "av_read_frame error" << errbuf;
break;
}
}
9. 开释资源
// 开释资源
av_packet_free(&pkt);
// 封闭文件
file.close();
// 封闭设备
avformat_close_input(&ctx);
六、显现BMP图片
文本的主要内容是:运用SDL显现一张BMP图片,算是为后边的《显现YUV图片》做准备。
为什么是显现BMP图片?而不是显现JPG或PNG图片?
- 由于SDL内置了加载BMP的API,运用起来会愈加简略,便于初学者学习运用SDL
- 假如想要轻松加载JPG、PNG等其他格局的图片,能够运用第三方库:SDL_image
1. 宏界说
#include <SDL2/SDL.h>
#include <QDebug>
// 出错了就履行goto end
#define END(judge, func) \
if (judge) { \
qDebug() << #func << "Error" << SDL_GetError(); \
goto end; \
}
2. 变量界说
// 窗口
SDL_Window *window = nullptr;
// 烘托上下文
SDL_Renderer *renderer = nullptr;
// 像素数据
SDL_Surface *surface = nullptr;
// 纹路(直接跟特定驱动程序相关的像素数据)
SDL_Texture *texture = nullptr;
3. 初始化子系统
// 初始化Video子系统
END(SDL_Init(SDL_INIT_VIDEO), SDL_Init);
4. 加载BMP
// 加载BMP
surface = SDL_LoadBMP("F:/in.bmp");
END(!surface, SDL_LoadBMP);
5. 创立窗口
// 创立窗口
window = SDL_CreateWindow(
// 窗口标题
"SDL显现BMP图片",
// 窗口X(未界说)
SDL_WINDOWPOS_UNDEFINED,
// 窗口Y(未界说)
SDL_WINDOWPOS_UNDEFINED,
// 窗口宽度(跟图片宽度相同)
surface->w,
// 窗口高度(跟图片高度相同)
surface->h,
// 显现窗口
SDL_WINDOW_SHOWN
);
END(!window, SDL_CreateWindow);
6. 创立烘托上下文
// 创立烘托上下文(默许的烘托方针是window)
renderer = SDL_CreateRenderer(window, -1,
SDL_RENDERER_ACCELERATED |
SDL_RENDERER_PRESENTVSYNC);
if (!renderer) { // 阐明敞开硬件加速失利
renderer = SDL_CreateRenderer(window, -1, 0);
}
END(!renderer, SDL_CreateRenderer);
7. 创立纹路
// 创立纹路
texture = SDL_CreateTextureFromSurface(
renderer,
surface);
END(!texture, SDL_CreateTextureFromSurface);
8. 烘托
// 设置制作色彩(这里随意设置了一个色彩:黄色)
END(SDL_SetRenderDrawColor(renderer,
255, 255, 0,
SDL_ALPHA_OPAQUE),
SDL_SetRenderDrawColor);
// 用DrawColor清除烘托方针
END(SDL_RenderClear(renderer),
SDL_RenderClear);
// 复制纹路到烘托方针上
END(SDL_RenderCopy(renderer, texture, nullptr, nullptr),
SDL_RenderCopy);
// 将此前的一切需要烘托的内容更新到屏幕上
SDL_RenderPresent(renderer);
9. 推迟退出
// 推迟3秒退出
SDL_Delay(3000);
10. 开释资源
end:
// 开释资源
SDL_FreeSurface(surface);
SDL_DestroyTexture(texture);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
七、显现YUV图片
文本的主要内容是:运用SDL显现一张YUV图片,全体进程跟《显现BMP图片》比较像。
1. 宏界说
#include <SDL2/SDL.h>
#include <QDebug>
#define END(judge, func) \
if (judge) { \
qDebug() << #func << "error" << SDL_GetError(); \
goto end; \
}
#define FILENAME "F:/res/in.yuv"
#define PIXEL_FORMAT SDL_PIXELFORMAT_IYUV
#define IMG_W 512
#define IMG_H 512
2. 变量界说
// 窗口
SDL_Window *window = nullptr;
// 烘托上下文
SDL_Renderer *renderer = nullptr;
// 纹路(直接跟特定驱动程序相关的像素数据)
SDL_Texture *texture = nullptr;
// 文件
QFile file(FILENAME);
3. 初始化子系统
// 初始化Video子系统
END(SDL_Init(SDL_INIT_VIDEO), SDL_Init);
4. 创立窗口
// 创立窗口
window = SDL_CreateWindow(
// 窗口标题
"SDL显现YUV图片",
// 窗口X(未界说)
SDL_WINDOWPOS_UNDEFINED,
// 窗口Y(未界说)
SDL_WINDOWPOS_UNDEFINED,
// 窗口宽度(跟图片宽度相同)
surface->w,
// 窗口高度(跟图片高度相同)
surface->h,
// 显现窗口
SDL_WINDOW_SHOWN
);
END(!window, SDL_CreateWindow);
5. 创立烘托上下文
// 创立烘托上下文(默许的烘托方针是window)
renderer = SDL_CreateRenderer(window, -1,
SDL_RENDERER_ACCELERATED |
SDL_RENDERER_PRESENTVSYNC);
if (!renderer) { // 阐明敞开硬件加速失利
renderer = SDL_CreateRenderer(window, -1, 0);
}
END(!renderer, SDL_CreateRenderer);
6. 创立纹路
// 创立纹路
texture = SDL_CreateTexture(renderer,
PIXEL_FORMAT,
SDL_TEXTUREACCESS_STREAMING,
IMG_W, IMG_H);
END(!texture, SDL_CreateTexture);
7. 翻开文件
// 翻开文件
if (!file.open(QFile::ReadOnly)) {
qDebug() << "file open error" << FILENAME;
goto end;
}
8. 烘托
// 将YUV的像素数据填充到texture
END(SDL_UpdateTexture(texture, nullptr, file.readAll().data(), IMG_W),
SDL_UpdateTexture);
// 设置制作色彩(画笔色彩)
END(SDL_SetRenderDrawColor(renderer,
0, 0, 0, SDL_ALPHA_OPAQUE),
SDL_SetRenderDrawColor);
// 用制作色彩(画笔色彩)清除烘托方针
END(SDL_RenderClear(renderer),
SDL_RenderClear);
// 拷贝纹路数据到烘托方针(默许是window)
END(SDL_RenderCopy(renderer, texture, nullptr, nullptr),
SDL_RenderCopy);
// 更新一切的烘托操作到屏幕上
SDL_RenderPresent(renderer);
9. 推迟退出
// 推迟3秒退出
SDL_Delay(3000);
10. 开释资源
end:
file.close();
SDL_DestroyTexture(texture);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
专题系列文章
1. 前常识
- 01-探求iOS底层原理|综述
- 02-探求iOS底层原理|编译器LLVM项目【Clang、SwiftC、优化器、LLVM】
- 03-探求iOS底层原理|LLDB
- 04-探求iOS底层原理|ARM64汇编
2. 根据OC言语探求iOS底层原理
- 05-探求iOS底层原理|OC的实质
- 06-探求iOS底层原理|OC目标的实质
- 07-探求iOS底层原理|几种OC目标【实例目标、类目标、元类】、目标的isa指针、superclass、目标的办法调用、Class的底层实质
- 08-探求iOS底层原理|Category底层结构、App启动时Class与Category装载进程、load 和 initialize 履行、相关目标
- 09-探求iOS底层原理|KVO
- 10-探求iOS底层原理|KVC
- 11-探求iOS底层原理|探求Block的实质|【Block的数据类型(实质)与内存布局、变量捕获、Block的种类、内存办理、Block的修饰符、循环引证】
- 12-探求iOS底层原理|Runtime1【isa详解、class的结构、办法缓存cache_t】
- 13-探求iOS底层原理|Runtime2【音讯处理(发送、转发)&&动态办法解析、super的实质】
- 14-探求iOS底层原理|Runtime3【Runtime的相关应用】
- 15-探求iOS底层原理|RunLoop【两种RunloopMode、RunLoopMode中的Source0、Source1、Timer、Observer】
- 16-探求iOS底层原理|RunLoop的应用
- 17-探求iOS底层原理|多线程技能的底层原理【GCD源码剖析1:主行列、串行行列&&并行行列、全局并发行列】
- 18-探求iOS底层原理|多线程技能【GCD源码剖析1:dispatch_get_global_queue与dispatch_(a)sync、单例、线程死锁】
- 19-探求iOS底层原理|多线程技能【GCD源码剖析2:栅栏函数dispatch_barrier_(a)sync、信号量dispatch_semaphore】
- 20-探求iOS底层原理|多线程技能【GCD源码剖析3:线程调度组dispatch_group、事情源dispatch Source】
- 21-探求iOS底层原理|多线程技能【线程锁:自旋锁、互斥锁、递归锁】
- 22-探求iOS底层原理|多线程技能【原子锁atomic、gcd Timer、NSTimer、CADisplayLink】
- 23-探求iOS底层原理|内存办理【Mach-O文件、Tagged Pointer、目标的内存办理、copy、引证计数、weak指针、autorelease
3. 根据Swift言语探求iOS底层原理
关于函数
、枚举
、可选项
、结构体
、类
、闭包
、特点
、办法
、swift多态原理
、String
、Array
、Dictionary
、引证计数
、MetaData
等Swift根本语法和相关的底层原理文章有如下几篇:
- 01-Swift5常用中心语法|了解Swift【Swift简介、Swift的版别、Swift编译原理】
- 02-Swift5常用中心语法|根底语法【Playground、常量与变量、常见数据类型、字面量、元组、流程操控、函数、枚举、可选项、guard语句、区间】
- 03-Swift5常用中心语法|面向目标【闭包、结构体、类、枚举】
- 04-Swift5常用中心语法|面向目标【特点、inout、类型特点、单例方式、办法、下标、承继、初始化】
- 05-Swift5常用中心语法|高档语法【可选链、协议、过错处理、泛型、String与Array、高档运算符、扩展、访问操控、内存办理、字面量、方式匹配】
- 06-Swift5常用中心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、呼应式编程、Swift源码剖析】
4. C++中心语法
- 01-C++中心语法|C++概述【C++简介、C++起源、可移植性和标准、为什么C++会成功、从一个简略的程序开端知道C++】
- 02-C++中心语法|C++对C的扩展【::作用域运算符、名字操控、struct类型加强、C/C++中的const、引证(reference)、函数】
- 03-C++中心语法|面向目标1【 C++编程标准、类和目标、面向目标程序设计事例、目标的结构和析构、C++面向目标模型初探】
- 04-C++中心语法|面向目标2【友元、内部类与局部类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转化、 C++标准、过错&&异常、智能指针】
- 05-C++中心语法|面向目标3【 承继和派生、多态、静态成员、const成员、引证类型成员、VS的内存窗口】
5. Vue全家桶
- 01-Vue全家桶中心常识|Vue根底【Vue概述、Vue根本运用、Vue模板语法、根底事例、Vue常用特性、归纳事例】
- 02-Vue全家桶中心常识|Vue常用特性【表单操作、自界说指令、计算特点、侦听器、过滤器、生命周期、归纳事例】
- 03-Vue全家桶中心常识|组件化开发【组件化开发思维、组件注册、Vue调试东西用法、组件间数据交互、组件插槽、根据组件的
- 04-Vue全家桶中心常识|多线程与网络【前后端交互方式、promise用法、fetch、axios、归纳事例】
- 05-Vue全家桶中心常识|Vue Router【根本运用、嵌套路由、动态路由匹配、命名路由、编程式导航、根据vue-router的事例】
- 06-Vue全家桶中心常识|前端工程化【模块化相关标准、webpack、Vue 单文件组件、Vue 脚手架、Element-UI 的根本运用】
- 07-Vue全家桶中心常识|Vuex【Vuex的根本运用、Vuex中的中心特性、vuex事例】
6. 音视频技能中心常识
- 01-音视频技能中心常识|了解音频技能【移动通讯技能的开展、声响的实质、深化了解音频】
- 02-音视频技能中心常识|建立开发环境【FFmpeg与Qt、Windows开发环境建立、Mac开发环境建立、Qt开发根底】
- 03-音视频技能中心常识|Qt开发根底【
.pro
文件的装备、Qt控件根底、信号与槽】 - 04-音视频技能中心常识|音频录制【指令行、C++编程】
- 05-音视频技能中心常识|音频播映【播映PCM、WAV、PCM转WAV、PCM转WAV、播映WAV】
- 06-音视频技能中心常识|音频重采样【音频重采样简介、用指令行进行重采样、经过编程重采样】
- 07-音视频技能中心常识|AAC编码【AAC编码器解码器、编译FFmpeg、AAC编码实战、AAC解码实战】
- 08-音视频技能中心常识|成像技能【重识图片、详解YUV、视频录制、显现BMP图片、显现YUV图片】
- 09-音视频技能中心常识|视频编码解码【了解H.264编码、H.264编码、H.264编码解码】
- 10-音视频技能中心常识|RTMP服务器建立【流媒体、服务器环境】
其它底层原理专题
1. 底层原理相关专题
- 01-计算机原理|计算机图形烘托原理这篇文章
- 02-计算机原理|移动终端屏幕成像与卡顿
2. iOS相关专题
- 01-iOS底层原理|iOS的各个烘托框架以及iOS图层烘托原理
- 02-iOS底层原理|iOS动画烘托原理
- 03-iOS底层原理|iOS OffScreen Rendering 离屏烘托原理
- 04-iOS底层原理|因CPU、GPU资源消耗导致卡顿的原因和解决计划
3. webApp相关专题
- 01-Web和类RN大前端的烘托原理
4. 跨渠道开发计划相关专题
- 01-Flutter页面烘托原理
5. 阶段性总结:Native、WebApp、跨渠道开发三种计划功能比较
- 01-Native、WebApp、跨渠道开发三种计划功能比较
6. Android、HarmonyOS页面烘托专题
- 01-Android页面烘托原理
- 02-HarmonyOS页面烘托原理 (
待输出
)
7. 小程序页面烘托专题
- 01-小程序框架烘托原理