为什么巨大的原始视频数据可以编码成很小的视频? 使用的是什么技术?
核心思想就是去除冗余信息:
-
空间冗余: 图像相邻像素之间有很强的想关性, 涉及到的相关技术有 - 关键帧的帧内压缩(有损压缩)、帧内预测、DCT转换量化、CABAC上下文压缩.
-
时间冗余: 视频序列的相邻图像之间的内容相似, 涉及到的相关技术/理论有 - 帧间压缩、运动估计(宏块匹配+运动矢量)、运动补偿、无损压缩
-
编码冗余: 不同像素值出现的概率不同, 涉及到的技术有 - VLC压缩(MPEG2)
-
视觉冗余: 人的视觉系统对某些细节不敏感
直播秒开优化
-
DNS解析 : 为了有效降低 DNS 解析对首开的影响,我们可以提前完成播放域名->IP 地址的解析,并缓存起来,播放的时候,直接传入带 IP 地址的播放地址,从而省去了 DNS 解析的耗时。如果要支持用 IP 地址播放,是需要修改底层 ffmpeg 源码的。
-
播放策略 : 很多侧重点播的播放器,为了减少卡顿,会有一些缓冲策略,当缓冲足够多的数据之后 ,再送入解码播放. 而为了加快首开效果,需要对播放的缓冲策略做一些调整,如果第一帧还没有渲染出来的情况下,不要做任何缓冲,直接送入解码器解码播放,这样就可以保证没有任何因为「主动」缓冲带来的首开延时.
-
播放参数设置 : 所有基于 ffmpeg 的播放器,都会遇到
avformat_find_stream_info这个函数耗时比较久,从而增大了首开时间,该函数主要作用是通过读取一定字节的码流数据,来分析码流的基本信息,如编码信息、时长、码率、帧率等等,它由两个参数来控制其读取的数据量大小和时长,一个是 probesize,一个是 analyzeduration。减少 probesize 和 analyzeduration 可以有效地减少avformat_find_stream_info的函数耗时,从而加快首开,但是需要注意的是,设置的太小可能会导致读取的数据量不足,从而无法解析出码流信息,导致播放失败,或者出现只有音频没有视频,只有视频没有音频的问题。 -
服务端优化 : 服务器关键帧缓冲、CDN策略
弱网直播优化
-
调整参数 : 根据上行带宽来动态调整码率、分辨率、帧率, 一般情况下视频码率默认是600Kbps, 音频码率默认64Kbps.
-
编码优化 : 1、 视频编码器开启最低延迟; 2、尽量使用ACC- LC Codec来编码音频, HE-ACC 或者 HE-ACC 2 虽然编码效率高,但是编码所需时间更长; 3、使用H264 baseline profile, 因为没有B帧, 编解码速度更快, X264还可以设置-tune zerolatency来降低编码的码率从而降低延迟,但是也会降低视频的质量. 4、固定码率编码 CBR 可以一定程度上消除网络抖动影响,如果能够使用可变码率编码 VBR 可以节省一些不必要的网络带宽,降低一定的延迟。因此建议尽量使用 VBR 进行编码.
传输协议优化
-
在服务端节点和节点之间尽量使用RTMP协议而非基于 HTTP 的 HLS 协议进行传输,这样可以降低整体的传输延迟。HLS协议通过切片传输数据,边下载边传输,所以传输延时要比RTMP长.
-
如果有必要,可以使用定制的 UDP 协议来替换 TCP 协议,省去弱网环节下的丢包重传可以降低延迟。它的主要缺点在于,基于 UDP 协议进行定制的协议的视频流的传输和分发不够通用,CDN 厂商支持的是标准的传输协议。另一个缺点在于可能出现丢包导致的花屏或者模糊(缺少关键帧的解码参考),这就要求协议定制方在 UDP 基础之上做好丢包控制。
丢帧处理
-
弱网环境下的丢帧策略。一般情况我们会有两种队列,分别是编码之前的原始数据队列和编码之后的编码队列。弱网丢帧策略常见的实现有两种:一种是丢弃原始数据队列中未编码的数据帧,另外一种是丢弃编码队列中的数据帧。这两种实现各有优缺点,无论采用哪种实现方式都以“不影响音视频的对齐”为第一准则。丢掉了一定时间的视频帧同时也需要丢掉同等时间的音频帧。
播放优化
-
动态码率播放策略。除了动态调整 buffer 大小的策略之外,也可以利用实时监测的网络信息来动态调整播放过程中的码率,在网络带宽不足的情况下降低码率进行播放,减少延迟。
NV21转I420
-
就是YUV420SP 转 YUV420P
-
NV12 是iOS中的格式,它的存储顺序是先存 Y 分量,再 UV 交替存储. NV21是安卓中的格式,它的存储顺序是先存 Y 分量,再 VU 交替存储.
-
I420, 也就是YU12, 分量的存储顺序是 YYYYYYYY UUUU VVVV
// pkt 为采集到的 NV21 格式的数据 memcpy(frame->data[0], pkt.data, V_WIDTH * V_HEIGHT); for (i = 0; i < V_WIDTH * V_HEIGHT / 4; i++) { frame->data[1][i] = pkt.data[V_WIDTH * V_HEIGHT + i * 2]; frame->data[2][i] = pkt.data[V_WIDTH * V_HEIGHT + i * 2 + 1];}fwrite(frame->data[0], 1, V_WIDTH * V_HEIGHT, outfile1);fwrite(frame->data[1], 1, V_WIDTH * V_HEIGHT / 4, outfile1);fwrite(frame->data[2], 1, V_WIDTH * V_HEIGHT / 4, outfile1);
DTS与PTS
-
DTS:Decode-Time-Stamp。DTS主要是标识读入内存中的比特流在什么时候开始送入解码器中进行解码。AVPacket中的参数, 编码后-解码前
-
PTS:Presentation-Time-Stamp。PTS主要用于度量解码后的视频帧什么时候被显示出来。AVFrame中的参数, 编码前 解码后. 手动设置使之连续, 使帧具有参考性, 否则就会画面花屏.
-
在没有B帧的情况下,DTS和PTS的输出顺序是一样的.
比特率
-
也叫码率, 表示经过编码(压缩)后的音、视频数据每秒钟需要用多少个比特来表示。
-
音频的比特率:比特率 =采样率 x 采用位数 x声道数
-
YUV420P的视频的码率 : 比特率 = 分辨率宽度的像素个数 * 分辨率高度的像素个数 * 3 / 2 * 帧率
影响视频清晰度的指标
-
分辨率
-
码率
-
帧率
-
压缩比
影响视频流畅度的指标
-
码率
-
帧率
GOP
-
group of picture : 一组强相关的视频帧
-
解码一个GOP时, 一定是I帧第一个 B帧最后一个, 但是播放顺序不受影响
-
每个GOP中的第一帧就是IDR帧
-
每个GOP可以有多个I帧,但只有一个IDR.
-
GOP的最后一帧一定是P帧
-
GOP长度越大 压缩率越高, 但是画面出现问题后 恢复需要等到下一个GOP开始
-
如果是实时传输, GOP不能设置太大 ,出现丢包画面很难恢复
一个多媒体文件大小的计算
-
文件大小 = (音频码率+ 视频码率) / 8 * 时长(单位 秒)
-
音/视频码率 = 原始数据大小 * 压缩比
SPS与PPS
- SPS (Sepuence Parameter Set) : 序列参数集, 对帧组的设置,作用于一串连续的视频图像. 如seq_parameter_set_id、帧数及POC(picture order count)的约束、参考帧数目(可以参考一帧或多帧)、解码图像尺寸和帧场编码模式选择标识等.
- PPS (Picture Parameter Set) : 图像参数集, 作用于视频序列中的图像. 如pic_parameter_set_id、熵编码模式选择标识、片组数目、初始量化参数和去方块滤波系数调整标识等.