功能:
- 实时播放视频流:采用xgplayer
- 实时点击按钮来截屏、录屏
注意点:
- 视频流播放时,获取每一帧,绘制canvas(界面上不显示)。
- 注意canvas是有默认宽高的,要修改width、height属性,否则绘制的canvas宽高比不对,会变形模糊。
- videoWidth、videoHeight获取 视频流真正的分辨率(用来获取宽高比)
index.html 引用下面两个工具
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/RecordRTC/5.6.1/RecordRTC.min.js"></script>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/html2canvas/0.4.0/html2canvas.min.js"></script>
<template>
<div class="video-player">
<div class="uav-video" :id="videoRef" :ref="videoRef"></div>
<canvas v-if="isShow" v-show="whetherShow" ref="myCanvas" class="clone-video"></canvas>
</div>
</template>
<script>
import 'flv.js';
import 'xgplayer';
import FlvJsPlayer from 'xgplayer-flv.js';
import api from '@/services';
import constant from '@/utils/common';
import utils from '@/utils/utils';
import axios from 'axios';
import bus from '@/bus';
import RecordRTC from 'recordrtc';
var player;
export default {
props: {
screenShot: {
type: Boolean,
default: false
},
videoUrl: {
type: String,
default: constant.uavUrl
},
videoRef: {
type: String,
default: 'uavVideo'
},
// 是否显示当前的canvas
isShow: {
type: Boolean,
default: false
}
},
data() {
return {
completeTimer: null, // 视频加载完成的interVal定时器
// 获取元素的canvas video 和 画板 解决延时器不一致的问题
timerId: null,
canvas: null,
context: null,
video: null, // recorder对象 和 按钮控制 以及是否展示
recorder: null,
whetherShow: false,
isStart: false // 开始录屏的标志位
};
},
mounted() {
this.createPlayer(constant[this.videoUrl]);
},
methods: {
// 创建新的监控
createPlayer(uid) {
console.log('flv url====', uid);
// 生成播放器
player = new FlvJsPlayer({
el: this.$refs[this.videoRef],
isLive: true,
url: uid,
autoplay: true,
height: '100%',
width: '100%',
// fluid: true,
// fitVideoSize: 'fixWidth',
/**
* 由于 Chrome 新的 autoplay 政策,必须设置静音才可播放。
* 但经测试 Chrome 下 xgplayer 仍无法自动播放。需要手动再配置一下muted为true。flv.js 却可播放,初步怀疑是 xgplayer 问题
* 其他浏览器暂无此问题(Firefox, Safari, 360极速浏览器, Edge)
*/
muted: true,
screenShot: this.screenShot,
flvOptionalConfig: {
enableWorker: true,
enableStashBuffer: false,
stashInitialSize: 128
},
lang: 'zh-cn',
controls: true,
ignores: ['play', 'volume', 'fullscreen'],
controlsList: ['nofullscreen', 'noremoteplayback']
});
player.muted = true; // 需要再次手动设置下静音才生效
this.resetFrame(player, this.videoRef);
// 重连
player.on('error', e => {
player.destroy();
});
player.once('destroy', () => {
this.isStart = false;
if (this.completeTimer) {
clearInterval(this.completeTimer);
}
setTimeout(() => {
this.createPlayer(uid);
}, 2000);
});
player.on('waiting', () => {
var timer = setTimeout(() => {
player.once('waiting', () => {
// console.log('等待三秒后重新播放咯');
player.reload();
});
player.once('canplaythrough', () => {
clearTimeout(timer);
timer = null;
});
}, 15000);
});
player.on('canplaythrough', e => {
console.log('视频可以流畅播放');
if (!this.isShow) return;
// 当视频可以播放时显示录屏按钮
this.whetherShow = true;
this.isStart = true;
// 当视频可以播放时开始获取当前的video元素和canvas元素以及创建recorder对象
this.video = e.video;
this.canvas = this.$refs['myCanvas'];
this.$nextTick(() => {
// 要手动修改 canvas 的宽高比,否则会使用默认的300 150.影响后面的计算
this.canvas.width = document.getElementsByClassName('video-player')[0].clientWidth;
this.canvas.height = document.getElementsByClassName('video-player')[0].clientHeight;
this.context = this.canvas.getContext('2d');
this.context.imageSmoothingEnabled = false;
});
});
},
// 销毁此播放器并通知后端关闭推流
closePlayer(player) {
player.destroy();
},
// 将直播帧设置为缓存的最新的一帧
resetFrame(player, mseId) {
player.once('complete', () => {
if (document.getElementById(mseId).childNodes && document.getElementById(mseId).childNodes.length > 0) {
let htmlMediaElement = document.getElementById(mseId).childNodes[0];
this.completeTimer = setInterval(() => {
if (!htmlMediaElement.buffered.length) {
return;
}
// 实时调用
if (this.isShow && this.isStart) {
this.draw(player.video);
}
// console.log('定时获取最新---视频');
let end = htmlMediaElement.buffered.end(0);
let diff = end - htmlMediaElement.currentTime;
if (diff >= 10) {
htmlMediaElement.currentTime = end;
}
}, 2000);
}
});
},
handleReload() {
player.reload();
},
// 实时获取当前页面中的video 动态 来实现录屏
draw() {
// 用视频真正宽高比例,以高为基准,水平方向留白,来画canvas。
let _contextWidth = (this.video.videoWidth * this.canvas.clientHeight) / this.video.videoHeight,
_contextHeight = this.canvas.clientHeight;
this.context.clearRect(0, 0, _contextWidth, _contextHeight);
this.context.drawImage(this.video, (this.canvas.width - _contextWidth) / 2, 0, _contextWidth, _contextHeight);
if (this.isStart) {
this.timerId = setTimeout(this.draw, 0);
}
},
// 点击录屏
StartRecord() {
this.recorder = new RecordRTC(this.canvas, {
type: 'canvas'
});
this.recorder.startRecording();
},
// 点击结束 获取媒体流
EndRecord() {
this.recorder.stopRecording(() => {
const blob = this.recorder.getBlob();
var _file = new File([blob], 'screenVideo-' + new Date().getTime() + '.mp4');
var forms = new FormData();
forms.append('file', _file);
forms.append('uavName', this.$store.state.uavId);
forms.append('type', 'video');
axios
.post('/api/file/upload', forms)
.then(res => {
this.$message.success('录频上传成功');
bus.$emit('uploadSceenShot', 'success');
})
.catch(res => {
this.$message.error(res.msg);
bus.$emit('uploadSceenShot', 'fail');
});
});
},
// 点击截图
async Screenshots() {
this.$nextTick(() => {
let _base64 = this.canvas.toDataURL('image/png');
let _file = utils.base64ToFile(_base64, 'screenShoot-' + new Date().getTime() + '.png');
var forms = new FormData();
forms.append('file', _file);
forms.append('uavName', this.$store.state.uavId);
forms.append('type', 'image');
axios
.post('/api/file/upload', forms)
.then(res => {
this.$message.success('截频上传成功');
bus.$emit('uploadSceenShot', 'success');
})
.catch(res => {
this.$message.error(res.msg);
bus.$emit('uploadSceenShot', 'fail');
});
});
}
},
destroyed() {
window.clearTimeout(this.timerId);
this.isStart = false;
}
};
</script>
<style lang="less" scoped>
.video-player,
.video-player #mse-monitor {
width: 100% !important;
height: 100% !important;
}
.video-player {
height: 100% !important;
.reload {
position: absolute;
top: 0;
cursor: pointer;
}
}
/deep/ .xgplayer-live {
display: none !important;
}
/deep/ .xgplayer-screenshot {
margin-right: 6vw !important;
span {
font-size: 1.2vw !important;
width: 4vw !important;
}
}
.clone-video {
width: 100%;
height: 100%;
position: absolute;
top: -99999999px; // 隐藏canvas
}
</style>