139 lines
4.4 KiB
JavaScript
139 lines
4.4 KiB
JavaScript
const socket = io();
|
|
const peerConnections = {};
|
|
const startBtn = document.getElementById('startBtn');
|
|
const localVideo = document.getElementById('localVideo');
|
|
const statusDot = document.getElementById('statusDot');
|
|
const statusText = document.getElementById('statusText');
|
|
|
|
let activeStream;
|
|
|
|
const config = {
|
|
iceServers: [
|
|
{ urls: "stun:localhost:3478" },
|
|
{
|
|
urls: "turn:localhost:3478",
|
|
username: "myuser",
|
|
credential: "mypassword"
|
|
}
|
|
]
|
|
};
|
|
|
|
socket.on('viewer', id => {
|
|
if (!activeStream) return;
|
|
|
|
const peerConnection = new RTCPeerConnection(config);
|
|
peerConnections[id] = peerConnection;
|
|
|
|
activeStream.getTracks().forEach(track => {
|
|
const sender = peerConnection.addTrack(track, activeStream);
|
|
|
|
// Force high bitrate for best possible 1080p60 quality
|
|
if (track.kind === 'video') {
|
|
const params = sender.getParameters();
|
|
if (!params.encodings) params.encodings = [{}];
|
|
|
|
// Set max bandwidth to 10 Mbps (10,000,000 bps)
|
|
params.encodings[0].maxBitrate = 10000000;
|
|
|
|
sender.setParameters(params).catch(e => console.error("Could not set high bitrate:", e));
|
|
}
|
|
});
|
|
|
|
peerConnection.onicecandidate = event => {
|
|
if (event.candidate) {
|
|
socket.emit('candidate', id, event.candidate);
|
|
}
|
|
};
|
|
|
|
peerConnection
|
|
.createOffer()
|
|
.then(sdp => {
|
|
// Force VP8/H264 on the SDP offer to maximize compatibility with Chromium
|
|
if (window.RTCRtpSender && window.RTCRtpSender.getCapabilities) {
|
|
const capabilities = window.RTCRtpSender.getCapabilities('video');
|
|
if (capabilities && capabilities.codecs) {
|
|
const preferredCodecs = capabilities.codecs.filter(c =>
|
|
c.mimeType.toLowerCase() === 'video/vp8' ||
|
|
c.mimeType.toLowerCase() === 'video/h264'
|
|
);
|
|
if (preferredCodecs.length > 0) {
|
|
const transceivers = peerConnection.getTransceivers();
|
|
transceivers.forEach(transceiver => {
|
|
if (transceiver.receiver.track.kind === 'video') {
|
|
try {
|
|
transceiver.setCodecPreferences(preferredCodecs);
|
|
} catch (e) {
|
|
console.warn('Failed to set codec preferences:', e);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
return peerConnection.setLocalDescription(sdp);
|
|
})
|
|
.then(() => {
|
|
socket.emit('offer', id, peerConnection.localDescription);
|
|
});
|
|
});
|
|
|
|
socket.on('answer', (id, description) => {
|
|
if (peerConnections[id]) {
|
|
peerConnections[id].setRemoteDescription(description);
|
|
}
|
|
});
|
|
|
|
socket.on('candidate', (id, candidate) => {
|
|
if (peerConnections[id]) {
|
|
peerConnections[id].addIceCandidate(new RTCIceCandidate(candidate));
|
|
}
|
|
});
|
|
|
|
socket.on('disconnectPeer', id => {
|
|
if (peerConnections[id]) {
|
|
peerConnections[id].close();
|
|
delete peerConnections[id];
|
|
}
|
|
});
|
|
|
|
startBtn.addEventListener('click', async () => {
|
|
try {
|
|
const stream = await navigator.mediaDevices.getDisplayMedia({
|
|
video: {
|
|
width: { ideal: 1920 },
|
|
height: { ideal: 1080 },
|
|
frameRate: { ideal: 60 }
|
|
},
|
|
audio: false
|
|
});
|
|
|
|
activeStream = stream;
|
|
localVideo.srcObject = stream;
|
|
localVideo.classList.add('active');
|
|
|
|
startBtn.style.display = 'none';
|
|
statusDot.classList.add('active');
|
|
statusText.innerText = 'Sharing Screen (1080p60)';
|
|
|
|
socket.emit('broadcaster');
|
|
|
|
stream.getVideoTracks()[0].onended = () => {
|
|
stopSharing();
|
|
};
|
|
|
|
} catch (err) {
|
|
console.error("Error accessing display media.", err);
|
|
statusText.innerText = 'Failed to access screen';
|
|
statusDot.style.backgroundColor = 'var(--error-color)';
|
|
}
|
|
});
|
|
|
|
function stopSharing() {
|
|
startBtn.style.display = 'inline-block';
|
|
localVideo.classList.remove('active');
|
|
statusDot.classList.remove('active');
|
|
statusText.innerText = 'Not Sharing';
|
|
activeStream = null;
|
|
socket.emit('disconnect');
|
|
}
|