像素是图像中的一个个点
图像的分辨率指图像的大小尺寸,一般我们用像素个数表示图像的尺寸。
如720P: 1280*720
; 1080P: 1920*1080
; 4K: 3840 * 2160
; 8K: 7680 * 4320
同一张图,用不同分辨率表示,直观感受不同:1*1
只能看到像素;10*10
是模糊的,100*100
有了形状,1000*1000
细节更清晰.
总体上: 分辨率越高, 越清晰(但注意: 通过插值算法脑补的图像,分辨率越高会越模糊)
8位深图像: 是指每个像素由RGB三通道组成, RGB各占8位(1个像素共占24位即3byte), 8位可表示256个颜色, 则共可表示256^3=1677万种颜色
位深越大,能表示的颜色值越多,能更清晰展示真实世界,但每个像素占用的位数更多,存储空间更大,网络传输的流量越多
跨距,是图像存储时,内存每行像素占用的空间,(即包括了内存对齐).
一般读图时一行行读,不会一列列读,故只有行的stride.
为了快速读一行像素,一般会对图像内存对齐(如16byte对齐,因一般芯片一次性读取16byte性能最高,也有32byte,甚至64byte的)
例如一张RGB图,分辨率为1278*720
,若存储一行需1278*3=3834
byte,但因3834无法整除16,故填6byte得到3840byte,即此图的stride为3840byte.
存储按stride=3840存,读图时跳过尾部填充像素
即可,否则若将第N行的尾部无效填充像素
视为第N+1行的头部有效像素
的话,就会花屏
:即屏幕出现N条斜线.
所以,无论读图,渲染图,存储图,均需要设置正确的stride.
帧率: 1秒内的图像数量, (视频其实是N帧图像组成),电影院帧率为24fps(帧/秒),监控行业25fps,声网有15,24,30fps.
帧率越高
因视频每秒有很多图像,一般先压缩后存储,以减少磁盘占用.
但压缩后的视频,如何衡量其大小,(因为有静态视频文件,和流媒体视频),故一般通过码率
衡量.
码率:1s内数据量的大小,如Kb/s或Mb/s.
文件大小/文件时长
计算,(因为虽然文件包括视频与音频,但音频码率相比视频小得多,可忽略)视频背景特别简单
和特别复杂
的情况下编码出的码率是不同的.视频背景特别简单
和特别复杂
的情况下,编码器对复杂背景压缩的狠,而简单背景不压缩opencv一般用BGR方式存图
因为RGB三色有相关性,不方便压缩,故视频领域一般用YUV
Y是亮度(包含了图像总体的轮廓),UV是色度(分为U分量和V分量),若一张图只有Y而无UV则为黑白图(若用RGB表示图像则黑白电视机无法播放,因为RGB三个通道都是彩色的)
有YUV4:4:4,YUV4:2:2,YUV4:2:0这些常见类型,其中YUV4:2:0最常用.主要区别是U和V分量像素点的个数和采集方式
YUV4:4:4是1个Y有1组UV
YUV4:2:2是2个Y共用1组UV
YUV4:2:0是4个Y共用1组UV
存储格式有Planar和Packed两种
Planar是先连续存所有像素点的Y,在存所有像素的的U,再存所有像素点的V
Packed是先存所以后像素点的Y,再U,V连续存储
4*2
像素的图像,的YUV4:4:4按如下存储
存储大小,和RGB相同,均为每个像素占3byte
2个Y公用1组UV, 有以下4种存储方式
均以4*2
像素的为例,RGB需24byte,而YUV4:2:2仅需16byte
即若为8bit位深,则RGB每个像素需3byte,而YUV4:2:2仅需2byte
Planar格式,先存Y,再U,再V
Planar格式,先存Y,再存V,再存U
Packed格式,先存Y,再UV连续交错存
Packed格式,先存Y,再VU连续交错存储
最常见,每上下左右4个像素点共用1组UV,如下均以4*4
像素的图像为例,4*4
的GRB图需48byte,而YUV4:2:0仅需24byte,存储空间仅为一半
即若8bit位深,则RGB每个像素需3byte,YUV4:2:0仅需1.5byte
Planar格式,先存Y,再存U,再存V
Packed格式,先存Y,再UV连续交错存储
Packed格式,先存Y,再VU连续交错存储
原图
和给显示器渲染的最终图
像均为RGB,但视频用YUV编码,故需转换.
FullRange的RGB取值范围均为0255,而LimitedRange的RGB取值范围均为16235.
BT709(高清)和BT601(标清)标准定义了RGB和YUV互转的规范.
YUV存储比GRB小,但会有一些色度失真(但人不敏感),故还是优点大于缺点
RGB是有相关性的, 相关性是指一幅图像在RGB格式的时候,将R、G、B三个通道分离开来当作图像来看的话,R、G、B三张图像内容几乎是一样的,只是颜色不同而已。具有相关性,
如果拿来编码的话,三张图像同等重要,而且轮廓还差不多,但颜色又不同,因此不好编码。而YUV不同,YUV中只有Y是图像的大体轮廓,没有颜色信息。U、V是颜色信息。三张图像相互独立。并且人眼对于色彩信息相比图像的轮廓信息不敏感些。我们可以缩小U、V的大小,比如YUV420中U、V只有Y的1/4大小,本身就相比于RGB图像小了一半。然后我们编码的时候Y、U、V相关性很小,可以独立编码,也很方便。
有3种缩放场景
原始分辨率
和播放窗口的大小
不一致,就需要缩放.
大多数缩放,通过插值算法
实现,即根据周围已有的像素值+通过加权运算得到插值像素值
,有最近邻插值算法,双线性插值算法,双三次插值算法等.
例如从720P放大到1080P,乘宽均放大1080/720=1.5倍
,目标图的(1,1)
是原图的(0.67,0.67)
,而后者可通过对原图插值得到
例如720P缩小到360P,长宽均缩小2倍,则目标图的(1,1)
是原图的(2,2)
即若原图分辨率为w0*h0
,需缩放到w1*h1
,则将目标图像中(x,y)值
映射到(x*w0/w1,y*h0/h1)
,在插值得到此像素值
缩放和插值在RGB和YUV颜色空间均可工作,下文不会区分
取周围4个像素中最近的一个, 即最近的像素点权重为1,其余3个权重为0
缺点:相邻的插值像素很大概率相同,如(1,0)
和(2,0)
的像素值相同
在两个水平方向线性插值的到m和n,再将W和H做垂直方向第三次插值得到p
即各像素以距离为权重,距离越近权重越大(即取距另一点(即距较远点的距离)
为比例的分子
),距离越远权重越小(即取距另一点(即距较近点距离)
为比例的分子
)
取周围16个像素点的加权平均
基于BiCubic基函数
(如下)
例如对P(1.33,1.33)
,对周围16个点求出16个像素值*水平权重*垂直权重
,然后将16个值取算数平均
.
如下例分别对(0,0)
,(1,2)
,(3,3)
的水平权重和垂直权重如下
若原图中的边角像素太小(如0.5,0.5
)直接用边角像素即可,不需要再插值了
可节省存储,如1080P的电影,视频中的图常用YUV存储,帧率25fps,时长2h,若不压缩则需(1920*1080)*1.5的YUVbit*25fps*(2*3600s)
=260GB
图像分为一个个宏块
来编码,一般为16*16(H264,VP8)
,32*32(H265,Vp9)
,64*64(H265,VP9,AV1)
,128*128(AV1)
图像有数据冗余,包括如下4种
宏块
的相似对YUV图像,以H264为例,划分为若干16*16
的宏块,Y,U,V分量的大小分别为16*16
,8*8
,8*8
,只对Y分量分析,若Y分量的16*16
个像素是一个个数字,则从左上角开始之字形扫描个像素值,则可得像素串,如下
因为aaaabbbccccc
可通过行程编码
压缩为4a3c5b
而abcdabcdabcd
可通过行程编码
压缩为1a1b1c1d1a1b1c1d1a1b1c1d
,为了避免此情况,需找到连续像素串
,尤其最好是一串数字很小的像素串
(如一串0,因为0仅需1bit存储)
那么如何将这个像素串
变为有很多0的像素串
呢?
一般将编码块
的左边块
,上边块
,左上角块
,右上角块
,通过将这些与编码块相邻的块
通过算法
得到预测块
,然后将编码块
与预测块
做diff得到残差块
,这样残差块
中各像素绝对值更小(更接近0),存储占用更小.
帧间预测是指,预测块
所在的帧称为参考帧
,预测块在参考帧的坐标值(x0,y0)
,与编码块在编码帧的坐标值(x1,y1)
,的差值(x0-x1,y0-y1)为运动矢量
的残差块
.在参考帧中找预测块的过程称为运动搜索
.
总之, 通过时空冗余信息移除,得到的像素值更小,做行程编码更容易有高压缩率.
I帧只能镇内预测(因为I帧需要能自己独立编解码,若用帧间预测就有依赖了);B帧和P帧即可帧内也可帧间预测.
为了分离图像的高频和低频信息,需将图像变换到频域,常用DCT变换(即离散余弦变换).在H264中,一个宏块MB
为16*16
(从图像的从左到右,从上到下,),我们常将其划分为16个4*4
的块,然后对每个4*4
的块做DCT变换得到相应的4*4的变换块
.
即滤波器组由若干滤波器组成,各滤波器大小相同(如3*3,5*5或7*7
的大小),各滤波器内部内容不同,左上角为低通(留下低频)滤波器,其余为高通(留下高频)滤波器(占大部分).
滤波后可仅留下低频或仅留下高频信息,的结果如下图
其中低通滤波器结果如下
其中高通滤波器结果如下
从人对原图的直观感受来看: 图像灰度变化越大的像素点,越高频(如轮廓);反之越低频(如目标内部).
低频表示图像总体样貌(一般值较大),高频表示图像中人或物的轮廓等变化剧烈之处(高频系数数量较多,但数值一般较小).下图黄色为低频,绿色为高频
量化:是指可去除大部分高频信息(即置为0),在不影响人观感的情况下,得到一连串0像素.
将变换块
的系数同时除以一个值,此值即为量化步长
(即编码器内部概念QStep
或与之可映射的量化参数QP
),得到的结果为量化后的系数
.QStep越大,量化后的系数越小.且相同的QStep值,高频系数比低频系数值更小,使更容易变为0. 这样可使大部分高频系数变为0,如下图
解码时,我们会将QStep
乘以量化后的系数
,得到变换系数(即经过量化算出的原图值)
;很明显变换系数
和原图未经量化的变换系数
不同,即有损编码. 其中QStep越大则损失越大. 而因为QStep和QP有映射, 即从编码器应用的角度看,QP越大,损失越大,画面清晰度越低;同时QP越大,被量化成0的概率越大,编码后的码流就越小,压缩就越高.
目标是在熵编码
时压缩率更高,希望输入到熵编码(以行程编码为例)
的像素串,是一串有很多0,且最好连续为0的像素串.
为了达到此目标
编码块
小很多的残差块
.熵编码
可把图像压缩为较小的数据.编码标准:H264,VP8是目前主流;H265,VP9是下一代标准,AV1是VP9的下一代标准.
其中H264,H265需专利费,而VP8,VP9,AV1免费
普通产品用H264最多,WebRTC用VP8多(也支持VP9,AV1)
标准越新,编码块越大,块划分方式越多,编码模式越多,压缩效率越高,但编码耗时越大.
目前支持H264,H265的硬件非常多了,AV1才刚开始
若在较差的机器编码,可用H264或VP8等速度快的编码器
若在较新机器可选H265;但H265需专利费且browser不原生支持H265,故不建议选择H265,而建议选择VP9,甚至AV1.
一般用VQAnalyzer工具分析H264字段
其实就是看编码后的二进制数据
怎么组织,即哪一块是一帧图像,哪一块是另一帧图像,每帧图像开头结尾是什么.
帧间预测,依赖于其他帧.
如下图,从左向右,第一个B帧
参考第一个I帧
和第一个P帧
,第一个P帧
只参考第一个I帧
因为P帧或B帧
需要参考其他帧
,若编码中有一个参考帧
出现错误的话,则依赖它的P帧和B帧也会出错,而这些有问题的P帧(B帧实际情况作为参考帧较少,暂不讨论)又会作为之后P帧或B帧的参考帧
,即错误会不断传递,为避免这种情况,有特殊的I帧:IDR帧,也叫立即刷新帧
.
因H264规定,IDR帧之后的帧,不能再参考IDR帧之前的帧
,故若I帧正确则可截断编码错误的传递,使之后的帧可正常编解码.
I帧分为普通I帧
和IDR帧
,但实际多数情况均用IDR帧.
GOP(图像组):从一个IDR帧到下一个IDR帧之间(此距离称为关键帧间隔
),包含的IDR帧,普通I帧,P帧,B帧
GOP若太大,可能会在RTC场景因网络丢包而使接收端丢帧,使丢失IDR帧而解码错误,引起长时间花屏/卡顿.
GOP若太小,码率会变大.
一般RTC场景设置大,而点播场景设置小.
将一帧图像分为几个slice(每个slice分为若干16*16MB的宏块)(每个宏块可细分为不同大小的子块),各slice间独立不依赖,可并行对多个slice同时编码.
有Annexb和MP4两种
00 00 00 01
或3byte的00 00 01
为起始码
;00 00 00
改为00 00 03 00
00 00 01
改为00 00 03 01
00 00 02
改为00 00 03 02
00 00 03
改为00 00 03 03
起始码
冲突.两者码流格式差别不大,下文已Annexb格式来讲解H264码流中的NALU
视频码流中,除了I,P,B帧(以Slice形式呈现)之外,还有通用的编码参数,分为SPS序数参数集
(含图像宽,高,YUV格式,位深)和PPS图像参数集
(含熵编码类型,基础QP,最大参考帧数量)两种.
故H264码流如下
H264有NALU(网络抽象层单元),SPS是一个NALU,PPS是一个NALU,每个Slice也是一个NALU.每个NALU由1byte的NALU Header和若干byte的NALU Data(又由Slice Header和Slice Data(又由若干MB Data组成)组成)组成.
NALU Header共1byte,如下
F:1bit,必须为0
NRI:2bit,可取00~11,表示当前NALU的重要性,IDR,SPS,PPS必须>0
Type:5bit,表示NALU的类型,仅区分了IDR与非IDR帧
具体的I或B或P帧类型,在Slice Header中的Slice Type可解析得到.
下面是实际码流例子
Slice Heder
的first_mb_in_slice
表示当前Slice的第一个宏块MB在当前编码图像中的序号
当前Slice的第一个宏块
是一帧的第一个宏块
,即当前Slice是一帧的第一个Slice
此Slice不是一帧的第一个Slice
,继续用同样方式向后找,知道=0
为止就代表新的一帧开始了.SliceHeader[0] & 0x80 == 1
,判断first_mb_in_slice==0
编码端需要通过设置分辨率,告诉编码器:图像的宽高
解码器部不需要设置分辨率,可从SPS中计算得出如下指标
并按如下公式算出分辨率
PPS中有全局基础QP(字段是pic_init_qu_minux26
),当前序列中所有依赖该PPS的Slice共用此基础QP,各Slice在此基础上用Slice Header中的slice_qp_delta字段
调整偏移值. H264允许在宏块级别对QP做进一步精细化调节.
帧内,相邻像素的亮度色度相近,且是逐渐变化而不会突变,故有空间相关性.
我们通过已编码的像素值
解码重建成重建像素
来做参考像素,再去预测待编码的像素值
H264的宏块为16*16
,YUV4:2:0的亮度块为16*16
(可继续划分为16个4*4的子块
), 色度块为8*8
,亮度块和色度快是分开独立预测的(即亮度块参考已编码亮度块的像素,色度块参考已编码色度块的像素).
所以实际帧内预测时,分为如下3种:4*4
亮度块预测,16*16
亮度块预测,8*8
色度块预测
有8种方向模式和1种DC模式
Vertical,Horizontal,DC,Plane4种模式
其中前三种和4*4
亮度块预测类似,Plane预测块
的各像素值都是将上边预测块的最下面一行
,和左边已编码块最右边一列
,计算得到
Vertical,Horizontal,DC,Plane4种模式
这是编码器的内部实现机制,总体思路如下
对1个16*16
块,用4种16*16
预测模式,和9种4*4
预测模式,计算出预测块
,和实际待编码块
相减得到残差块
,由如下3种方案比较得到最优预测模式
失真大小
和编码后的码流大小
,通过率失真优化
(一般会使QP值适中,在失真
和码流大小
间平衡,找到一定码率下,失真最小的模式,作为最优预测模式)来选择最优模式H264标准中, 视频的第一帧的第一个块的左和上都是空,没法预测,所以设置成了一个约定值128,方便编码。这边找个几个264的码流,第一个I帧的第一个4*4 子块的yuv 预测值全都是128
可有单个或多个参考帧,本文以单参考帧为例,且以P帧为例
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2GVXQZ8W-1666778673995)(https://note.youdao.com/yws/res/125526/WEBRESOURCEe3f043ea5dfa18287b759122bd1016e0)]
多种块划分类型,如下
YUV4:2:0中色度块宽高都是亮度块的一半
一般RTC场景,P帧所有块都参考同一个参考帧
,且会根据当前编码帧的前一帧做参考(因为和前一帧的像素变化是最小的,用运动矢量表示位置变化)
从(32,80)移动到(80,80),我们将运动矢量(-48,0)编码到码流中,解码时解出运动矢量,即可在参考帧中找到预测块,再解码出残差,恢复出的图像块
=残差块
+预测块
虽然人眼可看出上图中小车在两幅图中的位置,并找到相似块作为预测块,但编码器怎么找到预测块呢?它又没有眼镜,这就通过运动搜索实现.
在参考帧中找到一个块(称为预测块),使和编码块的差距(可用残差块的像素绝对值之和表示,称为SAD)最小.
16*16
,则我们去参考帧中找若干16*16
的块得到若干残差块,选残差块最小的.以菱形模式寻找, 以起始点为菱形中心点, 将该中心点左上角的16*16块
作为预测块,求残差块的SAD;
并对菱形4个角做同样操作,得到SAD
5个点中,最小的SAD值对应的就是当前最佳匹配点
若当前最佳匹配点
为菱形中心点则结束;若反之则以当前最佳匹配点
位中心继续搜索直到找到为止.
如下图,以绿点(起点)为中心点,和旁边菱形4点相比,得到最佳匹配点位橙点,因非中心点故继续
再以橙点为中心,搜旁边菱形4点,得到最佳匹配点为橙点,因为是中心点故搜索完毕
若最佳匹配点
为六边形中点,则以该点为中心的菱形和正方形,各进行一次精细化搜索,找到中心点
,菱形的4个顶点
,正方形的4个顶点
这9个点中SAD值最小的点为最佳匹配点
.
为了解决残差块有零头像素的问题,使残差块!=0,使压缩效率不完美而引入的方案
如下图运动矢量出来是(-48.25,0)
,但只能表示为(-48,0)
,故解码时通过运动矢量与I帧算出的预测帧
有偏差,就是为了解决此问题引入的方案.
如下图,整像素插值得到半像素,半像素插值得到1/4像素,下图灰色为整像素,黄色为水平半像素,橙色为垂直半像素,绿色为中心半像素
红色为1/4像素点
这样就可表示上文提到的(-48,25,0)
的运动矢量了,就可在解码时得到正确精确的预测位置
实际上,我们用整像素运动搜索
完毕后, 再做一次亚像素精度运动搜索
,得到最佳整像素运动矢量(a0,b0),最佳半像素运动矢量(a1,b1),最佳1/4像素运动矢量(a2,b2),则最终的运动矢量(a,b)计算公式如下
即(a,b)=4(a0,b0)+2(a1,b1)+(a2,b2)
运动矢量
预测算法和编码运动矢量
不是直接像编码块
一样编码到码流的,而是先用周围相邻运动矢量
预测出预测运动矢量(称为MVP)
,将当前运动矢量与MVP的残差
称为MVD
,然后编码到码流的
解码端,用同样的运动矢量预测算法得到MVP
,从码流中解码出运动矢量残差MVD
,则MVP+MVD可得运动矢量
.
运动矢量预测算法
步骤如下
若运动矢量MVP对应的MVD为(0,0)
且残差块都为0
,则当前编码块模式为SKIP,则压缩率特别高
如P帧的静止部分,前后两帧无变化,则运动矢量直接为0,而且残差块像素值本身因为无变化而基本为0,只有少部分噪声引起的较小的值(且经过量化后都变为了0),那么这种该图像中的静止部分,或图像中的背景部分大多数时候都是SKIP模式
.
包括参考帧的选择,运动矢量的确定,块大小的选择,是否为SKIP模式的判断,共4个部分
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l6HKEL4Z-1666778674004)(https://note.youdao.com/yws/res/125522/WEBRESOURCEb7c201e5b105e42bcbaa9407ff639d87)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GAWZ9z9a-1666778674005)(https://note.youdao.com/yws/res/125524/WEBRESOURCEf7279aff9ed00f33c81d3e2b50297924)]
通过DCT变换和量化可去除视觉冗余
DCT变换和量化
f(i)
为第i
个样点的信号值,N
为信号样点的总个数f(i,j)
为第(i,j)
位置的样点的信号值,N
为信号样点的总个数图像是二维信号,需用二维DCT变换的,实际编码经常用两个一维DCT变换代替.
因先经过帧内预测,帧间预测,得到了残差块, 再经过DCT变换.
宏块是16*16
(如下图),而DCT变换通常宏块的在4*4
子块上进行, 其可用如下公式表示
下例以4*4
的残差块子块
为例看DCT变换
的过程,我们称左上角为DC系数
,其他为AC系数
残差块如下
对此残差块用公式如下
DCT变换的结果如下
从DCT变化公式可知用了cos函数
,即涉及浮点运算,而浮点运算较慢.
故一般通过Hadamard变换提升计算性能, 其公式如下
量化: 将图像变到频域后,对AC系数和DC系数都量化,因AC系数较多但幅值较小, 故AC系数更大概率变为0,故可去除一些AC系数,使图像被压缩.
公式如下
量化过程中,最重要的是QStep(可和用户一般用的QP一一映射,H264的QP和QStep映射表如下)
通常QStep若大,则AC系数和DC系数被量化为0的概率越大,则压缩率越高,信息丢失越多;
例子如下
DCT变换和量化
H264为了减少在不同机器上浮点运算的漂移误差, 将DCT变换改为整数变换, 将DCT变换中的浮点运算与量化
两个过程合并, 这样只有一次浮点运算.
常规DCT变换如下
H264中, 通过下述推导变为整数变换
只用点乘
左边的公式,即得到H264的变换公式如下
用上文的残差块例子,经H264的DCT整数变换, 结果如下
因为上述点乘
右边的公式被拿出来了,故将此部分合并到H264的量化环节
了
其中MF
一般查表可得,表格如下. 其中QP>5时用QP=QP%6查表.
经上文整数变换后,再量化后的结果如下
可知虽H264的DCT变换和常规DCT变换公式不同,但结果相同,符合预期.其实是实现方式不同.
DCT变换和量化
过程16*16
的帧内预测块4*4
的小块,做整数变换,变换后将16个4*4
小块的DC系数拿出来,组成1个4*4
的DC块,再对此4*4
的DC块做Hadamard变换, 最终再量化.16*16
帧内预测块之外的其他亮度块,都直接划分为4*4
的块做整数变换,再量化.8*8
, 先将8*8
色度块划分为4个4*4
小块做整数变换, 再将变换后4个小块的DC系数拿出来, 组成2*2
的DC块, 再对此2*2
DC块做Hadamard变换, 再量化.怎么把码流打包成一个个数据包,发到网络上,且在发送过程中避免网络拥塞
为什么要打包成RTP包, 是因为接收端解码, 不仅需要原始码流, 还需额外信息: 如使用的编码方式(H264/H265/VP8/VP9/AV1),和视频的播放速度; 且可网络带宽预测和拥塞控制.
包头如下
若只负责传输RTP包,不需要管RTP有没有丢包,不需要管是否有网络拥塞;因为若用TCP传输则其会负责丢包与拥塞.
但一般TCP适合传文本和文件,不适合音视频,故一般用UDP
为了用UDP,实现丢包和拥塞,则需要RTCP
是辅助配合RTP协议的, 两者一起工作.
RTCP有多种报文协议:发送端报告SR,接收端报告RR,RTP反馈报告RTPFB. 每种报告的有效载荷不同, 我们通过这些报告在接收端
和发送端
传递当前统计的RTP包的传输情况. RTCP本身不具备丢包重传和带宽预测功能, 这些功能需我们自己实现.
下图是RTPFB报告中的NACK报告(丢包提示报告).
RTP传输视频数据, 像快递盒, 先装好视频, 在填好视频基本信息和收件人地址,最后讲视频送到收件人手里.
RTCP像统计快递运送情况的记录表, 其中的NACK报告就是快递丢件记录表, 发件人收到NACK后, 可重传
一个同样的快递给收件人.
RTP头部: 如上文所述; RTP内容: 放H264码流.
有3种方式: 单NALU封包(一个NALU打一个RTP包),组合封包(多个NALU打一个RTP包),分片封包(一个NALU分开放在连续多个RTP包).
此方式适合单个RTP包小于1500Byte(MTU大小), 一般若P帧和B帧编码后较小, 适合此方式.
此方式适合单个NALU较小的情况,如多个NALU打包小于1500Byte时,但此情况少见.
常用, 但缺点是若丢了一个片则整包数据都废弃了
一般在一个H264码流中会混合多种RTP打包方式
RTP和RTCP一般都用UDP做传输层协议,故需自己实现拥塞控制算法.
预测出带宽后, 即可控制发送端的数据量, 防止因不够, 而导致的延时/丢包.
网络设备分2种:
为了兼顾2种网络设备, 有2种带宽预测算法
计算一组RTP包的发送时长,接收时长,判断当前延时的变化趋势
若延时趋势逐渐变大, 则说明实际带宽
<当前发送码率
, 需降低预测的带宽值
若延时趋势没有变大, 则说明当前带宽良好, 则提高预测的带宽值
, 知道延时明显变大后, 再降低预测的带宽值
通过如此事实调整预测值, 控制发送速率.
有如下4个步骤:
预测的带宽值
按时间分组, 每5ms为一组
因为UDP可能乱序(如先发送的包,可能后到达), 为避免负责度, 故乱序的包不参与计算
发送端每发送一个RTP包, 就记录一个包的序号
和实际发送时间
, 把这些信息记录到发送历史数据
中
接收端每收到一个包, 也会记录包的序号
和实际接收时间
, 每隔一段时间就把这些统计信息
(1. 每个包序号, 所对应的包, 是否被接收到了; 2. 当前包, 相比前一个包的接收间隔)发到发送端
通过RTCP协议的Transport-CC报文
若发送端收到此报文, 即可知此RTP包是否被接收到了; 若发送端未收到, 即说明丢包了(也可知未丢失的包,的接收时间)
发送端根据发送历史数据
中各包的发送时间
和Transport-CC报文中计算的各包接收时间
, 即可算出两组包之间的发送时长,和接收时长
, 计算方法如下
Transport-CC报文格式如下
将上文得到的接收时长
-发送时长
,即为延时
若延时>0
,则说明网络慢,产生了缓存,继而产生了延时
若延时=0
,说明网络刚好能承受数据量
若延时<0
,一般出现在之前因网络带宽不足而已经缓存了一部分数据, 但目前网络明显在变好, 从而网络设备快速地将缓存中的数据发送出去, 此情况下机会出现接收时长很短, 导致接收时长小于发送时长, 即延时为负数.
因为但一个包的延时可能有噪声干扰(如网络抖动), 故需要结合多个包的延时趋势, 判断.
利用TrendlineFilter滤波器, 其缓存了最近20个延时数据(到达发送端的时间,平滑后的累积延时:避免某延时的抖动)
然后应线性回归(如最小二乘法)求斜率
k>0
时认为有延时,k=0
时无延时,k<0
无延时反而接收速度更快
过载检测器: 比较延时趋势
与延时阈值
来判断网络是过载/欠载/正常状态; 并通过延时趋势
更新延时阈值
(为了防止阈值太大导致不够灵敏, 或阈值太小导致太敏感, 故动态调整)
状态机: 上升/保持/下降状态.
基于Transport-CC报文,计算丢包率,调整带宽
丢包率=丢包的数量
/总发送包的数量
若丢包率<2%
,则网络状况很好,需调高带宽值,带宽值等于过去1秒内所有预测到的带宽最小值*1.08
若丢包率介于2%
到10%
之间,则网络正常,不调整
若丢包率>10%
,则网络状况不好,需降低带宽值,等于当前预估带宽值*(1-0.5*丢包率)
根据预测的带宽, 即可控制数据的发送码率.
码控原理: 控制编码器输出码流的大小,即为每帧图选合适的QP值, 当一帧图确定后, 其画面复杂度和QP值决定了其编码后的大小.
码率
和实际预测带宽
匹配先确定帧组级(8帧为一组)的输出接近目标码率
再确定各帧的大小
再确定帧的QP值
再确定宏块组的大小
再确定宏块的QP值
目的: 能大概衡量, 当前帧, 预测后, 残差块的大小
I帧: 各宏块的方差之和
P帧: 当前帧的宏块
减去参考帧对应位置的宏块
,求SAD值,各宏块的SAD值之和
前面帧用多了就从后面帧里扣除, 前面帧用少了的话后面帧就可多用一些
卡顿: 若两帧间隔超过200ms,人眼即可看出明显卡顿, 有如下原因
PacedSender是节奏发送器, 在编码打包之后, 发送之前. 其每隔5ms发一个包, 其内部会记录上一个5ms发送周期发送完后剩余可发送的大小
在一个码流中,含多个子码流,服务端根据接收端的网络情况,下发对应码率的码流,实现可伸缩性.
若两个人视频通话,发送端网络好,而接收端网络差
则会引起: 一组RTP包的接收时长长,发送时长短;或者丢包率高
如果不做带宽预测和码控,接收端画面会卡顿
做的话, 发送端基于演示和丢包的带宽预测算法, 预估出带宽, 据此降低码率, 码率下降引起QP上升, 画面质量下降, 但流畅性变好, 不会一直卡死.
但若多人开会(如10个人),只有主持人共享屏幕,而每个听众的网速不同(有的2M,有的500K)
如果都按2M预测带宽,那网速慢的人会卡住;如果都按500K那么网速好的人只能看很差的画质.
SVC是指码流分为多层(如3层)
第0层是最底层,可独立编解码,不依赖第1和2层
第1层依赖第0层,但不依赖第2层
第2层依赖第0和1层
第0层画质最低,越高层越复杂.
对网速慢的人只给他转发第0层码流对应的RTP包,对网速中等的人转发第0层加第1层码流对应的RTP包;对网速很好的人,给他转发所有层码流的RTP包.
SVC有如下分类
即在帧率上做SVC
若如下图连续每帧都编码,那么只要丢了一帧就会导致后序的帧都无法解码,强行解码即会花屏
若如下图隔一帧,参考一帧(帧0是第0层I帧,帧1是第1层的P帧,帧2是第0层的I帧,帧3是第1层的P帧)
优点: 若丢掉高层帧,底层帧不受影响,只是帧率下降,仍可观看
缺点:因为运动时连续的,如果隔帧参考的话,会是压缩率下降(二层下降10%,三层下降15%)
在分辨率上做SVC
如需要720P的分辨率,则第0层是360P的分辨率;第0层加第1层是720P的分辨率
H264,H265,VP8这些常用编码标准(除了扩展)是不支持空域SVC的,且压缩率没有优势
故WebRTC中直接用多个编码器编码多种分辨率的方式,来代替空域SVC
如果视频码率是2M,时域SVC编码,共3层,总帧率24fps.
第一层帧率是6fps,码率500k.
第二层帧率是6fps,码率500k.
第三层帧率是1M.
若有一个接收端只有600K,则服务器只转发第一层给他.
若另一个接收端1.5M,则服务器抓饭第一层和第二层给他.
若另一个接收端10M,则服务器转发一二三层给他.
以VP8编码的RTP协议为例,在其RTP头和VP8码流数据中间,还有一个RTP描述头,放帧号,层号信息.
服务器可以从RTP表述头中,得到RTP包对应的层号,这样服务器可通过RTP层号和RTP包大小,来估算每层的码率.
由若干Tag组成(视频Tag,音频Tag,Script Tag放元信息)
总体结构如下
总体格式如下
音频和视频通过时间戳同步
直播RTMP协议和HTTP-FLV协议用的就是FLV封装
PTS: 视频帧的显示时间
DTS: 视频帧的解码时间
因为B帧需要依赖其前和其后的帧,故后面的帧可能先解码
时间基: FLV中的DTS和PTS的单位都是1ms
diff
通过计算当前正在播放的帧的播放时长
(计算方法是用还未播放,但紧接着要播放的帧的PTS,减去正在播放的帧的PTS, 将其记为last_duration
)diff>0
,说明视频快了,需延长当前播放视频帧的播放时间,即增加last_duration值diff<0
,说明视频满了,就缩短正在播放的视频帧的播放时间,即减小last_duration值WebRTC, FFmpeg, OpenH264
学习方法: 看不懂的地方, 可先跳过, 中间可以回头再看原先学不懂的地方(此时会发现知识点间的关联)