直播传输协议

bridge
2022-11-17 / 0 评论 / 0 点赞 / 1,726 阅读 / 10,862 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2022-11-17,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

前言

随着移动网络网速的提升与资费的降低,视频直播作为一个新的娱乐方式已经被越来越多的用户逐渐接受。特别是最近这几年,视频直播已经不仅仅被运用在传统的秀场、游戏类板块,更是作为电商的一种新模式得到迅速成长。

一、直播来源

1.1、视频&互联网

我们先来看看,视频在互联网上的演进过程,如下:

一开始人们看电影都是需要先把视频文件先下载到自己电脑,在用播放器进行播放观看。随着网络带宽的改善以及视频编码技术的不断迭代,出现了可以在线观看电影的视频网站(如优酷、爱奇艺等)和新兴的直播平台(如YY、斗鱼等)。

1.2、推拉流

什么是推流?

推流,指的是把采集阶段封包好的内容传输到服务器的过程。其实就是将现场的视频信号传到网络的过程。

“推流”对网络要求比较高(特别是带宽上行速度),如果网络不稳定,直播效果就会很差,观众观看直播时就会发生卡顿等现象,观看体验很是糟糕。

要想用于推流还必须把音视频数据使用传输协议进行封装,变成流数据。常用的流传输协议有RTSP、RTMP、HLS等,使用RTMP传输的延时通常在1–3秒,对于手机直播这种实时性要求非常高的场景,RTMP也成为手机直播中最常用的流传输协议。

在直播场景中,网络不稳定是非常常见的,通常是通过主播端和播放端设置缓存,让码率均匀。

另外,针对实时变化的网络状况,动态码率和帧率也是最常用的策略。

视频一开始会由两个端采集,一个是视频输入口,是一个音频输入口。然后,采集的数据会分别进行相关处理,简而言之就是,将视频/音频流,通过一定的手段转换为比特流。最终,将这里比特流以一定顺序放到一个盒子里进行存放,从而生成我们最终所看到的,比如,mp4/mp3/flv 等等音视频格式

什么是拉流?

拉流是指服务器已有直播内容,根据协议类型(如RTMP、RTP、RTSP、HTTP等),与服务器建立连接并接收数据,进行拉取的过程。

拉流端的核心处理在播放器端的解码和渲染,在互动直播中还需集成聊天室、点赞和礼物系统等功能。

拉流端现在支持RTMP、HLS、HDL(HTTP-FLV)三种协议,其中,在网络稳定的情况下,对于HDL协议的延时控制可达1s,完全满足互动直播的业务需求。

二、传输协议

2.1、HTTP-FLV

HTTP-FLV是什么

FLV (Flash Video) 是 Adobe 公司推出的另一种视频格式,是一种在网络上传输的流媒体数据存储容器格式。其格式相对简单轻量,不需要很大的媒体头部信息。整个FLV由 The FLV Header, The FLV Body 以及其它 Tag 组成。因此加载速度极快。采用 FLV 格式封装的文件后缀为 .flv。而HTTP-FLV 即将流媒体数据封装成 FLV 格式,然后通过 HTTP 协议传输给客户端。

HTTP-FLV协议的优点主要是

  1. 穿墙:很多防火墙会墙掉RTMP,但是不会墙HTTP,因此HTTP FLV出现奇怪问题的概率很小。
  2. 调度:RTMP也有个302,可惜是播放器as中支持的,HTTP-FLV流就支持302方便CDN纠正DNS的错误。
  3. 容错:SRS的HTTP FLV回源时可以回多个,和RTMP一样,可以支持多级热备。
  4. 简单:FLV是最简单的流媒体封装,HTTP是最广泛的协议,这两个组合在一起维护性更高,比RTMP简单多了。

开源库

  • flv.js:现在浏览器都逐渐不支持Flash player,所以flv.js将FLV文件流转换为ISO BMFF(碎片MP4)片段,然后通过媒体源扩展API将mp4片段输入HTML5element。
  • mpegts.js:flv.js原作者因去日进修离开bilibili, mpegts.js 基于 flv.js 改造而来。(后知flv.js竟然是个高中生写的)

HTTP-FLV技术实现

HTTP协议中有个约定:content-length字段,http的body部分的长度;
服务器回复http请求的时候如果有这个字段,客户端就接收这个长度的数据然后就认为数据传输完成了,如果服务器回复http请求中没有这个字段,客户端就一直接收数据,直到服务器跟客户端的socket连接断开。

http-flv直播就是利用第二个原理,服务器回复客户端请求的时候不加content-length字段,在回复了http内容之后,紧接着发送flv数据,客户端就一直接收数据了。

HTTP-FLV在直播中是通过走HTTP长连接的方式,通过分块传输向请求端传递FLV封包数据。

在直播中,我们通过HTTP-FLV协议的拉流地址可以拉取到一段chunked数据。

打开文件后可以读取到16进制的文件流,通过和FLV包结构对比,可以发现这些数据就是我们需要的FLV数据。

首先开头是头部信息:464C56转换ASCII码后是FLV三个字符,01指的是版本号,05转换为2进制后第6位和第8位分别代表是否存在音频和视频,09代表头部长度占了几个字节。

后续就是正式的音视频数据:是通过一个个的FLV TAG进行封装,每一个TAG也有头部信息,标注这个TAG是音频信息、视频信息还是脚本信息。我们通过解析TAG就可以分别提取音视频的压缩编码信息。

FLV这一种格式在video中并不是原生支持的,我们要播放这一种格式的封包格式需要通过MSE对影视片的压缩编码信息进行解码,因此需要浏览器能够支持MSE这一API。由于HTTP-FLV的传输是通过长连接传输文件流的形式,需要浏览器支持Stream IO或者fetch,对于浏览器的兼容性要求会比较高。

FLV在延迟问题上相比切片播放的HLS会好很多,目前看来FLV的延迟主要是受编码时设置的GOP长度的影响。

FLV格式详解:https://www.jianshu.com/p/7ffaec7b3be6

2.2、RTMP

RTMP协议实际可以与HTTP-FLV协议归做同一种类型。

他们的封包格式都是FlV,但HTTP-FLV使用的传输协议是HTTP,RTMP拉流使用RTMP作为传输协议。

RTMP是Adobe公司基于TCP做的一套实时消息传输协议,经常与Flash播放器匹配使用。

RTMP协议的优缺点非常明显。

RTMP协议的优点主要是

  1. 首先和HTTP-FLV一样,延迟比较低;
  2. 其次它的稳定性非常好,适合长时间播放(由于播放时借用了Flash player强大的功能,即使开多路流同时播放也能保证页面不出现卡顿,很适合监控等场景)。

但是Flash player目前在web端属于墙倒众人推的境地,主流浏览器渐渐都表示不再支持Flash player插件,在MAC上使用能够立刻将电脑变成烧烤用的铁板,资源消耗很大。在移动端H5基本属于完全不支持的状态,兼容性是它最大的问题。

2.3、HLS

HLS是什么

HLS(Http Live Streaming)是由Apple公司定义的用于实时流传输的协议,HLS基于HTTP协议实现,传输内容包括两部分,一是M3U8描述文件,二是TS媒体文件。

当开始一个流媒体会话时,客户端会加载一个包含元数据的 extended M3U(m3u8) playlist 文件,作用是用于寻找可用的流媒体。和RTP(实时传输协议)不同的是,HLS之请求基本的HTTP报文,可以穿过任何允许HTTP数据通过的防火墙或者代理服务器。​它也很容易使用内容分发网络来传输媒体流。

HLS主要的应用场景

  • 跨平台:PC/Android/H5都本身或通过插件可以支持HLS。比如:hls.js、videojs-contrib-hls等。
  • IOS:IOS上最稳定的当然是HLS,稳定性不差于RTMP在PC-flash上的表现。
  • 简单:作为流媒体协议十分简单,苹果对其也支持的非常完善,并且安卓也在努力完善对其的支持。
  • 视频点播:切片播放的特性特别适用于点播播放中视频清晰度、多语种的热切换。比如我们播放一个视频,起初选择的是标清视频播放,当我们看了一半觉得不够清晰,需要换成超清的,这时候只需要将标清的M3U8文件替换成超清的M3U8文件,当我们播放到下一个TS节点时,视频就会自动替换成超清的TS文件,不需要对视频做重新初始化。

开源库

HLS协议详解

HLS是提供一个m3u8地址,Apple的Safari浏览器直接就能打开m3u8地址,譬如:

http://host/live/livestream.m3u8

HLS协议规定

  • 视频的封装格式是TS。
  • 视频的编码格式为H264,音频编码格式为MP3、AAC或者AC-3。
  • 除了TS视频文件本身,还定义了用来控制播放的m3u8文件(文本文件)。

HLS协议说明

HLS的m3u8,是一个ts的列表,也就是告诉浏览器可以播放这些ts文件,譬如:

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-ALLOW-CACHE:YES
#EXT-X-TARGETDURATION:13
#EXT-X-MEDIA-SEQUENCE:430
#EXT-X-PLAYLIST-TYPE:VOD
#EXTINF:11.800
news-430.ts
#EXTINF:10.120
news-431.ts
#EXT-X-DISCONTINUITY
#EXTINF:11.952
news-430.ts
#EXTINF:12.640
news-431.ts
#EXTINF:11.160
news-432.ts
#EXT-X-DISCONTINUITY
#EXTINF:11.751
news-430.ts
#EXTINF:2.040
news-431.ts
#EXT-X-ENDLIST
  • EXTM3U:每个M3U文件第一行必须是这个tag,提供标示作用
  • EXT-X-VERSION:用以标示协议版本。这里是3, 那么这里用的就是HLS协议第三个版本,此标签只能有0或1个,不写代表使用版本1
  • EXT-X-TARGETDURATION:所有切片的最大时长,有些Apple设备这个参数不正确会无法播放。
  • EXT-X-MEDIA-SEQUENCE:切片的开始序号。每一个切片都有唯一的序号,相邻之间序号+1。这个编号会继续增长,保证流的连续性。
  • EXTINF:ts 切片的实际时长。duration : 媒体持续时间
  • EXT-X-PLAYLIST-TYPE:类型,vod 表示点播。
  • EXT-X-ENDLIST:文件结束符号。表示不再向播放列表文件添加媒体文件。

2.4、MPEG-DASH

MPEG-DASH是什么

DASH的全称是Dynamic Adaptive Streaming over HTTP。DASH是一种新的视频传输协议,由微软、苹果、Adobe等公司共同主导完成,该标准于2012年制定完成,是一种自适应码率的流媒体技术,其主要特点是视频切片和基于HTTP传输,可以利用现有的HTTP网络架构(专门的服务器和CDN等)传输流媒体。

DASH将媒体文件切分成小的视频片段,每个小的片段都编码成几种不同的码率,甚至会存在不同的分辨率,以满足不同客户端的网络需求。比如,DASH可以根据当前的网络状况,来下载合适的码率的视频片段,从而避免停顿或重新缓冲。

如上图所示,DASH整个系统可以分成服务器和客户端两个模块,视频片段的封装格式,支持MPEG-2 TS、MP4等多种格式,并且,可以使用h.265,H.264,AVS,VP9等多种编码器进行编码。服务器端通过MPD(media presentation description)文件来描述媒体信息,包括,是直播还是点播、视频切片的大小,有几种不同的码率以及每个视频片段对应的URL地址等。客户端通过MPD文件就可以知道整个将要播放的媒体信息。

开源库

MPEG-DASH协议详解

一个常见的mpd文件如下,可以清楚的看到各个字段是如何描述的。

<!--
 MPD file Generated with GPAC version 0.5.1-DEV-rev5379  on 2014-09-13T02:57:14Z
-->
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" minBufferTime="PT1.500000S" type="static" mediaPresentationDuration="PT0H12M14.17S" profiles="urn:mpeg:dash:profile:isoff-live:2011">
    <ProgramInformation moreInformationURL="http://gpac.sourceforge.net">
        <Title>
        dashed/TearsOfSteel_4s_simple_2014_05_09.mpd generated by GPAC
        </Title>
    </ProgramInformation>
    <Period duration="PT0H12M14.17S">
        <AdaptationSet segmentAlignment="true" maxWidth="1920" maxHeight="1080" maxFrameRate="24" par="16:9">
            <Role schemeIdUri="urn:mpeg:dash:role:2011" value="main"/>
            <SegmentTemplate timescale="24000" media="tos_$Bandwidth$bps/TearsOfSteel_4s_$Number$.m4s" startNumber="1" duration="96000" initialization="tos_$Bandwidth$bps/TearsOfSteel_4s__init.mp4"/>
            <Representation id="1920x1080 10.0Mbps" mimeType="video/mp4" codecs="avc1.4d4029" width="1920" height="1080" frameRate="24" sar="1:1" startWithSAP="1" bandwidth="10045361"/>
            <Representation id="1920x1080 6.0Mbps" mimeType="video/mp4" codecs="avc1.4d4028" width="1920" height="1080" frameRate="24" sar="1:1" startWithSAP="1" bandwidth="6025488"/>
            <Representation id="1920x1080 4.0Mbps" mimeType="video/mp4" codecs="avc1.4d4028" width="1920" height="1080" frameRate="24" sar="1:1" startWithSAP="1" bandwidth="4011653"/>
            <Representation id="1920x1080 3.0Mbps" mimeType="video/mp4" codecs="avc1.4d4028" width="1920" height="1080" frameRate="24" sar="1:1" startWithSAP="1" bandwidth="3004351"/>
            <Representation id="1280x720 2.4Mbps" mimeType="video/mp4" codecs="avc1.4d401f" width="1280" height="720" frameRate="24" sar="1:1" startWithSAP="1" bandwidth="2416099"/>
            <Representation id="1280x720 1.5Mbps" mimeType="video/mp4" codecs="avc1.4d401f" width="1280" height="720" frameRate="24" sar="1:1" startWithSAP="1" bandwidth="1506971"/>
            <Representation id="640x360 807.0kbps" mimeType="video/mp4" codecs="avc1.4d401e" width="640" height="360" frameRate="24" sar="1:1" startWithSAP="1" bandwidth="807450"/>
            <Representation id="640x360 505.0kbps" mimeType="video/mp4" codecs="avc1.4d401e" width="640" height="360" frameRate="24" sar="1:1" startWithSAP="1" bandwidth="504741"/>
            <Representation id="480x270 253.0kbps" mimeType="video/mp4" codecs="avc1.4d4015" width="480" height="270" frameRate="24" sar="1:1" startWithSAP="1" bandwidth="253270"/>
        </AdaptationSet>
        <AdaptationSet segmentAlignment="true" bitstreamSwitching="true">
            <AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
            <Role schemeIdUri="urn:mpeg:dash:role:2011" value="main"/>
            <SegmentTemplate timescale="48000" media="tos_$Bandwidth$bps/TearsOfSteel_4s_$Number$.m4s" startNumber="1" duration="191472" initialization="TearsOfSteel_4s_simple_2014_05_09_set2_init.mp4"/>
            <Representation id="audio 48000kHz 66.0kbps" mimeType="audio/mp4" codecs="mp4a.40.2" audioSamplingRate="48000" startWithSAP="1" bandwidth="65509"/>
            <Representation id="audio 48000kHz 130.0kbps" mimeType="audio/mp4" codecs="mp4a.40.2" audioSamplingRate="48000" startWithSAP="1" bandwidth="129510"/>
            <Representation id="audio 48000kHz 192.0kbps" mimeType="audio/mp4" codecs="mp4a.40.2" audioSamplingRate="48000" startWithSAP="1" bandwidth="191511"/>
        </AdaptationSet>
        <AdaptationSet segmentAlignment="true" lang="eng">
            <Role schemeIdUri="urn:mpeg:dash:role:2011" value="subtitle"/>
            <SegmentTemplate timescale="1000" media="tos_$Bandwidth$bps/TearsOfSteel_4s_$Number$.m4s" startNumber="1" duration="4000" initialization="tos_$Bandwidth$bps/TearsOfSteel_4s__init.mp4"/>
            <Representation id="subtitles" mimeType="video/mp4" codecs="wvtt" startWithSAP="1" bandwidth="101"/>
        </AdaptationSet>
    </Period>
</MPD>

MPD(media presentation description)是一种XML文件,描述了视频的所有信息,在白皮书的Part1部分有详细的介绍。MPD通过不同的标签,层次化的描述媒体信息,层次结构可以见下图,

MPD主要标签简介:

  • Period : 一段连续的视频片段,标注了视频的时长信息,也可以看做是更新mpd文件的最长时长。
  • AdaptationSet : 每个Period包含多个音频、视频内容的集合。视频内容可以是音频视频混合的,这样一个Adaptation Set就足够了。或者,也可以用基本流(Elemental Stream),以支持多语言音频功能。
  • Representaiton : 每一个Adaptation Set含多个Representation,每个Representation代表一个独立的流。比如,一个Representation可以是640X480 @ 800Kbps的一个流,而另一个Representation 代表640X480 @ 500kbps的一个流。通过这样不同的Representation, DASH来达到自适应码率的目的。Representaiton这个标签描述了视频分辨率、码率、编码方式、帧率信息等。
  • Segment :每一个Representation由一系列的媒体段(Segment)组成。

实际播放的时候,视频会在一个AdaptationSet中的不同Representaiton 之间切换码率,会依次请求该Representaiton下不同Segment序列。

如何选择最优的视频直播传输协议

视频直播协议选择非常关键的两点,即低延时和更优的兼容性。

首先从延时角度考虑:

不考虑云端转码以及上下行的消耗,HLS和MPEG-DASH通过将切片时长减短,延时在10秒左右;RTMP和FLV理论上延时相当,在2-3秒。因此在延时方面HLS ≈ DASH > RTMP ≈ FLV。

从兼容性角度考虑:

HLS > FLV > RTMP,DASH由于一些项目历史原因,并且定位和HLS重复了,暂时没有对其兼容性做一个详尽的测试,被推出了选择的考虑范围。

综上所述:

我们可以通过动态判断环境的方式,选择当前环境下可用的最低延迟的协议。大致的策略就是优先使用HTTP-FLV,使用HLS作为兜底,在一些特殊需求场景下通过手动配置的方式切换为RTMP。

参考:

0

评论区