@epicgames-ps/lib-pixelstreamingfrontend-ue5.5 0.1.3 → 0.2.1

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 (94) hide show
  1. package/.eslintrc.js +1 -1
  2. package/.prettierrc.json +1 -0
  3. package/dist/lib-pixelstreamingfrontend.esm.js +1 -1
  4. package/dist/lib-pixelstreamingfrontend.js +1 -1
  5. package/package.json +6 -5
  6. package/src/AFK/AFKController.ts +10 -32
  7. package/src/Config/Config.ts +179 -201
  8. package/src/Config/SettingBase.ts +61 -2
  9. package/src/Config/SettingFlag.ts +10 -48
  10. package/src/Config/SettingNumber.ts +10 -28
  11. package/src/Config/SettingOption.ts +13 -46
  12. package/src/Config/SettingText.ts +9 -37
  13. package/src/DataChannel/DataChannelController.ts +6 -26
  14. package/src/DataChannel/DataChannelLatencyTestController.ts +38 -33
  15. package/src/DataChannel/DataChannelLatencyTestResults.ts +8 -10
  16. package/src/DataChannel/DataChannelSender.ts +5 -15
  17. package/src/DataChannel/LatencyTestResults.ts +5 -15
  18. package/src/FreezeFrame/FreezeFrame.ts +7 -19
  19. package/src/FreezeFrame/FreezeFrameController.ts +3 -14
  20. package/src/Inputs/GamepadController.ts +123 -221
  21. package/src/Inputs/GamepadTypes.ts +23 -0
  22. package/src/Inputs/IInputController.ts +17 -0
  23. package/src/Inputs/InputClassesFactory.ts +38 -45
  24. package/src/Inputs/KeyCodes.ts +114 -0
  25. package/src/Inputs/KeyboardController.ts +49 -232
  26. package/src/Inputs/MouseController.ts +71 -297
  27. package/src/Inputs/MouseControllerHovering.ts +118 -0
  28. package/src/Inputs/MouseControllerLocked.ts +194 -0
  29. package/src/Inputs/TouchController.ts +49 -105
  30. package/src/Inputs/TouchControllerFake.ts +132 -0
  31. package/src/Inputs/XRGamepadController.ts +35 -44
  32. package/src/PeerConnectionController/AggregatedStats.ts +26 -54
  33. package/src/PeerConnectionController/CandidatePairStats.ts +1 -1
  34. package/src/PeerConnectionController/CandidateStat.ts +1 -1
  35. package/src/PeerConnectionController/PeerConnectionController.ts +217 -164
  36. package/src/PixelStreaming/PixelStreaming.ts +174 -226
  37. package/src/UI/OnScreenKeyboard.ts +14 -9
  38. package/src/UeInstanceMessage/ResponseController.ts +6 -15
  39. package/src/UeInstanceMessage/SendMessageController.ts +16 -18
  40. package/src/UeInstanceMessage/StreamMessageController.ts +3 -12
  41. package/src/UeInstanceMessage/ToStreamerMessagesController.ts +3 -9
  42. package/src/Util/EventEmitter.ts +17 -22
  43. package/src/Util/FileUtil.ts +11 -34
  44. package/src/Util/IURLSearchParams.ts +25 -0
  45. package/src/Util/InputCoordTranslator.ts +73 -0
  46. package/src/Util/RTCUtils.ts +23 -15
  47. package/src/VideoPlayer/StreamController.ts +6 -23
  48. package/src/VideoPlayer/VideoPlayer.ts +9 -30
  49. package/src/WebRtcPlayer/WebRtcPlayerController.ts +328 -690
  50. package/src/WebXR/WebXRController.ts +82 -94
  51. package/src/pixelstreamingfrontend.ts +6 -10
  52. package/types/AFK/AFKController.d.ts +0 -1
  53. package/types/Config/Config.d.ts +6 -5
  54. package/types/Config/SettingBase.d.ts +13 -0
  55. package/types/Config/SettingFlag.d.ts +1 -10
  56. package/types/Config/SettingNumber.d.ts +1 -5
  57. package/types/Config/SettingOption.d.ts +1 -10
  58. package/types/Config/SettingText.d.ts +1 -9
  59. package/types/DataChannel/DataChannelLatencyTestController.d.ts +1 -1
  60. package/types/Inputs/GamepadController.d.ts +22 -46
  61. package/types/Inputs/GamepadTypes.d.ts +7 -0
  62. package/types/Inputs/IInputController.d.ts +16 -0
  63. package/types/Inputs/InputClassesFactory.d.ts +7 -8
  64. package/types/Inputs/KeyCodes.d.ts +5 -0
  65. package/types/Inputs/KeyboardController.d.ts +17 -45
  66. package/types/Inputs/MouseController.d.ts +33 -68
  67. package/types/Inputs/MouseControllerHovering.d.ts +26 -0
  68. package/types/Inputs/MouseControllerLocked.d.ts +31 -0
  69. package/types/Inputs/TouchController.d.ts +19 -44
  70. package/types/Inputs/TouchControllerFake.d.ts +29 -0
  71. package/types/Inputs/XRGamepadController.d.ts +0 -7
  72. package/types/PeerConnectionController/PeerConnectionController.d.ts +10 -1
  73. package/types/PixelStreaming/PixelStreaming.d.ts +14 -2
  74. package/types/UI/OnScreenKeyboard.d.ts +2 -2
  75. package/types/Util/EventEmitter.d.ts +1 -1
  76. package/types/Util/IURLSearchParams.d.ts +9 -0
  77. package/types/Util/InputCoordTranslator.d.ts +29 -0
  78. package/types/VideoPlayer/StreamController.d.ts +0 -2
  79. package/types/WebRtcPlayer/WebRtcPlayerController.d.ts +19 -17
  80. package/types/pixelstreamingfrontend.d.ts +1 -1
  81. package/src/Inputs/FakeTouchController.ts +0 -199
  82. package/src/Inputs/HoveringMouseEvents.ts +0 -192
  83. package/src/Inputs/IMouseEvents.ts +0 -64
  84. package/src/Inputs/ITouchController.ts +0 -29
  85. package/src/Inputs/LockedMouseEvents.ts +0 -287
  86. package/src/Util/CoordinateConverter.ts +0 -290
  87. package/src/Util/EventListenerTracker.ts +0 -29
  88. package/types/Inputs/FakeTouchController.d.ts +0 -61
  89. package/types/Inputs/HoveringMouseEvents.d.ts +0 -56
  90. package/types/Inputs/IMouseEvents.d.ts +0 -53
  91. package/types/Inputs/ITouchController.d.ts +0 -24
  92. package/types/Inputs/LockedMouseEvents.d.ts +0 -80
  93. package/types/Util/CoordinateConverter.d.ts +0 -100
  94. package/types/Util/EventListenerTracker.d.ts +0 -14
@@ -0,0 +1,194 @@
1
+ import { Logger } from '@epicgames-ps/lib-pixelstreamingcommon-ue5.5';
2
+ import { StreamMessageController } from '../UeInstanceMessage/StreamMessageController';
3
+ import { InputCoordTranslator, TranslatedCoordUnsigned } from '../Util/InputCoordTranslator';
4
+ import { VideoPlayer } from '../VideoPlayer/VideoPlayer';
5
+ import type { ActiveKeys } from './InputClassesFactory';
6
+ import { MouseController } from './MouseController';
7
+
8
+ /**
9
+ * A mouse controller that locks the mouse to the video document and prevents it from leaving the window
10
+ */
11
+ export class MouseControllerLocked extends MouseController {
12
+ videoElementParent: HTMLDivElement;
13
+ x: number;
14
+ y: number;
15
+ normalizedCoord: TranslatedCoordUnsigned;
16
+
17
+ // bound listeners
18
+ onRequestLockListener: () => void;
19
+ onLockStateChangeListener: () => void;
20
+ onMouseUpListener: (event: MouseEvent) => void;
21
+ onMouseDownListener: (event: MouseEvent) => void;
22
+ onMouseDblClickListener: (event: MouseEvent) => void;
23
+ onMouseWheelListener: (event: WheelEvent) => void;
24
+ onMouseMoveListener: (event: MouseEvent) => void;
25
+
26
+ constructor(
27
+ streamMessageController: StreamMessageController,
28
+ videoPlayer: VideoPlayer,
29
+ coordinateConverter: InputCoordTranslator,
30
+ activeKeys: ActiveKeys
31
+ ) {
32
+ super(streamMessageController, videoPlayer, coordinateConverter, activeKeys);
33
+ this.videoElementParent = videoPlayer.getVideoParentElement() as HTMLDivElement;
34
+ this.x = this.videoElementParent.getBoundingClientRect().width / 2;
35
+ this.y = this.videoElementParent.getBoundingClientRect().height / 2;
36
+ this.normalizedCoord = this.coordinateConverter.translateUnsigned(this.x, this.y);
37
+
38
+ this.onRequestLockListener = this.onRequestLock.bind(this);
39
+ this.onLockStateChangeListener = this.onLockStateChange.bind(this);
40
+ this.onMouseUpListener = this.onMouseUp.bind(this);
41
+ this.onMouseDownListener = this.onMouseDown.bind(this);
42
+ this.onMouseDblClickListener = this.onMouseDblClick.bind(this);
43
+ this.onMouseWheelListener = this.onMouseWheel.bind(this);
44
+ this.onMouseMoveListener = this.onMouseMove.bind(this);
45
+ }
46
+
47
+ register() {
48
+ super.register();
49
+
50
+ this.videoElementParent.requestPointerLock =
51
+ this.videoElementParent.requestPointerLock || this.videoElementParent.mozRequestPointerLock;
52
+ document.exitPointerLock = document.exitPointerLock || document.mozExitPointerLock;
53
+
54
+ if (this.videoElementParent.requestPointerLock) {
55
+ this.videoElementParent.addEventListener('click', this.onRequestLockListener);
56
+ }
57
+
58
+ document.addEventListener('pointerlockchange', this.onLockStateChangeListener);
59
+ document.addEventListener('mozpointerlockchange', this.onLockStateChangeListener);
60
+
61
+ this.videoElementParent.addEventListener('mousedown', this.onMouseDownListener);
62
+ this.videoElementParent.addEventListener('mouseup', this.onMouseUpListener);
63
+ this.videoElementParent.addEventListener('wheel', this.onMouseWheelListener);
64
+ this.videoElementParent.addEventListener('dblclick', this.onMouseDblClickListener);
65
+ }
66
+
67
+ unregister() {
68
+ const pointerLockElement = document.pointerLockElement || document.mozPointerLockElement;
69
+ if (document.exitPointerLock && pointerLockElement === this.videoElementParent) {
70
+ document.exitPointerLock();
71
+ }
72
+
73
+ this.videoElementParent.removeEventListener('click', this.onRequestLockListener);
74
+ document.removeEventListener('pointerlockchange', this.onLockStateChangeListener);
75
+ document.removeEventListener('mozpointerlockchange', this.onLockStateChangeListener);
76
+ document.removeEventListener('mousemove', this.onMouseMoveListener);
77
+
78
+ this.videoElementParent.removeEventListener('mousedown', this.onMouseDownListener);
79
+ this.videoElementParent.removeEventListener('mouseup', this.onMouseUpListener);
80
+ this.videoElementParent.removeEventListener('wheel', this.onMouseWheelListener);
81
+ this.videoElementParent.removeEventListener('dblclick', this.onMouseDblClickListener);
82
+
83
+ super.unregister();
84
+ }
85
+
86
+ private onRequestLock() {
87
+ this.videoElementParent.requestPointerLock();
88
+ }
89
+
90
+ private onLockStateChange() {
91
+ const pointerLockElement = document.pointerLockElement || document.mozPointerLockElement;
92
+ if (pointerLockElement === this.videoElementParent) {
93
+ Logger.Info('Pointer locked');
94
+ document.addEventListener('mousemove', this.onMouseMoveListener);
95
+ } else {
96
+ Logger.Info('The pointer lock status is now unlocked');
97
+ document.removeEventListener('mousemove', this.onMouseMoveListener);
98
+
99
+ // If mouse loses focus, send a key up for all of the currently held-down keys
100
+ // This is necessary as when the mouse loses focus, the windows stops listening for events and as such
101
+ // the keyup listener won't get fired
102
+ const activeKeys = this.activeKeys.getActiveKeys();
103
+ activeKeys.forEach((key: number) => {
104
+ this.streamMessageController.toStreamerHandlers.get('KeyUp')([key]);
105
+ });
106
+ }
107
+ }
108
+
109
+ private onMouseDown(event: MouseEvent) {
110
+ if (!this.videoPlayer.isVideoReady()) {
111
+ return;
112
+ }
113
+
114
+ this.streamMessageController.toStreamerHandlers.get('MouseDown')([
115
+ event.button,
116
+ // We use the store value of this.coord as opposed to the mouseEvent.x/y as the mouseEvent location
117
+ // uses the system cursor location which hasn't moved
118
+ this.normalizedCoord.x,
119
+ this.normalizedCoord.y
120
+ ]);
121
+ }
122
+
123
+ private onMouseUp(event: MouseEvent) {
124
+ if (!this.videoPlayer.isVideoReady()) {
125
+ return;
126
+ }
127
+ this.streamMessageController.toStreamerHandlers.get('MouseUp')([
128
+ event.button,
129
+ // We use the store value of this.coord as opposed to the mouseEvent.x/y as the mouseEvent location
130
+ // uses the system cursor location which hasn't moved
131
+ this.normalizedCoord.x,
132
+ this.normalizedCoord.y
133
+ ]);
134
+ }
135
+
136
+ private onMouseMove(event: MouseEvent) {
137
+ if (!this.videoPlayer.isVideoReady()) {
138
+ return;
139
+ }
140
+ const styleWidth = this.videoPlayer.getVideoParentElement().clientWidth;
141
+ const styleHeight = this.videoPlayer.getVideoParentElement().clientHeight;
142
+
143
+ this.x += event.movementX;
144
+ this.y += event.movementY;
145
+
146
+ while (this.x > styleWidth) {
147
+ this.x -= styleWidth;
148
+ }
149
+ while (this.y > styleHeight) {
150
+ this.y -= styleHeight;
151
+ }
152
+ while (this.x < 0) {
153
+ this.x += styleWidth;
154
+ }
155
+ while (this.y < 0) {
156
+ this.y += styleHeight;
157
+ }
158
+
159
+ this.normalizedCoord = this.coordinateConverter.translateUnsigned(this.x, this.y);
160
+ const delta = this.coordinateConverter.translateSigned(event.movementX, event.movementY);
161
+ this.streamMessageController.toStreamerHandlers.get('MouseMove')([
162
+ this.normalizedCoord.x,
163
+ this.normalizedCoord.y,
164
+ delta.x,
165
+ delta.y
166
+ ]);
167
+ }
168
+
169
+ private onMouseWheel(event: WheelEvent) {
170
+ if (!this.videoPlayer.isVideoReady()) {
171
+ return;
172
+ }
173
+ this.streamMessageController.toStreamerHandlers.get('MouseWheel')([
174
+ event.wheelDelta,
175
+ // We use the store value of this.coord as opposed to the mouseEvent.x/y as the mouseEvent location
176
+ // uses the system cursor location which hasn't moved
177
+ this.normalizedCoord.x,
178
+ this.normalizedCoord.y
179
+ ]);
180
+ }
181
+
182
+ private onMouseDblClick(event: MouseEvent) {
183
+ if (!this.videoPlayer.isVideoReady()) {
184
+ return;
185
+ }
186
+ this.streamMessageController.toStreamerHandlers.get('MouseDouble')([
187
+ event.button,
188
+ // We use the store value of this.coord as opposed to the mouseEvent.x/y as the mouseEvent location
189
+ // uses the system cursor location which hasn't moved
190
+ this.normalizedCoord.x,
191
+ this.normalizedCoord.y
192
+ ]);
193
+ }
194
+ }
@@ -1,98 +1,65 @@
1
1
  // Copyright Epic Games, Inc. All Rights Reserved.
2
2
 
3
3
  import { Logger } from '@epicgames-ps/lib-pixelstreamingcommon-ue5.5';
4
- import { CoordinateConverter } from '../Util/CoordinateConverter';
4
+ import { InputCoordTranslator } from '../Util/InputCoordTranslator';
5
5
  import { StreamMessageController } from '../UeInstanceMessage/StreamMessageController';
6
6
  import { VideoPlayer } from '../VideoPlayer/VideoPlayer';
7
- import { ITouchController } from './ITouchController';
8
- import { EventListenerTracker } from '../Util/EventListenerTracker';
7
+ import { IInputController } from './IInputController';
8
+
9
9
  /**
10
- * Handles the Touch input Events
10
+ * The basic touch controller that handles the touch events on the document.
11
11
  */
12
- export class TouchController implements ITouchController {
13
- toStreamerMessagesProvider: StreamMessageController;
14
- videoElementProvider: VideoPlayer;
15
- coordinateConverter: CoordinateConverter;
12
+ export class TouchController implements IInputController {
13
+ streamMessageController: StreamMessageController;
14
+ videoPlayer: VideoPlayer;
15
+ coordinateConverter: InputCoordTranslator;
16
+
16
17
  videoElementParent: HTMLVideoElement;
17
18
  fingers = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0];
18
19
  fingerIds = new Map();
19
20
  maxByteValue = 255;
20
21
 
21
- // Utility for keeping track of event handlers and to unregister them.
22
- private touchEventListenerTracker = new EventListenerTracker();
22
+ onTouchStartListener: (event: TouchEvent) => void;
23
+ onTouchEndListener: (event: TouchEvent) => void;
24
+ onTouchMoveListener: (event: TouchEvent) => void;
23
25
 
24
- /**
25
- * @param toStreamerMessagesProvider - Stream message instance
26
- * @param videoElementProvider - Video Player instance
27
- * @param coordinateConverter - A coordinate converter instance
28
- */
29
26
  constructor(
30
- toStreamerMessagesProvider: StreamMessageController,
31
- videoElementProvider: VideoPlayer,
32
- coordinateConverter: CoordinateConverter
27
+ streamMessageController: StreamMessageController,
28
+ videoPlayer: VideoPlayer,
29
+ coordinateConverter: InputCoordTranslator
33
30
  ) {
34
- this.toStreamerMessagesProvider = toStreamerMessagesProvider;
35
- this.videoElementProvider = videoElementProvider;
31
+ this.streamMessageController = streamMessageController;
32
+ this.videoPlayer = videoPlayer;
36
33
  this.coordinateConverter = coordinateConverter;
37
- this.videoElementParent = videoElementProvider.getVideoElement();
38
- const ontouchstart = (ev: TouchEvent) =>
39
- this.onTouchStart(ev);
40
- const ontouchend = (ev: TouchEvent) =>
41
- this.onTouchEnd(ev);
42
- const ontouchmove = (ev: TouchEvent) =>
43
- this.onTouchMove(ev);
44
- this.videoElementParent.addEventListener('touchstart', ontouchstart);
45
- this.videoElementParent.addEventListener('touchend', ontouchend);
46
- this.videoElementParent.addEventListener('touchmove', ontouchmove);
47
- this.touchEventListenerTracker.addUnregisterCallback(
48
- () => this.videoElementParent.removeEventListener('touchstart', ontouchstart)
49
- );
50
- this.touchEventListenerTracker.addUnregisterCallback(
51
- () => this.videoElementParent.removeEventListener('touchend', ontouchend)
52
- );
53
- this.touchEventListenerTracker.addUnregisterCallback(
54
- () => this.videoElementParent.removeEventListener('touchmove', ontouchmove)
55
- );
56
- Logger.Log(Logger.GetStackTrace(), 'Touch Events Registered', 6);
57
-
58
- // is this strictly necessary?
59
- const preventOnTouchMove = (event: TouchEvent) => {
60
- event.preventDefault();
61
- };
62
- document.addEventListener('touchmove', preventOnTouchMove);
63
- this.touchEventListenerTracker.addUnregisterCallback(
64
- () => document.removeEventListener('touchmove', preventOnTouchMove)
65
- );
34
+
35
+ this.videoElementParent = videoPlayer.getVideoElement();
36
+
37
+ this.onTouchStartListener = this.onTouchStart.bind(this);
38
+ this.onTouchEndListener = this.onTouchEnd.bind(this);
39
+ this.onTouchMoveListener = this.onTouchMove.bind(this);
66
40
  }
67
41
 
68
- /**
69
- * Unregister all touch events
70
- */
71
- unregisterTouchEvents() {
72
- this.touchEventListenerTracker.unregisterAll();
42
+ register() {
43
+ this.videoElementParent.addEventListener('touchstart', this.onTouchStartListener);
44
+ this.videoElementParent.addEventListener('touchend', this.onTouchEndListener);
45
+ this.videoElementParent.addEventListener('touchmove', this.onTouchMoveListener);
73
46
  }
74
47
 
75
- /**
76
- * Remember a touch command
77
- * @param touch - the touch command
78
- */
79
- rememberTouch(touch: Touch) {
48
+ unregister() {
49
+ this.videoElementParent.addEventListener('touchstart', this.onTouchStartListener);
50
+ this.videoElementParent.addEventListener('touchend', this.onTouchEndListener);
51
+ this.videoElementParent.addEventListener('touchmove', this.onTouchMoveListener);
52
+ }
53
+
54
+ private rememberTouch(touch: Touch) {
80
55
  const finger = this.fingers.pop();
81
56
  if (finger === undefined) {
82
- Logger.Log(
83
- Logger.GetStackTrace(),
84
- 'exhausted touch identifiers',
85
- 6
86
- );
57
+ Logger.Info('exhausted touch identifiers');
87
58
  }
88
59
  this.fingerIds.set(touch.identifier, finger);
89
60
  }
90
61
 
91
- /**
92
- * Forgets a touch command
93
- * @param touch - the touch command
94
- */
95
- forgetTouch(touch: Touch) {
62
+ private forgetTouch(touch: Touch) {
96
63
  this.fingers.push(this.fingerIds.get(touch.identifier));
97
64
  // Sort array back into descending order. This means if finger '1' were to lift after finger '0', we would ensure that 0 will be the first index to pop
98
65
  this.fingers.sort(function (a, b) {
@@ -101,32 +68,22 @@ export class TouchController implements ITouchController {
101
68
  this.fingerIds.delete(touch.identifier);
102
69
  }
103
70
 
104
- /**
105
- * When a touch event starts
106
- * @param touchEvent - the touch event being intercepted
107
- */
108
- onTouchStart(touchEvent: TouchEvent) {
109
- if (!this.videoElementProvider.isVideoReady()) {
71
+ private onTouchStart(touchEvent: TouchEvent) {
72
+ if (!this.videoPlayer.isVideoReady()) {
110
73
  return;
111
74
  }
112
75
  for (let t = 0; t < touchEvent.changedTouches.length; t++) {
113
76
  this.rememberTouch(touchEvent.changedTouches[t]);
114
77
  }
115
- Logger.Log(Logger.GetStackTrace(), 'touch start', 6);
116
78
 
117
79
  this.emitTouchData('TouchStart', touchEvent.changedTouches);
118
80
  touchEvent.preventDefault();
119
81
  }
120
82
 
121
- /**
122
- * When a touch event ends
123
- * @param touchEvent - the touch event being intercepted
124
- */
125
- onTouchEnd(touchEvent: TouchEvent) {
126
- if (!this.videoElementProvider.isVideoReady()) {
83
+ private onTouchEnd(touchEvent: TouchEvent) {
84
+ if (!this.videoPlayer.isVideoReady()) {
127
85
  return;
128
86
  }
129
- Logger.Log(Logger.GetStackTrace(), 'touch end', 6);
130
87
  this.emitTouchData('TouchEnd', touchEvent.changedTouches);
131
88
  // Re-cycle unique identifiers previously assigned to each touch.
132
89
  for (let t = 0; t < touchEvent.changedTouches.length; t++) {
@@ -135,42 +92,29 @@ export class TouchController implements ITouchController {
135
92
  touchEvent.preventDefault();
136
93
  }
137
94
 
138
- /**
139
- * when a moving touch event occurs
140
- * @param touchEvent - the touch event being intercepted
141
- */
142
- onTouchMove(touchEvent: TouchEvent) {
143
- if (!this.videoElementProvider.isVideoReady()) {
95
+ private onTouchMove(touchEvent: TouchEvent) {
96
+ if (!this.videoPlayer.isVideoReady()) {
144
97
  return;
145
98
  }
146
- Logger.Log(Logger.GetStackTrace(), 'touch move', 6);
147
99
  this.emitTouchData('TouchMove', touchEvent.touches);
148
100
  touchEvent.preventDefault();
149
101
  }
150
102
 
151
- emitTouchData(type: string, touches: TouchList) {
152
- if (!this.videoElementProvider.isVideoReady()) {
103
+ private emitTouchData(type: string, touches: TouchList) {
104
+ if (!this.videoPlayer.isVideoReady()) {
153
105
  return;
154
106
  }
155
- const offset = this.videoElementProvider.getVideoParentElement().getBoundingClientRect();
156
- const toStreamerHandlers =
157
- this.toStreamerMessagesProvider.toStreamerHandlers;
107
+ const offset = this.videoPlayer.getVideoParentElement().getBoundingClientRect();
108
+ const toStreamerHandlers = this.streamMessageController.toStreamerHandlers;
158
109
 
159
110
  for (let t = 0; t < touches.length; t++) {
160
111
  const numTouches = 1; // the number of touches to be sent this message
161
112
  const touch = touches[t];
162
113
  const x = touch.clientX - offset.left;
163
114
  const y = touch.clientY - offset.top;
164
- Logger.Log(
165
- Logger.GetStackTrace(),
166
- `F${this.fingerIds.get(touch.identifier)}=(${x}, ${y})`,
167
- 6
168
- );
169
-
170
- const coord = this.coordinateConverter.normalizeAndQuantizeUnsigned(
171
- x,
172
- y
173
- );
115
+ Logger.Info(`F${this.fingerIds.get(touch.identifier)}=(${x}, ${y})`);
116
+
117
+ const coord = this.coordinateConverter.translateUnsigned(x, y);
174
118
  switch (type) {
175
119
  case 'TouchStart':
176
120
  toStreamerHandlers.get('TouchStart')([
@@ -0,0 +1,132 @@
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+ import { InputCoordTranslator } from '../Util/InputCoordTranslator';
4
+ import { StreamMessageController } from '../UeInstanceMessage/StreamMessageController';
5
+ import { VideoPlayer } from '../VideoPlayer/VideoPlayer';
6
+ import { MouseButton } from './MouseButtons';
7
+ import { IInputController } from './IInputController';
8
+
9
+ interface FakeTouchFinger {
10
+ id: number;
11
+ x: number;
12
+ y: number;
13
+ }
14
+
15
+ /**
16
+ * Allows for the usage of fake touch events
17
+ */
18
+ export class TouchControllerFake implements IInputController {
19
+ streamMessageController: StreamMessageController;
20
+ videoPlayer: VideoPlayer;
21
+ coordinateConverter: InputCoordTranslator;
22
+
23
+ fakeTouchFinger: FakeTouchFinger;
24
+ videoElementParentClientRect: DOMRect;
25
+
26
+ onTouchStartListener: (event: TouchEvent) => void;
27
+ onTouchEndListener: (event: TouchEvent) => void;
28
+ onTouchMoveListener: (event: TouchEvent) => void;
29
+
30
+ constructor(
31
+ streamMessageController: StreamMessageController,
32
+ videoPlayer: VideoPlayer,
33
+ coordinateConverter: InputCoordTranslator
34
+ ) {
35
+ this.streamMessageController = streamMessageController;
36
+ this.videoPlayer = videoPlayer;
37
+ this.coordinateConverter = coordinateConverter;
38
+
39
+ this.onTouchStartListener = this.onTouchStart.bind(this);
40
+ this.onTouchEndListener = this.onTouchEnd.bind(this);
41
+ this.onTouchMoveListener = this.onTouchMove.bind(this);
42
+
43
+ this.videoElementParentClientRect = this.videoPlayer.getVideoParentElement().getBoundingClientRect();
44
+ }
45
+
46
+ register() {
47
+ document.addEventListener('touchstart', this.onTouchStartListener);
48
+ document.addEventListener('touchend', this.onTouchEndListener);
49
+ document.addEventListener('touchmove', this.onTouchMoveListener);
50
+ }
51
+
52
+ unregister() {
53
+ document.removeEventListener('touchstart', this.onTouchStartListener);
54
+ document.removeEventListener('touchend', this.onTouchEndListener);
55
+ document.removeEventListener('touchmove', this.onTouchMoveListener);
56
+ }
57
+
58
+ private onTouchStart(touch: TouchEvent): void {
59
+ if (!this.videoPlayer.isVideoReady() || touch.target !== this.videoPlayer.getVideoElement()) {
60
+ return;
61
+ }
62
+ if (this.fakeTouchFinger == null) {
63
+ const first_touch = touch.changedTouches[0];
64
+ this.fakeTouchFinger = {
65
+ id: first_touch.identifier,
66
+ x: first_touch.clientX - this.videoElementParentClientRect.left,
67
+ y: first_touch.clientY - this.videoElementParentClientRect.top
68
+ };
69
+
70
+ const videoElementParent = this.videoPlayer.getVideoParentElement() as HTMLDivElement;
71
+ const mouseEvent = new MouseEvent('mouseenter', first_touch);
72
+ videoElementParent.dispatchEvent(mouseEvent);
73
+
74
+ const coord = this.coordinateConverter.translateUnsigned(
75
+ this.fakeTouchFinger.x,
76
+ this.fakeTouchFinger.y
77
+ );
78
+ const toStreamerHandlers = this.streamMessageController.toStreamerHandlers;
79
+ toStreamerHandlers.get('MouseDown')([MouseButton.mainButton, coord.x, coord.y]);
80
+ }
81
+ touch.preventDefault();
82
+ }
83
+
84
+ private onTouchEnd(touchEvent: TouchEvent): void {
85
+ if (!this.videoPlayer.isVideoReady() || this.fakeTouchFinger == null) {
86
+ return;
87
+ }
88
+ const videoElementParent = this.videoPlayer.getVideoParentElement();
89
+ const toStreamerHandlers = this.streamMessageController.toStreamerHandlers;
90
+
91
+ for (let t = 0; t < touchEvent.changedTouches.length; t++) {
92
+ const touch = touchEvent.changedTouches[t];
93
+ if (touch.identifier === this.fakeTouchFinger.id) {
94
+ const x = touch.clientX - this.videoElementParentClientRect.left;
95
+ const y = touch.clientY - this.videoElementParentClientRect.top;
96
+ const coord = this.coordinateConverter.translateUnsigned(x, y);
97
+ toStreamerHandlers.get('MouseUp')([MouseButton.mainButton, coord.x, coord.y]);
98
+
99
+ const mouseEvent = new MouseEvent('mouseleave', touch);
100
+ videoElementParent.dispatchEvent(mouseEvent);
101
+ this.fakeTouchFinger = null;
102
+ break;
103
+ }
104
+ }
105
+ touchEvent.preventDefault();
106
+ }
107
+
108
+ private onTouchMove(touchEvent: TouchEvent): void {
109
+ if (!this.videoPlayer.isVideoReady() || this.fakeTouchFinger == null) {
110
+ return;
111
+ }
112
+ const toStreamerHandlers = this.streamMessageController.toStreamerHandlers;
113
+
114
+ for (let t = 0; t < touchEvent.touches.length; t++) {
115
+ const touch = touchEvent.touches[t];
116
+ if (touch.identifier === this.fakeTouchFinger.id) {
117
+ const x = touch.clientX - this.videoElementParentClientRect.left;
118
+ const y = touch.clientY - this.videoElementParentClientRect.top;
119
+ const coord = this.coordinateConverter.translateUnsigned(x, y);
120
+ const delta = this.coordinateConverter.translateSigned(
121
+ x - this.fakeTouchFinger.x,
122
+ y - this.fakeTouchFinger.y
123
+ );
124
+ toStreamerHandlers.get('MouseMove')([coord.x, coord.y, delta.x, delta.y]);
125
+ this.fakeTouchFinger.x = x;
126
+ this.fakeTouchFinger.y = y;
127
+ break;
128
+ }
129
+ }
130
+ touchEvent.preventDefault();
131
+ }
132
+ }
@@ -1,7 +1,7 @@
1
1
  // Copyright Epic Games, Inc. All Rights Reserved.
2
2
 
3
3
  import { StreamMessageController } from '../UeInstanceMessage/StreamMessageController';
4
- import { Controller } from './GamepadTypes';
4
+ import { Controller, deepCopyGamepad } from './GamepadTypes';
5
5
 
6
6
  /**
7
7
  * The class that handles the functionality of XR gamepads and controllers.
@@ -18,34 +18,7 @@ export class XRGamepadController {
18
18
  this.controllers = [];
19
19
  }
20
20
 
21
- /**
22
- * Deep copies the values from a gamepad by first converting it to a JSON object and then back to a gamepad
23
- *
24
- * @param gamepad the original gamepad
25
- * @returns a new gamepad object, populated with the original gamepads values
26
- */
27
- static deepCopyGamepad(gamepad: Gamepad): Gamepad {
28
- return JSON.parse(
29
- JSON.stringify({
30
- buttons: gamepad.buttons.map((b) =>
31
- JSON.parse(
32
- JSON.stringify({
33
- pressed: b.pressed,
34
- touched: b.touched,
35
- value: b.value
36
- })
37
- )
38
- ),
39
- axes: gamepad.axes
40
- })
41
- );
42
- }
43
-
44
- updateStatus(
45
- source: XRInputSource,
46
- frame: XRFrame,
47
- refSpace: XRReferenceSpace
48
- ) {
21
+ updateStatus(source: XRInputSource, frame: XRFrame, refSpace: XRReferenceSpace) {
49
22
  if (source.gamepad) {
50
23
  const gamepadPose = frame.getPose(source.gripSpace, refSpace);
51
24
  if (!gamepadPose) {
@@ -59,9 +32,7 @@ export class XRGamepadController {
59
32
  system = 2;
60
33
  }
61
34
  // TODO (william.belcher): Add other profiles (Quest, Microsoft Mixed Reality, etc)
62
- this.toStreamerMessagesProvider.toStreamerHandlers.get('XRSystem')([
63
- system
64
- ]);
35
+ this.toStreamerMessagesProvider.toStreamerHandlers.get('XRSystem')([system]);
65
36
 
66
37
  // Default: AnyHand (2)
67
38
  let handedness = 2;
@@ -95,12 +66,12 @@ export class XRGamepadController {
95
66
  this.controllers[handedness] = {
96
67
  prevState: undefined,
97
68
  currentState: undefined,
98
- id: undefined
69
+ id: undefined
99
70
  };
100
- this.controllers[handedness].prevState = XRGamepadController.deepCopyGamepad(source.gamepad);
71
+ this.controllers[handedness].prevState = deepCopyGamepad(source.gamepad);
101
72
  }
102
73
 
103
- this.controllers[handedness].currentState = XRGamepadController.deepCopyGamepad(source.gamepad);
74
+ this.controllers[handedness].currentState = deepCopyGamepad(source.gamepad);
104
75
 
105
76
  const controller = this.controllers[handedness];
106
77
  const currState = controller.currentState;
@@ -113,18 +84,34 @@ export class XRGamepadController {
113
84
  if (currButton.pressed) {
114
85
  // press
115
86
  const isRepeat = prevButton.pressed ? 1 : 0;
116
- this.toStreamerMessagesProvider.toStreamerHandlers.get('XRButtonPressed')([handedness, i, isRepeat, currButton.value]);
87
+ this.toStreamerMessagesProvider.toStreamerHandlers.get('XRButtonPressed')([
88
+ handedness,
89
+ i,
90
+ isRepeat,
91
+ currButton.value
92
+ ]);
117
93
  } else if (prevButton.pressed) {
118
- this.toStreamerMessagesProvider.toStreamerHandlers.get('XRButtonReleased')([handedness, i, 0]);
94
+ this.toStreamerMessagesProvider.toStreamerHandlers.get('XRButtonReleased')([
95
+ handedness,
96
+ i,
97
+ 0
98
+ ]);
119
99
  }
120
100
 
121
101
  if (currButton.touched) {
122
102
  // touched
123
103
  const isRepeat = prevButton.touched ? 1 : 0;
124
- this.toStreamerMessagesProvider.toStreamerHandlers.get('XRButtonTouched')([handedness, i, isRepeat]);
125
- }
126
- else if (prevButton.touched) {
127
- this.toStreamerMessagesProvider.toStreamerHandlers.get('XRButtonTouchReleased')([handedness, i, 0]);
104
+ this.toStreamerMessagesProvider.toStreamerHandlers.get('XRButtonTouched')([
105
+ handedness,
106
+ i,
107
+ isRepeat
108
+ ]);
109
+ } else if (prevButton.touched) {
110
+ this.toStreamerMessagesProvider.toStreamerHandlers.get('XRButtonTouchReleased')([
111
+ handedness,
112
+ i,
113
+ 0
114
+ ]);
128
115
  }
129
116
  }
130
117
 
@@ -133,12 +120,16 @@ export class XRGamepadController {
133
120
  const curAxisValue = currState.axes[i];
134
121
  const prevAxisValue = prevState.axes[i];
135
122
  // Only send axis update if there is a change
136
- if(curAxisValue != prevAxisValue) {
137
- this.toStreamerMessagesProvider.toStreamerHandlers.get('XRAnalog')([handedness, i, curAxisValue]);
123
+ if (curAxisValue != prevAxisValue) {
124
+ this.toStreamerMessagesProvider.toStreamerHandlers.get('XRAnalog')([
125
+ handedness,
126
+ i,
127
+ curAxisValue
128
+ ]);
138
129
  }
139
130
  }
140
131
 
141
132
  this.controllers[handedness].prevState = currState;
142
133
  }
143
134
  }
144
- }
135
+ }