之前我们已经在阿里云服务器上搭建了turn/stun服务(https://juejin.cn/post/7122267115540709407),也使用webRTC测试过rtsp转webRTC播放视频(https://juejin.cn/post/7119334586089209869),实时性很好,这里我们再搭建官网的全功能WebRTC应用程序AppRTC应用服务做一下实时音视频通话的测试。
之前说了很多webrtc的内容,但是一直没有系统说过如何学习,其实就像很久前就说过的从官网了解并获取资料:https://webrtc.org/
借助 WebRTC,您可以为应用添加基于开放标准运行的实时通信功能。它支持在对等设备之间发送视频、语音和通用数据,使开发者能够构建强大的语音和视频通信解决方案。这项技术适用于所有现代浏览器以及所有主要平台的原生客户端。WebRTC 采用的技术是开放网络标准,以常规 JavaScript API 的形式在所有主流浏览器中提供。对于原生客户端(例如 Android 和 iOS 应用),可以使用具备相同功能的库。WebRTC 项目属于开源项目,受 Apple、Google、Microsoft 和 Mozilla 等公司支持。本页面由 Google WebRTC 团队维护。
示例:https://webrtc.github.io/samples/
WebRTC 有许多不同的用例,从使用摄像头或麦克风的基本 Web 应用,到更高级的视频通话应用和屏幕共享。我们收集了大量的代码示例,以便更好地说明该技术的工作原理及其用途。
使用指南:https://webrtc.org/getting-started/overview
GitHub:https://github.com/webrtc/apprtc/
AppRTC 房间+Web服务器使用python+js语言
AppRTC Collider信令服务器采用go语言
Coturn 采用C语言
在部署到公网时需要通过Nginx做Web和Websocket的代理连接
实际开发:把信令+房间管理都是写到一个服务器(我们后续可以尝试将这些服务都用go来实现)
参考:https://chromium.googlesource.com/external/webrtc/apprtc/+/098481a24a37529565fe4bb416a11c84d04370f4/README.md
https://blog.csdn.net/lingshengxueyuan/article/details/100519054
注:下列搭建过程是我在阿里云的ubuntu20上利用docker实践总结的,测试效果还可以,并且测试Windows、macOS(后续可能会在支持docker的openWRT或Android上实践,有需要的可以先照猫画虎尝试一下)上docker拉取镜像后改进使用都没有任何问题,大家可以放心使用。
自行搜索解决(个人感觉你可以将其当成git来学,虽然两者是不同的东西,但是我感觉分布式的思想和命令行操作这块两者惊人的相似):https://blog.csdn.net/weixin_39510813/category_11255657.html?spm=1001.2014.3001.5482
docker pull ubuntu:16.04
docker images
docker run -it ubuntu:16.04 /bin/bash
apt-get update
apt-get install vim
# 1 在修改source.list前,最好先备份一份
mv /etc/apt/sources.list /etc/apt/sources.list.old
# 2 执行命令打开sourcse.list文件
vim /etc/apt/sources.list
# 3 复制更新源
apt-get update
# apt-get install openssh-server(我们是docker方式可以不需要)
# apt-get install net-tools
# apt-get install iputils-ping
apt-get install git
复制以下源到sources.list
# deb cdrom:[Ubuntu 16.04 LTS _Xenial Xerus_ - Release amd64 (20160420.1)]/ xenial main restricted
deb-src http://archive.ubuntu.com/ubuntu xenial main restricted #Added by software-properties
deb http://mirrors.aliyun.com/ubuntu/ xenial main restricted
deb-src http://mirrors.aliyun.com/ubuntu/ xenial main restricted multiverse universe #Added by software-properties
deb http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted
deb-src http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted multiverse universe #Added by software-properties
deb http://mirrors.aliyun.com/ubuntu/ xenial universe
deb http://mirrors.aliyun.com/ubuntu/ xenial-updates universe
deb http://mirrors.aliyun.com/ubuntu/ xenial multiverse
deb http://mirrors.aliyun.com/ubuntu/ xenial-updates multiverse
deb http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse #Added by software-properties
deb http://archive.canonical.com/ubuntu xenial partner
deb-src http://archive.canonical.com/ubuntu xenial partner
deb http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted
deb-src http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted multiverse universe #Added by software-properties
deb http://mirrors.aliyun.com/ubuntu/ xenial-security universe
deb http://mirrors.aliyun.com/ubuntu/ xenial-security multiverse
mkdir ~/webrtc
cd ~/webrtc
注意后续的目录,因为我们是docker中直接root用户,所有后续目录是/root/webrtc,后续的目录都是采用该目录。
安装需要的各种工具(除了apt之外还可以下载安装包或者源码自己编译安装)
# 先安装add-apt-repository命令支持
apt-get install python-software-properties
apt-get install software-properties-common
# 第一步:打开终端,添加ppa源
add-apt-repository ppa:openjdk-r/ppa
# 第二步:更新源
apt-get update
# 第三步:安装openjdk 8
apt-get install openjdk-8-jdk
# 第四步:配置openjdk 8为默认java环境
update-alternatives --config java
update-alternatives --config javac
# 最后,验证一下是否成功
java -version
# 返回
openjdk version "1.8.0_222"
OpenJDK Runtime Environment (build 1.8.0_222-8u222-b10-1ubuntu1~16.04.1-b10)
OpenJDK 64-Bit Server VM (build 25.222-b10, mixed mode)
cd ~/webrtc
apt-get install wget
wget https://nodejs.org/dist/v10.16.0/node-v10.16.0-linux-x64.tar.xz
# 解压
tar -xvf node-v10.16.0-linux-x64.tar.xz
# 进入目录
cd node-v10.16.0-linux-x64/
# 查看当前的目录
pwd
# 确认一下nodejs下bin目录是否有node 和npm文件,如果有就可以执行软连接,比如
ln -s /root/webrtc/node-v10.16.0-linux-x64/bin/npm /usr/local/bin/
ln -s /root/webrtc/node-v10.16.0-linux-x64/bin/node /usr/local/bin/
# 看清楚,这个路径是你自己创建的路径,我的路径是/root/webrtc/node-v10.16.0-linux-x64
# 查看是否安装,安装正常则打印版本号
node -v
npm -v
# 有版本信息后安装 grunt-cli,先进到nodejs的bin目录, 要和自己的目录匹配
cd /root/webrtc/node-v10.16.0-linux-x64/bin
npm -g install grunt-cli
ln -s /root/webrtc/node-v10.16.0-linux-x64/bin/grunt /usr/local/bin/
grunt --version
# 显示grunt-cli v1.4.3
#使用淘宝源替换npm,后续要执行npm时执行cnpm
npm install -g cnpm --registry=https://registry.npm.taobao.org
ln -s /root/webrtc/node-v10.16.0-linux-x64/lib/node_modules/cnpm/bin/cnpm /usr/local/bin/
ubuntu 一般都会自带python 2.7 输入 python -v
输出版本则已经有了
如果没有则安装:
# 没有则输入下命令
apt-get install python
# python 安装之后安装 Python-webtest
apt-get install python-webtest
python -V
#Python 2.x
# 回到webrtc目录
cd ~/webrtc/
# 下载google_appengine
wget https://storage.googleapis.com/appengine-sdks/featured/google_appengine_1.9.40.zip
apt-get install unzip
unzip google_appengine_1.9.40.zip
#配置环境变量:在/etc/profile文件最后增加一行,和自己路径保持一致
export PATH=$PATH:/root/webrtc/google_appengine
# 生效
source /etc/profile
安装go:
apt-get install golang-go
#检测go 版本
go version
#go version go1.6.2 linux/amd64
创建go工作目录
#创建go工作目录
mkdir -p ~/webrtc/goworkspace/src
#配置环境变量:在/etc/profile文件最后增加一行:
vim /etc/profile
export GOPATH=/root/webrtc/goworkspace
# 然后执行
source /etc/profile
cd ~/webrtc/
# 先尝试使用github的下载连接
git clone https://github.com/webrtc/apprtc.git
# 如果github的连接下不了就用码云的链接
git clone https://gitee.com/sabergithub/apprtc.git
#将collider的源码软连接到go的工作目录下
ln -s /root/webrtc/apprtc/src/collider/collider $GOPATH/src
ln -s /root/webrtc/apprtc/src/collider/collidermain $GOPATH/src
ln -s /root/webrtc/apprtc/src/collider/collidertest $GOPATH/src
#下一步在编译 go get collidermain: 被墙
#报错: package golang.org/x/net/websocket: unrecognized import path "golang.org/x/net/websocket"
#所以先执行:
mkdir -p $GOPATH/src/golang.org/x/
cd $GOPATH/src/golang.org/x/
git clone https://github.com/golang/net.git net
go install net
#如果git clone不行把代理关了再试下
#编译collidermain
cd ~/webrtc/goworkspace/src
go get collidermain
go install collidermain
apt-get install libssl-dev
apt-get install libevent-dev
# 返回webrtc目录
cd ~/webrtc
#git clone https://github.com/coturn/coturn
#cd coturn
#提供另一种安装方式turnserver是coturn的升级版本
wget http://coturn.net/turnserver/v4.5.0.7/turnserver-4.5.0.7.tar.gz
tar xfz turnserver-4.5.0.7.tar.gz
cd turnserver-4.5.0.7
./configure
make
make install
#安装依赖:gcc、g++依赖库
apt-get install build-essential libtool
#安装 pcre依赖库(http://www.pcre.org/)
apt-get install libpcre3 libpcre3-dev
#安装 zlib依赖库(http://www.zlib.net)
apt-get install zlib1g-dev
#安装ssl依赖库
apt-get install openssl
#下载nginx 1.15.8版本
wget http://nginx.org/download/nginx-1.15.8.tar.gz
tar xvzf nginx-1.15.8.tar.gz
cd nginx-1.15.8/
# 配置,一定要支持https
./configure --with-http_ssl_module
# 编译
make
#安装
make install
默认安装目录:/usr/local/nginx
启动:/usr/local/nginx/sbin/nginx
停止:/usr/local/nginx/sbin/nginx -s stop
重新加载配置文件:/usr/local/nginx/sbin/nginx -s reload
/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
配置防火墙,允许访问3478端口(含tcp和udp,此端口用于nat穿透),实际还有带加密的端口;
turnserver -L 0.0.0.0 -a -u admin:admin
#账号 admin 密码:admin 这一步随便给,但是后面配置apprtc时需要用到
#命令后加 & ,执行起来后按 ctr+c,不会停止
apt-get install lsof
#开启新窗口 执行
lsof -i:3478
#输出大致这样的成功
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
turnserve 30299 root 16u IPv4 61868 0t0 UDP *:3478
turnserve 30299 root 17u IPv4 61869 0t0 UDP *:3478
turnserve 30299 root 32u IPv4 61879 0t0 TCP *:3478 (LISTEN)
turnserve 30299 root 33u IPv4 61880 0t0 TCP *:3478 (LISTEN)
我们后面会通过docker运行该服务,大致命令:
docker run -p 3478:3478 -t -i -d ubuntu-apprtc:1.0.0 turnserver -L 0.0.0.0 -a -u admin:admin
配置防火墙,允许访问8089端口(tcp,用于客户端和collider建立websocket信令通信)
# 执行collider 信令服务器
sudo nohup $GOPATH/bin/collidermain -port=8089 -tls=false -room-server="http://192.168.2.143:8090" &
-room-server=“http://192.168.2.143:8090” 实际是连接房间服务器的地址(如果部署在本地可以使用localhost)
我们后面会通过docker运行该服务,大致命令:
docker run -t -i -d -p 8089:8089 ubuntu-apprtc:1.0.0 /root/webrtc/goworkspace/bin/collidermain -port=8089 -tls=false -room-server="http://www.xiaoyaoyou.xyz:8090"
先安装python的request模块
cd /root/webrtc
wget https://pypi.python.org/packages/2.7/s/setuptools/setuptools-0.6c11-py2.7.egg --no-check-certificate
chmod +x setuptools-0.6c11-py2.7.egg
./setuptools-0.6c11-py2.7.egg
wget https://pypi.python.org/packages/source/p/pip/pip-1.5.4.tar.gz
tar -xf pip-1.5.4.tar.gz
cd pip-1.5.4/
python setup.py install
pip install requests==2.20
配置防火墙,允许访问8090端口(tcp,此端口用于web访问)
配置文件修改(主要是配置apprtc对应的conturn和collider相关参数)
vim /root/webrtc/apprtc/src/app_engine/constants.py
修改后(填的都是外网IP,为了适合更多数朋友测试,我这里用的是内网的环境,在公网部署填入公网IP或域名即可;stun/turn可以使用网上免费的,国内也有很多免费的,就可以本地不启动stun/turn服务了,wss服务主要是信令服务器需要,如果信令服务是跑在本地就使用本机地址,后面配置证书使用nginx代理即可)
# ICE_SERVER_OVERRIDE = None 注释掉
# Turn/Stun server override. This allows AppRTC to connect to turn servers
# directly rather than retrieving them from an ICE server provider.
# ICE_SERVER_OVERRIDE = None
# Enable by uncomment below and comment out above, then specify turn and stun
ICE_SERVER_OVERRIDE = [
{
"urls": [
"turn:localhost:3478?transport=udp",
"turn:localhost:3478?transport=tcp"
],
"username": "admin",
"credential": "admin"
},
{
"urls": [
"stun:localhost:3478"
]
}
]
ICE_SERVER_BASE_URL = 'https:localhost'
ICE_SERVER_URL_TEMPLATE = '%s/v1alpha/iceconfig?key=%s'
ICE_SERVER_API_KEY = os.environ.get('ICE_SERVER_API_KEY')
# Dictionary keys in the collider instance info constant.
WSS_INSTANCE_HOST_KEY = 'localhost:8088'
WSS_INSTANCE_NAME_KEY = 'vm_name'
WSS_INSTANCE_ZONE_KEY = 'zone'
WSS_INSTANCES = [{
WSS_INSTANCE_HOST_KEY: 'localhost:8088',
WSS_INSTANCE_NAME_KEY: 'wsserver-std',
WSS_INSTANCE_ZONE_KEY: 'us-central1-a'
}]
进到apprtc目录(这个web后端后面我们尝试改成go实现来替代python实现):
#编译
cd /root/webrtc/apprtc
cnpm install
grunt build
npm install --dev coffeescript
grunt build
#如果cnpm安装报错:
npm uninstall -g cnpm // 首先卸载 cnpm
npm install [email protected] -g // 安装指定版本 cnpm
/root/webrtc/google_appengine/dev_appserver.py --host=0.0.0.0 --port=8090 /root/webrtc/apprtc/out/app_engine --skip_sdk_update_check
docker run -t -i -d -p 8090:8090 ubuntu-apprtc:1.0.0 /root/webrtc/google_appengine/dev_appserver.py --host=0.0.0.0 --port=8090 /root/webrtc/apprtc/out/app_engine --skip_sdk_update_check
此时可以通过谷歌浏览器访问测试:http://localhost:8090
虽然可以访问网页,但是无法进入房间无法获取音视频权限,还需要后续配置ssl。
#检查
lsof -i:8090
#输出下列内容
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
python 13744 root 8u IPv4 58173 0t0 TCP *:8090 (LISTEN)
注意:nginx我们这里暂时不使用docker方式,直接安装到本机进行代理配置,方便使用云服务器的证书,当然,你也可以选择docker方式,docker运行我们后面再说明,其实大差不差。
mkdir -p ~/cert
cd ~/cert
# CA私钥
openssl genrsa -out key.pem 2048
# 自签名证书
openssl req -new -x509 -key key.pem -out cert.pem -days 1095
之后需要输入国家省市地区公司名个人名邮件等,可以随意,这里是自签名证书,商用时需要申请域名证书,上节我们在阿里云上申请免费ssl证书已经总结过了(https://blog.csdn.net/weixin_39510813/article/details/125886341?spm=1001.2014.3001.5502)。
反向代理apprtc,使之支持https访问,如果http直接访问apprtc,则客户端无法启动视频音频采集(必须得用https访问)
– 配置web服务器
(1)配置自己的证书
// 注意证书所在的路径
ssl_certificate /root/cert/cert.pem
ssl_certificate_key /root/cert/key.pem
(2)配置主机域名或者主机IP
server_name 192.168.2.143;
完整配置文件:/usr/local/nginx/conf/conf.d/apprtc-https-proxy.conf
upstream roomserver {
server localhost:8090;
}
server {
listen 443 ssl;
ssl_certificate /root/cert/cert.pem;
ssl_certificate_key /root/cert/key.pem;
charset utf-8;
# ip地址或者域名
server_name localhost;
location / {
# 转向代理的地址
proxy_pass http://roomserver$request_uri;
proxy_set_header Host $host;
}
}
编辑nginx.conf文件,在末尾}之前添加包含文件
include /usr/local/nginx/conf/conf.d/*.conf;
}
ws是不安全的连接 类似http
wss是安全的连接,类似https
完整配置文件:/usr/local/nginx/conf/conf.d/apprtc-websocket-proxy.conf
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
upstream websocket {
server localhost:8089;
}
server {
listen 8088 ssl;
ssl_certificate /root/cert/cert.pem;
ssl_certificate_key /root/cert/key.pem;
server_name localhost;
location /ws {
proxy_pass http://websocket;
proxy_http_version 1.1;
proxy_connect_timeout 4s; #配置点1
proxy_read_timeout 6000s; #配置点2,如果没效,可以考虑这个时间配置长一点
proxy_send_timeout 6000s; #配置点3
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}
重启nginx加载配置文件(/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
)。
浏览器通话跨域问题 :pushState
Messages:Failed to start signaling: Failed to execute ‘pushState’ on ‘History’
解决方法:
修改文件apprtc/src/web_app/js/appcontroller.js
vim /home/lqf/webrtc/apprtc/src/web_app/js/appcontroller.js
#搜索 displaySharingInfo_ 大概是445行displaySharingInfo_函数第一行添加
roomLink=roomLink.replace("http","https");
最终结果(大概443行):
AppController.prototype.displaySharingInfo_ = function(roomId, roomLink) {
roomLink=roomLink.replace("http","https");
this.roomLinkHref_.href = roomLink;
this.roomLinkHref_.text = roomLink;
this.roomLink_ = roomLink;
this.pushCallNavigation_(roomId, roomLink);
this.activate_(this.sharingDiv_);
};
然后重新build:
cd ~/webrtc/apprtc
grunt build
重新运行:
/root/webrtc/google_appengine/dev_appserver.py --host=0.0.0.0 --port=8090 /root/webrtc/apprtc/out/app_engine --skip_sdk_update_check
docker运行这里我们可以有三种方式:
我们这里由于只做初步测试将使用第一种和第二种方式。
实际上我们也可以通过Dockerfile方式构建镜像,但是上述依赖太复杂了,写Dockerfile比较麻烦,我通常喜欢拉取一个基础镜像,然后bash进入后安装依赖,之后导出再导入形成一个新镜像(后续可以尝试第三种方式并根据不同的服务形成四个Dockerfile,这样更符合docker的设计目的,尽可能的将可复用的内容都单独容器化)。
#docker export -o 指定路径及镜像包名 容器名
docker export -o .\ubuntu-apprtc.tar naughty_banzai
#docker import 包名及路径 镜像名:版本tag
docker import ubuntu-apprtc.tar ubuntu-apprtc:1.0.1
#查看镜像
docker images
#登录
docker login
#添加tag:docker tag image1:tag 用户名/image1:tag版本
docker tag ubuntu-apprtc:1.0.1 19930128/ubuntu-apprtc:1.0.1
#docker push 用户名/image1:latest
docker push 19930128/ubuntu-apprtc:1.0.1
#之后可以利用pull在其它服务器上拉取现成的镜像修改运行容器了
docker hub上传失败的话可以选择国内的免费仓库,比如阿里云的,请自行搜索如何上传阿里云仓库
阿里云镜像上传参考地址:https://docs.docker.com/
docker run -t -i -p 8090:8090 ubuntu-apprtc:1.0.1 /root/webrtc/google_appengine/dev_appserver.py --host=0.0.0.0 --port=8090 /root/webrtc/apprtc/out/app_engine --skip_sdk_update_check
docker run -t -i -p 8089:8089 ubuntu-apprtc:1.0.1 /root/webrtc/goworkspace/bin/collidermain -port=8089 -tls=false -room-server="http://localhost:8090"
docker run -p 3478:3478 -t -i -d ubuntu-apprtc:1.0.1 turnserver -L 0.0.0.0 -a -u admin:admin
使用三个容器运行三个服务并将端口映射出来,之后在外部使用nginx做代理进行https\wss的代理即可。
我们也可以将coturn打洞中继服务默认运行在镜像内,或者直接使用一些免费的stun/turn服务;之后信令服务和nginx代理在容器内直接以服务运行,docker外部只暴露web服务启动即可(或者使用bash -c将上述三条命令放到一起启动一个容器即可)。
如果你是按照外部运行nginx,其它的服务运行在docker容器中的话可以直接使用我这里的镜像,修改其中的 **vim /root/webrtc/apprtc/src/app_engine/constants.py**
修改其中localhost的部分,修改为你的公网ip,修改后参考上面的编译运行方式重新运行启动(因为没有对turn进行配置,所以域名可能也不行,需要单独做域名配置)
测试Google浏览器443端口在Windows、MacOS、Android下是可用的(直接微信页面打开也可以),免费的ssl证书会报警告,不用理会。
目前我会在阿里云服务器上保留一段时间用于测试,各位i也可以测试一下:https://www.xiaoyaoyou.xyz:443
MacOS:
Windows(微信直接打开链接也可以,Google浏览器打开也可以):
Android和ios使用浏览器都报了错误,房间链接失败了,看着似乎是信令的问题,后面我们分析源码的时候再来试一下(而且退出房间后似乎ws/wss链接断开不及时,退出后再次使用一个房间号很可能会提升房间已满)。
docker的好处就在这里了,一次部署,只要在支持docker的环境再次部署就容易多了,再也不同担心部署过程中的环境依赖问题了,这让运维实施和开发都少了头疼的一件事(在我环境上是好的.)。