dash.js icon indicating copy to clipboard operation
dash.js copied to clipboard

Quality change is not possible once fully buffered

Open l1bbcsg opened this issue 3 years ago • 12 comments

  • 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>

l1bbcsg avatar Dec 23 '21 16:12 l1bbcsg

Good finding thanks. Will try to look into this as part of one of the next sprints

dsilhavy avatar Jan 04 '22 14:01 dsilhavy

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.

stale[bot] avatar Jun 03 '22 15:06 stale[bot]

unstale

dsilhavy avatar Jun 03 '22 16:06 dsilhavy

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.

stale[bot] avatar Jun 18 '22 07:06 stale[bot]

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.

stale[bot] avatar Jul 06 '22 19:07 stale[bot]

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.

stale[bot] avatar Jul 21 '22 02:07 stale[bot]

hey there, can this issue be looked into again? thanks.

oracularhades avatar Oct 15 '22 16:10 oracularhades

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)

hd51515 avatar Jan 10 '23 16:01 hd51515

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);

hd51515 avatar Jan 11 '23 17:01 hd51515

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);

hd51515 avatar Jan 11 '23 17:01 hd51515

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.

hd51515 avatar Jan 11 '23 18:01 hd51515

i submitted PR for review https://github.com/Dash-Industry-Forum/dash.js/pull/4113

hd51515 avatar Jan 18 '23 20:01 hd51515