侧边栏壁纸
  • 累计撰写 244 篇文章
  • 累计创建 16 个标签
  • 累计收到 0 条评论
隐藏侧边栏

如何基于 RTMP 协议实现视频直播

kaixindeken
2021-04-23 / 0 评论 / 0 点赞 / 53 阅读 / 1,834 字

1

主播在客户端采集视频后,照例客户端软件会对视频进行编码以压缩视频,然后通过 RTMP 协议将这个二进制的视频流打包成网络包发送给服务器,这个过程对应上述流程图中的推流(在实际开发中,通常通过 FFmpeg 进行推流)。

RTMP 是基于 TCP 的,因而双方需要建立一个 TCP 连接,在 TCP 连接的基础上,还需要建立一个 RTMP 连接,对应在程序里面,你需要调用 RTMP 类库的 Connect 函数,显示创建一个连接。

之所以要建立单独的 RTMP 连接,是因为客户端与服务器需要商量一些事情,以保证后续的传输能正常进行。主要是两个事情,一个是 RTMP 协议的版本号,如果客户端、服务器的版本号不一致,则不能工作;另一个就是时间戳,视频播放中,时间是很重要的,后面的数据流互通的时候,经常要带上时间戳的差值,因而一开始双方就要知道对方的时间戳。下面我们简单来介绍下 RTMP 连接的建立和数据传输的细节。

首先,客户端发送 C0 表示自己的版本号,不必等对方的回复,然后发送 C1 表示自己的时间戳。服务器只有在收到 C0 的时候,才会返回 S0,表明自己的版本号,如果版本不匹配,可以断开连接。服务器发送完 S0 后,也不用等什么,就直接发送自己的时间戳 S1。客户端收到 S1 的时候,发一个知道了对方时间戳的 ACK C2。同理服务器收到 C1 的时候,发一个知道了对方时间戳的 ACK S2。于是,握手完成,连接建立成功。

握手之后,双方需要互相传递一些控制信息,例如 Chunk 块的大小、窗口大小等。真正传输数据的时候,还需要创建一个 Stream,然后通过这个 Stream 来推流。前面我们讲推流之前会将视频编码,编码后的视频会变成由一个个称之为 NALU 单元构成的二进制流,推流的过程,就是将这些 NALU 放到 Message 报文里发送。

在收发数据的时候,和 HTTP 类似,RTMP 还会将 Message 报文拆分成 Chunk 进行发送,Chunk 的发送是有序的,一个 Chunk 发送完成之后,才能开始下一个 Chunk 的发送。每个 Chunk 中都带有 Message ID,表示属于哪个 Message,接收端也会按照这个 ID 将 Chunk 组装成 Message。前面连接建立之后,设置的 Chunk 块大小指的就是这个 Chunk,这样做的好处是将大的消息变为小的块发送,以便在低带宽的情况下,减少网络拥塞。

二进制视频流数据就是这样源源不断到达流媒体服务器。主播客户端推送的时序图如下:

1.jpeg

服务器接收主播推送视频流的环节叫做接流,获取到视频流后还要对视频流进行处理,以适配不同客户端的各种协议,从而方便观看直播的观众拉取到本地观看,这个过程就是流处理。

观看直播的观众同样可以通过 RTMP 协议从流媒体服务器上拉取视频流(当然也可以通过上篇分享介绍的其它流媒体协议拉取),这个过程叫做拉流,但是如果同时观看直播的观众很多,都去同一个地方拉取,还是视频这种很耗带宽的资源,服务器压力会很大,也会导致时延比较长(观众直观的感受就是卡),所以需要通过 CDN(分发网络) 提升直播流畅度,关于视频直播的 CDN 实现方案,我们放到下一篇分享详细介绍。

观众的客户端拉取服务器的视频流与主播推送视频流类似,先要建立 RTMP 连接,然后创建一个 Stream 用于拉流,拉流的时候,会获取到解码参数以及由 NALU 组成的一个个帧,将解码之后的帧交给播放器播放,就可以看到直播视频画面了。

观众客户端拉流的时序图如下:

1.jpeg

至此,我们就大致介绍完了从主播客户端推流到服务器处理,再到观众客户端拉流的视频直播整体技术实现方案。

0

评论区