unmute me

This commit is contained in:
legop3
2026-07-11 03:33:19 -04:00
parent c06ac6bc20
commit 0c0b55fe88
6 changed files with 57 additions and 14 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -78,7 +78,7 @@
<script defer src="https://analytics.otter.land/script.js" data-website-id="82dd56a5-db44-4279-bd1e-a4d9fee39af7" data-domains="rover.otter.land"></script>
<script defer src="https://analytics.otter.land/recorder.js" data-website-id="82dd56a5-db44-4279-bd1e-a4d9fee39af7" data-domains="rover.otter.land" data-sample-rate="0.15" data-mask-level="moderate" data-max-duration="300000"></script>
<title>Roomba Rover</title>
<script type="module" crossorigin src="/assets/index-DM5rqxP4.js"></script>
<script type="module" crossorigin src="/assets/index-C_Ron8xr.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DwpUkSbG.css">
</head>
<body>
@@ -12,6 +12,7 @@ import { isFeatureEnabled } from '../../lib/features.js';
import { innerFlowClass } from './constants.js';
const PTZ_CAMERA_ID = 'ptz-camera';
const PTZ_AUDIO_RETRY_MS = 1000;
function formatRemaining(deadline) {
const remaining = Math.max(0, Math.ceil((Number(deadline || 0) - Date.now()) / 1000));
@@ -81,6 +82,7 @@ function PtzLiveVideo({ enabled }) {
const videoRef = useRef(null);
const playerRef = useRef(null);
const retryTimerRef = useRef(null);
const playTimerRef = useRef(null);
const [status, setStatus] = useState('idle');
const [retryVersion, setRetryVersion] = useState(0);
const sources = useVideoRequests(
@@ -114,6 +116,7 @@ function PtzLiveVideo({ enabled }) {
url: source.url,
token: source.token,
video: videoRef.current,
startMuted: false,
onStatus: (nextStatus) => {
setStatus(nextStatus);
if (['error', 'failed', 'disconnected', 'closed'].includes(String(nextStatus || '').toLowerCase())) {
@@ -132,12 +135,45 @@ function PtzLiveVideo({ enabled }) {
};
}, [enabled, scheduleRetry, source?.token, source?.url]);
useEffect(() => {
const video = videoRef.current;
if (!enabled || !source?.url || !video) {
if (playTimerRef.current) clearInterval(playTimerRef.current);
playTimerRef.current = null;
return undefined;
}
/*
The server now publishes inline Opus audio on the PTZ WHEP stream. The
shared WHEP helper starts playback once, but browsers can still reject or
pause audible media depending on the exact timing of the fullscreen/user
gesture. Retry the same element with muted=false so unmuting is not a
manual DevTools-only operation.
*/
const attemptPlay = () => {
const target = videoRef.current;
if (!target) return;
target.muted = false;
if (!target.paused && !target.ended) return;
target.play().catch(() => {});
};
attemptPlay();
playTimerRef.current = setInterval(attemptPlay, PTZ_AUDIO_RETRY_MS);
return () => {
if (playTimerRef.current) clearInterval(playTimerRef.current);
playTimerRef.current = null;
};
}, [enabled, source?.url, status]);
useEffect(() => {
return () => {
if (retryTimerRef.current) {
clearTimeout(retryTimerRef.current);
retryTimerRef.current = null;
}
if (playTimerRef.current) {
clearInterval(playTimerRef.current);
playTimerRef.current = null;
}
};
}, []);
+9 -2
View File
@@ -29,12 +29,19 @@ function buildAuthHeader(token) {
}
export class WhepPlayer {
constructor({ url, token, video, onStatus, audioOnly = false, receiveAudio = true }) {
constructor({ url, token, video, onStatus, audioOnly = false, receiveAudio = true, startMuted = null }) {
this.url = url;
this.token = token;
this.video = video;
this.audioOnly = audioOnly;
this.receiveAudio = receiveAudio;
/*
Browser autoplay usually requires normal video streams to start muted,
while audio-only streams need to start audible. Keep that historical
default, but allow a caller that is already behind a user gesture, such as
the PTZ fullscreen controller, to request audible inline audio.
*/
this.startMuted = startMuted === null ? !audioOnly : Boolean(startMuted);
this.pc = null;
this.abortController = null;
this.onStatus = onStatus;
@@ -50,7 +57,7 @@ export class WhepPlayer {
if (!this.video) return;
this.video.playsInline = true;
this.video.autoplay = true;
this.video.muted = this.audioOnly ? false : true;
this.video.muted = this.startMuted;
if (typeof this.video.disableRemotePlayback !== 'undefined') {
this.video.disableRemotePlayback = true;
}