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

Commit

Permalink
Merge pull request #416 from EpicGames/backport/UE5.4/pr-411
Browse files Browse the repository at this point in the history
[UE5.4] Merge pull request #411 from mcottontensor/streamer_ids_fix
  • Loading branch information
mcottontensor authored Oct 31, 2023
2 parents d8007a3 + 22de24e commit b16bb6c
Show file tree
Hide file tree
Showing 13 changed files with 375 additions and 265 deletions.
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
)
);

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)
);
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

0 comments on commit b16bb6c

Please sign in to comment.