Skip to content

Commit 813dcf1

Browse files
Merge pull request #559 from mcottontensor/backport/UE5.5/pr-546
[UE5.5] Merge pull request #546 from mcottontensor/player_destroy
2 parents 681b87c + 4bcaae0 commit 813dcf1

File tree

4 files changed

+201
-176
lines changed

4 files changed

+201
-176
lines changed
Lines changed: 171 additions & 166 deletions
Original file line numberDiff line numberDiff line change
@@ -1,179 +1,184 @@
11
// Copyright Epic Games, Inc. All Rights Reserved.
22

3-
import { Config, Flags, PixelStreaming } from '@epicgames-ps/lib-pixelstreamingfrontend-ue5.5';
4-
import { Application, PixelStreamingApplicationStyle } from '@epicgames-ps/lib-pixelstreamingfrontend-ui-ue5.5';
5-
const PixelStreamingApplicationStyles =
6-
new PixelStreamingApplicationStyle();
3+
import { Config, Flags, PixelStreaming, Logger, LogLevel } from '@epicgames-ps/lib-pixelstreamingfrontend-ue5.5';
4+
import {
5+
Application,
6+
PixelStreamingApplicationStyle
7+
} from '@epicgames-ps/lib-pixelstreamingfrontend-ui-ue5.5';
8+
const PixelStreamingApplicationStyles = new PixelStreamingApplicationStyle();
79
PixelStreamingApplicationStyles.applyStyleSheet();
810

911
export class PixelStreamingFrame {
10-
element: HTMLElement;
11-
pixelStreamingApp: Application;
12+
element: HTMLElement;
13+
pixelStreamingApp: Application;
1214

13-
constructor(element: HTMLElement, pixelStreamingApp: Application) {
14-
this.element = element;
15-
this.pixelStreamingApp = pixelStreamingApp;
16-
}
15+
constructor(element: HTMLElement, pixelStreamingApp: Application) {
16+
this.element = element;
17+
this.pixelStreamingApp = pixelStreamingApp;
18+
}
1719
}
1820

1921
// This is the entrypoint to the stress test, all setup happens here
2022
export class StressTester {
21-
play: boolean;
22-
maxPeers: number;
23-
totalStreams: number;
24-
streamCreationIntervalMs: number;
25-
streamDeletionIntervalMs: number;
26-
pixelStreamingFrames: Array<PixelStreamingFrame>;
27-
creationIntervalHandle: number;
28-
deletionIntervalHandle: number;
29-
streamsContainer: HTMLElement;
30-
31-
constructor() {
32-
this.play = false;
33-
this.maxPeers = 3;
34-
this.totalStreams = 0;
35-
this.streamCreationIntervalMs = 1000;
36-
this.streamDeletionIntervalMs = 4000;
37-
this.pixelStreamingFrames = [];
38-
this.creationIntervalHandle = null;
39-
this.deletionIntervalHandle = null;
40-
// Get a container to put the "Pixel Streaming" pages in.
41-
this.streamsContainer = document.getElementById("streamsContainer");
42-
}
43-
44-
startStressTest() : void {
45-
this.setupNumPeersSlider();
46-
this.startStreamCreation();
47-
this.startStreamDeletion();
48-
this.setupPlayPause();
49-
50-
document.getElementById("creationIntervalInput").onchange = (event : Event) => {
51-
const inputElem = document.getElementById("creationIntervalInput") as HTMLInputElement;
52-
const parsedValue = Number.parseInt(inputElem.value);
53-
if(!Number.isNaN(parsedValue)) {
54-
this.streamCreationIntervalMs = parsedValue * 1000.0;
55-
this.startStreamCreation();
56-
}
57-
}
58-
59-
document.getElementById("deletionIntervalInput").onchange = (event: Event) => {
60-
const inputElem = document.getElementById("deletionIntervalInput") as HTMLInputElement;
61-
const parsedValue = Number.parseInt(inputElem.value);
62-
if (!Number.isNaN(parsedValue)) {
63-
this.streamDeletionIntervalMs = parsedValue * 1000.0;
64-
this.startStreamDeletion();
65-
}
66-
}
67-
68-
const creationIntervalInput = document.getElementById("creationIntervalInput") as HTMLInputElement;
69-
creationIntervalInput.value = (this.streamCreationIntervalMs / 1000.0).toString();
70-
71-
const deletionIntervalInput = document.getElementById("deletionIntervalInput") as HTMLInputElement;
72-
deletionIntervalInput.value = (this.streamDeletionIntervalMs / 1000.0).toString();
73-
}
74-
75-
76-
private setupNumPeersSlider() : void {
77-
const nPeersSlider: HTMLInputElement = document.getElementById("nPeersSlider") as HTMLInputElement;
78-
nPeersSlider.value = this.maxPeers.toString();
79-
80-
const nPeersLabel: HTMLElement = document.getElementById("nPeerLabel");
81-
nPeersLabel.innerHTML = this.maxPeers.toString();
82-
83-
nPeersSlider.onchange = (event: Event) => {
84-
const inputElem = event.target as HTMLInputElement;
85-
const parsedValue = Number.parseInt(inputElem.value);
86-
87-
if (!Number.isNaN(parsedValue)) {
88-
this.maxPeers = parsedValue;
89-
const nPeersLabel: HTMLElement = document.getElementById("nPeerLabel");
90-
nPeersLabel.innerHTML = this.maxPeers.toString();
91-
}
92-
}
93-
}
94-
95-
private startStreamCreation() : void {
96-
if (this.creationIntervalHandle) {
97-
clearInterval(this.creationIntervalHandle);
98-
}
99-
100-
this.creationIntervalHandle = window.setInterval(() => {
101-
if(this.play) {
102-
const curNPeers = this.pixelStreamingFrames.length;
103-
if(curNPeers >= this.maxPeers) return;
104-
105-
const maxPeersToCreate = this.maxPeers - curNPeers;
106-
const nPeersToCreate = Math.ceil(Math.random() * maxPeersToCreate);
107-
108-
for(let i = 0; i < nPeersToCreate; i++) {
109-
const psFrame = this.createPixelStreamingFrame();
110-
const n = this.pixelStreamingFrames.length;
111-
psFrame.element.id = `PixelStreamingFrame_${n + 1}`;
112-
this.streamsContainer.append(psFrame.element);
113-
this.pixelStreamingFrames.push(psFrame);
114-
this.totalStreams += 1;
115-
this.updateTotalStreams();
116-
}
117-
}
118-
}, this.streamCreationIntervalMs);
119-
}
120-
121-
private startStreamDeletion() : void {
122-
if(this.deletionIntervalHandle) {
123-
clearInterval(this.deletionIntervalHandle)
124-
}
125-
126-
this.deletionIntervalHandle = window.setInterval(() => {
127-
if(!this.play) return;
128-
129-
const curNPeers = this.pixelStreamingFrames.length;
130-
if(curNPeers === 0) return;
131-
132-
const nPeersToDelete = Math.ceil(Math.random() * curNPeers);
133-
for(let i = 0; i < nPeersToDelete; i++) {
134-
const psFrame = this.pixelStreamingFrames.shift();
135-
// Remove HTML element from DOM
136-
psFrame.element.parentNode.removeChild(psFrame.element);
137-
// Disconnect Pixel Streaming application so we don't have orphaned WebRTC/WebSocket/PeerConnections
138-
psFrame.pixelStreamingApp.stream.disconnect();
139-
}
140-
}, this.streamDeletionIntervalMs);
141-
}
142-
143-
private setupPlayPause() : void {
144-
const playPauseBtn = document.getElementById("playPause");
145-
playPauseBtn.innerHTML = this.play ? "Pause" : "Play";
146-
147-
playPauseBtn.onclick = (event : Event) => {
148-
this.play = !this.play;
149-
playPauseBtn.innerHTML = this.play ? "Pause" : "Play";
150-
}
151-
}
152-
153-
private createPixelStreamingFrame() : PixelStreamingFrame {
154-
const streamFrame = document.createElement("div");
155-
156-
const config = new Config();
157-
config.setFlagEnabled(Flags.AutoConnect, true);
158-
config.setFlagEnabled(Flags.AutoPlayVideo, true);
159-
config.setFlagEnabled(Flags.StartVideoMuted, true);
160-
161-
// Create a Native DOM delegate instance that implements the Delegate interface class
162-
const stream = new PixelStreaming(config);
163-
const application = new Application({
164-
stream,
165-
onColorModeChanged: (isLightMode : any) => PixelStreamingApplicationStyles.setColorMode(isLightMode)
166-
});
167-
streamFrame.appendChild(application.rootElement);
168-
169-
return new PixelStreamingFrame(streamFrame, application);
170-
}
171-
172-
private updateTotalStreams() : void {
173-
const nStreamsLabel = document.getElementById("nStreamsLabel");
174-
nStreamsLabel.innerHTML = this.totalStreams.toString();
175-
}
23+
play: boolean;
24+
maxPeers: number;
25+
totalStreams: number;
26+
streamCreationIntervalMs: number;
27+
streamDeletionIntervalMs: number;
28+
pixelStreamingFrames: Array<PixelStreamingFrame>;
29+
creationIntervalHandle: number;
30+
deletionIntervalHandle: number;
31+
streamsContainer: HTMLElement;
32+
33+
constructor() {
34+
this.play = false;
35+
this.maxPeers = 3;
36+
this.totalStreams = 0;
37+
this.streamCreationIntervalMs = 1000;
38+
this.streamDeletionIntervalMs = 4000;
39+
this.pixelStreamingFrames = [];
40+
this.creationIntervalHandle = null;
41+
this.deletionIntervalHandle = null;
42+
// Get a container to put the "Pixel Streaming" pages in.
43+
this.streamsContainer = document.getElementById('streamsContainer');
44+
Logger.InitLogging(LogLevel.Warning, true);
45+
}
46+
47+
startStressTest(): void {
48+
this.setupNumPeersSlider();
49+
this.startStreamCreation();
50+
this.startStreamDeletion();
51+
this.setupPlayPause();
52+
53+
document.getElementById('creationIntervalInput').onchange = (event: Event) => {
54+
const inputElem = document.getElementById('creationIntervalInput') as HTMLInputElement;
55+
const parsedValue = Number.parseInt(inputElem.value);
56+
if (!Number.isNaN(parsedValue)) {
57+
this.streamCreationIntervalMs = parsedValue * 1000.0;
58+
this.startStreamCreation();
59+
}
60+
};
61+
62+
document.getElementById('deletionIntervalInput').onchange = (event: Event) => {
63+
const inputElem = document.getElementById('deletionIntervalInput') as HTMLInputElement;
64+
const parsedValue = Number.parseInt(inputElem.value);
65+
if (!Number.isNaN(parsedValue)) {
66+
this.streamDeletionIntervalMs = parsedValue * 1000.0;
67+
this.startStreamDeletion();
68+
}
69+
};
70+
71+
const creationIntervalInput = document.getElementById('creationIntervalInput') as HTMLInputElement;
72+
creationIntervalInput.value = (this.streamCreationIntervalMs / 1000.0).toString();
73+
74+
const deletionIntervalInput = document.getElementById('deletionIntervalInput') as HTMLInputElement;
75+
deletionIntervalInput.value = (this.streamDeletionIntervalMs / 1000.0).toString();
76+
}
77+
78+
private setupNumPeersSlider(): void {
79+
const nPeersSlider: HTMLInputElement = document.getElementById('nPeersSlider') as HTMLInputElement;
80+
nPeersSlider.value = this.maxPeers.toString();
81+
82+
const nPeersLabel: HTMLElement = document.getElementById('nPeerLabel');
83+
nPeersLabel.innerHTML = this.maxPeers.toString();
84+
85+
nPeersSlider.onchange = (event: Event) => {
86+
const inputElem = event.target as HTMLInputElement;
87+
const parsedValue = Number.parseInt(inputElem.value);
88+
89+
if (!Number.isNaN(parsedValue)) {
90+
this.maxPeers = parsedValue;
91+
const nPeersLabel: HTMLElement = document.getElementById('nPeerLabel');
92+
nPeersLabel.innerHTML = this.maxPeers.toString();
93+
}
94+
};
95+
}
96+
97+
private startStreamCreation(): void {
98+
if (this.creationIntervalHandle) {
99+
clearInterval(this.creationIntervalHandle);
100+
}
101+
102+
this.creationIntervalHandle = window.setInterval(() => {
103+
if (this.play) {
104+
const curNPeers = this.pixelStreamingFrames.length;
105+
if (curNPeers >= this.maxPeers) return;
106+
107+
const maxPeersToCreate = this.maxPeers - curNPeers;
108+
const nPeersToCreate = Math.ceil(Math.random() * maxPeersToCreate);
109+
110+
for (let i = 0; i < nPeersToCreate; i++) {
111+
const psFrame = this.createPixelStreamingFrame();
112+
const n = this.pixelStreamingFrames.length;
113+
psFrame.element.id = `PixelStreamingFrame_${n + 1}`;
114+
this.streamsContainer.append(psFrame.element);
115+
this.pixelStreamingFrames.push(psFrame);
116+
this.totalStreams += 1;
117+
this.updateTotalStreams();
118+
}
119+
}
120+
}, this.streamCreationIntervalMs);
121+
}
122+
123+
private startStreamDeletion(): void {
124+
if (this.deletionIntervalHandle) {
125+
clearInterval(this.deletionIntervalHandle);
126+
}
127+
128+
this.deletionIntervalHandle = window.setInterval(() => {
129+
if (!this.play) return;
130+
131+
const curNPeers = this.pixelStreamingFrames.length;
132+
if (curNPeers === 0) return;
133+
134+
const nPeersToDelete = Math.ceil(Math.random() * curNPeers);
135+
for (let i = 0; i < nPeersToDelete; i++) {
136+
const psFrame = this.pixelStreamingFrames.shift();
137+
// Remove HTML element from DOM
138+
psFrame.element.parentNode.removeChild(psFrame.element);
139+
// Disconnect Pixel Streaming application so we don't have orphaned WebRTC/WebSocket/PeerConnections
140+
psFrame.pixelStreamingApp.stream.disconnect();
141+
psFrame?.pixelStreamingApp.stream.webRtcController.destroyVideoPlayer();
142+
}
143+
}, this.streamDeletionIntervalMs);
144+
}
145+
146+
private setupPlayPause(): void {
147+
const playPauseBtn = document.getElementById('playPause');
148+
playPauseBtn.innerHTML = this.play ? 'Pause' : 'Play';
149+
150+
playPauseBtn.onclick = (event: Event) => {
151+
this.play = !this.play;
152+
playPauseBtn.innerHTML = this.play ? 'Pause' : 'Play';
153+
};
154+
}
155+
156+
private createPixelStreamingFrame(): PixelStreamingFrame {
157+
const streamFrame = document.createElement('div');
158+
159+
const config = new Config();
160+
config.setFlagEnabled(Flags.AutoConnect, true);
161+
config.setFlagEnabled(Flags.AutoPlayVideo, true);
162+
config.setFlagEnabled(Flags.StartVideoMuted, true);
163+
164+
// Create a Native DOM delegate instance that implements the Delegate interface class
165+
const stream = new PixelStreaming(config);
166+
const application = new Application({
167+
stream,
168+
onColorModeChanged: (isLightMode: any) =>
169+
PixelStreamingApplicationStyles.setColorMode(isLightMode)
170+
});
171+
streamFrame.appendChild(application.rootElement);
172+
173+
return new PixelStreamingFrame(streamFrame, application);
174+
}
175+
176+
private updateTotalStreams(): void {
177+
const nStreamsLabel = document.getElementById('nStreamsLabel');
178+
nStreamsLabel.innerHTML = this.totalStreams.toString();
179+
}
176180
}
177181

178182
const tester = new StressTester();
179-
tester.startStressTest();
183+
tester.startStressTest();
184+

Frontend/library/src/Inputs/MouseController.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -58,30 +58,30 @@ export class MouseController implements IInputController {
5858
}
5959

6060
registerMouseEnterAndLeaveEvents() {
61-
const videoElementParent = this.videoPlayer.getVideoParentElement() as HTMLDivElement;
62-
videoElementParent.addEventListener('mouseenter', this.onEnterListener);
63-
videoElementParent.addEventListener('mouseleave', this.onLeaveListener);
61+
const videoElementParent = this.videoPlayer.getVideoParentElement();
62+
videoElementParent?.addEventListener('mouseenter', this.onEnterListener);
63+
videoElementParent?.addEventListener('mouseleave', this.onLeaveListener);
6464
}
6565

6666
unregisterMouseEnterAndLeaveEvents() {
67-
const videoElementParent = this.videoPlayer.getVideoParentElement() as HTMLDivElement;
68-
videoElementParent.removeEventListener('mouseenter', this.onEnterListener);
69-
videoElementParent.removeEventListener('mouseleave', this.onLeaveListener);
67+
const videoElementParent = this.videoPlayer.getVideoParentElement();
68+
videoElementParent?.removeEventListener('mouseenter', this.onEnterListener);
69+
videoElementParent?.removeEventListener('mouseleave', this.onLeaveListener);
7070
}
7171

7272
private onMouseEnter(event: MouseEvent) {
7373
if (!this.videoPlayer.isVideoReady()) {
7474
return;
7575
}
76-
this.streamMessageController.toStreamerHandlers.get('MouseEnter')();
76+
this.streamMessageController.toStreamerHandlers.get('MouseEnter')?.();
7777
this.pressMouseButtons(event.buttons, event.x, event.y);
7878
}
7979

8080
private onMouseLeave(event: MouseEvent) {
8181
if (!this.videoPlayer.isVideoReady()) {
8282
return;
8383
}
84-
this.streamMessageController.toStreamerHandlers.get('MouseLeave')();
84+
this.streamMessageController.toStreamerHandlers.get('MouseLeave')?.();
8585
this.releaseMouseButtons(event.buttons, event.x, event.y);
8686
}
8787

0 commit comments

Comments
 (0)