1、记录AppRTC的搭建过程,实现iOS、安卓、browser同异设备的视频通信 2、以下直接以root身份进行操作,所有的需要下载的文件均放置于/root
目录下,需要的话,可以自行决定存放位置,但是要注意修改相关的配置路径~
一、设备配置
阿里云ESC服务器 Ubuntu 16.04 64位
腾讯云域名
二、相关环境 1、JDK 1 2 3 add-apt-repository ppa:openjdk-r/ppa apt-get update apt-get install openjdk-8-jdk
2、nodejs nodejs官网 1 2 3 // 这里的版本8.x可以按自己的需求去修改 curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash - apt-get install -y nodejs
至此已经安装了最新版的nodejs和npm了,可以使用-v
来查看当前版本
安装grunt-cli
,后面需要grunt
来构建房间服务器
1 npm -g install grunt-cli
3、python与python-webtest 1 2 apt-get install python apt-get install python-webtest
libevent 1 2 apt-cache search libevent apt-get install libevent-dev
三、Room Server 房间服务器 1 2 3 git clone https://github.com/webrtc/apprtc.git cd apprtc npm install
1、配置 constants.py 1 2 # 当前目录 -- apprtc vim src/app_engine/constants.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 TURN_BASE_URL = 'https://linxunfeng.top' TURN_URL_TEMPLATE = '%s/turn.php?username=%s&key=%s' CEOD_KEY = 'lxf' ICE_SERVER_BASE_URL = 'https://linxunfeng.top' ICE_SERVER_URL_TEMPLATE = '%s/iceconfig.php?key=%s' ICE_SERVER_API_KEY = os.environ.get('ICE_SERVER_API_KEY' ) WSS_INSTANCE_HOST_KEY = 'linxunfeng.top:8089' WSS_INSTANCE_NAME_KEY = 'vm_name' WSS_INSTANCE_ZONE_KEY = 'zone' WSS_INSTANCES = [{ WSS_INSTANCE_HOST_KEY: 'linxunfeng.top:8089' , WSS_INSTANCE_NAME_KEY: 'wsserver-std' , WSS_INSTANCE_ZONE_KEY: 'us-central1-a' }, { WSS_INSTANCE_HOST_KEY: 'linxunfeng.top:8089' , WSS_INSTANCE_NAME_KEY: 'wsserver-std-2' , WSS_INSTANCE_ZONE_KEY: 'us-central1-f' }]
2、编译 1 2 # 当前目录 -- apprtc grunt build
编译好后apprtc
目录下就会多出一个名为out
的目录,里面存放的就是编译好的room server
3、GoogleAppEngine的安装与配置 官网:GoogleAppEngine
可以在此路径找最新版本
GoogleAppEngine -> Python -> Download and install the original App Engine SDK for Python.
目前最新版本为:google_appengine_1.9.70.zip
1 wget https://storage.googleapis.com/appengine-sdks/featured/google_appengine_1.9.70.zip
1 unzip google_appengine_1.9.70.zip
1 export PATH="$PATH:/root/google_appengine/"
4、开启 Room Server
1 2 # 当前路径 -- /root/google_appengine ./dev_appserver.py --host=linxunfeng.top ../apprtc/out/app_engine/
如果你使用的是阿里云服务器,这里就不能用域名linxunfeng.top
来启动room server
,而是使用本地网卡地址 ,否则就会提示
raise BindError(‘Unable to bind %s:%s’ % self.bind_addr) google.appengine.tools.devappserver2.wsgi_server.BindError: Unable to bind linxunfeng.top:8080
1 2 3 4 root@xxx:~/google_appengine# ifconfig eth0 Link encap:Ethernet HWaddr 00:16:3e:08:b4:02 inet addr:172.18.141.108 Bcast:172.18.143.255 Mask:255.255.240.0 ...
1 ./dev_appserver.py --host=172.18.141.108 ../apprtc/out/app_engine/
这样就好了吗?不,虽然没有报错,但是你用浏览器打开你的域名看看… 这里直接给出最终命令,具体原因看 疑难杂症 - 1
1 2 3 4 ./dev_appserver.py --enable_host_checking=false --host=172.18.141.108 ../apprtc/out/app_engine/ # 如果想直接后台运行,则使用如下命令 nohup ./dev_appserver.py --enable_host_checking=false --host=172.18.141.108 ../apprtc/out/app_engine/ &
四、Collider Server 信令服务器 1、拷贝collider源码 1 2 # 当前路径 -- /root mkdir -p goWorkspace/src
把apprtc/src/collider/
目录下的三个目录(collider、collidermain、collidertest)复制到goWorkspace/src/
目录下
1 cp -rf apprtc/src/collider/* /goWorkspace/src
2、修改代码
1 2 # goWorkspace/src/collidermain/main.go var roomSrv = flag.String("room-server", "https://域名", "The origin of the room server")
1 2 3 4 5 6 # goWorkspace/src/collider/collider.go e = server.ListenAndServeTLS("/etc/letsencrypt/live/域名/fullchain.pem", "/etc/letsencrypt/live/域名/privkey.pem") # 如: e = server.ListenAndServeTLS("/etc/letsencrypt/live/linxunfeng.top/fullchain.pem", "/etc/letsencrypt/live/linxunfeng.top/privkey.pem")
相关的SSL证书fullchain.pem
和privkey.pem
在后面的nginx配置中会提到,这里先写上
3、安装与配置环境
FQ 到 GO官网 上下载最新版本
当前最新版本:go1.10.2.linux-amd64.tar.gz
1 2 # 当前路径 -- /root wget https://dl.google.com/go/go1.10.2.linux-amd64.tar.gz
打开profile
后添加如下内容
1 2 3 export GOROOT=/root/go export GOPATH=/root/goWorkspace export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
4、编译 进入目录 goWorkspace/src/
1 2 go get collidermain go install collidermain
这里的编译过程需要翻墙,如果无法翻墙,请看以下内容,如果可以则直接跳至第5小点
1 2 3 4 # 当前路径 -- /root/goWorkspace/src mkdir -p golang.org/x cd golang.org/x/ git clone https://github.com/golang/net
git clone
成功后再执行上面的两行编译命令
5、开启 Collider Server 进入goWorkspace
下的bin
目录,执行命令
1 2 3 4 5 6 # 当前路径 -- /root/goWorkspace/bin # -tls=true : 指需要数字证书 ./collidermain -port=8089 -tls=true # 如果想直接后台运行,则使用如下命令 nohup ./collidermain -port=8089 -tls=true &
五、STUN\TURN服务器 1、安装coturn
2、修改配置
把TURNSERVER_ENABLED=1的注释去掉
1 vim /etc/turnserver.conf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 listening-device=eth0 #此处eth0是电脑网卡名称 listening-port=3478 #turn服务器的端口号 relay-device=eth0 #此处eth0是电脑网卡名称 min-port=49152 max-port=65535 Verbose fingerprint lt-cred-mech use-auth-secret static-auth-secret=lxf #此处要和房间服务器配置时constants.py文件中的CODE_KEY保持一致。 user=lxf:0x8638170519dd1309044bca55319ff929 user=lxf:lxf stale-nonce cert=/usr/local/etc/turn_server_cert.pem pkey=/usr/local/etc/turn_server_pkey.pem no-loopback-peers no-multicast-peers mobility no-cli
上述文件中 0x8638170519dd1309044bca55319ff929:
turnadmin -k -u lxf -r north.gov -p lxf
-k 表示生成一个long-term credential key -u 表示用户名 -p 表示密码 -r 表示Realm域
coturn的证书生成(即配置文件中cert和pkey)
1 openssl req -x509 -newkey rsa:2048 -keyout /usr/local/etc/turn_server_pkey.pem -out /usr/local/etc/turn_server_cert.pem -days 99999 -nodes
3、启动coturn服务器
用浏览器打开
显示如下内容则说明成功开启服务
TURN Server use https connection for the admin session
六、配置nginx服务器 1、生成SSL证书 这里使用let’s encrypt颁发的免费SSL证书 certbot.eff.org
1 2 3 4 5 sudo apt-get update apt-get install software-properties-common add-apt-repository ppa:certbot/certbot apt-get update apt-get install python-certbot-nginx
1 certbot --nginx certonly
SSL证书生成的目录为:/etc/letsencrypt/live/域名/
,里面存放着四个文件cert.pem,chain.pem,fullchain.pem,privkey.pem
2、安装php和php-fpm
1 2 apt-get install php apt-get install php7.0-fpm
3、安装与配置nginx
1 vim /etc/nginx/sites-available/default
将下面的linxunfeng.top
修改为你自己的域名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 upstream roomserver { server linxunfeng.top:8080; } server { listen 80 ; server_name linxunfeng.top; return 301 https://$server_name$request_uri; } server { #listen 80 default_server; #listen [::]:80 default_server; # SSL configuration # # listen 443 ssl default_server; # listen [::]:443 ssl default_server; listen 443; # # Note: You should disable gzip for SSL traffic. # See: https://bugs.debian.org/773332 # # Read up on ssl_ciphers to ensure a secure configuration. # See: https://bugs.debian.org/765782 # # Self signed certs generated by the ssl-cert package # Don't use them in a production server! # # include snippets/snakeoil.conf; root /var/www/html; # Add index.php to the list if you are using PHP index index.html index.htm index.nginx-debian.html; server_name linxunfeng.top; # 添加域名,如不添加,生成SSL证书时可能会有问题 #location / { # First attempt to serve request as file, then # as directory, then fall back to displaying a 404. # try_files $uri $uri/ =404; #} # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # location ~ \.php$ { include snippets/fastcgi-php.conf; # # # With php7.0-cgi alone: # fastcgi_pass 127.0.0.1:9000; # # With php7.0-fpm: fastcgi_pass unix:/run/php/php7.0-fpm.sock; } location / { proxy_pass http://roomserver$request_uri; proxy_set_header Host $host; } ssl on; ssl_certificate /etc/letsencrypt/live/linxunfeng.top/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/linxunfeng.top/privkey.pem; # deny access to .htaccess files, if Apache's document root # concurs with nginx's one # #location ~ /\.ht { # deny all; #} }
turn.php 与 iceconfig.php 在/var/www/html/
目录下创建两个文件
1 touch turn.php iceconfig.php
turn.php 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 <?php $request_username = $_GET["username" ]; if (empty ($request_username)) { echo "username == null" ; exit ; } $request_key = $_GET["key" ]; $time_to_live = 600 ; $timestamp = time() + $time_to_live; $response_username = $timestamp.":" .$_GET["username" ]; $response_key = $request_key; if (empty ($response_key)) { $response_key = "code_key" ; } $response_password = getSignature($response_username, $response_key); $jsonObj = new Response(); $jsonObj->username = $response_username; $jsonObj->password = $response_password; $jsonObj->ttl = 86400 ; $jsonObj->uris= array ("stun:linxunfeng.top:3478" ,"turn:linxunfeng.top:3478?transport=udp" ,"turn:linxunfeng.top?transport=tcp" ); echo json_encode($jsonObj); function getSignature ($str, $key) { $signature = "" ; if (function_exists('hash_hmac' )) { $signature = base64_encode(hash_hmac("sha1" , $str, $key, true )); } else { $blocksize = 64 ; $hashfunc = 'sha1' ; if (strlen($key) > $blocksize) { $key = pack('H*' , $hashfunc($key)); } $key = str_pad($key, $blocksize, chr(0x00 )); $ipad = str_repeat(chr(0x36 ), $blocksize); $opad = str_repeat(chr(0x5c ), $blocksize); $hmac = pack( 'H*' , $hashfunc( ($key ^ $opad) . pack( 'H*' , $hashfunc( ($key ^ $ipad) . $str ) ) ) ); $signature = base64_encode($hmac); } return $signature; } class Response { public $username = "" ; public $password = "" ; public $ttl = "" ; public $uris = array ("" ); } ?>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 <?php $request_username = "lxf" ; if (empty ($request_username)) { echo "username == null" ; exit ; } $request_key = "lxf" ; $time_to_live = 600 ; $timestamp = time() + $time_to_live; $response_username = $timestamp.":" .$_GET["username" ]; $response_key = $request_key; if (empty ($response_key)) { $response_key = "CEOD_KEY" ; } $response_password = getSignature($response_username, $response_key); $arrayObj = array (); $arrayObj[0 ]['username' ] = $response_username; $arrayObj[0 ]['credential' ] = $response_password; $arrayObj[0 ]['urls' ][0 ] = "stun:linxunfeng.top:3478" ; $arrayObj[0 ]['urls' ][1 ] = "turn:linxunfeng.top:3478?transport=tcp" ; $arrayObj[0 ]['uris' ][0 ] = "stun:linxunfeng.top:3478" ; $arrayObj[0 ]['uris' ][1 ] = "turn:linxunfeng.top:3478?transport=tcp" ; $jsonObj = new Response(); $jsonObj->lifetimeDuration = "300.000s" ; $jsonObj->iceServers = $arrayObj; echo json_encode($jsonObj); function getSignature ($str, $key) { $signature = "" ; if (function_exists('hash_hmac' )) { $signature = base64_encode(hash_hmac("sha1" , $str, $key, true )); } else { $blocksize = 64 ; $hashfunc = 'sha1' ; if (strlen($key) > $blocksize) { $key = pack('H*' , $hashfunc($key)); } $key = str_pad($key, $blocksize, chr(0x00 )); $ipad = str_repeat(chr(0x36 ), $blocksize); $opad = str_repeat(chr(0x5c ), $blocksize); $hmac = pack( 'H*' , $hashfunc( ($key ^ $opad) . pack( 'H*' , $hashfunc( ($key ^ $ipad) . $str ) ) ) ); $signature = base64_encode($hmac); } return $signature; } class Response { public $username = "" ; public $password = "" ; public $ttl = "" ; public $uris = array ("" ); } ?>
1 2 service nginx restart service php7.0-fpm restart
七、各端运行测试 1、Browser 浏览器直接打开即可,如果不能访问摄像头之类的,请查看疑难杂症 - 2
2、Android webrtc-android-demo-apprtc
直接安装手机,打开软件,点击右上角的扳手跳转至设置界面,滚到界面最下方,找到Room server URL.
,将其修改为刚刚搭建好的服务器域名即可,如
3、iOS GitHub - ISBX/apprtc-ios
打开AppRTC
项目,分别修改以下两个文件
ARTCVideoChatViewController.m
1 #define SERVER_HOST_URL @"https://linxunfeng.top"
Pods -> Development Pods -> AppRTC -> Lib -> ARDAppClient.m
1 2 3 4 5 6 7 static NSString *kARDRoomServerHostUrl = @"https://linxunfeng.top" ; static NSString *kARDDefaultSTUNServerUrl = @"stun:linxunfeng.top:3478" ; static NSString *kARDTurnRequestUrl = @"https://linxunfeng.top" @"/turn?username=lxf&key=lxf" ;
1 2 3 4 5 6 7 - (RTCICEServer *)defaultSTUNServer { NSURL *defaultSTUNServerURL = [NSURL URLWithString:kARDDefaultSTUNServerUrl]; return [[RTCICEServer alloc] initWithURI:defaultSTUNServerURL username:@"lxf" password:@"lxf" ]; }
疑难杂症 1、Request host is not whitelist enabled
具体提示 Request host is not whitelist enabled for this server. Please use the –host command-line flag to whitelist a specific host (recommended) or use –enable_host_checking to disable host checking. See the command-line flags help text for more information.
参考链接
执行dev_appserver.py时,加上如下参数1 --enable_host_checking=false
完整指令1 ./dev_appserver.py --enable_host_checking=false --host=172.18.141.108 ../apprtc/out/app_engine/
2、浏览器无法访问摄像头 浏览器访问设备的摄像头是需要使用https
链接或者localhost
来访问
3、端口 阿里云等国内大厂提供的服务器基本上都有一个叫安全组的玩意儿,我们搭建AppRTC服务器的所有服务所需的端口均要添加至安全组
4、Failed to execute ‘pushState’ on ‘History’
Failed to start signaling: Failed to execute ‘pushState’ on ‘History’: A history state object with URL ‘http://linxunfeng.top/r/598600855’ cannot be created in a document with origin ‘https://linxunfeng.top’ and URL ‘https://linxunfeng.top/
解决方法有两种
房间服务器编译完成后,在/root/apprtc/out/app_engine/js/apprtc.debug.js文件中找到window.history.pushState({‘roomId’: roomId, ‘roomLink’: roomLink}, roomId, roomLink),把这句话注释掉,重新运行即可。(如果重新编译,需要重新修改)
在/root/apprtc/src/web_app/js/appcontroller.js文件中找到window.history.pushState({‘roomId’: roomId, ‘roomLink’: roomLink}, roomId, roomLink),把这句话注释掉,然后重新编译,重新运行房间服务器即可。