dash.js
dash.js copied to clipboard
Quality change is not possible once fully buffered
- Dash.js version: nightly, 4.2.1 as of writing.
Once Dash.js completely buffers any stream quality it will not allow any further quality switches.
This is likely caused by calling MediaSource
's endOfStream
once last segment is downloaded, rather than once it's reached in playback.
Below is a demo to reproduce this issue. It starts playback of a reference stream in a worst possible quality and removes buffer limits. This is done to ensure the video is quickly buffered till the very end.
Once fully buffered it expands video size, the expected behaviour here is for ABR to increase quality since limitBitrateByPortal
is set to true
.
Then 5 seconds later it calls setQualityFor
to switch quality manually.
And after 5 more seconds it calls setQualityFor
with forceSwitch
set to true
.
None of the three methods result in quality switch, video continues to play in terrible quality despite network conditions allowing for much better and user explicitly requesting it.
While the demo is arbitrary, this does happen in real world either in the last 30/60 seconds (default settings) of any video or earlier with short videos.
limitBitrateByPortal
particularly also causes this frequently when video is started in small container and then expanded to full screen by user action.
Sample code:
<video height="50"></video>
Buffer level: <span id="bufferLevel">0%</span>
<script src="http://reference.dashif.org/dash.js/nightly/dist/dash.all.debug.js"></script>
<script>
const video = document.getElementsByTagName('video')[0];
const url = 'https://dash.akamaized.net/akamai/bbb_30fps/bbb_30fps.mpd';
const player = window.dashjs.MediaPlayer().create();
player.initialize(video, url);
player.setMute(true);
player.updateSettings({
streaming: {
buffer: {
bufferToKeep: Infinity,
bufferTimeAtTopQuality: Infinity,
bufferTimeAtTopQualityLongForm: Infinity,
},
abr: {
limitBitrateByPortal: true,
}
}
})
player.on(dashjs.MediaPlayer.events.STREAM_INITIALIZED, () => player.play());
video.addEventListener('progress', () => {
let start = 0;
let end = 0;
for (let i = 0; i < video.buffered.length; i++) {
start = Math.min(start, video.buffered.start(i));
end = Math.max(start, video.buffered.end(i));
}
const level = (end - start) / video.duration;
document.getElementById('bufferLevel').innerText = (level * 100).toFixed(2) + '%';
if (level >= 1) {
onBufferFull();
}
});
const onBufferFull = () => {
video.style.width = '100%';
video.style.height = '100%';
const highest = player.getBitrateInfoListFor('video').length - 1;
window.setTimeout(() => player.setQualityFor('video', highest), 5_000);
window.setTimeout(() => player.setQualityFor('video', highest, true), 10_000);
}
</script>
Good finding thanks. Will try to look into this as part of one of the next sprints
This issue has been automatically marked as stale because it has not had recent activity. However, it might still be relevant so please leave a short comment if it should remain open. Otherwise the issue will be closed automatically after two weeks. Thank you for your contributions.
unstale
This issue has been automatically closed because no further activity occurred. If you think this issue is still relevant please reopen it or contact @dsilhavy. Thank you for your contributions.
This issue has been automatically closed because no further activity occurred. If you think this issue is still relevant please reopen it or contact @dsilhavy. Thank you for your contributions.
This issue has been automatically closed because no further activity occurred. If you think this issue is still relevant please reopen it or contact @dsilhavy. Thank you for your contributions.
hey there, can this issue be looked into again? thanks.
I found that both calls to player.setQualityFor() do not take effect for the same reason in my setup. The highest quality requested exceeds the allowed highest quality. I think ABR also has no impact for the same reason. Looks like it is not related to fully buffered situation.
More details: The highest quality requested (which is index 9 that maps to the below line in MPD with 3840x2160) [bbb_30fps_3840x2160_12000k" codecs="avc1.640033" bandwidth="14931538" width="3840" height="2160] is way more than topquailty returned/allowed by _checkPortalSize() (which returns index < 9)
I retested with a lower bitrate request (5) like below (to bypass the quality check i described above). It still does not take effect. This might be due to Stream EOS. window.setTimeout(() => player.setQualityFor('video', 5), 5_000);
another test with forced switch using the below. This seems to have worked. I could see bit-rate change request events and the quality moving to a higher resolution window.setTimeout(() => player.setQualityFor('video', 5, true), 5_000);
At least 2 issues here seem to be due to early EOS on the stream (ABR not kicking in and setQualityFor without forceswitch not taking effect). The forceswitch flag appears to be working.
i submitted PR for review https://github.com/Dash-Industry-Forum/dash.js/pull/4113