zoukankan      html  css  js  c++  java
  • 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: {
              //    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,拨打。另外一端不需要做什么,如果提示要授权获取摄像头和麦克风,点允许即可

     

  • 相关阅读:
    2019.1.10英语笔记
    2019.1.9专业课笔记
    团队触发器
    团队脚本备份
    导表
    oslo.config
    nginx启动、重启、关闭
    常见的awk内建变量
    LVM
    Django, one-to-many, many-to-many
  • 原文地址:https://www.cnblogs.com/suruozhong/p/13322750.html
Copyright © 2011-2022 走看看