一、H5直播基础
1.典型的直播流程:
录制->编码->网络传输(推流->服务器处理->CDN分发)->解码->播放
2.MSE 意义
初识 HTML5 video 标签和MSE媒体源扩展
以往用户在浏览网页内容尤其是视频内容时,需要使用像Adobe Flash或是微软的Silverlight这样的插件,播放视音频内容即使是电脑小白也知道,需要媒体播放器的支持,前面提到的插件就是起到媒体播放器的作用。但是使用插件这样的方式是很不便捷且很不安全的,一些不法分子会在这些插件上动手脚。因此W3C的最新的HTML5标准中,定义了一系列新的元素来避免使用插件,其中就包含了标签这一大名鼎鼎的元素。
正是使用了标签,支持HTML5的浏览器得以实现无插件就原生支持播放媒体内容,但是对媒体内容的格式有所限制。说到媒体内容,就自然地需要谈到媒体的封装格式和编码格式,这里总结一下,原视频文件通过编码来压缩文件大小,再通过封装将压缩视音频、字幕组合到一个容器内,具体内容请大家自行查阅。
我们可以把标签看做拥有解封装和解码功能的浏览器自带播放器。随着视频点播、直播等视频业务的发展,视频通过流媒体传输协议(目前常用的有两种,MPEG-DASH和Apple的HLS)从服务器端分发给客户端,媒体内容进一步包含在一层传输协议中,这样就无法识别了。以HLS为例,将源文件内容分散地封装到了一个个TS文件中。
仅靠标签无法识别这样的TS文件,那么就引入了MSE拓展来帮助浏览器识别并处理TS文件,将其变回原来可识别的媒体容器格式,这样就可以识别并播放原来的文件了。那么支持HTML5的浏览器就相当于内置了一个能够解析流协议的播放器。
比如在hls.js 源码解读【1】中,介绍的hls.js
hls实际会先通过 ajax(loader 是可以完成自定义的) 请求 m3u8文件,然后会读取到文件的分片列表,以及视频的编码格式,时长等。随后会按照顺序(非 seek )去对分片进行请求,这些也是通过 ajax 请求二进制的文件,然后借助 Media Source Extensions 将 buffer 内容进行合流,然后组成一个可播的媒体资源文件。
3.video/audio支持的视频格式
video: mp4、webm、ogv
audio: mp3 wav ogg
1.一般承载视频的格式,比如flv、avi、mpg、vob、mov、mp4等。而视频是用什么方式进行编解码的,则与Codec相关。举个栗子,MP4格式根据编解码的不同,又分为nMP4、fMP4。nMP4是由嵌套的Boxes 组成,fMP4格式则是由一系列的片段组成,因此只有后者不需要加载整个文件进行播放。
值得注意的是: mp4规范第10章就是 h264, mp4和h264可以等同
2.Codec:多媒体数字信号编码解码器,能够对音视频进行压缩(CO)与解压缩( DEC ) 。CODEC技术能有效减少数字存储占用的空间,在计算机系统中,使用硬件完成CODEC可以节省CPU的资源,提高系统的运行效率。
4.常用视频编解码格式:
AVI、MOV、MPEG/MPG/DAT、Real Media、QuickTime、ASF、H264/X264/AVC、H.263、MPEG-1、MPEG-2、MPEG-4、Sorensen Spark、VC-1、JPEG、RV、DivX、On2 TrueMotion VP6
5.常用音频编解码格式:
PCM、WAV、OGG、APE、AAC、MP3、Vorbis、Opus...
二.常用直播协议: RTMP、HLS、HTTP-FLV、WebSocket-FLV
1.RTMP协议
- 基于TCP
- adobe垄断,国内支持度高
- 浏览器端依赖Flash进行播放
- 2~5秒的延迟 RTMP,全称 Real Time Messaging Protocol,即实时消息传送协议。Adobe 公司为 Flash 播放器和服务器之间音视频数据传输开发的私有协议。工作在 TCP 之上的明文协议,默认使用端口 1935。协议中的基本数据单元成为消息(Message),传输的过程中消息会被拆分为更小的消息块(Chunk)单元。最后将分割后的消息块通过 TCP 协议传输,接收端再反解接收的消息块恢复成流媒体数据。
RTMP 主要有以下几个优点:RTMP 是专为流媒体开发的协议,对底层的优化比其它协议更加优秀,同时它 Adobe Flash 支持好,基本上所有的编码器(摄像头之类)都支持 RTMP 输出。现在 PC 市场巨大,PC 主要是 Windows,Windows 的浏览器基本上都支持 Flash。另外RTMP适合长时间播放,曾经有过测试,联系 100 万秒,即 10 天多连续播放没有出现问题。最后 RTMP 的延迟相对较低,一般延时在 1-3s 之间,一般的视频会议,互动式直播,完全是够用的。
当然 RTMP 并没有尽善尽美,它也有不足的地方。一方面是它是基于 TCP 传输,非公共端口,可能会被防火墙阻拦;另一方面,也是比较坑的一方面是 RTMP 为 Adobe 私有协议,很多设备无法播放,特别是在 iOS 端,需要使用第三方解码器才能播放。
2.HLS 协议
- Http Live Streaming,苹果提出的基于HTTP的流媒体传输协议
- HTML5直接支持(video),适合APP直播,PC断只有Safari、Edge支持
- 必须是H264+AAC编码
- 因为传输的是切割后的音视频片段,导致内容延时较大 HLS (HTTP Live Streaming) 是苹果公司基于 HTTP 的流媒体传输协议。 主要应用于 iOS 设备,包含(iPhone, iPad, iPod touch) 以及 Mac OSX 提供音视频直播服务和录制内容(点播)等服务。
相对于常见的流媒体协议,HLS 最大的不同在于它并不是一下请求完整的数据流。它会在服务器端将流媒体数据切割成连续的时长较短的 ts 小文件,并通过 M3U8 索引文件按序访问 ts 文件。客户端只要不停的按序播放从服务器获取到的文件,从而实现播放音视频。
相较 RTMP 而言,使用 HLS 在 HTML5 页面上实现播放非常简单:
直接:
或者:
HLS 的优势:
-
Apple 的全系列产品支持:由于 HLS 是苹果提出的,所以在 Apple 的全系列产品包括 iPhone、 iPad、safari 都不需要安装任何插件就可以原生支持播放 HLS, 现在 Android 也加入了对 HLS 的支持。
-
穿透防火墙。基于 HTTP/80 传输,有效避免防火墙拦截
-
性能高。通过 HTTP 传输, 支持网络分发,CDN 支持良好,且自带多码率自适应,Apple 在提出 HLS 时,就已经考虑了码流自适应的问题。 HLS 的劣势:
-
实时性差,延迟高。HLS 的延迟基本在 10s+ 以上
-
文件碎片。特性的双刃剑,ts 切片较小,会造成海量小文件,对存储和缓存都有一定的挑战
3.flv.js
- Bilibli开源,解析flv数据,通过MSE封装成fMP4喂给video标签
- 编码为H264+AAC
- 使用HTTP的流式IO(fetch或stream)或WebSocket协议流式的传输媒体内容
- 2~5秒的延迟,首帧比RTMP更快 FLV (Flash Video) 是 Adobe 公司推出的另一种视频格式,是一种在网络上传输的流媒体数据存储容器格式。 其格式相对简单轻量,不需要很大的媒体头部信息。整个 FLV 由 The FLV Header, The FLV Body 以及其它 Tag 组成。因此加载速度极快。采用 FLV 格式封装的文件后缀为 .flv。
HTTP-FLV 基于HTTP
流式IO传输FLV,依赖浏览器支持播放FLV。即将流媒体数据封装成 FLV 格式,然后通过 HTTP 协议传输给客户端。
HTTP-FLV 依靠 MIME 的特性,根据协议中的 Content-Type 来选择相应的程序去处理相应的内容,使得流媒体可以通过 HTTP 传输。相较于 RTMP 协议,HTTP-FLV 能够好的穿透防火墙,它是基于 HTTP/80 传输,有效避免被防火墙拦截。除此之外,它可以通过 HTTP 302 跳转灵活调度/负载均衡,支持使用 HTTPS 加密传输,也能够兼容支持 Android,iOS 的移动端。\
WebSocket-FLV: 基于WebSocket
传输FLV,依赖浏览器支持播放FLV。WebSocket
建立在HTTP
之上,建立WebSocket
连接前还要先建立HTTP
连接。
4.流媒体协议 RTMP, HTTP-FLV, HLS 简单对比
在支持浏览器的协议里,延迟排序是:
RTMP = HTTP-FLV = WebSocket-FLV < HLS
而性能排序恰好相反:
RTMP > HTTP-FLV = WebSocket-FLV > HLS
也就是说延迟小的性能不好。
可以看出在浏览器里做直播,使用HTTP-FLV协议是不错的,性能优于RTMP+Flash,延迟可以做到和RTMP+Flash一样甚至更好。
5. flv.js 简介
flv.js是来自Bilibli的开源项目。它解析FLV文件喂给原生HTML5 Video标签播放音视频数据,使浏览器在不借助Flash的情况下播放FLV成为可能。
flv.js 优势
- 由于浏览器对原生Video标签采用了硬件加速,性能很好,支持高清。
- 同时支持录播和直播
- 去掉对Flash的依赖
flv.js 限制
- FLV里所包含的视频编码必须是
H.264
,音频编码必须是AAC
或MP3
, IE11和Edge浏览器不支持MP3音频编码,所以FLV里采用的编码最好是H.264+AAC,这个让音视频服务兼容不是问题。 - 对于录播,依赖
原生HTML5 Video标签
和 Media Source Extensions API - 对于直播,依赖录播所需要的播放技术,同时依赖
HTTP FLV
或者WebSocket
中的一种协议来传输FLV。其中HTTP FLV
需通过流式IO去拉取数据,支持流式IO的有fetch或者stream flv.min.js
文件大小 164Kb,gzip后 35.5Kb,flash播放器gzip后差不多也是这么大。- 由于依赖
Media Source Extensions
,目前所有iOS和Android4.4.4以下里的浏览器都不支持,也就是说目前对于移动端flv.js基本是不能用的。
flv.js依赖的浏览器特性兼容列表
- HTML5 Video
- Media Source Extensions
- WebSocket
- HTTP FLV: fetch 或 stream
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.js兼容方案
由于目前flv.js兼容性还不是很好,要用在产品中必要要兼顾到不支持flv.js的浏览器。兼容方案如下:
PC端
- 优先使用 HTTP-FLV,因为它延迟小,性能也不差1080P都很流畅。
- 不支持 flv.js 就使用 Flash播放器播 RTMP 流。Flash兼容性很好,但是性能差默认被很多浏览器禁用。
- 不想用Flash兼容也可以用HLS,但是PC端只有Safari支持HLS
移动端
- 优先使用 HTTP-FLV,因为它延迟小,支持HTTP-FLV的设备性能运行 flv.js 足够了。
- 不支持 flv.js 就使用 HLS,但是 HLS延迟非常大。
- HLS 也不支持就没法直播了,因为移动端都不支持Flash。
flv.js实战
说了这么多介绍与原理,接下来教大家如何用flv.js搭建一个完整的直播系统。
我已经搭建好了一个demo可以供大家体验。
搭建音视频服务
主播推流到音视频服务,音视频服务再转发给所有连接的客户端。为了让你快速搭建服务推荐我用go语言实现的livego,因为它可以运行在任何操作系统上,对Golang感兴趣?请看Golang 中文学习资料汇总。
- 下载livego,注意选对你的操作系统和位数。
- 解压,执行
livego
,服务就启动好了。它会启动RTMP(1935端口)服务用于主播推流,以及HTTP-FLV(7001端口)服务用于播放。
实现播放页
在react体系里使用react flv.js 组件reflv 快速实现。
先安装npm i reflv
,再写代码:
import React, { PureComponent } from 'react';
import Reflv from 'reflv';
export class HttpFlv extends PureComponent {
render() {
return (
<Reflv
url={`http://localhost:7001/live/test.flv`}
type="flv"
isLive
cors
/>
)
}
}
让以上代码在浏览器里运行。这是你还看不到直播,是因为还没有主播推流。
- 你可以使用OBS来推流,注意要配置好OBS:
- 也可以使用ffmpeg来推流,推流命令
ffmpeg -f avfoundation -i "0" -vcodec h264 -acodec aac -f flv rtmp://localhost/live/test
flv.js延迟优化
按照上面的教程运行起来的直播延迟大概有3秒,经过优化可以到1秒。在教你怎么优化前先要介绍下直播运行流程:
-
主播端在采集到一段时间的音视频原数据后,因为音视频原数据庞大需要先压缩数据:
- 通过H264视频编码压缩数据数据
- 通过PCM音频编码压缩音频AAC数据
-
压缩完后再通过FLV容器格式封装压缩后的数据,封装成一个FLV TAG
-
再把FLV TAG通过RTMP协议推流到音视频服务器,音视频服务器再从RTMP协议里解析出FLV TAG。
-
音视频服务器再通过HTTP协议通过和浏览器建立的长链接流式把FLV TAG传给浏览器。
-
flv.js 获取FLV TAG后解析出压缩后的音视频数据喂给Video播放。 知道流程后我们就知道从哪入手优化了:
-
主播端采集时收集了一段时间的音视频原数据,它专业的叫法是GOP。缩短这个收集时间(也就是减少GOP长度)可以优化延迟,但这样做的坏处是导致视频压缩率不高,传输效率低。
-
关闭音视频服务器的I桢缓存可以优化延迟,坏处是用户看到直播首屏的时间变大。
-
减少音视频服务器的buffer可以优化延迟,坏处是音视频服务器处理效率降低。
-
减少浏览器端flv.js的buffer可以优化延迟,坏处是浏览器端处理效率降低。
-
浏览器端开启flv.js的Worker,多线程运行flv.js提升解析速度可以优化延迟,这样做的flv.js配置代码是:
{
enableWorker: true,
enableStashBuffer: false,
stashInitialSize: 128,// 减少首桢显示等待时长
}
这里是优化后的完整代码
6.总结H5直播实际使用方案测试中:
B站的flv.js在web端和安卓浏览器表现良好,但值得注意的是在ios浏览器上无法播放flv,原因:
flvjs.isSupported()在ios上是值是undefined, 也就是mseFlvPlayback 和 mseLiveFlvPlayback属性没有,
也就是flv对应的 Media Source Extensions 在ios上没有
端 | 浏览器/小程序 | FLV | M3U8 | RTMP |
---|---|---|---|---|
PC端 | Chrome | 34及以上版本 | 34及以上版本 | ❎ ( 需flash ) |
Firefox | 49及以上版本 | 49及以上版本 | ❎ ( 需flash ) | |
IE | *IE11及以上版本 *Windows8.1及以上版本 | *IE11及以上版本 *Windows8.1及以上版本 | ❎ ( 需flash ) | |
Edge | ✅ | ✅ | ❎ ( 需flash ) | |
Opera | ✅ | ✅ | ❎ ( 需flash ) | |
Safari | 8及以上版本 | 8及以上版本 | ❎ ( 需flash ) | |
移动端 | Android | ✅ | ✅ | ❎ |
Ios | ❎ | ✅ | ❎ | |
小程序 | Android/Ios | *video可播 *live-player可播 | *video可播 *live-player无法播 | *video无法播 *live-player可播 |
Web-App | Android H5/Ios H5 | APLCloud支持 | APLCloud支持 | APLCloud支持 |
值得注意的是 live-player拉流专用组件标签: 官网仅支持flv和rtmp |
综上目前关于H5,在直播方面, 移动端ios上用hls, 其余都可用flv, 在不涉及直播点播方面用hls即可 浏览器用mse硬解只能做到这样, 加上wasm解码器来软解可以实现在ios也用flv, 并且因WebRTC能正在做到低延时, 高并发可用WebRTC + (flv+hls / wasm软解flv,hls)兼容方案, 做到向下兼容降级.
备注: 更新中,本调研摘录多个作者, 个人使用记录下
本网站是一个以CSS、JavaScript、Vue、HTML为核心的前端开发技术网站。我们致力于为广大前端开发者提供专业、全面、实用的前端开发知识和技术支持。 在本网站中,您可以学习到最新的前端开发技术,了解前端开发的最新趋势和最佳实践。我们提供丰富的教程和案例,让您可以快速掌握前端开发的核心技术和流程。 本网站还提供一系列实用的工具和插件,帮助您更加高效地进行前端开发工作。我们提供的工具和插件都经过精心设计和优化,可以帮助您节省时间和精力,提升开发效率。 除此之外,本网站还拥有一个活跃的社区,您可以在社区中与其他前端开发者交流技术、分享经验、解决问题。我们相信,社区的力量可以帮助您更好地成长和进步。 在本网站中,您可以找到您需要的一切前端开发资源,让您成为一名更加优秀的前端开发者。欢迎您加入我们的大家庭,一起探索前端开发的无限可能!