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>
|