UNPKG

@epicgames-ps/lib-pixelstreamingfrontend-ue5.4

Version:
261 lines (228 loc) 9.35 kB
// Copyright Epic Games, Inc. All Rights Reserved. import { Logger } from '../Logger/Logger'; import * as MessageReceive from './MessageReceive'; import * as MessageSend from './MessageSend'; import { SignallingProtocol } from './SignallingProtocol'; // declare the new method for the websocket interface declare global { interface WebSocket { onmessagebinary?(event?: MessageEvent): void; } } /** * The controller for the WebSocket and all associated methods */ export class WebSocketController { WS_OPEN_STATE = 1; webSocket: WebSocket; onOpen: EventTarget; onClose: EventTarget; signallingProtocol: SignallingProtocol; constructor() { this.onOpen = new EventTarget(); this.onClose = new EventTarget(); this.signallingProtocol = new SignallingProtocol(); SignallingProtocol.setupDefaultHandlers(this); } /** * Connect to the signaling server * @param connectionURL - The Address of the signaling server * @returns - If there is a connection */ connect(connectionURL: string): boolean { Logger.Log(Logger.GetStackTrace(), connectionURL, 6); try { this.webSocket = new WebSocket(connectionURL); this.webSocket.onopen = (event) => this.handleOnOpen(event); this.webSocket.onerror = () => this.handleOnError(); this.webSocket.onclose = (event) => this.handleOnClose(event); this.webSocket.onmessage = (event) => this.handleOnMessage(event); this.webSocket.onmessagebinary = (event) => this.handleOnMessageBinary(event); return true; } catch (error) { Logger.Error(error, error); return false; } } /** * Handles what happens when a message is received in binary form * @param event - Message Received */ handleOnMessageBinary(event: MessageEvent) { // if the event is empty return if (!event || !event.data) { return; } // handle the binary and then handle the message event.data .text() .then((messageString: unknown) => { // build a new message const constructedMessage = new MessageEvent( 'messageFromBinary', { data: messageString } ); // send the new stringified event back into `onmessage` this.handleOnMessage(constructedMessage); }) .catch((error: Error) => { Logger.Error( Logger.GetStackTrace(), `Failed to parse binary blob from websocket, reason: ${error}` ); }); } /** * Handles what happens when a message is received * @param event - Message Received */ handleOnMessage(event: MessageEvent) { // Check if websocket message is binary, if so, stringify it. if (event.data && event.data instanceof Blob) { this.handleOnMessageBinary(event); return; } const message: MessageReceive.MessageRecv = JSON.parse(event.data); Logger.Log( Logger.GetStackTrace(), 'received => \n' + JSON.stringify(JSON.parse(event.data), undefined, 4), 6 ); // Send to our signalling protocol to handle the incoming message this.signallingProtocol.handleMessage(message.type, event.data); } /** * Handles when the Websocket is opened * @param event - Not Used */ // eslint-disable-next-line @typescript-eslint/no-unused-vars handleOnOpen(event: Event) { Logger.Log( Logger.GetStackTrace(), 'Connected to the signalling server via WebSocket', 6 ); this.onOpen.dispatchEvent(new Event('open')); } /** * Handles when there is an error on the websocket * @param event - Error Payload */ handleOnError() { Logger.Error(Logger.GetStackTrace(), 'WebSocket error'); } /** * Handles when the Websocket is closed * @param event - Close Event */ handleOnClose(event: CloseEvent) { Logger.Log( Logger.GetStackTrace(), 'Disconnected to the signalling server via WebSocket: ' + JSON.stringify(event.code) + ' - ' + event.reason ); this.onClose.dispatchEvent(new CustomEvent('close', { 'detail': event })); } requestStreamerList() { const payload = new MessageSend.MessageListStreamers(); this.webSocket.send(payload.payload()); } sendSubscribe(streamerid: string) { const payload = new MessageSend.MessageSubscribe(streamerid); this.webSocket.send(payload.payload()); } sendUnsubscribe() { const payload = new MessageSend.MessageUnsubscribe(); this.webSocket.send(payload.payload()); } sendWebRtcOffer(offer: RTCSessionDescriptionInit, extraParams: MessageSend.ExtraOfferParameters) { const payload = new MessageSend.MessageWebRTCOffer(offer, extraParams); this.webSocket.send(payload.payload()); } sendWebRtcAnswer(answer: RTCSessionDescriptionInit, extraParams: MessageSend.ExtraAnswerParameters) { const payload = new MessageSend.MessageWebRTCAnswer(answer, extraParams); this.webSocket.send(payload.payload()); } sendWebRtcDatachannelRequest() { const payload = new MessageSend.MessageWebRTCDatachannelRequest(); this.webSocket.send(payload.payload()); } sendSFURecvDataChannelReady() { const payload = new MessageSend.MessageSFURecvDataChannelReady(); this.webSocket.send(payload.payload()); } /** * Sends an RTC Ice Candidate to the Server * @param candidate - RTC Ice Candidate */ sendIceCandidate(candidate: RTCIceCandidate) { Logger.Log(Logger.GetStackTrace(), 'Sending Ice Candidate'); if ( this.webSocket && this.webSocket.readyState === this.WS_OPEN_STATE ) { //ws.send(JSON.stringify({ type: 'iceCandidate', candidate: candidate })); const IceCandidate = new MessageSend.MessageIceCandidate(candidate); this.webSocket.send(IceCandidate.payload()); } } /** * Closes the Websocket connection */ close() { this.webSocket?.close(); } /** * The Message Contains the payload of the peer connection options used for the RTC Peer hand shake * @param messageConfig - Config Message received from he signaling server */ // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function onConfig(messageConfig: MessageReceive.MessageConfig) {} /** * The Message contains all the ids of streamers available on the server. * @param messageStreamerList - The message with the list of the available streamer ids. */ // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function onStreamerList(messageStreamerList: MessageReceive.MessageStreamerList) {} /** * The Message contains the new id of a subscribed to streamer. * @param message - Message conaining the new id of the streamer. */ // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function onStreamerIDChanged(message: MessageReceive.MessageStreamerIDChanged) {} /** * @param iceCandidate - Ice Candidate sent from the Signaling server server's RTC hand shake */ // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function onIceCandidate(iceCandidate: RTCIceCandidateInit) {} /** * Event is fired when the websocket receives the answer for the RTC peer Connection * @param messageAnswer - The RTC Answer payload from the signaling server */ // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function onWebRtcAnswer(messageAnswer: MessageReceive.MessageAnswer) {} /** * Event is fired when the websocket receives the offer for the RTC peer Connection * @param messageOffer - The sdp offer */ // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function onWebRtcOffer(messageOffer: MessageReceive.MessageOffer) {} /** * Event is fired when the websocket receives the data channels for the RTC peer Connection from the SFU * @param messageDataChannels - The data channels details */ // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function onWebRtcPeerDataChannels(messageDataChannels: MessageReceive.MessagePeerDataChannels) {} /** * Event is fired when the websocket receives the an updated player count from cirrus * @param MessagePlayerCount - The new player count */ // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function onPlayerCount(playerCount: MessageReceive.MessagePlayerCount) {} }