Video is working
Signed-off-by: James Ketrenos <james_eikona@ketrenos.com>
This commit is contained in:
parent
7bb7c74234
commit
f7e6d919e2
@ -100,8 +100,8 @@ body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
width: 30rem;
|
||||
max-width: 30rem;
|
||||
width: 25rem;
|
||||
max-width: 25rem;
|
||||
overflow: hidden;
|
||||
z-index: 5000;
|
||||
}
|
||||
@ -114,6 +114,10 @@ body {
|
||||
|
||||
.Table .Trade {
|
||||
z-index: 25000;
|
||||
transform-origin: right center;
|
||||
transform: scale(0.75);
|
||||
right: 0;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.Table .Dialogs {
|
||||
|
@ -325,6 +325,7 @@ const Table = () => {
|
||||
<div className="Sidebar">
|
||||
{ name !== "" && <PlayerList/> }
|
||||
{ name !== "" && <Chat/> }
|
||||
{ /* name !== "" && <VideoFeeds/> */ }
|
||||
{ loaded && <Actions {...{buildActive, setBuildActive}}/> }
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,12 +1,3 @@
|
||||
.MediaAgent {
|
||||
position: absolute;
|
||||
display: none;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 50000;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.MediaControl {
|
||||
display: flex;
|
||||
position: relative;
|
||||
@ -15,10 +6,20 @@
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.MediaControl > div {
|
||||
display: flex;
|
||||
.MediaControl .Video {
|
||||
width: 5rem;
|
||||
height: 3.75rem;
|
||||
max-width: 5rem;
|
||||
max-height: 3.75rem;
|
||||
background-color: #444;
|
||||
border-radius: 0.25rem;
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
.MediaAgent .Local {
|
||||
border: 3px solid red;
|
||||
}
|
||||
|
||||
.MediaControl > div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-right: 0.5rem;
|
||||
}
|
@ -6,27 +6,33 @@ import VolumeOff from '@mui/icons-material/VolumeOff';
|
||||
import VolumeUp from '@mui/icons-material/VolumeUp';
|
||||
import MicOff from '@mui/icons-material/MicOff';
|
||||
import Mic from '@mui/icons-material/Mic';
|
||||
import VideocamOff from '@mui/icons-material/VideocamOff';
|
||||
import Videocam from '@mui/icons-material/Videocam';
|
||||
|
||||
import { GlobalContext } from "./GlobalContext.js";
|
||||
const debug = true;
|
||||
|
||||
/* Proxy object so we can pass in srcObject to <audio> */
|
||||
const Audio = ({ srcObject, ...props }) => {
|
||||
const refAudio = useRef(null);
|
||||
const Video = ({ srcObject, local, ...props }) => {
|
||||
const refVideo = useRef(null);
|
||||
useEffect(() => {
|
||||
if (!refAudio.current) {
|
||||
if (!refVideo.current) {
|
||||
return;
|
||||
}
|
||||
const ref = refAudio.current;
|
||||
if (debug) console.log('<audio> bind');
|
||||
const ref = refVideo.current;
|
||||
if (debug) console.log('<video> bind');
|
||||
ref.srcObject = srcObject;
|
||||
if (local) {
|
||||
ref.muted = true;
|
||||
}
|
||||
return () => {
|
||||
if (debug) console.log('<audio> unbind');
|
||||
if (debug) console.log('<video> unbind');
|
||||
if (ref) {
|
||||
ref.srcObject = undefined;
|
||||
}
|
||||
};
|
||||
}, [srcObject]);
|
||||
return <audio ref={refAudio} {...props} />;
|
||||
}, [srcObject, local]);
|
||||
return <video ref={refVideo} {...props} />;
|
||||
}
|
||||
|
||||
const MediaAgent = () => {
|
||||
@ -38,19 +44,13 @@ const MediaAgent = () => {
|
||||
const connection = event.target;
|
||||
|
||||
if (debug) console.log("ontrack", event);
|
||||
let isLocal = true;
|
||||
for (let key in peers) {
|
||||
if (peers[key].connection === connection) {
|
||||
isLocal = false;
|
||||
Object.assign(peers[key].attributes, {
|
||||
srcObject: event.streams[0]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (isLocal) {
|
||||
throw new Error('Should not be local!');
|
||||
}
|
||||
|
||||
if (debug) console.log(`media-agent - ontrack - remote`, peers);
|
||||
setPeers(Object.assign({}, peers));
|
||||
@ -78,27 +78,9 @@ const MediaAgent = () => {
|
||||
const connection = new RTCPeerConnection({
|
||||
configuration: {
|
||||
offerToReceiveAudio: true,
|
||||
offerToReceiveVideo: false
|
||||
offerToReceiveVideo: true
|
||||
},
|
||||
iceServers: [
|
||||
/*
|
||||
{ urls: "stun:stun.l.google.com:19302" },
|
||||
{ urls: "stun:stun.stunprotocol.org:3478" },
|
||||
*/
|
||||
/*
|
||||
{
|
||||
urls: "turn:ketrenos.com:3478",
|
||||
username: "ketra",
|
||||
credential: "ketran"
|
||||
},
|
||||
*/
|
||||
/*{
|
||||
urls: "turn:ketrenos.com:3478?transport=tcp",
|
||||
username: "ketra",
|
||||
credential: "ketran"
|
||||
},
|
||||
*/
|
||||
{
|
||||
iceServers: [ {
|
||||
urls: "turns:ketrenos.com:5349",
|
||||
username: "ketra",
|
||||
credential: "ketran"
|
||||
@ -112,15 +94,12 @@ const MediaAgent = () => {
|
||||
*/
|
||||
|
||||
]
|
||||
}, {
|
||||
/* this will no longer be needed by chrome
|
||||
* eventually (supposedly), but is necessary
|
||||
* for now to get firefox to talk to chrome */
|
||||
//optional: [{DtlsSrtpKeyAgreement: true}]
|
||||
});
|
||||
|
||||
peers[peer_id] = {
|
||||
connection,
|
||||
muted: false,
|
||||
videoOn: true,
|
||||
attributes: {
|
||||
}
|
||||
};
|
||||
@ -206,19 +185,11 @@ const MediaAgent = () => {
|
||||
}
|
||||
|
||||
const sessionDescription = ({ peer_id, session_description }) => {
|
||||
/**
|
||||
* Peers exchange session descriptions which contains information
|
||||
* about their audio / video settings and that sort of stuff. First
|
||||
* the 'offerer' sends a description to the 'answerer' (with type
|
||||
* "offer"), then the answerer sends one back (with type "answer").
|
||||
*/
|
||||
// console.log('Remote description received: ', peer_id, session_description);
|
||||
const peer = peers[peer_id];
|
||||
if (!peer) {
|
||||
console.error(`media-agent - sessionDescription - No peer for ${peer_id}`);
|
||||
return;
|
||||
}
|
||||
// console.log(session_description);
|
||||
const { connection } = peer;
|
||||
const desc = new RTCSessionDescription(session_description);
|
||||
return connection.setRemoteDescription(desc, () => {
|
||||
@ -240,15 +211,12 @@ const MediaAgent = () => {
|
||||
console.error(`media-agent - sessionDescription - Answer setLocalDescription failed!`);
|
||||
});
|
||||
}, (error) => {
|
||||
// console.log("Error creating answer: ", error);
|
||||
console.error(peer);
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
}, (error) => {
|
||||
console.log(`media-agent - sessionDescription - setRemoteDescription error: `, error);
|
||||
});
|
||||
|
||||
// console.log("Description Object: ", desc);
|
||||
};
|
||||
|
||||
const removePeer = ({peer_id}) => {
|
||||
@ -348,7 +316,10 @@ const MediaAgent = () => {
|
||||
peers[name] = {
|
||||
local: true,
|
||||
muted: true,
|
||||
videoOn: false,
|
||||
attributes: {
|
||||
local: true,
|
||||
srcObject: stream
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -381,10 +352,25 @@ const MediaAgent = () => {
|
||||
navigator.mozGetUserMedia ||
|
||||
navigator.msGetUserMedia);
|
||||
|
||||
return navigator.mediaDevices.getUserMedia({audio: true, video: false})//, "video": true})
|
||||
return navigator.mediaDevices.getUserMedia({audio: true, video: true})
|
||||
.then((media) => { /* user accepted access to a/v */
|
||||
console.log("Access granted to audio/video");
|
||||
setStream(media);
|
||||
media.getVideoTracks().forEach((track) => {
|
||||
track.applyConstraints({
|
||||
"video": {
|
||||
"width": {
|
||||
"min": 160,
|
||||
"max": 320
|
||||
},
|
||||
"height": {
|
||||
"min": 120,
|
||||
"max": 240
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return media;
|
||||
});
|
||||
};
|
||||
|
||||
@ -393,106 +379,109 @@ const MediaAgent = () => {
|
||||
}
|
||||
|
||||
if (debug) console.log(`media-agent - WebSocket open request. Attempting to create local media.`)
|
||||
setup_local_media().then(() => {
|
||||
/* once the user has given us access to their
|
||||
* microphone/camcorder, join the channel and start peering up */
|
||||
join();
|
||||
}).catch((error) => { /* user denied access to a/v */
|
||||
console.error(error);
|
||||
console.log("Access denied for audio/video");
|
||||
});
|
||||
}, [ws, setStream, name, sendMessage]);
|
||||
|
||||
if (!ws) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
console.log(`media-agent - render`, peers);
|
||||
|
||||
const audioPeers = [];
|
||||
for (let id in peers) {
|
||||
const peer = peers[id];
|
||||
if (peer.local) {
|
||||
peer.attributes.srcObject = stream;
|
||||
if (peer.attributes.srcObject) {
|
||||
peer.attributes.srcObject.getAudioTracks().forEach((track) => {
|
||||
track.enabled = !peer.muted;
|
||||
});
|
||||
}
|
||||
audioPeers.push(
|
||||
<Audio
|
||||
className={peer.local ? 'Local' : 'Remote'}
|
||||
key={`Peer-${id}`}
|
||||
autoPlay='autoplay'
|
||||
controls
|
||||
muted
|
||||
{...peer.attributes}/>
|
||||
);
|
||||
} else {
|
||||
if (peer.muted) {
|
||||
audioPeers.push(
|
||||
<Audio
|
||||
className={peer.local ? 'Local' : 'Remote'}
|
||||
key={`Peer-${id}`}
|
||||
autoPlay='autoplay'
|
||||
controls
|
||||
muted
|
||||
{...peer.attributes}/>
|
||||
);
|
||||
} else {
|
||||
audioPeers.push(
|
||||
<Audio
|
||||
className={peer.local ? 'Local' : 'Remote'}
|
||||
key={`Peer-${id}`}
|
||||
autoPlay='autoplay'
|
||||
controls
|
||||
{...peer.attributes}/>
|
||||
);
|
||||
}
|
||||
if (!stream) {
|
||||
setup_local_media().then((media) => {
|
||||
/* once the user has given us access to their
|
||||
* microphone/camcorder, join the channel and start peering up */
|
||||
setStream(media);
|
||||
join();
|
||||
}).catch((error) => { /* user denied access to a/v */
|
||||
console.error(error);
|
||||
console.log("Access denied for audio/video");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return <div className="MediaAgent">
|
||||
{ audioPeers }
|
||||
</div>;
|
||||
}, [ws, setStream, stream, peers, name, sendMessage]);
|
||||
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const MediaControl = ({isSelf, peer}) => {
|
||||
const { peers, setPeers } = useContext(GlobalContext);
|
||||
const { peers } = useContext(GlobalContext);
|
||||
const [control, setControl] = useState(undefined);
|
||||
|
||||
const [media, setMedia] = useState(undefined);
|
||||
const [muted, setMuted] = useState(undefined);
|
||||
const [videoOn, setVideoOn] = useState(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
setControl(peers[peer]);
|
||||
}, [peer, peers, setControl]);
|
||||
|
||||
const toggleMute = (event) => {
|
||||
if (control) {
|
||||
control.muted = !control.muted;
|
||||
}
|
||||
const update = Object.assign({}, peers);
|
||||
update[peer].muted = control.muted;
|
||||
if (debug) console.log(`MediaControl - toggleMute`, update);
|
||||
setPeers(update);
|
||||
if (debug) console.log(`MediaControl - toggleMute`, !muted);
|
||||
setMuted(!muted);
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
if (!control) {
|
||||
return <div className="MediaControl">
|
||||
{ isSelf && <MicOff color={'disabled'}/> }
|
||||
{ !isSelf && <VolumeOff color={'disabled'}/> }
|
||||
</div>;
|
||||
|
||||
const toggleVideo = (event) => {
|
||||
if (debug) console.log(`MediaControl - toggleVideo`, !videoOn);
|
||||
setVideoOn(!videoOn);
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!control) {
|
||||
setMedia(undefined);
|
||||
return;
|
||||
}
|
||||
setMuted(control.muted);
|
||||
setVideoOn(control.videoOn);
|
||||
setMedia(control);
|
||||
}, [control, setMedia]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!control) {
|
||||
return;
|
||||
}
|
||||
if (control.attributes.srcObject) {
|
||||
control.attributes.srcObject.getAudioTracks().forEach((track) => {
|
||||
track.enabled = !muted;
|
||||
});
|
||||
}
|
||||
}, [control, muted]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!control) {
|
||||
return;
|
||||
}
|
||||
if (control.attributes.srcObject) {
|
||||
control.attributes.srcObject.getVideoTracks().forEach((track) => {
|
||||
track.enabled = videoOn;
|
||||
});
|
||||
}
|
||||
}, [control, videoOn]);
|
||||
|
||||
if (!control) {
|
||||
return <div className="MediaControl">
|
||||
<div>
|
||||
{ isSelf && <MicOff color={'disabled'}/> }
|
||||
{ !isSelf && <VolumeOff color={'disabled'}/> }
|
||||
<VideocamOff color={'disabled'}/>
|
||||
</div>
|
||||
<video className="Video"></video>
|
||||
</div>;
|
||||
}
|
||||
|
||||
return <div className="MediaControl">
|
||||
{ isSelf && <div onClick={toggleMute}>
|
||||
{ control.muted && <MicOff color={'primary'}/> }
|
||||
{ !control.muted && <Mic color={'primary'}/> }
|
||||
<div>
|
||||
{ isSelf && <div onClick={toggleMute}>
|
||||
{ muted && <MicOff color={'primary'}/> }
|
||||
{ !muted && <Mic color={'primary'}/> }
|
||||
</div> }
|
||||
{ !isSelf && <div onClick={toggleMute}>
|
||||
{ control.muted && <VolumeOff color={'primary'}/> }
|
||||
{ !control.muted && <VolumeUp color={'primary'}/> }
|
||||
{ muted && <VolumeOff color={'primary'}/> }
|
||||
{ !muted && <VolumeUp color={'primary'}/> }
|
||||
</div> }
|
||||
</div>;
|
||||
<div onClick={toggleVideo}>
|
||||
{ !videoOn && <VideocamOff color={'primary'}/> }
|
||||
{ videoOn && <Videocam color={'primary'}/> }
|
||||
</div>
|
||||
</div>
|
||||
{ media && <Video className="Video"
|
||||
autoPlay='autoplay'
|
||||
{...media.attributes}/>
|
||||
}
|
||||
</div>;
|
||||
};
|
||||
|
||||
export { MediaControl, MediaAgent };
|
||||
|
@ -27,6 +27,7 @@
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.PlayerList .Unselected > div {
|
||||
@ -35,10 +36,15 @@
|
||||
align-items: center;
|
||||
margin: 0.25rem;
|
||||
padding: 0.25rem;
|
||||
max-width: 8rem;
|
||||
width: 8rem;
|
||||
text-overflow: ellipsis;
|
||||
background-color: #eee;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
|
||||
.PlayerList .Unselected .Self {
|
||||
background-color: rgba(255, 255, 0, 0.5);
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
.PlayerList .PlayerSelector .PlayerColor {
|
||||
@ -66,18 +72,24 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.PlayerList .PlayerEntry {
|
||||
.PlayerList .PlayerSelector .PlayerEntry {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-overflow: ellipsis;
|
||||
flex: 1 1 0px;
|
||||
align-items: flex-start;
|
||||
border: 1px solid rgba(0,0,0,0);
|
||||
border-radius: 0.5em;
|
||||
min-width: 10em;
|
||||
border-radius: 0.25em;
|
||||
min-width: 11em;
|
||||
max-width: 11rem;
|
||||
padding: 0.25rem;
|
||||
}
|
||||
|
||||
.PlayerList .PlayerSelector .PlayerEntry {
|
||||
flex: 1 1 0px;
|
||||
align-items: center;
|
||||
display: inline-flex;
|
||||
.PlayerList .PlayerSelector .PlayerEntry > div {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
min-width: 10em;
|
||||
align-items: center;
|
||||
align-self: stretch;
|
||||
}
|
||||
|
||||
.PlayerList .PlayerEntry[data-selectable=true]:hover {
|
||||
@ -86,12 +98,13 @@
|
||||
}
|
||||
|
||||
.PlayerList .PlayerEntry[data-selected=true] {
|
||||
background-color: rgba(255, 255, 0, 0.5);
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
.PlayerList .PlayerEntry .MediaControl {
|
||||
.PlayerList .PlayerSelector .PlayerEntry .MediaControl {
|
||||
display: flex;
|
||||
justify-self: flex-end;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
|
||||
|
@ -94,9 +94,11 @@ const PlayerList = () => {
|
||||
className="PlayerEntry"
|
||||
onClick={() => { inLobby && selectable && toggleSelected(key) }}
|
||||
key={`player-${key}`}>
|
||||
<PlayerColor color={key}/>
|
||||
<div className="Name">{name ? name : 'Available' }</div>
|
||||
<div>
|
||||
<PlayerColor color={key}/>
|
||||
<div className="Name">{name ? name : 'Available' }</div>
|
||||
{ name && !item.live && <div className="NoNetwork"></div> }
|
||||
</div>
|
||||
{ name && item.live && <MediaControl peer={name} isSelf={key === color}/> }
|
||||
{ !name && <div></div> }
|
||||
</div>
|
||||
|
@ -1,8 +1,6 @@
|
||||
.Trade {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin: 0.25rem;
|
||||
@ -11,7 +9,7 @@
|
||||
.Trade > * {
|
||||
max-height: calc(100vh - 2rem);
|
||||
overflow: auto;
|
||||
width: 40rem;
|
||||
width: 32rem;
|
||||
display: inline-flex;
|
||||
padding: 0.5rem;
|
||||
flex-direction: column;
|
||||
|
@ -62,7 +62,7 @@ const Winner = ({ winnerDismissed, setWinnerDismissed }) => {
|
||||
type: 'goto-lobby'
|
||||
}));
|
||||
}
|
||||
}, [ws]);
|
||||
}, [ws, winnerDismissed, setWinnerDismissed]);
|
||||
|
||||
if (!winner || winnerDismissed) {
|
||||
return <></>;
|
||||
|
Loading…
x
Reference in New Issue
Block a user