@epicgames-ps/lib-pixelstreamingfrontend-ue5.5 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) 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/dist/lib-pixelstreamingfrontend.esm.js +1 -0
  7. package/dist/lib-pixelstreamingfrontend.js +1 -0
  8. package/jest.config.js +18 -0
  9. package/package.json +48 -0
  10. package/readme.md +15 -0
  11. package/src/AFK/AFKController.test.ts +162 -0
  12. package/src/AFK/AFKController.ts +158 -0
  13. package/src/Config/Config.test.ts +222 -0
  14. package/src/Config/Config.ts +970 -0
  15. package/src/Config/SettingBase.ts +65 -0
  16. package/src/Config/SettingFlag.ts +99 -0
  17. package/src/Config/SettingNumber.ts +111 -0
  18. package/src/Config/SettingOption.ts +124 -0
  19. package/src/Config/SettingText.ts +82 -0
  20. package/src/DataChannel/DataChannelController.ts +138 -0
  21. package/src/DataChannel/DataChannelLatencyTestController.ts +129 -0
  22. package/src/DataChannel/DataChannelLatencyTestResults.ts +67 -0
  23. package/src/DataChannel/DataChannelSender.ts +59 -0
  24. package/src/DataChannel/InitialSettings.ts +61 -0
  25. package/src/DataChannel/LatencyTestResults.ts +76 -0
  26. package/src/FreezeFrame/FreezeFrame.ts +114 -0
  27. package/src/FreezeFrame/FreezeFrameController.ts +114 -0
  28. package/src/Inputs/FakeTouchController.ts +199 -0
  29. package/src/Inputs/GamepadController.ts +314 -0
  30. package/src/Inputs/GamepadTypes.ts +10 -0
  31. package/src/Inputs/HoveringMouseEvents.ts +192 -0
  32. package/src/Inputs/IMouseEvents.ts +64 -0
  33. package/src/Inputs/ITouchController.ts +29 -0
  34. package/src/Inputs/InputClassesFactory.ts +140 -0
  35. package/src/Inputs/KeyboardController.ts +354 -0
  36. package/src/Inputs/LockedMouseEvents.ts +287 -0
  37. package/src/Inputs/MouseButtons.ts +25 -0
  38. package/src/Inputs/MouseController.ts +362 -0
  39. package/src/Inputs/SpecialKeyCodes.ts +16 -0
  40. package/src/Inputs/TouchController.ts +208 -0
  41. package/src/Inputs/XRGamepadController.ts +126 -0
  42. package/src/PeerConnectionController/AggregatedStats.ts +311 -0
  43. package/src/PeerConnectionController/CandidatePairStats.ts +17 -0
  44. package/src/PeerConnectionController/CandidateStat.ts +13 -0
  45. package/src/PeerConnectionController/CodecStats.ts +19 -0
  46. package/src/PeerConnectionController/DataChannelStats.ts +17 -0
  47. package/src/PeerConnectionController/InboundRTPStats.ts +154 -0
  48. package/src/PeerConnectionController/InboundTrackStats.ts +34 -0
  49. package/src/PeerConnectionController/OutBoundRTPStats.ts +26 -0
  50. package/src/PeerConnectionController/PeerConnectionController.ts +563 -0
  51. package/src/PeerConnectionController/SessionStats.ts +10 -0
  52. package/src/PeerConnectionController/StreamStats.ts +11 -0
  53. package/src/PixelStreaming/PixelStreaming.test.ts +626 -0
  54. package/src/PixelStreaming/PixelStreaming.ts +851 -0
  55. package/src/UI/OnScreenKeyboard.ts +97 -0
  56. package/src/UeInstanceMessage/ResponseController.ts +47 -0
  57. package/src/UeInstanceMessage/SendMessageController.ts +154 -0
  58. package/src/UeInstanceMessage/StreamMessageController.ts +233 -0
  59. package/src/UeInstanceMessage/ToStreamerMessagesController.ts +62 -0
  60. package/src/Util/CoordinateConverter.ts +289 -0
  61. package/src/Util/EventEmitter.ts +611 -0
  62. package/src/Util/EventListenerTracker.ts +29 -0
  63. package/src/Util/FileUtil.ts +140 -0
  64. package/src/Util/RTCUtils.ts +41 -0
  65. package/src/Util/WebGLUtils.ts +49 -0
  66. package/src/Util/WebXRUtils.ts +25 -0
  67. package/src/VideoPlayer/StreamController.ts +89 -0
  68. package/src/VideoPlayer/VideoPlayer.ts +246 -0
  69. package/src/WebRtcPlayer/WebRtcPlayerController.ts +2158 -0
  70. package/src/WebXR/WebXRController.ts +319 -0
  71. package/src/__test__/mockMediaStream.ts +124 -0
  72. package/src/__test__/mockRTCPeerConnection.ts +347 -0
  73. package/src/__test__/mockRTCRtpReceiver.ts +22 -0
  74. package/src/__test__/mockWebSocket.ts +136 -0
  75. package/src/pixelstreamingfrontend.ts +46 -0
  76. package/tsconfig.jest.json +8 -0
  77. package/tsconfig.json +24 -0
  78. package/types/AFK/AFKController.d.ts +39 -0
  79. package/types/Config/Config.d.ts +218 -0
  80. package/types/Config/SettingBase.d.ts +30 -0
  81. package/types/Config/SettingFlag.d.ts +33 -0
  82. package/types/Config/SettingNumber.d.ts +45 -0
  83. package/types/Config/SettingOption.d.ts +43 -0
  84. package/types/Config/SettingText.d.ts +29 -0
  85. package/types/DataChannel/DataChannelController.d.ts +59 -0
  86. package/types/DataChannel/DataChannelLatencyTestController.d.ts +26 -0
  87. package/types/DataChannel/DataChannelLatencyTestResults.d.ts +46 -0
  88. package/types/DataChannel/DataChannelSender.d.ts +21 -0
  89. package/types/DataChannel/InitialSettings.d.ts +44 -0
  90. package/types/DataChannel/LatencyTestResults.d.ts +31 -0
  91. package/types/FreezeFrame/FreezeFrame.d.ts +36 -0
  92. package/types/FreezeFrame/FreezeFrameController.d.ts +37 -0
  93. package/types/Inputs/FakeTouchController.d.ts +61 -0
  94. package/types/Inputs/GamepadController.d.ts +85 -0
  95. package/types/Inputs/GamepadTypes.d.ts +8 -0
  96. package/types/Inputs/HoveringMouseEvents.d.ts +56 -0
  97. package/types/Inputs/IMouseEvents.d.ts +53 -0
  98. package/types/Inputs/ITouchController.d.ts +24 -0
  99. package/types/Inputs/InputClassesFactory.d.ts +54 -0
  100. package/types/Inputs/KeyboardController.d.ts +62 -0
  101. package/types/Inputs/LockedMouseEvents.d.ts +80 -0
  102. package/types/Inputs/MouseButtons.d.ts +22 -0
  103. package/types/Inputs/MouseController.d.ts +75 -0
  104. package/types/Inputs/SpecialKeyCodes.d.ts +14 -0
  105. package/types/Inputs/TouchController.d.ts +53 -0
  106. package/types/Inputs/XRGamepadController.d.ts +15 -0
  107. package/types/PeerConnectionController/AggregatedStats.d.ts +77 -0
  108. package/types/PeerConnectionController/CandidatePairStats.d.ts +15 -0
  109. package/types/PeerConnectionController/CandidateStat.d.ts +11 -0
  110. package/types/PeerConnectionController/CodecStats.d.ts +14 -0
  111. package/types/PeerConnectionController/DataChannelStats.d.ts +15 -0
  112. package/types/PeerConnectionController/InboundRTPStats.d.ts +141 -0
  113. package/types/PeerConnectionController/InboundTrackStats.d.ts +32 -0
  114. package/types/PeerConnectionController/OutBoundRTPStats.d.ts +23 -0
  115. package/types/PeerConnectionController/PeerConnectionController.d.ts +132 -0
  116. package/types/PeerConnectionController/SessionStats.d.ts +8 -0
  117. package/types/PeerConnectionController/StreamStats.d.ts +9 -0
  118. package/types/PixelStreaming/PixelStreaming.d.ts +259 -0
  119. package/types/UI/OnScreenKeyboard.d.ts +31 -0
  120. package/types/UeInstanceMessage/ResponseController.d.ts +19 -0
  121. package/types/UeInstanceMessage/SendMessageController.d.ts +18 -0
  122. package/types/UeInstanceMessage/StreamMessageController.d.ts +29 -0
  123. package/types/UeInstanceMessage/ToStreamerMessagesController.d.ts +32 -0
  124. package/types/Util/CoordinateConverter.d.ts +100 -0
  125. package/types/Util/EventEmitter.d.ts +422 -0
  126. package/types/Util/EventListenerTracker.d.ts +14 -0
  127. package/types/Util/FileUtil.d.ts +32 -0
  128. package/types/Util/RTCUtils.d.ts +8 -0
  129. package/types/Util/WebGLUtils.d.ts +4 -0
  130. package/types/Util/WebXRUtils.d.ts +9 -0
  131. package/types/VideoPlayer/StreamController.d.ts +24 -0
  132. package/types/VideoPlayer/VideoPlayer.d.ts +78 -0
  133. package/types/WebRtcPlayer/WebRtcPlayerController.d.ts +377 -0
  134. package/types/WebXR/WebXRController.d.ts +26 -0
  135. package/types/pixelstreamingfrontend.d.ts +22 -0
  136. package/webpack.common.js +35 -0
  137. package/webpack.dev.js +35 -0
  138. package/webpack.prod.js +36 -0
@@ -0,0 +1,140 @@
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+
3
+ import { Logger } from '@epicgames-ps/lib-pixelstreamingcommon-ue5.5';
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 '@epicgames-ps/lib-pixelstreamingcommon-ue5.5';
5
+ import { VideoPlayer } from './VideoPlayer';
6
+
7
+ /**
8
+ * Video Player Controller handles the creation of the video HTML element and all handlers
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
+ * Creates the audio device when receiving an RTCTrackEvent with the kind of "audio"
67
+ * @param audioMediaStream - Audio Media stream track
68
+ */
69
+ CreateAudioTrack(audioMediaStream: MediaStream) {
70
+ const videoElement = this.videoElementProvider.getVideoElement();
71
+
72
+ // do nothing the video has the same media stream as the audio track we have here (they are linked)
73
+ if (videoElement.srcObject == audioMediaStream) {
74
+ return;
75
+ }
76
+ // video element has some other media stream that is not associated with this audio track
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 '@epicgames-ps/lib-pixelstreamingcommon-ue5.5';
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
+ }