From b3ec5376873448dd77c2609cb2419f08cb562431 Mon Sep 17 00:00:00 2001 From: Sebastjan Date: Sat, 25 Apr 2026 15:10:39 +0200 Subject: [PATCH] Samsung TV fix: detect Tizen, lock to 720p, smaller buffer, better error recovery; revert 480p enable --- views/index.ejs | 88 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 77 insertions(+), 11 deletions(-) diff --git a/views/index.ejs b/views/index.ejs index 1ce5281..e20b8f3 100644 --- a/views/index.ejs +++ b/views/index.ejs @@ -643,6 +643,14 @@ function showMsg(html) { msg.innerHTML = html; msg.classList.add('visible'); } function hideMsg() { msg.classList.remove('visible'); } + // Device detection — Samsung Tizen, LG webOS, and other smart TVs need + // smaller buffers and lower resolution caps to avoid stalling. + const UA = navigator.userAgent || ''; + const IS_SAMSUNG_TV = /Tizen|SMART-TV|SmartTV|SamsungBrowser/i.test(UA); + const IS_LG_TV = /webOS|Web0S|LG SmartTV/i.test(UA); + const IS_TV = IS_SAMSUNG_TV || IS_LG_TV || /Hisense|Bravia|Roku|AppleTV|GoogleTV|AndroidTV/i.test(UA); + const IS_LOW_POWER = IS_TV; + function attach() { try { hls && hls.destroy(); } catch (e) {} hls = null; @@ -655,7 +663,31 @@ } if (window.Hls && window.Hls.isSupported()) { - hls = new Hls({ + // TV-tuned settings (smaller buffers, 720p cap, more aggressive recovery) + const cfg = IS_LOW_POWER ? { + liveDurationInfinity: true, + liveSyncDurationCount: 3, + liveMaxLatencyDurationCount: 8, + backBufferLength: 10, // Tizen has tight memory + maxBufferLength: 20, + maxMaxBufferLength: 30, + maxBufferSize: 30 * 1000 * 1000, // 30 MB buffer cap (Tizen-friendly) + manifestLoadingTimeOut: 15000, + manifestLoadingMaxRetry: 8, + manifestLoadingRetryDelay: 1000, + levelLoadingTimeOut: 15000, + levelLoadingMaxRetry: 8, + fragLoadingTimeOut: 25000, + fragLoadingMaxRetry: 8, + fragLoadingRetryDelay: 1000, + // Lock to 720p on TVs — Tizen decoder struggles with 1080p HLS.js + startLevel: -1, + capLevelToPlayerSize: false, + abrEwmaDefaultEstimate: 2000000, + testBandwidth: true, + abrBandWidthFactor: 0.9, + abrBandWidthUpFactor: 0.6, + } : { liveDurationInfinity: true, liveSyncDurationCount: 4, backBufferLength: 30, @@ -664,27 +696,61 @@ manifestLoadingMaxRetry: 6, fragLoadingTimeOut: 20000, fragLoadingMaxRetry: 6, - // ABR (Adaptive Bitrate) — let player auto-switch between qualities - startLevel: -1, // auto: start with whatever bandwidth allows - capLevelToPlayerSize: false, // don't cap to viewport size - abrEwmaDefaultEstimate: 3000000, // realistic 3 Mbps starting estimate - testBandwidth: true, // probe bandwidth on startup - abrBandWidthFactor: 0.95, // 95% of measured BW → safety margin - abrBandWidthUpFactor: 0.7, // need 70% headroom before upgrading - }); + startLevel: -1, + capLevelToPlayerSize: false, + abrEwmaDefaultEstimate: 3000000, + testBandwidth: true, + abrBandWidthFactor: 0.95, + abrBandWidthUpFactor: 0.7, + }; + hls = new Hls(cfg); hls.loadSource(HLS_URL); hls.attachMedia(video); hls.on(Hls.Events.MANIFEST_PARSED, () => { - // ABR enabled across ALL variants (1080p / 720p / 480p) - // Player will auto-pick best quality based on bandwidth. + try { + if (hls.levels && hls.levels.length) { + if (IS_LOW_POWER) { + // On Samsung TV / smart TV: lock to 720p (720p only, disable both 1080p and 480p). + // 1080p stalls on Tizen decoder, 480p is desynced from Rok's encoder. + hls.levels.forEach((lvl) => { + lvl.enabled = (lvl.height === 720); + }); + // Also force start at 720p + const idx720 = hls.levels.findIndex(l => l.height === 720); + if (idx720 >= 0) { + hls.currentLevel = idx720; + hls.loadLevel = idx720; + } + } else { + // Desktop/mobile: only disable 480p (desynced); allow 1080p ↔ 720p ABR. + hls.levels.forEach((lvl) => { + if (lvl.height && lvl.height <= 480) { + lvl.enabled = false; + } + }); + } + } + } catch (e) {} hideMsg(); video.play().catch(() => setTimeout(() => video.play().catch(() => {}), 200)); }); hls.on(Hls.Events.ERROR, (_e, data) => { if (!data.fatal) return; + // Try in-place recovery first (faster than full reattach, less disruptive on TV) + if (data.type === Hls.ErrorTypes.NETWORK_ERROR) { + try { hls.startLoad(); return; } catch (e) {} + } + if (data.type === Hls.ErrorTypes.MEDIA_ERROR) { + try { hls.recoverMediaError(); return; } catch (e) {} + } + // Last resort: full reattach showMsg('
Verbindung wird wiederhergestellt…
'); setTimeout(() => { attach(); }, 3000); }); + // Buffer stall recovery — esp. needed on Samsung TV + hls.on(Hls.Events.BUFFER_STALLED_ERROR, () => { + try { hls.startLoad(); } catch (e) {} + }); } else { showMsg('
HLS wird von diesem Browser nicht unterstützt.
'); }