import Hls from 'hls.js'; import { log } from 'node:console'; let hlsInstance: Hls | null = null; // let hlsRetryCount = 0; export const initHls = (monitorVideoUrl, monitorVideoRef, isDialogVisible?: () => boolean) => { console.log('isDialogVisible', isDialogVisible); const url = monitorVideoUrl.value; const video = monitorVideoRef.value; if (!url || !video) return; // 销毁已有实例 if (hlsInstance) { try { hlsInstance.destroy(); } catch (e) { // ignore } hlsInstance = null; } if (Hls.isSupported()) { const hls = new Hls({ // ★ ZLM 注册流需要时间,配置自动重试 manifestLoadingMaxRetry: 5, // m3u8 加载最多重试20次 manifestLoadingRetryDelay: 500, // 每次重试间隔500ms manifestLoadingTimeOut: 5000, // m3u8 请求超时5秒 levelLoadingMaxRetry: 2, // ts 分片加载最多重试20次 levelLoadingRetryDelay: 500, lowLatencyMode: false, // 关掉低延迟模式,更稳定 startLevel: -1 // 自动选择码率 }); hls.loadSource(url); hls.attachMedia(video); hls.on(Hls.Events.MANIFEST_PARSED, () => { // 自动播放(若浏览器允许) try { video.play().catch(() => {}); } catch (e) {} // 加载成功,重置重试计数 // hlsRetryCount = 0; }); // ★ 错误自动恢复:hls.js 拿到空 m3u8 或 ts 加载失败时自动重试 hls.on(Hls.Events.ERROR, (event, data) => { if (data.fatal && (!isDialogVisible || isDialogVisible())) { switch (data.type) { case Hls.ErrorTypes.NETWORK_ERROR: hls.startLoad(); hls.once(Hls.Events.FRAG_LOADED, () => { //分片加载成功后播放 try { video.play().catch(() => {}); } catch (e) {} }); ElMessage.error('网络错误, 请检查网络或重试'); break; case Hls.ErrorTypes.MEDIA_ERROR: // 媒体解码错误 → 尝试恢复 hls.recoverMediaError(); break; default: // 其他致命错误 → 销毁,让下一次 initHls 重建 hls.destroy(); hlsInstance = null; break; } } }); hlsInstance = hls; } else if (video.canPlayType('application/vnd.apple.mpegurl')) { // Safari/原生支持 HLS video.src = url; video.addEventListener('loadedmetadata', () => { try { video.play().catch(() => {}); } catch (e) {} }); } else { //proxy?.$modal.msgWarning('当前浏览器不支持 HLS 播放'); ElMessage.error('当前浏览器不支持 HLS 播放'); } }; export const destroyHls = (monitorVideoRef) => { try { if (hlsInstance) { hlsInstance.destroy(); hlsInstance = null; } } catch (e) {} const video = monitorVideoRef.value; if (video) { // 清理原生 src,避免残留 try { video.pause(); video.removeAttribute('src'); // 对某些浏览器,需要调用 load() 来重置 media element if (typeof video.load === 'function') video.load(); } catch (e) { console.log('Error in destroyHls:', e); } } }; export default { initHls, destroyHls };