feat(video):重构视频监控模块并优化实时预览功能
- 移除旧的 DahuaVideoController 控制器及相关接口 - 在 NetSDKService 中实现基于 FFmpeg 的实时流转码与推送逻辑 - 新增对 H.264 和 HEVC 编码格式的识别与动态转码支持 - 实现 FLV 流封装及 WebSocket 推送机制 - 添加 WebSocket 会话管理与多用户连接跟踪 - 更新平台视频管理控制器中的通道标识字段 - 配置文件中增加大华设备连接参数- 调整日志级别为 debug 以便于开发调试 - 完善资源清理逻辑,确保登出时释放所有相关句柄和进程dev_dahua
parent
54ca5e9e4c
commit
566b1ae944
|
|
@ -1,9 +1,12 @@
|
||||||
VITE_BASE=/
|
VITE_BASE=/
|
||||||
# VITE_BASE_URL=http://192.168.0.25:8095/
|
# VITE_BASE_URL=http://192.168.0.25:8095/
|
||||||
VITE_BASE_URL=http://192.168.0.37:8095/
|
VITE_BASE_URL=http://192.168.4.40:8095/
|
||||||
|
|
||||||
#websocket t掉线
|
#websocket t掉线
|
||||||
VITE_ON_LINE_WEB_SOCKET_URL=ws://192.168.0.37:8869
|
VITE_ON_LINE_WEB_SOCKET_URL=ws://192.168.4.40:8869
|
||||||
|
|
||||||
#websocket 在线学习
|
#websocket 在线学习
|
||||||
VITE_LEARNING_WEB_SOCKET_URL=ws://192.168.0.37:8899
|
VITE_LEARNING_WEB_SOCKET_URL=ws://192.168.4.40:8899
|
||||||
|
|
||||||
|
#websocket 大华设备视频
|
||||||
|
VITE_DAHUA_WEB_SOCKET_URL=ws://192.168.4.40:8866
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,12 @@ VITE_BASE_URL=http://172.16.70.226:8081/sx_yjb/
|
||||||
#VITE_BASE_URL=https://qaaqwh.qhdsafety.com/integrated_whb/
|
#VITE_BASE_URL=https://qaaqwh.qhdsafety.com/integrated_whb/
|
||||||
|
|
||||||
#websocket t掉线
|
#websocket t掉线
|
||||||
VITE_ON_LINE_WEB_SOCKET_URL=ws://183.251.104.38:10103
|
VITE_ON_LINE_WEB_SOCKET_URL=ws://172.16.70.226:8869
|
||||||
# VITE_ON_LINE_WEB_SOCKET_URL=wss://qaaqwh.qhdsafety.com/disconnected/
|
# VITE_ON_LINE_WEB_SOCKET_URL=wss://qaaqwh.qhdsafety.com/disconnected/
|
||||||
|
|
||||||
#websocket 在线学习
|
#websocket 在线学习
|
||||||
VITE_LEARNING_WEB_SOCKET_URL=ws://183.251.104.38:10102
|
VITE_LEARNING_WEB_SOCKET_URL=ws://172.16.70.226:8899
|
||||||
#VITE_LEARNING_WEB_SOCKET_URL=wss://qaaqwh.qhdsafety.com/onlinelearning/
|
#VITE_LEARNING_WEB_SOCKET_URL=wss://qaaqwh.qhdsafety.com/onlinelearning/
|
||||||
|
|
||||||
|
#websocket 大华设备视频
|
||||||
|
VITE_DAHUA_WEB_SOCKET_URL=ws://172.16.70.226:8866
|
||||||
|
|
|
||||||
|
|
@ -19,4 +19,8 @@ interface ImportMetaEnv {
|
||||||
* websocket 在线学习
|
* websocket 在线学习
|
||||||
*/
|
*/
|
||||||
readonly VITE_LEARNING_WEB_SOCKET_URL: string
|
readonly VITE_LEARNING_WEB_SOCKET_URL: string
|
||||||
|
/**
|
||||||
|
* websocket 大华设备视频
|
||||||
|
*/
|
||||||
|
readonly VITE_DAHUA_WEB_SOCKET_URL: string
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,12 +20,14 @@
|
||||||
"dayjs": "^1.11.10",
|
"dayjs": "^1.11.10",
|
||||||
"echarts": "^5.4.3",
|
"echarts": "^5.4.3",
|
||||||
"element-plus": "^2.6.1",
|
"element-plus": "^2.6.1",
|
||||||
|
"flv.js": "^1.6.2",
|
||||||
"hls.js": "^1.6.13",
|
"hls.js": "^1.6.13",
|
||||||
"html2canvas": "^1.4.1",
|
"html2canvas": "^1.4.1",
|
||||||
"jspdf": "^2.5.1",
|
"jspdf": "^2.5.1",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"mitt": "^3.0.1",
|
"mitt": "^3.0.1",
|
||||||
"mp4box": "^0.5.2",
|
"mp4box": "^0.5.2",
|
||||||
|
"mpegts.js": "^1.8.0",
|
||||||
"nanoid": "^5.0.4",
|
"nanoid": "^5.0.4",
|
||||||
"normalize.css": "^8.0.1",
|
"normalize.css": "^8.0.1",
|
||||||
"pako": "^2.1.0",
|
"pako": "^2.1.0",
|
||||||
|
|
@ -3362,6 +3364,11 @@
|
||||||
"es6-symbol": "^3.1.1"
|
"es6-symbol": "^3.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/es6-promise": {
|
||||||
|
"version": "4.2.8",
|
||||||
|
"resolved": "https://registry.npmmirror.com/es6-promise/-/es6-promise-4.2.8.tgz",
|
||||||
|
"integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="
|
||||||
|
},
|
||||||
"node_modules/es6-symbol": {
|
"node_modules/es6-symbol": {
|
||||||
"version": "3.1.4",
|
"version": "3.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz",
|
||||||
|
|
@ -4110,6 +4117,15 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/flv.js": {
|
||||||
|
"version": "1.6.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/flv.js/-/flv.js-1.6.2.tgz",
|
||||||
|
"integrity": "sha512-xre4gUbX1MPtgQRKj2pxJENp/RnaHaxYvy3YToVVCrSmAWUu85b9mug6pTXF6zakUjNP2lFWZ1rkSX7gxhB/2A==",
|
||||||
|
"dependencies": {
|
||||||
|
"es6-promise": "^4.2.8",
|
||||||
|
"webworkify-webpack": "^2.1.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/follow-redirects": {
|
"node_modules/follow-redirects": {
|
||||||
"version": "1.15.11",
|
"version": "1.15.11",
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
|
||||||
|
|
@ -5445,6 +5461,15 @@
|
||||||
"integrity": "sha512-GcCH0fySxBurJtvr0dfhz0IxHZjc1RP+F+I8xw+LIwkU1a+7HJx8NCDiww1I5u4Hz6g4eR1JlGADEGJ9r4lSfA==",
|
"integrity": "sha512-GcCH0fySxBurJtvr0dfhz0IxHZjc1RP+F+I8xw+LIwkU1a+7HJx8NCDiww1I5u4Hz6g4eR1JlGADEGJ9r4lSfA==",
|
||||||
"license": "BSD-3-Clause"
|
"license": "BSD-3-Clause"
|
||||||
},
|
},
|
||||||
|
"node_modules/mpegts.js": {
|
||||||
|
"version": "1.8.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/mpegts.js/-/mpegts.js-1.8.0.tgz",
|
||||||
|
"integrity": "sha512-ZtujqtmTjWgcDDkoOnLvrOKUTO/MKgLHM432zGDI8oPaJ0S+ebPxg1nEpDpLw6I7KmV/GZgUIrfbWi3qqEircg==",
|
||||||
|
"dependencies": {
|
||||||
|
"es6-promise": "^4.2.5",
|
||||||
|
"webworkify-webpack": "github:xqq/webworkify-webpack"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
|
|
@ -7681,6 +7706,11 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/webworkify-webpack": {
|
||||||
|
"version": "2.1.5",
|
||||||
|
"resolved": "git+ssh://git@github.com/xqq/webworkify-webpack.git#24d1e719b4a6cac37a518b2bb10fe124527ef4ef",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/which": {
|
"node_modules/which": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
|
|
|
||||||
|
|
@ -22,12 +22,14 @@
|
||||||
"dayjs": "^1.11.10",
|
"dayjs": "^1.11.10",
|
||||||
"echarts": "^5.4.3",
|
"echarts": "^5.4.3",
|
||||||
"element-plus": "^2.6.1",
|
"element-plus": "^2.6.1",
|
||||||
|
"flv.js": "^1.6.2",
|
||||||
"hls.js": "^1.6.13",
|
"hls.js": "^1.6.13",
|
||||||
"html2canvas": "^1.4.1",
|
"html2canvas": "^1.4.1",
|
||||||
"jspdf": "^2.5.1",
|
"jspdf": "^2.5.1",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"mitt": "^3.0.1",
|
"mitt": "^3.0.1",
|
||||||
"mp4box": "^0.5.2",
|
"mp4box": "^0.5.2",
|
||||||
|
"mpegts.js": "^1.8.0",
|
||||||
"nanoid": "^5.0.4",
|
"nanoid": "^5.0.4",
|
||||||
"normalize.css": "^8.0.1",
|
"normalize.css": "^8.0.1",
|
||||||
"pako": "^2.1.0",
|
"pako": "^2.1.0",
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<el-dialog v-model="dialogVisible" title="播放后台转码视频" width="50%">
|
<el-dialog v-model="dialogVisible" title="播放大华设备视频" width="50%">
|
||||||
<!-- 原生video播放器 - 移除了controls属性以去掉默认控制栏 -->
|
<!-- 原生video播放器 - 移除了controls属性以去掉默认控制栏 -->
|
||||||
<video ref="videoRef" playsinline class="video-player"></video>
|
<video ref="videoRef" playsinline class="video-player"></video>
|
||||||
|
|
||||||
|
|
@ -11,12 +11,10 @@
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, watch } from "vue";
|
import { ref, watch, nextTick } from "vue";
|
||||||
import Hls from "hls.js"; // 引入HLS解析库
|
import mpegts from "mpegts.js"; // 引入mpegts.js解析库
|
||||||
import { ElMessage } from "element-plus";
|
import { ElMessage } from "element-plus";
|
||||||
import { getTranscodeStatus, stopTransCode } from "@/request/video_info.js"; // 后端“停止转码”接口
|
|
||||||
|
|
||||||
// 接收父组件传递的属性
|
// 接收父组件传递的属性
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
|
@ -24,54 +22,35 @@ const props = defineProps({
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
src: {
|
channel: {
|
||||||
type: String,
|
type: Number,
|
||||||
required: true, // 必须传递HLS流地址
|
default: 0, // 通道号,默认为0
|
||||||
},
|
|
||||||
videoId: {
|
|
||||||
type: String,
|
|
||||||
required: true, // 视频Id
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// 向父组件传递事件
|
// 向父组件传递事件
|
||||||
const emit = defineEmits(["update:visible", "onStop"]);
|
const emit = defineEmits(["update:visible"]);
|
||||||
|
|
||||||
// 控制弹窗显示(内部响应props.visible)
|
// 控制弹窗显示(内部响应props.visible)
|
||||||
const dialogVisible = ref(props.visible);
|
const dialogVisible = ref(props.visible);
|
||||||
const videoRef = ref(null); // video元素引用
|
const videoRef = ref(null); // video元素引用
|
||||||
const isPlaying = ref(false); // 视频播放状态
|
let player = null; // MPEGTS播放器实例
|
||||||
let hlsInstance = null; // HLS实例(用于管理流解析)
|
|
||||||
|
|
||||||
// 监听父组件visible变化,同步到内部dialogVisible
|
// 监听父组件visible变化,同步到内部dialogVisible
|
||||||
watch(
|
watch(
|
||||||
() => props.visible,
|
() => props.visible,
|
||||||
(newVal) => {
|
(newVal) => {
|
||||||
dialogVisible.value = newVal;
|
dialogVisible.value = newVal;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// 定义定时器变量
|
|
||||||
let checkInterval = null;
|
|
||||||
|
|
||||||
// 弹窗打开时执行的方法
|
// 弹窗打开时执行的方法
|
||||||
const onDialogOpen = () => {
|
const onDialogOpen = () => {
|
||||||
// 每3秒调用一次接口
|
// 确保DOM更新完成后再初始化播放器
|
||||||
checkInterval = setInterval(async () => {
|
nextTick(() => {
|
||||||
try {
|
// 大华设备直接播放
|
||||||
// 假设接口名为checkVideoProgress,需自行导入
|
handlePlay();
|
||||||
const response = await getTranscodeStatus({ id: props.videoId });
|
});
|
||||||
if (response.progress > 2) {
|
|
||||||
handlePlay();
|
|
||||||
// 播放后清除定时器
|
|
||||||
if (checkInterval) {
|
|
||||||
clearInterval(checkInterval);
|
|
||||||
checkInterval = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 可根据需要处理接口返回结果
|
|
||||||
} catch (error) {}
|
|
||||||
}, 3000); // 3秒间隔
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 监听内部dialogVisible变化,通知父组件
|
// 监听内部dialogVisible变化,通知父组件
|
||||||
|
|
@ -84,127 +63,110 @@ watch(dialogVisible, (newVal) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 播放视频
|
// 初始化MPEGTS播放器
|
||||||
const handlePlay = () => {
|
const initMpegtsPlayer = () => {
|
||||||
const video = videoRef.value;
|
// 如果播放器已存在,先销毁
|
||||||
if (!video) {
|
if (player) {
|
||||||
ElMessage.error("视频播放器初始化失败");
|
player.destroy();
|
||||||
|
player = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保video元素已准备好
|
||||||
|
if (!videoRef.value) {
|
||||||
|
console.error("无法获取video元素");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 先清空之前的资源
|
// 使用mpegts.js创建播放器实例
|
||||||
video.src = "";
|
if (mpegts.isSupported()) {
|
||||||
|
const videoElement = videoRef.value;
|
||||||
|
|
||||||
// 检查src是否有效
|
// 构建WebSocket URL,包含认证信息和通道号
|
||||||
if (!props.src) {
|
const baseUrl = import.meta.env.VITE_DAHUA_WEB_SOCKET_URL || "ws://localhost:8866";
|
||||||
ElMessage.error("视频源地址为空");
|
const separator = baseUrl.includes('?') ? '&' : '?';
|
||||||
return;
|
const websocketUrl = `${baseUrl}${separator}user=user1&channel=${props.channel}`;
|
||||||
}
|
|
||||||
|
|
||||||
// HLS.js 支持检测
|
// 如果播放器已存在,先销毁
|
||||||
if (Hls.isSupported()) {
|
if (player) {
|
||||||
// 如果已有实例,先销毁
|
player.destroy();
|
||||||
if (hlsInstance) {
|
player = null;
|
||||||
hlsInstance.destroy();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hlsInstance = new Hls({
|
// 创建mpegts.js播放器,让它自己管理WebSocket连接
|
||||||
maxBufferLength: 30, // 增加缓冲长度
|
player = mpegts.createPlayer({
|
||||||
maxMaxBufferLength: 60,
|
type: "flv",
|
||||||
|
isLive: true,
|
||||||
|
hasAudio: false,
|
||||||
|
hasVideo: true,
|
||||||
|
url: websocketUrl, // 直接提供WebSocket URL给mpegts.js
|
||||||
|
}, {
|
||||||
|
enableWorker: false,
|
||||||
|
lazyLoad: false,
|
||||||
|
reuseRedirectedURL: false
|
||||||
});
|
});
|
||||||
|
|
||||||
// 监听错误事件
|
// 将播放器附加到video元素
|
||||||
hlsInstance.on(Hls.Events.ERROR, (event, data) => {
|
player.attachMediaElement(videoElement);
|
||||||
if (data.fatal) {
|
|
||||||
switch (data.type) {
|
// 监听播放器事件
|
||||||
case Hls.ErrorTypes.NETWORK_ERROR:
|
player.on(mpegts.Events.MEDIA_INFO, (mediaInfo) => {
|
||||||
hlsInstance.startLoad();
|
console.log('媒体信息:', mediaInfo);
|
||||||
break;
|
|
||||||
case Hls.ErrorTypes.MEDIA_ERROR:
|
|
||||||
hlsInstance.recoverMediaError();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// 无法恢复的错误,需要重新初始化
|
|
||||||
destroyPlayer();
|
|
||||||
handlePlay();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
hlsInstance.loadSource(props.src);
|
player.on(mpegts.Events.ERROR, (type, details) => {
|
||||||
hlsInstance.attachMedia(video);
|
console.error('播放器错误:', type, details);
|
||||||
|
ElMessage.error(`播放器错误: ${type}`);
|
||||||
hlsInstance.on(Hls.Events.MANIFEST_PARSED, () => {
|
|
||||||
video.play().then(() => {
|
|
||||||
isPlaying.value = true;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
} else if (video.canPlayType("application/vnd.apple.mpegurl")) {
|
|
||||||
// Safari等原生支持HLS的浏览器
|
// 加载并播放
|
||||||
video.src = props.src;
|
player.load();
|
||||||
video.addEventListener("loadedmetadata", () => {
|
player.play().catch((e) => {
|
||||||
video
|
console.error("播放失败:", e);
|
||||||
.play()
|
ElMessage.error("播放失败: " + e.message);
|
||||||
.then(() => {
|
|
||||||
isPlaying.value = true;
|
|
||||||
ElMessage.success("视频开始播放");
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
ElMessage.error(`播放失败:${err.message}`);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
ElMessage.error("您的浏览器不支持HLS视频流播放,请更换浏览器");
|
ElMessage.error("浏览器不支持mpegts.js");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 销毁播放器时清除定时器
|
// 播放视频
|
||||||
const destroyPlayer = () => {
|
const handlePlay = () => {
|
||||||
// 清除定时器
|
// 先清空之前的资源
|
||||||
if (checkInterval) {
|
if (videoRef.value) {
|
||||||
clearInterval(checkInterval);
|
videoRef.value.src = "";
|
||||||
checkInterval = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 原有逻辑保持不变
|
// 大华设备播放
|
||||||
isPlaying.value = false;
|
playDahuaVideo();
|
||||||
if (hlsInstance) {
|
};
|
||||||
hlsInstance.destroy();
|
|
||||||
hlsInstance = null;
|
// 播放大华设备视频
|
||||||
|
const playDahuaVideo = () => {
|
||||||
|
if (!mpegts.isSupported()) {
|
||||||
|
ElMessage.error("您的浏览器不支持MPEG-TS视频播放");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 初始化并播放
|
||||||
|
initMpegtsPlayer();
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error("播放器初始化失败:" + error.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 销毁播放器时清除资源
|
||||||
|
const destroyPlayer = () => {
|
||||||
|
// 销毁MPEGTS播放器,它会自动处理WebSocket连接的关闭
|
||||||
|
if (player) {
|
||||||
|
player.destroy();
|
||||||
|
player = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (videoRef.value) {
|
if (videoRef.value) {
|
||||||
const video = videoRef.value;
|
const video = videoRef.value;
|
||||||
video.pause();
|
video.pause();
|
||||||
video.src = "";
|
video.src = "";
|
||||||
}
|
}
|
||||||
handleStopTranscode();
|
|
||||||
};
|
|
||||||
|
|
||||||
// 停止转码(调用后端接口)
|
|
||||||
const handleStopTranscode = async () => {
|
|
||||||
try {
|
|
||||||
await stopTransCode({ id: props.videoId }); // 调用后端“停止转码”接口
|
|
||||||
emit("onStop"); // 通知父组件“转码已停止”
|
|
||||||
dialogVisible.value = false; // 关闭弹窗
|
|
||||||
} catch (error) {
|
|
||||||
if (error !== "cancel") {
|
|
||||||
ElMessage.error(`停止转码失败:${error.message || "未知错误"}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.video-player {
|
|
||||||
width: 100%;
|
|
||||||
min-height: 600px;
|
|
||||||
object-fit: contain; /* 保持视频比例 */
|
|
||||||
background-color: #000; /* 增加黑色背景,使视频区域更明显 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.video-controls {
|
|
||||||
margin-top: 16px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,6 @@
|
||||||
<el-button native-type="reset" @click="fnResetPagination"
|
<el-button native-type="reset" @click="fnResetPagination"
|
||||||
>重置</el-button
|
>重置</el-button
|
||||||
>
|
>
|
||||||
<el-button type="success" @click="handleStartTranscode"
|
|
||||||
>播放后台转码视频</el-button
|
|
||||||
>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
@ -106,12 +103,11 @@
|
||||||
@get-data="fnResetPagination"
|
@get-data="fnResetPagination"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- 转码视频播放器(修改后:双向绑定visible + 传递src) -->
|
<!-- 大华设备视频播放器 -->
|
||||||
<play-video
|
<play-video
|
||||||
v-model:visible="data.transcodeVideoDialog.visible"
|
v-model:visible="data.dahuaVideoDialog.visible"
|
||||||
:src="data.transcodeVideoDialog.src"
|
:channel="data.dahuaVideoDialog.channel"
|
||||||
:video-id="data.transcodeVideoDialog.id"
|
@update:visible="handlePlayVideoClose"
|
||||||
@on-stop="onTranscodeStopped"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -130,8 +126,7 @@ import {ElMessage, ElMessageBox} from "element-plus";
|
||||||
import SelectingPoints from "./components/selecting_points.vue";
|
import SelectingPoints from "./components/selecting_points.vue";
|
||||||
import LayoutVideo from "@/components/video/index.vue";
|
import LayoutVideo from "@/components/video/index.vue";
|
||||||
import {setVideoManagerList} from "@/request/eightwork_videomanager.js";
|
import {setVideoManagerList} from "@/request/eightwork_videomanager.js";
|
||||||
import PlayVideo from "@/views/video_manager/video_manager/components/playVideo.vue"; // 引入新播放器组件
|
import PlayVideo from "@/views/video_manager/video_manager/components/playVideo.vue"; // 引入大华播放器组件
|
||||||
import {startTransCode, stopTransCode} from "@/request/video_info.js"; // 转码接口
|
|
||||||
|
|
||||||
const data = reactive({
|
const data = reactive({
|
||||||
addDialog: {
|
addDialog: {
|
||||||
|
|
@ -155,42 +150,18 @@ const data = reactive({
|
||||||
videomanagerId: "",
|
videomanagerId: "",
|
||||||
visible: false,
|
visible: false,
|
||||||
},
|
},
|
||||||
transcodeVideoDialog: {
|
dahuaVideoDialog: {
|
||||||
visible: false,
|
visible: false,
|
||||||
src: "http://localhost:8100/api/hls/stream.m3u8", // 后端转码后HLS流的访问路径
|
|
||||||
id: "",
|
id: "",
|
||||||
|
channel: 0 // 通道号
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// 启动转码并显示播放器
|
// 列表数据逻辑
|
||||||
const handleStartTranscode = async () => {
|
|
||||||
try {
|
|
||||||
await startTransCode(); // 调用后端“启动转码”接口
|
|
||||||
data.transcodeVideoDialog.visible = true; // 显示播放器弹窗
|
|
||||||
} catch (error) {
|
|
||||||
ElMessage.error("启动转码失败: " + (error.message || "未知错误"));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 转码停止后的回调
|
|
||||||
const onTranscodeStopped = async () => {
|
|
||||||
await stopTransCode(); // 调用后端“停止转码”接口
|
|
||||||
};
|
|
||||||
|
|
||||||
// 列表数据逻辑(保持不变)
|
|
||||||
const {list, pagination, searchForm, fnGetData, fnResetPagination} =
|
const {list, pagination, searchForm, fnGetData, fnResetPagination} =
|
||||||
useListData(getHkVideoManagerList);
|
useListData(getHkVideoManagerList);
|
||||||
|
|
||||||
// 其他方法(保持不变)
|
// 其他方法
|
||||||
// const fnSetPositioning = async (row) => {
|
|
||||||
// data.selectingPointsDialog.id = row.PLS_ID ? row.PLS_ID : "";
|
|
||||||
// data.selectingPointsDialog.indexCode = row.indexCode;
|
|
||||||
// data.selectingPointsDialog.regionPathName = row.regionPathName;
|
|
||||||
// data.selectingPointsDialog.camName = row.name;
|
|
||||||
// data.selectingPointsDialog.videomanagerId = row.videomanagerId;
|
|
||||||
// data.selectingPointsDialog.visible = true;
|
|
||||||
// };
|
|
||||||
|
|
||||||
const fnUpToBi = async (videomanagerId) => {
|
const fnUpToBi = async (videomanagerId) => {
|
||||||
await ElMessageBox.confirm("确定要置顶吗?置顶后将会默认展示在Bi页", {
|
await ElMessageBox.confirm("确定要置顶吗?置顶后将会默认展示在Bi页", {
|
||||||
type: "warning",
|
type: "warning",
|
||||||
|
|
@ -200,14 +171,16 @@ const fnUpToBi = async (videomanagerId) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const fnPreviewVideo = async (row) => {
|
const fnPreviewVideo = async (row) => {
|
||||||
try {
|
// 直接播放大华设备视频
|
||||||
const resData = await startTransCode({url: row.url, id: row.PLS_ID}); // 调用后端“启动转码”接口
|
data.dahuaVideoDialog.visible = true;
|
||||||
data.transcodeVideoDialog.visible = true; // 显示播放器弹窗
|
data.dahuaVideoDialog.channel = row.channel;
|
||||||
data.transcodeVideoDialog.id = row.PLS_ID; // 显示播放器弹窗
|
console.log("播放大华设备视频:", row.channel);
|
||||||
data.transcodeVideoDialog.src =
|
};
|
||||||
"http://172.16.70.226:7811/" + resData.videoUrl + "stream.m3u8";
|
|
||||||
} catch (error) {
|
const handlePlayVideoClose = (visible) => {
|
||||||
ElMessage.error("启动转码失败: " + (error.message || "未知错误"));
|
if (!visible) {
|
||||||
|
// 视频播放窗口关闭时的处理逻辑
|
||||||
|
console.log("视频播放窗口已关闭");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue