@chaomingd/design 0.0.68
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/LICENSE +21 -0
- package/README.md +23 -0
- package/dist/esm/CheckGroup/context.d.ts +9 -0
- package/dist/esm/CheckGroup/context.js +6 -0
- package/dist/esm/CheckGroup/hooks/useCheckGroup.d.ts +21 -0
- package/dist/esm/CheckGroup/hooks/useCheckGroup.js +180 -0
- package/dist/esm/CheckGroup/index.d.ts +9 -0
- package/dist/esm/CheckGroup/index.js +79 -0
- package/dist/esm/CheckGroup/type.d.ts +28 -0
- package/dist/esm/CheckGroup/type.js +1 -0
- package/dist/esm/Grid/index.d.ts +15 -0
- package/dist/esm/Grid/index.js +64 -0
- package/dist/esm/Grid/index.less +7 -0
- package/dist/esm/KaTeXRenderer/index.d.ts +9 -0
- package/dist/esm/KaTeXRenderer/index.js +33 -0
- package/dist/esm/KaTeXRenderer/index.less +0 -0
- package/dist/esm/PixelPlayer/index.d.ts +28 -0
- package/dist/esm/PixelPlayer/index.js +118 -0
- package/dist/esm/PixelPlayer/index.less +29 -0
- package/dist/esm/PixelPlayer/lib/peer-stream.d.ts +62 -0
- package/dist/esm/PixelPlayer/lib/peer-stream.js +876 -0
- package/dist/esm/PixelPlayer/useAutoLayoutContainer.d.ts +2 -0
- package/dist/esm/PixelPlayer/useAutoLayoutContainer.js +53 -0
- package/dist/esm/PixelPlayer/useVideoWithCustomEvent.d.ts +5 -0
- package/dist/esm/PixelPlayer/useVideoWithCustomEvent.js +37 -0
- package/dist/esm/Resizer/index.d.ts +17 -0
- package/dist/esm/Resizer/index.js +73 -0
- package/dist/esm/Resizer/index.less +73 -0
- package/dist/esm/SetupAntApp/index.d.ts +1 -0
- package/dist/esm/SetupAntApp/index.js +10 -0
- package/dist/esm/Transition/index.d.ts +4 -0
- package/dist/esm/Transition/index.js +132 -0
- package/dist/esm/Transition/type.d.ts +33 -0
- package/dist/esm/Transition/type.js +12 -0
- package/dist/esm/Transition/utils.d.ts +6 -0
- package/dist/esm/Transition/utils.js +15 -0
- package/dist/esm/UploadFile/components/IconButton/index.d.ts +11 -0
- package/dist/esm/UploadFile/components/IconButton/index.js +26 -0
- package/dist/esm/UploadFile/components/IconButton/index.less +58 -0
- package/dist/esm/UploadFile/components/Slash.d.ts +2 -0
- package/dist/esm/UploadFile/components/Slash.js +15 -0
- package/dist/esm/UploadFile/components/SvgIcons.d.ts +11 -0
- package/dist/esm/UploadFile/components/SvgIcons.js +141 -0
- package/dist/esm/UploadFile/hooks/useUploadModel.d.ts +16 -0
- package/dist/esm/UploadFile/hooks/useUploadModel.js +376 -0
- package/dist/esm/UploadFile/index.d.ts +4 -0
- package/dist/esm/UploadFile/index.js +277 -0
- package/dist/esm/UploadFile/index.less +274 -0
- package/dist/esm/UploadFile/type.d.ts +63 -0
- package/dist/esm/UploadFile/type.js +1 -0
- package/dist/esm/UploadFile/utils/formatBitString.d.ts +1 -0
- package/dist/esm/UploadFile/utils/formatBitString.js +9 -0
- package/dist/esm/UploadFile/utils/getExt.d.ts +1 -0
- package/dist/esm/UploadFile/utils/getExt.js +6 -0
- package/dist/esm/UploadFile/utils/isImage.d.ts +1 -0
- package/dist/esm/UploadFile/utils/isImage.js +8 -0
- package/dist/esm/UploadFile/utils/normalizeExt.d.ts +1 -0
- package/dist/esm/UploadFile/utils/normalizeExt.js +4 -0
- package/dist/esm/UploadFile/utils/validateFileTypes.d.ts +13 -0
- package/dist/esm/UploadFile/utils/validateFileTypes.js +83 -0
- package/dist/esm/UploadFile/utils/validateRule.d.ts +3 -0
- package/dist/esm/UploadFile/utils/validateRule.js +22 -0
- package/dist/esm/UploadFile/utils/validateUpload.d.ts +5 -0
- package/dist/esm/UploadFile/utils/validateUpload.js +35 -0
- package/dist/esm/constant/index.d.ts +7 -0
- package/dist/esm/constant/index.js +18 -0
- package/dist/esm/constants/cssPrefix.d.ts +1 -0
- package/dist/esm/constants/cssPrefix.js +1 -0
- package/dist/esm/getAntApp/index.d.ts +8 -0
- package/dist/esm/getAntApp/index.js +17 -0
- package/dist/esm/index.d.ts +10 -0
- package/dist/esm/index.js +10 -0
- package/dist/esm/interface.d.ts +1 -0
- package/dist/esm/interface.js +1 -0
- package/dist/esm/styles/cssVar.less +28 -0
- package/dist/esm/styles/reset.css +3 -0
- package/dist/esm/styles/var.d.ts +1 -0
- package/dist/esm/styles/var.js +1 -0
- package/dist/esm/styles/var.less +2 -0
- package/dist/esm/variable.less +52 -0
- package/dist/lib/CheckGroup/context.d.ts +9 -0
- package/dist/lib/CheckGroup/context.js +12 -0
- package/dist/lib/CheckGroup/hooks/useCheckGroup.d.ts +21 -0
- package/dist/lib/CheckGroup/hooks/useCheckGroup.js +164 -0
- package/dist/lib/CheckGroup/index.d.ts +9 -0
- package/dist/lib/CheckGroup/index.js +93 -0
- package/dist/lib/CheckGroup/type.d.ts +28 -0
- package/dist/lib/CheckGroup/type.js +5 -0
- package/dist/lib/Grid/index.d.ts +15 -0
- package/dist/lib/Grid/index.js +77 -0
- package/dist/lib/Grid/index.less +7 -0
- package/dist/lib/KaTeXRenderer/index.d.ts +9 -0
- package/dist/lib/KaTeXRenderer/index.js +41 -0
- package/dist/lib/KaTeXRenderer/index.less +0 -0
- package/dist/lib/PixelPlayer/index.d.ts +28 -0
- package/dist/lib/PixelPlayer/index.js +108 -0
- package/dist/lib/PixelPlayer/index.less +29 -0
- package/dist/lib/PixelPlayer/lib/peer-stream.d.ts +62 -0
- package/dist/lib/PixelPlayer/lib/peer-stream.js +707 -0
- package/dist/lib/PixelPlayer/useAutoLayoutContainer.d.ts +2 -0
- package/dist/lib/PixelPlayer/useAutoLayoutContainer.js +60 -0
- package/dist/lib/PixelPlayer/useVideoWithCustomEvent.d.ts +5 -0
- package/dist/lib/PixelPlayer/useVideoWithCustomEvent.js +35 -0
- package/dist/lib/Resizer/index.d.ts +17 -0
- package/dist/lib/Resizer/index.js +75 -0
- package/dist/lib/Resizer/index.less +73 -0
- package/dist/lib/SetupAntApp/index.d.ts +1 -0
- package/dist/lib/SetupAntApp/index.js +16 -0
- package/dist/lib/Transition/index.d.ts +4 -0
- package/dist/lib/Transition/index.js +128 -0
- package/dist/lib/Transition/type.d.ts +33 -0
- package/dist/lib/Transition/type.js +18 -0
- package/dist/lib/Transition/utils.d.ts +6 -0
- package/dist/lib/Transition/utils.js +22 -0
- package/dist/lib/UploadFile/components/IconButton/index.d.ts +11 -0
- package/dist/lib/UploadFile/components/IconButton/index.js +29 -0
- package/dist/lib/UploadFile/components/IconButton/index.less +58 -0
- package/dist/lib/UploadFile/components/Slash.d.ts +2 -0
- package/dist/lib/UploadFile/components/Slash.js +22 -0
- package/dist/lib/UploadFile/components/SvgIcons.d.ts +11 -0
- package/dist/lib/UploadFile/components/SvgIcons.js +158 -0
- package/dist/lib/UploadFile/hooks/useUploadModel.d.ts +16 -0
- package/dist/lib/UploadFile/hooks/useUploadModel.js +306 -0
- package/dist/lib/UploadFile/index.d.ts +4 -0
- package/dist/lib/UploadFile/index.js +280 -0
- package/dist/lib/UploadFile/index.less +274 -0
- package/dist/lib/UploadFile/type.d.ts +63 -0
- package/dist/lib/UploadFile/type.js +5 -0
- package/dist/lib/UploadFile/utils/formatBitString.d.ts +1 -0
- package/dist/lib/UploadFile/utils/formatBitString.js +15 -0
- package/dist/lib/UploadFile/utils/getExt.d.ts +1 -0
- package/dist/lib/UploadFile/utils/getExt.js +12 -0
- package/dist/lib/UploadFile/utils/isImage.d.ts +1 -0
- package/dist/lib/UploadFile/utils/isImage.js +14 -0
- package/dist/lib/UploadFile/utils/normalizeExt.d.ts +1 -0
- package/dist/lib/UploadFile/utils/normalizeExt.js +10 -0
- package/dist/lib/UploadFile/utils/validateFileTypes.d.ts +13 -0
- package/dist/lib/UploadFile/utils/validateFileTypes.js +87 -0
- package/dist/lib/UploadFile/utils/validateRule.d.ts +3 -0
- package/dist/lib/UploadFile/utils/validateRule.js +28 -0
- package/dist/lib/UploadFile/utils/validateUpload.d.ts +5 -0
- package/dist/lib/UploadFile/utils/validateUpload.js +31 -0
- package/dist/lib/constant/index.d.ts +7 -0
- package/dist/lib/constant/index.js +24 -0
- package/dist/lib/constants/cssPrefix.d.ts +1 -0
- package/dist/lib/constants/cssPrefix.js +7 -0
- package/dist/lib/getAntApp/index.d.ts +8 -0
- package/dist/lib/getAntApp/index.js +27 -0
- package/dist/lib/index.d.ts +10 -0
- package/dist/lib/index.js +69 -0
- package/dist/lib/interface.d.ts +1 -0
- package/dist/lib/interface.js +5 -0
- package/dist/lib/styles/cssVar.less +28 -0
- package/dist/lib/styles/reset.css +3 -0
- package/dist/lib/styles/var.d.ts +1 -0
- package/dist/lib/styles/var.js +7 -0
- package/dist/lib/styles/var.less +2 -0
- package/dist/lib/variable.less +52 -0
- package/package.json +54 -0
|
@@ -0,0 +1,707 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
'4.27.2';
|
|
3
|
+
|
|
4
|
+
/* eslint-disable -- hack skills */
|
|
5
|
+
Object.defineProperty(exports, "__esModule", {
|
|
6
|
+
value: true
|
|
7
|
+
});
|
|
8
|
+
exports.PeerStream = void 0;
|
|
9
|
+
exports.initialize = initialize;
|
|
10
|
+
// Must be kept in sync with JavaScriptKeyCodeToFKey C++ array.
|
|
11
|
+
// special keycodes different from KeyboardEvent.keyCode
|
|
12
|
+
const SpecialKeyCodes = {
|
|
13
|
+
Backspace: 8,
|
|
14
|
+
ShiftLeft: 16,
|
|
15
|
+
ControlLeft: 17,
|
|
16
|
+
AltLeft: 18,
|
|
17
|
+
ShiftRight: 253,
|
|
18
|
+
ControlRight: 254,
|
|
19
|
+
AltRight: 255
|
|
20
|
+
};
|
|
21
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
|
|
22
|
+
const MouseButton = {
|
|
23
|
+
MainButton: 0,
|
|
24
|
+
// Left button.
|
|
25
|
+
AuxiliaryButton: 1,
|
|
26
|
+
// Wheel button.
|
|
27
|
+
SecondaryButton: 2,
|
|
28
|
+
// Right button.
|
|
29
|
+
FourthButton: 3,
|
|
30
|
+
// Browser Back button.
|
|
31
|
+
FifthButton: 4 // Browser Forward button.
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// Must be kept in sync with PixelStreamingProtocol::EToClientMsg C++ enum.
|
|
35
|
+
const RECEIVE = {
|
|
36
|
+
QualityControlOwnership: 0,
|
|
37
|
+
Response: 1,
|
|
38
|
+
Command: 2,
|
|
39
|
+
FreezeFrame: 3,
|
|
40
|
+
UnfreezeFrame: 4,
|
|
41
|
+
VideoEncoderAvgQP: 5,
|
|
42
|
+
LatencyTest: 6,
|
|
43
|
+
InitialSettings: 7
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// Must be kept in sync with PixelStreamingProtocol::EToUE4Msg C++ enum.
|
|
47
|
+
const SEND = {
|
|
48
|
+
/*
|
|
49
|
+
* Control Messages. Range = 0..49.
|
|
50
|
+
*/
|
|
51
|
+
IFrameRequest: 0,
|
|
52
|
+
RequestQualityControl: 1,
|
|
53
|
+
MaxFpsRequest: 2,
|
|
54
|
+
AverageBitrateRequest: 3,
|
|
55
|
+
StartStreaming: 4,
|
|
56
|
+
StopStreaming: 5,
|
|
57
|
+
LatencyTest: 6,
|
|
58
|
+
RequestInitialSettings: 7,
|
|
59
|
+
/*
|
|
60
|
+
* Input Messages. Range = 50..89.
|
|
61
|
+
*/
|
|
62
|
+
|
|
63
|
+
// Generic Input Messages. Range = 50..59.
|
|
64
|
+
UIInteraction: 50,
|
|
65
|
+
Command: 51,
|
|
66
|
+
// Keyboard Input Message. Range = 60..69.
|
|
67
|
+
KeyDown: 60,
|
|
68
|
+
KeyUp: 61,
|
|
69
|
+
KeyPress: 62,
|
|
70
|
+
// Mouse Input Messages. Range = 70..79.
|
|
71
|
+
MouseEnter: 70,
|
|
72
|
+
MouseLeave: 71,
|
|
73
|
+
MouseDown: 72,
|
|
74
|
+
MouseUp: 73,
|
|
75
|
+
MouseMove: 74,
|
|
76
|
+
MouseWheel: 75,
|
|
77
|
+
// Touch Input Messages. Range = 80..89.
|
|
78
|
+
TouchStart: 80,
|
|
79
|
+
TouchEnd: 81,
|
|
80
|
+
TouchMove: 82
|
|
81
|
+
};
|
|
82
|
+
const customEventsArray = ['initialize', 'connected', 'disconnected', 'tryConnect', 'message', 'error'];
|
|
83
|
+
class PeerStream extends HTMLVideoElement {
|
|
84
|
+
ws = {
|
|
85
|
+
send() {},
|
|
86
|
+
close() {}
|
|
87
|
+
};
|
|
88
|
+
pc = {
|
|
89
|
+
close() {}
|
|
90
|
+
};
|
|
91
|
+
VideoEncoderQP;
|
|
92
|
+
QualityControlOwnership;
|
|
93
|
+
InitialSettings;
|
|
94
|
+
disablepictureinpicture;
|
|
95
|
+
dc;
|
|
96
|
+
audio;
|
|
97
|
+
ping = -1;
|
|
98
|
+
customEvents = {};
|
|
99
|
+
reconnect = -1;
|
|
100
|
+
reconnectCount = 0;
|
|
101
|
+
constructor(...params) {
|
|
102
|
+
console.log(params);
|
|
103
|
+
super();
|
|
104
|
+
window.ps = this;
|
|
105
|
+
|
|
106
|
+
// this.ws = { send() { }, close() { } }; // WebSocket
|
|
107
|
+
// this.pc = { close() { } }; // RTCPeerConnection
|
|
108
|
+
this.createCustomEvents();
|
|
109
|
+
this.setupVideo();
|
|
110
|
+
this.registerKeyboardEvents();
|
|
111
|
+
this.registerMouseHoverEvents();
|
|
112
|
+
this.registerFakeMouseEvents();
|
|
113
|
+
document.addEventListener('pointerlockchange', () => {
|
|
114
|
+
if (document.pointerLockElement === this) {
|
|
115
|
+
this.registerPointerlockEvents();
|
|
116
|
+
} else {
|
|
117
|
+
this.registerMouseHoverEvents();
|
|
118
|
+
}
|
|
119
|
+
}, false);
|
|
120
|
+
this.addEventListener('loadeddata', e => {
|
|
121
|
+
this.style.aspectRatio = `${this.videoWidth / this.videoHeight}`;
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
createCustomEvents() {
|
|
125
|
+
for (const eventName of customEventsArray) {
|
|
126
|
+
const event = new CustomEvent(eventName);
|
|
127
|
+
this.customEvents[eventName] = event;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// 自定义播放暂停
|
|
131
|
+
customPlay() {
|
|
132
|
+
this.setupWebsocket();
|
|
133
|
+
}
|
|
134
|
+
customPause() {
|
|
135
|
+
this.dc.close();
|
|
136
|
+
this.pause();
|
|
137
|
+
}
|
|
138
|
+
customStop() {
|
|
139
|
+
this.ws.close(1000, 'Infinity');
|
|
140
|
+
this.pc.close();
|
|
141
|
+
}
|
|
142
|
+
setupWebsocket() {
|
|
143
|
+
// This will happen each time the node is moved, and may happen before the element"s contents have been fully parsed. may be called once your element is no longer connected
|
|
144
|
+
if (!this.isConnected) return;
|
|
145
|
+
let signal = this.getAttribute('signal');
|
|
146
|
+
if (!signal) {
|
|
147
|
+
const ip = this.getAttribute('ip') || location.hostname || 'localhost';
|
|
148
|
+
const port = this.getAttribute('port') || 88;
|
|
149
|
+
const token = this.getAttribute('token') || 'hello';
|
|
150
|
+
signal = `ws://${ip}:${port}/${token}`;
|
|
151
|
+
}
|
|
152
|
+
// 是否需要ping,配套的是signal.js里的需要pong
|
|
153
|
+
const needPing = false;
|
|
154
|
+
this.ws.close(1000, 'Infinity');
|
|
155
|
+
this.ws = new WebSocket(signal);
|
|
156
|
+
this.ws.onerror = e => {
|
|
157
|
+
console.log(e);
|
|
158
|
+
};
|
|
159
|
+
this.ws.onopen = async e => {
|
|
160
|
+
console.info('✅ connected to', this.ws.url);
|
|
161
|
+
this.setupPeerConnection();
|
|
162
|
+
// If the new data channel is the first one added to the connection, renegotiation is started by delivering a negotiationneeded event.
|
|
163
|
+
this.setupDataChannel();
|
|
164
|
+
// this.pc.restartIce();
|
|
165
|
+
|
|
166
|
+
clearInterval(this.ping);
|
|
167
|
+
if (needPing) {
|
|
168
|
+
// @ts-ignore
|
|
169
|
+
this.ping = setInterval(() => {
|
|
170
|
+
this.ws.send(JSON.stringify({
|
|
171
|
+
type: 'ping',
|
|
172
|
+
time: Date.now()
|
|
173
|
+
}));
|
|
174
|
+
}, 1000 * 50);
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
this.ws.onmessage = e => {
|
|
178
|
+
this.onWebSocketMessage(e.data);
|
|
179
|
+
};
|
|
180
|
+
this.ws.onclose = e => {
|
|
181
|
+
console.info('❌ signaler closed:', e.reason || e.code);
|
|
182
|
+
clearInterval(this.ping);
|
|
183
|
+
const timeout = +e.reason || 3000;
|
|
184
|
+
if (timeout === Infinity) return;
|
|
185
|
+
clearTimeout(this.reconnect);
|
|
186
|
+
if (this.reconnectCount > 0) {
|
|
187
|
+
// @ts-ignore
|
|
188
|
+
this.reconnect = setTimeout(() => {
|
|
189
|
+
this.reconnectCount--;
|
|
190
|
+
this.dispatchEvent(this.customEvents.tryConnect);
|
|
191
|
+
this.setupWebsocket();
|
|
192
|
+
}, timeout);
|
|
193
|
+
}
|
|
194
|
+
this.dispatchEvent(this.customEvents.disconnected);
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
setupAttributes() {
|
|
198
|
+
this.poster = this.getAttribute('poster') || '';
|
|
199
|
+
console.log(this.poster);
|
|
200
|
+
this.reconnectCount = +(this.getAttribute('reconnectCount') || Number.MAX_SAFE_INTEGER);
|
|
201
|
+
}
|
|
202
|
+
connectedCallback() {
|
|
203
|
+
this.setupAttributes();
|
|
204
|
+
this.setupWebsocket();
|
|
205
|
+
}
|
|
206
|
+
disconnectedCallback() {
|
|
207
|
+
// WebRTC的生命周期与<video>的生命周期绑定
|
|
208
|
+
this.ws.close(1000, 'Infinity');
|
|
209
|
+
this.pc.close();
|
|
210
|
+
console.log('❌ peer connection closing');
|
|
211
|
+
// this.dc.close();
|
|
212
|
+
}
|
|
213
|
+
adoptedCallback() {}
|
|
214
|
+
static observedAttributes = ['signal', 'ip', 'port', 'token'];
|
|
215
|
+
// attributeChangedCallback(name, oldValue, newValue) {
|
|
216
|
+
attributeChangedCallback() {
|
|
217
|
+
if (!this.isConnected) return;
|
|
218
|
+
// fired before connectedCallback when startup 一开始会触发:oldValue从null变成newValue
|
|
219
|
+
this.ws.close(1000, '1');
|
|
220
|
+
}
|
|
221
|
+
async onWebSocketMessage(msgStr) {
|
|
222
|
+
let msg;
|
|
223
|
+
try {
|
|
224
|
+
msg = JSON.parse(msgStr);
|
|
225
|
+
} catch {
|
|
226
|
+
console.debug('signaler:', msg);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
if (msg.type === 'answer') {
|
|
230
|
+
const answer = new RTCSessionDescription(msg);
|
|
231
|
+
await this.pc.setRemoteDescription(answer);
|
|
232
|
+
console.log('Got answer:', answer);
|
|
233
|
+
// 功能不可用
|
|
234
|
+
for (const receiver of this.pc.getReceivers()) {
|
|
235
|
+
receiver.playoutDelayHint = 0;
|
|
236
|
+
}
|
|
237
|
+
} else if (msg.type === 'iceCandidate') {
|
|
238
|
+
const candidate = new RTCIceCandidate(msg.candidate);
|
|
239
|
+
await this.pc.addIceCandidate(candidate);
|
|
240
|
+
console.log('Got candidate:', candidate);
|
|
241
|
+
} else if (msg.type === 'ping') {
|
|
242
|
+
console.log('↓↓ ping:', msg);
|
|
243
|
+
msg.type = 'pong';
|
|
244
|
+
this.ws.send(JSON.stringify(msg));
|
|
245
|
+
} else {
|
|
246
|
+
console.warn('signaler:', msg);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
onDataChannelMessage(message) {
|
|
250
|
+
let data = new Uint8Array(message);
|
|
251
|
+
const utf16 = new TextDecoder('utf-16');
|
|
252
|
+
switch (data[0]) {
|
|
253
|
+
case RECEIVE.VideoEncoderAvgQP:
|
|
254
|
+
{
|
|
255
|
+
this.VideoEncoderQP = +utf16.decode(data.slice(1));
|
|
256
|
+
// console.debug("Got QP:", this.VideoEncoderQP);
|
|
257
|
+
break;
|
|
258
|
+
}
|
|
259
|
+
case RECEIVE.Response:
|
|
260
|
+
{
|
|
261
|
+
// user custom message
|
|
262
|
+
const detail = utf16.decode(data.slice(1));
|
|
263
|
+
this.dispatchEvent(new CustomEvent('message', {
|
|
264
|
+
detail
|
|
265
|
+
}));
|
|
266
|
+
console.info('Got ✉:', detail);
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
case RECEIVE.Command:
|
|
270
|
+
{
|
|
271
|
+
const command = JSON.parse(utf16.decode(data.slice(1)));
|
|
272
|
+
console.info('Got command:', command);
|
|
273
|
+
if (command.command === 'onScreenKeyboard') {
|
|
274
|
+
console.info('You should setup a on-screen keyboard');
|
|
275
|
+
}
|
|
276
|
+
break;
|
|
277
|
+
}
|
|
278
|
+
case RECEIVE.FreezeFrame:
|
|
279
|
+
{
|
|
280
|
+
const size = new DataView(data.slice(1, 5).buffer).getInt32(0, true);
|
|
281
|
+
const jpeg = data.slice(1 + 4);
|
|
282
|
+
console.info('Got freezed frame:', jpeg);
|
|
283
|
+
break;
|
|
284
|
+
}
|
|
285
|
+
case RECEIVE.UnfreezeFrame:
|
|
286
|
+
{
|
|
287
|
+
console.info('Got 【unfreeze frame】');
|
|
288
|
+
break;
|
|
289
|
+
}
|
|
290
|
+
case RECEIVE.LatencyTest:
|
|
291
|
+
{
|
|
292
|
+
const latencyTimings = JSON.parse(utf16.decode(data.slice(1)));
|
|
293
|
+
console.info('Got latency timings:', latencyTimings);
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
case RECEIVE.QualityControlOwnership:
|
|
297
|
+
{
|
|
298
|
+
this.QualityControlOwnership = data[1] !== 0;
|
|
299
|
+
console.info('Got Quality Control Ownership:', this.QualityControlOwnership);
|
|
300
|
+
break;
|
|
301
|
+
}
|
|
302
|
+
case RECEIVE.InitialSettings:
|
|
303
|
+
{
|
|
304
|
+
this.InitialSettings = JSON.parse(utf16.decode(data.slice(1)));
|
|
305
|
+
console.log('Got initial setting:', this.InitialSettings);
|
|
306
|
+
break;
|
|
307
|
+
}
|
|
308
|
+
default:
|
|
309
|
+
{
|
|
310
|
+
console.error('Got invalid data:', data);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
setupVideo() {
|
|
315
|
+
this.tabIndex = -1; // easy to focus..
|
|
316
|
+
this.autofocus = true;
|
|
317
|
+
this.playsInline = true;
|
|
318
|
+
this.disablepictureinpicture = true;
|
|
319
|
+
|
|
320
|
+
// Recently many browsers can only autoplay the videos with sound off
|
|
321
|
+
this.muted = true;
|
|
322
|
+
this.autoplay = true;
|
|
323
|
+
|
|
324
|
+
// this.onsuspend
|
|
325
|
+
// this.onresize
|
|
326
|
+
// this.requestPointerLock();
|
|
327
|
+
|
|
328
|
+
this.style.pointerEvents = 'none';
|
|
329
|
+
this.style.objectFit = 'fill';
|
|
330
|
+
}
|
|
331
|
+
setupDataChannel(label = 'hello') {
|
|
332
|
+
// See https://www.w3.org/TR/webrtc/#dom-rtcdatachannelinit for values (this is needed for Firefox to be consistent with Chrome.)
|
|
333
|
+
this.dc = this.pc.createDataChannel(label, {
|
|
334
|
+
ordered: true
|
|
335
|
+
});
|
|
336
|
+
// Inform browser we would like binary data as an ArrayBuffer (FF chooses Blob by default!)
|
|
337
|
+
this.dc.binaryType = 'arraybuffer';
|
|
338
|
+
this.dc.safeSend = (...params) => {
|
|
339
|
+
try {
|
|
340
|
+
if (this.dc.readyState === 'open') {
|
|
341
|
+
this.dc.send(...params);
|
|
342
|
+
}
|
|
343
|
+
} catch (error) {
|
|
344
|
+
console.log(error);
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
this.dc.onopen = () => {
|
|
348
|
+
this.dispatchEvent(this.customEvents.connected);
|
|
349
|
+
console.log('✅ data channel connected:', label);
|
|
350
|
+
this.style.pointerEvents = 'auto';
|
|
351
|
+
this.dc.send(new Uint8Array([SEND.RequestInitialSettings]));
|
|
352
|
+
this.dc.send(new Uint8Array([SEND.RequestQualityControl]));
|
|
353
|
+
};
|
|
354
|
+
this.dc.onclose = () => {
|
|
355
|
+
console.info('❌ data channel closed:', label);
|
|
356
|
+
// this.style.pointerEvents = "none";
|
|
357
|
+
};
|
|
358
|
+
this.dc.onmessage = e => {
|
|
359
|
+
this.onDataChannelMessage(e.data);
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
setupPeerConnection() {
|
|
363
|
+
console.log('setupPeerConnection');
|
|
364
|
+
this.pc.close();
|
|
365
|
+
this.pc = new RTCPeerConnection({
|
|
366
|
+
// sdpSemantics: "unified-plan",
|
|
367
|
+
iceServers: [
|
|
368
|
+
// {
|
|
369
|
+
// urls: [
|
|
370
|
+
// "stun:stun.l.google.com:19302",
|
|
371
|
+
// "stun:stun1.l.google.com:19302",
|
|
372
|
+
// "stun:stun2.l.google.com:19302",
|
|
373
|
+
// "stun:stun3.l.google.com:19302",
|
|
374
|
+
// "stun:stun4.l.google.com:19302",
|
|
375
|
+
// ],
|
|
376
|
+
// },
|
|
377
|
+
]
|
|
378
|
+
});
|
|
379
|
+
this.pc.ontrack = e => {
|
|
380
|
+
console.log(`Got ${e.track.kind} track:`, e);
|
|
381
|
+
if (e.track.kind === 'video') {
|
|
382
|
+
this.srcObject = e.streams[0];
|
|
383
|
+
} else if (e.track.kind === 'audio') {
|
|
384
|
+
this.audio = document.createElement('audio');
|
|
385
|
+
this.audio.autoplay = true;
|
|
386
|
+
this.audio.srcObject = e.streams[0];
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
this.pc.onicecandidate = e => {
|
|
390
|
+
// firefox
|
|
391
|
+
if (e.candidate?.candidate) {
|
|
392
|
+
console.log('sending candidate:', e.candidate);
|
|
393
|
+
this.ws.send(JSON.stringify({
|
|
394
|
+
type: 'iceCandidate',
|
|
395
|
+
candidate: e.candidate
|
|
396
|
+
}));
|
|
397
|
+
} else {
|
|
398
|
+
// Notice that the end of negotiation is detected here when the event"s candidate property is null.
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
this.pc.onnegotiationneeded = e => {
|
|
402
|
+
this.setupOffer();
|
|
403
|
+
};
|
|
404
|
+
const setPoster = () => this.poster = this.poster || `data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg"><style>text{font-size:7mm;fill:red;}</style>
|
|
405
|
+
<text x="10" y="030"> Signal: ${this.pc.signalingState} </text>
|
|
406
|
+
<text x="10" y="065"> Connect: ${this.pc.connectionState} </text>
|
|
407
|
+
<text x="10" y="100"> ICE Gather: ${this.pc.iceGatheringState} </text>
|
|
408
|
+
<text x="10" y="135"> ICE Connect: ${this.pc.iceConnectionState} </text>
|
|
409
|
+
</svg>`;
|
|
410
|
+
this.pc.onsignalingstatechange = this.pc.onconnectionstatechange = this.pc.oniceconnectionstatechange = this.pc.onicegatheringstatechange = setPoster;
|
|
411
|
+
}
|
|
412
|
+
async setupOffer() {
|
|
413
|
+
// this.pc.addTransceiver("video", { direction: "recvonly" });
|
|
414
|
+
|
|
415
|
+
const offer = await this.pc.createOffer({
|
|
416
|
+
offerToReceiveAudio: this.hasAttribute('audio'),
|
|
417
|
+
offerToReceiveVideo: true
|
|
418
|
+
// voiceActivityDetection: false,
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
// this indicate we support stereo (Chrome needs this)
|
|
422
|
+
offer.sdp = offer.sdp?.replace('useinbandfec=1', 'useinbandfec=1;stereo=1;sprop-maxcapturerate=48000');
|
|
423
|
+
this.pc.setLocalDescription(offer);
|
|
424
|
+
this.ws.send(JSON.stringify(offer));
|
|
425
|
+
console.log('sending offer:', offer);
|
|
426
|
+
}
|
|
427
|
+
registerKeyboardEvents() {
|
|
428
|
+
this.addEventListener('keydown', event => {
|
|
429
|
+
// Handle the keydown event here
|
|
430
|
+
console.log('Key pressed:', event.key);
|
|
431
|
+
});
|
|
432
|
+
this.onkeydown = e => {
|
|
433
|
+
console.log(e.code, e.keyCode);
|
|
434
|
+
this.dc.safeSend(new Uint8Array([SEND.KeyDown, SpecialKeyCodes[e.code] || e.keyCode, Number(e.repeat)]));
|
|
435
|
+
// whether to prevent browser"s default behavior when keyboard/mouse have inputs, like F1~F12 and Tab
|
|
436
|
+
e.preventDefault();
|
|
437
|
+
// e.stopPropagation
|
|
438
|
+
};
|
|
439
|
+
this.onkeyup = e => {
|
|
440
|
+
this.dc.safeSend(new Uint8Array([SEND.KeyUp, SpecialKeyCodes[e.code] || e.keyCode]));
|
|
441
|
+
e.preventDefault();
|
|
442
|
+
};
|
|
443
|
+
this.onkeypress = e => {
|
|
444
|
+
const data = new DataView(new ArrayBuffer(3));
|
|
445
|
+
data.setUint8(0, SEND.KeyPress);
|
|
446
|
+
data.setUint16(1, SpecialKeyCodes[e.code] || e.keyCode, true);
|
|
447
|
+
this.dc.safeSend(data);
|
|
448
|
+
e.preventDefault();
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
registerTouchEvents() {
|
|
452
|
+
// We need to assign a unique identifier to each finger.
|
|
453
|
+
// We do this by mapping each Touch object to the identifier.
|
|
454
|
+
const fingers = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0];
|
|
455
|
+
const fingerIds = {};
|
|
456
|
+
this.ontouchstart = e => {
|
|
457
|
+
// Assign a unique identifier to each touch.
|
|
458
|
+
for (const touch of Array.from(e.changedTouches)) {
|
|
459
|
+
// remember touch
|
|
460
|
+
const finger = fingers.pop();
|
|
461
|
+
if (finger === undefined) {
|
|
462
|
+
console.info('exhausted touch indentifiers');
|
|
463
|
+
}
|
|
464
|
+
fingerIds[touch.identifier] = finger;
|
|
465
|
+
}
|
|
466
|
+
this.emitTouchData(SEND.TouchStart, e.changedTouches, fingerIds);
|
|
467
|
+
e.preventDefault();
|
|
468
|
+
};
|
|
469
|
+
this.ontouchend = e => {
|
|
470
|
+
this.emitTouchData(SEND.TouchEnd, e.changedTouches, fingerIds);
|
|
471
|
+
// Re-cycle unique identifiers previously assigned to each touch.
|
|
472
|
+
for (const touch of Array.from(e.changedTouches)) {
|
|
473
|
+
// forget touch
|
|
474
|
+
fingers.push(fingerIds[touch.identifier]);
|
|
475
|
+
delete fingerIds[touch.identifier];
|
|
476
|
+
}
|
|
477
|
+
e.preventDefault();
|
|
478
|
+
};
|
|
479
|
+
this.ontouchmove = e => {
|
|
480
|
+
this.emitTouchData(SEND.TouchMove, e.touches, fingerIds);
|
|
481
|
+
e.preventDefault();
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// 触屏模拟鼠标
|
|
486
|
+
registerFakeMouseEvents() {
|
|
487
|
+
let finger = undefined;
|
|
488
|
+
const {
|
|
489
|
+
left,
|
|
490
|
+
top
|
|
491
|
+
} = this.getBoundingClientRect();
|
|
492
|
+
this.ontouchstart = e => {
|
|
493
|
+
if (finger === undefined) {
|
|
494
|
+
const firstTouch = e.changedTouches[0];
|
|
495
|
+
finger = {
|
|
496
|
+
id: firstTouch.identifier,
|
|
497
|
+
x: firstTouch.clientX - left,
|
|
498
|
+
y: firstTouch.clientY - top
|
|
499
|
+
};
|
|
500
|
+
// Hack: Mouse events require an enter and leave so we just enter and leave manually with each touch as this event is not fired with a touch device.
|
|
501
|
+
this.onmouseenter && this.onmouseenter(e);
|
|
502
|
+
this.emitMouseDown(MouseButton.MainButton, finger.x, finger.y);
|
|
503
|
+
}
|
|
504
|
+
e.preventDefault();
|
|
505
|
+
};
|
|
506
|
+
this.ontouchend = e => {
|
|
507
|
+
for (const touch of Array.from(e.changedTouches)) {
|
|
508
|
+
if (touch.identifier === finger?.id) {
|
|
509
|
+
const x = touch.clientX - left;
|
|
510
|
+
const y = touch.clientY - top;
|
|
511
|
+
this.emitMouseUp(MouseButton.MainButton, x, y);
|
|
512
|
+
// Hack: Manual mouse leave event.
|
|
513
|
+
this.onmouseleave && this.onmouseleave(e);
|
|
514
|
+
finger = undefined;
|
|
515
|
+
break;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
e.preventDefault();
|
|
519
|
+
};
|
|
520
|
+
this.ontouchmove = e => {
|
|
521
|
+
for (const touch of Array.from(e.changedTouches)) {
|
|
522
|
+
if (touch.identifier === finger?.id) {
|
|
523
|
+
const x = touch.clientX - left;
|
|
524
|
+
const y = touch.clientY - top;
|
|
525
|
+
this.emitMouseMove(x, y, x - finger.x, y - finger.y);
|
|
526
|
+
finger.x = x;
|
|
527
|
+
finger.y = y;
|
|
528
|
+
break;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
e.preventDefault();
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
registerMouseHoverEvents() {
|
|
535
|
+
this.registerMouseEnterAndLeaveEvents();
|
|
536
|
+
this.onmousemove = e => {
|
|
537
|
+
this.emitMouseMove(e.offsetX, e.offsetY, e.movementX, e.movementY);
|
|
538
|
+
e.preventDefault();
|
|
539
|
+
};
|
|
540
|
+
this.onmousedown = e => {
|
|
541
|
+
this.emitMouseDown(e.button, e.offsetX, e.offsetY);
|
|
542
|
+
// e.preventDefault();
|
|
543
|
+
};
|
|
544
|
+
this.onmouseup = e => {
|
|
545
|
+
this.emitMouseUp(e.button, e.offsetX, e.offsetY);
|
|
546
|
+
// e.preventDefault();
|
|
547
|
+
};
|
|
548
|
+
|
|
549
|
+
// When the context menu is shown then it is safest to release the button which was pressed when the event happened. This will guarantee we will get at least one mouse up corresponding to a mouse down event. Otherwise the mouse can get stuck.
|
|
550
|
+
// https://github.com/facebook/react/issues/5531
|
|
551
|
+
this.oncontextmenu = e => {
|
|
552
|
+
this.emitMouseUp(e.button, e.offsetX, e.offsetY);
|
|
553
|
+
e.preventDefault();
|
|
554
|
+
};
|
|
555
|
+
this.onwheel = e => {
|
|
556
|
+
// console.log(e,e.wheelDeltaY,e.deltaY)
|
|
557
|
+
// 临时处理
|
|
558
|
+
this.emitMouseWheel(-e.deltaY, e.offsetX, e.offsetY);
|
|
559
|
+
e.preventDefault();
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
registerPointerlockEvents() {
|
|
563
|
+
this.registerMouseEnterAndLeaveEvents();
|
|
564
|
+
console.info('mouse locked in, ESC to exit');
|
|
565
|
+
const {
|
|
566
|
+
clientWidth,
|
|
567
|
+
clientHeight
|
|
568
|
+
} = this;
|
|
569
|
+
let x = clientWidth / 2;
|
|
570
|
+
let y = clientHeight / 2;
|
|
571
|
+
this.onmousemove = e => {
|
|
572
|
+
x += e.movementX;
|
|
573
|
+
y += e.movementY;
|
|
574
|
+
x = (x + clientWidth) % clientWidth;
|
|
575
|
+
y = (y + clientHeight) % clientHeight;
|
|
576
|
+
this.emitMouseMove(x, y, e.movementX, e.movementY);
|
|
577
|
+
};
|
|
578
|
+
this.onmousedown = e => {
|
|
579
|
+
this.emitMouseDown(e.button, x, y);
|
|
580
|
+
};
|
|
581
|
+
this.onmouseup = e => {
|
|
582
|
+
this.emitMouseUp(e.button, x, y);
|
|
583
|
+
};
|
|
584
|
+
this.onwheel = e => {
|
|
585
|
+
// 临时处理
|
|
586
|
+
this.emitMouseWheel(-e.deltaY, x, y);
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
registerMouseEnterAndLeaveEvents() {
|
|
590
|
+
this.onmouseenter = e => {
|
|
591
|
+
this.dc.safeSend(new Uint8Array([SEND.MouseEnter]));
|
|
592
|
+
};
|
|
593
|
+
this.onmouseleave = e => {
|
|
594
|
+
this.dc.safeSend && this.dc.safeSend(new Uint8Array([SEND.MouseLeave]));
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
emitMouseMove(x, y, deltaX, deltaY) {
|
|
598
|
+
const coord = this._normalize(x, y);
|
|
599
|
+
deltaX = deltaX * 65536 / this.clientWidth;
|
|
600
|
+
deltaY = deltaY * 65536 / this.clientHeight;
|
|
601
|
+
const data = new DataView(new ArrayBuffer(9));
|
|
602
|
+
data.setUint8(0, SEND.MouseMove);
|
|
603
|
+
data.setUint16(1, coord.x, true);
|
|
604
|
+
data.setUint16(3, coord.y, true);
|
|
605
|
+
data.setInt16(5, deltaX, true);
|
|
606
|
+
data.setInt16(7, deltaY, true);
|
|
607
|
+
this.dc.safeSend(data);
|
|
608
|
+
}
|
|
609
|
+
emitMouseDown(button, x, y) {
|
|
610
|
+
const coord = this._normalize(x, y);
|
|
611
|
+
const data = new DataView(new ArrayBuffer(6));
|
|
612
|
+
data.setUint8(0, SEND.MouseDown);
|
|
613
|
+
data.setUint8(1, button);
|
|
614
|
+
data.setUint16(2, coord.x, true);
|
|
615
|
+
data.setUint16(4, coord.y, true);
|
|
616
|
+
this.dc.safeSend(data);
|
|
617
|
+
}
|
|
618
|
+
emitMouseUp(button, x, y) {
|
|
619
|
+
const coord = this._normalize(x, y);
|
|
620
|
+
const data = new DataView(new ArrayBuffer(6));
|
|
621
|
+
data.setUint8(0, SEND.MouseUp);
|
|
622
|
+
data.setUint8(1, button);
|
|
623
|
+
data.setUint16(2, coord.x, true);
|
|
624
|
+
data.setUint16(4, coord.y, true);
|
|
625
|
+
this.dc.safeSend(data);
|
|
626
|
+
}
|
|
627
|
+
emitMouseWheel(delta, x, y) {
|
|
628
|
+
const coord = this._normalize(x, y);
|
|
629
|
+
const data = new DataView(new ArrayBuffer(7));
|
|
630
|
+
data.setUint8(0, SEND.MouseWheel);
|
|
631
|
+
data.setInt16(1, delta, true);
|
|
632
|
+
data.setUint16(3, coord.x, true);
|
|
633
|
+
data.setUint16(5, coord.y, true);
|
|
634
|
+
this.dc.safeSend(data);
|
|
635
|
+
}
|
|
636
|
+
emitTouchData(type, touches, fingerIds) {
|
|
637
|
+
const data = new DataView(new ArrayBuffer(2 + 6 * touches.length));
|
|
638
|
+
data.setUint8(0, type);
|
|
639
|
+
data.setUint8(1, touches.length);
|
|
640
|
+
let byte = 2;
|
|
641
|
+
for (const touch of Array.from(touches)) {
|
|
642
|
+
const x = touch.clientX - this.offsetLeft;
|
|
643
|
+
const y = touch.clientY - this.offsetTop;
|
|
644
|
+
const coord = this._normalize(x, y);
|
|
645
|
+
data.setUint16(byte, coord.x, true);
|
|
646
|
+
byte += 2;
|
|
647
|
+
data.setUint16(byte, coord.y, true);
|
|
648
|
+
byte += 2;
|
|
649
|
+
data.setUint8(byte, fingerIds[touch.identifier]);
|
|
650
|
+
byte += 1;
|
|
651
|
+
data.setUint8(byte, 255 * touch.force); // force is between 0.0 and 1.0 so quantize into byte.
|
|
652
|
+
byte += 1;
|
|
653
|
+
}
|
|
654
|
+
this.dc.safeSend(data);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// emit string
|
|
658
|
+
emitMessage(msg, messageType = SEND.UIInteraction) {
|
|
659
|
+
if (typeof msg !== 'string') msg = JSON.stringify(msg);
|
|
660
|
+
|
|
661
|
+
// Add the UTF-16 JSON string to the array byte buffer, going two bytes at a time.
|
|
662
|
+
const data = new DataView(new ArrayBuffer(1 + 2 + 2 * msg.length));
|
|
663
|
+
let byteIdx = 0;
|
|
664
|
+
data.setUint8(byteIdx, messageType);
|
|
665
|
+
byteIdx++;
|
|
666
|
+
data.setUint16(byteIdx, msg.length, true);
|
|
667
|
+
byteIdx += 2;
|
|
668
|
+
for (const char of msg) {
|
|
669
|
+
// charCodeAt() is UTF-16, codePointAt() is Unicode.
|
|
670
|
+
data.setUint16(byteIdx, char.charCodeAt(0), true);
|
|
671
|
+
byteIdx += 2;
|
|
672
|
+
}
|
|
673
|
+
this.dc.safeSend(data);
|
|
674
|
+
// 有时候前端问:怎么返回undefined?是不是没发送成功?
|
|
675
|
+
return true;
|
|
676
|
+
}
|
|
677
|
+
_normalize(x, y) {
|
|
678
|
+
const normalizedX = x / this.clientWidth;
|
|
679
|
+
const normalizedY = y / this.clientHeight;
|
|
680
|
+
if (normalizedX < 0.0 || normalizedX > 1.0 || normalizedY < 0.0 || normalizedY > 1.0) {
|
|
681
|
+
return {
|
|
682
|
+
inRange: false,
|
|
683
|
+
x: 65535,
|
|
684
|
+
y: 65535
|
|
685
|
+
};
|
|
686
|
+
} else {
|
|
687
|
+
return {
|
|
688
|
+
inRange: true,
|
|
689
|
+
x: normalizedX * 65536,
|
|
690
|
+
y: normalizedY * 65536
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
debug(NodeJS) {
|
|
695
|
+
// 调试信令服务器
|
|
696
|
+
this.ws.send(JSON.stringify({
|
|
697
|
+
type: 'debug',
|
|
698
|
+
debug: NodeJS
|
|
699
|
+
}));
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
exports.PeerStream = PeerStream;
|
|
703
|
+
function initialize(initialSettings) {
|
|
704
|
+
customElements.define('peer-stream', PeerStream, {
|
|
705
|
+
extends: 'video'
|
|
706
|
+
});
|
|
707
|
+
}
|