@epicgames-ps/lib-pixelstreamingfrontend-ue5.5 0.0.10 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@epicgames-ps/lib-pixelstreamingfrontend-ue5.5",
3
- "version": "0.0.10",
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,7 +16,7 @@
16
16
  "spellcheck": "cspell \"{README.md,.github/*.md,src/**/*.ts}\""
17
17
  },
18
18
  "devDependencies": {
19
- "@epicgames-ps/lib-pixelstreamingcommon-ue5.5": "^0.0.12",
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
22
  "@typescript-eslint/eslint-plugin": "^6.21.0",
@@ -37,7 +37,7 @@
37
37
  },
38
38
  "repository": {
39
39
  "type": "git",
40
- "url": "https://github.com/EpicGames/PixelStreamingInfrastructure.git"
40
+ "url": "https://github.com/EpicGamesExt/PixelStreamingInfrastructure.git"
41
41
  },
42
42
  "author": "Epic Games",
43
43
  "license": "MIT",
@@ -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 = "WaitForStreamer" as const;
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'>;
@@ -500,6 +501,19 @@ export class Config {
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
@@ -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, [defaultTextValue], defaultOnChangeListener);
27
+ super(id, label, description, defaultTextValue, defaultOnChangeListener);
28
28
 
29
29
  this.options = options;
30
30
  const urlParams = new URLSearchParams(window.location.search);
@@ -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
- candidatePair: CandidatePairStats;
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
- this.candidatePair.bytesReceived = stat.bytesReceived;
124
- this.candidatePair.bytesSent = stat.bytesSent;
125
- this.candidatePair.localCandidateId = stat.localCandidateId;
126
- this.candidatePair.remoteCandidateId = stat.remoteCandidateId;
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 = 'local-candidate';
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
- writable: boolean;
17
+ remoteCandidateId: string;
14
18
  selected: boolean;
15
19
  state: string;
16
- currentRoundTripTime: number;
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
- candidatePair: expect.objectContaining({
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!
@@ -537,6 +537,16 @@ export class PlayerCountEvent extends Event {
537
537
  }
538
538
  }
539
539
 
540
+ /**
541
+ * An event that is emitted when the webRTC connections is relayed over TCP.
542
+ */
543
+ export class WebRtcTCPRelayDetectedEvent extends Event {
544
+ readonly type: 'webRtcTCPRelayDetected';
545
+ constructor() {
546
+ super('webRtcTCPRelayDetected');
547
+ }
548
+ }
549
+
540
550
  export type PixelStreamingEvent =
541
551
  | AfkWarningActivateEvent
542
552
  | AfkWarningUpdateEvent
@@ -573,7 +583,8 @@ export type PixelStreamingEvent =
573
583
  | XrSessionStartedEvent
574
584
  | XrSessionEndedEvent
575
585
  | XrFrameEvent
576
- | PlayerCountEvent;
586
+ | PlayerCountEvent
587
+ | WebRtcTCPRelayDetectedEvent;
577
588
 
578
589
  export class EventEmitter extends EventTarget {
579
590
  /**
@@ -27,6 +27,7 @@ export declare class Flags {
27
27
  static GamepadInput: "GamepadInput";
28
28
  static XRControllerInput: "XRControllerInput";
29
29
  static WaitForStreamer: "WaitForStreamer";
30
+ static HideUI: "HideUI";
30
31
  }
31
32
  export type FlagsKeys = Exclude<keyof typeof Flags, 'prototype'>;
32
33
  export type FlagsIds = typeof Flags[FlagsKeys];
@@ -12,7 +12,7 @@ export declare class AggregatedStats {
12
12
  inboundAudioStats: InboundAudioStats;
13
13
  lastVideoStats: InboundVideoStats;
14
14
  lastAudioStats: InboundAudioStats;
15
- candidatePair: CandidatePairStats;
15
+ candidatePairs: Array<CandidatePairStats>;
16
16
  DataChannelStats: DataChannelStats;
17
17
  localCandidates: Array<CandidateStat>;
18
18
  remoteCandidates: Array<CandidateStat>;
@@ -20,6 +20,7 @@ export declare class AggregatedStats {
20
20
  sessionStats: SessionStats;
21
21
  streamStats: StreamStats;
22
22
  codecs: Map<string, string>;
23
+ transportStats: RTCTransportStats;
23
24
  constructor();
24
25
  /**
25
26
  * Gather all the information from the RTC Peer Connection Report
@@ -67,6 +68,7 @@ export declare class AggregatedStats {
67
68
  * @param stat - video track stats
68
69
  */
69
70
  handleTrack(stat: InboundTrackStats): void;
71
+ handleTransport(stat: RTCTransportStats): void;
70
72
  handleCodec(stat: CodecStats): void;
71
73
  handleSessionStatistics(videoStartTime: number, inputController: boolean | null, videoEncoderAvgQP: number): void;
72
74
  /**
@@ -74,4 +76,9 @@ export declare class AggregatedStats {
74
76
  * @param value - the number to be checked
75
77
  */
76
78
  isNumber(value: unknown): boolean;
79
+ /**
80
+ * Helper function to return the active candidate pair
81
+ * @returns The candidate pair that is currently receiving data
82
+ */
83
+ getActiveCandidatePair(): CandidatePairStats | null;
77
84
  }
@@ -4,12 +4,19 @@
4
4
  export declare class CandidatePairStats {
5
5
  bytesReceived: number;
6
6
  bytesSent: number;
7
+ currentRoundTripTime: number;
8
+ id: string;
9
+ lastPacketReceivedTimestamp: number;
10
+ lastPacketSentTimestamp: number;
7
11
  localCandidateId: string;
8
- remoteCandidateId: string;
9
12
  nominated: boolean;
13
+ priority: number;
10
14
  readable: boolean;
11
- writable: boolean;
15
+ remoteCandidateId: string;
12
16
  selected: boolean;
13
17
  state: string;
14
- currentRoundTripTime: number;
18
+ timestamp: number;
19
+ transportId: string;
20
+ type: string;
21
+ writable: boolean;
15
22
  }
@@ -2,10 +2,12 @@
2
2
  * ICE Candidate Stat collected from the RTC Stats Report
3
3
  */
4
4
  export declare class CandidateStat {
5
- label: string;
6
- id: string;
7
5
  address: string;
8
6
  candidateType: string;
7
+ id: string;
8
+ label: string;
9
9
  port: number;
10
10
  protocol: 'tcp' | 'udp';
11
+ relayProtocol: 'tcp' | 'udp' | 'tls';
12
+ transportId: string;
11
13
  }
@@ -3,7 +3,7 @@ import { LatencyTestResults } from '../DataChannel/LatencyTestResults';
3
3
  import { AggregatedStats } from '../PeerConnectionController/AggregatedStats';
4
4
  import { WebRtcPlayerController } from '../WebRtcPlayer/WebRtcPlayerController';
5
5
  import { InitialSettings } from '../DataChannel/InitialSettings';
6
- import { PixelStreamingEvent } from '../Util/EventEmitter';
6
+ import { PixelStreamingEvent, StatsReceivedEvent } from '../Util/EventEmitter';
7
7
  import { WebXRController } from '../WebXR/WebXRController';
8
8
  import { MessageDirection } from '../UeInstanceMessage/StreamMessageController';
9
9
  import { DataChannelLatencyTestConfig, DataChannelLatencyTestController } from "../DataChannel/DataChannelLatencyTestController";
@@ -159,6 +159,7 @@ export declare class PixelStreaming {
159
159
  */
160
160
  _onQualityControlOwnership(hasQualityOwnership: boolean): void;
161
161
  _onPlayerCount(playerCount: number): void;
162
+ _setupWebRtcTCPRelayDetection(statsReceivedEvent: StatsReceivedEvent): void;
162
163
  /**
163
164
  * Request a connection latency test.
164
165
  * NOTE: There are plans to refactor all request* functions. Expect changes if you use this!
@@ -395,7 +395,14 @@ export declare class PlayerCountEvent extends Event {
395
395
  };
396
396
  constructor(data: PlayerCountEvent['data']);
397
397
  }
398
- export type PixelStreamingEvent = AfkWarningActivateEvent | AfkWarningUpdateEvent | AfkWarningDeactivateEvent | AfkTimedOutEvent | VideoEncoderAvgQPEvent | WebRtcSdpEvent | WebRtcAutoConnectEvent | WebRtcConnectingEvent | WebRtcConnectedEvent | WebRtcFailedEvent | WebRtcDisconnectedEvent | DataChannelOpenEvent | DataChannelCloseEvent | DataChannelErrorEvent | VideoInitializedEvent | StreamLoadingEvent | StreamPreConnectEvent | StreamReconnectEvent | StreamPreDisconnectEvent | PlayStreamErrorEvent | PlayStreamEvent | PlayStreamRejectedEvent | LoadFreezeFrameEvent | HideFreezeFrameEvent | StatsReceivedEvent | StreamerListMessageEvent | StreamerIDChangedMessageEvent | LatencyTestResultEvent | DataChannelLatencyTestResponseEvent | DataChannelLatencyTestResultEvent | InitialSettingsEvent | SettingsChangedEvent | XrSessionStartedEvent | XrSessionEndedEvent | XrFrameEvent | PlayerCountEvent;
398
+ /**
399
+ * An event that is emitted when the webRTC connections is relayed over TCP.
400
+ */
401
+ export declare class WebRtcTCPRelayDetectedEvent extends Event {
402
+ readonly type: 'webRtcTCPRelayDetected';
403
+ constructor();
404
+ }
405
+ export type PixelStreamingEvent = AfkWarningActivateEvent | AfkWarningUpdateEvent | AfkWarningDeactivateEvent | AfkTimedOutEvent | VideoEncoderAvgQPEvent | WebRtcSdpEvent | WebRtcAutoConnectEvent | WebRtcConnectingEvent | WebRtcConnectedEvent | WebRtcFailedEvent | WebRtcDisconnectedEvent | DataChannelOpenEvent | DataChannelCloseEvent | DataChannelErrorEvent | VideoInitializedEvent | StreamLoadingEvent | StreamPreConnectEvent | StreamReconnectEvent | StreamPreDisconnectEvent | PlayStreamErrorEvent | PlayStreamEvent | PlayStreamRejectedEvent | LoadFreezeFrameEvent | HideFreezeFrameEvent | StatsReceivedEvent | StreamerListMessageEvent | StreamerIDChangedMessageEvent | LatencyTestResultEvent | DataChannelLatencyTestResponseEvent | DataChannelLatencyTestResultEvent | InitialSettingsEvent | SettingsChangedEvent | XrSessionStartedEvent | XrSessionEndedEvent | XrFrameEvent | PlayerCountEvent | WebRtcTCPRelayDetectedEvent;
399
406
  export declare class EventEmitter extends EventTarget {
400
407
  /**
401
408
  * Dispatch a new event.