openvibers/openvibe.live Commits
Stream player: a muted autoplay shows a 'Tap for sound' pill over the live picture (it was a dark overlay hiding the stream); a tap anywhere or a media volume key brings the sound in (#358)
Changes
@@@ -2107,6 +2107,13 @@ a.pulse-card:hover { border-color: var(--accent); transform: translateY(-2px); }/* Viewers and uptime: plain stats on the bar, not coloured pills (owner, 2026-10-20: "this gui should be improved"). */.viewer-badge i, .uptime-badge i { color: rgba(255,255,255,0.65); }++/* Muted by the browser's autoplay rule: the stream stays in view, a pill says so, and a tap anywhere on it brings the sound++ in (stream-player.js showUnmuteOverlay; the same look as the shared player's "Tap for sound"). */++.unmute-overlay { position: absolute; inset: 0; z-index: 20; cursor: pointer; background: transparent; -webkit-tap-highlight-color: transparent; }++.unmute-pill { position: absolute; left: 12px; top: 12px; display: inline-flex; align-items: center; gap: 8px; padding: 8px 14px 8px 11px; border-radius: 999px; background: rgba(0,0,0,0.66); color: #fff; font: 650 13px/1 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); box-shadow: 0 4px 18px rgba(0,0,0,0.35); animation: unmute-pop .3s ease-out both; }++.unmute-overlay:hover .unmute-pill, .unmute-overlay:focus-visible .unmute-pill { background: var(--accent-strong, var(--accent, #3b82f6)); color: var(--on-accent-strong, #fff); }++@keyframes unmute-pop { from { opacity: 0; transform: translateY(-4px) scale(.96); } }++@media (prefers-reduced-motion: reduce) { .unmute-pill { animation: none; } }/* ── DVR Progress Bar (Live Stream Seeking) ─────────────────── */.dvr-progress-wrap {@@@ -1040,14 +1040,10 @@ function startJSMPEG(wsUrl, canvas, placeholder, bufferProfile = getJsmpegBufferif (container && !document.getElementById('unmute-overlay')) {const overlay = document.createElement('div');overlay.id = 'unmute-overlay';-- overlay.style.cssText = 'position:absolute;inset:0;z-index:20;display:flex;align-items:center;justify-content:center;cursor:pointer;background:rgba(0,0,0,0.45);transition:background 0.2s;';-- overlay.innerHTML = `-- <div style="display:flex;flex-direction:column;align-items:center;gap:12px;">-- <div style="width:72px;height:72px;border-radius:50%;background:rgba(255,255,255,0.15);border:2px solid rgba(255,255,255,0.6);display:flex;align-items:center;justify-content:center;backdrop-filter:blur(4px);transition:transform 0.15s,background 0.15s;">-- <i class="fa-solid fa-play" style="font-size:1.8rem;color:#fff;margin-left:4px;"></i>-- </div>-- <span style="color:#fff;font-size:0.9rem;opacity:0.85;text-shadow:0 1px 4px rgba(0,0,0,0.6);">Click to enable audio</span>-- </div>`;++ overlay.className = 'unmute-overlay';++ overlay.setAttribute('role', 'button');++ overlay.setAttribute('aria-label', 'Turn the sound on');++ overlay.innerHTML = '<span class="unmute-pill"><i class="fa-solid fa-volume-xmark"></i> Tap for sound</span>';overlay.addEventListener('click', () => {audioCtx.resume().then(() => {console.log('[JSMPEG] AudioContext resumed by user click');@@@ -3578,9 +3574,16 @@ function _clearStreamError() {}}++// The media volume keys (UI Events names, and the older ones): while the stream is muted for autoplay, volume up is a tap.++document.addEventListener('keydown', (e) => {++ if (!/^(AudioVolumeUp|VolumeUp|AudioVolumeMute|VolumeMute)$/.test(e.key)) return;++ const overlay = document.getElementById('unmute-overlay');++ if (overlay) overlay.click();++});++/**-- * Show a click-to-play overlay when autoplay policy forced muted playback.-- * One tap unmutes, restores volume, and removes the overlay.++ * Show a "Tap for sound" pill when autoplay policy forced muted playback.++ * One tap anywhere on the picture unmutes, restores volume, and removes it.*/function showUnmuteOverlay(video) {// Don't duplicate@@@ -3589,18 +3592,12 @@ function showUnmuteOverlay(video) {if (!container) return;const overlay = document.createElement('div');overlay.id = 'unmute-overlay';-- overlay.style.cssText = 'position:absolute;inset:0;z-index:20;display:flex;align-items:center;justify-content:center;cursor:pointer;background:rgba(0,0,0,0.45);transition:background 0.2s;';-- overlay.innerHTML = `-- <div style="display:flex;flex-direction:column;align-items:center;gap:12px;">-- <div style="width:72px;height:72px;border-radius:50%;background:rgba(255,255,255,0.15);border:2px solid rgba(255,255,255,0.6);display:flex;align-items:center;justify-content:center;backdrop-filter:blur(4px);transition:transform 0.15s,background 0.15s;">-- <i class="fa-solid fa-play" style="font-size:1.8rem;color:#fff;margin-left:4px;"></i>-- </div>-- <span style="color:#fff;font-size:0.9rem;opacity:0.85;text-shadow:0 1px 4px rgba(0,0,0,0.6);">Click to enable audio</span>-- </div>`;-- // Hover effect on the play circle-- const circle = overlay.querySelector('div > div');-- overlay.addEventListener('mouseenter', () => { if (circle) { circle.style.transform = 'scale(1.1)'; circle.style.background = 'rgba(255,255,255,0.25)'; } });-- overlay.addEventListener('mouseleave', () => { if (circle) { circle.style.transform = ''; circle.style.background = ''; } });++ overlay.className = 'unmute-overlay';++ overlay.setAttribute('role', 'button');++ overlay.setAttribute('aria-label', 'Turn the sound on');++ // The stream stays in view (owner, 2026-10-25): a small pill says it is muted, and a tap anywhere on the picture or a++ // media volume key (where the browser passes it to the page) turns the sound on.++ overlay.innerHTML = '<span class="unmute-pill"><i class="fa-solid fa-volume-xmark"></i> Tap for sound</span>';overlay.addEventListener('click', () => {video.muted = false;// Restore volume from saved preference / slider
@@ -2107,6 +2107,13 @@ a.pulse-card:hover { border-color: var(--accent); transform: translateY(-2px); }
@@ -2107,6 +2107,13 @@ a.pulse-card:hover { border-color: var(--accent); transform: translateY(-2px); }
/* Viewers and uptime: plain stats on the bar, not coloured pills (owner, 2026-10-20: "this gui should be improved"). */
/* Viewers and uptime: plain stats on the bar, not coloured pills (owner, 2026-10-20: "this gui should be improved"). */
.viewer-badge i, .uptime-badge i { color: rgba(255,255,255,0.65); }
.viewer-badge i, .uptime-badge i { color: rgba(255,255,255,0.65); }
+/* Muted by the browser's autoplay rule: the stream stays in view, a pill says so, and a tap anywhere on it brings the sound
+ in (stream-player.js showUnmuteOverlay; the same look as the shared player's "Tap for sound"). */
+.unmute-overlay { position: absolute; inset: 0; z-index: 20; cursor: pointer; background: transparent; -webkit-tap-highlight-color: transparent; }
+.unmute-pill { position: absolute; left: 12px; top: 12px; display: inline-flex; align-items: center; gap: 8px; padding: 8px 14px 8px 11px; border-radius: 999px; background: rgba(0,0,0,0.66); color: #fff; font: 650 13px/1 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); box-shadow: 0 4px 18px rgba(0,0,0,0.35); animation: unmute-pop .3s ease-out both; }
+.unmute-overlay:hover .unmute-pill, .unmute-overlay:focus-visible .unmute-pill { background: var(--accent-strong, var(--accent, #3b82f6)); color: var(--on-accent-strong, #fff); }
+@keyframes unmute-pop { from { opacity: 0; transform: translateY(-4px) scale(.96); } }
+@media (prefers-reduced-motion: reduce) { .unmute-pill { animation: none; } }
/* ── DVR Progress Bar (Live Stream Seeking) ─────────────────── */
/* ── DVR Progress Bar (Live Stream Seeking) ─────────────────── */
.dvr-progress-wrap {
.dvr-progress-wrap {
@@ -1040,14 +1040,10 @@ function startJSMPEG(wsUrl, canvas, placeholder, bufferProfile = getJsmpegBuffer
@@ -1040,14 +1040,10 @@ function startJSMPEG(wsUrl, canvas, placeholder, bufferProfile = getJsmpegBuffer
if (container && !document.getElementById('unmute-overlay')) {
if (container && !document.getElementById('unmute-overlay')) {
const overlay = document.createElement('div');
const overlay = document.createElement('div');
overlay.id = 'unmute-overlay';
overlay.id = 'unmute-overlay';
- overlay.style.cssText = 'position:absolute;inset:0;z-index:20;display:flex;align-items:center;justify-content:center;cursor:pointer;background:rgba(0,0,0,0.45);transition:background 0.2s;';
- overlay.innerHTML = `
- <div style="display:flex;flex-direction:column;align-items:center;gap:12px;">
- <div style="width:72px;height:72px;border-radius:50%;background:rgba(255,255,255,0.15);border:2px solid rgba(255,255,255,0.6);display:flex;align-items:center;justify-content:center;backdrop-filter:blur(4px);transition:transform 0.15s,background 0.15s;">
- <i class="fa-solid fa-play" style="font-size:1.8rem;color:#fff;margin-left:4px;"></i>
- </div>
- <span style="color:#fff;font-size:0.9rem;opacity:0.85;text-shadow:0 1px 4px rgba(0,0,0,0.6);">Click to enable audio</span>
- </div>`;
+ overlay.className = 'unmute-overlay';
+ overlay.setAttribute('role', 'button');
+ overlay.setAttribute('aria-label', 'Turn the sound on');
+ overlay.innerHTML = '<span class="unmute-pill"><i class="fa-solid fa-volume-xmark"></i> Tap for sound</span>';
overlay.addEventListener('click', () => {
overlay.addEventListener('click', () => {
audioCtx.resume().then(() => {
audioCtx.resume().then(() => {
console.log('[JSMPEG] AudioContext resumed by user click');
console.log('[JSMPEG] AudioContext resumed by user click');
@@ -3578,9 +3574,16 @@ function _clearStreamError() {
@@ -3578,9 +3574,16 @@ function _clearStreamError() {
}
}
}
}
+// The media volume keys (UI Events names, and the older ones): while the stream is muted for autoplay, volume up is a tap.
+document.addEventListener('keydown', (e) => {
+ if (!/^(AudioVolumeUp|VolumeUp|AudioVolumeMute|VolumeMute)$/.test(e.key)) return;
+ const overlay = document.getElementById('unmute-overlay');
+ if (overlay) overlay.click();
+});
+
/**
/**
- * Show a click-to-play overlay when autoplay policy forced muted playback.
- * One tap unmutes, restores volume, and removes the overlay.
+ * Show a "Tap for sound" pill when autoplay policy forced muted playback.
+ * One tap anywhere on the picture unmutes, restores volume, and removes it.
*/
*/
function showUnmuteOverlay(video) {
function showUnmuteOverlay(video) {
// Don't duplicate
// Don't duplicate
@@ -3589,18 +3592,12 @@ function showUnmuteOverlay(video) {
@@ -3589,18 +3592,12 @@ function showUnmuteOverlay(video) {
if (!container) return;
if (!container) return;
const overlay = document.createElement('div');
const overlay = document.createElement('div');
overlay.id = 'unmute-overlay';
overlay.id = 'unmute-overlay';
- overlay.style.cssText = 'position:absolute;inset:0;z-index:20;display:flex;align-items:center;justify-content:center;cursor:pointer;background:rgba(0,0,0,0.45);transition:background 0.2s;';
- overlay.innerHTML = `
- <div style="display:flex;flex-direction:column;align-items:center;gap:12px;">
- <div style="width:72px;height:72px;border-radius:50%;background:rgba(255,255,255,0.15);border:2px solid rgba(255,255,255,0.6);display:flex;align-items:center;justify-content:center;backdrop-filter:blur(4px);transition:transform 0.15s,background 0.15s;">
- <i class="fa-solid fa-play" style="font-size:1.8rem;color:#fff;margin-left:4px;"></i>
- </div>
- <span style="color:#fff;font-size:0.9rem;opacity:0.85;text-shadow:0 1px 4px rgba(0,0,0,0.6);">Click to enable audio</span>
- </div>`;
- // Hover effect on the play circle
- const circle = overlay.querySelector('div > div');
- overlay.addEventListener('mouseenter', () => { if (circle) { circle.style.transform = 'scale(1.1)'; circle.style.background = 'rgba(255,255,255,0.25)'; } });
- overlay.addEventListener('mouseleave', () => { if (circle) { circle.style.transform = ''; circle.style.background = ''; } });
+ overlay.className = 'unmute-overlay';
+ overlay.setAttribute('role', 'button');
+ overlay.setAttribute('aria-label', 'Turn the sound on');
+ // The stream stays in view (owner, 2026-10-25): a small pill says it is muted, and a tap anywhere on the picture or a
+ // media volume key (where the browser passes it to the page) turns the sound on.
+ overlay.innerHTML = '<span class="unmute-pill"><i class="fa-solid fa-volume-xmark"></i> Tap for sound</span>';
overlay.addEventListener('click', () => {
overlay.addEventListener('click', () => {
video.muted = false;
video.muted = false;
// Restore volume from saved preference / slider
// Restore volume from saved preference / slider