切换语言为:繁体
音视频通话之webRTC实现

音视频通话之webRTC实现

  • 爱糖宝
  • 2024-09-24
  • 2038
  • 0
  • 0

前言

接上篇文章,实现了音视频通话的转接mqtt的实现,接下来进行音视频的具体实现,这里采用的方案是通过webRTC进行实现,这个API的好处就是不用额外安装其他软件的前提下,浏览器直接点对点实现音视频通话,现在来简单介绍下具体实现过程,希望能对你有帮助。

webRTC是什么

webRTC是一项实时通讯技术,也叫点对点通信,它能让 Web应用程序站点能够流式传输音视频媒体,以及浏览器之间交换数据而无需中间件,建立浏览器数据共享和传输,而且通道一旦建立,可以不经过服务端,客户端对客户端进行实时通话。

准备工作

概念知识

  • RTCPeerConnection

简称peer,点对点通信(peer-to-peer),通过这个方法创建一个peer端,可以理解成创建了一个视频窗口壳子,例如发起方创建一个peer,接收方也创建一个peer,两端之间进行联通,即可实现实时通话。

  • offer

通过createOffer创建的offer信息,里面包含SDP offer信息,发起方将offer发送给对方,建立视频连接。

  • answer

通过createAnswer创建的应答信息,和offer类似,接收方接收到offer后,通过createAnswer方法创建本机的SDP信息,然后发送给发起方,这样双方的SDP交换就完成了。

  • candidate

有用过获取本机ip的对这个应该眼熟,获取本机ip地址,也是通过webRTCcandidate进行实现,这个的主要作用是获取本机的ip,它会优先抽局域网中获取,如果局域网获取不到,然后就会从TURN/STURN获取。

  • 信令服务器

通常使用webSocketmqtt等进行实现,在前面我们有说过创建offeranswer发送给对方,浏览器怎么知道要发给谁?所以这里就是使用信令服务器发送给指定的人。

  • TURN/STURN

这个俗称就是打洞,网络穿透,当两个设备不处于同一局域网的前提下,就需要这个进行一个网络穿透,通过这个可以获取两个设备各自在公网的ip,这样就能进行连接互通。

为什么要交换SDP和信令服务器

有人可能会问,WebRTC不是能够点对点直接通信吗?为什么还需要交换SDP和信令服务器?

举个梨子:张三会中,英,韩语三门语言,李四会中、日、俄语,现在张三和李四想要对话交流,自然而然的找到两个人共同点语言中文进行交流,同理,交换SDP其实就是找共同语言的一个过程,也就是取交集。

然后就是信令服务器,再打个比方,现在有100个房间,我现在只想去张三的房间,但是我又不知道张三的房间号,所以这个时候就需要信令服务器Socket,将我想去张三房间的这个想法转达给张三,并带我去他的房间。

常用方法

API

  • RTCPeerConnection:创建一个peer端

  • setLocalDescription:设置本地描述

  • setRemoteDescription:设置远程描述

  • createOffer:创建一个SDP offer

  • createAnswer:创建一个SDP Answer

  • addStream: 添加视频流

  • addIceCandidate:添加对方的网络信息

监听

  • onicecandidate:监听到ip信息

  • onaddstream:监护获取对方的视频流信息

  • onicegatheringstatechange :监听网络协商状态

实现步骤

  • 交换SDP

音视频通话之webRTC实现

  1. 发起方peerA,接收方peerB都创建一个peer

  2. 发起方获取本机摄像头信息,并通过addStream添加视频流到通道

  3. 接收方获取本机摄像头信息,并通过addStream添加视频流到通道

  4. 发起方通过createOffer获取到SDP信息,并通过setLocalDescription添加到本地视频描述信息

  5. 接收方拿到发起方SDP信息后,通过setRemoteDescription添加对方SDP到远程视频描述信息,再通过createAnswer创建应答SDP信息,通过setLocalDescription添加到本地视频描述信息,再将answer SDP信息发送给发起方

  6. 发起方拿到对方发送的SDP信息后,通过setRemoteDescription添加对方SDP到远程视频描述信息,到这里,双方交换SDP已经完成。

  • 建立Candidate连接

通过监听onicecandidate,我们能获取当前本机的Candidate(ip,网络信息),通过信令服务器发送给对方,对方接收到后通过addIceCandidate,将双方建立连接,从而进行画面的流式传递,通道建立完成。可以监听icegatheringstatechange,当iceGatheringState变为complete时,表示双方网络通道已经连接就绪。

简单实现

peer接收方主要做的事:

  1. 初始化摄像头并通过addStream添加

  2. 创建offer设置setLocalDescription并发送offer sdp

  3. 收到answer sdp设置setRemoteDescription

  4. 收集到candidate发送给对方

// peerA 发起方
let localStream;
let localPeer;

//获取本机摄像头
async function getLocalVideo(){
   const stream = await getAndSelectCamera({ audio: true });
    if (stream) {
      localStream = stream;
      document.getElementById("localVideo").srcObject = stream;
      connectInit()
    }
}
getLocalVideo()

// RTC初始化
function connectInit(){
  localPeer = new RTCPeerConnection({});
  localPeer.addStream(localStream);
  sendOffer();
  localPeer.onaddstream = (e) => {
    document.getElementById("remoteVideo").srcObject = e.stream;
  };
  localPeer.addEventListener("icegatheringstatechange",(ev) => {
      if (ev.target.iceGatheringState === "complete") {
         console.log("视频连接成功")
      }
  },false);
}

// 发送offer
function sendOffer(){
  localPeer.createOffer((offer) => {
      // 发送Offer给对方
      mqttServer.publish("/webrtc",{type: "offer", offer})
      // 将offer设置到本地
      localPeer.setLocalDescription(offer);
  });

  localPeer.onicecandidate = (event) => {
    if (event.candidate) {
      //收集到candidate后,将candidate信息发送给对方
      mqttServer.publish("/webrtc",{type: "candidate", candidate: event.candidate})
    }
  };
}

// 获取到peerB的answer sdp
function getAnswer(data){
   localPeer.setRemoteDescription(new RTCSessionDescription(data.answer))
}

// 收到对方的candidate后,配对
function handleCandidate(data) {
   localPeer.addIceCandidate(new RTCIceCandidate(data.candidate));
}

peer接收方主要做的事:

  1. 初始化摄像头并通过addStream添加

  2. 收到对方发送的offer设置setRemoteDescription

  3. 创建answer sdp设置设置setLocalDescription并发送answer sdp

  4. 收集到candidate发送给对方

// peerB接收方

let localStream;
let localPeer;

//获取本机摄像头
async function getLocalVideo(){
   const stream = await getAndSelectCamera({ audio: true });
    if (stream) {
      localStream = stream;
      document.getElementById("localVideo").srcObject = stream;
      connectInit()
    }
}
getLocalVideo()

// RTC初始化
function connectInit(){
  localPeer = new RTCPeerConnection({});
  localPeer.addStream(localStream);
  localPeer.onaddstream = (e) => {
    document.getElementById("remoteVideo").srcObject = e.stream;
  };
  localPeer.onicecandidate = (event) => {
    if (event.candidate) {
       mqttServer.publish("/webrtc",{type: "candidate", candidate: event.candidate})
    }
  };
  localPeer.addEventListener("icegatheringstatechange",(ev) => {
      if (ev.target.iceGatheringState === "complete") {
         console.log("视频连接成功")
      }
  },false);
}

// 收到对方的offer后创建answer
function handleSendAnswer(data){
  localPeer.setRemoteDescription(new RTCSessionDescription(data.offer));
  localPeer.createAnswer(
    (answer) => {
      localPeer.setLocalDescription(answer);
      mqttServer.publish("/webrtc",{type: "answer",answer})
  });
}

//收到对方的candidate后,配对
function handleCandidate(data) {
   localPeer.addIceCandidate(new RTCIceCandidate(data.candidate));
}

TURN/STURN

这个主要是进行网络穿透,需要搭建一个TURN服务器,在创建RTCPeerConnection的时候有一个可选参数,配置这个可以进行网络穿透,由于我目前场景没有使用到这个,所以这里只是简单提一下。

{
  iceServers: [
    {
      url: "xxxx",
      username: "xxxx",
      credential: "xxxxxxxx",
    },
  ],
}

最后

到这里webRTC音视频通话已经完成了,其实还是挺有意思的,除此之外,WebRTC还可以用来实时传输文件等,例如可以实现白板写字同步功能等

0条评论

您的电子邮件等信息不会被公开,以下所有项均必填

OK! You can skip this field.