| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467 |
- <template>
- <page-meta :page-font-size="fontValue+'px'" :root-font-size="fontValue+'px'"></page-meta>
- <view>
- <web-view v-if="webviewUrl" @message="handleMessage" :src="webviewUrl"></web-view>
- </view>
- </template>
- <script setup lang="ts">
- import {ref, watch} from 'vue'
- import {useUserStore} from '@/store/userStore'
- import {onLoad,onReady,onUnload} from "@dcloudio/uni-app";
- import {useWsStore} from "@/store/WsStore";
- import {storeToRefs} from "pinia";
- import {usePeerStore} from "@/store/peerStore";
- import UserApi from "@/api/UserApi";
- import type Webrtc from '@/mode/Webrtc';
- import Auth from "@/api/Auth";
- const fontValue=ref(Auth.getfontSize());
- import MessageUtils from "@/utils/MessageUtils";
- import ChatType from "@/utils/ChatType";
- import type VideoSendInfo from "@/plugins/video/mode/VideoSendInfo";
- import type VideoCallMessage from "@/plugins/video/mode/VideoCallMessage";
- import SendCode from "@/utils/SendCode";
- import type VideoClose from "@/plugins/video/mode/VideoClose";
- import MessageVideoViewPlugin from "@/plugins/video/MessageVideoViewPlugin";
- const user = useUserStore().getUser()
- const peerStore = usePeerStore()
- const friendId = ref("")
- const friend = ref("")
- const showVideo = ref(false)
- const isConnect = ref(false)
- const startTime = ref(0)
- const isCaller = ref(false);
- const hadClose = ref(false);
- const timeCall=ref(0);
- const timer=ref();
- let audioObj=null;
- const checkPermission = function (title: string) {
- // #ifndef H5
- if (uni.getSystemInfoSync().platform !== 'android') {
- return new Promise((resolve) => {
- resolve(true)
- })
- }else {
- return new Promise((resolve) => {
- plus.android.requestPermissions(
- ["android.permission.RECORD_AUDIO", "android.permission.CAMERA"],
- function (resultObj) {
- if (resultObj.granted.length < 2) {
- uni.showToast({
- icon: "none",
- title,
- });
- resolve(false)
- const timer1 = setTimeout(() => { //没有开对应的权限,打开app的系统权限管理页
- let Intent = plus.android.importClass("android.content.Intent");
- let Settings = plus.android.importClass("android.provider.Settings");
- let Uri = plus.android.importClass("android.net.Uri");
- let mainActivity = plus.android.runtimeMainActivity();
- let intent = new Intent();
- intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
- let uri = Uri.fromParts("package", mainActivity.getPackageName(), null);
- intent.setData(uri);
- mainActivity.startActivity(intent);
- clearTimeout(timer1)
- }, 1000)
- } else {
- resolve(true)
- }
- }
- );
- })
- }
- // #endif
- // #ifdef H5
- return new Promise((resolve) => {
- resolve(true)
- })
- // #endif
- }
- onLoad((opt) => {
-
- audioObj=uni.createInnerAudioContext();
- audioObj.loop=true;
- audioObj.src='/hybrid/html/images/calling.mp3';
- usePeerStore().updateBusyStatus(true)
- friendId.value = opt?.friendId
- console.log(opt);
- if(typeof opt?.showVideo === 'string'){
- showVideo.value = opt?.showVideo === 'true'
- }
- if(typeof opt?.isCaller === 'string'){
- isCaller.value = opt?.isCaller === 'true'
- }
- peerStore.updateBusyStatus(true)
- UserApi.getUser(friendId.value).then((res) => {
- friend.value = res.data
- if (user && friend.value) {
- checkPermission("请开启相机和麦克风权限")
- .then((res) => {
- peerStore.setCallId(friendId.value)
- peerStore.updateCloseStatus(false)
- const avatar = encodeURI(friend.value.avatar)
- if(showVideo.value){
- //转义url
- const url = `/hybrid/html/voices/${isCaller.value? 'Video' : 'Videoanswer'}.html?friendName=${friend.value.name}&friendAvatar=${avatar}&showVideo=${showVideo.value}`
- webviewUrl.value = encodeURI(url)
- }
- else{
- //转义url
- const url = `/hybrid/html/voices/${isCaller.value? 'audio' : 'audioanswer'}.html?friendName=${friend.value.name}&friendAvatar=${avatar}&showVideo=${showVideo.value}`
- webviewUrl.value = encodeURI(url)
- }
- })
- }
- sendcallingMsg(isCaller.value);
- })
- })
- onReady(()=>{
- chaoshipanduan();
- audioObj.play();
- });
- onUnload(()=>{
- clearInterval(timer.value);//正常接通
- audioObj.stop();
- if(!hadClose.value){
- sendCloseMsg(isCaller.value);
- }
- peerStore.setCallId(undefined);
- peerStore.setOffer(undefined);
- peerStore.setAnswer(undefined);
- peerStore.setCandidate(undefined);
- peerStore.updateBusyStatus(false);
- peerStore.updateCloseStatus(false);
- peerStore.setIsAccept(false);
- });
- const wsStore = useWsStore()
- const webviewUrl = ref()
- const {isClose} = storeToRefs(peerStore)
- const {isAccept} = storeToRefs(peerStore)
- const {offer} = storeToRefs(peerStore)
- const {candidate} = storeToRefs(peerStore)
- var wv=null;
- watch(isClose, (newVal) => {
- if (newVal) {
- console.log(newVal)
- uni.navigateBack()
- }
- })
- watch(isAccept, (newVal) => {
- console.log('isAccept',newVal)
- if (newVal) {
- if(!wv){
- timeCall.value=45;
- var pages = getCurrentPages();
- var page = pages[pages.length - 1];
- var currentWebview = page.$getAppWebview();
- wv = currentWebview.children()[0];
- }
- wv.evalJS('startWebRct()');
- }
- })
- watch(offer, (newVal) => {
- if (newVal) {//收到answeroffer
- if(!wv){
- var pages = getCurrentPages();
- var page = pages[pages.length - 1];
- var currentWebview = page.$getAppWebview();
- wv = currentWebview.children()[0];
- }
- wv.evalJS('appAct('+newVal+')');
- }
- })
- watch(candidate, (newVal) => {
- if (newVal) {//收到candidate
- if(!wv){
- var pages = getCurrentPages();
- var page = pages[pages.length - 1];
- var currentWebview = page.$getAppWebview();
- wv = currentWebview.children()[0];
- }
- console.log('candidate',newVal)
- wv.evalJS('appActcandidate('+newVal+')');
- }
- })
- /**
- * 发送通话请求
- * @param calling
- */
- const sendcallingMsg = (calling: boolean) => {
- console.log('sendcallingMsg',calling);
- if(calling){
- if (user.id) {
- let msg: Webrtc = {
- chatId:friendId.value,
- fromId: user.id,
- type: ChatType.FRIEND,
- msgtype:'calling',
- conetType:showVideo.value,
- timestamp: new Date().getTime(),
- payload:''
- }
- let data={
- code:SendCode.WEBRTC_CALL,
- message: msg
- }
- useWsStore().send(JSON.stringify(data))
- }
- }
- }
- /**
- * 发送关闭
- * @param calling
- */
- const sendCloseMsg = (calling: boolean) => {
- if (user.id) {
- const closeMessage= {
- code:SendCode.WEBRTC_CLOSE,//9
- message: {
- chatId:friendId.value,
- fromId: user.id,
- timestamp: new Date().getTime(),
- type: ChatType.FRIEND,
- msgtype:'close'
- }
- }
- wsStore.send(JSON.stringify(closeMessage))
- console.log('发送关闭消息', closeMessage)
- //发起方才有发送视频结果消息的权限
- if (isCaller.value) {
- //界面展示视频消息情况,例如:对方是否接受了视频通话,通话时长
- const message= {
- id:null,
- localtime:null,
- mine:true,
- messageType:SendCode.WEBRTC_result,
- chatId:friendId.value,
- fromId: user.id,
- timestamp: new Date().getTime(),
- type: ChatType.FRIEND,
- result: isConnect.value,
- video: showVideo.value,
- duration: isConnect.value ? new Date().getTime() - startTime.value : 0
- }
- console.log(message)
- wsStore.sendWEBRTCresult(message);
- }
- hadClose.value=true;
- }
- }
- /**
- * 发送信令交互
- * @param offer
- */
- const sendofferMsg = (offer:any,type:string) => {
-
- if (user.id) {
- let msg: Webrtc = {
- chatId:friendId.value,
- fromId: user.id,
- type: ChatType.FRIEND,
- msgtype:type,
- conetType:showVideo.value,
- payload:JSON.stringify(offer.data)
- }
- wsStore.sendWEBRTC(msg)
- console.log('sendofferMsg', msg)
- }
- }
- /**
- * 发送信令交互
- * @param candidate
- */
- const sendcandidateMsg = (candidate:any) => {
-
- if (user.id) {
- let msg: Webrtc = {
- chatId:friendId.value,
- fromId: user.id,
- type: ChatType.FRIEND,
- msgtype:'candidate',
- conetType:showVideo.value,
- payload:JSON.stringify(candidate.data)
- }
- wsStore.sendWEBRTC(msg)
- console.log('candidate', msg)
- //获取Webview
- var pages = getCurrentPages();
- var page = pages[pages.length - 1];
- var currentWebview = page.$getAppWebview();
- wv = currentWebview.children()[0];
- //wv.evalJS('appAct("11")');
- }
- }
- //接受通话请求
- const acceptWEBrtc = () =>{
- //console.log('acceptWEBrtc',peerStore.offer);
- isConnect.value = true
- startTime.value = new Date().getTime()
-
- //给自己发一份忙碌状态,多个客户端同一个人不能同时接受,需要客户端接受后,其他客户端就关闭
- useWsStore().send(
- JSON.stringify({
- code: SendCode.WEBRTC_BUSY,
- message: {
- chatId:friendId.value,
- fromId: user.id,
- type: ChatType.FRIEND,
- msgtype:'accept',
- video: showVideo.value,
- }
- })
- )
- }
- const chaoshipanduan = () => {
- timeCall.value=45;
- timer.value = setInterval(() => {
- timeCall.value=timeCall.value-1;
- if(timeCall.value==0&&!isConnect.value){
- MessageUtils.message('暂时无法连接,请稍后重试!')
- }
- else if(isConnect.value){
- clearInterval(timer.value);//正常接通
- return;
- }
- else if(timeCall.value<-2&&!isConnect.value){
- uni.navigateBack();
- }
- }, 1000);
- }
- /**
- * 处理消息 接受来自webview的消息
- * @param data
- */
- const handleMessage = (data: any) => {
- const message = data.detail.data[0];
- console.log('124444',message);
-
- // 创建音频播放器实例
- //var player = plus.audio.createPlayer(stream);
-
- switch (message.type) {
- case 'ws'://ws消息
- useWsStore().send(JSON.stringify(message.data))
- break;
- case 'changeSpeaker'://ws消息
- break;
- case 'connect'://ws消息
- isConnect.value = true
- startTime.value = new Date().getTime()
- audioObj.stop();
- break;
- case 'accept'://接受视频请求
- acceptWEBrtc()
- audioObj.stop();
- break;
- case 'close'://关闭
- // audioObj.stop();
- // sendCloseMsg(isCaller.value)
- // isConnect.value = false
- uni.navigateBack();
- break;
- case 'offer'://创建本地offer
- sendofferMsg(message,'offer');
- break;
- case 'answer'://创建本地offer
- sendofferMsg(message,'answer');
- break;
- case 'candidate'://创建本地offer
- sendcandidateMsg(message);
- break;
- }
- }
- </script>
- <style scoped lang="scss">
- .chat-container {
- display: block;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- height: 100vh;
- background-color: #000; /* 微信通常使用深色背景 */
- position: relative;
- }
- .main-video {
- width: 100%;
- height: 100%;
- object-fit: cover; /* 确保视频填充整个屏幕 */
- position: absolute;
- top: 0;
- left: 0;
- }
- .local-video {
- width: 50%;
- height: 50%;
- position: absolute;
- top: 50%;
- left: 50%;
- }
- .controls {
- position: absolute;
- bottom: 50px; /* 将控制按钮放在屏幕底部 */
- display: flex;
- justify-content: space-around;
- width: 100%;
- }
- button {
- border: none;
- border-radius: 50%;
- background-color: red;
- color: white;
- font-size: 32upx;
- cursor: pointer;
- transition: background-color 0.3s;
- z-index: 1000000000000000000000000;
- width: 20vw;
- height: 20vw;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- button:hover {
- background-color: rgba(0, 0, 0, 0.9); /* 鼠标悬停时颜色加深 */
- }
- button:active {
- background-color: rgba(0, 0, 0, 1); /* 点击时颜色更加深 */
- }
- /* 特定按钮的图标,例如使用Font Awesome或类似图标库 */
- .start-call-icon {
- /* 添加开始通话图标样式 */
- }
- .end-call-icon {
- /* 添加结束通话图标样式 */
- }
- </style>
|