yjz-web-sdk 1.0.10 → 1.0.11-beta.2
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/lib/components/RemotePlayer/index.vue.d.ts +1 -73
- package/lib/composables/useCursorStyle.d.ts +1 -1
- package/lib/composables/useKeyboardControl.d.ts +5 -1
- package/lib/composables/useMouseTouchControl.d.ts +5 -4
- package/lib/composables/useRemoteVideo.d.ts +8 -25
- package/lib/composables/useResizeObserver.d.ts +1 -1
- package/lib/core/data/WebRtcError.d.ts +1 -2
- package/lib/core/data/WebrtcDataType.d.ts +1 -11
- package/lib/core/groupctrl/SdkController.d.ts +2 -2
- package/lib/core/rtc/WebRTCClient.d.ts +2 -5
- package/lib/core/rtc/WebRTCConfig.d.ts +1 -1
- package/lib/core/rtc/WebRtcNegotiate.d.ts +2 -2
- package/lib/core/signal/SignalingClient.d.ts +1 -1
- package/lib/core/util/KeyCodeUtil.d.ts +6 -0
- package/lib/core/util/TurnTestUtil.d.ts +2 -2
- package/lib/yjz-web-sdk.js +728 -1307
- package/package.json +5 -16
- package/lib/components/RemotePlayer/type.d.ts +0 -9
- package/lib/core/util/MapCache.d.ts +0 -20
- package/lib/render/Canvas2DRenderer.d.ts +0 -10
- package/lib/render/WebGLRenderer.d.ts +0 -16
- package/lib/render/WebGPURenderer.d.ts +0 -18
- package/lib/types/index.d.ts +0 -13
- package/lib/util/WasmUtil.d.ts +0 -17
- package/lib/worker/worker.d.ts +0 -1
- package/src/assets/icon/circle.svg +0 -1
- package/src/assets/icon/triangle.svg +0 -1
- package/src/assets/wasm/h264-atomic.wasm +0 -0
- package/src/assets/wasm/h264-simd.wasm +0 -0
- package/src/components/RemotePlayer/index.vue +0 -170
- package/src/components/RemotePlayer/type.ts +0 -11
- package/src/composables/useCursorStyle.ts +0 -15
- package/src/composables/useKeyboardControl.ts +0 -32
- package/src/composables/useMouseTouchControl.ts +0 -158
- package/src/composables/useRemoteVideo.ts +0 -248
- package/src/composables/useResizeObserver.ts +0 -27
- package/src/core/WebRTCSdk.ts +0 -561
- package/src/core/data/MessageType.ts +0 -70
- package/src/core/data/TurnType.ts +0 -25
- package/src/core/data/WebRtcError.ts +0 -93
- package/src/core/data/WebrtcDataType.ts +0 -354
- package/src/core/groupctrl/GroupCtrlSocketManager.ts +0 -94
- package/src/core/groupctrl/SdkController.ts +0 -96
- package/src/core/rtc/WebRTCClient.ts +0 -862
- package/src/core/rtc/WebRTCConfig.ts +0 -86
- package/src/core/rtc/WebRtcNegotiate.ts +0 -164
- package/src/core/signal/SignalingClient.ts +0 -221
- package/src/core/util/FileTypeUtils.ts +0 -75
- package/src/core/util/KeyCodeUtil.ts +0 -162
- package/src/core/util/Logger.ts +0 -83
- package/src/core/util/MapCache.ts +0 -135
- package/src/core/util/ScreenControlUtil.ts +0 -174
- package/src/core/util/TurnTestUtil.ts +0 -123
- package/src/env.d.ts +0 -30
- package/src/index.ts +0 -61
- package/src/render/Canvas2DRenderer.ts +0 -38
- package/src/render/WebGLRenderer.ts +0 -150
- package/src/render/WebGPURenderer.ts +0 -194
- package/src/types/index.ts +0 -15
- package/src/types/webgpu.d.ts +0 -1158
- package/src/util/WasmUtil.ts +0 -291
- package/src/worker/worker.ts +0 -292
package/src/core/util/Logger.ts
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
// sdk/logger.ts
|
|
2
|
-
|
|
3
|
-
export enum LogLevel {
|
|
4
|
-
DEBUG = 10,
|
|
5
|
-
INFO = 20,
|
|
6
|
-
WARN = 30,
|
|
7
|
-
ERROR = 40,
|
|
8
|
-
OFF = 100,
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
// 全局控制
|
|
12
|
-
let globalLogLevel: LogLevel = LogLevel.DEBUG;
|
|
13
|
-
let globalEnable = true;
|
|
14
|
-
let globalNamespace = "SDK"; // 默认命名空间
|
|
15
|
-
|
|
16
|
-
export function setLogLevel(level: LogLevel) {
|
|
17
|
-
globalLogLevel = level;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function enableLog(enable: boolean) {
|
|
21
|
-
globalEnable = enable;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function setNamespace(ns: string) {
|
|
25
|
-
globalNamespace = ns;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// 颜色配置
|
|
29
|
-
const levelStyle: Record<LogLevel, string> = {
|
|
30
|
-
[LogLevel.DEBUG]: "color: #999",
|
|
31
|
-
[LogLevel.INFO]: "color: #2b90d9",
|
|
32
|
-
[LogLevel.WARN]: "color: #e6a23c",
|
|
33
|
-
[LogLevel.ERROR]: "color: #f56c6c",
|
|
34
|
-
[LogLevel.OFF]: "color: inherit",
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
class LoggerCore {
|
|
38
|
-
private canLog(level: LogLevel) {
|
|
39
|
-
return globalEnable && level >= globalLogLevel;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
private log(level: LogLevel, ...args: any[]) {
|
|
43
|
-
// console.log(LogLevel.DEBUG===level, !this.canLog(level), level, globalLogLevel)
|
|
44
|
-
if (!this.canLog(level)) return;
|
|
45
|
-
const levelName = LogLevel[level];
|
|
46
|
-
const prefix = `%c[${globalNamespace}] [${levelName}]`;
|
|
47
|
-
const style = levelStyle[level];
|
|
48
|
-
|
|
49
|
-
switch (level) {
|
|
50
|
-
case LogLevel.DEBUG:
|
|
51
|
-
console.log(prefix, style, ...args);
|
|
52
|
-
break;
|
|
53
|
-
case LogLevel.INFO:
|
|
54
|
-
console.info(prefix, style, ...args);
|
|
55
|
-
break;
|
|
56
|
-
case LogLevel.WARN:
|
|
57
|
-
console.warn(prefix, style, ...args);
|
|
58
|
-
break;
|
|
59
|
-
case LogLevel.ERROR:
|
|
60
|
-
console.error(prefix, style, ...args);
|
|
61
|
-
break;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
debug(...args: any[]) {
|
|
66
|
-
this.log(LogLevel.DEBUG, ...args);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
info(...args: any[]) {
|
|
70
|
-
this.log(LogLevel.INFO, ...args);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
warn(...args: any[]) {
|
|
74
|
-
this.log(LogLevel.WARN, ...args);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
error(...args: any[]) {
|
|
78
|
-
this.log(LogLevel.ERROR, ...args);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// 导出单例(不用实例化,直接用)
|
|
83
|
-
export const Logger = new LoggerCore();
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
export class MapCache {
|
|
2
|
-
private key: string;
|
|
3
|
-
private maxSize: number;
|
|
4
|
-
private defaultExpire: number; // 默认过期时间(毫秒)
|
|
5
|
-
|
|
6
|
-
constructor(key: string, maxSize = 100, expireMs = 5 * 60 * 1000) {
|
|
7
|
-
this.key = key;
|
|
8
|
-
this.maxSize = maxSize;
|
|
9
|
-
this.defaultExpire = expireMs;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/** 从 localStorage 读取 Map(自动清理过期的 item) */
|
|
13
|
-
getMap(): Map<string, any> {
|
|
14
|
-
const raw = localStorage.getItem(this.key);
|
|
15
|
-
if (!raw) return new Map();
|
|
16
|
-
|
|
17
|
-
try {
|
|
18
|
-
const data = JSON.parse(raw) as Record<string, { value: any; timestamp: number; expire: number }>;
|
|
19
|
-
const now = Date.now();
|
|
20
|
-
const result = new Map<string, any>();
|
|
21
|
-
let changed = false;
|
|
22
|
-
|
|
23
|
-
for (const [k, obj] of Object.entries(data)) {
|
|
24
|
-
if (now - obj.timestamp <= obj.expire) {
|
|
25
|
-
result.set(k, obj.value);
|
|
26
|
-
} else {
|
|
27
|
-
changed = true; // 有过期项,需要更新存储
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if (changed) this.saveMap(result);
|
|
32
|
-
|
|
33
|
-
return result;
|
|
34
|
-
} catch {
|
|
35
|
-
return new Map();
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/** 保存 Map(每个值都有独立的 timestamp/expire) */
|
|
40
|
-
private saveMap(map: Map<string, any>, meta?: Record<string, { timestamp: number; expire: number }>) {
|
|
41
|
-
const obj: Record<string, { value: any; timestamp: number; expire: number }> = {};
|
|
42
|
-
const now = Date.now();
|
|
43
|
-
|
|
44
|
-
for (const [k, v] of map.entries()) {
|
|
45
|
-
if (meta && meta[k]) {
|
|
46
|
-
obj[k] = { value: v, timestamp: meta[k].timestamp, expire: meta[k].expire };
|
|
47
|
-
} else {
|
|
48
|
-
obj[k] = { value: v, timestamp: now, expire: this.defaultExpire };
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
localStorage.setItem(this.key, JSON.stringify(obj));
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/** 设置值(支持单项自定义过期时间) */
|
|
55
|
-
set(key: string, value: any, expireMs?: number) {
|
|
56
|
-
const map = this.getMap();
|
|
57
|
-
|
|
58
|
-
// 取出旧的 meta 信息(避免覆盖其它项的 expire)
|
|
59
|
-
const raw = localStorage.getItem(this.key);
|
|
60
|
-
let meta: Record<string, { timestamp: number; expire: number }> = {};
|
|
61
|
-
if (raw) {
|
|
62
|
-
try {
|
|
63
|
-
meta = JSON.parse(raw);
|
|
64
|
-
} catch {
|
|
65
|
-
meta = {};
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// 超出限制时删除最早的
|
|
70
|
-
if (map.size >= this.maxSize && !map.has(key)) {
|
|
71
|
-
const firstKey = map.keys().next().value;
|
|
72
|
-
if (typeof firstKey === "string") {
|
|
73
|
-
map.delete(firstKey);
|
|
74
|
-
delete meta[firstKey];
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
map.set(key, value);
|
|
79
|
-
meta[key] = { timestamp: Date.now(), expire: expireMs ?? this.defaultExpire };
|
|
80
|
-
this.saveMap(map, meta);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/** 获取值(单项过期会自动清除) */
|
|
84
|
-
get(key: string) {
|
|
85
|
-
const map = this.getMap();
|
|
86
|
-
return map.get(key);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/** 检查是否存在且未过期 */
|
|
90
|
-
has(key: string): boolean {
|
|
91
|
-
const raw = localStorage.getItem(this.key);
|
|
92
|
-
if (!raw) return false;
|
|
93
|
-
|
|
94
|
-
try {
|
|
95
|
-
const data = JSON.parse(raw) as Record<string, { value: any; timestamp: number; expire: number }>;
|
|
96
|
-
const obj = data[key];
|
|
97
|
-
if (!obj) return false;
|
|
98
|
-
|
|
99
|
-
const now = Date.now();
|
|
100
|
-
if (now - obj.timestamp <= obj.expire) {
|
|
101
|
-
return true;
|
|
102
|
-
} else {
|
|
103
|
-
// 过期则清除
|
|
104
|
-
this.delete(key);
|
|
105
|
-
return false;
|
|
106
|
-
}
|
|
107
|
-
} catch {
|
|
108
|
-
return false;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/** 删除 */
|
|
113
|
-
delete(key: string) {
|
|
114
|
-
const map = this.getMap();
|
|
115
|
-
map.delete(key);
|
|
116
|
-
|
|
117
|
-
const raw = localStorage.getItem(this.key);
|
|
118
|
-
if (raw) {
|
|
119
|
-
try {
|
|
120
|
-
const meta = JSON.parse(raw);
|
|
121
|
-
delete meta[key];
|
|
122
|
-
this.saveMap(map, meta);
|
|
123
|
-
} catch {
|
|
124
|
-
this.saveMap(map);
|
|
125
|
-
}
|
|
126
|
-
} else {
|
|
127
|
-
this.saveMap(map);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/** 清空 */
|
|
132
|
-
clear() {
|
|
133
|
-
localStorage.removeItem(this.key);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 根据视图和云端流的尺寸、角度及输入坐标,转换为相应的坐标值
|
|
3
|
-
* @param viewWidth 视图宽度
|
|
4
|
-
* @param viewHeight 视图高度
|
|
5
|
-
* @param cloudWidth 云端流宽度
|
|
6
|
-
* @param cloudHeight 云端流高度
|
|
7
|
-
* @param viewAngle 视图旋转角度(0 或 -90)
|
|
8
|
-
* @param streamAngle 流旋转角度(0 或 -90)
|
|
9
|
-
* @param inputX 输入 X 坐标
|
|
10
|
-
* @param inputY 输入 Y 坐标
|
|
11
|
-
* @returns 转换后的坐标 [x, y],若超出有效区域则返回 null
|
|
12
|
-
*/
|
|
13
|
-
export function transformCoordinate(
|
|
14
|
-
viewWidth: number,
|
|
15
|
-
viewHeight: number,
|
|
16
|
-
cloudWidth: number,
|
|
17
|
-
cloudHeight: number,
|
|
18
|
-
viewAngle: number,
|
|
19
|
-
streamAngle: number,
|
|
20
|
-
inputX: number,
|
|
21
|
-
inputY: number,
|
|
22
|
-
): [number, number] | null {
|
|
23
|
-
let realShortWidth: number
|
|
24
|
-
let short: number
|
|
25
|
-
|
|
26
|
-
// 根据视图旋转角度判断短边及计算实际短边宽度
|
|
27
|
-
if (viewAngle === -90) {
|
|
28
|
-
short = viewHeight
|
|
29
|
-
realShortWidth = (cloudWidth * viewWidth) / cloudHeight
|
|
30
|
-
}
|
|
31
|
-
else {
|
|
32
|
-
short = viewWidth
|
|
33
|
-
realShortWidth = (cloudWidth * viewHeight) / cloudHeight
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// 计算无效区域(边缘空白区域)及中间有效区域参数
|
|
37
|
-
const invalidValue = (short - realShortWidth) / 2
|
|
38
|
-
const mid = short / 2
|
|
39
|
-
const start = invalidValue
|
|
40
|
-
const end = short - invalidValue
|
|
41
|
-
const len = mid - invalidValue
|
|
42
|
-
|
|
43
|
-
if (viewAngle === -90) {
|
|
44
|
-
// 视图横屏
|
|
45
|
-
const resultY = linearTransform(invalidValue, mid, start, end, len, inputY)
|
|
46
|
-
if (streamAngle === -90) {
|
|
47
|
-
// 流横屏:X 坐标不变,Y 坐标经过线性转换
|
|
48
|
-
return [inputX, resultY]
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
// 流竖屏:交换坐标并调整 Y 坐标
|
|
52
|
-
return [viewHeight - resultY, inputX]
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
else {
|
|
56
|
-
// 视图竖屏
|
|
57
|
-
const resultX = linearTransform(invalidValue, mid, start, end, len, inputX)
|
|
58
|
-
if (streamAngle === -90) {
|
|
59
|
-
// 流横屏:需要额外旋转坐标
|
|
60
|
-
const [rotX, rotY] = rotatePoint90(viewWidth, viewHeight, resultX, inputY)
|
|
61
|
-
return [rotX, rotY]
|
|
62
|
-
}
|
|
63
|
-
else {
|
|
64
|
-
// 流竖屏:X 坐标经过线性转换,Y 坐标不变
|
|
65
|
-
return [resultX, inputY]
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* 将输入坐标转换为百分比值(相对于视图尺寸),并考虑云端流的宽高比例
|
|
72
|
-
* @param viewWidth 视图宽度
|
|
73
|
-
* @param viewHeight 视图高度
|
|
74
|
-
* @param cloudWidth 云端流宽度
|
|
75
|
-
* @param cloudHeight 云端流高度
|
|
76
|
-
* @param viewAngle 视图旋转角度(0 或 -90)
|
|
77
|
-
* @param streamAngle 流旋转角度(0 或 -90)
|
|
78
|
-
* @param inputX 输入 X 坐标
|
|
79
|
-
* @param inputY 输入 Y 坐标
|
|
80
|
-
* @returns [xRatio, yRatio] 百分比比例值
|
|
81
|
-
*/
|
|
82
|
-
export function valueToPercentage(
|
|
83
|
-
viewWidth: number,
|
|
84
|
-
viewHeight: number,
|
|
85
|
-
cloudWidth: number,
|
|
86
|
-
cloudHeight: number,
|
|
87
|
-
viewAngle: number,
|
|
88
|
-
streamAngle: number,
|
|
89
|
-
inputX: number,
|
|
90
|
-
inputY: number,
|
|
91
|
-
): [number, number] {
|
|
92
|
-
let xRatio: number
|
|
93
|
-
let yRatio: number
|
|
94
|
-
|
|
95
|
-
if (viewAngle === 0 && streamAngle === 0) {
|
|
96
|
-
// 容器竖屏, 流竖屏
|
|
97
|
-
xRatio = inputX / viewWidth
|
|
98
|
-
yRatio = inputY / viewHeight
|
|
99
|
-
}
|
|
100
|
-
else if (viewAngle === -90 && streamAngle === 0) {
|
|
101
|
-
// 容器横屏, 流竖屏
|
|
102
|
-
xRatio = inputX / viewHeight
|
|
103
|
-
yRatio = inputY / viewWidth
|
|
104
|
-
}
|
|
105
|
-
else if (viewAngle === -90 && streamAngle === -90) {
|
|
106
|
-
// 容器横屏, 流横屏
|
|
107
|
-
xRatio = (inputX / viewWidth) * (cloudHeight / cloudWidth)
|
|
108
|
-
yRatio = (inputY / viewHeight) * (cloudWidth / cloudHeight)
|
|
109
|
-
}
|
|
110
|
-
else {
|
|
111
|
-
// 容器竖屏, 流横屏
|
|
112
|
-
xRatio = (inputX / viewHeight) * (cloudHeight / cloudWidth)
|
|
113
|
-
yRatio = (inputY / viewWidth) * (cloudWidth / cloudHeight)
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
return [xRatio, yRatio]
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* 对输入坐标做线性转换
|
|
121
|
-
* @param invalidValue 无效区域大小(边缘空白区域)
|
|
122
|
-
* @param mid 中间有效区域的中点
|
|
123
|
-
* @param start 有效区域开始位置
|
|
124
|
-
* @param end 有效区域结束位置
|
|
125
|
-
* @param len 有效区域长度的一半
|
|
126
|
-
* @param input 输入坐标
|
|
127
|
-
* @returns 转换后的坐标值
|
|
128
|
-
*/
|
|
129
|
-
function linearTransform(
|
|
130
|
-
invalidValue: number,
|
|
131
|
-
mid: number,
|
|
132
|
-
start: number,
|
|
133
|
-
end: number,
|
|
134
|
-
len: number,
|
|
135
|
-
input: number,
|
|
136
|
-
): number {
|
|
137
|
-
if (input >= start && input <= mid - 0.1) {
|
|
138
|
-
return ((input - invalidValue) * mid) / len
|
|
139
|
-
}
|
|
140
|
-
else if (input === mid) {
|
|
141
|
-
return mid
|
|
142
|
-
}
|
|
143
|
-
else if (input >= mid + 0.1 && input <= end) {
|
|
144
|
-
return mid + ((input - mid) * mid) / len
|
|
145
|
-
}
|
|
146
|
-
else {
|
|
147
|
-
return input
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* 将坐标进行 90 度旋转转换(以视图中心为原点)
|
|
153
|
-
* @param width 视图宽度
|
|
154
|
-
* @param height 视图高度
|
|
155
|
-
* @param inputX 输入 X 坐标
|
|
156
|
-
* @param inputY 输入 Y 坐标
|
|
157
|
-
* @returns 旋转后的坐标 [x, y]
|
|
158
|
-
*/
|
|
159
|
-
function rotatePoint90(
|
|
160
|
-
width: number,
|
|
161
|
-
height: number,
|
|
162
|
-
inputX: number,
|
|
163
|
-
inputY: number,
|
|
164
|
-
): [number, number] {
|
|
165
|
-
const offsetLongSide = height / 2
|
|
166
|
-
const offsetShortSide = width / 2
|
|
167
|
-
// 将输入点平移至以视图中心为原点
|
|
168
|
-
const x0 = inputX - offsetShortSide
|
|
169
|
-
const y0 = inputY - offsetLongSide
|
|
170
|
-
// 旋转 90 度后,计算新坐标
|
|
171
|
-
const xRotated = y0 + offsetLongSide
|
|
172
|
-
const yRotated = -x0 + offsetShortSide
|
|
173
|
-
return [xRotated, yRotated]
|
|
174
|
-
}
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
PublicTurnTestResult,
|
|
3
|
-
TurnSelectionResult,
|
|
4
|
-
TurnServerConfig,
|
|
5
|
-
TurnTestResult,
|
|
6
|
-
TurnTestSummary,
|
|
7
|
-
} from '../data/TurnType'
|
|
8
|
-
import type { WebRTCConfigOptions } from '../rtc/WebRTCConfig'
|
|
9
|
-
import {Logger} from "./Logger";
|
|
10
|
-
|
|
11
|
-
export const testTurnServer = (turnConfig: TurnServerConfig, timeoutMs = 600): Promise<TurnTestResult> => {
|
|
12
|
-
return new Promise((resolve) => {
|
|
13
|
-
const start = performance.now()
|
|
14
|
-
let resolved = false
|
|
15
|
-
|
|
16
|
-
const pc = new RTCPeerConnection({
|
|
17
|
-
iceServers: [turnConfig],
|
|
18
|
-
iceTransportPolicy: 'relay', // 强制走 TURN
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
// 创建一个 data channel,触发 ICE 流程
|
|
22
|
-
pc.createDataChannel('test')
|
|
23
|
-
|
|
24
|
-
pc.createOffer()
|
|
25
|
-
.then(offer => pc.setLocalDescription(offer))
|
|
26
|
-
.catch(() => {
|
|
27
|
-
if (!resolved) {
|
|
28
|
-
resolved = true
|
|
29
|
-
pc.close()
|
|
30
|
-
resolve({ ...turnConfig, rtt: Infinity })
|
|
31
|
-
}
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
pc.onicecandidate = (event) => {
|
|
35
|
-
if (resolved) return
|
|
36
|
-
|
|
37
|
-
if (event.candidate && event.candidate.candidate.includes('relay')) {
|
|
38
|
-
const rtt = Math.trunc(performance.now() - start)
|
|
39
|
-
resolved = true
|
|
40
|
-
pc.close()
|
|
41
|
-
resolve({ ...turnConfig, rtt })
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (event.candidate === null) {
|
|
45
|
-
// gathering 完成但没有 relay candidate
|
|
46
|
-
resolved = true
|
|
47
|
-
pc.close()
|
|
48
|
-
resolve({ ...turnConfig, rtt: Infinity })
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
setTimeout(() => {
|
|
53
|
-
if (!resolved) {
|
|
54
|
-
resolved = true
|
|
55
|
-
pc.close()
|
|
56
|
-
resolve({ ...turnConfig, rtt: Infinity })
|
|
57
|
-
}
|
|
58
|
-
}, timeoutMs)
|
|
59
|
-
})
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export const testMultipleTurnServers = async (
|
|
63
|
-
servers: string[],
|
|
64
|
-
timeoutMs = 600,
|
|
65
|
-
): Promise<TurnTestSummary> => {
|
|
66
|
-
const turnServers: TurnServerConfig[] = servers.map(url => ({
|
|
67
|
-
urls: url,
|
|
68
|
-
username: 'yangyj',
|
|
69
|
-
credential: 'hb@2025@168',
|
|
70
|
-
}))
|
|
71
|
-
|
|
72
|
-
// 发起所有测试请求
|
|
73
|
-
const testPromises = turnServers.map(cfg =>
|
|
74
|
-
testTurnServer(cfg, timeoutMs)
|
|
75
|
-
.then(res => res)
|
|
76
|
-
.catch((err) => {
|
|
77
|
-
Logger.warn('警告日志:',`中继计算超时=====>`, err)
|
|
78
|
-
return { ...cfg, rtt: Infinity } // 用 Infinity 表示失败
|
|
79
|
-
}),
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
// 等待所有测试完成
|
|
83
|
-
const results: TurnTestResult[] = await Promise.all(testPromises)
|
|
84
|
-
Logger.debug('调试日志:', `信令计算结果======>`, results)
|
|
85
|
-
const available = results.filter(r => r.rtt !== Infinity)
|
|
86
|
-
|
|
87
|
-
if (available.length === 0) {
|
|
88
|
-
// 全部测试失败,返回错误或抛出异常
|
|
89
|
-
throw new Error('All TURN servers are unreachable or slow (RTT = Infinity).')
|
|
90
|
-
// 或者 return 特定结构:
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const best = available.sort((a, b) => a.rtt - b.rtt)[0]
|
|
94
|
-
|
|
95
|
-
return {
|
|
96
|
-
best: best ? stripTurnAuth(best) : undefined,
|
|
97
|
-
all: results.map(stripTurnAuth),
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const stripTurnAuth = (result: TurnTestResult): PublicTurnTestResult => {
|
|
102
|
-
return {
|
|
103
|
-
urls: result.urls,
|
|
104
|
-
rtt: result.rtt,
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export const areTurnListsEmpty = (options: WebRTCConfigOptions): boolean => {
|
|
109
|
-
return (
|
|
110
|
-
(!options.hostTurn || options.hostTurn.length === 0)
|
|
111
|
-
&& (!options.spareTurn || options.spareTurn.length === 0)
|
|
112
|
-
)
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
export const selectBestTurnServer = async (turnList: string[]): Promise<TurnSelectionResult> => {
|
|
116
|
-
try {
|
|
117
|
-
const result = await testMultipleTurnServers(turnList)
|
|
118
|
-
return { url: result.best?.urls, rtt: result.best?.rtt }
|
|
119
|
-
}
|
|
120
|
-
catch (e) {
|
|
121
|
-
return { error: (e as Error).message || '未知错误' }
|
|
122
|
-
}
|
|
123
|
-
}
|
package/src/env.d.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
declare module '*.vue' {
|
|
2
|
-
import type { DefineComponent } from 'vue'
|
|
3
|
-
const component: DefineComponent<{}, {}, any>
|
|
4
|
-
export default component
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
declare module '*.ts?worker&inline' {
|
|
8
|
-
const workerConstructor: {
|
|
9
|
-
new (): Worker;
|
|
10
|
-
};
|
|
11
|
-
export default workerConstructor;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
declare module '*.js?worker&inline' {
|
|
15
|
-
const workerConstructor: {
|
|
16
|
-
new (): Worker;
|
|
17
|
-
};
|
|
18
|
-
export default workerConstructor;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
declare module "*.wasm?url" {
|
|
23
|
-
const value: string;
|
|
24
|
-
export default value;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
declare module "*.wasm" {
|
|
28
|
-
const value: string;
|
|
29
|
-
export default value;
|
|
30
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { WebRTCSdk } from './core/WebRTCSdk'
|
|
2
|
-
import { getKeyEventData } from "./core/util/KeyCodeUtil";
|
|
3
|
-
import { testMultipleTurnServers } from './core/util/TurnTestUtil'
|
|
4
|
-
import {transformCoordinate, valueToPercentage} from "./core/util/ScreenControlUtil";
|
|
5
|
-
import {
|
|
6
|
-
ActionType,
|
|
7
|
-
ChannelDataType,
|
|
8
|
-
ContainerDirection,
|
|
9
|
-
InputData,
|
|
10
|
-
KeyEventData,
|
|
11
|
-
TouchData,
|
|
12
|
-
WheelData,
|
|
13
|
-
GestureData,
|
|
14
|
-
type ActionCommand,
|
|
15
|
-
ActionCommandType,
|
|
16
|
-
ActionCommandEventType,
|
|
17
|
-
ActionCommandEventValue,
|
|
18
|
-
TrackEventData,
|
|
19
|
-
ClarityData,
|
|
20
|
-
} from "./core/data/WebrtcDataType";
|
|
21
|
-
import { type TurnServerConfig, type TurnTestResult, type TurnTestSummary } from "./core/data/TurnType";
|
|
22
|
-
import type { WebRTCConfigOptions} from './core/rtc/WebRTCConfig'
|
|
23
|
-
|
|
24
|
-
import {EmitType, type CameraError, type WebRtcError } from "./core/data/WebRtcError";
|
|
25
|
-
import RemotePlayer from './components/RemotePlayer/index.vue'
|
|
26
|
-
|
|
27
|
-
import { ConnectorType } from './core/data/MessageType'
|
|
28
|
-
import { GroupCtrlSocketManager } from './core/groupctrl/GroupCtrlSocketManager'
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
export {
|
|
32
|
-
WebRTCSdk,
|
|
33
|
-
getKeyEventData,
|
|
34
|
-
transformCoordinate,
|
|
35
|
-
valueToPercentage,
|
|
36
|
-
ActionType,
|
|
37
|
-
ChannelDataType,
|
|
38
|
-
InputData,
|
|
39
|
-
KeyEventData,
|
|
40
|
-
TouchData,
|
|
41
|
-
ContainerDirection,
|
|
42
|
-
EmitType,
|
|
43
|
-
WheelData,
|
|
44
|
-
GestureData,
|
|
45
|
-
ClarityData,
|
|
46
|
-
type ActionCommand,
|
|
47
|
-
ActionCommandType,
|
|
48
|
-
type WebRTCConfigOptions,
|
|
49
|
-
ActionCommandEventType,
|
|
50
|
-
ActionCommandEventValue,
|
|
51
|
-
RemotePlayer,
|
|
52
|
-
TrackEventData,
|
|
53
|
-
type TurnServerConfig,
|
|
54
|
-
type TurnTestResult,
|
|
55
|
-
type TurnTestSummary,
|
|
56
|
-
testMultipleTurnServers,
|
|
57
|
-
type CameraError,
|
|
58
|
-
type WebRtcError,
|
|
59
|
-
ConnectorType,
|
|
60
|
-
GroupCtrlSocketManager
|
|
61
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
export class Canvas2DRenderer {
|
|
2
|
-
private canvas: HTMLCanvasElement | OffscreenCanvas;
|
|
3
|
-
private ctx: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D;
|
|
4
|
-
|
|
5
|
-
constructor(canvas: HTMLCanvasElement | OffscreenCanvas) {
|
|
6
|
-
this.canvas = canvas;
|
|
7
|
-
const ctx = canvas.getContext("2d") as OffscreenCanvasRenderingContext2D | null;
|
|
8
|
-
if (!ctx) throw new Error("2D context not available");
|
|
9
|
-
this.ctx = ctx;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
render(frame: VideoFrame): void {
|
|
13
|
-
this.canvas.width = frame.displayWidth;
|
|
14
|
-
this.canvas.height = frame.displayHeight;
|
|
15
|
-
this.ctx.drawImage(frame, 0, 0, frame.displayWidth, frame.displayHeight);
|
|
16
|
-
frame.close();
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/** 清空画布 */
|
|
20
|
-
clear(): void {
|
|
21
|
-
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/** 完全销毁 Renderer,释放引用和上下文 */
|
|
25
|
-
destroy(): void {
|
|
26
|
-
// 清空画布
|
|
27
|
-
try {
|
|
28
|
-
this.clear();
|
|
29
|
-
} catch {}
|
|
30
|
-
|
|
31
|
-
// 释放引用
|
|
32
|
-
// @ts-ignore
|
|
33
|
-
this.ctx = null;
|
|
34
|
-
|
|
35
|
-
// @ts-ignore
|
|
36
|
-
this.canvas = null;
|
|
37
|
-
}
|
|
38
|
-
}
|