mirror of
https://github.com/tl-open-source/tl-rtc-file.git
synced 2025-10-26 08:40:31 +08:00
feat: 优化音视频/屏幕共享/直播功能
feat: 支持直播模式切换 feat: 支持开关摄像头麦克风 feat: 优化媒体流设备兼容性问题 feat: 优化加入不同类型房间的提示 feat: 调整加入流媒体房间弹窗input类型 feat: 补充页面功能区滚动条 feat: 优化图标和一些样式 feat: 调整部分功能的目录结构 feat: 调整部分存储表结构 feat: 更新adapter.js依赖版本 feat: 优化流媒体连接逻辑 fix. : 修复远程画笔绘制图片报错
This commit is contained in:
@@ -6,7 +6,7 @@ ADD . .
|
||||
|
||||
RUN npm conf set registry https://registry.npm.taobao.org; \
|
||||
npm install -g pm2; \
|
||||
cd /home/tlrtcfile/svr/; \
|
||||
cd /home/tlrtcfile/svr; \
|
||||
npm install;
|
||||
|
||||
EXPOSE 9092 8444
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
# tl-rtc-file-tool-svr
|
||||
|
||||
主要用作webrtc信令交换,部分socket消息处理
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "10.2.6",
|
||||
"version": "10.2.8",
|
||||
"ws": {
|
||||
"port": 8444,
|
||||
"host": "ws://127.0.0.1:8444"
|
||||
|
||||
3806
svr/package-lock.json
generated
3806
svr/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "tl-rtc-file",
|
||||
"version": "1.0.0",
|
||||
"description": "webrtc, p2p,file",
|
||||
"version": "10.2.8",
|
||||
"description": "webrtc, p2p, file, screen, video, live, draw, chat",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
"lapi": "cross-env ENV_MODE=local node localapi",
|
||||
@@ -14,7 +14,7 @@
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": ""
|
||||
"url": "https://github.com/tl-open-source/tl-rtc-file"
|
||||
},
|
||||
"keywords": [
|
||||
"none"
|
||||
@@ -22,19 +22,22 @@
|
||||
"author": "iamtsm",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"glob": "^10.3.1",
|
||||
"terser": "^5.18.2",
|
||||
"vite": "^4.3.9",
|
||||
"rollup-plugin-copy": "^3.4.0",
|
||||
"@grpc/grpc-js": "^1.8.0",
|
||||
"@grpc/proto-loader": "^0.6.0",
|
||||
"cross-env": "^5.2.0",
|
||||
"express": "^4.17.1",
|
||||
"glob": "^10.3.1",
|
||||
"google-protobuf": "^3.0.0",
|
||||
"mocha": "^10.2.0",
|
||||
"mysql2": "^2.1.0",
|
||||
"openai": "^1.0.0",
|
||||
"openai": "^3.3.0",
|
||||
"request": "^2.88.2",
|
||||
"rollup-plugin-copy": "^3.4.0",
|
||||
"sequelize": "^6.1.0",
|
||||
"sequelize-pool": "^6.0.0",
|
||||
"socket.io": "^2.3.0"
|
||||
},
|
||||
"devDependencies": {}
|
||||
"socket.io": "^2.3.0",
|
||||
"terser": "^5.18.2",
|
||||
"tl-ngrpc": "^1.0.1",
|
||||
"vite": "^4.3.9"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,11 +215,6 @@ body {
|
||||
color: black;
|
||||
}
|
||||
|
||||
/* .tl-rtc-file-send-file-txt-tool .swiper-slide{
|
||||
transition: all 0.3s;
|
||||
} */
|
||||
|
||||
|
||||
.tl-rtc-file-tool {
|
||||
cursor: pointer;
|
||||
background-color: rgb(248 253 255 / 45%);
|
||||
@@ -230,30 +225,21 @@ body {
|
||||
|
||||
.tl-rtc-file-tool:hover {
|
||||
box-shadow: rgba(9,30,66,0.25) 0px 8px 9px;
|
||||
/* box-shadow: 0 20px 32px -8px rgba(9,30,66,0.25), 0 0 1px rgba(9,30,66,0.31); */
|
||||
}
|
||||
|
||||
.tl-rtc-file-tool i {
|
||||
.tl-rtc-file-tool svg{
|
||||
color: black;
|
||||
margin: 12px 10px 10px 10px;
|
||||
transition: color 0.8s;
|
||||
}
|
||||
|
||||
.tl-rtc-file-tool-mobile i{
|
||||
padding-top : 0 !important;
|
||||
margin-bottom: 15px;
|
||||
padding:5px;
|
||||
font-size: 18px;
|
||||
transition: color 1.5s;
|
||||
}
|
||||
|
||||
.tl-rtc-file-tool b {
|
||||
margin: 10px 10px 10px 0;
|
||||
margin: 12px 10px 10px 0;
|
||||
letter-spacing: 2px;
|
||||
left: 15px;
|
||||
}
|
||||
|
||||
.tl-rtc-file-tool-disabled,
|
||||
.tl-rtc-file-tool-disabled i,
|
||||
.tl-rtc-file-tool-disabled svg,
|
||||
.tl-rtc-file-tool-disabled b {
|
||||
cursor: no-drop !important;
|
||||
color: #80808061 !important;
|
||||
@@ -780,55 +766,58 @@ body {
|
||||
-webkit-transition: all .3s;
|
||||
}
|
||||
|
||||
|
||||
/* 500px以下 */
|
||||
@media screen and (max-width: 500px) {
|
||||
.tl-rtc-file-send-txt-tool-send{
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.tl-rtc-file-send-file-tool-send {
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
/* 480px到765px */
|
||||
@media screen and (min-width: 480px) and (max-width: 765px) {
|
||||
|
||||
}
|
||||
/* 765px到1280px */
|
||||
@media screen and (min-width: 765px) and (max-width: 1280px) {
|
||||
|
||||
}
|
||||
/** 765px以上 */
|
||||
@media screen and (min-width: 765px) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
.tl-rtc-file-mask-media-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
/* display: flex; */
|
||||
}
|
||||
|
||||
.tl-rtc-file-mask-media-video {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 50%;
|
||||
width: 100%;
|
||||
background-color: black;
|
||||
margin-bottom: 15px;
|
||||
background-color: #36353c;
|
||||
border-radius: 15px;
|
||||
padding: 5px;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.tl-rtc-file-mask-media-video-tool{
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 90%;
|
||||
margin-left: 5%;
|
||||
overflow: auto;
|
||||
border-bottom: 2px dashed #e7e6e699;
|
||||
padding-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.tl-rtc-file-mask-media-video-tool-item{
|
||||
cursor: pointer;
|
||||
margin: 10px 5px 10px 5px;
|
||||
border-radius: 15px;
|
||||
background: #e2e2e2;
|
||||
padding: 5px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.tl-rtc-file-mask-media-video-tool-item:hover{
|
||||
background: #c6b6b6;
|
||||
}
|
||||
|
||||
.tl-rtc-file-mask-media-video video{
|
||||
height: 100%;
|
||||
border-radius: 15px;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
object-fit: cover;
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.tl-rtc-file-mask-media-video-other-audio{
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
position: absolute;
|
||||
bottom: 25px;
|
||||
left: 30px;
|
||||
}
|
||||
|
||||
.tl-rtc-file-mask-media-video b{
|
||||
@@ -927,17 +916,6 @@ body {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.tl-rtc-file-tool-mobile-title{
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
word-break: keep-all;
|
||||
margin: 0 !important;
|
||||
bottom: 0;
|
||||
-webkit-transform-origin-x: 0;
|
||||
transform: scale(0.6);
|
||||
-webkit-transform: scale(0.6);
|
||||
}
|
||||
|
||||
#tl-rtc-file-code-share{
|
||||
word-break: break-all;
|
||||
padding: 0px 50px 0px 50px;
|
||||
@@ -1159,3 +1137,70 @@ body {
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.swiper-scrollbar {
|
||||
min-width: 20px;
|
||||
max-width: 40px;
|
||||
width: 5% !important;
|
||||
margin-left: 50% !important;
|
||||
}
|
||||
|
||||
.tl-rtc-file-tool-mobile {
|
||||
padding: 5px !important;
|
||||
}
|
||||
.tl-rtc-file-tool-mobile svg{
|
||||
padding-top : 0 !important;
|
||||
margin-bottom: 15px !important;
|
||||
font-size: 18px !important;
|
||||
}
|
||||
.tl-rtc-file-tool-title-mobile{
|
||||
position: absolute !important;
|
||||
word-break: keep-all !important;
|
||||
margin: 0 !important;
|
||||
bottom: 0 !important;
|
||||
-webkit-transform-origin-x: 0 !important;
|
||||
transform: scale(0.6) !important;
|
||||
-webkit-transform: scale(0.6) !important;
|
||||
}
|
||||
|
||||
|
||||
/* 500px以下 */
|
||||
@media screen and (max-width: 500px) {
|
||||
.tl-rtc-file-send-txt-tool-send{
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.tl-rtc-file-send-file-tool-send {
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.tl-rtc-file-tool {
|
||||
padding: 5px;
|
||||
}
|
||||
.tl-rtc-file-tool svg{
|
||||
padding-top : 0 !important;
|
||||
margin-bottom: 15px;
|
||||
font-size: 18px;
|
||||
}
|
||||
.tl-rtc-file-tool-title{
|
||||
position: absolute;
|
||||
word-break: keep-all;
|
||||
margin: 0 !important;
|
||||
bottom: 0;
|
||||
-webkit-transform-origin-x: 0;
|
||||
transform: scale(0.6);
|
||||
-webkit-transform: scale(0.6);
|
||||
}
|
||||
}
|
||||
/* 480px到765px */
|
||||
@media screen and (min-width: 480px) and (max-width: 765px) {
|
||||
|
||||
}
|
||||
/* 765px到1280px */
|
||||
@media screen and (min-width: 765px) and (max-width: 1280px) {
|
||||
|
||||
}
|
||||
/** 765px以上 */
|
||||
@media screen and (min-width: 765px) {
|
||||
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
<link rel="stylesheet" href="/static/css/default.min.css">
|
||||
<link rel="stylesheet" href="/static/css/pdf_viewer.min.css">
|
||||
<script type="text/javascript" src="/static/js/swiper-bundle.min.js"></script>
|
||||
<script type="text/javascript" src="/static/js/adapter.js"></script>
|
||||
<script type="text/javascript" src="/static/js/socket.io.js"></script>
|
||||
<script type="text/javascript" src="/static/js/vue.min.js"></script>
|
||||
<script type="text/javascript" src="/static/js/axios.js"></script>
|
||||
@@ -27,7 +28,7 @@
|
||||
<script type="text/javascript" src="/static/js/docx-preview.min.js"></script>
|
||||
<script type="text/javascript" src="/static/js/qrcode.min.js"></script>
|
||||
<script type="text/javascript" src="/static/layui/font-ext/iconfont.js"></script>
|
||||
<link href="/image/44826979.png" rel="shortcut icon" type="image/x-icon">
|
||||
<link href="image/44826979.png" rel="shortcut icon" type="image/x-icon">
|
||||
<style>
|
||||
.icon {
|
||||
width: 1em;
|
||||
@@ -201,108 +202,107 @@
|
||||
<div class="swiper-wrapper">
|
||||
<div class="layui-col-xs3 swiper-slide" @click="openChatingComm"
|
||||
:class="switchData.openCommRoom ? '':'tl-rtc-file-tool-disabled'">
|
||||
<div class="tl-rtc-file-tool tl-rtc-file-tool-mobile" v-if="clientWidth < 450">
|
||||
<i class="layui-icon layui-icon-reply-fill"></i>
|
||||
<b class="tl-rtc-file-tool-mobile-title">{{lang.chat_comm}}</b>
|
||||
<span v-show="receiveChatCommList.length > 0" class="layui-badge tl-rtc-file-msg-dot"
|
||||
style="right: 5px; top: 4px; width: 7px; height: 7px;position: relative;"></span>
|
||||
</div>
|
||||
<div class="tl-rtc-file-tool" v-else>
|
||||
<i class="layui-icon layui-icon-reply-fill"></i>
|
||||
<b>{{lang.chat_comm}}</b>
|
||||
<div class="tl-rtc-file-tool" :class="clientWidth < 450 ? 'tl-rtc-file-tool-mobile' : ''">
|
||||
<svg class="icon" aria-hidden="true" style="width: 18px;height: 18px;">
|
||||
<use xlink:href="#icon-rtc-file-chat"></use>
|
||||
</svg>
|
||||
<b class="tl-rtc-file-tool-title" :class="clientWidth < 450 ? 'tl-rtc-file-tool-title-mobile' : ''">{{lang.chat_comm}}</b>
|
||||
<span v-show="receiveChatCommList.length > 0" class="layui-badge tl-rtc-file-msg-dot"
|
||||
style="right: 5px; top: 4px; width: 7px; height: 7px;position: relative;"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-xs3 swiper-slide" @click="startScreenShare"
|
||||
:class="switchData.openScreenShare ? '':'tl-rtc-file-tool-disabled'">
|
||||
<div class="tl-rtc-file-tool tl-rtc-file-tool-mobile" v-if="clientWidth < 450">
|
||||
<i class="layui-icon layui-icon-chart-screen" id="screenShareIcon"></i>
|
||||
<b class="tl-rtc-file-tool-mobile-title">{{lang.screen_sharing}}</b>
|
||||
<div class="tl-rtc-file-tool" v-if="isScreenShare" :class="clientWidth < 450 ? 'tl-rtc-file-tool-mobile' : ''">
|
||||
<svg id="screenShareIcon" class="icon" aria-hidden="true" style="width: 18px;height: 18px;">
|
||||
<use xlink:href="#icon-rtc-file-guaduandianhua"></use>
|
||||
</svg>
|
||||
<b class="tl-rtc-file-tool-title" :class="clientWidth < 450 ? 'tl-rtc-file-tool-title-mobile' : ''">{{lang.screen_sharing}}</b>
|
||||
</div>
|
||||
<div class="tl-rtc-file-tool" v-else>
|
||||
<i class="layui-icon layui-icon-chart-screen" id="screenShareIcon"></i>
|
||||
<b>{{lang.screen_sharing}}</b>
|
||||
<div class="tl-rtc-file-tool" v-else :class="clientWidth < 450 ? 'tl-rtc-file-tool-mobile' : ''">
|
||||
<svg id="screenShareIcon" class="icon" aria-hidden="true" style="width: 18px;height: 18px;">
|
||||
<use xlink:href="#icon-rtc-file-pingmugongxiang"></use>
|
||||
</svg>
|
||||
<b class="tl-rtc-file-tool-title" :class="clientWidth < 450 ? 'tl-rtc-file-tool-title-mobile' : ''">{{lang.screen_sharing}}</b>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-xs3 swiper-slide" @click="startVideoShare"
|
||||
:class="switchData.openVideoShare ? '':'tl-rtc-file-tool-disabled'">
|
||||
<div class="tl-rtc-file-tool tl-rtc-file-tool-mobile" v-if="clientWidth < 450">
|
||||
<i class="layui-icon layui-icon-video" id="videoShareIcon"></i>
|
||||
<b class="tl-rtc-file-tool-mobile-title">{{lang.video_call}}</b>
|
||||
<div class="tl-rtc-file-tool" v-if="isVideoShare" :class="clientWidth < 450 ? 'tl-rtc-file-tool-mobile' : ''">
|
||||
<svg id="videoShareIcon" class="icon" aria-hidden="true" style="width: 18px;height: 18px;">
|
||||
<use xlink:href="#icon-rtc-file-guaduandianhua"></use>
|
||||
</svg>
|
||||
<b class="tl-rtc-file-tool-title" :class="clientWidth < 450 ? 'tl-rtc-file-tool-title-mobile' : ''">{{lang.video_call}}</b>
|
||||
</div>
|
||||
<div class="tl-rtc-file-tool" v-else>
|
||||
<i class="layui-icon layui-icon-video" id="videoShareIcon"></i>
|
||||
<b>{{lang.video_call}}</b>
|
||||
<div class="tl-rtc-file-tool" v-else :class="clientWidth < 450 ? 'tl-rtc-file-tool-mobile' : ''">
|
||||
<svg id="videoShareIcon" class="icon" aria-hidden="true" style="width: 18px;height: 18px;">
|
||||
<use xlink:href="#icon-rtc-file-shexiangtou"></use>
|
||||
</svg>
|
||||
<b class="tl-rtc-file-tool-title" :class="clientWidth < 450 ? 'tl-rtc-file-tool-title-mobile' : ''">{{lang.video_call}}</b>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-xs3 swiper-slide" @click="startLiveShare"
|
||||
:class="switchData.openLiveShare ? '':'tl-rtc-file-tool-disabled'">
|
||||
<div class="tl-rtc-file-tool tl-rtc-file-tool-mobile" v-if="clientWidth < 450">
|
||||
<i class="layui-icon layui-icon-fire" id="liveShareIcon"></i>
|
||||
<b class="tl-rtc-file-tool-mobile-title">{{lang.start_live}}</b>
|
||||
<div class="tl-rtc-file-tool " v-if="isLiveShare" :class="clientWidth < 450 ? 'tl-rtc-file-tool-mobile' : ''">
|
||||
<svg id="liveShareIcon" class="icon" aria-hidden="true" style="width: 18px;height: 18px;">
|
||||
<use xlink:href="#icon-rtc-file-guaduandianhua"></use>
|
||||
</svg>
|
||||
<b class="tl-rtc-file-tool-title" :class="clientWidth < 450 ? 'tl-rtc-file-tool-title-mobile' : ''">{{lang.start_live}}</b>
|
||||
</div>
|
||||
<div class="tl-rtc-file-tool" v-else>
|
||||
<i class="layui-icon layui-icon-fire" id="liveShareIcon"></i>
|
||||
<b>{{lang.start_live}}</b>
|
||||
<div class="tl-rtc-file-tool" v-else :class="clientWidth < 450 ? 'tl-rtc-file-tool-mobile' : ''">
|
||||
<svg id="liveShareIcon" class="icon" aria-hidden="true" style="width: 18px;height: 18px;">
|
||||
<use xlink:href="#icon-rtc-file-zhibo"></use>
|
||||
</svg>
|
||||
<b class="tl-rtc-file-tool-title" :class="clientWidth < 450 ? 'tl-rtc-file-tool-title-mobile' : ''">{{lang.start_live}}</b>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-xs3 swiper-slide" @click="getCodeFile"
|
||||
:class="switchData.openGetCodeFile ? '':'tl-rtc-file-tool-disabled'">
|
||||
<div class="tl-rtc-file-tool tl-rtc-file-tool-mobile" v-if="clientWidth < 450">
|
||||
<i class="layui-icon layui-icon-email"></i>
|
||||
<b class="tl-rtc-file-tool-mobile-title">{{lang.pickup_code}}</b>
|
||||
</div>
|
||||
<div class="tl-rtc-file-tool" v-else>
|
||||
<i class="layui-icon layui-icon-email"></i>
|
||||
<b>{{lang.pickup_code}}</b>
|
||||
<div class="tl-rtc-file-tool" :class="clientWidth < 450 ? 'tl-rtc-file-tool-mobile' : ''">
|
||||
<svg id="liveShareIcon" class="icon" aria-hidden="true" style="width: 18px;height: 18px;">
|
||||
<use xlink:href="#icon-rtc-file-wangpan"></use>
|
||||
</svg>
|
||||
<b class="tl-rtc-file-tool-title" :class="clientWidth < 450 ? 'tl-rtc-file-tool-title-mobile' : ''">{{lang.pickup_code}}</b>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-xs3 swiper-slide" @click="openRemoteDraw"
|
||||
:class="switchData.openRemoteDraw ? '':'tl-rtc-file-tool-disabled'">
|
||||
<div class="tl-rtc-file-tool tl-rtc-file-tool-mobile" v-if="clientWidth < 450">
|
||||
<i class="layui-icon layui-icon-console" id="remoteDrawIcon"></i>
|
||||
<b class="tl-rtc-file-tool-mobile-title">{{lang.remote_draw}}</b>
|
||||
</div>
|
||||
<div class="tl-rtc-file-tool" v-else>
|
||||
<i class="layui-icon layui-icon-console" id="remoteDrawIcon"></i>
|
||||
<b>{{lang.remote_draw}}</b>
|
||||
<div class="tl-rtc-file-tool" :class="clientWidth < 450 ? 'tl-rtc-file-tool-mobile' : ''">
|
||||
<svg class="icon" aria-hidden="true" style="width: 18px;height: 18px;">
|
||||
<use xlink:href="#icon-rtc-file-shiwu-huabi"></use>
|
||||
</svg>
|
||||
<b class="tl-rtc-file-tool-title" :class="clientWidth < 450 ? 'tl-rtc-file-tool-title-mobile' : ''">{{lang.remote_draw}}</b>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-xs3 swiper-slide" @click="openLocalScreen"
|
||||
:class="switchData.openScreen ? '':'tl-rtc-file-tool-disabled'">
|
||||
<div class="tl-rtc-file-tool tl-rtc-file-tool-mobile" v-if="clientWidth < 450">
|
||||
<i class="layui-icon layui-icon-radio" id="screenIcon"></i>
|
||||
<b class="tl-rtc-file-tool-mobile-title">{{lang.screen_recording}}</b>
|
||||
</div>
|
||||
<div class="tl-rtc-file-tool" v-else>
|
||||
<i class="layui-icon layui-icon-radio" id="screenIcon"></i>
|
||||
<b>{{lang.screen_recording}}</b>
|
||||
<div class="tl-rtc-file-tool" :class="clientWidth < 450 ? 'tl-rtc-file-tool-mobile' : ''">
|
||||
<svg id="screenIcon" class="icon" aria-hidden="true" style="width: 18px;height: 18px;">
|
||||
<use xlink:href="#icon-rtc-file-luzhi"></use>
|
||||
</svg>
|
||||
<b class="tl-rtc-file-tool-title" :class="clientWidth < 450 ? 'tl-rtc-file-tool-title-mobile' : ''">{{lang.screen_recording}}</b>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-xs3 swiper-slide" @click="startPassword"
|
||||
:class="switchData.openPasswordRoom ? '':'tl-rtc-file-tool-disabled'">
|
||||
<div class="tl-rtc-file-tool tl-rtc-file-tool-mobile" v-if="clientWidth < 450">
|
||||
<i class="layui-icon layui-icon-key"></i>
|
||||
<b class="tl-rtc-file-tool-mobile-title">{{lang.password_room}}</b>
|
||||
</div>
|
||||
<div class="tl-rtc-file-tool" v-else>
|
||||
<i class="layui-icon layui-icon-key"></i>
|
||||
<b>{{lang.password_room}}</b>
|
||||
<div class="tl-rtc-file-tool" :class="clientWidth < 450 ? 'tl-rtc-file-tool-mobile' : ''">
|
||||
<svg class="icon" aria-hidden="true" style="width: 18px;height: 18px;">
|
||||
<use xlink:href="#icon-rtc-file-mima"></use>
|
||||
</svg>
|
||||
<b class="tl-rtc-file-tool-title" :class="clientWidth < 450 ? 'tl-rtc-file-tool-title-mobile' : ''">{{lang.password_room}}</b>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-xs3 swiper-slide" @click="openaiChat"
|
||||
:class="switchData.openAiChat ? '':'tl-rtc-file-tool-disabled'">
|
||||
<div class="tl-rtc-file-tool tl-rtc-file-tool-mobile" v-if="clientWidth < 450">
|
||||
<i class="layui-icon layui-icon-service"></i>
|
||||
<b class="tl-rtc-file-tool-mobile-title" style="letter-spacing: 0px;">{{lang.chat_gpt}}</b>
|
||||
</div>
|
||||
<div class="tl-rtc-file-tool" v-else>
|
||||
<i class="layui-icon layui-icon-service"></i>
|
||||
<b>{{lang.chat_gpt}}</b>
|
||||
<div class="tl-rtc-file-tool" :class="clientWidth < 450 ? 'tl-rtc-file-tool-mobile' : ''">
|
||||
<svg class="icon" aria-hidden="true" style="width: 18px;height: 18px;">
|
||||
<use xlink:href="#icon-rtc-file-24px2x"></use>
|
||||
</svg>
|
||||
<b class="tl-rtc-file-tool-title" :class="clientWidth < 450 ? 'tl-rtc-file-tool-title-mobile' : ''" style="letter-spacing: 0px;">{{lang.chat_gpt}}</b>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="swiper-scrollbar"></div>
|
||||
</div>
|
||||
|
||||
<!-- 自己 -->
|
||||
@@ -996,16 +996,41 @@
|
||||
<div id="videoMask" class="tl-rtc-file-mask-media-list" :style="{left: mediaVideoMaskHeightNum + '%'}">
|
||||
<div class="layui-col-sm2" style="width: 100%;">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">
|
||||
{{lang.video_call}}
|
||||
<i @click="startVideoShare" class="layui-icon layui-icon-close"
|
||||
style="cursor: pointer; right: 10px;position: absolute;"></i>
|
||||
</div>
|
||||
<div class="layui-card-header"> {{lang.video_call}} </div>
|
||||
</div>
|
||||
<div class="layui-card">
|
||||
<div :style="{height: logsHeight+'px',overflowY: 'auto'}">
|
||||
<div :style="{height: logsHeight + 80 +'px',overflowY: 'auto'}">
|
||||
<div class="layui-card-body">
|
||||
<div class="tl-rtc-file-mask-media-container " id="mediaVideoRoomList"> </div>
|
||||
<div class="tl-rtc-file-mask-media-container" id="mediaVideoRoomList">
|
||||
<div class="tl-rtc-file-mask-media-video">
|
||||
<video v-show="isCameraEnabled" id="selfMediaShareVideo"
|
||||
preload="auto" autoplay="autoplay" x-webkit-airplay="true"
|
||||
playsinline ="true" webkit-playsinline ="true" x5-video-player-type="h5"
|
||||
x5-video-player-fullscreen="true" x5-video-orientation="portraint"></video>
|
||||
<svg v-show="!isCameraEnabled" class="icon" aria-hidden="true" style="width: 100%;height: 100%;">
|
||||
<use xlink:href="#icon-rtc-file-shexiangtou_guanbi"></use>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="tl-rtc-file-mask-media-video-tool">
|
||||
<div class="tl-rtc-file-mask-media-video-tool-item" @click="changeShareStream('video','video')">
|
||||
<svg class="icon" aria-hidden="true" style="width: 18px;height: 18px;">
|
||||
<use v-if="isCameraEnabled" xlink:href="#icon-rtc-file-shexiangtou"></use>
|
||||
<use v-else xlink:href="#icon-rtc-file-shexiangtou_guanbi"></use>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="tl-rtc-file-mask-media-video-tool-item" @click="changeShareStream('video','audio')">
|
||||
<svg class="icon" aria-hidden="true" style="width: 18px;height: 18px;">
|
||||
<use v-if="isAudioEnabled" xlink:href="#icon-rtc-file-maikefeng-XDY"></use>
|
||||
<use v-else xlink:href="#icon-rtc-file-guanbimaikefeng"></use>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="tl-rtc-file-mask-media-video-tool-item" @click="startVideoShare()">
|
||||
<svg class="icon" aria-hidden="true" style="width: 18px;height: 18px;">
|
||||
<use xlink:href="#icon-rtc-file-guaduandianhua"></use>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1018,14 +1043,35 @@
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header" style="text-align: left;">
|
||||
{{lang.screen_sharing}}
|
||||
<i @click="startScreenShare" class="layui-icon layui-icon-close"
|
||||
style="cursor: pointer; right: 10px;position: absolute;"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-card">
|
||||
<div :style="{height: logsHeight+'px',overflowY: 'auto'}">
|
||||
<div :style="{height: logsHeight + 80 +'px',overflowY: 'auto'}">
|
||||
<div class="layui-card-body">
|
||||
<div class="tl-rtc-file-mask-media-container " id="mediaScreenRoomList"> </div>
|
||||
<div class="tl-rtc-file-mask-media-container" id="mediaScreenRoomList">
|
||||
<div class="tl-rtc-file-mask-media-video">
|
||||
<video v-show="isCameraEnabled" id="selfMediaShareScreen"
|
||||
preload="auto" autoplay="autoplay" x-webkit-airplay="true"
|
||||
playsinline ="true" webkit-playsinline ="true" x5-video-player-type="h5"
|
||||
x5-video-player-fullscreen="true" x5-video-orientation="portraint"></video>
|
||||
<svg v-show="!isCameraEnabled" class="icon" aria-hidden="true" style="width: 100%;height: 100%;">
|
||||
<use xlink:href="#icon-rtc-file-guanbipingmu"></use>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="tl-rtc-file-mask-media-video-tool">
|
||||
<div class="tl-rtc-file-mask-media-video-tool-item" @click="changeShareStream('screen','video')">
|
||||
<svg class="icon" aria-hidden="true" style="width: 18px;height: 18px;">
|
||||
<use v-if="isCameraEnabled" xlink:href="#icon-rtc-file-pingmugongxiang"></use>
|
||||
<use v-else xlink:href="#icon-rtc-file-guanbipingmu"></use>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="tl-rtc-file-mask-media-video-tool-item" @click="startScreenShare()">
|
||||
<svg class="icon" aria-hidden="true" style="width: 18px;height: 18px;">
|
||||
<use xlink:href="#icon-rtc-file-guaduandianhua"></use>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1037,15 +1083,55 @@
|
||||
<div class="layui-col-sm2" style="width: 100%;">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header" style="text-align: left;">
|
||||
{{lang.start_live}}
|
||||
<i @click="startLiveShare" class="layui-icon layui-icon-close"
|
||||
style="cursor: pointer; right: 10px;position: absolute;"></i>
|
||||
{{lang.start_live}} - {{owner ? lang.owner : lang.audience}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-card">
|
||||
<div :style="{height: logsHeight+'px',overflowY: 'auto'}">
|
||||
<div :style="{height: logsHeight + 80 +'px',overflowY: 'auto'}">
|
||||
<div class="layui-card-body">
|
||||
<div class="tl-rtc-file-mask-media-container " id="mediaLiveRoomList"> </div>
|
||||
<div class="tl-rtc-file-mask-media-container" id="mediaLiveRoomList">
|
||||
<div class="tl-rtc-file-mask-media-video" v-show="owner">
|
||||
<video v-show="isCameraEnabled" id="selfMediaShareLive"
|
||||
preload="auto" autoplay="autoplay" x-webkit-airplay="true"
|
||||
playsinline ="true" webkit-playsinline ="true" x5-video-player-type="h5"
|
||||
x5-video-player-fullscreen="true" x5-video-orientation="portraint"></video>
|
||||
<svg v-show="!isCameraEnabled" class="icon" aria-hidden="true" style="width: 100%;height: 100%;">
|
||||
<use v-if="liveShareMode === 'video'" xlink:href="#icon-rtc-file-shexiangtou_guanbi"></use>
|
||||
<use v-else xlink:href="#icon-rtc-file-guanbipingmu"></use>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="tl-rtc-file-mask-media-video-tool" v-show="owner">
|
||||
<div v-show="changeLiveShareMediaTrackAndStreamTime === 0" class="tl-rtc-file-mask-media-video-tool-item" @click="changeLiveShareMediaTrackAndStream()">
|
||||
<svg class="icon" aria-hidden="true" style="width: 18px;height: 18px;">
|
||||
<use xlink:href="#icon-rtc-file-fanzhuanjingtou"></use>
|
||||
</svg>
|
||||
</div>
|
||||
<div v-show="changeLiveShareMediaTrackAndStreamTime > 0" class="tl-rtc-file-mask-media-video-tool-item layui-disabled" style="padding: 2px 10px 2px 10px">
|
||||
{{changeLiveShareMediaTrackAndStreamTime}}
|
||||
</div>
|
||||
<div class="tl-rtc-file-mask-media-video-tool-item" @click="changeShareStream('live', 'video')">
|
||||
<svg v-if="liveShareMode === 'video'" class="icon" aria-hidden="true" style="width: 18px;height: 18px;">
|
||||
<use v-if="isCameraEnabled" xlink:href="#icon-rtc-file-shexiangtou"></use>
|
||||
<use v-else xlink:href="#icon-rtc-file-shexiangtou_guanbi"></use>
|
||||
</svg>
|
||||
<svg v-else class="icon" aria-hidden="true" style="width: 18px;height: 18px;">
|
||||
<use v-if="isCameraEnabled" xlink:href="#icon-rtc-file-pingmugongxiang"></use>
|
||||
<use v-else xlink:href="#icon-rtc-file-guanbipingmu"></use>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="tl-rtc-file-mask-media-video-tool-item" @click="changeShareStream('live','audio')">
|
||||
<svg class="icon" aria-hidden="true" style="width: 18px;height: 18px;">
|
||||
<use v-if="isAudioEnabled" xlink:href="#icon-rtc-file-maikefeng-XDY"></use>
|
||||
<use v-else xlink:href="#icon-rtc-file-guanbimaikefeng"></use>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="tl-rtc-file-mask-media-video-tool-item" @click="startLiveShare()">
|
||||
<svg class="icon" aria-hidden="true" style="width: 18px;height: 18px;">
|
||||
<use xlink:href="#icon-rtc-file-guaduandianhua"></use>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -214,64 +214,6 @@ window.tlrtcfile = {
|
||||
}
|
||||
}
|
||||
},
|
||||
closeFullVideo: function (node, type, from) {
|
||||
let stream = node.srcObject;
|
||||
let nodeId = node.id.substr(0, node.id.length - 5);
|
||||
if (window.layer) {
|
||||
layer.closeAll()
|
||||
}
|
||||
$(`${type === 'screen' ? '#mediaScreenRoomList' : '#mediaVideoRoomList'}`).append(`
|
||||
<div class="tl-rtc-file-mask-media-video">
|
||||
<video id="${nodeId}" autoplay playsinline onclick="tlrtcfile.openFullVideo(this,'${type}', '${from}')"></video>
|
||||
</div>
|
||||
`);
|
||||
var video = document.querySelector("#" + nodeId);
|
||||
video.srcObject = stream
|
||||
|
||||
video.addEventListener('loadedmetadata', function () {
|
||||
// ios 微信浏览器兼容问题
|
||||
video.play();
|
||||
document.addEventListener('WeixinJSBridgeReady', function () {
|
||||
video.play();
|
||||
}, false);
|
||||
});
|
||||
|
||||
},
|
||||
openFullVideo: function (node, type, from) {
|
||||
let stream = node.srcObject;
|
||||
let nodeId = node.id + "_full";
|
||||
if (window.layer) {
|
||||
layer.open({
|
||||
type: 1,
|
||||
title: false,
|
||||
area: ["95%", "auto"],
|
||||
shade: 0.3,
|
||||
content: `
|
||||
${from === 'self' ? '<b style="position: absolute;left: 5px; top: 8px;">自己</b>' : ''}
|
||||
<video style="width:100%;height:100%;border-radius:8px;" id="${nodeId}" autoplay playsinline onclick="tlrtcfile.closeFullVideo(this, '${type}', '${from}')"></video>
|
||||
`,
|
||||
success: function (layero) {
|
||||
document.querySelector("#" + nodeId).parentElement.style.height = "auto"
|
||||
|
||||
let video = document.querySelector("#" + nodeId);
|
||||
video.srcObject = stream;
|
||||
video.addEventListener('loadedmetadata', function () {
|
||||
// ios 微信浏览器兼容问题
|
||||
video.play();
|
||||
document.addEventListener('WeixinJSBridgeReady', function () {
|
||||
video.play();
|
||||
}, false);
|
||||
});
|
||||
|
||||
//并且需要移除父节点
|
||||
document.querySelector("#" + node.id).parentElement.remove();
|
||||
},
|
||||
cancel: function () {
|
||||
tlrtcfile.closeFullVideo(document.querySelector("#" + nodeId), type, from);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
supposeWebrtc: function (rtcConfig) {
|
||||
try {
|
||||
let testRTCPeerConnection = window.RTCPeerConnection || window.webkitRTCPeerConnection || window.mozRTCPeerConnection || window.RTCIceGatherer;
|
||||
|
||||
@@ -423,7 +423,9 @@ const draw = new Vue({
|
||||
}
|
||||
|
||||
document.getElementById("drawImage").onclick = function () {
|
||||
that.drawImage({ canvas, context, localDrawCallback })
|
||||
that.drawImage({ local : {
|
||||
canvas, context, localDrawCallback
|
||||
} })
|
||||
}
|
||||
|
||||
document.getElementById("drawDonwload").onclick = function () {
|
||||
|
||||
@@ -57,6 +57,13 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
isMouseDrag : false, //是否正在拖拽鼠标
|
||||
isSendAllWaiting : false, //一键发送文件时,有1秒时间间隔,这个记录当前是否是一键发送文件等待中
|
||||
isShareJoin : false, //是否是分享加入房间
|
||||
isCameraEnabled : true, //音视频场景下自己的摄像头是否开启
|
||||
isAudioEnabled : true, //音视频场景下自己的麦克风是否开启
|
||||
isLoudspeakerEnabled : true, //音视频场景下自己的扬声器是否开启
|
||||
facingMode : "user", //音视频场景下自己的摄像头前后置 ,user, environment
|
||||
videoDeviceId : "", //音视频场景下自己的摄像头设备id
|
||||
audioDeviceId : "", //音视频场景下自己的麦克风设备id
|
||||
loudspeakerDeviceId : "", //音视频场景下自己的扬声器设备id
|
||||
|
||||
sendFileMaskHeightNum: 150, // 用于控制发送文件列表面板展示
|
||||
chooseFileMaskHeightNum: 150, // 用于控制选择文件列表面板展示
|
||||
@@ -82,6 +89,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
socketId: 0, //本人的id
|
||||
roomId: "10086", //房间号
|
||||
roomType : "file", //房间类型
|
||||
liveShareMode : "video", //直播类型 video, screen
|
||||
codeId: "", //取件码
|
||||
recoderId: 0, //记录id
|
||||
rtcConns: {}, //远程连接
|
||||
@@ -110,6 +118,9 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
preMouseMove : {}, //上一次鼠标移动的事件
|
||||
ips: [], // 记录ip列表,检测是否支持p2p
|
||||
popUpMsgDom : [], // 消息弹窗dom
|
||||
videoDeviceList : [], //摄像头设备列表
|
||||
audioDeviceList : [], //麦克风设备列表
|
||||
loudspeakerDeviceList : [], //扬声器设备列表
|
||||
|
||||
|
||||
token: "", //登录token
|
||||
@@ -118,6 +129,9 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
aiAnsweringTxtIntervalId: 0, //实现等待动画
|
||||
aiAnsweringTxt: "思考中...", //ai思考中的文字
|
||||
logsFilter: "", //日志过滤参数
|
||||
changeLiveShareMediaTrackAndStreamTimeLimit : 9, //切换直播媒体流的时间限制, 允许9s内切换一次
|
||||
changeLiveShareMediaTrackAndStreamTime : 0, //可以下一次切换直播媒体流的时间
|
||||
changeLiveShareMediaTrackAndStreamIntervalId : 0, //切换直播媒体流的时间间隔id
|
||||
clientWidth : document.body.clientWidth, //页面宽度
|
||||
}
|
||||
},
|
||||
@@ -155,7 +169,25 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
},
|
||||
isMediaing : function(){
|
||||
return this.isVideoShare || this.isScreenShare || this.isLiveShare
|
||||
}
|
||||
},
|
||||
videoConstraints : function(){
|
||||
return {
|
||||
audio: true,
|
||||
video: {
|
||||
facingMode: this.facingMode,
|
||||
deviceId: this.videoDeviceId,
|
||||
width: {
|
||||
ideal : 200, max : 200, min : 100
|
||||
},
|
||||
height: {
|
||||
ideal : 150, max : 150, min : 150
|
||||
},
|
||||
frameRate: {
|
||||
ideal: 100, max: 100
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
isAiAnswering: function (newV, oldV) {
|
||||
@@ -204,6 +236,99 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
changeLiveShareMediaTrackAndStream : async function(){
|
||||
//切换媒体流之前,先将已关闭的媒体流打开之后再进行切换
|
||||
if(this.liveShareMode === 'live'){
|
||||
//屏幕共享流只需要打开video通道即可
|
||||
await this.changeShareStream('live', 'video', 'open')
|
||||
}else if(this.liveShareMode === 'video'){
|
||||
//视频流需要打开video和audio通道
|
||||
await this.changeShareStream('live', 'video', 'open')
|
||||
await this.changeShareStream('live', 'audio', 'open')
|
||||
}
|
||||
|
||||
let that = this;
|
||||
this.liveShareMode = this.liveShareMode === 'video' ? 'screen' : 'video';
|
||||
window.Bus.$emit("changeLiveShareMediaTrackAndStream", {
|
||||
type : this.liveShareMode,
|
||||
kind : 'video',
|
||||
constraints : this.videoConstraints,
|
||||
rtcConns : this.rtcConns,
|
||||
callback : (success) => {
|
||||
if(!success){ //没有成功,切换回来
|
||||
that.liveShareMode = that.liveShareMode === 'video' ? 'screen' : 'video';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if(this.changeLiveShareMediaTrackAndStreamTime === 0){
|
||||
this.changeLiveShareMediaTrackAndStreamTime = this.changeLiveShareMediaTrackAndStreamTimeLimit - 1;
|
||||
//10s内只允许切换一次
|
||||
this.changeLiveShareMediaTrackAndStreamIntervalId = setInterval(() => {
|
||||
if(this.changeLiveShareMediaTrackAndStreamTime === 0){
|
||||
clearInterval(this.changeLiveShareMediaTrackAndStreamIntervalId)
|
||||
return
|
||||
}
|
||||
this.changeLiveShareMediaTrackAndStreamTime -= 1;
|
||||
}, 1000);
|
||||
}
|
||||
},
|
||||
changeShareStream : async function(type, kind, value){
|
||||
let stream = null;
|
||||
|
||||
if(type === 'video'){
|
||||
stream = await new Promise((resolve, reject) => {
|
||||
window.Bus.$emit("getVideoShareTrackAndStream", (track, stream) => {
|
||||
resolve(stream)
|
||||
})
|
||||
});
|
||||
}else if(type === 'screen'){
|
||||
stream = await new Promise((resolve, reject) => {
|
||||
stream = window.Bus.$emit("getScreenShareTrackAndStream", (track, stream) => {
|
||||
resolve(stream)
|
||||
});
|
||||
});
|
||||
}else if(type === 'live'){
|
||||
stream = await new Promise((resolve, reject) => {
|
||||
stream = window.Bus.$emit("getLiveShareTrackAndStream", (track, stream) => {
|
||||
resolve(stream)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if(kind === 'video'){
|
||||
//指定开关
|
||||
if(value && ['open','close'].includes(value)){
|
||||
stream.getVideoTracks()[0].enabled = value === 'open'
|
||||
this.isCameraEnabled = value === 'open'
|
||||
}else{
|
||||
//根据当前状态取反
|
||||
stream.getVideoTracks()[0].enabled = !stream.getVideoTracks()[0].enabled
|
||||
this.isCameraEnabled = !this.isCameraEnabled
|
||||
}
|
||||
}
|
||||
if(kind === 'audio'){
|
||||
//指定开关
|
||||
if(value && ['open','close'].includes(value)){
|
||||
stream.getAudioTracks()[0].enabled = value === 'open'
|
||||
this.isAudioEnabled = value === 'open'
|
||||
}else{
|
||||
//根据当前状态取反
|
||||
stream.getAudioTracks()[0].enabled = !stream.getAudioTracks()[0].enabled
|
||||
this.isAudioEnabled = !this.isAudioEnabled
|
||||
}
|
||||
}
|
||||
this.socket.emit('message', {
|
||||
emitType: "openCamera",
|
||||
room: this.roomId,
|
||||
from : this.socketId,
|
||||
type : type,
|
||||
kind : kind,
|
||||
isCameraEnabled : this.isCameraEnabled,
|
||||
isAudioEnabled : this.isAudioEnabled
|
||||
});
|
||||
|
||||
},
|
||||
updateRemoteRtcState : async function(){
|
||||
for(let id in this.remoteMap){
|
||||
let stat = await window.tlrtcfile.getWebrtcStats(
|
||||
@@ -1276,7 +1401,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
that.addUserLogs(that.lang.start_video_call);
|
||||
}else{
|
||||
layer.prompt({
|
||||
formType: 1,
|
||||
formType: 0,
|
||||
title: that.lang.please_enter_video_call_room_num
|
||||
}, function (value, index, elem) {
|
||||
that.roomId = value;
|
||||
@@ -1335,7 +1460,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
that.addUserLogs(that.lang.start_screen_sharing);
|
||||
}else{
|
||||
layer.prompt({
|
||||
formType: 1,
|
||||
formType: 0,
|
||||
title: this.lang.please_enter_screen_sharing_room_num,
|
||||
}, function (value, index, elem) {
|
||||
that.roomId = value;
|
||||
@@ -1394,21 +1519,51 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
that.addUserLogs(that.lang.start_live);
|
||||
}else{
|
||||
layer.prompt({
|
||||
formType: 1,
|
||||
formType: 0,
|
||||
title: this.lang.please_enter_live_room_num,
|
||||
}, function (value, index, elem) {
|
||||
that.roomId = value;
|
||||
that.createMediaRoom("live");
|
||||
layer.close(index)
|
||||
btn : ['视频直播', '屏幕直播', '取消'],
|
||||
yes: function (index, layero) {
|
||||
const value = $('#layui-layer'+index + " .layui-layer-input").val();
|
||||
if(value === ''){
|
||||
return
|
||||
}
|
||||
that.roomId = value;
|
||||
that.liveShareMode = "video";
|
||||
that.createMediaRoom("live");
|
||||
layer.close(index)
|
||||
|
||||
that.socket.emit('message', {
|
||||
emitType: "startLiveShare",
|
||||
room: that.roomId,
|
||||
to : that.socketId
|
||||
});
|
||||
that.clickMediaLive();
|
||||
that.isLiveShare = !that.isLiveShare;
|
||||
that.addUserLogs(that.lang.start_live);
|
||||
that.socket.emit('message', {
|
||||
emitType: "startLiveShare",
|
||||
room: that.roomId,
|
||||
to : that.socketId,
|
||||
liveShareMode : that.liveShareMode
|
||||
});
|
||||
that.clickMediaLive();
|
||||
that.isLiveShare = !that.isLiveShare;
|
||||
that.addUserLogs(that.lang.start_live);
|
||||
return false
|
||||
},
|
||||
btn2: function (index, layero) {
|
||||
const value = $('#layui-layer'+index + " .layui-layer-input").val();
|
||||
if(value === ''){
|
||||
return false
|
||||
}
|
||||
that.roomId = value;
|
||||
that.liveShareMode = "screen";
|
||||
that.createMediaRoom("live");
|
||||
layer.close(index)
|
||||
|
||||
that.socket.emit('message', {
|
||||
emitType: "startLiveShare",
|
||||
room: that.roomId,
|
||||
to : that.socketId,
|
||||
liveShareMode : that.liveShareMode
|
||||
});
|
||||
that.clickMediaLive();
|
||||
that.isLiveShare = !that.isLiveShare;
|
||||
that.addUserLogs(that.lang.start_live);
|
||||
return false
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
@@ -2207,8 +2362,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
this.setNickName();
|
||||
this.socket.emit('createAndJoin', {
|
||||
room: this.roomId,
|
||||
type : 'password',
|
||||
password : '',
|
||||
type : 'file',
|
||||
nickName : this.nickName,
|
||||
langMode : this.langMode,
|
||||
ua: this.isMobile ? 'mobile' : 'pc',
|
||||
@@ -2317,6 +2471,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
let that = this;
|
||||
let rtcConnect = new RTCPeerConnection(this.config);
|
||||
|
||||
//ice
|
||||
rtcConnect.onicecandidate = (e) => {
|
||||
that.iceCandidate(rtcConnect, id, e)
|
||||
};
|
||||
@@ -2328,68 +2483,13 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
})
|
||||
}
|
||||
|
||||
//保存peer连接
|
||||
this.rtcConns[id] = rtcConnect;
|
||||
if (!this.remoteMap[id]) {
|
||||
Vue.set(this.remoteMap, id, {
|
||||
id: id,
|
||||
receiveChatRoomSingleList : [],
|
||||
p2pMode : '识别中...'
|
||||
})
|
||||
}
|
||||
|
||||
//数据通道
|
||||
this.initSendDataChannel(id);
|
||||
|
||||
rtcConnect.onremovestream = (e) => {
|
||||
that.removeStream(rtcConnect, id, e)
|
||||
//媒体流通道
|
||||
rtcConnect.ontrack = (e) => {
|
||||
that.mediaTrackHandler(e, id)
|
||||
};
|
||||
|
||||
return rtcConnect;
|
||||
},
|
||||
//获取本地与远程连接
|
||||
getOrCreateRtcConnect: function (id) {
|
||||
// 获取rtc缓存连接
|
||||
let rtcConnect = this.rtcConns[id];
|
||||
// 不存在,创建一个
|
||||
if (typeof (rtcConnect) == 'undefined') {
|
||||
rtcConnect = this.createRtcConnect(id);
|
||||
}
|
||||
return rtcConnect;
|
||||
},
|
||||
//当前用户开启了媒体流时 建立 stream share 链接
|
||||
initMediaShareChannel: function (rtcConnect, type, track, stream) {
|
||||
rtcConnect.ontrack = (event) => {
|
||||
if(event.track.kind === 'audio'){
|
||||
return;
|
||||
}
|
||||
$(`${type === 'screen' ? '#mediaScreenRoomList' : type === 'video' ? '#mediaVideoRoomList' : '#mediaLiveRoomList'}`).append(`
|
||||
<div class="tl-rtc-file-mask-media-video">
|
||||
<video id="otherMediaShareVideo" autoplay playsinline onclick="tlrtcfile.openFullVideo(this, '${type}', 'other')"></video>
|
||||
</div>
|
||||
`);
|
||||
|
||||
var video = document.querySelector("#otherMediaShareVideo");
|
||||
video.srcObject = event.streams[0]
|
||||
// ios 微信浏览器兼容问题
|
||||
video.addEventListener('loadedmetadata', function() {
|
||||
video.play();
|
||||
document.addEventListener('WeixinJSBridgeReady', function () {
|
||||
video.play();
|
||||
}, false);
|
||||
});
|
||||
};
|
||||
|
||||
if (track && stream) {
|
||||
rtcConnect.addTrack(track, stream);
|
||||
}
|
||||
},
|
||||
//连接创立时建立 send/receive Channel链接
|
||||
initSendDataChannel: function (id) {
|
||||
let that = this;
|
||||
|
||||
//文件发送数据通道
|
||||
let sendFileDataChannel = this.rtcConns[id].createDataChannel('sendFileDataChannel');
|
||||
let sendFileDataChannel = rtcConnect.createDataChannel('sendFileDataChannel');
|
||||
sendFileDataChannel.binaryType = 'arraybuffer';
|
||||
sendFileDataChannel.addEventListener('open', (event) => {
|
||||
if (sendFileDataChannel.readyState === 'open') {
|
||||
@@ -2408,7 +2508,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
});
|
||||
|
||||
//自定义数据发送通道
|
||||
let sendDataChannel = this.rtcConns[id].createDataChannel('sendDataChannel');
|
||||
let sendDataChannel = rtcConnect.createDataChannel('sendDataChannel');
|
||||
sendDataChannel.binaryType = 'arraybuffer';
|
||||
sendDataChannel.addEventListener('open', (event) => {
|
||||
if (sendDataChannel.readyState === 'open') {
|
||||
@@ -2426,13 +2526,125 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
that.removeStream(null, id, null)
|
||||
});
|
||||
|
||||
this.rtcConns[id].addEventListener('datachannel', (event) => {
|
||||
rtcConnect.addEventListener('datachannel', (event) => {
|
||||
that.initReceiveDataChannel(event, id);
|
||||
});
|
||||
this.setRemoteInfo(id, {
|
||||
sendFileDataChannel: sendFileDataChannel,
|
||||
sendDataChannel : sendDataChannel
|
||||
});
|
||||
|
||||
rtcConnect.onremovestream = (e) => {
|
||||
that.removeStream(rtcConnect, id, e)
|
||||
};
|
||||
|
||||
//保存peer连接
|
||||
this.rtcConns[id] = rtcConnect;
|
||||
if (!this.remoteMap[id]) {
|
||||
Vue.set(this.remoteMap, id, {
|
||||
id: id,
|
||||
receiveChatRoomSingleList : [],
|
||||
p2pMode : '识别中...',
|
||||
sendFileDataChannel: sendFileDataChannel,
|
||||
sendDataChannel : sendDataChannel
|
||||
})
|
||||
}
|
||||
|
||||
return rtcConnect;
|
||||
},
|
||||
//获取本地与远程连接
|
||||
getOrCreateRtcConnect: function (id) {
|
||||
// 获取rtc缓存连接
|
||||
let rtcConnect = this.rtcConns[id];
|
||||
// 不存在,创建一个
|
||||
if (typeof (rtcConnect) == 'undefined') {
|
||||
rtcConnect = this.createRtcConnect(id);
|
||||
}
|
||||
return rtcConnect;
|
||||
},
|
||||
//远程媒体流处理
|
||||
mediaTrackHandler: function(event, id){
|
||||
let that = this;
|
||||
if(event.track.kind === 'audio'){
|
||||
return;
|
||||
}
|
||||
|
||||
let video = null;
|
||||
if(this.roomType === 'video'){
|
||||
$(`#mediaVideoRoomList`).append(`
|
||||
<div class="tl-rtc-file-mask-media-video">
|
||||
<video id="otherMediaVideoShare${id}" preload="auto" autoplay="autoplay" x-webkit-airplay="true" playsinline ="true" webkit-playsinline ="true" x5-video-player-type="h5" x5-video-player-fullscreen="true" x5-video-orientation="portraint" ></video>
|
||||
<svg id="otherMediaVideoShareVideoSvg${id}" class="icon" aria-hidden="true" style="width: 100%;height: 100%;display:none;">
|
||||
<use xlink:href="#icon-rtc-file-shexiangtou_guanbi"></use>
|
||||
</svg>
|
||||
<svg id="otherMediaVideoShareAudioOpenSvg${id}" class="tl-rtc-file-mask-media-video-other-audio" aria-hidden="true">
|
||||
<use xlink:href="#icon-rtc-file-maikefeng-XDY"></use>
|
||||
</svg>
|
||||
<svg id="otherMediaVideoShareAudioCloseSvg${id}" class="tl-rtc-file-mask-media-video-other-audio" aria-hidden="true" style="display:none;">
|
||||
<use xlink:href="#icon-rtc-file-guanbimaikefeng"></use>
|
||||
</svg>
|
||||
</div>
|
||||
`);
|
||||
video = document.querySelector(`#otherMediaVideoShare${id}`);
|
||||
} else if(this.roomType === 'screen'){
|
||||
$(`#mediaScreenRoomList`).append(`
|
||||
<div class="tl-rtc-file-mask-media-video">
|
||||
<video id="otherMediaScreenShare${id}" playsinline ="true" webkit-playsinline ="true" x5-video-player-type="h5" x5-video-player-fullscreen="true" x5-video-orientation="portraint" ></video>
|
||||
<svg id="otherMediaScreenShareVideoSvg${id}" class="icon" aria-hidden="true" style="width: 100%;height: 100%;display:none;">
|
||||
<use xlink:href="#icon-rtc-file-guanbipingmu"></use>
|
||||
</svg>
|
||||
</div>
|
||||
`);
|
||||
video = document.querySelector(`#otherMediaScreenShare${id}`);
|
||||
} else if(this.roomType === 'live'){
|
||||
if(this.liveShareMode === 'video'){
|
||||
$(`#mediaLiveRoomList`).append(`
|
||||
<div class="tl-rtc-file-mask-media-video">
|
||||
<video id="otherMediaLiveShare${id}" playsinline ="true" webkit-playsinline ="true" x5-video-player-type="h5" x5-video-player-fullscreen="true" x5-video-orientation="portraint" ></video>
|
||||
<svg id="otherMediaLiveShareVideoSvg${id}" class="icon" aria-hidden="true" style="width: 100%;height: 100%;display:none;">
|
||||
<use xlink:href="#icon-rtc-file-shexiangtou_guanbi"></use>
|
||||
</svg>
|
||||
<svg id="otherMediaLiveShareAudioOpenSvg${id}" class="tl-rtc-file-mask-media-video-other-audio" aria-hidden="true">
|
||||
<use xlink:href="#icon-rtc-file-maikefeng-XDY"></use>
|
||||
</svg>
|
||||
<svg id="otherMediaLiveShareAudioCloseSvg${id}" class="tl-rtc-file-mask-media-video-other-audio" aria-hidden="true" style="display:none;">
|
||||
<use xlink:href="#icon-rtc-file-guanbimaikefeng"></use>
|
||||
</svg>
|
||||
</div>
|
||||
`);
|
||||
}else if(this.liveShareMode === 'screen'){
|
||||
$(`#mediaLiveRoomList`).append(`
|
||||
<div class="tl-rtc-file-mask-media-video">
|
||||
<video id="otherMediaLiveShare${id}" playsinline ="true" webkit-playsinline ="true" x5-video-player-type="h5" x5-video-player-fullscreen="true" x5-video-orientation="portraint"></video>
|
||||
<svg id="otherMediaLiveShareVideoSvg${id}" class="icon" aria-hidden="true" style="width: 100%;height: 100%;display:none;">
|
||||
<use xlink:href="#icon-rtc-file-shexiangtou_guanbi"></use>
|
||||
</svg>
|
||||
<svg id="otherMediaLiveShareAudioOpenSvg${id}" class="tl-rtc-file-mask-media-video-other-audio" aria-hidden="true">
|
||||
<use xlink:href="#icon-rtc-file-maikefeng-XDY"></use>
|
||||
</svg>
|
||||
<svg id="otherMediaLiveShareAudioCloseSvg${id}" class="tl-rtc-file-mask-media-video-other-audio" aria-hidden="true" style="display:none;">
|
||||
<use xlink:href="#icon-rtc-file-guanbimaikefeng"></use>
|
||||
</svg>
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
|
||||
video = document.querySelector(`#otherMediaLiveShare${id}`);
|
||||
}
|
||||
|
||||
if(video){
|
||||
video.addEventListener('loadedmetadata', function() {
|
||||
video.play();
|
||||
that.addSysLogs("loadedmetadata")
|
||||
// ios 微信浏览器兼容问题
|
||||
document.addEventListener('WeixinJSBridgeReady', function () {
|
||||
that.addSysLogs("loadedmetadata WeixinJSBridgeReady")
|
||||
video.play();
|
||||
}, false);
|
||||
});
|
||||
document.addEventListener('WeixinJSBridgeReady', function () {
|
||||
that.addSysLogs("WeixinJSBridgeReady")
|
||||
video.play();
|
||||
}, false);
|
||||
video.srcObject = event.streams[0]
|
||||
video.play();
|
||||
}
|
||||
},
|
||||
// 初始发送
|
||||
// pickRecoder : 指定发送记录进行发送
|
||||
@@ -2863,6 +3075,9 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
//移除rtc连接
|
||||
removeStream: function (rtcConnect, id, event) {
|
||||
this.getOrCreateRtcConnect(id).close;
|
||||
const remoteInfo = this.remoteMap[id] || {};
|
||||
const removeIsOwner = remoteInfo.owner;
|
||||
|
||||
delete this.rtcConns[id];
|
||||
delete this.remoteMap[id];
|
||||
|
||||
@@ -2870,6 +3085,18 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
this.sendFileRecoderList = this.sendFileRecoderList.filter(item => {
|
||||
return item.id !== id;
|
||||
})
|
||||
|
||||
if(['live','video','screen'].includes(this.roomType)){
|
||||
//主播异常关闭直播,观众页面强制刷新
|
||||
if(this.roomType === 'live' && removeIsOwner){
|
||||
window.location.reload()
|
||||
}
|
||||
|
||||
//多人音视频/多人屏幕共享,有人异常退出,移除对应的video标签
|
||||
if(this.roomType === 'video' || this.roomType === 'screen'){
|
||||
$(`#otherMediaVideoShare${id}`).parent().remove();
|
||||
}
|
||||
}
|
||||
},
|
||||
// ice
|
||||
iceCandidate: function (rtcConnect, id, event) {
|
||||
@@ -2934,6 +3161,13 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
socketListener: function () {
|
||||
let that = this;
|
||||
|
||||
this.socket.on('connect_error', error => {
|
||||
console.error('connect_error', error);
|
||||
if(error){
|
||||
layer.msg("socket服务连接失败,请检查socket服务是否正常启动 " + error.message);
|
||||
}
|
||||
});
|
||||
|
||||
// created作用是让自己去和其他人建立rtc连接
|
||||
// 1. 对于screen, video房间来说,是双方都需要传输各自的媒体流
|
||||
// 2. 对于live房间来说,只有房主需要获取媒体流
|
||||
@@ -2944,21 +3178,25 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
that.recoderId = data.recoderId;
|
||||
that.owner = data.owner;
|
||||
|
||||
//第一个人进入时,获取媒体流数据,此时无需addTrack,因为房间只有一个连接,addTrack没意义,
|
||||
//因为一个连接无需进行offer收集,offer信息收集是从第二个连接开始。
|
||||
if(data.peers.length === 0){
|
||||
if(data.type === 'screen'){
|
||||
window.Bus.$emit("startScreenShare");
|
||||
}
|
||||
if(data.type === 'video'){
|
||||
window.Bus.$emit("startVideoShare");
|
||||
window.Bus.$emit("startVideoShare", that.videoConstraints);
|
||||
}
|
||||
if(data.type === 'live'){
|
||||
window.Bus.$emit("startLiveShare");
|
||||
window.Bus.$emit("startLiveShare", {
|
||||
liveShareMode : that.liveShareMode
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < data.peers.length; i++) {
|
||||
let otherSocketId = data.peers[i].id;
|
||||
let rtcConnect = that.getOrCreateRtcConnect(otherSocketId);
|
||||
let otherRtcConnect = that.getOrCreateRtcConnect(otherSocketId);
|
||||
// 处理完连接后,更新下昵称
|
||||
that.setRemoteInfo(otherSocketId, {
|
||||
nickName : data.peers[i].nickName,
|
||||
@@ -2972,32 +3210,28 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
})
|
||||
|
||||
await new Promise(resolve => {
|
||||
// 处理音视频情况
|
||||
if (data.type === 'screen') {
|
||||
window.Bus.$emit("startScreenShare", (track, stream) => {
|
||||
that.initMediaShareChannel(rtcConnect, data.type, track, stream)
|
||||
if(data.type === 'screen'){
|
||||
window.Bus.$emit("startScreenShare",(track, stream) => {
|
||||
//其他人将数据流添加到通道中, 此时需要addTrack,因为后面会有offer收集,然后进行answer ...等后续操作
|
||||
otherRtcConnect.addTrack(track, stream);
|
||||
resolve()
|
||||
});
|
||||
}else if (data.type === 'video') {
|
||||
window.Bus.$emit("startVideoShare", (track, stream) => {
|
||||
that.initMediaShareChannel(rtcConnect, data.type, track, stream)
|
||||
resolve()
|
||||
});
|
||||
}else if (data.type === 'live') {
|
||||
window.Bus.$emit("getLiveShareTrackAndStream", (track, stream) => {
|
||||
that.initMediaShareChannel(rtcConnect, data.type, null, null)
|
||||
}else if(data.type === 'video'){
|
||||
window.Bus.$emit("startVideoShare", that.videoConstraints, (track, stream) => {
|
||||
//其他人将数据流添加到通道中, 此时需要addTrack,因为后面会有offer收集,然后进行answer ...等后续操作
|
||||
otherRtcConnect.addTrack(track, stream);
|
||||
resolve()
|
||||
});
|
||||
}else{
|
||||
resolve()
|
||||
resolve();
|
||||
}
|
||||
}).then(()=>{
|
||||
rtcConnect.createOffer(that.options).then(offer => {
|
||||
that.offerSuccess(rtcConnect, otherSocketId, offer);
|
||||
otherRtcConnect.createOffer(that.options).then(offer => {
|
||||
that.offerSuccess(otherRtcConnect, otherSocketId, offer);
|
||||
}, error => {
|
||||
that.offerFailed(rtcConnect, otherSocketId, error);
|
||||
that.offerFailed(otherRtcConnect, otherSocketId, error);
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -3017,20 +3251,30 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
userAgent : data.userAgent,
|
||||
ip : data.ip,
|
||||
})
|
||||
// 处理音视频逻辑
|
||||
|
||||
//这部分逻辑主要是将比当前加入连接更早加入的连接媒体流添加到track中
|
||||
//以便于当前加入的连接可以收到之前的连接的媒体流数据
|
||||
|
||||
if (data.type === 'screen') {
|
||||
//比如多人屏幕共享,后面加入的连接已经在created事件中addTrack了
|
||||
//所以这个地方主要是通知当前连接之前的那一些连接进行addTrack,以便于当前连接能收到
|
||||
window.Bus.$emit("getScreenShareTrackAndStream", (track, stream) => {
|
||||
that.initMediaShareChannel(rtcConnect, data.type, track, stream)
|
||||
rtcConnect.addTrack(track, stream);
|
||||
});
|
||||
}
|
||||
|
||||
if (data.type === 'video') {
|
||||
//比如多人音视频,后面加入的连接已经在created事件中addTrack了
|
||||
//所以这个地方主要是通知当前连接之前的那一些连接进行addTrack,以便于当前连接能收到
|
||||
window.Bus.$emit("getVideoShareTrackAndStream", (track, stream) => {
|
||||
that.initMediaShareChannel(rtcConnect, data.type, track, stream)
|
||||
rtcConnect.addTrack(track, stream);
|
||||
});
|
||||
}
|
||||
|
||||
if (data.type === 'live') {
|
||||
//比如直播,后面加入的都是观众,所以每个观众加入的时候,都会通知一下所有人可以添加媒体流到通道了(这里就是只有房主有媒体流数据)
|
||||
window.Bus.$emit("getLiveShareTrackAndStream", (track, stream) => {
|
||||
that.initMediaShareChannel(rtcConnect, data.type, track, stream)
|
||||
rtcConnect.addTrack(track, stream);
|
||||
});
|
||||
}
|
||||
that.addPopup({
|
||||
@@ -3039,6 +3283,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
this.socket.on('offer', function (data) {
|
||||
that.addSysLogs(that.lang.receive_offer_event + JSON.stringify(data));
|
||||
let rtcConnect = that.getOrCreateRtcConnect(data.from);
|
||||
@@ -3294,7 +3539,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
if (data.reload) {
|
||||
setTimeout(() => {
|
||||
window.location.reload()
|
||||
}, 1000);
|
||||
}, 1300);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -3302,36 +3547,64 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
//关闭共享
|
||||
this.socket.on('stopScreenShare', function (data) {
|
||||
if (data.id === that.socketId) {
|
||||
setTimeout(() => {
|
||||
$("#selfMediaShareVideo").parent().remove();
|
||||
}, 500);
|
||||
that.clickMediaScreen();
|
||||
} else {
|
||||
$("#otherMediaShareVideo").parent().remove();
|
||||
$(`#otherMediaScreenShare${data.id}`).parent().remove();
|
||||
}
|
||||
});
|
||||
|
||||
//关闭共享
|
||||
this.socket.on('openCamera', function (data) {
|
||||
that.setRemoteInfo(data.from, {
|
||||
isCameraEnabled : data.isCameraEnabled,
|
||||
isAudioEnabled : data.isAudioEnabled
|
||||
})
|
||||
if(data.type === 'video'){
|
||||
if(data.kind === 'video'){
|
||||
document.querySelector(`#otherMediaVideoShare${data.from}`).style.display = data.isCameraEnabled ? 'block' : 'none';
|
||||
document.querySelector(`#otherMediaVideoShareVideoSvg${data.from}`).style.display = data.isCameraEnabled ? 'none' : 'block';
|
||||
}else if(data.kind === 'audio'){
|
||||
document.querySelector(`#otherMediaVideoShareAudioOpenSvg${data.from}`).style.display = data.isAudioEnabled ? 'block' : 'none';
|
||||
document.querySelector(`#otherMediaVideoShareAudioCloseSvg${data.from}`).style.display = data.isAudioEnabled ? 'none' : 'block';
|
||||
}
|
||||
}else if(data.type === 'screen'){
|
||||
if(data.kind === 'video'){
|
||||
document.querySelector(`#otherMediaScreenShare${data.from}`).style.display = data.isCameraEnabled ? 'block' : 'none';
|
||||
document.querySelector(`#otherMediaScreenShareVideoSvg${data.from}`).style.display = data.isCameraEnabled ? 'none' : 'block';
|
||||
}else if(data.kind === 'audio'){
|
||||
document.querySelector(`#otherMediaScreenShareAudioOpenSvg${data.from}`).style.display = data.isAudioEnabled ? 'block' : 'none';
|
||||
document.querySelector(`#otherMediaScreenShareAudioCloseSvg${data.from}`).style.display = data.isAudioEnabled ? 'none' : 'block';
|
||||
}
|
||||
}else if(data.type === 'live'){
|
||||
if(data.kind === 'video'){
|
||||
document.querySelector(`#otherMediaLiveShare${data.from}`).style.display = data.isCameraEnabled ? 'block' : 'none';
|
||||
document.querySelector(`#otherMediaLiveShareVideoSvg${data.from}`).style.display = data.isCameraEnabled ? 'none' : 'block';
|
||||
}else if(data.kind === 'audio'){
|
||||
document.querySelector(`#otherMediaLiveShareAudioOpenSvg${data.from}`).style.display = data.isAudioEnabled ? 'block' : 'none';
|
||||
document.querySelector(`#otherMediaLiveShareAudioCloseSvg${data.from}`).style.display = data.isAudioEnabled ? 'none' : 'block';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//关闭音视频
|
||||
this.socket.on('stopVideoShare', function (data) {
|
||||
if (data.id === that.socketId) {
|
||||
setTimeout(() => {
|
||||
$("#selfMediaShareVideo").parent().remove();
|
||||
}, 500);
|
||||
that.clickMediaVideo();
|
||||
} else {
|
||||
$("#otherMediaShareVideo").parent().remove();
|
||||
$(`#otherMediaVideoShare${data.id}`).parent().remove();
|
||||
}
|
||||
});
|
||||
|
||||
//退出直播
|
||||
this.socket.on('stopLiveShare', function (data) {
|
||||
//如果是主动房主退出,所有观众都退出
|
||||
if(data.owner){
|
||||
window.location.reload();
|
||||
return
|
||||
}
|
||||
|
||||
if (data.id === that.socketId) {
|
||||
setTimeout(() => {
|
||||
$("#selfMediaShareVideo").parent().remove();
|
||||
}, 500);
|
||||
that.clickMediaLive();
|
||||
} else {
|
||||
$("#otherMediaShareVideo").parent().remove();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -3500,7 +3773,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
}
|
||||
|
||||
// logs height
|
||||
this.logsHeight = document.documentElement.clientHeight - 55;
|
||||
this.logsHeight = document.documentElement.clientHeight - 130;
|
||||
this.sendFileRecoderHeight = document.querySelector("#send-file-list").clientHeight - 190;
|
||||
this.chooseFileHeight = document.querySelector("#send-file-list-choose").clientHeight - 40;
|
||||
this.sendFileRecoderHistoryHeight = document.querySelector("#send-file-list-history").clientHeight - 40;
|
||||
@@ -3549,7 +3822,6 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
id: this.socketId,
|
||||
room: this.roomId,
|
||||
cost: this.screenShareTimes,
|
||||
to : this.socketId
|
||||
});
|
||||
}
|
||||
this.screenShareTimes = res
|
||||
@@ -3567,7 +3839,6 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
id: this.socketId,
|
||||
room: this.roomId,
|
||||
cost: this.videoShareTimes,
|
||||
to : this.socketId
|
||||
});
|
||||
}
|
||||
this.videoShareTimes = res
|
||||
@@ -3586,7 +3857,6 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
room: this.roomId,
|
||||
cost: this.liveShareTimes,
|
||||
owner : this.owner,
|
||||
to : this.socketId
|
||||
});
|
||||
}
|
||||
this.liveShareTimes = res
|
||||
@@ -3640,6 +3910,9 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
window.Bus.$on("relaySetting", (res) => {
|
||||
this.relaySetting()
|
||||
})
|
||||
window.Bus.$on("addSysLogs", (res) => {
|
||||
this.addSysLogs(res)
|
||||
})
|
||||
},
|
||||
// 初始化选择文件面板
|
||||
renderChooseFileComp: function () {
|
||||
@@ -3744,6 +4017,10 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
loop: false,
|
||||
slidesPerView: this.toolSlidesPerViewCount,
|
||||
observer: true,
|
||||
scrollbar: {
|
||||
el : '.swiper-scrollbar',
|
||||
hide: true,
|
||||
}
|
||||
})
|
||||
window.toolSwiper = toolSwiper;
|
||||
},
|
||||
@@ -3780,11 +4057,13 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
setInterval(() => {
|
||||
this.touchResize()
|
||||
}, 1000);
|
||||
this.addSysLogs(this.lang.window_event_init_done);
|
||||
|
||||
this.addSysLogs(this.lang.webrtc_check_init);
|
||||
setInterval(async () => {
|
||||
await this.updateRemoteRtcState()
|
||||
}, 5000);
|
||||
this.addSysLogs(this.lang.window_event_init_done);
|
||||
this.addSysLogs(this.lang.webrtc_check_init_done);
|
||||
|
||||
this.addSysLogs(this.lang.message_box_init);
|
||||
this.startPopUpMsg()
|
||||
@@ -3807,6 +4086,12 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
this.addSysLogs(this.lang.language_select_init);
|
||||
this.changeLanguage()
|
||||
this.addSysLogs(this.lang.language_select_init_done);
|
||||
|
||||
window.Bus.$emit("getVideoShareDeviceList", (v, a, l) => {
|
||||
this.videoDeviceList = v;
|
||||
this.audioDeviceList = a;
|
||||
this.loudspeakerDeviceList = l;
|
||||
});
|
||||
}, 2000);
|
||||
|
||||
this.addSysLogs(this.lang.debug_init);
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
|
||||
const local_lang = {
|
||||
"en": {
|
||||
"audience" : "Audience",
|
||||
"webrtc_check_init" : "Webrtc check init",
|
||||
"webrtc_check_init_done" : "Webrtc check init done",
|
||||
"add_ice_candidate_failed": "AddIceCandidate failed",
|
||||
"add_ice_candidate_success": "AddIceCandidateSuccess success",
|
||||
"ai_answering": "AI is answering your question, please ask again later",
|
||||
@@ -326,6 +329,9 @@ const local_lang = {
|
||||
"webrtc_ice_state" : "webrtc state"
|
||||
},
|
||||
"zh": {
|
||||
"audience" : "观众",
|
||||
"webrtc_check_init" : "Webrtc检测初始化",
|
||||
"webrtc_check_init_done" : "Webrtc检测初始化完成",
|
||||
"webrtc_ice_state" : "webrtc状态",
|
||||
"nickname" : "用户昵称",
|
||||
"userid" : "用户ID ",
|
||||
|
||||
@@ -15,9 +15,31 @@ var liveShare = new Vue({
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getMediaPlay: function () {
|
||||
getScreenMediaPlay: function () {
|
||||
let media = null;
|
||||
let constraints = {
|
||||
if (window.navigator.getDisplayMedia) {
|
||||
media = window.navigator.getDisplayMedia({
|
||||
video: true,
|
||||
audio:true
|
||||
});
|
||||
} else if (window.navigator.mediaDevices && window.navigator.mediaDevices.getDisplayMedia) {
|
||||
media = window.navigator.mediaDevices.getDisplayMedia({
|
||||
video: true,
|
||||
audio:true
|
||||
});
|
||||
} else if(window.navigator.mediaDevices && window.navigator.mediaDevices.getUserMedia){
|
||||
media = window.navigator.mediaDevices.getUserMedia({
|
||||
video: {
|
||||
mediaSource: 'screen'
|
||||
},
|
||||
audio:true
|
||||
});
|
||||
}
|
||||
return media
|
||||
},
|
||||
getVideoMediaPlay: function (constraints) {
|
||||
let media = null;
|
||||
let defaultConstraints = {
|
||||
// 音频轨道
|
||||
audio:true,
|
||||
// 视频轨道
|
||||
@@ -26,33 +48,36 @@ var liveShare = new Vue({
|
||||
facingMode: true ? "user" : "environment",
|
||||
// 分辨率
|
||||
width: {
|
||||
ideal : parseInt((document.documentElement.clientWidth - 20) / 2),
|
||||
max : document.documentElement.clientWidth,
|
||||
ideal : 200,
|
||||
max : 200,
|
||||
min : 100
|
||||
},
|
||||
height: {
|
||||
ideal : parseInt((document.documentElement.clientHeight - 20) / 2),
|
||||
max : document.documentElement.clientHeight,
|
||||
min : 100
|
||||
ideal : 150,
|
||||
max : 150,
|
||||
min : 150
|
||||
},
|
||||
// 码率
|
||||
frameRate: {
|
||||
ideal: 30,
|
||||
max: 50
|
||||
ideal: 100,
|
||||
max: 100
|
||||
},
|
||||
// 指定设备
|
||||
// deviceId: "",
|
||||
},
|
||||
};
|
||||
if(constraints){
|
||||
defaultConstraints = constraints
|
||||
}
|
||||
if(window.navigator.mediaDevices && window.navigator.mediaDevices.getUserMedia){
|
||||
media = window.navigator.mediaDevices.getUserMedia(constraints);
|
||||
media = window.navigator.mediaDevices.getUserMedia(defaultConstraints);
|
||||
} else if (window.navigator.mozGetUserMedia) {
|
||||
media = navagator.mozGetUserMedia(constraints);
|
||||
media = navagator.mozGetUserMedia(defaultConstraints);
|
||||
} else if (window.navigator.getUserMedia) {
|
||||
media = window.navigator.getUserMedia(constraints)
|
||||
media = window.navigator.getUserMedia(defaultConstraints)
|
||||
} else if (window.navigator.webkitGetUserMedia) {
|
||||
media = new Promise((resolve, reject) => {
|
||||
window.navigator.webkitGetUserMedia(constraints, (res) => {
|
||||
window.navigator.webkitGetUserMedia(defaultConstraints, (res) => {
|
||||
resolve(res)
|
||||
}, (err) => {
|
||||
reject(err)
|
||||
@@ -61,7 +86,7 @@ var liveShare = new Vue({
|
||||
}
|
||||
return media
|
||||
},
|
||||
startLiveShare: async function (id, callback) {
|
||||
startLiveShare: async function ({ liveShareMode, constraints, callback }) {
|
||||
let that = this;
|
||||
|
||||
let msgData = {
|
||||
@@ -71,7 +96,11 @@ var liveShare = new Vue({
|
||||
|
||||
if (this.stream == null) {
|
||||
try {
|
||||
this.stream = await this.getMediaPlay();
|
||||
if(liveShareMode === 'video'){
|
||||
this.stream = await this.getVideoMediaPlay(constraints);
|
||||
}else if(liveShareMode === 'screen'){
|
||||
this.stream = await this.getScreenMediaPlay();
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
msg = msgData[error.message]
|
||||
@@ -83,26 +112,26 @@ var liveShare = new Vue({
|
||||
layer.msg("获取设备权限失败")
|
||||
}
|
||||
window.Bus.$emit("changeLiveShareState", false)
|
||||
if (callback) {
|
||||
callback()
|
||||
}
|
||||
callback && callback()
|
||||
return;
|
||||
}
|
||||
|
||||
$("#mediaLiveRoomList").append(`
|
||||
<div class="tl-rtc-file-mask-media-video">
|
||||
<video id="selfMediaShareVideo" autoplay playsinline onclick="tlrtcfile.openFullVideo(this, 'live', 'self')"></video>
|
||||
</div>
|
||||
`);
|
||||
var video = document.querySelector("#selfMediaShareVideo");
|
||||
video.srcObject = this.stream
|
||||
const video = document.querySelector("#selfMediaShareLive");
|
||||
video.addEventListener('loadedmetadata', function() {
|
||||
// ios 微信浏览器兼容问题
|
||||
window.Bus.$emit("addSysLogs", "loadedmetadata")
|
||||
video.play();
|
||||
document.addEventListener('WeixinJSBridgeReady', function () {
|
||||
window.Bus.$emit("addSysLogs", "loadedmetadata WeixinJSBridgeReady")
|
||||
video.play();
|
||||
}, false);
|
||||
});
|
||||
document.addEventListener('WeixinJSBridgeReady', function () {
|
||||
window.Bus.$emit("addSysLogs", "WeixinJSBridgeReady")
|
||||
video.play();
|
||||
}, false);
|
||||
video.srcObject = this.stream;
|
||||
video.play();
|
||||
|
||||
//计算时间
|
||||
this.interverlId = setInterval(() => {
|
||||
@@ -123,9 +152,7 @@ var liveShare = new Vue({
|
||||
|
||||
this.stream.getTracks().forEach(function (track) {
|
||||
that.track = track;
|
||||
if (callback) {
|
||||
callback(track, that.stream)
|
||||
}
|
||||
callback && callback(track, that.stream)
|
||||
});
|
||||
},
|
||||
stopLiveShare: function () {
|
||||
@@ -149,13 +176,86 @@ var liveShare = new Vue({
|
||||
|
||||
return;
|
||||
},
|
||||
changeLiveShareMediaTrackAndStream: async function ({constraints, type, kind, rtcConns, callback}) {
|
||||
//重新获取流
|
||||
let newStream = null;
|
||||
try{
|
||||
if(type === 'video'){
|
||||
newStream = await this.getVideoMediaPlay(constraints);
|
||||
}else if(type === 'screen'){
|
||||
newStream = await this.getScreenMediaPlay();
|
||||
}
|
||||
}catch(e){
|
||||
console.log("changeLiveShareMediaTrackAndStream error! ", e)
|
||||
}
|
||||
|
||||
//获取流/权限失败
|
||||
if(newStream === null){
|
||||
callback && callback(false)
|
||||
return;
|
||||
}
|
||||
|
||||
if(kind === 'audio'){
|
||||
newStream.getAudioTracks()[0].enabled = true;
|
||||
if(rtcConns){//远程track替换
|
||||
for(let id in rtcConns){
|
||||
const senders = rtcConns[id].getSenders();
|
||||
const sender = senders.find((sender) => (sender.track ? sender.track.kind === 'audio' : false));
|
||||
if(!sender){
|
||||
console.error("changeDevice find sender error! ");
|
||||
return
|
||||
}
|
||||
sender.replaceTrack(newStream.getAudioTracks()[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(kind === 'video'){
|
||||
newStream.getVideoTracks()[0].enabled = true;
|
||||
if(rtcConns){//远程track替换
|
||||
for(let id in rtcConns){
|
||||
const senders = rtcConns[id].getSenders();
|
||||
const sender = senders.find((sender) => (sender.track ? sender.track.kind === 'video' : false));
|
||||
if(!sender){
|
||||
console.error("changeDevice find sender error! ");
|
||||
return
|
||||
}
|
||||
sender.replaceTrack(newStream.getVideoTracks()[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//替换本地流
|
||||
if(!this.stream.getAudioTracks()[0]){
|
||||
this.stream = new MediaStream([newStream.getVideoTracks()[0], newStream.getAudioTracks()[0]]);
|
||||
}else{
|
||||
this.stream = new MediaStream([newStream.getVideoTracks()[0], this.stream.getAudioTracks()[0]]);
|
||||
}
|
||||
const video = document.querySelector("#selfMediaShareLive");
|
||||
video.addEventListener('loadedmetadata', function() {
|
||||
// ios 微信浏览器兼容问题
|
||||
window.Bus.$emit("addSysLogs", "loadedmetadata")
|
||||
video.play();
|
||||
document.addEventListener('WeixinJSBridgeReady', function () {
|
||||
window.Bus.$emit("addSysLogs", "loadedmetadata WeixinJSBridgeReady")
|
||||
video.play();
|
||||
}, false);
|
||||
});
|
||||
document.addEventListener('WeixinJSBridgeReady', function () {
|
||||
window.Bus.$emit("addSysLogs", "WeixinJSBridgeReady")
|
||||
video.play();
|
||||
}, false);
|
||||
video.srcObject = this.stream;
|
||||
video.play();
|
||||
},
|
||||
getLiveShareTrackAndStream: function (callback) {
|
||||
callback(this.track, this.stream)
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted: function () {
|
||||
mounted: async function () {
|
||||
window.Bus.$on("startLiveShare", this.startLiveShare);
|
||||
window.Bus.$on("stopLiveShare", this.stopLiveShare);
|
||||
window.Bus.$on("getLiveShareTrackAndStream", this.getLiveShareTrackAndStream);
|
||||
window.Bus.$on("changeLiveShareMediaTrackAndStream", this.changeLiveShareMediaTrackAndStream);
|
||||
}
|
||||
})
|
||||
@@ -54,26 +54,26 @@ var screenShare = new Vue({
|
||||
layer.msg("获取设备屏幕录制权限失败")
|
||||
}
|
||||
window.Bus.$emit("changeScreenShareState", false)
|
||||
if(callback){
|
||||
callback()
|
||||
}
|
||||
callback && callback()
|
||||
return;
|
||||
}
|
||||
|
||||
$("#mediaScreenRoomList").append(`
|
||||
<div class="tl-rtc-file-mask-media-video">
|
||||
<video style="width:100%;height:30%;" id="selfMediaShareVideo" autoplay playsinline onclick="tlrtcfile.openFullVideo(this, 'screen', 'self')"></video>
|
||||
</div>
|
||||
`);
|
||||
var video = document.querySelector("#selfMediaShareVideo");
|
||||
video.srcObject = this.stream
|
||||
const video = document.querySelector("#selfMediaShareScreen");
|
||||
video.addEventListener('loadedmetadata', function() {
|
||||
// ios 微信浏览器兼容问题
|
||||
window.Bus.$emit("addSysLogs", "loadedmetadata")
|
||||
video.play();
|
||||
document.addEventListener('WeixinJSBridgeReady', function () {
|
||||
window.Bus.$emit("addSysLogs", "loadedmetadata WeixinJSBridgeReady")
|
||||
video.play();
|
||||
}, false);
|
||||
});
|
||||
document.addEventListener('WeixinJSBridgeReady', function () {
|
||||
window.Bus.$emit("addSysLogs", "WeixinJSBridgeReady")
|
||||
video.play();
|
||||
}, false);
|
||||
video.srcObject = this.stream;
|
||||
video.play();
|
||||
|
||||
//计算时间
|
||||
this.interverlId = setInterval(() => {
|
||||
@@ -93,9 +93,7 @@ var screenShare = new Vue({
|
||||
|
||||
this.stream.getTracks().forEach(function (track) {
|
||||
that.track = track;
|
||||
if(callback){
|
||||
callback(track, that.stream)
|
||||
}
|
||||
callback && callback(track, that.stream)
|
||||
});
|
||||
},
|
||||
stopScreenShare: function () {
|
||||
@@ -124,7 +122,7 @@ var screenShare = new Vue({
|
||||
callback(this.track, this.stream)
|
||||
},
|
||||
},
|
||||
mounted: function () {
|
||||
mounted: async function () {
|
||||
window.Bus.$on("startScreenShare", this.startScreenShare);
|
||||
window.Bus.$on("stopScreenShare", this.stopScreenShare);
|
||||
window.Bus.$on("getScreenShareTrackAndStream", this.getScreenShareTrackAndStream);
|
||||
|
||||
@@ -12,12 +12,15 @@ var videoShare = new Vue({
|
||||
times: 0,
|
||||
interverlId: 0,
|
||||
track: null,
|
||||
videoDeviceList: [], // 摄像头列表
|
||||
audioDeviceList: [], // 麦克风列表
|
||||
loudspeakerDeviceList: [], // 扬声器列表
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getMediaPlay: function () {
|
||||
getMediaPlay: function (constraints) {
|
||||
let media = null;
|
||||
let constraints = {
|
||||
let defaultConstraints = {
|
||||
// 音频轨道
|
||||
audio:true,
|
||||
// 视频轨道
|
||||
@@ -26,33 +29,36 @@ var videoShare = new Vue({
|
||||
facingMode: true ? "user" : "environment",
|
||||
// 分辨率
|
||||
width: {
|
||||
ideal : parseInt((document.documentElement.clientWidth - 20) / 2),
|
||||
max : document.documentElement.clientWidth,
|
||||
ideal : 200,
|
||||
max : 200,
|
||||
min : 100
|
||||
},
|
||||
height: {
|
||||
ideal : parseInt((document.documentElement.clientHeight - 20) / 2),
|
||||
max : document.documentElement.clientHeight,
|
||||
min : 100
|
||||
ideal : 150,
|
||||
max : 150,
|
||||
min : 150
|
||||
},
|
||||
// 码率
|
||||
frameRate: {
|
||||
ideal: 30,
|
||||
max: 50
|
||||
ideal: 100,
|
||||
max: 100
|
||||
},
|
||||
// 指定设备
|
||||
// deviceId: "",
|
||||
},
|
||||
};
|
||||
if(constraints){
|
||||
defaultConstraints = constraints
|
||||
}
|
||||
if(window.navigator.mediaDevices && window.navigator.mediaDevices.getUserMedia){
|
||||
media = window.navigator.mediaDevices.getUserMedia(constraints);
|
||||
media = window.navigator.mediaDevices.getUserMedia(defaultConstraints);
|
||||
} else if (window.navigator.mozGetUserMedia) {
|
||||
media = navagator.mozGetUserMedia(constraints);
|
||||
media = navagator.mozGetUserMedia(defaultConstraints);
|
||||
} else if (window.navigator.getUserMedia) {
|
||||
media = window.navigator.getUserMedia(constraints)
|
||||
media = window.navigator.getUserMedia(defaultConstraints)
|
||||
} else if (window.navigator.webkitGetUserMedia) {
|
||||
media = new Promise((resolve, reject) => {
|
||||
window.navigator.webkitGetUserMedia(constraints, (res) => {
|
||||
window.navigator.webkitGetUserMedia(defaultConstraints, (res) => {
|
||||
resolve(res)
|
||||
}, (err) => {
|
||||
reject(err)
|
||||
@@ -61,7 +67,7 @@ var videoShare = new Vue({
|
||||
}
|
||||
return media
|
||||
},
|
||||
startVideoShare: async function (callback) {
|
||||
startVideoShare: async function (constraints, callback) {
|
||||
let that = this;
|
||||
|
||||
let msgData = {
|
||||
@@ -71,7 +77,7 @@ var videoShare = new Vue({
|
||||
|
||||
if (this.stream == null) {
|
||||
try {
|
||||
this.stream = await this.getMediaPlay();
|
||||
this.stream = await this.getMediaPlay(constraints);
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
msg = msgData[error.message]
|
||||
@@ -83,26 +89,26 @@ var videoShare = new Vue({
|
||||
layer.msg("获取设备权限失败")
|
||||
}
|
||||
window.Bus.$emit("changeVideoShareState", false)
|
||||
if (callback) {
|
||||
callback()
|
||||
}
|
||||
callback && callback()
|
||||
return;
|
||||
}
|
||||
|
||||
$("#mediaVideoRoomList").append(`
|
||||
<div class="tl-rtc-file-mask-media-video">
|
||||
<video id="selfMediaShareVideo" autoplay playsinline onclick="tlrtcfile.openFullVideo(this, 'video', 'self')"></video>
|
||||
</div>
|
||||
`);
|
||||
var video = document.querySelector("#selfMediaShareVideo");
|
||||
video.srcObject = this.stream
|
||||
const video = document.querySelector("#selfMediaShareVideo");
|
||||
video.addEventListener('loadedmetadata', function() {
|
||||
window.Bus.$emit("addSysLogs", "loadedmetadata")
|
||||
// ios 微信浏览器兼容问题
|
||||
video.play();
|
||||
document.addEventListener('WeixinJSBridgeReady', function () {
|
||||
window.Bus.$emit("addSysLogs", "loadedmetadata WeixinJSBridgeReady")
|
||||
video.play();
|
||||
}, false);
|
||||
});
|
||||
document.addEventListener('WeixinJSBridgeReady', function () {
|
||||
window.Bus.$emit("addSysLogs", "WeixinJSBridgeReady")
|
||||
video.play();
|
||||
}, false);
|
||||
video.srcObject = this.stream;
|
||||
video.play();
|
||||
|
||||
//计算时间
|
||||
this.interverlId = setInterval(() => {
|
||||
@@ -123,9 +129,7 @@ var videoShare = new Vue({
|
||||
|
||||
this.stream.getTracks().forEach(function (track) {
|
||||
that.track = track;
|
||||
if (callback) {
|
||||
callback(track, that.stream)
|
||||
}
|
||||
callback && callback(track, that.stream)
|
||||
});
|
||||
},
|
||||
stopVideoShare: function () {
|
||||
@@ -149,13 +153,105 @@ var videoShare = new Vue({
|
||||
|
||||
return;
|
||||
},
|
||||
getDeviceList : async function(){
|
||||
const list = await new Promise((resolve, reject) => {
|
||||
navigator.mediaDevices && navigator.mediaDevices.enumerateDevices().then((devices) => {
|
||||
const videoDeviceList = devices.filter((device) => device.kind === "videoinput" && device.deviceId !== "default");
|
||||
const audioDeviceList = devices.filter((device) => device.kind === "audioinput" && device.deviceId !== "default");
|
||||
const loudspeakerDeviceList = devices.filter((device) => device.kind === "audiooutput" && device.deviceId !== "default");
|
||||
resolve({
|
||||
videoDeviceList, audioDeviceList, loudspeakerDeviceList
|
||||
})
|
||||
}, (err) => {
|
||||
console.error("getDeviceList error !")
|
||||
reject({
|
||||
videoDeviceList : [], audioDeviceList : [], loudspeakerDeviceList : []
|
||||
})
|
||||
});
|
||||
})
|
||||
this.audioDeviceList = list.audioDeviceList;
|
||||
this.videoDeviceList = list.videoDeviceList;
|
||||
this.loudspeakerDeviceList = list.loudspeakerDeviceList;
|
||||
},
|
||||
changeVideoShareDevice: async function ({constraints, kind, rtcConnect}) {
|
||||
//重新获取流
|
||||
let newStream = null;
|
||||
try{
|
||||
newStream = await this.getMediaPlay(constraints);
|
||||
}catch(e){
|
||||
console.log("changeLiveShareMediaTrackAndStream error! ", e)
|
||||
}
|
||||
|
||||
//获取流/权限失败
|
||||
if(newStream === null){
|
||||
callback && callback(false)
|
||||
return;
|
||||
}
|
||||
|
||||
if(kind === 'audio'){
|
||||
newStream.getAudioTracks()[0].enabled = true;
|
||||
if(rtcConns){//远程track替换
|
||||
for(let id in rtcConns){
|
||||
const senders = rtcConns[id].getSenders();
|
||||
const sender = senders.find((sender) => (sender.track ? sender.track.kind === 'audio' : false));
|
||||
if(!sender){
|
||||
console.error("changeDevice find sender error! ");
|
||||
return
|
||||
}
|
||||
sender.replaceTrack(newStream.getAudioTracks()[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(kind === 'video'){
|
||||
newStream.getVideoTracks()[0].enabled = true;
|
||||
if(rtcConns){//远程track替换
|
||||
for(let id in rtcConns){
|
||||
const senders = rtcConns[id].getSenders();
|
||||
const sender = senders.find((sender) => (sender.track ? sender.track.kind === 'video' : false));
|
||||
if(!sender){
|
||||
console.error("changeDevice find sender error! ");
|
||||
return
|
||||
}
|
||||
sender.replaceTrack(newStream.getVideoTracks()[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const video = document.querySelector("#selfMediaShareVideo");
|
||||
video.addEventListener('loadedmetadata', function() {
|
||||
// ios 微信浏览器兼容问题
|
||||
window.Bus.$emit("addSysLogs", "loadedmetadata")
|
||||
video.play();
|
||||
document.addEventListener('WeixinJSBridgeReady', function () {
|
||||
window.Bus.$emit("addSysLogs", "loadedmetadata WeixinJSBridgeReady")
|
||||
video.play();
|
||||
}, false);
|
||||
});
|
||||
document.addEventListener('WeixinJSBridgeReady', function () {
|
||||
window.Bus.$emit("addSysLogs", "WeixinJSBridgeReady")
|
||||
video.play();
|
||||
}, false);
|
||||
|
||||
//替换本地流
|
||||
this.stream = new MediaStream([newStream.getVideoTracks()[0], this.stream.getAudioTracks()[0]]);
|
||||
video.srcObject = this.stream;
|
||||
video.play();
|
||||
},
|
||||
getVideoShareTrackAndStream: function (callback) {
|
||||
callback(this.track, this.stream)
|
||||
},
|
||||
getVideoShareDeviceList : function(callback){
|
||||
callback(this.videoDeviceList, this.audioDeviceList, this.loudspeakerDeviceList)
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
mounted: async function () {
|
||||
//获取设备列表
|
||||
await this.getDeviceList();
|
||||
window.Bus.$on("startVideoShare", this.startVideoShare);
|
||||
window.Bus.$on("stopVideoShare", this.stopVideoShare);
|
||||
window.Bus.$on("getVideoShareTrackAndStream", this.getVideoShareTrackAndStream);
|
||||
window.Bus.$on("changeVideoShareDevice", this.changeVideoShareDevice);
|
||||
window.Bus.$on("getVideoShareDeviceList", this.getVideoShareDeviceList);
|
||||
}
|
||||
})
|
||||
@@ -12,7 +12,7 @@ async function getSettingPageHtml(data) {
|
||||
return 'db配置未开启';
|
||||
}
|
||||
let resData = await daoRoom.getOrCreateManageRoom({
|
||||
sid: data.socketId,
|
||||
socket_id: data.socketId,
|
||||
ip: data.ip,
|
||||
device: data.userAgent,
|
||||
}, data.tables, data.dbClient)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const check = require("../../utils/check/content");
|
||||
const check = require("../../bussiness/check/content");
|
||||
|
||||
/**
|
||||
* 检查内容
|
||||
|
||||
@@ -51,17 +51,16 @@ async function getOrCreateManageRoom(params, tables, dbClient) {
|
||||
|
||||
let manageRoomList = await tables.Room.findAll({
|
||||
where: {
|
||||
rname: manageConfig.room,
|
||||
room_id: manageConfig.room,
|
||||
flag: 1
|
||||
}
|
||||
});
|
||||
|
||||
if (manageRoomList.length === 0) {
|
||||
let res = await tables.Room.create({
|
||||
rcode: utils.genRoom(),
|
||||
rname: manageConfig.room,
|
||||
room_id: manageConfig.room,
|
||||
flag: 1,
|
||||
sid: params.sid,
|
||||
socket_id: params.socket_id,
|
||||
ip: params.ip,
|
||||
device: params.device,
|
||||
content: JSON.stringify(defaultSwitchData)
|
||||
@@ -71,7 +70,7 @@ async function getOrCreateManageRoom(params, tables, dbClient) {
|
||||
|
||||
manageRoomList = await tables.Room.findAll({
|
||||
where: {
|
||||
rname: manageConfig.room,
|
||||
room_id: manageConfig.room,
|
||||
flag: 1
|
||||
}
|
||||
});
|
||||
@@ -137,32 +136,32 @@ async function getRoomHistoryInfo(params, tables, dbClient, sockets) {
|
||||
|
||||
// 某日房间聚合列表,数量统计
|
||||
const [roomCoutingListToday, metadata] = await dbClient.query(
|
||||
`select rname, any_value(created_at) as created_at, count(*) as user from room where created_at >= "${chooseDay}" and created_at <= "${nextDay}" group by rname order by created_at desc`);
|
||||
`select room_id, any_value(created_at) as created_at, count(*) as user from room where created_at >= "${chooseDay}" and created_at <= "${nextDay}" group by room_id order by created_at desc`);
|
||||
|
||||
data.createRoomToday += roomCoutingListToday.length;
|
||||
roomCoutingListToday.forEach(element => {
|
||||
data.joinRoomTodady += element.user;
|
||||
data.todayRoomList.push({
|
||||
room: element.rname,
|
||||
room: element.room_id,
|
||||
count: element.user,
|
||||
createTime: utils.formateDateTime(new Date(element.created_at), "yyyy-MM-dd hh:mm:ss"),
|
||||
})
|
||||
});
|
||||
|
||||
// 全部数量统计
|
||||
const [roomCoutingListAll, metadata1] = await dbClient.query(`select count(*) as user from room group by rname`);
|
||||
const [roomCoutingListAll, metadata1] = await dbClient.query(`select count(*) as user from room group by room_id`);
|
||||
data.createRoomAll += roomCoutingListAll.length;
|
||||
roomCoutingListAll.forEach(element => {
|
||||
data.joinRoomAll += element.user
|
||||
});
|
||||
|
||||
// 某日房间设备统计列表
|
||||
const [roomListAgent, metadata2] = await dbClient.query(`select rname, content, created_at from room where created_at >= "${utils.formateDateTime(new Date(), "yyyy-MM-dd")}" order by created_at desc`);
|
||||
const [roomListAgent, metadata2] = await dbClient.query(`select room_id, content, created_at from room where created_at >= "${utils.formateDateTime(new Date(), "yyyy-MM-dd")}" order by created_at desc`);
|
||||
roomListAgent.forEach(element => {
|
||||
let content = JSON.parse(element.content);
|
||||
if (content && content.handshake) {
|
||||
data.userAgentIpList.push({
|
||||
room: element.rname,
|
||||
room: element.room_id,
|
||||
userAgent: content.handshake.headers['user-agent'],
|
||||
Ip: content.handshake.headers['x-real-ip'] || content.handshake.headers['x-forwarded-for'] || content.handshake.headers['host'],
|
||||
createTime: utils.formateDateTime(new Date(element.created_at), "yyyy-MM-dd hh:mm:ss"),
|
||||
@@ -199,16 +198,15 @@ async function createJoinRoom(params, tables, dbClient) {
|
||||
let data = await tables.Room.create({
|
||||
uid: params.uid,
|
||||
uname: params.uname,
|
||||
rcode: utils.genRoom(),
|
||||
rname: params.rname,
|
||||
sid: params.sid,
|
||||
room_id: params.room_id,
|
||||
pwd : params.pwd,
|
||||
socket_id: params.socket_id,
|
||||
ip: params.ip,
|
||||
device: params.device,
|
||||
url: params.url,
|
||||
content: params.content
|
||||
});
|
||||
|
||||
utils.tlConsole("加入房间 : ", params.uname, params.sid, params.rname)
|
||||
utils.tlConsole("加入房间 : ", params.uname, params.socket_id, params.room_id)
|
||||
|
||||
return data && data.dataValues ? data.dataValues.id : 0;
|
||||
}catch(e){
|
||||
@@ -219,13 +217,13 @@ async function createJoinRoom(params, tables, dbClient) {
|
||||
|
||||
|
||||
/**
|
||||
* 更新房间
|
||||
* 更新管理员房间
|
||||
* @param {*} params
|
||||
* @param {*} tables
|
||||
* @param {*} dbClient
|
||||
* @returns
|
||||
*/
|
||||
async function updateRoomContent(params, tables, dbClient) {
|
||||
async function updateManageRoomContent(params, tables, dbClient) {
|
||||
try{
|
||||
if(!tables || !dbClient){
|
||||
return {};
|
||||
@@ -274,7 +272,7 @@ async function exitRoomBySid(params, tables, dbClient) {
|
||||
status: 1
|
||||
}, {
|
||||
where: {
|
||||
sid: params.sid
|
||||
socket_id: params.socket_id
|
||||
}
|
||||
});
|
||||
|
||||
@@ -291,7 +289,7 @@ async function exitRoomBySid(params, tables, dbClient) {
|
||||
module.exports = dbOpen ? {
|
||||
getRoomHistoryInfo,
|
||||
createJoinRoom,
|
||||
updateRoomContent,
|
||||
updateManageRoomContent,
|
||||
exitRoomBySid,
|
||||
getOrCreateManageRoom
|
||||
} : {
|
||||
@@ -301,7 +299,7 @@ module.exports = dbOpen ? {
|
||||
createJoinRoom: () => {
|
||||
return {}
|
||||
},
|
||||
updateRoomContent: () => {
|
||||
updateManageRoomContent: () => {
|
||||
return {}
|
||||
},
|
||||
exitRoomBySid: () => {
|
||||
|
||||
@@ -4,7 +4,7 @@ const rtcCommData = require("../rtcCommData/commData");
|
||||
const utils = require("../../utils/utils");
|
||||
const rtcConstant = require("../rtcConstant");
|
||||
const rtcClientEvent = rtcConstant.rtcClientEvent
|
||||
const check = require("../../utils/check/content");
|
||||
const check = require("../../bussiness/check/content");
|
||||
/**
|
||||
* 公共聊天频道
|
||||
* @param {*} io socketio对象
|
||||
|
||||
@@ -3,7 +3,7 @@ const bussinessNotify = require("./../../bussiness/notify/notifyHandler")
|
||||
const utils = require("./../../utils/utils");
|
||||
const rtcConstant = require("../rtcConstant");
|
||||
const rtcClientEvent = rtcConstant.rtcClientEvent
|
||||
const check = require("./../../utils/check/content");
|
||||
const check = require("../../bussiness/check/content");
|
||||
|
||||
/**
|
||||
* 房间内聊天 群聊/私聊
|
||||
|
||||
@@ -6,7 +6,7 @@ const bussinessNotify = require("./../../bussiness/notify/notifyHandler")
|
||||
const utils = require("./../../utils/utils");
|
||||
const seafile = require("./../../bussiness/oss/seafile")
|
||||
const rtcCommData = require("./../rtcCommData/commData");
|
||||
const check = require("./../../utils/check/content");
|
||||
const check = require("../../bussiness/check/content");
|
||||
|
||||
/**
|
||||
* 添加取件码文件
|
||||
|
||||
@@ -5,7 +5,7 @@ const bussinessNotify = require("./../../bussiness/notify/notifyHandler")
|
||||
const utils = require("./../../utils/utils");
|
||||
const daoFile = require("./../../dao/file/file")
|
||||
const rtcCommData = require("./../rtcCommData/commData");
|
||||
const check = require("./../../utils/check/content");
|
||||
const check = require("../../bussiness/check/content");
|
||||
|
||||
/**
|
||||
* 取件码取件
|
||||
|
||||
@@ -5,7 +5,7 @@ const bussinessNotify = require("./../../bussiness/notify/notifyHandler")
|
||||
const utils = require("./../../utils/utils");
|
||||
const seafile = require("./../../bussiness/oss/seafile")
|
||||
const rtcCommData = require("./../rtcCommData/commData");
|
||||
const check = require("./../../utils/check/content");
|
||||
const check = require("../../bussiness/check/content");
|
||||
|
||||
/**
|
||||
* 生成取件码上传链接
|
||||
|
||||
@@ -24,7 +24,7 @@ async function getCommData(io, socket, tables, dbClient, data){
|
||||
let {handshake, userAgent, ip} = utils.getSocketClientInfo(socket);
|
||||
|
||||
let manageInfo = await daoRoom.getOrCreateManageRoom({
|
||||
sid: socket.id,
|
||||
socket_id: socket.id,
|
||||
ip: ip,
|
||||
device: userAgent,
|
||||
}, tables, dbClient)
|
||||
|
||||
@@ -5,7 +5,7 @@ const utils = require("./../../utils/utils");
|
||||
const cfg = require("./../../../conf/cfg.json")
|
||||
const rtcConstant = require("../rtcConstant");
|
||||
const rtcClientEvent = rtcConstant.rtcClientEvent
|
||||
const check = require("./../../utils/check/content");
|
||||
const check = require("../../bussiness/check/content");
|
||||
|
||||
/**
|
||||
* 用户创建或加入房间
|
||||
@@ -61,16 +61,16 @@ async function userCreateAndJoin(io, socket, tables, dbClient, data){
|
||||
const joinTime = utils.formateDateTime(new Date(), "yyyy-MM-dd hh:mm:ss")
|
||||
io.sockets.connected[socket.id].joinTime = joinTime;
|
||||
io.sockets.connected[socket.id].userAgent = userAgent;
|
||||
io.sockets.connected[socket.id].owner = false;
|
||||
|
||||
let recoderId = await daoRoom.createJoinRoom({
|
||||
uid: "1",
|
||||
uname: nickName,
|
||||
rname: room,
|
||||
sid: socket.id,
|
||||
room_id: room,
|
||||
socket_id: socket.id,
|
||||
pwd: password,
|
||||
ip: ip,
|
||||
device: userAgent,
|
||||
url: data.url || "",
|
||||
content: JSON.stringify({ handshake: handshake })
|
||||
}, tables, dbClient);
|
||||
|
||||
@@ -89,14 +89,30 @@ async function userCreateAndJoin(io, socket, tables, dbClient, data){
|
||||
owner : true,
|
||||
recoderId : recoderId
|
||||
});
|
||||
|
||||
//设置为房主
|
||||
io.sockets.connected[socket.id].owner = true;
|
||||
|
||||
//密码房间 首次创建设置密码
|
||||
//设置房间类型
|
||||
io.sockets.adapter.rooms[room].type = type;
|
||||
|
||||
//密码房间设置密码
|
||||
if(type === 'password'){
|
||||
io.sockets.adapter.rooms[room].password = password
|
||||
}
|
||||
}else {
|
||||
//加入时,房间类型不匹配,提示并退出
|
||||
const createdRoomType = io.sockets.adapter.rooms[room].type;
|
||||
if(type !== createdRoomType){
|
||||
socket.emit(rtcClientEvent.tips, {
|
||||
room : data.room,
|
||||
nickName : nickName,
|
||||
to : socket.id,
|
||||
msg : room + "是" + getRoomTypeZh(createdRoomType) + "类型的房间,请从对应功能入口进入",
|
||||
reload : true
|
||||
});
|
||||
return
|
||||
}
|
||||
|
||||
//流媒体房间只允许两个人同时在线
|
||||
if((type === 'screen' || type === 'video') && numClients >= 2){
|
||||
socket.emit(rtcClientEvent.tips, {
|
||||
@@ -122,9 +138,7 @@ async function userCreateAndJoin(io, socket, tables, dbClient, data){
|
||||
}
|
||||
}
|
||||
|
||||
io.sockets.connected[socket.id].owner = false;
|
||||
|
||||
io.sockets.in(room).emit(rtcClientEvent.joined,{
|
||||
io.sockets.in(room).emit(rtcClientEvent.joined, {
|
||||
id: socket.id,
|
||||
room: room,
|
||||
nickName : nickName,
|
||||
@@ -135,6 +149,7 @@ async function userCreateAndJoin(io, socket, tables, dbClient, data){
|
||||
network : network,
|
||||
joinTime : joinTime,
|
||||
ip : ip,
|
||||
owner : false,
|
||||
userAgent : userAgent
|
||||
});
|
||||
|
||||
@@ -203,6 +218,27 @@ async function userCreateAndJoin(io, socket, tables, dbClient, data){
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取房间类型提示
|
||||
* @param {*} type
|
||||
* @returns
|
||||
*/
|
||||
function getRoomTypeZh(type){
|
||||
if(type === 'file'){
|
||||
return "文件"
|
||||
}else if(type === 'live'){
|
||||
return "直播"
|
||||
}else if(type === 'video'){
|
||||
return "音视频"
|
||||
}else if(type === 'screen'){
|
||||
return "屏幕共享"
|
||||
}else if(type === 'password'){
|
||||
return "密码"
|
||||
}else{
|
||||
return "未知类型"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理员创建或加入房间
|
||||
* @param {*} io
|
||||
|
||||
@@ -16,7 +16,7 @@ async function disconnect(io, socket, tables, dbClient, data){
|
||||
from : socket.id,
|
||||
});
|
||||
|
||||
await daoRoom.exitRoomBySid({ sid: socket.id }, tables, dbClient);
|
||||
await daoRoom.exitRoomBySid({ socket_id: socket.id }, tables, dbClient);
|
||||
|
||||
rtcCount.count(io, socket, tables, dbClient, data)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ const utils = require("./../../utils/utils");
|
||||
const daoDog = require("./../../dao/dog/dog")
|
||||
const rtcConstant = require("../rtcConstant");
|
||||
const rtcClientEvent = rtcConstant.rtcClientEvent
|
||||
const check = require("./../../utils/check/content");
|
||||
const check = require("../../bussiness/check/content");
|
||||
/**
|
||||
* canvas画图
|
||||
* @param {*} io socketio对象
|
||||
|
||||
@@ -31,7 +31,7 @@ async function exit(io, socket, tables, dbClient, data){
|
||||
|
||||
let recoderId = data.recoderId;
|
||||
if (recoderId != undefined) {
|
||||
await daoRoom.exitRoomBySid({ sid: socket.id },tables, dbClient)
|
||||
await daoRoom.exitRoomBySid({ socket_id: socket.id },tables, dbClient)
|
||||
|
||||
let {handshake, userAgent, ip} = utils.getSocketClientInfo(socket);
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ const rtcCommData = require("./../rtcCommData/commData")
|
||||
const utils = require("./../../utils/utils");
|
||||
const rtcConstant = require("../rtcConstant");
|
||||
const rtcClientEvent = rtcConstant.rtcClientEvent
|
||||
const check = require("./../../utils/check/content");
|
||||
const check = require("../../bussiness/check/content");
|
||||
|
||||
/**
|
||||
* 管理后台修改数据
|
||||
@@ -34,7 +34,7 @@ async function change(io, socket, tables, dbClient, data){
|
||||
return
|
||||
}
|
||||
|
||||
await daoRoom.updateRoomContent({
|
||||
await daoRoom.updateManageRoomContent({
|
||||
id: data.id,
|
||||
content: JSON.stringify(data.content)
|
||||
}, tables, dbClient)
|
||||
|
||||
@@ -7,7 +7,7 @@ const cfg = require("./../../../conf/cfg.json")
|
||||
const manageConfig = cfg.manage
|
||||
const rtcConstant = require("../rtcConstant");
|
||||
const rtcClientEvent = rtcConstant.rtcClientEvent
|
||||
const check = require("./../../utils/check/content");
|
||||
const check = require("../../bussiness/check/content");
|
||||
|
||||
// 登陆token列表
|
||||
let tokens = [];
|
||||
@@ -79,7 +79,7 @@ async function confirm(io, socket, tables, dbClient, data){
|
||||
html: await bussinessManageSettingPage.getSettingPageHtml({
|
||||
tables: tables,
|
||||
dbClient: dbClient,
|
||||
sid: socket.socketId,
|
||||
socket_id: socket.socketId,
|
||||
ip: ip,
|
||||
device: userAgent
|
||||
})
|
||||
|
||||
@@ -8,7 +8,7 @@ const cfg = require("./../../../conf/cfg.json")
|
||||
const manageConfig = cfg.manage
|
||||
const rtcConstant = require("../rtcConstant");
|
||||
const rtcClientEvent = rtcConstant.rtcClientEvent
|
||||
const check = require("./../../utils/check/content");
|
||||
const check = require("../../bussiness/check/content");
|
||||
|
||||
/**
|
||||
* 管理后台登陆验证
|
||||
@@ -61,7 +61,7 @@ async function reload(io, socket, tables, dbClient, data){
|
||||
html: await bussinessManageSettingPage.getSettingPageHtml({
|
||||
tables: tables,
|
||||
dbClient: dbClient,
|
||||
sid: socket.id,
|
||||
socket_id: socket.id,
|
||||
ip: ip,
|
||||
device: userAgent
|
||||
})
|
||||
|
||||
@@ -3,7 +3,7 @@ const bussinessNotify = require("./../../bussiness/notify/notifyHandler")
|
||||
const utils = require("./../../utils/utils");
|
||||
const rtcConstant = require("../rtcConstant");
|
||||
const rtcServerMessageEvent = rtcConstant.rtcServerMessageEvent
|
||||
const check = require("./../../utils/check/content");
|
||||
const check = require("../../bussiness/check/content");
|
||||
|
||||
let rtcEventOpName = {
|
||||
"sendFileInfo": "准备发送文件",
|
||||
|
||||
@@ -5,7 +5,7 @@ const utils = require("./../../utils/utils");
|
||||
const bussinessOpenai = require("./../../bussiness/openai/openai")
|
||||
const rtcConstant = require("../rtcConstant");
|
||||
const rtcClientEvent = rtcConstant.rtcClientEvent
|
||||
const check = require("./../../utils/check/content");
|
||||
const check = require("../../bussiness/check/content");
|
||||
|
||||
/**
|
||||
* ai聊天
|
||||
|
||||
@@ -9,25 +9,21 @@ module.exports = (sequelize, DataTypes) => {
|
||||
unique: true,
|
||||
comment: '数据id',
|
||||
},
|
||||
rcode: {
|
||||
type: DataTypes.STRING(30),
|
||||
comment: '房间随机编号'
|
||||
},
|
||||
rname: {
|
||||
room_id: {
|
||||
type: DataTypes.STRING(32),
|
||||
comment: '房间频道号码'
|
||||
},
|
||||
uid: {
|
||||
type: DataTypes.INTEGER,
|
||||
comment: '属于的用户id, 待定~',
|
||||
comment: '匿名用户的id',
|
||||
},
|
||||
uname: {
|
||||
type: DataTypes.STRING(20),
|
||||
comment: '姓名, 待定~'
|
||||
comment: '匿名用户姓名,昵称'
|
||||
},
|
||||
sid: {
|
||||
socket_id: {
|
||||
type: DataTypes.STRING(30),
|
||||
comment: '进入房间时的sessionId'
|
||||
comment: '匿名用户进入房间时的socket.id'
|
||||
},
|
||||
pwd: {
|
||||
type: DataTypes.STRING(6),
|
||||
@@ -44,15 +40,7 @@ module.exports = (sequelize, DataTypes) => {
|
||||
},
|
||||
device: {
|
||||
type: DataTypes.STRING(256),
|
||||
comment: '设备'
|
||||
},
|
||||
localtion: {
|
||||
type: DataTypes.STRING(256),
|
||||
comment: '地理位置'
|
||||
},
|
||||
url: {
|
||||
type: DataTypes.STRING(256),
|
||||
comment: '请求url'
|
||||
comment: '设备信息'
|
||||
},
|
||||
flag: {
|
||||
type: DataTypes.INTEGER,
|
||||
@@ -65,7 +53,7 @@ module.exports = (sequelize, DataTypes) => {
|
||||
}
|
||||
}, {
|
||||
timestamps: true,
|
||||
comment: '房间表',
|
||||
comment: '匿名用户房间表',
|
||||
indexes: [{
|
||||
name: 'created_at_index',
|
||||
method: 'BTREE',
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
class tlQueue{
|
||||
constructor(options){
|
||||
let {
|
||||
max = 500, // 最多处理任务数量
|
||||
queueThreshold = 2, // 1秒最多并发任务数量,超过进入队列
|
||||
queueRate = 1000, // 队列处理单个任务时间间隔, 单位ms
|
||||
consumerFunc = ()=>{}, // 消费逻辑 Function
|
||||
queue = [], // 任务队列
|
||||
count = 0, // 计数器
|
||||
queueId = -1, // 消费轮询器id
|
||||
} = options;
|
||||
|
||||
this.options = options;
|
||||
this.max = max;
|
||||
this.queueThreshold = queueThreshold;
|
||||
this.queueRate = queueRate;
|
||||
this.queue = queue;
|
||||
this.count = count;
|
||||
this.consumerFunc = consumerFunc;
|
||||
this.queueId = queueId;
|
||||
|
||||
this.init()
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动
|
||||
*/
|
||||
init(){
|
||||
this.queueId = setInterval(() => {
|
||||
if(this.queue.length > 0){
|
||||
this.consume();
|
||||
console.log("tl-queue pop : ",this.queue.length)
|
||||
}
|
||||
}, this.queueRate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 推数据
|
||||
* @param {*} data
|
||||
*/
|
||||
produce(data) {
|
||||
//size <= queueThreshold, 允许一些并发执行
|
||||
if(this.queue.length <= this.queueThreshold){
|
||||
this.consume();
|
||||
return
|
||||
}
|
||||
// 队列满
|
||||
if(this.queue.length > this.max){
|
||||
console.warn("tl-queue out of size : ",this.queue.length)
|
||||
return
|
||||
}
|
||||
|
||||
console.warn("tl-queue push : ",this.queue.length)
|
||||
this.queue.push(data)
|
||||
}
|
||||
|
||||
/**
|
||||
* 拉数据
|
||||
*/
|
||||
consume(){
|
||||
this.consumerFunc(this.queue.shift(), this.options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁任务
|
||||
*/
|
||||
destroy() {
|
||||
this.queue = [];
|
||||
clearInterval(this.queueId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
tlQueue
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
const Worker = require("./worker").Worker;
|
||||
|
||||
async function removeRoomSokect(list){
|
||||
if(list == null || typeof list != 'array'){
|
||||
return;
|
||||
}
|
||||
if(list.length <= 0){
|
||||
return;
|
||||
}
|
||||
|
||||
let cusWorker = new Worker(5000,()=>{
|
||||
for(let i = 0; i < list.length; i++){
|
||||
console.log('i : ',list[i]);
|
||||
}
|
||||
});
|
||||
|
||||
cusWorker.run()
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
removeRoomSokect
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
class Master {
|
||||
constructor(maxCount = 4){
|
||||
this.workers = {}; //worker数量
|
||||
this.maxCount = maxCount; //最大worker数
|
||||
console.log("执行master")
|
||||
}
|
||||
|
||||
get(){
|
||||
let data = {
|
||||
workers : this.workers,
|
||||
maxCount : this.maxCount,
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
add(type = "", worker = null){
|
||||
if(Object.keys(this.workers) >= this.maxCount){
|
||||
return;
|
||||
}
|
||||
if(!worker || !type){
|
||||
return;
|
||||
}
|
||||
if(this.workers[type] != undefined){
|
||||
return;
|
||||
}
|
||||
this.workers[type] = worker;
|
||||
}
|
||||
|
||||
del(type = ""){
|
||||
delete this.workers[type];
|
||||
if(this.maxCount > 0){
|
||||
this.maxCount--;
|
||||
}
|
||||
}
|
||||
|
||||
clear(){
|
||||
this.workers = {};
|
||||
this.maxCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
class Worker {
|
||||
constructor(time = 2000, handler = null){
|
||||
this.isRunning = false;
|
||||
this.excuteCount = 0;
|
||||
this.errorCount = 0;
|
||||
|
||||
this.time = time;
|
||||
this.handler = handler;
|
||||
this.id = 0;
|
||||
console.log("执行worker")
|
||||
}
|
||||
|
||||
get(){
|
||||
let data = {
|
||||
isRunning : this.isRunning,
|
||||
excuteCount : this.errorCount,
|
||||
errorCount : this.errorCount,
|
||||
time : this.time,
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
run(){
|
||||
if(!this.time || this.time <= 0){
|
||||
return;
|
||||
}
|
||||
if(!this.handler){
|
||||
return;
|
||||
}
|
||||
if(this.errorCount > 5){
|
||||
console.log("errorCount > 5",this.errorCount)
|
||||
this.stop();
|
||||
}
|
||||
let that = this;
|
||||
function excute(){
|
||||
try{
|
||||
that.handler();
|
||||
that.excuteCount++;
|
||||
that.isRunning = true;
|
||||
}catch(err){
|
||||
that.errorCount++;
|
||||
}
|
||||
}
|
||||
this.id = setInterval(excute,this.time);
|
||||
}
|
||||
|
||||
stop(){
|
||||
clearInterval(this.id);
|
||||
this.id = 0;
|
||||
this.time = 0;
|
||||
this.handler = null;
|
||||
this.isRunning = false;
|
||||
this.excuteCount = 0;
|
||||
this.errorCount = 0;
|
||||
console.log("停止worker handler")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
Worker : Worker,
|
||||
Master : Master
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -54,6 +54,114 @@
|
||||
<div class="content unicode" style="display: block;">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">翻转镜头</div>
|
||||
<div class="code-name">&#xe872;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">hot</div>
|
||||
<div class="code-name">&#xe727;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">网盘</div>
|
||||
<div class="code-name">&#xe63d;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">密码</div>
|
||||
<div class="code-name">&#xe67a;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">录制</div>
|
||||
<div class="code-name">&#xe741;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">实物-画笔</div>
|
||||
<div class="code-name">&#xe753;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">ai</div>
|
||||
<div class="code-name">&#xe623;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">chat</div>
|
||||
<div class="code-name">&#xe69a;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">直播</div>
|
||||
<div class="code-name">&#xe74f;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">挂断电话</div>
|
||||
<div class="code-name">&#xe61c;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">关闭扬声器</div>
|
||||
<div class="code-name">&#xe61e;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">麦克风</div>
|
||||
<div class="code-name">&#xe663;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">屏幕共享</div>
|
||||
<div class="code-name">&#xe746;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">摄像头_关闭</div>
|
||||
<div class="code-name">&#xeca5;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">摄像头</div>
|
||||
<div class="code-name">&#xeca6;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">喇叭</div>
|
||||
<div class="code-name">&#xe61f;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">关闭屏幕</div>
|
||||
<div class="code-name">&#xe622;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">关闭麦克风</div>
|
||||
<div class="code-name">&#xe643;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">数据汇总</div>
|
||||
@@ -558,9 +666,9 @@
|
||||
<pre><code class="language-css"
|
||||
>@font-face {
|
||||
font-family: 'iconfont';
|
||||
src: url('iconfont.woff2?t=1688799262496') format('woff2'),
|
||||
url('iconfont.woff?t=1688799262496') format('woff'),
|
||||
url('iconfont.ttf?t=1688799262496') format('truetype');
|
||||
src: url('iconfont.woff2?t=1690027853800') format('woff2'),
|
||||
url('iconfont.woff?t=1690027853800') format('woff'),
|
||||
url('iconfont.ttf?t=1690027853800') format('truetype');
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
|
||||
@@ -586,6 +694,168 @@
|
||||
<div class="content font-class">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-rtc-file-fanzhuanjingtou"></span>
|
||||
<div class="name">
|
||||
翻转镜头
|
||||
</div>
|
||||
<div class="code-name">.icon-rtc-file-fanzhuanjingtou
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-rtc-file-hot"></span>
|
||||
<div class="name">
|
||||
hot
|
||||
</div>
|
||||
<div class="code-name">.icon-rtc-file-hot
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-rtc-file-wangpan"></span>
|
||||
<div class="name">
|
||||
网盘
|
||||
</div>
|
||||
<div class="code-name">.icon-rtc-file-wangpan
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-rtc-file-mima"></span>
|
||||
<div class="name">
|
||||
密码
|
||||
</div>
|
||||
<div class="code-name">.icon-rtc-file-mima
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-rtc-file-luzhi"></span>
|
||||
<div class="name">
|
||||
录制
|
||||
</div>
|
||||
<div class="code-name">.icon-rtc-file-luzhi
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-rtc-file-shiwu-huabi"></span>
|
||||
<div class="name">
|
||||
实物-画笔
|
||||
</div>
|
||||
<div class="code-name">.icon-rtc-file-shiwu-huabi
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-rtc-file-24px2x"></span>
|
||||
<div class="name">
|
||||
ai
|
||||
</div>
|
||||
<div class="code-name">.icon-rtc-file-24px2x
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-rtc-file-chat"></span>
|
||||
<div class="name">
|
||||
chat
|
||||
</div>
|
||||
<div class="code-name">.icon-rtc-file-chat
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-rtc-file-zhibo"></span>
|
||||
<div class="name">
|
||||
直播
|
||||
</div>
|
||||
<div class="code-name">.icon-rtc-file-zhibo
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-rtc-file-guaduandianhua"></span>
|
||||
<div class="name">
|
||||
挂断电话
|
||||
</div>
|
||||
<div class="code-name">.icon-rtc-file-guaduandianhua
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-rtc-file-guanbiyangshengqi"></span>
|
||||
<div class="name">
|
||||
关闭扬声器
|
||||
</div>
|
||||
<div class="code-name">.icon-rtc-file-guanbiyangshengqi
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-rtc-file-maikefeng-XDY"></span>
|
||||
<div class="name">
|
||||
麦克风
|
||||
</div>
|
||||
<div class="code-name">.icon-rtc-file-maikefeng-XDY
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-rtc-file-pingmugongxiang"></span>
|
||||
<div class="name">
|
||||
屏幕共享
|
||||
</div>
|
||||
<div class="code-name">.icon-rtc-file-pingmugongxiang
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-rtc-file-shexiangtou_guanbi"></span>
|
||||
<div class="name">
|
||||
摄像头_关闭
|
||||
</div>
|
||||
<div class="code-name">.icon-rtc-file-shexiangtou_guanbi
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-rtc-file-shexiangtou"></span>
|
||||
<div class="name">
|
||||
摄像头
|
||||
</div>
|
||||
<div class="code-name">.icon-rtc-file-shexiangtou
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-rtc-file-laba"></span>
|
||||
<div class="name">
|
||||
喇叭
|
||||
</div>
|
||||
<div class="code-name">.icon-rtc-file-laba
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-rtc-file-guanbipingmu"></span>
|
||||
<div class="name">
|
||||
关闭屏幕
|
||||
</div>
|
||||
<div class="code-name">.icon-rtc-file-guanbipingmu
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-rtc-file-guanbimaikefeng"></span>
|
||||
<div class="name">
|
||||
关闭麦克风
|
||||
</div>
|
||||
<div class="code-name">.icon-rtc-file-guanbimaikefeng
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-rtc-file-daohang-shujufenxi"></span>
|
||||
<div class="name">
|
||||
@@ -1342,6 +1612,150 @@
|
||||
<div class="content symbol">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-rtc-file-fanzhuanjingtou"></use>
|
||||
</svg>
|
||||
<div class="name">翻转镜头</div>
|
||||
<div class="code-name">#icon-rtc-file-fanzhuanjingtou</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-rtc-file-hot"></use>
|
||||
</svg>
|
||||
<div class="name">hot</div>
|
||||
<div class="code-name">#icon-rtc-file-hot</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-rtc-file-wangpan"></use>
|
||||
</svg>
|
||||
<div class="name">网盘</div>
|
||||
<div class="code-name">#icon-rtc-file-wangpan</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-rtc-file-mima"></use>
|
||||
</svg>
|
||||
<div class="name">密码</div>
|
||||
<div class="code-name">#icon-rtc-file-mima</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-rtc-file-luzhi"></use>
|
||||
</svg>
|
||||
<div class="name">录制</div>
|
||||
<div class="code-name">#icon-rtc-file-luzhi</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-rtc-file-shiwu-huabi"></use>
|
||||
</svg>
|
||||
<div class="name">实物-画笔</div>
|
||||
<div class="code-name">#icon-rtc-file-shiwu-huabi</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-rtc-file-24px2x"></use>
|
||||
</svg>
|
||||
<div class="name">ai</div>
|
||||
<div class="code-name">#icon-rtc-file-24px2x</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-rtc-file-chat"></use>
|
||||
</svg>
|
||||
<div class="name">chat</div>
|
||||
<div class="code-name">#icon-rtc-file-chat</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-rtc-file-zhibo"></use>
|
||||
</svg>
|
||||
<div class="name">直播</div>
|
||||
<div class="code-name">#icon-rtc-file-zhibo</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-rtc-file-guaduandianhua"></use>
|
||||
</svg>
|
||||
<div class="name">挂断电话</div>
|
||||
<div class="code-name">#icon-rtc-file-guaduandianhua</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-rtc-file-guanbiyangshengqi"></use>
|
||||
</svg>
|
||||
<div class="name">关闭扬声器</div>
|
||||
<div class="code-name">#icon-rtc-file-guanbiyangshengqi</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-rtc-file-maikefeng-XDY"></use>
|
||||
</svg>
|
||||
<div class="name">麦克风</div>
|
||||
<div class="code-name">#icon-rtc-file-maikefeng-XDY</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-rtc-file-pingmugongxiang"></use>
|
||||
</svg>
|
||||
<div class="name">屏幕共享</div>
|
||||
<div class="code-name">#icon-rtc-file-pingmugongxiang</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-rtc-file-shexiangtou_guanbi"></use>
|
||||
</svg>
|
||||
<div class="name">摄像头_关闭</div>
|
||||
<div class="code-name">#icon-rtc-file-shexiangtou_guanbi</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-rtc-file-shexiangtou"></use>
|
||||
</svg>
|
||||
<div class="name">摄像头</div>
|
||||
<div class="code-name">#icon-rtc-file-shexiangtou</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-rtc-file-laba"></use>
|
||||
</svg>
|
||||
<div class="name">喇叭</div>
|
||||
<div class="code-name">#icon-rtc-file-laba</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-rtc-file-guanbipingmu"></use>
|
||||
</svg>
|
||||
<div class="name">关闭屏幕</div>
|
||||
<div class="code-name">#icon-rtc-file-guanbipingmu</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-rtc-file-guanbimaikefeng"></use>
|
||||
</svg>
|
||||
<div class="name">关闭麦克风</div>
|
||||
<div class="code-name">#icon-rtc-file-guanbimaikefeng</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-rtc-file-daohang-shujufenxi"></use>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 4147343 */
|
||||
src: url('iconfont.woff2?t=1688799262496') format('woff2'),
|
||||
url('iconfont.woff?t=1688799262496') format('woff'),
|
||||
url('iconfont.ttf?t=1688799262496') format('truetype');
|
||||
src: url('iconfont.woff2?t=1690027853800') format('woff2'),
|
||||
url('iconfont.woff?t=1690027853800') format('woff'),
|
||||
url('iconfont.ttf?t=1690027853800') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
@@ -13,6 +13,78 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-rtc-file-fanzhuanjingtou:before {
|
||||
content: "\e872";
|
||||
}
|
||||
|
||||
.icon-rtc-file-hot:before {
|
||||
content: "\e727";
|
||||
}
|
||||
|
||||
.icon-rtc-file-wangpan:before {
|
||||
content: "\e63d";
|
||||
}
|
||||
|
||||
.icon-rtc-file-mima:before {
|
||||
content: "\e67a";
|
||||
}
|
||||
|
||||
.icon-rtc-file-luzhi:before {
|
||||
content: "\e741";
|
||||
}
|
||||
|
||||
.icon-rtc-file-shiwu-huabi:before {
|
||||
content: "\e753";
|
||||
}
|
||||
|
||||
.icon-rtc-file-24px2x:before {
|
||||
content: "\e623";
|
||||
}
|
||||
|
||||
.icon-rtc-file-chat:before {
|
||||
content: "\e69a";
|
||||
}
|
||||
|
||||
.icon-rtc-file-zhibo:before {
|
||||
content: "\e74f";
|
||||
}
|
||||
|
||||
.icon-rtc-file-guaduandianhua:before {
|
||||
content: "\e61c";
|
||||
}
|
||||
|
||||
.icon-rtc-file-guanbiyangshengqi:before {
|
||||
content: "\e61e";
|
||||
}
|
||||
|
||||
.icon-rtc-file-maikefeng-XDY:before {
|
||||
content: "\e663";
|
||||
}
|
||||
|
||||
.icon-rtc-file-pingmugongxiang:before {
|
||||
content: "\e746";
|
||||
}
|
||||
|
||||
.icon-rtc-file-shexiangtou_guanbi:before {
|
||||
content: "\eca5";
|
||||
}
|
||||
|
||||
.icon-rtc-file-shexiangtou:before {
|
||||
content: "\eca6";
|
||||
}
|
||||
|
||||
.icon-rtc-file-laba:before {
|
||||
content: "\e61f";
|
||||
}
|
||||
|
||||
.icon-rtc-file-guanbipingmu:before {
|
||||
content: "\e622";
|
||||
}
|
||||
|
||||
.icon-rtc-file-guanbimaikefeng:before {
|
||||
content: "\e643";
|
||||
}
|
||||
|
||||
.icon-rtc-file-daohang-shujufenxi:before {
|
||||
content: "\e61d";
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -5,6 +5,132 @@
|
||||
"css_prefix_text": "icon-rtc-file-",
|
||||
"description": "",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "2076200",
|
||||
"name": "翻转镜头",
|
||||
"font_class": "fanzhuanjingtou",
|
||||
"unicode": "e872",
|
||||
"unicode_decimal": 59506
|
||||
},
|
||||
{
|
||||
"icon_id": "7192490",
|
||||
"name": "hot",
|
||||
"font_class": "hot",
|
||||
"unicode": "e727",
|
||||
"unicode_decimal": 59175
|
||||
},
|
||||
{
|
||||
"icon_id": "564213",
|
||||
"name": "网盘",
|
||||
"font_class": "wangpan",
|
||||
"unicode": "e63d",
|
||||
"unicode_decimal": 58941
|
||||
},
|
||||
{
|
||||
"icon_id": "1134325",
|
||||
"name": "密码",
|
||||
"font_class": "mima",
|
||||
"unicode": "e67a",
|
||||
"unicode_decimal": 59002
|
||||
},
|
||||
{
|
||||
"icon_id": "4933362",
|
||||
"name": "录制",
|
||||
"font_class": "luzhi",
|
||||
"unicode": "e741",
|
||||
"unicode_decimal": 59201
|
||||
},
|
||||
{
|
||||
"icon_id": "4933398",
|
||||
"name": "实物-画笔",
|
||||
"font_class": "shiwu-huabi",
|
||||
"unicode": "e753",
|
||||
"unicode_decimal": 59219
|
||||
},
|
||||
{
|
||||
"icon_id": "15053872",
|
||||
"name": "ai",
|
||||
"font_class": "24px2x",
|
||||
"unicode": "e623",
|
||||
"unicode_decimal": 58915
|
||||
},
|
||||
{
|
||||
"icon_id": "21712533",
|
||||
"name": "chat",
|
||||
"font_class": "chat",
|
||||
"unicode": "e69a",
|
||||
"unicode_decimal": 59034
|
||||
},
|
||||
{
|
||||
"icon_id": "11520120",
|
||||
"name": "直播",
|
||||
"font_class": "zhibo",
|
||||
"unicode": "e74f",
|
||||
"unicode_decimal": 59215
|
||||
},
|
||||
{
|
||||
"icon_id": "1231602",
|
||||
"name": "挂断电话",
|
||||
"font_class": "guaduandianhua",
|
||||
"unicode": "e61c",
|
||||
"unicode_decimal": 58908
|
||||
},
|
||||
{
|
||||
"icon_id": "1374721",
|
||||
"name": "关闭扬声器",
|
||||
"font_class": "guanbiyangshengqi",
|
||||
"unicode": "e61e",
|
||||
"unicode_decimal": 58910
|
||||
},
|
||||
{
|
||||
"icon_id": "1780714",
|
||||
"name": "麦克风",
|
||||
"font_class": "maikefeng-XDY",
|
||||
"unicode": "e663",
|
||||
"unicode_decimal": 58979
|
||||
},
|
||||
{
|
||||
"icon_id": "4933368",
|
||||
"name": "屏幕共享",
|
||||
"font_class": "pingmugongxiang",
|
||||
"unicode": "e746",
|
||||
"unicode_decimal": 59206
|
||||
},
|
||||
{
|
||||
"icon_id": "6776383",
|
||||
"name": "摄像头_关闭",
|
||||
"font_class": "shexiangtou_guanbi",
|
||||
"unicode": "eca5",
|
||||
"unicode_decimal": 60581
|
||||
},
|
||||
{
|
||||
"icon_id": "6776384",
|
||||
"name": "摄像头",
|
||||
"font_class": "shexiangtou",
|
||||
"unicode": "eca6",
|
||||
"unicode_decimal": 60582
|
||||
},
|
||||
{
|
||||
"icon_id": "14191058",
|
||||
"name": "喇叭",
|
||||
"font_class": "laba",
|
||||
"unicode": "e61f",
|
||||
"unicode_decimal": 58911
|
||||
},
|
||||
{
|
||||
"icon_id": "16534085",
|
||||
"name": "关闭屏幕",
|
||||
"font_class": "guanbipingmu",
|
||||
"unicode": "e622",
|
||||
"unicode_decimal": 58914
|
||||
},
|
||||
{
|
||||
"icon_id": "26661601",
|
||||
"name": "关闭麦克风",
|
||||
"font_class": "guanbimaikefeng",
|
||||
"unicode": "e643",
|
||||
"unicode_decimal": 58947
|
||||
},
|
||||
{
|
||||
"icon_id": "3494344",
|
||||
"name": "数据汇总",
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user