H5与PC直播方案

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

本文用的技术:vuejsflvjsvue-video-playervideojs-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地址