音视频/流媒体开发 攻略

总结了从2022年底到2023年初在小红书实习期间学习到的 以及 其他时间自主学习的 音视频/流媒体开发 相关知识点。

音视频新手村攻略

常用名词解释

名词 解释 参考文档
分辨率 (Image Resolution) 屏幕显示图像的清晰度,通常用像素数目衡量,例如1920*1080 (1080P)、2560*1440 (2k)等。 分辨率 维基百科
显示分辨率列表 维基百科
码率 (Bit Rate) 单位时间内播放连续媒体的比特数量,标准单位bit/s (bps),常用单位kbps, mbps。
通常情况下,码率越大,视频文件越大,视频质量越高。
比特率 维基百科
帧率 (Frame Rate) 单位时间内播放的图片帧的数量,单位fps。
通常情况下,帧率越大,视频看起来就越流畅。
帧率 维基百科
GOP (Group of Pictures) 视频进行编解码前将其分成的若干小组,即图片组,每组由一张IDR帧(关键帧)和若干B/P帧(依赖帧)组成(也可能含有I帧)。每个GOP的第一个I帧为IDR帧(关键帧)。
I帧:帧内预测,无时间像关性,不依赖其他帧;
P帧:前向预测帧,通常依赖于前一个I/P帧;
B帧:双向预测帧,可以有多个,同时依赖以前和未来的帧,需在I/P帧解码完成后再解码(因此不会用在实时性很强的直播中)。
压缩率:I帧 < P帧 < B帧。因此可以发现压缩率高的视频文件中,B帧占比大,但解码时间也更长。
音视频开发 视频压缩IPB与GOP - 知乎
音视频开发学习:HEVC中的GOP/POC参数 - 知乎
I帧和IDR帧_F_Reading的博客-CSDN博客
PTS 与 DTS PTS:Presentation Time Stamp,视频帧被呈现出来的时间戳;
DTS:Decode Time Stamp,视频帧被解码出来的时间戳。
H264中的时间戳(DTS和PTS)_youzhouliu的博客-CSDN博客_pts时间戳
视频编码格式 用于存储或传输数字视频内容的内容表示格式,通常使用标准化的视频压缩算法(例如离散余弦变换、运动补偿等)。
常见的视频编码格式:H.264/AVC, H.265/HEVC, MPEG-2, VP8/VP9, WebM, AV1, H266(VVC), EVC, LCEVC
视频的编解码格式 - 知乎
关于H.264编解码原理:
H.264原理 - CSDN
音视频开发学习:H.264/AVC视频编解码技术 - 知乎
视频封装格式 在计算机系统上存储数字视频数据的文件格式,通常由包含视频、音频编码数据、同步信息、字幕和元数据的容器组成。
常见视频封装格式:MP4, M4V, FLV, WMV, MOV, MKV, AVI, RM, RMVB, VCD, DVD
视频的编解码格式 - 知乎
什么是视频封装格式和编码格式周末的音视频的博客-CSDN博客视频封装格式
色彩空间 色彩空间是描述使用一组值(通常使用三个、四个值或者颜色成分)表示颜色方法的抽象数学模型,在数字图像领域,采用不同的像素格式表征。
图片常用的色彩空间:
- RGB:基于红绿蓝的加法混色法
- CMYK:基于青、品红、黄和黑的减法混色法
- HSV:基于色相、饱和度、明度
视频常用的色彩空间:
- RGB
- YUV (一个亮度信道 Y + 两个色度信道UV)
注意,YUV是模拟信号:
关于色彩空间:
色彩空间 维基百科
像素格式(pix_fmt)
SAR / DAR
QP (Quantization Parameter )
ABR / VBR / CBR

FFmpeg工具常用姿势

概述

ffprobe

ffplay

ffmpeg

视频编码原理

step 1. 图像划分

step 2. 帧间预测 + 帧内预测

step 3. DCT变换

step 4. 量化

step 5. 压缩编码(熵编码)

step 6. 封装格式化

其他资源

直播体系架构学习

一些学习资料:

基于UDP的P2P打洞原理

P2P打洞原理

ICE交互流程

NAT分类

外部主机与NAT后设备通信难度:完全圆锥型 < 受限圆锥型 < 端口受限圆锥型 < 对称型

注意对称型特征:我们将内部地址 iAddr:iPort 与外部主机 nAddr:nPort 的地址和端口号组成一个四元组 (iAddr, iPort, nAddr, nPort) ,对于四元组中的不同取值,NAT 都会对应分配一个外部地址 eAddr:ePort ;并且也只有曾经收到内部主机数据的对应的外部主机,才能够把数据包发回。

完全圆锥形

受限圆锥型

端口受限圆锥形

对称型

ICE (Interactive Connectivity Establishment, 交互式连接建立)

作用:对于不同的NAT类型,借助ICE框架使用不同方式进行打洞。

NAT 自己不会告诉服务器它是什么类型的,我们需要一套交互式策略检测对应的类型,而检测工作一般是由 STUN 服务器完成的。

STUN (Session Traversal Utilities for NAT, NAT会话穿越应用程序)

作用:允许NAT之后的客户端找出自己的公网地址,并判断NAT的限制类型。

STUN测试流程:

STUN测试流程

TURN (Traversal Using Relays around NAT, NAT中继遍历程序)

作用:对于对称型NAT(或防火墙)无法被穿越,位于对称型 NAT 之后的客户端需要先在 TURN 服务器上创建连接,然后告诉所有对端设备发包到这个服务器上,然后服务器再把包转发给这个客户端。并且 TURN 服务器通常是和 STUN 服务器成对出现的,当 STUN 判断 NAT 为对称型时,就会交由 TURN 处理。

SDP(Session Description Protocal)会话描述协议

WebRTC主要在连接建立阶段用到SDP,连接双方通过信令服务交换会话信息,包括音视频编解码器(codec)、主机候选地址、网络传输协议等。

协议基本格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 协议版本号
v=0

// 会话发起者 唯一标识一个会话
// o=<username> <sess-id> <sess-version> <nettype> <addrtype> <unicast-address>
o=alice 2890844526 2890844526 IN IP4 host.anywhere.com

// 会话名(必选,若无意义可填空格)
// s=<session name>
s=

// 连接数据
// c=<nettype> <addrtype> <connection-address>
c=IN IP4 host.anywhere.com

// 时间,声明会话的开始和结束时间
// t=<start-time> <stop-time>
t=0 0 // start-time为0表示永久

// 媒体描述
// m=<media> <port> <proto> <fmt> ...
m=audio 49170 RTP/AVP 0

// 附加属性,两种格式
// a=<attribute>
// a=<attribute>:<value>
a=rtpmap:0 PCMU/8000

m=video 51372 RTP/AVP 31
a=rtpmap:31 H261/90000
m=video 53000 RTP/AVP 32
a=rtpmap:32 MPV/90000

示例SDP数据

code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
// sdp版本号为0
v=0
// o=<username> <sess-id> <sess-version> <nettype> <addrtype> <unicast-address>
// 用户名为空,会话id是8100750360520823155,会话版本是2(后面如果有类似改变编码的操作,sess-version加1),地址类型为IP4,地址为127.0.0.1(这里可以忽略)
o=- 7595655801978680453 2 IN IP4 112.90.139.105
// 会话名为空
s=-
// 会话的起始时间,都为0表示没有限制
t=0 0
a=ice-lite
// 音频、视频的传输的传输采取多路复用,通过同一个RTP通道传输音频、视频,可以参考 https://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-54
a=group:BUNDLE 0 1
// WMS是WebRTC Media Stram的缩写,这里给Media Stream定义了一个唯一的标识符。一个Media Stream可以有多个track(video track、audio track),这些track就是通过这个唯一标识符关联起来的,具体见下面的媒体行(m=)以及它对应的附加属性(a=ssrc:)
// 可以参考这里 http://tools.ietf.org/html/draft-ietf-mmusic-msid
a=msid-semantic: WMS 5Y2wZK8nANNAoVw6dSAHVjNxrD1ObBM2kBPV
// m=<media> <port> <proto> <fmt> ...
// 本次会话有音频,端口为9(可忽略,端口9为Discard Protocol专用),采用UDP传输加密的RTP包,并使用基于SRTCP的音视频反馈机制来提升传输质量,111、103、104等是audio可能采用的编码(参见前面m=的说明)
m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 126
// 音频发送者的IP4地址,WebRTC采用ICE,这里的 0.0.0.0 可直接忽略
c=IN IP4 0.0.0.0
// RTCP采用的端口、IP地址(可忽略)
a=rtcp:9 IN IP4 0.0.0.0
// ice-ufrag、ice-pwd 分别为ICE协商用到的认证信息
a=ice-ufrag:58142170598604946
a=ice-pwd:71696ad0528c4adb02bb40e1
// DTLS协商过程的指纹信息
a=fingerprint:sha-256 7F:98:08:AC:17:6A:34:DB:CF:3B:EC:93:ED:57:3F:5A:9E:1F:4A:F3:DB:D5:BF:66:EE:17:58:E0:57:EC:1B:19
// 当前客户端在DTLS协商过程中,既可以作为客户端,也可以作为服务端,具体可参考 RFC4572
a=setup:actpass
// 当前媒体行的标识符(在a=group:BUNDLE 0 1 这行里面用到,这里0表示audio)
a=mid:0
// RTP允许扩展首部,这里表示采用了RFC6464定义的针对audio的扩展首部,用来调节音量,比如在大型会议中,有多个音频流,就可以用这个来调整音频混流的策略
// 这里没有vad=1,表示不启用这个音量控制
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
// 表示既可以发送音频,也可以接收音频
a=sendrecv
// 表示启用多路复用,RTP、RTCP共用同个通道
a=rtcp-mux
// 下面几行都是对audio媒体行的补充说明(针对111),包括rtpmap、rtcp-fb、fmtp
// rtpmap:编解码器为opus,采样率是48000,2声道
a=rtpmap:111 opus/48000/2
// rtcp-fb:基于RTCP的反馈控制机制,可以参考 https://tools.ietf.org/html/rfc5124、https://webrtc.org/experiments/rtp-hdrext/transport-wide-cc-02/
a=rtcp-fb:111 transport-cc
a=rtcp-fb:111 nack
// 最小的音频打包时间
a=fmtp:111 minptime=20
// 跟前面的rtpmap类似
a=rtpmap:126 telephone-event/8000
// ssrc用来对媒体进行描述,格式为a=ssrc:<ssrc-id> <attribute>:<value>,具体可参考 RFC5576
// cname用来唯一标识媒体的数据源
a=ssrc:16864608 cname:YZcxBwerFFm6GH69
// msid后面带两个id,第一个是MediaStream的id,第二个是audio track的id(跟后面的mslabel、label对应)
a=ssrc:16864608 msid:5Y2wZK8nANNAoVw6dSAHVjNxrD1ObBM2kBPV 128f4fa0-81dd-4c3a-bbcd-22e71e29d178
a=ssrc:16864608 mslabel:5Y2wZK8nANNAoVw6dSAHVjNxrD1ObBM2kBPV
a=ssrc:16864608 label:128f4fa0-81dd-4c3a-bbcd-22e71e29d178
// 跟audio类似,不赘述
m=video 9 UDP/TLS/RTP/SAVPF 122 102 125 107 124 120 123 119
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:58142170598604946
a=ice-pwd:71696ad0528c4adb02bb40e1
a=fingerprint:sha-256 7F:98:08:AC:17:6A:34:DB:CF:3B:EC:93:ED:57:3F:5A:9E:1F:4A:F3:DB:D5:BF:66:EE:17:58:E0:57:EC:1B:19
a=setup:actpass
a=mid:1
a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:4 urn:3gpp:video-orientation
a=extmap:5 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
a=sendrecv
a=rtcp-mux
a=rtcp-rsize
a=rtpmap:122 H264/90000
a=rtcp-fb:122 ccm fir
a=rtcp-fb:122 nack
a=rtcp-fb:122 nack pli
a=rtcp-fb:122 goog-remb
a=rtcp-fb:122 transport-cc
a=fmtp:122 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f
a=rtpmap:102 rtx/90000
a=fmtp:102 apt=122
a=rtpmap:125 H264/90000
a=rtcp-fb:125 ccm fir
a=rtcp-fb:125 nack
a=rtcp-fb:125 nack pli
a=rtcp-fb:125 goog-remb
a=rtcp-fb:125 transport-cc
a=fmtp:125 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
a=rtpmap:107 rtx/90000
a=fmtp:107 apt=125
a=rtpmap:124 H264/90000
a=rtcp-fb:124 ccm fir
a=rtcp-fb:124 nack
a=rtcp-fb:124 nack pli
a=rtcp-fb:124 goog-remb
a=rtcp-fb:124 transport-cc
a=fmtp:124 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=4d0032
a=rtpmap:120 rtx/90000
a=fmtp:120 apt=124
a=rtpmap:123 H264/90000
a=rtcp-fb:123 ccm fir
a=rtcp-fb:123 nack
a=rtcp-fb:123 nack pli
a=rtcp-fb:123 goog-remb
a=rtcp-fb:123 transport-cc
a=fmtp:123 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=640032
a=rtpmap:119 rtx/90000
a=fmtp:119 apt=123
a=ssrc-group:FID 33718809 50483271
a=ssrc:33718809 cname:ovaCctnHP9Asci9c
a=ssrc:33718809 msid:5Y2wZK8nANNAoVw6dSAHVjNxrD1ObBM2kBPV 1d7fc300-9889-4f94-9f35-c0bcc77a260d
a=ssrc:33718809 mslabel:5Y2wZK8nANNAoVw6dSAHVjNxrD1ObBM2kBPV
a=ssrc:33718809 label:1d7fc300-9889-4f94-9f35-c0bcc77a260d
a=ssrc:50483271 cname:ovaCctnHP9Asci9c
a=ssrc:50483271 msid:5Y2wZK8nANNAoVw6dSAHVjNxrD1ObBM2kBPV 1d7fc300-9889-4f94-9f35-c0bcc77a260d
a=ssrc:50483271 mslabel:5Y2wZK8nANNAoVw6dSAHVjNxrD1ObBM2kBPV
a=ssrc:50483271 label:1d7fc300-9889-4f94-9f35-c0bcc77a260d

SDP交换流程

SDP交换流程

RTC拥塞控制 GCC/BBR

GCC(Google congestion control)

核心思想:通过丢包、延迟、抖动等参数预测当前可用带宽来控制发送速率,结合发送端和接收端两侧估计的带宽综合计算。

发送端估测:主要依赖于丢包率(+延迟);接收端估测:主要依赖于延迟变化。

基本原则:丢包率较小时缓慢(8%)上升预估带宽,较大时断崖式降低带宽。

主要分类及区别:

  • REMB-GCC:拥塞判断基于数据包的时延变化, 收端做拥塞估计,通过REMB报文反馈给发端做拥塞调节。 在webrtc的M55版本之前的版本支持。

  • TFB- GCC:拥塞判断基于数据包的时延变化, 收端通过Transport Feedback反馈收包时间给发端,发端做拥塞估计和拥塞调节。在webrtc的M55版本之后支持。

下图是TFB-GCC的流程:

TFB-GCC流程

优缺点

优点:灵敏度高, 能够及时响应, 提前避免拥塞。基于抖动和基于丢包的预估值可以很好的预测网络拥塞。

缺点:

  1. 与数据发送量强相关,网络好时发送量多,预估带宽才会上去。发送量发生大幅度波动时,预估带宽也会剧烈波动,从而影响发送量。

  2. 快升慢降,低带宽恢复高带宽所需时间较长,如果盲目增加恢复速度,又会导致带宽高估或波动剧烈。恢复速度和带宽稳定两者无法兼具。

  3. 对网络拥塞敏感,高抖动和高延时场景下GCC预估带宽波动大,准确性低,稳定性也较差。下图摘自webrtc拥塞控制算法对比-GCC vs BBR vs PCC - 知乎

GCC算法下的流量监测数据

BBR

参考文档:

核心思想:设计了一个状态机,通过调整发送量去探测最大带宽(Max BW)和最小延时(Min RTT),最大带宽和最小延时的乘积可以得到BDP(Bandwidth Delay Product), 而BDP就是网络链路中可以存放数据的最大容量。

BBR算法机制

BBR状态机主要是4个状态

  • STARTUP:连接启动后进入的阶段,主要目的是带宽探测,要考虑收敛性,快速的收敛到链路的最大带宽。该状态退出的条件是当前已经采集到了链路的最大带宽。

  • DRAIN:在STARTUP阶段最大链路带宽的采集是粗暴的,会导致很多数据报文缓存在链路的buffer中,链路的最大带宽不会因为中间链路数据缓存了而增大,所以需要排空这些缓存的数据。该状态的退出条件infilgh的报文数量刚好等于max bw对应的cwnd。

  • PROBE_BW:带宽的利用和上探阶段,一方面保证被探测到的带宽被充分利用,另外还实时上探感知链路带宽的变化。

  • PROBE_RTT:多流公平性考虑引入的超级机制,在PROBE_BW状态持续很长时间后,需要惩罚他,释放出部分带宽给其他流使用。

BBR状态机转移流程

优缺点

优点:

  • 抗丢包能力强

BBR vs CUBIC

  • 低延迟/抢占能力强(伴随公平性问题)

  • 平稳发送。通过cwnd控制发送数量

缺点:

  • 因为在PROBE_BW阶段每轮只有一次下调发送速度的机会,这个收到pacing_gain周期有6个RTT的影响,因此收敛速度慢,当丢包率较高时,吞吐量会出现断崖式下跌

  • 重传率较高

RTMP协议

参考文档:

RTP/RTCP协议

通过抓包学习RTCP与RTP协议

  • 通过ffmpeg推一个rtp流到本地服务器:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
ffmpeg \
-f avfoundation \
-pix_fmt yuyv422 \
-video_size 1920x1080 \
-vsync 2 \
-i "1:0" -ac 2 \
-vf format=yuyv422 \
-vcodec libx264 \
-maxrate 2560k \
-bufsize 1200k \
-acodec aac \
-ar 44100 \
-b:a 128k \
-f rtp_mpegts udp://127.0.0.1:9988

那么本地可以通过如下代码拉流观看:

1
ffplay -i udp://@0.0.0.0:9988

推流成功后,打开wireshark,选择Loopback即可看到RTCP包和对应的UDP(RTP)包

SCTP协议

SCTP vs TCP

直播技术 一些问题和应对方案

常见推流架构

网络拥塞导致的延迟问题

  1. 在推流端drop数据 或 音频做变速、视频drop非I帧(难度较高)
  2. 在拉流端drop数据 或 变速处理(难度较高)

ffmpeg函数阻塞问题

  1. 设置非阻塞
  2. 设置超时回调函数