Skip to content
This repository has been archived by the owner on Mar 1, 2024. It is now read-only.

Fixes for Streamers changing IDs when connecting & Updates to SFU behaviour relating to multi streamer changes. #411

Merged
merged 10 commits into from
Oct 31, 2023
Merged
1 change: 0 additions & 1 deletion Frontend/Docs/Settings Panel.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ This page will be updated with new features and commands as they become availabl
| **Browser send offer** | The browser will start the WebRTC handshake instead of the Unreal Engine application. This is an advanced setting for users customising the frontend. Primarily for backwards compatibility for 4.x versions of the engine. |
| **Use microphone** | Will start receiving audio input from your microphone and transmit it to the Unreal Engine. |
| **Start video muted** | Muted audio when the stream starts. |
| **Prefer SFU** | Will attempt to use the Selective Forwarding Unit (SFU), if you have one running. |
| **Is quality controller?** | Makes the encoder of the Pixel Streaming Plugin use the current browser connection to determine the bandwidth available, and therefore the quality of the stream encoding. **See notes below** |
| **Force mono audio** | Force the browser to request mono audio in the SDP. |
| **Force TURN** | Will attempt to connect exclusively via the TURN server. Will not work without an active TURN server. |
Expand Down
4 changes: 2 additions & 2 deletions Frontend/implementations/typescript/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 0 additions & 12 deletions Frontend/library/src/Config/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ export class Flags {
static FakeMouseWithTouches = 'FakeMouseWithTouches' as const;
static IsQualityController = 'ControlsQuality' as const;
static MatchViewportResolution = 'MatchViewportRes' as const;
static PreferSFU = 'preferSFU' as const;
static StartVideoMuted = 'StartVideoMuted' as const;
static SuppressBrowserKeys = 'SuppressBrowserKeys' as const;
static UseMic = 'UseMic' as const;
Expand Down Expand Up @@ -315,17 +314,6 @@ export class Config {
)
);

this.flags.set(
Flags.PreferSFU,
new SettingFlag(
Flags.PreferSFU,
'Prefer SFU',
'Try to connect to the SFU instead of P2P.',
false,
useUrlParams
)
);
lukehb marked this conversation as resolved.
Show resolved Hide resolved

this.flags.set(
Flags.IsQualityController,
new SettingFlag(
Expand Down
11 changes: 3 additions & 8 deletions Frontend/library/src/WebRtcPlayer/WebRtcPlayerController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1384,12 +1384,6 @@ export class WebRtcPlayerController {
if (messageStreamerList.ids.length == 1) {
// If there's only a single streamer, subscribe to it regardless of what is in the URL
autoSelectedStreamerId = messageStreamerList.ids[0];
} else if (
this.config.isFlagEnabled(Flags.PreferSFU) &&
messageStreamerList.ids.includes('SFU')
) {
// If the SFU toggle is on and there's an SFU connected, subscribe to it regardless of what is in the URL
autoSelectedStreamerId = 'SFU';
} else if (
urlParams.has(OptionParameters.StreamerId) &&
messageStreamerList.ids.includes(
Expand All @@ -1406,8 +1400,9 @@ export class WebRtcPlayerController {
);
} else {
// no auto selected streamer
if (this.config.isFlagEnabled(Flags.WaitForStreamer)) {
this.startAutoJoinTimer()
if (messageStreamerList.ids.length == 0 && this.config.isFlagEnabled(Flags.WaitForStreamer)) {
this.closeSignalingServer();
this.startAutoJoinTimer();
}
}
this.pixelStreaming.dispatchEvent(
Expand Down
4 changes: 0 additions & 4 deletions Frontend/ui-library/src/Config/ConfigUI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,10 +174,6 @@ export class ConfigUI {
psSettingsSection,
this.flagsUi.get(Flags.StartVideoMuted)
);
this.addSettingFlag(
psSettingsSection,
this.flagsUi.get(Flags.PreferSFU)
);
lukehb marked this conversation as resolved.
Show resolved Hide resolved
this.addSettingFlag(
psSettingsSection,
this.flagsUi.get(Flags.IsQualityController)
Expand Down
10 changes: 10 additions & 0 deletions SFU/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,18 @@ for(let arg of process.argv){
}

const config = {
// The URL of the signalling server to connect to
signallingURL: "ws://localhost:8889",

// The ID for this SFU to use. This will show up as a streamer ID on the signalling server
SFUId: "SFU",

// The ID of the streamer to subscribe to. If you leave this blank it will subscribe to the first streamer it sees.
subscribeStreamerId: "DefaultStreamer",

// Delay between list requests when looking for a specifc streamer.
retrySubscribeDelaySecs: 10,

mediasoup: {
worker: {
rtcMinPort: 40000,
Expand Down
56 changes: 54 additions & 2 deletions SFU/sfu_server.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ const WebSocket = require('ws');
const mediasoup = require('mediasoup_prebuilt');
const mediasoupSdp = require('mediasoup-sdp-bridge');

if (!config.retrySubscribeDelaySecs) {
config.retrySubscribeDelaySecs = 10;
}

let signalServer = null;
let mediasoupRouter;
let streamer = null;
Expand All @@ -24,6 +28,35 @@ function connectSignalling(server) {
});
}

async function onStreamerList(msg) {
let success = false;

// subscribe to either the configured streamer, or if not configured, just grab the first id
if (msg.ids.length > 0) {
if (!!config.subscribeStreamerId && config.subscribeStreamerId.length != 0) {
if (msg.ids.includes(config.subscribeStreamerId)) {
signalServer.send(JSON.stringify({type: 'subscribe', streamerId: config.subscribeStreamerId}));
success = true;
}
} else {
signalServer.send(JSON.stringify({type: 'subscribe', streamerId: msg.ids[0]}));
success = true;
}
}

if (!success) {
// did not subscribe to anything
setTimeout(function() {
signalServer.send(JSON.stringify({type: 'listStreamers'}));
}, config.retrySubscribeDelaySecs * 1000);
}
}

async function onIdentify(msg) {
signalServer.send(JSON.stringify({type: 'endpointId', id: config.SFUId}));
signalServer.send(JSON.stringify({type: 'listStreamers'}));
}

async function onStreamerOffer(sdp) {
console.log("Got offer from streamer");

Expand Down Expand Up @@ -57,6 +90,11 @@ function onStreamerDisconnected() {
}
streamer.transport.close();
streamer = null;
signalServer.send(JSON.stringify({type: 'stopStreaming'}));

setTimeout(function() {
signalServer.send(JSON.stringify({type: 'listStreamers'}));
}, config.retrySubscribeDelaySecs * 1000);
}
}

Expand Down Expand Up @@ -228,7 +266,7 @@ function onLayerPreference(msg) {
}

async function onSignallingMessage(message) {
//console.log(`Got MSG: ${message}`);
//console.log(`Got MSG: ${message}`);
const msg = JSON.parse(message);

if (msg.type == 'offer') {
Expand All @@ -255,6 +293,12 @@ async function onSignallingMessage(message) {
else if (msg.type == 'layerPreference') {
onLayerPreference(msg);
}
else if (msg.type == 'streamerList') {
onStreamerList(msg);
}
else if (msg.type == 'identify') {
onIdentify(msg);
}
}

async function startMediasoup() {
Expand All @@ -276,6 +320,14 @@ async function startMediasoup() {
return mediasoupRouter;
}

async function onICEStateChange(identifier, iceState) {
console.log("%s ICE state changed to %s", identifier, iceState);

if (identifier == 'Streamer' && iceState == 'completed') {
signalServer.send(JSON.stringify({type: 'startStreaming'}));
}
}

async function createWebRtcTransport(identifier) {
const {
listenIps,
Expand All @@ -291,7 +343,7 @@ async function createWebRtcTransport(identifier) {
initialAvailableOutgoingBitrate: initialAvailableOutgoingBitrate
});

transport.on("icestatechange", (iceState) => { console.log("%s ICE state changed to %s", identifier, iceState); });
transport.on("icestatechange", (iceState) => onICEStateChange(identifier, iceState));
transport.on("iceselectedtuplechange", (iceTuple) => { console.log("%s ICE selected tuple %s", identifier, JSON.stringify(iceTuple)); });
transport.on("sctpstatechange", (sctpState) => { console.log("%s SCTP state changed to %s", identifier, sctpState); });

Expand Down
Loading
Loading