fix(hls): retry on playback failure with exponential backoff
This commit is contained in:
parent
ce31a45124
commit
60e306d1db
1 changed files with 48 additions and 10 deletions
|
|
@ -387,23 +387,61 @@ function HlsPreview({ assetId, muted = true, controls = false, className }) {
|
|||
if (!assetId || !videoRef.current) return;
|
||||
const url = '/live/' + assetId + '/index.m3u8';
|
||||
const v = videoRef.current;
|
||||
let destroyed = false;
|
||||
let retryTimer = 0;
|
||||
let retryCount = 0;
|
||||
const MAX_RETRIES = 8;
|
||||
|
||||
const clearRetry = () => { if (retryTimer) { clearTimeout(retryTimer); retryTimer = 0; } };
|
||||
|
||||
// Safari can play HLS natively; everything else needs hls.js.
|
||||
if (v.canPlayType('application/vnd.apple.mpegurl')) {
|
||||
v.src = url;
|
||||
const onErr = () => setErr('playback failed');
|
||||
const tryLoad = () => {
|
||||
if (destroyed) return;
|
||||
v.removeAttribute('src');
|
||||
v.load();
|
||||
v.src = url;
|
||||
v.play().catch(() => {});
|
||||
};
|
||||
const onErr = () => {
|
||||
if (destroyed || retryCount >= MAX_RETRIES) { setErr('playback failed'); return; }
|
||||
retryCount++;
|
||||
clearRetry();
|
||||
retryTimer = setTimeout(tryLoad, Math.min(500 * Math.pow(2, retryCount - 1), 8000));
|
||||
setErr('connecting…');
|
||||
};
|
||||
v.addEventListener('error', onErr);
|
||||
return () => v.removeEventListener('error', onErr);
|
||||
v.addEventListener('playing', () => { retryCount = 0; setErr(null); }, { once: false });
|
||||
tryLoad();
|
||||
return () => { destroyed = true; clearRetry(); v.removeEventListener('error', onErr); };
|
||||
}
|
||||
if (!window.Hls) { setErr('hls.js missing'); return; }
|
||||
|
||||
const hls = new window.Hls({ liveSyncDurationCount: 2, lowLatencyMode: true });
|
||||
hls.loadSource(url);
|
||||
hls.attachMedia(v);
|
||||
hls.on(window.Hls.Events.ERROR, (_e, data) => {
|
||||
if (data.fatal) setErr(data.details || 'hls error');
|
||||
});
|
||||
return () => { try { hls.destroy(); } catch (_) {} };
|
||||
let hls = null;
|
||||
|
||||
const startHls = () => {
|
||||
if (destroyed) return;
|
||||
hls = new window.Hls({ liveSyncDurationCount: 2, lowLatencyMode: true });
|
||||
hls.loadSource(url);
|
||||
hls.attachMedia(v);
|
||||
hls.on(window.Hls.Events.ERROR, (_e, data) => {
|
||||
if (data.fatal) {
|
||||
if (retryCount >= MAX_RETRIES) { setErr(data.details || 'hls error'); return; }
|
||||
retryCount++;
|
||||
clearRetry();
|
||||
try { hls.destroy(); } catch (_) {}
|
||||
hls = null;
|
||||
setErr('connecting…');
|
||||
retryTimer = setTimeout(startHls, Math.min(500 * Math.pow(2, retryCount - 1), 8000));
|
||||
}
|
||||
});
|
||||
hls.on(window.Hls.Events.MANIFEST_PARSED, () => { retryCount = 0; setErr(null); });
|
||||
};
|
||||
|
||||
startHls();
|
||||
v.play().catch(() => {});
|
||||
|
||||
return () => { destroyed = true; clearRetry(); if (hls) { try { hls.destroy(); } catch (_) {} } };
|
||||
}, [assetId]);
|
||||
|
||||
return (
|
||||
|
|
|
|||
Loading…
Reference in a new issue