From 459dfb3432aa43864cfcbf3cd93d72ea8f9f1823 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=B7=E5=B8=83=E7=BD=97=E7=85=9C?= <1056317718@qq.com> Date: Tue, 31 May 2022 19:06:14 +0800 Subject: [PATCH] =?UTF-8?q?=E7=A7=81=E8=81=8A=E8=A7=86=E9=A2=91=E9=80=9A?= =?UTF-8?q?=E8=AF=9D=E5=AF=86=E9=92=A5=E5=8D=8F=E5=95=86=E9=80=BB=E8=BE=91?= =?UTF-8?q?=E6=9E=B6=E6=9E=84=E4=BF=AE=E6=94=B9:building=5Fconstruction:?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Components/ChatComponent/ChatRTC.tsx | 340 ++++++++++++----------- src/Utils/Constraints.ts | 2 + 2 files changed, 177 insertions(+), 165 deletions(-) diff --git a/src/Components/ChatComponent/ChatRTC.tsx b/src/Components/ChatComponent/ChatRTC.tsx index 81b6b88..c39411a 100644 --- a/src/Components/ChatComponent/ChatRTC.tsx +++ b/src/Components/ChatComponent/ChatRTC.tsx @@ -63,127 +63,176 @@ export class ChatRTC extends EventEmitter { this.localStream = null; this.remoteStream = null; this.useSecurity = false; - this.security = ''; + this.security = '[]'; - this.socket.on('ON_PRIVATE_WEBRTC_OFFER', (msg) => { - this.sender = msg.sender; - this.receiver = this.myId; - - const rejectOffer = (reason: number) => { - this.socket.send({ - type: ChatWebSocketType.CHAT_PRIVATE_WEBRTC_ANSWER, - accept: reason, - sender: msg.sender, - receiver: msg.receiver, - }); - }; - - if (store.getState().callStatus === CALL_STATUS_FREE) { - eventBus.emit('GET_PRIVATE_CALLED'); - this.answerAudioPrompt[0](); - store.dispatch(setNowChattingId(msg.sender)); - const pgArr = JSON.parse(msg.security); - this.answerModal = Modal.confirm({ - icon: msg.security ? : , - title: '视频通话邀请', - content: ( - - 用户 {msg.senderName}(id: {msg.sender})向您发出视频通话请求,是否接受? - {pgArr.length === 3 ? ( - -
- 注意:对方启用了私聊视频会话加密功能,接受此会话可能会导致您的CPU占用被大幅度提高,请与对方确认后选择是否接受此会话 -
- ) : ( - '' - )} -
- ), - cancelText: ( - <> - - 拒绝接受 - - ), - okText: ( - <> - - 同意请求 - - ), - onOk: () => { - this.useSecurity = pgArr.length === 3; - console.log(pgArr); - - if (this.useSecurity) this.createAnswer(msg.sender, msg.sdp, pgArr); - else this.createAnswer(msg.sender, msg.sdp); - }, - onCancel: () => { - rejectOffer(PRIVATE_WEBRTC_ANSWER_TYPE.REJECT); - this.answerModal = null; - this.sender = undefined; - this.receiver = undefined; - }, - afterClose: this.answerAudioPrompt[1], - centered: true, - getContainer: getMainContent, - }); - } else rejectOffer(PRIVATE_WEBRTC_ANSWER_TYPE.BUSY); + this.socket.on('ON_PRIVATE_WEBRTC_REQUEST', (msg) => { + this.responseCall(msg); }); - this.socket.on( - 'ON_PRIVATE_WEBRTC_ANSWER', - ({ accept, sdp, sender, receiver, security }) => { - if (sender === this.sender && receiver === this.receiver) { - this.callAudioPrompt[1](); - if (this.offerModal) { - this.offerModal.destroy(); - this.offerModal = null; - } - if (accept === PRIVATE_WEBRTC_ANSWER_TYPE.ACCEPT) { - this.receiveAnswer(sdp, security); - } else { - switch (accept) { - case PRIVATE_WEBRTC_ANSWER_TYPE.BUSY: - globalMessage.error({ - content: '对方正在通话中', - duration: 1.5, - }); - break; - case PRIVATE_WEBRTC_ANSWER_TYPE.NO_USER: - globalMessage.error({ - content: '呼叫的用户不存在', - duration: 1.5, - }); - break; - case PRIVATE_WEBRTC_ANSWER_TYPE.REJECT: - globalMessage.error({ - content: '对方拒绝了您的通话邀请', - duration: 1.5, - }); - break; - } - this.onEnded(); + this.socket.on('ON_PRIVATE_WEBRTC_RESPONSE', ({ accept, sender, receiver, security }) => { + if (sender === this.sender && receiver === this.receiver) { + this.callAudioPrompt[1](); + if (this.offerModal) { + this.offerModal.destroy(); + this.offerModal = null; + } + if (accept === PRIVATE_WEBRTC_ANSWER_TYPE.ACCEPT) { + this.createOffer(security); + } else { + switch (accept) { + case PRIVATE_WEBRTC_ANSWER_TYPE.BUSY: + globalMessage.error({ + content: '对方正在通话中', + duration: 1.5, + }); + break; + case PRIVATE_WEBRTC_ANSWER_TYPE.NO_USER: + globalMessage.error({ + content: '呼叫的用户不存在', + duration: 1.5, + }); + break; + case PRIVATE_WEBRTC_ANSWER_TYPE.REJECT: + globalMessage.error({ + content: '对方拒绝了您的通话邀请', + duration: 1.5, + }); + break; } + this.onEnded(); } } - ); + }); - this.socket.on('ON_PRIVATE_WEBRTC_CANDIDATE', this.handleCandidate.bind(this)); + this.socket.on('ON_PRIVATE_WEBRTC_OFFER', (msg) => { + if (msg.sender === this.sender && msg.receiver === this.receiver) + this.createAnswer(msg.sdp); + }); + + this.socket.on('ON_PRIVATE_WEBRTC_CANDIDATE', (msg) => { + if (msg.sender === this.sender && msg.receiver === this.receiver) { + this.handleCandidate(msg); + } + }); this.socket.on('ON_PRIVATE_WEBRTC_DISCONNECT', (msg) => { this.onHangUp(msg); }); } - async createOffer(targetId: number, myName: string, offerModal: any) { + async callRemote(targetId: number, myName: string, offerModal: any) { this.useSecurity = localStorage.getItem('securityPrivateWebrtc') === 'true'; - this.peer = this.buildPeer(); this.callAudioPrompt[0](); store.dispatch(setCallStatus(CALL_STATUS_OFFERING)); store.dispatch(setNowWebrtcFriendId(targetId)); this.sender = this.myId; this.receiver = targetId; + let pgArr: Array = []; + if (this.useSecurity) { + pgArr = await eWindow.ipc.invoke('DIFFIE_HELLMAN'); + } + this.socket.send({ + type: ChatWebSocketType.CHAT_PRIVATE_WEBRTC_REQUEST, + sender: this.myId, + senderName: myName, + security: JSON.stringify(pgArr), + receiver: targetId, + }); + this.offerModal = offerModal; + } + + responseCall(msg: any) { + this.sender = msg.sender; + this.receiver = this.myId; + + const rejectOffer = (reason: number) => { + this.socket.send({ + type: ChatWebSocketType.CHAT_PRIVATE_WEBRTC_RESPONSE, + security: '', + accept: reason, + sender: msg.sender, + receiver: msg.receiver, + }); + }; + + if (store.getState().callStatus === CALL_STATUS_FREE) { + eventBus.emit('GET_PRIVATE_CALLED'); + this.answerAudioPrompt[0](); + store.dispatch(setNowChattingId(msg.sender)); + const pgArr = JSON.parse(msg.security); + const useSecurity = pgArr.length === 3; + this.answerModal = Modal.confirm({ + icon: useSecurity ? : , + title: '视频通话邀请', + content: ( + + 用户 {msg.senderName}(id: {msg.sender})向您发出视频通话请求,是否接受? + {useSecurity ? ( + +
+ 注意:对方启用了私聊视频会话加密功能,接受此会话可能会导致您的CPU占用被大幅度提高,请与对方确认后选择是否接受此会话 +
+ ) : ( + '' + )} +
+ ), + cancelText: ( + <> + + 拒绝接受 + + ), + okText: ( + <> + + 同意请求 + + ), + onOk: () => { + this.useSecurity = useSecurity; + if (useSecurity) { + eWindow.ipc + .invoke('DIFFIE_HELLMAN', pgArr[0], pgArr[1], pgArr[2]) + .then((serverArr) => { + const [privateKey, publicKey] = serverArr; + this.security = privateKey; + this.socket.send({ + type: ChatWebSocketType.CHAT_PRIVATE_WEBRTC_RESPONSE, + accept: PRIVATE_WEBRTC_ANSWER_TYPE.ACCEPT, + sender: this.sender, + receiver: this.receiver, + security: publicKey, + }); + }); + } else { + this.socket.send({ + type: ChatWebSocketType.CHAT_PRIVATE_WEBRTC_RESPONSE, + accept: PRIVATE_WEBRTC_ANSWER_TYPE.ACCEPT, + sender: this.sender, + receiver: this.receiver, + security: '[]', + }); + } + + store.dispatch(setCallStatus(CALL_STATUS_ANSWERING)); + store.dispatch(setNowWebrtcFriendId(msg.sender)); + }, + onCancel: () => { + rejectOffer(PRIVATE_WEBRTC_ANSWER_TYPE.REJECT); + this.answerModal = null; + this.sender = undefined; + this.receiver = undefined; + }, + afterClose: this.answerAudioPrompt[1], + centered: true, + getContainer: getMainContent, + }); + } else rejectOffer(PRIVATE_WEBRTC_ANSWER_TYPE.BUSY); + } + + async createOffer(publicKey: string) { + this.peer = this.buildPeer(); this.localStream = new MediaStream(); this.localStream.addTrack( (await getDeviceStream(DEVICE_TYPE.VIDEO_DEVICE)).getVideoTracks()[0] @@ -195,9 +244,13 @@ export class ChatRTC extends EventEmitter { this.peer.addTrack(track, this.localStream); } - let pgArr: Array = []; - if (this.useSecurity) { - pgArr = await eWindow.ipc.invoke('DIFFIE_HELLMAN'); + // NOTE: 加密 + if (publicKey) { + const privateKey = await eWindow.ipc.invoke('DIFFIE_HELLMAN', publicKey); + this.security = privateKey; + this.peer.getSenders().forEach((sender) => { + setupSenderTransform(sender, privateKey); + }); } else if (senderCodecs) { this.peer .getTransceivers() @@ -215,19 +268,14 @@ export class ChatRTC extends EventEmitter { this.socket.send({ type: ChatWebSocketType.CHAT_PRIVATE_WEBRTC_OFFER, sdp: sdp.sdp, - sender: this.myId, - senderName: myName, - receiver: targetId, - security: JSON.stringify(pgArr), + sender: this.sender, + receiver: this.receiver, }); }); - this.offerModal = offerModal; } - async createAnswer(sender: number, remoteSdp: any, pgArr?: Array) { + async createAnswer(remoteSdp: any) { this.peer = this.buildPeer(); - store.dispatch(setCallStatus(CALL_STATUS_ANSWERING)); - store.dispatch(setNowWebrtcFriendId(sender)); this.peer.setRemoteDescription( new RTCSessionDescription({ @@ -251,34 +299,10 @@ export class ChatRTC extends EventEmitter { } // NOTE: 加密 - let privateKey: string, publicKey: string; - if (pgArr) { - const serverArr = await eWindow.ipc.invoke( - 'DIFFIE_HELLMAN', - pgArr[0], - pgArr[1], - pgArr[2] - ); - privateKey = serverArr[0]; - publicKey = serverArr[1]; - this.security = privateKey; + if (this.useSecurity) { this.peer.getSenders().forEach((sender) => { - setupSenderTransform(sender, privateKey); + setupSenderTransform(sender, this.security); }); - - this.remoteStream = new MediaStream(); - // NOTE: 解密 - if (this.useSecurity) { - this.peer.getReceivers().forEach((receiver) => { - setupReceiverTransform(receiver, privateKey); - this.remoteStream?.addTrack(receiver.track); - }); - } else if (receiverCodecs) - this.peer - .getTransceivers() - .find((t) => t.receiver.track.kind === 'video') - ?.setCodecPreferences(receiverCodecs); - this.emit('REMOTE_STREAM_READY', this.remoteStream); } else if (senderCodecs) { this.peer .getTransceivers() @@ -297,26 +321,15 @@ export class ChatRTC extends EventEmitter { this.peer.setLocalDescription(sdp); this.socket.send({ type: ChatWebSocketType.CHAT_PRIVATE_WEBRTC_ANSWER, - accept: PRIVATE_WEBRTC_ANSWER_TYPE.ACCEPT, sdp: sdp.sdp, sender: this.sender, receiver: this.receiver, - security: this.useSecurity ? publicKey : '', }); store.dispatch(setCallStatus(CALL_STATUS_CALLING)); }); } - async receiveAnswer(remoteSdp: any, publicKey: string) { - // NOTE: 加密 - if (this.useSecurity) { - const privateKey = await eWindow.ipc.invoke('DIFFIE_HELLMAN', publicKey); - this.security = privateKey; - this.peer.getSenders().forEach((sender) => { - setupSenderTransform(sender, privateKey); - }); - } - + async receiveAnswer(remoteSdp: any) { this.peer.setRemoteDescription( new RTCSessionDescription({ sdp: remoteSdp, @@ -391,19 +404,17 @@ export class ChatRTC extends EventEmitter { } }; peer.ontrack = (evt) => { - if (this.myId === this.sender) { - // NOTE: 解密 - if (this.useSecurity) setupReceiverTransform(evt.receiver, this.security); - else if (receiverCodecs) - peer.getTransceivers() - .find((t) => t.receiver.track.kind === 'video') - ?.setCodecPreferences(receiverCodecs); + // NOTE: 解密 + if (this.useSecurity) setupReceiverTransform(evt.receiver, this.security); + else if (receiverCodecs) + peer.getTransceivers() + .find((t) => t.receiver.track.kind === 'video') + ?.setCodecPreferences(receiverCodecs); - this.remoteStream = this.remoteStream || new MediaStream(); - this.remoteStream.addTrack(evt.track); - if (this.remoteStream.getTracks().length === 2) - this.emit('REMOTE_STREAM_READY', this.remoteStream); - } + this.remoteStream = this.remoteStream || new MediaStream(); + this.remoteStream.addTrack(evt.track); + if (this.remoteStream.getTracks().length === 2) + this.emit('REMOTE_STREAM_READY', this.remoteStream); }; // NOTE: 断连检测 @@ -427,12 +438,11 @@ export class ChatRTC extends EventEmitter { this.sender = undefined; this.receiver = undefined; this.useSecurity = false; - this.security = ''; + this.security = '[]'; store.dispatch(setNowWebrtcFriendId(null)); this.localStream = null; this.remoteStream = null; if (this.peer) this.peer.close(); - // this.peer = undefined; store.dispatch(setCallStatus(CALL_STATUS_FREE)); } } diff --git a/src/Utils/Constraints.ts b/src/Utils/Constraints.ts index 5a5a518..936bb3f 100644 --- a/src/Utils/Constraints.ts +++ b/src/Utils/Constraints.ts @@ -36,6 +36,8 @@ export enum ChatWebSocketType { CHAT_PRIVATE_WEBRTC_ANSWER, // 响应视频聊天请求 ANSWER CHAT_PRIVATE_WEBRTC_CANDIDATE, // 视频聊天 ICE 候选者 CHAT_PRIVATE_WEBRTC_DISCONNECT, // 断开视频聊天 + CHAT_PRIVATE_WEBRTC_REQUEST, // 发送视频通话请求 + CHAT_PRIVATE_WEBRTC_RESPONSE, // 响应视频通话请求 } /**