直播

我们借由腾讯直播解决方案来大致窥探下现在的直播架构

推流

基本上用的都是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秒以上的时长,分片数量一般为34个,所以总延迟约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
    23
    http {
    ...
    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
  • livego

    一个用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
    3
    RTMP: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
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
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>FLV live demo</title>
<script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/hls.js/8.0.0-beta.3/hls.min.js"></script>
<script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/flv.js/1.6.2/flv.min.js"></script>
</head>
<body>
<span>HLS</span>
<video width="45%" muted controls id="video"></video>
<span>HTTP-FLV</span>
<video width="45%" muted controls id="videoFlv"></video>
<div id="time" style="font-size: 128px"></div>
<script>
var video = document.getElementById("video");
var videoSrc = "http://10.0.0.249:7002/live/movie.m3u8";
if (Hls.isSupported()) {
var hls = new Hls();
hls.loadSource(videoSrc);
hls.attachMedia(video);
} else if (video.canPlayType("application/vnd.apple.mpegurl")) {
video.src = videoSrc;
}

if (flvjs.isSupported()) {
var videoFlv = document.getElementById("videoFlv");
var flvPlayer = flvjs.createPlayer({
type: "flv",
url: "http://10.0.0.249:7001/live/movie.flv",
enableWorker: true,
stashInitialSize: 128,
enableStashBuffer: false,
});
flvPlayer.attachMediaElement(videoFlv);
flvPlayer.load();
flvPlayer.play();
}

window.setInterval(function () {
var time = document.getElementById("time");
var ms = (window.performance.now() / 1000).toFixed(2);
time.innerHTML = ms;
}, 100);
</script>
</body>
</html>

延迟优化取舍

  • 采集端减少关键帧间隔GOP,例如OBS高级设置中修改。缺点是导致视频压缩率不高,传输效率低。
  • 取消或减少服务端的关键帧缓存(首屏时间换低延迟)
  • 浏览器端开启flv.js的Worker,多线程运行flv.js提升解析速度可以优化延迟(CPU换低延迟)

思考题,如何保存一个直播流

  • 基于直播互动的低延迟需求,直播流的服务端缓存一定不会很长
  • 推流、拉流都可能面临网络波动

云服务相关支持

  • 阿里云OSS 支持 RTMP 推流以及 HLS直播
  • 腾讯云COS 有官方文章说支持RTMP推流,但是文档没有相关内容
  • 腾讯云TRTC 支持RTMP推流以及转旁路CDN分发
  • 腾讯云直播解决方案

参考资料