diff --git a/src/EngineEntrance.js b/src/EngineEntrance.js index 6ef3bf7..1c814cb 100644 --- a/src/EngineEntrance.js +++ b/src/EngineEntrance.js @@ -63,7 +63,7 @@ export default class MessageEntrance extends Emiter { super(); this.lastClassActiveTime=0;//最后一次课堂激活的时间戳 //sdk 信息 - GlobalConfig.sdkVersion = "v2.32.1.20171123"; + GlobalConfig.sdkVersion = "v2.33.6.20171123"; loger.warn("sdkVersion:" + GlobalConfig.sdkVersion); console.log("sdkVersion:" + GlobalConfig.sdkVersion); //设置 @@ -102,6 +102,7 @@ export default class MessageEntrance extends Emiter { _webRtc.on(MessageTypes.USER_DEVICE_STATUS_CHAANGE, this.userDeviecStatusChange.bind(this)); //监听摄像头和麦克风的开启状态 _webRtc.on(MessageTypes.MEDIA_PUBLISH_STATUS_CHANGE, this.mediaPublishStatusChange.bind(this)); //webRtc推流状态发生改变 _webRtc.on(WebRtcApe.RE_JOIN_CHANNEL, this._webRtcRejoinChannel.bind(this)); //重先加入音视频频道 + _webRtc.on(MessageTypes.MEDIA_ENABLED_CHANGE, this._mediaEnabledChange.bind(this)); //音视频禁用状态改变 // Sass平台层 _sass = Sass; @@ -152,6 +153,7 @@ export default class MessageEntrance extends Emiter { //_confer_ape.on(MessageTypes.SWITCH_HLS_IP, this._switchHlsIpHandler.bind(this)); //MS HLS动态选点 _confer_ape.on(MessageTypes.STOP_ALL_MEDIA_PUBLISH, this._stopAllMediaPublishHandler.bind(this)); //课堂状态发生改变,需要停止当前的所有推流 _confer_ape.on(MessageTypes.CLASS_UPDATE_ROSTER, this._onRosterUpdateHandler.bind(this)); + _confer_ape.on(MessageTypes.RECEIVE_MEDIA_ENABLED_CHANGE, this._receiveMeiaEnabledChange.bind(this)); _chat_ape = new ChatApe(); _chat_ape.on('*', (type, data) => this._emit(type, data)); @@ -651,6 +653,8 @@ export default class MessageEntrance extends Emiter { randUserId = "A" + randUserId; } else if (GlobalConfig.userRole == ApeConsts.presenter) { randUserId = "P" + randUserId; + } else if (GlobalConfig.userRole == ApeConsts.invisible) { + randUserId = "I" + randUserId; } else { randUserId = "S" + randUserId; } @@ -700,7 +704,8 @@ export default class MessageEntrance extends Emiter { let autoLoginMd5 = MD5("" + GlobalConfig.classId + GlobalConfig.paramUserId + GlobalConfig.userRole); //let autoLoginMd5 = MD5("" + GlobalConfig.classId + GlobalConfig.userId + GlobalConfig.userRole); //loger.log("joinClass-GlobalConfig.autoLogin", GlobalConfig.autoLogin, "autoLoginMd5-", autoLoginMd5); - if (GlobalConfig.autoLogin && autoLoginMd5 == GlobalConfig.autoLogin || GlobalConfig.isInvisible) { + if ((GlobalConfig.autoLogin && autoLoginMd5 == GlobalConfig.autoLogin) || + GlobalConfig.isInvisible||GlobalConfig.isAssistant||GlobalConfig.isPresenter) { // MD5(classId+userId+userRole)==m //自动登录,跳过验证流程 loger.log("自动登录->" + GlobalConfig.userRole); @@ -1404,7 +1409,17 @@ export default class MessageEntrance extends Emiter { _webRtc.tryAddMobileStream(_data.nodeId); } } - + /* + * 收到媒体禁用状态切换的消息 + * */ + _receiveMeiaEnabledChange(_data){ + if(!_data||GlobalConfig.isRecordPlayBack||GlobalConfig.isH5){ + return; + } + if(_webRtc){ + _webRtc.webRtcMeiaEnabledChange(_data); + } + } //手动切换MS -> {ip;"xxx.xx.xx","port":"xxxx"} _switchMediaServer(_param) { if (GlobalConfig.isRecordPlayBack) { @@ -2153,6 +2168,17 @@ export default class MessageEntrance extends Emiter { _webRtcRejoinChannel(_data){ this._reJoinChannel(_data) } + /* + * 监听webRtc + * */ + _mediaEnabledChange(_data){ + if (!_mcu.connected||GlobalConfig.isRecordPlayBack) { + return; + } + if(_confer_ape){ + _confer_ape.sendMediaEnabledChange(_data); + } + } //监听摄像头麦克风状态 userDeviecStatusChange(_data) { diff --git a/src/EngineUtils.js b/src/EngineUtils.js index bd8a516..b23f04f 100644 --- a/src/EngineUtils.js +++ b/src/EngineUtils.js @@ -4,212 +4,222 @@ import Base64 from 'base64-js'; import UTF8 from 'utf-8'; -class EngineUtils{ - static isEmptyObject(O){ - for (var x in O){ - return false; - } - return true; +class EngineUtils { + static isEmptyObject(O) { + for (var x in O) { + return false; } - static arrayToJsonString(_param){ - try{ - return JSON.stringify(_param); - }catch (err){ - console.warn("数组转JSON失败->ERROR:"+err.message); - } - return ""; + return true; + } + + static arrayToJsonString(_param) { + try { + return JSON.stringify(_param); + } catch (err) { + console.warn("数组转JSON失败->ERROR:" + err.message); } - static arrayFromJsonString(_param){ - try{ - return JSON.parse(_param); - }catch (err){ - console.warn("JSON转数组失败->ERROR:"+err.message); - } - return []; + return ""; + } + + static arrayFromJsonString(_param) { + try { + return JSON.parse(_param); + } catch (err) { + console.warn("JSON转数组失败->ERROR:" + err.message); } + return []; + } - /* - * 生成随机数 _part 段数 默认3段; splitStr分隔符 - * */ - static creatRandomNum(_part=3,splitStr=""){ - let randNumStr=""; - for(let i=0;i<_part;i++){ - randNumStr+=splitStr+parseInt(Math.random()*1000); - } - return randNumStr; - } - - //生成时间戳后9位 保证唯一 - static creatSoleNumberFromTimestamp(){ - let time = new Date().getTime(); - let timestamp= time % 1000000000;//time后9位 - return timestamp; - } - - //生成时间戳毫秒 - static creatTimestamp(){ - let time = parseInt(new Date().getTime()/1000);//精确到秒 - return time; - } - //生成时间戳 string - static creatTimestampStr(){ - let curTime = new Date(); - let timeStr = "" + curTime.getFullYear() + "-"; - timeStr += (curTime.getMonth()+1) + "-"; - timeStr += curTime.getDate() + "-"; - timeStr+=curTime.getHours() + "-"; - timeStr+=curTime.getMinutes() + "-"; - timeStr+=curTime.getSeconds(); - return timeStr; - } - //根据时间戳字符串转换为时间戳 2017-10-27-15-38-15 - static getTimestampFromStr(_timeStr){ - if(!_timeStr) return this.creatTimestamp(); - let timeArr=_timeStr.split("_"); - if(timeArr&&timeArr.length==6){ - return parseInt( new Date(timeArr[0],parseInt(timeArr[1])-1,timeArr[2],timeArr[3],timeArr[4],timeArr[5]).getTime()/1000); - } - return this.creatTimestamp(); + /* + * 生成随机数 _part 段数 默认3段; splitStr分隔符 + * */ + static creatRandomNum(_part = 3, splitStr = "") { + let randNumStr = ""; + for (let i = 0; i < _part; i++) { + randNumStr += splitStr + parseInt(Math.random() * 1000); } - //生成时间戳 格式:"20170209" - static creatTimestampYMD(){ - let curTime = new Date(); - let year = "" + curTime.getFullYear(); - let month = "" +(curTime.getMonth()+1); - let day = "" + curTime.getDate(); + return randNumStr; + } - if(month.length<2){ - month="0"+month; - } - if(day.length<2){ - day="0"+day; - } - return year+month+day; - } - static objectToBase64(_object){ - try{ - let _objectStr=JSON.stringify(_object); - //console.log("objectToBase64------1----------") - let byte=UTF8.setBytesFromString(_objectStr); - //console.log("objectToBase64------2----------") - let _objectBase64=Base64.fromByteArray(byte); - return _objectBase64 - }catch (err){ - console.log("objectToBase64 err:"+err.message); - return ""; - } - return "" - } - static objectFromBase64(_objectBase64){ - try{ - let byte=Base64.toByteArray(_objectBase64); - let _objectStr=UTF8.getStringFromBytes(byte); - let _object=JSON.parse(_objectStr); - return _object - }catch (err){ - console.log("objectFromBase64 err:"+err.message); - return null; - } - return null; + //生成时间戳后9位 保证唯一 + static creatSoleNumberFromTimestamp() { + let time = new Date().getTime(); + let timestamp = time % 1000000000;//time后9位 + return timestamp; + } + + //生成时间戳毫秒 + static creatTimestamp() { + let time = parseInt(new Date().getTime() / 1000);//精确到秒 + return time; + } + + //生成时间戳 string + static creatTimestampStr() { + let curTime = new Date(); + let timeStr = "" + curTime.getFullYear() + "-"; + timeStr += (curTime.getMonth() + 1) + "-"; + timeStr += curTime.getDate() + "-"; + timeStr += curTime.getHours() + "-"; + timeStr += curTime.getMinutes() + "-"; + timeStr += curTime.getSeconds(); + return timeStr; + } + + //根据时间戳字符串转换为时间戳 2017-10-27-15-38-15 + static getTimestampFromStr(_timeStr) { + if (!_timeStr) return this.creatTimestamp(); + let timeArr = _timeStr.split("_"); + if (timeArr && timeArr.length == 6) { + return parseInt(new Date(timeArr[0], parseInt(timeArr[1]) - 1, timeArr[2], timeArr[3], timeArr[4], timeArr[5]).getTime() / 1000); } - //优化压缩坐标点数组,去除一些连续重复坐标的点,返回一个数组 - static compressPoint(_arr){ - if(!_arr){ - return []; - } - if(_arr.length<5){ - //点,直线,矩形 坐标点小于5不需要处理 - return _arr; - } - let tempPointArr=_arr; - let newPointArr=[]; - newPointArr.push(tempPointArr[0]); - let lastW=tempPointArr[0].w; - let continueNum=0;//坐标相同的连续次数 - //先筛除水平方向的连续重复坐标点 - let len=tempPointArr.length-1; - for(let i=1;i<len;i++){ - let item=tempPointArr[i]; - if(item&&item.w!=lastW){ - lastW=item.w; - if(continueNum>0){ - newPointArr.push(tempPointArr[i-1]); - } - newPointArr.push(item); - continueNum=0; - }else { - continueNum++; - } - } - //如果最终的坐标点数量小于2,需要把最后一个坐标点添加 - if(tempPointArr[len]){ - newPointArr.push(tempPointArr[len]); - } - //如果坐标点已经小于等于2不需要继续检测 - if(newPointArr.length<=2){ - return newPointArr; - } + return this.creatTimestamp(); + } - //筛除水垂直向的连续重复坐标点 - let finalPointArr=[]; - finalPointArr.push(newPointArr[0]); - let lastH=newPointArr[0].h; - continueNum=0; - len=newPointArr.length-1; - for(let k=1;k<len;k++){ - let item=newPointArr[k]; - if(item&&item.h!=lastH){ - lastH=item.h; - if(continueNum>0){ - finalPointArr.push(newPointArr[k-1]); - } - finalPointArr.push(item); - continueNum=0; - }else { - continueNum++; - } - } - if(newPointArr[len]){ - finalPointArr.push(newPointArr[len]); - } - return finalPointArr; + //生成时间戳 格式:"20170209" + static creatTimestampYMD() { + let curTime = new Date(); + let year = "" + curTime.getFullYear(); + let month = "" + (curTime.getMonth() + 1); + let day = "" + curTime.getDate(); + + if (month.length < 2) { + month = "0" + month; } - //压缩数据,把坐标点数组转换为字符串,返回字符串 - static optimizePoint(_pointGroup){ - if(!_pointGroup){ - return ""; - } - let tempStr=""; - try { - tempStr=JSON.stringify(_pointGroup); - }catch (err){ - return ""; - } - let regexp1=/},{"w":/g; - let regexp2=/,"h":/g; - tempStr=tempStr.replace(regexp1,"#"); - tempStr=tempStr.replace(regexp2,"&"); - //console.log("标注压缩后的字符长度->",tempStr.length); - return tempStr; - } - //把字符串坐标点集合转换为数组,返回数组的JSON - static unPackOptimizePoint(_str){ - if(!_str){ - return ""; - } - let tempStr=_str; - let regexp1=/#/g; - let regexp2=/&/g; - tempStr=tempStr.replace(regexp1,'},{"w":'); - tempStr=tempStr.replace(regexp2,',"h":'); + if (day.length < 2) { + day = "0" + day; + } + return year + month + day; + } - let dataArr=[]; - try { - dataArr=JSON.parse(tempStr); - }catch (err){ + static objectToBase64(_object) { + try { + let _objectStr = JSON.stringify(_object); + //console.log("objectToBase64------1----------") + let byte = UTF8.setBytesFromString(_objectStr); + //console.log("objectToBase64------2----------") + let _objectBase64 = Base64.fromByteArray(byte); + return _objectBase64 + } catch (err) { + console.log("objectToBase64 err:" + err.message); + return ""; + } + return "" + } + + static objectFromBase64(_objectBase64) { + try { + let byte = Base64.toByteArray(_objectBase64); + let _objectStr = UTF8.getStringFromBytes(byte); + let _object = JSON.parse(_objectStr); + return _object + } catch (err) { + console.log("objectFromBase64 err:" + err.message); + return null; + } + return null; + } + + //优化压缩坐标点数组,去除一些连续重复坐标的点,返回一个数组 + static compressPoint(_arr) { + if (!_arr) { + return []; + } + if (_arr.length < 5) { + //点,直线,矩形 坐标点小于5不需要处理 + return _arr; + } + let tempPointArr = _arr; + let newPointArr = []; + newPointArr.push(tempPointArr[0]); + let lastW = tempPointArr[0].w; + let continueNum = 0;//坐标相同的连续次数 + //先筛除水平方向的连续重复坐标点 + let len = tempPointArr.length - 1; + for (let i = 1; i < len; i++) { + let item = tempPointArr[i]; + if (item && item.w != lastW) { + lastW = item.w; + if (continueNum > 0) { + newPointArr.push(tempPointArr[i - 1]); + } + newPointArr.push(item); + continueNum = 0; + } else { + continueNum++; + } + } + //如果最终的坐标点数量小于2,需要把最后一个坐标点添加 + if (tempPointArr[len]) { + newPointArr.push(tempPointArr[len]); + } + //如果坐标点已经小于等于2不需要继续检测 + if (newPointArr.length <= 2) { + return newPointArr; + } + + //筛除水垂直向的连续重复坐标点 + let finalPointArr = []; + finalPointArr.push(newPointArr[0]); + let lastH = newPointArr[0].h; + continueNum = 0; + len = newPointArr.length - 1; + for (let k = 1; k < len; k++) { + let item = newPointArr[k]; + if (item && item.h != lastH) { + lastH = item.h; + if (continueNum > 0) { + finalPointArr.push(newPointArr[k - 1]); + } + finalPointArr.push(item); + continueNum = 0; + } else { + continueNum++; + } + } + if (newPointArr[len]) { + finalPointArr.push(newPointArr[len]); + } + return finalPointArr; + } + + //压缩数据,把坐标点数组转换为字符串,返回字符串 + static optimizePoint(_pointGroup) { + if (!_pointGroup) { + return ""; + } + let tempStr = ""; + try { + tempStr = JSON.stringify(_pointGroup); + } catch (err) { + return ""; + } + let regexp1 = /},{"w":/g; + let regexp2 = /,"h":/g; + tempStr = tempStr.replace(regexp1, "#"); + tempStr = tempStr.replace(regexp2, "&"); + //console.log("标注压缩后的字符长度->",tempStr.length); + return tempStr; + } + + //把字符串坐标点集合转换为数组,返回数组的JSON + static unPackOptimizePoint(_str) { + if (!_str) { + return ""; + } + let tempStr = _str; + let regexp1 = /#/g; + let regexp2 = /&/g; + tempStr = tempStr.replace(regexp1, '},{"w":'); + tempStr = tempStr.replace(regexp2, ',"h":'); + + let dataArr = []; + try { + dataArr = JSON.parse(tempStr); + } catch (err) { - } - return dataArr; } + return dataArr; + } } export default EngineUtils; \ No newline at end of file diff --git a/src/MessageTypes.js b/src/MessageTypes.js index 9984fb7..ac992c4 100644 --- a/src/MessageTypes.js +++ b/src/MessageTypes.js @@ -111,6 +111,8 @@ MessageTypes.WEB_RTC_JOIN_FAILED = "web_rtc_join_failed"; MessageTypes.WEB_RTC_PUBLISH_FAILED = "web_rtc_publish_failed"; MessageTypes.GET_DEVICES_SUCCESS = "get_devices_success"; MessageTypes.MEDIA_PUBLISH_STATUS_CHANGE = "media_publish_status_change";//音视频推流的状态发生改变 +MessageTypes.MEDIA_ENABLED_CHANGE = "media_enabled_change";//媒体禁用或开启状态改变 +MessageTypes.RECEIVE_MEDIA_ENABLED_CHANGE = "receive_media_enabled_change";//收到媒体禁用状态控制的消息 //MCU MS MessageTypes.SWITCH_MCU_IP = "switch_mcu_ip"; //切换mcu 重新选点 diff --git a/src/apes/ApeConsts.js b/src/apes/ApeConsts.js index da9a8ed..8ed675f 100644 --- a/src/apes/ApeConsts.js +++ b/src/apes/ApeConsts.js @@ -27,6 +27,8 @@ ApeConsts.USER_ACTION_SILENCE_STATUS_CHANGE = 3; //更改用户的禁言状态 ApeConsts.CLASS_ACTION_KICK_OUT_ROSTER=4; //指定的人踢出课堂 ApeConsts.STOP_ALL_PUBLISH=5; //所有人停止推流 ApeConsts.CLASS_ACTION_DRAW_STATUS_CHANGE = 6; //更改用户的画笔状态 +ApeConsts.MEDIA_ENABLED_CHANGE = 7; //更改用户的音视频禁用状态 + //课堂类型 1:1v1(2路流) 2:直播(1路流) 3:小班课(可以多路流) ApeConsts.CLASS_TYPE_1v1 = 1; // 互动课堂,通过MS转发音视频,不能进行H5观看 1v1(2路流) diff --git a/src/apes/ConferApe.js b/src/apes/ConferApe.js index aa80403..8bd0277 100644 --- a/src/apes/ConferApe.js +++ b/src/apes/ConferApe.js @@ -855,6 +855,17 @@ class ConferApe extends Ape { loger.warn('chatMsg->JSON数据解析失败'); } break; + case ApeConsts.MEDIA_ENABLED_CHANGE: + let mediaMsgObj = null; + try { + mediaMsgObj = JSON.parse(chatMsg.message); + if (mediaMsgObj) { + this.receiveChangeUserMediaEnabledStatus(mediaMsgObj); + } + } catch (err) { + loger.warn('chatMsg->JSON数据解析失败',chatMsg.message); + } + break; default: break; } @@ -1028,6 +1039,11 @@ class ConferApe extends Ape { loger.log("监课进入或更新数据"); return; } + if (nodeData.role == ApeConsts.NR_ASSISTANT && GlobalConfig.userRole != ApeConsts.invisible && GlobalConfig.userRole != ApeConsts.assistant) { + loger.log("助教进入或更新数据"); + return; + } + if (!rosterExists) { //this.rosterLen = Object.keys(this.rosters).length; //GlobalConfig.rosterNum = this.rosterLen;//记录当前的总人数 @@ -1091,11 +1107,36 @@ class ConferApe extends Ape { } } - //设备状态更新 + /* + * 设备状态更新 + * */ updaterUserDeviecStatusChange(_data) { loger.log("音视频设备状态更新->", _data); this.updateUserInfo(); } + /* + * 媒体开启或禁用 + * */ + sendMediaEnabledChange(_data){ + if(!_data){ + return; + } + + try{ + loger.log("发送->媒体开启或禁用",_data); + this.sendConferMsg({"to": 0, "message":JSON.stringify(_data), "actionType": ApeConsts.MEDIA_ENABLED_CHANGE}); + }catch (err){ + loger.warn("发送->媒体开启或禁用->失败",_data,err.message); + } + + } + + /* + * 收到媒体开启或禁用控制 + * */ + receiveChangeUserMediaEnabledStatus(_data){ + this._emit(MessageTypes.RECEIVE_MEDIA_ENABLED_CHANGE,_data); + } //删除用户 rosterDelHandler(nodeId) { @@ -1114,12 +1155,9 @@ class ConferApe extends Ape { if(GlobalConfig.classType!= ApeConsts.CLASS_TYPE_ZHIBO){ loger.log(nodeId, "->离开课堂->身份->", user.userRole); } - } delete this.rosters[nodeId]; GlobalConfig.rosters = this.rosters; - //this.rosterLen = Object.keys(this.rosters).length; - //GlobalConfig.rosterNum = this.rosterLen;//记录当前的总人数 if(!GlobalConfig.isH5) { this.emitRosterChange(); diff --git a/src/apes/WebRtcApe.js b/src/apes/WebRtcApe.js index 0634b5d..31f822d 100644 --- a/src/apes/WebRtcApe.js +++ b/src/apes/WebRtcApe.js @@ -1,6 +1,8 @@ //* // WebRtc // */ +require('../assets/css/mcuStyle.css'); + import Emiter from 'Emiter'; import Loger from "Loger"; import $ from "jquery"; @@ -10,12 +12,13 @@ import EngineUtils from 'EngineUtils'; import MessageTypes from 'MessageTypes'; var AgoraRTC = require('../AgoraRTCSDK-1.14.0'); let loger = Loger.getLoger('WebRtcApe'); -const SIZE_480=480; -const SIZE_360=360; -const SIZE_320=320; -const SIZE_240=240; -const SIZE_160=160; -const SIZE_120=120; +const SIZE_480 = 480; +const SIZE_360 = 360; +const SIZE_320 = 320; +const SIZE_240 = 240; +const SIZE_160 = 160; +const SIZE_120 = 120; + class WebRtcApe extends Emiter { constructor() { super(); @@ -24,10 +27,10 @@ class WebRtcApe extends Emiter { this.appCertificate = ""; this.appRecordingKey = ""; - this.setConfigTimestamp=0;//设置旁路地址的时间戳 + this.setConfigTimestamp = 0;//设置旁路地址的时间戳 this.configPublisherUrl = "";//旁路地址; - this.m3u8Url="";//旁路拉流地址 - this.rtmpUrl="";//旁路拉流地址 + this.m3u8Url = "";//旁路拉流地址 + this.rtmpUrl = "";//旁路拉流地址 this.channelKey = null; this.channelId = ""; @@ -47,10 +50,10 @@ class WebRtcApe extends Emiter { this.remoteVideoList = {};//记录远程视频流 - this.pWidth=SIZE_480; - this.pHeight=SIZE_360; - this.pFrameRate=30; - this.pBitrate=500; + this.pWidth = SIZE_480; + this.pHeight = SIZE_360; + this.pFrameRate = 30; + this.pBitrate = 500; //120P 0 160x120 15 65 //240P 20 320x240 15 200 @@ -58,10 +61,10 @@ class WebRtcApe extends Emiter { this.videoResolution = "240P"; this.isOpenVideo = true; - this.firstPublishSuccess=false;//记录加入频道成功之后是否推流成功过,离开频道之后需要设置为false + this.firstPublishSuccess = false;//记录加入频道成功之后是否推流成功过,离开频道之后需要设置为false this.isPublish = false;//当前是否正在推流 - this.videoScale=1;//视图的缩放比例,默认为1; + this.videoScale = 1;//视图的缩放比例,默认为1; this.normalRemoteViewId = ""; @@ -85,10 +88,10 @@ class WebRtcApe extends Emiter { this.invisibleVideoHeight = SIZE_360; this.xdyRemote = "xdy_remote"; - this.localWebRtcVideoClass='localWebRtcVideoClass';//本地视图统一的class名称 - this.invisibleWebRtcVideoClass='invisibleWebRtcVideoClass'; - this.normalWebRtcVideoClass='normalWebRtcVideoClass'; - this.hostWebRtcVideoClass='hostWebRtcVideoClass'; + this.localWebRtcVideoClass = 'localWebRtcVideoClass';//本地视图统一的class名称 + this.invisibleWebRtcVideoClass = 'invisibleWebRtcVideoClass'; + this.normalWebRtcVideoClass = 'normalWebRtcVideoClass'; + this.hostWebRtcVideoClass = 'hostWebRtcVideoClass'; //webRtc sdk this.client = AgoraRTC.createClient({mode: this.mode}); @@ -146,7 +149,7 @@ class WebRtcApe extends Emiter { this.client.on('stream-published', (evt)=> { loger.log("webRtc->推流成功->", new Date().getTime()); this.isPublish = true; - this.firstPublishSuccess=true; + this.firstPublishSuccess = true; GlobalConfig.openCamera = EngineUtils.creatTimestamp(); GlobalConfig.openMicrophones = GlobalConfig.openCamera; this._emit(MessageTypes.USER_DEVICE_STATUS_CHAANGE, { @@ -208,41 +211,47 @@ class WebRtcApe extends Emiter { } //添加之前先删除之前存在的重复视图 - let len=$("#"+this.xdyRemote + uid+" #player_" + uid).length; - if(len<1){ - loger.log("添加之前先删除之前存在的重复视图",uid); - $("#"+this.xdyRemote + uid).remove(); + let len = $("#" + this.xdyRemote + uid + " #player_" + uid).length; + if (len < 1) { + loger.log("添加之前先删除之前存在的重复视图", uid); + $("#" + this.xdyRemote + uid).remove(); + } + let audioMutedDiv = ""; + if (GlobalConfig.isTeachOrAssistant || GlobalConfig.isInvisible) { + audioMutedDiv = `<div class="audioMuted audioOpen" id=${"audioMuted_"+uid}></div>`; } if (userRole == ApeConsts.invisible) { - let nameDiv = `<div style=${this.invisibleVideoWidth}px;height:20px; position: absolute;bottom: 0; z-index: 1;overflow:hidden;font-size: 14px;text-align: right; color: #cccccc;display:${this.nameDisplay}">${userName}</div>`; + let nameDiv = `<div style=${this.invisibleVideoWidth}px;height:22px; position: absolute;bottom: 2px; z-index: 1;overflow:hidden;font-size: 14px;text-align: right; vertical-align: middle;background-color: #2926251a;color: #e7e7e7display:${this.nameDisplay}">${userName + audioMutedDiv}</div>`; //把远程视频添加到监课列表 loger.log("获取远程视频流成功->监课:" + userName + "->" + uid, new Date().getTime()); - let viewDiv = `<div id="${this.xdyRemote + uid}" class="${this.invisibleWebRtcVideoClass}" style="width:${this.invisibleVideoWidth*this.videoScale}px;height:${this.invisibleVideoHeight*this.videoScale}px;float: left;margin-right: 1px;pointer-events: none;">${nameDiv}</div>`; + let viewDiv = `<div id="${this.xdyRemote + uid}" class="${this.invisibleWebRtcVideoClass}" style="width:${this.invisibleVideoWidth * this.videoScale}px;height:${this.invisibleVideoHeight * this.videoScale}px;float: left;margin-right: 1px;">${nameDiv}</div>`; $(this.invisibleViewId).append(viewDiv); - } else if (userRole == ApeConsts.host||userRole == ApeConsts.assistant||userRole == ApeConsts.presenter) { - let nameDiv = `<div style="width:${this.hostRemoteVideoWidth}px;height:20px; position: absolute;bottom: 0; z-index: 1;overflow:hidden;font-size: 14px;text-align: right; color: #cccccc;display:${this.nameDisplay}">${userName}</div>`; + } else if (userRole == ApeConsts.host || userRole == ApeConsts.assistant || userRole == ApeConsts.presenter) { + let nameDiv = `<div style="width:${this.hostRemoteVideoWidth}px;height:22px; position: absolute;bottom: 2px; z-index: 1;overflow:hidden;font-size: 14px;text-align: right;vertical-align: middle; background-color: #2926251a;color: #e7e7e7;display:${this.nameDisplay}">${userName + audioMutedDiv}</div>`; //把远程视图添加到老师列表 - loger.log("获取远程视频流成功->userRole:"+userRole+":" + userName + "->" + uid, new Date().getTime()); - let viewDiv = `<div id="${this.xdyRemote + uid}" class="${this.hostWebRtcVideoClass}" style="width:${this.hostRemoteVideoWidth*this.videoScale}px;height:${this.hostRemoteVideoHeight*this.videoScale}px;float: left;margin-right: 1px;pointer-events: none;">${nameDiv}</div>`; + loger.log("获取远程视频流成功->userRole:" + userRole + ":" + userName + "->" + uid, new Date().getTime()); + let viewDiv = `<div id="${this.xdyRemote + uid}" class="${this.hostWebRtcVideoClass}" style="width:${this.hostRemoteVideoWidth * this.videoScale}px;height:${this.hostRemoteVideoHeight * this.videoScale}px;float: left;margin-right: 1px;">${nameDiv}</div>`; $(this.hostRemoteViewId).prepend(viewDiv); } else { - let nameDiv = `<div style="width:${this.normalRemoteVideoWidth}px;height:20px; position: absolute;bottom: 0; z-index: 1;overflow:hidden;font-size: 14px;text-align: right; color: #cccccc;display:${this.nameDisplay}">${userName}</div>`; + let nameDiv = `<div style="width:${this.normalRemoteVideoWidth}px;height:22px; position: absolute;bottom: 2px; z-index: 1;overflow:hidden;font-size: 14px;text-align: right;vertical-align: middle;background-color: #2926251a;color: #e7e7e7;display:${this.nameDisplay}">${userName + audioMutedDiv}</div>`; //把视图添加到学生列表 loger.log("获取远程视频流成功->学生:" + userName + "->" + uid, new Date().getTime()); - let viewDiv = `<div id="${this.xdyRemote + uid}" class="${this.normalWebRtcVideoClass}" style="width:${this.normalRemoteVideoWidth*this.videoScale}px;height:${this.normalRemoteVideoHeight*this.videoScale}px;float: left;margin-right: 1px;pointer-events: none;">${nameDiv}</div>`; + let viewDiv = `<div id="${this.xdyRemote + uid}" class="${this.normalWebRtcVideoClass}" style="width:${this.normalRemoteVideoWidth * this.videoScale}px;height:${this.normalRemoteVideoHeight * this.videoScale}px;float: left;margin-right: 1px;">${nameDiv}</div>`; $(this.normalRemoteViewId).append(viewDiv); } + $("#audioMuted_" + uid).off("click", this._clickAudioMuted.bind(this)); + $("#audioMuted_" + uid).on("click", this._clickAudioMuted.bind(this)); //播放视频,隐藏控制条 try { - $("bar_" + stream.getId()).hide(); + $("#bar_" + stream.getId()).hide(); stream.play(this.xdyRemote + stream.getId()); } catch (err) { //添加之前先删除之前存在的重复视图 - let len=$("#"+this.xdyRemote + uid+" #player_" + uid).length; - if(len<1){ - lloger.log("流播放失败->删除视图",uid); - $("#"+this.xdyRemote + uid).remove(); + let len = $("#" + this.xdyRemote + uid + " #player_" + uid).length; + if (len < 1) { + loger.log("流播放失败->删除视图", uid); + $("#" + this.xdyRemote + uid).remove(); } } if (user && (user.deviceType == GlobalConfig.deviceIOS || user.deviceType == GlobalConfig.deviceAndroid)) { @@ -252,43 +261,44 @@ class WebRtcApe extends Emiter { this.clearInvalidVideoView(); } + //清除无效的视图 - clearInvalidVideoView(){ - let normalList=document.getElementsByClassName(this.normalWebRtcVideoClass); - let hostList=document.getElementsByClassName(this.hostWebRtcVideoClass); - let localList=document.getElementsByClassName(this.localWebRtcVideoClass); - - let divItem=null; - if(normalList){ - for (let i=normalList.length-1;i>=0;i--){ - divItem=normalList[i]; - if(divItem&&divItem.children.length<2){ + clearInvalidVideoView() { + let normalList = document.getElementsByClassName(this.normalWebRtcVideoClass); + let hostList = document.getElementsByClassName(this.hostWebRtcVideoClass); + let localList = document.getElementsByClassName(this.localWebRtcVideoClass); + + let divItem = null; + if (normalList) { + for (let i = normalList.length - 1; i >= 0; i--) { + divItem = normalList[i]; + if (divItem && divItem.children.length < 2) { console.log("删除无效的学生视图"); divItem.remove(); } } } - divItem=null; - if(hostList){ - for (let i=hostList.length-1;i>=0;i--){ - divItem=hostList[i]; - if(divItem&&divItem.children.length<2){ + divItem = null; + if (hostList) { + for (let i = hostList.length - 1; i >= 0; i--) { + divItem = hostList[i]; + if (divItem && divItem.children.length < 2) { console.log("删除无效的老师视图"); divItem.remove(); } } } - divItem=null; - if(localList){ - for (let i=localList.length-1;i>=0;i--){ - divItem=localList[i]; - if(divItem&&divItem.children.length<2){ + divItem = null; + if (localList) { + for (let i = localList.length - 1; i >= 0; i--) { + divItem = localList[i]; + if (divItem && divItem.children.length < 2) { console.log("删除无效的本地视图"); divItem.remove(); } } } - divItem=null; + divItem = null; } @@ -331,8 +341,8 @@ class WebRtcApe extends Emiter { //console.log("remoteView->", remoteView) if (remoteView) { let player = document.getElementById("player_" + nodeId); - let len=$("#"+this.xdyRemote + nodeId+" #player_" + nodeId).length; - if (len>0) { + let len = $("#" + this.xdyRemote + nodeId + " #player_" + nodeId).length; + if (len > 0) { loger.log(nodeId + " 流已经添加显示,不需要再处理"); return; } else { @@ -358,29 +368,29 @@ class WebRtcApe extends Emiter { //记录加入频道成功之后是否立即推流,默认false //一般只有在刷新重进频道的时候会用到 - this.immediatePublish=_params.immediatePublish||false; + this.immediatePublish = _params.immediatePublish || false; //根据不同身份设置不同的分辨率 - if(GlobalConfig.isTeachOrAssistant){ - if(GlobalConfig.maxMediaChannels==1){ + if (GlobalConfig.isTeachOrAssistant) { + if (GlobalConfig.maxMediaChannels == 1) { //直播时使用标清最高档 - this.videoResolution="360P_8"; - }else { - this.videoResolution="240P"; + this.videoResolution = "360P_8"; + } else { + this.videoResolution = "240P"; } - }else if(GlobalConfig.isInvisible){ - this.videoResolution="120P"; - }else { + } else if (GlobalConfig.isInvisible) { + this.videoResolution = "120P"; + } else { //学生在两路视频的时候使用240P 其他多人课堂的时候使用低清的 - if(GlobalConfig.maxMediaChannels==2||GlobalConfig.maxMediaChannels>3){ - this.videoResolution="240P" - }else { + if (GlobalConfig.maxMediaChannels == 2 || GlobalConfig.maxMediaChannels > 3) { + this.videoResolution = "240P" + } else { //180P_4 13 240x180 15 120 - this.videoResolution="180P_4" + this.videoResolution = "180P_4" } } - loger.log("开始加入视频通话频道->channelId:" + this.channelId, "uid:" + this.uid,"videoResolution:"+this.videoResolution); + loger.log("开始加入视频通话频道->channelId:" + this.channelId, "uid:" + this.uid, "videoResolution:" + this.videoResolution); this.client.join(this.channelKey, "" + this.channelId, this.uid, (uid)=> { this.uid = uid; loger.log("加入视频通话频道->成功->channelId:" + this.channelId, "uid:" + this.uid); @@ -388,12 +398,12 @@ class WebRtcApe extends Emiter { this.openLoaclStream(); //加入频道成功之后需要判断是否立即开启推流 - if(this.immediatePublish==true){ - loger.log("加入音视频频道成功->立刻开始推流->"+this.immediatePublish); + if (this.immediatePublish == true) { + loger.log("加入音视频频道成功->立刻开始推流->" + this.immediatePublish); clearTimeout(this.rePublishDelay); - this.rePublishDelay=setTimeout(()=>{ + this.rePublishDelay = setTimeout(()=> { this.publish(); - },200); + }, 200); } }, (err)=> { loger.error("加入视频通话频道->失败->", err); @@ -439,11 +449,11 @@ class WebRtcApe extends Emiter { } leaveChannel() { - loger.log("调用离开视频通话频道->isPublish"+this.isPublish); + loger.log("调用离开视频通话频道->isPublish" + this.isPublish); if (!this.client) { return; } - this.firstPublishSuccess=false; + this.firstPublishSuccess = false; this.unpublish(); this.client.leave(() => { loger.log("离开视频通话频道->成功"); @@ -469,31 +479,39 @@ class WebRtcApe extends Emiter { /* * 设置旁录地址 * */ - setConfigPublisherUrl(_publishUrl){ - this.firstPublishSuccess=false; - this.setConfigTimestamp=new Date().getTime()/1000; - this.configPublisherUrl=_publishUrl; - if(this.client&& this.configPublisherUrl){ - let configObj={width: parseInt(this.pWidth), height: parseInt(this.pHeight), framerate: parseInt(this.pFrameRate), bitrate: parseInt(this.pBitrate), publishUrl:""+this.configPublisherUrl}; + setConfigPublisherUrl(_publishUrl) { + this.firstPublishSuccess = false; + this.setConfigTimestamp = new Date().getTime() / 1000; + this.configPublisherUrl = _publishUrl; + if (this.client && this.configPublisherUrl) { + let configObj = { + width: parseInt(this.pWidth), + height: parseInt(this.pHeight), + framerate: parseInt(this.pFrameRate), + bitrate: parseInt(this.pBitrate), + publishUrl: "" + this.configPublisherUrl + }; //let configObj={width: 480, height:360, framerate:30, bitrate:500, publishUrl:_publishUrl}; //let configObj={width: 480, height:360, framerate:30, bitrate:500, publishUrl:'rtmp://txlivepush.xuedianyun.com/live/markettest_395312484_T8440_983041_1507888360?bizid=11220&txSecret=15cc50d93f86f9e1a2a76a10db2b09a8&txTime=59e135a8&record=hls|flv&record_interval=5400'}; - loger.warn("设置旁路地址->",configObj); + loger.warn("设置旁路地址->", configObj); this.client.configPublisher(configObj); - }else { - loger.warn("设置旁路地址->失败->为初始化或旁路地址无效",_publishUrl); + } else { + loger.warn("设置旁路地址->失败->为初始化或旁路地址无效", _publishUrl); } } + /* * 设置旁录拉流地址 * */ - setRtmpM3u8Path(_param){ + setRtmpM3u8Path(_param) { //_webRtc.setRtmpM3u8Path({m3u8Url:m3u8Stream,rtmpUrl:rtmpStream}); - loger.log("设置旁录拉流地址",_param); - if(_param){ - this.m3u8Url=_param.m3u8Url||""; - this.rtmpUrl=_param.rtmpUrl||""; + loger.log("设置旁录拉流地址", _param); + if (_param) { + this.m3u8Url = _param.m3u8Url || ""; + this.rtmpUrl = _param.rtmpUrl || ""; } } + publish(_params) { if (!this.client || !this.localStream) { loger.warn("推流失败->未加入频道!"); @@ -504,13 +522,13 @@ class WebRtcApe extends Emiter { return; } //老师-助教-主讲人-->设置旁路大于30秒没有推流,推流服务会停止,需要重设旁录和重加频道; - if(this.setConfigTimestamp>0&&GlobalConfig.isTeachOrAssistant){ + if (this.setConfigTimestamp > 0 && GlobalConfig.isTeachOrAssistant) { //如果间隔大于28秒并且没有推过流需要重新加入频道,推成功一次之后就不需要 - let interval=parseInt(new Date().getTime()/1000-this.setConfigTimestamp); - loger.log("设置旁路的时间和推流时间的间隔->"+interval+"秒 firstPublishSuccess:"+ this.firstPublishSuccess); - if(interval>=28&&!this.firstPublishSuccess){ + let interval = parseInt(new Date().getTime() / 1000 - this.setConfigTimestamp); + loger.log("设置旁路的时间和推流时间的间隔->" + interval + "秒 firstPublishSuccess:" + this.firstPublishSuccess); + if (interval >= 28 && !this.firstPublishSuccess) { loger.warn("设置旁路大于30秒没有推流,推流服务会停止,需要重设旁录和重加频道"); - this._emit(WebRtcApe.RE_JOIN_CHANNEL,{publish:true}); + this._emit(WebRtcApe.RE_JOIN_CHANNEL, {publish: true}); return; } } @@ -520,9 +538,9 @@ class WebRtcApe extends Emiter { let viewName = 'localVideoBox_' + this.uid; let videoBox = document.createElement("div"); videoBox.id = viewName; - videoBox.className=this.localWebRtcVideoClass; - videoBox.style.width = (this.localVideoWidth*this.videoScale) + 'px'; - videoBox.style.height = (this.localVideoHeight*this.videoScale) + 'px'; + videoBox.className = this.localWebRtcVideoClass; + videoBox.style.width = (this.localVideoWidth * this.videoScale) + 'px'; + videoBox.style.height = (this.localVideoHeight * this.videoScale) + 'px'; videoBox.style.float = 'left'; videoBox.style.marginRight = "1px"; videoBox.style.pointerEvents = 'none'; @@ -536,8 +554,14 @@ class WebRtcApe extends Emiter { if (user) { userName = user.name || ""; } - let nameDiv = `<div id="${"videoOwnerName_" + this.uid}" class="localVideoOwnerName" style="width:${this.localVideoWidth}px;height:20px; position: absolute;bottom: 0; z-index: 1;overflow:hidden;font-size: 14px;text-align: right; color: #cccccc;display:${this.nameDisplay}">${userName}</div>`; + let audioMutedDiv = ""; + if (GlobalConfig.isTeachOrAssistant) { + audioMutedDiv = `<div class="audioMuted audioOpen" id=${"audioMuted_"+this.uid}></div>`; + } + let nameDiv = `<div id="${"videoOwnerName_" + this.uid}" class="localVideoOwnerName" style="width:${this.localVideoWidth}px;height:22px; position: absolute;bottom: 2px; z-index: 1;overflow:hidden;font-size: 14px;text-align: right; vertical-align: middle;background-color: #2926251a;color: #e7e7e7;display:${this.nameDisplay}">${userName + audioMutedDiv}</div>`; $(this.localViewId).prepend(nameDiv); + $(".audioMuted").off("click", this._clickAudioMuted.bind(this)); + $(".audioMuted").on("click", this._clickAudioMuted.bind(this)); loger.log("webRtc->推流->", viewName, new Date().getTime()); this.localStream.play(viewName); @@ -553,31 +577,31 @@ class WebRtcApe extends Emiter { this.isPublish = false; this.clearLocalView(); this._emit(MessageTypes.WEB_RTC_PUBLISH_FAILED, err); - if(err) { + if (err) { switch (err.msg) { case "DEVICES_NOT_FOUND": - this.curCameraId=""; - this.curMicrophoneId=""; + this.curCameraId = ""; + this.curMicrophoneId = ""; this.unpublish(); break; case "NOT_SUPPORTED": - this.curCameraId=""; - this.curMicrophoneId=""; + this.curCameraId = ""; + this.curMicrophoneId = ""; this.unpublish(); break; case "PERMISSION_DENIED": - this.curCameraId=""; - this.curMicrophoneId=""; + this.curCameraId = ""; + this.curMicrophoneId = ""; this.unpublish(); break; case "CONSTRAINT_NOT_SATISFIED": - this.curCameraId=""; - this.curMicrophoneId=""; + this.curCameraId = ""; + this.curMicrophoneId = ""; this.unpublish(); break; case "STREAM_ALREADY_INITIALIZED": - this.curCameraId=""; - this.curMicrophoneId=""; + this.curCameraId = ""; + this.curMicrophoneId = ""; this.unpublish(); break; default: @@ -617,38 +641,38 @@ class WebRtcApe extends Emiter { $('#localVideoBox_' + this.uid).remove(); $("#videoOwnerName_" + this.uid).remove(); $(".localVideoOwnerName").remove(); - $("."+this.localWebRtcVideoClass).remove(); + $("." + this.localWebRtcVideoClass).remove(); } /* * 更新所有视频的尺寸大小 * */ - updateAllVideoSize(){ - $("."+this.localWebRtcVideoClass).css("width",this.localVideoWidth*this.videoScale); - $("."+this.localWebRtcVideoClass).css("height",this.localVideoHeight*this.videoScale); + updateAllVideoSize() { + $("." + this.localWebRtcVideoClass).css("width", this.localVideoWidth * this.videoScale); + $("." + this.localWebRtcVideoClass).css("height", this.localVideoHeight * this.videoScale); - $("."+this.hostWebRtcVideoClass).css("width",this.hostRemoteVideoWidth*this.videoScale); - $("."+this.hostWebRtcVideoClass).css("height",this.hostRemoteVideoHeight*this.videoScale); + $("." + this.hostWebRtcVideoClass).css("width", this.hostRemoteVideoWidth * this.videoScale); + $("." + this.hostWebRtcVideoClass).css("height", this.hostRemoteVideoHeight * this.videoScale); - $("."+this.normalWebRtcVideoClass).css("width",this.normalRemoteVideoWidth*this.videoScale); - $("."+this.normalWebRtcVideoClass).css("height",this.normalRemoteVideoHeight*this.videoScale); + $("." + this.normalWebRtcVideoClass).css("width", this.normalRemoteVideoWidth * this.videoScale); + $("." + this.normalWebRtcVideoClass).css("height", this.normalRemoteVideoHeight * this.videoScale); } /* - * 设置rtc视频的属性 - * */ - changeRtcVideoConfig(_params){ + * 设置rtc视频的属性 + * */ + changeRtcVideoConfig(_params) { //{videoScale:1} - if(!_params){ + if (!_params) { return; } - let scale=parseInt(_params.videoScale)||1;//最小值只能为1,这个是按倍数缩放视频 - if(this.videoScale==scale){ + let scale = parseInt(_params.videoScale) || 1;//最小值只能为1,这个是按倍数缩放视频 + if (this.videoScale == scale) { return; } - this.videoScale=scale; - loger.log("更新视频视图大小->videoScale:"+this.videoScale); + this.videoScale = scale; + loger.log("更新视频视图大小->videoScale:" + this.videoScale); this.updateAllVideoSize(); } @@ -663,8 +687,8 @@ class WebRtcApe extends Emiter { this.localVideoHeight = parseInt(_params.height) || SIZE_360; this.nameDisplay = _params.nameDisplay || "block"; - this.localVideoWidth=this.localVideoWidth; - this.localVideoHeight=this.localVideoHeight; + this.localVideoWidth = this.localVideoWidth; + this.localVideoHeight = this.localVideoHeight; } /* @@ -676,8 +700,8 @@ class WebRtcApe extends Emiter { this.hostRemoteStyle = _params.styleStr || ""; this.hostRemoteVideoWidth = parseInt(_params.width) || SIZE_480; this.hostRemoteVideoHeight = parseInt(_params.height) || SIZE_360; - this.hostRemoteVideoWidth=this.hostRemoteVideoWidth; - this.hostRemoteVideoHeight=this.hostRemoteVideoHeight; + this.hostRemoteVideoWidth = this.hostRemoteVideoWidth; + this.hostRemoteVideoHeight = this.hostRemoteVideoHeight; } /* @@ -690,8 +714,8 @@ class WebRtcApe extends Emiter { this.normalRemoteVideoWidth = parseInt(_params.width) || SIZE_480; this.normalRemoteVideoHeight = parseInt(_params.height) || SIZE_360; - this.normalRemoteVideoWidth=this.normalRemoteVideoWidth; - this.normalRemoteVideoHeight=this.normalRemoteVideoHeight; + this.normalRemoteVideoWidth = this.normalRemoteVideoWidth; + this.normalRemoteVideoHeight = this.normalRemoteVideoHeight; } /* @@ -708,12 +732,12 @@ class WebRtcApe extends Emiter { /* * 设置旁录推流的参数 * */ - setConfigPublisher(_params){ - loger.log("设置旁录推流的参数",_params); - this.pWidth=_params.width||SIZE_480; - this.pHeight=_params.height||SIZE_360; - this.pFrameRate=_params.frameRate||30; - this.pBitrate=_params.bitrate||500; + setConfigPublisher(_params) { + loger.log("设置旁录推流的参数", _params); + this.pWidth = _params.width || SIZE_480; + this.pHeight = _params.height || SIZE_360; + this.pFrameRate = _params.frameRate || 30; + this.pBitrate = _params.bitrate || 500; } /* @@ -776,7 +800,7 @@ class WebRtcApe extends Emiter { for (let i = 0; i < devices.length; i++) { let device = devices[i]; //{"deviceId":"default","kind":"audiooutput","label":"默认","groupId":"cf49a03ca26700235629fc13d3e6630bd34407c66438d157056a34dd3ae03ef5"} - if(device){ + if (device) { if (device.kind == 'audioinput') { this.microphones.push(device); GlobalConfig.microphones.push(device.label); @@ -796,13 +820,107 @@ class WebRtcApe extends Emiter { }); } + _clickAudioMuted(evt) { + let className = evt.currentTarget.className; + console.log("点击禁音",evt.currentTarget.id); + let idArr = (evt.currentTarget.id).split("_"); + let uid = 10000000;//默认设置一个不存在的uid + if (idArr && idArr.length > 1) { + uid = parseInt(idArr[1]); + } + if (className.indexOf("audioOpen") > 0) { + loger.log("点击禁音->"+uid); + // evt.currentTarget.className="audioMuted audioClose"; + if (uid != GlobalConfig.nodeId) { + this.sendChangeUserMediaEnabled({nodeId: uid, video: true, audio: false}); + $("#audioMuted_" + uid).removeClass(); + $("#audioMuted_" + uid).addClass("audioMuted audioClose"); + } else { + this.disableAudio(uid); + } + } else { + loger.log("点击开启声音"); + //evt.currentTarget.className="audioMuted audioOpen"; + if (uid != GlobalConfig.nodeId) { + this.sendChangeUserMediaEnabled({nodeId: uid, video: true, audio: true}); + $("#audioMuted_" + uid).removeClass(); + $("#audioMuted_" + uid).addClass("audioMuted audioOpen"); + } else { + this.enableAudio(uid); + } + } + } + + /* + * 发送控制音视频禁用消息 + * */ + sendChangeUserMediaEnabled(_param) { + this._emit(MessageTypes.MEDIA_ENABLED_CHANGE, _param); + } + + /* + * 收到控制音视频禁用消息 + * */ + webRtcMeiaEnabledChange(_data) { + loger.log("收到控制音视频禁用消息",_data); + // {nodeId: uid, video: true, audio: false} + if(!_data){ + return; + } + if(_data.nodeId!=GlobalConfig.nodeId){ + if(_data.audio==false){ + $("#audioMuted_" + _data.nodeId).removeClass(); + $("#audioMuted_" + _data.nodeId).addClass("audioMuted audioClose"); + }else { + $("#audioMuted_" + _data.nodeId).removeClass(); + $("#audioMuted_" + _data.nodeId).addClass("audioMuted audioOpen"); + } + + }else{ + if(_data.audio==false){ + this.disableAudio(_data.nodeId); + }else { + this.enableAudio(_data.nodeId); + } + } + + } + + /* + * 开启禁音 + * */ + disableAudio(uid) { + loger.log("开启禁音:" + uid); + if (parseInt(uid) == GlobalConfig.nodeId) { + if (this.localStream) { + $("#audioMuted_" + uid).removeClass(); + $("#audioMuted_" + uid).addClass("audioMuted audioClose"); + this.localStream.disableAudio(); + } + } + } + + /* + * 开启音频 + * */ + enableAudio(_uid) { + loger.log("开启音频:" + _uid); + if (parseInt(_uid) == GlobalConfig.nodeId) { + if (this.localStream) { + this.localStream.enableAudio(); + $("#audioMuted_" + _uid).removeClass(); + $("#audioMuted_" + _uid).addClass("audioMuted audioOpen"); + } + } + } + //组织数据,发送给服务器,控制录制和开启录制-推流和停止推流 status:0 停止推流 1:开始推流(同时开启录制),2:停止录制(同时停止推流) packMediaInfoData(_status) { let curTimestamp = new Date().getTime(); let data = `appId=${GlobalConfig.appId}&channel=${GlobalConfig.channelId}&channelKey=${GlobalConfig.appCertificate}&uid=${GlobalConfig.userUid}&status=${_status}&userId=${GlobalConfig.userId}&userName=${GlobalConfig.userName}&userRole=${GlobalConfig.userRole}×tamp=${curTimestamp}&recordTimestamp=${GlobalConfig.recordTimestamp}`; //markettest_623790840_T9540_1508207080 - let streamId=GlobalConfig.siteId+"_"+GlobalConfig.classId+"_"+GlobalConfig.userId+"_"+curTimestamp; + let streamId = GlobalConfig.siteId + "_" + GlobalConfig.classId + "_" + GlobalConfig.userId + "_" + curTimestamp; //mcu记录一份数据 this._emit(MessageTypes.MEDIA_PUBLISH_STATUS_CHANGE, { appId: GlobalConfig.appId, @@ -815,10 +933,10 @@ class WebRtcApe extends Emiter { userRole: GlobalConfig.userRole, timestamp: curTimestamp, recordTimestamp: GlobalConfig.recordTimestamp, - streamId:streamId, - publishUrl:this.configPublisherUrl, - m3u8Url:this.m3u8Url, - rtmpUrl:this.rtmpUrl + streamId: streamId, + publishUrl: this.configPublisherUrl, + m3u8Url: this.m3u8Url, + rtmpUrl: this.rtmpUrl }); return data; } @@ -833,7 +951,7 @@ class WebRtcApe extends Emiter { } let url = GlobalConfig.locationProtocol + GlobalConfig.recordInterfaces; let data = this.packMediaInfoData(_status); - loger.log("调用服务器端更新视频录制状态->status",_status); + loger.log("调用服务器端更新视频录制状态->status", _status); fetch(encodeURI(url), { method: 'POST', headers: { @@ -863,14 +981,14 @@ class WebRtcApe extends Emiter { } /* - * 切换音视频的录制状态 - * */ + * 切换音视频的录制状态 + * */ changeMediaRecordStatus(_params) { - if (!GlobalConfig.recordInterfaces||!_params) { - loger.warn("切换音视频的录制状态->失败->接口地址无效",_params); + if (!GlobalConfig.recordInterfaces || !_params) { + loger.warn("切换音视频的录制状态->失败->接口地址无效", _params); return; } - loger.warn("切换音视频的录制状态->"+_params); + loger.warn("切换音视频的录制状态->" + _params); let url = GlobalConfig.locationProtocol + GlobalConfig.recordInterfaces; let curTimestamp = new Date().getTime(); let data = `appId=${GlobalConfig.appId}&channel=${GlobalConfig.channelId}&channelKey=${GlobalConfig.appCertificate}&uid=${GlobalConfig.userUid}&status=${_params.status}&userId=${GlobalConfig.userId}&userName=${GlobalConfig.userName}&userRole=${GlobalConfig.userRole}×tamp=${curTimestamp}&recordTimestamp=${GlobalConfig.recordTimestamp}`; diff --git a/src/assets/css/mcuStyle.css b/src/assets/css/mcuStyle.css new file mode 100644 index 0000000..f150bdc --- /dev/null +++ b/src/assets/css/mcuStyle.css @@ -0,0 +1,18 @@ +@charset "utf-8"; + +/*禁音按钮*/ +.audioMuted{ + margin-left: 6px; + float: right; + width: 20px; + height: 20px; + cursor: pointer; + background-size: auto; + background-repeat: no-repeat; +} +.audioClose { + background-image: url(../../assets/img/audioClose.png); +} +.audioOpen { + background-image: url(../../assets/img/audioOpen.png); +} \ No newline at end of file diff --git a/src/assets/img/audioClose.png b/src/assets/img/audioClose.png new file mode 100644 index 0000000..35d9a01 Binary files /dev/null and b/src/assets/img/audioClose.png differ diff --git a/src/assets/img/audioOpen.png b/src/assets/img/audioOpen.png new file mode 100644 index 0000000..241330f Binary files /dev/null and b/src/assets/img/audioOpen.png differ