capLevelToPlayerSize does not cap first level when startLevel is -1
What version of Hls.js are you using?
1.5.2
What browser (including version) are you using?
Chrome 120.0.6099.234 (Official Build) (arm64)
What OS (including version) are you using?
MacOS Monterey
Test stream
No response
Configuration
{
ignoreDevicePixelRatio: true,
capLevelToPlayerSize: true,
startLevel: -1,
}
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
- Stream a video that has multiple levels with above configuration.
- Resize your player to small size (e.g., 360px) and then play the video.
- Check your network and you will see the start level didn't follow the cap.
Expected behaviour
I expect that start level follows the rest
What actually happened?
I want to combine usage of automatic start level selection with capping level to the player size but based on what I described above, that works on all levels except the first level. I had the same issue #5664 on 1.4.6 version and I thought it would be fixed in 1.5 version but this issue still exists.
Console output
[log] > Debug logs enabled for "Hls instance" in hls.js version 1.5.2
player.jsx:1587 [log] > stopLoad
player.jsx:1587 [log] > loadSource:blob:https://www.***.com/
player.jsx:1587 [log] > [stream-controller]: Trigger BUFFER_RESET
video-player.jsx:798 [log] > attachMedia
video-player.jsx:798 [log] > [buffer-controller] created media source: MediaSource
hls.mjs:10350 [log] > [stream-controller]: STOPPED->ERROR
hls.mjs:10350 [log] > [audio-stream-controller]: STOPPED->ERROR
hls.mjs:28019 [log] > stopLoad
hls.mjs:10350 [log] > [stream-controller]: ERROR->STOPPED
hls.mjs:10350 [log] > [audio-stream-controller]: ERROR->STOPPED
video-player.jsx:912 [log] > detachMedia
video-player.jsx:912 [log] > [buffer-controller] media source detaching
player.jsx:1450 [log] > Debug logs enabled for "Hls instance" in hls.js version 1.5.2
player.jsx:1587 [log] > stopLoad
player.jsx:1587 [log] > loadSource:https://www.***.com/
player.jsx:1587 [log] > [stream-controller]: Trigger BUFFER_RESET
video-player.jsx:798 [log] > attachMedia
video-player.jsx:798 [log] > [buffer-controller] created media source: MediaSource
hls.mjs:17725 [log] > [buffer-controller] Media source opened
hls.mjs:25677 [log] > [level-controller]: manifest loaded, 6 level(s) found, first bitrate: 179344
hls.mjs:17837 [log] > [buffer-controller] 1 bufferCodec event(s) expected
hls.mjs:28008 [log] > startLoad(-1)
hls.mjs:25753 [log] > [level-controller]: Switching to level 0 (148p SDR @64336) from level -1
hls.mjs:25888 [log] > [level-controller]: Loading level index 0 with ***
hls.mjs:10350 [log] > [stream-controller]: STOPPED->IDLE
hls.mjs:10350 [log] > [subtitle-stream-controller]: STOPPED->IDLE
hls.mjs:27051 [log] > [stream-controller]: Level 0 loaded [1,22][part-22--1], cc [0, 0] duration:217.667
hls.mjs:18394 [log] > [buffer-controller] Updating Media Source duration to 217.667
hls.mjs:26839 [log] > [stream-controller]: Fragment 1 of level 0 is being downloaded to test bitrate and will not be buffered
hls.mjs:9590 [log] > [stream-controller]: Loading fragment 1 cc: 0 of [1-22] level: 0, target: 0
hls.mjs:10350 [log] > [stream-controller]: IDLE->FRAG_LOADING
hls.mjs:10350 [log] > [stream-controller]: FRAG_LOADING->IDLE
hls.mjs:7377 [log] > [abr] picked start tier {"videoRanges":[],"preferHDR":false,"minFramerate":0,"minBitrate":3613417.9510487127}
hls.mjs:26771 [log] > [stream-controller]: Adapting to level 5 from level 0
hls.mjs:25753 [log] > [level-controller]: Switching to level 5 (1080p SDR @548050) from level 0
hls.mjs:25888 [log] > [level-controller]: Loading level index 5 with ***
hls.mjs:10350 [log] > [stream-controller]: IDLE->WAITING_LEVEL
hls.mjs:27051 [log] > [stream-controller]: Level 5 loaded [1,22][part-22--1], cc [0, 0] duration:217.533
hls.mjs:10350 [log] > [stream-controller]: WAITING_LEVEL->IDLE
hls.mjs:9590 [log] > [stream-controller]: Loading fragment 1 cc: 0 of [1-22] level: 5, target: 0
hls.mjs:10350 [log] > [stream-controller]: IDLE->FRAG_LOADING
hls.mjs:15492 [log] > [transmuxer-interface, main]: Starting new transmux session for sn: 1 p: -1 level: 5 id: 1
discontinuity: true
trackSwitch: true
contiguous: false
accurateTimeOffset: true
timeOffset: 0
initSegmentChange: true
hls.mjs:13575 [log] > [mp4-remuxer]: ISGenerated flag reset
hls.mjs:13566 [log] > [mp4-remuxer]: initPTS & initDTS reset
hls.mjs:13570 [log] > [mp4-remuxer]: reset next timestamp
hls.mjs:10607 [log] > manifest codec:undefined, ADTS type:2, samplingIndex:4
hls.mjs:10757 [log] > parsed codec:mp4a.40.2, rate:44100, channels:2
hls.mjs:10350 [log] > [stream-controller]: FRAG_LOADING->PARSING
hls.mjs:27592 [log] > [stream-controller]: Init audio buffer, container:audio/mp4, codecs[selected/level/parsed]=[//mp4a.40.2]
hls.mjs:27597 [log] > [stream-controller]: Init video buffer, container:video/mp4, codecs[level/parsed]=[/avc1.640028]
hls.mjs:18002 [log] > [buffer-controller] 0 bufferCodec event(s) expected audio,video
hls.mjs:18468 [log] > [buffer-controller] creating sourceBuffer(audio/mp4;codecs=mp4a.40.2)
hls.mjs:18468 [log] > [buffer-controller] creating sourceBuffer(video/mp4;codecs=avc1.640028)
hls.mjs:21723 [log] > Setting autoLevelCapping to 1: 240p@83887 for media 344x192.6328125
hls.mjs:28265 [log] > set autoLevelCapping:1
hls.mjs:15741 [log] > [audio-stream-controller]: InitPTS for cc: 0 found from main: 9090
hls.mjs:9327 [log] > [stream-controller]: Loaded fragment 1 of level 5
hls.mjs:14822 [log] > [transmuxer.ts]: Flushed fragment 1 of level 5
hls.mjs:10350 [log] > [stream-controller]: PARSING->PARSED
hls.mjs:9474 [log] > [stream-controller]: Buffered main sn: 1 of level 5 (frag:[0.000-10.067] > buffer:[0.067-10.008])
hls.mjs:10350 [log] > [stream-controller]: PARSED->IDLE
hls.mjs:26771 [log] > [stream-controller]: Adapting to level 1 from level 5
hls.mjs:25753 [log] > [level-controller]: Switching to level 1 (240p SDR @83887) from level 5
hls.mjs:25888 [log] > [level-controller]: Loading level index 1 with ***
hls.mjs:10350 [log] > [stream-controller]: IDLE->WAITING_LEVEL
hls.mjs:27051 [log] > [stream-controller]: Level 1 loaded [1,22][part-22--1], cc [0, 0] duration:217.533
hls.mjs:10350 [log] > [stream-controller]: WAITING_LEVEL->IDLE
hls.mjs:9590 [log] > [stream-controller]: Loading fragment 2 cc: 0 of [1-22] level: 1, target: 10.008
hls.mjs:10350 [log] > [stream-controller]: IDLE->FRAG_LOADING
hls.mjs:15492 [log] > [transmuxer-interface, main]: Starting new transmux session for sn: 2 p: -1 level: 1 id: 1
discontinuity: false
trackSwitch: true
contiguous: false
accurateTimeOffset: true
timeOffset: 10
initSegmentChange: false
hls.mjs:13575 [log] > [mp4-remuxer]: ISGenerated flag reset
hls.mjs:13566 [log] > [mp4-remuxer]: initPTS & initDTS reset
hls.mjs:13570 [log] > [mp4-remuxer]: reset next timestamp
hls.mjs:10607 [log] > manifest codec:undefined, ADTS type:2, samplingIndex:7
hls.mjs:10757 [log] > parsed codec:mp4a.40.2, rate:22050, channels:1
hls.mjs:10350 [log] > [stream-controller]: FRAG_LOADING->PARSING
hls.mjs:27592 [log] > [stream-controller]: Init audio buffer, container:audio/mp4, codecs[selected/level/parsed]=[//mp4a.40.2]
hls.mjs:27597 [log] > [stream-controller]: Init video buffer, container:video/mp4, codecs[level/parsed]=[/avc1.4d4015]
hls.mjs:9327 [log] > [stream-controller]: Loaded fragment 2 of level 1
hls.mjs:14822 [log] > [transmuxer.ts]: Flushed fragment 2 of level 1
hls.mjs:10350 [log] > [stream-controller]: PARSING->PARSED
hls.mjs:9474 [log] > [stream-controller]: Buffered main sn: 2 of level 1 (frag:[10.008-20.067] > buffer:[0.067-19.992])
hls.mjs:10350 [log] > [stream-controller]: PARSED->IDLE
hls.mjs:9590 [log] > [stream-controller]: Loading fragment 3 cc: 0 of [1-22] level: 1, target: 19.992
hls.mjs:10350 [log] > [stream-controller]: IDLE->FRAG_LOADING
hls.mjs:10350 [log] > [stream-controller]: FRAG_LOADING->PARSING
hls.mjs:9327 [log] > [stream-controller]: Loaded fragment 3 of level 1
hls.mjs:14822 [log] > [transmuxer.ts]: Flushed fragment 3 of level 1
hls.mjs:10350 [log] > [stream-controller]: PARSING->PARSED
hls.mjs:9474 [log] > [stream-controller]: Buffered main sn: 3 of level 1 (frag:[19.992-30.067] > buffer:[0.067-29.977])
hls.mjs:10350 [log] > [stream-controller]: PARSED->IDLE
hls.mjs:9590 [log] > [stream-controller]: Loading fragment 4 cc: 0 of [1-22] level: 1, target: 29.977
hls.mjs:10350 [log] > [stream-controller]: IDLE->FRAG_LOADING
hls.mjs:10350 [log] > [stream-controller]: FRAG_LOADING->PARSING
hls.mjs:9327 [log] > [stream-controller]: Loaded fragment 4 of level 1
hls.mjs:14822 [log] > [transmuxer.ts]: Flushed fragment 4 of level 1
hls.mjs:10350 [log] > [stream-controller]: PARSING->PARSED
hls.mjs:9474 [log] > [stream-controller]: Buffered main sn: 4 of level 1 (frag:[29.977-40.067] > buffer:[0.067-40.008])
hls.mjs:10350 [log] > [stream-controller]: PARSED->IDLE
hls.mjs:9590 [log] > [stream-controller]: Loading fragment 5 cc: 0 of [1-22] level: 1, target: 40.008
hls.mjs:10350 [log] > [stream-controller]: IDLE->FRAG_LOADING
hls.mjs:10350 [log] > [stream-controller]: FRAG_LOADING->PARSING
hls.mjs:9327 [log] > [stream-controller]: Loaded fragment 5 of level 1
hls.mjs:14822 [log] > [transmuxer.ts]: Flushed fragment 5 of level 1
hls.mjs:10350 [log] > [stream-controller]: PARSING->PARSED
hls.mjs:9474 [log] > [stream-controller]: Buffered main sn: 5 of level 1 (frag:[40.008-50.067] > buffer:[0.067-49.993])
Chrome media internals output
No response
Note that the media element must be visible and have layout for its dimensions to be known to HLS.js. If the video element is not measurable before the HLS Playlist is loaded, auto level capping cannot happen.
Originally posted by @robwalch in https://github.com/video-dev/hls.js/issues/5664#issuecomment-1906806649
I'm sure the media element is precent, visible and have layout for its dimensions before HLS.js is initialized.
The logs in the description do not look like logs from 1.5.2. The logs also show loadSource called twice which suggests an issue with your setup (the first call causes and error and the second requires a detach/attach to clean it up). I wouldn't call this out if I could reproduce the issue with a clean setup. After modifying demo/basic-usage.html to match your config and scale the video element to 100% page width I cannot reproduce the issue.
Basic page setup:
<html>
<head>
<title>Hls.js demo - basic usage</title>
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/hls.min.js"></script>
<center>
<h1>Hls.js demo - basic usage</h1>
<video width="100%" id="video" controls></video>
</center>
<script>
var video = document.getElementById('video');
if (Hls.isSupported()) {
var hls = new Hls({
debug: true,
ignoreDevicePixelRatio: true,
capLevelToPlayerSize: true,
startLevel: -1,
});
hls.loadSource('https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8');
hls.attachMedia(video);
}
</script>
</body>
</html>
Logs with the page width at 480px and the video element at 464x261:
[log] > Debug logs enabled for "Hls instance" in hls.js version 1.5.2
hls.min.js:1 [log] > stopLoad
hls.min.js:1 [log] > loadSource:https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8
hls.min.js:1 [log] > [stream-controller]: Trigger BUFFER_RESET
hls.min.js:1 [log] > attachMedia
hls.min.js:1 [log] > [buffer-controller] created media source: MediaSource
hls.min.js:1 [log] > [buffer-controller] Media source opened
hls.min.js:1 [log] > [level-controller]: manifest loaded, 5 level(s) found, first bitrate: 2149280
hls.min.js:1 [log] > setting initial bwe to 2149280
hls.min.js:1 [log] > [buffer-controller] 1 bufferCodec event(s) expected
hls.min.js:1 [log] > Setting autoLevelCapping to 1: 288p@460560 for media 464x232
hls.min.js:1 [log] > set autoLevelCapping:1
hls.min.js:1 [log] > startLoad(-1)
hls.min.js:1 [log] > [level-controller]: Switching to level 0 (184p SDR avc1,mp4a @246440) from level -1
hls.min.js:1 [log] > [level-controller]: Loading level index 0 with https://test-streams.mux.dev/x36xhzz/url_2/193039199_mp4_h264_aac_ld_7.m3u8
hls.min.js:1 [log] > [stream-controller]: STOPPED->IDLE
hls.min.js:1 [log] > [subtitle-stream-controller]: STOPPED->IDLE
hls.min.js:1 [log] > [stream-controller]: Level 0 loaded [0,63][part-63--1], cc [0, 0] duration:634.634
hls.min.js:1 [log] > [buffer-controller] Updating Media Source duration to 634.634
hls.min.js:1 [log] > [stream-controller]: Fragment 0 of level 0 is being downloaded to test bitrate and will not be buffered
hls.min.js:1 [log] > [stream-controller]: Loading fragment 0 cc: 0 of [0-63] level: 0, target: 0
hls.min.js:1 [log] > [stream-controller]: IDLE->FRAG_LOADING
hls.min.js:1 [log] > [stream-controller]: FRAG_LOADING->IDLE
hls.min.js:1 [log] > [abr] picked start tier {"codecSet":"avc1,mp4a","videoRanges":["SDR"],"preferHDR":false,"minFramerate":0,"minBitrate":246440}
hls.min.js:1 [info] > [abr] switch candidate:3->1 adjustedbw(33069742)-bitrate=32609182 ttfb:0.1 avgDuration:0.0 maxFetchDuration:4.0 fetchDuration:0.1 firstSelection:true codecSet:avc1,mp4a videoRange:SDR hls.loadLevel:0
hls.min.js:1 [log] > [stream-controller]: Adapting to level 1 from level 0
hls.min.js:1 [log] > [level-controller]: Switching to level 1 (288p SDR avc1,mp4a @460560) from level 0
hls.min.js:1 [log] > [level-controller]: Loading level index 1 with https://test-streams.mux.dev/x36xhzz/url_4/193039199_mp4_h264_aac_7.m3u8
hls.min.js:1 [log] > [stream-controller]: IDLE->WAITING_LEVEL
hls.min.js:1 [log] > [stream-controller]: Level 1 loaded [0,63][part-63--1], cc [0, 0] duration:634.634
hls.min.js:1 [log] > [stream-controller]: WAITING_LEVEL->IDLE
hls.min.js:1 [log] > [stream-controller]: Loading fragment 0 cc: 0 of [0-63] level: 1, target: 0
hls.min.js:1 [log] > [stream-controller]: IDLE->FRAG_LOADING
hls.min.js:1 [log] > injecting Web Worker for "main"
hls.min.js:1 [log] > [transmuxer-interface, main]: Starting new transmux session for sn: 0 p: -1 level: 1 id: 1
discontinuity: true
trackSwitch: true
contiguous: false
accurateTimeOffset: true
timeOffset: 0
initSegmentChange: true
hls.min.js:1 [log] > [stream-controller]: Loaded fragment 0 of level 1
7e05be66-983a-4fbc-b31c-c92a9508d66a:1 [log] > Debug logs enabled for "main" in hls.js version 1.5.2
hls.min.js:1 [log] > [mp4-remuxer]: ISGenerated flag reset
hls.min.js:1 [log] > [mp4-remuxer]: initPTS & initDTS reset
hls.min.js:1 [log] > [mp4-remuxer]: reset next timestamp
hls.min.js:1 [log] > manifest codec:mp4a.40.5, ADTS type:2, samplingIndex:7
hls.min.js:1 [log] > parsed codec:mp4a.40.5, rate:22050, channels:2
hls.min.js:1 [log] > [stream-controller]: FRAG_LOADING->PARSING
hls.min.js:1 [log] > [stream-controller]: Init audio buffer, container:audio/mp4, codecs[selected/level/parsed]=[mp4a.40.5/mp4a.40.5/mp4a.40.5]
hls.min.js:1 [log] > [stream-controller]: Init video buffer, container:video/mp4, codecs[level/parsed]=[avc1.420016/avc1.42c016]
hls.min.js:1 [log] > [buffer-controller] 0 bufferCodec event(s) expected audio,video
hls.min.js:1 [log] > [buffer-controller] creating sourceBuffer(audio/mp4;codecs=mp4a.40.5)
hls.min.js:1 [log] > [buffer-controller] creating sourceBuffer(video/mp4;codecs=avc1.420016)
hls.min.js:1 [log] > [audio-stream-controller]: InitPTS for cc: 0 found from main: 900000
hls.min.js:1 [log] > [transmuxer.ts]: Flushed fragment 0 of level 1
hls.min.js:1 [log] > [stream-controller]: PARSING->PARSED
hls.min.js:1 [log] > [stream-controller]: Buffered main sn: 0 of level 1 (frag:[0.000-10.031] > buffer:[0.000-10.000])
hls.min.js:1 [log] > [stream-controller]: PARSED->IDLE
hls.min.js:1 [log] > [stream-controller]: Loading fragment 1 cc: 0 of [0-63] level: 1, target: 10
hls.min.js:1 [log] > [stream-controller]: IDLE->FRAG_LOADING
hls.min.js:1 [log] > [stream-controller]: Loaded fragment 1 of level 1
hls.min.js:1 [log] > [stream-controller]: FRAG_LOADING->PARSING
hls.min.js:1 [log] > [transmuxer.ts]: Flushed fragment 1 of level 1
hls.min.js:1 [log] > [stream-controller]: PARSING->PARSED
hls.min.js:1 [log] > [stream-controller]: Buffered main sn: 1 of level 1 (frag:[10.000-20.016] > buffer:[0.000-20.000])
You'll see that set autoLevelCapping is logged before the first level switch. Switching to level 0 is called because with these settings, a bandwidth test is performed on the first segment of the lowest level. After that, we get switch candidate:3->1 and Adapting to level 1 from level 0.
If this isn't happening for you make sure you are using the latest version and please share an example that reproduces the issue with updated logs. Please host the example or use something like codepen.io.
Closing with the assumption that the issue is resolved, please re-open if that isn't the case.