yjz-web-sdk 1.0.10 → 1.0.11-beta.10
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/README.md +90 -0
- package/lib/ScreenControlUtil-D4-BTCo9.js +5230 -0
- 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/WebRTCSdk.d.ts +3 -0
- package/lib/core/data/WebRtcError.d.ts +3 -3
- 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 +13 -3
- package/lib/core/rtc/WebRTCConfig.d.ts +3 -1
- package/lib/core/rtc/WebRtcNegotiate.d.ts +3 -3
- 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 +4 -4
- package/lib/index.d.ts +3 -3
- package/lib/uni/KeyboardControl.d.ts +53 -0
- package/lib/uni/Logger.d.ts +13 -0
- package/lib/uni/MouseTouchControl.d.ts +56 -0
- package/lib/uni/RemoteCanvasController.d.ts +11 -0
- package/lib/uni/RemoteController.d.ts +23 -0
- package/lib/uni/RemoteVideoController.d.ts +38 -0
- package/lib/uni/WebRTCWrapper.d.ts +57 -0
- package/lib/uni/constants.d.ts +42 -0
- package/lib/uni/index.d.ts +110 -0
- package/lib/{components/RemotePlayer → uni}/type.d.ts +1 -0
- package/lib/uni-sdk.js +1263 -0
- package/lib/yjz-web-sdk.js +312 -5955
- package/package.json +10 -20
- package/lib/core/data/TurnType.d.ts +0 -21
- 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
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import { ConnectorType } from '../data/MessageType'
|
|
2
|
-
import {LogLevel} from "../util/Logger";
|
|
3
|
-
|
|
4
|
-
export interface WebRTCConfigOptions {
|
|
5
|
-
signalServerUrl: string
|
|
6
|
-
myId?: string
|
|
7
|
-
roomId: string
|
|
8
|
-
targetId?: string
|
|
9
|
-
stunServerUri?: string
|
|
10
|
-
stunServerUriAli?: string
|
|
11
|
-
stunServerUriTel?: string
|
|
12
|
-
turnServerUri?: string
|
|
13
|
-
turnServerUserName?: string
|
|
14
|
-
turnServerPassword?: string
|
|
15
|
-
canOperate?: boolean
|
|
16
|
-
screenUrl?: string
|
|
17
|
-
connectStatus?: number
|
|
18
|
-
cloudName?: string
|
|
19
|
-
token: string
|
|
20
|
-
isGroup: boolean
|
|
21
|
-
turnKey?: Array<string>
|
|
22
|
-
hostTurn?: Array<string>
|
|
23
|
-
spareTurn?: Array<string>
|
|
24
|
-
maxRecount?: number
|
|
25
|
-
isCalculate?: boolean
|
|
26
|
-
traceId?: string
|
|
27
|
-
signAgain?: boolean
|
|
28
|
-
connectorType?: ConnectorType
|
|
29
|
-
mainRoomIdOfGroup?: string
|
|
30
|
-
subRoomIdsOfGroup?: string[]
|
|
31
|
-
mainCloudMyId?: string
|
|
32
|
-
connectorAndRoomId?: Map<string, string>
|
|
33
|
-
groupId?: string
|
|
34
|
-
cacheTimeout?: number
|
|
35
|
-
enableLogger?: boolean
|
|
36
|
-
loggerLevel?: LogLevel
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export class WebRTCConfig {
|
|
40
|
-
signalServerUrl: string
|
|
41
|
-
myId: string
|
|
42
|
-
roomId: string
|
|
43
|
-
targetId: string
|
|
44
|
-
stunServerUri: string
|
|
45
|
-
stunServerUriAli: string
|
|
46
|
-
stunServerUriTel: string
|
|
47
|
-
turnServerUri: string
|
|
48
|
-
turnServerUserName: string
|
|
49
|
-
turnServerPassword: string
|
|
50
|
-
token: string
|
|
51
|
-
connectorType: ConnectorType
|
|
52
|
-
mainRoomIdOfGroup?: string
|
|
53
|
-
subRoomIdsOfGroup?: string[]
|
|
54
|
-
mainCloudMyId?: string
|
|
55
|
-
connectorAndRoomId?: Map<string, string>
|
|
56
|
-
groupId?: string
|
|
57
|
-
isGroup: boolean
|
|
58
|
-
turnKey: Array<string>
|
|
59
|
-
traceId: string = ''
|
|
60
|
-
signAgain: boolean
|
|
61
|
-
|
|
62
|
-
constructor(options: WebRTCConfigOptions) {
|
|
63
|
-
this.signalServerUrl = options.signalServerUrl || ''
|
|
64
|
-
this.myId = options.myId || ''
|
|
65
|
-
this.roomId = options.roomId || ''
|
|
66
|
-
this.targetId = options.targetId || ''
|
|
67
|
-
this.turnKey = options.turnKey || []
|
|
68
|
-
|
|
69
|
-
this.stunServerUri = options.stunServerUri || 'stun:stun.l.google.com:19302'
|
|
70
|
-
this.stunServerUriAli = options.stunServerUriAli || 'stun:stun.middle.aliyun.com:3478'
|
|
71
|
-
this.stunServerUriTel = options.stunServerUriTel || 'stun:stun.qq.com:3478'
|
|
72
|
-
this.turnServerUri = options.turnServerUri || 'turn:121.37.25.106:3478'
|
|
73
|
-
this.turnServerUserName = options.turnServerUserName || 'yangyj'
|
|
74
|
-
this.turnServerPassword = options.turnServerPassword || 'hb@2025@168'
|
|
75
|
-
this.token = options.token
|
|
76
|
-
this.connectorType = options.connectorType || ConnectorType.WebRTC
|
|
77
|
-
this.mainRoomIdOfGroup = options.mainRoomIdOfGroup
|
|
78
|
-
this.subRoomIdsOfGroup = options.subRoomIdsOfGroup
|
|
79
|
-
this.connectorAndRoomId = options.connectorAndRoomId
|
|
80
|
-
this.mainCloudMyId = options.mainCloudMyId
|
|
81
|
-
this.groupId = options.groupId
|
|
82
|
-
this.isGroup = options.isGroup
|
|
83
|
-
this.traceId = options.traceId || ''
|
|
84
|
-
this.signAgain = options.signAgain || true
|
|
85
|
-
}
|
|
86
|
-
}
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
import {createWebRtcError, FailCode, type WebRtcError} from "../data/WebRtcError";
|
|
2
|
-
import {WebRTCConfig} from "./WebRTCConfig";
|
|
3
|
-
import 'webrtc-adapter';
|
|
4
|
-
import {Logger} from "../util/Logger";
|
|
5
|
-
let iceConnectionTimeout: ReturnType<typeof setTimeout> | null = null;
|
|
6
|
-
export const setRemoteDescriptionWithHandleOffer = (
|
|
7
|
-
peerConnection: RTCPeerConnection,
|
|
8
|
-
sdp: string,
|
|
9
|
-
sendAnswer?: (sdp: string) => void,
|
|
10
|
-
onError?: (err: WebRtcError) => void
|
|
11
|
-
): void => {
|
|
12
|
-
Logger.info('信息日志:', '设置远程offer Description=======>')
|
|
13
|
-
const description = new RTCSessionDescription({ type: "offer", sdp });
|
|
14
|
-
peerConnection.setRemoteDescription(description)
|
|
15
|
-
.then(() => createAnswer(peerConnection, sendAnswer, onError))
|
|
16
|
-
.catch(error => onError?.(createWebRtcError(FailCode.HANDLE_OFFER, error)));
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const createAnswer = (
|
|
20
|
-
peerConnection: RTCPeerConnection,
|
|
21
|
-
sendAnswer?: (sdp: string) => void,
|
|
22
|
-
onError?: (err: WebRtcError) => void
|
|
23
|
-
): void => {
|
|
24
|
-
Logger.info('信息日志:', '创建webrtcAnswer=======>')
|
|
25
|
-
peerConnection.createAnswer()
|
|
26
|
-
.then(answer => setLocalDescriptionWithCreateAnswer(peerConnection, answer, sendAnswer, onError))
|
|
27
|
-
.catch(error => onError?.(createWebRtcError(FailCode.CREATE_ANSWER, error)));
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const setLocalDescriptionWithCreateAnswer = (
|
|
31
|
-
peerConnection: RTCPeerConnection,
|
|
32
|
-
answer: RTCSessionDescriptionInit,
|
|
33
|
-
sendAnswer?: (sdp: string) => void,
|
|
34
|
-
onError?: (err: WebRtcError) => void
|
|
35
|
-
): void => {
|
|
36
|
-
Logger.info('信息日志:', '设置本地answer Description=======>')
|
|
37
|
-
|
|
38
|
-
// peerConnection.setLocalDescription({ type: answer.type, sdp })
|
|
39
|
-
peerConnection.setLocalDescription(answer)
|
|
40
|
-
.then(() => {
|
|
41
|
-
sendAnswer?.(answer.sdp ?? '');
|
|
42
|
-
}).catch(error => {
|
|
43
|
-
onError?.(createWebRtcError(FailCode.LOCAL_DES, error))
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export const createPeerConnection = (config: WebRTCConfig): RTCPeerConnection => {
|
|
48
|
-
Logger.info('信息日志:', '初始化 Webrtc PeerConnection=======>')
|
|
49
|
-
const iceServers: RTCIceServer[] = [
|
|
50
|
-
{ urls: config.stunServerUri },
|
|
51
|
-
{
|
|
52
|
-
urls: config.turnServerUri,
|
|
53
|
-
username: config.turnServerUserName,
|
|
54
|
-
credential: config.turnServerPassword,
|
|
55
|
-
}
|
|
56
|
-
];
|
|
57
|
-
|
|
58
|
-
const peerConnectionConfig: RTCConfiguration = {
|
|
59
|
-
iceServers,
|
|
60
|
-
iceTransportPolicy: "all",
|
|
61
|
-
bundlePolicy: "max-bundle"
|
|
62
|
-
};
|
|
63
|
-
return new RTCPeerConnection(peerConnectionConfig);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export const configPeerConnection = (
|
|
67
|
-
peerConnection: RTCPeerConnection,
|
|
68
|
-
onICEMessage: (candidate: string) => void,
|
|
69
|
-
onTrack?: (track: MediaStreamTrack) => void,
|
|
70
|
-
onConnectState?: (state: RTCIceConnectionState) => void,
|
|
71
|
-
onError?: (error: WebRtcError) => void
|
|
72
|
-
): void => {
|
|
73
|
-
peerConnection.onicecandidate = event => {
|
|
74
|
-
if (!event.candidate) {
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
const candidateObj = {
|
|
78
|
-
sdp: event.candidate.candidate,
|
|
79
|
-
sdpMid: event.candidate.sdpMid,
|
|
80
|
-
sdpMLineIndex: event.candidate.sdpMLineIndex,
|
|
81
|
-
};
|
|
82
|
-
Logger.debug('信息日志:', `webrtc 生成的icecandidate===>${JSON.stringify(candidateObj)}`)
|
|
83
|
-
onICEMessage(JSON.stringify(candidateObj));
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
peerConnection.onconnectionstatechange = () => {
|
|
87
|
-
const state:RTCIceConnectionState = peerConnection.iceConnectionState;
|
|
88
|
-
if(!state){
|
|
89
|
-
return
|
|
90
|
-
}
|
|
91
|
-
Logger.debug('信息日志:', `webrtc p2p连接状态===>`, state)
|
|
92
|
-
if (state === "failed" || state === 'disconnected' || state === 'closed') {
|
|
93
|
-
onError?.(createWebRtcError(FailCode.ICE_STATE, "failed"));
|
|
94
|
-
} else if(state === "connected"){
|
|
95
|
-
if(iceConnectionTimeout){
|
|
96
|
-
clearTimeout(iceConnectionTimeout)
|
|
97
|
-
}
|
|
98
|
-
} else if(state === "new" || state === "checking"){
|
|
99
|
-
if(iceConnectionTimeout){
|
|
100
|
-
iceConnectionTimeout = setTimeout(() => {
|
|
101
|
-
onError?.(createWebRtcError(FailCode.ICE_STATE, "请检查相关配置"));
|
|
102
|
-
iceConnectionTimeout = null;
|
|
103
|
-
}, 6000);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
onConnectState?.(state)
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
peerConnection.ontrack = event => {
|
|
110
|
-
Logger.debug('信息日志:', 'webrtc p2p连接后获取的音视频track')
|
|
111
|
-
const track = event.track;
|
|
112
|
-
track.contentHint = "motion";
|
|
113
|
-
onTrack?.(track);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export const setRemoteDescriptionWithHandleAnswer = (
|
|
118
|
-
peerConnection: RTCPeerConnection,
|
|
119
|
-
sdp: string,
|
|
120
|
-
onError?: (error: WebRtcError) => void
|
|
121
|
-
): void => {
|
|
122
|
-
const description = new RTCSessionDescription({ type: "answer", sdp });
|
|
123
|
-
peerConnection.setRemoteDescription(description)
|
|
124
|
-
.catch(err => onError?.(createWebRtcError(FailCode.REMOTE_DES, err)));
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
export const createOffer = async (
|
|
128
|
-
peerConnection: RTCPeerConnection,
|
|
129
|
-
sendOfferMessage: (sdp: string) => void
|
|
130
|
-
): Promise<void> => {
|
|
131
|
-
try {
|
|
132
|
-
const offer = await peerConnection.createOffer({"offerToReceiveAudio":true,"offerToReceiveVideo":true});
|
|
133
|
-
// setLocalDescriptionWithCreateOffer 也可能抛异常,需要 await
|
|
134
|
-
await setLocalDescriptionWithCreateOffer(peerConnection, offer, sendOfferMessage);
|
|
135
|
-
} catch (err) {
|
|
136
|
-
// 直接抛出异常,不再依赖回调
|
|
137
|
-
throw new Error("摄像头视频流添加失败");
|
|
138
|
-
}
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
export const setLocalDescriptionWithCreateOffer = async (
|
|
142
|
-
peerConnection: RTCPeerConnection,
|
|
143
|
-
offer: RTCSessionDescriptionInit,
|
|
144
|
-
sendOfferMessage: (sdp: string) => void
|
|
145
|
-
): Promise<void> => {
|
|
146
|
-
Logger.info('信息日志:', '设置本地offer Description=======>')
|
|
147
|
-
try {
|
|
148
|
-
await peerConnection.setLocalDescription(offer);
|
|
149
|
-
sendOfferMessage(offer.sdp ?? '');
|
|
150
|
-
} catch (err) {
|
|
151
|
-
// 直接抛出异常,不使用回调
|
|
152
|
-
throw new Error("摄像头视频流添加失败");
|
|
153
|
-
}
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
export const addIceCandidate = (
|
|
157
|
-
peerConnection: RTCPeerConnection,
|
|
158
|
-
candidate: RTCIceCandidateInit,
|
|
159
|
-
onError?: (error: WebRtcError) => void
|
|
160
|
-
): void => {
|
|
161
|
-
Logger.info('信息日志:', '接收远程ice 并设置=======>')
|
|
162
|
-
peerConnection.addIceCandidate(candidate)
|
|
163
|
-
.catch(err => onError?.(createWebRtcError(FailCode.HANDLE_ICE, err)));
|
|
164
|
-
}
|
|
@@ -1,221 +0,0 @@
|
|
|
1
|
-
import { EventEmitter } from 'eventemitter3'
|
|
2
|
-
import type { WebRTCConfig } from '../rtc/WebRTCConfig'
|
|
3
|
-
import { ConnectorType, MessageType, SendType } from '../data/MessageType'
|
|
4
|
-
import { createWebRtcError, EmitType, FailCode } from '../data/WebRtcError'
|
|
5
|
-
import {Logger} from "../util/Logger";
|
|
6
|
-
|
|
7
|
-
// 导出一个名为 SignalingClient 的类,该类继承自 EventEmitter
|
|
8
|
-
export class SignalingClient extends EventEmitter {
|
|
9
|
-
// 私有属性 config,用于存储 WebRTC 配置信息
|
|
10
|
-
private config: WebRTCConfig // 根据实际情况替换为具体配置类型
|
|
11
|
-
// 私有属性 webSocket,用于存储 WebSocket 连接实例,初始值为 null
|
|
12
|
-
private webSocket: WebSocket | null = null
|
|
13
|
-
private timeout: ReturnType<typeof setTimeout> | null = null
|
|
14
|
-
|
|
15
|
-
constructor(config: WebRTCConfig) {
|
|
16
|
-
// 构造函数,接收一个 WebRTCConfig 类型的参数 config
|
|
17
|
-
super()
|
|
18
|
-
// 调用父类 EventEmitter 的构造函数
|
|
19
|
-
this.config = config
|
|
20
|
-
// 将传入的配置信息赋值给私有属性 config
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* 启动 WebSocket 连接,并注册各事件处理
|
|
25
|
-
*/
|
|
26
|
-
start() {
|
|
27
|
-
// 创建一个新的 WebSocket 连接,使用配置中的信号服务器 URL
|
|
28
|
-
this.webSocket = new WebSocket(this.config.signalServerUrl)
|
|
29
|
-
// 当 WebSocket 连接成功打开时触发
|
|
30
|
-
this.webSocket.onopen = () => {
|
|
31
|
-
// 连接成功后发送 SignIn 消息
|
|
32
|
-
this.config.traceId = generateRandomString(16)
|
|
33
|
-
if (this.config.connectorType === ConnectorType.LanForwarding) {
|
|
34
|
-
this.sendGroupSignInMessage()
|
|
35
|
-
}
|
|
36
|
-
else {
|
|
37
|
-
this.sendSignInMessage()
|
|
38
|
-
}
|
|
39
|
-
// 设置 4 秒超时,如果在超时前没有收到 Offer 消息,则触发 notAvailable 事件
|
|
40
|
-
this.timeStart()
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// 当连接关闭时触发 close 事件
|
|
44
|
-
this.webSocket.onclose = () => {
|
|
45
|
-
this.webSocket = null
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// 当发生错误时触发 wsError 事件
|
|
49
|
-
this.webSocket.onerror = (event: Event) => {
|
|
50
|
-
Logger.error('错误日志:', `信令服务器连接失败或者已断开=======>${this.config.signalServerUrl}`)
|
|
51
|
-
this.emit(EmitType.webrtcError, createWebRtcError(FailCode.SOCKET, event))
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// 处理收到的消息
|
|
55
|
-
this.webSocket.onmessage = (event: MessageEvent) => {
|
|
56
|
-
const message = JSON.parse(event.data)
|
|
57
|
-
// 当收到 Offer 消息时,清除超时定时器
|
|
58
|
-
if (message.type === MessageType.Offer) {
|
|
59
|
-
this.clearTime()
|
|
60
|
-
}
|
|
61
|
-
// 通过事件通知外部收到消息
|
|
62
|
-
this.emit(EmitType.signalMessage, message)
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
timeStart() {
|
|
67
|
-
this.clearTime()
|
|
68
|
-
if (this.timeout === null) {
|
|
69
|
-
this.timeout = setTimeout(() => {
|
|
70
|
-
Logger.error('错误日志:', `远端云机未响应=======>${this.config.roomId}`)
|
|
71
|
-
this.emit(EmitType.webrtcError, createWebRtcError(FailCode.STATE_ERR, ''))
|
|
72
|
-
this.timeout = null
|
|
73
|
-
}, 6000)
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
clearTime() {
|
|
78
|
-
if (this.timeout) {
|
|
79
|
-
clearTimeout(this.timeout)
|
|
80
|
-
this.timeout = null
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* 关闭 WebSocket 连接,并通知外部
|
|
86
|
-
*/
|
|
87
|
-
close() {
|
|
88
|
-
if (this.webSocket) {
|
|
89
|
-
this.webSocket.onopen = null
|
|
90
|
-
// 清除所有事件处理函数
|
|
91
|
-
this.webSocket.onmessage = null
|
|
92
|
-
this.webSocket.onerror = null
|
|
93
|
-
this.webSocket.onclose = null
|
|
94
|
-
if (this.webSocket.readyState === WebSocket.OPEN || this.webSocket.readyState === WebSocket.CONNECTING) {
|
|
95
|
-
// 如果 WebSocket 连接处于打开或连接中状态,则关闭连接
|
|
96
|
-
this.webSocket.close(1000, 'Normal Closure')
|
|
97
|
-
}
|
|
98
|
-
this.webSocket = null
|
|
99
|
-
// 将 WebSocket 实例置为 null
|
|
100
|
-
}
|
|
101
|
-
this.removeAllListeners()
|
|
102
|
-
// 移除所有事件监听器
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* 发送消息到服务端
|
|
107
|
-
* @param message - 消息字符串
|
|
108
|
-
*/
|
|
109
|
-
sendMessage(message: string) {
|
|
110
|
-
if (this.webSocket && this.webSocket.readyState === WebSocket.OPEN) {
|
|
111
|
-
// 如果 WebSocket 连接存在且处于打开状态,则发送消息
|
|
112
|
-
this.webSocket.send(message)
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
sendSignInMessage() {
|
|
117
|
-
// 发送 SignIn 消息
|
|
118
|
-
const message = {
|
|
119
|
-
traceId: this.config.traceId,
|
|
120
|
-
type: SendType.SignIn,
|
|
121
|
-
targetId: this.config.myId,
|
|
122
|
-
identity: SendType.Identity,
|
|
123
|
-
turnServerUri: this.config.turnServerUri,
|
|
124
|
-
roomId: this.config.roomId,
|
|
125
|
-
token: this.config.token,
|
|
126
|
-
isGroup: this.config.isGroup,
|
|
127
|
-
turnKey: this.config.turnKey,
|
|
128
|
-
connectorType: this.config.connectorType,
|
|
129
|
-
}
|
|
130
|
-
const jsonMessage = JSON.stringify(message)
|
|
131
|
-
Logger.debug("调试日志:", `屏控登录信息====>${jsonMessage}`)
|
|
132
|
-
this.sendMessage(jsonMessage)
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
sendSwitchControlMessage(roomId: string, connectorType: ConnectorType) {
|
|
136
|
-
const message = {
|
|
137
|
-
type: SendType.SwitchControl,
|
|
138
|
-
identity: SendType.Identity,
|
|
139
|
-
roomId: roomId,
|
|
140
|
-
connectorType: connectorType,
|
|
141
|
-
targetId: this.config.myId,
|
|
142
|
-
}
|
|
143
|
-
const jsonMessage = JSON.stringify(message)
|
|
144
|
-
Logger.debug("调试日志:", `切换主控信令消息====>${jsonMessage}`)
|
|
145
|
-
this.sendMessage(jsonMessage)
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
sendSwitchControlToMainMessage(roomId: string) {
|
|
149
|
-
const message = {
|
|
150
|
-
type: SendType.SwitchControlToMain,
|
|
151
|
-
identity: SendType.Identity,
|
|
152
|
-
groupId: this.config.groupId,
|
|
153
|
-
turnKey: this.config.turnKey,
|
|
154
|
-
roomId: roomId,
|
|
155
|
-
traceId: generateRandomString(16),
|
|
156
|
-
}
|
|
157
|
-
const jsonMessage = JSON.stringify(message)
|
|
158
|
-
Logger.debug("调试日志:", `切换主控信令消息====>${jsonMessage}`)
|
|
159
|
-
this.sendMessage(jsonMessage)
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
sendGroupAcceptControl(roomIds: string[], acceptControl: boolean) {
|
|
163
|
-
const message = {
|
|
164
|
-
type: SendType.GroupAcceptControl,
|
|
165
|
-
targetId: this.config.myId,
|
|
166
|
-
identity: SendType.Identity,
|
|
167
|
-
roomIds: roomIds,
|
|
168
|
-
isAccept: acceptControl,
|
|
169
|
-
}
|
|
170
|
-
const jsonMessage = JSON.stringify(message)
|
|
171
|
-
Logger.debug("调试日志:", `修改控制状态====>${jsonMessage}`)
|
|
172
|
-
this.sendMessage(jsonMessage)
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
sendGroupAction(action: string) {
|
|
176
|
-
const tid = generateRandomString(16)
|
|
177
|
-
const message = {
|
|
178
|
-
type: SendType.GroupSendAction,
|
|
179
|
-
identity: SendType.Identity,
|
|
180
|
-
targetId: this.config.myId,
|
|
181
|
-
groupId: this.config.groupId,
|
|
182
|
-
action: action,
|
|
183
|
-
epSendAt: Date.now(),
|
|
184
|
-
traceId: tid,
|
|
185
|
-
}
|
|
186
|
-
const jsonMessage = JSON.stringify(message)
|
|
187
|
-
Logger.debug("调试日志:", `发送群控事件====>${jsonMessage}`)
|
|
188
|
-
this.sendMessage(jsonMessage)
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
sendGroupSignInMessage() {
|
|
192
|
-
const message = {
|
|
193
|
-
traceId: this.config.traceId,
|
|
194
|
-
type: SendType.GroupSignIn,
|
|
195
|
-
connectorType: 'websocket',
|
|
196
|
-
identity: SendType.Identity,
|
|
197
|
-
turnServerUri: this.config.turnServerUri,
|
|
198
|
-
mainRoomIdOfGroup: this.config.mainRoomIdOfGroup,
|
|
199
|
-
subRoomIdsOfGroup: this.config.subRoomIdsOfGroup,
|
|
200
|
-
turnKey: this.config.turnKey,
|
|
201
|
-
groupId: 'group_0001',
|
|
202
|
-
token: this.config.token,
|
|
203
|
-
}
|
|
204
|
-
const jsonMessage = JSON.stringify(message)
|
|
205
|
-
Logger.debug("调试日志:", `群控登录信息====>${jsonMessage}`)
|
|
206
|
-
this.sendMessage(jsonMessage)
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
function generateRandomString(length: number): string {
|
|
211
|
-
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
|
|
212
|
-
let result = ''
|
|
213
|
-
const charsLength = chars.length
|
|
214
|
-
|
|
215
|
-
for (let i = 0; i < length; i++) {
|
|
216
|
-
const randomIndex = Math.floor(Math.random() * charsLength)
|
|
217
|
-
result += chars[randomIndex]
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
return result
|
|
221
|
-
}
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
// FileTypeUtils.ts
|
|
2
|
-
export type FileKind = 'video' | 'image' | 'unknown'
|
|
3
|
-
|
|
4
|
-
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
|
|
5
|
-
export class FileTypeUtils {
|
|
6
|
-
/** 读取文件头并判断类型 */
|
|
7
|
-
static async detectFileType(file: File): Promise<FileKind> {
|
|
8
|
-
const buffer = await file.slice(0, 16).arrayBuffer()
|
|
9
|
-
const bytes = new Uint8Array(buffer)
|
|
10
|
-
|
|
11
|
-
// JPEG: FF D8 FF
|
|
12
|
-
if (bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff) {
|
|
13
|
-
return 'image'
|
|
14
|
-
}
|
|
15
|
-
// PNG: 89 50 4E 47
|
|
16
|
-
if (bytes[0] === 0x89 && bytes[1] === 0x50 && bytes[2] === 0x4e && bytes[3] === 0x47) {
|
|
17
|
-
return 'image'
|
|
18
|
-
}
|
|
19
|
-
// GIF: "GIF87a" or "GIF89a"
|
|
20
|
-
if (
|
|
21
|
-
bytes[0] === 0x47
|
|
22
|
-
&& bytes[1] === 0x49
|
|
23
|
-
&& bytes[2] === 0x46
|
|
24
|
-
&& bytes[3] === 0x38
|
|
25
|
-
) {
|
|
26
|
-
return 'image'
|
|
27
|
-
}
|
|
28
|
-
// WebP: "RIFF" .... "WEBP"
|
|
29
|
-
if (
|
|
30
|
-
bytes[0] === 0x52 // R
|
|
31
|
-
&& bytes[1] === 0x49 // I
|
|
32
|
-
&& bytes[2] === 0x46 // F
|
|
33
|
-
&& bytes[3] === 0x46 // F
|
|
34
|
-
&& bytes[8] === 0x57 // W
|
|
35
|
-
&& bytes[9] === 0x45 // E
|
|
36
|
-
&& bytes[10] === 0x42 // B
|
|
37
|
-
&& bytes[11] === 0x50 // P
|
|
38
|
-
) {
|
|
39
|
-
return 'image'
|
|
40
|
-
}
|
|
41
|
-
// MP4/QuickTime: ftyp
|
|
42
|
-
if (String.fromCharCode(...bytes.slice(4, 8)) === 'ftyp') {
|
|
43
|
-
return 'video'
|
|
44
|
-
}
|
|
45
|
-
// WebM / Matroska: 1A 45 DF A3
|
|
46
|
-
if (
|
|
47
|
-
bytes[0] === 0x1a
|
|
48
|
-
&& bytes[1] === 0x45
|
|
49
|
-
&& bytes[2] === 0xdf
|
|
50
|
-
&& bytes[3] === 0xa3
|
|
51
|
-
) {
|
|
52
|
-
return 'video'
|
|
53
|
-
}
|
|
54
|
-
// Ogg: "OggS"
|
|
55
|
-
if (
|
|
56
|
-
bytes[0] === 0x4f
|
|
57
|
-
&& bytes[1] === 0x67
|
|
58
|
-
&& bytes[2] === 0x67
|
|
59
|
-
&& bytes[3] === 0x53
|
|
60
|
-
) {
|
|
61
|
-
return 'video'
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return 'unknown'
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export const formattedTime = (timestamp: number): string => {
|
|
69
|
-
const date = new Date(timestamp)
|
|
70
|
-
const h = String(date.getHours()).padStart(2, '0')
|
|
71
|
-
const m = String(date.getMinutes()).padStart(2, '0')
|
|
72
|
-
const s = String(date.getSeconds()).padStart(2, '0')
|
|
73
|
-
const ms = String(date.getMilliseconds()).padStart(3, '0')
|
|
74
|
-
return `${h}:${m}:${s}.${ms}`
|
|
75
|
-
}
|
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
// 定义 Android 键盘状态位常量
|
|
2
|
-
const META_SHIFT_ON = 1 // Shift 键
|
|
3
|
-
const META_ALT_ON = 2 // Alt 键
|
|
4
|
-
const META_CTRL_ON = 4096 // Ctrl 键
|
|
5
|
-
const META_META_ON = 65536 // Meta 键
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* 根据浏览器 KeyboardEvent 计算 Android 平台的 meta 状态值
|
|
9
|
-
* @param event - 浏览器的 KeyboardEvent
|
|
10
|
-
* @returns 计算后的 meta 状态
|
|
11
|
-
*/
|
|
12
|
-
export const computeAndroidMetaState = (event: KeyboardEvent): number => {
|
|
13
|
-
let state = 0
|
|
14
|
-
if (event.shiftKey) state |= META_SHIFT_ON
|
|
15
|
-
if (event.altKey) state |= META_ALT_ON
|
|
16
|
-
if (event.ctrlKey) state |= META_CTRL_ON
|
|
17
|
-
if (event.metaKey) state |= META_META_ON
|
|
18
|
-
return state
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// 定义 KeyCodeMap 类型(键名映射为 Android KeyCode)
|
|
22
|
-
const KeyCodeMap: Record<string, number> = {
|
|
23
|
-
// 数字键
|
|
24
|
-
Digit1: 8,
|
|
25
|
-
Digit2: 9,
|
|
26
|
-
Digit3: 10,
|
|
27
|
-
Digit4: 11,
|
|
28
|
-
Digit5: 12,
|
|
29
|
-
Digit6: 13,
|
|
30
|
-
Digit7: 14,
|
|
31
|
-
Digit8: 15,
|
|
32
|
-
Digit9: 16,
|
|
33
|
-
Digit0: 7,
|
|
34
|
-
|
|
35
|
-
// 符号键(主键盘区)
|
|
36
|
-
Minus: 69,
|
|
37
|
-
Equal: 70,
|
|
38
|
-
Backquote: 68,
|
|
39
|
-
BracketLeft: 71,
|
|
40
|
-
BracketRight: 72,
|
|
41
|
-
Backslash: 73,
|
|
42
|
-
Semicolon: 74,
|
|
43
|
-
Quote: 75,
|
|
44
|
-
Comma: 55,
|
|
45
|
-
Period: 56,
|
|
46
|
-
Slash: 76,
|
|
47
|
-
|
|
48
|
-
// 功能键
|
|
49
|
-
Escape: 111,
|
|
50
|
-
Backspace: 67,
|
|
51
|
-
Tab: 61,
|
|
52
|
-
Enter: 66,
|
|
53
|
-
Space: 62,
|
|
54
|
-
CapsLock: 115,
|
|
55
|
-
|
|
56
|
-
// 修饰键
|
|
57
|
-
ShiftLeft: 59,
|
|
58
|
-
ShiftRight: 60,
|
|
59
|
-
ControlLeft: 113,
|
|
60
|
-
ControlRight: 114,
|
|
61
|
-
AltLeft: 57,
|
|
62
|
-
AltRight: 58,
|
|
63
|
-
MetaLeft: 117,
|
|
64
|
-
MetaRight: 118,
|
|
65
|
-
|
|
66
|
-
// 方向键
|
|
67
|
-
ArrowUp: 19,
|
|
68
|
-
ArrowDown: 20,
|
|
69
|
-
ArrowLeft: 21,
|
|
70
|
-
ArrowRight: 22,
|
|
71
|
-
|
|
72
|
-
// 字母键(物理位置映射)
|
|
73
|
-
KeyA: 29,
|
|
74
|
-
KeyB: 30,
|
|
75
|
-
KeyC: 31,
|
|
76
|
-
KeyD: 32,
|
|
77
|
-
KeyE: 33,
|
|
78
|
-
KeyF: 34,
|
|
79
|
-
KeyG: 35,
|
|
80
|
-
KeyH: 36,
|
|
81
|
-
KeyI: 37,
|
|
82
|
-
KeyJ: 38,
|
|
83
|
-
KeyK: 39,
|
|
84
|
-
KeyL: 40,
|
|
85
|
-
KeyM: 41,
|
|
86
|
-
KeyN: 42,
|
|
87
|
-
KeyO: 43,
|
|
88
|
-
KeyP: 44,
|
|
89
|
-
KeyQ: 45,
|
|
90
|
-
KeyR: 46,
|
|
91
|
-
KeyS: 47,
|
|
92
|
-
KeyT: 48,
|
|
93
|
-
KeyU: 49,
|
|
94
|
-
KeyV: 50,
|
|
95
|
-
KeyW: 51,
|
|
96
|
-
KeyX: 52,
|
|
97
|
-
KeyY: 53,
|
|
98
|
-
KeyZ: 54,
|
|
99
|
-
|
|
100
|
-
// 功能键扩展
|
|
101
|
-
F1: 131,
|
|
102
|
-
F2: 132,
|
|
103
|
-
F3: 133,
|
|
104
|
-
F4: 134,
|
|
105
|
-
F5: 135,
|
|
106
|
-
F6: 136,
|
|
107
|
-
F7: 137,
|
|
108
|
-
F8: 138,
|
|
109
|
-
F9: 139,
|
|
110
|
-
F10: 140,
|
|
111
|
-
F11: 141,
|
|
112
|
-
F12: 142,
|
|
113
|
-
|
|
114
|
-
// 数字小键盘
|
|
115
|
-
Numpad0: 7,
|
|
116
|
-
Numpad1: 8,
|
|
117
|
-
Numpad2: 9,
|
|
118
|
-
Numpad3: 10,
|
|
119
|
-
Numpad4: 11,
|
|
120
|
-
Numpad5: 12,
|
|
121
|
-
Numpad6: 13,
|
|
122
|
-
Numpad7: 14,
|
|
123
|
-
Numpad8: 15,
|
|
124
|
-
Numpad9: 16,
|
|
125
|
-
NumpadAdd: 157,
|
|
126
|
-
NumpadSubtract: 156,
|
|
127
|
-
NumpadMultiply: 155,
|
|
128
|
-
NumpadDivide: 154,
|
|
129
|
-
NumpadDecimal: 158,
|
|
130
|
-
NumpadEnter: 66,
|
|
131
|
-
|
|
132
|
-
// 其他特殊键
|
|
133
|
-
Insert: 124,
|
|
134
|
-
Delete: 112,
|
|
135
|
-
Home: 122,
|
|
136
|
-
End: 123,
|
|
137
|
-
PageUp: 92,
|
|
138
|
-
PageDown: 93,
|
|
139
|
-
ScrollLock: 116,
|
|
140
|
-
Pause: 121,
|
|
141
|
-
ContextMenu: 117,
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
export interface KeyEventData {
|
|
145
|
-
keyCode: number
|
|
146
|
-
meta: number
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* 根据 KeyboardEvent 生成 Android 平台下的按键数据
|
|
151
|
-
* @param event - 浏览器的 KeyboardEvent 对象
|
|
152
|
-
* @returns 包含 androidKeyCode 和 metaState 的对象
|
|
153
|
-
*/
|
|
154
|
-
export const getKeyEventData = (event: KeyboardEvent): KeyEventData => {
|
|
155
|
-
// 查找映射的 Android 键码,如果找不到返回 -1
|
|
156
|
-
const keyCode = KeyCodeMap[event.code] ?? -1
|
|
157
|
-
const meta = computeAndroidMetaState(event)
|
|
158
|
-
return {
|
|
159
|
-
keyCode,
|
|
160
|
-
meta,
|
|
161
|
-
}
|
|
162
|
-
}
|