Silverlight MediaElement tutorial. Creating a video and audio player.

Video in Silverlight is played by using the built-in MediaElement class. You might have seen some video frameworks like Silverlight Media Framework, Expression Encoder Player or Open Video Player, they all are based on this class. Though these frameworks look quite complex, in reality they are just wrappers around the built-in MediaElement or SmoothStreamingMediaElement classes and all that they do is adding design, control buttons and some extra functionality like advertisement and diagnostics. So you can build such video player by yourself.

The simplest media player

Here is the simplest code which will work:

<MediaElement 
    Source="http://ecn.channel9.msdn.com/o9/content/smf/progressivecontent/wildlife.wmv" />

It will start playing video as soon as the page is loaded and the video fills the entire page uniformly (keeping its aspect ratio).
Simplest_MediaElement

If it doesn’t work, make sure that the address of your page looks like `http://…` and not `file://…`. You can fix the url in this way by adding a web application project to your solution and running it.

Now I will explain how to improve the code above so that it looks more like a video player.

Play/Pause buttons

MediaElement_PlayPause
You should disable auto playing (the AutoPlay property) and also implement a grid layout with 2 buttons. Here is the xaml code:

<Grid Width="400" Height="300">
    <Grid.RowDefinitions>
        <RowDefinition Height="*" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    
    <Border BorderThickness="0" Background="Black">
        <MediaElement x:Name="media" 
                      Source="http://ecn.channel9.msdn.com/o9/content/smf/progressivecontent/wildlife.wmv"
                      AutoPlay="False" />
    </Border>
    
    <Grid Grid.Row="1" HorizontalAlignment="Left">
        <Button x:Name="playButton" Content="Play" Click="playButton_Click" />
        <Button x:Name="pauseButton" Content="Pause" Click="pauseButton_Click" Visibility="Collapsed" />
    </Grid>
    
</Grid>

It should work like this: if you click the play button – video starts playing and the pause button appears; if you click the pause button – video stops playing and the play button appears. I have added 2 event handlers and here is the C# code of them:

private void playButton_Click(object sender, RoutedEventArgs e)
{
    media.Play();

    playButton.Visibility = Visibility.Collapsed;
    pauseButton.Visibility = Visibility.Visible;
}

private void pauseButton_Click(object sender, RoutedEventArgs e)
{
    media.Pause();

    playButton.Visibility = Visibility.Visible;
    pauseButton.Visibility = Visibility.Collapsed;
}

Now you can see the media element and control it by using the button at the bottom.

Volume control

MediaElement_Volume
It is very simple to add a control for increasing or decreasing volume. Just add the Slider control and bind its value to the media element:

<Slider Width="50" Minimum="0" Maximum="1" Value="{Binding Path=Volume, ElementName=media, Mode=TwoWay}" />

Media length and position text

MediaElement_PositionText
This feature requires creating a ViewModel. It is because there is no PositionChanged event, so you should create a model property and bind it to the Position property of the MediaElement class.
Here is the xaml code which is bound to 3 properties: Position, PositionText and DurationText.

<MediaElement x:Name="media" 
              Source="http://ecn.channel9.msdn.com/o9/content/smf/progressivecontent/wildlife.wmv"
              AutoPlay="False"
              Position="{Binding Position, Mode=TwoWay}" />
<!-- ... -->
<TextBlock>
    <Run Text="{Binding PositionText}" Foreground="Gray" />
    <Run Text=" / " />
    <Run Text="{Binding DurationText}" />
</TextBlock>

The viewmodel listens to changes of the Position property and updates the PositionText property. Also it has the DurationText property which is updated only 1 time:

public class MediaViewModel : INotifyPropertyChanged
{
    private MediaElement _element;

    public MediaViewModel(MediaElement element)
    {
        this._element = element;

        this.PositionChanged += (s, e) => this.UpdatePositionInfo();
    }

    private TimeSpan _position;

    public TimeSpan Position
    {
        get { return this._position; }
        set
        {
            this._position = value;
            if (this._position != value)
            {
                this.RaisePropertyChanged("Position");
            }

            this.PositionChanged(this, EventArgs.Empty);
        }
    }

    private string _positionText;

    public string PositionText
    {
        get { return this._positionText; }
        set
        {
            this._positionText = value;
            this.RaisePropertyChanged("PositionText");
        }
    }

    private string _durationText;

    public string DurationText
    {
        get { return this._durationText; }
        set
        {
            this._durationText = value;
            this.RaisePropertyChanged("DurationText");
        }
    }

    public void UpdatePositionInfo()
    {
        this.PositionText = this.Position.ToString("mm\\:ss");
    }

    public void UpdateDurationInfo()
    {
        this.DurationText = this._element.NaturalDuration.TimeSpan.ToString("mm\\:ss");
    }

    public event EventHandler PositionChanged = delegate { }; 

    public event PropertyChangedEventHandler PropertyChanged = delegate { };

    private void RaisePropertyChanged(string propertyName)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

And code behind:

private readonly MediaViewModel _viewModel;

public MainPage()
{
    InitializeComponent();

    this.DataContext = this._viewModel = new MediaViewModel(this.media);

    this.media.MediaOpened += (s, e) => this._viewModel.UpdateDurationInfo();
}

Playing progress bar

MediaElement_PlayProgressBar
The text label with position and length is a good indicator, but it would be better to add a bar which displays a red line indicating played video and a gray line indicating buffered video as you have seen it on youtube.

I use this xaml code for displaying the progress bar:

<Canvas x:Name="playCanvas" Grid.Row="1" Background="LightGray" Height="10">
    <Rectangle x:Name="bufferBar" Fill="Gray" Height="10" Width="0" />
    <Rectangle x:Name="playBar" Fill="Red" Height="10" Width="0" />
</Canvas>

In code behind I update the position bar and buffered bar when they change. Note that I use the DownloadProgressChanged event instead of the BufferingProgressChanged event, because BufferingProgress is related to 5 buffered seconds, whereas DownloadProgress means the whole video length.

public MainPage()
{
    // ...
    this._viewModel.PositionChanged += (s, e) => this.UpdatePlayBar();
    this.media.DownloadProgressChanged += (s, e) => this.UpdateBufferBar();
}

private void UpdatePlayBar()
{
    if (media.NaturalDuration.TimeSpan != TimeSpan.Zero)
    {
        playBar.Width = media.Position.TotalMilliseconds / media.NaturalDuration.TimeSpan.TotalMilliseconds * playCanvas.ActualWidth;
    }
}

private void UpdateBufferBar()
{
    Canvas.SetLeft(playCanvas, media.DownloadProgressOffset * playCanvas.ActualWidth);
    bufferBar.Width = media.DownloadProgress * playCanvas.ActualWidth;
}

Changing position after clicking on the progress bar

So we have a playing progress bar, but it doesn’t react if we click it. It is easy to fix, I will just add a MouseLeftButtonUp event handler to the progress bar. It calculates the mouse position and changes the media position accordingly.

<Canvas x:Name="playCanvas" Grid.Row="1" Background="LightGray" Height="10" MouseLeftButtonDown="PlayCanvas_MouseLeftButtonUp">
    <Rectangle x:Name="bufferBar" Fill="Gray" Height="10" Width="0" />
    <Rectangle x:Name="playBar" Fill="Red" Height="10" Width="0" />
</Canvas>

The event handler:

private void PlayCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
    var position = e.GetPosition(playCanvas);
    var relativePosition = position.X / playCanvas.ActualWidth;

    media.Position = new TimeSpan((long)(media.NaturalDuration.TimeSpan.Ticks * relativePosition));
}

There can be one issue: if the video is not downloaded yet and you click at the end – you will have to wait a long time until the video has been compeletely downloaded. The player should send HTTP-range requests, but sometimes it does, sometimes it doesn’t. Anyway, Smooth Streaming don’t have such issues.

Displaying errors and state information

MediaElement_ErrorMessage

Sometimes video cannot be played because of buffering or if errors occur. Such information should be displayed to users so that they know what is happening.
It can be implemented by using TextBlock controls and some events.

<TextBlock x:Name="statusText" Grid.Row="3" Visibility="Collapsed" />
<TextBlock x:Name="errorText" Grid.Row="3" Foreground="Red" TextWrapping="Wrap" Visibility="Collapsed" />
public MainPage()
{
    // ...
    this.media.MediaFailed += this.MediaFailed;
    this.media.CurrentStateChanged += this.MediaCurrentStateChanged;
}

private void MediaFailed(object sender, ExceptionRoutedEventArgs e)
{
    statusText.Visibility = Visibility.Collapsed;
    errorText.Visibility = Visibility.Visible;
    errorText.Text = e.ErrorException.Message;
}

private void MediaCurrentStateChanged(object sender, RoutedEventArgs e)
{
    errorText.Visibility = Visibility.Collapsed;
    statusText.Visibility = Visibility.Visible;
    statusText.Text = media.CurrentState.ToString();
}

Source code and sample

You can download the project here: VideoPlayerSample.zip

Advertisements

4 Responses to Silverlight MediaElement tutorial. Creating a video and audio player.

  1. Pingback: Silverlight video player. YouTube style. | Silverlight, WPF and Windows 8 Development

  2. Vit says:

    Thanks for you sharing! By the way – is any way to use it with youtube videos ?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: