trtc-cloud-js-sdk 1.0.13 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +88 -0
- package/.prettierrc +5 -0
- package/CHANGELOG.md +58 -0
- package/build/jsdoc/clean-doc.js +12 -0
- package/build/jsdoc/fix-doc.js +141 -0
- package/build/jsdoc/jsdoc.json +42 -0
- package/build/package-bundle.js +29 -0
- package/build/rollup.config.dev.js +88 -0
- package/build/rollup.config.prod.js +93 -0
- package/build/rollup.js +359 -0
- package/build/template/npm-package/package.json +24 -0
- package/coverage/Chrome 103.0.5060 (Mac OS X 10.15.7)/base.css +213 -0
- package/coverage/Chrome 103.0.5060 (Mac OS X 10.15.7)/index.html +80 -0
- package/coverage/Chrome 103.0.5060 (Mac OS X 10.15.7)/prettify.css +1 -0
- package/coverage/Chrome 103.0.5060 (Mac OS X 10.15.7)/prettify.js +1 -0
- package/coverage/Chrome 103.0.5060 (Mac OS X 10.15.7)/sort-arrow-sprite.png +0 -0
- package/coverage/Chrome 103.0.5060 (Mac OS X 10.15.7)/sorter.js +158 -0
- package/examples/apiExample/.env +2 -0
- package/examples/apiExample/README.md +70 -0
- package/examples/apiExample/package-lock.json +30915 -0
- package/examples/apiExample/package.json +51 -0
- package/examples/apiExample/public/audio.js +195 -0
- package/examples/apiExample/public/audio.js.map +7 -0
- package/examples/apiExample/public/av_processing.js +1 -0
- package/examples/apiExample/public/basic/av_processing.wasm +0 -0
- package/examples/apiExample/public/basic/worker.js +10434 -0
- package/examples/apiExample/public/favicon.ico +0 -0
- package/examples/apiExample/public/index.html +47 -0
- package/examples/apiExample/public/logo192.png +0 -0
- package/examples/apiExample/public/logo512.png +0 -0
- package/examples/apiExample/public/manifest.json +25 -0
- package/examples/apiExample/public/robots.txt +3 -0
- package/examples/apiExample/src/App.css +37 -0
- package/examples/apiExample/src/App.js +25 -0
- package/examples/apiExample/src/api/http.js +127 -0
- package/examples/apiExample/src/api/nav.js +44 -0
- package/examples/apiExample/src/components/BasicInfoComponent.css +16 -0
- package/examples/apiExample/src/components/BasicInfoComponent.js +27 -0
- package/examples/apiExample/src/config/gen-test-user-sig.js +64 -0
- package/examples/apiExample/src/config/lib-generate-test-usersig.min.js +7052 -0
- package/examples/apiExample/src/config/nav.js +136 -0
- package/examples/apiExample/src/home.js +16 -0
- package/examples/apiExample/src/index.css +21 -0
- package/examples/apiExample/src/index.js +12 -0
- package/examples/apiExample/src/logo.svg +1 -0
- package/examples/apiExample/src/page/basic/screen-share/index.css +52 -0
- package/examples/apiExample/src/page/basic/screen-share/index.js +223 -0
- package/examples/apiExample/src/page/basic/setDevice/index.js +262 -0
- package/examples/apiExample/src/page/basic/setDevice/index.scss +93 -0
- package/examples/apiExample/src/page/basic/video-call/index.js +521 -0
- package/examples/apiExample/src/page/basic/video-call/index.scss +93 -0
- package/examples/apiExample/src/page/basic/video-call-init/index.js +382 -0
- package/examples/apiExample/src/page/basic/video-call-init/index.scss +93 -0
- package/examples/apiExample/src/page/basic/video-live/index.css +37 -0
- package/examples/apiExample/src/page/basic/video-live/index.js +188 -0
- package/examples/apiExample/src/page/layout.js +22 -0
- package/examples/apiExample/src/page/layout.scss +76 -0
- package/examples/apiExample/src/utils/utils.js +35 -0
- package/examples/jsExample/assets/css/bootstrap-material-design.css +12169 -0
- package/examples/jsExample/assets/css/bootstrap-material-design.min.css +8 -0
- package/examples/jsExample/assets/css/common.css +48 -0
- package/examples/jsExample/assets/icon/iconfont.js +1 -0
- package/examples/jsExample/assets/js/bootstrap-material-design.js +6939 -0
- package/examples/jsExample/assets/js/bootstrap-material-design.js.map +1 -0
- package/examples/jsExample/assets/js/bootstrap-material-design.min.js +1 -0
- package/examples/jsExample/assets/js/graph.js +695 -0
- package/examples/jsExample/assets/js/jquery-3.2.1.min.js +4 -0
- package/examples/jsExample/assets/js/jquery-3.2.1.slim.min.js +4 -0
- package/examples/jsExample/assets/js/lib-generate-test-usersig.min.js +2 -0
- package/examples/jsExample/assets/js/popper.js +2442 -0
- package/examples/jsExample/index.html +57 -0
- package/examples/jsExample/rtc/css/common.css +82 -0
- package/examples/jsExample/rtc/index.html +107 -0
- package/examples/jsExample/rtc/js/index.js +142 -0
- package/examples/vueDemo/LICENSE +21 -0
- package/examples/vueDemo/README.md +144 -0
- package/examples/vueDemo/README_EN.md +136 -0
- package/examples/vueDemo/av_processing.wasm +0 -0
- package/examples/vueDemo/index.html +23 -0
- package/examples/vueDemo/package-lock.json +1375 -0
- package/examples/vueDemo/package.json +36 -0
- package/examples/vueDemo/src/App.vue +12 -0
- package/examples/vueDemo/src/api/index.js +59 -0
- package/examples/vueDemo/src/assets/css/color-dark.css +28 -0
- package/examples/vueDemo/src/assets/css/icon.css +4 -0
- package/examples/vueDemo/src/assets/css/main.css +177 -0
- package/examples/vueDemo/src/assets/img/img.jpg +0 -0
- package/examples/vueDemo/src/assets/img/login-bg.jpg +0 -0
- package/examples/vueDemo/src/components/Header.vue +172 -0
- package/examples/vueDemo/src/components/Sidebar.vue +117 -0
- package/examples/vueDemo/src/components/Tags.vue +174 -0
- package/examples/vueDemo/src/components/tendency.vue +206 -0
- package/examples/vueDemo/src/components/trtc/main-menu.vue +50 -0
- package/examples/vueDemo/src/components/trtc/nav-bar.vue +53 -0
- package/examples/vueDemo/src/components/trtc/show-screen-capture.vue +118 -0
- package/examples/vueDemo/src/components/trtc/trtc-state-check.vue +117 -0
- package/examples/vueDemo/src/config/gen-test-user-sig.js +67 -0
- package/examples/vueDemo/src/config/lib-generate-test-usersig.min.js +7052 -0
- package/examples/vueDemo/src/main.js +11 -0
- package/examples/vueDemo/src/plugins/element.js +17 -0
- package/examples/vueDemo/src/router/index.js +73 -0
- package/examples/vueDemo/src/store/sidebar.js +17 -0
- package/examples/vueDemo/src/store/tags.js +48 -0
- package/examples/vueDemo/src/utils/i18n.js +24 -0
- package/examples/vueDemo/src/utils/request.js +34 -0
- package/examples/vueDemo/src/utils/utils.js +35 -0
- package/examples/vueDemo/src/views/Home.vue +46 -0
- package/examples/vueDemo/src/views/I18n.vue +40 -0
- package/examples/vueDemo/src/views/Icon.vue +229 -0
- package/examples/vueDemo/src/views/basic/trtc.vue +194 -0
- package/examples/vueDemo/src/views/feature/index.vue +259 -0
- package/examples/vueDemo/src/views/github/index.vue +243 -0
- package/examples/vueDemo/src/views/improve/live-index.vue +256 -0
- package/examples/vueDemo/src/views/improve/live-room-anchor.vue +689 -0
- package/examples/vueDemo/src/views/improve/live-room-audience.vue +383 -0
- package/examples/vueDemo/src/views/sdkAppId/index.vue +284 -0
- package/examples/vueDemo/vite.config.js +18 -0
- package/examples/vueDemo/worker.js +22 -0
- package/karma.conf.js +99 -0
- package/package.json +57 -7
- package/scripts/publish.js +86 -0
- package/src/Camera.ts +80 -0
- package/src/Mic.ts +145 -0
- package/src/common/IError.ts +6 -0
- package/src/common/ITRTCCloud.ts +68 -0
- package/src/common/constants.ts +116 -0
- package/src/common/trtc-code.ts +43 -0
- package/src/common/trtc-define.ts +1007 -0
- package/src/common/trtc-event.ts +29 -0
- package/src/index.ts +1672 -0
- package/src/utils/environment.js +297 -0
- package/src/utils/raf.js +131 -0
- package/src/utils/time.js +22 -0
- package/src/utils/utils.ts +71 -0
- package/src/utils/uuid.js +12 -0
- package/test/unit/env.test.js +25 -0
- package/test/unit/get-user-media.test.js +40 -0
- package/test/unit/ice-parser.test.js +23 -0
- package/test/unit/sdp.test.js +45 -0
- package/test/unit/signal.test.js +78 -0
- package/tsconfig.json +32 -0
- package/trtc-cloud-js-sdk.js +0 -1
- /package/{README.md → build/template/npm-package/README.md} +0 -0
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
// import logger from './log/logger';
|
|
2
|
+
import { isUndefined } from './utils';
|
|
3
|
+
|
|
4
|
+
export const USER_AGENT = (window.navigator && window.navigator.userAgent) || '';
|
|
5
|
+
const webkitVersionMap = /AppleWebKit\/([\d.]+)/i.exec(USER_AGENT);
|
|
6
|
+
const appleWebkitVersion = webkitVersionMap ? parseFloat(webkitVersionMap.pop()) : null;
|
|
7
|
+
/*
|
|
8
|
+
* Device is an iPhone
|
|
9
|
+
*
|
|
10
|
+
* @type {Boolean}
|
|
11
|
+
* @constant
|
|
12
|
+
* @private
|
|
13
|
+
*/
|
|
14
|
+
export const IS_IPAD = /iPad/i.test(USER_AGENT);
|
|
15
|
+
|
|
16
|
+
// The Facebook app's UIWebView identifies as both an iPhone and iPad, so
|
|
17
|
+
// to identify iPhones, we need to exclude iPads.
|
|
18
|
+
// http://artsy.github.io/blog/2012/10/18/the-perils-of-ios-user-agent-sniffing/
|
|
19
|
+
export const IS_IPHONE = /iPhone/i.test(USER_AGENT) && !IS_IPAD;
|
|
20
|
+
export const IS_IPOD = /iPod/i.test(USER_AGENT);
|
|
21
|
+
export const IS_IOS = IS_IPHONE || IS_IPAD || IS_IPOD;
|
|
22
|
+
|
|
23
|
+
export const IOS_VERSION = IS_IOS
|
|
24
|
+
&& (function () {
|
|
25
|
+
const match = USER_AGENT.match(/OS (\d+)_/i);
|
|
26
|
+
|
|
27
|
+
if (match && match[1]) {
|
|
28
|
+
return match[1];
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}());
|
|
32
|
+
|
|
33
|
+
export const IS_ANDROID = /Android/i.test(USER_AGENT);
|
|
34
|
+
export const ANDROID_VERSION = IS_ANDROID
|
|
35
|
+
&& (function () {
|
|
36
|
+
// This matches Android Major.Minor.Patch versions
|
|
37
|
+
// ANDROID_VERSION is Major.Minor as a Number, if Minor isn't available, then only Major is returned
|
|
38
|
+
const match = USER_AGENT.match(/Android (\d+)(?:\.(\d+))?(?:\.(\d+))*/i);
|
|
39
|
+
|
|
40
|
+
if (!match) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const major = match[1] && parseFloat(match[1]);
|
|
45
|
+
const minor = match[2] && parseFloat(match[2]);
|
|
46
|
+
|
|
47
|
+
if (major && minor) {
|
|
48
|
+
return parseFloat(`${match[1]}.${match[2]}`);
|
|
49
|
+
} if (major) {
|
|
50
|
+
return major;
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}());
|
|
54
|
+
|
|
55
|
+
// Old Android is defined as Version older than 2.3, and requiring a webkit version of the android browser
|
|
56
|
+
export const IS_OLD_ANDROID = IS_ANDROID && /webkit/i.test(USER_AGENT) && ANDROID_VERSION < 2.3;
|
|
57
|
+
export const IS_NATIVE_ANDROID = IS_ANDROID && ANDROID_VERSION < 5 && appleWebkitVersion < 537;
|
|
58
|
+
|
|
59
|
+
// Firefox
|
|
60
|
+
export const IS_FIREFOX = /Firefox/i.test(USER_AGENT);
|
|
61
|
+
export const FIREFOX_VERSION = IS_FIREFOX
|
|
62
|
+
&& (function () {
|
|
63
|
+
const match = USER_AGENT.match(/Firefox\/(\d+)/);
|
|
64
|
+
if (match && match[1]) {
|
|
65
|
+
return parseFloat(match[1]);
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
}());
|
|
69
|
+
|
|
70
|
+
// old Edge
|
|
71
|
+
export const IS_EDGE = /Edge\//i.test(USER_AGENT);
|
|
72
|
+
export const EDGE_VERSION = IS_EDGE
|
|
73
|
+
&& (function () {
|
|
74
|
+
const match = USER_AGENT.match(/Edge\/(\d+)/i);
|
|
75
|
+
if (match && match[1]) {
|
|
76
|
+
return match[1];
|
|
77
|
+
}
|
|
78
|
+
}());
|
|
79
|
+
// new Edge
|
|
80
|
+
export const IS_EDG = /Edg\//i.test(USER_AGENT);
|
|
81
|
+
export const EDG_VERSION = IS_EDG
|
|
82
|
+
&& (function () {
|
|
83
|
+
const match = USER_AGENT.match(/Edg\/(\d+)/);
|
|
84
|
+
if (match && match[1]) {
|
|
85
|
+
return parseFloat(match[1]);
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
}());
|
|
89
|
+
|
|
90
|
+
// sogou mobile
|
|
91
|
+
export const IS_SOGOUM = /SogouMobileBrowser\//i.test(USER_AGENT);
|
|
92
|
+
export const SOGOUM_VERSION = IS_SOGOUM
|
|
93
|
+
&& (function () {
|
|
94
|
+
const match = USER_AGENT.match(/SogouMobileBrowser\/(\d+)/);
|
|
95
|
+
if (match && match[1]) {
|
|
96
|
+
return parseFloat(match[1]);
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
}());
|
|
100
|
+
|
|
101
|
+
// sogou desktop
|
|
102
|
+
export const IS_SOGOU = /MetaSr\s/i.test(USER_AGENT);
|
|
103
|
+
export const SOGOU_VERSION = IS_SOGOU
|
|
104
|
+
&& (function () {
|
|
105
|
+
const match = USER_AGENT.match(/MetaSr(\s\d+(\.\d+)+)/);
|
|
106
|
+
if (match && match[1]) {
|
|
107
|
+
return parseFloat(match[1]);
|
|
108
|
+
}
|
|
109
|
+
return null;
|
|
110
|
+
}());
|
|
111
|
+
|
|
112
|
+
export const IS_TBS = /TBS\/\d+/i.test(USER_AGENT); // 仅X5内核,QQ浏览器默认x5内核,但是agent没有TBS
|
|
113
|
+
export const TBS_VERSION = IS_TBS
|
|
114
|
+
&& (function () {
|
|
115
|
+
const match = USER_AGENT.match(/TBS\/(\d+)/i);
|
|
116
|
+
if (match && match[1]) {
|
|
117
|
+
return match[1];
|
|
118
|
+
}
|
|
119
|
+
}()); // TBS内核版本
|
|
120
|
+
|
|
121
|
+
export const IS_XWEB = /XWEB\/\d+/i.test(USER_AGENT);
|
|
122
|
+
export const XWEB_VERSION = IS_XWEB
|
|
123
|
+
&& (function () {
|
|
124
|
+
const match = USER_AGENT.match(/XWEB\/(\d+)/i);
|
|
125
|
+
if (match && match[1]) {
|
|
126
|
+
return match[1];
|
|
127
|
+
}
|
|
128
|
+
}()); // XWEB内核版本
|
|
129
|
+
|
|
130
|
+
// IE
|
|
131
|
+
export const IS_IE8 = /MSIE\s8\.0/.test(USER_AGENT);
|
|
132
|
+
export const IS_IE = /MSIE\/\d+/i.test(USER_AGENT);
|
|
133
|
+
export const IE_VERSION = IS_IE
|
|
134
|
+
&& (function () {
|
|
135
|
+
const result = /MSIE\s(\d+)\.\d/.exec(USER_AGENT);
|
|
136
|
+
let version = result && parseFloat(result[1]);
|
|
137
|
+
|
|
138
|
+
if (!version && /Trident\/7.0/i.test(USER_AGENT) && /rv:11.0/.test(USER_AGENT)) {
|
|
139
|
+
// IE 11 has a different user agent string than other IE versions
|
|
140
|
+
version = 11.0;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return version;
|
|
144
|
+
}());
|
|
145
|
+
|
|
146
|
+
export const IS_WECHAT = /(micromessenger|webbrowser)/i.test(USER_AGENT);
|
|
147
|
+
export const WECHAT_VERSION = IS_WECHAT
|
|
148
|
+
&& (function () {
|
|
149
|
+
const match = USER_AGENT.match(/MicroMessenger\/(\d+)/i);
|
|
150
|
+
if (match && match[1]) {
|
|
151
|
+
return match[1];
|
|
152
|
+
}
|
|
153
|
+
}());
|
|
154
|
+
|
|
155
|
+
export const IS_X5MQQB = !IS_TBS && /MQQBrowser\/\d+/i.test(USER_AGENT) && /COVC\/\d+/i.test(USER_AGENT); // 移动端 QQ X5 内核浏览器
|
|
156
|
+
export const IS_MQQB = !IS_TBS && /MQQBrowser\/\d+/i.test(USER_AGENT) && !/COVC\/\d+/i.test(USER_AGENT); // 移动端 QQ 浏览器
|
|
157
|
+
export const MQQB_VERSION = (IS_MQQB || IS_X5MQQB)
|
|
158
|
+
&& (function () {
|
|
159
|
+
const match = USER_AGENT.match(/ MQQBrowser\/([\d.]+)/);
|
|
160
|
+
if (match && match[1]) return match[1];
|
|
161
|
+
return null;
|
|
162
|
+
}());
|
|
163
|
+
|
|
164
|
+
export const IS_WQQB = !IS_TBS && / QQBrowser\/\d+/i.test(USER_AGENT); // windows端QQ浏览器
|
|
165
|
+
export const WQQB_VERSION = IS_WQQB
|
|
166
|
+
&& (function () {
|
|
167
|
+
const match = USER_AGENT.match(/ QQBrowser\/([\d.]+)/);
|
|
168
|
+
if (match && match[1]) return match[1];
|
|
169
|
+
return null;
|
|
170
|
+
}());
|
|
171
|
+
|
|
172
|
+
export const IS_MACQQB = !IS_TBS && /QQBrowserLite\/\d+/i.test(USER_AGENT); // Mac端QQ浏览器
|
|
173
|
+
export const MACQQB_VERSION = IS_MACQQB
|
|
174
|
+
&& (function () {
|
|
175
|
+
const match = USER_AGENT.match(/QQBrowserLite\/([\d.]+)/);
|
|
176
|
+
if (match && match[1]) return match[1];
|
|
177
|
+
return null;
|
|
178
|
+
}());
|
|
179
|
+
|
|
180
|
+
export const IS_IPADQQB = !IS_TBS && /MQBHD\/\d+/i.test(USER_AGENT); // iPad端QQ浏览器
|
|
181
|
+
export const IPADQQB_VERSION = IS_IPADQQB
|
|
182
|
+
&& (function () {
|
|
183
|
+
const match = USER_AGENT.match(/MQBHD\/([\d.]+)/);
|
|
184
|
+
if (match && match[1]) return match[1];
|
|
185
|
+
return null;
|
|
186
|
+
}());
|
|
187
|
+
|
|
188
|
+
export const IS_WIN = /Windows/i.test(USER_AGENT); // window系统
|
|
189
|
+
export const IS_MAC = !IS_IOS && /MAC OS X/i.test(USER_AGENT); // MAC系统,先检查IOS
|
|
190
|
+
export const IS_LINUX = !IS_ANDROID && /Linux/i.test(USER_AGENT);
|
|
191
|
+
export const IS_WX = /MicroMessenger/i.test(USER_AGENT); // 是否为微信环境
|
|
192
|
+
export const IS_UCBROWSER = /UCBrowser/i.test(USER_AGENT);
|
|
193
|
+
export const IS_ELECTRON = /Electron/i.test(USER_AGENT); // 是否为 electron
|
|
194
|
+
|
|
195
|
+
export const IS_MIBROWSER = /MiuiBrowser/i.test(USER_AGENT); // 小米浏览器
|
|
196
|
+
export const MI_VERSION = IS_MIBROWSER
|
|
197
|
+
&& (function () {
|
|
198
|
+
const match = USER_AGENT.match(/MiuiBrowser\/([\d.]+)/);
|
|
199
|
+
if (match && match[1]) return match[1];
|
|
200
|
+
return null;
|
|
201
|
+
}());
|
|
202
|
+
export const IS_HUAWEIBROWSER = /HuaweiBrowser/i.test(USER_AGENT); // 华为浏览器
|
|
203
|
+
export const IS_HUAWEI = /Huawei/i.test(USER_AGENT); // 华为设备
|
|
204
|
+
export const HUAWEI_VERSION = IS_HUAWEIBROWSER
|
|
205
|
+
&& (function () {
|
|
206
|
+
const match = USER_AGENT.match(/HuaweiBrowser\/([\d.]+)/);
|
|
207
|
+
if (match && match[1]) return match[1];
|
|
208
|
+
return null;
|
|
209
|
+
}());
|
|
210
|
+
|
|
211
|
+
export const IS_SAMSUNGBROWSER = /SamsungBrowser/i.test(USER_AGENT); // 三星浏览器
|
|
212
|
+
export const SAMSUNG_VERSION = IS_SAMSUNGBROWSER
|
|
213
|
+
&& (function () {
|
|
214
|
+
const match = USER_AGENT.match(/SamsungBrowser\/([\d.]+)/);
|
|
215
|
+
if (match && match[1]) return match[1];
|
|
216
|
+
return null;
|
|
217
|
+
}());
|
|
218
|
+
export const IS_OPPOBROWSER = /HeyTapBrowser/i.test(USER_AGENT); // OPPO浏览器
|
|
219
|
+
export const OPPO_VERSION = IS_OPPOBROWSER
|
|
220
|
+
&& (function () {
|
|
221
|
+
const match = USER_AGENT.match(/HeyTapBrowser\/([\d.]+)/);
|
|
222
|
+
if (match && match[1]) return match[1];
|
|
223
|
+
return null;
|
|
224
|
+
}());
|
|
225
|
+
export const IS_VIVOBROWSER = /VivoBrowser/i.test(USER_AGENT); // OPPO浏览器
|
|
226
|
+
export const VIVO_VERSION = IS_VIVOBROWSER
|
|
227
|
+
&& (function () {
|
|
228
|
+
const match = USER_AGENT.match(/VivoBrowser\/([\d.]+)/);
|
|
229
|
+
if (match && match[1]) return match[1];
|
|
230
|
+
return null;
|
|
231
|
+
}());
|
|
232
|
+
|
|
233
|
+
// Chrome
|
|
234
|
+
export const IS_CHROME_ONLY = /Chrome/i.test(USER_AGENT);
|
|
235
|
+
export const IS_CHROME = !IS_EDGE
|
|
236
|
+
&& !IS_SOGOU
|
|
237
|
+
&& !IS_SOGOUM
|
|
238
|
+
&& !IS_TBS
|
|
239
|
+
&& !IS_XWEB
|
|
240
|
+
&& !IS_EDG
|
|
241
|
+
&& !IS_WQQB
|
|
242
|
+
&& !IS_MIBROWSER
|
|
243
|
+
&& !IS_HUAWEIBROWSER
|
|
244
|
+
&& !IS_SAMSUNGBROWSER
|
|
245
|
+
&& !IS_OPPOBROWSER
|
|
246
|
+
&& !IS_VIVOBROWSER
|
|
247
|
+
&& /Chrome/i.test(USER_AGENT);
|
|
248
|
+
export const CHROME_MAJOR_VERSION = IS_CHROME
|
|
249
|
+
&& (function () {
|
|
250
|
+
const match = USER_AGENT.match(/Chrome\/(\d+)/);
|
|
251
|
+
|
|
252
|
+
if (match && match[1]) {
|
|
253
|
+
return parseFloat(match[1]);
|
|
254
|
+
}
|
|
255
|
+
return null;
|
|
256
|
+
}());
|
|
257
|
+
export const CHROME_VERSION = IS_CHROME
|
|
258
|
+
&& (function () {
|
|
259
|
+
const match = USER_AGENT.match(/Chrome\/([\d.]+)/);
|
|
260
|
+
if (match && match[1]) return match[1];
|
|
261
|
+
return null;
|
|
262
|
+
}());
|
|
263
|
+
|
|
264
|
+
// Safari
|
|
265
|
+
export const IS_SAFARI = !IS_CHROME_ONLY && !IS_MQQB && !IS_X5MQQB && !IS_MACQQB && !IS_IPADQQB && /Safari/i.test(USER_AGENT);
|
|
266
|
+
export const IS_ANY_SAFARI = IS_SAFARI || IS_IOS;
|
|
267
|
+
export const SAFARI_VERSION = IS_SAFARI
|
|
268
|
+
&& (function () {
|
|
269
|
+
const match = USER_AGENT.match(/Version\/([\d.]+)/);
|
|
270
|
+
if (match && match[1]) return match[1];
|
|
271
|
+
return null;
|
|
272
|
+
}());
|
|
273
|
+
|
|
274
|
+
//
|
|
275
|
+
export const BROWSER_VERSION = IS_CHROME ? `Chrome/${CHROME_VERSION}` : IS_SAFARI ? `Safari/${SAFARI_VERSION}` : 'NotSupportedBrowser';
|
|
276
|
+
|
|
277
|
+
// 是否本地环境 file, localhost, ip 地址
|
|
278
|
+
export const IS_LOCAL = location.protocol === 'file:' || location.hostname === 'localhost' || /^\d+\.\d+\.\d+\.\d+$/.test(location.hostname);
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* 检测 LocalStorage 是否可用
|
|
282
|
+
* @export
|
|
283
|
+
* @return {Boolean}
|
|
284
|
+
*/
|
|
285
|
+
export const isLocalStorageEnabled = (() => {
|
|
286
|
+
let result;
|
|
287
|
+
return () => {
|
|
288
|
+
if (isUndefined(result)) {
|
|
289
|
+
try {
|
|
290
|
+
result = window.localStorage;
|
|
291
|
+
} catch (error) {
|
|
292
|
+
result = false;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return result;
|
|
296
|
+
};
|
|
297
|
+
})();
|
package/src/utils/raf.js
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/* eslint-disable no-underscore-dangle */
|
|
2
|
+
import generateUUID from './uuid';
|
|
3
|
+
import { performanceNow } from './utils';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 基于 requestAnimationFrame 实现类 setInterval 的功能
|
|
7
|
+
* @class RAF
|
|
8
|
+
*/
|
|
9
|
+
class RAF {
|
|
10
|
+
constructor() {
|
|
11
|
+
// Map<intervalId, { rafId, timeoutId, onVisibilityChange}>
|
|
12
|
+
// rafId: requestAnimationFrame 返回的 id
|
|
13
|
+
// timeoutId: setTimeout 返回的 id。在页面隐藏时,使用 setTimeout 作为 requestAnimationFrame 的 backup
|
|
14
|
+
// onVisibilityChange: 监听页面可视性变更的回调,保存该回调是用于在结束 interval 时注销监听。
|
|
15
|
+
this.intervalMap_ = new Map();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 基于 requestAnimationFrame 实现类 setInterval 的功能
|
|
20
|
+
* 当页面隐藏时,会用 setTimeout 作为 backup
|
|
21
|
+
* @param {function} fn
|
|
22
|
+
* @param {number} interval 时间间隔
|
|
23
|
+
* @param {boolean} [enableInBackground=true] 当页面切换到后台时,是否执行 interval
|
|
24
|
+
* @return {string} intervalId
|
|
25
|
+
* @memberof RAF
|
|
26
|
+
*/
|
|
27
|
+
setInterval(fn, interval, enableInBackground = true) {
|
|
28
|
+
const intervalId = generateUUID();
|
|
29
|
+
let startTime = performanceNow();
|
|
30
|
+
let endTime = startTime;
|
|
31
|
+
|
|
32
|
+
this.intervalMap_.set(intervalId, {
|
|
33
|
+
rafId: null,
|
|
34
|
+
timeoutId: null,
|
|
35
|
+
onVisibilityChange: null,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const rafLoop = () => {
|
|
39
|
+
// 当页面隐藏时,用 setTimeout 作为 backup
|
|
40
|
+
if (enableInBackground && document.hidden) {
|
|
41
|
+
fn();
|
|
42
|
+
const timeoutId = setTimeout(rafLoop, interval);
|
|
43
|
+
this.setTimeoutId(intervalId, timeoutId);
|
|
44
|
+
startTime = performanceNow();
|
|
45
|
+
endTime = startTime;
|
|
46
|
+
} else {
|
|
47
|
+
endTime = performanceNow();
|
|
48
|
+
if (endTime - startTime >= interval) {
|
|
49
|
+
startTime = endTime;
|
|
50
|
+
fn();
|
|
51
|
+
}
|
|
52
|
+
const rafId = requestAnimationFrame(rafLoop);
|
|
53
|
+
|
|
54
|
+
this.setRafId(intervalId, rafId);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
const rafId = requestAnimationFrame(rafLoop);
|
|
58
|
+
this.setRafId(intervalId, rafId);
|
|
59
|
+
|
|
60
|
+
// 当页面隐藏时,用 setTimeout 作为 backup
|
|
61
|
+
if (enableInBackground) {
|
|
62
|
+
const onVisibilityChange = () => {
|
|
63
|
+
if (document.hidden) {
|
|
64
|
+
const span = performanceNow() - startTime;
|
|
65
|
+
// 页面隐藏时,距离上次回调执行完的间隔超过 interval,则立即执行 rafLoop
|
|
66
|
+
if (span >= interval) {
|
|
67
|
+
rafLoop();
|
|
68
|
+
} else {
|
|
69
|
+
// 页面隐藏时,未达 interval,则在 interval - span 后执行 rafLoop
|
|
70
|
+
const timeoutId = setTimeout(rafLoop, interval - span);
|
|
71
|
+
this.setTimeoutId(intervalId, timeoutId);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
document.addEventListener('visibilitychange', onVisibilityChange);
|
|
76
|
+
this.setOnVisibilityChange(intervalId, onVisibilityChange);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return intervalId;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* 清除 interval
|
|
84
|
+
* @param {string} intervalId
|
|
85
|
+
* @memberof RAF
|
|
86
|
+
*/
|
|
87
|
+
clearInterval(intervalId) {
|
|
88
|
+
if (this.intervalMap_.has(intervalId)) {
|
|
89
|
+
const { rafId, timeoutId, onVisibilityChange } = this.intervalMap_.get(intervalId);
|
|
90
|
+
cancelAnimationFrame(rafId);
|
|
91
|
+
clearTimeout(timeoutId);
|
|
92
|
+
document.removeEventListener('visibilitychange', onVisibilityChange);
|
|
93
|
+
this.intervalMap_.delete(intervalId);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
setTimeoutId(intervalId, timeoutId) {
|
|
98
|
+
if (this.intervalMap_.has(intervalId)) {
|
|
99
|
+
const value = this.intervalMap_.get(intervalId);
|
|
100
|
+
// 在设置新的 timeout 之前,需清理先前的 timeout
|
|
101
|
+
// 避免来回切换 tab 时,重复设置
|
|
102
|
+
if (value.timeoutId) {
|
|
103
|
+
clearTimeout(value.timeoutId);
|
|
104
|
+
}
|
|
105
|
+
value.timeoutId = timeoutId;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
setRafId(intervalId, rafId) {
|
|
110
|
+
if (this.intervalMap_.has(intervalId)) {
|
|
111
|
+
const value = this.intervalMap_.get(intervalId);
|
|
112
|
+
// 在设置新的 requestAnimationFrame 之前,需清理先前设置的 raf
|
|
113
|
+
// 避免来回切换 tab 时,重复设置
|
|
114
|
+
if (value.rafId) {
|
|
115
|
+
cancelAnimationFrame(value.rafId);
|
|
116
|
+
}
|
|
117
|
+
value.rafId = rafId;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
setOnVisibilityChange(intervalId, onVisibilityChange) {
|
|
122
|
+
if (this.intervalMap_.has(intervalId)) {
|
|
123
|
+
const value = this.intervalMap_.get(intervalId);
|
|
124
|
+
value.onVisibilityChange = onVisibilityChange;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const raf = new RAF();
|
|
130
|
+
|
|
131
|
+
export default raf;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// user system clock may be inaccurate, so we need to adjust the timestamp
|
|
2
|
+
// based on the baseTime received from remote server
|
|
3
|
+
|
|
4
|
+
let baseTime = new Date().getTime();
|
|
5
|
+
let offset = 0;
|
|
6
|
+
|
|
7
|
+
export const setBaseTime = function (time) {
|
|
8
|
+
baseTime = time;
|
|
9
|
+
offset = baseTime - new Date().getTime();
|
|
10
|
+
const base = new Date();
|
|
11
|
+
base.setTime(time);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const getCurrentTime = function () {
|
|
15
|
+
return new Date().getTime() + offset;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const getTimestamp = function () {
|
|
19
|
+
const now = new Date();
|
|
20
|
+
now.setTime(getCurrentTime());
|
|
21
|
+
return now.toLocaleString();
|
|
22
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 获取向下取整的 performance.now() 值
|
|
3
|
+
* @export
|
|
4
|
+
* @return {Number}
|
|
5
|
+
*/
|
|
6
|
+
// eslint-disable-next-line require-jsdoc
|
|
7
|
+
export function performanceNow() {
|
|
8
|
+
return Math.floor(performance.now());
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const isFunction = (param: any) => typeof param === 'function';
|
|
12
|
+
export const isUndefined = (param: any) => typeof param === 'undefined';
|
|
13
|
+
export const isString = (param: any) => typeof param === 'string';
|
|
14
|
+
export const isNumber = (param: any) => typeof param === 'number';
|
|
15
|
+
export const isBoolean = (param: any) => typeof param === 'boolean';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* mixins class
|
|
19
|
+
*/
|
|
20
|
+
export function MixinsClass(...mixins: any[]): any {
|
|
21
|
+
class MixClass {
|
|
22
|
+
constructor() {
|
|
23
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
24
|
+
for (const Mixin of mixins) {
|
|
25
|
+
copyProperties(this, new Mixin(this)); // 拷贝实例属性 同时执行内部初始化
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const proto = MixClass.prototype;
|
|
30
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
31
|
+
for (const mixin of mixins) {
|
|
32
|
+
copyProperties(MixClass, mixin); // 拷贝静态属性
|
|
33
|
+
copyProperties(proto, mixin.prototype); // 拷贝原型属性
|
|
34
|
+
}
|
|
35
|
+
return MixClass;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
*
|
|
40
|
+
* @param target
|
|
41
|
+
* @param source
|
|
42
|
+
*/
|
|
43
|
+
function copyProperties(target: any, source: any) {
|
|
44
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
45
|
+
for (const key of Reflect.ownKeys(source)) {
|
|
46
|
+
if (key !== 'constructor' && key !== 'prototype' && key !== 'name') {
|
|
47
|
+
const desc = Object.getOwnPropertyDescriptor(source, key) || '';
|
|
48
|
+
Object.defineProperty(target, key, desc);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* 防抖函数
|
|
55
|
+
* @param {Function} fn 要执行的函数
|
|
56
|
+
* @param {Number} delay 间隔时间, 毫秒
|
|
57
|
+
* @returns function
|
|
58
|
+
*/
|
|
59
|
+
// eslint-disable-next-line no-unused-vars
|
|
60
|
+
export function debounce(fn: { apply: (arg0: any, arg1: any) => void; }, delay: number | undefined) {
|
|
61
|
+
let timer: number;
|
|
62
|
+
return function (this:any, ...args: any) {
|
|
63
|
+
if (timer > 0) {
|
|
64
|
+
clearTimeout(timer);
|
|
65
|
+
}
|
|
66
|
+
timer = window.setTimeout(() => {
|
|
67
|
+
fn.apply(this, args);
|
|
68
|
+
timer = -1;
|
|
69
|
+
}, delay);
|
|
70
|
+
};
|
|
71
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UUID generator - RFC4122 version 4 compliant solution
|
|
3
|
+
*/
|
|
4
|
+
const generateUUID = function () {
|
|
5
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
|
6
|
+
const r = (Math.random() * 16) | 0;
|
|
7
|
+
const v = c === 'x' ? r : (r & 0x3) | 0x8;
|
|
8
|
+
return v.toString(16);
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export default generateUUID;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import log from '../../src/utils/log/logger';
|
|
2
|
+
import * as env from '../../src/utils/environment';
|
|
3
|
+
import * as rtcDetection from '../../src/rtc/rtc-detection';
|
|
4
|
+
|
|
5
|
+
describe('environment & rtc-detection unit test', () => {
|
|
6
|
+
it('environment works as expected', () => {
|
|
7
|
+
const isChrome = env.IS_CHROME;
|
|
8
|
+
log.debug(`isChrome:${isChrome} Version: ${env.CHROME_VERSION}`);
|
|
9
|
+
expect(isChrome).toBe(true);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('rtc-detection works as expected', done => {
|
|
13
|
+
const isWebRTCSupported = rtcDetection.isWebRTCSupported();
|
|
14
|
+
expect(isWebRTCSupported).toBe(true);
|
|
15
|
+
const isUnifiedPlanDefault = rtcDetection.isUnifiedPlanDefault();
|
|
16
|
+
expect(isUnifiedPlanDefault).toBe(true);
|
|
17
|
+
const isScreenCaptureApiAvailable = rtcDetection.isScreenCaptureApiAvailable();
|
|
18
|
+
expect(isScreenCaptureApiAvailable).toBe(true);
|
|
19
|
+
rtcDetection.checkH264Support(supported => {
|
|
20
|
+
log.debug('h264Supported: ' + supported);
|
|
21
|
+
expect(supported).toBe(true);
|
|
22
|
+
done();
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import log from '../../src/utils/log/logger';
|
|
2
|
+
import GetUserMedia from '../../src/rtc/get-user-media';
|
|
3
|
+
|
|
4
|
+
describe('get-user-media.js: ', () => {
|
|
5
|
+
it('GetUserMedia works as expected', done => {
|
|
6
|
+
log.debug('testing GetUserMedia');
|
|
7
|
+
const gum = new GetUserMedia();
|
|
8
|
+
gum.getUserMedia(
|
|
9
|
+
{ audio: true, video: true },
|
|
10
|
+
stream => {
|
|
11
|
+
log.debug('getUserMedia got stream success');
|
|
12
|
+
expect(true).toBe(true);
|
|
13
|
+
done();
|
|
14
|
+
},
|
|
15
|
+
() => {
|
|
16
|
+
log.error('getUserMedia failed!');
|
|
17
|
+
expect(false).toBe(false);
|
|
18
|
+
done();
|
|
19
|
+
}
|
|
20
|
+
);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('GetUserMedia with video attributes works as expected', done => {
|
|
24
|
+
log.debug('testing GetUserMedia');
|
|
25
|
+
const gum = new GetUserMedia();
|
|
26
|
+
gum.getUserMedia(
|
|
27
|
+
{ audio: true, video: true, width: 640, height: 480, frameRate: 15 },
|
|
28
|
+
stream => {
|
|
29
|
+
log.debug('getUserMedia got stream success');
|
|
30
|
+
expect(true).toBe(true);
|
|
31
|
+
done();
|
|
32
|
+
},
|
|
33
|
+
() => {
|
|
34
|
+
log.error('getUserMedia failed!');
|
|
35
|
+
expect(false).toBe(false);
|
|
36
|
+
done();
|
|
37
|
+
}
|
|
38
|
+
);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import log from '../../src/utils/log/logger';
|
|
2
|
+
import * as iceParser from '../../src/rtc/ice-candidate-parser';
|
|
3
|
+
|
|
4
|
+
describe('ice-candidate-parser: ', () => {
|
|
5
|
+
it('iceParser works', () => {
|
|
6
|
+
log.debug('testing iceParser');
|
|
7
|
+
const hostCandidate =
|
|
8
|
+
'candidate:4211169504 1 udp 2122260223 10.73.47.64 56520 typ host generation 0 ufrag ulxL network-id 1 network-cost 10';
|
|
9
|
+
const srflxCandidate =
|
|
10
|
+
'candidate:1381947189 1 udp 1686052607 180.153.219.16 15826 typ srflx raddr 10.73.47.64 rport 56520 generation 0 ufrag ulxL network-id 1 network-cost 10';
|
|
11
|
+
const tcpCandidate =
|
|
12
|
+
'candidate:3045237776 1 tcp 1518280447 10.73.47.64 9 typ host tcptype active generation 0 ufrag ulxL network-id 1 network-cost 10';
|
|
13
|
+
const host = iceParser.parseIceCandidate(hostCandidate);
|
|
14
|
+
log.debug('host.type: ' + host.type);
|
|
15
|
+
expect(host.type).toBe(iceParser.IceType.HOST);
|
|
16
|
+
const srflx = iceParser.parseIceCandidate(srflxCandidate);
|
|
17
|
+
log.debug('srflx.type: ' + srflx.type);
|
|
18
|
+
expect(srflx.type).toBe(iceParser.IceType.SRFLX);
|
|
19
|
+
const tcp = iceParser.parseIceCandidate(tcpCandidate);
|
|
20
|
+
log.debug('tcp.transport: ' + tcp.transport);
|
|
21
|
+
expect(tcp.transport).toBe(iceParser.IceTransport.TCP);
|
|
22
|
+
});
|
|
23
|
+
});
|