licode安装过程及发布局域网服务

您所在的位置:网站首页 licode设置码率 licode安装过程及发布局域网服务

licode安装过程及发布局域网服务

2024-02-27 03:23| 来源: 网络整理| 查看: 265

为什么80%的码农都做不了架构师?>>>   hot3.png

系统说明:

本系统是基于谷歌的webRTC浏览器端视频点对点技术实现网页视频及语音即时通讯。采用开源项目licode作为聊天室管理,以及其web端js模块。

    目前系统采用前后端分离以及多服务器方式部署,采用nginx作为反向代理集成licode、apiserver、front。

    环境要求:

操作系统:ubuntu14、nginx、nodejs、RibbitMQ

Licode安装

在ubuntu中安装:

先从GitHub上下载源码

git clone https://github.com/ging/licode.gitcd licode

解决HTTPS/SSL访问错误:

由于是配置文件名不统一导致,而且在Sokect.js中的token.secure参数无法配置,故直接修改。

/licode/erizo_controller/erizoClient/src/Socket.js

167ccb46d10983a9ec6b8e12c3e50c6588d.jpg

将licode_default.js文件修改SSL相关配置,注意ssl证书也需要修改

ssl相关设置为true;

然后复制licode_default.js为licode_config.js 

 

获得源码后进入licode/scripts/目录,复制licode_default.js为licode_config.js,同时将licode_default.js和licode_config.js都修改为如下配置:

// 配置turnserver服务器

config.erizoController.iceServers = [{'url': 'stun:stun.l.google.com:19302'},{'url': 'stun:服务器IP:3478'}];//注意,配置的服务器必须是可访问的,否则启动失败

// 开启 SSL

config.erizoController.ssl = true;

config.erizoController.listen_ssl = true; //default value: false

config.erizoController.listen_port = 8080; //default value: 8080

 

// 配置SSL 文件

config.erizoController.ssl_key = '/full/path/to/ssl.key';

config.erizoController.ssl_cert = '/full/path/to/ssl.crt';

 

 

依次序执行安装:

1、安装系统依赖

./scripts/installUbuntuDeps.sh

 

2、安装Erizo和Nuve

./scripts/installErizo.sh./scripts/installNuve.sh

3、安装基本实例程序

./scripts/installBasicExample.sh

4、运行licode

./licode/scripts/initLicode.sh

5、运行基础实例web服务

./scripts/initBasicExample.sh

 

Licode API

官方地址:

http://licode.readthedocs.io/en/master/client_api/

包含 客户端API 、服务端API

具体详见官网。

 

 

 

turnserver安装

在使用WebRTC进行即时通讯时,需要使浏览器进行P2P通讯,但是由于NAT环境的复杂性,并不是所有情况下都能进行P2P,这时需要TURN Server来帮助客户端之间转发数据。rfc5766-turn-server是一个高性能的开源TURN Server实现。

 

以下是在EC2上使用Ubuntu操作系统安装rfc5766-turn-server:

1. 下载安装包:

$ wget http://ftp.cn.debian.org/debian/pool/main/r/rfc5766-turn-server/rfc5766-turn-server_3.2.4.4-1_amd64.deb

2. 安装:

$ sudo apt-get update

$ sudo apt-get install gdebi-core

$ sudo gdebi rfc5766-turn-server_3.2.4.4-1_amd64.deb

安装完后,在/usr/share/doc/rfc5766-turn-server下有很多文档可参考。

3. 配置:

$ sudo vi /etc/turnserver.conf

---------------------------------------

// 配置IP,EC2下需要配置listening-ip和external-ip

listening-ip=172.31.4.37 注意:如果是未知IP,可设置0.0.0.0

external-ip=54.223.149.60

// 当TURN Server用于WebRTC时,必须使用long-term credential mechanism

lt-cred-mech

// 增加一个用户

user=username1:password1

// 设定realm

realm=mycompany.org

---------------------------------------

 

4. 启动:

sudo turnserver -c /etc/turnserver.conf --daemon

 

5. 服务启动后,在上一个WebRTC示例中更改iceServers后测试:

"iceServers": [{

    "url": "stun:stun.l.google.com:19302"

}, {

    "url": "turn:54.223.149.60",

    "username": "username1",

    "credential": "password1"

}]

 

更多安装信息在:http://turnserver.open-sys.org/downloads/v3.2.4.4/INSTALL

 

rfc5766-turn-server当然也有STUN Server的能力,但是需要给它配置2个IP,以帮助探测客户端所在NAT环境的行为,这里没有做。

 

 

前端集成 时序图

WebRTC相关时序:

4d5cefe6edd278cd88d2e29ead38a363b22.jpg

    浏览器通过candidate 来进行连接。连接之后,就无需与服务器进行连接。

在获得candidate时需要连接turnserver。具体是由浏览器webRTC模块执行。

 

Licode集成时序图:

b2bf591839b070f04494726c3e46ba323ad.jpg

 

 

Nginx反向代理配置

运行./scripts/initLicode.sh 和./scripts/initBasicExample.sh

Licode服务器提供

http端口 3000 3001 websocket 端口8080

https 端口 3004 websocket 端口 8080

nginx配置如下:

前端程序nginx配置:

server {

        listen       443 ssl;

        ssl_certificate      /usr/local/etc/nginx/server.crt;

        ssl_certificate_key  /usr/local/etc/nginx/server.key;

 

    #    ssl_session_cache    shared:SSL:1m;

        ssl_session_timeout  5m;

 

    #    ssl_ciphers  HIGH:!aNULL:!MD5;

    #    ssl_prefer_server_ciphers  on;

        server_name '192.168.10.225';

        if ($scheme = http) {

            return 301 https://$server_name$request_uri;

        }

        location / {

            root   html; #前端静态运行目录

            index  index.html index.htm;

            proxy_redirect off;

            proxy_set_header Host $host;

            proxy_set_header X-Real-IP $remote_addr;

            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

            proxy_pass http://127.0.0.1:8085/;

        }

    location /api{ #接口服务

        proxy_redirect off;

            proxy_set_header Host $host;

            proxy_set_header X-Real-IP $remote_addr;

            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

            proxy_pass http://127.0.0.1:8080/api;

    }

       #文件上传后存放目录

        location /upload{

            proxy_redirect off;

            proxy_set_header Host $host;

            proxy_set_header X-Real-IP $remote_addr;

            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

            proxy_pass http://127.0.0.1:8080/upload;

        }

#licode 创建token路径转发

        location /createToken{

            proxy_redirect off;

            proxy_set_header Host $host;

            proxy_set_header X-Real-IP $remote_addr;

            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

            proxy_pass http://192.168.10.228:3001/createToken;

        }

    }

 

 

 

 

代码示例

Websock服务器:

io.on('connect', function (socket) {     socket.on('message', function (data) {         var data = JSON.parse(data);         console.log("请求数据", data);         //当前登录用户名         var userId = data.from.id;         //当前用户的聊天室id         var roomId = data.roomId;         socket.userId = userId;         socket.roomId = roomId;         function join(f) {             //当用户与聊天室id存在时             if (allUsers[roomId] == undefined) {                 allUsers[roomId] = {};             }             if (allSockets[roomId] == undefined) {                 allSockets[roomId] = {};             }             //保存用户信息             allUsers[roomId][userId] = data.from;             if (allSockets[roomId][userId] != undefined && allSockets[roomId][userId].scoket != undefined && f == true) {//给之前的连接发送退出信息                 sendTo(allSockets[roomId][userId].scoket,{                     event: "leave"                 });             }             allSockets[roomId][userId] = {'scoket': socket};         }         try {             switch (data.event) {                 case "heartbeat":                     join(false);                     break;                 //当有新用户加入时                 case "join":                     join(true);                     showUserInfo(allUsers[roomId]);                     sendTo(socket, {                         event: "join",                         "message": "成功加入聊天室",                         "success": true                     });                     break;                 //======== 即时通讯 IM  =========                 case "leave":                     delete allUsers[roomId][userId];                     showUserInfo(allUsers[roomId]);                     break;                 case "msg"://IM消息                     join();                     if (data.message == undefined || data.message == '' || data.message == null) {                         break;                     }                     //获取room用户                     var roomId = data.roomId;                     var ulist = allUsers[roomId];                     data.datetime = new Date();                     if (ulist != undefined) {                         //查找用户连接发送信息                         for (var u in ulist) {                             if (ulist[u])                                 sendTo(allSockets[roomId][u].scoket, data);                         }                     }                     break;                 case "historyMsg"://聊天历史                     join();                     //获取room用户                     var roomId = data.roomId;                     var ulist = allUsers[roomId];                     break                 //======== 基于allWebsockets start =========                 case "sigin"://登录系统                     for (var ws in allWebsockets) {                         if (allWebsockets[ws])                             sendTo(allWebsockets[ws], {                                 event: "sigin",                                 "message": "登录",                                 "success": true,                                 from: data.from                             });                     }                     socket.mainId=data.from.id;                     socket.userId=data.from.id;                     allWebsockets[data.from.id] = socket;                     break;                 case "joinChatRoom"://要求加入视频会议                     var userList = data.userList;                     if (userList) {                         for (var i = 0; i < userList.length; i++) {                             if (allWebsockets[userList[i]])                                 sendTo(allWebsockets[userList[i]], {                                     event: "joinChatRoom",                                     "message": data.message || "加入会议",                                     from: data.from,                                     roomId: data.roomId                                 });                         }                     }                     allWebsockets[data.from.id] = socket;                     break;                 case "rejectJoinChatRoom"://拒绝加入视频会议                     sendTo(allWebsockets[data.to], {                         event: "rejectJoinChatRoom",                         "message": data.message || "加入会议",                         from: data.from,                         roomId: data.roomId                     });                     break;                 //=================================             }         }         catch (ex) {             console.log("异常:\t", ex)         }     });     socket.on("disconnect", function () {         if (true) {             return;         }         var userId_ = socket.userId;         var roomId_ = socket.roomId;         if (userId_ != undefined && userId_ != null && userId_ != '') {             if (roomId_||allUsers[roomId_] == undefined || allSockets[roomId_] == undefined) {                 return;             }             var ulist = allUsers[roomId_];             if (ulist != undefined) {                 //查找用户连接发送信息                 for (var u in ulist) {                     if (allSockets[roomId_][u])                         sendTo(allSockets[roomId_][u].scoket, {event: 'leave', userId: userId_});                 }             }             //删除已经退出的用户             delete allUsers[roomId_][userId_];             if(socket.mainId)                 delete allWebsockets[socket.mainId];             if (allUsers[roomId_] != undefined) {                 delete allSockets[roomId_][userId_];                 showUserInfo(allUsers[roomId_]);             }         }     }); }); function showUserInfo(allUsers) {     console.log("在线用户:", allUsers);     sendTo(io, {         event: "showUserList",         "userList": allUsers,     }); } function removeUserInfo(roomId, userId) {     delete allUsers[roomId][userId];     delete allSockets[roomId][userId]; } function sendTo(connection, message) {     console.log("send data:\t", message);     if (connection != undefined)         connection.send(message); }

 

    启动websoket服务器 :

node server.js

以上包含两种websoket处理,如文本聊天处理、视频会议邀请处理。

 

前端集成:

在/front/src/views/Main.vue中定义一个主要的websoket连接,来处理视频会议邀请。

需要在index.html引用socket.io.js

代码如下:

export default { data() {     return { currentUser: this.$store.getters.getCurrentLoginInfo.user, main_websocket:io.connect(Constants.wsUrl)     }; }, methods: { send(message) {   message.from = {id: this.currentUser.id, name: this.currentUser.name, avator: this.currentUser.avator};   this.main_websocket.send(JSON.stringify(message)); } }, created() { //监听websoket消息       this.main_websocket.on('message', (data) => {         console.error("on message:", data);         switch (data.event) {           case "joinChatRoom":             var noticeName='roomId'+data.roomId;             this.$Notice.open({title:'视频会议邀请',name:noticeName,render:(r)=>{                 return r('div',[r('div',data.from.name+'邀请您加入视频会议,是否接受?'),                   r('Button',{                     on: {                       click:()=>{                         this.$router.push("/chat/" + data.roomId);                         this.$Notice.close(noticeName);                       }                     },props:{type:'primary',size:'small'}},'接受'),                   r('Button',{                     on: {click:()=>{               this.send({event:"rejectJoinChatRoom",to:data.from.id,roomId:data.roomId});                         this.$Notice.close(noticeName);                       }                     },props:{type:'error',size:'small'}},'拒绝')                 ]);               },duration:10,onClose:()=>{               }});             break;           case "sigin":             break;           case "rejectJoinChatRoom":             this.$Notice.warning({title:'拒绝视频会议',desc:data.from.name+'拒绝加入视频会议。',duration:10});             break;           case "message":             this.$Notice.info({title:data.title|'消息',desc:data.message,duration:10});             break;         }       });       this.send({event:"sigin"});     } };

视频会议邀约:

对应vuejs文件:/front/src/views/chat/index.vue

步骤代码如下:

import http from '../../api/http' import Room from './licodeRoom.vue' export default {   data() {     return {       roomShow: false,       hospno: null,       patient:null,       roomId: null,       doctorList: [],       tableHeight: window.innerHeight - 80     }   },   methods: {     chosePatient() {       http.post(this,'/pt/patient/choseOne',{hospno:this.hospno},(resp)=>{         var ret=resp.body;         if(ret.code=='111'){           this.patient=ret.value;         }else{           this.$Message.error(ret.msg);         }       });     },     joinRoom() {       this.roomId = this.hospno;       this.$router.push("/chat/"+this.roomId);   this.$parent.send({event:'joinChatRoom',roomId:this.roomId,userList:this.doctorList});     },     closeDiv() {       this.roomShow = false;       this.hospno = null;       this.roomId = null;     },     loadRouter(){       var params=this.$route.params;       console.error(params);       if(params.roomId!=''&¶ms.roomId!='-'&¶ms.roomId!='null'&¶ms.roomId!='undefined'){         this.roomId=params.roomId;         this.roomShow = true;       }     }   },   components: {     Room   },   watch:{     '$route'(val){ #监听路由变化'       if(val&&val!='-'){ #如果路由参数存在且不为‘-’ 则加载路由,并弹出licodeRoom.vue的界面 加入视频会议         this.loadRouter();       }     }   },   mounted() {     this.loadRouter();   },   created() {   } }

 

 

licodeRoom.vue代码:

代码文件:/front/src/views/chat/licodeRoom.vue

//设置视频位置 var fixedTop = function () {   //将已有的视频换到主视频模式   var els = document.getElementsByClassName('main_video');   //小窗口排列   var el_list = document.getElementsByClassName("video");   console.error("视频数量==》" + el_list.length);   if (els== undefined||els == null ||els.length== 0){     el_list[0].style.top="0px";     el_list[0].className = 'main_video';   }   for (var i = 0; i < el_list.length; i++) {     var el = el_list[i];     el.style.top = (i * 80) + 'px';     var p=el.querySelector(".licode_player");     if(p) {       p.ondblclick = function () {         document.getElementsByClassName("main_video")[0].className = 'video';         var pr = this.parentNode.parentNode;         pr.className = 'main_video';         pr.style.top = "0px";         console.error(this.parentNode.id);         fixedTop();       }     }   } }

//对应 export default 的methods的方法

//发布视频流 subscribeToStreams(streams) {   var cb = function (evt) {     console.error('Bandwidth Alert', evt.msg, evt.bandwidth);   };   for (var index in streams) {     var stream = streams[index];     if (this.localStream.getID() !== stream.getID()) {       this.room.subscribe(stream, {metadata: {type: 'subscriber'}});       stream.addEventListener('bandwidth-alert', cb);     }   } }, initLicode() {   this.localStream = Erizo.Stream({     audio: true, video: true, data: true, attributes: {id: this.currentUser.id, name: this.currentUser.name},     desktopStreamId: this.currentUser.id, videoSize: [640, 480, 1920, 1080]//,videoFrameRate: [10, 20]   });   axios.defaults.headers.post['Content-Type'] = 'application/json';   axios.defaults.headers.post['Accept'] = '*/*';   axios.post(Constants.licodeServer + 'createToken/', {     room: this.id,     username: encodeURI(this.currentUser.name),     role: 'presenter'   }).then((resp) => {     var token = resp.data;     this.room = Erizo.Room({token: token});       this.room.addEventListener('room-connected', (event) => {         this.room.publish(this.localStream, {maxVideoBW: 2000, minVideoBW: 1000,metadata: {type: 'publisher'}});         console.error("this.localStream room-connected id\t" + this.localStream.getID());         this.subscribeToStreams(event.streams);       });       this.room.addEventListener('stream-subscribed', (streamEvent) => {         var stream = streamEvent.stream;         var attrs = stream.getAttributes();         console.error("stream-subscribed\t接收视频流 id\t" + attrs.id);         if (attrs.id == this.currentUser.id)           return;         var s = this.$el.querySelector(".main_video");         if (s != undefined && s != null)           s.className = "video";         var elem = document.createElement("div");         elem.setAttribute("id", attrs.id);         elem.setAttribute("title", attrs.name);         elem.className = "main_video";         this.$el.querySelector("#video_list").appendChild(elem);         var videoEl = document.createElement("div");         videoEl.className = "videoPlay";         videoEl.setAttribute("id", "video_" + attrs.id);         elem.appendChild(videoEl);         stream.show("video_" + attrs.id);         // document.getElementById("audio_login").play();         //移除licode logo         document.getElementById("video_" + attrs.id).getElementsByClassName('licode_link')[0].innerHTML='';         fixedTop();       });       this.room.addEventListener('stream-added', (event) => {         console.error("stream-added id\t" + event.stream.getID());         var streams = [];         streams.push(event.stream);         this.subscribeToStreams(streams);       });       this.room.addEventListener('stream-removed', (streamEvent) => {         console.error("stream-removed");         // Remove stream from DOM         var stream = streamEvent.stream;         if (stream.getID() !== undefined) {           var attrs = stream.getAttributes();           var element = document.getElementById(attrs.id);           if (element != undefined && element != null)             this.$el.querySelector("#video_list").removeChild(element);           fixedTop();         }       });       this.room.addEventListener('stream-failed', () => {         console.error("stream-failed");         this.room.disconnect();       });     this.localStream.addEventListener('access-accepted', () => {       console.error("this.localStream access-accepted");       this.room.connect();       var attrs = this.localStream.getAttributes();       var elem = document.createElement("div");       elem.setAttribute("id", attrs.id);       elem.setAttribute("title", attrs.name);       elem.className = "main_video";       this.$el.querySelector("#video_list").appendChild(elem);       var videoEl = document.createElement("div");       videoEl.setAttribute("id", "video_" + attrs.id);       videoEl.className = "videoPlay";       elem.appendChild(videoEl);       this.localStream.show("video_" + attrs.id);//, {speaker: true});       //移除licode logo       document.getElementById("video_" + attrs.id).getElementsByClassName('licode_link')[0].innerHTML='';       fixedTop();     });     this.localStream.init();   }); }, quitRoom() {   if(this.localStream){}else{return;}   this.localStream.stop();   //取消订阅视频   this.room.unsubscribe(this.localStream, (result, error) => {     if (result === undefined) {       console.error("Error unsubscribing", error);     } else {       console.error("Stream unsubscribed!");     }   });   //取消发布视频   this.room.unpublish(this.localStream, (result, error) => {     if (result === undefined) {       console.error("Error unpublishing", error);     } else {       console.error("Stream unpublished!");     }   });   this.send({event:'leave'});   this.room.disconnect();   this.room = null;   this.localStream = null; }

 

会议聊天室说明:

初始化本地视频流 this.localStream = Erizo.Stream({   audio: true, video: true, data: true, attributes: {id: this.currentUser.id, name: this.currentUser.name},   desktopStreamId: this.currentUser.id, videoSize: [640, 480, 1920, 1080]//,videoFrameRate: [10, 20] });

 

获取聊天室token在获取token成功后初始化room,对room设置事件监听: room-connected 聊天室连接成功

连接成功后发布本地视频流localStream

stream-subscribed 视频流订阅成功

将获得的视频流播放, 通过fixedTop()方法设置位置,及设置主视频窗口。

stream-added 视频流加入后进行整个room订阅视频流。并对此视频流设置bandwith-alert监听。stream-removed 视频流移除或断开监听

对已经断开的视频流的video进行移除,并且调用fixedTop来重新布局设置主次屏。

stream-failed 视频连接失败监听

room断开连接。

对本地视频流监听,access-accepeted:

连接room:this.room.connect();

播放本地视频流,并且调用fixedTop来重新布局设置主次屏。

初始化localStream。                        this.localStream.init();


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3