vue播放器插件
项目要求:
我们做的是一个监控项目,需要实时监控某机器,播放器在监控页面可拖动。
插件选择:
项目是vue框架,播放器选择了vue-video-player插件, vue-video-player,其实就是 video.js 集成到 vue 中 ,视频流选择rtmp格式,实时性高,稳定性也高,但是依赖flash插件。
使用步骤
- 安装 vue-video-player
npm install vue-video-player -S
- 引用插件
import 'video.js/dist/video-js.css';
import 'vue-video-player/src/custom-theme.css';
import { videoPlayer } from 'vue-video-player';
import 'videojs-flash';
注意:
1、vue-video-player 其实就是 video.js 集成到 vue 中,所以千万不要再安装 video.js,可能会出错。
2、播放 HLS 流,需要 videojs-contrib-hls 插件,(!直接引用,因为在安装vue-video-player插件时,hls插件是一并下载下来的),如果需要 RTMP 流,需要 videojs-flash 插件,也是直接引用就可以了( flash 插件需要在 hls 之前引用)
- 使用插件播放器
<template>
<div
id="videoBox"
ref="videoBox"
v-drag
class="video-box"
>
<div class="video-box-header">
<div>
<svg-icon
class="video-box-header-mobile"
icon-class="mobile"
/>
<span>AGV监控视窗</span>
</div>
<a
class="el-icon-arrow-up"
@click="show=!show"
/>
</div>
<el-collapse-transition>
<div v-if="show">
<videoPlayer
ref="videoPlayer"
:options="videoOptions"
class="vjs-custom-skin videoPlayer"
:playsinline="false"
/>
</div>
</el-collapse-transition>
</div>
</template>
<script>
import 'video.js/dist/video-js.css';
import 'vue-video-player/src/custom-theme.css';
import { videoPlayer } from 'vue-video-player';
import 'videojs-contrib-hls';
import 'videojs-flash';
import ResizeMixin from '@/layout/mixin/ResizeHandler';
export default {
components: {
videoPlayer,
},
//注册局部组件指令
directives: {
drag: function(el) {
let dragBox = el; //获取当前元素
const sliderMenuWidth = 210;
const navBarHeight = 50;
dragBox.onmousedown = e => {
//算出鼠标相对元素的位置
let disX = e.clientX - dragBox.offsetLeft;
let disY = e.clientY - dragBox.offsetTop;
document.onmousemove = e => {
//用鼠标的位置减去鼠标相对元素的位置,得到元素的位置
let left = e.clientX - disX;
let top = e.clientY - disY;
//移动当前元素
if (left <= 0) {
left = 0;
} else if (left >= document.documentElement.clientWidth - dragBox.offsetWidth - sliderMenuWidth) {
//document.documentElement.clientWidth 屏幕的可视宽度
left = document.documentElement.clientWidth - dragBox.offsetWidth - sliderMenuWidth;
}
if (top <= 0) {
top = 0;
} else if (top >= document.documentElement.clientHeight - dragBox.offsetHeight - navBarHeight) {
// document.documentElement.clientHeight 屏幕的可视高度
top = document.documentElement.clientHeight - dragBox.offsetHeight - navBarHeight;
}
dragBox.style.right = 0;
dragBox.style.left = left + 'px';
dragBox.style.top = top + 'px';
};
document.onmouseup = e => {
//鼠标弹起来的时候不再移动
document.onmousemove = null;
//预防鼠标弹起来后还会循环(即预防鼠标放上去的时候还会移动)
document.onmouseup = null;
};
};
},
},
mixins: [ResizeMixin],
props: {
videoSrc: { type: String, default: 'rtmp://127.0.0.1/oflaDemo/B0_2' },
poster: {
type: String,
default: '',
},
},
data() {
return {
show: true,
player: null,
// 视频播放
videoOptions: {
live: true,
playbackRates: [0.7, 1.0, 1.5, 2.0], //播放速度
autoplay: true, //如果true,浏览器准备好时开始回放。
muted: false, // 默认情况下将会消除任何音频。
loop: false, // 导致视频一结束就重新开始。
preload: 'auto', // 建议浏览器在<video>加载元素后是否应该开始下载视频数据。auto浏览器选择最佳行为,立即开始加载视频(如果浏览器支持)
language: 'zh-CN',
aspectRatio: '4:3', // 将播放器置于流畅模式,并在计算播放器的动态大小时使用该值。值应该代表一个比例 - 用冒号分隔的两个数字(例如"16:9"或"4:3")
techOrder: ['flash', 'html5'], // 兼容顺序'flash', 'html5'
sources: [
{
// 流配置,数组形式,会根据兼容顺序自动切换
type: 'rtmp/flv', //application/x-mpegURL,'rtmp/flv',
src: this.videoSrc,
},
],
poster: this.poster, //你的封面地址
notSupportedMessage: '此视频暂无法播放,请稍后再试', // 允许覆盖Video.js无法播放媒体源时显示的默认信息。
controls: true, // 是否拥有控制条 【默认true】,如果设为false ,那么只能通过api进行控制了。也就是说界面上不会出现任何控制按钮
controlBar: {
children: [{ name: 'FullscreenToggle' }],
},
},
};
},
watch: {
containerSize: {
handler() {
this.handlerVideoPostion();
},
deep: true,
},
show(value) {
if (!value) {
this.$refs.videoPlayer.dispose();
}
},
},
beforeDestroy() {
this.$refs.videoPlayer.dispose();
},
created() {
this.show = true;
},
mounted() {
this.player = this.$refs.videoPlayer.player;
},
methods: {
handlerVideoPostion() {
const videoBox = this.$refs.videoBox;
if (videoBox.offsetTop + videoBox.offsetHeight >= this.containerSize.height) {
videoBox.style.top = this.containerSize.height - videoBox.offsetHeight + 'px';
}
if (videoBox.offsetLeft + videoBox.offsetWidth >= this.containerSize.width) {
videoBox.style.left = this.containerSize.width - videoBox.offsetWidth + 'px';
}
},
},
};
</script>
<style lang="scss" scoped>
$width: 420px;
$headerHeight: 40px;
$textColor: #999;
.video-box {
width: $width;
/* padding: 20px; */
position: absolute;
right: 0;
top: calc(100% - (#{$width} * 3 / 4) - #{$headerHeight});
left: calc(100% - #{$width});
border-radius: 4px;
background: #fff;
box-shadow: 0px 2px 8px 0px rgba(0, 0, 0, 0.1);
/deep/.vjs-custom-skin > .video-js .vjs-big-play-button {
display: none;
}
&-header {
// height: $headerHeight;
line-height: $headerHeight;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 15px;
font-size: 14px;
color: $textColor;
& > div {
display: flex;
align-items: center;
}
&-mobile {
width: 20px;
height: 20px;
margin-right: 5px;
}
}
}
</style>
注意事项:
1、autoplay: true,不能自动播放,因为flash插件内部的问题吧,视频的宽度不能小于400,如果小于400自动播放就不生效。
解决办法:
设置 video 播放器的 宽度大于等于 400;
2、因为我这边有可以动态折叠video播放器的功能,之前用的v-show去实现的,发现每次折叠隐藏播放组件后都会报错:this.el_.vjs_getProperty is not a function

videojs-flash.es.js?b5c1:582 Uncaught TypeError: this.el_.vjs_getProperty is not a function
at Flash.buffered (videojs-flash.es.js?b5c1:582)
at Flash.bufferedPercent$$1 [as bufferedPercent] (video.cjs.js?3d33:8176)
at Flash.eval (video.cjs.js?3d33:8118)
at Flash.bound (video.cjs.js?3d33:2344)
at bound (video.cjs.js?3d33:2344)
猜测应该是折叠后flash获取不到他的视频流报的错。
解决办法:
使用v-if代替v-show,并且在不显示的时候使用 dispose() 方法销毁 VideoPlayer 实例组件, this.$refs.videoPlayer.dispose(); v-if 为 true 的时候去自动重建 VideoPlayer 实例组件。
3、“The “flash” tech is undefined. Skipped browser support check for that tech”报错
VIDEOJS: ERROR: The "flash" tech is undefined. Skipped browser support check for that tech.
初级解决方案:
使用cnpm安装就会报错,使用npm安装video相关模块没问题,如果对使用 cnpm 还是npm 没有特定要求的话,可以使用这种方法解决。不过这不是最好的解决方案,只会治标不治本。
最终解决方案:
不管是删除node_module还是使用npm都不是根本的解决方案,根本原因是videojs和videojs-flash里的各有一个video.js,如果两个版本不一样可能就会报错了,终极解决方案就是配置第三方模块的查找顺序,优先查找本身安装的videojs就可以了
修改 webpack.config.js 文件
resolve: {
modules: [path.resolve('node_modules'), 'node_modules'],
...
}
注意: 如果是用vue-cli3项目脚手架搭建的项目,修改vue.config.js 文件
configureWebpack: (config) => {
config.resolve.modules[0] = path.resolve(__dirname, 'node_modules');
config.resolve.modules[1] = 'node_modules';
},
这里为什么要用这个方法呐,是因为vue-cli3 构建出来的项目里面,modules会存在三个不同的node_modules:
第一个:自身某块里的node_modules;
第二个:根目录里面的node_modules;
第三个:使用vue-cli3 构建项目,项目运行的时候会创建一个node_modules;
所以这里我们不能用覆盖的方法,只能使用更改modules数组某个元素的方法去更改查找顺序
至此问题解决!