视频播放同时支持h265和h264播放
This commit is contained in:
parent
c3f43f6e71
commit
489ec29184
@ -12,9 +12,10 @@ export function addStreamProxy(params?: any) {
|
|||||||
params,
|
params,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addFFmpegStreamProxy(params?: any) {
|
export function addFFmpegStreamProxy(params?: any) {
|
||||||
return requestClient.post<AddStreamProxyResult>(
|
return requestClient.post<AddStreamProxyResult>(
|
||||||
'sis/stream/realtime/addFfmpeg',
|
'sis/stream/FFmpeg/proxy',
|
||||||
params,
|
params,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
21
apps/web-antd/src/utils/video.ts
Normal file
21
apps/web-antd/src/utils/video.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* 验证浏览器是否支持播放h265 编码的视频流
|
||||||
|
*/
|
||||||
|
export function checkHEVCSupport() {
|
||||||
|
const video = document.createElement('video');
|
||||||
|
const h265Support = {
|
||||||
|
hevc: false,
|
||||||
|
hvc1: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 测试不同的HEVC MIME类型
|
||||||
|
if (video.canPlayType) {
|
||||||
|
h265Support.hevc =
|
||||||
|
video.canPlayType('video/mp4; codecs="hev1.1.6.L93.B0"') !== '';
|
||||||
|
h265Support.hvc1 =
|
||||||
|
video.canPlayType('video/mp4; codecs="hvc1.1.6.L93.B0"') !== '';
|
||||||
|
}
|
||||||
|
const result = h265Support.hevc || h265Support.hvc1;
|
||||||
|
console.log('当前浏览器是否支持h265:' + result);
|
||||||
|
return result;
|
||||||
|
}
|
@ -27,10 +27,12 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import DpTree from './dp-tree.vue';
|
import DpTree from './dp-tree.vue';
|
||||||
import { Page } from '@vben/common-ui';
|
import { Page } from '@vben/common-ui';
|
||||||
import { onMounted, ref, toRaw } from 'vue';
|
import { onMounted, onUnmounted, ref, toRaw } from 'vue';
|
||||||
import mpegts from 'mpegts.js';
|
import mpegts from 'mpegts.js';
|
||||||
import { addStreamProxy } from '#/api/sis/stream';
|
import { addFFmpegStreamProxy, addStreamProxy } from '#/api/sis/stream';
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
|
import { checkHEVCSupport } from '#/utils/video';
|
||||||
|
import type { AddStreamProxyResult } from '#/api/sis/stream/model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 屏幕播放器数量
|
* 屏幕播放器数量
|
||||||
@ -74,7 +76,7 @@ function handleParentNoe(node: any, newNode: any[] = []) {
|
|||||||
if (item.children && item.children.length >= 1) {
|
if (item.children && item.children.length >= 1) {
|
||||||
handleParentNoe(item.children, newNode);
|
handleParentNoe(item.children, newNode);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -138,6 +140,35 @@ function onNodeChecked(
|
|||||||
// 播放器数据, 每一个位置代表页面上行的一个矩形
|
// 播放器数据, 每一个位置代表页面上行的一个矩形
|
||||||
const playerList: any[] = [];
|
const playerList: any[] = [];
|
||||||
|
|
||||||
|
function streamProxy(nodeData: any, cb: Function) {
|
||||||
|
let params = {};
|
||||||
|
if (nodeData.nvrIp) {
|
||||||
|
params = {
|
||||||
|
videoIp: nodeData.nvrIp,
|
||||||
|
videoPort: nodeData.nvrPort,
|
||||||
|
factoryNo: nodeData.nvrFactoryNo,
|
||||||
|
account: nodeData.nvrAccount,
|
||||||
|
pwd: nodeData.nvrPwd,
|
||||||
|
channelId: nodeData.nvrChannelNo,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
params = {
|
||||||
|
videoIp: nodeData.deviceIp,
|
||||||
|
videoPort: nodeData.devicePort,
|
||||||
|
factoryNo: nodeData.factoryNo,
|
||||||
|
account: nodeData.deviceAccount,
|
||||||
|
pwd: nodeData.devicePwd,
|
||||||
|
channelId: nodeData.channelNo,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isSupportH265) {
|
||||||
|
addStreamProxy(params).then((res) => cb(res));
|
||||||
|
} else {
|
||||||
|
addFFmpegStreamProxy(params).then((res) => cb(res));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 开始播放视频流
|
* 开始播放视频流
|
||||||
* @param nodeData 播放的节点数据
|
* @param nodeData 播放的节点数据
|
||||||
@ -146,16 +177,7 @@ const playerList: any[] = [];
|
|||||||
function doPlayer(nodeData: any, index: number = 0) {
|
function doPlayer(nodeData: any, index: number = 0) {
|
||||||
console.log('index=', index);
|
console.log('index=', index);
|
||||||
if (mpegts.isSupported()) {
|
if (mpegts.isSupported()) {
|
||||||
const params = {
|
streamProxy(nodeData, (res: AddStreamProxyResult) => {
|
||||||
videoIp: nodeData.deviceIp,
|
|
||||||
videoPort: 554,
|
|
||||||
factoryNo: nodeData.factoryNo,
|
|
||||||
account: nodeData.deviceAccount,
|
|
||||||
pwd: nodeData.devicePwd,
|
|
||||||
channelId: nodeData.channelNo ? nodeData.channelNo : 101,
|
|
||||||
};
|
|
||||||
// }
|
|
||||||
addStreamProxy(params).then((res) => {
|
|
||||||
const url = res.wsFlv;
|
const url = res.wsFlv;
|
||||||
// 将url 绑定到 nodeData
|
// 将url 绑定到 nodeData
|
||||||
nodeData.url = url;
|
nodeData.url = url;
|
||||||
@ -195,55 +217,6 @@ function doPlayer(nodeData: any, index: number = 0) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeElPlayer(playerInfo: any, index: number) {
|
|
||||||
const playerData = playerInfo.data;
|
|
||||||
const oldPlayer = playerInfo.player;
|
|
||||||
if (oldPlayer) {
|
|
||||||
closePlayVieo(oldPlayer);
|
|
||||||
}
|
|
||||||
const videoConfig = {
|
|
||||||
type: 'flv',
|
|
||||||
url: playerData.url,
|
|
||||||
isLive: true,
|
|
||||||
hasAudio: false,
|
|
||||||
hasVideo: true,
|
|
||||||
enableWorker: true, // 启用分离的线程进行转码
|
|
||||||
enableStashBuffer: false, // 关闭IO隐藏缓冲区
|
|
||||||
stashInitialSize: 256, // 减少首帧显示等待时长
|
|
||||||
};
|
|
||||||
const playerConfig = {
|
|
||||||
enableErrorRecover: true, // 启用错误恢复
|
|
||||||
autoCleanupMaxBackwardDuration: 30,
|
|
||||||
autoCleanupMinBackwardDuration: 10,
|
|
||||||
};
|
|
||||||
const player = mpegts.createPlayer(videoConfig, playerConfig);
|
|
||||||
const videoElement = itemRefs.value[index];
|
|
||||||
if (videoElement) {
|
|
||||||
player.attachMediaElement(videoElement);
|
|
||||||
player.load();
|
|
||||||
player.play();
|
|
||||||
playerList[index] = {
|
|
||||||
player,
|
|
||||||
data: playerData,
|
|
||||||
el: videoElement,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
console.log('视频播放元素获取异常');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function closePlayVieo(plInfo: any) {
|
|
||||||
if (plInfo) {
|
|
||||||
try {
|
|
||||||
plInfo.pause(); // 暂停
|
|
||||||
plInfo.unload(); // 卸载
|
|
||||||
plInfo.destroy(); // 销毁
|
|
||||||
} catch (e) {
|
|
||||||
console.log('播放器关闭失败,e=', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function closePlayer(index: number) {
|
function closePlayer(index: number) {
|
||||||
// 如果播放器存在,尝试关闭
|
// 如果播放器存在,尝试关闭
|
||||||
const pData = playerList[index];
|
const pData = playerList[index];
|
||||||
@ -274,9 +247,18 @@ function catchUp() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let isSupportH265 = false;
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
// 检测浏览器是否支持h265
|
||||||
|
isSupportH265 = checkHEVCSupport();
|
||||||
setInterval(catchUp, 10000);
|
setInterval(catchUp, 10000);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
for (let i = 0; i < playerList.length; i++) {
|
||||||
|
closePlayer(i);
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
.player {
|
.player {
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<Page class="h-full w-full">
|
<Page class="h-full w-full">
|
||||||
<!-- 设备分组区域 -->
|
<!-- 设备分组区域 -->
|
||||||
<div class="flex h-full gap-[8px]">
|
<div class="flex h-full gap-[8px]">
|
||||||
<ChannelTree class="h-[83vh] w-[300px]" @check="onNodeChecked" />
|
<ChannelTree class="h-full w-[300px]" @check="onNodeChecked" />
|
||||||
|
|
||||||
<!-- 设备分组区域 -->
|
<!-- 设备分组区域 -->
|
||||||
<div class="bg-background flex-1">
|
<div class="bg-background flex-1">
|
||||||
@ -47,13 +47,15 @@ import { Page } from '@vben/common-ui';
|
|||||||
import ChannelTree from './channel-tree.vue';
|
import ChannelTree from './channel-tree.vue';
|
||||||
import mpegts from 'mpegts.js';
|
import mpegts from 'mpegts.js';
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
import { addStreamProxy } from '#/api/sis/stream';
|
import { addFFmpegStreamProxy, addStreamProxy } from '#/api/sis/stream';
|
||||||
import {
|
import {
|
||||||
Svg16FrameIcon,
|
Svg16FrameIcon,
|
||||||
Svg1FrameIcon,
|
Svg1FrameIcon,
|
||||||
Svg4FrameIcon,
|
Svg4FrameIcon,
|
||||||
Svg9FrameIcon,
|
Svg9FrameIcon,
|
||||||
} from '@vben/icons';
|
} from '@vben/icons';
|
||||||
|
import { checkHEVCSupport } from '#/utils/video';
|
||||||
|
import type { AddStreamProxyResult } from '#/api/sis/stream/model';
|
||||||
|
|
||||||
const selected = 'selected';
|
const selected = 'selected';
|
||||||
|
|
||||||
@ -265,6 +267,35 @@ function changeElPlayer(playerInfo: any, index: number) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function streamProxy(nodeData: any, cb: Function) {
|
||||||
|
let params = {};
|
||||||
|
if (nodeData.nvrIp) {
|
||||||
|
params = {
|
||||||
|
videoIp: nodeData.nvrIp,
|
||||||
|
videoPort: nodeData.nvrPort,
|
||||||
|
factoryNo: nodeData.nvrFactoryNo,
|
||||||
|
account: nodeData.nvrAccount,
|
||||||
|
pwd: nodeData.nvrPwd,
|
||||||
|
channelId: nodeData.nvrChannelNo,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
params = {
|
||||||
|
videoIp: nodeData.deviceIp,
|
||||||
|
videoPort: nodeData.devicePort,
|
||||||
|
factoryNo: nodeData.factoryNo,
|
||||||
|
account: nodeData.deviceAccount,
|
||||||
|
pwd: nodeData.devicePwd,
|
||||||
|
channelId: nodeData.channelNo,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isSupportH265) {
|
||||||
|
addStreamProxy(params).then((res) => cb(res));
|
||||||
|
} else {
|
||||||
|
addFFmpegStreamProxy(params).then((res) => cb(res));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 开始播放视频流
|
* 开始播放视频流
|
||||||
* @param nodeData 播放的节点数据
|
* @param nodeData 播放的节点数据
|
||||||
@ -273,27 +304,7 @@ function changeElPlayer(playerInfo: any, index: number) {
|
|||||||
function doPlayer(nodeData: any, index: number = 0) {
|
function doPlayer(nodeData: any, index: number = 0) {
|
||||||
console.log('index=', index);
|
console.log('index=', index);
|
||||||
if (mpegts.isSupported()) {
|
if (mpegts.isSupported()) {
|
||||||
let params = {};
|
streamProxy(nodeData, (res: AddStreamProxyResult) => {
|
||||||
// if (nodeData.nvrIp) {
|
|
||||||
// params = {
|
|
||||||
// videoIp: nodeData.nvrIp,
|
|
||||||
// videoPort: nodeData.nvrPort,
|
|
||||||
// factoryNo: nodeData.nvrFactoryNo,
|
|
||||||
// account: nodeData.nvrAccount,
|
|
||||||
// pwd: nodeData.nvrPwd,
|
|
||||||
// channelId: nodeData.nvrChannelNo,
|
|
||||||
// };
|
|
||||||
// } else {
|
|
||||||
params = {
|
|
||||||
videoIp: nodeData.deviceIp,
|
|
||||||
videoPort: nodeData.devicePort,
|
|
||||||
factoryNo: nodeData.factoryNo,
|
|
||||||
account: nodeData.deviceAccount,
|
|
||||||
pwd: nodeData.devicePwd,
|
|
||||||
channelId: nodeData.channelNo,
|
|
||||||
};
|
|
||||||
// }
|
|
||||||
addStreamProxy(params).then((res) => {
|
|
||||||
const url = res.wsFlv;
|
const url = res.wsFlv;
|
||||||
// 将url 绑定到 nodeData
|
// 将url 绑定到 nodeData
|
||||||
nodeData.url = url;
|
nodeData.url = url;
|
||||||
@ -360,8 +371,10 @@ function closePlayer(index: number) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let isSupportH265 = false;
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 初始化不加载任何视频
|
// 检测浏览器是否支持h265
|
||||||
|
isSupportH265 = checkHEVCSupport();
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
@ -27,11 +27,7 @@ export default defineConfig(async () => {
|
|||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
rewrite: (path) => path.replace(/^\/api/, ''),
|
rewrite: (path) => path.replace(/^\/api/, ''),
|
||||||
// mock代理目标地址
|
// mock代理目标地址
|
||||||
// target: 'http://192.168.43.169:8080',
|
|
||||||
// target: 'https://by.missmoc.top/api/',
|
|
||||||
target: 'http://127.0.0.1:8080',
|
target: 'http://127.0.0.1:8080',
|
||||||
// target: 'http://192.168.0.106:8080',
|
|
||||||
// target: 'http://47.109.37.87:3010',
|
|
||||||
ws: true,
|
ws: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user