yjz-web-sdk 1.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/README.md ADDED
@@ -0,0 +1,489 @@
1
+ # yjz-web-sdk
2
+
3
+ <div align=center>
4
+ <img src=public/vite.svg/>
5
+ </div>
6
+
7
+ 针对于亚矩阵项目的云手机投屏和屏幕控制的插件
8
+
9
+ ## 功能支持
10
+ * 支持亚矩阵平台webrtc协议的投屏
11
+ * 支持连接成功后远程视频流和音频流的回调
12
+ * 支持屏幕触控事件,包含电脑模式和手机模式(点击、移动、长按等)
13
+ * 支持电脑键盘映射功能(C+V、C+X、C+Z等)
14
+ * 支持手机端的常用快捷键(back、home、recent、音量等)
15
+ * 支持连接错误回调(分不通错误码返回)
16
+ * 支持webrtc的推拉流实时信息统计
17
+ * 支持webrtc连接状态的回调
18
+ * 支持Vue3
19
+
20
+ > 一句话描述SDK:
21
+ >
22
+ > 没有纯用户界面,都是使用的webrtc协议,纯js实现,没有依赖任何第三方库,可以很方便的集成到项目中,并且支持Vue3
23
+
24
+
25
+
26
+ ```shell
27
+ # install for vue3
28
+ npm install yjz-web-sdk --save
29
+ ```
30
+
31
+ ## 远程音视频流回调
32
+
33
+ ```ts
34
+ sdk.value.on(EmitType.streamTrack, (track: MediaStreamTrack) => {
35
+ if (!remoteVideoElement.value) return;
36
+ if (!remoteVideoElement.value.srcObject) {
37
+ remoteVideoElement.value.srcObject = new MediaStream();
38
+ }
39
+ remoteVideoElement.value.playsInline = true;
40
+ remoteVideoElement.value.setAttribute('webkit-playsinline', 'true');
41
+ (remoteVideoElement.value.srcObject as MediaStream).addTrack(track);
42
+ });
43
+ ```
44
+
45
+ ## Webrtc连接状态
46
+
47
+ ```ts
48
+ type RTCIceConnectionState = "checking" | "closed" | "completed" | "connected" | "disconnected" | "failed" | "new";
49
+
50
+ sdk.value.on(EmitType.iceConnectionState, (state: string) => {
51
+ console.log("iceConnectionState====>", state);
52
+ });
53
+ ```
54
+
55
+ ## Webrtc统计信息
56
+ | 左对齐 | 右对齐 |
57
+ | :-----|:-----------|
58
+ | connectionType | webrtc连接模式 |
59
+ | framesPerSecond | 每秒帧率 |
60
+ | currentRoundTripTime | 每秒帧率 |
61
+ | lostRate | 丢包率 |
62
+ | bitrate | 视频码率 |
63
+ | averageDecodeTime | 解码时间 |
64
+
65
+ ```ts
66
+ sdk.value.on(EmitType.statisticInfo, (info: any) => {
67
+ console.log("statisticInfo====>", info);
68
+ });
69
+ ```
70
+
71
+ ## 错误回调
72
+ | 错误码 | 左对齐 | 等级 |
73
+ |:------|:--------------|:--:|
74
+ | 10001 | WebSocket连接失败 | 高 |
75
+ | 10002 | WebSocket已关闭 | 高 |
76
+ | 10003 | 创建offer失败 | 高 |
77
+ | 10004 | 处理offer失败 | 高 |
78
+ | 10005 | 创建answer失败 | 高 |
79
+ | 10006 | 处理answer失败 | 高 |
80
+ | 10007 | 设置本地描述失败 | 高 |
81
+ | 10008 | 设置远端描述失败 | 高 |
82
+ | 10009 | 处理 ICE 交换失败 | 高 |
83
+ | 10010 | ICE 状态异常 | 高 |
84
+ | 10011 | 摄像头异常 | 高 |
85
+ | 10012 | 云机已关机或者不存在 | 高 |
86
+ | 10013 | 信令通道错误 | 低 |
87
+ | 10014 | webrtc统计信息获取失败 | 低 |
88
+
89
+ ```ts
90
+ sdk.value.on(EmitType.webrtcError, (error: any) => {
91
+ console.log("webrtcError====>", error);
92
+ });
93
+ ```
94
+
95
+
96
+ **Vue3**
97
+ 使用示例
98
+
99
+ ```vue
100
+ <template>
101
+ <div ref="videoFull" class="flex" style="height: 100vh;background: black;position: relative;width:100vw">
102
+ <div ref="videoContainer" class="flex flex-1 items-center justify-center" style="position:relative;overflow:hidden">
103
+ <div ref="keyboardArea" @mouseenter="startListening" @mouseleave="stopListening" tabindex="0"
104
+ class="vContainer"
105
+ :style="{height:videoSize.height + 'px', width:videoSize.width + 'px', transform: `rotate(${videoAngle}deg)`}" style="position: relative">
106
+ <video ref="remoteVideoElement" @touchstart="onTouchStart"
107
+ @touchmove="onTouchMove"
108
+ @touchend="onTouchEnd" @pointerenter="handleMouseEnter"
109
+ @pointerdown="handleMouseDown" @pointermove="handleMouseMove"
110
+ @pointerup="handleMouseUp" @pointerleave="handleMouseLeave" id="phoneVideo" style="width: 100%;height: 100%;" autoplay playsinline muted :disablePictureInPicture="true"></video>
111
+ </div>
112
+ </div>
113
+ </div>
114
+ </template>
115
+
116
+ <script setup lang="ts">
117
+ import { ref, onMounted, onUnmounted, computed } from "vue";
118
+ import {
119
+ WebRTCSdk,
120
+ getKeyEventData,
121
+ transformCoordinate,
122
+ valueToPercentage,
123
+ ActionType,
124
+ ChannelDataType,
125
+ InputData,
126
+ KeyEventData,
127
+ TouchData,
128
+ ContainerDirection,
129
+ EmitType
130
+ } from 'yjz-web-sdk';
131
+
132
+ interface Dimension {
133
+ width: number;
134
+ height: number;
135
+ }
136
+
137
+ const dimensions = ref<Dimension>({ width: 0, height: 0 });
138
+ const videoContainer = ref<HTMLElement | null>(null);
139
+ const videoFull = ref<HTMLElement | null>(null);
140
+ const screenStatus = ref(false);
141
+ const isMobile = ref(false);
142
+ const pointerList = ref<number[]>(new Array(20).fill(0));
143
+ const pointerDownTime = ref<number[]>(new Array(10).fill(0));
144
+ const bound = ref(100);
145
+ const remoteVideoElement = ref<HTMLVideoElement | null>(null);
146
+ const isDragging = ref(false);
147
+ const widthRadio = ref(0);
148
+ const heightRadio = ref(0);
149
+ const sdk = ref<WebRTCSdk | null>(null);
150
+ const streamAngle = ref(0);
151
+ const videoAngle = ref(0);
152
+ const cloudDeviceSize = ref<Dimension>({ width: 0, height: 0 });
153
+ const resizeObserver = ref<ResizeObserver | null>(null);
154
+ const remoteVideo = ref<Partial<Dimension>>({});
155
+ const videoDimension = ref<Dimension>({ width: 0, height: 0 });
156
+ const connectInfo = ref('');
157
+ const listening = ref(false);
158
+ const keyboardArea = ref<HTMLElement | null>(null);
159
+ const isComposing = ref(false);
160
+ const endX = ref(0);
161
+ const endY = ref(0);
162
+
163
+ const sendKeyBoardData = (value: { androidKeyCode: number; metaState: number }) => {
164
+ if (sdk.value) {
165
+ const keyEvent = new KeyEventData(value.androidKeyCode, value.metaState);
166
+ sdk.value.sendChannelData(ChannelDataType.ActionInput, keyEvent);
167
+ }
168
+ };
169
+
170
+ const sendKeyBoardData1 = (key: number) => {
171
+ if (sdk.value) {
172
+ const keyEvent = new KeyEventData(key, 0);
173
+ sdk.value.sendChannelData(ChannelDataType.ActionInput, keyEvent);
174
+ }
175
+ };
176
+
177
+ const sendChineseData = (value: string) => {
178
+ if (sdk.value) {
179
+ const inputData = new InputData(value);
180
+ sdk.value.sendChannelData(ChannelDataType.ActionChinese, inputData);
181
+ }
182
+ };
183
+
184
+ const startListening = () => {
185
+ listening.value = true;
186
+ document.addEventListener('keydown', handleKeyDown);
187
+ };
188
+
189
+ const stopListening = () => {
190
+ listening.value = false;
191
+ document.removeEventListener('keydown', handleKeyDown);
192
+ };
193
+
194
+ const handleKeyDown = (event: KeyboardEvent) => {
195
+ if (!listening.value || isComposing.value) return;
196
+ const androidEvent = getKeyEventData(event);
197
+ if (androidEvent.androidKeyCode === -1) return;
198
+ sendKeyBoardData(androidEvent);
199
+ };
200
+
201
+ const onTouchStart = (event: TouchEvent) => {
202
+ if (isMobile.value) {
203
+ const touches = Array.from(event.touches);
204
+ if (touches.length > 0) {
205
+ pointerDownTime.value[0] = event.timeStamp;
206
+ bound.value = Math.trunc(4 / widthRadio.value);
207
+ endX.value = touches[0].clientX;
208
+ endY.value = touches[0].clientY;
209
+ createTouchPacket({
210
+ clientX: endX.value,
211
+ clientY: endY.value,
212
+ timeStamp: event.timeStamp,
213
+ }, ActionType.ACTION_DOWN, 0);
214
+ }
215
+ }
216
+ };
217
+
218
+ const onTouchMove = (event: TouchEvent) => {
219
+ if (isMobile.value) {
220
+ const touches = Array.from(event.touches);
221
+ if (touches.length > 0) {
222
+ endX.value = touches[0].clientX;
223
+ endY.value = touches[0].clientY;
224
+ createTouchPacket({
225
+ clientX: endX.value,
226
+ clientY: endY.value,
227
+ timeStamp: event.timeStamp,
228
+ }, ActionType.ACTION_MOVE, 0);
229
+ }
230
+ }
231
+ };
232
+
233
+ const onTouchEnd = (event: TouchEvent) => {
234
+ if (isMobile.value) {
235
+ createTouchPacket({
236
+ clientX: endX.value,
237
+ clientY: endY.value,
238
+ timeStamp: event.timeStamp,
239
+ }, ActionType.ACTION_UP, 0);
240
+ }
241
+ };
242
+
243
+ const handleMouseDown = (event: MouseEvent) => {
244
+ if (isMobile.value) return;
245
+ isDragging.value = true;
246
+ pointerDownTime.value[0] = event.timeStamp;
247
+ bound.value = Math.trunc(4 / widthRadio.value);
248
+ createTouchPacket(event, ActionType.ACTION_DOWN, 0);
249
+ };
250
+
251
+ const handleMouseMove = (event: MouseEvent) => {
252
+ if (!isDragging.value || isMobile.value) return;
253
+ createTouchPacket(event, ActionType.ACTION_MOVE, 0);
254
+ };
255
+
256
+ const handleMouseEnter = (event: MouseEvent) => {
257
+ if (isMobile.value) return;
258
+ isDragging.value = true;
259
+ };
260
+
261
+ const handleMouseUp = (event: MouseEvent) => {
262
+ if (!isDragging.value || isMobile.value) return;
263
+ isDragging.value = false;
264
+ createTouchPacket(event, ActionType.ACTION_UP, 0);
265
+ };
266
+
267
+ const handleMouseLeave = (event: MouseEvent) => {
268
+ if (!isDragging.value) return;
269
+ isDragging.value = false;
270
+ };
271
+
272
+ const createTouchPacket = (
273
+ event: MouseEvent | { clientX: number; clientY: number; timeStamp: number },
274
+ action: number,
275
+ p: number
276
+ ) => {
277
+ if (!remoteVideoElement.value) return;
278
+
279
+ const offsetTime = Math.trunc((event.timeStamp - pointerDownTime.value[0]) / 2);
280
+ const rect = remoteVideoElement.value.getBoundingClientRect();
281
+ let x = event.clientX - rect.left;
282
+ let y = event.clientY - rect.top;
283
+
284
+ const cloudWidth = cloudDeviceSize.value.width;
285
+ const cloudHeight = cloudDeviceSize.value.height;
286
+ const result = transformCoordinate(
287
+ rect.width, rect.height, cloudWidth, cloudHeight,
288
+ videoAngle.value, streamAngle.value, x, y
289
+ );
290
+
291
+ if (!result || result.length < 2) return;
292
+ x = result[0];
293
+ y = result[1];
294
+
295
+ if (action === ActionType.ACTION_MOVE) {
296
+ const flipY = pointerList.value[10 + p] - y;
297
+ const flipX = pointerList.value[p] - x;
298
+ if (Math.abs(flipY) < bound.value && Math.abs(flipX) < bound.value) return;
299
+ }
300
+
301
+ pointerList.value[p] = x;
302
+ pointerList.value[10 + p] = y;
303
+
304
+ const percentResult = valueToPercentage(
305
+ rect.width, rect.height, cloudWidth, cloudHeight,
306
+ videoAngle.value, streamAngle.value, x, y
307
+ );
308
+
309
+ createTouchEvent(action, p, percentResult[0], percentResult[1], offsetTime);
310
+ };
311
+
312
+ const createTouchEvent = (action: number, p: number, x: number, y: number, offsetTime: number) => {
313
+ const touchData = new TouchData(action, p, x, y, offsetTime);
314
+ sendChannelData(touchData);
315
+ };
316
+
317
+ const videoSize = computed<Dimension>(() => {
318
+ let width = 0;
319
+ let height = 0;
320
+ if (dimensions.value.width > 0 && dimensions.value.height > 0) {
321
+ const isRotated = videoAngle.value % 180 !== 0;
322
+ const containerWidth = isRotated ? dimensions.value.height : dimensions.value.width;
323
+ const containerHeight = isRotated ? dimensions.value.width : dimensions.value.height;
324
+
325
+ if (remoteVideo.value.width && remoteVideo.value.height) {
326
+ const aspect = remoteVideo.value.width / remoteVideo.value.height;
327
+ const videoHeight = containerWidth / aspect;
328
+ if (videoHeight > containerHeight) {
329
+ height = containerHeight;
330
+ width = height * aspect;
331
+ } else {
332
+ width = containerWidth;
333
+ height = videoHeight;
334
+ }
335
+ widthRadio.value = remoteVideo.value.width / width;
336
+ heightRadio.value = remoteVideo.value.height / height;
337
+ }
338
+ }
339
+
340
+ videoDimension.value = { width, height };
341
+ return { width, height };
342
+ });
343
+
344
+ const initVideoContainer = () => {
345
+ resizeObserver.value = new ResizeObserver((entries) => {
346
+ for (const entry of entries) {
347
+ const { width, height } = entry.contentRect;
348
+ dimensions.value = { width, height };
349
+ }
350
+ });
351
+
352
+ if (videoContainer.value) {
353
+ resizeObserver.value.observe(videoContainer.value);
354
+ }
355
+
356
+ if (remoteVideoElement.value) {
357
+ remoteVideoElement.value.addEventListener('resize', () => {
358
+ if (!remoteVideoElement.value) return;
359
+ const actualWidth = remoteVideoElement.value.videoWidth;
360
+ const actualHeight = remoteVideoElement.value.videoHeight;
361
+ remoteVideo.value = { width: actualWidth, height: actualHeight };
362
+ screenStatus.value = true;
363
+ });
364
+ }
365
+ };
366
+
367
+ const startConnect = () => {
368
+ const config = {
369
+ signalServerUrl: "ws://cs11.nuliapp.com/v1/signaling",
370
+ roomId: "io_service_13",
371
+ turnServerUri: "turn:btweb.yajuzhen.com:3478"
372
+ };
373
+ sdk.value = new WebRTCSdk(config);
374
+ sdk.value.startConnection();
375
+
376
+ sdk.value.on(EmitType.streamTrack, (track: MediaStreamTrack) => {
377
+ if (!remoteVideoElement.value) return;
378
+ if (!remoteVideoElement.value.srcObject) {
379
+ remoteVideoElement.value.srcObject = new MediaStream();
380
+ }
381
+ remoteVideoElement.value.playsInline = true;
382
+ remoteVideoElement.value.setAttribute('webkit-playsinline', 'true');
383
+ (remoteVideoElement.value.srcObject as MediaStream).addTrack(track);
384
+ });
385
+
386
+ sdk.value.on(EmitType.iceConnectionState, (state: string) => {
387
+ console.log("iceConnectionState====>", state);
388
+ });
389
+
390
+ sdk.value.on(EmitType.cloudStatusChanged, (cloudStatus: any) => {
391
+ console.error("cloudStatusChanged====>", cloudStatus);
392
+ streamAngle.value = cloudStatus.direction === ContainerDirection.Vertical ? 0 : -90;
393
+ cloudDeviceSize.value = {
394
+ width: cloudStatus.screenWidth,
395
+ height: cloudStatus.screenHeight
396
+ };
397
+ });
398
+
399
+ sdk.value.on(EmitType.webrtcError, (error: any) => {
400
+ console.log("webrtcError====>", error);
401
+ });
402
+
403
+ sdk.value.on(EmitType.statisticInfo, (info: any) => {
404
+ console.log("statisticInfo====>", info);
405
+ });
406
+ };
407
+
408
+ const stopConnect = () => {
409
+ screenStatus.value = false;
410
+ if (sdk.value) {
411
+ sdk.value.stop();
412
+ sdk.value = null;
413
+ }
414
+
415
+ if (remoteVideoElement.value) {
416
+ remoteVideoElement.value.removeEventListener("resize", () => {});
417
+ if (remoteVideoElement.value.srcObject) {
418
+ (remoteVideoElement.value.srcObject as MediaStream).getTracks().forEach(track => track.stop());
419
+ }
420
+ remoteVideoElement.value.srcObject = null;
421
+ }
422
+
423
+ connectInfo.value = "";
424
+ };
425
+
426
+ const sendChannelData = (data: TouchData) => {
427
+ if (sdk.value) {
428
+ sdk.value.sendChannelData(ChannelDataType.ClickData, data);
429
+ }
430
+ };
431
+
432
+ const checkDeviceMode = () => {
433
+ isMobile.value = "ontouchstart" in window || navigator.maxTouchPoints > 0;
434
+ };
435
+
436
+ const checkTouchSupport = (event: PointerEvent) => {
437
+ isMobile.value = event.pointerType === "touch";
438
+ };
439
+
440
+ const handleClick = () => {
441
+ if (remoteVideoElement.value?.srcObject) {
442
+ remoteVideoElement.value.muted = false;
443
+ }
444
+ };
445
+
446
+ onMounted(() => {
447
+ checkDeviceMode();
448
+ window.addEventListener("resize", checkDeviceMode);
449
+ window.addEventListener("pointerdown", checkTouchSupport);
450
+ initVideoContainer();
451
+ startConnect();
452
+ document.addEventListener("click", handleClick);
453
+ });
454
+
455
+ onUnmounted(() => {
456
+ stopConnect();
457
+ document.removeEventListener("click", handleClick);
458
+ window.removeEventListener("resize", checkDeviceMode);
459
+ window.removeEventListener("pointerdown", checkTouchSupport);
460
+ if (resizeObserver.value) {
461
+ resizeObserver.value.disconnect();
462
+ resizeObserver.value = null;
463
+ }
464
+ });
465
+
466
+
467
+
468
+ </script>
469
+
470
+ <style scoped>
471
+ .vContainer{
472
+ transition: transform .2s linear
473
+ }
474
+
475
+ .flex {
476
+ display: flex;
477
+ }
478
+ .flex-1 {
479
+ flex: 1 1 0%;
480
+ }
481
+ .items-center {
482
+ align-items: center;
483
+ }
484
+ .justify-center {
485
+ justify-content: center;
486
+ }
487
+
488
+ </style>
489
+ ```
@@ -0,0 +1,33 @@
1
+ import { EventEmitter } from "eventemitter3";
2
+ import { SignalingClient } from "./signal/SignalingClient.ts";
3
+ import { WebRTCClient } from "./rtc/WebRTCClient.ts";
4
+ import { WebRTCConfig } from "./rtc/WebRTCConfig.ts";
5
+ import type { WebRTCConfigOptions } from './rtc/WebRTCConfig';
6
+ import { ChannelDataType } from "./data/WebrtcDataType.ts";
7
+ export declare class WebRTCSdk extends EventEmitter {
8
+ config: WebRTCConfig;
9
+ signalingClient: SignalingClient;
10
+ webRTCClient: WebRTCClient;
11
+ constructor(options: WebRTCConfigOptions);
12
+ /** 开始连接 signal 服务 */
13
+ startConnection(): void;
14
+ /** 停止连接,并发送退出信令 */
15
+ stop(): void;
16
+ /** 开始推流:触发媒体采集与轨道添加 */
17
+ startPush(): Promise<void>;
18
+ /** 发送信道数据 */
19
+ sendChannelData(type: ChannelDataType, data: any): void;
20
+ /**
21
+ * 处理 signal 消息,根据不同消息类型分发到 webRTCClient 或直接触发 SDK 事件
22
+ * @param message 信令消息
23
+ */
24
+ private handleSignaling;
25
+ /** 发送 Offer 信令 */
26
+ sendOffer: (offerSdp: string) => void;
27
+ /** 发送 Answer 信令 */
28
+ sendAnswer: (answerSdp: string) => void;
29
+ /** 发送 ICE 候选信息 */
30
+ sendICEMessage: (ice: string) => void;
31
+ sendPong(): void;
32
+ sendSignOut(): void;
33
+ }
@@ -0,0 +1,28 @@
1
+ export declare enum MessageType {
2
+ Peers = "Peers",
3
+ Offer = "Offer",
4
+ Answer = "Answer",
5
+ IceCandidates = "IceCandidates",
6
+ SignOut = "SignOut",
7
+ NotAvailable = "NotAvailable",
8
+ Ping = "Ping",
9
+ Pong = "Pong"
10
+ }
11
+ export declare enum SendType {
12
+ SignIn = "SignIn",
13
+ SignOut = "SignOut",
14
+ Answer = "Answer",
15
+ Offer = "Offer",
16
+ IceCandidates = "IceCandidates",
17
+ Pong = "Pong",
18
+ Identity = "Web"
19
+ }
20
+ export declare enum IceConnectionState {
21
+ NEW = "new",
22
+ CHECKING = "checking",
23
+ CONNECTED = "connected",
24
+ COMPLETED = "completed",
25
+ DISCONNECTED = "disconnected",
26
+ FAILED = "failed",
27
+ CLOSED = "closed"
28
+ }
@@ -0,0 +1,113 @@
1
+ export declare enum ChannelDataType {
2
+ ClickData = "ClickData",
3
+ ClipboardData = "ClipboardData",
4
+ ActionCommand = "ActionCommand",
5
+ ActionInput = "ActionInput",
6
+ ActionChinese = "ActionChinese",
7
+ ActionRequestCloudDeviceInfo = "ActionRequestCloudDeviceInfo",
8
+ ActionClarity = "ActionClarity"
9
+ }
10
+ export declare const ActionType: Readonly<{
11
+ ACTION_DOWN: 0;
12
+ ACTION_MOVE: 2;
13
+ ACTION_UP: 1;
14
+ }>;
15
+ export type ActionType = typeof ActionType[keyof typeof ActionType];
16
+ export declare const StreamRotation: {
17
+ readonly ROTATION_0: 0;
18
+ readonly ROTATION_90: 1;
19
+ readonly ROTATION_180: 2;
20
+ readonly ROTATION_270: 3;
21
+ };
22
+ export type StreamRotation = typeof StreamRotation[keyof typeof StreamRotation];
23
+ export declare const ContainerDirection: {
24
+ readonly Vertical: 0;
25
+ readonly Horizontal: 1;
26
+ };
27
+ export type ContainerDirection = typeof ContainerDirection[keyof typeof ContainerDirection];
28
+ /**
29
+ * TouchData - 触摸数据类,包含动作类型、指针索引、坐标、偏移时间以及触摸类型
30
+ */
31
+ export declare class TouchData {
32
+ action: ActionType;
33
+ p: number;
34
+ x: number;
35
+ y: number;
36
+ offsetTime: number;
37
+ type: string;
38
+ constructor(action: ActionType, p: number, x: number, y: number, offsetTime: number, type?: string);
39
+ }
40
+ /**
41
+ * InputData - 文本输入数据类
42
+ */
43
+ export declare class InputData {
44
+ text: string;
45
+ constructor(text: string);
46
+ }
47
+ /**
48
+ * KeyEventData - 键盘事件数据类,包含键码及修饰状态
49
+ */
50
+ export declare class KeyEventData {
51
+ keyCode: number;
52
+ meta: number;
53
+ constructor(keyCode: number, meta: number);
54
+ }
55
+ export declare class ChannelData {
56
+ type: ChannelDataType;
57
+ data: string | null;
58
+ constructor(type: ChannelDataType, data?: string | null);
59
+ toString(): string;
60
+ /**
61
+ * 格式化数据
62
+ * 如果数据已经是字符串,则直接返回;否则使用 JSON.stringify() 转换为字符串
63
+ * @param data 待转换数据
64
+ * @returns 格式化后的字符串
65
+ */
66
+ private static formatData;
67
+ /**
68
+ * 生成点击数据
69
+ * @param touchData 触摸数据,可以是任意类型
70
+ */
71
+ static click(touchData: unknown): ChannelData;
72
+ /**
73
+ * 生成剪贴板数据
74
+ * @param data 剪贴板数据,可以是字符串或其他类型
75
+ */
76
+ static clipboard(data: unknown): ChannelData;
77
+ /**
78
+ * 生成输入数据
79
+ * @param data 输入数据对象
80
+ */
81
+ static input(data: unknown): ChannelData;
82
+ /**
83
+ * 生成中文输入数据
84
+ * @param data 中文输入数据
85
+ */
86
+ static chinese(data: unknown): ChannelData;
87
+ /**
88
+ * 生成请求云设备信息数据
89
+ */
90
+ static requestCloudDeviceInfo(): ChannelData;
91
+ /**
92
+ * 生成清晰度数据(clarity)
93
+ * @param data 清晰度数据
94
+ */
95
+ static clarity(data: unknown): ChannelData;
96
+ }
97
+ export interface RTCStatsReportExtended extends RTCStatsReport {
98
+ get(id: string): RTCStats | undefined;
99
+ }
100
+ export interface ScreenStats {
101
+ connectionType: string;
102
+ framesPerSecond: number;
103
+ currentRoundTripTime: number;
104
+ lostRate: number;
105
+ bitrate: string;
106
+ pliCount: number;
107
+ averageDecodeTime: number;
108
+ }
109
+ export interface CloudStatusPayload {
110
+ direction: number;
111
+ screenWidth: number;
112
+ screenHeight: number;
113
+ }
@@ -0,0 +1,38 @@
1
+ export declare enum FailCode {
2
+ SOCKET = 10001,
3
+ SOCKET_CLOSE = 10002,
4
+ CREATE_OFFER = 10003,
5
+ HANDLE_OFFER = 10004,
6
+ CREATE_ANSWER = 10005,
7
+ HANDLE_ANSWER = 10006,
8
+ LOCAL_DES = 10007,
9
+ REMOTE_DES = 10008,
10
+ HANDLE_ICE = 10009,
11
+ ICE_STATE = 10010,
12
+ CAMERA = 10011,
13
+ NOT_AVAILABLE = 10012,
14
+ DATACHANNEL_ERR = 10013,
15
+ STREAM_STATE = 10014
16
+ }
17
+ export declare const FailInfoMap: Record<FailCode, {
18
+ type: string;
19
+ description: string;
20
+ }>;
21
+ export interface WebRtcError {
22
+ code: FailCode;
23
+ type: string;
24
+ message: string;
25
+ rawError?: any;
26
+ }
27
+ export declare function createWebRtcError(code: FailCode, rawError?: any): WebRtcError;
28
+ export declare enum EmitType {
29
+ signalMessage = "signalMessage",
30
+ webrtcError = "webrtcError",
31
+ sendAnswer = "sendAnswer",
32
+ sendOffer = "sendOffer",
33
+ sendICEMessage = "sendICEMessage",
34
+ streamTrack = "streamTrack",
35
+ iceConnectionState = "iceConnectionState",
36
+ cloudStatusChanged = "cloudStatusChanged",
37
+ statisticInfo = "statisticInfo"
38
+ }