@epicgames-ps/lib-pixelstreamingfrontend-ue5.5 0.0.5
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/.cspell.json +48 -0
- package/.eslintignore +8 -0
- package/.eslintrc.js +8 -0
- package/.prettierignore +0 -0
- package/.prettierrc.json +6 -0
- package/dist/lib-pixelstreamingfrontend.esm.js +1 -0
- package/dist/lib-pixelstreamingfrontend.js +1 -0
- package/jest.config.js +18 -0
- package/package.json +48 -0
- package/readme.md +15 -0
- package/src/AFK/AFKController.test.ts +162 -0
- package/src/AFK/AFKController.ts +158 -0
- package/src/Config/Config.test.ts +222 -0
- package/src/Config/Config.ts +970 -0
- package/src/Config/SettingBase.ts +65 -0
- package/src/Config/SettingFlag.ts +99 -0
- package/src/Config/SettingNumber.ts +111 -0
- package/src/Config/SettingOption.ts +124 -0
- package/src/Config/SettingText.ts +82 -0
- package/src/DataChannel/DataChannelController.ts +138 -0
- package/src/DataChannel/DataChannelLatencyTestController.ts +129 -0
- package/src/DataChannel/DataChannelLatencyTestResults.ts +67 -0
- package/src/DataChannel/DataChannelSender.ts +59 -0
- package/src/DataChannel/InitialSettings.ts +61 -0
- package/src/DataChannel/LatencyTestResults.ts +76 -0
- package/src/FreezeFrame/FreezeFrame.ts +114 -0
- package/src/FreezeFrame/FreezeFrameController.ts +114 -0
- package/src/Inputs/FakeTouchController.ts +199 -0
- package/src/Inputs/GamepadController.ts +314 -0
- package/src/Inputs/GamepadTypes.ts +10 -0
- package/src/Inputs/HoveringMouseEvents.ts +192 -0
- package/src/Inputs/IMouseEvents.ts +64 -0
- package/src/Inputs/ITouchController.ts +29 -0
- package/src/Inputs/InputClassesFactory.ts +140 -0
- package/src/Inputs/KeyboardController.ts +354 -0
- package/src/Inputs/LockedMouseEvents.ts +287 -0
- package/src/Inputs/MouseButtons.ts +25 -0
- package/src/Inputs/MouseController.ts +362 -0
- package/src/Inputs/SpecialKeyCodes.ts +16 -0
- package/src/Inputs/TouchController.ts +208 -0
- package/src/Inputs/XRGamepadController.ts +126 -0
- package/src/PeerConnectionController/AggregatedStats.ts +311 -0
- package/src/PeerConnectionController/CandidatePairStats.ts +17 -0
- package/src/PeerConnectionController/CandidateStat.ts +13 -0
- package/src/PeerConnectionController/CodecStats.ts +19 -0
- package/src/PeerConnectionController/DataChannelStats.ts +17 -0
- package/src/PeerConnectionController/InboundRTPStats.ts +154 -0
- package/src/PeerConnectionController/InboundTrackStats.ts +34 -0
- package/src/PeerConnectionController/OutBoundRTPStats.ts +26 -0
- package/src/PeerConnectionController/PeerConnectionController.ts +563 -0
- package/src/PeerConnectionController/SessionStats.ts +10 -0
- package/src/PeerConnectionController/StreamStats.ts +11 -0
- package/src/PixelStreaming/PixelStreaming.test.ts +626 -0
- package/src/PixelStreaming/PixelStreaming.ts +851 -0
- package/src/UI/OnScreenKeyboard.ts +97 -0
- package/src/UeInstanceMessage/ResponseController.ts +47 -0
- package/src/UeInstanceMessage/SendMessageController.ts +154 -0
- package/src/UeInstanceMessage/StreamMessageController.ts +233 -0
- package/src/UeInstanceMessage/ToStreamerMessagesController.ts +62 -0
- package/src/Util/CoordinateConverter.ts +289 -0
- package/src/Util/EventEmitter.ts +611 -0
- package/src/Util/EventListenerTracker.ts +29 -0
- package/src/Util/FileUtil.ts +140 -0
- package/src/Util/RTCUtils.ts +41 -0
- package/src/Util/WebGLUtils.ts +49 -0
- package/src/Util/WebXRUtils.ts +25 -0
- package/src/VideoPlayer/StreamController.ts +89 -0
- package/src/VideoPlayer/VideoPlayer.ts +246 -0
- package/src/WebRtcPlayer/WebRtcPlayerController.ts +2158 -0
- package/src/WebXR/WebXRController.ts +319 -0
- package/src/__test__/mockMediaStream.ts +124 -0
- package/src/__test__/mockRTCPeerConnection.ts +347 -0
- package/src/__test__/mockRTCRtpReceiver.ts +22 -0
- package/src/__test__/mockWebSocket.ts +136 -0
- package/src/pixelstreamingfrontend.ts +46 -0
- package/tsconfig.jest.json +8 -0
- package/tsconfig.json +24 -0
- package/types/AFK/AFKController.d.ts +39 -0
- package/types/Config/Config.d.ts +218 -0
- package/types/Config/SettingBase.d.ts +30 -0
- package/types/Config/SettingFlag.d.ts +33 -0
- package/types/Config/SettingNumber.d.ts +45 -0
- package/types/Config/SettingOption.d.ts +43 -0
- package/types/Config/SettingText.d.ts +29 -0
- package/types/DataChannel/DataChannelController.d.ts +59 -0
- package/types/DataChannel/DataChannelLatencyTestController.d.ts +26 -0
- package/types/DataChannel/DataChannelLatencyTestResults.d.ts +46 -0
- package/types/DataChannel/DataChannelSender.d.ts +21 -0
- package/types/DataChannel/InitialSettings.d.ts +44 -0
- package/types/DataChannel/LatencyTestResults.d.ts +31 -0
- package/types/FreezeFrame/FreezeFrame.d.ts +36 -0
- package/types/FreezeFrame/FreezeFrameController.d.ts +37 -0
- package/types/Inputs/FakeTouchController.d.ts +61 -0
- package/types/Inputs/GamepadController.d.ts +85 -0
- package/types/Inputs/GamepadTypes.d.ts +8 -0
- package/types/Inputs/HoveringMouseEvents.d.ts +56 -0
- package/types/Inputs/IMouseEvents.d.ts +53 -0
- package/types/Inputs/ITouchController.d.ts +24 -0
- package/types/Inputs/InputClassesFactory.d.ts +54 -0
- package/types/Inputs/KeyboardController.d.ts +62 -0
- package/types/Inputs/LockedMouseEvents.d.ts +80 -0
- package/types/Inputs/MouseButtons.d.ts +22 -0
- package/types/Inputs/MouseController.d.ts +75 -0
- package/types/Inputs/SpecialKeyCodes.d.ts +14 -0
- package/types/Inputs/TouchController.d.ts +53 -0
- package/types/Inputs/XRGamepadController.d.ts +15 -0
- package/types/PeerConnectionController/AggregatedStats.d.ts +77 -0
- package/types/PeerConnectionController/CandidatePairStats.d.ts +15 -0
- package/types/PeerConnectionController/CandidateStat.d.ts +11 -0
- package/types/PeerConnectionController/CodecStats.d.ts +14 -0
- package/types/PeerConnectionController/DataChannelStats.d.ts +15 -0
- package/types/PeerConnectionController/InboundRTPStats.d.ts +141 -0
- package/types/PeerConnectionController/InboundTrackStats.d.ts +32 -0
- package/types/PeerConnectionController/OutBoundRTPStats.d.ts +23 -0
- package/types/PeerConnectionController/PeerConnectionController.d.ts +132 -0
- package/types/PeerConnectionController/SessionStats.d.ts +8 -0
- package/types/PeerConnectionController/StreamStats.d.ts +9 -0
- package/types/PixelStreaming/PixelStreaming.d.ts +259 -0
- package/types/UI/OnScreenKeyboard.d.ts +31 -0
- package/types/UeInstanceMessage/ResponseController.d.ts +19 -0
- package/types/UeInstanceMessage/SendMessageController.d.ts +18 -0
- package/types/UeInstanceMessage/StreamMessageController.d.ts +29 -0
- package/types/UeInstanceMessage/ToStreamerMessagesController.d.ts +32 -0
- package/types/Util/CoordinateConverter.d.ts +100 -0
- package/types/Util/EventEmitter.d.ts +422 -0
- package/types/Util/EventListenerTracker.d.ts +14 -0
- package/types/Util/FileUtil.d.ts +32 -0
- package/types/Util/RTCUtils.d.ts +8 -0
- package/types/Util/WebGLUtils.d.ts +4 -0
- package/types/Util/WebXRUtils.d.ts +9 -0
- package/types/VideoPlayer/StreamController.d.ts +24 -0
- package/types/VideoPlayer/VideoPlayer.d.ts +78 -0
- package/types/WebRtcPlayer/WebRtcPlayerController.d.ts +377 -0
- package/types/WebXR/WebXRController.d.ts +26 -0
- package/types/pixelstreamingfrontend.d.ts +22 -0
- package/webpack.common.js +35 -0
- package/webpack.dev.js +35 -0
- package/webpack.prod.js +36 -0
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
2
|
+
|
|
3
|
+
import { Logger } from '@epicgames-ps/lib-pixelstreamingcommon-ue5.5';
|
|
4
|
+
import { WebRtcPlayerController } from '../WebRtcPlayer/WebRtcPlayerController';
|
|
5
|
+
import { WebGLUtils } from '../Util/WebGLUtils';
|
|
6
|
+
import { Controller } from '../Inputs/GamepadTypes';
|
|
7
|
+
import { XRGamepadController } from '../Inputs/XRGamepadController';
|
|
8
|
+
import { XrFrameEvent } from '../Util/EventEmitter'
|
|
9
|
+
import { Flags } from '../pixelstreamingfrontend';
|
|
10
|
+
|
|
11
|
+
export class WebXRController {
|
|
12
|
+
private xrSession: XRSession;
|
|
13
|
+
private xrRefSpace: XRReferenceSpace;
|
|
14
|
+
private gl: WebGL2RenderingContext;
|
|
15
|
+
|
|
16
|
+
private positionLocation: number;
|
|
17
|
+
private texcoordLocation: number;
|
|
18
|
+
private resolutionLocation: WebGLUniformLocation;
|
|
19
|
+
private offsetLocation: WebGLUniformLocation;
|
|
20
|
+
|
|
21
|
+
private positionBuffer: WebGLBuffer;
|
|
22
|
+
private texcoordBuffer: WebGLBuffer;
|
|
23
|
+
|
|
24
|
+
private webRtcController: WebRtcPlayerController;
|
|
25
|
+
private xrGamepadController: XRGamepadController;
|
|
26
|
+
private xrControllers: Array<Controller>;
|
|
27
|
+
|
|
28
|
+
onSessionStarted: EventTarget;
|
|
29
|
+
onSessionEnded: EventTarget;
|
|
30
|
+
onFrame: EventTarget;
|
|
31
|
+
|
|
32
|
+
constructor(webRtcPlayerController: WebRtcPlayerController) {
|
|
33
|
+
this.xrSession = null;
|
|
34
|
+
this.webRtcController = webRtcPlayerController;
|
|
35
|
+
this.xrControllers = [];
|
|
36
|
+
this.xrGamepadController = new XRGamepadController(
|
|
37
|
+
this.webRtcController.streamMessageController
|
|
38
|
+
);
|
|
39
|
+
this.onSessionEnded = new EventTarget();
|
|
40
|
+
this.onSessionStarted = new EventTarget();
|
|
41
|
+
this.onFrame = new EventTarget();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public xrClicked() {
|
|
45
|
+
if (!this.xrSession) {
|
|
46
|
+
navigator.xr
|
|
47
|
+
.requestSession('immersive-vr')
|
|
48
|
+
.then((session: XRSession) => {
|
|
49
|
+
this.onXrSessionStarted(session);
|
|
50
|
+
});
|
|
51
|
+
} else {
|
|
52
|
+
this.xrSession.end();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
onXrSessionEnded() {
|
|
57
|
+
Logger.Log(Logger.GetStackTrace(), 'XR Session ended');
|
|
58
|
+
this.xrSession = null;
|
|
59
|
+
this.onSessionEnded.dispatchEvent(new Event('xrSessionEnded'));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
onXrSessionStarted(session: XRSession) {
|
|
63
|
+
Logger.Log(Logger.GetStackTrace(), 'XR Session started');
|
|
64
|
+
|
|
65
|
+
this.xrSession = session;
|
|
66
|
+
this.xrSession.addEventListener('end', () => {
|
|
67
|
+
this.onXrSessionEnded();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const canvas = document.createElement('canvas');
|
|
71
|
+
this.gl = canvas.getContext('webgl2', {
|
|
72
|
+
xrCompatible: true
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
this.xrSession.updateRenderState({
|
|
76
|
+
baseLayer: new XRWebGLLayer(this.xrSession, this.gl)
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// setup vertex shader
|
|
80
|
+
const vertexShader = this.gl.createShader(this.gl.VERTEX_SHADER);
|
|
81
|
+
this.gl.shaderSource(vertexShader, WebGLUtils.vertexShader());
|
|
82
|
+
this.gl.compileShader(vertexShader);
|
|
83
|
+
|
|
84
|
+
// setup fragment shader
|
|
85
|
+
const fragmentShader = this.gl.createShader(this.gl.FRAGMENT_SHADER);
|
|
86
|
+
this.gl.shaderSource(fragmentShader, WebGLUtils.fragmentShader());
|
|
87
|
+
this.gl.compileShader(fragmentShader);
|
|
88
|
+
|
|
89
|
+
// setup GLSL program
|
|
90
|
+
const shaderProgram = this.gl.createProgram();
|
|
91
|
+
this.gl.attachShader(shaderProgram, vertexShader);
|
|
92
|
+
this.gl.attachShader(shaderProgram, fragmentShader);
|
|
93
|
+
this.gl.linkProgram(shaderProgram);
|
|
94
|
+
this.gl.useProgram(shaderProgram);
|
|
95
|
+
|
|
96
|
+
// look up where vertex data needs to go
|
|
97
|
+
this.positionLocation = this.gl.getAttribLocation(
|
|
98
|
+
shaderProgram,
|
|
99
|
+
'a_position'
|
|
100
|
+
);
|
|
101
|
+
this.texcoordLocation = this.gl.getAttribLocation(
|
|
102
|
+
shaderProgram,
|
|
103
|
+
'a_texCoord'
|
|
104
|
+
);
|
|
105
|
+
// Create a buffer to put three 2d clip space points in
|
|
106
|
+
this.positionBuffer = this.gl.createBuffer();
|
|
107
|
+
// Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer)
|
|
108
|
+
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer);
|
|
109
|
+
|
|
110
|
+
// Turn on the position attribute
|
|
111
|
+
this.gl.enableVertexAttribArray(this.positionLocation);
|
|
112
|
+
// Create a texture.
|
|
113
|
+
const texture = this.gl.createTexture();
|
|
114
|
+
this.gl.bindTexture(this.gl.TEXTURE_2D, texture);
|
|
115
|
+
// Set the parameters so we can render any size image.
|
|
116
|
+
this.gl.texParameteri(
|
|
117
|
+
this.gl.TEXTURE_2D,
|
|
118
|
+
this.gl.TEXTURE_WRAP_S,
|
|
119
|
+
this.gl.CLAMP_TO_EDGE
|
|
120
|
+
);
|
|
121
|
+
this.gl.texParameteri(
|
|
122
|
+
this.gl.TEXTURE_2D,
|
|
123
|
+
this.gl.TEXTURE_WRAP_T,
|
|
124
|
+
this.gl.CLAMP_TO_EDGE
|
|
125
|
+
);
|
|
126
|
+
this.gl.texParameteri(
|
|
127
|
+
this.gl.TEXTURE_2D,
|
|
128
|
+
this.gl.TEXTURE_MIN_FILTER,
|
|
129
|
+
this.gl.NEAREST
|
|
130
|
+
);
|
|
131
|
+
this.gl.texParameteri(
|
|
132
|
+
this.gl.TEXTURE_2D,
|
|
133
|
+
this.gl.TEXTURE_MAG_FILTER,
|
|
134
|
+
this.gl.NEAREST
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
this.texcoordBuffer = this.gl.createBuffer();
|
|
138
|
+
// lookup uniforms
|
|
139
|
+
this.resolutionLocation = this.gl.getUniformLocation(
|
|
140
|
+
shaderProgram,
|
|
141
|
+
'u_resolution'
|
|
142
|
+
);
|
|
143
|
+
this.offsetLocation = this.gl.getUniformLocation(
|
|
144
|
+
shaderProgram,
|
|
145
|
+
'u_offset'
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
session.requestReferenceSpace('local').then((refSpace) => {
|
|
149
|
+
this.xrRefSpace = refSpace;
|
|
150
|
+
this.xrSession.requestAnimationFrame(
|
|
151
|
+
(time: DOMHighResTimeStamp, frame: XRFrame) =>
|
|
152
|
+
this.onXrFrame(time, frame)
|
|
153
|
+
);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
this.onSessionStarted.dispatchEvent(new Event('xrSessionStarted'));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
onXrFrame(time: DOMHighResTimeStamp, frame: XRFrame) {
|
|
160
|
+
const pose = frame.getViewerPose(this.xrRefSpace);
|
|
161
|
+
if (pose) {
|
|
162
|
+
const matrix = pose.transform.matrix;
|
|
163
|
+
const mat = [];
|
|
164
|
+
for (let i = 0; i < 16; i++) {
|
|
165
|
+
mat[i] = new Float32Array([matrix[i]])[0];
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// prettier-ignore
|
|
169
|
+
this.webRtcController.streamMessageController.toStreamerHandlers.get('XRHMDTransform')([
|
|
170
|
+
mat[0], mat[4], mat[8], mat[12],
|
|
171
|
+
mat[1], mat[5], mat[9], mat[13],
|
|
172
|
+
mat[2], mat[6], mat[10], mat[14],
|
|
173
|
+
mat[3], mat[7], mat[11], mat[15]
|
|
174
|
+
]);
|
|
175
|
+
|
|
176
|
+
const glLayer = this.xrSession.renderState.baseLayer;
|
|
177
|
+
// If we do have a valid pose, bind the WebGL layer's framebuffer,
|
|
178
|
+
// which is where any content to be displayed on the XRDevice must be
|
|
179
|
+
// rendered.
|
|
180
|
+
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, glLayer.framebuffer);
|
|
181
|
+
|
|
182
|
+
// Upload the image into the texture. WebGL knows how to extract the current frame from the video element
|
|
183
|
+
this.gl.texImage2D(
|
|
184
|
+
this.gl.TEXTURE_2D,
|
|
185
|
+
0,
|
|
186
|
+
this.gl.RGBA,
|
|
187
|
+
this.gl.RGBA,
|
|
188
|
+
this.gl.UNSIGNED_BYTE,
|
|
189
|
+
this.webRtcController.videoPlayer.getVideoElement()
|
|
190
|
+
);
|
|
191
|
+
this.render(this.webRtcController.videoPlayer.getVideoElement());
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (this.webRtcController.config.isFlagEnabled(Flags.XRControllerInput)) {
|
|
195
|
+
this.xrSession.inputSources.forEach(
|
|
196
|
+
(source: XRInputSource, index: number, array: XRInputSource[]) => {
|
|
197
|
+
this.xrGamepadController.updateStatus(
|
|
198
|
+
source,
|
|
199
|
+
frame,
|
|
200
|
+
this.xrRefSpace
|
|
201
|
+
);
|
|
202
|
+
},
|
|
203
|
+
this
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
this.xrSession.requestAnimationFrame(
|
|
208
|
+
(time: DOMHighResTimeStamp, frame: XRFrame) =>
|
|
209
|
+
this.onXrFrame(time, frame)
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
this.onFrame.dispatchEvent(new XrFrameEvent({
|
|
213
|
+
time,
|
|
214
|
+
frame
|
|
215
|
+
}));
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
private render(videoElement: HTMLVideoElement) {
|
|
219
|
+
if (!this.gl) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const glLayer = this.xrSession.renderState.baseLayer;
|
|
224
|
+
this.gl.viewport(
|
|
225
|
+
0,
|
|
226
|
+
0,
|
|
227
|
+
glLayer.framebufferWidth,
|
|
228
|
+
glLayer.framebufferHeight
|
|
229
|
+
);
|
|
230
|
+
this.gl.uniform4f(this.offsetLocation, 1.0, 1.0, 0.0, 0.0);
|
|
231
|
+
|
|
232
|
+
// Set rectangle
|
|
233
|
+
// prettier-ignore
|
|
234
|
+
this.gl.bufferData(
|
|
235
|
+
this.gl.ARRAY_BUFFER,
|
|
236
|
+
new Float32Array([
|
|
237
|
+
0, 0,
|
|
238
|
+
videoElement.videoWidth, 0,
|
|
239
|
+
0, videoElement.videoHeight,
|
|
240
|
+
0, videoElement.videoHeight,
|
|
241
|
+
videoElement.videoWidth, 0,
|
|
242
|
+
videoElement.videoWidth, videoElement.videoHeight
|
|
243
|
+
]),
|
|
244
|
+
this.gl.STATIC_DRAW
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
// Provide texture coordinates for the rectangle
|
|
248
|
+
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.texcoordBuffer);
|
|
249
|
+
this.gl.bufferData(
|
|
250
|
+
this.gl.ARRAY_BUFFER,
|
|
251
|
+
new Float32Array([
|
|
252
|
+
0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0
|
|
253
|
+
]),
|
|
254
|
+
this.gl.STATIC_DRAW
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
let size; // components per iteration
|
|
258
|
+
let type; // the data type
|
|
259
|
+
let normalize; // normalize the data
|
|
260
|
+
let stride; // 0 = move forward size * sizeof(type) each iteration to get the next position
|
|
261
|
+
let offset; // start position of the buffer
|
|
262
|
+
|
|
263
|
+
// Bind the position buffer.
|
|
264
|
+
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer);
|
|
265
|
+
// Tell the position attribute how to get data out of positionBuffer (ARRAY_BUFFER)
|
|
266
|
+
size = 2; // 2 components per iteration
|
|
267
|
+
type = this.gl.FLOAT; // the data is 32bit floats
|
|
268
|
+
normalize = false; // don't normalize the data
|
|
269
|
+
stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
|
|
270
|
+
offset = 0; // start at the beginning of the buffer
|
|
271
|
+
this.gl.vertexAttribPointer(
|
|
272
|
+
this.positionLocation,
|
|
273
|
+
size,
|
|
274
|
+
type,
|
|
275
|
+
normalize,
|
|
276
|
+
stride,
|
|
277
|
+
offset
|
|
278
|
+
);
|
|
279
|
+
// Turn on the texcoord attribute
|
|
280
|
+
this.gl.enableVertexAttribArray(this.texcoordLocation);
|
|
281
|
+
// bind the texcoord buffer.
|
|
282
|
+
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.texcoordBuffer);
|
|
283
|
+
// Tell the texcoord attribute how to get data out of texcoordBuffer (ARRAY_BUFFER)
|
|
284
|
+
size = 2; // 2 components per iteration
|
|
285
|
+
type = this.gl.FLOAT; // the data is 32bit floats
|
|
286
|
+
normalize = false; // don't normalize the data
|
|
287
|
+
stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
|
|
288
|
+
offset = 0; // start at the beginning of the buffer
|
|
289
|
+
this.gl.vertexAttribPointer(
|
|
290
|
+
this.texcoordLocation,
|
|
291
|
+
size,
|
|
292
|
+
type,
|
|
293
|
+
normalize,
|
|
294
|
+
stride,
|
|
295
|
+
offset
|
|
296
|
+
);
|
|
297
|
+
// set the resolution
|
|
298
|
+
this.gl.uniform2f(
|
|
299
|
+
this.resolutionLocation,
|
|
300
|
+
videoElement.videoWidth,
|
|
301
|
+
videoElement.videoHeight
|
|
302
|
+
);
|
|
303
|
+
// draw the rectangle.
|
|
304
|
+
const primitiveType = this.gl.TRIANGLES;
|
|
305
|
+
const count = 6;
|
|
306
|
+
offset = 0;
|
|
307
|
+
this.gl.drawArrays(primitiveType, offset, count);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
static isSessionSupported(mode: XRSessionMode): Promise<boolean> {
|
|
311
|
+
if (navigator.xr) {
|
|
312
|
+
return navigator.xr.isSessionSupported(mode);
|
|
313
|
+
} else {
|
|
314
|
+
return new Promise<boolean>(() => {
|
|
315
|
+
return false;
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
export class MockMediaStreamImpl implements MediaStream {
|
|
2
|
+
active: boolean;
|
|
3
|
+
id: string;
|
|
4
|
+
|
|
5
|
+
constructor(data?: MediaStream | MediaStreamTrack[]) {
|
|
6
|
+
//
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
onaddtrack: ((this: MediaStream, ev: MediaStreamTrackEvent) => any) | null;
|
|
10
|
+
onremovetrack: ((this: MediaStream, ev: MediaStreamTrackEvent) => any) | null;
|
|
11
|
+
addTrack(track: MediaStreamTrack): void {
|
|
12
|
+
throw new Error("Method not implemented.");
|
|
13
|
+
}
|
|
14
|
+
clone(): MediaStream {
|
|
15
|
+
throw new Error("Method not implemented.");
|
|
16
|
+
}
|
|
17
|
+
getAudioTracks(): MediaStreamTrack[] {
|
|
18
|
+
throw new Error("Method not implemented.");
|
|
19
|
+
}
|
|
20
|
+
getTrackById(trackId: string): MediaStreamTrack | null {
|
|
21
|
+
throw new Error("Method not implemented.");
|
|
22
|
+
}
|
|
23
|
+
getTracks(): MediaStreamTrack[] {
|
|
24
|
+
throw new Error("Method not implemented.");
|
|
25
|
+
}
|
|
26
|
+
getVideoTracks(): MediaStreamTrack[] {
|
|
27
|
+
throw new Error("Method not implemented.");
|
|
28
|
+
}
|
|
29
|
+
removeTrack(track: MediaStreamTrack): void {
|
|
30
|
+
throw new Error("Method not implemented.");
|
|
31
|
+
}
|
|
32
|
+
addEventListener<K extends keyof MediaStreamEventMap>(type: K, listener: (this: MediaStream, ev: MediaStreamEventMap[K]) => any, options?: boolean | AddEventListenerOptions | undefined): void;
|
|
33
|
+
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions | undefined): void;
|
|
34
|
+
addEventListener(type: unknown, listener: unknown, options?: unknown): void {
|
|
35
|
+
throw new Error("Method not implemented.");
|
|
36
|
+
}
|
|
37
|
+
removeEventListener<K extends keyof MediaStreamEventMap>(type: K, listener: (this: MediaStream, ev: MediaStreamEventMap[K]) => any, options?: boolean | EventListenerOptions | undefined): void;
|
|
38
|
+
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions | undefined): void;
|
|
39
|
+
removeEventListener(type: unknown, listener: unknown, options?: unknown): void {
|
|
40
|
+
throw new Error("Method not implemented.");
|
|
41
|
+
}
|
|
42
|
+
dispatchEvent(event: Event): boolean {
|
|
43
|
+
throw new Error("Method not implemented.");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export class MockMediaStreamTrackImpl implements MediaStreamTrack {
|
|
49
|
+
contentHint: string;
|
|
50
|
+
enabled: boolean;
|
|
51
|
+
id: string;
|
|
52
|
+
kind: string;
|
|
53
|
+
label: string;
|
|
54
|
+
muted: boolean;
|
|
55
|
+
readyState: MediaStreamTrackState;
|
|
56
|
+
|
|
57
|
+
constructor() {
|
|
58
|
+
this.kind = 'video';
|
|
59
|
+
this.readyState = 'live';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
onended: ((this: MediaStreamTrack, ev: Event) => any) | null;
|
|
63
|
+
onmute: ((this: MediaStreamTrack, ev: Event) => any) | null;
|
|
64
|
+
onunmute: ((this: MediaStreamTrack, ev: Event) => any) | null;
|
|
65
|
+
applyConstraints(constraints?: MediaTrackConstraints | undefined): Promise<void> {
|
|
66
|
+
throw new Error("Method not implemented.");
|
|
67
|
+
}
|
|
68
|
+
clone(): MediaStreamTrack {
|
|
69
|
+
throw new Error("Method not implemented.");
|
|
70
|
+
}
|
|
71
|
+
getCapabilities(): MediaTrackCapabilities {
|
|
72
|
+
throw new Error("Method not implemented.");
|
|
73
|
+
}
|
|
74
|
+
getConstraints(): MediaTrackConstraints {
|
|
75
|
+
throw new Error("Method not implemented.");
|
|
76
|
+
}
|
|
77
|
+
getSettings(): MediaTrackSettings {
|
|
78
|
+
throw new Error("Method not implemented.");
|
|
79
|
+
}
|
|
80
|
+
stop(): void {
|
|
81
|
+
throw new Error("Method not implemented.");
|
|
82
|
+
}
|
|
83
|
+
addEventListener<K extends keyof MediaStreamTrackEventMap>(type: K, listener: (this: MediaStreamTrack, ev: MediaStreamTrackEventMap[K]) => any, options?: boolean | AddEventListenerOptions | undefined): void;
|
|
84
|
+
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions | undefined): void;
|
|
85
|
+
addEventListener(type: unknown, listener: unknown, options?: unknown): void {
|
|
86
|
+
throw new Error("Method not implemented.");
|
|
87
|
+
}
|
|
88
|
+
removeEventListener<K extends keyof MediaStreamTrackEventMap>(type: K, listener: (this: MediaStreamTrack, ev: MediaStreamTrackEventMap[K]) => any, options?: boolean | EventListenerOptions | undefined): void;
|
|
89
|
+
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions | undefined): void;
|
|
90
|
+
removeEventListener(type: unknown, listener: unknown, options?: unknown): void {
|
|
91
|
+
throw new Error("Method not implemented.");
|
|
92
|
+
}
|
|
93
|
+
dispatchEvent(event: Event): boolean {
|
|
94
|
+
throw new Error("Method not implemented.");
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const mockHTMLMediaElementPlay = (ableToPlay: boolean) => {
|
|
99
|
+
if (ableToPlay) {
|
|
100
|
+
return Promise.resolve();
|
|
101
|
+
}
|
|
102
|
+
return Promise.reject("mock cancel");
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const originalMediaStream = global.MediaStream;
|
|
106
|
+
const originalMediaStreamTrack = global.MediaStreamTrack;
|
|
107
|
+
export const mockMediaStream = () => {
|
|
108
|
+
global.MediaStream = MockMediaStreamImpl;
|
|
109
|
+
global.MediaStreamTrack = MockMediaStreamTrackImpl;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export const unmockMediaStream = () => {
|
|
113
|
+
global.MediaStream = originalMediaStream;
|
|
114
|
+
global.MediaStreamTrack = originalMediaStreamTrack;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export const mockHTMLMediaElement = (options: { ableToPlay: boolean, readyState?: number }) => {
|
|
118
|
+
const { ableToPlay, readyState } = options;
|
|
119
|
+
jest.spyOn(HTMLMediaElement.prototype, 'play').mockReturnValue(mockHTMLMediaElementPlay(ableToPlay));
|
|
120
|
+
if (readyState !== undefined) {
|
|
121
|
+
jest.spyOn(HTMLMediaElement.prototype, 'readyState', 'get').mockReturnValue(readyState);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|