parent
ab54e66297
commit
c028f200aa
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@ -0,0 +1,240 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
id="app"
|
||||||
|
class="parent"
|
||||||
|
>
|
||||||
|
<video
|
||||||
|
style="width: 100%; height: 100%; object-fit: fill;"
|
||||||
|
ref="video"
|
||||||
|
muted
|
||||||
|
autoplay
|
||||||
|
>
|
||||||
|
</video>
|
||||||
|
<!-- <ptz v-if="ptzShow" :cameraId="cameraId" /> -->
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { ZLMRTCClient } from "../camera/ZLMediaKit-Webrtc-Vue/ZLMRTCClient";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
cameraId: {
|
||||||
|
default: 2,
|
||||||
|
},
|
||||||
|
ptzShow: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
video: null,
|
||||||
|
selfVideo: null,
|
||||||
|
url: null,
|
||||||
|
simulcast: null,
|
||||||
|
useCamera: null,
|
||||||
|
audioEnable: null,
|
||||||
|
videoEnable: null,
|
||||||
|
datachannel: null,
|
||||||
|
msgsend: null,
|
||||||
|
msgrecv: "",
|
||||||
|
streamUrl: "",
|
||||||
|
player: null,
|
||||||
|
recvOnly: true,
|
||||||
|
resolutions: [],
|
||||||
|
resolution: "",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
cameraId(newCameraId, oldCameraId) {
|
||||||
|
console.log(newCameraId);
|
||||||
|
if (newCameraId !== oldCameraId) {
|
||||||
|
this.getHttp()
|
||||||
|
this.startVideo();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.video = this.$refs.video;
|
||||||
|
|
||||||
|
this.getHttp()
|
||||||
|
ZLMRTCClient.GetAllScanResolution().forEach((r, i) => {
|
||||||
|
let text = r.label + "(" + r.width + "x" + r.height + ")";
|
||||||
|
this.resolutions.push({
|
||||||
|
text: text,
|
||||||
|
value: r,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
this.resolution = this.resolutions[0].text;
|
||||||
|
this.startVideo();
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
this.stopVideo();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getHttp(){
|
||||||
|
this.streamUrl =
|
||||||
|
|
||||||
|
"http://127.0.0.1:8096/index/api/webrtc?app=live&stream=camera" +
|
||||||
|
this.cameraId +
|
||||||
|
"&type=play";
|
||||||
|
},
|
||||||
|
radioChange(value) {
|
||||||
|
let urlObj = new URL(this.streamUrl);
|
||||||
|
urlObj.searchParams.set("type", value);
|
||||||
|
this.streamUrl = urlObj.href;
|
||||||
|
|
||||||
|
if (value == "play") {
|
||||||
|
this.recvOnly = true;
|
||||||
|
} else if (value == "echo") {
|
||||||
|
this.recvOnly = false;
|
||||||
|
} else {
|
||||||
|
this.recvOnly = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
changeResolution(e) {
|
||||||
|
this.resolution = e.target.options[e.target.selectedIndex].text;
|
||||||
|
},
|
||||||
|
start_play() {
|
||||||
|
console.log(this.cameraId);
|
||||||
|
let res = this.resolution.match(/\d+/g);
|
||||||
|
let h = parseInt(res.pop());
|
||||||
|
let w = parseInt(res.pop());
|
||||||
|
|
||||||
|
this.player = new ZLMRTCClient.Endpoint({
|
||||||
|
element: this.video,
|
||||||
|
debug: false,
|
||||||
|
zlmsdpUrl: this.streamUrl,
|
||||||
|
simulecast: false,
|
||||||
|
useCamera: false,
|
||||||
|
audioEnable: false,
|
||||||
|
videoEnable: true,
|
||||||
|
recvOnly: this.recvOnly,
|
||||||
|
usedatachannel: false,
|
||||||
|
resolution: {
|
||||||
|
w: "100px",
|
||||||
|
h: "100px",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
this.player.on(
|
||||||
|
ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR,
|
||||||
|
(e) => {
|
||||||
|
console.log("ICE 协商出错");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.player.on(
|
||||||
|
ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS,
|
||||||
|
(e) => {
|
||||||
|
console.log("播放成功", e.streams);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.player.on(
|
||||||
|
ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED,
|
||||||
|
(e) => {
|
||||||
|
console.log("offer anwser 交换失败", e);
|
||||||
|
this.stopVideo();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.player.on(ZLMRTCClient.Events.WEBRTC_ON_LOCAL_STREAM, (s) => {
|
||||||
|
this.selfVideo.srcObject = s;
|
||||||
|
this.selfVideo.muted = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.player.on(ZLMRTCClient.Events.CAPTURE_STREAM_FAILED, (s) => {
|
||||||
|
console.log("获取本地流失败");
|
||||||
|
});
|
||||||
|
|
||||||
|
this.player.on(
|
||||||
|
ZLMRTCClient.Events.WEBRTC_ON_CONNECTION_STATE_CHANGE,
|
||||||
|
(state) => {
|
||||||
|
console.log("当前状态==>", state);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.player.on(
|
||||||
|
ZLMRTCClient.Events.WEBRTC_ON_DATA_CHANNEL_OPEN,
|
||||||
|
(event) => {
|
||||||
|
console.log("rtc datachannel 打开 :", event);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.player.on(
|
||||||
|
ZLMRTCClient.Events.WEBRTC_ON_DATA_CHANNEL_MSG,
|
||||||
|
(event) => {
|
||||||
|
console.log("rtc datachannel 消息 :", event.data);
|
||||||
|
this.msgrecv = event.data;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.player.on(
|
||||||
|
ZLMRTCClient.Events.WEBRTC_ON_DATA_CHANNEL_ERR,
|
||||||
|
(event) => {
|
||||||
|
console.log("rtc datachannel 错误 :", event);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.player.on(
|
||||||
|
ZLMRTCClient.Events.WEBRTC_ON_DATA_CHANNEL_CLOSE,
|
||||||
|
(event) => {
|
||||||
|
console.log("rtc datachannel 关闭 :", event);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
startVideo() {
|
||||||
|
this.stopVideo();
|
||||||
|
let res = this.resolution.match(/\d+/g);
|
||||||
|
let h = "50%";
|
||||||
|
let w = "50%";
|
||||||
|
this.start_play();
|
||||||
|
},
|
||||||
|
stopVideo() {
|
||||||
|
if (this.player) {
|
||||||
|
this.player.close();
|
||||||
|
this.player = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
send() {
|
||||||
|
if (this.player) {
|
||||||
|
this.player.sendMsg(this.msgsend);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
close() {
|
||||||
|
if (this.player) {
|
||||||
|
this.player.closeDataChannel();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.parent {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operation-item img {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
margin: 0 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.direction-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.direction-item img {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
margin: 0 5px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,283 @@
|
|||||||
|
<template>
|
||||||
|
<div class="ptz-control">
|
||||||
|
<div class="direction-list">
|
||||||
|
<div class="operation-item">
|
||||||
|
<Icon
|
||||||
|
:icon="zoomSubIcon"
|
||||||
|
alt="Zoom In"
|
||||||
|
@mousedown="handleControl('zoomSub', 'start')"
|
||||||
|
@mouseup="handleControl('zoomSub', 'stop')"
|
||||||
|
@mouseleave="handleControl('zoomSub', 'stop')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<span>变倍</span>
|
||||||
|
<div class="operation-item">
|
||||||
|
<Icon
|
||||||
|
:icon="zoomAddIcon"
|
||||||
|
alt="Zoom Out"
|
||||||
|
@mousedown="handleControl('zoomAdd', 'start')"
|
||||||
|
@mouseup="handleControl('zoomAdd', 'stop')"
|
||||||
|
@mouseleave="handleControl('zoomAdd', 'stop')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="operation-item">
|
||||||
|
<Icon
|
||||||
|
:icon="focusSubIcon"
|
||||||
|
alt="Focus In"
|
||||||
|
@mousedown="handleControl('focusSub', 'start')"
|
||||||
|
@mouseup="handleControl('focusSub', 'stop')"
|
||||||
|
@mouseleave="handleControl('focusSub', 'stop')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span>变焦</span>
|
||||||
|
<div class="operation-item">
|
||||||
|
<Icon
|
||||||
|
:icon="focusAddIcon"
|
||||||
|
alt="Focus Out"
|
||||||
|
@mousedown="handleControl('focusAdd', 'start')"
|
||||||
|
@mouseup="handleControl('focusAdd', 'stop')"
|
||||||
|
@mouseleave="handleControl('focusAdd', 'stop')"
|
||||||
|
/>
|
||||||
|
<span></span>
|
||||||
|
</div>
|
||||||
|
<div class="operation-item">
|
||||||
|
<Icon
|
||||||
|
:icon="irisSubIcon"
|
||||||
|
alt="Iris Open"
|
||||||
|
@mousedown="handleControl('irisSub', 'start')"
|
||||||
|
@mouseup="handleControl('irisSub', 'stop')"
|
||||||
|
@mouseleave="handleControl('irisSub', 'stop')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<span>光圈</span>
|
||||||
|
<div class="operation-item">
|
||||||
|
<Icon
|
||||||
|
:icon="irisAddIcon"
|
||||||
|
alt="Iris Close"
|
||||||
|
@mousedown="handleControl('irisAdd', 'start')"
|
||||||
|
@mouseup="handleControl('irisAdd', 'stop')"
|
||||||
|
@mouseleave="handleControl('irisAdd', 'stop')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="direction-item">
|
||||||
|
<Icon
|
||||||
|
:icon="leftUpIcon"
|
||||||
|
alt="Left Up"
|
||||||
|
@mousedown="handleControl('leftUp', 'start')"
|
||||||
|
@mouseup="handleControl('leftUp', 'stop')"
|
||||||
|
@mouseleave="handleControl('leftUp', 'stop')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="direction-item">
|
||||||
|
<Icon
|
||||||
|
:icon="upIcon"
|
||||||
|
alt="Up"
|
||||||
|
@mousedown="handleControl('up', 'start')"
|
||||||
|
@mouseup="handleControl('up', 'stop')"
|
||||||
|
@mouseleave="handleControl('up', 'stop')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="direction-item">
|
||||||
|
<Icon
|
||||||
|
:icon="rightUpIcon"
|
||||||
|
alt="Right Up"
|
||||||
|
@mousedown="handleControl('rightUp', 'start')"
|
||||||
|
@mouseup="handleControl('rightUp', 'stop')"
|
||||||
|
@mouseleave="handleControl('rightUp', 'stop')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="direction-item">
|
||||||
|
<Icon
|
||||||
|
:icon="leftIcon"
|
||||||
|
alt="Left"
|
||||||
|
@mousedown="handleControl('left', 'start')"
|
||||||
|
@mouseup="handleControl('left', 'stop')"
|
||||||
|
@mouseleave="handleControl('left', 'stop')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="direction-item">
|
||||||
|
<Icon
|
||||||
|
:icon="centerIcon"
|
||||||
|
alt="Center"
|
||||||
|
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="direction-item">
|
||||||
|
<Icon
|
||||||
|
:icon="rightIcon"
|
||||||
|
alt="Right"
|
||||||
|
@mousedown="handleControl('right', 'start')"
|
||||||
|
@mouseup="handleControl('right', 'stop')"
|
||||||
|
@mouseleave="handleControl('right', 'stop')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="direction-item">
|
||||||
|
<Icon
|
||||||
|
:icon="leftDownIcon"
|
||||||
|
alt="Left Down"
|
||||||
|
@mousedown="handleControl('leftDown', 'start')"
|
||||||
|
@mouseup="handleControl('leftDown', 'stop')"
|
||||||
|
@mouseleave="handleControl('leftDown', 'stop')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="direction-item">
|
||||||
|
<Icon
|
||||||
|
:icon="downIcon"
|
||||||
|
alt="Down"
|
||||||
|
@mousedown="handleControl('down', 'start')"
|
||||||
|
@mouseup="handleControl('down', 'stop')"
|
||||||
|
@mouseleave="handleControl('down', 'stop')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="direction-item">
|
||||||
|
<Icon
|
||||||
|
:icon="rightDownIcon"
|
||||||
|
alt="Right Down"
|
||||||
|
@mousedown="handleControl('rightDown', 'start')"
|
||||||
|
@mouseup="handleControl('rightDown', 'stop')"
|
||||||
|
@mouseleave="handleControl('rightDown', 'stop')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import axios from 'axios' // 确保 axios 已经安装并引入
|
||||||
|
import { config } from '@/config/axios/config'
|
||||||
|
import {CameraControlApi} from '@/api/cameraControl/camera'
|
||||||
|
|
||||||
|
const { result_code, base_url, request_timeout } = config
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
cameraId: {
|
||||||
|
default: 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
|
console.log(props.cameraId)
|
||||||
|
// 定义响应式数据
|
||||||
|
const zoomSubIcon = ref('ep:zoom-out') // 替换为实际的图标名称
|
||||||
|
const zoomAddIcon = ref('ep:zoom-in') // 替换为实际的图标名称
|
||||||
|
const focusSubIcon = ref('ep:minus') // 替换为实际的图标名称
|
||||||
|
const focusAddIcon = ref('ep:plus') // 替换为实际的图标名称
|
||||||
|
const irisSubIcon = ref('ep:minus') // 替换为实际的图标名称
|
||||||
|
const irisAddIcon = ref('ep:plus') // 替换为实际的图标名称
|
||||||
|
const leftUpIcon = ref('ep:top-left') // 替换为实际的图标名称
|
||||||
|
const upIcon = ref('ep:arrow-up') // 替换为实际的图标名称
|
||||||
|
const rightUpIcon = ref('ep:top-right') // 替换为实际的图标名称
|
||||||
|
const leftIcon = ref('ep:arrow-left') // 替换为实际的图标名称
|
||||||
|
const centerIcon = ref('ep:aim') // 替换为实际的图标名称
|
||||||
|
const rightIcon = ref('ep:arrow-right') // 替换为实际的图标名称
|
||||||
|
const leftDownIcon = ref('ep:bottom-left') // 替换为实际的图标名称
|
||||||
|
const downIcon = ref('ep:arrow-down') // 替换为实际的图标名称
|
||||||
|
const rightDownIcon = ref('ep:bottom-right') // 替换为实际的图标名称
|
||||||
|
|
||||||
|
const activeIcons = {
|
||||||
|
zoomSub: 'ep:zoom-out-active',
|
||||||
|
zoomAdd: 'ep:zoom-in-active',
|
||||||
|
focusSub: 'ep:minus-active',
|
||||||
|
focusAdd: 'ep:plus-active',
|
||||||
|
irisSub: 'ep:minus-active',
|
||||||
|
irisAdd: 'ep:plus-active',
|
||||||
|
leftUp: 'ep:top-left-active',
|
||||||
|
up: 'ep:arrow-up-active',
|
||||||
|
rightUp: 'ep:top-right-active',
|
||||||
|
left: 'ep:arrow-left-active',
|
||||||
|
center: 'ep:aim-active',
|
||||||
|
right: 'ep:arrow-right-active',
|
||||||
|
leftDown: 'ep:bottom-left-active',
|
||||||
|
down: 'ep:arrow-down-active',
|
||||||
|
rightDown: 'ep:bottom-right-active'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义控制方法
|
||||||
|
const handleControl = (action, state) => {
|
||||||
|
console.log(`${action}-${state}`)
|
||||||
|
const iconRef = `${action}Icon`
|
||||||
|
if (state === 'start') {
|
||||||
|
if (activeIcons[action]) {
|
||||||
|
eval(`${iconRef}.value = activeIcons[action]`)
|
||||||
|
}
|
||||||
|
} else if (state === 'stop') {
|
||||||
|
eval(`${iconRef}.value = '${eval(iconRef).value.replace('-active', '')}'`)
|
||||||
|
}
|
||||||
|
const data ={"id":props.cameraId}
|
||||||
|
CameraControlApi.cameraControl(`/camera/control/${action}/${state}`,data)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const close = () => {
|
||||||
|
// 关闭控制面板的逻辑
|
||||||
|
console.log('关闭控制面板')
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
zoomSubIcon,
|
||||||
|
zoomAddIcon,
|
||||||
|
focusSubIcon,
|
||||||
|
focusAddIcon,
|
||||||
|
irisSubIcon,
|
||||||
|
irisAddIcon,
|
||||||
|
leftUpIcon,
|
||||||
|
upIcon,
|
||||||
|
rightUpIcon,
|
||||||
|
leftIcon,
|
||||||
|
centerIcon,
|
||||||
|
rightIcon,
|
||||||
|
leftDownIcon,
|
||||||
|
downIcon,
|
||||||
|
rightDownIcon,
|
||||||
|
handleControl,
|
||||||
|
close
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.ptz-control-panel {
|
||||||
|
position: relative; /* 改为绝对定位 */
|
||||||
|
bottom: 20px;
|
||||||
|
right: 20px;
|
||||||
|
background-color: rgba(255, 255, 255, 0.8);
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
z-index: 1000; /* 确保浮在最上层 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.operation-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operation-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operation-item img {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.direction-list {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 40px);
|
||||||
|
grid-gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.direction-item img {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
Reference in New Issue