WebRTC是什么这里不做过多介绍, 可以参考WebRTC官网,以及它的Step by Step教程, 本文描述如何基于该项技术做一个简单的音视频对讲。
环境准备
- 浏览器:较新版本的Chrome
- 支持HTTPS的网页服务器,建议使用Nginx反向代理
通讯机制
浏览器使用一个简单的服务端key-value接口来交互数据
key-value实现
使用springboot实现一个简单的kv存储即可
(value = "/kv")
public class KeyValueController {
ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
(value = "/{key}")
String get(@PathVariable String key){
return map.get(key);
}
(value = "/{key}", method = RequestMethod.POST)
public String put(@PathVariable String key,
@RequestParam(value = "value") String value) throws UnsupportedEncodingException {
System.out.println(key + ":" + URLDecoder.decode(value,"utf-8"));
map.put(key,value);
return value;
}
}
浏览器调用key-value接口
浏览器使用js来访问kv存储, 除了读写数据外,还加入了一个wait函数,用于等待特定数据就位//为key添加一个前缀, 可以支持每个房间都有独立的key-value空间
var prefix = config.room;
//设置数据
function set(key, value, result) {
var str = encodeURI(JSON.stringify(value));
$.post("/kv/" + prefix + "_" + key, { value: str }, result);
}
//读取数据
function get(key, result) {
$.get(
"/kv/" + prefix + "_" + key,
null,
r1 => {
result(JSON.parse(decodeURI(r1)));
},
"text"
);
};
//等待指定数据就位
function wait(key,value,callback){
var timer = setInterval(()=>{
get(key,(v)=>{
if(v == value){
clearInterval(timer);
callback();
}
})
},1000);
}
工作流程
为了避免不必要的复杂度, 这里将通讯双方区分为主机和客机, 主机访问host.html,客机访问guest.html
主机工作流程
- 获取本地音视频,在本地显示
- 创建RTCPeerConnection,并监听onicecandidate和onaddstream
- 将本地流加入到RTCPeerConnection中,并createOffer
- createOffer成功后,设置RTCPeerConnection的本地描述符,并将本地描述符提交给kv存储
- onicecandidate 监听到candidates列表后,将该信息提交给kv存储,并报告kv存储主机网络已经就绪,并开始等待客户机网络就绪
- 当确认客户机网络就绪后,从kv存储中去除客户机的描述符和andidates列表,并设置在RTCPeerConnection上
- onaddstream 监听到事件后,表示客户机的流已经到位, 将客户机的流显示在本地
客机工作流程
- 获取本地音视频,在本地显示
- 等待主机的网络就位
- 当主机网络就位后,创建RTCPeerConnection,并监听onicecandidate和onaddstream
- 将本地流加入到RTCPeerConnection中
- 从kv存储中获取主机的candidates和流描述符,设置到RTCPeerConnection中
- 调用RTCPeerConnection的createAnswer
- createAnswer成功后,设置RTCPeerConnection的本地描述符,并将本地描述符提交给kv存储
- onicecandidate监听到candidates列表后,将该信息提交给kv存储,并报告kv存储客机网络已经就绪
- onaddstream 监听到事件后,表示主机的流已经到位, 将主机的流显示在本地
Turn服务
如果主机和客机处于同一个局域网,那么不需要额外的服务即可通讯, 否则的话,就有可能需要服务器来中转
可以使用Coturn TURN server的docker镜像boldt/coturn来快速搭建turn服务
下面是一段启动脚本#!/bin/bash
export MIN_PORT=50000
export MAX_PORT=51000
sudo docker run \
-d \
-p 3478:3478 \
-p 3478:3478/udp \
-p ${MIN_PORT}-${MAX_PORT}:${MIN_PORT}-${MAX_PORT}/udp \
-e USERNAME=username \
-e PASSWORD=password \
-e MIN_PORT=${MIN_PORT} \
-e MAX_PORT=${MAX_PORT} \
--restart=always \
--name coturn \
--volume /etc/pki/nginx/cert.pem:/etc/ssl/turn_server_cert.pem \
--volume /etc/pki/nginx/key.pem:/etc/ssl/turn_server_pkey.pem \
coturn