关于即时通讯:零基础入门基于开源WebRTC从0到1实现实时音视频聊天功能

49次阅读

共计 9464 个字符,预计需要花费 24 分钟才能阅读完成。

本文由微医云技术团队前端工程师张宇航分享,原题“从 0 到 1 打造一个 WebRTC 利用”,有订正和改变。

1、引言

去年初,从天而降的新冠肺炎疫情让线下就医渠道简直被切断,在此背景下,在线问诊模式疾速解决了大量急需就医病患的当务之急。而作为在线问诊中重要的一环——医患之间的视频问诊正是利用了实时音视频技术才得以实现。

众所周之,实时音视频聊天技术门槛很高,个别的公司要想在短时间内从零补齐这方面的技术短板相当艰难,而开源音视频工程 WebRTC 提供了这样一个捷径(包含笔者公司的产品在内,同样是基于 WebRTC 技术才得以达成)。

本文将基于笔者公司开发的在线问诊产品中 WebRTC 技术的实践经验,讲述的如何基于 WebRTC 从零开发一个实时音视频聊天性能。文章会从 WebRTC 的基本知识、技术原理开始,基于开源技术为你演示如何搭建一个 WebRTC 实时音视频聊天性能。

学习交换:

  • 挪动端 IM 开发入门文章:《新手入门一篇就够:从零开发挪动端 IM》
  • 开源 IM 框架源码:https://github.com/JackJiang2…

(本文同步公布于:http://www.52im.net/thread-36…)

2、本文源码

残缺源码附件下载:http://www.52im.net/thread-36…

cdwebrtc-server
yarn
npm start
援用
cdwebrtc-static
yarn
npm start

3、常识筹备

3.1 音视频实践根底
在理解 WebRTC 技术之前,如果你对音视频技术的基础理论还不理解的话,倡议优先从以下几篇入门文章先学一学。

《零根底,史上最艰深视频编码技术入门》(* 必读)
《写给小白的实时音视频技术入门提纲》
《零根底入门:实时音视频技术基础知识全面盘点》
《实时音视频面视必备:疾速把握 11 个视频技术相干的根底概念》(* 必读)
《爱奇艺技术分享:轻松滑稽,解说视频编解码技术的过来、当初和未来》

3.2 什么是 WebRTC

▲ 图片援用自《了不起的 WebRTC:生态日趋完善,或将实时音视频技术白菜化》

WebRTC(Web Real-Time Communication)是 Google 在 2010 年以 6820 万美元收买 VoIP 软件开发商 Global IP Solutions 的 GIPS 引擎,并改名为“WebRTC”于 2011 年将其开源的旨在建设一个互联网浏览器之间的音视频和数据实时通信的平台。更多 WebRTC 介绍详见《了不起的 WebRTC:生态日趋完善,或将实时音视频技术白菜化》,本文不做赘述。

那么 WebRTC 能做些什么呢?

除了咱们大家每天都在用的微信、钉钉、qq 这类传统的 IM 社交软件中的实时音视频通话以外,笔者公司产品中波及医疗畛域中的在线问诊 / 近程门诊 / 近程会诊,还有时下较为风行的互动直播、在线教育等场景。除此之外,随同着 5G 的疾速建设,WebRTC 也为云游戏提供了很好的技术撑持。

3.3 WebRTC 的学习资源
WebRTC 官网资源:

《WebRTC 开源工程官网》
《WebRTC 开源工程源码托管地址》
《WebRTC 规范 API 在线文档》

其它 WebRTC 学习资源:

《开源实时音视频技术 WebRTC 在 Windows 下的扼要编译教程》
《WebRTC 实时音视频技术的整体架构介绍》
《良心分享:WebRTC 零根底开发者教程(中文)[附件下载]》

4、WebRTC 技术组成

来自 WebRTC 官网的整体技术组成图:

整个 WebRTC 大抵能够分为以下 3 局部:

1)紫色提供给 Web 前端开发应用的 API;
2)蓝色实线局部提供各大浏览器厂商应用的 API;
3)蓝色虚线局部蕴含 3 局部:音频引擎、视频引擎、网络传输 (Transport),都能够自定义实现。

因篇幅无限,本节不深刻探讨,有趣味能够读读《WebRTC 实时音视频技术的整体架构介绍》。

5、WebRTC 的 P2P 通信原理

5.1 P2P 通信的技术难点
P2P 通信即点对点通信。

要实现两个不同网络环境(具备麦克风、摄像头设施)的客户端(可能是不同的 Web 浏览器或者手机 App)之间的实时音视频通信的难点在哪里、须要解决哪些问题?

总结一下,次要是上面这 3 个问题:

1)怎么晓得彼此的存在也就是如何发现对方?
2)彼此音视频编解码能力如何沟通?
3)音视频数据如何传输,怎么能让对方看得本人?

上面咱们将一一探讨这 3 个问题。

5.2 怎么晓得彼此的存在(也就是如何发现对方)?
对于问题 1:WebRTC 尽管反对端对端通信,然而这并不意味着 WebRTC 不再须要服务器。

在 P2P 通信的过程中,单方须要替换一些元数据比方媒体信息、网络数据等等信息, 咱们通常称这一过程叫做“信令(signaling)”。

对应的服务器即“信令服务器 (signaling server)”,通常也有人将之称为“房间服务器”,因为它不仅能够替换彼此的媒体信息和网络信息,同样也能够治理房间信息。

比方:

1)告诉彼此 who 退出了房间;
2)who 来到了房间
3)通知第三方房间人数是否已满是否能够退出房间。

为了避免出现冗余,并最大限度地进步与已有技术的兼容性,WebRTC 规范并没有规定信令办法和协定。在本文前面的实际章节会利用 Koa 和 Socket.io 技术实现一个信令服务器。

5.3 彼此音视频编解码能力如何沟通?
对于问题 2:咱们首先要晓得的是,不同浏览器对于音视频的编解码能力是不同的。

比方: Peer-A 端反对 H264、VP8 等多种编码格局,而 Peer-B 端反对 H264、VP9 等格局。为了保障单方都能够正确的编解码,最简略的方法即取它们所都反对格局的交加 -H264。

在 WebRTC 中:有一个专门的协定,称为 Session Description Protocol(SDP),能够用于形容上述这类信息。

因而:参加音视频通信的单方想要理解对方反对的媒体格式,必须要替换 SDP 信息。而替换 SDP 的过程,通常称之为媒体协商。

5.4 音视频数据如何传输,怎么能让对方看得本人?
对于问题 3:其本质上就是网络协商的过程,即参加音视频实时通信的单方要理解彼此的网络状况,这样才有可能找到一条互相通信的链路。

现实的网络状况是每个浏览器的电脑都有本人的公有公网 IP 地址,这样的话就能够间接进行点对点连贯。

但实际上:出于网络安全和 IPV4 地址不够的思考,咱们的电脑与电脑之间或大或小都是在某个局域网内,须要 NAT(“Network Address Translation,”中文译为“网络地址转换”)。在 WebRTC 中咱们应用 ICE 机制建设网络连接。

那么何为 ICE?

ICE (Interactive Connecctivity Establishment, 交互式连贯建设),ICE 不是一种协定,而是整合了 STUN 和 TURN 两种协定的框架。

其中:STUN(Sesssion Traversal Utilities for NAT, NAT 会话穿梭应用程序),它容许位于 NAT(或多重 NAT)后的客户端找出本人对应的公网 IP 地址和端口,也就是俗称的 P2P“打洞”。

然而:如果 NAT 类型是对称型的话,那么就无奈打洞胜利。这时候 TURN 就派上用场了,TURN(Traversal USing Replays around NAT)是 STUN/RFC5389 的一个拓展协定在其根底上增加了 Replay(中继)性能。

简略来说:其目标就是解决对称 NAT 无奈穿梭的问题,在 STUN 调配公网 IP 失败后,能够通过 TURN 服务器申请公网 IP 地址作为中继地址。

在 WebRTC 中有三种类型的 ICE 候选者,它们别离是:

1)主机候选者:示意的是本地局域网内的 IP 地址及端口。它是三个候选者中优先级最高的,也就是说在 WebRTC 底层,首先会尝试本地局域网内建设连贯;
2)反射候选者:示意的是获取 NAT 内主机的外网 IP 地址和端口。其优先级低于 主机候选者。也就是说当 WebRTC 尝试本地连接不通时,会尝试通过反射候选者取得的 IP 地址和端口进行连贯;
3)中继候选者:示意的是中继服务器的 IP 地址与端口,即通过服务器直达媒体数据。当 WebRTC 客户端通信单方无奈穿梭 P2P NAT 时,为了保障单方能够失常通信,此时只能通过服务器直达来保障服务质量了。

从上图咱们能够看出:在非本地局域网内 WebRTC 通过 STUN server 取得本人的外网 IP 和端口,而后通过信令服务器与远端的 WebRTC 替换网络信息,之后单方就能够尝试建设 P2P 连贯了。当 NAT 穿梭不胜利时,则会通过 Relay server (TURN)直达。

值得一提的是:在 WebRTC 中网络信息通常用 candidate 来形容,而上述图中的 STUN server 和 Replay server 也都能够是同一个 server。在文末的实际章节即是采纳了集成了 STUN(打洞)和 TURN(中继)性能的开源我的项目 coturn。

综上对三个问题的解释,咱们能够用下图来阐明 WebRTC 点对点通信的基本原理。

简而言之:就是通过 WebRTC 提供的 API 获取各端的媒体信息 SDP 以及 网络信息 candidate,并通过信令服务器替换,进而建设了两端的连贯通道实现实时视频语音通话。

PS:无关 P2P 的相干常识,能够深刻学习一下文章:

《P2P 技术详解 (一):NAT 详解——具体原理、P2P 简介》
《P2P 技术详解(二):P2P 中的 NAT 穿梭(打洞) 计划详解 (基本原理篇)》
《P2P 技术详解(三):P2P 中的 NAT 穿梭(打洞) 计划详解(进阶剖析篇)》
《P2P 技术详解(四):P2P 技术之 STUN、TURN、ICE 详解》
《通俗易懂:疾速了解 P2P 技术中的 NAT 穿透原理》

6、WebRTC 的几个重要的 API

6.1 音视频采集 API
音视频采集 API,即 MediaDevices.getUserMedia()。

示例代码:

const constraints = {
        video: true,
        audio: true
};

// 非平安模式(非 https/localhost)下 navigator.mediaDevices 会返回 undefined

try{const stream = await navigator.mediaDevices.getUserMedia(constraints);
        document.querySelector('video').srcObject = stream;
    }   catch(error) {console.error(error);
    }

6.2 获取音视频设施输入输出列表
获取音视频设施输入输出列表 API,即 MediaDevices.enumerateDevices()。

示例代码:

try{const devices = await navigator.mediaDevices.enumerateDevices();
        this.videoinputs = devices.filter(device => device.kind === 'videoinput');
        this.audiooutputs = devices.filter(device => device.kind === 'audiooutput');
        this.audioinputs = devices.filter(device => device.kind === 'audioinput');

      } catch(error) {console.error(error);
      }

6.3 RTCPeerConnection
RTCPeerConnection 作为创立点对点连贯的 API, 是咱们实现音视频实时通信的要害。

在本文的实际章节中,次要使用到了以下办法。

媒体协商办法:

createOffer
createAnswer
localDesccription
remoteDesccription

重要事件:

onicecandidate
onaddstream

在上个章节的形容中能够晓得 P2P 通信中最重要的一个环节就是替换媒体信息。

媒体协商原理:

从上图不难发现,整个媒体协商过程能够简化为三个步骤对应上述四个媒体协商办法。

具体是:

1)呼叫端 Amy 创立 Offer(createOffer)并将 offer 音讯(内容是呼叫端 Amy 的 SDP 信息)通过信令服务器传送给接收端 Bob, 同时调用 setLocalDesccription 将含有本地 SDP 信息的 Offer 保存起来;
2)接收端 Bob 收到对端的 Offer 信息后调用 setRemoteDesccription 办法将含有对端 SDP 信息的 Offer 保存起来,并创立 Answer(createAnswer)并将 Answer 音讯(内容是接收端 Bob 的 SDP 信息)通过信令服务器传送给呼叫端 Amy;
3)呼叫端 Amy 收到对端的 Answer 信息后调用 setRemoteDesccription 办法将含有对端 SDP 信息的 Answer 保存起来。

通过上述三个步骤,则实现了 P2P 通信过程中的媒体协商局部。

实际上:在呼叫端以及接收端调用 setLocalDesccription 同时也开始了收集各端本人的网络信息(candidate),而后各端通过监听事件 onicecandidate 收集到各自的 candidate 并通过信令服务器传送给对端,进而买通 P2P 通信的网络通道,并通过监听 onaddstream 事件拿到对方的视频流进而实现了整个视频通话过程。

7、入手编码实际

提醒:本节所波及的残缺源码,请从本文“2、本文源码”一节的附件下载。

7.1 coturn 服务器的搭建
留神:如果只是本地局域网测试则无需搭建 [url=%5Burl=https://github.com/coturn/%5D…[/url]]coturn[/url] 服务器,如果须要外网拜访在搭建 coturn 服务器之前你须要购买一台云主机以及绑定反对 https 拜访的域名。以下是笔者本人搭建的过程,感兴趣的能够参照着自已实际一次。

coturn 服务器的搭建次要是为了解决 NAT 无奈穿梭的问题。

其装置也较为简单:

  1. git clone [url=https://github.com/coturn/cot…]https://github.com/coturn/coturn.git[/url]
  2. cdcoturn/
  3. ./configure–prefix=/usr/local/coturn
  4. make-j 4
  5. makeinstall
    // 生成 key
  6. openssl req -x509 -newkey rsa:2048 -keyout /etc/turn_server_pkey.pem -out /etc/turn_server_cert.pem -days 99999 -nodes

7.2 coturn 服务配置
我的配置如下:

vim /usr/local/coturn/etc/turnserver.conf
listening-port=3478
external-ip=xxx.xxx // 你的主机公网 IP
user=xxx:xxx // 账号: 明码
realm=xxx.com // 你的域名

7.3 启动 coturn 服务
我的启动过程:

  1. cd/usr/local/coturn/bin/
  2. ./turnserver-c ../etc/turnserver.conf
    // 留神:云主机内的 TCP 和 UDP 的 3478 端口都要开启

7.4 实际代码
在编写代码之前,联合上述章节 WebRTC 点对点通信的基本原理,能够得出以下流程图。

从图中不难看出,假如 PeerA 为发起方,PeerB 为接管方要实现 WebRTC 点对点的实时音视频通信,信令 (Signal) 服务器是必要的,以治理房间信息以及转发网络信息和媒体信息的。

在本文中是利用 koa 及 socket.io 搭建的信令服务器:

// server 端 server.js
const Koa = require(‘koa’);
const socket = require(‘socket.io’);
const http = require(‘http’);
const app = newKoa();
const httpServer = http.createServer(app.callback()).listen(3000, ()=>{});
socket(httpServer).on(‘connection’, (sock)=>{

// ....

});
援用
// client 端 socket.js
import io from ‘socket.io-client’;
const socket = io.connect(window.location.origin);
export defaultsocket;

在搭建好信令服务器后,联合流程图,有以下步骤。

步骤 1:PeerA 和 PeerB 端别离连贯信令服务器,信令服务器记录房间信息:

// server 端 server.js
socket(httpServer).on('connection', (sock)=>{
    // 用户来到房间
    sock.on('userLeave',()=>{// ...});

    // 查看房间是否可退出
    sock.on('checkRoom',()=>{// ...});
    // ....
});
// client 端 Room.vue
import socket from '../utils/socket.js';

// 服务端告知用户是否可退出房间
socket.on('checkRoomSuccess',()=>{// ...});
// 服务端告知用户胜利退出房间
socket.on('joinRoomSuccess',()=>{// ...});
//....

步骤 2:A 端作为发动方向接管方 B 端发动视频邀请,在失去 B 批准视频申请后,单方都会创立本地的 RTCPeerConnection,增加本地视频流,其中发送方会创立 offer 设置本地 sdp 信息形容,并通过信令服务器将本人的 SDP 信息发送给对端

socket.on('answerVideo', async (user) => {VIDEO_VIEW.showInvideoModal();
        // 创立本地视频流信息
        const localStream = await this.createLocalVideoStream();
        this.localStream = localStream;
        document.querySelector('#echat-local').srcObject = this.localStream;
        this.peer = newRTCPeerConnection();
        this.initPeerListen();
        this.peer.addStream(this.localStream);
        if(user.sockId === this.sockId) {// 接管方} else{
          // 发送方 创立 offer
          const offer = await this.peer.createOffer(this.offerOption);
          await this.peer.setLocalDescription(offer);
          socket.emit('receiveOffer', { user: this.user, offer});
        }
 });

步骤 3:后面提起过其实在调用 setLocalDescription 的同时,也会开始收集本人端的网络信息(candidate),如果在非局域网内或者网络“打洞”不胜利,还会尝试向 Stun/Turn 服务器发动申请,也就是收集“中继候选者”,因而在创立 RTCPeerConnection 咱们还须要监听 ICE 网络候选者的事件:

init PeerListen () {
      // 收集本人的网络信息并发送给对端
      this.peer.onicecandidate = (event) => {if(event.candidate) {socket.emit('addIceCandidate', { candidate: event.candidate, user: this.user}); }
      };
      // ....
    }

步骤 4:当接管方 B 端通过信令服务器拿到对端发送方 A 端的含有 SDP 的 offer 信息后则会调用 setRemoteDescription 存储对端的 SDP 信息,创立及设置本地的 SDP 信息, 并通过信令服务器传送含有本地 SDP 信息的 answer:

socket.on('receiveOffer', async (offer) => {await this.peer.setRemoteDescription(offer);
        const answer = await this.peer.createAnswer();
        await this.peer.setLocalDescription(answer);
        socket.emit('receiveAnsewer', { answer, user: this.user});
 });

步骤 5:当发起方 A 通过信令服务器接管到接管方 B 的 answer 信息后则也会调用 setRemoteDescription,这样单方就实现了 SDP 信息的替换:

socket.on('receiveAnsewer', (answer) => {this.peer.setRemoteDescription(answer);
      });

步骤 6:当单方 SDP 信息替换实现并且监听 icecandidate 收集到网络候选者通过信令服务器替换后,则会拿到彼此的视频流:

socket.on('addIceCandidate', async (candidate) => {await this.peer.addIceCandidate(candidate);
});

this.peer.onaddstream = (event) => {
        // 拿到对方的视频流
        document.querySelector('#remote-video').srcObject = event.stream;
};

7.5 运行成果

8、本文小结

通过上个章节的 6 个步骤,即可实现一个基于 WebRTC 的残缺 P2P 视频实时通话性能(代码可通过:本节所波及的残缺源码,请从本文“2、本文源码”一节的附件下载)。

值得一提的是:代码中的 VIDEO_VIEW 是专一于视频 UI 层的 JS SDK,蕴含了发动视频 Modal、接管视频 Modal、视频中 Modal,其是从笔者线上 Web 视频问诊产品所应用的 JS SDK 抽离进去的。

本文只是简略地介绍了 WebRTC P2P 的通信基本原理以及简略的代码实际,事实上咱们生产环境所应用的 SDK 不仅反对点对点通信,还反对多人视频通话,屏幕共享等性能这些都是基于 WebRTC 实现的。

9、参考资料

[1] WebRTC 规范 API 在线文档
[2] WebRTC in the real world: STUN, TURN and signaling
[3] WebRTC 信令管制与 STUN/TURN 服务器搭建
[4] 了不起的 WebRTC:生态日趋完善,或将实时音视频技术白菜化
[5] 开源实时音视频技术 WebRTC 在 Windows 下的扼要编译教程
[6] WebRTC 实时音视频技术的整体架构介绍
[7] 良心分享:WebRTC 零根底开发者教程(中文)[附件下载]
[8] P2P 技术详解(二):P2P 中的 NAT 穿梭(打洞) 计划详解(基本原理篇)
[9] P2P 技术详解(四):P2P 技术之 STUN、TURN、ICE 详解
[10] 通俗易懂:疾速了解 P2P 技术中的 NAT 穿透原理

本文已同步公布于“即时通讯技术圈”公众号。
同步公布链接是:http://www.52im.net/thread-36…

正文完
 0