Skip to content
Advertisement

Browser sending request for already buffered video data

Problem

As the title says, I’m seeing an issue where the browser (when seeking through a video) is constantly making requests for ranges of bytes it should already have.

enter image description here

Code

I’ve provided a minimal code sample; to replicate the issue, whip around the current time control on the video and look at your network log.

window.onload = function() {

  var myVideo = document.getElementById('my-video');

  myVideo.addEventListener('progress', function() {
    var bufferedEnd = myVideo.buffered.end(myVideo.buffered.length - 1);
    var duration = myVideo.duration;
    if (duration > 0) {
      document.getElementById('buffered-amount').style.width = ((bufferedEnd / duration) * 100) + "%";
    }
  });

  myVideo.addEventListener('timeupdate', function() {
    var duration = myVideo.duration;
    if (duration > 0) {
      document.getElementById('progress-amount').style.width = ((myVideo.currentTime / duration) * 100) + "%";
    }
  });
}
.buffered {
  height: 20px;
  position: relative;
  background: #555;
  width: 300px;
}

#buffered-amount {
  display: block;
  height: 100%;
  background-color: #777;
  width: 0;
}

.progress {
  margin-top: -20px;
  height: 20px;
  position: relative;
  width: 300px;
}

#progress-amount {
  display: block;
  height: 100%;
  background-color: #595;
  width: 0;
}
<video id="my-video" controls preload="auto"><source src="https://media-dev.ozone.tech/media/045728df-7431-4548-8353-318357cc2643/video_preview-1655322764.5499873.mp4?Expires=1656287999&Signature=3RnkLu03JreuUZk9bICqqC5IwGiq~tWpBCaUSfyGb9W9VLPfw6Na4zh~tDTxxDjIirhocj7es2T0ONm0L6XsuFxsCx2T9-CANok014Kt6ddHEaUmuOi5uztEWyXpLmI3ussq4dc5lW2Xw5WH6uV-5Z8fIlIQehMxQK-XQ6RpdHhWBQLsmfPBqSSPnDk6KH5fkK-xCwQmxUwAN1nRVH8H6p~43~E6MZa7gSS~i1717~FlHto3depk~AvREjm59T1rNBvaZeb6ZI-pnYAf52wwQs3pWXln7PCtiIOYQCxE10l4nkOvJ--KGmPA0jOeSuv0dCRUi3QLZD5JxsJgBVM4sA__&Key-Pair-Id=K16V0JZ9ZAOTC6" type="video/mp4"></video>
<div class="buffered">
  <span id="buffered-amount"></span>
</div>
<div class="progress">
  <span id="progress-amount"></span>
</div>

Question

Can someone explain why this is happening and how I can prevent it? Seems like this could potentially be a bug but since all the browsers are doing it, I wonder if it’s something on my end; maybe the way we encode the video?

Advertisement

Answer

When we reference the specification on TimeRanges we find the following definition.

When a TimeRanges object is said to be a normalized TimeRanges object, the ranges it represents must obey the following criteria:

  • The start of a range must be greater than the end of all earlier ranges.
  • The start of a range must be less than or equal to the end of that same range.

In other words, the ranges in such an object are ordered, don’t overlap, and don’t touch (adjacent ranges are folded into one bigger range). A range can be empty (referencing just a single moment in time), e.g. to indicate that only one frame is currently buffered in the case that the user agent has discarded the entire media resource except for the current frame, when a media element is paused.

Ranges in a TimeRanges object must be inclusive.

Thus, the end of a range would be equal to the start of a following adjacent (touching but not overlapping) range. Similarly, a range covering a whole timeline anchored at zero would have a start equal to zero and an end equal to the duration of the timeline.

Which if I were to interpret I would imagine that only if the end of the smallest TimeRange (being index 0) is equal to duration will 100% buffering be achieved, which is not the case even at the end multiple ranges may remain. This is contradicted by the the Mozilla documentation, so much for a sanity check.

Basically the only thing we need to deduce is the buffered progress, consoling ourselves that no where is there any guarantee or even mention that buffered means already downloaded, stored and readily available frames (never needed to be fetched again). In fact the opposite is true, the possibility exists that “a range can be empty (referencing just a single moment in time)”.

Since the only buffered time ranges of any use will always be future content considering current time as our base, which may not necessarily be the smallest range.

Based on deductive reasoning we may safely assume:

  • That total buffered must at least be current time
  • Adding only the difference from future range end, and
  • Include the sum of the difference between start and end of any subsequent ranges

Or in other more eloquent words:

function bufferedTotal(buffered, currentTime) {
    let total = currentTime;
    for (i = 0; i < buffered.length; i++)
        if (buffered.end(i) > currentTime)
            if (buffered.start(i) < currentTime)
                total += buffered.end(i) - currentTime;
            else
                total += buffered.end(i) - buffered.start(i);
    return total;
}

With all our ducks neatly in a row this might just be considered done.

window.onload = function() {

  function bufferedTotal(buffered, currentTime) {
     let total = currentTime;
     for (i = 0; i < buffered.length; i++)
          if (buffered.end(i) > currentTime)
              if (buffered.start(i) < currentTime)
                  total += buffered.end(i) - currentTime;
              else
                  total += buffered.end(i) - buffered.start(i);
      return total;
  }

  var myVideo = document.getElementById('my-video');

  myVideo.addEventListener('progress', function() {
    var bufferedEnd = bufferedTotal(myVideo.buffered, myVideo.currentTime);
    var duration = myVideo.duration;
    if (duration > 0) {
      document.getElementById('buffered-amount').style.width = ((bufferedEnd / duration) * 100) + "%";
    }
  });

  myVideo.addEventListener('timeupdate', function() {
    var duration = myVideo.duration;
    if (duration > 0) {
      document.getElementById('progress-amount').style.width = ((myVideo.currentTime / duration) * 100) + "%";
    }
  });
}
.buffered {
  height: 20px;
  position: relative;
  background: #555;
  width: 300px;
}

#buffered-amount {
  display: block;
  height: 100%;
  background-color: #777;
  width: 0;
}

.progress {
  margin-top: -20px;
  height: 20px;
  position: relative;
  width: 300px;
}

#progress-amount {
  display: block;
  height: 100%;
  background-color: #595;
  width: 0;
}
<video id="my-video" controls preload="auto"><source src="https://media-dev.ozone.tech/media/045728df-7431-4548-8353-318357cc2643/video_preview-1655322764.5499873.mp4?Expires=1656287999&Signature=3RnkLu03JreuUZk9bICqqC5IwGiq~tWpBCaUSfyGb9W9VLPfw6Na4zh~tDTxxDjIirhocj7es2T0ONm0L6XsuFxsCx2T9-CANok014Kt6ddHEaUmuOi5uztEWyXpLmI3ussq4dc5lW2Xw5WH6uV-5Z8fIlIQehMxQK-XQ6RpdHhWBQLsmfPBqSSPnDk6KH5fkK-xCwQmxUwAN1nRVH8H6p~43~E6MZa7gSS~i1717~FlHto3depk~AvREjm59T1rNBvaZeb6ZI-pnYAf52wwQs3pWXln7PCtiIOYQCxE10l4nkOvJ--KGmPA0jOeSuv0dCRUi3QLZD5JxsJgBVM4sA__&Key-Pair-Id=K16V0JZ9ZAOTC6" type="video/mp4"></video>
<div class="buffered">
  <span id="buffered-amount"></span>
</div>
<div class="progress">
  <span id="progress-amount"></span>
</div>
User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement