Merge pull request #411 from mcottontensor/streamer_ids_fix

Fixes for Streamers changing IDs when connecting & Updates to SFU behaviour relating to multi streamer changes.
This commit is contained in:
mcottontensor 2023-11-01 09:56:47 +11:00 committed by GitHub
commit 25024f6848
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 376 additions and 266 deletions

View File

@ -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. |

View File

@ -1,11 +1,11 @@
{
"name": "@epicgames-ps/reference-pixelstreamingfrontend-ue5.3",
"name": "@epicgames-ps/reference-pixelstreamingfrontend-ue5.4",
"version": "0.0.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@epicgames-ps/reference-pixelstreamingfrontend-ue5.3",
"name": "@epicgames-ps/reference-pixelstreamingfrontend-ue5.4",
"version": "0.0.1",
"devDependencies": {
"css-loader": "^6.7.3",

View File

@ -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;
@ -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(

View File

@ -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(
@ -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(

View File

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

View File

@ -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,

View File

@ -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;
@ -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");
@ -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);
}
}
@ -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') {
@ -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() {
@ -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,
@ -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); });

View File

@ -288,14 +288,83 @@ console.logColor(logging.Cyan, `Running Cirrus - The Pixel Streaming reference i
let nextPlayerId = 1;
const StreamerType = { Regular: 0, SFU: 1 };
class Streamer {
constructor(initialId, ws, type) {
this.id = initialId;
this.ws = ws;
this.type = type;
this.idCommitted = false;
}
// registers this streamers id
commitId(id) {
this.id = id;
this.idCommitted = true;
}
// returns true if we have a valid id
isIdCommitted() {
return this.idCommitted;
}
// links this streamer to a subscribed SFU player (player component of an SFU)
addSFUPlayer(sfuPlayerId) {
if (!!this.SFUPlayerId && this.SFUPlayerId != sfuPlayerId) {
console.error(`Streamer ${this.id} already has an SFU ${this.SFUPlayerId}. Trying to add ${sfuPlayerId} as SFU.`);
return;
}
this.SFUPlayerId = sfuPlayerId;
}
// removes the previously subscribed SFU player
removeSFUPlayer() {
delete this.SFUPlayerId;
}
// gets the player id of the subscribed SFU if any
getSFUPlayerId() {
return this.SFUPlayerId;
}
// returns true if this streamer is forwarding another streamer
isSFU() {
return this.type == StreamerType.SFU;
}
// links this streamer to a player, used for SFU connections since they have both components
setSFUPlayerComponent(playerComponent) {
if (!this.isSFU()) {
console.error(`Trying to add an SFU player component ${playerComponent.id} to streamer ${this.id} but it is not an SFU type.`);
return;
}
this.sfuPlayerComponent = playerComponent;
}
// gets the player component for this sfu
getSFUPlayerComponent() {
if (!this.isSFU()) {
console.error(`Trying to get an SFU player component from streamer ${this.id} but it is not an SFU type.`);
return null;
}
return this.sfuPlayerComponent;
}
}
const PlayerType = { Regular: 0, SFU: 1 };
const WhoSendsOffer = { Streamer: 0, Browser: 1 };
class Player {
constructor(id, ws, type, browserSendOffer) {
constructor(id, ws, type, whoSendsOffer) {
this.id = id;
this.ws = ws;
this.type = type;
this.browserSendOffer = browserSendOffer;
this.whoSendsOffer = whoSendsOffer;
}
isSFU() {
return this.type == PlayerType.SFU;
}
subscribe(streamerId) {
@ -304,13 +373,25 @@ class Player {
return;
}
this.streamerId = streamerId;
const msg = { type: 'playerConnected', playerId: this.id, dataChannel: true, sfu: this.type == PlayerType.SFU, sendOffer: !this.browserSendOffer };
if (this.type == PlayerType.SFU) {
let streamer = streamers.get(this.streamerId);
streamer.addSFUPlayer(this.id);
}
const msg = { type: 'playerConnected', playerId: this.id, dataChannel: true, sfu: this.type == PlayerType.SFU, sendOffer: this.whoSendsOffer == WhoSendsOffer.Streamer };
logOutgoing(this.streamerId, msg);
this.sendFrom(msg);
}
unsubscribe() {
if (this.streamerId && streamers.has(this.streamerId)) {
if (this.type == PlayerType.SFU) {
let streamer = streamers.get(this.streamerId);
if (streamer.getSFUPlayerId() != this.id) {
console.error(`Trying to unsibscribe SFU player ${this.id} from streamer ${streamer.id} but the current SFUId does not match (${streamer.getSFUPlayerId()}).`)
} else {
streamer.removeSFUPlayer();
}
}
const msg = { type: 'playerDisconnected', playerId: this.id };
logOutgoing(this.streamerId, msg);
this.sendFrom(msg);
@ -348,20 +429,41 @@ class Player {
const msgString = JSON.stringify(message);
this.ws.send(msgString);
}
setSFUStreamerComponent(streamerComponent) {
if (!this.isSFU()) {
console.error(`Trying to add an SFU streamer component ${streamerComponent.id} to player ${this.id} but it is not an SFU type.`);
return;
}
this.sfuStreamerComponent = streamerComponent;
}
getSFUStreamerComponent() {
if (!this.isSFU()) {
console.error(`Trying to get an SFU streamer component from player ${this.id} but it is not an SFU type.`);
return null;
}
return this.sfuStreamerComponent;
}
};
let streamers = new Map(); // streamerId <-> streamer socket
let players = new Map(); // playerId <-> player, where player is either a web-browser or a native webrtc player
const SFUPlayerId = "SFU";
const LegacyStreamerId = "__LEGACY__"; // old streamers that dont know how to ID will be assigned this id.
let streamers = new Map(); // streamerId <-> streamer
let players = new Map(); // playerId <-> player/peer/viewer
const LegacyStreamerPrefix = "__LEGACY_STREAMER__"; // old streamers that dont know how to ID will be assigned this id prefix.
const LegacySFUPrefix = "__LEGACY_SFU__"; // same as streamer version but for SFUs
const streamerIdTimeoutSecs = 5;
function sfuIsConnected() {
const sfuPlayer = players.get(SFUPlayerId);
return sfuPlayer && sfuPlayer.ws && sfuPlayer.ws.readyState == 1;
}
function getSFU() {
return players.get(SFUPlayerId);
// gets the SFU subscribed to this streamer if any.
function getSFUForStreamer(streamerId) {
if (!streamers.has(streamerId)) {
return null;
}
const streamer = streamers.get(streamerId);
const sfuPlayerId = streamer.getSFUPlayerId();
if (!sfuPlayerId) {
return null;
}
return players.get(sfuPlayerId);
}
function logIncoming(sourceName, msg) {
@ -401,30 +503,91 @@ function getPlayerIdFromMessage(msg) {
return sanitizePlayerId(msg.playerId);
}
let uniqueLegacyStreamerPostfix = 0;
function getUniqueLegacyStreamerId() {
const finalId = LegacyStreamerPrefix + uniqueLegacyStreamerPostfix;
++uniqueLegacyStreamerPostfix;
return finalId;
}
let uniqueLegacySFUPostfix = 0;
function getUniqueLegacySFUId() {
const finalId = LegacySFUPrefix + uniqueLegacySFUPostfix;
++uniqueLegacySFUPostfix;
return finalId;
}
function requestStreamerId(streamer) {
// first we ask the streamer to id itself.
// if it doesnt reply within a time limit we assume it's an older streamer
// and assign it an id.
// request id
const msg = { type: "identify" };
logOutgoing(streamer.id, msg);
streamer.ws.send(JSON.stringify(msg));
streamer.idTimer = setTimeout(function() {
// streamer did not respond in time. give it a legacy id.
const newLegacyId = getUniqueLegacyId();
if (newLegacyId.length == 0) {
const error = `Ran out of legacy ids.`;
console.error(error);
streamer.ws.close(1008, error);
} else {
registerStreamer(newLegacyId, streamer);
}
}, streamerIdTimeoutSecs * 1000);
}
function sanitizeStreamerId(id) {
let maxPostfix = -1;
for (let [streamerId, streamer] of streamers) {
const idMatchRegex = /^(.*?)(\d*)$/;
const [, baseId, postfix] = streamerId.match(idMatchRegex);
// if the id is numeric then base id will be empty and we need to compare with the postfix
if ((baseId != '' && baseId != id) || (baseId == '' && postfix != id)) {
continue;
}
const numPostfix = Number(postfix);
if (numPostfix > maxPostfix) {
maxPostfix = numPostfix
}
}
if (maxPostfix >= 0) {
return id + (maxPostfix + 1);
}
return id;
}
function registerStreamer(id, streamer) {
streamer.id = id;
streamers.set(streamer.id, streamer);
// make sure the id is unique
const uniqueId = sanitizeStreamerId(id);
streamer.commitId(uniqueId);
if (!!streamer.idTimer) {
clearTimeout(streamer.idTimer);
delete streamer.idTimer;
}
streamers.set(uniqueId, streamer);
console.logColor(logging.Green, `Registered new streamer: ${streamer.id}`);
}
function onStreamerDisconnected(streamer) {
if (!streamer.id) {
if (!streamer.id || !streamers.has(streamer.id)) {
return;
}
if (!streamers.has(streamer.id)) {
console.error(`Disconnecting streamer ${streamer.id} does not exist.`);
} else {
sendStreamerDisconnectedToMatchmaker();
let sfuPlayer = getSFU();
if (sfuPlayer) {
const msg = { type: "streamerDisconnected" };
logOutgoing(sfuPlayer.id, msg);
sfuPlayer.sendTo(msg);
disconnectAllPlayers(sfuPlayer.id);
}
disconnectAllPlayers(streamer.id);
streamers.delete(streamer.id);
sendStreamerDisconnectedToMatchmaker();
let sfuPlayer = getSFUForStreamer(streamer.id);
if (sfuPlayer) {
const msg = { type: "streamerDisconnected" };
logOutgoing(sfuPlayer.id, msg);
sfuPlayer.sendTo(msg);
disconnectAllPlayers(sfuPlayer.id);
}
disconnectAllPlayers(streamer.id);
streamers.delete(streamer.id);
}
function onStreamerMessageId(streamer, msg) {
@ -432,15 +595,6 @@ function onStreamerMessageId(streamer, msg) {
let streamerId = msg.id;
registerStreamer(streamerId, streamer);
// subscribe any sfu to the latest connected streamer
const sfuPlayer = getSFU();
if (sfuPlayer) {
sfuPlayer.subscribe(streamer.id);
}
// if any streamer id's assume the legacy streamer is not needed.
streamers.delete(LegacyStreamerId);
}
function onStreamerMessagePing(streamer, msg) {
@ -461,7 +615,7 @@ function onStreamerMessageDisconnectPlayer(streamer, msg) {
}
function onStreamerMessageLayerPreference(streamer, msg) {
let sfuPlayer = getSFU();
let sfuPlayer = getSFUForStreamer(streamer.id);
if (sfuPlayer) {
logOutgoing(sfuPlayer.id, msg);
sfuPlayer.sendTo(msg);
@ -495,7 +649,8 @@ streamerServer.on('connection', function (ws, req) {
console.logColor(logging.Green, `Streamer connected: ${req.connection.remoteAddress}`);
sendStreamerConnectedToMatchmaker();
let streamer = { ws: ws };
const temporaryId = req.connection.remoteAddress;
let streamer = new Streamer(temporaryId, ws, StreamerType.Regular);
ws.on('message', (msgRaw) => {
var msg;
@ -535,69 +690,124 @@ streamerServer.on('connection', function (ws, req) {
});
ws.send(JSON.stringify(clientConfig));
// request id
const msg = { type: "identify" };
logOutgoing("unknown", msg);
ws.send(JSON.stringify(msg));
registerStreamer(LegacyStreamerId, streamer);
requestStreamerId(streamer);
});
function forwardSFUMessageToPlayer(msg) {
function forwardSFUMessageToPlayer(sfuPlayer, msg) {
const playerId = getPlayerIdFromMessage(msg);
const player = players.get(playerId);
if (player) {
logForward(SFUPlayerId, playerId, msg);
logForward(sfuPlayer.getSFUStreamerComponent().id, playerId, msg);
player.sendTo(msg);
}
}
function forwardSFUMessageToStreamer(msg) {
const sfuPlayer = getSFU();
if (sfuPlayer) {
logForward(SFUPlayerId, sfuPlayer.streamerId, msg);
msg.sfuId = SFUPlayerId;
sfuPlayer.sendFrom(msg);
}
function forwardSFUMessageToStreamer(sfuPlayer, msg) {
logForward(sfuPlayer.getSFUStreamerComponent().id, sfuPlayer.streamerId, msg);
msg.sfuId = sfuPlayer.id;
sfuPlayer.sendFrom(msg);
}
function onPeerDataChannelsSFUMessage(msg) {
function onPeerDataChannelsSFUMessage(sfuPlayer, msg) {
// sfu is telling a peer what stream id to use for a data channel
const playerId = getPlayerIdFromMessage(msg);
const player = players.get(playerId);
if (player) {
logForward(SFUPlayerId, playerId, msg);
logForward(sfuPlayer.getSFUStreamerComponent().id, playerId, msg);
player.sendTo(msg);
player.datachannel = true;
}
}
function onSFUDisconnected() {
console.log("disconnecting SFU from streamer");
disconnectAllPlayers(SFUPlayerId);
const sfuPlayer = getSFU();
if (sfuPlayer) {
sfuPlayer.unsubscribe();
sfuPlayer.ws.close(4000, "SFU Disconnected");
}
players.delete(SFUPlayerId);
streamers.delete(SFUPlayerId);
// basically a duplicate of the streamer id request but this one does not register the streamer
function requestSFUStreamerId(sfuPlayer) {
// request id
const msg = { type: "identify" };
const sfuStreamerComponent = sfuPlayer.getSFUStreamerComponent();
logOutgoing(sfuStreamerComponent.id, msg);
sfuStreamerComponent.ws.send(JSON.stringify(msg));
sfuStreamerComponent.idTimer = setTimeout(function() {
// streamer did not respond in time. give it a legacy id.
const newLegacyId = getUniqueSFUId();
if (newLegacyId.length == 0) {
const error = `Ran out of legacy ids.`;
console.error(error);
sfuPlayer.ws.close(1008, error);
} else {
sfuStreamerComponent.id = newLegacyId;
}
}, streamerIdTimeoutSecs * 1000);
}
function onSFUMessageId(sfuPlayer, msg) {
const sfuStreamerComponent = sfuPlayer.getSFUStreamerComponent();
logIncoming(sfuStreamerComponent.id, msg);
sfuStreamerComponent.id = msg.id;
if (!!sfuStreamerComponent.idTimer) {
clearTimeout(sfuStreamerComponent.idTimer);
delete sfuStreamerComponent.idTimer;
}
}
function onSFUMessageStartStreaming(sfuPlayer, msg) {
const sfuStreamerComponent = sfuPlayer.getSFUStreamerComponent();
logIncoming(sfuStreamerComponent.id, msg);
if (streamers.has(sfuStreamerComponent.id)) {
console.error(`SFU ${sfuStreamerComponent.id} is already registered as a streamer and streaming.`)
return;
}
registerStreamer(sfuStreamerComponent.id, sfuStreamerComponent);
}
function onSFUMessageStopStreaming(sfuPlayer, msg) {
const sfuStreamerComponent = sfuPlayer.getSFUStreamerComponent();
logIncoming(sfuStreamerComponent.id, msg);
if (!streamers.has(sfuStreamerComponent.id)) {
console.error(`SFU ${sfuStreamerComponent.id} is not registered as a streamer or streaming.`)
return;
}
onStreamerDisconnected(sfuStreamerComponent);
}
function onSFUDisconnected(sfuPlayer) {
console.log("disconnecting SFU from streamer");
disconnectAllPlayers(sfuPlayer.id);
onStreamerDisconnected(sfuPlayer.getSFUStreamerComponent());
sfuPlayer.unsubscribe();
sfuPlayer.ws.close(4000, "SFU Disconnected");
players.delete(sfuPlayer.id);
streamers.delete(sfuPlayer.id);
}
sfuMessageHandlers.set('listStreamers', onPlayerMessageListStreamers);
sfuMessageHandlers.set('subscribe', onPlayerMessageSubscribe);
sfuMessageHandlers.set('unsubscribe', onPlayerMessageUnsubscribe);
sfuMessageHandlers.set('offer', forwardSFUMessageToPlayer);
sfuMessageHandlers.set('answer', forwardSFUMessageToStreamer);
sfuMessageHandlers.set('streamerDataChannels', forwardSFUMessageToStreamer);
sfuMessageHandlers.set('peerDataChannels', onPeerDataChannelsSFUMessage);
sfuMessageHandlers.set('endpointId', onSFUMessageId);
sfuMessageHandlers.set('startStreaming', onSFUMessageStartStreaming);
sfuMessageHandlers.set('stopStreaming', onSFUMessageStopStreaming);
console.logColor(logging.Green, `WebSocket listening for SFU connections on :${sfuPort}`);
let sfuServer = new WebSocket.Server({ port: sfuPort });
sfuServer.on('connection', function (ws, req) {
// reject if we already have an sfu
if (sfuIsConnected()) {
ws.close(1013, 'Already have an SFU');
return;
}
let playerId = sanitizePlayerId(nextPlayerId++);
console.logColor(logging.Green, `SFU (${req.connection.remoteAddress}) connected `);
let streamerComponent = new Streamer(req.connection.remoteAddress, ws, StreamerType.SFU);
let playerComponent = new Player(playerId, ws, PlayerType.SFU, WhoSendsOffer.Streamer);
streamerComponent.setSFUPlayerComponent(playerComponent);
playerComponent.setSFUStreamerComponent(streamerComponent);
players.set(playerId, playerComponent);
ws.on('message', (msgRaw) => {
var msg;
@ -609,26 +819,33 @@ sfuServer.on('connection', function (ws, req) {
return;
}
let sfuPlayer = players.get(playerId);
if (!sfuPlayer) {
console.error(`Received a message from an SFU not in the player list ${playerId}`);
ws.close(1001, 'Broken');
return;
}
let handler = sfuMessageHandlers.get(msg.type);
if (!handler || (typeof handler != 'function')) {
if (config.LogVerbose) {
console.logColor(logging.White, "\x1b[37m-> %s\x1b[34m: %s", SFUPlayerId, msgRaw);
console.logColor(logging.White, "\x1b[37m-> %s\x1b[34m: %s", sfuPlayer.id, msgRaw);
}
console.error(`unsupported SFU message type: ${msg.type}`);
ws.close(1008, 'Unsupported message type');
return;
}
handler(msg);
handler(sfuPlayer, msg);
});
ws.on('close', function(code, reason) {
console.error(`SFU disconnected: ${code} - ${reason}`);
onSFUDisconnected();
onSFUDisconnected(playerComponent);
});
ws.on('error', function(error) {
console.error(`SFU connection error: ${error}`);
onSFUDisconnected();
onSFUDisconnected(playerComponent);
try {
ws.close(1006 /* abnormal closure */, error);
} catch(err) {
@ -636,18 +853,7 @@ sfuServer.on('connection', function (ws, req) {
}
});
let sfuPlayer = new Player(SFUPlayerId, ws, PlayerType.SFU, false);
players.set(SFUPlayerId, sfuPlayer);
console.logColor(logging.Green, `SFU (${req.connection.remoteAddress}) connected `);
// TODO subscribe it to one of any of the streamers for now
for (let [streamerId, streamer] of streamers) {
sfuPlayer.subscribe(streamerId);
break;
}
// sfu also acts as a streamer
registerStreamer(SFUPlayerId, { ws: ws });
requestStreamerId(playerComponent.getSFUStreamerComponent());
});
let playerCount = 0;
@ -718,7 +924,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';
const whoSendsOffer = urlParams.has('OfferToReceive') && urlParams.get('OfferToReceive') !== 'false' ? WhoSendsOffer.Browser : WhoSendsOffer.Streamer;
if (playerCount + 1 > maxPlayerCount && maxPlayerCount !== -1)
{
@ -730,7 +936,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, browserSendOffer);
let player = new Player(playerId, ws, PlayerType.Regular, whoSendsOffer);
players.set(playerId, player);
ws.on('message', (msgRaw) =>{
@ -788,9 +994,9 @@ function disconnectAllPlayers(streamerId) {
for (let player of clone.values()) {
if (player.streamerId == streamerId) {
// disconnect players but just unsubscribe the SFU
if (player.id == SFUPlayerId) {
// because we're working on a clone here we have to access directly
getSFU().unsubscribe();
const sfuPlayer = getSFUForStreamer(streamerId);
if (sfuPlayer && player.id == sfuPlayer.id) {
sfuPlayer.unsubscribe();
} else {
player.ws.close();
}

View File

@ -1,66 +0,0 @@
::
:: RefreshEnv.cmd
::
:: Batch file to read environment variables from registry and
:: set session variables to these values.
::
:: With this batch file, there should be no need to reload command
:: environment every time you want environment changes to propagate
::echo "RefreshEnv.cmd only works from cmd.exe, please install the Chocolatey Profile to take advantage of refreshenv from PowerShell"
echo | set /p dummy="Refreshing environment variables from registry for cmd.exe. Please wait..."
goto main
:: Set one environment variable from registry key
:SetFromReg
"%WinDir%\System32\Reg" QUERY "%~1" /v "%~2" > "%TEMP%\_envset.tmp" 2>NUL
for /f "usebackq skip=2 tokens=2,*" %%A IN ("%TEMP%\_envset.tmp") do (
echo/set "%~3=%%B"
)
goto :EOF
:: Get a list of environment variables from registry
:GetRegEnv
"%WinDir%\System32\Reg" QUERY "%~1" > "%TEMP%\_envget.tmp"
for /f "usebackq skip=2" %%A IN ("%TEMP%\_envget.tmp") do (
if /I not "%%~A"=="Path" (
call :SetFromReg "%~1" "%%~A" "%%~A"
)
)
goto :EOF
:main
echo/@echo off >"%TEMP%\_env.cmd"
:: Slowly generating final file
call :GetRegEnv "HKLM\System\CurrentControlSet\Control\Session Manager\Environment" >> "%TEMP%\_env.cmd"
call :GetRegEnv "HKCU\Environment">>"%TEMP%\_env.cmd" >> "%TEMP%\_env.cmd"
:: Special handling for PATH - mix both User and System
call :SetFromReg "HKLM\System\CurrentControlSet\Control\Session Manager\Environment" Path Path_HKLM >> "%TEMP%\_env.cmd"
call :SetFromReg "HKCU\Environment" Path Path_HKCU >> "%TEMP%\_env.cmd"
:: Caution: do not insert space-chars before >> redirection sign
echo/set "Path=%%Path_HKLM%%;%%Path_HKCU%%" >> "%TEMP%\_env.cmd"
:: Cleanup
del /f /q "%TEMP%\_envset.tmp" 2>nul
del /f /q "%TEMP%\_envget.tmp" 2>nul
:: capture user / architecture
SET "OriginalUserName=%USERNAME%"
SET "OriginalArchitecture=%PROCESSOR_ARCHITECTURE%"
:: Set these variables
call "%TEMP%\_env.cmd"
:: Cleanup
del /f /q "%TEMP%\_env.cmd" 2>nul
:: reset user / architecture
SET "USERNAME=%OriginalUserName%"
SET "PROCESSOR_ARCHITECTURE=%OriginalArchitecture%"
echo | set /p dummy="Finished."
echo ...

View File

@ -1,24 +0,0 @@
License
-------
Copyright (C) 1999-2008 - Jonathan Wilkes
http://www.xanya.net
Installing and using this software (or source code) signifies acceptance of these terms and the conditions of the license.
This license applies to everything in this package (Including any supplied Source Code), except where otherwise noted.
License Agreement
-----------------
This software is provided 'as-is', without any express or implied warranty.
In no event will the author be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software/source code.
(If you use the supplied source code (if any) in a product, then an acknowledgment in the product documentation would be appreciated but is not required.)
2. If you have downloaded the Source Code for this application (where available) then altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any distribution of the software.
(If you use the supplied source code (if any) in a product, including commercial applications, then you do NOT need to distribute this license with your product.)

View File

@ -1,46 +0,0 @@
SetEnv
Version 1.09 - ( For Windows 9x/NT/2000/XP/S2K3/Vista )
Copyright (C) 2005-2008 - Jonathan Wilkes - All Rights Reserved.
http://www.xanya.net
================================================================================
1. Installation
Simply download and run the Setup_SetEnv.exe application to install SetEnv.
2. Using SetEnv
The SetEnv is a free tool for setting/updating/deleting System Environment Variables.
Type the following at a command prompt (assumes SetEnv.exe is in current path), for command line usage information.
setenv -?
See our website for full usage details, http://www.xanya.net/site/utils/setenv.php
3. Version History
1.09 [Fix] - (Feb 9, 2008) - Fixed a problem on Windows 98 where it sometimes failed to open the Autoexec.bat file.
1.08 [New] - (May 31, 2007) - Added how to delete a USER environment variable to the usage information.
1.07 [Fix] - (Jan 25, 2007) - Fixed a bug found by depaolim.
1.06 [New] - (Jan 14, 2007) - Added dynamic expansion support (same as using ~ with setx)
- Originally requested by Andre Amaral, further Request by Synetech
1.05 [New] - (Sep 06, 2006) - Added support to prepend (rather than append) a value to an expanded string
- Requested by Masuia
1.04 [New] - (May 30, 2006) - Added support for User environment variables.
1.03 [Fix] - (Apr 20, 2006) - Bug fix in ProcessWinXP() discovered by attiasr
1.01 [Fix] - (Nov 15, 2005) - Bug fix in IsWinME() discovered by frankd
1.00 [New] - (Oct 29, 2005) - Initial Public Release.
4. License and Terms of Use
Please see the License.txt file for licensing information.
5. Reporting Problems
If you encounter any problems whilst using SetEnv, please try downloading the latest version from http://www.xanya.net to see if the problem has already been resolved.
If this does not help, then please send an e-mail to darka@xanya.net with details describing the problem.
================================================================================

View File

@ -43,10 +43,10 @@
@Rem Save our current directory (the NodeJS dir) in a variable
set "NodeDir=%CD%\SignallingWebServer\platform_scripts\cmd\node"
@Rem Prepend NodeDir to PATH temporarily using a custom tool called SetEnv
call SignallingWebServer\platform_scripts\cmd\setenv\SetEnv.exe -uap PATH %%%%"%NodeDir%"
@Rem Refresh the cmd session with new PATH
call %~dp0\refreshenv.cmd
@rem Save the old path variable
set OLDPATH=%PATH%
@Rem Prepend NodeDir to PATH temporarily
set PATH=%PATH%;%NodeDir%
@Rem Do npm install in the Frontend\lib directory (note we use start because that loads PATH)
echo ----------------------------
@ -73,7 +73,7 @@
echo End of build reference frontend step.
echo ----------------------------
@Rem Remove our NodeJS from the PATH
call SignallingWebServer\platform_scripts\cmd\setenv\SetEnv.exe -ud PATH %%%%"%NodeDir%"
@Rem Restore path
set PATH=%OLDPATH%
goto :eof