C# Corner

The Windows Runtime Media API

Learn how to play multimedia files with the Windows Runtime media API.

More on media in Windows Store apps:

In this article, I'll discuss how to use the Windows Runtime media API to play both music and video files within a Windows Store app. To demonstrate the media APIs I'll create a sample application that allows a user to browse and play media files from their primary storage drive.

To get started, create a new C# Windows Store Blank App, as seen in Figure 1.

Next, open up the Package.appxmanifest file and go to the Capabilities tab. Then set the Internet, Music Library and Video Library check boxes as seen in Figure 2.

[Click on image for larger view.] Figure 1. Create a new C# Windows Store Blank App.
[Click on image for larger view.] Figure 2. The Package.appxmanifest Capabilities tab.

Then go to the Declarations tab and add a BackgroundTask. Enable the Audio setting and set the Entry point to the App class of your Project, as seen in Figure 3.

[Click on image for larger view.] Figure 3. The Package.appxmanifest Declarations tab.

Next, create a new folder in the project named ViewModels. Then add a new class named MediaInfo to the ViewModels folder. The MediaInfo class will be used to store details on a particular video or music file. Add string typed Title and Artist properties to the MediaInfo class. Also, you'll want to add a TimeSpan property named Duration to the MediaInfo class.

Now it's time to set up the GUI for the app. Open up the Main.xaml file and copy the XAML from Listing 1 into your root Grid element.

Listing 1. Main.xaml StackPanel XAML.

<StackPanel Name="MediaInfoPanel" HorizontalAlignment="Center" 
  VerticalAlignment="Top" Margin="0,10" Canvas.ZIndex="1">
  <TextBlock Name="MediaTitle" FontSize="14" Text="{Binding Title}"></TextBlock>
  <TextBlock Name="MediaArtist" FontSize="14" Text="{Binding Artist}"></TextBlock>
  <TextBlock Name="MediaDuration" FontSize="13" Text="{Binding Duration}"></TextBlock>
</StackPanel>
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
  <MediaElement Name="Media"></MediaElement>
</StackPanel>
<StackPanel Name="ControlsPanel" Visibility="Collapsed" Orientation="Horizontal" 
  VerticalAlignment="Bottom" HorizontalAlignment="Center" Margin="10" Canvas.ZIndex="1">
  <Button Name="LoadMediaButton" Click="LoadMedia_Click">Load Media</Button>
  <Button Name="RewindMediaButton" Click="RewindMedia_Click">Rewind</Button>
  <Button Name="PlayMediaButton" Click="PlayMedia_Click">Play / Pause</Button>
  <Button x:Name="StopMediaButton" Content="Stop" Click="StopMedia_Click"/>
  <Button Name="ForwardMediaButton" Click="ForwardMedia_Click">Fast Forward</Button>
</StackPanel>

The GUI allows a user to load, rewind, play, stop and fast-forward a media file. In addition, the current media file's track name, artist and duration will be displayed at the top of the window. Now it's time to put the Windows Runtime multimedia API to good use. Open up the Main.xaml.cs MainPage class file. Start out by adding a using statement for the Windows.Storage.Pickers namespace:

using Windows.Storage.Pickers;

Next, add a private class variable of Type FileOpenPicker:

private FileOpenPicker _fileOpenPicker;

Then add a private class variable of Type const double and initialize it to a value of 1.5:

private const double _skipRate = 1.5;

Next, in the OnNavigatedTo event, I set up the file picker that will be used to load a media file. I also subscribe to the Loaded event of the MediaElement on the window (the MediaElement will be used later to control playback of a given media file):

protected override void OnNavigatedTo(NavigationEventArgs e)
{
  SetupFilePicker();
  Media.Loaded += Media_Loaded;
}

In the SetupFilePicker method I add a list of allowable music and video file extensions for the app. The valid file extensions are .mp3, .mov and .wmv. Feel free to add any additional music or video file extensions that you'd like to play through the MediaElement (note that you'll need to have the appropriate codecs to play any additional media file types):

private void SetupFilePicker()
{
  _fileOpenPicker = new FileOpenPicker();
  _fileOpenPicker.FileTypeFilter.Add(".MP3");
  _fileOpenPicker.FileTypeFilter.Add(".MOV");
  _fileOpenPicker.FileTypeFilter.Add(".WMV");
}

In the MediaElement's Loaded event I display the ControlsPanel StackPanel. I also set up the media controls, and subscribe to the CurrentStateChanged event on the MediaElement:

private void Media_Loaded(object sender, RoutedEventArgs e)
{
  ControlsPanel.Visibility = Visibility.Visible;
  SetupMediaControls();
  Media.CurrentStateChanged += MediaOnCurrentStateChanged;
}

In the SetupMediaControls method I set up all of the media control keys. I also enable the playback buttons through the EnablePlayControls method. The MediaControl static object contains events for capturing pause, play, rewind, fast-forward and pause/play media control key presses:

private void SetupMediaControls()
{
  MediaControl.PausePressed += MediaControlOnPausePressed;
  MediaControl.PlayPressed += MediaControlOnPlayPressed;
  MediaControl.PlayPauseTogglePressed += MediaControlOnPlayPauseTogglePressed;
  MediaControl.StopPressed += MediaControlOnStopPressed;
  MediaControl.RewindPressed += MediaControlOnRewindPressed;
  MediaControl.FastForwardPressed += MediaControlOnFastForwardPressed;
  EnablePlayControls(false);
}

In the EnablePlayControls button I either enable or disable all of the playback controls on the window:

private void EnablePlayControls(bool enabled)
 {
   PlayMediaButton.IsEnabled = enabled;
   StopMediaButton.IsEnabled = enabled;
   RewindMediaButton.IsEnabled = enabled;
   ForwardMediaButton.IsEnabled = enabled;
 }

In the CurrentStateChanged event of the MediaElement I set the IsPlaying property on the MediaControl object and enable the playback buttons if the media file has started playing:

private void MediaOnCurrentStateChanged(object sender, RoutedEventArgs routedEventArgs)
{
  switch (Media.CurrentState)
  {
    case MediaElementState.Playing:
      EnablePlayControls(true);
      MediaControl.IsPlaying = true;
      break;
    default:
      MediaControl.IsPlaying = false;
      break;
  }
}

Now it's time to implement the event handlers for the media control keys. I first implement the pause media control key handler in the MediaControlOnPausePressed method. The method simply calls the PauseMedia method on the UI thread:

private async void MediaControlOnPausePressed(object sender, object o)
{
    await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, PauseMedia);
}

The PauseMedia method pauses the currently playing media file through the Pause method on the MediaElement. In addition, I set the MediaControl IsPlaying property to false:

private void PauseMedia()
{
  Media.Pause();
  MediaControl.IsPlaying = false;
}

Next, I implement the pause media control key handler. The method calls the PausePlayMedia method on the UI thread:

private async void MediaControlOnPlayPauseTogglePressed(object sender, object o)
{
  await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, PausePlayMedia);
}

The PausePlayMedia method handles toggling between playing and pausing the active media file. I first set the DefaultPlaybackRate on the MediaElement to 1.0. Then I set the PlaybackRate on the MediaElement to the DefaultPlaybackRate value. After that I check the CurrentState property on the MediaElement and pause the file (whether it's currently being played, or otherwise):

private void PausePlayMedia()
{
  Media.DefaultPlaybackRate = 1.0;
  Media.PlaybackRate = Media.DefaultPlaybackRate;

  if (Media.CurrentState != MediaElementState.Paused)
    Media.Pause();
  else
    Media.Play();
}

Next, I implement the MediaControlOnStopPressed event, which handles the stop media control key press event. The method invokes the StopMedia method on the UI thread:

private async void MediaControlOnStopPressed(object sender, object o)
{
  await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, StopMedia);
}

The StopMedia method stops the currently playing media file through the Stop method on the MediaElement:

private void StopMedia()
{
  Media.Stop();
}

The MediaControlOnRewindPressed event handles a rewind media control key press. The method rewinds the current media file through the RewindMedia method:

private async void MediaControlOnRewindPressed(object sender, object o)
{
  await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, RewindMedia);
}

The RewindMedia method sets the default playback rate on the MediaElement to 0 and sets the playback rate to a negative _skipRate value:

private void RewindMedia()
{
  Media.DefaultPlaybackRate = 0.0;
  Media.PlaybackRate = -_skipRate;
}

The MediaControlOnFastForwardPressed method handles the fast-forward media control key press event. The method will fast-forward the current media file by calling the ForwardMedia method:

private async void MediaControlOnFastForwardPressed(object sender, object o)
{
  await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, ForwardMedia);
}

The ForwardMedia method will fast-forward the media file by setting the default playback rate to 0 and setting the playback rate to the _skipRate value:

private void ForwardMedia()
{
  Media.DefaultPlaybackRate = 0.0;
  Media.PlaybackRate = _skipRate;
}

Now it's time to implement all of the playback control button click event handlers. First, I implement the Play button click event handler through the PlayMedia_Click event. The method simply calls the PausePlayMedia method to either pause or play the current media file:

private void PlayMedia_Click(object sender, RoutedEventArgs e)
{
  PausePlayMedia();
}

Next, I implement the RewindMedia_Click method, which handles the Rewind button's Click event. The method calls the RewindMedia method to rewind the current media file:

private void RewindMedia_Click(object sender, RoutedEventArgs e)
{
  RewindMedia();
}

The ForwardMedia_Click method handles the Forward button's Click event and calls the ForwardMedia method:

private void ForwardMedia_Click(object sender, RoutedEventArgs e)
{
  ForwardMedia();
}

The StopMedia_Click method handles the Stop button's Click event and calls the StopMedia method:

private void StopMedia_Click(object sender, RoutedEventArgs e)
{
  StopMedia();
}

Now it's time to implement the LoadMedia_Click method, which handles the LoadMedia button's Click event. The method calls the PickSingleFileAsync method on the configured FileOpenPicker object to allow the user to load a single media file. Once a media file is loaded it's opened through the OpenAsync method. Then I set the MediaControl.IsPlaying property to true to indicate the media file is playing. Last, I call the LoadMediaInfo method, passing it the opened mediaFile instance:

private async void LoadMedia_Click(object sender, RoutedEventArgs e)
{
  var mediaFile = await _fileOpenPicker.PickSingleFileAsync();

  if (mediaFile != null)
  {
    var fileStream = await mediaFile.OpenAsync(Windows.Storage.FileAccessMode.Read);
    Media.SetSource(fileStream, mediaFile.ContentType);
    MediaControl.IsPlaying = true;
    LoadMediaInfo(mediaFile);
  }
}

The LoadMediaInfo method sets the track name, artist and duration properties and binds them to the MediaInfoPanel StackPanel. First I get the music and video properties for the given mediaFile:

var musicProperties = await mediaFile.Properties.GetMusicPropertiesAsync();
var videoProperties =  await mediaFile.Properties.GetVideoPropertiesAsync();

Next, I check if the file is a music file by checking to see if the Title property is set on the musicProperties object. If the file is a music file I set the TrackName and ArtistName properties on the MediaControl and retrieve the duration from the musicProperties object's Duration property:

if (!string.IsNullOrWhiteSpace(musicProperties.Title))
{
  MediaControl.TrackName = musicProperties.Title;
  MediaControl.ArtistName = musicProperties.Artist;
  duration = musicProperties.Duration;
}

Otherwise, the file is a video file, and I set the TrackName and ArtistName properties from the Title and Publisher properties on the videoProperties object. I also retrieve the media file's duration through the videoProperties object's Duration property:

else
{
  MediaControl.TrackName = videoProperties.Title;
  MediaControl.ArtistName = videoProperties.Publisher;
  duration = videoProperties.Duration;
}

Finally, I create a MediaInfo object and bind it to the MediaInfoPanel StackPanel control:

MediaInfoPanel.DataContext = 
  new ViewModel.MediaInfo(MediaControl.TrackName, MediaControl.ArtistName, duration);

See Listing 2 for the completed LoadMediaInfo method.

Listing 2. The LoadMediaInfo method.

private async void LoadMediaInfo(StorageFile mediaFile)
{
  var musicProperties = await mediaFile.Properties.GetMusicPropertiesAsync();
  var videoProperties =  await mediaFile.Properties.GetVideoPropertiesAsync();
  TimeSpan duration;

  if (!string.IsNullOrWhiteSpace(musicProperties.Title))
  {
    MediaControl.TrackName = musicProperties.Title;
    MediaControl.ArtistName = musicProperties.Artist;
    duration = musicProperties.Duration;
  }
  else
  {
    MediaControl.TrackName = videoProperties.Title;
    MediaControl.ArtistName = videoProperties.Publisher;
    duration = videoProperties.Duration;
  }

  MediaInfoPanel.DataContext = 
    new ViewModel.MediaInfo(MediaControl.TrackName,    MediaControl.ArtistName, duration);
}
See Listing 3 for the complete MainPage class implementation.

Listing 3. The complete MainPage class implementation.

using System;
using Windows.Media;
using Windows.Storage;
using Windows.Storage.Pickers;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

// The Blank Page item template is documented at go.microsoft.com/fwlink/?LinkId=234238

namespace VSMWinRTMultiMediaDemo
{
  /// <summary>
  /// An empty page that can be used on its own or navigated to within a Frame
  /// </summary>
  public sealed partial class MainPage : Page
  {
    private FileOpenPicker _fileOpenPicker;
    private const double _skipRate = 1.5;

    public MainPage()
    {
      this.InitializeComponent();
    }

    /// <summary>
    /// Invoked when this page is about to be displayed in a Frame.
    /// </summary>
    /// <param name="e">Event data that describes how this page was reached.  The Parameter
    /// property is typically used to configure the page.</param>
    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
      SetupFilePicker();
      Media.Loaded += Media_Loaded;
    }

    private void SetupFilePicker()
    {
      _fileOpenPicker = new FileOpenPicker();
      _fileOpenPicker.FileTypeFilter.Add(".MP3");
      _fileOpenPicker.FileTypeFilter.Add(".MOV");
      _fileOpenPicker.FileTypeFilter.Add(".WMV");
    }

    private void Media_Loaded(object sender, RoutedEventArgs e)
    {
      ControlsPanel.Visibility = Visibility.Visible;
      SetupMediaControls();
      Media.CurrentStateChanged += MediaOnCurrentStateChanged;
    }

    private void SetupMediaControls()
    {
      MediaControl.PausePressed += MediaControlOnPausePressed;
      MediaControl.PlayPressed += MediaControlOnPlayPressed;
      MediaControl.PlayPauseTogglePressed += MediaControlOnPlayPauseTogglePressed;
      MediaControl.StopPressed += MediaControlOnStopPressed;
      MediaControl.RewindPressed += MediaControlOnRewindPressed;
      MediaControl.FastForwardPressed += MediaControlOnFastForwardPressed;
      EnablePlayControls(false);
    }

    private void EnablePlayControls(bool enabled)
    {
      PlayMediaButton.IsEnabled = enabled;
      StopMediaButton.IsEnabled = enabled;
      RewindMediaButton.IsEnabled = enabled;
      ForwardMediaButton.IsEnabled = enabled;
    }

    private void MediaOnCurrentStateChanged(object sender, RoutedEventArgs routedEventArgs)
    {
      switch (Media.CurrentState)
      {
        case MediaElementState.Playing:
          EnablePlayControls(true);
          MediaControl.IsPlaying = true;
          break;
        default:
          MediaControl.IsPlaying = false;
          break;
      }
    }

    private async void MediaControlOnPausePressed(object sender, object o)
    {
      await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, PauseMedia);
    }

    private void PauseMedia()
    {
      Media.Pause();
      MediaControl.IsPlaying = false;
    }

    private async void MediaControlOnPlayPressed(object sender, object o)
    {
      await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, PlayMedia);
    }

    private void PlayMedia()
    {
      Media.Play();
    }

    private async void MediaControlOnPlayPauseTogglePressed(object sender, object o)
    {
      await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, PausePlayMedia);
    }

    private void PausePlayMedia()
    {
      Media.DefaultPlaybackRate = 1.0;
      Media.PlaybackRate = Media.DefaultPlaybackRate;

      if (Media.CurrentState != MediaElementState.Paused)
        Media.Pause();
      else
        Media.Play();
    }

    private async void MediaControlOnStopPressed(object sender, object o)
    {
      await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, StopMedia);
    }

    private void StopMedia()
    {
      Media.Stop();
    }

    private async void MediaControlOnRewindPressed(object sender, object o)
    {
      await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, RewindMedia);
    }

    private void RewindMedia()
    {
      Media.DefaultPlaybackRate = 0.0;
      Media.PlaybackRate = -_skipRate;
    }

    private async void MediaControlOnFastForwardPressed(object sender, object o)
    {
      await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, ForwardMedia);
    }

    private void ForwardMedia()
    {
      Media.DefaultPlaybackRate = 0.0;
      Media.PlaybackRate = _skipRate;
    }
        
    private void PlayMedia_Click(object sender, RoutedEventArgs e)
    {
      PausePlayMedia();
    }

    private void RewindMedia_Click(object sender, RoutedEventArgs e)
    {
      RewindMedia();
    }

    private void ForwardMedia_Click(object sender, RoutedEventArgs e)
    {
      ForwardMedia();
    }

    private void StopMedia_Click(object sender, RoutedEventArgs e)
    {
      StopMedia();
    }

    private async void LoadMedia_Click(object sender, RoutedEventArgs e)
    {
      var mediaFile = await _fileOpenPicker.PickSingleFileAsync();

      if (mediaFile != null)
      {
        var fileStream = await mediaFile.OpenAsync(Windows.Storage.FileAccessMode.Read);
        Media.SetSource(fileStream, mediaFile.ContentType);
        MediaControl.IsPlaying = true;
        LoadMediaInfo(mediaFile);
      }
    }

    private async void LoadMediaInfo(StorageFile mediaFile)
    {
      var musicProperties = await mediaFile.Properties.GetMusicPropertiesAsync();
      var videoProperties = await mediaFile.Properties.GetVideoPropertiesAsync();
      TimeSpan duration;

      if (!string.IsNullOrWhiteSpace(musicProperties.Title))
      {
        MediaControl.TrackName = musicProperties.Title;
        MediaControl.ArtistName = musicProperties.Artist;
        duration = musicProperties.Duration;
      }
      else
      {
        MediaControl.TrackName = videoProperties.Title;
        MediaControl.ArtistName = videoProperties.Publisher;
        duration = videoProperties.Duration;
      }

      MediaInfoPanel.DataContext = 
        new ViewModel.MediaInfo(MediaControl.TrackName, MediaControl.ArtistName, duration);
      }
  }
}

You should now be able to play a music file (as seen in Figure 4) or a video file (as seen in Figure 5).

[Click on image for larger view.] Figure 4. The finished app playing a music file.
[Click on image for larger view.] Figure 5. The finished app playing a video file.

As you can see, the Windows Runtime multimedia API is easy and straightforward to use. You've also seen how easy it is to add media key controls to your Windows Store app. Stay tuned for the next installment in the series, where I'll cover how to add multimedia recording capabilities to a Windows Store app.

comments powered by Disqus

Featured

Subscribe on YouTube