WebRTC實時音視頻通訊


先看一段視頻演示

簡介

WebRTC允許網絡應用或者站點,在不借助中間媒介的情況下,建立瀏覽器之間點對點(Peer-to-Peer)的連接,p2p實現視頻流和(或)音頻流或者其他任意數據的傳輸”。

通話流程

如瀏覽器 A 想和瀏覽器 B 進行音視頻通話:

A、B 都連接信令服務器(ws);

A 創建本地視頻,並獲取會話描述對象(offer sdp)信息;

A 將 offer sdp 通過 ws 發送給 B;

B 收到信令后,B 創建本地視頻,並獲取會話描述對象(answer sdp)信息;

B 將 answer sdp 通過 ws 發送給 A;

A 和 B 開始打洞,收集並通過 ws 交換 ice 信息; 完成打洞后,

A 和 B 開始為安全的媒體通信協商秘鑰;

至此, A 和 B 可以進行音視頻通話。

 

 

我們將使用websocket來作為信令服務器

如果要在非局域網使用,需要再搭一個穿透服務器,coturn 

連接信令服務,websocket

      initWs(){
        let _this = this
        //修改成你自己websocket服務端的ip和端口
        _this.ws = new WebSocket("ws://localhost:9326/ws?username="+this.fromIm);
        _this.ws.onopen = function(){
            // Web Socket 已連接上,使用 send() 方法發送數據
            alert("WebSocket連接成功");
            //心跳檢測
            setInterval(() => {
                _this.ws.send("{msgType: 'PING'}")
            },5000)
        };
        //我自己定義的格式,你們可以根據自己修改
        //ret = {msgType: 'RTC',msg: '消息體',toIm: '接收人', fromIm: '發送人'}
        _this.ws.onmessage = function (ret){ 
            var data = JSON.parse(ret.data)
            console.log("收到消息",data)
            if(data.msgType!=='RTC'){
            return;
            }
            const { type, sdp, iceCandidate } = JSON.parse(data.msg)
            console.log("收到消息",type,iceCandidate)
            if (type === 'answer') {
                _this.rtcPeer.setRemoteDescription(new RTCSessionDescription({ type, sdp }));
            } else if (type === 'answer_ice') {
                _this.answerIces.push(iceCandidate)
            } else if (type === 'offer') {
                _this.toIm = data.fromIm
                _this.initMedia("answer",new RTCSessionDescription({ type, sdp }));
            } else if (type === 'offer_ice') {
                _this.offerIces.push(iceCandidate)
            }
        };
        
        _this.ws.onclose = function(){ 
            // 關閉 websocket
            alert("WebSocket連接已關閉..."); 
        };
      },
      //接收撥打方的消息,判斷后添加候選人(因為addIceCandidate必須在設置描述remoteDescription之后,如果還沒設置描述,我們先把它存起來,添加描述后再添加候選人)
      intervalAddIce(){
        let _this = this
        setInterval(() => {
          if(_this.rtcPeer && _this.rtcPeer.remoteDescription && _this.rtcPeer.remoteDescription.type){
            if(!_this.iceFlag){
              _this.iceFlag = true;
              while(_this.offerIces.length>0){
                let iceCandidate = _this.offerIces.shift();
                _this.rtcPeer.addIceCandidate(iceCandidate).then(_=>{
                  console.log("success addIceCandidate()");
                }).catch(e=>{
                  console.log("Error: Failure during addIceCandidate()",e);
                });
              }

              while(_this.answerIces.length>0){
                let iceCandidate = _this.answerIces.shift();
                _this.rtcPeer.addIceCandidate(iceCandidate).then(_=>{
                  console.log("success addIceCandidate()");
                }).catch(e=>{
                  console.log("Error: Failure during addIceCandidate()",e);
                });
              }
              _this.iceFlag = false;
            }
          }
        }, 1000);
      },

創建媒體源

      async initMedia(iceType,offerSdp){
        var _this = this
        //ios瀏覽器不判斷這部分會提示不支持  (navigator.mediaDevices && navigator.mediaDevices.getUserMedia)
        const UserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia || (navigator.mediaDevices && navigator.mediaDevices.getUserMedia);
        if(!UserMedia){
          alert("media,您的瀏覽器不支持訪問用戶媒體設備,請換一個瀏覽器")
          return;
        }
        //RTCPeerConnection 接口代表一個由本地計算機到遠端的WebRTC連接。該接口提供了創建,保持,監控,關閉連接的方法的實現。   
        const PeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
        if(!PeerConnection){
          alert("peer,您的瀏覽器不支持訪問用戶媒體設備,請換一個瀏覽器")
          return;
        }

        _this.localVideo = document.getElementById('localVideo');

        navigator.mediaDevices.getUserMedia({
          audio: {
            channelCount: {ideal: 2,min: 1}, //雙聲道
            echoCancellation: true, //回聲消除
            autoGainControl: true, //修改麥克風輸入音量,自動增益
            noiseSuppression: true //消除背景噪聲
          }
          // video: {
          //   width: 400,
          //   height: 500
          // }
          ,video: false
        }).then(async stream => {
          await _this.initPeer(iceType,PeerConnection)
          _this.intervalAddIce()
          //成功打開音視頻流
          try {
            _this.localVideo.srcObject = stream;
          } catch (error) {
            _this.localVideo.src = await window.URL.createObjectURL(stream);
          }
          stream.getTracks().forEach( async track => {
           await _this.rtcPeer.addTrack(track, stream);
          });
          if (!offerSdp) {
            console.log('創建本地SDP');
            const offer = await _this.rtcPeer.createOffer();
            await _this.rtcPeer.setLocalDescription(offer);
            
            console.log(`傳輸發起方本地SDP`,offer);
            await _this.ws.send(_this.getMsgObj(offer));

          } else {
            console.log('接收到發送方SDP');
            await _this.rtcPeer.setRemoteDescription(offerSdp);

            console.log('創建接收方(應答)SDP');
            const answer = await _this.rtcPeer.createAnswer();
            console.log(`傳輸接收方(應答)SDP`);
            await _this.ws.send(_this.getMsgObj(answer));
            await _this.rtcPeer.setLocalDescription(answer);
          }
        }).catch(error => {
          alert("無法開啟本地媒體源:"+error);
        })
      },

創建連接PeerConnection,並通過信令服務器發送到對等端,以啟動與遠程對等端的新WebRTC連接

      async initPeer(iceType,PeerConnection){
        let _this = this
        _this.remoteVideo = document.getElementById('remoteVideo');

        if(!_this.rtcPeer){
          // var stun = "stun:134.175.163.78:3478"
          // var turn = "turn:134.175.163.78:3478"
          var stun = "stun:120.24.202.127:3478"
          var turn = "turn:120.24.202.127:3478"
          var peerConfig = {
              "iceServers": [{
                  "urls": stun
              }, {
                  "urls": turn,
                  "username": "admin",
                  "credential": "123456"
              }]
          };
          _this.rtcPeer = new PeerConnection(peerConfig);
          _this.rtcPeer.onicecandidate = async (e) => {
            if (e.candidate) {
              console.log("搜集並發送候選人")
              await _this.ws.send(_this.getMsgObj({
                type: iceType+'_ice',
                iceCandidate: e.candidate
              }));
            }else{
              console.log("候選人收集完成")
            }
          };
          _this.rtcPeer.ontrack =  async(e) => {
            console.log("ontrack",e)
              if (e && e.streams) {
                _this.remoteVideo.srcObject = await e.streams[0];
              }
          };
        }
      },

 

我部署到服務器的,演示地址,https://www.xsport.site/webrtc/

源碼地址 https://gitee.com/suruozhong/webrtc-demo

 

修改成你的,websocket服務地址,turn穿透服務地址

npm run build,打包完把dist文件夾扔到服務器,就可以音視頻通話了

 

演示效果:

以下是打包完放到服務器后,手機和電腦進行的音視頻通話

用兩個端打開地址,然后一個端輸入對方的fromIm,撥打。另外一端不需要做什么,如果提示要授權獲取攝像頭和麥克風,點允許即可

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM