前言
第一次写博客,有点小鸡冻~
之前领导要求研究一下webrtc,在Android和web间的使用。在网上找了demo,准备边看边学,并在此记录下来。新手上路,有不对的地方恳请各位大佬指教!
项目地址
ProjectRTC & AndroidRTC是webRTC的一个开源实现,利用webRTC实现了Android端与web端、web端与web端之间的RTC通话功能。
AndroidRTC:https://github.com/pchab/AndroidRTC
ProjectRTC: https://github.com/pchab/ProjectRTC
项目运行
ProjectRTC
1.ProjectRTC运行在node服务器上,所以首先要安装node环境(之前已经装过node,且网上教程非常多,这里不记录了。npm貌似在安装node的时候会一起装上)。
2.打开terminal、打开ProjectRTC所在文件夹
1 | cd /Users/xxx/Desktop/ProjectRTC-master |
3.安装模块(只有第一次需要安装,之后直接跳过这步)
1 | npm install |
4.开启服务器
1 | npm start |
5.测试是否成功开启
在Chrome中打开下面的网址
1 2 3 | 1.http://localhost:3000/ 2.http://127.0.0.1:3000/ 3.http://10.53.60.175:3000/(这里是本机IP地址,需要修改为自己的) |
结果出现了这样的画面
看来并没有正常运行,右键选择检查->选择console
6.可以看到是angular.min.js文件出现问题,通过检索,发现 views/index.ejs 第12行有https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js,猜测是无法连接Google导致,然后在网上找到1.3.15版本的angular.min.js文件保存到本地,放入/public/javascripts/,然后修改index.ejs第12行的src为
1 | src="javascripts/angular.min.js" |
7.重新打开localhost:3000->点击start->浏览器申请摄像头权限-> 出现自己的大脸->点击stop出现链接(感觉好像是个bug?)->打开新的浏览器窗口并输入复制的网址->看到两张自己的大脸
8.慢着!在第7步的时候,如果打开的不是 localhost:3000,而是IP:3000,会很尴尬地发现自己的大脸并没有出现,而是会
(不知道别人会不会,反正我这样了)
Browser does not appear to be WebRTC-capable
毕竟用手机连的话还是得用IP地址,所以这个问题不能不解决。
网上看到webrtc必须用https,猜想或许是这个问题。
于是使用OpenSSL自签名生成服务器的加密证书 (免费,如果有自己的域名,也可以去申请SSL证书并下载,这里只为把服务器配置为https访问方式,并不真的验证,因此随便用啥都行,网上有很多OpenSSL生成证书的教程,后面有时间再补充进来)
得到服务器端的证书文件server.crt和server.key,新建一个文件夹ssl并将证书文件放入,然后将文件夹放入项目根目录下
在app.js中加入SSL相关的代码,下面是修改后的完整代码
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 | /** * Module dependencies. */ var express = require('express') , path = require('path') , streams = require('./app/streams.js')(); var favicon = require('serve-favicon') , logger = require('morgan') , methodOverride = require('method-override') , bodyParser = require('body-parser') , errorHandler = require('errorhandler'); var https = require("https"); //1. var fs = require("fs"); //2. var app = express(); // all environments app.set('port', process.env.PORT || 3000); app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'ejs'); app.use(favicon(__dirname + '/public/images/favicon.ico')); app.use(logger('dev')); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); app.use(methodOverride()); app.use(express.static(path.join(__dirname, 'public'))); const httpsOption = { //3. key : fs.readFileSync("ssl/server.key"), cert: fs.readFileSync("ssl/server.crt") } // development only if ('development' == app.get('env')) { app.use(errorHandler()); } // routing require('./app/routes.js')(app, streams); // var server = app.listen(app.get('port'), function(){ 4.// // console.log('Express server listening on port ' + app.get('port')); // }); var Httpsserver = https.createServer(httpsOption, app).listen(app.get('port'), function(){ //5. console.log('Express server listening on port ' + app.get('port')); }); // var io = require('socket.io').listen(server); //6. var io = require('socket.io').listen(Httpsserver); //7. /** * Socket.io event handling */ require('./app/socketHandler.js')(io, streams); |
重启服务器:
1 | npm restart |
Chrome中打开https://IP地址:3000(注意是https),成功!
此时用localhost和127.0.0.1打开也是用https(提示您的连接不是私密连接,选择高级->继续前往),成功!
AndroidRTC
1.res/values/strings.xml 中修改host为自己的服务器IP
2.java/fr/pchab/androidrtc/RtcActivity.java 中第65行修改http为https
3.build.gradle(Module:webrtc-client) 中修改socket.io的版本
1 2 3 4 5 6 7 8 | dependencies { //implementation 'com.github.nkzawa:socket.io-client:0.4.2' api 'io.pristine:libjingle:9127@aar' api ('io.socket:socket.io-client:1.0.0') { // excluding org.json which is provided by Android exclude group: 'org.json', module: 'json' } } |
4.webrtc-client/src/main/java/fr/pchab/webrtcclient/WebRtcClient.java 中增加SSL验证代码(这里是信任所有证书,只增加了一个过程,并没有实际去校验证书)
修改import
1 2 3 4 5 6 7 | //import com.github.nkzawa.socketio.client.IO; //import com.github.nkzawa.socketio.client.Socket; import io.socket.client.IO; import io.socket.client.Socket; import io.socket.emitter.Emitter; import okhttp3.OkHttpClient; import java.security.NoSuchAlgorithmException; |
修改webrtcclient(miTM为新增函数,WebRtcClient是在原先基础上修改过后的)
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 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | static class miTM implements TrustManager,X509TrustManager { public X509Certificate[] getAcceptedIssuers() { return null; } public boolean isServerTrusted(X509Certificate[] certs) { return true; } public boolean isClientTrusted(X509Certificate[] certs) { return true; } public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException { return; } public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException { return; } } public WebRtcClient(RtcListener listener, String host, PeerConnectionParameters params, EGLContext mEGLcontext) { mListener = listener; pcParams = params; PeerConnectionFactory.initializeAndroidGlobals(listener, true, true, params.videoCodecHwAcceleration, mEGLcontext); factory = new PeerConnectionFactory(); MessageHandler messageHandler = new MessageHandler(); try { TrustManager[] trustAllCerts = new TrustManager[1]; TrustManager tm = new miTM(); trustAllCerts[0] = tm; SSLContext sc = SSLContext.getInstance("SSL"); X509TrustManager x509m = new X509TrustManager() { // 返回受信任的X509证书数组。 @Override public X509Certificate[] getAcceptedIssuers() { return new java.security.cert.X509Certificate[] {}; } // 该方法检查服务器的证书,若不信任该证书同样抛出异常。通过自己实现该方法,可以使之信任我们指定的任何证书。 // 在实现该方法时,也可以简单的不做任何处理,即一个空的函数体,由于不会抛出异常,它就会信任任何证书。 @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } // 该方法检查客户端的证书,若不信任该证书则抛出异常。由于我们不需要对客户端进行认证, // 因此我们只需要执行默认的信任管理器的这个方法。JSSE中,默认的信任管理器类为TrustManager。 @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } }; try { sc.init(null, trustAllCerts, null); } catch ( KeyManagementException e ) { e.printStackTrace(); } OkHttpClient okHttpClient = new OkHttpClient.Builder() .hostnameVerifier(new HostnameVerifier() { @Override public boolean verify( String s, SSLSession sslSession ) { return true; } }) .sslSocketFactory(sc.getSocketFactory(), x509m) .build(); IO.Options opts = new IO.Options(); opts.callFactory = okHttpClient; opts.webSocketFactory = okHttpClient; // default settings for all sockets IO.setDefaultOkHttpWebSocketFactory(okHttpClient); IO.setDefaultOkHttpCallFactory(okHttpClient); // set as an option client = IO.socket(host,opts); } catch (URISyntaxException | NoSuchAlgorithmException e) { e.printStackTrace(); } client.on("id", messageHandler.onId); client.on("message", messageHandler.onMessage); client.connect(); iceServers.add(new PeerConnection.IceServer("stun:23.21.150.121")); iceServers.add(new PeerConnection.IceServer("stun:stun.l.google.com:19302")); pcConstraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true")); pcConstraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveVideo", "true")); pcConstraints.optional.add(new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true")); } |
5.重新编译运行。(注意服务器必须处于开启状态)
6.弹窗中出现的连接复制下来,在浏览器中打开。完成!(Android并没有获取到视频,好像是因为模拟器不支持调用摄像头,在真机上是可以正常跑的。)
总结
这个demo时间比较早,webrtc的新版本要用到https,因此需要增加ssl证书,Android也需要验证。Android端也没有错误提示,因此在整个连接过程中有一个环节出错,在App跑的时候就不会出现弹窗。