DownloadProgress, DownloadProgressOffset, and BufferProgress of the MediaElement

Introduction:

This article is about the DownloadProgress, DownloadProgressOffset, and BufferProgress properties of MediaElement.

Background:

I was working with MediaElement as I had a requirement to play a video from an HTTP URL. I encountered a strange issue. In a normal scenario, MediaElement’s DownloadProgress property updates a UI progress bar. It works correctly while downloading and the DownloadProgress increases. But, when the user clicks another position in the file, the DownloadProgress value becomes 1.0 (100%) and the same with DownloadProgressOffset. After some time, it corrects itself. You can find this same issue at the following link: https://silverlight.net/forums/p/69162/167219.aspx.
Before explaining the issue, let me explain some definitions that I would use to explain the issue in showing the DownloadProgress and the DownloadProgressOffset.
Byte Range Request:

Byte Range Request:

HTTP requests have an option to do a partial content range request. To explain it, let’s take a scenario. Say, we want to request a very large file from a service, but before we do so, we want to find out exactly how large it is and whether the service supports “range requests”. To do this, we make a HEAD request to the large resource we want to retrieve. The HEAD method is a standard HTTP method that acts as if we have made a GET request, but it returns only the headers and not the body. This allows us to find out information about the resource without actually taking the time or using the bandwidth to download it. For example, we can read the Content-Length header and determine the size of the resource.
Another header of great importance is the Accept-Ranges header. If it’s present, it may contain a value “bytes”. If so, we know that we can make a GET request and request a byte range to retrieve only those bytes. This is the first important key component.
So, now, we know we can make a range request to the service, which we do by sending a standard GET request including a Range header that specifies the range of bytes we can  request it like like so: Range: bytes=0-999. The service should then respond with a 206 Partial Content status code, a Content-Range header, and the requested range of bytes. This is the second key component.
we should note that the service cannot force the client to make a range request. It is entirely up to the client to make such requests, so the service should honor all standard GET requests that do not contain a Range header by sending back the full representation of the requested resource. If your service allows range requests, it is to your benefit to tell the client with an Accept-Ranges header. If you want to explicitly tell the client that you do not allow range requests, send a value of “none” back with the Accept-Ranges header.
I got this definition from the following article, so in case it is not clear to you, you can refer to the following article:

https://benramsey.com/archives/206-partial-content-and-range-requests/.

DownloadProgress:

The DownloadProgress property of MediaElement is a percentage indicating the amount of download completed for content located on a remote server. The value ranges from 0 to 1. Multiply it by 100 to get the percentage. The default value is 0. When this property is set, the DownloadProgressChanged event occurs. The reference point for this value would be the start of the video so even if you set the position of the video to some middle position, it’s value would be relative to 0.
Let’s take an example. If I am playing a video and the video is 100 minute long. Now If I set the position of the video to 50th minute. After some time if it’s value is 0.6  then it doesn’t mean that it have been downloaded 60%, it means that it have been downloaded from 50th minute to 60th minute in crude term.

DownloadProgressOffset:

It gets the offset of the download progress of the MediaElement. When the user seeks to a point in the download progress (for example, 30 seconds into the video), that becomes the offset for the download progress.

 BufferingProgress:

Gets a value that indicates the current buffering progress. It is the amount of buffering that is completed for the media content. The value ranges from 0 to 1. Multiply by 100 to obtain a percentage. The default value is 0. The BufferingProgressChanged event occurs whenever the BufferingProgress property is updated by 0.05 or more, or reaches 1.0. In some cases, the BufferingProgress value does not remain at 1 as the media plays.

Explanation of the Issue:

When the user plays an HTTP URL, it downloads the file, and DowloadProgress and DownloadProgressOffest works fine, but when the user changes the position of the play or when the first time “seek” operation happens, the player actually fires a BRR, and the index of the WMV file (which is at the end of the file) will be downloaded. At this point, the DownloadProgress and the DownloadProgressOffset both would be very near to the end point, so after some time, the DownloadProgress and the DownloadProgressOffset both reach 100%, which actually means the index file downloaded completely. After this, DownloadProgress will go back to the seek point.

Using the Code:

There is nothing much to explain in the code. It is self-explanatory. I am using the DownloadProgressChanged and BufferingProgressChanged events to set the DownloadProgress, DownloadProgressOffset and BufferOffset progressbars.

 private void VideoMediaElement_MediaOpened(object sender, RoutedEventArgs e)
{
    //VideoMediaElement.Position = TimeSpan.FromDays(1);
    //VideoMediaElement.Position = TimeSpan.FromSeconds(0);
}

private void Video_BufferingProgressChanged(object sender, RoutedEventArgs e)
{
    BufferProgress.Value = VideoMediaElement.BufferingProgress * 100;
    DownloadProgress.Value = VideoMediaElement.DownloadProgress * 100;
    OffsetValue.Value = VideoMediaElement.DownloadProgressOffset * 100;
}

private void Video_DownloadProgressChanged(object sender, RoutedEventArgs e)
{
    DownloadProgress.Value = VideoMediaElement.DownloadProgress * 100;
    OffsetValue.Value = VideoMediaElement.DownloadProgressOffset * 100;
}

private void Pause_Click(object sender, RoutedEventArgs e)
{
    VideoMediaElement.Pause();
}
private void Play_Click(object sender, RoutedEventArgs e)
{
    if (!Uri.IsWellFormedUriString(URI.Text, UriKind.Absolute))
    {
        MessageBox.Show("Invalid URI");
        return;
    }
    VideoMediaElement.Source = new Uri(URI.Text);
    VideoMediaElement.Play();
}
private void Stop_Click(object sender, RoutedEventArgs e)
{
    VideoMediaElement.Stop();
}
private void Farward_Click(object sender, RoutedEventArgs e)
{
    //if (VideoMediaElement.CurrentState != MediaElementState.Buffering)
    VideoMediaElement.Position = VideoMediaElement.Position +
                      TimeSpan.FromSeconds(Int16.Parse(Amount.Text));
}
private void Backward_Click(object sender, RoutedEventArgs e)
{
    //if (VideoMediaElement.CurrentState != MediaElementState.Buffering)
    VideoMediaElement.Position = VideoMediaElement.Position -
                      TimeSpan.FromSeconds(Int16.Parse(Amount.Text));
}

MediaElementDownloadProgress.zip