H5与PC直播方案

研究了几天的b站的直播,理清了大概的思路,做下总结(文尾附源码)。

本文用的技术:vuejs,flvjs,vue-video-player,videojs-contrib-hls,阿里云视频web播放器。

本文以B站为例来做解析。

开始之前

正式介绍一大堆专业名词之前,先问几个问题。

  • 为什么b站要做h5视频播放器。
  • 为什么chrome默认禁止了flash。

好吧,带着这两个问题我要开始吹水了。。

为什么b站要做h5视频播放器

在B站推出H5播放器之前,B站PC端的视频使用的可能是RTMP或HTTP-FLV协议(这两个协议功能类似,只是后者没有专利限制),视频格式是flv。RTMP协议是Adobe公司提出的,要使用RTMP或HTTP-FLV协议来传输播放视频,只能使用Flash播放器。

这样想其实没什么问题,那就用flash播放器咯,可是,有个很致命的问题,所有ios设备都不支持flash,也就是说所有iphone、ipad都不支持RTMP或HTTP-FLV协议的视频流,但是支持苹果自家的hls协议,而在ios设备上,h5支持播放hls协议的视频流,所以,B站要留住ios用户,使用h5播放器是大势所趋。

至于为什么苹果不支持flash,归结起来有几个原因:

  1. 耗电,在mac使用会明显发热。
  2. h5完全开源,flash是商业化产品。
  3. flash对触摸支持不够好。
  4. 安全性问题,内置flash意味着有可能引入相关漏洞,主动权在adobe手上。
  5. 基于Flash平台的应用非常丰富,很有可能威胁到苹果自家的AppStore。

有兴趣的可以去看看知乎上这个问题:苹果为什么不支持flash,而是用html5技术呢?

为什么chrome默认禁止了flash

这个问题,就一句话,google baba想让flash死,广告费都不想挣了,什么想法可想而知。

这个问题可以看看这个同类问题:chrome默认禁止flash的意图是什么?

回到上一个的问题,既让google都默认禁止了flash了,那b站如果一如既往的只支持flash,那后果也可想而知。

进入正题

有了上面的知识储备,先理清一下关系。

  1. b站PC端的视频格式是flv,但是b站也可能有淘汰flash的想法,研发了flvjs,实现了flash播放器向h5播放器的平滑过渡,现在mac上也可以直接看B站的视频而不需要下flash。
  2. 苹果自己推出了视频协议fls,在ios设备上,已得到h5的支持。
  3. 目前不只ios设备,在大多数android设备上,也原生支持hls协议。也就是说,大部分移动设备上,使用h5的video标签都能直接播放hls视频流,而不需要增加其他额外支持。

上面介绍了很多名词,现在做下简单介绍,才能继续讲下面的。

主流直播协议对比

协议 httpflv rtmp hls dash
传输方式 http流 tcp流 http http
视频封装格式 flv flv tag ts文件 Mp4 3gp webm
延时 低 低 高 高
数据分段 连续流 连续流 切片文件 切片文件
H5播放 可通过h5解封包播放(flvjs) 不支持 可通过H5解封包播放(hlsjs) 如果dash文件列表是mp4webm文件可直接播放
其他 需要flash支持 需要 flash支持

详细的介绍请戳:全面进阶 H5 直播

原生h5支持格式

大家都知道h5中的video标签能播放视频,但是视频格式有限制,目前只支持mp4,ogg,webm这三种格式。

1
2
3
4
5
<video controls autoplay>
<source src="media/butterfly.mp4" type="video/mp4">
<source src="media/butterfly.webm" type="video/webm">
<source src="media/butterfly.ogv" type="video/ogg">
</video>

那么问题来了,我想用<video>标签来播放flv格式的视频,播放hls协议视频流,要怎么办。这就是接下来要讲的内容。

H5与PC的直播方案

接下来的介绍仅从前端的角度出发,视频源全部来自B站。

开始之前先初始化一个vue项目。

PC端使用http-flv协议,移动端使用hls协议

先说下为什么要这样做,因为使用http-flv协议,延时能控制在2s左右,而是用hls,按Apple官方的说法,延时最好10s左右。那么如果项目中追求用户体验的话,PC端的优化就可以从这方面入手。

安装依赖包:

1
npm install --save flv.js

按照文档开始使用:

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
<!--flvplayer-->
<template>
<video id="videoElement" style="width: 100%"></video>
</template>

<script>
import flvjs from 'flv.js/dist/flv'
export default {
name: "flvplayer",

mounted(){
if (flvjs.isSupported()) {
let videoElement = document.getElementById('videoElement');
let flvPlayer = flvjs.createPlayer({
type: 'flv',
url: 'https://js.live-play.acgvideo.com/live-js/316521/live_30116877_9419411.flv?wsSecret=0f3b8d7ea6150aad8afb0d3b07d9287f&wsTime=1525504329'
});
flvPlayer.attachMediaElement(videoElement);
flvPlayer.load();
flvPlayer.play();
}
}
}
</script>
<style scoped></style>

从B站抓取PC端直播连接,填入指定位置即可。

image.png

查看效果(chrome浏览器,IE、QQ浏览器均无异常):

image.png

关于使用hls协议获取数据流播放的方式,在下面一起讲。

PC和移动端都使用hls协议

目前大多数移动设备都原生支持这种方式播放,PC的话使用一个插件也能达到同样的效果。主要起作用的是一个后缀为.m3u8的文件。大致用法如下:

1
<video src="yourM3u8File.m3u8"></video>

那么问题又来了,播放的原理是什么,为什么这种方式延时会高呢?

这个m3u8文件实际上存着的是一套切片规则,在这里不对这套规则做详细介绍,有兴趣的自行查阅资料。可以简单理解为里面规定了视频如何进行分片,切割为一个个ts文件,一次发送几个数据片。而延时的高低,也是由这个m3u8决定的,假设一次发送3个数据片,一个数据片的播放时长为3s,则延时为9s。

移动端

直接使用最原始的方法,抓取m3u8文件:

image.png

在PC假装移动端在B站是无法播放的,但是可以抓取到请求。组件代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<template>
<video width="100%" height="auto" playsinline webkit-playsinline autoplay controls preload="auto" x-webkit-airplay="true" x5-video-player-fullscreen="true" x5-video-player-typ="h5">
<source src="https://cn-jxjj-dx-live-01.live-play.acgvideo.com/live-bvc/live_20165629_5904889.m3u8?expires=1526111544&ssig=U_wsYr639DbZE6e6tDQdbA&oi=3658513157" type="application/x-mpegURL">
</video>
</template>

<script>
export default {
name: "hlsplayer"
}
</script>

<style scoped>
</style>

测试结果为ios设备能正常播放,ios8会自动全屏播放,浏览器上都无法播放,android设备也无法正常播放。

image.png

PC端

为了使PC也支持这种播放方式,我使用了vue-video-player + videojs-contrib-hls.js,vue-video-player本质上是封装了videojs。

1
2
npm install vue-video-player --save
npm install --save videojs-contrib-hls

在main.js中引入:

1
2
3
import VueVideoPlayer from 'vue-video-player'
import 'video.js/dist/video-js.css'
Vue.use(VueVideoPlayer);

组件如下:

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
48
49
50
51
52
53
54
55
56
import VueVideoPlayer from 'vue-video-player'
import 'video.js/dist/video-js.css'
var options = {hls: {
withCredentials: true
}};
Vue.use(VueVideoPlayer,{flash: options, html5: options})


<template>
<div class="item">
<div class="player">
<video-player class="vjs-custom-skin"
:options="playerOptions"
@ready="playerReadied">
</video-player>
</div>
</div>
</template>
<script>
import videojs from 'video.js'
window.videojs = videojs
require('videojs-contrib-hls/dist/videojs-contrib-hls.js')
export default {
name: "vue-video-player",
data() {
return {
playerOptions: {
// videojs and plugin options
height: '360',
sources: [{
withCredentials: false,
type: "application/x-mpegURL",
src: "https://cn-jxjj-dx-live-01.live-play.acgvideo.com/live-bvc/live_20165629_5904889.m3u8?expires=1526111544&ssig=U_wsYr639DbZE6e6tDQdbA&oi=3658513157"
}],
controlBar: {
timeDivider: false,
durationDisplay: false
},
flash: { hls: { withCredentials: false }},
html5: { hls: { withCredentials: false }},
}
}
},
methods: {
playerReadied(player) {
var hls = player.tech({ IWillNotUseThisInPlugins: true }).hls
player.tech_.hls.xhr.beforeRequest = function(options) {
// console.log(options)
return options
}
}
}
}
</script>
<style scoped>
</style>

浏览器正常:

image.png

ios设备正常,android设备也正常,不过我使用android模拟器说视频格式不支持。。。

image.png

所以到这里,就可以初步确定直播方案了。

总结

按我最近几天的学习,可以初步制定出2套方案:

一套是http-flv+hls:PC端flvjs,移动端使用videojs+videojs-contrib-hls.js。

另一套是直接使用hls:PC和移动端都使用videojs+vvideojs-contrib-hls.js。

前者的体验在一定程度上优于后者,主要体现在PC端上,flvjs延时更小。

还有一种方法是接入第三方播放器,不过要收费,阿里的腾讯的,不展开写了。

demo地址