# 简述
# 音视频的应用
- 互动直播系统,如一对一视频等
- 娱乐直播系统,如主播等
- 音视频特效
- 音视频编辑
# 音视频处理流程
- 采集 ==> 预处理 ==> 编码 ==> 文件格式封装 ==> 协议传输 ==> 文件格式解封装 ==> 解码 ==> 渲染
# 主要概念
- 原始数据格式
- 视频:RAW ==> YUV
- 音频:PCM
- 编码格式
- 视频:H264、HEVC、VP8、VP9、VC-1、MPEG2、MPEG4
- 音频:AAC、MP3、WMV、AC-3
- 硬解码和软解码
- 硬解码通过 GPU 解码,速度快,兼容性差
- 软解码通过 CPU 解码,速度慢,兼容性好
- 封装格式(容器),其实是包裹了音视频编码数据的容器,用来把以特定编码标准编码的视频流和音频流混在一起,成为一个文件
- 视频:MP4、FLV、TS(注意 m3u8 只是一个播放索引文件,并不是封装格式)
- 音频:不封装
- 传输协议
- 下载式传输
- 我们知道音视频文件普通体积都比较大,在网络带宽的限制,下载常常需要耗费花较长的时间。所以这种处理方法延迟也很大。并且用户需要等到把整个音视频文件全部下载完后才能使用播放器进行观看
- 流式传输(流媒体协议)
- 流式传输时,声音、影像或动画等时基媒体由音视频服务器向用户计算机的连续、实时传送,用户不必等到整个文件全部下载完毕,而只需经过几秒或十数秒的启动延时即可进行观看。当声音等时基媒体在客户机上播放时,文件的剩余部分将在后台从服务器内继续下载。流式不仅使启动延时成十倍、百倍地缩短,而且不需要太大的缓存容量。流式传输避免了用户必须等待整个文件全部从 Internet 上下载才能观看的缺点。而定义音视频数据如何流式传输的则是流媒体传输协议
- 格式
- RTMP
- 基于 TCP
- 一般用于推流端,当然也可以做拉流
- 地址是 rtmp:// 开头的
- 延迟在 1-3s 左右
- 浏览器端播放需要 flash 插件支持,因为 flash 已被弃用,所以很少用 RTMP 做拉流播放
- 传输的是流数据
- HLS
- 基于 HTTP
- 一般用于拉流端
- 地址是 http:// 开头的
- 延迟较高 5-30s 左右
- 传输的是文件
- 从严格意义上讲,它并不是流式协议,因为工作原理是通过 HTTP 下载静态文件,一个是索引文件 m3u8,一个是碎片文件 ts,这两个都是直接写入磁盘的
- HTTP-FLV
- 基于 HTTP
- HTTP 版本的 RTMP,一般用于拉流端
- 地址是 http:// 开头的
- 延迟在 1-3s 左右
- 传输的是流数据
- RTMP
- 下载式传输
# 浏览器兼容性
以下兼容性可通过 canisue 查验
MSE:Media Source Extensions
HLS
- H5 主流浏览器大部分都支持,除了 Opera Mini、Firefox for Android、KaiOS Browser
- PC 除 Safari 外,其他都不支持
- 因为 hls.js 依赖的是 MSE,如果浏览器不支持 MSE 那使用 hls.js 也没用,H5 的 Safari 不支持 MSE,但是它系统里面有内置 hls 模块,其他大部分浏览器对 MSE 的支持都还不错
- 因为 Safari 有内置了 hls 功能,所以使用时需要判断一下是使用 hls.js 还是使用内置的模块,具体 demo 可参考 hls 官网
RTMP
- 需要 flash 支持的浏览器或者要安装 flash player 才能播放
- 可以考虑转成 flv 进行播放,即 HTTP-FLV
HTTP-FLV
- 需要使用 flv.js 插件,但是 flv.js 和 hls.js 一样,依赖于 MSE,而 MSE 在 PC 主流浏览器都支持,但是在 H5 的 Safari 中却不支持
- 也就是说此方案适用于 PC 端,RTMP 推流 + FLV 拉流
WebRTC
- 主流浏览器都支持
- 主要的应用场景是在实时通讯、音视频通话、屏幕共享等
H265 编码
- 除了 H5 的 Safari、Samsung Internet、Baidu Browser 和 PC 的 Safari,其他浏览器都不支持,那为什么大部分的浏览器都能够支持呢?这其实使用了 WebAssembly 技术,用 C/C++ 代码调用 ffmpeg 进行解码,然后使用 emscripten 编译成 wams 文件,在 js 中调用,实际上用的不是浏览器的能力
# ffmpeg
# 常用工具
- ffmpeg 对音视频的编码解码,特效处理等
- ffplay 基于 ffmpeg,播放器
- vlc 依赖于 ffmpeg,播放器,有图形化界面
# 如何安装
- Mac 使用命令安装 brew install ffmpeg
- Mac/Linux 源码方式安装
- 下载 ffmpeg,http://ffmpeg.org/download.html
- 编译 ffmpeg
- ./configure --prefix=/usr/local/ffmpeg --enable-debug=3 (生成编译脚本,并打开调试模式)
- make -j 4 (指定四个进程同时进行编译)
- make install(安装)
- Windows 编译方式
- Cygwin(Cygnus Windows) 直接安装
- MinGW(Minimalist GNU for Windows) + MSYS2(Minimal SYStem2)(推荐)
- VS(Visual Studio 2015/2017...) + MSYS2(Minimal SYStem2)
# 音频
# 声音是如何被听到的
人类听觉范围在:20Hz —— 20kHz 之间
- 声音是由物体振动产生的
- 它可以通过空气、固体、液体等进行传输
- 振动耳膜
# 声音三要素
- 音调
- 音量
- 音色
# 模数转换(模拟信号转数字信号)
- 对声音进行量化,即采样,常见的采样率有 48000/s、32000/s、16000/s、8000/s,采样率越高,还原度越高
- 拿到采样结果再转为二进制格式,采样一般是十进制,最后得到二进制方波
# 音频原始数据格式
- PCM (纯粹的原始数据)
- WAV (在 PCM 上加了些基本的头信息,便于播放器识别)
- 头信息包含了 WAV 的标识,也包含了采样大小、采样率、声道数等信息,也就是说可以通过头信息得到对应文件的信息
- 量化的基本概念
- 采样大小:一个采样用多少 bit 存放,常用的是 16bit
- 采样率:采样频率 8k、16k、32k、44.1k、48k
- 声道数:单声道、双声道、多声道(立体声)
- 码率计算:
- 一个 PCM 音频流的码率计算 = 采样率 * 采样大小 * 声道数
- 例子:采样率为 44.1KHz,采样大小为 16bit,双声道的 PCM 编码的 WAV 文件,它的码率为 44.1K * 16 * 2 = 1411.2Kb/s
# 音频数据的流转
- PCM(采集到的原始数据,即数字信号)==> aac/mp3(编码器,即得到压缩后的数据)==> mp4/flv(多媒体文件,格式)
- 以上步骤,可以反过来,即从一个多媒体文件解析成原始数据
# 音频采集
- 各个平台都有对应的 API,如 Android、iOS、Windows,并且每个平台都有多套的 API
- 而 FFmpeg 封装了各个平台的 API,可以兼容各个平台
- 通过命令采集
- ffmpeg -f avfoundation -i :0 out.wav(注意这个 avfoundation 和 :0,是 Mac 平台下的 API,如果是其他平台则需要使用其他平台的 API,可到官网搜索)
- ffplay out.wav
# 音频压缩
- 压缩的两个极端:将数据压缩的越小越好,压缩的速度越快越好
- 音频压缩技术是在保证信号在听觉方面不产生失真的前提下,对音频数据信号进行尽可能大的压缩
- 压缩的主要方法是去除采集到的音频冗余信息,所谓冗余信息包括人耳听觉范围外的音频信号以及被屏蔽掉的音频信息
- 有损压缩:还原不到原始数据
- 消除冗余信息,如人类听觉范围在:20Hz —— 20kHz 之间,就可以把这范围外的去除掉
- 遮蔽掉的信息(时域转频域变换器,生成多种频段的数据,根据频段去屏蔽)
- 频域遮蔽,比如声音中包含了脚步声、汽车声等,这些是我们不关心的,可以去除掉
- 时域遮蔽,同个时间段内,声音大的会把声音小的给遮蔽掉
- 无损压缩:数据压缩后,通过解码可以还原为原来的信息
- 熵编码
- 哈夫曼编码
- 算术编码(基于香农编码)
- 香农编码
- 应用
- zip
- rar
- zz
- 7z
- 熵编码
# 音频编码器
- 音频编码过程:时域转频域变换(屏蔽信息) ==> 心理声学模型(消除冗余信息) ==> 量化编码(熵编码) ==> 比特流格式化 ==> 比特流
- 常见的音频编码器
- 常见的音频编码器包括 OPUS、AAC、Ogg、Speex、iLBC、AMR、G.711 等
- 其中,AAC 在直播系统中应用的比较广泛,OPUS 是比较新的音频编码器,WebRTC 默认使用 OPUS,固话一般用的是 G.711 系列
- 网上评测结果:OPUS > AAC > Ogg
- 音频编码质量,OPUS 最好
- 音频编码码率,OPUS 延迟低,而 AAC、MP3 延迟比较大,适合用于有一定延迟的直播
- AAC 编码器介绍
- AAC(Advanced Audio Coding)由 Fraunhofer IIS、杜比实验室、AT&T、Sony 等公司共同开发,目的是取代 MP3 格式
- 最开始是基于 MPEG-2 的音频编码技术,MPEG-4 标准出现后,AAC 重新集成了其特性,加入了 SBR 技术和 PS 技术
- 目前常用的规格有 AAC LC(常用)、AAC HE V2(常用)、AAC HE V1
- AAC 规格
- AAC LC + SBR = AAC HE V1
- AAC HE V1 + PS = AAC HE V2
- AAC 规格描述
- AAC LC(Low Complexity)低复杂度规格,码流是 128k,音质好
- AAC HE V1 等于 AAC LC + SBR(Spectral Band Replication),其核心思想是按频谱保存,低频编码保存主要成分,高频单独放大编码保存音质,码流在 64k 左右
- AAC HE V2 等于 AAC LC + SBR + PS (Parametric Stereo),其核心思想是双声道中的声音存在某种相似性,只需要存储一个声音的全部信息,然后花很少的字节用参数描述另一个声道和它不同的地方
- AAC 格式
- ADIF(Audio Data Interchange Format)这种格式的特征是可以确定的找到这个音频数据的开始,只能从头开始解码,不能在音频数据流中间开始,这种格式常用在磁盘文件中
- ADTS(Audio Data Transport Stream)这种格式的特征是每一帧都有一个同步字,所以可以在音频流的任何位置开始解码,它类似于数据流格式,这种格式会比 ADIF 的数据大,因为它相当于在每个音频帧前面加了一个头信息
- ADTS 格式:http://www.p23.nl/projects/aac-header,头信息中包含了编码信息
- ffmpeg 生成 AAC 文件
- ffmpeg -i xxx.mp4 -vn -c:a libfdk_aac -ar 44100 -channels 2 -profile:a aache_v2 xxx.aac
- 音频重采样
- 将音频三元组(采样率、采样大小和通道数)的值转成另外一组值就是重采样,只要其中一个值发生变化都是重采样,如将 44100/16/2 转成 48000/16/2
- 为什么需要重采样?
- 从设备采集的音频数据与编码器要求的数据不一致
- 扬声器要求的音频数据与要播放的音频数据不一致
- 更方便运算,如回音消除时单声道比多声道更容易处理
- 如何知道各个平台的参数?
- 要了解音频设备的参数
- 查看 ffmpeg 源码
- ffmpeg 重采样的步骤
- 创建重采样上下文
- 设置参数
- 初始化重采样
- 进行重采样
- 涉及到的 API
- swr_alloc_set_opts
- swr_init
- swr_convert
- swr_free
# 视频
# 图像的基本概念
- 视频是由一组图像组成的,为了传输或占用更小的空间而被压缩,最终在显示设备上展示(未被压缩)
- 图像是由像素组成的
- 每个像素是由 RGB 组成的
- 分辨率:x 轴的像素点和 y 轴的像素点
- 每个像素的位深:RGB888(24 位),RGBA(32 位)
- 每个像素是由三根二极管组成的,就是黄绿蓝,根据二极管显示的强弱就可以展示不同的颜色
# 屏幕显示器
- 图像与屏幕的关系
- 图像是数据
- 屏幕是显示设备
- 图像数据经过驱动程序让屏幕显示图像
- RGB 的色彩问题
- RGB 与 BGR,正常应该是 RGB,但是 BMP 使用的是 BGR 格式,这时就需要进行转换,否则就会出现展示出来的颜色不对
- 屏幕指标
- PPI(pixels per inch)每英寸的像素数
- DPI(Dots pen inch)每英寸的点数
- 一般是 PPI === DPI
- PPI > 300 就属于视网膜级别
# 码流的计算
- 分辨率
- x 轴的像素个数 * y 轴的像素个数
- 常见的宽高比是 16:9 或 4:3,现在大部分是 16:9,4:3 比如以前的电视机,如果视频的宽高不是这两种的话,则需要转换为这两种,否则会出现一些问题
- 帧率
- 每秒钟采集或播放图像的个数
- 动画的帧率是 25 帧/s
- 常见的帧率:15 帧/s(实时通讯),30 帧/s(录课场景),60 帧/s(电影场景)
- 未编码视频的 RGB 码流
- RGB 码流 = 分辨率(宽 x 高) * 3(Byte) * 帧率
- 例如:1280 * 720 * 3 * 25 = 69120000,约 69M
# 图像的显示
- 图像大小等于显示区域大小
- 图像小于显示区域(拉伸或留白)
- 图像大于显示区域(缩小或截断)
# YUV 颜色编码
- YUV 也称 YCbCr,Y 表示明亮度,UV 的作用是描述影像色彩及饱和度
- 主要的采样格式有 YUV4:2:0、YUV4:2:2、YUV4:4:4
- RGB 与 YUV 的关系
- RGB 用于屏幕图像的展示
- YUV 用于采集与编码
- RGB 转 YUV
- Y = 0.299 * R + 0.587 * G + 0.114 * B
- U = -0.147 * R - 0.289 * G + 0.436 * B = 0.492 * (B - Y)
- V = 0.615 * R - 0.515 * G - 0.100 * B = 0.877 * (R - Y)
- YUV 转 RGB
- R = Y + 1.140 * V
- G = Y - 0.394 * U - 0.581 * V
- B = Y + 2.032 * U
- YUV 常见格式
- YUV4:2:0(最标准、应用最广泛)
- YUV4:2:2
- YUV4:4:4
- 有些播放器不支持第二和第三中格式,此时应该转换为第一种
- 数据量的计算
- YUV = Y * 1.5
- YUV = RGB / 2
- 为什么 YUV 在视频中被广泛使用
- 一个是因为视频是从黑白电视机过来的,为了兼容以前的格式,所以使用 YUV
- 从数据量来说比 RGB 更小
# H264 编码
首先要弄明白编码的目的,有目的的学习效率会更好。编码是为了将数据进行压缩,这样在传输的过程中就不会使资源被浪费,用一个简单的例子来说明编码的必要性:当你此刻显示器正在播放一个视频,分辨率是 1280 * 720,帧率是 25,那么一秒所产生正常的数据大小为:1280 * 720(位像素)* 25(张) / 8(1 字节 8 位)(结果:B) / 1024(结果:KB) / 1024 (结果:MB) = 2.75MB 显然一秒这么大的数据你是无法接受的,所以如果不将数据进行压缩,那么只能一首凉凉表达此刻的感受了;
- 压缩比是 1/100(也就是说有 100m 的数据大小,只占 1m 的空间)
- 建议码流是 500kpbs,没有一个公式进行计算,而是经验总结得出的,这边参考的是声网 (opens new window),码率越高延迟越大,码率越低延迟越低,而质量正好相反
- GOP 指是一组类似或强相关的帧,里面帧与帧之间的差别小
- 编码帧的分类
- GOP 的第一帧是 I 帧(intraframe frame),关键帧,采用帧内压缩技术,不依赖于任何参考帧
- P 帧(forward Predicted frame),向前参考帧,压缩时,只参考前面已经处理的帧,采用帧间压缩技术,它占 I 帧的一半大小
- B 帧(Bidirectionally predicted frame),双向参考帧,压缩时,既参考前面已经处理的帧,也参考后面的帧,帧间压缩技术,它占 I 帧的 1/4 大小
- IDR 帧与 I 帧的区别与联系
- GOP 的第一帧就是 IDR 帧
- IDR 帧是一种特殊的 I 帧
- IDR(Instantaneous Decider Refresh)解码器立即刷新
- 每当遇到 IDR 帧时,解码器就会清空解码器参考 buffer 中的内容
- 帧与分组的关系
- 在解码时,先解 I 帧,然后就要先解码 P 帧,再去解码 B 帧,因为 B 帧是前后参考帧,但是在实际播放时,还是 I ==> B ==> P 的顺序
- B 帧之间是没有参考的
- SPS 与 PPS
- 会出现在 IDR 帧的前面并且同时存在的帧
- SPS(Sequence Parameter Set),作用是修饰一组帧的约束,序列参数集,作用与一串连续的视频图像,如 seq_parameter_set_id、帧数及 POC(picture order count)的约束、参考帧数目、解码图像尺寸和帧场编码模式选择标识等
- PPS(Picture Parameter Set),作用是修饰图像的约束,图像参数集,作用与视频序列中的图像,如 pic_parameter_set_id、熵编码模式选择标识、片组数目、初始量化参数和去方块滤波系数调整标识等
- 编码帧的分类
- H264 压缩技术
- 帧内压缩,解决的是空域数据冗余问题
- 帧间压缩,解决的是时域数据冗余问题
- 整数离散余弦变换(DCT),将空间上的相关性变为频域上无关的数据然后进行量化
- CABAC 压缩
- 宏块
- 宏块是视频压缩操作的基本单元
- 无论是帧内压缩还是帧间压缩,他们都以宏块为单位
- 帧内压缩的理论
- 相临像素差别不大,所以可以进行宏块预测,H264 提供了 9 种预测模式
- 人们对亮度的敏感度超过色度
- YUV 很容易将亮度与色度分开
- 预测模式信息与残差值压缩,残差值指的是预测出来的图片和原始图片的差别
- 帧间压缩原理
- GOP
- 参考帧
- 运动估计(宏块匹配 + 运行矢量)
- 运动补偿(解码)
- 视频花屏原因
- 如果 GOP 分组中有帧丢失,会造成解码端的图像发生错误,这会出现马赛克(花屏)
- 视频卡顿原因
- 为了避免花屏问题的发生,当发现有帧丢失时,就丢弃 GOP 内的所有帧,直到下一个 IDR 帧重新刷新图像
- I 帧是按照帧周期来的,需要一个比较长的时间周期,如果在下一个 I 帧来之前不显示后来的图像,那么视频就静止不动了,这就是出现了所谓的卡顿现象
- 花屏和卡顿并不能同时兼顾的去解决,得看具体的使用场景去解决,要么花屏,要么卡顿,如果想要卡顿时间短一些,可以 1s 插入 IDR 帧,但是这样数据量会变大,带宽会有压力
- 无损压缩
- 上面压缩后,再进行无损压缩
- DCT 变换
- VLC 压缩
- CABAC 压缩
- H264 码流
- 分层
- NAL 层(Network Abstraction Layer),视频数据网络抽象层
- VCL 层(Video Coding Layer),视频数据编码层
- VCL 结构关系
- 每个视频帧是由一片一片的 slice 组成的,slice 是由宏块组成的
- 码流基本概念
- SODB(String Of Data Bits),原始数据流,二进制数据串,没有任何处理,它是由 VCL 层产生的,以位进行计算,更好的进行压缩,但是长度不一定是 8 的倍数,所以需要补齐
- RBSP(Raw Byte Sequence Payload),SODB + trailing bits,让原始数据变成字节进行组装,就是补齐,算法是如果 SODB 最后一个字节不对齐,则补 1 和多个 0
- NALU,一字节的 Header + RBSP
- 分层
# 参考文献
作者:chenjie
链接:https://webchenjie.cn
来源:ChenJieBlog