Files
yass/public/app.js

154 lines
4.9 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');
const passwordInput = document.getElementById('broadcasterPassword');
const authContainer = document.getElementById('authContainer');
let activeStream;
const config = {
iceServers: [
{ urls: "stun:localhost:3478" },
{
urls: "turn:localhost:3478",
username: "myuser",
credential: "mypassword"
}
]
};
socket.on('authError', (msg) => {
alert(msg);
stopSharing();
});
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 () => {
const password = passwordInput.value;
if (!password) {
alert("Please enter the stream password to broadcast.");
return;
}
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';
authContainer.style.display = 'none';
statusDot.classList.add('active');
statusText.innerText = 'Sharing Screen (1080p60)';
socket.emit('broadcaster', password);
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';
authContainer.style.display = 'block';
localVideo.classList.remove('active');
statusDot.classList.remove('active');
statusText.innerText = 'Not Sharing';
activeStream = null;
socket.emit('disconnect');
}