@epicgames-ps/lib-pixelstreamingfrontend-ue5.5 0.0.8 → 0.0.11
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/.eslintignore +6 -2
- package/.eslintrc.js +12 -1
- package/dist/lib-pixelstreamingfrontend.esm.js +1 -1
- package/dist/lib-pixelstreamingfrontend.js +1 -1
- package/package.json +4 -6
- package/src/Config/Config.ts +45 -31
- package/src/Config/SettingOption.ts +1 -1
- package/src/DataChannel/DataChannelLatencyTestController.ts +3 -3
- package/src/Inputs/GamepadController.ts +6 -1
- package/src/Inputs/TouchController.ts +2 -2
- package/src/PeerConnectionController/AggregatedStats.ts +34 -13
- package/src/PeerConnectionController/CandidatePairStats.ts +10 -3
- package/src/PeerConnectionController/CandidateStat.ts +4 -2
- package/src/PixelStreaming/PixelStreaming.test.ts +3 -3
- package/src/PixelStreaming/PixelStreaming.ts +34 -1
- package/src/UI/OnScreenKeyboard.ts +1 -4
- package/src/Util/EventEmitter.ts +12 -1
- package/src/WebRtcPlayer/WebRtcPlayerController.ts +20 -22
- package/src/WebXR/WebXRController.ts +1 -1
- package/types/Config/Config.d.ts +1 -0
- package/types/Inputs/GamepadController.d.ts +1 -1
- package/types/PeerConnectionController/AggregatedStats.d.ts +8 -1
- package/types/PeerConnectionController/CandidatePairStats.d.ts +10 -3
- package/types/PeerConnectionController/CandidateStat.d.ts +4 -2
- package/types/PixelStreaming/PixelStreaming.d.ts +2 -1
- package/types/Util/EventEmitter.d.ts +8 -1
- package/types/WebRtcPlayer/WebRtcPlayerController.d.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@epicgames-ps/lib-pixelstreamingfrontend-ue5.5",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.11",
|
|
4
4
|
"description": "Frontend library for Unreal Engine 5.5 Pixel Streaming",
|
|
5
5
|
"main": "dist/lib-pixelstreamingfrontend.js",
|
|
6
6
|
"module": "dist/lib-pixelstreamingfrontend.esm.js",
|
|
@@ -16,11 +16,10 @@
|
|
|
16
16
|
"spellcheck": "cspell \"{README.md,.github/*.md,src/**/*.ts}\""
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
|
-
"@epicgames-ps/lib-pixelstreamingcommon-ue5.5": "^0.0.
|
|
19
|
+
"@epicgames-ps/lib-pixelstreamingcommon-ue5.5": "^0.0.13",
|
|
20
20
|
"@types/jest": "27.5.1",
|
|
21
21
|
"@types/webxr": "^0.5.1",
|
|
22
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
23
|
-
"@typescript-eslint/parser": "^5.16.0",
|
|
22
|
+
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
|
24
23
|
"cspell": "^4.1.0",
|
|
25
24
|
"eslint": "^8.11.0",
|
|
26
25
|
"jest": "^27.5.1",
|
|
@@ -38,7 +37,7 @@
|
|
|
38
37
|
},
|
|
39
38
|
"repository": {
|
|
40
39
|
"type": "git",
|
|
41
|
-
"url": "https://github.com/
|
|
40
|
+
"url": "https://github.com/EpicGamesExt/PixelStreamingInfrastructure.git"
|
|
42
41
|
},
|
|
43
42
|
"author": "Epic Games",
|
|
44
43
|
"license": "MIT",
|
|
@@ -46,4 +45,3 @@
|
|
|
46
45
|
"access": "public"
|
|
47
46
|
}
|
|
48
47
|
}
|
|
49
|
-
|
package/src/Config/Config.ts
CHANGED
|
@@ -31,7 +31,8 @@ export class Flags {
|
|
|
31
31
|
static TouchInput = 'TouchInput' as const;
|
|
32
32
|
static GamepadInput = 'GamepadInput' as const;
|
|
33
33
|
static XRControllerInput = 'XRControllerInput' as const;
|
|
34
|
-
static WaitForStreamer =
|
|
34
|
+
static WaitForStreamer = 'WaitForStreamer' as const;
|
|
35
|
+
static HideUI = 'HideUI' as const;
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
export type FlagsKeys = Exclude<keyof typeof Flags, 'prototype'>;
|
|
@@ -181,7 +182,7 @@ export class Config {
|
|
|
181
182
|
TextParameters.SignallingServerUrl,
|
|
182
183
|
'Signalling url',
|
|
183
184
|
'Url of the signalling server',
|
|
184
|
-
settings &&
|
|
185
|
+
settings && Object.prototype.hasOwnProperty.call(settings, TextParameters.SignallingServerUrl) ?
|
|
185
186
|
settings[TextParameters.SignallingServerUrl] :
|
|
186
187
|
(location.protocol === 'https:' ? 'wss://' : 'ws://') +
|
|
187
188
|
window.location.hostname +
|
|
@@ -200,7 +201,7 @@ export class Config {
|
|
|
200
201
|
OptionParameters.StreamerId,
|
|
201
202
|
'Streamer ID',
|
|
202
203
|
'The ID of the streamer to stream.',
|
|
203
|
-
settings &&
|
|
204
|
+
settings && Object.prototype.hasOwnProperty.call(settings, OptionParameters.StreamerId) ?
|
|
204
205
|
settings[OptionParameters.StreamerId] :
|
|
205
206
|
'',
|
|
206
207
|
[],
|
|
@@ -218,7 +219,7 @@ export class Config {
|
|
|
218
219
|
'Preferred Codec',
|
|
219
220
|
'The preferred codec to be used during codec negotiation',
|
|
220
221
|
'H264 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f',
|
|
221
|
-
settings &&
|
|
222
|
+
settings && Object.prototype.hasOwnProperty.call(settings, OptionParameters.PreferredCodec) ?
|
|
222
223
|
[settings[OptionParameters.PreferredCodec]] :
|
|
223
224
|
(function (): Array<string> {
|
|
224
225
|
const browserSupportedCodecs: Array<string> = [];
|
|
@@ -257,7 +258,7 @@ export class Config {
|
|
|
257
258
|
Flags.AutoConnect,
|
|
258
259
|
'Auto connect to stream',
|
|
259
260
|
'Whether we should attempt to auto connect to the signalling server or show a click to start prompt.',
|
|
260
|
-
settings &&
|
|
261
|
+
settings && Object.prototype.hasOwnProperty.call(settings, Flags.AutoConnect) ?
|
|
261
262
|
settings[Flags.AutoConnect] :
|
|
262
263
|
false,
|
|
263
264
|
useUrlParams
|
|
@@ -270,7 +271,7 @@ export class Config {
|
|
|
270
271
|
Flags.AutoPlayVideo,
|
|
271
272
|
'Auto play video',
|
|
272
273
|
'When video is ready automatically start playing it as opposed to showing a play button.',
|
|
273
|
-
settings &&
|
|
274
|
+
settings && Object.prototype.hasOwnProperty.call(settings, Flags.AutoPlayVideo) ?
|
|
274
275
|
settings[Flags.AutoPlayVideo] :
|
|
275
276
|
true,
|
|
276
277
|
useUrlParams
|
|
@@ -283,7 +284,7 @@ export class Config {
|
|
|
283
284
|
Flags.BrowserSendOffer,
|
|
284
285
|
'Browser send offer',
|
|
285
286
|
'Browser will initiate the WebRTC handshake by sending the offer to the streamer',
|
|
286
|
-
settings &&
|
|
287
|
+
settings && Object.prototype.hasOwnProperty.call(settings, Flags.BrowserSendOffer) ?
|
|
287
288
|
settings[Flags.BrowserSendOffer] :
|
|
288
289
|
false,
|
|
289
290
|
useUrlParams
|
|
@@ -296,7 +297,7 @@ export class Config {
|
|
|
296
297
|
Flags.UseMic,
|
|
297
298
|
'Use microphone',
|
|
298
299
|
'Make browser request microphone access and open an input audio track.',
|
|
299
|
-
settings &&
|
|
300
|
+
settings && Object.prototype.hasOwnProperty.call(settings, Flags.UseMic) ?
|
|
300
301
|
settings[Flags.UseMic] :
|
|
301
302
|
false,
|
|
302
303
|
useUrlParams
|
|
@@ -309,7 +310,7 @@ export class Config {
|
|
|
309
310
|
Flags.StartVideoMuted,
|
|
310
311
|
'Start video muted',
|
|
311
312
|
'Video will start muted if true.',
|
|
312
|
-
settings &&
|
|
313
|
+
settings && Object.prototype.hasOwnProperty.call(settings, Flags.StartVideoMuted) ?
|
|
313
314
|
settings[Flags.StartVideoMuted] :
|
|
314
315
|
false,
|
|
315
316
|
useUrlParams
|
|
@@ -322,7 +323,7 @@ export class Config {
|
|
|
322
323
|
Flags.SuppressBrowserKeys,
|
|
323
324
|
'Suppress browser keys',
|
|
324
325
|
'Suppress certain browser keys that we use in UE, for example F5 to show shader complexity instead of refresh the page.',
|
|
325
|
-
settings &&
|
|
326
|
+
settings && Object.prototype.hasOwnProperty.call(settings, Flags.SuppressBrowserKeys) ?
|
|
326
327
|
settings[Flags.SuppressBrowserKeys] :
|
|
327
328
|
true,
|
|
328
329
|
useUrlParams
|
|
@@ -335,7 +336,7 @@ export class Config {
|
|
|
335
336
|
Flags.IsQualityController,
|
|
336
337
|
'Is quality controller?',
|
|
337
338
|
'True if this peer controls stream quality',
|
|
338
|
-
settings &&
|
|
339
|
+
settings && Object.prototype.hasOwnProperty.call(settings, Flags.IsQualityController) ?
|
|
339
340
|
settings[Flags.IsQualityController] :
|
|
340
341
|
true,
|
|
341
342
|
useUrlParams
|
|
@@ -348,7 +349,7 @@ export class Config {
|
|
|
348
349
|
Flags.ForceMonoAudio,
|
|
349
350
|
'Force mono audio',
|
|
350
351
|
'Force browser to request mono audio in the SDP',
|
|
351
|
-
settings &&
|
|
352
|
+
settings && Object.prototype.hasOwnProperty.call(settings, Flags.ForceMonoAudio) ?
|
|
352
353
|
settings[Flags.ForceMonoAudio] :
|
|
353
354
|
false,
|
|
354
355
|
useUrlParams
|
|
@@ -361,7 +362,7 @@ export class Config {
|
|
|
361
362
|
Flags.ForceTURN,
|
|
362
363
|
'Force TURN',
|
|
363
364
|
'Only generate TURN/Relayed ICE candidates.',
|
|
364
|
-
settings &&
|
|
365
|
+
settings && Object.prototype.hasOwnProperty.call(settings, Flags.ForceTURN) ?
|
|
365
366
|
settings[Flags.ForceTURN] :
|
|
366
367
|
false,
|
|
367
368
|
useUrlParams
|
|
@@ -374,7 +375,7 @@ export class Config {
|
|
|
374
375
|
Flags.AFKDetection,
|
|
375
376
|
'AFK if idle',
|
|
376
377
|
'Timeout the experience if user is AFK for a period.',
|
|
377
|
-
settings &&
|
|
378
|
+
settings && Object.prototype.hasOwnProperty.call(settings, Flags.AFKDetection) ?
|
|
378
379
|
settings[Flags.AFKDetection] :
|
|
379
380
|
false,
|
|
380
381
|
useUrlParams
|
|
@@ -387,7 +388,7 @@ export class Config {
|
|
|
387
388
|
Flags.MatchViewportResolution,
|
|
388
389
|
'Match viewport resolution',
|
|
389
390
|
'Pixel Streaming will be instructed to dynamically resize the video stream to match the size of the video element.',
|
|
390
|
-
settings &&
|
|
391
|
+
settings && Object.prototype.hasOwnProperty.call(settings, Flags.MatchViewportResolution) ?
|
|
391
392
|
settings[Flags.MatchViewportResolution] :
|
|
392
393
|
false,
|
|
393
394
|
useUrlParams
|
|
@@ -400,7 +401,7 @@ export class Config {
|
|
|
400
401
|
Flags.HoveringMouseMode,
|
|
401
402
|
'Control Scheme: Locked Mouse',
|
|
402
403
|
'Either locked mouse, where the pointer is consumed by the video and locked to it, or hovering mouse, where the mouse is not consumed.',
|
|
403
|
-
settings &&
|
|
404
|
+
settings && Object.prototype.hasOwnProperty.call(settings, Flags.HoveringMouseMode) ?
|
|
404
405
|
settings[Flags.HoveringMouseMode] :
|
|
405
406
|
false,
|
|
406
407
|
useUrlParams,
|
|
@@ -416,7 +417,7 @@ export class Config {
|
|
|
416
417
|
Flags.FakeMouseWithTouches,
|
|
417
418
|
'Fake mouse with touches',
|
|
418
419
|
'A single finger touch is converted into a mouse event. This allows a non-touch application to be controlled partially via a touch device.',
|
|
419
|
-
settings &&
|
|
420
|
+
settings && Object.prototype.hasOwnProperty.call(settings, Flags.FakeMouseWithTouches) ?
|
|
420
421
|
settings[Flags.FakeMouseWithTouches] :
|
|
421
422
|
true,
|
|
422
423
|
useUrlParams
|
|
@@ -429,7 +430,7 @@ export class Config {
|
|
|
429
430
|
Flags.KeyboardInput,
|
|
430
431
|
'Keyboard input',
|
|
431
432
|
'If enabled, send keyboard events to streamer',
|
|
432
|
-
settings &&
|
|
433
|
+
settings && Object.prototype.hasOwnProperty.call(settings, Flags.KeyboardInput) ?
|
|
433
434
|
settings[Flags.KeyboardInput] :
|
|
434
435
|
true,
|
|
435
436
|
useUrlParams
|
|
@@ -442,7 +443,7 @@ export class Config {
|
|
|
442
443
|
Flags.MouseInput,
|
|
443
444
|
'Mouse input',
|
|
444
445
|
'If enabled, send mouse events to streamer',
|
|
445
|
-
settings &&
|
|
446
|
+
settings && Object.prototype.hasOwnProperty.call(settings, Flags.MouseInput) ?
|
|
446
447
|
settings[Flags.MouseInput] :
|
|
447
448
|
true,
|
|
448
449
|
useUrlParams
|
|
@@ -455,7 +456,7 @@ export class Config {
|
|
|
455
456
|
Flags.TouchInput,
|
|
456
457
|
'Touch input',
|
|
457
458
|
'If enabled, send touch events to streamer',
|
|
458
|
-
settings &&
|
|
459
|
+
settings && Object.prototype.hasOwnProperty.call(settings, Flags.TouchInput) ?
|
|
459
460
|
settings[Flags.TouchInput] :
|
|
460
461
|
true,
|
|
461
462
|
useUrlParams
|
|
@@ -468,7 +469,7 @@ export class Config {
|
|
|
468
469
|
Flags.GamepadInput,
|
|
469
470
|
'Gamepad input',
|
|
470
471
|
'If enabled, send gamepad events to streamer',
|
|
471
|
-
settings &&
|
|
472
|
+
settings && Object.prototype.hasOwnProperty.call(settings, Flags.GamepadInput) ?
|
|
472
473
|
settings[Flags.GamepadInput] :
|
|
473
474
|
true,
|
|
474
475
|
useUrlParams
|
|
@@ -481,7 +482,7 @@ export class Config {
|
|
|
481
482
|
Flags.XRControllerInput,
|
|
482
483
|
'XR controller input',
|
|
483
484
|
'If enabled, send XR controller events to streamer',
|
|
484
|
-
settings &&
|
|
485
|
+
settings && Object.prototype.hasOwnProperty.call(settings, Flags.XRControllerInput) ?
|
|
485
486
|
settings[Flags.XRControllerInput] :
|
|
486
487
|
true,
|
|
487
488
|
useUrlParams
|
|
@@ -494,12 +495,25 @@ export class Config {
|
|
|
494
495
|
Flags.WaitForStreamer,
|
|
495
496
|
'Wait for streamer',
|
|
496
497
|
'Will continue trying to connect to the first streamer available.',
|
|
497
|
-
settings &&
|
|
498
|
+
settings && Object.prototype.hasOwnProperty.call(settings, Flags.WaitForStreamer) ?
|
|
498
499
|
settings[Flags.WaitForStreamer] :
|
|
499
500
|
true,
|
|
500
501
|
useUrlParams
|
|
501
502
|
)
|
|
502
503
|
);
|
|
504
|
+
|
|
505
|
+
this.flags.set(
|
|
506
|
+
Flags.HideUI,
|
|
507
|
+
new SettingFlag(
|
|
508
|
+
Flags.HideUI,
|
|
509
|
+
'Hide the UI overlay',
|
|
510
|
+
'Will hide all UI overlay details',
|
|
511
|
+
settings && settings.hasOwnProperty(Flags.HideUI) ?
|
|
512
|
+
settings[Flags.HideUI] :
|
|
513
|
+
false,
|
|
514
|
+
useUrlParams
|
|
515
|
+
)
|
|
516
|
+
);
|
|
503
517
|
|
|
504
518
|
/**
|
|
505
519
|
* Numeric parameters
|
|
@@ -513,7 +527,7 @@ export class Config {
|
|
|
513
527
|
'The time (in seconds) it takes for the application to time out if AFK timeout is enabled.',
|
|
514
528
|
0 /*min*/,
|
|
515
529
|
600 /*max*/,
|
|
516
|
-
settings &&
|
|
530
|
+
settings && Object.prototype.hasOwnProperty.call(settings, NumericParameters.AFKTimeoutSecs) ?
|
|
517
531
|
settings[NumericParameters.AFKTimeoutSecs] :
|
|
518
532
|
120, /*value*/
|
|
519
533
|
useUrlParams
|
|
@@ -528,7 +542,7 @@ export class Config {
|
|
|
528
542
|
'Maximum number of reconnects the application will attempt when a streamer disconnects.',
|
|
529
543
|
0 /*min*/,
|
|
530
544
|
999 /*max*/,
|
|
531
|
-
settings &&
|
|
545
|
+
settings && Object.prototype.hasOwnProperty.call(settings, NumericParameters.MaxReconnectAttempts) ?
|
|
532
546
|
settings[NumericParameters.MaxReconnectAttempts] :
|
|
533
547
|
3, /*value*/
|
|
534
548
|
useUrlParams
|
|
@@ -543,7 +557,7 @@ export class Config {
|
|
|
543
557
|
'The lower bound for the quantization parameter (QP) of the encoder. 0 = Best quality, 51 = worst quality.',
|
|
544
558
|
0 /*min*/,
|
|
545
559
|
51 /*max*/,
|
|
546
|
-
settings &&
|
|
560
|
+
settings && Object.prototype.hasOwnProperty.call(settings, NumericParameters.MinQP) ?
|
|
547
561
|
settings[NumericParameters.MinQP] :
|
|
548
562
|
0, /*value*/
|
|
549
563
|
useUrlParams
|
|
@@ -558,7 +572,7 @@ export class Config {
|
|
|
558
572
|
'The upper bound for the quantization parameter (QP) of the encoder. 0 = Best quality, 51 = worst quality.',
|
|
559
573
|
0 /*min*/,
|
|
560
574
|
51 /*max*/,
|
|
561
|
-
settings &&
|
|
575
|
+
settings && Object.prototype.hasOwnProperty.call(settings, NumericParameters.MaxQP) ?
|
|
562
576
|
settings[NumericParameters.MaxQP] :
|
|
563
577
|
51, /*value*/
|
|
564
578
|
useUrlParams
|
|
@@ -573,7 +587,7 @@ export class Config {
|
|
|
573
587
|
'The maximum FPS that WebRTC will try to transmit frames at.',
|
|
574
588
|
1 /*min*/,
|
|
575
589
|
999 /*max*/,
|
|
576
|
-
settings &&
|
|
590
|
+
settings && Object.prototype.hasOwnProperty.call(settings, NumericParameters.WebRTCFPS) ?
|
|
577
591
|
settings[NumericParameters.WebRTCFPS] :
|
|
578
592
|
60, /*value*/
|
|
579
593
|
useUrlParams
|
|
@@ -588,7 +602,7 @@ export class Config {
|
|
|
588
602
|
'The minimum bitrate that WebRTC should use.',
|
|
589
603
|
0 /*min*/,
|
|
590
604
|
500000 /*max*/,
|
|
591
|
-
settings &&
|
|
605
|
+
settings && Object.prototype.hasOwnProperty.call(settings, NumericParameters.WebRTCMinBitrate) ?
|
|
592
606
|
settings[NumericParameters.WebRTCMinBitrate] :
|
|
593
607
|
0, /*value*/
|
|
594
608
|
useUrlParams
|
|
@@ -603,7 +617,7 @@ export class Config {
|
|
|
603
617
|
'The maximum bitrate that WebRTC should use.',
|
|
604
618
|
0 /*min*/,
|
|
605
619
|
500000 /*max*/,
|
|
606
|
-
settings &&
|
|
620
|
+
settings && Object.prototype.hasOwnProperty.call(settings, NumericParameters.WebRTCMaxBitrate) ?
|
|
607
621
|
settings[NumericParameters.WebRTCMaxBitrate] :
|
|
608
622
|
0, /*value*/
|
|
609
623
|
useUrlParams
|
|
@@ -618,7 +632,7 @@ export class Config {
|
|
|
618
632
|
'Delay between retries when waiting for an available streamer.',
|
|
619
633
|
500 /*min*/,
|
|
620
634
|
900000 /*max*/,
|
|
621
|
-
settings &&
|
|
635
|
+
settings && Object.prototype.hasOwnProperty.call(settings, NumericParameters.StreamerAutoJoinInterval) ?
|
|
622
636
|
settings[NumericParameters.StreamerAutoJoinInterval] :
|
|
623
637
|
3000, /*value*/
|
|
624
638
|
useUrlParams
|
|
@@ -24,7 +24,7 @@ export class SettingOption<
|
|
|
24
24
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
25
25
|
defaultOnChangeListener: (changedValue: unknown, setting: SettingBase) => void = () => { /* Do nothing, to be overridden. */ }
|
|
26
26
|
) {
|
|
27
|
-
super(id, label, description,
|
|
27
|
+
super(id, label, description, defaultTextValue, defaultOnChangeListener);
|
|
28
28
|
|
|
29
29
|
this.options = options;
|
|
30
30
|
const urlParams = new URLSearchParams(window.location.search);
|
|
@@ -105,15 +105,15 @@ export class DataChannelLatencyTestController {
|
|
|
105
105
|
);
|
|
106
106
|
return;
|
|
107
107
|
}
|
|
108
|
-
|
|
108
|
+
const record = this.records.get(response.Seq);
|
|
109
109
|
if (record) {
|
|
110
110
|
record.update(response);
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
sendRequest(requestSize: number, responseSize: number) {
|
|
115
|
-
|
|
116
|
-
|
|
115
|
+
const request = this.createRequest(requestSize, responseSize);
|
|
116
|
+
const record = new DataChannelLatencyTestRecord(request);
|
|
117
117
|
this.records.set(record.seq, record);
|
|
118
118
|
this.sink(request);
|
|
119
119
|
}
|
|
@@ -260,7 +260,7 @@ export class GamePadController {
|
|
|
260
260
|
// Default Functionality: Do Nothing
|
|
261
261
|
}
|
|
262
262
|
|
|
263
|
-
onBeforeUnload(
|
|
263
|
+
onBeforeUnload(_: Event) {
|
|
264
264
|
// When a user navigates away from the page, we need to inform UE of all the disconnecting
|
|
265
265
|
// controllers
|
|
266
266
|
for(const controller of this.controllers) {
|
|
@@ -285,6 +285,8 @@ declare global {
|
|
|
285
285
|
}
|
|
286
286
|
}
|
|
287
287
|
|
|
288
|
+
/* eslint-disable @typescript-eslint/no-duplicate-enum-values */
|
|
289
|
+
|
|
288
290
|
/**
|
|
289
291
|
* Gamepad layout codes enum
|
|
290
292
|
*/
|
|
@@ -312,3 +314,6 @@ export enum gamepadLayout {
|
|
|
312
314
|
RightStickHorizontal = 2,
|
|
313
315
|
RightStickVertical = 3
|
|
314
316
|
}
|
|
317
|
+
|
|
318
|
+
/* eslint-enable @typescript-eslint/no-duplicate-enum-values */
|
|
319
|
+
|
|
@@ -178,7 +178,7 @@ export class TouchController implements ITouchController {
|
|
|
178
178
|
coord.x,
|
|
179
179
|
coord.y,
|
|
180
180
|
this.fingerIds.get(touch.identifier),
|
|
181
|
-
this.maxByteValue * touch.force,
|
|
181
|
+
this.maxByteValue * (touch.force > 0 ? touch.force : 1),
|
|
182
182
|
coord.inRange ? 1 : 0
|
|
183
183
|
]);
|
|
184
184
|
break;
|
|
@@ -198,7 +198,7 @@ export class TouchController implements ITouchController {
|
|
|
198
198
|
coord.x,
|
|
199
199
|
coord.y,
|
|
200
200
|
this.fingerIds.get(touch.identifier),
|
|
201
|
-
this.maxByteValue * touch.force,
|
|
201
|
+
this.maxByteValue * (touch.force > 0 ? touch.force : 1),
|
|
202
202
|
coord.inRange ? 1 : 0
|
|
203
203
|
]);
|
|
204
204
|
break;
|
|
@@ -25,7 +25,7 @@ export class AggregatedStats {
|
|
|
25
25
|
inboundAudioStats: InboundAudioStats;
|
|
26
26
|
lastVideoStats: InboundVideoStats;
|
|
27
27
|
lastAudioStats: InboundAudioStats;
|
|
28
|
-
|
|
28
|
+
candidatePairs: Array<CandidatePairStats>;
|
|
29
29
|
DataChannelStats: DataChannelStats;
|
|
30
30
|
localCandidates: Array<CandidateStat>;
|
|
31
31
|
remoteCandidates: Array<CandidateStat>;
|
|
@@ -33,11 +33,11 @@ export class AggregatedStats {
|
|
|
33
33
|
sessionStats: SessionStats;
|
|
34
34
|
streamStats: StreamStats;
|
|
35
35
|
codecs: Map<string, string>;
|
|
36
|
+
transportStats: RTCTransportStats;
|
|
36
37
|
|
|
37
38
|
constructor() {
|
|
38
39
|
this.inboundVideoStats = new InboundVideoStats();
|
|
39
40
|
this.inboundAudioStats = new InboundAudioStats();
|
|
40
|
-
this.candidatePair = new CandidatePairStats();
|
|
41
41
|
this.DataChannelStats = new DataChannelStats();
|
|
42
42
|
this.outBoundVideoStats = new OutBoundVideoStats();
|
|
43
43
|
this.sessionStats = new SessionStats();
|
|
@@ -52,6 +52,7 @@ export class AggregatedStats {
|
|
|
52
52
|
processStats(rtcStatsReport: RTCStatsReport) {
|
|
53
53
|
this.localCandidates = new Array<CandidateStat>();
|
|
54
54
|
this.remoteCandidates = new Array<CandidateStat>();
|
|
55
|
+
this.candidatePairs = new Array<CandidatePairStats>();
|
|
55
56
|
|
|
56
57
|
rtcStatsReport.forEach((stat) => {
|
|
57
58
|
const type: RTCStatsTypePS = stat.type;
|
|
@@ -94,6 +95,7 @@ export class AggregatedStats {
|
|
|
94
95
|
this.handleTrack(stat);
|
|
95
96
|
break;
|
|
96
97
|
case 'transport':
|
|
98
|
+
this.handleTransport(stat);
|
|
97
99
|
break;
|
|
98
100
|
case 'stream':
|
|
99
101
|
this.handleStream(stat);
|
|
@@ -120,16 +122,10 @@ export class AggregatedStats {
|
|
|
120
122
|
* @param stat - the stats coming in from ice candidates
|
|
121
123
|
*/
|
|
122
124
|
handleCandidatePair(stat: CandidatePairStats) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
this.
|
|
126
|
-
|
|
127
|
-
this.candidatePair.nominated = stat.nominated;
|
|
128
|
-
this.candidatePair.readable = stat.readable;
|
|
129
|
-
this.candidatePair.selected = stat.selected;
|
|
130
|
-
this.candidatePair.writable = stat.writable;
|
|
131
|
-
this.candidatePair.state = stat.state;
|
|
132
|
-
this.candidatePair.currentRoundTripTime = stat.currentRoundTripTime;
|
|
125
|
+
|
|
126
|
+
// Add the candidate pair to the candidate pair array
|
|
127
|
+
this.candidatePairs.push(stat)
|
|
128
|
+
|
|
133
129
|
}
|
|
134
130
|
|
|
135
131
|
/**
|
|
@@ -162,6 +158,8 @@ export class AggregatedStats {
|
|
|
162
158
|
localCandidate.protocol = stat.protocol;
|
|
163
159
|
localCandidate.candidateType = stat.candidateType;
|
|
164
160
|
localCandidate.id = stat.id;
|
|
161
|
+
localCandidate.relayProtocol = stat.relayProtocol;
|
|
162
|
+
localCandidate.transportId = stat.transportId;
|
|
165
163
|
this.localCandidates.push(localCandidate);
|
|
166
164
|
}
|
|
167
165
|
|
|
@@ -171,12 +169,14 @@ export class AggregatedStats {
|
|
|
171
169
|
*/
|
|
172
170
|
handleRemoteCandidate(stat: CandidateStat) {
|
|
173
171
|
const RemoteCandidate = new CandidateStat();
|
|
174
|
-
RemoteCandidate.label = '
|
|
172
|
+
RemoteCandidate.label = 'remote-candidate';
|
|
175
173
|
RemoteCandidate.address = stat.address;
|
|
176
174
|
RemoteCandidate.port = stat.port;
|
|
177
175
|
RemoteCandidate.protocol = stat.protocol;
|
|
178
176
|
RemoteCandidate.id = stat.id;
|
|
179
177
|
RemoteCandidate.candidateType = stat.candidateType;
|
|
178
|
+
RemoteCandidate.relayProtocol = stat.relayProtocol;
|
|
179
|
+
RemoteCandidate.transportId = stat.transportId
|
|
180
180
|
this.remoteCandidates.push(RemoteCandidate);
|
|
181
181
|
}
|
|
182
182
|
|
|
@@ -269,6 +269,11 @@ export class AggregatedStats {
|
|
|
269
269
|
}
|
|
270
270
|
}
|
|
271
271
|
|
|
272
|
+
handleTransport(stat: RTCTransportStats){
|
|
273
|
+
this.transportStats = stat;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
|
|
272
277
|
handleCodec(stat: CodecStats) {
|
|
273
278
|
const codecId = stat.id;
|
|
274
279
|
const codecType = `${stat.mimeType
|
|
@@ -308,4 +313,20 @@ export class AggregatedStats {
|
|
|
308
313
|
isNumber(value: unknown): boolean {
|
|
309
314
|
return typeof value === 'number' && isFinite(value);
|
|
310
315
|
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Helper function to return the active candidate pair
|
|
319
|
+
* @returns The candidate pair that is currently receiving data
|
|
320
|
+
*/
|
|
321
|
+
public getActiveCandidatePair(): CandidatePairStats | null {
|
|
322
|
+
|
|
323
|
+
// Check if the RTCTransport stat is not undefined
|
|
324
|
+
if (this.transportStats){
|
|
325
|
+
// Return the candidate pair that matches the transport candidate pair id
|
|
326
|
+
return this.candidatePairs.find((candidatePair) => candidatePair.id === this.transportStats.selectedCandidatePairId, null);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Fall back to the selected candidate pair
|
|
330
|
+
return this.candidatePairs.find((candidatePair) => candidatePair.selected, null);
|
|
331
|
+
}
|
|
311
332
|
}
|
|
@@ -6,12 +6,19 @@
|
|
|
6
6
|
export class CandidatePairStats {
|
|
7
7
|
bytesReceived: number;
|
|
8
8
|
bytesSent: number;
|
|
9
|
+
currentRoundTripTime: number;
|
|
10
|
+
id: string;
|
|
11
|
+
lastPacketReceivedTimestamp: number;
|
|
12
|
+
lastPacketSentTimestamp: number;
|
|
9
13
|
localCandidateId: string;
|
|
10
|
-
remoteCandidateId: string;
|
|
11
14
|
nominated: boolean;
|
|
15
|
+
priority: number;
|
|
12
16
|
readable: boolean;
|
|
13
|
-
|
|
17
|
+
remoteCandidateId: string;
|
|
14
18
|
selected: boolean;
|
|
15
19
|
state: string;
|
|
16
|
-
|
|
20
|
+
timestamp: number;
|
|
21
|
+
transportId: string;
|
|
22
|
+
type: string;
|
|
23
|
+
writable: boolean;
|
|
17
24
|
}
|
|
@@ -4,10 +4,12 @@
|
|
|
4
4
|
* ICE Candidate Stat collected from the RTC Stats Report
|
|
5
5
|
*/
|
|
6
6
|
export class CandidateStat {
|
|
7
|
-
label: string;
|
|
8
|
-
id: string;
|
|
9
7
|
address: string;
|
|
10
8
|
candidateType: string;
|
|
9
|
+
id: string;
|
|
10
|
+
label: string;
|
|
11
11
|
port: number;
|
|
12
12
|
protocol: 'tcp' | 'udp';
|
|
13
|
+
relayProtocol: 'tcp' | 'udp' | 'tls';
|
|
14
|
+
transportId: string;
|
|
13
15
|
}
|
|
@@ -400,9 +400,9 @@ describe('PixelStreaming', () => {
|
|
|
400
400
|
expect.objectContaining({
|
|
401
401
|
data: {
|
|
402
402
|
aggregatedStats: expect.objectContaining({
|
|
403
|
-
|
|
404
|
-
bytesReceived: 123
|
|
405
|
-
|
|
403
|
+
candidatePairs: [
|
|
404
|
+
expect.objectContaining({ bytesReceived: 123 })
|
|
405
|
+
],
|
|
406
406
|
localCandidates: [
|
|
407
407
|
expect.objectContaining({ address: 'mock-address' })
|
|
408
408
|
]
|
|
@@ -28,7 +28,8 @@ import {
|
|
|
28
28
|
WebRtcSdpEvent,
|
|
29
29
|
DataChannelLatencyTestResponseEvent,
|
|
30
30
|
DataChannelLatencyTestResultEvent,
|
|
31
|
-
PlayerCountEvent
|
|
31
|
+
PlayerCountEvent,
|
|
32
|
+
WebRtcTCPRelayDetectedEvent
|
|
32
33
|
} from '../Util/EventEmitter';
|
|
33
34
|
import { WebXRController } from '../WebXR/WebXRController';
|
|
34
35
|
import { MessageDirection } from '../UeInstanceMessage/StreamMessageController';
|
|
@@ -61,6 +62,7 @@ export class PixelStreaming {
|
|
|
61
62
|
protected _webRtcController: WebRtcPlayerController;
|
|
62
63
|
protected _webXrController: WebXRController;
|
|
63
64
|
protected _dataChannelLatencyTestController: DataChannelLatencyTestController;
|
|
65
|
+
|
|
64
66
|
/**
|
|
65
67
|
* Configuration object. You can read or modify config through this object. Whenever
|
|
66
68
|
* the configuration is changed, the library will emit a `settingsChanged` event.
|
|
@@ -115,6 +117,15 @@ export class PixelStreaming {
|
|
|
115
117
|
this.onScreenKeyboardHelper.showOnScreenKeyboard(command);
|
|
116
118
|
|
|
117
119
|
this._webXrController = new WebXRController(this._webRtcController);
|
|
120
|
+
|
|
121
|
+
this._setupWebRtcTCPRelayDetection = this._setupWebRtcTCPRelayDetection.bind(this)
|
|
122
|
+
|
|
123
|
+
// Add event listener for the webRtcConnected event
|
|
124
|
+
this._eventEmitter.addEventListener("webRtcConnected", (webRtcConnectedEvent: WebRtcConnectedEvent) => {
|
|
125
|
+
|
|
126
|
+
// Bind to the stats received event
|
|
127
|
+
this._eventEmitter.addEventListener("statsReceived", this._setupWebRtcTCPRelayDetection);
|
|
128
|
+
});
|
|
118
129
|
}
|
|
119
130
|
|
|
120
131
|
/**
|
|
@@ -626,6 +637,28 @@ export class PixelStreaming {
|
|
|
626
637
|
);
|
|
627
638
|
}
|
|
628
639
|
|
|
640
|
+
// Sets up to emit the webrtc tcp relay detect event
|
|
641
|
+
_setupWebRtcTCPRelayDetection(statsReceivedEvent: StatsReceivedEvent) {
|
|
642
|
+
// Get the active candidate pair
|
|
643
|
+
let activeCandidatePair = statsReceivedEvent.data.aggregatedStats.getActiveCandidatePair();
|
|
644
|
+
|
|
645
|
+
// Check if the active candidate pair is not null
|
|
646
|
+
if (activeCandidatePair != null) {
|
|
647
|
+
|
|
648
|
+
// Get the local candidate assigned to the active candidate pair
|
|
649
|
+
let localCandidate = statsReceivedEvent.data.aggregatedStats.localCandidates.find((candidate) => candidate.id == activeCandidatePair.localCandidateId, null)
|
|
650
|
+
|
|
651
|
+
// Check if the local candidate is not null, candidate type is relay and the relay protocol is tcp
|
|
652
|
+
if (localCandidate != null && localCandidate.candidateType == 'relay' && localCandidate.relayProtocol == 'tcp') {
|
|
653
|
+
|
|
654
|
+
// Send the web rtc tcp relay detected event
|
|
655
|
+
this._eventEmitter.dispatchEvent(new WebRtcTCPRelayDetectedEvent());
|
|
656
|
+
}
|
|
657
|
+
// The check is completed and the stats listen event can be removed
|
|
658
|
+
this._eventEmitter.removeEventListener("statsReceived", this._setupWebRtcTCPRelayDetection);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
|
|
629
662
|
/**
|
|
630
663
|
* Request a connection latency test.
|
|
631
664
|
* NOTE: There are plans to refactor all request* functions. Expect changes if you use this!
|
|
@@ -35,10 +35,7 @@ export class OnScreenKeyboard {
|
|
|
35
35
|
* @returns unquantizeAndDenormalizeUnsigned object
|
|
36
36
|
*/
|
|
37
37
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
38
|
-
unquantizeAndDenormalizeUnsigned(
|
|
39
|
-
x: number,
|
|
40
|
-
y: number
|
|
41
|
-
): UnquantizedDenormalizedUnsignedCoord {
|
|
38
|
+
unquantizeAndDenormalizeUnsigned(x: number, y: number): UnquantizedDenormalizedUnsignedCoord {
|
|
42
39
|
return null;
|
|
43
40
|
}
|
|
44
41
|
|