diff --git a/README.md b/README.md index bca439e..4d9c138 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,8 @@ PORT=8081 npm start and periodic packet-loss, jitter, and RTT quality diagnostics. - Audio input has manual gain control and a live signal/clipping meter in the settings dialog; microphone processing is split into `public/audio-manager.js`. +- Screen sharing uses the browser's default codec negotiation, with selectable + `detail` (text/UI) or `motion` (gameplay) content hints. ## ICE configuration (server side) diff --git a/public/app.js b/public/app.js index d3ff79e..c1105e5 100644 --- a/public/app.js +++ b/public/app.js @@ -37,6 +37,7 @@ const FALLBACK_ICE = { iceServers: [{ urls: ['stun:turn.k6n.net:3478', 'stun:stu const LS_NAME = 'voicepoc.nick'; const LS_ICE = 'voicepoc.iceOverride'; const LS_AUDIO_INPUT = 'voicepoc.audioInput'; +const LS_SCREEN_HINT = 'voicepoc.screenHint'; const S = { ws: null, @@ -159,26 +160,6 @@ function preferOpus(pc) { } } -function preferH264(pc) { - const capabilities = globalThis.RTCRtpReceiver?.getCapabilities?.('video'); - const codecs = capabilities?.codecs || []; - const rank = (codec) => { - const mime = codec.mimeType?.toLowerCase() || ''; - if (mime === 'video/h264') return 0; - if (mime === 'video/vp8') return 1; - if (mime === 'video/vp9') return 2; - return 3; - }; - const ordered = [...codecs].sort((a, b) => rank(a) - rank(b)); - for (const transceiver of pc.getTransceivers()) { - if (transceiver.receiver?.track?.kind === 'video' || transceiver.sender?.track?.kind === 'video') { - try { transceiver.setCodecPreferences(ordered); } catch (err) { - captureRumIssue('Could not prefer H.264 video codec', 'WARNING', { error: err?.message || String(err) }); - } - } - } -} - function monitorPeerQuality(peer) { if (peer.qualityTimer) clearInterval(peer.qualityTimer); peer.qualityTimer = setInterval(async () => { @@ -1035,6 +1016,8 @@ async function startScreenShare() { for (const peer of S.peers.values()) addScreenToPeer(peer); const videoTrack = stream.getVideoTracks()[0]; if (videoTrack) { + const screenHint = localStorage.getItem(LS_SCREEN_HINT) || ''; + if ('contentHint' in videoTrack) videoTrack.contentHint = screenHint; videoTrack.addEventListener('ended', () => stopScreenShare(stream)); } renderScreens(); @@ -1057,7 +1040,6 @@ function addScreenToPeer(peer) { } peerSenders.set(streamId, senders); } - preferH264(peer.pc); S.screenSenders.set(peer.id, peerSenders); // renegotiation fires automatically } @@ -2199,6 +2181,8 @@ function openSettings() { const gain = $('#mic-gain'); if (gain) gain.value = S.audioManager.gain; updateMicGainUi(); + const screenHint = $('#screen-hint'); + if (screenHint) screenHint.value = localStorage.getItem(LS_SCREEN_HINT) || ''; const override = localStorage.getItem(LS_ICE); $('#override-enabled').checked = !!override; const current = override ? JSON.parse(override) : effectiveIce(); @@ -2220,6 +2204,10 @@ $('#mic-gain').oninput = (ev) => { S.audioManager.setGain(ev.target.value); updateMicGainUi(); }; +$('#screen-hint').onchange = (ev) => { + localStorage.setItem(LS_SCREEN_HINT, ev.target.value); + toast('Screen-share hint saved — applies next time you share', 'ok'); +}; $('#audio-input').onchange = (ev) => switchAudioInput(ev.target.value); $('#ice-cancel').onclick = () => $('#modal-backdrop').classList.add('hidden'); $('#modal-backdrop').onclick = (ev) => { diff --git a/public/index.html b/public/index.html index eb590ce..31b4a16 100644 --- a/public/index.html +++ b/public/index.html @@ -150,6 +150,14 @@

Changes apply immediately when connected, or the next time you join.

+

Screen sharing

+ + +

Details favors sharp text; motion favors smoother movement.

Overrides the server-provided ICE configuration (stored in localStorage, applied on next join).