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: http://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
HTTP requests have an option to do a partial content range request. To explain it, let’s take a scenario. Say, I want to request a very large file from a service, but before I do so, I want to find out exactly how large it is and whether the service supports “range requests”. To do this, I make a HEAD request to the large resource I want to retrieve. The HEAD method is a standard HTTP method that acts as if I’ve made a GET request, but it returns only the headers and not the body. This allows me to find out information about the resource without actually taking the time or using the bandwidth to download it. For example, I 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, I know that I can make a GET request and request a byte range to retrieve only those bytes. This is the first important key component.
So, now, I know I can make a range request to the service, which I do by sending a standard GET request including a Range header that specifies the range of bytes I’m requesting, 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.
I 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: http://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.
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)
{
}
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)
{
VideoMediaElement.Position = VideoMediaElement.Position +
TimeSpan.FromSeconds(Int16.Parse(Amount.Text));
}
private void Backward_Click(object sender, RoutedEventArgs e)
{
VideoMediaElement.Position = VideoMediaElement.Position -
TimeSpan.FromSeconds(Int16.Parse(Amount.Text));
}