@needle-tools/engine 2.30.0-pre → 2.31.0-pre
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/CHANGELOG.md +4 -0
- package/dist/needle-engine.d.ts +807 -753
- package/dist/needle-engine.js +336 -336
- package/dist/needle-engine.js.map +4 -4
- package/dist/needle-engine.min.js +16 -16
- package/dist/needle-engine.min.js.map +4 -4
- package/lib/engine/engine_physics.d.ts +19 -8
- package/lib/engine/engine_physics.js +57 -48
- package/lib/engine/engine_physics.js.map +1 -1
- package/lib/engine-components/Camera.js +0 -1
- package/lib/engine-components/Camera.js.map +1 -1
- package/lib/engine-components/Component.d.ts +10 -0
- package/lib/engine-components/Component.js +53 -0
- package/lib/engine-components/Component.js.map +1 -1
- package/lib/engine-components/Rigidbody.d.ts +1 -1
- package/lib/engine-components/Rigidbody.js +3 -3
- package/lib/engine-components/Rigidbody.js.map +1 -1
- package/lib/engine-components/ScreenCapture.d.ts +35 -6
- package/lib/engine-components/ScreenCapture.js +356 -103
- package/lib/engine-components/ScreenCapture.js.map +1 -1
- package/lib/engine-components/VideoPlayer.d.ts +4 -0
- package/lib/engine-components/VideoPlayer.js +22 -1
- package/lib/engine-components/VideoPlayer.js.map +1 -1
- package/lib/needle-engine.d.ts +1 -0
- package/lib/needle-engine.js +1 -0
- package/lib/needle-engine.js.map +1 -1
- package/package.json +1 -1
- package/src/engine/engine_physics.ts +74 -57
- package/src/engine-components/Camera.ts +0 -1
- package/src/engine-components/Component.ts +58 -0
- package/src/engine-components/RigidBody.ts +2 -2
- package/src/engine-components/ScreenCapture.ts +386 -114
- package/src/engine-components/VideoPlayer.ts +26 -2
- package/src/needle-engine.ts +1 -0
|
@@ -6,75 +6,269 @@ import { RoomEvents } from "../engine/engine_networking";
|
|
|
6
6
|
import { UserJoinedOrLeftRoomModel } from "../engine/engine_networking";
|
|
7
7
|
import { serializeable } from "../engine/engine_serialization";
|
|
8
8
|
import { IModel } from "../engine/engine_networking";
|
|
9
|
+
import { IPointerClickHandler } from "./ui/PointerEvents";
|
|
10
|
+
import { EventDispatcher } from "three";
|
|
11
|
+
import { AudioSource } from "./AudioSource";
|
|
12
|
+
import { getWorldScale, setWorldScale } from "../engine/engine_three_utils";
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
export enum ScreenCaptureDevice {
|
|
16
|
+
Screen = 0,
|
|
17
|
+
Camera = 1
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export enum ScreenCaptureMode {
|
|
21
|
+
Idle = 0,
|
|
22
|
+
Sending = 1,
|
|
23
|
+
Receiving = 2
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export enum AspectMode {
|
|
27
|
+
None = 0,
|
|
28
|
+
AdjustHeight = 1,
|
|
29
|
+
AdjustWidth = 2,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function disposeStream(str: MediaStream | null | undefined) {
|
|
33
|
+
if (!str) return;
|
|
34
|
+
for (const cap of str.getTracks())
|
|
35
|
+
cap.stop();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export class ScreenCapture extends Behaviour implements IPointerClickHandler {
|
|
39
|
+
|
|
40
|
+
onPointerClick() {
|
|
41
|
+
if (this.isSending) {
|
|
42
|
+
this.close();
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
this.share();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
|
|
9
49
|
|
|
10
|
-
export class ScreenCapture extends Behaviour {
|
|
11
50
|
|
|
12
51
|
@serializeable()
|
|
13
|
-
|
|
52
|
+
device: ScreenCaptureDevice = ScreenCaptureDevice.Screen;
|
|
14
53
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
54
|
+
@serializeable()
|
|
55
|
+
aspectMode: AspectMode = AspectMode.AdjustHeight;
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
get isSending() {
|
|
59
|
+
return this._currentStream && this._currentMode === ScreenCaptureMode.Sending;
|
|
60
|
+
}
|
|
61
|
+
get isReceiving() {
|
|
62
|
+
return this._currentStream && this._currentMode === ScreenCaptureMode.Receiving;
|
|
19
63
|
}
|
|
20
64
|
|
|
21
|
-
private
|
|
22
|
-
private
|
|
65
|
+
private _net?: NetworkedVideo;
|
|
66
|
+
private _video?: VideoPlayer;
|
|
67
|
+
private _requestOpen: boolean = false;
|
|
68
|
+
private _currentStream: MediaStream | null = null;
|
|
69
|
+
private _currentMode: ScreenCaptureMode = ScreenCaptureMode.Idle;
|
|
23
70
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
71
|
+
awake() {
|
|
72
|
+
AudioSource.registerWaitForAllowAudio(() => {
|
|
73
|
+
if (this._video && this._currentStream && this._currentMode === ScreenCaptureMode.Receiving) {
|
|
74
|
+
this._video.setVideo(this._currentStream);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
start() {
|
|
80
|
+
this._video = GameObject.getComponentInChildren(this.gameObject, VideoPlayer) ?? undefined;
|
|
81
|
+
if (!this._video) {
|
|
82
|
+
console.error("Missing VideoPlayer component");
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const handle = PeerHandle.getOrCreate(this.context, this.guid);
|
|
86
|
+
this._net = new NetworkedVideo(this.context, handle);
|
|
87
|
+
this._net.enable();
|
|
88
|
+
//@ts-ignore
|
|
89
|
+
this._net.addEventListener(PeerEvent.ReceiveVideo, this.onReceiveVideo.bind(this));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async share() {
|
|
93
|
+
this._requestOpen = true;
|
|
28
94
|
try {
|
|
29
|
-
|
|
30
|
-
|
|
95
|
+
if (this._video) {
|
|
96
|
+
|
|
97
|
+
const settings: MediaTrackConstraints = {
|
|
98
|
+
echoCancellation: true,
|
|
99
|
+
autoGainControl: false,
|
|
100
|
+
};
|
|
31
101
|
const displayMediaOptions: DisplayMediaStreamConstraints = {
|
|
32
|
-
video:
|
|
33
|
-
audio:
|
|
102
|
+
video: settings,
|
|
103
|
+
audio: settings,
|
|
34
104
|
};
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
105
|
+
|
|
106
|
+
switch (this.device) {
|
|
107
|
+
case ScreenCaptureDevice.Camera:
|
|
108
|
+
this.tryShareUserCamera(displayMediaOptions);
|
|
109
|
+
break;
|
|
110
|
+
|
|
111
|
+
case ScreenCaptureDevice.Screen:
|
|
112
|
+
if (!navigator.mediaDevices.getDisplayMedia) {
|
|
113
|
+
console.error("No getDisplayMedia support");
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const myVideo = await navigator.mediaDevices.getDisplayMedia(displayMediaOptions);
|
|
117
|
+
if (this._requestOpen) {
|
|
118
|
+
this.setVideo(myVideo, ScreenCaptureMode.Sending);
|
|
119
|
+
}
|
|
120
|
+
else disposeStream(myVideo);
|
|
121
|
+
break;
|
|
40
122
|
}
|
|
41
|
-
this.handleNetworkedVideo();
|
|
42
123
|
}
|
|
43
|
-
} catch (err) {
|
|
44
|
-
|
|
124
|
+
} catch (err: any) {
|
|
125
|
+
if (err.name === "NotAllowedError") {
|
|
126
|
+
// user cancelled stream selection
|
|
127
|
+
console.log("Selection cancelled");
|
|
128
|
+
this._requestOpen = false;
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
console.error("Error opening video", err);
|
|
45
132
|
}
|
|
46
133
|
}
|
|
47
134
|
|
|
48
135
|
close() {
|
|
49
|
-
this.
|
|
50
|
-
if (this.
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
this.
|
|
136
|
+
this._requestOpen = false;
|
|
137
|
+
if (this._currentStream) {
|
|
138
|
+
console.warn("Close current stream / disposing resources");
|
|
139
|
+
disposeStream(this._currentStream);
|
|
140
|
+
this._net?.stopSendingVideo(this._currentStream);
|
|
141
|
+
this._currentMode = ScreenCaptureMode.Idle;
|
|
142
|
+
this._currentStream = null;
|
|
54
143
|
}
|
|
55
144
|
}
|
|
56
145
|
|
|
57
|
-
private
|
|
58
|
-
if (
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
146
|
+
private setVideo(stream: MediaStream, mode: ScreenCaptureMode) {
|
|
147
|
+
if (stream === this._currentStream) return;
|
|
148
|
+
this.close();
|
|
149
|
+
if (!stream || !this._video) return;
|
|
150
|
+
this._currentStream = stream;
|
|
151
|
+
this._requestOpen = true;
|
|
152
|
+
this._currentMode = mode;
|
|
153
|
+
this._video.setVideo(stream);
|
|
154
|
+
if (mode === ScreenCaptureMode.Sending)
|
|
155
|
+
this._net?.startSendingVideo(stream);
|
|
156
|
+
|
|
157
|
+
this._targetObjects = undefined;
|
|
158
|
+
this.updateAspect();
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
private onReceiveVideo(evt: ReceiveVideoEvent) {
|
|
162
|
+
this.setVideo(evt.stream, ScreenCaptureMode.Receiving);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
private async tryShareUserCamera(opts: DisplayMediaStreamConstraints) {
|
|
169
|
+
|
|
170
|
+
// let newWindow = open('', 'example', 'width=300,height=300');
|
|
171
|
+
// if (window) {
|
|
172
|
+
// newWindow!.document.body.innerHTML = "Please allow access to your camera and microphone";
|
|
173
|
+
// }
|
|
174
|
+
|
|
175
|
+
// TODO: allow user to select device
|
|
176
|
+
const devices = (await navigator.mediaDevices.enumerateDevices()).filter(d => d.kind === "videoinput");
|
|
177
|
+
console.log("Request camera", devices);
|
|
178
|
+
for (const dev of devices) {
|
|
179
|
+
try {
|
|
180
|
+
if (!this._requestOpen) break;
|
|
181
|
+
if (dev.kind !== "videoinput") continue;
|
|
182
|
+
const id = dev.deviceId;
|
|
183
|
+
if (opts.video !== false) {
|
|
184
|
+
if (typeof opts.video === "undefined" || typeof opts.video === "boolean") {
|
|
185
|
+
opts.video = {};
|
|
186
|
+
}
|
|
187
|
+
opts.video.deviceId = id;
|
|
188
|
+
}
|
|
189
|
+
const userMedia = await navigator.mediaDevices.getUserMedia(opts);
|
|
190
|
+
if (this._requestOpen) {
|
|
191
|
+
this.setVideo(userMedia, ScreenCaptureMode.Sending);
|
|
192
|
+
}
|
|
193
|
+
else disposeStream(userMedia);
|
|
194
|
+
console.log("Selected camera", dev);
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
catch (err) {
|
|
198
|
+
console.warn(err);
|
|
199
|
+
}
|
|
70
200
|
}
|
|
71
201
|
}
|
|
202
|
+
// private _cameraSelectionWindow : Window | null = null;
|
|
203
|
+
// private openWindowToSelectCamera(){
|
|
204
|
+
|
|
205
|
+
// }
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
private _targetObjects?: Array<THREE.Object3D>;
|
|
211
|
+
|
|
212
|
+
private updateAspect() {
|
|
213
|
+
this.startCoroutine(this.updateAspectImpl());
|
|
214
|
+
}
|
|
72
215
|
|
|
216
|
+
private _updateAspectRoutineId: number = -1;
|
|
217
|
+
private *updateAspectImpl() {
|
|
218
|
+
const id = ++this._updateAspectRoutineId;
|
|
219
|
+
while (this.aspectMode !== AspectMode.None && this._currentStream && id === this._updateAspectRoutineId) {
|
|
220
|
+
const video = this._video;
|
|
221
|
+
const stream = this._currentStream;
|
|
222
|
+
if (!video) return;
|
|
223
|
+
if (!stream) return;
|
|
224
|
+
let aspect: number | undefined = undefined;
|
|
225
|
+
for (const track of stream.getVideoTracks()) {
|
|
226
|
+
const settings = track.getSettings();
|
|
227
|
+
if (settings.width && settings.height) {
|
|
228
|
+
aspect = settings.width / settings.height;
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
if (aspect === undefined) return;
|
|
233
|
+
if (!this._targetObjects)
|
|
234
|
+
this._targetObjects = video.getTargetObjects();
|
|
235
|
+
for (const obj of this._targetObjects) {
|
|
236
|
+
let worldAspect = 1;
|
|
237
|
+
if (obj.parent) {
|
|
238
|
+
const parentScale = getWorldScale(obj.parent);
|
|
239
|
+
worldAspect = parentScale.x / parentScale.y;
|
|
240
|
+
}
|
|
241
|
+
switch (this.aspectMode) {
|
|
242
|
+
case AspectMode.AdjustHeight:
|
|
243
|
+
obj.scale.y = 1 / aspect * obj.scale.x * worldAspect;
|
|
244
|
+
break;
|
|
245
|
+
case AspectMode.AdjustWidth:
|
|
246
|
+
obj.scale.x = aspect * obj.scale.y * worldAspect;
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
yield;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
73
253
|
}
|
|
74
254
|
|
|
75
255
|
|
|
76
|
-
|
|
256
|
+
/////// PEER
|
|
257
|
+
|
|
258
|
+
enum PeerEvent {
|
|
77
259
|
Connected = "peer-user-connected",
|
|
260
|
+
ReceiveVideo = "receive-video",
|
|
261
|
+
Disconnected = "peer-user-disconnected",
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
class ReceiveVideoEvent {
|
|
265
|
+
readonly type = PeerEvent.ReceiveVideo;
|
|
266
|
+
readonly stream: MediaStream;
|
|
267
|
+
readonly target: CallHandle;
|
|
268
|
+
constructor(stream: MediaStream, target: CallHandle) {
|
|
269
|
+
this.stream = stream
|
|
270
|
+
this.target = target;
|
|
271
|
+
}
|
|
78
272
|
}
|
|
79
273
|
|
|
80
274
|
class PeerUserConnectedModel implements IModel {
|
|
@@ -94,22 +288,48 @@ enum CallDirection {
|
|
|
94
288
|
Outgoing = "outgoing",
|
|
95
289
|
}
|
|
96
290
|
|
|
97
|
-
class CallHandle {
|
|
291
|
+
class CallHandle extends EventDispatcher {
|
|
292
|
+
readonly userId: string;
|
|
98
293
|
readonly direction: CallDirection;
|
|
99
294
|
readonly call: Peer.MediaConnection;
|
|
100
|
-
stream
|
|
295
|
+
get stream() { return this._stream; };
|
|
296
|
+
|
|
297
|
+
private _stream: MediaStream | null = null;
|
|
298
|
+
private _isDisposed: boolean = false;
|
|
299
|
+
|
|
300
|
+
close() {
|
|
301
|
+
if (this._isDisposed) return;
|
|
302
|
+
this._isDisposed = true;
|
|
303
|
+
this.call.close();
|
|
304
|
+
disposeStream(this._stream);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
get isOpen() {
|
|
308
|
+
return this.call.peerConnection?.connectionState === "connected";// && this._stream?.active;
|
|
309
|
+
}
|
|
101
310
|
|
|
102
|
-
|
|
311
|
+
get isClosed() {
|
|
312
|
+
return !this.isOpen;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
constructor(userId: string, call: Peer.MediaConnection, direction: CallDirection) {
|
|
316
|
+
super();
|
|
317
|
+
this.userId = userId;
|
|
103
318
|
this.call = call;
|
|
104
319
|
this.direction = direction;
|
|
105
|
-
this.
|
|
320
|
+
this._stream = null;
|
|
106
321
|
call.on("stream", stream => {
|
|
107
|
-
|
|
322
|
+
console.log("Receive video", stream.getAudioTracks(), stream.getVideoTracks());
|
|
323
|
+
this._stream = stream;
|
|
324
|
+
if (direction === CallDirection.Incoming) {
|
|
325
|
+
const args: ReceiveVideoEvent = new ReceiveVideoEvent(stream, this);
|
|
326
|
+
this.dispatchEvent(args);
|
|
327
|
+
}
|
|
108
328
|
});
|
|
109
329
|
}
|
|
110
330
|
}
|
|
111
331
|
|
|
112
|
-
class PeerHandle {
|
|
332
|
+
class PeerHandle extends EventDispatcher {
|
|
113
333
|
|
|
114
334
|
private static readonly instances: Map<string, PeerHandle> = new Map();
|
|
115
335
|
|
|
@@ -125,6 +345,12 @@ class PeerHandle {
|
|
|
125
345
|
return peer;
|
|
126
346
|
}
|
|
127
347
|
|
|
348
|
+
getMyPeerId(): string | undefined {
|
|
349
|
+
if (this.context.connection.connectionId)
|
|
350
|
+
return this.getPeerIdFromUserId(this.context.connection.connectionId);
|
|
351
|
+
return undefined;
|
|
352
|
+
}
|
|
353
|
+
|
|
128
354
|
getPeerIdFromUserId(userConnectionId: string): string {
|
|
129
355
|
// we build the peer id ourselves so we dont need to wait for peer to report it
|
|
130
356
|
return this.id + "-" + userConnectionId;
|
|
@@ -135,13 +361,8 @@ class PeerHandle {
|
|
|
135
361
|
}
|
|
136
362
|
|
|
137
363
|
makeCall(peerId: string, stream: MediaStream): CallHandle | undefined {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
// return;
|
|
141
|
-
// }
|
|
142
|
-
// const peerId = this.getUserPeerId(userId);
|
|
143
|
-
console.log("CALL", peerId);
|
|
144
|
-
const call = this._peer?.call(peerId, stream);
|
|
364
|
+
const opts = { metadata: { userId: this.context.connection.connectionId } };
|
|
365
|
+
const call = this._peer?.call(peerId, stream, opts);
|
|
145
366
|
if (call)
|
|
146
367
|
return this.registerCall(call, CallDirection.Outgoing);
|
|
147
368
|
return undefined;
|
|
@@ -156,6 +377,7 @@ class PeerHandle {
|
|
|
156
377
|
private _outgoingCalls: CallHandle[] = [];
|
|
157
378
|
|
|
158
379
|
private constructor(context: Context, id: string) {
|
|
380
|
+
super();
|
|
159
381
|
this.context = context;
|
|
160
382
|
this.id = id;
|
|
161
383
|
this.setupPeer();
|
|
@@ -203,7 +425,7 @@ class PeerHandle {
|
|
|
203
425
|
if (this._enabledPeer) return;
|
|
204
426
|
this._enabledPeer = true;
|
|
205
427
|
if (!this._peer) {
|
|
206
|
-
const peerId = this.
|
|
428
|
+
const peerId = this.getMyPeerId();
|
|
207
429
|
this._peer = new Peer(peerId);
|
|
208
430
|
}
|
|
209
431
|
if (this._enabled)
|
|
@@ -218,31 +440,60 @@ class PeerHandle {
|
|
|
218
440
|
}
|
|
219
441
|
|
|
220
442
|
private unsubscribePeerEvents() {
|
|
443
|
+
// TODO: unsubscribe
|
|
221
444
|
}
|
|
222
445
|
|
|
223
446
|
private onPeerConnect(id): void {
|
|
224
|
-
console.log("
|
|
225
|
-
this.context.connection.send(
|
|
447
|
+
console.log("Peer connected as", id);
|
|
448
|
+
this.context.connection.send(PeerEvent.Connected, new PeerUserConnectedModel(this, id));
|
|
226
449
|
}
|
|
227
450
|
|
|
228
451
|
private onPeerReceivingCall(call: Peer.MediaConnection): void {
|
|
229
|
-
console.log("RECEIVE CALL");
|
|
230
452
|
call.answer();
|
|
231
453
|
this.registerCall(call, CallDirection.Incoming);
|
|
232
454
|
}
|
|
233
455
|
|
|
234
456
|
private registerCall(call: Peer.MediaConnection, direction: CallDirection): CallHandle {
|
|
457
|
+
|
|
458
|
+
const meta = call.metadata;
|
|
459
|
+
if (!meta || !meta.userId) {
|
|
460
|
+
console.error("Missing call metadata", call);
|
|
461
|
+
}
|
|
462
|
+
const userId = meta.userId;
|
|
463
|
+
|
|
464
|
+
if (direction === CallDirection.Incoming) console.log("Receive call from", call.metadata);
|
|
465
|
+
else console.log("Make call to", call.metadata);
|
|
466
|
+
|
|
235
467
|
const arr = direction === CallDirection.Incoming ? this._incomingCalls : this._outgoingCalls;
|
|
236
|
-
const handle = new CallHandle(call, direction);
|
|
468
|
+
const handle = new CallHandle(userId, call, direction);
|
|
237
469
|
arr.push(handle);
|
|
238
470
|
call.on("error", err => {
|
|
239
|
-
console.error(err);
|
|
471
|
+
console.error("Call error", err);
|
|
240
472
|
});
|
|
241
473
|
call.on("close", () => {
|
|
474
|
+
console.log("Call ended", call.metadata);
|
|
475
|
+
call.close();
|
|
242
476
|
const index = arr.indexOf(handle);
|
|
243
477
|
if (index !== -1)
|
|
244
478
|
arr.splice(index, 1);
|
|
245
479
|
});
|
|
480
|
+
|
|
481
|
+
if (direction === CallDirection.Incoming) {
|
|
482
|
+
|
|
483
|
+
handle.addEventListener(PeerEvent.ReceiveVideo, e => {
|
|
484
|
+
this.dispatchEvent(e);
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
call.on("stream", () => {
|
|
488
|
+
// workaround for https://github.com/peers/peerjs/issues/636
|
|
489
|
+
let closeInterval = setInterval(() => {
|
|
490
|
+
if (!handle.isOpen) {
|
|
491
|
+
clearInterval(closeInterval);
|
|
492
|
+
handle.close();
|
|
493
|
+
}
|
|
494
|
+
}, 2000);
|
|
495
|
+
});
|
|
496
|
+
}
|
|
246
497
|
return handle;
|
|
247
498
|
}
|
|
248
499
|
|
|
@@ -252,36 +503,29 @@ class PeerHandle {
|
|
|
252
503
|
}
|
|
253
504
|
|
|
254
505
|
|
|
255
|
-
type UserVideoCall = {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
}
|
|
506
|
+
// type UserVideoCall = {
|
|
507
|
+
// call: Peer.MediaConnection;
|
|
508
|
+
// stream: MediaStream;
|
|
509
|
+
// userId: string;
|
|
510
|
+
// }
|
|
260
511
|
|
|
261
|
-
|
|
512
|
+
// type IncomingStreamArgs = {
|
|
513
|
+
// stream: MediaStream;
|
|
514
|
+
// userId: string;
|
|
515
|
+
// }
|
|
516
|
+
|
|
517
|
+
class NetworkedVideo extends EventDispatcher {
|
|
262
518
|
|
|
263
519
|
private readonly context: Context;
|
|
264
520
|
private readonly peer: PeerHandle;
|
|
265
521
|
|
|
266
|
-
private
|
|
522
|
+
// private _receiveVideoStreamListeners: Array<(info: IncomingStreamArgs) => void> = [];
|
|
523
|
+
private _sendingVideoStreams: Map<MediaStream, CallHandle[]> = new Map();
|
|
267
524
|
|
|
268
525
|
constructor(context: Context, peer: PeerHandle) {
|
|
526
|
+
super();
|
|
269
527
|
this.context = context;
|
|
270
528
|
this.peer = peer;
|
|
271
|
-
|
|
272
|
-
// this.peer.on("open", (id) => {
|
|
273
|
-
// console.log("PEERID", id);
|
|
274
|
-
// });
|
|
275
|
-
// // this.peer.on("call", (call) => {
|
|
276
|
-
// // });
|
|
277
|
-
// // this.peer.on("connection", (conn) => {
|
|
278
|
-
// // });
|
|
279
|
-
// // this.peer.on("error", (err) => {
|
|
280
|
-
// // });
|
|
281
|
-
// // this.peer.on("disconnected", () => {
|
|
282
|
-
// // });
|
|
283
|
-
// // this.peer.on("close", () => {
|
|
284
|
-
// // });
|
|
285
529
|
}
|
|
286
530
|
|
|
287
531
|
startSendingVideo(stream: MediaStream) {
|
|
@@ -291,8 +535,18 @@ class NetworkedVideo {
|
|
|
291
535
|
};
|
|
292
536
|
}
|
|
293
537
|
|
|
294
|
-
stopSendingVideo(_steam: MediaStream) {
|
|
295
|
-
|
|
538
|
+
stopSendingVideo(_steam: MediaStream | undefined | null) {
|
|
539
|
+
if (_steam) {
|
|
540
|
+
const calls = this._sendingVideoStreams.get(_steam);
|
|
541
|
+
if (calls) {
|
|
542
|
+
for (const call of calls) {
|
|
543
|
+
call.close();
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
this._sendingVideoStreams.delete(_steam);
|
|
547
|
+
if (calls)
|
|
548
|
+
console.log("Currently sending", this._sendingVideoStreams);
|
|
549
|
+
}
|
|
296
550
|
}
|
|
297
551
|
|
|
298
552
|
private onConnectRoomFn: Function = this.onConnectRoom.bind(this);
|
|
@@ -300,14 +554,22 @@ class NetworkedVideo {
|
|
|
300
554
|
private onUserLeftFn: Function = this.onUserLeft.bind(this);
|
|
301
555
|
|
|
302
556
|
enable() {
|
|
303
|
-
this.
|
|
557
|
+
this.peer.enable();
|
|
558
|
+
this.context.connection.beginListen(PeerEvent.Connected, this.onUserConnectedFn);
|
|
559
|
+
this.peer.addEventListener("receive-video", this.onReceiveVideo.bind(this));
|
|
304
560
|
}
|
|
305
561
|
|
|
306
562
|
disable() {
|
|
563
|
+
this.peer.disable();
|
|
307
564
|
// this.context.connection.stopListening(RoomEvents.UserJoinedRoom, this.onUserConnectedFn);
|
|
308
565
|
// this.context.connection.stopListening(RoomEvents.UserLeftRoom, this.onUserLeftFn);
|
|
309
566
|
}
|
|
310
567
|
|
|
568
|
+
private onReceiveVideo(evt) {
|
|
569
|
+
console.log("RECEIVE VIDEO", evt);
|
|
570
|
+
this.dispatchEvent({ type: "receive-video", target: this, stream: evt.stream, userId: evt.userId });
|
|
571
|
+
}
|
|
572
|
+
|
|
311
573
|
private onConnectRoom() {
|
|
312
574
|
|
|
313
575
|
}
|
|
@@ -316,41 +578,51 @@ class NetworkedVideo {
|
|
|
316
578
|
// console.log(this.peer.id, user.guid)
|
|
317
579
|
if (this.peer.id === user.guid) {
|
|
318
580
|
console.log("USER CONNECTED", user);
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
// this.updateSendingCalls();
|
|
322
|
-
const call = this.peer.makeCall(user.peerId, this._sendingVideoStreams.keys().next().value);
|
|
323
|
-
|
|
324
|
-
// for (const userId of this.context.connection.usersInRoom()) {
|
|
325
|
-
// const id = this.peer.getPeerIdFromUserId(userId);
|
|
326
|
-
// if(id === user.peerId) console.log(userId);
|
|
327
|
-
// }
|
|
581
|
+
const stream = this._sendingVideoStreams.keys().next().value;
|
|
582
|
+
this.peer.makeCall(user.peerId, stream);
|
|
328
583
|
}
|
|
329
584
|
}
|
|
330
585
|
|
|
331
586
|
private onUserLeft(_: UserJoinedOrLeftRoomModel) {
|
|
332
|
-
|
|
587
|
+
this.stopCallsToUsersThatAreNotInTheRoomAnymore();
|
|
333
588
|
}
|
|
334
589
|
|
|
335
590
|
private updateSendingCalls() {
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
591
|
+
let startedNewCall = false;
|
|
592
|
+
for (const stream of this._sendingVideoStreams.keys()) {
|
|
593
|
+
const calls = this._sendingVideoStreams.get(stream) || [];
|
|
594
|
+
for (const userId of this.context.connection.usersInRoom()) {
|
|
595
|
+
if (userId === this.context.connection.connectionId) continue;
|
|
596
|
+
const existing = calls.find(c => c.userId === userId);
|
|
597
|
+
if (!existing) {
|
|
598
|
+
const handle = this.peer.makeCall(this.peer.getPeerIdFromUserId(userId), stream);
|
|
599
|
+
if (handle) {
|
|
600
|
+
startedNewCall = true;
|
|
601
|
+
calls.push(handle);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
this._sendingVideoStreams.set(stream, calls);
|
|
607
|
+
}
|
|
608
|
+
this.stopCallsToUsersThatAreNotInTheRoomAnymore();
|
|
609
|
+
if (startedNewCall) {
|
|
610
|
+
console.log("Currently sending", this._sendingVideoStreams);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
private stopCallsToUsersThatAreNotInTheRoomAnymore() {
|
|
615
|
+
for (const stream of this._sendingVideoStreams.keys()) {
|
|
616
|
+
const calls = this._sendingVideoStreams.get(stream);
|
|
617
|
+
if (!calls) continue;
|
|
618
|
+
for (let i = calls.length - 1; i >= 0; i--) {
|
|
619
|
+
const call = calls[i];
|
|
620
|
+
if (!this.context.connection.userIsInRoom(call.userId)) {
|
|
621
|
+
call.close();
|
|
622
|
+
calls.splice(i, 1);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
354
626
|
}
|
|
355
627
|
|
|
356
628
|
// const call = peer.call(peerId, stream);
|