正在显示
9 个修改的文件
包含
471 行增加
和
257 行删除
| @@ -63,7 +63,7 @@ export default class MessageEntrance extends Emiter { | @@ -63,7 +63,7 @@ export default class MessageEntrance extends Emiter { | ||
| 63 | super(); | 63 | super(); |
| 64 | this.lastClassActiveTime=0;//最后一次课堂激活的时间戳 | 64 | this.lastClassActiveTime=0;//最后一次课堂激活的时间戳 |
| 65 | //sdk 信息 | 65 | //sdk 信息 |
| 66 | - GlobalConfig.sdkVersion = "v2.32.1.20171123"; | 66 | + GlobalConfig.sdkVersion = "v2.33.6.20171123"; |
| 67 | loger.warn("sdkVersion:" + GlobalConfig.sdkVersion); | 67 | loger.warn("sdkVersion:" + GlobalConfig.sdkVersion); |
| 68 | console.log("sdkVersion:" + GlobalConfig.sdkVersion); | 68 | console.log("sdkVersion:" + GlobalConfig.sdkVersion); |
| 69 | //设置 | 69 | //设置 |
| @@ -102,6 +102,7 @@ export default class MessageEntrance extends Emiter { | @@ -102,6 +102,7 @@ export default class MessageEntrance extends Emiter { | ||
| 102 | _webRtc.on(MessageTypes.USER_DEVICE_STATUS_CHAANGE, this.userDeviecStatusChange.bind(this)); //监听摄像头和麦克风的开启状态 | 102 | _webRtc.on(MessageTypes.USER_DEVICE_STATUS_CHAANGE, this.userDeviecStatusChange.bind(this)); //监听摄像头和麦克风的开启状态 |
| 103 | _webRtc.on(MessageTypes.MEDIA_PUBLISH_STATUS_CHANGE, this.mediaPublishStatusChange.bind(this)); //webRtc推流状态发生改变 | 103 | _webRtc.on(MessageTypes.MEDIA_PUBLISH_STATUS_CHANGE, this.mediaPublishStatusChange.bind(this)); //webRtc推流状态发生改变 |
| 104 | _webRtc.on(WebRtcApe.RE_JOIN_CHANNEL, this._webRtcRejoinChannel.bind(this)); //重先加入音视频频道 | 104 | _webRtc.on(WebRtcApe.RE_JOIN_CHANNEL, this._webRtcRejoinChannel.bind(this)); //重先加入音视频频道 |
| 105 | + _webRtc.on(MessageTypes.MEDIA_ENABLED_CHANGE, this._mediaEnabledChange.bind(this)); //音视频禁用状态改变 | ||
| 105 | 106 | ||
| 106 | // Sass平台层 | 107 | // Sass平台层 |
| 107 | _sass = Sass; | 108 | _sass = Sass; |
| @@ -152,6 +153,7 @@ export default class MessageEntrance extends Emiter { | @@ -152,6 +153,7 @@ export default class MessageEntrance extends Emiter { | ||
| 152 | //_confer_ape.on(MessageTypes.SWITCH_HLS_IP, this._switchHlsIpHandler.bind(this)); //MS HLS动态选点 | 153 | //_confer_ape.on(MessageTypes.SWITCH_HLS_IP, this._switchHlsIpHandler.bind(this)); //MS HLS动态选点 |
| 153 | _confer_ape.on(MessageTypes.STOP_ALL_MEDIA_PUBLISH, this._stopAllMediaPublishHandler.bind(this)); //课堂状态发生改变,需要停止当前的所有推流 | 154 | _confer_ape.on(MessageTypes.STOP_ALL_MEDIA_PUBLISH, this._stopAllMediaPublishHandler.bind(this)); //课堂状态发生改变,需要停止当前的所有推流 |
| 154 | _confer_ape.on(MessageTypes.CLASS_UPDATE_ROSTER, this._onRosterUpdateHandler.bind(this)); | 155 | _confer_ape.on(MessageTypes.CLASS_UPDATE_ROSTER, this._onRosterUpdateHandler.bind(this)); |
| 156 | + _confer_ape.on(MessageTypes.RECEIVE_MEDIA_ENABLED_CHANGE, this._receiveMeiaEnabledChange.bind(this)); | ||
| 155 | 157 | ||
| 156 | _chat_ape = new ChatApe(); | 158 | _chat_ape = new ChatApe(); |
| 157 | _chat_ape.on('*', (type, data) => this._emit(type, data)); | 159 | _chat_ape.on('*', (type, data) => this._emit(type, data)); |
| @@ -651,6 +653,8 @@ export default class MessageEntrance extends Emiter { | @@ -651,6 +653,8 @@ export default class MessageEntrance extends Emiter { | ||
| 651 | randUserId = "A" + randUserId; | 653 | randUserId = "A" + randUserId; |
| 652 | } else if (GlobalConfig.userRole == ApeConsts.presenter) { | 654 | } else if (GlobalConfig.userRole == ApeConsts.presenter) { |
| 653 | randUserId = "P" + randUserId; | 655 | randUserId = "P" + randUserId; |
| 656 | + } else if (GlobalConfig.userRole == ApeConsts.invisible) { | ||
| 657 | + randUserId = "I" + randUserId; | ||
| 654 | } else { | 658 | } else { |
| 655 | randUserId = "S" + randUserId; | 659 | randUserId = "S" + randUserId; |
| 656 | } | 660 | } |
| @@ -700,7 +704,8 @@ export default class MessageEntrance extends Emiter { | @@ -700,7 +704,8 @@ export default class MessageEntrance extends Emiter { | ||
| 700 | let autoLoginMd5 = MD5("" + GlobalConfig.classId + GlobalConfig.paramUserId + GlobalConfig.userRole); | 704 | let autoLoginMd5 = MD5("" + GlobalConfig.classId + GlobalConfig.paramUserId + GlobalConfig.userRole); |
| 701 | //let autoLoginMd5 = MD5("" + GlobalConfig.classId + GlobalConfig.userId + GlobalConfig.userRole); | 705 | //let autoLoginMd5 = MD5("" + GlobalConfig.classId + GlobalConfig.userId + GlobalConfig.userRole); |
| 702 | //loger.log("joinClass-GlobalConfig.autoLogin", GlobalConfig.autoLogin, "autoLoginMd5-", autoLoginMd5); | 706 | //loger.log("joinClass-GlobalConfig.autoLogin", GlobalConfig.autoLogin, "autoLoginMd5-", autoLoginMd5); |
| 703 | - if (GlobalConfig.autoLogin && autoLoginMd5 == GlobalConfig.autoLogin || GlobalConfig.isInvisible) { | 707 | + if ((GlobalConfig.autoLogin && autoLoginMd5 == GlobalConfig.autoLogin) || |
| 708 | + GlobalConfig.isInvisible||GlobalConfig.isAssistant||GlobalConfig.isPresenter) { | ||
| 704 | // MD5(classId+userId+userRole)==m | 709 | // MD5(classId+userId+userRole)==m |
| 705 | //自动登录,跳过验证流程 | 710 | //自动登录,跳过验证流程 |
| 706 | loger.log("自动登录->" + GlobalConfig.userRole); | 711 | loger.log("自动登录->" + GlobalConfig.userRole); |
| @@ -1404,7 +1409,17 @@ export default class MessageEntrance extends Emiter { | @@ -1404,7 +1409,17 @@ export default class MessageEntrance extends Emiter { | ||
| 1404 | _webRtc.tryAddMobileStream(_data.nodeId); | 1409 | _webRtc.tryAddMobileStream(_data.nodeId); |
| 1405 | } | 1410 | } |
| 1406 | } | 1411 | } |
| 1407 | - | 1412 | + /* |
| 1413 | + * 收到媒体禁用状态切换的消息 | ||
| 1414 | + * */ | ||
| 1415 | + _receiveMeiaEnabledChange(_data){ | ||
| 1416 | + if(!_data||GlobalConfig.isRecordPlayBack||GlobalConfig.isH5){ | ||
| 1417 | + return; | ||
| 1418 | + } | ||
| 1419 | + if(_webRtc){ | ||
| 1420 | + _webRtc.webRtcMeiaEnabledChange(_data); | ||
| 1421 | + } | ||
| 1422 | + } | ||
| 1408 | //手动切换MS -> {ip;"xxx.xx.xx","port":"xxxx"} | 1423 | //手动切换MS -> {ip;"xxx.xx.xx","port":"xxxx"} |
| 1409 | _switchMediaServer(_param) { | 1424 | _switchMediaServer(_param) { |
| 1410 | if (GlobalConfig.isRecordPlayBack) { | 1425 | if (GlobalConfig.isRecordPlayBack) { |
| @@ -2153,6 +2168,17 @@ export default class MessageEntrance extends Emiter { | @@ -2153,6 +2168,17 @@ export default class MessageEntrance extends Emiter { | ||
| 2153 | _webRtcRejoinChannel(_data){ | 2168 | _webRtcRejoinChannel(_data){ |
| 2154 | this._reJoinChannel(_data) | 2169 | this._reJoinChannel(_data) |
| 2155 | } | 2170 | } |
| 2171 | + /* | ||
| 2172 | + * 监听webRtc | ||
| 2173 | + * */ | ||
| 2174 | + _mediaEnabledChange(_data){ | ||
| 2175 | + if (!_mcu.connected||GlobalConfig.isRecordPlayBack) { | ||
| 2176 | + return; | ||
| 2177 | + } | ||
| 2178 | + if(_confer_ape){ | ||
| 2179 | + _confer_ape.sendMediaEnabledChange(_data); | ||
| 2180 | + } | ||
| 2181 | + } | ||
| 2156 | 2182 | ||
| 2157 | //监听摄像头麦克风状态 | 2183 | //监听摄像头麦克风状态 |
| 2158 | userDeviecStatusChange(_data) { | 2184 | userDeviecStatusChange(_data) { |
| @@ -4,26 +4,28 @@ | @@ -4,26 +4,28 @@ | ||
| 4 | import Base64 from 'base64-js'; | 4 | import Base64 from 'base64-js'; |
| 5 | import UTF8 from 'utf-8'; | 5 | import UTF8 from 'utf-8'; |
| 6 | 6 | ||
| 7 | -class EngineUtils{ | ||
| 8 | - static isEmptyObject(O){ | ||
| 9 | - for (var x in O){ | 7 | +class EngineUtils { |
| 8 | + static isEmptyObject(O) { | ||
| 9 | + for (var x in O) { | ||
| 10 | return false; | 10 | return false; |
| 11 | } | 11 | } |
| 12 | return true; | 12 | return true; |
| 13 | } | 13 | } |
| 14 | - static arrayToJsonString(_param){ | ||
| 15 | - try{ | 14 | + |
| 15 | + static arrayToJsonString(_param) { | ||
| 16 | + try { | ||
| 16 | return JSON.stringify(_param); | 17 | return JSON.stringify(_param); |
| 17 | - }catch (err){ | ||
| 18 | - console.warn("数组转JSON失败->ERROR:"+err.message); | 18 | + } catch (err) { |
| 19 | + console.warn("数组转JSON失败->ERROR:" + err.message); | ||
| 19 | } | 20 | } |
| 20 | return ""; | 21 | return ""; |
| 21 | } | 22 | } |
| 22 | - static arrayFromJsonString(_param){ | ||
| 23 | - try{ | 23 | + |
| 24 | + static arrayFromJsonString(_param) { | ||
| 25 | + try { | ||
| 24 | return JSON.parse(_param); | 26 | return JSON.parse(_param); |
| 25 | - }catch (err){ | ||
| 26 | - console.warn("JSON转数组失败->ERROR:"+err.message); | 27 | + } catch (err) { |
| 28 | + console.warn("JSON转数组失败->ERROR:" + err.message); | ||
| 27 | } | 29 | } |
| 28 | return []; | 30 | return []; |
| 29 | } | 31 | } |
| @@ -31,182 +33,190 @@ class EngineUtils{ | @@ -31,182 +33,190 @@ class EngineUtils{ | ||
| 31 | /* | 33 | /* |
| 32 | * 生成随机数 _part 段数 默认3段; splitStr分隔符 | 34 | * 生成随机数 _part 段数 默认3段; splitStr分隔符 |
| 33 | * */ | 35 | * */ |
| 34 | - static creatRandomNum(_part=3,splitStr=""){ | ||
| 35 | - let randNumStr=""; | ||
| 36 | - for(let i=0;i<_part;i++){ | ||
| 37 | - randNumStr+=splitStr+parseInt(Math.random()*1000); | 36 | + static creatRandomNum(_part = 3, splitStr = "") { |
| 37 | + let randNumStr = ""; | ||
| 38 | + for (let i = 0; i < _part; i++) { | ||
| 39 | + randNumStr += splitStr + parseInt(Math.random() * 1000); | ||
| 38 | } | 40 | } |
| 39 | return randNumStr; | 41 | return randNumStr; |
| 40 | } | 42 | } |
| 41 | 43 | ||
| 42 | //生成时间戳后9位 保证唯一 | 44 | //生成时间戳后9位 保证唯一 |
| 43 | - static creatSoleNumberFromTimestamp(){ | 45 | + static creatSoleNumberFromTimestamp() { |
| 44 | let time = new Date().getTime(); | 46 | let time = new Date().getTime(); |
| 45 | - let timestamp= time % 1000000000;//time后9位 | 47 | + let timestamp = time % 1000000000;//time后9位 |
| 46 | return timestamp; | 48 | return timestamp; |
| 47 | } | 49 | } |
| 48 | 50 | ||
| 49 | //生成时间戳毫秒 | 51 | //生成时间戳毫秒 |
| 50 | - static creatTimestamp(){ | ||
| 51 | - let time = parseInt(new Date().getTime()/1000);//精确到秒 | 52 | + static creatTimestamp() { |
| 53 | + let time = parseInt(new Date().getTime() / 1000);//精确到秒 | ||
| 52 | return time; | 54 | return time; |
| 53 | } | 55 | } |
| 56 | + | ||
| 54 | //生成时间戳 string | 57 | //生成时间戳 string |
| 55 | - static creatTimestampStr(){ | 58 | + static creatTimestampStr() { |
| 56 | let curTime = new Date(); | 59 | let curTime = new Date(); |
| 57 | let timeStr = "" + curTime.getFullYear() + "-"; | 60 | let timeStr = "" + curTime.getFullYear() + "-"; |
| 58 | - timeStr += (curTime.getMonth()+1) + "-"; | 61 | + timeStr += (curTime.getMonth() + 1) + "-"; |
| 59 | timeStr += curTime.getDate() + "-"; | 62 | timeStr += curTime.getDate() + "-"; |
| 60 | - timeStr+=curTime.getHours() + "-"; | ||
| 61 | - timeStr+=curTime.getMinutes() + "-"; | ||
| 62 | - timeStr+=curTime.getSeconds(); | 63 | + timeStr += curTime.getHours() + "-"; |
| 64 | + timeStr += curTime.getMinutes() + "-"; | ||
| 65 | + timeStr += curTime.getSeconds(); | ||
| 63 | return timeStr; | 66 | return timeStr; |
| 64 | } | 67 | } |
| 68 | + | ||
| 65 | //根据时间戳字符串转换为时间戳 2017-10-27-15-38-15 | 69 | //根据时间戳字符串转换为时间戳 2017-10-27-15-38-15 |
| 66 | - static getTimestampFromStr(_timeStr){ | ||
| 67 | - if(!_timeStr) return this.creatTimestamp(); | ||
| 68 | - let timeArr=_timeStr.split("_"); | ||
| 69 | - if(timeArr&&timeArr.length==6){ | ||
| 70 | - return parseInt( new Date(timeArr[0],parseInt(timeArr[1])-1,timeArr[2],timeArr[3],timeArr[4],timeArr[5]).getTime()/1000); | 70 | + static getTimestampFromStr(_timeStr) { |
| 71 | + if (!_timeStr) return this.creatTimestamp(); | ||
| 72 | + let timeArr = _timeStr.split("_"); | ||
| 73 | + if (timeArr && timeArr.length == 6) { | ||
| 74 | + return parseInt(new Date(timeArr[0], parseInt(timeArr[1]) - 1, timeArr[2], timeArr[3], timeArr[4], timeArr[5]).getTime() / 1000); | ||
| 71 | } | 75 | } |
| 72 | return this.creatTimestamp(); | 76 | return this.creatTimestamp(); |
| 73 | } | 77 | } |
| 78 | + | ||
| 74 | //生成时间戳 格式:"20170209" | 79 | //生成时间戳 格式:"20170209" |
| 75 | - static creatTimestampYMD(){ | 80 | + static creatTimestampYMD() { |
| 76 | let curTime = new Date(); | 81 | let curTime = new Date(); |
| 77 | let year = "" + curTime.getFullYear(); | 82 | let year = "" + curTime.getFullYear(); |
| 78 | - let month = "" +(curTime.getMonth()+1); | 83 | + let month = "" + (curTime.getMonth() + 1); |
| 79 | let day = "" + curTime.getDate(); | 84 | let day = "" + curTime.getDate(); |
| 80 | 85 | ||
| 81 | - if(month.length<2){ | ||
| 82 | - month="0"+month; | 86 | + if (month.length < 2) { |
| 87 | + month = "0" + month; | ||
| 83 | } | 88 | } |
| 84 | - if(day.length<2){ | ||
| 85 | - day="0"+day; | 89 | + if (day.length < 2) { |
| 90 | + day = "0" + day; | ||
| 86 | } | 91 | } |
| 87 | - return year+month+day; | 92 | + return year + month + day; |
| 88 | } | 93 | } |
| 89 | - static objectToBase64(_object){ | ||
| 90 | - try{ | ||
| 91 | - let _objectStr=JSON.stringify(_object); | 94 | + |
| 95 | + static objectToBase64(_object) { | ||
| 96 | + try { | ||
| 97 | + let _objectStr = JSON.stringify(_object); | ||
| 92 | //console.log("objectToBase64------1----------") | 98 | //console.log("objectToBase64------1----------") |
| 93 | - let byte=UTF8.setBytesFromString(_objectStr); | 99 | + let byte = UTF8.setBytesFromString(_objectStr); |
| 94 | //console.log("objectToBase64------2----------") | 100 | //console.log("objectToBase64------2----------") |
| 95 | - let _objectBase64=Base64.fromByteArray(byte); | 101 | + let _objectBase64 = Base64.fromByteArray(byte); |
| 96 | return _objectBase64 | 102 | return _objectBase64 |
| 97 | - }catch (err){ | ||
| 98 | - console.log("objectToBase64 err:"+err.message); | 103 | + } catch (err) { |
| 104 | + console.log("objectToBase64 err:" + err.message); | ||
| 99 | return ""; | 105 | return ""; |
| 100 | } | 106 | } |
| 101 | return "" | 107 | return "" |
| 102 | } | 108 | } |
| 103 | - static objectFromBase64(_objectBase64){ | ||
| 104 | - try{ | ||
| 105 | - let byte=Base64.toByteArray(_objectBase64); | ||
| 106 | - let _objectStr=UTF8.getStringFromBytes(byte); | ||
| 107 | - let _object=JSON.parse(_objectStr); | 109 | + |
| 110 | + static objectFromBase64(_objectBase64) { | ||
| 111 | + try { | ||
| 112 | + let byte = Base64.toByteArray(_objectBase64); | ||
| 113 | + let _objectStr = UTF8.getStringFromBytes(byte); | ||
| 114 | + let _object = JSON.parse(_objectStr); | ||
| 108 | return _object | 115 | return _object |
| 109 | - }catch (err){ | ||
| 110 | - console.log("objectFromBase64 err:"+err.message); | 116 | + } catch (err) { |
| 117 | + console.log("objectFromBase64 err:" + err.message); | ||
| 111 | return null; | 118 | return null; |
| 112 | } | 119 | } |
| 113 | return null; | 120 | return null; |
| 114 | } | 121 | } |
| 122 | + | ||
| 115 | //优化压缩坐标点数组,去除一些连续重复坐标的点,返回一个数组 | 123 | //优化压缩坐标点数组,去除一些连续重复坐标的点,返回一个数组 |
| 116 | - static compressPoint(_arr){ | ||
| 117 | - if(!_arr){ | 124 | + static compressPoint(_arr) { |
| 125 | + if (!_arr) { | ||
| 118 | return []; | 126 | return []; |
| 119 | } | 127 | } |
| 120 | - if(_arr.length<5){ | 128 | + if (_arr.length < 5) { |
| 121 | //点,直线,矩形 坐标点小于5不需要处理 | 129 | //点,直线,矩形 坐标点小于5不需要处理 |
| 122 | return _arr; | 130 | return _arr; |
| 123 | } | 131 | } |
| 124 | - let tempPointArr=_arr; | ||
| 125 | - let newPointArr=[]; | 132 | + let tempPointArr = _arr; |
| 133 | + let newPointArr = []; | ||
| 126 | newPointArr.push(tempPointArr[0]); | 134 | newPointArr.push(tempPointArr[0]); |
| 127 | - let lastW=tempPointArr[0].w; | ||
| 128 | - let continueNum=0;//坐标相同的连续次数 | 135 | + let lastW = tempPointArr[0].w; |
| 136 | + let continueNum = 0;//坐标相同的连续次数 | ||
| 129 | //先筛除水平方向的连续重复坐标点 | 137 | //先筛除水平方向的连续重复坐标点 |
| 130 | - let len=tempPointArr.length-1; | ||
| 131 | - for(let i=1;i<len;i++){ | ||
| 132 | - let item=tempPointArr[i]; | ||
| 133 | - if(item&&item.w!=lastW){ | ||
| 134 | - lastW=item.w; | ||
| 135 | - if(continueNum>0){ | ||
| 136 | - newPointArr.push(tempPointArr[i-1]); | 138 | + let len = tempPointArr.length - 1; |
| 139 | + for (let i = 1; i < len; i++) { | ||
| 140 | + let item = tempPointArr[i]; | ||
| 141 | + if (item && item.w != lastW) { | ||
| 142 | + lastW = item.w; | ||
| 143 | + if (continueNum > 0) { | ||
| 144 | + newPointArr.push(tempPointArr[i - 1]); | ||
| 137 | } | 145 | } |
| 138 | newPointArr.push(item); | 146 | newPointArr.push(item); |
| 139 | - continueNum=0; | ||
| 140 | - }else { | 147 | + continueNum = 0; |
| 148 | + } else { | ||
| 141 | continueNum++; | 149 | continueNum++; |
| 142 | } | 150 | } |
| 143 | } | 151 | } |
| 144 | //如果最终的坐标点数量小于2,需要把最后一个坐标点添加 | 152 | //如果最终的坐标点数量小于2,需要把最后一个坐标点添加 |
| 145 | - if(tempPointArr[len]){ | 153 | + if (tempPointArr[len]) { |
| 146 | newPointArr.push(tempPointArr[len]); | 154 | newPointArr.push(tempPointArr[len]); |
| 147 | } | 155 | } |
| 148 | //如果坐标点已经小于等于2不需要继续检测 | 156 | //如果坐标点已经小于等于2不需要继续检测 |
| 149 | - if(newPointArr.length<=2){ | 157 | + if (newPointArr.length <= 2) { |
| 150 | return newPointArr; | 158 | return newPointArr; |
| 151 | } | 159 | } |
| 152 | 160 | ||
| 153 | //筛除水垂直向的连续重复坐标点 | 161 | //筛除水垂直向的连续重复坐标点 |
| 154 | - let finalPointArr=[]; | 162 | + let finalPointArr = []; |
| 155 | finalPointArr.push(newPointArr[0]); | 163 | finalPointArr.push(newPointArr[0]); |
| 156 | - let lastH=newPointArr[0].h; | ||
| 157 | - continueNum=0; | ||
| 158 | - len=newPointArr.length-1; | ||
| 159 | - for(let k=1;k<len;k++){ | ||
| 160 | - let item=newPointArr[k]; | ||
| 161 | - if(item&&item.h!=lastH){ | ||
| 162 | - lastH=item.h; | ||
| 163 | - if(continueNum>0){ | ||
| 164 | - finalPointArr.push(newPointArr[k-1]); | 164 | + let lastH = newPointArr[0].h; |
| 165 | + continueNum = 0; | ||
| 166 | + len = newPointArr.length - 1; | ||
| 167 | + for (let k = 1; k < len; k++) { | ||
| 168 | + let item = newPointArr[k]; | ||
| 169 | + if (item && item.h != lastH) { | ||
| 170 | + lastH = item.h; | ||
| 171 | + if (continueNum > 0) { | ||
| 172 | + finalPointArr.push(newPointArr[k - 1]); | ||
| 165 | } | 173 | } |
| 166 | finalPointArr.push(item); | 174 | finalPointArr.push(item); |
| 167 | - continueNum=0; | ||
| 168 | - }else { | 175 | + continueNum = 0; |
| 176 | + } else { | ||
| 169 | continueNum++; | 177 | continueNum++; |
| 170 | } | 178 | } |
| 171 | } | 179 | } |
| 172 | - if(newPointArr[len]){ | 180 | + if (newPointArr[len]) { |
| 173 | finalPointArr.push(newPointArr[len]); | 181 | finalPointArr.push(newPointArr[len]); |
| 174 | } | 182 | } |
| 175 | return finalPointArr; | 183 | return finalPointArr; |
| 176 | } | 184 | } |
| 185 | + | ||
| 177 | //压缩数据,把坐标点数组转换为字符串,返回字符串 | 186 | //压缩数据,把坐标点数组转换为字符串,返回字符串 |
| 178 | - static optimizePoint(_pointGroup){ | ||
| 179 | - if(!_pointGroup){ | 187 | + static optimizePoint(_pointGroup) { |
| 188 | + if (!_pointGroup) { | ||
| 180 | return ""; | 189 | return ""; |
| 181 | } | 190 | } |
| 182 | - let tempStr=""; | 191 | + let tempStr = ""; |
| 183 | try { | 192 | try { |
| 184 | - tempStr=JSON.stringify(_pointGroup); | ||
| 185 | - }catch (err){ | 193 | + tempStr = JSON.stringify(_pointGroup); |
| 194 | + } catch (err) { | ||
| 186 | return ""; | 195 | return ""; |
| 187 | } | 196 | } |
| 188 | - let regexp1=/},{"w":/g; | ||
| 189 | - let regexp2=/,"h":/g; | ||
| 190 | - tempStr=tempStr.replace(regexp1,"#"); | ||
| 191 | - tempStr=tempStr.replace(regexp2,"&"); | 197 | + let regexp1 = /},{"w":/g; |
| 198 | + let regexp2 = /,"h":/g; | ||
| 199 | + tempStr = tempStr.replace(regexp1, "#"); | ||
| 200 | + tempStr = tempStr.replace(regexp2, "&"); | ||
| 192 | //console.log("标注压缩后的字符长度->",tempStr.length); | 201 | //console.log("标注压缩后的字符长度->",tempStr.length); |
| 193 | return tempStr; | 202 | return tempStr; |
| 194 | } | 203 | } |
| 204 | + | ||
| 195 | //把字符串坐标点集合转换为数组,返回数组的JSON | 205 | //把字符串坐标点集合转换为数组,返回数组的JSON |
| 196 | - static unPackOptimizePoint(_str){ | ||
| 197 | - if(!_str){ | 206 | + static unPackOptimizePoint(_str) { |
| 207 | + if (!_str) { | ||
| 198 | return ""; | 208 | return ""; |
| 199 | } | 209 | } |
| 200 | - let tempStr=_str; | ||
| 201 | - let regexp1=/#/g; | ||
| 202 | - let regexp2=/&/g; | ||
| 203 | - tempStr=tempStr.replace(regexp1,'},{"w":'); | ||
| 204 | - tempStr=tempStr.replace(regexp2,',"h":'); | 210 | + let tempStr = _str; |
| 211 | + let regexp1 = /#/g; | ||
| 212 | + let regexp2 = /&/g; | ||
| 213 | + tempStr = tempStr.replace(regexp1, '},{"w":'); | ||
| 214 | + tempStr = tempStr.replace(regexp2, ',"h":'); | ||
| 205 | 215 | ||
| 206 | - let dataArr=[]; | 216 | + let dataArr = []; |
| 207 | try { | 217 | try { |
| 208 | - dataArr=JSON.parse(tempStr); | ||
| 209 | - }catch (err){ | 218 | + dataArr = JSON.parse(tempStr); |
| 219 | + } catch (err) { | ||
| 210 | 220 | ||
| 211 | } | 221 | } |
| 212 | return dataArr; | 222 | return dataArr; |
| @@ -111,6 +111,8 @@ MessageTypes.WEB_RTC_JOIN_FAILED = "web_rtc_join_failed"; | @@ -111,6 +111,8 @@ MessageTypes.WEB_RTC_JOIN_FAILED = "web_rtc_join_failed"; | ||
| 111 | MessageTypes.WEB_RTC_PUBLISH_FAILED = "web_rtc_publish_failed"; | 111 | MessageTypes.WEB_RTC_PUBLISH_FAILED = "web_rtc_publish_failed"; |
| 112 | MessageTypes.GET_DEVICES_SUCCESS = "get_devices_success"; | 112 | MessageTypes.GET_DEVICES_SUCCESS = "get_devices_success"; |
| 113 | MessageTypes.MEDIA_PUBLISH_STATUS_CHANGE = "media_publish_status_change";//音视频推流的状态发生改变 | 113 | MessageTypes.MEDIA_PUBLISH_STATUS_CHANGE = "media_publish_status_change";//音视频推流的状态发生改变 |
| 114 | +MessageTypes.MEDIA_ENABLED_CHANGE = "media_enabled_change";//媒体禁用或开启状态改变 | ||
| 115 | +MessageTypes.RECEIVE_MEDIA_ENABLED_CHANGE = "receive_media_enabled_change";//收到媒体禁用状态控制的消息 | ||
| 114 | 116 | ||
| 115 | //MCU MS | 117 | //MCU MS |
| 116 | MessageTypes.SWITCH_MCU_IP = "switch_mcu_ip"; //切换mcu 重新选点 | 118 | MessageTypes.SWITCH_MCU_IP = "switch_mcu_ip"; //切换mcu 重新选点 |
| @@ -27,6 +27,8 @@ ApeConsts.USER_ACTION_SILENCE_STATUS_CHANGE = 3; //更改用户的禁言状态 | @@ -27,6 +27,8 @@ ApeConsts.USER_ACTION_SILENCE_STATUS_CHANGE = 3; //更改用户的禁言状态 | ||
| 27 | ApeConsts.CLASS_ACTION_KICK_OUT_ROSTER=4; //指定的人踢出课堂 | 27 | ApeConsts.CLASS_ACTION_KICK_OUT_ROSTER=4; //指定的人踢出课堂 |
| 28 | ApeConsts.STOP_ALL_PUBLISH=5; //所有人停止推流 | 28 | ApeConsts.STOP_ALL_PUBLISH=5; //所有人停止推流 |
| 29 | ApeConsts.CLASS_ACTION_DRAW_STATUS_CHANGE = 6; //更改用户的画笔状态 | 29 | ApeConsts.CLASS_ACTION_DRAW_STATUS_CHANGE = 6; //更改用户的画笔状态 |
| 30 | +ApeConsts.MEDIA_ENABLED_CHANGE = 7; //更改用户的音视频禁用状态 | ||
| 31 | + | ||
| 30 | 32 | ||
| 31 | //课堂类型 1:1v1(2路流) 2:直播(1路流) 3:小班课(可以多路流) | 33 | //课堂类型 1:1v1(2路流) 2:直播(1路流) 3:小班课(可以多路流) |
| 32 | ApeConsts.CLASS_TYPE_1v1 = 1; // 互动课堂,通过MS转发音视频,不能进行H5观看 1v1(2路流) | 34 | ApeConsts.CLASS_TYPE_1v1 = 1; // 互动课堂,通过MS转发音视频,不能进行H5观看 1v1(2路流) |
| @@ -855,6 +855,17 @@ class ConferApe extends Ape { | @@ -855,6 +855,17 @@ class ConferApe extends Ape { | ||
| 855 | loger.warn('chatMsg->JSON数据解析失败'); | 855 | loger.warn('chatMsg->JSON数据解析失败'); |
| 856 | } | 856 | } |
| 857 | break; | 857 | break; |
| 858 | + case ApeConsts.MEDIA_ENABLED_CHANGE: | ||
| 859 | + let mediaMsgObj = null; | ||
| 860 | + try { | ||
| 861 | + mediaMsgObj = JSON.parse(chatMsg.message); | ||
| 862 | + if (mediaMsgObj) { | ||
| 863 | + this.receiveChangeUserMediaEnabledStatus(mediaMsgObj); | ||
| 864 | + } | ||
| 865 | + } catch (err) { | ||
| 866 | + loger.warn('chatMsg->JSON数据解析失败',chatMsg.message); | ||
| 867 | + } | ||
| 868 | + break; | ||
| 858 | default: | 869 | default: |
| 859 | break; | 870 | break; |
| 860 | } | 871 | } |
| @@ -1028,6 +1039,11 @@ class ConferApe extends Ape { | @@ -1028,6 +1039,11 @@ class ConferApe extends Ape { | ||
| 1028 | loger.log("监课进入或更新数据"); | 1039 | loger.log("监课进入或更新数据"); |
| 1029 | return; | 1040 | return; |
| 1030 | } | 1041 | } |
| 1042 | + if (nodeData.role == ApeConsts.NR_ASSISTANT && GlobalConfig.userRole != ApeConsts.invisible && GlobalConfig.userRole != ApeConsts.assistant) { | ||
| 1043 | + loger.log("助教进入或更新数据"); | ||
| 1044 | + return; | ||
| 1045 | + } | ||
| 1046 | + | ||
| 1031 | if (!rosterExists) { | 1047 | if (!rosterExists) { |
| 1032 | //this.rosterLen = Object.keys(this.rosters).length; | 1048 | //this.rosterLen = Object.keys(this.rosters).length; |
| 1033 | //GlobalConfig.rosterNum = this.rosterLen;//记录当前的总人数 | 1049 | //GlobalConfig.rosterNum = this.rosterLen;//记录当前的总人数 |
| @@ -1091,11 +1107,36 @@ class ConferApe extends Ape { | @@ -1091,11 +1107,36 @@ class ConferApe extends Ape { | ||
| 1091 | } | 1107 | } |
| 1092 | } | 1108 | } |
| 1093 | 1109 | ||
| 1094 | - //设备状态更新 | 1110 | + /* |
| 1111 | + * 设备状态更新 | ||
| 1112 | + * */ | ||
| 1095 | updaterUserDeviecStatusChange(_data) { | 1113 | updaterUserDeviecStatusChange(_data) { |
| 1096 | loger.log("音视频设备状态更新->", _data); | 1114 | loger.log("音视频设备状态更新->", _data); |
| 1097 | this.updateUserInfo(); | 1115 | this.updateUserInfo(); |
| 1098 | } | 1116 | } |
| 1117 | + /* | ||
| 1118 | + * 媒体开启或禁用 | ||
| 1119 | + * */ | ||
| 1120 | + sendMediaEnabledChange(_data){ | ||
| 1121 | + if(!_data){ | ||
| 1122 | + return; | ||
| 1123 | + } | ||
| 1124 | + | ||
| 1125 | + try{ | ||
| 1126 | + loger.log("发送->媒体开启或禁用",_data); | ||
| 1127 | + this.sendConferMsg({"to": 0, "message":JSON.stringify(_data), "actionType": ApeConsts.MEDIA_ENABLED_CHANGE}); | ||
| 1128 | + }catch (err){ | ||
| 1129 | + loger.warn("发送->媒体开启或禁用->失败",_data,err.message); | ||
| 1130 | + } | ||
| 1131 | + | ||
| 1132 | + } | ||
| 1133 | + | ||
| 1134 | + /* | ||
| 1135 | + * 收到媒体开启或禁用控制 | ||
| 1136 | + * */ | ||
| 1137 | + receiveChangeUserMediaEnabledStatus(_data){ | ||
| 1138 | + this._emit(MessageTypes.RECEIVE_MEDIA_ENABLED_CHANGE,_data); | ||
| 1139 | + } | ||
| 1099 | 1140 | ||
| 1100 | //删除用户 | 1141 | //删除用户 |
| 1101 | rosterDelHandler(nodeId) { | 1142 | rosterDelHandler(nodeId) { |
| @@ -1114,12 +1155,9 @@ class ConferApe extends Ape { | @@ -1114,12 +1155,9 @@ class ConferApe extends Ape { | ||
| 1114 | if(GlobalConfig.classType!= ApeConsts.CLASS_TYPE_ZHIBO){ | 1155 | if(GlobalConfig.classType!= ApeConsts.CLASS_TYPE_ZHIBO){ |
| 1115 | loger.log(nodeId, "->离开课堂->身份->", user.userRole); | 1156 | loger.log(nodeId, "->离开课堂->身份->", user.userRole); |
| 1116 | } | 1157 | } |
| 1117 | - | ||
| 1118 | } | 1158 | } |
| 1119 | delete this.rosters[nodeId]; | 1159 | delete this.rosters[nodeId]; |
| 1120 | GlobalConfig.rosters = this.rosters; | 1160 | GlobalConfig.rosters = this.rosters; |
| 1121 | - //this.rosterLen = Object.keys(this.rosters).length; | ||
| 1122 | - //GlobalConfig.rosterNum = this.rosterLen;//记录当前的总人数 | ||
| 1123 | 1161 | ||
| 1124 | if(!GlobalConfig.isH5) { | 1162 | if(!GlobalConfig.isH5) { |
| 1125 | this.emitRosterChange(); | 1163 | this.emitRosterChange(); |
| 1 | //* | 1 | //* |
| 2 | // WebRtc | 2 | // WebRtc |
| 3 | // */ | 3 | // */ |
| 4 | +require('../assets/css/mcuStyle.css'); | ||
| 5 | + | ||
| 4 | import Emiter from 'Emiter'; | 6 | import Emiter from 'Emiter'; |
| 5 | import Loger from "Loger"; | 7 | import Loger from "Loger"; |
| 6 | import $ from "jquery"; | 8 | import $ from "jquery"; |
| @@ -10,12 +12,13 @@ import EngineUtils from 'EngineUtils'; | @@ -10,12 +12,13 @@ import EngineUtils from 'EngineUtils'; | ||
| 10 | import MessageTypes from 'MessageTypes'; | 12 | import MessageTypes from 'MessageTypes'; |
| 11 | var AgoraRTC = require('../AgoraRTCSDK-1.14.0'); | 13 | var AgoraRTC = require('../AgoraRTCSDK-1.14.0'); |
| 12 | let loger = Loger.getLoger('WebRtcApe'); | 14 | let loger = Loger.getLoger('WebRtcApe'); |
| 13 | -const SIZE_480=480; | ||
| 14 | -const SIZE_360=360; | ||
| 15 | -const SIZE_320=320; | ||
| 16 | -const SIZE_240=240; | ||
| 17 | -const SIZE_160=160; | ||
| 18 | -const SIZE_120=120; | 15 | +const SIZE_480 = 480; |
| 16 | +const SIZE_360 = 360; | ||
| 17 | +const SIZE_320 = 320; | ||
| 18 | +const SIZE_240 = 240; | ||
| 19 | +const SIZE_160 = 160; | ||
| 20 | +const SIZE_120 = 120; | ||
| 21 | + | ||
| 19 | class WebRtcApe extends Emiter { | 22 | class WebRtcApe extends Emiter { |
| 20 | constructor() { | 23 | constructor() { |
| 21 | super(); | 24 | super(); |
| @@ -24,10 +27,10 @@ class WebRtcApe extends Emiter { | @@ -24,10 +27,10 @@ class WebRtcApe extends Emiter { | ||
| 24 | this.appCertificate = ""; | 27 | this.appCertificate = ""; |
| 25 | this.appRecordingKey = ""; | 28 | this.appRecordingKey = ""; |
| 26 | 29 | ||
| 27 | - this.setConfigTimestamp=0;//设置旁路地址的时间戳 | 30 | + this.setConfigTimestamp = 0;//设置旁路地址的时间戳 |
| 28 | this.configPublisherUrl = "";//旁路地址; | 31 | this.configPublisherUrl = "";//旁路地址; |
| 29 | - this.m3u8Url="";//旁路拉流地址 | ||
| 30 | - this.rtmpUrl="";//旁路拉流地址 | 32 | + this.m3u8Url = "";//旁路拉流地址 |
| 33 | + this.rtmpUrl = "";//旁路拉流地址 | ||
| 31 | 34 | ||
| 32 | this.channelKey = null; | 35 | this.channelKey = null; |
| 33 | this.channelId = ""; | 36 | this.channelId = ""; |
| @@ -47,10 +50,10 @@ class WebRtcApe extends Emiter { | @@ -47,10 +50,10 @@ class WebRtcApe extends Emiter { | ||
| 47 | 50 | ||
| 48 | this.remoteVideoList = {};//记录远程视频流 | 51 | this.remoteVideoList = {};//记录远程视频流 |
| 49 | 52 | ||
| 50 | - this.pWidth=SIZE_480; | ||
| 51 | - this.pHeight=SIZE_360; | ||
| 52 | - this.pFrameRate=30; | ||
| 53 | - this.pBitrate=500; | 53 | + this.pWidth = SIZE_480; |
| 54 | + this.pHeight = SIZE_360; | ||
| 55 | + this.pFrameRate = 30; | ||
| 56 | + this.pBitrate = 500; | ||
| 54 | 57 | ||
| 55 | //120P 0 160x120 15 65 | 58 | //120P 0 160x120 15 65 |
| 56 | //240P 20 320x240 15 200 | 59 | //240P 20 320x240 15 200 |
| @@ -58,10 +61,10 @@ class WebRtcApe extends Emiter { | @@ -58,10 +61,10 @@ class WebRtcApe extends Emiter { | ||
| 58 | this.videoResolution = "240P"; | 61 | this.videoResolution = "240P"; |
| 59 | this.isOpenVideo = true; | 62 | this.isOpenVideo = true; |
| 60 | 63 | ||
| 61 | - this.firstPublishSuccess=false;//记录加入频道成功之后是否推流成功过,离开频道之后需要设置为false | 64 | + this.firstPublishSuccess = false;//记录加入频道成功之后是否推流成功过,离开频道之后需要设置为false |
| 62 | 65 | ||
| 63 | this.isPublish = false;//当前是否正在推流 | 66 | this.isPublish = false;//当前是否正在推流 |
| 64 | - this.videoScale=1;//视图的缩放比例,默认为1; | 67 | + this.videoScale = 1;//视图的缩放比例,默认为1; |
| 65 | 68 | ||
| 66 | 69 | ||
| 67 | this.normalRemoteViewId = ""; | 70 | this.normalRemoteViewId = ""; |
| @@ -85,10 +88,10 @@ class WebRtcApe extends Emiter { | @@ -85,10 +88,10 @@ class WebRtcApe extends Emiter { | ||
| 85 | this.invisibleVideoHeight = SIZE_360; | 88 | this.invisibleVideoHeight = SIZE_360; |
| 86 | this.xdyRemote = "xdy_remote"; | 89 | this.xdyRemote = "xdy_remote"; |
| 87 | 90 | ||
| 88 | - this.localWebRtcVideoClass='localWebRtcVideoClass';//本地视图统一的class名称 | ||
| 89 | - this.invisibleWebRtcVideoClass='invisibleWebRtcVideoClass'; | ||
| 90 | - this.normalWebRtcVideoClass='normalWebRtcVideoClass'; | ||
| 91 | - this.hostWebRtcVideoClass='hostWebRtcVideoClass'; | 91 | + this.localWebRtcVideoClass = 'localWebRtcVideoClass';//本地视图统一的class名称 |
| 92 | + this.invisibleWebRtcVideoClass = 'invisibleWebRtcVideoClass'; | ||
| 93 | + this.normalWebRtcVideoClass = 'normalWebRtcVideoClass'; | ||
| 94 | + this.hostWebRtcVideoClass = 'hostWebRtcVideoClass'; | ||
| 92 | 95 | ||
| 93 | //webRtc sdk | 96 | //webRtc sdk |
| 94 | this.client = AgoraRTC.createClient({mode: this.mode}); | 97 | this.client = AgoraRTC.createClient({mode: this.mode}); |
| @@ -146,7 +149,7 @@ class WebRtcApe extends Emiter { | @@ -146,7 +149,7 @@ class WebRtcApe extends Emiter { | ||
| 146 | this.client.on('stream-published', (evt)=> { | 149 | this.client.on('stream-published', (evt)=> { |
| 147 | loger.log("webRtc->推流成功->", new Date().getTime()); | 150 | loger.log("webRtc->推流成功->", new Date().getTime()); |
| 148 | this.isPublish = true; | 151 | this.isPublish = true; |
| 149 | - this.firstPublishSuccess=true; | 152 | + this.firstPublishSuccess = true; |
| 150 | GlobalConfig.openCamera = EngineUtils.creatTimestamp(); | 153 | GlobalConfig.openCamera = EngineUtils.creatTimestamp(); |
| 151 | GlobalConfig.openMicrophones = GlobalConfig.openCamera; | 154 | GlobalConfig.openMicrophones = GlobalConfig.openCamera; |
| 152 | this._emit(MessageTypes.USER_DEVICE_STATUS_CHAANGE, { | 155 | this._emit(MessageTypes.USER_DEVICE_STATUS_CHAANGE, { |
| @@ -208,41 +211,47 @@ class WebRtcApe extends Emiter { | @@ -208,41 +211,47 @@ class WebRtcApe extends Emiter { | ||
| 208 | } | 211 | } |
| 209 | 212 | ||
| 210 | //添加之前先删除之前存在的重复视图 | 213 | //添加之前先删除之前存在的重复视图 |
| 211 | - let len=$("#"+this.xdyRemote + uid+" #player_" + uid).length; | ||
| 212 | - if(len<1){ | ||
| 213 | - loger.log("添加之前先删除之前存在的重复视图",uid); | ||
| 214 | - $("#"+this.xdyRemote + uid).remove(); | 214 | + let len = $("#" + this.xdyRemote + uid + " #player_" + uid).length; |
| 215 | + if (len < 1) { | ||
| 216 | + loger.log("添加之前先删除之前存在的重复视图", uid); | ||
| 217 | + $("#" + this.xdyRemote + uid).remove(); | ||
| 218 | + } | ||
| 219 | + let audioMutedDiv = ""; | ||
| 220 | + if (GlobalConfig.isTeachOrAssistant || GlobalConfig.isInvisible) { | ||
| 221 | + audioMutedDiv = `<div class="audioMuted audioOpen" id=${"audioMuted_"+uid}></div>`; | ||
| 215 | } | 222 | } |
| 216 | 223 | ||
| 217 | if (userRole == ApeConsts.invisible) { | 224 | if (userRole == ApeConsts.invisible) { |
| 218 | - 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>`; | 225 | + 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>`; |
| 219 | //把远程视频添加到监课列表 | 226 | //把远程视频添加到监课列表 |
| 220 | loger.log("获取远程视频流成功->监课:" + userName + "->" + uid, new Date().getTime()); | 227 | loger.log("获取远程视频流成功->监课:" + userName + "->" + uid, new Date().getTime()); |
| 221 | - 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>`; | 228 | + 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>`; |
| 222 | $(this.invisibleViewId).append(viewDiv); | 229 | $(this.invisibleViewId).append(viewDiv); |
| 223 | - } else if (userRole == ApeConsts.host||userRole == ApeConsts.assistant||userRole == ApeConsts.presenter) { | ||
| 224 | - 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>`; | 230 | + } else if (userRole == ApeConsts.host || userRole == ApeConsts.assistant || userRole == ApeConsts.presenter) { |
| 231 | + 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>`; | ||
| 225 | //把远程视图添加到老师列表 | 232 | //把远程视图添加到老师列表 |
| 226 | - loger.log("获取远程视频流成功->userRole:"+userRole+":" + userName + "->" + uid, new Date().getTime()); | ||
| 227 | - 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>`; | 233 | + loger.log("获取远程视频流成功->userRole:" + userRole + ":" + userName + "->" + uid, new Date().getTime()); |
| 234 | + 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>`; | ||
| 228 | $(this.hostRemoteViewId).prepend(viewDiv); | 235 | $(this.hostRemoteViewId).prepend(viewDiv); |
| 229 | } else { | 236 | } else { |
| 230 | - 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>`; | 237 | + 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>`; |
| 231 | //把视图添加到学生列表 | 238 | //把视图添加到学生列表 |
| 232 | loger.log("获取远程视频流成功->学生:" + userName + "->" + uid, new Date().getTime()); | 239 | loger.log("获取远程视频流成功->学生:" + userName + "->" + uid, new Date().getTime()); |
| 233 | - 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>`; | 240 | + 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>`; |
| 234 | $(this.normalRemoteViewId).append(viewDiv); | 241 | $(this.normalRemoteViewId).append(viewDiv); |
| 235 | } | 242 | } |
| 243 | + $("#audioMuted_" + uid).off("click", this._clickAudioMuted.bind(this)); | ||
| 244 | + $("#audioMuted_" + uid).on("click", this._clickAudioMuted.bind(this)); | ||
| 236 | //播放视频,隐藏控制条 | 245 | //播放视频,隐藏控制条 |
| 237 | try { | 246 | try { |
| 238 | - $("bar_" + stream.getId()).hide(); | 247 | + $("#bar_" + stream.getId()).hide(); |
| 239 | stream.play(this.xdyRemote + stream.getId()); | 248 | stream.play(this.xdyRemote + stream.getId()); |
| 240 | } catch (err) { | 249 | } catch (err) { |
| 241 | //添加之前先删除之前存在的重复视图 | 250 | //添加之前先删除之前存在的重复视图 |
| 242 | - let len=$("#"+this.xdyRemote + uid+" #player_" + uid).length; | ||
| 243 | - if(len<1){ | ||
| 244 | - lloger.log("流播放失败->删除视图",uid); | ||
| 245 | - $("#"+this.xdyRemote + uid).remove(); | 251 | + let len = $("#" + this.xdyRemote + uid + " #player_" + uid).length; |
| 252 | + if (len < 1) { | ||
| 253 | + loger.log("流播放失败->删除视图", uid); | ||
| 254 | + $("#" + this.xdyRemote + uid).remove(); | ||
| 246 | } | 255 | } |
| 247 | } | 256 | } |
| 248 | if (user && (user.deviceType == GlobalConfig.deviceIOS || user.deviceType == GlobalConfig.deviceAndroid)) { | 257 | if (user && (user.deviceType == GlobalConfig.deviceIOS || user.deviceType == GlobalConfig.deviceAndroid)) { |
| @@ -252,43 +261,44 @@ class WebRtcApe extends Emiter { | @@ -252,43 +261,44 @@ class WebRtcApe extends Emiter { | ||
| 252 | 261 | ||
| 253 | this.clearInvalidVideoView(); | 262 | this.clearInvalidVideoView(); |
| 254 | } | 263 | } |
| 264 | + | ||
| 255 | //清除无效的视图 | 265 | //清除无效的视图 |
| 256 | - clearInvalidVideoView(){ | ||
| 257 | - let normalList=document.getElementsByClassName(this.normalWebRtcVideoClass); | ||
| 258 | - let hostList=document.getElementsByClassName(this.hostWebRtcVideoClass); | ||
| 259 | - let localList=document.getElementsByClassName(this.localWebRtcVideoClass); | ||
| 260 | - | ||
| 261 | - let divItem=null; | ||
| 262 | - if(normalList){ | ||
| 263 | - for (let i=normalList.length-1;i>=0;i--){ | ||
| 264 | - divItem=normalList[i]; | ||
| 265 | - if(divItem&&divItem.children.length<2){ | 266 | + clearInvalidVideoView() { |
| 267 | + let normalList = document.getElementsByClassName(this.normalWebRtcVideoClass); | ||
| 268 | + let hostList = document.getElementsByClassName(this.hostWebRtcVideoClass); | ||
| 269 | + let localList = document.getElementsByClassName(this.localWebRtcVideoClass); | ||
| 270 | + | ||
| 271 | + let divItem = null; | ||
| 272 | + if (normalList) { | ||
| 273 | + for (let i = normalList.length - 1; i >= 0; i--) { | ||
| 274 | + divItem = normalList[i]; | ||
| 275 | + if (divItem && divItem.children.length < 2) { | ||
| 266 | console.log("删除无效的学生视图"); | 276 | console.log("删除无效的学生视图"); |
| 267 | divItem.remove(); | 277 | divItem.remove(); |
| 268 | } | 278 | } |
| 269 | } | 279 | } |
| 270 | } | 280 | } |
| 271 | - divItem=null; | ||
| 272 | - if(hostList){ | ||
| 273 | - for (let i=hostList.length-1;i>=0;i--){ | ||
| 274 | - divItem=hostList[i]; | ||
| 275 | - if(divItem&&divItem.children.length<2){ | 281 | + divItem = null; |
| 282 | + if (hostList) { | ||
| 283 | + for (let i = hostList.length - 1; i >= 0; i--) { | ||
| 284 | + divItem = hostList[i]; | ||
| 285 | + if (divItem && divItem.children.length < 2) { | ||
| 276 | console.log("删除无效的老师视图"); | 286 | console.log("删除无效的老师视图"); |
| 277 | divItem.remove(); | 287 | divItem.remove(); |
| 278 | } | 288 | } |
| 279 | } | 289 | } |
| 280 | } | 290 | } |
| 281 | - divItem=null; | ||
| 282 | - if(localList){ | ||
| 283 | - for (let i=localList.length-1;i>=0;i--){ | ||
| 284 | - divItem=localList[i]; | ||
| 285 | - if(divItem&&divItem.children.length<2){ | 291 | + divItem = null; |
| 292 | + if (localList) { | ||
| 293 | + for (let i = localList.length - 1; i >= 0; i--) { | ||
| 294 | + divItem = localList[i]; | ||
| 295 | + if (divItem && divItem.children.length < 2) { | ||
| 286 | console.log("删除无效的本地视图"); | 296 | console.log("删除无效的本地视图"); |
| 287 | divItem.remove(); | 297 | divItem.remove(); |
| 288 | } | 298 | } |
| 289 | } | 299 | } |
| 290 | } | 300 | } |
| 291 | - divItem=null; | 301 | + divItem = null; |
| 292 | } | 302 | } |
| 293 | 303 | ||
| 294 | 304 | ||
| @@ -331,8 +341,8 @@ class WebRtcApe extends Emiter { | @@ -331,8 +341,8 @@ class WebRtcApe extends Emiter { | ||
| 331 | //console.log("remoteView->", remoteView) | 341 | //console.log("remoteView->", remoteView) |
| 332 | if (remoteView) { | 342 | if (remoteView) { |
| 333 | let player = document.getElementById("player_" + nodeId); | 343 | let player = document.getElementById("player_" + nodeId); |
| 334 | - let len=$("#"+this.xdyRemote + nodeId+" #player_" + nodeId).length; | ||
| 335 | - if (len>0) { | 344 | + let len = $("#" + this.xdyRemote + nodeId + " #player_" + nodeId).length; |
| 345 | + if (len > 0) { | ||
| 336 | loger.log(nodeId + " 流已经添加显示,不需要再处理"); | 346 | loger.log(nodeId + " 流已经添加显示,不需要再处理"); |
| 337 | return; | 347 | return; |
| 338 | } else { | 348 | } else { |
| @@ -358,29 +368,29 @@ class WebRtcApe extends Emiter { | @@ -358,29 +368,29 @@ class WebRtcApe extends Emiter { | ||
| 358 | 368 | ||
| 359 | //记录加入频道成功之后是否立即推流,默认false | 369 | //记录加入频道成功之后是否立即推流,默认false |
| 360 | //一般只有在刷新重进频道的时候会用到 | 370 | //一般只有在刷新重进频道的时候会用到 |
| 361 | - this.immediatePublish=_params.immediatePublish||false; | 371 | + this.immediatePublish = _params.immediatePublish || false; |
| 362 | 372 | ||
| 363 | //根据不同身份设置不同的分辨率 | 373 | //根据不同身份设置不同的分辨率 |
| 364 | - if(GlobalConfig.isTeachOrAssistant){ | ||
| 365 | - if(GlobalConfig.maxMediaChannels==1){ | 374 | + if (GlobalConfig.isTeachOrAssistant) { |
| 375 | + if (GlobalConfig.maxMediaChannels == 1) { | ||
| 366 | //直播时使用标清最高档 | 376 | //直播时使用标清最高档 |
| 367 | - this.videoResolution="360P_8"; | ||
| 368 | - }else { | ||
| 369 | - this.videoResolution="240P"; | 377 | + this.videoResolution = "360P_8"; |
| 378 | + } else { | ||
| 379 | + this.videoResolution = "240P"; | ||
| 370 | } | 380 | } |
| 371 | - }else if(GlobalConfig.isInvisible){ | ||
| 372 | - this.videoResolution="120P"; | ||
| 373 | - }else { | 381 | + } else if (GlobalConfig.isInvisible) { |
| 382 | + this.videoResolution = "120P"; | ||
| 383 | + } else { | ||
| 374 | //学生在两路视频的时候使用240P 其他多人课堂的时候使用低清的 | 384 | //学生在两路视频的时候使用240P 其他多人课堂的时候使用低清的 |
| 375 | - if(GlobalConfig.maxMediaChannels==2||GlobalConfig.maxMediaChannels>3){ | ||
| 376 | - this.videoResolution="240P" | ||
| 377 | - }else { | 385 | + if (GlobalConfig.maxMediaChannels == 2 || GlobalConfig.maxMediaChannels > 3) { |
| 386 | + this.videoResolution = "240P" | ||
| 387 | + } else { | ||
| 378 | //180P_4 13 240x180 15 120 | 388 | //180P_4 13 240x180 15 120 |
| 379 | - this.videoResolution="180P_4" | 389 | + this.videoResolution = "180P_4" |
| 380 | } | 390 | } |
| 381 | } | 391 | } |
| 382 | 392 | ||
| 383 | - loger.log("开始加入视频通话频道->channelId:" + this.channelId, "uid:" + this.uid,"videoResolution:"+this.videoResolution); | 393 | + loger.log("开始加入视频通话频道->channelId:" + this.channelId, "uid:" + this.uid, "videoResolution:" + this.videoResolution); |
| 384 | this.client.join(this.channelKey, "" + this.channelId, this.uid, (uid)=> { | 394 | this.client.join(this.channelKey, "" + this.channelId, this.uid, (uid)=> { |
| 385 | this.uid = uid; | 395 | this.uid = uid; |
| 386 | loger.log("加入视频通话频道->成功->channelId:" + this.channelId, "uid:" + this.uid); | 396 | loger.log("加入视频通话频道->成功->channelId:" + this.channelId, "uid:" + this.uid); |
| @@ -388,12 +398,12 @@ class WebRtcApe extends Emiter { | @@ -388,12 +398,12 @@ class WebRtcApe extends Emiter { | ||
| 388 | this.openLoaclStream(); | 398 | this.openLoaclStream(); |
| 389 | 399 | ||
| 390 | //加入频道成功之后需要判断是否立即开启推流 | 400 | //加入频道成功之后需要判断是否立即开启推流 |
| 391 | - if(this.immediatePublish==true){ | ||
| 392 | - loger.log("加入音视频频道成功->立刻开始推流->"+this.immediatePublish); | 401 | + if (this.immediatePublish == true) { |
| 402 | + loger.log("加入音视频频道成功->立刻开始推流->" + this.immediatePublish); | ||
| 393 | clearTimeout(this.rePublishDelay); | 403 | clearTimeout(this.rePublishDelay); |
| 394 | - this.rePublishDelay=setTimeout(()=>{ | 404 | + this.rePublishDelay = setTimeout(()=> { |
| 395 | this.publish(); | 405 | this.publish(); |
| 396 | - },200); | 406 | + }, 200); |
| 397 | } | 407 | } |
| 398 | }, (err)=> { | 408 | }, (err)=> { |
| 399 | loger.error("加入视频通话频道->失败->", err); | 409 | loger.error("加入视频通话频道->失败->", err); |
| @@ -439,11 +449,11 @@ class WebRtcApe extends Emiter { | @@ -439,11 +449,11 @@ class WebRtcApe extends Emiter { | ||
| 439 | } | 449 | } |
| 440 | 450 | ||
| 441 | leaveChannel() { | 451 | leaveChannel() { |
| 442 | - loger.log("调用离开视频通话频道->isPublish"+this.isPublish); | 452 | + loger.log("调用离开视频通话频道->isPublish" + this.isPublish); |
| 443 | if (!this.client) { | 453 | if (!this.client) { |
| 444 | return; | 454 | return; |
| 445 | } | 455 | } |
| 446 | - this.firstPublishSuccess=false; | 456 | + this.firstPublishSuccess = false; |
| 447 | this.unpublish(); | 457 | this.unpublish(); |
| 448 | this.client.leave(() => { | 458 | this.client.leave(() => { |
| 449 | loger.log("离开视频通话频道->成功"); | 459 | loger.log("离开视频通话频道->成功"); |
| @@ -469,31 +479,39 @@ class WebRtcApe extends Emiter { | @@ -469,31 +479,39 @@ class WebRtcApe extends Emiter { | ||
| 469 | /* | 479 | /* |
| 470 | * 设置旁录地址 | 480 | * 设置旁录地址 |
| 471 | * */ | 481 | * */ |
| 472 | - setConfigPublisherUrl(_publishUrl){ | ||
| 473 | - this.firstPublishSuccess=false; | ||
| 474 | - this.setConfigTimestamp=new Date().getTime()/1000; | ||
| 475 | - this.configPublisherUrl=_publishUrl; | ||
| 476 | - if(this.client&& this.configPublisherUrl){ | ||
| 477 | - let configObj={width: parseInt(this.pWidth), height: parseInt(this.pHeight), framerate: parseInt(this.pFrameRate), bitrate: parseInt(this.pBitrate), publishUrl:""+this.configPublisherUrl}; | 482 | + setConfigPublisherUrl(_publishUrl) { |
| 483 | + this.firstPublishSuccess = false; | ||
| 484 | + this.setConfigTimestamp = new Date().getTime() / 1000; | ||
| 485 | + this.configPublisherUrl = _publishUrl; | ||
| 486 | + if (this.client && this.configPublisherUrl) { | ||
| 487 | + let configObj = { | ||
| 488 | + width: parseInt(this.pWidth), | ||
| 489 | + height: parseInt(this.pHeight), | ||
| 490 | + framerate: parseInt(this.pFrameRate), | ||
| 491 | + bitrate: parseInt(this.pBitrate), | ||
| 492 | + publishUrl: "" + this.configPublisherUrl | ||
| 493 | + }; | ||
| 478 | //let configObj={width: 480, height:360, framerate:30, bitrate:500, publishUrl:_publishUrl}; | 494 | //let configObj={width: 480, height:360, framerate:30, bitrate:500, publishUrl:_publishUrl}; |
| 479 | //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'}; | 495 | //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'}; |
| 480 | - loger.warn("设置旁路地址->",configObj); | 496 | + loger.warn("设置旁路地址->", configObj); |
| 481 | this.client.configPublisher(configObj); | 497 | this.client.configPublisher(configObj); |
| 482 | - }else { | ||
| 483 | - loger.warn("设置旁路地址->失败->为初始化或旁路地址无效",_publishUrl); | 498 | + } else { |
| 499 | + loger.warn("设置旁路地址->失败->为初始化或旁路地址无效", _publishUrl); | ||
| 484 | } | 500 | } |
| 485 | } | 501 | } |
| 502 | + | ||
| 486 | /* | 503 | /* |
| 487 | * 设置旁录拉流地址 | 504 | * 设置旁录拉流地址 |
| 488 | * */ | 505 | * */ |
| 489 | - setRtmpM3u8Path(_param){ | 506 | + setRtmpM3u8Path(_param) { |
| 490 | //_webRtc.setRtmpM3u8Path({m3u8Url:m3u8Stream,rtmpUrl:rtmpStream}); | 507 | //_webRtc.setRtmpM3u8Path({m3u8Url:m3u8Stream,rtmpUrl:rtmpStream}); |
| 491 | - loger.log("设置旁录拉流地址",_param); | ||
| 492 | - if(_param){ | ||
| 493 | - this.m3u8Url=_param.m3u8Url||""; | ||
| 494 | - this.rtmpUrl=_param.rtmpUrl||""; | 508 | + loger.log("设置旁录拉流地址", _param); |
| 509 | + if (_param) { | ||
| 510 | + this.m3u8Url = _param.m3u8Url || ""; | ||
| 511 | + this.rtmpUrl = _param.rtmpUrl || ""; | ||
| 495 | } | 512 | } |
| 496 | } | 513 | } |
| 514 | + | ||
| 497 | publish(_params) { | 515 | publish(_params) { |
| 498 | if (!this.client || !this.localStream) { | 516 | if (!this.client || !this.localStream) { |
| 499 | loger.warn("推流失败->未加入频道!"); | 517 | loger.warn("推流失败->未加入频道!"); |
| @@ -504,13 +522,13 @@ class WebRtcApe extends Emiter { | @@ -504,13 +522,13 @@ class WebRtcApe extends Emiter { | ||
| 504 | return; | 522 | return; |
| 505 | } | 523 | } |
| 506 | //老师-助教-主讲人-->设置旁路大于30秒没有推流,推流服务会停止,需要重设旁录和重加频道; | 524 | //老师-助教-主讲人-->设置旁路大于30秒没有推流,推流服务会停止,需要重设旁录和重加频道; |
| 507 | - if(this.setConfigTimestamp>0&&GlobalConfig.isTeachOrAssistant){ | 525 | + if (this.setConfigTimestamp > 0 && GlobalConfig.isTeachOrAssistant) { |
| 508 | //如果间隔大于28秒并且没有推过流需要重新加入频道,推成功一次之后就不需要 | 526 | //如果间隔大于28秒并且没有推过流需要重新加入频道,推成功一次之后就不需要 |
| 509 | - let interval=parseInt(new Date().getTime()/1000-this.setConfigTimestamp); | ||
| 510 | - loger.log("设置旁路的时间和推流时间的间隔->"+interval+"秒 firstPublishSuccess:"+ this.firstPublishSuccess); | ||
| 511 | - if(interval>=28&&!this.firstPublishSuccess){ | 527 | + let interval = parseInt(new Date().getTime() / 1000 - this.setConfigTimestamp); |
| 528 | + loger.log("设置旁路的时间和推流时间的间隔->" + interval + "秒 firstPublishSuccess:" + this.firstPublishSuccess); | ||
| 529 | + if (interval >= 28 && !this.firstPublishSuccess) { | ||
| 512 | loger.warn("设置旁路大于30秒没有推流,推流服务会停止,需要重设旁录和重加频道"); | 530 | loger.warn("设置旁路大于30秒没有推流,推流服务会停止,需要重设旁录和重加频道"); |
| 513 | - this._emit(WebRtcApe.RE_JOIN_CHANNEL,{publish:true}); | 531 | + this._emit(WebRtcApe.RE_JOIN_CHANNEL, {publish: true}); |
| 514 | return; | 532 | return; |
| 515 | } | 533 | } |
| 516 | } | 534 | } |
| @@ -520,9 +538,9 @@ class WebRtcApe extends Emiter { | @@ -520,9 +538,9 @@ class WebRtcApe extends Emiter { | ||
| 520 | let viewName = 'localVideoBox_' + this.uid; | 538 | let viewName = 'localVideoBox_' + this.uid; |
| 521 | let videoBox = document.createElement("div"); | 539 | let videoBox = document.createElement("div"); |
| 522 | videoBox.id = viewName; | 540 | videoBox.id = viewName; |
| 523 | - videoBox.className=this.localWebRtcVideoClass; | ||
| 524 | - videoBox.style.width = (this.localVideoWidth*this.videoScale) + 'px'; | ||
| 525 | - videoBox.style.height = (this.localVideoHeight*this.videoScale) + 'px'; | 541 | + videoBox.className = this.localWebRtcVideoClass; |
| 542 | + videoBox.style.width = (this.localVideoWidth * this.videoScale) + 'px'; | ||
| 543 | + videoBox.style.height = (this.localVideoHeight * this.videoScale) + 'px'; | ||
| 526 | videoBox.style.float = 'left'; | 544 | videoBox.style.float = 'left'; |
| 527 | videoBox.style.marginRight = "1px"; | 545 | videoBox.style.marginRight = "1px"; |
| 528 | videoBox.style.pointerEvents = 'none'; | 546 | videoBox.style.pointerEvents = 'none'; |
| @@ -536,8 +554,14 @@ class WebRtcApe extends Emiter { | @@ -536,8 +554,14 @@ class WebRtcApe extends Emiter { | ||
| 536 | if (user) { | 554 | if (user) { |
| 537 | userName = user.name || ""; | 555 | userName = user.name || ""; |
| 538 | } | 556 | } |
| 539 | - 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>`; | 557 | + let audioMutedDiv = ""; |
| 558 | + if (GlobalConfig.isTeachOrAssistant) { | ||
| 559 | + audioMutedDiv = `<div class="audioMuted audioOpen" id=${"audioMuted_"+this.uid}></div>`; | ||
| 560 | + } | ||
| 561 | + 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>`; | ||
| 540 | $(this.localViewId).prepend(nameDiv); | 562 | $(this.localViewId).prepend(nameDiv); |
| 563 | + $(".audioMuted").off("click", this._clickAudioMuted.bind(this)); | ||
| 564 | + $(".audioMuted").on("click", this._clickAudioMuted.bind(this)); | ||
| 541 | 565 | ||
| 542 | loger.log("webRtc->推流->", viewName, new Date().getTime()); | 566 | loger.log("webRtc->推流->", viewName, new Date().getTime()); |
| 543 | this.localStream.play(viewName); | 567 | this.localStream.play(viewName); |
| @@ -553,31 +577,31 @@ class WebRtcApe extends Emiter { | @@ -553,31 +577,31 @@ class WebRtcApe extends Emiter { | ||
| 553 | this.isPublish = false; | 577 | this.isPublish = false; |
| 554 | this.clearLocalView(); | 578 | this.clearLocalView(); |
| 555 | this._emit(MessageTypes.WEB_RTC_PUBLISH_FAILED, err); | 579 | this._emit(MessageTypes.WEB_RTC_PUBLISH_FAILED, err); |
| 556 | - if(err) { | 580 | + if (err) { |
| 557 | switch (err.msg) { | 581 | switch (err.msg) { |
| 558 | case "DEVICES_NOT_FOUND": | 582 | case "DEVICES_NOT_FOUND": |
| 559 | - this.curCameraId=""; | ||
| 560 | - this.curMicrophoneId=""; | 583 | + this.curCameraId = ""; |
| 584 | + this.curMicrophoneId = ""; | ||
| 561 | this.unpublish(); | 585 | this.unpublish(); |
| 562 | break; | 586 | break; |
| 563 | case "NOT_SUPPORTED": | 587 | case "NOT_SUPPORTED": |
| 564 | - this.curCameraId=""; | ||
| 565 | - this.curMicrophoneId=""; | 588 | + this.curCameraId = ""; |
| 589 | + this.curMicrophoneId = ""; | ||
| 566 | this.unpublish(); | 590 | this.unpublish(); |
| 567 | break; | 591 | break; |
| 568 | case "PERMISSION_DENIED": | 592 | case "PERMISSION_DENIED": |
| 569 | - this.curCameraId=""; | ||
| 570 | - this.curMicrophoneId=""; | 593 | + this.curCameraId = ""; |
| 594 | + this.curMicrophoneId = ""; | ||
| 571 | this.unpublish(); | 595 | this.unpublish(); |
| 572 | break; | 596 | break; |
| 573 | case "CONSTRAINT_NOT_SATISFIED": | 597 | case "CONSTRAINT_NOT_SATISFIED": |
| 574 | - this.curCameraId=""; | ||
| 575 | - this.curMicrophoneId=""; | 598 | + this.curCameraId = ""; |
| 599 | + this.curMicrophoneId = ""; | ||
| 576 | this.unpublish(); | 600 | this.unpublish(); |
| 577 | break; | 601 | break; |
| 578 | case "STREAM_ALREADY_INITIALIZED": | 602 | case "STREAM_ALREADY_INITIALIZED": |
| 579 | - this.curCameraId=""; | ||
| 580 | - this.curMicrophoneId=""; | 603 | + this.curCameraId = ""; |
| 604 | + this.curMicrophoneId = ""; | ||
| 581 | this.unpublish(); | 605 | this.unpublish(); |
| 582 | break; | 606 | break; |
| 583 | default: | 607 | default: |
| @@ -617,38 +641,38 @@ class WebRtcApe extends Emiter { | @@ -617,38 +641,38 @@ class WebRtcApe extends Emiter { | ||
| 617 | $('#localVideoBox_' + this.uid).remove(); | 641 | $('#localVideoBox_' + this.uid).remove(); |
| 618 | $("#videoOwnerName_" + this.uid).remove(); | 642 | $("#videoOwnerName_" + this.uid).remove(); |
| 619 | $(".localVideoOwnerName").remove(); | 643 | $(".localVideoOwnerName").remove(); |
| 620 | - $("."+this.localWebRtcVideoClass).remove(); | 644 | + $("." + this.localWebRtcVideoClass).remove(); |
| 621 | } | 645 | } |
| 622 | 646 | ||
| 623 | /* | 647 | /* |
| 624 | * 更新所有视频的尺寸大小 | 648 | * 更新所有视频的尺寸大小 |
| 625 | * */ | 649 | * */ |
| 626 | - updateAllVideoSize(){ | ||
| 627 | - $("."+this.localWebRtcVideoClass).css("width",this.localVideoWidth*this.videoScale); | ||
| 628 | - $("."+this.localWebRtcVideoClass).css("height",this.localVideoHeight*this.videoScale); | 650 | + updateAllVideoSize() { |
| 651 | + $("." + this.localWebRtcVideoClass).css("width", this.localVideoWidth * this.videoScale); | ||
| 652 | + $("." + this.localWebRtcVideoClass).css("height", this.localVideoHeight * this.videoScale); | ||
| 629 | 653 | ||
| 630 | - $("."+this.hostWebRtcVideoClass).css("width",this.hostRemoteVideoWidth*this.videoScale); | ||
| 631 | - $("."+this.hostWebRtcVideoClass).css("height",this.hostRemoteVideoHeight*this.videoScale); | 654 | + $("." + this.hostWebRtcVideoClass).css("width", this.hostRemoteVideoWidth * this.videoScale); |
| 655 | + $("." + this.hostWebRtcVideoClass).css("height", this.hostRemoteVideoHeight * this.videoScale); | ||
| 632 | 656 | ||
| 633 | - $("."+this.normalWebRtcVideoClass).css("width",this.normalRemoteVideoWidth*this.videoScale); | ||
| 634 | - $("."+this.normalWebRtcVideoClass).css("height",this.normalRemoteVideoHeight*this.videoScale); | 657 | + $("." + this.normalWebRtcVideoClass).css("width", this.normalRemoteVideoWidth * this.videoScale); |
| 658 | + $("." + this.normalWebRtcVideoClass).css("height", this.normalRemoteVideoHeight * this.videoScale); | ||
| 635 | 659 | ||
| 636 | } | 660 | } |
| 637 | 661 | ||
| 638 | /* | 662 | /* |
| 639 | * 设置rtc视频的属性 | 663 | * 设置rtc视频的属性 |
| 640 | * */ | 664 | * */ |
| 641 | - changeRtcVideoConfig(_params){ | 665 | + changeRtcVideoConfig(_params) { |
| 642 | //{videoScale:1} | 666 | //{videoScale:1} |
| 643 | - if(!_params){ | 667 | + if (!_params) { |
| 644 | return; | 668 | return; |
| 645 | } | 669 | } |
| 646 | - let scale=parseInt(_params.videoScale)||1;//最小值只能为1,这个是按倍数缩放视频 | ||
| 647 | - if(this.videoScale==scale){ | 670 | + let scale = parseInt(_params.videoScale) || 1;//最小值只能为1,这个是按倍数缩放视频 |
| 671 | + if (this.videoScale == scale) { | ||
| 648 | return; | 672 | return; |
| 649 | } | 673 | } |
| 650 | - this.videoScale=scale; | ||
| 651 | - loger.log("更新视频视图大小->videoScale:"+this.videoScale); | 674 | + this.videoScale = scale; |
| 675 | + loger.log("更新视频视图大小->videoScale:" + this.videoScale); | ||
| 652 | this.updateAllVideoSize(); | 676 | this.updateAllVideoSize(); |
| 653 | } | 677 | } |
| 654 | 678 | ||
| @@ -663,8 +687,8 @@ class WebRtcApe extends Emiter { | @@ -663,8 +687,8 @@ class WebRtcApe extends Emiter { | ||
| 663 | this.localVideoHeight = parseInt(_params.height) || SIZE_360; | 687 | this.localVideoHeight = parseInt(_params.height) || SIZE_360; |
| 664 | this.nameDisplay = _params.nameDisplay || "block"; | 688 | this.nameDisplay = _params.nameDisplay || "block"; |
| 665 | 689 | ||
| 666 | - this.localVideoWidth=this.localVideoWidth; | ||
| 667 | - this.localVideoHeight=this.localVideoHeight; | 690 | + this.localVideoWidth = this.localVideoWidth; |
| 691 | + this.localVideoHeight = this.localVideoHeight; | ||
| 668 | } | 692 | } |
| 669 | 693 | ||
| 670 | /* | 694 | /* |
| @@ -676,8 +700,8 @@ class WebRtcApe extends Emiter { | @@ -676,8 +700,8 @@ class WebRtcApe extends Emiter { | ||
| 676 | this.hostRemoteStyle = _params.styleStr || ""; | 700 | this.hostRemoteStyle = _params.styleStr || ""; |
| 677 | this.hostRemoteVideoWidth = parseInt(_params.width) || SIZE_480; | 701 | this.hostRemoteVideoWidth = parseInt(_params.width) || SIZE_480; |
| 678 | this.hostRemoteVideoHeight = parseInt(_params.height) || SIZE_360; | 702 | this.hostRemoteVideoHeight = parseInt(_params.height) || SIZE_360; |
| 679 | - this.hostRemoteVideoWidth=this.hostRemoteVideoWidth; | ||
| 680 | - this.hostRemoteVideoHeight=this.hostRemoteVideoHeight; | 703 | + this.hostRemoteVideoWidth = this.hostRemoteVideoWidth; |
| 704 | + this.hostRemoteVideoHeight = this.hostRemoteVideoHeight; | ||
| 681 | } | 705 | } |
| 682 | 706 | ||
| 683 | /* | 707 | /* |
| @@ -690,8 +714,8 @@ class WebRtcApe extends Emiter { | @@ -690,8 +714,8 @@ class WebRtcApe extends Emiter { | ||
| 690 | this.normalRemoteVideoWidth = parseInt(_params.width) || SIZE_480; | 714 | this.normalRemoteVideoWidth = parseInt(_params.width) || SIZE_480; |
| 691 | this.normalRemoteVideoHeight = parseInt(_params.height) || SIZE_360; | 715 | this.normalRemoteVideoHeight = parseInt(_params.height) || SIZE_360; |
| 692 | 716 | ||
| 693 | - this.normalRemoteVideoWidth=this.normalRemoteVideoWidth; | ||
| 694 | - this.normalRemoteVideoHeight=this.normalRemoteVideoHeight; | 717 | + this.normalRemoteVideoWidth = this.normalRemoteVideoWidth; |
| 718 | + this.normalRemoteVideoHeight = this.normalRemoteVideoHeight; | ||
| 695 | } | 719 | } |
| 696 | 720 | ||
| 697 | /* | 721 | /* |
| @@ -708,12 +732,12 @@ class WebRtcApe extends Emiter { | @@ -708,12 +732,12 @@ class WebRtcApe extends Emiter { | ||
| 708 | /* | 732 | /* |
| 709 | * 设置旁录推流的参数 | 733 | * 设置旁录推流的参数 |
| 710 | * */ | 734 | * */ |
| 711 | - setConfigPublisher(_params){ | ||
| 712 | - loger.log("设置旁录推流的参数",_params); | ||
| 713 | - this.pWidth=_params.width||SIZE_480; | ||
| 714 | - this.pHeight=_params.height||SIZE_360; | ||
| 715 | - this.pFrameRate=_params.frameRate||30; | ||
| 716 | - this.pBitrate=_params.bitrate||500; | 735 | + setConfigPublisher(_params) { |
| 736 | + loger.log("设置旁录推流的参数", _params); | ||
| 737 | + this.pWidth = _params.width || SIZE_480; | ||
| 738 | + this.pHeight = _params.height || SIZE_360; | ||
| 739 | + this.pFrameRate = _params.frameRate || 30; | ||
| 740 | + this.pBitrate = _params.bitrate || 500; | ||
| 717 | } | 741 | } |
| 718 | 742 | ||
| 719 | /* | 743 | /* |
| @@ -776,7 +800,7 @@ class WebRtcApe extends Emiter { | @@ -776,7 +800,7 @@ class WebRtcApe extends Emiter { | ||
| 776 | for (let i = 0; i < devices.length; i++) { | 800 | for (let i = 0; i < devices.length; i++) { |
| 777 | let device = devices[i]; | 801 | let device = devices[i]; |
| 778 | //{"deviceId":"default","kind":"audiooutput","label":"默认","groupId":"cf49a03ca26700235629fc13d3e6630bd34407c66438d157056a34dd3ae03ef5"} | 802 | //{"deviceId":"default","kind":"audiooutput","label":"默认","groupId":"cf49a03ca26700235629fc13d3e6630bd34407c66438d157056a34dd3ae03ef5"} |
| 779 | - if(device){ | 803 | + if (device) { |
| 780 | if (device.kind == 'audioinput') { | 804 | if (device.kind == 'audioinput') { |
| 781 | this.microphones.push(device); | 805 | this.microphones.push(device); |
| 782 | GlobalConfig.microphones.push(device.label); | 806 | GlobalConfig.microphones.push(device.label); |
| @@ -796,13 +820,107 @@ class WebRtcApe extends Emiter { | @@ -796,13 +820,107 @@ class WebRtcApe extends Emiter { | ||
| 796 | }); | 820 | }); |
| 797 | } | 821 | } |
| 798 | 822 | ||
| 823 | + _clickAudioMuted(evt) { | ||
| 824 | + let className = evt.currentTarget.className; | ||
| 825 | + console.log("点击禁音",evt.currentTarget.id); | ||
| 826 | + let idArr = (evt.currentTarget.id).split("_"); | ||
| 827 | + let uid = 10000000;//默认设置一个不存在的uid | ||
| 828 | + if (idArr && idArr.length > 1) { | ||
| 829 | + uid = parseInt(idArr[1]); | ||
| 830 | + } | ||
| 831 | + if (className.indexOf("audioOpen") > 0) { | ||
| 832 | + loger.log("点击禁音->"+uid); | ||
| 833 | + // evt.currentTarget.className="audioMuted audioClose"; | ||
| 834 | + if (uid != GlobalConfig.nodeId) { | ||
| 835 | + this.sendChangeUserMediaEnabled({nodeId: uid, video: true, audio: false}); | ||
| 836 | + $("#audioMuted_" + uid).removeClass(); | ||
| 837 | + $("#audioMuted_" + uid).addClass("audioMuted audioClose"); | ||
| 838 | + } else { | ||
| 839 | + this.disableAudio(uid); | ||
| 840 | + } | ||
| 841 | + } else { | ||
| 842 | + loger.log("点击开启声音"); | ||
| 843 | + //evt.currentTarget.className="audioMuted audioOpen"; | ||
| 844 | + if (uid != GlobalConfig.nodeId) { | ||
| 845 | + this.sendChangeUserMediaEnabled({nodeId: uid, video: true, audio: true}); | ||
| 846 | + $("#audioMuted_" + uid).removeClass(); | ||
| 847 | + $("#audioMuted_" + uid).addClass("audioMuted audioOpen"); | ||
| 848 | + } else { | ||
| 849 | + this.enableAudio(uid); | ||
| 850 | + } | ||
| 851 | + } | ||
| 852 | + } | ||
| 853 | + | ||
| 854 | + /* | ||
| 855 | + * 发送控制音视频禁用消息 | ||
| 856 | + * */ | ||
| 857 | + sendChangeUserMediaEnabled(_param) { | ||
| 858 | + this._emit(MessageTypes.MEDIA_ENABLED_CHANGE, _param); | ||
| 859 | + } | ||
| 860 | + | ||
| 861 | + /* | ||
| 862 | + * 收到控制音视频禁用消息 | ||
| 863 | + * */ | ||
| 864 | + webRtcMeiaEnabledChange(_data) { | ||
| 865 | + loger.log("收到控制音视频禁用消息",_data); | ||
| 866 | + // {nodeId: uid, video: true, audio: false} | ||
| 867 | + if(!_data){ | ||
| 868 | + return; | ||
| 869 | + } | ||
| 870 | + if(_data.nodeId!=GlobalConfig.nodeId){ | ||
| 871 | + if(_data.audio==false){ | ||
| 872 | + $("#audioMuted_" + _data.nodeId).removeClass(); | ||
| 873 | + $("#audioMuted_" + _data.nodeId).addClass("audioMuted audioClose"); | ||
| 874 | + }else { | ||
| 875 | + $("#audioMuted_" + _data.nodeId).removeClass(); | ||
| 876 | + $("#audioMuted_" + _data.nodeId).addClass("audioMuted audioOpen"); | ||
| 877 | + } | ||
| 878 | + | ||
| 879 | + }else{ | ||
| 880 | + if(_data.audio==false){ | ||
| 881 | + this.disableAudio(_data.nodeId); | ||
| 882 | + }else { | ||
| 883 | + this.enableAudio(_data.nodeId); | ||
| 884 | + } | ||
| 885 | + } | ||
| 886 | + | ||
| 887 | + } | ||
| 888 | + | ||
| 889 | + /* | ||
| 890 | + * 开启禁音 | ||
| 891 | + * */ | ||
| 892 | + disableAudio(uid) { | ||
| 893 | + loger.log("开启禁音:" + uid); | ||
| 894 | + if (parseInt(uid) == GlobalConfig.nodeId) { | ||
| 895 | + if (this.localStream) { | ||
| 896 | + $("#audioMuted_" + uid).removeClass(); | ||
| 897 | + $("#audioMuted_" + uid).addClass("audioMuted audioClose"); | ||
| 898 | + this.localStream.disableAudio(); | ||
| 899 | + } | ||
| 900 | + } | ||
| 901 | + } | ||
| 902 | + | ||
| 903 | + /* | ||
| 904 | + * 开启音频 | ||
| 905 | + * */ | ||
| 906 | + enableAudio(_uid) { | ||
| 907 | + loger.log("开启音频:" + _uid); | ||
| 908 | + if (parseInt(_uid) == GlobalConfig.nodeId) { | ||
| 909 | + if (this.localStream) { | ||
| 910 | + this.localStream.enableAudio(); | ||
| 911 | + $("#audioMuted_" + _uid).removeClass(); | ||
| 912 | + $("#audioMuted_" + _uid).addClass("audioMuted audioOpen"); | ||
| 913 | + } | ||
| 914 | + } | ||
| 915 | + } | ||
| 916 | + | ||
| 799 | //组织数据,发送给服务器,控制录制和开启录制-推流和停止推流 status:0 停止推流 1:开始推流(同时开启录制),2:停止录制(同时停止推流) | 917 | //组织数据,发送给服务器,控制录制和开启录制-推流和停止推流 status:0 停止推流 1:开始推流(同时开启录制),2:停止录制(同时停止推流) |
| 800 | packMediaInfoData(_status) { | 918 | packMediaInfoData(_status) { |
| 801 | let curTimestamp = new Date().getTime(); | 919 | let curTimestamp = new Date().getTime(); |
| 802 | 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}`; | 920 | 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}`; |
| 803 | 921 | ||
| 804 | //markettest_623790840_T9540_1508207080 | 922 | //markettest_623790840_T9540_1508207080 |
| 805 | - let streamId=GlobalConfig.siteId+"_"+GlobalConfig.classId+"_"+GlobalConfig.userId+"_"+curTimestamp; | 923 | + let streamId = GlobalConfig.siteId + "_" + GlobalConfig.classId + "_" + GlobalConfig.userId + "_" + curTimestamp; |
| 806 | //mcu记录一份数据 | 924 | //mcu记录一份数据 |
| 807 | this._emit(MessageTypes.MEDIA_PUBLISH_STATUS_CHANGE, { | 925 | this._emit(MessageTypes.MEDIA_PUBLISH_STATUS_CHANGE, { |
| 808 | appId: GlobalConfig.appId, | 926 | appId: GlobalConfig.appId, |
| @@ -815,10 +933,10 @@ class WebRtcApe extends Emiter { | @@ -815,10 +933,10 @@ class WebRtcApe extends Emiter { | ||
| 815 | userRole: GlobalConfig.userRole, | 933 | userRole: GlobalConfig.userRole, |
| 816 | timestamp: curTimestamp, | 934 | timestamp: curTimestamp, |
| 817 | recordTimestamp: GlobalConfig.recordTimestamp, | 935 | recordTimestamp: GlobalConfig.recordTimestamp, |
| 818 | - streamId:streamId, | ||
| 819 | - publishUrl:this.configPublisherUrl, | ||
| 820 | - m3u8Url:this.m3u8Url, | ||
| 821 | - rtmpUrl:this.rtmpUrl | 936 | + streamId: streamId, |
| 937 | + publishUrl: this.configPublisherUrl, | ||
| 938 | + m3u8Url: this.m3u8Url, | ||
| 939 | + rtmpUrl: this.rtmpUrl | ||
| 822 | }); | 940 | }); |
| 823 | return data; | 941 | return data; |
| 824 | } | 942 | } |
| @@ -833,7 +951,7 @@ class WebRtcApe extends Emiter { | @@ -833,7 +951,7 @@ class WebRtcApe extends Emiter { | ||
| 833 | } | 951 | } |
| 834 | let url = GlobalConfig.locationProtocol + GlobalConfig.recordInterfaces; | 952 | let url = GlobalConfig.locationProtocol + GlobalConfig.recordInterfaces; |
| 835 | let data = this.packMediaInfoData(_status); | 953 | let data = this.packMediaInfoData(_status); |
| 836 | - loger.log("调用服务器端更新视频录制状态->status",_status); | 954 | + loger.log("调用服务器端更新视频录制状态->status", _status); |
| 837 | fetch(encodeURI(url), { | 955 | fetch(encodeURI(url), { |
| 838 | method: 'POST', | 956 | method: 'POST', |
| 839 | headers: { | 957 | headers: { |
| @@ -866,11 +984,11 @@ class WebRtcApe extends Emiter { | @@ -866,11 +984,11 @@ class WebRtcApe extends Emiter { | ||
| 866 | * 切换音视频的录制状态 | 984 | * 切换音视频的录制状态 |
| 867 | * */ | 985 | * */ |
| 868 | changeMediaRecordStatus(_params) { | 986 | changeMediaRecordStatus(_params) { |
| 869 | - if (!GlobalConfig.recordInterfaces||!_params) { | ||
| 870 | - loger.warn("切换音视频的录制状态->失败->接口地址无效",_params); | 987 | + if (!GlobalConfig.recordInterfaces || !_params) { |
| 988 | + loger.warn("切换音视频的录制状态->失败->接口地址无效", _params); | ||
| 871 | return; | 989 | return; |
| 872 | } | 990 | } |
| 873 | - loger.warn("切换音视频的录制状态->"+_params); | 991 | + loger.warn("切换音视频的录制状态->" + _params); |
| 874 | let url = GlobalConfig.locationProtocol + GlobalConfig.recordInterfaces; | 992 | let url = GlobalConfig.locationProtocol + GlobalConfig.recordInterfaces; |
| 875 | let curTimestamp = new Date().getTime(); | 993 | let curTimestamp = new Date().getTime(); |
| 876 | 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}`; | 994 | 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}`; |
src/assets/css/mcuStyle.css
0 → 100644
| 1 | +@charset "utf-8"; | ||
| 2 | + | ||
| 3 | +/*禁音按钮*/ | ||
| 4 | +.audioMuted{ | ||
| 5 | + margin-left: 6px; | ||
| 6 | + float: right; | ||
| 7 | + width: 20px; | ||
| 8 | + height: 20px; | ||
| 9 | + cursor: pointer; | ||
| 10 | + background-size: auto; | ||
| 11 | + background-repeat: no-repeat; | ||
| 12 | +} | ||
| 13 | +.audioClose { | ||
| 14 | + background-image: url(../../assets/img/audioClose.png); | ||
| 15 | +} | ||
| 16 | +.audioOpen { | ||
| 17 | + background-image: url(../../assets/img/audioOpen.png); | ||
| 18 | +} |
src/assets/img/audioClose.png
0 → 100644
562 字节
src/assets/img/audioOpen.png
0 → 100644
733 字节
-
请 注册 或 登录 后发表评论