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

Commit

Permalink
Multi-streamer QOL improvements around stream switching and selection (
Browse files Browse the repository at this point in the history
…#88)

* Set streamer selector on stream start

* Restart stream automatically one streamer selection change

* Only respect the `preferSFU` toggle if there's an SFU connected

* Only subscribe to the URL specified streamer if that streamer is connected
  • Loading branch information
Belchy06 committed Feb 13, 2023
1 parent 165e211 commit 4fc43bc
Show file tree
Hide file tree
Showing 7 changed files with 54 additions and 138 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ node_modules/
**/platform_scripts/bash/*/
node.zip
SignallingWebServer/Public/
SignallingWebServer/certificates
.vscode
21 changes: 9 additions & 12 deletions Frontend/library/src/Config/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -463,18 +463,6 @@ export class Config {
this.numericParameters.get(NumericParameters.AFKTimeoutSecs)
);

const preferredCodecOption = this.optionParameters.get(
OptionParameters.PreferredCodec
);
this.addSettingOption(psSettingsSection, preferredCodecOption);
if (
[...preferredCodecOption.selector.options]
.map((o) => o.value)
.includes('Only available on Chrome')
) {
preferredCodecOption.disable();
}

/* Setup all view/ui related settings under this section */
const viewSettingsSection = this.buildSectionWithHeading(
settingsElem,
Expand Down Expand Up @@ -512,6 +500,15 @@ export class Config {
this.numericParameters.get(NumericParameters.MaxQP)
);

const preferredCodecOption = this.optionParameters.get(OptionParameters.PreferredCodec);
this.addSettingOption(
encoderSettingsSection,
preferredCodecOption
);
if([...preferredCodecOption.selector.options].map(o => o.value).includes("Only available on Chrome")) {
preferredCodecOption.disable();
}

/* Setup all webrtc related settings under this section */
const webrtcSettingsSection = this.buildSectionWithHeading(
settingsElem,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,12 @@ export class PeerConnectionController {
preferredCodec: string
) {
this.config = config;
this.createPeerConnection(options, preferredCodec);
}

createPeerConnection(options: RTCConfiguration, preferredCodec: string) {
// Set the ICE transport to relay if TURN enabled
if (config.isFlagEnabled(Flags.ForceTURN)) {
if (this.config.isFlagEnabled(Flags.ForceTURN)) {
options.iceTransportPolicy = 'relay';
Logger.Log(
Logger.GetStackTrace(),
Expand Down Expand Up @@ -176,12 +179,8 @@ export class PeerConnectionController {
this.onVideoStats(this.aggregatedStats);

// Update the preferred codec selection based on what was actually negotiated
if (this.updateCodecSelection) {
this.config.getSettingOption(
OptionParameters.PreferredCodec
).selected = this.aggregatedStats.codecs.get(
this.aggregatedStats.inboundVideoStats.codecId
);
if(this.updateCodecSelection) {
this.config.setOptionSettingValue(OptionParameters.PreferredCodec, this.aggregatedStats.codecs.get(this.aggregatedStats.inboundVideoStats.codecId))
}
});
}
Expand Down
60 changes: 32 additions & 28 deletions Frontend/library/src/WebRtcPlayer/WebRtcPlayerController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export class WebRtcPlayerController {
statsTimerHandle: number;
file: FileTemplate;
preferredCodec: string;
peerConfig: RTCConfiguration

// if you override the disconnection message by calling the interface method setDisconnectMessageOverride
// it will use this property to store the override message string
Expand Down Expand Up @@ -179,14 +180,7 @@ export class WebRtcPlayerController {
}`
);
this.webSocketController.onOpen.addEventListener('open', () => {
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.has(OptionParameters.StreamerId)) {
this.webSocketController.sendSubscribe(
urlParams.get(OptionParameters.StreamerId)
);
} else {
this.webSocketController.requestStreamerList();
}
this.webSocketController.requestStreamerList();
});
this.webSocketController.onClose.addEventListener('close', () => {
this.afkController.stopAfkWarningTimer();
Expand Down Expand Up @@ -228,9 +222,10 @@ export class WebRtcPlayerController {
this.isQualityController = false;
this.preferredCodec = '';

this.config.addOnOptionSettingChangedListener(
OptionParameters.StreamerId,
(streamerid) => {
this.config.addOnOptionSettingChangedListener(OptionParameters.StreamerId, (streamerid) => {
// close the current peer connection and create a new one
this.peerConnectionController.peerConnection.close();
this.peerConnectionController.createPeerConnection(this.peerConfig, this.preferredCodec);
this.webSocketController.sendSubscribe(streamerid);
}
);
Expand Down Expand Up @@ -705,7 +700,7 @@ export class WebRtcPlayerController {
Logger.Error(
Logger.GetStackTrace(),
`ToStreamer->${messageType} protocol definition was malformed as it didn't contain at least an id and a byteLength\n
Definition was: ${JSON.stringify(message, null, 2)}`
Definition was: ${JSON.stringify(message, null, 2)}`
);
// return in a forEach is equivalent to a continue in a normal for loop
return;
Expand Down Expand Up @@ -751,7 +746,7 @@ export class WebRtcPlayerController {
Logger.Error(
Logger.GetStackTrace(),
`FromStreamer->${messageType} protocol definition was malformed as it didn't contain at least an id\n
Definition was: ${JSON.stringify(message, null, 2)}`
Definition was: ${JSON.stringify(message, null, 2)}`
);
// return in a forEach is equivalent to a continue in a normal for loop
return;
Expand Down Expand Up @@ -1045,18 +1040,15 @@ export class WebRtcPlayerController {
);

// If we are connecting to the SFU add a special url parameter to the url
if (this.config.isFlagEnabled(Flags.PreferSFU)) {
signallingServerUrl += '?' + Flags.PreferSFU + '=true';
}

// If we are sending the offer add a special url parameter to the url, making sure we append correctly
if (this.config.isFlagEnabled(Flags.BrowserSendOffer)) {
signallingServerUrl +=
(signallingServerUrl.includes('?') ? '&' : '?') +
Flags.BrowserSendOffer +
'=true';
signallingServerUrl += '?' + Flags.BrowserSendOffer + '=true';
}

// This code is no longer needed, but is a good example for how subsequent config flags can be appended
// if (this.config.isFlagEnabled(Flags.BrowserSendOffer)) {
// signallingServerUrl += (signallingServerUrl.includes('?') ? '&' : '?') + Flags.BrowserSendOffer + '=true';
// }

return signallingServerUrl;
}

Expand All @@ -1074,6 +1066,7 @@ export class WebRtcPlayerController {
* @remark RTC Peer Connection on Ice Candidate event have it handled by handle Send Ice Candidate
*/
startSession(peerConfig: RTCConfiguration) {
this.peerConfig = peerConfig;
// check for forcing turn
if (this.config.isFlagEnabled(Flags.ForceTURN)) {
// check for a turn server
Expand All @@ -1095,7 +1088,7 @@ export class WebRtcPlayerController {

// set up the peer connection controller
this.peerConnectionController = new PeerConnectionController(
peerConfig,
this.peerConfig,
this.config,
this.preferredCodec
);
Expand Down Expand Up @@ -1218,11 +1211,22 @@ export class WebRtcPlayerController {
`Got streamer list ${messageStreamerList.ids}`,
6
);
messageStreamerList.ids.unshift(''); // add an empty option at the top
this.config.setOptionSettingOptions(
OptionParameters.StreamerId,
messageStreamerList.ids
);

const settingOptions = [...messageStreamerList.ids] // copy the original messageStreamerList.ids
settingOptions.unshift('') // add an empty option at the top
this.config.setOptionSettingOptions(OptionParameters.StreamerId, settingOptions);

const urlParams = new URLSearchParams(window.location.search);
if(messageStreamerList.ids.length == 1) {
// If there's only a single streamer, subscribe to it regardless of what is in the URL
this.config.setOptionSettingValue(OptionParameters.StreamerId, 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
this.config.setOptionSettingValue(OptionParameters.StreamerId, "SFU");
} else if (urlParams.has(OptionParameters.StreamerId) && messageStreamerList.ids.includes(urlParams.get(OptionParameters.StreamerId))) {
// If there's a streamer ID in the URL and a streamer with this ID is connected, set it as the selected streamer
this.config.setOptionSettingValue(OptionParameters.StreamerId, urlParams.get(OptionParameters.StreamerId));
}
}

/**
Expand Down
37 changes: 0 additions & 37 deletions SignallingWebServer/Public/login.html

This file was deleted.

42 changes: 0 additions & 42 deletions SignallingWebServer/Public/stresstest.html

This file was deleted.

18 changes: 6 additions & 12 deletions SignallingWebServer/cirrus.js
Original file line number Diff line number Diff line change
Expand Up @@ -275,10 +275,11 @@ let nextPlayerId = 1;
const PlayerType = { Regular: 0, SFU: 1 };

class Player {
constructor(id, ws, type) {
constructor(id, ws, type, browserSendOffer) {
this.id = id;
this.ws = ws;
this.type = type;
this.browserSendOffer = browserSendOffer;
}

subscribe(streamerId) {
Expand All @@ -287,7 +288,7 @@ class Player {
return;
}
this.streamerId = streamerId;
const msg = { type: 'playerConnected', playerId: this.id, dataChannel: true, sfu: this.type == PlayerType.SFU };
const msg = { type: 'playerConnected', playerId: this.id, dataChannel: true, sfu: this.type == PlayerType.SFU, sendOffer: !this.browserSendOffer };
logOutgoing(this.streamerId, msg);
this.sendFrom(msg);
}
Expand Down Expand Up @@ -596,7 +597,7 @@ sfuServer.on('connection', function (ws, req) {
}
});

let sfuPlayer = new Player(SFUPlayerId, ws, PlayerType.SFU);
let sfuPlayer = new Player(SFUPlayerId, ws, PlayerType.SFU, false);
players.set(SFUPlayerId, sfuPlayer);
console.logColor(logging.Green, `SFU (${req.connection.remoteAddress}) connected `);

Expand Down Expand Up @@ -678,6 +679,7 @@ playerServer.on('connection', function (ws, req) {
var url = require('url');
const parsedUrl = url.parse(req.url);
const urlParams = new URLSearchParams(parsedUrl.search);
const browserSendOffer = urlParams.has('OfferToReceive') && urlParams.get('OfferToReceive') !== 'false';

if (playerCount + 1 > maxPlayerCount && maxPlayerCount !== -1)
{
Expand All @@ -689,7 +691,7 @@ playerServer.on('connection', function (ws, req) {
++playerCount;
let playerId = sanitizePlayerId(nextPlayerId++);
console.logColor(logging.Green, `player ${playerId} (${req.connection.remoteAddress}) connected`);
let player = new Player(playerId, ws, PlayerType.Regular);
let player = new Player(playerId, ws, PlayerType.Regular, browserSendOffer);
players.set(playerId, player);

ws.on('message', (msgRaw) =>{
Expand Down Expand Up @@ -739,14 +741,6 @@ playerServer.on('connection', function (ws, req) {
sendPlayerConnectedToMatchmaker();
player.ws.send(JSON.stringify(clientConfig));
sendPlayersCount();

// if we only have one streamer just subscribe this player to that one
if (streamers.size == 1) {
for (let [streamerId, streamer] of streamers) {
player.subscribe(streamerId);
break;
}
}
});

function disconnectAllPlayers(streamerId) {
Expand Down

0 comments on commit 4fc43bc

Please sign in to comment.