352 lines
9.3 KiB
Vue
352 lines
9.3 KiB
Vue
|
|
<template>
|
|||
|
|
<div class="mt20 flex align-center">
|
|||
|
|
<el-input style="width:200px" class="mr10" v-model="keyWoed" placeholder="输入编号"></el-input>
|
|||
|
|
<el-button @click="handleBtn('无人机视频')">无人机视频</el-button>
|
|||
|
|
<el-button @click="handleBtn('音频')">音频</el-button>
|
|||
|
|
</div>
|
|||
|
|
<DraggableResizableVue id="call_div" v-model:x="element.x" v-model:y="element.y" v-model:h="element.height" v-model:w="element.width" v-model:active="element.isActive" class="container">
|
|||
|
|
<div>
|
|||
|
|
<el-row justify="space-around">
|
|||
|
|
<a-tag v-for="item in speakers" :key="item.number" bordered color="green">{{ item.alias || item.number }}</a-tag>
|
|||
|
|
</el-row>
|
|||
|
|
<div id="voice_wave" class="wave"></div>
|
|||
|
|
<audio ref="audioRef" :volume="80 / 100" autoplay controls hidden />
|
|||
|
|
<video id="video" ref="videoRef" autoplay playsinline :hidden="!showVideo" class="video"/>
|
|||
|
|
<video id="localvideo" ref="localvideoRef" autoplay playsinline :hidden="!showlocalVideo" class="video"/>
|
|||
|
|
<video id="pushVideo" ref="pushVideoRef" autoplay playsinline :hidden="!showPushlVideo" class="video" />
|
|||
|
|
<el-row class="buttonContainer" justify="space-around">
|
|||
|
|
<el-button :type="selfGranted ? 'success' : 'pramary'" shape="round" @click="applayFloor">话权</el-button>
|
|||
|
|
<el-button type="danger" shape="round" @click="hangup">挂断</el-button>
|
|||
|
|
</el-row>
|
|||
|
|
</div>
|
|||
|
|
</DraggableResizableVue>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script setup>
|
|||
|
|
import useCallModule from '@/sdk/call';
|
|||
|
|
import useRecorder from '@/hooks/recorder';
|
|||
|
|
import DraggableResizableVue from "draggable-resizable-vue3";
|
|||
|
|
import { nextTick, onMounted, onUnmounted, ref } from "vue";
|
|||
|
|
import SiriWave from "siriwave";
|
|||
|
|
import { init } from "echarts";
|
|||
|
|
let waveContainer;
|
|||
|
|
let audioWave = SiriWave || undefined;
|
|||
|
|
const element = ref({
|
|||
|
|
x: 0,
|
|||
|
|
y: 0,
|
|||
|
|
width: 200,
|
|||
|
|
height: 200,
|
|||
|
|
isActive: false
|
|||
|
|
});
|
|||
|
|
let rootView;
|
|||
|
|
let streamCbId;
|
|||
|
|
let hangupCbId;
|
|||
|
|
let grantCbId;
|
|||
|
|
// 本端视频流 local video stream
|
|||
|
|
let localVideoStream;
|
|||
|
|
// 远端视频流 remote video stream
|
|||
|
|
let remoteVideoStream;
|
|||
|
|
|
|||
|
|
const selfGranted = ref(false);
|
|||
|
|
// 54040254001310305009 视频
|
|||
|
|
// 38467417 音频
|
|||
|
|
const keyWoed = ref('38467417') //关键字
|
|||
|
|
|
|||
|
|
|
|||
|
|
// 是否 半双工用一个Video来显示远端流和本地流。 true 为 共用一个video。 false 为 2个video,分别显示。
|
|||
|
|
const useOneVideoShow = true;
|
|||
|
|
const useMedia = useRecorder();
|
|||
|
|
const CALL = useCallModule();
|
|||
|
|
|
|||
|
|
const audioRef = ref();
|
|||
|
|
// 远端流和Video。
|
|||
|
|
const videoRef = ref();
|
|||
|
|
const showVideo = ref(false);
|
|||
|
|
|
|||
|
|
const localvideoRef = ref(); // 本端Video
|
|||
|
|
const showlocalVideo = ref(false); // 显示本端流
|
|||
|
|
|
|||
|
|
const pushVideoRef = ref();
|
|||
|
|
// 显示推送流 show push video
|
|||
|
|
const showPushlVideo = ref(false);
|
|||
|
|
|
|||
|
|
const showPanel = ref(false);
|
|||
|
|
const speakers = ref([]);
|
|||
|
|
|
|||
|
|
const codeId = ref(null)
|
|||
|
|
|
|||
|
|
|
|||
|
|
// 处理按钮
|
|||
|
|
const handleBtn = (type) =>{
|
|||
|
|
if(!keyWoed.value) return;
|
|||
|
|
let params = { page_size:10,page_index:1,org_id:"",key_word:keyWoed.value}
|
|||
|
|
lemon.basedata.fetchDeviceList(params).then(resp => {
|
|||
|
|
if(resp.device_list[0].basedata_id){
|
|||
|
|
let basedata_id = resp.device_list[0].basedata_id
|
|||
|
|
debugger
|
|||
|
|
switch(type){
|
|||
|
|
case '无人机视频':
|
|||
|
|
let obj = { basedata_id , video_frame_size: 3, hook_flag: 0 }
|
|||
|
|
window.lemon.call.makeVideoCall(obj)?.then((resp) => {
|
|||
|
|
console.log('无人机视频', resp);
|
|||
|
|
codeId.value = resp.call_id
|
|||
|
|
});
|
|||
|
|
break;
|
|||
|
|
case '音频':
|
|||
|
|
let obj1 = { basedata_id,"hook_flag":0 }
|
|||
|
|
window.lemon.call.makeVoiceCall(obj1)?.then((resp) => {
|
|||
|
|
console.log('音频', resp);
|
|||
|
|
codeId.value = resp.call_id
|
|||
|
|
});
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}).catch(err => {})
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// register media stream callback,include audio and video stream
|
|||
|
|
const registerStream = () => {
|
|||
|
|
streamCbId = window.lemon.call.addMediaStream((call_id, stream, type) => {
|
|||
|
|
console.log(type,'============监听播放类型');
|
|||
|
|
showPanel.value = true;
|
|||
|
|
rootView.style.display = 'unset';
|
|||
|
|
if (type === 'audio_dst') {
|
|||
|
|
setAudio(stream);
|
|||
|
|
} else if (type === 'video_dst') {
|
|||
|
|
if (useOneVideoShow) {
|
|||
|
|
remoteVideoStream = stream;
|
|||
|
|
showVideo.value = true;
|
|||
|
|
videoRef.value.srcObject = stream;
|
|||
|
|
remoteVideoStream = stream;
|
|||
|
|
} else {
|
|||
|
|
showVideo.value = true;
|
|||
|
|
showlocalVideo.value = false;
|
|||
|
|
videoRef.value.srcObject = stream;
|
|||
|
|
}
|
|||
|
|
} else if (type === 'video_src') {
|
|||
|
|
if (useOneVideoShow) {
|
|||
|
|
// 一般全双工显示远端的画面。 拥有话权的时候,才显示自己的画面,所以流赋值到话权模块处理。
|
|||
|
|
showVideo.value = false;
|
|||
|
|
localVideoStream = stream;
|
|||
|
|
} else {
|
|||
|
|
showlocalVideo.value = true;
|
|||
|
|
showVideo.value = false;
|
|||
|
|
localvideoRef.value.srcObject = stream;
|
|||
|
|
localVideoStream = stream;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const setAudio = (stream) => {
|
|||
|
|
if (audioRef.value) {
|
|||
|
|
audioRef.value.srcObject = stream;
|
|||
|
|
} else {
|
|||
|
|
nextTick(() => {
|
|||
|
|
setAudio();
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const registerhangupEvent = () => {
|
|||
|
|
hangupCbId = window.lemon.call.addHangupEvt(() => {
|
|||
|
|
speakers.value.length = 0;
|
|||
|
|
selfGranted.value = false;
|
|||
|
|
showVideo.value = false;
|
|||
|
|
showlocalVideo.value = false;
|
|||
|
|
showPanel.value = false;
|
|||
|
|
try {
|
|||
|
|
videoRef.value.pause();
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('on hangup,video stop error', error);
|
|||
|
|
}
|
|||
|
|
videoRef.value.srcObject = null;
|
|||
|
|
close();
|
|||
|
|
});
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const registerGrantEvent = () => {
|
|||
|
|
grantCbId = window.lemon.floor.addGrantEvt((event) => {
|
|||
|
|
speakers.value = event.speaker;
|
|||
|
|
selfGranted.value = event.speaker.findIndex((item) => item.number === sessionStorage.getItem('user_id')) >= 0;
|
|||
|
|
if (!localVideoStream && !remoteVideoStream) return;
|
|||
|
|
switch (event.grant_status) {
|
|||
|
|
case '0':
|
|||
|
|
// 话权空闲,本端和对端都不显示画面
|
|||
|
|
if (useOneVideoShow) {
|
|||
|
|
videoRef.value.srcObject = null;
|
|||
|
|
} else {
|
|||
|
|
showVideo.value = false;
|
|||
|
|
showlocalVideo.value = false;
|
|||
|
|
}
|
|||
|
|
break;
|
|||
|
|
case '3':
|
|||
|
|
// 远端有话权,设置video 为远端流
|
|||
|
|
if (useOneVideoShow) {
|
|||
|
|
videoRef.value.srcObject = remoteVideoStream;
|
|||
|
|
} else {
|
|||
|
|
showVideo.value = true;
|
|||
|
|
showlocalVideo.value = false;
|
|||
|
|
}
|
|||
|
|
break;
|
|||
|
|
case '4':
|
|||
|
|
// 自己有话权,显示本端,设置video 为本地流,此时对端是没有画面的
|
|||
|
|
if (useOneVideoShow) {
|
|||
|
|
videoRef.value.srcObject = localVideoStream;
|
|||
|
|
} else {
|
|||
|
|
showVideo.value = false;
|
|||
|
|
showlocalVideo.value = true;
|
|||
|
|
}
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
|
|||
|
|
const close = () => {
|
|||
|
|
if (rootView) {
|
|||
|
|
rootView.style.display = 'none';
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const applayFloor = () => {
|
|||
|
|
const self = sessionStorage.getItem('user_id');
|
|||
|
|
const index = speakers.value?.findIndex((item) => item.number === self);
|
|||
|
|
if (index >= 0) {
|
|||
|
|
CALL.releaseFloor();
|
|||
|
|
} else {
|
|||
|
|
CALL.applyFloor();
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const hangup = () => {
|
|||
|
|
CALL.hangup(codeId.value);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
|
|||
|
|
const Init = () => {
|
|||
|
|
let token = window.localStorage.getItem("rhToken");
|
|||
|
|
if (!token) {
|
|||
|
|
let userInfo = {
|
|||
|
|
username: "sgxtcs",
|
|||
|
|
password: "123456",
|
|||
|
|
realm: "puc.com",
|
|||
|
|
webpucUrl: "https://89.40.9.95:16888"
|
|||
|
|
};
|
|||
|
|
lemon.login.login(userInfo).then((esacpe) => {
|
|||
|
|
token = esacpe.token;
|
|||
|
|
window.localStorage.setItem("rhToken", esacpe.token);
|
|||
|
|
});
|
|||
|
|
} else {
|
|||
|
|
ConnectWebsocket(token);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const ConnectWebsocket = (token) => {
|
|||
|
|
lemon.login.reConnectWebsocket({
|
|||
|
|
username: "sgxtcs",
|
|||
|
|
realm: "puc.com",
|
|||
|
|
webpucUrl: "https://89.40.9.95:16888",
|
|||
|
|
token: token
|
|||
|
|
}).then((resp) => {
|
|||
|
|
console.log(resp,"ConnectWebsocket")
|
|||
|
|
window.lemon.call.addMediaStream((call_id, stream, type) => {
|
|||
|
|
console.log(call_id, stream, type);
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
};
|
|||
|
|
const initWave = () => {
|
|||
|
|
if (!waveContainer) {
|
|||
|
|
waveContainer = document.getElementById('voice_wave');
|
|||
|
|
}
|
|||
|
|
audioWave = new SiriWave({
|
|||
|
|
container: waveContainer,
|
|||
|
|
autostart: false,
|
|||
|
|
width: element.value.width,
|
|||
|
|
});
|
|||
|
|
audioWave.start();
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 如果要用到录音功能,绑定的video 不能切流。 如果半双工,要显示本端流和远端流需要用两个Video。
|
|||
|
|
const initVideo = () => {
|
|||
|
|
if (!useOneVideoShow) {
|
|||
|
|
useMedia.init(videoRef.value, audioRef.value);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
|
|||
|
|
onMounted(() => {
|
|||
|
|
nextTick(() => {
|
|||
|
|
Init();
|
|||
|
|
registerStream();
|
|||
|
|
registerhangupEvent();
|
|||
|
|
registerGrantEvent();
|
|||
|
|
videoRef.value.onloadedmetadata = initVideo;
|
|||
|
|
initWave();
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
rootView = document.getElementById('call_div');
|
|||
|
|
window.video = pushVideoRef.value;
|
|||
|
|
window.showVideo = showPushlVideo;
|
|||
|
|
window.videoViewRoot = rootView;
|
|||
|
|
|
|||
|
|
});
|
|||
|
|
onUnmounted(() => {
|
|||
|
|
window.lemon.call.removeMediaStream(streamCbId);
|
|||
|
|
window.lemon.call.removeHangupEvt(hangupCbId);
|
|||
|
|
window.lemon.floor.removeGrantEvt(grantCbId);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style scoped>
|
|||
|
|
.container {
|
|||
|
|
position: absolute;
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
justify-content: center;
|
|||
|
|
margin: 2px;
|
|||
|
|
background: #000;
|
|||
|
|
border: 2px dashed #ccc;
|
|||
|
|
border-radius: 16px;
|
|||
|
|
cursor: pointer;
|
|||
|
|
top: 30%;
|
|||
|
|
left: 40%;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.video {
|
|||
|
|
position: absolute;
|
|||
|
|
top: 0;
|
|||
|
|
left: 0;
|
|||
|
|
width: 100%;
|
|||
|
|
height: 100%;
|
|||
|
|
|
|||
|
|
object-fit: scale-down;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.buttonContainer {
|
|||
|
|
position: absolute;
|
|||
|
|
bottom: 0;
|
|||
|
|
width: 96%;
|
|||
|
|
margin: 8px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.speaks {
|
|||
|
|
align-items: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.wave {
|
|||
|
|
top: 0;
|
|||
|
|
width: 100%;
|
|||
|
|
height: 100px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.close {
|
|||
|
|
position: absolute;
|
|||
|
|
top: 0;
|
|||
|
|
right: 0;
|
|||
|
|
margin: 5px;
|
|||
|
|
}
|
|||
|
|
</style>
|