├── .github
└── workflows
│ └── build-release.yml
├── .gitignore
├── LICENSE
├── README.md
├── install-package
└── urlProtocol.nsh
├── main.js
├── package.json
└── static
├── audio
└── some_one_join_room.wav
├── css
├── index.css
├── room.css
└── screen.css
├── element-ui
├── element-ui.js
├── fonts
│ └── element-icons.woff
├── theme-chalk.css
└── vue.js
├── icon
├── camera.png
├── camera_no.png
├── close-hover.png
├── close.png
├── desktop-share.png
├── desktop-share_no.png
├── emoji.png
├── emoji_hover.png
├── exit.png
├── fullscreen.png
├── fullscreen_hover.png
├── fullscreen_no.png
├── layout.png
├── logo-512.png
├── logo.icns
├── logo.ico
├── logo.jpg
├── logo.png
├── logo.psd
├── m3u8.png
├── members.png
├── mic.png
├── mic_no.png
├── minimize.png
├── minimize_hover.png
├── msg.png
├── msg_hover.png
├── network-0.png
├── network-1.png
├── network-2.png
├── network.png
├── record.png
├── screen-1-1.png
├── screen-scale.png
├── show.png
├── speak.png
└── speak_no.png
├── imgs
├── background-00.webp
├── background-01.webp
├── background-02.webp
├── background-03.png
├── background-04.png
├── emoji
│ ├── emoji_0.png
│ ├── emoji_1.png
│ ├── emoji_2.png
│ ├── emoji_3.png
│ ├── emoji_4.png
│ ├── emoji_5.png
│ ├── emoji_6.png
│ └── emoji_7.png
├── room-bkg.svg
├── ui-01.png
├── ui-02.png
├── ui-03.png
└── ui-04.gif
├── index.html
├── loginRoom.html
├── scripts
├── adapter-7.0.0.js
├── app-index.js
├── app-videoWindows.js
├── jquery-3.2.1.min.js
├── owt.js
├── rest-sample.js
├── rest.js
└── socket.io.js
├── server
└── samplertcservice.js
└── videoWindows.html
/.github/workflows/build-release.yml:
--------------------------------------------------------------------------------
1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
3 |
4 | name: OWT-Client-Build
5 |
6 | on:
7 | push:
8 | tags:
9 | - v*.*.*
10 |
11 | jobs:
12 | build:
13 | runs-on: ${{ matrix.os }}
14 |
15 | strategy:
16 | matrix:
17 | os: [windows-latest, macos-latest, ubuntu-latest]
18 | node-version: [ 14.16.0 ]
19 |
20 | steps:
21 | - uses: actions/checkout@v2
22 | - name: Use Node.js ${{ matrix.node-version }}
23 | uses: actions/setup-node@v1
24 | with:
25 | node-version: ${{ matrix.node-version }}
26 | - run: |
27 | npm install
28 | npm run ${{ matrix.os }}
29 | env:
30 | GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }}
31 | GH_TOKEN: ${{ secrets.ACCESS_TOKEN }}
32 | CSC_IDENTITY_AUTO_DISCOVERY: false
33 |
34 | - name: Upload artifact
35 | uses: actions/upload-artifact@v2
36 | with:
37 | name: dist
38 | path: |
39 | dist/OWTClient--*
40 | !dist/OWTClient-*.blockmap
41 | !dist/latest*.yml
42 | env:
43 | GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }}
44 | GH_TOKEN: ${{ secrets.ACCESS_TOKEN }}
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Diagnostic reports (https://nodejs.org/api/report.html)
10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 |
18 | # Directory for instrumented libs generated by jscoverage/JSCover
19 | lib-cov
20 |
21 | # Coverage directory used by tools like istanbul
22 | coverage
23 | *.lcov
24 |
25 | # nyc test coverage
26 | .nyc_output
27 |
28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
29 | .grunt
30 |
31 | # Bower dependency directory (https://bower.io/)
32 | bower_components
33 |
34 | # node-waf configuration
35 | .lock-wscript
36 |
37 | # Compiled binary addons (https://nodejs.org/api/addons.html)
38 | build/Release
39 |
40 | # Dependency directories
41 | node_modules/
42 | jspm_packages/
43 |
44 | # TypeScript v1 declaration files
45 | typings/
46 |
47 | # TypeScript cache
48 | *.tsbuildinfo
49 |
50 | # Optional npm cache directory
51 | .npm
52 |
53 | # Optional eslint cache
54 | .eslintcache
55 |
56 | # Microbundle cache
57 | .rpt2_cache/
58 | .rts2_cache_cjs/
59 | .rts2_cache_es/
60 | .rts2_cache_umd/
61 |
62 | # Optional REPL history
63 | .node_repl_history
64 |
65 | # Output of 'npm pack'
66 | *.tgz
67 |
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
71 | # dotenv environment variables file
72 | .env
73 | .env.test
74 |
75 | # parcel-bundler cache (https://parceljs.org/)
76 | .cache
77 |
78 | # Next.js build output
79 | .next
80 |
81 | # Nuxt.js build / generate output
82 | .nuxt
83 | dist
84 |
85 | # Gatsby files
86 | .cache/
87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js
88 | # https://nextjs.org/blog/next-9-1#public-directory-support
89 | # public
90 |
91 | # vuepress build output
92 | .vuepress/dist
93 |
94 | # Serverless directories
95 | .serverless/
96 |
97 | # FuseBox cache
98 | .fusebox/
99 |
100 | # DynamoDB Local files
101 | .dynamodb/
102 |
103 | # TernJS port file
104 | .tern-port
105 |
106 | dist/*
107 | node_modules/*
108 | package-lock.json
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 heisir
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://github.com/heisir2014/owt-client-rtc/releases/latest)
2 | [](https://github.com/heisir2014/owt-client-rtc/releases/latest)
3 | # OWT Client [直接下载](#下载)
4 |
5 | 服务器程序使用的OWT-Server [Open WebRTC Toolkit](https://github.com/open-webrtc-toolkit/)
6 |
7 | OWT-Client 是基于OWT、Electron开发的视频会议客户端,支持Windows、Linux、MacOS系统。
8 |
9 | ## 特性
10 |
11 | - PC多平台支持(Windows、Linux、MacOS)
12 | - 视频聊天
13 | - 文字聊天
14 | - 多摄像头支持
15 | - 桌面分享
16 | - 本地录制
17 |
18 | ## UI
19 |
20 |
21 |

22 |
23 |
24 |
25 | ## 下载
26 |
27 | [Release](https://github.com/HeiSir2014/owt-client-rtc/releases/)
28 |
29 | ## 开发 用源码编译
30 |
31 | 依赖:nodejs、git、electron
32 |
33 | 1. install Node.js [Node.js](https://nodejs.org/)
34 |
35 | 2. install Git [Download Git](https://git-scm.com/)
36 |
37 | 3. shell
38 | ```
39 | mkdir owt-client
40 | cd owt-client
41 | git clone https://github.com/HeiSir2014/owt-client-rtc.git .
42 | npm install
43 | npm run start
44 | ```
45 |
46 | ## 交流学习
47 |
48 | 下面的视频是和一个小伙伴介绍本项目的录屏。
49 |
50 | [交流录屏 - B站](https://www.bilibili.com/video/BV1H5411g7A6/) | [交流录屏 - YouTube](https://www.youtube.com/watch?v=fn1tBTqsjyA)
51 |
52 | QQ群:213941700
53 |
54 |
--------------------------------------------------------------------------------
/install-package/urlProtocol.nsh:
--------------------------------------------------------------------------------
1 | !macro customInstall
2 | DetailPrint "Register owtclient URI Handler"
3 | DeleteRegKey HKCR "owtclient"
4 | WriteRegStr HKCR "owtclient" "" "URL:owtclient"
5 | WriteRegStr HKCR "owtclient" "URL Protocol" ""
6 | WriteRegStr HKCR "owtclient\shell" "" ""
7 | WriteRegStr HKCR "owtclient\shell\Open" "" ""
8 | WriteRegStr HKCR "owtclient\shell\Open\command" "" "$INSTDIR\${APP_EXECUTABLE_FILENAME} %1"
9 | !macroend
--------------------------------------------------------------------------------
/main.js:
--------------------------------------------------------------------------------
1 | const os = require('os');
2 | const path = require('path');
3 | const fs = require('fs');
4 | const winston = require('winston');
5 | const { app, BrowserWindow, Tray, ipcMain, shell, Menu, dialog,session, webContents,systemPreferences } = require('electron');
6 | const isDev = require('electron-is-dev');
7 | const package_self = require('./package.json');
8 | let mainWindow = null;
9 | let videoWindows = [];
10 | let loginRoomWindow = null;
11 | let logger;
12 | let localConfig;
13 | let _startParam = null;
14 |
15 |
16 | (function(){
17 |
18 | localConfig = path.join(app.getPath('userData'),'config.json');
19 | logger = winston.createLogger({
20 | level: 'debug',
21 | format: winston.format.combine(
22 | winston.format.timestamp({
23 | format: 'YYYY-MM-DD HH:mm:ss'
24 | }),
25 | winston.format.printf(info => `${info.timestamp} ${info.level}: ${info.message}`+(info.splat!==undefined?`${info.splat}`:" "))
26 | ),
27 | transports: [
28 | new winston.transports.Console(),
29 | new winston.transports.File({ filename: path.join(app.getPath('userData'),'logs/error.log'), level: 'error' }),
30 | new winston.transports.File({ filename: path.join(app.getPath('userData'),'logs/all.log') }),
31 | ],
32 | });
33 |
34 | // 单例应用程序
35 | if (!isDev && !app.requestSingleInstanceLock()) {
36 | app.quit()
37 | return
38 | }
39 | app.on('second-instance', (event, argv, cwd) => {
40 | const [win] = BrowserWindow.getAllWindows();
41 | logger.info( "second-instance: "+ JSON.stringify(argv))
42 | if (win) {
43 | if (win.isMinimized()) {
44 | win.restore()
45 | }
46 | win.show()
47 | win.focus()
48 | }
49 | if(argv)
50 | {
51 | console.log(argv);
52 | let param = getStartParam(argv);
53 | param.userId != 'any' && CreateMainWin(param);
54 | }
55 | });
56 | process.on('uncaughtException',(err, origin) =>{
57 | logger.error(`uncaughtException: ${err} | ${origin}`)
58 | });
59 | process.on('unhandledRejection',(reason, promise) =>{
60 | logger.error(`unhandledRejection: ${promise} | ${reason}`)
61 | });
62 |
63 | app.on('open-url', function (event, url) {
64 | event.preventDefault();
65 | logger.info('open-url:' + url);
66 | var u = new URL(url);
67 | if(u.pathname == "/inmeeting")
68 | {
69 | let param = getStartParam();
70 | param['room'] = u.searchParams.get('room');
71 | param['userId'] = u.searchParams.get('user');
72 | if(app.isReady())
73 | {
74 | CreateMainWin(param);
75 | return;
76 | }
77 | _startParam = param;
78 | }
79 | });
80 |
81 | app.on('ready', async () => {
82 |
83 | !app.isDefaultProtocolClient('rtcclient') && app.setAsDefaultProtocolClient("rtcclient");
84 |
85 | logger.info('load success');
86 |
87 | let param = _startParam ? _startParam : getStartParam();
88 | if(param.userId == 'any')
89 | {
90 | loginRoomWindow = CreateDefaultWin({width:800,height:520,resizable:false});
91 | loginRoomWindow.loadFile( path.join(__dirname, 'static/loginRoom.html'));
92 | loginRoomWindow.on('closed', () => {
93 | loginRoomWindow = null;
94 | });
95 | }
96 | else
97 | {
98 | CreateMainWin(param);
99 | }
100 | });
101 | })();
102 |
103 | function MergeObject(a, b) {
104 | let c = JSON.parse(JSON.stringify(a))
105 | for (const key in b) {
106 | if (Object.hasOwnProperty.call(b, key)) {
107 | c[key] = (typeof b[key] == 'object' && c[key] && typeof c[key] == 'object') ? MergeObject(c[key],b[key]) : b[key]
108 | }
109 | }
110 | return c;
111 | }
112 |
113 | function getStartParam(argv)
114 | {
115 | let param = {
116 | serverURL: Buffer.from('aHR0cHM6Ly95enNsLmJlaWppbmd5dW56aGlzaGFuZy5jb20v','base64').toString(),
117 | room:'8888',
118 | userId:'any',
119 | userNick:'游客'
120 | };
121 |
122 | let _argv = argv ? argv : process.argv;
123 |
124 | logger.info(`process.argv = ${JSON.stringify(_argv)}`);
125 |
126 | _argv.forEach(arg => {
127 | let _ = null;
128 | if(arg.startsWith("rtcclient://page/inmeeting"))
129 | {
130 | let u = new URL(arg);
131 | param['room'] = u.searchParams.get('room');
132 | param['userId'] = u.searchParams.get('user');
133 | return;
134 | }
135 | if((_ = arg.match(/^--(.*)=([^=]*)$/)) && _.length > 2)
136 | {
137 | param[ _[1] ]=_[2];
138 | }
139 | });
140 |
141 | logger.info(`param = ${JSON.stringify(param)}`);
142 |
143 | return param;
144 | }
145 |
146 | function CreateMainWin(param)
147 | {
148 | let win = mainWindow;
149 |
150 | os.platform == 'darwin' && systemPreferences.askForMediaAccess('microphone');
151 | os.platform == 'darwin' && systemPreferences.askForMediaAccess('camera');
152 |
153 | mainWindow = CreateDefaultWin();
154 | mainWindow.loadFile( path.join(__dirname, 'static/index.html'),{ query:param });
155 | mainWindow.on('closed', () => {
156 | mainWindow = null;
157 | BrowserWindow.getAllWindows().forEach(window => {
158 | isDev && window.webContents.closeDevTools()
159 | window.close();
160 | })
161 | });
162 |
163 | mainWindow.once('ready-to-show',()=>{
164 | mainWindow.focus();
165 | mainWindow.moveTop();
166 | //mainWindow.setAspectRatio(16.0/9.0);
167 | });
168 |
169 | win && win.close();
170 |
171 | _startParam = null;
172 | }
173 |
174 | function CreateDefaultWin(options)
175 | {
176 | let opt = {
177 | width: 960,
178 | height: 572,
179 | backgroundColor: '#ff2e2c29',
180 | skipTaskbar: false,
181 | transparent: false, frame: false, resizable: true,
182 | webPreferences: {
183 | nodeIntegration: true,
184 | spellcheck: false,
185 | webSecurity:!isDev,
186 | contextIsolation:false
187 | },
188 | icon: path.join(__dirname, 'static/icon/logo.png'),
189 | alwaysOnTop: false,
190 | hasShadow: false,
191 | };
192 | options && (opt = MergeObject(opt,options));
193 | let win = new BrowserWindow(opt);
194 | win.setMenu(null);
195 | isDev && win.webContents.openDevTools();
196 | win.webContents.on('ipc-message',ipcMessageFun);
197 | win.webContents.on('ipc-message-sync',ipcMessageFun);
198 | win.on('enter-full-screen',fullScreenChanged);
199 | win.on('leave-full-screen',fullScreenChanged);
200 | win.webContents.on('new-window', function(event, url, frameName, disposition, options){
201 | event.preventDefault();
202 | shell.openExternal(url);
203 | });
204 | win.webContents.on('dom-ready',function(e){
205 | let win = BrowserWindow.fromWebContents(e.sender);
206 | e.sender.send('maximizeChanged', win.isFullScreen());
207 | e.sender.send('set-version', package_self.version);
208 | });
209 | return win;
210 | }
211 |
212 | function fullScreenChanged(e){
213 | e.sender.webContents.send('maximizeChanged', !e.sender.isFullScreen());
214 | }
215 |
216 |
217 | function ipcMessageFun(e,channel,...theArgs){
218 | const data = theArgs.length ? theArgs[0] : null;
219 | logger.info( `win webContents Id: ${ e.sender.id } | ${channel} | ${data}`);
220 | let win = BrowserWindow.fromWebContents( webContents.fromId(e.sender.id) );
221 | if(win == null)
222 | {
223 | logger.error( `winId:${e.sender.id } | win = null`);
224 | return;
225 | }
226 |
227 | if (/-win$/.test(channel)) {
228 | const cmd = channel.replace(/-win$/,'');
229 | isDev && cmd == 'close' && win.webContents.closeDevTools();
230 | win[cmd].call(win,...theArgs);
231 | return;
232 | }
233 |
234 | if (channel === 'getUser') {
235 | if(fs.existsSync(localConfig))
236 | {
237 | let con = JSON.parse(fs.readFileSync(localConfig,{encoding:'utf-8',flag:'r'}));
238 | win.webContents.send('getUserRsp',con);
239 | }
240 | return;
241 | }
242 |
243 | if(channel === 'create-video-windows'){
244 | newWindows = CreateDefaultWin();
245 | newWindows.loadFile( path.join(__dirname, 'static/videoWindows.html'),{query:{ id:e.sender.id}});
246 | newWindows.moveTop();
247 | videoWindows.push( newWindows );
248 | e.returnValue = { webContentsId: newWindows.webContents.id };
249 | return;
250 | }
251 | if(channel === 'joinRoom'){
252 | let param = getStartParam();
253 |
254 | let con = fs.existsSync(localConfig)?
255 | JSON.parse(fs.readFileSync(localConfig,{encoding:'utf8',flag:'r'})):{};
256 | let lastConfigStr = JSON.stringify(con);
257 | let mapKeys = ['userId','enableAudio','enableVideo']
258 | for (const key in data) {
259 | Object.hasOwnProperty.call(data, key) && (param[key] = data[key],mapKeys.indexOf(key) != -1 && (con[key] = data[key]));
260 | }
261 | JSON.stringify(con) != lastConfigStr && (fs.writeFileSync(localConfig,JSON.stringify(con),{encoding:'utf-8'}));
262 |
263 | CreateMainWin(param);
264 |
265 | loginRoomWindow && (loginRoomWindow.close());
266 | return;
267 | }
268 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "owt-client",
3 | "version": "2.0.1",
4 | "description": "owt-client",
5 | "main": "main.js",
6 | "repository": {
7 | "type": "git",
8 | "url": "git+https://github.com/HeiSir2014/owt-client-rtc.git"
9 | },
10 | "keywords": [
11 | "electron",
12 | "webrtc",
13 | "rtc",
14 | "owt"
15 | ],
16 | "author": "heisir (QQ:1586462)",
17 | "license": "MIT",
18 | "bugs": {
19 | "url": "https://github.com/HeiSir2014/owt-client-rtc/issues"
20 | },
21 | "homepage": "https://github.com/HeiSir2014/owt-client-rtc#readme",
22 | "devDependencies": {
23 | "electron": "^13.1.6",
24 | "electron-builder": "^22.11.7"
25 | },
26 | "dependencies": {
27 | "electron-is-dev": "^2.0.0",
28 | "winston": "^3.3.3"
29 | },
30 | "scripts": {
31 | "start": "electron .",
32 | "pack-mac": "electron-builder --mac --publish always",
33 | "pack-win": "electron-builder --win --publish always",
34 | "postinstall": "electron-builder install-app-deps",
35 | "pack-linux": "electron-builder --linux --publish always",
36 | "windows-latest": "npm run pack-win",
37 | "macos-latest": "npm run pack-mac",
38 | "ubuntu-latest": "npm run pack-linux"
39 | },
40 | "build": {
41 | "appId": "cn.heisir.owtclientrtc",
42 | "artifactName": "OWTClient-${os}_${arch}-v${version}.${ext}",
43 | "electronVersion": "13.1.6",
44 | "copyright": "Copyright © 2021 ${author}",
45 | "compression": "normal",
46 | "directories": {
47 | "output": "dist"
48 | },
49 | "files": [
50 | "**/*",
51 | "!dist/*",
52 | "!.github/*",
53 | "!README.md",
54 | "!.gitignore"
55 | ],
56 | "asar": true,
57 | "win": {
58 | "target": [
59 | {
60 | "target": "nsis"
61 | },
62 | {
63 | "target": "zip",
64 | "arch": [
65 | "x64",
66 | "ia32"
67 | ]
68 | }
69 | ],
70 | "icon": "static/icon/logo-512.png"
71 | },
72 | "dmg": {
73 | "window": {
74 | "width": 540,
75 | "height": 380
76 | },
77 | "contents": [
78 | {
79 | "x": 410,
80 | "y": 230,
81 | "type": "link",
82 | "path": "/Applications"
83 | },
84 | {
85 | "x": 130,
86 | "y": 230,
87 | "type": "file"
88 | }
89 | ]
90 | },
91 | "mac": {
92 | "hardenedRuntime": true,
93 | "appId": "cn.heisir.owtclientrtc-mac",
94 | "category": "public.app-category.productivity",
95 | "target": [
96 | "dmg"
97 | ],
98 | "icon": "static/icon/logo.icns",
99 | "minimumSystemVersion": "10.15"
100 | },
101 | "nsis": {
102 | "perMachine": true,
103 | "oneClick": false,
104 | "allowElevation": true,
105 | "allowToChangeInstallationDirectory": true,
106 | "installerIcon": "static/icon/logo.ico",
107 | "uninstallerIcon": "static/icon/logo.ico",
108 | "installerHeaderIcon": "static/icon/logo.ico",
109 | "createDesktopShortcut": true,
110 | "createStartMenuShortcut": true,
111 | "shortcutName": "OWTClient",
112 | "include": "install-package/urlProtocol.nsh"
113 | },
114 | "protocols": [
115 | {
116 | "name": "owtclient",
117 | "schemes": [
118 | "owtclient"
119 | ]
120 | }
121 | ],
122 | "appImage": {
123 | "category": "public.app-category.productivity"
124 | },
125 | "linux": {
126 | "target": [
127 | "AppImage",
128 | "deb"
129 | ],
130 | "maintainer": "heisir ",
131 | "category": "Utility"
132 | }
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/static/audio/some_one_join_room.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/audio/some_one_join_room.wav
--------------------------------------------------------------------------------
/static/css/index.css:
--------------------------------------------------------------------------------
1 | html,body{
2 | padding: 0;
3 | margin: 0;
4 | box-sizing: border-box;
5 | width: 100%;
6 | height: 100%;
7 | }
8 | html,body *{
9 | user-select:none;
10 | }
11 |
12 | div,input,span,a,p,br,h1,h2,h3,ul,li,ol{
13 | padding: 0;
14 | margin: 0;
15 | box-sizing: border-box;
16 | }
17 |
18 | .video-container{
19 | margin: 0 auto;
20 | width: 100%;
21 | height: calc(100% - 32px);
22 | position: relative;
23 | overflow: hidden;
24 | top: 0;
25 | left: 0;
26 | right: 0;
27 | bottom: 0;
28 | box-sizing: border-box;
29 | }
30 |
31 | .video-container.fullscreen{
32 | height: 100%;
33 | }
34 |
35 | .video-container video{
36 | position: relative;
37 | width: 100%;
38 | height: 100%;
39 | object-fit: contain;
40 | box-sizing: border-box;
41 | }
42 |
43 | @keyframes toolsAnimation{
44 | from {filter: opacity(0);}
45 | to {filter: opacity(1);}
46 | }
47 |
48 | .video-container .tools{
49 | position: absolute;
50 | width: fit-content;
51 | height: 62px;
52 | bottom: 50px;
53 | left: 0;
54 | right: 0;
55 | margin: 0 auto;
56 | background-color: #404040;
57 | border-radius: 5px;
58 | box-shadow: 0px 0px 3px 0px #4f4f4f8f;
59 | overflow: visible;
60 | padding: 0;
61 | opacity: 0.85;
62 | -webkit-app-region: no-drag;
63 | text-align: left;
64 | }
65 |
66 | .video-container .tools .exit{
67 | float: right;
68 | }
69 | /*
70 | .video-container .tools:hover{
71 | animation: toolsAnimation 1.5s;
72 | opacity:1;
73 | } */
74 |
75 | .video-container .tools:hover >*{
76 | filter: none;
77 | }
78 |
79 | .video-container .tools .el-dropdown,.video-container .tools .dorpdownLabel,.video-container .tools .el-menu-member{
80 | width: 100%;
81 | height: 100%;
82 | color:white;
83 | }
84 |
85 | .video-container .tools > *{
86 | width: 76px;
87 | line-height: 1.0;
88 | height: 100%;
89 | border: none;
90 | text-align: center;
91 | background-color: transparent;
92 | color: white;
93 | box-sizing: content-box;
94 | display: inline-block;
95 | vertical-align: middle;
96 | border-right: solid 1px #a0a0a040;
97 | border-left: solid 1px transparent;
98 | position: relative;
99 | font-size: 0;
100 | margin: 0;
101 | padding: 0;
102 | }
103 |
104 | .video-container .tools > *:not(:first-child){
105 | margin-left: -4px;
106 | }
107 |
108 | .video-container .tools > *:hover{
109 | background-color:#00000033;
110 | }
111 |
112 | .video-container .tools >:last-child{
113 | border-right: 0;
114 | }
115 |
116 | .video-container .tools option{
117 | text-align: center;
118 | background-color: #404040AA;
119 | height: 32px;
120 | line-height: 1.0;
121 | }
122 |
123 | .video-container .tools .layout{
124 | line-height: 1.0;
125 | }
126 | .video-container .tools .label{
127 | width: 28px;
128 | height: 60%;
129 | line-height: 1.0;
130 | object-fit: contain;
131 | margin-top: 5px;
132 | }
133 | .video-container .tools .tip{
134 | display: none;
135 | }
136 |
137 | .video-container .tools > *:hover .tip{
138 | display: block;
139 | position: absolute;
140 | top: -40px;
141 | min-width: 150px;
142 | width: fit-content;
143 | height: fit-content;
144 | font-size: 14px;
145 | white-space: nowrap;
146 | text-align: center;
147 | padding: 10px;
148 | background-color: brown;
149 | border-radius: 5px;
150 | left: -30px;
151 | }
152 |
153 | .video-container .tools .record{
154 | display: none;
155 | }
156 |
157 | .video-container .tools .title{
158 | position: absolute;
159 | bottom: 8px;
160 | width: 100%;
161 | font-size: 12px;
162 | white-space: nowrap;
163 | text-align: center;
164 | overflow: hidden;
165 | padding: 0 1px;
166 | font-weight: 400;
167 | letter-spacing: 2px;
168 | text-indent:2px;
169 | }
170 |
171 | .video-container .chat{
172 | position: absolute;
173 | width: fit-content;
174 | height: fit-content;
175 | bottom: 50px;
176 | left: 16px;
177 | background-color:#FAFAFA;
178 | border-radius: 2px;
179 | padding: 0 5px;
180 | -webkit-app-region: no-drag;
181 | }
182 |
183 | .video-container .chat>*{
184 | width: 32px;
185 | height: 32px;
186 | background-repeat: no-repeat;
187 | background-size: 26px;
188 | background-position: center;
189 | display: inline;
190 | vertical-align: middle;
191 | font-size: 0;
192 | float: left;
193 | }
194 | .video-container .chat .emoji_group span
195 | {
196 | width: 100%;
197 | height: 100%;
198 | display: block;
199 | }
200 |
201 | .video-container .chat .emoji_group span button{
202 | width: 100%;
203 | height: 100%;
204 | padding: 0;
205 | border: 0;
206 | }
207 |
208 | .video-container .chat .emoji{
209 | background-image: url(../icon/emoji.png);
210 |
211 | background-repeat: no-repeat;
212 | background-size: 24px;
213 | background-position: center;
214 |
215 | }
216 | .video-container .chat .emoji:hover{
217 | background-image: url(../icon/emoji_hover.png);
218 | }
219 | .video-container .chat .msg{
220 | background-image: url(../icon/msg.png);
221 |
222 | }
223 | .video-container .chat .msg:hover{
224 | background-image: url(../icon/msg_hover.png);
225 | }
226 |
227 | .video-container .chat .msg_input{
228 | width: 120px;
229 | padding: 2px 5px;
230 | border-left: solid 1px #aaa;
231 | }
232 |
233 | .video-container .chat .msg_input > input{
234 | width: 100%;
235 | height: 100%;
236 | line-height: 1.0;
237 | border: 0;
238 | padding: 0 5px;
239 | font-size: 12px;
240 | margin: 0;
241 | }
242 |
243 | .video-container .drag{
244 | position: absolute;
245 | width: 80%;
246 | height: 80%;
247 | left: 10%;
248 | top:10%;
249 | font-size: 0;
250 | margin: 0;
251 | padding: 0;
252 | background: transparent;
253 | -webkit-app-region: drag;
254 | }
255 |
256 | .heisir{
257 | padding: 0;
258 | margin: 0;
259 | background-color: #252526;
260 | background-size: cover;
261 | background-position: center;
262 | position: relative;
263 | overflow: hidden;
264 |
265 | -webkit-app-region: no-drag;
266 | }
267 |
268 | .heisir .header{
269 | width: 100%;
270 | height: 32px;
271 | position: relative;
272 | background-color: #333333;
273 | border-bottom: solid 1px #454545;
274 | z-index: 9999;
275 | -webkit-app-region: drag;
276 | box-sizing: border-box;
277 | }
278 |
279 | .heisir .toolbar{
280 | width: 100%;
281 | height: fit-content;
282 | position: absolute;
283 | z-index: 2;
284 | }
285 |
286 | .heisir .toolbar >:last-child{
287 | border-bottom-left-radius: 4px;
288 | }
289 |
290 | .heisir .network{
291 | width: fit-content;
292 | position: relative;
293 | text-align: left;
294 | background-color: #00000088;
295 | height: 24px;
296 | padding: 0 5px;
297 | box-sizing: content-box;
298 | float: right;
299 | }
300 | .heisir .network >*{
301 | font-size: 10px;
302 | line-height: 24px;
303 | font-style: normal;
304 | display: inline-block;
305 | background-size: contain;
306 | background-repeat: no-repeat;
307 | background-position: center;
308 | height: 100%;
309 | color: white;
310 | vertical-align: middle;
311 | white-space: nowrap;
312 | }
313 | .heisir .network .upload,.heisir .network .download{
314 | width: fit-content;
315 | min-width: 72px;
316 | text-indent: 1px;
317 | text-align: left;
318 | }
319 | .heisir .network .wireless{
320 | width: 12px;
321 | background-image: url(../icon/network.png);
322 | margin-right: 5px;
323 | }
324 |
325 | .heisir .fullscreenbar{
326 | width: 24px;
327 | right: 0;
328 | position: relative;
329 | background-color: #00000088;
330 | height: 24px;
331 | padding: 0;
332 | box-sizing: content-box;
333 | font-size: 0;
334 | outline: 0;
335 | float: right;
336 | }
337 |
338 | .heisir .fullscreenbar .fullscreen{
339 | width: 100%;
340 | height: 100%;
341 | background-image: url(../icon/fullscreen.png);
342 | background-repeat: no-repeat;
343 | background-size: 18px;
344 | background-position: center;
345 | display: inline-block;
346 | }
347 | .heisir .fullscreenbar .unfullscreen{
348 | width: 100%;
349 | height: 100%;
350 | background-image: url(../icon/fullscreen_no.png);
351 | background-repeat: no-repeat;
352 | background-size: 18px;
353 | background-position: center;
354 | display: inline-block;
355 | cursor: pointer;
356 | }
357 |
358 | .heisir .fullscreenbar .unfullscreen:hover {
359 | background-color: #00000044;
360 | }
361 |
362 | .heisir .header > *{
363 | display: inline-block;
364 | }
365 | .heisir .systools {
366 | width: fit-content;
367 | right: 0px;
368 | position: absolute;
369 | height: 100%;
370 | box-sizing: content-box;
371 | overflow: hidden;
372 | -webkit-app-region: no-drag;
373 | }
374 |
375 | .heisir .header .title{
376 | width: fit-content;
377 | height: 100%;
378 | line-height: 32px;
379 | color: #e5e5e5;
380 | vertical-align: middle;
381 | margin: 0 10px;
382 | font-size: 12px;
383 | font-weight: 400;
384 | letter-spacing: 1px;
385 | text-indent: 1px;
386 | }
387 |
388 | .heisir .systools >*{
389 | width: 32px;
390 | height: 100%;
391 | display: inline-block;
392 | vertical-align: middle;
393 | color: #e5e5e5;
394 | font-size: 0;
395 | background-size: 14px;
396 | background-repeat: no-repeat;
397 | background-position: center;
398 | }
399 |
400 | .heisir .systools .fullscreen{
401 | background-image: url(../icon/fullscreen.png);
402 |
403 | background-size: 18px;
404 | }
405 | .heisir .systools .fullscreen:hover{
406 | background-image: url(../icon/fullscreen_hover.png);
407 | }
408 | .heisir .systools .minimize{
409 | background-image: url(../icon/minimize.png);
410 | }
411 |
412 | .heisir .systools .minimize:hover{
413 | background-color: #505050;
414 | background-image: url(../icon/minimize_hover.png);
415 | }
416 |
417 | .heisir .header .systools .close{
418 | background-image: url(../icon/close.png);
419 | }
420 |
421 | .heisir .header .systools .close:hover{
422 | background-color: #D71526;
423 | background-image: url(../icon/close-hover.png);
424 | }
425 |
426 | .heisir .header
427 |
428 | .heisir .bitrate-container{
429 | position: absolute;
430 | top: 10px;
431 | right: 100px;
432 | -webkit-app-region: no-drag;
433 | }
434 |
435 | .heisir .bitrate-container .bitrateSelects,.heisir .bitrate-container .bitrateOption{
436 | margin: 5px 2px;
437 | border: 0px;
438 | padding: 5px 15px;
439 | font-size: 1em;
440 | background-color: black;
441 | color: white;
442 | -webkit-app-region: no-drag;
443 | }
444 |
445 | .heisir .desktop .transparent
446 | {
447 | width: 100%;
448 | height: 100%;
449 | position: absolute;
450 | left: 0;
451 | top: 0;
452 | background-color: transparent;
453 | border: 0;
454 | }
455 |
456 | .heisir .desktop .screen-dialog{
457 | display: none;
458 | position: absolute;
459 | width: 700px;
460 | background-color: #d8d8d8ab;
461 | bottom: 72px;
462 | margin: 0 auto;
463 | border-radius: 4px;
464 | padding: 10px;
465 | box-shadow: 0px 0px 10px 1px #666;
466 | text-align: center;
467 | left: -490px;
468 | overflow-x: auto;
469 | z-index: 5;
470 | -webkit-app-region: no-drag;
471 | opacity: 0;
472 | }
473 |
474 |
475 | .heisir .desktop .screen-dialog .screen-poster{
476 | width: 200px;
477 | margin: 10px;
478 | box-shadow: 0px 0px 10px 1px #333;
479 | }
480 |
481 | .heisir .desktop:hover .screen-dialog{
482 | opacity: 1;
483 | }
484 |
485 | .heisir .desktop .screen-dialog .screen-poster:hover{
486 | transform: scale(1.1);
487 | }
488 |
489 | #App{
490 | width: 100%;
491 | height: 100%;
492 | }
493 |
494 |
495 | .el-header {
496 | -webkit-app-region: drag;
497 | padding:0 !important;
498 | background-color:#009afb;
499 | line-height: 40px;
500 | }
501 |
502 | .el-header .title{
503 | margin-left: 10px;
504 | font-weight: 600;
505 | font-size: 16px;
506 | color: #f3f3f3;
507 | margin-top: -6px;
508 | }
509 |
510 | .systemTool{
511 | position: fixed;
512 | right: 0;
513 | top: 0;
514 | margin-top: -6px;
515 | margin-right: 0px;
516 | -webkit-app-region: no-drag;
517 | }
518 |
519 |
520 | .systemTool .mylink{
521 | color: #f3f3f3;
522 | margin: 0 5px;
523 | text-decoration: none;
524 | font-size: 14px;
525 | }
526 | .systemTool .el-button{
527 | padding:0 !important;
528 | border:0 !important;
529 | border-radius:0 !important;
530 | color: #f3f3f3 !important;
531 | }
532 | .systemTool .el-button:hover{
533 | background-color: #D71526;
534 | }
535 | .systemTool .button{
536 | width: 30px;
537 | height: 30px;
538 | background-color: #00000000;
539 | font-size: large;
540 | }
541 |
542 | .systemTool .el-button+.el-button{
543 | margin-left: 0 !important;
544 | }
545 |
546 | .heisir .message_tip{
547 | width: fit-content;
548 | min-width: auto;
549 | border: 0;
550 | background-color:#66666680;
551 | }
552 |
553 | .heisir .message_tip .el-message__content{
554 | color: #fff;
555 | }
556 |
557 | .heisir .notify_join,.heisir .notify_msg{
558 | width: fit-content;
559 | min-width: 0;
560 | width: fit-content;
561 | padding: 2px 5px;
562 | border-radius: 2px;
563 | background-color: #444444ee;
564 | border: 0;
565 | }
566 |
567 | .heisir .notify_join.left,.heisir .notify_msg.left{
568 | left: 16px;
569 | }
570 |
571 | .heisir .notify_join .el-notification__content,.heisir .notify_msg .el-notification__content{
572 | margin:0;
573 | color: #e6e6e6;
574 | text-indent: 2px;
575 | letter-spacing: 2px;
576 | vertical-align: middle;
577 | }
578 |
579 | .heisir .notify_join .el-notification__group,.heisir .notify_msg .el-notification__group{
580 | margin: 0;
581 | }
582 |
583 | .heisir .notify_msg .el-notification__content div{
584 | display: inline-block;
585 | height: 24px;
586 | line-height: 24px;
587 | margin: 0;
588 | padding: 0;
589 | vertical-align: middle;
590 | }
591 |
592 | .heisir .notify_msg .el-notification__content .nick{
593 | color: #efff00;
594 | }
595 |
596 | .heisir .notify_msg .el-notification__content .content{
597 | color: #efefef;
598 | vertical-align: middle;
599 | }
600 |
601 | .heisir .notify_msg .el-notification__content img{
602 | vertical-align: middle;
603 | }
604 |
605 | .heisir .emoji_popover{
606 | min-width:auto;
607 | padding: 5px;
608 | }
609 |
610 | .heisir .emoji_popover ul{
611 | text-align: center;
612 | }
613 |
614 | .heisir .emoji_popover li{
615 | display: inline-block;
616 | cursor: pointer;
617 | font-size: 0;
618 | outline: 0;
619 | inset: 0;
620 | }
621 |
622 | .heisir .emoji_popover li img{
623 | width: 48px;
624 | height: 48px;
625 | }
626 |
627 | .heisir .emoji_popover li img:hover{
628 | box-sizing: border-box;
629 | box-shadow: inset 0px 0px 8px 1px #ccc;
630 | border-radius: 15px;
631 | }
632 |
633 | .heisir .screen_select_dialog{
634 | width: fit-content;
635 | min-width: 40vw;
636 | }
637 |
638 | .heisir .screen_select_dialog .el-dialog__body{
639 | text-align: center;
640 | }
641 |
642 | .heisir .screen_select li{
643 | list-style: none;
644 | display: inline;
645 | margin: 0 10px;
646 | }
647 | .heisir .screen_select li img{
648 | width: 200px;
649 | height: auto;
650 | }
651 |
652 | .heisir .el-dropdown-menu.el-popper{
653 | background-color: #404040;
654 | border: 0;
655 | opacity: 0.85;
656 | margin: 10px;
657 | color:#ffd400;
658 |
659 | }
660 | .heisir .el-dropdown-menu.el-popper .el-dropdown-menu__item{
661 | color: white;
662 | }
663 |
664 | .heisir .el-dropdown-menu__item:focus,.el-dropdown-menu__item:not(.is-disabled):hover {
665 | background-color:#303030 !important;
666 | color: #ffd400 !important;
667 | }
668 |
669 | .heisir .el-dropdown-menu__item--divided:before{
670 | background-color:#404040 !important;
671 | }
672 |
673 | .heisir .el-dropdown-menu__item--divided {
674 | position: relative;
675 | margin-top: 6px;
676 | border-top: 1px solid #888888
677 | }
678 | .heisir .el-popper[x-placement^=top] .popper__arrow,.el-popper[x-placement^=top] .popper__arrow::after{
679 | border-top-color: #404040 !important;
680 | }
--------------------------------------------------------------------------------
/static/css/room.css:
--------------------------------------------------------------------------------
1 | html,body{
2 | padding: 0;
3 | margin: 0;
4 | box-sizing: border-box;
5 | width: 100%;
6 | height: 100%;
7 | }
8 | html,body *{
9 | user-select:none;
10 | }
11 |
12 | div,input,span,a,p,br,h1,h2,h3,ul,li,ol{
13 | padding: 0;
14 | margin: 0;
15 | box-sizing: border-box;
16 | }
17 |
18 |
19 | .heisir{
20 | padding: 0;
21 | margin: 0;
22 | box-shadow: inset 0px 0px 25px 0px #fff;
23 | position: relative;
24 | overflow: hidden;
25 | background-color: #f0f8ff;
26 | font-weight: 500;
27 | -webkit-app-region: drag;
28 | }
29 |
30 |
31 | .heisir .bk{
32 | width: 100%;
33 | height: 100%;
34 | position: relative;
35 | }
36 |
37 | .heisir .bk .room{
38 | position:absolute;
39 | top:calc((100% - 320px) / 2);
40 | height: 320px;
41 | left: 10%;
42 | }
43 |
44 |
45 | .heisir .header{
46 | width: 100%;
47 | height: 32px;
48 | position: absolute;
49 | z-index: 2;
50 | }
51 |
52 | .heisir .header .systools{
53 | width: fit-content;
54 | right: 0px;
55 | position: absolute;
56 | text-align: left;
57 | height: 100%;
58 | box-sizing: content-box;
59 | overflow: hidden;
60 | -webkit-app-region: no-drag;
61 | }
62 |
63 | .heisir .header .systools:hover{
64 | background-color: rgb(177, 0, 0);
65 | }
66 |
67 | .heisir .header .systools .close{
68 | background-image: url(../icon/close.png);
69 | height: 100%;
70 | width: 32px;
71 | background-size: 14px 14px;
72 | background-repeat: no-repeat;
73 | vertical-align: middle;
74 | margin: 0 auto;
75 | background-position: center;
76 | }
77 |
78 | .heisir .header .systools .close:hover{
79 | background-image: url(../icon/close-hover.png);
80 | }
81 |
82 | .heisir .content{
83 | position:absolute;
84 | top: 10%;
85 | right: 5%;
86 | -webkit-app-region: no-drag;
87 | width: 300px;
88 | height: 80%;
89 | background-color: white;
90 | border-radius: 10px;
91 | padding: 10px;
92 | box-shadow: 2px 2px 5px 1px #666;
93 | font-size: 14px;
94 | font-family: "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif;
95 | }
96 | .heisir .content *{
97 | font-size: 14px;
98 | font-family: "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif;
99 | line-height: 1.0;
100 | }
101 |
102 | .heisir .content .line{
103 | margin: 8px auto;
104 | width: 80%;
105 | display: block;
106 | height: fit-content;
107 | min-height: 14px;
108 | }
109 |
110 | .heisir .content .line > *{
111 | width: 100%;
112 | height: 100%;
113 | padding: 0 5px;
114 | }
115 |
116 | .heisir .content .line input{
117 | height: 32px;
118 | margin: 0 auto;
119 | }
120 |
121 | .heisir .content .line input[type=checkbox]{
122 | height: 24px;
123 | width: fit-content;
124 | vertical-align: middle;
125 | cursor: pointer;
126 | }
127 |
128 | .heisir .content .line input[type=checkbox]:not(:first-child){
129 | margin-left:50px;
130 | }
131 |
132 | .heisir .content .line .checkLabel{
133 | height: 24px;
134 | vertical-align: middle;
135 | margin:3px 0;
136 | cursor: pointer;
137 | }
138 |
139 | .heisir .content .line .checkLabel:hover{
140 | color:#005CC8;
141 | }
142 |
143 | .heisir .content .line input[type=text],.heisir .content .line input[type=number]{
144 | border-radius: 5px;
145 | border-width: 1px;
146 | border-color: #fff;
147 | box-shadow: 1px 1px 2px 0px #666;
148 | }
149 |
150 | .heisir .content .line input[type=button]{
151 | height: 42px;
152 | border: 0;
153 | border-radius: 20px;
154 | background-color: #0079F2;
155 | color: white;
156 | cursor: pointer;
157 | box-shadow: 1px 1px 2px 0px #666;
158 | }
159 |
160 | .heisir .content .line input[type=button]:hover{
161 | font-size: 15px;
162 | font-weight: 450;
163 | }
164 |
165 | .heisir .content .title{
166 | font-size: 28px;
167 | font-weight: 500;
168 | text-shadow: 1px 1px #888;
169 | text-align: center;
170 | color: #333;
171 | letter-spacing: 8px;
172 | text-indent: 8px;
173 | padding: 40px 0px;
174 | margin: 0 auto;
175 |
176 | -webkit-app-region: drag;
177 | }
178 |
179 | .heisir .content .msg{
180 | color: tomato;
181 | }
182 |
183 | .heisir .content .copyright{
184 | text-align: center;
185 | position: absolute;
186 | bottom: 0;
187 | width: 100%;
188 | }
189 |
190 | .heisir .content .copyright a{
191 | text-decoration: transparent;
192 | color: gray;
193 | font-size: 12px;
194 | }
--------------------------------------------------------------------------------
/static/css/screen.css:
--------------------------------------------------------------------------------
1 | html,body{
2 | padding: 0;
3 | margin: 0;
4 | box-sizing: border-box;
5 | width: 100%;
6 | height: 100%;
7 | }
8 | html,body *{
9 | user-select:none;
10 | }
11 |
12 | div,input,span,a,p,br,h1,h2,h3,ul,li,ol{
13 | padding: 0;
14 | margin: 0;
15 | box-sizing: border-box;
16 | }
17 |
18 | .video-container{
19 | margin: 0 auto;
20 | width: 100%;
21 | height: 100%;
22 | overflow: auto;
23 | }
24 |
25 | .video-container .drag{
26 | position: absolute;
27 | width: 80%;
28 | height: 80%;
29 | left: 10%;
30 | top:10%;
31 | font-size: 0;
32 | margin: 0;
33 | padding: 0;
34 | background: transparent;
35 | -webkit-app-region: drag;
36 | }
37 |
38 | .video-container video{
39 | object-fit: none;
40 | box-shadow: inset 0px 0px 25px 0px #fff;
41 | }
42 |
43 | .video-container .tools{
44 | position: relative;
45 | width: fit-content;
46 | height: 60px;
47 | bottom: 15%;
48 | margin: 0 auto;
49 | background: #404040AA;
50 | border-radius: 10px;
51 | overflow: hidden;
52 | padding: 0 10px;
53 | -webkit-app-region: no-drag;
54 | }
55 |
56 | .video-container .tools > *{
57 | width: 60px;
58 | line-height: 1.0;
59 | height: 100%;
60 | border: none;
61 | text-align: center;
62 | background-color: transparent;
63 | color: white;
64 | box-sizing: content-box;
65 | display: inline-block;
66 | vertical-align: middle;
67 | }
68 |
69 | .video-container .tools > *:hover{
70 | transform: scale(1.3);
71 | }
72 |
73 | .video-container .tools option{
74 | text-align: center;
75 | background-color: #404040AA;
76 | height: 32px;
77 | line-height: 1.0;
78 | }
79 |
80 | .video-container .tools .layout{
81 | line-height: 1.0;
82 | }
83 | .video-container .tools .label{
84 | width: 32px;
85 | height: 100%;
86 | line-height: 1.0;
87 | object-fit: contain;
88 | }
89 |
90 |
91 | .video-container .tools .line{
92 | border-right: solid 1px #a0a0a040;
93 | width: 0px;
94 | height: 100%;
95 | line-height: 1.0;
96 | }
97 |
98 | .heisir{
99 | padding: 0;
100 | margin: 0;
101 | background-image: url(../imgs/background-04.png);
102 | background-size: cover;
103 | background-position: center;
104 | box-shadow: inset 0px 0px 25px 0px #fff;
105 | position: relative;
106 | overflow: hidden;
107 |
108 | -webkit-app-region: no-drag;
109 | }
110 |
111 | .heisir .header{
112 | width: 100%;
113 | height: 32px;
114 | position: absolute;
115 | z-index: 2;
116 | -webkit-app-region: drag;
117 | }
118 |
119 | .heisir .header .tools{
120 | width: fit-content;
121 | right: 32px;
122 | position: absolute;
123 | text-align: left;
124 | background-color: #000000;
125 | height: 100%;
126 | padding: 0 5px;
127 | box-sizing: content-box;
128 | border-bottom-left-radius: 5px;
129 | overflow: hidden;
130 |
131 | -webkit-app-region: no-drag;
132 | }
133 | .heisir .header .tools *{
134 | font-size: 12px;
135 | line-height: 32px;
136 | font-style: normal;
137 | display: inline-block;
138 | background-size: contain;
139 | background-repeat: no-repeat;
140 | background-position: center;
141 | height: 100%;
142 | color: white;
143 | vertical-align: middle;
144 | white-space: nowrap;
145 | width: 28px;
146 | }
147 | .heisir .header .tools .screen-scale{
148 | background-image: url(../icon/screen-scale.png);
149 | margin-right: 5px;
150 | }
151 |
152 | .heisir .header .tools .screen-1{
153 | background-image: url(../icon/screen-1-1.png);
154 | margin-right: 5px;
155 | }
156 |
157 | .heisir .header .systools{
158 | width: 32px;
159 | right: 0px;
160 | position: absolute;
161 | text-align: left;
162 | background-color: #000000bb;
163 | height: 100%;
164 | box-sizing: content-box;
165 | overflow: hidden;
166 | border-left: solid 1px #a0a0a088;
167 | -webkit-app-region: no-drag;
168 | }
169 |
170 | .heisir .header .systools:hover{
171 | background-color: rgb(177, 0, 0);
172 | }
173 |
174 | .heisir .header .systools .close{
175 | background-image: url(../icon/close.png);
176 | height: 100%;
177 | width: 14px;
178 | background-size: contain;
179 | background-repeat: no-repeat;
180 | vertical-align: middle;
181 | margin: 0 auto;
182 | background-position: center;
183 | }
184 |
185 | .heisir .header .systools .close:hover{
186 | background-image: url(../icon/close-hover.png);
187 | }
188 |
189 |
190 | .heisir .bitrate-container{
191 | position: absolute;
192 | top: 10px;
193 | right: 100px;
194 | -webkit-app-region: no-drag;
195 | }
196 |
197 | .heisir .bitrate-container .bitrateSelects,.heisir .bitrate-container .bitrateOption{
198 | margin: 5px 2px;
199 | border: 0px;
200 | padding: 5px 15px;
201 | font-size: 1em;
202 | background-color: black;
203 | color: white;
204 | -webkit-app-region: no-drag;
205 | }
206 |
207 | .heisir .screen-dialog{
208 | display: none;
209 | position: absolute;
210 | width: 700px;
211 | background-color: #d8d8d8ab;
212 | bottom: calc(15% + 10px);
213 | margin: 0 auto;
214 | border-radius: 4px;
215 | padding: 10px;
216 | box-shadow: 0px 0px 10px 1px #666;
217 | text-align: center;
218 | left: calc(50% - 350px);
219 | overflow-x: auto;
220 | z-index: 5;
221 | -webkit-app-region: no-drag;
222 | }
223 |
224 |
225 | .heisir .screen-dialog .screen-poster{
226 | width: 200px;
227 | margin: 10px;
228 | box-shadow: 0px 0px 10px 1px #333;
229 | }
230 |
231 | .heisir .screen-dialog .screen-poster:hover{
232 | transform: scale(1.1);
233 | }
--------------------------------------------------------------------------------
/static/element-ui/fonts/element-icons.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/element-ui/fonts/element-icons.woff
--------------------------------------------------------------------------------
/static/icon/camera.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/icon/camera.png
--------------------------------------------------------------------------------
/static/icon/camera_no.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/icon/camera_no.png
--------------------------------------------------------------------------------
/static/icon/close-hover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/icon/close-hover.png
--------------------------------------------------------------------------------
/static/icon/close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/icon/close.png
--------------------------------------------------------------------------------
/static/icon/desktop-share.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/icon/desktop-share.png
--------------------------------------------------------------------------------
/static/icon/desktop-share_no.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/icon/desktop-share_no.png
--------------------------------------------------------------------------------
/static/icon/emoji.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/icon/emoji.png
--------------------------------------------------------------------------------
/static/icon/emoji_hover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/icon/emoji_hover.png
--------------------------------------------------------------------------------
/static/icon/exit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/icon/exit.png
--------------------------------------------------------------------------------
/static/icon/fullscreen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/icon/fullscreen.png
--------------------------------------------------------------------------------
/static/icon/fullscreen_hover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/icon/fullscreen_hover.png
--------------------------------------------------------------------------------
/static/icon/fullscreen_no.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/icon/fullscreen_no.png
--------------------------------------------------------------------------------
/static/icon/layout.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/icon/layout.png
--------------------------------------------------------------------------------
/static/icon/logo-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/icon/logo-512.png
--------------------------------------------------------------------------------
/static/icon/logo.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/icon/logo.icns
--------------------------------------------------------------------------------
/static/icon/logo.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/icon/logo.ico
--------------------------------------------------------------------------------
/static/icon/logo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/icon/logo.jpg
--------------------------------------------------------------------------------
/static/icon/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/icon/logo.png
--------------------------------------------------------------------------------
/static/icon/logo.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/icon/logo.psd
--------------------------------------------------------------------------------
/static/icon/m3u8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/icon/m3u8.png
--------------------------------------------------------------------------------
/static/icon/members.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/icon/members.png
--------------------------------------------------------------------------------
/static/icon/mic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/icon/mic.png
--------------------------------------------------------------------------------
/static/icon/mic_no.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/icon/mic_no.png
--------------------------------------------------------------------------------
/static/icon/minimize.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/icon/minimize.png
--------------------------------------------------------------------------------
/static/icon/minimize_hover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/icon/minimize_hover.png
--------------------------------------------------------------------------------
/static/icon/msg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/icon/msg.png
--------------------------------------------------------------------------------
/static/icon/msg_hover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/icon/msg_hover.png
--------------------------------------------------------------------------------
/static/icon/network-0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/icon/network-0.png
--------------------------------------------------------------------------------
/static/icon/network-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/icon/network-1.png
--------------------------------------------------------------------------------
/static/icon/network-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/icon/network-2.png
--------------------------------------------------------------------------------
/static/icon/network.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/icon/network.png
--------------------------------------------------------------------------------
/static/icon/record.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/icon/record.png
--------------------------------------------------------------------------------
/static/icon/screen-1-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/icon/screen-1-1.png
--------------------------------------------------------------------------------
/static/icon/screen-scale.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/icon/screen-scale.png
--------------------------------------------------------------------------------
/static/icon/show.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/icon/show.png
--------------------------------------------------------------------------------
/static/icon/speak.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/icon/speak.png
--------------------------------------------------------------------------------
/static/icon/speak_no.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/icon/speak_no.png
--------------------------------------------------------------------------------
/static/imgs/background-00.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/imgs/background-00.webp
--------------------------------------------------------------------------------
/static/imgs/background-01.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/imgs/background-01.webp
--------------------------------------------------------------------------------
/static/imgs/background-02.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/imgs/background-02.webp
--------------------------------------------------------------------------------
/static/imgs/background-03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/imgs/background-03.png
--------------------------------------------------------------------------------
/static/imgs/background-04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/imgs/background-04.png
--------------------------------------------------------------------------------
/static/imgs/emoji/emoji_0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/imgs/emoji/emoji_0.png
--------------------------------------------------------------------------------
/static/imgs/emoji/emoji_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/imgs/emoji/emoji_1.png
--------------------------------------------------------------------------------
/static/imgs/emoji/emoji_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/imgs/emoji/emoji_2.png
--------------------------------------------------------------------------------
/static/imgs/emoji/emoji_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/imgs/emoji/emoji_3.png
--------------------------------------------------------------------------------
/static/imgs/emoji/emoji_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/imgs/emoji/emoji_4.png
--------------------------------------------------------------------------------
/static/imgs/emoji/emoji_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/imgs/emoji/emoji_5.png
--------------------------------------------------------------------------------
/static/imgs/emoji/emoji_6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/imgs/emoji/emoji_6.png
--------------------------------------------------------------------------------
/static/imgs/emoji/emoji_7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/imgs/emoji/emoji_7.png
--------------------------------------------------------------------------------
/static/imgs/room-bkg.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/imgs/ui-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/imgs/ui-01.png
--------------------------------------------------------------------------------
/static/imgs/ui-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/imgs/ui-02.png
--------------------------------------------------------------------------------
/static/imgs/ui-03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/imgs/ui-03.png
--------------------------------------------------------------------------------
/static/imgs/ui-04.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HeiSir2014/owt-client-rtc/c8c89cb4cb14808d1a5000eff859bef4c1e18088/static/imgs/ui-04.gif
--------------------------------------------------------------------------------
/static/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 多人会议
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
24 |
25 |
35 |
40 |
112 |
113 |
114 |
116 |
117 | ![]()
119 |
120 |
121 |
122 |
123 |
124 |
125 |
127 |
128 |
129 |
130 |
131 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
161 |
162 |
163 |
--------------------------------------------------------------------------------
/static/loginRoom.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 多人会议
7 |
8 |
9 |
10 |
16 |
17 |

18 |
19 |
20 |
多人会议
21 |
房间号
22 |
23 |
姓名 / 昵称
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
67 |
68 |
--------------------------------------------------------------------------------
/static/scripts/app-index.js:
--------------------------------------------------------------------------------
1 | const { ipcRenderer, desktopCapturer } = require('electron');
2 |
3 | Vue.directive('focus', {
4 | update: function (el, { oldValue, value }) {
5 | oldValue != value && value && el.focus();
6 | }
7 | });
8 |
9 | const _app = new Vue({
10 | el: '#App',
11 | data: function () {
12 | return {
13 | version: '',
14 | isMaximized: false,
15 | playerStream: null,
16 | myRoom: '',
17 | statUpload: 0,
18 | statDownload: 0,
19 | playerStream: null,
20 | isSpeakMuted: false,
21 | isMicMuted: false,
22 | isCameraMuted: false,
23 | isCamera2Muted: false,
24 | isDesktopShared: false,
25 | isRecordStarted: false,
26 | usedMicrophone: '',
27 | usedCamera: '',
28 | usedCamera2: '',
29 | usedCamera2DeviceId: '',
30 | layoutIndex: 0,
31 | msg_input_visible: false,
32 | msg_content: '',
33 | emoji_visible: false,
34 | emoji_data: ['emoji_5', 'emoji_6', 'emoji_7', 'emoji_4', 'emoji_0', 'emoji_1', 'emoji_2', 'emoji_3'],
35 | screen_data: [],
36 | screen_select_visible: false,
37 | lastMoveTime: 0,
38 | hideCursorIntervalHandle: null,
39 | tools_visible: true,
40 | tools_hover: false,
41 | participants: [],
42 | remoteStreams: [],
43 | screenSharingUser: '',
44 | localStream:null,
45 | localStreamSecond:null,
46 | selectStreamId:'',
47 |
48 | }
49 | },
50 | methods: {
51 | init: function (e) {
52 | const that = this;
53 |
54 | ipcRenderer.on('maximizeChanged', that.maximizeChanged.bind(that));
55 | ipcRenderer.on('set-version', that._setVersion.bind(that));
56 | window.addEventListener('keyup', that.onkeyup.bind(that));
57 | window.addEventListener('mousemove', that.onmousemove.bind(that));
58 |
59 | that.myRoom = getParameterByName('room');
60 | that.myUserId = getParameterByName('userId');
61 | that.myUserNick = getParameterByName('userNick');
62 | that.enableAudio = getParameterByName('enableAudio');
63 | that.enableVideo = getParameterByName('enableVideo');
64 |
65 | that.enableAudio = ((!that.enableAudio || that.enableAudio == 'true') ? true : false);
66 | that.enableVideo = ((!that.enableVideo || that.enableVideo == 'true') ? true : false);
67 |
68 | that.isMicMuted = true;
69 | that.isCameraMuted = true;
70 | that.isCamera2Muted = true;
71 | that.isDesktopShared = false;
72 | that.isRecordStarted = false;
73 |
74 | that.checkDevices();
75 |
76 | createToken(that.myRoom, that.myUserId, 'presenter', function (response) {
77 | var token = response;
78 | if (!token) {
79 | console.error('token is empty');
80 | return;
81 | }
82 | that.conference = new Owt.Conference.ConferenceClient();
83 | that.conference.join(token).then(resp => {
84 | that.myId = resp.self.id;
85 | that.myRoomId = resp.id;
86 |
87 | that.participants = resp.participants;
88 |
89 | that.participants.forEach(p => {
90 | p.addEventListener('left', that.participantleft.bind(that, p));
91 | });
92 | that.remoteStreams = resp.remoteStreams.filter(r => r.source && r.source.video && r.source.video != 'mixed');
93 |
94 | that.subscribeMixStream();
95 | }, function (err) {
96 | console.error('server connection failed:', err);
97 | if (err.message.indexOf('connect_error:') >= 0) {
98 | const div = $('网络错误
');
99 | div.appendTo($p);
100 | $p.appendTo($('body'));
101 | }
102 | });
103 |
104 | that.conference.addEventListener('participantjoined', that.participantjoined.bind(that));
105 | that.conference.addEventListener('streamadded', that.streamAdded.bind(that));
106 | that.conference.addEventListener('messagereceived', that.messagereceived.bind(that));
107 | that.conference.addEventListener('serverdisconnected', that.serverdisconnected.bind(that));
108 |
109 | that.statInterval && (clearInterval(statInterval), that.statInterval = 0);
110 | that.statInterval = setInterval(that._getStat.bind(that), 1000);
111 | that.onmousemove();
112 | });
113 |
114 | },
115 | _setVersion:function(event , version){
116 | this.version = version;
117 | },
118 | onkeyup: function (e) {
119 | e.keyCode == 27 && this.isMaximized && ipcRenderer.send('setFullScreen-win', false);
120 | return e.keyCode != 27;
121 | },
122 | onmousemove: function (e) {
123 | if (this.hideCursorIntervalHandle == null) {
124 | document.body.style.cursor = "default";
125 | this.tools_visible = true;
126 | this.hideCursorIntervalHandle = setInterval(this.hideCursorInterval.bind(this), 1000);
127 | }
128 | this.lastMoveTime = Date.now();
129 | return true;
130 | },
131 | hideCursorInterval: function () {
132 | if (!this.tools_hover && this.lastMoveTime && Date.now() - (this.isFullViewer ? 5000 : 2000) > this.lastMoveTime) {
133 |
134 | clearInterval(this.hideCursorIntervalHandle), this.hideCursorIntervalHandle = null;
135 | document.body.style.cursor = "none";
136 | this.tools_visible = false;
137 | }
138 | },
139 | msg_input_keyup: function (e) {
140 | e.keyCode == 13 && this.msg_content && (this._sendMsg('msg_text', this.msg_content), this.msg_content = '');
141 | return e.keyCode != 13;
142 | },
143 | _sendMsg: function (type, content) {
144 | if (!this.conference) return;
145 | let msg = { type: type, content: content };
146 | this.conference.send(msg);
147 | this.showMessage(this.myUserId, msg);
148 | },
149 | sendEmojiMsg: function (e) {
150 | this._sendMsg('msg_emoji', e.target.parentElement.getAttribute('data'));
151 | this.emoji_visible = false;
152 | },
153 | maximizeChanged: function (event, isMaximized) {
154 | this.isMaximized = isMaximized;
155 | if (this.isMaximized) {
156 | this.$message({
157 | message: '按下 ESC 键可以退出全屏',
158 | center: true,
159 | iconClass: '',
160 | customClass: 'message_tip',
161 | duration: 3000,
162 | offset: (document.body.clientHeight / 2) - 24
163 | });
164 | }
165 | },
166 | fullscreen: function (e) {
167 | ipcRenderer.send('setFullScreen-win', true);
168 | },
169 | unFullscreen: function (e) {
170 | ipcRenderer.send('setFullScreen-win', false);
171 | },
172 | close: function (e) {
173 | this._exitRoom();
174 | ipcRenderer.send("close-win");
175 | },
176 | minimize: function (e) {
177 | ipcRenderer.send("minimize-win");
178 | },
179 | subscribeMixStream:async function(){
180 | const that = this;
181 | (that.enableAudio || that.enableVideo) && that.publishVideo();
182 |
183 | var streams = that.conference.info.remoteStreams;
184 | console.log(streams);
185 | for (const stream of streams) {
186 | if ((stream.source.audio === 'mixed' || stream.source.video ===
187 | 'mixed') && stream.id.indexOf('-common') != -1) {
188 | that.subscribeStream(stream, (subscribe) => {
189 | console.log('subscribeStream result');
190 | that.subscriptionGlobal = subscribe;
191 | that.subscriptionGlobal && (that.playerStream = stream.mediaStream, that.mixStreamGlobal = stream);
192 | that.subscriptionGlobal.addEventListener('error',(e)=>{
193 | console.log("subscriptionGlobal error",e);
194 |
195 | });
196 | streams.filter((s)=>s.source.video == 'screen-cast').length > 0 && that.switchVideoParam(true);
197 | that.subscriptionGlobal.addEventListener('ended',(e)=>{
198 | console.log("subscriptionGlobal ended",e);
199 | setTimeout(that.subscribeMixStream.bind(that),5000);
200 | });
201 | },()=>{
202 | setTimeout(that.subscribeMixStream.bind(that),5000);
203 | });
204 | console.log('subscribeStream finish');
205 | }
206 | that.streamAdded({ stream });
207 | }
208 | },
209 | subscribeStream: async function (stream, callback,reject) {
210 | const that = this;
211 | if (!that.conference) return;
212 | try {
213 | let resolution = stream.settings.video[0].resolution;
214 | if(stream.source.video != 'screen-cast')
215 | {
216 | stream.settings.video.forEach(v => {
217 | v.resolution.width == 1280 && v.resolution.height == 720 && (resolution = v.resolution);
218 | });
219 |
220 | resolution.width != 1280 && stream.extraCapabilities.video.resolutions.forEach(r => {
221 | r.width == 1280 && (resolution = r);
222 | });
223 |
224 | console.log(resolution,stream.settings.video)
225 | }
226 | await that.conference.subscribe(stream, {
227 | audio: stream.source.audio ? true : false,
228 | video: {
229 | codec: { name: "h264", profile: "CB" },
230 | resolution: resolution,
231 | }
232 | }).then(callback, (err) => {
233 | console.log('subscribe failed', err);
234 | reject && reject();
235 | });
236 | } catch (error) {
237 | console.log('subscribe failed', error);
238 | reject && reject();
239 | }
240 | return;
241 | },
242 | convertSource: function (streamSource) {
243 | const dict = { camera: "摄像头", "screen-cast": "桌面" };
244 | if (streamSource in dict) return dict[streamSource];
245 | return '视频';
246 | },
247 | convertSource2: function (remoteStream) {
248 | return this.getStreamUserId(remoteStream)+"的"+this.convertSource(remoteStream.source.video);
249 | },
250 | getUserIdFromOrigin: function (origin) {
251 | const p = this.participants.find(p => p.id == origin);
252 | if (!p) return '';
253 | const userId = this.participants.find(p => p.id == origin).userId;
254 | return userId == this.myId ? userId + '(我)' : userId;
255 | },
256 | getStreamUserId: function (remoteStream) {
257 | if (!remoteStream) return ''
258 | return this.getUserIdFromOrigin(remoteStream.origin);
259 | },
260 | participantjoined: function (e) {
261 | if (/robot/.test(e.participant.userId)) return;
262 | console.log('participantjoined', e);
263 |
264 | e.participant.addEventListener('left', this.participantleft.bind(this, e.participant))
265 |
266 | this.participants = this.conference.info.participants.filter(p => !/robot/.test(p.userId));
267 | console.log(this.conference.info);
268 | var audio = new Audio('audio/some_one_join_room.wav'); // path to file
269 | audio.play();
270 | audio = null;
271 | this.showMessage(null, { type: 'msg_text', content: `成员
${e.participant.userId}
加入频道
` });
272 | },
273 | participantleft: function (participant, e) {
274 | console.log('participantleft', e);
275 |
276 | this.participants = this.conference.info.participants.filter(p => !/robot/.test(p.userId));
277 | var audio = new Audio('audio/some_one_join_room.wav'); // path to file
278 | audio.play();
279 | audio = null;
280 | this.showMessage(null, { type: 'msg_text', content: `成员
${participant.userId}
离开频道
` });
281 | },
282 | messagereceived: function (e) {
283 |
284 | console.log(e);
285 | if (e.origin == this.myId) return;
286 | if (e.message.type == 'msg_text' || e.message.type == 'msg_emoji') {
287 | this.showMessage(this.getUserIdFromOrigin(e.origin), e.message);
288 | }
289 | },
290 | showMessage: function (userId, message) {
291 | const duration = 8888;
292 | const offset = 94;
293 | const spacing = 2;
294 | message.type == 'msg_text' && this.$notify({
295 | customClass: 'notify_msg',
296 | message: `${userId ? ('' + userId + '
') : ''} ${message.content}
`,
297 | position: 'bottom-left',
298 | duration: duration,
299 | dangerouslyUseHTMLString: true,
300 | showClose: false,
301 | offset: offset,
302 | spacing: spacing,
303 | insertHead: true
304 | });
305 | message.type == 'msg_emoji' && this.$notify({
306 | customClass: 'notify_msg emoji',
307 | message: `${userId ? ('' + userId + '
') : ''}
`,
308 | position: 'bottom-left',
309 | duration: duration,
310 | dangerouslyUseHTMLString: true,
311 | showClose: false,
312 | offset: offset,
313 | spacing: spacing,
314 | insertHead: true
315 | });
316 | message.type == 'msg_html' && this.$notify({
317 | customClass: 'notify_msg emoji',
318 | message: `${userId ? ('' + userId + '
') : ''} ${message.content}`,
319 | position: 'bottom-left',
320 | duration: duration,
321 | dangerouslyUseHTMLString: true,
322 | showClose: false,
323 | offset: offset,
324 | spacing: spacing,
325 | insertHead: true
326 | });
327 | },
328 | serverdisconnected: function (e) {
329 | console.log('serverdisconnected', e)
330 | this._exitRoom();
331 | },
332 | streamAdded: function (e) {
333 | const that = this;
334 | const stream = e.stream;
335 | console.log('A new stream is added ', stream.id);
336 | if (!that.conference) return;
337 | that.remoteStreams = that.conference.info.remoteStreams.filter(r => r.source && r.source.video && r.source.video != 'mixed');
338 | if (stream.origin !== that.myId && stream.source
339 | && stream.source.video
340 | && stream.source.video == 'screen-cast') {
341 | that.screenSharingUser = that.getStreamUserId(stream);
342 | // that.subscribeStream(stream, (subscription) => {
343 | // that.showScreenStream(stream, subscription);
344 | // });
345 | that.switchVideoParam(true);
346 | }
347 | stream.addEventListener('ended', that.streamEnded.bind(that, stream));
348 | },
349 | streamEnded: function (stream) {
350 | console.log(stream.id + ' is ended.');
351 | const that = this;
352 | if (!that.conference) return;
353 | stream.source && stream.source.video == 'screen-cast' && (that.screenSharingUser = '',that.subscriptionGlobal && that.subscriptionGlobal.applyOptions({video:{frameRate:24}}))
354 | that.remoteStreams = that.conference.info.remoteStreams.filter(r => r.source && r.source.video && r.source.video != 'mixed');
355 | },
356 | switchVideoParam:function(isScreenShared, bitrateMultiplier){
357 |
358 | const that = this;
359 | const frameRateOrigin = that.mixStreamGlobal ? that.mixStreamGlobal.settings.video[0].frameRate:24;
360 | that.mixStreamGlobal && that.subscriptionGlobal && that.subscriptionGlobal.applyOptions({
361 | video:{
362 | frameRate:isScreenShared?6:frameRateOrigin,
363 | bitrateMultiplier:0.9,
364 | resolution:isScreenShared?{width:1920,height:1080}:{width:1280,height:720}
365 | }})
366 | },
367 | checkDevices: async function () {
368 | const that = this;
369 | let videoConstraints = new Owt.Base.VideoTrackConstraints(Owt.Base.VideoSourceInfo.CAMERA);
370 | let audioConstraints = [new Owt.Base.AudioTrackConstraints(Owt.Base.AudioSourceInfo.MIC), false];
371 | let resolutions = [{ width: 1280, height: 720 },{ width: 720, height: 1280 },undefined, false];
372 | let mediaStream;
373 | for (const audioConstraint of audioConstraints) {
374 | for (const resolution of resolutions) {
375 | try {
376 | if (resolution === false) {
377 | videoConstraints = false;
378 | }
379 | else {
380 | videoConstraints.resolution = resolution;
381 | if (resolution == undefined) videoConstraints = new Owt.Base.VideoTrackConstraints(Owt.Base.VideoSourceInfo.CAMERA);
382 | }
383 | mediaStream = await Owt.Base.MediaStreamFactory.createMediaStream(new Owt.Base.StreamConstraints(
384 | audioConstraint, videoConstraints));
385 | break;
386 | } catch (error) {
387 | mediaStream = null;
388 | console.error(error);
389 | }
390 | }
391 | if (mediaStream) break;
392 | }
393 | if (!mediaStream) return;
394 | let vT = mediaStream.getVideoTracks();
395 | let aT = mediaStream.getAudioTracks();
396 | let videoTrack, audioTrack;
397 | vT && vT.length && (videoTrack = vT[0]) &&
398 | (that.usedCamera = videoTrack.label.replace(/ ?\([\w:]{9}\)/, ''))
399 | aT && aT.length && (audioTrack = aT[0]) &&
400 | (that.usedMicrophone = audioTrack.label.replace(/ ?\([\w:]{9}\)/, ''))
401 |
402 | try {
403 | let devices = await navigator.mediaDevices.enumerateDevices();
404 | let vDevices = devices.filter(d => d.kind && d.kind == 'videoinput');
405 | if (vDevices.length >= 2 && videoTrack) {
406 | vDevices = vDevices.filter(d => d.label != videoTrack.label);
407 | vDevices && vDevices.length && (that.usedCamera2 = vDevices[0].label.replace(/ ?\([\w:]{9}\)/, ''), that.usedCamera2DeviceId = vDevices[0].deviceId)
408 | }
409 | else {
410 | that.usedCamera2 = '';
411 | that.usedCamera2DeviceId = '';
412 | }
413 | } catch (error) {
414 | console.error(error);
415 | }
416 | that._destroyMediaStream(mediaStream);
417 | mediaStream = null;
418 | },
419 | publishVideo: async function () {
420 | const that = this;
421 | let audioConstraints = [new Owt.Base.AudioTrackConstraints(Owt.Base.AudioSourceInfo.MIC), false];
422 | let facingModes = [{exact:"user"},undefined];
423 | let resolutions = [{ width: 1280, height: 720 }, { width: 640, height: 360 }, undefined, false];
424 | screen && screen.orientation && screen.orientation.type && screen.orientation.type.indexOf('landscape') == -1 &&
425 | resolutions.unshift({ width: 720, height: 1280 },{ width: 360, height: 640 });
426 | (!screen || !screen.orientation ) && document.body.clientWidth < document.body.clientHeight &&
427 | resolutions.unshift({ width: 720, height: 1280 },{ width: 360, height: 640 });
428 | let mediaStream;
429 | for (const audioConstraint of audioConstraints) {
430 |
431 | for(const facingMode of facingModes){
432 |
433 | for (const resolution of resolutions) {
434 | try {
435 | let videoConstraints = new Owt.Base.VideoTrackConstraints(Owt.Base.VideoSourceInfo.CAMERA);
436 | facingMode && (videoConstraints.facingMode = facingMode);
437 |
438 | if (resolution === false) {
439 | videoConstraints = false;
440 | }
441 | else {
442 | videoConstraints.resolution = resolution;
443 | if (resolution == undefined) videoConstraints = new Owt.Base.VideoTrackConstraints(Owt.Base.VideoSourceInfo.CAMERA);
444 | }
445 | mediaStream = await Owt.Base.MediaStreamFactory.createMediaStream(new Owt.Base.StreamConstraints(
446 | audioConstraint, videoConstraints));
447 | break;
448 | } catch (error) {
449 | mediaStream = null;
450 | console.error(error);
451 | }
452 | }
453 | if (mediaStream) break;
454 | }
455 | if (mediaStream) break;
456 | }
457 | if (!mediaStream) return;
458 | let vT = mediaStream.getVideoTracks();
459 | let aT = mediaStream.getAudioTracks();
460 | let videoTrack, audioTrack;
461 | vT && vT.length && (videoTrack = vT[0]) &&
462 | (that.usedCamera = videoTrack.label.replace(/ ?\([\w:]{9}\)/, ''))
463 | aT && aT.length && (audioTrack = aT[0]) &&
464 | (that.usedMicrophone = audioTrack.label.replace(/ ?\([\w:]{9}\)/, ''))
465 |
466 | let devices = await navigator.mediaDevices.enumerateDevices();
467 | let vDevices = devices.filter(d => d.kind && d.kind == 'videoinput');
468 | if (vDevices.length >= 2 && videoTrack) {
469 | vDevices = vDevices.filter(d => d.label != videoTrack.label);
470 | vDevices && vDevices.length && (that.usedCamera2 = vDevices[0].label.replace(/ ?\([\w:]{9}\)/, ''), that.usedCamera2DeviceId = vDevices[0].deviceId)
471 | }
472 | else {
473 | that.usedCamera2 = '';
474 | that.usedCamera2DeviceId = '';
475 | }
476 |
477 | audioTrack && (audioTrack.enabled = that.enableAudio);
478 | videoTrack && (videoTrack.enabled = that.enableVideo);
479 | try{
480 | await videoTrack.applyConstraints({frameRate:{max:15}})
481 | }
482 | catch(err){
483 | console.log("applyConstraints")
484 | console.erroe(err)
485 | }
486 | that.isCameraMuted = videoTrack ? !videoTrack.enabled : true;
487 | that.isMicMuted = audioTrack ? !audioTrack.enabled : true;
488 |
489 | that.localStream = new Owt.Base.LocalStream(mediaStream, new Owt.Base.StreamSourceInfo('mic', 'camera'));
490 | try {
491 | that.publicationGlobal = await that.conference.publish(that.localStream, { video: [{ codec: { name: 'h264', profile: 'CB' }, maxBitrate: 1024 }] });
492 | } catch (error) {
493 | that.publicationGlobal = null;
494 | console.error(error);
495 | that._clearLocalCamera();
496 | }
497 | if (!that.publicationGlobal)
498 | return;
499 | mixStream(that.myRoomId, that.publicationGlobal.id, ['common']);
500 | that.publicationGlobal.addEventListener('error', that._clearLocalCamera.bind(that));
501 | that.publicationGlobal.addEventListener('ended', that._clearLocalCamera.bind(that));
502 | },
503 | publishVideoSecond: async function () {
504 | const that = this;
505 | let videoConstraints = new Owt.Base.VideoTrackConstraints(Owt.Base.VideoSourceInfo.CAMERA);
506 | let resolutions = [{ width: 1280, height: 720 }, { width: 1280, height: 720 }, { width: 640, height: 360 }, undefined];
507 | screen && screen.orientation && screen.orientation.type && screen.orientation.type.indexOf('landscape') == -1 &&
508 | resolutions.unshift({ width: 720, height: 1280 },{ width: 360, height: 640 });
509 |
510 | let mediaStream;
511 | videoConstraints.deviceId = that.usedCamera2DeviceId;
512 | for (const resolution of resolutions) {
513 | try {
514 | videoConstraints.resolution = resolution;
515 | if(resolution == undefined) videoConstraints = new Owt.Base.VideoTrackConstraints(Owt.Base.VideoSourceInfo.CAMERA);
516 |
517 | mediaStream = await Owt.Base.MediaStreamFactory.createMediaStream(new Owt.Base.StreamConstraints(
518 | false, videoConstraints));
519 | break;
520 | } catch (error) {
521 | mediaStream = null;
522 | console.error(error);
523 | }
524 | }
525 | if (!mediaStream) return;
526 |
527 | let vT = mediaStream.getVideoTracks();
528 | let videoTrack;
529 | vT && vT.length && (videoTrack =vT[0] )
530 | try{
531 | await videoTrack.applyConstraints({frameRate:{exact:15,ideal:15}})
532 | }
533 | catch(err){
534 | console.log("applyConstraints")
535 | console.erroe(err)
536 | }
537 |
538 | that.localStreamSecond = new Owt.Base.LocalStream(mediaStream, new Owt.Base.StreamSourceInfo('mic', 'camera'));
539 | try {
540 | that.publicationGlobalSecond = await that.conference.publish(that.localStreamSecond, { video: [{ codec: { name: 'h264', profile: 'CB' }, maxBitrate: 1024 }], audio: false });
541 | that.isCamera2Muted = false;
542 | } catch (error) {
543 | that.publicationGlobalSecond = null;
544 | console.error(error);
545 | that._clearLocalCameraSecond();
546 | }
547 | if (!that.publicationGlobalSecond)
548 | return;
549 | mixStream(that.myRoomId, that.publicationGlobalSecond.id, ['common']);
550 | that.publicationGlobalSecond.addEventListener('error', that._clearLocalCameraSecond.bind(that));
551 | that.publicationGlobalSecond.addEventListener('ended', that._clearLocalCameraSecond.bind(that));
552 | },
553 | _clearLocalCamera: function () {
554 | const that = this;
555 | that.localStream && that.localStream.mediaStream && that._destroyMediaStream(that.localStream.mediaStream), (that.localStream = null);
556 |
557 | that.isCameraMuted = that.isMicMuted = true;
558 | },
559 | _clearLocalCameraSecond: function () {
560 | const that = this;
561 | that.localStreamSecond && that.localStreamSecond.mediaStream && that._destroyMediaStream(that.localStreamSecond.mediaStream), (that.localStreamSecond = null);
562 |
563 | that.isCamera2Muted = true;
564 | },
565 | _startShareScreen: async function (id) {
566 | const that = this;
567 | let mediaStream;
568 | try{
569 | mediaStream = await navigator.mediaDevices.getUserMedia({
570 | audio: false,
571 | video: {
572 | mandatory: {
573 | chromeMediaSource: 'screen',
574 | chromeMediaSourceId: id,
575 | maxWidth: 1920
576 | }
577 | }
578 | });
579 | }
580 | catch(err){
581 | console.log("applyConstraints")
582 | console.error(err)
583 | return;
584 | }
585 |
586 | let vT = mediaStream.getVideoTracks();
587 | let videoTrack;
588 | vT && vT.length && (videoTrack =vT[0] )
589 | try{
590 | await videoTrack.applyConstraints({frameRate:{max:5}})
591 | }
592 | catch(err){
593 | console.log("applyConstraints")
594 | console.erroe(err)
595 | }
596 |
597 | that.screen_select_visible = false;
598 |
599 | let publishOption = { video: [{ codec: { name: 'h264', profile: 'CB' }, maxBitrate: 2500 }] };
600 | that.ScreenStream = new Owt.Base.LocalStream(mediaStream, new Owt.Base.StreamSourceInfo('screen-cast', 'screen-cast'));
601 | that.conference.publish(that.ScreenStream, publishOption).then(publication => {
602 | that.publicationScreenGlobal = publication;
603 |
604 | that.screenSharingUser = "我";
605 | that.isDesktopShared = true;
606 |
607 | mixStream(that.myRoomId, publication.id, ['common'],()=>{
608 |
609 | const remoteStreams = this.conference.info.remoteStreams.filter(r => r.source && r.source.video && r.source.video == 'mixed');
610 | remoteStreams.forEach(remoteStream => {
611 | activeLayoutStream(that.myRoomId, remoteStream.id, publication.id);
612 | })
613 | });
614 | publication.addEventListener('error', that._clearScreenShare.bind(that));
615 | publication.addEventListener('ended', that._clearScreenShare.bind(that));
616 |
617 | that.switchVideoParam(true);
618 | // that.showScreenStream(that.ScreenStream);
619 |
620 | }, err => {
621 | that._clearScreenShare();
622 | })
623 | },
624 | startShareScreen: function (e) {
625 | this._startShareScreen(e.target.getAttribute('data'));
626 | },
627 | showScreenStream: function (stream, subscription) {
628 | if (!stream.mediaStream) return;
629 | const that = this;
630 | const { webContentsId } = ipcRenderer.sendSync('create-video-windows');
631 | ipcRenderer.on('win-onload', this._onLoadWindow.bind(this, webContentsId, stream, subscription));
632 | },
633 | _onLoadWindow: async function (webContentsId, stream, subscription, e) {
634 | if (e.senderId != webContentsId) return;
635 | const pc = new RTCPeerConnection();
636 | stream.mediaStream.getTracks().forEach(track => pc.addTransceiver(track, { streams: [stream.mediaStream], direction: 'sendonly' }));
637 | stream.addEventListener('ended', this.showStreamEnded.bind(this, pc, webContentsId, subscription));
638 | pc.onicecandidate = function ({ candidate }) {
639 | candidate && ipcRenderer.sendTo(webContentsId, 'set-peer-param', { candidate: candidate.toJSON() });
640 | }
641 | pc.onnegotiationneeded = async () => {
642 | await pc.setLocalDescription();
643 | ipcRenderer.sendTo(webContentsId, 'set-peer-param', { localDescription: pc.localDescription.toJSON() });
644 | }
645 | ipcRenderer.on('set-peer-param', this._setPeerParam.bind(this, pc, webContentsId, subscription));
646 | },
647 | _setPeerParam: async function (peerConnection, webContentsId, subscription, e, { localDescription, candidate, close }) {
648 | if (e.senderId != webContentsId) return;
649 | peerConnection && localDescription && await peerConnection.setRemoteDescription(localDescription);
650 | peerConnection && candidate && await peerConnection.addIceCandidate(candidate);
651 | close && subscription && subscription.stop();
652 | },
653 | showStreamEnded: function (pc, webContentsId, subscription) {
654 | try {
655 | console.log("showStreamEnded", pc);
656 | pc && pc.close();
657 | } catch (error) {
658 |
659 | }
660 | try {
661 | subscription && subscription.stop();
662 | } catch (error) {
663 |
664 | }
665 | ipcRenderer.sendTo(webContentsId, 'stream_ended');
666 | },
667 | _clearScreenShare: function () {
668 | const that = this;
669 | that.ScreenStream && that.ScreenStream.mediaStream && (that.ScreenStream.dispatchEvent({ type: 'ended' }), that._destroyMediaStream(that.ScreenStream.mediaStream)), (that.ScreenStream = null);
670 | try {
671 | that.publicationScreenGlobal && that.publicationScreenGlobal.stop();
672 | } catch (error) {
673 | console.error(error)
674 | }
675 | finally {
676 | that.publicationScreenGlobal = null;
677 | }
678 | that.screenSharingUser = '';
679 | that.isDesktopShared = false;
680 | that.parentWebContentsId >= 0 && ipcRenderer.sendTo(that.parentWebContentsId, 'message', { isDesktopShared: false });
681 | that.switchVideoParam(false);
682 | },
683 | _getStat: async function () {
684 | const that = this;
685 | let bytesSent = 0;
686 | let bytesReceived = 0;
687 | let stats;
688 |
689 | function statForEach(stat) {
690 | /^RTCIceCandidatePair/.test(stat['id']) && stat['bytesSent'] && (bytesSent = bytesSent + stat['bytesSent']);
691 | /^RTCIceCandidatePair/.test(stat['id']) && stat['bytesReceived'] && (bytesReceived = bytesReceived + stat['bytesReceived']);
692 | }
693 |
694 | that.subscriptionGlobal && (stats = await that.subscriptionGlobal.getStats());
695 | stats && (stats.forEach(statForEach),stats = null);
696 | that.publicationGlobal && (stats = await that.publicationGlobal.getStats());
697 | stats && (stats.forEach(statForEach),stats = null);
698 | that.publicationGlobalSecond && (stats = await that.publicationGlobalSecond.getStats());
699 | stats && (stats.forEach(statForEach),stats = null);
700 | that.publicationScreenGlobal && (stats = await that.publicationScreenGlobal.getStats());
701 | stats && (stats.forEach(statForEach),stats = null);
702 |
703 | if (that.bytesReceivedGlobal && bytesReceived > that.bytesReceivedGlobal) {
704 | that.statDownload = Math.round((bytesReceived - that.bytesReceivedGlobal) / 1024);
705 | }
706 | if (that.bytesSentGlobal && bytesSent > that.bytesSentGlobal) {
707 | that.statUpload = Math.round((bytesSent - that.bytesSentGlobal) / 1024);
708 | }
709 | that.bytesReceivedGlobal = bytesReceived;
710 | that.bytesSentGlobal = bytesSent;
711 | },
712 | selectStreamChange: function(){
713 | if(!this.selectStreamId) return;
714 | this.clickChangeLayout(`activeLayout-${this.selectStreamId}`);
715 | },
716 | clickChangeLayout: function (e) {
717 | const that = this;
718 | console.log(e);
719 | if (e.indexOf('activeLayout') == 0)
720 | {
721 | const subStream = e.substr('activeLayout-'.length);
722 | const remoteStreams = this.conference.info.remoteStreams.filter(r => r.source && r.source.video && r.source.video == 'mixed');
723 | remoteStreams.forEach(remoteStream => {
724 | activeLayoutStream(that.myRoomId, remoteStream.id, subStream);
725 | })
726 | return;
727 | }
728 | if (e.indexOf('switchLayout') != 0) return;
729 | if (!this.subscriptionGlobal) return;
730 | const layouts = [
731 | [{"region":[{"id":"1","area":{"height":"1","width":"1","top":"0","left":"0"},"shape":"rectangle"}]},{"region":[{"id":"1","area":{"height":"1","width":"1","top":"0","left":"0"},"shape":"rectangle"},{"id":"2","area":{"height":"214/1080","width":"386/1920","top":"8/10","left":"8/10"},"shape":"rectangle"},{"id":"3","area":{"height":"214/1080","width":"386/1920","top":"6/10","left":"8/10"},"shape":"rectangle"},{"id":"4","area":{"height":"214/1080","width":"386/1920","top":"4/10","left":"8/10"},"shape":"rectangle"},{"id":"5","area":{"height":"214/1080","width":"386/1920","top":"2/10","left":"8/10"},"shape":"rectangle"},{"id":"6","area":{"height":"214/1080","width":"386/1920","top":"0","left":"8/10"},"shape":"rectangle"}]},{"region":[{"id":"1","area":{"height":"8/10","width":"8/10","top":"0","left":"0"},"shape":"rectangle"},{"id":"2","area":{"height":"214/1080","width":"386/1920","top":"8/10","left":"8/10"},"shape":"rectangle"},{"id":"3","area":{"height":"214/1080","width":"386/1920","top":"6/10","left":"8/10"},"shape":"rectangle"},{"id":"4","area":{"height":"214/1080","width":"386/1920","top":"4/10","left":"8/10"},"shape":"rectangle"},{"id":"5","area":{"height":"214/1080","width":"386/1920","top":"2/10","left":"8/10"},"shape":"rectangle"},{"id":"6","area":{"height":"214/1080","width":"386/1920","top":"0","left":"8/10"},"shape":"rectangle"},{"id":"7","area":{"height":"214/1080","width":"386/1920","top":"8/10","left":"6/10"},"shape":"rectangle"},{"id":"8","area":{"height":"214/1080","width":"386/1920","top":"8/10","left":"4/10"},"shape":"rectangle"},{"id":"9","area":{"height":"214/1080","width":"386/1920","top":"8/10","left":"2/10"},"shape":"rectangle"},{"id":"10","area":{"height":"214/1080","width":"386/1920","top":"8/10","left":"0"},"shape":"rectangle"}]}],
732 | [{ "region": [{ "id": "1", "area": { "height": "1", "width": "1", "top": "0", "left": "0" }, "shape": "rectangle" }] }, { "region": [{ "id": "1", "area": { "height": "1", "width": "959/1920", "top": "0", "left": "0" }, "shape": "rectangle" }, { "id": "2", "area": { "height": "1", "width": "959/1920", "top": "0", "left": "961/1920" }, "shape": "rectangle" }] }, { "region": [{ "id": "1", "area": { "height": "1", "width": "959/1920", "top": "0", "left": "0" }, "shape": "rectangle" }, { "id": "2", "area": { "height": "959/1920", "width": "959/1920", "top": "0", "left": "961/1920" }, "shape": "rectangle" }, { "id": "3", "area": { "height": "539/1080", "width": "959/1920", "top": "541/1080", "left": "961/1920" }, "shape": "rectangle" }] }, { "region": [{ "id": "1", "area": { "height": "539/1080", "width": "959/1920", "top": "0", "left": "0" }, "shape": "rectangle" }, { "id": "2", "area": { "height": "539/1080", "width": "959/1920", "top": "0", "left": "961/1920" }, "shape": "rectangle" }, { "id": "3", "area": { "height": "538/1080", "width": "959/1920", "top": "541/1080", "left": "0" }, "shape": "rectangle" }, { "id": "4", "area": { "height": "539/1080", "width": "959/1920", "top": "541/1080", "left": "961/1920" }, "shape": "rectangle" }] }, { "region": [{ "id": "1", "area": { "height": "539/1080", "width": "959/1920", "top": "0", "left": "0" }, "shape": "rectangle" }, { "id": "2", "area": { "height": "539/1080", "width": "959/1920", "top": "0", "left": "961/1920" }, "shape": "rectangle" }, { "id": "3", "area": { "height": "538/1080", "width": "639/1920", "top": "541/1080", "left": "0" }, "shape": "rectangle" }, { "id": "4", "area": { "height": "539/1080", "width": "639/1920", "top": "541/1080", "left": "641/1920" }, "shape": "rectangle" }, { "id": "5", "area": { "height": "539/1080", "width": "639/1920", "top": "541/1080", "left": "1281/1920" }, "shape": "rectangle" }] }, { "region": [{ "id": "1", "area": { "height": "539/1080", "width": "639/1920", "top": "0", "left": "0" }, "shape": "rectangle" }, { "id": "2", "area": { "height": "539/1080", "width": "638/1920", "top": "0", "left": "641/1920" }, "shape": "rectangle" }, { "id": "3", "area": { "height": "539/1080", "width": "639/1920", "top": "0", "left": "1281/1920" }, "shape": "rectangle" }, { "id": "4", "area": { "height": "539/1080", "width": "639/1920", "top": "541/1080", "left": "0" }, "shape": "rectangle" }, { "id": "5", "area": { "height": "539/1080", "width": "638/1920", "top": "541/1080", "left": "641/1920" }, "shape": "rectangle" }, { "id": "6", "area": { "height": "539/1080", "width": "639/1920", "top": "541/1080", "left": "1281/1920" }, "shape": "rectangle" }] }, { "region": [{ "id": "1", "area": { "height": "359/1080", "width": "639/1920", "top": "0", "left": "0" }, "shape": "rectangle" }, { "id": "2", "area": { "height": "359/1080", "width": "638/1920", "top": "0", "left": "641/1920" }, "shape": "rectangle" }, { "id": "3", "area": { "height": "359/1080", "width": "639/1920", "top": "0", "left": "1281/1920" }, "shape": "rectangle" }, { "id": "4", "area": { "height": "358/1080", "width": "639/1920", "top": "361/1080", "left": "0" }, "shape": "rectangle" }, { "id": "5", "area": { "height": "358/1080", "width": "638/1920", "top": "361/1080", "left": "641/1920" }, "shape": "rectangle" }, { "id": "6", "area": { "height": "358/1080", "width": "639/1920", "top": "361/1080", "left": "1281/1920" }, "shape": "rectangle" }, { "id": "7", "area": { "height": "359/1080", "width": "639/1920", "top": "721/1080", "left": "0" }, "shape": "rectangle" }, { "id": "8", "area": { "height": "359/1080", "width": "638/1920", "top": "721/1080", "left": "641/1920" }, "shape": "rectangle" }, { "id": "9", "area": { "height": "359/1080", "width": "639/1920", "top": "721/1080", "left": "1281/1920" }, "shape": "rectangle" }] }],
733 | [{"region":[{"id":"1","area":{"height":"1","width":"1","top":"0","left":"0"},"shape":"rectangle"}]},{"region":[{"id":"1","area":{"height":"971/1080","width":"1","top":"109/1080","left":"0"},"shape":"rectangle"},{"id":"2","area":{"height":"108/1080","width":"190/1920","top":"0","left":"864/1920"},"shape":"rectangle"}]},{"region":[{"id":"1","area":{"height":"971/1080","width":"1","top":"109/1080","left":"0"},"shape":"rectangle"},{"id":"2","area":{"height":"108/1080","width":"190/1920","top":"0","left":"767/1920"},"shape":"rectangle"},{"id":"3","area":{"height":"108/1080","width":"190/1920","top":"0","left":"961/1920"},"shape":"rectangle"}]},{"region":[{"id":"1","area":{"height":"971/1080","width":"1","top":"109/1080","left":"0"},"shape":"rectangle"},{"id":"2","area":{"height":"108/1080","width":"190/1920","top":"0","left":"864/1920"},"shape":"rectangle"},{"id":"3","area":{"height":"108/1080","width":"190/1920","top":"0","left":"1057/1920"},"shape":"rectangle"},{"id":"4","area":{"height":"108/1080","width":"190/1920","top":"0","left":"671/1920"},"shape":"rectangle"}]},{"region":[{"id":"1","area":{"height":"971/1080","width":"1","top":"109/1080","left":"0"},"shape":"rectangle"},{"id":"2","area":{"height":"108/1080","width":"190/1920","top":"0","left":"767/1920"},"shape":"rectangle"},{"id":"3","area":{"height":"108/1080","width":"190/1920","top":"0","left":"961/1920"},"shape":"rectangle"},{"id":"4","area":{"height":"108/1080","width":"190/1920","top":"0","left":"576/1920"},"shape":"rectangle"},{"id":"5","area":{"height":"108/1080","width":"190/1920","top":"0","left":"1154/1920"},"shape":"rectangle"}]},{"region":[{"id":"1","area":{"height":"971/1080","width":"1","top":"109/1080","left":"0"},"shape":"rectangle"},{"id":"2","area":{"height":"108/1080","width":"190/1920","top":"0","left":"864/1920"},"shape":"rectangle"},{"id":"3","area":{"height":"108/1080","width":"190/1920","top":"0","left":"1057/1920"},"shape":"rectangle"},{"id":"4","area":{"height":"108/1080","width":"190/1920","top":"0","left":"671/1920"},"shape":"rectangle"},{"id":"5","area":{"height":"108/1080","width":"190/1920","top":"0","left":"1250/1920"},"shape":"rectangle"},{"id":"6","area":{"height":"108/1080","width":"190/1920","top":"0","left":"478/1920"},"shape":"rectangle"}]},{"region":[{"id":"1","area":{"height":"971/1080","width":"1","top":"109/1080","left":"0"},"shape":"rectangle"},{"id":"2","area":{"height":"108/1080","width":"190/1920","top":"0","left":"767/1920"},"shape":"rectangle"},{"id":"3","area":{"height":"108/1080","width":"190/1920","top":"0","left":"961/1920"},"shape":"rectangle"},{"id":"4","area":{"height":"108/1080","width":"190/1920","top":"0","left":"576/1920"},"shape":"rectangle"},{"id":"5","area":{"height":"108/1080","width":"190/1920","top":"0","left":"1154/1920"},"shape":"rectangle"},{"id":"6","area":{"height":"108/1080","width":"190/1920","top":"0","left":"383/1920"},"shape":"rectangle"},{"id":"7","area":{"height":"108/1080","width":"190/1920","top":"0","left":"1347/1920"},"shape":"rectangle"}]},{"region":[{"id":"1","area":{"height":"971/1080","width":"1","top":"109/1080","left":"0"},"shape":"rectangle"},{"id":"2","area":{"height":"108/1080","width":"190/1920","top":"0","left":"864/1920"},"shape":"rectangle"},{"id":"3","area":{"height":"108/1080","width":"190/1920","top":"0","left":"1057/1920"},"shape":"rectangle"},{"id":"4","area":{"height":"108/1080","width":"190/1920","top":"0","left":"671/1920"},"shape":"rectangle"},{"id":"5","area":{"height":"108/1080","width":"190/1920","top":"0","left":"1250/1920"},"shape":"rectangle"},{"id":"6","area":{"height":"108/1080","width":"190/1920","top":"0","left":"478/1920"},"shape":"rectangle"},{"id":"7","area":{"height":"108/1080","width":"190/1920","top":"0","left":"1443/1920"},"shape":"rectangle"},{"id":"8","area":{"height":"108/1080","width":"190/1920","top":"0","left":"285/1920"},"shape":"rectangle"}]},{"region":[{"id":"1","area":{"height":"971/1080","width":"1","top":"109/1080","left":"0"},"shape":"rectangle"},{"id":"2","area":{"height":"108/1080","width":"190/1920","top":"0","left":"767/1920"},"shape":"rectangle"},{"id":"3","area":{"height":"108/1080","width":"190/1920","top":"0","left":"961/1920"},"shape":"rectangle"},{"id":"4","area":{"height":"108/1080","width":"190/1920","top":"0","left":"576/1920"},"shape":"rectangle"},{"id":"5","area":{"height":"108/1080","width":"190/1920","top":"0","left":"1154/1920"},"shape":"rectangle"},{"id":"6","area":{"height":"108/1080","width":"190/1920","top":"0","left":"383/1920"},"shape":"rectangle"},{"id":"7","area":{"height":"108/1080","width":"190/1920","top":"0","left":"1347/1920"},"shape":"rectangle"},{"id":"8","area":{"height":"108/1080","width":"190/1920","top":"0","left":"190/1920"},"shape":"rectangle"},{"id":"9","area":{"height":"108/1080","width":"190/1920","top":"0","left":"1540/1920"},"shape":"rectangle"}]}],
734 | [{"region":[{"id":"1","area":{"height":"1","width":"1","top":"0","left":"0"},"shape":"rectangle"}]},{"region":[{"id":"1","area":{"height":"1","width":"1","top":"0","left":"0"},"shape":"rectangle"},{"id":"2","area":{"height":"0","width":"0","top":"0","left":"0"},"shape":"rectangle"},{"id":"3","area":{"height":"0","width":"0","top":"0","left":"0"},"shape":"rectangle"},{"id":"4","area":{"height":"0","width":"0","top":"0","left":"0"},"shape":"rectangle"},{"id":"5","area":{"height":"0","width":"0","top":"0","left":"0"},"shape":"rectangle"},{"id":"6","area":{"height":"0","width":"0","top":"0","left":"0"},"shape":"rectangle"},{"id":"7","area":{"height":"0","width":"0","top":"0","left":"0"},"shape":"rectangle"},{"id":"8","area":{"height":"0","width":"0","top":"0","left":"0"},"shape":"rectangle"},{"id":"9","area":{"height":"0","width":"0","top":"0","left":"0"},"shape":"rectangle"},{"id":"10","area":{"height":"0","width":"0","top":"0","left":"0"},"shape":"rectangle"},{"id":"11","area":{"height":"0","width":"0","top":"0","left":"0"},"shape":"rectangle"},{"id":"12","area":{"height":"0","width":"0","top":"0","left":"0"},"shape":"rectangle"},{"id":"13","area":{"height":"0","width":"0","top":"0","left":"0"},"shape":"rectangle"},{"id":"14","area":{"height":"0","width":"0","top":"0","left":"0"},"shape":"rectangle"},{"id":"15","area":{"height":"0","width":"0","top":"0","left":"0"},"shape":"rectangle"},{"id":"16","area":{"height":"0","width":"0","top":"0","left":"0"},"shape":"rectangle"}]}],
735 | ];
736 | this.layoutIndex = Number.parseInt(e.substr(-1,1));
737 |
738 | let values = [];
739 | layouts[this.layoutIndex].forEach(__ => {
740 | let regions = __["region"];
741 | let _ = [];
742 | regions.forEach(region => {
743 | _.push({ region: region });
744 | }),values.push(_);
745 | });
746 | setLayoutStream(this.myRoomId, this.mixStreamGlobal.id, values);
747 | },
748 | clickSpeak: function (e) {
749 | this.isSpeakMuted = !this.isSpeakMuted;
750 | },
751 | clickMicrophone: function (e) {
752 | let tracks, track;
753 | if (!this.localStream || !this.localStream.mediaStream) {
754 | this.isMicMuted = true;
755 | this.enableAudio = true;
756 | this.enableVideo = false;
757 | this.publishVideo();
758 | return;
759 | }
760 | tracks = this.localStream.mediaStream.getAudioTracks();
761 | tracks && tracks.length && (track = tracks[0]);
762 | this.isMicMuted = track ? !this.isMicMuted : true;
763 | track && (track.enabled = !this.isMicMuted);
764 | },
765 | clickCamera: function (e) {
766 | let tracks, track;
767 | if (!this.localStream || !this.localStream.mediaStream) {
768 | this.isCameraMuted = true;
769 | this.enableAudio = false;
770 | this.enableVideo = true;
771 | this.publishVideo();
772 | return;
773 | }
774 | tracks = this.localStream.mediaStream.getVideoTracks();
775 | tracks && tracks.length && (track = tracks[0]);
776 | this.isCameraMuted = track ? !this.isCameraMuted : true;
777 | track && (track.enabled = !this.isCameraMuted);
778 | },
779 | clickCamera2: function (e) {
780 | if (!this.publicationGlobalSecond || !this.localStreamSecond || !this.localStreamSecond.mediaStream) {
781 | this.isCamera2Muted = true;
782 | this.publishVideoSecond();
783 | return;
784 | }
785 | this._clearLocalCameraSecond();
786 | this.publicationGlobalSecond && this.publicationGlobalSecond.stop(), this.publicationGlobalSecond = null;
787 | this.isCamera2Muted = true;
788 | },
789 | clickDesktop: function (e) {
790 | const that = this;
791 | if (that.publicationScreenGlobal) {
792 | that._clearScreenShare();
793 | return;
794 | }
795 | if (that.screenSharingUser != '') {
796 | that.$alert(`[${that.screenSharingUser}]`+'已经有人在分享桌面了,您当前不能再进行分享操作', '提示', { confirmButtonText: '确定' });
797 | return
798 | }
799 | that.screen_data = [];
800 | desktopCapturer.getSources({ types: ['screen'] }).then(sources => sources.forEach(source => that.screen_data.push({ id: source.id, src: source.thumbnail.toDataURL() })));
801 | that.screen_select_visible = true;
802 | },
803 | clickRecord: function (e) {
804 |
805 | },
806 | _exitRoom: function (e) {
807 | const that = this;
808 | try {
809 | that.publicationGlobal && that.publicationGlobal.stop(), that.publicationGlobal = null;
810 | that.publicationGlobalSecond && that.publicationGlobalSecond.stop(), that.publicationGlobalSecond = null;
811 | that.subscriptionGlobal && that.subscriptionGlobal.stop(), that.subscriptionGlobal = null;
812 | that.publicationScreenGlobal && that.publicationScreenGlobal.stop(), that.publicationScreenGlobal = null;
813 | } catch (_) { }
814 |
815 | try {
816 | that.conference && that.conference.leave(), that.conference = null;
817 | } catch (_) { }
818 |
819 | that.mixStreamGlobal && that.mixStreamGlobal.mediaStream && that._destroyMediaStream(that.mixStreamGlobal.mediaStream), (that.mixStreamGlobal = null);
820 | that.ScreenStream && that.ScreenStream.mediaStream && that._destroyMediaStream(that.ScreenStream.mediaStream), (that.ScreenStream = null);
821 | that.localStream && that.localStream.mediaStream && that._destroyMediaStream(that.localStream.mediaStream), (that.localStream = null);
822 | that.conference = that.publicationGlobal = that.subscriptionGlobal = null;
823 |
824 | that.playerStream = null;
825 | that.statInterval && (clearInterval(that.statInterval), that.statInterval = 0);
826 | },
827 | _destroyMediaStream: function (mediaStream) {
828 | if (!mediaStream) return
829 | try {
830 | mediaStream.getTracks().forEach(t => { t.stop(); mediaStream.removeTrack(t); }), mediaStream = null;
831 | } catch (err) {
832 | console.error(err);
833 | }
834 | finally {
835 | mediaStream = null;
836 | }
837 | }
838 | },
839 | mounted: function () {
840 | this.init();
841 | }
842 | });
843 |
844 | window.onbeforeunload = _app._exitRoom.bind(_app);
--------------------------------------------------------------------------------
/static/scripts/app-videoWindows.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const { ipcRenderer } = require('electron');
3 |
4 | const _app = new Vue({
5 | el: '#App',
6 | data:function(){
7 | return {
8 | version:'',
9 | isMaximized:false,
10 | playerStream:null,
11 | isSpeakMuted:false,
12 | lastMoveTime: 0,
13 | hideCursorHandle:null,
14 | toolsIsVisible:true,
15 | toolsIsHover:false,
16 | mainWebContentsId: Number.parseInt(getParameterByName('id'))
17 | }
18 | },
19 | methods:{
20 | init:function(e){
21 | const that = this;
22 |
23 | ipcRenderer.on('maximizeChanged',that.maximizeChanged.bind(that));
24 | ipcRenderer.on('set-version', that._setVersion.bind(that) );
25 | ipcRenderer.on('stream_ended', that.clickClose.bind(that) );
26 | window.addEventListener('keyup', that.onkeyup.bind(that));
27 | window.addEventListener('mousemove', that.onmousemove.bind(that));
28 |
29 | const pc = new RTCPeerConnection();
30 | ipcRenderer.on('set-peer-param',that._setPeerParam.bind(that , pc));
31 | pc.onicecandidate = function( { candidate } ) {
32 | console.log('onicecandidate')
33 | candidate && ipcRenderer.sendTo(that.mainWebContentsId,'set-peer-param',{ candidate:candidate.toJSON() });
34 | }
35 |
36 | pc.ontrack = function(e) {
37 | console.log(e);
38 |
39 | that.playerStream = null, (that.playerStream = e.streams[0],
40 | that.playerStream.onremovetrack = that._removeTrack.bind(that) )
41 | }
42 |
43 | pc.onconnectionstatechange = () => {
44 | console.log('onconnectionstatechange');
45 | (pc.connectionState == 'closed'|| pc.connectionState == 'failed') && (pc.close(),that.clickClose());
46 | }
47 |
48 | ipcRenderer.sendTo(this.mainWebContentsId , 'win-onload' );
49 | this.lastMoveTime = Date.now();
50 | this.hideCursorHandle = setInterval(this.hideCursorInterval.bind(this), 2000);
51 | },
52 | _setVersion:function(event , version){
53 | this.version = version;
54 | },
55 | _setPeerParam:async function(peerConnection, e, { localDescription, candidate }){
56 | console.log('_setPeerParam', e , localDescription , candidate , peerConnection);
57 | this.mainWebContentsId == -1 && (this.mainWebContentsId = e.senderId);
58 | localDescription && ( await peerConnection.setRemoteDescription(localDescription),
59 | await peerConnection.setLocalDescription(),
60 | ipcRenderer.sendTo(this.mainWebContentsId,'set-peer-param',{ localDescription:peerConnection.localDescription.toJSON() }) );
61 |
62 | candidate && peerConnection.addIceCandidate(candidate);
63 | },
64 | _removeTrack:function(e){
65 | console.log('_removeTrack')
66 | },
67 | onkeyup:function(e){
68 | e.keyCode == 27 && this.isMaximized && ipcRenderer.send('setFullScreen-win',false);
69 | return e.keyCode != 27;
70 | },
71 | onmousemove:function(e){
72 | if(this.hideCursorHandle == null)
73 | {
74 | this.lastMoveTime = Date.now();
75 | this.hideCursorHandle = setInterval(this.hideCursorInterval.bind(this), 2000);
76 |
77 | document.body.style.cursor = "default";
78 | this.toolsIsVisible = true;
79 | }
80 | this.lastMoveTime = Date.now();
81 | return true;
82 | },
83 | hideCursorInterval:function(){
84 | if(!this.toolsIsHover && this.lastMoveTime && Date.now() - 5000 > this.lastMoveTime){
85 | clearInterval(this.hideCursorHandle),this.hideCursorHandle = null;
86 |
87 | document.body.style.cursor = "none";
88 | this.toolsIsVisible = false;
89 | }
90 | },
91 | maximizeChanged:function( event , isMaximized ) {
92 | this.isMaximized = isMaximized;
93 | if(this.isMaximized)
94 | {
95 | this.$message({
96 | message: '按下 ESC 键可以退出全屏',
97 | center: true,
98 | iconClass: '',
99 | customClass: 'message_tip',
100 | duration: 3000,
101 | offset: (document.body.clientHeight / 2) - 24
102 | });
103 | }
104 | },
105 | clickFullScreen:function(e){
106 | ipcRenderer.send('setFullScreen-win',true);
107 | },
108 | clickUnFullScreen:function(e){
109 | ipcRenderer.send('setFullScreen-win',false);
110 | },
111 | clickClose:function(e){
112 | this.playerStream = null;
113 |
114 | this.mainWebContentsId >0 && ipcRenderer.sendTo(this.mainWebContentsId,'set-peer-param',{close:true});
115 | ipcRenderer.send("close-win");
116 | },
117 | clickMinimize:function(e){
118 | ipcRenderer.send("minimize-win");
119 | },
120 | _destroyMediaStream:function( mediaStream ){
121 | if( !mediaStream ) return
122 | try {
123 | mediaStream.getTracks().forEach(t=>{ t.stop(); mediaStream.removeTrack(t);}),mediaStream = null;
124 | } catch (err) {
125 | console.error(err);
126 | }
127 | finally {
128 | mediaStream = null;
129 | }
130 | },
131 | _unInit:function(e)
132 | {
133 | const that = this;
134 | that.playerStream = null;
135 | }
136 | },
137 | mounted:function(){
138 | this.init();
139 | }
140 | });
141 |
142 | window.onbeforeunload = _app._unInit.bind(_app);
143 |
144 | function getParameterByName(name) {
145 | name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
146 | var regex = new RegExp("[\\?&]" + name + "=([^]*)"),
147 | results = regex.exec(location.search);
148 | return results === null ?
149 | "" :
150 | decodeURIComponent(results[1].replace(/\+/g, " "));
151 | }
--------------------------------------------------------------------------------
/static/scripts/rest-sample.js:
--------------------------------------------------------------------------------
1 | // Copyright (C) <2018> Intel Corporation
2 | //
3 | // SPDX-License-Identifier: Apache-2.0
4 |
5 | // REST samples. It sends HTTP requests to sample server, and sample server sends requests to conference server.
6 | // Both this file and sample server are samples.
7 | 'use strict';
8 | var send = function (method, path, body, onRes, host) {
9 | var req = new XMLHttpRequest()
10 | req.onreadystatechange = function () {
11 | if (req.readyState === 4) {
12 | onRes && onRes(req.responseText);
13 | }
14 | };
15 | let url = generateUrl(host, path);
16 | req.open(method, url, true);
17 | if (body !== undefined && body !== null) {
18 | req.setRequestHeader('Content-Type', 'application/json; charset=utf-8');
19 | req.send(JSON.stringify(body));
20 | } else {
21 | req.send();
22 | }
23 | };
24 | function getParameterByName(name) {
25 | name = name.replace(/[\[]/, '\\\[').replace(/[\]]/, '\\\]');
26 | var regex = new RegExp('[\\?&]' + name + '=([^]*)'),
27 | results = regex.exec(location.search);
28 | return results === null ? '' : decodeURIComponent(results[1].replace(
29 | /\+/g, ' '));
30 | }
31 | var generateUrl = function(host, path) {
32 | let url;
33 | if (host !== undefined) {
34 | url = host + path; // Use the host user set.
35 | }else {
36 | let s = getParameterByName('serverURL');
37 | !s && (s = location.href);
38 | let u = new URL(s);
39 | url = u.origin + path; // Get the string before last '/'.
40 | }
41 | return url;
42 | }
43 |
44 | var onResponse = function (result) {
45 | if (result) {
46 | try {
47 | console.info('Result:', JSON.parse(result));
48 | } catch (e) {
49 | console.info('Result:', result);
50 | }
51 | } else {
52 | console.info('Null');
53 | }
54 | };
55 |
56 | var mixStream = function (room, stream, views, callback, host) {
57 | var jsonPatch = [];
58 | views.forEach(view => {
59 | jsonPatch.push({
60 | op: 'add',
61 | path: '/info/inViews',
62 | value: view
63 | });
64 | });
65 | send('PATCH', '/rooms/' + room + '/streams/' + stream, jsonPatch,
66 | callback, host);
67 | };
68 | var unMixStream = function (room, stream, views, callback, host) {
69 | var jsonPatch = [];
70 | views.forEach(view => {
71 | jsonPatch.push({
72 | op: 'remove',
73 | path: '/info/inViews',
74 | value: view
75 | });
76 | });
77 | send('PATCH', '/rooms/' + room + '/streams/' + stream, jsonPatch,
78 | callback, host);
79 | };
80 |
81 | var setLayoutStream = function (room, stream, views, host) {
82 | var jsonPatch = [];
83 | views.forEach(view => {
84 | jsonPatch.push({
85 | op: 'replace',
86 | path: '/info/layout',
87 | value: view
88 | });
89 | });
90 |
91 | send('PATCH', '/rooms/' + room + '/streams/' + stream, jsonPatch,
92 | onResponse, host);
93 | };
94 |
95 | var activeLayoutStream = function (room,mixedStream,subStream, host) {
96 | var jsonPatch = [{
97 | op: 'replace',
98 | path: '/info/layout/0/stream',
99 | value: subStream
100 | }];
101 | send('PATCH', '/rooms/' + room + '/streams/' + mixedStream, jsonPatch,
102 | onResponse, host);
103 | };
104 |
105 | var getRecording = function(room, callback, host) {
106 | send('GET', '/rooms/' + room + '/recordings', null, function(recordingRtn) {
107 | if(callback == null) return;
108 | var result = JSON.parse(recordingRtn);
109 | callback && callback(result);
110 | }, host);
111 | };
112 | var startRecording = function(room, audioFrom, videoFrom, container, callback, host) {
113 | var options = {
114 | media: {
115 | audio: {
116 | from: audioFrom
117 | },
118 | video: {
119 | from: videoFrom
120 | }
121 | },
122 | container: (container ? container : 'auto')
123 | };
124 | send('POST', '/rooms/' + room + '/recordings/', options, function(recordingRtn) {
125 | if(callback == null) return;
126 | var result = JSON.parse(recordingRtn);
127 | callback && callback(result);
128 | }, host);
129 | };
130 |
131 | var stopRecording = function(room, id, data ,callback, host) {
132 | send('DELETE', '/rooms/' + room + '/recordings/' + id, data, callback, host);
133 | };
134 |
135 | var getStreamingOuts = function(room,callback,host){
136 | send('GET', '/rooms/' + room + '/streaming-outs', null, function(ret) {
137 | if(callback == null) return;
138 | var result = JSON.parse(ret);
139 | callback && callback(result);
140 | }, host);
141 | }
142 |
143 | var startStreamingOut = function (room, streamId, callback, host) {
144 | var options = {
145 | url: 'rtmp://127.0.0.1/live/'+room,
146 | media: {
147 | audio: {from:streamId},
148 | video: {from:streamId}
149 | }
150 | };
151 | send('POST', '/rooms/' + room + '/streaming-outs', options, function(ret) {
152 | if(callback == null) return;
153 | var result = JSON.parse(ret);
154 | callback && callback(result);
155 | }, host);
156 | };
157 |
158 | var stopStreamingOut = function (room, outId, callback, host) {
159 | send('DELETE', '/rooms/' + room + '/streaming-outs/'+ outId, null, function(ret) {
160 | if(callback == null) return;
161 | var result = JSON.parse(ret);
162 | callback && callback(result);
163 | }, host);
164 | };
165 |
166 | var startStreamingIn = function (room, inUrl, host) {
167 | var options = {
168 | url: inUrl,
169 | media: {
170 | audio: 'auto',
171 | video: true
172 | },
173 | transport: {
174 | protocol: 'udp',
175 | bufferSize: 2048
176 | }
177 | };
178 | send('POST', '/rooms/' + room + '/streaming-ins', options, onResponse, host);
179 | };
180 |
181 | var createToken = function (room, user, role, callback, host) {
182 | var body = {
183 | appKey:"OpenRemote",
184 | room: room,
185 | user: user,
186 | role: role
187 | };
188 | send('POST', '/tokens/', body, callback, host);
189 | };
--------------------------------------------------------------------------------
/static/server/samplertcservice.js:
--------------------------------------------------------------------------------
1 | /*global require, __dirname, console, process*/
2 | 'use strict';
3 |
4 | var express = require('express'),
5 | spdy = require('spdy'),
6 | bodyParser = require('body-parser'),
7 | errorhandler = require('errorhandler'),
8 | morgan = require('morgan'),
9 | fs = require('fs'),
10 | path = require('path'),
11 | log4js = require('log4js'),
12 | https = require('https'),
13 | icsREST = require('./rest');
14 |
15 |
16 |
17 | log4js.configure({
18 | appenders: {
19 | console: {
20 | type: 'console'
21 | },
22 | server: {
23 | type: 'file',
24 | filename: __dirname + '/../../logs/server.log',
25 | "maxLogSize": 4096760,
26 | "numBackups": 5
27 | },
28 | stream: {
29 | type: 'file',
30 | filename: __dirname + '/../../logs/stream.log',
31 | "maxLogSize": 4096760,
32 | "numBackups": 5
33 | },
34 | 'just-errors': {
35 | type: 'logLevelFilter',
36 | appender: 'stream',
37 | level: 'error'
38 | }
39 | },
40 | categories: {
41 | default: {
42 | appenders: ['console', 'server', 'just-errors'],
43 | level: 'debug'
44 | }
45 | }
46 | });
47 |
48 | const logger = log4js.getLogger();
49 |
50 | var app = express();
51 |
52 | // app.configure ya no existe
53 | app.use(errorhandler());
54 | app.use(morgan('dev'));
55 | app.use(express.static(__dirname + '/public'));
56 | app.use(bodyParser.json());
57 | app.use(bodyParser.urlencoded({
58 | extended: true
59 | }));
60 | app.disable('x-powered-by');
61 |
62 | app.use(function (req, res, next) {
63 | res.header('Access-Control-Allow-Origin', '*');
64 | res.header('Access-Control-Allow-Methods', 'POST, GET, PUT, PATCH, OPTIONS, DELETE');
65 | res.header('Access-Control-Allow-Headers', 'origin, content-type');
66 | res.header('Strict-Transport-Security', 'max-age=1024000; includeSubDomain');
67 | res.header('X-Content-Type-Options', 'nosniff');
68 | if (req.method == 'OPTIONS') {
69 | res.send(200);
70 | } else {
71 | next();
72 | }
73 | });
74 |
75 | //vod 点播目录
76 | if(!sfs.existsSync(path.join(__dirname ,'/../../vod/')))
77 | {
78 | fs.mkdirSync(path.join(__dirname ,'/../../vod/'));
79 | }
80 | app.use(express.static(fs.realpathSync(__dirname + '/../../vod/')));
81 |
82 | icsREST.API.init('_service_ID_', '_service_KEY_', 'http://localhost:3000/', false);
83 |
84 | logger.info('AppService Start...');
85 |
86 | var sampleRoom;
87 | var pageOption = {
88 | page: 1,
89 | per_page: 9999999
90 | };
91 | (function initSampleRoom() {
92 | icsREST.API.getRooms(pageOption, function (rooms) {
93 | logger.info(rooms.length + ' rooms in this service.');
94 | logger.info('AppService Start Success!');
95 | var tryCreate = function (room, callback) {
96 | var options = {};
97 | //setDefaultRoomOption(options);
98 | icsREST.API.createRoom(room.name, options, function (response) {
99 | let r = response;
100 | filterRoom(r);
101 | icsREST.API.updateRoom(response._id, r, function (result) {
102 |
103 | }, function (err) {
104 | logger.error('createRoom end' + err);
105 | r.send(err);
106 | });
107 | callback(r._id);
108 | }, function (status, err) {
109 | logger.error('Error in creating room:' + err + " [Retry]");
110 | setTimeout(function () {
111 | tryCreate(room, options, callback);
112 | }, 100);
113 | }, room);
114 | };
115 |
116 | var room;
117 | if (!sampleRoom) {
118 | room = {
119 | name: 'sampleRoom'
120 | };
121 | tryCreate(room, function (Id) {
122 | sampleRoom = Id;
123 | logger.info('sampleRoom Id:', sampleRoom);
124 | });
125 | }
126 | }, function (stCode, msg) {
127 | console.log('getRooms failed(', stCode, '):', msg);
128 | });
129 |
130 | })();
131 |
132 |
133 | ////////////////////////////////////////////////////////////////////////////////////////////
134 | // legacy interface begin
135 | // /////////////////////////////////////////////////////////////////////////////////////////
136 | app.get('/getLog', function (req, res) {
137 | res.writeHead(200, {
138 | 'Content-Type': 'text/plain; charset=utf8'
139 | });
140 | res.end(fs.readFileSync(__dirname + '/../../logs/server.log'));
141 | });
142 | app.get('/getLogError', function (req, res) {
143 | res.writeHead(200, {
144 | 'Content-Type': 'text/plain; charset=utf8'
145 | });
146 | res.end(fs.readFileSync(__dirname + '/../../logs/stream.log'));
147 | });
148 |
149 | app.get('/getUsers/:room', function (req, res) {
150 | var room = req.params.room;
151 | icsREST.API.getParticipants(room, function (users) {
152 | res.send(users);
153 | }, function (err) {
154 | res.send(err);
155 | });
156 | });
157 |
158 | app.get('/getRoom/:room', function (req, res) {
159 | 'use strict';
160 | var room = req.params.room;
161 | icsREST.API.getRoom(room, function (rooms) {
162 | res.send(rooms);
163 | }, function (err) {
164 | res.send(err);
165 | });
166 | });
167 |
168 | app.get('/room/:room/user/:user', function (req, res) {
169 | 'use strict';
170 | var room = req.params.room;
171 | var participant_id = req.params.user;
172 | icsREST.API.getParticipant(room, participant_id, function (user) {
173 | res.send(user);
174 | }, function (err) {
175 | res.send(err);
176 | });
177 | });
178 |
179 | app.delete('/room/:room/user/:user', function (req, res) {
180 | 'use strict';
181 | var room = req.params.room;
182 | var participant_id = req.params.user;
183 | icsREST.API.dropParticipant(room, participant_id, function (result) {
184 | res.send(result);
185 | }, function (err) {
186 | res.send(err);
187 | });
188 | })
189 |
190 | app.delete('/room/:room', function (req, res) {
191 | 'use strict';
192 | var room = req.params.room;
193 | icsREST.API.deleteRoom(room, function (result) {
194 | res.send(result);
195 | }, function (err) {
196 | res.send(err);
197 | });
198 | })
199 | ////////////////////////////////////////////////////////////////////////////////////////////
200 | // legacy interface begin
201 | // /////////////////////////////////////////////////////////////////////////////////////////
202 |
203 | function filterRoom(room) {
204 | if (typeof room != 'object' || room == null) {
205 | return;
206 | }
207 | let r = room;
208 | r.roles = [{
209 | "subscribe": {
210 | "video": true,
211 | "audio": true
212 | },
213 | "publish": {
214 | "video": true,
215 | "audio": true
216 | },
217 | "role": "presenter"
218 | }, {
219 | "subscribe": {
220 | "video": true,
221 | "audio": true
222 | },
223 | "publish": {
224 | "video": true,
225 | "audio": true
226 | },
227 | "role": "sip"
228 | }, {
229 | "subscribe": {
230 | "video": true,
231 | "audio": true
232 | },
233 | "publish": {
234 | "video": true,
235 | "audio": true
236 | },
237 | "role": "presenter_guest"
238 | }, {
239 | "subscribe": {
240 | "video": true,
241 | "audio": true
242 | },
243 | "publish": {
244 | "video": false,
245 | "audio": false
246 | },
247 | "role": "viewer"
248 | }];
249 | r.mediaOut.video.format = [{ "codec": "h264", "profile": "CB" }];
250 | r.mediaOut.video.parameters.bitrate = ["x1.2", "x1.0", "x0.8", "x0.6"];
251 | r.transcoding.video.parameters.bitrate = true;
252 | r.transcoding.video.parameters.framerate = true;
253 |
254 | r.views[0].audio.vad = false;
255 | r.views[0].video.bgColor = { "b": 44, "g": 44, "r": 44 };
256 | r.views[0].video.format = { "codec": "h264", "profile": "CB" };
257 | r.views[0].video.motionFactor = 1.0;
258 | r.views[0].video.parameters.resolution.height = 1080;
259 | r.views[0].video.parameters.resolution.width = 1920;
260 | r.views[0].video.layout.templates.custom = [{ "region": [{ "id": "1", "area": { "height": "1", "width": "1", "top": "0", "left": "0" }, "shape": "rectangle" }] }, { "region": [{ "id": "1", "area": { "height": "1", "width": "1", "top": "0", "left": "0" }, "shape": "rectangle" }, { "id": "2", "area": { "height": "1/4", "width": "1/4", "top": "3/4", "left": "3/4" }, "shape": "rectangle" }] }, { "region": [{ "id": "1", "area": { "height": "1", "width": "1279/1920", "top": "0", "left": "0" }, "shape": "rectangle" }, { "id": "2", "area": { "height": "539/1080", "width": "639/1920", "top": "0", "left": "1281/1920" }, "shape": "rectangle" }, { "id": "3", "area": { "height": "539/1080", "width": "639/1920", "top": "541/1080", "left": "1281/1920" }, "shape": "rectangle" }] }, { "region": [{ "id": "1", "area": { "height": "1", "width": "1279/1920", "top": "0", "left": "0" }, "shape": "rectangle" }, { "id": "2", "area": { "height": "359/1080", "width": "639/1920", "top": "0", "left": "1281/1920" }, "shape": "rectangle" }, { "id": "3", "area": { "height": "358/1080", "width": "639/1920", "top": "361/1080", "left": "1281/1920" }, "shape": "rectangle" }, { "id": "4", "area": { "height": "359/1080", "width": "639/1920", "top": "721/1080", "left": "1281/1920" }, "shape": "rectangle" }] }, { "region": [{ "id": "1", "area": { "height": "1", "width": "1439/1920", "top": "0", "left": "0" }, "shape": "rectangle" }, { "id": "2", "area": { "height": "269/1080", "width": "479/1920", "top": "0", "left": "1441/1920" }, "shape": "rectangle" }, { "id": "3", "area": { "height": "268/1080", "width": "479/1920", "top": "271/1080", "left": "1441/1920" }, "shape": "rectangle" }, { "id": "4", "area": { "height": "268/1080", "width": "479/1920", "top": "541/1080", "left": "1441/1920" }, "shape": "rectangle" }, { "id": "5", "area": { "height": "269/1080", "width": "479/1920", "top": "811/1080", "left": "1441/1920" }, "shape": "rectangle" }] }, { "region": [{ "id": "1", "area": { "height": "719/1080", "width": "1279/1920", "top": "0", "left": "0" }, "shape": "rectangle" }, { "id": "2", "area": { "height": "359/1080", "width": "639/1920", "top": "0", "left": "1281/1920" }, "shape": "rectangle" }, { "id": "3", "area": { "height": "358/1080", "width": "639/1920", "top": "361/1080", "left": "1281/1920" }, "shape": "rectangle" }, { "id": "4", "area": { "height": "358/1080", "width": "639/1920", "top": "721/1080", "left": "1281/1920" }, "shape": "rectangle" }, { "id": "5", "area": { "height": "359/1080", "width": "639/1920", "top": "721/1080", "left": "641/1920" }, "shape": "rectangle" }, { "id": "6", "area": { "height": "359/1080", "width": "639/1920", "top": "721/1080", "left": "0" }, "shape": "rectangle" }] }, { "region": [{ "id": "1", "area": { "height": "809/1080", "width": "1439/1920", "top": "0", "left": "0" }, "shape": "rectangle" }, { "id": "2", "area": { "height": "269/1080", "width": "479/1920", "top": "0", "left": "481/1920" }, "shape": "rectangle" }, { "id": "3", "area": { "height": "268/1080", "width": "479/1920", "top": "281/1080", "left": "1441/1920" }, "shape": "rectangle" }, { "id": "4", "area": { "height": "268/1080", "width": "479/1920", "top": "541/1080", "left": "1441/1920" }, "shape": "rectangle" }, { "id": "5", "area": { "height": "268/1080", "width": "479/1920", "top": "811/1080", "left": "1441/1920" }, "shape": "rectangle" }, { "id": "6", "area": { "height": "269/1080", "width": "479/1920", "top": "811/1080", "left": "961/1920" }, "shape": "rectangle" }, { "id": "7", "area": { "height": "269/1080", "width": "479/1920", "top": "811/1080", "left": "481/1920" }, "shape": "rectangle" }, { "id": "8", "area": { "height": "269/1080", "width": "479/1920", "top": "811/1080", "left": "0" }, "shape": "rectangle" }] }];
261 | r.views[0].video.layout.templates.base = "lecture";
262 | //r.views[0].video.layout.templates.inputCount = 9;
263 | r.views[0].video.layout.fitPolicy = "crop"; //crop / letterbox
264 | r.views[0].video.layout.crop = true;
265 | if (r.views.length <= 1) {
266 | var presenters = JSON.parse(JSON.stringify(r.views[0]));
267 | presenters['label'] = 'presenters';
268 | presenters.video.parameters.resolution.height = 1080;
269 | presenters.video.parameters.resolution.width = 1920;
270 | presenters.video.layout.templates.inputCount = 9;
271 | r.views.push(presenters);
272 | }
273 | if (r.views.length <= 2) {
274 | var presenters = JSON.parse(JSON.stringify(r.views[0]));
275 | presenters['label'] = 'small';
276 | presenters.video.parameters.resolution.height = 1080;
277 | presenters.video.parameters.resolution.width = 480; // 720 - 320 | 1080 - 480
278 | presenters.video.maxInput = 4;
279 | presenters.video.layout.templates.inputCount = 4;
280 | //不携带边框的布局
281 | //presenters.video.layout.templates.custom = [{"region":[{"id":"1","area":{"height":"1/4","width":"1","top":"0","left":"0"},"shape":"rectangle"}]},{"region":[{"id":"1","area":{"height":"1/4","width":"1","top":"0","left":"0"},"shape":"rectangle"},{"id":"2","area":{"height":"1/4","width":"1","top":"1/4","left":"0"},"shape":"rectangle"}]},{"region":[{"id":"1","area":{"height":"1/4","width":"1","top":"0","left":"0"},"shape":"rectangle"},{"id":"2","area":{"height":"1/4","width":"1","top":"1/4","left":"0"},"shape":"rectangle"},{"id":"3","area":{"height":"1/4","width":"1","top":"1/2","left":"0"},"shape":"rectangle"}]},{"region":[{"id":"1","area":{"height":"1/4","width":"1","top":"0","left":"0"},"shape":"rectangle"},{"id":"2","area":{"height":"1/4","width":"1","top":"1/4","left":"0"},"shape":"rectangle"},{"id":"3","area":{"height":"1/4","width":"1","top":"1/2","left":"0"},"shape":"rectangle"},{"id":"4","area":{"height":"1/4","width":"1","top":"3/4","left":"0"},"shape":"rectangle"}]},{"region":[{"id":"1","area":{"height":"1/4","width":"1","top":"0","left":"0"},"shape":"rectangle"},{"id":"2","area":{"height":"1/4","width":"1","top":"1/4","left":"0"},"shape":"rectangle"},{"id":"3","area":{"height":"1/4","width":"1","top":"1/2","left":"0"},"shape":"rectangle"},{"id":"4","area":{"height":"1/4","width":"1","top":"3/4","left":"0"},"shape":"rectangle"},{"id":"5","area":{"height":"0","width":"0","top":"0","left":"0"},"shape":"rectangle"}]},{"region":[{"id":"1","area":{"height":"1/4","width":"1","top":"0","left":"0"},"shape":"rectangle"},{"id":"2","area":{"height":"1/4","width":"1","top":"1/4","left":"0"},"shape":"rectangle"},{"id":"3","area":{"height":"1/4","width":"1","top":"1/2","left":"0"},"shape":"rectangle"},{"id":"4","area":{"height":"1/4","width":"1","top":"3/4","left":"0"},"shape":"rectangle"},{"id":"5","area":{"height":"0","width":"0","top":"0","left":"0"},"shape":"rectangle"},{"id":"6","area":{"height":"0","width":"0","top":"0","left":"0"},"shape":"rectangle"}]},{"region":[{"id":"1","area":{"height":"1/4","width":"1","top":"0","left":"0"},"shape":"rectangle"},{"id":"2","area":{"height":"1/4","width":"1","top":"1/4","left":"0"},"shape":"rectangle"},{"id":"3","area":{"height":"1/4","width":"1","top":"1/2","left":"0"},"shape":"rectangle"},{"id":"4","area":{"height":"1/4","width":"1","top":"3/4","left":"0"},"shape":"rectangle"},{"id":"5","area":{"height":"0","width":"0","top":"0","left":"0"},"shape":"rectangle"},{"id":"6","area":{"height":"0","width":"0","top":"0","left":"0"},"shape":"rectangle"},{"id":"7","area":{"height":"0","width":"0","top":"0","left":"0"},"shape":"rectangle"}]},{"region":[{"id":"1","area":{"height":"1/4","width":"1","top":"0","left":"0"},"shape":"rectangle"},{"id":"2","area":{"height":"1/4","width":"1","top":"1/4","left":"0"},"shape":"rectangle"},{"id":"3","area":{"height":"1/4","width":"1","top":"1/2","left":"0"},"shape":"rectangle"},{"id":"4","area":{"height":"1/4","width":"1","top":"3/4","left":"0"},"shape":"rectangle"},{"id":"5","area":{"height":"0","width":"0","top":"0","left":"0"},"shape":"rectangle"},{"id":"6","area":{"height":"0","width":"0","top":"0","left":"0"},"shape":"rectangle"},{"id":"7","area":{"height":"0","width":"0","top":"0","left":"0"},"shape":"rectangle"},{"id":"8","area":{"height":"0","width":"0","top":"0","left":"0"},"shape":"rectangle"}]}];
282 | //携带边框的布局
283 | presenters.video.layout.templates.custom = [{ "region": [{ "id": "1", "area": { "height": "269/1080", "width": "1", "top": "0", "left": "0" }, "shape": "rectangle" }] }, { "region": [{ "id": "1", "area": { "height": "269/1080", "width": "1", "top": "0", "left": "0" }, "shape": "rectangle" }, { "id": "2", "area": { "height": "269/1080", "width": "1", "top": "271/1080", "left": "0" }, "shape": "rectangle" }] }, { "region": [{ "id": "1", "area": { "height": "269/1080", "width": "1", "top": "0", "left": "0" }, "shape": "rectangle" }, { "id": "2", "area": { "height": "269/1080", "width": "1", "top": "271/1080", "left": "0" }, "shape": "rectangle" }, { "id": "3", "area": { "height": "269/1080", "width": "1", "top": "541/1080", "left": "0" }, "shape": "rectangle" }] }, { "region": [{ "id": "1", "area": { "height": "269/1080", "width": "1", "top": "0", "left": "0" }, "shape": "rectangle" }, { "id": "2", "area": { "height": "268/1080", "width": "1", "top": "271/1080", "left": "0" }, "shape": "rectangle" }, { "id": "3", "area": { "height": "268/1080", "width": "1", "top": "541/1080", "left": "0" }, "shape": "rectangle" }, { "id": "4", "area": { "height": "269/1080", "width": "1", "top": "811/1080", "left": "0" }, "shape": "rectangle" }] }];
284 | //presenters["audio"] = true;
285 | r.views.push(presenters);
286 | }
287 | if (false && r.views.length <= 3) {
288 | var presenters = JSON.parse(JSON.stringify(r.views[0]));
289 | presenters['label'] = 'rectangle';
290 | presenters.video.parameters.resolution.height = 720;
291 | presenters.video.parameters.resolution.width = 1280;
292 | presenters.video.layout.templates.inputCount = 9;
293 | presenters.video.maxInput = 9;
294 | //不携带边框的布局
295 | //presenters.video.layout.templates.custom = [{"region":[{"id":"1","area":{"height":"1","width":"1","top":"0","left":"0"},"shape":"rectangle"}]},{"region":[{"id":"1","area":{"height":"1","width":"1/2","top":"0","left":"0"},"shape":"rectangle"},{"id":"2","area":{"height":"1","width":"1/2","top":"0","left":"1/2"},"shape":"rectangle"}]},{"region":[{"id":"1","area":{"height":"1","width":"1/2","top":"0","left":"0"},"shape":"rectangle"},{"id":"2","area":{"height":"1/2","width":"1/2","top":"0","left":"1/2"},"shape":"rectangle"},{"id":"3","area":{"height":"1/2","width":"1/2","top":"1/2","left":"1/2"},"shape":"rectangle"}]},{"region":[{"id":"1","area":{"height":"1/2","width":"1/2","top":"0","left":"0"},"shape":"rectangle"},{"id":"2","area":{"height":"1/2","width":"1/2","top":"0","left":"1/2"},"shape":"rectangle"},{"id":"3","area":{"height":"1/2","width":"1/2","top":"1/2","left":"0"},"shape":"rectangle"},{"id":"4","area":{"height":"1/2","width":"1/2","top":"1/2","left":"1/2"},"shape":"rectangle"}]},{"region":[{"id":"1","area":{"height":"1/2","width":"1/2","top":"0","left":"0"},"shape":"rectangle"},{"id":"2","area":{"height":"1/2","width":"1/2","top":"0","left":"1/2"},"shape":"rectangle"},{"id":"3","area":{"height":"1/2","width":"1/3","top":"1/2","left":"0"},"shape":"rectangle"},{"id":"4","area":{"height":"1/2","width":"1/3","top":"1/2","left":"1/3"},"shape":"rectangle"},{"id":"5","area":{"height":"1/2","width":"1/3","top":"1/2","left":"2/3"},"shape":"rectangle"}]},{"region":[{"id":"1","area":{"height":"1/2","width":"1/3","top":"0","left":"0"},"shape":"rectangle"},{"id":"2","area":{"height":"1/2","width":"1/3","top":"0","left":"1/3"},"shape":"rectangle"},{"id":"3","area":{"height":"1/2","width":"1/3","top":"0","left":"2/3"},"shape":"rectangle"},{"id":"4","area":{"height":"1/2","width":"1/3","top":"1/2","left":"0"},"shape":"rectangle"},{"id":"5","area":{"height":"1/2","width":"1/3","top":"1/2","left":"1/3"},"shape":"rectangle"},{"id":"6","area":{"height":"1/2","width":"1/3","top":"1/2","left":"2/3"},"shape":"rectangle"}]},{"region":[{"id":"1","area":{"height":"1/3","width":"1/3","top":"0","left":"0"},"shape":"rectangle"},{"id":"2","area":{"height":"1/3","width":"1/3","top":"0","left":"1/3"},"shape":"rectangle"},{"id":"3","area":{"height":"1/3","width":"1/3","top":"0","left":"2/3"},"shape":"rectangle"},{"id":"4","area":{"height":"1/3","width":"1/3","top":"1/3","left":"0"},"shape":"rectangle"},{"id":"5","area":{"height":"1/3","width":"1/3","top":"1/3","left":"1/3"},"shape":"rectangle"},{"id":"6","area":{"height":"1/3","width":"1/3","top":"1/3","left":"2/3"},"shape":"rectangle"},{"id":"7","area":{"height":"1/3","width":"1/3","top":"2/3","left":"0"},"shape":"rectangle"},{"id":"8","area":{"height":"1/3","width":"1/3","top":"2/3","left":"1/3"},"shape":"rectangle"},{"id":"9","area":{"height":"1/3","width":"1/3","top":"2/3","left":"2/3"},"shape":"rectangle"}]}];
296 | //携带边框的布局
297 | presenters.video.layout.templates.custom = [{ "region": [{ "id": "1", "area": { "height": "1", "width": "1", "top": "0", "left": "0" }, "shape": "rectangle" }] }, { "region": [{ "id": "1", "area": { "height": "1", "width": "959/1920", "top": "0", "left": "0" }, "shape": "rectangle" }, { "id": "2", "area": { "height": "1", "width": "959/1920", "top": "0", "left": "961/1920" }, "shape": "rectangle" }] }, { "region": [{ "id": "1", "area": { "height": "1", "width": "959/1920", "top": "0", "left": "0" }, "shape": "rectangle" }, { "id": "2", "area": { "height": "959/1920", "width": "959/1920", "top": "0", "left": "961/1920" }, "shape": "rectangle" }, { "id": "3", "area": { "height": "539/1080", "width": "959/1920", "top": "541/1080", "left": "961/1920" }, "shape": "rectangle" }] }, { "region": [{ "id": "1", "area": { "height": "539/1080", "width": "959/1920", "top": "0", "left": "0" }, "shape": "rectangle" }, { "id": "2", "area": { "height": "539/1080", "width": "959/1920", "top": "0", "left": "961/1920" }, "shape": "rectangle" }, { "id": "3", "area": { "height": "538/1080", "width": "959/1920", "top": "541/1080", "left": "0" }, "shape": "rectangle" }, { "id": "4", "area": { "height": "539/1080", "width": "959/1920", "top": "541/1080", "left": "961/1920" }, "shape": "rectangle" }] }, { "region": [{ "id": "1", "area": { "height": "539/1080", "width": "959/1920", "top": "0", "left": "0" }, "shape": "rectangle" }, { "id": "2", "area": { "height": "539/1080", "width": "959/1920", "top": "0", "left": "961/1920" }, "shape": "rectangle" }, { "id": "3", "area": { "height": "538/1080", "width": "639/1920", "top": "541/1080", "left": "0" }, "shape": "rectangle" }, { "id": "4", "area": { "height": "539/1080", "width": "639/1920", "top": "541/1080", "left": "641/1920" }, "shape": "rectangle" }, { "id": "5", "area": { "height": "539/1080", "width": "639/1920", "top": "541/1080", "left": "1281/1920" }, "shape": "rectangle" }] }, { "region": [{ "id": "1", "area": { "height": "539/1080", "width": "639/1920", "top": "0", "left": "0" }, "shape": "rectangle" }, { "id": "2", "area": { "height": "539/1080", "width": "638/1920", "top": "0", "left": "641/1920" }, "shape": "rectangle" }, { "id": "3", "area": { "height": "539/1080", "width": "639/1920", "top": "0", "left": "1281/1920" }, "shape": "rectangle" }, { "id": "4", "area": { "height": "539/1080", "width": "639/1920", "top": "541/1080", "left": "0" }, "shape": "rectangle" }, { "id": "5", "area": { "height": "539/1080", "width": "638/1920", "top": "541/1080", "left": "641/1920" }, "shape": "rectangle" }, { "id": "6", "area": { "height": "539/1080", "width": "639/1920", "top": "541/1080", "left": "1281/1920" }, "shape": "rectangle" }] }, { "region": [{ "id": "1", "area": { "height": "359/1080", "width": "639/1920", "top": "0", "left": "0" }, "shape": "rectangle" }, { "id": "2", "area": { "height": "359/1080", "width": "638/1920", "top": "0", "left": "641/1920" }, "shape": "rectangle" }, { "id": "3", "area": { "height": "359/1080", "width": "639/1920", "top": "0", "left": "1281/1920" }, "shape": "rectangle" }, { "id": "4", "area": { "height": "358/1080", "width": "639/1920", "top": "361/1080", "left": "0" }, "shape": "rectangle" }, { "id": "5", "area": { "height": "358/1080", "width": "638/1920", "top": "361/1080", "left": "641/1920" }, "shape": "rectangle" }, { "id": "6", "area": { "height": "358/1080", "width": "639/1920", "top": "361/1080", "left": "1281/1920" }, "shape": "rectangle" }, { "id": "7", "area": { "height": "359/1080", "width": "639/1920", "top": "721/1080", "left": "0" }, "shape": "rectangle" }, { "id": "8", "area": { "height": "359/1080", "width": "638/1920", "top": "721/1080", "left": "641/1920" }, "shape": "rectangle" }, { "id": "9", "area": { "height": "359/1080", "width": "639/1920", "top": "721/1080", "left": "1281/1920" }, "shape": "rectangle" }] }];
298 | //presenters.audio = null;
299 |
300 | r.views.push(presenters);
301 | }
302 | console.log("viewport length:" + r.views.length);
303 | }
304 |
305 | ////////////////////////////////////////////////////////////////////////////////////////////
306 | // New RESTful interface begin
307 | // /////////////////////////////////////////////////////////////////////////////////////////
308 | app.post('/rooms', function (req, res) {
309 | 'use strict';
310 | var name = req.body.name;
311 | var options = req.body.options;
312 | logger.info(req.originalUrl + " " + JSON.stringify(req.body));
313 | icsREST.API.createRoom(name, options, function (response) {
314 | let r = response;
315 | filterRoom(r);
316 | icsREST.API.updateRoom(response._id, r, function (result) {
317 | res.send(result);
318 | }, function (err) {
319 | logger.error('createRoom end' + err);
320 | res.send(err);
321 | })
322 | }, function (err) {
323 | logger.error('createRoom end' + err);
324 | res.send(err);
325 | });
326 | });
327 |
328 | app.get('/rooms', function (req, res) {
329 | 'use strict';
330 | var psw = req.query.psw;
331 | psw == 'token' && icsREST.API.getRooms(pageOption, function (rooms) {
332 | res.send(rooms);
333 | }, function (err) {
334 | res.send(err);
335 | });
336 | psw != 'token' && res.status(404).send(`
337 |
338 |
339 |
340 | Error
341 |
342 |
343 | Cannot GET /rooms
344 |
345 | `);
346 | });
347 |
348 | app.get('/rooms/:room', function (req, res) {
349 | 'use strict';
350 | var room = req.params.room;
351 | icsREST.API.getRoom(room, function (result) {
352 | res.send(result);
353 | }, function (err) {
354 | res.send(err);
355 | });
356 | });
357 |
358 | app.put('/rooms/:room', function (req, res) {
359 | 'use strict';
360 | var room = req.params.room,
361 | config = req.body;
362 | icsREST.API.updateRoom(room, config, function (result) {
363 | res.send(result);
364 | }, function (err) {
365 | res.send(err);
366 | });
367 | });
368 |
369 | app.patch('/rooms/:room', function (req, res) {
370 | 'use strict';
371 | var room = req.params.room,
372 | items = req.body;
373 | icsREST.API.updateRoomPartially(room, items, function (result) {
374 | res.send(result);
375 | }, function (err) {
376 | res.send(err);
377 | });
378 | });
379 |
380 | app.delete('/rooms/:room', function (req, res) {
381 | 'use strict';
382 | var room = req.params.room;
383 | icsREST.API.deleteRoom(room, function (result) {
384 | res.send(result);
385 | }, function (err) {
386 | res.send(err);
387 | });
388 | });
389 |
390 | app.get('/rooms/:room/participants', function (req, res) {
391 | 'use strict';
392 | var room = req.params.room;
393 | icsREST.API.getParticipants(room, function (participants) {
394 | res.send(participants);
395 | }, function (err) {
396 | res.send(err);
397 | });
398 | });
399 |
400 | app.get('/rooms/:room/participants/:id', function (req, res) {
401 | 'use strict';
402 | var room = req.params.room;
403 | var participant_id = req.params.id;
404 | icsREST.API.getParticipant(room, participant_id, function (info) {
405 | res.send(info);
406 | }, function (err) {
407 | res.send(err);
408 | });
409 | });
410 |
411 | app.patch('/rooms/:room/participants/:id', function (req, res) {
412 | 'use strict';
413 | var room = req.params.room;
414 | var participant_id = req.params.id;
415 | var items = req.body;
416 | icsREST.API.updateParticipant(room, participant_id, items, function (result) {
417 | res.send(result);
418 | }, function (err) {
419 | res.send(err);
420 | });
421 | });
422 |
423 | app.delete('/rooms/:room/participants/:id', function (req, res) {
424 | 'use strict';
425 | var room = req.params.room;
426 | var participant_id = req.params.id;
427 | icsREST.API.dropParticipant(room, participant_id, function (result) {
428 | res.send(result);
429 | }, function (err) {
430 | res.send(err);
431 | });
432 | });
433 |
434 | app.get('/rooms/:room/streams', function (req, res) {
435 | 'use strict';
436 | var room = req.params.room;
437 | icsREST.API.getStreams(room, function (streams) {
438 | res.send(streams);
439 | }, function (err) {
440 | res.send(err);
441 | });
442 | });
443 |
444 | app.get('/rooms/:room/streams/:stream', function (req, res) {
445 | 'use strict';
446 | var room = req.params.room,
447 | stream_id = req.params.stream;
448 | icsREST.API.getStream(room, stream_id, function (info) {
449 | res.send(info);
450 | }, function (err) {
451 | res.send(err);
452 | });
453 | });
454 |
455 | app.patch('/rooms/:room/streams/:stream', function (req, res) {
456 | 'use strict';
457 | var room = req.params.room,
458 | stream_id = req.params.stream,
459 | items = req.body;
460 | icsREST.API.updateStream(room, stream_id, items, function (result) {
461 | res.send(result);
462 | }, function (err) {
463 | res.send(err);
464 | });
465 | });
466 |
467 | app.delete('/rooms/:room/streams/:stream', function (req, res) {
468 | 'use strict';
469 | var room = req.params.room,
470 | stream_id = req.params.stream;
471 | icsREST.API.deleteStream(room, stream_id, function (result) {
472 | res.send(result);
473 | }, function (err) {
474 | res.send(err);
475 | });
476 | });
477 |
478 | app.post('/rooms/:room/streaming-ins', function (req, res) {
479 | 'use strict';
480 | var room = req.params.room,
481 | url = req.body.url,
482 | transport = req.body.transport,
483 | media = req.body.media;
484 |
485 | icsREST.API.startStreamingIn(room, url, transport, media, function (result) {
486 | res.send(result);
487 | }, function (err) {
488 | res.send(err);
489 | });
490 | });
491 |
492 | app.delete('/rooms/:room/streaming-ins/:id', function (req, res) {
493 | 'use strict';
494 | var room = req.params.room,
495 | stream_id = req.params.id;
496 | icsREST.API.stopStreamingIn(room, stream_id, function (result) {
497 | res.send(result);
498 | }, function (err) {
499 | res.send(err);
500 | });
501 | });
502 |
503 | app.get('/rooms/:room/streaming-outs', function (req, res) {
504 | 'use strict';
505 | var room = req.params.room;
506 | icsREST.API.getStreamingOuts(room, function (streamingOuts) {
507 | res.send(streamingOuts);
508 | }, function (err) {
509 | res.send(err);
510 | });
511 | });
512 |
513 | app.post('/rooms/:room/streaming-outs', function (req, res) {
514 | 'use strict';
515 | var room = req.params.room,
516 | protocol = req.body.protocol,
517 | url = req.body.url,
518 | parameters = req.body.parameters,
519 | media = req.body.media;
520 |
521 | icsREST.API.startStreamingOut(room, protocol, url, parameters, media, function (info) {
522 | res.send(info);
523 | }, function (err) {
524 | res.send(err);
525 | });
526 | });
527 |
528 | app.patch('/rooms/:room/streaming-outs/:id', function (req, res) {
529 | 'use strict';
530 | var room = req.params.room,
531 | id = req.params.id,
532 | commands = req.body;
533 | icsREST.API.updateStreamingOut(room, id, commands, function (result) {
534 | res.send(result);
535 | }, function (err) {
536 | res.send(err);
537 | });
538 | });
539 |
540 | app.delete('/rooms/:room/streaming-outs/:id', function (req, res) {
541 | 'use strict';
542 | var room = req.params.room,
543 | id = req.params.id;
544 | icsREST.API.stopStreamingOut(room, id, function (result) {
545 | res.send(result);
546 | }, function (err) {
547 | res.send(err);
548 | });
549 | });
550 |
551 | app.get('/rooms/:room/recordings', function (req, res) {
552 | 'use strict';
553 | var room = req.params.room;
554 | icsREST.API.getRecordings(room, function (recordings) {
555 | res.send(recordings);
556 | }, function (err) {
557 | res.send(err);
558 | });
559 | });
560 |
561 | app.post('/rooms/:room/recordings', function (req, res) {
562 | 'use strict';
563 | var room = req.params.room,
564 | container = req.body.container,
565 | media = req.body.media;
566 | logger.info("startRecording " + [room, container, media].join("|"));
567 | icsREST.API.startRecording(room, container, media, function (info) {
568 | res.send(info);
569 | }, function (err) {
570 | logger.error("startRecording error " + [room, container, media, err].join("|"));
571 | res.send(err);
572 | });
573 | });
574 |
575 | app.patch('/rooms/:room/recordings/:id', function (req, res) {
576 | 'use strict';
577 | var room = req.params.room,
578 | id = req.params.id,
579 | commands = req.body;
580 | icsREST.API.updateRecording(room, id, commands, function (result) {
581 | res.send(result);
582 | }, function (err) {
583 | res.send(err);
584 | });
585 | });
586 |
587 | const {
588 | exec
589 | } = require('child_process');
590 | const os = require('os');
591 |
592 |
593 | function CopyFile(srcFile, destFile, callback) {
594 | var readStream = fs.createReadStream(srcFile);
595 | var writeStream = fs.createWriteStream(destFile);
596 | readStream.pipe(writeStream);
597 | writeStream.on('close', (e) => {
598 | if (callback != null) {
599 | callback();
600 | }
601 | //console.log("CopyFile " + srcFile + " =>> " + destFile);
602 | })
603 |
604 |
605 | }
606 |
607 | Date.prototype.Format = function (fmt) { //author: meizz
608 | var o = {
609 | "M+": this.getMonth() + 1, //月份
610 | "d+": this.getDate(), //日
611 | "h+": this.getHours(), //小时
612 | "m+": this.getMinutes(), //分
613 | "s+": this.getSeconds(), //秒
614 | "q+": Math.floor((this.getMonth() + 3) / 3), //季度
615 | "S": this.getMilliseconds() //毫秒
616 | };
617 | if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
618 | for (var k in o)
619 | if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
620 | return fmt;
621 | };
622 |
623 | app.get('/vod/:recordid/:recordm3u8', function (req, res, next) {
624 | 'use strict';
625 | let recordid = req.params.recordid;
626 | let recordm3u8 = req.params.recordm3u8;
627 | let vodRootDir = fs.realpathSync(__dirname + '/../../vod');
628 | let destDescPath = `${vodRootDir}/${recordid}.json`;
629 | if (fs.existsSync(destDescPath)) {
630 | let desc = JSON.parse(fs.readFileSync(destDescPath));
631 | if (desc.room) {
632 | let id = desc.room + (new Date(desc.time)).Format("yyyyMMdd");
633 | if (recordm3u8.endsWith('m3u8')) {
634 | res.redirect(`/${id}/index.m3u8`)
635 | return
636 | }
637 | else if (recordm3u8.endsWith('mp4')) {
638 | res.redirect(`/${id}/${id}.mp4`)
639 | }
640 | }
641 | }
642 | res.redirect(`/${recordid}/${recordm3u8}`)
643 | });
644 |
645 | app.delete('/rooms/:room/recordings/:id', function (req, res) {
646 | 'use strict';
647 | var room = req.params.room,
648 | id = req.params.id;
649 | logger.info("stopRecording " + room + " id = " + id);
650 | icsREST.API.stopRecording(room, id, function (result) {
651 | res.send(result);
652 | }, function (err) {
653 | logger.error("stopRecording error:" + err);
654 | res.send(err);
655 | });
656 | });
657 |
658 | var dict_room_rid = {};
659 |
660 | function getTokenById(req, res) {
661 | var room = req.body.room || sampleRoom,
662 | user = req.body.user,
663 | role = req.body.role,
664 | appKey = req.body.appKey;
665 | var preference = {
666 | isp: 'isp',
667 | region: 'region'
668 | };
669 | var room_key = appKey + "_" + room;
670 | if (dict_room_rid[room_key] == null) {
671 | icsREST.API.getRooms({
672 | page: 1,
673 | per_page: 99999999
674 | }, function (rooms) {
675 | console.log(`all rooms:${rooms.length} room_key:${room_key}`)
676 | for (const idx in rooms) {
677 | let _room = rooms[idx];
678 | if (_room.name == room_key) {
679 | dict_room_rid[room_key] = _room._id;
680 | icsREST.API.createToken(_room._id, user, role, preference, function (token_) {
681 | res.send(token_);
682 | }, function (err_) {
683 | logger.error(req.originalUrl + " " + [room, user, role].join(' ') + " /tokens error:" + err_);
684 | res.status(401).send(err_);
685 | });
686 | return;
687 | }
688 | }
689 | console.log(`not found room ${room_key}`)
690 |
691 | icsREST.API.createRoom(room_key, {}, function (response) {
692 | let r = response;
693 | filterRoom(r);
694 | let oldid = response._id;
695 | dict_room_rid[room_key] = oldid;
696 | icsREST.API.updateRoom(oldid, r, function (result) {
697 | icsREST.API.createToken(result._id, user, role, preference, function (token_) {
698 | res.send(token_);
699 | }, function (err_) {
700 | logger.error(req.originalUrl + " " + [room, user, role].join(' ') + " /tokens error:" + err_);
701 | res.status(401).send(err_);
702 | });
703 | }, function (err) {
704 | logger.error('createRoom end' + err);
705 | res.send(err);
706 | });
707 | }, function (err) {
708 | logger.error('createRoom end' + err);
709 | res.send(err);
710 | });
711 | });
712 | }
713 | else {
714 | icsREST.API.createToken(dict_room_rid[room_key], user, role, preference, function (token_) {
715 | res.send(token_);
716 | }, function (err_) {
717 | logger.error(req.originalUrl + " " + [room, user, role].join(' ') + " /tokens error:" + err_);
718 | res.status(401).send(err_);
719 | });
720 | }
721 | }
722 |
723 | //Sip call management.
724 | app.get('/rooms/:room/sipcalls', function (req, res) {
725 | 'use strict';
726 | var room = req.params.room;
727 | icsREST.API.getSipCalls(room, function (sipCalls) {
728 | res.send(sipCalls);
729 | }, function (err) {
730 | res.send(err);
731 | });
732 | });
733 |
734 | app.post('/rooms/:room/sipcalls', function (req, res) {
735 | 'use strict';
736 | var room = req.params.room,
737 | peerUri = req.body.peerURI,
738 | mediaIn = req.body.mediaIn,
739 | mediaOut = req.body.mediaOut;
740 | icsREST.API.makeSipCall(room, peerUri, mediaIn, mediaOut, function (info) {
741 | res.send(info);
742 | }, function (err) {
743 | res.send(err);
744 | });
745 | });
746 |
747 | app.patch('/rooms/:room/sipcalls/:id', function (req, res) {
748 | 'use strict';
749 | var room = req.params.room,
750 | id = req.params.id,
751 | commands = req.body;
752 | icsREST.API.updateSipCall(room, id, commands, function (result) {
753 | res.send(result);
754 | }, function (err) {
755 | res.send(err);
756 | });
757 | });
758 |
759 | app.delete('/rooms/:room/sipcalls/:id', function (req, res) {
760 | 'use strict';
761 | var room = req.params.room,
762 | id = req.params.id;
763 | icsREST.API.endSipCall(room, id, function (result) {
764 | res.send(result);
765 | }, function (err) {
766 | res.send(err);
767 | });
768 | });
769 |
770 | app.post('/tokens', function (req, res) {
771 | 'use strict';
772 | var room = req.body.room || sampleRoom,
773 | user = req.body.user,
774 | role = req.body.role,
775 | appKey = req.body.appKey;
776 |
777 | logger.info(req.originalUrl + " " + [room, user, role].join(' '));
778 | //Note: The actual *ISP* and *region* information should be retrieved from the *req* object and filled in the following 'preference' data.
779 | var preference = {
780 | isp: 'isp',
781 | region: 'region'
782 | };
783 |
784 | if (appKey) {
785 | getTokenById(req, res);
786 | return
787 | }
788 | res.status(404).send("appKey not found");
789 | });
790 | ////////////////////////////////////////////////////////////////////////////////////////////
791 | // New RESTful interface end
792 | ////////////////////////////////////////////////////////////////////////////////////////////
793 |
794 | spdy.createServer({
795 | spdy: {
796 | plain: true
797 | }
798 | }, app).listen(3001, (err) => {
799 | if (err) {
800 | logger.error("Failed to setup plain server, " + err);
801 | return process.exit(1);
802 | }
803 | });
804 |
805 | var cipher = require('./cipher');
806 | cipher.unlock(cipher.k, 'cert/.woogeen.keystore', function cb(err, obj) {
807 | if (!err) {
808 | spdy.createServer({
809 | pfx: fs.readFileSync('cert/certificate.pfx'),
810 | passphrase: obj.sample
811 | }, app).listen(3004, (error) => {
812 | if (error) {
813 | console.log('Failed to setup secured server: ', error);
814 | return process.exit(1);
815 | }
816 | });
817 | }
818 | if (err) {
819 | console.error('Failed to setup secured server:', err);
820 | return process.exit();
821 | }
822 | });
823 |
--------------------------------------------------------------------------------
/static/videoWindows.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 屏幕分享
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
23 |
28 |
29 |
33 |
44 |
45 |
46 |
47 |
48 |
58 |
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------