From d6bc54cb137c34d7cd1f586c5a2f092a5141c67d Mon Sep 17 00:00:00 2001
From: konenet <kone_net@163.com>
Date: Sun, 12 Dec 2021 11:16:10 +0800
Subject: [PATCH] feat: optimize the video and audio call

---
 README.md                                     |  6 ++
 src/chat/Panel.jsx                            | 31 ++++-----
 .../panel/right/component/ChatAudioOline.jsx  | 55 ++++++++--------
 .../panel/right/component/ChatVideoOline.jsx  | 50 +++++++++------
 src/chat/panel/right/index.jsx                | 63 ++++---------------
 5 files changed, 91 insertions(+), 114 deletions(-)

diff --git a/README.md b/README.md
index 40eb29c..c3e4855 100644
--- a/README.md
+++ b/README.md
@@ -70,3 +70,9 @@ https://github.com/kone-net/go-chat-web
 * 屏幕共享
 ![screen-share](/public/screenshot/screen-share.png)
 
+## 分支说明
+one-file分支:
+该分支是所有逻辑都在一个文件实现,包括语音,文字,图片,视频消息,视频通话,语音电话,屏幕共享。
+main分支:
+是将各个部分进行拆分。将Panel拆分成,左、中、右。又将右边的发送文件,图片,文件拆分成更小的组件。
+
diff --git a/src/chat/Panel.jsx b/src/chat/Panel.jsx
index b77b838..a179aa7 100755
--- a/src/chat/Panel.jsx
+++ b/src/chat/Panel.jsx
@@ -80,7 +80,6 @@ class Panel extends React.Component {
                 height: 0,
                 width: 0
             },
-            rtcType: 'answer',
         }
     }
 
@@ -191,11 +190,10 @@ class Panel extends React.Component {
          * @param {候选人信息} e 
          */
         peer.onicecandidate = (e) => {
-            console.log(this.state.rtcType + '_ice', e)
             if (e.candidate) {
                 // rtcType参数默认是对端值为answer,如果是发起端,会将值设置为offer
                 let candidate = {
-                    type: this.state.rtcType + '_ice',
+                    type: 'answer_ice',
                     iceCandidate: e.candidate
                 }
                 let message = {
@@ -212,10 +210,9 @@ class Panel extends React.Component {
          * @param {包含语音视频流} e 
          */
         peer.ontrack = (e) => {
-            console.log(e)
             if (e && e.streams) {
                 if (this.state.onlineType === 1) {
-                    let remoteVideo = document.getElementById("remoteVideo");
+                    let remoteVideo = document.getElementById("remoteVideoReceiver");
                     remoteVideo.srcObject = e.streams[0];
                 } else {
                     let remoteAudio = document.getElementById("audioPhone");
@@ -231,10 +228,10 @@ class Panel extends React.Component {
      */
     dealWebRtcMessage = (messagePB) => {
         const { type, sdp, iceCandidate } = JSON.parse(messagePB.content);
-        console.log(type)
+
         if (type === "answer") {
-            const offerSdp = new RTCSessionDescription({ type, sdp });
-            this.props.peer.localPeer.setRemoteDescription(offerSdp)
+            const answerSdp = new RTCSessionDescription({ type, sdp });
+            this.props.peer.localPeer.setRemoteDescription(answerSdp)
         } else if (type === "answer_ice") {
             this.props.peer.localPeer.addIceCandidate(iceCandidate)
         } else if (type === "offer_ice") {
@@ -247,7 +244,7 @@ class Panel extends React.Component {
 
             let video = false;
             if (messagePB.contentType === Constant.VIDEO_ONLINE) {
-                preview = document.getElementById("preview1");
+                preview = document.getElementById("localVideoReceiver");
                 video = true
                 this.setState({
                     onlineType: 1,
@@ -368,9 +365,9 @@ class Panel extends React.Component {
             isRecord: false
         })
 
-        let preview1 = document.getElementById("preview1");
-        if (preview1 && preview1.srcObject && preview1.srcObject.getTracks()) {
-            preview1.srcObject.getTracks().forEach((track) => track.stop());
+        let localVideoReceiver = document.getElementById("localVideoReceiver");
+        if (localVideoReceiver && localVideoReceiver.srcObject && localVideoReceiver.srcObject.getTracks()) {
+            localVideoReceiver.srcObject.getTracks().forEach((track) => track.stop());
         }
 
         let preview = document.getElementById("preview");
@@ -419,7 +416,11 @@ class Panel extends React.Component {
                     </Col>
 
                     <Col offset={1} span={16}>
-                        <Right history={this.props.history} />
+                        <Right
+                            history={this.props.history}
+                            sendMessage={this.sendMessage}
+                            checkMediaPermisssion={this.checkMediaPermisssion}
+                        />
                     </Col>
                 </Row>
 
@@ -434,8 +435,8 @@ class Panel extends React.Component {
                         />
                     </Tooltip>
                     <br />
-                    <video id="preview1" width="700px" height="auto" autoPlay muted controls />
-                    <video id="remoteVideo" width="700px" height="auto" autoPlay muted controls />
+                    <video id="localVideoReceiver" width="700px" height="auto" autoPlay muted controls />
+                    <video id="remoteVideoReceiver" width="700px" height="auto" autoPlay muted controls />
 
                     <img id="receiver" width={this.state.currentScreen.width} height="auto" alt="" />
                     <canvas id="canvas" width={this.state.currentScreen.width} height={this.state.currentScreen.height} />
diff --git a/src/chat/panel/right/component/ChatAudioOline.jsx b/src/chat/panel/right/component/ChatAudioOline.jsx
index b150883..a546eed 100644
--- a/src/chat/panel/right/component/ChatAudioOline.jsx
+++ b/src/chat/panel/right/component/ChatAudioOline.jsx
@@ -19,7 +19,7 @@ class ChatAudioOline extends React.Component {
     constructor(props) {
         super(props)
         this.state = {
-            
+            mediaPanelDrawerVisible: false,
         }
     }
 
@@ -40,25 +40,19 @@ class ChatAudioOline extends React.Component {
         }
 
         this.webrtcConnection();
-        console.log(this.props.peer)
-        this.setState({
-            onlineType: 2,
-            rtcType: 'offer'
-        })
-
         navigator.mediaDevices
             .getUserMedia({
                 audio: true,
                 video: false,
             }).then((stream) => {
                 stream.getTracks().forEach(track => {
-                    this.props.peer.localPeer.addTrack(track, stream);
+                    localPeer.addTrack(track, stream);
                 });
 
                 // 一定注意:需要将该动作,放在这里面,即流获取成功后,再进行offer创建。不然不能获取到流,从而不能播放视频。
-                this.props.peer.localPeer.createOffer()
+                localPeer.createOffer()
                     .then(offer => {
-                        this.props.peer.localPeer.setLocalDescription(offer);
+                        localPeer.setLocalDescription(offer);
                         let data = {
                             contentType: Constant.AUDIO_ONLINE,  // 消息内容类型
                             content: JSON.stringify(offer),
@@ -77,14 +71,12 @@ class ChatAudioOline extends React.Component {
     * webrtc 绑定事件
     */
     webrtcConnection = () => {
-        
 
         /**
          * 对等方收到ice信息后,通过调用 addIceCandidate 将接收的候选者信息传递给浏览器的ICE代理。
          * @param {候选人信息} e 
          */
-         localPeer.onicecandidate = (e) => {
-            console.log(e)
+        localPeer.onicecandidate = (e) => {
             if (e.candidate) {
                 // rtcType参数默认是对端值为answer,如果是发起端,会将值设置为offer
                 let candidate = {
@@ -104,29 +96,30 @@ class ChatAudioOline extends React.Component {
          * 当连接成功后,从里面获取语音视频流
          * @param {包含语音视频流} e 
          */
-         localPeer.ontrack = (e) => {
+        localPeer.ontrack = (e) => {
             if (e && e.streams) {
-                if (this.state.onlineType === 1) {
-                    let remoteVideo = document.getElementById("remoteVideo");
-                    remoteVideo.srcObject = e.streams[0];
-                } else {
-                    let remoteAudio = document.getElementById("audioPhone");
-                    remoteAudio.srcObject = e.streams[0];
-                }
+                let remoteAudio = document.getElementById("remoteAudioPhone");
+                remoteAudio.srcObject = e.streams[0];
             }
         };
     }
 
     /**
-     * 停止视频电话,屏幕共享
+     * 停止语音电话
      */
-     stopVideoOnline = () => {
-        let audioPhone = document.getElementById("audioPhone");
+    stopAudioOnline = () => {
+        let audioPhone = document.getElementById("remoteAudioPhone");
         if (audioPhone && audioPhone.srcObject && audioPhone.srcObject.getTracks()) {
             audioPhone.srcObject.getTracks().forEach((track) => track.stop());
         }
     }
 
+    mediaPanelDrawerOnClose = () => {
+        this.setState({
+            mediaPanelDrawerVisible: false
+        })
+    }
+
     render() {
         const { chooseUser } = this.props;
         return (
@@ -141,18 +134,24 @@ class ChatAudioOline extends React.Component {
                     />
                 </Tooltip>
 
-                <Drawer width='820px' forceRender={true} title="媒体面板" placement="right" onClose={this.mediaPanelDrawerOnClose} visible={this.state.mediaPanelDrawerVisible}>
+                <Drawer width='420px'
+                    forceRender={true}
+                    title="媒体面板"
+                    placement="right"
+                    onClose={this.mediaPanelDrawerOnClose}
+                    visible={this.state.mediaPanelDrawerVisible}
+                >
                     <Tooltip title="结束视频语音">
                         <Button
                             shape="circle"
-                            onClick={this.stopVideoOnline}
+                            onClick={this.stopAudioOnline}
                             style={{ marginRight: 10, float: 'right' }}
                             icon={<PoweroffOutlined style={{ color: 'red' }} />}
                         />
                     </Tooltip>
                     <br />
-                    
-                    <audio id="audioPhone" autoPlay controls />
+
+                    <audio id="remoteAudioPhone" autoPlay controls />
                 </Drawer>
             </>
         );
diff --git a/src/chat/panel/right/component/ChatVideoOline.jsx b/src/chat/panel/right/component/ChatVideoOline.jsx
index 17fcf40..dad9a28 100644
--- a/src/chat/panel/right/component/ChatVideoOline.jsx
+++ b/src/chat/panel/right/component/ChatVideoOline.jsx
@@ -19,17 +19,25 @@ class ChatVideoOline extends React.Component {
     constructor(props) {
         super(props)
         this.state = {
-
+            mediaPanelDrawerVisible: false,
         }
     }
 
     componentDidMount() {
+        // const configuration = {
+        //     iceServers: [{
+        //         "url": "stun:23.21.150.121"
+        //     }, {
+        //         "url": "stun:stun.l.google.com:19302"
+        //     }]
+        // };
         localPeer = new RTCPeerConnection();
         let peer = {
             ...this.props.peer,
             localPeer: localPeer
         }
         this.props.setPeer(peer);
+        this.webrtcConnection();
     }
     /**
      * 开启视频电话
@@ -38,28 +46,22 @@ class ChatVideoOline extends React.Component {
         if (!this.props.checkMediaPermisssion()) {
             return;
         }
-        this.webrtcConnection();
-        let preview = document.getElementById("preview");
-        this.setState({
-            onlineType: 1,
-            rtcType: 'offer'
-        })
+        let preview = document.getElementById("localPreviewSender");
 
         navigator.mediaDevices
             .getUserMedia({
                 audio: true,
                 video: true,
             }).then((stream) => {
-                console.log(stream)
                 preview.srcObject = stream;
                 stream.getTracks().forEach(track => {
-                    this.props.peer.localPeer.addTrack(track, stream);
+                    localPeer.addTrack(track, stream);
                 });
 
                 // 一定注意:需要将该动作,放在这里面,即流获取成功后,再进行offer创建。不然不能获取到流,从而不能播放视频。
-                this.props.peer.localPeer.createOffer()
+                localPeer.createOffer()
                     .then(offer => {
-                        this.props.peer.localPeer.setLocalDescription(offer);
+                        localPeer.setLocalDescription(offer);
                         let data = {
                             contentType: Constant.VIDEO_ONLINE,
                             content: JSON.stringify(offer),
@@ -79,13 +81,11 @@ class ChatVideoOline extends React.Component {
     */
     webrtcConnection = () => {
 
-
         /**
          * 对等方收到ice信息后,通过调用 addIceCandidate 将接收的候选者信息传递给浏览器的ICE代理。
          * @param {候选人信息} e 
          */
         localPeer.onicecandidate = (e) => {
-            console.log(e)
             if (e.candidate) {
                 // rtcType参数默认是对端值为answer,如果是发起端,会将值设置为offer
                 let candidate = {
@@ -107,7 +107,7 @@ class ChatVideoOline extends React.Component {
          */
         localPeer.ontrack = (e) => {
             if (e && e.streams) {
-                let remoteVideo = document.getElementById("remoteVideo");
+                let remoteVideo = document.getElementById("remoteVideoSender");
                 remoteVideo.srcObject = e.streams[0];
             }
         };
@@ -117,17 +117,23 @@ class ChatVideoOline extends React.Component {
      * 停止视频电话,屏幕共享
      */
     stopVideoOnline = () => {
-        let preview = document.getElementById("preview");
+        let preview = document.getElementById("localPreviewSender");
         if (preview && preview.srcObject && preview.srcObject.getTracks()) {
             preview.srcObject.getTracks().forEach((track) => track.stop());
         }
 
-        let remoteVideo = document.getElementById("remoteVideo");
+        let remoteVideo = document.getElementById("remoteVideoSender");
         if (remoteVideo && remoteVideo.srcObject && remoteVideo.srcObject.getTracks()) {
             remoteVideo.srcObject.getTracks().forEach((track) => track.stop());
         }
     }
 
+    mediaPanelDrawerOnClose = () => {
+        this.setState({
+            mediaPanelDrawerVisible: false
+        })
+    }
+
     render() {
         const { chooseUser } = this.props;
         return (
@@ -141,7 +147,13 @@ class ChatVideoOline extends React.Component {
                     />
                 </Tooltip>
 
-                <Drawer width='820px' forceRender={true} title="媒体面板" placement="right" onClose={this.mediaPanelDrawerOnClose} visible={this.state.mediaPanelDrawerVisible}>
+                <Drawer width='820px'
+                    forceRender={true}
+                    title="媒体面板"
+                    placement="right"
+                    onClose={this.mediaPanelDrawerOnClose}
+                    visible={this.state.mediaPanelDrawerVisible}
+                >
                     <Tooltip title="结束视频语音">
                         <Button
                             shape="circle"
@@ -151,8 +163,8 @@ class ChatVideoOline extends React.Component {
                         />
                     </Tooltip>
                     <br />
-                    <video id="preview" width="700px" height="auto" autoPlay muted controls />
-                    <video id="remoteVideo" width="700px" height="auto" autoPlay muted controls />
+                    <video id="localPreviewSender" width="700px" height="auto" autoPlay muted controls />
+                    <video id="remoteVideoSender" width="700px" height="auto" autoPlay muted controls />
                 </Drawer>
             </>
         );
diff --git a/src/chat/panel/right/index.jsx b/src/chat/panel/right/index.jsx
index 65d5238..3d575e7 100644
--- a/src/chat/panel/right/index.jsx
+++ b/src/chat/panel/right/index.jsx
@@ -1,7 +1,6 @@
 import React from 'react';
 
 import {
-    message,
     Tag,
     Tooltip,
     Button
@@ -22,7 +21,6 @@ import ChatVideoOline from './component/ChatVideoOline';
 import ChatEdit from './component/ChatEdit';
 
 import moment from 'moment';
-import protobuf from '../../proto/proto';
 import { connect } from 'react-redux';
 import { actions } from '../../redux/module/panel';
 
@@ -62,45 +60,6 @@ class RightIndex extends React.Component {
         this.appendMessage(<img src={base64String} alt="" width="150px" />);
     }
 
-    /**
-     * 发送消息
-     * @param {消息内容} messageData 
-     */
-    sendMessage = (messageData) => {
-        let data = {
-            ...messageData,
-            messageType: this.props.chooseUser.messageType, // 消息类型,1.单聊 2.群聊
-            fromUsername: localStorage.username,
-            from: localStorage.uuid,
-            to: this.props.chooseUser.toUser,
-        }
-        let message = protobuf.lookup("protocol.Message")
-        const messagePB = message.create(data)
-
-        let socket = this.props.socket;
-        if (null == socket) {
-            message.error("socket未连接");
-            return;
-        }
-        socket.send(message.encode(messagePB).finish())
-    }
-
-    /**
-     * 检查媒体权限是否开启
-     * @returns 媒体权限是否开启
-     */
-    checkMediaPermisssion = () => {
-        navigator.getUserMedia = navigator.getUserMedia ||
-            navigator.webkitGetUserMedia ||
-            navigator.mozGetUserMedia ||
-            navigator.msGetUserMedia; //获取媒体对象(这里指摄像头)
-        if (!navigator || !navigator.mediaDevices) {
-            message.error("获取摄像头权限失败!")
-            return false;
-        }
-        return true;
-    }
-
     showMediaPanel = () => {
         let media = {
             ...this.props.media,
@@ -119,37 +78,37 @@ class RightIndex extends React.Component {
                     history={this.props.history}
                     appendMessage={this.appendMessage}
                     appendImgToPanel={this.appendImgToPanel}
-                    sendMessage={this.sendMessage}
+                    sendMessage={this.props.sendMessage}
                 />
                 <ChatAudio
                     history={this.props.history}
                     appendMessage={this.appendMessage}
-                    sendMessage={this.sendMessage}
+                    sendMessage={this.props.sendMessage}
                 />
 
                 <ChatVideo
                     history={this.props.history}
                     appendMessage={this.appendMessage}
-                    sendMessage={this.sendMessage}
-                    checkMediaPermisssion={this.checkMediaPermisssion}
+                    sendMessage={this.props.sendMessage}
+                    checkMediaPermisssion={this.props.checkMediaPermisssion}
                 />
 
                 <ChatShareScreen
                     history={this.props.history}
-                    sendMessage={this.sendMessage}
-                    checkMediaPermisssion={this.checkMediaPermisssion}
+                    sendMessage={this.props.sendMessage}
+                    checkMediaPermisssion={this.props.checkMediaPermisssion}
                 />
 
                 <ChatAudioOline
                     history={this.props.history}
-                    sendMessage={this.sendMessage}
-                    checkMediaPermisssion={this.checkMediaPermisssion}
+                    sendMessage={this.props.sendMessage}
+                    checkMediaPermisssion={this.props.checkMediaPermisssion}
                 />
 
                 <ChatVideoOline
                     history={this.props.history}
-                    sendMessage={this.sendMessage}
-                    checkMediaPermisssion={this.checkMediaPermisssion}
+                    sendMessage={this.props.sendMessage}
+                    checkMediaPermisssion={this.props.checkMediaPermisssion}
                 />
 
                 <Tooltip title="显示视频面板">
@@ -169,7 +128,7 @@ class RightIndex extends React.Component {
                     history={this.props.history}
                     appendMessage={this.appendMessage}
                     appendImgToPanel={this.appendImgToPanel}
-                    sendMessage={this.sendMessage}
+                    sendMessage={this.props.sendMessage}
                 />
 
             </>