Configure live polling update tolerance
What version of Hls.js are you using?
v1.5.20
What browser (including version) are you using?
safari
What OS (including version) are you using?
iphone, ios
Test stream
No response
Configuration
{}
Additional player setup steps
No response
Checklist
- [x] The issue observed is not already reported by searching on Github under https://github.com/video-dev/hls.js/issues
- [x] The issue occurs in the stable client (latest release) on https://hlsjs.video-dev.org/demo and not just on my page
- [x] The issue occurs in the latest client (main branch) on https://hlsjs-dev.video-dev.org/demo and not just on my page
- [x] The stream has correct Access-Control-Allow-Origin headers (CORS)
- [x] There are no network errors such as 404s in the browser console when trying to play the stream
Steps to reproduce
- Open the HLS.js player in Safari on an iPhone and start playing an HLS stream.
- Background the Safari browser, allowing the device to automatically lock and turn off the screen.
- Stop the OBS stream and wait for a few minutes.
- Reopen Safari and return to the HLS.js player.
Expected behaviour
After reopening Safari, the player should detect that the stream has stopped and throw an error event.
What actually happened?
The player remains in a loading state indefinitely without throwing any error event, even though the stream has stopped.
Console output
not error event
Chrome media internals output
Please include debug logs, preferably with timestamps to show the activity before and after locking the device.
Are the HLS playlists for the live stream still served after stopping OBS?
If so the current behavior is to keep reloading in case it comes back online. Serving an empty playlist or returning an HTTP status error code would produce a fatal error.
From the client, you can use HLS.js level event "misses" (no playlist change) and stall error events to detect that the live source is no updating.
Would you like a feature that stops HLS.js with an error after a certain period or number retries on a stalled live playlist?
Not OP but I think a config for max retries on a stalled playlist would be great. I'm facing similar where HLS.js will continue to fetch the same manifest despite no changes as the stream has ended.
I'm facing the same issue using Cloudflare streams, but on desktop. I pause the player, leave it idle for a while, when I come back and try to play again, I get HLS error. I didn't modify the config, I'm using HLS through Vidstack.
Error data:
Error stack:
Error: Playback stalling at @91.270427 due to low buffer ({"len":68.489572,"start":84.261333,"end":159.759999})
at GapController._reportStall (https://cdn.jsdelivr.net/npm/hls.js@%5E1.5.0/dist/hls.js:27253:21)
at GapController.poll (https://cdn.jsdelivr.net/npm/hls.js@%5E1.5.0/dist/hls.js:27192:14)
at StreamController.checkBuffer (https://cdn.jsdelivr.net/npm/hls.js@%5E1.5.0/dist/hls.js:28078:23)
at StreamController.onTickEnd (https://cdn.jsdelivr.net/npm/hls.js@%5E1.5.0/dist/hls.js:27525:12)
at StreamController.doTick (https://cdn.jsdelivr.net/npm/hls.js@%5E1.5.0/dist/hls.js:27521:12)
at StreamController.tick (https://cdn.jsdelivr.net/npm/hls.js@%5E1.5.0/dist/hls.js:7964:14)
Config of HLS.js instance:
{
"autoStartLoad": true,
"startPosition": -1,
"debug": false,
"capLevelOnFPSDrop": false,
"capLevelToPlayerSize": false,
"ignoreDevicePixelRatio": false,
"preferManagedMediaSource": true,
"initialLiveManifestSize": 1,
"maxBufferLength": 30,
"frontBufferFlushThreshold": null,
"maxBufferSize": 60000000,
"maxBufferHole": 0.1,
"highBufferWatchdogPeriod": 2,
"nudgeOffset": 0.1,
"nudgeMaxRetry": 3,
"maxFragLookUpTolerance": 0.25,
"liveSyncDurationCount": 3,
"liveMaxLatencyDurationCount": null,
"maxLiveSyncPlaybackRate": 1,
"liveDurationInfinity": false,
"liveBackBufferLength": null,
"maxMaxBufferLength": 600,
"enableWorker": true,
"workerPath": null,
"enableSoftwareAES": true,
"startFragPrefetch": false,
"fpsDroppedMonitoringPeriod": 5000,
"fpsDroppedMonitoringThreshold": 0.2,
"appendErrorMaxRetry": 3,
"stretchShortVideoTrack": false,
"maxAudioFramesDrift": 1,
"forceKeyFrameOnDiscontinuity": true,
"abrEwmaFastLive": 3,
"abrEwmaSlowLive": 9,
"abrEwmaFastVoD": 3,
"abrEwmaSlowVoD": 9,
"abrEwmaDefaultEstimate": 500000,
"abrEwmaDefaultEstimateMax": 5000000,
"abrBandWidthFactor": 0.95,
"abrBandWidthUpFactor": 0.7,
"abrMaxWithRealBitrate": false,
"maxStarvationDelay": 4,
"maxLoadingDelay": 4,
"minAutoBitrate": 0,
"emeEnabled": false,
"drmSystems": {},
"drmSystemOptions": {},
"testBandwidth": true,
"progressive": false,
"lowLatencyMode": false,
"enableDateRangeMetadataCues": true,
"enableEmsgMetadataCues": true,
"enableID3MetadataCues": true,
"useMediaCapabilities": true,
"certLoadPolicy": {
"default": {
"maxTimeToFirstByteMs": 8000,
"maxLoadTimeMs": 20000,
"timeoutRetry": null,
"errorRetry": null
}
},
"keyLoadPolicy": {
"default": {
"maxTimeToFirstByteMs": 8000,
"maxLoadTimeMs": 20000,
"timeoutRetry": {
"maxNumRetry": 1,
"retryDelayMs": 1000,
"maxRetryDelayMs": 20000,
"backoff": "linear"
},
"errorRetry": {
"maxNumRetry": 8,
"retryDelayMs": 1000,
"maxRetryDelayMs": 20000,
"backoff": "linear"
}
}
},
"manifestLoadPolicy": {
"default": {
"maxTimeToFirstByteMs": null,
"maxLoadTimeMs": 20000,
"timeoutRetry": {
"maxNumRetry": 2,
"retryDelayMs": 0,
"maxRetryDelayMs": 0
},
"errorRetry": {
"maxNumRetry": 1,
"retryDelayMs": 1000,
"maxRetryDelayMs": 8000
}
}
},
"playlistLoadPolicy": {
"default": {
"maxTimeToFirstByteMs": 10000,
"maxLoadTimeMs": 20000,
"timeoutRetry": {
"maxNumRetry": 2,
"retryDelayMs": 0,
"maxRetryDelayMs": 0
},
"errorRetry": {
"maxNumRetry": 2,
"retryDelayMs": 1000,
"maxRetryDelayMs": 8000
}
}
},
"fragLoadPolicy": {
"default": {
"maxTimeToFirstByteMs": 10000,
"maxLoadTimeMs": 120000,
"timeoutRetry": {
"maxNumRetry": 4,
"retryDelayMs": 0,
"maxRetryDelayMs": 0
},
"errorRetry": {
"maxNumRetry": 6,
"retryDelayMs": 1000,
"maxRetryDelayMs": 8000
}
}
},
"steeringManifestLoadPolicy": {
"default": {
"maxTimeToFirstByteMs": 10000,
"maxLoadTimeMs": 20000,
"timeoutRetry": {
"maxNumRetry": 2,
"retryDelayMs": 0,
"maxRetryDelayMs": 0
},
"errorRetry": {
"maxNumRetry": 1,
"retryDelayMs": 1000,
"maxRetryDelayMs": 8000
}
}
},
"manifestLoadingTimeOut": 10000,
"manifestLoadingMaxRetry": 1,
"manifestLoadingRetryDelay": 1000,
"manifestLoadingMaxRetryTimeout": 64000,
"levelLoadingTimeOut": 10000,
"levelLoadingMaxRetry": 4,
"levelLoadingRetryDelay": 1000,
"levelLoadingMaxRetryTimeout": 64000,
"fragLoadingTimeOut": 20000,
"fragLoadingMaxRetry": 6,
"fragLoadingRetryDelay": 1000,
"fragLoadingMaxRetryTimeout": 64000,
"cueHandler": {},
"enableWebVTT": true,
"enableIMSC1": true,
"enableCEA708Captions": true,
"captionsTextTrack1Label": "English",
"captionsTextTrack1LanguageCode": "en",
"captionsTextTrack2Label": "Spanish",
"captionsTextTrack2LanguageCode": "es",
"captionsTextTrack3Label": "Unknown CC",
"captionsTextTrack3LanguageCode": "",
"captionsTextTrack4Label": "Unknown CC",
"captionsTextTrack4LanguageCode": "",
"renderTextTracksNatively": false
}
Changing to an enhancement request to stop poling live playlists that do not update after a certain amount (of time or number of refreshes) defined in the config.