@yang__yj/pixelstreaming-core 1.0.0 → 1.0.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.
- package/.cspell.json +48 -0
- package/.eslintignore +8 -0
- package/.eslintrc.js +8 -0
- package/.prettierignore +0 -0
- package/.prettierrc.json +6 -0
- package/coverage/clover.xml +3480 -0
- package/coverage/coverage-final.json +62 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +356 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +196 -0
- package/coverage/lcov-report/src/AFK/AFKController.ts.html +559 -0
- package/coverage/lcov-report/src/AFK/index.html +116 -0
- package/coverage/lcov-report/src/Config/Config.ts.html +2764 -0
- package/coverage/lcov-report/src/Config/SettingBase.ts.html +280 -0
- package/coverage/lcov-report/src/Config/SettingFlag.ts.html +382 -0
- package/coverage/lcov-report/src/Config/SettingNumber.ts.html +418 -0
- package/coverage/lcov-report/src/Config/SettingOption.ts.html +424 -0
- package/coverage/lcov-report/src/Config/SettingText.ts.html +331 -0
- package/coverage/lcov-report/src/Config/index.html +191 -0
- package/coverage/lcov-report/src/DataChannel/DataChannelController.ts.html +499 -0
- package/coverage/lcov-report/src/DataChannel/DataChannelSender.ts.html +262 -0
- package/coverage/lcov-report/src/DataChannel/InitialSettings.ts.html +268 -0
- package/coverage/lcov-report/src/DataChannel/LatencyTestResults.ts.html +313 -0
- package/coverage/lcov-report/src/DataChannel/index.html +161 -0
- package/coverage/lcov-report/src/FreezeFrame/FreezeFrame.ts.html +427 -0
- package/coverage/lcov-report/src/FreezeFrame/FreezeFrameController.ts.html +427 -0
- package/coverage/lcov-report/src/FreezeFrame/index.html +131 -0
- package/coverage/lcov-report/src/Inputs/FakeTouchController.ts.html +682 -0
- package/coverage/lcov-report/src/Inputs/GamepadController.ts.html +985 -0
- package/coverage/lcov-report/src/Inputs/HoveringMouseEvents.ts.html +688 -0
- package/coverage/lcov-report/src/Inputs/InputClassesFactory.ts.html +505 -0
- package/coverage/lcov-report/src/Inputs/KeyboardController.ts.html +1048 -0
- package/coverage/lcov-report/src/Inputs/LockedMouseEvents.ts.html +946 -0
- package/coverage/lcov-report/src/Inputs/MouseButtons.ts.html +160 -0
- package/coverage/lcov-report/src/Inputs/MouseController.ts.html +1171 -0
- package/coverage/lcov-report/src/Inputs/SpecialKeyCodes.ts.html +133 -0
- package/coverage/lcov-report/src/Inputs/TouchController.ts.html +712 -0
- package/coverage/lcov-report/src/Inputs/XRGamepadController.ts.html +463 -0
- package/coverage/lcov-report/src/Inputs/index.html +266 -0
- package/coverage/lcov-report/src/Logger/Logger.ts.html +325 -0
- package/coverage/lcov-report/src/Logger/index.html +116 -0
- package/coverage/lcov-report/src/PeerConnectionController/AggregatedStats.ts.html +1018 -0
- package/coverage/lcov-report/src/PeerConnectionController/CandidatePairStats.ts.html +136 -0
- package/coverage/lcov-report/src/PeerConnectionController/CandidateStat.ts.html +124 -0
- package/coverage/lcov-report/src/PeerConnectionController/DataChannelStats.ts.html +136 -0
- package/coverage/lcov-report/src/PeerConnectionController/InboundRTPStats.ts.html +547 -0
- package/coverage/lcov-report/src/PeerConnectionController/OutBoundRTPStats.ts.html +163 -0
- package/coverage/lcov-report/src/PeerConnectionController/PeerConnectionController.ts.html +1795 -0
- package/coverage/lcov-report/src/PeerConnectionController/SessionStats.ts.html +115 -0
- package/coverage/lcov-report/src/PeerConnectionController/StreamStats.ts.html +118 -0
- package/coverage/lcov-report/src/PeerConnectionController/index.html +236 -0
- package/coverage/lcov-report/src/PixelStreaming/PixelStreaming.ts.html +2269 -0
- package/coverage/lcov-report/src/PixelStreaming/index.html +116 -0
- package/coverage/lcov-report/src/UI/OnScreenKeyboard.ts.html +376 -0
- package/coverage/lcov-report/src/UI/index.html +116 -0
- package/coverage/lcov-report/src/UeInstanceMessage/ResponseController.ts.html +226 -0
- package/coverage/lcov-report/src/UeInstanceMessage/SendDescriptorController.ts.html +346 -0
- package/coverage/lcov-report/src/UeInstanceMessage/SendMessageController.ts.html +364 -0
- package/coverage/lcov-report/src/UeInstanceMessage/StreamMessageController.ts.html +862 -0
- package/coverage/lcov-report/src/UeInstanceMessage/ToStreamerMessagesController.ts.html +271 -0
- package/coverage/lcov-report/src/UeInstanceMessage/TwoWayMap.ts.html +241 -0
- package/coverage/lcov-report/src/UeInstanceMessage/index.html +191 -0
- package/coverage/lcov-report/src/Util/CoordinateConverter.ts.html +952 -0
- package/coverage/lcov-report/src/Util/EventEmitter.ts.html +1705 -0
- package/coverage/lcov-report/src/Util/EventListenerTracker.ts.html +172 -0
- package/coverage/lcov-report/src/Util/FileUtil.ts.html +505 -0
- package/coverage/lcov-report/src/Util/WebGLUtils.ts.html +232 -0
- package/coverage/lcov-report/src/Util/WebXRUtils.ts.html +160 -0
- package/coverage/lcov-report/src/Util/index.html +191 -0
- package/coverage/lcov-report/src/VideoPlayer/StreamController.ts.html +349 -0
- package/coverage/lcov-report/src/VideoPlayer/VideoPlayer.ts.html +799 -0
- package/coverage/lcov-report/src/VideoPlayer/index.html +131 -0
- package/coverage/lcov-report/src/WebRtcPlayer/WebRtcPlayerController.ts.html +6199 -0
- package/coverage/lcov-report/src/WebRtcPlayer/index.html +116 -0
- package/coverage/lcov-report/src/WebSockets/MessageReceive.ts.html +352 -0
- package/coverage/lcov-report/src/WebSockets/MessageSend.ts.html +604 -0
- package/coverage/lcov-report/src/WebSockets/SignallingProtocol.ts.html +622 -0
- package/coverage/lcov-report/src/WebSockets/WebSocketController.ts.html +844 -0
- package/coverage/lcov-report/src/WebSockets/index.html +161 -0
- package/coverage/lcov-report/src/WebXR/WebXRController.ts.html +1042 -0
- package/coverage/lcov-report/src/WebXR/index.html +116 -0
- package/coverage/lcov-report/src/__test__/index.html +161 -0
- package/coverage/lcov-report/src/__test__/mockMediaStream.ts.html +457 -0
- package/coverage/lcov-report/src/__test__/mockRTCPeerConnection.ts.html +1126 -0
- package/coverage/lcov-report/src/__test__/mockRTCRtpReceiver.ts.html +151 -0
- package/coverage/lcov-report/src/__test__/mockWebSocket.ts.html +475 -0
- package/coverage/lcov-report/src/index.html +116 -0
- package/coverage/lcov-report/src/pixelstreamingfrontend.ts.html +232 -0
- package/coverage/lcov.info +6458 -0
- package/jest.config.js +18 -0
- package/package.json +46 -18
- 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 +909 -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 +300 -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 +318 -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 +253 -0
- package/src/Inputs/XRGamepadController.ts +126 -0
- package/src/Logger/Logger.ts +80 -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 +624 -0
- package/src/PixelStreaming/PixelStreaming.ts +847 -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 +595 -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 +2144 -0
- package/src/WebSockets/MessageReceive.ts +89 -0
- package/src/WebSockets/MessageSend.ts +185 -0
- package/src/WebSockets/SignallingProtocol.ts +180 -0
- package/src/WebSockets/WebSocketController.ts +267 -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 +130 -0
- package/src/pixelstreamingfrontend.ts +50 -0
- package/tsconfig.jest.json +8 -0
- package/tsconfig.json +24 -0
- package/webpack.common.js +35 -0
- package/webpack.dev.js +35 -0
- package/webpack.prod.js +36 -0
- package/yang__yj-pixelstreaming-core-1.0.1.tgz +0 -0
- /package/{library/dist → dist}/lib-pixelstreamingfrontend.esm.js +0 -0
- /package/{library/dist → dist}/lib-pixelstreamingfrontend.js +0 -0
- /package/{library/types → types}/AFK/AFKController.d.ts +0 -0
- /package/{library/types → types}/Config/Config.d.ts +0 -0
- /package/{library/types → types}/Config/SettingBase.d.ts +0 -0
- /package/{library/types → types}/Config/SettingFlag.d.ts +0 -0
- /package/{library/types → types}/Config/SettingNumber.d.ts +0 -0
- /package/{library/types → types}/Config/SettingOption.d.ts +0 -0
- /package/{library/types → types}/Config/SettingText.d.ts +0 -0
- /package/{library/types → types}/DataChannel/DataChannelController.d.ts +0 -0
- /package/{library/types → types}/DataChannel/DataChannelSender.d.ts +0 -0
- /package/{library/types → types}/DataChannel/InitialSettings.d.ts +0 -0
- /package/{library/types → types}/DataChannel/LatencyTestResults.d.ts +0 -0
- /package/{library/types → types}/FreezeFrame/FreezeFrame.d.ts +0 -0
- /package/{library/types → types}/FreezeFrame/FreezeFrameController.d.ts +0 -0
- /package/{library/types → types}/Inputs/FakeTouchController.d.ts +0 -0
- /package/{library/types → types}/Inputs/GamepadController.d.ts +0 -0
- /package/{library/types → types}/Inputs/GamepadTypes.d.ts +0 -0
- /package/{library/types → types}/Inputs/HoveringMouseEvents.d.ts +0 -0
- /package/{library/types → types}/Inputs/IMouseEvents.d.ts +0 -0
- /package/{library/types → types}/Inputs/ITouchController.d.ts +0 -0
- /package/{library/types → types}/Inputs/InputClassesFactory.d.ts +0 -0
- /package/{library/types → types}/Inputs/KeyboardController.d.ts +0 -0
- /package/{library/types → types}/Inputs/LockedMouseEvents.d.ts +0 -0
- /package/{library/types → types}/Inputs/MouseButtons.d.ts +0 -0
- /package/{library/types → types}/Inputs/MouseController.d.ts +0 -0
- /package/{library/types → types}/Inputs/SpecialKeyCodes.d.ts +0 -0
- /package/{library/types → types}/Inputs/TouchController.d.ts +0 -0
- /package/{library/types → types}/Inputs/XRGamepadController.d.ts +0 -0
- /package/{library/types → types}/Logger/Logger.d.ts +0 -0
- /package/{library/types → types}/PeerConnectionController/AggregatedStats.d.ts +0 -0
- /package/{library/types → types}/PeerConnectionController/CandidatePairStats.d.ts +0 -0
- /package/{library/types → types}/PeerConnectionController/CandidateStat.d.ts +0 -0
- /package/{library/types → types}/PeerConnectionController/CodecStats.d.ts +0 -0
- /package/{library/types → types}/PeerConnectionController/DataChannelStats.d.ts +0 -0
- /package/{library/types → types}/PeerConnectionController/InboundRTPStats.d.ts +0 -0
- /package/{library/types → types}/PeerConnectionController/InboundTrackStats.d.ts +0 -0
- /package/{library/types → types}/PeerConnectionController/OutBoundRTPStats.d.ts +0 -0
- /package/{library/types → types}/PeerConnectionController/PeerConnectionController.d.ts +0 -0
- /package/{library/types → types}/PeerConnectionController/SessionStats.d.ts +0 -0
- /package/{library/types → types}/PeerConnectionController/StreamStats.d.ts +0 -0
- /package/{library/types → types}/PixelStreaming/PixelStreaming.d.ts +0 -0
- /package/{library/types → types}/UI/OnScreenKeyboard.d.ts +0 -0
- /package/{library/types → types}/UeInstanceMessage/ResponseController.d.ts +0 -0
- /package/{library/types → types}/UeInstanceMessage/SendDescriptorController.d.ts +0 -0
- /package/{library/types → types}/UeInstanceMessage/SendMessageController.d.ts +0 -0
- /package/{library/types → types}/UeInstanceMessage/StreamMessageController.d.ts +0 -0
- /package/{library/types → types}/UeInstanceMessage/ToStreamerMessagesController.d.ts +0 -0
- /package/{library/types → types}/UeInstanceMessage/TwoWayMap.d.ts +0 -0
- /package/{library/types → types}/Util/CoordinateConverter.d.ts +0 -0
- /package/{library/types → types}/Util/EventEmitter.d.ts +0 -0
- /package/{library/types → types}/Util/EventListenerTracker.d.ts +0 -0
- /package/{library/types → types}/Util/FileUtil.d.ts +0 -0
- /package/{library/types → types}/Util/WebGLUtils.d.ts +0 -0
- /package/{library/types → types}/Util/WebXRUtils.d.ts +0 -0
- /package/{library/types → types}/VideoPlayer/StreamController.d.ts +0 -0
- /package/{library/types → types}/VideoPlayer/VideoPlayer.d.ts +0 -0
- /package/{library/types → types}/WebRtcPlayer/WebRtcPlayerController.d.ts +0 -0
- /package/{library/types → types}/WebSockets/MessageReceive.d.ts +0 -0
- /package/{library/types → types}/WebSockets/MessageSend.d.ts +0 -0
- /package/{library/types → types}/WebSockets/SignallingProtocol.d.ts +0 -0
- /package/{library/types → types}/WebSockets/WebSocketController.d.ts +0 -0
- /package/{library/types → types}/WebXR/WebXRController.d.ts +0 -0
- /package/{library/types → types}/pixelstreamingfrontend.d.ts +0 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
2
|
+
|
|
3
|
+
import { Logger } from '../Logger/Logger';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Utility function for populate file information from byte buffers.
|
|
7
|
+
*/
|
|
8
|
+
export class FileUtil {
|
|
9
|
+
/**
|
|
10
|
+
* Processes a files extension when received over data channel
|
|
11
|
+
* @param view - the file extension data
|
|
12
|
+
*/
|
|
13
|
+
static setExtensionFromBytes(view: Uint8Array, file: FileTemplate) {
|
|
14
|
+
// Reset file if we got a file message and we are not "receiving" it yet
|
|
15
|
+
if (!file.receiving) {
|
|
16
|
+
file.mimetype = '';
|
|
17
|
+
file.extension = '';
|
|
18
|
+
file.receiving = true;
|
|
19
|
+
file.valid = false;
|
|
20
|
+
file.size = 0;
|
|
21
|
+
file.data = [];
|
|
22
|
+
file.timestampStart = new Date().getTime();
|
|
23
|
+
Logger.Log(
|
|
24
|
+
Logger.GetStackTrace(),
|
|
25
|
+
'Received first chunk of file',
|
|
26
|
+
6
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const extensionAsString = new TextDecoder('utf-16').decode(
|
|
31
|
+
view.slice(1)
|
|
32
|
+
);
|
|
33
|
+
Logger.Log(Logger.GetStackTrace(), extensionAsString, 6);
|
|
34
|
+
file.extension = extensionAsString;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Processes a files mime type when received over data channel
|
|
39
|
+
* @param view - the file mime type data
|
|
40
|
+
*/
|
|
41
|
+
static setMimeTypeFromBytes(view: Uint8Array, file: FileTemplate) {
|
|
42
|
+
// Reset file if we got a file message and we are not "receiving" it yet
|
|
43
|
+
if (!file.receiving) {
|
|
44
|
+
file.mimetype = '';
|
|
45
|
+
file.extension = '';
|
|
46
|
+
file.receiving = true;
|
|
47
|
+
file.valid = false;
|
|
48
|
+
file.size = 0;
|
|
49
|
+
file.data = [];
|
|
50
|
+
file.timestampStart = new Date().getTime();
|
|
51
|
+
Logger.Log(
|
|
52
|
+
Logger.GetStackTrace(),
|
|
53
|
+
'Received first chunk of file',
|
|
54
|
+
6
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const mimeAsString = new TextDecoder('utf-16').decode(view.slice(1));
|
|
59
|
+
Logger.Log(Logger.GetStackTrace(), mimeAsString, 6);
|
|
60
|
+
file.mimetype = mimeAsString;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Processes a files contents when received over data channel
|
|
65
|
+
* @param view - the file contents data
|
|
66
|
+
*/
|
|
67
|
+
static setContentsFromBytes(view: Uint8Array, file: FileTemplate) {
|
|
68
|
+
// If we haven't received the initial setup instructions, return
|
|
69
|
+
if (!file.receiving) return;
|
|
70
|
+
|
|
71
|
+
// Extract the total size of the file (across all chunks)
|
|
72
|
+
file.size = Math.ceil(
|
|
73
|
+
new DataView(view.slice(1, 5).buffer).getInt32(0, true) /
|
|
74
|
+
16379 /* The maximum number of payload bits per message*/
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
// Get the file part of the payload
|
|
78
|
+
const fileBytes = view.slice(1 + 4);
|
|
79
|
+
|
|
80
|
+
// Append to existing data that holds the file
|
|
81
|
+
file.data.push(fileBytes);
|
|
82
|
+
|
|
83
|
+
// Uncomment for debug
|
|
84
|
+
Logger.Log(
|
|
85
|
+
Logger.GetStackTrace(),
|
|
86
|
+
`Received file chunk: ${file.data.length}/${file.size}`,
|
|
87
|
+
6
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
if (file.data.length === file.size) {
|
|
91
|
+
file.receiving = false;
|
|
92
|
+
file.valid = true;
|
|
93
|
+
Logger.Log(Logger.GetStackTrace(), 'Received complete file', 6);
|
|
94
|
+
const transferDuration = new Date().getTime() - file.timestampStart;
|
|
95
|
+
const transferBitrate = Math.round(
|
|
96
|
+
(file.size * 16 * 1024) / transferDuration
|
|
97
|
+
);
|
|
98
|
+
Logger.Log(
|
|
99
|
+
Logger.GetStackTrace(),
|
|
100
|
+
`Average transfer bitrate: ${transferBitrate}kb/s over ${
|
|
101
|
+
transferDuration / 1000
|
|
102
|
+
} seconds`,
|
|
103
|
+
6
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
// File reconstruction
|
|
107
|
+
/**
|
|
108
|
+
* Example code to reconstruct the file
|
|
109
|
+
*
|
|
110
|
+
* This code reconstructs the received data into the original file based on the mime type and extension provided and then downloads the reconstructed file
|
|
111
|
+
*/
|
|
112
|
+
const received = new Blob(file.data, { type: file.mimetype });
|
|
113
|
+
const a = document.createElement('a');
|
|
114
|
+
a.setAttribute('href', URL.createObjectURL(received));
|
|
115
|
+
a.setAttribute('download', `transfer.${file.extension}`);
|
|
116
|
+
document.body.append(a);
|
|
117
|
+
// if you are so inclined to make it auto-download, do something like: a.click();
|
|
118
|
+
a.remove();
|
|
119
|
+
} else if (file.data.length > file.size) {
|
|
120
|
+
file.receiving = false;
|
|
121
|
+
Logger.Error(
|
|
122
|
+
Logger.GetStackTrace(),
|
|
123
|
+
`Received bigger file than advertised: ${file.data.length}/${file.size}`
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* A class that represents a template for a downloaded file
|
|
131
|
+
*/
|
|
132
|
+
export class FileTemplate {
|
|
133
|
+
mimetype = '';
|
|
134
|
+
extension = '';
|
|
135
|
+
receiving = false;
|
|
136
|
+
size = 0;
|
|
137
|
+
data: Array<Uint8Array> = [];
|
|
138
|
+
valid = false;
|
|
139
|
+
timestampStart: number;
|
|
140
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export class RTCUtils {
|
|
2
|
+
static isVideoTransciever(transceiver : RTCRtpTransceiver | undefined) : boolean {
|
|
3
|
+
return this.canTransceiverReceiveVideo(transceiver) || this.canTransceiverSendVideo(transceiver);
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
static canTransceiverReceiveVideo(transceiver : RTCRtpTransceiver | undefined) : boolean {
|
|
7
|
+
return !!transceiver &&
|
|
8
|
+
(transceiver.direction === 'sendrecv' || transceiver.direction === 'recvonly') &&
|
|
9
|
+
transceiver.receiver &&
|
|
10
|
+
transceiver.receiver.track &&
|
|
11
|
+
transceiver.receiver.track.kind === 'video';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
static canTransceiverSendVideo(transceiver : RTCRtpTransceiver | undefined) : boolean {
|
|
15
|
+
return !!transceiver &&
|
|
16
|
+
(transceiver.direction === 'sendrecv' || transceiver.direction === 'sendonly') &&
|
|
17
|
+
transceiver.sender &&
|
|
18
|
+
transceiver.sender.track &&
|
|
19
|
+
transceiver.sender.track.kind === 'video';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
static isAudioTransciever(transceiver : RTCRtpTransceiver | undefined) : boolean {
|
|
23
|
+
return this.canTransceiverReceiveAudio(transceiver) || this.canTransceiverSendAudio(transceiver);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
static canTransceiverReceiveAudio(transceiver : RTCRtpTransceiver | undefined) : boolean {
|
|
27
|
+
return !!transceiver &&
|
|
28
|
+
(transceiver.direction === 'sendrecv' || transceiver.direction === 'recvonly') &&
|
|
29
|
+
transceiver.receiver &&
|
|
30
|
+
transceiver.receiver.track &&
|
|
31
|
+
transceiver.receiver.track.kind === 'audio';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
static canTransceiverSendAudio(transceiver : RTCRtpTransceiver | undefined) : boolean {
|
|
35
|
+
return !!transceiver &&
|
|
36
|
+
(transceiver.direction === 'sendrecv' || transceiver.direction === 'sendonly') &&
|
|
37
|
+
transceiver.sender &&
|
|
38
|
+
transceiver.sender.track &&
|
|
39
|
+
transceiver.sender.track.kind === 'audio';
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
2
|
+
|
|
3
|
+
export class WebGLUtils {
|
|
4
|
+
static vertexShader(): string {
|
|
5
|
+
return `
|
|
6
|
+
attribute vec2 a_position;
|
|
7
|
+
attribute vec2 a_texCoord;
|
|
8
|
+
|
|
9
|
+
// input
|
|
10
|
+
uniform vec2 u_resolution;
|
|
11
|
+
uniform vec4 u_offset;
|
|
12
|
+
|
|
13
|
+
//
|
|
14
|
+
varying vec2 v_texCoord;
|
|
15
|
+
|
|
16
|
+
void main() {
|
|
17
|
+
// convert the rectangle from pixels to 0.0 to 1.0
|
|
18
|
+
vec2 zeroToOne = a_position / u_resolution;
|
|
19
|
+
|
|
20
|
+
// convert from 0->1 to 0->2
|
|
21
|
+
vec2 zeroToTwo = zeroToOne * 2.0;
|
|
22
|
+
|
|
23
|
+
// convert from 0->2 to -1->+1 (clipspace)
|
|
24
|
+
vec2 clipSpace = zeroToTwo - 1.0;
|
|
25
|
+
|
|
26
|
+
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
|
|
27
|
+
// pass the texCoord to the fragment shader
|
|
28
|
+
// The GPU will interpolate this value between points.
|
|
29
|
+
v_texCoord = (a_texCoord * u_offset.xy) + u_offset.zw;
|
|
30
|
+
}
|
|
31
|
+
`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
static fragmentShader(): string {
|
|
35
|
+
return `
|
|
36
|
+
precision mediump float;
|
|
37
|
+
|
|
38
|
+
// our texture
|
|
39
|
+
uniform sampler2D u_image;
|
|
40
|
+
|
|
41
|
+
// the texCoords passed in from the vertex shader.
|
|
42
|
+
varying vec2 v_texCoord;
|
|
43
|
+
|
|
44
|
+
void main() {
|
|
45
|
+
gl_FragColor = texture2D(u_image, v_texCoord);
|
|
46
|
+
}
|
|
47
|
+
`;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
2
|
+
|
|
3
|
+
export class WebXRUtils {
|
|
4
|
+
/**
|
|
5
|
+
* Deep copies a gamepad's values by first converting it to a JSON object and then back to a gamepad
|
|
6
|
+
*
|
|
7
|
+
* @param gamepad the original gamepad
|
|
8
|
+
* @returns a new gamepad object, populated with the original gamepads values
|
|
9
|
+
*/
|
|
10
|
+
static deepCopyGamepad(gamepad: Gamepad): Gamepad {
|
|
11
|
+
return JSON.parse(
|
|
12
|
+
JSON.stringify({
|
|
13
|
+
buttons: gamepad.buttons.map((b) =>
|
|
14
|
+
JSON.parse(
|
|
15
|
+
JSON.stringify({
|
|
16
|
+
pressed: b.pressed,
|
|
17
|
+
touched: b.touched
|
|
18
|
+
})
|
|
19
|
+
)
|
|
20
|
+
),
|
|
21
|
+
axes: gamepad.axes
|
|
22
|
+
})
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
2
|
+
|
|
3
|
+
import { MouseController } from '../Inputs/MouseController';
|
|
4
|
+
import { Logger } from '../Logger/Logger';
|
|
5
|
+
import { VideoPlayer } from './VideoPlayer';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 视频播放器控制器处理视频HTML元素和所有处理程序的创建
|
|
9
|
+
*/
|
|
10
|
+
export class StreamController {
|
|
11
|
+
videoElementProvider: VideoPlayer;
|
|
12
|
+
audioElement: HTMLAudioElement;
|
|
13
|
+
mouseController: MouseController;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @param videoElementProvider Video Player instance
|
|
17
|
+
*/
|
|
18
|
+
constructor(videoElementProvider: VideoPlayer) {
|
|
19
|
+
this.videoElementProvider = videoElementProvider;
|
|
20
|
+
this.audioElement = document.createElement('Audio') as HTMLAudioElement;
|
|
21
|
+
this.videoElementProvider.setAudioElement(this.audioElement);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Handles when the Peer connection has a track event
|
|
26
|
+
* @param rtcTrackEvent - RTC Track Event
|
|
27
|
+
*/
|
|
28
|
+
handleOnTrack(rtcTrackEvent: RTCTrackEvent) {
|
|
29
|
+
Logger.Log(
|
|
30
|
+
Logger.GetStackTrace(),
|
|
31
|
+
'handleOnTrack ' + JSON.stringify(rtcTrackEvent.streams),
|
|
32
|
+
6
|
|
33
|
+
);
|
|
34
|
+
const videoElement = this.videoElementProvider.getVideoElement();
|
|
35
|
+
|
|
36
|
+
if (rtcTrackEvent.track) {
|
|
37
|
+
Logger.Log(
|
|
38
|
+
Logger.GetStackTrace(),
|
|
39
|
+
'Got track - ' +
|
|
40
|
+
rtcTrackEvent.track.kind +
|
|
41
|
+
' id=' +
|
|
42
|
+
rtcTrackEvent.track.id +
|
|
43
|
+
' readyState=' +
|
|
44
|
+
rtcTrackEvent.track.readyState,
|
|
45
|
+
6
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (rtcTrackEvent.track.kind == 'audio') {
|
|
50
|
+
this.CreateAudioTrack(rtcTrackEvent.streams[0]);
|
|
51
|
+
return;
|
|
52
|
+
} else if (
|
|
53
|
+
rtcTrackEvent.track.kind == 'video' &&
|
|
54
|
+
videoElement.srcObject !== rtcTrackEvent.streams[0]
|
|
55
|
+
) {
|
|
56
|
+
videoElement.srcObject = rtcTrackEvent.streams[0];
|
|
57
|
+
Logger.Log(
|
|
58
|
+
Logger.GetStackTrace(),
|
|
59
|
+
'Set video source from video track ontrack.'
|
|
60
|
+
);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 当接收到类型为"audio"的RTCTrackEvent时创建音频设备
|
|
67
|
+
* @param audioMediaStream—音频媒体流track
|
|
68
|
+
*/
|
|
69
|
+
CreateAudioTrack(audioMediaStream: MediaStream) {
|
|
70
|
+
const videoElement = this.videoElementProvider.getVideoElement();
|
|
71
|
+
|
|
72
|
+
// 什么都不做视频和音频具有相同的媒体流(它们是链接的)
|
|
73
|
+
if (videoElement.srcObject == audioMediaStream) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
// Video元素有一些与此音轨无关的其他媒体流
|
|
77
|
+
else if (
|
|
78
|
+
videoElement.srcObject &&
|
|
79
|
+
videoElement.srcObject !== audioMediaStream
|
|
80
|
+
) {
|
|
81
|
+
// create a new audio element
|
|
82
|
+
this.audioElement.srcObject = audioMediaStream;
|
|
83
|
+
Logger.Log(
|
|
84
|
+
Logger.GetStackTrace(),
|
|
85
|
+
'Created new audio element to play separate audio stream.'
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
2
|
+
|
|
3
|
+
import { Config, Flags } from '../Config/Config';
|
|
4
|
+
import { Logger } from '../Logger/Logger';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Extra types for the HTMLElement
|
|
8
|
+
*/
|
|
9
|
+
declare global {
|
|
10
|
+
interface HTMLElement {
|
|
11
|
+
mozRequestPointerLock?(): void;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The video player html element
|
|
17
|
+
*/
|
|
18
|
+
export class VideoPlayer {
|
|
19
|
+
private config: Config;
|
|
20
|
+
private videoElement: HTMLVideoElement;
|
|
21
|
+
private audioElement?: HTMLAudioElement;
|
|
22
|
+
private orientationChangeTimeout: number;
|
|
23
|
+
private lastTimeResized = new Date().getTime();
|
|
24
|
+
|
|
25
|
+
onMatchViewportResolutionCallback: (width: number, height: number) => void;
|
|
26
|
+
onResizePlayerCallback: () => void;
|
|
27
|
+
resizeTimeoutHandle: number;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @param videoElementParent the html div the the video player will be injected into
|
|
31
|
+
* @param config the applications configuration. We're interested in the startVideoMuted flag
|
|
32
|
+
*/
|
|
33
|
+
constructor(videoElementParent: HTMLElement, config: Config) {
|
|
34
|
+
this.videoElement = document.createElement('video');
|
|
35
|
+
this.config = config;
|
|
36
|
+
this.videoElement.id = 'streamingVideo';
|
|
37
|
+
this.videoElement.disablePictureInPicture = true;
|
|
38
|
+
this.videoElement.playsInline = true;
|
|
39
|
+
this.videoElement.style.width = '100%';
|
|
40
|
+
this.videoElement.style.height = '100%';
|
|
41
|
+
this.videoElement.style.position = 'absolute';
|
|
42
|
+
this.videoElement.style.pointerEvents = 'all';
|
|
43
|
+
videoElementParent.appendChild(this.videoElement);
|
|
44
|
+
|
|
45
|
+
this.onResizePlayerCallback = () => {
|
|
46
|
+
console.log(
|
|
47
|
+
'Resolution changed, restyling player, did you forget to override this function?'
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
this.onMatchViewportResolutionCallback = () => {
|
|
51
|
+
console.log(
|
|
52
|
+
'Resolution changed and match viewport resolution is turned on, did you forget to override this function?'
|
|
53
|
+
);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// set play for video (and audio)
|
|
57
|
+
this.videoElement.onclick = () => {
|
|
58
|
+
if (this.audioElement != undefined && this.audioElement.paused) {
|
|
59
|
+
this.audioElement.play();
|
|
60
|
+
}
|
|
61
|
+
if (this.videoElement.paused) {
|
|
62
|
+
this.videoElement.play();
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
this.videoElement.onloadedmetadata = () => {
|
|
67
|
+
this.onVideoInitialized();
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// set resize events to the windows if it is resized or its orientation is changed
|
|
71
|
+
window.addEventListener('resize', () => this.resizePlayerStyle(), true);
|
|
72
|
+
window.addEventListener('orientationchange', () =>
|
|
73
|
+
this.onOrientationChange()
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
public setAudioElement(audioElement: HTMLAudioElement) : void {
|
|
78
|
+
this.audioElement = audioElement;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Sets up the video element with any application config and plays the video element.
|
|
83
|
+
* @returns A promise for if playing the video was successful or not.
|
|
84
|
+
*/
|
|
85
|
+
play(): Promise<void> {
|
|
86
|
+
this.videoElement.muted = this.config.isFlagEnabled(
|
|
87
|
+
Flags.StartVideoMuted
|
|
88
|
+
);
|
|
89
|
+
this.videoElement.autoplay = this.config.isFlagEnabled(
|
|
90
|
+
Flags.AutoPlayVideo
|
|
91
|
+
);
|
|
92
|
+
return this.videoElement.play();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* @returns True if the video element is paused.
|
|
97
|
+
*/
|
|
98
|
+
isPaused(): boolean {
|
|
99
|
+
return this.videoElement.paused;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* @returns - whether the video element is playing.
|
|
104
|
+
*/
|
|
105
|
+
isVideoReady(): boolean {
|
|
106
|
+
return (
|
|
107
|
+
this.videoElement.readyState !== undefined &&
|
|
108
|
+
this.videoElement.readyState > 0
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* @returns True if the video element has a valid video source (srcObject).
|
|
114
|
+
*/
|
|
115
|
+
hasVideoSource(): boolean {
|
|
116
|
+
return (
|
|
117
|
+
this.videoElement.srcObject !== undefined &&
|
|
118
|
+
this.videoElement.srcObject !== null
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Get the current context of the html video element
|
|
124
|
+
* @returns - the current context of the video element
|
|
125
|
+
*/
|
|
126
|
+
getVideoElement(): HTMLVideoElement {
|
|
127
|
+
return this.videoElement;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Get the current context of the html video elements parent
|
|
132
|
+
* @returns - the current context of the video elements parent
|
|
133
|
+
*/
|
|
134
|
+
getVideoParentElement(): HTMLElement {
|
|
135
|
+
return this.videoElement.parentElement;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Set the Video Elements src object tracks to enable
|
|
140
|
+
* @param enabled - Enable Tracks on the Src Object
|
|
141
|
+
*/
|
|
142
|
+
setVideoEnabled(enabled: boolean) {
|
|
143
|
+
// this is a temporary hack until type scripts video element is updated to reflect the need for tracks on a html video element
|
|
144
|
+
const videoElement = this.videoElement;
|
|
145
|
+
(<MediaStream>videoElement.srcObject)
|
|
146
|
+
.getTracks()
|
|
147
|
+
.forEach((track: MediaStreamTrack) => (track.enabled = enabled));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* An override for when the video has been initialized with a srcObject
|
|
152
|
+
*/
|
|
153
|
+
onVideoInitialized() {
|
|
154
|
+
// Default Functionality: Do Nothing
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* On the orientation change of a window clear the timeout
|
|
159
|
+
*/
|
|
160
|
+
onOrientationChange() {
|
|
161
|
+
clearTimeout(this.orientationChangeTimeout);
|
|
162
|
+
this.orientationChangeTimeout = window.setTimeout(() => {
|
|
163
|
+
this.resizePlayerStyle();
|
|
164
|
+
}, 500);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Resizes the player style based on the window height and width
|
|
169
|
+
* @returns - nil if requirements are satisfied
|
|
170
|
+
*/
|
|
171
|
+
resizePlayerStyle() {
|
|
172
|
+
const videoElementParent = this.getVideoParentElement();
|
|
173
|
+
|
|
174
|
+
if (!videoElementParent) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
this.updateVideoStreamSize();
|
|
179
|
+
|
|
180
|
+
if (videoElementParent.classList.contains('fixed-size')) {
|
|
181
|
+
this.onResizePlayerCallback();
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// controls for resizing the player
|
|
186
|
+
this.resizePlayerStyleToFillParentElement();
|
|
187
|
+
this.onResizePlayerCallback();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Resizes the player element to fill the parent element
|
|
192
|
+
*/
|
|
193
|
+
resizePlayerStyleToFillParentElement() {
|
|
194
|
+
const videoElementParent = this.getVideoParentElement();
|
|
195
|
+
|
|
196
|
+
//Video is not initialized yet so set videoElementParent to size of parent element
|
|
197
|
+
const styleWidth = '100%';
|
|
198
|
+
const styleHeight = '100%';
|
|
199
|
+
const styleTop = 0;
|
|
200
|
+
const styleLeft = 0;
|
|
201
|
+
videoElementParent.setAttribute(
|
|
202
|
+
'style',
|
|
203
|
+
'top: ' +
|
|
204
|
+
styleTop +
|
|
205
|
+
'px; left: ' +
|
|
206
|
+
styleLeft +
|
|
207
|
+
'px; width: ' +
|
|
208
|
+
styleWidth +
|
|
209
|
+
'; height: ' +
|
|
210
|
+
styleHeight +
|
|
211
|
+
'; cursor: default;'
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
updateVideoStreamSize() {
|
|
216
|
+
if (!this.config.isFlagEnabled(Flags.MatchViewportResolution)) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const now = new Date().getTime();
|
|
221
|
+
if (now - this.lastTimeResized > 300) {
|
|
222
|
+
const videoElementParent = this.getVideoParentElement();
|
|
223
|
+
if (!videoElementParent) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
this.onMatchViewportResolutionCallback(
|
|
228
|
+
videoElementParent.clientWidth,
|
|
229
|
+
videoElementParent.clientHeight
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
this.lastTimeResized = new Date().getTime();
|
|
233
|
+
} else {
|
|
234
|
+
Logger.Log(
|
|
235
|
+
Logger.GetStackTrace(),
|
|
236
|
+
'Resizing too often - skipping',
|
|
237
|
+
6
|
|
238
|
+
);
|
|
239
|
+
clearTimeout(this.resizeTimeoutHandle);
|
|
240
|
+
this.resizeTimeoutHandle = window.setTimeout(
|
|
241
|
+
() => this.updateVideoStreamSize(),
|
|
242
|
+
100
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|