WebRTC快速了解和实践

时间:2021-1-8 作者:admin

前言

WebRTC 是网络实时通信(Web Real-Time Communication)的缩写,通过它 Web 开发者能够轻易快捷地开发出丰富的实时多媒体应用,而无需下载安装任何插件。

概述

WebRTC 允许网络应用建立浏览器之间的点对点连接,实现视频流、音频流或者其他任意数据的传输。

基本所有现代浏览器都支持 WebRTC:

WebRTC 主要提供了三个核心的 API:

  • getUserMedia:可以获取本地的媒体流,一个流包含几个轨道,比如视频和音频轨道。
  • RTCPeerConnection:用于建立 P2P 连接以及传输多媒体数据。
  • RTCDataChannel:建立一个双向通信的数据通道,可以传递多种数据类型。

通过这三个 API,我们可以获取本地的音视频流,然后与其他浏览器建立点对点连接并将音视频流发送给对方,还可以建立一个建立一个双向的数据通道,发送文本、文件等实时数据。

流程

这里举例小徐和小罗的通话流程:

  • 小徐首先创建PeerConnection对象,然后打开本地音视频设备,将音视频数据封装成MediaStream添加到PeerConnection中。

  • 然后小徐调用PeerConnection的CreateOffer方法创建一个用于offer的SDP对象,通过PeerConnection的SetLocalDescription方法将该SDP对象保存起来,并通过信令服务器发送给小罗。

  • 小罗接收到小徐发送过的offer SDP对象,通过PeerConnection的SetRemoteDescription方法将其保存起来,并调用PeerConnection的CreateAnswer方法创建一个应答的SDP对象,通过PeerConnection的SetLocalDescription的方法保存该应答SDP对象并将它通过信令服务器发送给小徐。

  • 小徐接收到小罗发送过来的应答SDP对象,将其通过PeerConnection的SetRemoteDescription方法保存起来。

  • 在SDP信息的offer/answer流程中,ClientA和ClientB已经根据SDP信息创建好相应的音频Channel和视频Channel并开启Candidate数据的收集,Candidate数据可以简单地理解成客户端的IP地址信息(本地IP地址、公网IP地址、Relay服务端分配的地址)。

  • 当小徐收集到Candidate信息后,PeerConnection会通过OnIceCandidate接口给自己发送通知,小徐将收到的Candidate信息通过信令服务器发送给小徐,小罗通过PeerConnection的AddIceCandidate方法保存起来。同样的操作小罗对小徐再来一次。

  • 小徐和小罗就已经建立了音视频传输的P2P通道,小罗接收到小徐传送过来的音视频流,会通过PeerConnection的OnAddStream回调接口返回一个标识小徐的音视频流的MediaStream对象,在小罗端渲染出来即可。同样操作也适应小罗到小徐的音视频流的传输。

代码实践(vue)

这里因为方便测试,省略了信令服务器,直接模拟本地端对端通道通信,通信原理流程还是一样的,属于精简版。

<template>
  <div class="container">
    <video id="localWebcam" class="local-video" autoplay="autoplay" />
    <video id="webcam" class="video" autoplay="autoplay" />
  </div>
</template>

<script>
export default {
  name: 'WebRtcDemo',
  mounted() {
    this.init()
  },
  methods: {
    init() {
      // 获取本地流
      navigator.mediaDevices
        .getUserMedia({ video: { width: 720, height: 1200 }, audio: true })
        .then(stream => {
          document.querySelector('#localWebcam').srcObject = stream
          this.startPeerConnection(stream)
        })
        .catch(err => {
          console.log('The following error occurred: ' + err.name)
        })
    },

    /**
     * @author xuchen
     * @date 2020-11-07 11:28:26
     * @desc 建立通信
     */
    startPeerConnection(stream) {
      // stun服务器
      const config = {
        iceServers: [
          { url: 'stun:stun.services.mozilla.com' },
          { url: 'stun:stunserver.org' },
          { url: 'stun:stun.l.google.com:19302' }
        ]
      }

      // 创建通道
      const selfConnection = new RTCPeerConnection(config)
      const otherConnection = new RTCPeerConnection(config)

      // 塞入本地流
      selfConnection.addStream(stream)

      // Candidate 信息
      selfConnection.onicecandidate = e => {
        if (e.candidate) {
          otherConnection.addIceCandidate(new RTCIceCandidate(e.candidate))
        }
      }

      otherConnection.onicecandidate = e => {
        if (e.candidate) {
          selfConnection.addIceCandidate(new RTCIceCandidate(e.candidate))
        }
      }

      // 接收远端的媒体流
      otherConnection.onaddstream = e => {
        document.querySelector('#webcam').srcObject = e.stream
      }

      // 创建offer
      selfConnection.createOffer().then(offer => {
        selfConnection.setLocalDescription(offer) // 保存本地的sdp信息
        otherConnection.setRemoteDescription(offer) // 设置远端的sdp信息

        // 创建应答
        otherConnection.createAnswer().then(answer => {
          otherConnection.setLocalDescription(answer) // 保存本地的sdp信息
          selfConnection.setRemoteDescription(answer) // 设置远端的sdp信息
        })
      })
    }
  }
}
</script>

<style lang="scss" scoped>
.container {
  position: fixed;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  margin: auto;
  width: 300px;
  height: 500px;
  background-color: #1c4054;
  .local-video,
  .video {
    width: 100%;
    height: 250px;
  }
}
</style>
声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。