一、【rtmp视频流】
要点:主要是使用vue-video-player进行播放。要注意,这种播放方式需要flash播放器,遗憾的是很多浏览器已经不再支持flash播放器,如果是这样建议直接看下边第二种【flv视频流】。
1、安装vue-video-player
npm i -S vue-video-player
2、编写my-video.vue 组件
<template>
<div class="video-js">
<div
v-if="!videoSrc"
class="no-video">
暂未播放视频
</div>
<video-player
v-else
class="video-player vjs-custom-skin"
ref="videoPlayer"
:playsinline="true"
:options="playerOptions"
>
</video-player>
</div>
</template>
script - 引入依赖文件
// 引入以下文件
import videojs from 'video.js'
import 'video.js/dist/video-js.css'
import 'vue-video-player/src/custom-theme.css'
import {videoPlayer} from 'vue-video-player'
import 'videojs-flash'
import SWF_URL from 'videojs-swf/dist/video-js.swf'
videojs.options.flash.swf = SWF_URL // 设置flash路径,Video.js会在不支持html5的浏览中使用flash播放视频文件
配置video相关属性
export default {
name: 'videojs',
components: {
videoPlayer
},
data () {
return {
videoSrc: '',
playerOptions: {
live: true,
autoplay: true, // 如果true,浏览器准备好时开始播放
muted: false, // 默认情况下将会消除任何音频
loop: false, // 是否视频一结束就重新开始
preload: 'auto', // 建议浏览器在<video>加载元素后是否应该开始下载视频数据。auto浏览器选择最佳行为,立即开始加载视频(如果浏览器支持)
aspectRatio: '16:9', // 将播放器置于流畅模式,并在计算播放器的动态大小时使用该值。值应该代表一个比例 - 用冒号分隔的两个数字(例如"16:9"或"4:3")
fluid: true, // 当true时,Video.js player将拥有流体大小。换句话说,它将按比例缩放以适应其容器。
controlBar: {
timeDivider: false,
durationDisplay: false,
remainingTimeDisplay: false,
currentTimeDisplay: false, // 当前时间
volumeControl: false, // 声音控制键
playToggle: false, // 暂停和播放键
progressControl: false, // 进度条
fullscreenToggle: true // 全屏按钮
},
techOrder: ['flash', 'html5'], // 兼容顺序
flash: {
hls: {
withCredentials: false
},
swf: SWF_URL
},
sources: [{
type: 'rtmp/flv',
src: '' // 视频地址-改变它的值播放的视频会改变
}],
notSupportedMessage: '此视频暂无法播放,请稍后再试' // 允许覆盖Video.js无法播放媒体源时显示的默认信息。
}
}
}
}
注意样式:
<style scoped lang="scss">
.video-js{
width:100%;
height:100%;
.no-video{
display:flex;
height:100%;
font-size:14px;
text-align:center;
justify-content: center;
align-items:center;
}
}
</style>
3、封装 video.vue,引入上边封装好的my-video.vue
<template>
<div class="about">
<MyVideo
ref="playerObj">
</MyVideo>
<!-- <a @click="playVideo">播放视频</a> -->
</div>
</template>
<script>
import MyVideo from './my-video';
import {
getVideoStream,
getCameraInfo,
putCameraHeart
} from '@/apis/equipmentApis'
export default {
name: 'about',
props: {
row: {
type: Object,
default () {
return {
}
}
}
},
components: {
MyVideo
},
data() {
return {}
},
methods: {
// 播放视频
playVideo (url) {
// 可以测试一下 下边是湖南卫视直播视频流 看看能不能播放
// this.$refs['playerObj'].videoSrc = 'rtmp://192.168.6.101:1935/live/7d7f8e6632dc0cb60292e5c519ef6981'
// this.$refs['playerObj'].playerOptions.sources[0].src = 'rtmp://192.168.6.101:1935/live/7d7f8e6632dc0cb60292e5c519ef6981'
this.$refs['playerObj'].videoSrc = url;
this.$refs['playerObj'].playerOptions.sources[0].src = url;
},
// 动态获取视频流
async getVideoSrc (params) {
try {
let res = await getVideoStream(params);
console.log('动态获取视频流 - res', res);
if (res.data.code == 0 && res.data.data) {
let {
url,
token
} = res.data.data;
window.sessionStorage.setItem('videoToken', String(token));
url!= '' && (this.playVideo(url));
token && (await putCameraHeart(token));
}
} catch (error) {
console.log('动态获取视频流 - 失败', error);
}
},
// 获取摄像头的信息 比如 ip password等等
async getVideoInfo () {
try {
let res = await getCameraInfo(this.row.devId);
console.log('相机的信息---', res);
if (res.data.code == 0 && res.data.data) {
let {
ip,
username,
password,
} = res.data.data;
// 获取视频流的参数
let params = {
channel: '1',
factory: 'hikvision',
ip,
username,
password,
stream: 'main',
};
this.getVideoSrc(params);
}
} catch (error) {
console.log('获取摄像头的信息 - 失败', error);
}
}
},
created () {
this.getVideoInfo();
},
mounted () {
// setTimeout(() => {
// this.playVideo();
// }, 1500);
}
}
</script>
<style lang="scss">
.about {
width: 500px;
height: 300px;
}
</style>
4、在需要直播室视频的地方引入video.vue
......
<my-video :row="rowinfo">
</my-video>
......
<script>
export default {
name: 'camera',
}
</script>
注意:
页面关闭之后,实际上引入的视频控件(创建的video的dom)并没有销毁。这就会导致一直报错,this.xxxx is not a function 这样的错误。所以要在你关闭这个有视频的页面或者是弹窗的时候,手动清除这个控件。
因为我的视频控件在一个对话框内,所有我在关闭对话框的时候,停止了这个视频流。
// dialog关闭弹窗的回调
closeDialog () {
this.stopVideoStream();
},
// 停止视频流
async stopVideoStream () {
try {
let token = window.sessionStorage.getItem('videoToken');
let res = await stopCameraHeart(token);
console.log('停止视频流', res);
} catch (error) {
console.log('停止视频流', error);
}
},
也有人在beforeDestory中直接通过ref操作dom清除的。
二、【flv视频流】
要点:这种视频流只要是利用flv.js和html5的video进行播放,解决了很多浏览器不支持flash问题。
1、安装
npm i -S flv.js
2、直接在对应的vue组件中直接引入使用
<template>
<div id="flv-app">
<video
controls
muted
id="videoElement"></video>
</div>
</template>
<script>
// 引入flvjs
import flvjs from 'flv.js'
export default {
name: 'flv-app',
data() {
return {
flvPlayer: null,
urlHttpFlv: null
}
},
methods: {
/**
* @description 新建flv实例
*/
createFlvPlayer(url) {
if (flvjs.isSupported()) {
const videoElement = document.getElementById('videoElement')
this.flvPlayer = flvjs.createPlayer({
type: 'flv',
isLive: true,
url
})
this.flvPlayer.attachMediaElement(videoElement)
this.flvPlayer.load()
this.flvPlayer.play()
}
},
// 切换历史视频源头
switchHistoryVideo (url) {
this.pausemix();
this.createFlvPlayer(url);
},
// 转换到实时视频
switchLiveVideo () {
this.pausemix();
this.getVideoInfo();
},
/**
* @description 停止混流播放并移除直播流抓取
* (注:离开并重新进入当前路由,观察Network,可知该操作的必要性)
*/
pausemix() {
this.flvPlayer.pause()
this.flvPlayer.unload()
this.flvPlayer.detachMediaElement()
this.flvPlayer.destroy()
this.flvPlayer = null
},
// 动态获取视频流
// 参数
// {
// "channel": "1", 暂时写死
// "factory": "hikvision", 暂时写死
// "ip": "192.168.10.59",
// "password": "tsl123456",
// "stream": "main", -> main/sub 用于切换全屏和小屏播放
// "username": "admin"
// }
async getVideoSrc (params) {
try {
let res = await getVideoStream(params);
console.log('动态获取视频流 - res', res);
if (res.data.code == 0 && res.data.data) {
let {
urlHttpFlv,
token
} = res.data.data;
window.sessionStorage.setItem('videoToken', String(token));
urlHttpFlv!= '' && (this.createFlvPlayer(urlHttpFlv));
token && (await putCameraHeart(token));
}
} catch (error) {
console.log('动态获取视频流 - 失败', error);
}
},
// 获取摄像头的信息 比如 ip password等等
async getVideoInfo () {
try {
let res = await getCameraInfo(this.row.devId);
console.log('相机的信息---', res);
if (res.data.code == 0 && res.data.data) {
let {
ip,
username,
password,
nvrId
} = res.data.data;
// 传递相机的基本信息
this.$emit('getcamerainfo', res.data.data);
let params = {
channel: '1',
factory: 'hikvision',
ip,
username,
password,
stream: 'main',
};
// let params = {
// "channel": "1",
// "factory": "hikvision",
// "ip": "192.168.10.44",
// "password": "tsl123456",
// "stream": "main",
// "username": "admin"
// }
this.getVideoSrc(params);
this.getNvrVideoInfo(nvrId);
}
} catch (error) {
console.log('获取摄像头的信息 - 失败', error);
}
},
// 获取nvr相关信息 用于获取历史视频参数 getNvrCaremaInfo
async getNvrVideoInfo (nvrId) {
try {
let res = await getNvrCaremaInfo(nvrId);
console.log('获取nvr相关信息---', res);
if (res.data.code == 0 && res.data.data) {
// description: "超脑"
// installLocationId: null
// ip: "192.168.10.80"
// mcn: null
// name: "NVR-10.80"
// nvrId: "1605578253-192.168.10.80"
// password: "tsl123456"
// productKey: "a4f3f1dc1744823b8369eba387ebf6a9"
// projectId: 1605578224
// status: "online"
// statusInfo: "telnet ok"
// statusTime: 1614068464
// username: "admin"
this.$emit('getcamerainfonvrid', res.data.data)
} else {
this.$message({
type: 'error',
message: `${res.data.msg}`
})
}
} catch (error) {
console.log('获取nvr相关信息 - 失败', error);
}
},
},
mounted() {
this.getVideoInfo();
},
beforeDestroy() {
this.pausemix()
},
}
</script>
这种方案更加快捷。