Simplify watch-page live timing: rely on hls.js initialization only. #18

Closed
opened 2026-04-28 21:13:02 +02:00 by wandabastyle · 1 comment
wandabastyle commented 2026-04-28 21:13:02 +02:00 (Migrated from github.com)

Goal:
Remove the custom live-edge / auto-seek timing layer and make hls.js initialization the single source of truth for live latency and buffer behavior.

The desired player behavior:

  • Prefer auto quality, but target best/source.
  • Keep Twitch-like low latency.
  • Target roughly 5–7 seconds behind live.
  • Avoid fragile ~2 second live-edge hugging.
  • Avoid a delayed 12–16 second stable profile.
  • Do not have custom app code repeatedly seeking toward live.

Important:

  • hls.js supports seconds-based live sync via liveSyncDuration and liveMaxLatencyDuration.
  • Do not mix seconds-based live sync with count-based options.
  • Remove liveSyncDurationCount and liveMaxLatencyDurationCount from the HLS config.
  • Do not keep custom AUTO_LIVE_* control logic if it only exists to repeatedly seek toward live.
  • hls.js should handle live sync, catch-up, and max latency.

Tasks:

  1. Locate the embedded watch-page JavaScript

In src/app.rs, inside render_stream_page(...), find the embedded JS in the returned raw HTML string.

Look for:

  • hlsInstance = new Hls(...)
  • new Hls({
  • AUTO_LIVE_* constants
  • custom live-edge or auto-seek logic
  • custom seek-to-live loops/checks/cooldowns
  1. Remove custom live-edge / auto-seek control logic

Remove the app-level logic that repeatedly checks live drift and seeks closer to live.

Remove this kind of logic if present:

  • AUTO_LIVE_SEEK_LAG_SECS
  • AUTO_LIVE_TARGET_OFFSET_SECS
  • AUTO_LIVE_SEEK_CONSECUTIVE_CHECKS
  • AUTO_LIVE_SEEK_COOLDOWN_MS
  • AUTO_LIVE_CHECK_INTERVAL_MS
  • counters for consecutive live drift checks
  • cooldown timestamps for auto live seeking
  • periodic background checks that auto-seek toward live
  • logic that seeks to live edge minus a hardcoded offset such as 2 seconds
  • custom “force player closer to live” behavior

The player should not have an app-level live timing controller competing with hls.js.

  1. Keep Go Live only if it stays simple

If there is a user-facing Go Live button:

  • Keep it only as a simple user-triggered action.
  • It may seek once to hls.js live sync position if available.
  • Prefer using hlsInstance.liveSyncPosition when available.
  • Fallback to the end of the seekable range if needed.
  • Do not use or reintroduce AUTO_LIVE_* constants for this.
  • Do not keep a repeated background correction loop just to support Go Live.
  • If the old Go Live behavior depends on the removed auto-seek loop, simplify it.

Suggested helper shape if useful:

function seekToLive() {
if (!video) return;

const liveSyncPosition =
  hlsInstance && Number.isFinite(hlsInstance.liveSyncPosition)
    ? hlsInstance.liveSyncPosition
    : null;

if (liveSyncPosition !== null) {
  video.currentTime = liveSyncPosition;
  return;
}

const ranges = video.seekable;
if (ranges && ranges.length > 0) {
  video.currentTime = ranges.end(ranges.length - 1);
}

}

Keep this small and adapt it to the existing code style.

  1. Replace hls.js initialization with one clear profile

Find the existing hls.js initialization. It may currently look similar to:

hlsInstance = new Hls({
startPosition: -4,
lowLatencyMode: true,
liveSyncDurationCount: 1,
liveMaxLatencyDurationCount: 3,
maxLiveSyncPlaybackRate: 1.3,
maxBufferLength: 30,
maxMaxBufferLength: 60
});

Replace it with:

hlsInstance = new Hls({
startPosition: -6,
lowLatencyMode: true,

liveSyncDuration: 6,
liveMaxLatencyDuration: 14,

maxLiveSyncPlaybackRate: 1.1,

maxBufferLength: 20,
maxMaxBufferLength: 45,
backBufferLength: 15,

manifestLoadingTimeOut: 15000,
levelLoadingTimeOut: 15000,
fragLoadingTimeOut: 20000,

manifestLoadingMaxRetry: 3,
levelLoadingMaxRetry: 3,
fragLoadingMaxRetry: 5,

manifestLoadingRetryDelay: 750,
levelLoadingRetryDelay: 750,
fragLoadingRetryDelay: 750

});

Important:

  • Remove liveSyncDurationCount.
  • Remove liveMaxLatencyDurationCount.
  • Do not mix count-based and seconds-based live sync settings.
  • Keep lowLatencyMode: true.
  • Keep startPosition close to the target, around -6.
  • Keep best/source quality preference.
  • Do not change backend "best" stream session setup.
  1. Add a short explanatory comment

Near the HLS config, add a concise comment:

// Twitch-like low-latency HLS profile:
// hls.js is the single source of truth for live timing.
// Target roughly 5-7s behind live with enough buffer/retry tolerance
// to avoid stalls on imperfect networks.

  1. Preserve unrelated behavior

Do not change:

  • backend stream routing
  • stream resolver behavior
  • relay/CDN delivery behavior
  • auth
  • chat backend
  • recording code
  • channel list behavior
  • current "best" stream session default in render_watch_page
  • quality preference away from best/source

Do not break:

  • quality switching
  • quality menu state
  • progress bar display
  • controls visibility
  • play/pause controls
  • volume controls
  • fullscreen/theatre behavior
  • chat subscription/event stream
  • chat send box
  • emote picker
  • native HLS fallback
  • relay query handling
  1. Scope limits
  • Modify src/app.rs only.
  • Do not add new files.
  • Do not add new dependencies.
  • Do not introduce a settings UI.
  • Do not refactor the whole rendered HTML string.
  • Keep the Rust raw string syntax valid.
  • Run cargo fmt if formatting changes outside the raw string.

Acceptance criteria:

  • Watch page still loads and plays live streams.
  • Default stream session behavior still uses "best".
  • hls.js is the single source of truth for live latency/buffer timing.
  • Custom background live-edge / auto-seek logic is removed.
  • AUTO_LIVE_* constants are removed if they only existed for custom auto-seek control.
  • hls.js no longer uses liveSyncDurationCount or liveMaxLatencyDurationCount.
  • hls.js uses seconds-based live sync:
    • liveSyncDuration: 6
    • liveMaxLatencyDuration: 14
  • hls.js does not mix seconds-based and count-based live sync settings.
  • Go Live, if kept, is a simple user-triggered one-shot seek and does not run background correction logic.
  • Existing quality switching still works.
  • Chat and emote UI remain unaffected.
  • The generated Rust raw string remains valid.
Goal: Remove the custom live-edge / auto-seek timing layer and make hls.js initialization the single source of truth for live latency and buffer behavior. The desired player behavior: - Prefer auto quality, but target best/source. - Keep Twitch-like low latency. - Target roughly 5–7 seconds behind live. - Avoid fragile ~2 second live-edge hugging. - Avoid a delayed 12–16 second stable profile. - Do not have custom app code repeatedly seeking toward live. Important: - hls.js supports seconds-based live sync via liveSyncDuration and liveMaxLatencyDuration. - Do not mix seconds-based live sync with count-based options. - Remove liveSyncDurationCount and liveMaxLatencyDurationCount from the HLS config. - Do not keep custom AUTO_LIVE_* control logic if it only exists to repeatedly seek toward live. - hls.js should handle live sync, catch-up, and max latency. Tasks: 1. Locate the embedded watch-page JavaScript In src/app.rs, inside render_stream_page(...), find the embedded JS in the returned raw HTML string. Look for: - hlsInstance = new Hls(...) - new Hls({ - AUTO_LIVE_* constants - custom live-edge or auto-seek logic - custom seek-to-live loops/checks/cooldowns 2. Remove custom live-edge / auto-seek control logic Remove the app-level logic that repeatedly checks live drift and seeks closer to live. Remove this kind of logic if present: - AUTO_LIVE_SEEK_LAG_SECS - AUTO_LIVE_TARGET_OFFSET_SECS - AUTO_LIVE_SEEK_CONSECUTIVE_CHECKS - AUTO_LIVE_SEEK_COOLDOWN_MS - AUTO_LIVE_CHECK_INTERVAL_MS - counters for consecutive live drift checks - cooldown timestamps for auto live seeking - periodic background checks that auto-seek toward live - logic that seeks to live edge minus a hardcoded offset such as 2 seconds - custom “force player closer to live” behavior The player should not have an app-level live timing controller competing with hls.js. 3. Keep Go Live only if it stays simple If there is a user-facing Go Live button: - Keep it only as a simple user-triggered action. - It may seek once to hls.js live sync position if available. - Prefer using hlsInstance.liveSyncPosition when available. - Fallback to the end of the seekable range if needed. - Do not use or reintroduce AUTO_LIVE_* constants for this. - Do not keep a repeated background correction loop just to support Go Live. - If the old Go Live behavior depends on the removed auto-seek loop, simplify it. Suggested helper shape if useful: function seekToLive() { if (!video) return; const liveSyncPosition = hlsInstance && Number.isFinite(hlsInstance.liveSyncPosition) ? hlsInstance.liveSyncPosition : null; if (liveSyncPosition !== null) { video.currentTime = liveSyncPosition; return; } const ranges = video.seekable; if (ranges && ranges.length > 0) { video.currentTime = ranges.end(ranges.length - 1); } } Keep this small and adapt it to the existing code style. 4. Replace hls.js initialization with one clear profile Find the existing hls.js initialization. It may currently look similar to: hlsInstance = new Hls({ startPosition: -4, lowLatencyMode: true, liveSyncDurationCount: 1, liveMaxLatencyDurationCount: 3, maxLiveSyncPlaybackRate: 1.3, maxBufferLength: 30, maxMaxBufferLength: 60 }); Replace it with: hlsInstance = new Hls({ startPosition: -6, lowLatencyMode: true, liveSyncDuration: 6, liveMaxLatencyDuration: 14, maxLiveSyncPlaybackRate: 1.1, maxBufferLength: 20, maxMaxBufferLength: 45, backBufferLength: 15, manifestLoadingTimeOut: 15000, levelLoadingTimeOut: 15000, fragLoadingTimeOut: 20000, manifestLoadingMaxRetry: 3, levelLoadingMaxRetry: 3, fragLoadingMaxRetry: 5, manifestLoadingRetryDelay: 750, levelLoadingRetryDelay: 750, fragLoadingRetryDelay: 750 }); Important: - Remove liveSyncDurationCount. - Remove liveMaxLatencyDurationCount. - Do not mix count-based and seconds-based live sync settings. - Keep lowLatencyMode: true. - Keep startPosition close to the target, around -6. - Keep best/source quality preference. - Do not change backend "best" stream session setup. 5. Add a short explanatory comment Near the HLS config, add a concise comment: // Twitch-like low-latency HLS profile: // hls.js is the single source of truth for live timing. // Target roughly 5-7s behind live with enough buffer/retry tolerance // to avoid stalls on imperfect networks. 6. Preserve unrelated behavior Do not change: - backend stream routing - stream resolver behavior - relay/CDN delivery behavior - auth - chat backend - recording code - channel list behavior - current "best" stream session default in render_watch_page - quality preference away from best/source Do not break: - quality switching - quality menu state - progress bar display - controls visibility - play/pause controls - volume controls - fullscreen/theatre behavior - chat subscription/event stream - chat send box - emote picker - native HLS fallback - relay query handling 7. Scope limits - Modify src/app.rs only. - Do not add new files. - Do not add new dependencies. - Do not introduce a settings UI. - Do not refactor the whole rendered HTML string. - Keep the Rust raw string syntax valid. - Run cargo fmt if formatting changes outside the raw string. Acceptance criteria: - Watch page still loads and plays live streams. - Default stream session behavior still uses "best". - hls.js is the single source of truth for live latency/buffer timing. - Custom background live-edge / auto-seek logic is removed. - AUTO_LIVE_* constants are removed if they only existed for custom auto-seek control. - hls.js no longer uses liveSyncDurationCount or liveMaxLatencyDurationCount. - hls.js uses seconds-based live sync: - liveSyncDuration: 6 - liveMaxLatencyDuration: 14 - hls.js does not mix seconds-based and count-based live sync settings. - Go Live, if kept, is a simple user-triggered one-shot seek and does not run background correction logic. - Existing quality switching still works. - Chat and emote UI remain unaffected. - The generated Rust raw string remains valid.
wandabastyle commented 2026-04-28 21:37:44 +02:00 (Migrated from github.com)
https://github.com/wandabastyle/twitch_relay/commit/ac7b49b2cd50b73ddc18c73fc5fb74eae2ec766a
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
wandabastyle/twitch_relay#18
No description provided.