在公司项目开发中,有一个项目里面需要做一个视频播放的功能,播放方式是调用海康平台提供的接口获取流地址来进行视频的播放并且最重要的是需要支持flash。由于前端用的Vue,对比了几个,最后选择了LivePlayer H5播放器。
官网介绍:H5直播/点播播放器,使用简单,功能强大, 免费使用。
官网地址:https://www.liveqing.com/docs/manuals/LivePlayer.html
github源码地址:https://github.com/livegbs/GB28181-Server
因为官网只是做了介绍,并没有具体的详细API,可以参考github源码,如果还不能解决问题,可以在官网加官方QQ群,会有专业人员帮忙解答。
有如下特点:
方法(Medthod)
事件(Event)
安装使用(Install)
一、安装:npm install @liveqing/liveplayer
二、编辑webpack.xxx.config.js:
const CopyWebpackPlugin = require('copy-webpack-plugin');
......
// copy js lib and swf files to dist dir
new CopyWebpackPlugin([
{
from: 'node_modules/@liveqing/liveplayer/dist/component/crossdomain.xml'},
{
from: 'node_modules/@liveqing/liveplayer/dist/component/liveplayer.swf'},
{
from: 'node_modules/@liveqing/liveplayer/dist/component/liveplayer-lib.min.js', to: 'js/'}
]),
......
注意:本地开发的配置文件和生产环境的都要配置。
三、引入依赖js
在index.html中引入:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="renderer" content="webkit">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>xxx</title>
</head>
...
<script type="text/javascript" src="./js/liveplayer-lib.min.js"></script>
...
<body>
...
</body>
</html>
四、编辑 Vue 组件
......
import LivePlayer from '@liveqing/liveplayer'
......
components: {
LivePlayer
}
......
<LivePlayer :videoUrl="videoUrl" fluent autoplay live stretch></LivePlayer>
实战:播放器组件化
<template>
<div style="border:#a8cae5 1px solid;">
<el-row >
<el-col :span="5" style="height: 25px">
窗口{
{
rowIdx*winNum + colIdx + 1}}:{
{
cameraName}}
</el-col>
<el-col v-if="isShowTab" :span="19-winNum">
<el-radio v-if="isShowTab" v-model="protocol" label="hls">HLS流</el-radio>
<el-radio v-if="isShowTab" v-model="protocol" label="rtmp">RTMP流</el-radio>
</el-col>
<el-col v-if="isShowTab" :span="winNum">
<el-button size="mini" @click="close">关闭</el-button>
</el-col>
</el-row>
<LivePlayer :cameraId="cameraId"
:video-url='videoUrl'
:video-title="videoTitle"
muted live loading stretch resolution='hd,sd'
v-loading="bLoading"
element-loading-background="#000"
:loading.sync="bLoading"></LivePlayer>
<div :id="showPTZ" v-if="isShowTab" style="position: absolute;z-index:9999;" :style="{top:ptzTop + '%'}">
<div class="showPTZ left-panel-accordion-content ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content-active" style="height: 0px; overflow: auto; display: block;" role="tabpanel" _mask_="1">
<i :class="iconClass" @click="chgTabState"></i>
<div class="ptz-panal-up">
<div id="ptz-speed-container" class="ptz-panal-up-left">
<div id="ptz-speed" class="ptz-speed"></div>
<div id="ptz-speed-high" class="ptz-speed-high" style="height: 32.5px; top: 39.5px;"></div>
</div>
<div class="ptz-panal-up-right" >
<span id="ptz-auto" class="ptz-auto direction" title="自动扫描"></span>
<span @click="controlPtz('UP')" id="ptz-up" class="ptz-up direction" title="上移" ctrlcmd="UP"></span>
<span @click="controlPtz('DOWN')" id="ptz-down" class="ptz-down direction" title="下移" ctrlcmd="DOWN"></span>
<span @click="controlPtz('LEFT')" id="ptz-left" class="ptz-left direction" title="左移" ctrlcmd="LEFT"></span>
<span @click="controlPtz('RIGHT')" id="ptz-right" class="ptz-right direction" title="右移" ctrlcmd="RIGHT"></span>
<span @click="controlPtz('LEFT_UP')" id="ptz-up-left" class="ptz-up-left direction" title="左上移动" ctrlcmd="LEFT_UP"></span>
<span @click="controlPtz('RIGHT_UP')" id="ptz-up-right" class="ptz-up-right direction" title="右上移动" ctrlcmd="RIGHT_UP"></span>
<span @click="controlPtz('LEFT_DOWN')" id="ptz-down-left" class="ptz-down-left direction" title="左下移动" ctrlcmd="LEFT_DOWN"></span>
<span @click="controlPtz('RIGHT_DOWN')" id="ptz-down-right" class="ptz-down-right direction" title="右下移动" ctrlcmd="RIGHT_DOWN"></span>
<span @click="controlPtz('ZOOM_IN')" id="ptz-zoomin" class="ptz-zoomin ptz-up-right-btn" title="变大焦距" ctrlcmd="ZOOM_IN"><span class="icon"></span></span>
<span @click="controlPtz('ZOOM_OUT')" id="ptz-zoomout" class="ptz-zoomout ptz-up-right-btn" title="变小焦距" ctrlcmd="ZOOM_OUT"><span class="icon"></span></span>
<span @click="controlPtz('13')" id="ptz-focusin" class="ptz-focusin ptz-up-right-btn" title="前调焦点" ctrlcmd="13"><span class="icon"></span></span>
<span @click="controlPtz('FOCUS_FAR')" id="ptz-focusout" class="ptz-focusout ptz-up-right-btn" title="后调焦点" ctrlcmd="FOCUS_FAR"><span class="icon"></span></span>
<span @click="controlPtz('IRIS_ENLARGE')" id="ptz-irisout" class="ptz-irisout ptz-up-right-btn" title="扩大光圈" ctrlcmd="IRIS_ENLARGE"><span class="icon"></span></span>
<span @click="controlPtz('IRIS_REDUCE')" id="ptz-irisin" class="ptz-irisin ptz-up-right-btn" title="缩小光圈" ctrlcmd="IRIS_REDUCE"><span class="icon"></span></span>
</div>
<!-- <div id="ptz-slider" class="ptz-slider" style="font-size: 11px !important; text-indent: 3px; top: 39.5px;" title="4">4</div>-->
</div>
<div class="loading-container" style="left: 0px; top: 0px; height: 110px; width: 218px;">
<div class="loading-container-overlay"></div>
</div>
</div>
</div>
</div>
</template>
<script>
import LivePlayer from '@liveqing/liveplayer'
import {
getPreviewURLs,controllerHik } from '../../../../api/zhgd/video/hikVideo.js'
export default {
name: 'myVideoPlayer',
props: ['cameraId', 'videoUrl', 'videoTitle', 'cameraName', 'rowIdx', 'colIdx', 'winNum'],
components: {
LivePlayer
},
data() {
return {
showPTZ: 'showPTZ' + this.rowIdx + this.colIdx,
iconClass: 'el-icon-d-arrow-right',
ptzTop: 20,
isShowTab: this.videoUrl !== '',
protocol: 'hls',
bLoading: false
// ptzRight:50,
// videoUrl: 'rtmp://58.200.131.2:1935/livetv/hunantv'
}
},
watch: {
protocol(val) {
console.log(this.rowIdx + '---' + this.colIdx + val)
const params = {
t: Math.random(), code: this.cameraId, protocol: val, streamType: '1' };
getPreviewURLs(params).then(data => {
if (data.code === '0') {
let purl = data.data.url;
if (window.location.host.indexOf('2.2.2.2') === 0) {
purl = data.data.url.replace('36.32.24.38', '2.2.2.2');
}
this.videoUrl = purl;
}
});
}
},
methods: {
chgTabState() {
if (this.iconClass === 'el-icon-d-arrow-right') {
$('#showPTZ' + this.rowIdx + this.colIdx + ' > .left-panel-accordion-content').css('width', '190px');
// $('#showPTZ' + this.rowIdx + this.colIdx + ' > .showPTZ').css('right', '0px');
this.iconClass = 'el-icon-d-arrow-left'
} else {
$('#showPTZ' + this.rowIdx + this.colIdx + ' > .left-panel-accordion-content ').css('width', '25px');
// $('#showPTZ' + this.rowIdx + this.colIdx + ' > .showPTZ').css('right', '0px');
this.iconClass = 'el-icon-d-arrow-right'
}
},
close() {
this.$emit('close');
},
controlPtz(code){
var params={
t:Math.random(),cameraId:this.cameraId,code:code,action:"0"};
controllerHik(params).then(data => {
if(data.code==0){
window.setTimeout(() => {
params.action = "1";
controllerHik(params).then(data => {
if(data.code==0){
}else if(data.code=="0x01900050"){
alert("设备操作失败!");
}else{
alert(data.msg);
}
});
}, 2000);
}else if(data.code=="0x01900050"){
alert("设备操作失败!");
}else{
alert(data.msg);
}
});
}
},
mounted() {
$('#showPTZ' + this.rowIdx + this.colIdx + ' > .left-panel-accordion-content').css('width', '25px');
$('#showPTZ' + this.rowIdx + this.colIdx + ' > .showPTZ').css('right', '0px');
}
}
</script>
<style scoped>
.showPTZ{
height: 110px !important;
position: relative;
overflow: hidden !important;
display: block;
/*background-color:rgba(43, 51, 63, 0.7)*/
background-color:rgba(255, 255, 255, 0.7)
}
.ptz-panal-up {
background-image: none;
width: 160px;
margin-left: 20px;
}
.ptz-panal-up-left {
background-image: none;
}
.ptz-panal-up-right {
background-image: none;
top:8px;
}
.ptz-speed{
background-image: none;
}
.left-panel-accordion-content {
width: 190px;
}
.el-icon-d-arrow-left,.el-icon-d-arrow-right{
position: absolute;
top:40px;
font-size: 25px;
cursor: pointer;
color: black;
}
.ptz-auto {
background-position: -801px top;
}
.ptz-up {
background-position: -549px top;
}
.ptz-down {
background-position: -591px top;
}
.ptz-left {
background-position: -758px top;
}
.ptz-right {
background-position: -717px top;
}
.ptz-up-left {
background-position: -841px top;
}
.ptz-up-right {
background-position: -633px top;
}
.ptz-down-left {
background-position: -883px top;
}
.ptz-down-right {
background-position: -675px top;
}
.ptz-zoomin .icon {
background-position: -1079px top;
}
.ptz-zoomout .icon {
background-position: -959px top;
}
.ptz-focusin .icon {
background-position: -1119px top;
}
.ptz-focusout .icon {
background-position: -999px top;
}
.ptz-irisout .icon {
background-position: -1159px top;
}
.ptz-irisin .icon {
background-position: -1039px top;
}
/*>>> .video-wrapper .video-title {*/
/* max-width: 400px;*/
/* top:50px;*/
/* }*/
</style>
说明:
# 属性
v-loading="bLoading"
element-loading-background="#000"
:loading.sync="bLoading"
<div class="loading-container" style="left: 0px; top: 0px; height: 110px; width: 218px;">
<div class="loading-container-overlay"></div>
</div>
效果图:效果图:
调用该组件相关代码:
<el-row v-for="(rowData,rowIdx) in winRows" :key="rowData">
<el-col v-for="(colData,colIdx) in rowData.cols" :key="colData.url" :span="colData.span" style="border: 1px #0fa2ff solid;">
<my-video-player
:cameraId="colData.cameraId"
:videoUrl="colData.url"
:videoTitle="colData.title"
:rowIdx="rowIdx"
:colIdx="colIdx"
:winNum="winNum"
@close="close(colData)"></my-video-player>
</el-col>
</el-row>
winRows: [
{
cols: [
{
span: 24, url: '', title: '', cameraId: '' }
] }
],
winNum: 1
#分屏方法
setWinNum(winNum) {
const rows = Math.sqrt(winNum);
this.winNum = rows;
// 原来的组件数据存起来
const oriArray = [];
for (let i = 0; i < this.winRows.length; i++) {
for (let j = 0; j < this.winRows[i].cols.length; j++) {
if (this.winRows[i].cols[j].url !== '') {
oriArray.push(this.winRows[i].cols[j])
}
}
}
// 创建一个新的winRows
this.winRows = [];
for (let i = 0; i < rows; i++) {
this.winRows.push({
cols: [] })
}
for (let i = 0; i < this.winRows.length; i++) {
for (let j = 0; j < rows; j++) {
if (oriArray.length > 0) {
this.winRows[i].cols.push({
span: 24 / rows, url: oriArray[0].url, title: oriArray[0].title, cameraId: oriArray[0].cameraId })
oriArray.splice(0, 1);
} else {
this.winRows[i].cols.push({
span: 24 / rows, url: '', title: '', cameraId: '' })
}
}
}
}
#关闭播放窗口
close(colData) {
colData.url = ''
colData.title = ''
colData.cameraId = ''
},
#点击摄像头播放
playVideo(treeNode) {
for (let i = 0; i < this.winRows.length; i++) {
let isBreak = false;
for (let j = 0; j < this.winRows[i].cols.length; j++) {
if (this.winRows[i].cols[j].url === '') {
const params = {
t: Math.random(), code: treeNode.id, protocol: 'hls', streamType: '1' };
getPreviewURLs(params).then(data => {
if (data.code === '0') {
let purl = data.data.url;
if (window.location.host.indexOf('2.2.2.2') === 0) {
purl = data.data.url.replace('36.32.24.38', '2.2.2.2');
}
const title = treeNode.lineName + '>' + treeNode.workareaName + '>' + treeNode.siteName;
// + '>' + treeNode.title;
this.winRows[i].cols[j].cameraId = treeNode.id;
this.winRows[i].cols[j].url = purl;
this.winRows[i].cols[j].title = title;
}
});
isBreak = true;
break;
} else {
continue;
}
}
if (isBreak) {
break;
}
}
},
因为牵扯到多分屏的切换,切换后所有播放组件会重新加载。