@epicgames-ps/lib-pixelstreamingfrontend-ue5.5 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/cjs/Config/Config.js +16 -23
  3. package/dist/cjs/Config/Config.js.map +1 -1
  4. package/dist/cjs/Inputs/GamepadController.js.map +1 -1
  5. package/dist/cjs/Inputs/KeyboardController.js +0 -23
  6. package/dist/cjs/Inputs/KeyboardController.js.map +1 -1
  7. package/dist/cjs/Inputs/MouseController.js +9 -6
  8. package/dist/cjs/Inputs/MouseController.js.map +1 -1
  9. package/dist/cjs/Inputs/MouseControllerHovering.js.map +1 -1
  10. package/dist/cjs/Inputs/MouseControllerLocked.js +1 -0
  11. package/dist/cjs/Inputs/MouseControllerLocked.js.map +1 -1
  12. package/dist/cjs/PixelStreaming/PixelStreaming.js +12 -13
  13. package/dist/cjs/PixelStreaming/PixelStreaming.js.map +1 -1
  14. package/dist/cjs/UeInstanceMessage/StreamMessageController.js +4 -0
  15. package/dist/cjs/UeInstanceMessage/StreamMessageController.js.map +1 -1
  16. package/dist/cjs/Util/BrowserUtils.js +32 -0
  17. package/dist/cjs/Util/BrowserUtils.js.map +1 -0
  18. package/dist/cjs/Util/EventEmitter.js +11 -1
  19. package/dist/cjs/Util/EventEmitter.js.map +1 -1
  20. package/dist/cjs/Util/IURLSearchParams.js +1 -0
  21. package/dist/cjs/Util/IURLSearchParams.js.map +1 -1
  22. package/dist/cjs/Util/RTCUtils.js +1 -0
  23. package/dist/cjs/Util/RTCUtils.js.map +1 -1
  24. package/dist/cjs/VideoPlayer/VideoPlayer.js +12 -1
  25. package/dist/cjs/VideoPlayer/VideoPlayer.js.map +1 -1
  26. package/dist/cjs/WebRtcPlayer/WebRtcPlayerController.js +31 -7
  27. package/dist/cjs/WebRtcPlayer/WebRtcPlayerController.js.map +1 -1
  28. package/dist/esm/Config/Config.js +16 -23
  29. package/dist/esm/Config/Config.js.map +1 -1
  30. package/dist/esm/Inputs/GamepadController.js.map +1 -1
  31. package/dist/esm/Inputs/KeyboardController.js +0 -23
  32. package/dist/esm/Inputs/KeyboardController.js.map +1 -1
  33. package/dist/esm/Inputs/MouseController.js +9 -6
  34. package/dist/esm/Inputs/MouseController.js.map +1 -1
  35. package/dist/esm/Inputs/MouseControllerHovering.js.map +1 -1
  36. package/dist/esm/Inputs/MouseControllerLocked.js +1 -0
  37. package/dist/esm/Inputs/MouseControllerLocked.js.map +1 -1
  38. package/dist/esm/PixelStreaming/PixelStreaming.js +12 -13
  39. package/dist/esm/PixelStreaming/PixelStreaming.js.map +1 -1
  40. package/dist/esm/UeInstanceMessage/StreamMessageController.js +4 -0
  41. package/dist/esm/UeInstanceMessage/StreamMessageController.js.map +1 -1
  42. package/dist/esm/Util/BrowserUtils.js +28 -0
  43. package/dist/esm/Util/BrowserUtils.js.map +1 -0
  44. package/dist/esm/Util/EventEmitter.js +9 -0
  45. package/dist/esm/Util/EventEmitter.js.map +1 -1
  46. package/dist/esm/Util/IURLSearchParams.js +1 -0
  47. package/dist/esm/Util/IURLSearchParams.js.map +1 -1
  48. package/dist/esm/Util/RTCUtils.js +1 -0
  49. package/dist/esm/Util/RTCUtils.js.map +1 -1
  50. package/dist/esm/VideoPlayer/VideoPlayer.js +12 -1
  51. package/dist/esm/VideoPlayer/VideoPlayer.js.map +1 -1
  52. package/dist/esm/WebRtcPlayer/WebRtcPlayerController.js +32 -8
  53. package/dist/esm/WebRtcPlayer/WebRtcPlayerController.js.map +1 -1
  54. package/dist/types/Config/Config.d.ts +8 -0
  55. package/dist/types/Inputs/KeyboardController.d.ts +0 -2
  56. package/dist/types/PixelStreaming/PixelStreaming.d.ts +6 -6
  57. package/dist/types/Util/BrowserUtils.d.ts +3 -0
  58. package/dist/types/Util/EventEmitter.d.ts +14 -1
  59. package/dist/types/VideoPlayer/VideoPlayer.d.ts +2 -1
  60. package/dist/types/WebRtcPlayer/WebRtcPlayerController.d.ts +10 -5
  61. package/eslint.config.mjs +4 -4
  62. package/package.json +2 -2
  63. package/src/Config/Config.ts +32 -30
  64. package/src/Config/SettingBase.ts +1 -1
  65. package/src/Config/SettingFlag.ts +1 -1
  66. package/src/Config/SettingNumber.ts +1 -1
  67. package/src/Config/SettingOption.ts +2 -2
  68. package/src/Config/SettingText.ts +1 -1
  69. package/src/Inputs/GamepadController.ts +1 -2
  70. package/src/Inputs/IInputController.ts +1 -0
  71. package/src/Inputs/KeyboardController.ts +0 -31
  72. package/src/Inputs/MouseController.ts +9 -8
  73. package/src/Inputs/MouseControllerHovering.ts +1 -0
  74. package/src/Inputs/MouseControllerLocked.ts +1 -0
  75. package/src/PixelStreaming/PixelStreaming.ts +13 -19
  76. package/src/UeInstanceMessage/StreamMessageController.ts +4 -0
  77. package/src/Util/BrowserUtils.ts +36 -0
  78. package/src/Util/EventEmitter.ts +19 -0
  79. package/src/Util/IURLSearchParams.ts +1 -0
  80. package/src/Util/RTCUtils.ts +1 -0
  81. package/src/VideoPlayer/VideoPlayer.ts +14 -2
  82. package/src/WebRtcPlayer/WebRtcPlayerController.ts +35 -8
  83. package/tsconfig.cjs.json +1 -1
  84. package/tsconfig.esm.json +1 -1
  85. package/tsconfig.jest.json +1 -1
  86. package/dist/cjs/UI/OnScreenKeyboard.js +0 -86
  87. package/dist/cjs/UI/OnScreenKeyboard.js.map +0 -1
  88. package/dist/esm/UI/OnScreenKeyboard.js +0 -82
  89. package/dist/esm/UI/OnScreenKeyboard.js.map +0 -1
  90. package/dist/types/UI/OnScreenKeyboard.d.ts +0 -30
  91. package/src/UI/OnScreenKeyboard.ts +0 -102
  92. /package/{tsconfig.base.json → tsconfig.json} +0 -0
@@ -313,6 +313,19 @@ export declare class LatencyCalculatedEvent extends Event {
313
313
  };
314
314
  constructor(data: LatencyCalculatedEvent['data']);
315
315
  }
316
+ /**
317
+ * An event that is emitted when we receive the "onScreenKeyboard" command from UE.
318
+ */
319
+ export declare class ShowOnScreenKeyboardEvent extends Event {
320
+ readonly type: 'showOnScreenKeyboard';
321
+ readonly data: {
322
+ showOnScreenKeyboard: boolean;
323
+ x: number;
324
+ y: number;
325
+ contents: string;
326
+ };
327
+ constructor(data: ShowOnScreenKeyboardEvent['data']);
328
+ }
316
329
  /**
317
330
  * An event that is emitted when receiving data channel latency test response from server.
318
331
  * This event is handled by DataChannelLatencyTestController
@@ -441,7 +454,7 @@ export declare class WebRtcTCPRelayDetectedEvent extends Event {
441
454
  readonly type: 'webRtcTCPRelayDetected';
442
455
  constructor();
443
456
  }
444
- export type PixelStreamingEvent = AfkWarningActivateEvent | AfkWarningUpdateEvent | AfkWarningDeactivateEvent | AfkTimedOutEvent | VideoEncoderAvgQPEvent | WebRtcSdpEvent | WebRtcSdpOfferEvent | WebRtcSdpAnswerEvent | WebRtcAutoConnectEvent | WebRtcConnectingEvent | WebRtcConnectedEvent | WebRtcFailedEvent | WebRtcDisconnectedEvent | DataChannelOpenEvent | DataChannelCloseEvent | DataChannelErrorEvent | VideoInitializedEvent | StreamLoadingEvent | StreamPreConnectEvent | StreamReconnectEvent | StreamPreDisconnectEvent | PlayStreamErrorEvent | PlayStreamEvent | PlayStreamRejectedEvent | LoadFreezeFrameEvent | HideFreezeFrameEvent | StatsReceivedEvent | StreamerListMessageEvent | StreamerIDChangedMessageEvent | LatencyCalculatedEvent | LatencyTestResultEvent | DataChannelLatencyTestResponseEvent | DataChannelLatencyTestResultEvent | SubscribeFailedEvent | InitialSettingsEvent | SettingsChangedEvent | XrSessionStartedEvent | XrSessionEndedEvent | XrFrameEvent | PlayerCountEvent | WebRtcTCPRelayDetectedEvent;
457
+ export type PixelStreamingEvent = AfkWarningActivateEvent | AfkWarningUpdateEvent | AfkWarningDeactivateEvent | AfkTimedOutEvent | VideoEncoderAvgQPEvent | WebRtcSdpEvent | WebRtcSdpOfferEvent | WebRtcSdpAnswerEvent | WebRtcAutoConnectEvent | WebRtcConnectingEvent | WebRtcConnectedEvent | WebRtcFailedEvent | WebRtcDisconnectedEvent | DataChannelOpenEvent | DataChannelCloseEvent | DataChannelErrorEvent | VideoInitializedEvent | ShowOnScreenKeyboardEvent | StreamLoadingEvent | StreamPreConnectEvent | StreamReconnectEvent | StreamPreDisconnectEvent | PlayStreamErrorEvent | PlayStreamEvent | PlayStreamRejectedEvent | LoadFreezeFrameEvent | HideFreezeFrameEvent | StatsReceivedEvent | StreamerListMessageEvent | StreamerIDChangedMessageEvent | LatencyCalculatedEvent | LatencyTestResultEvent | DataChannelLatencyTestResponseEvent | DataChannelLatencyTestResultEvent | SubscribeFailedEvent | InitialSettingsEvent | SettingsChangedEvent | XrSessionStartedEvent | XrSessionEndedEvent | XrFrameEvent | PlayerCountEvent | WebRtcTCPRelayDetectedEvent;
445
458
  export declare class PixelStreamingEventEmitter extends EventTarget {
446
459
  /**
447
460
  * Dispatch a new event.
@@ -24,6 +24,7 @@ export declare class VideoPlayer {
24
24
  * @param config the applications configuration. We're interested in the startVideoMuted flag
25
25
  */
26
26
  constructor(videoElementParent: HTMLElement, config: Config);
27
+ destroy(): void;
27
28
  setAudioElement(audioElement: HTMLAudioElement): void;
28
29
  /**
29
30
  * Sets up the video element with any application config and plays the video element.
@@ -51,7 +52,7 @@ export declare class VideoPlayer {
51
52
  * Get the current context of the html video elements parent
52
53
  * @returns - the current context of the video elements parent
53
54
  */
54
- getVideoParentElement(): HTMLElement;
55
+ getVideoParentElement(): HTMLElement | undefined;
55
56
  /**
56
57
  * Set the Video Elements src object tracks to enable
57
58
  * @param enabled - Enable Tracks on the Src Object
@@ -14,7 +14,7 @@ import { ResponseController } from '../UeInstanceMessage/ResponseController';
14
14
  import { SendMessageController } from '../UeInstanceMessage/SendMessageController';
15
15
  import { ToStreamerMessagesController } from '../UeInstanceMessage/ToStreamerMessagesController';
16
16
  import { DataChannelSender } from '../DataChannel/DataChannelSender';
17
- import { InputCoordTranslator, UntranslatedCoordUnsigned } from '../Util/InputCoordTranslator';
17
+ import { InputCoordTranslator } from '../Util/InputCoordTranslator';
18
18
  import { PixelStreaming } from '../PixelStreaming/PixelStreaming';
19
19
  import { DataChannelLatencyTestRequest } from '../DataChannel/DataChannelLatencyTestResults';
20
20
  import { IInputController } from '../Inputs/IInputController';
@@ -74,11 +74,10 @@ export declare class WebRtcPlayerController {
74
74
  */
75
75
  constructor(config: Config, pixelStreaming: PixelStreaming);
76
76
  /**
77
- * Make a request to UnquantizedAndDenormalizeUnsigned coordinates
78
- * @param x x axis coordinate
79
- * @param y y axis coordinate
77
+ * Destroys the video player and makes sure resources are freed. This helps to prevent the issue in chrome
78
+ * where it refuses to make new video players.
80
79
  */
81
- requestUnquantizedAndDenormalizeUnsigned(x: number, y: number): UntranslatedCoordUnsigned;
80
+ destroyVideoPlayer(): void;
82
81
  /**
83
82
  * Handles when a message is received
84
83
  * @param event - Message Event
@@ -93,6 +92,7 @@ export declare class WebRtcPlayerController {
93
92
  * @param message
94
93
  */
95
94
  onCommand(message: ArrayBuffer): void;
95
+ handleOnScreenKeyboardCommand(command: any): void;
96
96
  /**
97
97
  * Handles a protocol message received from the streamer
98
98
  * @param message the message data from the streamer
@@ -338,6 +338,11 @@ export declare class WebRtcPlayerController {
338
338
  * Sends a request to the UE Instance to have ownership of Quality
339
339
  */
340
340
  sendRequestQualityControlOwnership(): void;
341
+ /**
342
+ * Send a `TextBoxEntry` message back to UE.
343
+ * @param contents The new contents of the UE side text box.
344
+ */
345
+ sendTextboxEntry(contents: string): void;
341
346
  /**
342
347
  * Handles when a Latency Test Result are received from the UE Instance
343
348
  * @param message - Latency Test Timings
package/eslint.config.mjs CHANGED
@@ -1,18 +1,18 @@
1
1
  // Copyright Epic Games, Inc. All Rights Reserved.
2
- import eslint from '@eslint/js';
2
+
3
3
  import tseslint from 'typescript-eslint';
4
+ import baseConfig from '../../eslint.config.mjs'
4
5
 
5
6
  export default tseslint.config(
7
+ baseConfig,
6
8
  {
7
9
  ignores: ["src/__test__/**/*.ts", "**/*.test.ts"],
8
10
  },
9
- eslint.configs.recommended,
10
- tseslint.configs.recommendedTypeCheckedOnly,
11
11
  {
12
12
  languageOptions: {
13
13
  parser: tseslint.parser,
14
14
  parserOptions: {
15
- project: 'tsconfig.base.json',
15
+ project: 'tsconfig.json',
16
16
  },
17
17
  },
18
18
  files: ["src/**/*.ts"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@epicgames-ps/lib-pixelstreamingfrontend-ue5.5",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "description": "Frontend library for Unreal Engine 5.5 Pixel Streaming",
5
5
  "main": "dist/cjs/pixelstreamingfrontend.js",
6
6
  "module": "dist/esm/pixelstreamingfrontend.js",
@@ -32,7 +32,7 @@
32
32
  "typescript-eslint": "^8.24.0"
33
33
  },
34
34
  "dependencies": {
35
- "@epicgames-ps/lib-pixelstreamingcommon-ue5.5": "^0.2.8",
35
+ "@epicgames-ps/lib-pixelstreamingcommon-ue5.5": "^0.2.9",
36
36
  "sdp": "^3.2.0"
37
37
  },
38
38
  "repository": {
@@ -7,6 +7,7 @@ import { SettingText } from './SettingText';
7
7
  import { SettingOption } from './SettingOption';
8
8
  import { PixelStreamingEventEmitter, SettingsChangedEvent } from '../Util/EventEmitter';
9
9
  import { SettingBase } from './SettingBase';
10
+ import { BrowserUtils } from '../Util/BrowserUtils';
10
11
 
11
12
  /**
12
13
  * A collection of flags that can be toggled and are core to all Pixel Streaming experiences.
@@ -25,6 +26,7 @@ export class Flags {
25
26
  static StartVideoMuted = 'StartVideoMuted' as const;
26
27
  static SuppressBrowserKeys = 'SuppressBrowserKeys' as const;
27
28
  static UseMic = 'UseMic' as const;
29
+ static UseModalForTextInput = 'UseModalForTextInput' as const;
28
30
  static UseCamera = 'UseCamera' as const;
29
31
  static KeyboardInput = 'KeyboardInput' as const;
30
32
  static MouseInput = 'MouseInput' as const;
@@ -128,6 +130,8 @@ export interface ConfigParams {
128
130
  initialSettings?: Partial<AllSettings>;
129
131
  /** If useUrlParams is set true, will read initial values from URL parameters and persist changed settings into URL */
130
132
  useUrlParams?: boolean;
133
+ /** If webSocketProtocols is set the protocols will be passed to the websocket constructor */
134
+ webSocketProtocols?: string | string[];
131
135
  }
132
136
  export class Config {
133
137
  /* A map of flags that can be toggled - options that can be set in the application - e.g. Use Mic? */
@@ -144,11 +148,14 @@ export class Config {
144
148
 
145
149
  private _useUrlParams: boolean;
146
150
 
151
+ private _webSocketProtocols?: string | string[];
152
+
147
153
  // ------------ Settings -----------------
148
154
 
149
155
  constructor(config: ConfigParams = {}) {
150
- const { initialSettings, useUrlParams } = config;
156
+ const { initialSettings, useUrlParams, webSocketProtocols } = config;
151
157
  this._useUrlParams = !!useUrlParams;
158
+ this._webSocketProtocols = webSocketProtocols;
152
159
  this.populateDefaultSettings(this._useUrlParams, initialSettings);
153
160
  }
154
161
 
@@ -160,10 +167,17 @@ export class Config {
160
167
  return this._useUrlParams;
161
168
  }
162
169
 
170
+ /**
171
+ * Gets a protocol or list of protocols to pass to the websocket if set.
172
+ */
173
+ public get webSocketProtocols() {
174
+ return this._webSocketProtocols;
175
+ }
176
+
163
177
  /**
164
178
  * Populate the default settings for a Pixel Streaming application
165
179
  */
166
- private populateDefaultSettings(useUrlParams: boolean, settings: Partial<AllSettings>): void {
180
+ private populateDefaultSettings(useUrlParams: boolean, settings?: Partial<AllSettings>): void {
167
181
  /**
168
182
  * Text Parameters
169
183
  */
@@ -202,33 +216,8 @@ export class Config {
202
216
  )
203
217
  );
204
218
 
205
- const getBrowserSupportedVideoCodecs = function (): Array<string> {
206
- const browserSupportedCodecs: Array<string> = [];
207
- // Try get the info needed from the RTCRtpReceiver. This is only available on chrome
208
- if (!RTCRtpReceiver.getCapabilities) {
209
- Logger.Warning(
210
- 'RTCRtpReceiver.getCapabilities API is not available in your browser, defaulting to guess that we support H.264.'
211
- );
212
- browserSupportedCodecs.push(
213
- 'H264 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f'
214
- );
215
- return browserSupportedCodecs;
216
- }
217
-
218
- const matcher = /(VP\d|H26\d|AV1).*/;
219
- const codecs = RTCRtpReceiver.getCapabilities('video').codecs;
220
- codecs.forEach((codec) => {
221
- const str = codec.mimeType.split('/')[1] + ' ' + (codec.sdpFmtpLine || '');
222
- const match = matcher.exec(str);
223
- if (match !== null) {
224
- browserSupportedCodecs.push(str);
225
- }
226
- });
227
- return browserSupportedCodecs;
228
- };
229
-
230
219
  const getDefaultVideoCodec = function (): string {
231
- const videoCodecs = getBrowserSupportedVideoCodecs();
220
+ const videoCodecs = BrowserUtils.getSupportedVideoCodecs();
232
221
  // If only one option, then select that.
233
222
  if (videoCodecs.length == 1) {
234
223
  return videoCodecs[0];
@@ -247,7 +236,7 @@ export class Config {
247
236
  };
248
237
 
249
238
  const matchSpecifiedCodecToClosestSupported = function (specifiedCodec: string): string {
250
- const browserSupportedCodecs: Array<string> = getBrowserSupportedVideoCodecs();
239
+ const browserSupportedCodecs: Array<string> = BrowserUtils.getSupportedVideoCodecs();
251
240
 
252
241
  // Codec supplied in url param is an exact match for the browser codec.
253
242
  // (e.g. H264 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f)
@@ -278,7 +267,7 @@ export class Config {
278
267
  settings && Object.prototype.hasOwnProperty.call(settings, OptionParameters.PreferredCodec)
279
268
  ? matchSpecifiedCodecToClosestSupported(settings[OptionParameters.PreferredCodec])
280
269
  : getDefaultVideoCodec(),
281
- getBrowserSupportedVideoCodecs(),
270
+ BrowserUtils.getSupportedVideoCodecs(),
282
271
  useUrlParams,
283
272
  matchSpecifiedCodecToClosestSupported
284
273
  )
@@ -341,6 +330,19 @@ export class Config {
341
330
  )
342
331
  );
343
332
 
333
+ this.flags.set(
334
+ Flags.UseModalForTextInput,
335
+ new SettingFlag(
336
+ Flags.UseModalForTextInput,
337
+ 'Use modal for text input',
338
+ 'When entering input into a streamed UE text widget, use an input modal.',
339
+ settings && Object.prototype.hasOwnProperty.call(settings, Flags.UseModalForTextInput)
340
+ ? settings[Flags.UseModalForTextInput]
341
+ : true,
342
+ useUrlParams
343
+ )
344
+ );
345
+
344
346
  this.flags.set(
345
347
  Flags.UseCamera,
346
348
  new SettingFlag(
@@ -18,7 +18,7 @@ export class SettingBase {
18
18
  label: string,
19
19
  description: string,
20
20
  defaultSettingValue: unknown,
21
-
21
+
22
22
  defaultOnChangeListener: (changedValue: unknown, setting: SettingBase) => void = () => {
23
23
  /* Do nothing, to be overridden. */
24
24
  }
@@ -16,7 +16,7 @@ export class SettingFlag<CustomIds extends string = FlagsIds> extends SettingBas
16
16
  description: string,
17
17
  defaultFlagValue: boolean,
18
18
  useUrlParams: boolean,
19
-
19
+
20
20
  defaultOnChangeListener: (changedValue: unknown, setting: SettingBase) => void = () => {
21
21
  /* Do nothing, to be overridden. */
22
22
  }
@@ -21,7 +21,7 @@ export class SettingNumber<CustomIds extends string = NumericParametersIds> exte
21
21
  max: number | null,
22
22
  defaultNumber: number,
23
23
  useUrlParams: boolean,
24
-
24
+
25
25
  defaultOnChangeListener: (changedValue: unknown, setting: SettingBase) => void = () => {
26
26
  /* Do nothing, to be overridden. */
27
27
  }
@@ -22,12 +22,12 @@ export class SettingOption<CustomIds extends string = OptionParametersIds> exten
22
22
  defaultTextValue: string,
23
23
  options: Array<string>,
24
24
  useUrlParams: boolean,
25
-
25
+
26
26
  defaultUrlParamResolver: (urlParamValue: string) => string = function (value: string) {
27
27
  /* Return the string as-is by default */
28
28
  return value;
29
29
  },
30
-
30
+
31
31
  defaultOnChangeListener: (changedValue: unknown, setting: SettingBase) => void = () => {
32
32
  /* Do nothing, to be overridden. */
33
33
  }
@@ -17,7 +17,7 @@ export class SettingText<CustomIds extends string = TextParametersIds> extends S
17
17
  description: string,
18
18
  defaultTextValue: string,
19
19
  useUrlParams: boolean,
20
-
20
+
21
21
  defaultOnChangeListener: (changedValue: unknown, setting: SettingBase) => void = () => {
22
22
  /* Do nothing, to be overridden. */
23
23
  }
@@ -21,7 +21,7 @@ declare global {
21
21
  /**
22
22
  * Gamepad layout codes enum
23
23
  */
24
-
24
+
25
25
  export enum GamepadLayout {
26
26
  RightClusterBottomButton = 0,
27
27
  RightClusterRightButton = 1,
@@ -46,7 +46,6 @@ export enum GamepadLayout {
46
46
  RightStickHorizontal = 2,
47
47
  RightStickVertical = 3
48
48
  }
49
-
50
49
 
51
50
  /**
52
51
  * Handles gamepad events from the document to send to the streamer.
@@ -1,3 +1,4 @@
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
1
2
  /**
2
3
  * The base interface for all input controllers.
3
4
  * Since controllers mostly just register events and handle them the external interface is limited
@@ -19,7 +19,6 @@ export class KeyboardController implements IInputController {
19
19
  onKeyDownListener: (event: KeyboardEvent) => void;
20
20
  onKeyUpListener: (event: KeyboardEvent) => void;
21
21
  onKeyPressListener: (event: KeyboardEvent) => void;
22
- onCompositionEndListener: (event: CompositionEvent) => void;
23
22
 
24
23
  constructor(streamMessageController: StreamMessageController, config: Config, activeKeys: ActiveKeys) {
25
24
  this.streamMessageController = streamMessageController;
@@ -29,11 +28,9 @@ export class KeyboardController implements IInputController {
29
28
  this.onKeyDownListener = this.handleOnKeyDown.bind(this);
30
29
  this.onKeyUpListener = this.handleOnKeyUp.bind(this);
31
30
  this.onKeyPressListener = this.handleOnKeyPress.bind(this);
32
- this.onCompositionEndListener = this.handleOnCompositionEnd.bind(this);
33
31
  }
34
32
 
35
33
  register() {
36
- document.addEventListener('compositionend', this.onCompositionEndListener);
37
34
  document.addEventListener('keydown', this.onKeyDownListener);
38
35
  document.addEventListener('keyup', this.onKeyUpListener);
39
36
  //This has been deprecated as at Jun 13 2021
@@ -41,7 +38,6 @@ export class KeyboardController implements IInputController {
41
38
  }
42
39
 
43
40
  unregister() {
44
- document.removeEventListener('compositionend', this.onCompositionEndListener);
45
41
  document.removeEventListener('keydown', this.onKeyDownListener);
46
42
  document.removeEventListener('keyup', this.onKeyUpListener);
47
43
  document.removeEventListener('keypress', this.onKeyPressListener);
@@ -100,33 +96,6 @@ export class KeyboardController implements IInputController {
100
96
  toStreamerHandlers.get('KeyPress')?.([keyCode]);
101
97
  }
102
98
 
103
- private handleOnCompositionEnd(compositionEvent: CompositionEvent) {
104
- if (compositionEvent.data && compositionEvent.data.length) {
105
- compositionEvent.data.split('').forEach((char) => {
106
- // This keydown, keypress, keyup flow is required to mimic the way characters are
107
- // normally triggered
108
- this.handleOnKeyDown(
109
- new KeyboardEvent('keydown', {
110
- keyCode: char.toUpperCase().charCodeAt(0),
111
- charCode: char.charCodeAt(0)
112
- })
113
- );
114
- this.handleOnKeyPress(
115
- new KeyboardEvent('keypress', {
116
- keyCode: char.toUpperCase().charCodeAt(0),
117
- charCode: char.charCodeAt(0)
118
- })
119
- );
120
- this.handleOnKeyUp(
121
- new KeyboardEvent('keyup', {
122
- keyCode: char.toUpperCase().charCodeAt(0),
123
- charCode: char.charCodeAt(0)
124
- })
125
- );
126
- });
127
- }
128
- }
129
-
130
99
  /**
131
100
  * Gets the Keycode of the Key pressed
132
101
  * @param keyboardEvent - Key board Event
@@ -1,3 +1,4 @@
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
1
2
  import { MouseButtonsMask, MouseButton } from './MouseButtons';
2
3
  import { StreamMessageController } from '../UeInstanceMessage/StreamMessageController';
3
4
  import { InputCoordTranslator } from '../Util/InputCoordTranslator';
@@ -57,22 +58,22 @@ export class MouseController implements IInputController {
57
58
  }
58
59
 
59
60
  registerMouseEnterAndLeaveEvents() {
60
- const videoElementParent = this.videoPlayer.getVideoParentElement() as HTMLDivElement;
61
- videoElementParent.addEventListener('mouseenter', this.onEnterListener);
62
- videoElementParent.addEventListener('mouseleave', this.onLeaveListener);
61
+ const videoElementParent = this.videoPlayer.getVideoParentElement();
62
+ videoElementParent?.addEventListener('mouseenter', this.onEnterListener);
63
+ videoElementParent?.addEventListener('mouseleave', this.onLeaveListener);
63
64
  }
64
65
 
65
66
  unregisterMouseEnterAndLeaveEvents() {
66
- const videoElementParent = this.videoPlayer.getVideoParentElement() as HTMLDivElement;
67
- videoElementParent.removeEventListener('mouseenter', this.onEnterListener);
68
- videoElementParent.removeEventListener('mouseleave', this.onLeaveListener);
67
+ const videoElementParent = this.videoPlayer.getVideoParentElement();
68
+ videoElementParent?.removeEventListener('mouseenter', this.onEnterListener);
69
+ videoElementParent?.removeEventListener('mouseleave', this.onLeaveListener);
69
70
  }
70
71
 
71
72
  private onMouseEnter(event: MouseEvent) {
72
73
  if (!this.videoPlayer.isVideoReady()) {
73
74
  return;
74
75
  }
75
- this.streamMessageController.toStreamerHandlers.get('MouseEnter')();
76
+ this.streamMessageController.toStreamerHandlers.get('MouseEnter')?.();
76
77
  this.pressMouseButtons(event.buttons, event.x, event.y);
77
78
  }
78
79
 
@@ -80,7 +81,7 @@ export class MouseController implements IInputController {
80
81
  if (!this.videoPlayer.isVideoReady()) {
81
82
  return;
82
83
  }
83
- this.streamMessageController.toStreamerHandlers.get('MouseLeave')();
84
+ this.streamMessageController.toStreamerHandlers.get('MouseLeave')?.();
84
85
  this.releaseMouseButtons(event.buttons, event.x, event.y);
85
86
  }
86
87
 
@@ -1,3 +1,4 @@
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
1
2
  import { StreamMessageController } from '../UeInstanceMessage/StreamMessageController';
2
3
  import { InputCoordTranslator } from '../Util/InputCoordTranslator';
3
4
  import { VideoPlayer } from '../VideoPlayer/VideoPlayer';
@@ -1,3 +1,4 @@
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
1
2
  import { Logger } from '@epicgames-ps/lib-pixelstreamingcommon-ue5.5';
2
3
  import { StreamMessageController } from '../UeInstanceMessage/StreamMessageController';
3
4
  import { InputCoordTranslator, TranslatedCoordUnsigned } from '../Util/InputCoordTranslator';
@@ -7,7 +7,6 @@ import { WebRtcPlayerController } from '../WebRtcPlayer/WebRtcPlayerController';
7
7
  import { Flags, NumericParameters } from '../Config/Config';
8
8
  import { Logger } from '@epicgames-ps/lib-pixelstreamingcommon-ue5.5';
9
9
  import { InitialSettings } from '../DataChannel/InitialSettings';
10
- import { OnScreenKeyboard } from '../UI/OnScreenKeyboard';
11
10
  import {
12
11
  PixelStreamingEventEmitter,
13
12
  InitialSettingsEvent,
@@ -78,8 +77,6 @@ export class PixelStreaming {
78
77
 
79
78
  private allowConsoleCommands = false;
80
79
 
81
- private onScreenKeyboardHelper: OnScreenKeyboard;
82
-
83
80
  private _videoStartTime: number;
84
81
  private _inputController: boolean;
85
82
 
@@ -104,13 +101,6 @@ export class PixelStreaming {
104
101
  // setup WebRTC
105
102
  this.setWebRtcPlayerController(new WebRtcPlayerController(this.config, this));
106
103
 
107
- // Onscreen keyboard
108
- this.onScreenKeyboardHelper = new OnScreenKeyboard(this.videoElementParent);
109
- this.onScreenKeyboardHelper.unquantizeAndDenormalizeUnsigned = (x: number, y: number) =>
110
- this._webRtcController.requestUnquantizedAndDenormalizeUnsigned(x, y);
111
- this._activateOnScreenKeyboard = (command: any) =>
112
- this.onScreenKeyboardHelper.showOnScreenKeyboard(command);
113
-
114
104
  this._webXrController = new WebXRController(this._webRtcController);
115
105
 
116
106
  this._setupWebRtcTCPRelayDetection = this._setupWebRtcTCPRelayDetection.bind(this);
@@ -277,15 +267,6 @@ export class PixelStreaming {
277
267
  this.config._registerOnChangeEvents(this._eventEmitter);
278
268
  }
279
269
 
280
- /**
281
- * Activate the on screen keyboard when receiving the command from the streamer
282
- * @param command - the keyboard command
283
- */
284
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
285
- _activateOnScreenKeyboard(command: any): void {
286
- throw new Error('Method not implemented.');
287
- }
288
-
289
270
  /**
290
271
  * Set the input control ownership
291
272
  * @param inputControlOwnership - does the user have input control ownership
@@ -819,6 +800,19 @@ export class PixelStreaming {
819
800
  return true;
820
801
  }
821
802
 
803
+ /**
804
+ * Sets the text contents of the currently focused UE text box widget.
805
+ * @param contents The new contents of the UE text box.
806
+ * @returns True if the message could be sent.
807
+ */
808
+ public sendTextboxEntry(contents: string): boolean {
809
+ if (!this._webRtcController.videoPlayer.isVideoReady()) {
810
+ return false;
811
+ }
812
+ this._webRtcController.sendTextboxEntry(contents);
813
+ return true;
814
+ }
815
+
822
816
  /**
823
817
  * Add a UE -> browser response event listener
824
818
  * @param name - The name of the response handler
@@ -81,6 +81,10 @@ export class StreamMessageController {
81
81
  id: 51,
82
82
  structure: ['string']
83
83
  });
84
+ this.toStreamerMessages.set('TextboxEntry', {
85
+ id: 52,
86
+ structure: ['string']
87
+ });
84
88
  // Keyboard Input Message. Range = 60..69.
85
89
  this.toStreamerMessages.set('KeyDown', {
86
90
  id: 60,
@@ -0,0 +1,36 @@
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+ import { Logger } from '@epicgames-ps/lib-pixelstreamingcommon-ue5.5';
4
+
5
+ export class BrowserUtils {
6
+ static getSupportedVideoCodecs(): Array<string> {
7
+ const browserSupportedCodecs: Array<string> = [];
8
+ // Try get the info needed from the RTCRtpReceiver. This is only available on chrome
9
+ if (!RTCRtpReceiver.getCapabilities) {
10
+ Logger.Warning(
11
+ 'RTCRtpReceiver.getCapabilities API is not available in your browser, defaulting to guess that we support H.264.'
12
+ );
13
+ browserSupportedCodecs.push(
14
+ 'H264 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f'
15
+ );
16
+ return browserSupportedCodecs;
17
+ }
18
+
19
+ const matcher = /(VP\d|H26\d|AV1).*/;
20
+ const capabilities = RTCRtpReceiver.getCapabilities('video');
21
+ if (!capabilities) {
22
+ browserSupportedCodecs.push(
23
+ 'H264 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f'
24
+ );
25
+ return browserSupportedCodecs;
26
+ }
27
+ capabilities.codecs.forEach((codec) => {
28
+ const str = codec.mimeType.split('/')[1] + ' ' + (codec.sdpFmtpLine || '');
29
+ const match = matcher.exec(str);
30
+ if (match !== null) {
31
+ browserSupportedCodecs.push(str);
32
+ }
33
+ });
34
+ return browserSupportedCodecs;
35
+ }
36
+ }
@@ -1,3 +1,4 @@
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
1
2
  import { FlagsIds, NumericParametersIds, OptionParametersIds, TextParametersIds } from '../Config/Config';
2
3
  import { LatencyTestResults } from '../DataChannel/LatencyTestResults';
3
4
  import { AggregatedStats } from '../PeerConnectionController/AggregatedStats';
@@ -427,6 +428,23 @@ export class LatencyCalculatedEvent extends Event {
427
428
  }
428
429
  }
429
430
 
431
+ /**
432
+ * An event that is emitted when we receive the "onScreenKeyboard" command from UE.
433
+ */
434
+ export class ShowOnScreenKeyboardEvent extends Event {
435
+ override readonly type: 'showOnScreenKeyboard';
436
+ readonly data: {
437
+ showOnScreenKeyboard: boolean;
438
+ x: number;
439
+ y: number;
440
+ contents: string;
441
+ };
442
+ constructor(data: ShowOnScreenKeyboardEvent['data']) {
443
+ super('showOnScreenKeyboard');
444
+ this.data = data;
445
+ }
446
+ }
447
+
430
448
  /**
431
449
  * An event that is emitted when receiving data channel latency test response from server.
432
450
  * This event is handled by DataChannelLatencyTestController
@@ -616,6 +634,7 @@ export type PixelStreamingEvent =
616
634
  | DataChannelCloseEvent
617
635
  | DataChannelErrorEvent
618
636
  | VideoInitializedEvent
637
+ | ShowOnScreenKeyboardEvent
619
638
  | StreamLoadingEvent
620
639
  | StreamPreConnectEvent
621
640
  | StreamReconnectEvent
@@ -1,3 +1,4 @@
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
1
2
  /**
2
3
  * A case insensitive, partial implementation of URLSearchParams
3
4
  */
@@ -1,3 +1,4 @@
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
1
2
  export class RTCUtils {
2
3
  static isVideoTransceiver(transceiver: RTCRtpTransceiver | undefined): boolean {
3
4
  return this.canTransceiverReceiveVideo(transceiver) || this.canTransceiverSendVideo(transceiver);
@@ -70,6 +70,18 @@ export class VideoPlayer {
70
70
  window.addEventListener('orientationchange', () => this.onOrientationChange());
71
71
  }
72
72
 
73
+ public destroy() {
74
+ this.videoElement.src = '';
75
+ this.videoElement.srcObject = null;
76
+ this.videoElement.remove();
77
+
78
+ if (this.audioElement) {
79
+ this.audioElement.src = '';
80
+ this.audioElement.srcObject = null;
81
+ this.audioElement.remove();
82
+ }
83
+ }
84
+
73
85
  public setAudioElement(audioElement: HTMLAudioElement): void {
74
86
  this.audioElement = audioElement;
75
87
  }
@@ -117,8 +129,8 @@ export class VideoPlayer {
117
129
  * Get the current context of the html video elements parent
118
130
  * @returns - the current context of the video elements parent
119
131
  */
120
- getVideoParentElement(): HTMLElement {
121
- return this.videoElement.parentElement;
132
+ getVideoParentElement(): HTMLElement | undefined {
133
+ return this.videoElement.parentElement ?? undefined;
122
134
  }
123
135
 
124
136
  /**