647 lines
17 KiB
Vue
647 lines
17 KiB
Vue
<template>
|
||
<div id="player-main">
|
||
<div id="player"></div>
|
||
<!-- 为每个窗口添加控制按钮容器 -->
|
||
<div class="video-controls" v-for="i in videoInfo.maxWindows" :key="i" :id="'controls-' + i" :style="{ display: 'none' }">
|
||
<div class="controls-top">
|
||
<div></div>
|
||
<div @click="stopPlay(i)" class="top-close">
|
||
关闭
|
||
<i class="el-icon-close"></i>
|
||
</div>
|
||
</div>
|
||
<div class="controls-bottom">
|
||
<div></div>
|
||
<div>
|
||
<el-tooltip class="item" effect="dark" :content="`${videoInfo.recordingBegin ? '结束' : '开始'}录制`" placement="top">
|
||
<div class="bgImage transcribe" @click="isTranscribe(i)"></div>
|
||
</el-tooltip>
|
||
<el-tooltip class="item" effect="dark" :content="`抓图`" placement="top">
|
||
<div class="bgImage screenshot" @click="capture('JPEG', i)"></div>
|
||
</el-tooltip>
|
||
|
||
<!-- <div>流畅</div> -->
|
||
<el-tooltip class="item" effect="dark" :content="`${muted ? '关闭' : '开启'}音量`" placement="top">
|
||
<div @click="handleVolume(i)" :class="videoInfo.muted ? 'openVolume' : 'disableVolume'" class="bgImage"></div>
|
||
</el-tooltip>
|
||
<el-tooltip class="item" effect="dark" :content="`${isFullScreen ? '退出' : '进入'}全屏模式`" placement="top">
|
||
<div
|
||
class="bgImage"
|
||
:class="videoInfo.isFullScreen ? 'exitFullScreen' : 'fullScreen'"
|
||
@click="singleFullScreen(i)"
|
||
></div>
|
||
</el-tooltip>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="player-tool">
|
||
<el-tooltip class="item" effect="dark" :content="`默认`" placement="top">
|
||
<div class="bgImage splitscreen1" @click="onTwoSubmit(1)"></div>
|
||
</el-tooltip>
|
||
<el-tooltip class="item" effect="dark" :content="`2x2`" placement="top">
|
||
<div class="bgImage splitscreen2" @click="onTwoSubmit(2)"></div>
|
||
</el-tooltip>
|
||
<el-tooltip class="item" effect="dark" :content="`4x4`" placement="top">
|
||
<div class="bgImage splitscreen3" @click="onTwoSubmit(4)"></div>
|
||
</el-tooltip>
|
||
<el-tooltip class="item" effect="dark" :content="`停止所有播放`" placement="top">
|
||
<div class="bgImage stopAll" @click="stopAllPlay"></div>
|
||
</el-tooltip>
|
||
<el-tooltip class="item" effect="dark" :content="`${videoInfo.isFullScreen ? '退出' : '进入'}全屏模式`" placement="top">
|
||
<div class="bgImage" :class="videoInfo.isFullScreen ? 'exitFullScreen' : 'fullScreen'" @click="wholeFullScreen(i)"></div>
|
||
</el-tooltip>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
// 请求工具
|
||
// import http from "@/http/http2.js";
|
||
import { getVideoItemInfo } from "@/api/modules/mapConfig";
|
||
import moment from "moment";
|
||
import dayjs from "dayjs";
|
||
import { reactive, watch, onMounted } from "vue";
|
||
import { ElMessage } from "element-plus";
|
||
import loadingGif from "@/assets/images/iscImage/loading.gif";
|
||
import errorPng from "@/assets/images/iscImage/text-to-image.png";
|
||
const props = defineProps({
|
||
devList: {
|
||
type: Array,
|
||
default: () => []
|
||
}
|
||
});
|
||
const videoInfo = reactive({
|
||
// 监控点编码
|
||
code: "",
|
||
// 播放器对象
|
||
player: null,
|
||
devH5List: [],
|
||
numCount: 1,
|
||
iWndIndex: 0,
|
||
maxWindows: 16, // 最大窗口数
|
||
recordingBegin: false,
|
||
muted: true,
|
||
playback: {
|
||
startTime: "2023-08-16T00:00:00",
|
||
endTime: "2023-08-16T23:00:00",
|
||
valueFormat: moment.HTML5_FMT.DATETIME_LOCAL_SECONDS,
|
||
seekStart: "2023-08-16T10:00:00",
|
||
rate: ""
|
||
},
|
||
isFullScreen: false,
|
||
isFullScreenAll: false
|
||
});
|
||
// 关闭所有视频
|
||
const stopAllPlay = () => {
|
||
videoInfo.player.JS_StopRealPlayAll().then(
|
||
() => {
|
||
videoInfo.playback.rate = 0;
|
||
console.log("stopAllPlay success");
|
||
videoInfo.devH5List.forEach((item, index) => {
|
||
const wnd = document.querySelector(`#player #player-container-${index}`);
|
||
if (wnd) {
|
||
const controls = document.getElementById(`controls-${index + 1}`);
|
||
if (controls) {
|
||
controls.classList.remove("video-controls_flex");
|
||
}
|
||
}
|
||
});
|
||
videoInfo.devH5List = [];
|
||
},
|
||
e => {
|
||
console.error(e);
|
||
}
|
||
);
|
||
};
|
||
// 关闭单个
|
||
const stopPlay = (currentWindowIndex, type) => {
|
||
videoInfo.player.JS_Stop(currentWindowIndex - 1).then(
|
||
() => {
|
||
videoInfo.playback.rate = 0;
|
||
console.log("stop realplay success");
|
||
const wnd = document.querySelector(`#player #player-container-${currentWindowIndex - 1}`);
|
||
if (wnd) {
|
||
const controls = document.getElementById(`controls-${currentWindowIndex}`);
|
||
if (controls) {
|
||
controls.classList.remove("video-controls_flex");
|
||
}
|
||
if (type == "delete") return;
|
||
videoInfo.devH5List.splice(currentWindowIndex - 1, 1);
|
||
}
|
||
},
|
||
e => {
|
||
console.error(e);
|
||
}
|
||
);
|
||
};
|
||
const handleVolume = currentWindowIndex => {
|
||
if (videoInfo.muted) {
|
||
openSound(currentWindowIndex);
|
||
} else {
|
||
closeSound(currentWindowIndex);
|
||
}
|
||
};
|
||
/* 声音、抓图、录像 */
|
||
const openSound = currentWindowIndex => {
|
||
videoInfo.player.JS_OpenSound(currentWindowIndex - 1).then(
|
||
() => {
|
||
console.log("openSound success");
|
||
videoInfo.muted = false;
|
||
},
|
||
e => {
|
||
console.error(e);
|
||
}
|
||
);
|
||
};
|
||
const closeSound = currentWindowIndex => {
|
||
videoInfo.player.JS_CloseSound(currentWindowIndex - 1).then(
|
||
() => {
|
||
console.log("closeSound success");
|
||
videoInfo.muted = true;
|
||
},
|
||
e => {
|
||
console.error(e);
|
||
}
|
||
);
|
||
};
|
||
// 单个窗口全屏
|
||
const singleFullScreen = currentWindowIndex => {
|
||
if (videoInfo.isFullScreenAll) {
|
||
videoInfo.wholeFullScreen();
|
||
return;
|
||
}
|
||
videoInfo.player.JS_FullScreenSingle(currentWindowIndex - 1).then(
|
||
() => {
|
||
console.log(`singleFullScreen success`);
|
||
},
|
||
e => {
|
||
console.error(e);
|
||
}
|
||
);
|
||
};
|
||
// 整体窗口全屏
|
||
const wholeFullScreen = () => {
|
||
videoInfo.player.JS_FullScreenDisplay(videoInfo.isFullScreenAll).then(
|
||
() => {
|
||
console.log(`wholeFullScreen success`);
|
||
videoInfo.isFullScreenAll = !videoInfo.isFullScreenAll;
|
||
},
|
||
e => {
|
||
console.error(e);
|
||
}
|
||
);
|
||
};
|
||
const isTranscribe = currentWindowIndex => {
|
||
if (videoInfo.recordingBegin) {
|
||
recordStop(currentWindowIndex);
|
||
} else {
|
||
recordStart("MP4", currentWindowIndex);
|
||
}
|
||
};
|
||
// 开始录制
|
||
const recordStart = (type, currentWindowIndex) => {
|
||
const codeMap = { MP4: 5, PS: 2 };
|
||
//let options = {irecordType: 2, cbStreamCB: streamcb}
|
||
let player = videoInfo.player,
|
||
index = currentWindowIndex - 1,
|
||
fileName = `${moment().format("YYYYMMDDHHmmss")}.mp4`,
|
||
typeCode = codeMap[type];
|
||
|
||
player
|
||
.JS_StartSaveEx(index, fileName, typeCode, {
|
||
irecordType: 1,
|
||
cbStreamCB: videoInfo.streamcb
|
||
})
|
||
.then(
|
||
() => {
|
||
ElMessage.success("开始录制");
|
||
videoInfo.recordingBegin = true;
|
||
console.log("record start ...");
|
||
},
|
||
e => {
|
||
console.error(e);
|
||
}
|
||
);
|
||
};
|
||
// 结束录制
|
||
const recordStop = currentWindowIndex => {
|
||
let player = videoInfo.player,
|
||
index = currentWindowIndex - 1;
|
||
|
||
player.JS_StopSave(index).then(
|
||
res => {
|
||
console.log("record stoped, saving ...", res);
|
||
videoInfo.recordingBegin = false;
|
||
},
|
||
e => {
|
||
console.error(e);
|
||
}
|
||
);
|
||
};
|
||
// 抓图
|
||
const capture = (imageType, currentWindowIndex) => {
|
||
let player = videoInfo.player,
|
||
index = currentWindowIndex - 1;
|
||
|
||
player.JS_CapturePicture(index, "img", imageType).then(
|
||
() => {
|
||
console.log("capture success", imageType);
|
||
},
|
||
e => {
|
||
console.error(e);
|
||
}
|
||
);
|
||
};
|
||
/**
|
||
* 初始化播放器
|
||
*/
|
||
const initPlayer = () => {
|
||
videoInfo.player = new JSPlugin({
|
||
// 需要英文字母开头 必填
|
||
szId: "player",
|
||
// 必填,引用H5player.min.js的js相对路径
|
||
szBasePath: "/public/bin",
|
||
|
||
iMaxSplit: 4,
|
||
openDebug: true,
|
||
mseWorkerEnable: false, //是否开启多线程解码,分辨率大于1080P建议开启,否则可能卡顿
|
||
bSupporDoubleClickFull: true, //是否支持双击全屏,true-双击是全屏;false-双击无响应
|
||
|
||
// 分屏播放,默认最大分屏4*4
|
||
// iMaxSplit: 16,
|
||
iCurrentSplit: 1,
|
||
|
||
// 样式
|
||
oStyle: {
|
||
border: "#343434",
|
||
borderSelect: "#FFCC00",
|
||
background: "#000"
|
||
}
|
||
});
|
||
// 事件回调绑定
|
||
videoInfo.player.JS_SetWindowControlCallback({
|
||
windowEventSelect: function (iWndIndex) {
|
||
//插件选中窗口回调
|
||
console.log("windowSelect callback: ", iWndIndex);
|
||
videoInfo.iWndIndex = iWndIndex;
|
||
},
|
||
pluginErrorHandler: function (iWndIndex, iErrorCode, oError) {
|
||
//插件错误回调
|
||
console.log("pluginError callback: ", iWndIndex, iErrorCode, oError);
|
||
},
|
||
windowEventOver: function (iWndIndex) {
|
||
//鼠标移过回调
|
||
console.log(iWndIndex);
|
||
// 使用选中事件来定位控制按钮
|
||
const wnd = document.querySelector(`#player #player-container-${iWndIndex}`);
|
||
console.log(1111222, wnd);
|
||
if (wnd) {
|
||
const controls = document.getElementById(`controls-${iWndIndex + 1}`);
|
||
console.log(controls, wnd);
|
||
if (controls && !wnd.contains(controls)) {
|
||
// 确保控制按钮只添加一次
|
||
if (!wnd.querySelector(".video-controls")) {
|
||
wnd.appendChild(controls);
|
||
// controls.style.display = "flex";
|
||
}
|
||
}
|
||
if (controls) {
|
||
// if (iWndIndex > videoInfo.devH5List.length - 1) return;
|
||
// controls.classList.add("video-controls_flex");
|
||
const player_playVideo = wnd.querySelector(`#player_playVideo${iWndIndex}`);
|
||
if (player_playVideo.src) {
|
||
controls.classList.add("video-controls_flex");
|
||
}
|
||
}
|
||
}
|
||
},
|
||
windowEventOut: function (iWndIndex) {
|
||
//鼠标移出回调
|
||
console.log(iWndIndex);
|
||
const wnd = document.querySelector(`#player #player-container-${iWndIndex}`);
|
||
if (wnd) {
|
||
const controls = document.getElementById(`controls-${iWndIndex + 1}`);
|
||
if (controls) {
|
||
controls.classList.remove("video-controls_flex");
|
||
}
|
||
}
|
||
},
|
||
windowEventUp: function (iWndIndex) {
|
||
//鼠标mouseup事件回调
|
||
//console.log(iWndIndex);
|
||
},
|
||
windowFullCcreenChange: function (bFull) {
|
||
//全屏切换回调
|
||
console.log("fullScreen callback: ", bFull);
|
||
videoInfo.isFullScreen = bFull;
|
||
videoInfo.isFullScreenAll = false;
|
||
},
|
||
firstFrameDisplay: function (iWndIndex, iWidth, iHeight) {
|
||
//首帧显示回调
|
||
console.log("firstFrame loaded callback: ", iWndIndex, iWidth, iHeight);
|
||
},
|
||
performanceLack: function (iWndIndex) {
|
||
//性能不足回调
|
||
console.log("performanceLack callback: ", iWndIndex);
|
||
},
|
||
StreamEnd: function (iWndIndex) {
|
||
//性能不足回调
|
||
console.log("recv StreamEnd: ", iWndIndex);
|
||
},
|
||
StreamHeadChanged: function (iWndIndex) {
|
||
console.log("recv StreamHeadChanged: ", iWndIndex);
|
||
},
|
||
ThumbnailsEvent: (iWndIndex, eventType, eventCode) => {
|
||
console.log("recv ThumbnailsEvent: " + iWndIndex + ", eventType:" + eventType + ", eventCode:" + eventCode);
|
||
},
|
||
InterruptStream: (iWndIndex, iTime) => {
|
||
console.log("recv InterruptStream: " + iWndIndex + ", iTime:" + iTime);
|
||
},
|
||
ElementChanged: (iWndIndex, szElementType) => {
|
||
//回调采用的是video还是canvas
|
||
console.log("recv ElementChanged: " + iWndIndex + ", szElementType:" + szElementType);
|
||
}
|
||
});
|
||
play(videoInfo.devH5List[0], videoInfo.iWndIndex);
|
||
};
|
||
/**
|
||
* 获取取流连接
|
||
* @returns {*}
|
||
*/
|
||
const getPreviewUrl = row => {
|
||
let tempCode = row.serialNumber;
|
||
const param = {
|
||
cameraIndexCode: tempCode,
|
||
streamType: row.defaultStreamType == 2 ? 0 : row.defaultStreamType,
|
||
type: window.location.protocol.includes("https") ? "wss" : "ws",
|
||
transmode: 1,
|
||
itemId: row.itemId
|
||
};
|
||
// 这里
|
||
return getVideoItemInfo(param);
|
||
};
|
||
/**
|
||
* 播放
|
||
*/
|
||
const play = (row, index) => {
|
||
stopPlay(index + 1, "delete");
|
||
getPreviewUrl({
|
||
...row
|
||
}).then(res => {
|
||
if (res.code !== 200 && !res.result.videoInfo) {
|
||
ElMessage.warning("获取视频流失败!");
|
||
return;
|
||
}
|
||
// 视频添加加载中图片
|
||
const id = "player-container-" + index;
|
||
var d1 = document.getElementById(id); //获取div元素
|
||
d1.childNodes.forEach(item => {
|
||
if (item.nodeName == "" || item.nodeName == "IMG") {
|
||
d1.removeChild(item);
|
||
}
|
||
});
|
||
var im = document.createElement("img"); //创建图片
|
||
im.src = loadingGif;
|
||
//图片设置成和div一样大小
|
||
const divHeightNum = d1.style.height.slice(0, d1.style.height.length - 2);
|
||
// const imgHeightNum = divHeightNum * 0.2
|
||
const imgHeightNum = 256;
|
||
im.style.width = imgHeightNum + "px";
|
||
im.style.height = imgHeightNum + "px";
|
||
im.style.position = "absolute";
|
||
im.style.top = "50%";
|
||
im.style.left = "50%";
|
||
const positionNum = imgHeightNum / 2 - imgHeightNum;
|
||
im.style.marginLeft = positionNum + "px";
|
||
im.style.marginTop = positionNum + "px";
|
||
d1.appendChild(im); //图片挂载到div上
|
||
let preUrl = res.result.videoInfo.url;
|
||
const param = {
|
||
playURL: preUrl,
|
||
// 1:高级模式 0:普通模式,高级模式支持所有
|
||
mode: 0
|
||
};
|
||
// 索引默认0
|
||
if (!index) {
|
||
index = 0;
|
||
}
|
||
videoInfo.player.JS_Play(preUrl, param, index).then(
|
||
() => {
|
||
// 播放成功回调
|
||
d1.removeChild(d1.childNodes[d1.childNodes.length - 1]);
|
||
console.log("播放成功");
|
||
},
|
||
err => {
|
||
console.log("播放失败");
|
||
im.src = errorPng;
|
||
const imgHeightNum = 20;
|
||
const imgWidthNum = 150;
|
||
im.style.width = imgWidthNum + "px";
|
||
im.style.height = imgHeightNum + "px";
|
||
const positionNum = imgHeightNum / 2 - imgHeightNum;
|
||
const positionWidthNum = imgWidthNum / 2 - imgWidthNum;
|
||
im.style.marginLeft = positionWidthNum + "px";
|
||
im.style.marginTop = positionNum + "px";
|
||
}
|
||
);
|
||
videoInfo.player.JS_SetConnectTimeOut(index, 60).then(
|
||
() => {
|
||
console.info("JS_SetConnectTimeOut success");
|
||
// do you want...
|
||
},
|
||
err => {
|
||
console.info("JS_SetConnectTimeOut failed", err);
|
||
// do you want...
|
||
}
|
||
);
|
||
});
|
||
};
|
||
/**
|
||
* 分屏,这里我太懒了,就循环了一个视频流
|
||
*/
|
||
const onTwoSubmit = num => {
|
||
// 这里的分屏,是以列来算的,如果这里参数2,那么就是横竖两列,就是4格
|
||
videoInfo.numCount = num;
|
||
videoInfo.player.JS_ArrangeWindow(num).then(
|
||
() => {
|
||
// 循环取流
|
||
// for (let i = 0; i < num * num; i++) {
|
||
// videoInfo.play(i);
|
||
// }
|
||
},
|
||
e => {
|
||
console.error(e);
|
||
}
|
||
);
|
||
};
|
||
watch(
|
||
() => props.devList,
|
||
(a, b) => {
|
||
console.log("isc_plugin.vue获取到设备列表", a, b, videoInfo.devH5List);
|
||
//a是value的新值,b是旧值
|
||
a.forEach((item, index) => {
|
||
if (videoInfo.numCount == 1) {
|
||
play(item, videoInfo.iWndIndex);
|
||
} else if (a.length == 1) {
|
||
play(item, videoInfo.iWndIndex);
|
||
} else {
|
||
play(item, videoInfo.index);
|
||
}
|
||
});
|
||
videoInfo.devH5List = a;
|
||
},
|
||
{ deep: true }
|
||
);
|
||
onMounted(() => {
|
||
videoInfo.devH5List = props.devList;
|
||
console.log("我进来了", props.devList);
|
||
// 页面加载初始化`
|
||
initPlayer();
|
||
});
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.bgImage {
|
||
width: 18px;
|
||
height: 18px;
|
||
background-repeat: no-repeat;
|
||
background-size: 100% 100%;
|
||
cursor: pointer;
|
||
}
|
||
.transcribe {
|
||
width: 20px;
|
||
height: 20px;
|
||
background-image: url("@/assets/images/iscImage/transcribe.png");
|
||
}
|
||
.screenshot {
|
||
background-image: url("@/assets/images/iscImage/screenshot.png");
|
||
}
|
||
.disableVolume {
|
||
background-image: url("@/assets/images/iscImage/disableVolume.png");
|
||
}
|
||
.fullScreen {
|
||
background-image: url("@/assets/images/iscImage/fullScreen.png");
|
||
}
|
||
.openVolume {
|
||
background-image: url("@/assets/images/iscImage/openVolume.png");
|
||
}
|
||
.exitFullScreen {
|
||
background-image: url("@/assets/images/iscImage/exitFullScreen.png");
|
||
}
|
||
.splitscreen1 {
|
||
background-image: url("@/assets/images/iscImage/splitscreen1.png");
|
||
}
|
||
.splitscreen2 {
|
||
background-image: url("@/assets/images/iscImage/splitscreen2.png");
|
||
}
|
||
.splitscreen3 {
|
||
background-image: url("@/assets/images/iscImage/splitscreen3.png");
|
||
}
|
||
.stopAll {
|
||
background-image: url("@/assets/images/iscImage/stopAll.png");
|
||
}
|
||
.video-controls_flex {
|
||
display: flex !important;
|
||
}
|
||
/* 添加控制按钮样式 */
|
||
.video-controls {
|
||
width: 100%;
|
||
height: 100%;
|
||
position: absolute;
|
||
z-index: 100;
|
||
// background: rgba(0, 0, 0, 0.7);
|
||
display: none;
|
||
flex-direction: column;
|
||
justify-content: space-between;
|
||
.controls-bottom {
|
||
height: 35px;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
background: rgba(0, 0, 0, 0.7);
|
||
color: white;
|
||
padding: 0 16px;
|
||
> div {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
> div:not(:first-child) {
|
||
margin-left: 15px;
|
||
}
|
||
}
|
||
}
|
||
.controls-top {
|
||
padding: 16px;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
.top-close {
|
||
width: 84px;
|
||
height: 27px;
|
||
background: rgba(0, 0, 0, 0.7);
|
||
border-radius: 41px;
|
||
font-size: 15px;
|
||
color: #ffffff;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
> i {
|
||
margin-left: 5px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
#player-main {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
#player {
|
||
width: 100%;
|
||
height: calc(100% - 40px);
|
||
}
|
||
.player-tool {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: flex-end;
|
||
background-color: #3d3d3d;
|
||
height: 40px;
|
||
padding: 0 16px;
|
||
> div:not(:first-child) {
|
||
margin-left: 16px;
|
||
}
|
||
}
|
||
.hikvision-player {
|
||
width: 100%;
|
||
height: 100%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
#video-container {
|
||
width: 100%;
|
||
height: 500px;
|
||
background-color: #000;
|
||
}
|
||
|
||
.control-bar {
|
||
padding: 10px;
|
||
background: #f5f5f5;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
}
|
||
|
||
.video-source {
|
||
padding: 10px;
|
||
background: #f5f5f5;
|
||
display: flex;
|
||
gap: 10px;
|
||
}
|
||
|
||
.volume-control {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
}
|
||
</style>
|