一、视频直播的协议
视频直播服务支持输出RTMP、FLV、HLS、ARTC四种协议
- RTMP(Real Time Messaging Protocol,实时消息传送协议)
是Adobe公司为Flash播放器和服务器之间音频、视频和数据传输开发的开放协议。RTMP协议比较全能,既可以用来推送,又可以用来直播。其核心理念是将大块的视频帧和音频帧“剁碎”,然后以小数据包的形式在互联网上进行传输,且支持加密,因此隐私性相对比较理想,但拆包组包的过程比较复杂,所以在海量并发时容易出现一些不可预期的稳定性问题。 - HLS
HLS是苹果推出的解决方案。
将视频分成5秒10秒的视频小分片,然后用m3u8索引表进行管理。由于客户端下载到的视频都是5秒10秒的完整数据,故视频的流畅性很好,但也同样引入了很大的延迟(HLS的一般延迟在10秒~30秒左右)。相比于FLV、HLS在iPhone和大部分Android手机浏览器上的支持非常足,所以常用于QQ和微信朋友圈的URL分享。在safari上可以直接打开m3u8格式的文件。 - FLV
HTTP-FLV协议由Adobe公司主推。
格式极其简单,只是在大块的视频帧和音视频头部加入一些标记头信息,由于这种极致的简洁,在延迟表现和大规模并发方面都很成熟。唯一的不足就是在手机浏览器上的支持非常有限,但是用作手机端App直播协议却异常合适。 - ARTC是阿里云提供的低延迟直播RTS(Real-time Streaming)解决方案使用的协议头。
该方案参考WebRTC交互方式,采用UDP传输协议打造,实现可以承载大规模并发,端到端延时1秒内的低延时直播体验,同时对交互流程做了秒开优化,以及兼容更多的直播音视频特性例如AAC解码、B帧。RTS服务部署于阿里云直播节点,在接入成本、节点覆盖、承载能力上实现了平衡,大幅优化直播体验。
RTMP、HLS、HTTP-FLV、ARTC协议对比
二、推流+拉流是什么?
- 推流:把本地的视频直播推到服务器上,用OBS可以推流
- 拉流:已有直播内容,从指定的地址拉取
三、 浏览器播放器选择,为什么选择vue-video-player
- video.js:包比较大,实现hls直播的时候其实是内嵌了hls.js的代码,由于封装好UI和功能,使其不够灵活,修改UI时需要通过插件实现。
- vue-video-player:将video.js集成到了Vue中,考虑vue项目,技术融合比较好
- clappr:就是个垃圾,反正我不会,不妨碍我骂他垃圾。气死。
- 阿里云播放器
- 大牛播放器
- flv.js:bilibili开源
四、实现代码
1. 下载依赖
cnpm i vue-video-player -S
cnpm i videojs-flash -S //这个一定要下载
1. 引入
- 全局引入
import VideoPlayer from 'vue-video-player'
require('video.js/dist/video-js.css')
require('vue-video-player/src/custom-theme.css')
Vue.use(VideoPlayer)
//引入 hls,视频直播(m3u8)必须引入的
import 'videojs-contrib-hls'
//播放rtmp视频
import 'videojs-flash'
这个我没有在全局引入,只有这个页面用到播放器
- 局部引入
import { videoPlayer } from 'vue-video-player'
import 'video.js/dist/video-js.css'
import 'vue-video-player/src/custom-theme.css'
// import 'videojs-contrib-hls/dist/videojs-contrib-hls'
//引入 hls,视频直播(m3u8)必须引入的
import 'videojs-contrib-hls'
//播放rtmp视频
import 'videojs-flash'
export default {
name: 'live',
components: {
videoPlayer
},
main.js中
//引入 hls,视频直播(m3u8)必须引入的
import 'videojs-contrib-hls'
//播放rtmp视频
import 'videojs-flash'
3. 基本功能可行。
<template>
<div class="liveView">
<video-player
class="vjs-custom-skin"
ref="videoPlayer"
:options="playerOptions"
@ready="onPlayerReadied"
@timeupdate="onTimeupdate"
>
</video-player>
<div class="inputWrapper">
<div class="form-group row">
<label for="" class="col-sm-4 col-form-label">HLS: </label>
<div class="col-sm-8">
<input class="form-control" type="text" placeholder="HLS url here" v-model="streams.hls" />
</div>
</div>
</div>
<div class="buttonWrapper">
<button class="btn btn-primary" type="button" @click="enterStream">Apply</button>
</div>
</div>
</template>
<script>
import { videoPlayer } from 'vue-video-player'
import 'video.js/dist/video-js.css'
import 'vue-video-player/src/custom-theme.css'
// import 'videojs-contrib-hls/dist/videojs-contrib-hls'
//引入 hls,视频直播(m3u8)必须引入的
import 'videojs-contrib-hls'
//播放rtmp视频
import 'videojs-flash'
export default {
name: 'live',
components: {
videoPlayer
},
data() {
return {
initialized: false,
currentTech: '',
streams: {
rtmp: '',
hls: ''
},
playerOptions: {
overNative: true,
autoplay: false,
controls: true,
techOrder: ['html5'],
sourceOrder: true,
flash: {
hls: { withCredentials: false }
},
html5: { hls: { withCredentials: false } },
sources: [
{
withCredentials: false,
type: 'application/x-mpegURL',
src: 'http://ivi.bupt.edu.cn/hls/btv1hd.m3u8'
}
],
controlBar: {
timeDivider: false, // 时间分割线
durationDisplay: false, // 总时间
progressControl: true, // 进度条
customControlSpacer: true, // 未知
fullscreenToggle: true // 全屏
},
}
}
},
computed: {
player() {
return this.$refs.videoPlayer.player
},
currentStream() {
return this.currentTech === 'Flash' ? 'RTMP' : 'HLS'
}
},
methods: {
onPlayerReadied() {
if (!this.initialized) {
this.initialized = true
this.currentTech = this.player.techName_
}
},
// record current time
onTimeupdate(e) {
console.log('currentTime', e.cache_.currentTime)
},
enterStream() {
this.playerOptions.sources[0].src = this.streams.hls
this.playerOptions.autoplay = true
},
changeTech() {
if (this.currentTech === 'Html5') {
this.playerOptions.techOrder = ['html5']
} else if (this.currentTech === 'Flash') {
this.playerOptions.techOrder = ['flash']
}
this.playerOptions.autoplay = true
}
}
}
</script>
<style scoped>
.liveView {
position: relative;
}
.selectWrapper {
display: flex;
justify-content: center;
align-items: center;
font-size: 20px;
line-height: 30px;
margin: 20px;
vertical-align: baseline;
}
.inputWrapper {
width: 500px;
margin: 0 auto;
}
.buttonWrapper {
text-align: center;
}
</style>