1
0

Allow non-media players to connect to other players

Signed-off-by: James Ketrenos <james_eikona@ketrenos.com>
This commit is contained in:
James Ketrenos 2022-03-20 11:01:06 -07:00
parent 0bab275447
commit ec557e6926
2 changed files with 120 additions and 69 deletions

View File

@ -78,6 +78,29 @@ const MediaAgent = ({setPeers}) => {
return; return;
} }
} }
/* Even if reviving, allocate a new Object so <MediaControl> will
* have its peer state change and trigger an update from
* <PlayerList> */
const peer = {
name: peer_id,
hasAudio: config.hasAudio,
hasVideo: config.hasVideo,
attributes: {},
};
if (peer_id in peers) {
peer.muted = peers[peer_id].muted;
peer.videoOn = peers[peer_id].videoOn;
console.log(`media-agent - addPeer - reviving dead peer ${peer_id}`, peer);
} else {
peer.muted = false;
peer.videoOn = true;
}
peers[peer_id] = peer;
console.log(`media-agent - addPeer - remote`, peers);
setPeers(Object.assign({}, peers));
const connection = new RTCPeerConnection({ const connection = new RTCPeerConnection({
configuration: { configuration: {
offerToReceiveAudio: true, offerToReceiveAudio: true,
@ -97,30 +120,16 @@ const MediaAgent = ({setPeers}) => {
*/ */
] ]
}); });
peer.connection = connection;
/* Even if reviving, allocate a new Object so <MediaControl> will
* have its peer state change and trigger an update from
* <PlayerList> */
const peer = {
name: peer_id,
connection,
attributes: {},
};
if (peer_id in peers) {
peer.muted = peers[peer_id].muted;
peer.videoOn = peers[peer_id].videoOn;
console.log(`media-agent - addPeer - reviving dead peer ${peer_id}`, peer);
} else {
peer.muted = false;
peer.videoOn = true;
}
peers[peer_id] = peer;
console.log(`media-agent - addPeer - remote`, peers);
setPeers(Object.assign({}, peers));
connection.addEventListener('connectionstatechange', (event) => { connection.addEventListener('connectionstatechange', (event) => {
console.log(`media-agent - connectionstatechange - `, connection.connectionState, event); console.log(`media-agent - connectionstatechange - `,
connection.connectionState, event);
});
connection.addEventListener('negotiationneeded', (event) => {
console.log(`media-agent - negotiationneeded - `,
connection.connectionState, event);
}); });
connection.addEventListener('icecandidateerror', (event) => { connection.addEventListener('icecandidateerror', (event) => {
@ -163,7 +172,7 @@ const MediaAgent = ({setPeers}) => {
connection.ontrack = e => refOnTrack.current(e); connection.ontrack = e => refOnTrack.current(e);
/* Add our local stream */ /* Add our local stream */
connection.addStream(stream); connection.addStream(stream.media);
/* Only one side of the peer connection should create the /* Only one side of the peer connection should create the
* offer, the signaling server picks one to be the offerer. * offer, the signaling server picks one to be the offerer.
@ -172,10 +181,12 @@ const MediaAgent = ({setPeers}) => {
* to us * to us
*/ */
if (config.should_create_offer) { if (config.should_create_offer) {
if (debug) console.log(`media-agent - Creating RTC offer to ${peer_id}`); if (debug) console.log(`media-agent - Creating RTC offer to ` +
`${peer_id}`);
return connection.createOffer() return connection.createOffer()
.then((local_description) => { .then((local_description) => {
if (debug) console.log(`media-agent - Local offer description is: `, local_description); if (debug) console.log(`media-agent - Local offer ` +
`description is: `, local_description);
return connection.setLocalDescription(local_description) return connection.setLocalDescription(local_description)
.then(() => { .then(() => {
sendMessage({ sendMessage({
@ -185,7 +196,8 @@ const MediaAgent = ({setPeers}) => {
'session_description': local_description 'session_description': local_description
} }
}); });
if (debug) console.log(`media-agent - Offer setLocalDescription succeeded`); if (debug) console.log(`media-agent - Offer ` +
`setLocalDescription succeeded`);
}) })
.catch((error) => { .catch((error) => {
console.error(`media-agent - Offer setLocalDescription failed!`); console.error(`media-agent - Offer setLocalDescription failed!`);
@ -200,17 +212,21 @@ const MediaAgent = ({setPeers}) => {
const sessionDescription = ({ peer_id, session_description }) => { const sessionDescription = ({ peer_id, session_description }) => {
const peer = peers[peer_id]; const peer = peers[peer_id];
if (!peer) { if (!peer) {
console.error(`media-agent - sessionDescription - No peer for ${peer_id}`); console.error(`media-agent - sessionDescription - ` +
`No peer for ${peer_id}`);
return; return;
} }
const { connection } = peer; const { connection } = peer;
const desc = new RTCSessionDescription(session_description); const desc = new RTCSessionDescription(session_description);
return connection.setRemoteDescription(desc, () => { return connection.setRemoteDescription(desc, () => {
if (debug) console.log(`media-agent - sessionDescription - setRemoteDescription succeeded`); if (debug) console.log(`media-agent - sessionDescription - ` +
`setRemoteDescription succeeded`);
if (session_description.type === "offer") { if (session_description.type === "offer") {
if (debug) console.log(`media-agent - sessionDescription - Creating answer`); if (debug) console.log(`media-agent - sessionDescription - ` +
`Creating answer`);
connection.createAnswer((local_description) => { connection.createAnswer((local_description) => {
if (debug) console.log(`media-agent - sessionDescription - Answer description is: `, local_description); if (debug) console.log(`media-agent - sessionDescription - ` +
`Answer description is: `, local_description);
connection.setLocalDescription(local_description, () => { connection.setLocalDescription(local_description, () => {
sendMessage({ sendMessage({
type: 'relaySessionDescription', type: 'relaySessionDescription',
@ -219,21 +235,25 @@ const MediaAgent = ({setPeers}) => {
session_description: local_description session_description: local_description
} }
}); });
if (debug) console.log(`media-agent - sessionDescription - Answer setLocalDescription succeeded`); if (debug) console.log(`media-agent - sessionDescription ` +
`- Answer setLocalDescription succeeded`);
}, () => { }, () => {
console.error(`media-agent - sessionDescription - Answer setLocalDescription failed!`); console.error(`media-agent - sessionDescription - ` +
`Answer setLocalDescription failed!`);
}); });
}, (error) => { }, (error) => {
console.error(error); console.error(error);
}); });
} }
}, (error) => { }, (error) => {
console.log(`media-agent - sessionDescription - setRemoteDescription error: `, error); console.log(`media-agent - sessionDescription - ` +
`setRemoteDescription error: `, error);
}); });
}; };
const removePeer = ({peer_id}) => { const removePeer = ({peer_id}) => {
console.log(`media-agent - removePeer - Signaling server said to remove peer ${peer_id}`); console.log(`media-agent - removePeer - Signaling server said to ` +
`remove peer ${peer_id}`);
if (peer_id in peers) { if (peer_id in peers) {
if (peers[peer_id].connection) { if (peers[peer_id].connection) {
peers[peer_id].connection.close(); peers[peer_id].connection.close();
@ -256,12 +276,14 @@ const MediaAgent = ({setPeers}) => {
*/ */
const peer = peers[peer_id]; const peer = peers[peer_id];
if (!peer) { if (!peer) {
console.error(`media-agent - iceCandidate - No peer for ${peer_id}`, peers); console.error(`media-agent - iceCandidate - No peer for ` +
`${peer_id}`, peers);
return; return;
} }
peer.connection.addIceCandidate(new RTCIceCandidate(candidate)) peer.connection.addIceCandidate(new RTCIceCandidate(candidate))
.then(() => { .then(() => {
if (debug) console.log(`media-agent - iceCandidate - Successfully added Ice Candidate for ${peer_id}`); if (debug) console.log(`media-agent - iceCandidate - ` +
`Successfully added Ice Candidate for ${peer_id}`);
}) })
.catch((error) => { .catch((error) => {
console.error(error, peer, candidate); console.error(error, peer, candidate);
@ -269,7 +291,8 @@ const MediaAgent = ({setPeers}) => {
}; };
const data = JSON.parse(event.data); const data = JSON.parse(event.data);
if (data.type in [ 'addPeer', 'removePeer', 'iceCandidate', 'sessionDescription' ]) { if (data.type in [ 'addPeer', 'removePeer',
'iceCandidate', 'sessionDescription' ]) {
console.log(`media-agent - message - ${data.type}`, peers); console.log(`media-agent - message - ${data.type}`, peers);
} }
switch (data.type) { switch (data.type) {
@ -329,7 +352,13 @@ const MediaAgent = ({setPeers}) => {
console.log(`media-control - WebSocket or Stream changed`); console.log(`media-control - WebSocket or Stream changed`);
const join = () => { const join = () => {
sendMessage({ type: 'join' }); sendMessage({
type: 'join',
config: {
hasAudio: stream.audio,
hasVideo: stream.video
}
});
} }
if (ws && stream) { if (ws && stream) {
@ -359,9 +388,11 @@ const MediaAgent = ({setPeers}) => {
local: true, local: true,
muted: true, muted: true,
videoOn: false, videoOn: false,
hasVideo: stream.video,
hasAudio: stream.audio,
attributes: { attributes: {
local: true, local: true,
srcObject: stream srcObject: stream.media
} }
}; };
} }
@ -390,10 +421,11 @@ const MediaAgent = ({setPeers}) => {
const setup_local_media = () => { const setup_local_media = () => {
/* Ask user for permission to use the computers microphone and/or camera, /* Ask user for permission to use the computers microphone and/or camera,
* attach it to an <audio> or <video> tag if they give us access. */ * attach it to an <audio> or <video> tag if they give us access. */
console.log("media-agent - Requesting access to local audio / video inputs"); console.log(`media-agent - Requesting access to local ` +
`audio / video inputs`);
/* See Dummy Tracks for more ideas... /* See Dummy Tracks for more ideas...
https://blog.mozilla.org/webrtc/warm-up-with-replacetrack/ * https://blog.mozilla.org/webrtc/warm-up-with-replacetrack/
*/ */
navigator.getUserMedia = (navigator.getUserMedia || navigator.getUserMedia = (navigator.getUserMedia ||
navigator.webkitGetUserMedia || navigator.webkitGetUserMedia ||
@ -453,7 +485,7 @@ const MediaAgent = ({setPeers}) => {
} }
}); });
}); });
return context.media; return context;
} }
const black = ({ width = 640, height = 480 } = {}) => { const black = ({ width = 640, height = 480 } = {}) => {
@ -489,23 +521,26 @@ const MediaAgent = ({setPeers}) => {
enabled: true enabled: true
}); });
} }
return new MediaStream([context.media, black()]); context.media = new MediaStream([context.media, black()]);
return context;
} }
return new MediaStream([black(), silence()]); context.media = new MediaStream([black(), silence()]);
return context;
}); });
}; };
let abort = false; let abort = false;
if (!stream) { if (!stream) {
if (debug) console.log(`media-agent - WebSocket open request. Attempting to create local media.`); if (debug) console.log(`media-agent - WebSocket open request. ` +
setup_local_media().then((media) => { `Attempting to create local media.`);
setup_local_media().then((context) => {
/* once the user has given us access to their /* once the user has given us access to their
* microphone/camcorder, join the channel and start peering up */ * microphone/camcorder, join the channel and start peering up */
if (abort) { if (abort) {
console.log(`media-agent - aborting setting local media`); console.log(`media-agent - aborting setting local media`);
} else { } else {
setStream(media); setStream(context);
} }
}).catch((error) => { /* user denied access to a/v */ }).catch((error) => { /* user denied access to a/v */
console.error(error); console.error(error);
@ -565,7 +600,7 @@ const MediaControl = ({isSelf, peer, className}) => {
if (media.attributes.srcObject) { if (media.attributes.srcObject) {
console.log(`media-control - audio enable - ${peer.name}:${!muted}`); console.log(`media-control - audio enable - ${peer.name}:${!muted}`);
media.attributes.srcObject.getAudioTracks().forEach((track) => { media.attributes.srcObject.getAudioTracks().forEach((track) => {
track.enabled = !muted; track.enabled = media.hasAudio && !muted;
}); });
} }
}); /* run after every render to hit when ontrack has received and set }); /* run after every render to hit when ontrack has received and set
@ -578,28 +613,29 @@ const MediaControl = ({isSelf, peer, className}) => {
if (media.attributes.srcObject) { if (media.attributes.srcObject) {
console.log(`media-control - video enable - ${peer.name}:${videoOn}`); console.log(`media-control - video enable - ${peer.name}:${videoOn}`);
media.attributes.srcObject.getVideoTracks().forEach((track) => { media.attributes.srcObject.getVideoTracks().forEach((track) => {
track.enabled = videoOn; track.enabled = media.hasVideo && videoOn;
}); });
} }
}); /* run after every render to hit when ontrack has received and set }); /* run after every render to hit when ontrack has received and set
* the stream //, [media, videoOn]); */ * the stream //, [media, videoOn]); */
const isValid = media && !media.dead, const isValid = media && !media.dead,
color = isValid ? 'primary' : 'disabled'; colorAudio = (isValid && media.hasAudio) ? 'primary' : 'disabled',
colorVideo = (isValid && media.hasVideo) ? 'primary' : 'disabled';
return <div className={`MediaControl ${className}`}> return <div className={`MediaControl ${className}`}>
<div className="Controls" > <div className="Controls" >
{ isSelf && <div onClick={toggleMute}> { isSelf && <div onClick={toggleMute}>
{ muted && <MicOff color={color}/> } { muted && <MicOff color={colorAudio}/> }
{ !muted && <Mic color={color}/> } {!muted && <Mic color={colorAudio}/> }
</div> } </div> }
{ !isSelf && <div onClick={toggleMute}> { !isSelf && <div onClick={toggleMute}>
{ muted && <VolumeOff color={color}/> } {muted && <VolumeOff color={colorAudio}/> }
{ !muted && <VolumeUp color={color}/> } {!muted && <VolumeUp color={colorAudio}/> }
</div> } </div> }
<div onClick={toggleVideo}> <div onClick={toggleVideo}>
{ !videoOn && <VideocamOff color={color}/> } { !videoOn && <VideocamOff color={colorVideo}/> }
{ videoOn && <Videocam color={color}/> } {videoOn && <Videocam color={colorVideo}/> }
</div> </div>
</div> </div>
{ isValid && <Video className="Video" { isValid && <Video className="Video"

View File

@ -2961,7 +2961,7 @@ const resetDisconnectCheck = (game, req) => {
//req.disconnectCheck = setTimeout(() => { wsInactive(game, req) }, 20000); //req.disconnectCheck = setTimeout(() => { wsInactive(game, req) }, 20000);
} }
const join = (peers, session) => { const join = (peers, session, { hasVideo, hasAudio }) => {
const ws = session.ws; const ws = session.ws;
if (!session.name) { if (!session.name) {
@ -2978,19 +2978,34 @@ const join = (peers, session) => {
} }
for (let peer in peers) { for (let peer in peers) {
peers[peer].send(JSON.stringify({ /* Add this caller to all peers */
peers[peer].ws.send(JSON.stringify({
type: 'addPeer', type: 'addPeer',
data: { 'peer_id': session.name, 'should_create_offer': false } data: {
peer_id: session.name,
should_create_offer: false,
hasAudio, hasVideo
}
})); }));
/* Add each other peer to the caller */
ws.send(JSON.stringify({ ws.send(JSON.stringify({
type: 'addPeer', type: 'addPeer',
data: {'peer_id': peer, 'should_create_offer': true} data: {
peer_id: peer,
should_create_offer: true,
hasAudio: peers[peer].hasAudio,
hasVideo: peers[peer].hasVideo
}
})); }));
} }
/* Add this user as a peer connected to this WebSocket */ /* Add this user as a peer connected to this WebSocket */
peers[session.name] = ws; peers[session.name] = {
ws,
hasAudio,
hasVideo
};
}; };
const part = (peers, session) => { const part = (peers, session) => {
@ -3014,7 +3029,7 @@ const part = (peers, session) => {
/* Remove this peer from all other peers, and remove each /* Remove this peer from all other peers, and remove each
* peer from this peer */ * peer from this peer */
for (let peer in peers) { for (let peer in peers) {
peers[peer].send(JSON.stringify({ peers[peer].ws.send(JSON.stringify({
type: 'removePeer', type: 'removePeer',
data: {'peer_id': session.name} data: {'peer_id': session.name}
})); }));
@ -3533,7 +3548,7 @@ router.ws("/ws/:id", async (ws, req) => {
switch (data.type) { switch (data.type) {
case 'join': case 'join':
join(audio[id], session); join(audio[id], session, data.config);
break; break;
case 'part': case 'part':
@ -3556,7 +3571,7 @@ router.ws("/ws/:id", async (ws, req) => {
}); });
if (peer_id in audio[id]) { if (peer_id in audio[id]) {
audio[id][peer_id].send(message); audio[id][peer_id].ws.send(message);
} }
} break; } break;
@ -3573,7 +3588,7 @@ router.ws("/ws/:id", async (ws, req) => {
data: {'peer_id': getName(session), 'session_description': session_description } data: {'peer_id': getName(session), 'session_description': session_description }
}); });
if (peer_id in audio[id]) { if (peer_id in audio[id]) {
audio[id][peer_id].send(message); audio[id][peer_id].ws.send(message);
} }
} break; } break;