@yang__yj/pixelstreaming-core 1.0.0 → 1.0.2

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 (243) hide show
  1. package/.cspell.json +48 -0
  2. package/.eslintignore +8 -0
  3. package/.eslintrc.js +8 -0
  4. package/.prettierignore +0 -0
  5. package/.prettierrc.json +6 -0
  6. package/coverage/clover.xml +3480 -0
  7. package/coverage/coverage-final.json +62 -0
  8. package/coverage/lcov-report/base.css +224 -0
  9. package/coverage/lcov-report/block-navigation.js +87 -0
  10. package/coverage/lcov-report/favicon.png +0 -0
  11. package/coverage/lcov-report/index.html +356 -0
  12. package/coverage/lcov-report/prettify.css +1 -0
  13. package/coverage/lcov-report/prettify.js +2 -0
  14. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  15. package/coverage/lcov-report/sorter.js +196 -0
  16. package/coverage/lcov-report/src/AFK/AFKController.ts.html +559 -0
  17. package/coverage/lcov-report/src/AFK/index.html +116 -0
  18. package/coverage/lcov-report/src/Config/Config.ts.html +2764 -0
  19. package/coverage/lcov-report/src/Config/SettingBase.ts.html +280 -0
  20. package/coverage/lcov-report/src/Config/SettingFlag.ts.html +382 -0
  21. package/coverage/lcov-report/src/Config/SettingNumber.ts.html +418 -0
  22. package/coverage/lcov-report/src/Config/SettingOption.ts.html +424 -0
  23. package/coverage/lcov-report/src/Config/SettingText.ts.html +331 -0
  24. package/coverage/lcov-report/src/Config/index.html +191 -0
  25. package/coverage/lcov-report/src/DataChannel/DataChannelController.ts.html +499 -0
  26. package/coverage/lcov-report/src/DataChannel/DataChannelSender.ts.html +262 -0
  27. package/coverage/lcov-report/src/DataChannel/InitialSettings.ts.html +268 -0
  28. package/coverage/lcov-report/src/DataChannel/LatencyTestResults.ts.html +313 -0
  29. package/coverage/lcov-report/src/DataChannel/index.html +161 -0
  30. package/coverage/lcov-report/src/FreezeFrame/FreezeFrame.ts.html +427 -0
  31. package/coverage/lcov-report/src/FreezeFrame/FreezeFrameController.ts.html +427 -0
  32. package/coverage/lcov-report/src/FreezeFrame/index.html +131 -0
  33. package/coverage/lcov-report/src/Inputs/FakeTouchController.ts.html +682 -0
  34. package/coverage/lcov-report/src/Inputs/GamepadController.ts.html +985 -0
  35. package/coverage/lcov-report/src/Inputs/HoveringMouseEvents.ts.html +688 -0
  36. package/coverage/lcov-report/src/Inputs/InputClassesFactory.ts.html +505 -0
  37. package/coverage/lcov-report/src/Inputs/KeyboardController.ts.html +1048 -0
  38. package/coverage/lcov-report/src/Inputs/LockedMouseEvents.ts.html +946 -0
  39. package/coverage/lcov-report/src/Inputs/MouseButtons.ts.html +160 -0
  40. package/coverage/lcov-report/src/Inputs/MouseController.ts.html +1171 -0
  41. package/coverage/lcov-report/src/Inputs/SpecialKeyCodes.ts.html +133 -0
  42. package/coverage/lcov-report/src/Inputs/TouchController.ts.html +712 -0
  43. package/coverage/lcov-report/src/Inputs/XRGamepadController.ts.html +463 -0
  44. package/coverage/lcov-report/src/Inputs/index.html +266 -0
  45. package/coverage/lcov-report/src/Logger/Logger.ts.html +325 -0
  46. package/coverage/lcov-report/src/Logger/index.html +116 -0
  47. package/coverage/lcov-report/src/PeerConnectionController/AggregatedStats.ts.html +1018 -0
  48. package/coverage/lcov-report/src/PeerConnectionController/CandidatePairStats.ts.html +136 -0
  49. package/coverage/lcov-report/src/PeerConnectionController/CandidateStat.ts.html +124 -0
  50. package/coverage/lcov-report/src/PeerConnectionController/DataChannelStats.ts.html +136 -0
  51. package/coverage/lcov-report/src/PeerConnectionController/InboundRTPStats.ts.html +547 -0
  52. package/coverage/lcov-report/src/PeerConnectionController/OutBoundRTPStats.ts.html +163 -0
  53. package/coverage/lcov-report/src/PeerConnectionController/PeerConnectionController.ts.html +1795 -0
  54. package/coverage/lcov-report/src/PeerConnectionController/SessionStats.ts.html +115 -0
  55. package/coverage/lcov-report/src/PeerConnectionController/StreamStats.ts.html +118 -0
  56. package/coverage/lcov-report/src/PeerConnectionController/index.html +236 -0
  57. package/coverage/lcov-report/src/PixelStreaming/PixelStreaming.ts.html +2269 -0
  58. package/coverage/lcov-report/src/PixelStreaming/index.html +116 -0
  59. package/coverage/lcov-report/src/UI/OnScreenKeyboard.ts.html +376 -0
  60. package/coverage/lcov-report/src/UI/index.html +116 -0
  61. package/coverage/lcov-report/src/UeInstanceMessage/ResponseController.ts.html +226 -0
  62. package/coverage/lcov-report/src/UeInstanceMessage/SendDescriptorController.ts.html +346 -0
  63. package/coverage/lcov-report/src/UeInstanceMessage/SendMessageController.ts.html +364 -0
  64. package/coverage/lcov-report/src/UeInstanceMessage/StreamMessageController.ts.html +862 -0
  65. package/coverage/lcov-report/src/UeInstanceMessage/ToStreamerMessagesController.ts.html +271 -0
  66. package/coverage/lcov-report/src/UeInstanceMessage/TwoWayMap.ts.html +241 -0
  67. package/coverage/lcov-report/src/UeInstanceMessage/index.html +191 -0
  68. package/coverage/lcov-report/src/Util/CoordinateConverter.ts.html +952 -0
  69. package/coverage/lcov-report/src/Util/EventEmitter.ts.html +1705 -0
  70. package/coverage/lcov-report/src/Util/EventListenerTracker.ts.html +172 -0
  71. package/coverage/lcov-report/src/Util/FileUtil.ts.html +505 -0
  72. package/coverage/lcov-report/src/Util/WebGLUtils.ts.html +232 -0
  73. package/coverage/lcov-report/src/Util/WebXRUtils.ts.html +160 -0
  74. package/coverage/lcov-report/src/Util/index.html +191 -0
  75. package/coverage/lcov-report/src/VideoPlayer/StreamController.ts.html +349 -0
  76. package/coverage/lcov-report/src/VideoPlayer/VideoPlayer.ts.html +799 -0
  77. package/coverage/lcov-report/src/VideoPlayer/index.html +131 -0
  78. package/coverage/lcov-report/src/WebRtcPlayer/WebRtcPlayerController.ts.html +6199 -0
  79. package/coverage/lcov-report/src/WebRtcPlayer/index.html +116 -0
  80. package/coverage/lcov-report/src/WebSockets/MessageReceive.ts.html +352 -0
  81. package/coverage/lcov-report/src/WebSockets/MessageSend.ts.html +604 -0
  82. package/coverage/lcov-report/src/WebSockets/SignallingProtocol.ts.html +622 -0
  83. package/coverage/lcov-report/src/WebSockets/WebSocketController.ts.html +844 -0
  84. package/coverage/lcov-report/src/WebSockets/index.html +161 -0
  85. package/coverage/lcov-report/src/WebXR/WebXRController.ts.html +1042 -0
  86. package/coverage/lcov-report/src/WebXR/index.html +116 -0
  87. package/coverage/lcov-report/src/__test__/index.html +161 -0
  88. package/coverage/lcov-report/src/__test__/mockMediaStream.ts.html +457 -0
  89. package/coverage/lcov-report/src/__test__/mockRTCPeerConnection.ts.html +1126 -0
  90. package/coverage/lcov-report/src/__test__/mockRTCRtpReceiver.ts.html +151 -0
  91. package/coverage/lcov-report/src/__test__/mockWebSocket.ts.html +475 -0
  92. package/coverage/lcov-report/src/index.html +116 -0
  93. package/coverage/lcov-report/src/pixelstreamingfrontend.ts.html +232 -0
  94. package/coverage/lcov.info +6458 -0
  95. package/dist/lib-pixelstreamingfrontend.esm.js +1 -0
  96. package/dist/lib-pixelstreamingfrontend.js +1 -0
  97. package/jest.config.js +18 -0
  98. package/package.json +47 -18
  99. package/readme.md +15 -0
  100. package/src/AFK/AFKController.test.ts +162 -0
  101. package/src/AFK/AFKController.ts +158 -0
  102. package/src/Config/Config.test.ts +222 -0
  103. package/src/Config/Config.ts +909 -0
  104. package/src/Config/SettingBase.ts +65 -0
  105. package/src/Config/SettingFlag.ts +99 -0
  106. package/src/Config/SettingNumber.ts +111 -0
  107. package/src/Config/SettingOption.ts +124 -0
  108. package/src/Config/SettingText.ts +82 -0
  109. package/src/DataChannel/DataChannelController.ts +138 -0
  110. package/src/DataChannel/DataChannelLatencyTestController.ts +129 -0
  111. package/src/DataChannel/DataChannelLatencyTestResults.ts +67 -0
  112. package/src/DataChannel/DataChannelSender.ts +59 -0
  113. package/src/DataChannel/InitialSettings.ts +61 -0
  114. package/src/DataChannel/LatencyTestResults.ts +76 -0
  115. package/src/FreezeFrame/FreezeFrame.ts +114 -0
  116. package/src/FreezeFrame/FreezeFrameController.ts +114 -0
  117. package/src/Inputs/FakeTouchController.ts +199 -0
  118. package/src/Inputs/GamepadController.ts +300 -0
  119. package/src/Inputs/GamepadTypes.ts +10 -0
  120. package/src/Inputs/HoveringMouseEvents.ts +192 -0
  121. package/src/Inputs/IMouseEvents.ts +64 -0
  122. package/src/Inputs/ITouchController.ts +29 -0
  123. package/src/Inputs/InputClassesFactory.ts +140 -0
  124. package/src/Inputs/KeyboardController.ts +318 -0
  125. package/src/Inputs/LockedMouseEvents.ts +287 -0
  126. package/src/Inputs/MouseButtons.ts +25 -0
  127. package/src/Inputs/MouseController.ts +362 -0
  128. package/src/Inputs/SpecialKeyCodes.ts +16 -0
  129. package/src/Inputs/TouchController.ts +253 -0
  130. package/src/Inputs/XRGamepadController.ts +126 -0
  131. package/src/Logger/Logger.ts +80 -0
  132. package/src/PeerConnectionController/AggregatedStats.ts +311 -0
  133. package/src/PeerConnectionController/CandidatePairStats.ts +17 -0
  134. package/src/PeerConnectionController/CandidateStat.ts +13 -0
  135. package/src/PeerConnectionController/CodecStats.ts +19 -0
  136. package/src/PeerConnectionController/DataChannelStats.ts +17 -0
  137. package/src/PeerConnectionController/InboundRTPStats.ts +154 -0
  138. package/src/PeerConnectionController/InboundTrackStats.ts +34 -0
  139. package/src/PeerConnectionController/OutBoundRTPStats.ts +26 -0
  140. package/src/PeerConnectionController/PeerConnectionController.ts +563 -0
  141. package/src/PeerConnectionController/SessionStats.ts +10 -0
  142. package/src/PeerConnectionController/StreamStats.ts +11 -0
  143. package/src/PixelStreaming/PixelStreaming.test.ts +624 -0
  144. package/src/PixelStreaming/PixelStreaming.ts +847 -0
  145. package/src/UI/OnScreenKeyboard.ts +97 -0
  146. package/src/UeInstanceMessage/ResponseController.ts +47 -0
  147. package/src/UeInstanceMessage/SendMessageController.ts +154 -0
  148. package/src/UeInstanceMessage/StreamMessageController.ts +233 -0
  149. package/src/UeInstanceMessage/ToStreamerMessagesController.ts +62 -0
  150. package/src/Util/CoordinateConverter.ts +289 -0
  151. package/src/Util/EventEmitter.ts +595 -0
  152. package/src/Util/EventListenerTracker.ts +29 -0
  153. package/src/Util/FileUtil.ts +140 -0
  154. package/src/Util/RTCUtils.ts +41 -0
  155. package/src/Util/WebGLUtils.ts +49 -0
  156. package/src/Util/WebXRUtils.ts +25 -0
  157. package/src/VideoPlayer/StreamController.ts +89 -0
  158. package/src/VideoPlayer/VideoPlayer.ts +246 -0
  159. package/src/WebRtcPlayer/WebRtcPlayerController.ts +2221 -0
  160. package/src/WebSockets/MessageReceive.ts +89 -0
  161. package/src/WebSockets/MessageSend.ts +185 -0
  162. package/src/WebSockets/SignallingProtocol.ts +180 -0
  163. package/src/WebSockets/WebSocketController.ts +267 -0
  164. package/src/WebXR/WebXRController.ts +319 -0
  165. package/src/__test__/mockMediaStream.ts +124 -0
  166. package/src/__test__/mockRTCPeerConnection.ts +347 -0
  167. package/src/__test__/mockRTCRtpReceiver.ts +22 -0
  168. package/src/__test__/mockWebSocket.ts +130 -0
  169. package/src/pixelstreamingfrontend.ts +50 -0
  170. package/tsconfig.jest.json +8 -0
  171. package/tsconfig.json +24 -0
  172. package/{library/types → types}/Config/Config.d.ts +5 -4
  173. package/types/DataChannel/DataChannelLatencyTestController.d.ts +26 -0
  174. package/types/DataChannel/DataChannelLatencyTestResults.d.ts +46 -0
  175. package/{library/types → types}/Inputs/HoveringMouseEvents.d.ts +1 -1
  176. package/{library/types → types}/PeerConnectionController/PeerConnectionController.d.ts +1 -1
  177. package/{library/types → types}/PixelStreaming/PixelStreaming.d.ts +31 -6
  178. package/{library/types → types}/UeInstanceMessage/SendMessageController.d.ts +1 -1
  179. package/{library/types → types}/UeInstanceMessage/StreamMessageController.d.ts +3 -5
  180. package/{library/types → types}/Util/EventEmitter.d.ts +40 -3
  181. package/types/Util/RTCUtils.d.ts +8 -0
  182. package/{library/types → types}/VideoPlayer/StreamController.d.ts +3 -3
  183. package/{library/types → types}/VideoPlayer/VideoPlayer.d.ts +2 -0
  184. package/{library/types → types}/WebRtcPlayer/WebRtcPlayerController.d.ts +30 -27
  185. package/{library/types → types}/WebSockets/MessageReceive.d.ts +9 -9
  186. package/{library/types → types}/WebSockets/MessageSend.d.ts +13 -9
  187. package/{library/types → types}/WebSockets/SignallingProtocol.d.ts +3 -3
  188. package/{library/types → types}/WebSockets/WebSocketController.d.ts +16 -12
  189. package/{library/types → types}/pixelstreamingfrontend.d.ts +2 -1
  190. package/webpack.common.js +35 -0
  191. package/webpack.dev.js +35 -0
  192. package/webpack.prod.js +36 -0
  193. package/yang__yj-pixelstreaming-core-1.0.1.tgz +0 -0
  194. package/yang__yj-pixelstreaming-core-1.0.2.tgz +0 -0
  195. package/library/dist/lib-pixelstreamingfrontend.esm.js +0 -1
  196. package/library/dist/lib-pixelstreamingfrontend.js +0 -1
  197. /package/{library/types → types}/AFK/AFKController.d.ts +0 -0
  198. /package/{library/types → types}/Config/SettingBase.d.ts +0 -0
  199. /package/{library/types → types}/Config/SettingFlag.d.ts +0 -0
  200. /package/{library/types → types}/Config/SettingNumber.d.ts +0 -0
  201. /package/{library/types → types}/Config/SettingOption.d.ts +0 -0
  202. /package/{library/types → types}/Config/SettingText.d.ts +0 -0
  203. /package/{library/types → types}/DataChannel/DataChannelController.d.ts +0 -0
  204. /package/{library/types → types}/DataChannel/DataChannelSender.d.ts +0 -0
  205. /package/{library/types → types}/DataChannel/InitialSettings.d.ts +0 -0
  206. /package/{library/types → types}/DataChannel/LatencyTestResults.d.ts +0 -0
  207. /package/{library/types → types}/FreezeFrame/FreezeFrame.d.ts +0 -0
  208. /package/{library/types → types}/FreezeFrame/FreezeFrameController.d.ts +0 -0
  209. /package/{library/types → types}/Inputs/FakeTouchController.d.ts +0 -0
  210. /package/{library/types → types}/Inputs/GamepadController.d.ts +0 -0
  211. /package/{library/types → types}/Inputs/GamepadTypes.d.ts +0 -0
  212. /package/{library/types → types}/Inputs/IMouseEvents.d.ts +0 -0
  213. /package/{library/types → types}/Inputs/ITouchController.d.ts +0 -0
  214. /package/{library/types → types}/Inputs/InputClassesFactory.d.ts +0 -0
  215. /package/{library/types → types}/Inputs/KeyboardController.d.ts +0 -0
  216. /package/{library/types → types}/Inputs/LockedMouseEvents.d.ts +0 -0
  217. /package/{library/types → types}/Inputs/MouseButtons.d.ts +0 -0
  218. /package/{library/types → types}/Inputs/MouseController.d.ts +0 -0
  219. /package/{library/types → types}/Inputs/SpecialKeyCodes.d.ts +0 -0
  220. /package/{library/types → types}/Inputs/TouchController.d.ts +0 -0
  221. /package/{library/types → types}/Inputs/XRGamepadController.d.ts +0 -0
  222. /package/{library/types → types}/Logger/Logger.d.ts +0 -0
  223. /package/{library/types → types}/PeerConnectionController/AggregatedStats.d.ts +0 -0
  224. /package/{library/types → types}/PeerConnectionController/CandidatePairStats.d.ts +0 -0
  225. /package/{library/types → types}/PeerConnectionController/CandidateStat.d.ts +0 -0
  226. /package/{library/types → types}/PeerConnectionController/CodecStats.d.ts +0 -0
  227. /package/{library/types → types}/PeerConnectionController/DataChannelStats.d.ts +0 -0
  228. /package/{library/types → types}/PeerConnectionController/InboundRTPStats.d.ts +0 -0
  229. /package/{library/types → types}/PeerConnectionController/InboundTrackStats.d.ts +0 -0
  230. /package/{library/types → types}/PeerConnectionController/OutBoundRTPStats.d.ts +0 -0
  231. /package/{library/types → types}/PeerConnectionController/SessionStats.d.ts +0 -0
  232. /package/{library/types → types}/PeerConnectionController/StreamStats.d.ts +0 -0
  233. /package/{library/types → types}/UI/OnScreenKeyboard.d.ts +0 -0
  234. /package/{library/types → types}/UeInstanceMessage/ResponseController.d.ts +0 -0
  235. /package/{library/types → types}/UeInstanceMessage/SendDescriptorController.d.ts +0 -0
  236. /package/{library/types → types}/UeInstanceMessage/ToStreamerMessagesController.d.ts +0 -0
  237. /package/{library/types → types}/UeInstanceMessage/TwoWayMap.d.ts +0 -0
  238. /package/{library/types → types}/Util/CoordinateConverter.d.ts +0 -0
  239. /package/{library/types → types}/Util/EventListenerTracker.d.ts +0 -0
  240. /package/{library/types → types}/Util/FileUtil.d.ts +0 -0
  241. /package/{library/types → types}/Util/WebGLUtils.d.ts +0 -0
  242. /package/{library/types → types}/Util/WebXRUtils.d.ts +0 -0
  243. /package/{library/types → types}/WebXR/WebXRController.d.ts +0 -0
@@ -0,0 +1,2221 @@
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+ import { ElMessage, ElMessageBox } from 'element-plus';
4
+ import { WebSocketController } from '../WebSockets/WebSocketController';
5
+ import { StreamController } from '../VideoPlayer/StreamController';
6
+ import {
7
+ MessageAnswer,
8
+ MessageOffer,
9
+ MessageConfig,
10
+ MessageStreamerList
11
+ } from '../WebSockets/MessageReceive';
12
+ import { FreezeFrameController } from '../FreezeFrame/FreezeFrameController';
13
+ import { AFKController } from '../AFK/AFKController';
14
+ import { DataChannelController } from '../DataChannel/DataChannelController';
15
+ import { PeerConnectionController } from '../PeerConnectionController/PeerConnectionController';
16
+ import { KeyboardController } from '../Inputs/KeyboardController';
17
+ import { AggregatedStats } from '../PeerConnectionController/AggregatedStats';
18
+ import {
19
+ Config,
20
+ Flags,
21
+ ControlSchemeType,
22
+ TextParameters,
23
+ OptionParameters,
24
+ NumericParameters
25
+ } from '../Config/Config';
26
+ import {
27
+ EncoderSettings,
28
+ InitialSettings,
29
+ WebRTCSettings
30
+ } from '../DataChannel/InitialSettings';
31
+ import { LatencyTestResults } from '../DataChannel/LatencyTestResults';
32
+ import { Logger } from '../Logger/Logger';
33
+ import { FileTemplate, FileUtil } from '../Util/FileUtil';
34
+ import { InputClassesFactory } from '../Inputs/InputClassesFactory';
35
+ import { VideoPlayer } from '../VideoPlayer/VideoPlayer';
36
+ import {
37
+ StreamMessageController,
38
+ MessageDirection
39
+ } from '../UeInstanceMessage/StreamMessageController';
40
+ import { ResponseController } from '../UeInstanceMessage/ResponseController';
41
+ import * as MessageReceive from '../WebSockets/MessageReceive';
42
+ import { MessageOnScreenKeyboard } from '../WebSockets/MessageReceive';
43
+ import { SendMessageController } from '../UeInstanceMessage/SendMessageController';
44
+ import { ToStreamerMessagesController } from '../UeInstanceMessage/ToStreamerMessagesController';
45
+ import { MouseController } from '../Inputs/MouseController';
46
+ import { GamePadController } from '../Inputs/GamepadController';
47
+ import { DataChannelSender } from '../DataChannel/DataChannelSender';
48
+ import {
49
+ CoordinateConverter,
50
+ UnquantizedDenormalizedUnsignedCoord
51
+ } from '../Util/CoordinateConverter';
52
+ import { PixelStreaming } from '../PixelStreaming/PixelStreaming';
53
+ import { ITouchController } from '../Inputs/ITouchController';
54
+ import {
55
+ DataChannelCloseEvent,
56
+ DataChannelErrorEvent,
57
+ DataChannelOpenEvent,
58
+ HideFreezeFrameEvent,
59
+ LoadFreezeFrameEvent,
60
+ PlayStreamErrorEvent,
61
+ PlayStreamEvent,
62
+ PlayStreamRejectedEvent,
63
+ StreamerListMessageEvent
64
+ } from '../Util/EventEmitter';
65
+ import {
66
+ DataChannelLatencyTestRequest,
67
+ DataChannelLatencyTestResponse
68
+ } from '../DataChannel/DataChannelLatencyTestResults';
69
+ /**
70
+ * Entry point for the WebRTC Player
71
+ */
72
+ export class WebRtcPlayerController {
73
+ config: Config;
74
+ responseController: ResponseController;
75
+ sdpConstraints: RTCOfferOptions;
76
+ webSocketController: WebSocketController;
77
+ // The primary data channel. This is bidirectional when p2p and send only when using an SFU
78
+ sendrecvDataChannelController: DataChannelController;
79
+ // A recv only data channel required when using an SFU
80
+ recvDataChannelController: DataChannelController;
81
+ dataChannelSender: DataChannelSender;
82
+ datachannelOptions: RTCDataChannelInit;
83
+ videoPlayer: VideoPlayer;
84
+ streamController: StreamController;
85
+ peerConnectionController: PeerConnectionController;
86
+ inputClassesFactory: InputClassesFactory;
87
+ freezeFrameController: FreezeFrameController;
88
+ shouldShowPlayOverlay = true;
89
+ afkController: AFKController;
90
+ videoElementParentClientRect: DOMRect;
91
+ latencyStartTime: number;
92
+ pixelStreaming: PixelStreaming;
93
+ streamMessageController: StreamMessageController;
94
+ sendMessageController: SendMessageController;
95
+ toStreamerMessagesController: ToStreamerMessagesController;
96
+ keyboardController: KeyboardController;
97
+ mouseController: MouseController;
98
+ touchController: ITouchController;
99
+ gamePadController: GamePadController;
100
+ coordinateConverter: CoordinateConverter;
101
+ isUsingSFU: boolean;
102
+ isQualityController: boolean;
103
+ statsTimerHandle: number;
104
+ file: FileTemplate;
105
+ preferredCodec: string;
106
+ peerConfig: RTCConfiguration;
107
+ videoAvgQp: number;
108
+ locallyClosed: boolean;
109
+ shouldReconnect: boolean;
110
+ isReconnecting: boolean;
111
+ reconnectAttempt: number;
112
+ disconnectMessage: string;
113
+ subscribedStream: string;
114
+ signallingUrlBuilder: () => string;
115
+ autoJoinTimer: ReturnType<typeof setTimeout> = undefined;
116
+
117
+ /**
118
+ *
119
+ * @param config - the frontend config object
120
+ * @param pixelStreaming - the PixelStreaming object
121
+ */
122
+ constructor(config: Config, pixelStreaming: PixelStreaming) {
123
+ this.config = config;
124
+ this.pixelStreaming = pixelStreaming;
125
+ this.responseController = new ResponseController();
126
+ this.file = new FileTemplate();
127
+
128
+ this.sdpConstraints = {
129
+ offerToReceiveAudio: true,
130
+ offerToReceiveVideo: true
131
+ };
132
+
133
+ // set up the afk logic class and connect up its method for closing the signaling server
134
+ this.afkController = new AFKController(
135
+ this.config,
136
+ this.pixelStreaming,
137
+ this.onAfkTriggered.bind(this)
138
+ );
139
+ this.afkController.onAFKTimedOutCallback = () => {
140
+ this.closeSignalingServer(
141
+ 'You have been disconnected due to inactivity'
142
+ );
143
+ };
144
+
145
+ this.freezeFrameController = new FreezeFrameController(
146
+ this.pixelStreaming.videoElementParent
147
+ );
148
+
149
+ this.videoPlayer = new VideoPlayer(
150
+ this.pixelStreaming.videoElementParent,
151
+ this.config
152
+ );
153
+ this.videoPlayer.onVideoInitialized = () =>
154
+ this.handleVideoInitialized();
155
+
156
+ // When in match viewport resolution mode, when the browser viewport is resized we send a resize command back to UE.
157
+ this.videoPlayer.onMatchViewportResolutionCallback = (
158
+ width: number,
159
+ height: number
160
+ ) => {
161
+ const descriptor = {
162
+ 'Resolution.Width': width,
163
+ 'Resolution.Height': height
164
+ };
165
+
166
+ this.streamMessageController.toStreamerHandlers.get('Command')([
167
+ JSON.stringify(descriptor)
168
+ ]);
169
+ };
170
+
171
+ // Every time video player is resized in browser we need to reinitialize the mouse coordinate conversion and freeze frame sizing logic.
172
+ this.videoPlayer.onResizePlayerCallback = () => {
173
+ this.setUpMouseAndFreezeFrame();
174
+ };
175
+
176
+ this.streamController = new StreamController(this.videoPlayer);
177
+
178
+ this.coordinateConverter = new CoordinateConverter(this.videoPlayer);
179
+
180
+ this.sendrecvDataChannelController = new DataChannelController();
181
+ this.recvDataChannelController = new DataChannelController();
182
+ this.registerDataChannelEventEmitters(
183
+ this.sendrecvDataChannelController
184
+ );
185
+ this.registerDataChannelEventEmitters(this.recvDataChannelController);
186
+ this.dataChannelSender = new DataChannelSender(
187
+ this.sendrecvDataChannelController
188
+ );
189
+ this.dataChannelSender.resetAfkWarningTimerOnDataSend = () =>
190
+ this.afkController.resetAfkWarningTimer();
191
+
192
+ this.streamMessageController = new StreamMessageController();
193
+
194
+ // 设置websocket方法
195
+ this.webSocketController = new WebSocketController();
196
+
197
+ this.webSocketController.onConfig = (
198
+ messageConfig: MessageReceive.MessageConfig
199
+ ) => this.handleOnConfigMessage(messageConfig);
200
+
201
+ this.webSocketController.onStreamerList = (
202
+ messageList: MessageReceive.MessageStreamerList
203
+ ) => this.handleStreamerListMessage(messageList);
204
+
205
+ this.webSocketController.onPlayerCount = (
206
+ playerCount: MessageReceive.MessagePlayerCount
207
+ ) => {
208
+ this.pixelStreaming._onPlayerCount(playerCount.count);
209
+ };
210
+
211
+ this.webSocketController.onOpen.addEventListener('open', () => {
212
+ const BrowserSendsOffer = this.config.isFlagEnabled(
213
+ Flags.BrowserSendOffer
214
+ );
215
+ if (!BrowserSendsOffer) {
216
+ // console.log(this.config.useUrlParams, '=====');
217
+
218
+ // 发送当前连接 所有像素流 TODO:无法判断是固定的什么项目的像素流
219
+ // this.webSocketController.requestStreamerList();
220
+ // this.webSocketController.requestStreamerObject(window.streamerName());
221
+ this.webSocketController.requestStreamerObject(
222
+ sessionStorage.getItem('streamerName')
223
+ );
224
+
225
+ // this.webSocketController.onStreamerList = (
226
+ // messageList: MessageReceive.MessageStreamerList
227
+ // ) => {
228
+ // }
229
+ }
230
+ });
231
+
232
+ this.webSocketController.onClose.addEventListener(
233
+ 'close',
234
+ (event: CustomEvent) => {
235
+ // when we refresh the page during a stream we get the going away code.
236
+ // in that case we don't want to reconnect since we're navigating away.
237
+ // https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent/code
238
+ // lists all the codes.
239
+ const CODE_GOING_AWAY = 1001;
240
+
241
+ const willTryReconnect =
242
+ this.shouldReconnect &&
243
+ event.detail.code != CODE_GOING_AWAY &&
244
+ this.config.getNumericSettingValue(
245
+ NumericParameters.MaxReconnectAttempts
246
+ ) > 0;
247
+
248
+ const disconnectMessage = this.disconnectMessage
249
+ ? this.disconnectMessage
250
+ : event.detail.reason;
251
+ this.pixelStreaming._onDisconnect(
252
+ disconnectMessage,
253
+ !willTryReconnect && !this.isReconnecting
254
+ );
255
+
256
+ this.afkController.stopAfkWarningTimer();
257
+
258
+ // stop sending stats on interval if we have closed our connection
259
+ if (
260
+ this.statsTimerHandle &&
261
+ this.statsTimerHandle !== undefined
262
+ ) {
263
+ window.clearInterval(this.statsTimerHandle);
264
+ }
265
+
266
+ // reset the stream quality icon.
267
+ this.setVideoEncoderAvgQP(0);
268
+
269
+ // unregister all input device event handlers on disconnect
270
+ this.setTouchInputEnabled(false);
271
+ this.setMouseInputEnabled(false);
272
+ this.setKeyboardInputEnabled(false);
273
+ this.setGamePadInputEnabled(false);
274
+
275
+ if (willTryReconnect) {
276
+ // need a small delay here to prevent reconnect spamming
277
+ setTimeout(() => {
278
+ this.isReconnecting = true;
279
+ this.reconnectAttempt++;
280
+ this.tryReconnect(event.detail.reason);
281
+ }, 2000);
282
+ }
283
+ }
284
+ );
285
+
286
+ // 在应用中设置webRtc播放器控制器的最后几个方法,以便激活连接
287
+ this.sendMessageController = new SendMessageController(
288
+ this.dataChannelSender,
289
+ this.streamMessageController
290
+ );
291
+ this.toStreamerMessagesController = new ToStreamerMessagesController(
292
+ this.sendMessageController
293
+ );
294
+ this.registerMessageHandlers();
295
+ this.streamMessageController.populateDefaultProtocol();
296
+
297
+ this.inputClassesFactory = new InputClassesFactory(
298
+ this.streamMessageController,
299
+ this.videoPlayer,
300
+ this.coordinateConverter
301
+ );
302
+
303
+ this.isUsingSFU = false;
304
+ this.isQualityController = false;
305
+ this.preferredCodec = '';
306
+ this.shouldReconnect = true;
307
+ this.isReconnecting = false;
308
+ this.reconnectAttempt = 0;
309
+
310
+ this.config._addOnOptionSettingChangedListener(
311
+ OptionParameters.StreamerId,
312
+ (streamerid) => {
313
+ if (streamerid === '') {
314
+ return;
315
+ }
316
+
317
+ // 关闭当前端连接并创建一个新的连接
318
+ this.peerConnectionController.peerConnection.close();
319
+ this.peerConnectionController.createPeerConnection(
320
+ this.peerConfig,
321
+ this.preferredCodec
322
+ );
323
+ this.subscribedStream = streamerid;
324
+ this.webSocketController.sendSubscribe(streamerid);
325
+ }
326
+ );
327
+
328
+ this.setVideoEncoderAvgQP(-1);
329
+
330
+ this.signallingUrlBuilder = () => {
331
+ let signallingServerUrl = this.config.getTextSettingValue(
332
+ TextParameters.SignallingServerUrl
333
+ );
334
+
335
+ // If we are connecting to the SFU add a special url parameter to the url
336
+ if (this.config.isFlagEnabled(Flags.BrowserSendOffer)) {
337
+ signallingServerUrl += '?' + Flags.BrowserSendOffer + '=true';
338
+ }
339
+
340
+ // This code is no longer needed, but is a good example for how subsequent config flags can be appended
341
+ // if (this.config.isFlagEnabled(Flags.BrowserSendOffer)) {
342
+ // signallingServerUrl += (signallingServerUrl.includes('?') ? '&' : '?') + Flags.BrowserSendOffer + '=true';
343
+ // }
344
+
345
+ return signallingServerUrl;
346
+ };
347
+ }
348
+
349
+ /**
350
+ * Make a request to UnquantizedAndDenormalizeUnsigned coordinates
351
+ * @param x x axis coordinate
352
+ * @param y y axis coordinate
353
+ */
354
+ requestUnquantizedAndDenormalizeUnsigned(
355
+ x: number,
356
+ y: number
357
+ ): UnquantizedDenormalizedUnsignedCoord {
358
+ return this.coordinateConverter.unquantizeAndDenormalizeUnsigned(x, y);
359
+ }
360
+
361
+ /**
362
+ * Handles when a message is received
363
+ * @param event - Message Event
364
+ */
365
+ handleOnMessage(event: MessageEvent) {
366
+ const message = new Uint8Array(event.data);
367
+ Logger.Log(Logger.GetStackTrace(), 'Message incoming:' + message, 6);
368
+
369
+ //try {
370
+ const messageType =
371
+ this.streamMessageController.fromStreamerMessages.get(message[0]);
372
+ this.streamMessageController.fromStreamerHandlers.get(messageType)(
373
+ event.data
374
+ );
375
+ //} catch (e) {
376
+ //Logger.Error(Logger.GetStackTrace(), `Custom data channel message with message type that is unknown to the Pixel Streaming protocol. Does your PixelStreamingProtocol need updating? The message type was: ${message[0]}`);
377
+ //}
378
+ }
379
+
380
+ /**
381
+ * Register message all handlers
382
+ */
383
+ registerMessageHandlers() {
384
+ // From Streamer
385
+ // Message events from the streamer have a data type of ArrayBuffer as we force this type in the DatachannelController
386
+ this.streamMessageController.registerMessageHandler(
387
+ MessageDirection.FromStreamer,
388
+ 'QualityControlOwnership',
389
+ (data: ArrayBuffer) => this.onQualityControlOwnership(data)
390
+ );
391
+ this.streamMessageController.registerMessageHandler(
392
+ MessageDirection.FromStreamer,
393
+ 'Response',
394
+ (data: ArrayBuffer) => this.responseController.onResponse(data)
395
+ );
396
+ this.streamMessageController.registerMessageHandler(
397
+ MessageDirection.FromStreamer,
398
+ 'Command',
399
+ (data: ArrayBuffer) => {
400
+ this.onCommand(data);
401
+ }
402
+ );
403
+ this.streamMessageController.registerMessageHandler(
404
+ MessageDirection.FromStreamer,
405
+ 'FreezeFrame',
406
+ (data: ArrayBuffer) => this.onFreezeFrameMessage(data)
407
+ );
408
+ this.streamMessageController.registerMessageHandler(
409
+ MessageDirection.FromStreamer,
410
+ 'UnfreezeFrame',
411
+ () => this.invalidateFreezeFrameAndEnableVideo()
412
+ );
413
+ this.streamMessageController.registerMessageHandler(
414
+ MessageDirection.FromStreamer,
415
+ 'VideoEncoderAvgQP',
416
+ (data: ArrayBuffer) => this.handleVideoEncoderAvgQP(data)
417
+ );
418
+ this.streamMessageController.registerMessageHandler(
419
+ MessageDirection.FromStreamer,
420
+ 'LatencyTest',
421
+ (data: ArrayBuffer) => this.handleLatencyTestResult(data)
422
+ );
423
+ this.streamMessageController.registerMessageHandler(
424
+ MessageDirection.FromStreamer,
425
+ 'DataChannelLatencyTest',
426
+ (data: ArrayBuffer) =>
427
+ this.handleDataChannelLatencyTestResponse(data)
428
+ );
429
+ this.streamMessageController.registerMessageHandler(
430
+ MessageDirection.FromStreamer,
431
+ 'InitialSettings',
432
+ (data: ArrayBuffer) => this.handleInitialSettings(data)
433
+ );
434
+ this.streamMessageController.registerMessageHandler(
435
+ MessageDirection.FromStreamer,
436
+ 'FileExtension',
437
+ (data: ArrayBuffer) => this.onFileExtension(data)
438
+ );
439
+ this.streamMessageController.registerMessageHandler(
440
+ MessageDirection.FromStreamer,
441
+ 'FileMimeType',
442
+ (data: ArrayBuffer) => this.onFileMimeType(data)
443
+ );
444
+ this.streamMessageController.registerMessageHandler(
445
+ MessageDirection.FromStreamer,
446
+ 'FileContents',
447
+ (data: ArrayBuffer) => this.onFileContents(data)
448
+ );
449
+ this.streamMessageController.registerMessageHandler(
450
+ MessageDirection.FromStreamer,
451
+ 'TestEcho',
452
+ () => {
453
+ /* Do nothing */
454
+ }
455
+ );
456
+ this.streamMessageController.registerMessageHandler(
457
+ MessageDirection.FromStreamer,
458
+ 'InputControlOwnership',
459
+ (data: ArrayBuffer) => this.onInputControlOwnership(data)
460
+ );
461
+ this.streamMessageController.registerMessageHandler(
462
+ MessageDirection.FromStreamer,
463
+ 'GamepadResponse',
464
+ (data: ArrayBuffer) => this.onGamepadResponse(data)
465
+ );
466
+ this.streamMessageController.registerMessageHandler(
467
+ MessageDirection.FromStreamer,
468
+ 'Protocol',
469
+ (data: ArrayBuffer) => this.onProtocolMessage(data)
470
+ );
471
+
472
+ // To Streamer
473
+ this.streamMessageController.registerMessageHandler(
474
+ MessageDirection.ToStreamer,
475
+ 'IFrameRequest',
476
+ () =>
477
+ this.sendMessageController.sendMessageToStreamer(
478
+ 'IFrameRequest'
479
+ )
480
+ );
481
+ this.streamMessageController.registerMessageHandler(
482
+ MessageDirection.ToStreamer,
483
+ 'RequestQualityControl',
484
+ () =>
485
+ this.sendMessageController.sendMessageToStreamer(
486
+ 'RequestQualityControl'
487
+ )
488
+ );
489
+ this.streamMessageController.registerMessageHandler(
490
+ MessageDirection.ToStreamer,
491
+ 'FpsRequest',
492
+ () => this.sendMessageController.sendMessageToStreamer('FpsRequest')
493
+ );
494
+ this.streamMessageController.registerMessageHandler(
495
+ MessageDirection.ToStreamer,
496
+ 'AverageBitrateRequest',
497
+ () =>
498
+ this.sendMessageController.sendMessageToStreamer(
499
+ 'AverageBitrateRequest'
500
+ )
501
+ );
502
+ this.streamMessageController.registerMessageHandler(
503
+ MessageDirection.ToStreamer,
504
+ 'StartStreaming',
505
+ () =>
506
+ this.sendMessageController.sendMessageToStreamer(
507
+ 'StartStreaming'
508
+ )
509
+ );
510
+ this.streamMessageController.registerMessageHandler(
511
+ MessageDirection.ToStreamer,
512
+ 'StopStreaming',
513
+ () =>
514
+ this.sendMessageController.sendMessageToStreamer(
515
+ 'StopStreaming'
516
+ )
517
+ );
518
+ this.streamMessageController.registerMessageHandler(
519
+ MessageDirection.ToStreamer,
520
+ 'LatencyTest',
521
+ (data: Array<number | string>) =>
522
+ this.sendMessageController.sendMessageToStreamer(
523
+ 'LatencyTest',
524
+ data
525
+ )
526
+ );
527
+ this.streamMessageController.registerMessageHandler(
528
+ MessageDirection.ToStreamer,
529
+ 'RequestInitialSettings',
530
+ () =>
531
+ this.sendMessageController.sendMessageToStreamer(
532
+ 'RequestInitialSettings'
533
+ )
534
+ );
535
+ this.streamMessageController.registerMessageHandler(
536
+ MessageDirection.ToStreamer,
537
+ 'TestEcho',
538
+ () => {
539
+ /* Do nothing */
540
+ }
541
+ );
542
+ this.streamMessageController.registerMessageHandler(
543
+ MessageDirection.ToStreamer,
544
+ 'UIInteraction',
545
+ (data: Array<number | string>) =>
546
+ this.sendMessageController.sendMessageToStreamer(
547
+ 'UIInteraction',
548
+ data
549
+ )
550
+ );
551
+ this.streamMessageController.registerMessageHandler(
552
+ MessageDirection.ToStreamer,
553
+ 'Command',
554
+ (data: Array<number | string>) =>
555
+ this.sendMessageController.sendMessageToStreamer(
556
+ 'Command',
557
+ data
558
+ )
559
+ );
560
+ this.streamMessageController.registerMessageHandler(
561
+ MessageDirection.ToStreamer,
562
+ 'TextboxEntry',
563
+ (data: Array<number | string>) =>
564
+ this.sendMessageController.sendMessageToStreamer(
565
+ 'TextboxEntry',
566
+ data
567
+ )
568
+ );
569
+ this.streamMessageController.registerMessageHandler(
570
+ MessageDirection.ToStreamer,
571
+ 'KeyDown',
572
+ (data: Array<number | string>) =>
573
+ this.sendMessageController.sendMessageToStreamer(
574
+ 'KeyDown',
575
+ data
576
+ )
577
+ );
578
+ this.streamMessageController.registerMessageHandler(
579
+ MessageDirection.ToStreamer,
580
+ 'KeyUp',
581
+ (data: Array<number | string>) =>
582
+ this.sendMessageController.sendMessageToStreamer('KeyUp', data)
583
+ );
584
+ this.streamMessageController.registerMessageHandler(
585
+ MessageDirection.ToStreamer,
586
+ 'KeyPress',
587
+ (data: Array<number | string>) =>
588
+ this.sendMessageController.sendMessageToStreamer(
589
+ 'KeyPress',
590
+ data
591
+ )
592
+ );
593
+ this.streamMessageController.registerMessageHandler(
594
+ MessageDirection.ToStreamer,
595
+ 'MouseEnter',
596
+ (data: Array<number | string>) =>
597
+ this.sendMessageController.sendMessageToStreamer(
598
+ 'MouseEnter',
599
+ data
600
+ )
601
+ );
602
+ this.streamMessageController.registerMessageHandler(
603
+ MessageDirection.ToStreamer,
604
+ 'MouseLeave',
605
+ (data: Array<number | string>) =>
606
+ this.sendMessageController.sendMessageToStreamer(
607
+ 'MouseLeave',
608
+ data
609
+ )
610
+ );
611
+ this.streamMessageController.registerMessageHandler(
612
+ MessageDirection.ToStreamer,
613
+ 'MouseDown',
614
+ (data: Array<number | string>) =>
615
+ this.sendMessageController.sendMessageToStreamer(
616
+ 'MouseDown',
617
+ data
618
+ )
619
+ );
620
+ this.streamMessageController.registerMessageHandler(
621
+ MessageDirection.ToStreamer,
622
+ 'MouseUp',
623
+ (data: Array<number | string>) =>
624
+ this.sendMessageController.sendMessageToStreamer(
625
+ 'MouseUp',
626
+ data
627
+ )
628
+ );
629
+ this.streamMessageController.registerMessageHandler(
630
+ MessageDirection.ToStreamer,
631
+ 'MouseMove',
632
+ (data: Array<number | string>) =>
633
+ this.sendMessageController.sendMessageToStreamer(
634
+ 'MouseMove',
635
+ data
636
+ )
637
+ );
638
+ this.streamMessageController.registerMessageHandler(
639
+ MessageDirection.ToStreamer,
640
+ 'MouseWheel',
641
+ (data: Array<number | string>) =>
642
+ this.sendMessageController.sendMessageToStreamer(
643
+ 'MouseWheel',
644
+ data
645
+ )
646
+ );
647
+ this.streamMessageController.registerMessageHandler(
648
+ MessageDirection.ToStreamer,
649
+ 'MouseDouble',
650
+ (data: Array<number | string>) =>
651
+ this.sendMessageController.sendMessageToStreamer(
652
+ 'MouseDouble',
653
+ data
654
+ )
655
+ );
656
+ this.streamMessageController.registerMessageHandler(
657
+ MessageDirection.ToStreamer,
658
+ 'TouchStart',
659
+ (data: Array<number | string>) =>
660
+ this.sendMessageController.sendMessageToStreamer(
661
+ 'TouchStart',
662
+ data
663
+ )
664
+ );
665
+ this.streamMessageController.registerMessageHandler(
666
+ MessageDirection.ToStreamer,
667
+ 'TouchEnd',
668
+ (data: Array<number | string>) =>
669
+ this.sendMessageController.sendMessageToStreamer(
670
+ 'TouchEnd',
671
+ data
672
+ )
673
+ );
674
+ this.streamMessageController.registerMessageHandler(
675
+ MessageDirection.ToStreamer,
676
+ 'TouchMove',
677
+ (data: Array<number | string>) =>
678
+ this.sendMessageController.sendMessageToStreamer(
679
+ 'TouchMove',
680
+ data
681
+ )
682
+ );
683
+ this.streamMessageController.registerMessageHandler(
684
+ MessageDirection.ToStreamer,
685
+ 'GamepadConnected',
686
+ () =>
687
+ this.sendMessageController.sendMessageToStreamer(
688
+ 'GamepadConnected'
689
+ )
690
+ );
691
+ this.streamMessageController.registerMessageHandler(
692
+ MessageDirection.ToStreamer,
693
+ 'GamepadButtonPressed',
694
+ (data: Array<number | string>) =>
695
+ this.sendMessageController.sendMessageToStreamer(
696
+ 'GamepadButtonPressed',
697
+ data
698
+ )
699
+ );
700
+ this.streamMessageController.registerMessageHandler(
701
+ MessageDirection.ToStreamer,
702
+ 'GamepadButtonReleased',
703
+ (data: Array<number | string>) =>
704
+ this.sendMessageController.sendMessageToStreamer(
705
+ 'GamepadButtonReleased',
706
+ data
707
+ )
708
+ );
709
+ this.streamMessageController.registerMessageHandler(
710
+ MessageDirection.ToStreamer,
711
+ 'GamepadAnalog',
712
+ (data: Array<number | string>) =>
713
+ this.sendMessageController.sendMessageToStreamer(
714
+ 'GamepadAnalog',
715
+ data
716
+ )
717
+ );
718
+ this.streamMessageController.registerMessageHandler(
719
+ MessageDirection.ToStreamer,
720
+ 'GamepadDisconnected',
721
+ (data: Array<number | string>) =>
722
+ this.sendMessageController.sendMessageToStreamer(
723
+ 'GamepadDisconnected',
724
+ data
725
+ )
726
+ );
727
+ this.streamMessageController.registerMessageHandler(
728
+ MessageDirection.ToStreamer,
729
+ 'XRHMDTransform',
730
+ (data: Array<number | string>) =>
731
+ this.sendMessageController.sendMessageToStreamer(
732
+ 'XRHMDTransform',
733
+ data
734
+ )
735
+ );
736
+ this.streamMessageController.registerMessageHandler(
737
+ MessageDirection.ToStreamer,
738
+ 'XRControllerTransform',
739
+ (data: Array<number | string>) =>
740
+ this.sendMessageController.sendMessageToStreamer(
741
+ 'XRControllerTransform',
742
+ data
743
+ )
744
+ );
745
+ this.streamMessageController.registerMessageHandler(
746
+ MessageDirection.ToStreamer,
747
+ 'XRSystem',
748
+ (data: Array<number | string>) =>
749
+ this.sendMessageController.sendMessageToStreamer(
750
+ 'XRSystem',
751
+ data
752
+ )
753
+ );
754
+ this.streamMessageController.registerMessageHandler(
755
+ MessageDirection.ToStreamer,
756
+ 'XRButtonTouched',
757
+ (data: Array<number | string>) =>
758
+ this.sendMessageController.sendMessageToStreamer(
759
+ 'XRButtonTouched',
760
+ data
761
+ )
762
+ );
763
+ this.streamMessageController.registerMessageHandler(
764
+ MessageDirection.ToStreamer,
765
+ 'XRButtonPressed',
766
+ (data: Array<number | string>) =>
767
+ this.sendMessageController.sendMessageToStreamer(
768
+ 'XRButtonPressed',
769
+ data
770
+ )
771
+ );
772
+ this.streamMessageController.registerMessageHandler(
773
+ MessageDirection.ToStreamer,
774
+ 'XRButtonReleased',
775
+ (data: Array<number | string>) =>
776
+ this.sendMessageController.sendMessageToStreamer(
777
+ 'XRButtonReleased',
778
+ data
779
+ )
780
+ );
781
+ this.streamMessageController.registerMessageHandler(
782
+ MessageDirection.ToStreamer,
783
+ 'XRAnalog',
784
+ (data: Array<number | string>) =>
785
+ this.sendMessageController.sendMessageToStreamer(
786
+ 'XRAnalog',
787
+ data
788
+ )
789
+ );
790
+ }
791
+
792
+ /**
793
+ * Activate the logic associated with a command from UE
794
+ * @param message
795
+ */
796
+ onCommand(message: ArrayBuffer) {
797
+ Logger.Log(
798
+ Logger.GetStackTrace(),
799
+ 'DataChannelReceiveMessageType.Command',
800
+ 6
801
+ );
802
+ const commandAsString = new TextDecoder('utf-16').decode(
803
+ message.slice(1)
804
+ );
805
+
806
+ Logger.Log(
807
+ Logger.GetStackTrace(),
808
+ 'Data Channel Command: ' + commandAsString,
809
+ 6
810
+ );
811
+ const command: MessageOnScreenKeyboard = JSON.parse(commandAsString);
812
+ if (command.command === 'onScreenKeyboard') {
813
+ this.pixelStreaming._activateOnScreenKeyboard(command);
814
+ }
815
+ }
816
+
817
+ /**
818
+ * Handles a protocol message received from the streamer
819
+ * @param message the message data from the streamer
820
+ */
821
+ onProtocolMessage(message: ArrayBuffer) {
822
+ try {
823
+ const protocolString = new TextDecoder('utf-16').decode(
824
+ message.slice(1)
825
+ );
826
+ const protocolJSON = JSON.parse(protocolString);
827
+ if (
828
+ !Object.prototype.hasOwnProperty.call(protocolJSON, 'Direction')
829
+ ) {
830
+ Logger.Error(
831
+ Logger.GetStackTrace(),
832
+ 'Malformed protocol received. Ensure the protocol message contains a direction'
833
+ );
834
+ }
835
+ const direction = protocolJSON.Direction;
836
+ delete protocolJSON.Direction;
837
+ Logger.Log(
838
+ Logger.GetStackTrace(),
839
+ `Received new ${
840
+ direction == MessageDirection.FromStreamer
841
+ ? 'FromStreamer'
842
+ : 'ToStreamer'
843
+ } protocol. Updating existing protocol...(更新现有协议)`
844
+ );
845
+ Object.keys(protocolJSON).forEach((messageType) => {
846
+ const message = protocolJSON[messageType];
847
+ switch (direction) {
848
+ case MessageDirection.ToStreamer:
849
+ // Check that the message contains all the relevant params
850
+ if (
851
+ !Object.prototype.hasOwnProperty.call(message, 'id')
852
+ ) {
853
+ Logger.Error(
854
+ Logger.GetStackTrace(),
855
+ `ToStreamer->${messageType} protocol definition was malformed as it didn't contain at least an id\n
856
+ Definition was: ${JSON.stringify(
857
+ message,
858
+ null,
859
+ 2
860
+ )}`
861
+ );
862
+ // return in a forEach is equivalent to a continue in a normal for loop
863
+ return;
864
+ }
865
+
866
+ // UE5.1 and UE5.2 don't send a structure for these message types, but they actually do have a structure so ignore updating them
867
+ if (
868
+ messageType === 'UIInteraction' ||
869
+ messageType === 'Command' ||
870
+ messageType === 'LatencyTest'
871
+ ) {
872
+ return;
873
+ }
874
+
875
+ if (
876
+ this.streamMessageController.toStreamerHandlers.get(
877
+ messageType
878
+ )
879
+ ) {
880
+ // If we've registered a handler for this message type we can add it to our supported messages. ie registerMessageHandler(...)
881
+ this.streamMessageController.toStreamerMessages.set(
882
+ messageType,
883
+ message
884
+ );
885
+ } else {
886
+ Logger.Error(
887
+ Logger.GetStackTrace(),
888
+ `There was no registered handler for "${messageType}" - try adding one using registerMessageHandler(MessageDirection.ToStreamer, "${messageType}", myHandler)`
889
+ );
890
+ }
891
+ break;
892
+ case MessageDirection.FromStreamer:
893
+ // Check that the message contains all the relevant params
894
+ if (
895
+ !Object.prototype.hasOwnProperty.call(message, 'id')
896
+ ) {
897
+ Logger.Error(
898
+ Logger.GetStackTrace(),
899
+ `FromStreamer->${messageType} protocol definition was malformed as it didn't contain at least an id\n
900
+ Definition was: ${JSON.stringify(message, null, 2)}`
901
+ );
902
+ // return in a forEach is equivalent to a continue in a normal for loop
903
+ return;
904
+ }
905
+ if (
906
+ this.streamMessageController.fromStreamerHandlers.get(
907
+ messageType
908
+ )
909
+ ) {
910
+ // If we've registered a handler for this message type. ie registerMessageHandler(...)
911
+ this.streamMessageController.fromStreamerMessages.set(
912
+ message.id,
913
+ messageType
914
+ );
915
+ } else {
916
+ Logger.Error(
917
+ Logger.GetStackTrace(),
918
+ `There was no registered handler for "${message}" - try adding one using registerMessageHandler(MessageDirection.FromStreamer, "${messageType}", myHandler)`
919
+ );
920
+ }
921
+ break;
922
+ default:
923
+ Logger.Error(
924
+ Logger.GetStackTrace(),
925
+ `Unknown direction: ${direction}`
926
+ );
927
+ }
928
+ });
929
+
930
+ // Once the protocol has been received, we can send our control messages
931
+ this.toStreamerMessagesController.SendRequestInitialSettings();
932
+ this.toStreamerMessagesController.SendRequestQualityControl();
933
+ } catch (e) {
934
+ Logger.Log(Logger.GetStackTrace(), e);
935
+ }
936
+ }
937
+
938
+ /**
939
+ * Handles an input control message when it is received from the streamer
940
+ * @param message The input control message
941
+ */
942
+ onInputControlOwnership(message: ArrayBuffer) {
943
+ const view = new Uint8Array(message);
944
+ Logger.Log(
945
+ Logger.GetStackTrace(),
946
+ 'DataChannelReceiveMessageType.InputControlOwnership',
947
+ 6
948
+ );
949
+ const inputControlOwnership = new Boolean(view[1]).valueOf();
950
+ Logger.Log(
951
+ Logger.GetStackTrace(),
952
+ `Received input controller message - will your input control the stream: ${inputControlOwnership}`
953
+ );
954
+ this.pixelStreaming._onInputControlOwnership(inputControlOwnership);
955
+ }
956
+
957
+ /**
958
+ *
959
+ * @param message
960
+ */
961
+ onGamepadResponse(message: ArrayBuffer) {
962
+ const responseString = new TextDecoder('utf-16').decode(
963
+ message.slice(1)
964
+ );
965
+ const responseJSON = JSON.parse(responseString);
966
+ this.gamePadController.onGamepadResponseReceived(
967
+ responseJSON.controllerId
968
+ );
969
+ }
970
+
971
+ onAfkTriggered(): void {
972
+ this.afkController.onAfkClick();
973
+
974
+ // if the stream is paused play it, if we can
975
+ if (this.videoPlayer.isPaused() && this.videoPlayer.hasVideoSource()) {
976
+ this.playStream();
977
+ }
978
+ }
979
+
980
+ /**
981
+ * Set whether we should timeout when afk.
982
+ * @param afkEnabled If true we timeout when idle for some given amount of time.
983
+ */
984
+ setAfkEnabled(afkEnabled: boolean): void {
985
+ if (afkEnabled) {
986
+ this.onAfkTriggered();
987
+ } else {
988
+ this.afkController.stopAfkWarningTimer();
989
+ }
990
+ }
991
+
992
+ /**
993
+ * 尝试重新连接到信令服务器
994
+ */
995
+ tryReconnect(message: string) {
996
+ // 如果没有webSocketController,立即返回,否则将不起作用
997
+ if (!this.webSocketController) {
998
+ Logger.Log(
999
+ Logger.GetStackTrace(),
1000
+ 'The Web Socket Controller does not exist so this will not work right now.'
1001
+ );
1002
+ return;
1003
+ }
1004
+
1005
+ // 如果连接是打开的,首先关闭它。等待一段时间再试一次
1006
+ this.isReconnecting = true;
1007
+ if (
1008
+ this.webSocketController.webSocket &&
1009
+ this.webSocketController.webSocket.readyState != WebSocket.CLOSED
1010
+ ) {
1011
+ this.closeSignalingServer(`${message} Restarting stream...`);
1012
+ setTimeout(() => {
1013
+ this.tryReconnect(message);
1014
+ }, 3000);
1015
+ } else {
1016
+ this.pixelStreaming._onWebRtcAutoConnect();
1017
+ this.connectToSignallingServer();
1018
+ }
1019
+ }
1020
+
1021
+ /**
1022
+ * 如果需要,加载一个定格帧,否则显示播放覆盖
1023
+ */
1024
+ loadFreezeFrameOrShowPlayOverlay() {
1025
+ this.pixelStreaming.dispatchEvent(
1026
+ new LoadFreezeFrameEvent({
1027
+ shouldShowPlayOverlay: this.shouldShowPlayOverlay,
1028
+ isValid: this.freezeFrameController.valid,
1029
+ jpegData: this.freezeFrameController.jpeg
1030
+ })
1031
+ );
1032
+ if (this.shouldShowPlayOverlay === true) {
1033
+ Logger.Log(Logger.GetStackTrace(), 'showing play overlay');
1034
+ this.resizePlayerStyle();
1035
+ } else {
1036
+ Logger.Log(Logger.GetStackTrace(), 'showing freeze frame');
1037
+ this.freezeFrameController.showFreezeFrame();
1038
+ }
1039
+ setTimeout(() => {
1040
+ this.videoPlayer.setVideoEnabled(false);
1041
+ }, this.freezeFrameController.freezeFrameDelay);
1042
+ }
1043
+
1044
+ /**
1045
+ * Process the freeze frame and load it
1046
+ * @param message The freeze frame data in bytes
1047
+ */
1048
+ onFreezeFrameMessage(message: ArrayBuffer) {
1049
+ Logger.Log(
1050
+ Logger.GetStackTrace(),
1051
+ 'DataChannelReceiveMessageType.FreezeFrame',
1052
+ 6
1053
+ );
1054
+ const view = new Uint8Array(message);
1055
+ this.freezeFrameController.processFreezeFrameMessage(view, () =>
1056
+ this.loadFreezeFrameOrShowPlayOverlay()
1057
+ );
1058
+ }
1059
+
1060
+ /**
1061
+ * Enable the video after hiding a freeze frame
1062
+ */
1063
+ invalidateFreezeFrameAndEnableVideo() {
1064
+ Logger.Log(
1065
+ Logger.GetStackTrace(),
1066
+ 'DataChannelReceiveMessageType.FreezeFrame',
1067
+ 6
1068
+ );
1069
+ setTimeout(() => {
1070
+ this.pixelStreaming.dispatchEvent(new HideFreezeFrameEvent());
1071
+ this.freezeFrameController.hideFreezeFrame();
1072
+ }, this.freezeFrameController.freezeFrameDelay);
1073
+ if (this.videoPlayer.getVideoElement()) {
1074
+ this.videoPlayer.setVideoEnabled(true);
1075
+ }
1076
+ }
1077
+
1078
+ /**
1079
+ * Prep datachannel data for processing file extension
1080
+ * @param data the file extension data
1081
+ */
1082
+ onFileExtension(data: ArrayBuffer) {
1083
+ const view = new Uint8Array(data);
1084
+ FileUtil.setExtensionFromBytes(view, this.file);
1085
+ }
1086
+
1087
+ /**
1088
+ * Prep datachannel data for processing the file mime type
1089
+ * @param data the file mime type data
1090
+ */
1091
+ onFileMimeType(data: ArrayBuffer) {
1092
+ const view = new Uint8Array(data);
1093
+ FileUtil.setMimeTypeFromBytes(view, this.file);
1094
+ }
1095
+
1096
+ /**
1097
+ * Prep datachannel data for processing the file contents
1098
+ * @param data the file contents data
1099
+ */
1100
+ onFileContents(data: ArrayBuffer) {
1101
+ const view = new Uint8Array(data);
1102
+ FileUtil.setContentsFromBytes(view, this.file);
1103
+ }
1104
+
1105
+ /**
1106
+ * 播放流音频和视频源,并在流启动时设置其他片段
1107
+ */
1108
+ playStream() {
1109
+ if (!this.videoPlayer.getVideoElement()) {
1110
+ const message =
1111
+ 'Could not play video stream because the video player was not initialized correctly.';
1112
+ this.pixelStreaming.dispatchEvent(
1113
+ new PlayStreamErrorEvent({ message })
1114
+ );
1115
+ Logger.Error(Logger.GetStackTrace(), message);
1116
+
1117
+ // close the connection
1118
+ this.closeSignalingServer('Stream not initialized correctly');
1119
+ return;
1120
+ }
1121
+
1122
+ if (!this.videoPlayer.hasVideoSource()) {
1123
+ Logger.Warning(
1124
+ Logger.GetStackTrace(),
1125
+ 'Cannot play stream, the video element has no srcObject to play.'
1126
+ );
1127
+ return;
1128
+ }
1129
+
1130
+ this.setTouchInputEnabled(this.config.isFlagEnabled(Flags.TouchInput));
1131
+ this.pixelStreaming.dispatchEvent(new PlayStreamEvent());
1132
+
1133
+ if (this.streamController.audioElement.srcObject) {
1134
+ const startMuted = this.config.isFlagEnabled(Flags.StartVideoMuted);
1135
+ this.streamController.audioElement.muted = startMuted;
1136
+
1137
+ if (startMuted) {
1138
+ this.playVideo();
1139
+ } else {
1140
+ this.streamController.audioElement
1141
+ .play()
1142
+ .then(() => {
1143
+ this.playVideo();
1144
+ })
1145
+ .catch((onRejectedReason) => {
1146
+ Logger.Log(Logger.GetStackTrace(), onRejectedReason);
1147
+ Logger.Log(
1148
+ Logger.GetStackTrace(),
1149
+ 'Browser does not support autoplaying video without interaction - to resolve this we are going to show the play button overlay.'
1150
+ );
1151
+ this.pixelStreaming.dispatchEvent(
1152
+ new PlayStreamRejectedEvent({
1153
+ reason: onRejectedReason
1154
+ })
1155
+ );
1156
+ });
1157
+ }
1158
+ } else {
1159
+ this.playVideo();
1160
+ }
1161
+
1162
+ this.shouldShowPlayOverlay = false;
1163
+ this.freezeFrameController.showFreezeFrame();
1164
+ }
1165
+
1166
+ /**
1167
+ * Plays the video stream
1168
+ */
1169
+ private playVideo() {
1170
+ // handle play() with promise as it is an asynchronous call
1171
+ this.videoPlayer.play().catch((onRejectedReason: string) => {
1172
+ if (this.streamController.audioElement.srcObject) {
1173
+ this.streamController.audioElement.pause();
1174
+ }
1175
+ Logger.Log(Logger.GetStackTrace(), onRejectedReason);
1176
+ Logger.Log(
1177
+ Logger.GetStackTrace(),
1178
+ 'Browser does not support autoplaying video without interaction - to resolve this we are going to show the play button overlay.'
1179
+ );
1180
+ this.pixelStreaming.dispatchEvent(
1181
+ new PlayStreamRejectedEvent({ reason: onRejectedReason })
1182
+ );
1183
+ });
1184
+ }
1185
+
1186
+ /**
1187
+ * Enable the video to play automatically if enableAutoplay is true
1188
+ */
1189
+ autoPlayVideoOrSetUpPlayOverlay() {
1190
+ if (this.config.isFlagEnabled(Flags.AutoPlayVideo)) {
1191
+ // attempt to play the video
1192
+ this.playStream();
1193
+ }
1194
+ this.resizePlayerStyle();
1195
+ }
1196
+
1197
+ /**
1198
+ * 连接发信服务器
1199
+ */
1200
+ connectToSignallingServer() {
1201
+ this.locallyClosed = false;
1202
+ this.shouldReconnect = true;
1203
+ this.disconnectMessage = null;
1204
+ const signallingUrl = this.signallingUrlBuilder();
1205
+ this.webSocketController.connect(signallingUrl);
1206
+ }
1207
+
1208
+ /**
1209
+ * This will start the handshake to the signalling server
1210
+ * @param peerConfig - RTC Configuration Options from the Signaling server
1211
+ * @remark RTC Peer Connection on Ice Candidate event have it handled by handle Send Ice Candidate
1212
+ */
1213
+ startSession(peerConfig: RTCConfiguration) {
1214
+ this.peerConfig = peerConfig;
1215
+ // check for forcing turn
1216
+ if (this.config.isFlagEnabled(Flags.ForceTURN)) {
1217
+ // check for a turn server
1218
+ const hasTurnServer = this.checkTurnServerAvailability(peerConfig);
1219
+
1220
+ // close and error if turn is forced and there is no turn server
1221
+ if (!hasTurnServer) {
1222
+ Logger.Info(
1223
+ Logger.GetStackTrace(),
1224
+ 'No turn server was found in the Peer Connection Options. TURN cannot be forced, closing connection. Please use STUN instead'
1225
+ );
1226
+ this.closeSignalingServer(
1227
+ 'TURN cannot be forced, closing connection. Please use STUN instead.'
1228
+ );
1229
+ return;
1230
+ }
1231
+ }
1232
+
1233
+ // set up the peer connection controller
1234
+ this.peerConnectionController = new PeerConnectionController(
1235
+ this.peerConfig,
1236
+ this.config,
1237
+ this.preferredCodec
1238
+ );
1239
+
1240
+ // set up peer connection controller video stats
1241
+ this.peerConnectionController.onVideoStats = (event: AggregatedStats) =>
1242
+ this.handleVideoStats(event);
1243
+
1244
+ /* When the Peer Connection wants to send an offer have it handled */
1245
+ this.peerConnectionController.onSendWebRTCOffer = (
1246
+ offer: RTCSessionDescriptionInit
1247
+ ) => this.handleSendWebRTCOffer(offer);
1248
+
1249
+ /* When the Peer Connection wants to send an answer have it handled */
1250
+ this.peerConnectionController.onSendWebRTCAnswer = (
1251
+ offer: RTCSessionDescriptionInit
1252
+ ) => this.handleSendWebRTCAnswer(offer);
1253
+
1254
+ /* When the Peer Connection ice candidate is added have it handled */
1255
+ this.peerConnectionController.onPeerIceCandidate = (
1256
+ peerConnectionIceEvent: RTCPeerConnectionIceEvent
1257
+ ) => this.handleSendIceCandidate(peerConnectionIceEvent);
1258
+
1259
+ /* When the Peer Connection has a data channel created for it by the browser, handle it */
1260
+ this.peerConnectionController.onDataChannel = (
1261
+ datachannelEvent: RTCDataChannelEvent
1262
+ ) => this.handleDataChannel(datachannelEvent);
1263
+
1264
+ // 设置webRtc的文本覆盖层
1265
+ this.peerConnectionController.showTextOverlayConnecting = () =>
1266
+ this.pixelStreaming._onWebRtcConnecting();
1267
+ this.peerConnectionController.showTextOverlaySetupFailure = () =>
1268
+ this.pixelStreaming._onWebRtcFailed();
1269
+ let webRtcConnectedSent = false;
1270
+ this.peerConnectionController.onIceConnectionStateChange = () => {
1271
+ // Browsers emit "connected" when getting first connection and "completed" when finishing
1272
+ // candidate checking. However, sometimes browsers can skip "connected" and only emit "completed".
1273
+ // Therefore need to check both cases and emit onWebRtcConnected only once on the first hit.
1274
+ if (
1275
+ !webRtcConnectedSent &&
1276
+ ['connected', 'completed'].includes(
1277
+ this.peerConnectionController.peerConnection
1278
+ .iceConnectionState
1279
+ )
1280
+ ) {
1281
+ this.pixelStreaming._onWebRtcConnected();
1282
+ webRtcConnectedSent = true;
1283
+ }
1284
+ };
1285
+
1286
+ /* RTC Peer Connection on Track event -> handle on track */
1287
+ this.peerConnectionController.onTrack = (trackEvent: RTCTrackEvent) =>
1288
+ this.streamController.handleOnTrack(trackEvent);
1289
+
1290
+ /* Start the Hand shake process by creating an Offer */
1291
+ const BrowserSendsOffer = this.config.isFlagEnabled(
1292
+ Flags.BrowserSendOffer
1293
+ );
1294
+ if (BrowserSendsOffer) {
1295
+ // If browser is sending the offer, create an offer and send it to the streamer
1296
+ this.sendrecvDataChannelController.createDataChannel(
1297
+ this.peerConnectionController.peerConnection,
1298
+ 'cirrus',
1299
+ this.datachannelOptions
1300
+ );
1301
+ this.sendrecvDataChannelController.handleOnMessage = (
1302
+ ev: MessageEvent<ArrayBuffer>
1303
+ ) => this.handleOnMessage(ev);
1304
+ this.peerConnectionController.createOffer(
1305
+ this.sdpConstraints,
1306
+ this.config
1307
+ );
1308
+ }
1309
+ }
1310
+
1311
+ /**
1312
+ * Checks the peer connection options for a turn server and returns true or false
1313
+ */
1314
+ checkTurnServerAvailability(options: RTCConfiguration) {
1315
+ // if iceServers is empty return false this should not be the general use case but is here incase
1316
+ if (!options.iceServers) {
1317
+ Logger.Info(Logger.GetStackTrace(), 'A turn sever was not found');
1318
+ return false;
1319
+ }
1320
+
1321
+ // loop through the ice servers to check for a turn url
1322
+ for (const iceServer of options.iceServers) {
1323
+ for (const url of iceServer.urls) {
1324
+ if (url.includes('turn')) {
1325
+ Logger.Log(
1326
+ Logger.GetStackTrace(),
1327
+ `A turn sever was found at ${url}`
1328
+ );
1329
+ return true;
1330
+ }
1331
+ }
1332
+ }
1333
+
1334
+ Logger.Info(Logger.GetStackTrace(), 'A turn sever was not found');
1335
+ return false;
1336
+ }
1337
+
1338
+ /**
1339
+ * 接收到配置消息时的处理包含所需的对等连接选项(STUN和TURN服务器信息)
1340
+ * @param messageConfig—从发信服务器接收到的配置消息
1341
+ */
1342
+ handleOnConfigMessage(messageConfig: MessageConfig) {
1343
+ this.resizePlayerStyle();
1344
+
1345
+ // 告诉WebRtcController使用发信服务器发送的对等点选项启动会话
1346
+ this.startSession(messageConfig.peerConnectionOptions);
1347
+
1348
+ // 发信服务器通过websocket连接发送WebRTC应答时,让WebRtcController处理消息
1349
+ this.webSocketController.onWebRtcAnswer = (
1350
+ messageAnswer: MessageReceive.MessageAnswer
1351
+ ) => this.handleWebRtcAnswer(messageAnswer);
1352
+
1353
+ this.webSocketController.onWebRtcOffer = (
1354
+ messageOffer: MessageReceive.MessageOffer
1355
+ ) => this.handleWebRtcOffer(messageOffer);
1356
+
1357
+ this.webSocketController.onWebRtcPeerDataChannels = (
1358
+ messageDataChannels: MessageReceive.MessagePeerDataChannels
1359
+ ) => this.handleWebRtcSFUPeerDatachannels(messageDataChannels);
1360
+
1361
+ // 发信服务器通过websocket连接发送iccandidate时,让WebRtcController处理消息
1362
+ this.webSocketController.onIceCandidate = (
1363
+ iceCandidate: RTCIceCandidateInit
1364
+ ) => this.handleIceCandidate(iceCandidate);
1365
+ }
1366
+
1367
+ /**
1368
+ * 当信令服务器给我们streamer id列表时处理。
1369
+ */
1370
+ handleStreamerListMessage(messageStreamerList: MessageStreamerList) {
1371
+ console.log(messageStreamerList, '=====');
1372
+
1373
+ Logger.Log(
1374
+ Logger.GetStackTrace(),
1375
+ `Got streamer list ${messageStreamerList.ids}`,
1376
+ 6
1377
+ );
1378
+
1379
+ const streamerBusyList =
1380
+ (
1381
+ messageStreamerList as unknown as {
1382
+ streamerBusyList?: Array<{ streamerId: string }>;
1383
+ }
1384
+ ).streamerBusyList ?? [];
1385
+
1386
+
1387
+ if (streamerBusyList.length > 0) {
1388
+ console.log(window.location.href, '======');
1389
+ if (window.location.href.indexOf('home') !== -1) {
1390
+ return;
1391
+ }
1392
+ const confirmed = window.confirm(
1393
+ '监测到当前窗口有人在使用,是否强制进入?'
1394
+ );
1395
+ if (confirmed) {
1396
+ this.webSocketController.sendSubscribe(
1397
+ streamerBusyList[0].streamerId
1398
+ );
1399
+ }
1400
+ }
1401
+
1402
+ // 将streamer添加到UI中
1403
+ const settingOptions = [...messageStreamerList.ids]; // 复制原始的messageStreamerList.ids
1404
+ // settingOptions.unshift(''); // 在顶部添加一个空选项
1405
+ console.log(settingOptions);
1406
+
1407
+ this.config.setOptionSettingOptions(
1408
+ OptionParameters.StreamerId,
1409
+ settingOptions
1410
+ );
1411
+
1412
+ let wantedStreamerId: string = null;
1413
+ let autoSelectedStreamerId: string = null;
1414
+
1415
+ const waitForStreamer = this.config.isFlagEnabled(
1416
+ Flags.WaitForStreamer
1417
+ );
1418
+ const reconnectLimit = this.config.getNumericSettingValue(
1419
+ NumericParameters.MaxReconnectAttempts
1420
+ );
1421
+ const reconnectDelay = this.config.getNumericSettingValue(
1422
+ NumericParameters.StreamerAutoJoinInterval
1423
+ );
1424
+
1425
+ // 首先,我们通过各种方式找出一个通缉的主播id
1426
+ const urlParams = new URLSearchParams(window.location.search);
1427
+ if (urlParams.has(OptionParameters.StreamerId)) {
1428
+ // 如果我们已经在url上设置了streamer id,我们只想要那个streamer id
1429
+ wantedStreamerId = urlParams.get(OptionParameters.StreamerId);
1430
+ } else if (this.subscribedStream) {
1431
+ // 我们之前订阅了一个主播,我们想要那个
1432
+ wantedStreamerId = this.subscribedStream;
1433
+ }
1434
+
1435
+ // 现在我们看看能不能选出来。
1436
+ if (
1437
+ wantedStreamerId &&
1438
+ messageStreamerList.ids.includes(wantedStreamerId)
1439
+ ) {
1440
+ // 如果想要的流在列表中。我们选择它
1441
+ autoSelectedStreamerId = wantedStreamerId;
1442
+ } else if (
1443
+ (!wantedStreamerId || !waitForStreamer) &&
1444
+ messageStreamerList.ids.length == 1
1445
+ ) {
1446
+ // 否则,如果我们没有等待想要的streamer,而且只有一个streamer,请连接到它
1447
+ autoSelectedStreamerId = messageStreamerList.ids[0];
1448
+ }
1449
+
1450
+ // 如果我们找到了自动选择的streamer id,请选择它
1451
+ if (autoSelectedStreamerId) {
1452
+ this.isReconnecting = false;
1453
+ this.reconnectAttempt = 0;
1454
+ this.config.setOptionSettingValue(
1455
+ OptionParameters.StreamerId,
1456
+ autoSelectedStreamerId
1457
+ );
1458
+ } else {
1459
+ //没有自动选择的streamer。
1460
+ //如果我们正在等待streamer,那么尝试重新连接
1461
+ if (waitForStreamer) {
1462
+ if (this.reconnectAttempt < reconnectLimit) {
1463
+ // still reconnects available
1464
+ this.isReconnecting = true;
1465
+ this.reconnectAttempt++;
1466
+ setTimeout(() => {
1467
+ // console.log(1121);
1468
+ this.webSocketController.requestStreamerList();
1469
+ }, reconnectDelay);
1470
+ } else {
1471
+ // We've exhausted our reconnect attempts, return to main screen
1472
+ this.reconnectAttempt = 0;
1473
+ this.isReconnecting = false;
1474
+ this.shouldReconnect = false;
1475
+ }
1476
+ }
1477
+ }
1478
+
1479
+ // 最后Dispatch这个事件
1480
+ this.pixelStreaming.dispatchEvent(
1481
+ new StreamerListMessageEvent({
1482
+ messageStreamerList,
1483
+ autoSelectedStreamerId,
1484
+ wantedStreamerId
1485
+ })
1486
+ );
1487
+ }
1488
+
1489
+ /**
1490
+ * 处理发信服务器的RTC应答
1491
+ * @param Answer - Answer SDP from the peer.
1492
+ */
1493
+ handleWebRtcAnswer(Answer: MessageAnswer) {
1494
+ Logger.Log(Logger.GetStackTrace(), `Got answer sdp ${Answer.sdp}`, 6);
1495
+
1496
+ const sdpAnswer: RTCSessionDescriptionInit = {
1497
+ sdp: Answer.sdp,
1498
+ type: 'answer'
1499
+ };
1500
+
1501
+ this.peerConnectionController.receiveAnswer(sdpAnswer);
1502
+ this.handlePostWebrtcNegotiation();
1503
+ }
1504
+
1505
+ /**
1506
+ * Handle the RTC offer from a WebRTC peer (received through the signalling server).
1507
+ * @param Offer - Offer SDP from the peer.
1508
+ */
1509
+ handleWebRtcOffer(Offer: MessageOffer) {
1510
+ Logger.Log(Logger.GetStackTrace(), `Got offer sdp ${Offer.sdp}`, 6);
1511
+
1512
+ this.isUsingSFU = Offer.sfu ? Offer.sfu : false;
1513
+ if (this.isUsingSFU) {
1514
+ // Disable negotiating with the sfu as the sfu only supports one codec at a time
1515
+ this.peerConnectionController.preferredCodec = '';
1516
+ }
1517
+
1518
+ const sdpOffer: RTCSessionDescriptionInit = {
1519
+ sdp: Offer.sdp,
1520
+ type: 'offer'
1521
+ };
1522
+
1523
+ this.peerConnectionController.receiveOffer(sdpOffer, this.config);
1524
+ this.handlePostWebrtcNegotiation();
1525
+ }
1526
+
1527
+ /**
1528
+ * Handle when the SFU provides the peer with its data channels
1529
+ * @param DataChannels - The message from the SFU containing the data channels ids
1530
+ */
1531
+ handleWebRtcSFUPeerDatachannels(
1532
+ DataChannels: MessageReceive.MessagePeerDataChannels
1533
+ ) {
1534
+ const SendOptions: RTCDataChannelInit = {
1535
+ ordered: true,
1536
+ negotiated: true,
1537
+ id: DataChannels.sendStreamId
1538
+ };
1539
+
1540
+ const unidirectional =
1541
+ DataChannels.sendStreamId != DataChannels.recvStreamId;
1542
+
1543
+ this.sendrecvDataChannelController.createDataChannel(
1544
+ this.peerConnectionController.peerConnection,
1545
+ unidirectional ? 'send-datachannel' : 'datachannel',
1546
+ SendOptions
1547
+ );
1548
+
1549
+ if (unidirectional) {
1550
+ const RecvOptions: RTCDataChannelInit = {
1551
+ ordered: true,
1552
+ negotiated: true,
1553
+ id: DataChannels.recvStreamId
1554
+ };
1555
+
1556
+ this.recvDataChannelController.createDataChannel(
1557
+ this.peerConnectionController.peerConnection,
1558
+ 'recv-datachannel',
1559
+ RecvOptions
1560
+ );
1561
+ this.recvDataChannelController.handleOnOpen = () =>
1562
+ this.webSocketController.sendSFURecvDataChannelReady();
1563
+ // If we're uni-directional, only the recv data channel should handle incoming messages
1564
+ this.recvDataChannelController.handleOnMessage = (
1565
+ ev: MessageEvent
1566
+ ) => this.handleOnMessage(ev);
1567
+ } else {
1568
+ // else our primary datachannel is send/recv so it can handle incoming messages
1569
+ this.sendrecvDataChannelController.handleOnMessage = (
1570
+ ev: MessageEvent
1571
+ ) => this.handleOnMessage(ev);
1572
+ }
1573
+ }
1574
+
1575
+ handlePostWebrtcNegotiation() {
1576
+ // start the afk warning timer as PS is now running
1577
+ this.afkController.startAfkWarningTimer();
1578
+ // show the overlay that we have negotiated a connection
1579
+ this.pixelStreaming._onWebRtcSdp();
1580
+
1581
+ if (this.statsTimerHandle && this.statsTimerHandle !== undefined) {
1582
+ window.clearInterval(this.statsTimerHandle);
1583
+ }
1584
+
1585
+ this.statsTimerHandle = window.setInterval(() => this.getStats(), 1000);
1586
+
1587
+ /* */
1588
+ this.setMouseInputEnabled(this.config.isFlagEnabled(Flags.MouseInput));
1589
+ this.setKeyboardInputEnabled(
1590
+ this.config.isFlagEnabled(Flags.KeyboardInput)
1591
+ );
1592
+ this.setGamePadInputEnabled(
1593
+ this.config.isFlagEnabled(Flags.GamepadInput)
1594
+ );
1595
+ }
1596
+
1597
+ /**
1598
+ * When an ice Candidate is received from the Signaling server add it to the Peer Connection Client
1599
+ * @param iceCandidate - Ice Candidate from Server
1600
+ */
1601
+ handleIceCandidate(iceCandidate: RTCIceCandidateInit) {
1602
+ Logger.Log(
1603
+ Logger.GetStackTrace(),
1604
+ 'Web RTC Controller: onWebRtcIce',
1605
+ 6
1606
+ );
1607
+
1608
+ const candidate = new RTCIceCandidate(iceCandidate);
1609
+ this.peerConnectionController.handleOnIce(candidate);
1610
+ }
1611
+
1612
+ /**
1613
+ * Send the ice Candidate to the signaling server via websocket
1614
+ * @param iceEvent - RTC Peer ConnectionIceEvent) {
1615
+ */
1616
+ handleSendIceCandidate(iceEvent: RTCPeerConnectionIceEvent) {
1617
+ Logger.Log(Logger.GetStackTrace(), 'OnIceCandidate', 6);
1618
+ if (iceEvent.candidate && iceEvent.candidate.candidate) {
1619
+ this.webSocketController.sendIceCandidate(iceEvent.candidate);
1620
+ }
1621
+ }
1622
+
1623
+ /**
1624
+ * Send the ice Candidate to the signaling server via websocket
1625
+ * @param iceEvent - RTC Peer ConnectionIceEvent) {
1626
+ */
1627
+ handleDataChannel(datachannelEvent: RTCDataChannelEvent) {
1628
+ Logger.Log(
1629
+ Logger.GetStackTrace(),
1630
+ 'Data channel created for us by browser as we are a receiving peer.',
1631
+ 6
1632
+ );
1633
+ this.sendrecvDataChannelController.dataChannel =
1634
+ datachannelEvent.channel;
1635
+ // Data channel was created for us, so we just need to setup its callbacks and array type
1636
+ this.sendrecvDataChannelController.setupDataChannel();
1637
+ this.sendrecvDataChannelController.handleOnMessage = (
1638
+ ev: MessageEvent<ArrayBuffer>
1639
+ ) => this.handleOnMessage(ev);
1640
+ }
1641
+
1642
+ /**
1643
+ * Send the RTC Offer Session to the Signaling server via websocket
1644
+ * @param offer - RTC Session Description
1645
+ */
1646
+ handleSendWebRTCOffer(offer: RTCSessionDescriptionInit) {
1647
+ Logger.Log(
1648
+ Logger.GetStackTrace(),
1649
+ 'Sending the offer to the Server',
1650
+ 6
1651
+ );
1652
+ this.webSocketController.sendWebRtcOffer(offer);
1653
+ }
1654
+
1655
+ /**
1656
+ * Send the RTC Offer Session to the Signaling server via websocket
1657
+ * @param answer - RTC Session Description
1658
+ */
1659
+ handleSendWebRTCAnswer(answer: RTCSessionDescriptionInit) {
1660
+ Logger.Log(
1661
+ Logger.GetStackTrace(),
1662
+ 'Sending the answer to the Server',
1663
+ 6
1664
+ );
1665
+ this.webSocketController.sendWebRtcAnswer(answer);
1666
+
1667
+ if (this.isUsingSFU) {
1668
+ this.webSocketController.sendWebRtcDatachannelRequest();
1669
+ }
1670
+ }
1671
+
1672
+ /**
1673
+ * Set the freeze frame overlay to the player div
1674
+ */
1675
+ setUpMouseAndFreezeFrame() {
1676
+ // Calculating and normalizing positions depends on the width and height of the player.
1677
+ this.videoElementParentClientRect = this.videoPlayer
1678
+ .getVideoParentElement()
1679
+ .getBoundingClientRect();
1680
+ this.coordinateConverter.setupNormalizeAndQuantize();
1681
+ this.freezeFrameController.freezeFrame.resize();
1682
+ }
1683
+
1684
+ /**
1685
+ * 关闭到发信服务器的连接
1686
+ */
1687
+ closeSignalingServer(message: string) {
1688
+ // We explicitly called close, therefore we don't want to trigger auto reconnect
1689
+ this.locallyClosed = true;
1690
+ this.shouldReconnect = false;
1691
+ this.disconnectMessage = message;
1692
+ this.webSocketController?.close();
1693
+ }
1694
+
1695
+ /**
1696
+ * 关闭对端连接
1697
+ */
1698
+ closePeerConnection() {
1699
+ this.peerConnectionController?.close();
1700
+ }
1701
+
1702
+ /**
1703
+ * 关闭所有连接
1704
+ */
1705
+ close() {
1706
+ this.closeSignalingServer('');
1707
+ this.closePeerConnection();
1708
+ }
1709
+
1710
+ /**
1711
+ * Fires a Video Stats Event in the RTC Peer Connection
1712
+ */
1713
+ getStats() {
1714
+ this.peerConnectionController.generateStats();
1715
+ }
1716
+
1717
+ /**
1718
+ * 向UE实例发送时延测试请求
1719
+ */
1720
+ sendLatencyTest() {
1721
+ this.latencyStartTime = Date.now();
1722
+
1723
+ this.streamMessageController.toStreamerHandlers.get('LatencyTest')([
1724
+ JSON.stringify({
1725
+ StartTime: this.latencyStartTime
1726
+ })
1727
+ ]);
1728
+ }
1729
+
1730
+ /**
1731
+ * Send a Data Channel Latency Test Request to the UE Instance
1732
+ */
1733
+ sendDataChannelLatencyTest(descriptor: DataChannelLatencyTestRequest) {
1734
+ this.streamMessageController.toStreamerHandlers.get(
1735
+ 'DataChannelLatencyTest'
1736
+ )([JSON.stringify(descriptor)]);
1737
+ }
1738
+
1739
+ /**
1740
+ * Send the MinQP encoder setting to the UE Instance.
1741
+ * @param minQP - The lower bound for QP when encoding
1742
+ * valid values are (1-51) where:
1743
+ * 1 = Best quality but highest bitrate.
1744
+ * 51 = Worst quality but lowest bitrate.
1745
+ * By default the minQP is 1 meaning the encoder is free
1746
+ * to aim for the best quality it can on the given network link.
1747
+ */
1748
+ sendEncoderMinQP(minQP: number) {
1749
+ Logger.Log(Logger.GetStackTrace(), `MinQP=${minQP}\n`, 6);
1750
+
1751
+ if (minQP != null) {
1752
+ this.streamMessageController.toStreamerHandlers.get('Command')([
1753
+ JSON.stringify({
1754
+ 'Encoder.MinQP': minQP
1755
+ })
1756
+ ]);
1757
+ }
1758
+ }
1759
+
1760
+ /**
1761
+ * Send the MaxQP encoder setting to the UE Instance.
1762
+ * @param maxQP - The upper bound for QP when encoding
1763
+ * valid values are (1-51) where:
1764
+ * 1 = Best quality but highest bitrate.
1765
+ * 51 = Worst quality but lowest bitrate.
1766
+ * By default the maxQP is 51 meaning the encoder is free
1767
+ * to drop quality as low as needed on the given network link.
1768
+ */
1769
+ sendEncoderMaxQP(maxQP: number) {
1770
+ Logger.Log(Logger.GetStackTrace(), `MaxQP=${maxQP}\n`, 6);
1771
+
1772
+ if (maxQP != null) {
1773
+ this.streamMessageController.toStreamerHandlers.get('Command')([
1774
+ JSON.stringify({
1775
+ 'Encoder.MaxQP': maxQP
1776
+ })
1777
+ ]);
1778
+ }
1779
+ }
1780
+
1781
+ /**
1782
+ * Send the { WebRTC.MinBitrate: SomeNumber }} command to UE to set
1783
+ * the minimum bitrate that we allow WebRTC to use
1784
+ * (note setting this too high in poor networks can be problematic).
1785
+ * @param minBitrate - The minimum bitrate we would like WebRTC to not fall below.
1786
+ */
1787
+ sendWebRTCMinBitrate(minBitrate: number) {
1788
+ Logger.Log(
1789
+ Logger.GetStackTrace(),
1790
+ `WebRTC Min Bitrate=${minBitrate}`,
1791
+ 6
1792
+ );
1793
+ if (minBitrate != null) {
1794
+ this.streamMessageController.toStreamerHandlers.get('Command')([
1795
+ JSON.stringify({
1796
+ 'WebRTC.MinBitrate': minBitrate
1797
+ })
1798
+ ]);
1799
+ }
1800
+ }
1801
+
1802
+ /**
1803
+ * Send the { WebRTC.MaxBitrate: SomeNumber }} command to UE to set
1804
+ * the minimum bitrate that we allow WebRTC to use
1805
+ * (note setting this too low could result in blocky video).
1806
+ * @param minBitrate - The minimum bitrate we would like WebRTC to not fall below.
1807
+ */
1808
+ sendWebRTCMaxBitrate(maxBitrate: number) {
1809
+ Logger.Log(
1810
+ Logger.GetStackTrace(),
1811
+ `WebRTC Max Bitrate=${maxBitrate}`,
1812
+ 6
1813
+ );
1814
+ if (maxBitrate != null) {
1815
+ this.streamMessageController.toStreamerHandlers.get('Command')([
1816
+ JSON.stringify({
1817
+ 'WebRTC.MaxBitrate': maxBitrate
1818
+ })
1819
+ ]);
1820
+ }
1821
+ }
1822
+
1823
+ /**
1824
+ * Send the { WebRTC.Fps: SomeNumber }} UE 5.0+
1825
+ * and { WebRTC.MaxFps } UE 4.27 command to set
1826
+ * the maximum fps we would like WebRTC to stream at.
1827
+ * @param fps - The maximum stream fps.
1828
+ */
1829
+ sendWebRTCFps(fps: number) {
1830
+ Logger.Log(Logger.GetStackTrace(), `WebRTC FPS=${fps}`, 6);
1831
+ if (fps != null) {
1832
+ this.streamMessageController.toStreamerHandlers.get('Command')([
1833
+ JSON.stringify({ 'WebRTC.Fps': fps })
1834
+ ]);
1835
+
1836
+ /* TODO: Remove when UE 4.27 unsupported. */
1837
+ this.streamMessageController.toStreamerHandlers.get('Command')([
1838
+ JSON.stringify({ 'WebRTC.MaxFps': fps })
1839
+ ]);
1840
+ }
1841
+ }
1842
+
1843
+ /**
1844
+ * Sends the UI Descriptor `stat fps` to the UE Instance
1845
+ */
1846
+ sendShowFps(): void {
1847
+ Logger.Log(
1848
+ Logger.GetStackTrace(),
1849
+ '---- Sending show stat to UE ----',
1850
+ 6
1851
+ );
1852
+
1853
+ this.streamMessageController.toStreamerHandlers.get('Command')([
1854
+ JSON.stringify({ 'stat.fps': '' })
1855
+ ]);
1856
+ }
1857
+
1858
+ /**
1859
+ * Send an Iframe request to the streamer
1860
+ */
1861
+ sendIframeRequest(): void {
1862
+ Logger.Log(
1863
+ Logger.GetStackTrace(),
1864
+ '---- Sending Request for an IFrame ----',
1865
+ 6
1866
+ );
1867
+ this.streamMessageController.toStreamerHandlers.get('IFrameRequest')();
1868
+ }
1869
+
1870
+ /**
1871
+ * Send a UIInteraction message
1872
+ */
1873
+ emitUIInteraction(descriptor: object | string) {
1874
+ Logger.Log(
1875
+ Logger.GetStackTrace(),
1876
+ '---- Sending custom UIInteraction message ----',
1877
+ 6
1878
+ );
1879
+
1880
+ this.streamMessageController.toStreamerHandlers.get('UIInteraction')([
1881
+ JSON.stringify(descriptor)
1882
+ ]);
1883
+ }
1884
+
1885
+ /**
1886
+ * Send a Command message
1887
+ */
1888
+ emitCommand(descriptor: object) {
1889
+ Logger.Log(
1890
+ Logger.GetStackTrace(),
1891
+ '---- Sending custom Command message ----',
1892
+ 6
1893
+ );
1894
+
1895
+ this.streamMessageController.toStreamerHandlers.get('Command')([
1896
+ JSON.stringify(descriptor)
1897
+ ]);
1898
+ }
1899
+
1900
+ /**
1901
+ * Send a console command message
1902
+ */
1903
+ emitConsoleCommand(command: string) {
1904
+ Logger.Log(
1905
+ Logger.GetStackTrace(),
1906
+ '---- Sending custom Command:ConsoleCommand message ----',
1907
+ 6
1908
+ );
1909
+
1910
+ this.streamMessageController.toStreamerHandlers.get('Command')([
1911
+ JSON.stringify({
1912
+ ConsoleCommand: command
1913
+ })
1914
+ ]);
1915
+ }
1916
+
1917
+ /**
1918
+ * Sends a request to the UE Instance to have ownership of Quality
1919
+ */
1920
+ sendRequestQualityControlOwnership(): void {
1921
+ Logger.Log(
1922
+ Logger.GetStackTrace(),
1923
+ '---- Sending Request to Control Quality ----',
1924
+ 6
1925
+ );
1926
+ this.toStreamerMessagesController.SendRequestQualityControl();
1927
+ }
1928
+
1929
+ /**
1930
+ * Handles when a Latency Test Result are received from the UE Instance
1931
+ * @param message - Latency Test Timings
1932
+ */
1933
+ handleLatencyTestResult(message: ArrayBuffer) {
1934
+ Logger.Log(
1935
+ Logger.GetStackTrace(),
1936
+ 'DataChannelReceiveMessageType.latencyTest',
1937
+ 6
1938
+ );
1939
+ const latencyAsString = new TextDecoder('utf-16').decode(
1940
+ message.slice(1)
1941
+ );
1942
+ const latencyTestResults: LatencyTestResults = new LatencyTestResults();
1943
+ Object.assign(latencyTestResults, JSON.parse(latencyAsString));
1944
+ latencyTestResults.processFields();
1945
+
1946
+ latencyTestResults.testStartTimeMs = this.latencyStartTime;
1947
+ latencyTestResults.browserReceiptTimeMs = Date.now();
1948
+
1949
+ latencyTestResults.latencyExcludingDecode = ~~(
1950
+ latencyTestResults.browserReceiptTimeMs -
1951
+ latencyTestResults.testStartTimeMs
1952
+ );
1953
+ latencyTestResults.testDuration = ~~(
1954
+ latencyTestResults.TransmissionTimeMs -
1955
+ latencyTestResults.ReceiptTimeMs
1956
+ );
1957
+ latencyTestResults.networkLatency = ~~(
1958
+ latencyTestResults.latencyExcludingDecode -
1959
+ latencyTestResults.testDuration
1960
+ );
1961
+
1962
+ if (
1963
+ latencyTestResults.frameDisplayDeltaTimeMs &&
1964
+ latencyTestResults.browserReceiptTimeMs
1965
+ ) {
1966
+ latencyTestResults.endToEndLatency =
1967
+ ~~(latencyTestResults.frameDisplayDeltaTimeMs +
1968
+ latencyTestResults.networkLatency,
1969
+ +latencyTestResults.CaptureToSendMs);
1970
+ }
1971
+ this.pixelStreaming._onLatencyTestResult(latencyTestResults);
1972
+ }
1973
+
1974
+ /**
1975
+ * Handles when a Data Channel Latency Test Response is received from the UE Instance
1976
+ * @param message - Data Channel Latency Test Response
1977
+ */
1978
+ handleDataChannelLatencyTestResponse(message: ArrayBuffer) {
1979
+ Logger.Log(
1980
+ Logger.GetStackTrace(),
1981
+ 'DataChannelReceiveMessageType.dataChannelLatencyResponse',
1982
+ 6
1983
+ );
1984
+ const responseAsString = new TextDecoder('utf-16').decode(
1985
+ message.slice(1)
1986
+ );
1987
+ const latencyTestResponse: DataChannelLatencyTestResponse =
1988
+ JSON.parse(responseAsString);
1989
+ this.pixelStreaming._onDataChannelLatencyTestResponse(
1990
+ latencyTestResponse
1991
+ );
1992
+ }
1993
+
1994
+ /**
1995
+ * Handles when the Encoder and Web RTC Settings are received from the UE Instance
1996
+ * @param message - Initial Encoder and Web RTC Settings
1997
+ */
1998
+ handleInitialSettings(message: ArrayBuffer) {
1999
+ Logger.Log(
2000
+ Logger.GetStackTrace(),
2001
+ 'DataChannelReceiveMessageType.InitialSettings',
2002
+ 6
2003
+ );
2004
+ const payloadAsString = new TextDecoder('utf-16').decode(
2005
+ message.slice(1)
2006
+ );
2007
+ const parsedInitialSettings = JSON.parse(payloadAsString);
2008
+
2009
+ const initialSettings: InitialSettings = new InitialSettings();
2010
+
2011
+ if (parsedInitialSettings.Encoder) {
2012
+ initialSettings.EncoderSettings = parsedInitialSettings.Encoder;
2013
+ }
2014
+
2015
+ if (parsedInitialSettings.WebRTC) {
2016
+ initialSettings.WebRTCSettings = parsedInitialSettings.WebRTC;
2017
+ }
2018
+
2019
+ if (parsedInitialSettings.PixelStreaming) {
2020
+ initialSettings.PixelStreamingSettings =
2021
+ parsedInitialSettings.PixelStreaming;
2022
+ }
2023
+
2024
+ if (
2025
+ parsedInitialSettings.ConfigOptions &&
2026
+ parsedInitialSettings.ConfigOptions.DefaultToHover !== undefined
2027
+ ) {
2028
+ this.config.setFlagEnabled(
2029
+ Flags.HoveringMouseMode,
2030
+ !!parsedInitialSettings.ConfigOptions.DefaultToHover
2031
+ );
2032
+ }
2033
+
2034
+ initialSettings.ueCompatible();
2035
+ Logger.Log(Logger.GetStackTrace(), payloadAsString, 6);
2036
+
2037
+ this.pixelStreaming._onInitialSettings(initialSettings);
2038
+ }
2039
+
2040
+ /**
2041
+ * Handles when the Quantization Parameter are received from the UE Instance
2042
+ * @param message - Encoders Quantization Parameter
2043
+ */
2044
+ handleVideoEncoderAvgQP(message: ArrayBuffer) {
2045
+ Logger.Log(
2046
+ Logger.GetStackTrace(),
2047
+ 'DataChannelReceiveMessageType.VideoEncoderAvgQP',
2048
+ 6
2049
+ );
2050
+ const AvgQP = Number(
2051
+ new TextDecoder('utf-16').decode(message.slice(1))
2052
+ );
2053
+ this.setVideoEncoderAvgQP(AvgQP);
2054
+ }
2055
+
2056
+ /**
2057
+ * Handles when the video element has been loaded with a srcObject
2058
+ */
2059
+ handleVideoInitialized() {
2060
+ this.pixelStreaming._onVideoInitialized();
2061
+
2062
+ // either autoplay the video or set up the play overlay
2063
+ this.autoPlayVideoOrSetUpPlayOverlay();
2064
+ this.resizePlayerStyle();
2065
+ this.videoPlayer.updateVideoStreamSize();
2066
+ }
2067
+
2068
+ /**
2069
+ * Flag set if the user has Quality Ownership
2070
+ * @param message - Does the current client have Quality Ownership
2071
+ */
2072
+ onQualityControlOwnership(message: ArrayBuffer) {
2073
+ const view = new Uint8Array(message);
2074
+ Logger.Log(
2075
+ Logger.GetStackTrace(),
2076
+ 'DataChannelReceiveMessageType.QualityControlOwnership',
2077
+ 6
2078
+ );
2079
+ this.isQualityController = new Boolean(view[1]).valueOf();
2080
+ Logger.Log(
2081
+ Logger.GetStackTrace(),
2082
+ `Received quality controller message, will control quality: ${this.isQualityController}`
2083
+ );
2084
+ this.pixelStreaming._onQualityControlOwnership(
2085
+ this.isQualityController
2086
+ );
2087
+ }
2088
+
2089
+ /**
2090
+ * Handles when the Aggregated stats are Collected
2091
+ * @param stats - Aggregated Stats
2092
+ */
2093
+ handleVideoStats(stats: AggregatedStats) {
2094
+ this.pixelStreaming._onVideoStats(stats);
2095
+ }
2096
+
2097
+ /**
2098
+ * To Resize the Video Player element
2099
+ */
2100
+ resizePlayerStyle(): void {
2101
+ this.videoPlayer.resizePlayerStyle();
2102
+ }
2103
+
2104
+ setPreferredCodec(codec: string) {
2105
+ this.preferredCodec = codec;
2106
+ if (this.peerConnectionController) {
2107
+ this.peerConnectionController.preferredCodec = codec;
2108
+ this.peerConnectionController.updateCodecSelection = false;
2109
+ }
2110
+ }
2111
+
2112
+ setVideoEncoderAvgQP(avgQP: number) {
2113
+ this.videoAvgQp = avgQP;
2114
+ this.pixelStreaming._onVideoEncoderAvgQP(this.videoAvgQp);
2115
+ }
2116
+
2117
+ /**
2118
+ * enables/disables keyboard event listeners
2119
+ */
2120
+ setKeyboardInputEnabled(isEnabled: boolean) {
2121
+ this.keyboardController?.unregisterKeyBoardEvents();
2122
+ if (isEnabled) {
2123
+ this.keyboardController = this.inputClassesFactory.registerKeyBoard(
2124
+ this.config
2125
+ );
2126
+ }
2127
+ }
2128
+
2129
+ /**
2130
+ * enables/disables mouse event listeners
2131
+ */
2132
+ setMouseInputEnabled(isEnabled: boolean) {
2133
+ this.mouseController?.unregisterMouseEvents();
2134
+ if (isEnabled) {
2135
+ const mouseMode = this.config.isFlagEnabled(Flags.HoveringMouseMode)
2136
+ ? ControlSchemeType.HoveringMouse
2137
+ : ControlSchemeType.LockedMouse;
2138
+ this.mouseController =
2139
+ this.inputClassesFactory.registerMouse(mouseMode);
2140
+ }
2141
+ }
2142
+
2143
+ /**
2144
+ * enables/disables touch event listeners
2145
+ */
2146
+ setTouchInputEnabled(isEnabled: boolean) {
2147
+ this.touchController?.unregisterTouchEvents();
2148
+ if (isEnabled) {
2149
+ this.touchController = this.inputClassesFactory.registerTouch(
2150
+ this.config.isFlagEnabled(Flags.FakeMouseWithTouches),
2151
+ this.videoElementParentClientRect
2152
+ );
2153
+ }
2154
+ }
2155
+
2156
+ /**
2157
+ * enables/disables game pad event listeners
2158
+ */
2159
+ setGamePadInputEnabled(isEnabled: boolean) {
2160
+ this.gamePadController?.unregisterGamePadEvents();
2161
+ if (isEnabled) {
2162
+ this.gamePadController = this.inputClassesFactory.registerGamePad();
2163
+ this.gamePadController.onGamepadConnected = () => {
2164
+ this.streamMessageController.toStreamerHandlers.get(
2165
+ 'GamepadConnected'
2166
+ )();
2167
+ };
2168
+ this.gamePadController.onGamepadDisconnected = (
2169
+ controllerIdx: number
2170
+ ) => {
2171
+ this.streamMessageController.toStreamerHandlers.get(
2172
+ 'GamepadDisconnected'
2173
+ )([controllerIdx]);
2174
+ };
2175
+ }
2176
+ }
2177
+
2178
+ registerDataChannelEventEmitters(dataChannel: DataChannelController) {
2179
+ dataChannel.onOpen = (label, event) =>
2180
+ this.pixelStreaming.dispatchEvent(
2181
+ new DataChannelOpenEvent({ label, event })
2182
+ );
2183
+ dataChannel.onClose = (label, event) =>
2184
+ this.pixelStreaming.dispatchEvent(
2185
+ new DataChannelCloseEvent({ label, event })
2186
+ );
2187
+ dataChannel.onError = (label, event) =>
2188
+ this.pixelStreaming.dispatchEvent(
2189
+ new DataChannelErrorEvent({ label, event })
2190
+ );
2191
+ }
2192
+
2193
+ public registerMessageHandler(
2194
+ name: string,
2195
+ direction: MessageDirection,
2196
+ handler?: (data: ArrayBuffer | Array<number | string>) => void
2197
+ ) {
2198
+ if (
2199
+ direction === MessageDirection.FromStreamer &&
2200
+ typeof handler === 'undefined'
2201
+ ) {
2202
+ Logger.Warning(
2203
+ Logger.GetStackTrace(),
2204
+ `Unable to register handler for ${name} as no handler was passed`
2205
+ );
2206
+ }
2207
+
2208
+ this.streamMessageController.registerMessageHandler(
2209
+ direction,
2210
+ name,
2211
+ (data: Array<number | string>) =>
2212
+ typeof handler === 'undefined' &&
2213
+ direction === MessageDirection.ToStreamer
2214
+ ? this.sendMessageController.sendMessageToStreamer(
2215
+ name,
2216
+ data
2217
+ )
2218
+ : handler(data)
2219
+ );
2220
+ }
2221
+ }