| <template> | 
|   <div class="demo"> | 
|     <div class="rtcBox"> | 
|       <div> | 
|         <div class="video-box"> | 
|           <video src="" id="rtcA" controls autoplay></video> | 
|           <h5>A</h5> | 
|         </div> | 
|         <div class="chat-box" v-show="!allowHangup && messageOpen"> | 
|           <h5>收消息</h5> | 
|           <p>{{ receiveText }}</p> | 
|         </div> | 
|       </div> | 
|       <div> | 
|         <div class="video-box"> | 
|           <video src="" id="rtcB" controls autoplay></video> | 
|           <h5>B</h5> | 
|           <button @click="call" :disabled="allowCall">发起连接</button> | 
|           <button @click="hangup" :disabled="allowHangup">hangup</button> | 
|         </div> | 
|         <div class="chat-box" v-show="!allowHangup && messageOpen"> | 
|           <h5>发消息</h5> | 
|           <textarea v-model="sendText"></textarea> | 
|           <br /> | 
|           <button @click="send">发送</button> | 
|         </div> | 
|       </div> | 
|     </div> | 
|   </div> | 
| </template> | 
|   | 
| <script> | 
| export default { | 
|   name: "local1", | 
|   data() { | 
|     return { | 
|       peerA: null, | 
|       peerB: null, | 
|       channelA: null, | 
|       channelB: null, | 
|       offerOption: { | 
|         offerToReceiveAudio: 1, | 
|         offerToReceiveVideo: 1, | 
|       }, | 
|       allowCall: true, | 
|       allowHangup: true, | 
|       messageOpen: false, | 
|       sendText: "", | 
|       receiveText: "", | 
|     }; | 
|   }, | 
|   methods: { | 
|     send() { | 
|       this.channelB.send(JSON.stringify({ name: this.sendText })); | 
|       this.sendText = ""; | 
|     }, | 
|     start() { | 
|       this.state = "2"; | 
|       this.newRecognition.start(); | 
|     }, | 
|     stop() { | 
|       this.state = "1"; | 
|       this.newRecognition.stop(); | 
|     }, | 
|     async call() { | 
|       if (!this.peerA || !this.peerB) { | 
|         // 判断是否有对应实例,没有就重新创建 | 
|         this.initPeer(); | 
|       } | 
|       try { | 
|         let offer = await this.peerB.createOffer(this.offerOption); // 创建 offer | 
|         await this.onCreateOffer(offer); | 
|       } catch (e) { | 
|         console.log("createOffer: ", e); | 
|       } | 
|   | 
|       this.allowCall = true; | 
|       this.allowHangup = false; | 
|     }, | 
|     hangup() { | 
|       this.peerA.close(); | 
|       this.peerB.close(); | 
|       this.channelA.close(); | 
|       this.channelB.close(); | 
|       this.peerA = null; | 
|       this.peerB = null; | 
|       this.channelA = null; | 
|       this.channelB = null; | 
|       this.sendText = ""; | 
|       this.receiveText = ""; | 
|       this.allowCall = false; | 
|       this.allowHangup = true; | 
|     }, | 
|     async onCreateOffer(desc) { | 
|       try { | 
|         await this.peerB.setLocalDescription(desc); // 呼叫端设置本地 offer 描述 | 
|       } catch (e) { | 
|         console.log("Offer-setLocalDescription: ", e); | 
|       } | 
|       try { | 
|         await this.peerA.setRemoteDescription(desc); // 接收端设置远程 offer 描述 | 
|       } catch (e) { | 
|         console.log("Offer-setRemoteDescription: ", e); | 
|       } | 
|       try { | 
|         let answer = await this.peerA.createAnswer(); // 接收端创建 answer | 
|         await this.onCreateAnswer(answer); | 
|       } catch (e) { | 
|         console.log("createAnswer: ", e); | 
|       } | 
|     }, | 
|     async onCreateAnswer(desc) { | 
|       try { | 
|         await this.peerA.setLocalDescription(desc); // 接收端设置本地 answer 描述 | 
|       } catch (e) { | 
|         console.log("answer-setLocalDescription: ", e); | 
|       } | 
|       try { | 
|         await this.peerB.setRemoteDescription(desc); // 呼叫端设置远程 answer 描述 | 
|       } catch (e) { | 
|         console.log("answer-setRemoteDescription: ", e); | 
|       } | 
|     }, | 
|     initPeer() { | 
|       // 创建输出端 PeerConnection | 
|       let PeerConnection = | 
|         window.RTCPeerConnection || | 
|         window.mozRTCPeerConnection || | 
|         window.webkitRTCPeerConnection; | 
|       this.peerA = new PeerConnection(); | 
|       this.peerA.addStream(this.localstream); // 添加本地流 | 
|       // 监听 A 的ICE候选信息 | 
|       // 如果收集到,就添加给 B | 
|       this.peerA.onicecandidate = (event) => { | 
|         if (event.candidate) { | 
|           this.peerB.addIceCandidate(event.candidate); | 
|         } | 
|       }; | 
|       this.peerA.ondatachannel = (event) => { | 
|         console.log(event); | 
|         this.channelA = event.channel; | 
|         this.channelA.binaryType = "arraybuffer"; | 
|         this.channelA.onopen = (e) => { | 
|           console.log("channelA onopen", e); | 
|         }; | 
|         this.channelA.onclose = (e) => { | 
|           console.log("channelA onclose", e); | 
|         }; | 
|         this.channelA.onmessage = (e) => { | 
|           this.receiveText = JSON.parse(e.data).name; | 
|           console.log("channelA onmessage", e.data); | 
|         }; | 
|       }; | 
|       //                this.channelA.send('Hi you!'); | 
|       // 创建呼叫端 | 
|       this.peerB = new PeerConnection(); | 
|       this.peerB.onaddstream = (event) => { | 
|         // 监听是否有媒体流接入,如果有就赋值给 rtcB 的 src | 
|         console.log("event-stream", event); | 
|         let video = document.querySelector("#rtcB"); | 
|         video.srcObject = event.stream; | 
|       }; | 
|       this.channelB = this.peerB.createDataChannel("messagechannel"); | 
|       console.log("this.channelB", this.channelB); | 
|       this.channelB.binaryType = "arraybuffer"; | 
|       this.channelB.onopen = (event) => { | 
|         console.log(1); | 
|         console.log("channelB onopen", event); | 
|         this.messageOpen = true; | 
|       }; | 
|       this.channelB.onclose = function (event) { | 
|         console.log(1); | 
|         console.log("channelB onclose", event); | 
|       }; | 
|       // 监听 B 的ICE候选信息 | 
|       // 如果收集到,就添加给 A | 
|       this.peerB.onicecandidate = (event) => { | 
|         if (event.candidate) { | 
|           this.peerA.addIceCandidate(event.candidate); | 
|         } | 
|       }; | 
|       this.allowCall = false; | 
|     }, | 
|     async createMedia() { | 
|       // 保存本地流到全局(视频音频都支持) | 
|       this.localstream = await navigator.mediaDevices.getUserMedia({ | 
|         audio: true, | 
|         video: true, | 
|       }); | 
|       console.log(this.localstream); | 
|       console.log( | 
|         this.localstream.getVideoTracks(), | 
|         this.localstream.getAudioTracks() | 
|       ); | 
|       let video = document.querySelector("#rtcA"); | 
|       video.srcObject = this.localstream; | 
|       this.initPeer(); // 获取到媒体流后,调用函数初始化 RTCPeerConnection | 
|     }, | 
|   }, | 
|   mounted() { | 
|     // 获取最新视图后获取本地流 | 
|     this.$nextTick(() => { | 
|       // {mediaSource: 'screen'} | 
|       this.createMedia(); | 
|     }); | 
|   }, | 
| }; | 
| </script> | 
|   | 
| <style lang="scss"> | 
| .rtcBox { | 
|   display: flex; | 
|   justify-content: center; | 
|   .video-box { | 
|     height: 380px; | 
|     border-bottom: 1px solid #1fbeca; | 
|     margin-bottom: 10px; | 
|   } | 
|   video { | 
|     width: 400px; | 
|     height: 300px; | 
|     margin-left: 20px; | 
|     background-color: #ddd; | 
|   } | 
|   .chat-box { | 
|     text-align: center; | 
|     h5 { | 
|       margin-bottom: 10px; | 
|     } | 
|     p, | 
|     textarea { | 
|       width: 240px; | 
|       height: 60px; | 
|       border: 1px solid #000; | 
|       display: inline-block; | 
|     } | 
|   } | 
| } | 
| </style> |