diff --git a/Frontend/Docs/Accessing the Pixel Streaming Blueprint API.md b/Frontend/Docs/Accessing the Pixel Streaming Blueprint API.md index 9859cbea..d2886c82 100644 --- a/Frontend/Docs/Accessing the Pixel Streaming Blueprint API.md +++ b/Frontend/Docs/Accessing the Pixel Streaming Blueprint API.md @@ -1,5 +1,3 @@ -**TO DO**: Update this information to match the new front end. - ## Accessing the Pixel Streaming Blueprint API The Pixel Streaming Plugin that runs within the Unreal Engine exposes a Blueprint API that you can use in your gameplay logic to handle custom UI events sent by the player HTML page, and to emit events from the Unreal Engine to the player page. @@ -7,7 +5,9 @@ The Pixel Streaming Plugin that runs within the Unreal Engine exposes a Blueprin To access this Blueprint API, add the **PixelStreamingInputComponent** to an Actor in your level. Your application's **PlayerController** is a safe choice. You can do this by clicking **Add Component** in the Blueprint menu and selecting the **Pixel Streaming Input** component from the dropdown. -![Adding the Pixel Streaming component.](Resources\Images\pixelstreaming-add-component.jpg) +

+ Adding the Pixel Streaming component +

**_NOTE:_** Prior to UE 4.27, the PixelStreamingInput Component was automatically added when you loaded the Pixel Streaming plugin. This was problematic, and now requires users to add this to their project themselves, as seen above. diff --git a/Frontend/Docs/Communicating from UE5 to the Player Page.md b/Frontend/Docs/Communicating from UE5 to the Player Page.md index eb4cb659..97074eb1 100644 --- a/Frontend/Docs/Communicating from UE5 to the Player Page.md +++ b/Frontend/Docs/Communicating from UE5 to the Player Page.md @@ -9,7 +9,9 @@ To set this up: 1. In your Unreal Engine application, any time you want to emit an event to the player page, use the **Pixel Streaming > Send Pixel Streaming Response** node. Specify a custom string argument to the node to indicate to the player page what event has happened. - ![](Docs\Resources\Images\pixelstreaming-send-game-event.JPG) +

+ Send game event +

2. In the JavaScript of your player page, you'll need to write a custom event handler function that will be invoked each time the page receives a response event from the Unreal Engine application. It will be passed the original string argument that was sent by the **Send Pixel Streaming Response** node. For example: @@ -33,6 +35,10 @@ To set this up: **_Tip:_** If you want to pass more complex data, you can format the string you pass to the **Send Pixel Streaming Response** node as JSON. For example: -![Send Pixel Streaming response using JSON](Resources\Images\pixelstreaming-send-game-event-json.png "Send Pixel Streaming response using JSON") + +

+ Send Pixel Streaming response using JSON +

+ Then, in your JavaScript event handler function, use `JSON.parse(data)` to decode the string back into a JavaScript object. diff --git a/Frontend/Docs/Communicating from the Player Page to UE5.md b/Frontend/Docs/Communicating from the Player Page to UE5.md index 92abe560..7077e9c6 100644 --- a/Frontend/Docs/Communicating from the Player Page to UE5.md +++ b/Frontend/Docs/Communicating from the Player Page to UE5.md @@ -4,8 +4,8 @@ The `app.js` file provides two JavaScript functions that you can call in your HTML player page to allow the user to send events and commands from the browser to the Unreal Engine application: -* You can use `emitCommand` to send console commands back to Unreal Engine. For example, `stat fps` to show the frame rate. See [Using the emitCommand Function below](#usingtheemitcommandfunction). -* `emitUIInteraction` sends any arbitrary string or JavaScript object to the game. Use this function to send your own custom commands from your player UI, which you can respond to in your gameplay logic to produce any effect you need in your application. See [Using the emitUIInteraction Function below](#usingtheemituiinteractionfunction). +* You can use `emitCommand` to send console commands back to Unreal Engine. For example, `stat fps` to show the frame rate. See [Using the emitCommand Function below](#using-the-emitcommand-function). +* `emitUIInteraction` sends any arbitrary string or JavaScript object to the game. Use this function to send your own custom commands from your player UI, which you can respond to in your gameplay logic to produce any effect you need in your application. See [Using the emitUIInteraction Function below](#using-the-emituiinteraction-function). ### Using the emitCommand Function @@ -41,7 +41,9 @@ or If you pass a JavaScript object, the `emitUIInteraction` function converts it to a JSON string internally. It then passes the resulting string back to the Pixel Streaming Plugin in your Unreal Engine application, which raises an event on the input controller. In your application's gameplay logic, you bind your own custom event to handle these inputs, using the **Bind Event to OnPixelStreamingInputEvent** node. For example: -![Bind Event to OnPixelStreamingInputEvent](Resources\Images\pixelstreaming-uiinteractionrespond.JPG "Bind Event to OnPixelStreamingInputEvent") +

+ Bind Event to OnPixelStreamingInputEvent +

You need to bind this event once, typically at the start of your game. Each time any player HTML page connected to an instance of your Unreal Engine application calls the `emitUIInteraction`function, your custom event is automatically invoked, regardless of the input passed to `emitUIInteraction`. @@ -50,11 +52,16 @@ The custom event you assign (for example, the **UI Interaction** node in the ima For example, the following Blueprint tests to see whether the input given to `emitUIInteraction` contains the string "MyCustomCommand", and calls a custom function to handle the event: -![Search for substring](Resources\Images\pixelstreaming-respond-searchsubstring.JPG "Search for substring") +

+ Search for substring +

If you originally passed a JavaScript object to `emitUIInteraction`, you can retrieve the value of any key from that JSON object using the **Pixel Streaming > Get Json String Value** node. For example, the following Blueprint tests for a key named LoadLevel. If that key is present, it calls a custom function to handle the event: -[![Get a JSON field value](Resources\Images\pixelstreaming-respond-json.JPG "Get a JSON field value")](pixelstreaming-respond-json.JPG) + +

+ Get a JSON field value +

**_Tip:_** diff --git a/Frontend/Docs/Customizing the Player Webpage.md b/Frontend/Docs/Customizing the Player Webpage.md index 1a36b00c..ed30bcc2 100644 --- a/Frontend/Docs/Customizing the Player Webpage.md +++ b/Frontend/Docs/Customizing the Player Webpage.md @@ -18,11 +18,4 @@ However, with a little creativity and some knowledge of web technologies like J We recommend using the default player page as a starting point for creating your own custom player page. You'll find this page at `PixelStreamingInfrastructure\SignallingWebServer\Public\player.html` under your Unreal Engine installation folder. Then, use the information on this page to learn how to extend your page and tie it in with your Project's gameplay logic. -Additionally, if you have cloned the Pixel Streaming Infrastructure repository and made upstream changes, you can fork the repo and make a pull request. - -_The default Pixel Streaming player page:_ -![PixelStreamingDefaultPlayer](Resources\Images\pixelstreaming-default-interface.JPG) - -_A customised Pixel Streaming player page:_ -![PixelStreamingCustomPlayer](Resources\Images\pixelstreaming-custom-player.JPG) - +Additionally, if you have cloned the Pixel Streaming Infrastructure repository and made upstream changes, you can fork the repo and make a pull request. \ No newline at end of file diff --git a/Frontend/Docs/HTML Page Requirements.md b/Frontend/Docs/HTML Page Requirements.md index 990450d6..371f8948 100644 --- a/Frontend/Docs/HTML Page Requirements.md +++ b/Frontend/Docs/HTML Page Requirements.md @@ -30,4 +30,4 @@ You have a few options for where you can place your custom HTML player page, an For example, if you save a file to `Engine/Source/Programs/PixelStreaming/WebServers/SignallingWebServer/myfolder/myplayerpage.html`, and you set the `HomepageFile` parameter to `myfolder/myplayerpage.html`, the page would be accessible without needing to provide a file name in the URL: `http://127.0.0.1/`. * You can also use the **AdditionalRoutes** parameter for the Signaling and Web Server to customize the mapping between URL paths and local folders on your computer. -For additional details on these parameters, see also the [Pixel Streaming Reference](sharing-and-releasing-projects/pixel-streaming/pixel-streaming-reference). +For additional details on these parameters, see also the [Pixel Streaming Reference](https://docs.unrealengine.com/5.1/en-US/unreal-engine-pixel-streaming-reference/). diff --git a/Frontend/Docs/Timing Out Inactive Connections.md b/Frontend/Docs/Timing Out Inactive Connections.md index 968630ec..ddfeb545 100644 --- a/Frontend/Docs/Timing Out Inactive Connections.md +++ b/Frontend/Docs/Timing Out Inactive Connections.md @@ -6,7 +6,9 @@ In some kinds of Pixel Streaming deployments, you may want to automatically disc You can configure your Pixel Streaming player page to detect when a user appears to be away from keyboard (AFK)—that is, when the user has not interacted with the player widget within a customizable time interval. The AFK system warns the user: -![AFK timeout warning](Resources\Images\afk-warning.png "AFK timeout warning") +

+ AFK timeout warning +

If the user continues not to respond, the AFK system ultimately disconnects their session. diff --git a/Frontend/implementations/EpicGames/src/player.ts b/Frontend/implementations/EpicGames/src/player.ts index c229b809..77565315 100644 --- a/Frontend/implementations/EpicGames/src/player.ts +++ b/Frontend/implementations/EpicGames/src/player.ts @@ -14,8 +14,8 @@ document.body.onload = function() { const config = new Config({ useUrlParams: true }); // Create a Native DOM delegate instance that implements the Delegate interface class - const pixelStreaming = new PixelStreaming(config); - const application = new Application({ pixelStreaming }); + const stream = new PixelStreaming(config); + const application = new Application({ stream }); // document.getElementById("centrebox").appendChild(application.rootElement); document.body.appendChild(application.rootElement); } diff --git a/Frontend/implementations/EpicGames/src/stresstest.ts b/Frontend/implementations/EpicGames/src/stresstest.ts index 2e96c7db..5a86dbd6 100644 --- a/Frontend/implementations/EpicGames/src/stresstest.ts +++ b/Frontend/implementations/EpicGames/src/stresstest.ts @@ -145,8 +145,8 @@ export class StressTester { config.setFlagEnabled(Flags.StartVideoMuted, true); // Create a Native DOM delegate instance that implements the Delegate interface class - const pixelStreaming = new PixelStreaming(config); - const application = new Application({ pixelStreaming }); + const stream = new PixelStreaming(config); + const application = new Application({ stream }); streamFrame.appendChild(application.rootElement); return streamFrame; } diff --git a/Frontend/library/src/PixelStreaming/PixelStreaming.ts b/Frontend/library/src/PixelStreaming/PixelStreaming.ts index eb3fb3c5..e647b6f0 100644 --- a/Frontend/library/src/PixelStreaming/PixelStreaming.ts +++ b/Frontend/library/src/PixelStreaming/PixelStreaming.ts @@ -6,11 +6,7 @@ import { AggregatedStats } from '../PeerConnectionController/AggregatedStats'; import { WebRtcPlayerController } from '../WebRtcPlayer/WebRtcPlayerController'; import { Flags, NumericParameters } from '../Config/Config'; import { Logger } from '../Logger/Logger'; -import { - InitialSettings, - EncoderSettings, - WebRTCSettings -} from '../DataChannel/InitialSettings'; +import { InitialSettings } from '../DataChannel/InitialSettings'; import { OnScreenKeyboard } from '../UI/OnScreenKeyboard'; import { EventEmitter, @@ -46,8 +42,8 @@ export interface PixelStreamingOverrides { * this will likely be the core of your Pixel Streaming experience in terms of functionality. */ export class PixelStreaming { - private webRtcController: WebRtcPlayerController; - private webXrController: WebXRController; + private _webRtcController: WebRtcPlayerController; + private _webXrController: WebXRController; /** * Configuration object. You can read or modify config through this object. Whenever * the configuration is changed, the library will emit a `settingsChanged` event. @@ -94,14 +90,14 @@ export class PixelStreaming { x: number, y: number ) => - this.webRtcController.requestUnquantizedAndDenormalizeUnsigned( + this._webRtcController.requestUnquantizedAndDenormalizeUnsigned( x, y ); this._activateOnScreenKeyboard = (command: MessageOnScreenKeyboard) => this.onScreenKeyboardHelper.showOnScreenKeyboard(command); - this.webXrController = new WebXRController(this.webRtcController); + this._webXrController = new WebXRController(this._webRtcController); } /** @@ -126,9 +122,9 @@ export class PixelStreaming { // and we aren't currently quality controller, send the request if ( wantsQualityController === true && - !this.webRtcController.isQualityController + !this._webRtcController.isQualityController ) { - this.webRtcController.sendRequestQualityControlOwnership(); + this._webRtcController.sendRequestQualityControlOwnership(); } } ); @@ -136,14 +132,14 @@ export class PixelStreaming { this.config._addOnSettingChangedListener( Flags.AFKDetection, (isAFKEnabled: boolean) => { - this.webRtcController.setAfkEnabled(isAFKEnabled); + this._webRtcController.setAfkEnabled(isAFKEnabled); } ); this.config._addOnSettingChangedListener( Flags.MatchViewportResolution, () => { - this.webRtcController.videoPlayer.updateVideoStreamSize(); + this._webRtcController.videoPlayer.updateVideoStreamSize(); } ); @@ -156,7 +152,7 @@ export class PixelStreaming { isHoveringMouse ? 'Hovering' : 'Locked' } Mouse` ); - this.webRtcController.activateRegisterMouse(); + this._webRtcController.activateRegisterMouse(); } ); @@ -169,7 +165,7 @@ export class PixelStreaming { '-------- Sending MinQP --------', 7 ); - this.webRtcController.sendEncoderMinQP(newValue); + this._webRtcController.sendEncoderMinQP(newValue); Logger.Log( Logger.GetStackTrace(), '-------------------------------------------', @@ -186,7 +182,7 @@ export class PixelStreaming { '-------- Sending encoder settings --------', 7 ); - this.webRtcController.sendEncoderMaxQP(newValue); + this._webRtcController.sendEncoderMaxQP(newValue); Logger.Log( Logger.GetStackTrace(), '-------------------------------------------', @@ -204,7 +200,7 @@ export class PixelStreaming { '-------- Sending web rtc settings --------', 7 ); - this.webRtcController.sendWebRTCMinBitrate(newValue * 1000 /* kbps to bps */); + this._webRtcController.sendWebRTCMinBitrate(newValue * 1000 /* kbps to bps */); Logger.Log( Logger.GetStackTrace(), '-------------------------------------------', @@ -221,7 +217,7 @@ export class PixelStreaming { '-------- Sending web rtc settings --------', 7 ); - this.webRtcController.sendWebRTCMaxBitrate(newValue * 1000 /* kbps to bps */); + this._webRtcController.sendWebRTCMaxBitrate(newValue * 1000 /* kbps to bps */); Logger.Log( Logger.GetStackTrace(), '-------------------------------------------', @@ -238,7 +234,7 @@ export class PixelStreaming { '-------- Sending web rtc settings --------', 7 ); - this.webRtcController.sendWebRTCFps(newValue); + this._webRtcController.sendWebRTCFps(newValue); Logger.Log( Logger.GetStackTrace(), '-------------------------------------------', @@ -250,8 +246,8 @@ export class PixelStreaming { this.config._addOnOptionSettingChangedListener( OptionParameters.PreferredCodec, (newValue: string) => { - if (this.webRtcController) { - this.webRtcController.setPreferredCodec(newValue); + if (this._webRtcController) { + this._webRtcController.setPreferredCodec(newValue); } } ); @@ -283,13 +279,13 @@ export class PixelStreaming { private setWebRtcPlayerController( webRtcPlayerController: WebRtcPlayerController ) { - this.webRtcController = webRtcPlayerController; + this._webRtcController = webRtcPlayerController; - this.webRtcController.setPreferredCodec( + this._webRtcController.setPreferredCodec( this.config.getSettingOption(OptionParameters.PreferredCodec) .selected ); - this.webRtcController.resizePlayerStyle(); + this._webRtcController.resizePlayerStyle(); // connect if auto connect flag is enabled this.checkForAutoConnect(); @@ -299,7 +295,7 @@ export class PixelStreaming { * Connect to signaling server. */ public connect() { - this.webRtcController.connectToSignallingServer(); + this._webRtcController.connectToSignallingServer(); } /** @@ -307,14 +303,14 @@ export class PixelStreaming { * before establishing a new connection */ public reconnect() { - this.webRtcController.restartStreamAutomatically(); + this._webRtcController.restartStreamAutomatically(); } /** * Disconnect from the signaling server and close open peer connections. */ public disconnect() { - this.webRtcController.close(); + this._webRtcController.close(); } /** @@ -322,7 +318,7 @@ export class PixelStreaming { */ public play() { this._onStreamLoading(); - this.webRtcController.playStream(); + this._webRtcController.playStream(); } /** @@ -333,7 +329,7 @@ export class PixelStreaming { if (this.config.isFlagEnabled(Flags.AutoConnect)) { // if autoplaying show an info overlay while while waiting for the connection to begin this._onWebRtcAutoConnect(); - this.webRtcController.connectToSignallingServer(); + this._webRtcController.connectToSignallingServer(); } } @@ -367,13 +363,13 @@ export class PixelStreaming { _onDisconnect(eventString: string) { // if we have overridden the default disconnection message, assign the new value here if ( - this.webRtcController.getDisconnectMessageOverride() != '' && - this.webRtcController.getDisconnectMessageOverride() !== + this._webRtcController.getDisconnectMessageOverride() != '' && + this._webRtcController.getDisconnectMessageOverride() !== undefined && - this.webRtcController.getDisconnectMessageOverride() != null + this._webRtcController.getDisconnectMessageOverride() != null ) { - eventString = this.webRtcController.getDisconnectMessageOverride(); - this.webRtcController.setDisconnectMessageOverride(''); + eventString = this._webRtcController.getDisconnectMessageOverride(); + this._webRtcController.setDisconnectMessageOverride(''); } this._eventEmitter.dispatchEvent( @@ -439,7 +435,7 @@ export class PixelStreaming { videoStats.handleSessionStatistics( this._videoStartTime, this._inputController, - this.webRtcController.videoAvgQp + this._webRtcController.videoAvgQp ); this._eventEmitter.dispatchEvent( @@ -518,10 +514,10 @@ export class PixelStreaming { * @returns */ public requestLatencyTest() { - if (!this.webRtcController.videoPlayer.isVideoReady()) { + if (!this._webRtcController.videoPlayer.isVideoReady()) { return false; } - this.webRtcController.sendLatencyTest(); + this._webRtcController.sendLatencyTest(); return true; } @@ -531,10 +527,10 @@ export class PixelStreaming { * @returns */ public requestShowFps() { - if (!this.webRtcController.videoPlayer.isVideoReady()) { + if (!this._webRtcController.videoPlayer.isVideoReady()) { return false; } - this.webRtcController.sendShowFps(); + this._webRtcController.sendShowFps(); return true; } @@ -544,10 +540,10 @@ export class PixelStreaming { * @returns */ public requestIframe() { - if (!this.webRtcController.videoPlayer.isVideoReady()) { + if (!this._webRtcController.videoPlayer.isVideoReady()) { return false; } - this.webRtcController.sendIframeRequest(); + this._webRtcController.sendIframeRequest(); return true; } @@ -590,4 +586,19 @@ export class PixelStreaming { public toggleXR() { this.webXrController.xrClicked(); } + + /** + * Public getter for the websocket controller. Access to this property allows you to send + * custom websocket messages. + */ + public get webSocketController() { + return this._webRtcController.webSocketController; + } + + /** + * Public getter for the webXrController controller. Used for all XR features. + */ + public get webXrController() { + return this._webXrController; + } } diff --git a/Frontend/library/src/Util/EventEmitter.ts b/Frontend/library/src/Util/EventEmitter.ts index c172ebdd..89eb3a51 100644 --- a/Frontend/library/src/Util/EventEmitter.ts +++ b/Frontend/library/src/Util/EventEmitter.ts @@ -401,6 +401,45 @@ export class SettingsChangedEvent extends Event { } } +/** + * Event emitted when an XR Session starts + */ +export class XrSessionStartedEvent extends Event { + readonly type: 'xrSessionStarted'; + constructor() { + super('xrSessionStarted'); + } +} + +/** + * Event emitted when an XR Session ends + */ +export class XrSessionEndedEvent extends Event { + readonly type: 'xrSessionEnded'; + constructor() { + super('xrSessionEnded'); + } +} + +export type XrFrameData = { + /** The frame timestamp */ + time: DOMHighResTimeStamp; + /** The XRFrame */ + frame: XRFrame; +}; + +/** + * Event emitted when an XR Frame is complete + */ +export class XrFrameEvent extends Event { + readonly type: 'xrFrame'; + readonly data: XrFrameData + constructor(data: XrFrameEvent['data']) { + super('xrFrame'); + this.data = data; + } +} + export type PixelStreamingEvent = | AfkWarningActivateEvent | AfkWarningUpdateEvent @@ -427,7 +466,10 @@ export type PixelStreamingEvent = | StreamerListMessageEvent | LatencyTestResultEvent | InitialSettingsEvent - | SettingsChangedEvent; + | SettingsChangedEvent + | XrSessionStartedEvent + | XrSessionEndedEvent + | XrFrameEvent; export class EventEmitter extends EventTarget { /** diff --git a/Frontend/library/src/WebXR/WebXRController.ts b/Frontend/library/src/WebXR/WebXRController.ts index 955f7af2..c2d087b6 100644 --- a/Frontend/library/src/WebXR/WebXRController.ts +++ b/Frontend/library/src/WebXR/WebXRController.ts @@ -1,27 +1,32 @@ // Copyright Epic Games, Inc. All Rights Reserved. import { Logger } from '../Logger/Logger'; -import { WebRtcPlayerController } from '../pixelstreamingfrontend'; +import { WebRtcPlayerController } from '../WebRtcPlayer/WebRtcPlayerController'; import { WebGLUtils } from '../Util/WebGLUtils'; import { Controller } from '../Inputs/GamepadTypes'; import { XRGamepadController } from '../Inputs/XRGamepadController'; +import { XrFrameEvent } from '../Util/EventEmitter' export class WebXRController { - xrSession: XRSession; - xrRefSpace: XRReferenceSpace; - gl: WebGL2RenderingContext; + private xrSession: XRSession; + private xrRefSpace: XRReferenceSpace; + private gl: WebGL2RenderingContext; - positionLocation: number; - texcoordLocation: number; - resolutionLocation: WebGLUniformLocation; - offsetLocation: WebGLUniformLocation; + private positionLocation: number; + private texcoordLocation: number; + private resolutionLocation: WebGLUniformLocation; + private offsetLocation: WebGLUniformLocation; - positionBuffer: WebGLBuffer; - texcoordBuffer: WebGLBuffer; + private positionBuffer: WebGLBuffer; + private texcoordBuffer: WebGLBuffer; - webRtcController: WebRtcPlayerController; - xrGamepadController: XRGamepadController; - xrControllers: Array; + private webRtcController: WebRtcPlayerController; + private xrGamepadController: XRGamepadController; + private xrControllers: Array; + + onSessionStarted: EventTarget; + onSessionEnded: EventTarget; + onFrame: EventTarget; constructor(webRtcPlayerController: WebRtcPlayerController) { this.xrSession = null; @@ -30,6 +35,9 @@ export class WebXRController { this.xrGamepadController = new XRGamepadController( this.webRtcController.streamMessageController ); + this.onSessionEnded = new EventTarget(); + this.onSessionStarted = new EventTarget(); + this.onFrame = new EventTarget(); } public xrClicked() { @@ -47,6 +55,7 @@ export class WebXRController { onXrSessionEnded() { Logger.Log(Logger.GetStackTrace(), 'XR Session ended'); this.xrSession = null; + this.onSessionEnded.dispatchEvent(new Event('xrSessionEnded')); } onXrSessionStarted(session: XRSession) { @@ -142,6 +151,8 @@ export class WebXRController { this.onXrFrame(time, frame) ); }); + + this.onSessionStarted.dispatchEvent(new Event('xrSessionStarted')); } onXrFrame(time: DOMHighResTimeStamp, frame: XRFrame) { @@ -194,6 +205,11 @@ export class WebXRController { (time: DOMHighResTimeStamp, frame: XRFrame) => this.onXrFrame(time, frame) ); + + this.onFrame.dispatchEvent(new XrFrameEvent({ + time, + frame + })); } private render(videoElement: HTMLVideoElement) { diff --git a/Frontend/ui-library/src/Application/Application.ts b/Frontend/ui-library/src/Application/Application.ts index 84d81da9..531cf819 100644 --- a/Frontend/ui-library/src/Application/Application.ts +++ b/Frontend/ui-library/src/Application/Application.ts @@ -26,14 +26,14 @@ import { VideoQpIndicator } from '../UI/VideoQpIndicator'; import { ConfigUI, LightMode } from '../Config/ConfigUI'; export interface UIOptions { - pixelStreaming: PixelStreaming; + stream: PixelStreaming; } /** * Provides common base functionality for applications that extend this application */ export class Application { - pixelStreaming: PixelStreaming; + stream: PixelStreaming; _rootElement: HTMLElement; _uiFeatureElement: HTMLElement; @@ -59,8 +59,8 @@ export class Application { * @param options - Initialization options */ constructor(options: UIOptions) { - this.pixelStreaming = options.pixelStreaming; - this.configUI = new ConfigUI(this.pixelStreaming.config); + this.stream = options.stream; + this.configUI = new ConfigUI(this.stream.config); this.createOverlays(); @@ -90,31 +90,31 @@ export class Application { public createOverlays(): void { // build all of the overlays this.disconnectOverlay = new DisconnectOverlay( - this.pixelStreaming.videoElementParent + this.stream.videoElementParent ); this.connectOverlay = new ConnectOverlay( - this.pixelStreaming.videoElementParent + this.stream.videoElementParent ); this.playOverlay = new PlayOverlay( - this.pixelStreaming.videoElementParent + this.stream.videoElementParent ); this.infoOverlay = new InfoOverlay( - this.pixelStreaming.videoElementParent + this.stream.videoElementParent ); this.errorOverlay = new ErrorOverlay( - this.pixelStreaming.videoElementParent + this.stream.videoElementParent ); this.afkOverlay = new AFKOverlay( - this.pixelStreaming.videoElementParent + this.stream.videoElementParent ); - this.disconnectOverlay.onAction(() => this.pixelStreaming.reconnect()); + this.disconnectOverlay.onAction(() => this.stream.reconnect()); // Build the webRtc connect overlay Event Listener and show the connect overlay - this.connectOverlay.onAction(() => this.pixelStreaming.connect()); + this.connectOverlay.onAction(() => this.stream.connect()); // set up the play overlays action - this.playOverlay.onAction(() => this.pixelStreaming.play()); + this.playOverlay.onAction(() => this.stream.play()); } /** @@ -136,7 +136,7 @@ export class Application { // Add WebXR button to controls controls.xrIcon.rootElement.onclick = () => - this.pixelStreaming.toggleXR(); + this.stream.toggleXR(); // setup the stats/info button controls.statsIcon.rootElement.onclick = () => this.statsClicked(); @@ -146,7 +146,7 @@ export class Application { // Add button for toggle fps const showFPSButton = new LabelledButton('Show FPS', 'Toggle'); showFPSButton.addOnClickListener(() => { - this.pixelStreaming.requestShowFps(); + this.stream.requestShowFps(); }); // Add button for restart stream @@ -155,7 +155,7 @@ export class Application { 'Restart' ); restartStreamButton.addOnClickListener(() => { - this.pixelStreaming.reconnect(); + this.stream.reconnect(); }); // Add button for request keyframe @@ -164,7 +164,7 @@ export class Application { 'Request' ); requestKeyframeButton.addOnClickListener(() => { - this.pixelStreaming.requestIframe(); + this.stream.requestIframe(); }); const commandsSectionElem = this.configUI.buildSectionWithHeading( @@ -198,85 +198,85 @@ export class Application { } registerCallbacks() { - this.pixelStreaming.addEventListener( + this.stream.addEventListener( 'afkWarningActivate', ({ data: { countDown, dismissAfk } }) => this.showAfkOverlay(countDown, dismissAfk) ); - this.pixelStreaming.addEventListener( + this.stream.addEventListener( 'afkWarningUpdate', ({ data: { countDown } }) => this.afkOverlay.updateCountdown(countDown) ); - this.pixelStreaming.addEventListener( + this.stream.addEventListener( 'afkWarningDeactivate', () => this.afkOverlay.hide() ); - this.pixelStreaming.addEventListener('afkTimedOut', () => + this.stream.addEventListener('afkTimedOut', () => this.afkOverlay.hide() ); - this.pixelStreaming.addEventListener( + this.stream.addEventListener( 'videoEncoderAvgQP', ({ data: { avgQP } }) => this.onVideoEncoderAvgQP(avgQP) ); - this.pixelStreaming.addEventListener('webRtcSdp', () => + this.stream.addEventListener('webRtcSdp', () => this.onWebRtcSdp() ); - this.pixelStreaming.addEventListener('webRtcAutoConnect', () => + this.stream.addEventListener('webRtcAutoConnect', () => this.onWebRtcAutoConnect() ); - this.pixelStreaming.addEventListener('webRtcConnecting', () => + this.stream.addEventListener('webRtcConnecting', () => this.onWebRtcConnecting() ); - this.pixelStreaming.addEventListener('webRtcConnected', () => + this.stream.addEventListener('webRtcConnected', () => this.onWebRtcConnected() ); - this.pixelStreaming.addEventListener('webRtcFailed', () => + this.stream.addEventListener('webRtcFailed', () => this.onWebRtcFailed() ); - this.pixelStreaming.addEventListener( + this.stream.addEventListener( 'webRtcDisconnected', ({ data: { eventString, showActionOrErrorOnDisconnect } }) => this.onDisconnect(eventString, showActionOrErrorOnDisconnect) ); - this.pixelStreaming.addEventListener('videoInitialized', () => + this.stream.addEventListener('videoInitialized', () => this.onVideoInitialized() ); - this.pixelStreaming.addEventListener('streamLoading', () => + this.stream.addEventListener('streamLoading', () => this.onStreamLoading() ); - this.pixelStreaming.addEventListener( + this.stream.addEventListener( 'playStreamError', ({ data: { message } }) => this.onPlayStreamError(message) ); - this.pixelStreaming.addEventListener('playStream', () => + this.stream.addEventListener('playStream', () => this.onPlayStream() ); - this.pixelStreaming.addEventListener( + this.stream.addEventListener( 'playStreamRejected', ({ data: { reason } }) => this.onPlayStreamRejected(reason) ); - this.pixelStreaming.addEventListener( + this.stream.addEventListener( 'loadFreezeFrame', ({ data: { shouldShowPlayOverlay } }) => this.onLoadFreezeFrame(shouldShowPlayOverlay) ); - this.pixelStreaming.addEventListener( + this.stream.addEventListener( 'statsReceived', ({ data: { aggregatedStats } }) => this.onStatsReceived(aggregatedStats) ); - this.pixelStreaming.addEventListener( + this.stream.addEventListener( 'latencyTestResult', ({ data: { latencyTimings } }) => this.onLatencyTestResults(latencyTimings) ); - this.pixelStreaming.addEventListener( + this.stream.addEventListener( 'streamerListMessage', ({ data: { messageStreamerList, autoSelectedStreamerId } }) => this.handleStreamerListMessage(messageStreamerList, autoSelectedStreamerId) ); - this.pixelStreaming.addEventListener( + this.stream.addEventListener( 'settingsChanged', (event) => this.configUI.onSettingsChanged(event) ); @@ -291,7 +291,7 @@ export class Application { this._rootElement.id = 'playerUI'; this._rootElement.classList.add('noselect'); this._rootElement.appendChild( - this.pixelStreaming.videoElementParent + this.stream.videoElementParent ); this._rootElement.appendChild(this.uiFeaturesElement); } @@ -432,7 +432,7 @@ export class Application { */ showConnectOrAutoConnectOverlays() { // set up if the auto play will be used or regular click to start - if (!this.pixelStreaming.config.isFlagEnabled(Flags.AutoConnect)) { + if (!this.stream.config.isFlagEnabled(Flags.AutoConnect)) { this.showConnectOverlay(); } } @@ -531,13 +531,13 @@ export class Application { } onVideoInitialized() { - if (!this.pixelStreaming.config.isFlagEnabled(Flags.AutoPlayVideo)) { + if (!this.stream.config.isFlagEnabled(Flags.AutoPlayVideo)) { this.showPlayOverlay(); } // starting a latency check this.statsPanel.latencyTest.latencyTestButton.onclick = () => { - this.pixelStreaming.requestLatencyTest(); + this.stream.requestLatencyTest(); }; } diff --git a/Frontend/ui-library/src/pixelstreamingfrontend-ui.ts b/Frontend/ui-library/src/pixelstreamingfrontend-ui.ts index e9b1ced0..c11b5a29 100644 --- a/Frontend/ui-library/src/pixelstreamingfrontend-ui.ts +++ b/Frontend/ui-library/src/pixelstreamingfrontend-ui.ts @@ -1,6 +1,6 @@ // Copyright Epic Games, Inc. All Rights Reserved. -export { Application } from './Application/Application'; +export { Application, UIOptions } from './Application/Application'; export { PixelStreamingApplicationStyle } from './Styles/PixelStreamingApplicationStyles'; @@ -14,3 +14,8 @@ export { InfoOverlay } from './Overlay/InfoOverlay'; export { PlayOverlay } from './Overlay/PlayOverlay'; export { TextOverlay } from './Overlay/TextOverlay'; export { ConfigUI } from './Config/ConfigUI'; +export { SettingUIBase } from './Config/SettingUIBase'; +export { SettingUIFlag } from './Config/SettingUIFlag'; +export { SettingUINumber } from './Config/SettingUINumber'; +export { SettingUIOption } from './Config/SettingUIOption'; +export { SettingUIText } from './Config/SettingUIText'; diff --git a/SignallingWebServer/Dockerfile b/SignallingWebServer/Dockerfile index ceb20346..d329a758 100644 --- a/SignallingWebServer/Dockerfile +++ b/SignallingWebServer/Dockerfile @@ -3,10 +3,11 @@ FROM node:lts # Copy the signalling server source code from the build context COPY . /opt/SignallingWebServer +COPY . /opt/Frontend # Install the dependencies for the signalling server WORKDIR /opt/SignallingWebServer -RUN npm install . +RUN ./platform_scripts/bash/setup.sh --build # Expose TCP ports 80 and 443 for player WebSocket connections and web server HTTP(S) access EXPOSE 80