视频直播技术简介以及自建直播服务器
直播
我们借由腾讯直播解决方案来大致窥探下现在的直播架构
推流
基本上用的都是RTMP(Real-Time Messaging Protocol)协议
RTMP协议是应用层协议,是要靠底层可靠的传输层协议(通常是TCP)来保证信息传输的可靠性的。在基于传输层协议的链接建立完成后,RTMP协议也要客户端和服务器通过“握手”来建立基于传输层链接之上的RTMP Connection链接,在Connection链接上会传输一些控制信息,如SetChunkSize,SetACKWindowSize。其中CreateStream命令会创建一个Stream链接,用于传输具体的音视频数据和控制这些信息传输的命令信息。RTMP协议传输时会对数据做自己的格式化,这种格式的消息我们称之为RTMP Message,而实际传输的时候为了更好地实现多路复用、分包和信息的公平性,发送端会把Message划分为带有Message ID的Chunk,每个Chunk可能是一个单独的Message,也可能是Message的一部分,在接受端会根据chunk中包含的data的长度,message id和message的长度把chunk还原成完整的Message,从而实现信息的收发。
从推流的角度,步骤为
TCP握手 -> RTMP握手 -> 建立connetion -> 创建Stream -> 推送内容
工具方面,以PC端为例,OBS应该是一个事实标准工具。他也有丰富的插件生态。下载
拉流
如果说直播是一条海上航线,http-flv,rtmp,hls这三就是船。flv与ts就相当于集装箱。
一般主播端用rtmp进行推流,推到cdn以后,cdn支持观众用http-flv,hls,rtmp三种方式进行拉流,一般直播app用的是http-flv。这些协议相当于载具,载的是什么呢,rtmp,http-flv运载的是flv,hls运载的是m3u8与ts。
RTMP
和前文的推流差异主要在,publish指令变为play指令
- 优点: 低延迟,客户端兼容性好,h5原生支持等
- 缺点: 基于TCP协议,非常规端口
1935
可能会被各种防火墙和安全策略拦截
HLS
Http Live Streaming,苹果提出基于HTTP的流媒体传输协议。HTML5可以直接打开播放。
以 H.264 格式对图像进行编码,以 MP3 或者 HE-AAC 对声音进行编码,最终打包到 MPEG-2 TS(Transport Stream)容器之中;把编码好的 TS 文件等长切分成后缀为 ts 的小文件,并生成一个 .m3u8 的纯文本索引文件。
客户端先拉取m3u8文件清单,再拉取索引清单文件中的ts视频流文件进行播放,清单播完了重新拉取m3u8清单,循环形成直播。- 优点: 兼容性好,特别移动端
- 缺点: 延迟太高,TS分片每个分片一般有5秒以上的时长,分片数量一般为3
4个,所以总延迟约10秒30秒。
HTTP-FLV
包装成FLV流媒体,不设置content-length,客户端不断的接收数据,直到服务端断开连接或者发送空chunk
优点: 因为是HTTP, 支持302。低延时。
缺点: 客户端要求高,flash淘汰之后需要一些依赖。
优缺点
协议 | 延迟 | 封装格式 | 播放端支持 |
---|---|---|---|
HLS | 10~30s | M3U8、TS | 移动端原生支持,PC需要hls.js |
RTMP | 1~3s | FLV TAG | flash |
HTTP-FLV | 1~3s | FLV TAG | flv.js, libvlc |
flv.js
flv.js只做了一件事,在获取到FLV格式的音视频数据后通过原生的JS去解码FLV数据,再通过Media Source Extensions API 喂给原生HTML5 Video标签。(HTML5 原生仅支持播放 mp4/webm 格式,不支持 FLV)
flv.js 为什么要绕一圈,从服务器获取FLV再解码转换后再喂给Video标签呢?原因如下:
兼容目前的直播方案:目前大多数直播方案的音视频服务都是采用FLV容器格式传输音视频数据。
FLV容器格式相比于MP4格式更加简单,解析起来更快更方便。
音频视频的容器的格式
重点说一下FLV容器, FLV 是Flash video 的缩写,是Adobe Flash player 支持的一种流媒体播放格式。
FLV文件格式长啥样:
其中,tag分为三种
- Script 记录各种元数据,包括像素码率等,第一个tag
- Audio 音频,一般为ACC编码
- Video 视频,一般为H.264
有一些软件可以直观的查看flv文件对应的结构,例如mac下的FLVParse
国内直播
基本上都是RTMP推流,客户端拉流这边以PC端为例,主流都是http-flv。
差异也仅仅在于http1.1协议下的Connection不同
- 斗鱼, 抖音
- B站
自己搭建一个RTMP服务以及HTTP-FLV服务
nginx
基于nginx开发的一个模块
这个
nginx-http-flv-module
又是基于nginx-rtmp-module实现的。构建方式是编译nginx时加上这个模块
1
2
3./configure --add-module=/path/to/nginx-rtmp-module
make
make install对应的nginx.conf配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23http {
...
server {
listen 80;
...
location /live {
flv_live on;
}
}
}
rtmp {
...
server {
listen 1935;
...
application myapp {
live on;
}
}
}这边有一个现成的docker封装用来演示:
1
docker run --rm -it -p 80:80 -p 1935:1935 mugennsou/nginx-http-flv
推流地址
1
rtmp://127.0.0.1/demo/stream-1
http-flv 拉流地址
1
http://host[:port]/live?app=demo&stream=stream-1
-
一个用GO写的直播服务器,支持RTMP推流以及HLS和HTTP-FLV推流。熟悉GO的同学可以借助这个项目熟悉相关协议。
启动1
docker run -p 1935:1935 -p 7001:7001 -p 7002:7002 -p 8090:8090 -d gwuhaolin/livego
获取推流秘钥
1
http://localhost:8090/control/get?room=movie
拉流地址
1
2
3RTMP:rtmp://localhost:1935/{appname}/movie
FLV:http://127.0.0.1:7001/{appname}/movie.flv
HLS:http://127.0.0.1:7002/{appname}/movie.m3u8
演示
这边一个简单的html页面演示HLS和HTTP-FLV的延迟,两个推流地址基于livego搭建,可以自行替换
1 | <!DOCTYPE html> |
延迟优化取舍
- 采集端减少关键帧间隔GOP,例如OBS高级设置中修改。缺点是导致视频压缩率不高,传输效率低。
- 取消或减少服务端的关键帧缓存(首屏时间换低延迟)
- 浏览器端开启flv.js的Worker,多线程运行flv.js提升解析速度可以优化延迟(CPU换低延迟)
思考题,如何保存一个直播流
- 基于直播互动的低延迟需求,直播流的服务端缓存一定不会很长
- 推流、拉流都可能面临网络波动